| 1 | /* |
| 2 | * Copyright (c) 2006-2018 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 | #ifndef SYS_MEMORYSTATUS_FREEZE_H |
| 30 | #define SYS_MEMORYSTATUS_FREEZE_H |
| 31 | |
| 32 | #include <stdint.h> |
| 33 | #include <sys/time.h> |
| 34 | #include <sys/proc.h> |
| 35 | #include <sys/param.h> |
| 36 | #include <sys/kern_memorystatus.h> |
| 37 | #include <mach/resource_monitors.h> // command/proc_name_t |
| 38 | #include <uuid/uuid.h> |
| 39 | |
| 40 | typedef struct memorystatus_freeze_entry { |
| 41 | int32_t pid; |
| 42 | uint32_t flags; |
| 43 | uint32_t pages; |
| 44 | } memorystatus_freeze_entry_t; |
| 45 | |
| 46 | #ifdef PRIVATE |
| 47 | #define FREEZE_PROCESSES_MAX 20 |
| 48 | #define FREEZE_PROCESSES_MAX_SWAP_ENABLED 36 |
| 49 | #endif /* PRIVATE */ |
| 50 | |
| 51 | #ifdef XNU_KERNEL_PRIVATE |
| 52 | |
| 53 | extern unsigned long freeze_threshold_percentage; |
| 54 | extern unsigned int memorystatus_frozen_count; /* # of processes that are currently frozen. */ |
| 55 | extern unsigned int memorystatus_frozen_count_webcontent; /* # of webcontent processes that are currently frozen. */ |
| 56 | extern unsigned int memorystatus_frozen_count_xpc_service; /* # of xpc services that are currently frozen. */ |
| 57 | extern unsigned int memorystatus_frozen_processes_max; |
| 58 | extern unsigned int memorystatus_frozen_shared_mb; |
| 59 | extern unsigned int memorystatus_frozen_shared_mb_max; |
| 60 | extern unsigned int memorystatus_freeze_shared_mb_per_process_max; /* Max. MB allowed per process to be freezer-eligible. */ |
| 61 | extern unsigned int memorystatus_freeze_private_shared_pages_ratio; /* Ratio of private:shared pages for a process to be freezer-eligible. */ |
| 62 | extern unsigned int memorystatus_suspended_count; |
| 63 | extern unsigned int memorystatus_thaw_count; /* # of processes that have been thawed in the current interval. */ |
| 64 | extern unsigned int memorystatus_refreeze_eligible_count; /* # of processes currently thawed i.e. have state on disk & in-memory */ |
| 65 | extern unsigned int memorystatus_freeze_max_candidate_band; |
| 66 | extern uint32_t memorystatus_freeze_current_interval; /* Monotonically increasing interval id. */ |
| 67 | |
| 68 | void memorystatus_freeze_init(void); |
| 69 | extern int memorystatus_freeze_process_sync(proc_t p); |
| 70 | |
| 71 | #ifdef CONFIG_FREEZE |
| 72 | extern int memorystatus_entitled_max_task_footprint_mb; |
| 73 | |
| 74 | #if XNU_TARGET_OS_WATCH |
| 75 | #define FREEZE_PAGES_MIN ( 2 * 1024 * 1024 / PAGE_SIZE) |
| 76 | #else |
| 77 | #define FREEZE_PAGES_MIN ( 8 * 1024 * 1024 / PAGE_SIZE) |
| 78 | #endif |
| 79 | #define FREEZE_PAGES_MAX (max_task_footprint_mb == 0 ? INT_MAX : (max_task_footprint_mb << (20 - PAGE_SHIFT))) |
| 80 | #define FREEZE_PAGES_MAX_SWAP_ENABLED \ |
| 81 | (memorystatus_entitled_max_task_footprint_mb == 0 ? INT_MAX : (memorystatus_entitled_max_task_footprint_mb << (20 - PAGE_SHIFT))) |
| 82 | |
| 83 | #if XNU_TARGET_OS_WATCH |
| 84 | #define FREEZE_SUSPENDED_THRESHOLD_DEFAULT 0 |
| 85 | #else |
| 86 | #define FREEZE_SUSPENDED_THRESHOLD_DEFAULT 4 |
| 87 | #endif |
| 88 | |
| 89 | #define FREEZE_DAILY_MB_MAX_DEFAULT 1024 |
| 90 | #define FREEZE_DEGRADATION_BUDGET_THRESHOLD 25 //degraded perf. when the daily budget left falls below this threshold percentage |
| 91 | |
| 92 | #define MAX_FROZEN_SHARED_MB_PERCENT 10 |
| 93 | #define MAX_FROZEN_PROCESS_DEMOTIONS 2 |
| 94 | #define MAX_FROZEN_PROCESS_DEMOTIONS_SWAP_ENABLED 4 |
| 95 | #define MIN_THAW_DEMOTION_THRESHOLD 5 |
| 96 | |
| 97 | #if XNU_TARGET_OS_WATCH |
| 98 | #define MIN_THAW_REFREEZE_THRESHOLD 0 |
| 99 | #else |
| 100 | #define MIN_THAW_REFREEZE_THRESHOLD 3 |
| 101 | #endif |
| 102 | |
| 103 | #if XNU_TARGET_OS_WATCH |
| 104 | #define FREEZE_MAX_CANDIDATE_BAND JETSAM_PRIORITY_ELEVATED_INACTIVE |
| 105 | #else |
| 106 | #define FREEZE_MAX_CANDIDATE_BAND JETSAM_PRIORITY_AGING_BAND2 |
| 107 | #endif |
| 108 | |
| 109 | |
| 110 | typedef struct throttle_interval_t { |
| 111 | uint32_t mins; |
| 112 | uint32_t burst_multiple; |
| 113 | uint32_t pageouts; |
| 114 | uint32_t max_pageouts; |
| 115 | mach_timespec_t ts; |
| 116 | } throttle_interval_t; |
| 117 | |
| 118 | extern bool memorystatus_freeze_enabled; |
| 119 | extern int memorystatus_freeze_wakeup; |
| 120 | |
| 121 | /* Thresholds */ |
| 122 | extern unsigned int memorystatus_freeze_threshold; |
| 123 | extern unsigned int memorystatus_freeze_pages_min; |
| 124 | extern unsigned int memorystatus_freeze_pages_max; |
| 125 | extern unsigned int memorystatus_freeze_suspended_threshold; |
| 126 | extern unsigned int memorystatus_freeze_daily_mb_max; |
| 127 | extern uint64_t memorystatus_freeze_budget_pages_remaining; //remaining # of pages that can be frozen to disk |
| 128 | extern boolean_t memorystatus_freeze_degradation; //protected by the freezer mutex. Signals we are in a degraded freeze mode. |
| 129 | |
| 130 | extern unsigned int memorystatus_max_frozen_demotions_daily; |
| 131 | extern unsigned int memorystatus_thaw_count_demotion_threshold; |
| 132 | extern unsigned int memorystatus_min_thaw_refreeze_threshold; |
| 133 | |
| 134 | #if DEVELOPMENT || DEBUG |
| 135 | #define FREEZER_CONTROL_GET_STATUS (1) |
| 136 | #endif /* DEVELOPMENT || DEBUG */ |
| 137 | |
| 138 | extern int memorystatus_freeze_jetsam_band; /* the jetsam band which will contain P_MEMSTAT_FROZEN processes */ |
| 139 | |
| 140 | bool memorystatus_freeze_thread_should_run(void); |
| 141 | int memorystatus_set_process_is_freezable(pid_t pid, boolean_t is_freezable); |
| 142 | int memorystatus_get_process_is_freezable(pid_t pid, int *is_freezable); |
| 143 | int memorystatus_freezer_control(int32_t flags, user_addr_t buffer, size_t buffer_size, int32_t *retval); |
| 144 | void memorystatus_freeze_init_proc(proc_t p); |
| 145 | errno_t memorystatus_get_process_is_frozen(pid_t pid, int *is_freezable); |
| 146 | errno_t memorystatus_cmd_grp_set_freeze_list(user_addr_t buffer, size_t buffer_size); |
| 147 | errno_t memorystatus_cmd_grp_set_demote_list(user_addr_t buffer, size_t buffer_size); |
| 148 | |
| 149 | /* Freezer counters collected for telemtry */ |
| 150 | struct memorystatus_freezer_stats_t { |
| 151 | /* |
| 152 | * # of processes that we've considered freezing. |
| 153 | * Used to normalize the error reasons below. |
| 154 | */ |
| 155 | uint64_t mfs_process_considered_count; |
| 156 | |
| 157 | /* |
| 158 | * The following counters track how many times we've failed to freeze |
| 159 | * a process because of a specific FREEZER_ERROR. |
| 160 | */ |
| 161 | /* EXCESS_SHARED_MEMORY */ |
| 162 | uint64_t mfs_error_excess_shared_memory_count; |
| 163 | /* LOW_PRIVATE_SHARED_RATIO */ |
| 164 | uint64_t mfs_error_low_private_shared_ratio_count; |
| 165 | /* NO_COMPRESSOR_SPACE */ |
| 166 | uint64_t mfs_error_no_compressor_space_count; |
| 167 | /* NO_SWAP_SPACE */ |
| 168 | uint64_t mfs_error_no_swap_space_count; |
| 169 | /* pages < memorystatus_freeze_pages_min */ |
| 170 | uint64_t mfs_error_below_min_pages_count; |
| 171 | /* dasd determined it was unlikely to be relaunched. */ |
| 172 | uint64_t mfs_error_low_probability_of_use_count; |
| 173 | /* not in idle bands */ |
| 174 | uint64_t mfs_error_elevated_count; |
| 175 | /* transient reasons (like inability to acquire a lock). */ |
| 176 | uint64_t mfs_error_other_count; |
| 177 | |
| 178 | /* |
| 179 | * # of times that we saw memorystatus_available_pages <= memorystatus_freeze_threshold. |
| 180 | * Used to normalize skipped_full_count and shared_mb_high_count. |
| 181 | */ |
| 182 | uint64_t mfs_below_threshold_count; |
| 183 | |
| 184 | /* Skipped running the freezer because we were out of slots */ |
| 185 | uint64_t mfs_skipped_full_count; |
| 186 | |
| 187 | /* Skipped running the freezer because we were over the shared mb limit*/ |
| 188 | uint64_t mfs_skipped_shared_mb_high_count; |
| 189 | |
| 190 | /* |
| 191 | * How many pages have not been sent to swap because they were in a shared object? |
| 192 | * This is being used to gather telemtry so we can understand the impact we'd have |
| 193 | * on our NAND budget if we did swap out these pages. |
| 194 | */ |
| 195 | uint64_t mfs_shared_pages_skipped; |
| 196 | |
| 197 | /* |
| 198 | * A running sum of the total number of bytes sent to NAND during |
| 199 | * refreeze operations since boot. |
| 200 | */ |
| 201 | uint64_t mfs_bytes_refrozen; |
| 202 | /* The number of refreeze operations since boot */ |
| 203 | uint64_t mfs_refreeze_count; |
| 204 | |
| 205 | /* The number of proceses which have been frozen at least once in the current interval. */ |
| 206 | uint64_t mfs_processes_frozen; |
| 207 | /* The number of processes which have been thawed at least once in the current interval. */ |
| 208 | uint64_t mfs_processes_thawed; |
| 209 | |
| 210 | /* |
| 211 | * Telemetry shows that the majority of freezer usage is attributed to webcontent |
| 212 | * so we track some specific webcontent telemetry here to get more visibility. |
| 213 | */ |
| 214 | |
| 215 | /* The number of webcontent processes which have been frozen at least once in the current interval. */ |
| 216 | uint64_t mfs_processes_frozen_webcontent; |
| 217 | /* The number of webcontent processes which have been thawed at least once in the current interval. */ |
| 218 | uint64_t mfs_processes_thawed_webcontent; |
| 219 | |
| 220 | /* The number of xpc service processes which have been frozen at least once in the current interval. */ |
| 221 | uint64_t mfs_processes_frozen_xpc_service; |
| 222 | |
| 223 | /* The number of fg thaws in the current interval. */ |
| 224 | uint64_t mfs_processes_thawed_fg; |
| 225 | /* The number of fg xpc service thaws in the current interval. */ |
| 226 | uint64_t mfs_processes_thawed_fg_xpc_service; |
| 227 | |
| 228 | /* |
| 229 | * Counts the number of incorrect pids provided via |
| 230 | * MEMORYSTATUS_FLAGS_GRP_SET_FREEZE_PRIORITY in the current interval. |
| 231 | * A high value means dasd should be updating the list more |
| 232 | * frequently. |
| 233 | */ |
| 234 | uint64_t mfs_freeze_pid_mismatches; |
| 235 | /* |
| 236 | * Counts the number of incorrect pids provided via |
| 237 | * MEMORYSTATUS_FLAGS_GRP_SET_DEMOTE_PRIORITY in the current interval. |
| 238 | * A high value means dasd should be updating the list more |
| 239 | * frequently. |
| 240 | */ |
| 241 | uint64_t mfs_demote_pid_mismatches; |
| 242 | |
| 243 | /* |
| 244 | * When we run out of budget, this records how much time is left in the current |
| 245 | * interval. 0 means we have not run out of budget. |
| 246 | */ |
| 247 | uint64_t mfs_budget_exhaustion_duration_remaining; |
| 248 | |
| 249 | /* The number of visible resumes in this interval. Mostly used to filter out idle devices. */ |
| 250 | uint64_t mfs_processes_fg_resumed; |
| 251 | }; |
| 252 | extern struct memorystatus_freezer_stats_t memorystatus_freezer_stats; |
| 253 | |
| 254 | /* |
| 255 | * Called by kern_resource when a process gets a UI priority |
| 256 | */ |
| 257 | void memorystatus_freezer_mark_ui_transition(proc_t p); |
| 258 | |
| 259 | #endif /* CONFIG_FREEZE */ |
| 260 | |
| 261 | #endif /* XNU_KERNEL_PRIVATE */ |
| 262 | |
| 263 | #ifdef PRIVATE |
| 264 | /* Lists all the processes that are currently in the freezer. */ |
| 265 | #define FREEZER_CONTROL_GET_PROCS (2) |
| 266 | |
| 267 | #define FREEZER_CONTROL_GET_PROCS_MAX_COUNT (FREEZE_PROCESSES_MAX * 2) |
| 268 | |
| 269 | typedef struct _global_frozen_procs { |
| 270 | uint64_t gfp_num_frozen; |
| 271 | |
| 272 | struct { |
| 273 | pid_t fp_pid; |
| 274 | proc_name_t fp_name; |
| 275 | } gfp_procs[FREEZER_CONTROL_GET_PROCS_MAX_COUNT]; |
| 276 | } global_frozen_procs_t; |
| 277 | |
| 278 | /* Set the dasd trial identifiers */ |
| 279 | #define FREEZER_CONTROL_SET_DASD_TRIAL_IDENTIFIERS (3) |
| 280 | |
| 281 | typedef struct _memorystatus_freezer_trial_identifiers_v1 { |
| 282 | int version; /* Must be set to 1 */ |
| 283 | uuid_string_t treatment_id; |
| 284 | uuid_string_t experiment_id; |
| 285 | int deployment_id; |
| 286 | } memorystatus_freezer_trial_identifiers_v1; |
| 287 | |
| 288 | /* |
| 289 | * Destructively reset the freezer state in order to perform a policy change. |
| 290 | * Note that this could result in multiple suspended apps getting killed, |
| 291 | * so it should only be used when the device is idle. |
| 292 | */ |
| 293 | #define FREEZER_CONTROL_RESET_STATE (4) |
| 294 | |
| 295 | #endif /* PRIVATE */ |
| 296 | |
| 297 | #endif /* SYS_MEMORYSTATUS_FREEZE_H */ |
| 298 | |