unsigned int memorystatus_thaw_count = 0;
unsigned int memorystatus_refreeze_eligible_count = 0; /* # of processes currently thawed i.e. have state on disk & in-memory */
+/* Freezer counters collected for telemtry */
+static struct memorystatus_freezer_stats_t {
+ /*
+ * # of processes that we've considered freezing.
+ * Used to normalize the error reasons below.
+ */
+ uint64_t mfs_process_considered_count;
+
+ /*
+ * The following counters track how many times we've failed to freeze
+ * a process because of a specific FREEZER_ERROR.
+ */
+ /* EXCESS_SHARED_MEMORY */
+ uint64_t mfs_error_excess_shared_memory_count;
+ /* LOW_PRIVATE_SHARED_RATIO */
+ uint64_t mfs_error_low_private_shared_ratio_count;
+ /* NO_COMPRESSOR_SPACE */
+ uint64_t mfs_error_no_compressor_space_count;
+ /* NO_SWAP_SPACE */
+ uint64_t mfs_error_no_swap_space_count;
+ /* pages < memorystatus_freeze_pages_min */
+ uint64_t mfs_error_below_min_pages_count;
+ /* dasd determined it was unlikely to be relaunched. */
+ uint64_t mfs_error_low_probability_of_use_count;
+ /* transient reasons (like inability to acquire a lock). */
+ uint64_t mfs_error_other_count;
+
+ /*
+ * # of times that we saw memorystatus_available_pages <= memorystatus_freeze_threshold.
+ * Used to normalize skipped_full_count and shared_mb_high_count.
+ */
+ uint64_t mfs_below_threshold_count;
+
+ /* Skipped running the freezer because we were out of slots */
+ uint64_t mfs_skipped_full_count;
+
+ /* Skipped running the freezer because we were over the shared mb limit*/
+ uint64_t mfs_skipped_shared_mb_high_count;
+
+ /*
+ * How many pages have not been sent to swap because they were in a shared object?
+ * This is being used to gather telemtry so we can understand the impact we'd have
+ * on our NAND budget if we did swap out these pages.
+ */
+ uint64_t mfs_shared_pages_skipped;
+} memorystatus_freezer_stats = {0};
+
#endif /* XNU_KERNEL_PRIVATE */
static inline boolean_t memorystatus_can_freeze_processes(void);
#define DEGRADED_WINDOW_MINS (30)
#define NORMAL_WINDOW_MINS (24 * 60)
+/* Protected by the freezer_mutex */
static throttle_interval_t throttle_intervals[] = {
{ DEGRADED_WINDOW_MINS, 1, 0, 0, { 0, 0 }},
{ NORMAL_WINDOW_MINS, 1, 0, 0, { 0, 0 }},
SYSCTL_UINT(_kern, OID_AUTO, memorystatus_thaw_count, CTLFLAG_RD | CTLFLAG_LOCKED, &memorystatus_thaw_count, 0, "");
SYSCTL_QUAD(_kern, OID_AUTO, memorystatus_freeze_pageouts, CTLFLAG_RD | CTLFLAG_LOCKED, &memorystatus_freeze_pageouts, "");
SYSCTL_QUAD(_kern, OID_AUTO, memorystatus_freeze_budget_pages_remaining, CTLFLAG_RD | CTLFLAG_LOCKED, &memorystatus_freeze_budget_pages_remaining, "");
+SYSCTL_QUAD(_kern, OID_AUTO, memorystatus_freezer_error_excess_shared_memory_count, CTLFLAG_RD | CTLFLAG_LOCKED, &memorystatus_freezer_stats.mfs_error_excess_shared_memory_count, "");
+SYSCTL_QUAD(_kern, OID_AUTO, memorystatus_freezer_error_low_private_shared_ratio_count, CTLFLAG_RD | CTLFLAG_LOCKED, &memorystatus_freezer_stats.mfs_error_low_private_shared_ratio_count, "");
+SYSCTL_QUAD(_kern, OID_AUTO, memorystatus_freezer_error_no_compressor_space_count, CTLFLAG_RD | CTLFLAG_LOCKED, &memorystatus_freezer_stats.mfs_error_no_compressor_space_count, "");
+SYSCTL_QUAD(_kern, OID_AUTO, memorystatus_freezer_error_no_swap_space_count, CTLFLAG_RD | CTLFLAG_LOCKED, &memorystatus_freezer_stats.mfs_error_no_swap_space_count, "");
+SYSCTL_QUAD(_kern, OID_AUTO, memorystatus_freezer_error_below_min_pages_count, CTLFLAG_RD | CTLFLAG_LOCKED, &memorystatus_freezer_stats.mfs_error_below_min_pages_count, "");
+SYSCTL_QUAD(_kern, OID_AUTO, memorystatus_freezer_error_low_probability_of_use_count, CTLFLAG_RD | CTLFLAG_LOCKED, &memorystatus_freezer_stats.mfs_error_low_probability_of_use_count, "");
+SYSCTL_QUAD(_kern, OID_AUTO, memorystatus_freezer_error_other_count, CTLFLAG_RD | CTLFLAG_LOCKED, &memorystatus_freezer_stats.mfs_error_other_count, "");
+SYSCTL_QUAD(_kern, OID_AUTO, memorystatus_freezer_process_considered_count, CTLFLAG_RD | CTLFLAG_LOCKED, &memorystatus_freezer_stats.mfs_process_considered_count, "");
+SYSCTL_QUAD(_kern, OID_AUTO, memorystatus_freezer_below_threshold_count, CTLFLAG_RD | CTLFLAG_LOCKED, &memorystatus_freezer_stats.mfs_below_threshold_count, "");
+SYSCTL_QUAD(_kern, OID_AUTO, memorystatus_freezer_skipped_full_count, CTLFLAG_RD | CTLFLAG_LOCKED, &memorystatus_freezer_stats.mfs_skipped_full_count, "");
+SYSCTL_QUAD(_kern, OID_AUTO, memorystatus_freezer_skipped_shared_mb_high_count, CTLFLAG_RD | CTLFLAG_LOCKED, &memorystatus_freezer_stats.mfs_skipped_shared_mb_high_count, "");
+SYSCTL_QUAD(_kern, OID_AUTO, memorystatus_freezer_shared_pages_skipped, CTLFLAG_RD | CTLFLAG_LOCKED, &memorystatus_freezer_stats.mfs_shared_pages_skipped, "");
+
+/*
+ * Calculates the hit rate for the freezer.
+ * The hit rate is defined as the percentage of procs that are currently in the
+ * freezer which we have thawed.
+ * A low hit rate means we're freezing bad candidates since they're not re-used.
+ */
+static int sysctl_memorystatus_freezer_thaw_percentage SYSCTL_HANDLER_ARGS
+{
+#pragma unused(arg1, arg2)
+ size_t thaw_count = 0, frozen_count = 0;
+ int thaw_percentage = 100;
+ unsigned int band = (unsigned int) memorystatus_freeze_jetsam_band;
+ proc_t p = PROC_NULL;
+ proc_list_lock();
+
+ p = memorystatus_get_first_proc_locked(&band, FALSE);
+
+ while (p) {
+ if (p->p_memstat_state & P_MEMSTAT_FROZEN) {
+ if (p->p_memstat_thaw_count > 0) {
+ thaw_count++;
+ }
+ frozen_count++;
+ }
+ p = memorystatus_get_next_proc_locked(&band, p, FALSE);
+ }
+ proc_list_unlock();
+ if (frozen_count > 0) {
+ thaw_percentage = 100 * thaw_count / frozen_count;
+ }
+ return sysctl_handle_int(oidp, &thaw_percentage, 0, req);
+}
+SYSCTL_PROC(_kern, OID_AUTO, memorystatus_freezer_thaw_percentage, CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_LOCKED, 0, 0, &sysctl_memorystatus_freezer_thaw_percentage, "I", "");
#if DEVELOPMENT || DEBUG
again:
p = proc_find(pid);
if (p != NULL) {
+ memorystatus_freezer_stats.mfs_process_considered_count++;
uint32_t purgeable, wired, clean, dirty, shared;
uint32_t max_pages = 0, state = 0;
}
error = task_freeze(p->task, &purgeable, &wired, &clean, &dirty, max_pages, &shared, &freezer_error_code, FALSE /* eval only */);
+ if (!error || freezer_error_code == FREEZER_ERROR_LOW_PRIVATE_SHARED_RATIO) {
+ memorystatus_freezer_stats.mfs_shared_pages_skipped += shared;
+ }
if (error) {
char reason[128];
if (freezer_error_code == FREEZER_ERROR_EXCESS_SHARED_MEMORY) {
+ memorystatus_freezer_stats.mfs_error_excess_shared_memory_count++;
strlcpy(reason, "too much shared memory", 128);
}
if (freezer_error_code == FREEZER_ERROR_LOW_PRIVATE_SHARED_RATIO) {
+ memorystatus_freezer_stats.mfs_error_low_private_shared_ratio_count++;
strlcpy(reason, "low private-shared pages ratio", 128);
}
if (freezer_error_code == FREEZER_ERROR_NO_COMPRESSOR_SPACE) {
+ memorystatus_freezer_stats.mfs_error_no_compressor_space_count++;
strlcpy(reason, "no compressor space", 128);
}
sysctl_memorystatus_demote_frozen_process SYSCTL_HANDLER_ARGS
{
#pragma unused(arg1, arg2, oidp, req)
+ int error, val;
+ /*
+ * Only demote on write to prevent demoting during `sysctl -a`.
+ * The actual value written doesn't matter.
+ */
+ error = sysctl_handle_int(oidp, &val, 0, req);
+ if (error || !req->newptr) {
+ return error;
+ }
memorystatus_demote_frozen_processes(false);
return 0;
}
-SYSCTL_PROC(_kern, OID_AUTO, memorystatus_demote_frozen_processes, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_LOCKED | CTLFLAG_MASKED, 0, 0, &sysctl_memorystatus_demote_frozen_process, "I", "");
+SYSCTL_PROC(_kern, OID_AUTO, memorystatus_demote_frozen_processes, CTLTYPE_INT | CTLFLAG_WR | CTLFLAG_LOCKED | CTLFLAG_MASKED, 0, 0, &sysctl_memorystatus_demote_frozen_process, "I", "");
static int
sysctl_memorystatus_available_pages_thaw SYSCTL_HANDLER_ARGS
}
}
+ /*
+ * This proc is a suspended application.
+ * We're interested in tracking what percentage of these
+ * actually get frozen.
+ */
+ memorystatus_freezer_stats.mfs_process_considered_count++;
/* Only freeze applications meeting our minimum resident page criteria */
memorystatus_get_task_page_counts(p->task, &pages, NULL, NULL);
if (pages < memorystatus_freeze_pages_min) {
+ memorystatus_freezer_stats.mfs_error_below_min_pages_count++;
goto out;
}
* memorystatus_freeze_top_process holds the proc_list_lock while it traverses the bands.
*/
if ((p->p_listflag & P_LIST_EXITED) != 0) {
+ memorystatus_freezer_stats.mfs_error_other_count++;
goto out;
}
}
if (probability_of_use == 0) {
+ memorystatus_freezer_stats.mfs_error_low_probability_of_use_count++;
goto out;
}
}
memorystatus_available_pages, 0, 0, 0, 0);
ret = task_freeze(p->task, &purgeable, &wired, &clean, &dirty, max_pages, &shared, &freezer_error_code, FALSE /* eval only */);
+ if (ret == KERN_SUCCESS || freezer_error_code == FREEZER_ERROR_LOW_PRIVATE_SHARED_RATIO) {
+ memorystatus_freezer_stats.mfs_shared_pages_skipped += shared;
+ }
KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_FREEZE) | DBG_FUNC_END,
memorystatus_available_pages, aPid, 0, 0, 0);
ret = 0;
}
- proc_list_lock();
/* Update stats */
for (i = 0; i < sizeof(throttle_intervals) / sizeof(struct throttle_interval_t); i++) {
throttle_intervals[i].pageouts += dirty;
}
- } else {
- proc_list_lock();
}
+ memorystatus_freeze_update_throttle(&memorystatus_freeze_budget_pages_remaining);
+ os_log_with_startup_serial(OS_LOG_DEFAULT, "memorystatus: freezing (specific) pid %d [%s] done memorystatus_freeze_budget_pages_remaining %llu froze %u pages",
+ aPid, ((p && *p->p_name) ? p->p_name : "unknown"), memorystatus_freeze_budget_pages_remaining, dirty);
+
+ proc_list_lock();
memorystatus_freeze_pageouts += dirty;
* can freeze a more eligible process at this moment in time?
*/
}
-
- memorystatus_freeze_update_throttle(&memorystatus_freeze_budget_pages_remaining);
- os_log_with_startup_serial(OS_LOG_DEFAULT, "memorystatus: freezing (specific) pid %d [%s] done memorystatus_freeze_budget_pages_remaining %llu froze %u pages",
- aPid, ((p && *p->p_name) ? p->p_name : "unknown"), memorystatus_freeze_budget_pages_remaining, dirty);
} else {
char reason[128];
if (freezer_error_code == FREEZER_ERROR_EXCESS_SHARED_MEMORY) {
+ memorystatus_freezer_stats.mfs_error_excess_shared_memory_count++;
strlcpy(reason, "too much shared memory", 128);
}
if (freezer_error_code == FREEZER_ERROR_LOW_PRIVATE_SHARED_RATIO) {
+ memorystatus_freezer_stats.mfs_error_low_private_shared_ratio_count++;
strlcpy(reason, "low private-shared pages ratio", 128);
}
if (freezer_error_code == FREEZER_ERROR_NO_COMPRESSOR_SPACE) {
+ memorystatus_freezer_stats.mfs_error_no_compressor_space_count++;
strlcpy(reason, "no compressor space", 128);
}
if (freezer_error_code == FREEZER_ERROR_NO_SWAP_SPACE) {
+ memorystatus_freezer_stats.mfs_error_no_swap_space_count++;
strlcpy(reason, "no swap space", 128);
}
return ret;
}
+/*
+ * Caller must hold the freezer_mutex and it will be locked on return.
+ */
static int
memorystatus_freeze_top_process(void)
{
coalition_t coal = COALITION_NULL;
pid_t pid_list[MAX_XPC_SERVICE_PIDS];
unsigned int ntasks = 0;
+ LCK_MTX_ASSERT(&freezer_mutex, LCK_MTX_ASSERT_OWNED);
KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_FREEZE_SCAN) | DBG_FUNC_START, memorystatus_available_pages, 0, 0, 0, 0);
p = proc_ref_locked(p);
if (!p) {
+ memorystatus_freezer_stats.mfs_error_other_count++;
break;
}
memorystatus_available_pages, 0, 0, 0, 0);
kr = task_freeze(p->task, &purgeable, &wired, &clean, &dirty, max_pages, &shared, &freezer_error_code, FALSE /* eval only */);
+ if (kr == KERN_SUCCESS || freezer_error_code == FREEZER_ERROR_LOW_PRIVATE_SHARED_RATIO) {
+ memorystatus_freezer_stats.mfs_shared_pages_skipped += shared;
+ }
KERNEL_DEBUG_CONSTANT(BSDDBG_CODE(DBG_BSD_MEMSTAT, BSD_MEMSTAT_FREEZE) | DBG_FUNC_END,
memorystatus_available_pages, aPid, 0, 0, 0);
ret = 0;
}
- proc_list_lock();
-
/* Update stats */
for (i = 0; i < sizeof(throttle_intervals) / sizeof(struct throttle_interval_t); i++) {
throttle_intervals[i].pageouts += dirty;
}
- } else {
- proc_list_lock();
}
+ memorystatus_freeze_update_throttle(&memorystatus_freeze_budget_pages_remaining);
+ os_log_with_startup_serial(OS_LOG_DEFAULT, "memorystatus: %sfreezing (%s) pid %d [%s] done, memorystatus_freeze_budget_pages_remaining %llu %sfroze %u pages\n",
+ refreeze_processes? "re" : "", (coal == NULL ? "general" : "coalition-driven"), aPid, ((p && *p->p_name) ? p->p_name : "unknown"), memorystatus_freeze_budget_pages_remaining, refreeze_processes? "Re" : "", dirty);
+
+ proc_list_lock();
memorystatus_freeze_pageouts += dirty;
*/
}
- memorystatus_freeze_update_throttle(&memorystatus_freeze_budget_pages_remaining);
- os_log_with_startup_serial(OS_LOG_DEFAULT, "memorystatus: %sfreezing (%s) pid %d [%s] done, memorystatus_freeze_budget_pages_remaining %llu %sfroze %u pages\n",
- refreeze_processes? "re" : "", (coal == NULL ? "general" : "coalition-driven"), aPid, ((p && *p->p_name) ? p->p_name : "unknown"), memorystatus_freeze_budget_pages_remaining, refreeze_processes? "Re" : "", dirty);
-
/* Return KERN_SUCCESS */
ret = kr;
char reason[128];
if (freezer_error_code == FREEZER_ERROR_EXCESS_SHARED_MEMORY) {
+ memorystatus_freezer_stats.mfs_error_excess_shared_memory_count++;
strlcpy(reason, "too much shared memory", 128);
}
if (freezer_error_code == FREEZER_ERROR_LOW_PRIVATE_SHARED_RATIO) {
+ memorystatus_freezer_stats.mfs_error_low_private_shared_ratio_count++;
strlcpy(reason, "low private-shared pages ratio", 128);
}
if (freezer_error_code == FREEZER_ERROR_NO_COMPRESSOR_SPACE) {
+ memorystatus_freezer_stats.mfs_error_no_compressor_space_count++;
strlcpy(reason, "no compressor space", 128);
}
if (freezer_error_code == FREEZER_ERROR_NO_SWAP_SPACE) {
+ memorystatus_freezer_stats.mfs_error_no_swap_space_count++;
strlcpy(reason, "no swap space", 128);
}
if (force_one == FALSE) {
/*
- * We use this counter to track daily thaws.
- * So we only reset it to 0 under the normal
+ * We use these counters to track daily hit rates.
+ * So we only reset them to 0 under the normal
* mode.
*/
memorystatus_thaw_count = 0;
proc_list_unlock();
}
+/*
+ * Calculate a new freezer budget.
+ * @param time_since_last_interval_expired_sec How long has it been (in seconds) since the previous interval expired.
+ * @param burst_multiple The burst_multiple for the new period
+ * @param interval_duration_min How many minutes will the new interval be?
+ * @param rollover The amount to rollover from the previous budget.
+ *
+ * @return A budget for the new interval.
+ */
+static uint32_t
+memorystatus_freeze_calculate_new_budget(
+ unsigned int time_since_last_interval_expired_sec,
+ unsigned int burst_multiple,
+ unsigned int interval_duration_min,
+ uint32_t rollover)
+{
+ uint64_t freeze_daily_budget = 0;
+ unsigned int daily_budget_pageouts = 0;
+ unsigned int freeze_daily_pageouts_max = 0;
+ const static unsigned int kNumSecondsInDay = 60 * 60 * 24;
+ /* Precision factor for days_missed. 2 decimal points. */
+ const static unsigned int kFixedPointFactor = 100;
+ unsigned int days_missed, budget_missed;
+
+ /* Get the daily budget from the storage layer */
+ if (vm_swap_max_budget(&freeze_daily_budget)) {
+ memorystatus_freeze_daily_mb_max = (freeze_daily_budget / (1024 * 1024));
+ os_log_with_startup_serial(OS_LOG_DEFAULT, "memorystatus: memorystatus_freeze_daily_mb_max set to %dMB\n", memorystatus_freeze_daily_mb_max);
+ }
+ /* Calculate the daily pageout budget */
+ freeze_daily_pageouts_max = memorystatus_freeze_daily_mb_max * (1024 * 1024 / PAGE_SIZE);
+
+ daily_budget_pageouts = (burst_multiple * (((uint64_t) interval_duration_min * freeze_daily_pageouts_max) / (kNumSecondsInDay / 60)));
+
+ /*
+ * Add additional budget for time since the interval expired.
+ * For example, if the interval expired n days ago, we should get an additional n days
+ * of budget since we didn't use any budget during those n days.
+ */
+ days_missed = time_since_last_interval_expired_sec * kFixedPointFactor / kNumSecondsInDay;
+ budget_missed = days_missed * freeze_daily_pageouts_max / kFixedPointFactor;
+ return rollover + daily_budget_pageouts + budget_missed;
+}
+
+#if DEVELOPMENT || DEBUG
+
+static int
+sysctl_memorystatus_freeze_calculate_new_budget SYSCTL_HANDLER_ARGS
+{
+#pragma unused(arg1, arg2)
+ int error = 0;
+ unsigned int time_since_last_interval_expired_sec = 0;
+ unsigned int new_budget;
+
+ error = sysctl_handle_int(oidp, &time_since_last_interval_expired_sec, 0, req);
+ if (error || !req->newptr) {
+ return error;
+ }
+ new_budget = memorystatus_freeze_calculate_new_budget(time_since_last_interval_expired_sec, 1, NORMAL_WINDOW_MINS, 0);
+ return copyout(&new_budget, req->oldptr, MIN(sizeof(req->oldlen), sizeof(new_budget)));
+}
+
+SYSCTL_PROC(_vm, OID_AUTO, memorystatus_freeze_calculate_new_budget, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MASKED,
+ 0, 0, &sysctl_memorystatus_freeze_calculate_new_budget, "I", "");
+
+#endif /* DEVELOPMENT || DEBUG */
/*
* This function will do 4 things:
* 4) calculate the current rate of pageouts for DEGRADED_WINDOW_MINS duration. If that rate is below
* what we would normally expect, then we are running low on our daily budget and need to enter
* degraded perf. mode.
+ *
+ * Caller must hold the freezer mutex
+ * Caller must not hold the proc_list lock
*/
static void
{
clock_sec_t sec;
clock_nsec_t nsec;
- mach_timespec_t ts;
+ mach_timespec_t now_ts;
+ LCK_MTX_ASSERT(&freezer_mutex, LCK_MTX_ASSERT_OWNED);
+ LCK_MTX_ASSERT(proc_list_mlock, LCK_MTX_ASSERT_NOTOWNED);
unsigned int freeze_daily_pageouts_max = 0;
#endif
clock_get_system_nanotime(&sec, &nsec);
- ts.tv_sec = sec;
- ts.tv_nsec = nsec;
+ now_ts.tv_sec = sec;
+ now_ts.tv_nsec = nsec;
struct throttle_interval_t *interval = NULL;
if (memorystatus_freeze_degradation == TRUE) {
interval = degraded_throttle_window;
- if (CMP_MACH_TIMESPEC(&ts, &interval->ts) >= 0) {
+ if (CMP_MACH_TIMESPEC(&now_ts, &interval->ts) >= 0) {
memorystatus_freeze_degradation = FALSE;
interval->pageouts = 0;
interval->max_pageouts = 0;
interval = normal_throttle_window;
- if (CMP_MACH_TIMESPEC(&ts, &interval->ts) >= 0) {
- /*
- * New throttle window.
- * Rollover any unused budget.
- * Also ask the storage layer what the new budget needs to be.
- */
- uint64_t freeze_daily_budget = 0;
- unsigned int daily_budget_pageouts = 0;
-
- if (vm_swap_max_budget(&freeze_daily_budget)) {
- memorystatus_freeze_daily_mb_max = (freeze_daily_budget / (1024 * 1024));
- os_log_with_startup_serial(OS_LOG_DEFAULT, "memorystatus: memorystatus_freeze_daily_mb_max set to %dMB\n", memorystatus_freeze_daily_mb_max);
- }
-
- freeze_daily_pageouts_max = memorystatus_freeze_daily_mb_max * (1024 * 1024 / PAGE_SIZE);
-
- daily_budget_pageouts = (interval->burst_multiple * (((uint64_t)interval->mins * freeze_daily_pageouts_max) / NORMAL_WINDOW_MINS));
- interval->max_pageouts = (interval->max_pageouts - interval->pageouts) + daily_budget_pageouts;
+ if (CMP_MACH_TIMESPEC(&now_ts, &interval->ts) >= 0) {
+ /* How long has it been since the previous interval expired? */
+ mach_timespec_t expiration_period_ts = now_ts;
+ SUB_MACH_TIMESPEC(&expiration_period_ts, &interval->ts);
+ interval->max_pageouts = memorystatus_freeze_calculate_new_budget(
+ expiration_period_ts.tv_sec, interval->burst_multiple,
+ interval->mins, interval->max_pageouts - interval->pageouts);
interval->ts.tv_sec = interval->mins * 60;
interval->ts.tv_nsec = 0;
- ADD_MACH_TIMESPEC(&interval->ts, &ts);
+ ADD_MACH_TIMESPEC(&interval->ts, &now_ts);
/* Since we update the throttle stats pre-freeze, adjust for overshoot here */
if (interval->pageouts > interval->max_pageouts) {
interval->pageouts -= interval->max_pageouts;
interval->pageouts = 0;
}
*budget_pages_allowed = interval->max_pageouts;
+ memorystatus_freezer_stats.mfs_shared_pages_skipped = 0;
memorystatus_demote_frozen_processes(FALSE); /* normal mode...don't force a demotion */
} else {
time_left.tv_sec = interval->ts.tv_sec;
time_left.tv_nsec = 0;
- SUB_MACH_TIMESPEC(&time_left, &ts);
+ SUB_MACH_TIMESPEC(&time_left, &now_ts);
if (budget_left <= budget_threshold) {
/*
}
MEMORYSTATUS_DEBUG(1, "memorystatus_freeze_update_throttle_interval: throttle updated - %d frozen (%d max) within %dm; %dm remaining; throttle %s\n",
- interval->pageouts, interval->max_pageouts, interval->mins, (interval->ts.tv_sec - ts->tv_sec) / 60,
+ interval->pageouts, interval->max_pageouts, interval->mins, (interval->ts.tv_sec - now_ts->tv_sec) / 60,
interval->throttle ? "on" : "off");
}
goto out;
}
- if ((memorystatus_frozen_count >= memorystatus_frozen_processes_max) &&
- (memorystatus_refreeze_eligible_count < MIN_THAW_REFREEZE_THRESHOLD)) {
- goto out;
+ memorystatus_freezer_stats.mfs_below_threshold_count++;
+
+ if ((memorystatus_frozen_count >= memorystatus_frozen_processes_max)) {
+ /*
+ * Consider this as a skip even if we wake up to refreeze because
+ * we won't freeze any new procs.
+ */
+ memorystatus_freezer_stats.mfs_skipped_full_count++;
+ if (memorystatus_refreeze_eligible_count < MIN_THAW_REFREEZE_THRESHOLD) {
+ goto out;
+ }
}
if (memorystatus_frozen_shared_mb_max && (memorystatus_frozen_shared_mb >= memorystatus_frozen_shared_mb_max)) {
+ memorystatus_freezer_stats.mfs_skipped_shared_mb_high_count++;
goto out;
}