-/*
- * Triggers a sort_order on a specified jetsam priority band.
- * This is for testing only, used to force a path through the sort
- * function.
- */
-static int
-memorystatus_cmd_test_jetsam_sort(int priority, int sort_order) {
-
- int error = 0;
-
- unsigned int bucket_index = 0;
-
- if (priority == -1) {
- /* Use as shorthand for default priority */
- bucket_index = JETSAM_PRIORITY_DEFAULT;
- } else {
- bucket_index = (unsigned int)priority;
- }
-
- error = memorystatus_sort_bucket(bucket_index, sort_order);
-
- return (error);
-}
-
-#endif /* DEVELOPMENT || DEBUG */
-
-/*
- * Jetsam the first process in the queue.
- */
-static boolean_t
-memorystatus_kill_top_process(boolean_t any, boolean_t sort_flag, uint32_t cause, os_reason_t jetsam_reason,
- int32_t *priority, uint32_t *errors)
-{
- pid_t aPid;
- proc_t p = PROC_NULL, next_p = PROC_NULL;
- boolean_t new_snapshot = FALSE, killed = FALSE;
- int kill_count = 0;
- unsigned int i = 0;
- uint32_t aPid_ep;
- uint64_t killtime = 0;
- clock_sec_t tv_sec;
- clock_usec_t tv_usec;
- uint32_t tv_msec;
-
-#ifndef CONFIG_FREEZE
-#pragma unused(any)
-#endif
-
- KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_JETSAM) | DBG_FUNC_START,
- memorystatus_available_pages, 0, 0, 0, 0);
-
-
- if (sort_flag == TRUE) {
- (void)memorystatus_sort_bucket(JETSAM_PRIORITY_FOREGROUND, JETSAM_SORT_DEFAULT);
- }
-
- proc_list_lock();
-
- next_p = memorystatus_get_first_proc_locked(&i, TRUE);
- while (next_p) {
-#if DEVELOPMENT || DEBUG
- int activeProcess;
- int procSuspendedForDiagnosis;
-#endif /* DEVELOPMENT || DEBUG */
-
- p = next_p;
- next_p = memorystatus_get_next_proc_locked(&i, p, TRUE);
-
-#if DEVELOPMENT || DEBUG
- activeProcess = p->p_memstat_state & P_MEMSTAT_FOREGROUND;
- procSuspendedForDiagnosis = p->p_memstat_state & P_MEMSTAT_DIAG_SUSPENDED;
-#endif /* DEVELOPMENT || DEBUG */
-
- aPid = p->p_pid;
- aPid_ep = p->p_memstat_effectivepriority;
-
- if (p->p_memstat_state & (P_MEMSTAT_ERROR | P_MEMSTAT_TERMINATED)) {
- continue; /* with lock held */
- }
-
-#if DEVELOPMENT || DEBUG
- if ((memorystatus_jetsam_policy & kPolicyDiagnoseActive) && procSuspendedForDiagnosis) {
- printf("jetsam: continuing after ignoring proc suspended already for diagnosis - %d\n", aPid);
- continue;
- }
-#endif /* DEVELOPMENT || DEBUG */
-
- if (cause == kMemorystatusKilledVnodes)
- {
- /*
- * If the system runs out of vnodes, we systematically jetsam
- * processes in hopes of stumbling onto a vnode gain that helps
- * the system recover. The process that happens to trigger
- * this path has no known relationship to the vnode consumption.
- * We attempt to safeguard that process e.g: do not jetsam it.
- */
-
- if (p == current_proc()) {
- /* do not jetsam the current process */
- continue;
- }
- }
-
-#if CONFIG_FREEZE
- boolean_t skip;
- boolean_t reclaim_proc = !(p->p_memstat_state & (P_MEMSTAT_LOCKED | P_MEMSTAT_NORECLAIM));
- if (any || reclaim_proc) {
- skip = FALSE;
- } else {
- skip = TRUE;
- }
-
- if (skip) {
- continue;
- } else
-#endif
- {
- /*
- * Capture a snapshot if none exists and:
- * - priority was not requested (this is something other than an ambient kill)
- * - the priority was requested *and* the targeted process is not at idle priority
- */
- if ((memorystatus_jetsam_snapshot_count == 0) &&
- (memorystatus_idle_snapshot || ((!priority) || (priority && (aPid_ep != JETSAM_PRIORITY_IDLE))))) {
- memorystatus_init_jetsam_snapshot_locked(NULL,0);
- new_snapshot = TRUE;
- }
-
- /*
- * Mark as terminated so that if exit1() indicates success, but the process (for example)
- * is blocked in task_exception_notify(), it'll be skipped if encountered again - see
- * <rdar://problem/13553476>. This is cheaper than examining P_LEXIT, which requires the
- * acquisition of the proc lock.
- */
- p->p_memstat_state |= P_MEMSTAT_TERMINATED;
-
- killtime = mach_absolute_time();
- absolutetime_to_microtime(killtime, &tv_sec, &tv_usec);
- tv_msec = tv_usec / 1000;
-
-#if DEVELOPMENT || DEBUG
- if ((memorystatus_jetsam_policy & kPolicyDiagnoseActive) && activeProcess) {
- MEMORYSTATUS_DEBUG(1, "jetsam: suspending pid %d [%s] (active) for diagnosis - memory_status_level: %d\n",
- aPid, (*p->p_name ? p->p_name: "(unknown)"), memorystatus_level);
- memorystatus_update_jetsam_snapshot_entry_locked(p, kMemorystatusKilledDiagnostic, killtime);
- p->p_memstat_state |= P_MEMSTAT_DIAG_SUSPENDED;
- if (memorystatus_jetsam_policy & kPolicyDiagnoseFirst) {
- jetsam_diagnostic_suspended_one_active_proc = 1;
- printf("jetsam: returning after suspending first active proc - %d\n", aPid);
- }
-
- p = proc_ref_locked(p);
- proc_list_unlock();
- if (p) {
- task_suspend(p->task);
- if (priority) {
- *priority = aPid_ep;
- }
- proc_rele(p);
- killed = TRUE;
- }
-
- goto exit;
- } else
-#endif /* DEVELOPMENT || DEBUG */
- {
- /* Shift queue, update stats */
- memorystatus_update_jetsam_snapshot_entry_locked(p, cause, killtime);
-
- if (proc_ref_locked(p) == p) {
- proc_list_unlock();
- printf("%lu.%02d memorystatus: %s %d [%s] (%s %d) - memorystatus_available_pages: %d\n",
- (unsigned long)tv_sec, tv_msec,
- ((aPid_ep == JETSAM_PRIORITY_IDLE) ? "idle exiting pid" : "jetsam killing top process pid"),
- aPid, (*p->p_name ? p->p_name : "(unknown)"),
- jetsam_kill_cause_name[cause], aPid_ep, memorystatus_available_pages);
-
- /*
- * memorystatus_do_kill() drops a reference, so take another one so we can
- * continue to use this exit reason even after memorystatus_do_kill()
- * returns.
- */
- os_reason_ref(jetsam_reason);
-
- killed = memorystatus_do_kill(p, cause, jetsam_reason);
-
- /* Success? */
- if (killed) {
- if (priority) {
- *priority = aPid_ep;
- }
- proc_rele(p);
- kill_count++;
- goto exit;
- }
-
- /*
- * Failure - first unwind the state,
- * then fall through to restart the search.
- */
- proc_list_lock();
- proc_rele_locked(p);
- p->p_memstat_state &= ~P_MEMSTAT_TERMINATED;
- p->p_memstat_state |= P_MEMSTAT_ERROR;
- *errors += 1;
- }
-
- /*
- * Failure - restart the search.
- *
- * We might have raced with "p" exiting on another core, resulting in no
- * ref on "p". Or, we may have failed to kill "p".
- *
- * Either way, we fall thru to here, leaving the proc in the
- * P_MEMSTAT_TERMINATED state.
- *
- * And, we hold the the proc_list_lock at this point.
- */
-
- i = 0;
- next_p = memorystatus_get_first_proc_locked(&i, TRUE);
- }
- }
- }
-
- proc_list_unlock();
-
-exit:
- os_reason_free(jetsam_reason);
-
- /* Clear snapshot if freshly captured and no target was found */
- if (new_snapshot && !killed) {
- proc_list_lock();
- memorystatus_jetsam_snapshot->entry_count = memorystatus_jetsam_snapshot_count = 0;
- proc_list_unlock();
- }
-
- KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_JETSAM) | DBG_FUNC_END,
- memorystatus_available_pages, killed ? aPid : 0, kill_count, 0, 0);
-
- return killed;
-}
-
-/*
- * Jetsam aggressively
- */
-static boolean_t
-memorystatus_kill_top_process_aggressive(boolean_t any, uint32_t cause, os_reason_t jetsam_reason, int aggr_count,
- int32_t priority_max, uint32_t *errors)
-{
- pid_t aPid;
- proc_t p = PROC_NULL, next_p = PROC_NULL;
- boolean_t new_snapshot = FALSE, killed = FALSE;
- int kill_count = 0;
- unsigned int i = 0;
- int32_t aPid_ep = 0;
- unsigned int memorystatus_level_snapshot = 0;
- uint64_t killtime = 0;
- clock_sec_t tv_sec;
- clock_usec_t tv_usec;
- uint32_t tv_msec;
-
-#pragma unused(any)
-
- KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_JETSAM) | DBG_FUNC_START,
- memorystatus_available_pages, priority_max, 0, 0, 0);
-
- memorystatus_sort_bucket(JETSAM_PRIORITY_FOREGROUND, JETSAM_SORT_DEFAULT);
-
- proc_list_lock();
-
- next_p = memorystatus_get_first_proc_locked(&i, TRUE);
- while (next_p) {
-#if DEVELOPMENT || DEBUG
- int activeProcess;
- int procSuspendedForDiagnosis;
-#endif /* DEVELOPMENT || DEBUG */
-
- if ((unsigned int)(next_p->p_memstat_effectivepriority) != i) {
-
- /*
- * We have raced with next_p running on another core, as it has
- * moved to a different jetsam priority band. This means we have
- * lost our place in line while traversing the jetsam list. We
- * attempt to recover by rewinding to the beginning of the band
- * we were already traversing. By doing this, we do not guarantee
- * that no process escapes this aggressive march, but we can make
- * skipping an entire range of processes less likely. (PR-21069019)
- */
-
- MEMORYSTATUS_DEBUG(1, "memorystatus: aggressive%d: rewinding %s moved from band %d --> %d\n",
- aggr_count, (*next_p->p_name ? next_p->p_name : "unknown"), i, next_p->p_memstat_effectivepriority);
-
- next_p = memorystatus_get_first_proc_locked(&i, TRUE);
- continue;
- }
-
- p = next_p;
- next_p = memorystatus_get_next_proc_locked(&i, p, TRUE);
-
- if (p->p_memstat_effectivepriority > priority_max) {
- /*
- * Bail out of this killing spree if we have
- * reached beyond the priority_max jetsam band.
- * That is, we kill up to and through the
- * priority_max jetsam band.
- */
- proc_list_unlock();
- goto exit;
- }
-
-#if DEVELOPMENT || DEBUG
- activeProcess = p->p_memstat_state & P_MEMSTAT_FOREGROUND;
- procSuspendedForDiagnosis = p->p_memstat_state & P_MEMSTAT_DIAG_SUSPENDED;
-#endif /* DEVELOPMENT || DEBUG */
-
- aPid = p->p_pid;
- aPid_ep = p->p_memstat_effectivepriority;
-
- if (p->p_memstat_state & (P_MEMSTAT_ERROR | P_MEMSTAT_TERMINATED)) {
- continue;
- }
-
-#if DEVELOPMENT || DEBUG
- if ((memorystatus_jetsam_policy & kPolicyDiagnoseActive) && procSuspendedForDiagnosis) {
- printf("jetsam: continuing after ignoring proc suspended already for diagnosis - %d\n", aPid);
- continue;
- }
-#endif /* DEVELOPMENT || DEBUG */
-
- /*
- * Capture a snapshot if none exists.
- */
- if (memorystatus_jetsam_snapshot_count == 0) {
- memorystatus_init_jetsam_snapshot_locked(NULL,0);
- new_snapshot = TRUE;
- }
-
- /*
- * Mark as terminated so that if exit1() indicates success, but the process (for example)
- * is blocked in task_exception_notify(), it'll be skipped if encountered again - see
- * <rdar://problem/13553476>. This is cheaper than examining P_LEXIT, which requires the
- * acquisition of the proc lock.
- */
- p->p_memstat_state |= P_MEMSTAT_TERMINATED;
-
- killtime = mach_absolute_time();
- absolutetime_to_microtime(killtime, &tv_sec, &tv_usec);
- tv_msec = tv_usec / 1000;
-
- /* Shift queue, update stats */
- memorystatus_update_jetsam_snapshot_entry_locked(p, cause, killtime);
-
- /*
- * In order to kill the target process, we will drop the proc_list_lock.
- * To guaranteee that p and next_p don't disappear out from under the lock,
- * we must take a ref on both.
- * If we cannot get a reference, then it's likely we've raced with
- * that process exiting on another core.
- */
- if (proc_ref_locked(p) == p) {
- if (next_p) {
- while (next_p && (proc_ref_locked(next_p) != next_p)) {
- proc_t temp_p;
-
- /*
- * We must have raced with next_p exiting on another core.
- * Recover by getting the next eligible process in the band.
- */
-
- MEMORYSTATUS_DEBUG(1, "memorystatus: aggressive%d: skipping %d [%s] (exiting?)\n",
- aggr_count, next_p->p_pid, (*next_p->p_name ? next_p->p_name : "(unknown)"));
-
- temp_p = next_p;
- next_p = memorystatus_get_next_proc_locked(&i, temp_p, TRUE);
- }
- }
- proc_list_unlock();
-
- printf("%lu.%01d memorystatus: aggressive%d: %s %d [%s] (%s %d) - memorystatus_available_pages: %d\n",
- (unsigned long)tv_sec, tv_msec, aggr_count,
- ((aPid_ep == JETSAM_PRIORITY_IDLE) ? "idle exiting pid" : "jetsam killing pid"),
- aPid, (*p->p_name ? p->p_name : "(unknown)"),
- jetsam_kill_cause_name[cause], aPid_ep, memorystatus_available_pages);
-
- memorystatus_level_snapshot = memorystatus_level;
-
- /*
- * memorystatus_do_kill() drops a reference, so take another one so we can
- * continue to use this exit reason even after memorystatus_do_kill()
- * returns.
- */
- os_reason_ref(jetsam_reason);
- killed = memorystatus_do_kill(p, cause, jetsam_reason);
-
- /* Success? */
- if (killed) {
- proc_rele(p);
- kill_count++;
- p = NULL;
- killed = FALSE;
-
- /*
- * Continue the killing spree.
- */
- proc_list_lock();
- if (next_p) {
- proc_rele_locked(next_p);
- }
-
- if (aPid_ep == JETSAM_PRIORITY_FOREGROUND && memorystatus_aggressive_jetsam_lenient == TRUE) {
- if (memorystatus_level > memorystatus_level_snapshot && ((memorystatus_level - memorystatus_level_snapshot) >= AGGRESSIVE_JETSAM_LENIENT_MODE_THRESHOLD)) {
-#if DEVELOPMENT || DEBUG
- printf("Disabling Lenient mode after one-time deployment.\n");
-#endif /* DEVELOPMENT || DEBUG */
- memorystatus_aggressive_jetsam_lenient = FALSE;
- break;
- }
- }
-
- continue;
- }
-
- /*
- * Failure - first unwind the state,
- * then fall through to restart the search.
- */
- proc_list_lock();
- proc_rele_locked(p);
- if (next_p) {
- proc_rele_locked(next_p);
- }
- p->p_memstat_state &= ~P_MEMSTAT_TERMINATED;
- p->p_memstat_state |= P_MEMSTAT_ERROR;
- *errors += 1;
- p = NULL;
- }
-
- /*
- * Failure - restart the search at the beginning of
- * the band we were already traversing.
- *
- * We might have raced with "p" exiting on another core, resulting in no
- * ref on "p". Or, we may have failed to kill "p".
- *
- * Either way, we fall thru to here, leaving the proc in the
- * P_MEMSTAT_TERMINATED or P_MEMSTAT_ERROR state.
- *
- * And, we hold the the proc_list_lock at this point.
- */
-
- next_p = memorystatus_get_first_proc_locked(&i, TRUE);
- }
-
- proc_list_unlock();
-
-exit:
- os_reason_free(jetsam_reason);
-
- /* Clear snapshot if freshly captured and no target was found */
- if (new_snapshot && (kill_count == 0)) {
- memorystatus_jetsam_snapshot->entry_count = memorystatus_jetsam_snapshot_count = 0;
- }
-
- KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_JETSAM) | DBG_FUNC_END,
- memorystatus_available_pages, killed ? aPid : 0, kill_count, 0, 0);
-
- if (kill_count > 0) {
- return(TRUE);
- }
- else {
- return(FALSE);
- }
-}
-
-static boolean_t
-memorystatus_kill_hiwat_proc(uint32_t *errors)
-{
- pid_t aPid = 0;
- proc_t p = PROC_NULL, next_p = PROC_NULL;
- boolean_t new_snapshot = FALSE, killed = FALSE;
- int kill_count = 0;
- unsigned int i = 0;
- uint32_t aPid_ep;
- uint64_t killtime = 0;
- clock_sec_t tv_sec;
- clock_usec_t tv_usec;
- uint32_t tv_msec;
- os_reason_t jetsam_reason = OS_REASON_NULL;
- KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_JETSAM_HIWAT) | DBG_FUNC_START,
- memorystatus_available_pages, 0, 0, 0, 0);
-
- jetsam_reason = os_reason_create(OS_REASON_JETSAM, JETSAM_REASON_MEMORY_HIGHWATER);
- if (jetsam_reason == OS_REASON_NULL) {
- printf("memorystatus_kill_hiwat_proc: failed to allocate exit reason\n");
- }
-
- proc_list_lock();
-
- next_p = memorystatus_get_first_proc_locked(&i, TRUE);
- while (next_p) {
- uint64_t footprint_in_bytes = 0;
- uint64_t memlimit_in_bytes = 0;
- boolean_t skip = 0;
-
- p = next_p;
- next_p = memorystatus_get_next_proc_locked(&i, p, TRUE);
-
- aPid = p->p_pid;
- aPid_ep = p->p_memstat_effectivepriority;
-
- if (p->p_memstat_state & (P_MEMSTAT_ERROR | P_MEMSTAT_TERMINATED)) {
- continue;
- }
-
- /* skip if no limit set */
- if (p->p_memstat_memlimit <= 0) {
- continue;
- }
-
-#if 0
- /*
- * No need to consider P_MEMSTAT_MEMLIMIT_BACKGROUND anymore.
- * Background limits are described via the inactive limit slots.
- * Their fatal/non-fatal setting will drive whether or not to be
- * considered in this kill path.
- */
-
- /* skip if a currently inapplicable limit is encountered */
- if ((p->p_memstat_state & P_MEMSTAT_MEMLIMIT_BACKGROUND) && (p->p_memstat_effectivepriority >= JETSAM_PRIORITY_FOREGROUND)) {
- continue;
- }
-#endif
- footprint_in_bytes = get_task_phys_footprint(p->task);
- memlimit_in_bytes = (((uint64_t)p->p_memstat_memlimit) * 1024ULL * 1024ULL); /* convert MB to bytes */
- skip = (footprint_in_bytes <= memlimit_in_bytes);
-
-#if DEVELOPMENT || DEBUG
- if (!skip && (memorystatus_jetsam_policy & kPolicyDiagnoseActive)) {
- if (p->p_memstat_state & P_MEMSTAT_DIAG_SUSPENDED) {
- continue;
- }
- }
-#endif /* DEVELOPMENT || DEBUG */
-
-#if CONFIG_FREEZE
- if (!skip) {
- if (p->p_memstat_state & P_MEMSTAT_LOCKED) {
- skip = TRUE;
- } else {
- skip = FALSE;
- }
- }
-#endif
-
- if (skip) {
- continue;
- } else {
-#if DEVELOPMENT || DEBUG
- MEMORYSTATUS_DEBUG(1, "jetsam: %s pid %d [%s] - %lld Mb > 1 (%d Mb)\n",
- (memorystatus_jetsam_policy & kPolicyDiagnoseActive) ? "suspending": "killing",
- aPid, (*p->p_name ? p->p_name : "unknown"),
- (footprint_in_bytes / (1024ULL * 1024ULL)), /* converted bytes to MB */
- p->p_memstat_memlimit);
-#endif /* DEVELOPMENT || DEBUG */
-
- if (memorystatus_jetsam_snapshot_count == 0) {
- memorystatus_init_jetsam_snapshot_locked(NULL,0);
- new_snapshot = TRUE;
- }
-
- p->p_memstat_state |= P_MEMSTAT_TERMINATED;
-
- killtime = mach_absolute_time();
- absolutetime_to_microtime(killtime, &tv_sec, &tv_usec);
- tv_msec = tv_usec / 1000;
-
-#if DEVELOPMENT || DEBUG
- if (memorystatus_jetsam_policy & kPolicyDiagnoseActive) {
- MEMORYSTATUS_DEBUG(1, "jetsam: pid %d suspended for diagnosis - memorystatus_available_pages: %d\n", aPid, memorystatus_available_pages);
- memorystatus_update_jetsam_snapshot_entry_locked(p, kMemorystatusKilledDiagnostic, killtime);
- p->p_memstat_state |= P_MEMSTAT_DIAG_SUSPENDED;
-
- p = proc_ref_locked(p);
- proc_list_unlock();
- if (p) {
- task_suspend(p->task);
- proc_rele(p);
- killed = TRUE;
- }
-
- goto exit;
- } else
-#endif /* DEVELOPMENT || DEBUG */
- {
- memorystatus_update_jetsam_snapshot_entry_locked(p, kMemorystatusKilledHiwat, killtime);
-
- if (proc_ref_locked(p) == p) {
- proc_list_unlock();
-
- printf("%lu.%02d memorystatus: jetsam killing pid %d [%s] (highwater %d) - memorystatus_available_pages: %d\n",
- (unsigned long)tv_sec, tv_msec, aPid, (*p->p_name ? p->p_name : "(unknown)"), aPid_ep, memorystatus_available_pages);
-
- /*
- * memorystatus_do_kill drops a reference, so take another one so we can
- * continue to use this exit reason even after memorystatus_do_kill()
- * returns
- */
- os_reason_ref(jetsam_reason);
-
- killed = memorystatus_do_kill(p, kMemorystatusKilledHiwat, jetsam_reason);
-
- /* Success? */
- if (killed) {
- proc_rele(p);
- kill_count++;
- goto exit;
- }
-
- /*
- * Failure - first unwind the state,
- * then fall through to restart the search.
- */
- proc_list_lock();
- proc_rele_locked(p);
- p->p_memstat_state &= ~P_MEMSTAT_TERMINATED;
- p->p_memstat_state |= P_MEMSTAT_ERROR;
- *errors += 1;
- }
-
- /*
- * Failure - restart the search.
- *
- * We might have raced with "p" exiting on another core, resulting in no
- * ref on "p". Or, we may have failed to kill "p".
- *
- * Either way, we fall thru to here, leaving the proc in the
- * P_MEMSTAT_TERMINATED state.
- *
- * And, we hold the the proc_list_lock at this point.
- */
-
- i = 0;
- next_p = memorystatus_get_first_proc_locked(&i, TRUE);
- }
- }
- }
-
- proc_list_unlock();
-
-exit:
- os_reason_free(jetsam_reason);
-
- /* Clear snapshot if freshly captured and no target was found */
- if (new_snapshot && !killed) {
- proc_list_lock();
- memorystatus_jetsam_snapshot->entry_count = memorystatus_jetsam_snapshot_count = 0;
- proc_list_unlock();
- }
-
- KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_JETSAM_HIWAT) | DBG_FUNC_END,
- memorystatus_available_pages, killed ? aPid : 0, kill_count, 0, 0);
-
- return killed;
-}
-
-/*
- * Jetsam a process pinned in the elevated band.
- *
- * Return: true -- at least one pinned process was jetsammed
- * false -- no pinned process was jetsammed
- */
-static boolean_t
-memorystatus_kill_elevated_process(uint32_t cause, os_reason_t jetsam_reason, int aggr_count, uint32_t *errors)
-{
- pid_t aPid = 0;
- proc_t p = PROC_NULL, next_p = PROC_NULL;
- boolean_t new_snapshot = FALSE, killed = FALSE;
- int kill_count = 0;
- unsigned int i = JETSAM_PRIORITY_ELEVATED_INACTIVE;
- uint32_t aPid_ep;
- uint64_t killtime = 0;
- clock_sec_t tv_sec;
- clock_usec_t tv_usec;
- uint32_t tv_msec;
-
-
- KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_JETSAM) | DBG_FUNC_START,
- memorystatus_available_pages, 0, 0, 0, 0);
-
- proc_list_lock();
-
- next_p = memorystatus_get_first_proc_locked(&i, FALSE);
- while (next_p) {
-
- p = next_p;
- next_p = memorystatus_get_next_proc_locked(&i, p, FALSE);
-
- aPid = p->p_pid;
- aPid_ep = p->p_memstat_effectivepriority;
-
- /*
- * Only pick a process pinned in this elevated band
- */
- if (!(p->p_memstat_state & P_MEMSTAT_USE_ELEVATED_INACTIVE_BAND)) {
- continue;
- }
-
- if (p->p_memstat_state & (P_MEMSTAT_ERROR | P_MEMSTAT_TERMINATED)) {
- continue;
- }
-
-#if CONFIG_FREEZE
- if (p->p_memstat_state & P_MEMSTAT_LOCKED) {
- continue;
- }
-#endif
-
-#if DEVELOPMENT || DEBUG
- MEMORYSTATUS_DEBUG(1, "jetsam: elevated%d process pid %d [%s] - memorystatus_available_pages: %d\n",
- aggr_count,
- aPid, (*p->p_name ? p->p_name : "unknown"),
- memorystatus_available_pages);
-#endif /* DEVELOPMENT || DEBUG */
-
- if (memorystatus_jetsam_snapshot_count == 0) {
- memorystatus_init_jetsam_snapshot_locked(NULL,0);
- new_snapshot = TRUE;
- }
-
- p->p_memstat_state |= P_MEMSTAT_TERMINATED;
-
- killtime = mach_absolute_time();
- absolutetime_to_microtime(killtime, &tv_sec, &tv_usec);
- tv_msec = tv_usec / 1000;
-
- memorystatus_update_jetsam_snapshot_entry_locked(p, cause, killtime);
-
- if (proc_ref_locked(p) == p) {
-
- proc_list_unlock();
-
- printf("%lu.%01d memorystatus: elevated%d: jetsam killing pid %d [%s] (%s %d) - memorystatus_available_pages: %d\n",
- (unsigned long)tv_sec, tv_msec,
- aggr_count,
- aPid, (*p->p_name ? p->p_name : "(unknown)"),
- jetsam_kill_cause_name[cause], aPid_ep, memorystatus_available_pages);
-
- /*
- * memorystatus_do_kill drops a reference, so take another one so we can
- * continue to use this exit reason even after memorystatus_do_kill()
- * returns
- */
- os_reason_ref(jetsam_reason);
- killed = memorystatus_do_kill(p, cause, jetsam_reason);
-
- /* Success? */
- if (killed) {
- proc_rele(p);
- kill_count++;
- goto exit;
- }
-
- /*
- * Failure - first unwind the state,
- * then fall through to restart the search.
- */
- proc_list_lock();
- proc_rele_locked(p);
- p->p_memstat_state &= ~P_MEMSTAT_TERMINATED;
- p->p_memstat_state |= P_MEMSTAT_ERROR;
- *errors += 1;
- }
-
- /*
- * Failure - restart the search.
- *
- * We might have raced with "p" exiting on another core, resulting in no
- * ref on "p". Or, we may have failed to kill "p".
- *
- * Either way, we fall thru to here, leaving the proc in the
- * P_MEMSTAT_TERMINATED state or P_MEMSTAT_ERROR state.
- *
- * And, we hold the the proc_list_lock at this point.
- */
-
- next_p = memorystatus_get_first_proc_locked(&i, FALSE);
- }
-
- proc_list_unlock();
-
-exit:
- os_reason_free(jetsam_reason);
-
- /* Clear snapshot if freshly captured and no target was found */
- if (new_snapshot && (kill_count == 0)) {
- proc_list_lock();
- memorystatus_jetsam_snapshot->entry_count = memorystatus_jetsam_snapshot_count = 0;
- proc_list_unlock();
- }
-
- KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_JETSAM) | DBG_FUNC_END,
- memorystatus_available_pages, killed ? aPid : 0, kill_count, 0, 0);
-
- return (killed);
-}
-
-static boolean_t
-memorystatus_kill_process_async(pid_t victim_pid, uint32_t cause) {
- /*
- * TODO: allow a general async path
- *
- * NOTE: If a new async kill cause is added, make sure to update memorystatus_thread() to
- * add the appropriate exit reason code mapping.
- */
- if ((victim_pid != -1) || (cause != kMemorystatusKilledVMPageShortage && cause != kMemorystatusKilledVMThrashing &&
- cause != kMemorystatusKilledFCThrashing)) {
- return FALSE;
- }
-
- kill_under_pressure_cause = cause;
- memorystatus_thread_wake();
- return TRUE;
-}
-
-boolean_t
-memorystatus_kill_on_VM_page_shortage(boolean_t async) {
- if (async) {
- return memorystatus_kill_process_async(-1, kMemorystatusKilledVMPageShortage);
- } else {
- os_reason_t jetsam_reason = os_reason_create(OS_REASON_JETSAM, JETSAM_REASON_MEMORY_VMPAGESHORTAGE);
- if (jetsam_reason == OS_REASON_NULL) {
- printf("memorystatus_kill_on_VM_page_shortage -- sync: failed to allocate jetsam reason\n");
- }
-
- return memorystatus_kill_process_sync(-1, kMemorystatusKilledVMPageShortage, jetsam_reason);
- }
-}
-
-boolean_t
-memorystatus_kill_on_VM_thrashing(boolean_t async) {
- if (async) {
- return memorystatus_kill_process_async(-1, kMemorystatusKilledVMThrashing);
- } else {
- os_reason_t jetsam_reason = os_reason_create(OS_REASON_JETSAM, JETSAM_REASON_MEMORY_VMTHRASHING);
- if (jetsam_reason == OS_REASON_NULL) {
- printf("memorystatus_kill_on_VM_thrashing -- sync: failed to allocate jetsam reason\n");
- }
-
- return memorystatus_kill_process_sync(-1, kMemorystatusKilledVMThrashing, jetsam_reason);
- }
-}
-
-boolean_t
-memorystatus_kill_on_FC_thrashing(boolean_t async) {
-
-
- if (async) {
- return memorystatus_kill_process_async(-1, kMemorystatusKilledFCThrashing);
- } else {
- os_reason_t jetsam_reason = os_reason_create(OS_REASON_JETSAM, JETSAM_REASON_MEMORY_FCTHRASHING);
- if (jetsam_reason == OS_REASON_NULL) {
- printf("memorystatus_kill_on_FC_thrashing -- sync: failed to allocate jetsam reason\n");
- }
-
- return memorystatus_kill_process_sync(-1, kMemorystatusKilledFCThrashing, jetsam_reason);
- }
-}
-
-boolean_t
-memorystatus_kill_on_vnode_limit(void) {
- os_reason_t jetsam_reason = os_reason_create(OS_REASON_JETSAM, JETSAM_REASON_VNODE);
- if (jetsam_reason == OS_REASON_NULL) {
- printf("memorystatus_kill_on_vnode_limit: failed to allocate jetsam reason\n");
- }
-
- return memorystatus_kill_process_sync(-1, kMemorystatusKilledVnodes, jetsam_reason);
-}
-
-#endif /* CONFIG_JETSAM */
-
-#if CONFIG_FREEZE
-
-__private_extern__ void
-memorystatus_freeze_init(void)
-{
- kern_return_t result;
- thread_t thread;
-
- freezer_lck_grp_attr = lck_grp_attr_alloc_init();
- freezer_lck_grp = lck_grp_alloc_init("freezer", freezer_lck_grp_attr);
-
- lck_mtx_init(&freezer_mutex, freezer_lck_grp, NULL);
-
- result = kernel_thread_start(memorystatus_freeze_thread, NULL, &thread);
- if (result == KERN_SUCCESS) {
- thread_deallocate(thread);
- } else {
- panic("Could not create memorystatus_freeze_thread");
- }
-}
-
-/*
- * Synchronously freeze the passed proc. Called with a reference to the proc held.
- *
- * Returns EINVAL or the value returned by task_freeze().
- */
-int
-memorystatus_freeze_process_sync(proc_t p)