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 int coalition_get_task_count(coalition_t coal
);
55 uint64_t coalition_get_page_count(coalition_t coal
, int *ntasks
);
56 int coalition_get_pid_list(coalition_t coal
, uint32_t rolemask
, int sort_order
,
57 int *pid_list
, int list_sz
);
59 /* defined in task.c */
60 extern ledger_template_t task_ledger_template
;
63 * Coalition zone needs limits. We expect there will be as many coalitions as
64 * tasks (same order of magnitude), so use the task zone's limits.
66 #define CONFIG_COALITION_MAX CONFIG_TASK_MAX
67 #define COALITION_CHUNK TASK_CHUNK
69 int unrestrict_coalition_syscalls
;
71 lck_attr_t coalitions_lck_attr
;
72 lck_grp_t coalitions_lck_grp
;
73 lck_grp_attr_t coalitions_lck_grp_attr
;
75 /* coalitions_list_lock protects coalition_count, coalitions queue, next_coalition_id. */
76 decl_lck_mtx_data(static,coalitions_list_lock
);
77 static uint64_t coalition_count
;
78 static uint64_t coalition_next_id
= 1;
79 static queue_head_t coalitions_q
;
81 coalition_t init_coalition
[COALITION_NUM_TYPES
];
83 zone_t coalition_zone
;
85 static const char *coal_type_str(int type
)
88 case COALITION_TYPE_RESOURCE
:
90 case COALITION_TYPE_JETSAM
:
97 struct coalition_type
{
102 * pre-condition: coalition just allocated (unlocked), unreferenced,
105 kern_return_t (*init
)(coalition_t coal
, boolean_t privileged
);
109 * pre-condition: coalition unlocked
110 * pre-condition: coalition refcount=0, active_count=0,
111 * termrequested=1, terminated=1, reaped=1
113 void (*dealloc
)(coalition_t coal
);
117 * pre-condition: coalition locked
118 * pre-condition: coalition !repead and !terminated
120 kern_return_t (*adopt_task
)(coalition_t coal
, task_t task
);
124 * pre-condition: coalition locked
125 * pre-condition: task has been removed from coalition's task list
127 kern_return_t (*remove_task
)(coalition_t coal
, task_t task
);
131 * pre-condition: coalition locked
132 * pre-condition: task added to coalition's task list,
133 * active_count >= 1 (at least the given task is active)
135 kern_return_t (*set_taskrole
)(coalition_t coal
, task_t task
, int role
);
139 * pre-condition: coalition locked
140 * pre-condition: task added to coalition's task list,
141 * active_count >= 1 (at least the given task is active)
143 int (*get_taskrole
)(coalition_t coal
, task_t task
);
147 * pre-condition: coalition locked
149 void (*iterate_tasks
)(coalition_t coal
, void *ctx
, void (*callback
)(coalition_t
, void *, task_t
));
153 * COALITION_TYPE_RESOURCE
156 static kern_return_t
i_coal_resource_init(coalition_t coal
, boolean_t privileged
);
157 static void i_coal_resource_dealloc(coalition_t coal
);
158 static kern_return_t
i_coal_resource_adopt_task(coalition_t coal
, task_t task
);
159 static kern_return_t
i_coal_resource_remove_task(coalition_t coal
, task_t task
);
160 static kern_return_t
i_coal_resource_set_taskrole(coalition_t coal
,
161 task_t task
, int role
);
162 static int i_coal_resource_get_taskrole(coalition_t coal
, task_t task
);
163 static void i_coal_resource_iterate_tasks(coalition_t coal
, void *ctx
,
164 void (*callback
)(coalition_t
, void *, task_t
));
166 struct i_resource_coalition
{
169 uint64_t byteswritten
;
172 uint64_t task_count
; /* tasks that have started in this coalition */
173 uint64_t dead_task_count
; /* tasks that have exited in this coalition;
174 subtract from task_count to get count
177 * Count the length of time this coalition had at least one active task.
178 * This can be a 'denominator' to turn e.g. cpu_time to %cpu.
180 uint64_t last_became_nonempty_time
;
181 uint64_t time_nonempty
;
183 queue_head_t tasks
; /* List of active tasks in the coalition */
187 * COALITION_TYPE_JETSAM
190 static kern_return_t
i_coal_jetsam_init(coalition_t coal
, boolean_t privileged
);
191 static void i_coal_jetsam_dealloc(coalition_t coal
);
192 static kern_return_t
i_coal_jetsam_adopt_task(coalition_t coal
, task_t task
);
193 static kern_return_t
i_coal_jetsam_remove_task(coalition_t coal
, task_t task
);
194 static kern_return_t
i_coal_jetsam_set_taskrole(coalition_t coal
,
195 task_t task
, int role
);
196 static int i_coal_jetsam_get_taskrole(coalition_t coal
, task_t task
);
197 static void i_coal_jetsam_iterate_tasks(coalition_t coal
, void *ctx
,
198 void (*callback
)(coalition_t
, void *, task_t
));
200 struct i_jetsam_coalition
{
202 queue_head_t extensions
;
203 queue_head_t services
;
209 * main coalition structure
212 uint64_t id
; /* monotonically increasing */
214 uint32_t ref_count
; /* Number of references to the memory containing this struct */
215 uint32_t active_count
; /* Number of members of (tasks in) the
216 coalition, plus vouchers referring
218 uint32_t focal_task_count
; /* Number of TASK_FOREGROUND_APPLICATION tasks in the coalition */
219 uint32_t nonfocal_task_count
; /* Number of TASK_BACKGROUND_APPLICATION tasks in the coalition */
221 /* coalition flags */
222 uint32_t privileged
: 1; /* Members of this coalition may create
223 and manage coalitions and may posix_spawn
224 processes into selected coalitions */
227 uint32_t termrequested
: 1; /* launchd has requested termination when coalition becomes empty */
228 uint32_t terminated
: 1; /* coalition became empty and spawns are now forbidden */
229 uint32_t reaped
: 1; /* reaped, invisible to userspace, but waiting for ref_count to go to zero */
230 uint32_t notified
: 1; /* no-more-processes notification was sent via special port */
231 #if defined(DEVELOPMENT) || defined(DEBUG)
232 uint32_t should_notify
: 1; /* should this coalition send notifications (default: yes) */
235 queue_chain_t coalitions
; /* global list of coalitions */
237 decl_lck_mtx_data(,lock
) /* Coalition lock. */
239 /* put coalition type-specific structures here */
241 struct i_resource_coalition r
;
242 struct i_jetsam_coalition j
;
247 * register different coalition types:
248 * these must be kept in the order specified in coalition.h
250 static const struct coalition_type
251 s_coalition_types
[COALITION_NUM_TYPES
] = {
253 COALITION_TYPE_RESOURCE
,
255 i_coal_resource_init
,
256 i_coal_resource_dealloc
,
257 i_coal_resource_adopt_task
,
258 i_coal_resource_remove_task
,
259 i_coal_resource_set_taskrole
,
260 i_coal_resource_get_taskrole
,
261 i_coal_resource_iterate_tasks
,
264 COALITION_TYPE_JETSAM
,
267 i_coal_jetsam_dealloc
,
268 i_coal_jetsam_adopt_task
,
269 i_coal_jetsam_remove_task
,
270 i_coal_jetsam_set_taskrole
,
271 i_coal_jetsam_get_taskrole
,
272 i_coal_jetsam_iterate_tasks
,
276 #define coal_call(coal, func, ...) \
277 (s_coalition_types[(coal)->type].func)(coal, ## __VA_ARGS__)
280 #define coalition_lock(c) do{ lck_mtx_lock(&c->lock); }while(0)
281 #define coalition_unlock(c) do{ lck_mtx_unlock(&c->lock); }while(0)
284 coalition_notify_user(uint64_t id
, uint32_t flags
)
286 mach_port_t user_port
;
289 kr
= host_get_coalition_port(host_priv_self(), &user_port
);
290 if ((kr
!= KERN_SUCCESS
) || !IPC_PORT_VALID(user_port
)) {
294 coalition_notification(user_port
, id
, flags
);
299 * COALITION_TYPE_RESOURCE
303 i_coal_resource_init(coalition_t coal
, boolean_t privileged
)
306 assert(coal
&& coal
->type
== COALITION_TYPE_RESOURCE
);
307 coal
->r
.ledger
= ledger_instantiate(task_ledger_template
,
308 LEDGER_CREATE_ACTIVE_ENTRIES
);
309 if (coal
->r
.ledger
== NULL
)
310 return KERN_RESOURCE_SHORTAGE
;
312 queue_init(&coal
->r
.tasks
);
318 i_coal_resource_dealloc(coalition_t coal
)
320 assert(coal
&& coal
->type
== COALITION_TYPE_RESOURCE
);
321 ledger_dereference(coal
->r
.ledger
);
325 i_coal_resource_adopt_task(coalition_t coal
, task_t task
)
327 struct i_resource_coalition
*cr
;
329 assert(coal
&& coal
->type
== COALITION_TYPE_RESOURCE
);
330 assert(queue_empty(&task
->task_coalition
[COALITION_TYPE_RESOURCE
]));
335 if (cr
->task_count
< cr
->dead_task_count
) {
336 panic("%s: coalition %p id:%llu type:%s task_count(%llu) < dead_task_count(%llu)",
337 __func__
, coal
, coal
->id
, coal_type_str(coal
->type
),
338 cr
->task_count
, cr
->dead_task_count
);
341 /* If moving from 0->1 active tasks */
342 if (cr
->task_count
- cr
->dead_task_count
== 1) {
343 cr
->last_became_nonempty_time
= mach_absolute_time();
346 /* put the task on the coalition's list of tasks */
347 enqueue_tail(&cr
->tasks
, &task
->task_coalition
[COALITION_TYPE_RESOURCE
]);
349 coal_dbg("Added PID:%d to id:%llu, task_count:%llu, dead_count:%llu, nonempty_time:%llu",
350 task_pid(task
), coal
->id
, cr
->task_count
, cr
->dead_task_count
,
351 cr
->last_became_nonempty_time
);
357 i_coal_resource_remove_task(coalition_t coal
, task_t task
)
359 struct i_resource_coalition
*cr
;
361 assert(coal
&& coal
->type
== COALITION_TYPE_RESOURCE
);
362 assert(task
->coalition
[COALITION_TYPE_RESOURCE
] == coal
);
363 assert(!queue_empty(&task
->task_coalition
[COALITION_TYPE_RESOURCE
]));
366 * handle resource coalition accounting rollup for dead tasks
370 cr
->dead_task_count
++;
372 if (cr
->task_count
< cr
->dead_task_count
) {
373 panic("%s: coalition %p id:%llu type:%s task_count(%llu) < dead_task_count(%llu)",
374 __func__
, coal
, coal
->id
, coal_type_str(coal
->type
), cr
->task_count
, cr
->dead_task_count
);
377 /* If moving from 1->0 active tasks */
378 if (cr
->task_count
- cr
->dead_task_count
== 0) {
379 uint64_t last_time_nonempty
= mach_absolute_time() - cr
->last_became_nonempty_time
;
380 cr
->last_became_nonempty_time
= 0;
381 cr
->time_nonempty
+= last_time_nonempty
;
384 ledger_rollup(cr
->ledger
, task
->ledger
);
385 cr
->bytesread
+= task
->task_io_stats
->disk_reads
.size
;
386 cr
->byteswritten
+= task
->task_io_stats
->total_io
.size
- task
->task_io_stats
->disk_reads
.size
;
387 cr
->gpu_time
+= task_gpu_utilisation(task
);
389 /* remove the task from the coalition's list */
390 remqueue(&task
->task_coalition
[COALITION_TYPE_RESOURCE
]);
391 queue_chain_init(task
->task_coalition
[COALITION_TYPE_RESOURCE
]);
393 coal_dbg("removed PID:%d from id:%llu, task_count:%llu, dead_count:%llu",
394 task_pid(task
), coal
->id
, cr
->task_count
, cr
->dead_task_count
);
400 i_coal_resource_set_taskrole(__unused coalition_t coal
,
401 __unused task_t task
, __unused
int role
)
407 i_coal_resource_get_taskrole(__unused coalition_t coal
, __unused task_t task
)
411 assert(coal
&& coal
->type
== COALITION_TYPE_RESOURCE
);
413 qe_foreach_element(t
, &coal
->r
.tasks
, task_coalition
[COALITION_TYPE_RESOURCE
]) {
415 return COALITION_TASKROLE_UNDEF
;
422 i_coal_resource_iterate_tasks(coalition_t coal
, void *ctx
, void (*callback
)(coalition_t
, void *, task_t
))
425 assert(coal
&& coal
->type
== COALITION_TYPE_RESOURCE
);
427 qe_foreach_element(t
, &coal
->r
.tasks
, task_coalition
[COALITION_TYPE_RESOURCE
])
428 callback(coal
, ctx
, t
);
432 coalition_resource_usage_internal(coalition_t coal
, struct coalition_resource_usage
*cru_out
)
435 ledger_amount_t credit
, debit
;
437 if (coal
->type
!= COALITION_TYPE_RESOURCE
)
438 return KERN_INVALID_ARGUMENT
;
440 ledger_t sum_ledger
= ledger_instantiate(task_ledger_template
, LEDGER_CREATE_ACTIVE_ENTRIES
);
441 if (sum_ledger
== LEDGER_NULL
)
442 return KERN_RESOURCE_SHORTAGE
;
444 coalition_lock(coal
);
447 * Start with the coalition's ledger, which holds the totals from all
450 ledger_rollup(sum_ledger
, coal
->r
.ledger
);
451 uint64_t bytesread
= coal
->r
.bytesread
;
452 uint64_t byteswritten
= coal
->r
.byteswritten
;
453 uint64_t gpu_time
= coal
->r
.gpu_time
;
454 int64_t cpu_time_billed_to_me
= 0;
455 int64_t cpu_time_billed_to_others
= 0;
457 kr
= ledger_get_balance(sum_ledger
, task_ledgers
.cpu_time_billed_to_me
, (int64_t *)&cpu_time_billed_to_me
);
458 if (kr
!= KERN_SUCCESS
|| cpu_time_billed_to_me
< 0) {
459 #if DEVELOPMENT || DEBUG
460 printf("ledger_get_balance failed or ledger negative in coalition_resource_usage_internal: %lld\n", cpu_time_billed_to_me
);
461 #endif /* DEVELOPMENT || DEBUG */
462 cpu_time_billed_to_me
= 0;
465 kr
= ledger_get_balance(sum_ledger
, task_ledgers
.cpu_time_billed_to_others
, (int64_t *)&cpu_time_billed_to_others
);
466 if (kr
!= KERN_SUCCESS
|| cpu_time_billed_to_others
< 0) {
467 #if DEVELOPMENT || DEBUG
468 printf("ledger_get_balance failed or ledger negative in coalition_resource_usage_internal: %lld\n", cpu_time_billed_to_others
);
469 #endif /* DEVELOPMENT || DEBUG */
470 cpu_time_billed_to_others
= 0;
474 * Add to that all the active tasks' ledgers. Tasks cannot deallocate
475 * out from under us, since we hold the coalition lock.
476 * Do not use the on-behalf of cpu time from ledger for live tasks, since
477 * it will not have cpu time for active linkages between tasks.
480 qe_foreach_element(task
, &coal
->r
.tasks
, task_coalition
[COALITION_TYPE_RESOURCE
]) {
481 ledger_rollup(sum_ledger
, task
->ledger
);
482 bytesread
+= task
->task_io_stats
->disk_reads
.size
;
483 byteswritten
+= task
->task_io_stats
->total_io
.size
- task
->task_io_stats
->disk_reads
.size
;
484 gpu_time
+= task_gpu_utilisation(task
);
485 cpu_time_billed_to_me
+= (int64_t)bank_billed_time(task
->bank_context
);
486 cpu_time_billed_to_others
+= (int64_t)bank_serviced_time(task
->bank_context
);
489 /* collect information from the coalition itself */
490 cru_out
->tasks_started
= coal
->r
.task_count
;
491 cru_out
->tasks_exited
= coal
->r
.dead_task_count
;
493 uint64_t time_nonempty
= coal
->r
.time_nonempty
;
494 uint64_t last_became_nonempty_time
= coal
->r
.last_became_nonempty_time
;
496 coalition_unlock(coal
);
498 /* Copy the totals out of sum_ledger */
499 kr
= ledger_get_entries(sum_ledger
, task_ledgers
.cpu_time
,
501 if (kr
!= KERN_SUCCESS
) {
504 cru_out
->cpu_time
= credit
;
505 cru_out
->cpu_time_billed_to_me
= (uint64_t)cpu_time_billed_to_me
;
506 cru_out
->cpu_time_billed_to_others
= (uint64_t)cpu_time_billed_to_others
;
508 kr
= ledger_get_entries(sum_ledger
, task_ledgers
.interrupt_wakeups
,
510 if (kr
!= KERN_SUCCESS
) {
513 cru_out
->interrupt_wakeups
= credit
;
515 kr
= ledger_get_entries(sum_ledger
, task_ledgers
.platform_idle_wakeups
,
517 if (kr
!= KERN_SUCCESS
) {
520 cru_out
->platform_idle_wakeups
= credit
;
522 cru_out
->bytesread
= bytesread
;
523 cru_out
->byteswritten
= byteswritten
;
524 cru_out
->gpu_time
= gpu_time
;
526 ledger_dereference(sum_ledger
);
527 sum_ledger
= LEDGER_NULL
;
529 if (last_became_nonempty_time
) {
530 time_nonempty
+= mach_absolute_time() - last_became_nonempty_time
;
532 absolutetime_to_nanoseconds(time_nonempty
, &cru_out
->time_nonempty
);
539 * COALITION_TYPE_JETSAM
543 i_coal_jetsam_init(coalition_t coal
, boolean_t privileged
)
545 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
548 coal
->j
.leader
= TASK_NULL
;
549 queue_head_init(coal
->j
.extensions
);
550 queue_head_init(coal
->j
.services
);
551 queue_head_init(coal
->j
.other
);
557 i_coal_jetsam_dealloc(__unused coalition_t coal
)
559 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
561 /* the coalition should be completely clear at this point */
562 assert(queue_empty(&coal
->j
.extensions
));
563 assert(queue_empty(&coal
->j
.services
));
564 assert(queue_empty(&coal
->j
.other
));
565 assert(coal
->j
.leader
== TASK_NULL
);
569 i_coal_jetsam_adopt_task(coalition_t coal
, task_t task
)
571 struct i_jetsam_coalition
*cj
;
572 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
576 assert(queue_empty(&task
->task_coalition
[COALITION_TYPE_JETSAM
]));
578 /* put each task initially in the "other" list */
579 enqueue_tail(&cj
->other
, &task
->task_coalition
[COALITION_TYPE_JETSAM
]);
580 coal_dbg("coalition %lld adopted PID:%d as UNDEF",
581 coal
->id
, task_pid(task
));
587 i_coal_jetsam_remove_task(coalition_t coal
, task_t task
)
589 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
590 assert(task
->coalition
[COALITION_TYPE_JETSAM
] == coal
);
592 coal_dbg("removing PID:%d from coalition id:%lld",
593 task_pid(task
), coal
->id
);
595 if (task
== coal
->j
.leader
) {
596 coal
->j
.leader
= NULL
;
597 coal_dbg(" PID:%d was the leader!", task_pid(task
));
599 assert(!queue_empty(&task
->task_coalition
[COALITION_TYPE_JETSAM
]));
602 /* remove the task from the specific coalition role queue */
603 remqueue(&task
->task_coalition
[COALITION_TYPE_JETSAM
]);
604 queue_chain_init(task
->task_coalition
[COALITION_TYPE_RESOURCE
]);
610 i_coal_jetsam_set_taskrole(coalition_t coal
, task_t task
, int role
)
612 struct i_jetsam_coalition
*cj
;
614 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
615 assert(task
->coalition
[COALITION_TYPE_JETSAM
] == coal
);
620 case COALITION_TASKROLE_LEADER
:
621 coal_dbg("setting PID:%d as LEADER of %lld",
622 task_pid(task
), coal
->id
);
623 if (cj
->leader
!= TASK_NULL
) {
624 /* re-queue the exiting leader onto the "other" list */
625 coal_dbg(" re-queue existing leader (%d) as OTHER",
626 task_pid(cj
->leader
));
627 re_queue_tail(&cj
->other
, &cj
->leader
->task_coalition
[COALITION_TYPE_JETSAM
]);
630 * remove the task from the "other" list
631 * (where it was put by default)
633 remqueue(&task
->task_coalition
[COALITION_TYPE_JETSAM
]);
634 queue_chain_init(task
->task_coalition
[COALITION_TYPE_JETSAM
]);
636 /* set the coalition leader */
639 case COALITION_TASKROLE_UNDEF
:
640 coal_dbg("setting PID:%d as UNDEF in %lld",
641 task_pid(task
), coal
->id
);
642 q
= (queue_t
)&cj
->other
;
644 case COALITION_TASKROLE_XPC
:
645 coal_dbg("setting PID:%d as XPC in %lld",
646 task_pid(task
), coal
->id
);
647 q
= (queue_t
)&cj
->services
;
649 case COALITION_TASKROLE_EXT
:
650 coal_dbg("setting PID:%d as EXT in %lld",
651 task_pid(task
), coal
->id
);
652 q
= (queue_t
)&cj
->extensions
;
655 panic("%s: invalid role(%d) for task", __func__
, role
);
656 return KERN_INVALID_ARGUMENT
;
660 re_queue_tail(q
, &task
->task_coalition
[COALITION_TYPE_JETSAM
]);
666 i_coal_jetsam_get_taskrole(coalition_t coal
, task_t task
)
668 struct i_jetsam_coalition
*cj
;
671 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
672 assert(task
->coalition
[COALITION_TYPE_JETSAM
] == coal
);
676 if (task
== cj
->leader
)
677 return COALITION_TASKROLE_LEADER
;
679 qe_foreach_element(t
, &cj
->services
, task_coalition
[COALITION_TYPE_JETSAM
]) {
681 return COALITION_TASKROLE_XPC
;
684 qe_foreach_element(t
, &cj
->extensions
, task_coalition
[COALITION_TYPE_JETSAM
]) {
686 return COALITION_TASKROLE_EXT
;
689 qe_foreach_element(t
, &cj
->other
, task_coalition
[COALITION_TYPE_JETSAM
]) {
691 return COALITION_TASKROLE_UNDEF
;
694 /* task not in the coalition?! */
699 i_coal_jetsam_iterate_tasks(coalition_t coal
, void *ctx
, void (*callback
)(coalition_t
, void *, task_t
))
701 struct i_jetsam_coalition
*cj
;
704 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
709 callback(coal
, ctx
, cj
->leader
);
711 qe_foreach_element(t
, &cj
->services
, task_coalition
[COALITION_TYPE_JETSAM
])
712 callback(coal
, ctx
, t
);
714 qe_foreach_element(t
, &cj
->extensions
, task_coalition
[COALITION_TYPE_JETSAM
])
715 callback(coal
, ctx
, t
);
717 qe_foreach_element(t
, &cj
->other
, task_coalition
[COALITION_TYPE_JETSAM
])
718 callback(coal
, ctx
, t
);
724 * Main Coalition implementation
729 * coalition_create_internal
730 * Returns: New coalition object, referenced for the caller and unlocked.
731 * Condition: coalitions_list_lock must be UNLOCKED.
734 coalition_create_internal(int type
, boolean_t privileged
, coalition_t
*out
)
737 struct coalition
*new_coal
;
739 if (type
< 0 || type
> COALITION_TYPE_MAX
)
740 return KERN_INVALID_ARGUMENT
;
742 new_coal
= (struct coalition
*)zalloc(coalition_zone
);
743 if (new_coal
== COALITION_NULL
)
744 return KERN_RESOURCE_SHORTAGE
;
745 bzero(new_coal
, sizeof(*new_coal
));
747 new_coal
->type
= type
;
749 /* initialize type-specific resources */
750 kr
= coal_call(new_coal
, init
, privileged
);
751 if (kr
!= KERN_SUCCESS
) {
752 zfree(coalition_zone
, new_coal
);
756 /* One for caller, one for coalitions list */
757 new_coal
->ref_count
= 2;
759 new_coal
->privileged
= privileged
? TRUE
: FALSE
;
760 #if defined(DEVELOPMENT) || defined(DEBUG)
761 new_coal
->should_notify
= 1;
764 lck_mtx_init(&new_coal
->lock
, &coalitions_lck_grp
, &coalitions_lck_attr
);
766 lck_mtx_lock(&coalitions_list_lock
);
767 new_coal
->id
= coalition_next_id
++;
769 enqueue_tail(&coalitions_q
, &new_coal
->coalitions
);
770 lck_mtx_unlock(&coalitions_list_lock
);
772 coal_dbg("id:%llu, type:%s", new_coal
->id
, coal_type_str(new_coal
->type
));
780 * Condition: coalition must be UNLOCKED.
783 coalition_release(coalition_t coal
)
785 /* TODO: This can be done with atomics. */
786 coalition_lock(coal
);
790 uint32_t rc
= coal
->ref_count
;
791 uint32_t ac
= coal
->active_count
;
792 #endif /* COALITION_DEBUG */
794 coal_dbg("id:%llu type:%s ref_count:%u active_count:%u%s",
795 coal
->id
, coal_type_str(coal
->type
), rc
, ac
,
796 rc
<= 0 ? ", will deallocate now" : "");
798 if (coal
->ref_count
> 0) {
799 coalition_unlock(coal
);
803 assert(coal
->termrequested
);
804 assert(coal
->terminated
);
805 assert(coal
->active_count
== 0);
806 assert(coal
->reaped
);
807 assert(coal
->focal_task_count
== 0);
808 assert(coal
->nonfocal_task_count
== 0);
810 coal_call(coal
, dealloc
);
812 coalition_unlock(coal
);
814 lck_mtx_destroy(&coal
->lock
, &coalitions_lck_grp
);
816 zfree(coalition_zone
, coal
);
820 * coalition_find_by_id_internal
821 * Returns: Coalition object with specified id, NOT referenced.
822 * If not found, returns COALITION_NULL.
823 * Condition: coalitions_list_lock must be LOCKED.
826 coalition_find_by_id_internal(uint64_t coal_id
)
829 return COALITION_NULL
;
832 lck_mtx_assert(&coalitions_list_lock
, LCK_MTX_ASSERT_OWNED
);
834 qe_foreach_element(coal
, &coalitions_q
, coalitions
) {
835 if (coal
->id
== coal_id
) {
839 return COALITION_NULL
;
843 * coalition_find_by_id
844 * Returns: Coalition object with specified id, referenced.
845 * Condition: coalitions_list_lock must be UNLOCKED.
848 coalition_find_by_id(uint64_t cid
)
851 return COALITION_NULL
;
854 lck_mtx_lock(&coalitions_list_lock
);
856 coalition_t coal
= coalition_find_by_id_internal(cid
);
857 if (coal
== COALITION_NULL
) {
858 lck_mtx_unlock(&coalitions_list_lock
);
859 return COALITION_NULL
;
862 coalition_lock(coal
);
865 coalition_unlock(coal
);
866 lck_mtx_unlock(&coalitions_list_lock
);
867 return COALITION_NULL
;
870 if (coal
->ref_count
== 0) {
871 panic("resurrecting coalition %p id:%llu type:%s, active_count:%u\n",
872 coal
, coal
->id
, coal_type_str(coal
->type
), coal
->active_count
);
876 uint32_t rc
= coal
->ref_count
;
879 coalition_unlock(coal
);
880 lck_mtx_unlock(&coalitions_list_lock
);
882 coal_dbg("id:%llu type:%s ref_count:%u",
883 coal
->id
, coal_type_str(coal
->type
), rc
);
889 * coalition_find_and_activate_by_id
890 * Returns: Coalition object with specified id, referenced, and activated.
891 * Condition: coalitions_list_lock must be UNLOCKED.
892 * This is the function to use when putting a 'new' thing into a coalition,
893 * like posix_spawn of an XPC service by launchd.
894 * See also coalition_extend_active.
897 coalition_find_and_activate_by_id(uint64_t cid
)
900 return COALITION_NULL
;
903 lck_mtx_lock(&coalitions_list_lock
);
905 coalition_t coal
= coalition_find_by_id_internal(cid
);
906 if (coal
== COALITION_NULL
) {
907 lck_mtx_unlock(&coalitions_list_lock
);
908 return COALITION_NULL
;
911 coalition_lock(coal
);
913 if (coal
->reaped
|| coal
->terminated
) {
914 /* Too late to put something new into this coalition, it's
915 * already on its way out the door */
916 coalition_unlock(coal
);
917 lck_mtx_unlock(&coalitions_list_lock
);
918 return COALITION_NULL
;
921 if (coal
->ref_count
== 0) {
922 panic("resurrecting coalition %p id:%llu type:%s, active_count:%u\n",
923 coal
, coal
->id
, coal_type_str(coal
->type
), coal
->active_count
);
927 coal
->active_count
++;
930 uint32_t rc
= coal
->ref_count
;
931 uint32_t ac
= coal
->active_count
;
934 coalition_unlock(coal
);
935 lck_mtx_unlock(&coalitions_list_lock
);
937 coal_dbg("id:%llu type:%s ref_count:%u, active_count:%u",
938 coal
->id
, coal_type_str(coal
->type
), rc
, ac
);
944 coalition_id(coalition_t coal
)
950 task_coalition_ids(task_t task
, uint64_t ids
[COALITION_NUM_TYPES
])
953 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
954 if (task
->coalition
[i
])
955 ids
[i
] = task
->coalition
[i
]->id
;
962 task_coalition_roles(task_t task
, int roles
[COALITION_NUM_TYPES
])
965 memset(roles
, 0, COALITION_NUM_TYPES
* sizeof(roles
[0]));
967 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
968 if (task
->coalition
[i
]) {
969 coalition_lock(task
->coalition
[i
]);
970 roles
[i
] = coal_call(task
->coalition
[i
],
972 coalition_unlock(task
->coalition
[i
]);
981 coalition_type(coalition_t coal
)
987 coalition_is_privileged(coalition_t coal
)
989 return coal
->privileged
|| unrestrict_coalition_syscalls
;
993 task_is_in_privileged_coalition(task_t task
, int type
)
995 if (type
< 0 || type
> COALITION_TYPE_MAX
)
997 if (unrestrict_coalition_syscalls
)
999 if (!task
->coalition
[type
])
1001 return task
->coalition
[type
]->privileged
;
1004 void task_coalition_update_gpu_stats(task_t task
, uint64_t gpu_ns_delta
)
1008 assert(task
!= TASK_NULL
);
1009 if (gpu_ns_delta
== 0)
1012 coal
= task
->coalition
[COALITION_TYPE_RESOURCE
];
1013 assert(coal
!= COALITION_NULL
);
1015 coalition_lock(coal
);
1016 coal
->r
.gpu_time
+= gpu_ns_delta
;
1017 coalition_unlock(coal
);
1020 uint32_t task_coalition_adjust_focal_count(task_t task
, int count
)
1026 * For now: only use the resource coalition. Perhaps in the
1027 * future we may combine all coalition types, or even make
1028 * a special coalition type just for this.
1030 coal
= task
->coalition
[COALITION_TYPE_RESOURCE
];
1031 assert(coal
!= COALITION_NULL
);
1033 ret
= hw_atomic_add(&coal
->focal_task_count
, count
);
1035 /* catch underflow */
1036 assert(ret
!= UINT32_MAX
);
1040 uint32_t task_coalition_focal_count(task_t task
)
1043 coal
= task
->coalition
[COALITION_TYPE_RESOURCE
];
1044 assert(coal
!= COALITION_NULL
);
1046 return coal
->focal_task_count
;
1049 uint32_t task_coalition_adjust_nonfocal_count(task_t task
, int count
)
1055 * For now: only use the resource coalition. Perhaps in the
1056 * future we may combine all coalition types, or even make
1057 * a special coalition type just for this.
1059 coal
= task
->coalition
[COALITION_TYPE_RESOURCE
];
1060 assert(coal
!= COALITION_NULL
);
1062 ret
= hw_atomic_add(&coal
->nonfocal_task_count
, count
);
1064 /* catch underflow */
1065 assert(ret
!= UINT32_MAX
);
1069 uint32_t task_coalition_nonfocal_count(task_t task
)
1072 coal
= task
->coalition
[COALITION_TYPE_RESOURCE
];
1073 assert(coal
!= COALITION_NULL
);
1075 return coal
->nonfocal_task_count
;
1078 void coalition_for_each_task(coalition_t coal
, void *ctx
,
1079 void (*callback
)(coalition_t
, void *, task_t
))
1081 assert(coal
!= COALITION_NULL
);
1083 coal_dbg("iterating tasks in coalition %p id:%llu type:%s, active_count:%u",
1084 coal
, coal
->id
, coal_type_str(coal
->type
), coal
->active_count
);
1086 coalition_lock(coal
);
1088 coal_call(coal
, iterate_tasks
, ctx
, callback
);
1090 coalition_unlock(coal
);
1095 coalition_remove_active(coalition_t coal
)
1097 coalition_lock(coal
);
1099 assert(!coal
->reaped
);
1100 assert(coal
->active_count
> 0);
1102 coal
->active_count
--;
1104 boolean_t do_notify
= FALSE
;
1105 uint64_t notify_id
= 0;
1106 uint32_t notify_flags
= 0;
1107 if (coal
->termrequested
&& coal
->active_count
== 0) {
1108 /* We only notify once, when active_count reaches zero.
1109 * We just decremented, so if it reached zero, we mustn't have
1112 assert(!coal
->terminated
);
1113 coal
->terminated
= TRUE
;
1115 assert(!coal
->notified
);
1117 coal
->notified
= TRUE
;
1118 #if defined(DEVELOPMENT) || defined(DEBUG)
1119 do_notify
= coal
->should_notify
;
1123 notify_id
= coal
->id
;
1128 uint64_t cid
= coal
->id
;
1129 uint32_t rc
= coal
->ref_count
;
1130 int ac
= coal
->active_count
;
1131 int ct
= coal
->type
;
1133 coalition_unlock(coal
);
1135 coal_dbg("id:%llu type:%s ref_count:%u, active_count:%u,%s",
1136 cid
, coal_type_str(ct
), rc
, ac
, do_notify
? " NOTIFY" : " ");
1139 coalition_notify_user(notify_id
, notify_flags
);
1143 /* Used for kernel_task, launchd, launchd's early boot tasks... */
1145 coalitions_adopt_init_task(task_t task
)
1148 kr
= coalitions_adopt_task(init_coalition
, task
);
1149 if (kr
!= KERN_SUCCESS
) {
1150 panic("failed to adopt task %p into default coalition: %d", task
, kr
);
1156 * coalition_adopt_task_internal
1157 * Condition: Coalition must be referenced and unlocked. Will fail if coalition
1158 * is already terminated.
1160 static kern_return_t
1161 coalition_adopt_task_internal(coalition_t coal
, task_t task
)
1165 if (task
->coalition
[coal
->type
]) {
1166 return KERN_ALREADY_IN_SET
;
1169 coalition_lock(coal
);
1171 if (coal
->reaped
|| coal
->terminated
) {
1172 coalition_unlock(coal
);
1173 return KERN_TERMINATED
;
1176 kr
= coal_call(coal
, adopt_task
, task
);
1177 if (kr
!= KERN_SUCCESS
)
1180 coal
->active_count
++;
1184 task
->coalition
[coal
->type
] = coal
;
1188 (void)coal
; /* need expression after label */
1189 uint64_t cid
= coal
->id
;
1190 uint32_t rc
= coal
->ref_count
;
1191 uint32_t ct
= coal
->type
;
1193 coalition_unlock(coal
);
1195 coal_dbg("task:%d, id:%llu type:%s ref_count:%u, kr=%d",
1196 task_pid(task
), cid
, coal_type_str(ct
), rc
, kr
);
1200 static kern_return_t
1201 coalition_remove_task_internal(task_t task
, int type
)
1205 coalition_t coal
= task
->coalition
[type
];
1208 return KERN_SUCCESS
;
1210 assert(coal
->type
== (uint32_t)type
);
1212 coalition_lock(coal
);
1214 kr
= coal_call(coal
, remove_task
, task
);
1217 uint64_t cid
= coal
->id
;
1218 uint32_t rc
= coal
->ref_count
;
1219 int ac
= coal
->active_count
;
1220 int ct
= coal
->type
;
1222 coalition_unlock(coal
);
1224 coal_dbg("id:%llu type:%s ref_count:%u, active_count:%u, kr=%d",
1225 cid
, coal_type_str(ct
), rc
, ac
, kr
);
1227 coalition_remove_active(coal
);
1233 * coalitions_adopt_task
1234 * Condition: All coalitions must be referenced and unlocked.
1235 * Will fail if any coalition is already terminated.
1238 coalitions_adopt_task(coalition_t
*coals
, task_t task
)
1243 if (!coals
|| coals
[COALITION_TYPE_RESOURCE
] == COALITION_NULL
)
1244 return KERN_INVALID_ARGUMENT
;
1246 /* verify that the incoming coalitions are what they say they are */
1247 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++)
1248 if (coals
[i
] && coals
[i
]->type
!= (uint32_t)i
)
1249 return KERN_INVALID_ARGUMENT
;
1251 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
1254 kr
= coalition_adopt_task_internal(coals
[i
], task
);
1255 if (kr
!= KERN_SUCCESS
) {
1256 /* dis-associate any coalitions that just adopted this task */
1258 if (task
->coalition
[i
])
1259 coalition_remove_task_internal(task
, i
);
1268 * coalitions_remove_task
1269 * Condition: task must be referenced and UNLOCKED; all task's coalitions must be UNLOCKED
1272 coalitions_remove_task(task_t task
)
1277 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
1278 kr
= coalition_remove_task_internal(task
, i
);
1279 assert(kr
== KERN_SUCCESS
);
1286 * task_release_coalitions
1287 * helper function to release references to all coalitions in which
1288 * 'task' is a member.
1291 task_release_coalitions(task_t task
)
1294 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
1295 if (task
->coalition
[i
])
1296 coalition_release(task
->coalition
[i
]);
1301 * coalitions_set_roles
1302 * for each type of coalition, if the task is a member of a coalition of
1303 * that type (given in the coalitions parameter) then set the role of
1304 * the task within that that coalition.
1306 kern_return_t
coalitions_set_roles(coalition_t coalitions
[COALITION_NUM_TYPES
],
1307 task_t task
, int roles
[COALITION_NUM_TYPES
])
1309 kern_return_t kr
= KERN_SUCCESS
;
1312 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
1315 coalition_lock(coalitions
[i
]);
1316 kr
= coal_call(coalitions
[i
], set_taskrole
, task
, roles
[i
]);
1317 coalition_unlock(coalitions
[i
]);
1318 assert(kr
== KERN_SUCCESS
);
1325 * coalition_terminate_internal
1326 * Condition: Coalition must be referenced and UNLOCKED.
1329 coalition_request_terminate_internal(coalition_t coal
)
1331 assert(coal
->type
>= 0 && coal
->type
<= COALITION_TYPE_MAX
);
1333 if (coal
== init_coalition
[coal
->type
]) {
1334 return KERN_DEFAULT_SET
;
1337 coalition_lock(coal
);
1340 coalition_unlock(coal
);
1341 return KERN_INVALID_NAME
;
1344 if (coal
->terminated
|| coal
->termrequested
) {
1345 coalition_unlock(coal
);
1346 return KERN_TERMINATED
;
1349 coal
->termrequested
= TRUE
;
1351 boolean_t do_notify
= FALSE
;
1352 uint64_t note_id
= 0;
1353 uint32_t note_flags
= 0;
1355 if (coal
->active_count
== 0) {
1357 * We only notify once, when active_count reaches zero.
1358 * We just set termrequested to zero. If the active count
1359 * was already at zero (tasks died before we could request
1360 * a termination notification), we should notify.
1362 assert(!coal
->terminated
);
1363 coal
->terminated
= TRUE
;
1365 assert(!coal
->notified
);
1367 coal
->notified
= TRUE
;
1368 #if defined(DEVELOPMENT) || defined(DEBUG)
1369 do_notify
= coal
->should_notify
;
1377 coalition_unlock(coal
);
1380 coalition_notify_user(note_id
, note_flags
);
1383 return KERN_SUCCESS
;
1387 * coalition_reap_internal
1388 * Condition: Coalition must be referenced and UNLOCKED.
1391 coalition_reap_internal(coalition_t coal
)
1393 assert(coal
->type
<= COALITION_TYPE_MAX
);
1395 if (coal
== init_coalition
[coal
->type
]) {
1396 return KERN_DEFAULT_SET
;
1399 coalition_lock(coal
);
1401 coalition_unlock(coal
);
1402 return KERN_TERMINATED
;
1404 if (!coal
->terminated
) {
1405 coalition_unlock(coal
);
1406 return KERN_FAILURE
;
1408 assert(coal
->termrequested
);
1409 if (coal
->active_count
> 0) {
1410 coalition_unlock(coal
);
1411 return KERN_FAILURE
;
1414 coal
->reaped
= TRUE
;
1416 /* Caller, launchd, and coalitions list should each have a reference */
1417 assert(coal
->ref_count
> 2);
1419 coalition_unlock(coal
);
1421 lck_mtx_lock(&coalitions_list_lock
);
1423 remqueue(&coal
->coalitions
);
1424 lck_mtx_unlock(&coalitions_list_lock
);
1426 /* Release the list's reference and launchd's reference. */
1427 coalition_release(coal
);
1428 coalition_release(coal
);
1430 return KERN_SUCCESS
;
1433 #if defined(DEVELOPMENT) || defined(DEBUG)
1434 int coalition_should_notify(coalition_t coal
)
1439 coalition_lock(coal
);
1440 should
= coal
->should_notify
;
1441 coalition_unlock(coal
);
1446 void coalition_set_notify(coalition_t coal
, int notify
)
1450 coalition_lock(coal
);
1451 coal
->should_notify
= !!notify
;
1452 coalition_unlock(coal
);
1457 coalitions_init(void)
1461 const struct coalition_type
*ctype
;
1463 coalition_zone
= zinit(
1464 sizeof(struct coalition
),
1465 CONFIG_COALITION_MAX
* sizeof(struct coalition
),
1466 COALITION_CHUNK
* sizeof(struct coalition
),
1468 zone_change(coalition_zone
, Z_NOENCRYPT
, TRUE
);
1469 queue_head_init(coalitions_q
);
1471 if (!PE_parse_boot_argn("unrestrict_coalition_syscalls", &unrestrict_coalition_syscalls
,
1472 sizeof (unrestrict_coalition_syscalls
))) {
1473 unrestrict_coalition_syscalls
= 0;
1476 lck_grp_attr_setdefault(&coalitions_lck_grp_attr
);
1477 lck_grp_init(&coalitions_lck_grp
, "coalition", &coalitions_lck_grp_attr
);
1478 lck_attr_setdefault(&coalitions_lck_attr
);
1479 lck_mtx_init(&coalitions_list_lock
, &coalitions_lck_grp
, &coalitions_lck_attr
);
1481 init_task_ledgers();
1483 for (i
= 0, ctype
= &s_coalition_types
[0]; i
< COALITION_NUM_TYPES
; ctype
++, i
++) {
1484 /* verify the entry in the global coalition types array */
1485 if (ctype
->type
!= i
||
1488 !ctype
->adopt_task
||
1489 !ctype
->remove_task
) {
1490 panic("%s: Malformed coalition type %s(%d) in slot for type:%s(%d)",
1491 __func__
, coal_type_str(ctype
->type
), ctype
->type
, coal_type_str(i
), i
);
1493 if (!ctype
->has_default
)
1495 kr
= coalition_create_internal(ctype
->type
, TRUE
, &init_coalition
[ctype
->type
]);
1496 if (kr
!= KERN_SUCCESS
)
1497 panic("%s: could not create init %s coalition: kr:%d",
1498 __func__
, coal_type_str(i
), kr
);
1501 /* "Leak" our reference to the global object */
1505 * BSD Kernel interface functions
1508 static void coalition_fill_procinfo(struct coalition
*coal
,
1509 struct procinfo_coalinfo
*coalinfo
)
1511 coalinfo
->coalition_id
= coal
->id
;
1512 coalinfo
->coalition_type
= coal
->type
;
1513 coalinfo
->coalition_tasks
= coalition_get_task_count(coal
);
1517 int coalitions_get_list(int type
, struct procinfo_coalinfo
*coal_list
, int list_sz
)
1520 struct coalition
*coal
;
1522 lck_mtx_lock(&coalitions_list_lock
);
1523 qe_foreach_element(coal
, &coalitions_q
, coalitions
) {
1524 if (!coal
->reaped
&& (type
< 0 || type
== (int)coal
->type
)) {
1525 if (coal_list
&& ncoals
< list_sz
)
1526 coalition_fill_procinfo(coal
, &coal_list
[ncoals
]);
1530 lck_mtx_unlock(&coalitions_list_lock
);
1536 * Jetsam coalition interface
1539 boolean_t
coalition_is_leader(task_t task
, int coal_type
, coalition_t
*coal
)
1544 if (coal
) /* handle the error cases gracefully */
1545 *coal
= COALITION_NULL
;
1550 if (coal_type
> COALITION_TYPE_MAX
)
1553 c
= task
->coalition
[coal_type
];
1557 assert((int)c
->type
== coal_type
);
1565 if (c
->type
== COALITION_TYPE_JETSAM
&& c
->j
.leader
== task
)
1568 coalition_unlock(c
);
1574 int coalition_get_task_count(coalition_t coal
)
1577 struct queue_entry
*qe
;
1581 coalition_lock(coal
);
1582 switch (coal
->type
) {
1583 case COALITION_TYPE_RESOURCE
:
1584 qe_foreach(qe
, &coal
->r
.tasks
)
1587 case COALITION_TYPE_JETSAM
:
1590 qe_foreach(qe
, &coal
->j
.other
)
1592 qe_foreach(qe
, &coal
->j
.extensions
)
1594 qe_foreach(qe
, &coal
->j
.services
)
1600 coalition_unlock(coal
);
1606 static uint64_t i_get_list_footprint(queue_t list
, int type
, int *ntasks
)
1611 qe_foreach_element(task
, list
, task_coalition
[type
]) {
1612 bytes
+= get_task_phys_footprint(task
);
1613 coal_dbg(" [%d] task_pid:%d, type:%d, footprint:%lld",
1614 *ntasks
, task_pid(task
), type
, bytes
);
1621 uint64_t coalition_get_page_count(coalition_t coal
, int *ntasks
)
1631 coalition_lock(coal
);
1633 switch (coal
->type
) {
1634 case COALITION_TYPE_RESOURCE
:
1635 bytes
+= i_get_list_footprint(&coal
->r
.tasks
, COALITION_TYPE_RESOURCE
, &num_tasks
);
1637 case COALITION_TYPE_JETSAM
:
1638 if (coal
->j
.leader
) {
1639 bytes
+= get_task_phys_footprint(coal
->j
.leader
);
1642 bytes
+= i_get_list_footprint(&coal
->j
.extensions
, COALITION_TYPE_JETSAM
, &num_tasks
);
1643 bytes
+= i_get_list_footprint(&coal
->j
.services
, COALITION_TYPE_JETSAM
, &num_tasks
);
1644 bytes
+= i_get_list_footprint(&coal
->j
.other
, COALITION_TYPE_JETSAM
, &num_tasks
);
1650 coalition_unlock(coal
);
1653 *ntasks
= num_tasks
;
1655 return bytes
/ PAGE_SIZE_64
;
1658 struct coal_sort_s
{
1665 * return < 0 for a < b
1669 typedef int (*cmpfunc_t
)(const void *a
, const void *b
);
1672 qsort(void *a
, size_t n
, size_t es
, cmpfunc_t cmp
);
1674 static int dflt_cmp(const void *a
, const void *b
)
1676 const struct coal_sort_s
*csA
= (const struct coal_sort_s
*)a
;
1677 const struct coal_sort_s
*csB
= (const struct coal_sort_s
*)b
;
1680 * if both A and B are equal, use a memory descending sort
1682 if (csA
->usr_order
== csB
->usr_order
)
1683 return (int)((int64_t)csB
->bytes
- (int64_t)csA
->bytes
);
1685 /* otherwise, return the relationship between user specified orders */
1686 return (csA
->usr_order
- csB
->usr_order
);
1689 static int mem_asc_cmp(const void *a
, const void *b
)
1691 const struct coal_sort_s
*csA
= (const struct coal_sort_s
*)a
;
1692 const struct coal_sort_s
*csB
= (const struct coal_sort_s
*)b
;
1694 return (int)((int64_t)csA
->bytes
- (int64_t)csB
->bytes
);
1697 static int mem_dec_cmp(const void *a
, const void *b
)
1699 const struct coal_sort_s
*csA
= (const struct coal_sort_s
*)a
;
1700 const struct coal_sort_s
*csB
= (const struct coal_sort_s
*)b
;
1702 return (int)((int64_t)csB
->bytes
- (int64_t)csA
->bytes
);
1705 static int usr_asc_cmp(const void *a
, const void *b
)
1707 const struct coal_sort_s
*csA
= (const struct coal_sort_s
*)a
;
1708 const struct coal_sort_s
*csB
= (const struct coal_sort_s
*)b
;
1710 return (csA
->usr_order
- csB
->usr_order
);
1713 static int usr_dec_cmp(const void *a
, const void *b
)
1715 const struct coal_sort_s
*csA
= (const struct coal_sort_s
*)a
;
1716 const struct coal_sort_s
*csB
= (const struct coal_sort_s
*)b
;
1718 return (csB
->usr_order
- csA
->usr_order
);
1721 /* avoid dynamic allocation in this path */
1722 #define MAX_SORTED_PIDS 80
1724 static int coalition_get_sort_list(coalition_t coal
, int sort_order
, queue_t list
,
1725 struct coal_sort_s
*sort_array
, int array_sz
)
1730 assert(sort_array
!= NULL
);
1737 * this function will only be called with a NULL
1738 * list for JETSAM-type coalitions, and is intended
1739 * to investigate the leader process
1741 if (coal
->type
!= COALITION_TYPE_JETSAM
||
1742 coal
->j
.leader
== TASK_NULL
)
1744 sort_array
[0].pid
= task_pid(coal
->j
.leader
);
1745 switch (sort_order
) {
1746 case COALITION_SORT_DEFAULT
:
1747 sort_array
[0].usr_order
= 0;
1749 case COALITION_SORT_MEM_ASC
:
1750 case COALITION_SORT_MEM_DEC
:
1751 sort_array
[0].bytes
= get_task_phys_footprint(coal
->j
.leader
);
1753 case COALITION_SORT_USER_ASC
:
1754 case COALITION_SORT_USER_DEC
:
1755 sort_array
[0].usr_order
= 0;
1763 qe_foreach_element(task
, list
, task_coalition
[coal
->type
]) {
1764 if (ntasks
>= array_sz
) {
1765 printf("WARNING: more than %d pids in coalition %llu\n",
1766 MAX_SORTED_PIDS
, coal
->id
);
1770 sort_array
[ntasks
].pid
= task_pid(task
);
1772 switch (sort_order
) {
1773 case COALITION_SORT_DEFAULT
:
1774 sort_array
[ntasks
].usr_order
= 0;
1776 case COALITION_SORT_MEM_ASC
:
1777 case COALITION_SORT_MEM_DEC
:
1778 sort_array
[ntasks
].bytes
= get_task_phys_footprint(task
);
1780 case COALITION_SORT_USER_ASC
:
1781 case COALITION_SORT_USER_DEC
:
1782 sort_array
[ntasks
].usr_order
= 0;
1794 int coalition_get_pid_list(coalition_t coal
, uint32_t rolemask
, int sort_order
,
1795 int *pid_list
, int list_sz
)
1797 struct i_jetsam_coalition
*cj
;
1799 cmpfunc_t cmp_func
= NULL
;
1800 struct coal_sort_s sort_array
[MAX_SORTED_PIDS
] = { {0,0,0} }; /* keep to < 2k */
1803 !(rolemask
& COALITION_ROLEMASK_ALLROLES
) ||
1804 !pid_list
|| list_sz
< 1) {
1805 coal_dbg("Invalid parameters: coal:%p, type:%d, rolemask:0x%x, "
1806 "pid_list:%p, list_sz:%d", coal
, coal
? coal
->type
: -1,
1807 rolemask
, pid_list
, list_sz
);
1811 switch (sort_order
) {
1812 case COALITION_SORT_NOSORT
:
1815 case COALITION_SORT_DEFAULT
:
1816 cmp_func
= dflt_cmp
;
1818 case COALITION_SORT_MEM_ASC
:
1819 cmp_func
= mem_asc_cmp
;
1821 case COALITION_SORT_MEM_DEC
:
1822 cmp_func
= mem_dec_cmp
;
1824 case COALITION_SORT_USER_ASC
:
1825 cmp_func
= usr_asc_cmp
;
1827 case COALITION_SORT_USER_DEC
:
1828 cmp_func
= usr_dec_cmp
;
1834 coalition_lock(coal
);
1836 if (coal
->type
== COALITION_TYPE_RESOURCE
) {
1837 ntasks
+= coalition_get_sort_list(coal
, sort_order
, &coal
->r
.tasks
,
1838 sort_array
, MAX_SORTED_PIDS
);
1844 if (rolemask
& COALITION_ROLEMASK_UNDEF
)
1845 ntasks
+= coalition_get_sort_list(coal
, sort_order
, &cj
->other
,
1846 sort_array
+ ntasks
,
1847 MAX_SORTED_PIDS
- ntasks
);
1849 if (rolemask
& COALITION_ROLEMASK_XPC
)
1850 ntasks
+= coalition_get_sort_list(coal
, sort_order
, &cj
->services
,
1851 sort_array
+ ntasks
,
1852 MAX_SORTED_PIDS
- ntasks
);
1854 if (rolemask
& COALITION_ROLEMASK_EXT
)
1855 ntasks
+= coalition_get_sort_list(coal
, sort_order
, &cj
->extensions
,
1856 sort_array
+ ntasks
,
1857 MAX_SORTED_PIDS
- ntasks
);
1859 if (rolemask
& COALITION_ROLEMASK_LEADER
)
1860 ntasks
+= coalition_get_sort_list(coal
, sort_order
, NULL
,
1861 sort_array
+ ntasks
,
1862 MAX_SORTED_PIDS
- ntasks
);
1865 coalition_unlock(coal
);
1867 /* sort based on the chosen criterion (no sense sorting 1 item) */
1868 if (cmp_func
&& ntasks
> 1)
1869 qsort(sort_array
, ntasks
, sizeof(struct coal_sort_s
), cmp_func
);
1871 for (int i
= 0; i
< ntasks
; i
++) {
1874 coal_dbg(" [%d] PID:%d, footprint:%lld, usr_order:%d",
1875 i
, sort_array
[i
].pid
, sort_array
[i
].bytes
,
1876 sort_array
[i
].usr_order
);
1877 pid_list
[i
] = sort_array
[i
].pid
;