2 * Copyright (c) 2006-2018 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
30 #include <kern/sched_prim.h>
31 #include <kern/kalloc.h>
32 #include <kern/assert.h>
33 #include <kern/debug.h>
34 #include <kern/locks.h>
35 #include <kern/task.h>
36 #include <kern/thread.h>
37 #include <kern/host.h>
38 #include <kern/policy_internal.h>
39 #include <kern/thread_group.h>
41 #include <libkern/libkern.h>
42 #include <mach/coalition.h>
43 #include <mach/mach_time.h>
44 #include <mach/task.h>
45 #include <mach/host_priv.h>
46 #include <mach/mach_host.h>
48 #include <pexpert/pexpert.h>
49 #include <sys/coalition.h>
50 #include <sys/kern_event.h>
52 #include <sys/proc_info.h>
53 #include <sys/reason.h>
54 #include <sys/signal.h>
55 #include <sys/signalvar.h>
56 #include <sys/sysctl.h>
57 #include <sys/sysproto.h>
61 #include <vm/vm_pageout.h>
62 #include <vm/vm_protos.h>
63 #include <mach/machine/sdt.h>
64 #include <libkern/section_keywords.h>
65 #include <stdatomic.h>
67 #include <IOKit/IOBSD.h>
70 #include <vm/vm_map.h>
71 #endif /* CONFIG_FREEZE */
73 #include <sys/kern_memorystatus.h>
74 #include <sys/kern_memorystatus_freeze.h>
75 #include <sys/kern_memorystatus_notify.h>
79 extern unsigned int memorystatus_available_pages
;
80 extern unsigned int memorystatus_available_pages_pressure
;
81 extern unsigned int memorystatus_available_pages_critical
;
82 extern unsigned int memorystatus_available_pages_critical_base
;
83 extern unsigned int memorystatus_available_pages_critical_idle_offset
;
85 #else /* CONFIG_JETSAM */
87 extern uint64_t memorystatus_available_pages
;
88 extern uint64_t memorystatus_available_pages_pressure
;
89 extern uint64_t memorystatus_available_pages_critical
;
91 #endif /* CONFIG_JETSAM */
93 unsigned int memorystatus_frozen_count
= 0;
94 unsigned int memorystatus_suspended_count
= 0;
95 unsigned long freeze_threshold_percentage
= 50;
99 lck_grp_attr_t
*freezer_lck_grp_attr
;
100 lck_grp_t
*freezer_lck_grp
;
101 static lck_mtx_t freezer_mutex
;
104 unsigned int memorystatus_freeze_threshold
= 0;
105 unsigned int memorystatus_freeze_pages_min
= 0;
106 unsigned int memorystatus_freeze_pages_max
= 0;
107 unsigned int memorystatus_freeze_suspended_threshold
= FREEZE_SUSPENDED_THRESHOLD_DEFAULT
;
108 unsigned int memorystatus_freeze_daily_mb_max
= FREEZE_DAILY_MB_MAX_DEFAULT
;
109 uint64_t memorystatus_freeze_budget_pages_remaining
= 0; /* Remaining # of pages that can be frozen to disk */
110 boolean_t memorystatus_freeze_degradation
= FALSE
; /* Protected by the freezer mutex. Signals we are in a degraded freeze mode. */
112 unsigned int memorystatus_max_frozen_demotions_daily
= 0;
113 unsigned int memorystatus_thaw_count_demotion_threshold
= 0;
115 boolean_t memorystatus_freeze_enabled
= FALSE
;
116 int memorystatus_freeze_wakeup
= 0;
117 int memorystatus_freeze_jetsam_band
= 0; /* the jetsam band which will contain P_MEMSTAT_FROZEN processes */
119 #define MAX_XPC_SERVICE_PIDS 10 /* Max. # of XPC services per coalition we'll consider freezing. */
121 #ifdef XNU_KERNEL_PRIVATE
123 unsigned int memorystatus_frozen_processes_max
= 0;
124 unsigned int memorystatus_frozen_shared_mb
= 0;
125 unsigned int memorystatus_frozen_shared_mb_max
= 0;
126 unsigned int memorystatus_freeze_shared_mb_per_process_max
= 0; /* Max. MB allowed per process to be freezer-eligible. */
127 unsigned int memorystatus_freeze_private_shared_pages_ratio
= 2; /* Ratio of private:shared pages for a process to be freezer-eligible. */
128 unsigned int memorystatus_thaw_count
= 0; /* # of thaws in the current freezer interval */
129 uint64_t memorystatus_thaw_count_since_boot
= 0; /* The number of thaws since boot */
130 unsigned int memorystatus_refreeze_eligible_count
= 0; /* # of processes currently thawed i.e. have state on disk & in-memory */
132 /* Freezer counters collected for telemtry */
133 static struct memorystatus_freezer_stats_t
{
135 * # of processes that we've considered freezing.
136 * Used to normalize the error reasons below.
138 uint64_t mfs_process_considered_count
;
141 * The following counters track how many times we've failed to freeze
142 * a process because of a specific FREEZER_ERROR.
144 /* EXCESS_SHARED_MEMORY */
145 uint64_t mfs_error_excess_shared_memory_count
;
146 /* LOW_PRIVATE_SHARED_RATIO */
147 uint64_t mfs_error_low_private_shared_ratio_count
;
148 /* NO_COMPRESSOR_SPACE */
149 uint64_t mfs_error_no_compressor_space_count
;
151 uint64_t mfs_error_no_swap_space_count
;
152 /* pages < memorystatus_freeze_pages_min */
153 uint64_t mfs_error_below_min_pages_count
;
154 /* dasd determined it was unlikely to be relaunched. */
155 uint64_t mfs_error_low_probability_of_use_count
;
156 /* transient reasons (like inability to acquire a lock). */
157 uint64_t mfs_error_other_count
;
160 * # of times that we saw memorystatus_available_pages <= memorystatus_freeze_threshold.
161 * Used to normalize skipped_full_count and shared_mb_high_count.
163 uint64_t mfs_below_threshold_count
;
165 /* Skipped running the freezer because we were out of slots */
166 uint64_t mfs_skipped_full_count
;
168 /* Skipped running the freezer because we were over the shared mb limit*/
169 uint64_t mfs_skipped_shared_mb_high_count
;
172 * How many pages have not been sent to swap because they were in a shared object?
173 * This is being used to gather telemtry so we can understand the impact we'd have
174 * on our NAND budget if we did swap out these pages.
176 uint64_t mfs_shared_pages_skipped
;
179 * A running sum of the total number of bytes sent to NAND during
180 * refreeze operations since boot.
182 uint64_t mfs_bytes_refrozen
;
183 /* The number of refreeze operations since boot */
184 uint64_t mfs_refreeze_count
;
185 } memorystatus_freezer_stats
= {0};
187 #endif /* XNU_KERNEL_PRIVATE */
189 static inline boolean_t
memorystatus_can_freeze_processes(void);
190 static boolean_t
memorystatus_can_freeze(boolean_t
*memorystatus_freeze_swap_low
);
191 static boolean_t
memorystatus_is_process_eligible_for_freeze(proc_t p
);
192 static void memorystatus_freeze_thread(void *param __unused
, wait_result_t wr __unused
);
193 static void memorystatus_freeze_start_normal_throttle_interval(uint32_t new_budget
, mach_timespec_t start_ts
);
195 void memorystatus_disable_freeze(void);
198 static uint64_t memorystatus_freeze_pageouts
= 0;
201 #define DEGRADED_WINDOW_MINS (30)
202 #define NORMAL_WINDOW_MINS (24 * 60)
204 /* Protected by the freezer_mutex */
205 static throttle_interval_t throttle_intervals
[] = {
206 { DEGRADED_WINDOW_MINS
, 1, 0, 0, { 0, 0 }},
207 { NORMAL_WINDOW_MINS
, 1, 0, 0, { 0, 0 }},
209 throttle_interval_t
*degraded_throttle_window
= &throttle_intervals
[0];
210 throttle_interval_t
*normal_throttle_window
= &throttle_intervals
[1];
212 extern uint64_t vm_swap_get_free_space(void);
213 extern boolean_t
vm_swap_max_budget(uint64_t *);
214 extern int i_coal_jetsam_get_taskrole(coalition_t coal
, task_t task
);
216 static void memorystatus_freeze_update_throttle(uint64_t *budget_pages_allowed
);
217 static void memorystatus_demote_frozen_processes(boolean_t force_one
);
219 static void memorystatus_freeze_handle_error(proc_t p
, const int freezer_error_code
, bool was_refreeze
, pid_t pid
, const coalition_t coalition
, const char* log_prefix
);
220 static void memorystatus_freeze_out_of_slots(void);
221 static uint64_t memorystatus_freezer_thread_next_run_ts
= 0;
223 /* Sysctls needed for aggd stats */
225 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_freeze_count
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_frozen_count
, 0, "");
226 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_thaw_count
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_thaw_count
, 0, "");
227 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_thaw_count_since_boot
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_thaw_count_since_boot
, "");
228 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_freeze_pageouts
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_freeze_pageouts
, "");
229 #if DEVELOPMENT || DEBUG
230 static int sysctl_memorystatus_freeze_budget_pages_remaining SYSCTL_HANDLER_ARGS
232 #pragma unused(arg1, arg2, oidp)
234 uint64_t new_budget
= memorystatus_freeze_budget_pages_remaining
;
235 mach_timespec_t now_ts
;
239 lck_mtx_lock(&freezer_mutex
);
241 error
= sysctl_io_number(req
, memorystatus_freeze_budget_pages_remaining
, sizeof(uint64_t), &new_budget
, &changed
);
243 /* Start a new interval with this budget. */
244 clock_get_system_nanotime(&sec
, &nsec
);
245 now_ts
.tv_sec
= (unsigned int)(MIN(sec
, UINT32_MAX
));
246 now_ts
.tv_nsec
= nsec
;
247 memorystatus_freeze_start_normal_throttle_interval((uint32_t) MIN(new_budget
, UINT32_MAX
), now_ts
);
248 /* Don't carry over any excess pageouts since we're forcing a new budget */
249 normal_throttle_window
->pageouts
= 0;
250 memorystatus_freeze_budget_pages_remaining
= normal_throttle_window
->max_pageouts
;
253 lck_mtx_unlock(&freezer_mutex
);
257 SYSCTL_PROC(_kern
, OID_AUTO
, memorystatus_freeze_budget_pages_remaining
, CTLTYPE_QUAD
| CTLFLAG_RW
| CTLFLAG_LOCKED
, 0, 0, &sysctl_memorystatus_freeze_budget_pages_remaining
, "Q", "");
258 #else /* DEVELOPMENT || DEBUG */
259 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_freeze_budget_pages_remaining
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_freeze_budget_pages_remaining
, "");
260 #endif /* DEVELOPMENT || DEBUG */
261 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_freezer_error_excess_shared_memory_count
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_freezer_stats
.mfs_error_excess_shared_memory_count
, "");
262 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_freezer_error_low_private_shared_ratio_count
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_freezer_stats
.mfs_error_low_private_shared_ratio_count
, "");
263 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_freezer_error_no_compressor_space_count
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_freezer_stats
.mfs_error_no_compressor_space_count
, "");
264 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_freezer_error_no_swap_space_count
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_freezer_stats
.mfs_error_no_swap_space_count
, "");
265 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_freezer_error_below_min_pages_count
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_freezer_stats
.mfs_error_below_min_pages_count
, "");
266 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_freezer_error_low_probability_of_use_count
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_freezer_stats
.mfs_error_low_probability_of_use_count
, "");
267 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_freezer_error_other_count
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_freezer_stats
.mfs_error_other_count
, "");
268 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_freezer_process_considered_count
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_freezer_stats
.mfs_process_considered_count
, "");
269 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_freezer_below_threshold_count
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_freezer_stats
.mfs_below_threshold_count
, "");
270 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_freezer_skipped_full_count
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_freezer_stats
.mfs_skipped_full_count
, "");
271 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_freezer_skipped_shared_mb_high_count
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_freezer_stats
.mfs_skipped_shared_mb_high_count
, "");
272 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_freezer_shared_pages_skipped
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_freezer_stats
.mfs_shared_pages_skipped
, "");
273 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_freezer_bytes_refrozen
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_freezer_stats
.mfs_bytes_refrozen
, "");
274 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_freezer_refreeze_count
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_freezer_stats
.mfs_refreeze_count
, "");
276 static_assert(_kMemorystatusFreezeSkipReasonMax
<= UINT8_MAX
);
280 * Calculates the hit rate for the freezer.
281 * The hit rate is defined as the percentage of procs that are currently in the
282 * freezer which we have thawed.
283 * A low hit rate means we're freezing bad candidates since they're not re-used.
285 static int sysctl_memorystatus_freezer_thaw_percentage SYSCTL_HANDLER_ARGS
287 #pragma unused(arg1, arg2)
288 size_t thaw_count
= 0, frozen_count
= 0;
289 int thaw_percentage
= 100;
290 unsigned int band
= (unsigned int) memorystatus_freeze_jetsam_band
;
291 proc_t p
= PROC_NULL
;
294 p
= memorystatus_get_first_proc_locked(&band
, FALSE
);
297 if (p
->p_memstat_state
& P_MEMSTAT_FROZEN
) {
298 if (p
->p_memstat_thaw_count
> 0) {
303 p
= memorystatus_get_next_proc_locked(&band
, p
, FALSE
);
306 if (frozen_count
> 0) {
307 assert(thaw_count
<= frozen_count
);
308 thaw_percentage
= (int)(100 * thaw_count
/ frozen_count
);
310 return sysctl_handle_int(oidp
, &thaw_percentage
, 0, req
);
312 SYSCTL_PROC(_kern
, OID_AUTO
, memorystatus_freezer_thaw_percentage
, CTLTYPE_INT
| CTLFLAG_RD
| CTLFLAG_LOCKED
, 0, 0, &sysctl_memorystatus_freezer_thaw_percentage
, "I", "");
314 #define FREEZER_ERROR_STRING_LENGTH 128
316 #if DEVELOPMENT || DEBUG
318 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_freeze_jetsam_band
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &memorystatus_freeze_jetsam_band
, 0, "");
319 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_freeze_daily_mb_max
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &memorystatus_freeze_daily_mb_max
, 0, "");
320 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_freeze_degraded_mode
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_freeze_degradation
, 0, "");
321 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_freeze_threshold
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &memorystatus_freeze_threshold
, 0, "");
322 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_freeze_pages_min
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &memorystatus_freeze_pages_min
, 0, "");
323 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_freeze_pages_max
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &memorystatus_freeze_pages_max
, 0, "");
324 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_refreeze_eligible_count
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_refreeze_eligible_count
, 0, "");
325 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_freeze_processes_max
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &memorystatus_frozen_processes_max
, 0, "");
328 * Max. shared-anonymous memory in MB that can be held by frozen processes in the high jetsam band.
329 * "0" means no limit.
330 * Default is 10% of system-wide task limit.
333 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_freeze_shared_mb_max
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &memorystatus_frozen_shared_mb_max
, 0, "");
334 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_freeze_shared_mb
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &memorystatus_frozen_shared_mb
, 0, "");
336 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_freeze_shared_mb_per_process_max
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &memorystatus_freeze_shared_mb_per_process_max
, 0, "");
337 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_freeze_private_shared_pages_ratio
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &memorystatus_freeze_private_shared_pages_ratio
, 0, "");
339 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_freeze_min_processes
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &memorystatus_freeze_suspended_threshold
, 0, "");
342 * max. # of frozen process demotions we will allow in our daily cycle.
344 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_max_freeze_demotions_daily
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &memorystatus_max_frozen_demotions_daily
, 0, "");
346 * min # of thaws needed by a process to protect it from getting demoted into the IDLE band.
348 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_thaw_count_demotion_threshold
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &memorystatus_thaw_count_demotion_threshold
, 0, "");
350 boolean_t memorystatus_freeze_throttle_enabled
= TRUE
;
351 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_freeze_throttle_enabled
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &memorystatus_freeze_throttle_enabled
, 0, "");
354 * When set to true, this keeps frozen processes in the compressor pool in memory, instead of swapping them out to disk.
355 * Exposed via the sysctl kern.memorystatus_freeze_to_memory.
357 boolean_t memorystatus_freeze_to_memory
= FALSE
;
358 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_freeze_to_memory
, CTLFLAG_RW
| CTLFLAG_LOCKED
, &memorystatus_freeze_to_memory
, 0, "");
360 #define VM_PAGES_FOR_ALL_PROCS (2)
363 * Manual trigger of freeze and thaw for dev / debug kernels only.
366 sysctl_memorystatus_freeze SYSCTL_HANDLER_ARGS
368 #pragma unused(arg1, arg2)
371 int freezer_error_code
= 0;
372 pid_t pid_list
[MAX_XPC_SERVICE_PIDS
];
374 coalition_t coal
= COALITION_NULL
;
376 if (memorystatus_freeze_enabled
== FALSE
) {
377 printf("sysctl_freeze: Freeze is DISABLED\n");
381 error
= sysctl_handle_int(oidp
, &pid
, 0, req
);
382 if (error
|| !req
->newptr
) {
386 if (pid
== VM_PAGES_FOR_ALL_PROCS
) {
387 vm_pageout_anonymous_pages();
392 lck_mtx_lock(&freezer_mutex
);
397 memorystatus_freezer_stats
.mfs_process_considered_count
++;
398 uint32_t purgeable
, wired
, clean
, dirty
, shared
;
399 uint32_t max_pages
= 0, state
= 0;
401 if (VM_CONFIG_FREEZER_SWAP_IS_ACTIVE
) {
403 * Freezer backed by the compressor and swap file(s)
404 * will hold compressed data.
406 * Set the sysctl kern.memorystatus_freeze_to_memory to true to keep compressed data from
407 * being swapped out to disk. Note that this disables freezer swap support globally,
408 * not just for the process being frozen.
411 * We don't care about the global freezer budget or the process's (min/max) budget here.
412 * The freeze sysctl is meant to force-freeze a process.
414 * We also don't update any global or process stats on this path, so that the jetsam/ freeze
415 * logic remains unaffected. The tasks we're performing here are: freeze the process, set the
416 * P_MEMSTAT_FROZEN bit, and elevate the process to a higher band (if the freezer is active).
418 max_pages
= memorystatus_freeze_pages_max
;
421 * We only have the compressor without any swap.
423 max_pages
= UINT32_MAX
- 1;
427 state
= p
->p_memstat_state
;
431 * The jetsam path also verifies that the process is a suspended App. We don't care about that here.
432 * We simply ensure that jetsam is not already working on the process and that the process has not
433 * explicitly disabled freezing.
435 if (state
& (P_MEMSTAT_TERMINATED
| P_MEMSTAT_LOCKED
| P_MEMSTAT_FREEZE_DISABLED
)) {
436 printf("sysctl_freeze: p_memstat_state check failed, process is%s%s%s\n",
437 (state
& P_MEMSTAT_TERMINATED
) ? " terminated" : "",
438 (state
& P_MEMSTAT_LOCKED
) ? " locked" : "",
439 (state
& P_MEMSTAT_FREEZE_DISABLED
) ? " unfreezable" : "");
442 lck_mtx_unlock(&freezer_mutex
);
446 error
= task_freeze(p
->task
, &purgeable
, &wired
, &clean
, &dirty
, max_pages
, &shared
, &freezer_error_code
, FALSE
/* eval only */);
447 if (!error
|| freezer_error_code
== FREEZER_ERROR_LOW_PRIVATE_SHARED_RATIO
) {
448 memorystatus_freezer_stats
.mfs_shared_pages_skipped
+= shared
;
452 memorystatus_freeze_handle_error(p
, freezer_error_code
, state
& P_MEMSTAT_FROZEN
, pid
, coal
, "sysctl_freeze");
453 if (error
== KERN_NO_SPACE
) {
454 /* Make it easy to distinguish between failures due to low compressor/ swap space and other failures. */
461 if ((p
->p_memstat_state
& P_MEMSTAT_FROZEN
) == 0) {
462 p
->p_memstat_state
|= P_MEMSTAT_FROZEN
;
463 p
->p_memstat_freeze_skip_reason
= kMemorystatusFreezeSkipReasonNone
;
464 memorystatus_frozen_count
++;
465 if (memorystatus_frozen_count
== memorystatus_frozen_processes_max
) {
466 memorystatus_freeze_out_of_slots();
469 // This was a re-freeze
470 if (VM_CONFIG_FREEZER_SWAP_IS_ACTIVE
) {
471 memorystatus_freezer_stats
.mfs_bytes_refrozen
+= dirty
* PAGE_SIZE
;
472 memorystatus_freezer_stats
.mfs_refreeze_count
++;
475 p
->p_memstat_frozen_count
++;
480 if (VM_CONFIG_FREEZER_SWAP_IS_ACTIVE
) {
482 * We elevate only if we are going to swap out the data.
484 error
= memorystatus_update_inactive_jetsam_priority_band(pid
, MEMORYSTATUS_CMD_ELEVATED_INACTIVEJETSAMPRIORITY_ENABLE
,
485 memorystatus_freeze_jetsam_band
, TRUE
);
488 printf("sysctl_freeze: Elevating frozen process to higher jetsam band failed with %d\n", error
);
493 if ((error
== 0) && (coal
== NULL
)) {
495 * We froze a process and so we check to see if it was
496 * a coalition leader and if it has XPC services that
497 * might need freezing.
498 * Only one leader can be frozen at a time and so we shouldn't
499 * enter this block more than once per call. Hence the
500 * check that 'coal' has to be NULL. We should make this an
501 * assert() or panic() once we have a much more concrete way
502 * to detect an app vs a daemon.
505 task_t curr_task
= NULL
;
507 curr_task
= proc_task(p
);
508 coal
= task_get_coalition(curr_task
, COALITION_TYPE_JETSAM
);
509 if (coalition_is_leader(curr_task
, coal
)) {
510 ntasks
= coalition_get_pid_list(coal
, COALITION_ROLEMASK_XPC
,
511 COALITION_SORT_DEFAULT
, pid_list
, MAX_XPC_SERVICE_PIDS
);
513 if (ntasks
> MAX_XPC_SERVICE_PIDS
) {
514 ntasks
= MAX_XPC_SERVICE_PIDS
;
522 pid
= pid_list
[--ntasks
];
526 lck_mtx_unlock(&freezer_mutex
);
529 printf("sysctl_freeze: Invalid process\n");
533 lck_mtx_unlock(&freezer_mutex
);
537 SYSCTL_PROC(_kern
, OID_AUTO
, memorystatus_freeze
, CTLTYPE_INT
| CTLFLAG_WR
| CTLFLAG_LOCKED
| CTLFLAG_MASKED
,
538 0, 0, &sysctl_memorystatus_freeze
, "I", "");
541 * Manual trigger of agressive frozen demotion for dev / debug kernels only.
544 sysctl_memorystatus_demote_frozen_process SYSCTL_HANDLER_ARGS
546 #pragma unused(arg1, arg2, oidp, req)
549 * Only demote on write to prevent demoting during `sysctl -a`.
550 * The actual value written doesn't matter.
552 error
= sysctl_handle_int(oidp
, &val
, 0, req
);
553 if (error
|| !req
->newptr
) {
556 memorystatus_demote_frozen_processes(false);
560 SYSCTL_PROC(_kern
, OID_AUTO
, memorystatus_demote_frozen_processes
, CTLTYPE_INT
| CTLFLAG_WR
| CTLFLAG_LOCKED
| CTLFLAG_MASKED
, 0, 0, &sysctl_memorystatus_demote_frozen_process
, "I", "");
563 sysctl_memorystatus_available_pages_thaw SYSCTL_HANDLER_ARGS
565 #pragma unused(arg1, arg2)
570 if (memorystatus_freeze_enabled
== FALSE
) {
574 error
= sysctl_handle_int(oidp
, &pid
, 0, req
);
575 if (error
|| !req
->newptr
) {
579 if (pid
== VM_PAGES_FOR_ALL_PROCS
) {
580 do_fastwake_warmup_all();
585 error
= task_thaw(p
->task
);
591 * task_thaw() succeeded.
593 * We increment memorystatus_frozen_count on the sysctl freeze path.
594 * And so we need the P_MEMSTAT_FROZEN to decrement the frozen count
595 * when this process exits.
598 * p->p_memstat_state &= ~P_MEMSTAT_FROZEN;
599 * proc_list_unlock();
610 SYSCTL_PROC(_kern
, OID_AUTO
, memorystatus_thaw
, CTLTYPE_INT
| CTLFLAG_WR
| CTLFLAG_LOCKED
| CTLFLAG_MASKED
,
611 0, 0, &sysctl_memorystatus_available_pages_thaw
, "I", "");
614 typedef struct _global_freezable_status
{
615 boolean_t freeze_pages_threshold_crossed
;
616 boolean_t freeze_eligible_procs_available
;
617 boolean_t freeze_scheduled_in_future
;
618 }global_freezable_status_t
;
620 typedef struct _proc_freezable_status
{
621 boolean_t freeze_has_memstat_state
;
622 boolean_t freeze_has_pages_min
;
623 int freeze_has_probability
;
624 int freeze_leader_eligible
;
625 boolean_t freeze_attempted
;
626 uint32_t p_memstat_state
;
628 int p_freeze_error_code
;
631 char p_name
[MAXCOMLEN
+ 1];
632 }proc_freezable_status_t
;
634 #define MAX_FREEZABLE_PROCESSES 200 /* Total # of processes in band 0 that we evaluate for freezability */
637 * For coalition based freezing evaluations, we proceed as follows:
638 * - detect that the process is a coalition member and a XPC service
639 * - mark its 'freeze_leader_eligible' field with FREEZE_PROC_LEADER_FREEZABLE_UNKNOWN
640 * - continue its freezability evaluation assuming its leader will be freezable too
642 * Once we are done evaluating all processes, we do a quick run thru all
643 * processes and for a coalition member XPC service we look up the 'freezable'
644 * status of its leader and iff:
645 * - the xpc service is freezable i.e. its individual freeze evaluation worked
646 * - and, its leader is also marked freezable
647 * we update its 'freeze_leader_eligible' to FREEZE_PROC_LEADER_FREEZABLE_SUCCESS.
650 #define FREEZE_PROC_LEADER_FREEZABLE_UNKNOWN (-1)
651 #define FREEZE_PROC_LEADER_FREEZABLE_SUCCESS (1)
652 #define FREEZE_PROC_LEADER_FREEZABLE_FAILURE (2)
655 memorystatus_freezer_get_status(user_addr_t buffer
, size_t buffer_size
, int32_t *retval
)
657 uint32_t proc_count
= 0, freeze_eligible_proc_considered
= 0, band
= 0, xpc_index
= 0, leader_index
= 0;
658 global_freezable_status_t
*list_head
;
659 proc_freezable_status_t
*list_entry
, *list_entry_start
;
660 size_t list_size
= 0, entry_count
= 0;
661 proc_t p
, leader_proc
;
662 memstat_bucket_t
*bucket
;
663 uint32_t state
= 0, pages
= 0;
664 boolean_t try_freeze
= TRUE
, xpc_skip_size_probability_check
= FALSE
;
665 int error
= 0, probability_of_use
= 0;
666 pid_t leader_pid
= 0;
669 if (VM_CONFIG_FREEZER_SWAP_IS_ACTIVE
== FALSE
) {
673 list_size
= sizeof(global_freezable_status_t
) + (sizeof(proc_freezable_status_t
) * MAX_FREEZABLE_PROCESSES
);
675 if (buffer_size
< list_size
) {
679 list_head
= kheap_alloc(KHEAP_TEMP
, list_size
, Z_WAITOK
| Z_ZERO
);
680 if (list_head
== NULL
) {
684 list_size
= sizeof(global_freezable_status_t
);
688 uint64_t curr_time
= mach_absolute_time();
690 list_head
->freeze_pages_threshold_crossed
= (memorystatus_available_pages
< memorystatus_freeze_threshold
);
691 list_head
->freeze_eligible_procs_available
= ((memorystatus_suspended_count
- memorystatus_frozen_count
) > memorystatus_freeze_suspended_threshold
);
692 list_head
->freeze_scheduled_in_future
= (curr_time
< memorystatus_freezer_thread_next_run_ts
);
694 list_entry_start
= (proc_freezable_status_t
*) ((uintptr_t)list_head
+ sizeof(global_freezable_status_t
));
695 list_entry
= list_entry_start
;
697 bucket
= &memstat_bucket
[JETSAM_PRIORITY_IDLE
];
699 entry_count
= (memorystatus_global_probabilities_size
/ sizeof(memorystatus_internal_probabilities_t
));
701 p
= memorystatus_get_first_proc_locked(&band
, FALSE
);
704 while ((proc_count
<= MAX_FREEZABLE_PROCESSES
) &&
706 (list_size
< buffer_size
)) {
709 * Daemon:- We will consider freezing it iff:
710 * - it belongs to a coalition and the leader is freeze-eligible (delayed evaluation)
711 * - its role in the coalition is XPC service.
713 * We skip memory size requirements in this case.
716 coalition_t coal
= COALITION_NULL
;
717 task_t leader_task
= NULL
, curr_task
= NULL
;
718 int task_role_in_coalition
= 0;
720 curr_task
= proc_task(p
);
721 coal
= task_get_coalition(curr_task
, COALITION_TYPE_JETSAM
);
723 if (coal
== COALITION_NULL
|| coalition_is_leader(curr_task
, coal
)) {
725 * By default, XPC services without an app
726 * will be the leader of their own single-member
729 goto skip_ineligible_xpc
;
732 leader_task
= coalition_get_leader(coal
);
733 if (leader_task
== TASK_NULL
) {
735 * This jetsam coalition is currently leader-less.
736 * This could happen if the app died, but XPC services
737 * have not yet exited.
739 goto skip_ineligible_xpc
;
742 leader_proc
= (proc_t
)get_bsdtask_info(leader_task
);
743 task_deallocate(leader_task
);
745 if (leader_proc
== PROC_NULL
) {
746 /* leader task is exiting */
747 goto skip_ineligible_xpc
;
750 task_role_in_coalition
= i_coal_jetsam_get_taskrole(coal
, curr_task
);
752 if (task_role_in_coalition
== COALITION_TASKROLE_XPC
) {
753 xpc_skip_size_probability_check
= TRUE
;
754 leader_pid
= leader_proc
->p_pid
;
759 p
= memorystatus_get_next_proc_locked(&band
, p
, FALSE
);
765 strlcpy(list_entry
->p_name
, p
->p_name
, MAXCOMLEN
+ 1);
767 list_entry
->p_pid
= p
->p_pid
;
769 state
= p
->p_memstat_state
;
771 if ((state
& (P_MEMSTAT_TERMINATED
| P_MEMSTAT_LOCKED
| P_MEMSTAT_FREEZE_DISABLED
| P_MEMSTAT_FREEZE_IGNORE
)) ||
772 !(state
& P_MEMSTAT_SUSPENDED
)) {
773 try_freeze
= list_entry
->freeze_has_memstat_state
= FALSE
;
775 try_freeze
= list_entry
->freeze_has_memstat_state
= TRUE
;
778 list_entry
->p_memstat_state
= state
;
780 if (xpc_skip_size_probability_check
== TRUE
) {
782 * Assuming the coalition leader is freezable
783 * we don't care re. minimum pages and probability
784 * as long as the process isn't marked P_MEMSTAT_FREEZE_DISABLED.
785 * XPC services have to be explicity opted-out of the disabled
786 * state. And we checked that state above.
788 list_entry
->freeze_has_pages_min
= TRUE
;
789 list_entry
->p_pages
= -1;
790 list_entry
->freeze_has_probability
= -1;
792 list_entry
->freeze_leader_eligible
= FREEZE_PROC_LEADER_FREEZABLE_UNKNOWN
;
793 list_entry
->p_leader_pid
= leader_pid
;
795 xpc_skip_size_probability_check
= FALSE
;
797 list_entry
->freeze_leader_eligible
= FREEZE_PROC_LEADER_FREEZABLE_SUCCESS
; /* Apps are freeze eligible and their own leaders. */
798 list_entry
->p_leader_pid
= 0; /* Setting this to 0 signifies this isn't a coalition driven freeze. */
800 memorystatus_get_task_page_counts(p
->task
, &pages
, NULL
, NULL
);
801 if (pages
< memorystatus_freeze_pages_min
) {
802 try_freeze
= list_entry
->freeze_has_pages_min
= FALSE
;
804 list_entry
->freeze_has_pages_min
= TRUE
;
807 list_entry
->p_pages
= pages
;
811 for (j
= 0; j
< entry_count
; j
++) {
812 if (strncmp(memorystatus_global_probabilities_table
[j
].proc_name
,
814 MAXCOMLEN
+ 1) == 0) {
815 probability_of_use
= memorystatus_global_probabilities_table
[j
].use_probability
;
820 list_entry
->freeze_has_probability
= probability_of_use
;
822 try_freeze
= ((probability_of_use
> 0) && try_freeze
);
824 list_entry
->freeze_has_probability
= -1;
829 uint32_t purgeable
, wired
, clean
, dirty
, shared
;
830 uint32_t max_pages
= 0;
831 int freezer_error_code
= 0;
833 error
= task_freeze(p
->task
, &purgeable
, &wired
, &clean
, &dirty
, max_pages
, &shared
, &freezer_error_code
, TRUE
/* eval only */);
836 list_entry
->p_freeze_error_code
= freezer_error_code
;
839 list_entry
->freeze_attempted
= TRUE
;
843 freeze_eligible_proc_considered
++;
845 list_size
+= sizeof(proc_freezable_status_t
);
847 p
= memorystatus_get_next_proc_locked(&band
, p
, FALSE
);
853 list_entry
= list_entry_start
;
855 for (xpc_index
= 0; xpc_index
< freeze_eligible_proc_considered
; xpc_index
++) {
856 if (list_entry
[xpc_index
].freeze_leader_eligible
== FREEZE_PROC_LEADER_FREEZABLE_UNKNOWN
) {
857 leader_pid
= list_entry
[xpc_index
].p_leader_pid
;
859 leader_proc
= proc_find(leader_pid
);
862 if (leader_proc
->p_memstat_state
& P_MEMSTAT_FROZEN
) {
864 * Leader has already been frozen.
866 list_entry
[xpc_index
].freeze_leader_eligible
= FREEZE_PROC_LEADER_FREEZABLE_SUCCESS
;
867 proc_rele(leader_proc
);
870 proc_rele(leader_proc
);
873 for (leader_index
= 0; leader_index
< freeze_eligible_proc_considered
; leader_index
++) {
874 if (list_entry
[leader_index
].p_pid
== leader_pid
) {
875 if (list_entry
[leader_index
].freeze_attempted
&& list_entry
[leader_index
].p_freeze_error_code
== 0) {
876 list_entry
[xpc_index
].freeze_leader_eligible
= FREEZE_PROC_LEADER_FREEZABLE_SUCCESS
;
878 list_entry
[xpc_index
].freeze_leader_eligible
= FREEZE_PROC_LEADER_FREEZABLE_FAILURE
;
879 list_entry
[xpc_index
].p_freeze_error_code
= FREEZER_ERROR_GENERIC
;
886 * Didn't find the leader entry. This might be likely because
887 * the leader never made it down to band 0.
889 if (leader_index
== freeze_eligible_proc_considered
) {
890 list_entry
[xpc_index
].freeze_leader_eligible
= FREEZE_PROC_LEADER_FREEZABLE_FAILURE
;
891 list_entry
[xpc_index
].p_freeze_error_code
= FREEZER_ERROR_GENERIC
;
896 buffer_size
= MIN(list_size
, INT32_MAX
);
898 error
= copyout(list_head
, buffer
, buffer_size
);
900 *retval
= (int32_t) buffer_size
;
905 list_size
= sizeof(global_freezable_status_t
) + (sizeof(proc_freezable_status_t
) * MAX_FREEZABLE_PROCESSES
);
906 kheap_free(KHEAP_TEMP
, list_head
, list_size
);
908 MEMORYSTATUS_DEBUG(1, "memorystatus_freezer_get_status: returning %d (%lu - size)\n", error
, (unsigned long)*list_size
);
913 #endif /* DEVELOPMENT || DEBUG */
916 * Get a list of all processes in the freezer band which are currently frozen.
917 * Used by powerlog to collect analytics on frozen process.
920 memorystatus_freezer_get_procs(user_addr_t buffer
, size_t buffer_size
, int32_t *retval
)
922 global_frozen_procs_t
*frozen_procs
= NULL
;
923 uint32_t band
= memorystatus_freeze_jetsam_band
;
927 if (VM_CONFIG_FREEZER_SWAP_IS_ACTIVE
== FALSE
) {
930 if (buffer_size
< sizeof(global_frozen_procs_t
)) {
933 frozen_procs
= kheap_alloc(KHEAP_TEMP
, sizeof(global_frozen_procs_t
),
935 if (frozen_procs
== NULL
) {
940 p
= memorystatus_get_first_proc_locked(&band
, FALSE
);
941 while (p
&& frozen_procs
->gfp_num_frozen
< FREEZER_CONTROL_GET_PROCS_MAX_COUNT
) {
942 state
= p
->p_memstat_state
;
943 if (state
& P_MEMSTAT_FROZEN
) {
944 frozen_procs
->gfp_procs
[frozen_procs
->gfp_num_frozen
].fp_pid
= p
->p_pid
;
945 strlcpy(frozen_procs
->gfp_procs
[frozen_procs
->gfp_num_frozen
].fp_name
,
946 p
->p_name
, sizeof(proc_name_t
));
947 frozen_procs
->gfp_num_frozen
++;
949 p
= memorystatus_get_next_proc_locked(&band
, p
, FALSE
);
953 buffer_size
= MIN(buffer_size
, sizeof(global_frozen_procs_t
));
954 error
= copyout(frozen_procs
, buffer
, buffer_size
);
956 *retval
= (int32_t) buffer_size
;
960 kheap_free(KHEAP_TEMP
, frozen_procs
, sizeof(global_frozen_procs_t
));
966 memorystatus_freezer_control(int32_t flags
, user_addr_t buffer
, size_t buffer_size
, int32_t *retval
)
970 #if DEVELOPMENT || DEBUG
971 if (flags
== FREEZER_CONTROL_GET_STATUS
) {
972 err
= memorystatus_freezer_get_status(buffer
, buffer_size
, retval
);
974 #endif /* DEVELOPMENT || DEBUG */
975 if (flags
== FREEZER_CONTROL_GET_PROCS
) {
976 err
= memorystatus_freezer_get_procs(buffer
, buffer_size
, retval
);
982 extern void vm_swap_consider_defragmenting(int);
983 extern boolean_t
memorystatus_kill_elevated_process(uint32_t, os_reason_t
, unsigned int, int, uint32_t *, uint64_t *);
986 * This routine will _jetsam_ all frozen processes
987 * and reclaim the swap space immediately.
989 * So freeze has to be DISABLED when we call this routine.
993 memorystatus_disable_freeze(void)
995 memstat_bucket_t
*bucket
;
996 int bucket_count
= 0, retries
= 0;
997 boolean_t retval
= FALSE
, killed
= FALSE
;
998 uint32_t errors
= 0, errors_over_prev_iteration
= 0;
999 os_reason_t jetsam_reason
= 0;
1000 unsigned int band
= 0;
1001 proc_t p
= PROC_NULL
, next_p
= PROC_NULL
;
1002 uint64_t memory_reclaimed
= 0, footprint
= 0;
1004 KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT
, BSD_MEMSTAT_FREEZE_DISABLE
) | DBG_FUNC_START
,
1005 memorystatus_available_pages
, 0, 0, 0, 0);
1007 assert(memorystatus_freeze_enabled
== FALSE
);
1009 jetsam_reason
= os_reason_create(OS_REASON_JETSAM
, JETSAM_REASON_MEMORY_DISK_SPACE_SHORTAGE
);
1010 if (jetsam_reason
== OS_REASON_NULL
) {
1011 printf("memorystatus_disable_freeze: failed to allocate jetsam reason\n");
1015 * Let's relocate all frozen processes into band 8. Demoted frozen processes
1016 * are sitting in band 0 currently and it's possible to have a frozen process
1017 * in the FG band being actively used. We don't reset its frozen state when
1018 * it is resumed because it has state on disk.
1020 * We choose to do this relocation rather than implement a new 'kill frozen'
1021 * process function for these reasons:
1022 * - duplication of code: too many kill functions exist and we need to rework them better.
1023 * - disk-space-shortage kills are rare
1024 * - not having the 'real' jetsam band at time of the this frozen kill won't preclude us
1025 * from answering any imp. questions re. jetsam policy/effectiveness.
1027 * This is essentially what memorystatus_update_inactive_jetsam_priority_band() does while
1028 * avoiding the application of memory limits.
1034 band
= JETSAM_PRIORITY_IDLE
;
1038 next_p
= memorystatus_get_first_proc_locked(&band
, TRUE
);
1041 next_p
= memorystatus_get_next_proc_locked(&band
, p
, TRUE
);
1043 if (p
->p_memstat_effectivepriority
> JETSAM_PRIORITY_FOREGROUND
) {
1047 if ((p
->p_memstat_state
& P_MEMSTAT_FROZEN
) == FALSE
) {
1051 if (p
->p_memstat_state
& P_MEMSTAT_ERROR
) {
1052 p
->p_memstat_state
&= ~P_MEMSTAT_ERROR
;
1055 if (p
->p_memstat_effectivepriority
== memorystatus_freeze_jetsam_band
) {
1060 * We explicitly add this flag here so the process looks like a normal
1061 * frozen process i.e. P_MEMSTAT_FROZEN and P_MEMSTAT_USE_ELEVATED_INACTIVE_BAND.
1062 * We don't bother with assigning the 'active' memory
1063 * limits at this point because we are going to be killing it soon below.
1065 p
->p_memstat_state
|= P_MEMSTAT_USE_ELEVATED_INACTIVE_BAND
;
1066 memorystatus_invalidate_idle_demotion_locked(p
, TRUE
);
1068 memorystatus_update_priority_locked(p
, memorystatus_freeze_jetsam_band
, FALSE
, TRUE
);
1071 bucket
= &memstat_bucket
[memorystatus_freeze_jetsam_band
];
1072 bucket_count
= bucket
->count
;
1076 * Bucket count is already stale at this point. But, we don't expect
1077 * freezing to continue since we have already disabled the freeze functionality.
1078 * However, an existing freeze might be in progress. So we might miss that process
1079 * in the first go-around. We hope to catch it in the next.
1082 errors_over_prev_iteration
= 0;
1083 while (bucket_count
) {
1087 * memorystatus_kill_elevated_process() drops a reference,
1088 * so take another one so we can continue to use this exit reason
1089 * even after it returns.
1092 os_reason_ref(jetsam_reason
);
1093 retval
= memorystatus_kill_elevated_process(
1094 kMemorystatusKilledDiskSpaceShortage
,
1096 memorystatus_freeze_jetsam_band
,
1097 0, /* the iteration of aggressive jetsam..ignored here */
1102 printf("memorystatus_disable_freeze: memorystatus_kill_elevated_process returned %d error(s)\n", errors
);
1103 errors_over_prev_iteration
+= errors
;
1109 * No frozen processes left to kill.
1115 memory_reclaimed
+= footprint
;
1120 if (memorystatus_frozen_count
) {
1122 * A frozen process snuck in and so
1123 * go back around to kill it. That
1124 * process may have been resumed and
1125 * put into the FG band too. So we
1126 * have to do the relocation again.
1128 assert(memorystatus_freeze_enabled
== FALSE
);
1135 #if DEVELOPMENT || DEBUG
1136 panic("memorystatus_disable_freeze: Failed to kill all frozen processes, memorystatus_frozen_count = %d, errors = %d",
1137 memorystatus_frozen_count
, errors_over_prev_iteration
);
1138 #endif /* DEVELOPMENT || DEBUG */
1142 os_reason_free(jetsam_reason
);
1145 vm_swap_consider_defragmenting(VM_SWAP_FLAGS_FORCE_DEFRAG
| VM_SWAP_FLAGS_FORCE_RECLAIM
);
1148 size_t snapshot_size
= sizeof(memorystatus_jetsam_snapshot_t
) +
1149 sizeof(memorystatus_jetsam_snapshot_entry_t
) * (memorystatus_jetsam_snapshot_count
);
1150 uint64_t timestamp_now
= mach_absolute_time();
1151 memorystatus_jetsam_snapshot
->notification_time
= timestamp_now
;
1152 memorystatus_jetsam_snapshot
->js_gencount
++;
1153 if (memorystatus_jetsam_snapshot_count
> 0 && (memorystatus_jetsam_snapshot_last_timestamp
== 0 ||
1154 timestamp_now
> memorystatus_jetsam_snapshot_last_timestamp
+ memorystatus_jetsam_snapshot_timeout
)) {
1156 int ret
= memorystatus_send_note(kMemorystatusSnapshotNote
, &snapshot_size
, sizeof(snapshot_size
));
1159 memorystatus_jetsam_snapshot_last_timestamp
= timestamp_now
;
1167 KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT
, BSD_MEMSTAT_FREEZE_DISABLE
) | DBG_FUNC_END
,
1168 memorystatus_available_pages
, memory_reclaimed
, 0, 0, 0);
1173 __private_extern__
void
1174 memorystatus_freeze_init(void)
1176 kern_return_t result
;
1179 freezer_lck_grp_attr
= lck_grp_attr_alloc_init();
1180 freezer_lck_grp
= lck_grp_alloc_init("freezer", freezer_lck_grp_attr
);
1182 lck_mtx_init(&freezer_mutex
, freezer_lck_grp
, NULL
);
1185 * This is just the default value if the underlying
1186 * storage device doesn't have any specific budget.
1187 * We check with the storage layer in memorystatus_freeze_update_throttle()
1188 * before we start our freezing the first time.
1190 memorystatus_freeze_budget_pages_remaining
= (memorystatus_freeze_daily_mb_max
* 1024 * 1024) / PAGE_SIZE
;
1192 result
= kernel_thread_start(memorystatus_freeze_thread
, NULL
, &thread
);
1193 if (result
== KERN_SUCCESS
) {
1194 proc_set_thread_policy(thread
, TASK_POLICY_INTERNAL
, TASK_POLICY_IO
, THROTTLE_LEVEL_COMPRESSOR_TIER2
);
1195 proc_set_thread_policy(thread
, TASK_POLICY_INTERNAL
, TASK_POLICY_PASSIVE_IO
, TASK_POLICY_ENABLE
);
1196 thread_set_thread_name(thread
, "VM_freezer");
1198 thread_deallocate(thread
);
1200 panic("Could not create memorystatus_freeze_thread");
1205 memorystatus_is_process_eligible_for_freeze(proc_t p
)
1208 * Called with proc_list_lock held.
1211 LCK_MTX_ASSERT(proc_list_mlock
, LCK_MTX_ASSERT_OWNED
);
1213 boolean_t should_freeze
= FALSE
;
1214 uint32_t state
= 0, pages
= 0;
1215 int probability_of_use
= 0;
1216 size_t entry_count
= 0, i
= 0;
1217 bool first_consideration
= true;
1219 state
= p
->p_memstat_state
;
1221 if (state
& (P_MEMSTAT_TERMINATED
| P_MEMSTAT_LOCKED
| P_MEMSTAT_FREEZE_DISABLED
| P_MEMSTAT_FREEZE_IGNORE
)) {
1222 if (state
& P_MEMSTAT_FREEZE_DISABLED
) {
1223 p
->p_memstat_freeze_skip_reason
= kMemorystatusFreezeSkipReasonDisabled
;
1230 * Daemon:- We consider freezing it if:
1231 * - it belongs to a coalition and the leader is frozen, and,
1232 * - its role in the coalition is XPC service.
1234 * We skip memory size requirements in this case.
1237 coalition_t coal
= COALITION_NULL
;
1238 task_t leader_task
= NULL
, curr_task
= NULL
;
1239 proc_t leader_proc
= NULL
;
1240 int task_role_in_coalition
= 0;
1242 curr_task
= proc_task(p
);
1243 coal
= task_get_coalition(curr_task
, COALITION_TYPE_JETSAM
);
1245 if (coal
== NULL
|| coalition_is_leader(curr_task
, coal
)) {
1247 * By default, XPC services without an app
1248 * will be the leader of their own single-member
1254 leader_task
= coalition_get_leader(coal
);
1255 if (leader_task
== TASK_NULL
) {
1257 * This jetsam coalition is currently leader-less.
1258 * This could happen if the app died, but XPC services
1259 * have not yet exited.
1264 leader_proc
= (proc_t
)get_bsdtask_info(leader_task
);
1265 task_deallocate(leader_task
);
1267 if (leader_proc
== PROC_NULL
) {
1268 /* leader task is exiting */
1272 if (!(leader_proc
->p_memstat_state
& P_MEMSTAT_FROZEN
)) {
1276 task_role_in_coalition
= i_coal_jetsam_get_taskrole(coal
, curr_task
);
1278 if (task_role_in_coalition
== COALITION_TASKROLE_XPC
) {
1279 should_freeze
= TRUE
;
1285 * Application. In addition to the above states we need to make
1286 * sure we only consider suspended applications for freezing.
1288 if (!(state
& P_MEMSTAT_SUSPENDED
)) {
1294 * This proc is a suspended application.
1295 * We're interested in tracking what percentage of these
1296 * actually get frozen.
1297 * To avoid skewing the metrics towards processes which
1298 * are considered more frequently, we only track failures once
1301 first_consideration
= !(state
& P_MEMSTAT_FREEZE_CONSIDERED
);
1303 if (first_consideration
) {
1304 memorystatus_freezer_stats
.mfs_process_considered_count
++;
1305 p
->p_memstat_state
|= P_MEMSTAT_FREEZE_CONSIDERED
;
1308 /* Only freeze applications meeting our minimum resident page criteria */
1309 memorystatus_get_task_page_counts(p
->task
, &pages
, NULL
, NULL
);
1310 if (pages
< memorystatus_freeze_pages_min
) {
1311 if (first_consideration
) {
1312 memorystatus_freezer_stats
.mfs_error_below_min_pages_count
++;
1314 p
->p_memstat_freeze_skip_reason
= kMemorystatusFreezeSkipReasonBelowMinPages
;
1318 /* Don't freeze processes that are already exiting on core. It may have started exiting
1319 * after we chose it for freeze, but before we obtained the proc_list_lock.
1320 * NB: This is only possible if we're coming in from memorystatus_freeze_process_sync.
1321 * memorystatus_freeze_top_process holds the proc_list_lock while it traverses the bands.
1323 if ((p
->p_listflag
& P_LIST_EXITED
) != 0) {
1324 if (first_consideration
) {
1325 memorystatus_freezer_stats
.mfs_error_other_count
++;
1327 p
->p_memstat_freeze_skip_reason
= kMemorystatusFreezeSkipReasonOther
;
1331 entry_count
= (memorystatus_global_probabilities_size
/ sizeof(memorystatus_internal_probabilities_t
));
1334 for (i
= 0; i
< entry_count
; i
++) {
1335 if (strncmp(memorystatus_global_probabilities_table
[i
].proc_name
,
1337 MAXCOMLEN
+ 1) == 0) {
1338 probability_of_use
= memorystatus_global_probabilities_table
[i
].use_probability
;
1343 if (probability_of_use
== 0) {
1344 if (first_consideration
) {
1345 memorystatus_freezer_stats
.mfs_error_low_probability_of_use_count
++;
1347 p
->p_memstat_freeze_skip_reason
= kMemorystatusFreezeSkipReasonLowProbOfUse
;
1352 should_freeze
= TRUE
;
1354 if (should_freeze
&& !(state
& P_MEMSTAT_FROZEN
)) {
1356 * Reset the skip reason. If it's killed before we manage to actually freeze it
1357 * we failed to consider it early enough.
1359 p
->p_memstat_freeze_skip_reason
= kMemorystatusFreezeSkipReasonNone
;
1360 if (!first_consideration
) {
1362 * We're freezing this for the first time and we previously considered it ineligible.
1363 * Bump the considered count so that we track this as 1 failure
1366 memorystatus_freezer_stats
.mfs_process_considered_count
++;
1369 return should_freeze
;
1373 * Synchronously freeze the passed proc. Called with a reference to the proc held.
1375 * Doesn't deal with:
1376 * - re-freezing because this is called on a specific process and
1377 * not by the freezer thread. If that changes, we'll have to teach it about
1378 * refreezing a frozen process.
1380 * - grouped/coalition freezing because we are hoping to deprecate this
1381 * interface as it was used by user-space to freeze particular processes. But
1382 * we have moved away from that approach to having the kernel choose the optimal
1383 * candidates to be frozen.
1385 * Returns EINVAL or the value returned by task_freeze().
1388 memorystatus_freeze_process_sync(proc_t p
)
1392 boolean_t memorystatus_freeze_swap_low
= FALSE
;
1393 int freezer_error_code
= 0;
1395 lck_mtx_lock(&freezer_mutex
);
1398 printf("memorystatus_freeze_process_sync: Invalid process\n");
1402 if (memorystatus_freeze_enabled
== FALSE
) {
1403 printf("memorystatus_freeze_process_sync: Freezing is DISABLED\n");
1407 if (!memorystatus_can_freeze(&memorystatus_freeze_swap_low
)) {
1408 printf("memorystatus_freeze_process_sync: Low compressor and/or low swap space...skipping freeze\n");
1412 memorystatus_freeze_update_throttle(&memorystatus_freeze_budget_pages_remaining
);
1413 if (!memorystatus_freeze_budget_pages_remaining
) {
1414 printf("memorystatus_freeze_process_sync: exit with NO available budget\n");
1421 uint32_t purgeable
, wired
, clean
, dirty
, shared
;
1427 /* Ensure the process is eligible for freezing */
1428 if (memorystatus_is_process_eligible_for_freeze(p
) == FALSE
) {
1433 if (VM_CONFIG_FREEZER_SWAP_IS_ACTIVE
) {
1434 max_pages
= MIN(memorystatus_freeze_pages_max
, memorystatus_freeze_budget_pages_remaining
);
1437 * We only have the compressor without any swap.
1439 max_pages
= UINT32_MAX
- 1;
1442 /* Mark as locked temporarily to avoid kill */
1443 p
->p_memstat_state
|= P_MEMSTAT_LOCKED
;
1446 KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT
, BSD_MEMSTAT_FREEZE
) | DBG_FUNC_START
,
1447 memorystatus_available_pages
, 0, 0, 0, 0);
1449 max_pages
= MIN(max_pages
, UINT32_MAX
);
1450 ret
= task_freeze(p
->task
, &purgeable
, &wired
, &clean
, &dirty
, (uint32_t) max_pages
, &shared
, &freezer_error_code
, FALSE
/* eval only */);
1451 if (ret
== KERN_SUCCESS
|| freezer_error_code
== FREEZER_ERROR_LOW_PRIVATE_SHARED_RATIO
) {
1452 memorystatus_freezer_stats
.mfs_shared_pages_skipped
+= shared
;
1455 KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT
, BSD_MEMSTAT_FREEZE
) | DBG_FUNC_END
,
1456 memorystatus_available_pages
, aPid
, 0, 0, 0);
1458 DTRACE_MEMORYSTATUS6(memorystatus_freeze
, proc_t
, p
, unsigned int, memorystatus_available_pages
, boolean_t
, purgeable
, unsigned int, wired
, uint32_t, clean
, uint32_t, dirty
);
1460 MEMORYSTATUS_DEBUG(1, "memorystatus_freeze_process_sync: task_freeze %s for pid %d [%s] - "
1461 "memorystatus_pages: %d, purgeable: %d, wired: %d, clean: %d, dirty: %d, max_pages %d, shared %d\n",
1462 (ret
== KERN_SUCCESS
) ? "SUCCEEDED" : "FAILED", aPid
, (*p
->p_name
? p
->p_name
: "(unknown)"),
1463 memorystatus_available_pages
, purgeable
, wired
, clean
, dirty
, max_pages
, shared
);
1467 if (ret
== KERN_SUCCESS
) {
1468 memorystatus_freeze_entry_t data
= { aPid
, TRUE
, dirty
};
1470 p
->p_memstat_freeze_sharedanon_pages
+= shared
;
1472 memorystatus_frozen_shared_mb
+= shared
;
1474 if ((p
->p_memstat_state
& P_MEMSTAT_FROZEN
) == 0) {
1475 p
->p_memstat_state
|= P_MEMSTAT_FROZEN
;
1476 p
->p_memstat_freeze_skip_reason
= kMemorystatusFreezeSkipReasonNone
;
1477 memorystatus_frozen_count
++;
1478 if (memorystatus_frozen_count
== memorystatus_frozen_processes_max
) {
1479 memorystatus_freeze_out_of_slots();
1482 // This was a re-freeze
1483 if (VM_CONFIG_FREEZER_SWAP_IS_ACTIVE
) {
1484 memorystatus_freezer_stats
.mfs_bytes_refrozen
+= dirty
* PAGE_SIZE
;
1485 memorystatus_freezer_stats
.mfs_refreeze_count
++;
1489 p
->p_memstat_frozen_count
++;
1492 * Still keeping the P_MEMSTAT_LOCKED bit till we are actually done elevating this frozen process
1493 * to its higher jetsam band.
1497 memorystatus_send_note(kMemorystatusFreezeNote
, &data
, sizeof(data
));
1499 if (VM_CONFIG_FREEZER_SWAP_IS_ACTIVE
) {
1500 ret
= memorystatus_update_inactive_jetsam_priority_band(p
->p_pid
, MEMORYSTATUS_CMD_ELEVATED_INACTIVEJETSAMPRIORITY_ENABLE
,
1501 memorystatus_freeze_jetsam_band
, TRUE
);
1504 printf("Elevating the frozen process failed with %d\n", ret
);
1511 for (i
= 0; i
< sizeof(throttle_intervals
) / sizeof(struct throttle_interval_t
); i
++) {
1512 throttle_intervals
[i
].pageouts
+= dirty
;
1515 memorystatus_freeze_update_throttle(&memorystatus_freeze_budget_pages_remaining
);
1516 os_log_with_startup_serial(OS_LOG_DEFAULT
, "memorystatus: freezing (specific) pid %d [%s] done memorystatus_freeze_budget_pages_remaining %llu froze %u pages",
1517 aPid
, ((p
&& *p
->p_name
) ? p
->p_name
: "unknown"), memorystatus_freeze_budget_pages_remaining
, dirty
);
1521 memorystatus_freeze_pageouts
+= dirty
;
1523 if (memorystatus_frozen_count
== (memorystatus_frozen_processes_max
- 1)) {
1525 * Add some eviction logic here? At some point should we
1526 * jetsam a process to get back its swap space so that we
1527 * can freeze a more eligible process at this moment in time?
1531 memorystatus_freeze_handle_error(p
, freezer_error_code
, p
->p_memstat_state
& P_MEMSTAT_FROZEN
, aPid
, NULL
, "memorystatus_freeze_process_sync");
1532 p
->p_memstat_state
|= P_MEMSTAT_FREEZE_IGNORE
;
1535 p
->p_memstat_state
&= ~P_MEMSTAT_LOCKED
;
1536 wakeup(&p
->p_memstat_state
);
1541 lck_mtx_unlock(&freezer_mutex
);
1547 * Caller must hold the freezer_mutex and it will be locked on return.
1550 memorystatus_freeze_top_process(void)
1552 pid_t aPid
= 0, coal_xpc_pid
= 0;
1554 proc_t p
= PROC_NULL
, next_p
= PROC_NULL
;
1556 unsigned int band
= JETSAM_PRIORITY_IDLE
;
1557 bool refreeze_processes
= false;
1558 task_t curr_task
= NULL
;
1559 coalition_t coal
= COALITION_NULL
;
1560 pid_t pid_list
[MAX_XPC_SERVICE_PIDS
];
1561 unsigned int ntasks
= 0;
1562 LCK_MTX_ASSERT(&freezer_mutex
, LCK_MTX_ASSERT_OWNED
);
1564 KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT
, BSD_MEMSTAT_FREEZE_SCAN
) | DBG_FUNC_START
, memorystatus_available_pages
, 0, 0, 0, 0);
1568 if (memorystatus_frozen_count
>= memorystatus_frozen_processes_max
) {
1570 * Freezer is already full but we are here and so let's
1571 * try to refreeze any processes we might have thawed
1572 * in the past and push out their compressed state out.
1574 refreeze_processes
= true;
1575 band
= (unsigned int) memorystatus_freeze_jetsam_band
;
1580 next_p
= memorystatus_get_first_proc_locked(&band
, FALSE
);
1583 uint32_t purgeable
, wired
, clean
, dirty
, shared
;
1584 uint64_t max_pages
= 0;
1585 int freezer_error_code
= 0;
1586 bool was_refreeze
= false;
1591 next_p
= memorystatus_get_next_proc_locked(&band
, p
, FALSE
);
1594 * We have frozen a coalition leader and now are
1595 * dealing with its XPC services. We get our
1596 * next_p for each XPC service from the pid_list
1597 * acquired after a successful task_freeze call
1598 * on the coalition leader.
1602 coal_xpc_pid
= pid_list
[--ntasks
];
1603 next_p
= proc_findinternal(coal_xpc_pid
, 1 /* proc_list_lock held */);
1605 * We grab a reference when we are about to freeze the process. So, drop
1606 * the reference that proc_findinternal() grabbed for us.
1607 * We also have the proc_list_lock and so this process is stable.
1610 proc_rele_locked(next_p
);
1619 if (p
->p_memstat_effectivepriority
!= (int32_t) band
) {
1621 * We shouldn't be freezing processes outside the
1627 /* Ensure the process is eligible for (re-)freezing */
1628 if (refreeze_processes
) {
1630 * Has to have been frozen once before.
1632 if ((p
->p_memstat_state
& P_MEMSTAT_FROZEN
) == FALSE
) {
1637 * Has to have been resumed once before.
1639 if ((p
->p_memstat_state
& P_MEMSTAT_REFREEZE_ELIGIBLE
) == FALSE
) {
1644 * Not currently being looked at for something.
1646 if (p
->p_memstat_state
& P_MEMSTAT_LOCKED
) {
1651 * We are going to try and refreeze and so re-evaluate
1652 * the process. We don't want to double count the shared
1653 * memory. So deduct the old snapshot here.
1655 memorystatus_frozen_shared_mb
-= p
->p_memstat_freeze_sharedanon_pages
;
1656 p
->p_memstat_freeze_sharedanon_pages
= 0;
1658 p
->p_memstat_state
&= ~P_MEMSTAT_REFREEZE_ELIGIBLE
;
1659 memorystatus_refreeze_eligible_count
--;
1661 if (memorystatus_is_process_eligible_for_freeze(p
) == FALSE
) {
1662 continue; // with lock held
1666 if (VM_CONFIG_FREEZER_SWAP_IS_ACTIVE
) {
1668 * Freezer backed by the compressor and swap file(s)
1669 * will hold compressed data.
1672 max_pages
= MIN(memorystatus_freeze_pages_max
, memorystatus_freeze_budget_pages_remaining
);
1675 * We only have the compressor pool.
1677 max_pages
= UINT32_MAX
- 1;
1680 /* Mark as locked temporarily to avoid kill */
1681 p
->p_memstat_state
|= P_MEMSTAT_LOCKED
;
1683 p
= proc_ref_locked(p
);
1685 memorystatus_freezer_stats
.mfs_error_other_count
++;
1691 KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT
, BSD_MEMSTAT_FREEZE
) | DBG_FUNC_START
,
1692 memorystatus_available_pages
, 0, 0, 0, 0);
1694 max_pages
= MIN(max_pages
, UINT32_MAX
);
1695 kr
= task_freeze(p
->task
, &purgeable
, &wired
, &clean
, &dirty
, (uint32_t) max_pages
, &shared
, &freezer_error_code
, FALSE
/* eval only */);
1696 if (kr
== KERN_SUCCESS
|| freezer_error_code
== FREEZER_ERROR_LOW_PRIVATE_SHARED_RATIO
) {
1697 memorystatus_freezer_stats
.mfs_shared_pages_skipped
+= shared
;
1700 KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT
, BSD_MEMSTAT_FREEZE
) | DBG_FUNC_END
,
1701 memorystatus_available_pages
, aPid
, 0, 0, 0);
1703 MEMORYSTATUS_DEBUG(1, "memorystatus_freeze_top_process: task_freeze %s for pid %d [%s] - "
1704 "memorystatus_pages: %d, purgeable: %d, wired: %d, clean: %d, dirty: %d, max_pages %d, shared %d\n",
1705 (kr
== KERN_SUCCESS
) ? "SUCCEEDED" : "FAILED", aPid
, (*p
->p_name
? p
->p_name
: "(unknown)"),
1706 memorystatus_available_pages
, purgeable
, wired
, clean
, dirty
, max_pages
, shared
);
1711 if (KERN_SUCCESS
== kr
) {
1712 memorystatus_freeze_entry_t data
= { aPid
, TRUE
, dirty
};
1714 p
->p_memstat_freeze_sharedanon_pages
+= shared
;
1716 memorystatus_frozen_shared_mb
+= shared
;
1718 if ((p
->p_memstat_state
& P_MEMSTAT_FROZEN
) == 0) {
1719 p
->p_memstat_state
|= P_MEMSTAT_FROZEN
;
1720 p
->p_memstat_freeze_skip_reason
= kMemorystatusFreezeSkipReasonNone
;
1721 memorystatus_frozen_count
++;
1722 if (memorystatus_frozen_count
== memorystatus_frozen_processes_max
) {
1723 memorystatus_freeze_out_of_slots();
1726 // This was a re-freeze
1727 if (VM_CONFIG_FREEZER_SWAP_IS_ACTIVE
) {
1728 memorystatus_freezer_stats
.mfs_bytes_refrozen
+= dirty
* PAGE_SIZE
;
1729 memorystatus_freezer_stats
.mfs_refreeze_count
++;
1731 was_refreeze
= true;
1734 p
->p_memstat_frozen_count
++;
1737 * Still keeping the P_MEMSTAT_LOCKED bit till we are actually done elevating this frozen process
1738 * to its higher jetsam band.
1742 memorystatus_send_note(kMemorystatusFreezeNote
, &data
, sizeof(data
));
1744 if (VM_CONFIG_FREEZER_SWAP_IS_ACTIVE
) {
1745 ret
= memorystatus_update_inactive_jetsam_priority_band(p
->p_pid
, MEMORYSTATUS_CMD_ELEVATED_INACTIVEJETSAMPRIORITY_ENABLE
, memorystatus_freeze_jetsam_band
, TRUE
);
1748 printf("Elevating the frozen process failed with %d\n", ret
);
1754 for (i
= 0; i
< sizeof(throttle_intervals
) / sizeof(struct throttle_interval_t
); i
++) {
1755 throttle_intervals
[i
].pageouts
+= dirty
;
1758 memorystatus_freeze_update_throttle(&memorystatus_freeze_budget_pages_remaining
);
1759 os_log_with_startup_serial(OS_LOG_DEFAULT
, "memorystatus: %sfreezing (%s) pid %d [%s] done, memorystatus_freeze_budget_pages_remaining %llu %sfroze %u pages\n",
1760 was_refreeze
? "re" : "", (coal
== NULL
? "general" : "coalition-driven"), aPid
, ((p
&& *p
->p_name
) ? p
->p_name
: "unknown"), memorystatus_freeze_budget_pages_remaining
, was_refreeze
? "Re" : "", dirty
);
1764 memorystatus_freeze_pageouts
+= dirty
;
1766 if (memorystatus_frozen_count
== (memorystatus_frozen_processes_max
- 1)) {
1768 * Add some eviction logic here? At some point should we
1769 * jetsam a process to get back its swap space so that we
1770 * can freeze a more eligible process at this moment in time?
1774 /* Return KERN_SUCCESS */
1778 * We froze a process successfully. We can stop now
1779 * and see if that helped if this process isn't part
1783 * - if it is a leader, get the list of XPC services
1784 * that need to be frozen.
1785 * - if it is a XPC service whose leader was frozen
1786 * here, continue on to the next XPC service in the list.
1790 curr_task
= proc_task(p
);
1791 coal
= task_get_coalition(curr_task
, COALITION_TYPE_JETSAM
);
1792 if (coalition_is_leader(curr_task
, coal
)) {
1793 ntasks
= coalition_get_pid_list(coal
, COALITION_ROLEMASK_XPC
,
1794 COALITION_SORT_DEFAULT
, pid_list
, MAX_XPC_SERVICE_PIDS
);
1796 if (ntasks
> MAX_XPC_SERVICE_PIDS
) {
1797 ntasks
= MAX_XPC_SERVICE_PIDS
;
1805 * Start off with our first next_p in this list.
1807 coal_xpc_pid
= pid_list
[--ntasks
];
1808 next_p
= proc_findinternal(coal_xpc_pid
, 1 /* proc_list_lock held */);
1811 * We grab a reference when we are about to freeze the process. So drop
1812 * the reference that proc_findinternal() grabbed for us.
1813 * We also have the proc_list_lock and so this process is stable.
1816 proc_rele_locked(next_p
);
1821 p
->p_memstat_state
&= ~P_MEMSTAT_LOCKED
;
1822 wakeup(&p
->p_memstat_state
);
1823 proc_rele_locked(p
);
1825 if (coal
&& next_p
) {
1830 * No coalition leader was frozen. So we don't
1831 * need to evaluate any XPC services.
1835 * We have frozen all eligible XPC services for
1836 * the current coalition leader.
1838 * Either way, we can break here and see if freezing
1844 p
->p_memstat_state
&= ~P_MEMSTAT_LOCKED
;
1845 wakeup(&p
->p_memstat_state
);
1847 if (refreeze_processes
) {
1848 if ((freezer_error_code
== FREEZER_ERROR_EXCESS_SHARED_MEMORY
) ||
1849 (freezer_error_code
== FREEZER_ERROR_LOW_PRIVATE_SHARED_RATIO
)) {
1851 * Keeping this prior-frozen process in this high band when
1852 * we failed to re-freeze it due to bad shared memory usage
1853 * could cause excessive pressure on the lower bands.
1854 * We need to demote it for now. It'll get re-evaluated next
1855 * time because we don't set the P_MEMSTAT_FREEZE_IGNORE
1859 p
->p_memstat_state
&= ~P_MEMSTAT_USE_ELEVATED_INACTIVE_BAND
;
1860 memorystatus_invalidate_idle_demotion_locked(p
, TRUE
);
1861 memorystatus_update_priority_locked(p
, JETSAM_PRIORITY_IDLE
, TRUE
, TRUE
);
1864 p
->p_memstat_state
|= P_MEMSTAT_FREEZE_IGNORE
;
1866 memorystatus_freeze_handle_error(p
, p
->p_memstat_state
& P_MEMSTAT_FROZEN
, freezer_error_code
, aPid
, coal
, "memorystatus_freeze_top_process");
1868 proc_rele_locked(p
);
1870 if (vm_compressor_low_on_space() || vm_swap_low_on_space()) {
1877 (memorystatus_refreeze_eligible_count
>= MIN_THAW_REFREEZE_THRESHOLD
) &&
1878 (!refreeze_processes
)) {
1880 * We failed to freeze a process from the IDLE
1881 * band AND we have some thawed processes
1882 * AND haven't tried refreezing as yet.
1883 * Let's try and re-freeze processes in the
1884 * frozen band that have been resumed in the past
1885 * and so have brought in state from disk.
1888 band
= (unsigned int) memorystatus_freeze_jetsam_band
;
1890 refreeze_processes
= true;
1892 goto freeze_process
;
1897 KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT
, BSD_MEMSTAT_FREEZE_SCAN
) | DBG_FUNC_END
, memorystatus_available_pages
, aPid
, 0, 0, 0);
1902 static inline boolean_t
1903 memorystatus_can_freeze_processes(void)
1909 if (memorystatus_suspended_count
) {
1910 memorystatus_freeze_suspended_threshold
= MIN(memorystatus_freeze_suspended_threshold
, FREEZE_SUSPENDED_THRESHOLD_DEFAULT
);
1912 if ((memorystatus_suspended_count
- memorystatus_frozen_count
) > memorystatus_freeze_suspended_threshold
) {
1927 memorystatus_can_freeze(boolean_t
*memorystatus_freeze_swap_low
)
1929 boolean_t can_freeze
= TRUE
;
1931 /* Only freeze if we're sufficiently low on memory; this holds off freeze right
1932 * after boot, and is generally is a no-op once we've reached steady state. */
1933 if (memorystatus_available_pages
> memorystatus_freeze_threshold
) {
1937 /* Check minimum suspended process threshold. */
1938 if (!memorystatus_can_freeze_processes()) {
1941 assert(VM_CONFIG_COMPRESSOR_IS_PRESENT
);
1943 if (!VM_CONFIG_FREEZER_SWAP_IS_ACTIVE
) {
1945 * In-core compressor used for freezing WITHOUT on-disk swap support.
1947 if (vm_compressor_low_on_space()) {
1948 if (*memorystatus_freeze_swap_low
) {
1949 *memorystatus_freeze_swap_low
= TRUE
;
1954 if (*memorystatus_freeze_swap_low
) {
1955 *memorystatus_freeze_swap_low
= FALSE
;
1962 * Freezing WITH on-disk swap support.
1964 * In-core compressor fronts the swap.
1966 if (vm_swap_low_on_space()) {
1967 if (*memorystatus_freeze_swap_low
) {
1968 *memorystatus_freeze_swap_low
= TRUE
;
1979 * This function evaluates if the currently frozen processes deserve
1980 * to stay in the higher jetsam band. There are 2 modes:
1981 * - 'force one == TRUE': (urgent mode)
1982 * We are out of budget and can't refreeze a process. The process's
1983 * state, if it was resumed, will stay in compressed memory. If we let it
1984 * remain up in the higher frozen jetsam band, it'll put a lot of pressure on
1985 * the lower bands. So we force-demote the least-recently-used-and-thawed
1988 * - 'force_one == FALSE': (normal mode)
1989 * If the # of thaws of a process is below our threshold, then we
1990 * will demote that process into the IDLE band.
1991 * We don't immediately kill the process here because it already has
1992 * state on disk and so it might be worth giving it another shot at
1993 * getting thawed/resumed and used.
1996 memorystatus_demote_frozen_processes(boolean_t force_one
)
1998 unsigned int band
= (unsigned int) memorystatus_freeze_jetsam_band
;
1999 unsigned int demoted_proc_count
= 0;
2000 proc_t p
= PROC_NULL
, next_p
= PROC_NULL
;
2001 /* We demote to IDLE unless someone has asserted a higher priority on this process. */
2002 int maxpriority
= JETSAM_PRIORITY_IDLE
;
2006 if (memorystatus_freeze_enabled
== FALSE
) {
2008 * Freeze has been disabled likely to
2009 * reclaim swap space. So don't change
2010 * any state on the frozen processes.
2016 next_p
= memorystatus_get_first_proc_locked(&band
, FALSE
);
2019 next_p
= memorystatus_get_next_proc_locked(&band
, p
, FALSE
);
2021 if ((p
->p_memstat_state
& P_MEMSTAT_FROZEN
) == FALSE
) {
2025 if (p
->p_memstat_state
& P_MEMSTAT_LOCKED
) {
2029 if (force_one
== TRUE
) {
2030 if ((p
->p_memstat_state
& P_MEMSTAT_REFREEZE_ELIGIBLE
) == 0) {
2032 * This process hasn't been thawed recently and so most of
2033 * its state sits on NAND and so we skip it -- jetsamming it
2034 * won't help with memory pressure.
2039 if (p
->p_memstat_thaw_count
>= memorystatus_thaw_count_demotion_threshold
) {
2041 * This process has met / exceeded our thaw count demotion threshold
2042 * and so we let it live in the higher bands.
2048 p
->p_memstat_state
&= ~P_MEMSTAT_USE_ELEVATED_INACTIVE_BAND
;
2049 memorystatus_invalidate_idle_demotion_locked(p
, TRUE
);
2051 maxpriority
= MAX(p
->p_memstat_assertionpriority
, maxpriority
);
2052 memorystatus_update_priority_locked(p
, maxpriority
, FALSE
, FALSE
);
2053 #if DEVELOPMENT || DEBUG
2054 os_log_with_startup_serial(OS_LOG_DEFAULT
, "memorystatus_demote_frozen_process(%s) pid %d [%s]",
2055 (force_one
? "urgent" : "normal"), (p
? p
->p_pid
: -1), ((p
&& *p
->p_name
) ? p
->p_name
: "unknown"));
2056 #endif /* DEVELOPMENT || DEBUG */
2059 * The freezer thread will consider this a normal app to be frozen
2060 * because it is in the IDLE band. So we don't need the
2061 * P_MEMSTAT_REFREEZE_ELIGIBLE state here. Also, if it gets resumed
2062 * we'll correctly count it as eligible for re-freeze again.
2064 * We don't drop the frozen count because this process still has
2065 * state on disk. So there's a chance it gets resumed and then it
2066 * should land in the higher jetsam band. For that it needs to
2067 * remain marked frozen.
2069 if (p
->p_memstat_state
& P_MEMSTAT_REFREEZE_ELIGIBLE
) {
2070 p
->p_memstat_state
&= ~P_MEMSTAT_REFREEZE_ELIGIBLE
;
2071 memorystatus_refreeze_eligible_count
--;
2074 demoted_proc_count
++;
2076 if ((force_one
== TRUE
) || (demoted_proc_count
== memorystatus_max_frozen_demotions_daily
)) {
2081 if (force_one
== FALSE
) {
2083 * We use these counters to track daily hit rates.
2084 * So we only reset them to 0 under the normal
2087 memorystatus_thaw_count
= 0;
2094 * Calculate a new freezer budget.
2095 * @param time_since_last_interval_expired_sec How long has it been (in seconds) since the previous interval expired.
2096 * @param burst_multiple The burst_multiple for the new period
2097 * @param interval_duration_min How many minutes will the new interval be?
2098 * @param rollover The amount to rollover from the previous budget.
2100 * @return A budget for the new interval.
2103 memorystatus_freeze_calculate_new_budget(
2104 unsigned int time_since_last_interval_expired_sec
,
2105 unsigned int burst_multiple
,
2106 unsigned int interval_duration_min
,
2109 uint64_t freeze_daily_budget
= 0, freeze_daily_budget_mb
= 0, daily_budget_pageouts
= 0, budget_missed
= 0, freeze_daily_pageouts_max
= 0, new_budget
= 0;
2110 const static unsigned int kNumSecondsInDay
= 60 * 60 * 24;
2111 /* Precision factor for days_missed. 2 decimal points. */
2112 const static unsigned int kFixedPointFactor
= 100;
2113 unsigned int days_missed
;
2115 /* Get the daily budget from the storage layer */
2116 if (vm_swap_max_budget(&freeze_daily_budget
)) {
2117 freeze_daily_budget_mb
= freeze_daily_budget
/ (1024 * 1024);
2118 assert(freeze_daily_budget_mb
<= UINT32_MAX
);
2119 memorystatus_freeze_daily_mb_max
= (unsigned int) freeze_daily_budget_mb
;
2120 os_log_with_startup_serial(OS_LOG_DEFAULT
, "memorystatus: memorystatus_freeze_daily_mb_max set to %dMB\n", memorystatus_freeze_daily_mb_max
);
2122 /* Calculate the daily pageout budget */
2123 freeze_daily_pageouts_max
= memorystatus_freeze_daily_mb_max
* (1024 * 1024 / PAGE_SIZE
);
2125 daily_budget_pageouts
= (burst_multiple
* (((uint64_t) interval_duration_min
* freeze_daily_pageouts_max
) / (kNumSecondsInDay
/ 60)));
2128 * Add additional budget for time since the interval expired.
2129 * For example, if the interval expired n days ago, we should get an additional n days
2130 * of budget since we didn't use any budget during those n days.
2132 days_missed
= time_since_last_interval_expired_sec
* kFixedPointFactor
/ kNumSecondsInDay
;
2133 budget_missed
= days_missed
* freeze_daily_pageouts_max
/ kFixedPointFactor
;
2134 new_budget
= rollover
+ daily_budget_pageouts
+ budget_missed
;
2135 return (uint32_t) MIN(new_budget
, UINT32_MAX
);
2139 * Mark all non frozen, freezer-eligible processes as skipped for the given reason.
2140 * Used when we hit some system freeze limit and know that we won't be considering remaining processes.
2141 * If you're using this for a new reason, make sure to add it to memorystatus_freeze_init_proc so that
2142 * it gets set for new processes.
2143 * NB: These processes will retain this skip reason until they are reconsidered by memorystatus_is_process_eligible_for_freeze.
2146 memorystatus_freeze_mark_eligible_processes_with_skip_reason(memorystatus_freeze_skip_reason_t reason
, bool locked
)
2148 LCK_MTX_ASSERT(&freezer_mutex
, LCK_MTX_ASSERT_OWNED
);
2149 LCK_MTX_ASSERT(proc_list_mlock
, locked
? LCK_MTX_ASSERT_OWNED
: LCK_MTX_ASSERT_NOTOWNED
);
2150 unsigned int band
= JETSAM_PRIORITY_IDLE
;
2156 p
= memorystatus_get_first_proc_locked(&band
, FALSE
);
2158 assert(p
->p_memstat_effectivepriority
== (int32_t) band
);
2159 if (!(p
->p_memstat_state
& P_MEMSTAT_FROZEN
) && memorystatus_is_process_eligible_for_freeze(p
)) {
2160 assert(p
->p_memstat_freeze_skip_reason
== kMemorystatusFreezeSkipReasonNone
);
2161 p
->p_memstat_freeze_skip_reason
= (uint8_t) reason
;
2163 p
= memorystatus_get_next_proc_locked(&band
, p
, FALSE
);
2171 * Called after we fail to freeze a process.
2172 * Logs the failure, marks the process with the failure reason, and updates freezer stats.
2175 memorystatus_freeze_handle_error(
2177 const int freezer_error_code
,
2180 const coalition_t coalition
,
2181 const char* log_prefix
)
2184 memorystatus_freeze_skip_reason_t skip_reason
;
2186 switch (freezer_error_code
) {
2187 case FREEZER_ERROR_EXCESS_SHARED_MEMORY
:
2188 memorystatus_freezer_stats
.mfs_error_excess_shared_memory_count
++;
2189 reason
= "too much shared memory";
2190 skip_reason
= kMemorystatusFreezeSkipReasonExcessSharedMemory
;
2192 case FREEZER_ERROR_LOW_PRIVATE_SHARED_RATIO
:
2193 memorystatus_freezer_stats
.mfs_error_low_private_shared_ratio_count
++;
2194 reason
= "private-shared pages ratio";
2195 skip_reason
= kMemorystatusFreezeSkipReasonLowPrivateSharedRatio
;
2197 case FREEZER_ERROR_NO_COMPRESSOR_SPACE
:
2198 memorystatus_freezer_stats
.mfs_error_no_compressor_space_count
++;
2199 reason
= "no compressor space";
2200 skip_reason
= kMemorystatusFreezeSkipReasonNoCompressorSpace
;
2202 case FREEZER_ERROR_NO_SWAP_SPACE
:
2203 memorystatus_freezer_stats
.mfs_error_no_swap_space_count
++;
2204 reason
= "no swap space";
2205 skip_reason
= kMemorystatusFreezeSkipReasonNoSwapSpace
;
2208 reason
= "unknown error";
2209 skip_reason
= kMemorystatusFreezeSkipReasonOther
;
2212 p
->p_memstat_freeze_skip_reason
= (uint8_t) skip_reason
;
2214 os_log_with_startup_serial(OS_LOG_DEFAULT
, "%s: %sfreezing (%s) pid %d [%s]...skipped (%s)\n",
2215 log_prefix
, was_refreeze
? "re" : "",
2216 (coalition
== NULL
? "general" : "coalition-driven"), pid
,
2217 ((p
&& *p
->p_name
) ? p
->p_name
: "unknown"), reason
);
2221 * Start a new normal throttle interval with the given budget.
2222 * Caller must hold the freezer mutex
2225 memorystatus_freeze_start_normal_throttle_interval(uint32_t new_budget
, mach_timespec_t start_ts
)
2227 LCK_MTX_ASSERT(&freezer_mutex
, LCK_MTX_ASSERT_OWNED
);
2228 LCK_MTX_ASSERT(proc_list_mlock
, LCK_MTX_ASSERT_NOTOWNED
);
2230 normal_throttle_window
->max_pageouts
= new_budget
;
2231 normal_throttle_window
->ts
.tv_sec
= normal_throttle_window
->mins
* 60;
2232 normal_throttle_window
->ts
.tv_nsec
= 0;
2233 ADD_MACH_TIMESPEC(&normal_throttle_window
->ts
, &start_ts
);
2234 /* Since we update the throttle stats pre-freeze, adjust for overshoot here */
2235 if (normal_throttle_window
->pageouts
> normal_throttle_window
->max_pageouts
) {
2236 normal_throttle_window
->pageouts
-= normal_throttle_window
->max_pageouts
;
2238 normal_throttle_window
->pageouts
= 0;
2240 /* Ensure the normal window is now active. */
2241 memorystatus_freeze_degradation
= FALSE
;
2244 #if DEVELOPMENT || DEBUG
2247 sysctl_memorystatus_freeze_calculate_new_budget SYSCTL_HANDLER_ARGS
2249 #pragma unused(arg1, arg2)
2251 unsigned int time_since_last_interval_expired_sec
= 0;
2252 unsigned int new_budget
;
2254 error
= sysctl_handle_int(oidp
, &time_since_last_interval_expired_sec
, 0, req
);
2255 if (error
|| !req
->newptr
) {
2258 new_budget
= memorystatus_freeze_calculate_new_budget(time_since_last_interval_expired_sec
, 1, NORMAL_WINDOW_MINS
, 0);
2259 return copyout(&new_budget
, req
->oldptr
, MIN(sizeof(req
->oldlen
), sizeof(new_budget
)));
2262 SYSCTL_PROC(_vm
, OID_AUTO
, memorystatus_freeze_calculate_new_budget
, CTLTYPE_INT
| CTLFLAG_RW
| CTLFLAG_MASKED
,
2263 0, 0, &sysctl_memorystatus_freeze_calculate_new_budget
, "I", "");
2265 #endif /* DEVELOPMENT || DEBUG */
2268 * Called when we first run out of budget in an interval.
2269 * Marks idle processes as not frozen due to lack of budget.
2270 * NB: It might be worth having a CA event here.
2273 memorystatus_freeze_out_of_budget(const struct throttle_interval_t
*interval
)
2275 LCK_MTX_ASSERT(&freezer_mutex
, LCK_MTX_ASSERT_OWNED
);
2276 LCK_MTX_ASSERT(proc_list_mlock
, LCK_MTX_ASSERT_NOTOWNED
);
2278 mach_timespec_t time_left
= {0, 0};
2279 mach_timespec_t now_ts
;
2283 time_left
.tv_sec
= interval
->ts
.tv_sec
;
2284 time_left
.tv_nsec
= 0;
2285 clock_get_system_nanotime(&sec
, &nsec
);
2286 now_ts
.tv_sec
= (unsigned int)(MIN(sec
, UINT32_MAX
));
2287 now_ts
.tv_nsec
= nsec
;
2289 SUB_MACH_TIMESPEC(&time_left
, &now_ts
);
2290 os_log(OS_LOG_DEFAULT
,
2291 "memorystatus_freeze: Out of NAND write budget with %u minutes left in the current freezer interval. %u procs are frozen.\n",
2292 time_left
.tv_sec
/ 60, memorystatus_frozen_count
);
2294 memorystatus_freeze_mark_eligible_processes_with_skip_reason(kMemorystatusFreezeSkipReasonOutOfBudget
, false);
2298 * Called when we cross over the threshold of maximum frozen processes allowed.
2299 * Marks remaining idle processes as not frozen due to lack of slots.
2302 memorystatus_freeze_out_of_slots(void)
2304 LCK_MTX_ASSERT(&freezer_mutex
, LCK_MTX_ASSERT_OWNED
);
2305 LCK_MTX_ASSERT(proc_list_mlock
, LCK_MTX_ASSERT_OWNED
);
2306 assert(memorystatus_frozen_count
== memorystatus_frozen_processes_max
);
2308 os_log(OS_LOG_DEFAULT
,
2309 "memorystatus_freeze: Out of slots in the freezer. %u procs are frozen.\n",
2310 memorystatus_frozen_count
);
2312 memorystatus_freeze_mark_eligible_processes_with_skip_reason(kMemorystatusFreezeSkipReasonOutOfSlots
, true);
2316 * This function will do 4 things:
2318 * 1) check to see if we are currently in a degraded freezer mode, and if so:
2319 * - check to see if our window has expired and we should exit this mode, OR,
2320 * - return a budget based on the degraded throttle window's max. pageouts vs current pageouts.
2322 * 2) check to see if we are in a NEW normal window and update the normal throttle window's params.
2324 * 3) check what the current normal window allows for a budget.
2326 * 4) calculate the current rate of pageouts for DEGRADED_WINDOW_MINS duration. If that rate is below
2327 * what we would normally expect, then we are running low on our daily budget and need to enter
2328 * degraded perf. mode.
2330 * Caller must hold the freezer mutex
2331 * Caller must not hold the proc_list lock
2335 memorystatus_freeze_update_throttle(uint64_t *budget_pages_allowed
)
2339 mach_timespec_t now_ts
;
2340 LCK_MTX_ASSERT(&freezer_mutex
, LCK_MTX_ASSERT_OWNED
);
2341 LCK_MTX_ASSERT(proc_list_mlock
, LCK_MTX_ASSERT_NOTOWNED
);
2343 unsigned int freeze_daily_pageouts_max
= 0;
2344 uint32_t budget_rollover
= 0;
2345 bool started_with_budget
= (*budget_pages_allowed
> 0);
2347 #if DEVELOPMENT || DEBUG
2348 if (!memorystatus_freeze_throttle_enabled
) {
2350 * No throttling...we can use the full budget everytime.
2352 *budget_pages_allowed
= UINT64_MAX
;
2357 clock_get_system_nanotime(&sec
, &nsec
);
2358 now_ts
.tv_sec
= (unsigned int)(MIN(sec
, UINT32_MAX
));
2359 now_ts
.tv_nsec
= nsec
;
2361 struct throttle_interval_t
*interval
= NULL
;
2363 if (memorystatus_freeze_degradation
== TRUE
) {
2364 interval
= degraded_throttle_window
;
2366 if (CMP_MACH_TIMESPEC(&now_ts
, &interval
->ts
) >= 0) {
2367 interval
->pageouts
= 0;
2368 interval
->max_pageouts
= 0;
2370 *budget_pages_allowed
= interval
->max_pageouts
- interval
->pageouts
;
2374 interval
= normal_throttle_window
;
2376 if (CMP_MACH_TIMESPEC(&now_ts
, &interval
->ts
) >= 0) {
2377 /* How long has it been since the previous interval expired? */
2378 mach_timespec_t expiration_period_ts
= now_ts
;
2379 SUB_MACH_TIMESPEC(&expiration_period_ts
, &interval
->ts
);
2380 /* Get unused budget. Clamp to 0. We'll adjust for overused budget in the next interval. */
2381 budget_rollover
= interval
->pageouts
> interval
->max_pageouts
?
2382 0 : interval
->max_pageouts
- interval
->pageouts
;
2384 memorystatus_freeze_start_normal_throttle_interval(memorystatus_freeze_calculate_new_budget(
2385 expiration_period_ts
.tv_sec
, interval
->burst_multiple
,
2386 interval
->mins
, budget_rollover
),
2388 *budget_pages_allowed
= interval
->max_pageouts
;
2389 memorystatus_freezer_stats
.mfs_shared_pages_skipped
= 0;
2391 memorystatus_demote_frozen_processes(FALSE
); /* normal mode...don't force a demotion */
2394 * Current throttle window.
2395 * Deny freezing if we have no budget left.
2396 * Try graceful degradation if we are within 25% of:
2397 * - the daily budget, and
2398 * - the current budget left is below our normal budget expectations.
2401 if (memorystatus_freeze_degradation
== FALSE
) {
2402 if (interval
->pageouts
>= interval
->max_pageouts
) {
2403 *budget_pages_allowed
= 0;
2404 if (started_with_budget
) {
2405 memorystatus_freeze_out_of_budget(interval
);
2408 int budget_left
= interval
->max_pageouts
- interval
->pageouts
;
2409 int budget_threshold
= (freeze_daily_pageouts_max
* FREEZE_DEGRADATION_BUDGET_THRESHOLD
) / 100;
2411 mach_timespec_t time_left
= {0, 0};
2413 time_left
.tv_sec
= interval
->ts
.tv_sec
;
2414 time_left
.tv_nsec
= 0;
2416 SUB_MACH_TIMESPEC(&time_left
, &now_ts
);
2418 if (budget_left
<= budget_threshold
) {
2420 * For the current normal window, calculate how much we would pageout in a DEGRADED_WINDOW_MINS duration.
2421 * And also calculate what we would pageout for the same DEGRADED_WINDOW_MINS duration if we had the full
2422 * daily pageout budget.
2425 unsigned int current_budget_rate_allowed
= ((budget_left
/ time_left
.tv_sec
) / 60) * DEGRADED_WINDOW_MINS
;
2426 unsigned int normal_budget_rate_allowed
= (freeze_daily_pageouts_max
/ NORMAL_WINDOW_MINS
) * DEGRADED_WINDOW_MINS
;
2429 * The current rate of pageouts is below what we would expect for
2430 * the normal rate i.e. we have below normal budget left and so...
2433 if (current_budget_rate_allowed
< normal_budget_rate_allowed
) {
2434 memorystatus_freeze_degradation
= TRUE
;
2435 degraded_throttle_window
->max_pageouts
= current_budget_rate_allowed
;
2436 degraded_throttle_window
->pageouts
= 0;
2439 * Switch over to the degraded throttle window so the budget
2440 * doled out is based on that window.
2442 interval
= degraded_throttle_window
;
2446 *budget_pages_allowed
= interval
->max_pageouts
- interval
->pageouts
;
2451 MEMORYSTATUS_DEBUG(1, "memorystatus_freeze_update_throttle_interval: throttle updated - %d frozen (%d max) within %dm; %dm remaining; throttle %s\n",
2452 interval
->pageouts
, interval
->max_pageouts
, interval
->mins
, (interval
->ts
.tv_sec
- now_ts
->tv_sec
) / 60,
2453 interval
->throttle
? "on" : "off");
2457 memorystatus_freeze_thread(void *param __unused
, wait_result_t wr __unused
)
2459 static boolean_t memorystatus_freeze_swap_low
= FALSE
;
2461 lck_mtx_lock(&freezer_mutex
);
2463 if (memorystatus_freeze_enabled
) {
2464 if ((memorystatus_frozen_count
< memorystatus_frozen_processes_max
) ||
2465 (memorystatus_refreeze_eligible_count
>= MIN_THAW_REFREEZE_THRESHOLD
)) {
2466 if (memorystatus_can_freeze(&memorystatus_freeze_swap_low
)) {
2467 /* Only freeze if we've not exceeded our pageout budgets.*/
2468 memorystatus_freeze_update_throttle(&memorystatus_freeze_budget_pages_remaining
);
2470 if (memorystatus_freeze_budget_pages_remaining
) {
2471 memorystatus_freeze_top_process();
2473 memorystatus_demote_frozen_processes(TRUE
); /* urgent mode..force one demotion */
2480 * Give applications currently in the aging band a chance to age out into the idle band before
2481 * running the freezer again.
2483 memorystatus_freezer_thread_next_run_ts
= mach_absolute_time() + memorystatus_apps_idle_delay_time
;
2485 assert_wait((event_t
) &memorystatus_freeze_wakeup
, THREAD_UNINT
);
2486 lck_mtx_unlock(&freezer_mutex
);
2488 thread_block((thread_continue_t
) memorystatus_freeze_thread
);
2492 memorystatus_freeze_thread_should_run(void)
2495 * No freezer_mutex held here...see why near call-site
2496 * within memorystatus_pages_update().
2499 boolean_t should_run
= FALSE
;
2501 if (memorystatus_freeze_enabled
== FALSE
) {
2505 if (memorystatus_available_pages
> memorystatus_freeze_threshold
) {
2509 memorystatus_freezer_stats
.mfs_below_threshold_count
++;
2511 if ((memorystatus_frozen_count
>= memorystatus_frozen_processes_max
)) {
2513 * Consider this as a skip even if we wake up to refreeze because
2514 * we won't freeze any new procs.
2516 memorystatus_freezer_stats
.mfs_skipped_full_count
++;
2517 if (memorystatus_refreeze_eligible_count
< MIN_THAW_REFREEZE_THRESHOLD
) {
2522 if (memorystatus_frozen_shared_mb_max
&& (memorystatus_frozen_shared_mb
>= memorystatus_frozen_shared_mb_max
)) {
2523 memorystatus_freezer_stats
.mfs_skipped_shared_mb_high_count
++;
2527 uint64_t curr_time
= mach_absolute_time();
2529 if (curr_time
< memorystatus_freezer_thread_next_run_ts
) {
2540 memorystatus_get_process_is_freezable(pid_t pid
, int *is_freezable
)
2542 proc_t p
= PROC_NULL
;
2554 * Only allow this on the current proc for now.
2555 * We can check for privileges and allow targeting another process in the future.
2557 if (p
!= current_proc()) {
2563 *is_freezable
= ((p
->p_memstat_state
& P_MEMSTAT_FREEZE_DISABLED
) ? 0 : 1);
2564 proc_rele_locked(p
);
2571 memorystatus_get_process_is_frozen(pid_t pid
, int *is_frozen
)
2573 proc_t p
= PROC_NULL
;
2580 * Only allow this on the current proc for now.
2581 * We can check for privileges and allow targeting another process in the future.
2584 if (p
->p_pid
!= pid
) {
2589 *is_frozen
= (p
->p_memstat_state
& P_MEMSTAT_FROZEN
) != 0;
2596 memorystatus_set_process_is_freezable(pid_t pid
, boolean_t is_freezable
)
2598 proc_t p
= PROC_NULL
;
2605 * To enable freezable status, you need to be root or an entitlement.
2608 !kauth_cred_issuser(kauth_cred_get()) &&
2609 !IOTaskHasEntitlement(current_task(), MEMORYSTATUS_ENTITLEMENT
)) {
2619 * A process can change its own status. A coalition leader can
2620 * change the status of coalition members.
2622 if (p
!= current_proc()) {
2623 coalition_t coal
= task_get_coalition(proc_task(p
), COALITION_TYPE_JETSAM
);
2624 if (!coalition_is_leader(proc_task(current_proc()), coal
)) {
2631 if (is_freezable
== FALSE
) {
2632 /* Freeze preference set to FALSE. Set the P_MEMSTAT_FREEZE_DISABLED bit. */
2633 p
->p_memstat_state
|= P_MEMSTAT_FREEZE_DISABLED
;
2634 printf("memorystatus_set_process_is_freezable: disabling freeze for pid %d [%s]\n",
2635 p
->p_pid
, (*p
->p_name
? p
->p_name
: "unknown"));
2637 p
->p_memstat_state
&= ~P_MEMSTAT_FREEZE_DISABLED
;
2638 printf("memorystatus_set_process_is_freezable: enabling freeze for pid %d [%s]\n",
2639 p
->p_pid
, (*p
->p_name
? p
->p_name
: "unknown"));
2641 proc_rele_locked(p
);
2648 * Called when process is created before it is added to a memorystatus bucket.
2651 memorystatus_freeze_init_proc(proc_t p
)
2653 /* NB: Process is not on the memorystatus lists yet so it's safe to modify the skip reason without the freezer mutex. */
2654 if (memorystatus_freeze_budget_pages_remaining
== 0) {
2655 p
->p_memstat_freeze_skip_reason
= kMemorystatusFreezeSkipReasonOutOfBudget
;
2656 } else if ((memorystatus_frozen_count
>= memorystatus_frozen_processes_max
)) {
2657 p
->p_memstat_freeze_skip_reason
= kMemorystatusFreezeSkipReasonOutOfSlots
;
2659 p
->p_memstat_freeze_skip_reason
= kMemorystatusFreezeSkipReasonNone
;
2665 sysctl_memorystatus_do_fastwake_warmup_all SYSCTL_HANDLER_ARGS
2667 #pragma unused(oidp, arg1, arg2)
2673 /* Need to be root or have entitlement */
2674 if (!kauth_cred_issuser(kauth_cred_get()) && !IOTaskHasEntitlement(current_task(), MEMORYSTATUS_ENTITLEMENT
)) {
2678 if (memorystatus_freeze_enabled
== FALSE
) {
2682 do_fastwake_warmup_all();
2687 SYSCTL_PROC(_kern
, OID_AUTO
, memorystatus_do_fastwake_warmup_all
, CTLTYPE_INT
| CTLFLAG_WR
| CTLFLAG_LOCKED
| CTLFLAG_MASKED
,
2688 0, 0, &sysctl_memorystatus_do_fastwake_warmup_all
, "I", "");
2690 #endif /* CONFIG_FREEZE */