]>
Commit | Line | Data |
---|---|---|
fe8ab488 | 1 | /* |
cb323159 | 2 | * Copyright (c) 2019 Apple Inc. All rights reserved. |
fe8ab488 A |
3 | * |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
0a7de745 | 5 | * |
fe8ab488 A |
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. | |
0a7de745 | 14 | * |
fe8ab488 A |
15 | * Please obtain a copy of the License at |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
0a7de745 | 17 | * |
fe8ab488 A |
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. | |
0a7de745 | 25 | * |
fe8ab488 A |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ |
27 | */ | |
28 | ||
29 | #include <kern/kern_types.h> | |
30 | #include <mach/mach_types.h> | |
31 | #include <mach/boolean.h> | |
32 | ||
33 | #include <kern/coalition.h> | |
cb323159 | 34 | #include <kern/exc_resource.h> |
fe8ab488 | 35 | #include <kern/host.h> |
fe8ab488 | 36 | #include <kern/kalloc.h> |
3e170ce0 | 37 | #include <kern/ledger.h> |
fe8ab488 | 38 | #include <kern/mach_param.h> /* for TASK_CHUNK */ |
cb323159 A |
39 | #if MONOTONIC |
40 | #include <kern/monotonic.h> | |
41 | #endif /* MONOTONIC */ | |
42 | #include <kern/policy_internal.h> | |
fe8ab488 | 43 | #include <kern/task.h> |
5ba3f43e | 44 | #include <kern/thread_group.h> |
fe8ab488 A |
45 | #include <kern/zalloc.h> |
46 | ||
47 | #include <libkern/OSAtomic.h> | |
48 | ||
49 | #include <mach/coalition_notification_server.h> | |
50 | #include <mach/host_priv.h> | |
51 | #include <mach/host_special_ports.h> | |
52 | ||
cb323159 A |
53 | #include <os/log.h> |
54 | ||
fe8ab488 A |
55 | #include <sys/errno.h> |
56 | ||
3e170ce0 A |
57 | /* |
58 | * BSD interface functions | |
59 | */ | |
60 | int coalitions_get_list(int type, struct procinfo_coalinfo *coal_list, int list_sz); | |
cb323159 A |
61 | coalition_t task_get_coalition(task_t task, int type); |
62 | boolean_t coalition_is_leader(task_t task, coalition_t coal); | |
4bd07ac2 | 63 | task_t coalition_get_leader(coalition_t coal); |
3e170ce0 A |
64 | int coalition_get_task_count(coalition_t coal); |
65 | uint64_t coalition_get_page_count(coalition_t coal, int *ntasks); | |
66 | int coalition_get_pid_list(coalition_t coal, uint32_t rolemask, int sort_order, | |
0a7de745 | 67 | int *pid_list, int list_sz); |
3e170ce0 | 68 | |
fe8ab488 A |
69 | /* defined in task.c */ |
70 | extern ledger_template_t task_ledger_template; | |
71 | ||
cb323159 A |
72 | /* |
73 | * Templates; task template is copied due to potential allocation limits on | |
74 | * task ledgers. | |
75 | */ | |
76 | ledger_template_t coalition_task_ledger_template = NULL; | |
77 | ledger_template_t coalition_ledger_template = NULL; | |
78 | ||
79 | extern int proc_selfpid(void); | |
fe8ab488 A |
80 | /* |
81 | * Coalition zone needs limits. We expect there will be as many coalitions as | |
82 | * tasks (same order of magnitude), so use the task zone's limits. | |
83 | * */ | |
84 | #define CONFIG_COALITION_MAX CONFIG_TASK_MAX | |
85 | #define COALITION_CHUNK TASK_CHUNK | |
86 | ||
87 | int unrestrict_coalition_syscalls; | |
5ba3f43e | 88 | int merge_adaptive_coalitions; |
fe8ab488 A |
89 | |
90 | lck_attr_t coalitions_lck_attr; | |
91 | lck_grp_t coalitions_lck_grp; | |
92 | lck_grp_attr_t coalitions_lck_grp_attr; | |
93 | ||
94 | /* coalitions_list_lock protects coalition_count, coalitions queue, next_coalition_id. */ | |
0a7de745 | 95 | decl_lck_mtx_data(static, coalitions_list_lock); |
fe8ab488 A |
96 | static uint64_t coalition_count; |
97 | static uint64_t coalition_next_id = 1; | |
3e170ce0 | 98 | static queue_head_t coalitions_q; |
fe8ab488 | 99 | |
3e170ce0 | 100 | coalition_t init_coalition[COALITION_NUM_TYPES]; |
39037602 | 101 | coalition_t corpse_coalition[COALITION_NUM_TYPES]; |
fe8ab488 A |
102 | |
103 | zone_t coalition_zone; | |
104 | ||
0a7de745 A |
105 | static const char * |
106 | coal_type_str(int type) | |
3e170ce0 | 107 | { |
0a7de745 | 108 | switch (type) { |
3e170ce0 A |
109 | case COALITION_TYPE_RESOURCE: |
110 | return "RESOURCE"; | |
111 | case COALITION_TYPE_JETSAM: | |
112 | return "JETSAM"; | |
113 | default: | |
114 | return "<unknown>"; | |
115 | } | |
116 | } | |
117 | ||
118 | struct coalition_type { | |
119 | int type; | |
120 | int has_default; | |
121 | /* | |
122 | * init | |
123 | * pre-condition: coalition just allocated (unlocked), unreferenced, | |
124 | * type field set | |
125 | */ | |
126 | kern_return_t (*init)(coalition_t coal, boolean_t privileged); | |
127 | ||
128 | /* | |
129 | * dealloc | |
130 | * pre-condition: coalition unlocked | |
131 | * pre-condition: coalition refcount=0, active_count=0, | |
132 | * termrequested=1, terminated=1, reaped=1 | |
133 | */ | |
134 | void (*dealloc)(coalition_t coal); | |
135 | ||
136 | /* | |
137 | * adopt_task | |
138 | * pre-condition: coalition locked | |
139 | * pre-condition: coalition !repead and !terminated | |
140 | */ | |
141 | kern_return_t (*adopt_task)(coalition_t coal, task_t task); | |
142 | ||
143 | /* | |
144 | * remove_task | |
145 | * pre-condition: coalition locked | |
146 | * pre-condition: task has been removed from coalition's task list | |
147 | */ | |
148 | kern_return_t (*remove_task)(coalition_t coal, task_t task); | |
149 | ||
150 | /* | |
151 | * set_taskrole | |
152 | * pre-condition: coalition locked | |
153 | * pre-condition: task added to coalition's task list, | |
154 | * active_count >= 1 (at least the given task is active) | |
155 | */ | |
156 | kern_return_t (*set_taskrole)(coalition_t coal, task_t task, int role); | |
157 | ||
158 | /* | |
159 | * get_taskrole | |
160 | * pre-condition: coalition locked | |
161 | * pre-condition: task added to coalition's task list, | |
162 | * active_count >= 1 (at least the given task is active) | |
163 | */ | |
164 | int (*get_taskrole)(coalition_t coal, task_t task); | |
165 | ||
166 | /* | |
167 | * iterate_tasks | |
168 | * pre-condition: coalition locked | |
169 | */ | |
170 | void (*iterate_tasks)(coalition_t coal, void *ctx, void (*callback)(coalition_t, void *, task_t)); | |
171 | }; | |
172 | ||
173 | /* | |
174 | * COALITION_TYPE_RESOURCE | |
175 | */ | |
fe8ab488 | 176 | |
3e170ce0 A |
177 | static kern_return_t i_coal_resource_init(coalition_t coal, boolean_t privileged); |
178 | static void i_coal_resource_dealloc(coalition_t coal); | |
179 | static kern_return_t i_coal_resource_adopt_task(coalition_t coal, task_t task); | |
180 | static kern_return_t i_coal_resource_remove_task(coalition_t coal, task_t task); | |
181 | static kern_return_t i_coal_resource_set_taskrole(coalition_t coal, | |
0a7de745 | 182 | task_t task, int role); |
3e170ce0 A |
183 | static int i_coal_resource_get_taskrole(coalition_t coal, task_t task); |
184 | static void i_coal_resource_iterate_tasks(coalition_t coal, void *ctx, | |
0a7de745 | 185 | void (*callback)(coalition_t, void *, task_t)); |
3e170ce0 | 186 | |
a39ff7e2 A |
187 | /* |
188 | * Ensure COALITION_NUM_THREAD_QOS_TYPES defined in mach/coalition.h still | |
189 | * matches THREAD_QOS_LAST defined in mach/thread_policy.h | |
190 | */ | |
191 | static_assert(COALITION_NUM_THREAD_QOS_TYPES == THREAD_QOS_LAST); | |
192 | ||
3e170ce0 | 193 | struct i_resource_coalition { |
cb323159 A |
194 | /* |
195 | * This keeps track of resource utilization of tasks that are no longer active | |
196 | * in the coalition and is updated when a task is removed from the coalition. | |
197 | */ | |
fe8ab488 A |
198 | ledger_t ledger; |
199 | uint64_t bytesread; | |
200 | uint64_t byteswritten; | |
39037602 | 201 | uint64_t energy; |
fe8ab488 | 202 | uint64_t gpu_time; |
4bd07ac2 A |
203 | uint64_t logical_immediate_writes; |
204 | uint64_t logical_deferred_writes; | |
205 | uint64_t logical_invalidated_writes; | |
206 | uint64_t logical_metadata_writes; | |
cb323159 A |
207 | uint64_t logical_immediate_writes_to_external; |
208 | uint64_t logical_deferred_writes_to_external; | |
209 | uint64_t logical_invalidated_writes_to_external; | |
210 | uint64_t logical_metadata_writes_to_external; | |
5ba3f43e | 211 | uint64_t cpu_ptime; |
a39ff7e2 A |
212 | uint64_t cpu_time_eqos[COALITION_NUM_THREAD_QOS_TYPES]; /* cpu time per effective QoS class */ |
213 | uint64_t cpu_time_rqos[COALITION_NUM_THREAD_QOS_TYPES]; /* cpu time per requested QoS class */ | |
cb323159 A |
214 | uint64_t cpu_instructions; |
215 | uint64_t cpu_cycles; | |
fe8ab488 | 216 | |
3e170ce0 A |
217 | uint64_t task_count; /* tasks that have started in this coalition */ |
218 | uint64_t dead_task_count; /* tasks that have exited in this coalition; | |
0a7de745 A |
219 | * subtract from task_count to get count |
220 | * of "active" tasks */ | |
fe8ab488 A |
221 | /* |
222 | * Count the length of time this coalition had at least one active task. | |
223 | * This can be a 'denominator' to turn e.g. cpu_time to %cpu. | |
224 | * */ | |
225 | uint64_t last_became_nonempty_time; | |
226 | uint64_t time_nonempty; | |
227 | ||
3e170ce0 | 228 | queue_head_t tasks; /* List of active tasks in the coalition */ |
cb323159 A |
229 | /* |
230 | * This ledger is used for triggering resource exception. For the tracked resources, this is updated | |
231 | * when the member tasks' resource usage changes. | |
232 | */ | |
233 | ledger_t resource_monitor_ledger; | |
3e170ce0 | 234 | }; |
fe8ab488 | 235 | |
3e170ce0 A |
236 | /* |
237 | * COALITION_TYPE_JETSAM | |
238 | */ | |
fe8ab488 | 239 | |
3e170ce0 A |
240 | static kern_return_t i_coal_jetsam_init(coalition_t coal, boolean_t privileged); |
241 | static void i_coal_jetsam_dealloc(coalition_t coal); | |
242 | static kern_return_t i_coal_jetsam_adopt_task(coalition_t coal, task_t task); | |
243 | static kern_return_t i_coal_jetsam_remove_task(coalition_t coal, task_t task); | |
244 | static kern_return_t i_coal_jetsam_set_taskrole(coalition_t coal, | |
0a7de745 | 245 | task_t task, int role); |
cb323159 | 246 | int i_coal_jetsam_get_taskrole(coalition_t coal, task_t task); |
3e170ce0 | 247 | static void i_coal_jetsam_iterate_tasks(coalition_t coal, void *ctx, |
0a7de745 | 248 | void (*callback)(coalition_t, void *, task_t)); |
3e170ce0 A |
249 | |
250 | struct i_jetsam_coalition { | |
251 | task_t leader; | |
252 | queue_head_t extensions; | |
253 | queue_head_t services; | |
254 | queue_head_t other; | |
d9a64523 | 255 | struct thread_group *thread_group; |
3e170ce0 | 256 | }; |
fe8ab488 | 257 | |
fe8ab488 | 258 | |
3e170ce0 A |
259 | /* |
260 | * main coalition structure | |
261 | */ | |
262 | struct coalition { | |
263 | uint64_t id; /* monotonically increasing */ | |
264 | uint32_t type; | |
5ba3f43e | 265 | uint32_t role; /* default task role (background, adaptive, interactive, etc) */ |
3e170ce0 A |
266 | uint32_t ref_count; /* Number of references to the memory containing this struct */ |
267 | uint32_t active_count; /* Number of members of (tasks in) the | |
0a7de745 A |
268 | * coalition, plus vouchers referring |
269 | * to the coalition */ | |
3e170ce0 A |
270 | uint32_t focal_task_count; /* Number of TASK_FOREGROUND_APPLICATION tasks in the coalition */ |
271 | uint32_t nonfocal_task_count; /* Number of TASK_BACKGROUND_APPLICATION tasks in the coalition */ | |
272 | ||
273 | /* coalition flags */ | |
274 | uint32_t privileged : 1; /* Members of this coalition may create | |
0a7de745 A |
275 | * and manage coalitions and may posix_spawn |
276 | * processes into selected coalitions */ | |
fe8ab488 | 277 | /* ast? */ |
fe8ab488 | 278 | /* voucher */ |
3e170ce0 A |
279 | uint32_t termrequested : 1; /* launchd has requested termination when coalition becomes empty */ |
280 | uint32_t terminated : 1; /* coalition became empty and spawns are now forbidden */ | |
281 | uint32_t reaped : 1; /* reaped, invisible to userspace, but waiting for ref_count to go to zero */ | |
282 | uint32_t notified : 1; /* no-more-processes notification was sent via special port */ | |
0a7de745 | 283 | uint32_t efficient : 1; /* launchd has marked the coalition as efficient */ |
5ba3f43e | 284 | #if DEVELOPMENT || DEBUG |
3e170ce0 A |
285 | uint32_t should_notify : 1; /* should this coalition send notifications (default: yes) */ |
286 | #endif | |
fe8ab488 | 287 | |
3e170ce0 | 288 | queue_chain_t coalitions; /* global list of coalitions */ |
a1c7dba1 | 289 | |
cb323159 | 290 | decl_lck_mtx_data(, lock); /* Coalition lock. */ |
3e170ce0 A |
291 | |
292 | /* put coalition type-specific structures here */ | |
293 | union { | |
294 | struct i_resource_coalition r; | |
295 | struct i_jetsam_coalition j; | |
296 | }; | |
297 | }; | |
298 | ||
299 | /* | |
300 | * register different coalition types: | |
301 | * these must be kept in the order specified in coalition.h | |
302 | */ | |
303 | static const struct coalition_type | |
0a7de745 | 304 | s_coalition_types[COALITION_NUM_TYPES] = { |
3e170ce0 A |
305 | { |
306 | COALITION_TYPE_RESOURCE, | |
307 | 1, | |
308 | i_coal_resource_init, | |
309 | i_coal_resource_dealloc, | |
310 | i_coal_resource_adopt_task, | |
311 | i_coal_resource_remove_task, | |
312 | i_coal_resource_set_taskrole, | |
313 | i_coal_resource_get_taskrole, | |
314 | i_coal_resource_iterate_tasks, | |
315 | }, | |
316 | { | |
317 | COALITION_TYPE_JETSAM, | |
318 | 1, | |
319 | i_coal_jetsam_init, | |
320 | i_coal_jetsam_dealloc, | |
321 | i_coal_jetsam_adopt_task, | |
322 | i_coal_jetsam_remove_task, | |
323 | i_coal_jetsam_set_taskrole, | |
324 | i_coal_jetsam_get_taskrole, | |
325 | i_coal_jetsam_iterate_tasks, | |
326 | }, | |
fe8ab488 A |
327 | }; |
328 | ||
3e170ce0 A |
329 | #define coal_call(coal, func, ...) \ |
330 | (s_coalition_types[(coal)->type].func)(coal, ## __VA_ARGS__) | |
331 | ||
332 | ||
fe8ab488 A |
333 | #define coalition_lock(c) do{ lck_mtx_lock(&c->lock); }while(0) |
334 | #define coalition_unlock(c) do{ lck_mtx_unlock(&c->lock); }while(0) | |
335 | ||
5ba3f43e A |
336 | /* |
337 | * Define the coalition type to track focal tasks. | |
338 | * On embedded, track them using jetsam coalitions since they have associated thread | |
339 | * groups which reflect this property as a flag (and pass it down to CLPC). | |
340 | * On non-embedded platforms, since not all coalitions have jetsam coalitions | |
341 | * track focal counts on the resource coalition. | |
342 | */ | |
343 | #if CONFIG_EMBEDDED | |
344 | #define COALITION_FOCAL_TASKS_ACCOUNTING COALITION_TYPE_JETSAM | |
345 | #else /* CONFIG_EMBEDDED */ | |
346 | #define COALITION_FOCAL_TASKS_ACCOUNTING COALITION_TYPE_RESOURCE | |
347 | #endif /* CONFIG_EMBEDDED */ | |
348 | ||
349 | ||
cb323159 A |
350 | /* |
351 | * | |
352 | * Coalition ledger implementation | |
353 | * | |
354 | */ | |
355 | ||
356 | struct coalition_ledger_indices coalition_ledgers = | |
357 | {.logical_writes = -1, }; | |
358 | void __attribute__((noinline)) SENDING_NOTIFICATION__THIS_COALITION_IS_CAUSING_TOO_MUCH_IO(int flavor); | |
359 | ||
360 | ledger_t | |
361 | coalition_ledger_get_from_task(task_t task) | |
362 | { | |
363 | ledger_t ledger = LEDGER_NULL; | |
364 | coalition_t coal = task->coalition[COALITION_TYPE_RESOURCE]; | |
365 | ||
366 | if (coal != NULL && (!queue_empty(&task->task_coalition[COALITION_TYPE_RESOURCE]))) { | |
367 | ledger = coal->r.resource_monitor_ledger; | |
368 | ledger_reference(ledger); | |
369 | } | |
370 | return ledger; | |
371 | } | |
372 | ||
373 | ||
374 | enum { | |
375 | COALITION_IO_LEDGER_ENABLE, | |
376 | COALITION_IO_LEDGER_DISABLE | |
377 | }; | |
378 | ||
379 | void | |
380 | coalition_io_monitor_ctl(struct coalition *coalition, uint32_t flags, int64_t limit) | |
381 | { | |
382 | ledger_t ledger = coalition->r.resource_monitor_ledger; | |
383 | ||
384 | if (flags == COALITION_IO_LEDGER_ENABLE) { | |
385 | /* Configure the logical I/O ledger */ | |
386 | ledger_set_limit(ledger, coalition_ledgers.logical_writes, (limit * 1024 * 1024), 0); | |
387 | ledger_set_period(ledger, coalition_ledgers.logical_writes, (COALITION_LEDGER_MONITOR_INTERVAL_SECS * NSEC_PER_SEC)); | |
388 | } else if (flags == COALITION_IO_LEDGER_DISABLE) { | |
389 | ledger_disable_refill(ledger, coalition_ledgers.logical_writes); | |
390 | ledger_disable_callback(ledger, coalition_ledgers.logical_writes); | |
391 | } | |
392 | } | |
393 | ||
394 | int | |
395 | coalition_ledger_set_logical_writes_limit(struct coalition *coalition, int64_t limit) | |
396 | { | |
397 | int error = 0; | |
398 | ||
399 | /* limit = -1 will be used to disable the limit and the callback */ | |
400 | if (limit > COALITION_MAX_LOGICAL_WRITES_LIMIT || limit == 0 || limit < -1) { | |
401 | error = EINVAL; | |
402 | goto out; | |
403 | } | |
404 | ||
405 | coalition_lock(coalition); | |
406 | if (limit == -1) { | |
407 | coalition_io_monitor_ctl(coalition, COALITION_IO_LEDGER_DISABLE, limit); | |
408 | } else { | |
409 | coalition_io_monitor_ctl(coalition, COALITION_IO_LEDGER_ENABLE, limit); | |
410 | } | |
411 | coalition_unlock(coalition); | |
412 | out: | |
413 | return error; | |
414 | } | |
415 | ||
416 | void __attribute__((noinline)) | |
417 | SENDING_NOTIFICATION__THIS_COALITION_IS_CAUSING_TOO_MUCH_IO(int flavor) | |
418 | { | |
419 | int pid = proc_selfpid(); | |
420 | ledger_amount_t new_limit; | |
421 | task_t task = current_task(); | |
422 | struct ledger_entry_info lei; | |
423 | kern_return_t kr; | |
424 | ledger_t ledger; | |
425 | struct coalition *coalition = task->coalition[COALITION_TYPE_RESOURCE]; | |
426 | ||
427 | assert(coalition != NULL); | |
428 | ledger = coalition->r.resource_monitor_ledger; | |
429 | ||
430 | switch (flavor) { | |
431 | case FLAVOR_IO_LOGICAL_WRITES: | |
432 | ledger_get_entry_info(ledger, coalition_ledgers.logical_writes, &lei); | |
433 | trace_resource_violation(RMON_LOGWRITES_VIOLATED, &lei); | |
434 | break; | |
435 | default: | |
436 | goto Exit; | |
437 | } | |
438 | ||
439 | os_log(OS_LOG_DEFAULT, "Coalition [%lld] caught causing excessive I/O (flavor: %d). Task I/O: %lld MB. [Limit : %lld MB per %lld secs]. Triggered by process [%d]\n", | |
440 | coalition->id, flavor, (lei.lei_balance / (1024 * 1024)), (lei.lei_limit / (1024 * 1024)), | |
441 | (lei.lei_refill_period / NSEC_PER_SEC), pid); | |
442 | ||
443 | kr = send_resource_violation(send_disk_writes_violation, task, &lei, kRNFlagsNone); | |
444 | if (kr) { | |
445 | os_log(OS_LOG_DEFAULT, "ERROR %#x returned from send_resource_violation(disk_writes, ...)\n", kr); | |
446 | } | |
447 | ||
448 | /* | |
449 | * Continue to monitor the coalition after it hits the initital limit, but increase | |
450 | * the limit exponentially so that we don't spam the listener. | |
451 | */ | |
452 | new_limit = (lei.lei_limit / 1024 / 1024) * 4; | |
453 | coalition_lock(coalition); | |
454 | if (new_limit > COALITION_MAX_LOGICAL_WRITES_LIMIT) { | |
455 | coalition_io_monitor_ctl(coalition, COALITION_IO_LEDGER_DISABLE, -1); | |
456 | } else { | |
457 | coalition_io_monitor_ctl(coalition, COALITION_IO_LEDGER_ENABLE, new_limit); | |
458 | } | |
459 | coalition_unlock(coalition); | |
460 | ||
461 | Exit: | |
462 | return; | |
463 | } | |
464 | ||
465 | void | |
466 | coalition_io_rate_exceeded(int warning, const void *param0, __unused const void *param1) | |
467 | { | |
468 | if (warning == 0) { | |
469 | SENDING_NOTIFICATION__THIS_COALITION_IS_CAUSING_TOO_MUCH_IO((int)param0); | |
470 | } | |
471 | } | |
472 | ||
473 | void | |
474 | init_coalition_ledgers(void) | |
475 | { | |
476 | ledger_template_t t; | |
477 | assert(coalition_ledger_template == NULL); | |
478 | ||
479 | if ((t = ledger_template_create("Per-coalition ledgers")) == NULL) { | |
480 | panic("couldn't create coalition ledger template"); | |
481 | } | |
482 | ||
483 | coalition_ledgers.logical_writes = ledger_entry_add(t, "logical_writes", "res", "bytes"); | |
484 | ||
485 | if (coalition_ledgers.logical_writes < 0) { | |
486 | panic("couldn't create entries for coaliton ledger template"); | |
487 | } | |
488 | ||
489 | ledger_set_callback(t, coalition_ledgers.logical_writes, coalition_io_rate_exceeded, (void *)FLAVOR_IO_LOGICAL_WRITES, NULL); | |
490 | ledger_template_complete(t); | |
491 | ||
492 | coalition_task_ledger_template = ledger_template_copy(task_ledger_template, "Coalition task ledgers"); | |
493 | ||
494 | if (coalition_task_ledger_template == NULL) { | |
495 | panic("couldn't create coalition task ledger template"); | |
496 | } | |
497 | ||
498 | ledger_template_complete(coalition_task_ledger_template); | |
499 | ||
500 | coalition_ledger_template = t; | |
501 | } | |
502 | ||
503 | void | |
504 | coalition_io_ledger_update(task_t task, int32_t flavor, boolean_t is_credit, uint32_t io_size) | |
505 | { | |
506 | ledger_t ledger; | |
507 | coalition_t coal = task->coalition[COALITION_TYPE_RESOURCE]; | |
508 | ||
509 | assert(coal != NULL); | |
510 | ledger = coal->r.resource_monitor_ledger; | |
511 | if (LEDGER_VALID(ledger)) { | |
512 | if (flavor == FLAVOR_IO_LOGICAL_WRITES) { | |
513 | if (is_credit) { | |
514 | ledger_credit(ledger, coalition_ledgers.logical_writes, io_size); | |
515 | } else { | |
516 | ledger_debit(ledger, coalition_ledgers.logical_writes, io_size); | |
517 | } | |
518 | } | |
519 | } | |
520 | } | |
521 | ||
fe8ab488 A |
522 | static void |
523 | coalition_notify_user(uint64_t id, uint32_t flags) | |
524 | { | |
525 | mach_port_t user_port; | |
526 | kern_return_t kr; | |
527 | ||
528 | kr = host_get_coalition_port(host_priv_self(), &user_port); | |
529 | if ((kr != KERN_SUCCESS) || !IPC_PORT_VALID(user_port)) { | |
530 | return; | |
531 | } | |
532 | ||
533 | coalition_notification(user_port, id, flags); | |
39037602 | 534 | ipc_port_release_send(user_port); |
fe8ab488 A |
535 | } |
536 | ||
537 | /* | |
3e170ce0 A |
538 | * |
539 | * COALITION_TYPE_RESOURCE | |
540 | * | |
fe8ab488 | 541 | */ |
3e170ce0 A |
542 | static kern_return_t |
543 | i_coal_resource_init(coalition_t coal, boolean_t privileged) | |
fe8ab488 | 544 | { |
3e170ce0 A |
545 | (void)privileged; |
546 | assert(coal && coal->type == COALITION_TYPE_RESOURCE); | |
cb323159 | 547 | coal->r.ledger = ledger_instantiate(coalition_task_ledger_template, |
0a7de745 A |
548 | LEDGER_CREATE_ACTIVE_ENTRIES); |
549 | if (coal->r.ledger == NULL) { | |
3e170ce0 | 550 | return KERN_RESOURCE_SHORTAGE; |
0a7de745 | 551 | } |
3e170ce0 | 552 | |
cb323159 A |
553 | coal->r.resource_monitor_ledger = ledger_instantiate(coalition_ledger_template, |
554 | LEDGER_CREATE_ACTIVE_ENTRIES); | |
555 | if (coal->r.resource_monitor_ledger == NULL) { | |
556 | return KERN_RESOURCE_SHORTAGE; | |
557 | } | |
558 | ||
3e170ce0 A |
559 | queue_init(&coal->r.tasks); |
560 | ||
561 | return KERN_SUCCESS; | |
562 | } | |
563 | ||
564 | static void | |
565 | i_coal_resource_dealloc(coalition_t coal) | |
566 | { | |
567 | assert(coal && coal->type == COALITION_TYPE_RESOURCE); | |
cb323159 | 568 | |
3e170ce0 | 569 | ledger_dereference(coal->r.ledger); |
cb323159 | 570 | ledger_dereference(coal->r.resource_monitor_ledger); |
3e170ce0 A |
571 | } |
572 | ||
573 | static kern_return_t | |
574 | i_coal_resource_adopt_task(coalition_t coal, task_t task) | |
575 | { | |
576 | struct i_resource_coalition *cr; | |
577 | ||
578 | assert(coal && coal->type == COALITION_TYPE_RESOURCE); | |
579 | assert(queue_empty(&task->task_coalition[COALITION_TYPE_RESOURCE])); | |
580 | ||
581 | cr = &coal->r; | |
582 | cr->task_count++; | |
583 | ||
584 | if (cr->task_count < cr->dead_task_count) { | |
585 | panic("%s: coalition %p id:%llu type:%s task_count(%llu) < dead_task_count(%llu)", | |
0a7de745 A |
586 | __func__, coal, coal->id, coal_type_str(coal->type), |
587 | cr->task_count, cr->dead_task_count); | |
fe8ab488 A |
588 | } |
589 | ||
3e170ce0 A |
590 | /* If moving from 0->1 active tasks */ |
591 | if (cr->task_count - cr->dead_task_count == 1) { | |
592 | cr->last_became_nonempty_time = mach_absolute_time(); | |
fe8ab488 | 593 | } |
3e170ce0 A |
594 | |
595 | /* put the task on the coalition's list of tasks */ | |
596 | enqueue_tail(&cr->tasks, &task->task_coalition[COALITION_TYPE_RESOURCE]); | |
597 | ||
598 | coal_dbg("Added PID:%d to id:%llu, task_count:%llu, dead_count:%llu, nonempty_time:%llu", | |
0a7de745 A |
599 | task_pid(task), coal->id, cr->task_count, cr->dead_task_count, |
600 | cr->last_became_nonempty_time); | |
3e170ce0 A |
601 | |
602 | return KERN_SUCCESS; | |
603 | } | |
604 | ||
605 | static kern_return_t | |
606 | i_coal_resource_remove_task(coalition_t coal, task_t task) | |
607 | { | |
608 | struct i_resource_coalition *cr; | |
609 | ||
610 | assert(coal && coal->type == COALITION_TYPE_RESOURCE); | |
611 | assert(task->coalition[COALITION_TYPE_RESOURCE] == coal); | |
612 | assert(!queue_empty(&task->task_coalition[COALITION_TYPE_RESOURCE])); | |
613 | ||
614 | /* | |
615 | * handle resource coalition accounting rollup for dead tasks | |
616 | */ | |
617 | cr = &coal->r; | |
618 | ||
619 | cr->dead_task_count++; | |
620 | ||
621 | if (cr->task_count < cr->dead_task_count) { | |
622 | panic("%s: coalition %p id:%llu type:%s task_count(%llu) < dead_task_count(%llu)", | |
0a7de745 | 623 | __func__, coal, coal->id, coal_type_str(coal->type), cr->task_count, cr->dead_task_count); |
3e170ce0 A |
624 | } |
625 | ||
626 | /* If moving from 1->0 active tasks */ | |
627 | if (cr->task_count - cr->dead_task_count == 0) { | |
628 | uint64_t last_time_nonempty = mach_absolute_time() - cr->last_became_nonempty_time; | |
629 | cr->last_became_nonempty_time = 0; | |
630 | cr->time_nonempty += last_time_nonempty; | |
631 | } | |
632 | ||
743345f9 A |
633 | /* Do not roll up for exec'd task or exec copy task */ |
634 | if (!task_is_exec_copy(task) && !task_did_exec(task)) { | |
635 | ledger_rollup(cr->ledger, task->ledger); | |
636 | cr->bytesread += task->task_io_stats->disk_reads.size; | |
637 | cr->byteswritten += task->task_io_stats->total_io.size - task->task_io_stats->disk_reads.size; | |
5ba3f43e | 638 | #if !CONFIG_EMBEDDED |
743345f9 | 639 | cr->gpu_time += task_gpu_utilisation(task); |
5ba3f43e A |
640 | #else |
641 | cr->energy += task_energy(task); | |
642 | #endif | |
cb323159 A |
643 | cr->logical_immediate_writes += task->task_writes_counters_internal.task_immediate_writes; |
644 | cr->logical_deferred_writes += task->task_writes_counters_internal.task_deferred_writes; | |
645 | cr->logical_invalidated_writes += task->task_writes_counters_internal.task_invalidated_writes; | |
646 | cr->logical_metadata_writes += task->task_writes_counters_internal.task_metadata_writes; | |
647 | cr->logical_immediate_writes_to_external += task->task_writes_counters_external.task_immediate_writes; | |
648 | cr->logical_deferred_writes_to_external += task->task_writes_counters_external.task_deferred_writes; | |
649 | cr->logical_invalidated_writes_to_external += task->task_writes_counters_external.task_invalidated_writes; | |
650 | cr->logical_metadata_writes_to_external += task->task_writes_counters_external.task_metadata_writes; | |
5ba3f43e | 651 | cr->cpu_ptime += task_cpu_ptime(task); |
a39ff7e2 | 652 | task_update_cpu_time_qos_stats(task, cr->cpu_time_eqos, cr->cpu_time_rqos); |
cb323159 A |
653 | #if MONOTONIC |
654 | uint64_t counts[MT_CORE_NFIXED] = {}; | |
655 | (void)mt_fixed_task_counts(task, counts); | |
656 | cr->cpu_cycles += counts[MT_CORE_CYCLES]; | |
657 | #if defined(MT_CORE_INSTRS) | |
658 | cr->cpu_instructions += counts[MT_CORE_INSTRS]; | |
659 | #endif /* defined(MT_CORE_INSTRS) */ | |
660 | #endif /* MONOTONIC */ | |
743345f9 | 661 | } |
3e170ce0 A |
662 | |
663 | /* remove the task from the coalition's list */ | |
664 | remqueue(&task->task_coalition[COALITION_TYPE_RESOURCE]); | |
665 | queue_chain_init(task->task_coalition[COALITION_TYPE_RESOURCE]); | |
666 | ||
667 | coal_dbg("removed PID:%d from id:%llu, task_count:%llu, dead_count:%llu", | |
0a7de745 | 668 | task_pid(task), coal->id, cr->task_count, cr->dead_task_count); |
3e170ce0 A |
669 | |
670 | return KERN_SUCCESS; | |
671 | } | |
672 | ||
673 | static kern_return_t | |
674 | i_coal_resource_set_taskrole(__unused coalition_t coal, | |
0a7de745 | 675 | __unused task_t task, __unused int role) |
3e170ce0 A |
676 | { |
677 | return KERN_SUCCESS; | |
678 | } | |
679 | ||
680 | static int | |
681 | i_coal_resource_get_taskrole(__unused coalition_t coal, __unused task_t task) | |
682 | { | |
683 | task_t t; | |
684 | ||
685 | assert(coal && coal->type == COALITION_TYPE_RESOURCE); | |
686 | ||
687 | qe_foreach_element(t, &coal->r.tasks, task_coalition[COALITION_TYPE_RESOURCE]) { | |
0a7de745 | 688 | if (t == task) { |
3e170ce0 | 689 | return COALITION_TASKROLE_UNDEF; |
0a7de745 | 690 | } |
3e170ce0 A |
691 | } |
692 | ||
693 | return -1; | |
694 | } | |
695 | ||
696 | static void | |
697 | i_coal_resource_iterate_tasks(coalition_t coal, void *ctx, void (*callback)(coalition_t, void *, task_t)) | |
698 | { | |
699 | task_t t; | |
700 | assert(coal && coal->type == COALITION_TYPE_RESOURCE); | |
701 | ||
702 | qe_foreach_element(t, &coal->r.tasks, task_coalition[COALITION_TYPE_RESOURCE]) | |
0a7de745 | 703 | callback(coal, ctx, t); |
fe8ab488 A |
704 | } |
705 | ||
706 | kern_return_t | |
707 | coalition_resource_usage_internal(coalition_t coal, struct coalition_resource_usage *cru_out) | |
708 | { | |
709 | kern_return_t kr; | |
710 | ledger_amount_t credit, debit; | |
39037602 | 711 | int i; |
fe8ab488 | 712 | |
0a7de745 | 713 | if (coal->type != COALITION_TYPE_RESOURCE) { |
3e170ce0 | 714 | return KERN_INVALID_ARGUMENT; |
0a7de745 | 715 | } |
3e170ce0 | 716 | |
39037602 A |
717 | /* Return KERN_INVALID_ARGUMENT for Corpse coalition */ |
718 | for (i = 0; i < COALITION_NUM_TYPES; i++) { | |
719 | if (coal == corpse_coalition[i]) { | |
720 | return KERN_INVALID_ARGUMENT; | |
721 | } | |
722 | } | |
723 | ||
cb323159 | 724 | ledger_t sum_ledger = ledger_instantiate(coalition_task_ledger_template, LEDGER_CREATE_ACTIVE_ENTRIES); |
0a7de745 | 725 | if (sum_ledger == LEDGER_NULL) { |
fe8ab488 | 726 | return KERN_RESOURCE_SHORTAGE; |
0a7de745 | 727 | } |
fe8ab488 A |
728 | |
729 | coalition_lock(coal); | |
730 | ||
731 | /* | |
732 | * Start with the coalition's ledger, which holds the totals from all | |
733 | * the dead tasks. | |
734 | */ | |
3e170ce0 A |
735 | ledger_rollup(sum_ledger, coal->r.ledger); |
736 | uint64_t bytesread = coal->r.bytesread; | |
737 | uint64_t byteswritten = coal->r.byteswritten; | |
738 | uint64_t gpu_time = coal->r.gpu_time; | |
39037602 | 739 | uint64_t energy = coal->r.energy; |
4bd07ac2 A |
740 | uint64_t logical_immediate_writes = coal->r.logical_immediate_writes; |
741 | uint64_t logical_deferred_writes = coal->r.logical_deferred_writes; | |
742 | uint64_t logical_invalidated_writes = coal->r.logical_invalidated_writes; | |
743 | uint64_t logical_metadata_writes = coal->r.logical_metadata_writes; | |
cb323159 A |
744 | uint64_t logical_immediate_writes_to_external = coal->r.logical_immediate_writes_to_external; |
745 | uint64_t logical_deferred_writes_to_external = coal->r.logical_deferred_writes_to_external; | |
746 | uint64_t logical_invalidated_writes_to_external = coal->r.logical_invalidated_writes_to_external; | |
747 | uint64_t logical_metadata_writes_to_external = coal->r.logical_metadata_writes_to_external; | |
3e170ce0 A |
748 | int64_t cpu_time_billed_to_me = 0; |
749 | int64_t cpu_time_billed_to_others = 0; | |
5ba3f43e A |
750 | int64_t energy_billed_to_me = 0; |
751 | int64_t energy_billed_to_others = 0; | |
752 | uint64_t cpu_ptime = coal->r.cpu_ptime; | |
a39ff7e2 A |
753 | uint64_t cpu_time_eqos[COALITION_NUM_THREAD_QOS_TYPES]; |
754 | memcpy(cpu_time_eqos, coal->r.cpu_time_eqos, sizeof(cpu_time_eqos)); | |
755 | uint64_t cpu_time_rqos[COALITION_NUM_THREAD_QOS_TYPES]; | |
756 | memcpy(cpu_time_rqos, coal->r.cpu_time_rqos, sizeof(cpu_time_rqos)); | |
cb323159 A |
757 | uint64_t cpu_instructions = coal->r.cpu_instructions; |
758 | uint64_t cpu_cycles = coal->r.cpu_cycles; | |
759 | ||
fe8ab488 A |
760 | /* |
761 | * Add to that all the active tasks' ledgers. Tasks cannot deallocate | |
762 | * out from under us, since we hold the coalition lock. | |
763 | */ | |
764 | task_t task; | |
3e170ce0 | 765 | qe_foreach_element(task, &coal->r.tasks, task_coalition[COALITION_TYPE_RESOURCE]) { |
743345f9 A |
766 | /* |
767 | * Rolling up stats for exec copy task or exec'd task will lead to double accounting. | |
768 | * Cannot take task lock after taking coaliton lock | |
769 | */ | |
770 | if (task_is_exec_copy(task) || task_did_exec(task)) { | |
771 | continue; | |
772 | } | |
773 | ||
fe8ab488 A |
774 | ledger_rollup(sum_ledger, task->ledger); |
775 | bytesread += task->task_io_stats->disk_reads.size; | |
776 | byteswritten += task->task_io_stats->total_io.size - task->task_io_stats->disk_reads.size; | |
5ba3f43e | 777 | #if !CONFIG_EMBEDDED |
fe8ab488 | 778 | gpu_time += task_gpu_utilisation(task); |
5ba3f43e A |
779 | #else |
780 | energy += task_energy(task); | |
781 | #endif | |
cb323159 A |
782 | logical_immediate_writes += task->task_writes_counters_internal.task_immediate_writes; |
783 | logical_deferred_writes += task->task_writes_counters_internal.task_deferred_writes; | |
784 | logical_invalidated_writes += task->task_writes_counters_internal.task_invalidated_writes; | |
785 | logical_metadata_writes += task->task_writes_counters_internal.task_metadata_writes; | |
786 | logical_immediate_writes_to_external += task->task_writes_counters_external.task_immediate_writes; | |
787 | logical_deferred_writes_to_external += task->task_writes_counters_external.task_deferred_writes; | |
788 | logical_invalidated_writes_to_external += task->task_writes_counters_external.task_invalidated_writes; | |
789 | logical_metadata_writes_to_external += task->task_writes_counters_external.task_metadata_writes; | |
790 | ||
5ba3f43e | 791 | cpu_ptime += task_cpu_ptime(task); |
a39ff7e2 | 792 | task_update_cpu_time_qos_stats(task, cpu_time_eqos, cpu_time_rqos); |
cb323159 A |
793 | #if MONOTONIC |
794 | uint64_t counts[MT_CORE_NFIXED] = {}; | |
795 | (void)mt_fixed_task_counts(task, counts); | |
796 | cpu_cycles += counts[MT_CORE_CYCLES]; | |
797 | #if defined(MT_CORE_INSTRS) | |
798 | cpu_instructions += counts[MT_CORE_INSTRS]; | |
799 | #endif /* defined(MT_CORE_INSTRS) */ | |
800 | #endif /* MONOTONIC */ | |
5ba3f43e A |
801 | } |
802 | ||
803 | kr = ledger_get_balance(sum_ledger, task_ledgers.cpu_time_billed_to_me, (int64_t *)&cpu_time_billed_to_me); | |
804 | if (kr != KERN_SUCCESS || cpu_time_billed_to_me < 0) { | |
805 | cpu_time_billed_to_me = 0; | |
806 | } | |
807 | ||
808 | kr = ledger_get_balance(sum_ledger, task_ledgers.cpu_time_billed_to_others, (int64_t *)&cpu_time_billed_to_others); | |
809 | if (kr != KERN_SUCCESS || cpu_time_billed_to_others < 0) { | |
810 | cpu_time_billed_to_others = 0; | |
811 | } | |
812 | ||
813 | kr = ledger_get_balance(sum_ledger, task_ledgers.energy_billed_to_me, (int64_t *)&energy_billed_to_me); | |
814 | if (kr != KERN_SUCCESS || energy_billed_to_me < 0) { | |
815 | energy_billed_to_me = 0; | |
816 | } | |
817 | ||
818 | kr = ledger_get_balance(sum_ledger, task_ledgers.energy_billed_to_others, (int64_t *)&energy_billed_to_others); | |
819 | if (kr != KERN_SUCCESS || energy_billed_to_others < 0) { | |
820 | energy_billed_to_others = 0; | |
fe8ab488 A |
821 | } |
822 | ||
823 | /* collect information from the coalition itself */ | |
3e170ce0 A |
824 | cru_out->tasks_started = coal->r.task_count; |
825 | cru_out->tasks_exited = coal->r.dead_task_count; | |
fe8ab488 | 826 | |
3e170ce0 A |
827 | uint64_t time_nonempty = coal->r.time_nonempty; |
828 | uint64_t last_became_nonempty_time = coal->r.last_became_nonempty_time; | |
fe8ab488 A |
829 | |
830 | coalition_unlock(coal); | |
831 | ||
832 | /* Copy the totals out of sum_ledger */ | |
833 | kr = ledger_get_entries(sum_ledger, task_ledgers.cpu_time, | |
0a7de745 | 834 | &credit, &debit); |
fe8ab488 A |
835 | if (kr != KERN_SUCCESS) { |
836 | credit = 0; | |
837 | } | |
838 | cru_out->cpu_time = credit; | |
3e170ce0 A |
839 | cru_out->cpu_time_billed_to_me = (uint64_t)cpu_time_billed_to_me; |
840 | cru_out->cpu_time_billed_to_others = (uint64_t)cpu_time_billed_to_others; | |
5ba3f43e A |
841 | cru_out->energy_billed_to_me = (uint64_t)energy_billed_to_me; |
842 | cru_out->energy_billed_to_others = (uint64_t)energy_billed_to_others; | |
fe8ab488 A |
843 | |
844 | kr = ledger_get_entries(sum_ledger, task_ledgers.interrupt_wakeups, | |
0a7de745 | 845 | &credit, &debit); |
fe8ab488 A |
846 | if (kr != KERN_SUCCESS) { |
847 | credit = 0; | |
848 | } | |
849 | cru_out->interrupt_wakeups = credit; | |
850 | ||
851 | kr = ledger_get_entries(sum_ledger, task_ledgers.platform_idle_wakeups, | |
0a7de745 | 852 | &credit, &debit); |
fe8ab488 A |
853 | if (kr != KERN_SUCCESS) { |
854 | credit = 0; | |
855 | } | |
856 | cru_out->platform_idle_wakeups = credit; | |
857 | ||
858 | cru_out->bytesread = bytesread; | |
859 | cru_out->byteswritten = byteswritten; | |
860 | cru_out->gpu_time = gpu_time; | |
39037602 | 861 | cru_out->energy = energy; |
4bd07ac2 A |
862 | cru_out->logical_immediate_writes = logical_immediate_writes; |
863 | cru_out->logical_deferred_writes = logical_deferred_writes; | |
864 | cru_out->logical_invalidated_writes = logical_invalidated_writes; | |
865 | cru_out->logical_metadata_writes = logical_metadata_writes; | |
cb323159 A |
866 | cru_out->logical_immediate_writes_to_external = logical_immediate_writes_to_external; |
867 | cru_out->logical_deferred_writes_to_external = logical_deferred_writes_to_external; | |
868 | cru_out->logical_invalidated_writes_to_external = logical_invalidated_writes_to_external; | |
869 | cru_out->logical_metadata_writes_to_external = logical_metadata_writes_to_external; | |
5ba3f43e | 870 | cru_out->cpu_ptime = cpu_ptime; |
a39ff7e2 A |
871 | cru_out->cpu_time_eqos_len = COALITION_NUM_THREAD_QOS_TYPES; |
872 | memcpy(cru_out->cpu_time_eqos, cpu_time_eqos, sizeof(cru_out->cpu_time_eqos)); | |
cb323159 A |
873 | cru_out->cpu_cycles = cpu_cycles; |
874 | cru_out->cpu_instructions = cpu_instructions; | |
fe8ab488 A |
875 | ledger_dereference(sum_ledger); |
876 | sum_ledger = LEDGER_NULL; | |
877 | ||
878 | if (last_became_nonempty_time) { | |
879 | time_nonempty += mach_absolute_time() - last_became_nonempty_time; | |
880 | } | |
881 | absolutetime_to_nanoseconds(time_nonempty, &cru_out->time_nonempty); | |
882 | ||
883 | return KERN_SUCCESS; | |
884 | } | |
885 | ||
3e170ce0 A |
886 | /* |
887 | * | |
888 | * COALITION_TYPE_JETSAM | |
889 | * | |
890 | */ | |
891 | static kern_return_t | |
892 | i_coal_jetsam_init(coalition_t coal, boolean_t privileged) | |
893 | { | |
894 | assert(coal && coal->type == COALITION_TYPE_JETSAM); | |
895 | (void)privileged; | |
896 | ||
0a7de745 | 897 | coal->j.leader = TASK_NULL; |
3e170ce0 A |
898 | queue_head_init(coal->j.extensions); |
899 | queue_head_init(coal->j.services); | |
900 | queue_head_init(coal->j.other); | |
901 | ||
902 | return KERN_SUCCESS; | |
903 | } | |
904 | ||
905 | static void | |
906 | i_coal_jetsam_dealloc(__unused coalition_t coal) | |
907 | { | |
908 | assert(coal && coal->type == COALITION_TYPE_JETSAM); | |
909 | ||
910 | /* the coalition should be completely clear at this point */ | |
911 | assert(queue_empty(&coal->j.extensions)); | |
912 | assert(queue_empty(&coal->j.services)); | |
913 | assert(queue_empty(&coal->j.other)); | |
914 | assert(coal->j.leader == TASK_NULL); | |
5ba3f43e | 915 | |
3e170ce0 A |
916 | } |
917 | ||
918 | static kern_return_t | |
919 | i_coal_jetsam_adopt_task(coalition_t coal, task_t task) | |
920 | { | |
921 | struct i_jetsam_coalition *cj; | |
922 | assert(coal && coal->type == COALITION_TYPE_JETSAM); | |
923 | ||
924 | cj = &coal->j; | |
925 | ||
926 | assert(queue_empty(&task->task_coalition[COALITION_TYPE_JETSAM])); | |
927 | ||
928 | /* put each task initially in the "other" list */ | |
929 | enqueue_tail(&cj->other, &task->task_coalition[COALITION_TYPE_JETSAM]); | |
930 | coal_dbg("coalition %lld adopted PID:%d as UNDEF", | |
0a7de745 | 931 | coal->id, task_pid(task)); |
3e170ce0 A |
932 | |
933 | return KERN_SUCCESS; | |
934 | } | |
935 | ||
936 | static kern_return_t | |
937 | i_coal_jetsam_remove_task(coalition_t coal, task_t task) | |
938 | { | |
939 | assert(coal && coal->type == COALITION_TYPE_JETSAM); | |
940 | assert(task->coalition[COALITION_TYPE_JETSAM] == coal); | |
941 | ||
942 | coal_dbg("removing PID:%d from coalition id:%lld", | |
0a7de745 | 943 | task_pid(task), coal->id); |
3e170ce0 A |
944 | |
945 | if (task == coal->j.leader) { | |
946 | coal->j.leader = NULL; | |
947 | coal_dbg(" PID:%d was the leader!", task_pid(task)); | |
948 | } else { | |
949 | assert(!queue_empty(&task->task_coalition[COALITION_TYPE_JETSAM])); | |
950 | } | |
951 | ||
952 | /* remove the task from the specific coalition role queue */ | |
953 | remqueue(&task->task_coalition[COALITION_TYPE_JETSAM]); | |
954 | queue_chain_init(task->task_coalition[COALITION_TYPE_RESOURCE]); | |
955 | ||
956 | return KERN_SUCCESS; | |
957 | } | |
958 | ||
959 | static kern_return_t | |
960 | i_coal_jetsam_set_taskrole(coalition_t coal, task_t task, int role) | |
961 | { | |
962 | struct i_jetsam_coalition *cj; | |
963 | queue_t q = NULL; | |
964 | assert(coal && coal->type == COALITION_TYPE_JETSAM); | |
965 | assert(task->coalition[COALITION_TYPE_JETSAM] == coal); | |
966 | ||
967 | cj = &coal->j; | |
968 | ||
969 | switch (role) { | |
970 | case COALITION_TASKROLE_LEADER: | |
971 | coal_dbg("setting PID:%d as LEADER of %lld", | |
0a7de745 | 972 | task_pid(task), coal->id); |
3e170ce0 A |
973 | if (cj->leader != TASK_NULL) { |
974 | /* re-queue the exiting leader onto the "other" list */ | |
975 | coal_dbg(" re-queue existing leader (%d) as OTHER", | |
0a7de745 | 976 | task_pid(cj->leader)); |
3e170ce0 A |
977 | re_queue_tail(&cj->other, &cj->leader->task_coalition[COALITION_TYPE_JETSAM]); |
978 | } | |
979 | /* | |
980 | * remove the task from the "other" list | |
981 | * (where it was put by default) | |
982 | */ | |
983 | remqueue(&task->task_coalition[COALITION_TYPE_JETSAM]); | |
984 | queue_chain_init(task->task_coalition[COALITION_TYPE_JETSAM]); | |
985 | ||
986 | /* set the coalition leader */ | |
987 | cj->leader = task; | |
988 | break; | |
3e170ce0 A |
989 | case COALITION_TASKROLE_XPC: |
990 | coal_dbg("setting PID:%d as XPC in %lld", | |
0a7de745 | 991 | task_pid(task), coal->id); |
3e170ce0 A |
992 | q = (queue_t)&cj->services; |
993 | break; | |
994 | case COALITION_TASKROLE_EXT: | |
995 | coal_dbg("setting PID:%d as EXT in %lld", | |
0a7de745 | 996 | task_pid(task), coal->id); |
3e170ce0 A |
997 | q = (queue_t)&cj->extensions; |
998 | break; | |
5ba3f43e A |
999 | case COALITION_TASKROLE_NONE: |
1000 | /* | |
1001 | * Tasks with a role of "none" should fall through to an | |
1002 | * undefined role so long as the task is currently a member | |
1003 | * of the coalition. This scenario can happen if a task is | |
1004 | * killed (usually via jetsam) during exec. | |
1005 | */ | |
1006 | if (task->coalition[COALITION_TYPE_JETSAM] != coal) { | |
1007 | panic("%s: task %p attempting to set role %d " | |
0a7de745 | 1008 | "in coalition %p to which it does not belong!", __func__, task, role, coal); |
5ba3f43e | 1009 | } |
0a7de745 | 1010 | /* fall through */ |
5ba3f43e A |
1011 | case COALITION_TASKROLE_UNDEF: |
1012 | coal_dbg("setting PID:%d as UNDEF in %lld", | |
0a7de745 | 1013 | task_pid(task), coal->id); |
5ba3f43e A |
1014 | q = (queue_t)&cj->other; |
1015 | break; | |
3e170ce0 A |
1016 | default: |
1017 | panic("%s: invalid role(%d) for task", __func__, role); | |
1018 | return KERN_INVALID_ARGUMENT; | |
1019 | } | |
1020 | ||
0a7de745 | 1021 | if (q != NULL) { |
3e170ce0 | 1022 | re_queue_tail(q, &task->task_coalition[COALITION_TYPE_JETSAM]); |
0a7de745 | 1023 | } |
3e170ce0 A |
1024 | |
1025 | return KERN_SUCCESS; | |
1026 | } | |
1027 | ||
cb323159 | 1028 | int |
3e170ce0 A |
1029 | i_coal_jetsam_get_taskrole(coalition_t coal, task_t task) |
1030 | { | |
1031 | struct i_jetsam_coalition *cj; | |
1032 | task_t t; | |
1033 | ||
1034 | assert(coal && coal->type == COALITION_TYPE_JETSAM); | |
1035 | assert(task->coalition[COALITION_TYPE_JETSAM] == coal); | |
1036 | ||
1037 | cj = &coal->j; | |
1038 | ||
0a7de745 | 1039 | if (task == cj->leader) { |
3e170ce0 | 1040 | return COALITION_TASKROLE_LEADER; |
0a7de745 | 1041 | } |
3e170ce0 A |
1042 | |
1043 | qe_foreach_element(t, &cj->services, task_coalition[COALITION_TYPE_JETSAM]) { | |
0a7de745 | 1044 | if (t == task) { |
3e170ce0 | 1045 | return COALITION_TASKROLE_XPC; |
0a7de745 | 1046 | } |
3e170ce0 A |
1047 | } |
1048 | ||
1049 | qe_foreach_element(t, &cj->extensions, task_coalition[COALITION_TYPE_JETSAM]) { | |
0a7de745 | 1050 | if (t == task) { |
3e170ce0 | 1051 | return COALITION_TASKROLE_EXT; |
0a7de745 | 1052 | } |
3e170ce0 A |
1053 | } |
1054 | ||
1055 | qe_foreach_element(t, &cj->other, task_coalition[COALITION_TYPE_JETSAM]) { | |
0a7de745 | 1056 | if (t == task) { |
3e170ce0 | 1057 | return COALITION_TASKROLE_UNDEF; |
0a7de745 | 1058 | } |
3e170ce0 A |
1059 | } |
1060 | ||
1061 | /* task not in the coalition?! */ | |
5ba3f43e | 1062 | return COALITION_TASKROLE_NONE; |
3e170ce0 A |
1063 | } |
1064 | ||
1065 | static void | |
1066 | i_coal_jetsam_iterate_tasks(coalition_t coal, void *ctx, void (*callback)(coalition_t, void *, task_t)) | |
1067 | { | |
1068 | struct i_jetsam_coalition *cj; | |
1069 | task_t t; | |
1070 | ||
1071 | assert(coal && coal->type == COALITION_TYPE_JETSAM); | |
1072 | ||
1073 | cj = &coal->j; | |
1074 | ||
0a7de745 | 1075 | if (cj->leader) { |
3e170ce0 | 1076 | callback(coal, ctx, cj->leader); |
0a7de745 | 1077 | } |
3e170ce0 A |
1078 | |
1079 | qe_foreach_element(t, &cj->services, task_coalition[COALITION_TYPE_JETSAM]) | |
0a7de745 | 1080 | callback(coal, ctx, t); |
3e170ce0 A |
1081 | |
1082 | qe_foreach_element(t, &cj->extensions, task_coalition[COALITION_TYPE_JETSAM]) | |
0a7de745 | 1083 | callback(coal, ctx, t); |
3e170ce0 A |
1084 | |
1085 | qe_foreach_element(t, &cj->other, task_coalition[COALITION_TYPE_JETSAM]) | |
0a7de745 | 1086 | callback(coal, ctx, t); |
3e170ce0 A |
1087 | } |
1088 | ||
1089 | ||
1090 | /* | |
1091 | * | |
1092 | * Main Coalition implementation | |
1093 | * | |
1094 | */ | |
1095 | ||
fe8ab488 A |
1096 | /* |
1097 | * coalition_create_internal | |
1098 | * Returns: New coalition object, referenced for the caller and unlocked. | |
1099 | * Condition: coalitions_list_lock must be UNLOCKED. | |
1100 | */ | |
1101 | kern_return_t | |
5ba3f43e | 1102 | coalition_create_internal(int type, int role, boolean_t privileged, coalition_t *out) |
fe8ab488 | 1103 | { |
3e170ce0 A |
1104 | kern_return_t kr; |
1105 | struct coalition *new_coal; | |
1106 | ||
0a7de745 | 1107 | if (type < 0 || type > COALITION_TYPE_MAX) { |
3e170ce0 | 1108 | return KERN_INVALID_ARGUMENT; |
0a7de745 | 1109 | } |
3e170ce0 A |
1110 | |
1111 | new_coal = (struct coalition *)zalloc(coalition_zone); | |
0a7de745 | 1112 | if (new_coal == COALITION_NULL) { |
fe8ab488 | 1113 | return KERN_RESOURCE_SHORTAGE; |
0a7de745 | 1114 | } |
fe8ab488 A |
1115 | bzero(new_coal, sizeof(*new_coal)); |
1116 | ||
3e170ce0 | 1117 | new_coal->type = type; |
5ba3f43e | 1118 | new_coal->role = role; |
3e170ce0 A |
1119 | |
1120 | /* initialize type-specific resources */ | |
1121 | kr = coal_call(new_coal, init, privileged); | |
1122 | if (kr != KERN_SUCCESS) { | |
fe8ab488 | 1123 | zfree(coalition_zone, new_coal); |
3e170ce0 | 1124 | return kr; |
fe8ab488 A |
1125 | } |
1126 | ||
1127 | /* One for caller, one for coalitions list */ | |
1128 | new_coal->ref_count = 2; | |
1129 | ||
1130 | new_coal->privileged = privileged ? TRUE : FALSE; | |
5ba3f43e | 1131 | #if DEVELOPMENT || DEBUG |
3e170ce0 A |
1132 | new_coal->should_notify = 1; |
1133 | #endif | |
fe8ab488 A |
1134 | |
1135 | lck_mtx_init(&new_coal->lock, &coalitions_lck_grp, &coalitions_lck_attr); | |
fe8ab488 A |
1136 | |
1137 | lck_mtx_lock(&coalitions_list_lock); | |
1138 | new_coal->id = coalition_next_id++; | |
1139 | coalition_count++; | |
3e170ce0 | 1140 | enqueue_tail(&coalitions_q, &new_coal->coalitions); |
5ba3f43e A |
1141 | |
1142 | KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_COALITION, MACH_COALITION_NEW), | |
0a7de745 | 1143 | new_coal->id, new_coal->type); |
fe8ab488 A |
1144 | lck_mtx_unlock(&coalitions_list_lock); |
1145 | ||
3e170ce0 | 1146 | coal_dbg("id:%llu, type:%s", new_coal->id, coal_type_str(new_coal->type)); |
fe8ab488 A |
1147 | |
1148 | *out = new_coal; | |
1149 | return KERN_SUCCESS; | |
1150 | } | |
1151 | ||
1152 | /* | |
1153 | * coalition_release | |
1154 | * Condition: coalition must be UNLOCKED. | |
1155 | * */ | |
1156 | void | |
1157 | coalition_release(coalition_t coal) | |
1158 | { | |
fe8ab488 A |
1159 | /* TODO: This can be done with atomics. */ |
1160 | coalition_lock(coal); | |
1161 | coal->ref_count--; | |
3e170ce0 | 1162 | |
fe8ab488 A |
1163 | #if COALITION_DEBUG |
1164 | uint32_t rc = coal->ref_count; | |
3e170ce0 | 1165 | uint32_t ac = coal->active_count; |
fe8ab488 A |
1166 | #endif /* COALITION_DEBUG */ |
1167 | ||
3e170ce0 | 1168 | coal_dbg("id:%llu type:%s ref_count:%u active_count:%u%s", |
0a7de745 A |
1169 | coal->id, coal_type_str(coal->type), rc, ac, |
1170 | rc <= 0 ? ", will deallocate now" : ""); | |
3e170ce0 A |
1171 | |
1172 | if (coal->ref_count > 0) { | |
1173 | coalition_unlock(coal); | |
1174 | return; | |
1175 | } | |
1176 | ||
1177 | assert(coal->termrequested); | |
1178 | assert(coal->terminated); | |
1179 | assert(coal->active_count == 0); | |
1180 | assert(coal->reaped); | |
1181 | assert(coal->focal_task_count == 0); | |
1182 | assert(coal->nonfocal_task_count == 0); | |
5ba3f43e | 1183 | KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_COALITION, MACH_COALITION_FREE), |
0a7de745 | 1184 | coal->id, coal->type); |
3e170ce0 A |
1185 | |
1186 | coal_call(coal, dealloc); | |
1187 | ||
fe8ab488 A |
1188 | coalition_unlock(coal); |
1189 | ||
3e170ce0 | 1190 | lck_mtx_destroy(&coal->lock, &coalitions_lck_grp); |
fe8ab488 | 1191 | |
3e170ce0 A |
1192 | zfree(coalition_zone, coal); |
1193 | } | |
fe8ab488 | 1194 | |
3e170ce0 A |
1195 | /* |
1196 | * coalition_find_by_id_internal | |
1197 | * Returns: Coalition object with specified id, NOT referenced. | |
1198 | * If not found, returns COALITION_NULL. | |
1199 | * Condition: coalitions_list_lock must be LOCKED. | |
1200 | */ | |
1201 | static coalition_t | |
1202 | coalition_find_by_id_internal(uint64_t coal_id) | |
1203 | { | |
1204 | if (coal_id == 0) { | |
1205 | return COALITION_NULL; | |
fe8ab488 | 1206 | } |
3e170ce0 A |
1207 | |
1208 | lck_mtx_assert(&coalitions_list_lock, LCK_MTX_ASSERT_OWNED); | |
1209 | coalition_t coal; | |
1210 | qe_foreach_element(coal, &coalitions_q, coalitions) { | |
1211 | if (coal->id == coal_id) { | |
1212 | return coal; | |
1213 | } | |
1214 | } | |
1215 | return COALITION_NULL; | |
fe8ab488 A |
1216 | } |
1217 | ||
1218 | /* | |
1219 | * coalition_find_by_id | |
1220 | * Returns: Coalition object with specified id, referenced. | |
1221 | * Condition: coalitions_list_lock must be UNLOCKED. | |
1222 | */ | |
1223 | coalition_t | |
1224 | coalition_find_by_id(uint64_t cid) | |
1225 | { | |
1226 | if (cid == 0) { | |
1227 | return COALITION_NULL; | |
1228 | } | |
1229 | ||
1230 | lck_mtx_lock(&coalitions_list_lock); | |
1231 | ||
1232 | coalition_t coal = coalition_find_by_id_internal(cid); | |
1233 | if (coal == COALITION_NULL) { | |
1234 | lck_mtx_unlock(&coalitions_list_lock); | |
1235 | return COALITION_NULL; | |
1236 | } | |
1237 | ||
1238 | coalition_lock(coal); | |
1239 | ||
1240 | if (coal->reaped) { | |
1241 | coalition_unlock(coal); | |
1242 | lck_mtx_unlock(&coalitions_list_lock); | |
1243 | return COALITION_NULL; | |
1244 | } | |
1245 | ||
1246 | if (coal->ref_count == 0) { | |
3e170ce0 | 1247 | panic("resurrecting coalition %p id:%llu type:%s, active_count:%u\n", |
0a7de745 | 1248 | coal, coal->id, coal_type_str(coal->type), coal->active_count); |
fe8ab488 A |
1249 | } |
1250 | coal->ref_count++; | |
1251 | #if COALITION_DEBUG | |
1252 | uint32_t rc = coal->ref_count; | |
1253 | #endif | |
1254 | ||
1255 | coalition_unlock(coal); | |
1256 | lck_mtx_unlock(&coalitions_list_lock); | |
1257 | ||
3e170ce0 | 1258 | coal_dbg("id:%llu type:%s ref_count:%u", |
0a7de745 | 1259 | coal->id, coal_type_str(coal->type), rc); |
3e170ce0 | 1260 | |
fe8ab488 A |
1261 | return coal; |
1262 | } | |
1263 | ||
1264 | /* | |
1265 | * coalition_find_and_activate_by_id | |
1266 | * Returns: Coalition object with specified id, referenced, and activated. | |
1267 | * Condition: coalitions_list_lock must be UNLOCKED. | |
1268 | * This is the function to use when putting a 'new' thing into a coalition, | |
1269 | * like posix_spawn of an XPC service by launchd. | |
1270 | * See also coalition_extend_active. | |
1271 | */ | |
1272 | coalition_t | |
1273 | coalition_find_and_activate_by_id(uint64_t cid) | |
1274 | { | |
1275 | if (cid == 0) { | |
1276 | return COALITION_NULL; | |
1277 | } | |
1278 | ||
1279 | lck_mtx_lock(&coalitions_list_lock); | |
1280 | ||
1281 | coalition_t coal = coalition_find_by_id_internal(cid); | |
1282 | if (coal == COALITION_NULL) { | |
1283 | lck_mtx_unlock(&coalitions_list_lock); | |
1284 | return COALITION_NULL; | |
1285 | } | |
1286 | ||
1287 | coalition_lock(coal); | |
1288 | ||
1289 | if (coal->reaped || coal->terminated) { | |
1290 | /* Too late to put something new into this coalition, it's | |
1291 | * already on its way out the door */ | |
1292 | coalition_unlock(coal); | |
1293 | lck_mtx_unlock(&coalitions_list_lock); | |
1294 | return COALITION_NULL; | |
1295 | } | |
1296 | ||
1297 | if (coal->ref_count == 0) { | |
3e170ce0 | 1298 | panic("resurrecting coalition %p id:%llu type:%s, active_count:%u\n", |
0a7de745 | 1299 | coal, coal->id, coal_type_str(coal->type), coal->active_count); |
fe8ab488 A |
1300 | } |
1301 | ||
1302 | coal->ref_count++; | |
1303 | coal->active_count++; | |
1304 | ||
1305 | #if COALITION_DEBUG | |
1306 | uint32_t rc = coal->ref_count; | |
1307 | uint32_t ac = coal->active_count; | |
1308 | #endif | |
1309 | ||
1310 | coalition_unlock(coal); | |
1311 | lck_mtx_unlock(&coalitions_list_lock); | |
1312 | ||
3e170ce0 | 1313 | coal_dbg("id:%llu type:%s ref_count:%u, active_count:%u", |
0a7de745 | 1314 | coal->id, coal_type_str(coal->type), rc, ac); |
3e170ce0 | 1315 | |
fe8ab488 A |
1316 | return coal; |
1317 | } | |
1318 | ||
1319 | uint64_t | |
1320 | coalition_id(coalition_t coal) | |
1321 | { | |
1322 | return coal->id; | |
1323 | } | |
1324 | ||
3e170ce0 A |
1325 | void |
1326 | task_coalition_ids(task_t task, uint64_t ids[COALITION_NUM_TYPES]) | |
1327 | { | |
1328 | int i; | |
1329 | for (i = 0; i < COALITION_NUM_TYPES; i++) { | |
0a7de745 | 1330 | if (task->coalition[i]) { |
3e170ce0 | 1331 | ids[i] = task->coalition[i]->id; |
0a7de745 | 1332 | } else { |
3e170ce0 | 1333 | ids[i] = 0; |
0a7de745 | 1334 | } |
3e170ce0 A |
1335 | } |
1336 | } | |
1337 | ||
1338 | void | |
1339 | task_coalition_roles(task_t task, int roles[COALITION_NUM_TYPES]) | |
fe8ab488 | 1340 | { |
3e170ce0 A |
1341 | int i; |
1342 | memset(roles, 0, COALITION_NUM_TYPES * sizeof(roles[0])); | |
1343 | ||
1344 | for (i = 0; i < COALITION_NUM_TYPES; i++) { | |
1345 | if (task->coalition[i]) { | |
1346 | coalition_lock(task->coalition[i]); | |
1347 | roles[i] = coal_call(task->coalition[i], | |
0a7de745 | 1348 | get_taskrole, task); |
3e170ce0 A |
1349 | coalition_unlock(task->coalition[i]); |
1350 | } else { | |
5ba3f43e | 1351 | roles[i] = COALITION_TASKROLE_NONE; |
3e170ce0 A |
1352 | } |
1353 | } | |
1354 | } | |
1355 | ||
1356 | ||
1357 | int | |
1358 | coalition_type(coalition_t coal) | |
1359 | { | |
1360 | return coal->type; | |
fe8ab488 A |
1361 | } |
1362 | ||
5ba3f43e A |
1363 | boolean_t |
1364 | coalition_term_requested(coalition_t coal) | |
1365 | { | |
1366 | return coal->termrequested; | |
1367 | } | |
1368 | ||
1369 | boolean_t | |
1370 | coalition_is_terminated(coalition_t coal) | |
1371 | { | |
1372 | return coal->terminated; | |
1373 | } | |
1374 | ||
1375 | boolean_t | |
1376 | coalition_is_reaped(coalition_t coal) | |
1377 | { | |
1378 | return coal->reaped; | |
1379 | } | |
1380 | ||
fe8ab488 A |
1381 | boolean_t |
1382 | coalition_is_privileged(coalition_t coal) | |
1383 | { | |
1384 | return coal->privileged || unrestrict_coalition_syscalls; | |
1385 | } | |
1386 | ||
1387 | boolean_t | |
3e170ce0 | 1388 | task_is_in_privileged_coalition(task_t task, int type) |
fe8ab488 | 1389 | { |
0a7de745 | 1390 | if (type < 0 || type > COALITION_TYPE_MAX) { |
3e170ce0 | 1391 | return FALSE; |
0a7de745 A |
1392 | } |
1393 | if (unrestrict_coalition_syscalls) { | |
3e170ce0 | 1394 | return TRUE; |
0a7de745 A |
1395 | } |
1396 | if (!task->coalition[type]) { | |
3e170ce0 | 1397 | return FALSE; |
0a7de745 | 1398 | } |
3e170ce0 | 1399 | return task->coalition[type]->privileged; |
fe8ab488 A |
1400 | } |
1401 | ||
0a7de745 A |
1402 | void |
1403 | task_coalition_update_gpu_stats(task_t task, uint64_t gpu_ns_delta) | |
fe8ab488 | 1404 | { |
3e170ce0 A |
1405 | coalition_t coal; |
1406 | ||
1407 | assert(task != TASK_NULL); | |
0a7de745 | 1408 | if (gpu_ns_delta == 0) { |
3e170ce0 | 1409 | return; |
0a7de745 | 1410 | } |
3e170ce0 A |
1411 | |
1412 | coal = task->coalition[COALITION_TYPE_RESOURCE]; | |
1413 | assert(coal != COALITION_NULL); | |
1414 | ||
1415 | coalition_lock(coal); | |
1416 | coal->r.gpu_time += gpu_ns_delta; | |
1417 | coalition_unlock(coal); | |
fe8ab488 A |
1418 | } |
1419 | ||
0a7de745 A |
1420 | boolean_t |
1421 | task_coalition_adjust_focal_count(task_t task, int count, uint32_t *new_count) | |
fe8ab488 | 1422 | { |
5ba3f43e | 1423 | coalition_t coal = task->coalition[COALITION_FOCAL_TASKS_ACCOUNTING]; |
0a7de745 A |
1424 | if (coal == COALITION_NULL) { |
1425 | return FALSE; | |
1426 | } | |
fe8ab488 | 1427 | |
cb323159 | 1428 | *new_count = os_atomic_add(&coal->focal_task_count, count, relaxed); |
5ba3f43e A |
1429 | assert(*new_count != UINT32_MAX); |
1430 | return TRUE; | |
3e170ce0 A |
1431 | } |
1432 | ||
0a7de745 A |
1433 | uint32_t |
1434 | task_coalition_focal_count(task_t task) | |
3e170ce0 | 1435 | { |
5ba3f43e | 1436 | coalition_t coal = task->coalition[COALITION_FOCAL_TASKS_ACCOUNTING]; |
0a7de745 A |
1437 | if (coal == COALITION_NULL) { |
1438 | return 0; | |
1439 | } | |
3e170ce0 A |
1440 | |
1441 | return coal->focal_task_count; | |
1442 | } | |
1443 | ||
0a7de745 A |
1444 | boolean_t |
1445 | task_coalition_adjust_nonfocal_count(task_t task, int count, uint32_t *new_count) | |
3e170ce0 | 1446 | { |
5ba3f43e | 1447 | coalition_t coal = task->coalition[COALITION_FOCAL_TASKS_ACCOUNTING]; |
0a7de745 A |
1448 | if (coal == COALITION_NULL) { |
1449 | return FALSE; | |
1450 | } | |
3e170ce0 | 1451 | |
cb323159 | 1452 | *new_count = os_atomic_add(&coal->nonfocal_task_count, count, relaxed); |
5ba3f43e A |
1453 | assert(*new_count != UINT32_MAX); |
1454 | return TRUE; | |
3e170ce0 A |
1455 | } |
1456 | ||
0a7de745 A |
1457 | uint32_t |
1458 | task_coalition_nonfocal_count(task_t task) | |
3e170ce0 | 1459 | { |
5ba3f43e | 1460 | coalition_t coal = task->coalition[COALITION_FOCAL_TASKS_ACCOUNTING]; |
0a7de745 A |
1461 | if (coal == COALITION_NULL) { |
1462 | return 0; | |
1463 | } | |
3e170ce0 A |
1464 | |
1465 | return coal->nonfocal_task_count; | |
1466 | } | |
1467 | ||
0a7de745 A |
1468 | void |
1469 | coalition_set_efficient(coalition_t coal) | |
5ba3f43e | 1470 | { |
0a7de745 A |
1471 | coalition_lock(coal); |
1472 | coal->efficient = TRUE; | |
1473 | coalition_unlock(coal); | |
5ba3f43e A |
1474 | } |
1475 | ||
1476 | ||
0a7de745 A |
1477 | void |
1478 | coalition_for_each_task(coalition_t coal, void *ctx, | |
1479 | void (*callback)(coalition_t, void *, task_t)) | |
3e170ce0 A |
1480 | { |
1481 | assert(coal != COALITION_NULL); | |
1482 | ||
1483 | coal_dbg("iterating tasks in coalition %p id:%llu type:%s, active_count:%u", | |
0a7de745 | 1484 | coal, coal->id, coal_type_str(coal->type), coal->active_count); |
3e170ce0 A |
1485 | |
1486 | coalition_lock(coal); | |
1487 | ||
1488 | coal_call(coal, iterate_tasks, ctx, callback); | |
fe8ab488 A |
1489 | |
1490 | coalition_unlock(coal); | |
fe8ab488 A |
1491 | } |
1492 | ||
3e170ce0 | 1493 | |
fe8ab488 A |
1494 | void |
1495 | coalition_remove_active(coalition_t coal) | |
1496 | { | |
1497 | coalition_lock(coal); | |
1498 | ||
1499 | assert(!coal->reaped); | |
1500 | assert(coal->active_count > 0); | |
1501 | ||
1502 | coal->active_count--; | |
1503 | ||
1504 | boolean_t do_notify = FALSE; | |
1505 | uint64_t notify_id = 0; | |
1506 | uint32_t notify_flags = 0; | |
1507 | if (coal->termrequested && coal->active_count == 0) { | |
1508 | /* We only notify once, when active_count reaches zero. | |
1509 | * We just decremented, so if it reached zero, we mustn't have | |
1510 | * notified already. | |
1511 | */ | |
1512 | assert(!coal->terminated); | |
1513 | coal->terminated = TRUE; | |
1514 | ||
1515 | assert(!coal->notified); | |
1516 | ||
1517 | coal->notified = TRUE; | |
5ba3f43e | 1518 | #if DEVELOPMENT || DEBUG |
3e170ce0 A |
1519 | do_notify = coal->should_notify; |
1520 | #else | |
fe8ab488 | 1521 | do_notify = TRUE; |
3e170ce0 | 1522 | #endif |
fe8ab488 A |
1523 | notify_id = coal->id; |
1524 | notify_flags = 0; | |
1525 | } | |
1526 | ||
3e170ce0 A |
1527 | #if COALITION_DEBUG |
1528 | uint64_t cid = coal->id; | |
1529 | uint32_t rc = coal->ref_count; | |
1530 | int ac = coal->active_count; | |
1531 | int ct = coal->type; | |
1532 | #endif | |
fe8ab488 A |
1533 | coalition_unlock(coal); |
1534 | ||
3e170ce0 | 1535 | coal_dbg("id:%llu type:%s ref_count:%u, active_count:%u,%s", |
0a7de745 | 1536 | cid, coal_type_str(ct), rc, ac, do_notify ? " NOTIFY" : " "); |
3e170ce0 | 1537 | |
fe8ab488 A |
1538 | if (do_notify) { |
1539 | coalition_notify_user(notify_id, notify_flags); | |
1540 | } | |
1541 | } | |
1542 | ||
1543 | /* Used for kernel_task, launchd, launchd's early boot tasks... */ | |
1544 | kern_return_t | |
3e170ce0 | 1545 | coalitions_adopt_init_task(task_t task) |
fe8ab488 A |
1546 | { |
1547 | kern_return_t kr; | |
3e170ce0 | 1548 | kr = coalitions_adopt_task(init_coalition, task); |
fe8ab488 A |
1549 | if (kr != KERN_SUCCESS) { |
1550 | panic("failed to adopt task %p into default coalition: %d", task, kr); | |
1551 | } | |
1552 | return kr; | |
1553 | } | |
1554 | ||
39037602 A |
1555 | /* Used for forked corpses. */ |
1556 | kern_return_t | |
1557 | coalitions_adopt_corpse_task(task_t task) | |
1558 | { | |
1559 | kern_return_t kr; | |
1560 | kr = coalitions_adopt_task(corpse_coalition, task); | |
1561 | if (kr != KERN_SUCCESS) { | |
1562 | panic("failed to adopt task %p into corpse coalition: %d", task, kr); | |
1563 | } | |
1564 | return kr; | |
1565 | } | |
1566 | ||
fe8ab488 | 1567 | /* |
3e170ce0 | 1568 | * coalition_adopt_task_internal |
fe8ab488 A |
1569 | * Condition: Coalition must be referenced and unlocked. Will fail if coalition |
1570 | * is already terminated. | |
1571 | */ | |
3e170ce0 A |
1572 | static kern_return_t |
1573 | coalition_adopt_task_internal(coalition_t coal, task_t task) | |
fe8ab488 | 1574 | { |
3e170ce0 A |
1575 | kern_return_t kr; |
1576 | ||
1577 | if (task->coalition[coal->type]) { | |
fe8ab488 A |
1578 | return KERN_ALREADY_IN_SET; |
1579 | } | |
1580 | ||
1581 | coalition_lock(coal); | |
1582 | ||
1583 | if (coal->reaped || coal->terminated) { | |
1584 | coalition_unlock(coal); | |
1585 | return KERN_TERMINATED; | |
1586 | } | |
1587 | ||
3e170ce0 | 1588 | kr = coal_call(coal, adopt_task, task); |
0a7de745 | 1589 | if (kr != KERN_SUCCESS) { |
3e170ce0 | 1590 | goto out_unlock; |
0a7de745 | 1591 | } |
3e170ce0 | 1592 | |
fe8ab488 A |
1593 | coal->active_count++; |
1594 | ||
1595 | coal->ref_count++; | |
fe8ab488 | 1596 | |
3e170ce0 | 1597 | task->coalition[coal->type] = coal; |
fe8ab488 | 1598 | |
3e170ce0 | 1599 | out_unlock: |
fe8ab488 | 1600 | #if COALITION_DEBUG |
3e170ce0 A |
1601 | (void)coal; /* need expression after label */ |
1602 | uint64_t cid = coal->id; | |
fe8ab488 | 1603 | uint32_t rc = coal->ref_count; |
3e170ce0 | 1604 | uint32_t ct = coal->type; |
fe8ab488 | 1605 | #endif |
5ba3f43e A |
1606 | if (get_task_uniqueid(task) != UINT64_MAX) { |
1607 | /* On 32-bit targets, uniqueid will get truncated to 32 bits */ | |
1608 | KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_COALITION, MACH_COALITION_ADOPT), | |
0a7de745 | 1609 | coal->id, get_task_uniqueid(task)); |
5ba3f43e A |
1610 | } |
1611 | ||
fe8ab488 A |
1612 | coalition_unlock(coal); |
1613 | ||
3e170ce0 | 1614 | coal_dbg("task:%d, id:%llu type:%s ref_count:%u, kr=%d", |
0a7de745 | 1615 | task_pid(task), cid, coal_type_str(ct), rc, kr); |
3e170ce0 A |
1616 | return kr; |
1617 | } | |
1618 | ||
1619 | static kern_return_t | |
1620 | coalition_remove_task_internal(task_t task, int type) | |
1621 | { | |
1622 | kern_return_t kr; | |
1623 | ||
1624 | coalition_t coal = task->coalition[type]; | |
1625 | ||
0a7de745 | 1626 | if (!coal) { |
3e170ce0 | 1627 | return KERN_SUCCESS; |
0a7de745 | 1628 | } |
3e170ce0 A |
1629 | |
1630 | assert(coal->type == (uint32_t)type); | |
1631 | ||
1632 | coalition_lock(coal); | |
1633 | ||
1634 | kr = coal_call(coal, remove_task, task); | |
1635 | ||
fe8ab488 | 1636 | #if COALITION_DEBUG |
3e170ce0 A |
1637 | uint64_t cid = coal->id; |
1638 | uint32_t rc = coal->ref_count; | |
1639 | int ac = coal->active_count; | |
1640 | int ct = coal->type; | |
fe8ab488 | 1641 | #endif |
5ba3f43e | 1642 | KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_COALITION, MACH_COALITION_REMOVE), |
0a7de745 | 1643 | coal->id, get_task_uniqueid(task)); |
3e170ce0 A |
1644 | coalition_unlock(coal); |
1645 | ||
1646 | coal_dbg("id:%llu type:%s ref_count:%u, active_count:%u, kr=%d", | |
0a7de745 | 1647 | cid, coal_type_str(ct), rc, ac, kr); |
3e170ce0 A |
1648 | |
1649 | coalition_remove_active(coal); | |
1650 | ||
1651 | return kr; | |
fe8ab488 A |
1652 | } |
1653 | ||
1654 | /* | |
3e170ce0 A |
1655 | * coalitions_adopt_task |
1656 | * Condition: All coalitions must be referenced and unlocked. | |
1657 | * Will fail if any coalition is already terminated. | |
fe8ab488 A |
1658 | */ |
1659 | kern_return_t | |
3e170ce0 | 1660 | coalitions_adopt_task(coalition_t *coals, task_t task) |
fe8ab488 | 1661 | { |
3e170ce0 A |
1662 | int i; |
1663 | kern_return_t kr; | |
fe8ab488 | 1664 | |
0a7de745 | 1665 | if (!coals || coals[COALITION_TYPE_RESOURCE] == COALITION_NULL) { |
3e170ce0 | 1666 | return KERN_INVALID_ARGUMENT; |
0a7de745 | 1667 | } |
3e170ce0 A |
1668 | |
1669 | /* verify that the incoming coalitions are what they say they are */ | |
0a7de745 A |
1670 | for (i = 0; i < COALITION_NUM_TYPES; i++) { |
1671 | if (coals[i] && coals[i]->type != (uint32_t)i) { | |
3e170ce0 | 1672 | return KERN_INVALID_ARGUMENT; |
0a7de745 A |
1673 | } |
1674 | } | |
3e170ce0 A |
1675 | |
1676 | for (i = 0; i < COALITION_NUM_TYPES; i++) { | |
1677 | kr = KERN_SUCCESS; | |
0a7de745 | 1678 | if (coals[i]) { |
3e170ce0 | 1679 | kr = coalition_adopt_task_internal(coals[i], task); |
0a7de745 | 1680 | } |
3e170ce0 A |
1681 | if (kr != KERN_SUCCESS) { |
1682 | /* dis-associate any coalitions that just adopted this task */ | |
1683 | while (--i >= 0) { | |
0a7de745 | 1684 | if (task->coalition[i]) { |
3e170ce0 | 1685 | coalition_remove_task_internal(task, i); |
0a7de745 | 1686 | } |
3e170ce0 A |
1687 | } |
1688 | break; | |
1689 | } | |
1690 | } | |
1691 | return kr; | |
1692 | } | |
fe8ab488 | 1693 | |
3e170ce0 A |
1694 | /* |
1695 | * coalitions_remove_task | |
1696 | * Condition: task must be referenced and UNLOCKED; all task's coalitions must be UNLOCKED | |
1697 | */ | |
1698 | kern_return_t | |
1699 | coalitions_remove_task(task_t task) | |
1700 | { | |
1701 | kern_return_t kr; | |
1702 | int i; | |
fe8ab488 | 1703 | |
3e170ce0 A |
1704 | for (i = 0; i < COALITION_NUM_TYPES; i++) { |
1705 | kr = coalition_remove_task_internal(task, i); | |
1706 | assert(kr == KERN_SUCCESS); | |
fe8ab488 A |
1707 | } |
1708 | ||
3e170ce0 A |
1709 | return kr; |
1710 | } | |
1711 | ||
1712 | /* | |
1713 | * task_release_coalitions | |
1714 | * helper function to release references to all coalitions in which | |
1715 | * 'task' is a member. | |
1716 | */ | |
1717 | void | |
1718 | task_release_coalitions(task_t task) | |
1719 | { | |
1720 | int i; | |
1721 | for (i = 0; i < COALITION_NUM_TYPES; i++) { | |
39037602 | 1722 | if (task->coalition[i]) { |
3e170ce0 | 1723 | coalition_release(task->coalition[i]); |
39037602 A |
1724 | } else if (i == COALITION_TYPE_RESOURCE) { |
1725 | panic("deallocating task %p was not a member of a resource coalition", task); | |
1726 | } | |
fe8ab488 | 1727 | } |
3e170ce0 | 1728 | } |
fe8ab488 | 1729 | |
3e170ce0 A |
1730 | /* |
1731 | * coalitions_set_roles | |
1732 | * for each type of coalition, if the task is a member of a coalition of | |
1733 | * that type (given in the coalitions parameter) then set the role of | |
1734 | * the task within that that coalition. | |
1735 | */ | |
0a7de745 A |
1736 | kern_return_t |
1737 | coalitions_set_roles(coalition_t coalitions[COALITION_NUM_TYPES], | |
1738 | task_t task, int roles[COALITION_NUM_TYPES]) | |
3e170ce0 A |
1739 | { |
1740 | kern_return_t kr = KERN_SUCCESS; | |
1741 | int i; | |
fe8ab488 | 1742 | |
3e170ce0 | 1743 | for (i = 0; i < COALITION_NUM_TYPES; i++) { |
0a7de745 | 1744 | if (!coalitions[i]) { |
3e170ce0 | 1745 | continue; |
0a7de745 | 1746 | } |
3e170ce0 A |
1747 | coalition_lock(coalitions[i]); |
1748 | kr = coal_call(coalitions[i], set_taskrole, task, roles[i]); | |
1749 | coalition_unlock(coalitions[i]); | |
1750 | assert(kr == KERN_SUCCESS); | |
1751 | } | |
fe8ab488 | 1752 | |
3e170ce0 | 1753 | return kr; |
fe8ab488 A |
1754 | } |
1755 | ||
1756 | /* | |
1757 | * coalition_terminate_internal | |
1758 | * Condition: Coalition must be referenced and UNLOCKED. | |
1759 | */ | |
1760 | kern_return_t | |
1761 | coalition_request_terminate_internal(coalition_t coal) | |
1762 | { | |
3e170ce0 A |
1763 | assert(coal->type >= 0 && coal->type <= COALITION_TYPE_MAX); |
1764 | ||
1765 | if (coal == init_coalition[coal->type]) { | |
fe8ab488 A |
1766 | return KERN_DEFAULT_SET; |
1767 | } | |
1768 | ||
1769 | coalition_lock(coal); | |
1770 | ||
1771 | if (coal->reaped) { | |
1772 | coalition_unlock(coal); | |
1773 | return KERN_INVALID_NAME; | |
1774 | } | |
1775 | ||
1776 | if (coal->terminated || coal->termrequested) { | |
1777 | coalition_unlock(coal); | |
1778 | return KERN_TERMINATED; | |
1779 | } | |
1780 | ||
1781 | coal->termrequested = TRUE; | |
1782 | ||
1783 | boolean_t do_notify = FALSE; | |
1784 | uint64_t note_id = 0; | |
1785 | uint32_t note_flags = 0; | |
1786 | ||
1787 | if (coal->active_count == 0) { | |
1788 | /* | |
1789 | * We only notify once, when active_count reaches zero. | |
3e170ce0 A |
1790 | * We just set termrequested to zero. If the active count |
1791 | * was already at zero (tasks died before we could request | |
1792 | * a termination notification), we should notify. | |
fe8ab488 A |
1793 | */ |
1794 | assert(!coal->terminated); | |
1795 | coal->terminated = TRUE; | |
1796 | ||
1797 | assert(!coal->notified); | |
1798 | ||
1799 | coal->notified = TRUE; | |
5ba3f43e | 1800 | #if DEVELOPMENT || DEBUG |
3e170ce0 A |
1801 | do_notify = coal->should_notify; |
1802 | #else | |
fe8ab488 | 1803 | do_notify = TRUE; |
3e170ce0 | 1804 | #endif |
fe8ab488 A |
1805 | note_id = coal->id; |
1806 | note_flags = 0; | |
1807 | } | |
1808 | ||
1809 | coalition_unlock(coal); | |
1810 | ||
1811 | if (do_notify) { | |
1812 | coalition_notify_user(note_id, note_flags); | |
1813 | } | |
1814 | ||
1815 | return KERN_SUCCESS; | |
1816 | } | |
1817 | ||
1818 | /* | |
1819 | * coalition_reap_internal | |
1820 | * Condition: Coalition must be referenced and UNLOCKED. | |
1821 | */ | |
1822 | kern_return_t | |
1823 | coalition_reap_internal(coalition_t coal) | |
1824 | { | |
3e170ce0 A |
1825 | assert(coal->type <= COALITION_TYPE_MAX); |
1826 | ||
1827 | if (coal == init_coalition[coal->type]) { | |
fe8ab488 A |
1828 | return KERN_DEFAULT_SET; |
1829 | } | |
1830 | ||
1831 | coalition_lock(coal); | |
1832 | if (coal->reaped) { | |
1833 | coalition_unlock(coal); | |
1834 | return KERN_TERMINATED; | |
1835 | } | |
1836 | if (!coal->terminated) { | |
1837 | coalition_unlock(coal); | |
1838 | return KERN_FAILURE; | |
1839 | } | |
1840 | assert(coal->termrequested); | |
1841 | if (coal->active_count > 0) { | |
1842 | coalition_unlock(coal); | |
1843 | return KERN_FAILURE; | |
1844 | } | |
1845 | ||
1846 | coal->reaped = TRUE; | |
1847 | ||
1848 | /* Caller, launchd, and coalitions list should each have a reference */ | |
1849 | assert(coal->ref_count > 2); | |
1850 | ||
1851 | coalition_unlock(coal); | |
1852 | ||
1853 | lck_mtx_lock(&coalitions_list_lock); | |
1854 | coalition_count--; | |
3e170ce0 | 1855 | remqueue(&coal->coalitions); |
fe8ab488 A |
1856 | lck_mtx_unlock(&coalitions_list_lock); |
1857 | ||
1858 | /* Release the list's reference and launchd's reference. */ | |
1859 | coalition_release(coal); | |
1860 | coalition_release(coal); | |
1861 | ||
1862 | return KERN_SUCCESS; | |
1863 | } | |
1864 | ||
5ba3f43e | 1865 | #if DEVELOPMENT || DEBUG |
0a7de745 A |
1866 | int |
1867 | coalition_should_notify(coalition_t coal) | |
3e170ce0 A |
1868 | { |
1869 | int should; | |
0a7de745 | 1870 | if (!coal) { |
3e170ce0 | 1871 | return -1; |
0a7de745 | 1872 | } |
3e170ce0 A |
1873 | coalition_lock(coal); |
1874 | should = coal->should_notify; | |
1875 | coalition_unlock(coal); | |
1876 | ||
1877 | return should; | |
1878 | } | |
1879 | ||
0a7de745 A |
1880 | void |
1881 | coalition_set_notify(coalition_t coal, int notify) | |
3e170ce0 | 1882 | { |
0a7de745 | 1883 | if (!coal) { |
3e170ce0 | 1884 | return; |
0a7de745 | 1885 | } |
3e170ce0 A |
1886 | coalition_lock(coal); |
1887 | coal->should_notify = !!notify; | |
1888 | coalition_unlock(coal); | |
1889 | } | |
1890 | #endif | |
1891 | ||
fe8ab488 | 1892 | void |
3e170ce0 | 1893 | coalitions_init(void) |
fe8ab488 | 1894 | { |
3e170ce0 A |
1895 | kern_return_t kr; |
1896 | int i; | |
1897 | const struct coalition_type *ctype; | |
1898 | ||
fe8ab488 | 1899 | coalition_zone = zinit( |
0a7de745 A |
1900 | sizeof(struct coalition), |
1901 | CONFIG_COALITION_MAX * sizeof(struct coalition), | |
1902 | COALITION_CHUNK * sizeof(struct coalition), | |
1903 | "coalitions"); | |
fe8ab488 | 1904 | zone_change(coalition_zone, Z_NOENCRYPT, TRUE); |
3e170ce0 | 1905 | queue_head_init(coalitions_q); |
fe8ab488 A |
1906 | |
1907 | if (!PE_parse_boot_argn("unrestrict_coalition_syscalls", &unrestrict_coalition_syscalls, | |
0a7de745 | 1908 | sizeof(unrestrict_coalition_syscalls))) { |
fe8ab488 A |
1909 | unrestrict_coalition_syscalls = 0; |
1910 | } | |
1911 | ||
5ba3f43e | 1912 | if (!PE_parse_boot_argn("tg_adaptive", &merge_adaptive_coalitions, |
0a7de745 | 1913 | sizeof(merge_adaptive_coalitions))) { |
5ba3f43e A |
1914 | merge_adaptive_coalitions = 0; |
1915 | } | |
1916 | ||
fe8ab488 A |
1917 | lck_grp_attr_setdefault(&coalitions_lck_grp_attr); |
1918 | lck_grp_init(&coalitions_lck_grp, "coalition", &coalitions_lck_grp_attr); | |
1919 | lck_attr_setdefault(&coalitions_lck_attr); | |
1920 | lck_mtx_init(&coalitions_list_lock, &coalitions_lck_grp, &coalitions_lck_attr); | |
1921 | ||
1922 | init_task_ledgers(); | |
1923 | ||
cb323159 A |
1924 | init_coalition_ledgers(); |
1925 | ||
3e170ce0 A |
1926 | for (i = 0, ctype = &s_coalition_types[0]; i < COALITION_NUM_TYPES; ctype++, i++) { |
1927 | /* verify the entry in the global coalition types array */ | |
1928 | if (ctype->type != i || | |
1929 | !ctype->init || | |
1930 | !ctype->dealloc || | |
1931 | !ctype->adopt_task || | |
1932 | !ctype->remove_task) { | |
1933 | panic("%s: Malformed coalition type %s(%d) in slot for type:%s(%d)", | |
0a7de745 | 1934 | __func__, coal_type_str(ctype->type), ctype->type, coal_type_str(i), i); |
3e170ce0 | 1935 | } |
0a7de745 | 1936 | if (!ctype->has_default) { |
3e170ce0 | 1937 | continue; |
0a7de745 | 1938 | } |
5ba3f43e | 1939 | kr = coalition_create_internal(ctype->type, COALITION_ROLE_SYSTEM, TRUE, &init_coalition[ctype->type]); |
0a7de745 | 1940 | if (kr != KERN_SUCCESS) { |
3e170ce0 | 1941 | panic("%s: could not create init %s coalition: kr:%d", |
0a7de745 A |
1942 | __func__, coal_type_str(i), kr); |
1943 | } | |
5ba3f43e | 1944 | kr = coalition_create_internal(ctype->type, COALITION_ROLE_SYSTEM, FALSE, &corpse_coalition[ctype->type]); |
0a7de745 | 1945 | if (kr != KERN_SUCCESS) { |
39037602 | 1946 | panic("%s: could not create corpse %s coalition: kr:%d", |
0a7de745 A |
1947 | __func__, coal_type_str(i), kr); |
1948 | } | |
fe8ab488 | 1949 | } |
3e170ce0 | 1950 | |
fe8ab488 A |
1951 | /* "Leak" our reference to the global object */ |
1952 | } | |
1953 | ||
3e170ce0 A |
1954 | /* |
1955 | * BSD Kernel interface functions | |
1956 | * | |
1957 | */ | |
0a7de745 A |
1958 | static void |
1959 | coalition_fill_procinfo(struct coalition *coal, | |
1960 | struct procinfo_coalinfo *coalinfo) | |
a1c7dba1 | 1961 | { |
3e170ce0 A |
1962 | coalinfo->coalition_id = coal->id; |
1963 | coalinfo->coalition_type = coal->type; | |
1964 | coalinfo->coalition_tasks = coalition_get_task_count(coal); | |
a1c7dba1 A |
1965 | } |
1966 | ||
3e170ce0 | 1967 | |
0a7de745 A |
1968 | int |
1969 | coalitions_get_list(int type, struct procinfo_coalinfo *coal_list, int list_sz) | |
a1c7dba1 | 1970 | { |
3e170ce0 A |
1971 | int ncoals = 0; |
1972 | struct coalition *coal; | |
1973 | ||
1974 | lck_mtx_lock(&coalitions_list_lock); | |
1975 | qe_foreach_element(coal, &coalitions_q, coalitions) { | |
1976 | if (!coal->reaped && (type < 0 || type == (int)coal->type)) { | |
0a7de745 | 1977 | if (coal_list && ncoals < list_sz) { |
3e170ce0 | 1978 | coalition_fill_procinfo(coal, &coal_list[ncoals]); |
0a7de745 | 1979 | } |
3e170ce0 A |
1980 | ++ncoals; |
1981 | } | |
1982 | } | |
1983 | lck_mtx_unlock(&coalitions_list_lock); | |
1984 | ||
1985 | return ncoals; | |
a1c7dba1 A |
1986 | } |
1987 | ||
3e170ce0 | 1988 | /* |
cb323159 | 1989 | * Return the coaltion of the given type to which the task belongs. |
3e170ce0 | 1990 | */ |
cb323159 A |
1991 | coalition_t |
1992 | task_get_coalition(task_t task, int coal_type) | |
a1c7dba1 | 1993 | { |
3e170ce0 | 1994 | coalition_t c; |
3e170ce0 | 1995 | |
cb323159 A |
1996 | if (task == NULL || coal_type > COALITION_TYPE_MAX) { |
1997 | return COALITION_NULL; | |
0a7de745 | 1998 | } |
3e170ce0 A |
1999 | |
2000 | c = task->coalition[coal_type]; | |
cb323159 A |
2001 | assert(c == COALITION_NULL || (int)c->type == coal_type); |
2002 | return c; | |
2003 | } | |
3e170ce0 | 2004 | |
cb323159 A |
2005 | /* |
2006 | * Report if the given task is the leader of the given jetsam coalition. | |
2007 | */ | |
2008 | boolean_t | |
2009 | coalition_is_leader(task_t task, coalition_t coal) | |
2010 | { | |
2011 | boolean_t ret = FALSE; | |
3e170ce0 | 2012 | |
cb323159 A |
2013 | if (coal != COALITION_NULL) { |
2014 | coalition_lock(coal); | |
3e170ce0 | 2015 | |
cb323159 | 2016 | ret = (coal->type == COALITION_TYPE_JETSAM && coal->j.leader == task); |
3e170ce0 | 2017 | |
cb323159 | 2018 | coalition_unlock(coal); |
0a7de745 | 2019 | } |
3e170ce0 | 2020 | |
3e170ce0 | 2021 | return ret; |
a1c7dba1 A |
2022 | } |
2023 | ||
0a7de745 A |
2024 | kern_return_t |
2025 | coalition_iterate_stackshot(coalition_iterate_fn_t callout, void *arg, uint32_t coalition_type) | |
5ba3f43e A |
2026 | { |
2027 | coalition_t coal; | |
2028 | int i = 0; | |
2029 | ||
2030 | qe_foreach_element(coal, &coalitions_q, coalitions) { | |
0a7de745 | 2031 | if (coal == NULL || !ml_validate_nofault((vm_offset_t)coal, sizeof(struct coalition))) { |
5ba3f43e | 2032 | return KERN_FAILURE; |
0a7de745 | 2033 | } |
5ba3f43e | 2034 | |
0a7de745 | 2035 | if (coalition_type == coal->type) { |
5ba3f43e | 2036 | callout(arg, i++, coal); |
0a7de745 | 2037 | } |
5ba3f43e A |
2038 | } |
2039 | ||
2040 | return KERN_SUCCESS; | |
2041 | } | |
2042 | ||
0a7de745 A |
2043 | task_t |
2044 | kdp_coalition_get_leader(coalition_t coal) | |
5ba3f43e | 2045 | { |
0a7de745 | 2046 | if (!coal) { |
5ba3f43e | 2047 | return TASK_NULL; |
0a7de745 | 2048 | } |
5ba3f43e A |
2049 | |
2050 | if (coal->type == COALITION_TYPE_JETSAM) { | |
2051 | return coal->j.leader; | |
2052 | } | |
2053 | return TASK_NULL; | |
2054 | } | |
3e170ce0 | 2055 | |
0a7de745 A |
2056 | task_t |
2057 | coalition_get_leader(coalition_t coal) | |
4bd07ac2 A |
2058 | { |
2059 | task_t leader = TASK_NULL; | |
2060 | ||
0a7de745 | 2061 | if (!coal) { |
4bd07ac2 | 2062 | return TASK_NULL; |
0a7de745 | 2063 | } |
4bd07ac2 A |
2064 | |
2065 | coalition_lock(coal); | |
0a7de745 | 2066 | if (coal->type != COALITION_TYPE_JETSAM) { |
4bd07ac2 | 2067 | goto out_unlock; |
0a7de745 | 2068 | } |
4bd07ac2 A |
2069 | |
2070 | leader = coal->j.leader; | |
0a7de745 | 2071 | if (leader != TASK_NULL) { |
4bd07ac2 | 2072 | task_reference(leader); |
0a7de745 | 2073 | } |
4bd07ac2 A |
2074 | |
2075 | out_unlock: | |
2076 | coalition_unlock(coal); | |
2077 | return leader; | |
2078 | } | |
2079 | ||
2080 | ||
0a7de745 A |
2081 | int |
2082 | coalition_get_task_count(coalition_t coal) | |
a1c7dba1 | 2083 | { |
3e170ce0 A |
2084 | int ntasks = 0; |
2085 | struct queue_entry *qe; | |
0a7de745 | 2086 | if (!coal) { |
3e170ce0 | 2087 | return 0; |
0a7de745 | 2088 | } |
3e170ce0 A |
2089 | |
2090 | coalition_lock(coal); | |
2091 | switch (coal->type) { | |
2092 | case COALITION_TYPE_RESOURCE: | |
2093 | qe_foreach(qe, &coal->r.tasks) | |
0a7de745 | 2094 | ntasks++; |
3e170ce0 A |
2095 | break; |
2096 | case COALITION_TYPE_JETSAM: | |
0a7de745 | 2097 | if (coal->j.leader) { |
3e170ce0 | 2098 | ntasks++; |
0a7de745 | 2099 | } |
3e170ce0 | 2100 | qe_foreach(qe, &coal->j.other) |
0a7de745 | 2101 | ntasks++; |
3e170ce0 | 2102 | qe_foreach(qe, &coal->j.extensions) |
0a7de745 | 2103 | ntasks++; |
3e170ce0 | 2104 | qe_foreach(qe, &coal->j.services) |
0a7de745 | 2105 | ntasks++; |
3e170ce0 A |
2106 | break; |
2107 | default: | |
2108 | break; | |
2109 | } | |
2110 | coalition_unlock(coal); | |
2111 | ||
2112 | return ntasks; | |
a1c7dba1 A |
2113 | } |
2114 | ||
3e170ce0 | 2115 | |
0a7de745 A |
2116 | static uint64_t |
2117 | i_get_list_footprint(queue_t list, int type, int *ntasks) | |
3e170ce0 | 2118 | { |
a1c7dba1 | 2119 | task_t task; |
3e170ce0 | 2120 | uint64_t bytes = 0; |
a1c7dba1 | 2121 | |
3e170ce0 A |
2122 | qe_foreach_element(task, list, task_coalition[type]) { |
2123 | bytes += get_task_phys_footprint(task); | |
2124 | coal_dbg(" [%d] task_pid:%d, type:%d, footprint:%lld", | |
0a7de745 | 2125 | *ntasks, task_pid(task), type, bytes); |
3e170ce0 A |
2126 | *ntasks += 1; |
2127 | } | |
a1c7dba1 | 2128 | |
3e170ce0 A |
2129 | return bytes; |
2130 | } | |
a1c7dba1 | 2131 | |
0a7de745 A |
2132 | uint64_t |
2133 | coalition_get_page_count(coalition_t coal, int *ntasks) | |
3e170ce0 A |
2134 | { |
2135 | uint64_t bytes = 0; | |
2136 | int num_tasks = 0; | |
2137 | ||
0a7de745 | 2138 | if (ntasks) { |
3e170ce0 | 2139 | *ntasks = 0; |
0a7de745 A |
2140 | } |
2141 | if (!coal) { | |
3e170ce0 | 2142 | return bytes; |
0a7de745 | 2143 | } |
a1c7dba1 | 2144 | |
3e170ce0 | 2145 | coalition_lock(coal); |
a1c7dba1 | 2146 | |
3e170ce0 A |
2147 | switch (coal->type) { |
2148 | case COALITION_TYPE_RESOURCE: | |
2149 | bytes += i_get_list_footprint(&coal->r.tasks, COALITION_TYPE_RESOURCE, &num_tasks); | |
2150 | break; | |
2151 | case COALITION_TYPE_JETSAM: | |
2152 | if (coal->j.leader) { | |
2153 | bytes += get_task_phys_footprint(coal->j.leader); | |
2154 | num_tasks = 1; | |
a1c7dba1 | 2155 | } |
3e170ce0 A |
2156 | bytes += i_get_list_footprint(&coal->j.extensions, COALITION_TYPE_JETSAM, &num_tasks); |
2157 | bytes += i_get_list_footprint(&coal->j.services, COALITION_TYPE_JETSAM, &num_tasks); | |
2158 | bytes += i_get_list_footprint(&coal->j.other, COALITION_TYPE_JETSAM, &num_tasks); | |
2159 | break; | |
2160 | default: | |
2161 | break; | |
a1c7dba1 | 2162 | } |
3e170ce0 | 2163 | |
a1c7dba1 | 2164 | coalition_unlock(coal); |
3e170ce0 | 2165 | |
0a7de745 | 2166 | if (ntasks) { |
3e170ce0 | 2167 | *ntasks = num_tasks; |
0a7de745 | 2168 | } |
3e170ce0 A |
2169 | |
2170 | return bytes / PAGE_SIZE_64; | |
a1c7dba1 A |
2171 | } |
2172 | ||
3e170ce0 A |
2173 | struct coal_sort_s { |
2174 | int pid; | |
2175 | int usr_order; | |
2176 | uint64_t bytes; | |
2177 | }; | |
2178 | ||
2179 | /* | |
2180 | * return < 0 for a < b | |
2181 | * 0 for a == b | |
2182 | * > 0 for a > b | |
2183 | */ | |
2184 | typedef int (*cmpfunc_t)(const void *a, const void *b); | |
2185 | ||
2186 | extern void | |
2187 | qsort(void *a, size_t n, size_t es, cmpfunc_t cmp); | |
2188 | ||
0a7de745 A |
2189 | static int |
2190 | dflt_cmp(const void *a, const void *b) | |
3e170ce0 A |
2191 | { |
2192 | const struct coal_sort_s *csA = (const struct coal_sort_s *)a; | |
2193 | const struct coal_sort_s *csB = (const struct coal_sort_s *)b; | |
2194 | ||
2195 | /* | |
2196 | * if both A and B are equal, use a memory descending sort | |
2197 | */ | |
0a7de745 | 2198 | if (csA->usr_order == csB->usr_order) { |
3e170ce0 | 2199 | return (int)((int64_t)csB->bytes - (int64_t)csA->bytes); |
0a7de745 | 2200 | } |
3e170ce0 A |
2201 | |
2202 | /* otherwise, return the relationship between user specified orders */ | |
0a7de745 | 2203 | return csA->usr_order - csB->usr_order; |
3e170ce0 A |
2204 | } |
2205 | ||
0a7de745 A |
2206 | static int |
2207 | mem_asc_cmp(const void *a, const void *b) | |
3e170ce0 A |
2208 | { |
2209 | const struct coal_sort_s *csA = (const struct coal_sort_s *)a; | |
2210 | const struct coal_sort_s *csB = (const struct coal_sort_s *)b; | |
2211 | ||
2212 | return (int)((int64_t)csA->bytes - (int64_t)csB->bytes); | |
2213 | } | |
2214 | ||
0a7de745 A |
2215 | static int |
2216 | mem_dec_cmp(const void *a, const void *b) | |
3e170ce0 A |
2217 | { |
2218 | const struct coal_sort_s *csA = (const struct coal_sort_s *)a; | |
2219 | const struct coal_sort_s *csB = (const struct coal_sort_s *)b; | |
2220 | ||
2221 | return (int)((int64_t)csB->bytes - (int64_t)csA->bytes); | |
2222 | } | |
2223 | ||
0a7de745 A |
2224 | static int |
2225 | usr_asc_cmp(const void *a, const void *b) | |
3e170ce0 A |
2226 | { |
2227 | const struct coal_sort_s *csA = (const struct coal_sort_s *)a; | |
2228 | const struct coal_sort_s *csB = (const struct coal_sort_s *)b; | |
2229 | ||
0a7de745 | 2230 | return csA->usr_order - csB->usr_order; |
3e170ce0 A |
2231 | } |
2232 | ||
0a7de745 A |
2233 | static int |
2234 | usr_dec_cmp(const void *a, const void *b) | |
3e170ce0 A |
2235 | { |
2236 | const struct coal_sort_s *csA = (const struct coal_sort_s *)a; | |
2237 | const struct coal_sort_s *csB = (const struct coal_sort_s *)b; | |
2238 | ||
0a7de745 | 2239 | return csB->usr_order - csA->usr_order; |
3e170ce0 A |
2240 | } |
2241 | ||
2242 | /* avoid dynamic allocation in this path */ | |
2243 | #define MAX_SORTED_PIDS 80 | |
2244 | ||
0a7de745 A |
2245 | static int |
2246 | coalition_get_sort_list(coalition_t coal, int sort_order, queue_t list, | |
2247 | struct coal_sort_s *sort_array, int array_sz) | |
3e170ce0 A |
2248 | { |
2249 | int ntasks = 0; | |
2250 | task_t task; | |
2251 | ||
2252 | assert(sort_array != NULL); | |
2253 | ||
0a7de745 | 2254 | if (array_sz <= 0) { |
3e170ce0 | 2255 | return 0; |
0a7de745 | 2256 | } |
3e170ce0 A |
2257 | |
2258 | if (!list) { | |
2259 | /* | |
2260 | * this function will only be called with a NULL | |
2261 | * list for JETSAM-type coalitions, and is intended | |
2262 | * to investigate the leader process | |
2263 | */ | |
2264 | if (coal->type != COALITION_TYPE_JETSAM || | |
0a7de745 | 2265 | coal->j.leader == TASK_NULL) { |
3e170ce0 | 2266 | return 0; |
0a7de745 | 2267 | } |
3e170ce0 A |
2268 | sort_array[0].pid = task_pid(coal->j.leader); |
2269 | switch (sort_order) { | |
2270 | case COALITION_SORT_DEFAULT: | |
2271 | sort_array[0].usr_order = 0; | |
0a7de745 | 2272 | /* fall-through */ |
3e170ce0 A |
2273 | case COALITION_SORT_MEM_ASC: |
2274 | case COALITION_SORT_MEM_DEC: | |
2275 | sort_array[0].bytes = get_task_phys_footprint(coal->j.leader); | |
2276 | break; | |
2277 | case COALITION_SORT_USER_ASC: | |
2278 | case COALITION_SORT_USER_DEC: | |
2279 | sort_array[0].usr_order = 0; | |
2280 | break; | |
2281 | default: | |
2282 | break; | |
2283 | } | |
2284 | return 1; | |
2285 | } | |
2286 | ||
2287 | qe_foreach_element(task, list, task_coalition[coal->type]) { | |
2288 | if (ntasks >= array_sz) { | |
2289 | printf("WARNING: more than %d pids in coalition %llu\n", | |
0a7de745 | 2290 | MAX_SORTED_PIDS, coal->id); |
3e170ce0 A |
2291 | break; |
2292 | } | |
2293 | ||
2294 | sort_array[ntasks].pid = task_pid(task); | |
2295 | ||
2296 | switch (sort_order) { | |
2297 | case COALITION_SORT_DEFAULT: | |
2298 | sort_array[ntasks].usr_order = 0; | |
0a7de745 | 2299 | /* fall-through */ |
3e170ce0 A |
2300 | case COALITION_SORT_MEM_ASC: |
2301 | case COALITION_SORT_MEM_DEC: | |
2302 | sort_array[ntasks].bytes = get_task_phys_footprint(task); | |
2303 | break; | |
2304 | case COALITION_SORT_USER_ASC: | |
2305 | case COALITION_SORT_USER_DEC: | |
2306 | sort_array[ntasks].usr_order = 0; | |
2307 | break; | |
2308 | default: | |
2309 | break; | |
2310 | } | |
2311 | ||
2312 | ntasks++; | |
2313 | } | |
2314 | ||
2315 | return ntasks; | |
2316 | } | |
2317 | ||
0a7de745 A |
2318 | int |
2319 | coalition_get_pid_list(coalition_t coal, uint32_t rolemask, int sort_order, | |
2320 | int *pid_list, int list_sz) | |
3e170ce0 A |
2321 | { |
2322 | struct i_jetsam_coalition *cj; | |
2323 | int ntasks = 0; | |
2324 | cmpfunc_t cmp_func = NULL; | |
0a7de745 | 2325 | struct coal_sort_s sort_array[MAX_SORTED_PIDS] = { {0, 0, 0} }; /* keep to < 2k */ |
3e170ce0 A |
2326 | |
2327 | if (!coal || | |
2328 | !(rolemask & COALITION_ROLEMASK_ALLROLES) || | |
2329 | !pid_list || list_sz < 1) { | |
2330 | coal_dbg("Invalid parameters: coal:%p, type:%d, rolemask:0x%x, " | |
0a7de745 A |
2331 | "pid_list:%p, list_sz:%d", coal, coal ? coal->type : -1, |
2332 | rolemask, pid_list, list_sz); | |
3e170ce0 A |
2333 | return -EINVAL; |
2334 | } | |
2335 | ||
2336 | switch (sort_order) { | |
2337 | case COALITION_SORT_NOSORT: | |
2338 | cmp_func = NULL; | |
2339 | break; | |
2340 | case COALITION_SORT_DEFAULT: | |
2341 | cmp_func = dflt_cmp; | |
2342 | break; | |
2343 | case COALITION_SORT_MEM_ASC: | |
2344 | cmp_func = mem_asc_cmp; | |
2345 | break; | |
2346 | case COALITION_SORT_MEM_DEC: | |
2347 | cmp_func = mem_dec_cmp; | |
2348 | break; | |
2349 | case COALITION_SORT_USER_ASC: | |
2350 | cmp_func = usr_asc_cmp; | |
2351 | break; | |
2352 | case COALITION_SORT_USER_DEC: | |
2353 | cmp_func = usr_dec_cmp; | |
2354 | break; | |
2355 | default: | |
2356 | return -ENOTSUP; | |
2357 | } | |
2358 | ||
2359 | coalition_lock(coal); | |
2360 | ||
2361 | if (coal->type == COALITION_TYPE_RESOURCE) { | |
2362 | ntasks += coalition_get_sort_list(coal, sort_order, &coal->r.tasks, | |
0a7de745 | 2363 | sort_array, MAX_SORTED_PIDS); |
3e170ce0 A |
2364 | goto unlock_coal; |
2365 | } | |
2366 | ||
2367 | cj = &coal->j; | |
2368 | ||
0a7de745 | 2369 | if (rolemask & COALITION_ROLEMASK_UNDEF) { |
3e170ce0 | 2370 | ntasks += coalition_get_sort_list(coal, sort_order, &cj->other, |
0a7de745 A |
2371 | sort_array + ntasks, |
2372 | MAX_SORTED_PIDS - ntasks); | |
2373 | } | |
3e170ce0 | 2374 | |
0a7de745 | 2375 | if (rolemask & COALITION_ROLEMASK_XPC) { |
3e170ce0 | 2376 | ntasks += coalition_get_sort_list(coal, sort_order, &cj->services, |
0a7de745 A |
2377 | sort_array + ntasks, |
2378 | MAX_SORTED_PIDS - ntasks); | |
2379 | } | |
3e170ce0 | 2380 | |
0a7de745 | 2381 | if (rolemask & COALITION_ROLEMASK_EXT) { |
3e170ce0 | 2382 | ntasks += coalition_get_sort_list(coal, sort_order, &cj->extensions, |
0a7de745 A |
2383 | sort_array + ntasks, |
2384 | MAX_SORTED_PIDS - ntasks); | |
2385 | } | |
3e170ce0 | 2386 | |
0a7de745 | 2387 | if (rolemask & COALITION_ROLEMASK_LEADER) { |
3e170ce0 | 2388 | ntasks += coalition_get_sort_list(coal, sort_order, NULL, |
0a7de745 A |
2389 | sort_array + ntasks, |
2390 | MAX_SORTED_PIDS - ntasks); | |
2391 | } | |
3e170ce0 A |
2392 | |
2393 | unlock_coal: | |
2394 | coalition_unlock(coal); | |
2395 | ||
2396 | /* sort based on the chosen criterion (no sense sorting 1 item) */ | |
0a7de745 | 2397 | if (cmp_func && ntasks > 1) { |
3e170ce0 | 2398 | qsort(sort_array, ntasks, sizeof(struct coal_sort_s), cmp_func); |
0a7de745 | 2399 | } |
3e170ce0 A |
2400 | |
2401 | for (int i = 0; i < ntasks; i++) { | |
0a7de745 | 2402 | if (i >= list_sz) { |
3e170ce0 | 2403 | break; |
0a7de745 | 2404 | } |
3e170ce0 | 2405 | coal_dbg(" [%d] PID:%d, footprint:%lld, usr_order:%d", |
0a7de745 A |
2406 | i, sort_array[i].pid, sort_array[i].bytes, |
2407 | sort_array[i].usr_order); | |
3e170ce0 A |
2408 | pid_list[i] = sort_array[i].pid; |
2409 | } | |
2410 | ||
2411 | return ntasks; | |
2412 | } |