2 * Copyright (c) 2013 Apple Computer, Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
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.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
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.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <kern/kern_types.h>
30 #include <mach/mach_types.h>
31 #include <mach/boolean.h>
33 #include <kern/coalition.h>
34 #include <kern/host.h>
35 #include <kern/kalloc.h>
36 #include <kern/ledger.h>
37 #include <kern/mach_param.h> /* for TASK_CHUNK */
38 #include <kern/task.h>
39 #include <kern/zalloc.h>
41 #include <libkern/OSAtomic.h>
43 #include <mach/coalition_notification_server.h>
44 #include <mach/host_priv.h>
45 #include <mach/host_special_ports.h>
47 #include <sys/errno.h>
50 * BSD interface functions
52 int coalitions_get_list(int type
, struct procinfo_coalinfo
*coal_list
, int list_sz
);
53 boolean_t
coalition_is_leader(task_t task
, int coal_type
, coalition_t
*coal
);
54 task_t
coalition_get_leader(coalition_t coal
);
55 int coalition_get_task_count(coalition_t coal
);
56 uint64_t coalition_get_page_count(coalition_t coal
, int *ntasks
);
57 int coalition_get_pid_list(coalition_t coal
, uint32_t rolemask
, int sort_order
,
58 int *pid_list
, int list_sz
);
60 /* defined in task.c */
61 extern ledger_template_t task_ledger_template
;
64 * Coalition zone needs limits. We expect there will be as many coalitions as
65 * tasks (same order of magnitude), so use the task zone's limits.
67 #define CONFIG_COALITION_MAX CONFIG_TASK_MAX
68 #define COALITION_CHUNK TASK_CHUNK
70 int unrestrict_coalition_syscalls
;
72 lck_attr_t coalitions_lck_attr
;
73 lck_grp_t coalitions_lck_grp
;
74 lck_grp_attr_t coalitions_lck_grp_attr
;
76 /* coalitions_list_lock protects coalition_count, coalitions queue, next_coalition_id. */
77 decl_lck_mtx_data(static,coalitions_list_lock
);
78 static uint64_t coalition_count
;
79 static uint64_t coalition_next_id
= 1;
80 static queue_head_t coalitions_q
;
82 coalition_t init_coalition
[COALITION_NUM_TYPES
];
83 coalition_t corpse_coalition
[COALITION_NUM_TYPES
];
85 zone_t coalition_zone
;
87 static const char *coal_type_str(int type
)
90 case COALITION_TYPE_RESOURCE
:
92 case COALITION_TYPE_JETSAM
:
99 struct coalition_type
{
104 * pre-condition: coalition just allocated (unlocked), unreferenced,
107 kern_return_t (*init
)(coalition_t coal
, boolean_t privileged
);
111 * pre-condition: coalition unlocked
112 * pre-condition: coalition refcount=0, active_count=0,
113 * termrequested=1, terminated=1, reaped=1
115 void (*dealloc
)(coalition_t coal
);
119 * pre-condition: coalition locked
120 * pre-condition: coalition !repead and !terminated
122 kern_return_t (*adopt_task
)(coalition_t coal
, task_t task
);
126 * pre-condition: coalition locked
127 * pre-condition: task has been removed from coalition's task list
129 kern_return_t (*remove_task
)(coalition_t coal
, task_t task
);
133 * pre-condition: coalition locked
134 * pre-condition: task added to coalition's task list,
135 * active_count >= 1 (at least the given task is active)
137 kern_return_t (*set_taskrole
)(coalition_t coal
, task_t task
, int role
);
141 * pre-condition: coalition locked
142 * pre-condition: task added to coalition's task list,
143 * active_count >= 1 (at least the given task is active)
145 int (*get_taskrole
)(coalition_t coal
, task_t task
);
149 * pre-condition: coalition locked
151 void (*iterate_tasks
)(coalition_t coal
, void *ctx
, void (*callback
)(coalition_t
, void *, task_t
));
155 * COALITION_TYPE_RESOURCE
158 static kern_return_t
i_coal_resource_init(coalition_t coal
, boolean_t privileged
);
159 static void i_coal_resource_dealloc(coalition_t coal
);
160 static kern_return_t
i_coal_resource_adopt_task(coalition_t coal
, task_t task
);
161 static kern_return_t
i_coal_resource_remove_task(coalition_t coal
, task_t task
);
162 static kern_return_t
i_coal_resource_set_taskrole(coalition_t coal
,
163 task_t task
, int role
);
164 static int i_coal_resource_get_taskrole(coalition_t coal
, task_t task
);
165 static void i_coal_resource_iterate_tasks(coalition_t coal
, void *ctx
,
166 void (*callback
)(coalition_t
, void *, task_t
));
168 struct i_resource_coalition
{
171 uint64_t byteswritten
;
174 uint64_t logical_immediate_writes
;
175 uint64_t logical_deferred_writes
;
176 uint64_t logical_invalidated_writes
;
177 uint64_t logical_metadata_writes
;
179 uint64_t task_count
; /* tasks that have started in this coalition */
180 uint64_t dead_task_count
; /* tasks that have exited in this coalition;
181 subtract from task_count to get count
184 * Count the length of time this coalition had at least one active task.
185 * This can be a 'denominator' to turn e.g. cpu_time to %cpu.
187 uint64_t last_became_nonempty_time
;
188 uint64_t time_nonempty
;
190 queue_head_t tasks
; /* List of active tasks in the coalition */
194 * COALITION_TYPE_JETSAM
197 static kern_return_t
i_coal_jetsam_init(coalition_t coal
, boolean_t privileged
);
198 static void i_coal_jetsam_dealloc(coalition_t coal
);
199 static kern_return_t
i_coal_jetsam_adopt_task(coalition_t coal
, task_t task
);
200 static kern_return_t
i_coal_jetsam_remove_task(coalition_t coal
, task_t task
);
201 static kern_return_t
i_coal_jetsam_set_taskrole(coalition_t coal
,
202 task_t task
, int role
);
203 static int i_coal_jetsam_get_taskrole(coalition_t coal
, task_t task
);
204 static void i_coal_jetsam_iterate_tasks(coalition_t coal
, void *ctx
,
205 void (*callback
)(coalition_t
, void *, task_t
));
207 struct i_jetsam_coalition
{
209 queue_head_t extensions
;
210 queue_head_t services
;
216 * main coalition structure
219 uint64_t id
; /* monotonically increasing */
221 uint32_t ref_count
; /* Number of references to the memory containing this struct */
222 uint32_t active_count
; /* Number of members of (tasks in) the
223 coalition, plus vouchers referring
225 uint32_t focal_task_count
; /* Number of TASK_FOREGROUND_APPLICATION tasks in the coalition */
226 uint32_t nonfocal_task_count
; /* Number of TASK_BACKGROUND_APPLICATION tasks in the coalition */
228 /* coalition flags */
229 uint32_t privileged
: 1; /* Members of this coalition may create
230 and manage coalitions and may posix_spawn
231 processes into selected coalitions */
234 uint32_t termrequested
: 1; /* launchd has requested termination when coalition becomes empty */
235 uint32_t terminated
: 1; /* coalition became empty and spawns are now forbidden */
236 uint32_t reaped
: 1; /* reaped, invisible to userspace, but waiting for ref_count to go to zero */
237 uint32_t notified
: 1; /* no-more-processes notification was sent via special port */
238 #if defined(DEVELOPMENT) || defined(DEBUG)
239 uint32_t should_notify
: 1; /* should this coalition send notifications (default: yes) */
242 queue_chain_t coalitions
; /* global list of coalitions */
244 decl_lck_mtx_data(,lock
) /* Coalition lock. */
246 /* put coalition type-specific structures here */
248 struct i_resource_coalition r
;
249 struct i_jetsam_coalition j
;
254 * register different coalition types:
255 * these must be kept in the order specified in coalition.h
257 static const struct coalition_type
258 s_coalition_types
[COALITION_NUM_TYPES
] = {
260 COALITION_TYPE_RESOURCE
,
262 i_coal_resource_init
,
263 i_coal_resource_dealloc
,
264 i_coal_resource_adopt_task
,
265 i_coal_resource_remove_task
,
266 i_coal_resource_set_taskrole
,
267 i_coal_resource_get_taskrole
,
268 i_coal_resource_iterate_tasks
,
271 COALITION_TYPE_JETSAM
,
274 i_coal_jetsam_dealloc
,
275 i_coal_jetsam_adopt_task
,
276 i_coal_jetsam_remove_task
,
277 i_coal_jetsam_set_taskrole
,
278 i_coal_jetsam_get_taskrole
,
279 i_coal_jetsam_iterate_tasks
,
283 #define coal_call(coal, func, ...) \
284 (s_coalition_types[(coal)->type].func)(coal, ## __VA_ARGS__)
287 #define coalition_lock(c) do{ lck_mtx_lock(&c->lock); }while(0)
288 #define coalition_unlock(c) do{ lck_mtx_unlock(&c->lock); }while(0)
291 coalition_notify_user(uint64_t id
, uint32_t flags
)
293 mach_port_t user_port
;
296 kr
= host_get_coalition_port(host_priv_self(), &user_port
);
297 if ((kr
!= KERN_SUCCESS
) || !IPC_PORT_VALID(user_port
)) {
301 coalition_notification(user_port
, id
, flags
);
302 ipc_port_release_send(user_port
);
307 * COALITION_TYPE_RESOURCE
311 i_coal_resource_init(coalition_t coal
, boolean_t privileged
)
314 assert(coal
&& coal
->type
== COALITION_TYPE_RESOURCE
);
315 coal
->r
.ledger
= ledger_instantiate(task_ledger_template
,
316 LEDGER_CREATE_ACTIVE_ENTRIES
);
317 if (coal
->r
.ledger
== NULL
)
318 return KERN_RESOURCE_SHORTAGE
;
320 queue_init(&coal
->r
.tasks
);
326 i_coal_resource_dealloc(coalition_t coal
)
328 assert(coal
&& coal
->type
== COALITION_TYPE_RESOURCE
);
329 ledger_dereference(coal
->r
.ledger
);
333 i_coal_resource_adopt_task(coalition_t coal
, task_t task
)
335 struct i_resource_coalition
*cr
;
337 assert(coal
&& coal
->type
== COALITION_TYPE_RESOURCE
);
338 assert(queue_empty(&task
->task_coalition
[COALITION_TYPE_RESOURCE
]));
343 if (cr
->task_count
< cr
->dead_task_count
) {
344 panic("%s: coalition %p id:%llu type:%s task_count(%llu) < dead_task_count(%llu)",
345 __func__
, coal
, coal
->id
, coal_type_str(coal
->type
),
346 cr
->task_count
, cr
->dead_task_count
);
349 /* If moving from 0->1 active tasks */
350 if (cr
->task_count
- cr
->dead_task_count
== 1) {
351 cr
->last_became_nonempty_time
= mach_absolute_time();
354 /* put the task on the coalition's list of tasks */
355 enqueue_tail(&cr
->tasks
, &task
->task_coalition
[COALITION_TYPE_RESOURCE
]);
357 coal_dbg("Added PID:%d to id:%llu, task_count:%llu, dead_count:%llu, nonempty_time:%llu",
358 task_pid(task
), coal
->id
, cr
->task_count
, cr
->dead_task_count
,
359 cr
->last_became_nonempty_time
);
365 i_coal_resource_remove_task(coalition_t coal
, task_t task
)
367 struct i_resource_coalition
*cr
;
369 assert(coal
&& coal
->type
== COALITION_TYPE_RESOURCE
);
370 assert(task
->coalition
[COALITION_TYPE_RESOURCE
] == coal
);
371 assert(!queue_empty(&task
->task_coalition
[COALITION_TYPE_RESOURCE
]));
374 * handle resource coalition accounting rollup for dead tasks
378 cr
->dead_task_count
++;
380 if (cr
->task_count
< cr
->dead_task_count
) {
381 panic("%s: coalition %p id:%llu type:%s task_count(%llu) < dead_task_count(%llu)",
382 __func__
, coal
, coal
->id
, coal_type_str(coal
->type
), cr
->task_count
, cr
->dead_task_count
);
385 /* If moving from 1->0 active tasks */
386 if (cr
->task_count
- cr
->dead_task_count
== 0) {
387 uint64_t last_time_nonempty
= mach_absolute_time() - cr
->last_became_nonempty_time
;
388 cr
->last_became_nonempty_time
= 0;
389 cr
->time_nonempty
+= last_time_nonempty
;
392 /* Do not roll up for exec'd task or exec copy task */
393 if (!task_is_exec_copy(task
) && !task_did_exec(task
)) {
394 ledger_rollup(cr
->ledger
, task
->ledger
);
395 cr
->bytesread
+= task
->task_io_stats
->disk_reads
.size
;
396 cr
->byteswritten
+= task
->task_io_stats
->total_io
.size
- task
->task_io_stats
->disk_reads
.size
;
397 cr
->gpu_time
+= task_gpu_utilisation(task
);
398 cr
->logical_immediate_writes
+= task
->task_immediate_writes
;
399 cr
->logical_deferred_writes
+= task
->task_deferred_writes
;
400 cr
->logical_invalidated_writes
+= task
->task_invalidated_writes
;
401 cr
->logical_metadata_writes
+= task
->task_metadata_writes
;
404 /* remove the task from the coalition's list */
405 remqueue(&task
->task_coalition
[COALITION_TYPE_RESOURCE
]);
406 queue_chain_init(task
->task_coalition
[COALITION_TYPE_RESOURCE
]);
408 coal_dbg("removed PID:%d from id:%llu, task_count:%llu, dead_count:%llu",
409 task_pid(task
), coal
->id
, cr
->task_count
, cr
->dead_task_count
);
415 i_coal_resource_set_taskrole(__unused coalition_t coal
,
416 __unused task_t task
, __unused
int role
)
422 i_coal_resource_get_taskrole(__unused coalition_t coal
, __unused task_t task
)
426 assert(coal
&& coal
->type
== COALITION_TYPE_RESOURCE
);
428 qe_foreach_element(t
, &coal
->r
.tasks
, task_coalition
[COALITION_TYPE_RESOURCE
]) {
430 return COALITION_TASKROLE_UNDEF
;
437 i_coal_resource_iterate_tasks(coalition_t coal
, void *ctx
, void (*callback
)(coalition_t
, void *, task_t
))
440 assert(coal
&& coal
->type
== COALITION_TYPE_RESOURCE
);
442 qe_foreach_element(t
, &coal
->r
.tasks
, task_coalition
[COALITION_TYPE_RESOURCE
])
443 callback(coal
, ctx
, t
);
447 coalition_resource_usage_internal(coalition_t coal
, struct coalition_resource_usage
*cru_out
)
450 ledger_amount_t credit
, debit
;
453 if (coal
->type
!= COALITION_TYPE_RESOURCE
)
454 return KERN_INVALID_ARGUMENT
;
456 /* Return KERN_INVALID_ARGUMENT for Corpse coalition */
457 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
458 if (coal
== corpse_coalition
[i
]) {
459 return KERN_INVALID_ARGUMENT
;
463 ledger_t sum_ledger
= ledger_instantiate(task_ledger_template
, LEDGER_CREATE_ACTIVE_ENTRIES
);
464 if (sum_ledger
== LEDGER_NULL
)
465 return KERN_RESOURCE_SHORTAGE
;
467 coalition_lock(coal
);
470 * Start with the coalition's ledger, which holds the totals from all
473 ledger_rollup(sum_ledger
, coal
->r
.ledger
);
474 uint64_t bytesread
= coal
->r
.bytesread
;
475 uint64_t byteswritten
= coal
->r
.byteswritten
;
476 uint64_t gpu_time
= coal
->r
.gpu_time
;
477 uint64_t energy
= coal
->r
.energy
;
478 uint64_t logical_immediate_writes
= coal
->r
.logical_immediate_writes
;
479 uint64_t logical_deferred_writes
= coal
->r
.logical_deferred_writes
;
480 uint64_t logical_invalidated_writes
= coal
->r
.logical_invalidated_writes
;
481 uint64_t logical_metadata_writes
= coal
->r
.logical_metadata_writes
;
482 int64_t cpu_time_billed_to_me
= 0;
483 int64_t cpu_time_billed_to_others
= 0;
485 kr
= ledger_get_balance(sum_ledger
, task_ledgers
.cpu_time_billed_to_me
, (int64_t *)&cpu_time_billed_to_me
);
486 if (kr
!= KERN_SUCCESS
|| cpu_time_billed_to_me
< 0) {
487 #if DEVELOPMENT || DEBUG
488 printf("ledger_get_balance failed or ledger negative in coalition_resource_usage_internal: %lld\n", cpu_time_billed_to_me
);
489 #endif /* DEVELOPMENT || DEBUG */
490 cpu_time_billed_to_me
= 0;
493 kr
= ledger_get_balance(sum_ledger
, task_ledgers
.cpu_time_billed_to_others
, (int64_t *)&cpu_time_billed_to_others
);
494 if (kr
!= KERN_SUCCESS
|| cpu_time_billed_to_others
< 0) {
495 #if DEVELOPMENT || DEBUG
496 printf("ledger_get_balance failed or ledger negative in coalition_resource_usage_internal: %lld\n", cpu_time_billed_to_others
);
497 #endif /* DEVELOPMENT || DEBUG */
498 cpu_time_billed_to_others
= 0;
502 * Add to that all the active tasks' ledgers. Tasks cannot deallocate
503 * out from under us, since we hold the coalition lock.
504 * Do not use the on-behalf of cpu time from ledger for live tasks, since
505 * it will not have cpu time for active linkages between tasks.
508 qe_foreach_element(task
, &coal
->r
.tasks
, task_coalition
[COALITION_TYPE_RESOURCE
]) {
510 * Rolling up stats for exec copy task or exec'd task will lead to double accounting.
511 * Cannot take task lock after taking coaliton lock
513 if (task_is_exec_copy(task
) || task_did_exec(task
)) {
517 ledger_rollup(sum_ledger
, task
->ledger
);
518 bytesread
+= task
->task_io_stats
->disk_reads
.size
;
519 byteswritten
+= task
->task_io_stats
->total_io
.size
- task
->task_io_stats
->disk_reads
.size
;
520 gpu_time
+= task_gpu_utilisation(task
);
521 logical_immediate_writes
+= task
->task_immediate_writes
;
522 logical_deferred_writes
+= task
->task_deferred_writes
;
523 logical_invalidated_writes
+= task
->task_invalidated_writes
;
524 logical_metadata_writes
+= task
->task_metadata_writes
;
525 cpu_time_billed_to_me
+= (int64_t)bank_billed_time_safe(task
);
526 cpu_time_billed_to_others
+= (int64_t)bank_serviced_time_safe(task
);
529 /* collect information from the coalition itself */
530 cru_out
->tasks_started
= coal
->r
.task_count
;
531 cru_out
->tasks_exited
= coal
->r
.dead_task_count
;
533 uint64_t time_nonempty
= coal
->r
.time_nonempty
;
534 uint64_t last_became_nonempty_time
= coal
->r
.last_became_nonempty_time
;
536 coalition_unlock(coal
);
538 /* Copy the totals out of sum_ledger */
539 kr
= ledger_get_entries(sum_ledger
, task_ledgers
.cpu_time
,
541 if (kr
!= KERN_SUCCESS
) {
544 cru_out
->cpu_time
= credit
;
545 cru_out
->cpu_time_billed_to_me
= (uint64_t)cpu_time_billed_to_me
;
546 cru_out
->cpu_time_billed_to_others
= (uint64_t)cpu_time_billed_to_others
;
548 kr
= ledger_get_entries(sum_ledger
, task_ledgers
.interrupt_wakeups
,
550 if (kr
!= KERN_SUCCESS
) {
553 cru_out
->interrupt_wakeups
= credit
;
555 kr
= ledger_get_entries(sum_ledger
, task_ledgers
.platform_idle_wakeups
,
557 if (kr
!= KERN_SUCCESS
) {
560 cru_out
->platform_idle_wakeups
= credit
;
562 cru_out
->bytesread
= bytesread
;
563 cru_out
->byteswritten
= byteswritten
;
564 cru_out
->gpu_time
= gpu_time
;
565 cru_out
->energy
= energy
;
566 cru_out
->logical_immediate_writes
= logical_immediate_writes
;
567 cru_out
->logical_deferred_writes
= logical_deferred_writes
;
568 cru_out
->logical_invalidated_writes
= logical_invalidated_writes
;
569 cru_out
->logical_metadata_writes
= logical_metadata_writes
;
571 ledger_dereference(sum_ledger
);
572 sum_ledger
= LEDGER_NULL
;
574 if (last_became_nonempty_time
) {
575 time_nonempty
+= mach_absolute_time() - last_became_nonempty_time
;
577 absolutetime_to_nanoseconds(time_nonempty
, &cru_out
->time_nonempty
);
584 * COALITION_TYPE_JETSAM
588 i_coal_jetsam_init(coalition_t coal
, boolean_t privileged
)
590 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
593 coal
->j
.leader
= TASK_NULL
;
594 queue_head_init(coal
->j
.extensions
);
595 queue_head_init(coal
->j
.services
);
596 queue_head_init(coal
->j
.other
);
602 i_coal_jetsam_dealloc(__unused coalition_t coal
)
604 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
606 /* the coalition should be completely clear at this point */
607 assert(queue_empty(&coal
->j
.extensions
));
608 assert(queue_empty(&coal
->j
.services
));
609 assert(queue_empty(&coal
->j
.other
));
610 assert(coal
->j
.leader
== TASK_NULL
);
614 i_coal_jetsam_adopt_task(coalition_t coal
, task_t task
)
616 struct i_jetsam_coalition
*cj
;
617 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
621 assert(queue_empty(&task
->task_coalition
[COALITION_TYPE_JETSAM
]));
623 /* put each task initially in the "other" list */
624 enqueue_tail(&cj
->other
, &task
->task_coalition
[COALITION_TYPE_JETSAM
]);
625 coal_dbg("coalition %lld adopted PID:%d as UNDEF",
626 coal
->id
, task_pid(task
));
632 i_coal_jetsam_remove_task(coalition_t coal
, task_t task
)
634 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
635 assert(task
->coalition
[COALITION_TYPE_JETSAM
] == coal
);
637 coal_dbg("removing PID:%d from coalition id:%lld",
638 task_pid(task
), coal
->id
);
640 if (task
== coal
->j
.leader
) {
641 coal
->j
.leader
= NULL
;
642 coal_dbg(" PID:%d was the leader!", task_pid(task
));
644 assert(!queue_empty(&task
->task_coalition
[COALITION_TYPE_JETSAM
]));
647 /* remove the task from the specific coalition role queue */
648 remqueue(&task
->task_coalition
[COALITION_TYPE_JETSAM
]);
649 queue_chain_init(task
->task_coalition
[COALITION_TYPE_RESOURCE
]);
655 i_coal_jetsam_set_taskrole(coalition_t coal
, task_t task
, int role
)
657 struct i_jetsam_coalition
*cj
;
659 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
660 assert(task
->coalition
[COALITION_TYPE_JETSAM
] == coal
);
665 case COALITION_TASKROLE_LEADER
:
666 coal_dbg("setting PID:%d as LEADER of %lld",
667 task_pid(task
), coal
->id
);
668 if (cj
->leader
!= TASK_NULL
) {
669 /* re-queue the exiting leader onto the "other" list */
670 coal_dbg(" re-queue existing leader (%d) as OTHER",
671 task_pid(cj
->leader
));
672 re_queue_tail(&cj
->other
, &cj
->leader
->task_coalition
[COALITION_TYPE_JETSAM
]);
675 * remove the task from the "other" list
676 * (where it was put by default)
678 remqueue(&task
->task_coalition
[COALITION_TYPE_JETSAM
]);
679 queue_chain_init(task
->task_coalition
[COALITION_TYPE_JETSAM
]);
681 /* set the coalition leader */
684 case COALITION_TASKROLE_UNDEF
:
685 coal_dbg("setting PID:%d as UNDEF in %lld",
686 task_pid(task
), coal
->id
);
687 q
= (queue_t
)&cj
->other
;
689 case COALITION_TASKROLE_XPC
:
690 coal_dbg("setting PID:%d as XPC in %lld",
691 task_pid(task
), coal
->id
);
692 q
= (queue_t
)&cj
->services
;
694 case COALITION_TASKROLE_EXT
:
695 coal_dbg("setting PID:%d as EXT in %lld",
696 task_pid(task
), coal
->id
);
697 q
= (queue_t
)&cj
->extensions
;
700 panic("%s: invalid role(%d) for task", __func__
, role
);
701 return KERN_INVALID_ARGUMENT
;
705 re_queue_tail(q
, &task
->task_coalition
[COALITION_TYPE_JETSAM
]);
711 i_coal_jetsam_get_taskrole(coalition_t coal
, task_t task
)
713 struct i_jetsam_coalition
*cj
;
716 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
717 assert(task
->coalition
[COALITION_TYPE_JETSAM
] == coal
);
721 if (task
== cj
->leader
)
722 return COALITION_TASKROLE_LEADER
;
724 qe_foreach_element(t
, &cj
->services
, task_coalition
[COALITION_TYPE_JETSAM
]) {
726 return COALITION_TASKROLE_XPC
;
729 qe_foreach_element(t
, &cj
->extensions
, task_coalition
[COALITION_TYPE_JETSAM
]) {
731 return COALITION_TASKROLE_EXT
;
734 qe_foreach_element(t
, &cj
->other
, task_coalition
[COALITION_TYPE_JETSAM
]) {
736 return COALITION_TASKROLE_UNDEF
;
739 /* task not in the coalition?! */
744 i_coal_jetsam_iterate_tasks(coalition_t coal
, void *ctx
, void (*callback
)(coalition_t
, void *, task_t
))
746 struct i_jetsam_coalition
*cj
;
749 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
754 callback(coal
, ctx
, cj
->leader
);
756 qe_foreach_element(t
, &cj
->services
, task_coalition
[COALITION_TYPE_JETSAM
])
757 callback(coal
, ctx
, t
);
759 qe_foreach_element(t
, &cj
->extensions
, task_coalition
[COALITION_TYPE_JETSAM
])
760 callback(coal
, ctx
, t
);
762 qe_foreach_element(t
, &cj
->other
, task_coalition
[COALITION_TYPE_JETSAM
])
763 callback(coal
, ctx
, t
);
769 * Main Coalition implementation
774 * coalition_create_internal
775 * Returns: New coalition object, referenced for the caller and unlocked.
776 * Condition: coalitions_list_lock must be UNLOCKED.
779 coalition_create_internal(int type
, boolean_t privileged
, coalition_t
*out
)
782 struct coalition
*new_coal
;
784 if (type
< 0 || type
> COALITION_TYPE_MAX
)
785 return KERN_INVALID_ARGUMENT
;
787 new_coal
= (struct coalition
*)zalloc(coalition_zone
);
788 if (new_coal
== COALITION_NULL
)
789 return KERN_RESOURCE_SHORTAGE
;
790 bzero(new_coal
, sizeof(*new_coal
));
792 new_coal
->type
= type
;
794 /* initialize type-specific resources */
795 kr
= coal_call(new_coal
, init
, privileged
);
796 if (kr
!= KERN_SUCCESS
) {
797 zfree(coalition_zone
, new_coal
);
801 /* One for caller, one for coalitions list */
802 new_coal
->ref_count
= 2;
804 new_coal
->privileged
= privileged
? TRUE
: FALSE
;
805 #if defined(DEVELOPMENT) || defined(DEBUG)
806 new_coal
->should_notify
= 1;
809 lck_mtx_init(&new_coal
->lock
, &coalitions_lck_grp
, &coalitions_lck_attr
);
811 lck_mtx_lock(&coalitions_list_lock
);
812 new_coal
->id
= coalition_next_id
++;
814 enqueue_tail(&coalitions_q
, &new_coal
->coalitions
);
815 lck_mtx_unlock(&coalitions_list_lock
);
817 coal_dbg("id:%llu, type:%s", new_coal
->id
, coal_type_str(new_coal
->type
));
825 * Condition: coalition must be UNLOCKED.
828 coalition_release(coalition_t coal
)
830 /* TODO: This can be done with atomics. */
831 coalition_lock(coal
);
835 uint32_t rc
= coal
->ref_count
;
836 uint32_t ac
= coal
->active_count
;
837 #endif /* COALITION_DEBUG */
839 coal_dbg("id:%llu type:%s ref_count:%u active_count:%u%s",
840 coal
->id
, coal_type_str(coal
->type
), rc
, ac
,
841 rc
<= 0 ? ", will deallocate now" : "");
843 if (coal
->ref_count
> 0) {
844 coalition_unlock(coal
);
848 assert(coal
->termrequested
);
849 assert(coal
->terminated
);
850 assert(coal
->active_count
== 0);
851 assert(coal
->reaped
);
852 assert(coal
->focal_task_count
== 0);
853 assert(coal
->nonfocal_task_count
== 0);
855 coal_call(coal
, dealloc
);
857 coalition_unlock(coal
);
859 lck_mtx_destroy(&coal
->lock
, &coalitions_lck_grp
);
861 zfree(coalition_zone
, coal
);
865 * coalition_find_by_id_internal
866 * Returns: Coalition object with specified id, NOT referenced.
867 * If not found, returns COALITION_NULL.
868 * Condition: coalitions_list_lock must be LOCKED.
871 coalition_find_by_id_internal(uint64_t coal_id
)
874 return COALITION_NULL
;
877 lck_mtx_assert(&coalitions_list_lock
, LCK_MTX_ASSERT_OWNED
);
879 qe_foreach_element(coal
, &coalitions_q
, coalitions
) {
880 if (coal
->id
== coal_id
) {
884 return COALITION_NULL
;
888 * coalition_find_by_id
889 * Returns: Coalition object with specified id, referenced.
890 * Condition: coalitions_list_lock must be UNLOCKED.
893 coalition_find_by_id(uint64_t cid
)
896 return COALITION_NULL
;
899 lck_mtx_lock(&coalitions_list_lock
);
901 coalition_t coal
= coalition_find_by_id_internal(cid
);
902 if (coal
== COALITION_NULL
) {
903 lck_mtx_unlock(&coalitions_list_lock
);
904 return COALITION_NULL
;
907 coalition_lock(coal
);
910 coalition_unlock(coal
);
911 lck_mtx_unlock(&coalitions_list_lock
);
912 return COALITION_NULL
;
915 if (coal
->ref_count
== 0) {
916 panic("resurrecting coalition %p id:%llu type:%s, active_count:%u\n",
917 coal
, coal
->id
, coal_type_str(coal
->type
), coal
->active_count
);
921 uint32_t rc
= coal
->ref_count
;
924 coalition_unlock(coal
);
925 lck_mtx_unlock(&coalitions_list_lock
);
927 coal_dbg("id:%llu type:%s ref_count:%u",
928 coal
->id
, coal_type_str(coal
->type
), rc
);
934 * coalition_find_and_activate_by_id
935 * Returns: Coalition object with specified id, referenced, and activated.
936 * Condition: coalitions_list_lock must be UNLOCKED.
937 * This is the function to use when putting a 'new' thing into a coalition,
938 * like posix_spawn of an XPC service by launchd.
939 * See also coalition_extend_active.
942 coalition_find_and_activate_by_id(uint64_t cid
)
945 return COALITION_NULL
;
948 lck_mtx_lock(&coalitions_list_lock
);
950 coalition_t coal
= coalition_find_by_id_internal(cid
);
951 if (coal
== COALITION_NULL
) {
952 lck_mtx_unlock(&coalitions_list_lock
);
953 return COALITION_NULL
;
956 coalition_lock(coal
);
958 if (coal
->reaped
|| coal
->terminated
) {
959 /* Too late to put something new into this coalition, it's
960 * already on its way out the door */
961 coalition_unlock(coal
);
962 lck_mtx_unlock(&coalitions_list_lock
);
963 return COALITION_NULL
;
966 if (coal
->ref_count
== 0) {
967 panic("resurrecting coalition %p id:%llu type:%s, active_count:%u\n",
968 coal
, coal
->id
, coal_type_str(coal
->type
), coal
->active_count
);
972 coal
->active_count
++;
975 uint32_t rc
= coal
->ref_count
;
976 uint32_t ac
= coal
->active_count
;
979 coalition_unlock(coal
);
980 lck_mtx_unlock(&coalitions_list_lock
);
982 coal_dbg("id:%llu type:%s ref_count:%u, active_count:%u",
983 coal
->id
, coal_type_str(coal
->type
), rc
, ac
);
989 coalition_id(coalition_t coal
)
995 task_coalition_ids(task_t task
, uint64_t ids
[COALITION_NUM_TYPES
])
998 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
999 if (task
->coalition
[i
])
1000 ids
[i
] = task
->coalition
[i
]->id
;
1007 task_coalition_roles(task_t task
, int roles
[COALITION_NUM_TYPES
])
1010 memset(roles
, 0, COALITION_NUM_TYPES
* sizeof(roles
[0]));
1012 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
1013 if (task
->coalition
[i
]) {
1014 coalition_lock(task
->coalition
[i
]);
1015 roles
[i
] = coal_call(task
->coalition
[i
],
1016 get_taskrole
, task
);
1017 coalition_unlock(task
->coalition
[i
]);
1026 coalition_type(coalition_t coal
)
1032 coalition_is_privileged(coalition_t coal
)
1034 return coal
->privileged
|| unrestrict_coalition_syscalls
;
1038 task_is_in_privileged_coalition(task_t task
, int type
)
1040 if (type
< 0 || type
> COALITION_TYPE_MAX
)
1042 if (unrestrict_coalition_syscalls
)
1044 if (!task
->coalition
[type
])
1046 return task
->coalition
[type
]->privileged
;
1049 void task_coalition_update_gpu_stats(task_t task
, uint64_t gpu_ns_delta
)
1053 assert(task
!= TASK_NULL
);
1054 if (gpu_ns_delta
== 0)
1057 coal
= task
->coalition
[COALITION_TYPE_RESOURCE
];
1058 assert(coal
!= COALITION_NULL
);
1060 coalition_lock(coal
);
1061 coal
->r
.gpu_time
+= gpu_ns_delta
;
1062 coalition_unlock(coal
);
1065 uint32_t task_coalition_adjust_focal_count(task_t task
, int count
)
1071 * For now: only use the resource coalition. Perhaps in the
1072 * future we may combine all coalition types, or even make
1073 * a special coalition type just for this.
1075 coal
= task
->coalition
[COALITION_TYPE_RESOURCE
];
1076 assert(coal
!= COALITION_NULL
);
1078 ret
= hw_atomic_add(&coal
->focal_task_count
, count
);
1080 /* catch underflow */
1081 assert(ret
!= UINT32_MAX
);
1085 uint32_t task_coalition_focal_count(task_t task
)
1088 coal
= task
->coalition
[COALITION_TYPE_RESOURCE
];
1089 assert(coal
!= COALITION_NULL
);
1091 return coal
->focal_task_count
;
1094 uint32_t task_coalition_adjust_nonfocal_count(task_t task
, int count
)
1100 * For now: only use the resource coalition. Perhaps in the
1101 * future we may combine all coalition types, or even make
1102 * a special coalition type just for this.
1104 coal
= task
->coalition
[COALITION_TYPE_RESOURCE
];
1105 assert(coal
!= COALITION_NULL
);
1107 ret
= hw_atomic_add(&coal
->nonfocal_task_count
, count
);
1109 /* catch underflow */
1110 assert(ret
!= UINT32_MAX
);
1114 uint32_t task_coalition_nonfocal_count(task_t task
)
1117 coal
= task
->coalition
[COALITION_TYPE_RESOURCE
];
1118 assert(coal
!= COALITION_NULL
);
1120 return coal
->nonfocal_task_count
;
1123 void coalition_for_each_task(coalition_t coal
, void *ctx
,
1124 void (*callback
)(coalition_t
, void *, task_t
))
1126 assert(coal
!= COALITION_NULL
);
1128 coal_dbg("iterating tasks in coalition %p id:%llu type:%s, active_count:%u",
1129 coal
, coal
->id
, coal_type_str(coal
->type
), coal
->active_count
);
1131 coalition_lock(coal
);
1133 coal_call(coal
, iterate_tasks
, ctx
, callback
);
1135 coalition_unlock(coal
);
1140 coalition_remove_active(coalition_t coal
)
1142 coalition_lock(coal
);
1144 assert(!coal
->reaped
);
1145 assert(coal
->active_count
> 0);
1147 coal
->active_count
--;
1149 boolean_t do_notify
= FALSE
;
1150 uint64_t notify_id
= 0;
1151 uint32_t notify_flags
= 0;
1152 if (coal
->termrequested
&& coal
->active_count
== 0) {
1153 /* We only notify once, when active_count reaches zero.
1154 * We just decremented, so if it reached zero, we mustn't have
1157 assert(!coal
->terminated
);
1158 coal
->terminated
= TRUE
;
1160 assert(!coal
->notified
);
1162 coal
->notified
= TRUE
;
1163 #if defined(DEVELOPMENT) || defined(DEBUG)
1164 do_notify
= coal
->should_notify
;
1168 notify_id
= coal
->id
;
1173 uint64_t cid
= coal
->id
;
1174 uint32_t rc
= coal
->ref_count
;
1175 int ac
= coal
->active_count
;
1176 int ct
= coal
->type
;
1178 coalition_unlock(coal
);
1180 coal_dbg("id:%llu type:%s ref_count:%u, active_count:%u,%s",
1181 cid
, coal_type_str(ct
), rc
, ac
, do_notify
? " NOTIFY" : " ");
1184 coalition_notify_user(notify_id
, notify_flags
);
1188 /* Used for kernel_task, launchd, launchd's early boot tasks... */
1190 coalitions_adopt_init_task(task_t task
)
1193 kr
= coalitions_adopt_task(init_coalition
, task
);
1194 if (kr
!= KERN_SUCCESS
) {
1195 panic("failed to adopt task %p into default coalition: %d", task
, kr
);
1200 /* Used for forked corpses. */
1202 coalitions_adopt_corpse_task(task_t task
)
1205 kr
= coalitions_adopt_task(corpse_coalition
, task
);
1206 if (kr
!= KERN_SUCCESS
) {
1207 panic("failed to adopt task %p into corpse coalition: %d", task
, kr
);
1213 * coalition_adopt_task_internal
1214 * Condition: Coalition must be referenced and unlocked. Will fail if coalition
1215 * is already terminated.
1217 static kern_return_t
1218 coalition_adopt_task_internal(coalition_t coal
, task_t task
)
1222 if (task
->coalition
[coal
->type
]) {
1223 return KERN_ALREADY_IN_SET
;
1226 coalition_lock(coal
);
1228 if (coal
->reaped
|| coal
->terminated
) {
1229 coalition_unlock(coal
);
1230 return KERN_TERMINATED
;
1233 kr
= coal_call(coal
, adopt_task
, task
);
1234 if (kr
!= KERN_SUCCESS
)
1237 coal
->active_count
++;
1241 task
->coalition
[coal
->type
] = coal
;
1245 (void)coal
; /* need expression after label */
1246 uint64_t cid
= coal
->id
;
1247 uint32_t rc
= coal
->ref_count
;
1248 uint32_t ct
= coal
->type
;
1250 coalition_unlock(coal
);
1252 coal_dbg("task:%d, id:%llu type:%s ref_count:%u, kr=%d",
1253 task_pid(task
), cid
, coal_type_str(ct
), rc
, kr
);
1257 static kern_return_t
1258 coalition_remove_task_internal(task_t task
, int type
)
1262 coalition_t coal
= task
->coalition
[type
];
1265 return KERN_SUCCESS
;
1267 assert(coal
->type
== (uint32_t)type
);
1269 coalition_lock(coal
);
1271 kr
= coal_call(coal
, remove_task
, task
);
1274 uint64_t cid
= coal
->id
;
1275 uint32_t rc
= coal
->ref_count
;
1276 int ac
= coal
->active_count
;
1277 int ct
= coal
->type
;
1279 coalition_unlock(coal
);
1281 coal_dbg("id:%llu type:%s ref_count:%u, active_count:%u, kr=%d",
1282 cid
, coal_type_str(ct
), rc
, ac
, kr
);
1284 coalition_remove_active(coal
);
1290 * coalitions_adopt_task
1291 * Condition: All coalitions must be referenced and unlocked.
1292 * Will fail if any coalition is already terminated.
1295 coalitions_adopt_task(coalition_t
*coals
, task_t task
)
1300 if (!coals
|| coals
[COALITION_TYPE_RESOURCE
] == COALITION_NULL
)
1301 return KERN_INVALID_ARGUMENT
;
1303 /* verify that the incoming coalitions are what they say they are */
1304 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++)
1305 if (coals
[i
] && coals
[i
]->type
!= (uint32_t)i
)
1306 return KERN_INVALID_ARGUMENT
;
1308 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
1311 kr
= coalition_adopt_task_internal(coals
[i
], task
);
1312 if (kr
!= KERN_SUCCESS
) {
1313 /* dis-associate any coalitions that just adopted this task */
1315 if (task
->coalition
[i
])
1316 coalition_remove_task_internal(task
, i
);
1325 * coalitions_remove_task
1326 * Condition: task must be referenced and UNLOCKED; all task's coalitions must be UNLOCKED
1329 coalitions_remove_task(task_t task
)
1334 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
1335 kr
= coalition_remove_task_internal(task
, i
);
1336 assert(kr
== KERN_SUCCESS
);
1343 * task_release_coalitions
1344 * helper function to release references to all coalitions in which
1345 * 'task' is a member.
1348 task_release_coalitions(task_t task
)
1351 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
1352 if (task
->coalition
[i
]) {
1353 coalition_release(task
->coalition
[i
]);
1354 } else if (i
== COALITION_TYPE_RESOURCE
) {
1355 panic("deallocating task %p was not a member of a resource coalition", task
);
1361 * coalitions_set_roles
1362 * for each type of coalition, if the task is a member of a coalition of
1363 * that type (given in the coalitions parameter) then set the role of
1364 * the task within that that coalition.
1366 kern_return_t
coalitions_set_roles(coalition_t coalitions
[COALITION_NUM_TYPES
],
1367 task_t task
, int roles
[COALITION_NUM_TYPES
])
1369 kern_return_t kr
= KERN_SUCCESS
;
1372 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
1375 coalition_lock(coalitions
[i
]);
1376 kr
= coal_call(coalitions
[i
], set_taskrole
, task
, roles
[i
]);
1377 coalition_unlock(coalitions
[i
]);
1378 assert(kr
== KERN_SUCCESS
);
1385 * coalition_terminate_internal
1386 * Condition: Coalition must be referenced and UNLOCKED.
1389 coalition_request_terminate_internal(coalition_t coal
)
1391 assert(coal
->type
>= 0 && coal
->type
<= COALITION_TYPE_MAX
);
1393 if (coal
== init_coalition
[coal
->type
]) {
1394 return KERN_DEFAULT_SET
;
1397 coalition_lock(coal
);
1400 coalition_unlock(coal
);
1401 return KERN_INVALID_NAME
;
1404 if (coal
->terminated
|| coal
->termrequested
) {
1405 coalition_unlock(coal
);
1406 return KERN_TERMINATED
;
1409 coal
->termrequested
= TRUE
;
1411 boolean_t do_notify
= FALSE
;
1412 uint64_t note_id
= 0;
1413 uint32_t note_flags
= 0;
1415 if (coal
->active_count
== 0) {
1417 * We only notify once, when active_count reaches zero.
1418 * We just set termrequested to zero. If the active count
1419 * was already at zero (tasks died before we could request
1420 * a termination notification), we should notify.
1422 assert(!coal
->terminated
);
1423 coal
->terminated
= TRUE
;
1425 assert(!coal
->notified
);
1427 coal
->notified
= TRUE
;
1428 #if defined(DEVELOPMENT) || defined(DEBUG)
1429 do_notify
= coal
->should_notify
;
1437 coalition_unlock(coal
);
1440 coalition_notify_user(note_id
, note_flags
);
1443 return KERN_SUCCESS
;
1447 * coalition_reap_internal
1448 * Condition: Coalition must be referenced and UNLOCKED.
1451 coalition_reap_internal(coalition_t coal
)
1453 assert(coal
->type
<= COALITION_TYPE_MAX
);
1455 if (coal
== init_coalition
[coal
->type
]) {
1456 return KERN_DEFAULT_SET
;
1459 coalition_lock(coal
);
1461 coalition_unlock(coal
);
1462 return KERN_TERMINATED
;
1464 if (!coal
->terminated
) {
1465 coalition_unlock(coal
);
1466 return KERN_FAILURE
;
1468 assert(coal
->termrequested
);
1469 if (coal
->active_count
> 0) {
1470 coalition_unlock(coal
);
1471 return KERN_FAILURE
;
1474 coal
->reaped
= TRUE
;
1476 /* Caller, launchd, and coalitions list should each have a reference */
1477 assert(coal
->ref_count
> 2);
1479 coalition_unlock(coal
);
1481 lck_mtx_lock(&coalitions_list_lock
);
1483 remqueue(&coal
->coalitions
);
1484 lck_mtx_unlock(&coalitions_list_lock
);
1486 /* Release the list's reference and launchd's reference. */
1487 coalition_release(coal
);
1488 coalition_release(coal
);
1490 return KERN_SUCCESS
;
1493 #if defined(DEVELOPMENT) || defined(DEBUG)
1494 int coalition_should_notify(coalition_t coal
)
1499 coalition_lock(coal
);
1500 should
= coal
->should_notify
;
1501 coalition_unlock(coal
);
1506 void coalition_set_notify(coalition_t coal
, int notify
)
1510 coalition_lock(coal
);
1511 coal
->should_notify
= !!notify
;
1512 coalition_unlock(coal
);
1517 coalitions_init(void)
1521 const struct coalition_type
*ctype
;
1523 coalition_zone
= zinit(
1524 sizeof(struct coalition
),
1525 CONFIG_COALITION_MAX
* sizeof(struct coalition
),
1526 COALITION_CHUNK
* sizeof(struct coalition
),
1528 zone_change(coalition_zone
, Z_NOENCRYPT
, TRUE
);
1529 queue_head_init(coalitions_q
);
1531 if (!PE_parse_boot_argn("unrestrict_coalition_syscalls", &unrestrict_coalition_syscalls
,
1532 sizeof (unrestrict_coalition_syscalls
))) {
1533 unrestrict_coalition_syscalls
= 0;
1536 lck_grp_attr_setdefault(&coalitions_lck_grp_attr
);
1537 lck_grp_init(&coalitions_lck_grp
, "coalition", &coalitions_lck_grp_attr
);
1538 lck_attr_setdefault(&coalitions_lck_attr
);
1539 lck_mtx_init(&coalitions_list_lock
, &coalitions_lck_grp
, &coalitions_lck_attr
);
1541 init_task_ledgers();
1543 for (i
= 0, ctype
= &s_coalition_types
[0]; i
< COALITION_NUM_TYPES
; ctype
++, i
++) {
1544 /* verify the entry in the global coalition types array */
1545 if (ctype
->type
!= i
||
1548 !ctype
->adopt_task
||
1549 !ctype
->remove_task
) {
1550 panic("%s: Malformed coalition type %s(%d) in slot for type:%s(%d)",
1551 __func__
, coal_type_str(ctype
->type
), ctype
->type
, coal_type_str(i
), i
);
1553 if (!ctype
->has_default
)
1555 kr
= coalition_create_internal(ctype
->type
, TRUE
, &init_coalition
[ctype
->type
]);
1556 if (kr
!= KERN_SUCCESS
)
1557 panic("%s: could not create init %s coalition: kr:%d",
1558 __func__
, coal_type_str(i
), kr
);
1559 kr
= coalition_create_internal(ctype
->type
, FALSE
, &corpse_coalition
[ctype
->type
]);
1560 if (kr
!= KERN_SUCCESS
)
1561 panic("%s: could not create corpse %s coalition: kr:%d",
1562 __func__
, coal_type_str(i
), kr
);
1565 /* "Leak" our reference to the global object */
1569 * BSD Kernel interface functions
1572 static void coalition_fill_procinfo(struct coalition
*coal
,
1573 struct procinfo_coalinfo
*coalinfo
)
1575 coalinfo
->coalition_id
= coal
->id
;
1576 coalinfo
->coalition_type
= coal
->type
;
1577 coalinfo
->coalition_tasks
= coalition_get_task_count(coal
);
1581 int coalitions_get_list(int type
, struct procinfo_coalinfo
*coal_list
, int list_sz
)
1584 struct coalition
*coal
;
1586 lck_mtx_lock(&coalitions_list_lock
);
1587 qe_foreach_element(coal
, &coalitions_q
, coalitions
) {
1588 if (!coal
->reaped
&& (type
< 0 || type
== (int)coal
->type
)) {
1589 if (coal_list
&& ncoals
< list_sz
)
1590 coalition_fill_procinfo(coal
, &coal_list
[ncoals
]);
1594 lck_mtx_unlock(&coalitions_list_lock
);
1600 * Jetsam coalition interface
1603 boolean_t
coalition_is_leader(task_t task
, int coal_type
, coalition_t
*coal
)
1608 if (coal
) /* handle the error cases gracefully */
1609 *coal
= COALITION_NULL
;
1614 if (coal_type
> COALITION_TYPE_MAX
)
1617 c
= task
->coalition
[coal_type
];
1621 assert((int)c
->type
== coal_type
);
1629 if (c
->type
== COALITION_TYPE_JETSAM
&& c
->j
.leader
== task
)
1632 coalition_unlock(c
);
1638 task_t
coalition_get_leader(coalition_t coal
)
1640 task_t leader
= TASK_NULL
;
1645 coalition_lock(coal
);
1646 if (coal
->type
!= COALITION_TYPE_JETSAM
)
1649 leader
= coal
->j
.leader
;
1650 if (leader
!= TASK_NULL
)
1651 task_reference(leader
);
1654 coalition_unlock(coal
);
1659 int coalition_get_task_count(coalition_t coal
)
1662 struct queue_entry
*qe
;
1666 coalition_lock(coal
);
1667 switch (coal
->type
) {
1668 case COALITION_TYPE_RESOURCE
:
1669 qe_foreach(qe
, &coal
->r
.tasks
)
1672 case COALITION_TYPE_JETSAM
:
1675 qe_foreach(qe
, &coal
->j
.other
)
1677 qe_foreach(qe
, &coal
->j
.extensions
)
1679 qe_foreach(qe
, &coal
->j
.services
)
1685 coalition_unlock(coal
);
1691 static uint64_t i_get_list_footprint(queue_t list
, int type
, int *ntasks
)
1696 qe_foreach_element(task
, list
, task_coalition
[type
]) {
1697 bytes
+= get_task_phys_footprint(task
);
1698 coal_dbg(" [%d] task_pid:%d, type:%d, footprint:%lld",
1699 *ntasks
, task_pid(task
), type
, bytes
);
1706 uint64_t coalition_get_page_count(coalition_t coal
, int *ntasks
)
1716 coalition_lock(coal
);
1718 switch (coal
->type
) {
1719 case COALITION_TYPE_RESOURCE
:
1720 bytes
+= i_get_list_footprint(&coal
->r
.tasks
, COALITION_TYPE_RESOURCE
, &num_tasks
);
1722 case COALITION_TYPE_JETSAM
:
1723 if (coal
->j
.leader
) {
1724 bytes
+= get_task_phys_footprint(coal
->j
.leader
);
1727 bytes
+= i_get_list_footprint(&coal
->j
.extensions
, COALITION_TYPE_JETSAM
, &num_tasks
);
1728 bytes
+= i_get_list_footprint(&coal
->j
.services
, COALITION_TYPE_JETSAM
, &num_tasks
);
1729 bytes
+= i_get_list_footprint(&coal
->j
.other
, COALITION_TYPE_JETSAM
, &num_tasks
);
1735 coalition_unlock(coal
);
1738 *ntasks
= num_tasks
;
1740 return bytes
/ PAGE_SIZE_64
;
1743 struct coal_sort_s
{
1750 * return < 0 for a < b
1754 typedef int (*cmpfunc_t
)(const void *a
, const void *b
);
1757 qsort(void *a
, size_t n
, size_t es
, cmpfunc_t cmp
);
1759 static int dflt_cmp(const void *a
, const void *b
)
1761 const struct coal_sort_s
*csA
= (const struct coal_sort_s
*)a
;
1762 const struct coal_sort_s
*csB
= (const struct coal_sort_s
*)b
;
1765 * if both A and B are equal, use a memory descending sort
1767 if (csA
->usr_order
== csB
->usr_order
)
1768 return (int)((int64_t)csB
->bytes
- (int64_t)csA
->bytes
);
1770 /* otherwise, return the relationship between user specified orders */
1771 return (csA
->usr_order
- csB
->usr_order
);
1774 static int mem_asc_cmp(const void *a
, const void *b
)
1776 const struct coal_sort_s
*csA
= (const struct coal_sort_s
*)a
;
1777 const struct coal_sort_s
*csB
= (const struct coal_sort_s
*)b
;
1779 return (int)((int64_t)csA
->bytes
- (int64_t)csB
->bytes
);
1782 static int mem_dec_cmp(const void *a
, const void *b
)
1784 const struct coal_sort_s
*csA
= (const struct coal_sort_s
*)a
;
1785 const struct coal_sort_s
*csB
= (const struct coal_sort_s
*)b
;
1787 return (int)((int64_t)csB
->bytes
- (int64_t)csA
->bytes
);
1790 static int usr_asc_cmp(const void *a
, const void *b
)
1792 const struct coal_sort_s
*csA
= (const struct coal_sort_s
*)a
;
1793 const struct coal_sort_s
*csB
= (const struct coal_sort_s
*)b
;
1795 return (csA
->usr_order
- csB
->usr_order
);
1798 static int usr_dec_cmp(const void *a
, const void *b
)
1800 const struct coal_sort_s
*csA
= (const struct coal_sort_s
*)a
;
1801 const struct coal_sort_s
*csB
= (const struct coal_sort_s
*)b
;
1803 return (csB
->usr_order
- csA
->usr_order
);
1806 /* avoid dynamic allocation in this path */
1807 #define MAX_SORTED_PIDS 80
1809 static int coalition_get_sort_list(coalition_t coal
, int sort_order
, queue_t list
,
1810 struct coal_sort_s
*sort_array
, int array_sz
)
1815 assert(sort_array
!= NULL
);
1822 * this function will only be called with a NULL
1823 * list for JETSAM-type coalitions, and is intended
1824 * to investigate the leader process
1826 if (coal
->type
!= COALITION_TYPE_JETSAM
||
1827 coal
->j
.leader
== TASK_NULL
)
1829 sort_array
[0].pid
= task_pid(coal
->j
.leader
);
1830 switch (sort_order
) {
1831 case COALITION_SORT_DEFAULT
:
1832 sort_array
[0].usr_order
= 0;
1834 case COALITION_SORT_MEM_ASC
:
1835 case COALITION_SORT_MEM_DEC
:
1836 sort_array
[0].bytes
= get_task_phys_footprint(coal
->j
.leader
);
1838 case COALITION_SORT_USER_ASC
:
1839 case COALITION_SORT_USER_DEC
:
1840 sort_array
[0].usr_order
= 0;
1848 qe_foreach_element(task
, list
, task_coalition
[coal
->type
]) {
1849 if (ntasks
>= array_sz
) {
1850 printf("WARNING: more than %d pids in coalition %llu\n",
1851 MAX_SORTED_PIDS
, coal
->id
);
1855 sort_array
[ntasks
].pid
= task_pid(task
);
1857 switch (sort_order
) {
1858 case COALITION_SORT_DEFAULT
:
1859 sort_array
[ntasks
].usr_order
= 0;
1861 case COALITION_SORT_MEM_ASC
:
1862 case COALITION_SORT_MEM_DEC
:
1863 sort_array
[ntasks
].bytes
= get_task_phys_footprint(task
);
1865 case COALITION_SORT_USER_ASC
:
1866 case COALITION_SORT_USER_DEC
:
1867 sort_array
[ntasks
].usr_order
= 0;
1879 int coalition_get_pid_list(coalition_t coal
, uint32_t rolemask
, int sort_order
,
1880 int *pid_list
, int list_sz
)
1882 struct i_jetsam_coalition
*cj
;
1884 cmpfunc_t cmp_func
= NULL
;
1885 struct coal_sort_s sort_array
[MAX_SORTED_PIDS
] = { {0,0,0} }; /* keep to < 2k */
1888 !(rolemask
& COALITION_ROLEMASK_ALLROLES
) ||
1889 !pid_list
|| list_sz
< 1) {
1890 coal_dbg("Invalid parameters: coal:%p, type:%d, rolemask:0x%x, "
1891 "pid_list:%p, list_sz:%d", coal
, coal
? coal
->type
: -1,
1892 rolemask
, pid_list
, list_sz
);
1896 switch (sort_order
) {
1897 case COALITION_SORT_NOSORT
:
1900 case COALITION_SORT_DEFAULT
:
1901 cmp_func
= dflt_cmp
;
1903 case COALITION_SORT_MEM_ASC
:
1904 cmp_func
= mem_asc_cmp
;
1906 case COALITION_SORT_MEM_DEC
:
1907 cmp_func
= mem_dec_cmp
;
1909 case COALITION_SORT_USER_ASC
:
1910 cmp_func
= usr_asc_cmp
;
1912 case COALITION_SORT_USER_DEC
:
1913 cmp_func
= usr_dec_cmp
;
1919 coalition_lock(coal
);
1921 if (coal
->type
== COALITION_TYPE_RESOURCE
) {
1922 ntasks
+= coalition_get_sort_list(coal
, sort_order
, &coal
->r
.tasks
,
1923 sort_array
, MAX_SORTED_PIDS
);
1929 if (rolemask
& COALITION_ROLEMASK_UNDEF
)
1930 ntasks
+= coalition_get_sort_list(coal
, sort_order
, &cj
->other
,
1931 sort_array
+ ntasks
,
1932 MAX_SORTED_PIDS
- ntasks
);
1934 if (rolemask
& COALITION_ROLEMASK_XPC
)
1935 ntasks
+= coalition_get_sort_list(coal
, sort_order
, &cj
->services
,
1936 sort_array
+ ntasks
,
1937 MAX_SORTED_PIDS
- ntasks
);
1939 if (rolemask
& COALITION_ROLEMASK_EXT
)
1940 ntasks
+= coalition_get_sort_list(coal
, sort_order
, &cj
->extensions
,
1941 sort_array
+ ntasks
,
1942 MAX_SORTED_PIDS
- ntasks
);
1944 if (rolemask
& COALITION_ROLEMASK_LEADER
)
1945 ntasks
+= coalition_get_sort_list(coal
, sort_order
, NULL
,
1946 sort_array
+ ntasks
,
1947 MAX_SORTED_PIDS
- ntasks
);
1950 coalition_unlock(coal
);
1952 /* sort based on the chosen criterion (no sense sorting 1 item) */
1953 if (cmp_func
&& ntasks
> 1)
1954 qsort(sort_array
, ntasks
, sizeof(struct coal_sort_s
), cmp_func
);
1956 for (int i
= 0; i
< ntasks
; i
++) {
1959 coal_dbg(" [%d] PID:%d, footprint:%lld, usr_order:%d",
1960 i
, sort_array
[i
].pid
, sort_array
[i
].bytes
,
1961 sort_array
[i
].usr_order
);
1962 pid_list
[i
] = sort_array
[i
].pid
;