]> git.saurik.com Git - apple/xnu.git/blob - bsd/kern/kern_memorystatus.c
xnu-1504.3.12.tar.gz
[apple/xnu.git] / bsd / kern / kern_memorystatus.c
1 /*
2 * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 *
28 */
29
30 #include <sys/kern_event.h>
31 #include <sys/kern_memorystatus.h>
32
33 #include <kern/sched_prim.h>
34 #include <kern/lock.h>
35 #include <kern/task.h>
36 #include <kern/thread.h>
37 #include <libkern/libkern.h>
38 #include <mach/task.h>
39 #include <mach/task_info.h>
40 #include <sys/proc.h>
41 #include <sys/signal.h>
42 #include <sys/signalvar.h>
43 #include <sys/sysctl.h>
44 #include <sys/wait.h>
45
46 extern unsigned int vm_page_free_count;
47 extern unsigned int vm_page_active_count;
48 extern unsigned int vm_page_inactive_count;
49 extern unsigned int vm_page_purgeable_count;
50 extern unsigned int vm_page_wire_count;
51
52 static void kern_memorystatus_thread(void);
53
54 int kern_memorystatus_wakeup = 0;
55 int kern_memorystatus_level = 0;
56 int kern_memorystatus_last_level = 0;
57 unsigned int kern_memorystatus_kev_failure_count = 0;
58 int kern_memorystatus_level_critical = 5;
59
60 static struct {
61 jetsam_kernel_stats_t stats;
62 size_t entry_count;
63 jetsam_snapshot_entry_t entries[kMaxSnapshotEntries];
64 } jetsam_snapshot;
65
66 static jetsam_priority_entry_t jetsam_priority_list[kMaxPriorityEntries];
67 #define jetsam_snapshot_list jetsam_snapshot.entries
68
69 static int jetsam_priority_list_index = 0;
70 static int jetsam_priority_list_count = 0;
71 static int jetsam_snapshot_list_count = 0;
72
73 static lck_mtx_t * jetsam_list_mlock;
74 static lck_attr_t * jetsam_lck_attr;
75 static lck_grp_t * jetsam_lck_grp;
76 static lck_grp_attr_t * jetsam_lck_grp_attr;
77
78 SYSCTL_INT(_kern, OID_AUTO, memorystatus_level, CTLFLAG_RD, &kern_memorystatus_level, 0, "");
79 SYSCTL_UINT(_kern, OID_AUTO, memorystatus_kev_failure_count, CTLFLAG_RD, &kern_memorystatus_kev_failure_count, 0, "");
80
81 __private_extern__ void
82 kern_memorystatus_init(void)
83 {
84 jetsam_lck_attr = lck_attr_alloc_init();
85 jetsam_lck_grp_attr= lck_grp_attr_alloc_init();
86 jetsam_lck_grp = lck_grp_alloc_init("jetsam", jetsam_lck_grp_attr);
87 jetsam_list_mlock = lck_mtx_alloc_init(jetsam_lck_grp, jetsam_lck_attr);
88
89 (void)kernel_thread(kernel_task, kern_memorystatus_thread);
90 }
91
92 static uint32_t
93 jetsam_task_page_count(task_t task)
94 {
95 kern_return_t ret;
96 static task_info_data_t data;
97 static struct task_basic_info *info = (struct task_basic_info *)&data;
98 static mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
99
100 ret = task_info(task, TASK_BASIC_INFO, (task_info_t)&data, &count);
101 if (ret == KERN_SUCCESS) {
102 return info->resident_size / PAGE_SIZE;
103 }
104 return 0;
105 }
106
107 static uint32_t
108 jetsam_flags_for_pid(pid_t pid)
109 {
110 int i;
111
112 for (i = 0; i < jetsam_priority_list_count; i++) {
113 if (pid == jetsam_priority_list[i].pid) {
114 return jetsam_priority_list[i].flags;
115 }
116 }
117 return 0;
118 }
119
120 static void
121 jetsam_snapshot_procs(void)
122 {
123 proc_t p;
124 int i = 0;
125
126 jetsam_snapshot.stats.free_pages = vm_page_free_count;
127 jetsam_snapshot.stats.active_pages = vm_page_active_count;
128 jetsam_snapshot.stats.inactive_pages = vm_page_inactive_count;
129 jetsam_snapshot.stats.purgeable_pages = vm_page_purgeable_count;
130 jetsam_snapshot.stats.wired_pages = vm_page_wire_count;
131 proc_list_lock();
132 LIST_FOREACH(p, &allproc, p_list) {
133 task_t task = p->task;
134 jetsam_snapshot_list[i].pid = p->p_pid;
135 jetsam_snapshot_list[i].pages = jetsam_task_page_count(task);
136 jetsam_snapshot_list[i].flags = jetsam_flags_for_pid(p->p_pid);
137 strlcpy(&jetsam_snapshot_list[i].name[0], p->p_comm, MAXCOMLEN+1);
138 #ifdef DEBUG
139 printf("jetsam snapshot pid = %d, uuid = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
140 p->p_pid,
141 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],
142 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]);
143 #endif
144 memcpy(&jetsam_snapshot_list[i].uuid[0], &p->p_uuid[0], sizeof(p->p_uuid));
145 i++;
146 if (i == kMaxSnapshotEntries) {
147 break;
148 }
149 }
150 proc_list_unlock();
151 jetsam_snapshot.entry_count = jetsam_snapshot_list_count = i - 1;
152 }
153
154 static void
155 jetsam_mark_pid_in_snapshot(pid_t pid)
156 {
157
158 int i = 0;
159
160 for (i = 0; i < jetsam_snapshot_list_count; i++) {
161 if (jetsam_snapshot_list[i].pid == pid) {
162 jetsam_snapshot_list[i].flags |= kJetsamFlagsKilled;
163 return;
164 }
165 }
166 }
167
168 static int
169 jetsam_kill_top_proc(void)
170 {
171 proc_t p;
172
173 if (jetsam_snapshot_list_count == 0) {
174 jetsam_snapshot_procs();
175 }
176 lck_mtx_lock(jetsam_list_mlock);
177 while (jetsam_priority_list_index < jetsam_priority_list_count) {
178 pid_t aPid;
179 aPid = jetsam_priority_list[jetsam_priority_list_index].pid;
180 jetsam_priority_list_index++;
181 /* skip empty slots in the list */
182 if (aPid == 0) {
183 continue; // with lock held
184 }
185 lck_mtx_unlock(jetsam_list_mlock);
186 jetsam_mark_pid_in_snapshot(aPid);
187 p = proc_find(aPid);
188 if (p != NULL) {
189 #if DEBUG
190 printf("jetsam: killing pid %d [%s] - memory_status_level: %d - ", aPid, p->p_comm, kern_memorystatus_level);
191 #endif /* DEBUG */
192 exit1(p, W_EXITCODE(0, SIGKILL), (int *)NULL);
193 proc_rele(p);
194 #if DEBUG
195 printf("jetsam: pid %d killed - memory_status_level: %d\n", aPid, kern_memorystatus_level);
196 #endif /* DEBUG */
197 return 0;
198 }
199 lck_mtx_lock(jetsam_list_mlock);
200 }
201 lck_mtx_unlock(jetsam_list_mlock);
202 return -1;
203 }
204
205 static void
206 kern_memorystatus_thread(void)
207 {
208 struct kev_msg ev_msg;
209 jetsam_kernel_stats_t data;
210 int ret;
211
212 while(1) {
213
214 while (kern_memorystatus_level <= kern_memorystatus_level_critical) {
215 if (jetsam_kill_top_proc() < 0) {
216 break;
217 }
218 }
219
220 kern_memorystatus_last_level = kern_memorystatus_level;
221
222 ev_msg.vendor_code = KEV_VENDOR_APPLE;
223 ev_msg.kev_class = KEV_SYSTEM_CLASS;
224 ev_msg.kev_subclass = KEV_MEMORYSTATUS_SUBCLASS;
225
226 /* pass the memory status level (percent free) */
227 ev_msg.event_code = kMemoryStatusLevelNote;
228
229 ev_msg.dv[0].data_length = sizeof kern_memorystatus_last_level;
230 ev_msg.dv[0].data_ptr = &kern_memorystatus_last_level;
231 ev_msg.dv[1].data_length = sizeof data;
232 ev_msg.dv[1].data_ptr = &data;
233 ev_msg.dv[2].data_length = 0;
234
235 data.free_pages = vm_page_free_count;
236 data.active_pages = vm_page_active_count;
237 data.inactive_pages = vm_page_inactive_count;
238 data.purgeable_pages = vm_page_purgeable_count;
239 data.wired_pages = vm_page_wire_count;
240
241 ret = kev_post_msg(&ev_msg);
242 if (ret) {
243 kern_memorystatus_kev_failure_count++;
244 printf("%s: kev_post_msg() failed, err %d\n", __func__, ret);
245 }
246
247 if (jetsam_snapshot_list_count) {
248 size_t snapshot_size = sizeof(jetsam_kernel_stats_t) + sizeof(size_t) + sizeof(jetsam_snapshot_entry_t) * jetsam_snapshot_list_count;
249 ev_msg.event_code = kMemoryStatusSnapshotNote;
250 ev_msg.dv[0].data_length = sizeof snapshot_size;
251 ev_msg.dv[0].data_ptr = &snapshot_size;
252 ev_msg.dv[1].data_length = 0;
253
254 ret = kev_post_msg(&ev_msg);
255 if (ret) {
256 kern_memorystatus_kev_failure_count++;
257 printf("%s: kev_post_msg() failed, err %d\n", __func__, ret);
258 }
259 }
260
261 if (kern_memorystatus_level >= kern_memorystatus_last_level + 5 ||
262 kern_memorystatus_level <= kern_memorystatus_last_level - 5)
263 continue;
264
265 assert_wait(&kern_memorystatus_wakeup, THREAD_UNINT);
266 (void)thread_block((thread_continue_t)kern_memorystatus_thread);
267 }
268 }
269
270 static int
271 sysctl_io_variable(struct sysctl_req *req, void *pValue, size_t currentsize, size_t maxsize, size_t *newsize)
272 {
273 int error;
274
275 /* Copy blob out */
276 error = SYSCTL_OUT(req, pValue, currentsize);
277
278 /* error or nothing to set */
279 if (error || !req->newptr)
280 return(error);
281
282 if (req->newlen > maxsize) {
283 return EINVAL;
284 }
285 error = SYSCTL_IN(req, pValue, req->newlen);
286
287 if (!error) {
288 *newsize = req->newlen;
289 }
290
291 return(error);
292 }
293
294 static int
295 sysctl_handle_kern_memorystatus_priority_list(__unused struct sysctl_oid *oid, __unused void *arg1, __unused int arg2, struct sysctl_req *req)
296 {
297 int i, ret;
298 jetsam_priority_entry_t temp_list[kMaxPriorityEntries];
299 size_t newsize, currentsize;
300
301 if (req->oldptr) {
302 lck_mtx_lock(jetsam_list_mlock);
303 for (i = 0; i < jetsam_priority_list_count; i++) {
304 temp_list[i] = jetsam_priority_list[i];
305 }
306 lck_mtx_unlock(jetsam_list_mlock);
307 }
308
309 currentsize = sizeof(jetsam_priority_list[0]) * jetsam_priority_list_count;
310
311 ret = sysctl_io_variable(req, &temp_list[0], currentsize, sizeof(temp_list), &newsize);
312
313 if (!ret && req->newptr) {
314 jetsam_priority_list_count = newsize / sizeof(jetsam_priority_list[0]);
315 #if DEBUG
316 printf("set jetsam priority pids = { ");
317 for (i = 0; i < jetsam_priority_list_count; i++) {
318 printf("(%d, 0x%08x, %d) ", temp_list[i].pid, temp_list[i].flags, temp_list[i].hiwat_pages);
319 }
320 printf("}\n");
321 #endif /* DEBUG */
322 lck_mtx_lock(jetsam_list_mlock);
323 for (i = 0; i < jetsam_priority_list_count; i++) {
324 jetsam_priority_list[i] = temp_list[i];
325 }
326 for (i = jetsam_priority_list_count; i < kMaxPriorityEntries; i++) {
327 jetsam_priority_list[i].pid = 0;
328 jetsam_priority_list[i].flags = 0;
329 jetsam_priority_list[i].hiwat_pages = -1;
330 jetsam_priority_list[i].hiwat_reserved1 = -1;
331 jetsam_priority_list[i].hiwat_reserved2 = -1;
332 jetsam_priority_list[i].hiwat_reserved3 = -1;
333 }
334 jetsam_priority_list_index = 0;
335 lck_mtx_unlock(jetsam_list_mlock);
336 }
337 return ret;
338 }
339
340 static int
341 sysctl_handle_kern_memorystatus_snapshot(__unused struct sysctl_oid *oid, __unused void *arg1, __unused int arg2, struct sysctl_req *req)
342 {
343 int ret;
344 size_t currentsize = 0;
345
346 if (jetsam_snapshot_list_count > 0) {
347 currentsize = sizeof(jetsam_kernel_stats_t) + sizeof(size_t) + sizeof(jetsam_snapshot_entry_t) * jetsam_snapshot_list_count;
348 }
349 if (!currentsize) {
350 if (req->oldptr) {
351 #ifdef DEBUG
352 printf("kern.memorystatus_snapshot returning EINVAL\n");
353 #endif
354 return EINVAL;
355 }
356 else {
357 #ifdef DEBUG
358 printf("kern.memorystatus_snapshot returning 0 for size\n");
359 #endif
360 }
361 } else {
362 #ifdef DEBUG
363 printf("kern.memorystatus_snapshot returning %ld for size\n", (long)currentsize);
364 #endif
365 }
366 ret = sysctl_io_variable(req, &jetsam_snapshot, currentsize, 0, NULL);
367 if (!ret && req->oldptr) {
368 jetsam_snapshot.entry_count = jetsam_snapshot_list_count = 0;
369 }
370 return ret;
371 }
372
373 SYSCTL_PROC(_kern, OID_AUTO, memorystatus_priority_list, CTLTYPE_OPAQUE|CTLFLAG_RW, 0, 0, sysctl_handle_kern_memorystatus_priority_list, "S,jetsam_priorities", "");
374 SYSCTL_PROC(_kern, OID_AUTO, memorystatus_snapshot, CTLTYPE_OPAQUE|CTLFLAG_RD, 0, 0, sysctl_handle_kern_memorystatus_snapshot, "S,jetsam_snapshot", "");