]> git.saurik.com Git - apple/xnu.git/blob - osfmk/kern/coalition.c
xnu-2782.30.5.tar.gz
[apple/xnu.git] / osfmk / kern / coalition.c
1 /*
2 * Copyright (c) 2013 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <kern/kern_types.h>
30 #include <mach/mach_types.h>
31 #include <mach/boolean.h>
32
33 #include <kern/coalition.h>
34 #include <kern/host.h>
35 #include <kern/ledger.h>
36 #include <kern/kalloc.h>
37 #include <kern/mach_param.h> /* for TASK_CHUNK */
38 #include <kern/task.h>
39 #include <kern/zalloc.h>
40 #include <kern/sfi.h>
41
42 #include <libkern/OSAtomic.h>
43
44 #include <mach/coalition_notification_server.h>
45 #include <mach/host_priv.h>
46 #include <mach/host_special_ports.h>
47
48 #include <sys/errno.h>
49
50 /* defined in task.c */
51 extern ledger_template_t task_ledger_template;
52
53 /*
54 * Coalition zone needs limits. We expect there will be as many coalitions as
55 * tasks (same order of magnitude), so use the task zone's limits.
56 * */
57 #define CONFIG_COALITION_MAX CONFIG_TASK_MAX
58 #define COALITION_CHUNK TASK_CHUNK
59
60 int unrestrict_coalition_syscalls;
61
62 lck_attr_t coalitions_lck_attr;
63 lck_grp_t coalitions_lck_grp;
64 lck_grp_attr_t coalitions_lck_grp_attr;
65
66 /* coalitions_list_lock protects coalition_count, coalitions queue, next_coalition_id. */
67 decl_lck_mtx_data(static,coalitions_list_lock);
68 static uint64_t coalition_count;
69 static uint64_t coalition_next_id = 1;
70 static queue_head_t coalitions;
71
72 coalition_t default_coalition;
73
74 zone_t coalition_zone;
75
76 struct coalition {
77 uint64_t id; /* monotonically increasing */
78
79 ledger_t ledger;
80 uint64_t bytesread;
81 uint64_t byteswritten;
82 uint64_t gpu_time;
83
84 /*
85 * Count the length of time this coalition had at least one active task.
86 * This can be a 'denominator' to turn e.g. cpu_time to %cpu.
87 * */
88 uint64_t last_became_nonempty_time;
89 uint64_t time_nonempty;
90
91 uint64_t task_count; /* Count of tasks that have started in this coalition */
92 uint64_t dead_task_count; /* Count of tasks that have exited in this coalition; subtract from task_count to get count of "active" */
93 queue_head_t tasks; /* List of active tasks in the coalition */
94
95 queue_chain_t coalitions; /* global list of coalitions */
96
97 decl_lck_mtx_data(,lock) /* Coalition lock. */
98
99 uint32_t ref_count; /* Number of references to the memory containing this struct */
100 uint32_t active_count; /* Number of members of (tasks in) the coalition, plus vouchers referring to the coalition */
101
102 unsigned int privileged : 1; /* Members of this coalition may create and manage coalitions and may posix_spawn processes into selected coalitions */
103
104 /* ast? */
105
106 /* voucher */
107
108 /* state of the coalition */
109 unsigned int termrequested : 1; /* launchd has requested termination when coalition becomes empty */
110 unsigned int terminated : 1; /* coalition became empty and spawns are now forbidden */
111 unsigned int reaped : 1; /* reaped, invisible to userspace, but waiting for ref_count to go to zero */
112 unsigned int notified : 1; /* no-more-processes notification was sent via special port */
113
114 uint32_t focal_tasks_count; /* count of TASK_FOREGROUND_APPLICATION tasks in the coalition */
115 uint32_t non_focal_tasks_count; /* count of TASK_BACKGROUND_APPLICATION tasks in the coalition */
116 };
117
118 #define coalition_lock(c) do{ lck_mtx_lock(&c->lock); }while(0)
119 #define coalition_unlock(c) do{ lck_mtx_unlock(&c->lock); }while(0)
120
121 static void
122 coalition_notify_user(uint64_t id, uint32_t flags)
123 {
124 mach_port_t user_port;
125 kern_return_t kr;
126
127 kr = host_get_coalition_port(host_priv_self(), &user_port);
128 if ((kr != KERN_SUCCESS) || !IPC_PORT_VALID(user_port)) {
129 return;
130 }
131
132 coalition_notification(user_port, id, flags);
133 }
134
135 /*
136 * coalition_find_by_id_internal
137 * Returns: Coalition object with specified id, NOT referenced.
138 * If not found, returns COALITION_NULL.
139 * Condition: coalitions_list_lock must be LOCKED.
140 */
141 static coalition_t
142 coalition_find_by_id_internal(uint64_t coal_id)
143 {
144 if (coal_id == 0) {
145 return COALITION_NULL;
146 }
147
148 lck_mtx_assert(&coalitions_list_lock, LCK_MTX_ASSERT_OWNED);
149 coalition_t coal;
150 queue_iterate(&coalitions, coal, coalition_t, coalitions) {
151 if (coal->id == coal_id) {
152 return coal;
153 }
154 }
155 return COALITION_NULL;
156 }
157
158 kern_return_t
159 coalition_resource_usage_internal(coalition_t coal, struct coalition_resource_usage *cru_out)
160 {
161 kern_return_t kr;
162 ledger_amount_t credit, debit;
163
164 ledger_t sum_ledger = ledger_instantiate(task_ledger_template, LEDGER_CREATE_ACTIVE_ENTRIES);
165 if (sum_ledger == LEDGER_NULL) {
166 return KERN_RESOURCE_SHORTAGE;
167 }
168
169 coalition_lock(coal);
170
171 /*
172 * Start with the coalition's ledger, which holds the totals from all
173 * the dead tasks.
174 */
175 ledger_rollup(sum_ledger, coal->ledger);
176 uint64_t bytesread = coal->bytesread;
177 uint64_t byteswritten = coal->byteswritten;
178 uint64_t gpu_time = coal->gpu_time;
179
180 /*
181 * Add to that all the active tasks' ledgers. Tasks cannot deallocate
182 * out from under us, since we hold the coalition lock.
183 */
184 task_t task;
185 queue_iterate(&coal->tasks, task, task_t, coalition_tasks) {
186 ledger_rollup(sum_ledger, task->ledger);
187 bytesread += task->task_io_stats->disk_reads.size;
188 byteswritten += task->task_io_stats->total_io.size - task->task_io_stats->disk_reads.size;
189 gpu_time += task_gpu_utilisation(task);
190 }
191
192 /* collect information from the coalition itself */
193 cru_out->tasks_started = coal->task_count;
194 cru_out->tasks_exited = coal->dead_task_count;
195
196 uint64_t time_nonempty = coal->time_nonempty;
197 uint64_t last_became_nonempty_time = coal->last_became_nonempty_time;
198
199 coalition_unlock(coal);
200
201 /* Copy the totals out of sum_ledger */
202 kr = ledger_get_entries(sum_ledger, task_ledgers.cpu_time,
203 &credit, &debit);
204 if (kr != KERN_SUCCESS) {
205 credit = 0;
206 }
207 cru_out->cpu_time = credit;
208
209 kr = ledger_get_entries(sum_ledger, task_ledgers.interrupt_wakeups,
210 &credit, &debit);
211 if (kr != KERN_SUCCESS) {
212 credit = 0;
213 }
214 cru_out->interrupt_wakeups = credit;
215
216 kr = ledger_get_entries(sum_ledger, task_ledgers.platform_idle_wakeups,
217 &credit, &debit);
218 if (kr != KERN_SUCCESS) {
219 credit = 0;
220 }
221 cru_out->platform_idle_wakeups = credit;
222
223 cru_out->bytesread = bytesread;
224 cru_out->byteswritten = byteswritten;
225 cru_out->gpu_time = gpu_time;
226
227 ledger_dereference(sum_ledger);
228 sum_ledger = LEDGER_NULL;
229
230 if (last_became_nonempty_time) {
231 time_nonempty += mach_absolute_time() - last_became_nonempty_time;
232 }
233 absolutetime_to_nanoseconds(time_nonempty, &cru_out->time_nonempty);
234
235 return KERN_SUCCESS;
236 }
237
238 /*
239 * coalition_create_internal
240 * Returns: New coalition object, referenced for the caller and unlocked.
241 * Condition: coalitions_list_lock must be UNLOCKED.
242 */
243 kern_return_t
244 coalition_create_internal(coalition_t *out, boolean_t privileged)
245 {
246 struct coalition *new_coal = (struct coalition *)zalloc(coalition_zone);
247 if (new_coal == COALITION_NULL) {
248 return KERN_RESOURCE_SHORTAGE;
249 }
250 bzero(new_coal, sizeof(*new_coal));
251
252 new_coal->ledger = ledger_instantiate(task_ledger_template, LEDGER_CREATE_ACTIVE_ENTRIES);
253 if (new_coal->ledger == NULL) {
254 zfree(coalition_zone, new_coal);
255 return KERN_RESOURCE_SHORTAGE;
256 }
257
258 /* One for caller, one for coalitions list */
259 new_coal->ref_count = 2;
260
261 new_coal->privileged = privileged ? TRUE : FALSE;
262
263 lck_mtx_init(&new_coal->lock, &coalitions_lck_grp, &coalitions_lck_attr);
264 queue_init(&new_coal->tasks);
265
266 lck_mtx_lock(&coalitions_list_lock);
267 new_coal->id = coalition_next_id++;
268 coalition_count++;
269 queue_enter(&coalitions, new_coal, coalition_t, coalitions);
270 lck_mtx_unlock(&coalitions_list_lock);
271
272 #if COALITION_DEBUG
273 printf("%s: new coal id %llu\n", __func__, new_coal->id);
274 #endif
275
276 *out = new_coal;
277 return KERN_SUCCESS;
278 }
279
280 /*
281 * coalition_release
282 * Condition: coalition must be UNLOCKED.
283 * */
284 void
285 coalition_release(coalition_t coal)
286 {
287 boolean_t do_dealloc = FALSE;
288
289 /* TODO: This can be done with atomics. */
290 coalition_lock(coal);
291 coal->ref_count--;
292 if (coal->ref_count == 0) {
293 do_dealloc = TRUE;
294 }
295 #if COALITION_DEBUG
296 uint32_t rc = coal->ref_count;
297 #endif /* COALITION_DEBUG */
298
299 coalition_unlock(coal);
300
301 #if COALITION_DEBUG
302 printf("%s: coal %llu ref_count-- -> %u%s\n", __func__, coal->id, rc,
303 do_dealloc ? ", will deallocate now" : "");
304 #endif /* COALITION_DEBUG */
305
306 if (do_dealloc) {
307 assert(coal->termrequested);
308 assert(coal->terminated);
309 assert(coal->active_count == 0);
310 assert(coal->reaped);
311 assert(coal->focal_tasks_count == 0);
312 assert(coal->non_focal_tasks_count == 0);
313
314 ledger_dereference(coal->ledger);
315 lck_mtx_destroy(&coal->lock, &coalitions_lck_grp);
316 zfree(coalition_zone, coal);
317 }
318 }
319
320 /*
321 * coalition_find_by_id
322 * Returns: Coalition object with specified id, referenced.
323 * Condition: coalitions_list_lock must be UNLOCKED.
324 */
325 coalition_t
326 coalition_find_by_id(uint64_t cid)
327 {
328 if (cid == 0) {
329 return COALITION_NULL;
330 }
331
332 lck_mtx_lock(&coalitions_list_lock);
333
334 coalition_t coal = coalition_find_by_id_internal(cid);
335 if (coal == COALITION_NULL) {
336 lck_mtx_unlock(&coalitions_list_lock);
337 return COALITION_NULL;
338 }
339
340 coalition_lock(coal);
341
342 if (coal->reaped) {
343 coalition_unlock(coal);
344 lck_mtx_unlock(&coalitions_list_lock);
345 return COALITION_NULL;
346 }
347
348 if (coal->ref_count == 0) {
349 panic("resurrecting coalition %p id %llu, active_count = %u\n",
350 coal, coal->id, coal->active_count);
351 }
352 coal->ref_count++;
353 #if COALITION_DEBUG
354 uint32_t rc = coal->ref_count;
355 #endif
356
357 coalition_unlock(coal);
358 lck_mtx_unlock(&coalitions_list_lock);
359
360 #if COALITION_DEBUG
361 printf("%s: coal %llu ref_count++ -> %u\n", __func__, coal->id, rc);
362 #endif
363 return coal;
364 }
365
366 /*
367 * coalition_find_and_activate_by_id
368 * Returns: Coalition object with specified id, referenced, and activated.
369 * Condition: coalitions_list_lock must be UNLOCKED.
370 * This is the function to use when putting a 'new' thing into a coalition,
371 * like posix_spawn of an XPC service by launchd.
372 * See also coalition_extend_active.
373 */
374 coalition_t
375 coalition_find_and_activate_by_id(uint64_t cid)
376 {
377 if (cid == 0) {
378 return COALITION_NULL;
379 }
380
381 lck_mtx_lock(&coalitions_list_lock);
382
383 coalition_t coal = coalition_find_by_id_internal(cid);
384 if (coal == COALITION_NULL) {
385 lck_mtx_unlock(&coalitions_list_lock);
386 return COALITION_NULL;
387 }
388
389 coalition_lock(coal);
390
391 if (coal->reaped || coal->terminated) {
392 /* Too late to put something new into this coalition, it's
393 * already on its way out the door */
394 coalition_unlock(coal);
395 lck_mtx_unlock(&coalitions_list_lock);
396 return COALITION_NULL;
397 }
398
399 if (coal->ref_count == 0) {
400 panic("resurrecting coalition %p id %llu, active_count = %u\n",
401 coal, coal->id, coal->active_count);
402 }
403
404 coal->ref_count++;
405 coal->active_count++;
406
407 #if COALITION_DEBUG
408 uint32_t rc = coal->ref_count;
409 uint32_t ac = coal->active_count;
410 #endif
411
412 coalition_unlock(coal);
413 lck_mtx_unlock(&coalitions_list_lock);
414
415 #if COALITION_DEBUG
416 printf("%s: coal %llu ref_count++ -> %u, active_count++ -> %u\n",
417 __func__, coal->id, rc, ac);
418 #endif
419 return coal;
420 }
421
422 uint64_t
423 coalition_id(coalition_t coal)
424 {
425 return coal->id;
426 }
427
428 uint64_t
429 task_coalition_id(task_t task)
430 {
431 return task->coalition->id;
432 }
433
434 boolean_t
435 coalition_is_privileged(coalition_t coal)
436 {
437 return coal->privileged || unrestrict_coalition_syscalls;
438 }
439
440 boolean_t
441 task_is_in_privileged_coalition(task_t task)
442 {
443 return task->coalition->privileged || unrestrict_coalition_syscalls;
444 }
445
446 /*
447 * coalition_get_ledger
448 * Returns: Coalition's ledger, NOT referenced.
449 * Condition: Caller must have a coalition reference.
450 */
451 ledger_t
452 coalition_get_ledger(coalition_t coal)
453 {
454 return coal->ledger;
455 }
456
457 /*
458 * This is the function to use when you already hold an activation on the
459 * coalition, and want to extend it to a second activation owned by a new
460 * object, like when a task in the coalition calls fork(). This is analogous
461 * to taking a second reference when you already hold one.
462 * See also coalition_find_and_activate_by_id.
463 */
464 kern_return_t
465 coalition_extend_active(coalition_t coal)
466 {
467 coalition_lock(coal);
468
469 if (coal->reaped) {
470 panic("cannot make a reaped coalition active again");
471 }
472
473 if (coal->terminated) {
474 coalition_unlock(coal);
475 return KERN_TERMINATED;
476 }
477
478 assert(coal->active_count > 0);
479 coal->active_count++;
480
481 coalition_unlock(coal);
482 return KERN_SUCCESS;
483 }
484
485 void
486 coalition_remove_active(coalition_t coal)
487 {
488 coalition_lock(coal);
489
490 assert(!coal->reaped);
491 assert(coal->active_count > 0);
492
493 coal->active_count--;
494
495 boolean_t do_notify = FALSE;
496 uint64_t notify_id = 0;
497 uint32_t notify_flags = 0;
498 if (coal->termrequested && coal->active_count == 0) {
499 /* We only notify once, when active_count reaches zero.
500 * We just decremented, so if it reached zero, we mustn't have
501 * notified already.
502 */
503 assert(!coal->terminated);
504 coal->terminated = TRUE;
505
506 assert(!coal->notified);
507
508 coal->notified = TRUE;
509 do_notify = TRUE;
510 notify_id = coal->id;
511 notify_flags = 0;
512 }
513
514 coalition_unlock(coal);
515
516 if (do_notify) {
517 coalition_notify_user(notify_id, notify_flags);
518 }
519 }
520
521 /* Used for kernel_task, launchd, launchd's early boot tasks... */
522 kern_return_t
523 coalition_default_adopt_task(task_t task)
524 {
525 kern_return_t kr;
526 kr = coalition_adopt_task(default_coalition, task);
527 if (kr != KERN_SUCCESS) {
528 panic("failed to adopt task %p into default coalition: %d", task, kr);
529 }
530 return kr;
531 }
532
533 /*
534 * coalition_adopt_task
535 * Condition: Coalition must be referenced and unlocked. Will fail if coalition
536 * is already terminated.
537 */
538 kern_return_t
539 coalition_adopt_task(coalition_t coal, task_t task)
540 {
541 if (task->coalition) {
542 return KERN_ALREADY_IN_SET;
543 }
544
545 coalition_lock(coal);
546
547 if (coal->reaped || coal->terminated) {
548 coalition_unlock(coal);
549 return KERN_TERMINATED;
550 }
551
552 coal->active_count++;
553
554 coal->ref_count++;
555 task->coalition = coal;
556
557 queue_enter(&coal->tasks, task, task_t, coalition_tasks);
558 coal->task_count++;
559
560 if(coal->task_count < coal->dead_task_count) {
561 panic("%s: coalition %p id %llu task_count < dead_task_count", __func__, coal, coal->id);
562 }
563
564 /* If moving from 0->1 active tasks */
565 if (coal->task_count - coal->dead_task_count == 1) {
566 coal->last_became_nonempty_time = mach_absolute_time();
567 }
568
569 #if COALITION_DEBUG
570 uint32_t rc = coal->ref_count;
571 #endif
572
573 coalition_unlock(coal);
574
575 #if COALITION_DEBUG
576 if (rc) {
577 printf("%s: coal %llu ref_count++ -> %u\n", __func__, coal->id, rc);
578 }
579 #endif
580 return KERN_SUCCESS;
581 }
582
583 /*
584 * coalition_remove_task
585 * Condition: task must be referenced and UNLOCKED; task's coalition must be UNLOCKED
586 */
587 kern_return_t
588 coalition_remove_task(task_t task)
589 {
590 coalition_t coal = task->coalition;
591 assert(coal);
592
593 coalition_lock(coal);
594
595 queue_remove(&coal->tasks, task, task_t, coalition_tasks);
596 coal->dead_task_count++;
597
598 if(coal->task_count < coal->dead_task_count) {
599 panic("%s: coalition %p id %llu task_count < dead_task_count", __func__, coal, coal->id);
600 }
601
602 /* If moving from 1->0 active tasks */
603 if (coal->task_count - coal->dead_task_count == 0) {
604 uint64_t last_time_nonempty = mach_absolute_time() - coal->last_became_nonempty_time;
605 coal->last_became_nonempty_time = 0;
606 coal->time_nonempty += last_time_nonempty;
607 }
608
609 ledger_rollup(coal->ledger, task->ledger);
610 coal->bytesread += task->task_io_stats->disk_reads.size;
611 coal->byteswritten += task->task_io_stats->total_io.size - task->task_io_stats->disk_reads.size;
612 coal->gpu_time += task_gpu_utilisation(task);
613
614 coalition_unlock(coal);
615
616 coalition_remove_active(coal);
617 return KERN_SUCCESS;
618 }
619
620 /*
621 * coalition_terminate_internal
622 * Condition: Coalition must be referenced and UNLOCKED.
623 */
624 kern_return_t
625 coalition_request_terminate_internal(coalition_t coal)
626 {
627 if (coal == default_coalition) {
628 return KERN_DEFAULT_SET;
629 }
630
631 coalition_lock(coal);
632
633 if (coal->reaped) {
634 coalition_unlock(coal);
635 return KERN_INVALID_NAME;
636 }
637
638 if (coal->terminated || coal->termrequested) {
639 coalition_unlock(coal);
640 return KERN_TERMINATED;
641 }
642
643 coal->termrequested = TRUE;
644
645 boolean_t do_notify = FALSE;
646 uint64_t note_id = 0;
647 uint32_t note_flags = 0;
648
649 if (coal->active_count == 0) {
650 /*
651 * We only notify once, when active_count reaches zero.
652 * We just decremented, so if it reached zero, we mustn't have
653 * notified already.
654 */
655 assert(!coal->terminated);
656 coal->terminated = TRUE;
657
658 assert(!coal->notified);
659
660 coal->notified = TRUE;
661 do_notify = TRUE;
662 note_id = coal->id;
663 note_flags = 0;
664 }
665
666 coalition_unlock(coal);
667
668 if (do_notify) {
669 coalition_notify_user(note_id, note_flags);
670 }
671
672 return KERN_SUCCESS;
673 }
674
675 /*
676 * coalition_reap_internal
677 * Condition: Coalition must be referenced and UNLOCKED.
678 */
679 kern_return_t
680 coalition_reap_internal(coalition_t coal)
681 {
682 if (coal == default_coalition) {
683 return KERN_DEFAULT_SET;
684 }
685
686 coalition_lock(coal);
687 if (coal->reaped) {
688 coalition_unlock(coal);
689 return KERN_TERMINATED;
690 }
691 if (!coal->terminated) {
692 coalition_unlock(coal);
693 return KERN_FAILURE;
694 }
695 assert(coal->termrequested);
696 if (coal->active_count > 0) {
697 coalition_unlock(coal);
698 return KERN_FAILURE;
699 }
700
701 coal->reaped = TRUE;
702
703 /* Caller, launchd, and coalitions list should each have a reference */
704 assert(coal->ref_count > 2);
705
706 coalition_unlock(coal);
707
708 lck_mtx_lock(&coalitions_list_lock);
709 coalition_count--;
710 queue_remove(&coalitions, coal, coalition_t, coalitions);
711 lck_mtx_unlock(&coalitions_list_lock);
712
713 /* Release the list's reference and launchd's reference. */
714 coalition_release(coal);
715 coalition_release(coal);
716
717 return KERN_SUCCESS;
718 }
719
720 void
721 coalition_init(void)
722 {
723 coalition_zone = zinit(
724 sizeof(struct coalition),
725 CONFIG_COALITION_MAX * sizeof(struct coalition),
726 COALITION_CHUNK * sizeof(struct coalition),
727 "coalitions");
728 zone_change(coalition_zone, Z_NOENCRYPT, TRUE);
729 queue_init(&coalitions);
730
731 if (!PE_parse_boot_argn("unrestrict_coalition_syscalls", &unrestrict_coalition_syscalls,
732 sizeof (unrestrict_coalition_syscalls))) {
733 unrestrict_coalition_syscalls = 0;
734 }
735
736 lck_grp_attr_setdefault(&coalitions_lck_grp_attr);
737 lck_grp_init(&coalitions_lck_grp, "coalition", &coalitions_lck_grp_attr);
738 lck_attr_setdefault(&coalitions_lck_attr);
739 lck_mtx_init(&coalitions_list_lock, &coalitions_lck_grp, &coalitions_lck_attr);
740
741 init_task_ledgers();
742
743 kern_return_t kr = coalition_create_internal(&default_coalition, TRUE);
744 if (kr != KERN_SUCCESS) {
745 panic("%s: could not create default coalition: %d", __func__, kr);
746 }
747 /* "Leak" our reference to the global object */
748 }
749
750 /* coalition focal tasks */
751 uint32_t coalition_adjust_focal_task_count(coalition_t coal, int count)
752 {
753 return hw_atomic_add(&coal->focal_tasks_count, count);
754 }
755
756 uint32_t coalition_focal_task_count(coalition_t coal)
757 {
758 return coal->focal_tasks_count;
759 }
760
761 uint32_t coalition_adjust_non_focal_task_count(coalition_t coal, int count)
762 {
763 return hw_atomic_add(&coal->non_focal_tasks_count, count);
764 }
765
766 uint32_t coalition_non_focal_task_count(coalition_t coal)
767 {
768 return coal->non_focal_tasks_count;
769 }
770
771 /* Call sfi_reevaluate() for every thread in the coalition */
772 void coalition_sfi_reevaluate(coalition_t coal, task_t updated_task) {
773 task_t task;
774 thread_t thread;
775
776 coalition_lock(coal);
777
778 queue_iterate(&coal->tasks, task, task_t, coalition_tasks) {
779
780 /* Skip the task we're doing this on behalf of - it's already updated */
781 if (task == updated_task)
782 continue;
783
784 task_lock(task);
785
786 queue_iterate(&task->threads, thread, thread_t, task_threads) {
787 sfi_reevaluate(thread);
788 }
789 task_unlock(task);
790 }
791 coalition_unlock(coal);
792 }
793