2 * Copyright (c) 2006 Apple Computer, 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 <sys/kern_event.h>
31 #include <sys/kern_memorystatus.h>
33 #include <kern/sched_prim.h>
34 #include <kern/kalloc.h>
35 #include <kern/debug.h>
36 #include <kern/lock.h>
37 #include <kern/task.h>
38 #include <kern/thread.h>
39 #include <libkern/libkern.h>
40 #include <mach/task.h>
41 #include <mach/task_info.h>
43 #include <sys/signal.h>
44 #include <sys/signalvar.h>
45 #include <sys/sysctl.h>
48 #include <pexpert/pexpert.h>
51 #include <vm/vm_protos.h>
52 #include <vm/vm_map.h>
55 kProcessSuspended
= (1 << 0),
56 kProcessHibernated
= (1 << 1),
57 kProcessNoReclaimWorth
= (1 << 2),
58 kProcessIgnored
= (1 << 3),
59 kProcessBusy
= (1 << 4)
62 static lck_mtx_t
* hibernation_mlock
;
63 static lck_attr_t
* hibernation_lck_attr
;
64 static lck_grp_t
* hibernation_lck_grp
;
65 static lck_grp_attr_t
* hibernation_lck_grp_attr
;
67 typedef struct hibernation_node
{
68 RB_ENTRY(hibernation_node
) link
;
71 mach_timespec_t hibernation_ts
;
74 static int hibernation_tree_compare(hibernation_node
*n1
, hibernation_node
*n2
) {
75 if (n1
->pid
< n2
->pid
)
77 else if (n1
->pid
> n2
->pid
)
83 static RB_HEAD(hibernation_tree
, hibernation_node
) hibernation_tree_head
;
84 RB_PROTOTYPE_SC(static, hibernation_tree
, hibernation_node
, link
, hibernation_tree_compare
);
86 RB_GENERATE(hibernation_tree
, hibernation_node
, link
, hibernation_tree_compare
);
88 static inline boolean_t
kern_hibernation_can_hibernate_processes(void);
89 static boolean_t
kern_hibernation_can_hibernate(void);
91 static void kern_hibernation_add_node(hibernation_node
*node
);
92 static hibernation_node
*kern_hibernation_get_node(pid_t pid
);
93 static void kern_hibernation_release_node(hibernation_node
*node
);
94 static void kern_hibernation_free_node(hibernation_node
*node
, boolean_t unlock
);
96 static void kern_hibernation_register_pid(pid_t pid
);
97 static void kern_hibernation_unregister_pid(pid_t pid
);
99 static int kern_hibernation_get_process_state(pid_t pid
, uint32_t *state
, mach_timespec_t
*ts
);
100 static int kern_hibernation_set_process_state(pid_t pid
, uint32_t state
);
102 static void kern_hibernation_cull(void);
104 static void kern_hibernation_thread(void);
106 extern boolean_t vm_freeze_enabled
;
108 int kern_hibernation_wakeup
= 0;
110 static int jetsam_priority_list_hibernation_index
= 0;
113 static int kern_memorystatus_level_hibernate
= 50;
115 #define HIBERNATION_PAGES_MIN ( 1 * 1024 * 1024 / PAGE_SIZE)
116 #define HIBERNATION_PAGES_MAX (16 * 1024 * 1024 / PAGE_SIZE)
118 static unsigned int kern_memorystatus_hibernation_pages_min
= HIBERNATION_PAGES_MIN
;
119 static unsigned int kern_memorystatus_hibernation_pages_max
= HIBERNATION_PAGES_MAX
;
121 static unsigned int kern_memorystatus_suspended_count
= 0;
122 static unsigned int kern_memorystatus_hibernated_count
= 0;
124 static unsigned int kern_memorystatus_hibernation_suspended_minimum
= 4;
126 static unsigned int kern_memorystatus_low_swap_pages
= 0;
129 #define HIBERNATION_DAILY_MB_MAX 1024
130 #define HIBERNATION_DAILY_PAGEOUTS_MAX (HIBERNATION_DAILY_MB_MAX * (1024 * 1024 / PAGE_SIZE))
132 static struct throttle_interval_t
{
134 uint32_t burst_multiple
;
136 uint32_t max_pageouts
;
139 } throttle_intervals
[] = {
140 { 60, 8, 0, 0, { 0, 0 }, FALSE
}, /* 1 hour intermediate interval, 8x burst */
141 { 24 * 60, 1, 0, 0, { 0, 0 }, FALSE
}, /* 24 hour long interval, no burst */
145 static uint64_t kern_memorystatus_hibernation_count
= 0;
146 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_hibernation_count
, CTLFLAG_RD
, &kern_memorystatus_hibernation_count
, "");
148 static uint64_t kern_memorystatus_hibernation_pageouts
= 0;
149 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_hibernation_pageouts
, CTLFLAG_RD
, &kern_memorystatus_hibernation_pageouts
, "");
151 static uint64_t kern_memorystatus_hibernation_throttle_count
= 0;
152 SYSCTL_QUAD(_kern
, OID_AUTO
, memorystatus_hibernation_throttle_count
, CTLFLAG_RD
, &kern_memorystatus_hibernation_throttle_count
, "");
154 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_hibernation_min_processes
, CTLFLAG_RW
, &kern_memorystatus_hibernation_suspended_minimum
, 0, "");
156 #if DEVELOPMENT || DEBUG
157 /* Allow parameter tweaking in these builds */
158 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_level_hibernate
, CTLFLAG_RW
, &kern_memorystatus_level_hibernate
, 0, "");
160 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_hibernation_pages_min
, CTLFLAG_RW
, &kern_memorystatus_hibernation_pages_min
, 0, "");
161 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_hibernation_pages_max
, CTLFLAG_RW
, &kern_memorystatus_hibernation_pages_max
, 0, "");
163 boolean_t kern_memorystatus_hibernation_throttle_enabled
= TRUE
;
164 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_hibernation_throttle_enabled
, CTLFLAG_RW
, &kern_memorystatus_hibernation_throttle_enabled
, 0, "");
165 #endif /* DEVELOPMENT || DEBUG */
166 #endif /* CONFIG_FREEZE */
168 extern unsigned int vm_page_free_count
;
169 extern unsigned int vm_page_active_count
;
170 extern unsigned int vm_page_inactive_count
;
171 extern unsigned int vm_page_purgeable_count
;
172 extern unsigned int vm_page_wire_count
;
174 static void kern_memorystatus_thread(void);
176 int kern_memorystatus_wakeup
= 0;
177 int kern_memorystatus_level
= 0;
178 int kern_memorystatus_last_level
= 0;
179 unsigned int kern_memorystatus_delta
;
181 unsigned int kern_memorystatus_kev_failure_count
= 0;
182 int kern_memorystatus_level_critical
= 5;
183 #define kern_memorystatus_level_highwater (kern_memorystatus_level_critical + 5)
186 jetsam_kernel_stats_t stats
;
188 jetsam_snapshot_entry_t entries
[kMaxSnapshotEntries
];
191 static jetsam_priority_entry_t jetsam_priority_list
[kMaxPriorityEntries
];
192 #define jetsam_snapshot_list jetsam_snapshot.entries
194 static int jetsam_priority_list_index
= 0;
195 static int jetsam_priority_list_count
= 0;
196 static int jetsam_snapshot_list_count
= 0;
198 static lck_mtx_t
* jetsam_list_mlock
;
199 static lck_attr_t
* jetsam_lck_attr
;
200 static lck_grp_t
* jetsam_lck_grp
;
201 static lck_grp_attr_t
* jetsam_lck_grp_attr
;
203 SYSCTL_INT(_kern
, OID_AUTO
, memorystatus_level
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &kern_memorystatus_level
, 0, "");
204 SYSCTL_UINT(_kern
, OID_AUTO
, memorystatus_kev_failure_count
, CTLFLAG_RD
| CTLFLAG_LOCKED
, &kern_memorystatus_kev_failure_count
, 0, "");
206 #if DEVELOPMENT || DEBUG
209 kJetsamDiagnosticModeNone
= 0,
210 kJetsamDiagnosticModeAll
= 1,
211 kJetsamDiagnosticModeStopAtFirstActive
= 2
212 } jetsam_diagnostic_mode
= kJetsamDiagnosticModeNone
;
214 static int jetsam_diagnostic_suspended_one_active_proc
= 0;
217 sysctl_jetsam_diagnostic_mode SYSCTL_HANDLER_ARGS
219 #pragma unused(arg1, arg2)
220 int error
, val
= jetsam_diagnostic_mode
;
223 error
= sysctl_handle_int(oidp
, &val
, 0, req
);
224 if (error
|| !req
->newptr
)
226 if ((val
< 0) || (val
> 2)) {
227 printf("jetsam: diagnostic mode: invalid value - %d\n", val
);
232 * If jetsam_diagnostic_mode is set, we need to lower memory threshold for jetsam
234 disabled
= (val
== 0) && (jetsam_diagnostic_mode
!= kJetsamDiagnosticModeNone
);
236 jetsam_diagnostic_mode
= val
;
239 kern_memorystatus_level_critical
= 5;
240 printf("jetsam: diagnostic mode: resetting critical level to %d\n", kern_memorystatus_level_critical
);
242 kern_memorystatus_level_critical
= 10;
243 printf("jetsam: diagnostic mode: %d: increasing critical level to %d\n", (int) jetsam_diagnostic_mode
, kern_memorystatus_level_critical
);
244 if (jetsam_diagnostic_mode
== kJetsamDiagnosticModeStopAtFirstActive
)
245 printf("jetsam: diagnostic mode: will stop at first active app\n");
251 SYSCTL_PROC(_debug
, OID_AUTO
, jetsam_diagnostic_mode
, CTLTYPE_INT
|CTLFLAG_RW
|CTLFLAG_ANYBODY
,
252 &jetsam_diagnostic_mode
, 0, sysctl_jetsam_diagnostic_mode
, "I", "Jetsam Diagnostic Mode");
253 #endif /* DEVELOPMENT || DEBUG */
255 __private_extern__
void
256 kern_memorystatus_init(void)
258 jetsam_lck_attr
= lck_attr_alloc_init();
259 jetsam_lck_grp_attr
= lck_grp_attr_alloc_init();
260 jetsam_lck_grp
= lck_grp_alloc_init("jetsam", jetsam_lck_grp_attr
);
261 jetsam_list_mlock
= lck_mtx_alloc_init(jetsam_lck_grp
, jetsam_lck_attr
);
262 kern_memorystatus_delta
= 5 * atop_64(max_mem
) / 100;
264 (void)kernel_thread(kernel_task
, kern_memorystatus_thread
);
268 jetsam_task_page_count(task_t task
)
271 static task_info_data_t data
;
272 static struct task_basic_info
*info
= (struct task_basic_info
*)&data
;
273 static mach_msg_type_number_t count
= TASK_BASIC_INFO_COUNT
;
275 ret
= task_info(task
, TASK_BASIC_INFO
, (task_info_t
)&data
, &count
);
276 if (ret
== KERN_SUCCESS
) {
277 return info
->resident_size
/ PAGE_SIZE
;
283 jetsam_flags_for_pid(pid_t pid
)
287 for (i
= 0; i
< jetsam_priority_list_count
; i
++) {
288 if (pid
== jetsam_priority_list
[i
].pid
) {
289 return jetsam_priority_list
[i
].flags
;
296 jetsam_snapshot_procs(void)
301 jetsam_snapshot
.stats
.free_pages
= vm_page_free_count
;
302 jetsam_snapshot
.stats
.active_pages
= vm_page_active_count
;
303 jetsam_snapshot
.stats
.inactive_pages
= vm_page_inactive_count
;
304 jetsam_snapshot
.stats
.purgeable_pages
= vm_page_purgeable_count
;
305 jetsam_snapshot
.stats
.wired_pages
= vm_page_wire_count
;
307 LIST_FOREACH(p
, &allproc
, p_list
) {
308 task_t task
= p
->task
;
309 jetsam_snapshot_list
[i
].pid
= p
->p_pid
;
310 jetsam_snapshot_list
[i
].pages
= jetsam_task_page_count(task
);
311 jetsam_snapshot_list
[i
].flags
= jetsam_flags_for_pid(p
->p_pid
);
312 strlcpy(&jetsam_snapshot_list
[i
].name
[0], p
->p_comm
, MAXCOMLEN
+1);
314 printf("jetsam snapshot pid = %d, uuid = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
316 p
->p_uuid
[0], p
->p_uuid
[1], p
->p_uuid
[2], p
->p_uuid
[3], p
->p_uuid
[4], p
->p_uuid
[5], p
->p_uuid
[6], p
->p_uuid
[7],
317 p
->p_uuid
[8], p
->p_uuid
[9], p
->p_uuid
[10], p
->p_uuid
[11], p
->p_uuid
[12], p
->p_uuid
[13], p
->p_uuid
[14], p
->p_uuid
[15]);
319 memcpy(&jetsam_snapshot_list
[i
].uuid
[0], &p
->p_uuid
[0], sizeof(p
->p_uuid
));
321 if (i
== kMaxSnapshotEntries
) {
326 jetsam_snapshot
.entry_count
= jetsam_snapshot_list_count
= i
- 1;
330 jetsam_mark_pid_in_snapshot(pid_t pid
, int flags
)
335 for (i
= 0; i
< jetsam_snapshot_list_count
; i
++) {
336 if (jetsam_snapshot_list
[i
].pid
== pid
) {
337 jetsam_snapshot_list
[i
].flags
|= flags
;
344 jetsam_kill_top_proc(boolean_t any
, uint32_t cause
)
348 #ifndef CONFIG_FREEZE
352 if (jetsam_snapshot_list_count
== 0) {
353 jetsam_snapshot_procs();
355 lck_mtx_lock(jetsam_list_mlock
);
356 while (jetsam_priority_list_index
< jetsam_priority_list_count
) {
357 jetsam_priority_entry_t
* jetsam_priority_entry
= &jetsam_priority_list
[jetsam_priority_list_index
];
358 pid_t aPid
= jetsam_priority_entry
->pid
;
359 #if DEVELOPMENT || DEBUG
360 int activeProcess
= jetsam_priority_entry
->flags
& kJetsamFlagsFrontmost
;
361 int procSuspendedForDiagnosis
= jetsam_priority_entry
->flags
& kJetsamFlagsSuspForDiagnosis
;
362 #endif /* DEVELOPMENT || DEBUG */
363 jetsam_priority_list_index
++;
364 /* skip empty slots in the list */
366 continue; // with lock held
368 lck_mtx_unlock(jetsam_list_mlock
);
372 #if DEVELOPMENT || DEBUG
373 if ((jetsam_diagnostic_mode
!= kJetsamDiagnosticModeNone
) && procSuspendedForDiagnosis
) {
374 printf("jetsam: continuing after ignoring proc suspended already for diagnosis - %d\n", aPid
);
376 lck_mtx_lock(jetsam_list_mlock
);
379 #endif /* DEVELOPMENT || DEBUG */
381 hibernation_node
*node
;
383 if ((node
= kern_hibernation_get_node(aPid
))) {
384 boolean_t reclaim_proc
= !(node
->state
& (kProcessBusy
| kProcessNoReclaimWorth
));
385 if (any
|| reclaim_proc
) {
386 if (node
->state
& kProcessHibernated
) {
387 flags
|= kJetsamFlagsHibernated
;
393 kern_hibernation_release_node(node
);
402 #if DEVELOPMENT || DEBUG
403 if ((jetsam_diagnostic_mode
!= kJetsamDiagnosticModeNone
) && activeProcess
) {
405 printf("jetsam: suspending pid %d [%s] (active) for diagnosis - memory_status_level: %d\n",
406 aPid
, (p
->p_comm
? p
->p_comm
: "(unknown)"), kern_memorystatus_level
);
408 jetsam_mark_pid_in_snapshot(aPid
, kJetsamFlagsSuspForDiagnosis
);
409 jetsam_priority_entry
->flags
|= kJetsamFlagsSuspForDiagnosis
;
410 task_suspend(p
->task
);
412 if (jetsam_diagnostic_mode
== kJetsamDiagnosticModeStopAtFirstActive
) {
413 jetsam_diagnostic_suspended_one_active_proc
= 1;
414 printf("jetsam: returning after suspending first active proc - %d\n", aPid
);
418 #endif /* DEVELOPMENT || DEBUG */
420 printf("jetsam: killing pid %d [%s] - memory_status_level: %d\n",
421 aPid
, (p
->p_comm
? p
->p_comm
: "(unknown)"), kern_memorystatus_level
);
422 jetsam_mark_pid_in_snapshot(aPid
, flags
);
423 exit1(p
, W_EXITCODE(0, SIGKILL
), (int *)NULL
);
426 printf("jetsam: pid %d killed - memory_status_level: %d\n", aPid
, kern_memorystatus_level
);
432 lck_mtx_lock(jetsam_list_mlock
);
434 lck_mtx_unlock(jetsam_list_mlock
);
439 jetsam_kill_hiwat_proc(void)
443 if (jetsam_snapshot_list_count
== 0) {
444 jetsam_snapshot_procs();
446 lck_mtx_lock(jetsam_list_mlock
);
447 for (i
= jetsam_priority_list_index
; i
< jetsam_priority_list_count
; i
++) {
450 aPid
= jetsam_priority_list
[i
].pid
;
451 hiwat
= jetsam_priority_list
[i
].hiwat_pages
;
452 /* skip empty or non-hiwat slots in the list */
453 if (aPid
== 0 || (hiwat
< 0)) {
454 continue; // with lock held
458 int32_t pages
= (int32_t)jetsam_task_page_count(p
->task
);
459 boolean_t skip
= (pages
<= hiwat
);
460 #if DEVELOPMENT || DEBUG
461 if (!skip
&& (jetsam_diagnostic_mode
!= kJetsamDiagnosticModeNone
)) {
462 if (jetsam_priority_list
[i
].flags
& kJetsamFlagsSuspForDiagnosis
) {
467 #endif /* DEVELOPMENT || DEBUG */
470 hibernation_node
*node
;
471 if ((node
= kern_hibernation_get_node(aPid
))) {
472 if (node
->state
& kProcessBusy
) {
473 kern_hibernation_release_node(node
);
476 kern_hibernation_free_node(node
, TRUE
);
484 printf("jetsam: %s pid %d [%s] - %d pages > hiwat (%d)\n",
485 (jetsam_diagnostic_mode
!= kJetsamDiagnosticModeNone
)?"suspending": "killing", aPid
, p
->p_comm
, pages
, hiwat
);
487 #if DEVELOPMENT || DEBUG
488 if (jetsam_diagnostic_mode
!= kJetsamDiagnosticModeNone
) {
489 lck_mtx_unlock(jetsam_list_mlock
);
490 task_suspend(p
->task
);
493 printf("jetsam: pid %d suspended for diagnosis - memory_status_level: %d\n", aPid
, kern_memorystatus_level
);
495 jetsam_mark_pid_in_snapshot(aPid
, kJetsamFlagsSuspForDiagnosis
);
496 jetsam_priority_list
[i
].flags
|= kJetsamFlagsSuspForDiagnosis
;
498 #endif /* DEVELOPMENT || DEBUG */
500 jetsam_priority_list
[i
].pid
= 0;
501 lck_mtx_unlock(jetsam_list_mlock
);
502 exit1(p
, W_EXITCODE(0, SIGKILL
), (int *)NULL
);
505 printf("jetsam: pid %d killed - memory_status_level: %d\n", aPid
, kern_memorystatus_level
);
507 jetsam_mark_pid_in_snapshot(aPid
, kJetsamFlagsKilledHiwat
);
516 lck_mtx_unlock(jetsam_list_mlock
);
522 jetsam_send_hibernation_note(uint32_t flags
, pid_t pid
, uint32_t pages
) {
524 struct kev_msg ev_msg
;
525 jetsam_hibernation_entry_t data
;
527 ev_msg
.vendor_code
= KEV_VENDOR_APPLE
;
528 ev_msg
.kev_class
= KEV_SYSTEM_CLASS
;
529 ev_msg
.kev_subclass
= KEV_MEMORYSTATUS_SUBCLASS
;
531 ev_msg
.event_code
= kMemoryStatusHibernationNote
;
533 ev_msg
.dv
[0].data_length
= sizeof data
;
534 ev_msg
.dv
[0].data_ptr
= &data
;
535 ev_msg
.dv
[1].data_length
= 0;
541 ret
= kev_post_msg(&ev_msg
);
543 kern_memorystatus_kev_failure_count
++;
544 printf("%s: kev_post_msg() failed, err %d\n", __func__
, ret
);
549 jetsam_hibernate_top_proc(void)
555 lck_mtx_lock(jetsam_list_mlock
);
557 for (hibernate_index
= jetsam_priority_list_index
; hibernate_index
< jetsam_priority_list_count
; hibernate_index
++) {
561 aPid
= jetsam_priority_list
[hibernate_index
].pid
;
563 /* skip empty slots in the list */
565 continue; // with lock held
568 if (kern_hibernation_get_process_state(aPid
, &state
, NULL
) != 0) {
569 continue; // with lock held
572 /* ensure the process isn't marked as busy and is suspended */
573 if ((state
& kProcessBusy
) || !(state
& kProcessSuspended
)) {
574 continue; // with lock held
579 hibernation_node
*node
;
581 uint32_t purgeable
, wired
, clean
, dirty
;
584 lck_mtx_unlock(jetsam_list_mlock
);
586 if ((node
= kern_hibernation_get_node(aPid
))) {
587 if (node
->state
& kProcessBusy
) {
590 node
->state
|= kProcessBusy
;
591 /* Whether we hibernate or not, increase the count so can we maintain the gap between hibernated and suspended processes. */
592 kern_memorystatus_hibernated_count
++;
595 kern_hibernation_release_node(node
);
601 /* Only hibernate processes meeting our size criteria. If not met, mark it as such and return. */
602 task_freeze(p
->task
, &purgeable
, &wired
, &clean
, &dirty
, &shared
, TRUE
);
603 skip
= (dirty
< kern_memorystatus_hibernation_pages_min
) || (dirty
> kern_memorystatus_hibernation_pages_max
);
607 unsigned int swap_pages_free
= default_pager_swap_pages_free();
609 /* Ensure there's actually enough space free to hibernate this process. */
610 if (dirty
> swap_pages_free
) {
611 kern_memorystatus_low_swap_pages
= swap_pages_free
;
617 kern_hibernation_set_process_state(aPid
, kProcessIgnored
);
623 printf("jetsam: pid %d [%s] hibernating - memory_status_level: %d, purgeable: %d, wired: %d, clean: %d, dirty: %d, shared %d, free swap: %d\n",
624 aPid
, (p
->p_comm
? p
->p_comm
: "(unknown)"), kern_memorystatus_level
, purgeable
, wired
, clean
, dirty
, shared
, default_pager_swap_pages_free());
627 task_freeze(p
->task
, &purgeable
, &wired
, &clean
, &dirty
, &shared
, FALSE
);
630 kern_hibernation_set_process_state(aPid
, kProcessHibernated
| (shared
? 0: kProcessNoReclaimWorth
));
633 for (i
= 0; i
< sizeof(throttle_intervals
) / sizeof(struct throttle_interval_t
); i
++) {
634 throttle_intervals
[i
].pageouts
+= dirty
;
636 kern_memorystatus_hibernation_pageouts
+= dirty
;
637 kern_memorystatus_hibernation_count
++;
639 jetsam_send_hibernation_note(kJetsamFlagsHibernated
, aPid
, dirty
);
644 lck_mtx_unlock(jetsam_list_mlock
);
647 #endif /* CONFIG_FREEZE */
650 kern_memorystatus_thread(void)
652 struct kev_msg ev_msg
;
653 jetsam_kernel_stats_t data
;
654 boolean_t post_memorystatus_snapshot
= FALSE
;
657 bzero(&data
, sizeof(jetsam_kernel_stats_t
));
658 bzero(&ev_msg
, sizeof(struct kev_msg
));
661 #if DEVELOPMENT || DEBUG
662 jetsam_diagnostic_suspended_one_active_proc
= 0;
663 #endif /* DEVELOPMENT || DEBUG */
665 while (kern_memorystatus_level
<= kern_memorystatus_level_highwater
) {
666 if (jetsam_kill_hiwat_proc() < 0) {
669 post_memorystatus_snapshot
= TRUE
;
672 while (kern_memorystatus_level
<= kern_memorystatus_level_critical
) {
673 if (jetsam_kill_top_proc(FALSE
, kJetsamFlagsKilled
) < 0) {
676 post_memorystatus_snapshot
= TRUE
;
677 #if DEVELOPMENT || DEBUG
678 if ((jetsam_diagnostic_mode
== kJetsamDiagnosticModeStopAtFirstActive
) && jetsam_diagnostic_suspended_one_active_proc
) {
679 printf("jetsam: stopping killing since 1 active proc suspended already for diagnosis\n");
680 break; // we found first active proc, let's not kill any more
682 #endif /* DEVELOPMENT || DEBUG */
685 kern_memorystatus_last_level
= kern_memorystatus_level
;
687 ev_msg
.vendor_code
= KEV_VENDOR_APPLE
;
688 ev_msg
.kev_class
= KEV_SYSTEM_CLASS
;
689 ev_msg
.kev_subclass
= KEV_MEMORYSTATUS_SUBCLASS
;
691 /* pass the memory status level (percent free) */
692 ev_msg
.event_code
= kMemoryStatusLevelNote
;
694 ev_msg
.dv
[0].data_length
= sizeof kern_memorystatus_last_level
;
695 ev_msg
.dv
[0].data_ptr
= &kern_memorystatus_last_level
;
696 ev_msg
.dv
[1].data_length
= sizeof data
;
697 ev_msg
.dv
[1].data_ptr
= &data
;
698 ev_msg
.dv
[2].data_length
= 0;
700 data
.free_pages
= vm_page_free_count
;
701 data
.active_pages
= vm_page_active_count
;
702 data
.inactive_pages
= vm_page_inactive_count
;
703 data
.purgeable_pages
= vm_page_purgeable_count
;
704 data
.wired_pages
= vm_page_wire_count
;
706 ret
= kev_post_msg(&ev_msg
);
708 kern_memorystatus_kev_failure_count
++;
709 printf("%s: kev_post_msg() failed, err %d\n", __func__
, ret
);
712 if (post_memorystatus_snapshot
) {
713 size_t snapshot_size
= sizeof(jetsam_kernel_stats_t
) + sizeof(size_t) + sizeof(jetsam_snapshot_entry_t
) * jetsam_snapshot_list_count
;
714 ev_msg
.event_code
= kMemoryStatusSnapshotNote
;
715 ev_msg
.dv
[0].data_length
= sizeof snapshot_size
;
716 ev_msg
.dv
[0].data_ptr
= &snapshot_size
;
717 ev_msg
.dv
[1].data_length
= 0;
719 ret
= kev_post_msg(&ev_msg
);
721 kern_memorystatus_kev_failure_count
++;
722 printf("%s: kev_post_msg() failed, err %d\n", __func__
, ret
);
726 if (kern_memorystatus_level
>= kern_memorystatus_last_level
+ 5 ||
727 kern_memorystatus_level
<= kern_memorystatus_last_level
- 5)
730 assert_wait(&kern_memorystatus_wakeup
, THREAD_UNINT
);
731 (void)thread_block((thread_continue_t
)kern_memorystatus_thread
);
737 __private_extern__
void
738 kern_hibernation_init(void)
740 hibernation_lck_attr
= lck_attr_alloc_init();
741 hibernation_lck_grp_attr
= lck_grp_attr_alloc_init();
742 hibernation_lck_grp
= lck_grp_alloc_init("hibernation", hibernation_lck_grp_attr
);
743 hibernation_mlock
= lck_mtx_alloc_init(hibernation_lck_grp
, hibernation_lck_attr
);
745 RB_INIT(&hibernation_tree_head
);
747 (void)kernel_thread(kernel_task
, kern_hibernation_thread
);
750 static inline boolean_t
751 kern_hibernation_can_hibernate_processes(void)
755 lck_mtx_lock_spin(hibernation_mlock
);
756 ret
= (kern_memorystatus_suspended_count
- kern_memorystatus_hibernated_count
) >
757 kern_memorystatus_hibernation_suspended_minimum
? TRUE
: FALSE
;
758 lck_mtx_unlock(hibernation_mlock
);
764 kern_hibernation_can_hibernate(void)
766 /* Only hibernate if we're sufficiently low on memory; this holds off hibernation right after boot,
767 and is generally is a no-op once we've reached steady state. */
768 if (kern_memorystatus_level
> kern_memorystatus_level_hibernate
) {
772 /* Check minimum suspended process threshold. */
773 if (!kern_hibernation_can_hibernate_processes()) {
777 /* Is swap running low? */
778 if (kern_memorystatus_low_swap_pages
) {
779 /* If there's been no movement in free swap pages since we last attempted hibernation, return. */
780 if (default_pager_swap_pages_free() <= kern_memorystatus_low_swap_pages
) {
784 /* Pages have been freed, so we can retry. */
785 kern_memorystatus_low_swap_pages
= 0;
793 kern_hibernation_add_node(hibernation_node
*node
)
795 lck_mtx_lock_spin(hibernation_mlock
);
797 RB_INSERT(hibernation_tree
, &hibernation_tree_head
, node
);
798 kern_memorystatus_suspended_count
++;
800 lck_mtx_unlock(hibernation_mlock
);
803 /* Returns with the hibernation lock taken */
804 static hibernation_node
*
805 kern_hibernation_get_node(pid_t pid
)
807 hibernation_node sought
, *found
;
809 lck_mtx_lock_spin(hibernation_mlock
);
810 found
= RB_FIND(hibernation_tree
, &hibernation_tree_head
, &sought
);
812 lck_mtx_unlock(hibernation_mlock
);
818 kern_hibernation_release_node(hibernation_node
*node
)
821 lck_mtx_unlock(hibernation_mlock
);
825 kern_hibernation_free_node(hibernation_node
*node
, boolean_t unlock
)
827 /* make sure we're called with the hibernation_mlock held */
828 lck_mtx_assert(hibernation_mlock
, LCK_MTX_ASSERT_OWNED
);
830 if (node
->state
& (kProcessHibernated
| kProcessIgnored
)) {
831 kern_memorystatus_hibernated_count
--;
834 kern_memorystatus_suspended_count
--;
836 RB_REMOVE(hibernation_tree
, &hibernation_tree_head
, node
);
837 kfree(node
, sizeof(hibernation_node
));
840 lck_mtx_unlock(hibernation_mlock
);
845 kern_hibernation_register_pid(pid_t pid
)
847 hibernation_node
*node
;
849 #if DEVELOPMENT || DEBUG
850 node
= kern_hibernation_get_node(pid
);
852 printf("kern_hibernation_register_pid: pid %d already registered!\n", pid
);
853 kern_hibernation_release_node(node
);
858 /* Register as a candiate for hibernation */
859 node
= (hibernation_node
*)kalloc(sizeof(hibernation_node
));
865 memset(node
, 0, sizeof(hibernation_node
));
868 node
->state
= kProcessSuspended
;
870 clock_get_system_nanotime(&sec
, &nsec
);
874 node
->hibernation_ts
= ts
;
876 kern_hibernation_add_node(node
);
881 kern_hibernation_unregister_pid(pid_t pid
)
883 hibernation_node
*node
;
885 node
= kern_hibernation_get_node(pid
);
887 kern_hibernation_free_node(node
, TRUE
);
892 kern_hibernation_on_pid_suspend(pid_t pid
)
894 kern_hibernation_register_pid(pid
);
897 /* If enabled, we bring all the hibernated pages back prior to resumption; otherwise, they're faulted back in on demand */
898 #define THAW_ON_RESUME 1
901 kern_hibernation_on_pid_resume(pid_t pid
, task_t task
)
904 hibernation_node
*node
;
905 if ((node
= kern_hibernation_get_node(pid
))) {
906 if (node
->state
& kProcessHibernated
) {
907 node
->state
|= kProcessBusy
;
908 kern_hibernation_release_node(node
);
910 jetsam_send_hibernation_note(kJetsamFlagsThawed
, pid
, 0);
912 kern_hibernation_release_node(node
);
918 kern_hibernation_unregister_pid(pid
);
922 kern_hibernation_on_pid_hibernate(pid_t pid
)
926 /* Wake the hibernation thread */
927 thread_wakeup((event_t
)&kern_hibernation_wakeup
);
931 kern_hibernation_get_process_state(pid_t pid
, uint32_t *state
, mach_timespec_t
*ts
)
933 hibernation_node
*found
;
938 found
= kern_hibernation_get_node(pid
);
940 *state
= found
->state
;
942 *ts
= found
->hibernation_ts
;
945 kern_hibernation_release_node(found
);
952 kern_hibernation_set_process_state(pid_t pid
, uint32_t state
)
954 hibernation_node
*found
;
957 found
= kern_hibernation_get_node(pid
);
959 found
->state
= state
;
961 kern_hibernation_release_node(found
);
968 kern_hibernation_update_throttle_interval(mach_timespec_t
*ts
, struct throttle_interval_t
*interval
)
970 if (CMP_MACH_TIMESPEC(ts
, &interval
->ts
) >= 0) {
971 if (!interval
->max_pageouts
) {
972 interval
->max_pageouts
= (interval
->burst_multiple
* (((uint64_t)interval
->mins
* HIBERNATION_DAILY_PAGEOUTS_MAX
) / (24 * 60)));
974 printf("jetsam: %d minute throttle timeout, resetting\n", interval
->mins
);
976 interval
->ts
.tv_sec
= interval
->mins
* 60;
977 interval
->ts
.tv_nsec
= 0;
978 ADD_MACH_TIMESPEC(&interval
->ts
, ts
);
979 /* Since we update the throttle stats pre-hibernation, adjust for overshoot here */
980 if (interval
->pageouts
> interval
->max_pageouts
) {
981 interval
->pageouts
-= interval
->max_pageouts
;
983 interval
->pageouts
= 0;
985 interval
->throttle
= FALSE
;
986 } else if (!interval
->throttle
&& interval
->pageouts
>= interval
->max_pageouts
) {
987 printf("jetsam: %d minute pageout limit exceeded; enabling throttle\n", interval
->mins
);
988 interval
->throttle
= TRUE
;
991 printf("jetsam: throttle updated - %d frozen (%d max) within %dm; %dm remaining; throttle %s\n",
992 interval
->pageouts
, interval
->max_pageouts
, interval
->mins
, (interval
->ts
.tv_sec
- ts
->tv_sec
) / 60,
993 interval
->throttle
? "on" : "off");
998 kern_hibernation_throttle_update(void)
1004 boolean_t throttled
= FALSE
;
1006 #if DEVELOPMENT || DEBUG
1007 if (!kern_memorystatus_hibernation_throttle_enabled
)
1011 clock_get_system_nanotime(&sec
, &nsec
);
1015 /* Check hibernation pageouts over multiple intervals and throttle if we've exceeded our budget.
1017 * This ensures that periods of inactivity can't be used as 'credit' towards hibernation if the device has
1018 * remained dormant for a long period. We do, however, allow increased thresholds for shorter intervals in
1019 * order to allow for bursts of activity.
1021 for (i
= 0; i
< sizeof(throttle_intervals
) / sizeof(struct throttle_interval_t
); i
++) {
1022 kern_hibernation_update_throttle_interval(&ts
, &throttle_intervals
[i
]);
1023 if (throttle_intervals
[i
].throttle
== TRUE
)
1031 kern_hibernation_cull(void)
1033 hibernation_node
*node
, *next
;
1034 lck_mtx_lock(hibernation_mlock
);
1036 for (node
= RB_MIN(hibernation_tree
, &hibernation_tree_head
); node
!= NULL
; node
= next
) {
1039 next
= RB_NEXT(hibernation_tree
, &hibernation_tree_head
, node
);
1041 /* TODO: probably suboptimal, so revisit should it cause a performance issue */
1042 p
= proc_find(node
->pid
);
1046 kern_hibernation_free_node(node
, FALSE
);
1050 lck_mtx_unlock(hibernation_mlock
);
1054 kern_hibernation_thread(void)
1056 if (vm_freeze_enabled
) {
1057 if (kern_hibernation_can_hibernate()) {
1059 /* Cull dead processes */
1060 kern_hibernation_cull();
1062 /* Only hibernate if we've not exceeded our pageout budgets */
1063 if (!kern_hibernation_throttle_update()) {
1064 jetsam_hibernate_top_proc();
1066 printf("kern_hibernation_thread: in throttle, ignoring hibernation\n");
1067 kern_memorystatus_hibernation_throttle_count
++; /* Throttled, update stats */
1072 assert_wait((event_t
) &kern_hibernation_wakeup
, THREAD_UNINT
);
1073 thread_block((thread_continue_t
) kern_hibernation_thread
);
1076 #endif /* CONFIG_FREEZE */
1079 sysctl_io_variable(struct sysctl_req
*req
, void *pValue
, size_t currentsize
, size_t maxsize
, size_t *newsize
)
1084 error
= SYSCTL_OUT(req
, pValue
, currentsize
);
1086 /* error or nothing to set */
1087 if (error
|| !req
->newptr
)
1090 if (req
->newlen
> maxsize
) {
1093 error
= SYSCTL_IN(req
, pValue
, req
->newlen
);
1096 *newsize
= req
->newlen
;
1103 sysctl_handle_kern_memorystatus_priority_list(__unused
struct sysctl_oid
*oid
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
1106 jetsam_priority_entry_t temp_list
[kMaxPriorityEntries
];
1107 size_t newsize
, currentsize
;
1110 lck_mtx_lock(jetsam_list_mlock
);
1111 for (i
= 0; i
< jetsam_priority_list_count
; i
++) {
1112 temp_list
[i
] = jetsam_priority_list
[i
];
1114 lck_mtx_unlock(jetsam_list_mlock
);
1117 currentsize
= sizeof(jetsam_priority_list
[0]) * jetsam_priority_list_count
;
1119 ret
= sysctl_io_variable(req
, &temp_list
[0], currentsize
, sizeof(temp_list
), &newsize
);
1121 if (!ret
&& req
->newptr
) {
1122 int temp_list_count
= newsize
/ sizeof(jetsam_priority_list
[0]);
1124 printf("set jetsam priority pids = { ");
1125 for (i
= 0; i
< temp_list_count
; i
++) {
1126 printf("(%d, 0x%08x, %d) ", temp_list
[i
].pid
, temp_list
[i
].flags
, temp_list
[i
].hiwat_pages
);
1130 lck_mtx_lock(jetsam_list_mlock
);
1132 jetsam_priority_list_hibernation_index
= 0;
1134 jetsam_priority_list_index
= 0;
1135 jetsam_priority_list_count
= temp_list_count
;
1136 for (i
= 0; i
< temp_list_count
; i
++) {
1137 jetsam_priority_list
[i
] = temp_list
[i
];
1139 for (i
= temp_list_count
; i
< kMaxPriorityEntries
; i
++) {
1140 jetsam_priority_list
[i
].pid
= 0;
1141 jetsam_priority_list
[i
].flags
= 0;
1142 jetsam_priority_list
[i
].hiwat_pages
= -1;
1143 jetsam_priority_list
[i
].hiwat_reserved1
= -1;
1144 jetsam_priority_list
[i
].hiwat_reserved2
= -1;
1145 jetsam_priority_list
[i
].hiwat_reserved3
= -1;
1147 lck_mtx_unlock(jetsam_list_mlock
);
1153 sysctl_handle_kern_memorystatus_snapshot(__unused
struct sysctl_oid
*oid
, __unused
void *arg1
, __unused
int arg2
, struct sysctl_req
*req
)
1156 size_t currentsize
= 0;
1158 if (jetsam_snapshot_list_count
> 0) {
1159 currentsize
= sizeof(jetsam_kernel_stats_t
) + sizeof(size_t) + sizeof(jetsam_snapshot_entry_t
) * jetsam_snapshot_list_count
;
1164 printf("kern.memorystatus_snapshot returning EINVAL\n");
1170 printf("kern.memorystatus_snapshot returning 0 for size\n");
1175 printf("kern.memorystatus_snapshot returning %ld for size\n", (long)currentsize
);
1178 ret
= sysctl_io_variable(req
, &jetsam_snapshot
, currentsize
, 0, NULL
);
1179 if (!ret
&& req
->oldptr
) {
1180 jetsam_snapshot
.entry_count
= jetsam_snapshot_list_count
= 0;
1185 SYSCTL_PROC(_kern
, OID_AUTO
, memorystatus_priority_list
, CTLTYPE_OPAQUE
|CTLFLAG_RW
| CTLFLAG_LOCKED
, 0, 0, sysctl_handle_kern_memorystatus_priority_list
, "S,jetsam_priorities", "");
1186 SYSCTL_PROC(_kern
, OID_AUTO
, memorystatus_snapshot
, CTLTYPE_OPAQUE
|CTLFLAG_RD
, 0, 0, sysctl_handle_kern_memorystatus_snapshot
, "S,jetsam_snapshot", "");