2 * Copyright (c) 2013 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/kalloc.h>
40 #include <kern/zalloc.h>
41 #include <kern/queue.h>
42 #include <kern/task.h>
43 #include <kern/policy_internal.h>
45 #include <sys/kdebug.h>
47 #include <mach/mach_voucher_attr_control.h>
48 #include <mach/machine/sdt.h>
50 extern int proc_pid(void *);
51 extern int proc_selfpid(void);
52 extern uint64_t proc_uniqueid(void *p
);
53 extern char *proc_name_address(void *p
);
56 * Globals for delayed boost drop processing.
58 static queue_head_t ipc_importance_delayed_drop_queue
;
59 static thread_call_t ipc_importance_delayed_drop_call
;
60 static uint64_t ipc_importance_delayed_drop_timestamp
;
61 static boolean_t ipc_importance_delayed_drop_call_requested
= FALSE
;
63 #define DENAP_DROP_TARGET (1000 * NSEC_PER_MSEC) /* optimum denap delay */
64 #define DENAP_DROP_SKEW (100 * NSEC_PER_MSEC) /* request skew for wakeup */
65 #define DENAP_DROP_LEEWAY (2 * DENAP_DROP_SKEW) /* specified wakeup leeway */
67 #define DENAP_DROP_DELAY (DENAP_DROP_TARGET + DENAP_DROP_SKEW)
68 #define DENAP_DROP_FLAGS (THREAD_CALL_DELAY_SYS_NORMAL | THREAD_CALL_DELAY_LEEWAY)
71 * Importance Voucher Attribute Manager
74 static lck_spin_t ipc_importance_lock_data
; /* single lock for now */
77 #define ipc_importance_lock_init() \
78 lck_spin_init(&ipc_importance_lock_data, &ipc_lck_grp, &ipc_lck_attr)
79 #define ipc_importance_lock_destroy() \
80 lck_spin_destroy(&ipc_importance_lock_data, &ipc_lck_grp)
81 #define ipc_importance_lock() \
82 lck_spin_lock_grp(&ipc_importance_lock_data, &ipc_lck_grp)
83 #define ipc_importance_lock_try() \
84 lck_spin_try_lock_grp(&ipc_importance_lock_data, &ipc_lck_grp)
85 #define ipc_importance_unlock() \
86 lck_spin_unlock(&ipc_importance_lock_data)
87 #define ipc_importance_assert_held() \
88 lck_spin_assert(&ipc_importance_lock_data, LCK_ASSERT_OWNED)
91 #define incr_ref_counter(x) (hw_atomic_add(&(x), 1))
95 ipc_importance_reference_internal(ipc_importance_elem_t elem
)
97 incr_ref_counter(elem
->iie_refs_added
);
98 return hw_atomic_add(&elem
->iie_bits
, 1) & IIE_REFS_MASK
;
103 ipc_importance_release_internal(ipc_importance_elem_t elem
)
105 incr_ref_counter(elem
->iie_refs_dropped
);
106 return hw_atomic_sub(&elem
->iie_bits
, 1) & IIE_REFS_MASK
;
111 ipc_importance_task_reference_internal(ipc_importance_task_t task_imp
)
114 out
= ipc_importance_reference_internal(&task_imp
->iit_elem
);
115 incr_ref_counter(task_imp
->iit_elem
.iie_task_refs_added
);
121 ipc_importance_task_release_internal(ipc_importance_task_t task_imp
)
125 assert(1 < IIT_REFS(task_imp
));
126 incr_ref_counter(task_imp
->iit_elem
.iie_task_refs_dropped
);
127 out
= ipc_importance_release_internal(&task_imp
->iit_elem
);
133 ipc_importance_counter_init(ipc_importance_elem_t elem
)
135 elem
->iie_refs_added
= 0;
136 elem
->iie_refs_dropped
= 0;
137 elem
->iie_kmsg_refs_added
= 0;
138 elem
->iie_kmsg_refs_inherited
= 0;
139 elem
->iie_kmsg_refs_coalesced
= 0;
140 elem
->iie_kmsg_refs_dropped
= 0;
141 elem
->iie_task_refs_added
= 0;
142 elem
->iie_task_refs_added_inherit_from
= 0;
143 elem
->iie_task_refs_added_transition
= 0;
144 elem
->iie_task_refs_self_added
= 0;
145 elem
->iie_task_refs_inherited
= 0;
146 elem
->iie_task_refs_coalesced
= 0;
147 elem
->iie_task_refs_dropped
= 0;
150 #define incr_ref_counter(x)
153 #if DEVELOPMENT || DEBUG
154 static queue_head_t global_iit_alloc_queue
;
157 /* TODO: remove this varibale when interactive daemon audit is complete */
158 boolean_t ipc_importance_interactive_receiver
= FALSE
;
160 static zone_t ipc_importance_task_zone
;
161 static zone_t ipc_importance_inherit_zone
;
163 static ipc_voucher_attr_control_t ipc_importance_control
;
165 static boolean_t
ipc_importance_task_check_transition(ipc_importance_task_t task_imp
,
166 iit_update_type_t type
, uint32_t delta
);
168 static void ipc_importance_task_propagate_assertion_locked(ipc_importance_task_t task_imp
,
169 iit_update_type_t type
, boolean_t update_task_imp
);
171 static ipc_importance_inherit_t
ipc_importance_inherit_from_task(task_t from_task
, task_t to_task
);
174 * Routine: ipc_importance_kmsg_link
176 * Link the kmsg onto the appropriate propagation chain.
177 * If the element is a task importance, we link directly
178 * on its propagation chain. Otherwise, we link onto the
179 * destination task of the inherit.
181 * Importance lock held.
182 * Caller is donating an importance elem reference to the kmsg.
185 ipc_importance_kmsg_link(
187 ipc_importance_elem_t elem
)
189 ipc_importance_elem_t link_elem
;
191 assert(IIE_NULL
== kmsg
->ikm_importance
);
193 link_elem
= (IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) ?
194 (ipc_importance_elem_t
)((ipc_importance_inherit_t
)elem
)->iii_to_task
:
197 queue_enter(&link_elem
->iie_kmsgs
, kmsg
, ipc_kmsg_t
, ikm_inheritance
);
198 kmsg
->ikm_importance
= elem
;
202 * Routine: ipc_importance_kmsg_unlink
204 * Unlink the kmsg from its current propagation chain.
205 * If the element is a task importance, we unlink directly
206 * from its propagation chain. Otherwise, we unlink from the
207 * destination task of the inherit.
209 * The reference to the importance element it was linked on.
211 * Importance lock held.
212 * Caller is responsible for dropping reference on returned elem.
214 static ipc_importance_elem_t
215 ipc_importance_kmsg_unlink(
218 ipc_importance_elem_t elem
= kmsg
->ikm_importance
;
220 if (IIE_NULL
!= elem
) {
221 ipc_importance_elem_t unlink_elem
;
223 unlink_elem
= (IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) ?
224 (ipc_importance_elem_t
)((ipc_importance_inherit_t
)elem
)->iii_to_task
:
227 queue_remove(&unlink_elem
->iie_kmsgs
, kmsg
, ipc_kmsg_t
, ikm_inheritance
);
228 kmsg
->ikm_importance
= IIE_NULL
;
234 * Routine: ipc_importance_inherit_link
236 * Link the inherit onto the appropriate propagation chain.
237 * If the element is a task importance, we link directly
238 * on its propagation chain. Otherwise, we link onto the
239 * destination task of the inherit.
241 * Importance lock held.
242 * Caller is donating an elem importance reference to the inherit.
245 ipc_importance_inherit_link(
246 ipc_importance_inherit_t inherit
,
247 ipc_importance_elem_t elem
)
249 ipc_importance_task_t link_task
;
251 assert(IIE_NULL
== inherit
->iii_from_elem
);
252 link_task
= (IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) ?
253 ((ipc_importance_inherit_t
)elem
)->iii_to_task
:
254 (ipc_importance_task_t
)elem
;
256 queue_enter(&link_task
->iit_inherits
, inherit
,
257 ipc_importance_inherit_t
, iii_inheritance
);
258 inherit
->iii_from_elem
= elem
;
262 * Routine: ipc_importance_inherit_find
264 * Find an existing inherit that links the from element to the
265 * to_task at a given nesting depth. As inherits from other
266 * inherits are actually linked off the original inherit's donation
267 * receiving task, we have to conduct our search from there if
268 * the from element is an inherit.
270 * A pointer (not a reference) to the matching inherit.
272 * Importance lock held.
274 static ipc_importance_inherit_t
275 ipc_importance_inherit_find(
276 ipc_importance_elem_t from
,
277 ipc_importance_task_t to_task
,
280 ipc_importance_task_t link_task
;
281 ipc_importance_inherit_t inherit
;
283 link_task
= (IIE_TYPE_INHERIT
== IIE_TYPE(from
)) ?
284 ((ipc_importance_inherit_t
)from
)->iii_to_task
:
285 (ipc_importance_task_t
)from
;
287 queue_iterate(&link_task
->iit_inherits
, inherit
,
288 ipc_importance_inherit_t
, iii_inheritance
) {
289 if (inherit
->iii_to_task
== to_task
&& inherit
->iii_depth
== depth
) {
297 * Routine: ipc_importance_inherit_unlink
299 * Unlink the inherit from its current propagation chain.
300 * If the element is a task importance, we unlink directly
301 * from its propagation chain. Otherwise, we unlink from the
302 * destination task of the inherit.
304 * The reference to the importance element it was linked on.
306 * Importance lock held.
307 * Caller is responsible for dropping reference on returned elem.
309 static ipc_importance_elem_t
310 ipc_importance_inherit_unlink(
311 ipc_importance_inherit_t inherit
)
313 ipc_importance_elem_t elem
= inherit
->iii_from_elem
;
315 if (IIE_NULL
!= elem
) {
316 ipc_importance_task_t unlink_task
;
318 unlink_task
= (IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) ?
319 ((ipc_importance_inherit_t
)elem
)->iii_to_task
:
320 (ipc_importance_task_t
)elem
;
322 queue_remove(&unlink_task
->iit_inherits
, inherit
,
323 ipc_importance_inherit_t
, iii_inheritance
);
324 inherit
->iii_from_elem
= IIE_NULL
;
330 * Routine: ipc_importance_reference
332 * Add a reference to the importance element.
334 * Caller must hold a reference on the element.
337 ipc_importance_reference(ipc_importance_elem_t elem
)
339 assert(0 < IIE_REFS(elem
));
340 ipc_importance_reference_internal(elem
);
344 * Routine: ipc_importance_release_locked
346 * Release a reference on an importance attribute value,
347 * unlinking and deallocating the attribute if the last reference.
349 * Entered with importance lock held, leaves with it unlocked.
352 ipc_importance_release_locked(ipc_importance_elem_t elem
)
354 assert(0 < IIE_REFS(elem
));
357 ipc_importance_inherit_t temp_inherit
;
358 ipc_importance_task_t link_task
;
359 ipc_kmsg_t temp_kmsg
;
360 uint32_t expected
= 0;
362 if (0 < elem
->iie_made
) {
366 link_task
= (IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) ?
367 ((ipc_importance_inherit_t
)elem
)->iii_to_task
:
368 (ipc_importance_task_t
)elem
;
370 queue_iterate(&link_task
->iit_kmsgs
, temp_kmsg
, ipc_kmsg_t
, ikm_inheritance
)
371 if (temp_kmsg
->ikm_importance
== elem
) {
374 queue_iterate(&link_task
->iit_inherits
, temp_inherit
,
375 ipc_importance_inherit_t
, iii_inheritance
)
376 if (temp_inherit
->iii_from_elem
== elem
) {
379 if (IIE_REFS(elem
) < expected
+ 1) {
380 panic("ipc_importance_release_locked (%p)", elem
);
382 #endif /* IMPORTANCE_DEBUG */
384 if (0 < ipc_importance_release_internal(elem
)) {
385 ipc_importance_unlock();
391 switch (IIE_TYPE(elem
)) {
392 /* just a "from" task reference to drop */
395 ipc_importance_task_t task_elem
;
397 task_elem
= (ipc_importance_task_t
)elem
;
399 /* the task can't still hold a reference on the task importance */
400 assert(TASK_NULL
== task_elem
->iit_task
);
402 #if DEVELOPMENT || DEBUG
403 queue_remove(&global_iit_alloc_queue
, task_elem
, ipc_importance_task_t
, iit_allocation
);
406 ipc_importance_unlock();
408 zfree(ipc_importance_task_zone
, task_elem
);
412 /* dropping an inherit element */
413 case IIE_TYPE_INHERIT
:
415 ipc_importance_inherit_t inherit
= (ipc_importance_inherit_t
)elem
;
416 ipc_importance_task_t to_task
= inherit
->iii_to_task
;
417 ipc_importance_elem_t from_elem
;
419 assert(IIT_NULL
!= to_task
);
420 assert(ipc_importance_task_is_any_receiver_type(to_task
));
422 /* unlink the inherit from its source element */
423 from_elem
= ipc_importance_inherit_unlink(inherit
);
424 assert(IIE_NULL
!= from_elem
);
427 * The attribute might have pending external boosts if the attribute
428 * was given out during exec, drop them from the appropriate destination
431 * The attribute will not have any pending external boosts if the
432 * attribute was given out to voucher system since it would have been
433 * dropped by ipc_importance_release_value, but there is not way to
434 * detect that, thus if the attribute has a pending external boost,
435 * drop them from the appropriate destination task.
437 * The inherit attribute from exec and voucher system would not
438 * get deduped to each other, thus dropping the external boost
439 * from destination task at two different places will not have
440 * any unintended side effects.
442 assert(inherit
->iii_externcnt
>= inherit
->iii_externdrop
);
443 if (inherit
->iii_donating
) {
444 uint32_t assertcnt
= III_EXTERN(inherit
);
446 assert(ipc_importance_task_is_any_receiver_type(to_task
));
447 assert(to_task
->iit_externcnt
>= inherit
->iii_externcnt
);
448 assert(to_task
->iit_externdrop
>= inherit
->iii_externdrop
);
449 to_task
->iit_externcnt
-= inherit
->iii_externcnt
;
450 to_task
->iit_externdrop
-= inherit
->iii_externdrop
;
451 inherit
->iii_externcnt
= 0;
452 inherit
->iii_externdrop
= 0;
453 inherit
->iii_donating
= FALSE
;
455 /* adjust the internal assertions - and propagate as needed */
456 if (ipc_importance_task_check_transition(to_task
, IIT_UPDATE_DROP
, assertcnt
)) {
457 ipc_importance_task_propagate_assertion_locked(to_task
, IIT_UPDATE_DROP
, TRUE
);
460 inherit
->iii_externcnt
= 0;
461 inherit
->iii_externdrop
= 0;
464 /* release the reference on the source element */
465 ipc_importance_release_locked(from_elem
);
466 /* unlocked on return */
468 /* release the reference on the destination task */
469 ipc_importance_task_release(to_task
);
471 /* free the inherit */
472 zfree(ipc_importance_inherit_zone
, inherit
);
479 * Routine: ipc_importance_release
481 * Release a reference on an importance attribute value,
482 * unlinking and deallocating the attribute if the last reference.
484 * nothing locked on entrance, nothing locked on exit.
488 ipc_importance_release(ipc_importance_elem_t elem
)
490 if (IIE_NULL
== elem
) {
494 ipc_importance_lock();
495 ipc_importance_release_locked(elem
);
500 * Routine: ipc_importance_task_reference
504 * Retain a reference on a task importance attribute value.
506 * nothing locked on entrance, nothing locked on exit.
507 * caller holds a reference already.
510 ipc_importance_task_reference(ipc_importance_task_t task_elem
)
512 if (IIT_NULL
== task_elem
) {
516 incr_ref_counter(task_elem
->iit_elem
.iie_task_refs_added
);
518 ipc_importance_reference(&task_elem
->iit_elem
);
522 * Routine: ipc_importance_task_release
524 * Release a reference on a task importance attribute value,
525 * unlinking and deallocating the attribute if the last reference.
527 * nothing locked on entrance, nothing locked on exit.
531 ipc_importance_task_release(ipc_importance_task_t task_elem
)
533 if (IIT_NULL
== task_elem
) {
537 ipc_importance_lock();
539 incr_ref_counter(task_elem
->iit_elem
.iie_task_refs_dropped
);
541 ipc_importance_release_locked(&task_elem
->iit_elem
);
546 * Routine: ipc_importance_task_release_locked
548 * Release a reference on a task importance attribute value,
549 * unlinking and deallocating the attribute if the last reference.
551 * importance lock held on entry, nothing locked on exit.
555 ipc_importance_task_release_locked(ipc_importance_task_t task_elem
)
557 if (IIT_NULL
== task_elem
) {
558 ipc_importance_unlock();
562 incr_ref_counter(task_elem
->iit_elem
.iie_task_refs_dropped
);
564 ipc_importance_release_locked(&task_elem
->iit_elem
);
569 * Routines for importance donation/inheritance/boosting
574 * External importance assertions are managed by the process in userspace
575 * Internal importance assertions are the responsibility of the kernel
576 * Assertions are changed from internal to external via task_importance_externalize_assertion
580 * Routine: ipc_importance_task_check_transition
582 * Increase or decrement the internal task importance counter of the
583 * specified task and determine if propagation and a task policy
584 * update is required.
586 * If it is already enqueued for a policy update, steal it from that queue
587 * (as we are reversing that update before it happens).
590 * Called with the importance lock held.
591 * It is the caller's responsibility to perform the propagation of the
592 * transition and/or policy changes by checking the return value.
595 ipc_importance_task_check_transition(
596 ipc_importance_task_t task_imp
,
597 iit_update_type_t type
,
601 task_t target_task
= task_imp
->iit_task
;
603 boolean_t boost
= (IIT_UPDATE_HOLD
== type
);
604 boolean_t before_boosted
, after_boosted
;
606 ipc_importance_assert_held();
608 if (!ipc_importance_task_is_any_receiver_type(task_imp
)) {
613 int target_pid
= task_pid(target_task
);
615 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, (((boost
) ? IMP_HOLD
: IMP_DROP
) | TASK_POLICY_INTERNAL
))) | DBG_FUNC_START
,
616 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_EXTERN(task_imp
), 0);
619 /* snapshot the effective boosting status before making any changes */
620 before_boosted
= (task_imp
->iit_assertcnt
> 0);
622 /* Adjust the assertcnt appropriately */
624 task_imp
->iit_assertcnt
+= delta
;
626 DTRACE_BOOST6(send_boost
, task_t
, target_task
, int, target_pid
,
627 task_t
, current_task(), int, proc_selfpid(), int, delta
, int, task_imp
->iit_assertcnt
);
630 // assert(delta <= task_imp->iit_assertcnt);
631 if (task_imp
->iit_assertcnt
< delta
+ IIT_EXTERN(task_imp
)) {
632 /* TODO: Turn this back into a panic <rdar://problem/12592649> */
633 task_imp
->iit_assertcnt
= IIT_EXTERN(task_imp
);
635 task_imp
->iit_assertcnt
-= delta
;
638 // This convers both legacy and voucher-based importance.
639 DTRACE_BOOST4(drop_boost
, task_t
, target_task
, int, target_pid
, int, delta
, int, task_imp
->iit_assertcnt
);
644 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, (((boost
) ? IMP_HOLD
: IMP_DROP
) | TASK_POLICY_INTERNAL
))) | DBG_FUNC_END
,
645 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_EXTERN(task_imp
), 0);
648 /* did the change result in an effective donor status change? */
649 after_boosted
= (task_imp
->iit_assertcnt
> 0);
651 if (after_boosted
!= before_boosted
) {
653 * If the task importance is already on an update queue, we just reversed the need for a
654 * pending policy update. If the queue is any other than the delayed-drop-queue, pull it
655 * off that queue and release the reference it got going onto the update queue. If it is
656 * the delayed-drop-queue we leave it in place in case it comes back into the drop state
657 * before its time delay is up.
659 * We still need to propagate the change downstream to reverse the assertcnt effects,
660 * but we no longer need to update this task's boost policy state.
662 * Otherwise, mark it as needing a policy update.
664 assert(0 == task_imp
->iit_updatepolicy
);
665 if (NULL
!= task_imp
->iit_updateq
) {
666 if (&ipc_importance_delayed_drop_queue
!= task_imp
->iit_updateq
) {
667 queue_remove(task_imp
->iit_updateq
, task_imp
, ipc_importance_task_t
, iit_updates
);
668 task_imp
->iit_updateq
= NULL
;
669 ipc_importance_task_release_internal(task_imp
); /* can't be last ref */
672 task_imp
->iit_updatepolicy
= 1;
682 * Routine: ipc_importance_task_propagate_helper
684 * Increase or decrement the internal task importance counter of all
685 * importance tasks inheriting from the specified one. If this causes
686 * that importance task to change state, add it to the list of tasks
687 * to do a policy update against.
689 * Called with the importance lock held.
690 * It is the caller's responsibility to iterate down the generated list
691 * and propagate any subsequent assertion changes from there.
694 ipc_importance_task_propagate_helper(
695 ipc_importance_task_t task_imp
,
696 iit_update_type_t type
,
699 ipc_importance_task_t temp_task_imp
;
702 * iterate the downstream kmsgs, adjust their boosts,
703 * and capture the next task to adjust for each message
706 ipc_kmsg_t temp_kmsg
;
708 queue_iterate(&task_imp
->iit_kmsgs
, temp_kmsg
, ipc_kmsg_t
, ikm_inheritance
) {
709 mach_msg_header_t
*hdr
= temp_kmsg
->ikm_header
;
710 mach_port_delta_t delta
;
713 /* toggle the kmsg importance bit as a barrier to parallel adjusts */
714 if (IIT_UPDATE_HOLD
== type
) {
715 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(hdr
->msgh_bits
)) {
719 /* mark the message as now carrying importance */
720 hdr
->msgh_bits
|= MACH_MSGH_BITS_RAISEIMP
;
723 if (!MACH_MSGH_BITS_RAISED_IMPORTANCE(hdr
->msgh_bits
)) {
727 /* clear the message as now carrying importance */
728 hdr
->msgh_bits
&= ~MACH_MSGH_BITS_RAISEIMP
;
732 /* determine the task importance to adjust as result (if any) */
733 port
= (ipc_port_t
) hdr
->msgh_remote_port
;
734 assert(IP_VALID(port
));
736 temp_task_imp
= IIT_NULL
;
737 if (!ipc_port_importance_delta_internal(port
, IPID_OPTION_NORMAL
, &delta
, &temp_task_imp
)) {
741 /* no task importance to adjust associated with the port? */
742 if (IIT_NULL
== temp_task_imp
) {
746 /* hold a reference on temp_task_imp */
748 /* Adjust the task assertions and determine if an edge was crossed */
749 if (ipc_importance_task_check_transition(temp_task_imp
, type
, 1)) {
750 incr_ref_counter(temp_task_imp
->iit_elem
.iie_task_refs_added_transition
);
751 queue_enter(propagation
, temp_task_imp
, ipc_importance_task_t
, iit_props
);
752 /* reference donated */
754 ipc_importance_task_release_internal(temp_task_imp
);
759 * iterate the downstream importance inherits
760 * and capture the next task importance to boost for each
762 ipc_importance_inherit_t temp_inherit
;
764 queue_iterate(&task_imp
->iit_inherits
, temp_inherit
, ipc_importance_inherit_t
, iii_inheritance
) {
765 uint32_t assertcnt
= III_EXTERN(temp_inherit
);
767 temp_task_imp
= temp_inherit
->iii_to_task
;
768 assert(IIT_NULL
!= temp_task_imp
);
770 if (IIT_UPDATE_HOLD
== type
) {
771 /* if no undropped externcnts in the inherit, nothing to do */
772 if (0 == assertcnt
) {
773 assert(temp_inherit
->iii_donating
== FALSE
);
777 /* nothing to do if the inherit is already donating (forced donation) */
778 if (temp_inherit
->iii_donating
) {
782 /* mark it donating and contribute to the task externcnts */
783 temp_inherit
->iii_donating
= TRUE
;
784 temp_task_imp
->iit_externcnt
+= temp_inherit
->iii_externcnt
;
785 temp_task_imp
->iit_externdrop
+= temp_inherit
->iii_externdrop
;
787 /* if no contributing assertions, move on */
788 if (0 == assertcnt
) {
789 assert(temp_inherit
->iii_donating
== FALSE
);
793 /* nothing to do if the inherit is not donating */
794 if (!temp_inherit
->iii_donating
) {
798 /* mark it no longer donating */
799 temp_inherit
->iii_donating
= FALSE
;
801 /* remove the contribution the inherit made to the to-task */
802 assert(IIT_EXTERN(temp_task_imp
) >= III_EXTERN(temp_inherit
));
803 assert(temp_task_imp
->iit_externcnt
>= temp_inherit
->iii_externcnt
);
804 assert(temp_task_imp
->iit_externdrop
>= temp_inherit
->iii_externdrop
);
805 temp_task_imp
->iit_externcnt
-= temp_inherit
->iii_externcnt
;
806 temp_task_imp
->iit_externdrop
-= temp_inherit
->iii_externdrop
;
809 /* Adjust the task assertions and determine if an edge was crossed */
810 assert(ipc_importance_task_is_any_receiver_type(temp_task_imp
));
811 if (ipc_importance_task_check_transition(temp_task_imp
, type
, assertcnt
)) {
812 ipc_importance_task_reference(temp_task_imp
);
813 incr_ref_counter(temp_task_imp
->iit_elem
.iie_task_refs_added_transition
);
814 queue_enter(propagation
, temp_task_imp
, ipc_importance_task_t
, iit_props
);
820 * Routine: ipc_importance_task_process_updates
822 * Process the queue of task importances and apply the policy
823 * update called for. Only process tasks in the queue with an
824 * update timestamp less than the supplied max.
826 * Called and returns with importance locked.
827 * May drop importance lock and block temporarily.
830 ipc_importance_task_process_updates(
831 queue_t supplied_queue
,
833 uint64_t max_timestamp
)
835 ipc_importance_task_t task_imp
;
836 queue_head_t second_chance
;
837 queue_t queue
= supplied_queue
;
840 * This queue will hold the task's we couldn't trylock on first pass.
841 * By using a second (private) queue, we guarantee all tasks that get
842 * entered on this queue have a timestamp under the maximum.
844 queue_init(&second_chance
);
846 /* process any resulting policy updates */
848 while (!queue_empty(queue
)) {
850 struct task_pend_token pend_token
= {};
852 task_imp
= (ipc_importance_task_t
)queue_first(queue
);
853 assert(0 == task_imp
->iit_updatepolicy
);
854 assert(queue
== task_imp
->iit_updateq
);
856 /* if timestamp is too big, we're done */
857 if (task_imp
->iit_updatetime
> max_timestamp
) {
861 /* we were given a reference on each task in the queue */
863 /* remove it from the supplied queue */
864 queue_remove(queue
, task_imp
, ipc_importance_task_t
, iit_updates
);
865 task_imp
->iit_updateq
= NULL
;
867 target_task
= task_imp
->iit_task
;
869 /* Is it well on the way to exiting? */
870 if (TASK_NULL
== target_task
) {
871 ipc_importance_task_release_locked(task_imp
);
872 /* importance unlocked */
873 ipc_importance_lock();
877 /* Has the update been reversed on the hysteresis queue? */
878 if (0 < task_imp
->iit_assertcnt
&&
879 queue
== &ipc_importance_delayed_drop_queue
) {
880 ipc_importance_task_release_locked(task_imp
);
881 /* importance unlocked */
882 ipc_importance_lock();
887 * Can we get the task lock out-of-order?
888 * If not, stick this back on the second-chance queue.
890 if (!task_lock_try(target_task
)) {
891 boolean_t should_wait_lock
= (queue
== &second_chance
);
892 task_imp
->iit_updateq
= &second_chance
;
895 * If we're already processing second-chances on
896 * tasks, keep this task on the front of the queue.
897 * We will wait for the task lock before coming
898 * back and trying again, and we have a better
899 * chance of re-acquiring the lock if we come back
902 if (should_wait_lock
) {
903 task_reference(target_task
);
904 queue_enter_first(&second_chance
, task_imp
,
905 ipc_importance_task_t
, iit_updates
);
907 queue_enter(&second_chance
, task_imp
,
908 ipc_importance_task_t
, iit_updates
);
910 ipc_importance_unlock();
912 if (should_wait_lock
) {
913 task_lock(target_task
);
914 task_unlock(target_task
);
915 task_deallocate(target_task
);
918 ipc_importance_lock();
922 /* is it going away? */
923 if (!target_task
->active
) {
924 task_unlock(target_task
);
925 ipc_importance_task_release_locked(task_imp
);
926 /* importance unlocked */
927 ipc_importance_lock();
931 /* take a task reference for while we don't have the importance lock */
932 task_reference(target_task
);
934 /* count the transition */
936 task_imp
->iit_transitions
++;
939 ipc_importance_unlock();
941 /* apply the policy adjust to the target task (while it is still locked) */
942 task_update_boost_locked(target_task
, boost
, &pend_token
);
944 /* complete the policy update with the task unlocked */
945 ipc_importance_task_release(task_imp
);
946 task_unlock(target_task
);
947 task_policy_update_complete_unlocked(target_task
, &pend_token
);
948 task_deallocate(target_task
);
950 ipc_importance_lock();
953 /* If there are tasks we couldn't update the first time, try again */
954 if (!queue_empty(&second_chance
)) {
955 queue
= &second_chance
;
962 * Routine: ipc_importance_task_delayed_drop_scan
964 * The thread call routine to scan the delayed drop queue,
965 * requesting all updates with a deadline up to the last target
966 * for the thread-call (which is DENAP_DROP_SKEW beyond the first
967 * thread's optimum delay).
968 * update to drop its boost.
973 ipc_importance_task_delayed_drop_scan(
977 ipc_importance_lock();
979 /* process all queued task drops with timestamps up to TARGET(first)+SKEW */
980 ipc_importance_task_process_updates(&ipc_importance_delayed_drop_queue
,
982 ipc_importance_delayed_drop_timestamp
);
984 /* importance lock may have been temporarily dropped */
986 /* If there are any entries left in the queue, re-arm the call here */
987 if (!queue_empty(&ipc_importance_delayed_drop_queue
)) {
988 ipc_importance_task_t task_imp
;
992 task_imp
= (ipc_importance_task_t
)queue_first(&ipc_importance_delayed_drop_queue
);
994 nanoseconds_to_absolutetime(DENAP_DROP_DELAY
, &deadline
);
995 deadline
+= task_imp
->iit_updatetime
;
996 ipc_importance_delayed_drop_timestamp
= deadline
;
998 nanoseconds_to_absolutetime(DENAP_DROP_LEEWAY
, &leeway
);
1000 thread_call_enter_delayed_with_leeway(
1001 ipc_importance_delayed_drop_call
,
1007 ipc_importance_delayed_drop_call_requested
= FALSE
;
1009 ipc_importance_unlock();
1013 * Routine: ipc_importance_task_delayed_drop
1015 * Queue the specified task importance for delayed policy
1016 * update to drop its boost.
1018 * Called with the importance lock held.
1021 ipc_importance_task_delayed_drop(ipc_importance_task_t task_imp
)
1023 uint64_t timestamp
= mach_absolute_time(); /* no mach_approximate_time() in kernel */
1025 assert(ipc_importance_delayed_drop_call
!= NULL
);
1028 * If still on an update queue from a previous change,
1029 * remove it first (and use that reference). Otherwise, take
1030 * a new reference for the delay drop update queue.
1032 if (NULL
!= task_imp
->iit_updateq
) {
1033 queue_remove(task_imp
->iit_updateq
, task_imp
,
1034 ipc_importance_task_t
, iit_updates
);
1036 ipc_importance_task_reference_internal(task_imp
);
1039 task_imp
->iit_updateq
= &ipc_importance_delayed_drop_queue
;
1040 task_imp
->iit_updatetime
= timestamp
;
1042 queue_enter(&ipc_importance_delayed_drop_queue
, task_imp
,
1043 ipc_importance_task_t
, iit_updates
);
1045 /* request the delayed thread-call if not already requested */
1046 if (!ipc_importance_delayed_drop_call_requested
) {
1050 nanoseconds_to_absolutetime(DENAP_DROP_DELAY
, &deadline
);
1051 deadline
+= task_imp
->iit_updatetime
;
1052 ipc_importance_delayed_drop_timestamp
= deadline
;
1054 nanoseconds_to_absolutetime(DENAP_DROP_LEEWAY
, &leeway
);
1056 ipc_importance_delayed_drop_call_requested
= TRUE
;
1057 thread_call_enter_delayed_with_leeway(
1058 ipc_importance_delayed_drop_call
,
1068 * Routine: ipc_importance_task_propagate_assertion_locked
1070 * Propagate the importance transition type to every item
1071 * If this causes a boost to be applied, determine if that
1072 * boost should propagate downstream.
1074 * Called with the importance lock held.
1077 ipc_importance_task_propagate_assertion_locked(
1078 ipc_importance_task_t task_imp
,
1079 iit_update_type_t type
,
1080 boolean_t update_task_imp
)
1082 boolean_t boost
= (IIT_UPDATE_HOLD
== type
);
1083 ipc_importance_task_t temp_task_imp
;
1084 queue_head_t propagate
;
1085 queue_head_t updates
;
1087 queue_init(&updates
);
1088 queue_init(&propagate
);
1090 ipc_importance_assert_held();
1093 * If we're going to update the policy for the provided task,
1094 * enqueue it on the propagate queue itself. Otherwise, only
1095 * enqueue downstream things.
1097 if (update_task_imp
) {
1098 ipc_importance_task_reference(task_imp
);
1099 incr_ref_counter(task_imp
->iit_elem
.iie_task_refs_added_transition
);
1100 queue_enter(&propagate
, task_imp
, ipc_importance_task_t
, iit_props
);
1102 ipc_importance_task_propagate_helper(task_imp
, type
, &propagate
);
1106 * for each item on the propagation list, propagate any change downstream,
1107 * adding new tasks to propagate further if they transistioned as well.
1109 while (!queue_empty(&propagate
)) {
1110 boolean_t need_update
;
1112 queue_remove_first(&propagate
, temp_task_imp
, ipc_importance_task_t
, iit_props
);
1113 /* hold a reference on temp_task_imp */
1115 assert(IIT_NULL
!= temp_task_imp
);
1117 /* only propagate for receivers not already marked as a donor */
1118 if (!ipc_importance_task_is_marked_donor(temp_task_imp
) &&
1119 ipc_importance_task_is_marked_receiver(temp_task_imp
)) {
1120 ipc_importance_task_propagate_helper(temp_task_imp
, type
, &propagate
);
1123 /* if we have a policy update to apply, enqueue a reference for later processing */
1124 need_update
= (0 != temp_task_imp
->iit_updatepolicy
);
1125 temp_task_imp
->iit_updatepolicy
= 0;
1126 if (need_update
&& TASK_NULL
!= temp_task_imp
->iit_task
) {
1127 if (NULL
== temp_task_imp
->iit_updateq
) {
1129 * If a downstream task that needs an update is subjects to AppNap,
1130 * drop boosts according to the delay hysteresis. Otherwise,
1131 * immediate update it.
1133 if (!boost
&& temp_task_imp
!= task_imp
&&
1134 ipc_importance_delayed_drop_call
!= NULL
&&
1135 ipc_importance_task_is_marked_denap_receiver(temp_task_imp
)) {
1136 ipc_importance_task_delayed_drop(temp_task_imp
);
1138 temp_task_imp
->iit_updatetime
= 0;
1139 temp_task_imp
->iit_updateq
= &updates
;
1140 ipc_importance_task_reference_internal(temp_task_imp
);
1142 queue_enter(&updates
, temp_task_imp
,
1143 ipc_importance_task_t
, iit_updates
);
1145 queue_enter_first(&updates
, temp_task_imp
,
1146 ipc_importance_task_t
, iit_updates
);
1150 /* Must already be on the AppNap hysteresis queue */
1151 assert(ipc_importance_delayed_drop_call
!= NULL
);
1152 assert(ipc_importance_task_is_marked_denap_receiver(temp_task_imp
));
1156 ipc_importance_task_release_internal(temp_task_imp
);
1159 /* apply updates to task (may drop importance lock) */
1160 if (!queue_empty(&updates
)) {
1161 ipc_importance_task_process_updates(&updates
, boost
, 0);
1166 * Routine: ipc_importance_task_hold_internal_assertion_locked
1168 * Increment the assertion count on the task importance.
1169 * If this results in a boost state change in that task,
1170 * prepare to update task policy for this task AND, if
1171 * if not just waking out of App Nap, all down-stream
1172 * tasks that have a similar transition through inheriting
1175 * importance locked on entry and exit.
1176 * May temporarily drop importance lock and block.
1178 static kern_return_t
1179 ipc_importance_task_hold_internal_assertion_locked(ipc_importance_task_t task_imp
, uint32_t count
)
1181 if (ipc_importance_task_check_transition(task_imp
, IIT_UPDATE_HOLD
, count
)) {
1182 ipc_importance_task_propagate_assertion_locked(task_imp
, IIT_UPDATE_HOLD
, TRUE
);
1184 return KERN_SUCCESS
;
1188 * Routine: ipc_importance_task_drop_internal_assertion_locked
1190 * Decrement the assertion count on the task importance.
1191 * If this results in a boost state change in that task,
1192 * prepare to update task policy for this task AND, if
1193 * if not just waking out of App Nap, all down-stream
1194 * tasks that have a similar transition through inheriting
1197 * importance locked on entry and exit.
1198 * May temporarily drop importance lock and block.
1200 static kern_return_t
1201 ipc_importance_task_drop_internal_assertion_locked(ipc_importance_task_t task_imp
, uint32_t count
)
1203 if (ipc_importance_task_check_transition(task_imp
, IIT_UPDATE_DROP
, count
)) {
1204 ipc_importance_task_propagate_assertion_locked(task_imp
, IIT_UPDATE_DROP
, TRUE
);
1206 return KERN_SUCCESS
;
1210 * Routine: ipc_importance_task_hold_internal_assertion
1212 * Increment the assertion count on the task importance.
1213 * If this results in a 0->1 change in that count,
1214 * prepare to update task policy for this task AND
1215 * (potentially) all down-stream tasks that have a
1216 * similar transition through inheriting this update.
1219 * May block after dropping importance lock.
1222 ipc_importance_task_hold_internal_assertion(ipc_importance_task_t task_imp
, uint32_t count
)
1224 int ret
= KERN_SUCCESS
;
1226 if (ipc_importance_task_is_any_receiver_type(task_imp
)) {
1227 ipc_importance_lock();
1228 ret
= ipc_importance_task_hold_internal_assertion_locked(task_imp
, count
);
1229 ipc_importance_unlock();
1235 * Routine: ipc_importance_task_drop_internal_assertion
1237 * Decrement the assertion count on the task importance.
1238 * If this results in a X->0 change in that count,
1239 * prepare to update task policy for this task AND
1240 * all down-stream tasks that have a similar transition
1241 * through inheriting this drop update.
1243 * Nothing locked on entry.
1244 * May block after dropping importance lock.
1247 ipc_importance_task_drop_internal_assertion(ipc_importance_task_t task_imp
, uint32_t count
)
1249 kern_return_t ret
= KERN_SUCCESS
;
1251 if (ipc_importance_task_is_any_receiver_type(task_imp
)) {
1252 ipc_importance_lock();
1253 ret
= ipc_importance_task_drop_internal_assertion_locked(task_imp
, count
);
1254 ipc_importance_unlock();
1260 * Routine: ipc_importance_task_hold_file_lock_assertion
1262 * Increment the file lock assertion count on the task importance.
1263 * If this results in a 0->1 change in that count,
1264 * prepare to update task policy for this task AND
1265 * (potentially) all down-stream tasks that have a
1266 * similar transition through inheriting this update.
1269 * May block after dropping importance lock.
1272 ipc_importance_task_hold_file_lock_assertion(ipc_importance_task_t task_imp
, uint32_t count
)
1274 kern_return_t ret
= KERN_SUCCESS
;
1276 if (ipc_importance_task_is_any_receiver_type(task_imp
)) {
1277 ipc_importance_lock();
1278 ret
= ipc_importance_task_hold_internal_assertion_locked(task_imp
, count
);
1279 if (KERN_SUCCESS
== ret
) {
1280 task_imp
->iit_filelocks
+= count
;
1282 ipc_importance_unlock();
1288 * Routine: ipc_importance_task_drop_file_lock_assertion
1290 * Decrement the assertion count on the task importance.
1291 * If this results in a X->0 change in that count,
1292 * prepare to update task policy for this task AND
1293 * all down-stream tasks that have a similar transition
1294 * through inheriting this drop update.
1296 * Nothing locked on entry.
1297 * May block after dropping importance lock.
1300 ipc_importance_task_drop_file_lock_assertion(ipc_importance_task_t task_imp
, uint32_t count
)
1302 kern_return_t ret
= KERN_SUCCESS
;
1304 if (ipc_importance_task_is_any_receiver_type(task_imp
)) {
1305 ipc_importance_lock();
1306 if (count
<= task_imp
->iit_filelocks
) {
1307 task_imp
->iit_filelocks
-= count
;
1308 ret
= ipc_importance_task_drop_internal_assertion_locked(task_imp
, count
);
1310 ret
= KERN_INVALID_ARGUMENT
;
1312 ipc_importance_unlock();
1318 * Routine: ipc_importance_task_hold_legacy_external_assertion
1320 * Increment the external assertion count on the task importance.
1321 * This cannot result in an 0->1 transition, as the caller must
1322 * already hold an external boost.
1324 * Nothing locked on entry.
1325 * May block after dropping importance lock.
1326 * A queue of task importance structures is returned
1327 * by ipc_importance_task_hold_assertion_locked(). Each
1328 * needs to be updated (outside the importance lock hold).
1331 ipc_importance_task_hold_legacy_external_assertion(ipc_importance_task_t task_imp
, uint32_t count
)
1334 uint32_t target_assertcnt
;
1335 uint32_t target_externcnt
;
1336 uint32_t target_legacycnt
;
1340 ipc_importance_lock();
1341 target_task
= task_imp
->iit_task
;
1343 #if IMPORTANCE_TRACE
1344 int target_pid
= task_pid(target_task
);
1346 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, (IMP_HOLD
| TASK_POLICY_EXTERNAL
))) | DBG_FUNC_START
,
1347 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_LEGACY_EXTERN(task_imp
), 0);
1350 if (IIT_LEGACY_EXTERN(task_imp
) == 0) {
1351 /* Only allowed to take a new boost assertion when holding an external boost */
1352 /* save data for diagnostic printf below */
1353 target_assertcnt
= task_imp
->iit_assertcnt
;
1354 target_externcnt
= IIT_EXTERN(task_imp
);
1355 target_legacycnt
= IIT_LEGACY_EXTERN(task_imp
);
1359 assert(ipc_importance_task_is_any_receiver_type(task_imp
));
1360 assert(0 < task_imp
->iit_assertcnt
);
1361 assert(0 < IIT_EXTERN(task_imp
));
1362 task_imp
->iit_assertcnt
+= count
;
1363 task_imp
->iit_externcnt
+= count
;
1364 task_imp
->iit_legacy_externcnt
+= count
;
1367 ipc_importance_unlock();
1369 #if IMPORTANCE_TRACE
1370 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, (IMP_HOLD
| TASK_POLICY_EXTERNAL
))) | DBG_FUNC_END
,
1371 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_LEGACY_EXTERN(task_imp
), 0);
1372 // This covers the legacy case where a task takes an extra boost.
1373 DTRACE_BOOST5(receive_boost
, task_t
, target_task
, int, target_pid
, int, proc_selfpid(), int, count
, int, task_imp
->iit_assertcnt
);
1376 if (KERN_FAILURE
== ret
&& target_task
!= TASK_NULL
) {
1377 printf("BUG in process %s[%d]: "
1378 "attempt to acquire an additional legacy external boost assertion without holding an existing legacy external assertion. "
1379 "(%d total, %d external, %d legacy-external)\n",
1380 proc_name_address(target_task
->bsd_info
), task_pid(target_task
),
1381 target_assertcnt
, target_externcnt
, target_legacycnt
);
1388 * Routine: ipc_importance_task_drop_legacy_external_assertion
1390 * Drop the legacy external assertion count on the task and
1391 * reflect that change to total external assertion count and
1392 * then onto the internal importance count.
1394 * If this results in a X->0 change in the internal,
1395 * count, prepare to update task policy for this task AND
1396 * all down-stream tasks that have a similar transition
1397 * through inheriting this update.
1399 * Nothing locked on entry.
1402 ipc_importance_task_drop_legacy_external_assertion(ipc_importance_task_t task_imp
, uint32_t count
)
1404 int ret
= KERN_SUCCESS
;
1406 uint32_t target_assertcnt
;
1407 uint32_t target_externcnt
;
1408 uint32_t target_legacycnt
;
1411 return KERN_INVALID_ARGUMENT
;
1414 ipc_importance_lock();
1415 target_task
= task_imp
->iit_task
;
1417 #if IMPORTANCE_TRACE
1418 int target_pid
= task_pid(target_task
);
1420 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, (IMP_DROP
| TASK_POLICY_EXTERNAL
))) | DBG_FUNC_START
,
1421 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_LEGACY_EXTERN(task_imp
), 0);
1424 if (count
> IIT_LEGACY_EXTERN(task_imp
)) {
1425 /* Process over-released its boost count - save data for diagnostic printf */
1426 /* TODO: If count > 1, we should clear out as many external assertions as there are left. */
1427 target_assertcnt
= task_imp
->iit_assertcnt
;
1428 target_externcnt
= IIT_EXTERN(task_imp
);
1429 target_legacycnt
= IIT_LEGACY_EXTERN(task_imp
);
1433 * decrement legacy external count from the top level and reflect
1434 * into internal for this and all subsequent updates.
1436 assert(ipc_importance_task_is_any_receiver_type(task_imp
));
1437 assert(IIT_EXTERN(task_imp
) >= count
);
1439 task_imp
->iit_legacy_externdrop
+= count
;
1440 task_imp
->iit_externdrop
+= count
;
1442 /* reset extern counters (if appropriate) */
1443 if (IIT_LEGACY_EXTERN(task_imp
) == 0) {
1444 if (IIT_EXTERN(task_imp
) != 0) {
1445 task_imp
->iit_externcnt
-= task_imp
->iit_legacy_externcnt
;
1446 task_imp
->iit_externdrop
-= task_imp
->iit_legacy_externdrop
;
1448 task_imp
->iit_externcnt
= 0;
1449 task_imp
->iit_externdrop
= 0;
1451 task_imp
->iit_legacy_externcnt
= 0;
1452 task_imp
->iit_legacy_externdrop
= 0;
1455 /* reflect the drop to the internal assertion count (and effect any importance change) */
1456 if (ipc_importance_task_check_transition(task_imp
, IIT_UPDATE_DROP
, count
)) {
1457 ipc_importance_task_propagate_assertion_locked(task_imp
, IIT_UPDATE_DROP
, TRUE
);
1462 #if IMPORTANCE_TRACE
1463 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, (IMP_DROP
| TASK_POLICY_EXTERNAL
))) | DBG_FUNC_END
,
1464 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_LEGACY_EXTERN(task_imp
), 0);
1467 ipc_importance_unlock();
1469 /* delayed printf for user-supplied data failures */
1470 if (KERN_FAILURE
== ret
&& TASK_NULL
!= target_task
) {
1471 printf("BUG in process %s[%d]: over-released legacy external boost assertions (%d total, %d external, %d legacy-external)\n",
1472 proc_name_address(target_task
->bsd_info
), task_pid(target_task
),
1473 target_assertcnt
, target_externcnt
, target_legacycnt
);
1481 /* Transfer an assertion to legacy userspace responsibility */
1482 static kern_return_t
1483 ipc_importance_task_externalize_legacy_assertion(ipc_importance_task_t task_imp
, uint32_t count
, __unused
int sender_pid
)
1487 assert(IIT_NULL
!= task_imp
);
1488 target_task
= task_imp
->iit_task
;
1490 if (TASK_NULL
== target_task
||
1491 !ipc_importance_task_is_any_receiver_type(task_imp
)) {
1492 return KERN_FAILURE
;
1495 #if IMPORTANCE_TRACE
1496 int target_pid
= task_pid(target_task
);
1498 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, IMP_EXTERN
)) | DBG_FUNC_START
,
1499 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_EXTERN(task_imp
), 0);
1502 ipc_importance_lock();
1503 /* assert(task_imp->iit_assertcnt >= IIT_EXTERN(task_imp) + count); */
1504 assert(IIT_EXTERN(task_imp
) >= IIT_LEGACY_EXTERN(task_imp
));
1505 task_imp
->iit_legacy_externcnt
+= count
;
1506 task_imp
->iit_externcnt
+= count
;
1507 ipc_importance_unlock();
1509 #if IMPORTANCE_TRACE
1510 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, IMP_EXTERN
)) | DBG_FUNC_END
,
1511 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_LEGACY_EXTERN(task_imp
), 0);
1512 // This is the legacy boosting path
1513 DTRACE_BOOST5(receive_boost
, task_t
, target_task
, int, target_pid
, int, sender_pid
, int, count
, int, IIT_LEGACY_EXTERN(task_imp
));
1514 #endif /* IMPORTANCE_TRACE */
1516 return KERN_SUCCESS
;
1520 * Routine: ipc_importance_task_update_live_donor
1522 * Read the live donor status and update the live_donor bit/propagate the change in importance.
1524 * Nothing locked on entrance, nothing locked on exit.
1526 * TODO: Need tracepoints around this function...
1529 ipc_importance_task_update_live_donor(ipc_importance_task_t task_imp
)
1531 uint32_t task_live_donor
;
1532 boolean_t before_donor
;
1533 boolean_t after_donor
;
1536 assert(task_imp
!= NULL
);
1539 * Nothing to do if the task is not marked as expecting
1540 * live donor updates.
1542 if (!ipc_importance_task_is_marked_live_donor(task_imp
)) {
1546 ipc_importance_lock();
1548 /* If the task got disconnected on the way here, no use (or ability) adjusting live donor status */
1549 target_task
= task_imp
->iit_task
;
1550 if (TASK_NULL
== target_task
) {
1551 ipc_importance_unlock();
1554 before_donor
= ipc_importance_task_is_marked_donor(task_imp
);
1556 /* snapshot task live donor status - may change, but another call will accompany the change */
1557 task_live_donor
= target_task
->effective_policy
.tep_live_donor
;
1559 #if IMPORTANCE_TRACE
1560 int target_pid
= task_pid(target_task
);
1562 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1563 (IMPORTANCE_CODE(IMP_DONOR_CHANGE
, IMP_DONOR_UPDATE_LIVE_DONOR_STATE
)) | DBG_FUNC_START
,
1564 target_pid
, task_imp
->iit_donor
, task_live_donor
, before_donor
, 0);
1567 /* update the task importance live donor status based on the task's value */
1568 task_imp
->iit_donor
= task_live_donor
;
1570 after_donor
= ipc_importance_task_is_marked_donor(task_imp
);
1572 /* Has the effectiveness of being a donor changed as a result of this update? */
1573 if (before_donor
!= after_donor
) {
1574 iit_update_type_t type
;
1576 /* propagate assertions without updating the current task policy (already handled) */
1577 if (0 == before_donor
) {
1578 task_imp
->iit_transitions
++;
1579 type
= IIT_UPDATE_HOLD
;
1581 type
= IIT_UPDATE_DROP
;
1583 ipc_importance_task_propagate_assertion_locked(task_imp
, type
, FALSE
);
1586 #if IMPORTANCE_TRACE
1587 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1588 (IMPORTANCE_CODE(IMP_DONOR_CHANGE
, IMP_DONOR_UPDATE_LIVE_DONOR_STATE
)) | DBG_FUNC_END
,
1589 target_pid
, task_imp
->iit_donor
, task_live_donor
, after_donor
, 0);
1592 ipc_importance_unlock();
1597 * Routine: ipc_importance_task_mark_donor
1599 * Set the task importance donor flag.
1601 * Nothing locked on entrance, nothing locked on exit.
1603 * This is only called while the task is being constructed,
1604 * so no need to update task policy or propagate downstream.
1607 ipc_importance_task_mark_donor(ipc_importance_task_t task_imp
, boolean_t donating
)
1609 assert(task_imp
!= NULL
);
1611 ipc_importance_lock();
1613 int old_donor
= task_imp
->iit_donor
;
1615 task_imp
->iit_donor
= (donating
? 1 : 0);
1617 if (task_imp
->iit_donor
> 0 && old_donor
== 0) {
1618 task_imp
->iit_transitions
++;
1621 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1622 (IMPORTANCE_CODE(IMP_DONOR_CHANGE
, IMP_DONOR_INIT_DONOR_STATE
)) | DBG_FUNC_NONE
,
1623 task_pid(task_imp
->iit_task
), donating
,
1624 old_donor
, task_imp
->iit_donor
, 0);
1626 ipc_importance_unlock();
1630 * Routine: ipc_importance_task_marked_donor
1632 * Query the donor flag for the given task importance.
1634 * May be called without taking the importance lock.
1635 * In that case, donor status can change so you must
1636 * check only once for each donation event.
1639 ipc_importance_task_is_marked_donor(ipc_importance_task_t task_imp
)
1641 if (IIT_NULL
== task_imp
) {
1644 return 0 != task_imp
->iit_donor
;
1648 * Routine: ipc_importance_task_mark_live_donor
1650 * Indicate that the task is eligible for live donor updates.
1652 * Nothing locked on entrance, nothing locked on exit.
1654 * This is only called while the task is being constructed.
1657 ipc_importance_task_mark_live_donor(ipc_importance_task_t task_imp
, boolean_t live_donating
)
1659 assert(task_imp
!= NULL
);
1661 ipc_importance_lock();
1662 task_imp
->iit_live_donor
= (live_donating
? 1 : 0);
1663 ipc_importance_unlock();
1667 * Routine: ipc_importance_task_is_marked_live_donor
1669 * Query the live donor and donor flags for the given task importance.
1671 * May be called without taking the importance lock.
1672 * In that case, donor status can change so you must
1673 * check only once for each donation event.
1676 ipc_importance_task_is_marked_live_donor(ipc_importance_task_t task_imp
)
1678 if (IIT_NULL
== task_imp
) {
1681 return 0 != task_imp
->iit_live_donor
;
1685 * Routine: ipc_importance_task_is_donor
1687 * Query the full donor status for the given task importance.
1689 * May be called without taking the importance lock.
1690 * In that case, donor status can change so you must
1691 * check only once for each donation event.
1694 ipc_importance_task_is_donor(ipc_importance_task_t task_imp
)
1696 if (IIT_NULL
== task_imp
) {
1699 return ipc_importance_task_is_marked_donor(task_imp
) ||
1700 (ipc_importance_task_is_marked_receiver(task_imp
) &&
1701 task_imp
->iit_assertcnt
> 0);
1705 * Routine: ipc_importance_task_is_never_donor
1707 * Query if a given task can ever donate importance.
1709 * May be called without taking the importance lock.
1710 * Condition is permanent for a give task.
1713 ipc_importance_task_is_never_donor(ipc_importance_task_t task_imp
)
1715 if (IIT_NULL
== task_imp
) {
1718 return !ipc_importance_task_is_marked_donor(task_imp
) &&
1719 !ipc_importance_task_is_marked_live_donor(task_imp
) &&
1720 !ipc_importance_task_is_marked_receiver(task_imp
);
1724 * Routine: ipc_importance_task_mark_receiver
1726 * Update the task importance receiver flag.
1728 * Nothing locked on entrance, nothing locked on exit.
1729 * This can only be invoked before the task is discoverable,
1730 * so no worries about atomicity(?)
1733 ipc_importance_task_mark_receiver(ipc_importance_task_t task_imp
, boolean_t receiving
)
1735 assert(task_imp
!= NULL
);
1737 ipc_importance_lock();
1739 assert(task_imp
->iit_assertcnt
== 0);
1740 assert(task_imp
->iit_externcnt
== 0);
1741 assert(task_imp
->iit_externdrop
== 0);
1742 assert(task_imp
->iit_denap
== 0);
1743 task_imp
->iit_receiver
= 1; /* task can receive importance boost */
1744 } else if (task_imp
->iit_receiver
) {
1745 assert(task_imp
->iit_denap
== 0);
1746 if (task_imp
->iit_assertcnt
!= 0 || IIT_EXTERN(task_imp
) != 0) {
1747 panic("disabling imp_receiver on task with pending importance boosts!");
1749 task_imp
->iit_receiver
= 0;
1751 ipc_importance_unlock();
1756 * Routine: ipc_importance_task_marked_receiver
1758 * Query the receiver flag for the given task importance.
1760 * May be called without taking the importance lock as
1761 * the importance flag can never change after task init.
1764 ipc_importance_task_is_marked_receiver(ipc_importance_task_t task_imp
)
1766 return IIT_NULL
!= task_imp
&& 0 != task_imp
->iit_receiver
;
1771 * Routine: ipc_importance_task_mark_denap_receiver
1773 * Update the task importance de-nap receiver flag.
1775 * Nothing locked on entrance, nothing locked on exit.
1776 * This can only be invoked before the task is discoverable,
1777 * so no worries about atomicity(?)
1780 ipc_importance_task_mark_denap_receiver(ipc_importance_task_t task_imp
, boolean_t denap
)
1782 assert(task_imp
!= NULL
);
1784 ipc_importance_lock();
1786 assert(task_imp
->iit_assertcnt
== 0);
1787 assert(task_imp
->iit_externcnt
== 0);
1788 assert(task_imp
->iit_receiver
== 0);
1789 task_imp
->iit_denap
= 1; /* task can receive de-nap boost */
1790 } else if (task_imp
->iit_denap
) {
1791 assert(task_imp
->iit_receiver
== 0);
1792 if (0 < task_imp
->iit_assertcnt
|| 0 < IIT_EXTERN(task_imp
)) {
1793 panic("disabling de-nap on task with pending de-nap boosts!");
1795 task_imp
->iit_denap
= 0;
1797 ipc_importance_unlock();
1802 * Routine: ipc_importance_task_marked_denap_receiver
1804 * Query the de-nap receiver flag for the given task importance.
1806 * May be called without taking the importance lock as
1807 * the de-nap flag can never change after task init.
1810 ipc_importance_task_is_marked_denap_receiver(ipc_importance_task_t task_imp
)
1812 return IIT_NULL
!= task_imp
&& 0 != task_imp
->iit_denap
;
1816 * Routine: ipc_importance_task_is_denap_receiver
1818 * Query the full de-nap receiver status for the given task importance.
1819 * For now, that is simply whether the receiver flag is set.
1821 * May be called without taking the importance lock as
1822 * the de-nap receiver flag can never change after task init.
1825 ipc_importance_task_is_denap_receiver(ipc_importance_task_t task_imp
)
1827 return ipc_importance_task_is_marked_denap_receiver(task_imp
);
1831 * Routine: ipc_importance_task_is_any_receiver_type
1833 * Query if the task is marked to receive boosts - either
1834 * importance or denap.
1836 * May be called without taking the importance lock as both
1837 * the importance and de-nap receiver flags can never change
1841 ipc_importance_task_is_any_receiver_type(ipc_importance_task_t task_imp
)
1843 return ipc_importance_task_is_marked_receiver(task_imp
) ||
1844 ipc_importance_task_is_marked_denap_receiver(task_imp
);
1847 #if 0 /* currently unused */
1850 * Routine: ipc_importance_inherit_reference
1852 * Add a reference to the inherit importance element.
1854 * Caller most hold a reference on the inherit element.
1857 ipc_importance_inherit_reference(ipc_importance_inherit_t inherit
)
1859 ipc_importance_reference(&inherit
->iii_elem
);
1861 #endif /* currently unused */
1864 * Routine: ipc_importance_inherit_release_locked
1866 * Release a reference on an inherit importance attribute value,
1867 * unlinking and deallocating the attribute if the last reference.
1869 * Entered with importance lock held, leaves with it unlocked.
1872 ipc_importance_inherit_release_locked(ipc_importance_inherit_t inherit
)
1874 ipc_importance_release_locked(&inherit
->iii_elem
);
1877 #if 0 /* currently unused */
1879 * Routine: ipc_importance_inherit_release
1881 * Release a reference on an inherit importance attribute value,
1882 * unlinking and deallocating the attribute if the last reference.
1884 * nothing locked on entrance, nothing locked on exit.
1888 ipc_importance_inherit_release(ipc_importance_inherit_t inherit
)
1890 if (III_NULL
!= inherit
) {
1891 ipc_importance_release(&inherit
->iii_elem
);
1894 #endif /* 0 currently unused */
1897 * Routine: ipc_importance_for_task
1899 * Create a reference for the specified task's base importance
1900 * element. If the base importance element doesn't exist, make it and
1901 * bind it to the active task. If the task is inactive, there isn't
1902 * any need to return a new reference.
1904 * If made is true, a "made" reference is returned (for donating to
1905 * the voucher system). Otherwise an internal reference is returned.
1907 * Nothing locked on entry. May block.
1909 ipc_importance_task_t
1910 ipc_importance_for_task(task_t task
, boolean_t made
)
1912 ipc_importance_task_t task_elem
;
1913 boolean_t first_pass
= TRUE
;
1915 assert(TASK_NULL
!= task
);
1918 /* No use returning anything for inactive task */
1919 if (!task
->active
) {
1923 ipc_importance_lock();
1924 task_elem
= task
->task_imp_base
;
1925 if (IIT_NULL
!= task_elem
) {
1926 /* Add a made reference (borrowing active task ref to do it) */
1928 if (0 == task_elem
->iit_made
++) {
1929 assert(IIT_REFS_MAX
> IIT_REFS(task_elem
));
1930 ipc_importance_task_reference_internal(task_elem
);
1933 assert(IIT_REFS_MAX
> IIT_REFS(task_elem
));
1934 ipc_importance_task_reference_internal(task_elem
);
1936 ipc_importance_unlock();
1939 ipc_importance_unlock();
1946 /* Need to make one - may race with others (be prepared to drop) */
1947 task_elem
= (ipc_importance_task_t
)zalloc(ipc_importance_task_zone
);
1948 if (IIT_NULL
== task_elem
) {
1952 task_elem
->iit_bits
= IIE_TYPE_TASK
| 2; /* one for task, one for return/made */
1953 task_elem
->iit_made
= (made
) ? 1 : 0;
1954 task_elem
->iit_task
= task
; /* take actual ref when we're sure */
1955 task_elem
->iit_updateq
= NULL
;
1956 task_elem
->iit_receiver
= 0;
1957 task_elem
->iit_denap
= 0;
1958 task_elem
->iit_donor
= 0;
1959 task_elem
->iit_live_donor
= 0;
1960 task_elem
->iit_updatepolicy
= 0;
1961 task_elem
->iit_reserved
= 0;
1962 task_elem
->iit_filelocks
= 0;
1963 task_elem
->iit_updatetime
= 0;
1964 task_elem
->iit_transitions
= 0;
1965 task_elem
->iit_assertcnt
= 0;
1966 task_elem
->iit_externcnt
= 0;
1967 task_elem
->iit_externdrop
= 0;
1968 task_elem
->iit_legacy_externcnt
= 0;
1969 task_elem
->iit_legacy_externdrop
= 0;
1971 ipc_importance_counter_init(&task_elem
->iit_elem
);
1973 queue_init(&task_elem
->iit_kmsgs
);
1974 queue_init(&task_elem
->iit_inherits
);
1976 ipc_importance_lock();
1977 if (!task
->active
) {
1978 ipc_importance_unlock();
1979 zfree(ipc_importance_task_zone
, task_elem
);
1983 /* did we lose the race? */
1984 if (IIT_NULL
!= task
->task_imp_base
) {
1985 ipc_importance_unlock();
1986 zfree(ipc_importance_task_zone
, task_elem
);
1990 /* we won the race */
1991 task
->task_imp_base
= task_elem
;
1992 task_reference(task
);
1993 #if DEVELOPMENT || DEBUG
1994 queue_enter(&global_iit_alloc_queue
, task_elem
, ipc_importance_task_t
, iit_allocation
);
1995 task_importance_update_owner_info(task
);
1997 ipc_importance_unlock();
2002 #if DEVELOPMENT || DEBUG
2004 task_importance_update_owner_info(task_t task
)
2006 if (task
!= TASK_NULL
&& task
->task_imp_base
!= IIT_NULL
) {
2007 ipc_importance_task_t task_elem
= task
->task_imp_base
;
2009 task_elem
->iit_bsd_pid
= task_pid(task
);
2010 if (task
->bsd_info
) {
2011 strncpy(&task_elem
->iit_procname
[0], proc_name_address(task
->bsd_info
), 16);
2012 task_elem
->iit_procname
[16] = '\0';
2014 strncpy(&task_elem
->iit_procname
[0], "unknown", 16);
2021 * Routine: ipc_importance_reset_locked
2023 * Reset a task's IPC importance (the task is going away or exec'ing)
2025 * Remove the donor bit and legacy externalized assertions from the
2026 * current task importance and see if that wipes out downstream donations.
2028 * importance lock held.
2032 ipc_importance_reset_locked(ipc_importance_task_t task_imp
, boolean_t donor
)
2034 boolean_t before_donor
, after_donor
;
2036 /* remove the donor bit, live-donor bit and externalized boosts */
2037 before_donor
= ipc_importance_task_is_donor(task_imp
);
2039 task_imp
->iit_donor
= 0;
2041 assert(IIT_LEGACY_EXTERN(task_imp
) <= IIT_EXTERN(task_imp
));
2042 assert(task_imp
->iit_legacy_externcnt
<= task_imp
->iit_externcnt
);
2043 assert(task_imp
->iit_legacy_externdrop
<= task_imp
->iit_externdrop
);
2044 task_imp
->iit_externcnt
-= task_imp
->iit_legacy_externcnt
;
2045 task_imp
->iit_externdrop
-= task_imp
->iit_legacy_externdrop
;
2047 /* assert(IIT_LEGACY_EXTERN(task_imp) <= task_imp->iit_assertcnt); */
2048 if (IIT_EXTERN(task_imp
) < task_imp
->iit_assertcnt
) {
2049 task_imp
->iit_assertcnt
-= IIT_LEGACY_EXTERN(task_imp
);
2051 task_imp
->iit_assertcnt
= IIT_EXTERN(task_imp
);
2053 task_imp
->iit_legacy_externcnt
= 0;
2054 task_imp
->iit_legacy_externdrop
= 0;
2055 after_donor
= ipc_importance_task_is_donor(task_imp
);
2057 #if DEVELOPMENT || DEBUG
2058 if (task_imp
->iit_assertcnt
> 0 && task_imp
->iit_live_donor
) {
2059 printf("Live donor task %s[%d] still has %d importance assertions after reset\n",
2060 task_imp
->iit_procname
, task_imp
->iit_bsd_pid
, task_imp
->iit_assertcnt
);
2064 /* propagate a downstream drop if there was a change in donor status */
2065 if (after_donor
!= before_donor
) {
2066 ipc_importance_task_propagate_assertion_locked(task_imp
, IIT_UPDATE_DROP
, FALSE
);
2071 * Routine: ipc_importance_reset
2073 * Reset a task's IPC importance
2075 * The task is being reset, although staying around. Arrange to have the
2076 * external state of the task reset from the importance.
2078 * importance lock not held.
2082 ipc_importance_reset(ipc_importance_task_t task_imp
, boolean_t donor
)
2084 if (IIT_NULL
== task_imp
) {
2087 ipc_importance_lock();
2088 ipc_importance_reset_locked(task_imp
, donor
);
2089 ipc_importance_unlock();
2093 * Routine: ipc_importance_disconnect_task
2095 * Disconnect a task from its importance.
2097 * Clear the task pointer from the importance and drop the
2098 * reference the task held on the importance object. Before
2099 * doing that, reset the effects the current task holds on
2100 * the importance and see if that wipes out downstream donations.
2102 * We allow the upstream boosts to continue to affect downstream
2103 * even though the local task is being effectively pulled from
2109 ipc_importance_disconnect_task(task_t task
)
2111 ipc_importance_task_t task_imp
;
2114 ipc_importance_lock();
2115 task_imp
= task
->task_imp_base
;
2117 /* did somebody beat us to it? */
2118 if (IIT_NULL
== task_imp
) {
2119 ipc_importance_unlock();
2124 /* disconnect the task from this importance */
2125 assert(task_imp
->iit_task
== task
);
2126 task_imp
->iit_task
= TASK_NULL
;
2127 task
->task_imp_base
= IIT_NULL
;
2130 /* reset the effects the current task hold on the importance */
2131 ipc_importance_reset_locked(task_imp
, TRUE
);
2133 ipc_importance_task_release_locked(task_imp
);
2134 /* importance unlocked */
2136 /* deallocate the task now that the importance is unlocked */
2137 task_deallocate(task
);
2141 * Routine: ipc_importance_exec_switch_task
2143 * Switch importance task base from old task to new task in exec.
2145 * Create an ipc importance linkage from old task to new task,
2146 * once the linkage is created, switch the importance task base
2147 * from old task to new task. After the switch, the linkage will
2148 * represent importance linkage from new task to old task with
2149 * watch port importance inheritance linked to new task.
2152 * Returns a reference on importance inherit.
2154 ipc_importance_inherit_t
2155 ipc_importance_exec_switch_task(
2159 ipc_importance_inherit_t inherit
= III_NULL
;
2160 ipc_importance_task_t old_task_imp
= IIT_NULL
;
2161 ipc_importance_task_t new_task_imp
= IIT_NULL
;
2163 task_importance_reset(old_task
);
2165 /* Create an importance linkage from old_task to new_task */
2166 inherit
= ipc_importance_inherit_from_task(old_task
, new_task
);
2168 /* Switch task importance base from old task to new task */
2169 ipc_importance_lock();
2171 old_task_imp
= old_task
->task_imp_base
;
2172 new_task_imp
= new_task
->task_imp_base
;
2174 old_task_imp
->iit_task
= new_task
;
2175 new_task_imp
->iit_task
= old_task
;
2177 old_task
->task_imp_base
= new_task_imp
;
2178 new_task
->task_imp_base
= old_task_imp
;
2180 #if DEVELOPMENT || DEBUG
2182 * Update the pid an proc name for importance base if any
2184 task_importance_update_owner_info(new_task
);
2186 ipc_importance_unlock();
2192 * Routine: ipc_importance_check_circularity
2194 * Check if queueing "port" in a message for "dest"
2195 * would create a circular group of ports and messages.
2197 * If no circularity (FALSE returned), then "port"
2198 * is changed from "in limbo" to "in transit".
2200 * That is, we want to set port->ip_destination == dest,
2201 * but guaranteeing that this doesn't create a circle
2202 * port->ip_destination->ip_destination->... == port
2204 * Additionally, if port was successfully changed to "in transit",
2205 * propagate boost assertions from the "in limbo" port to all
2206 * the ports in the chain, and, if the destination task accepts
2207 * boosts, to the destination task.
2210 * No ports locked. References held for "port" and "dest".
2214 ipc_importance_check_circularity(
2218 ipc_importance_task_t imp_task
= IIT_NULL
;
2219 ipc_importance_task_t release_imp_task
= IIT_NULL
;
2220 boolean_t imp_lock_held
= FALSE
;
2223 struct turnstile
*send_turnstile
= TURNSTILE_NULL
;
2225 assert(port
!= IP_NULL
);
2226 assert(dest
!= IP_NULL
);
2233 /* Check if destination needs a turnstile */
2234 ipc_port_send_turnstile_prepare(dest
);
2236 /* port is in limbo, so donation status is safe to latch */
2237 if (port
->ip_impdonation
!= 0) {
2238 imp_lock_held
= TRUE
;
2239 ipc_importance_lock();
2243 * First try a quick check that can run in parallel.
2244 * No circularity if dest is not in transit.
2249 * Even if port is just carrying assertions for others,
2250 * we need the importance lock.
2252 if (port
->ip_impcount
> 0 && !imp_lock_held
) {
2253 if (!ipc_importance_lock_try()) {
2255 ipc_importance_lock();
2258 imp_lock_held
= TRUE
;
2261 if (ip_lock_try(dest
)) {
2262 if (!ip_active(dest
) ||
2263 (dest
->ip_receiver_name
!= MACH_PORT_NULL
) ||
2264 (dest
->ip_destination
== IP_NULL
)) {
2268 /* dest is in transit; further checking necessary */
2275 * We're about to pay the cost to serialize,
2276 * just go ahead and grab importance lock.
2278 if (!imp_lock_held
) {
2279 ipc_importance_lock();
2280 imp_lock_held
= TRUE
;
2283 ipc_port_multiple_lock(); /* massive serialization */
2286 * Search for the end of the chain (a port not in transit),
2287 * acquiring locks along the way.
2293 if (!ip_active(base
) ||
2294 (base
->ip_receiver_name
!= MACH_PORT_NULL
) ||
2295 (base
->ip_destination
== IP_NULL
)) {
2299 base
= base
->ip_destination
;
2302 /* all ports in chain from dest to base, inclusive, are locked */
2305 /* circularity detected! */
2307 ipc_port_multiple_unlock();
2309 /* port (== base) is in limbo */
2311 assert(ip_active(port
));
2312 assert(port
->ip_receiver_name
== MACH_PORT_NULL
);
2313 assert(port
->ip_destination
== IP_NULL
);
2316 while (base
!= IP_NULL
) {
2319 /* base is in transit or in limbo */
2321 assert(ip_active(base
));
2322 assert(base
->ip_receiver_name
== MACH_PORT_NULL
);
2324 next
= base
->ip_destination
;
2329 if (imp_lock_held
) {
2330 ipc_importance_unlock();
2333 ipc_port_send_turnstile_complete(dest
);
2338 * The guarantee: lock port while the entire chain is locked.
2339 * Once port is locked, we can take a reference to dest,
2340 * add port to the chain, and unlock everything.
2344 ipc_port_multiple_unlock();
2347 /* port is in limbo */
2348 imq_lock(&port
->ip_messages
);
2350 assert(ip_active(port
));
2351 assert(port
->ip_receiver_name
== MACH_PORT_NULL
);
2352 assert(port
->ip_destination
== IP_NULL
);
2355 port
->ip_destination
= dest
;
2357 /* must have been in limbo or still bound to a task */
2358 assert(port
->ip_tempowner
!= 0);
2361 * We delayed dropping assertions from a specific task.
2362 * Cache that info now (we'll drop assertions and the
2363 * task reference below).
2365 release_imp_task
= port
->ip_imp_task
;
2366 if (IIT_NULL
!= release_imp_task
) {
2367 port
->ip_imp_task
= IIT_NULL
;
2369 assertcnt
= port
->ip_impcount
;
2371 /* take the port out of limbo w.r.t. assertions */
2372 port
->ip_tempowner
= 0;
2375 * Setup linkage for source port if it has a send turnstile i.e. it has
2376 * a thread waiting in send or has a port enqueued in it or has sync ipc
2377 * push from a special reply port.
2379 if (port_send_turnstile(port
)) {
2380 send_turnstile
= turnstile_prepare((uintptr_t)port
,
2381 port_send_turnstile_address(port
),
2382 TURNSTILE_NULL
, TURNSTILE_SYNC_IPC
);
2384 turnstile_update_inheritor(send_turnstile
, port_send_turnstile(dest
),
2385 (TURNSTILE_INHERITOR_TURNSTILE
| TURNSTILE_IMMEDIATE_UPDATE
));
2387 /* update complete and turnstile complete called after dropping all locks */
2389 imq_unlock(&port
->ip_messages
);
2391 /* now unlock chain */
2397 /* every port along chain track assertions behind it */
2398 ipc_port_impcount_delta(dest
, assertcnt
, base
);
2404 /* port is in transit */
2406 assert(ip_active(dest
));
2407 assert(dest
->ip_receiver_name
== MACH_PORT_NULL
);
2408 assert(dest
->ip_destination
!= IP_NULL
);
2409 assert(dest
->ip_tempowner
== 0);
2411 next
= dest
->ip_destination
;
2416 /* base is not in transit */
2417 assert(!ip_active(base
) ||
2418 (base
->ip_receiver_name
!= MACH_PORT_NULL
) ||
2419 (base
->ip_destination
== IP_NULL
));
2422 * Find the task to boost (if any).
2423 * We will boost "through" ports that don't know
2424 * about inheritance to deliver receive rights that
2427 if (ip_active(base
) && (assertcnt
> 0)) {
2428 assert(imp_lock_held
);
2429 if (base
->ip_tempowner
!= 0) {
2430 if (IIT_NULL
!= base
->ip_imp_task
) {
2431 /* specified tempowner task */
2432 imp_task
= base
->ip_imp_task
;
2433 assert(ipc_importance_task_is_any_receiver_type(imp_task
));
2435 /* otherwise don't boost current task */
2436 } else if (base
->ip_receiver_name
!= MACH_PORT_NULL
) {
2437 ipc_space_t space
= base
->ip_receiver
;
2439 /* only spaces with boost-accepting tasks */
2440 if (space
->is_task
!= TASK_NULL
&&
2441 ipc_importance_task_is_any_receiver_type(space
->is_task
->task_imp_base
)) {
2442 imp_task
= space
->is_task
->task_imp_base
;
2446 /* take reference before unlocking base */
2447 if (imp_task
!= IIT_NULL
) {
2448 ipc_importance_task_reference(imp_task
);
2455 * Transfer assertions now that the ports are unlocked.
2456 * Avoid extra overhead if transferring to/from the same task.
2458 * NOTE: If a transfer is occurring, the new assertions will
2459 * be added to imp_task BEFORE the importance lock is unlocked.
2460 * This is critical - to avoid decrements coming from the kmsgs
2461 * beating the increment to the task.
2463 boolean_t transfer_assertions
= (imp_task
!= release_imp_task
);
2465 if (imp_task
!= IIT_NULL
) {
2466 assert(imp_lock_held
);
2467 if (transfer_assertions
) {
2468 ipc_importance_task_hold_internal_assertion_locked(imp_task
, assertcnt
);
2472 if (release_imp_task
!= IIT_NULL
) {
2473 assert(imp_lock_held
);
2474 if (transfer_assertions
) {
2475 ipc_importance_task_drop_internal_assertion_locked(release_imp_task
, assertcnt
);
2479 if (imp_lock_held
) {
2480 ipc_importance_unlock();
2483 /* All locks dropped, call turnstile_update_inheritor_complete for source port's turnstile */
2484 if (send_turnstile
) {
2485 turnstile_update_inheritor_complete(send_turnstile
, TURNSTILE_INTERLOCK_NOT_HELD
);
2487 /* Take the mq lock to call turnstile complete */
2488 imq_lock(&port
->ip_messages
);
2489 turnstile_complete((uintptr_t)port
, port_send_turnstile_address(port
), NULL
);
2490 send_turnstile
= TURNSTILE_NULL
;
2491 imq_unlock(&port
->ip_messages
);
2492 turnstile_cleanup();
2495 if (imp_task
!= IIT_NULL
) {
2496 ipc_importance_task_release(imp_task
);
2499 if (release_imp_task
!= IIT_NULL
) {
2500 ipc_importance_task_release(release_imp_task
);
2507 * Routine: ipc_importance_send
2509 * Post the importance voucher attribute [if sent] or a static
2510 * importance boost depending upon options and conditions.
2512 * Destination port locked on entry and exit, may be dropped during the call.
2514 * A boolean identifying if the port lock was tempoarily dropped.
2517 ipc_importance_send(
2519 mach_msg_option_t option
)
2521 ipc_port_t port
= (ipc_port_t
) kmsg
->ikm_header
->msgh_remote_port
;
2522 boolean_t port_lock_dropped
= FALSE
;
2523 ipc_importance_elem_t elem
;
2525 ipc_importance_task_t task_imp
;
2528 assert(IP_VALID(port
));
2530 /* If no donation to be made, return quickly */
2531 if ((port
->ip_impdonation
== 0) ||
2532 (option
& MACH_SEND_NOIMPORTANCE
) != 0) {
2533 return port_lock_dropped
;
2536 task
= current_task();
2538 /* If forced sending a static boost, go update the port */
2539 if ((option
& MACH_SEND_IMPORTANCE
) != 0) {
2540 /* acquire the importance lock while trying to hang on to port lock */
2541 if (!ipc_importance_lock_try()) {
2542 port_lock_dropped
= TRUE
;
2544 ipc_importance_lock();
2549 task_imp
= task
->task_imp_base
;
2550 assert(IIT_NULL
!= task_imp
);
2552 /* If the sender can never donate importance, nothing to do */
2553 if (ipc_importance_task_is_never_donor(task_imp
)) {
2554 return port_lock_dropped
;
2559 /* If importance receiver and passing a voucher, look for importance in there */
2560 if (IP_VALID(kmsg
->ikm_voucher
) &&
2561 ipc_importance_task_is_marked_receiver(task_imp
)) {
2562 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2563 mach_voucher_attr_value_handle_array_size_t val_count
;
2564 ipc_voucher_t voucher
;
2566 assert(ip_kotype(kmsg
->ikm_voucher
) == IKOT_VOUCHER
);
2567 voucher
= (ipc_voucher_t
)kmsg
->ikm_voucher
->ip_kobject
;
2569 /* check to see if the voucher has an importance attribute */
2570 val_count
= MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
;
2571 kr
= mach_voucher_attr_control_get_values(ipc_importance_control
, voucher
,
2573 assert(KERN_SUCCESS
== kr
);
2576 * Only use importance associated with our task (either directly
2577 * or through an inherit that donates to our task).
2579 if (0 < val_count
) {
2580 ipc_importance_elem_t check_elem
;
2582 check_elem
= (ipc_importance_elem_t
)vals
[0];
2583 assert(IIE_NULL
!= check_elem
);
2584 if (IIE_TYPE_INHERIT
== IIE_TYPE(check_elem
)) {
2585 ipc_importance_inherit_t inherit
;
2586 inherit
= (ipc_importance_inherit_t
) check_elem
;
2587 if (inherit
->iii_to_task
== task_imp
) {
2590 } else if (check_elem
== (ipc_importance_elem_t
)task_imp
) {
2596 /* If we haven't found an importance attribute to send yet, use the task's */
2597 if (IIE_NULL
== elem
) {
2598 elem
= (ipc_importance_elem_t
)task_imp
;
2601 /* take a reference for the message to hold */
2602 ipc_importance_reference_internal(elem
);
2604 /* acquire the importance lock while trying to hang on to port lock */
2605 if (!ipc_importance_lock_try()) {
2606 port_lock_dropped
= TRUE
;
2608 ipc_importance_lock();
2611 /* link kmsg onto the donor element propagation chain */
2612 ipc_importance_kmsg_link(kmsg
, elem
);
2613 /* elem reference transfered to kmsg */
2615 incr_ref_counter(elem
->iie_kmsg_refs_added
);
2617 /* If the sender isn't currently a donor, no need to apply boost */
2618 if (!ipc_importance_task_is_donor(task_imp
)) {
2619 ipc_importance_unlock();
2621 /* re-acquire port lock, if needed */
2622 if (TRUE
== port_lock_dropped
) {
2626 return port_lock_dropped
;
2630 /* Mark the fact that we are (currently) donating through this message */
2631 kmsg
->ikm_header
->msgh_bits
|= MACH_MSGH_BITS_RAISEIMP
;
2634 * If we need to relock the port, do it with the importance still locked.
2635 * This assures we get to add the importance boost through the port to
2636 * the task BEFORE anyone else can attempt to undo that operation if
2637 * the sender lost donor status.
2639 if (TRUE
== port_lock_dropped
) {
2643 ipc_importance_assert_held();
2645 #if IMPORTANCE_TRACE
2646 if (kdebug_enable
) {
2647 mach_msg_max_trailer_t
*dbgtrailer
= (mach_msg_max_trailer_t
*)
2648 ((vm_offset_t
)kmsg
->ikm_header
+ round_msg(kmsg
->ikm_header
->msgh_size
));
2649 unsigned int sender_pid
= dbgtrailer
->msgh_audit
.val
[5];
2650 mach_msg_id_t imp_msgh_id
= kmsg
->ikm_header
->msgh_id
;
2651 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_MSG
, IMP_MSG_SEND
)) | DBG_FUNC_START
,
2652 task_pid(task
), sender_pid
, imp_msgh_id
, 0, 0);
2654 #endif /* IMPORTANCE_TRACE */
2656 mach_port_delta_t delta
= 1;
2657 boolean_t need_port_lock
;
2658 task_imp
= IIT_NULL
;
2660 /* adjust port boost count (with importance and port locked) */
2661 need_port_lock
= ipc_port_importance_delta_internal(port
, IPID_OPTION_NORMAL
, &delta
, &task_imp
);
2662 /* hold a reference on task_imp */
2664 /* if we need to adjust a task importance as a result, apply that here */
2665 if (IIT_NULL
!= task_imp
&& delta
!= 0) {
2668 /* if this results in a change of state, propagate the transistion */
2669 if (ipc_importance_task_check_transition(task_imp
, IIT_UPDATE_HOLD
, delta
)) {
2670 /* can't hold the port lock during task transition(s) */
2671 if (!need_port_lock
) {
2672 need_port_lock
= TRUE
;
2675 ipc_importance_task_propagate_assertion_locked(task_imp
, IIT_UPDATE_HOLD
, TRUE
);
2680 ipc_importance_task_release_locked(task_imp
);
2681 /* importance unlocked */
2683 ipc_importance_unlock();
2686 if (need_port_lock
) {
2687 port_lock_dropped
= TRUE
;
2691 return port_lock_dropped
;
2695 * Routine: ipc_importance_inherit_from_kmsg
2697 * Create a "made" reference for an importance attribute representing
2698 * an inheritance between the sender of a message (if linked) and the
2699 * current task importance. If the message is not linked, a static
2700 * boost may be created, based on the boost state of the message.
2702 * Any transfer from kmsg linkage to inherit linkage must be atomic.
2704 * If the task is inactive, there isn't any need to return a new reference.
2706 * Nothing locked on entry. May block.
2708 static ipc_importance_inherit_t
2709 ipc_importance_inherit_from_kmsg(ipc_kmsg_t kmsg
)
2711 ipc_importance_task_t task_imp
= IIT_NULL
;
2712 ipc_importance_elem_t from_elem
= kmsg
->ikm_importance
;
2713 ipc_importance_elem_t elem
;
2714 task_t task_self
= current_task();
2716 ipc_port_t port
= kmsg
->ikm_header
->msgh_remote_port
;
2717 ipc_importance_inherit_t inherit
= III_NULL
;
2718 ipc_importance_inherit_t alloc
= III_NULL
;
2719 boolean_t cleared_self_donation
= FALSE
;
2723 /* The kmsg must have an importance donor or static boost to proceed */
2724 if (IIE_NULL
== kmsg
->ikm_importance
&&
2725 !MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
)) {
2730 * No need to set up an inherit linkage if the dest isn't a receiver
2731 * of one type or the other.
2733 if (!ipc_importance_task_is_any_receiver_type(task_self
->task_imp_base
)) {
2734 ipc_importance_lock();
2738 /* Grab a reference on the importance of the destination */
2739 task_imp
= ipc_importance_for_task(task_self
, FALSE
);
2741 ipc_importance_lock();
2743 if (IIT_NULL
== task_imp
) {
2747 incr_ref_counter(task_imp
->iit_elem
.iie_task_refs_added_inherit_from
);
2749 /* If message is already associated with an inherit... */
2750 if (IIE_TYPE_INHERIT
== IIE_TYPE(from_elem
)) {
2751 ipc_importance_inherit_t from_inherit
= (ipc_importance_inherit_t
)from_elem
;
2753 /* already targeting our task? - just use it */
2754 if (from_inherit
->iii_to_task
== task_imp
) {
2755 /* clear self-donation if not also present in inherit */
2756 if (!from_inherit
->iii_donating
&&
2757 MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
)) {
2758 kmsg
->ikm_header
->msgh_bits
&= ~MACH_MSGH_BITS_RAISEIMP
;
2759 cleared_self_donation
= TRUE
;
2761 inherit
= from_inherit
;
2762 } else if (III_DEPTH_MAX
== III_DEPTH(from_inherit
)) {
2763 ipc_importance_task_t to_task
;
2764 ipc_importance_elem_t unlinked_from
;
2767 * Chain too long. Switch to looking
2768 * directly at the from_inherit's to-task
2769 * as our source of importance.
2771 to_task
= from_inherit
->iii_to_task
;
2772 ipc_importance_task_reference(to_task
);
2773 from_elem
= (ipc_importance_elem_t
)to_task
;
2774 depth
= III_DEPTH_RESET
| 1;
2776 /* Fixup the kmsg linkage to reflect change */
2777 unlinked_from
= ipc_importance_kmsg_unlink(kmsg
);
2778 assert(unlinked_from
== (ipc_importance_elem_t
)from_inherit
);
2779 ipc_importance_kmsg_link(kmsg
, from_elem
);
2780 ipc_importance_inherit_release_locked(from_inherit
);
2781 /* importance unlocked */
2782 ipc_importance_lock();
2784 /* inheriting from an inherit */
2785 depth
= from_inherit
->iii_depth
+ 1;
2790 * Don't allow a task to inherit from itself (would keep it permanently
2791 * boosted even if all other donors to the task went away).
2794 if (from_elem
== (ipc_importance_elem_t
)task_imp
) {
2799 * But if the message isn't associated with any linked source, it is
2800 * intended to be permanently boosting (static boost from kernel).
2801 * In that case DO let the process permanently boost itself.
2803 if (IIE_NULL
== from_elem
) {
2804 assert(MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
));
2805 ipc_importance_task_reference_internal(task_imp
);
2806 from_elem
= (ipc_importance_elem_t
)task_imp
;
2810 * Now that we have the from_elem figured out,
2811 * check to see if we already have an inherit for this pairing
2813 while (III_NULL
== inherit
) {
2814 inherit
= ipc_importance_inherit_find(from_elem
, task_imp
, depth
);
2816 /* Do we have to allocate a new inherit */
2817 if (III_NULL
== inherit
) {
2818 if (III_NULL
!= alloc
) {
2822 /* allocate space */
2823 ipc_importance_unlock();
2824 alloc
= (ipc_importance_inherit_t
)
2825 zalloc(ipc_importance_inherit_zone
);
2826 ipc_importance_lock();
2830 /* snapshot the donating status while we have importance locked */
2831 donating
= MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
);
2833 if (III_NULL
!= inherit
) {
2834 /* We found one, piggyback on that */
2835 assert(0 < III_REFS(inherit
));
2836 assert(0 < IIE_REFS(inherit
->iii_from_elem
));
2837 assert(inherit
->iii_externcnt
>= inherit
->iii_made
);
2839 /* add in a made reference */
2840 if (0 == inherit
->iii_made
++) {
2841 assert(III_REFS_MAX
> III_REFS(inherit
));
2842 ipc_importance_inherit_reference_internal(inherit
);
2845 /* Reflect the inherit's change of status into the task boosts */
2846 if (0 == III_EXTERN(inherit
)) {
2847 assert(!inherit
->iii_donating
);
2848 inherit
->iii_donating
= donating
;
2850 task_imp
->iit_externcnt
+= inherit
->iii_externcnt
;
2851 task_imp
->iit_externdrop
+= inherit
->iii_externdrop
;
2854 assert(donating
== inherit
->iii_donating
);
2857 /* add in a external reference for this use of the inherit */
2858 inherit
->iii_externcnt
++;
2860 /* initialize the previously allocated space */
2862 inherit
->iii_bits
= IIE_TYPE_INHERIT
| 1;
2863 inherit
->iii_made
= 1;
2864 inherit
->iii_externcnt
= 1;
2865 inherit
->iii_externdrop
= 0;
2866 inherit
->iii_depth
= depth
;
2867 inherit
->iii_to_task
= task_imp
;
2868 inherit
->iii_from_elem
= IIE_NULL
;
2869 queue_init(&inherit
->iii_kmsgs
);
2872 inherit
->iii_donating
= TRUE
;
2874 inherit
->iii_donating
= FALSE
;
2878 * Chain our new inherit on the element it inherits from.
2879 * The new inherit takes our reference on from_elem.
2881 ipc_importance_inherit_link(inherit
, from_elem
);
2884 ipc_importance_counter_init(&inherit
->iii_elem
);
2885 from_elem
->iie_kmsg_refs_inherited
++;
2886 task_imp
->iit_elem
.iie_task_refs_inherited
++;
2892 * for those paths that came straight here: snapshot the donating status
2893 * (this should match previous snapshot for other paths).
2895 donating
= MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
);
2897 /* unlink the kmsg inheritance (if any) */
2898 elem
= ipc_importance_kmsg_unlink(kmsg
);
2899 assert(elem
== from_elem
);
2901 /* If found inherit and donating, reflect that in the task externcnt */
2902 if (III_NULL
!= inherit
&& donating
) {
2903 task_imp
->iit_externcnt
++;
2904 /* The owner of receive right might have changed, take the internal assertion */
2905 ipc_importance_task_hold_internal_assertion_locked(task_imp
, 1);
2906 /* may have dropped and retaken importance lock */
2909 /* If we didn't create a new inherit, we have some resources to release */
2910 if (III_NULL
== inherit
|| inherit
!= alloc
) {
2911 if (IIE_NULL
!= from_elem
) {
2912 if (III_NULL
!= inherit
) {
2913 incr_ref_counter(from_elem
->iie_kmsg_refs_coalesced
);
2915 incr_ref_counter(from_elem
->iie_kmsg_refs_dropped
);
2917 ipc_importance_release_locked(from_elem
);
2918 /* importance unlocked */
2920 ipc_importance_unlock();
2923 if (IIT_NULL
!= task_imp
) {
2924 if (III_NULL
!= inherit
) {
2925 incr_ref_counter(task_imp
->iit_elem
.iie_task_refs_coalesced
);
2927 ipc_importance_task_release(task_imp
);
2930 if (III_NULL
!= alloc
) {
2931 zfree(ipc_importance_inherit_zone
, alloc
);
2934 /* from_elem and task_imp references transferred to new inherit */
2935 ipc_importance_unlock();
2939 * decrement port boost count
2940 * This is OK to do without the importance lock as we atomically
2941 * unlinked the kmsg and snapshot the donating state while holding
2942 * the importance lock
2944 if (donating
|| cleared_self_donation
) {
2946 /* drop importance from port and destination task */
2947 if (ipc_port_importance_delta(port
, IPID_OPTION_NORMAL
, -1) == FALSE
) {
2952 if (III_NULL
!= inherit
) {
2953 /* have an associated importance attr, even if currently not donating */
2954 kmsg
->ikm_header
->msgh_bits
|= MACH_MSGH_BITS_RAISEIMP
;
2956 /* we won't have an importance attribute associated with our message */
2957 kmsg
->ikm_header
->msgh_bits
&= ~MACH_MSGH_BITS_RAISEIMP
;
2964 * Routine: ipc_importance_inherit_from_task
2966 * Create a reference for an importance attribute representing
2967 * an inheritance between the to_task and from_task. The iii
2968 * created will be marked as III_FLAGS_FOR_OTHERS.
2970 * It will not dedup any iii which are not marked as III_FLAGS_FOR_OTHERS.
2972 * If the task is inactive, there isn't any need to return a new reference.
2974 * Nothing locked on entry. May block.
2975 * It should not be called from voucher subsystem.
2977 static ipc_importance_inherit_t
2978 ipc_importance_inherit_from_task(
2982 ipc_importance_task_t to_task_imp
= IIT_NULL
;
2983 ipc_importance_task_t from_task_imp
= IIT_NULL
;
2984 ipc_importance_elem_t from_elem
= IIE_NULL
;
2986 ipc_importance_inherit_t inherit
= III_NULL
;
2987 ipc_importance_inherit_t alloc
= III_NULL
;
2991 to_task_imp
= ipc_importance_for_task(to_task
, FALSE
);
2992 from_task_imp
= ipc_importance_for_task(from_task
, FALSE
);
2993 from_elem
= (ipc_importance_elem_t
)from_task_imp
;
2995 ipc_importance_lock();
2997 if (IIT_NULL
== to_task_imp
|| IIT_NULL
== from_task_imp
) {
3002 * No need to set up an inherit linkage if the to_task or from_task
3003 * isn't a receiver of one type or the other.
3005 if (!ipc_importance_task_is_any_receiver_type(to_task_imp
) ||
3006 !ipc_importance_task_is_any_receiver_type(from_task_imp
)) {
3010 /* Do not allow to create a linkage to self */
3011 if (to_task_imp
== from_task_imp
) {
3015 incr_ref_counter(to_task_imp
->iit_elem
.iie_task_refs_added_inherit_from
);
3016 incr_ref_counter(from_elem
->iie_kmsg_refs_added
);
3019 * Now that we have the from_elem figured out,
3020 * check to see if we already have an inherit for this pairing
3022 while (III_NULL
== inherit
) {
3023 inherit
= ipc_importance_inherit_find(from_elem
, to_task_imp
, depth
);
3025 /* Do we have to allocate a new inherit */
3026 if (III_NULL
== inherit
) {
3027 if (III_NULL
!= alloc
) {
3031 /* allocate space */
3032 ipc_importance_unlock();
3033 alloc
= (ipc_importance_inherit_t
)
3034 zalloc(ipc_importance_inherit_zone
);
3035 ipc_importance_lock();
3039 /* snapshot the donating status while we have importance locked */
3040 donating
= ipc_importance_task_is_donor(from_task_imp
);
3042 if (III_NULL
!= inherit
) {
3043 /* We found one, piggyback on that */
3044 assert(0 < III_REFS(inherit
));
3045 assert(0 < IIE_REFS(inherit
->iii_from_elem
));
3047 /* Take a reference for inherit */
3048 assert(III_REFS_MAX
> III_REFS(inherit
));
3049 ipc_importance_inherit_reference_internal(inherit
);
3051 /* Reflect the inherit's change of status into the task boosts */
3052 if (0 == III_EXTERN(inherit
)) {
3053 assert(!inherit
->iii_donating
);
3054 inherit
->iii_donating
= donating
;
3056 to_task_imp
->iit_externcnt
+= inherit
->iii_externcnt
;
3057 to_task_imp
->iit_externdrop
+= inherit
->iii_externdrop
;
3060 assert(donating
== inherit
->iii_donating
);
3063 /* add in a external reference for this use of the inherit */
3064 inherit
->iii_externcnt
++;
3066 /* initialize the previously allocated space */
3068 inherit
->iii_bits
= IIE_TYPE_INHERIT
| 1;
3069 inherit
->iii_made
= 0;
3070 inherit
->iii_externcnt
= 1;
3071 inherit
->iii_externdrop
= 0;
3072 inherit
->iii_depth
= depth
;
3073 inherit
->iii_to_task
= to_task_imp
;
3074 inherit
->iii_from_elem
= IIE_NULL
;
3075 queue_init(&inherit
->iii_kmsgs
);
3078 inherit
->iii_donating
= TRUE
;
3080 inherit
->iii_donating
= FALSE
;
3084 * Chain our new inherit on the element it inherits from.
3085 * The new inherit takes our reference on from_elem.
3087 ipc_importance_inherit_link(inherit
, from_elem
);
3090 ipc_importance_counter_init(&inherit
->iii_elem
);
3091 from_elem
->iie_kmsg_refs_inherited
++;
3092 task_imp
->iit_elem
.iie_task_refs_inherited
++;
3098 /* If found inherit and donating, reflect that in the task externcnt */
3099 if (III_NULL
!= inherit
&& donating
) {
3100 to_task_imp
->iit_externcnt
++;
3101 /* take the internal assertion */
3102 ipc_importance_task_hold_internal_assertion_locked(to_task_imp
, 1);
3103 /* may have dropped and retaken importance lock */
3106 /* If we didn't create a new inherit, we have some resources to release */
3107 if (III_NULL
== inherit
|| inherit
!= alloc
) {
3108 if (IIE_NULL
!= from_elem
) {
3109 if (III_NULL
!= inherit
) {
3110 incr_ref_counter(from_elem
->iie_kmsg_refs_coalesced
);
3112 incr_ref_counter(from_elem
->iie_kmsg_refs_dropped
);
3114 ipc_importance_release_locked(from_elem
);
3115 /* importance unlocked */
3117 ipc_importance_unlock();
3120 if (IIT_NULL
!= to_task_imp
) {
3121 if (III_NULL
!= inherit
) {
3122 incr_ref_counter(to_task_imp
->iit_elem
.iie_task_refs_coalesced
);
3124 ipc_importance_task_release(to_task_imp
);
3127 if (III_NULL
!= alloc
) {
3128 zfree(ipc_importance_inherit_zone
, alloc
);
3131 /* from_elem and to_task_imp references transferred to new inherit */
3132 ipc_importance_unlock();
3139 * Routine: ipc_importance_receive
3141 * Process importance attributes in a received message.
3143 * If an importance voucher attribute was sent, transform
3144 * that into an attribute value reflecting the inheritance
3145 * from the sender to the receiver.
3147 * If a static boost is received (or the voucher isn't on
3148 * a voucher-based boost), export a static boost.
3153 ipc_importance_receive(
3155 mach_msg_option_t option
)
3157 unsigned int sender_pid
= ((mach_msg_max_trailer_t
*)
3158 ((vm_offset_t
)kmsg
->ikm_header
+
3159 round_msg(kmsg
->ikm_header
->msgh_size
)))->msgh_audit
.val
[5];
3160 task_t task_self
= current_task();
3163 /* convert to a voucher with an inherit importance attribute? */
3164 if ((option
& MACH_RCV_VOUCHER
) != 0) {
3165 uint8_t recipes
[2 * sizeof(ipc_voucher_attr_recipe_data_t
) +
3166 sizeof(mach_voucher_attr_value_handle_t
)];
3167 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= 0;
3168 ipc_voucher_attr_recipe_t recipe
= (ipc_voucher_attr_recipe_t
)recipes
;
3169 ipc_voucher_t recv_voucher
;
3170 mach_voucher_attr_value_handle_t handle
;
3171 ipc_importance_inherit_t inherit
;
3174 /* set up recipe to copy the old voucher */
3175 if (IP_VALID(kmsg
->ikm_voucher
)) {
3176 ipc_voucher_t sent_voucher
= (ipc_voucher_t
)kmsg
->ikm_voucher
->ip_kobject
;
3178 recipe
->key
= MACH_VOUCHER_ATTR_KEY_ALL
;
3179 recipe
->command
= MACH_VOUCHER_ATTR_COPY
;
3180 recipe
->previous_voucher
= sent_voucher
;
3181 recipe
->content_size
= 0;
3182 recipe_size
+= sizeof(*recipe
);
3186 * create an inheritance attribute from the kmsg (may be NULL)
3187 * transferring any boosts from the kmsg linkage through the
3188 * port directly to the new inheritance object.
3190 inherit
= ipc_importance_inherit_from_kmsg(kmsg
);
3191 handle
= (mach_voucher_attr_value_handle_t
)inherit
;
3193 assert(IIE_NULL
== kmsg
->ikm_importance
);
3196 * Only create a new voucher if we have an inherit object
3197 * (from the ikm_importance field of the incoming message), OR
3198 * we have a valid incoming voucher. If we have neither of
3199 * these things then there is no need to create a new voucher.
3201 if (IP_VALID(kmsg
->ikm_voucher
) || inherit
!= III_NULL
) {
3202 /* replace the importance attribute with the handle we created */
3203 /* our made reference on the inherit is donated to the voucher */
3204 recipe
= (ipc_voucher_attr_recipe_t
)&recipes
[recipe_size
];
3205 recipe
->key
= MACH_VOUCHER_ATTR_KEY_IMPORTANCE
;
3206 recipe
->command
= MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
;
3207 recipe
->previous_voucher
= IPC_VOUCHER_NULL
;
3208 recipe
->content_size
= sizeof(mach_voucher_attr_value_handle_t
);
3209 *(mach_voucher_attr_value_handle_t
*)(void *)recipe
->content
= handle
;
3210 recipe_size
+= sizeof(*recipe
) + sizeof(mach_voucher_attr_value_handle_t
);
3212 kr
= ipc_voucher_attr_control_create_mach_voucher(ipc_importance_control
,
3216 assert(KERN_SUCCESS
== kr
);
3218 /* swap the voucher port (and set voucher bits in case it didn't already exist) */
3219 kmsg
->ikm_header
->msgh_bits
|= (MACH_MSG_TYPE_MOVE_SEND
<< 16);
3220 ipc_port_release_send(kmsg
->ikm_voucher
);
3221 kmsg
->ikm_voucher
= convert_voucher_to_port(recv_voucher
);
3222 if (III_NULL
!= inherit
) {
3226 } else { /* Don't want a voucher */
3227 /* got linked importance? have to drop */
3228 if (IIE_NULL
!= kmsg
->ikm_importance
) {
3229 ipc_importance_elem_t elem
;
3231 ipc_importance_lock();
3232 elem
= ipc_importance_kmsg_unlink(kmsg
);
3234 elem
->iie_kmsg_refs_dropped
++;
3236 ipc_importance_release_locked(elem
);
3237 /* importance unlocked */
3240 /* With kmsg unlinked, can safely examine message importance attribute. */
3241 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
)) {
3242 ipc_importance_task_t task_imp
= task_self
->task_imp_base
;
3243 ipc_port_t port
= kmsg
->ikm_header
->msgh_remote_port
;
3245 /* The owner of receive right might have changed, take the internal assertion */
3246 if (KERN_SUCCESS
== ipc_importance_task_hold_internal_assertion(task_imp
, 1)) {
3247 ipc_importance_task_externalize_legacy_assertion(task_imp
, 1, sender_pid
);
3250 /* The importance boost never applied to task (clear the bit) */
3251 kmsg
->ikm_header
->msgh_bits
&= ~MACH_MSGH_BITS_RAISEIMP
;
3255 /* Drop the boost on the port and the owner of the receive right */
3257 if (ipc_port_importance_delta(port
, IPID_OPTION_NORMAL
, -1) == FALSE
) {
3263 #if IMPORTANCE_TRACE
3264 if (-1 < impresult
) {
3265 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_MSG
, IMP_MSG_DELV
)) | DBG_FUNC_NONE
,
3266 sender_pid
, task_pid(task_self
),
3267 kmsg
->ikm_header
->msgh_id
, impresult
, 0);
3269 if (impresult
== 2) {
3271 * This probe only covers new voucher-based path. Legacy importance
3272 * will trigger the probe in ipc_importance_task_externalize_assertion()
3273 * above and have impresult==1 here.
3275 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
);
3277 #endif /* IMPORTANCE_TRACE */
3281 * Routine: ipc_importance_unreceive
3283 * Undo receive of importance attributes in a message.
3289 ipc_importance_unreceive(
3291 mach_msg_option_t __unused option
)
3293 /* importance should already be in the voucher and out of the kmsg */
3294 assert(IIE_NULL
== kmsg
->ikm_importance
);
3296 /* See if there is a legacy boost to be dropped from receiver */
3297 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
)) {
3298 ipc_importance_task_t task_imp
;
3300 kmsg
->ikm_header
->msgh_bits
&= ~MACH_MSGH_BITS_RAISEIMP
;
3301 task_imp
= current_task()->task_imp_base
;
3302 if (!IP_VALID(kmsg
->ikm_voucher
) && IIT_NULL
!= task_imp
) {
3303 ipc_importance_task_drop_legacy_external_assertion(task_imp
, 1);
3306 * ipc_kmsg_copyout_dest() will consume the voucher
3307 * and any contained importance.
3313 * Routine: ipc_importance_clean
3315 * Clean up importance state in a kmsg that is being cleaned.
3316 * Unlink the importance chain if one was set up, and drop
3317 * the reference this kmsg held on the donor. Then check to
3318 * if importance was carried to the port, and remove that if
3324 ipc_importance_clean(
3329 /* Is the kmsg still linked? If so, remove that first */
3330 if (IIE_NULL
!= kmsg
->ikm_importance
) {
3331 ipc_importance_elem_t elem
;
3333 ipc_importance_lock();
3334 elem
= ipc_importance_kmsg_unlink(kmsg
);
3335 assert(IIE_NULL
!= elem
);
3336 ipc_importance_release_locked(elem
);
3337 /* importance unlocked */
3340 /* See if there is a legacy importance boost to be dropped from port */
3341 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
)) {
3342 kmsg
->ikm_header
->msgh_bits
&= ~MACH_MSGH_BITS_RAISEIMP
;
3343 port
= kmsg
->ikm_header
->msgh_remote_port
;
3344 if (IP_VALID(port
)) {
3346 /* inactive ports already had their importance boosts dropped */
3347 if (!ip_active(port
) ||
3348 ipc_port_importance_delta(port
, IPID_OPTION_NORMAL
, -1) == FALSE
) {
3356 ipc_importance_assert_clean(__assert_only ipc_kmsg_t kmsg
)
3358 assert(IIE_NULL
== kmsg
->ikm_importance
);
3359 assert(!MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
));
3363 * IPC Importance Attribute Manager definition
3366 static kern_return_t
3367 ipc_importance_release_value(
3368 ipc_voucher_attr_manager_t manager
,
3369 mach_voucher_attr_key_t key
,
3370 mach_voucher_attr_value_handle_t value
,
3371 mach_voucher_attr_value_reference_t sync
);
3373 static kern_return_t
3374 ipc_importance_get_value(
3375 ipc_voucher_attr_manager_t manager
,
3376 mach_voucher_attr_key_t key
,
3377 mach_voucher_attr_recipe_command_t command
,
3378 mach_voucher_attr_value_handle_array_t prev_values
,
3379 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
3380 mach_voucher_attr_content_t content
,
3381 mach_voucher_attr_content_size_t content_size
,
3382 mach_voucher_attr_value_handle_t
*out_value
,
3383 mach_voucher_attr_value_flags_t
*out_flags
,
3384 ipc_voucher_t
*out_value_voucher
);
3386 static kern_return_t
3387 ipc_importance_extract_content(
3388 ipc_voucher_attr_manager_t manager
,
3389 mach_voucher_attr_key_t key
,
3390 mach_voucher_attr_value_handle_array_t values
,
3391 mach_voucher_attr_value_handle_array_size_t value_count
,
3392 mach_voucher_attr_recipe_command_t
*out_command
,
3393 mach_voucher_attr_content_t out_content
,
3394 mach_voucher_attr_content_size_t
*in_out_content_size
);
3396 static kern_return_t
3397 ipc_importance_command(
3398 ipc_voucher_attr_manager_t manager
,
3399 mach_voucher_attr_key_t key
,
3400 mach_voucher_attr_value_handle_array_t values
,
3401 mach_msg_type_number_t value_count
,
3402 mach_voucher_attr_command_t command
,
3403 mach_voucher_attr_content_t in_content
,
3404 mach_voucher_attr_content_size_t in_content_size
,
3405 mach_voucher_attr_content_t out_content
,
3406 mach_voucher_attr_content_size_t
*out_content_size
);
3409 ipc_importance_manager_release(
3410 ipc_voucher_attr_manager_t manager
);
3412 struct ipc_voucher_attr_manager ipc_importance_manager
= {
3413 .ivam_release_value
= ipc_importance_release_value
,
3414 .ivam_get_value
= ipc_importance_get_value
,
3415 .ivam_extract_content
= ipc_importance_extract_content
,
3416 .ivam_command
= ipc_importance_command
,
3417 .ivam_release
= ipc_importance_manager_release
,
3418 .ivam_flags
= IVAM_FLAGS_NONE
,
3421 #define IMPORTANCE_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_IMPORTANCE == (key))
3422 #define IMPORTANCE_ASSERT_MANAGER(manager) assert(&ipc_importance_manager == (manager))
3425 * Routine: ipc_importance_release_value [Voucher Attribute Manager Interface]
3427 * Release what the voucher system believes is the last "made" reference
3428 * on an importance attribute value handle. The sync parameter is used to
3429 * avoid races with new made references concurrently being returned to the
3430 * voucher system in other threads.
3432 * Nothing locked on entry. May block.
3434 static kern_return_t
3435 ipc_importance_release_value(
3436 ipc_voucher_attr_manager_t __assert_only manager
,
3437 mach_voucher_attr_key_t __assert_only key
,
3438 mach_voucher_attr_value_handle_t value
,
3439 mach_voucher_attr_value_reference_t sync
)
3441 ipc_importance_elem_t elem
;
3443 IMPORTANCE_ASSERT_MANAGER(manager
);
3444 IMPORTANCE_ASSERT_KEY(key
);
3447 elem
= (ipc_importance_elem_t
)value
;
3449 ipc_importance_lock();
3451 /* Any oustanding made refs? */
3452 if (sync
!= elem
->iie_made
) {
3453 assert(sync
< elem
->iie_made
);
3454 ipc_importance_unlock();
3455 return KERN_FAILURE
;
3462 * If there are pending external boosts represented by this attribute,
3463 * drop them from the apropriate task
3465 if (IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) {
3466 ipc_importance_inherit_t inherit
= (ipc_importance_inherit_t
)elem
;
3468 assert(inherit
->iii_externcnt
>= inherit
->iii_externdrop
);
3470 if (inherit
->iii_donating
) {
3471 ipc_importance_task_t imp_task
= inherit
->iii_to_task
;
3472 uint32_t assertcnt
= III_EXTERN(inherit
);
3474 assert(ipc_importance_task_is_any_receiver_type(imp_task
));
3475 assert(imp_task
->iit_externcnt
>= inherit
->iii_externcnt
);
3476 assert(imp_task
->iit_externdrop
>= inherit
->iii_externdrop
);
3477 imp_task
->iit_externcnt
-= inherit
->iii_externcnt
;
3478 imp_task
->iit_externdrop
-= inherit
->iii_externdrop
;
3479 inherit
->iii_externcnt
= 0;
3480 inherit
->iii_externdrop
= 0;
3481 inherit
->iii_donating
= FALSE
;
3483 /* adjust the internal assertions - and propagate if needed */
3484 if (ipc_importance_task_check_transition(imp_task
, IIT_UPDATE_DROP
, assertcnt
)) {
3485 ipc_importance_task_propagate_assertion_locked(imp_task
, IIT_UPDATE_DROP
, TRUE
);
3488 inherit
->iii_externcnt
= 0;
3489 inherit
->iii_externdrop
= 0;
3493 /* drop the made reference on elem */
3494 ipc_importance_release_locked(elem
);
3495 /* returns unlocked */
3497 return KERN_SUCCESS
;
3502 * Routine: ipc_importance_get_value [Voucher Attribute Manager Interface]
3504 * Convert command and content data into a reference on a [potentially new]
3505 * attribute value. The importance attribute manager will only allow the
3506 * caller to get a value for the current task's importance, or to redeem
3507 * an importance attribute from an existing voucher.
3509 * Nothing locked on entry. May block.
3511 static kern_return_t
3512 ipc_importance_get_value(
3513 ipc_voucher_attr_manager_t __assert_only manager
,
3514 mach_voucher_attr_key_t __assert_only key
,
3515 mach_voucher_attr_recipe_command_t command
,
3516 mach_voucher_attr_value_handle_array_t prev_values
,
3517 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
3518 mach_voucher_attr_content_t __unused content
,
3519 mach_voucher_attr_content_size_t content_size
,
3520 mach_voucher_attr_value_handle_t
*out_value
,
3521 mach_voucher_attr_value_flags_t
*out_flags
,
3522 ipc_voucher_t
*out_value_voucher
)
3524 ipc_importance_elem_t elem
;
3527 IMPORTANCE_ASSERT_MANAGER(manager
);
3528 IMPORTANCE_ASSERT_KEY(key
);
3530 if (0 != content_size
) {
3531 return KERN_INVALID_ARGUMENT
;
3534 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
;
3535 /* never an out voucher */
3538 case MACH_VOUCHER_ATTR_REDEEM
:
3540 /* redeem of previous values is the value */
3541 if (0 < prev_value_count
) {
3542 elem
= (ipc_importance_elem_t
)prev_values
[0];
3543 assert(IIE_NULL
!= elem
);
3545 ipc_importance_lock();
3546 assert(0 < elem
->iie_made
);
3548 ipc_importance_unlock();
3550 *out_value
= prev_values
[0];
3551 return KERN_SUCCESS
;
3554 /* redeem of default is default */
3556 *out_value_voucher
= IPC_VOUCHER_NULL
;
3557 return KERN_SUCCESS
;
3559 case MACH_VOUCHER_ATTR_IMPORTANCE_SELF
:
3560 self
= current_task();
3562 elem
= (ipc_importance_elem_t
)ipc_importance_for_task(self
, TRUE
);
3563 /* made reference added (or IIE_NULL which isn't referenced) */
3565 *out_value
= (mach_voucher_attr_value_handle_t
)elem
;
3566 *out_value_voucher
= IPC_VOUCHER_NULL
;
3567 return KERN_SUCCESS
;
3571 * every other command is unknown
3573 * Specifically, there is no mechanism provided to construct an
3574 * importance attribute for a task/process from just a pid or
3575 * task port. It has to be copied (or redeemed) from a previous
3576 * voucher that has it.
3578 return KERN_INVALID_ARGUMENT
;
3583 * Routine: ipc_importance_extract_content [Voucher Attribute Manager Interface]
3585 * Extract meaning from the attribute value present in a voucher. While
3586 * the real goal is to provide commands and data that can reproduce the
3587 * voucher's value "out of thin air", this isn't possible with importance
3588 * attribute values. Instead, return debug info to help track down dependencies.
3590 * Nothing locked on entry. May block.
3592 static kern_return_t
3593 ipc_importance_extract_content(
3594 ipc_voucher_attr_manager_t __assert_only manager
,
3595 mach_voucher_attr_key_t __assert_only key
,
3596 mach_voucher_attr_value_handle_array_t values
,
3597 mach_voucher_attr_value_handle_array_size_t value_count
,
3598 mach_voucher_attr_recipe_command_t
*out_command
,
3599 mach_voucher_attr_content_t out_content
,
3600 mach_voucher_attr_content_size_t
*in_out_content_size
)
3602 mach_voucher_attr_content_size_t size
= 0;
3603 ipc_importance_elem_t elem
;
3606 IMPORTANCE_ASSERT_MANAGER(manager
);
3607 IMPORTANCE_ASSERT_KEY(key
);
3609 /* the first non-default value provides the data */
3610 for (i
= 0; i
< value_count
&& *in_out_content_size
> 0; i
++) {
3611 elem
= (ipc_importance_elem_t
)values
[i
];
3612 if (IIE_NULL
== elem
) {
3616 snprintf((char *)out_content
, *in_out_content_size
, "Importance for pid ");
3617 size
= (mach_voucher_attr_content_size_t
)strlen((char *)out_content
);
3620 ipc_importance_inherit_t inherit
= III_NULL
;
3621 ipc_importance_task_t task_imp
;
3625 if (IIE_TYPE_TASK
== IIE_TYPE(elem
)) {
3626 task_imp
= (ipc_importance_task_t
)elem
;
3627 task
= task_imp
->iit_task
;
3628 t_pid
= (TASK_NULL
!= task
) ?
3629 task_pid(task
) : -1;
3630 snprintf((char *)out_content
+ size
, *in_out_content_size
- size
, "%d", t_pid
);
3632 inherit
= (ipc_importance_inherit_t
)elem
;
3633 task_imp
= inherit
->iii_to_task
;
3634 task
= task_imp
->iit_task
;
3635 t_pid
= (TASK_NULL
!= task
) ?
3636 task_pid(task
) : -1;
3637 snprintf((char *)out_content
+ size
, *in_out_content_size
- size
,
3638 "%d (%d of %d boosts) %s from pid ", t_pid
,
3639 III_EXTERN(inherit
), inherit
->iii_externcnt
,
3640 (inherit
->iii_donating
) ? "donated" : "linked");
3643 size
= (mach_voucher_attr_content_size_t
)strlen((char *)out_content
);
3645 if (III_NULL
== inherit
) {
3649 elem
= inherit
->iii_from_elem
;
3651 size
++; /* account for NULL */
3653 *out_command
= MACH_VOUCHER_ATTR_NOOP
; /* cannot be used to regenerate value */
3654 *in_out_content_size
= size
;
3655 return KERN_SUCCESS
;
3659 * Routine: ipc_importance_command [Voucher Attribute Manager Interface]
3661 * Run commands against the importance attribute value found in a voucher.
3662 * No such commands are currently supported.
3664 * Nothing locked on entry. May block.
3666 static kern_return_t
3667 ipc_importance_command(
3668 ipc_voucher_attr_manager_t __assert_only manager
,
3669 mach_voucher_attr_key_t __assert_only key
,
3670 mach_voucher_attr_value_handle_array_t values
,
3671 mach_msg_type_number_t value_count
,
3672 mach_voucher_attr_command_t command
,
3673 mach_voucher_attr_content_t in_content
,
3674 mach_voucher_attr_content_size_t in_content_size
,
3675 mach_voucher_attr_content_t out_content
,
3676 mach_voucher_attr_content_size_t
*out_content_size
)
3678 ipc_importance_inherit_t inherit
;
3679 ipc_importance_task_t to_task
;
3680 uint32_t refs
, *outrefsp
;
3681 mach_msg_type_number_t i
;
3684 IMPORTANCE_ASSERT_MANAGER(manager
);
3685 IMPORTANCE_ASSERT_KEY(key
);
3687 if (in_content_size
!= sizeof(refs
) ||
3688 (*out_content_size
!= 0 && *out_content_size
!= sizeof(refs
))) {
3689 return KERN_INVALID_ARGUMENT
;
3691 refs
= *(uint32_t *)(void *)in_content
;
3692 outrefsp
= (*out_content_size
!= 0) ? (uint32_t *)(void *)out_content
: NULL
;
3694 if (MACH_VOUCHER_IMPORTANCE_ATTR_DROP_EXTERNAL
!= command
) {
3695 return KERN_NOT_SUPPORTED
;
3698 /* the first non-default value of the apropos type provides the data */
3700 for (i
= 0; i
< value_count
; i
++) {
3701 ipc_importance_elem_t elem
= (ipc_importance_elem_t
)values
[i
];
3703 if (IIE_NULL
!= elem
&& IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) {
3704 inherit
= (ipc_importance_inherit_t
)elem
;
3708 if (III_NULL
== inherit
) {
3709 return KERN_INVALID_ARGUMENT
;
3712 ipc_importance_lock();
3715 if (NULL
!= outrefsp
) {
3716 *outrefsp
= III_EXTERN(inherit
);
3718 ipc_importance_unlock();
3719 return KERN_SUCCESS
;
3722 to_task
= inherit
->iii_to_task
;
3723 assert(ipc_importance_task_is_any_receiver_type(to_task
));
3725 /* if not donating to a denap receiver, it was called incorrectly */
3726 if (!ipc_importance_task_is_marked_denap_receiver(to_task
)) {
3727 ipc_importance_unlock();
3728 return KERN_INVALID_TASK
; /* keeps dispatch happy */
3731 /* Enough external references left to drop? */
3732 if (III_EXTERN(inherit
) < refs
) {
3733 ipc_importance_unlock();
3734 return KERN_FAILURE
;
3737 /* re-base external and internal counters at the inherit and the to-task (if apropos) */
3738 if (inherit
->iii_donating
) {
3739 assert(IIT_EXTERN(to_task
) >= III_EXTERN(inherit
));
3740 assert(to_task
->iit_externcnt
>= inherit
->iii_externcnt
);
3741 assert(to_task
->iit_externdrop
>= inherit
->iii_externdrop
);
3742 inherit
->iii_externdrop
+= refs
;
3743 to_task
->iit_externdrop
+= refs
;
3744 externcnt
= III_EXTERN(inherit
);
3745 if (0 == externcnt
) {
3746 inherit
->iii_donating
= FALSE
;
3747 to_task
->iit_externcnt
-= inherit
->iii_externcnt
;
3748 to_task
->iit_externdrop
-= inherit
->iii_externdrop
;
3751 /* Start AppNap delay hysteresis - even if not the last boost for the task. */
3752 if (ipc_importance_delayed_drop_call
!= NULL
&&
3753 ipc_importance_task_is_marked_denap_receiver(to_task
)) {
3754 ipc_importance_task_delayed_drop(to_task
);
3757 /* drop task assertions associated with the dropped boosts */
3758 if (ipc_importance_task_check_transition(to_task
, IIT_UPDATE_DROP
, refs
)) {
3759 ipc_importance_task_propagate_assertion_locked(to_task
, IIT_UPDATE_DROP
, TRUE
);
3760 /* may have dropped and retaken importance lock */
3763 /* assert(to_task->iit_assertcnt >= refs + externcnt); */
3764 /* defensive deduction in case of assertcnt underflow */
3765 if (to_task
->iit_assertcnt
> refs
+ externcnt
) {
3766 to_task
->iit_assertcnt
-= refs
;
3768 to_task
->iit_assertcnt
= externcnt
;
3772 inherit
->iii_externdrop
+= refs
;
3773 externcnt
= III_EXTERN(inherit
);
3776 /* capture result (if requested) */
3777 if (NULL
!= outrefsp
) {
3778 *outrefsp
= externcnt
;
3781 ipc_importance_unlock();
3782 return KERN_SUCCESS
;
3786 * Routine: ipc_importance_manager_release [Voucher Attribute Manager Interface]
3788 * Release the Voucher system's reference on the IPC importance attribute
3791 * As this can only occur after the manager drops the Attribute control
3792 * reference granted back at registration time, and that reference is never
3793 * 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)
3813 natural_t ipc_importance_max
= (task_max
+ thread_max
) * 2;
3817 if (PE_parse_boot_argn("imp_interactive_receiver", temp_buf
, sizeof(temp_buf
))) {
3818 ipc_importance_interactive_receiver
= TRUE
;
3821 ipc_importance_task_zone
= zinit(sizeof(struct ipc_importance_task
),
3822 ipc_importance_max
* sizeof(struct ipc_importance_task
),
3823 sizeof(struct ipc_importance_task
),
3824 "ipc task importance");
3825 zone_change(ipc_importance_task_zone
, Z_NOENCRYPT
, TRUE
);
3827 ipc_importance_inherit_zone
= zinit(sizeof(struct ipc_importance_inherit
),
3828 ipc_importance_max
* sizeof(struct ipc_importance_inherit
),
3829 sizeof(struct ipc_importance_inherit
),
3830 "ipc importance inherit");
3831 zone_change(ipc_importance_inherit_zone
, Z_NOENCRYPT
, TRUE
);
3834 #if DEVELOPMENT || DEBUG
3835 queue_init(&global_iit_alloc_queue
);
3838 /* initialize global locking */
3839 ipc_importance_lock_init();
3841 kr
= ipc_register_well_known_mach_voucher_attr_manager(&ipc_importance_manager
,
3842 (mach_voucher_attr_value_handle_t
)0,
3843 MACH_VOUCHER_ATTR_KEY_IMPORTANCE
,
3844 &ipc_importance_control
);
3845 if (KERN_SUCCESS
!= kr
) {
3846 printf("Voucher importance manager register returned %d", kr
);
3851 * Routine: ipc_importance_thread_call_init
3853 * Initialize the IPC importance code dependent upon
3854 * thread-call support being available.
3856 * Thread-call mechanism is already initialized.
3859 ipc_importance_thread_call_init(void)
3861 /* initialize delayed drop queue and thread-call */
3862 queue_init(&ipc_importance_delayed_drop_queue
);
3863 ipc_importance_delayed_drop_call
=
3864 thread_call_allocate(ipc_importance_task_delayed_drop_scan
, NULL
);
3865 if (NULL
== ipc_importance_delayed_drop_call
) {
3866 panic("ipc_importance_init");
3871 * Routing: task_importance_list_pids
3872 * Purpose: list pids where task in donating importance.
3873 * Conditions: To be called only from kdp stackshot code.
3874 * Will panic the system otherwise.
3877 task_importance_list_pids(task_t task
, int flags
, char *pid_list
, unsigned int max_count
)
3879 if (kdp_lck_spin_is_acquired(&ipc_importance_lock_data
) ||
3881 task
->task_imp_base
== IIT_NULL
||
3883 flags
!= TASK_IMP_LIST_DONATING_PIDS
) {
3886 unsigned int pidcount
= 0;
3888 ipc_importance_task_t task_imp
= task
->task_imp_base
;
3889 ipc_kmsg_t temp_kmsg
;
3890 ipc_importance_inherit_t temp_inherit
;
3891 ipc_importance_elem_t elem
;
3892 int target_pid
= 0, previous_pid
;
3894 queue_iterate(&task_imp
->iit_inherits
, temp_inherit
, ipc_importance_inherit_t
, iii_inheritance
) {
3895 /* check space in buffer */
3896 if (pidcount
>= max_count
) {
3899 previous_pid
= target_pid
;
3902 if (temp_inherit
->iii_donating
) {
3903 #if DEVELOPMENT || DEBUG
3904 target_pid
= temp_inherit
->iii_to_task
->iit_bsd_pid
;
3906 temp_task
= temp_inherit
->iii_to_task
->iit_task
;
3907 if (temp_task
!= TASK_NULL
) {
3908 target_pid
= task_pid(temp_task
);
3913 if (target_pid
!= -1 && previous_pid
!= target_pid
) {
3914 memcpy(pid_list
, &target_pid
, sizeof(target_pid
));
3915 pid_list
+= sizeof(target_pid
);
3921 queue_iterate(&task_imp
->iit_kmsgs
, temp_kmsg
, ipc_kmsg_t
, ikm_inheritance
) {
3922 if (pidcount
>= max_count
) {
3925 previous_pid
= target_pid
;
3927 elem
= temp_kmsg
->ikm_importance
;
3928 temp_task
= TASK_NULL
;
3930 if (elem
== IIE_NULL
) {
3934 if (!(temp_kmsg
->ikm_header
&& MACH_MSGH_BITS_RAISED_IMPORTANCE(temp_kmsg
->ikm_header
->msgh_bits
))) {
3938 if (IIE_TYPE_TASK
== IIE_TYPE(elem
) &&
3939 (((ipc_importance_task_t
)elem
)->iit_task
!= TASK_NULL
)) {
3940 target_pid
= task_pid(((ipc_importance_task_t
)elem
)->iit_task
);
3942 temp_inherit
= (ipc_importance_inherit_t
)elem
;
3943 #if DEVELOPMENT || DEBUG
3944 target_pid
= temp_inherit
->iii_to_task
->iit_bsd_pid
;
3946 temp_task
= temp_inherit
->iii_to_task
->iit_task
;
3947 if (temp_task
!= TASK_NULL
) {
3948 target_pid
= task_pid(temp_task
);
3953 if (target_pid
!= -1 && previous_pid
!= target_pid
) {
3954 memcpy(pid_list
, &target_pid
, sizeof(target_pid
));
3955 pid_list
+= sizeof(target_pid
);