]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ipc/ipc_importance.c
749a9c2785b75e6eb19c7bb3415d33efb4b0fc48
[apple/xnu.git] / osfmk / ipc / ipc_importance.c
1 /*
2 * Copyright (c) 2013 Apple 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 <mach/mach_types.h>
30 #include <mach/notify.h>
31 #include <ipc/ipc_types.h>
32 #include <ipc/ipc_importance.h>
33 #include <ipc/ipc_port.h>
34 #include <ipc/ipc_voucher.h>
35 #include <kern/ipc_kobject.h>
36 #include <kern/ipc_tt.h>
37 #include <kern/mach_param.h>
38 #include <kern/misc_protos.h>
39 #include <kern/kalloc.h>
40 #include <kern/zalloc.h>
41 #include <kern/queue.h>
42 #include <kern/task.h>
43 #include <kern/policy_internal.h>
44
45 #include <sys/kdebug.h>
46
47 #include <mach/mach_voucher_attr_control.h>
48 #include <mach/machine/sdt.h>
49
50 extern int proc_pid(void *);
51 extern int proc_selfpid(void);
52 extern uint64_t proc_uniqueid(void *p);
53 extern char *proc_name_address(void *p);
54
55 /*
56 * Globals for delayed boost drop processing.
57 */
58 static queue_head_t ipc_importance_delayed_drop_queue;
59 static thread_call_t ipc_importance_delayed_drop_call;
60 static uint64_t ipc_importance_delayed_drop_timestamp;
61 static boolean_t ipc_importance_delayed_drop_call_requested = FALSE;
62
63 #define DENAP_DROP_TARGET (1000 * NSEC_PER_MSEC) /* optimum denap delay */
64 #define DENAP_DROP_SKEW (100 * NSEC_PER_MSEC) /* request skew for wakeup */
65 #define DENAP_DROP_LEEWAY (2 * DENAP_DROP_SKEW) /* specified wakeup leeway */
66
67 #define DENAP_DROP_DELAY (DENAP_DROP_TARGET + DENAP_DROP_SKEW)
68 #define DENAP_DROP_FLAGS (THREAD_CALL_DELAY_SYS_NORMAL | THREAD_CALL_DELAY_LEEWAY)
69
70 /*
71 * Importance Voucher Attribute Manager
72 */
73
74 static lck_spin_t ipc_importance_lock_data; /* single lock for now */
75
76
77 #define ipc_importance_lock_init() \
78 lck_spin_init(&ipc_importance_lock_data, &ipc_lck_grp, &ipc_lck_attr)
79 #define ipc_importance_lock_destroy() \
80 lck_spin_destroy(&ipc_importance_lock_data, &ipc_lck_grp)
81 #define ipc_importance_lock() \
82 lck_spin_lock(&ipc_importance_lock_data)
83 #define ipc_importance_lock_try() \
84 lck_spin_try_lock(&ipc_importance_lock_data)
85 #define ipc_importance_unlock() \
86 lck_spin_unlock(&ipc_importance_lock_data)
87 #define ipc_importance_sleep(elem) lck_spin_sleep(&ipc_importance_lock_data, \
88 LCK_SLEEP_DEFAULT, \
89 (event_t)(elem), \
90 THREAD_UNINT)
91 #define ipc_importance_wakeup(elem) thread_wakeup((event_t)(elem))
92
93 #if IIE_REF_DEBUG
94 #define incr_ref_counter(x) (hw_atomic_add(&(x), 1))
95
96 static inline
97 uint32_t ipc_importance_reference_internal(ipc_importance_elem_t elem)
98 {
99 incr_ref_counter(elem->iie_refs_added);
100 return (hw_atomic_add(&elem->iie_bits, 1) & IIE_REFS_MASK);
101 }
102
103 static inline
104 uint32_t ipc_importance_release_internal(ipc_importance_elem_t elem)
105 {
106 incr_ref_counter(elem->iie_refs_dropped);
107 return (hw_atomic_sub(&elem->iie_bits, 1) & IIE_REFS_MASK);
108 }
109
110 static inline
111 uint32_t ipc_importance_task_reference_internal(ipc_importance_task_t task_imp)
112 {
113 uint32_t out;
114 out = ipc_importance_reference_internal(&task_imp->iit_elem);
115 incr_ref_counter(task_imp->iit_elem.iie_task_refs_added);
116 return out;
117 }
118
119 static inline
120 uint32_t ipc_importance_task_release_internal(ipc_importance_task_t task_imp)
121 {
122 uint32_t out;
123
124 assert(1 < IIT_REFS(task_imp));
125 incr_ref_counter(task_imp->iit_elem.iie_task_refs_dropped);
126 out = ipc_importance_release_internal(&task_imp->iit_elem);
127 return out;
128 }
129
130 static inline
131 void ipc_importance_counter_init(ipc_importance_elem_t elem)
132 {
133
134 elem->iie_refs_added = 0;
135 elem->iie_refs_dropped = 0;
136 elem->iie_kmsg_refs_added = 0;
137 elem->iie_kmsg_refs_inherited = 0;
138 elem->iie_kmsg_refs_coalesced = 0;
139 elem->iie_kmsg_refs_dropped = 0;
140 elem->iie_task_refs_added = 0;
141 elem->iie_task_refs_added_inherit_from = 0;
142 elem->iie_task_refs_added_transition = 0;
143 elem->iie_task_refs_self_added = 0;
144 elem->iie_task_refs_inherited = 0;
145 elem->iie_task_refs_coalesced = 0;
146 elem->iie_task_refs_dropped = 0;
147 }
148 #else
149 #define incr_ref_counter(x)
150 #endif
151
152 #if DEVELOPMENT || DEBUG
153 static queue_head_t global_iit_alloc_queue;
154 #endif
155
156 /* TODO: remove this varibale when interactive daemon audit is complete */
157 boolean_t ipc_importance_interactive_receiver = FALSE;
158
159 static zone_t ipc_importance_task_zone;
160 static zone_t ipc_importance_inherit_zone;
161
162 static ipc_voucher_attr_control_t ipc_importance_control;
163
164 static boolean_t ipc_importance_task_check_transition(ipc_importance_task_t task_imp,
165 iit_update_type_t type, uint32_t delta);
166
167 static void ipc_importance_task_propagate_assertion_locked(ipc_importance_task_t task_imp,
168 iit_update_type_t type, boolean_t update_task_imp);
169
170 static ipc_importance_inherit_t ipc_importance_inherit_from_task(task_t from_task, task_t to_task);
171
172 /*
173 * Routine: ipc_importance_kmsg_link
174 * Purpose:
175 * Link the kmsg onto the appropriate propagation chain.
176 * If the element is a task importance, we link directly
177 * on its propagation chain. Otherwise, we link onto the
178 * destination task of the inherit.
179 * Conditions:
180 * Importance lock held.
181 * Caller is donating an importance elem reference to the kmsg.
182 */
183 static void
184 ipc_importance_kmsg_link(
185 ipc_kmsg_t kmsg,
186 ipc_importance_elem_t elem)
187 {
188 ipc_importance_elem_t link_elem;
189
190 assert(IIE_NULL == kmsg->ikm_importance);
191
192 link_elem = (IIE_TYPE_INHERIT == IIE_TYPE(elem)) ?
193 (ipc_importance_elem_t)((ipc_importance_inherit_t)elem)->iii_to_task :
194 elem;
195
196 queue_enter(&link_elem->iie_kmsgs, kmsg, ipc_kmsg_t, ikm_inheritance);
197 kmsg->ikm_importance = elem;
198 }
199
200 /*
201 * Routine: ipc_importance_kmsg_unlink
202 * Purpose:
203 * Unlink the kmsg from its current propagation chain.
204 * If the element is a task importance, we unlink directly
205 * from its propagation chain. Otherwise, we unlink from the
206 * destination task of the inherit.
207 * Returns:
208 * The reference to the importance element it was linked on.
209 * Conditions:
210 * Importance lock held.
211 * Caller is responsible for dropping reference on returned elem.
212 */
213 static ipc_importance_elem_t
214 ipc_importance_kmsg_unlink(
215 ipc_kmsg_t kmsg)
216 {
217 ipc_importance_elem_t elem = kmsg->ikm_importance;
218
219 if (IIE_NULL != elem) {
220 ipc_importance_elem_t unlink_elem;
221
222 unlink_elem = (IIE_TYPE_INHERIT == IIE_TYPE(elem)) ?
223 (ipc_importance_elem_t)((ipc_importance_inherit_t)elem)->iii_to_task :
224 elem;
225
226 queue_remove(&unlink_elem->iie_kmsgs, kmsg, ipc_kmsg_t, ikm_inheritance);
227 kmsg->ikm_importance = IIE_NULL;
228 }
229 return elem;
230 }
231
232 /*
233 * Routine: ipc_importance_inherit_link
234 * Purpose:
235 * Link the inherit onto the appropriate propagation chain.
236 * If the element is a task importance, we link directly
237 * on its propagation chain. Otherwise, we link onto the
238 * destination task of the inherit.
239 * Conditions:
240 * Importance lock held.
241 * Caller is donating an elem importance reference to the inherit.
242 */
243 static void
244 ipc_importance_inherit_link(
245 ipc_importance_inherit_t inherit,
246 ipc_importance_elem_t elem)
247 {
248 ipc_importance_task_t link_task;
249
250 assert(IIE_NULL == inherit->iii_from_elem);
251 link_task = (IIE_TYPE_INHERIT == IIE_TYPE(elem)) ?
252 ((ipc_importance_inherit_t)elem)->iii_to_task :
253 (ipc_importance_task_t)elem;
254
255 queue_enter(&link_task->iit_inherits, inherit,
256 ipc_importance_inherit_t, iii_inheritance);
257 inherit->iii_from_elem = elem;
258 }
259
260 /*
261 * Routine: ipc_importance_inherit_find
262 * Purpose:
263 * Find an existing inherit that links the from element to the
264 * to_task at a given nesting depth. As inherits from other
265 * inherits are actually linked off the original inherit's donation
266 * receiving task, we have to conduct our search from there if
267 * the from element is an inherit.
268 * Returns:
269 * A pointer (not a reference) to the matching inherit.
270 * Conditions:
271 * Importance lock held.
272 */
273 static ipc_importance_inherit_t
274 ipc_importance_inherit_find(
275 ipc_importance_elem_t from,
276 ipc_importance_task_t to_task,
277 unsigned int depth)
278 {
279 ipc_importance_task_t link_task;
280 ipc_importance_inherit_t inherit;
281
282 link_task = (IIE_TYPE_INHERIT == IIE_TYPE(from)) ?
283 ((ipc_importance_inherit_t)from)->iii_to_task :
284 (ipc_importance_task_t)from;
285
286 queue_iterate(&link_task->iit_inherits, inherit,
287 ipc_importance_inherit_t, iii_inheritance) {
288 if (inherit->iii_to_task == to_task && inherit->iii_depth == depth) {
289 return inherit;
290 }
291 }
292 return III_NULL;
293 }
294
295 /*
296 * Routine: ipc_importance_inherit_unlink
297 * Purpose:
298 * Unlink the inherit from its current propagation chain.
299 * If the element is a task importance, we unlink directly
300 * from its propagation chain. Otherwise, we unlink from the
301 * destination task of the inherit.
302 * Returns:
303 * The reference to the importance element it was linked on.
304 * Conditions:
305 * Importance lock held.
306 * Caller is responsible for dropping reference on returned elem.
307 */
308 static ipc_importance_elem_t
309 ipc_importance_inherit_unlink(
310 ipc_importance_inherit_t inherit)
311 {
312 ipc_importance_elem_t elem = inherit->iii_from_elem;
313
314 if (IIE_NULL != elem) {
315 ipc_importance_task_t unlink_task;
316
317 unlink_task = (IIE_TYPE_INHERIT == IIE_TYPE(elem)) ?
318 ((ipc_importance_inherit_t)elem)->iii_to_task :
319 (ipc_importance_task_t)elem;
320
321 queue_remove(&unlink_task->iit_inherits, inherit,
322 ipc_importance_inherit_t, iii_inheritance);
323 inherit->iii_from_elem = IIE_NULL;
324 }
325 return elem;
326 }
327
328 /*
329 * Routine: ipc_importance_reference
330 * Purpose:
331 * Add a reference to the importance element.
332 * Conditions:
333 * Caller must hold a reference on the element.
334 */
335 void
336 ipc_importance_reference(ipc_importance_elem_t elem)
337 {
338 assert(0 < IIE_REFS(elem));
339 ipc_importance_reference_internal(elem);
340 }
341
342 /*
343 * Routine: ipc_importance_release_locked
344 * Purpose:
345 * Release a reference on an importance attribute value,
346 * unlinking and deallocating the attribute if the last reference.
347 * Conditions:
348 * Entered with importance lock held, leaves with it unlocked.
349 */
350 static void
351 ipc_importance_release_locked(ipc_importance_elem_t elem)
352 {
353 assert(0 < IIE_REFS(elem));
354
355 #if DEVELOPMENT || DEBUG
356 ipc_importance_inherit_t temp_inherit;
357 ipc_importance_task_t link_task;
358 ipc_kmsg_t temp_kmsg;
359 uint32_t expected = 0;
360
361 if (0 < elem->iie_made)
362 expected++;
363
364 link_task = (IIE_TYPE_INHERIT == IIE_TYPE(elem)) ?
365 ((ipc_importance_inherit_t)elem)->iii_to_task :
366 (ipc_importance_task_t)elem;
367
368 queue_iterate(&link_task->iit_kmsgs, temp_kmsg, ipc_kmsg_t, ikm_inheritance)
369 if (temp_kmsg->ikm_importance == elem)
370 expected++;
371 queue_iterate(&link_task->iit_inherits, temp_inherit,
372 ipc_importance_inherit_t, iii_inheritance)
373 if (temp_inherit->iii_from_elem == elem)
374 expected++;
375 if (IIE_REFS(elem) < expected + 1)
376 panic("ipc_importance_release_locked (%p)", elem);
377 #endif
378
379 if (0 < ipc_importance_release_internal(elem)) {
380 ipc_importance_unlock();
381 return;
382 }
383
384 /* last ref */
385
386 switch (IIE_TYPE(elem)) {
387
388 /* just a "from" task reference to drop */
389 case IIE_TYPE_TASK:
390 {
391 ipc_importance_task_t task_elem;
392
393 task_elem = (ipc_importance_task_t)elem;
394
395 /* the task can't still hold a reference on the task importance */
396 assert(TASK_NULL == task_elem->iit_task);
397
398 #if DEVELOPMENT || DEBUG
399 queue_remove(&global_iit_alloc_queue, task_elem, ipc_importance_task_t, iit_allocation);
400 #endif
401
402 ipc_importance_unlock();
403
404 zfree(ipc_importance_task_zone, task_elem);
405 break;
406 }
407
408 /* dropping an inherit element */
409 case IIE_TYPE_INHERIT:
410 {
411 ipc_importance_inherit_t inherit = (ipc_importance_inherit_t)elem;
412 ipc_importance_task_t to_task = inherit->iii_to_task;
413 ipc_importance_elem_t from_elem;
414
415 assert(IIT_NULL != to_task);
416 assert(ipc_importance_task_is_any_receiver_type(to_task));
417
418 /* unlink the inherit from its source element */
419 from_elem = ipc_importance_inherit_unlink(inherit);
420 assert(IIE_NULL != from_elem);
421
422 /*
423 * The attribute might have pending external boosts if the attribute
424 * was given out during exec, drop them from the appropriate destination
425 * task.
426 *
427 * The attribute will not have any pending external boosts if the
428 * attribute was given out to voucher system since it would have been
429 * dropped by ipc_importance_release_value, but there is not way to
430 * detect that, thus if the attribute has a pending external boost,
431 * drop them from the appropriate destination task.
432 *
433 * The inherit attribute from exec and voucher system would not
434 * get deduped to each other, thus dropping the external boost
435 * from destination task at two different places will not have
436 * any unintended side effects.
437 */
438 assert(inherit->iii_externcnt >= inherit->iii_externdrop);
439 if (inherit->iii_donating) {
440 uint32_t assertcnt = III_EXTERN(inherit);
441
442 assert(ipc_importance_task_is_any_receiver_type(to_task));
443 assert(to_task->iit_externcnt >= inherit->iii_externcnt);
444 assert(to_task->iit_externdrop >= inherit->iii_externdrop);
445 to_task->iit_externcnt -= inherit->iii_externcnt;
446 to_task->iit_externdrop -= inherit->iii_externdrop;
447 inherit->iii_externcnt = 0;
448 inherit->iii_externdrop = 0;
449 inherit->iii_donating = FALSE;
450
451 /* adjust the internal assertions - and propagate as needed */
452 if (ipc_importance_task_check_transition(to_task, IIT_UPDATE_DROP, assertcnt)) {
453 ipc_importance_task_propagate_assertion_locked(to_task, IIT_UPDATE_DROP, TRUE);
454 }
455 } else {
456 inherit->iii_externcnt = 0;
457 inherit->iii_externdrop = 0;
458 }
459
460 /* release the reference on the source element */
461 ipc_importance_release_locked(from_elem);
462 /* unlocked on return */
463
464 /* release the reference on the destination task */
465 ipc_importance_task_release(to_task);
466
467 /* free the inherit */
468 zfree(ipc_importance_inherit_zone, inherit);
469 break;
470 }
471 }
472 }
473
474 /*
475 * Routine: ipc_importance_release
476 * Purpose:
477 * Release a reference on an importance attribute value,
478 * unlinking and deallocating the attribute if the last reference.
479 * Conditions:
480 * nothing locked on entrance, nothing locked on exit.
481 * May block.
482 */
483 void
484 ipc_importance_release(ipc_importance_elem_t elem)
485 {
486 if (IIE_NULL == elem)
487 return;
488
489 ipc_importance_lock();
490 ipc_importance_release_locked(elem);
491 /* unlocked */
492 }
493
494 /*
495 * Routine: ipc_importance_task_reference
496
497
498 * Purpose:
499 * Retain a reference on a task importance attribute value.
500 * Conditions:
501 * nothing locked on entrance, nothing locked on exit.
502 * caller holds a reference already.
503 */
504 void
505 ipc_importance_task_reference(ipc_importance_task_t task_elem)
506 {
507 if (IIT_NULL == task_elem)
508 return;
509 #if IIE_REF_DEBUG
510 incr_ref_counter(task_elem->iit_elem.iie_task_refs_added);
511 #endif
512 ipc_importance_reference(&task_elem->iit_elem);
513 }
514
515 /*
516 * Routine: ipc_importance_task_release
517 * Purpose:
518 * Release a reference on a task importance attribute value,
519 * unlinking and deallocating the attribute if the last reference.
520 * Conditions:
521 * nothing locked on entrance, nothing locked on exit.
522 * May block.
523 */
524 void
525 ipc_importance_task_release(ipc_importance_task_t task_elem)
526 {
527 if (IIT_NULL == task_elem)
528 return;
529
530 ipc_importance_lock();
531 #if IIE_REF_DEBUG
532 incr_ref_counter(task_elem->iit_elem.iie_task_refs_dropped);
533 #endif
534 ipc_importance_release_locked(&task_elem->iit_elem);
535 /* unlocked */
536 }
537
538 /*
539 * Routine: ipc_importance_task_release_locked
540 * Purpose:
541 * Release a reference on a task importance attribute value,
542 * unlinking and deallocating the attribute if the last reference.
543 * Conditions:
544 * importance lock held on entry, nothing locked on exit.
545 * May block.
546 */
547 static void
548 ipc_importance_task_release_locked(ipc_importance_task_t task_elem)
549 {
550 if (IIT_NULL == task_elem) {
551 ipc_importance_unlock();
552 return;
553 }
554 #if IIE_REF_DEBUG
555 incr_ref_counter(task_elem->iit_elem.iie_task_refs_dropped);
556 #endif
557 ipc_importance_release_locked(&task_elem->iit_elem);
558 /* unlocked */
559 }
560
561 /*
562 * Routines for importance donation/inheritance/boosting
563 */
564
565
566 /*
567 * External importance assertions are managed by the process in userspace
568 * Internal importance assertions are the responsibility of the kernel
569 * Assertions are changed from internal to external via task_importance_externalize_assertion
570 */
571
572 /*
573 * Routine: ipc_importance_task_check_transition
574 * Purpose:
575 * Increase or decrement the internal task importance counter of the
576 * specified task and determine if propagation and a task policy
577 * update is required.
578 *
579 * If it is already enqueued for a policy update, steal it from that queue
580 * (as we are reversing that update before it happens).
581 *
582 * Conditions:
583 * Called with the importance lock held.
584 * It is the caller's responsibility to perform the propagation of the
585 * transition and/or policy changes by checking the return value.
586 */
587 static boolean_t
588 ipc_importance_task_check_transition(
589 ipc_importance_task_t task_imp,
590 iit_update_type_t type,
591 uint32_t delta)
592 {
593
594 task_t target_task = task_imp->iit_task;
595 boolean_t boost = (IIT_UPDATE_HOLD == type);
596 boolean_t before_boosted, after_boosted;
597
598 if (!ipc_importance_task_is_any_receiver_type(task_imp))
599 return FALSE;
600
601 #if IMPORTANCE_DEBUG
602 int target_pid = task_pid(target_task);
603
604 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, (((boost) ? IMP_HOLD : IMP_DROP) | TASK_POLICY_INTERNAL))) | DBG_FUNC_START,
605 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_EXTERN(task_imp), 0);
606 #endif
607
608 /* snapshot the effective boosting status before making any changes */
609 before_boosted = (task_imp->iit_assertcnt > 0);
610
611 /* Adjust the assertcnt appropriately */
612 if (boost) {
613 task_imp->iit_assertcnt += delta;
614 #if IMPORTANCE_DEBUG
615 DTRACE_BOOST6(send_boost, task_t, target_task, int, target_pid,
616 task_t, current_task(), int, proc_selfpid(), int, delta, int, task_imp->iit_assertcnt);
617 #endif
618 } else {
619 // assert(delta <= task_imp->iit_assertcnt);
620 if (task_imp->iit_assertcnt < delta + IIT_EXTERN(task_imp)) {
621 /* TODO: Turn this back into a panic <rdar://problem/12592649> */
622 if (target_task != TASK_NULL) {
623 printf("Over-release of kernel-internal importance assertions for pid %d (%s), "
624 "dropping %d assertion(s) but task only has %d remaining (%d external).\n",
625 task_pid(target_task),
626 (target_task->bsd_info == NULL) ? "" : proc_name_address(target_task->bsd_info),
627 delta,
628 task_imp->iit_assertcnt,
629 IIT_EXTERN(task_imp));
630 }
631 task_imp->iit_assertcnt = IIT_EXTERN(task_imp);
632 } else {
633 task_imp->iit_assertcnt -= delta;
634 }
635 #if IMPORTANCE_DEBUG
636 // This convers both legacy and voucher-based importance.
637 DTRACE_BOOST4(drop_boost, task_t, target_task, int, target_pid, int, delta, int, task_imp->iit_assertcnt);
638 #endif
639 }
640
641 #if IMPORTANCE_DEBUG
642 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, (((boost) ? IMP_HOLD : IMP_DROP) | TASK_POLICY_INTERNAL))) | DBG_FUNC_END,
643 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_EXTERN(task_imp), 0);
644 #endif
645
646 /* did the change result in an effective donor status change? */
647 after_boosted = (task_imp->iit_assertcnt > 0);
648
649 if (after_boosted != before_boosted) {
650
651 /*
652 * If the task importance is already on an update queue, we just reversed the need for a
653 * pending policy update. If the queue is any other than the delayed-drop-queue, pull it
654 * off that queue and release the reference it got going onto the update queue. If it is
655 * the delayed-drop-queue we leave it in place in case it comes back into the drop state
656 * before its time delay is up.
657 *
658 * We still need to propagate the change downstream to reverse the assertcnt effects,
659 * but we no longer need to update this task's boost policy state.
660 *
661 * Otherwise, mark it as needing a policy update.
662 */
663 assert(0 == task_imp->iit_updatepolicy);
664 if (NULL != task_imp->iit_updateq) {
665 if (&ipc_importance_delayed_drop_queue != task_imp->iit_updateq) {
666 queue_remove(task_imp->iit_updateq, task_imp, ipc_importance_task_t, iit_updates);
667 task_imp->iit_updateq = NULL;
668 ipc_importance_task_release_internal(task_imp); /* can't be last ref */
669 }
670 } else {
671 task_imp->iit_updatepolicy = 1;
672 }
673 return TRUE;
674 }
675
676 return FALSE;
677 }
678
679
680 /*
681 * Routine: ipc_importance_task_propagate_helper
682 * Purpose:
683 * Increase or decrement the internal task importance counter of all
684 * importance tasks inheriting from the specified one. If this causes
685 * that importance task to change state, add it to the list of tasks
686 * to do a policy update against.
687 * Conditions:
688 * Called with the importance lock held.
689 * It is the caller's responsibility to iterate down the generated list
690 * and propagate any subsequent assertion changes from there.
691 */
692 static void
693 ipc_importance_task_propagate_helper(
694 ipc_importance_task_t task_imp,
695 iit_update_type_t type,
696 queue_t propagation)
697 {
698 ipc_importance_task_t temp_task_imp;
699
700 /*
701 * iterate the downstream kmsgs, adjust their boosts,
702 * and capture the next task to adjust for each message
703 */
704
705 ipc_kmsg_t temp_kmsg;
706
707 queue_iterate(&task_imp->iit_kmsgs, temp_kmsg, ipc_kmsg_t, ikm_inheritance) {
708 mach_msg_header_t *hdr = temp_kmsg->ikm_header;
709 mach_port_delta_t delta;
710 ipc_port_t port;
711
712 /* toggle the kmsg importance bit as a barrier to parallel adjusts */
713 if (IIT_UPDATE_HOLD == type) {
714 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(hdr->msgh_bits)) {
715 continue;
716 }
717
718 /* mark the message as now carrying importance */
719 hdr->msgh_bits |= MACH_MSGH_BITS_RAISEIMP;
720 delta = 1;
721 } else {
722 if (!MACH_MSGH_BITS_RAISED_IMPORTANCE(hdr->msgh_bits)) {
723 continue;
724 }
725
726 /* clear the message as now carrying importance */
727 hdr->msgh_bits &= ~MACH_MSGH_BITS_RAISEIMP;
728 delta = -1;
729 }
730
731 /* determine the task importance to adjust as result (if any) */
732 port = (ipc_port_t) hdr->msgh_remote_port;
733 assert(IP_VALID(port));
734 ip_lock(port);
735 temp_task_imp = IIT_NULL;
736 if (!ipc_port_importance_delta_internal(port, IPID_OPTION_NORMAL, &delta, &temp_task_imp)) {
737 ip_unlock(port);
738 }
739
740 /* no task importance to adjust associated with the port? */
741 if (IIT_NULL == temp_task_imp) {
742 continue;
743 }
744
745 /* hold a reference on temp_task_imp */
746
747 /* Adjust the task assertions and determine if an edge was crossed */
748 if (ipc_importance_task_check_transition(temp_task_imp, type, 1)) {
749 incr_ref_counter(task_imp->iit_elem.iie_task_refs_added_transition);
750 queue_enter(propagation, temp_task_imp, ipc_importance_task_t, iit_props);
751 /* reference donated */
752 } else {
753 ipc_importance_task_release_internal(temp_task_imp);
754 }
755 }
756
757 /*
758 * iterate the downstream importance inherits
759 * and capture the next task importance to boost for each
760 */
761 ipc_importance_inherit_t temp_inherit;
762
763 queue_iterate(&task_imp->iit_inherits, temp_inherit, ipc_importance_inherit_t, iii_inheritance) {
764 uint32_t assertcnt = III_EXTERN(temp_inherit);
765
766 temp_task_imp = temp_inherit->iii_to_task;
767 assert(IIT_NULL != temp_task_imp);
768
769 if (IIT_UPDATE_HOLD == type) {
770 /* if no undropped externcnts in the inherit, nothing to do */
771 if (0 == assertcnt) {
772 assert(temp_inherit->iii_donating == FALSE);
773 continue;
774 }
775
776 /* nothing to do if the inherit is already donating (forced donation) */
777 if (temp_inherit->iii_donating) {
778 continue;
779 }
780
781 /* mark it donating and contribute to the task externcnts */
782 temp_inherit->iii_donating = TRUE;
783 temp_task_imp->iit_externcnt += temp_inherit->iii_externcnt;
784 temp_task_imp->iit_externdrop += temp_inherit->iii_externdrop;
785
786 } else {
787 /* if no contributing assertions, move on */
788 if (0 == assertcnt) {
789 assert(temp_inherit->iii_donating == FALSE);
790 continue;
791 }
792
793 /* nothing to do if the inherit is not donating */
794 if (!temp_inherit->iii_donating) {
795 continue;
796 }
797
798 /* mark it no longer donating */
799 temp_inherit->iii_donating = FALSE;
800
801 /* remove the contribution the inherit made to the to-task */
802 assert(IIT_EXTERN(temp_task_imp) >= III_EXTERN(temp_inherit));
803 assert(temp_task_imp->iit_externcnt >= temp_inherit->iii_externcnt);
804 assert(temp_task_imp->iit_externdrop >= temp_inherit->iii_externdrop);
805 temp_task_imp->iit_externcnt -= temp_inherit->iii_externcnt;
806 temp_task_imp->iit_externdrop -= temp_inherit->iii_externdrop;
807
808 }
809
810 /* Adjust the task assertions and determine if an edge was crossed */
811 assert(ipc_importance_task_is_any_receiver_type(temp_task_imp));
812 if (ipc_importance_task_check_transition(temp_task_imp, type, assertcnt)) {
813 ipc_importance_task_reference(temp_task_imp);
814 incr_ref_counter(task_imp->iit_elem.iie_task_refs_added_transition);
815 queue_enter(propagation, temp_task_imp, ipc_importance_task_t, iit_props);
816 }
817 }
818 }
819
820 /*
821 * Routine: ipc_importance_task_process_updates
822 * Purpose:
823 * Process the queue of task importances and apply the policy
824 * update called for. Only process tasks in the queue with an
825 * update timestamp less than the supplied max.
826 * Conditions:
827 * Called and returns with importance locked.
828 * May drop importance lock and block temporarily.
829 */
830 static void
831 ipc_importance_task_process_updates(
832 queue_t supplied_queue,
833 boolean_t boost,
834 uint64_t max_timestamp)
835 {
836 ipc_importance_task_t task_imp;
837 queue_head_t second_chance;
838 queue_t queue = supplied_queue;
839
840 /*
841 * This queue will hold the task's we couldn't trylock on first pass.
842 * By using a second (private) queue, we guarantee all tasks that get
843 * entered on this queue have a timestamp under the maximum.
844 */
845 queue_init(&second_chance);
846
847 /* process any resulting policy updates */
848 retry:
849 while(!queue_empty(queue)) {
850 task_t target_task;
851 struct task_pend_token pend_token = {};
852
853 task_imp = (ipc_importance_task_t)queue_first(queue);
854 assert(0 == task_imp->iit_updatepolicy);
855 assert(queue == task_imp->iit_updateq);
856
857 /* if timestamp is too big, we're done */
858 if (task_imp->iit_updatetime > max_timestamp) {
859 break;
860 }
861
862 /* we were given a reference on each task in the queue */
863
864 /* remove it from the supplied queue */
865 queue_remove(queue, task_imp, ipc_importance_task_t, iit_updates);
866 task_imp->iit_updateq = NULL;
867
868 target_task = task_imp->iit_task;
869
870 /* Is it well on the way to exiting? */
871 if (TASK_NULL == target_task) {
872 ipc_importance_task_release_locked(task_imp);
873 /* importance unlocked */
874 ipc_importance_lock();
875 continue;
876 }
877
878 /* Has the update been reversed on the hysteresis queue? */
879 if (0 < task_imp->iit_assertcnt &&
880 queue == &ipc_importance_delayed_drop_queue) {
881 ipc_importance_task_release_locked(task_imp);
882 /* importance unlocked */
883 ipc_importance_lock();
884 continue;
885 }
886
887 /*
888 * Can we get the task lock out-of-order?
889 * If not, stick this back on the second-chance queue.
890 */
891 if (!task_lock_try(target_task)) {
892 boolean_t should_wait_lock = (queue == &second_chance);
893 task_imp->iit_updateq = &second_chance;
894
895 /*
896 * If we're already processing second-chances on
897 * tasks, keep this task on the front of the queue.
898 * We will wait for the task lock before coming
899 * back and trying again, and we have a better
900 * chance of re-acquiring the lock if we come back
901 * to it right away.
902 */
903 if (should_wait_lock){
904 task_reference(target_task);
905 queue_enter_first(&second_chance, task_imp,
906 ipc_importance_task_t, iit_updates);
907 } else {
908 queue_enter(&second_chance, task_imp,
909 ipc_importance_task_t, iit_updates);
910 }
911 ipc_importance_unlock();
912
913 if (should_wait_lock) {
914 task_lock(target_task);
915 task_unlock(target_task);
916 task_deallocate(target_task);
917 }
918
919 ipc_importance_lock();
920 continue;
921 }
922
923 /* is it going away? */
924 if (!target_task->active) {
925 task_unlock(target_task);
926 ipc_importance_task_release_locked(task_imp);
927 /* importance unlocked */
928 ipc_importance_lock();
929 continue;
930 }
931
932 /* take a task reference for while we don't have the importance lock */
933 task_reference(target_task);
934
935 /* count the transition */
936 if (boost)
937 task_imp->iit_transitions++;
938
939 ipc_importance_unlock();
940
941 /* apply the policy adjust to the target task (while it is still locked) */
942 task_update_boost_locked(target_task, boost, &pend_token);
943
944 /* complete the policy update with the task unlocked */
945 ipc_importance_task_release(task_imp);
946 task_unlock(target_task);
947 task_policy_update_complete_unlocked(target_task, &pend_token);
948 task_deallocate(target_task);
949
950 ipc_importance_lock();
951 }
952
953 /* If there are tasks we couldn't update the first time, try again */
954 if (!queue_empty(&second_chance)) {
955 queue = &second_chance;
956 goto retry;
957 }
958 }
959
960
961 /*
962 * Routine: ipc_importance_task_delayed_drop_scan
963 * Purpose:
964 * The thread call routine to scan the delayed drop queue,
965 * requesting all updates with a deadline up to the last target
966 * for the thread-call (which is DENAP_DROP_SKEW beyond the first
967 * thread's optimum delay).
968 * update to drop its boost.
969 * Conditions:
970 * Nothing locked
971 */
972 static void
973 ipc_importance_task_delayed_drop_scan(
974 __unused void *arg1,
975 __unused void *arg2)
976 {
977 ipc_importance_lock();
978
979 /* process all queued task drops with timestamps up to TARGET(first)+SKEW */
980 ipc_importance_task_process_updates(&ipc_importance_delayed_drop_queue,
981 FALSE,
982 ipc_importance_delayed_drop_timestamp);
983
984 /* importance lock may have been temporarily dropped */
985
986 /* If there are any entries left in the queue, re-arm the call here */
987 if (!queue_empty(&ipc_importance_delayed_drop_queue)) {
988 ipc_importance_task_t task_imp;
989 uint64_t deadline;
990 uint64_t leeway;
991
992 task_imp = (ipc_importance_task_t)queue_first(&ipc_importance_delayed_drop_queue);
993
994 nanoseconds_to_absolutetime(DENAP_DROP_DELAY, &deadline);
995 deadline += task_imp->iit_updatetime;
996 ipc_importance_delayed_drop_timestamp = deadline;
997
998 nanoseconds_to_absolutetime(DENAP_DROP_LEEWAY, &leeway);
999
1000 thread_call_enter_delayed_with_leeway(
1001 ipc_importance_delayed_drop_call,
1002 NULL,
1003 deadline,
1004 leeway,
1005 DENAP_DROP_FLAGS);
1006 } else {
1007 ipc_importance_delayed_drop_call_requested = FALSE;
1008 }
1009 ipc_importance_unlock();
1010 }
1011
1012 /*
1013 * Routine: ipc_importance_task_delayed_drop
1014 * Purpose:
1015 * Queue the specified task importance for delayed policy
1016 * update to drop its boost.
1017 * Conditions:
1018 * Called with the importance lock held.
1019 */
1020 static void
1021 ipc_importance_task_delayed_drop(ipc_importance_task_t task_imp)
1022 {
1023 uint64_t timestamp = mach_absolute_time(); /* no mach_approximate_time() in kernel */
1024
1025 assert(ipc_importance_delayed_drop_call != NULL);
1026
1027 /*
1028 * If still on an update queue from a previous change,
1029 * remove it first (and use that reference). Otherwise, take
1030 * a new reference for the delay drop update queue.
1031 */
1032 if (NULL != task_imp->iit_updateq) {
1033 queue_remove(task_imp->iit_updateq, task_imp,
1034 ipc_importance_task_t, iit_updates);
1035 } else {
1036 ipc_importance_task_reference_internal(task_imp);
1037 }
1038
1039 task_imp->iit_updateq = &ipc_importance_delayed_drop_queue;
1040 task_imp->iit_updatetime = timestamp;
1041
1042 queue_enter(&ipc_importance_delayed_drop_queue, task_imp,
1043 ipc_importance_task_t, iit_updates);
1044
1045 /* request the delayed thread-call if not already requested */
1046 if (!ipc_importance_delayed_drop_call_requested) {
1047 uint64_t deadline;
1048 uint64_t leeway;
1049
1050 nanoseconds_to_absolutetime(DENAP_DROP_DELAY, &deadline);
1051 deadline += task_imp->iit_updatetime;
1052 ipc_importance_delayed_drop_timestamp = deadline;
1053
1054 nanoseconds_to_absolutetime(DENAP_DROP_LEEWAY, &leeway);
1055
1056 ipc_importance_delayed_drop_call_requested = TRUE;
1057 thread_call_enter_delayed_with_leeway(
1058 ipc_importance_delayed_drop_call,
1059 NULL,
1060 deadline,
1061 leeway,
1062 DENAP_DROP_FLAGS);
1063 }
1064 }
1065
1066
1067 /*
1068 * Routine: ipc_importance_task_propagate_assertion_locked
1069 * Purpose:
1070 * Propagate the importance transition type to every item
1071 * If this causes a boost to be applied, determine if that
1072 * boost should propagate downstream.
1073 * Conditions:
1074 * Called with the importance lock held.
1075 */
1076 static void
1077 ipc_importance_task_propagate_assertion_locked(
1078 ipc_importance_task_t task_imp,
1079 iit_update_type_t type,
1080 boolean_t update_task_imp)
1081 {
1082 boolean_t boost = (IIT_UPDATE_HOLD == type);
1083 ipc_importance_task_t temp_task_imp;
1084 queue_head_t propagate;
1085 queue_head_t updates;
1086
1087 queue_init(&updates);
1088 queue_init(&propagate);
1089
1090 /*
1091 * If we're going to update the policy for the provided task,
1092 * enqueue it on the propagate queue itself. Otherwise, only
1093 * enqueue downstream things.
1094 */
1095 if (update_task_imp) {
1096 queue_enter(&propagate, task_imp, ipc_importance_task_t, iit_props);
1097 } else {
1098 ipc_importance_task_propagate_helper(task_imp, type, &propagate);
1099 }
1100
1101 /*
1102 * for each item on the propagation list, propagate any change downstream,
1103 * adding new tasks to propagate further if they transistioned as well.
1104 */
1105 while (!queue_empty(&propagate)) {
1106 boolean_t need_update;
1107
1108 queue_remove_first(&propagate, temp_task_imp, ipc_importance_task_t, iit_props);
1109 assert(IIT_NULL != temp_task_imp);
1110
1111 /* only propagate for receivers not already marked as a donor */
1112 if (!ipc_importance_task_is_marked_donor(temp_task_imp) &&
1113 ipc_importance_task_is_marked_receiver(temp_task_imp)) {
1114 ipc_importance_task_propagate_helper(temp_task_imp, type, &propagate);
1115 }
1116
1117 /* if we have a policy update to apply, enqueue a reference for later processing */
1118 need_update = (0 != temp_task_imp->iit_updatepolicy);
1119 temp_task_imp->iit_updatepolicy = 0;
1120 if (need_update && TASK_NULL != temp_task_imp->iit_task) {
1121 if (NULL == temp_task_imp->iit_updateq) {
1122
1123 /*
1124 * If a downstream task that needs an update is subjects to AppNap,
1125 * drop boosts according to the delay hysteresis. Otherwise,
1126 * immediate update it.
1127 */
1128 if (!boost && temp_task_imp != task_imp &&
1129 ipc_importance_delayed_drop_call != NULL &&
1130 ipc_importance_task_is_marked_denap_receiver(temp_task_imp)) {
1131 ipc_importance_task_delayed_drop(temp_task_imp);
1132 } else {
1133 temp_task_imp->iit_updatetime = 0;
1134 temp_task_imp->iit_updateq = &updates;
1135 ipc_importance_task_reference_internal(temp_task_imp);
1136 if (boost) {
1137 queue_enter(&updates, temp_task_imp,
1138 ipc_importance_task_t, iit_updates);
1139 } else {
1140 queue_enter_first(&updates, temp_task_imp,
1141 ipc_importance_task_t, iit_updates);
1142 }
1143 }
1144 } else {
1145 /* Must already be on the AppNap hysteresis queue */
1146 assert(ipc_importance_delayed_drop_call != NULL);
1147 assert(ipc_importance_task_is_marked_denap_receiver(temp_task_imp));
1148 }
1149 }
1150 }
1151
1152 /* apply updates to task (may drop importance lock) */
1153 if (!queue_empty(&updates)) {
1154 ipc_importance_task_process_updates(&updates, boost, 0);
1155 }
1156 }
1157
1158 /*
1159 * Routine: ipc_importance_task_hold_internal_assertion_locked
1160 * Purpose:
1161 * Increment the assertion count on the task importance.
1162 * If this results in a boost state change in that task,
1163 * prepare to update task policy for this task AND, if
1164 * if not just waking out of App Nap, all down-stream
1165 * tasks that have a similar transition through inheriting
1166 * this update.
1167 * Conditions:
1168 * importance locked on entry and exit.
1169 * May temporarily drop importance lock and block.
1170 */
1171 static kern_return_t
1172 ipc_importance_task_hold_internal_assertion_locked(ipc_importance_task_t task_imp, uint32_t count)
1173 {
1174 if (ipc_importance_task_check_transition(task_imp, IIT_UPDATE_HOLD, count)) {
1175 ipc_importance_task_propagate_assertion_locked(task_imp, IIT_UPDATE_HOLD, TRUE);
1176 }
1177 return KERN_SUCCESS;
1178 }
1179
1180 /*
1181 * Routine: ipc_importance_task_drop_internal_assertion_locked
1182 * Purpose:
1183 * Decrement the assertion count on the task importance.
1184 * If this results in a boost state change in that task,
1185 * prepare to update task policy for this task AND, if
1186 * if not just waking out of App Nap, all down-stream
1187 * tasks that have a similar transition through inheriting
1188 * this update.
1189 * Conditions:
1190 * importance locked on entry and exit.
1191 * May temporarily drop importance lock and block.
1192 */
1193 static kern_return_t
1194 ipc_importance_task_drop_internal_assertion_locked(ipc_importance_task_t task_imp, uint32_t count)
1195 {
1196 if (ipc_importance_task_check_transition(task_imp, IIT_UPDATE_DROP, count)) {
1197 ipc_importance_task_propagate_assertion_locked(task_imp, IIT_UPDATE_DROP, TRUE);
1198 }
1199 return KERN_SUCCESS;
1200 }
1201
1202 /*
1203 * Routine: ipc_importance_task_hold_internal_assertion
1204 * Purpose:
1205 * Increment the assertion count on the task importance.
1206 * If this results in a 0->1 change in that count,
1207 * prepare to update task policy for this task AND
1208 * (potentially) all down-stream tasks that have a
1209 * similar transition through inheriting this update.
1210 * Conditions:
1211 * Nothing locked
1212 * May block after dropping importance lock.
1213 */
1214 int
1215 ipc_importance_task_hold_internal_assertion(ipc_importance_task_t task_imp, uint32_t count)
1216 {
1217 int ret = KERN_SUCCESS;
1218
1219 if (ipc_importance_task_is_any_receiver_type(task_imp)) {
1220 ipc_importance_lock();
1221 ret = ipc_importance_task_hold_internal_assertion_locked(task_imp, count);
1222 ipc_importance_unlock();
1223 }
1224 return ret;
1225 }
1226
1227 /*
1228 * Routine: ipc_importance_task_drop_internal_assertion
1229 * Purpose:
1230 * Decrement the assertion count on the task importance.
1231 * If this results in a X->0 change in that count,
1232 * prepare to update task policy for this task AND
1233 * all down-stream tasks that have a similar transition
1234 * through inheriting this drop update.
1235 * Conditions:
1236 * Nothing locked on entry.
1237 * May block after dropping importance lock.
1238 */
1239 kern_return_t
1240 ipc_importance_task_drop_internal_assertion(ipc_importance_task_t task_imp, uint32_t count)
1241 {
1242 kern_return_t ret = KERN_SUCCESS;
1243
1244 if (ipc_importance_task_is_any_receiver_type(task_imp)) {
1245 ipc_importance_lock();
1246 ret = ipc_importance_task_drop_internal_assertion_locked(task_imp, count);
1247 ipc_importance_unlock();
1248 }
1249 return ret;
1250 }
1251
1252 /*
1253 * Routine: ipc_importance_task_hold_file_lock_assertion
1254 * Purpose:
1255 * Increment the file lock assertion count on the task importance.
1256 * If this results in a 0->1 change in that count,
1257 * prepare to update task policy for this task AND
1258 * (potentially) all down-stream tasks that have a
1259 * similar transition through inheriting this update.
1260 * Conditions:
1261 * Nothing locked
1262 * May block after dropping importance lock.
1263 */
1264 kern_return_t
1265 ipc_importance_task_hold_file_lock_assertion(ipc_importance_task_t task_imp, uint32_t count)
1266 {
1267 kern_return_t ret = KERN_SUCCESS;
1268
1269 if (ipc_importance_task_is_any_receiver_type(task_imp)) {
1270 ipc_importance_lock();
1271 ret = ipc_importance_task_hold_internal_assertion_locked(task_imp, count);
1272 if (KERN_SUCCESS == ret) {
1273 task_imp->iit_filelocks += count;
1274 }
1275 ipc_importance_unlock();
1276 }
1277 return ret;
1278 }
1279
1280 /*
1281 * Routine: ipc_importance_task_drop_file_lock_assertion
1282 * Purpose:
1283 * Decrement the assertion count on the task importance.
1284 * If this results in a X->0 change in that count,
1285 * prepare to update task policy for this task AND
1286 * all down-stream tasks that have a similar transition
1287 * through inheriting this drop update.
1288 * Conditions:
1289 * Nothing locked on entry.
1290 * May block after dropping importance lock.
1291 */
1292 kern_return_t
1293 ipc_importance_task_drop_file_lock_assertion(ipc_importance_task_t task_imp, uint32_t count)
1294 {
1295 kern_return_t ret = KERN_SUCCESS;
1296
1297 if (ipc_importance_task_is_any_receiver_type(task_imp)) {
1298 ipc_importance_lock();
1299 if (count <= task_imp->iit_filelocks) {
1300 task_imp->iit_filelocks -= count;
1301 ret = ipc_importance_task_drop_internal_assertion_locked(task_imp, count);
1302 } else {
1303 ret = KERN_INVALID_ARGUMENT;
1304 }
1305 ipc_importance_unlock();
1306 }
1307 return ret;
1308 }
1309
1310 /*
1311 * Routine: ipc_importance_task_hold_legacy_external_assertion
1312 * Purpose:
1313 * Increment the external assertion count on the task importance.
1314 * This cannot result in an 0->1 transition, as the caller must
1315 * already hold an external boost.
1316 * Conditions:
1317 * Nothing locked on entry.
1318 * May block after dropping importance lock.
1319 * A queue of task importance structures is returned
1320 * by ipc_importance_task_hold_assertion_locked(). Each
1321 * needs to be updated (outside the importance lock hold).
1322 */
1323 kern_return_t
1324 ipc_importance_task_hold_legacy_external_assertion(ipc_importance_task_t task_imp, uint32_t count)
1325 {
1326 task_t target_task;
1327 uint32_t target_assertcnt;
1328 uint32_t target_externcnt;
1329 uint32_t target_legacycnt;
1330
1331 kern_return_t ret;
1332
1333 ipc_importance_lock();
1334 target_task = task_imp->iit_task;
1335
1336 #if IMPORTANCE_DEBUG
1337 int target_pid = task_pid(target_task);
1338
1339 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, (IMP_HOLD | TASK_POLICY_EXTERNAL))) | DBG_FUNC_START,
1340 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_LEGACY_EXTERN(task_imp), 0);
1341 #endif
1342
1343 if (IIT_LEGACY_EXTERN(task_imp) == 0) {
1344 /* Only allowed to take a new boost assertion when holding an external boost */
1345 /* save data for diagnostic printf below */
1346 target_assertcnt = task_imp->iit_assertcnt;
1347 target_externcnt = IIT_EXTERN(task_imp);
1348 target_legacycnt = IIT_LEGACY_EXTERN(task_imp);
1349 ret = KERN_FAILURE;
1350 count = 0;
1351 } else {
1352 assert(ipc_importance_task_is_any_receiver_type(task_imp));
1353 assert(0 < task_imp->iit_assertcnt);
1354 assert(0 < IIT_EXTERN(task_imp));
1355 task_imp->iit_assertcnt += count;
1356 task_imp->iit_externcnt += count;
1357 task_imp->iit_legacy_externcnt += count;
1358 ret = KERN_SUCCESS;
1359 }
1360 ipc_importance_unlock();
1361
1362 #if IMPORTANCE_DEBUG
1363 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, (IMP_HOLD | TASK_POLICY_EXTERNAL))) | DBG_FUNC_END,
1364 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_LEGACY_EXTERN(task_imp), 0);
1365 // This covers the legacy case where a task takes an extra boost.
1366 DTRACE_BOOST5(receive_boost, task_t, target_task, int, target_pid, int, proc_selfpid(), int, count, int, task_imp->iit_assertcnt);
1367 #endif
1368
1369 if (KERN_FAILURE == ret && target_task != TASK_NULL) {
1370 printf("BUG in process %s[%d]: "
1371 "attempt to acquire an additional legacy external boost assertion without holding an existing legacy external assertion. "
1372 "(%d total, %d external, %d legacy-external)\n",
1373 proc_name_address(target_task->bsd_info), task_pid(target_task),
1374 target_assertcnt, target_externcnt, target_legacycnt);
1375 }
1376
1377 return(ret);
1378 }
1379
1380 /*
1381 * Routine: ipc_importance_task_drop_legacy_external_assertion
1382 * Purpose:
1383 * Drop the legacy external assertion count on the task and
1384 * reflect that change to total external assertion count and
1385 * then onto the internal importance count.
1386 *
1387 * If this results in a X->0 change in the internal,
1388 * count, prepare to update task policy for this task AND
1389 * all down-stream tasks that have a similar transition
1390 * through inheriting this update.
1391 * Conditions:
1392 * Nothing locked on entry.
1393 */
1394 kern_return_t
1395 ipc_importance_task_drop_legacy_external_assertion(ipc_importance_task_t task_imp, uint32_t count)
1396 {
1397 int ret = KERN_SUCCESS;
1398 task_t target_task;
1399 uint32_t target_assertcnt;
1400 uint32_t target_externcnt;
1401 uint32_t target_legacycnt;
1402
1403 if (count > 1) {
1404 return KERN_INVALID_ARGUMENT;
1405 }
1406
1407 ipc_importance_lock();
1408 target_task = task_imp->iit_task;
1409
1410 #if IMPORTANCE_DEBUG
1411 int target_pid = task_pid(target_task);
1412
1413 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, (IMP_DROP | TASK_POLICY_EXTERNAL))) | DBG_FUNC_START,
1414 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_LEGACY_EXTERN(task_imp), 0);
1415 #endif
1416
1417 if (count > IIT_LEGACY_EXTERN(task_imp)) {
1418 /* Process over-released its boost count - save data for diagnostic printf */
1419 /* TODO: If count > 1, we should clear out as many external assertions as there are left. */
1420 target_assertcnt = task_imp->iit_assertcnt;
1421 target_externcnt = IIT_EXTERN(task_imp);
1422 target_legacycnt = IIT_LEGACY_EXTERN(task_imp);
1423 ret = KERN_FAILURE;
1424 } else {
1425 /*
1426 * decrement legacy external count from the top level and reflect
1427 * into internal for this and all subsequent updates.
1428 */
1429 assert(ipc_importance_task_is_any_receiver_type(task_imp));
1430 assert(IIT_EXTERN(task_imp) >= count);
1431
1432 task_imp->iit_legacy_externdrop += count;
1433 task_imp->iit_externdrop += count;
1434
1435 /* reset extern counters (if appropriate) */
1436 if (IIT_LEGACY_EXTERN(task_imp) == 0) {
1437 if (IIT_EXTERN(task_imp) != 0) {
1438 task_imp->iit_externcnt -= task_imp->iit_legacy_externcnt;
1439 task_imp->iit_externdrop -= task_imp->iit_legacy_externdrop;
1440 } else {
1441 task_imp->iit_externcnt = 0;
1442 task_imp->iit_externdrop = 0;
1443 }
1444 task_imp->iit_legacy_externcnt = 0;
1445 task_imp->iit_legacy_externdrop = 0;
1446 }
1447
1448 /* reflect the drop to the internal assertion count (and effect any importance change) */
1449 if (ipc_importance_task_check_transition(task_imp, IIT_UPDATE_DROP, count)) {
1450 ipc_importance_task_propagate_assertion_locked(task_imp, IIT_UPDATE_DROP, TRUE);
1451 }
1452 ret = KERN_SUCCESS;
1453 }
1454
1455 #if IMPORTANCE_DEBUG
1456 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, (IMP_DROP | TASK_POLICY_EXTERNAL))) | DBG_FUNC_END,
1457 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_LEGACY_EXTERN(task_imp), 0);
1458 #endif
1459
1460 ipc_importance_unlock();
1461
1462 /* delayed printf for user-supplied data failures */
1463 if (KERN_FAILURE == ret && TASK_NULL != target_task) {
1464 printf("BUG in process %s[%d]: over-released legacy external boost assertions (%d total, %d external, %d legacy-external)\n",
1465 proc_name_address(target_task->bsd_info), task_pid(target_task),
1466 target_assertcnt, target_externcnt, target_legacycnt);
1467 }
1468
1469 return(ret);
1470 }
1471
1472
1473
1474 /* Transfer an assertion to legacy userspace responsibility */
1475 static kern_return_t
1476 ipc_importance_task_externalize_legacy_assertion(ipc_importance_task_t task_imp, uint32_t count, __unused int sender_pid)
1477 {
1478 task_t target_task;
1479
1480 assert(IIT_NULL != task_imp);
1481 target_task = task_imp->iit_task;
1482
1483 if (TASK_NULL == target_task ||
1484 !ipc_importance_task_is_any_receiver_type(task_imp)) {
1485 return KERN_FAILURE;
1486 }
1487
1488 #if IMPORTANCE_DEBUG
1489 int target_pid = task_pid(target_task);
1490
1491 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, IMP_EXTERN)) | DBG_FUNC_START,
1492 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_EXTERN(task_imp), 0);
1493 #endif
1494
1495 ipc_importance_lock();
1496 /* assert(task_imp->iit_assertcnt >= IIT_EXTERN(task_imp) + count); */
1497 assert(IIT_EXTERN(task_imp) >= IIT_LEGACY_EXTERN(task_imp));
1498 task_imp->iit_legacy_externcnt += count;
1499 task_imp->iit_externcnt += count;
1500 ipc_importance_unlock();
1501
1502 #if IMPORTANCE_DEBUG
1503 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, IMP_EXTERN)) | DBG_FUNC_END,
1504 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_LEGACY_EXTERN(task_imp), 0);
1505 // This is the legacy boosting path
1506 DTRACE_BOOST5(receive_boost, task_t, target_task, int, target_pid, int, sender_pid, int, count, int, IIT_LEGACY_EXTERN(task_imp));
1507 #endif /* IMPORTANCE_DEBUG */
1508
1509 return(KERN_SUCCESS);
1510 }
1511
1512 /*
1513 * Routine: ipc_importance_task_update_live_donor
1514 * Purpose:
1515 * Read the live donor status and update the live_donor bit/propagate the change in importance.
1516 * Conditions:
1517 * Nothing locked on entrance, nothing locked on exit.
1518 *
1519 * TODO: Need tracepoints around this function...
1520 */
1521 void
1522 ipc_importance_task_update_live_donor(ipc_importance_task_t task_imp)
1523 {
1524 uint32_t task_live_donor;
1525 boolean_t before_donor;
1526 boolean_t after_donor;
1527 task_t target_task;
1528
1529 assert(task_imp != NULL);
1530
1531 /*
1532 * Nothing to do if the task is not marked as expecting
1533 * live donor updates.
1534 */
1535 if (!ipc_importance_task_is_marked_live_donor(task_imp)) {
1536 return;
1537 }
1538
1539 ipc_importance_lock();
1540
1541 /* If the task got disconnected on the way here, no use (or ability) adjusting live donor status */
1542 target_task = task_imp->iit_task;
1543 if (TASK_NULL == target_task) {
1544 ipc_importance_unlock();
1545 return;
1546 }
1547 before_donor = ipc_importance_task_is_marked_donor(task_imp);
1548
1549 /* snapshot task live donor status - may change, but another call will accompany the change */
1550 task_live_donor = target_task->effective_policy.tep_live_donor;
1551
1552 #if IMPORTANCE_DEBUG
1553 int target_pid = task_pid(target_task);
1554
1555 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
1556 (IMPORTANCE_CODE(IMP_DONOR_CHANGE, IMP_DONOR_UPDATE_LIVE_DONOR_STATE)) | DBG_FUNC_START,
1557 target_pid, task_imp->iit_donor, task_live_donor, before_donor, 0);
1558 #endif
1559
1560 /* update the task importance live donor status based on the task's value */
1561 task_imp->iit_donor = task_live_donor;
1562
1563 after_donor = ipc_importance_task_is_marked_donor(task_imp);
1564
1565 /* Has the effectiveness of being a donor changed as a result of this update? */
1566 if (before_donor != after_donor) {
1567 iit_update_type_t type;
1568
1569 /* propagate assertions without updating the current task policy (already handled) */
1570 if (0 == before_donor) {
1571 task_imp->iit_transitions++;
1572 type = IIT_UPDATE_HOLD;
1573 } else {
1574 type = IIT_UPDATE_DROP;
1575 }
1576 ipc_importance_task_propagate_assertion_locked(task_imp, type, FALSE);
1577 }
1578
1579 #if IMPORTANCE_DEBUG
1580 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
1581 (IMPORTANCE_CODE(IMP_DONOR_CHANGE, IMP_DONOR_UPDATE_LIVE_DONOR_STATE)) | DBG_FUNC_END,
1582 target_pid, task_imp->iit_donor, task_live_donor, after_donor, 0);
1583 #endif
1584
1585 ipc_importance_unlock();
1586 }
1587
1588
1589 /*
1590 * Routine: ipc_importance_task_mark_donor
1591 * Purpose:
1592 * Set the task importance donor flag.
1593 * Conditions:
1594 * Nothing locked on entrance, nothing locked on exit.
1595 *
1596 * This is only called while the task is being constructed,
1597 * so no need to update task policy or propagate downstream.
1598 */
1599 void
1600 ipc_importance_task_mark_donor(ipc_importance_task_t task_imp, boolean_t donating)
1601 {
1602 assert(task_imp != NULL);
1603
1604 ipc_importance_lock();
1605
1606 int old_donor = task_imp->iit_donor;
1607
1608 task_imp->iit_donor = (donating ? 1 : 0);
1609
1610 if (task_imp->iit_donor > 0 && old_donor == 0)
1611 task_imp->iit_transitions++;
1612
1613 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
1614 (IMPORTANCE_CODE(IMP_DONOR_CHANGE, IMP_DONOR_INIT_DONOR_STATE)) | DBG_FUNC_NONE,
1615 task_pid(task_imp->iit_task), donating,
1616 old_donor, task_imp->iit_donor, 0);
1617
1618 ipc_importance_unlock();
1619 }
1620
1621 /*
1622 * Routine: ipc_importance_task_marked_donor
1623 * Purpose:
1624 * Query the donor flag for the given task importance.
1625 * Conditions:
1626 * May be called without taking the importance lock.
1627 * In that case, donor status can change so you must
1628 * check only once for each donation event.
1629 */
1630 boolean_t
1631 ipc_importance_task_is_marked_donor(ipc_importance_task_t task_imp)
1632 {
1633 if (IIT_NULL == task_imp) {
1634 return FALSE;
1635 }
1636 return (0 != task_imp->iit_donor);
1637 }
1638
1639 /*
1640 * Routine: ipc_importance_task_mark_live_donor
1641 * Purpose:
1642 * Indicate that the task is eligible for live donor updates.
1643 * Conditions:
1644 * Nothing locked on entrance, nothing locked on exit.
1645 *
1646 * This is only called while the task is being constructed.
1647 */
1648 void
1649 ipc_importance_task_mark_live_donor(ipc_importance_task_t task_imp, boolean_t live_donating)
1650 {
1651 assert(task_imp != NULL);
1652
1653 ipc_importance_lock();
1654 task_imp->iit_live_donor = (live_donating ? 1 : 0);
1655 ipc_importance_unlock();
1656 }
1657
1658 /*
1659 * Routine: ipc_importance_task_marked_live_donor
1660 * Purpose:
1661 * Query the live donor and donor flags for the given task importance.
1662 * Conditions:
1663 * May be called without taking the importance lock.
1664 * In that case, donor status can change so you must
1665 * check only once for each donation event.
1666 */
1667 boolean_t
1668 ipc_importance_task_is_marked_live_donor(ipc_importance_task_t task_imp)
1669 {
1670 if (IIT_NULL == task_imp) {
1671 return FALSE;
1672 }
1673 return (0 != task_imp->iit_live_donor);
1674 }
1675
1676 /*
1677 * Routine: ipc_importance_task_is_donor
1678 * Purpose:
1679 * Query the full donor status for the given task importance.
1680 * Conditions:
1681 * May be called without taking the importance lock.
1682 * In that case, donor status can change so you must
1683 * check only once for each donation event.
1684 */
1685 boolean_t
1686 ipc_importance_task_is_donor(ipc_importance_task_t task_imp)
1687 {
1688 if (IIT_NULL == task_imp) {
1689 return FALSE;
1690 }
1691 return (ipc_importance_task_is_marked_donor(task_imp) ||
1692 (ipc_importance_task_is_marked_receiver(task_imp) &&
1693 task_imp->iit_assertcnt > 0));
1694 }
1695
1696 /*
1697 * Routine: ipc_importance_task_is_never_donor
1698 * Purpose:
1699 * Query if a given task can ever donate importance.
1700 * Conditions:
1701 * May be called without taking the importance lock.
1702 * Condition is permanent for a give task.
1703 */
1704 boolean_t
1705 ipc_importance_task_is_never_donor(ipc_importance_task_t task_imp)
1706 {
1707 if (IIT_NULL == task_imp) {
1708 return FALSE;
1709 }
1710 return (!ipc_importance_task_is_marked_donor(task_imp) &&
1711 !ipc_importance_task_is_marked_live_donor(task_imp) &&
1712 !ipc_importance_task_is_marked_receiver(task_imp));
1713 }
1714
1715 /*
1716 * Routine: ipc_importance_task_mark_receiver
1717 * Purpose:
1718 * Update the task importance receiver flag.
1719 * Conditions:
1720 * Nothing locked on entrance, nothing locked on exit.
1721 * This can only be invoked before the task is discoverable,
1722 * so no worries about atomicity(?)
1723 */
1724 void
1725 ipc_importance_task_mark_receiver(ipc_importance_task_t task_imp, boolean_t receiving)
1726 {
1727 assert(task_imp != NULL);
1728
1729 ipc_importance_lock();
1730 if (receiving) {
1731 assert(task_imp->iit_assertcnt == 0);
1732 assert(task_imp->iit_externcnt == 0);
1733 assert(task_imp->iit_externdrop == 0);
1734 assert(task_imp->iit_denap == 0);
1735 task_imp->iit_receiver = 1; /* task can receive importance boost */
1736 } else if (task_imp->iit_receiver) {
1737 assert(task_imp->iit_denap == 0);
1738 if (task_imp->iit_assertcnt != 0 || IIT_EXTERN(task_imp) != 0) {
1739 panic("disabling imp_receiver on task with pending importance boosts!");
1740 }
1741 task_imp->iit_receiver = 0;
1742 }
1743 ipc_importance_unlock();
1744 }
1745
1746
1747 /*
1748 * Routine: ipc_importance_task_marked_receiver
1749 * Purpose:
1750 * Query the receiver flag for the given task importance.
1751 * Conditions:
1752 * May be called without taking the importance lock as
1753 * the importance flag can never change after task init.
1754 */
1755 boolean_t
1756 ipc_importance_task_is_marked_receiver(ipc_importance_task_t task_imp)
1757 {
1758 return (IIT_NULL != task_imp && 0 != task_imp->iit_receiver);
1759 }
1760
1761
1762 /*
1763 * Routine: ipc_importance_task_mark_denap_receiver
1764 * Purpose:
1765 * Update the task importance de-nap receiver flag.
1766 * Conditions:
1767 * Nothing locked on entrance, nothing locked on exit.
1768 * This can only be invoked before the task is discoverable,
1769 * so no worries about atomicity(?)
1770 */
1771 void
1772 ipc_importance_task_mark_denap_receiver(ipc_importance_task_t task_imp, boolean_t denap)
1773 {
1774 assert(task_imp != NULL);
1775
1776 ipc_importance_lock();
1777 if (denap) {
1778 assert(task_imp->iit_assertcnt == 0);
1779 assert(task_imp->iit_externcnt == 0);
1780 assert(task_imp->iit_receiver == 0);
1781 task_imp->iit_denap = 1; /* task can receive de-nap boost */
1782 } else if (task_imp->iit_denap) {
1783 assert(task_imp->iit_receiver == 0);
1784 if (0 < task_imp->iit_assertcnt || 0 < IIT_EXTERN(task_imp)) {
1785 panic("disabling de-nap on task with pending de-nap boosts!");
1786 }
1787 task_imp->iit_denap = 0;
1788 }
1789 ipc_importance_unlock();
1790 }
1791
1792
1793 /*
1794 * Routine: ipc_importance_task_marked_denap_receiver
1795 * Purpose:
1796 * Query the de-nap receiver flag for the given task importance.
1797 * Conditions:
1798 * May be called without taking the importance lock as
1799 * the de-nap flag can never change after task init.
1800 */
1801 boolean_t
1802 ipc_importance_task_is_marked_denap_receiver(ipc_importance_task_t task_imp)
1803 {
1804 return (IIT_NULL != task_imp && 0 != task_imp->iit_denap);
1805 }
1806
1807 /*
1808 * Routine: ipc_importance_task_is_denap_receiver
1809 * Purpose:
1810 * Query the full de-nap receiver status for the given task importance.
1811 * For now, that is simply whether the receiver flag is set.
1812 * Conditions:
1813 * May be called without taking the importance lock as
1814 * the de-nap receiver flag can never change after task init.
1815 */
1816 boolean_t
1817 ipc_importance_task_is_denap_receiver(ipc_importance_task_t task_imp)
1818 {
1819 return (ipc_importance_task_is_marked_denap_receiver(task_imp));
1820 }
1821
1822 /*
1823 * Routine: ipc_importance_task_is_any_receiver_type
1824 * Purpose:
1825 * Query if the task is marked to receive boosts - either
1826 * importance or denap.
1827 * Conditions:
1828 * May be called without taking the importance lock as both
1829 * the importance and de-nap receiver flags can never change
1830 * after task init.
1831 */
1832 boolean_t
1833 ipc_importance_task_is_any_receiver_type(ipc_importance_task_t task_imp)
1834 {
1835 return (ipc_importance_task_is_marked_receiver(task_imp) ||
1836 ipc_importance_task_is_marked_denap_receiver(task_imp));
1837 }
1838
1839 #if 0 /* currently unused */
1840
1841 /*
1842 * Routine: ipc_importance_inherit_reference
1843 * Purpose:
1844 * Add a reference to the inherit importance element.
1845 * Conditions:
1846 * Caller most hold a reference on the inherit element.
1847 */
1848 static inline void
1849 ipc_importance_inherit_reference(ipc_importance_inherit_t inherit)
1850 {
1851 ipc_importance_reference(&inherit->iii_elem);
1852 }
1853 #endif /* currently unused */
1854
1855 /*
1856 * Routine: ipc_importance_inherit_release_locked
1857 * Purpose:
1858 * Release a reference on an inherit importance attribute value,
1859 * unlinking and deallocating the attribute if the last reference.
1860 * Conditions:
1861 * Entered with importance lock held, leaves with it unlocked.
1862 */
1863 static inline void
1864 ipc_importance_inherit_release_locked(ipc_importance_inherit_t inherit)
1865 {
1866 ipc_importance_release_locked(&inherit->iii_elem);
1867 }
1868
1869 #if 0 /* currently unused */
1870 /*
1871 * Routine: ipc_importance_inherit_release
1872 * Purpose:
1873 * Release a reference on an inherit importance attribute value,
1874 * unlinking and deallocating the attribute if the last reference.
1875 * Conditions:
1876 * nothing locked on entrance, nothing locked on exit.
1877 * May block.
1878 */
1879 void
1880 ipc_importance_inherit_release(ipc_importance_inherit_t inherit)
1881 {
1882 if (III_NULL != inherit)
1883 ipc_importance_release(&inherit->iii_elem);
1884 }
1885 #endif /* 0 currently unused */
1886
1887 /*
1888 * Routine: ipc_importance_for_task
1889 * Purpose:
1890 * Create a reference for the specified task's base importance
1891 * element. If the base importance element doesn't exist, make it and
1892 * bind it to the active task. If the task is inactive, there isn't
1893 * any need to return a new reference.
1894 * Conditions:
1895 * If made is true, a "made" reference is returned (for donating to
1896 * the voucher system). Otherwise an internal reference is returned.
1897 *
1898 * Nothing locked on entry. May block.
1899 */
1900 ipc_importance_task_t
1901 ipc_importance_for_task(task_t task, boolean_t made)
1902 {
1903 ipc_importance_task_t task_elem;
1904 boolean_t first_pass = TRUE;
1905
1906 assert(TASK_NULL != task);
1907
1908 retry:
1909 /* No use returning anything for inactive task */
1910 if (!task->active)
1911 return IIT_NULL;
1912
1913 ipc_importance_lock();
1914 task_elem = task->task_imp_base;
1915 if (IIT_NULL != task_elem) {
1916 /* Add a made reference (borrowing active task ref to do it) */
1917 if (made) {
1918 if (0 == task_elem->iit_made++) {
1919 assert(IIT_REFS_MAX > IIT_REFS(task_elem));
1920 ipc_importance_task_reference_internal(task_elem);
1921 }
1922 } else {
1923 assert(IIT_REFS_MAX > IIT_REFS(task_elem));
1924 ipc_importance_task_reference_internal(task_elem);
1925 }
1926 ipc_importance_unlock();
1927 return task_elem;
1928 }
1929 ipc_importance_unlock();
1930
1931 if (!first_pass)
1932 return IIT_NULL;
1933 first_pass = FALSE;
1934
1935 /* Need to make one - may race with others (be prepared to drop) */
1936 task_elem = (ipc_importance_task_t)zalloc(ipc_importance_task_zone);
1937 if (IIT_NULL == task_elem)
1938 goto retry;
1939
1940 task_elem->iit_bits = IIE_TYPE_TASK | 2; /* one for task, one for return/made */
1941 task_elem->iit_made = (made) ? 1 : 0;
1942 task_elem->iit_task = task; /* take actual ref when we're sure */
1943 task_elem->iit_updateq = NULL;
1944 task_elem->iit_receiver = 0;
1945 task_elem->iit_denap = 0;
1946 task_elem->iit_donor = 0;
1947 task_elem->iit_live_donor = 0;
1948 task_elem->iit_updatepolicy = 0;
1949 task_elem->iit_reserved = 0;
1950 task_elem->iit_filelocks = 0;
1951 task_elem->iit_updatetime = 0;
1952 task_elem->iit_transitions = 0;
1953 task_elem->iit_assertcnt = 0;
1954 task_elem->iit_externcnt = 0;
1955 task_elem->iit_externdrop = 0;
1956 task_elem->iit_legacy_externcnt = 0;
1957 task_elem->iit_legacy_externdrop = 0;
1958 #if IIE_REF_DEBUG
1959 ipc_importance_counter_init(&task_elem->iit_elem);
1960 #endif
1961 queue_init(&task_elem->iit_kmsgs);
1962 queue_init(&task_elem->iit_inherits);
1963
1964 ipc_importance_lock();
1965 if (!task->active) {
1966 ipc_importance_unlock();
1967 zfree(ipc_importance_task_zone, task_elem);
1968 return IIT_NULL;
1969 }
1970
1971 /* did we lose the race? */
1972 if (IIT_NULL != task->task_imp_base) {
1973 ipc_importance_unlock();
1974 zfree(ipc_importance_task_zone, task_elem);
1975 goto retry;
1976 }
1977
1978 /* we won the race */
1979 task->task_imp_base = task_elem;
1980 task_reference(task);
1981 #if DEVELOPMENT || DEBUG
1982 queue_enter(&global_iit_alloc_queue, task_elem, ipc_importance_task_t, iit_allocation);
1983 task_importance_update_owner_info(task);
1984 #endif
1985 ipc_importance_unlock();
1986
1987 return task_elem;
1988 }
1989
1990 #if DEVELOPMENT || DEBUG
1991 void task_importance_update_owner_info(task_t task) {
1992
1993 if (task != TASK_NULL && task->task_imp_base != IIT_NULL) {
1994 ipc_importance_task_t task_elem = task->task_imp_base;
1995
1996 task_elem->iit_bsd_pid = task_pid(task);
1997 if (task->bsd_info) {
1998 strncpy(&task_elem->iit_procname[0], proc_name_address(task->bsd_info), 16);
1999 task_elem->iit_procname[16] = '\0';
2000 } else {
2001 strncpy(&task_elem->iit_procname[0], "unknown", 16);
2002 }
2003 }
2004 }
2005 #endif
2006
2007 /*
2008 * Routine: ipc_importance_reset_locked
2009 * Purpose:
2010 * Reset a task's IPC importance (the task is going away or exec'ing)
2011 *
2012 * Remove the donor bit and legacy externalized assertions from the
2013 * current task importance and see if that wipes out downstream donations.
2014 * Conditions:
2015 * importance lock held.
2016 */
2017
2018 static void
2019 ipc_importance_reset_locked(ipc_importance_task_t task_imp, boolean_t donor)
2020 {
2021 boolean_t before_donor, after_donor;
2022
2023 /* remove the donor bit, live-donor bit and externalized boosts */
2024 before_donor = ipc_importance_task_is_donor(task_imp);
2025 if (donor) {
2026 task_imp->iit_donor = 0;
2027 }
2028 assert(IIT_LEGACY_EXTERN(task_imp) <= IIT_EXTERN(task_imp));
2029 assert(task_imp->iit_legacy_externcnt <= task_imp->iit_externcnt);
2030 assert(task_imp->iit_legacy_externdrop <= task_imp->iit_externdrop);
2031 task_imp->iit_externcnt -= task_imp->iit_legacy_externcnt;
2032 task_imp->iit_externdrop -= task_imp->iit_legacy_externdrop;
2033
2034 /* assert(IIT_LEGACY_EXTERN(task_imp) <= task_imp->iit_assertcnt); */
2035 if (IIT_EXTERN(task_imp) < task_imp->iit_assertcnt) {
2036 task_imp->iit_assertcnt -= IIT_LEGACY_EXTERN(task_imp);
2037 } else {
2038 task_imp->iit_assertcnt = IIT_EXTERN(task_imp);
2039 }
2040 task_imp->iit_legacy_externcnt = 0;
2041 task_imp->iit_legacy_externdrop = 0;
2042 after_donor = ipc_importance_task_is_donor(task_imp);
2043
2044 #if DEVELOPMENT || DEBUG
2045 if (task_imp->iit_assertcnt > 0 && task_imp->iit_live_donor) {
2046 printf("Live donor task %s[%d] still has %d importance assertions after reset\n",
2047 task_imp->iit_procname, task_imp->iit_bsd_pid, task_imp->iit_assertcnt);
2048 }
2049 #endif
2050
2051 /* propagate a downstream drop if there was a change in donor status */
2052 if (after_donor != before_donor) {
2053 ipc_importance_task_propagate_assertion_locked(task_imp, IIT_UPDATE_DROP, FALSE);
2054 }
2055 }
2056
2057 /*
2058 * Routine: ipc_importance_reset
2059 * Purpose:
2060 * Reset a task's IPC importance
2061 *
2062 * The task is being reset, although staying around. Arrange to have the
2063 * external state of the task reset from the importance.
2064 * Conditions:
2065 * importance lock not held.
2066 */
2067
2068 void
2069 ipc_importance_reset(ipc_importance_task_t task_imp, boolean_t donor)
2070 {
2071 if (IIT_NULL == task_imp) {
2072 return;
2073 }
2074 ipc_importance_lock();
2075 ipc_importance_reset_locked(task_imp, donor);
2076 ipc_importance_unlock();
2077 }
2078
2079 /*
2080 * Routine: ipc_importance_disconnect_task
2081 * Purpose:
2082 * Disconnect a task from its importance.
2083 *
2084 * Clear the task pointer from the importance and drop the
2085 * reference the task held on the importance object. Before
2086 * doing that, reset the effects the current task holds on
2087 * the importance and see if that wipes out downstream donations.
2088 *
2089 * We allow the upstream boosts to continue to affect downstream
2090 * even though the local task is being effectively pulled from
2091 * the chain.
2092 * Conditions:
2093 * Nothing locked.
2094 */
2095 void
2096 ipc_importance_disconnect_task(task_t task)
2097 {
2098 ipc_importance_task_t task_imp;
2099
2100 task_lock(task);
2101 ipc_importance_lock();
2102 task_imp = task->task_imp_base;
2103
2104 /* did somebody beat us to it? */
2105 if (IIT_NULL == task_imp) {
2106 ipc_importance_unlock();
2107 task_unlock(task);
2108 return;
2109 }
2110
2111 /* disconnect the task from this importance */
2112 assert(task_imp->iit_task == task);
2113 task_imp->iit_task = TASK_NULL;
2114 task->task_imp_base = IIT_NULL;
2115 task_unlock(task);
2116
2117 /* reset the effects the current task hold on the importance */
2118 ipc_importance_reset_locked(task_imp, TRUE);
2119
2120 ipc_importance_task_release_locked(task_imp);
2121 /* importance unlocked */
2122
2123 /* deallocate the task now that the importance is unlocked */
2124 task_deallocate(task);
2125 }
2126
2127 /*
2128 * Routine: ipc_importance_exec_switch_task
2129 * Purpose:
2130 * Switch importance task base from old task to new task in exec.
2131 *
2132 * Create an ipc importance linkage from old task to new task,
2133 * once the linkage is created, switch the importance task base
2134 * from old task to new task. After the switch, the linkage will
2135 * represent importance linkage from new task to old task with
2136 * watch port importance inheritance linked to new task.
2137 * Conditions:
2138 * Nothing locked.
2139 * Returns a reference on importance inherit.
2140 */
2141 ipc_importance_inherit_t
2142 ipc_importance_exec_switch_task(
2143 task_t old_task,
2144 task_t new_task)
2145 {
2146 ipc_importance_inherit_t inherit = III_NULL;
2147 ipc_importance_task_t old_task_imp = IIT_NULL;
2148 ipc_importance_task_t new_task_imp = IIT_NULL;
2149
2150 task_importance_reset(old_task);
2151
2152 /* Create an importance linkage from old_task to new_task */
2153 inherit = ipc_importance_inherit_from_task(old_task, new_task);
2154 if (inherit == III_NULL) {
2155 return inherit;
2156 }
2157
2158 /* Switch task importance base from old task to new task */
2159 ipc_importance_lock();
2160
2161 old_task_imp = old_task->task_imp_base;
2162 new_task_imp = new_task->task_imp_base;
2163
2164 old_task_imp->iit_task = new_task;
2165 new_task_imp->iit_task = old_task;
2166
2167 old_task->task_imp_base = new_task_imp;
2168 new_task->task_imp_base = old_task_imp;
2169
2170 #if DEVELOPMENT || DEBUG
2171 /*
2172 * Update the pid an proc name for importance base if any
2173 */
2174 task_importance_update_owner_info(new_task);
2175 #endif
2176 ipc_importance_unlock();
2177
2178 return inherit;
2179 }
2180
2181 /*
2182 * Routine: ipc_importance_check_circularity
2183 * Purpose:
2184 * Check if queueing "port" in a message for "dest"
2185 * would create a circular group of ports and messages.
2186 *
2187 * If no circularity (FALSE returned), then "port"
2188 * is changed from "in limbo" to "in transit".
2189 *
2190 * That is, we want to set port->ip_destination == dest,
2191 * but guaranteeing that this doesn't create a circle
2192 * port->ip_destination->ip_destination->... == port
2193 *
2194 * Additionally, if port was successfully changed to "in transit",
2195 * propagate boost assertions from the "in limbo" port to all
2196 * the ports in the chain, and, if the destination task accepts
2197 * boosts, to the destination task.
2198 *
2199 * Conditions:
2200 * No ports locked. References held for "port" and "dest".
2201 */
2202
2203 boolean_t
2204 ipc_importance_check_circularity(
2205 ipc_port_t port,
2206 ipc_port_t dest)
2207 {
2208 ipc_importance_task_t imp_task = IIT_NULL;
2209 ipc_importance_task_t release_imp_task = IIT_NULL;
2210 boolean_t imp_lock_held = FALSE;
2211 int assertcnt = 0;
2212 ipc_port_t base;
2213
2214 assert(port != IP_NULL);
2215 assert(dest != IP_NULL);
2216
2217 if (port == dest)
2218 return TRUE;
2219 base = dest;
2220
2221 /* port is in limbo, so donation status is safe to latch */
2222 if (port->ip_impdonation != 0) {
2223 imp_lock_held = TRUE;
2224 ipc_importance_lock();
2225 }
2226
2227 /*
2228 * First try a quick check that can run in parallel.
2229 * No circularity if dest is not in transit.
2230 */
2231 ip_lock(port);
2232
2233 /*
2234 * Even if port is just carrying assertions for others,
2235 * we need the importance lock.
2236 */
2237 if (port->ip_impcount > 0 && !imp_lock_held) {
2238 if (!ipc_importance_lock_try()) {
2239 ip_unlock(port);
2240 ipc_importance_lock();
2241 ip_lock(port);
2242 }
2243 imp_lock_held = TRUE;
2244 }
2245
2246 if (ip_lock_try(dest)) {
2247 if (!ip_active(dest) ||
2248 (dest->ip_receiver_name != MACH_PORT_NULL) ||
2249 (dest->ip_destination == IP_NULL))
2250 goto not_circular;
2251
2252 /* dest is in transit; further checking necessary */
2253
2254 ip_unlock(dest);
2255 }
2256 ip_unlock(port);
2257
2258 /*
2259 * We're about to pay the cost to serialize,
2260 * just go ahead and grab importance lock.
2261 */
2262 if (!imp_lock_held) {
2263 ipc_importance_lock();
2264 imp_lock_held = TRUE;
2265 }
2266
2267 ipc_port_multiple_lock(); /* massive serialization */
2268
2269 /*
2270 * Search for the end of the chain (a port not in transit),
2271 * acquiring locks along the way.
2272 */
2273
2274 for (;;) {
2275 ip_lock(base);
2276
2277 if (!ip_active(base) ||
2278 (base->ip_receiver_name != MACH_PORT_NULL) ||
2279 (base->ip_destination == IP_NULL))
2280 break;
2281
2282 base = base->ip_destination;
2283 }
2284
2285 /* all ports in chain from dest to base, inclusive, are locked */
2286
2287 if (port == base) {
2288 /* circularity detected! */
2289
2290 ipc_port_multiple_unlock();
2291
2292 /* port (== base) is in limbo */
2293
2294 assert(ip_active(port));
2295 assert(port->ip_receiver_name == MACH_PORT_NULL);
2296 assert(port->ip_destination == IP_NULL);
2297
2298 while (dest != IP_NULL) {
2299 ipc_port_t next;
2300
2301 /* dest is in transit or in limbo */
2302
2303 assert(ip_active(dest));
2304 assert(dest->ip_receiver_name == MACH_PORT_NULL);
2305
2306 next = dest->ip_destination;
2307 ip_unlock(dest);
2308 dest = next;
2309 }
2310
2311 if (imp_lock_held)
2312 ipc_importance_unlock();
2313
2314 return TRUE;
2315 }
2316
2317 /*
2318 * The guarantee: lock port while the entire chain is locked.
2319 * Once port is locked, we can take a reference to dest,
2320 * add port to the chain, and unlock everything.
2321 */
2322
2323 ip_lock(port);
2324 ipc_port_multiple_unlock();
2325
2326 not_circular:
2327
2328 /* port is in limbo */
2329
2330 assert(ip_active(port));
2331 assert(port->ip_receiver_name == MACH_PORT_NULL);
2332 assert(port->ip_destination == IP_NULL);
2333
2334 ip_reference(dest);
2335 port->ip_destination = dest;
2336
2337 /* must have been in limbo or still bound to a task */
2338 assert(port->ip_tempowner != 0);
2339
2340 /*
2341 * We delayed dropping assertions from a specific task.
2342 * Cache that info now (we'll drop assertions and the
2343 * task reference below).
2344 */
2345 release_imp_task = port->ip_imp_task;
2346 if (IIT_NULL != release_imp_task) {
2347 port->ip_imp_task = IIT_NULL;
2348 }
2349 assertcnt = port->ip_impcount;
2350
2351 /* take the port out of limbo w.r.t. assertions */
2352 port->ip_tempowner = 0;
2353
2354 /* now unlock chain */
2355
2356 ip_unlock(port);
2357
2358 for (;;) {
2359
2360 /* every port along chain track assertions behind it */
2361 ipc_port_impcount_delta(dest, assertcnt, base);
2362
2363 if (dest == base)
2364 break;
2365
2366 /* port is in transit */
2367
2368 assert(ip_active(dest));
2369 assert(dest->ip_receiver_name == MACH_PORT_NULL);
2370 assert(dest->ip_destination != IP_NULL);
2371 assert(dest->ip_tempowner == 0);
2372
2373 port = dest->ip_destination;
2374 ip_unlock(dest);
2375 dest = port;
2376 }
2377
2378 /* base is not in transit */
2379 assert(!ip_active(base) ||
2380 (base->ip_receiver_name != MACH_PORT_NULL) ||
2381 (base->ip_destination == IP_NULL));
2382
2383 /*
2384 * Find the task to boost (if any).
2385 * We will boost "through" ports that don't know
2386 * about inheritance to deliver receive rights that
2387 * do.
2388 */
2389 if (ip_active(base) && (assertcnt > 0)) {
2390 assert(imp_lock_held);
2391 if (base->ip_tempowner != 0) {
2392 if (IIT_NULL != base->ip_imp_task) {
2393 /* specified tempowner task */
2394 imp_task = base->ip_imp_task;
2395 assert(ipc_importance_task_is_any_receiver_type(imp_task));
2396 }
2397 /* otherwise don't boost current task */
2398
2399 } else if (base->ip_receiver_name != MACH_PORT_NULL) {
2400 ipc_space_t space = base->ip_receiver;
2401
2402 /* only spaces with boost-accepting tasks */
2403 if (space->is_task != TASK_NULL &&
2404 ipc_importance_task_is_any_receiver_type(space->is_task->task_imp_base))
2405 imp_task = space->is_task->task_imp_base;
2406 }
2407
2408 /* take reference before unlocking base */
2409 if (imp_task != IIT_NULL) {
2410 ipc_importance_task_reference(imp_task);
2411 }
2412 }
2413
2414 ip_unlock(base);
2415
2416 /*
2417 * Transfer assertions now that the ports are unlocked.
2418 * Avoid extra overhead if transferring to/from the same task.
2419 *
2420 * NOTE: If a transfer is occurring, the new assertions will
2421 * be added to imp_task BEFORE the importance lock is unlocked.
2422 * This is critical - to avoid decrements coming from the kmsgs
2423 * beating the increment to the task.
2424 */
2425 boolean_t transfer_assertions = (imp_task != release_imp_task);
2426
2427 if (imp_task != IIT_NULL) {
2428 assert(imp_lock_held);
2429 if (transfer_assertions)
2430 ipc_importance_task_hold_internal_assertion_locked(imp_task, assertcnt);
2431 }
2432
2433 if (release_imp_task != IIT_NULL) {
2434 assert(imp_lock_held);
2435 if (transfer_assertions)
2436 ipc_importance_task_drop_internal_assertion_locked(release_imp_task, assertcnt);
2437 }
2438
2439 if (imp_lock_held)
2440 ipc_importance_unlock();
2441
2442 if (imp_task != IIT_NULL)
2443 ipc_importance_task_release(imp_task);
2444
2445 if (release_imp_task != IIT_NULL)
2446 ipc_importance_task_release(release_imp_task);
2447
2448 return FALSE;
2449 }
2450
2451 /*
2452 * Routine: ipc_importance_send
2453 * Purpose:
2454 * Post the importance voucher attribute [if sent] or a static
2455 * importance boost depending upon options and conditions.
2456 * Conditions:
2457 * Destination port locked on entry and exit, may be dropped during the call.
2458 * Returns:
2459 * A boolean identifying if the port lock was tempoarily dropped.
2460 */
2461 boolean_t
2462 ipc_importance_send(
2463 ipc_kmsg_t kmsg,
2464 mach_msg_option_t option)
2465 {
2466 ipc_port_t port = (ipc_port_t) kmsg->ikm_header->msgh_remote_port;
2467 boolean_t port_lock_dropped = FALSE;
2468 ipc_importance_elem_t elem;
2469 task_t task;
2470 ipc_importance_task_t task_imp;
2471 kern_return_t kr;
2472
2473 assert(IP_VALID(port));
2474
2475 /* If no donation to be made, return quickly */
2476 if ((port->ip_impdonation == 0) ||
2477 (option & MACH_SEND_NOIMPORTANCE) != 0) {
2478 return port_lock_dropped;
2479 }
2480
2481 task = current_task();
2482
2483 /* If forced sending a static boost, go update the port */
2484 if ((option & MACH_SEND_IMPORTANCE) != 0) {
2485 kmsg->ikm_header->msgh_bits |= MACH_MSGH_BITS_RAISEIMP;
2486 goto portupdate;
2487 }
2488
2489 task_imp = task->task_imp_base;
2490 assert(IIT_NULL != task_imp);
2491
2492 /* If the sender can never donate importance, nothing to do */
2493 if (ipc_importance_task_is_never_donor(task_imp)) {
2494 return port_lock_dropped;
2495 }
2496
2497 elem = IIE_NULL;
2498
2499 /* If importance receiver and passing a voucher, look for importance in there */
2500 if (IP_VALID(kmsg->ikm_voucher) &&
2501 ipc_importance_task_is_marked_receiver(task_imp)) {
2502 mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
2503 mach_voucher_attr_value_handle_array_size_t val_count;
2504 ipc_voucher_t voucher;
2505
2506 assert(ip_kotype(kmsg->ikm_voucher) == IKOT_VOUCHER);
2507 voucher = (ipc_voucher_t)kmsg->ikm_voucher->ip_kobject;
2508
2509 /* check to see if the voucher has an importance attribute */
2510 val_count = MACH_VOUCHER_ATTR_VALUE_MAX_NESTED;
2511 kr = mach_voucher_attr_control_get_values(ipc_importance_control, voucher,
2512 vals, &val_count);
2513 assert(KERN_SUCCESS == kr);
2514
2515 /*
2516 * Only use importance associated with our task (either directly
2517 * or through an inherit that donates to our task).
2518 */
2519 if (0 < val_count) {
2520 ipc_importance_elem_t check_elem;
2521
2522 check_elem = (ipc_importance_elem_t)vals[0];
2523 assert(IIE_NULL != check_elem);
2524 if (IIE_TYPE_INHERIT == IIE_TYPE(check_elem)) {
2525 ipc_importance_inherit_t inherit;
2526 inherit = (ipc_importance_inherit_t) check_elem;
2527 if (inherit->iii_to_task == task_imp) {
2528 elem = check_elem;
2529 }
2530 } else if (check_elem == (ipc_importance_elem_t)task_imp) {
2531 elem = check_elem;
2532 }
2533 }
2534 }
2535
2536 /* If we haven't found an importance attribute to send yet, use the task's */
2537 if (IIE_NULL == elem) {
2538 elem = (ipc_importance_elem_t)task_imp;
2539 }
2540
2541 /* take a reference for the message to hold */
2542 ipc_importance_reference_internal(elem);
2543
2544 /* acquire the importance lock while trying to hang on to port lock */
2545 if (!ipc_importance_lock_try()) {
2546 port_lock_dropped = TRUE;
2547 ip_unlock(port);
2548 ipc_importance_lock();
2549 }
2550
2551 /* link kmsg onto the donor element propagation chain */
2552 ipc_importance_kmsg_link(kmsg, elem);
2553 /* elem reference transfered to kmsg */
2554
2555 incr_ref_counter(elem->iie_kmsg_refs_added);
2556
2557 /* If the sender isn't currently a donor, no need to apply boost */
2558 if (!ipc_importance_task_is_donor(task_imp)) {
2559 ipc_importance_unlock();
2560
2561 /* re-acquire port lock, if needed */
2562 if (TRUE == port_lock_dropped)
2563 ip_lock(port);
2564
2565 return port_lock_dropped;
2566 }
2567
2568 /* Mark the fact that we are (currently) donating through this message */
2569 kmsg->ikm_header->msgh_bits |= MACH_MSGH_BITS_RAISEIMP;
2570
2571 /*
2572 * If we need to relock the port, do it with the importance still locked.
2573 * This assures we get to add the importance boost through the port to
2574 * the task BEFORE anyone else can attempt to undo that operation if
2575 * the sender lost donor status.
2576 */
2577 if (TRUE == port_lock_dropped) {
2578 ip_lock(port);
2579 }
2580
2581 portupdate:
2582
2583 #if IMPORTANCE_DEBUG
2584 if (kdebug_enable) {
2585 mach_msg_max_trailer_t *dbgtrailer = (mach_msg_max_trailer_t *)
2586 ((vm_offset_t)kmsg->ikm_header + round_msg(kmsg->ikm_header->msgh_size));
2587 unsigned int sender_pid = dbgtrailer->msgh_audit.val[5];
2588 mach_msg_id_t imp_msgh_id = kmsg->ikm_header->msgh_id;
2589 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_MSG, IMP_MSG_SEND)) | DBG_FUNC_START,
2590 task_pid(task), sender_pid, imp_msgh_id, 0, 0);
2591 }
2592 #endif /* IMPORTANCE_DEBUG */
2593
2594 mach_port_delta_t delta = 1;
2595 boolean_t need_port_lock;
2596 task_imp = IIT_NULL;
2597
2598 /* adjust port boost count (with importance and port locked) */
2599 need_port_lock = ipc_port_importance_delta_internal(port, IPID_OPTION_NORMAL, &delta, &task_imp);
2600
2601 /* if we need to adjust a task importance as a result, apply that here */
2602 if (IIT_NULL != task_imp && delta != 0) {
2603 assert(delta == 1);
2604
2605 /* if this results in a change of state, propagate the transistion */
2606 if (ipc_importance_task_check_transition(task_imp, IIT_UPDATE_HOLD, delta)) {
2607
2608 /* can't hold the port lock during task transition(s) */
2609 if (!need_port_lock) {
2610 need_port_lock = TRUE;
2611 ip_unlock(port);
2612 }
2613 ipc_importance_task_propagate_assertion_locked(task_imp, IIT_UPDATE_HOLD, TRUE);
2614 }
2615 }
2616
2617 ipc_importance_unlock();
2618
2619 if (need_port_lock) {
2620 port_lock_dropped = TRUE;
2621 ip_lock(port);
2622 }
2623
2624 return port_lock_dropped;
2625 }
2626
2627 /*
2628 * Routine: ipc_importance_inherit_from_kmsg
2629 * Purpose:
2630 * Create a "made" reference for an importance attribute representing
2631 * an inheritance between the sender of a message (if linked) and the
2632 * current task importance. If the message is not linked, a static
2633 * boost may be created, based on the boost state of the message.
2634 *
2635 * Any transfer from kmsg linkage to inherit linkage must be atomic.
2636 *
2637 * If the task is inactive, there isn't any need to return a new reference.
2638 * Conditions:
2639 * Nothing locked on entry. May block.
2640 */
2641 static ipc_importance_inherit_t
2642 ipc_importance_inherit_from_kmsg(ipc_kmsg_t kmsg)
2643 {
2644 ipc_importance_task_t task_imp = IIT_NULL;
2645 ipc_importance_elem_t from_elem = kmsg->ikm_importance;
2646 ipc_importance_elem_t elem;
2647 task_t task_self = current_task();
2648
2649 ipc_port_t port = kmsg->ikm_header->msgh_remote_port;
2650 ipc_importance_inherit_t inherit = III_NULL;
2651 ipc_importance_inherit_t alloc = III_NULL;
2652 boolean_t cleared_self_donation = FALSE;
2653 boolean_t donating;
2654 uint32_t depth = 1;
2655
2656 /* The kmsg must have an importance donor or static boost to proceed */
2657 if (IIE_NULL == kmsg->ikm_importance &&
2658 !MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits)) {
2659 return III_NULL;
2660 }
2661
2662 /*
2663 * No need to set up an inherit linkage if the dest isn't a receiver
2664 * of one type or the other.
2665 */
2666 if (!ipc_importance_task_is_any_receiver_type(task_self->task_imp_base)) {
2667 ipc_importance_lock();
2668 goto out_locked;
2669 }
2670
2671 /* Grab a reference on the importance of the destination */
2672 task_imp = ipc_importance_for_task(task_self, FALSE);
2673
2674 ipc_importance_lock();
2675
2676 if (IIT_NULL == task_imp) {
2677 goto out_locked;
2678 }
2679
2680 incr_ref_counter(task_imp->iit_elem.iie_task_refs_added_inherit_from);
2681
2682 /* If message is already associated with an inherit... */
2683 if (IIE_TYPE_INHERIT == IIE_TYPE(from_elem)) {
2684 ipc_importance_inherit_t from_inherit = (ipc_importance_inherit_t)from_elem;
2685
2686 /* already targeting our task? - just use it */
2687 if (from_inherit->iii_to_task == task_imp) {
2688 /* clear self-donation if not also present in inherit */
2689 if (!from_inherit->iii_donating &&
2690 MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits)) {
2691 kmsg->ikm_header->msgh_bits &= ~MACH_MSGH_BITS_RAISEIMP;
2692 cleared_self_donation = TRUE;
2693 }
2694 inherit = from_inherit;
2695
2696 } else if (III_DEPTH_MAX == III_DEPTH(from_inherit)) {
2697 ipc_importance_task_t to_task;
2698 ipc_importance_elem_t unlinked_from;
2699
2700 /*
2701 * Chain too long. Switch to looking
2702 * directly at the from_inherit's to-task
2703 * as our source of importance.
2704 */
2705 to_task = from_inherit->iii_to_task;
2706 ipc_importance_task_reference(to_task);
2707 from_elem = (ipc_importance_elem_t)to_task;
2708 depth = III_DEPTH_RESET | 1;
2709
2710 /* Fixup the kmsg linkage to reflect change */
2711 unlinked_from = ipc_importance_kmsg_unlink(kmsg);
2712 assert(unlinked_from == (ipc_importance_elem_t)from_inherit);
2713 ipc_importance_kmsg_link(kmsg, from_elem);
2714 ipc_importance_inherit_release_locked(from_inherit);
2715 /* importance unlocked */
2716 ipc_importance_lock();
2717
2718 } else {
2719 /* inheriting from an inherit */
2720 depth = from_inherit->iii_depth + 1;
2721 }
2722 }
2723
2724 /*
2725 * Don't allow a task to inherit from itself (would keep it permanently
2726 * boosted even if all other donors to the task went away).
2727 */
2728
2729 if (from_elem == (ipc_importance_elem_t)task_imp) {
2730 goto out_locked;
2731 }
2732
2733 /*
2734 * But if the message isn't associated with any linked source, it is
2735 * intended to be permanently boosting (static boost from kernel).
2736 * In that case DO let the process permanently boost itself.
2737 */
2738 if (IIE_NULL == from_elem) {
2739 assert(MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits));
2740 ipc_importance_task_reference_internal(task_imp);
2741 from_elem = (ipc_importance_elem_t)task_imp;
2742 }
2743
2744 /*
2745 * Now that we have the from_elem figured out,
2746 * check to see if we already have an inherit for this pairing
2747 */
2748 while (III_NULL == inherit) {
2749 inherit = ipc_importance_inherit_find(from_elem, task_imp, depth);
2750
2751 /* Do we have to allocate a new inherit */
2752 if (III_NULL == inherit) {
2753 if (III_NULL != alloc) {
2754 break;
2755 }
2756
2757 /* allocate space */
2758 ipc_importance_unlock();
2759 alloc = (ipc_importance_inherit_t)
2760 zalloc(ipc_importance_inherit_zone);
2761 ipc_importance_lock();
2762 }
2763 }
2764
2765 /* snapshot the donating status while we have importance locked */
2766 donating = MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits);
2767
2768 if (III_NULL != inherit) {
2769 /* We found one, piggyback on that */
2770 assert(0 < III_REFS(inherit));
2771 assert(0 < IIE_REFS(inherit->iii_from_elem));
2772 assert(inherit->iii_externcnt >= inherit->iii_made);
2773
2774 /* add in a made reference */
2775 if (0 == inherit->iii_made++) {
2776 assert(III_REFS_MAX > III_REFS(inherit));
2777 ipc_importance_inherit_reference_internal(inherit);
2778 }
2779
2780 /* Reflect the inherit's change of status into the task boosts */
2781 if (0 == III_EXTERN(inherit)) {
2782 assert(!inherit->iii_donating);
2783 inherit->iii_donating = donating;
2784 if (donating) {
2785 task_imp->iit_externcnt += inherit->iii_externcnt;
2786 task_imp->iit_externdrop += inherit->iii_externdrop;
2787 }
2788 } else {
2789 assert(donating == inherit->iii_donating);
2790 }
2791
2792 /* add in a external reference for this use of the inherit */
2793 inherit->iii_externcnt++;
2794 } else {
2795 /* initialize the previously allocated space */
2796 inherit = alloc;
2797 inherit->iii_bits = IIE_TYPE_INHERIT | 1;
2798 inherit->iii_made = 1;
2799 inherit->iii_externcnt = 1;
2800 inherit->iii_externdrop = 0;
2801 inherit->iii_depth = depth;
2802 inherit->iii_to_task = task_imp;
2803 inherit->iii_from_elem = IIE_NULL;
2804 queue_init(&inherit->iii_kmsgs);
2805
2806 if (donating) {
2807 inherit->iii_donating = TRUE;
2808 } else {
2809 inherit->iii_donating = FALSE;
2810 }
2811
2812 /*
2813 * Chain our new inherit on the element it inherits from.
2814 * The new inherit takes our reference on from_elem.
2815 */
2816 ipc_importance_inherit_link(inherit, from_elem);
2817
2818 #if IIE_REF_DEBUG
2819 ipc_importance_counter_init(&inherit->iii_elem);
2820 from_elem->iie_kmsg_refs_inherited++;
2821 task_imp->iit_elem.iie_task_refs_inherited++;
2822 #endif
2823 }
2824
2825 out_locked:
2826 /*
2827 * for those paths that came straight here: snapshot the donating status
2828 * (this should match previous snapshot for other paths).
2829 */
2830 donating = MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits);
2831
2832 /* unlink the kmsg inheritance (if any) */
2833 elem = ipc_importance_kmsg_unlink(kmsg);
2834 assert(elem == from_elem);
2835
2836 /* If found inherit and donating, reflect that in the task externcnt */
2837 if (III_NULL != inherit && donating) {
2838 task_imp->iit_externcnt++;
2839 /* The owner of receive right might have changed, take the internal assertion */
2840 ipc_importance_task_hold_internal_assertion_locked(task_imp, 1);
2841 /* may have dropped and retaken importance lock */
2842 }
2843
2844 /* If we didn't create a new inherit, we have some resources to release */
2845 if (III_NULL == inherit || inherit != alloc) {
2846 if (IIE_NULL != from_elem) {
2847 if (III_NULL != inherit) {
2848 incr_ref_counter(from_elem->iie_kmsg_refs_coalesced);
2849 } else {
2850 incr_ref_counter(from_elem->iie_kmsg_refs_dropped);
2851 }
2852 ipc_importance_release_locked(from_elem);
2853 /* importance unlocked */
2854 } else {
2855 ipc_importance_unlock();
2856 }
2857
2858 if (IIT_NULL != task_imp) {
2859 if (III_NULL != inherit) {
2860 incr_ref_counter(task_imp->iit_elem.iie_task_refs_coalesced);
2861 }
2862 ipc_importance_task_release(task_imp);
2863 }
2864
2865 if (III_NULL != alloc)
2866 zfree(ipc_importance_inherit_zone, alloc);
2867 } else {
2868 /* from_elem and task_imp references transferred to new inherit */
2869 ipc_importance_unlock();
2870 }
2871
2872 /*
2873 * decrement port boost count
2874 * This is OK to do without the importance lock as we atomically
2875 * unlinked the kmsg and snapshot the donating state while holding
2876 * the importance lock
2877 */
2878 if (donating || cleared_self_donation) {
2879 ip_lock(port);
2880 /* drop importance from port and destination task */
2881 if (ipc_port_importance_delta(port, IPID_OPTION_NORMAL, -1) == FALSE) {
2882 ip_unlock(port);
2883 }
2884 }
2885
2886 if (III_NULL != inherit) {
2887 /* have an associated importance attr, even if currently not donating */
2888 kmsg->ikm_header->msgh_bits |= MACH_MSGH_BITS_RAISEIMP;
2889 } else {
2890 /* we won't have an importance attribute associated with our message */
2891 kmsg->ikm_header->msgh_bits &= ~MACH_MSGH_BITS_RAISEIMP;
2892 }
2893
2894 return inherit;
2895 }
2896
2897 /*
2898 * Routine: ipc_importance_inherit_from_task
2899 * Purpose:
2900 * Create a reference for an importance attribute representing
2901 * an inheritance between the to_task and from_task. The iii
2902 * created will be marked as III_FLAGS_FOR_OTHERS.
2903 *
2904 * It will not dedup any iii which are not marked as III_FLAGS_FOR_OTHERS.
2905 *
2906 * If the task is inactive, there isn't any need to return a new reference.
2907 * Conditions:
2908 * Nothing locked on entry. May block.
2909 * It should not be called from voucher subsystem.
2910 */
2911 static ipc_importance_inherit_t
2912 ipc_importance_inherit_from_task(
2913 task_t from_task,
2914 task_t to_task)
2915 {
2916 ipc_importance_task_t to_task_imp = IIT_NULL;
2917 ipc_importance_task_t from_task_imp = IIT_NULL;
2918 ipc_importance_elem_t from_elem = IIE_NULL;
2919
2920 ipc_importance_inherit_t inherit = III_NULL;
2921 ipc_importance_inherit_t alloc = III_NULL;
2922 boolean_t donating;
2923 uint32_t depth = 1;
2924
2925 to_task_imp = ipc_importance_for_task(to_task, FALSE);
2926 from_task_imp = ipc_importance_for_task(from_task, FALSE);
2927 from_elem = (ipc_importance_elem_t)from_task_imp;
2928
2929 ipc_importance_lock();
2930
2931 if (IIT_NULL == to_task_imp || IIT_NULL == from_task_imp) {
2932 goto out_locked;
2933 }
2934
2935 /*
2936 * No need to set up an inherit linkage if the to_task or from_task
2937 * isn't a receiver of one type or the other.
2938 */
2939 if (!ipc_importance_task_is_any_receiver_type(to_task_imp) ||
2940 !ipc_importance_task_is_any_receiver_type(from_task_imp)) {
2941 goto out_locked;
2942 }
2943
2944 /* Do not allow to create a linkage to self */
2945 if (to_task_imp == from_task_imp) {
2946 goto out_locked;
2947 }
2948
2949 incr_ref_counter(to_task_imp->iit_elem.iie_task_refs_added_inherit_from);
2950 incr_ref_counter(from_elem->iie_kmsg_refs_added);
2951
2952 /*
2953 * Now that we have the from_elem figured out,
2954 * check to see if we already have an inherit for this pairing
2955 */
2956 while (III_NULL == inherit) {
2957 inherit = ipc_importance_inherit_find(from_elem, to_task_imp, depth);
2958
2959 /* Do we have to allocate a new inherit */
2960 if (III_NULL == inherit) {
2961 if (III_NULL != alloc) {
2962 break;
2963 }
2964
2965 /* allocate space */
2966 ipc_importance_unlock();
2967 alloc = (ipc_importance_inherit_t)
2968 zalloc(ipc_importance_inherit_zone);
2969 ipc_importance_lock();
2970 }
2971 }
2972
2973 /* snapshot the donating status while we have importance locked */
2974 donating = ipc_importance_task_is_donor(from_task_imp);
2975
2976 if (III_NULL != inherit) {
2977 /* We found one, piggyback on that */
2978 assert(0 < III_REFS(inherit));
2979 assert(0 < IIE_REFS(inherit->iii_from_elem));
2980
2981 /* Take a reference for inherit */
2982 assert(III_REFS_MAX > III_REFS(inherit));
2983 ipc_importance_inherit_reference_internal(inherit);
2984
2985 /* Reflect the inherit's change of status into the task boosts */
2986 if (0 == III_EXTERN(inherit)) {
2987 assert(!inherit->iii_donating);
2988 inherit->iii_donating = donating;
2989 if (donating) {
2990 to_task_imp->iit_externcnt += inherit->iii_externcnt;
2991 to_task_imp->iit_externdrop += inherit->iii_externdrop;
2992 }
2993 } else {
2994 assert(donating == inherit->iii_donating);
2995 }
2996
2997 /* add in a external reference for this use of the inherit */
2998 inherit->iii_externcnt++;
2999 } else {
3000 /* initialize the previously allocated space */
3001 inherit = alloc;
3002 inherit->iii_bits = IIE_TYPE_INHERIT | 1;
3003 inherit->iii_made = 0;
3004 inherit->iii_externcnt = 1;
3005 inherit->iii_externdrop = 0;
3006 inherit->iii_depth = depth;
3007 inherit->iii_to_task = to_task_imp;
3008 inherit->iii_from_elem = IIE_NULL;
3009 queue_init(&inherit->iii_kmsgs);
3010
3011 if (donating) {
3012 inherit->iii_donating = TRUE;
3013 } else {
3014 inherit->iii_donating = FALSE;
3015 }
3016
3017 /*
3018 * Chain our new inherit on the element it inherits from.
3019 * The new inherit takes our reference on from_elem.
3020 */
3021 ipc_importance_inherit_link(inherit, from_elem);
3022
3023 #if IIE_REF_DEBUG
3024 ipc_importance_counter_init(&inherit->iii_elem);
3025 from_elem->iie_kmsg_refs_inherited++;
3026 task_imp->iit_elem.iie_task_refs_inherited++;
3027 #endif
3028 }
3029
3030 out_locked:
3031
3032 /* If found inherit and donating, reflect that in the task externcnt */
3033 if (III_NULL != inherit && donating) {
3034 to_task_imp->iit_externcnt++;
3035 /* take the internal assertion */
3036 ipc_importance_task_hold_internal_assertion_locked(to_task_imp, 1);
3037 /* may have dropped and retaken importance lock */
3038 }
3039
3040 /* If we didn't create a new inherit, we have some resources to release */
3041 if (III_NULL == inherit || inherit != alloc) {
3042 if (IIE_NULL != from_elem) {
3043 if (III_NULL != inherit) {
3044 incr_ref_counter(from_elem->iie_kmsg_refs_coalesced);
3045 } else {
3046 incr_ref_counter(from_elem->iie_kmsg_refs_dropped);
3047 }
3048 ipc_importance_release_locked(from_elem);
3049 /* importance unlocked */
3050 } else {
3051 ipc_importance_unlock();
3052 }
3053
3054 if (IIT_NULL != to_task_imp) {
3055 if (III_NULL != inherit) {
3056 incr_ref_counter(to_task_imp->iit_elem.iie_task_refs_coalesced);
3057 }
3058 ipc_importance_task_release(to_task_imp);
3059 }
3060
3061 if (III_NULL != alloc) {
3062 zfree(ipc_importance_inherit_zone, alloc);
3063 }
3064 } else {
3065 /* from_elem and to_task_imp references transferred to new inherit */
3066 ipc_importance_unlock();
3067 }
3068
3069 return inherit;
3070 }
3071
3072 /*
3073 * Routine: ipc_importance_receive
3074 * Purpose:
3075 * Process importance attributes in a received message.
3076 *
3077 * If an importance voucher attribute was sent, transform
3078 * that into an attribute value reflecting the inheritance
3079 * from the sender to the receiver.
3080 *
3081 * If a static boost is received (or the voucher isn't on
3082 * a voucher-based boost), export a static boost.
3083 * Conditions:
3084 * Nothing locked.
3085 */
3086 void
3087 ipc_importance_receive(
3088 ipc_kmsg_t kmsg,
3089 mach_msg_option_t option)
3090 {
3091 unsigned int sender_pid = ((mach_msg_max_trailer_t *)
3092 ((vm_offset_t)kmsg->ikm_header +
3093 round_msg(kmsg->ikm_header->msgh_size)))->msgh_audit.val[5];
3094 task_t task_self = current_task();
3095 int impresult = -1;
3096
3097 /* convert to a voucher with an inherit importance attribute? */
3098 if ((option & MACH_RCV_VOUCHER) != 0) {
3099 uint8_t recipes[2 * sizeof(ipc_voucher_attr_recipe_data_t) +
3100 sizeof(mach_voucher_attr_value_handle_t)];
3101 ipc_voucher_attr_raw_recipe_array_size_t recipe_size = 0;
3102 ipc_voucher_attr_recipe_t recipe = (ipc_voucher_attr_recipe_t)recipes;
3103 ipc_voucher_t recv_voucher;
3104 mach_voucher_attr_value_handle_t handle;
3105 ipc_importance_inherit_t inherit;
3106 kern_return_t kr;
3107
3108 /* set up recipe to copy the old voucher */
3109 if (IP_VALID(kmsg->ikm_voucher)) {
3110 ipc_voucher_t sent_voucher = (ipc_voucher_t)kmsg->ikm_voucher->ip_kobject;
3111
3112 recipe->key = MACH_VOUCHER_ATTR_KEY_ALL;
3113 recipe->command = MACH_VOUCHER_ATTR_COPY;
3114 recipe->previous_voucher = sent_voucher;
3115 recipe->content_size = 0;
3116 recipe_size += sizeof(*recipe);
3117 }
3118
3119 /*
3120 * create an inheritance attribute from the kmsg (may be NULL)
3121 * transferring any boosts from the kmsg linkage through the
3122 * port directly to the new inheritance object.
3123 */
3124 inherit = ipc_importance_inherit_from_kmsg(kmsg);
3125 handle = (mach_voucher_attr_value_handle_t)inherit;
3126
3127 assert(IIE_NULL == kmsg->ikm_importance);
3128
3129 /*
3130 * Only create a new voucher if we have an inherit object
3131 * (from the ikm_importance field of the incoming message), OR
3132 * we have a valid incoming voucher. If we have neither of
3133 * these things then there is no need to create a new voucher.
3134 */
3135 if (IP_VALID(kmsg->ikm_voucher) || inherit != III_NULL) {
3136 /* replace the importance attribute with the handle we created */
3137 /* our made reference on the inherit is donated to the voucher */
3138 recipe = (ipc_voucher_attr_recipe_t)&recipes[recipe_size];
3139 recipe->key = MACH_VOUCHER_ATTR_KEY_IMPORTANCE;
3140 recipe->command = MACH_VOUCHER_ATTR_SET_VALUE_HANDLE;
3141 recipe->previous_voucher = IPC_VOUCHER_NULL;
3142 recipe->content_size = sizeof(mach_voucher_attr_value_handle_t);
3143 *(mach_voucher_attr_value_handle_t *)(void *)recipe->content = handle;
3144 recipe_size += sizeof(*recipe) + sizeof(mach_voucher_attr_value_handle_t);
3145
3146 kr = ipc_voucher_attr_control_create_mach_voucher(ipc_importance_control,
3147 recipes,
3148 recipe_size,
3149 &recv_voucher);
3150 assert(KERN_SUCCESS == kr);
3151
3152 /* swap the voucher port (and set voucher bits in case it didn't already exist) */
3153 kmsg->ikm_header->msgh_bits |= (MACH_MSG_TYPE_MOVE_SEND << 16);
3154 ipc_port_release_send(kmsg->ikm_voucher);
3155 kmsg->ikm_voucher = convert_voucher_to_port(recv_voucher);
3156 if (III_NULL != inherit)
3157 impresult = 2;
3158 }
3159 } else { /* Don't want a voucher */
3160
3161 /* got linked importance? have to drop */
3162 if (IIE_NULL != kmsg->ikm_importance) {
3163 ipc_importance_elem_t elem;
3164
3165 ipc_importance_lock();
3166 elem = ipc_importance_kmsg_unlink(kmsg);
3167 #if IIE_REF_DEBUG
3168 elem->iie_kmsg_refs_dropped++;
3169 #endif
3170 ipc_importance_release_locked(elem);
3171 /* importance unlocked */
3172 }
3173
3174 /* With kmsg unlinked, can safely examine message importance attribute. */
3175 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits)) {
3176 ipc_importance_task_t task_imp = task_self->task_imp_base;
3177 ipc_port_t port = kmsg->ikm_header->msgh_remote_port;
3178
3179 /* The owner of receive right might have changed, take the internal assertion */
3180 if (KERN_SUCCESS == ipc_importance_task_hold_internal_assertion(task_imp, 1)) {
3181 ipc_importance_task_externalize_legacy_assertion(task_imp, 1, sender_pid);
3182 impresult = 1;
3183 } else {
3184 /* The importance boost never applied to task (clear the bit) */
3185 kmsg->ikm_header->msgh_bits &= ~MACH_MSGH_BITS_RAISEIMP;
3186 impresult = 0;
3187 }
3188
3189 /* Drop the boost on the port and the owner of the receive right */
3190 ip_lock(port);
3191 if (ipc_port_importance_delta(port, IPID_OPTION_NORMAL, -1) == FALSE) {
3192 ip_unlock(port);
3193 }
3194 }
3195 }
3196
3197 #if IMPORTANCE_DEBUG
3198 if (-1 < impresult)
3199 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_MSG, IMP_MSG_DELV)) | DBG_FUNC_NONE,
3200 sender_pid, task_pid(task_self),
3201 kmsg->ikm_header->msgh_id, impresult, 0);
3202 if (impresult == 2){
3203 /*
3204 * This probe only covers new voucher-based path. Legacy importance
3205 * will trigger the probe in ipc_importance_task_externalize_assertion()
3206 * above and have impresult==1 here.
3207 */
3208 DTRACE_BOOST5(receive_boost, task_t, task_self, int, task_pid(task_self), int, sender_pid, int, 1, int, task_self->task_imp_base->iit_assertcnt);
3209 }
3210 #endif /* IMPORTANCE_DEBUG */
3211 }
3212
3213 /*
3214 * Routine: ipc_importance_unreceive
3215 * Purpose:
3216 * Undo receive of importance attributes in a message.
3217 *
3218 * Conditions:
3219 * Nothing locked.
3220 */
3221 void
3222 ipc_importance_unreceive(
3223 ipc_kmsg_t kmsg,
3224 mach_msg_option_t __unused option)
3225 {
3226 /* importance should already be in the voucher and out of the kmsg */
3227 assert(IIE_NULL == kmsg->ikm_importance);
3228
3229 /* See if there is a legacy boost to be dropped from receiver */
3230 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits)) {
3231 ipc_importance_task_t task_imp;
3232
3233 kmsg->ikm_header->msgh_bits &= ~MACH_MSGH_BITS_RAISEIMP;
3234 task_imp = current_task()->task_imp_base;
3235 if (!IP_VALID(kmsg->ikm_voucher) && IIT_NULL != task_imp) {
3236 ipc_importance_task_drop_legacy_external_assertion(task_imp, 1);
3237 }
3238 /*
3239 * ipc_kmsg_copyout_dest() will consume the voucher
3240 * and any contained importance.
3241 */
3242 }
3243 }
3244
3245 /*
3246 * Routine: ipc_importance_clean
3247 * Purpose:
3248 * Clean up importance state in a kmsg that is being cleaned.
3249 * Unlink the importance chain if one was set up, and drop
3250 * the reference this kmsg held on the donor. Then check to
3251 * if importance was carried to the port, and remove that if
3252 * needed.
3253 * Conditions:
3254 * Nothing locked.
3255 */
3256 void
3257 ipc_importance_clean(
3258 ipc_kmsg_t kmsg)
3259 {
3260 ipc_port_t port;
3261
3262 /* Is the kmsg still linked? If so, remove that first */
3263 if (IIE_NULL != kmsg->ikm_importance) {
3264 ipc_importance_elem_t elem;
3265
3266 ipc_importance_lock();
3267 elem = ipc_importance_kmsg_unlink(kmsg);
3268 assert(IIE_NULL != elem);
3269 ipc_importance_release_locked(elem);
3270 /* importance unlocked */
3271 }
3272
3273 /* See if there is a legacy importance boost to be dropped from port */
3274 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits)) {
3275 kmsg->ikm_header->msgh_bits &= ~MACH_MSGH_BITS_RAISEIMP;
3276 port = kmsg->ikm_header->msgh_remote_port;
3277 if (IP_VALID(port)) {
3278 ip_lock(port);
3279 /* inactive ports already had their importance boosts dropped */
3280 if (!ip_active(port) ||
3281 ipc_port_importance_delta(port, IPID_OPTION_NORMAL, -1) == FALSE) {
3282 ip_unlock(port);
3283 }
3284 }
3285 }
3286 }
3287
3288 void
3289 ipc_importance_assert_clean(__assert_only ipc_kmsg_t kmsg)
3290 {
3291 assert(IIE_NULL == kmsg->ikm_importance);
3292 assert(!MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits));
3293 }
3294
3295 /*
3296 * IPC Importance Attribute Manager definition
3297 */
3298
3299 static kern_return_t
3300 ipc_importance_release_value(
3301 ipc_voucher_attr_manager_t manager,
3302 mach_voucher_attr_key_t key,
3303 mach_voucher_attr_value_handle_t value,
3304 mach_voucher_attr_value_reference_t sync);
3305
3306 static kern_return_t
3307 ipc_importance_get_value(
3308 ipc_voucher_attr_manager_t manager,
3309 mach_voucher_attr_key_t key,
3310 mach_voucher_attr_recipe_command_t command,
3311 mach_voucher_attr_value_handle_array_t prev_values,
3312 mach_voucher_attr_value_handle_array_size_t prev_value_count,
3313 mach_voucher_attr_content_t content,
3314 mach_voucher_attr_content_size_t content_size,
3315 mach_voucher_attr_value_handle_t *out_value,
3316 mach_voucher_attr_value_flags_t *out_flags,
3317 ipc_voucher_t *out_value_voucher);
3318
3319 static kern_return_t
3320 ipc_importance_extract_content(
3321 ipc_voucher_attr_manager_t manager,
3322 mach_voucher_attr_key_t key,
3323 mach_voucher_attr_value_handle_array_t values,
3324 mach_voucher_attr_value_handle_array_size_t value_count,
3325 mach_voucher_attr_recipe_command_t *out_command,
3326 mach_voucher_attr_content_t out_content,
3327 mach_voucher_attr_content_size_t *in_out_content_size);
3328
3329 static kern_return_t
3330 ipc_importance_command(
3331 ipc_voucher_attr_manager_t manager,
3332 mach_voucher_attr_key_t key,
3333 mach_voucher_attr_value_handle_array_t values,
3334 mach_msg_type_number_t value_count,
3335 mach_voucher_attr_command_t command,
3336 mach_voucher_attr_content_t in_content,
3337 mach_voucher_attr_content_size_t in_content_size,
3338 mach_voucher_attr_content_t out_content,
3339 mach_voucher_attr_content_size_t *out_content_size);
3340
3341 static void
3342 ipc_importance_manager_release(
3343 ipc_voucher_attr_manager_t manager);
3344
3345 struct ipc_voucher_attr_manager ipc_importance_manager = {
3346 .ivam_release_value = ipc_importance_release_value,
3347 .ivam_get_value = ipc_importance_get_value,
3348 .ivam_extract_content = ipc_importance_extract_content,
3349 .ivam_command = ipc_importance_command,
3350 .ivam_release = ipc_importance_manager_release,
3351 .ivam_flags = IVAM_FLAGS_NONE,
3352 };
3353
3354 #define IMPORTANCE_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_IMPORTANCE == (key))
3355 #define IMPORTANCE_ASSERT_MANAGER(manager) assert(&ipc_importance_manager == (manager))
3356
3357 /*
3358 * Routine: ipc_importance_release_value [Voucher Attribute Manager Interface]
3359 * Purpose:
3360 * Release what the voucher system believes is the last "made" reference
3361 * on an importance attribute value handle. The sync parameter is used to
3362 * avoid races with new made references concurrently being returned to the
3363 * voucher system in other threads.
3364 * Conditions:
3365 * Nothing locked on entry. May block.
3366 */
3367 static kern_return_t
3368 ipc_importance_release_value(
3369 ipc_voucher_attr_manager_t __assert_only manager,
3370 mach_voucher_attr_key_t __assert_only key,
3371 mach_voucher_attr_value_handle_t value,
3372 mach_voucher_attr_value_reference_t sync)
3373 {
3374 ipc_importance_elem_t elem;
3375
3376 IMPORTANCE_ASSERT_MANAGER(manager);
3377 IMPORTANCE_ASSERT_KEY(key);
3378 assert(0 < sync);
3379
3380 elem = (ipc_importance_elem_t)value;
3381
3382 ipc_importance_lock();
3383
3384 /* Any oustanding made refs? */
3385 if (sync != elem->iie_made) {
3386 assert(sync < elem->iie_made);
3387 ipc_importance_unlock();
3388 return KERN_FAILURE;
3389 }
3390
3391 /* clear made */
3392 elem->iie_made = 0;
3393
3394 /*
3395 * If there are pending external boosts represented by this attribute,
3396 * drop them from the apropriate task
3397 */
3398 if (IIE_TYPE_INHERIT == IIE_TYPE(elem)) {
3399 ipc_importance_inherit_t inherit = (ipc_importance_inherit_t)elem;
3400
3401 assert(inherit->iii_externcnt >= inherit->iii_externdrop);
3402
3403 if (inherit->iii_donating) {
3404 ipc_importance_task_t imp_task = inherit->iii_to_task;
3405 uint32_t assertcnt = III_EXTERN(inherit);
3406
3407 assert(ipc_importance_task_is_any_receiver_type(imp_task));
3408 assert(imp_task->iit_externcnt >= inherit->iii_externcnt);
3409 assert(imp_task->iit_externdrop >= inherit->iii_externdrop);
3410 imp_task->iit_externcnt -= inherit->iii_externcnt;
3411 imp_task->iit_externdrop -= inherit->iii_externdrop;
3412 inherit->iii_externcnt = 0;
3413 inherit->iii_externdrop = 0;
3414 inherit->iii_donating = FALSE;
3415
3416 /* adjust the internal assertions - and propagate if needed */
3417 if (ipc_importance_task_check_transition(imp_task, IIT_UPDATE_DROP, assertcnt)) {
3418 ipc_importance_task_propagate_assertion_locked(imp_task, IIT_UPDATE_DROP, TRUE);
3419 }
3420 } else {
3421 inherit->iii_externcnt = 0;
3422 inherit->iii_externdrop = 0;
3423 }
3424 }
3425
3426 /* drop the made reference on elem */
3427 ipc_importance_release_locked(elem);
3428 /* returns unlocked */
3429
3430 return KERN_SUCCESS;
3431 }
3432
3433
3434 /*
3435 * Routine: ipc_importance_get_value [Voucher Attribute Manager Interface]
3436 * Purpose:
3437 * Convert command and content data into a reference on a [potentially new]
3438 * attribute value. The importance attribute manager will only allow the
3439 * caller to get a value for the current task's importance, or to redeem
3440 * an importance attribute from an existing voucher.
3441 * Conditions:
3442 * Nothing locked on entry. May block.
3443 */
3444 static kern_return_t
3445 ipc_importance_get_value(
3446 ipc_voucher_attr_manager_t __assert_only manager,
3447 mach_voucher_attr_key_t __assert_only key,
3448 mach_voucher_attr_recipe_command_t command,
3449 mach_voucher_attr_value_handle_array_t prev_values,
3450 mach_voucher_attr_value_handle_array_size_t prev_value_count,
3451 mach_voucher_attr_content_t __unused content,
3452 mach_voucher_attr_content_size_t content_size,
3453 mach_voucher_attr_value_handle_t *out_value,
3454 mach_voucher_attr_value_flags_t *out_flags,
3455 ipc_voucher_t *out_value_voucher)
3456 {
3457 ipc_importance_elem_t elem;
3458 task_t self;
3459
3460 IMPORTANCE_ASSERT_MANAGER(manager);
3461 IMPORTANCE_ASSERT_KEY(key);
3462
3463 if (0 != content_size)
3464 return KERN_INVALID_ARGUMENT;
3465
3466 *out_flags = MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE;
3467 /* never an out voucher */
3468
3469 switch (command) {
3470
3471 case MACH_VOUCHER_ATTR_REDEEM:
3472
3473 /* redeem of previous values is the value */
3474 if (0 < prev_value_count) {
3475 elem = (ipc_importance_elem_t)prev_values[0];
3476 assert(IIE_NULL != elem);
3477
3478 ipc_importance_lock();
3479 assert(0 < elem->iie_made);
3480 elem->iie_made++;
3481 ipc_importance_unlock();
3482
3483 *out_value = prev_values[0];
3484 return KERN_SUCCESS;
3485 }
3486
3487 /* redeem of default is default */
3488 *out_value = 0;
3489 *out_value_voucher = IPC_VOUCHER_NULL;
3490 return KERN_SUCCESS;
3491
3492 case MACH_VOUCHER_ATTR_IMPORTANCE_SELF:
3493 self = current_task();
3494
3495 elem = (ipc_importance_elem_t)ipc_importance_for_task(self, TRUE);
3496 /* made reference added (or IIE_NULL which isn't referenced) */
3497
3498 *out_value = (mach_voucher_attr_value_handle_t)elem;
3499 *out_value_voucher = IPC_VOUCHER_NULL;
3500 return KERN_SUCCESS;
3501
3502 default:
3503 /*
3504 * every other command is unknown
3505 *
3506 * Specifically, there is no mechanism provided to construct an
3507 * importance attribute for a task/process from just a pid or
3508 * task port. It has to be copied (or redeemed) from a previous
3509 * voucher that has it.
3510 */
3511 return KERN_INVALID_ARGUMENT;
3512 }
3513 }
3514
3515 /*
3516 * Routine: ipc_importance_extract_content [Voucher Attribute Manager Interface]
3517 * Purpose:
3518 * Extract meaning from the attribute value present in a voucher. While
3519 * the real goal is to provide commands and data that can reproduce the
3520 * voucher's value "out of thin air", this isn't possible with importance
3521 * attribute values. Instead, return debug info to help track down dependencies.
3522 * Conditions:
3523 * Nothing locked on entry. May block.
3524 */
3525 static kern_return_t
3526 ipc_importance_extract_content(
3527 ipc_voucher_attr_manager_t __assert_only manager,
3528 mach_voucher_attr_key_t __assert_only key,
3529 mach_voucher_attr_value_handle_array_t values,
3530 mach_voucher_attr_value_handle_array_size_t value_count,
3531 mach_voucher_attr_recipe_command_t *out_command,
3532 mach_voucher_attr_content_t out_content,
3533 mach_voucher_attr_content_size_t *in_out_content_size)
3534 {
3535 mach_voucher_attr_content_size_t size = 0;
3536 ipc_importance_elem_t elem;
3537 unsigned int i;
3538
3539 IMPORTANCE_ASSERT_MANAGER(manager);
3540 IMPORTANCE_ASSERT_KEY(key);
3541
3542 /* the first non-default value provides the data */
3543 for (i = 0; i < value_count ; i++) {
3544 elem = (ipc_importance_elem_t)values[i];
3545 if (IIE_NULL == elem)
3546 continue;
3547
3548 snprintf((char *)out_content, *in_out_content_size, "Importance for pid ");
3549 size = (mach_voucher_attr_content_size_t)strlen((char *)out_content);
3550
3551 for(;;) {
3552 ipc_importance_inherit_t inherit = III_NULL;
3553 ipc_importance_task_t task_imp;
3554 task_t task;
3555 int t_pid;
3556
3557 if (IIE_TYPE_TASK == IIE_TYPE(elem)) {
3558 task_imp = (ipc_importance_task_t)elem;
3559 task = task_imp->iit_task;
3560 t_pid = (TASK_NULL != task) ?
3561 task_pid(task) : -1;
3562 snprintf((char *)out_content + size, *in_out_content_size - size, "%d", t_pid);
3563 } else {
3564 inherit = (ipc_importance_inherit_t)elem;
3565 task_imp = inherit->iii_to_task;
3566 task = task_imp->iit_task;
3567 t_pid = (TASK_NULL != task) ?
3568 task_pid(task) : -1;
3569 snprintf((char *)out_content + size, *in_out_content_size - size,
3570 "%d (%d of %d boosts) %s from pid ", t_pid,
3571 III_EXTERN(inherit), inherit->iii_externcnt,
3572 (inherit->iii_donating) ? "donated" : "linked");
3573 }
3574
3575 size = (mach_voucher_attr_content_size_t)strlen((char *)out_content);
3576
3577 if (III_NULL == inherit)
3578 break;
3579
3580 elem = inherit->iii_from_elem;
3581 }
3582 size++; /* account for NULL */
3583 }
3584 *out_command = MACH_VOUCHER_ATTR_NOOP; /* cannot be used to regenerate value */
3585 *in_out_content_size = size;
3586 return KERN_SUCCESS;
3587 }
3588
3589 /*
3590 * Routine: ipc_importance_command [Voucher Attribute Manager Interface]
3591 * Purpose:
3592 * Run commands against the importance attribute value found in a voucher.
3593 * No such commands are currently supported.
3594 * Conditions:
3595 * Nothing locked on entry. May block.
3596 */
3597 static kern_return_t
3598 ipc_importance_command(
3599 ipc_voucher_attr_manager_t __assert_only manager,
3600 mach_voucher_attr_key_t __assert_only key,
3601 mach_voucher_attr_value_handle_array_t values,
3602 mach_msg_type_number_t value_count,
3603 mach_voucher_attr_command_t command,
3604 mach_voucher_attr_content_t in_content,
3605 mach_voucher_attr_content_size_t in_content_size,
3606 mach_voucher_attr_content_t out_content,
3607 mach_voucher_attr_content_size_t *out_content_size)
3608 {
3609 ipc_importance_inherit_t inherit;
3610 ipc_importance_task_t to_task;
3611 uint32_t refs, *outrefsp;
3612 mach_msg_type_number_t i;
3613 uint32_t externcnt;
3614
3615 IMPORTANCE_ASSERT_MANAGER(manager);
3616 IMPORTANCE_ASSERT_KEY(key);
3617
3618 if (in_content_size != sizeof(refs) ||
3619 (*out_content_size != 0 && *out_content_size != sizeof(refs))) {
3620 return KERN_INVALID_ARGUMENT;
3621 }
3622 refs = *(uint32_t *)(void *)in_content;
3623 outrefsp = (*out_content_size != 0) ? (uint32_t *)(void *)out_content : NULL;
3624
3625 if (MACH_VOUCHER_IMPORTANCE_ATTR_DROP_EXTERNAL != command) {
3626 return KERN_NOT_SUPPORTED;
3627 }
3628
3629 /* the first non-default value of the apropos type provides the data */
3630 inherit = III_NULL;
3631 for (i = 0; i < value_count; i++) {
3632 ipc_importance_elem_t elem = (ipc_importance_elem_t)values[i];
3633
3634 if (IIE_NULL != elem && IIE_TYPE_INHERIT == IIE_TYPE(elem)) {
3635 inherit = (ipc_importance_inherit_t)elem;
3636 break;
3637 }
3638 }
3639 if (III_NULL == inherit) {
3640 return KERN_INVALID_ARGUMENT;
3641 }
3642
3643 ipc_importance_lock();
3644
3645 if (0 == refs) {
3646 if (NULL != outrefsp) {
3647 *outrefsp = III_EXTERN(inherit);
3648 }
3649 ipc_importance_unlock();
3650 return KERN_SUCCESS;
3651 }
3652
3653 to_task = inherit->iii_to_task;
3654 assert(ipc_importance_task_is_any_receiver_type(to_task));
3655
3656 /* if not donating to a denap receiver, it was called incorrectly */
3657 if (!ipc_importance_task_is_marked_denap_receiver(to_task)) {
3658 ipc_importance_unlock();
3659 return KERN_INVALID_TASK; /* keeps dispatch happy */
3660 }
3661
3662 /* Enough external references left to drop? */
3663 if (III_EXTERN(inherit) < refs) {
3664 ipc_importance_unlock();
3665 return KERN_FAILURE;
3666 }
3667
3668 /* re-base external and internal counters at the inherit and the to-task (if apropos) */
3669 if (inherit->iii_donating) {
3670 assert(IIT_EXTERN(to_task) >= III_EXTERN(inherit));
3671 assert(to_task->iit_externcnt >= inherit->iii_externcnt);
3672 assert(to_task->iit_externdrop >= inherit->iii_externdrop);
3673 inherit->iii_externdrop += refs;
3674 to_task->iit_externdrop += refs;
3675 externcnt = III_EXTERN(inherit);
3676 if (0 == externcnt) {
3677 inherit->iii_donating = FALSE;
3678 to_task->iit_externcnt -= inherit->iii_externcnt;
3679 to_task->iit_externdrop -= inherit->iii_externdrop;
3680
3681
3682 /* Start AppNap delay hysteresis - even if not the last boost for the task. */
3683 if (ipc_importance_delayed_drop_call != NULL &&
3684 ipc_importance_task_is_marked_denap_receiver(to_task)) {
3685 ipc_importance_task_delayed_drop(to_task);
3686 }
3687
3688 /* drop task assertions associated with the dropped boosts */
3689 if (ipc_importance_task_check_transition(to_task, IIT_UPDATE_DROP, refs)) {
3690 ipc_importance_task_propagate_assertion_locked(to_task, IIT_UPDATE_DROP, TRUE);
3691 /* may have dropped and retaken importance lock */
3692 }
3693 } else {
3694 /* assert(to_task->iit_assertcnt >= refs + externcnt); */
3695 /* defensive deduction in case of assertcnt underflow */
3696 if (to_task->iit_assertcnt > refs + externcnt) {
3697 to_task->iit_assertcnt -= refs;
3698 } else {
3699 to_task->iit_assertcnt = externcnt;
3700 }
3701 }
3702 } else {
3703 inherit->iii_externdrop += refs;
3704 externcnt = III_EXTERN(inherit);
3705 }
3706
3707 /* capture result (if requested) */
3708 if (NULL != outrefsp) {
3709 *outrefsp = externcnt;
3710 }
3711
3712 ipc_importance_unlock();
3713 return KERN_SUCCESS;
3714 }
3715
3716 /*
3717 * Routine: ipc_importance_manager_release [Voucher Attribute Manager Interface]
3718 * Purpose:
3719 * Release the Voucher system's reference on the IPC importance attribute
3720 * manager.
3721 * Conditions:
3722 * As this can only occur after the manager drops the Attribute control
3723 * reference granted back at registration time, and that reference is never
3724 * dropped, this should never be called.
3725 */
3726 static void
3727 ipc_importance_manager_release(
3728 ipc_voucher_attr_manager_t __assert_only manager)
3729 {
3730 IMPORTANCE_ASSERT_MANAGER(manager);
3731 panic("Voucher importance manager released");
3732 }
3733
3734 /*
3735 * Routine: ipc_importance_init
3736 * Purpose:
3737 * Initialize the IPC importance manager.
3738 * Conditions:
3739 * Zones and Vouchers are already initialized.
3740 */
3741 void
3742 ipc_importance_init(void)
3743 {
3744 natural_t ipc_importance_max = (task_max + thread_max) * 2;
3745 char temp_buf[26];
3746 kern_return_t kr;
3747
3748 if (PE_parse_boot_argn("imp_interactive_receiver", temp_buf, sizeof(temp_buf))) {
3749 ipc_importance_interactive_receiver = TRUE;
3750 }
3751
3752 ipc_importance_task_zone = zinit(sizeof(struct ipc_importance_task),
3753 ipc_importance_max * sizeof(struct ipc_importance_task),
3754 sizeof(struct ipc_importance_task),
3755 "ipc task importance");
3756 zone_change(ipc_importance_task_zone, Z_NOENCRYPT, TRUE);
3757
3758 ipc_importance_inherit_zone = zinit(sizeof(struct ipc_importance_inherit),
3759 ipc_importance_max * sizeof(struct ipc_importance_inherit),
3760 sizeof(struct ipc_importance_inherit),
3761 "ipc importance inherit");
3762 zone_change(ipc_importance_inherit_zone, Z_NOENCRYPT, TRUE);
3763
3764
3765 #if DEVELOPMENT || DEBUG
3766 queue_init(&global_iit_alloc_queue);
3767 #endif
3768
3769 /* initialize global locking */
3770 ipc_importance_lock_init();
3771
3772 kr = ipc_register_well_known_mach_voucher_attr_manager(&ipc_importance_manager,
3773 (mach_voucher_attr_value_handle_t)0,
3774 MACH_VOUCHER_ATTR_KEY_IMPORTANCE,
3775 &ipc_importance_control);
3776 if (KERN_SUCCESS != kr)
3777 printf("Voucher importance manager register returned %d", kr);
3778 }
3779
3780 /*
3781 * Routine: ipc_importance_thread_call_init
3782 * Purpose:
3783 * Initialize the IPC importance code dependent upon
3784 * thread-call support being available.
3785 * Conditions:
3786 * Thread-call mechanism is already initialized.
3787 */
3788 void
3789 ipc_importance_thread_call_init(void)
3790 {
3791 /* initialize delayed drop queue and thread-call */
3792 queue_init(&ipc_importance_delayed_drop_queue);
3793 ipc_importance_delayed_drop_call =
3794 thread_call_allocate(ipc_importance_task_delayed_drop_scan, NULL);
3795 if (NULL == ipc_importance_delayed_drop_call) {
3796 panic("ipc_importance_init");
3797 }
3798 }
3799
3800 /*
3801 * Routing: task_importance_list_pids
3802 * Purpose: list pids where task in donating importance.
3803 * Conditions: To be called only from kdp stackshot code.
3804 * Will panic the system otherwise.
3805 */
3806 extern int
3807 task_importance_list_pids(task_t task, int flags, char *pid_list, unsigned int max_count)
3808 {
3809 if (kdp_lck_spin_is_acquired(&ipc_importance_lock_data) ||
3810 max_count < 1 ||
3811 task->task_imp_base == IIT_NULL ||
3812 pid_list == NULL ||
3813 flags != TASK_IMP_LIST_DONATING_PIDS) {
3814 return 0;
3815 }
3816 unsigned int pidcount = 0;
3817 task_t temp_task;
3818 ipc_importance_task_t task_imp = task->task_imp_base;
3819 ipc_kmsg_t temp_kmsg;
3820 ipc_importance_inherit_t temp_inherit;
3821 ipc_importance_elem_t elem;
3822 int target_pid = 0, previous_pid;
3823
3824 queue_iterate(&task_imp->iit_inherits, temp_inherit, ipc_importance_inherit_t, iii_inheritance) {
3825 /* check space in buffer */
3826 if (pidcount >= max_count)
3827 break;
3828 previous_pid = target_pid;
3829 target_pid = -1;
3830
3831 if (temp_inherit->iii_donating) {
3832
3833 #if DEVELOPMENT || DEBUG
3834 target_pid = temp_inherit->iii_to_task->iit_bsd_pid;
3835 #else
3836 temp_task = temp_inherit->iii_to_task->iit_task;
3837 if (temp_task != TASK_NULL) {
3838 target_pid = task_pid(temp_task);
3839 }
3840 #endif
3841 }
3842
3843 if (target_pid != -1 && previous_pid != target_pid) {
3844 memcpy(pid_list, &target_pid, sizeof(target_pid));
3845 pid_list += sizeof(target_pid);
3846 pidcount++;
3847 }
3848
3849 }
3850
3851 target_pid = 0;
3852 queue_iterate(&task_imp->iit_kmsgs, temp_kmsg, ipc_kmsg_t, ikm_inheritance) {
3853 if (pidcount >= max_count)
3854 break;
3855 previous_pid = target_pid;
3856 target_pid = -1;
3857 elem = temp_kmsg->ikm_importance;
3858 temp_task = TASK_NULL;
3859
3860 if (elem == IIE_NULL) {
3861 continue;
3862 }
3863
3864 if (!(temp_kmsg->ikm_header && MACH_MSGH_BITS_RAISED_IMPORTANCE(temp_kmsg->ikm_header->msgh_bits))) {
3865 continue;
3866 }
3867
3868 if (IIE_TYPE_TASK == IIE_TYPE(elem) &&
3869 (((ipc_importance_task_t)elem)->iit_task != TASK_NULL)) {
3870 target_pid = task_pid(((ipc_importance_task_t)elem)->iit_task);
3871 } else {
3872 temp_inherit = (ipc_importance_inherit_t)elem;
3873 #if DEVELOPMENT || DEBUG
3874 target_pid = temp_inherit->iii_to_task->iit_bsd_pid;
3875 #else
3876 temp_task = temp_inherit->iii_to_task->iit_task;
3877 if (temp_task != TASK_NULL) {
3878 target_pid = task_pid(temp_task);
3879 }
3880 #endif
3881 }
3882
3883 if (target_pid != -1 && previous_pid != target_pid) {
3884 memcpy(pid_list, &target_pid, sizeof(target_pid));
3885 pid_list += sizeof(target_pid);
3886 pidcount++;
3887 }
3888 }
3889
3890 return pidcount;
3891 }
3892