2 * Copyright (c) 2013-2020 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <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/zalloc.h>
40 #include <kern/queue.h>
41 #include <kern/task.h>
42 #include <kern/policy_internal.h>
44 #include <sys/kdebug.h>
46 #include <mach/mach_voucher_attr_control.h>
47 #include <mach/machine/sdt.h>
49 extern int proc_pid(void *);
50 extern int proc_selfpid(void);
51 extern uint64_t proc_uniqueid(void *p
);
52 extern char *proc_name_address(void *p
);
55 * Globals for delayed boost drop processing.
57 static queue_head_t ipc_importance_delayed_drop_queue
;
58 static thread_call_t ipc_importance_delayed_drop_call
;
59 static uint64_t ipc_importance_delayed_drop_timestamp
;
60 static boolean_t ipc_importance_delayed_drop_call_requested
= FALSE
;
62 #define DENAP_DROP_TARGET (1000 * NSEC_PER_MSEC) /* optimum denap delay */
63 #define DENAP_DROP_SKEW (100 * NSEC_PER_MSEC) /* request skew for wakeup */
64 #define DENAP_DROP_LEEWAY (2 * DENAP_DROP_SKEW) /* specified wakeup leeway */
66 #define DENAP_DROP_DELAY (DENAP_DROP_TARGET + DENAP_DROP_SKEW)
67 #define DENAP_DROP_FLAGS (THREAD_CALL_DELAY_SYS_NORMAL | THREAD_CALL_DELAY_LEEWAY)
70 * Importance Voucher Attribute Manager
72 static LCK_SPIN_DECLARE_ATTR(ipc_importance_lock_data
, &ipc_lck_grp
, &ipc_lck_attr
);
74 #define ipc_importance_lock() \
75 lck_spin_lock_grp(&ipc_importance_lock_data, &ipc_lck_grp)
76 #define ipc_importance_lock_try() \
77 lck_spin_try_lock_grp(&ipc_importance_lock_data, &ipc_lck_grp)
78 #define ipc_importance_unlock() \
79 lck_spin_unlock(&ipc_importance_lock_data)
80 #define ipc_importance_assert_held() \
81 lck_spin_assert(&ipc_importance_lock_data, LCK_ASSERT_OWNED)
84 #define incr_ref_counter(x) (os_atomic_inc(&(x), relaxed))
88 ipc_importance_reference_internal(ipc_importance_elem_t elem
)
90 incr_ref_counter(elem
->iie_refs_added
);
91 return os_atomic_inc(&elem
->iie_bits
, relaxed
) & IIE_REFS_MASK
;
96 ipc_importance_release_internal(ipc_importance_elem_t elem
)
98 incr_ref_counter(elem
->iie_refs_dropped
);
99 return os_atomic_dec(&elem
->iie_bits
, relaxed
) & IIE_REFS_MASK
;
104 ipc_importance_task_reference_internal(ipc_importance_task_t task_imp
)
107 out
= ipc_importance_reference_internal(&task_imp
->iit_elem
);
108 incr_ref_counter(task_imp
->iit_elem
.iie_task_refs_added
);
114 ipc_importance_task_release_internal(ipc_importance_task_t task_imp
)
118 assert(1 < IIT_REFS(task_imp
));
119 incr_ref_counter(task_imp
->iit_elem
.iie_task_refs_dropped
);
120 out
= ipc_importance_release_internal(&task_imp
->iit_elem
);
126 ipc_importance_counter_init(ipc_importance_elem_t elem
)
128 elem
->iie_refs_added
= 0;
129 elem
->iie_refs_dropped
= 0;
130 elem
->iie_kmsg_refs_added
= 0;
131 elem
->iie_kmsg_refs_inherited
= 0;
132 elem
->iie_kmsg_refs_coalesced
= 0;
133 elem
->iie_kmsg_refs_dropped
= 0;
134 elem
->iie_task_refs_added
= 0;
135 elem
->iie_task_refs_added_inherit_from
= 0;
136 elem
->iie_task_refs_added_transition
= 0;
137 elem
->iie_task_refs_self_added
= 0;
138 elem
->iie_task_refs_inherited
= 0;
139 elem
->iie_task_refs_coalesced
= 0;
140 elem
->iie_task_refs_dropped
= 0;
143 #define incr_ref_counter(x)
146 #if DEVELOPMENT || DEBUG
147 static queue_head_t global_iit_alloc_queue
=
148 QUEUE_HEAD_INITIALIZER(global_iit_alloc_queue
);
151 static ZONE_DECLARE(ipc_importance_task_zone
, "ipc task importance",
152 sizeof(struct ipc_importance_task
), ZC_NOENCRYPT
);
153 static ZONE_DECLARE(ipc_importance_inherit_zone
, "ipc importance inherit",
154 sizeof(struct ipc_importance_inherit
), ZC_NOENCRYPT
);
155 static zone_t ipc_importance_inherit_zone
;
157 static ipc_voucher_attr_control_t ipc_importance_control
;
159 static boolean_t
ipc_importance_task_check_transition(ipc_importance_task_t task_imp
,
160 iit_update_type_t type
, uint32_t delta
);
162 static void ipc_importance_task_propagate_assertion_locked(ipc_importance_task_t task_imp
,
163 iit_update_type_t type
, boolean_t update_task_imp
);
165 static ipc_importance_inherit_t
ipc_importance_inherit_from_task(task_t from_task
, task_t to_task
);
168 * Routine: ipc_importance_kmsg_link
170 * Link the kmsg onto the appropriate propagation chain.
171 * If the element is a task importance, we link directly
172 * on its propagation chain. Otherwise, we link onto the
173 * destination task of the inherit.
175 * Importance lock held.
176 * Caller is donating an importance elem reference to the kmsg.
179 ipc_importance_kmsg_link(
181 ipc_importance_elem_t elem
)
183 ipc_importance_elem_t link_elem
;
185 assert(IIE_NULL
== kmsg
->ikm_importance
);
187 link_elem
= (IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) ?
188 (ipc_importance_elem_t
)((ipc_importance_inherit_t
)elem
)->iii_to_task
:
191 queue_enter(&link_elem
->iie_kmsgs
, kmsg
, ipc_kmsg_t
, ikm_inheritance
);
192 kmsg
->ikm_importance
= elem
;
196 * Routine: ipc_importance_kmsg_unlink
198 * Unlink the kmsg from its current propagation chain.
199 * If the element is a task importance, we unlink directly
200 * from its propagation chain. Otherwise, we unlink from the
201 * destination task of the inherit.
203 * The reference to the importance element it was linked on.
205 * Importance lock held.
206 * Caller is responsible for dropping reference on returned elem.
208 static ipc_importance_elem_t
209 ipc_importance_kmsg_unlink(
212 ipc_importance_elem_t elem
= kmsg
->ikm_importance
;
214 if (IIE_NULL
!= elem
) {
215 ipc_importance_elem_t unlink_elem
;
217 unlink_elem
= (IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) ?
218 (ipc_importance_elem_t
)((ipc_importance_inherit_t
)elem
)->iii_to_task
:
221 queue_remove(&unlink_elem
->iie_kmsgs
, kmsg
, ipc_kmsg_t
, ikm_inheritance
);
222 kmsg
->ikm_importance
= IIE_NULL
;
228 * Routine: ipc_importance_inherit_link
230 * Link the inherit onto the appropriate propagation chain.
231 * If the element is a task importance, we link directly
232 * on its propagation chain. Otherwise, we link onto the
233 * destination task of the inherit.
235 * Importance lock held.
236 * Caller is donating an elem importance reference to the inherit.
239 ipc_importance_inherit_link(
240 ipc_importance_inherit_t inherit
,
241 ipc_importance_elem_t elem
)
243 ipc_importance_task_t link_task
;
245 assert(IIE_NULL
== inherit
->iii_from_elem
);
246 link_task
= (IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) ?
247 ((ipc_importance_inherit_t
)elem
)->iii_to_task
:
248 (ipc_importance_task_t
)elem
;
250 queue_enter(&link_task
->iit_inherits
, inherit
,
251 ipc_importance_inherit_t
, iii_inheritance
);
252 inherit
->iii_from_elem
= elem
;
256 * Routine: ipc_importance_inherit_find
258 * Find an existing inherit that links the from element to the
259 * to_task at a given nesting depth. As inherits from other
260 * inherits are actually linked off the original inherit's donation
261 * receiving task, we have to conduct our search from there if
262 * the from element is an inherit.
264 * A pointer (not a reference) to the matching inherit.
266 * Importance lock held.
268 static ipc_importance_inherit_t
269 ipc_importance_inherit_find(
270 ipc_importance_elem_t from
,
271 ipc_importance_task_t to_task
,
274 ipc_importance_task_t link_task
;
275 ipc_importance_inherit_t inherit
;
277 link_task
= (IIE_TYPE_INHERIT
== IIE_TYPE(from
)) ?
278 ((ipc_importance_inherit_t
)from
)->iii_to_task
:
279 (ipc_importance_task_t
)from
;
281 queue_iterate(&link_task
->iit_inherits
, inherit
,
282 ipc_importance_inherit_t
, iii_inheritance
) {
283 if (inherit
->iii_to_task
== to_task
&& inherit
->iii_depth
== depth
) {
291 * Routine: ipc_importance_inherit_unlink
293 * Unlink the inherit from its current propagation chain.
294 * If the element is a task importance, we unlink directly
295 * from its propagation chain. Otherwise, we unlink from the
296 * destination task of the inherit.
298 * The reference to the importance element it was linked on.
300 * Importance lock held.
301 * Caller is responsible for dropping reference on returned elem.
303 static ipc_importance_elem_t
304 ipc_importance_inherit_unlink(
305 ipc_importance_inherit_t inherit
)
307 ipc_importance_elem_t elem
= inherit
->iii_from_elem
;
309 if (IIE_NULL
!= elem
) {
310 ipc_importance_task_t unlink_task
;
312 unlink_task
= (IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) ?
313 ((ipc_importance_inherit_t
)elem
)->iii_to_task
:
314 (ipc_importance_task_t
)elem
;
316 queue_remove(&unlink_task
->iit_inherits
, inherit
,
317 ipc_importance_inherit_t
, iii_inheritance
);
318 inherit
->iii_from_elem
= IIE_NULL
;
324 * Routine: ipc_importance_reference
326 * Add a reference to the importance element.
328 * Caller must hold a reference on the element.
331 ipc_importance_reference(ipc_importance_elem_t elem
)
333 assert(0 < IIE_REFS(elem
));
334 ipc_importance_reference_internal(elem
);
338 * Routine: ipc_importance_release_locked
340 * Release a reference on an importance attribute value,
341 * unlinking and deallocating the attribute if the last reference.
343 * Entered with importance lock held, leaves with it unlocked.
346 ipc_importance_release_locked(ipc_importance_elem_t elem
)
348 assert(0 < IIE_REFS(elem
));
351 ipc_importance_inherit_t temp_inherit
;
352 ipc_importance_task_t link_task
;
353 ipc_kmsg_t temp_kmsg
;
354 uint32_t expected
= 0;
356 if (0 < elem
->iie_made
) {
360 link_task
= (IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) ?
361 ((ipc_importance_inherit_t
)elem
)->iii_to_task
:
362 (ipc_importance_task_t
)elem
;
364 queue_iterate(&link_task
->iit_kmsgs
, temp_kmsg
, ipc_kmsg_t
, ikm_inheritance
)
365 if (temp_kmsg
->ikm_importance
== elem
) {
368 queue_iterate(&link_task
->iit_inherits
, temp_inherit
,
369 ipc_importance_inherit_t
, iii_inheritance
)
370 if (temp_inherit
->iii_from_elem
== elem
) {
373 if (IIE_REFS(elem
) < expected
+ 1) {
374 panic("ipc_importance_release_locked (%p)", elem
);
376 #endif /* IMPORTANCE_DEBUG */
378 if (0 < ipc_importance_release_internal(elem
)) {
379 ipc_importance_unlock();
385 switch (IIE_TYPE(elem
)) {
386 /* just a "from" task reference to drop */
389 ipc_importance_task_t task_elem
;
391 task_elem
= (ipc_importance_task_t
)elem
;
393 /* the task can't still hold a reference on the task importance */
394 assert(TASK_NULL
== task_elem
->iit_task
);
396 #if DEVELOPMENT || DEBUG
397 queue_remove(&global_iit_alloc_queue
, task_elem
, ipc_importance_task_t
, iit_allocation
);
400 ipc_importance_unlock();
402 zfree(ipc_importance_task_zone
, task_elem
);
406 /* dropping an inherit element */
407 case IIE_TYPE_INHERIT
:
409 ipc_importance_inherit_t inherit
= (ipc_importance_inherit_t
)elem
;
410 ipc_importance_task_t to_task
= inherit
->iii_to_task
;
411 ipc_importance_elem_t from_elem
;
413 assert(IIT_NULL
!= to_task
);
414 assert(ipc_importance_task_is_any_receiver_type(to_task
));
416 /* unlink the inherit from its source element */
417 from_elem
= ipc_importance_inherit_unlink(inherit
);
418 assert(IIE_NULL
!= from_elem
);
421 * The attribute might have pending external boosts if the attribute
422 * was given out during exec, drop them from the appropriate destination
425 * The attribute will not have any pending external boosts if the
426 * attribute was given out to voucher system since it would have been
427 * dropped by ipc_importance_release_value, but there is not way to
428 * detect that, thus if the attribute has a pending external boost,
429 * drop them from the appropriate destination task.
431 * The inherit attribute from exec and voucher system would not
432 * get deduped to each other, thus dropping the external boost
433 * from destination task at two different places will not have
434 * any unintended side effects.
436 assert(inherit
->iii_externcnt
>= inherit
->iii_externdrop
);
437 if (inherit
->iii_donating
) {
438 uint32_t assertcnt
= III_EXTERN(inherit
);
440 assert(ipc_importance_task_is_any_receiver_type(to_task
));
441 assert(to_task
->iit_externcnt
>= inherit
->iii_externcnt
);
442 assert(to_task
->iit_externdrop
>= inherit
->iii_externdrop
);
443 to_task
->iit_externcnt
-= inherit
->iii_externcnt
;
444 to_task
->iit_externdrop
-= inherit
->iii_externdrop
;
445 inherit
->iii_externcnt
= 0;
446 inherit
->iii_externdrop
= 0;
447 inherit
->iii_donating
= FALSE
;
449 /* adjust the internal assertions - and propagate as needed */
450 if (ipc_importance_task_check_transition(to_task
, IIT_UPDATE_DROP
, assertcnt
)) {
451 ipc_importance_task_propagate_assertion_locked(to_task
, IIT_UPDATE_DROP
, TRUE
);
454 inherit
->iii_externcnt
= 0;
455 inherit
->iii_externdrop
= 0;
458 /* release the reference on the source element */
459 ipc_importance_release_locked(from_elem
);
460 /* unlocked on return */
462 /* release the reference on the destination task */
463 ipc_importance_task_release(to_task
);
465 /* free the inherit */
466 zfree(ipc_importance_inherit_zone
, inherit
);
473 * Routine: ipc_importance_release
475 * Release a reference on an importance attribute value,
476 * unlinking and deallocating the attribute if the last reference.
478 * nothing locked on entrance, nothing locked on exit.
482 ipc_importance_release(ipc_importance_elem_t elem
)
484 if (IIE_NULL
== elem
) {
488 ipc_importance_lock();
489 ipc_importance_release_locked(elem
);
494 * Routine: ipc_importance_task_reference
498 * Retain a reference on a task importance attribute value.
500 * nothing locked on entrance, nothing locked on exit.
501 * caller holds a reference already.
504 ipc_importance_task_reference(ipc_importance_task_t task_elem
)
506 if (IIT_NULL
== task_elem
) {
510 incr_ref_counter(task_elem
->iit_elem
.iie_task_refs_added
);
512 ipc_importance_reference(&task_elem
->iit_elem
);
516 * Routine: ipc_importance_task_release
518 * Release a reference on a task importance attribute value,
519 * unlinking and deallocating the attribute if the last reference.
521 * nothing locked on entrance, nothing locked on exit.
525 ipc_importance_task_release(ipc_importance_task_t task_elem
)
527 if (IIT_NULL
== task_elem
) {
531 ipc_importance_lock();
533 incr_ref_counter(task_elem
->iit_elem
.iie_task_refs_dropped
);
535 ipc_importance_release_locked(&task_elem
->iit_elem
);
540 * Routine: ipc_importance_task_release_locked
542 * Release a reference on a task importance attribute value,
543 * unlinking and deallocating the attribute if the last reference.
545 * importance lock held on entry, nothing locked on exit.
549 ipc_importance_task_release_locked(ipc_importance_task_t task_elem
)
551 if (IIT_NULL
== task_elem
) {
552 ipc_importance_unlock();
556 incr_ref_counter(task_elem
->iit_elem
.iie_task_refs_dropped
);
558 ipc_importance_release_locked(&task_elem
->iit_elem
);
563 * Routines for importance donation/inheritance/boosting
568 * External importance assertions are managed by the process in userspace
569 * Internal importance assertions are the responsibility of the kernel
570 * Assertions are changed from internal to external via task_importance_externalize_assertion
574 * Routine: ipc_importance_task_check_transition
576 * Increase or decrement the internal task importance counter of the
577 * specified task and determine if propagation and a task policy
578 * update is required.
580 * If it is already enqueued for a policy update, steal it from that queue
581 * (as we are reversing that update before it happens).
584 * Called with the importance lock held.
585 * It is the caller's responsibility to perform the propagation of the
586 * transition and/or policy changes by checking the return value.
589 ipc_importance_task_check_transition(
590 ipc_importance_task_t task_imp
,
591 iit_update_type_t type
,
595 task_t target_task
= task_imp
->iit_task
;
597 boolean_t boost
= (IIT_UPDATE_HOLD
== type
);
598 boolean_t before_boosted
, after_boosted
;
600 ipc_importance_assert_held();
602 if (!ipc_importance_task_is_any_receiver_type(task_imp
)) {
607 int target_pid
= task_pid(target_task
);
609 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, (((boost
) ? IMP_HOLD
: IMP_DROP
) | TASK_POLICY_INTERNAL
))) | DBG_FUNC_START
,
610 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_EXTERN(task_imp
), 0);
613 /* snapshot the effective boosting status before making any changes */
614 before_boosted
= (task_imp
->iit_assertcnt
> 0);
616 /* Adjust the assertcnt appropriately */
618 task_imp
->iit_assertcnt
+= delta
;
620 DTRACE_BOOST6(send_boost
, task_t
, target_task
, int, target_pid
,
621 task_t
, current_task(), int, proc_selfpid(), int, delta
, int, task_imp
->iit_assertcnt
);
624 // assert(delta <= task_imp->iit_assertcnt);
625 if (task_imp
->iit_assertcnt
< delta
+ IIT_EXTERN(task_imp
)) {
626 /* TODO: Turn this back into a panic <rdar://problem/12592649> */
627 task_imp
->iit_assertcnt
= IIT_EXTERN(task_imp
);
629 task_imp
->iit_assertcnt
-= delta
;
632 // This convers both legacy and voucher-based importance.
633 DTRACE_BOOST4(drop_boost
, task_t
, target_task
, int, target_pid
, int, delta
, int, task_imp
->iit_assertcnt
);
638 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, (((boost
) ? IMP_HOLD
: IMP_DROP
) | TASK_POLICY_INTERNAL
))) | DBG_FUNC_END
,
639 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_EXTERN(task_imp
), 0);
642 /* did the change result in an effective donor status change? */
643 after_boosted
= (task_imp
->iit_assertcnt
> 0);
645 if (after_boosted
!= before_boosted
) {
647 * If the task importance is already on an update queue, we just reversed the need for a
648 * pending policy update. If the queue is any other than the delayed-drop-queue, pull it
649 * off that queue and release the reference it got going onto the update queue. If it is
650 * the delayed-drop-queue we leave it in place in case it comes back into the drop state
651 * before its time delay is up.
653 * We still need to propagate the change downstream to reverse the assertcnt effects,
654 * but we no longer need to update this task's boost policy state.
656 * Otherwise, mark it as needing a policy update.
658 assert(0 == task_imp
->iit_updatepolicy
);
659 if (NULL
!= task_imp
->iit_updateq
) {
660 if (&ipc_importance_delayed_drop_queue
!= task_imp
->iit_updateq
) {
661 queue_remove(task_imp
->iit_updateq
, task_imp
, ipc_importance_task_t
, iit_updates
);
662 task_imp
->iit_updateq
= NULL
;
663 ipc_importance_task_release_internal(task_imp
); /* can't be last ref */
666 task_imp
->iit_updatepolicy
= 1;
676 * Routine: ipc_importance_task_propagate_helper
678 * Increase or decrement the internal task importance counter of all
679 * importance tasks inheriting from the specified one. If this causes
680 * that importance task to change state, add it to the list of tasks
681 * to do a policy update against.
683 * Called with the importance lock held.
684 * It is the caller's responsibility to iterate down the generated list
685 * and propagate any subsequent assertion changes from there.
688 ipc_importance_task_propagate_helper(
689 ipc_importance_task_t task_imp
,
690 iit_update_type_t type
,
693 ipc_importance_task_t temp_task_imp
;
696 * iterate the downstream kmsgs, adjust their boosts,
697 * and capture the next task to adjust for each message
700 ipc_kmsg_t temp_kmsg
;
702 queue_iterate(&task_imp
->iit_kmsgs
, temp_kmsg
, ipc_kmsg_t
, ikm_inheritance
) {
703 mach_msg_header_t
*hdr
= temp_kmsg
->ikm_header
;
704 mach_port_delta_t delta
;
707 /* toggle the kmsg importance bit as a barrier to parallel adjusts */
708 if (IIT_UPDATE_HOLD
== type
) {
709 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(hdr
->msgh_bits
)) {
713 /* mark the message as now carrying importance */
714 hdr
->msgh_bits
|= MACH_MSGH_BITS_RAISEIMP
;
717 if (!MACH_MSGH_BITS_RAISED_IMPORTANCE(hdr
->msgh_bits
)) {
721 /* clear the message as now carrying importance */
722 hdr
->msgh_bits
&= ~MACH_MSGH_BITS_RAISEIMP
;
726 /* determine the task importance to adjust as result (if any) */
727 port
= hdr
->msgh_remote_port
;
728 assert(IP_VALID(port
));
730 temp_task_imp
= IIT_NULL
;
731 if (!ipc_port_importance_delta_internal(port
, IPID_OPTION_NORMAL
, &delta
, &temp_task_imp
)) {
735 /* no task importance to adjust associated with the port? */
736 if (IIT_NULL
== temp_task_imp
) {
740 /* hold a reference on temp_task_imp */
742 /* Adjust the task assertions and determine if an edge was crossed */
743 if (ipc_importance_task_check_transition(temp_task_imp
, type
, 1)) {
744 incr_ref_counter(temp_task_imp
->iit_elem
.iie_task_refs_added_transition
);
745 queue_enter(propagation
, temp_task_imp
, ipc_importance_task_t
, iit_props
);
746 /* reference donated */
748 ipc_importance_task_release_internal(temp_task_imp
);
753 * iterate the downstream importance inherits
754 * and capture the next task importance to boost for each
756 ipc_importance_inherit_t temp_inherit
;
758 queue_iterate(&task_imp
->iit_inherits
, temp_inherit
, ipc_importance_inherit_t
, iii_inheritance
) {
759 uint32_t assertcnt
= III_EXTERN(temp_inherit
);
761 temp_task_imp
= temp_inherit
->iii_to_task
;
762 assert(IIT_NULL
!= temp_task_imp
);
764 if (IIT_UPDATE_HOLD
== type
) {
765 /* if no undropped externcnts in the inherit, nothing to do */
766 if (0 == assertcnt
) {
767 assert(temp_inherit
->iii_donating
== FALSE
);
771 /* nothing to do if the inherit is already donating (forced donation) */
772 if (temp_inherit
->iii_donating
) {
776 /* mark it donating and contribute to the task externcnts */
777 temp_inherit
->iii_donating
= TRUE
;
778 temp_task_imp
->iit_externcnt
+= temp_inherit
->iii_externcnt
;
779 temp_task_imp
->iit_externdrop
+= temp_inherit
->iii_externdrop
;
781 /* if no contributing assertions, move on */
782 if (0 == assertcnt
) {
783 assert(temp_inherit
->iii_donating
== FALSE
);
787 /* nothing to do if the inherit is not donating */
788 if (!temp_inherit
->iii_donating
) {
792 /* mark it no longer donating */
793 temp_inherit
->iii_donating
= FALSE
;
795 /* remove the contribution the inherit made to the to-task */
796 assert(IIT_EXTERN(temp_task_imp
) >= III_EXTERN(temp_inherit
));
797 assert(temp_task_imp
->iit_externcnt
>= temp_inherit
->iii_externcnt
);
798 assert(temp_task_imp
->iit_externdrop
>= temp_inherit
->iii_externdrop
);
799 temp_task_imp
->iit_externcnt
-= temp_inherit
->iii_externcnt
;
800 temp_task_imp
->iit_externdrop
-= temp_inherit
->iii_externdrop
;
803 /* Adjust the task assertions and determine if an edge was crossed */
804 assert(ipc_importance_task_is_any_receiver_type(temp_task_imp
));
805 if (ipc_importance_task_check_transition(temp_task_imp
, type
, assertcnt
)) {
806 ipc_importance_task_reference(temp_task_imp
);
807 incr_ref_counter(temp_task_imp
->iit_elem
.iie_task_refs_added_transition
);
808 queue_enter(propagation
, temp_task_imp
, ipc_importance_task_t
, iit_props
);
814 * Routine: ipc_importance_task_process_updates
816 * Process the queue of task importances and apply the policy
817 * update called for. Only process tasks in the queue with an
818 * update timestamp less than the supplied max.
820 * Called and returns with importance locked.
821 * May drop importance lock and block temporarily.
824 ipc_importance_task_process_updates(
825 queue_t supplied_queue
,
827 uint64_t max_timestamp
)
829 ipc_importance_task_t task_imp
;
830 queue_head_t second_chance
;
831 queue_t queue
= supplied_queue
;
834 * This queue will hold the task's we couldn't trylock on first pass.
835 * By using a second (private) queue, we guarantee all tasks that get
836 * entered on this queue have a timestamp under the maximum.
838 queue_init(&second_chance
);
840 /* process any resulting policy updates */
842 while (!queue_empty(queue
)) {
844 struct task_pend_token pend_token
= {};
846 task_imp
= (ipc_importance_task_t
)queue_first(queue
);
847 assert(0 == task_imp
->iit_updatepolicy
);
848 assert(queue
== task_imp
->iit_updateq
);
850 /* if timestamp is too big, we're done */
851 if (task_imp
->iit_updatetime
> max_timestamp
) {
855 /* we were given a reference on each task in the queue */
857 /* remove it from the supplied queue */
858 queue_remove(queue
, task_imp
, ipc_importance_task_t
, iit_updates
);
859 task_imp
->iit_updateq
= NULL
;
861 target_task
= task_imp
->iit_task
;
863 /* Is it well on the way to exiting? */
864 if (TASK_NULL
== target_task
) {
865 ipc_importance_task_release_locked(task_imp
);
866 /* importance unlocked */
867 ipc_importance_lock();
871 /* Has the update been reversed on the hysteresis queue? */
872 if (0 < task_imp
->iit_assertcnt
&&
873 queue
== &ipc_importance_delayed_drop_queue
) {
874 ipc_importance_task_release_locked(task_imp
);
875 /* importance unlocked */
876 ipc_importance_lock();
881 * Can we get the task lock out-of-order?
882 * If not, stick this back on the second-chance queue.
884 if (!task_lock_try(target_task
)) {
885 boolean_t should_wait_lock
= (queue
== &second_chance
);
886 task_imp
->iit_updateq
= &second_chance
;
889 * If we're already processing second-chances on
890 * tasks, keep this task on the front of the queue.
891 * We will wait for the task lock before coming
892 * back and trying again, and we have a better
893 * chance of re-acquiring the lock if we come back
896 if (should_wait_lock
) {
897 task_reference(target_task
);
898 queue_enter_first(&second_chance
, task_imp
,
899 ipc_importance_task_t
, iit_updates
);
901 queue_enter(&second_chance
, task_imp
,
902 ipc_importance_task_t
, iit_updates
);
904 ipc_importance_unlock();
906 if (should_wait_lock
) {
907 task_lock(target_task
);
908 task_unlock(target_task
);
909 task_deallocate(target_task
);
912 ipc_importance_lock();
916 /* is it going away? */
917 if (!target_task
->active
) {
918 task_unlock(target_task
);
919 ipc_importance_task_release_locked(task_imp
);
920 /* importance unlocked */
921 ipc_importance_lock();
925 /* take a task reference for while we don't have the importance lock */
926 task_reference(target_task
);
928 /* count the transition */
930 task_imp
->iit_transitions
++;
933 ipc_importance_unlock();
935 /* apply the policy adjust to the target task (while it is still locked) */
936 task_update_boost_locked(target_task
, boost
, &pend_token
);
938 /* complete the policy update with the task unlocked */
939 ipc_importance_task_release(task_imp
);
940 task_unlock(target_task
);
941 task_policy_update_complete_unlocked(target_task
, &pend_token
);
942 task_deallocate(target_task
);
944 ipc_importance_lock();
947 /* If there are tasks we couldn't update the first time, try again */
948 if (!queue_empty(&second_chance
)) {
949 queue
= &second_chance
;
956 * Routine: ipc_importance_task_delayed_drop_scan
958 * The thread call routine to scan the delayed drop queue,
959 * requesting all updates with a deadline up to the last target
960 * for the thread-call (which is DENAP_DROP_SKEW beyond the first
961 * thread's optimum delay).
962 * update to drop its boost.
967 ipc_importance_task_delayed_drop_scan(
971 ipc_importance_lock();
973 /* process all queued task drops with timestamps up to TARGET(first)+SKEW */
974 ipc_importance_task_process_updates(&ipc_importance_delayed_drop_queue
,
976 ipc_importance_delayed_drop_timestamp
);
978 /* importance lock may have been temporarily dropped */
980 /* If there are any entries left in the queue, re-arm the call here */
981 if (!queue_empty(&ipc_importance_delayed_drop_queue
)) {
982 ipc_importance_task_t task_imp
;
986 task_imp
= (ipc_importance_task_t
)queue_first(&ipc_importance_delayed_drop_queue
);
988 nanoseconds_to_absolutetime(DENAP_DROP_DELAY
, &deadline
);
989 deadline
+= task_imp
->iit_updatetime
;
990 ipc_importance_delayed_drop_timestamp
= deadline
;
992 nanoseconds_to_absolutetime(DENAP_DROP_LEEWAY
, &leeway
);
994 thread_call_enter_delayed_with_leeway(
995 ipc_importance_delayed_drop_call
,
1001 ipc_importance_delayed_drop_call_requested
= FALSE
;
1003 ipc_importance_unlock();
1007 * Routine: ipc_importance_task_delayed_drop
1009 * Queue the specified task importance for delayed policy
1010 * update to drop its boost.
1012 * Called with the importance lock held.
1015 ipc_importance_task_delayed_drop(ipc_importance_task_t task_imp
)
1017 uint64_t timestamp
= mach_absolute_time(); /* no mach_approximate_time() in kernel */
1019 assert(ipc_importance_delayed_drop_call
!= NULL
);
1022 * If still on an update queue from a previous change,
1023 * remove it first (and use that reference). Otherwise, take
1024 * a new reference for the delay drop update queue.
1026 if (NULL
!= task_imp
->iit_updateq
) {
1027 queue_remove(task_imp
->iit_updateq
, task_imp
,
1028 ipc_importance_task_t
, iit_updates
);
1030 ipc_importance_task_reference_internal(task_imp
);
1033 task_imp
->iit_updateq
= &ipc_importance_delayed_drop_queue
;
1034 task_imp
->iit_updatetime
= timestamp
;
1036 queue_enter(&ipc_importance_delayed_drop_queue
, task_imp
,
1037 ipc_importance_task_t
, iit_updates
);
1039 /* request the delayed thread-call if not already requested */
1040 if (!ipc_importance_delayed_drop_call_requested
) {
1044 nanoseconds_to_absolutetime(DENAP_DROP_DELAY
, &deadline
);
1045 deadline
+= task_imp
->iit_updatetime
;
1046 ipc_importance_delayed_drop_timestamp
= deadline
;
1048 nanoseconds_to_absolutetime(DENAP_DROP_LEEWAY
, &leeway
);
1050 ipc_importance_delayed_drop_call_requested
= TRUE
;
1051 thread_call_enter_delayed_with_leeway(
1052 ipc_importance_delayed_drop_call
,
1062 * Routine: ipc_importance_task_propagate_assertion_locked
1064 * Propagate the importance transition type to every item
1065 * If this causes a boost to be applied, determine if that
1066 * boost should propagate downstream.
1068 * Called with the importance lock held.
1071 ipc_importance_task_propagate_assertion_locked(
1072 ipc_importance_task_t task_imp
,
1073 iit_update_type_t type
,
1074 boolean_t update_task_imp
)
1076 boolean_t boost
= (IIT_UPDATE_HOLD
== type
);
1077 ipc_importance_task_t temp_task_imp
;
1078 queue_head_t propagate
;
1079 queue_head_t updates
;
1081 queue_init(&updates
);
1082 queue_init(&propagate
);
1084 ipc_importance_assert_held();
1087 * If we're going to update the policy for the provided task,
1088 * enqueue it on the propagate queue itself. Otherwise, only
1089 * enqueue downstream things.
1091 if (update_task_imp
) {
1092 ipc_importance_task_reference(task_imp
);
1093 incr_ref_counter(task_imp
->iit_elem
.iie_task_refs_added_transition
);
1094 queue_enter(&propagate
, task_imp
, ipc_importance_task_t
, iit_props
);
1096 ipc_importance_task_propagate_helper(task_imp
, type
, &propagate
);
1100 * for each item on the propagation list, propagate any change downstream,
1101 * adding new tasks to propagate further if they transistioned as well.
1103 while (!queue_empty(&propagate
)) {
1104 boolean_t need_update
;
1106 queue_remove_first(&propagate
, temp_task_imp
, ipc_importance_task_t
, iit_props
);
1107 /* hold a reference on temp_task_imp */
1109 assert(IIT_NULL
!= temp_task_imp
);
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
);
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
) {
1123 * If a downstream task that needs an update is subjects to AppNap,
1124 * drop boosts according to the delay hysteresis. Otherwise,
1125 * immediate update it.
1127 if (!boost
&& temp_task_imp
!= task_imp
&&
1128 ipc_importance_delayed_drop_call
!= NULL
&&
1129 ipc_importance_task_is_marked_denap_receiver(temp_task_imp
)) {
1130 ipc_importance_task_delayed_drop(temp_task_imp
);
1132 temp_task_imp
->iit_updatetime
= 0;
1133 temp_task_imp
->iit_updateq
= &updates
;
1134 ipc_importance_task_reference_internal(temp_task_imp
);
1136 queue_enter(&updates
, temp_task_imp
,
1137 ipc_importance_task_t
, iit_updates
);
1139 queue_enter_first(&updates
, temp_task_imp
,
1140 ipc_importance_task_t
, iit_updates
);
1144 /* Must already be on the AppNap hysteresis queue */
1145 assert(ipc_importance_delayed_drop_call
!= NULL
);
1146 assert(ipc_importance_task_is_marked_denap_receiver(temp_task_imp
));
1150 ipc_importance_task_release_internal(temp_task_imp
);
1153 /* apply updates to task (may drop importance lock) */
1154 if (!queue_empty(&updates
)) {
1155 ipc_importance_task_process_updates(&updates
, boost
, 0);
1160 * Routine: ipc_importance_task_hold_internal_assertion_locked
1162 * Increment the assertion count on the task importance.
1163 * If this results in a boost state change in that task,
1164 * prepare to update task policy for this task AND, if
1165 * if not just waking out of App Nap, all down-stream
1166 * tasks that have a similar transition through inheriting
1169 * importance locked on entry and exit.
1170 * May temporarily drop importance lock and block.
1172 static kern_return_t
1173 ipc_importance_task_hold_internal_assertion_locked(ipc_importance_task_t task_imp
, uint32_t count
)
1175 if (ipc_importance_task_check_transition(task_imp
, IIT_UPDATE_HOLD
, count
)) {
1176 ipc_importance_task_propagate_assertion_locked(task_imp
, IIT_UPDATE_HOLD
, TRUE
);
1178 return KERN_SUCCESS
;
1182 * Routine: ipc_importance_task_drop_internal_assertion_locked
1184 * Decrement the assertion count on the task importance.
1185 * If this results in a boost state change in that task,
1186 * prepare to update task policy for this task AND, if
1187 * if not just waking out of App Nap, all down-stream
1188 * tasks that have a similar transition through inheriting
1191 * importance locked on entry and exit.
1192 * May temporarily drop importance lock and block.
1194 static kern_return_t
1195 ipc_importance_task_drop_internal_assertion_locked(ipc_importance_task_t task_imp
, uint32_t count
)
1197 if (ipc_importance_task_check_transition(task_imp
, IIT_UPDATE_DROP
, count
)) {
1198 ipc_importance_task_propagate_assertion_locked(task_imp
, IIT_UPDATE_DROP
, TRUE
);
1200 return KERN_SUCCESS
;
1204 * Routine: ipc_importance_task_hold_internal_assertion
1206 * Increment the assertion count on the task importance.
1207 * If this results in a 0->1 change in that count,
1208 * prepare to update task policy for this task AND
1209 * (potentially) all down-stream tasks that have a
1210 * similar transition through inheriting this update.
1213 * May block after dropping importance lock.
1216 ipc_importance_task_hold_internal_assertion(ipc_importance_task_t task_imp
, uint32_t count
)
1218 int ret
= KERN_SUCCESS
;
1220 if (ipc_importance_task_is_any_receiver_type(task_imp
)) {
1221 ipc_importance_lock();
1222 ret
= ipc_importance_task_hold_internal_assertion_locked(task_imp
, count
);
1223 ipc_importance_unlock();
1229 * Routine: ipc_importance_task_drop_internal_assertion
1231 * Decrement the assertion count on the task importance.
1232 * If this results in a X->0 change in that count,
1233 * prepare to update task policy for this task AND
1234 * all down-stream tasks that have a similar transition
1235 * through inheriting this drop update.
1237 * Nothing locked on entry.
1238 * May block after dropping importance lock.
1241 ipc_importance_task_drop_internal_assertion(ipc_importance_task_t task_imp
, uint32_t count
)
1243 kern_return_t ret
= KERN_SUCCESS
;
1245 if (ipc_importance_task_is_any_receiver_type(task_imp
)) {
1246 ipc_importance_lock();
1247 ret
= ipc_importance_task_drop_internal_assertion_locked(task_imp
, count
);
1248 ipc_importance_unlock();
1254 * Routine: ipc_importance_task_hold_file_lock_assertion
1256 * Increment the file lock assertion count on the task importance.
1257 * If this results in a 0->1 change in that count,
1258 * prepare to update task policy for this task AND
1259 * (potentially) all down-stream tasks that have a
1260 * similar transition through inheriting this update.
1263 * May block after dropping importance lock.
1266 ipc_importance_task_hold_file_lock_assertion(ipc_importance_task_t task_imp
, uint32_t count
)
1268 kern_return_t ret
= KERN_SUCCESS
;
1270 if (ipc_importance_task_is_any_receiver_type(task_imp
)) {
1271 ipc_importance_lock();
1272 ret
= ipc_importance_task_hold_internal_assertion_locked(task_imp
, count
);
1273 if (KERN_SUCCESS
== ret
) {
1274 task_imp
->iit_filelocks
+= count
;
1276 ipc_importance_unlock();
1282 * Routine: ipc_importance_task_drop_file_lock_assertion
1284 * Decrement the assertion count on the task importance.
1285 * If this results in a X->0 change in that count,
1286 * prepare to update task policy for this task AND
1287 * all down-stream tasks that have a similar transition
1288 * through inheriting this drop update.
1290 * Nothing locked on entry.
1291 * May block after dropping importance lock.
1294 ipc_importance_task_drop_file_lock_assertion(ipc_importance_task_t task_imp
, uint32_t count
)
1296 kern_return_t ret
= KERN_SUCCESS
;
1298 if (ipc_importance_task_is_any_receiver_type(task_imp
)) {
1299 ipc_importance_lock();
1300 if (count
<= task_imp
->iit_filelocks
) {
1301 task_imp
->iit_filelocks
-= count
;
1302 ret
= ipc_importance_task_drop_internal_assertion_locked(task_imp
, count
);
1304 ret
= KERN_INVALID_ARGUMENT
;
1306 ipc_importance_unlock();
1312 * Routine: ipc_importance_task_hold_legacy_external_assertion
1314 * Increment the external assertion count on the task importance.
1315 * This cannot result in an 0->1 transition, as the caller must
1316 * already hold an external boost.
1318 * Nothing locked on entry.
1319 * May block after dropping importance lock.
1320 * A queue of task importance structures is returned
1321 * by ipc_importance_task_hold_assertion_locked(). Each
1322 * needs to be updated (outside the importance lock hold).
1325 ipc_importance_task_hold_legacy_external_assertion(ipc_importance_task_t task_imp
, uint32_t count
)
1328 uint32_t target_assertcnt
;
1329 uint32_t target_externcnt
;
1330 uint32_t target_legacycnt
;
1334 ipc_importance_lock();
1335 target_task
= task_imp
->iit_task
;
1337 #if IMPORTANCE_TRACE
1338 int target_pid
= task_pid(target_task
);
1340 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, (IMP_HOLD
| TASK_POLICY_EXTERNAL
))) | DBG_FUNC_START
,
1341 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_LEGACY_EXTERN(task_imp
), 0);
1344 if (IIT_LEGACY_EXTERN(task_imp
) == 0) {
1345 /* Only allowed to take a new boost assertion when holding an external boost */
1346 /* save data for diagnostic printf below */
1347 target_assertcnt
= task_imp
->iit_assertcnt
;
1348 target_externcnt
= IIT_EXTERN(task_imp
);
1349 target_legacycnt
= IIT_LEGACY_EXTERN(task_imp
);
1353 assert(ipc_importance_task_is_any_receiver_type(task_imp
));
1354 assert(0 < task_imp
->iit_assertcnt
);
1355 assert(0 < IIT_EXTERN(task_imp
));
1356 task_imp
->iit_assertcnt
+= count
;
1357 task_imp
->iit_externcnt
+= count
;
1358 task_imp
->iit_legacy_externcnt
+= count
;
1361 ipc_importance_unlock();
1363 #if IMPORTANCE_TRACE
1364 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, (IMP_HOLD
| TASK_POLICY_EXTERNAL
))) | DBG_FUNC_END
,
1365 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_LEGACY_EXTERN(task_imp
), 0);
1366 // This covers the legacy case where a task takes an extra boost.
1367 DTRACE_BOOST5(receive_boost
, task_t
, target_task
, int, target_pid
, int, proc_selfpid(), int, count
, int, task_imp
->iit_assertcnt
);
1370 if (KERN_FAILURE
== ret
&& target_task
!= TASK_NULL
) {
1371 printf("BUG in process %s[%d]: "
1372 "attempt to acquire an additional legacy external boost assertion without holding an existing legacy external assertion. "
1373 "(%d total, %d external, %d legacy-external)\n",
1374 proc_name_address(target_task
->bsd_info
), task_pid(target_task
),
1375 target_assertcnt
, target_externcnt
, target_legacycnt
);
1382 * Routine: ipc_importance_task_drop_legacy_external_assertion
1384 * Drop the legacy external assertion count on the task and
1385 * reflect that change to total external assertion count and
1386 * then onto the internal importance count.
1388 * If this results in a X->0 change in the internal,
1389 * count, prepare to update task policy for this task AND
1390 * all down-stream tasks that have a similar transition
1391 * through inheriting this update.
1393 * Nothing locked on entry.
1396 ipc_importance_task_drop_legacy_external_assertion(ipc_importance_task_t task_imp
, uint32_t count
)
1398 int ret
= KERN_SUCCESS
;
1400 uint32_t target_assertcnt
;
1401 uint32_t target_externcnt
;
1402 uint32_t target_legacycnt
;
1405 return KERN_INVALID_ARGUMENT
;
1408 ipc_importance_lock();
1409 target_task
= task_imp
->iit_task
;
1411 #if IMPORTANCE_TRACE
1412 int target_pid
= task_pid(target_task
);
1414 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, (IMP_DROP
| TASK_POLICY_EXTERNAL
))) | DBG_FUNC_START
,
1415 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_LEGACY_EXTERN(task_imp
), 0);
1418 if (count
> IIT_LEGACY_EXTERN(task_imp
)) {
1419 /* Process over-released its boost count - save data for diagnostic printf */
1420 /* TODO: If count > 1, we should clear out as many external assertions as there are left. */
1421 target_assertcnt
= task_imp
->iit_assertcnt
;
1422 target_externcnt
= IIT_EXTERN(task_imp
);
1423 target_legacycnt
= IIT_LEGACY_EXTERN(task_imp
);
1427 * decrement legacy external count from the top level and reflect
1428 * into internal for this and all subsequent updates.
1430 assert(ipc_importance_task_is_any_receiver_type(task_imp
));
1431 assert(IIT_EXTERN(task_imp
) >= count
);
1433 task_imp
->iit_legacy_externdrop
+= count
;
1434 task_imp
->iit_externdrop
+= count
;
1436 /* reset extern counters (if appropriate) */
1437 if (IIT_LEGACY_EXTERN(task_imp
) == 0) {
1438 if (IIT_EXTERN(task_imp
) != 0) {
1439 task_imp
->iit_externcnt
-= task_imp
->iit_legacy_externcnt
;
1440 task_imp
->iit_externdrop
-= task_imp
->iit_legacy_externdrop
;
1442 task_imp
->iit_externcnt
= 0;
1443 task_imp
->iit_externdrop
= 0;
1445 task_imp
->iit_legacy_externcnt
= 0;
1446 task_imp
->iit_legacy_externdrop
= 0;
1449 /* reflect the drop to the internal assertion count (and effect any importance change) */
1450 if (ipc_importance_task_check_transition(task_imp
, IIT_UPDATE_DROP
, count
)) {
1451 ipc_importance_task_propagate_assertion_locked(task_imp
, IIT_UPDATE_DROP
, TRUE
);
1456 #if IMPORTANCE_TRACE
1457 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, (IMP_DROP
| TASK_POLICY_EXTERNAL
))) | DBG_FUNC_END
,
1458 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_LEGACY_EXTERN(task_imp
), 0);
1461 ipc_importance_unlock();
1463 /* delayed printf for user-supplied data failures */
1464 if (KERN_FAILURE
== ret
&& TASK_NULL
!= target_task
) {
1465 printf("BUG in process %s[%d]: over-released legacy external boost assertions (%d total, %d external, %d legacy-external)\n",
1466 proc_name_address(target_task
->bsd_info
), task_pid(target_task
),
1467 target_assertcnt
, target_externcnt
, target_legacycnt
);
1474 #if LEGACY_IMPORTANCE_DELIVERY
1475 /* Transfer an assertion to legacy userspace responsibility */
1476 static kern_return_t
1477 ipc_importance_task_externalize_legacy_assertion(ipc_importance_task_t task_imp
, uint32_t count
, __unused
int sender_pid
)
1481 assert(IIT_NULL
!= task_imp
);
1482 target_task
= task_imp
->iit_task
;
1484 if (TASK_NULL
== target_task
||
1485 !ipc_importance_task_is_any_receiver_type(task_imp
)) {
1486 return KERN_FAILURE
;
1489 #if IMPORTANCE_TRACE
1490 int target_pid
= task_pid(target_task
);
1492 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, IMP_EXTERN
)) | DBG_FUNC_START
,
1493 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_EXTERN(task_imp
), 0);
1496 ipc_importance_lock();
1497 /* assert(task_imp->iit_assertcnt >= IIT_EXTERN(task_imp) + count); */
1498 assert(IIT_EXTERN(task_imp
) >= IIT_LEGACY_EXTERN(task_imp
));
1499 task_imp
->iit_legacy_externcnt
+= count
;
1500 task_imp
->iit_externcnt
+= count
;
1501 ipc_importance_unlock();
1503 #if IMPORTANCE_TRACE
1504 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, IMP_EXTERN
)) | DBG_FUNC_END
,
1505 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_LEGACY_EXTERN(task_imp
), 0);
1506 // This is the legacy boosting path
1507 DTRACE_BOOST5(receive_boost
, task_t
, target_task
, int, target_pid
, int, sender_pid
, int, count
, int, IIT_LEGACY_EXTERN(task_imp
));
1508 #endif /* IMPORTANCE_TRACE */
1510 return KERN_SUCCESS
;
1512 #endif /* LEGACY_IMPORTANCE_DELIVERY */
1515 * Routine: ipc_importance_task_update_live_donor
1517 * Read the live donor status and update the live_donor bit/propagate the change in importance.
1519 * Nothing locked on entrance, nothing locked on exit.
1521 * TODO: Need tracepoints around this function...
1524 ipc_importance_task_update_live_donor(ipc_importance_task_t task_imp
)
1526 uint32_t task_live_donor
;
1527 boolean_t before_donor
;
1528 boolean_t after_donor
;
1531 assert(task_imp
!= NULL
);
1534 * Nothing to do if the task is not marked as expecting
1535 * live donor updates.
1537 if (!ipc_importance_task_is_marked_live_donor(task_imp
)) {
1541 ipc_importance_lock();
1543 /* If the task got disconnected on the way here, no use (or ability) adjusting live donor status */
1544 target_task
= task_imp
->iit_task
;
1545 if (TASK_NULL
== target_task
) {
1546 ipc_importance_unlock();
1549 before_donor
= ipc_importance_task_is_marked_donor(task_imp
);
1551 /* snapshot task live donor status - may change, but another call will accompany the change */
1552 task_live_donor
= target_task
->effective_policy
.tep_live_donor
;
1554 #if IMPORTANCE_TRACE
1555 int target_pid
= task_pid(target_task
);
1557 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1558 (IMPORTANCE_CODE(IMP_DONOR_CHANGE
, IMP_DONOR_UPDATE_LIVE_DONOR_STATE
)) | DBG_FUNC_START
,
1559 target_pid
, task_imp
->iit_donor
, task_live_donor
, before_donor
, 0);
1562 /* update the task importance live donor status based on the task's value */
1563 task_imp
->iit_donor
= task_live_donor
;
1565 after_donor
= ipc_importance_task_is_marked_donor(task_imp
);
1567 /* Has the effectiveness of being a donor changed as a result of this update? */
1568 if (before_donor
!= after_donor
) {
1569 iit_update_type_t type
;
1571 /* propagate assertions without updating the current task policy (already handled) */
1572 if (0 == before_donor
) {
1573 task_imp
->iit_transitions
++;
1574 type
= IIT_UPDATE_HOLD
;
1576 type
= IIT_UPDATE_DROP
;
1578 ipc_importance_task_propagate_assertion_locked(task_imp
, type
, FALSE
);
1581 #if IMPORTANCE_TRACE
1582 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1583 (IMPORTANCE_CODE(IMP_DONOR_CHANGE
, IMP_DONOR_UPDATE_LIVE_DONOR_STATE
)) | DBG_FUNC_END
,
1584 target_pid
, task_imp
->iit_donor
, task_live_donor
, after_donor
, 0);
1587 ipc_importance_unlock();
1592 * Routine: ipc_importance_task_mark_donor
1594 * Set the task importance donor flag.
1596 * Nothing locked on entrance, nothing locked on exit.
1598 * This is only called while the task is being constructed,
1599 * so no need to update task policy or propagate downstream.
1602 ipc_importance_task_mark_donor(ipc_importance_task_t task_imp
, boolean_t donating
)
1604 assert(task_imp
!= NULL
);
1606 ipc_importance_lock();
1608 int old_donor
= task_imp
->iit_donor
;
1610 task_imp
->iit_donor
= (donating
? 1 : 0);
1612 if (task_imp
->iit_donor
> 0 && old_donor
== 0) {
1613 task_imp
->iit_transitions
++;
1616 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1617 (IMPORTANCE_CODE(IMP_DONOR_CHANGE
, IMP_DONOR_INIT_DONOR_STATE
)) | DBG_FUNC_NONE
,
1618 task_pid(task_imp
->iit_task
), donating
,
1619 old_donor
, task_imp
->iit_donor
, 0);
1621 ipc_importance_unlock();
1625 * Routine: ipc_importance_task_marked_donor
1627 * Query the donor flag for the given task importance.
1629 * May be called without taking the importance lock.
1630 * In that case, donor status can change so you must
1631 * check only once for each donation event.
1634 ipc_importance_task_is_marked_donor(ipc_importance_task_t task_imp
)
1636 if (IIT_NULL
== task_imp
) {
1639 return 0 != task_imp
->iit_donor
;
1643 * Routine: ipc_importance_task_mark_live_donor
1645 * Indicate that the task is eligible for live donor updates.
1647 * Nothing locked on entrance, nothing locked on exit.
1649 * This is only called while the task is being constructed.
1652 ipc_importance_task_mark_live_donor(ipc_importance_task_t task_imp
, boolean_t live_donating
)
1654 assert(task_imp
!= NULL
);
1656 ipc_importance_lock();
1657 task_imp
->iit_live_donor
= (live_donating
? 1 : 0);
1658 ipc_importance_unlock();
1662 * Routine: ipc_importance_task_is_marked_live_donor
1664 * Query the live donor and donor flags for the given task importance.
1666 * May be called without taking the importance lock.
1667 * In that case, donor status can change so you must
1668 * check only once for each donation event.
1671 ipc_importance_task_is_marked_live_donor(ipc_importance_task_t task_imp
)
1673 if (IIT_NULL
== task_imp
) {
1676 return 0 != task_imp
->iit_live_donor
;
1680 * Routine: ipc_importance_task_is_donor
1682 * Query the full donor status for the given task importance.
1684 * May be called without taking the importance lock.
1685 * In that case, donor status can change so you must
1686 * check only once for each donation event.
1689 ipc_importance_task_is_donor(ipc_importance_task_t task_imp
)
1691 if (IIT_NULL
== task_imp
) {
1694 return ipc_importance_task_is_marked_donor(task_imp
) ||
1695 (ipc_importance_task_is_marked_receiver(task_imp
) &&
1696 task_imp
->iit_assertcnt
> 0);
1700 * Routine: ipc_importance_task_is_never_donor
1702 * Query if a given task can ever donate importance.
1704 * May be called without taking the importance lock.
1705 * Condition is permanent for a give task.
1708 ipc_importance_task_is_never_donor(ipc_importance_task_t task_imp
)
1710 if (IIT_NULL
== task_imp
) {
1713 return !ipc_importance_task_is_marked_donor(task_imp
) &&
1714 !ipc_importance_task_is_marked_live_donor(task_imp
) &&
1715 !ipc_importance_task_is_marked_receiver(task_imp
);
1719 * Routine: ipc_importance_task_mark_receiver
1721 * Update the task importance receiver flag.
1723 * Nothing locked on entrance, nothing locked on exit.
1724 * This can only be invoked before the task is discoverable,
1725 * so no worries about atomicity(?)
1728 ipc_importance_task_mark_receiver(ipc_importance_task_t task_imp
, boolean_t receiving
)
1730 assert(task_imp
!= NULL
);
1732 ipc_importance_lock();
1734 assert(task_imp
->iit_assertcnt
== 0);
1735 assert(task_imp
->iit_externcnt
== 0);
1736 assert(task_imp
->iit_externdrop
== 0);
1737 assert(task_imp
->iit_denap
== 0);
1738 task_imp
->iit_receiver
= 1; /* task can receive importance boost */
1739 } else if (task_imp
->iit_receiver
) {
1740 assert(task_imp
->iit_denap
== 0);
1741 if (task_imp
->iit_assertcnt
!= 0 || IIT_EXTERN(task_imp
) != 0) {
1742 panic("disabling imp_receiver on task with pending importance boosts!");
1744 task_imp
->iit_receiver
= 0;
1746 ipc_importance_unlock();
1751 * Routine: ipc_importance_task_marked_receiver
1753 * Query the receiver flag for the given task importance.
1755 * May be called without taking the importance lock as
1756 * the importance flag can never change after task init.
1759 ipc_importance_task_is_marked_receiver(ipc_importance_task_t task_imp
)
1761 return IIT_NULL
!= task_imp
&& 0 != task_imp
->iit_receiver
;
1766 * Routine: ipc_importance_task_mark_denap_receiver
1768 * Update the task importance de-nap receiver flag.
1770 * Nothing locked on entrance, nothing locked on exit.
1771 * This can only be invoked before the task is discoverable,
1772 * so no worries about atomicity(?)
1775 ipc_importance_task_mark_denap_receiver(ipc_importance_task_t task_imp
, boolean_t denap
)
1777 assert(task_imp
!= NULL
);
1779 ipc_importance_lock();
1781 assert(task_imp
->iit_assertcnt
== 0);
1782 assert(task_imp
->iit_externcnt
== 0);
1783 assert(task_imp
->iit_receiver
== 0);
1784 task_imp
->iit_denap
= 1; /* task can receive de-nap boost */
1785 } else if (task_imp
->iit_denap
) {
1786 assert(task_imp
->iit_receiver
== 0);
1787 if (0 < task_imp
->iit_assertcnt
|| 0 < IIT_EXTERN(task_imp
)) {
1788 panic("disabling de-nap on task with pending de-nap boosts!");
1790 task_imp
->iit_denap
= 0;
1792 ipc_importance_unlock();
1797 * Routine: ipc_importance_task_marked_denap_receiver
1799 * Query the de-nap receiver flag for the given task importance.
1801 * May be called without taking the importance lock as
1802 * the de-nap flag can never change after task init.
1805 ipc_importance_task_is_marked_denap_receiver(ipc_importance_task_t task_imp
)
1807 return IIT_NULL
!= task_imp
&& 0 != task_imp
->iit_denap
;
1811 * Routine: ipc_importance_task_is_denap_receiver
1813 * Query the full de-nap receiver status for the given task importance.
1814 * For now, that is simply whether the receiver flag is set.
1816 * May be called without taking the importance lock as
1817 * the de-nap receiver flag can never change after task init.
1820 ipc_importance_task_is_denap_receiver(ipc_importance_task_t task_imp
)
1822 return ipc_importance_task_is_marked_denap_receiver(task_imp
);
1826 * Routine: ipc_importance_task_is_any_receiver_type
1828 * Query if the task is marked to receive boosts - either
1829 * importance or denap.
1831 * May be called without taking the importance lock as both
1832 * the importance and de-nap receiver flags can never change
1836 ipc_importance_task_is_any_receiver_type(ipc_importance_task_t task_imp
)
1838 return ipc_importance_task_is_marked_receiver(task_imp
) ||
1839 ipc_importance_task_is_marked_denap_receiver(task_imp
);
1842 #if 0 /* currently unused */
1845 * Routine: ipc_importance_inherit_reference
1847 * Add a reference to the inherit importance element.
1849 * Caller most hold a reference on the inherit element.
1852 ipc_importance_inherit_reference(ipc_importance_inherit_t inherit
)
1854 ipc_importance_reference(&inherit
->iii_elem
);
1856 #endif /* currently unused */
1859 * Routine: ipc_importance_inherit_release_locked
1861 * Release a reference on an inherit importance attribute value,
1862 * unlinking and deallocating the attribute if the last reference.
1864 * Entered with importance lock held, leaves with it unlocked.
1867 ipc_importance_inherit_release_locked(ipc_importance_inherit_t inherit
)
1869 ipc_importance_release_locked(&inherit
->iii_elem
);
1872 #if 0 /* currently unused */
1874 * Routine: ipc_importance_inherit_release
1876 * Release a reference on an inherit importance attribute value,
1877 * unlinking and deallocating the attribute if the last reference.
1879 * nothing locked on entrance, nothing locked on exit.
1883 ipc_importance_inherit_release(ipc_importance_inherit_t inherit
)
1885 if (III_NULL
!= inherit
) {
1886 ipc_importance_release(&inherit
->iii_elem
);
1889 #endif /* 0 currently unused */
1892 * Routine: ipc_importance_for_task
1894 * Create a reference for the specified task's base importance
1895 * element. If the base importance element doesn't exist, make it and
1896 * bind it to the active task. If the task is inactive, there isn't
1897 * any need to return a new reference.
1899 * If made is true, a "made" reference is returned (for donating to
1900 * the voucher system). Otherwise an internal reference is returned.
1902 * Nothing locked on entry. May block.
1904 ipc_importance_task_t
1905 ipc_importance_for_task(task_t task
, boolean_t made
)
1907 ipc_importance_task_t task_elem
;
1908 boolean_t first_pass
= TRUE
;
1910 assert(TASK_NULL
!= task
);
1913 /* No use returning anything for inactive task */
1914 if (!task
->active
) {
1918 ipc_importance_lock();
1919 task_elem
= task
->task_imp_base
;
1920 if (IIT_NULL
!= task_elem
) {
1921 /* Add a made reference (borrowing active task ref to do it) */
1923 if (0 == task_elem
->iit_made
++) {
1924 assert(IIT_REFS_MAX
> IIT_REFS(task_elem
));
1925 ipc_importance_task_reference_internal(task_elem
);
1928 assert(IIT_REFS_MAX
> IIT_REFS(task_elem
));
1929 ipc_importance_task_reference_internal(task_elem
);
1931 ipc_importance_unlock();
1934 ipc_importance_unlock();
1941 /* Need to make one - may race with others (be prepared to drop) */
1942 task_elem
= zalloc_flags(ipc_importance_task_zone
, Z_WAITOK
| Z_ZERO
);
1943 if (IIT_NULL
== task_elem
) {
1947 task_elem
->iit_bits
= IIE_TYPE_TASK
| 2; /* one for task, one for return/made */
1948 task_elem
->iit_made
= (made
) ? 1 : 0;
1949 task_elem
->iit_task
= task
; /* take actual ref when we're sure */
1951 ipc_importance_counter_init(&task_elem
->iit_elem
);
1953 queue_init(&task_elem
->iit_kmsgs
);
1954 queue_init(&task_elem
->iit_inherits
);
1956 ipc_importance_lock();
1957 if (!task
->active
) {
1958 ipc_importance_unlock();
1959 zfree(ipc_importance_task_zone
, task_elem
);
1963 /* did we lose the race? */
1964 if (IIT_NULL
!= task
->task_imp_base
) {
1965 ipc_importance_unlock();
1966 zfree(ipc_importance_task_zone
, task_elem
);
1970 /* we won the race */
1971 task
->task_imp_base
= task_elem
;
1972 task_reference(task
);
1973 #if DEVELOPMENT || DEBUG
1974 queue_enter(&global_iit_alloc_queue
, task_elem
, ipc_importance_task_t
, iit_allocation
);
1975 task_importance_update_owner_info(task
);
1977 ipc_importance_unlock();
1982 #if DEVELOPMENT || DEBUG
1984 task_importance_update_owner_info(task_t task
)
1986 if (task
!= TASK_NULL
&& task
->task_imp_base
!= IIT_NULL
) {
1987 ipc_importance_task_t task_elem
= task
->task_imp_base
;
1989 task_elem
->iit_bsd_pid
= task_pid(task
);
1990 if (task
->bsd_info
) {
1991 strncpy(&task_elem
->iit_procname
[0], proc_name_address(task
->bsd_info
), 16);
1992 task_elem
->iit_procname
[16] = '\0';
1994 strncpy(&task_elem
->iit_procname
[0], "unknown", 16);
2001 * Routine: ipc_importance_reset_locked
2003 * Reset a task's IPC importance (the task is going away or exec'ing)
2005 * Remove the donor bit and legacy externalized assertions from the
2006 * current task importance and see if that wipes out downstream donations.
2008 * importance lock held.
2012 ipc_importance_reset_locked(ipc_importance_task_t task_imp
, boolean_t donor
)
2014 boolean_t before_donor
, after_donor
;
2016 /* remove the donor bit, live-donor bit and externalized boosts */
2017 before_donor
= ipc_importance_task_is_donor(task_imp
);
2019 task_imp
->iit_donor
= 0;
2021 assert(IIT_LEGACY_EXTERN(task_imp
) <= IIT_EXTERN(task_imp
));
2022 assert(task_imp
->iit_legacy_externcnt
<= task_imp
->iit_externcnt
);
2023 assert(task_imp
->iit_legacy_externdrop
<= task_imp
->iit_externdrop
);
2024 task_imp
->iit_externcnt
-= task_imp
->iit_legacy_externcnt
;
2025 task_imp
->iit_externdrop
-= task_imp
->iit_legacy_externdrop
;
2027 /* assert(IIT_LEGACY_EXTERN(task_imp) <= task_imp->iit_assertcnt); */
2028 if (IIT_EXTERN(task_imp
) < task_imp
->iit_assertcnt
) {
2029 task_imp
->iit_assertcnt
-= IIT_LEGACY_EXTERN(task_imp
);
2031 task_imp
->iit_assertcnt
= IIT_EXTERN(task_imp
);
2033 task_imp
->iit_legacy_externcnt
= 0;
2034 task_imp
->iit_legacy_externdrop
= 0;
2035 after_donor
= ipc_importance_task_is_donor(task_imp
);
2037 #if DEVELOPMENT || DEBUG
2038 if (task_imp
->iit_assertcnt
> 0 && task_imp
->iit_live_donor
) {
2039 printf("Live donor task %s[%d] still has %d importance assertions after reset\n",
2040 task_imp
->iit_procname
, task_imp
->iit_bsd_pid
, task_imp
->iit_assertcnt
);
2044 /* propagate a downstream drop if there was a change in donor status */
2045 if (after_donor
!= before_donor
) {
2046 ipc_importance_task_propagate_assertion_locked(task_imp
, IIT_UPDATE_DROP
, FALSE
);
2051 * Routine: ipc_importance_reset
2053 * Reset a task's IPC importance
2055 * The task is being reset, although staying around. Arrange to have the
2056 * external state of the task reset from the importance.
2058 * importance lock not held.
2062 ipc_importance_reset(ipc_importance_task_t task_imp
, boolean_t donor
)
2064 if (IIT_NULL
== task_imp
) {
2067 ipc_importance_lock();
2068 ipc_importance_reset_locked(task_imp
, donor
);
2069 ipc_importance_unlock();
2073 * Routine: ipc_importance_disconnect_task
2075 * Disconnect a task from its importance.
2077 * Clear the task pointer from the importance and drop the
2078 * reference the task held on the importance object. Before
2079 * doing that, reset the effects the current task holds on
2080 * the importance and see if that wipes out downstream donations.
2082 * We allow the upstream boosts to continue to affect downstream
2083 * even though the local task is being effectively pulled from
2089 ipc_importance_disconnect_task(task_t task
)
2091 ipc_importance_task_t task_imp
;
2094 ipc_importance_lock();
2095 task_imp
= task
->task_imp_base
;
2097 /* did somebody beat us to it? */
2098 if (IIT_NULL
== task_imp
) {
2099 ipc_importance_unlock();
2104 /* disconnect the task from this importance */
2105 assert(task_imp
->iit_task
== task
);
2106 task_imp
->iit_task
= TASK_NULL
;
2107 task
->task_imp_base
= IIT_NULL
;
2110 /* reset the effects the current task hold on the importance */
2111 ipc_importance_reset_locked(task_imp
, TRUE
);
2113 ipc_importance_task_release_locked(task_imp
);
2114 /* importance unlocked */
2116 /* deallocate the task now that the importance is unlocked */
2117 task_deallocate(task
);
2121 * Routine: ipc_importance_exec_switch_task
2123 * Switch importance task base from old task to new task in exec.
2125 * Create an ipc importance linkage from old task to new task,
2126 * once the linkage is created, switch the importance task base
2127 * from old task to new task. After the switch, the linkage will
2128 * represent importance linkage from new task to old task with
2129 * watch port importance inheritance linked to new task.
2132 * Returns a reference on importance inherit.
2134 ipc_importance_inherit_t
2135 ipc_importance_exec_switch_task(
2139 ipc_importance_inherit_t inherit
= III_NULL
;
2140 ipc_importance_task_t old_task_imp
= IIT_NULL
;
2141 ipc_importance_task_t new_task_imp
= IIT_NULL
;
2143 task_importance_reset(old_task
);
2145 /* Create an importance linkage from old_task to new_task */
2146 inherit
= ipc_importance_inherit_from_task(old_task
, new_task
);
2148 /* Switch task importance base from old task to new task */
2149 ipc_importance_lock();
2151 old_task_imp
= old_task
->task_imp_base
;
2152 new_task_imp
= new_task
->task_imp_base
;
2154 old_task_imp
->iit_task
= new_task
;
2155 new_task_imp
->iit_task
= old_task
;
2157 old_task
->task_imp_base
= new_task_imp
;
2158 new_task
->task_imp_base
= old_task_imp
;
2160 #if DEVELOPMENT || DEBUG
2162 * Update the pid an proc name for importance base if any
2164 task_importance_update_owner_info(new_task
);
2166 ipc_importance_unlock();
2172 * Routine: ipc_importance_check_circularity
2174 * Check if queueing "port" in a message for "dest"
2175 * would create a circular group of ports and messages.
2177 * If no circularity (FALSE returned), then "port"
2178 * is changed from "in limbo" to "in transit".
2180 * That is, we want to set port->ip_destination == dest,
2181 * but guaranteeing that this doesn't create a circle
2182 * port->ip_destination->ip_destination->... == port
2184 * Additionally, if port was successfully changed to "in transit",
2185 * propagate boost assertions from the "in limbo" port to all
2186 * the ports in the chain, and, if the destination task accepts
2187 * boosts, to the destination task.
2190 * No ports locked. References held for "port" and "dest".
2194 ipc_importance_check_circularity(
2198 ipc_importance_task_t imp_task
= IIT_NULL
;
2199 ipc_importance_task_t release_imp_task
= IIT_NULL
;
2200 boolean_t imp_lock_held
= FALSE
;
2203 struct turnstile
*send_turnstile
= TURNSTILE_NULL
;
2204 struct task_watchport_elem
*watchport_elem
= NULL
;
2206 assert(port
!= IP_NULL
);
2207 assert(dest
!= IP_NULL
);
2214 /* Check if destination needs a turnstile */
2215 ipc_port_send_turnstile_prepare(dest
);
2217 /* port is in limbo, so donation status is safe to latch */
2218 if (port
->ip_impdonation
!= 0) {
2219 imp_lock_held
= TRUE
;
2220 ipc_importance_lock();
2224 * First try a quick check that can run in parallel.
2225 * No circularity if dest is not in transit.
2230 * Even if port is just carrying assertions for others,
2231 * we need the importance lock.
2233 if (port
->ip_impcount
> 0 && !imp_lock_held
) {
2234 if (!ipc_importance_lock_try()) {
2236 ipc_importance_lock();
2239 imp_lock_held
= TRUE
;
2242 if (ip_lock_try(dest
)) {
2243 if (!ip_active(dest
) ||
2244 (dest
->ip_receiver_name
!= MACH_PORT_NULL
) ||
2245 (dest
->ip_destination
== IP_NULL
)) {
2249 /* dest is in transit; further checking necessary */
2256 * We're about to pay the cost to serialize,
2257 * just go ahead and grab importance lock.
2259 if (!imp_lock_held
) {
2260 ipc_importance_lock();
2261 imp_lock_held
= TRUE
;
2264 ipc_port_multiple_lock(); /* massive serialization */
2267 * Search for the end of the chain (a port not in transit),
2268 * acquiring locks along the way.
2274 if (!ip_active(base
) ||
2275 (base
->ip_receiver_name
!= MACH_PORT_NULL
) ||
2276 (base
->ip_destination
== IP_NULL
)) {
2280 base
= base
->ip_destination
;
2283 /* all ports in chain from dest to base, inclusive, are locked */
2286 /* circularity detected! */
2288 ipc_port_multiple_unlock();
2290 /* port (== base) is in limbo */
2292 require_ip_active(port
);
2293 assert(port
->ip_receiver_name
== MACH_PORT_NULL
);
2294 assert(port
->ip_destination
== IP_NULL
);
2297 while (base
!= IP_NULL
) {
2300 /* base is in transit or in limbo */
2302 require_ip_active(base
);
2303 assert(base
->ip_receiver_name
== MACH_PORT_NULL
);
2305 next
= base
->ip_destination
;
2310 if (imp_lock_held
) {
2311 ipc_importance_unlock();
2314 ipc_port_send_turnstile_complete(dest
);
2319 * The guarantee: lock port while the entire chain is locked.
2320 * Once port is locked, we can take a reference to dest,
2321 * add port to the chain, and unlock everything.
2325 ipc_port_multiple_unlock();
2328 /* port is in limbo */
2329 imq_lock(&port
->ip_messages
);
2331 require_ip_active(port
);
2332 assert(port
->ip_receiver_name
== MACH_PORT_NULL
);
2333 assert(port
->ip_destination
== IP_NULL
);
2335 /* Port is being enqueued in a kmsg, remove the watchport boost in order to push on destination port */
2336 watchport_elem
= ipc_port_clear_watchport_elem_internal(port
);
2338 /* Check if the port is being enqueued as a part of sync bootstrap checkin */
2339 if (dest
->ip_specialreply
&& dest
->ip_sync_bootstrap_checkin
) {
2340 port
->ip_sync_bootstrap_checkin
= 1;
2344 port
->ip_destination
= dest
;
2346 /* must have been in limbo or still bound to a task */
2347 assert(port
->ip_tempowner
!= 0);
2350 * We delayed dropping assertions from a specific task.
2351 * Cache that info now (we'll drop assertions and the
2352 * task reference below).
2354 release_imp_task
= port
->ip_imp_task
;
2355 if (IIT_NULL
!= release_imp_task
) {
2356 port
->ip_imp_task
= IIT_NULL
;
2358 assertcnt
= port
->ip_impcount
;
2360 /* take the port out of limbo w.r.t. assertions */
2361 port
->ip_tempowner
= 0;
2364 * Setup linkage for source port if it has a send turnstile i.e. it has
2365 * a thread waiting in send or has a port enqueued in it or has sync ipc
2366 * push from a special reply port.
2368 if (port_send_turnstile(port
)) {
2369 send_turnstile
= turnstile_prepare((uintptr_t)port
,
2370 port_send_turnstile_address(port
),
2371 TURNSTILE_NULL
, TURNSTILE_SYNC_IPC
);
2373 turnstile_update_inheritor(send_turnstile
, port_send_turnstile(dest
),
2374 (TURNSTILE_INHERITOR_TURNSTILE
| TURNSTILE_IMMEDIATE_UPDATE
));
2376 /* update complete and turnstile complete called after dropping all locks */
2378 imq_unlock(&port
->ip_messages
);
2380 /* now unlock chain */
2386 /* every port along chain track assertions behind it */
2387 ipc_port_impcount_delta(dest
, assertcnt
, base
);
2393 /* port is in transit */
2395 require_ip_active(dest
);
2396 assert(dest
->ip_receiver_name
== MACH_PORT_NULL
);
2397 assert(dest
->ip_destination
!= IP_NULL
);
2398 assert(dest
->ip_tempowner
== 0);
2400 next
= dest
->ip_destination
;
2405 /* base is not in transit */
2406 assert(!ip_active(base
) ||
2407 (base
->ip_receiver_name
!= MACH_PORT_NULL
) ||
2408 (base
->ip_destination
== IP_NULL
));
2411 * Find the task to boost (if any).
2412 * We will boost "through" ports that don't know
2413 * about inheritance to deliver receive rights that
2416 if (ip_active(base
) && (assertcnt
> 0)) {
2417 assert(imp_lock_held
);
2418 if (base
->ip_tempowner
!= 0) {
2419 if (IIT_NULL
!= base
->ip_imp_task
) {
2420 /* specified tempowner task */
2421 imp_task
= base
->ip_imp_task
;
2422 assert(ipc_importance_task_is_any_receiver_type(imp_task
));
2424 /* otherwise don't boost current task */
2425 } else if (base
->ip_receiver_name
!= MACH_PORT_NULL
) {
2426 ipc_space_t space
= base
->ip_receiver
;
2428 /* only spaces with boost-accepting tasks */
2429 if (space
->is_task
!= TASK_NULL
&&
2430 ipc_importance_task_is_any_receiver_type(space
->is_task
->task_imp_base
)) {
2431 imp_task
= space
->is_task
->task_imp_base
;
2435 /* take reference before unlocking base */
2436 if (imp_task
!= IIT_NULL
) {
2437 ipc_importance_task_reference(imp_task
);
2443 /* All locks dropped, call turnstile_update_inheritor_complete for source port's turnstile */
2444 if (send_turnstile
) {
2445 turnstile_update_inheritor_complete(send_turnstile
, TURNSTILE_INTERLOCK_NOT_HELD
);
2447 /* Take the mq lock to call turnstile complete */
2448 imq_lock(&port
->ip_messages
);
2449 turnstile_complete((uintptr_t)port
, port_send_turnstile_address(port
), NULL
, TURNSTILE_SYNC_IPC
);
2450 send_turnstile
= TURNSTILE_NULL
;
2451 imq_unlock(&port
->ip_messages
);
2452 turnstile_cleanup();
2456 * Transfer assertions now that the ports are unlocked.
2457 * Avoid extra overhead if transferring to/from the same task.
2459 * NOTE: If a transfer is occurring, the new assertions will
2460 * be added to imp_task BEFORE the importance lock is unlocked.
2461 * This is critical - to avoid decrements coming from the kmsgs
2462 * beating the increment to the task.
2464 boolean_t transfer_assertions
= (imp_task
!= release_imp_task
);
2466 if (imp_task
!= IIT_NULL
) {
2467 assert(imp_lock_held
);
2468 if (transfer_assertions
) {
2469 ipc_importance_task_hold_internal_assertion_locked(imp_task
, assertcnt
);
2473 if (release_imp_task
!= IIT_NULL
) {
2474 assert(imp_lock_held
);
2475 if (transfer_assertions
) {
2476 ipc_importance_task_drop_internal_assertion_locked(release_imp_task
, assertcnt
);
2480 if (imp_lock_held
) {
2481 ipc_importance_unlock();
2484 if (imp_task
!= IIT_NULL
) {
2485 ipc_importance_task_release(imp_task
);
2488 if (release_imp_task
!= IIT_NULL
) {
2489 ipc_importance_task_release(release_imp_task
);
2492 if (watchport_elem
) {
2493 task_watchport_elem_deallocate(watchport_elem
);
2500 * Routine: ipc_importance_send
2502 * Post the importance voucher attribute [if sent] or a static
2503 * importance boost depending upon options and conditions.
2505 * Destination port locked on entry and exit, may be dropped during the call.
2507 * A boolean identifying if the port lock was tempoarily dropped.
2510 ipc_importance_send(
2512 mach_msg_option_t option
)
2514 ipc_port_t port
= kmsg
->ikm_header
->msgh_remote_port
;
2515 boolean_t port_lock_dropped
= FALSE
;
2516 ipc_importance_elem_t elem
;
2518 ipc_importance_task_t task_imp
;
2521 assert(IP_VALID(port
));
2523 /* If no donation to be made, return quickly */
2524 if ((port
->ip_impdonation
== 0) ||
2525 (option
& MACH_SEND_NOIMPORTANCE
) != 0) {
2526 return port_lock_dropped
;
2529 task
= current_task();
2531 /* If forced sending a static boost, go update the port */
2532 if ((option
& MACH_SEND_IMPORTANCE
) != 0) {
2533 /* acquire the importance lock while trying to hang on to port lock */
2534 if (!ipc_importance_lock_try()) {
2535 port_lock_dropped
= TRUE
;
2537 ipc_importance_lock();
2542 task_imp
= task
->task_imp_base
;
2543 assert(IIT_NULL
!= task_imp
);
2545 /* If the sender can never donate importance, nothing to do */
2546 if (ipc_importance_task_is_never_donor(task_imp
)) {
2547 return port_lock_dropped
;
2552 /* If importance receiver and passing a voucher, look for importance in there */
2553 if (IP_VALID(kmsg
->ikm_voucher
) &&
2554 ipc_importance_task_is_marked_receiver(task_imp
)) {
2555 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2556 mach_voucher_attr_value_handle_array_size_t val_count
;
2557 ipc_voucher_t voucher
;
2559 assert(ip_kotype(kmsg
->ikm_voucher
) == IKOT_VOUCHER
);
2560 voucher
= (ipc_voucher_t
)ip_get_kobject(kmsg
->ikm_voucher
);
2562 /* check to see if the voucher has an importance attribute */
2563 val_count
= MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
;
2564 kr
= mach_voucher_attr_control_get_values(ipc_importance_control
, voucher
,
2566 assert(KERN_SUCCESS
== kr
);
2569 * Only use importance associated with our task (either directly
2570 * or through an inherit that donates to our task).
2572 if (0 < val_count
) {
2573 ipc_importance_elem_t check_elem
;
2575 check_elem
= (ipc_importance_elem_t
)vals
[0];
2576 assert(IIE_NULL
!= check_elem
);
2577 if (IIE_TYPE_INHERIT
== IIE_TYPE(check_elem
)) {
2578 ipc_importance_inherit_t inherit
;
2579 inherit
= (ipc_importance_inherit_t
) check_elem
;
2580 if (inherit
->iii_to_task
== task_imp
) {
2583 } else if (check_elem
== (ipc_importance_elem_t
)task_imp
) {
2589 /* If we haven't found an importance attribute to send yet, use the task's */
2590 if (IIE_NULL
== elem
) {
2591 elem
= (ipc_importance_elem_t
)task_imp
;
2594 /* take a reference for the message to hold */
2595 ipc_importance_reference_internal(elem
);
2597 /* acquire the importance lock while trying to hang on to port lock */
2598 if (!ipc_importance_lock_try()) {
2599 port_lock_dropped
= TRUE
;
2601 ipc_importance_lock();
2604 /* link kmsg onto the donor element propagation chain */
2605 ipc_importance_kmsg_link(kmsg
, elem
);
2606 /* elem reference transfered to kmsg */
2608 incr_ref_counter(elem
->iie_kmsg_refs_added
);
2610 /* If the sender isn't currently a donor, no need to apply boost */
2611 if (!ipc_importance_task_is_donor(task_imp
)) {
2612 ipc_importance_unlock();
2614 /* re-acquire port lock, if needed */
2615 if (TRUE
== port_lock_dropped
) {
2619 return port_lock_dropped
;
2623 /* Mark the fact that we are (currently) donating through this message */
2624 kmsg
->ikm_header
->msgh_bits
|= MACH_MSGH_BITS_RAISEIMP
;
2627 * If we need to relock the port, do it with the importance still locked.
2628 * This assures we get to add the importance boost through the port to
2629 * the task BEFORE anyone else can attempt to undo that operation if
2630 * the sender lost donor status.
2632 if (TRUE
== port_lock_dropped
) {
2636 ipc_importance_assert_held();
2638 #if IMPORTANCE_TRACE
2639 if (kdebug_enable
) {
2640 mach_msg_max_trailer_t
*dbgtrailer
= (mach_msg_max_trailer_t
*)
2641 ((vm_offset_t
)kmsg
->ikm_header
+ mach_round_msg(kmsg
->ikm_header
->msgh_size
));
2642 unsigned int sender_pid
= dbgtrailer
->msgh_audit
.val
[5];
2643 mach_msg_id_t imp_msgh_id
= kmsg
->ikm_header
->msgh_id
;
2644 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_MSG
, IMP_MSG_SEND
)) | DBG_FUNC_START
,
2645 task_pid(task
), sender_pid
, imp_msgh_id
, 0, 0);
2647 #endif /* IMPORTANCE_TRACE */
2649 mach_port_delta_t delta
= 1;
2650 boolean_t need_port_lock
;
2651 task_imp
= IIT_NULL
;
2653 /* adjust port boost count (with importance and port locked) */
2654 need_port_lock
= ipc_port_importance_delta_internal(port
, IPID_OPTION_NORMAL
, &delta
, &task_imp
);
2655 /* hold a reference on task_imp */
2657 /* if we need to adjust a task importance as a result, apply that here */
2658 if (IIT_NULL
!= task_imp
&& delta
!= 0) {
2661 /* if this results in a change of state, propagate the transistion */
2662 if (ipc_importance_task_check_transition(task_imp
, IIT_UPDATE_HOLD
, delta
)) {
2663 /* can't hold the port lock during task transition(s) */
2664 if (!need_port_lock
) {
2665 need_port_lock
= TRUE
;
2668 ipc_importance_task_propagate_assertion_locked(task_imp
, IIT_UPDATE_HOLD
, TRUE
);
2673 ipc_importance_task_release_locked(task_imp
);
2674 /* importance unlocked */
2676 ipc_importance_unlock();
2679 if (need_port_lock
) {
2680 port_lock_dropped
= TRUE
;
2684 return port_lock_dropped
;
2688 * Routine: ipc_importance_inherit_from_kmsg
2690 * Create a "made" reference for an importance attribute representing
2691 * an inheritance between the sender of a message (if linked) and the
2692 * current task importance. If the message is not linked, a static
2693 * boost may be created, based on the boost state of the message.
2695 * Any transfer from kmsg linkage to inherit linkage must be atomic.
2697 * If the task is inactive, there isn't any need to return a new reference.
2699 * Nothing locked on entry. May block.
2701 static ipc_importance_inherit_t
2702 ipc_importance_inherit_from_kmsg(ipc_kmsg_t kmsg
)
2704 ipc_importance_task_t task_imp
= IIT_NULL
;
2705 ipc_importance_elem_t from_elem
= kmsg
->ikm_importance
;
2706 ipc_importance_elem_t elem
;
2707 task_t task_self
= current_task();
2709 ipc_port_t port
= kmsg
->ikm_header
->msgh_remote_port
;
2710 ipc_importance_inherit_t inherit
= III_NULL
;
2711 ipc_importance_inherit_t alloc
= III_NULL
;
2712 boolean_t cleared_self_donation
= FALSE
;
2716 /* The kmsg must have an importance donor or static boost to proceed */
2717 if (IIE_NULL
== kmsg
->ikm_importance
&&
2718 !MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
)) {
2723 * No need to set up an inherit linkage if the dest isn't a receiver
2724 * of one type or the other.
2726 if (!ipc_importance_task_is_any_receiver_type(task_self
->task_imp_base
)) {
2727 ipc_importance_lock();
2731 /* Grab a reference on the importance of the destination */
2732 task_imp
= ipc_importance_for_task(task_self
, FALSE
);
2734 ipc_importance_lock();
2736 if (IIT_NULL
== task_imp
) {
2740 incr_ref_counter(task_imp
->iit_elem
.iie_task_refs_added_inherit_from
);
2742 /* If message is already associated with an inherit... */
2743 if (IIE_TYPE_INHERIT
== IIE_TYPE(from_elem
)) {
2744 ipc_importance_inherit_t from_inherit
= (ipc_importance_inherit_t
)from_elem
;
2746 /* already targeting our task? - just use it */
2747 if (from_inherit
->iii_to_task
== task_imp
) {
2748 /* clear self-donation if not also present in inherit */
2749 if (!from_inherit
->iii_donating
&&
2750 MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
)) {
2751 kmsg
->ikm_header
->msgh_bits
&= ~MACH_MSGH_BITS_RAISEIMP
;
2752 cleared_self_donation
= TRUE
;
2754 inherit
= from_inherit
;
2755 } else if (III_DEPTH_MAX
== III_DEPTH(from_inherit
)) {
2756 ipc_importance_task_t to_task
;
2757 ipc_importance_elem_t unlinked_from
;
2760 * Chain too long. Switch to looking
2761 * directly at the from_inherit's to-task
2762 * as our source of importance.
2764 to_task
= from_inherit
->iii_to_task
;
2765 ipc_importance_task_reference(to_task
);
2766 from_elem
= (ipc_importance_elem_t
)to_task
;
2767 depth
= III_DEPTH_RESET
| 1;
2769 /* Fixup the kmsg linkage to reflect change */
2770 unlinked_from
= ipc_importance_kmsg_unlink(kmsg
);
2771 assert(unlinked_from
== (ipc_importance_elem_t
)from_inherit
);
2772 ipc_importance_kmsg_link(kmsg
, from_elem
);
2773 ipc_importance_inherit_release_locked(from_inherit
);
2774 /* importance unlocked */
2775 ipc_importance_lock();
2777 /* inheriting from an inherit */
2778 depth
= from_inherit
->iii_depth
+ 1;
2783 * Don't allow a task to inherit from itself (would keep it permanently
2784 * boosted even if all other donors to the task went away).
2787 if (from_elem
== (ipc_importance_elem_t
)task_imp
) {
2792 * But if the message isn't associated with any linked source, it is
2793 * intended to be permanently boosting (static boost from kernel).
2794 * In that case DO let the process permanently boost itself.
2796 if (IIE_NULL
== from_elem
) {
2797 assert(MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
));
2798 ipc_importance_task_reference_internal(task_imp
);
2799 from_elem
= (ipc_importance_elem_t
)task_imp
;
2803 * Now that we have the from_elem figured out,
2804 * check to see if we already have an inherit for this pairing
2806 while (III_NULL
== inherit
) {
2807 inherit
= ipc_importance_inherit_find(from_elem
, task_imp
, depth
);
2809 /* Do we have to allocate a new inherit */
2810 if (III_NULL
== inherit
) {
2811 if (III_NULL
!= alloc
) {
2815 /* allocate space */
2816 ipc_importance_unlock();
2817 alloc
= (ipc_importance_inherit_t
)
2818 zalloc(ipc_importance_inherit_zone
);
2819 ipc_importance_lock();
2823 /* snapshot the donating status while we have importance locked */
2824 donating
= MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
);
2826 if (III_NULL
!= inherit
) {
2827 /* We found one, piggyback on that */
2828 assert(0 < III_REFS(inherit
));
2829 assert(0 < IIE_REFS(inherit
->iii_from_elem
));
2830 assert(inherit
->iii_externcnt
>= inherit
->iii_made
);
2832 /* add in a made reference */
2833 if (0 == inherit
->iii_made
++) {
2834 assert(III_REFS_MAX
> III_REFS(inherit
));
2835 ipc_importance_inherit_reference_internal(inherit
);
2838 /* Reflect the inherit's change of status into the task boosts */
2839 if (0 == III_EXTERN(inherit
)) {
2840 assert(!inherit
->iii_donating
);
2841 inherit
->iii_donating
= donating
;
2843 task_imp
->iit_externcnt
+= inherit
->iii_externcnt
;
2844 task_imp
->iit_externdrop
+= inherit
->iii_externdrop
;
2847 assert(donating
== inherit
->iii_donating
);
2850 /* add in a external reference for this use of the inherit */
2851 inherit
->iii_externcnt
++;
2853 /* initialize the previously allocated space */
2855 inherit
->iii_bits
= IIE_TYPE_INHERIT
| 1;
2856 inherit
->iii_made
= 1;
2857 inherit
->iii_externcnt
= 1;
2858 inherit
->iii_externdrop
= 0;
2859 inherit
->iii_depth
= depth
;
2860 inherit
->iii_to_task
= task_imp
;
2861 inherit
->iii_from_elem
= IIE_NULL
;
2862 queue_init(&inherit
->iii_kmsgs
);
2865 inherit
->iii_donating
= TRUE
;
2867 inherit
->iii_donating
= FALSE
;
2871 * Chain our new inherit on the element it inherits from.
2872 * The new inherit takes our reference on from_elem.
2874 ipc_importance_inherit_link(inherit
, from_elem
);
2877 ipc_importance_counter_init(&inherit
->iii_elem
);
2878 from_elem
->iie_kmsg_refs_inherited
++;
2879 task_imp
->iit_elem
.iie_task_refs_inherited
++;
2885 * for those paths that came straight here: snapshot the donating status
2886 * (this should match previous snapshot for other paths).
2888 donating
= MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
);
2890 /* unlink the kmsg inheritance (if any) */
2891 elem
= ipc_importance_kmsg_unlink(kmsg
);
2892 assert(elem
== from_elem
);
2894 /* If found inherit and donating, reflect that in the task externcnt */
2895 if (III_NULL
!= inherit
&& donating
) {
2896 task_imp
->iit_externcnt
++;
2897 /* The owner of receive right might have changed, take the internal assertion */
2898 ipc_importance_task_hold_internal_assertion_locked(task_imp
, 1);
2899 /* may have dropped and retaken importance lock */
2902 /* If we didn't create a new inherit, we have some resources to release */
2903 if (III_NULL
== inherit
|| inherit
!= alloc
) {
2904 if (IIE_NULL
!= from_elem
) {
2905 if (III_NULL
!= inherit
) {
2906 incr_ref_counter(from_elem
->iie_kmsg_refs_coalesced
);
2908 incr_ref_counter(from_elem
->iie_kmsg_refs_dropped
);
2910 ipc_importance_release_locked(from_elem
);
2911 /* importance unlocked */
2913 ipc_importance_unlock();
2916 if (IIT_NULL
!= task_imp
) {
2917 if (III_NULL
!= inherit
) {
2918 incr_ref_counter(task_imp
->iit_elem
.iie_task_refs_coalesced
);
2920 ipc_importance_task_release(task_imp
);
2923 if (III_NULL
!= alloc
) {
2924 zfree(ipc_importance_inherit_zone
, alloc
);
2927 /* from_elem and task_imp references transferred to new inherit */
2928 ipc_importance_unlock();
2932 * decrement port boost count
2933 * This is OK to do without the importance lock as we atomically
2934 * unlinked the kmsg and snapshot the donating state while holding
2935 * the importance lock
2937 if (donating
|| cleared_self_donation
) {
2939 /* drop importance from port and destination task */
2940 if (ipc_port_importance_delta(port
, IPID_OPTION_NORMAL
, -1) == FALSE
) {
2945 if (III_NULL
!= inherit
) {
2946 /* have an associated importance attr, even if currently not donating */
2947 kmsg
->ikm_header
->msgh_bits
|= MACH_MSGH_BITS_RAISEIMP
;
2949 /* we won't have an importance attribute associated with our message */
2950 kmsg
->ikm_header
->msgh_bits
&= ~MACH_MSGH_BITS_RAISEIMP
;
2957 * Routine: ipc_importance_inherit_from_task
2959 * Create a reference for an importance attribute representing
2960 * an inheritance between the to_task and from_task. The iii
2961 * created will be marked as III_FLAGS_FOR_OTHERS.
2963 * It will not dedup any iii which are not marked as III_FLAGS_FOR_OTHERS.
2965 * If the task is inactive, there isn't any need to return a new reference.
2967 * Nothing locked on entry. May block.
2968 * It should not be called from voucher subsystem.
2970 static ipc_importance_inherit_t
2971 ipc_importance_inherit_from_task(
2975 ipc_importance_task_t to_task_imp
= IIT_NULL
;
2976 ipc_importance_task_t from_task_imp
= IIT_NULL
;
2977 ipc_importance_elem_t from_elem
= IIE_NULL
;
2979 ipc_importance_inherit_t inherit
= III_NULL
;
2980 ipc_importance_inherit_t alloc
= III_NULL
;
2984 to_task_imp
= ipc_importance_for_task(to_task
, FALSE
);
2985 from_task_imp
= ipc_importance_for_task(from_task
, FALSE
);
2986 from_elem
= (ipc_importance_elem_t
)from_task_imp
;
2988 ipc_importance_lock();
2990 if (IIT_NULL
== to_task_imp
|| IIT_NULL
== from_task_imp
) {
2995 * No need to set up an inherit linkage if the to_task or from_task
2996 * isn't a receiver of one type or the other.
2998 if (!ipc_importance_task_is_any_receiver_type(to_task_imp
) ||
2999 !ipc_importance_task_is_any_receiver_type(from_task_imp
)) {
3003 /* Do not allow to create a linkage to self */
3004 if (to_task_imp
== from_task_imp
) {
3008 incr_ref_counter(to_task_imp
->iit_elem
.iie_task_refs_added_inherit_from
);
3009 incr_ref_counter(from_elem
->iie_kmsg_refs_added
);
3012 * Now that we have the from_elem figured out,
3013 * check to see if we already have an inherit for this pairing
3015 while (III_NULL
== inherit
) {
3016 inherit
= ipc_importance_inherit_find(from_elem
, to_task_imp
, depth
);
3018 /* Do we have to allocate a new inherit */
3019 if (III_NULL
== inherit
) {
3020 if (III_NULL
!= alloc
) {
3024 /* allocate space */
3025 ipc_importance_unlock();
3026 alloc
= (ipc_importance_inherit_t
)
3027 zalloc(ipc_importance_inherit_zone
);
3028 ipc_importance_lock();
3032 /* snapshot the donating status while we have importance locked */
3033 donating
= ipc_importance_task_is_donor(from_task_imp
);
3035 if (III_NULL
!= inherit
) {
3036 /* We found one, piggyback on that */
3037 assert(0 < III_REFS(inherit
));
3038 assert(0 < IIE_REFS(inherit
->iii_from_elem
));
3040 /* Take a reference for inherit */
3041 assert(III_REFS_MAX
> III_REFS(inherit
));
3042 ipc_importance_inherit_reference_internal(inherit
);
3044 /* Reflect the inherit's change of status into the task boosts */
3045 if (0 == III_EXTERN(inherit
)) {
3046 assert(!inherit
->iii_donating
);
3047 inherit
->iii_donating
= donating
;
3049 to_task_imp
->iit_externcnt
+= inherit
->iii_externcnt
;
3050 to_task_imp
->iit_externdrop
+= inherit
->iii_externdrop
;
3053 assert(donating
== inherit
->iii_donating
);
3056 /* add in a external reference for this use of the inherit */
3057 inherit
->iii_externcnt
++;
3059 /* initialize the previously allocated space */
3061 inherit
->iii_bits
= IIE_TYPE_INHERIT
| 1;
3062 inherit
->iii_made
= 0;
3063 inherit
->iii_externcnt
= 1;
3064 inherit
->iii_externdrop
= 0;
3065 inherit
->iii_depth
= depth
;
3066 inherit
->iii_to_task
= to_task_imp
;
3067 inherit
->iii_from_elem
= IIE_NULL
;
3068 queue_init(&inherit
->iii_kmsgs
);
3071 inherit
->iii_donating
= TRUE
;
3073 inherit
->iii_donating
= FALSE
;
3077 * Chain our new inherit on the element it inherits from.
3078 * The new inherit takes our reference on from_elem.
3080 ipc_importance_inherit_link(inherit
, from_elem
);
3083 ipc_importance_counter_init(&inherit
->iii_elem
);
3084 from_elem
->iie_kmsg_refs_inherited
++;
3085 task_imp
->iit_elem
.iie_task_refs_inherited
++;
3091 /* If found inherit and donating, reflect that in the task externcnt */
3092 if (III_NULL
!= inherit
&& donating
) {
3093 to_task_imp
->iit_externcnt
++;
3094 /* take the internal assertion */
3095 ipc_importance_task_hold_internal_assertion_locked(to_task_imp
, 1);
3096 /* may have dropped and retaken importance lock */
3099 /* If we didn't create a new inherit, we have some resources to release */
3100 if (III_NULL
== inherit
|| inherit
!= alloc
) {
3101 if (IIE_NULL
!= from_elem
) {
3102 if (III_NULL
!= inherit
) {
3103 incr_ref_counter(from_elem
->iie_kmsg_refs_coalesced
);
3105 incr_ref_counter(from_elem
->iie_kmsg_refs_dropped
);
3107 ipc_importance_release_locked(from_elem
);
3108 /* importance unlocked */
3110 ipc_importance_unlock();
3113 if (IIT_NULL
!= to_task_imp
) {
3114 if (III_NULL
!= inherit
) {
3115 incr_ref_counter(to_task_imp
->iit_elem
.iie_task_refs_coalesced
);
3117 ipc_importance_task_release(to_task_imp
);
3120 if (III_NULL
!= alloc
) {
3121 zfree(ipc_importance_inherit_zone
, alloc
);
3124 /* from_elem and to_task_imp references transferred to new inherit */
3125 ipc_importance_unlock();
3132 * Routine: ipc_importance_receive
3134 * Process importance attributes in a received message.
3136 * If an importance voucher attribute was sent, transform
3137 * that into an attribute value reflecting the inheritance
3138 * from the sender to the receiver.
3140 * If a static boost is received (or the voucher isn't on
3141 * a voucher-based boost), export a static boost.
3146 ipc_importance_receive(
3148 mach_msg_option_t option
)
3152 #if IMPORTANCE_TRACE || LEGACY_IMPORTANCE_DELIVERY
3153 task_t task_self
= current_task();
3154 unsigned int sender_pid
= ((mach_msg_max_trailer_t
*)
3155 ((vm_offset_t
)kmsg
->ikm_header
+
3156 mach_round_msg(kmsg
->ikm_header
->msgh_size
)))->msgh_audit
.val
[5];
3159 /* convert to a voucher with an inherit importance attribute? */
3160 if ((option
& MACH_RCV_VOUCHER
) != 0) {
3161 uint8_t recipes
[2 * sizeof(ipc_voucher_attr_recipe_data_t
) +
3162 sizeof(mach_voucher_attr_value_handle_t
)];
3163 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= 0;
3164 ipc_voucher_attr_recipe_t recipe
= (ipc_voucher_attr_recipe_t
)recipes
;
3165 ipc_voucher_t recv_voucher
;
3166 mach_voucher_attr_value_handle_t handle
;
3167 ipc_importance_inherit_t inherit
;
3170 /* set up recipe to copy the old voucher */
3171 if (IP_VALID(kmsg
->ikm_voucher
)) {
3172 ipc_voucher_t sent_voucher
= (ipc_voucher_t
)ip_get_kobject(kmsg
->ikm_voucher
);
3174 recipe
->key
= MACH_VOUCHER_ATTR_KEY_ALL
;
3175 recipe
->command
= MACH_VOUCHER_ATTR_COPY
;
3176 recipe
->previous_voucher
= sent_voucher
;
3177 recipe
->content_size
= 0;
3178 recipe_size
+= sizeof(*recipe
);
3182 * create an inheritance attribute from the kmsg (may be NULL)
3183 * transferring any boosts from the kmsg linkage through the
3184 * port directly to the new inheritance object.
3186 inherit
= ipc_importance_inherit_from_kmsg(kmsg
);
3187 handle
= (mach_voucher_attr_value_handle_t
)inherit
;
3189 assert(IIE_NULL
== kmsg
->ikm_importance
);
3192 * Only create a new voucher if we have an inherit object
3193 * (from the ikm_importance field of the incoming message), OR
3194 * we have a valid incoming voucher. If we have neither of
3195 * these things then there is no need to create a new voucher.
3197 if (IP_VALID(kmsg
->ikm_voucher
) || inherit
!= III_NULL
) {
3198 /* replace the importance attribute with the handle we created */
3199 /* our made reference on the inherit is donated to the voucher */
3200 recipe
= (ipc_voucher_attr_recipe_t
)&recipes
[recipe_size
];
3201 recipe
->key
= MACH_VOUCHER_ATTR_KEY_IMPORTANCE
;
3202 recipe
->command
= MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
;
3203 recipe
->previous_voucher
= IPC_VOUCHER_NULL
;
3204 recipe
->content_size
= sizeof(mach_voucher_attr_value_handle_t
);
3205 *(mach_voucher_attr_value_handle_t
*)(void *)recipe
->content
= handle
;
3206 recipe_size
+= sizeof(*recipe
) + sizeof(mach_voucher_attr_value_handle_t
);
3208 kr
= ipc_voucher_attr_control_create_mach_voucher(ipc_importance_control
,
3212 assert(KERN_SUCCESS
== kr
);
3214 /* swap the voucher port (and set voucher bits in case it didn't already exist) */
3215 kmsg
->ikm_header
->msgh_bits
|= (MACH_MSG_TYPE_MOVE_SEND
<< 16);
3216 ipc_port_release_send(kmsg
->ikm_voucher
);
3217 kmsg
->ikm_voucher
= convert_voucher_to_port(recv_voucher
);
3218 if (III_NULL
!= inherit
) {
3222 } else { /* Don't want a voucher */
3223 /* got linked importance? have to drop */
3224 if (IIE_NULL
!= kmsg
->ikm_importance
) {
3225 ipc_importance_elem_t elem
;
3227 ipc_importance_lock();
3228 elem
= ipc_importance_kmsg_unlink(kmsg
);
3230 elem
->iie_kmsg_refs_dropped
++;
3232 ipc_importance_release_locked(elem
);
3233 /* importance unlocked */
3236 /* With kmsg unlinked, can safely examine message importance attribute. */
3237 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
)) {
3238 ipc_port_t port
= kmsg
->ikm_header
->msgh_remote_port
;
3239 #if LEGACY_IMPORTANCE_DELIVERY
3240 ipc_importance_task_t task_imp
= task_self
->task_imp_base
;
3242 /* The owner of receive right might have changed, take the internal assertion */
3243 if (KERN_SUCCESS
== ipc_importance_task_hold_internal_assertion(task_imp
, 1)) {
3244 ipc_importance_task_externalize_legacy_assertion(task_imp
, 1, sender_pid
);
3249 /* The importance boost never applied to task (clear the bit) */
3250 kmsg
->ikm_header
->msgh_bits
&= ~MACH_MSGH_BITS_RAISEIMP
;
3254 /* Drop the boost on the port and the owner of the receive right */
3256 if (ipc_port_importance_delta(port
, IPID_OPTION_NORMAL
, -1) == FALSE
) {
3262 #if IMPORTANCE_TRACE
3263 if (-1 < impresult
) {
3264 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_MSG
, IMP_MSG_DELV
)) | DBG_FUNC_NONE
,
3265 sender_pid
, task_pid(task_self
),
3266 kmsg
->ikm_header
->msgh_id
, impresult
, 0);
3268 if (impresult
== 2) {
3270 * This probe only covers new voucher-based path. Legacy importance
3271 * will trigger the probe in ipc_importance_task_externalize_assertion()
3272 * above and have impresult==1 here.
3274 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
);
3276 #endif /* IMPORTANCE_TRACE */
3280 * Routine: ipc_importance_unreceive
3282 * Undo receive of importance attributes in a message.
3288 ipc_importance_unreceive(
3290 mach_msg_option_t __unused option
)
3292 /* importance should already be in the voucher and out of the kmsg */
3293 assert(IIE_NULL
== kmsg
->ikm_importance
);
3295 /* See if there is a legacy boost to be dropped from receiver */
3296 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
)) {
3297 ipc_importance_task_t task_imp
;
3299 kmsg
->ikm_header
->msgh_bits
&= ~MACH_MSGH_BITS_RAISEIMP
;
3300 task_imp
= current_task()->task_imp_base
;
3301 if (!IP_VALID(kmsg
->ikm_voucher
) && IIT_NULL
!= task_imp
) {
3302 ipc_importance_task_drop_legacy_external_assertion(task_imp
, 1);
3305 * ipc_kmsg_copyout_dest() will consume the voucher
3306 * and any contained importance.
3312 * Routine: ipc_importance_clean
3314 * Clean up importance state in a kmsg that is being cleaned.
3315 * Unlink the importance chain if one was set up, and drop
3316 * the reference this kmsg held on the donor. Then check to
3317 * if importance was carried to the port, and remove that if
3323 ipc_importance_clean(
3328 /* Is the kmsg still linked? If so, remove that first */
3329 if (IIE_NULL
!= kmsg
->ikm_importance
) {
3330 ipc_importance_elem_t elem
;
3332 ipc_importance_lock();
3333 elem
= ipc_importance_kmsg_unlink(kmsg
);
3334 assert(IIE_NULL
!= elem
);
3335 ipc_importance_release_locked(elem
);
3336 /* importance unlocked */
3339 /* See if there is a legacy importance boost to be dropped from port */
3340 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
)) {
3341 kmsg
->ikm_header
->msgh_bits
&= ~MACH_MSGH_BITS_RAISEIMP
;
3342 port
= kmsg
->ikm_header
->msgh_remote_port
;
3343 if (IP_VALID(port
)) {
3345 /* inactive ports already had their importance boosts dropped */
3346 if (!ip_active(port
) ||
3347 ipc_port_importance_delta(port
, IPID_OPTION_NORMAL
, -1) == FALSE
) {
3355 ipc_importance_assert_clean(__assert_only ipc_kmsg_t kmsg
)
3357 assert(IIE_NULL
== kmsg
->ikm_importance
);
3358 assert(!MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
));
3362 * IPC Importance Attribute Manager definition
3365 static kern_return_t
3366 ipc_importance_release_value(
3367 ipc_voucher_attr_manager_t manager
,
3368 mach_voucher_attr_key_t key
,
3369 mach_voucher_attr_value_handle_t value
,
3370 mach_voucher_attr_value_reference_t sync
);
3372 static kern_return_t
3373 ipc_importance_get_value(
3374 ipc_voucher_attr_manager_t manager
,
3375 mach_voucher_attr_key_t key
,
3376 mach_voucher_attr_recipe_command_t command
,
3377 mach_voucher_attr_value_handle_array_t prev_values
,
3378 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
3379 mach_voucher_attr_content_t content
,
3380 mach_voucher_attr_content_size_t content_size
,
3381 mach_voucher_attr_value_handle_t
*out_value
,
3382 mach_voucher_attr_value_flags_t
*out_flags
,
3383 ipc_voucher_t
*out_value_voucher
);
3385 static kern_return_t
3386 ipc_importance_extract_content(
3387 ipc_voucher_attr_manager_t manager
,
3388 mach_voucher_attr_key_t key
,
3389 mach_voucher_attr_value_handle_array_t values
,
3390 mach_voucher_attr_value_handle_array_size_t value_count
,
3391 mach_voucher_attr_recipe_command_t
*out_command
,
3392 mach_voucher_attr_content_t out_content
,
3393 mach_voucher_attr_content_size_t
*in_out_content_size
);
3395 static kern_return_t
3396 ipc_importance_command(
3397 ipc_voucher_attr_manager_t manager
,
3398 mach_voucher_attr_key_t key
,
3399 mach_voucher_attr_value_handle_array_t values
,
3400 mach_msg_type_number_t value_count
,
3401 mach_voucher_attr_command_t command
,
3402 mach_voucher_attr_content_t in_content
,
3403 mach_voucher_attr_content_size_t in_content_size
,
3404 mach_voucher_attr_content_t out_content
,
3405 mach_voucher_attr_content_size_t
*out_content_size
);
3408 ipc_importance_manager_release(
3409 ipc_voucher_attr_manager_t manager
);
3411 const struct ipc_voucher_attr_manager ipc_importance_manager
= {
3412 .ivam_release_value
= ipc_importance_release_value
,
3413 .ivam_get_value
= ipc_importance_get_value
,
3414 .ivam_extract_content
= ipc_importance_extract_content
,
3415 .ivam_command
= ipc_importance_command
,
3416 .ivam_release
= ipc_importance_manager_release
,
3417 .ivam_flags
= IVAM_FLAGS_NONE
,
3420 #define IMPORTANCE_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_IMPORTANCE == (key))
3421 #define IMPORTANCE_ASSERT_MANAGER(manager) assert(&ipc_importance_manager == (manager))
3424 * Routine: ipc_importance_release_value [Voucher Attribute Manager Interface]
3426 * Release what the voucher system believes is the last "made" reference
3427 * on an importance attribute value handle. The sync parameter is used to
3428 * avoid races with new made references concurrently being returned to the
3429 * voucher system in other threads.
3431 * Nothing locked on entry. May block.
3433 static kern_return_t
3434 ipc_importance_release_value(
3435 ipc_voucher_attr_manager_t __assert_only manager
,
3436 mach_voucher_attr_key_t __assert_only key
,
3437 mach_voucher_attr_value_handle_t value
,
3438 mach_voucher_attr_value_reference_t sync
)
3440 ipc_importance_elem_t elem
;
3442 IMPORTANCE_ASSERT_MANAGER(manager
);
3443 IMPORTANCE_ASSERT_KEY(key
);
3446 elem
= (ipc_importance_elem_t
)value
;
3448 ipc_importance_lock();
3450 /* Any oustanding made refs? */
3451 if (sync
!= elem
->iie_made
) {
3452 assert(sync
< elem
->iie_made
);
3453 ipc_importance_unlock();
3454 return KERN_FAILURE
;
3461 * If there are pending external boosts represented by this attribute,
3462 * drop them from the apropriate task
3464 if (IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) {
3465 ipc_importance_inherit_t inherit
= (ipc_importance_inherit_t
)elem
;
3467 assert(inherit
->iii_externcnt
>= inherit
->iii_externdrop
);
3469 if (inherit
->iii_donating
) {
3470 ipc_importance_task_t imp_task
= inherit
->iii_to_task
;
3471 uint32_t assertcnt
= III_EXTERN(inherit
);
3473 assert(ipc_importance_task_is_any_receiver_type(imp_task
));
3474 assert(imp_task
->iit_externcnt
>= inherit
->iii_externcnt
);
3475 assert(imp_task
->iit_externdrop
>= inherit
->iii_externdrop
);
3476 imp_task
->iit_externcnt
-= inherit
->iii_externcnt
;
3477 imp_task
->iit_externdrop
-= inherit
->iii_externdrop
;
3478 inherit
->iii_externcnt
= 0;
3479 inherit
->iii_externdrop
= 0;
3480 inherit
->iii_donating
= FALSE
;
3482 /* adjust the internal assertions - and propagate if needed */
3483 if (ipc_importance_task_check_transition(imp_task
, IIT_UPDATE_DROP
, assertcnt
)) {
3484 ipc_importance_task_propagate_assertion_locked(imp_task
, IIT_UPDATE_DROP
, TRUE
);
3487 inherit
->iii_externcnt
= 0;
3488 inherit
->iii_externdrop
= 0;
3492 /* drop the made reference on elem */
3493 ipc_importance_release_locked(elem
);
3494 /* returns unlocked */
3496 return KERN_SUCCESS
;
3501 * Routine: ipc_importance_get_value [Voucher Attribute Manager Interface]
3503 * Convert command and content data into a reference on a [potentially new]
3504 * attribute value. The importance attribute manager will only allow the
3505 * caller to get a value for the current task's importance, or to redeem
3506 * an importance attribute from an existing voucher.
3508 * Nothing locked on entry. May block.
3510 static kern_return_t
3511 ipc_importance_get_value(
3512 ipc_voucher_attr_manager_t __assert_only manager
,
3513 mach_voucher_attr_key_t __assert_only key
,
3514 mach_voucher_attr_recipe_command_t command
,
3515 mach_voucher_attr_value_handle_array_t prev_values
,
3516 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
3517 mach_voucher_attr_content_t __unused content
,
3518 mach_voucher_attr_content_size_t content_size
,
3519 mach_voucher_attr_value_handle_t
*out_value
,
3520 mach_voucher_attr_value_flags_t
*out_flags
,
3521 ipc_voucher_t
*out_value_voucher
)
3523 ipc_importance_elem_t elem
;
3526 IMPORTANCE_ASSERT_MANAGER(manager
);
3527 IMPORTANCE_ASSERT_KEY(key
);
3529 if (0 != content_size
) {
3530 return KERN_INVALID_ARGUMENT
;
3533 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
;
3534 /* never an out voucher */
3537 case MACH_VOUCHER_ATTR_REDEEM
:
3539 /* redeem of previous values is the value */
3540 if (0 < prev_value_count
) {
3541 elem
= (ipc_importance_elem_t
)prev_values
[0];
3542 assert(IIE_NULL
!= elem
);
3544 ipc_importance_lock();
3545 assert(0 < elem
->iie_made
);
3547 ipc_importance_unlock();
3549 *out_value
= prev_values
[0];
3550 return KERN_SUCCESS
;
3553 /* redeem of default is default */
3555 *out_value_voucher
= IPC_VOUCHER_NULL
;
3556 return KERN_SUCCESS
;
3558 case MACH_VOUCHER_ATTR_IMPORTANCE_SELF
:
3559 self
= current_task();
3561 elem
= (ipc_importance_elem_t
)ipc_importance_for_task(self
, TRUE
);
3562 /* made reference added (or IIE_NULL which isn't referenced) */
3564 *out_value
= (mach_voucher_attr_value_handle_t
)elem
;
3565 *out_value_voucher
= IPC_VOUCHER_NULL
;
3566 return KERN_SUCCESS
;
3570 * every other command is unknown
3572 * Specifically, there is no mechanism provided to construct an
3573 * importance attribute for a task/process from just a pid or
3574 * task port. It has to be copied (or redeemed) from a previous
3575 * voucher that has it.
3577 return KERN_INVALID_ARGUMENT
;
3582 * Routine: ipc_importance_extract_content [Voucher Attribute Manager Interface]
3584 * Extract meaning from the attribute value present in a voucher. While
3585 * the real goal is to provide commands and data that can reproduce the
3586 * voucher's value "out of thin air", this isn't possible with importance
3587 * attribute values. Instead, return debug info to help track down dependencies.
3589 * Nothing locked on entry. May block.
3591 static kern_return_t
3592 ipc_importance_extract_content(
3593 ipc_voucher_attr_manager_t __assert_only manager
,
3594 mach_voucher_attr_key_t __assert_only key
,
3595 mach_voucher_attr_value_handle_array_t values
,
3596 mach_voucher_attr_value_handle_array_size_t value_count
,
3597 mach_voucher_attr_recipe_command_t
*out_command
,
3598 mach_voucher_attr_content_t out_content
,
3599 mach_voucher_attr_content_size_t
*in_out_content_size
)
3601 mach_voucher_attr_content_size_t size
= 0;
3602 ipc_importance_elem_t elem
;
3605 IMPORTANCE_ASSERT_MANAGER(manager
);
3606 IMPORTANCE_ASSERT_KEY(key
);
3608 /* the first non-default value provides the data */
3609 for (i
= 0; i
< value_count
&& *in_out_content_size
> 0; i
++) {
3610 elem
= (ipc_importance_elem_t
)values
[i
];
3611 if (IIE_NULL
== elem
) {
3615 snprintf((char *)out_content
, *in_out_content_size
, "Importance for pid ");
3616 size
= (mach_voucher_attr_content_size_t
)strlen((char *)out_content
);
3619 ipc_importance_inherit_t inherit
= III_NULL
;
3620 ipc_importance_task_t task_imp
;
3624 if (IIE_TYPE_TASK
== IIE_TYPE(elem
)) {
3625 task_imp
= (ipc_importance_task_t
)elem
;
3626 task
= task_imp
->iit_task
;
3627 t_pid
= (TASK_NULL
!= task
) ?
3628 task_pid(task
) : -1;
3629 snprintf((char *)out_content
+ size
, *in_out_content_size
- size
, "%d", t_pid
);
3631 inherit
= (ipc_importance_inherit_t
)elem
;
3632 task_imp
= inherit
->iii_to_task
;
3633 task
= task_imp
->iit_task
;
3634 t_pid
= (TASK_NULL
!= task
) ?
3635 task_pid(task
) : -1;
3636 snprintf((char *)out_content
+ size
, *in_out_content_size
- size
,
3637 "%d (%d of %d boosts) %s from pid ", t_pid
,
3638 III_EXTERN(inherit
), inherit
->iii_externcnt
,
3639 (inherit
->iii_donating
) ? "donated" : "linked");
3642 size
= (mach_voucher_attr_content_size_t
)strlen((char *)out_content
);
3644 if (III_NULL
== inherit
) {
3648 elem
= inherit
->iii_from_elem
;
3650 size
++; /* account for NULL */
3652 *out_command
= MACH_VOUCHER_ATTR_NOOP
; /* cannot be used to regenerate value */
3653 *in_out_content_size
= size
;
3654 return KERN_SUCCESS
;
3658 * Routine: ipc_importance_command [Voucher Attribute Manager Interface]
3660 * Run commands against the importance attribute value found in a voucher.
3661 * No such commands are currently supported.
3663 * Nothing locked on entry. May block.
3665 static kern_return_t
3666 ipc_importance_command(
3667 ipc_voucher_attr_manager_t __assert_only manager
,
3668 mach_voucher_attr_key_t __assert_only key
,
3669 mach_voucher_attr_value_handle_array_t values
,
3670 mach_msg_type_number_t value_count
,
3671 mach_voucher_attr_command_t command
,
3672 mach_voucher_attr_content_t in_content
,
3673 mach_voucher_attr_content_size_t in_content_size
,
3674 mach_voucher_attr_content_t out_content
,
3675 mach_voucher_attr_content_size_t
*out_content_size
)
3677 ipc_importance_inherit_t inherit
;
3678 ipc_importance_task_t to_task
;
3679 uint32_t refs
, *outrefsp
;
3680 mach_msg_type_number_t i
;
3683 IMPORTANCE_ASSERT_MANAGER(manager
);
3684 IMPORTANCE_ASSERT_KEY(key
);
3686 if (in_content_size
!= sizeof(refs
) ||
3687 (*out_content_size
!= 0 && *out_content_size
!= sizeof(refs
))) {
3688 return KERN_INVALID_ARGUMENT
;
3690 refs
= *(uint32_t *)(void *)in_content
;
3691 outrefsp
= (*out_content_size
!= 0) ? (uint32_t *)(void *)out_content
: NULL
;
3693 if (MACH_VOUCHER_IMPORTANCE_ATTR_DROP_EXTERNAL
!= command
) {
3694 return KERN_NOT_SUPPORTED
;
3697 /* the first non-default value of the apropos type provides the data */
3699 for (i
= 0; i
< value_count
; i
++) {
3700 ipc_importance_elem_t elem
= (ipc_importance_elem_t
)values
[i
];
3702 if (IIE_NULL
!= elem
&& IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) {
3703 inherit
= (ipc_importance_inherit_t
)elem
;
3707 if (III_NULL
== inherit
) {
3708 return KERN_INVALID_ARGUMENT
;
3711 ipc_importance_lock();
3714 if (NULL
!= outrefsp
) {
3715 *outrefsp
= III_EXTERN(inherit
);
3717 ipc_importance_unlock();
3718 return KERN_SUCCESS
;
3721 to_task
= inherit
->iii_to_task
;
3722 assert(ipc_importance_task_is_any_receiver_type(to_task
));
3724 /* if not donating to a denap receiver, it was called incorrectly */
3725 if (!ipc_importance_task_is_marked_denap_receiver(to_task
)) {
3726 ipc_importance_unlock();
3727 return KERN_INVALID_TASK
; /* keeps dispatch happy */
3730 /* Enough external references left to drop? */
3731 if (III_EXTERN(inherit
) < refs
) {
3732 ipc_importance_unlock();
3733 return KERN_FAILURE
;
3736 /* re-base external and internal counters at the inherit and the to-task (if apropos) */
3737 if (inherit
->iii_donating
) {
3738 assert(IIT_EXTERN(to_task
) >= III_EXTERN(inherit
));
3739 assert(to_task
->iit_externcnt
>= inherit
->iii_externcnt
);
3740 assert(to_task
->iit_externdrop
>= inherit
->iii_externdrop
);
3741 inherit
->iii_externdrop
+= refs
;
3742 to_task
->iit_externdrop
+= refs
;
3743 externcnt
= III_EXTERN(inherit
);
3744 if (0 == externcnt
) {
3745 inherit
->iii_donating
= FALSE
;
3746 to_task
->iit_externcnt
-= inherit
->iii_externcnt
;
3747 to_task
->iit_externdrop
-= inherit
->iii_externdrop
;
3750 /* Start AppNap delay hysteresis - even if not the last boost for the task. */
3751 if (ipc_importance_delayed_drop_call
!= NULL
&&
3752 ipc_importance_task_is_marked_denap_receiver(to_task
)) {
3753 ipc_importance_task_delayed_drop(to_task
);
3756 /* drop task assertions associated with the dropped boosts */
3757 if (ipc_importance_task_check_transition(to_task
, IIT_UPDATE_DROP
, refs
)) {
3758 ipc_importance_task_propagate_assertion_locked(to_task
, IIT_UPDATE_DROP
, TRUE
);
3759 /* may have dropped and retaken importance lock */
3762 /* assert(to_task->iit_assertcnt >= refs + externcnt); */
3763 /* defensive deduction in case of assertcnt underflow */
3764 if (to_task
->iit_assertcnt
> refs
+ externcnt
) {
3765 to_task
->iit_assertcnt
-= refs
;
3767 to_task
->iit_assertcnt
= externcnt
;
3771 inherit
->iii_externdrop
+= refs
;
3772 externcnt
= III_EXTERN(inherit
);
3775 /* capture result (if requested) */
3776 if (NULL
!= outrefsp
) {
3777 *outrefsp
= externcnt
;
3780 ipc_importance_unlock();
3781 return KERN_SUCCESS
;
3785 * Routine: ipc_importance_manager_release [Voucher Attribute Manager Interface]
3787 * Release the Voucher system's reference on the IPC importance attribute
3790 * As this can only occur after the manager drops the Attribute control
3791 * reference granted back at registration time, and that reference is never
3792 * dropped, this should never be called.
3796 ipc_importance_manager_release(
3797 ipc_voucher_attr_manager_t __assert_only manager
)
3799 IMPORTANCE_ASSERT_MANAGER(manager
);
3800 panic("Voucher importance manager released");
3804 * Routine: ipc_importance_init
3806 * Initialize the IPC importance manager.
3808 * Zones and Vouchers are already initialized.
3811 ipc_importance_init(void)
3815 kr
= ipc_register_well_known_mach_voucher_attr_manager(&ipc_importance_manager
,
3816 (mach_voucher_attr_value_handle_t
)0,
3817 MACH_VOUCHER_ATTR_KEY_IMPORTANCE
,
3818 &ipc_importance_control
);
3819 if (KERN_SUCCESS
!= kr
) {
3820 printf("Voucher importance manager register returned %d", kr
);
3825 * Routine: ipc_importance_thread_call_init
3827 * Initialize the IPC importance code dependent upon
3828 * thread-call support being available.
3830 * Thread-call mechanism is already initialized.
3833 ipc_importance_thread_call_init(void)
3835 /* initialize delayed drop queue and thread-call */
3836 queue_init(&ipc_importance_delayed_drop_queue
);
3837 ipc_importance_delayed_drop_call
=
3838 thread_call_allocate(ipc_importance_task_delayed_drop_scan
, NULL
);
3839 if (NULL
== ipc_importance_delayed_drop_call
) {
3840 panic("ipc_importance_init");
3845 * Routing: task_importance_list_pids
3846 * Purpose: list pids where task in donating importance.
3847 * Conditions: To be called only from kdp stackshot code.
3848 * Will panic the system otherwise.
3851 task_importance_list_pids(task_t task
, int flags
, char *pid_list
, unsigned int max_count
)
3853 if (kdp_lck_spin_is_acquired(&ipc_importance_lock_data
) ||
3855 task
->task_imp_base
== IIT_NULL
||
3857 flags
!= TASK_IMP_LIST_DONATING_PIDS
) {
3860 unsigned int pidcount
= 0;
3862 ipc_importance_task_t task_imp
= task
->task_imp_base
;
3863 ipc_kmsg_t temp_kmsg
;
3864 ipc_importance_inherit_t temp_inherit
;
3865 ipc_importance_elem_t elem
;
3866 int target_pid
= 0, previous_pid
;
3868 queue_iterate(&task_imp
->iit_inherits
, temp_inherit
, ipc_importance_inherit_t
, iii_inheritance
) {
3869 /* check space in buffer */
3870 if (pidcount
>= max_count
) {
3873 previous_pid
= target_pid
;
3876 if (temp_inherit
->iii_donating
) {
3877 #if DEVELOPMENT || DEBUG
3878 target_pid
= temp_inherit
->iii_to_task
->iit_bsd_pid
;
3880 temp_task
= temp_inherit
->iii_to_task
->iit_task
;
3881 if (temp_task
!= TASK_NULL
) {
3882 target_pid
= task_pid(temp_task
);
3887 if (target_pid
!= -1 && previous_pid
!= target_pid
) {
3888 memcpy(pid_list
, &target_pid
, sizeof(target_pid
));
3889 pid_list
+= sizeof(target_pid
);
3895 queue_iterate(&task_imp
->iit_kmsgs
, temp_kmsg
, ipc_kmsg_t
, ikm_inheritance
) {
3896 if (pidcount
>= max_count
) {
3899 previous_pid
= target_pid
;
3901 elem
= temp_kmsg
->ikm_importance
;
3902 temp_task
= TASK_NULL
;
3904 if (elem
== IIE_NULL
) {
3908 if (!(temp_kmsg
->ikm_header
&& MACH_MSGH_BITS_RAISED_IMPORTANCE(temp_kmsg
->ikm_header
->msgh_bits
))) {
3912 if (IIE_TYPE_TASK
== IIE_TYPE(elem
) &&
3913 (((ipc_importance_task_t
)elem
)->iit_task
!= TASK_NULL
)) {
3914 target_pid
= task_pid(((ipc_importance_task_t
)elem
)->iit_task
);
3916 temp_inherit
= (ipc_importance_inherit_t
)elem
;
3917 #if DEVELOPMENT || DEBUG
3918 target_pid
= temp_inherit
->iii_to_task
->iit_bsd_pid
;
3920 temp_task
= temp_inherit
->iii_to_task
->iit_task
;
3921 if (temp_task
!= TASK_NULL
) {
3922 target_pid
= task_pid(temp_task
);
3927 if (target_pid
!= -1 && previous_pid
!= target_pid
) {
3928 memcpy(pid_list
, &target_pid
, sizeof(target_pid
));
3929 pid_list
+= sizeof(target_pid
);