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