X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/e2fac8b15b12a7979f72090454d850e612fc5b13..b0d623f7f2ae71ed96e60569f61f9a9a27016e80:/bsd/kern/kern_memorystatus.c?ds=sidebyside diff --git a/bsd/kern/kern_memorystatus.c b/bsd/kern/kern_memorystatus.c index 912fdef3f..f5e141455 100644 --- a/bsd/kern/kern_memorystatus.c +++ b/bsd/kern/kern_memorystatus.c @@ -26,40 +26,22 @@ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ * */ -/*- - * Copyright (c) 1999,2000,2001 Jonathan Lemon - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ #include #include #include +#include #include #include #include +#include +#include +#include +#include +#include #include +#include extern unsigned int vm_page_free_count; extern unsigned int vm_page_active_count; @@ -73,6 +55,25 @@ int kern_memorystatus_wakeup = 0; int kern_memorystatus_level = 0; int kern_memorystatus_last_level = 0; unsigned int kern_memorystatus_kev_failure_count = 0; +int kern_memorystatus_level_critical = 5; + +static struct { + jetsam_kernel_stats_t stats; + size_t entry_count; + jetsam_snapshot_entry_t entries[kMaxSnapshotEntries]; +} jetsam_snapshot; + +static jetsam_priority_entry_t jetsam_priority_list[kMaxPriorityEntries]; +#define jetsam_snapshot_list jetsam_snapshot.entries + +static int jetsam_priority_list_index = 0; +static int jetsam_priority_list_count = 0; +static int jetsam_snapshot_list_count = 0; + +static lck_mtx_t * jetsam_list_mlock; +static lck_attr_t * jetsam_lck_attr; +static lck_grp_t * jetsam_lck_grp; +static lck_grp_attr_t * jetsam_lck_grp_attr; SYSCTL_INT(_kern, OID_AUTO, memorystatus_level, CTLFLAG_RD, &kern_memorystatus_level, 0, ""); SYSCTL_UINT(_kern, OID_AUTO, memorystatus_kev_failure_count, CTLFLAG_RD, &kern_memorystatus_kev_failure_count, 0, ""); @@ -80,36 +81,156 @@ SYSCTL_UINT(_kern, OID_AUTO, memorystatus_kev_failure_count, CTLFLAG_RD, &kern_m __private_extern__ void kern_memorystatus_init(void) { + jetsam_lck_attr = lck_attr_alloc_init(); + jetsam_lck_grp_attr= lck_grp_attr_alloc_init(); + jetsam_lck_grp = lck_grp_alloc_init("jetsam", jetsam_lck_grp_attr); + jetsam_list_mlock = lck_mtx_alloc_init(jetsam_lck_grp, jetsam_lck_attr); + (void)kernel_thread(kernel_task, kern_memorystatus_thread); } +static uint32_t +jetsam_task_page_count(task_t task) +{ + kern_return_t ret; + static task_info_data_t data; + static struct task_basic_info *info = (struct task_basic_info *)&data; + static mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT; + + ret = task_info(task, TASK_BASIC_INFO, (task_info_t)&data, &count); + if (ret == KERN_SUCCESS) { + return info->resident_size / PAGE_SIZE; + } + return 0; +} + +static uint32_t +jetsam_flags_for_pid(pid_t pid) +{ + int i; + + for (i = 0; i < jetsam_priority_list_count; i++) { + if (pid == jetsam_priority_list[i].pid) { + return jetsam_priority_list[i].flags; + } + } + return 0; +} + +static void +jetsam_snapshot_procs(void) +{ + proc_t p; + int i = 0; + + jetsam_snapshot.stats.free_pages = vm_page_free_count; + jetsam_snapshot.stats.active_pages = vm_page_active_count; + jetsam_snapshot.stats.inactive_pages = vm_page_inactive_count; + jetsam_snapshot.stats.purgeable_pages = vm_page_purgeable_count; + jetsam_snapshot.stats.wired_pages = vm_page_wire_count; + proc_list_lock(); + LIST_FOREACH(p, &allproc, p_list) { + task_t task = p->task; + jetsam_snapshot_list[i].pid = p->p_pid; + jetsam_snapshot_list[i].pages = jetsam_task_page_count(task); + jetsam_snapshot_list[i].flags = jetsam_flags_for_pid(p->p_pid); + strlcpy(&jetsam_snapshot_list[i].name[0], p->p_comm, MAXCOMLEN+1); +#ifdef DEBUG + printf("jetsam snapshot pid = %d, uuid = %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n", + p->p_pid, + 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], + 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]); +#endif + memcpy(&jetsam_snapshot_list[i].uuid[0], &p->p_uuid[0], sizeof(p->p_uuid)); + i++; + if (i == kMaxSnapshotEntries) { + break; + } + } + proc_list_unlock(); + jetsam_snapshot.entry_count = jetsam_snapshot_list_count = i - 1; +} + +static void +jetsam_mark_pid_in_snapshot(pid_t pid) +{ + + int i = 0; + + for (i = 0; i < jetsam_snapshot_list_count; i++) { + if (jetsam_snapshot_list[i].pid == pid) { + jetsam_snapshot_list[i].flags |= kJetsamFlagsKilled; + return; + } + } +} + +static int +jetsam_kill_top_proc(void) +{ + proc_t p; + + if (jetsam_snapshot_list_count == 0) { + jetsam_snapshot_procs(); + } + lck_mtx_lock(jetsam_list_mlock); + while (jetsam_priority_list_index < jetsam_priority_list_count) { + pid_t aPid; + aPid = jetsam_priority_list[jetsam_priority_list_index].pid; + jetsam_priority_list_index++; + /* skip empty slots in the list */ + if (aPid == 0) { + continue; // with lock held + } + lck_mtx_unlock(jetsam_list_mlock); + jetsam_mark_pid_in_snapshot(aPid); + p = proc_find(aPid); + if (p != NULL) { +#if DEBUG + printf("jetsam: killing pid %d [%s] - memory_status_level: %d - ", aPid, p->p_comm, kern_memorystatus_level); +#endif /* DEBUG */ + exit1(p, W_EXITCODE(0, SIGKILL), (int *)NULL); + proc_rele(p); +#if DEBUG + printf("jetsam: pid %d killed - memory_status_level: %d\n", aPid, kern_memorystatus_level); +#endif /* DEBUG */ + return 0; + } + lck_mtx_lock(jetsam_list_mlock); + } + lck_mtx_unlock(jetsam_list_mlock); + return -1; +} + static void kern_memorystatus_thread(void) { struct kev_msg ev_msg; - struct { - uint32_t free_pages; - uint32_t active_pages; - uint32_t inactive_pages; - uint32_t purgeable_pages; - uint32_t wired_pages; - } data; + jetsam_kernel_stats_t data; int ret; while(1) { - + + while (kern_memorystatus_level <= kern_memorystatus_level_critical) { + if (jetsam_kill_top_proc() < 0) { + break; + } + } + kern_memorystatus_last_level = kern_memorystatus_level; ev_msg.vendor_code = KEV_VENDOR_APPLE; ev_msg.kev_class = KEV_SYSTEM_CLASS; ev_msg.kev_subclass = KEV_MEMORYSTATUS_SUBCLASS; - /* pass the memory status level in the event code (as percent used) */ - ev_msg.event_code = 100 - kern_memorystatus_last_level; + /* pass the memory status level (percent free) */ + ev_msg.event_code = kMemoryStatusLevelNote; - ev_msg.dv[0].data_length = sizeof data; - ev_msg.dv[0].data_ptr = &data; - ev_msg.dv[1].data_length = 0; + ev_msg.dv[0].data_length = sizeof kern_memorystatus_last_level; + ev_msg.dv[0].data_ptr = &kern_memorystatus_last_level; + ev_msg.dv[1].data_length = sizeof data; + ev_msg.dv[1].data_ptr = &data; + ev_msg.dv[2].data_length = 0; data.free_pages = vm_page_free_count; data.active_pages = vm_page_active_count; @@ -123,6 +244,20 @@ kern_memorystatus_thread(void) printf("%s: kev_post_msg() failed, err %d\n", __func__, ret); } + if (jetsam_snapshot_list_count) { + size_t snapshot_size = sizeof(jetsam_kernel_stats_t) + sizeof(size_t) + sizeof(jetsam_snapshot_entry_t) * jetsam_snapshot_list_count; + ev_msg.event_code = kMemoryStatusSnapshotNote; + ev_msg.dv[0].data_length = sizeof snapshot_size; + ev_msg.dv[0].data_ptr = &snapshot_size; + ev_msg.dv[1].data_length = 0; + + ret = kev_post_msg(&ev_msg); + if (ret) { + kern_memorystatus_kev_failure_count++; + printf("%s: kev_post_msg() failed, err %d\n", __func__, ret); + } + } + if (kern_memorystatus_level >= kern_memorystatus_last_level + 5 || kern_memorystatus_level <= kern_memorystatus_last_level - 5) continue; @@ -131,3 +266,105 @@ kern_memorystatus_thread(void) (void)thread_block((thread_continue_t)kern_memorystatus_thread); } } + +static int +sysctl_io_variable(struct sysctl_req *req, void *pValue, size_t currentsize, size_t maxsize, size_t *newsize) +{ + int error; + + /* Copy blob out */ + error = SYSCTL_OUT(req, pValue, currentsize); + + /* error or nothing to set */ + if (error || !req->newptr) + return(error); + + if (req->newlen > maxsize) { + return EINVAL; + } + error = SYSCTL_IN(req, pValue, req->newlen); + + if (!error) { + *newsize = req->newlen; + } + + return(error); +} + +static int +sysctl_handle_kern_memorystatus_priority_list(__unused struct sysctl_oid *oid, __unused void *arg1, __unused int arg2, struct sysctl_req *req) +{ + int i, ret; + jetsam_priority_entry_t temp_list[kMaxPriorityEntries]; + size_t newsize, currentsize; + + if (req->oldptr) { + lck_mtx_lock(jetsam_list_mlock); + for (i = 0; i < jetsam_priority_list_count; i++) { + temp_list[i] = jetsam_priority_list[i]; + } + lck_mtx_unlock(jetsam_list_mlock); + } + + currentsize = sizeof(jetsam_priority_list[0]) * jetsam_priority_list_count; + + ret = sysctl_io_variable(req, &temp_list[0], currentsize, sizeof(temp_list), &newsize); + + if (!ret && req->newptr) { + jetsam_priority_list_count = newsize / sizeof(jetsam_priority_list[0]); +#if DEBUG + printf("set jetsam priority pids = { "); + for (i = 0; i < jetsam_priority_list_count; i++) { + printf("%d ", temp_list[i].pid); + } + printf("}\n"); +#endif /* DEBUG */ + lck_mtx_lock(jetsam_list_mlock); + for (i = 0; i < jetsam_priority_list_count; i++) { + jetsam_priority_list[i] = temp_list[i]; + } + for (i = jetsam_priority_list_count; i < kMaxPriorityEntries; i++) { + jetsam_priority_list[i].pid = 0; + jetsam_priority_list[i].flags = 0; + } + jetsam_priority_list_index = 0; + lck_mtx_unlock(jetsam_list_mlock); + } + return ret; +} + +static int +sysctl_handle_kern_memorystatus_snapshot(__unused struct sysctl_oid *oid, __unused void *arg1, __unused int arg2, struct sysctl_req *req) +{ + int ret; + size_t currentsize = 0; + + if (jetsam_snapshot_list_count > 0) { + currentsize = sizeof(jetsam_kernel_stats_t) + sizeof(size_t) + sizeof(jetsam_snapshot_entry_t) * jetsam_snapshot_list_count; + } + if (!currentsize) { + if (req->oldptr) { +#ifdef DEBUG + printf("kern.memorystatus_snapshot returning EINVAL\n"); +#endif + return EINVAL; + } + else { +#ifdef DEBUG + printf("kern.memorystatus_snapshot returning 0 for size\n"); +#endif + } + } else { +#ifdef DEBUG + printf("kern.memorystatus_snapshot returning %ld for size\n", (long)currentsize); +#endif + } + ret = sysctl_io_variable(req, &jetsam_snapshot, currentsize, 0, NULL); + if (!ret && req->oldptr) { + jetsam_snapshot.entry_count = jetsam_snapshot_list_count = 0; + } + return ret; +} + +SYSCTL_PROC(_kern, OID_AUTO, memorystatus_priority_list, CTLTYPE_OPAQUE|CTLFLAG_RW, 0, 0, sysctl_handle_kern_memorystatus_priority_list, "S,jetsam_priorities", ""); +SYSCTL_PROC(_kern, OID_AUTO, memorystatus_snapshot, CTLTYPE_OPAQUE|CTLFLAG_RD, 0, 0, sysctl_handle_kern_memorystatus_snapshot, "S,jetsam_snapshot", "");