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(&ipc_importance_lock_data)
83 #define ipc_importance_lock_try() \
84 lck_spin_try_lock(&ipc_importance_lock_data)
85 #define ipc_importance_unlock() \
86 lck_spin_unlock(&ipc_importance_lock_data)
87 #define ipc_importance_assert_held() \
88 lck_spin_assert(&ipc_importance_lock_data, LCK_ASSERT_OWNED)
89 #define ipc_importance_sleep(elem) lck_spin_sleep(&ipc_importance_lock_data, \
93 #define ipc_importance_wakeup(elem) thread_wakeup((event_t)(elem))
96 #define incr_ref_counter(x) (hw_atomic_add(&(x), 1))
99 uint32_t ipc_importance_reference_internal(ipc_importance_elem_t elem
)
101 incr_ref_counter(elem
->iie_refs_added
);
102 return (hw_atomic_add(&elem
->iie_bits
, 1) & IIE_REFS_MASK
);
106 uint32_t ipc_importance_release_internal(ipc_importance_elem_t elem
)
108 incr_ref_counter(elem
->iie_refs_dropped
);
109 return (hw_atomic_sub(&elem
->iie_bits
, 1) & IIE_REFS_MASK
);
113 uint32_t ipc_importance_task_reference_internal(ipc_importance_task_t task_imp
)
116 out
= ipc_importance_reference_internal(&task_imp
->iit_elem
);
117 incr_ref_counter(task_imp
->iit_elem
.iie_task_refs_added
);
122 uint32_t ipc_importance_task_release_internal(ipc_importance_task_t task_imp
)
126 assert(1 < IIT_REFS(task_imp
));
127 incr_ref_counter(task_imp
->iit_elem
.iie_task_refs_dropped
);
128 out
= ipc_importance_release_internal(&task_imp
->iit_elem
);
133 void ipc_importance_counter_init(ipc_importance_elem_t elem
)
136 elem
->iie_refs_added
= 0;
137 elem
->iie_refs_dropped
= 0;
138 elem
->iie_kmsg_refs_added
= 0;
139 elem
->iie_kmsg_refs_inherited
= 0;
140 elem
->iie_kmsg_refs_coalesced
= 0;
141 elem
->iie_kmsg_refs_dropped
= 0;
142 elem
->iie_task_refs_added
= 0;
143 elem
->iie_task_refs_added_inherit_from
= 0;
144 elem
->iie_task_refs_added_transition
= 0;
145 elem
->iie_task_refs_self_added
= 0;
146 elem
->iie_task_refs_inherited
= 0;
147 elem
->iie_task_refs_coalesced
= 0;
148 elem
->iie_task_refs_dropped
= 0;
151 #define incr_ref_counter(x)
154 #if DEVELOPMENT || DEBUG
155 static queue_head_t global_iit_alloc_queue
;
158 /* TODO: remove this varibale when interactive daemon audit is complete */
159 boolean_t ipc_importance_interactive_receiver
= FALSE
;
161 static zone_t ipc_importance_task_zone
;
162 static zone_t ipc_importance_inherit_zone
;
164 static ipc_voucher_attr_control_t ipc_importance_control
;
166 static boolean_t
ipc_importance_task_check_transition(ipc_importance_task_t task_imp
,
167 iit_update_type_t type
, uint32_t delta
);
169 static void ipc_importance_task_propagate_assertion_locked(ipc_importance_task_t task_imp
,
170 iit_update_type_t type
, boolean_t update_task_imp
);
172 static ipc_importance_inherit_t
ipc_importance_inherit_from_task(task_t from_task
, task_t to_task
);
175 * Routine: ipc_importance_kmsg_link
177 * Link the kmsg onto the appropriate propagation chain.
178 * If the element is a task importance, we link directly
179 * on its propagation chain. Otherwise, we link onto the
180 * destination task of the inherit.
182 * Importance lock held.
183 * Caller is donating an importance elem reference to the kmsg.
186 ipc_importance_kmsg_link(
188 ipc_importance_elem_t elem
)
190 ipc_importance_elem_t link_elem
;
192 assert(IIE_NULL
== kmsg
->ikm_importance
);
194 link_elem
= (IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) ?
195 (ipc_importance_elem_t
)((ipc_importance_inherit_t
)elem
)->iii_to_task
:
198 queue_enter(&link_elem
->iie_kmsgs
, kmsg
, ipc_kmsg_t
, ikm_inheritance
);
199 kmsg
->ikm_importance
= elem
;
203 * Routine: ipc_importance_kmsg_unlink
205 * Unlink the kmsg from its current propagation chain.
206 * If the element is a task importance, we unlink directly
207 * from its propagation chain. Otherwise, we unlink from the
208 * destination task of the inherit.
210 * The reference to the importance element it was linked on.
212 * Importance lock held.
213 * Caller is responsible for dropping reference on returned elem.
215 static ipc_importance_elem_t
216 ipc_importance_kmsg_unlink(
219 ipc_importance_elem_t elem
= kmsg
->ikm_importance
;
221 if (IIE_NULL
!= elem
) {
222 ipc_importance_elem_t unlink_elem
;
224 unlink_elem
= (IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) ?
225 (ipc_importance_elem_t
)((ipc_importance_inherit_t
)elem
)->iii_to_task
:
228 queue_remove(&unlink_elem
->iie_kmsgs
, kmsg
, ipc_kmsg_t
, ikm_inheritance
);
229 kmsg
->ikm_importance
= IIE_NULL
;
235 * Routine: ipc_importance_inherit_link
237 * Link the inherit onto the appropriate propagation chain.
238 * If the element is a task importance, we link directly
239 * on its propagation chain. Otherwise, we link onto the
240 * destination task of the inherit.
242 * Importance lock held.
243 * Caller is donating an elem importance reference to the inherit.
246 ipc_importance_inherit_link(
247 ipc_importance_inherit_t inherit
,
248 ipc_importance_elem_t elem
)
250 ipc_importance_task_t link_task
;
252 assert(IIE_NULL
== inherit
->iii_from_elem
);
253 link_task
= (IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) ?
254 ((ipc_importance_inherit_t
)elem
)->iii_to_task
:
255 (ipc_importance_task_t
)elem
;
257 queue_enter(&link_task
->iit_inherits
, inherit
,
258 ipc_importance_inherit_t
, iii_inheritance
);
259 inherit
->iii_from_elem
= elem
;
263 * Routine: ipc_importance_inherit_find
265 * Find an existing inherit that links the from element to the
266 * to_task at a given nesting depth. As inherits from other
267 * inherits are actually linked off the original inherit's donation
268 * receiving task, we have to conduct our search from there if
269 * the from element is an inherit.
271 * A pointer (not a reference) to the matching inherit.
273 * Importance lock held.
275 static ipc_importance_inherit_t
276 ipc_importance_inherit_find(
277 ipc_importance_elem_t from
,
278 ipc_importance_task_t to_task
,
281 ipc_importance_task_t link_task
;
282 ipc_importance_inherit_t inherit
;
284 link_task
= (IIE_TYPE_INHERIT
== IIE_TYPE(from
)) ?
285 ((ipc_importance_inherit_t
)from
)->iii_to_task
:
286 (ipc_importance_task_t
)from
;
288 queue_iterate(&link_task
->iit_inherits
, inherit
,
289 ipc_importance_inherit_t
, iii_inheritance
) {
290 if (inherit
->iii_to_task
== to_task
&& inherit
->iii_depth
== depth
) {
298 * Routine: ipc_importance_inherit_unlink
300 * Unlink the inherit from its current propagation chain.
301 * If the element is a task importance, we unlink directly
302 * from its propagation chain. Otherwise, we unlink from the
303 * destination task of the inherit.
305 * The reference to the importance element it was linked on.
307 * Importance lock held.
308 * Caller is responsible for dropping reference on returned elem.
310 static ipc_importance_elem_t
311 ipc_importance_inherit_unlink(
312 ipc_importance_inherit_t inherit
)
314 ipc_importance_elem_t elem
= inherit
->iii_from_elem
;
316 if (IIE_NULL
!= elem
) {
317 ipc_importance_task_t unlink_task
;
319 unlink_task
= (IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) ?
320 ((ipc_importance_inherit_t
)elem
)->iii_to_task
:
321 (ipc_importance_task_t
)elem
;
323 queue_remove(&unlink_task
->iit_inherits
, inherit
,
324 ipc_importance_inherit_t
, iii_inheritance
);
325 inherit
->iii_from_elem
= IIE_NULL
;
331 * Routine: ipc_importance_reference
333 * Add a reference to the importance element.
335 * Caller must hold a reference on the element.
338 ipc_importance_reference(ipc_importance_elem_t elem
)
340 assert(0 < IIE_REFS(elem
));
341 ipc_importance_reference_internal(elem
);
345 * Routine: ipc_importance_release_locked
347 * Release a reference on an importance attribute value,
348 * unlinking and deallocating the attribute if the last reference.
350 * Entered with importance lock held, leaves with it unlocked.
353 ipc_importance_release_locked(ipc_importance_elem_t elem
)
355 assert(0 < IIE_REFS(elem
));
358 ipc_importance_inherit_t temp_inherit
;
359 ipc_importance_task_t link_task
;
360 ipc_kmsg_t temp_kmsg
;
361 uint32_t expected
= 0;
363 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
)
373 queue_iterate(&link_task
->iit_inherits
, temp_inherit
,
374 ipc_importance_inherit_t
, iii_inheritance
)
375 if (temp_inherit
->iii_from_elem
== elem
)
377 if (IIE_REFS(elem
) < expected
+ 1)
378 panic("ipc_importance_release_locked (%p)", elem
);
379 #endif /* IMPORTANCE_DEBUG */
381 if (0 < ipc_importance_release_internal(elem
)) {
382 ipc_importance_unlock();
388 switch (IIE_TYPE(elem
)) {
390 /* just a "from" task reference to drop */
393 ipc_importance_task_t task_elem
;
395 task_elem
= (ipc_importance_task_t
)elem
;
397 /* the task can't still hold a reference on the task importance */
398 assert(TASK_NULL
== task_elem
->iit_task
);
400 #if DEVELOPMENT || DEBUG
401 queue_remove(&global_iit_alloc_queue
, task_elem
, ipc_importance_task_t
, iit_allocation
);
404 ipc_importance_unlock();
406 zfree(ipc_importance_task_zone
, task_elem
);
410 /* dropping an inherit element */
411 case IIE_TYPE_INHERIT
:
413 ipc_importance_inherit_t inherit
= (ipc_importance_inherit_t
)elem
;
414 ipc_importance_task_t to_task
= inherit
->iii_to_task
;
415 ipc_importance_elem_t from_elem
;
417 assert(IIT_NULL
!= to_task
);
418 assert(ipc_importance_task_is_any_receiver_type(to_task
));
420 /* unlink the inherit from its source element */
421 from_elem
= ipc_importance_inherit_unlink(inherit
);
422 assert(IIE_NULL
!= from_elem
);
425 * The attribute might have pending external boosts if the attribute
426 * was given out during exec, drop them from the appropriate destination
429 * The attribute will not have any pending external boosts if the
430 * attribute was given out to voucher system since it would have been
431 * dropped by ipc_importance_release_value, but there is not way to
432 * detect that, thus if the attribute has a pending external boost,
433 * drop them from the appropriate destination task.
435 * The inherit attribute from exec and voucher system would not
436 * get deduped to each other, thus dropping the external boost
437 * from destination task at two different places will not have
438 * any unintended side effects.
440 assert(inherit
->iii_externcnt
>= inherit
->iii_externdrop
);
441 if (inherit
->iii_donating
) {
442 uint32_t assertcnt
= III_EXTERN(inherit
);
444 assert(ipc_importance_task_is_any_receiver_type(to_task
));
445 assert(to_task
->iit_externcnt
>= inherit
->iii_externcnt
);
446 assert(to_task
->iit_externdrop
>= inherit
->iii_externdrop
);
447 to_task
->iit_externcnt
-= inherit
->iii_externcnt
;
448 to_task
->iit_externdrop
-= inherit
->iii_externdrop
;
449 inherit
->iii_externcnt
= 0;
450 inherit
->iii_externdrop
= 0;
451 inherit
->iii_donating
= FALSE
;
453 /* adjust the internal assertions - and propagate as needed */
454 if (ipc_importance_task_check_transition(to_task
, IIT_UPDATE_DROP
, assertcnt
)) {
455 ipc_importance_task_propagate_assertion_locked(to_task
, IIT_UPDATE_DROP
, TRUE
);
458 inherit
->iii_externcnt
= 0;
459 inherit
->iii_externdrop
= 0;
462 /* release the reference on the source element */
463 ipc_importance_release_locked(from_elem
);
464 /* unlocked on return */
466 /* release the reference on the destination task */
467 ipc_importance_task_release(to_task
);
469 /* free the inherit */
470 zfree(ipc_importance_inherit_zone
, inherit
);
477 * Routine: ipc_importance_release
479 * Release a reference on an importance attribute value,
480 * unlinking and deallocating the attribute if the last reference.
482 * nothing locked on entrance, nothing locked on exit.
486 ipc_importance_release(ipc_importance_elem_t elem
)
488 if (IIE_NULL
== elem
)
491 ipc_importance_lock();
492 ipc_importance_release_locked(elem
);
497 * Routine: ipc_importance_task_reference
501 * Retain a reference on a task importance attribute value.
503 * nothing locked on entrance, nothing locked on exit.
504 * caller holds a reference already.
507 ipc_importance_task_reference(ipc_importance_task_t task_elem
)
509 if (IIT_NULL
== task_elem
)
512 incr_ref_counter(task_elem
->iit_elem
.iie_task_refs_added
);
514 ipc_importance_reference(&task_elem
->iit_elem
);
518 * Routine: ipc_importance_task_release
520 * Release a reference on a task importance attribute value,
521 * unlinking and deallocating the attribute if the last reference.
523 * nothing locked on entrance, nothing locked on exit.
527 ipc_importance_task_release(ipc_importance_task_t task_elem
)
529 if (IIT_NULL
== task_elem
)
532 ipc_importance_lock();
534 incr_ref_counter(task_elem
->iit_elem
.iie_task_refs_dropped
);
536 ipc_importance_release_locked(&task_elem
->iit_elem
);
541 * Routine: ipc_importance_task_release_locked
543 * Release a reference on a task importance attribute value,
544 * unlinking and deallocating the attribute if the last reference.
546 * importance lock held on entry, nothing locked on exit.
550 ipc_importance_task_release_locked(ipc_importance_task_t task_elem
)
552 if (IIT_NULL
== task_elem
) {
553 ipc_importance_unlock();
557 incr_ref_counter(task_elem
->iit_elem
.iie_task_refs_dropped
);
559 ipc_importance_release_locked(&task_elem
->iit_elem
);
564 * Routines for importance donation/inheritance/boosting
569 * External importance assertions are managed by the process in userspace
570 * Internal importance assertions are the responsibility of the kernel
571 * Assertions are changed from internal to external via task_importance_externalize_assertion
575 * Routine: ipc_importance_task_check_transition
577 * Increase or decrement the internal task importance counter of the
578 * specified task and determine if propagation and a task policy
579 * update is required.
581 * If it is already enqueued for a policy update, steal it from that queue
582 * (as we are reversing that update before it happens).
585 * Called with the importance lock held.
586 * It is the caller's responsibility to perform the propagation of the
587 * transition and/or policy changes by checking the return value.
590 ipc_importance_task_check_transition(
591 ipc_importance_task_t task_imp
,
592 iit_update_type_t type
,
596 task_t target_task
= task_imp
->iit_task
;
598 boolean_t boost
= (IIT_UPDATE_HOLD
== type
);
599 boolean_t before_boosted
, after_boosted
;
601 ipc_importance_assert_held();
603 if (!ipc_importance_task_is_any_receiver_type(task_imp
))
607 int target_pid
= task_pid(target_task
);
609 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, (((boost
) ? IMP_HOLD
: IMP_DROP
) | TASK_POLICY_INTERNAL
))) | DBG_FUNC_START
,
610 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_EXTERN(task_imp
), 0);
613 /* snapshot the effective boosting status before making any changes */
614 before_boosted
= (task_imp
->iit_assertcnt
> 0);
616 /* Adjust the assertcnt appropriately */
618 task_imp
->iit_assertcnt
+= delta
;
620 DTRACE_BOOST6(send_boost
, task_t
, target_task
, int, target_pid
,
621 task_t
, current_task(), int, proc_selfpid(), int, delta
, int, task_imp
->iit_assertcnt
);
624 // assert(delta <= task_imp->iit_assertcnt);
625 if (task_imp
->iit_assertcnt
< delta
+ IIT_EXTERN(task_imp
)) {
626 /* TODO: Turn this back into a panic <rdar://problem/12592649> */
627 task_imp
->iit_assertcnt
= IIT_EXTERN(task_imp
);
629 task_imp
->iit_assertcnt
-= delta
;
632 // This convers both legacy and voucher-based importance.
633 DTRACE_BOOST4(drop_boost
, task_t
, target_task
, int, target_pid
, int, delta
, int, task_imp
->iit_assertcnt
);
638 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, (((boost
) ? IMP_HOLD
: IMP_DROP
) | TASK_POLICY_INTERNAL
))) | DBG_FUNC_END
,
639 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_EXTERN(task_imp
), 0);
642 /* did the change result in an effective donor status change? */
643 after_boosted
= (task_imp
->iit_assertcnt
> 0);
645 if (after_boosted
!= before_boosted
) {
648 * If the task importance is already on an update queue, we just reversed the need for a
649 * pending policy update. If the queue is any other than the delayed-drop-queue, pull it
650 * off that queue and release the reference it got going onto the update queue. If it is
651 * the delayed-drop-queue we leave it in place in case it comes back into the drop state
652 * before its time delay is up.
654 * We still need to propagate the change downstream to reverse the assertcnt effects,
655 * but we no longer need to update this task's boost policy state.
657 * Otherwise, mark it as needing a policy update.
659 assert(0 == task_imp
->iit_updatepolicy
);
660 if (NULL
!= task_imp
->iit_updateq
) {
661 if (&ipc_importance_delayed_drop_queue
!= task_imp
->iit_updateq
) {
662 queue_remove(task_imp
->iit_updateq
, task_imp
, ipc_importance_task_t
, iit_updates
);
663 task_imp
->iit_updateq
= NULL
;
664 ipc_importance_task_release_internal(task_imp
); /* can't be last ref */
667 task_imp
->iit_updatepolicy
= 1;
677 * Routine: ipc_importance_task_propagate_helper
679 * Increase or decrement the internal task importance counter of all
680 * importance tasks inheriting from the specified one. If this causes
681 * that importance task to change state, add it to the list of tasks
682 * to do a policy update against.
684 * Called with the importance lock held.
685 * It is the caller's responsibility to iterate down the generated list
686 * and propagate any subsequent assertion changes from there.
689 ipc_importance_task_propagate_helper(
690 ipc_importance_task_t task_imp
,
691 iit_update_type_t type
,
694 ipc_importance_task_t temp_task_imp
;
697 * iterate the downstream kmsgs, adjust their boosts,
698 * and capture the next task to adjust for each message
701 ipc_kmsg_t temp_kmsg
;
703 queue_iterate(&task_imp
->iit_kmsgs
, temp_kmsg
, ipc_kmsg_t
, ikm_inheritance
) {
704 mach_msg_header_t
*hdr
= temp_kmsg
->ikm_header
;
705 mach_port_delta_t delta
;
708 /* toggle the kmsg importance bit as a barrier to parallel adjusts */
709 if (IIT_UPDATE_HOLD
== type
) {
710 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(hdr
->msgh_bits
)) {
714 /* mark the message as now carrying importance */
715 hdr
->msgh_bits
|= MACH_MSGH_BITS_RAISEIMP
;
718 if (!MACH_MSGH_BITS_RAISED_IMPORTANCE(hdr
->msgh_bits
)) {
722 /* clear the message as now carrying importance */
723 hdr
->msgh_bits
&= ~MACH_MSGH_BITS_RAISEIMP
;
727 /* determine the task importance to adjust as result (if any) */
728 port
= (ipc_port_t
) hdr
->msgh_remote_port
;
729 assert(IP_VALID(port
));
731 temp_task_imp
= IIT_NULL
;
732 if (!ipc_port_importance_delta_internal(port
, IPID_OPTION_NORMAL
, &delta
, &temp_task_imp
)) {
736 /* no task importance to adjust associated with the port? */
737 if (IIT_NULL
== temp_task_imp
) {
741 /* hold a reference on temp_task_imp */
743 /* Adjust the task assertions and determine if an edge was crossed */
744 if (ipc_importance_task_check_transition(temp_task_imp
, type
, 1)) {
745 incr_ref_counter(temp_task_imp
->iit_elem
.iie_task_refs_added_transition
);
746 queue_enter(propagation
, temp_task_imp
, ipc_importance_task_t
, iit_props
);
747 /* reference donated */
749 ipc_importance_task_release_internal(temp_task_imp
);
754 * iterate the downstream importance inherits
755 * and capture the next task importance to boost for each
757 ipc_importance_inherit_t temp_inherit
;
759 queue_iterate(&task_imp
->iit_inherits
, temp_inherit
, ipc_importance_inherit_t
, iii_inheritance
) {
760 uint32_t assertcnt
= III_EXTERN(temp_inherit
);
762 temp_task_imp
= temp_inherit
->iii_to_task
;
763 assert(IIT_NULL
!= temp_task_imp
);
765 if (IIT_UPDATE_HOLD
== type
) {
766 /* if no undropped externcnts in the inherit, nothing to do */
767 if (0 == assertcnt
) {
768 assert(temp_inherit
->iii_donating
== FALSE
);
772 /* nothing to do if the inherit is already donating (forced donation) */
773 if (temp_inherit
->iii_donating
) {
777 /* mark it donating and contribute to the task externcnts */
778 temp_inherit
->iii_donating
= TRUE
;
779 temp_task_imp
->iit_externcnt
+= temp_inherit
->iii_externcnt
;
780 temp_task_imp
->iit_externdrop
+= temp_inherit
->iii_externdrop
;
783 /* if no contributing assertions, move on */
784 if (0 == assertcnt
) {
785 assert(temp_inherit
->iii_donating
== FALSE
);
789 /* nothing to do if the inherit is not donating */
790 if (!temp_inherit
->iii_donating
) {
794 /* mark it no longer donating */
795 temp_inherit
->iii_donating
= FALSE
;
797 /* remove the contribution the inherit made to the to-task */
798 assert(IIT_EXTERN(temp_task_imp
) >= III_EXTERN(temp_inherit
));
799 assert(temp_task_imp
->iit_externcnt
>= temp_inherit
->iii_externcnt
);
800 assert(temp_task_imp
->iit_externdrop
>= temp_inherit
->iii_externdrop
);
801 temp_task_imp
->iit_externcnt
-= temp_inherit
->iii_externcnt
;
802 temp_task_imp
->iit_externdrop
-= temp_inherit
->iii_externdrop
;
806 /* Adjust the task assertions and determine if an edge was crossed */
807 assert(ipc_importance_task_is_any_receiver_type(temp_task_imp
));
808 if (ipc_importance_task_check_transition(temp_task_imp
, type
, assertcnt
)) {
809 ipc_importance_task_reference(temp_task_imp
);
810 incr_ref_counter(temp_task_imp
->iit_elem
.iie_task_refs_added_transition
);
811 queue_enter(propagation
, temp_task_imp
, ipc_importance_task_t
, iit_props
);
817 * Routine: ipc_importance_task_process_updates
819 * Process the queue of task importances and apply the policy
820 * update called for. Only process tasks in the queue with an
821 * update timestamp less than the supplied max.
823 * Called and returns with importance locked.
824 * May drop importance lock and block temporarily.
827 ipc_importance_task_process_updates(
828 queue_t supplied_queue
,
830 uint64_t max_timestamp
)
832 ipc_importance_task_t task_imp
;
833 queue_head_t second_chance
;
834 queue_t queue
= supplied_queue
;
837 * This queue will hold the task's we couldn't trylock on first pass.
838 * By using a second (private) queue, we guarantee all tasks that get
839 * entered on this queue have a timestamp under the maximum.
841 queue_init(&second_chance
);
843 /* process any resulting policy updates */
845 while(!queue_empty(queue
)) {
847 struct task_pend_token pend_token
= {};
849 task_imp
= (ipc_importance_task_t
)queue_first(queue
);
850 assert(0 == task_imp
->iit_updatepolicy
);
851 assert(queue
== task_imp
->iit_updateq
);
853 /* if timestamp is too big, we're done */
854 if (task_imp
->iit_updatetime
> max_timestamp
) {
858 /* we were given a reference on each task in the queue */
860 /* remove it from the supplied queue */
861 queue_remove(queue
, task_imp
, ipc_importance_task_t
, iit_updates
);
862 task_imp
->iit_updateq
= NULL
;
864 target_task
= task_imp
->iit_task
;
866 /* Is it well on the way to exiting? */
867 if (TASK_NULL
== target_task
) {
868 ipc_importance_task_release_locked(task_imp
);
869 /* importance unlocked */
870 ipc_importance_lock();
874 /* Has the update been reversed on the hysteresis queue? */
875 if (0 < task_imp
->iit_assertcnt
&&
876 queue
== &ipc_importance_delayed_drop_queue
) {
877 ipc_importance_task_release_locked(task_imp
);
878 /* importance unlocked */
879 ipc_importance_lock();
884 * Can we get the task lock out-of-order?
885 * If not, stick this back on the second-chance queue.
887 if (!task_lock_try(target_task
)) {
888 boolean_t should_wait_lock
= (queue
== &second_chance
);
889 task_imp
->iit_updateq
= &second_chance
;
892 * If we're already processing second-chances on
893 * tasks, keep this task on the front of the queue.
894 * We will wait for the task lock before coming
895 * back and trying again, and we have a better
896 * chance of re-acquiring the lock if we come back
899 if (should_wait_lock
){
900 task_reference(target_task
);
901 queue_enter_first(&second_chance
, task_imp
,
902 ipc_importance_task_t
, iit_updates
);
904 queue_enter(&second_chance
, task_imp
,
905 ipc_importance_task_t
, iit_updates
);
907 ipc_importance_unlock();
909 if (should_wait_lock
) {
910 task_lock(target_task
);
911 task_unlock(target_task
);
912 task_deallocate(target_task
);
915 ipc_importance_lock();
919 /* is it going away? */
920 if (!target_task
->active
) {
921 task_unlock(target_task
);
922 ipc_importance_task_release_locked(task_imp
);
923 /* importance unlocked */
924 ipc_importance_lock();
928 /* take a task reference for while we don't have the importance lock */
929 task_reference(target_task
);
931 /* count the transition */
933 task_imp
->iit_transitions
++;
935 ipc_importance_unlock();
937 /* apply the policy adjust to the target task (while it is still locked) */
938 task_update_boost_locked(target_task
, boost
, &pend_token
);
940 /* complete the policy update with the task unlocked */
941 ipc_importance_task_release(task_imp
);
942 task_unlock(target_task
);
943 task_policy_update_complete_unlocked(target_task
, &pend_token
);
944 task_deallocate(target_task
);
946 ipc_importance_lock();
949 /* If there are tasks we couldn't update the first time, try again */
950 if (!queue_empty(&second_chance
)) {
951 queue
= &second_chance
;
958 * Routine: ipc_importance_task_delayed_drop_scan
960 * The thread call routine to scan the delayed drop queue,
961 * requesting all updates with a deadline up to the last target
962 * for the thread-call (which is DENAP_DROP_SKEW beyond the first
963 * thread's optimum delay).
964 * update to drop its boost.
969 ipc_importance_task_delayed_drop_scan(
973 ipc_importance_lock();
975 /* process all queued task drops with timestamps up to TARGET(first)+SKEW */
976 ipc_importance_task_process_updates(&ipc_importance_delayed_drop_queue
,
978 ipc_importance_delayed_drop_timestamp
);
980 /* importance lock may have been temporarily dropped */
982 /* If there are any entries left in the queue, re-arm the call here */
983 if (!queue_empty(&ipc_importance_delayed_drop_queue
)) {
984 ipc_importance_task_t task_imp
;
988 task_imp
= (ipc_importance_task_t
)queue_first(&ipc_importance_delayed_drop_queue
);
990 nanoseconds_to_absolutetime(DENAP_DROP_DELAY
, &deadline
);
991 deadline
+= task_imp
->iit_updatetime
;
992 ipc_importance_delayed_drop_timestamp
= deadline
;
994 nanoseconds_to_absolutetime(DENAP_DROP_LEEWAY
, &leeway
);
996 thread_call_enter_delayed_with_leeway(
997 ipc_importance_delayed_drop_call
,
1003 ipc_importance_delayed_drop_call_requested
= FALSE
;
1005 ipc_importance_unlock();
1009 * Routine: ipc_importance_task_delayed_drop
1011 * Queue the specified task importance for delayed policy
1012 * update to drop its boost.
1014 * Called with the importance lock held.
1017 ipc_importance_task_delayed_drop(ipc_importance_task_t task_imp
)
1019 uint64_t timestamp
= mach_absolute_time(); /* no mach_approximate_time() in kernel */
1021 assert(ipc_importance_delayed_drop_call
!= NULL
);
1024 * If still on an update queue from a previous change,
1025 * remove it first (and use that reference). Otherwise, take
1026 * a new reference for the delay drop update queue.
1028 if (NULL
!= task_imp
->iit_updateq
) {
1029 queue_remove(task_imp
->iit_updateq
, task_imp
,
1030 ipc_importance_task_t
, iit_updates
);
1032 ipc_importance_task_reference_internal(task_imp
);
1035 task_imp
->iit_updateq
= &ipc_importance_delayed_drop_queue
;
1036 task_imp
->iit_updatetime
= timestamp
;
1038 queue_enter(&ipc_importance_delayed_drop_queue
, task_imp
,
1039 ipc_importance_task_t
, iit_updates
);
1041 /* request the delayed thread-call if not already requested */
1042 if (!ipc_importance_delayed_drop_call_requested
) {
1046 nanoseconds_to_absolutetime(DENAP_DROP_DELAY
, &deadline
);
1047 deadline
+= task_imp
->iit_updatetime
;
1048 ipc_importance_delayed_drop_timestamp
= deadline
;
1050 nanoseconds_to_absolutetime(DENAP_DROP_LEEWAY
, &leeway
);
1052 ipc_importance_delayed_drop_call_requested
= TRUE
;
1053 thread_call_enter_delayed_with_leeway(
1054 ipc_importance_delayed_drop_call
,
1064 * Routine: ipc_importance_task_propagate_assertion_locked
1066 * Propagate the importance transition type to every item
1067 * If this causes a boost to be applied, determine if that
1068 * boost should propagate downstream.
1070 * Called with the importance lock held.
1073 ipc_importance_task_propagate_assertion_locked(
1074 ipc_importance_task_t task_imp
,
1075 iit_update_type_t type
,
1076 boolean_t update_task_imp
)
1078 boolean_t boost
= (IIT_UPDATE_HOLD
== type
);
1079 ipc_importance_task_t temp_task_imp
;
1080 queue_head_t propagate
;
1081 queue_head_t updates
;
1083 queue_init(&updates
);
1084 queue_init(&propagate
);
1086 ipc_importance_assert_held();
1089 * If we're going to update the policy for the provided task,
1090 * enqueue it on the propagate queue itself. Otherwise, only
1091 * enqueue downstream things.
1093 if (update_task_imp
) {
1094 ipc_importance_task_reference(task_imp
);
1095 incr_ref_counter(task_imp
->iit_elem
.iie_task_refs_added_transition
);
1096 queue_enter(&propagate
, task_imp
, ipc_importance_task_t
, iit_props
);
1098 ipc_importance_task_propagate_helper(task_imp
, type
, &propagate
);
1102 * for each item on the propagation list, propagate any change downstream,
1103 * adding new tasks to propagate further if they transistioned as well.
1105 while (!queue_empty(&propagate
)) {
1106 boolean_t need_update
;
1108 queue_remove_first(&propagate
, temp_task_imp
, ipc_importance_task_t
, iit_props
);
1109 /* hold a reference on temp_task_imp */
1111 assert(IIT_NULL
!= temp_task_imp
);
1113 /* only propagate for receivers not already marked as a donor */
1114 if (!ipc_importance_task_is_marked_donor(temp_task_imp
) &&
1115 ipc_importance_task_is_marked_receiver(temp_task_imp
)) {
1116 ipc_importance_task_propagate_helper(temp_task_imp
, type
, &propagate
);
1119 /* if we have a policy update to apply, enqueue a reference for later processing */
1120 need_update
= (0 != temp_task_imp
->iit_updatepolicy
);
1121 temp_task_imp
->iit_updatepolicy
= 0;
1122 if (need_update
&& TASK_NULL
!= temp_task_imp
->iit_task
) {
1123 if (NULL
== temp_task_imp
->iit_updateq
) {
1126 * If a downstream task that needs an update is subjects to AppNap,
1127 * drop boosts according to the delay hysteresis. Otherwise,
1128 * immediate update it.
1130 if (!boost
&& temp_task_imp
!= task_imp
&&
1131 ipc_importance_delayed_drop_call
!= NULL
&&
1132 ipc_importance_task_is_marked_denap_receiver(temp_task_imp
)) {
1133 ipc_importance_task_delayed_drop(temp_task_imp
);
1135 temp_task_imp
->iit_updatetime
= 0;
1136 temp_task_imp
->iit_updateq
= &updates
;
1137 ipc_importance_task_reference_internal(temp_task_imp
);
1139 queue_enter(&updates
, temp_task_imp
,
1140 ipc_importance_task_t
, iit_updates
);
1142 queue_enter_first(&updates
, temp_task_imp
,
1143 ipc_importance_task_t
, iit_updates
);
1147 /* Must already be on the AppNap hysteresis queue */
1148 assert(ipc_importance_delayed_drop_call
!= NULL
);
1149 assert(ipc_importance_task_is_marked_denap_receiver(temp_task_imp
));
1153 ipc_importance_task_release_internal(temp_task_imp
);
1156 /* apply updates to task (may drop importance lock) */
1157 if (!queue_empty(&updates
)) {
1158 ipc_importance_task_process_updates(&updates
, boost
, 0);
1163 * Routine: ipc_importance_task_hold_internal_assertion_locked
1165 * Increment the assertion count on the task importance.
1166 * If this results in a boost state change in that task,
1167 * prepare to update task policy for this task AND, if
1168 * if not just waking out of App Nap, all down-stream
1169 * tasks that have a similar transition through inheriting
1172 * importance locked on entry and exit.
1173 * May temporarily drop importance lock and block.
1175 static kern_return_t
1176 ipc_importance_task_hold_internal_assertion_locked(ipc_importance_task_t task_imp
, uint32_t count
)
1178 if (ipc_importance_task_check_transition(task_imp
, IIT_UPDATE_HOLD
, count
)) {
1179 ipc_importance_task_propagate_assertion_locked(task_imp
, IIT_UPDATE_HOLD
, TRUE
);
1181 return KERN_SUCCESS
;
1185 * Routine: ipc_importance_task_drop_internal_assertion_locked
1187 * Decrement the assertion count on the task importance.
1188 * If this results in a boost state change in that task,
1189 * prepare to update task policy for this task AND, if
1190 * if not just waking out of App Nap, all down-stream
1191 * tasks that have a similar transition through inheriting
1194 * importance locked on entry and exit.
1195 * May temporarily drop importance lock and block.
1197 static kern_return_t
1198 ipc_importance_task_drop_internal_assertion_locked(ipc_importance_task_t task_imp
, uint32_t count
)
1200 if (ipc_importance_task_check_transition(task_imp
, IIT_UPDATE_DROP
, count
)) {
1201 ipc_importance_task_propagate_assertion_locked(task_imp
, IIT_UPDATE_DROP
, TRUE
);
1203 return KERN_SUCCESS
;
1207 * Routine: ipc_importance_task_hold_internal_assertion
1209 * Increment the assertion count on the task importance.
1210 * If this results in a 0->1 change in that count,
1211 * prepare to update task policy for this task AND
1212 * (potentially) all down-stream tasks that have a
1213 * similar transition through inheriting this update.
1216 * May block after dropping importance lock.
1219 ipc_importance_task_hold_internal_assertion(ipc_importance_task_t task_imp
, uint32_t count
)
1221 int ret
= KERN_SUCCESS
;
1223 if (ipc_importance_task_is_any_receiver_type(task_imp
)) {
1224 ipc_importance_lock();
1225 ret
= ipc_importance_task_hold_internal_assertion_locked(task_imp
, count
);
1226 ipc_importance_unlock();
1232 * Routine: ipc_importance_task_drop_internal_assertion
1234 * Decrement the assertion count on the task importance.
1235 * If this results in a X->0 change in that count,
1236 * prepare to update task policy for this task AND
1237 * all down-stream tasks that have a similar transition
1238 * through inheriting this drop update.
1240 * Nothing locked on entry.
1241 * May block after dropping importance lock.
1244 ipc_importance_task_drop_internal_assertion(ipc_importance_task_t task_imp
, uint32_t count
)
1246 kern_return_t ret
= KERN_SUCCESS
;
1248 if (ipc_importance_task_is_any_receiver_type(task_imp
)) {
1249 ipc_importance_lock();
1250 ret
= ipc_importance_task_drop_internal_assertion_locked(task_imp
, count
);
1251 ipc_importance_unlock();
1257 * Routine: ipc_importance_task_hold_file_lock_assertion
1259 * Increment the file lock assertion count on the task importance.
1260 * If this results in a 0->1 change in that count,
1261 * prepare to update task policy for this task AND
1262 * (potentially) all down-stream tasks that have a
1263 * similar transition through inheriting this update.
1266 * May block after dropping importance lock.
1269 ipc_importance_task_hold_file_lock_assertion(ipc_importance_task_t task_imp
, uint32_t count
)
1271 kern_return_t ret
= KERN_SUCCESS
;
1273 if (ipc_importance_task_is_any_receiver_type(task_imp
)) {
1274 ipc_importance_lock();
1275 ret
= ipc_importance_task_hold_internal_assertion_locked(task_imp
, count
);
1276 if (KERN_SUCCESS
== ret
) {
1277 task_imp
->iit_filelocks
+= count
;
1279 ipc_importance_unlock();
1285 * Routine: ipc_importance_task_drop_file_lock_assertion
1287 * Decrement the assertion count on the task importance.
1288 * If this results in a X->0 change in that count,
1289 * prepare to update task policy for this task AND
1290 * all down-stream tasks that have a similar transition
1291 * through inheriting this drop update.
1293 * Nothing locked on entry.
1294 * May block after dropping importance lock.
1297 ipc_importance_task_drop_file_lock_assertion(ipc_importance_task_t task_imp
, uint32_t count
)
1299 kern_return_t ret
= KERN_SUCCESS
;
1301 if (ipc_importance_task_is_any_receiver_type(task_imp
)) {
1302 ipc_importance_lock();
1303 if (count
<= task_imp
->iit_filelocks
) {
1304 task_imp
->iit_filelocks
-= count
;
1305 ret
= ipc_importance_task_drop_internal_assertion_locked(task_imp
, count
);
1307 ret
= KERN_INVALID_ARGUMENT
;
1309 ipc_importance_unlock();
1315 * Routine: ipc_importance_task_hold_legacy_external_assertion
1317 * Increment the external assertion count on the task importance.
1318 * This cannot result in an 0->1 transition, as the caller must
1319 * already hold an external boost.
1321 * Nothing locked on entry.
1322 * May block after dropping importance lock.
1323 * A queue of task importance structures is returned
1324 * by ipc_importance_task_hold_assertion_locked(). Each
1325 * needs to be updated (outside the importance lock hold).
1328 ipc_importance_task_hold_legacy_external_assertion(ipc_importance_task_t task_imp
, uint32_t count
)
1331 uint32_t target_assertcnt
;
1332 uint32_t target_externcnt
;
1333 uint32_t target_legacycnt
;
1337 ipc_importance_lock();
1338 target_task
= task_imp
->iit_task
;
1340 #if IMPORTANCE_TRACE
1341 int target_pid
= task_pid(target_task
);
1343 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, (IMP_HOLD
| TASK_POLICY_EXTERNAL
))) | DBG_FUNC_START
,
1344 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_LEGACY_EXTERN(task_imp
), 0);
1347 if (IIT_LEGACY_EXTERN(task_imp
) == 0) {
1348 /* Only allowed to take a new boost assertion when holding an external boost */
1349 /* save data for diagnostic printf below */
1350 target_assertcnt
= task_imp
->iit_assertcnt
;
1351 target_externcnt
= IIT_EXTERN(task_imp
);
1352 target_legacycnt
= IIT_LEGACY_EXTERN(task_imp
);
1356 assert(ipc_importance_task_is_any_receiver_type(task_imp
));
1357 assert(0 < task_imp
->iit_assertcnt
);
1358 assert(0 < IIT_EXTERN(task_imp
));
1359 task_imp
->iit_assertcnt
+= count
;
1360 task_imp
->iit_externcnt
+= count
;
1361 task_imp
->iit_legacy_externcnt
+= count
;
1364 ipc_importance_unlock();
1366 #if IMPORTANCE_TRACE
1367 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, (IMP_HOLD
| TASK_POLICY_EXTERNAL
))) | DBG_FUNC_END
,
1368 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_LEGACY_EXTERN(task_imp
), 0);
1369 // This covers the legacy case where a task takes an extra boost.
1370 DTRACE_BOOST5(receive_boost
, task_t
, target_task
, int, target_pid
, int, proc_selfpid(), int, count
, int, task_imp
->iit_assertcnt
);
1373 if (KERN_FAILURE
== ret
&& target_task
!= TASK_NULL
) {
1374 printf("BUG in process %s[%d]: "
1375 "attempt to acquire an additional legacy external boost assertion without holding an existing legacy external assertion. "
1376 "(%d total, %d external, %d legacy-external)\n",
1377 proc_name_address(target_task
->bsd_info
), task_pid(target_task
),
1378 target_assertcnt
, target_externcnt
, target_legacycnt
);
1385 * Routine: ipc_importance_task_drop_legacy_external_assertion
1387 * Drop the legacy external assertion count on the task and
1388 * reflect that change to total external assertion count and
1389 * then onto the internal importance count.
1391 * If this results in a X->0 change in the internal,
1392 * count, prepare to update task policy for this task AND
1393 * all down-stream tasks that have a similar transition
1394 * through inheriting this update.
1396 * Nothing locked on entry.
1399 ipc_importance_task_drop_legacy_external_assertion(ipc_importance_task_t task_imp
, uint32_t count
)
1401 int ret
= KERN_SUCCESS
;
1403 uint32_t target_assertcnt
;
1404 uint32_t target_externcnt
;
1405 uint32_t target_legacycnt
;
1408 return KERN_INVALID_ARGUMENT
;
1411 ipc_importance_lock();
1412 target_task
= task_imp
->iit_task
;
1414 #if IMPORTANCE_TRACE
1415 int target_pid
= task_pid(target_task
);
1417 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, (IMP_DROP
| TASK_POLICY_EXTERNAL
))) | DBG_FUNC_START
,
1418 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_LEGACY_EXTERN(task_imp
), 0);
1421 if (count
> IIT_LEGACY_EXTERN(task_imp
)) {
1422 /* Process over-released its boost count - save data for diagnostic printf */
1423 /* TODO: If count > 1, we should clear out as many external assertions as there are left. */
1424 target_assertcnt
= task_imp
->iit_assertcnt
;
1425 target_externcnt
= IIT_EXTERN(task_imp
);
1426 target_legacycnt
= IIT_LEGACY_EXTERN(task_imp
);
1430 * decrement legacy external count from the top level and reflect
1431 * into internal for this and all subsequent updates.
1433 assert(ipc_importance_task_is_any_receiver_type(task_imp
));
1434 assert(IIT_EXTERN(task_imp
) >= count
);
1436 task_imp
->iit_legacy_externdrop
+= count
;
1437 task_imp
->iit_externdrop
+= count
;
1439 /* reset extern counters (if appropriate) */
1440 if (IIT_LEGACY_EXTERN(task_imp
) == 0) {
1441 if (IIT_EXTERN(task_imp
) != 0) {
1442 task_imp
->iit_externcnt
-= task_imp
->iit_legacy_externcnt
;
1443 task_imp
->iit_externdrop
-= task_imp
->iit_legacy_externdrop
;
1445 task_imp
->iit_externcnt
= 0;
1446 task_imp
->iit_externdrop
= 0;
1448 task_imp
->iit_legacy_externcnt
= 0;
1449 task_imp
->iit_legacy_externdrop
= 0;
1452 /* reflect the drop to the internal assertion count (and effect any importance change) */
1453 if (ipc_importance_task_check_transition(task_imp
, IIT_UPDATE_DROP
, count
)) {
1454 ipc_importance_task_propagate_assertion_locked(task_imp
, IIT_UPDATE_DROP
, TRUE
);
1459 #if IMPORTANCE_TRACE
1460 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, (IMP_DROP
| TASK_POLICY_EXTERNAL
))) | DBG_FUNC_END
,
1461 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_LEGACY_EXTERN(task_imp
), 0);
1464 ipc_importance_unlock();
1466 /* delayed printf for user-supplied data failures */
1467 if (KERN_FAILURE
== ret
&& TASK_NULL
!= target_task
) {
1468 printf("BUG in process %s[%d]: over-released legacy external boost assertions (%d total, %d external, %d legacy-external)\n",
1469 proc_name_address(target_task
->bsd_info
), task_pid(target_task
),
1470 target_assertcnt
, target_externcnt
, target_legacycnt
);
1478 /* Transfer an assertion to legacy userspace responsibility */
1479 static kern_return_t
1480 ipc_importance_task_externalize_legacy_assertion(ipc_importance_task_t task_imp
, uint32_t count
, __unused
int sender_pid
)
1484 assert(IIT_NULL
!= task_imp
);
1485 target_task
= task_imp
->iit_task
;
1487 if (TASK_NULL
== target_task
||
1488 !ipc_importance_task_is_any_receiver_type(task_imp
)) {
1489 return KERN_FAILURE
;
1492 #if IMPORTANCE_TRACE
1493 int target_pid
= task_pid(target_task
);
1495 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, IMP_EXTERN
)) | DBG_FUNC_START
,
1496 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_EXTERN(task_imp
), 0);
1499 ipc_importance_lock();
1500 /* assert(task_imp->iit_assertcnt >= IIT_EXTERN(task_imp) + count); */
1501 assert(IIT_EXTERN(task_imp
) >= IIT_LEGACY_EXTERN(task_imp
));
1502 task_imp
->iit_legacy_externcnt
+= count
;
1503 task_imp
->iit_externcnt
+= count
;
1504 ipc_importance_unlock();
1506 #if IMPORTANCE_TRACE
1507 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_ASSERTION
, IMP_EXTERN
)) | DBG_FUNC_END
,
1508 proc_selfpid(), target_pid
, task_imp
->iit_assertcnt
, IIT_LEGACY_EXTERN(task_imp
), 0);
1509 // This is the legacy boosting path
1510 DTRACE_BOOST5(receive_boost
, task_t
, target_task
, int, target_pid
, int, sender_pid
, int, count
, int, IIT_LEGACY_EXTERN(task_imp
));
1511 #endif /* IMPORTANCE_TRACE */
1513 return(KERN_SUCCESS
);
1517 * Routine: ipc_importance_task_update_live_donor
1519 * Read the live donor status and update the live_donor bit/propagate the change in importance.
1521 * Nothing locked on entrance, nothing locked on exit.
1523 * TODO: Need tracepoints around this function...
1526 ipc_importance_task_update_live_donor(ipc_importance_task_t task_imp
)
1528 uint32_t task_live_donor
;
1529 boolean_t before_donor
;
1530 boolean_t after_donor
;
1533 assert(task_imp
!= NULL
);
1536 * Nothing to do if the task is not marked as expecting
1537 * live donor updates.
1539 if (!ipc_importance_task_is_marked_live_donor(task_imp
)) {
1543 ipc_importance_lock();
1545 /* If the task got disconnected on the way here, no use (or ability) adjusting live donor status */
1546 target_task
= task_imp
->iit_task
;
1547 if (TASK_NULL
== target_task
) {
1548 ipc_importance_unlock();
1551 before_donor
= ipc_importance_task_is_marked_donor(task_imp
);
1553 /* snapshot task live donor status - may change, but another call will accompany the change */
1554 task_live_donor
= target_task
->effective_policy
.tep_live_donor
;
1556 #if IMPORTANCE_TRACE
1557 int target_pid
= task_pid(target_task
);
1559 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1560 (IMPORTANCE_CODE(IMP_DONOR_CHANGE
, IMP_DONOR_UPDATE_LIVE_DONOR_STATE
)) | DBG_FUNC_START
,
1561 target_pid
, task_imp
->iit_donor
, task_live_donor
, before_donor
, 0);
1564 /* update the task importance live donor status based on the task's value */
1565 task_imp
->iit_donor
= task_live_donor
;
1567 after_donor
= ipc_importance_task_is_marked_donor(task_imp
);
1569 /* Has the effectiveness of being a donor changed as a result of this update? */
1570 if (before_donor
!= after_donor
) {
1571 iit_update_type_t type
;
1573 /* propagate assertions without updating the current task policy (already handled) */
1574 if (0 == before_donor
) {
1575 task_imp
->iit_transitions
++;
1576 type
= IIT_UPDATE_HOLD
;
1578 type
= IIT_UPDATE_DROP
;
1580 ipc_importance_task_propagate_assertion_locked(task_imp
, type
, FALSE
);
1583 #if IMPORTANCE_TRACE
1584 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1585 (IMPORTANCE_CODE(IMP_DONOR_CHANGE
, IMP_DONOR_UPDATE_LIVE_DONOR_STATE
)) | DBG_FUNC_END
,
1586 target_pid
, task_imp
->iit_donor
, task_live_donor
, after_donor
, 0);
1589 ipc_importance_unlock();
1594 * Routine: ipc_importance_task_mark_donor
1596 * Set the task importance donor flag.
1598 * Nothing locked on entrance, nothing locked on exit.
1600 * This is only called while the task is being constructed,
1601 * so no need to update task policy or propagate downstream.
1604 ipc_importance_task_mark_donor(ipc_importance_task_t task_imp
, boolean_t donating
)
1606 assert(task_imp
!= NULL
);
1608 ipc_importance_lock();
1610 int old_donor
= task_imp
->iit_donor
;
1612 task_imp
->iit_donor
= (donating
? 1 : 0);
1614 if (task_imp
->iit_donor
> 0 && old_donor
== 0)
1615 task_imp
->iit_transitions
++;
1617 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
,
1618 (IMPORTANCE_CODE(IMP_DONOR_CHANGE
, IMP_DONOR_INIT_DONOR_STATE
)) | DBG_FUNC_NONE
,
1619 task_pid(task_imp
->iit_task
), donating
,
1620 old_donor
, task_imp
->iit_donor
, 0);
1622 ipc_importance_unlock();
1626 * Routine: ipc_importance_task_marked_donor
1628 * Query the donor flag for the given task importance.
1630 * May be called without taking the importance lock.
1631 * In that case, donor status can change so you must
1632 * check only once for each donation event.
1635 ipc_importance_task_is_marked_donor(ipc_importance_task_t task_imp
)
1637 if (IIT_NULL
== task_imp
) {
1640 return (0 != task_imp
->iit_donor
);
1644 * Routine: ipc_importance_task_mark_live_donor
1646 * Indicate that the task is eligible for live donor updates.
1648 * Nothing locked on entrance, nothing locked on exit.
1650 * This is only called while the task is being constructed.
1653 ipc_importance_task_mark_live_donor(ipc_importance_task_t task_imp
, boolean_t live_donating
)
1655 assert(task_imp
!= NULL
);
1657 ipc_importance_lock();
1658 task_imp
->iit_live_donor
= (live_donating
? 1 : 0);
1659 ipc_importance_unlock();
1663 * Routine: ipc_importance_task_marked_live_donor
1665 * Query the live donor and donor flags for the given task importance.
1667 * May be called without taking the importance lock.
1668 * In that case, donor status can change so you must
1669 * check only once for each donation event.
1672 ipc_importance_task_is_marked_live_donor(ipc_importance_task_t task_imp
)
1674 if (IIT_NULL
== task_imp
) {
1677 return (0 != task_imp
->iit_live_donor
);
1681 * Routine: ipc_importance_task_is_donor
1683 * Query the full donor status for the given task importance.
1685 * May be called without taking the importance lock.
1686 * In that case, donor status can change so you must
1687 * check only once for each donation event.
1690 ipc_importance_task_is_donor(ipc_importance_task_t task_imp
)
1692 if (IIT_NULL
== task_imp
) {
1695 return (ipc_importance_task_is_marked_donor(task_imp
) ||
1696 (ipc_importance_task_is_marked_receiver(task_imp
) &&
1697 task_imp
->iit_assertcnt
> 0));
1701 * Routine: ipc_importance_task_is_never_donor
1703 * Query if a given task can ever donate importance.
1705 * May be called without taking the importance lock.
1706 * Condition is permanent for a give task.
1709 ipc_importance_task_is_never_donor(ipc_importance_task_t task_imp
)
1711 if (IIT_NULL
== task_imp
) {
1714 return (!ipc_importance_task_is_marked_donor(task_imp
) &&
1715 !ipc_importance_task_is_marked_live_donor(task_imp
) &&
1716 !ipc_importance_task_is_marked_receiver(task_imp
));
1720 * Routine: ipc_importance_task_mark_receiver
1722 * Update the task importance receiver flag.
1724 * Nothing locked on entrance, nothing locked on exit.
1725 * This can only be invoked before the task is discoverable,
1726 * so no worries about atomicity(?)
1729 ipc_importance_task_mark_receiver(ipc_importance_task_t task_imp
, boolean_t receiving
)
1731 assert(task_imp
!= NULL
);
1733 ipc_importance_lock();
1735 assert(task_imp
->iit_assertcnt
== 0);
1736 assert(task_imp
->iit_externcnt
== 0);
1737 assert(task_imp
->iit_externdrop
== 0);
1738 assert(task_imp
->iit_denap
== 0);
1739 task_imp
->iit_receiver
= 1; /* task can receive importance boost */
1740 } else if (task_imp
->iit_receiver
) {
1741 assert(task_imp
->iit_denap
== 0);
1742 if (task_imp
->iit_assertcnt
!= 0 || IIT_EXTERN(task_imp
) != 0) {
1743 panic("disabling imp_receiver on task with pending importance boosts!");
1745 task_imp
->iit_receiver
= 0;
1747 ipc_importance_unlock();
1752 * Routine: ipc_importance_task_marked_receiver
1754 * Query the receiver flag for the given task importance.
1756 * May be called without taking the importance lock as
1757 * the importance flag can never change after task init.
1760 ipc_importance_task_is_marked_receiver(ipc_importance_task_t task_imp
)
1762 return (IIT_NULL
!= task_imp
&& 0 != task_imp
->iit_receiver
);
1767 * Routine: ipc_importance_task_mark_denap_receiver
1769 * Update the task importance de-nap receiver flag.
1771 * Nothing locked on entrance, nothing locked on exit.
1772 * This can only be invoked before the task is discoverable,
1773 * so no worries about atomicity(?)
1776 ipc_importance_task_mark_denap_receiver(ipc_importance_task_t task_imp
, boolean_t denap
)
1778 assert(task_imp
!= NULL
);
1780 ipc_importance_lock();
1782 assert(task_imp
->iit_assertcnt
== 0);
1783 assert(task_imp
->iit_externcnt
== 0);
1784 assert(task_imp
->iit_receiver
== 0);
1785 task_imp
->iit_denap
= 1; /* task can receive de-nap boost */
1786 } else if (task_imp
->iit_denap
) {
1787 assert(task_imp
->iit_receiver
== 0);
1788 if (0 < task_imp
->iit_assertcnt
|| 0 < IIT_EXTERN(task_imp
)) {
1789 panic("disabling de-nap on task with pending de-nap boosts!");
1791 task_imp
->iit_denap
= 0;
1793 ipc_importance_unlock();
1798 * Routine: ipc_importance_task_marked_denap_receiver
1800 * Query the de-nap receiver flag for the given task importance.
1802 * May be called without taking the importance lock as
1803 * the de-nap flag can never change after task init.
1806 ipc_importance_task_is_marked_denap_receiver(ipc_importance_task_t task_imp
)
1808 return (IIT_NULL
!= task_imp
&& 0 != task_imp
->iit_denap
);
1812 * Routine: ipc_importance_task_is_denap_receiver
1814 * Query the full de-nap receiver status for the given task importance.
1815 * For now, that is simply whether the receiver flag is set.
1817 * May be called without taking the importance lock as
1818 * the de-nap receiver flag can never change after task init.
1821 ipc_importance_task_is_denap_receiver(ipc_importance_task_t task_imp
)
1823 return (ipc_importance_task_is_marked_denap_receiver(task_imp
));
1827 * Routine: ipc_importance_task_is_any_receiver_type
1829 * Query if the task is marked to receive boosts - either
1830 * importance or denap.
1832 * May be called without taking the importance lock as both
1833 * the importance and de-nap receiver flags can never change
1837 ipc_importance_task_is_any_receiver_type(ipc_importance_task_t task_imp
)
1839 return (ipc_importance_task_is_marked_receiver(task_imp
) ||
1840 ipc_importance_task_is_marked_denap_receiver(task_imp
));
1843 #if 0 /* currently unused */
1846 * Routine: ipc_importance_inherit_reference
1848 * Add a reference to the inherit importance element.
1850 * Caller most hold a reference on the inherit element.
1853 ipc_importance_inherit_reference(ipc_importance_inherit_t inherit
)
1855 ipc_importance_reference(&inherit
->iii_elem
);
1857 #endif /* currently unused */
1860 * Routine: ipc_importance_inherit_release_locked
1862 * Release a reference on an inherit importance attribute value,
1863 * unlinking and deallocating the attribute if the last reference.
1865 * Entered with importance lock held, leaves with it unlocked.
1868 ipc_importance_inherit_release_locked(ipc_importance_inherit_t inherit
)
1870 ipc_importance_release_locked(&inherit
->iii_elem
);
1873 #if 0 /* currently unused */
1875 * Routine: ipc_importance_inherit_release
1877 * Release a reference on an inherit importance attribute value,
1878 * unlinking and deallocating the attribute if the last reference.
1880 * nothing locked on entrance, nothing locked on exit.
1884 ipc_importance_inherit_release(ipc_importance_inherit_t inherit
)
1886 if (III_NULL
!= inherit
)
1887 ipc_importance_release(&inherit
->iii_elem
);
1889 #endif /* 0 currently unused */
1892 * Routine: ipc_importance_for_task
1894 * Create a reference for the specified task's base importance
1895 * element. If the base importance element doesn't exist, make it and
1896 * bind it to the active task. If the task is inactive, there isn't
1897 * any need to return a new reference.
1899 * If made is true, a "made" reference is returned (for donating to
1900 * the voucher system). Otherwise an internal reference is returned.
1902 * Nothing locked on entry. May block.
1904 ipc_importance_task_t
1905 ipc_importance_for_task(task_t task
, boolean_t made
)
1907 ipc_importance_task_t task_elem
;
1908 boolean_t first_pass
= TRUE
;
1910 assert(TASK_NULL
!= task
);
1913 /* No use returning anything for inactive task */
1917 ipc_importance_lock();
1918 task_elem
= task
->task_imp_base
;
1919 if (IIT_NULL
!= task_elem
) {
1920 /* Add a made reference (borrowing active task ref to do it) */
1922 if (0 == task_elem
->iit_made
++) {
1923 assert(IIT_REFS_MAX
> IIT_REFS(task_elem
));
1924 ipc_importance_task_reference_internal(task_elem
);
1927 assert(IIT_REFS_MAX
> IIT_REFS(task_elem
));
1928 ipc_importance_task_reference_internal(task_elem
);
1930 ipc_importance_unlock();
1933 ipc_importance_unlock();
1939 /* Need to make one - may race with others (be prepared to drop) */
1940 task_elem
= (ipc_importance_task_t
)zalloc(ipc_importance_task_zone
);
1941 if (IIT_NULL
== task_elem
)
1944 task_elem
->iit_bits
= IIE_TYPE_TASK
| 2; /* one for task, one for return/made */
1945 task_elem
->iit_made
= (made
) ? 1 : 0;
1946 task_elem
->iit_task
= task
; /* take actual ref when we're sure */
1947 task_elem
->iit_updateq
= NULL
;
1948 task_elem
->iit_receiver
= 0;
1949 task_elem
->iit_denap
= 0;
1950 task_elem
->iit_donor
= 0;
1951 task_elem
->iit_live_donor
= 0;
1952 task_elem
->iit_updatepolicy
= 0;
1953 task_elem
->iit_reserved
= 0;
1954 task_elem
->iit_filelocks
= 0;
1955 task_elem
->iit_updatetime
= 0;
1956 task_elem
->iit_transitions
= 0;
1957 task_elem
->iit_assertcnt
= 0;
1958 task_elem
->iit_externcnt
= 0;
1959 task_elem
->iit_externdrop
= 0;
1960 task_elem
->iit_legacy_externcnt
= 0;
1961 task_elem
->iit_legacy_externdrop
= 0;
1963 ipc_importance_counter_init(&task_elem
->iit_elem
);
1965 queue_init(&task_elem
->iit_kmsgs
);
1966 queue_init(&task_elem
->iit_inherits
);
1968 ipc_importance_lock();
1969 if (!task
->active
) {
1970 ipc_importance_unlock();
1971 zfree(ipc_importance_task_zone
, task_elem
);
1975 /* did we lose the race? */
1976 if (IIT_NULL
!= task
->task_imp_base
) {
1977 ipc_importance_unlock();
1978 zfree(ipc_importance_task_zone
, task_elem
);
1982 /* we won the race */
1983 task
->task_imp_base
= task_elem
;
1984 task_reference(task
);
1985 #if DEVELOPMENT || DEBUG
1986 queue_enter(&global_iit_alloc_queue
, task_elem
, ipc_importance_task_t
, iit_allocation
);
1987 task_importance_update_owner_info(task
);
1989 ipc_importance_unlock();
1994 #if DEVELOPMENT || DEBUG
1995 void task_importance_update_owner_info(task_t task
) {
1997 if (task
!= TASK_NULL
&& task
->task_imp_base
!= IIT_NULL
) {
1998 ipc_importance_task_t task_elem
= task
->task_imp_base
;
2000 task_elem
->iit_bsd_pid
= task_pid(task
);
2001 if (task
->bsd_info
) {
2002 strncpy(&task_elem
->iit_procname
[0], proc_name_address(task
->bsd_info
), 16);
2003 task_elem
->iit_procname
[16] = '\0';
2005 strncpy(&task_elem
->iit_procname
[0], "unknown", 16);
2012 * Routine: ipc_importance_reset_locked
2014 * Reset a task's IPC importance (the task is going away or exec'ing)
2016 * Remove the donor bit and legacy externalized assertions from the
2017 * current task importance and see if that wipes out downstream donations.
2019 * importance lock held.
2023 ipc_importance_reset_locked(ipc_importance_task_t task_imp
, boolean_t donor
)
2025 boolean_t before_donor
, after_donor
;
2027 /* remove the donor bit, live-donor bit and externalized boosts */
2028 before_donor
= ipc_importance_task_is_donor(task_imp
);
2030 task_imp
->iit_donor
= 0;
2032 assert(IIT_LEGACY_EXTERN(task_imp
) <= IIT_EXTERN(task_imp
));
2033 assert(task_imp
->iit_legacy_externcnt
<= task_imp
->iit_externcnt
);
2034 assert(task_imp
->iit_legacy_externdrop
<= task_imp
->iit_externdrop
);
2035 task_imp
->iit_externcnt
-= task_imp
->iit_legacy_externcnt
;
2036 task_imp
->iit_externdrop
-= task_imp
->iit_legacy_externdrop
;
2038 /* assert(IIT_LEGACY_EXTERN(task_imp) <= task_imp->iit_assertcnt); */
2039 if (IIT_EXTERN(task_imp
) < task_imp
->iit_assertcnt
) {
2040 task_imp
->iit_assertcnt
-= IIT_LEGACY_EXTERN(task_imp
);
2042 task_imp
->iit_assertcnt
= IIT_EXTERN(task_imp
);
2044 task_imp
->iit_legacy_externcnt
= 0;
2045 task_imp
->iit_legacy_externdrop
= 0;
2046 after_donor
= ipc_importance_task_is_donor(task_imp
);
2048 #if DEVELOPMENT || DEBUG
2049 if (task_imp
->iit_assertcnt
> 0 && task_imp
->iit_live_donor
) {
2050 printf("Live donor task %s[%d] still has %d importance assertions after reset\n",
2051 task_imp
->iit_procname
, task_imp
->iit_bsd_pid
, task_imp
->iit_assertcnt
);
2055 /* propagate a downstream drop if there was a change in donor status */
2056 if (after_donor
!= before_donor
) {
2057 ipc_importance_task_propagate_assertion_locked(task_imp
, IIT_UPDATE_DROP
, FALSE
);
2062 * Routine: ipc_importance_reset
2064 * Reset a task's IPC importance
2066 * The task is being reset, although staying around. Arrange to have the
2067 * external state of the task reset from the importance.
2069 * importance lock not held.
2073 ipc_importance_reset(ipc_importance_task_t task_imp
, boolean_t donor
)
2075 if (IIT_NULL
== task_imp
) {
2078 ipc_importance_lock();
2079 ipc_importance_reset_locked(task_imp
, donor
);
2080 ipc_importance_unlock();
2084 * Routine: ipc_importance_disconnect_task
2086 * Disconnect a task from its importance.
2088 * Clear the task pointer from the importance and drop the
2089 * reference the task held on the importance object. Before
2090 * doing that, reset the effects the current task holds on
2091 * the importance and see if that wipes out downstream donations.
2093 * We allow the upstream boosts to continue to affect downstream
2094 * even though the local task is being effectively pulled from
2100 ipc_importance_disconnect_task(task_t task
)
2102 ipc_importance_task_t task_imp
;
2105 ipc_importance_lock();
2106 task_imp
= task
->task_imp_base
;
2108 /* did somebody beat us to it? */
2109 if (IIT_NULL
== task_imp
) {
2110 ipc_importance_unlock();
2115 /* disconnect the task from this importance */
2116 assert(task_imp
->iit_task
== task
);
2117 task_imp
->iit_task
= TASK_NULL
;
2118 task
->task_imp_base
= IIT_NULL
;
2121 /* reset the effects the current task hold on the importance */
2122 ipc_importance_reset_locked(task_imp
, TRUE
);
2124 ipc_importance_task_release_locked(task_imp
);
2125 /* importance unlocked */
2127 /* deallocate the task now that the importance is unlocked */
2128 task_deallocate(task
);
2132 * Routine: ipc_importance_exec_switch_task
2134 * Switch importance task base from old task to new task in exec.
2136 * Create an ipc importance linkage from old task to new task,
2137 * once the linkage is created, switch the importance task base
2138 * from old task to new task. After the switch, the linkage will
2139 * represent importance linkage from new task to old task with
2140 * watch port importance inheritance linked to new task.
2143 * Returns a reference on importance inherit.
2145 ipc_importance_inherit_t
2146 ipc_importance_exec_switch_task(
2150 ipc_importance_inherit_t inherit
= III_NULL
;
2151 ipc_importance_task_t old_task_imp
= IIT_NULL
;
2152 ipc_importance_task_t new_task_imp
= IIT_NULL
;
2154 task_importance_reset(old_task
);
2156 /* Create an importance linkage from old_task to new_task */
2157 inherit
= ipc_importance_inherit_from_task(old_task
, new_task
);
2158 if (inherit
== III_NULL
) {
2162 /* Switch task importance base from old task to new task */
2163 ipc_importance_lock();
2165 old_task_imp
= old_task
->task_imp_base
;
2166 new_task_imp
= new_task
->task_imp_base
;
2168 old_task_imp
->iit_task
= new_task
;
2169 new_task_imp
->iit_task
= old_task
;
2171 old_task
->task_imp_base
= new_task_imp
;
2172 new_task
->task_imp_base
= old_task_imp
;
2174 #if DEVELOPMENT || DEBUG
2176 * Update the pid an proc name for importance base if any
2178 task_importance_update_owner_info(new_task
);
2180 ipc_importance_unlock();
2186 * Routine: ipc_importance_check_circularity
2188 * Check if queueing "port" in a message for "dest"
2189 * would create a circular group of ports and messages.
2191 * If no circularity (FALSE returned), then "port"
2192 * is changed from "in limbo" to "in transit".
2194 * That is, we want to set port->ip_destination == dest,
2195 * but guaranteeing that this doesn't create a circle
2196 * port->ip_destination->ip_destination->... == port
2198 * Additionally, if port was successfully changed to "in transit",
2199 * propagate boost assertions from the "in limbo" port to all
2200 * the ports in the chain, and, if the destination task accepts
2201 * boosts, to the destination task.
2204 * No ports locked. References held for "port" and "dest".
2208 ipc_importance_check_circularity(
2212 ipc_importance_task_t imp_task
= IIT_NULL
;
2213 ipc_importance_task_t release_imp_task
= IIT_NULL
;
2214 boolean_t imp_lock_held
= FALSE
;
2217 sync_qos_count_t sync_qos_delta_add
[THREAD_QOS_LAST
] = {0};
2218 sync_qos_count_t sync_qos_delta_sub
[THREAD_QOS_LAST
] = {0};
2219 boolean_t update_knote
= FALSE
;
2221 assert(port
!= IP_NULL
);
2222 assert(dest
!= IP_NULL
);
2228 /* port is in limbo, so donation status is safe to latch */
2229 if (port
->ip_impdonation
!= 0) {
2230 imp_lock_held
= TRUE
;
2231 ipc_importance_lock();
2235 * First try a quick check that can run in parallel.
2236 * No circularity if dest is not in transit.
2241 * Even if port is just carrying assertions for others,
2242 * we need the importance lock.
2244 if (port
->ip_impcount
> 0 && !imp_lock_held
) {
2245 if (!ipc_importance_lock_try()) {
2247 ipc_importance_lock();
2250 imp_lock_held
= TRUE
;
2253 if (ip_lock_try(dest
)) {
2254 if (!ip_active(dest
) ||
2255 (dest
->ip_receiver_name
!= MACH_PORT_NULL
) ||
2256 (dest
->ip_destination
== IP_NULL
))
2259 /* dest is in transit; further checking necessary */
2266 * We're about to pay the cost to serialize,
2267 * just go ahead and grab importance lock.
2269 if (!imp_lock_held
) {
2270 ipc_importance_lock();
2271 imp_lock_held
= TRUE
;
2274 ipc_port_multiple_lock(); /* massive serialization */
2277 * Search for the end of the chain (a port not in transit),
2278 * acquiring locks along the way.
2284 if (!ip_active(base
) ||
2285 (base
->ip_receiver_name
!= MACH_PORT_NULL
) ||
2286 (base
->ip_destination
== IP_NULL
))
2289 base
= base
->ip_destination
;
2292 /* all ports in chain from dest to base, inclusive, are locked */
2295 /* circularity detected! */
2297 ipc_port_multiple_unlock();
2299 /* port (== base) is in limbo */
2301 assert(ip_active(port
));
2302 assert(port
->ip_receiver_name
== MACH_PORT_NULL
);
2303 assert(port
->ip_destination
== IP_NULL
);
2305 while (dest
!= IP_NULL
) {
2308 /* dest is in transit or in limbo */
2310 assert(ip_active(dest
));
2311 assert(dest
->ip_receiver_name
== MACH_PORT_NULL
);
2313 next
= dest
->ip_destination
;
2319 ipc_importance_unlock();
2325 * The guarantee: lock port while the entire chain is locked.
2326 * Once port is locked, we can take a reference to dest,
2327 * add port to the chain, and unlock everything.
2331 ipc_port_multiple_unlock();
2334 imq_lock(&base
->ip_messages
);
2336 /* port is in limbo */
2338 assert(ip_active(port
));
2339 assert(port
->ip_receiver_name
== MACH_PORT_NULL
);
2340 assert(port
->ip_destination
== IP_NULL
);
2343 port
->ip_destination
= dest
;
2345 /* must have been in limbo or still bound to a task */
2346 assert(port
->ip_tempowner
!= 0);
2349 * We delayed dropping assertions from a specific task.
2350 * Cache that info now (we'll drop assertions and the
2351 * task reference below).
2353 release_imp_task
= port
->ip_imp_task
;
2354 if (IIT_NULL
!= release_imp_task
) {
2355 port
->ip_imp_task
= IIT_NULL
;
2357 assertcnt
= port
->ip_impcount
;
2359 /* take the port out of limbo w.r.t. assertions */
2360 port
->ip_tempowner
= 0;
2362 /* Capture the sync qos count delta */
2363 for (int i
= 0; i
< THREAD_QOS_LAST
; i
++) {
2364 sync_qos_delta_add
[i
] = port_sync_qos(port
, i
);
2367 /* now unlock chain */
2373 /* every port along chain track assertions behind it */
2374 ipc_port_impcount_delta(dest
, assertcnt
, base
);
2375 update_knote
= ipc_port_sync_qos_delta(dest
, sync_qos_delta_add
, sync_qos_delta_sub
);
2380 /* port is in transit */
2382 assert(ip_active(dest
));
2383 assert(dest
->ip_receiver_name
== MACH_PORT_NULL
);
2384 assert(dest
->ip_destination
!= IP_NULL
);
2385 assert(dest
->ip_tempowner
== 0);
2387 port
= dest
->ip_destination
;
2392 /* base is not in transit */
2393 assert(!ip_active(base
) ||
2394 (base
->ip_receiver_name
!= MACH_PORT_NULL
) ||
2395 (base
->ip_destination
== IP_NULL
));
2398 * Find the task to boost (if any).
2399 * We will boost "through" ports that don't know
2400 * about inheritance to deliver receive rights that
2403 if (ip_active(base
) && (assertcnt
> 0)) {
2404 assert(imp_lock_held
);
2405 if (base
->ip_tempowner
!= 0) {
2406 if (IIT_NULL
!= base
->ip_imp_task
) {
2407 /* specified tempowner task */
2408 imp_task
= base
->ip_imp_task
;
2409 assert(ipc_importance_task_is_any_receiver_type(imp_task
));
2411 /* otherwise don't boost current task */
2413 } else if (base
->ip_receiver_name
!= MACH_PORT_NULL
) {
2414 ipc_space_t space
= base
->ip_receiver
;
2416 /* only spaces with boost-accepting tasks */
2417 if (space
->is_task
!= TASK_NULL
&&
2418 ipc_importance_task_is_any_receiver_type(space
->is_task
->task_imp_base
))
2419 imp_task
= space
->is_task
->task_imp_base
;
2422 /* take reference before unlocking base */
2423 if (imp_task
!= IIT_NULL
) {
2424 ipc_importance_task_reference(imp_task
);
2429 KNOTE(&base
->ip_messages
.imq_klist
, 0);
2431 imq_unlock(&base
->ip_messages
);
2435 * Transfer assertions now that the ports are unlocked.
2436 * Avoid extra overhead if transferring to/from the same task.
2438 * NOTE: If a transfer is occurring, the new assertions will
2439 * be added to imp_task BEFORE the importance lock is unlocked.
2440 * This is critical - to avoid decrements coming from the kmsgs
2441 * beating the increment to the task.
2443 boolean_t transfer_assertions
= (imp_task
!= release_imp_task
);
2445 if (imp_task
!= IIT_NULL
) {
2446 assert(imp_lock_held
);
2447 if (transfer_assertions
)
2448 ipc_importance_task_hold_internal_assertion_locked(imp_task
, assertcnt
);
2451 if (release_imp_task
!= IIT_NULL
) {
2452 assert(imp_lock_held
);
2453 if (transfer_assertions
)
2454 ipc_importance_task_drop_internal_assertion_locked(release_imp_task
, assertcnt
);
2458 ipc_importance_unlock();
2460 if (imp_task
!= IIT_NULL
)
2461 ipc_importance_task_release(imp_task
);
2463 if (release_imp_task
!= IIT_NULL
)
2464 ipc_importance_task_release(release_imp_task
);
2470 * Routine: ipc_importance_send
2472 * Post the importance voucher attribute [if sent] or a static
2473 * importance boost depending upon options and conditions.
2475 * Destination port locked on entry and exit, may be dropped during the call.
2477 * A boolean identifying if the port lock was tempoarily dropped.
2480 ipc_importance_send(
2482 mach_msg_option_t option
)
2484 ipc_port_t port
= (ipc_port_t
) kmsg
->ikm_header
->msgh_remote_port
;
2485 boolean_t port_lock_dropped
= FALSE
;
2486 ipc_importance_elem_t elem
;
2488 ipc_importance_task_t task_imp
;
2491 assert(IP_VALID(port
));
2493 /* If no donation to be made, return quickly */
2494 if ((port
->ip_impdonation
== 0) ||
2495 (option
& MACH_SEND_NOIMPORTANCE
) != 0) {
2496 return port_lock_dropped
;
2499 task
= current_task();
2501 /* If forced sending a static boost, go update the port */
2502 if ((option
& MACH_SEND_IMPORTANCE
) != 0) {
2503 /* acquire the importance lock while trying to hang on to port lock */
2504 if (!ipc_importance_lock_try()) {
2505 port_lock_dropped
= TRUE
;
2507 ipc_importance_lock();
2512 task_imp
= task
->task_imp_base
;
2513 assert(IIT_NULL
!= task_imp
);
2515 /* If the sender can never donate importance, nothing to do */
2516 if (ipc_importance_task_is_never_donor(task_imp
)) {
2517 return port_lock_dropped
;
2522 /* If importance receiver and passing a voucher, look for importance in there */
2523 if (IP_VALID(kmsg
->ikm_voucher
) &&
2524 ipc_importance_task_is_marked_receiver(task_imp
)) {
2525 mach_voucher_attr_value_handle_t vals
[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
];
2526 mach_voucher_attr_value_handle_array_size_t val_count
;
2527 ipc_voucher_t voucher
;
2529 assert(ip_kotype(kmsg
->ikm_voucher
) == IKOT_VOUCHER
);
2530 voucher
= (ipc_voucher_t
)kmsg
->ikm_voucher
->ip_kobject
;
2532 /* check to see if the voucher has an importance attribute */
2533 val_count
= MACH_VOUCHER_ATTR_VALUE_MAX_NESTED
;
2534 kr
= mach_voucher_attr_control_get_values(ipc_importance_control
, voucher
,
2536 assert(KERN_SUCCESS
== kr
);
2539 * Only use importance associated with our task (either directly
2540 * or through an inherit that donates to our task).
2542 if (0 < val_count
) {
2543 ipc_importance_elem_t check_elem
;
2545 check_elem
= (ipc_importance_elem_t
)vals
[0];
2546 assert(IIE_NULL
!= check_elem
);
2547 if (IIE_TYPE_INHERIT
== IIE_TYPE(check_elem
)) {
2548 ipc_importance_inherit_t inherit
;
2549 inherit
= (ipc_importance_inherit_t
) check_elem
;
2550 if (inherit
->iii_to_task
== task_imp
) {
2553 } else if (check_elem
== (ipc_importance_elem_t
)task_imp
) {
2559 /* If we haven't found an importance attribute to send yet, use the task's */
2560 if (IIE_NULL
== elem
) {
2561 elem
= (ipc_importance_elem_t
)task_imp
;
2564 /* take a reference for the message to hold */
2565 ipc_importance_reference_internal(elem
);
2567 /* acquire the importance lock while trying to hang on to port lock */
2568 if (!ipc_importance_lock_try()) {
2569 port_lock_dropped
= TRUE
;
2571 ipc_importance_lock();
2574 /* link kmsg onto the donor element propagation chain */
2575 ipc_importance_kmsg_link(kmsg
, elem
);
2576 /* elem reference transfered to kmsg */
2578 incr_ref_counter(elem
->iie_kmsg_refs_added
);
2580 /* If the sender isn't currently a donor, no need to apply boost */
2581 if (!ipc_importance_task_is_donor(task_imp
)) {
2582 ipc_importance_unlock();
2584 /* re-acquire port lock, if needed */
2585 if (TRUE
== port_lock_dropped
)
2588 return port_lock_dropped
;
2592 /* Mark the fact that we are (currently) donating through this message */
2593 kmsg
->ikm_header
->msgh_bits
|= MACH_MSGH_BITS_RAISEIMP
;
2596 * If we need to relock the port, do it with the importance still locked.
2597 * This assures we get to add the importance boost through the port to
2598 * the task BEFORE anyone else can attempt to undo that operation if
2599 * the sender lost donor status.
2601 if (TRUE
== port_lock_dropped
) {
2605 ipc_importance_assert_held();
2607 #if IMPORTANCE_TRACE
2608 if (kdebug_enable
) {
2609 mach_msg_max_trailer_t
*dbgtrailer
= (mach_msg_max_trailer_t
*)
2610 ((vm_offset_t
)kmsg
->ikm_header
+ round_msg(kmsg
->ikm_header
->msgh_size
));
2611 unsigned int sender_pid
= dbgtrailer
->msgh_audit
.val
[5];
2612 mach_msg_id_t imp_msgh_id
= kmsg
->ikm_header
->msgh_id
;
2613 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_MSG
, IMP_MSG_SEND
)) | DBG_FUNC_START
,
2614 task_pid(task
), sender_pid
, imp_msgh_id
, 0, 0);
2616 #endif /* IMPORTANCE_TRACE */
2618 mach_port_delta_t delta
= 1;
2619 boolean_t need_port_lock
;
2620 task_imp
= IIT_NULL
;
2622 /* adjust port boost count (with importance and port locked) */
2623 need_port_lock
= ipc_port_importance_delta_internal(port
, IPID_OPTION_NORMAL
, &delta
, &task_imp
);
2624 /* hold a reference on task_imp */
2626 /* if we need to adjust a task importance as a result, apply that here */
2627 if (IIT_NULL
!= task_imp
&& delta
!= 0) {
2630 /* if this results in a change of state, propagate the transistion */
2631 if (ipc_importance_task_check_transition(task_imp
, IIT_UPDATE_HOLD
, delta
)) {
2633 /* can't hold the port lock during task transition(s) */
2634 if (!need_port_lock
) {
2635 need_port_lock
= TRUE
;
2638 ipc_importance_task_propagate_assertion_locked(task_imp
, IIT_UPDATE_HOLD
, TRUE
);
2643 ipc_importance_task_release_locked(task_imp
);
2644 /* importance unlocked */
2646 ipc_importance_unlock();
2649 if (need_port_lock
) {
2650 port_lock_dropped
= TRUE
;
2654 return port_lock_dropped
;
2658 * Routine: ipc_importance_inherit_from_kmsg
2660 * Create a "made" reference for an importance attribute representing
2661 * an inheritance between the sender of a message (if linked) and the
2662 * current task importance. If the message is not linked, a static
2663 * boost may be created, based on the boost state of the message.
2665 * Any transfer from kmsg linkage to inherit linkage must be atomic.
2667 * If the task is inactive, there isn't any need to return a new reference.
2669 * Nothing locked on entry. May block.
2671 static ipc_importance_inherit_t
2672 ipc_importance_inherit_from_kmsg(ipc_kmsg_t kmsg
)
2674 ipc_importance_task_t task_imp
= IIT_NULL
;
2675 ipc_importance_elem_t from_elem
= kmsg
->ikm_importance
;
2676 ipc_importance_elem_t elem
;
2677 task_t task_self
= current_task();
2679 ipc_port_t port
= kmsg
->ikm_header
->msgh_remote_port
;
2680 ipc_importance_inherit_t inherit
= III_NULL
;
2681 ipc_importance_inherit_t alloc
= III_NULL
;
2682 boolean_t cleared_self_donation
= FALSE
;
2686 /* The kmsg must have an importance donor or static boost to proceed */
2687 if (IIE_NULL
== kmsg
->ikm_importance
&&
2688 !MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
)) {
2693 * No need to set up an inherit linkage if the dest isn't a receiver
2694 * of one type or the other.
2696 if (!ipc_importance_task_is_any_receiver_type(task_self
->task_imp_base
)) {
2697 ipc_importance_lock();
2701 /* Grab a reference on the importance of the destination */
2702 task_imp
= ipc_importance_for_task(task_self
, FALSE
);
2704 ipc_importance_lock();
2706 if (IIT_NULL
== task_imp
) {
2710 incr_ref_counter(task_imp
->iit_elem
.iie_task_refs_added_inherit_from
);
2712 /* If message is already associated with an inherit... */
2713 if (IIE_TYPE_INHERIT
== IIE_TYPE(from_elem
)) {
2714 ipc_importance_inherit_t from_inherit
= (ipc_importance_inherit_t
)from_elem
;
2716 /* already targeting our task? - just use it */
2717 if (from_inherit
->iii_to_task
== task_imp
) {
2718 /* clear self-donation if not also present in inherit */
2719 if (!from_inherit
->iii_donating
&&
2720 MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
)) {
2721 kmsg
->ikm_header
->msgh_bits
&= ~MACH_MSGH_BITS_RAISEIMP
;
2722 cleared_self_donation
= TRUE
;
2724 inherit
= from_inherit
;
2726 } else if (III_DEPTH_MAX
== III_DEPTH(from_inherit
)) {
2727 ipc_importance_task_t to_task
;
2728 ipc_importance_elem_t unlinked_from
;
2731 * Chain too long. Switch to looking
2732 * directly at the from_inherit's to-task
2733 * as our source of importance.
2735 to_task
= from_inherit
->iii_to_task
;
2736 ipc_importance_task_reference(to_task
);
2737 from_elem
= (ipc_importance_elem_t
)to_task
;
2738 depth
= III_DEPTH_RESET
| 1;
2740 /* Fixup the kmsg linkage to reflect change */
2741 unlinked_from
= ipc_importance_kmsg_unlink(kmsg
);
2742 assert(unlinked_from
== (ipc_importance_elem_t
)from_inherit
);
2743 ipc_importance_kmsg_link(kmsg
, from_elem
);
2744 ipc_importance_inherit_release_locked(from_inherit
);
2745 /* importance unlocked */
2746 ipc_importance_lock();
2749 /* inheriting from an inherit */
2750 depth
= from_inherit
->iii_depth
+ 1;
2755 * Don't allow a task to inherit from itself (would keep it permanently
2756 * boosted even if all other donors to the task went away).
2759 if (from_elem
== (ipc_importance_elem_t
)task_imp
) {
2764 * But if the message isn't associated with any linked source, it is
2765 * intended to be permanently boosting (static boost from kernel).
2766 * In that case DO let the process permanently boost itself.
2768 if (IIE_NULL
== from_elem
) {
2769 assert(MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
));
2770 ipc_importance_task_reference_internal(task_imp
);
2771 from_elem
= (ipc_importance_elem_t
)task_imp
;
2775 * Now that we have the from_elem figured out,
2776 * check to see if we already have an inherit for this pairing
2778 while (III_NULL
== inherit
) {
2779 inherit
= ipc_importance_inherit_find(from_elem
, task_imp
, depth
);
2781 /* Do we have to allocate a new inherit */
2782 if (III_NULL
== inherit
) {
2783 if (III_NULL
!= alloc
) {
2787 /* allocate space */
2788 ipc_importance_unlock();
2789 alloc
= (ipc_importance_inherit_t
)
2790 zalloc(ipc_importance_inherit_zone
);
2791 ipc_importance_lock();
2795 /* snapshot the donating status while we have importance locked */
2796 donating
= MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
);
2798 if (III_NULL
!= inherit
) {
2799 /* We found one, piggyback on that */
2800 assert(0 < III_REFS(inherit
));
2801 assert(0 < IIE_REFS(inherit
->iii_from_elem
));
2802 assert(inherit
->iii_externcnt
>= inherit
->iii_made
);
2804 /* add in a made reference */
2805 if (0 == inherit
->iii_made
++) {
2806 assert(III_REFS_MAX
> III_REFS(inherit
));
2807 ipc_importance_inherit_reference_internal(inherit
);
2810 /* Reflect the inherit's change of status into the task boosts */
2811 if (0 == III_EXTERN(inherit
)) {
2812 assert(!inherit
->iii_donating
);
2813 inherit
->iii_donating
= donating
;
2815 task_imp
->iit_externcnt
+= inherit
->iii_externcnt
;
2816 task_imp
->iit_externdrop
+= inherit
->iii_externdrop
;
2819 assert(donating
== inherit
->iii_donating
);
2822 /* add in a external reference for this use of the inherit */
2823 inherit
->iii_externcnt
++;
2825 /* initialize the previously allocated space */
2827 inherit
->iii_bits
= IIE_TYPE_INHERIT
| 1;
2828 inherit
->iii_made
= 1;
2829 inherit
->iii_externcnt
= 1;
2830 inherit
->iii_externdrop
= 0;
2831 inherit
->iii_depth
= depth
;
2832 inherit
->iii_to_task
= task_imp
;
2833 inherit
->iii_from_elem
= IIE_NULL
;
2834 queue_init(&inherit
->iii_kmsgs
);
2837 inherit
->iii_donating
= TRUE
;
2839 inherit
->iii_donating
= FALSE
;
2843 * Chain our new inherit on the element it inherits from.
2844 * The new inherit takes our reference on from_elem.
2846 ipc_importance_inherit_link(inherit
, from_elem
);
2849 ipc_importance_counter_init(&inherit
->iii_elem
);
2850 from_elem
->iie_kmsg_refs_inherited
++;
2851 task_imp
->iit_elem
.iie_task_refs_inherited
++;
2857 * for those paths that came straight here: snapshot the donating status
2858 * (this should match previous snapshot for other paths).
2860 donating
= MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
);
2862 /* unlink the kmsg inheritance (if any) */
2863 elem
= ipc_importance_kmsg_unlink(kmsg
);
2864 assert(elem
== from_elem
);
2866 /* If found inherit and donating, reflect that in the task externcnt */
2867 if (III_NULL
!= inherit
&& donating
) {
2868 task_imp
->iit_externcnt
++;
2869 /* The owner of receive right might have changed, take the internal assertion */
2870 ipc_importance_task_hold_internal_assertion_locked(task_imp
, 1);
2871 /* may have dropped and retaken importance lock */
2874 /* If we didn't create a new inherit, we have some resources to release */
2875 if (III_NULL
== inherit
|| inherit
!= alloc
) {
2876 if (IIE_NULL
!= from_elem
) {
2877 if (III_NULL
!= inherit
) {
2878 incr_ref_counter(from_elem
->iie_kmsg_refs_coalesced
);
2880 incr_ref_counter(from_elem
->iie_kmsg_refs_dropped
);
2882 ipc_importance_release_locked(from_elem
);
2883 /* importance unlocked */
2885 ipc_importance_unlock();
2888 if (IIT_NULL
!= task_imp
) {
2889 if (III_NULL
!= inherit
) {
2890 incr_ref_counter(task_imp
->iit_elem
.iie_task_refs_coalesced
);
2892 ipc_importance_task_release(task_imp
);
2895 if (III_NULL
!= alloc
)
2896 zfree(ipc_importance_inherit_zone
, alloc
);
2898 /* from_elem and task_imp references transferred to new inherit */
2899 ipc_importance_unlock();
2903 * decrement port boost count
2904 * This is OK to do without the importance lock as we atomically
2905 * unlinked the kmsg and snapshot the donating state while holding
2906 * the importance lock
2908 if (donating
|| cleared_self_donation
) {
2910 /* drop importance from port and destination task */
2911 if (ipc_port_importance_delta(port
, IPID_OPTION_NORMAL
, -1) == FALSE
) {
2916 if (III_NULL
!= inherit
) {
2917 /* have an associated importance attr, even if currently not donating */
2918 kmsg
->ikm_header
->msgh_bits
|= MACH_MSGH_BITS_RAISEIMP
;
2920 /* we won't have an importance attribute associated with our message */
2921 kmsg
->ikm_header
->msgh_bits
&= ~MACH_MSGH_BITS_RAISEIMP
;
2928 * Routine: ipc_importance_inherit_from_task
2930 * Create a reference for an importance attribute representing
2931 * an inheritance between the to_task and from_task. The iii
2932 * created will be marked as III_FLAGS_FOR_OTHERS.
2934 * It will not dedup any iii which are not marked as III_FLAGS_FOR_OTHERS.
2936 * If the task is inactive, there isn't any need to return a new reference.
2938 * Nothing locked on entry. May block.
2939 * It should not be called from voucher subsystem.
2941 static ipc_importance_inherit_t
2942 ipc_importance_inherit_from_task(
2946 ipc_importance_task_t to_task_imp
= IIT_NULL
;
2947 ipc_importance_task_t from_task_imp
= IIT_NULL
;
2948 ipc_importance_elem_t from_elem
= IIE_NULL
;
2950 ipc_importance_inherit_t inherit
= III_NULL
;
2951 ipc_importance_inherit_t alloc
= III_NULL
;
2955 to_task_imp
= ipc_importance_for_task(to_task
, FALSE
);
2956 from_task_imp
= ipc_importance_for_task(from_task
, FALSE
);
2957 from_elem
= (ipc_importance_elem_t
)from_task_imp
;
2959 ipc_importance_lock();
2961 if (IIT_NULL
== to_task_imp
|| IIT_NULL
== from_task_imp
) {
2966 * No need to set up an inherit linkage if the to_task or from_task
2967 * isn't a receiver of one type or the other.
2969 if (!ipc_importance_task_is_any_receiver_type(to_task_imp
) ||
2970 !ipc_importance_task_is_any_receiver_type(from_task_imp
)) {
2974 /* Do not allow to create a linkage to self */
2975 if (to_task_imp
== from_task_imp
) {
2979 incr_ref_counter(to_task_imp
->iit_elem
.iie_task_refs_added_inherit_from
);
2980 incr_ref_counter(from_elem
->iie_kmsg_refs_added
);
2983 * Now that we have the from_elem figured out,
2984 * check to see if we already have an inherit for this pairing
2986 while (III_NULL
== inherit
) {
2987 inherit
= ipc_importance_inherit_find(from_elem
, to_task_imp
, depth
);
2989 /* Do we have to allocate a new inherit */
2990 if (III_NULL
== inherit
) {
2991 if (III_NULL
!= alloc
) {
2995 /* allocate space */
2996 ipc_importance_unlock();
2997 alloc
= (ipc_importance_inherit_t
)
2998 zalloc(ipc_importance_inherit_zone
);
2999 ipc_importance_lock();
3003 /* snapshot the donating status while we have importance locked */
3004 donating
= ipc_importance_task_is_donor(from_task_imp
);
3006 if (III_NULL
!= inherit
) {
3007 /* We found one, piggyback on that */
3008 assert(0 < III_REFS(inherit
));
3009 assert(0 < IIE_REFS(inherit
->iii_from_elem
));
3011 /* Take a reference for inherit */
3012 assert(III_REFS_MAX
> III_REFS(inherit
));
3013 ipc_importance_inherit_reference_internal(inherit
);
3015 /* Reflect the inherit's change of status into the task boosts */
3016 if (0 == III_EXTERN(inherit
)) {
3017 assert(!inherit
->iii_donating
);
3018 inherit
->iii_donating
= donating
;
3020 to_task_imp
->iit_externcnt
+= inherit
->iii_externcnt
;
3021 to_task_imp
->iit_externdrop
+= inherit
->iii_externdrop
;
3024 assert(donating
== inherit
->iii_donating
);
3027 /* add in a external reference for this use of the inherit */
3028 inherit
->iii_externcnt
++;
3030 /* initialize the previously allocated space */
3032 inherit
->iii_bits
= IIE_TYPE_INHERIT
| 1;
3033 inherit
->iii_made
= 0;
3034 inherit
->iii_externcnt
= 1;
3035 inherit
->iii_externdrop
= 0;
3036 inherit
->iii_depth
= depth
;
3037 inherit
->iii_to_task
= to_task_imp
;
3038 inherit
->iii_from_elem
= IIE_NULL
;
3039 queue_init(&inherit
->iii_kmsgs
);
3042 inherit
->iii_donating
= TRUE
;
3044 inherit
->iii_donating
= FALSE
;
3048 * Chain our new inherit on the element it inherits from.
3049 * The new inherit takes our reference on from_elem.
3051 ipc_importance_inherit_link(inherit
, from_elem
);
3054 ipc_importance_counter_init(&inherit
->iii_elem
);
3055 from_elem
->iie_kmsg_refs_inherited
++;
3056 task_imp
->iit_elem
.iie_task_refs_inherited
++;
3062 /* If found inherit and donating, reflect that in the task externcnt */
3063 if (III_NULL
!= inherit
&& donating
) {
3064 to_task_imp
->iit_externcnt
++;
3065 /* take the internal assertion */
3066 ipc_importance_task_hold_internal_assertion_locked(to_task_imp
, 1);
3067 /* may have dropped and retaken importance lock */
3070 /* If we didn't create a new inherit, we have some resources to release */
3071 if (III_NULL
== inherit
|| inherit
!= alloc
) {
3072 if (IIE_NULL
!= from_elem
) {
3073 if (III_NULL
!= inherit
) {
3074 incr_ref_counter(from_elem
->iie_kmsg_refs_coalesced
);
3076 incr_ref_counter(from_elem
->iie_kmsg_refs_dropped
);
3078 ipc_importance_release_locked(from_elem
);
3079 /* importance unlocked */
3081 ipc_importance_unlock();
3084 if (IIT_NULL
!= to_task_imp
) {
3085 if (III_NULL
!= inherit
) {
3086 incr_ref_counter(to_task_imp
->iit_elem
.iie_task_refs_coalesced
);
3088 ipc_importance_task_release(to_task_imp
);
3091 if (III_NULL
!= alloc
) {
3092 zfree(ipc_importance_inherit_zone
, alloc
);
3095 /* from_elem and to_task_imp references transferred to new inherit */
3096 ipc_importance_unlock();
3103 * Routine: ipc_importance_receive
3105 * Process importance attributes in a received message.
3107 * If an importance voucher attribute was sent, transform
3108 * that into an attribute value reflecting the inheritance
3109 * from the sender to the receiver.
3111 * If a static boost is received (or the voucher isn't on
3112 * a voucher-based boost), export a static boost.
3117 ipc_importance_receive(
3119 mach_msg_option_t option
)
3121 unsigned int sender_pid
= ((mach_msg_max_trailer_t
*)
3122 ((vm_offset_t
)kmsg
->ikm_header
+
3123 round_msg(kmsg
->ikm_header
->msgh_size
)))->msgh_audit
.val
[5];
3124 task_t task_self
= current_task();
3127 /* convert to a voucher with an inherit importance attribute? */
3128 if ((option
& MACH_RCV_VOUCHER
) != 0) {
3129 uint8_t recipes
[2 * sizeof(ipc_voucher_attr_recipe_data_t
) +
3130 sizeof(mach_voucher_attr_value_handle_t
)];
3131 ipc_voucher_attr_raw_recipe_array_size_t recipe_size
= 0;
3132 ipc_voucher_attr_recipe_t recipe
= (ipc_voucher_attr_recipe_t
)recipes
;
3133 ipc_voucher_t recv_voucher
;
3134 mach_voucher_attr_value_handle_t handle
;
3135 ipc_importance_inherit_t inherit
;
3138 /* set up recipe to copy the old voucher */
3139 if (IP_VALID(kmsg
->ikm_voucher
)) {
3140 ipc_voucher_t sent_voucher
= (ipc_voucher_t
)kmsg
->ikm_voucher
->ip_kobject
;
3142 recipe
->key
= MACH_VOUCHER_ATTR_KEY_ALL
;
3143 recipe
->command
= MACH_VOUCHER_ATTR_COPY
;
3144 recipe
->previous_voucher
= sent_voucher
;
3145 recipe
->content_size
= 0;
3146 recipe_size
+= sizeof(*recipe
);
3150 * create an inheritance attribute from the kmsg (may be NULL)
3151 * transferring any boosts from the kmsg linkage through the
3152 * port directly to the new inheritance object.
3154 inherit
= ipc_importance_inherit_from_kmsg(kmsg
);
3155 handle
= (mach_voucher_attr_value_handle_t
)inherit
;
3157 assert(IIE_NULL
== kmsg
->ikm_importance
);
3160 * Only create a new voucher if we have an inherit object
3161 * (from the ikm_importance field of the incoming message), OR
3162 * we have a valid incoming voucher. If we have neither of
3163 * these things then there is no need to create a new voucher.
3165 if (IP_VALID(kmsg
->ikm_voucher
) || inherit
!= III_NULL
) {
3166 /* replace the importance attribute with the handle we created */
3167 /* our made reference on the inherit is donated to the voucher */
3168 recipe
= (ipc_voucher_attr_recipe_t
)&recipes
[recipe_size
];
3169 recipe
->key
= MACH_VOUCHER_ATTR_KEY_IMPORTANCE
;
3170 recipe
->command
= MACH_VOUCHER_ATTR_SET_VALUE_HANDLE
;
3171 recipe
->previous_voucher
= IPC_VOUCHER_NULL
;
3172 recipe
->content_size
= sizeof(mach_voucher_attr_value_handle_t
);
3173 *(mach_voucher_attr_value_handle_t
*)(void *)recipe
->content
= handle
;
3174 recipe_size
+= sizeof(*recipe
) + sizeof(mach_voucher_attr_value_handle_t
);
3176 kr
= ipc_voucher_attr_control_create_mach_voucher(ipc_importance_control
,
3180 assert(KERN_SUCCESS
== kr
);
3182 /* swap the voucher port (and set voucher bits in case it didn't already exist) */
3183 kmsg
->ikm_header
->msgh_bits
|= (MACH_MSG_TYPE_MOVE_SEND
<< 16);
3184 ipc_port_release_send(kmsg
->ikm_voucher
);
3185 kmsg
->ikm_voucher
= convert_voucher_to_port(recv_voucher
);
3186 if (III_NULL
!= inherit
)
3189 } else { /* Don't want a voucher */
3191 /* got linked importance? have to drop */
3192 if (IIE_NULL
!= kmsg
->ikm_importance
) {
3193 ipc_importance_elem_t elem
;
3195 ipc_importance_lock();
3196 elem
= ipc_importance_kmsg_unlink(kmsg
);
3198 elem
->iie_kmsg_refs_dropped
++;
3200 ipc_importance_release_locked(elem
);
3201 /* importance unlocked */
3204 /* With kmsg unlinked, can safely examine message importance attribute. */
3205 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
)) {
3206 ipc_importance_task_t task_imp
= task_self
->task_imp_base
;
3207 ipc_port_t port
= kmsg
->ikm_header
->msgh_remote_port
;
3209 /* The owner of receive right might have changed, take the internal assertion */
3210 if (KERN_SUCCESS
== ipc_importance_task_hold_internal_assertion(task_imp
, 1)) {
3211 ipc_importance_task_externalize_legacy_assertion(task_imp
, 1, sender_pid
);
3214 /* The importance boost never applied to task (clear the bit) */
3215 kmsg
->ikm_header
->msgh_bits
&= ~MACH_MSGH_BITS_RAISEIMP
;
3219 /* Drop the boost on the port and the owner of the receive right */
3221 if (ipc_port_importance_delta(port
, IPID_OPTION_NORMAL
, -1) == FALSE
) {
3227 #if IMPORTANCE_TRACE
3229 KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE
, (IMPORTANCE_CODE(IMP_MSG
, IMP_MSG_DELV
)) | DBG_FUNC_NONE
,
3230 sender_pid
, task_pid(task_self
),
3231 kmsg
->ikm_header
->msgh_id
, impresult
, 0);
3232 if (impresult
== 2){
3234 * This probe only covers new voucher-based path. Legacy importance
3235 * will trigger the probe in ipc_importance_task_externalize_assertion()
3236 * above and have impresult==1 here.
3238 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
);
3240 #endif /* IMPORTANCE_TRACE */
3244 * Routine: ipc_importance_unreceive
3246 * Undo receive of importance attributes in a message.
3252 ipc_importance_unreceive(
3254 mach_msg_option_t __unused option
)
3256 /* importance should already be in the voucher and out of the kmsg */
3257 assert(IIE_NULL
== kmsg
->ikm_importance
);
3259 /* See if there is a legacy boost to be dropped from receiver */
3260 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
)) {
3261 ipc_importance_task_t task_imp
;
3263 kmsg
->ikm_header
->msgh_bits
&= ~MACH_MSGH_BITS_RAISEIMP
;
3264 task_imp
= current_task()->task_imp_base
;
3265 if (!IP_VALID(kmsg
->ikm_voucher
) && IIT_NULL
!= task_imp
) {
3266 ipc_importance_task_drop_legacy_external_assertion(task_imp
, 1);
3269 * ipc_kmsg_copyout_dest() will consume the voucher
3270 * and any contained importance.
3276 * Routine: ipc_importance_clean
3278 * Clean up importance state in a kmsg that is being cleaned.
3279 * Unlink the importance chain if one was set up, and drop
3280 * the reference this kmsg held on the donor. Then check to
3281 * if importance was carried to the port, and remove that if
3287 ipc_importance_clean(
3292 /* Is the kmsg still linked? If so, remove that first */
3293 if (IIE_NULL
!= kmsg
->ikm_importance
) {
3294 ipc_importance_elem_t elem
;
3296 ipc_importance_lock();
3297 elem
= ipc_importance_kmsg_unlink(kmsg
);
3298 assert(IIE_NULL
!= elem
);
3299 ipc_importance_release_locked(elem
);
3300 /* importance unlocked */
3303 /* See if there is a legacy importance boost to be dropped from port */
3304 if (MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
)) {
3305 kmsg
->ikm_header
->msgh_bits
&= ~MACH_MSGH_BITS_RAISEIMP
;
3306 port
= kmsg
->ikm_header
->msgh_remote_port
;
3307 if (IP_VALID(port
)) {
3309 /* inactive ports already had their importance boosts dropped */
3310 if (!ip_active(port
) ||
3311 ipc_port_importance_delta(port
, IPID_OPTION_NORMAL
, -1) == FALSE
) {
3319 ipc_importance_assert_clean(__assert_only ipc_kmsg_t kmsg
)
3321 assert(IIE_NULL
== kmsg
->ikm_importance
);
3322 assert(!MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg
->ikm_header
->msgh_bits
));
3326 * IPC Importance Attribute Manager definition
3329 static kern_return_t
3330 ipc_importance_release_value(
3331 ipc_voucher_attr_manager_t manager
,
3332 mach_voucher_attr_key_t key
,
3333 mach_voucher_attr_value_handle_t value
,
3334 mach_voucher_attr_value_reference_t sync
);
3336 static kern_return_t
3337 ipc_importance_get_value(
3338 ipc_voucher_attr_manager_t manager
,
3339 mach_voucher_attr_key_t key
,
3340 mach_voucher_attr_recipe_command_t command
,
3341 mach_voucher_attr_value_handle_array_t prev_values
,
3342 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
3343 mach_voucher_attr_content_t content
,
3344 mach_voucher_attr_content_size_t content_size
,
3345 mach_voucher_attr_value_handle_t
*out_value
,
3346 mach_voucher_attr_value_flags_t
*out_flags
,
3347 ipc_voucher_t
*out_value_voucher
);
3349 static kern_return_t
3350 ipc_importance_extract_content(
3351 ipc_voucher_attr_manager_t manager
,
3352 mach_voucher_attr_key_t key
,
3353 mach_voucher_attr_value_handle_array_t values
,
3354 mach_voucher_attr_value_handle_array_size_t value_count
,
3355 mach_voucher_attr_recipe_command_t
*out_command
,
3356 mach_voucher_attr_content_t out_content
,
3357 mach_voucher_attr_content_size_t
*in_out_content_size
);
3359 static kern_return_t
3360 ipc_importance_command(
3361 ipc_voucher_attr_manager_t manager
,
3362 mach_voucher_attr_key_t key
,
3363 mach_voucher_attr_value_handle_array_t values
,
3364 mach_msg_type_number_t value_count
,
3365 mach_voucher_attr_command_t command
,
3366 mach_voucher_attr_content_t in_content
,
3367 mach_voucher_attr_content_size_t in_content_size
,
3368 mach_voucher_attr_content_t out_content
,
3369 mach_voucher_attr_content_size_t
*out_content_size
);
3372 ipc_importance_manager_release(
3373 ipc_voucher_attr_manager_t manager
);
3375 struct ipc_voucher_attr_manager ipc_importance_manager
= {
3376 .ivam_release_value
= ipc_importance_release_value
,
3377 .ivam_get_value
= ipc_importance_get_value
,
3378 .ivam_extract_content
= ipc_importance_extract_content
,
3379 .ivam_command
= ipc_importance_command
,
3380 .ivam_release
= ipc_importance_manager_release
,
3381 .ivam_flags
= IVAM_FLAGS_NONE
,
3384 #define IMPORTANCE_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_IMPORTANCE == (key))
3385 #define IMPORTANCE_ASSERT_MANAGER(manager) assert(&ipc_importance_manager == (manager))
3388 * Routine: ipc_importance_release_value [Voucher Attribute Manager Interface]
3390 * Release what the voucher system believes is the last "made" reference
3391 * on an importance attribute value handle. The sync parameter is used to
3392 * avoid races with new made references concurrently being returned to the
3393 * voucher system in other threads.
3395 * Nothing locked on entry. May block.
3397 static kern_return_t
3398 ipc_importance_release_value(
3399 ipc_voucher_attr_manager_t __assert_only manager
,
3400 mach_voucher_attr_key_t __assert_only key
,
3401 mach_voucher_attr_value_handle_t value
,
3402 mach_voucher_attr_value_reference_t sync
)
3404 ipc_importance_elem_t elem
;
3406 IMPORTANCE_ASSERT_MANAGER(manager
);
3407 IMPORTANCE_ASSERT_KEY(key
);
3410 elem
= (ipc_importance_elem_t
)value
;
3412 ipc_importance_lock();
3414 /* Any oustanding made refs? */
3415 if (sync
!= elem
->iie_made
) {
3416 assert(sync
< elem
->iie_made
);
3417 ipc_importance_unlock();
3418 return KERN_FAILURE
;
3425 * If there are pending external boosts represented by this attribute,
3426 * drop them from the apropriate task
3428 if (IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) {
3429 ipc_importance_inherit_t inherit
= (ipc_importance_inherit_t
)elem
;
3431 assert(inherit
->iii_externcnt
>= inherit
->iii_externdrop
);
3433 if (inherit
->iii_donating
) {
3434 ipc_importance_task_t imp_task
= inherit
->iii_to_task
;
3435 uint32_t assertcnt
= III_EXTERN(inherit
);
3437 assert(ipc_importance_task_is_any_receiver_type(imp_task
));
3438 assert(imp_task
->iit_externcnt
>= inherit
->iii_externcnt
);
3439 assert(imp_task
->iit_externdrop
>= inherit
->iii_externdrop
);
3440 imp_task
->iit_externcnt
-= inherit
->iii_externcnt
;
3441 imp_task
->iit_externdrop
-= inherit
->iii_externdrop
;
3442 inherit
->iii_externcnt
= 0;
3443 inherit
->iii_externdrop
= 0;
3444 inherit
->iii_donating
= FALSE
;
3446 /* adjust the internal assertions - and propagate if needed */
3447 if (ipc_importance_task_check_transition(imp_task
, IIT_UPDATE_DROP
, assertcnt
)) {
3448 ipc_importance_task_propagate_assertion_locked(imp_task
, IIT_UPDATE_DROP
, TRUE
);
3451 inherit
->iii_externcnt
= 0;
3452 inherit
->iii_externdrop
= 0;
3456 /* drop the made reference on elem */
3457 ipc_importance_release_locked(elem
);
3458 /* returns unlocked */
3460 return KERN_SUCCESS
;
3465 * Routine: ipc_importance_get_value [Voucher Attribute Manager Interface]
3467 * Convert command and content data into a reference on a [potentially new]
3468 * attribute value. The importance attribute manager will only allow the
3469 * caller to get a value for the current task's importance, or to redeem
3470 * an importance attribute from an existing voucher.
3472 * Nothing locked on entry. May block.
3474 static kern_return_t
3475 ipc_importance_get_value(
3476 ipc_voucher_attr_manager_t __assert_only manager
,
3477 mach_voucher_attr_key_t __assert_only key
,
3478 mach_voucher_attr_recipe_command_t command
,
3479 mach_voucher_attr_value_handle_array_t prev_values
,
3480 mach_voucher_attr_value_handle_array_size_t prev_value_count
,
3481 mach_voucher_attr_content_t __unused content
,
3482 mach_voucher_attr_content_size_t content_size
,
3483 mach_voucher_attr_value_handle_t
*out_value
,
3484 mach_voucher_attr_value_flags_t
*out_flags
,
3485 ipc_voucher_t
*out_value_voucher
)
3487 ipc_importance_elem_t elem
;
3490 IMPORTANCE_ASSERT_MANAGER(manager
);
3491 IMPORTANCE_ASSERT_KEY(key
);
3493 if (0 != content_size
)
3494 return KERN_INVALID_ARGUMENT
;
3496 *out_flags
= MACH_VOUCHER_ATTR_VALUE_FLAGS_NONE
;
3497 /* never an out voucher */
3501 case MACH_VOUCHER_ATTR_REDEEM
:
3503 /* redeem of previous values is the value */
3504 if (0 < prev_value_count
) {
3505 elem
= (ipc_importance_elem_t
)prev_values
[0];
3506 assert(IIE_NULL
!= elem
);
3508 ipc_importance_lock();
3509 assert(0 < elem
->iie_made
);
3511 ipc_importance_unlock();
3513 *out_value
= prev_values
[0];
3514 return KERN_SUCCESS
;
3517 /* redeem of default is default */
3519 *out_value_voucher
= IPC_VOUCHER_NULL
;
3520 return KERN_SUCCESS
;
3522 case MACH_VOUCHER_ATTR_IMPORTANCE_SELF
:
3523 self
= current_task();
3525 elem
= (ipc_importance_elem_t
)ipc_importance_for_task(self
, TRUE
);
3526 /* made reference added (or IIE_NULL which isn't referenced) */
3528 *out_value
= (mach_voucher_attr_value_handle_t
)elem
;
3529 *out_value_voucher
= IPC_VOUCHER_NULL
;
3530 return KERN_SUCCESS
;
3534 * every other command is unknown
3536 * Specifically, there is no mechanism provided to construct an
3537 * importance attribute for a task/process from just a pid or
3538 * task port. It has to be copied (or redeemed) from a previous
3539 * voucher that has it.
3541 return KERN_INVALID_ARGUMENT
;
3546 * Routine: ipc_importance_extract_content [Voucher Attribute Manager Interface]
3548 * Extract meaning from the attribute value present in a voucher. While
3549 * the real goal is to provide commands and data that can reproduce the
3550 * voucher's value "out of thin air", this isn't possible with importance
3551 * attribute values. Instead, return debug info to help track down dependencies.
3553 * Nothing locked on entry. May block.
3555 static kern_return_t
3556 ipc_importance_extract_content(
3557 ipc_voucher_attr_manager_t __assert_only manager
,
3558 mach_voucher_attr_key_t __assert_only key
,
3559 mach_voucher_attr_value_handle_array_t values
,
3560 mach_voucher_attr_value_handle_array_size_t value_count
,
3561 mach_voucher_attr_recipe_command_t
*out_command
,
3562 mach_voucher_attr_content_t out_content
,
3563 mach_voucher_attr_content_size_t
*in_out_content_size
)
3565 mach_voucher_attr_content_size_t size
= 0;
3566 ipc_importance_elem_t elem
;
3569 IMPORTANCE_ASSERT_MANAGER(manager
);
3570 IMPORTANCE_ASSERT_KEY(key
);
3572 /* the first non-default value provides the data */
3573 for (i
= 0; i
< value_count
; i
++) {
3574 elem
= (ipc_importance_elem_t
)values
[i
];
3575 if (IIE_NULL
== elem
)
3578 snprintf((char *)out_content
, *in_out_content_size
, "Importance for pid ");
3579 size
= (mach_voucher_attr_content_size_t
)strlen((char *)out_content
);
3582 ipc_importance_inherit_t inherit
= III_NULL
;
3583 ipc_importance_task_t task_imp
;
3587 if (IIE_TYPE_TASK
== IIE_TYPE(elem
)) {
3588 task_imp
= (ipc_importance_task_t
)elem
;
3589 task
= task_imp
->iit_task
;
3590 t_pid
= (TASK_NULL
!= task
) ?
3591 task_pid(task
) : -1;
3592 snprintf((char *)out_content
+ size
, *in_out_content_size
- size
, "%d", t_pid
);
3594 inherit
= (ipc_importance_inherit_t
)elem
;
3595 task_imp
= inherit
->iii_to_task
;
3596 task
= task_imp
->iit_task
;
3597 t_pid
= (TASK_NULL
!= task
) ?
3598 task_pid(task
) : -1;
3599 snprintf((char *)out_content
+ size
, *in_out_content_size
- size
,
3600 "%d (%d of %d boosts) %s from pid ", t_pid
,
3601 III_EXTERN(inherit
), inherit
->iii_externcnt
,
3602 (inherit
->iii_donating
) ? "donated" : "linked");
3605 size
= (mach_voucher_attr_content_size_t
)strlen((char *)out_content
);
3607 if (III_NULL
== inherit
)
3610 elem
= inherit
->iii_from_elem
;
3612 size
++; /* account for NULL */
3614 *out_command
= MACH_VOUCHER_ATTR_NOOP
; /* cannot be used to regenerate value */
3615 *in_out_content_size
= size
;
3616 return KERN_SUCCESS
;
3620 * Routine: ipc_importance_command [Voucher Attribute Manager Interface]
3622 * Run commands against the importance attribute value found in a voucher.
3623 * No such commands are currently supported.
3625 * Nothing locked on entry. May block.
3627 static kern_return_t
3628 ipc_importance_command(
3629 ipc_voucher_attr_manager_t __assert_only manager
,
3630 mach_voucher_attr_key_t __assert_only key
,
3631 mach_voucher_attr_value_handle_array_t values
,
3632 mach_msg_type_number_t value_count
,
3633 mach_voucher_attr_command_t command
,
3634 mach_voucher_attr_content_t in_content
,
3635 mach_voucher_attr_content_size_t in_content_size
,
3636 mach_voucher_attr_content_t out_content
,
3637 mach_voucher_attr_content_size_t
*out_content_size
)
3639 ipc_importance_inherit_t inherit
;
3640 ipc_importance_task_t to_task
;
3641 uint32_t refs
, *outrefsp
;
3642 mach_msg_type_number_t i
;
3645 IMPORTANCE_ASSERT_MANAGER(manager
);
3646 IMPORTANCE_ASSERT_KEY(key
);
3648 if (in_content_size
!= sizeof(refs
) ||
3649 (*out_content_size
!= 0 && *out_content_size
!= sizeof(refs
))) {
3650 return KERN_INVALID_ARGUMENT
;
3652 refs
= *(uint32_t *)(void *)in_content
;
3653 outrefsp
= (*out_content_size
!= 0) ? (uint32_t *)(void *)out_content
: NULL
;
3655 if (MACH_VOUCHER_IMPORTANCE_ATTR_DROP_EXTERNAL
!= command
) {
3656 return KERN_NOT_SUPPORTED
;
3659 /* the first non-default value of the apropos type provides the data */
3661 for (i
= 0; i
< value_count
; i
++) {
3662 ipc_importance_elem_t elem
= (ipc_importance_elem_t
)values
[i
];
3664 if (IIE_NULL
!= elem
&& IIE_TYPE_INHERIT
== IIE_TYPE(elem
)) {
3665 inherit
= (ipc_importance_inherit_t
)elem
;
3669 if (III_NULL
== inherit
) {
3670 return KERN_INVALID_ARGUMENT
;
3673 ipc_importance_lock();
3676 if (NULL
!= outrefsp
) {
3677 *outrefsp
= III_EXTERN(inherit
);
3679 ipc_importance_unlock();
3680 return KERN_SUCCESS
;
3683 to_task
= inherit
->iii_to_task
;
3684 assert(ipc_importance_task_is_any_receiver_type(to_task
));
3686 /* if not donating to a denap receiver, it was called incorrectly */
3687 if (!ipc_importance_task_is_marked_denap_receiver(to_task
)) {
3688 ipc_importance_unlock();
3689 return KERN_INVALID_TASK
; /* keeps dispatch happy */
3692 /* Enough external references left to drop? */
3693 if (III_EXTERN(inherit
) < refs
) {
3694 ipc_importance_unlock();
3695 return KERN_FAILURE
;
3698 /* re-base external and internal counters at the inherit and the to-task (if apropos) */
3699 if (inherit
->iii_donating
) {
3700 assert(IIT_EXTERN(to_task
) >= III_EXTERN(inherit
));
3701 assert(to_task
->iit_externcnt
>= inherit
->iii_externcnt
);
3702 assert(to_task
->iit_externdrop
>= inherit
->iii_externdrop
);
3703 inherit
->iii_externdrop
+= refs
;
3704 to_task
->iit_externdrop
+= refs
;
3705 externcnt
= III_EXTERN(inherit
);
3706 if (0 == externcnt
) {
3707 inherit
->iii_donating
= FALSE
;
3708 to_task
->iit_externcnt
-= inherit
->iii_externcnt
;
3709 to_task
->iit_externdrop
-= inherit
->iii_externdrop
;
3712 /* Start AppNap delay hysteresis - even if not the last boost for the task. */
3713 if (ipc_importance_delayed_drop_call
!= NULL
&&
3714 ipc_importance_task_is_marked_denap_receiver(to_task
)) {
3715 ipc_importance_task_delayed_drop(to_task
);
3718 /* drop task assertions associated with the dropped boosts */
3719 if (ipc_importance_task_check_transition(to_task
, IIT_UPDATE_DROP
, refs
)) {
3720 ipc_importance_task_propagate_assertion_locked(to_task
, IIT_UPDATE_DROP
, TRUE
);
3721 /* may have dropped and retaken importance lock */
3724 /* assert(to_task->iit_assertcnt >= refs + externcnt); */
3725 /* defensive deduction in case of assertcnt underflow */
3726 if (to_task
->iit_assertcnt
> refs
+ externcnt
) {
3727 to_task
->iit_assertcnt
-= refs
;
3729 to_task
->iit_assertcnt
= externcnt
;
3733 inherit
->iii_externdrop
+= refs
;
3734 externcnt
= III_EXTERN(inherit
);
3737 /* capture result (if requested) */
3738 if (NULL
!= outrefsp
) {
3739 *outrefsp
= externcnt
;
3742 ipc_importance_unlock();
3743 return KERN_SUCCESS
;
3747 * Routine: ipc_importance_manager_release [Voucher Attribute Manager Interface]
3749 * Release the Voucher system's reference on the IPC importance attribute
3752 * As this can only occur after the manager drops the Attribute control
3753 * reference granted back at registration time, and that reference is never
3754 * dropped, this should never be called.
3757 ipc_importance_manager_release(
3758 ipc_voucher_attr_manager_t __assert_only manager
)
3760 IMPORTANCE_ASSERT_MANAGER(manager
);
3761 panic("Voucher importance manager released");
3765 * Routine: ipc_importance_init
3767 * Initialize the IPC importance manager.
3769 * Zones and Vouchers are already initialized.
3772 ipc_importance_init(void)
3774 natural_t ipc_importance_max
= (task_max
+ thread_max
) * 2;
3778 if (PE_parse_boot_argn("imp_interactive_receiver", temp_buf
, sizeof(temp_buf
))) {
3779 ipc_importance_interactive_receiver
= TRUE
;
3782 ipc_importance_task_zone
= zinit(sizeof(struct ipc_importance_task
),
3783 ipc_importance_max
* sizeof(struct ipc_importance_task
),
3784 sizeof(struct ipc_importance_task
),
3785 "ipc task importance");
3786 zone_change(ipc_importance_task_zone
, Z_NOENCRYPT
, TRUE
);
3788 ipc_importance_inherit_zone
= zinit(sizeof(struct ipc_importance_inherit
),
3789 ipc_importance_max
* sizeof(struct ipc_importance_inherit
),
3790 sizeof(struct ipc_importance_inherit
),
3791 "ipc importance inherit");
3792 zone_change(ipc_importance_inherit_zone
, Z_NOENCRYPT
, TRUE
);
3795 #if DEVELOPMENT || DEBUG
3796 queue_init(&global_iit_alloc_queue
);
3799 /* initialize global locking */
3800 ipc_importance_lock_init();
3802 kr
= ipc_register_well_known_mach_voucher_attr_manager(&ipc_importance_manager
,
3803 (mach_voucher_attr_value_handle_t
)0,
3804 MACH_VOUCHER_ATTR_KEY_IMPORTANCE
,
3805 &ipc_importance_control
);
3806 if (KERN_SUCCESS
!= kr
)
3807 printf("Voucher importance manager register returned %d", kr
);
3811 * Routine: ipc_importance_thread_call_init
3813 * Initialize the IPC importance code dependent upon
3814 * thread-call support being available.
3816 * Thread-call mechanism is already initialized.
3819 ipc_importance_thread_call_init(void)
3821 /* initialize delayed drop queue and thread-call */
3822 queue_init(&ipc_importance_delayed_drop_queue
);
3823 ipc_importance_delayed_drop_call
=
3824 thread_call_allocate(ipc_importance_task_delayed_drop_scan
, NULL
);
3825 if (NULL
== ipc_importance_delayed_drop_call
) {
3826 panic("ipc_importance_init");
3831 * Routing: task_importance_list_pids
3832 * Purpose: list pids where task in donating importance.
3833 * Conditions: To be called only from kdp stackshot code.
3834 * Will panic the system otherwise.
3837 task_importance_list_pids(task_t task
, int flags
, char *pid_list
, unsigned int max_count
)
3839 if (kdp_lck_spin_is_acquired(&ipc_importance_lock_data
) ||
3841 task
->task_imp_base
== IIT_NULL
||
3843 flags
!= TASK_IMP_LIST_DONATING_PIDS
) {
3846 unsigned int pidcount
= 0;
3848 ipc_importance_task_t task_imp
= task
->task_imp_base
;
3849 ipc_kmsg_t temp_kmsg
;
3850 ipc_importance_inherit_t temp_inherit
;
3851 ipc_importance_elem_t elem
;
3852 int target_pid
= 0, previous_pid
;
3854 queue_iterate(&task_imp
->iit_inherits
, temp_inherit
, ipc_importance_inherit_t
, iii_inheritance
) {
3855 /* check space in buffer */
3856 if (pidcount
>= max_count
)
3858 previous_pid
= target_pid
;
3861 if (temp_inherit
->iii_donating
) {
3863 #if DEVELOPMENT || DEBUG
3864 target_pid
= temp_inherit
->iii_to_task
->iit_bsd_pid
;
3866 temp_task
= temp_inherit
->iii_to_task
->iit_task
;
3867 if (temp_task
!= TASK_NULL
) {
3868 target_pid
= task_pid(temp_task
);
3873 if (target_pid
!= -1 && previous_pid
!= target_pid
) {
3874 memcpy(pid_list
, &target_pid
, sizeof(target_pid
));
3875 pid_list
+= sizeof(target_pid
);
3882 queue_iterate(&task_imp
->iit_kmsgs
, temp_kmsg
, ipc_kmsg_t
, ikm_inheritance
) {
3883 if (pidcount
>= max_count
)
3885 previous_pid
= target_pid
;
3887 elem
= temp_kmsg
->ikm_importance
;
3888 temp_task
= TASK_NULL
;
3890 if (elem
== IIE_NULL
) {
3894 if (!(temp_kmsg
->ikm_header
&& MACH_MSGH_BITS_RAISED_IMPORTANCE(temp_kmsg
->ikm_header
->msgh_bits
))) {
3898 if (IIE_TYPE_TASK
== IIE_TYPE(elem
) &&
3899 (((ipc_importance_task_t
)elem
)->iit_task
!= TASK_NULL
)) {
3900 target_pid
= task_pid(((ipc_importance_task_t
)elem
)->iit_task
);
3902 temp_inherit
= (ipc_importance_inherit_t
)elem
;
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
);