xref: /xnu-8792.81.2/bsd/kern/kern_memorystatus_internal.h (revision 19c3b8c28c31cb8130e034cfb5df6bf9ba342d90)
1 /*
2  * Copyright (c) 2006-2019 Apple Inc. All rights reserved.
3  *
4  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5  *
6  * This file contains Original Code and/or Modifications of Original Code
7  * as defined in and that are subject to the Apple Public Source License
8  * Version 2.0 (the 'License'). You may not use this file except in
9  * compliance with the License. The rights granted to you under the License
10  * may not be used to create, or enable the creation or redistribution of,
11  * unlawful or unlicensed copies of an Apple operating system, or to
12  * circumvent, violate, or enable the circumvention or violation of, any
13  * terms of an Apple operating system software license agreement.
14  *
15  * Please obtain a copy of the License at
16  * http://www.opensource.apple.com/apsl/ and read it before using this file.
17  *
18  * The Original Code and all software distributed under the License are
19  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23  * Please see the License for the specific language governing rights and
24  * limitations under the License.
25  *
26  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27  *
28  */
29 
30 #ifndef _KERN_MEMORYSTATUS_INTERNAL_H_
31 #define _KERN_MEMORYSTATUS_INTERNAL_H_
32 
33 /*
34  * Contains memorystatus subsystem definitions that are not
35  * exported outside of the memorystatus subsystem.
36  *
37  * For example, all of the mechanisms used by kern_memorystatus_policy.c
38  * should be defined in this header.
39  */
40 
41 #if BSD_KERNEL_PRIVATE
42 
43 #include <mach/boolean.h>
44 #include <stdbool.h>
45 #include <os/base.h>
46 #include <kern/sched_prim.h>
47 
48 #if CONFIG_FREEZE
49 #include <sys/kern_memorystatus_freeze.h>
50 #endif /* CONFIG_FREEZE */
51 
52 /*
53  * memorystatus subsystem globals
54  */
55 #if CONFIG_JETSAM
56 extern unsigned int memorystatus_available_pages;
57 extern unsigned int memorystatus_available_pages_pressure;
58 extern unsigned int memorystatus_available_pages_critical;
59 extern uint32_t jetsam_kill_on_low_swap;
60 #else /* CONFIG_JETSAM */
61 extern uint64_t memorystatus_available_pages;
62 extern uint64_t memorystatus_available_pages_pressure;
63 extern uint64_t memorystatus_available_pages_critical;
64 #endif /* CONFIG_JETSAM */
65 extern int block_corpses; /* counter to block new corpses if jetsam purges them */
66 extern int system_procs_aging_band;
67 extern int applications_aging_band;
68 /*
69  * TODO(jason): This should really be calculated dynamically by the zalloc
70  * subsystem before we do a zone map exhaustion kill. But the zone_gc
71  * logic is non-trivial, so for now it just sets this global.
72  */
73 extern _Atomic bool memorystatus_zone_map_is_exhausted;
74 /*
75  * TODO(jason): We should get rid of this global
76  * and have the memorystatus thread check for compressor space shortages
77  * itself. However, there are 3 async call sites remaining that require more work to get us there:
78  * 2 of them are in vm_swap_defragment. When it's about to swap in a segment, it checks if that
79  * will cause a compressor space shortage & pre-emptively triggers jetsam. vm_compressor_backing_store
80  * needs to keep track of in-flight swapins due to defrag so we can perform those checks
81  * in the memorystatus thread.
82  * The other is in no_paging_space_action. This is only on macOS right now, but will
83  * be needed on iPad when we run out of swap space. This should be a new kill
84  * reason and we need to add a new health check for it.
85  * We need to maintain the macOS behavior though that we kill no more than 1 process
86  * every 5 seconds.
87  */
88 extern _Atomic bool memorystatus_compressor_space_shortage;
89 /*
90  * TODO(jason): We should also get rid of this global
91  * and check for phantom cache pressure from the memorystatus
92  * thread. But first we need to fix the syncronization in
93  * vm_phantom_cache_check_pressure
94  */
95 extern _Atomic bool memorystatus_phantom_cache_pressure;
96 
97 /*
98  * The actions that the memorystatus thread can perform
99  * when we're low on memory.
100  * See memorystatus_pick_action to see when each action is deployed.
101  */
102 OS_CLOSED_ENUM(memorystatus_action, uint32_t,
103     MEMORYSTATUS_KILL_HIWATER,     // Kill 1 highwatermark process
104     MEMORYSTATUS_KILL_AGGRESSIVE,     // Do aggressive jetsam
105     MEMORYSTATUS_KILL_TOP_PROCESS,     // Kill based on jetsam priority
106     MEMORYSTATUS_WAKE_SWAPPER,  // Wake up the swap thread
107     MEMORYSTATUS_PROCESS_SWAPIN_QUEUE, // Compact the swapin queue and move segments to the swapout queue
108     MEMORYSTATUS_KILL_SUSPENDED_SWAPPABLE, // Kill a suspended swap-eligible processes based on jetsam priority
109     MEMORYSTATUS_KILL_SWAPPABLE, // Kill a swap-eligible process (even if it's running)  based on jetsam priority
110     MEMORYSTATUS_KILL_NONE,     // Do nothing
111     );
112 
113 /*
114  * Structure to hold state for a jetsam thread.
115  * Typically there should be a single jetsam thread
116  * unless parallel jetsam is enabled.
117  */
118 typedef struct jetsam_thread_state {
119 	uint8_t                         inited; /* boolean - if the thread is initialized */
120 	uint8_t                         limit_to_low_bands; /* boolean */
121 	int                             index; /* jetsam thread index */
122 	thread_t                        thread; /* jetsam thread pointer */
123 	int                             jld_idle_kills; /*  idle jetsam kill counter for this session */
124 	uint32_t                        errors; /* Error accumulator */
125 	bool                            sort_flag; /* Sort the fg band (idle on macOS) before killing? */
126 	bool                            corpse_list_purged; /* Has the corpse list been purged? */
127 	bool                            post_snapshot; /* Do we need to post a jetsam snapshot after this session? */
128 	uint64_t                        memory_reclaimed; /* Amount of memory that was just reclaimed */
129 	uint32_t                        hwm_kills; /* hwm kill counter for this session */
130 	sched_cond_atomic_t             jt_wakeup_cond; /* condition var used to synchronize wake/sleep operations for this jetsam thread */
131 } jetsam_thread_state_t;
132 
133 /*
134  * The memorystatus thread monitors these conditions
135  * and will continue to act until the system is considered
136  * healthy.
137  */
138 typedef struct memorystatus_system_health {
139 #if CONFIG_JETSAM
140 	bool msh_available_pages_below_pressure;
141 	bool msh_available_pages_below_critical;
142 	bool msh_compressor_needs_to_swap;
143 	bool msh_compressor_is_low_on_space;
144 	bool msh_compressor_is_thrashing;
145 	bool msh_compressed_pages_nearing_limit;
146 	bool msh_filecache_is_thrashing;
147 	bool msh_phantom_cache_pressure;
148 	bool msh_swappable_compressor_segments_over_limit;
149 	bool msh_swapin_queue_over_limit;
150 	bool msh_swap_low_on_space;
151 	bool msh_swap_out_of_space;
152 #endif /* CONFIG_JETSAM */
153 	bool msh_zone_map_is_exhausted;
154 } memorystatus_system_health_t;
155 
156 void memorystatus_log_system_health(const memorystatus_system_health_t *health);
157 bool memorystatus_is_system_healthy(const memorystatus_system_health_t *status);
158 /* Picks a kill cause given an unhealthy system status */
159 uint32_t memorystatus_pick_kill_cause(const memorystatus_system_health_t *status);
160 
161 /*
162  * Agressive jetsam tunables
163  */
164 #define kJetsamAgingPolicyNone                          (0)
165 #define kJetsamAgingPolicyLegacy                        (1)
166 #define kJetsamAgingPolicySysProcsReclaimedFirst        (2)
167 #define kJetsamAgingPolicyAppsReclaimedFirst            (3)
168 #define kJetsamAgingPolicyMax                           kJetsamAgingPolicyAppsReclaimedFirst
169 extern boolean_t memorystatus_jld_enabled;              /* Enable jetsam loop detection */
170 extern uint32_t memorystatus_jld_eval_period_msecs;         /* Init pass sets this based on device memory size */
171 extern int      memorystatus_jld_eval_aggressive_count;     /* Raise the priority max after 'n' aggressive loops */
172 extern int      memorystatus_jld_eval_aggressive_priority_band_max;  /* Kill aggressively up through this band */
173 extern int      memorystatus_jld_max_kill_loops;            /* How many times should we try and kill up to the target band */
174 extern unsigned int memorystatus_sysproc_aging_aggr_pages; /* Aggressive jetsam pages threshold for sysproc aging policy */
175 extern int       jld_eval_aggressive_count;
176 extern int32_t   jld_priority_band_max;
177 extern uint64_t  jld_timestamp_msecs;
178 extern int       jld_idle_kill_candidates;
179 
180 
181 /*
182  * VM globals read by the memorystatus subsystem
183  */
184 extern unsigned int    vm_page_free_count;
185 extern unsigned int    vm_page_active_count;
186 extern unsigned int    vm_page_inactive_count;
187 extern unsigned int    vm_page_throttled_count;
188 extern unsigned int    vm_page_purgeable_count;
189 extern unsigned int    vm_page_wire_count;
190 extern unsigned int    vm_page_speculative_count;
191 extern uint32_t        c_late_swapout_count, c_late_swappedin_count;
192 extern uint32_t        c_seg_allocsize;
193 extern bool            vm_swapout_thread_running;
194 extern _Atomic bool    vm_swapout_wake_pending;
195 #define VM_PAGE_DONATE_DISABLED     0
196 #define VM_PAGE_DONATE_ENABLED      1
197 extern uint32_t vm_page_donate_mode;
198 void vm_swapout_thread(void);
199 void vm_compressor_process_special_swapped_in_segments(void);
200 
201 #if CONFIG_JETSAM
202 #define MEMORYSTATUS_LOG_AVAILABLE_PAGES memorystatus_available_pages
203 #else /* CONFIG_JETSAM */
204 #define MEMORYSTATUS_LOG_AVAILABLE_PAGES (vm_page_active_count + vm_page_inactive_count + vm_page_free_count + vm_page_speculative_count)
205 #endif /* CONFIG_JETSAM */
206 
207 bool memorystatus_avail_pages_below_pressure(void);
208 bool memorystatus_avail_pages_below_critical(void);
209 #if CONFIG_JETSAM
210 bool memorystatus_swap_over_trigger(uint64_t adjustment_factor);
211 bool memorystatus_swapin_over_trigger(void);
212 #endif /* CONFIG_JETSAM */
213 
214 /* Does cause indicate vm or fc thrashing? */
215 bool is_reason_thrashing(unsigned cause);
216 /* Is the zone map almost full? */
217 bool is_reason_zone_map_exhaustion(unsigned cause);
218 
219 memorystatus_action_t memorystatus_pick_action(struct jetsam_thread_state *jetsam_thread,
220     uint32_t *kill_cause, bool highwater_remaining,
221     bool suspended_swappable_apps_remaining,
222     bool swappable_apps_remaining, int *jld_idle_kills);
223 
224 #pragma mark Freezer
225 #if CONFIG_FREEZE
226 /*
227  * Freezer data types
228  */
229 
230 /* An ordered list of freeze or demotion candidates */
231 struct memorystatus_freezer_candidate_list {
232 	memorystatus_properties_freeze_entry_v1 *mfcl_list;
233 	size_t mfcl_length;
234 };
235 
236 struct memorystatus_freeze_list_iterator {
237 	bool refreeze_only;
238 	proc_t last_p;
239 	size_t global_freeze_list_index;
240 };
241 
242 /*
243  * Freezer globals
244  */
245 extern struct memorystatus_freezer_stats_t memorystatus_freezer_stats;
246 extern int memorystatus_freezer_use_ordered_list;
247 extern struct memorystatus_freezer_candidate_list memorystatus_global_freeze_list;
248 extern struct memorystatus_freezer_candidate_list memorystatus_global_demote_list;
249 extern uint64_t memorystatus_freezer_thread_next_run_ts;
250 bool memorystatus_is_process_eligible_for_freeze(proc_t p);
251 bool memorystatus_freeze_proc_is_refreeze_eligible(proc_t p);
252 
253 proc_t memorystatus_freezer_candidate_list_get_proc(
254 	struct memorystatus_freezer_candidate_list *list,
255 	size_t index,
256 	uint64_t *pid_mismatch_counter);
257 /*
258  * Returns the leader of the p's jetsam coalition
259  * and the role of p in that coalition.
260  */
261 proc_t memorystatus_get_coalition_leader_and_role(proc_t p, int *role_in_coalition);
262 bool memorystatus_freeze_process_is_recommended(const proc_t p);
263 
264 /*
265  * Ordered iterator over all freeze candidates.
266  * The iterator should initially be zeroed out by the caller and
267  * can be zeroed out whenever the caller wishes to start from the beginning
268  * of the list again.
269  * Returns PROC_NULL when all candidates have been iterated over.
270  */
271 proc_t memorystatus_freeze_pick_process(struct memorystatus_freeze_list_iterator *iterator);
272 
273 /*
274  * Returns the number of processes that the freezer thread should try to freeze
275  * on this wakeup.
276  */
277 size_t memorystatus_pick_freeze_count_for_wakeup(void);
278 
279 /*
280  * Configure the freezer for app-based swap mode.
281  * Should be called at boot.
282  */
283 void memorystatus_freeze_configure_for_swap(void);
284 /*
285  * Undo memorystatus_freeze_configure_for_swap
286  */
287 void memorystatus_freeze_disable_swap(void);
288 #endif /* CONFIG_FREEZE */
289 
290 #endif /* BSD_KERNEL_PRIVATE */
291 
292 #endif /* _KERN_MEMORYSTATUS_INTERNAL_H_ */
293