2 * Copyright (c) 2009-2010 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@
29 #include <libkern/libkern.h>
30 #include <mach/mach_types.h>
31 #include <mach/task.h>
32 #include <sys/proc_internal.h>
33 #include <sys/event.h>
34 #include <sys/eventvar.h>
35 #include <kern/locks.h>
36 #include <sys/queue.h>
37 #include <kern/vm_pressure.h>
38 #include <sys/malloc.h>
39 #include <sys/errno.h>
40 #include <sys/systm.h>
41 #include <sys/types.h>
42 #include <sys/sysctl.h>
44 void vm_pressure_klist_lock(void);
45 void vm_pressure_klist_unlock(void);
47 void vm_dispatch_memory_pressure(void);
48 int vm_try_terminate_candidates(void);
49 int vm_try_pressure_candidates(void);
50 void vm_recharge_active_list(void);
52 struct klist vm_pressure_klist
;
53 struct klist vm_pressure_klist_dormant
;
55 void vm_pressure_klist_lock(void) {
56 lck_mtx_lock(&vm_pressure_klist_mutex
);
59 void vm_pressure_klist_unlock(void) {
60 lck_mtx_unlock(&vm_pressure_klist_mutex
);
63 int vm_knote_register(struct knote
*kn
) {
66 vm_pressure_klist_lock();
68 if ((kn
->kn_sfflags
& (NOTE_VM_PRESSURE
))) {
70 printf("[vm_pressure] process %d registering pressure notification\n", kn
->kn_kq
->kq_p
->p_pid
);
72 KNOTE_ATTACH(&vm_pressure_klist
, kn
);
76 vm_pressure_klist_unlock();
81 void vm_knote_unregister(struct knote
*kn
) {
82 struct knote
*kn_temp
;
84 vm_pressure_klist_lock();
87 printf("[vm_pressure] process %d cancelling pressure notification\n", kn
->kn_kq
->kq_p
->p_pid
);
90 SLIST_FOREACH(kn_temp
, &vm_pressure_klist
, kn_selnext
) {
92 KNOTE_DETACH(&vm_pressure_klist
, kn
);
93 vm_pressure_klist_unlock();
97 KNOTE_DETACH(&vm_pressure_klist_dormant
, kn
);
99 vm_pressure_klist_unlock();
102 /* Interface for event dispatch from vm_pageout_garbage_collect thread */
103 void consider_pressure_events(void) {
104 vm_dispatch_memory_pressure();
107 void vm_dispatch_memory_pressure(void) {
108 vm_pressure_klist_lock();
110 if (!SLIST_EMPTY(&vm_pressure_klist
)) {
113 printf("[vm_pressure] vm_dispatch_memory_pressure\n");
116 if (vm_try_pressure_candidates()) {
117 vm_pressure_klist_unlock();
126 printf("[vm_pressure] could not find suitable event candidate\n");
129 vm_recharge_active_list();
131 vm_pressure_klist_unlock();
135 * Try standard pressure event candidates. Called with klist lock held.
137 int vm_try_pressure_candidates(void) {
139 * This value is the threshold that a process must meet to be considered for scavenging.
140 * If a process has sufficiently little resident memory, there is probably no use scavenging it.
141 * At best, we'll scavenge very little memory. At worst, we'll page in code pages or malloc metadata.
144 #define VM_PRESSURE_MINIMUM_RSIZE (10 * 1024 * 1024)
146 struct proc
*p_max
= NULL
;
147 unsigned int resident_max
= 0;
148 struct knote
*kn_max
= NULL
;
151 SLIST_FOREACH(kn
, &vm_pressure_klist
, kn_selnext
) {
152 if ( (kn
!= NULL
) && ( kn
->kn_kq
!= NULL
) && ( kn
->kn_kq
->kq_p
!= NULL
) ) {
153 if (kn
->kn_sfflags
& NOTE_VM_PRESSURE
) {
154 struct proc
*p
= kn
->kn_kq
->kq_p
;
155 if (!(kn
->kn_status
& KN_DISABLED
)) {
156 kern_return_t kr
= KERN_SUCCESS
;
157 struct task
*t
= (struct task
*)(p
->task
);
158 struct task_basic_info basic_info
;
159 mach_msg_type_number_t size
= TASK_BASIC_INFO_COUNT
;
160 if( ( kr
= task_info(t
, TASK_BASIC_INFO
, (task_info_t
)(&basic_info
), &size
)) == KERN_SUCCESS
) {
161 unsigned int resident_size
= basic_info
.resident_size
;
163 * We don't want a small process to block large processes from
164 * being notified again. <rdar://problem/7955532>
166 if (resident_size
>= VM_PRESSURE_MINIMUM_RSIZE
) {
167 if (resident_size
> resident_max
) {
169 resident_max
= resident_size
;
174 /* There was no candidate with enough resident memory to scavenge */
175 /* This debug print makes too much noise now */
176 //printf("[vm_pressure] threshold failed for pid %d with %u resident, skipping...\n", p->p_pid, resident_size);
181 printf("[vm_pressure] task_info for pid %d failed with %d\n", p
->p_pid
, kr
);
186 printf("[vm_pressure] pid %d currently disabled, skipping...\n", p
->p_pid
);
193 printf("[vm_pressure] kn is NULL\n");
194 } else if (kn
->kn_kq
== NULL
) {
195 printf("[vm_pressure] kn->kn_kq is NULL\n");
196 } else if (kn
->kn_kq
->kq_p
== NULL
) {
197 printf("[vm_pressure] kn->kn_kq->kq_p is NULL\n");
203 if (kn_max
== NULL
) return 0;
206 printf("[vm_pressure] sending event to pid %d with %u resident\n", kn_max
->kn_kq
->kq_p
->p_pid
, resident_max
);
209 KNOTE_DETACH(&vm_pressure_klist
, kn_max
);
210 struct klist dispatch_klist
= { NULL
};
211 KNOTE_ATTACH(&dispatch_klist
, kn_max
);
212 KNOTE(&dispatch_klist
, NOTE_VM_PRESSURE
);
213 KNOTE_ATTACH(&vm_pressure_klist_dormant
, kn_max
);
220 * Remove all elements from the dormant list and place them on the active list.
221 * Called with klist lock held.
223 void vm_recharge_active_list(void) {
224 /* Re-charge the main list from the dormant list if possible */
225 if (!SLIST_EMPTY(&vm_pressure_klist_dormant
)) {
227 printf("[vm_pressure] recharging main list from dormant list\n");
230 while (!SLIST_EMPTY(&vm_pressure_klist_dormant
)) {
231 kn
= SLIST_FIRST(&vm_pressure_klist_dormant
);
232 SLIST_REMOVE_HEAD(&vm_pressure_klist_dormant
, kn_selnext
);
233 SLIST_INSERT_HEAD(&vm_pressure_klist
, kn
, kn_selnext
);