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