]> git.saurik.com Git - apple/xnu.git/blame - osfmk/kern/coalition.c
xnu-6153.141.1.tar.gz
[apple/xnu.git] / osfmk / kern / coalition.c
CommitLineData
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 */
60int coalitions_get_list(int type, struct procinfo_coalinfo *coal_list, int list_sz);
cb323159
A
61coalition_t task_get_coalition(task_t task, int type);
62boolean_t coalition_is_leader(task_t task, coalition_t coal);
4bd07ac2 63task_t coalition_get_leader(coalition_t coal);
3e170ce0
A
64int coalition_get_task_count(coalition_t coal);
65uint64_t coalition_get_page_count(coalition_t coal, int *ntasks);
66int 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 */
70extern 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 */
76ledger_template_t coalition_task_ledger_template = NULL;
77ledger_template_t coalition_ledger_template = NULL;
78
79extern 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
87int unrestrict_coalition_syscalls;
5ba3f43e 88int merge_adaptive_coalitions;
fe8ab488
A
89
90lck_attr_t coalitions_lck_attr;
91lck_grp_t coalitions_lck_grp;
92lck_grp_attr_t coalitions_lck_grp_attr;
93
94/* coalitions_list_lock protects coalition_count, coalitions queue, next_coalition_id. */
0a7de745 95decl_lck_mtx_data(static, coalitions_list_lock);
fe8ab488
A
96static uint64_t coalition_count;
97static uint64_t coalition_next_id = 1;
3e170ce0 98static queue_head_t coalitions_q;
fe8ab488 99
3e170ce0 100coalition_t init_coalition[COALITION_NUM_TYPES];
39037602 101coalition_t corpse_coalition[COALITION_NUM_TYPES];
fe8ab488
A
102
103zone_t coalition_zone;
104
0a7de745
A
105static const char *
106coal_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
118struct 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
177static kern_return_t i_coal_resource_init(coalition_t coal, boolean_t privileged);
178static void i_coal_resource_dealloc(coalition_t coal);
179static kern_return_t i_coal_resource_adopt_task(coalition_t coal, task_t task);
180static kern_return_t i_coal_resource_remove_task(coalition_t coal, task_t task);
181static kern_return_t i_coal_resource_set_taskrole(coalition_t coal,
0a7de745 182 task_t task, int role);
3e170ce0
A
183static int i_coal_resource_get_taskrole(coalition_t coal, task_t task);
184static 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 */
191static_assert(COALITION_NUM_THREAD_QOS_TYPES == THREAD_QOS_LAST);
192
3e170ce0 193struct 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
240static kern_return_t i_coal_jetsam_init(coalition_t coal, boolean_t privileged);
241static void i_coal_jetsam_dealloc(coalition_t coal);
242static kern_return_t i_coal_jetsam_adopt_task(coalition_t coal, task_t task);
243static kern_return_t i_coal_jetsam_remove_task(coalition_t coal, task_t task);
244static kern_return_t i_coal_jetsam_set_taskrole(coalition_t coal,
0a7de745 245 task_t task, int role);
cb323159 246int i_coal_jetsam_get_taskrole(coalition_t coal, task_t task);
3e170ce0 247static void i_coal_jetsam_iterate_tasks(coalition_t coal, void *ctx,
0a7de745 248 void (*callback)(coalition_t, void *, task_t));
3e170ce0
A
249
250struct 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 */
262struct 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 */
303static 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
356struct coalition_ledger_indices coalition_ledgers =
357{.logical_writes = -1, };
358void __attribute__((noinline)) SENDING_NOTIFICATION__THIS_COALITION_IS_CAUSING_TOO_MUCH_IO(int flavor);
359
360ledger_t
361coalition_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
374enum {
375 COALITION_IO_LEDGER_ENABLE,
376 COALITION_IO_LEDGER_DISABLE
377};
378
379void
380coalition_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
394int
395coalition_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);
412out:
413 return error;
414}
415
416void __attribute__((noinline))
417SENDING_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
461Exit:
462 return;
463}
464
465void
466coalition_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
473void
474init_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
503void
504coalition_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
522static void
523coalition_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
542static kern_return_t
543i_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
564static void
565i_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
573static kern_return_t
574i_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
605static kern_return_t
606i_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
673static kern_return_t
674i_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
680static int
681i_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
696static void
697i_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
706kern_return_t
707coalition_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 */
891static kern_return_t
892i_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
905static void
906i_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
918static kern_return_t
919i_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
936static kern_return_t
937i_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
959static kern_return_t
960i_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 1028int
3e170ce0
A
1029i_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
1065static void
1066i_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 */
1101kern_return_t
5ba3f43e 1102coalition_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 * */
1156void
1157coalition_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 */
1201static coalition_t
1202coalition_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 */
1223coalition_t
1224coalition_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 */
1272coalition_t
1273coalition_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
1319uint64_t
1320coalition_id(coalition_t coal)
1321{
1322 return coal->id;
1323}
1324
3e170ce0
A
1325void
1326task_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
1338void
1339task_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
1357int
1358coalition_type(coalition_t coal)
1359{
1360 return coal->type;
fe8ab488
A
1361}
1362
5ba3f43e
A
1363boolean_t
1364coalition_term_requested(coalition_t coal)
1365{
1366 return coal->termrequested;
1367}
1368
1369boolean_t
1370coalition_is_terminated(coalition_t coal)
1371{
1372 return coal->terminated;
1373}
1374
1375boolean_t
1376coalition_is_reaped(coalition_t coal)
1377{
1378 return coal->reaped;
1379}
1380
fe8ab488
A
1381boolean_t
1382coalition_is_privileged(coalition_t coal)
1383{
1384 return coal->privileged || unrestrict_coalition_syscalls;
1385}
1386
1387boolean_t
3e170ce0 1388task_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
1402void
1403task_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
1420boolean_t
1421task_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
1433uint32_t
1434task_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
1444boolean_t
1445task_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
1457uint32_t
1458task_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
1468void
1469coalition_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
1477void
1478coalition_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
1494void
1495coalition_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... */
1544kern_return_t
3e170ce0 1545coalitions_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. */
1556kern_return_t
1557coalitions_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
1572static kern_return_t
1573coalition_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 1599out_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
1619static kern_return_t
1620coalition_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 */
1659kern_return_t
3e170ce0 1660coalitions_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 */
1698kern_return_t
1699coalitions_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 */
1717void
1718task_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
1736kern_return_t
1737coalitions_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 */
1760kern_return_t
1761coalition_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 */
1822kern_return_t
1823coalition_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
1866int
1867coalition_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
1880void
1881coalition_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 1892void
3e170ce0 1893coalitions_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
1958static void
1959coalition_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
1968int
1969coalitions_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
1991coalition_t
1992task_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 */
2008boolean_t
2009coalition_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
2024kern_return_t
2025coalition_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
2043task_t
2044kdp_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
2056task_t
2057coalition_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
2075out_unlock:
2076 coalition_unlock(coal);
2077 return leader;
2078}
2079
2080
0a7de745
A
2081int
2082coalition_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
2116static uint64_t
2117i_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
2132uint64_t
2133coalition_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
2173struct 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 */
2184typedef int (*cmpfunc_t)(const void *a, const void *b);
2185
2186extern void
2187qsort(void *a, size_t n, size_t es, cmpfunc_t cmp);
2188
0a7de745
A
2189static int
2190dflt_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
2206static int
2207mem_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
2215static int
2216mem_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
2224static int
2225usr_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
2233static int
2234usr_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
2245static int
2246coalition_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
2318int
2319coalition_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
2393unlock_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}