2 * Copyright (c) 2019 Apple 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/exc_resource.h>
35 #include <kern/host.h>
36 #include <kern/kalloc.h>
37 #include <kern/ledger.h>
38 #include <kern/mach_param.h> /* for TASK_CHUNK */
40 #include <kern/monotonic.h>
41 #endif /* MONOTONIC */
42 #include <kern/policy_internal.h>
43 #include <kern/task.h>
44 #include <kern/thread_group.h>
45 #include <kern/zalloc.h>
47 #include <libkern/OSAtomic.h>
49 #include <mach/coalition_notification_server.h>
50 #include <mach/host_priv.h>
51 #include <mach/host_special_ports.h>
55 #include <sys/errno.h>
58 * BSD interface functions
60 int coalitions_get_list(int type
, struct procinfo_coalinfo
*coal_list
, int list_sz
);
61 coalition_t
task_get_coalition(task_t task
, int type
);
62 boolean_t
coalition_is_leader(task_t task
, coalition_t coal
);
63 task_t
coalition_get_leader(coalition_t coal
);
64 int coalition_get_task_count(coalition_t coal
);
65 uint64_t coalition_get_page_count(coalition_t coal
, int *ntasks
);
66 int coalition_get_pid_list(coalition_t coal
, uint32_t rolemask
, int sort_order
,
67 int *pid_list
, int list_sz
);
69 /* defined in task.c */
70 extern ledger_template_t task_ledger_template
;
73 * Templates; task template is copied due to potential allocation limits on
76 ledger_template_t coalition_task_ledger_template
= NULL
;
77 ledger_template_t coalition_ledger_template
= NULL
;
79 extern int proc_selfpid(void);
81 * Coalition zone needs limits. We expect there will be as many coalitions as
82 * tasks (same order of magnitude), so use the task zone's limits.
84 #define CONFIG_COALITION_MAX CONFIG_TASK_MAX
85 #define COALITION_CHUNK TASK_CHUNK
87 int unrestrict_coalition_syscalls
;
88 int merge_adaptive_coalitions
;
90 lck_attr_t coalitions_lck_attr
;
91 lck_grp_t coalitions_lck_grp
;
92 lck_grp_attr_t coalitions_lck_grp_attr
;
94 /* coalitions_list_lock protects coalition_count, coalitions queue, next_coalition_id. */
95 decl_lck_mtx_data(static, coalitions_list_lock
);
96 static uint64_t coalition_count
;
97 static uint64_t coalition_next_id
= 1;
98 static queue_head_t coalitions_q
;
100 coalition_t init_coalition
[COALITION_NUM_TYPES
];
101 coalition_t corpse_coalition
[COALITION_NUM_TYPES
];
103 zone_t coalition_zone
;
106 coal_type_str(int type
)
109 case COALITION_TYPE_RESOURCE
:
111 case COALITION_TYPE_JETSAM
:
118 struct coalition_type
{
123 * pre-condition: coalition just allocated (unlocked), unreferenced,
126 kern_return_t (*init
)(coalition_t coal
, boolean_t privileged
);
130 * pre-condition: coalition unlocked
131 * pre-condition: coalition refcount=0, active_count=0,
132 * termrequested=1, terminated=1, reaped=1
134 void (*dealloc
)(coalition_t coal
);
138 * pre-condition: coalition locked
139 * pre-condition: coalition !repead and !terminated
141 kern_return_t (*adopt_task
)(coalition_t coal
, task_t task
);
145 * pre-condition: coalition locked
146 * pre-condition: task has been removed from coalition's task list
148 kern_return_t (*remove_task
)(coalition_t coal
, task_t task
);
152 * pre-condition: coalition locked
153 * pre-condition: task added to coalition's task list,
154 * active_count >= 1 (at least the given task is active)
156 kern_return_t (*set_taskrole
)(coalition_t coal
, task_t task
, int role
);
160 * pre-condition: coalition locked
161 * pre-condition: task added to coalition's task list,
162 * active_count >= 1 (at least the given task is active)
164 int (*get_taskrole
)(coalition_t coal
, task_t task
);
168 * pre-condition: coalition locked
170 void (*iterate_tasks
)(coalition_t coal
, void *ctx
, void (*callback
)(coalition_t
, void *, task_t
));
174 * COALITION_TYPE_RESOURCE
177 static kern_return_t
i_coal_resource_init(coalition_t coal
, boolean_t privileged
);
178 static void i_coal_resource_dealloc(coalition_t coal
);
179 static kern_return_t
i_coal_resource_adopt_task(coalition_t coal
, task_t task
);
180 static kern_return_t
i_coal_resource_remove_task(coalition_t coal
, task_t task
);
181 static kern_return_t
i_coal_resource_set_taskrole(coalition_t coal
,
182 task_t task
, int role
);
183 static int i_coal_resource_get_taskrole(coalition_t coal
, task_t task
);
184 static void i_coal_resource_iterate_tasks(coalition_t coal
, void *ctx
,
185 void (*callback
)(coalition_t
, void *, task_t
));
188 * Ensure COALITION_NUM_THREAD_QOS_TYPES defined in mach/coalition.h still
189 * matches THREAD_QOS_LAST defined in mach/thread_policy.h
191 static_assert(COALITION_NUM_THREAD_QOS_TYPES
== THREAD_QOS_LAST
);
193 struct i_resource_coalition
{
195 * This keeps track of resource utilization of tasks that are no longer active
196 * in the coalition and is updated when a task is removed from the coalition.
200 uint64_t byteswritten
;
203 uint64_t logical_immediate_writes
;
204 uint64_t logical_deferred_writes
;
205 uint64_t logical_invalidated_writes
;
206 uint64_t logical_metadata_writes
;
207 uint64_t logical_immediate_writes_to_external
;
208 uint64_t logical_deferred_writes_to_external
;
209 uint64_t logical_invalidated_writes_to_external
;
210 uint64_t logical_metadata_writes_to_external
;
212 uint64_t cpu_time_eqos
[COALITION_NUM_THREAD_QOS_TYPES
]; /* cpu time per effective QoS class */
213 uint64_t cpu_time_rqos
[COALITION_NUM_THREAD_QOS_TYPES
]; /* cpu time per requested QoS class */
214 uint64_t cpu_instructions
;
217 uint64_t task_count
; /* tasks that have started in this coalition */
218 uint64_t dead_task_count
; /* tasks that have exited in this coalition;
219 * subtract from task_count to get count
220 * of "active" tasks */
222 * Count the length of time this coalition had at least one active task.
223 * This can be a 'denominator' to turn e.g. cpu_time to %cpu.
225 uint64_t last_became_nonempty_time
;
226 uint64_t time_nonempty
;
228 queue_head_t tasks
; /* List of active tasks in the coalition */
230 * This ledger is used for triggering resource exception. For the tracked resources, this is updated
231 * when the member tasks' resource usage changes.
233 ledger_t resource_monitor_ledger
;
237 * COALITION_TYPE_JETSAM
240 static kern_return_t
i_coal_jetsam_init(coalition_t coal
, boolean_t privileged
);
241 static void i_coal_jetsam_dealloc(coalition_t coal
);
242 static kern_return_t
i_coal_jetsam_adopt_task(coalition_t coal
, task_t task
);
243 static kern_return_t
i_coal_jetsam_remove_task(coalition_t coal
, task_t task
);
244 static kern_return_t
i_coal_jetsam_set_taskrole(coalition_t coal
,
245 task_t task
, int role
);
246 int i_coal_jetsam_get_taskrole(coalition_t coal
, task_t task
);
247 static void i_coal_jetsam_iterate_tasks(coalition_t coal
, void *ctx
,
248 void (*callback
)(coalition_t
, void *, task_t
));
250 struct i_jetsam_coalition
{
252 queue_head_t extensions
;
253 queue_head_t services
;
255 struct thread_group
*thread_group
;
260 * main coalition structure
263 uint64_t id
; /* monotonically increasing */
265 uint32_t role
; /* default task role (background, adaptive, interactive, etc) */
266 uint32_t ref_count
; /* Number of references to the memory containing this struct */
267 uint32_t active_count
; /* Number of members of (tasks in) the
268 * coalition, plus vouchers referring
269 * to the coalition */
270 uint32_t focal_task_count
; /* Number of TASK_FOREGROUND_APPLICATION tasks in the coalition */
271 uint32_t nonfocal_task_count
; /* Number of TASK_BACKGROUND_APPLICATION tasks in the coalition */
273 /* coalition flags */
274 uint32_t privileged
: 1; /* Members of this coalition may create
275 * and manage coalitions and may posix_spawn
276 * processes into selected coalitions */
279 uint32_t termrequested
: 1; /* launchd has requested termination when coalition becomes empty */
280 uint32_t terminated
: 1; /* coalition became empty and spawns are now forbidden */
281 uint32_t reaped
: 1; /* reaped, invisible to userspace, but waiting for ref_count to go to zero */
282 uint32_t notified
: 1; /* no-more-processes notification was sent via special port */
283 uint32_t efficient
: 1; /* launchd has marked the coalition as efficient */
284 #if DEVELOPMENT || DEBUG
285 uint32_t should_notify
: 1; /* should this coalition send notifications (default: yes) */
288 queue_chain_t coalitions
; /* global list of coalitions */
290 decl_lck_mtx_data(, lock
); /* Coalition lock. */
292 /* put coalition type-specific structures here */
294 struct i_resource_coalition r
;
295 struct i_jetsam_coalition j
;
300 * register different coalition types:
301 * these must be kept in the order specified in coalition.h
303 static const struct coalition_type
304 s_coalition_types
[COALITION_NUM_TYPES
] = {
306 COALITION_TYPE_RESOURCE
,
308 i_coal_resource_init
,
309 i_coal_resource_dealloc
,
310 i_coal_resource_adopt_task
,
311 i_coal_resource_remove_task
,
312 i_coal_resource_set_taskrole
,
313 i_coal_resource_get_taskrole
,
314 i_coal_resource_iterate_tasks
,
317 COALITION_TYPE_JETSAM
,
320 i_coal_jetsam_dealloc
,
321 i_coal_jetsam_adopt_task
,
322 i_coal_jetsam_remove_task
,
323 i_coal_jetsam_set_taskrole
,
324 i_coal_jetsam_get_taskrole
,
325 i_coal_jetsam_iterate_tasks
,
329 #define coal_call(coal, func, ...) \
330 (s_coalition_types[(coal)->type].func)(coal, ## __VA_ARGS__)
333 #define coalition_lock(c) do{ lck_mtx_lock(&c->lock); }while(0)
334 #define coalition_unlock(c) do{ lck_mtx_unlock(&c->lock); }while(0)
337 * Define the coalition type to track focal tasks.
338 * On embedded, track them using jetsam coalitions since they have associated thread
339 * groups which reflect this property as a flag (and pass it down to CLPC).
340 * On non-embedded platforms, since not all coalitions have jetsam coalitions
341 * track focal counts on the resource coalition.
344 #define COALITION_FOCAL_TASKS_ACCOUNTING COALITION_TYPE_JETSAM
345 #else /* CONFIG_EMBEDDED */
346 #define COALITION_FOCAL_TASKS_ACCOUNTING COALITION_TYPE_RESOURCE
347 #endif /* CONFIG_EMBEDDED */
352 * Coalition ledger implementation
356 struct coalition_ledger_indices coalition_ledgers
=
357 {.logical_writes
= -1, };
358 void __attribute__((noinline
)) SENDING_NOTIFICATION__THIS_COALITION_IS_CAUSING_TOO_MUCH_IO(int flavor
);
361 coalition_ledger_get_from_task(task_t task
)
363 ledger_t ledger
= LEDGER_NULL
;
364 coalition_t coal
= task
->coalition
[COALITION_TYPE_RESOURCE
];
366 if (coal
!= NULL
&& (!queue_empty(&task
->task_coalition
[COALITION_TYPE_RESOURCE
]))) {
367 ledger
= coal
->r
.resource_monitor_ledger
;
368 ledger_reference(ledger
);
375 COALITION_IO_LEDGER_ENABLE
,
376 COALITION_IO_LEDGER_DISABLE
380 coalition_io_monitor_ctl(struct coalition
*coalition
, uint32_t flags
, int64_t limit
)
382 ledger_t ledger
= coalition
->r
.resource_monitor_ledger
;
384 if (flags
== COALITION_IO_LEDGER_ENABLE
) {
385 /* Configure the logical I/O ledger */
386 ledger_set_limit(ledger
, coalition_ledgers
.logical_writes
, (limit
* 1024 * 1024), 0);
387 ledger_set_period(ledger
, coalition_ledgers
.logical_writes
, (COALITION_LEDGER_MONITOR_INTERVAL_SECS
* NSEC_PER_SEC
));
388 } else if (flags
== COALITION_IO_LEDGER_DISABLE
) {
389 ledger_disable_refill(ledger
, coalition_ledgers
.logical_writes
);
390 ledger_disable_callback(ledger
, coalition_ledgers
.logical_writes
);
395 coalition_ledger_set_logical_writes_limit(struct coalition
*coalition
, int64_t limit
)
399 /* limit = -1 will be used to disable the limit and the callback */
400 if (limit
> COALITION_MAX_LOGICAL_WRITES_LIMIT
|| limit
== 0 || limit
< -1) {
405 coalition_lock(coalition
);
407 coalition_io_monitor_ctl(coalition
, COALITION_IO_LEDGER_DISABLE
, limit
);
409 coalition_io_monitor_ctl(coalition
, COALITION_IO_LEDGER_ENABLE
, limit
);
411 coalition_unlock(coalition
);
416 void __attribute__((noinline
))
417 SENDING_NOTIFICATION__THIS_COALITION_IS_CAUSING_TOO_MUCH_IO(int flavor
)
419 int pid
= proc_selfpid();
420 ledger_amount_t new_limit
;
421 task_t task
= current_task();
422 struct ledger_entry_info lei
;
425 struct coalition
*coalition
= task
->coalition
[COALITION_TYPE_RESOURCE
];
427 assert(coalition
!= NULL
);
428 ledger
= coalition
->r
.resource_monitor_ledger
;
431 case FLAVOR_IO_LOGICAL_WRITES
:
432 ledger_get_entry_info(ledger
, coalition_ledgers
.logical_writes
, &lei
);
433 trace_resource_violation(RMON_LOGWRITES_VIOLATED
, &lei
);
439 os_log(OS_LOG_DEFAULT
, "Coalition [%lld] caught causing excessive I/O (flavor: %d). Task I/O: %lld MB. [Limit : %lld MB per %lld secs]. Triggered by process [%d]\n",
440 coalition
->id
, flavor
, (lei
.lei_balance
/ (1024 * 1024)), (lei
.lei_limit
/ (1024 * 1024)),
441 (lei
.lei_refill_period
/ NSEC_PER_SEC
), pid
);
443 kr
= send_resource_violation(send_disk_writes_violation
, task
, &lei
, kRNFlagsNone
);
445 os_log(OS_LOG_DEFAULT
, "ERROR %#x returned from send_resource_violation(disk_writes, ...)\n", kr
);
449 * Continue to monitor the coalition after it hits the initital limit, but increase
450 * the limit exponentially so that we don't spam the listener.
452 new_limit
= (lei
.lei_limit
/ 1024 / 1024) * 4;
453 coalition_lock(coalition
);
454 if (new_limit
> COALITION_MAX_LOGICAL_WRITES_LIMIT
) {
455 coalition_io_monitor_ctl(coalition
, COALITION_IO_LEDGER_DISABLE
, -1);
457 coalition_io_monitor_ctl(coalition
, COALITION_IO_LEDGER_ENABLE
, new_limit
);
459 coalition_unlock(coalition
);
466 coalition_io_rate_exceeded(int warning
, const void *param0
, __unused
const void *param1
)
469 SENDING_NOTIFICATION__THIS_COALITION_IS_CAUSING_TOO_MUCH_IO((int)param0
);
474 init_coalition_ledgers(void)
477 assert(coalition_ledger_template
== NULL
);
479 if ((t
= ledger_template_create("Per-coalition ledgers")) == NULL
) {
480 panic("couldn't create coalition ledger template");
483 coalition_ledgers
.logical_writes
= ledger_entry_add(t
, "logical_writes", "res", "bytes");
485 if (coalition_ledgers
.logical_writes
< 0) {
486 panic("couldn't create entries for coaliton ledger template");
489 ledger_set_callback(t
, coalition_ledgers
.logical_writes
, coalition_io_rate_exceeded
, (void *)FLAVOR_IO_LOGICAL_WRITES
, NULL
);
490 ledger_template_complete(t
);
492 coalition_task_ledger_template
= ledger_template_copy(task_ledger_template
, "Coalition task ledgers");
494 if (coalition_task_ledger_template
== NULL
) {
495 panic("couldn't create coalition task ledger template");
498 ledger_template_complete(coalition_task_ledger_template
);
500 coalition_ledger_template
= t
;
504 coalition_io_ledger_update(task_t task
, int32_t flavor
, boolean_t is_credit
, uint32_t io_size
)
507 coalition_t coal
= task
->coalition
[COALITION_TYPE_RESOURCE
];
509 assert(coal
!= NULL
);
510 ledger
= coal
->r
.resource_monitor_ledger
;
511 if (LEDGER_VALID(ledger
)) {
512 if (flavor
== FLAVOR_IO_LOGICAL_WRITES
) {
514 ledger_credit(ledger
, coalition_ledgers
.logical_writes
, io_size
);
516 ledger_debit(ledger
, coalition_ledgers
.logical_writes
, io_size
);
523 coalition_notify_user(uint64_t id
, uint32_t flags
)
525 mach_port_t user_port
;
528 kr
= host_get_coalition_port(host_priv_self(), &user_port
);
529 if ((kr
!= KERN_SUCCESS
) || !IPC_PORT_VALID(user_port
)) {
533 coalition_notification(user_port
, id
, flags
);
534 ipc_port_release_send(user_port
);
539 * COALITION_TYPE_RESOURCE
543 i_coal_resource_init(coalition_t coal
, boolean_t privileged
)
546 assert(coal
&& coal
->type
== COALITION_TYPE_RESOURCE
);
547 coal
->r
.ledger
= ledger_instantiate(coalition_task_ledger_template
,
548 LEDGER_CREATE_ACTIVE_ENTRIES
);
549 if (coal
->r
.ledger
== NULL
) {
550 return KERN_RESOURCE_SHORTAGE
;
553 coal
->r
.resource_monitor_ledger
= ledger_instantiate(coalition_ledger_template
,
554 LEDGER_CREATE_ACTIVE_ENTRIES
);
555 if (coal
->r
.resource_monitor_ledger
== NULL
) {
556 return KERN_RESOURCE_SHORTAGE
;
559 queue_init(&coal
->r
.tasks
);
565 i_coal_resource_dealloc(coalition_t coal
)
567 assert(coal
&& coal
->type
== COALITION_TYPE_RESOURCE
);
569 ledger_dereference(coal
->r
.ledger
);
570 ledger_dereference(coal
->r
.resource_monitor_ledger
);
574 i_coal_resource_adopt_task(coalition_t coal
, task_t task
)
576 struct i_resource_coalition
*cr
;
578 assert(coal
&& coal
->type
== COALITION_TYPE_RESOURCE
);
579 assert(queue_empty(&task
->task_coalition
[COALITION_TYPE_RESOURCE
]));
584 if (cr
->task_count
< cr
->dead_task_count
) {
585 panic("%s: coalition %p id:%llu type:%s task_count(%llu) < dead_task_count(%llu)",
586 __func__
, coal
, coal
->id
, coal_type_str(coal
->type
),
587 cr
->task_count
, cr
->dead_task_count
);
590 /* If moving from 0->1 active tasks */
591 if (cr
->task_count
- cr
->dead_task_count
== 1) {
592 cr
->last_became_nonempty_time
= mach_absolute_time();
595 /* put the task on the coalition's list of tasks */
596 enqueue_tail(&cr
->tasks
, &task
->task_coalition
[COALITION_TYPE_RESOURCE
]);
598 coal_dbg("Added PID:%d to id:%llu, task_count:%llu, dead_count:%llu, nonempty_time:%llu",
599 task_pid(task
), coal
->id
, cr
->task_count
, cr
->dead_task_count
,
600 cr
->last_became_nonempty_time
);
606 i_coal_resource_remove_task(coalition_t coal
, task_t task
)
608 struct i_resource_coalition
*cr
;
610 assert(coal
&& coal
->type
== COALITION_TYPE_RESOURCE
);
611 assert(task
->coalition
[COALITION_TYPE_RESOURCE
] == coal
);
612 assert(!queue_empty(&task
->task_coalition
[COALITION_TYPE_RESOURCE
]));
615 * handle resource coalition accounting rollup for dead tasks
619 cr
->dead_task_count
++;
621 if (cr
->task_count
< cr
->dead_task_count
) {
622 panic("%s: coalition %p id:%llu type:%s task_count(%llu) < dead_task_count(%llu)",
623 __func__
, coal
, coal
->id
, coal_type_str(coal
->type
), cr
->task_count
, cr
->dead_task_count
);
626 /* If moving from 1->0 active tasks */
627 if (cr
->task_count
- cr
->dead_task_count
== 0) {
628 uint64_t last_time_nonempty
= mach_absolute_time() - cr
->last_became_nonempty_time
;
629 cr
->last_became_nonempty_time
= 0;
630 cr
->time_nonempty
+= last_time_nonempty
;
633 /* Do not roll up for exec'd task or exec copy task */
634 if (!task_is_exec_copy(task
) && !task_did_exec(task
)) {
635 ledger_rollup(cr
->ledger
, task
->ledger
);
636 cr
->bytesread
+= task
->task_io_stats
->disk_reads
.size
;
637 cr
->byteswritten
+= task
->task_io_stats
->total_io
.size
- task
->task_io_stats
->disk_reads
.size
;
639 cr
->gpu_time
+= task_gpu_utilisation(task
);
641 cr
->energy
+= task_energy(task
);
643 cr
->logical_immediate_writes
+= task
->task_writes_counters_internal
.task_immediate_writes
;
644 cr
->logical_deferred_writes
+= task
->task_writes_counters_internal
.task_deferred_writes
;
645 cr
->logical_invalidated_writes
+= task
->task_writes_counters_internal
.task_invalidated_writes
;
646 cr
->logical_metadata_writes
+= task
->task_writes_counters_internal
.task_metadata_writes
;
647 cr
->logical_immediate_writes_to_external
+= task
->task_writes_counters_external
.task_immediate_writes
;
648 cr
->logical_deferred_writes_to_external
+= task
->task_writes_counters_external
.task_deferred_writes
;
649 cr
->logical_invalidated_writes_to_external
+= task
->task_writes_counters_external
.task_invalidated_writes
;
650 cr
->logical_metadata_writes_to_external
+= task
->task_writes_counters_external
.task_metadata_writes
;
651 cr
->cpu_ptime
+= task_cpu_ptime(task
);
652 task_update_cpu_time_qos_stats(task
, cr
->cpu_time_eqos
, cr
->cpu_time_rqos
);
654 uint64_t counts
[MT_CORE_NFIXED
] = {};
655 (void)mt_fixed_task_counts(task
, counts
);
656 cr
->cpu_cycles
+= counts
[MT_CORE_CYCLES
];
657 #if defined(MT_CORE_INSTRS)
658 cr
->cpu_instructions
+= counts
[MT_CORE_INSTRS
];
659 #endif /* defined(MT_CORE_INSTRS) */
660 #endif /* MONOTONIC */
663 /* remove the task from the coalition's list */
664 remqueue(&task
->task_coalition
[COALITION_TYPE_RESOURCE
]);
665 queue_chain_init(task
->task_coalition
[COALITION_TYPE_RESOURCE
]);
667 coal_dbg("removed PID:%d from id:%llu, task_count:%llu, dead_count:%llu",
668 task_pid(task
), coal
->id
, cr
->task_count
, cr
->dead_task_count
);
674 i_coal_resource_set_taskrole(__unused coalition_t coal
,
675 __unused task_t task
, __unused
int role
)
681 i_coal_resource_get_taskrole(__unused coalition_t coal
, __unused task_t task
)
685 assert(coal
&& coal
->type
== COALITION_TYPE_RESOURCE
);
687 qe_foreach_element(t
, &coal
->r
.tasks
, task_coalition
[COALITION_TYPE_RESOURCE
]) {
689 return COALITION_TASKROLE_UNDEF
;
697 i_coal_resource_iterate_tasks(coalition_t coal
, void *ctx
, void (*callback
)(coalition_t
, void *, task_t
))
700 assert(coal
&& coal
->type
== COALITION_TYPE_RESOURCE
);
702 qe_foreach_element(t
, &coal
->r
.tasks
, task_coalition
[COALITION_TYPE_RESOURCE
])
703 callback(coal
, ctx
, t
);
707 coalition_resource_usage_internal(coalition_t coal
, struct coalition_resource_usage
*cru_out
)
710 ledger_amount_t credit
, debit
;
713 if (coal
->type
!= COALITION_TYPE_RESOURCE
) {
714 return KERN_INVALID_ARGUMENT
;
717 /* Return KERN_INVALID_ARGUMENT for Corpse coalition */
718 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
719 if (coal
== corpse_coalition
[i
]) {
720 return KERN_INVALID_ARGUMENT
;
724 ledger_t sum_ledger
= ledger_instantiate(coalition_task_ledger_template
, LEDGER_CREATE_ACTIVE_ENTRIES
);
725 if (sum_ledger
== LEDGER_NULL
) {
726 return KERN_RESOURCE_SHORTAGE
;
729 coalition_lock(coal
);
732 * Start with the coalition's ledger, which holds the totals from all
735 ledger_rollup(sum_ledger
, coal
->r
.ledger
);
736 uint64_t bytesread
= coal
->r
.bytesread
;
737 uint64_t byteswritten
= coal
->r
.byteswritten
;
738 uint64_t gpu_time
= coal
->r
.gpu_time
;
739 uint64_t energy
= coal
->r
.energy
;
740 uint64_t logical_immediate_writes
= coal
->r
.logical_immediate_writes
;
741 uint64_t logical_deferred_writes
= coal
->r
.logical_deferred_writes
;
742 uint64_t logical_invalidated_writes
= coal
->r
.logical_invalidated_writes
;
743 uint64_t logical_metadata_writes
= coal
->r
.logical_metadata_writes
;
744 uint64_t logical_immediate_writes_to_external
= coal
->r
.logical_immediate_writes_to_external
;
745 uint64_t logical_deferred_writes_to_external
= coal
->r
.logical_deferred_writes_to_external
;
746 uint64_t logical_invalidated_writes_to_external
= coal
->r
.logical_invalidated_writes_to_external
;
747 uint64_t logical_metadata_writes_to_external
= coal
->r
.logical_metadata_writes_to_external
;
748 int64_t cpu_time_billed_to_me
= 0;
749 int64_t cpu_time_billed_to_others
= 0;
750 int64_t energy_billed_to_me
= 0;
751 int64_t energy_billed_to_others
= 0;
752 uint64_t cpu_ptime
= coal
->r
.cpu_ptime
;
753 uint64_t cpu_time_eqos
[COALITION_NUM_THREAD_QOS_TYPES
];
754 memcpy(cpu_time_eqos
, coal
->r
.cpu_time_eqos
, sizeof(cpu_time_eqos
));
755 uint64_t cpu_time_rqos
[COALITION_NUM_THREAD_QOS_TYPES
];
756 memcpy(cpu_time_rqos
, coal
->r
.cpu_time_rqos
, sizeof(cpu_time_rqos
));
757 uint64_t cpu_instructions
= coal
->r
.cpu_instructions
;
758 uint64_t cpu_cycles
= coal
->r
.cpu_cycles
;
761 * Add to that all the active tasks' ledgers. Tasks cannot deallocate
762 * out from under us, since we hold the coalition lock.
765 qe_foreach_element(task
, &coal
->r
.tasks
, task_coalition
[COALITION_TYPE_RESOURCE
]) {
767 * Rolling up stats for exec copy task or exec'd task will lead to double accounting.
768 * Cannot take task lock after taking coaliton lock
770 if (task_is_exec_copy(task
) || task_did_exec(task
)) {
774 ledger_rollup(sum_ledger
, task
->ledger
);
775 bytesread
+= task
->task_io_stats
->disk_reads
.size
;
776 byteswritten
+= task
->task_io_stats
->total_io
.size
- task
->task_io_stats
->disk_reads
.size
;
778 gpu_time
+= task_gpu_utilisation(task
);
780 energy
+= task_energy(task
);
782 logical_immediate_writes
+= task
->task_writes_counters_internal
.task_immediate_writes
;
783 logical_deferred_writes
+= task
->task_writes_counters_internal
.task_deferred_writes
;
784 logical_invalidated_writes
+= task
->task_writes_counters_internal
.task_invalidated_writes
;
785 logical_metadata_writes
+= task
->task_writes_counters_internal
.task_metadata_writes
;
786 logical_immediate_writes_to_external
+= task
->task_writes_counters_external
.task_immediate_writes
;
787 logical_deferred_writes_to_external
+= task
->task_writes_counters_external
.task_deferred_writes
;
788 logical_invalidated_writes_to_external
+= task
->task_writes_counters_external
.task_invalidated_writes
;
789 logical_metadata_writes_to_external
+= task
->task_writes_counters_external
.task_metadata_writes
;
791 cpu_ptime
+= task_cpu_ptime(task
);
792 task_update_cpu_time_qos_stats(task
, cpu_time_eqos
, cpu_time_rqos
);
794 uint64_t counts
[MT_CORE_NFIXED
] = {};
795 (void)mt_fixed_task_counts(task
, counts
);
796 cpu_cycles
+= counts
[MT_CORE_CYCLES
];
797 #if defined(MT_CORE_INSTRS)
798 cpu_instructions
+= counts
[MT_CORE_INSTRS
];
799 #endif /* defined(MT_CORE_INSTRS) */
800 #endif /* MONOTONIC */
803 kr
= ledger_get_balance(sum_ledger
, task_ledgers
.cpu_time_billed_to_me
, (int64_t *)&cpu_time_billed_to_me
);
804 if (kr
!= KERN_SUCCESS
|| cpu_time_billed_to_me
< 0) {
805 cpu_time_billed_to_me
= 0;
808 kr
= ledger_get_balance(sum_ledger
, task_ledgers
.cpu_time_billed_to_others
, (int64_t *)&cpu_time_billed_to_others
);
809 if (kr
!= KERN_SUCCESS
|| cpu_time_billed_to_others
< 0) {
810 cpu_time_billed_to_others
= 0;
813 kr
= ledger_get_balance(sum_ledger
, task_ledgers
.energy_billed_to_me
, (int64_t *)&energy_billed_to_me
);
814 if (kr
!= KERN_SUCCESS
|| energy_billed_to_me
< 0) {
815 energy_billed_to_me
= 0;
818 kr
= ledger_get_balance(sum_ledger
, task_ledgers
.energy_billed_to_others
, (int64_t *)&energy_billed_to_others
);
819 if (kr
!= KERN_SUCCESS
|| energy_billed_to_others
< 0) {
820 energy_billed_to_others
= 0;
823 /* collect information from the coalition itself */
824 cru_out
->tasks_started
= coal
->r
.task_count
;
825 cru_out
->tasks_exited
= coal
->r
.dead_task_count
;
827 uint64_t time_nonempty
= coal
->r
.time_nonempty
;
828 uint64_t last_became_nonempty_time
= coal
->r
.last_became_nonempty_time
;
830 coalition_unlock(coal
);
832 /* Copy the totals out of sum_ledger */
833 kr
= ledger_get_entries(sum_ledger
, task_ledgers
.cpu_time
,
835 if (kr
!= KERN_SUCCESS
) {
838 cru_out
->cpu_time
= credit
;
839 cru_out
->cpu_time_billed_to_me
= (uint64_t)cpu_time_billed_to_me
;
840 cru_out
->cpu_time_billed_to_others
= (uint64_t)cpu_time_billed_to_others
;
841 cru_out
->energy_billed_to_me
= (uint64_t)energy_billed_to_me
;
842 cru_out
->energy_billed_to_others
= (uint64_t)energy_billed_to_others
;
844 kr
= ledger_get_entries(sum_ledger
, task_ledgers
.interrupt_wakeups
,
846 if (kr
!= KERN_SUCCESS
) {
849 cru_out
->interrupt_wakeups
= credit
;
851 kr
= ledger_get_entries(sum_ledger
, task_ledgers
.platform_idle_wakeups
,
853 if (kr
!= KERN_SUCCESS
) {
856 cru_out
->platform_idle_wakeups
= credit
;
858 cru_out
->bytesread
= bytesread
;
859 cru_out
->byteswritten
= byteswritten
;
860 cru_out
->gpu_time
= gpu_time
;
861 cru_out
->energy
= energy
;
862 cru_out
->logical_immediate_writes
= logical_immediate_writes
;
863 cru_out
->logical_deferred_writes
= logical_deferred_writes
;
864 cru_out
->logical_invalidated_writes
= logical_invalidated_writes
;
865 cru_out
->logical_metadata_writes
= logical_metadata_writes
;
866 cru_out
->logical_immediate_writes_to_external
= logical_immediate_writes_to_external
;
867 cru_out
->logical_deferred_writes_to_external
= logical_deferred_writes_to_external
;
868 cru_out
->logical_invalidated_writes_to_external
= logical_invalidated_writes_to_external
;
869 cru_out
->logical_metadata_writes_to_external
= logical_metadata_writes_to_external
;
870 cru_out
->cpu_ptime
= cpu_ptime
;
871 cru_out
->cpu_time_eqos_len
= COALITION_NUM_THREAD_QOS_TYPES
;
872 memcpy(cru_out
->cpu_time_eqos
, cpu_time_eqos
, sizeof(cru_out
->cpu_time_eqos
));
873 cru_out
->cpu_cycles
= cpu_cycles
;
874 cru_out
->cpu_instructions
= cpu_instructions
;
875 ledger_dereference(sum_ledger
);
876 sum_ledger
= LEDGER_NULL
;
878 if (last_became_nonempty_time
) {
879 time_nonempty
+= mach_absolute_time() - last_became_nonempty_time
;
881 absolutetime_to_nanoseconds(time_nonempty
, &cru_out
->time_nonempty
);
888 * COALITION_TYPE_JETSAM
892 i_coal_jetsam_init(coalition_t coal
, boolean_t privileged
)
894 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
897 coal
->j
.leader
= TASK_NULL
;
898 queue_head_init(coal
->j
.extensions
);
899 queue_head_init(coal
->j
.services
);
900 queue_head_init(coal
->j
.other
);
906 i_coal_jetsam_dealloc(__unused coalition_t coal
)
908 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
910 /* the coalition should be completely clear at this point */
911 assert(queue_empty(&coal
->j
.extensions
));
912 assert(queue_empty(&coal
->j
.services
));
913 assert(queue_empty(&coal
->j
.other
));
914 assert(coal
->j
.leader
== TASK_NULL
);
919 i_coal_jetsam_adopt_task(coalition_t coal
, task_t task
)
921 struct i_jetsam_coalition
*cj
;
922 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
926 assert(queue_empty(&task
->task_coalition
[COALITION_TYPE_JETSAM
]));
928 /* put each task initially in the "other" list */
929 enqueue_tail(&cj
->other
, &task
->task_coalition
[COALITION_TYPE_JETSAM
]);
930 coal_dbg("coalition %lld adopted PID:%d as UNDEF",
931 coal
->id
, task_pid(task
));
937 i_coal_jetsam_remove_task(coalition_t coal
, task_t task
)
939 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
940 assert(task
->coalition
[COALITION_TYPE_JETSAM
] == coal
);
942 coal_dbg("removing PID:%d from coalition id:%lld",
943 task_pid(task
), coal
->id
);
945 if (task
== coal
->j
.leader
) {
946 coal
->j
.leader
= NULL
;
947 coal_dbg(" PID:%d was the leader!", task_pid(task
));
949 assert(!queue_empty(&task
->task_coalition
[COALITION_TYPE_JETSAM
]));
952 /* remove the task from the specific coalition role queue */
953 remqueue(&task
->task_coalition
[COALITION_TYPE_JETSAM
]);
954 queue_chain_init(task
->task_coalition
[COALITION_TYPE_RESOURCE
]);
960 i_coal_jetsam_set_taskrole(coalition_t coal
, task_t task
, int role
)
962 struct i_jetsam_coalition
*cj
;
964 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
965 assert(task
->coalition
[COALITION_TYPE_JETSAM
] == coal
);
970 case COALITION_TASKROLE_LEADER
:
971 coal_dbg("setting PID:%d as LEADER of %lld",
972 task_pid(task
), coal
->id
);
973 if (cj
->leader
!= TASK_NULL
) {
974 /* re-queue the exiting leader onto the "other" list */
975 coal_dbg(" re-queue existing leader (%d) as OTHER",
976 task_pid(cj
->leader
));
977 re_queue_tail(&cj
->other
, &cj
->leader
->task_coalition
[COALITION_TYPE_JETSAM
]);
980 * remove the task from the "other" list
981 * (where it was put by default)
983 remqueue(&task
->task_coalition
[COALITION_TYPE_JETSAM
]);
984 queue_chain_init(task
->task_coalition
[COALITION_TYPE_JETSAM
]);
986 /* set the coalition leader */
989 case COALITION_TASKROLE_XPC
:
990 coal_dbg("setting PID:%d as XPC in %lld",
991 task_pid(task
), coal
->id
);
992 q
= (queue_t
)&cj
->services
;
994 case COALITION_TASKROLE_EXT
:
995 coal_dbg("setting PID:%d as EXT in %lld",
996 task_pid(task
), coal
->id
);
997 q
= (queue_t
)&cj
->extensions
;
999 case COALITION_TASKROLE_NONE
:
1001 * Tasks with a role of "none" should fall through to an
1002 * undefined role so long as the task is currently a member
1003 * of the coalition. This scenario can happen if a task is
1004 * killed (usually via jetsam) during exec.
1006 if (task
->coalition
[COALITION_TYPE_JETSAM
] != coal
) {
1007 panic("%s: task %p attempting to set role %d "
1008 "in coalition %p to which it does not belong!", __func__
, task
, role
, coal
);
1011 case COALITION_TASKROLE_UNDEF
:
1012 coal_dbg("setting PID:%d as UNDEF in %lld",
1013 task_pid(task
), coal
->id
);
1014 q
= (queue_t
)&cj
->other
;
1017 panic("%s: invalid role(%d) for task", __func__
, role
);
1018 return KERN_INVALID_ARGUMENT
;
1022 re_queue_tail(q
, &task
->task_coalition
[COALITION_TYPE_JETSAM
]);
1025 return KERN_SUCCESS
;
1029 i_coal_jetsam_get_taskrole(coalition_t coal
, task_t task
)
1031 struct i_jetsam_coalition
*cj
;
1034 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
1035 assert(task
->coalition
[COALITION_TYPE_JETSAM
] == coal
);
1039 if (task
== cj
->leader
) {
1040 return COALITION_TASKROLE_LEADER
;
1043 qe_foreach_element(t
, &cj
->services
, task_coalition
[COALITION_TYPE_JETSAM
]) {
1045 return COALITION_TASKROLE_XPC
;
1049 qe_foreach_element(t
, &cj
->extensions
, task_coalition
[COALITION_TYPE_JETSAM
]) {
1051 return COALITION_TASKROLE_EXT
;
1055 qe_foreach_element(t
, &cj
->other
, task_coalition
[COALITION_TYPE_JETSAM
]) {
1057 return COALITION_TASKROLE_UNDEF
;
1061 /* task not in the coalition?! */
1062 return COALITION_TASKROLE_NONE
;
1066 i_coal_jetsam_iterate_tasks(coalition_t coal
, void *ctx
, void (*callback
)(coalition_t
, void *, task_t
))
1068 struct i_jetsam_coalition
*cj
;
1071 assert(coal
&& coal
->type
== COALITION_TYPE_JETSAM
);
1076 callback(coal
, ctx
, cj
->leader
);
1079 qe_foreach_element(t
, &cj
->services
, task_coalition
[COALITION_TYPE_JETSAM
])
1080 callback(coal
, ctx
, t
);
1082 qe_foreach_element(t
, &cj
->extensions
, task_coalition
[COALITION_TYPE_JETSAM
])
1083 callback(coal
, ctx
, t
);
1085 qe_foreach_element(t
, &cj
->other
, task_coalition
[COALITION_TYPE_JETSAM
])
1086 callback(coal
, ctx
, t
);
1092 * Main Coalition implementation
1097 * coalition_create_internal
1098 * Returns: New coalition object, referenced for the caller and unlocked.
1099 * Condition: coalitions_list_lock must be UNLOCKED.
1102 coalition_create_internal(int type
, int role
, boolean_t privileged
, coalition_t
*out
)
1105 struct coalition
*new_coal
;
1107 if (type
< 0 || type
> COALITION_TYPE_MAX
) {
1108 return KERN_INVALID_ARGUMENT
;
1111 new_coal
= (struct coalition
*)zalloc(coalition_zone
);
1112 if (new_coal
== COALITION_NULL
) {
1113 return KERN_RESOURCE_SHORTAGE
;
1115 bzero(new_coal
, sizeof(*new_coal
));
1117 new_coal
->type
= type
;
1118 new_coal
->role
= role
;
1120 /* initialize type-specific resources */
1121 kr
= coal_call(new_coal
, init
, privileged
);
1122 if (kr
!= KERN_SUCCESS
) {
1123 zfree(coalition_zone
, new_coal
);
1127 /* One for caller, one for coalitions list */
1128 new_coal
->ref_count
= 2;
1130 new_coal
->privileged
= privileged
? TRUE
: FALSE
;
1131 #if DEVELOPMENT || DEBUG
1132 new_coal
->should_notify
= 1;
1135 lck_mtx_init(&new_coal
->lock
, &coalitions_lck_grp
, &coalitions_lck_attr
);
1137 lck_mtx_lock(&coalitions_list_lock
);
1138 new_coal
->id
= coalition_next_id
++;
1140 enqueue_tail(&coalitions_q
, &new_coal
->coalitions
);
1142 KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_COALITION
, MACH_COALITION_NEW
),
1143 new_coal
->id
, new_coal
->type
);
1144 lck_mtx_unlock(&coalitions_list_lock
);
1146 coal_dbg("id:%llu, type:%s", new_coal
->id
, coal_type_str(new_coal
->type
));
1149 return KERN_SUCCESS
;
1154 * Condition: coalition must be UNLOCKED.
1157 coalition_release(coalition_t coal
)
1159 /* TODO: This can be done with atomics. */
1160 coalition_lock(coal
);
1164 uint32_t rc
= coal
->ref_count
;
1165 uint32_t ac
= coal
->active_count
;
1166 #endif /* COALITION_DEBUG */
1168 coal_dbg("id:%llu type:%s ref_count:%u active_count:%u%s",
1169 coal
->id
, coal_type_str(coal
->type
), rc
, ac
,
1170 rc
<= 0 ? ", will deallocate now" : "");
1172 if (coal
->ref_count
> 0) {
1173 coalition_unlock(coal
);
1177 assert(coal
->termrequested
);
1178 assert(coal
->terminated
);
1179 assert(coal
->active_count
== 0);
1180 assert(coal
->reaped
);
1181 assert(coal
->focal_task_count
== 0);
1182 assert(coal
->nonfocal_task_count
== 0);
1183 KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_COALITION
, MACH_COALITION_FREE
),
1184 coal
->id
, coal
->type
);
1186 coal_call(coal
, dealloc
);
1188 coalition_unlock(coal
);
1190 lck_mtx_destroy(&coal
->lock
, &coalitions_lck_grp
);
1192 zfree(coalition_zone
, coal
);
1196 * coalition_find_by_id_internal
1197 * Returns: Coalition object with specified id, NOT referenced.
1198 * If not found, returns COALITION_NULL.
1199 * Condition: coalitions_list_lock must be LOCKED.
1202 coalition_find_by_id_internal(uint64_t coal_id
)
1205 return COALITION_NULL
;
1208 lck_mtx_assert(&coalitions_list_lock
, LCK_MTX_ASSERT_OWNED
);
1210 qe_foreach_element(coal
, &coalitions_q
, coalitions
) {
1211 if (coal
->id
== coal_id
) {
1215 return COALITION_NULL
;
1219 * coalition_find_by_id
1220 * Returns: Coalition object with specified id, referenced.
1221 * Condition: coalitions_list_lock must be UNLOCKED.
1224 coalition_find_by_id(uint64_t cid
)
1227 return COALITION_NULL
;
1230 lck_mtx_lock(&coalitions_list_lock
);
1232 coalition_t coal
= coalition_find_by_id_internal(cid
);
1233 if (coal
== COALITION_NULL
) {
1234 lck_mtx_unlock(&coalitions_list_lock
);
1235 return COALITION_NULL
;
1238 coalition_lock(coal
);
1241 coalition_unlock(coal
);
1242 lck_mtx_unlock(&coalitions_list_lock
);
1243 return COALITION_NULL
;
1246 if (coal
->ref_count
== 0) {
1247 panic("resurrecting coalition %p id:%llu type:%s, active_count:%u\n",
1248 coal
, coal
->id
, coal_type_str(coal
->type
), coal
->active_count
);
1252 uint32_t rc
= coal
->ref_count
;
1255 coalition_unlock(coal
);
1256 lck_mtx_unlock(&coalitions_list_lock
);
1258 coal_dbg("id:%llu type:%s ref_count:%u",
1259 coal
->id
, coal_type_str(coal
->type
), rc
);
1265 * coalition_find_and_activate_by_id
1266 * Returns: Coalition object with specified id, referenced, and activated.
1267 * Condition: coalitions_list_lock must be UNLOCKED.
1268 * This is the function to use when putting a 'new' thing into a coalition,
1269 * like posix_spawn of an XPC service by launchd.
1270 * See also coalition_extend_active.
1273 coalition_find_and_activate_by_id(uint64_t cid
)
1276 return COALITION_NULL
;
1279 lck_mtx_lock(&coalitions_list_lock
);
1281 coalition_t coal
= coalition_find_by_id_internal(cid
);
1282 if (coal
== COALITION_NULL
) {
1283 lck_mtx_unlock(&coalitions_list_lock
);
1284 return COALITION_NULL
;
1287 coalition_lock(coal
);
1289 if (coal
->reaped
|| coal
->terminated
) {
1290 /* Too late to put something new into this coalition, it's
1291 * already on its way out the door */
1292 coalition_unlock(coal
);
1293 lck_mtx_unlock(&coalitions_list_lock
);
1294 return COALITION_NULL
;
1297 if (coal
->ref_count
== 0) {
1298 panic("resurrecting coalition %p id:%llu type:%s, active_count:%u\n",
1299 coal
, coal
->id
, coal_type_str(coal
->type
), coal
->active_count
);
1303 coal
->active_count
++;
1306 uint32_t rc
= coal
->ref_count
;
1307 uint32_t ac
= coal
->active_count
;
1310 coalition_unlock(coal
);
1311 lck_mtx_unlock(&coalitions_list_lock
);
1313 coal_dbg("id:%llu type:%s ref_count:%u, active_count:%u",
1314 coal
->id
, coal_type_str(coal
->type
), rc
, ac
);
1320 coalition_id(coalition_t coal
)
1326 task_coalition_ids(task_t task
, uint64_t ids
[COALITION_NUM_TYPES
])
1329 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
1330 if (task
->coalition
[i
]) {
1331 ids
[i
] = task
->coalition
[i
]->id
;
1339 task_coalition_roles(task_t task
, int roles
[COALITION_NUM_TYPES
])
1342 memset(roles
, 0, COALITION_NUM_TYPES
* sizeof(roles
[0]));
1344 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
1345 if (task
->coalition
[i
]) {
1346 coalition_lock(task
->coalition
[i
]);
1347 roles
[i
] = coal_call(task
->coalition
[i
],
1348 get_taskrole
, task
);
1349 coalition_unlock(task
->coalition
[i
]);
1351 roles
[i
] = COALITION_TASKROLE_NONE
;
1358 coalition_type(coalition_t coal
)
1364 coalition_term_requested(coalition_t coal
)
1366 return coal
->termrequested
;
1370 coalition_is_terminated(coalition_t coal
)
1372 return coal
->terminated
;
1376 coalition_is_reaped(coalition_t coal
)
1378 return coal
->reaped
;
1382 coalition_is_privileged(coalition_t coal
)
1384 return coal
->privileged
|| unrestrict_coalition_syscalls
;
1388 task_is_in_privileged_coalition(task_t task
, int type
)
1390 if (type
< 0 || type
> COALITION_TYPE_MAX
) {
1393 if (unrestrict_coalition_syscalls
) {
1396 if (!task
->coalition
[type
]) {
1399 return task
->coalition
[type
]->privileged
;
1403 task_coalition_update_gpu_stats(task_t task
, uint64_t gpu_ns_delta
)
1407 assert(task
!= TASK_NULL
);
1408 if (gpu_ns_delta
== 0) {
1412 coal
= task
->coalition
[COALITION_TYPE_RESOURCE
];
1413 assert(coal
!= COALITION_NULL
);
1415 coalition_lock(coal
);
1416 coal
->r
.gpu_time
+= gpu_ns_delta
;
1417 coalition_unlock(coal
);
1421 task_coalition_adjust_focal_count(task_t task
, int count
, uint32_t *new_count
)
1423 coalition_t coal
= task
->coalition
[COALITION_FOCAL_TASKS_ACCOUNTING
];
1424 if (coal
== COALITION_NULL
) {
1428 *new_count
= os_atomic_add(&coal
->focal_task_count
, count
, relaxed
);
1429 assert(*new_count
!= UINT32_MAX
);
1434 task_coalition_focal_count(task_t task
)
1436 coalition_t coal
= task
->coalition
[COALITION_FOCAL_TASKS_ACCOUNTING
];
1437 if (coal
== COALITION_NULL
) {
1441 return coal
->focal_task_count
;
1445 task_coalition_adjust_nonfocal_count(task_t task
, int count
, uint32_t *new_count
)
1447 coalition_t coal
= task
->coalition
[COALITION_FOCAL_TASKS_ACCOUNTING
];
1448 if (coal
== COALITION_NULL
) {
1452 *new_count
= os_atomic_add(&coal
->nonfocal_task_count
, count
, relaxed
);
1453 assert(*new_count
!= UINT32_MAX
);
1458 task_coalition_nonfocal_count(task_t task
)
1460 coalition_t coal
= task
->coalition
[COALITION_FOCAL_TASKS_ACCOUNTING
];
1461 if (coal
== COALITION_NULL
) {
1465 return coal
->nonfocal_task_count
;
1469 coalition_set_efficient(coalition_t coal
)
1471 coalition_lock(coal
);
1472 coal
->efficient
= TRUE
;
1473 coalition_unlock(coal
);
1478 coalition_for_each_task(coalition_t coal
, void *ctx
,
1479 void (*callback
)(coalition_t
, void *, task_t
))
1481 assert(coal
!= COALITION_NULL
);
1483 coal_dbg("iterating tasks in coalition %p id:%llu type:%s, active_count:%u",
1484 coal
, coal
->id
, coal_type_str(coal
->type
), coal
->active_count
);
1486 coalition_lock(coal
);
1488 coal_call(coal
, iterate_tasks
, ctx
, callback
);
1490 coalition_unlock(coal
);
1495 coalition_remove_active(coalition_t coal
)
1497 coalition_lock(coal
);
1499 assert(!coal
->reaped
);
1500 assert(coal
->active_count
> 0);
1502 coal
->active_count
--;
1504 boolean_t do_notify
= FALSE
;
1505 uint64_t notify_id
= 0;
1506 uint32_t notify_flags
= 0;
1507 if (coal
->termrequested
&& coal
->active_count
== 0) {
1508 /* We only notify once, when active_count reaches zero.
1509 * We just decremented, so if it reached zero, we mustn't have
1512 assert(!coal
->terminated
);
1513 coal
->terminated
= TRUE
;
1515 assert(!coal
->notified
);
1517 coal
->notified
= TRUE
;
1518 #if DEVELOPMENT || DEBUG
1519 do_notify
= coal
->should_notify
;
1523 notify_id
= coal
->id
;
1528 uint64_t cid
= coal
->id
;
1529 uint32_t rc
= coal
->ref_count
;
1530 int ac
= coal
->active_count
;
1531 int ct
= coal
->type
;
1533 coalition_unlock(coal
);
1535 coal_dbg("id:%llu type:%s ref_count:%u, active_count:%u,%s",
1536 cid
, coal_type_str(ct
), rc
, ac
, do_notify
? " NOTIFY" : " ");
1539 coalition_notify_user(notify_id
, notify_flags
);
1543 /* Used for kernel_task, launchd, launchd's early boot tasks... */
1545 coalitions_adopt_init_task(task_t task
)
1548 kr
= coalitions_adopt_task(init_coalition
, task
);
1549 if (kr
!= KERN_SUCCESS
) {
1550 panic("failed to adopt task %p into default coalition: %d", task
, kr
);
1555 /* Used for forked corpses. */
1557 coalitions_adopt_corpse_task(task_t task
)
1560 kr
= coalitions_adopt_task(corpse_coalition
, task
);
1561 if (kr
!= KERN_SUCCESS
) {
1562 panic("failed to adopt task %p into corpse coalition: %d", task
, kr
);
1568 * coalition_adopt_task_internal
1569 * Condition: Coalition must be referenced and unlocked. Will fail if coalition
1570 * is already terminated.
1572 static kern_return_t
1573 coalition_adopt_task_internal(coalition_t coal
, task_t task
)
1577 if (task
->coalition
[coal
->type
]) {
1578 return KERN_ALREADY_IN_SET
;
1581 coalition_lock(coal
);
1583 if (coal
->reaped
|| coal
->terminated
) {
1584 coalition_unlock(coal
);
1585 return KERN_TERMINATED
;
1588 kr
= coal_call(coal
, adopt_task
, task
);
1589 if (kr
!= KERN_SUCCESS
) {
1593 coal
->active_count
++;
1597 task
->coalition
[coal
->type
] = coal
;
1601 (void)coal
; /* need expression after label */
1602 uint64_t cid
= coal
->id
;
1603 uint32_t rc
= coal
->ref_count
;
1604 uint32_t ct
= coal
->type
;
1606 if (get_task_uniqueid(task
) != UINT64_MAX
) {
1607 /* On 32-bit targets, uniqueid will get truncated to 32 bits */
1608 KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_COALITION
, MACH_COALITION_ADOPT
),
1609 coal
->id
, get_task_uniqueid(task
));
1612 coalition_unlock(coal
);
1614 coal_dbg("task:%d, id:%llu type:%s ref_count:%u, kr=%d",
1615 task_pid(task
), cid
, coal_type_str(ct
), rc
, kr
);
1619 static kern_return_t
1620 coalition_remove_task_internal(task_t task
, int type
)
1624 coalition_t coal
= task
->coalition
[type
];
1627 return KERN_SUCCESS
;
1630 assert(coal
->type
== (uint32_t)type
);
1632 coalition_lock(coal
);
1634 kr
= coal_call(coal
, remove_task
, task
);
1637 uint64_t cid
= coal
->id
;
1638 uint32_t rc
= coal
->ref_count
;
1639 int ac
= coal
->active_count
;
1640 int ct
= coal
->type
;
1642 KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_COALITION
, MACH_COALITION_REMOVE
),
1643 coal
->id
, get_task_uniqueid(task
));
1644 coalition_unlock(coal
);
1646 coal_dbg("id:%llu type:%s ref_count:%u, active_count:%u, kr=%d",
1647 cid
, coal_type_str(ct
), rc
, ac
, kr
);
1649 coalition_remove_active(coal
);
1655 * coalitions_adopt_task
1656 * Condition: All coalitions must be referenced and unlocked.
1657 * Will fail if any coalition is already terminated.
1660 coalitions_adopt_task(coalition_t
*coals
, task_t task
)
1665 if (!coals
|| coals
[COALITION_TYPE_RESOURCE
] == COALITION_NULL
) {
1666 return KERN_INVALID_ARGUMENT
;
1669 /* verify that the incoming coalitions are what they say they are */
1670 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
1671 if (coals
[i
] && coals
[i
]->type
!= (uint32_t)i
) {
1672 return KERN_INVALID_ARGUMENT
;
1676 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
1679 kr
= coalition_adopt_task_internal(coals
[i
], task
);
1681 if (kr
!= KERN_SUCCESS
) {
1682 /* dis-associate any coalitions that just adopted this task */
1684 if (task
->coalition
[i
]) {
1685 coalition_remove_task_internal(task
, i
);
1695 * coalitions_remove_task
1696 * Condition: task must be referenced and UNLOCKED; all task's coalitions must be UNLOCKED
1699 coalitions_remove_task(task_t task
)
1704 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
1705 kr
= coalition_remove_task_internal(task
, i
);
1706 assert(kr
== KERN_SUCCESS
);
1713 * task_release_coalitions
1714 * helper function to release references to all coalitions in which
1715 * 'task' is a member.
1718 task_release_coalitions(task_t task
)
1721 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
1722 if (task
->coalition
[i
]) {
1723 coalition_release(task
->coalition
[i
]);
1724 } else if (i
== COALITION_TYPE_RESOURCE
) {
1725 panic("deallocating task %p was not a member of a resource coalition", task
);
1731 * coalitions_set_roles
1732 * for each type of coalition, if the task is a member of a coalition of
1733 * that type (given in the coalitions parameter) then set the role of
1734 * the task within that that coalition.
1737 coalitions_set_roles(coalition_t coalitions
[COALITION_NUM_TYPES
],
1738 task_t task
, int roles
[COALITION_NUM_TYPES
])
1740 kern_return_t kr
= KERN_SUCCESS
;
1743 for (i
= 0; i
< COALITION_NUM_TYPES
; i
++) {
1744 if (!coalitions
[i
]) {
1747 coalition_lock(coalitions
[i
]);
1748 kr
= coal_call(coalitions
[i
], set_taskrole
, task
, roles
[i
]);
1749 coalition_unlock(coalitions
[i
]);
1750 assert(kr
== KERN_SUCCESS
);
1757 * coalition_terminate_internal
1758 * Condition: Coalition must be referenced and UNLOCKED.
1761 coalition_request_terminate_internal(coalition_t coal
)
1763 assert(coal
->type
>= 0 && coal
->type
<= COALITION_TYPE_MAX
);
1765 if (coal
== init_coalition
[coal
->type
]) {
1766 return KERN_DEFAULT_SET
;
1769 coalition_lock(coal
);
1772 coalition_unlock(coal
);
1773 return KERN_INVALID_NAME
;
1776 if (coal
->terminated
|| coal
->termrequested
) {
1777 coalition_unlock(coal
);
1778 return KERN_TERMINATED
;
1781 coal
->termrequested
= TRUE
;
1783 boolean_t do_notify
= FALSE
;
1784 uint64_t note_id
= 0;
1785 uint32_t note_flags
= 0;
1787 if (coal
->active_count
== 0) {
1789 * We only notify once, when active_count reaches zero.
1790 * We just set termrequested to zero. If the active count
1791 * was already at zero (tasks died before we could request
1792 * a termination notification), we should notify.
1794 assert(!coal
->terminated
);
1795 coal
->terminated
= TRUE
;
1797 assert(!coal
->notified
);
1799 coal
->notified
= TRUE
;
1800 #if DEVELOPMENT || DEBUG
1801 do_notify
= coal
->should_notify
;
1809 coalition_unlock(coal
);
1812 coalition_notify_user(note_id
, note_flags
);
1815 return KERN_SUCCESS
;
1819 * coalition_reap_internal
1820 * Condition: Coalition must be referenced and UNLOCKED.
1823 coalition_reap_internal(coalition_t coal
)
1825 assert(coal
->type
<= COALITION_TYPE_MAX
);
1827 if (coal
== init_coalition
[coal
->type
]) {
1828 return KERN_DEFAULT_SET
;
1831 coalition_lock(coal
);
1833 coalition_unlock(coal
);
1834 return KERN_TERMINATED
;
1836 if (!coal
->terminated
) {
1837 coalition_unlock(coal
);
1838 return KERN_FAILURE
;
1840 assert(coal
->termrequested
);
1841 if (coal
->active_count
> 0) {
1842 coalition_unlock(coal
);
1843 return KERN_FAILURE
;
1846 coal
->reaped
= TRUE
;
1848 /* Caller, launchd, and coalitions list should each have a reference */
1849 assert(coal
->ref_count
> 2);
1851 coalition_unlock(coal
);
1853 lck_mtx_lock(&coalitions_list_lock
);
1855 remqueue(&coal
->coalitions
);
1856 lck_mtx_unlock(&coalitions_list_lock
);
1858 /* Release the list's reference and launchd's reference. */
1859 coalition_release(coal
);
1860 coalition_release(coal
);
1862 return KERN_SUCCESS
;
1865 #if DEVELOPMENT || DEBUG
1867 coalition_should_notify(coalition_t coal
)
1873 coalition_lock(coal
);
1874 should
= coal
->should_notify
;
1875 coalition_unlock(coal
);
1881 coalition_set_notify(coalition_t coal
, int notify
)
1886 coalition_lock(coal
);
1887 coal
->should_notify
= !!notify
;
1888 coalition_unlock(coal
);
1893 coalitions_init(void)
1897 const struct coalition_type
*ctype
;
1899 coalition_zone
= zinit(
1900 sizeof(struct coalition
),
1901 CONFIG_COALITION_MAX
* sizeof(struct coalition
),
1902 COALITION_CHUNK
* sizeof(struct coalition
),
1904 zone_change(coalition_zone
, Z_NOENCRYPT
, TRUE
);
1905 queue_head_init(coalitions_q
);
1907 if (!PE_parse_boot_argn("unrestrict_coalition_syscalls", &unrestrict_coalition_syscalls
,
1908 sizeof(unrestrict_coalition_syscalls
))) {
1909 unrestrict_coalition_syscalls
= 0;
1912 if (!PE_parse_boot_argn("tg_adaptive", &merge_adaptive_coalitions
,
1913 sizeof(merge_adaptive_coalitions
))) {
1914 merge_adaptive_coalitions
= 0;
1917 lck_grp_attr_setdefault(&coalitions_lck_grp_attr
);
1918 lck_grp_init(&coalitions_lck_grp
, "coalition", &coalitions_lck_grp_attr
);
1919 lck_attr_setdefault(&coalitions_lck_attr
);
1920 lck_mtx_init(&coalitions_list_lock
, &coalitions_lck_grp
, &coalitions_lck_attr
);
1922 init_task_ledgers();
1924 init_coalition_ledgers();
1926 for (i
= 0, ctype
= &s_coalition_types
[0]; i
< COALITION_NUM_TYPES
; ctype
++, i
++) {
1927 /* verify the entry in the global coalition types array */
1928 if (ctype
->type
!= i
||
1931 !ctype
->adopt_task
||
1932 !ctype
->remove_task
) {
1933 panic("%s: Malformed coalition type %s(%d) in slot for type:%s(%d)",
1934 __func__
, coal_type_str(ctype
->type
), ctype
->type
, coal_type_str(i
), i
);
1936 if (!ctype
->has_default
) {
1939 kr
= coalition_create_internal(ctype
->type
, COALITION_ROLE_SYSTEM
, TRUE
, &init_coalition
[ctype
->type
]);
1940 if (kr
!= KERN_SUCCESS
) {
1941 panic("%s: could not create init %s coalition: kr:%d",
1942 __func__
, coal_type_str(i
), kr
);
1944 kr
= coalition_create_internal(ctype
->type
, COALITION_ROLE_SYSTEM
, FALSE
, &corpse_coalition
[ctype
->type
]);
1945 if (kr
!= KERN_SUCCESS
) {
1946 panic("%s: could not create corpse %s coalition: kr:%d",
1947 __func__
, coal_type_str(i
), kr
);
1951 /* "Leak" our reference to the global object */
1955 * BSD Kernel interface functions
1959 coalition_fill_procinfo(struct coalition
*coal
,
1960 struct procinfo_coalinfo
*coalinfo
)
1962 coalinfo
->coalition_id
= coal
->id
;
1963 coalinfo
->coalition_type
= coal
->type
;
1964 coalinfo
->coalition_tasks
= coalition_get_task_count(coal
);
1969 coalitions_get_list(int type
, struct procinfo_coalinfo
*coal_list
, int list_sz
)
1972 struct coalition
*coal
;
1974 lck_mtx_lock(&coalitions_list_lock
);
1975 qe_foreach_element(coal
, &coalitions_q
, coalitions
) {
1976 if (!coal
->reaped
&& (type
< 0 || type
== (int)coal
->type
)) {
1977 if (coal_list
&& ncoals
< list_sz
) {
1978 coalition_fill_procinfo(coal
, &coal_list
[ncoals
]);
1983 lck_mtx_unlock(&coalitions_list_lock
);
1989 * Return the coaltion of the given type to which the task belongs.
1992 task_get_coalition(task_t task
, int coal_type
)
1996 if (task
== NULL
|| coal_type
> COALITION_TYPE_MAX
) {
1997 return COALITION_NULL
;
2000 c
= task
->coalition
[coal_type
];
2001 assert(c
== COALITION_NULL
|| (int)c
->type
== coal_type
);
2006 * Report if the given task is the leader of the given jetsam coalition.
2009 coalition_is_leader(task_t task
, coalition_t coal
)
2011 boolean_t ret
= FALSE
;
2013 if (coal
!= COALITION_NULL
) {
2014 coalition_lock(coal
);
2016 ret
= (coal
->type
== COALITION_TYPE_JETSAM
&& coal
->j
.leader
== task
);
2018 coalition_unlock(coal
);
2025 coalition_iterate_stackshot(coalition_iterate_fn_t callout
, void *arg
, uint32_t coalition_type
)
2030 qe_foreach_element(coal
, &coalitions_q
, coalitions
) {
2031 if (coal
== NULL
|| !ml_validate_nofault((vm_offset_t
)coal
, sizeof(struct coalition
))) {
2032 return KERN_FAILURE
;
2035 if (coalition_type
== coal
->type
) {
2036 callout(arg
, i
++, coal
);
2040 return KERN_SUCCESS
;
2044 kdp_coalition_get_leader(coalition_t coal
)
2050 if (coal
->type
== COALITION_TYPE_JETSAM
) {
2051 return coal
->j
.leader
;
2057 coalition_get_leader(coalition_t coal
)
2059 task_t leader
= TASK_NULL
;
2065 coalition_lock(coal
);
2066 if (coal
->type
!= COALITION_TYPE_JETSAM
) {
2070 leader
= coal
->j
.leader
;
2071 if (leader
!= TASK_NULL
) {
2072 task_reference(leader
);
2076 coalition_unlock(coal
);
2082 coalition_get_task_count(coalition_t coal
)
2085 struct queue_entry
*qe
;
2090 coalition_lock(coal
);
2091 switch (coal
->type
) {
2092 case COALITION_TYPE_RESOURCE
:
2093 qe_foreach(qe
, &coal
->r
.tasks
)
2096 case COALITION_TYPE_JETSAM
:
2097 if (coal
->j
.leader
) {
2100 qe_foreach(qe
, &coal
->j
.other
)
2102 qe_foreach(qe
, &coal
->j
.extensions
)
2104 qe_foreach(qe
, &coal
->j
.services
)
2110 coalition_unlock(coal
);
2117 i_get_list_footprint(queue_t list
, int type
, int *ntasks
)
2122 qe_foreach_element(task
, list
, task_coalition
[type
]) {
2123 bytes
+= get_task_phys_footprint(task
);
2124 coal_dbg(" [%d] task_pid:%d, type:%d, footprint:%lld",
2125 *ntasks
, task_pid(task
), type
, bytes
);
2133 coalition_get_page_count(coalition_t coal
, int *ntasks
)
2145 coalition_lock(coal
);
2147 switch (coal
->type
) {
2148 case COALITION_TYPE_RESOURCE
:
2149 bytes
+= i_get_list_footprint(&coal
->r
.tasks
, COALITION_TYPE_RESOURCE
, &num_tasks
);
2151 case COALITION_TYPE_JETSAM
:
2152 if (coal
->j
.leader
) {
2153 bytes
+= get_task_phys_footprint(coal
->j
.leader
);
2156 bytes
+= i_get_list_footprint(&coal
->j
.extensions
, COALITION_TYPE_JETSAM
, &num_tasks
);
2157 bytes
+= i_get_list_footprint(&coal
->j
.services
, COALITION_TYPE_JETSAM
, &num_tasks
);
2158 bytes
+= i_get_list_footprint(&coal
->j
.other
, COALITION_TYPE_JETSAM
, &num_tasks
);
2164 coalition_unlock(coal
);
2167 *ntasks
= num_tasks
;
2170 return bytes
/ PAGE_SIZE_64
;
2173 struct coal_sort_s
{
2180 * return < 0 for a < b
2184 typedef int (*cmpfunc_t
)(const void *a
, const void *b
);
2187 qsort(void *a
, size_t n
, size_t es
, cmpfunc_t cmp
);
2190 dflt_cmp(const void *a
, const void *b
)
2192 const struct coal_sort_s
*csA
= (const struct coal_sort_s
*)a
;
2193 const struct coal_sort_s
*csB
= (const struct coal_sort_s
*)b
;
2196 * if both A and B are equal, use a memory descending sort
2198 if (csA
->usr_order
== csB
->usr_order
) {
2199 return (int)((int64_t)csB
->bytes
- (int64_t)csA
->bytes
);
2202 /* otherwise, return the relationship between user specified orders */
2203 return csA
->usr_order
- csB
->usr_order
;
2207 mem_asc_cmp(const void *a
, const void *b
)
2209 const struct coal_sort_s
*csA
= (const struct coal_sort_s
*)a
;
2210 const struct coal_sort_s
*csB
= (const struct coal_sort_s
*)b
;
2212 return (int)((int64_t)csA
->bytes
- (int64_t)csB
->bytes
);
2216 mem_dec_cmp(const void *a
, const void *b
)
2218 const struct coal_sort_s
*csA
= (const struct coal_sort_s
*)a
;
2219 const struct coal_sort_s
*csB
= (const struct coal_sort_s
*)b
;
2221 return (int)((int64_t)csB
->bytes
- (int64_t)csA
->bytes
);
2225 usr_asc_cmp(const void *a
, const void *b
)
2227 const struct coal_sort_s
*csA
= (const struct coal_sort_s
*)a
;
2228 const struct coal_sort_s
*csB
= (const struct coal_sort_s
*)b
;
2230 return csA
->usr_order
- csB
->usr_order
;
2234 usr_dec_cmp(const void *a
, const void *b
)
2236 const struct coal_sort_s
*csA
= (const struct coal_sort_s
*)a
;
2237 const struct coal_sort_s
*csB
= (const struct coal_sort_s
*)b
;
2239 return csB
->usr_order
- csA
->usr_order
;
2242 /* avoid dynamic allocation in this path */
2243 #define MAX_SORTED_PIDS 80
2246 coalition_get_sort_list(coalition_t coal
, int sort_order
, queue_t list
,
2247 struct coal_sort_s
*sort_array
, int array_sz
)
2252 assert(sort_array
!= NULL
);
2254 if (array_sz
<= 0) {
2260 * this function will only be called with a NULL
2261 * list for JETSAM-type coalitions, and is intended
2262 * to investigate the leader process
2264 if (coal
->type
!= COALITION_TYPE_JETSAM
||
2265 coal
->j
.leader
== TASK_NULL
) {
2268 sort_array
[0].pid
= task_pid(coal
->j
.leader
);
2269 switch (sort_order
) {
2270 case COALITION_SORT_DEFAULT
:
2271 sort_array
[0].usr_order
= 0;
2273 case COALITION_SORT_MEM_ASC
:
2274 case COALITION_SORT_MEM_DEC
:
2275 sort_array
[0].bytes
= get_task_phys_footprint(coal
->j
.leader
);
2277 case COALITION_SORT_USER_ASC
:
2278 case COALITION_SORT_USER_DEC
:
2279 sort_array
[0].usr_order
= 0;
2287 qe_foreach_element(task
, list
, task_coalition
[coal
->type
]) {
2288 if (ntasks
>= array_sz
) {
2289 printf("WARNING: more than %d pids in coalition %llu\n",
2290 MAX_SORTED_PIDS
, coal
->id
);
2294 sort_array
[ntasks
].pid
= task_pid(task
);
2296 switch (sort_order
) {
2297 case COALITION_SORT_DEFAULT
:
2298 sort_array
[ntasks
].usr_order
= 0;
2300 case COALITION_SORT_MEM_ASC
:
2301 case COALITION_SORT_MEM_DEC
:
2302 sort_array
[ntasks
].bytes
= get_task_phys_footprint(task
);
2304 case COALITION_SORT_USER_ASC
:
2305 case COALITION_SORT_USER_DEC
:
2306 sort_array
[ntasks
].usr_order
= 0;
2319 coalition_get_pid_list(coalition_t coal
, uint32_t rolemask
, int sort_order
,
2320 int *pid_list
, int list_sz
)
2322 struct i_jetsam_coalition
*cj
;
2324 cmpfunc_t cmp_func
= NULL
;
2325 struct coal_sort_s sort_array
[MAX_SORTED_PIDS
] = { {0, 0, 0} }; /* keep to < 2k */
2328 !(rolemask
& COALITION_ROLEMASK_ALLROLES
) ||
2329 !pid_list
|| list_sz
< 1) {
2330 coal_dbg("Invalid parameters: coal:%p, type:%d, rolemask:0x%x, "
2331 "pid_list:%p, list_sz:%d", coal
, coal
? coal
->type
: -1,
2332 rolemask
, pid_list
, list_sz
);
2336 switch (sort_order
) {
2337 case COALITION_SORT_NOSORT
:
2340 case COALITION_SORT_DEFAULT
:
2341 cmp_func
= dflt_cmp
;
2343 case COALITION_SORT_MEM_ASC
:
2344 cmp_func
= mem_asc_cmp
;
2346 case COALITION_SORT_MEM_DEC
:
2347 cmp_func
= mem_dec_cmp
;
2349 case COALITION_SORT_USER_ASC
:
2350 cmp_func
= usr_asc_cmp
;
2352 case COALITION_SORT_USER_DEC
:
2353 cmp_func
= usr_dec_cmp
;
2359 coalition_lock(coal
);
2361 if (coal
->type
== COALITION_TYPE_RESOURCE
) {
2362 ntasks
+= coalition_get_sort_list(coal
, sort_order
, &coal
->r
.tasks
,
2363 sort_array
, MAX_SORTED_PIDS
);
2369 if (rolemask
& COALITION_ROLEMASK_UNDEF
) {
2370 ntasks
+= coalition_get_sort_list(coal
, sort_order
, &cj
->other
,
2371 sort_array
+ ntasks
,
2372 MAX_SORTED_PIDS
- ntasks
);
2375 if (rolemask
& COALITION_ROLEMASK_XPC
) {
2376 ntasks
+= coalition_get_sort_list(coal
, sort_order
, &cj
->services
,
2377 sort_array
+ ntasks
,
2378 MAX_SORTED_PIDS
- ntasks
);
2381 if (rolemask
& COALITION_ROLEMASK_EXT
) {
2382 ntasks
+= coalition_get_sort_list(coal
, sort_order
, &cj
->extensions
,
2383 sort_array
+ ntasks
,
2384 MAX_SORTED_PIDS
- ntasks
);
2387 if (rolemask
& COALITION_ROLEMASK_LEADER
) {
2388 ntasks
+= coalition_get_sort_list(coal
, sort_order
, NULL
,
2389 sort_array
+ ntasks
,
2390 MAX_SORTED_PIDS
- ntasks
);
2394 coalition_unlock(coal
);
2396 /* sort based on the chosen criterion (no sense sorting 1 item) */
2397 if (cmp_func
&& ntasks
> 1) {
2398 qsort(sort_array
, ntasks
, sizeof(struct coal_sort_s
), cmp_func
);
2401 for (int i
= 0; i
< ntasks
; i
++) {
2405 coal_dbg(" [%d] PID:%d, footprint:%lld, usr_order:%d",
2406 i
, sort_array
[i
].pid
, sort_array
[i
].bytes
,
2407 sort_array
[i
].usr_order
);
2408 pid_list
[i
] = sort_array
[i
].pid
;