]> git.saurik.com Git - apple/xnu.git/blobdiff - osfmk/ipc/ipc_importance.c
xnu-2782.1.97.tar.gz
[apple/xnu.git] / osfmk / ipc / ipc_importance.c
diff --git a/osfmk/ipc/ipc_importance.c b/osfmk/ipc/ipc_importance.c
new file mode 100644 (file)
index 0000000..a8aa9c4
--- /dev/null
@@ -0,0 +1,3266 @@
+/*
+ * Copyright (c) 2013 Apple Inc. All rights reserved.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. The rights granted to you under the License
+ * may not be used to create, or enable the creation or redistribution of,
+ * unlawful or unlicensed copies of an Apple operating system, or to
+ * circumvent, violate, or enable the circumvention or violation of, any
+ * terms of an Apple operating system software license agreement.
+ *
+ * Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
+ */
+
+#include <mach/mach_types.h>
+#include <mach/notify.h>
+#include <ipc/ipc_types.h>
+#include <ipc/ipc_importance.h>
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_voucher.h>
+#include <kern/ipc_kobject.h>
+#include <kern/ipc_tt.h>
+#include <kern/mach_param.h>
+#include <kern/misc_protos.h>
+#include <kern/kalloc.h>
+#include <kern/zalloc.h>
+#include <kern/queue.h>
+#include <kern/task.h>
+
+#include <sys/kdebug.h>
+
+#include <mach/mach_voucher_attr_control.h>
+#include <mach/machine/sdt.h>
+
+extern int     proc_pid(void *);
+extern int     proc_selfpid(void);
+extern uint64_t proc_uniqueid(void *p);
+extern char    *proc_name_address(void *p);
+
+/*
+ * Globals for delayed boost drop processing.
+ */
+static queue_head_t ipc_importance_delayed_drop_queue;
+static thread_call_t ipc_importance_delayed_drop_call;
+static uint64_t ipc_importance_delayed_drop_timestamp;
+static boolean_t ipc_importance_delayed_drop_call_requested = FALSE;
+
+#define DENAP_DROP_TARGET (1000 * NSEC_PER_MSEC) /* optimum denap delay */
+#define DENAP_DROP_SKEW    (100 * NSEC_PER_MSEC) /* request skew for wakeup */
+#define DENAP_DROP_LEEWAY  (2 * DENAP_DROP_SKEW)  /* specified wakeup leeway */
+
+#define DENAP_DROP_DELAY (DENAP_DROP_TARGET + DENAP_DROP_SKEW)
+#define DENAP_DROP_FLAGS (THREAD_CALL_DELAY_SYS_NORMAL | THREAD_CALL_DELAY_LEEWAY)
+
+/*
+ * Importance Voucher Attribute Manager
+ */
+
+static lck_spin_t ipc_importance_lock_data;    /* single lock for now */
+
+
+#define ipc_importance_lock_init() \
+       lck_spin_init(&ipc_importance_lock_data, &ipc_lck_grp, &ipc_lck_attr)
+#define ipc_importance_lock_destroy() \
+       lck_spin_destroy(&ipc_importance_lock_data, &ipc_lck_grp)
+#define        ipc_importance_lock() \
+       lck_spin_lock(&ipc_importance_lock_data)
+#define        ipc_importance_lock_try() \
+       lck_spin_try_lock(&ipc_importance_lock_data)
+#define        ipc_importance_unlock() \
+       lck_spin_unlock(&ipc_importance_lock_data)
+#define ipc_importance_sleep(elem) lck_spin_sleep(&ipc_importance_lock_data,   \
+                                       LCK_SLEEP_DEFAULT,                      \
+                                       (event_t)(elem),                        \
+                                       THREAD_UNINT)
+#define ipc_importance_wakeup(elem) thread_wakeup((event_t)(elem))
+
+#if IIE_REF_DEBUG
+#define incr_ref_counter(x) (hw_atomic_add(&(x), 1))
+
+static inline
+uint32_t ipc_importance_reference_internal(ipc_importance_elem_t elem) 
+{
+       incr_ref_counter(elem->iie_refs_added);
+       return (hw_atomic_add(&elem->iie_bits, 1) & IIE_REFS_MASK);
+}
+
+static inline
+uint32_t ipc_importance_release_internal(ipc_importance_elem_t elem)
+{
+       incr_ref_counter(elem->iie_refs_dropped);
+       return (hw_atomic_sub(&elem->iie_bits, 1) & IIE_REFS_MASK);
+}
+
+static inline 
+uint32_t ipc_importance_task_reference_internal(ipc_importance_task_t task_imp)
+{
+  uint32_t out;
+  out = ipc_importance_reference_internal(&task_imp->iit_elem);
+  incr_ref_counter(task_imp->iit_elem.iie_task_refs_added);
+  return out;
+}
+
+static inline
+uint32_t ipc_importance_task_release_internal(ipc_importance_task_t task_imp)
+{
+  uint32_t out;
+
+  assert(1 < IIT_REFS(task_imp));
+  incr_ref_counter(task_imp->iit_elem.iie_task_refs_dropped);
+  out = ipc_importance_release_internal(&task_imp->iit_elem);
+  return out;
+}
+
+static inline
+void ipc_importance_counter_init(ipc_importance_elem_t elem)
+{
+  
+       elem->iie_refs_added = 0;
+       elem->iie_refs_dropped = 0;
+       elem->iie_kmsg_refs_added = 0;
+       elem->iie_kmsg_refs_inherited = 0;
+       elem->iie_kmsg_refs_coalesced = 0;
+       elem->iie_kmsg_refs_dropped = 0;
+       elem->iie_task_refs_added = 0;
+       elem->iie_task_refs_added_inherit_from = 0;
+       elem->iie_task_refs_added_transition = 0;
+       elem->iie_task_refs_self_added = 0;
+       elem->iie_task_refs_inherited = 0;
+       elem->iie_task_refs_coalesced = 0;
+       elem->iie_task_refs_dropped = 0;
+}
+#else
+#define incr_ref_counter(x)
+#endif
+
+#if DEVELOPMENT || DEBUG
+static queue_head_t global_iit_alloc_queue;
+#endif
+
+/* TODO: remove this varibale when interactive daemon audit is complete */
+boolean_t ipc_importance_interactive_receiver = FALSE;
+
+static zone_t ipc_importance_task_zone;
+static zone_t ipc_importance_inherit_zone;
+
+static ipc_voucher_attr_control_t ipc_importance_control;
+
+/*
+ *     Routine:        ipc_importance_kmsg_link
+ *     Purpose:
+ *             Link the kmsg onto the appropriate propagation chain.
+ *             If the element is a task importance, we link directly
+ *             on its propagation chain. Otherwise, we link onto the
+ *             destination task of the inherit.
+ *     Conditions:
+ *             Importance lock held.
+ *             Caller is donating an importance elem reference to the kmsg.
+ */
+static void
+ipc_importance_kmsg_link(
+       ipc_kmsg_t              kmsg,
+       ipc_importance_elem_t   elem)
+{
+       ipc_importance_elem_t link_elem;
+
+       assert(IIE_NULL == kmsg->ikm_importance);
+
+       link_elem = (IIE_TYPE_INHERIT == IIE_TYPE(elem)) ?
+               (ipc_importance_elem_t)((ipc_importance_inherit_t)elem)->iii_to_task :
+               elem;
+
+       queue_enter(&link_elem->iie_kmsgs, kmsg, ipc_kmsg_t, ikm_inheritance);
+       kmsg->ikm_importance = elem;
+}
+
+/*
+ *     Routine:        ipc_importance_kmsg_unlink
+ *     Purpose:
+ *             Unlink the kmsg from its current propagation chain.
+ *             If the element is a task importance, we unlink directly
+ *             from its propagation chain. Otherwise, we unlink from the
+ *             destination task of the inherit.
+ *     Returns:
+ *             The reference to the importance element it was linked on.
+ *     Conditions:
+ *             Importance lock held.
+ *             Caller is responsible for dropping reference on returned elem.
+ */
+static ipc_importance_elem_t
+ipc_importance_kmsg_unlink(
+       ipc_kmsg_t              kmsg)
+{
+       ipc_importance_elem_t elem = kmsg->ikm_importance;
+
+       if (IIE_NULL != elem) {
+               ipc_importance_elem_t unlink_elem;
+
+               unlink_elem = (IIE_TYPE_INHERIT == IIE_TYPE(elem)) ?
+                       (ipc_importance_elem_t)((ipc_importance_inherit_t)elem)->iii_to_task : 
+                       elem;
+
+               queue_remove(&unlink_elem->iie_kmsgs, kmsg, ipc_kmsg_t, ikm_inheritance);
+               kmsg->ikm_importance = IIE_NULL;
+       }
+       return elem;
+}
+
+/*
+ *     Routine:        ipc_importance_inherit_link
+ *     Purpose:
+ *             Link the inherit onto the appropriate propagation chain.
+ *             If the element is a task importance, we link directly
+ *             on its propagation chain. Otherwise, we link onto the
+ *             destination task of the inherit.
+ *     Conditions:
+ *             Importance lock held.
+ *             Caller is donating an elem importance reference to the inherit.
+ */
+static void
+ipc_importance_inherit_link(
+       ipc_importance_inherit_t inherit,
+       ipc_importance_elem_t elem)
+{
+       ipc_importance_elem_t link_elem;
+
+       assert(IIE_NULL == inherit->iii_from_elem);
+       link_elem = (IIE_TYPE_INHERIT == IIE_TYPE(elem)) ?
+               (ipc_importance_elem_t)((ipc_importance_inherit_t)elem)->iii_to_task :
+               elem;
+
+       queue_enter(&link_elem->iie_inherits, inherit, 
+                   ipc_importance_inherit_t, iii_inheritance);
+       inherit->iii_from_elem = elem;
+}
+
+/*
+ *     Routine:        ipc_importance_inherit_unlink
+ *     Purpose:
+ *             Unlink the inherit from its current propagation chain.
+ *             If the element is a task importance, we unlink directly
+ *             from its propagation chain. Otherwise, we unlink from the
+ *             destination task of the inherit.
+ *     Returns:
+ *             The reference to the importance element it was linked on.
+ *     Conditions:
+ *             Importance lock held.
+ *             Caller is responsible for dropping reference on returned elem.
+ */
+static ipc_importance_elem_t
+ipc_importance_inherit_unlink(
+       ipc_importance_inherit_t inherit)
+{
+       ipc_importance_elem_t elem = inherit->iii_from_elem;
+
+       if (IIE_NULL != elem) {
+               ipc_importance_elem_t unlink_elem;
+
+               unlink_elem = (IIE_TYPE_INHERIT == IIE_TYPE(elem)) ?
+                       (ipc_importance_elem_t)((ipc_importance_inherit_t)elem)->iii_to_task : 
+                       elem;
+
+               queue_remove(&unlink_elem->iie_inherits, inherit, 
+                            ipc_importance_inherit_t, iii_inheritance);
+               inherit->iii_from_elem = IIE_NULL;
+       }
+       return elem;
+}
+
+/*
+ *     Routine:        ipc_importance_reference
+ *     Purpose:
+ *             Add a reference to the importance element.
+ *     Conditions:
+ *             Caller must hold a reference on the element.
+ */
+void
+ipc_importance_reference(ipc_importance_elem_t elem)
+{
+       assert(0 < IIE_REFS(elem));
+       ipc_importance_reference_internal(elem);
+}
+
+/*
+ *     Routine:        ipc_importance_release_locked
+ *     Purpose:
+ *             Release a reference on an importance attribute value,
+ *             unlinking and deallocating the attribute if the last reference.
+ *     Conditions:
+ *             Entered with importance lock held, leaves with it unlocked.
+ */
+static void
+ipc_importance_release_locked(ipc_importance_elem_t elem)
+{
+       assert(0 < IIE_REFS(elem));
+
+       if (0 < ipc_importance_release_internal(elem)) {
+
+#if DEVELOPMENT || DEBUG
+               ipc_importance_inherit_t temp_inherit;
+               ipc_importance_task_t link_task;
+               ipc_kmsg_t temp_kmsg;
+               uint32_t expected = 0;
+
+               if (0 < elem->iie_made)
+                       expected++;
+
+               link_task = (IIE_TYPE_INHERIT == IIE_TYPE(elem)) ?
+                       ((ipc_importance_inherit_t)elem)->iii_to_task : 
+                       (ipc_importance_task_t)elem;
+
+               queue_iterate(&link_task->iit_kmsgs, temp_kmsg, ipc_kmsg_t, ikm_inheritance)
+                       if (temp_kmsg->ikm_importance == elem)
+                               expected++;
+               queue_iterate(&link_task->iit_inherits, temp_inherit,
+                             ipc_importance_inherit_t, iii_inheritance)
+                       if (temp_inherit->iii_from_elem == elem)
+                               expected++;
+
+               if (IIE_REFS(elem) < expected)
+                       panic("ipc_importance_release_locked (%p)", elem);
+#endif
+               ipc_importance_unlock();
+               return;
+       }
+
+       /* last ref */
+       /* can't get to no refs if we contribute to something else's importance */
+       assert(queue_empty(&elem->iie_kmsgs));
+       assert(queue_empty(&elem->iie_inherits));
+
+       switch (IIE_TYPE(elem)) {
+
+       /* just a "from" task reference to drop */
+       case IIE_TYPE_TASK:
+       {
+               ipc_importance_task_t task_elem;
+
+               task_elem = (ipc_importance_task_t)elem;
+               assert(TASK_NULL == task_elem->iit_task);
+
+#if DEVELOPMENT || DEBUG
+               queue_remove(&global_iit_alloc_queue, task_elem, ipc_importance_task_t, iit_allocation);
+#endif
+
+               ipc_importance_unlock();
+
+               zfree(ipc_importance_task_zone, task_elem);
+               break;
+       }
+
+       /* dropping an inherit element */
+       case IIE_TYPE_INHERIT:
+       {
+               ipc_importance_inherit_t inherit;
+               ipc_importance_elem_t from_elem;
+               ipc_importance_task_t to_task;
+
+
+               inherit = (ipc_importance_inherit_t)elem;
+               to_task = inherit->iii_to_task;
+               assert(IIT_NULL != to_task);
+               assert(!inherit->iii_donating);
+
+               /* unlink and release the inherit */
+               assert(ipc_importance_task_is_any_receiver_type(to_task));
+               from_elem = ipc_importance_inherit_unlink(inherit);
+               assert(IIE_NULL != from_elem);
+               ipc_importance_release_locked(from_elem);
+               /* unlocked on return */
+
+               ipc_importance_task_release(to_task);
+
+               zfree(ipc_importance_inherit_zone, inherit);
+               break;
+       }
+       }
+}
+
+/*
+ *     Routine:        ipc_importance_release
+ *     Purpose:
+ *             Release a reference on an importance attribute value,
+ *             unlinking and deallocating the attribute if the last reference.
+ *     Conditions:
+ *             nothing locked on entrance, nothing locked on exit.
+ *             May block.
+ */
+void
+ipc_importance_release(ipc_importance_elem_t elem)
+{
+       if (IIE_NULL == elem)
+               return;
+
+       ipc_importance_lock();
+       ipc_importance_release_locked(elem);
+       /* unlocked */
+}
+
+/*
+ *     Routine:        ipc_importance_task_reference
+
+
+ *     Purpose:
+ *             Retain a reference on a task importance attribute value.
+ *     Conditions:
+ *             nothing locked on entrance, nothing locked on exit.
+ *             caller holds a reference already.
+ */
+void
+ipc_importance_task_reference(ipc_importance_task_t task_elem)
+{
+       if (IIT_NULL == task_elem)
+               return;
+#if IIE_REF_DEBUG
+       incr_ref_counter(task_elem->iit_elem.iie_task_refs_added);
+#endif
+       ipc_importance_reference(&task_elem->iit_elem);
+}
+
+/*
+ *     Routine:        ipc_importance_task_release
+ *     Purpose:
+ *             Release a reference on a task importance attribute value,
+ *             unlinking and deallocating the attribute if the last reference.
+ *     Conditions:
+ *             nothing locked on entrance, nothing locked on exit.
+ *             May block.
+ */
+void
+ipc_importance_task_release(ipc_importance_task_t task_elem)
+{
+       if (IIT_NULL == task_elem)
+               return;
+
+       ipc_importance_lock();
+#if IIE_REF_DEBUG
+       incr_ref_counter(task_elem->iit_elem.iie_task_refs_dropped);
+#endif
+       ipc_importance_release_locked(&task_elem->iit_elem);
+       /* unlocked */
+}
+
+/*
+ *     Routine:        ipc_importance_task_release_locked
+ *     Purpose:
+ *             Release a reference on a task importance attribute value,
+ *             unlinking and deallocating the attribute if the last reference.
+ *     Conditions:
+ *             importance lock held on entry, nothing locked on exit.
+ *             May block.
+ */
+static void
+ipc_importance_task_release_locked(ipc_importance_task_t task_elem)
+{
+       if (IIT_NULL == task_elem) {
+               ipc_importance_unlock();
+               return;
+       }
+#if IIE_REF_DEBUG
+       incr_ref_counter(task_elem->iit_elem.iie_task_refs_dropped);
+#endif
+       ipc_importance_release_locked(&task_elem->iit_elem);
+       /* unlocked */
+}
+
+/*
+ * Routines for importance donation/inheritance/boosting
+ */
+
+
+/*
+ * External importance assertions are managed by the process in userspace
+ * Internal importance assertions are the responsibility of the kernel
+ * Assertions are changed from internal to external via task_importance_externalize_assertion
+ */
+
+/*
+ *     Routine:        ipc_importance_task_check_transition
+ *     Purpose:
+ *             Increase or decrement the internal task importance counter of the
+ *             specified task and determine if propagation and a task policy
+ *             update is required.
+ *
+ *             If it is already enqueued for a policy update, steal it from that queue
+ *             (as we are reversing that update before it happens).
+ *
+ *     Conditions:
+ *             Called with the importance lock held.
+ *             It is the caller's responsibility to perform the propagation of the
+ *             transition and/or policy changes by checking the return value.
+ */
+static boolean_t
+ipc_importance_task_check_transition(
+       ipc_importance_task_t task_imp,
+       iit_update_type_t type,
+       uint32_t delta)
+{
+
+       task_t target_task = task_imp->iit_task;
+       boolean_t boost = (IIT_UPDATE_HOLD == type);
+       boolean_t before_boosted, after_boosted;
+
+       if (!ipc_importance_task_is_any_receiver_type(task_imp))
+               return FALSE;
+
+#if IMPORTANCE_DEBUG
+       int target_pid = (TASK_NULL != target_task) ? audit_token_pid_from_task(target_task) : -1;
+
+       KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, (((boost) ? IMP_HOLD : IMP_DROP) | TASK_POLICY_INTERNAL))) | DBG_FUNC_START,
+                                         proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_EXTERN(task_imp), 0);
+#endif
+
+       /* snapshot the effective boosting status before making any changes */
+       before_boosted = (task_imp->iit_assertcnt > 0);
+
+       /* Adjust the assertcnt appropriately */
+       if (boost) {
+               task_imp->iit_assertcnt += delta;
+#if IMPORTANCE_DEBUG
+        DTRACE_BOOST6(send_boost, task_t, target_task, int, target_pid,
+                      task_t, current_task(), int, proc_selfpid(), int, delta, int, task_imp->iit_assertcnt);
+#endif
+       } else {
+               // assert(delta <= task_imp->iit_assertcnt);
+               if (delta > task_imp->iit_assertcnt - IIT_EXTERN(task_imp)) {
+                       /* TODO: Turn this back into a panic <rdar://problem/12592649> */
+                       if (target_task != TASK_NULL) {
+                               printf("Over-release of kernel-internal importance assertions for pid %d (%s), "
+                                      "dropping %d assertion(s) but task only has %d remaining (%d external).\n",
+                                      audit_token_pid_from_task(target_task),
+                                      (target_task->bsd_info == NULL) ? "" : proc_name_address(target_task->bsd_info),
+                                      delta,
+                                      task_imp->iit_assertcnt,
+                                      IIT_EXTERN(task_imp));
+                       }
+                       task_imp->iit_assertcnt = IIT_EXTERN(task_imp);
+               } else {
+                       task_imp->iit_assertcnt -= delta;
+               }
+#if IMPORTANCE_DEBUG
+               // This convers both legacy and voucher-based importance.
+               DTRACE_BOOST4(drop_boost, task_t, target_task, int, target_pid, int, delta, int, task_imp->iit_assertcnt);
+#endif
+       }
+
+#if IMPORTANCE_DEBUG
+       KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, (((boost) ? IMP_HOLD : IMP_DROP) | TASK_POLICY_INTERNAL))) | DBG_FUNC_END,
+                                 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_EXTERN(task_imp), 0);
+#endif
+
+       /* did the change result in an effective donor status change? */
+       after_boosted = (task_imp->iit_assertcnt > 0);
+
+       if (after_boosted != before_boosted) {
+
+               /* 
+                * If the task importance is already on an update queue, we just reversed the need for a
+                * pending policy update.  If the queue is any other than the delayed-drop-queue, pull it 
+                * off that queue and release the reference it got going onto the update queue.  If it is
+                * the delayed-drop-queue we leave it in place in case it comes back into the drop state
+                * before its time delay is up.
+                *
+                * We still need to propagate the change downstream to reverse the assertcnt effects,
+                * but we no longer need to update this task's boost policy state.
+                *
+                * Otherwise, mark it as needing a policy update.
+                */
+               assert(0 == task_imp->iit_updatepolicy);
+               if (NULL != task_imp->iit_updateq) {
+                       if (&ipc_importance_delayed_drop_queue != task_imp->iit_updateq) {
+                               queue_remove(task_imp->iit_updateq, task_imp, ipc_importance_task_t, iit_updates);
+                               task_imp->iit_updateq = NULL;
+                               ipc_importance_task_release_internal(task_imp); /* can't be last ref */
+                       }
+               } else {
+                       task_imp->iit_updatepolicy = 1;
+               }
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+/*
+ *     Routine:        ipc_importance_task_propagate_helper
+ *     Purpose:
+ *             Increase or decrement the internal task importance counter of all
+ *             importance tasks inheriting from the specified one.  If this causes
+ *             that importance task to change state, add it to the list of tasks
+ *             to do a policy update against.
+ *     Conditions:
+ *             Called with the importance lock held.
+ *             It is the caller's responsibility to iterate down the generated list
+ *             and propagate any subsequent assertion changes from there.
+ */
+static void
+ipc_importance_task_propagate_helper(
+       ipc_importance_task_t task_imp,
+       iit_update_type_t type,
+       queue_t propagation)
+{
+       ipc_importance_task_t temp_task_imp;
+
+       /* 
+        * iterate the downstream kmsgs, adjust their boosts, 
+        * and capture the next task to adjust for each message
+        */
+
+       ipc_kmsg_t temp_kmsg;
+
+       queue_iterate(&task_imp->iit_kmsgs, temp_kmsg, ipc_kmsg_t, ikm_inheritance) {
+               mach_msg_header_t *hdr = temp_kmsg->ikm_header;
+               mach_port_delta_t delta;
+               ipc_port_t port;
+
+               /* toggle the kmsg importance bit as a barrier to parallel adjusts */
+               if (IIT_UPDATE_HOLD == type) {
+                       if (MACH_MSGH_BITS_RAISED_IMPORTANCE(hdr->msgh_bits)) {
+                               continue;
+                       }
+
+                       /* mark the message as now carrying importance */
+                       hdr->msgh_bits |= MACH_MSGH_BITS_RAISEIMP;
+                       delta = 1;
+               } else {
+                       if (!MACH_MSGH_BITS_RAISED_IMPORTANCE(hdr->msgh_bits)) {
+                               continue;
+                       }
+
+                       /* clear the message as now carrying importance */
+                       hdr->msgh_bits &= ~MACH_MSGH_BITS_RAISEIMP;
+                       delta = -1;
+               }
+
+               /* determine the task importance to adjust as result (if any) */
+               port = (ipc_port_t) hdr->msgh_remote_port;
+               assert(IP_VALID(port));
+               ip_lock(port);
+               temp_task_imp = IIT_NULL;
+               if (!ipc_port_importance_delta_internal(port, &delta, &temp_task_imp)) {
+                       ip_unlock(port);
+               }
+
+               /* no task importance to adjust associated with the port? */
+               if (IIT_NULL == temp_task_imp) {
+                       continue;
+               }
+
+               /* hold a reference on temp_task_imp */
+
+               /* Adjust the task assertions and determine if an edge was crossed */
+               if (ipc_importance_task_check_transition(temp_task_imp, type, 1)) {
+                       incr_ref_counter(task_imp->iit_elem.iie_task_refs_added_transition);
+                       queue_enter(propagation, temp_task_imp, ipc_importance_task_t, iit_props);
+                       /* reference donated */
+               } else {
+                       ipc_importance_task_release_internal(temp_task_imp);
+               }
+       }
+
+       /*
+        * iterate the downstream importance inherits
+        * and capture the next task importance to boost for each
+        */
+       ipc_importance_inherit_t temp_inherit;
+
+       queue_iterate(&task_imp->iit_inherits, temp_inherit, ipc_importance_inherit_t, iii_inheritance) {
+               uint32_t assertcnt = III_EXTERN(temp_inherit); 
+
+               temp_task_imp = temp_inherit->iii_to_task;
+               assert(IIT_NULL != temp_task_imp);
+
+               if (IIT_UPDATE_HOLD == type) {
+                       /* if no undropped externcnts in the inherit, nothing to do */
+                       if (0 == assertcnt) {
+                               assert(temp_inherit->iii_donating == FALSE);
+                               continue;
+                       }
+
+                       /* nothing to do if the inherit is already donating (forced donation) */
+                       if (temp_inherit->iii_donating) {
+                               continue;
+                       }
+
+                       /* mark it donating and contribute to the task externcnts */
+                       temp_inherit->iii_donating = TRUE;
+                       temp_task_imp->iit_externcnt += temp_inherit->iii_externcnt;
+                       temp_task_imp->iit_externdrop += temp_inherit->iii_externdrop;
+
+               } else {
+                       /* if no contributing assertions, move on */
+                       if (0 == assertcnt) {
+                               assert(temp_inherit->iii_donating == FALSE);
+                               continue;
+                       }
+
+                       /* nothing to do if the inherit is not donating */
+                       if (!temp_inherit->iii_donating) { 
+                               continue;
+                       }
+
+                       /* mark it no longer donating */
+                       temp_inherit->iii_donating = FALSE;
+
+                       /* remove the contribution the inherit made to the to-task */
+                       assert(IIT_EXTERN(temp_task_imp) >= III_EXTERN(temp_inherit));
+                       assert(temp_task_imp->iit_externcnt >= temp_inherit->iii_externcnt);
+                       assert(temp_task_imp->iit_externdrop >= temp_inherit->iii_externdrop);
+                       temp_task_imp->iit_externcnt -= temp_inherit->iii_externcnt;
+                       temp_task_imp->iit_externdrop -= temp_inherit->iii_externdrop;
+
+               }
+
+               /* Adjust the task assertions and determine if an edge was crossed */
+               assert(ipc_importance_task_is_any_receiver_type(temp_task_imp));
+               if (ipc_importance_task_check_transition(temp_task_imp, type, assertcnt)) {
+                       ipc_importance_task_reference(temp_task_imp);
+                       incr_ref_counter(task_imp->iit_elem.iie_task_refs_added_transition);
+                       queue_enter(propagation, temp_task_imp, ipc_importance_task_t, iit_props);
+               } 
+       }
+}
+
+/*
+ *     Routine:        ipc_importance_task_process_updates
+ *     Purpose:
+ *             Process the queue of task importances and apply the policy
+ *             update called for.  Only process tasks in the queue with an
+ *             update timestamp less than the supplied max.
+ *     Conditions:
+ *             Called and returns with importance locked.
+ *             May drop importance lock and block temporarily.
+ */
+static void
+ipc_importance_task_process_updates(
+       queue_t   supplied_queue,
+       boolean_t boost,
+       uint64_t  max_timestamp)
+{
+       ipc_importance_task_t task_imp;
+       queue_head_t second_chance;
+       queue_t queue = supplied_queue;
+
+       /*
+        * This queue will hold the task's we couldn't trylock on first pass.
+        * By using a second (private) queue, we guarantee all tasks that get
+        * entered on this queue have a timestamp under the maximum.
+        */
+       queue_init(&second_chance);
+
+       /* process any resulting policy updates */
+ retry:
+       while(!queue_empty(queue)) {
+               task_t target_task;
+               struct task_pend_token pend_token = {};
+
+               task_imp = (ipc_importance_task_t)queue_first(queue);
+               assert(0 == task_imp->iit_updatepolicy);
+               assert(queue == task_imp->iit_updateq);
+
+               /* if timestamp is too big, we're done */
+               if (task_imp->iit_updatetime > max_timestamp) {
+                       break;
+               }
+
+               /* we were given a reference on each task in the queue */
+
+               /* remove it from the supplied queue */
+               queue_remove(queue, task_imp, ipc_importance_task_t, iit_updates);
+               task_imp->iit_updateq = NULL;
+
+               target_task = task_imp->iit_task;
+
+               /* Is it well on the way to exiting? */
+               if (TASK_NULL == target_task) {
+                       ipc_importance_task_release_locked(task_imp);
+                       /* importance unlocked */
+                       ipc_importance_lock();
+                       continue;
+               }
+
+               /* Has the update been reversed on the hysteresis queue? */
+               if (0 < task_imp->iit_assertcnt &&
+                   queue == &ipc_importance_delayed_drop_queue) {
+                       ipc_importance_task_release_locked(task_imp);
+                       /* importance unlocked */
+                       ipc_importance_lock();
+                       continue;
+               }
+
+               /* 
+                * Can we get the task lock out-of-order? 
+                * If not, stick this back on the second-chance queue.
+                */
+               if (!task_lock_try(target_task)) {
+                       boolean_t should_wait_lock = (queue == &second_chance);
+                       task_imp->iit_updateq = &second_chance;
+               
+                       /*
+                        * If we're already processing second-chances on
+                        * tasks, keep this task on the front of the queue.
+                        * We will wait for the task lock before coming
+                        * back and trying again, and we have a better
+                        * chance of re-acquiring the lock if we come back
+                        * to it right away.
+                        */
+                       if (should_wait_lock){
+                               task_reference(target_task);
+                               queue_enter_first(&second_chance, task_imp,
+                                                 ipc_importance_task_t, iit_updates);
+                       } else {
+                               queue_enter(&second_chance, task_imp, 
+                                           ipc_importance_task_t, iit_updates);
+                       }
+                       ipc_importance_unlock();
+                       
+                       if (should_wait_lock) {
+                               task_lock(target_task);
+                               task_unlock(target_task);
+                               task_deallocate(target_task);
+                       }
+
+                       ipc_importance_lock();
+                       continue;
+               }
+
+               /* is it going away? */
+               if (!target_task->active) {
+                       task_unlock(target_task);
+                       ipc_importance_task_release_locked(task_imp);
+                       /* importance unlocked */
+                       ipc_importance_lock();
+                       continue;
+               }
+
+               /* take a task reference for while we don't have the importance lock */
+               task_reference(target_task);
+
+               /* count the transition */
+               if (boost)
+                       task_imp->iit_transitions++;
+
+               ipc_importance_unlock();
+
+               /* apply the policy adjust to the target task (while it is still locked) */
+               task_update_boost_locked(target_task, boost, &pend_token);
+
+               /* complete the policy update with the task unlocked */
+               ipc_importance_task_release(task_imp);
+               task_unlock(target_task);
+               task_policy_update_complete_unlocked(target_task, THREAD_NULL, &pend_token);
+               task_deallocate(target_task);
+
+               ipc_importance_lock();
+       }
+
+       /* If there are tasks we couldn't update the first time, try again */
+       if (!queue_empty(&second_chance)) {
+               queue = &second_chance;
+               goto retry;
+       }
+}
+
+
+/*
+ *     Routine:        ipc_importance_task_delayed_drop_scan
+ *     Purpose:
+ *             The thread call routine to scan the delayed drop queue,
+ *             requesting all updates with a deadline up to the last target
+ *             for the thread-call (which is DENAP_DROP_SKEW beyond the first
+ *             thread's optimum delay).
+ *             update to drop its boost.
+ *     Conditions:
+ *             Nothing locked
+ */
+static void
+ipc_importance_task_delayed_drop_scan(
+       __unused void *arg1,
+       __unused void *arg2) 
+{
+       ipc_importance_lock();
+
+       /* process all queued task drops with timestamps up to TARGET(first)+SKEW */
+       ipc_importance_task_process_updates(&ipc_importance_delayed_drop_queue, 
+                                           FALSE,
+                                           ipc_importance_delayed_drop_timestamp);
+
+       /* importance lock may have been temporarily dropped */
+
+       /* If there are any entries left in the queue, re-arm the call here */
+       if (!queue_empty(&ipc_importance_delayed_drop_queue)) {
+               ipc_importance_task_t task_imp;
+               uint64_t deadline;
+               uint64_t leeway;
+
+               task_imp = (ipc_importance_task_t)queue_first(&ipc_importance_delayed_drop_queue);
+
+               nanoseconds_to_absolutetime(DENAP_DROP_DELAY, &deadline);
+               deadline += task_imp->iit_updatetime;
+               ipc_importance_delayed_drop_timestamp = deadline;
+
+               nanoseconds_to_absolutetime(DENAP_DROP_LEEWAY, &leeway);
+
+               thread_call_enter_delayed_with_leeway(
+                                       ipc_importance_delayed_drop_call,
+                                       NULL,
+                                       deadline,
+                                       leeway,
+                                       DENAP_DROP_FLAGS);
+       } else {
+               ipc_importance_delayed_drop_call_requested = FALSE;
+       }
+       ipc_importance_unlock();
+}
+
+/*
+ *     Routine:        ipc_importance_task_delayed_drop
+ *     Purpose:
+ *             Queue the specified task importance for delayed policy
+ *             update to drop its boost.
+ *     Conditions:
+ *             Called with the importance lock held.
+ */
+static void
+ipc_importance_task_delayed_drop(ipc_importance_task_t task_imp)
+{
+       uint64_t timestamp = mach_absolute_time(); /* no mach_approximate_time() in kernel */
+
+       assert(ipc_importance_delayed_drop_call != NULL);
+
+       /*
+        * If still on an update queue from a previous change,
+        * remove it first (and use that reference).  Otherwise, take
+        * a new reference for the delay drop update queue.
+        */
+       if (NULL != task_imp->iit_updateq) {
+               queue_remove(task_imp->iit_updateq, task_imp, 
+                            ipc_importance_task_t, iit_updates);
+       } else {
+               ipc_importance_task_reference_internal(task_imp);
+       }
+
+       task_imp->iit_updateq = &ipc_importance_delayed_drop_queue;
+       task_imp->iit_updatetime = timestamp;
+
+       queue_enter(&ipc_importance_delayed_drop_queue, task_imp, 
+                   ipc_importance_task_t, iit_updates);
+
+       /* request the delayed thread-call if not already requested */
+       if (!ipc_importance_delayed_drop_call_requested) {
+               uint64_t deadline;
+               uint64_t leeway;
+
+               nanoseconds_to_absolutetime(DENAP_DROP_DELAY, &deadline);
+               deadline += task_imp->iit_updatetime;
+               ipc_importance_delayed_drop_timestamp = deadline;
+
+               nanoseconds_to_absolutetime(DENAP_DROP_LEEWAY, &leeway);
+
+               ipc_importance_delayed_drop_call_requested = TRUE;
+               thread_call_enter_delayed_with_leeway(
+                                       ipc_importance_delayed_drop_call,
+                                       NULL,
+                                       deadline,
+                                       leeway,
+                                       DENAP_DROP_FLAGS);
+       }
+}
+
+
+/*
+ *     Routine:        ipc_importance_task_propagate_assertion_locked
+ *     Purpose:
+ *             Propagate the importance transition type to every item
+ *             If this causes a boost to be applied, determine if that
+ *             boost should propagate downstream.
+ *     Conditions:
+ *             Called with the importance lock held.
+ */
+static void
+ipc_importance_task_propagate_assertion_locked(
+       ipc_importance_task_t task_imp,
+       iit_update_type_t type,
+       boolean_t update_task_imp)
+{
+       boolean_t boost = (IIT_UPDATE_HOLD == type);
+       ipc_importance_task_t temp_task_imp;
+       queue_head_t propagate;
+       queue_head_t updates;
+
+       queue_init(&updates);
+       queue_init(&propagate);
+
+       /*
+        * If we're going to update the policy for the provided task,
+        * enqueue it on the propagate queue itself.  Otherwise, only
+        * enqueue downstream things.
+        */
+       if (update_task_imp) {
+               queue_enter(&propagate, task_imp, ipc_importance_task_t, iit_props);
+       } else {
+               ipc_importance_task_propagate_helper(task_imp, type, &propagate);
+       }
+
+       /*
+        * for each item on the propagation list, propagate any change downstream,
+        * adding new tasks to propagate further if they transistioned as well.
+        */
+       while (!queue_empty(&propagate)) {
+               boolean_t need_update;
+
+               queue_remove_first(&propagate, temp_task_imp, ipc_importance_task_t, iit_props);
+               assert(IIT_NULL != temp_task_imp);
+
+               /* only propagate for receivers not already marked as a donor */
+               if (!ipc_importance_task_is_marked_donor(temp_task_imp) &&
+                   ipc_importance_task_is_marked_receiver(temp_task_imp)) {
+                       ipc_importance_task_propagate_helper(temp_task_imp, type, &propagate);
+               }
+
+               /* if we have a policy update to apply, enqueue a reference for later processing */
+               need_update = (0 != temp_task_imp->iit_updatepolicy);
+               temp_task_imp->iit_updatepolicy = 0;
+               if (need_update && TASK_NULL != temp_task_imp->iit_task) {
+                       if (NULL == temp_task_imp->iit_updateq) {
+                               temp_task_imp->iit_updatetime = 0;
+                               temp_task_imp->iit_updateq = &updates;
+                               ipc_importance_task_reference_internal(temp_task_imp);
+                               if (boost) {
+                                       queue_enter(&updates, temp_task_imp,
+                                                   ipc_importance_task_t, iit_updates);
+                               } else {
+                                       queue_enter_first(&updates, temp_task_imp,
+                                                         ipc_importance_task_t, iit_updates);
+                               }
+                       } else {
+                               /* Must already be on the AppNap hysteresis queue */
+                               assert(&ipc_importance_delayed_drop_queue);
+                               assert(ipc_importance_task_is_marked_denap_receiver(temp_task_imp));
+                       }       
+               }
+       }
+
+       /* apply updates to task (may drop importance lock) */
+       if (!queue_empty(&updates)) {
+               ipc_importance_task_process_updates(&updates, boost, 0);
+       }
+}
+
+/*
+ *     Routine:        ipc_importance_task_hold_internal_assertion_locked
+ *     Purpose:
+ *             Increment the assertion count on the task importance.
+ *             If this results in a boost state change in that task,
+ *             prepare to update task policy for this task AND, if
+ *             if not just waking out of App Nap, all down-stream
+ *             tasks that have a similar transition through inheriting
+ *             this update.
+ *     Conditions:
+ *             importance locked on entry and exit.
+ *             May temporarily drop importance lock and block.
+ */
+static kern_return_t
+ipc_importance_task_hold_internal_assertion_locked(ipc_importance_task_t task_imp, uint32_t count)
+{
+       if (ipc_importance_task_check_transition(task_imp, IIT_UPDATE_HOLD, count)) {
+               ipc_importance_task_propagate_assertion_locked(task_imp, IIT_UPDATE_HOLD, TRUE);
+       }
+       return KERN_SUCCESS;
+}
+
+/*
+ *     Routine:        ipc_importance_task_drop_internal_assertion_locked
+ *     Purpose:
+ *             Decrement the assertion count on the task importance.
+ *             If this results in a boost state change in that task,
+ *             prepare to update task policy for this task AND, if
+ *             if not just waking out of App Nap, all down-stream
+ *             tasks that have a similar transition through inheriting
+ *             this update.
+ *     Conditions:
+ *             importance locked on entry and exit.
+ *             May temporarily drop importance lock and block.
+ */
+static kern_return_t
+ipc_importance_task_drop_internal_assertion_locked(ipc_importance_task_t task_imp, uint32_t count)
+{
+       if (ipc_importance_task_check_transition(task_imp, IIT_UPDATE_DROP, count)) {
+               ipc_importance_task_propagate_assertion_locked(task_imp, IIT_UPDATE_DROP, TRUE);
+       }
+       return KERN_SUCCESS;
+}
+
+/*
+ *      Routine:        ipc_importance_task_hold_internal_assertion
+ *      Purpose:
+ *              Increment the assertion count on the task importance.
+ *              If this results in a 0->1 change in that count,
+ *              prepare to update task policy for this task AND
+ *              (potentially) all down-stream tasks that have a
+ *             similar transition through inheriting this update.
+ *      Conditions:
+ *              Nothing locked
+ *              May block after dropping importance lock.
+ */
+int
+ipc_importance_task_hold_internal_assertion(ipc_importance_task_t task_imp, uint32_t count)
+{
+       int ret = KERN_SUCCESS;
+
+       if (ipc_importance_task_is_any_receiver_type(task_imp)) {
+               ipc_importance_lock();
+               ret = ipc_importance_task_hold_internal_assertion_locked(task_imp, count);
+               ipc_importance_unlock();
+       }
+       return ret;
+}
+
+/*
+ *     Routine:        ipc_importance_task_drop_internal_assertion
+ *     Purpose:
+ *             Decrement the assertion count on the task importance.
+ *             If this results in a X->0 change in that count,
+ *             prepare to update task policy for this task AND
+ *             all down-stream tasks that have a similar transition
+ *             through inheriting this drop update.
+ *     Conditions:
+ *             Nothing locked on entry.
+ *             May block after dropping importance lock.
+ */
+kern_return_t
+ipc_importance_task_drop_internal_assertion(ipc_importance_task_t task_imp, uint32_t count)
+{
+       kern_return_t ret = KERN_SUCCESS;
+
+       if (ipc_importance_task_is_any_receiver_type(task_imp)) {
+               ipc_importance_lock();
+               ret = ipc_importance_task_drop_internal_assertion_locked(task_imp, count);
+               ipc_importance_unlock();
+       }
+       return ret;
+}
+
+/*
+ *      Routine:        ipc_importance_task_hold_file_lock_assertion
+ *      Purpose:
+ *              Increment the file lock assertion count on the task importance.
+ *              If this results in a 0->1 change in that count,
+ *              prepare to update task policy for this task AND
+ *              (potentially) all down-stream tasks that have a
+ *             similar transition through inheriting this update.
+ *      Conditions:
+ *              Nothing locked
+ *              May block after dropping importance lock.
+ */
+kern_return_t
+ipc_importance_task_hold_file_lock_assertion(ipc_importance_task_t task_imp, uint32_t count)
+{
+       kern_return_t ret = KERN_SUCCESS;
+
+       if (ipc_importance_task_is_any_receiver_type(task_imp)) {
+               ipc_importance_lock();
+               ret = ipc_importance_task_hold_internal_assertion_locked(task_imp, count);
+               if (KERN_SUCCESS == ret) {
+                       task_imp->iit_filelocks += count;
+               }
+               ipc_importance_unlock();
+       }
+       return ret;
+}
+
+/*
+ *     Routine:        ipc_importance_task_drop_file_lock_assertion
+ *     Purpose:
+ *             Decrement the assertion count on the task importance.
+ *             If this results in a X->0 change in that count,
+ *             prepare to update task policy for this task AND
+ *             all down-stream tasks that have a similar transition
+ *             through inheriting this drop update.
+ *     Conditions:
+ *             Nothing locked on entry.
+ *             May block after dropping importance lock.
+ */
+kern_return_t
+ipc_importance_task_drop_file_lock_assertion(ipc_importance_task_t task_imp, uint32_t count)
+{
+       kern_return_t ret = KERN_SUCCESS;
+
+       if (ipc_importance_task_is_any_receiver_type(task_imp)) {
+               ipc_importance_lock();
+               if (count <= task_imp->iit_filelocks) {
+                       task_imp->iit_filelocks -= count;
+                       ret = ipc_importance_task_drop_internal_assertion_locked(task_imp, count);
+               } else {
+                       ret = KERN_INVALID_ARGUMENT;
+               }
+               ipc_importance_unlock();
+       }
+       return ret;
+}
+
+/*
+ *     Routine:        ipc_importance_task_hold_legacy_external_assertion
+ *     Purpose:
+ *             Increment the external assertion count on the task importance.
+ *             This cannot result in an 0->1 transition, as the caller must
+ *             already hold an external boost.
+ *     Conditions:
+ *             Nothing locked on entry.
+ *             May block after dropping importance lock.
+ *             A queue of task importance structures is returned
+ *             by ipc_importance_task_hold_assertion_locked(). Each
+ *             needs to be updated (outside the importance lock hold).
+ */
+kern_return_t
+ipc_importance_task_hold_legacy_external_assertion(ipc_importance_task_t task_imp, uint32_t count)
+{
+       task_t target_task;
+       uint32_t target_assertcnt;
+       uint32_t target_externcnt;
+       uint32_t target_legacycnt;
+
+       kern_return_t ret;
+
+       ipc_importance_lock();
+       target_task = task_imp->iit_task;
+
+#if IMPORTANCE_DEBUG
+       int target_pid = (TASK_NULL != target_task) ? audit_token_pid_from_task(target_task) : -1;
+
+       KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, (IMP_HOLD | TASK_POLICY_EXTERNAL))) | DBG_FUNC_START,
+               proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_LEGACY_EXTERN(task_imp), 0);
+#endif
+
+       if (IIT_LEGACY_EXTERN(task_imp) == 0) {
+               /* Only allowed to take a new boost assertion when holding an external boost */
+               /* save data for diagnostic printf below */
+               target_assertcnt = task_imp->iit_assertcnt;
+               target_externcnt = IIT_EXTERN(task_imp);
+               target_legacycnt = IIT_LEGACY_EXTERN(task_imp);
+               ret = KERN_FAILURE;
+               count = 0;
+       } else {
+               assert(ipc_importance_task_is_any_receiver_type(task_imp));
+               assert(0 < task_imp->iit_assertcnt);
+               assert(0 < IIT_EXTERN(task_imp));
+               task_imp->iit_assertcnt += count;
+               task_imp->iit_externcnt += count;
+               task_imp->iit_legacy_externcnt += count;
+               ret = KERN_SUCCESS;
+       }
+       ipc_importance_unlock();
+
+#if IMPORTANCE_DEBUG
+       KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, (IMP_HOLD | TASK_POLICY_EXTERNAL))) | DBG_FUNC_END,
+                                 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_LEGACY_EXTERN(task_imp), 0);
+        // This covers the legacy case where a task takes an extra boost.
+       DTRACE_BOOST5(receive_boost, task_t, target_task, int, target_pid, int, proc_selfpid(), int, count, int, task_imp->iit_assertcnt);
+#endif
+
+       if (KERN_FAILURE == ret && target_task != TASK_NULL) {
+               printf("BUG in process %s[%d]: "
+                      "attempt to acquire an additional legacy external boost assertion without holding an existing legacy external assertion. "
+                      "(%d total, %d external, %d legacy-external)\n",
+                      proc_name_address(target_task->bsd_info), audit_token_pid_from_task(target_task),
+                      target_assertcnt, target_externcnt, target_legacycnt);
+       }
+
+       return(ret);
+}
+
+/*
+ *     Routine:        ipc_importance_task_drop_legacy_external_assertion
+ *     Purpose:
+ *             Drop the legacy external assertion count on the task and
+ *             reflect that change to total external assertion count and
+ *             then onto the internal importance count.
+ *
+ *             If this results in a X->0 change in the internal,
+ *             count, prepare to update task policy for this task AND
+ *             all down-stream tasks that have a similar transition
+ *             through inheriting this update.
+ *     Conditions:
+ *             Nothing locked on entry.
+ */
+kern_return_t
+ipc_importance_task_drop_legacy_external_assertion(ipc_importance_task_t task_imp, uint32_t count)
+{
+       int ret = KERN_SUCCESS;
+       task_t target_task;
+       uint32_t target_assertcnt;
+       uint32_t target_externcnt;
+       uint32_t target_legacycnt;
+
+       if (count > 1) {
+               return KERN_INVALID_ARGUMENT;
+       }
+               
+       ipc_importance_lock();
+       target_task = task_imp->iit_task;
+
+#if IMPORTANCE_DEBUG
+       int target_pid = (TASK_NULL != target_task) ? audit_token_pid_from_task(target_task) : -1;
+
+       KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, (IMP_DROP | TASK_POLICY_EXTERNAL))) | DBG_FUNC_START,
+               proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_LEGACY_EXTERN(task_imp), 0);
+#endif
+
+       if (count > IIT_LEGACY_EXTERN(task_imp)) {
+               /* Process over-released its boost count - save data for diagnostic printf */
+               /* TODO: If count > 1, we should clear out as many external assertions as there are left. */
+               target_assertcnt = task_imp->iit_assertcnt;
+               target_externcnt = IIT_EXTERN(task_imp);
+               target_legacycnt = IIT_LEGACY_EXTERN(task_imp);
+               ret = KERN_FAILURE;
+       } else {
+               /* 
+                * decrement legacy external count from the top level and reflect
+                * into internal for this and all subsequent updates.
+                */
+               assert(ipc_importance_task_is_any_receiver_type(task_imp));
+               assert(IIT_EXTERN(task_imp) >= count);
+
+               task_imp->iit_legacy_externdrop += count;
+               task_imp->iit_externdrop += count;
+
+               /* reset extern counters (if appropriate) */
+               if (IIT_LEGACY_EXTERN(task_imp) == 0) {
+                       if (IIT_EXTERN(task_imp) != 0) {
+                               task_imp->iit_externcnt -= task_imp->iit_legacy_externcnt;
+                               task_imp->iit_externdrop -= task_imp->iit_legacy_externdrop;
+                       } else {
+                               task_imp->iit_externcnt = 0;
+                               task_imp->iit_externdrop = 0;
+                       }
+                       task_imp->iit_legacy_externcnt = 0;
+                       task_imp->iit_legacy_externdrop = 0;
+               }
+               
+               /* reflect the drop to the internal assertion count (and effect any importance change) */
+               if (ipc_importance_task_check_transition(task_imp, IIT_UPDATE_DROP, count)) {
+                       ipc_importance_task_propagate_assertion_locked(task_imp, IIT_UPDATE_DROP, TRUE);
+               }
+               ret = KERN_SUCCESS;
+       }
+
+#if IMPORTANCE_DEBUG
+               KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, (IMP_DROP | TASK_POLICY_EXTERNAL))) | DBG_FUNC_END,
+                                         proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_LEGACY_EXTERN(task_imp), 0);
+#endif
+
+       ipc_importance_unlock();
+
+       /* delayed printf for user-supplied data failures */
+       if (KERN_FAILURE == ret && TASK_NULL != target_task) {
+               printf("BUG in process %s[%d]: over-released legacy external boost assertions (%d total, %d external, %d legacy-external)\n",
+                      proc_name_address(target_task->bsd_info), audit_token_pid_from_task(target_task),
+                      target_assertcnt, target_externcnt, target_legacycnt);
+       }
+
+       return(ret);
+}
+
+
+
+/* Transfer an assertion to legacy userspace responsibility */
+static kern_return_t
+ipc_importance_task_externalize_legacy_assertion(ipc_importance_task_t task_imp, uint32_t count, __unused int sender_pid)
+{
+       task_t target_task;
+
+       assert(IIT_NULL != task_imp);
+       target_task = task_imp->iit_task;
+
+       if (TASK_NULL == target_task ||
+           !ipc_importance_task_is_any_receiver_type(task_imp)) {
+               return KERN_FAILURE;
+       }
+
+#if IMPORTANCE_DEBUG
+       int target_pid = audit_token_pid_from_task(target_task);
+
+       KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, IMP_EXTERN)) | DBG_FUNC_START,
+               proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_EXTERN(task_imp), 0);
+#endif
+
+       ipc_importance_lock();
+       /* assert(task_imp->iit_assertcnt >= IIT_EXTERN(task_imp) + count); */
+       assert(IIT_EXTERN(task_imp) >= IIT_LEGACY_EXTERN(task_imp));
+       task_imp->iit_legacy_externcnt += count;
+       task_imp->iit_externcnt += count;
+       ipc_importance_unlock();
+
+#if IMPORTANCE_DEBUG
+       KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_ASSERTION, IMP_EXTERN)) | DBG_FUNC_END,
+                                 proc_selfpid(), target_pid, task_imp->iit_assertcnt, IIT_LEGACY_EXTERN(task_imp), 0);
+    // This is the legacy boosting path
+       DTRACE_BOOST5(receive_boost, task_t, target_task, int, target_pid, int, sender_pid, int, count, int, IIT_LEGACY_EXTERN(task_imp));
+#endif /* IMPORTANCE_DEBUG */
+
+       return(KERN_SUCCESS);
+}
+
+/*
+ *     Routine:        ipc_importance_task_update_live_donor
+ *     Purpose:
+ *             Read the live donor status and update the live_donor bit/propagate the change in importance.
+ *     Conditions:
+ *             Nothing locked on entrance, nothing locked on exit.
+ *
+ *             TODO: Need tracepoints around this function...
+ */
+void
+ipc_importance_task_update_live_donor(ipc_importance_task_t task_imp)
+{
+       uint32_t task_live_donor;
+       boolean_t before_donor;
+       boolean_t after_donor;
+       task_t target_task;
+  
+       assert(task_imp != NULL);
+
+       /*
+        * Nothing to do if the task is not marked as expecting
+        * live donor updates.
+        */
+       if (!ipc_importance_task_is_marked_live_donor(task_imp)) {
+               return;
+       }
+
+       ipc_importance_lock();
+
+       /* If the task got disconnected on the way here, no use (or ability) adjusting live donor status */
+       target_task = task_imp->iit_task;
+       if (TASK_NULL == target_task) {
+               ipc_importance_unlock();
+               return;
+       }
+       before_donor = ipc_importance_task_is_marked_donor(task_imp);
+
+       /* snapshot task live donor status - may change, but another call will accompany the change */
+       task_live_donor = target_task->effective_policy.t_live_donor;
+
+#if IMPORTANCE_DEBUG
+       int target_pid = audit_token_pid_from_task(target_task);
+
+       KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
+                                 (IMPORTANCE_CODE(IMP_DONOR_CHANGE, IMP_DONOR_UPDATE_LIVE_DONOR_STATE)) | DBG_FUNC_START,
+                                 target_pid, task_imp->iit_donor, task_live_donor, before_donor, 0);
+#endif                           
+
+       /* update the task importance live donor status based on the task's value */
+       task_imp->iit_donor = task_live_donor;
+
+       after_donor = ipc_importance_task_is_marked_donor(task_imp);
+
+       /* Has the effectiveness of being a donor changed as a result of this update? */
+       if (before_donor != after_donor) {
+               iit_update_type_t type;
+
+               /* propagate assertions without updating the current task policy (already handled) */
+               if (0 == before_donor) {
+                       task_imp->iit_transitions++;
+                       type = IIT_UPDATE_HOLD;
+               } else {
+                       type = IIT_UPDATE_DROP;
+               }
+               ipc_importance_task_propagate_assertion_locked(task_imp, type, FALSE);
+       }
+
+#if IMPORTANCE_DEBUG
+       KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
+                                 (IMPORTANCE_CODE(IMP_DONOR_CHANGE, IMP_DONOR_UPDATE_LIVE_DONOR_STATE)) | DBG_FUNC_END,
+                                 target_pid, task_imp->iit_donor, task_live_donor, after_donor, 0);
+#endif
+
+       ipc_importance_unlock();
+}
+
+
+/*
+ *     Routine:        ipc_importance_task_mark_donor
+ *     Purpose:
+ *             Set the task importance donor flag.
+ *     Conditions:
+ *             Nothing locked on entrance, nothing locked on exit.
+ *
+ *             This is only called while the task is being constructed,
+ *             so no need to update task policy or propagate downstream.
+ */
+void
+ipc_importance_task_mark_donor(ipc_importance_task_t task_imp, boolean_t donating)
+{
+       assert(task_imp != NULL);
+
+       ipc_importance_lock();
+
+       int old_donor = task_imp->iit_donor;
+
+       task_imp->iit_donor = (donating ? 1 : 0);
+
+       if (task_imp->iit_donor > 0 && old_donor == 0)
+               task_imp->iit_transitions++;
+
+       KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE,
+                                 (IMPORTANCE_CODE(IMP_DONOR_CHANGE, IMP_DONOR_INIT_DONOR_STATE)) | DBG_FUNC_NONE,
+                                 audit_token_pid_from_task(task_imp->iit_task), donating,
+                                 old_donor, task_imp->iit_donor, 0);
+       
+       ipc_importance_unlock();
+}
+
+/*
+ *     Routine:        ipc_importance_task_marked_donor
+ *     Purpose:
+ *             Query the donor flag for the given task importance.
+ *     Conditions:
+ *             May be called without taking the importance lock.
+ *             In that case, donor status can change so you must
+ *             check only once for each donation event.
+ */
+boolean_t
+ipc_importance_task_is_marked_donor(ipc_importance_task_t task_imp)
+{
+       if (IIT_NULL == task_imp) {
+               return FALSE;
+       }
+       return (0 != task_imp->iit_donor);
+}
+
+/*
+ *     Routine:        ipc_importance_task_mark_live_donor
+ *     Purpose:
+ *             Indicate that the task is eligible for live donor updates.
+ *     Conditions:
+ *             Nothing locked on entrance, nothing locked on exit.
+ *
+ *             This is only called while the task is being constructed.
+ */
+void
+ipc_importance_task_mark_live_donor(ipc_importance_task_t task_imp, boolean_t live_donating)
+{
+       assert(task_imp != NULL);
+
+       ipc_importance_lock();
+       task_imp->iit_live_donor = (live_donating ? 1 : 0);
+       ipc_importance_unlock();
+}
+
+/*
+ *     Routine:        ipc_importance_task_marked_live_donor
+ *     Purpose:
+ *             Query the live donor and donor flags for the given task importance.
+ *     Conditions:
+ *             May be called without taking the importance lock.
+ *             In that case, donor status can change so you must
+ *             check only once for each donation event.
+ */
+boolean_t
+ipc_importance_task_is_marked_live_donor(ipc_importance_task_t task_imp)
+{
+       if (IIT_NULL == task_imp) {
+               return FALSE;
+       }
+       return (0 != task_imp->iit_live_donor);
+}
+
+/*
+ *     Routine:        ipc_importance_task_is_donor
+ *     Purpose:
+ *             Query the full donor status for the given task importance.
+ *     Conditions:
+ *             May be called without taking the importance lock.
+ *             In that case, donor status can change so you must
+ *             check only once for each donation event.
+ */
+boolean_t
+ipc_importance_task_is_donor(ipc_importance_task_t task_imp)
+{
+       if (IIT_NULL == task_imp) {
+               return FALSE;
+       }
+       return (ipc_importance_task_is_marked_donor(task_imp) ||
+               (ipc_importance_task_is_marked_receiver(task_imp) &&
+                task_imp->iit_assertcnt > 0));
+}
+
+/*
+ *     Routine:        ipc_importance_task_is_never_donor
+ *     Purpose:
+ *             Query if a given task can ever donate importance.
+ *     Conditions:
+ *             May be called without taking the importance lock.
+ *             Condition is permanent for a give task.         
+ */
+boolean_t
+ipc_importance_task_is_never_donor(ipc_importance_task_t task_imp)
+{
+       if (IIT_NULL == task_imp) {
+               return FALSE;
+       }
+       return (!ipc_importance_task_is_marked_donor(task_imp) &&
+               !ipc_importance_task_is_marked_live_donor(task_imp) &&
+               !ipc_importance_task_is_marked_receiver(task_imp));
+}
+
+/*
+ *     Routine:        ipc_importance_task_mark_receiver
+ *     Purpose:
+ *             Update the task importance receiver flag.
+ *     Conditions:
+ *             Nothing locked on entrance, nothing locked on exit.
+ *             This can only be invoked before the task is discoverable,
+ *             so no worries about atomicity(?)
+ */
+void
+ipc_importance_task_mark_receiver(ipc_importance_task_t task_imp, boolean_t receiving)
+{
+       assert(task_imp != NULL);       
+
+       ipc_importance_lock();
+       if (receiving) {
+               assert(task_imp->iit_assertcnt == 0);
+               assert(task_imp->iit_externcnt == 0);
+               assert(task_imp->iit_externdrop == 0);
+               assert(task_imp->iit_denap == 0);
+               task_imp->iit_receiver = 1;  /* task can receive importance boost */
+       } else if (task_imp->iit_receiver) {
+               assert(task_imp->iit_denap == 0);
+               if (task_imp->iit_assertcnt != 0 || IIT_EXTERN(task_imp) != 0) {
+                       panic("disabling imp_receiver on task with pending importance boosts!");
+               }
+               task_imp->iit_receiver = 0;
+       }
+       ipc_importance_unlock();
+}
+
+
+/*
+ *     Routine:        ipc_importance_task_marked_receiver
+ *     Purpose:
+ *             Query the receiver flag for the given task importance.
+ *     Conditions:
+ *             May be called without taking the importance lock as
+ *             the importance flag can never change after task init.
+ */
+boolean_t
+ipc_importance_task_is_marked_receiver(ipc_importance_task_t task_imp)
+{
+       return (IIT_NULL != task_imp && 0 != task_imp->iit_receiver);
+}
+
+
+/*
+ *     Routine:        ipc_importance_task_mark_denap_receiver
+ *     Purpose:
+ *             Update the task importance de-nap receiver flag.
+ *     Conditions:
+ *             Nothing locked on entrance, nothing locked on exit.
+ *             This can only be invoked before the task is discoverable,
+ *             so no worries about atomicity(?)
+ */
+void
+ipc_importance_task_mark_denap_receiver(ipc_importance_task_t task_imp, boolean_t denap)
+{
+       assert(task_imp != NULL);       
+
+       ipc_importance_lock();
+       if (denap) {
+               assert(task_imp->iit_assertcnt == 0);
+               assert(task_imp->iit_externcnt == 0);
+               assert(task_imp->iit_receiver == 0);
+               task_imp->iit_denap = 1;  /* task can receive de-nap boost */
+       } else if (task_imp->iit_denap) {
+               assert(task_imp->iit_receiver == 0);
+               if (0 < task_imp->iit_assertcnt || 0 < IIT_EXTERN(task_imp)) {
+                       panic("disabling de-nap on task with pending de-nap boosts!");
+               }
+               task_imp->iit_denap = 0;
+       }
+       ipc_importance_unlock();
+}
+
+
+/*
+ *     Routine:        ipc_importance_task_marked_denap_receiver
+ *     Purpose:
+ *             Query the de-nap receiver flag for the given task importance.
+ *     Conditions:
+ *             May be called without taking the importance lock as
+ *             the de-nap flag can never change after task init.
+ */
+boolean_t
+ipc_importance_task_is_marked_denap_receiver(ipc_importance_task_t task_imp)
+{
+       return (IIT_NULL != task_imp && 0 != task_imp->iit_denap);
+}
+
+/*
+ *     Routine:        ipc_importance_task_is_denap_receiver
+ *     Purpose:
+ *             Query the full de-nap receiver status for the given task importance.
+ *             For now, that is simply whether the receiver flag is set.
+ *     Conditions:
+ *             May be called without taking the importance lock as
+ *             the de-nap receiver flag can never change after task init.
+ */
+boolean_t
+ipc_importance_task_is_denap_receiver(ipc_importance_task_t task_imp)
+{
+       return (ipc_importance_task_is_marked_denap_receiver(task_imp));
+}
+
+/*
+ *     Routine:        ipc_importance_task_is_any_receiver_type
+ *     Purpose:
+ *             Query if the task is marked to receive boosts - either
+ *             importance or denap.
+ *     Conditions:
+ *             May be called without taking the importance lock as both
+ *             the importance and de-nap receiver flags can never change
+ *             after task init.
+ */
+boolean_t
+ipc_importance_task_is_any_receiver_type(ipc_importance_task_t task_imp)
+{
+       return (ipc_importance_task_is_marked_receiver(task_imp) ||
+               ipc_importance_task_is_marked_denap_receiver(task_imp));
+}
+
+#if 0 /* currently unused */
+
+/*
+ *     Routine:        ipc_importance_inherit_reference
+ *     Purpose:
+ *             Add a reference to the inherit importance element.
+ *     Conditions:
+ *             Caller most hold a reference on the inherit element.
+ */
+static inline void
+ipc_importance_inherit_reference(ipc_importance_inherit_t inherit)
+{
+       ipc_importance_reference(&inherit->iii_elem);
+}
+#endif /* currently unused */
+
+/*
+ *     Routine:        ipc_importance_inherit_release_locked
+ *     Purpose:
+ *             Release a reference on an inherit importance attribute value,
+ *             unlinking and deallocating the attribute if the last reference.
+ *     Conditions:
+ *             Entered with importance lock held, leaves with it unlocked.
+ */
+static inline void
+ipc_importance_inherit_release_locked(ipc_importance_inherit_t inherit)
+{
+       ipc_importance_release_locked(&inherit->iii_elem);
+}
+
+#if 0 /* currently unused */
+/*
+ *     Routine:        ipc_importance_inherit_release
+ *     Purpose:
+ *             Release a reference on an inherit importance attribute value,
+ *             unlinking and deallocating the attribute if the last reference.
+ *     Conditions:
+ *             nothing locked on entrance, nothing locked on exit.
+ *             May block.
+ */
+void
+ipc_importance_inherit_release(ipc_importance_inherit_t inherit)
+{
+       if (III_NULL != inherit)
+               ipc_importance_release(&inherit->iii_elem);
+}
+#endif /* 0 currently unused */
+       
+/*
+ *     Routine:        ipc_importance_for_task
+ *     Purpose:
+ *             Create a reference for the specified task's base importance
+ *             element.  If the base importance element doesn't exist, make it and
+ *             bind it to the active task.  If the task is inactive, there isn't
+ *             any need to return a new reference.
+ *     Conditions:
+ *             If made is true, a "made" reference is returned (for donating to
+ *             the voucher system).  Otherwise an internal reference is returned.
+ *
+ *             Nothing locked on entry.  May block.
+ */
+ipc_importance_task_t
+ipc_importance_for_task(task_t task, boolean_t made)
+{
+       ipc_importance_task_t task_elem;
+       boolean_t first_pass = TRUE;
+
+       assert(TASK_NULL != task);
+
+ retry:
+       /* No use returning anything for inactive task */
+       if (!task->active)
+               return IIT_NULL;
+
+       ipc_importance_lock();
+       task_elem = task->task_imp_base;
+       if (IIT_NULL != task_elem) {
+               /* Add a made reference (borrowing active task ref to do it) */
+               if (made) {
+                       if (0 == task_elem->iit_made++) {
+                               assert(IIT_REFS_MAX > IIT_REFS(task_elem));
+                               ipc_importance_task_reference_internal(task_elem);
+                       }
+               } else {
+                       assert(IIT_REFS_MAX > IIT_REFS(task_elem));
+                       ipc_importance_task_reference_internal(task_elem);
+               }
+               ipc_importance_unlock();
+               return task_elem;
+       }
+       ipc_importance_unlock();
+
+       if (!first_pass)
+               return IIT_NULL;
+       first_pass = FALSE;
+
+       /* Need to make one - may race with others (be prepared to drop) */
+       task_elem = (ipc_importance_task_t)zalloc(ipc_importance_task_zone);
+       if (IIT_NULL == task_elem)
+               goto retry;
+
+       task_elem->iit_bits = IIE_TYPE_TASK | 2; /* one for task, one for return/made */
+       task_elem->iit_made = (made) ? 1 : 0;
+       task_elem->iit_task = task; /* take actual ref when we're sure */
+       task_elem->iit_updateq = NULL;
+       task_elem->iit_receiver = 0;
+       task_elem->iit_denap = 0;
+       task_elem->iit_donor = 0;
+       task_elem->iit_live_donor = 0;
+       task_elem->iit_updatepolicy = 0;
+       task_elem->iit_reserved = 0;
+       task_elem->iit_filelocks = 0;
+       task_elem->iit_updatetime = 0;
+       task_elem->iit_transitions = 0;
+       task_elem->iit_assertcnt = 0;
+       task_elem->iit_externcnt = 0;
+       task_elem->iit_externdrop = 0;
+       task_elem->iit_legacy_externcnt = 0;
+       task_elem->iit_legacy_externdrop = 0;
+#if IIE_REF_DEBUG
+       ipc_importance_counter_init(&task_elem->iit_elem);
+#endif
+       queue_init(&task_elem->iit_kmsgs);
+       queue_init(&task_elem->iit_inherits);
+
+       ipc_importance_lock();
+       if (!task->active) {
+               ipc_importance_unlock();
+               zfree(ipc_importance_task_zone, task_elem);
+               return IIT_NULL;
+       }
+
+       /* did we lose the race? */
+       if (IIT_NULL != task->task_imp_base) {
+               ipc_importance_unlock();
+               zfree(ipc_importance_task_zone, task_elem);
+               goto retry;
+       }
+
+       /* we won the race */
+       task->task_imp_base = task_elem;
+       task_reference(task);
+#if DEVELOPMENT || DEBUG
+       queue_enter(&global_iit_alloc_queue, task_elem, ipc_importance_task_t, iit_allocation);
+       task_importance_update_owner_info(task);
+#endif
+       ipc_importance_unlock();
+
+       return task_elem;
+}
+
+#if DEVELOPMENT || DEBUG
+void task_importance_update_owner_info(task_t task) {
+
+       if (task != TASK_NULL && task->task_imp_base != IIT_NULL) {
+               ipc_importance_task_t task_elem = task->task_imp_base;
+
+               task_elem->iit_bsd_pid = audit_token_pid_from_task(task);
+               if (task->bsd_info) {
+                       strncpy(&task_elem->iit_procname[0], proc_name_address(task->bsd_info), 16);
+                       task_elem->iit_procname[16] = '\0';
+               } else {
+                       strncpy(&task_elem->iit_procname[0], "unknown", 16);
+               }
+       }
+}
+#endif
+
+/*
+ *     Routine:        ipc_importance_reset_locked
+ *     Purpose:
+ *             Reset a task's IPC importance (the task is going away or exec'ing)
+ *
+ *             Remove the donor bit and legacy externalized assertions from the
+ *             current task importance and see if that wipes out downstream donations.
+ *     Conditions:
+ *             importance lock held.
+ */
+
+static void
+ipc_importance_reset_locked(ipc_importance_task_t task_imp, boolean_t donor)
+{
+       boolean_t before_donor, after_donor;
+
+       /* remove the donor bit, live-donor bit and externalized boosts */
+       before_donor = ipc_importance_task_is_donor(task_imp);
+       if (donor) {
+               task_imp->iit_donor = 0;
+       }
+       assert(IIT_LEGACY_EXTERN(task_imp) <= IIT_EXTERN(task_imp));
+       assert(task_imp->iit_legacy_externcnt <= task_imp->iit_externcnt);
+       assert(task_imp->iit_legacy_externdrop <= task_imp->iit_externdrop);
+       task_imp->iit_externcnt -= task_imp->iit_legacy_externcnt;
+       task_imp->iit_externdrop -= task_imp->iit_legacy_externdrop;
+
+       /* assert(IIT_LEGACY_EXTERN(task_imp) <= task_imp->iit_assertcnt); */
+       if (IIT_LEGACY_EXTERN(task_imp) < task_imp->iit_assertcnt) {
+               task_imp->iit_assertcnt -= IIT_LEGACY_EXTERN(task_imp);
+       } else {
+               assert(IIT_LEGACY_EXTERN(task_imp) == task_imp->iit_assertcnt);
+               task_imp->iit_assertcnt = 0;
+       }
+       task_imp->iit_legacy_externcnt = 0;
+       task_imp->iit_legacy_externdrop = 0;
+       after_donor = ipc_importance_task_is_donor(task_imp);
+
+#if DEVELOPMENT || DEBUG
+       if (task_imp->iit_assertcnt > 0 && task_imp->iit_live_donor) {
+               printf("Live donor task %s[%d] still has %d importance assertions after reset\n", 
+                      task_imp->iit_procname, task_imp->iit_bsd_pid, task_imp->iit_assertcnt);
+       }
+#endif
+
+       /* propagate a downstream drop if there was a change in donor status */
+       if (after_donor != before_donor) {
+               ipc_importance_task_propagate_assertion_locked(task_imp, IIT_UPDATE_DROP, FALSE);
+       }
+}
+
+/*
+ *     Routine:        ipc_importance_reset
+ *     Purpose:
+ *             Reset a task's IPC importance
+ *
+ *             The task is being reset, although staying around. Arrange to have the
+ *             external state of the task reset from the importance.
+ *     Conditions:
+ *             importance lock not held.
+ */
+
+void
+ipc_importance_reset(ipc_importance_task_t task_imp, boolean_t donor)
+{
+       if (IIT_NULL == task_imp) {
+               return;
+       }
+       ipc_importance_lock();
+       ipc_importance_reset_locked(task_imp, donor);
+       ipc_importance_unlock();
+}
+
+/*
+ *     Routine:        ipc_importance_disconnect_task
+ *     Purpose:
+ *             Disconnect a task from its importance.
+ *
+ *             Clear the task pointer from the importance and drop the
+ *             reference the task held on the importance object.  Before
+ *             doing that, reset the effects the current task holds on
+ *             the importance and see if that wipes out downstream donations.
+ *
+ *             We allow the upstream boosts to continue to affect downstream
+ *             even though the local task is being effectively pulled from
+ *             the chain.
+ *     Conditions:
+ *             Nothing locked.
+ */
+void
+ipc_importance_disconnect_task(task_t task)
+{
+       ipc_importance_task_t task_imp;
+
+       task_lock(task);
+       ipc_importance_lock();
+       task_imp = task->task_imp_base;
+
+       /* did somebody beat us to it? */
+       if (IIT_NULL == task_imp) {
+               ipc_importance_unlock();
+               task_unlock(task);
+               return;
+       }
+
+       /* disconnect the task from this importance */
+       assert(task_imp->iit_task == task);
+       task_imp->iit_task = TASK_NULL;
+       task->task_imp_base = IIT_NULL;
+       task_unlock(task);
+       
+       /* reset the effects the current task hold on the importance */
+       ipc_importance_reset_locked(task_imp, TRUE);
+
+       ipc_importance_task_release_locked(task_imp);
+       /* importance unlocked */
+
+       /* deallocate the task now that the importance is unlocked */
+       task_deallocate(task);
+}
+
+/*
+ *     Routine:        ipc_importance_send
+ *     Purpose:
+ *             Post the importance voucher attribute [if sent] or a static
+ *             importance boost depending upon options and conditions.
+ *     Conditions:
+ *             Destination port locked on entry and exit, may be dropped during the call.
+ *     Returns:
+ *             A boolean identifying if the port lock was tempoarily dropped.
+ */
+boolean_t
+ipc_importance_send(
+       ipc_kmsg_t              kmsg,
+       mach_msg_option_t       option)
+{
+       ipc_port_t port = (ipc_port_t) kmsg->ikm_header->msgh_remote_port;
+       boolean_t port_lock_dropped = FALSE;
+       ipc_importance_elem_t elem;
+       task_t task;
+       ipc_importance_task_t task_imp;
+       kern_return_t kr;
+
+
+       assert(IP_VALID(port));
+
+       /* If no donation to be made, return quickly */
+       if ((port->ip_impdonation == 0) ||
+           (option & MACH_SEND_NOIMPORTANCE) != 0) {
+               return port_lock_dropped;
+       }
+
+       task = current_task();
+
+       /* If forced sending a static boost, go update the port */
+       if ((option & MACH_SEND_IMPORTANCE) != 0) {
+               kmsg->ikm_header->msgh_bits |= MACH_MSGH_BITS_RAISEIMP;
+               goto portupdate;
+       }
+
+       task_imp = task->task_imp_base;
+       assert(IIT_NULL != task_imp);
+
+       /* If the sender can never donate importance, nothing to do */
+       if (ipc_importance_task_is_never_donor(task_imp)) {
+               return port_lock_dropped;
+       }
+
+       elem = IIE_NULL;
+
+       /* If importance receiver and passing a voucher, look for importance in there */
+       if (IP_VALID(kmsg->ikm_voucher) &&
+           ipc_importance_task_is_marked_receiver(task_imp)) {
+               mach_voucher_attr_value_handle_t vals[MACH_VOUCHER_ATTR_VALUE_MAX_NESTED];
+               mach_voucher_attr_value_handle_array_size_t val_count;
+               ipc_voucher_t voucher;
+                       
+               assert(ip_kotype(kmsg->ikm_voucher) == IKOT_VOUCHER);
+               voucher = (ipc_voucher_t)kmsg->ikm_voucher->ip_kobject;
+
+               /* check to see if the voucher has an importance attribute */
+               val_count = MACH_VOUCHER_ATTR_VALUE_MAX_NESTED;
+               kr = mach_voucher_attr_control_get_values(ipc_importance_control, voucher,
+                                                         vals, &val_count);
+               assert(KERN_SUCCESS == kr);
+
+               /*
+                * Only use importance associated with our task (either directly
+                * or through an inherit that donates to our task).
+                */
+               if (0 < val_count) {
+                       ipc_importance_elem_t check_elem;
+
+                       check_elem = (ipc_importance_elem_t)vals[0];
+                       assert(IIE_NULL != check_elem);
+                       if (IIE_TYPE_INHERIT == IIE_TYPE(check_elem)) {
+                               ipc_importance_inherit_t inherit;
+                               inherit = (ipc_importance_inherit_t) check_elem;
+                               if (inherit->iii_to_task == task_imp) {
+                                       elem = check_elem;
+                               }
+                       } else if (check_elem == (ipc_importance_elem_t)task_imp) {
+                               elem = check_elem;
+                       }
+               }
+       }
+
+       /* If we haven't found an importance attribute to send yet, use the task's */
+       if (IIE_NULL == elem) {
+               elem = (ipc_importance_elem_t)task_imp;
+       }
+
+       /* take a reference for the message to hold */
+       ipc_importance_reference_internal(elem);
+
+       /* acquire the importance lock while trying to hang on to port lock */
+       if (!ipc_importance_lock_try()) {
+               port_lock_dropped = TRUE;
+               ip_unlock(port);
+               ipc_importance_lock();
+       }
+
+       /* link kmsg onto the donor element propagation chain */
+       ipc_importance_kmsg_link(kmsg, elem);
+       /* elem reference transfered to kmsg */
+
+       incr_ref_counter(elem->iie_kmsg_refs_added);
+
+       /* If the sender isn't currently a donor, no need to apply boost */
+       if (!ipc_importance_task_is_donor(task_imp)) {
+               ipc_importance_unlock();
+
+               /* re-acquire port lock, if needed */
+               if (TRUE == port_lock_dropped)
+                       ip_lock(port);
+
+               return port_lock_dropped;
+       }
+
+       /* Mark the fact that we are (currently) donating through this message */
+       kmsg->ikm_header->msgh_bits |= MACH_MSGH_BITS_RAISEIMP;
+
+       /*
+        * If we need to relock the port, do it with the importance still locked.
+        * This assures we get to add the importance boost through the port to
+        * the task BEFORE anyone else can attempt to undo that operation because
+        * the sender lost donor status.
+        */
+       if (TRUE == port_lock_dropped) {
+               ip_lock(port);
+       }
+       ipc_importance_unlock();
+
+ portupdate:
+                               
+#if IMPORTANCE_DEBUG
+       if (kdebug_enable) {
+               mach_msg_max_trailer_t *dbgtrailer = (mach_msg_max_trailer_t *)
+                               ((vm_offset_t)kmsg->ikm_header + round_msg(kmsg->ikm_header->msgh_size));
+               unsigned int sender_pid = dbgtrailer->msgh_audit.val[5];
+               mach_msg_id_t imp_msgh_id = kmsg->ikm_header->msgh_id;
+               KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_MSG, IMP_MSG_SEND)) | DBG_FUNC_START,
+                                          audit_token_pid_from_task(task), sender_pid, imp_msgh_id, 0, 0);
+       }
+#endif /* IMPORTANCE_DEBUG */
+
+       /* adjust port boost count (with port locked) */
+       if (TRUE == ipc_port_importance_delta(port, 1)) {
+               port_lock_dropped = TRUE;
+               ip_lock(port);
+       }
+       return port_lock_dropped;
+}
+       
+/*
+ *     Routine:        ipc_importance_inherit_from
+ *     Purpose:
+ *             Create a "made" reference for an importance attribute representing
+ *             an inheritance between the sender of a message (if linked) and the
+ *             current task importance.  If the message is not linked, a static
+ *             boost may be created, based on the boost state of the message.
+ *
+ *             Any transfer from kmsg linkage to inherit linkage must be atomic.
+ *
+ *             If the task is inactive, there isn't any need to return a new reference.
+ *     Conditions:
+ *             Nothing locked on entry.  May block.
+ */
+static ipc_importance_inherit_t
+ipc_importance_inherit_from(ipc_kmsg_t kmsg)
+{
+       ipc_importance_task_t   task_imp = IIT_NULL;
+       ipc_importance_elem_t   from_elem = kmsg->ikm_importance;
+       ipc_importance_elem_t   elem;
+       task_t  task_self = current_task();
+
+       ipc_port_t port = kmsg->ikm_header->msgh_remote_port;
+       ipc_importance_inherit_t inherit = III_NULL;
+       ipc_importance_inherit_t alloc = III_NULL;
+       ipc_importance_inherit_t temp_inherit;
+       boolean_t cleared_self_donation = FALSE;
+       boolean_t donating;
+       uint32_t depth = 1;
+
+       /* The kmsg must have an importance donor or static boost to proceed */
+       if (IIE_NULL == kmsg->ikm_importance &&
+           !MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits)) {
+               return III_NULL;
+       }
+
+       /* 
+        * No need to set up an inherit linkage if the dest isn't a receiver
+        * of one type or the other.
+        */
+       if (!ipc_importance_task_is_any_receiver_type(task_self->task_imp_base)) {
+               ipc_importance_lock();
+               goto out_locked;
+       }
+
+       /* Grab a reference on the importance of the destination */
+       task_imp = ipc_importance_for_task(task_self, FALSE);
+
+       ipc_importance_lock();
+
+       if (IIT_NULL == task_imp) {
+               goto out_locked;
+       }
+
+       incr_ref_counter(task_imp->iit_elem.iie_task_refs_added_inherit_from);
+
+       /* If message is already associated with an inherit... */
+       if (IIE_TYPE_INHERIT == IIE_TYPE(from_elem)) {
+               ipc_importance_inherit_t from_inherit = (ipc_importance_inherit_t)from_elem;
+
+               /* already targeting our task? - just use it */
+               if (from_inherit->iii_to_task == task_imp) {
+                       /* clear self-donation if not also present in inherit */
+                       if (!from_inherit->iii_donating &&
+                           MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits)) {
+                               kmsg->ikm_header->msgh_bits &= ~MACH_MSGH_BITS_RAISEIMP;
+                               cleared_self_donation = TRUE;
+                       }
+                       inherit = from_inherit;
+                               
+               } else if (III_DEPTH_MAX == III_DEPTH(from_inherit)) {
+                       ipc_importance_task_t to_task;
+                       ipc_importance_elem_t unlinked_from;
+
+                       /*
+                        * Chain too long. Switch to looking
+                        * directly at the from_inherit's to-task
+                        * as our source of importance. 
+                        */
+                       to_task = from_inherit->iii_to_task;
+                       ipc_importance_task_reference(to_task);
+                       from_elem = (ipc_importance_elem_t)to_task;
+                       depth = III_DEPTH_RESET | 1;
+
+                       /* Fixup the kmsg linkage to reflect change */
+                       unlinked_from = ipc_importance_kmsg_unlink(kmsg);
+                       assert(unlinked_from == (ipc_importance_elem_t)from_inherit);
+                       ipc_importance_kmsg_link(kmsg, from_elem);
+                       ipc_importance_inherit_release_locked(from_inherit);
+                       /* importance unlocked */
+                       ipc_importance_lock();
+
+               } else {
+                       /* inheriting from an inherit */
+                       depth = from_inherit->iii_depth + 1;
+               }
+       } 
+
+       /* 
+        * Don't allow a task to inherit from itself (would keep it permanently
+        * boosted even if all other donors to the task went away).
+        */
+
+       if (from_elem == (ipc_importance_elem_t)task_imp) {
+               goto out_locked;
+       }
+
+       /* 
+        * But if the message isn't associated with any linked source, it is
+        * intended to be permanently boosting (static boost from kernel).
+        * In that case DO let the process permanently boost itself.
+        */
+       if (IIE_NULL == from_elem) {
+               assert(MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits));
+               ipc_importance_task_reference_internal(task_imp);
+               from_elem = (ipc_importance_elem_t)task_imp;
+       }
+       
+       /* 
+        * Now that we have the from_elem figured out,
+        * check to see if we already have an inherit for this pairing
+        */
+       while (III_NULL == inherit) {
+               queue_iterate(&from_elem->iie_inherits, temp_inherit,
+                             ipc_importance_inherit_t, iii_inheritance) {
+                       if (temp_inherit->iii_to_task == task_imp &&
+                           temp_inherit->iii_depth == depth) {
+                               inherit = temp_inherit;
+                               break;
+                       }
+               }
+
+               /* Do we have to allocate a new inherit */
+               if (III_NULL == inherit) {
+                       if (III_NULL != alloc) {
+                               break;
+                       }
+
+                       /* allocate space */
+                       ipc_importance_unlock();
+                       alloc = (ipc_importance_inherit_t)
+                               zalloc(ipc_importance_inherit_zone);
+                       ipc_importance_lock();
+               }
+       }
+
+       /* snapshot the donating status while we have importance locked */
+       donating = MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits);
+
+       if (III_NULL != inherit) {
+               /* We found one, piggyback on that */
+               assert(0 < III_REFS(inherit));
+               assert(0 < IIE_REFS(inherit->iii_from_elem));
+               assert(inherit->iii_externcnt >= inherit->iii_made);
+
+               /* add in a made reference */
+               if (0 == inherit->iii_made++) {
+                       assert(III_REFS_MAX > III_REFS(inherit));
+                       ipc_importance_inherit_reference_internal(inherit);
+               }
+
+               /* Reflect the inherit's change of status into the task boosts */
+               if (0 == III_EXTERN(inherit)) {
+                       assert(!inherit->iii_donating);
+                       inherit->iii_donating = donating;
+                       if (donating) {
+                               task_imp->iit_externcnt += inherit->iii_externcnt;
+                               task_imp->iit_externdrop += inherit->iii_externdrop;
+                       }
+               } else {
+                       assert(donating == inherit->iii_donating);
+               }
+
+               /* add in a external reference for this use of the inherit */
+               inherit->iii_externcnt++;
+               if (donating) {
+                       task_imp->iit_externcnt++;
+               }
+       } else {
+               /* initialize the previously allocated space */
+               inherit = alloc;
+               inherit->iii_bits = IIE_TYPE_INHERIT | 1;
+               inherit->iii_made = 1;
+               inherit->iii_externcnt = 1;
+               inherit->iii_externdrop = 0;
+               inherit->iii_depth = depth;
+               inherit->iii_to_task = task_imp;
+               inherit->iii_from_elem = IIE_NULL;
+               queue_init(&inherit->iii_kmsgs);
+               queue_init(&inherit->iii_inherits);
+
+               /* If donating, reflect that in the task externcnt */
+               if (donating) {
+                       inherit->iii_donating = TRUE;
+                       task_imp->iit_externcnt++;
+               } else {
+                       inherit->iii_donating = FALSE;
+               }
+
+               /*
+                * Chain our new inherit on the element it inherits from.
+                * The new inherit takes our reference on from_elem.
+                */
+               ipc_importance_inherit_link(inherit, from_elem);
+
+#if IIE_REF_DEBUG
+               ipc_importance_counter_init(&inherit->iii_elem);
+               from_elem->iie_kmsg_refs_inherited++;
+               task_imp->iit_elem.iie_task_refs_inherited++;
+#endif
+       }
+
+ out_locked:
+       /* 
+        * for those paths that came straight here: snapshot the donating status
+        * (this should match previous snapshot for other paths).
+        */
+       donating = MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits);
+
+       /* unlink the kmsg inheritance (if any) */
+       elem = ipc_importance_kmsg_unlink(kmsg);
+       assert(elem == from_elem);
+
+       /* If we didn't create a new inherit, we have some resources to release */
+       if (III_NULL == inherit || inherit != alloc) {
+               if (IIE_NULL != from_elem) {
+                       if (III_NULL != inherit) {
+                               incr_ref_counter(from_elem->iie_kmsg_refs_coalesced);
+                       } else {
+                               incr_ref_counter(from_elem->iie_kmsg_refs_dropped);
+                       }
+                       ipc_importance_release_locked(from_elem);
+                       /* importance unlocked */
+               } else {
+                       ipc_importance_unlock();
+               }
+
+               if (IIT_NULL != task_imp) {
+                       if (III_NULL != inherit) {
+                               incr_ref_counter(task_imp->iit_elem.iie_task_refs_coalesced);
+                       }
+                       ipc_importance_task_release(task_imp);
+               }
+
+               if (III_NULL != alloc)
+                       zfree(ipc_importance_inherit_zone, alloc);
+       } else {
+               /* from_elem and task_imp references transferred to new inherit */
+               ipc_importance_unlock();
+       }
+
+       /* decrement port boost count */ 
+       if (donating) {
+               ip_lock(port);
+               if (III_NULL != inherit) {
+                       /* task assertions transferred to inherit, just adjust port count */
+                       ipc_port_impcount_delta(port, -1, IP_NULL);
+                       ip_unlock(port);
+               }  else {
+                       /* drop importance from port and destination task */
+                       if (ipc_port_importance_delta(port, -1) == FALSE) {
+                               ip_unlock(port);
+                       }
+               }
+       } else if (cleared_self_donation) {
+               ip_lock(port);
+               /* drop cleared donation from port and destination task */
+               if (ipc_port_importance_delta(port, -1) == FALSE) {
+                       ip_unlock(port);
+               }
+       }
+
+       if (III_NULL != inherit) {
+               /* have an associated importance attr, even if currently not donating */
+               kmsg->ikm_header->msgh_bits |= MACH_MSGH_BITS_RAISEIMP;
+       } else {
+               /* we won't have an importance attribute associated with our message */
+               kmsg->ikm_header->msgh_bits &= ~MACH_MSGH_BITS_RAISEIMP;
+       }
+
+       return inherit;
+}
+
+/*
+ *     Routine:        ipc_importance_receive
+ *     Purpose:
+ *             Process importance attributes in a received message.
+ *
+ *             If an importance voucher attribute was sent, transform
+ *             that into an attribute value reflecting the inheritance
+ *             from the sender to the receiver.
+ *
+ *             If a static boost is received (or the voucher isn't on
+ *             a voucher-based boost), export a static boost.
+ *     Conditions:
+ *             Nothing locked.
+ */
+void
+ipc_importance_receive(
+       ipc_kmsg_t              kmsg,
+       mach_msg_option_t       option)
+{
+       unsigned int sender_pid = ((mach_msg_max_trailer_t *)
+                                  ((vm_offset_t)kmsg->ikm_header + 
+                                   round_msg(kmsg->ikm_header->msgh_size)))->msgh_audit.val[5];
+       task_t task_self = current_task();
+       int impresult = -1;
+       
+       /* convert to a voucher with an inherit importance attribute? */
+       if ((option & MACH_RCV_VOUCHER) != 0) {
+               uint8_t recipes[2 * sizeof(ipc_voucher_attr_recipe_data_t) + 
+                               sizeof(mach_voucher_attr_value_handle_t)];
+               ipc_voucher_attr_raw_recipe_array_size_t recipe_size = 0;
+               ipc_voucher_attr_recipe_t recipe = (ipc_voucher_attr_recipe_t)recipes;
+                       ipc_voucher_t recv_voucher;
+               mach_voucher_attr_value_handle_t handle;
+               ipc_importance_inherit_t inherit;
+               kern_return_t kr;
+
+               /* set up recipe to copy the old voucher */
+               if (IP_VALID(kmsg->ikm_voucher)) {
+                       ipc_voucher_t sent_voucher = (ipc_voucher_t)kmsg->ikm_voucher->ip_kobject;
+
+                       recipe->key = MACH_VOUCHER_ATTR_KEY_ALL;
+                       recipe->command = MACH_VOUCHER_ATTR_COPY;
+                       recipe->previous_voucher = sent_voucher;
+                       recipe->content_size = 0;
+                       recipe_size += sizeof(*recipe);
+               }
+
+               /*
+                * create an inheritance attribute from the kmsg (may be NULL)
+                * transferring any boosts from the kmsg linkage through the
+                * port directly to the new inheritance object.
+                */
+               inherit = ipc_importance_inherit_from(kmsg);
+               handle = (mach_voucher_attr_value_handle_t)inherit;
+
+               assert(IIE_NULL == kmsg->ikm_importance);
+
+               /* replace the importance attribute with the handle we created */
+               /*  our made reference on the inhert is donated to the voucher */
+               recipe = (ipc_voucher_attr_recipe_t)&recipes[recipe_size];
+               recipe->key = MACH_VOUCHER_ATTR_KEY_IMPORTANCE;
+               recipe->command = MACH_VOUCHER_ATTR_SET_VALUE_HANDLE;
+               recipe->previous_voucher = IPC_VOUCHER_NULL;
+               recipe->content_size = sizeof(mach_voucher_attr_value_handle_t);
+               *(mach_voucher_attr_value_handle_t *)(void *)recipe->content = handle;
+               recipe_size += sizeof(*recipe) + sizeof(mach_voucher_attr_value_handle_t);
+
+               kr = ipc_voucher_attr_control_create_mach_voucher(ipc_importance_control,
+                                                                 recipes,
+                                                                 recipe_size,
+                                                                 &recv_voucher);
+               assert(KERN_SUCCESS == kr);
+
+               /* swap the voucher port (and set voucher bits in case it didn't already exist) */
+               kmsg->ikm_header->msgh_bits |= (MACH_MSG_TYPE_MOVE_SEND << 16);
+               ipc_port_release_send(kmsg->ikm_voucher);
+               kmsg->ikm_voucher = convert_voucher_to_port(recv_voucher);
+               if (III_NULL != inherit)
+                       impresult = 2;
+
+       } else { /* Don't want a voucher */
+
+               /* got linked importance? have to drop */
+               if (IIE_NULL != kmsg->ikm_importance) {
+                       ipc_importance_elem_t elem;
+
+                       ipc_importance_lock();
+                       elem = ipc_importance_kmsg_unlink(kmsg);
+#if IIE_REF_DEBUG
+                       elem->iie_kmsg_refs_dropped++;
+#endif
+                       ipc_importance_release_locked(elem);
+                       /* importance unlocked */
+               }
+
+               /* With kmsg unlinked, can safely examine message importance attribute. */
+               if (MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits)) {
+                       ipc_importance_task_t task_imp = task_self->task_imp_base;
+                       ipc_port_t port = kmsg->ikm_header->msgh_remote_port;
+
+                       /* defensive deduction for release builds lacking the assert */
+                       ip_lock(port);
+                       ipc_port_impcount_delta(port, -1, IP_NULL);
+                       ip_unlock(port);
+
+                       /* will user accept legacy responsibility for the importance boost */
+                       if (KERN_SUCCESS == ipc_importance_task_externalize_legacy_assertion(task_imp, 1, sender_pid)) {
+                               impresult = 1;
+                       } else {
+                               /* The importance boost never applied to task (clear the bit) */
+                               kmsg->ikm_header->msgh_bits &= ~MACH_MSGH_BITS_RAISEIMP;
+                               impresult = 0;
+                       }
+               }
+       }
+
+#if IMPORTANCE_DEBUG
+       if (-1 < impresult)
+               KERNEL_DEBUG_CONSTANT_IST(KDEBUG_TRACE, (IMPORTANCE_CODE(IMP_MSG, IMP_MSG_DELV)) | DBG_FUNC_NONE,
+                               sender_pid, audit_token_pid_from_task(task_self),
+                               kmsg->ikm_header->msgh_id, impresult, 0);
+       if (impresult == 2){
+               /*
+                * This probe only covers new voucher-based path.  Legacy importance
+                * will trigger the probe in ipc_importance_task_externalize_assertion() 
+                * above and have impresult==1 here.
+                */
+               DTRACE_BOOST5(receive_boost, task_t, task_self, int, audit_token_pid_from_task(task_self), int, sender_pid, int, 1, int, task_self->task_imp_base->iit_assertcnt);
+    }
+#endif /* IMPORTANCE_DEBUG */
+}
+
+/*
+ *     Routine:        ipc_importance_unreceive
+ *     Purpose:
+ *             Undo receive of importance attributes in a message.
+ *
+ *     Conditions:
+ *             Nothing locked.
+ */
+void
+ipc_importance_unreceive(
+       ipc_kmsg_t              kmsg,
+       mach_msg_option_t       __unused option)
+{
+       /* importance should already be in the voucher and out of the kmsg */
+       assert(IIE_NULL == kmsg->ikm_importance);
+
+       /* See if there is a legacy boost to be dropped from receiver */
+       if (MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits)) {
+               ipc_importance_task_t task_imp;
+
+               kmsg->ikm_header->msgh_bits &= ~MACH_MSGH_BITS_RAISEIMP;
+               task_imp = current_task()->task_imp_base;
+               if (!IP_VALID(kmsg->ikm_voucher) && IIT_NULL != task_imp) {
+                       ipc_importance_task_drop_legacy_external_assertion(task_imp, 1);
+               }
+               /*
+                * ipc_kmsg_copyout_dest() will consume the voucher
+                * and any contained importance.
+                */
+       }
+}
+
+/*
+ *     Routine:        ipc_importance_clean
+ *     Purpose:
+ *             Clean up importance state in a kmsg that is being cleaned.
+ *             Unlink the importance chain if one was set up, and drop
+ *             the reference this kmsg held on the donor.  Then check to
+ *             if importance was carried to the port, and remove that if
+ *             needed.
+ *     Conditions:
+ *             Nothing locked.
+ */
+void
+ipc_importance_clean(
+       ipc_kmsg_t              kmsg)
+{
+       ipc_port_t              port;
+
+       /* Is the kmsg still linked? If so, remove that first */
+       if (IIE_NULL != kmsg->ikm_importance) {
+               ipc_importance_elem_t   elem;
+
+               ipc_importance_lock();
+               elem = ipc_importance_kmsg_unlink(kmsg);
+               assert(IIE_NULL != elem);
+               ipc_importance_release_locked(elem);
+               /* importance unlocked */
+       }
+
+       /* See if there is a legacy importance boost to be dropped from port */
+       if (MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits)) {
+               kmsg->ikm_header->msgh_bits &= ~MACH_MSGH_BITS_RAISEIMP;
+               port = kmsg->ikm_header->msgh_remote_port;
+               if (IP_VALID(port)) {
+                       ip_lock(port);
+                       /* inactive ports already had their importance boosts dropped */
+                       if (!ip_active(port) || 
+                           ipc_port_importance_delta(port, -1) == FALSE) {
+                               ip_unlock(port);
+                       }
+               }
+       }
+}
+
+void
+ipc_importance_assert_clean(__assert_only ipc_kmsg_t kmsg)
+{
+  assert(IIE_NULL == kmsg->ikm_importance);
+  assert(!MACH_MSGH_BITS_RAISED_IMPORTANCE(kmsg->ikm_header->msgh_bits));
+}
+
+/*
+ * IPC Importance Attribute Manager definition
+ */
+
+static kern_return_t
+ipc_importance_release_value(
+       ipc_voucher_attr_manager_t              manager,
+       mach_voucher_attr_key_t                 key,
+       mach_voucher_attr_value_handle_t        value,
+       mach_voucher_attr_value_reference_t     sync);
+
+static kern_return_t
+ipc_importance_get_value(
+       ipc_voucher_attr_manager_t                      manager,
+       mach_voucher_attr_key_t                         key,
+       mach_voucher_attr_recipe_command_t              command,
+       mach_voucher_attr_value_handle_array_t          prev_values,
+       mach_voucher_attr_value_handle_array_size_t     prev_value_count,
+       mach_voucher_attr_content_t                     content,
+       mach_voucher_attr_content_size_t                content_size,
+       mach_voucher_attr_value_handle_t                *out_value,
+       ipc_voucher_t                                   *out_value_voucher);
+
+static kern_return_t
+ipc_importance_extract_content(
+       ipc_voucher_attr_manager_t                      manager,
+       mach_voucher_attr_key_t                         key,
+       mach_voucher_attr_value_handle_array_t          values,
+       mach_voucher_attr_value_handle_array_size_t     value_count,
+       mach_voucher_attr_recipe_command_t              *out_command,
+       mach_voucher_attr_content_t                     out_content,
+       mach_voucher_attr_content_size_t                *in_out_content_size);
+
+static kern_return_t
+ipc_importance_command(
+       ipc_voucher_attr_manager_t                      manager,
+       mach_voucher_attr_key_t                         key,
+       mach_voucher_attr_value_handle_array_t          values,
+       mach_msg_type_number_t                          value_count,
+       mach_voucher_attr_command_t                     command,
+       mach_voucher_attr_content_t                     in_content,
+       mach_voucher_attr_content_size_t                in_content_size,
+       mach_voucher_attr_content_t                     out_content,
+       mach_voucher_attr_content_size_t                *out_content_size);
+
+static void
+ipc_importance_manager_release(
+       ipc_voucher_attr_manager_t              manager);
+
+struct ipc_voucher_attr_manager ipc_importance_manager = {
+       .ivam_release_value =   ipc_importance_release_value,
+       .ivam_get_value =       ipc_importance_get_value,
+       .ivam_extract_content = ipc_importance_extract_content,
+       .ivam_command =         ipc_importance_command,
+       .ivam_release =         ipc_importance_manager_release,
+};
+
+#define IMPORTANCE_ASSERT_KEY(key) assert(MACH_VOUCHER_ATTR_KEY_IMPORTANCE == (key))
+#define IMPORTANCE_ASSERT_MANAGER(manager) assert(&ipc_importance_manager == (manager))
+
+/*
+ *     Routine:        ipc_importance_release_value [Voucher Attribute Manager Interface]
+ *     Purpose:
+ *             Release what the voucher system believes is the last "made" reference
+ *             on an importance attribute value handle.  The sync parameter is used to
+ *             avoid races with new made references concurrently being returned to the
+ *             voucher system in other threads.
+ *     Conditions:
+ *             Nothing locked on entry.  May block.
+ */
+static kern_return_t
+ipc_importance_release_value(
+       ipc_voucher_attr_manager_t              __assert_only manager,
+       mach_voucher_attr_key_t                 __assert_only key,
+       mach_voucher_attr_value_handle_t        value,
+       mach_voucher_attr_value_reference_t     sync)
+{
+       ipc_importance_elem_t elem;
+
+       IMPORTANCE_ASSERT_MANAGER(manager);
+       IMPORTANCE_ASSERT_KEY(key);
+       assert(0 < sync);
+
+       elem = (ipc_importance_elem_t)value;
+
+       ipc_importance_lock();
+
+       /* Any oustanding made refs? */
+       if (sync != elem->iie_made) {
+               assert(sync < elem->iie_made);
+               ipc_importance_unlock();
+               return KERN_FAILURE;
+       }
+
+       /* clear made */ 
+       elem->iie_made = 0;
+
+       /* 
+        * If there are pending external boosts represented by this attribute, 
+        * drop them from the apropriate task 
+        */
+       if (IIE_TYPE_INHERIT == IIE_TYPE(elem)) {
+               ipc_importance_inherit_t inherit = (ipc_importance_inherit_t)elem;
+
+               assert(inherit->iii_externcnt >= inherit->iii_externdrop);
+
+               if (inherit->iii_donating) {
+                       ipc_importance_task_t imp_task = inherit->iii_to_task;
+                       uint32_t assertcnt = III_EXTERN(inherit);
+
+                       assert(ipc_importance_task_is_any_receiver_type(imp_task));
+                       assert(imp_task->iit_externcnt >= inherit->iii_externcnt);
+                       assert(imp_task->iit_externdrop >= inherit->iii_externdrop);
+                       imp_task->iit_externcnt -= inherit->iii_externcnt;
+                       imp_task->iit_externdrop -= inherit->iii_externdrop;
+                       inherit->iii_externcnt = 0;
+                       inherit->iii_externdrop = 0;
+                       inherit->iii_donating = FALSE;
+
+                       /* adjust the internal assertions - and propagate if needed */
+                       if (ipc_importance_task_check_transition(imp_task, IIT_UPDATE_DROP, assertcnt)) {
+                               ipc_importance_task_propagate_assertion_locked(imp_task, IIT_UPDATE_DROP, TRUE);
+                       }
+               } else {
+                       inherit->iii_externcnt = 0;
+                       inherit->iii_externdrop = 0;
+               }
+       } 
+
+       /* drop the made reference on elem */
+       ipc_importance_release_locked(elem);
+       /* returns unlocked */
+
+       return KERN_SUCCESS;
+}
+
+
+/*
+ *     Routine:        ipc_importance_get_value [Voucher Attribute Manager Interface]
+ *     Purpose:
+ *             Convert command and content data into a reference on a [potentially new]
+ *             attribute value.  The importance attribute manager will only allow the
+ *             caller to get a value for the current task's importance, or to redeem
+ *             an importance attribute from an existing voucher.
+ *     Conditions:
+ *             Nothing locked on entry.  May block.
+ */
+static kern_return_t
+ipc_importance_get_value(
+       ipc_voucher_attr_manager_t                      __assert_only manager,
+       mach_voucher_attr_key_t                         __assert_only key,
+       mach_voucher_attr_recipe_command_t              command,
+       mach_voucher_attr_value_handle_array_t          prev_values,
+       mach_voucher_attr_value_handle_array_size_t     prev_value_count,
+       mach_voucher_attr_content_t                     __unused content,
+       mach_voucher_attr_content_size_t                content_size,
+       mach_voucher_attr_value_handle_t                *out_value,
+       ipc_voucher_t                                   *out_value_voucher)
+{
+       ipc_importance_elem_t elem;
+       task_t self;
+
+       IMPORTANCE_ASSERT_MANAGER(manager);
+       IMPORTANCE_ASSERT_KEY(key);
+
+       if (0 != content_size)
+               return KERN_INVALID_ARGUMENT;
+
+       /* never an out voucher */
+
+       switch (command) {
+
+       case MACH_VOUCHER_ATTR_REDEEM:
+
+               /* redeem of previous values is the value */
+               if (0 < prev_value_count) {
+                       elem = (ipc_importance_elem_t)prev_values[0];
+                       assert(IIE_NULL != elem);
+
+                       ipc_importance_lock();
+                       assert(0 < elem->iie_made);
+                       elem->iie_made++;
+                       ipc_importance_unlock();
+
+                       *out_value = prev_values[0];
+                       return KERN_SUCCESS;
+               }
+
+               /* redeem of default is default */
+               *out_value = 0;
+               *out_value_voucher = IPC_VOUCHER_NULL;
+               return KERN_SUCCESS;
+
+       case MACH_VOUCHER_ATTR_IMPORTANCE_SELF:
+               self = current_task();
+
+               elem = (ipc_importance_elem_t)ipc_importance_for_task(self, TRUE);
+               /* made reference added (or IIE_NULL which isn't referenced) */
+
+               *out_value = (mach_voucher_attr_value_handle_t)elem;
+               *out_value_voucher = IPC_VOUCHER_NULL;
+               return KERN_SUCCESS;
+               
+       default:
+               /* 
+                * every other command is unknown
+                *
+                * Specifically, there is no mechanism provided to construct an
+                * importance attribute for a task/process from just a pid or
+                * task port.  It has to be copied (or redeemed) from a previous
+                * voucher that has it.
+                */
+               return KERN_INVALID_ARGUMENT;
+       }
+}
+
+/*
+ *     Routine:        ipc_importance_extract_content [Voucher Attribute Manager Interface]
+ *     Purpose:
+ *             Extract meaning from the attribute value present in a voucher.  While
+ *             the real goal is to provide commands and data that can reproduce the
+ *             voucher's value "out of thin air", this isn't possible with importance
+ *             attribute values.  Instead, return debug info to help track down dependencies.
+ *     Conditions:
+ *             Nothing locked on entry.  May block.
+ */
+static kern_return_t
+ipc_importance_extract_content(
+       ipc_voucher_attr_manager_t                      __assert_only manager,
+       mach_voucher_attr_key_t                         __assert_only key,
+       mach_voucher_attr_value_handle_array_t          values,
+       mach_voucher_attr_value_handle_array_size_t     value_count,
+       mach_voucher_attr_recipe_command_t              *out_command,
+       mach_voucher_attr_content_t                     out_content,
+       mach_voucher_attr_content_size_t                *in_out_content_size)
+{
+       mach_voucher_attr_content_size_t size = 0;
+       ipc_importance_elem_t elem;
+       unsigned int i;
+
+       IMPORTANCE_ASSERT_MANAGER(manager);
+       IMPORTANCE_ASSERT_KEY(key);
+
+       /* the first non-default value provides the data */
+       for (i = 0; i < value_count ; i++) {
+               elem = (ipc_importance_elem_t)values[i];
+               if (IIE_NULL == elem)
+                       continue;
+
+               snprintf((char *)out_content, *in_out_content_size, "Importance for pid ");
+               size = (mach_voucher_attr_content_size_t)strlen((char *)out_content);
+
+                for(;;) {
+                       ipc_importance_inherit_t inherit = III_NULL;
+                       ipc_importance_task_t task_imp;
+                       task_t task;
+                       int task_pid;
+
+                       if (IIE_TYPE_TASK == IIE_TYPE(elem)) {
+                               task_imp = (ipc_importance_task_t)elem;
+                               task = task_imp->iit_task;
+                               task_pid = (TASK_NULL != task) ?
+                                          audit_token_pid_from_task(task) : -1;
+                               snprintf((char *)out_content + size, *in_out_content_size - size, "%d", task_pid);
+                       } else {
+                               inherit = (ipc_importance_inherit_t)elem;
+                               task_imp = inherit->iii_to_task;
+                               task = task_imp->iit_task;
+                               task_pid = (TASK_NULL != task) ?
+                                          audit_token_pid_from_task(task) : -1;
+                               snprintf((char *)out_content + size, *in_out_content_size - size, 
+                                        "%d (%d of %d boosts) %s from pid ", task_pid,
+                                        III_EXTERN(inherit), inherit->iii_externcnt, 
+                                        (inherit->iii_donating) ? "donated" : "linked");
+                       }
+                       
+                       size = (mach_voucher_attr_content_size_t)strlen((char *)out_content);
+
+                       if (III_NULL == inherit)
+                               break;
+
+                       elem = inherit->iii_from_elem;
+                }
+                size++; /* account for NULL */
+       }
+       *out_command = MACH_VOUCHER_ATTR_NOOP; /* cannot be used to regenerate value */
+       *in_out_content_size = size;
+       return KERN_SUCCESS;
+}
+
+/*
+ *     Routine:        ipc_importance_command [Voucher Attribute Manager Interface]
+ *     Purpose:
+ *             Run commands against the importance attribute value found in a voucher.
+ *             No such commands are currently supported.
+ *     Conditions:
+ *             Nothing locked on entry.  May block.
+ */
+static kern_return_t
+ipc_importance_command(
+       ipc_voucher_attr_manager_t              __assert_only manager,
+       mach_voucher_attr_key_t                 __assert_only key,
+       mach_voucher_attr_value_handle_array_t  values,
+       mach_msg_type_number_t                  value_count,
+       mach_voucher_attr_command_t             command,
+       mach_voucher_attr_content_t             in_content,
+       mach_voucher_attr_content_size_t        in_content_size,
+       mach_voucher_attr_content_t             out_content,
+       mach_voucher_attr_content_size_t        *out_content_size)
+{
+       ipc_importance_inherit_t inherit;
+       ipc_importance_task_t to_task;
+       uint32_t refs, *outrefsp;
+       mach_msg_type_number_t i;
+       uint32_t externcnt;
+
+       IMPORTANCE_ASSERT_MANAGER(manager);
+       IMPORTANCE_ASSERT_KEY(key);
+
+       if (in_content_size != sizeof(refs) ||
+           (*out_content_size != 0 && *out_content_size != sizeof(refs))) {
+               return KERN_INVALID_ARGUMENT;
+       }
+       refs = *(uint32_t *)(void *)in_content;
+       outrefsp = (*out_content_size != 0) ? (uint32_t *)(void *)out_content : NULL;
+
+       if (MACH_VOUCHER_IMPORTANCE_ATTR_DROP_EXTERNAL != command) {
+               return KERN_NOT_SUPPORTED;
+       }
+
+       /* the first non-default value of the apropos type provides the data */
+       inherit = III_NULL;
+       for (i = 0; i < value_count; i++) {
+               ipc_importance_elem_t elem = (ipc_importance_elem_t)values[i];
+
+               if (IIE_NULL != elem && IIE_TYPE_INHERIT == IIE_TYPE(elem)) {
+                       inherit = (ipc_importance_inherit_t)elem;
+                       break;
+               }
+       }
+       if (III_NULL == inherit) {
+               return KERN_INVALID_ARGUMENT;
+       }
+               
+       ipc_importance_lock();
+
+       if (0 == refs) {
+               if (NULL != outrefsp) {
+                       *outrefsp = III_EXTERN(inherit);
+               }
+               ipc_importance_unlock();
+               return KERN_SUCCESS;
+       }
+
+       /* Enough external references left to drop? */
+       if (III_EXTERN(inherit) < refs) {
+               ipc_importance_unlock();
+               return KERN_FAILURE;
+       }
+
+       to_task = inherit->iii_to_task;
+       assert(ipc_importance_task_is_any_receiver_type(to_task));
+
+       /* re-base external and internal counters at the inherit and the to-task (if apropos) */
+       if (inherit->iii_donating) {
+               assert(IIT_EXTERN(to_task) >= III_EXTERN(inherit));
+               assert(to_task->iit_externcnt >= inherit->iii_externcnt);
+               assert(to_task->iit_externdrop >= inherit->iii_externdrop);
+               inherit->iii_externdrop += refs;
+               to_task->iit_externdrop += refs;
+               externcnt = III_EXTERN(inherit);
+               if (0 == externcnt) {
+                       inherit->iii_donating = FALSE;
+                       to_task->iit_externcnt -= inherit->iii_externcnt;
+                       to_task->iit_externdrop -= inherit->iii_externdrop;
+
+
+                       /* Start AppNap delay hysteresis - even if not the last boost for the task. */
+                       if (ipc_importance_delayed_drop_call != NULL &&
+                           ipc_importance_task_is_marked_denap_receiver(to_task)) {
+                               ipc_importance_task_delayed_drop(to_task);
+                       } 
+
+                       /* drop task assertions associated with the dropped boosts */
+                       if (ipc_importance_task_check_transition(to_task, IIT_UPDATE_DROP, refs)) {
+                               ipc_importance_task_propagate_assertion_locked(to_task, IIT_UPDATE_DROP, TRUE);
+                               /* may have dropped and retaken importance lock */
+                       }
+               } else {
+                       /* assert(to_task->iit_assertcnt >= refs + externcnt); */
+                       /* defensive deduction in case of assertcnt underflow */
+                       if (to_task->iit_assertcnt > refs + externcnt) {
+                               to_task->iit_assertcnt -= refs;
+                       } else {
+                               to_task->iit_assertcnt = externcnt;
+                       }
+               }
+       } else {
+               inherit->iii_externdrop += refs;
+               externcnt = III_EXTERN(inherit);
+       }
+
+       /* capture result (if requested) */
+       if (NULL != outrefsp) {
+               *outrefsp = externcnt;
+       }
+
+       ipc_importance_unlock();
+       return KERN_SUCCESS;
+}
+
+/*
+ *     Routine:        ipc_importance_manager_release [Voucher Attribute Manager Interface]
+ *     Purpose:
+ *             Release the Voucher system's reference on the IPC importance attribute
+ *             manager.
+ *     Conditions:
+ *             As this can only occur after the manager drops the Attribute control
+ *             reference granted back at registration time, and that reference is never
+ *             dropped, this should never be called.
+ */
+static void
+ipc_importance_manager_release(
+       ipc_voucher_attr_manager_t              __assert_only manager)
+{
+       IMPORTANCE_ASSERT_MANAGER(manager);
+       panic("Voucher importance manager released");
+}
+
+/*
+ *     Routine:        ipc_importance_init
+ *     Purpose:
+ *             Initialize the  IPC importance manager.
+ *     Conditions:
+ *             Zones and Vouchers are already initialized.
+ */
+void
+ipc_importance_init(void)
+{
+       natural_t ipc_importance_max = (task_max + thread_max) * 2;
+       char temp_buf[26];
+       kern_return_t kr;
+
+       if (PE_parse_boot_argn("imp_interactive_receiver", temp_buf, sizeof(temp_buf))) {
+               ipc_importance_interactive_receiver = TRUE;
+       }
+
+       ipc_importance_task_zone = zinit(sizeof(struct ipc_importance_task),
+                                        ipc_importance_max * sizeof(struct ipc_importance_task),
+                                        sizeof(struct ipc_importance_task),
+                                        "ipc task importance");
+       zone_change(ipc_importance_task_zone, Z_NOENCRYPT, TRUE);
+
+       ipc_importance_inherit_zone = zinit(sizeof(struct ipc_importance_inherit),
+                                           ipc_importance_max * sizeof(struct ipc_importance_inherit),
+                                           sizeof(struct ipc_importance_inherit),
+                                           "ipc importance inherit");
+       zone_change(ipc_importance_inherit_zone, Z_NOENCRYPT, TRUE);
+
+
+#if DEVELOPMENT || DEBUG 
+       queue_init(&global_iit_alloc_queue);
+#endif
+
+       /* initialize global locking */
+       ipc_importance_lock_init();
+
+       kr = ipc_register_well_known_mach_voucher_attr_manager(&ipc_importance_manager,
+                                               (mach_voucher_attr_value_handle_t)0,
+                                               MACH_VOUCHER_ATTR_KEY_IMPORTANCE,
+                                               &ipc_importance_control);
+       if (KERN_SUCCESS != kr)
+               printf("Voucher importance manager register returned %d", kr);
+}
+
+/*
+ *     Routine:        ipc_importance_thread_call_init
+ *     Purpose:
+ *             Initialize the IPC importance code dependent upon
+ *             thread-call support being available.
+ *     Conditions:
+ *             Thread-call mechanism is already initialized.
+ */
+void
+ipc_importance_thread_call_init(void)
+{
+       /* initialize delayed drop queue and thread-call */
+       queue_init(&ipc_importance_delayed_drop_queue);
+       ipc_importance_delayed_drop_call = 
+               thread_call_allocate(ipc_importance_task_delayed_drop_scan, NULL);
+       if (NULL == ipc_importance_delayed_drop_call) {
+               panic("ipc_importance_init");
+       }
+}
+
+/*
+ * Routing: task_importance_list_pids
+ * Purpose: list pids where task in donating importance.
+ * Conditions: To be called only from kdp stackshot code.
+ *             Will panic the system otherwise.
+ */
+extern int
+task_importance_list_pids(task_t task, int flags, int *pid_list, unsigned int max_count)
+{
+       if (lck_spin_is_acquired(&ipc_importance_lock_data) ||
+             max_count < 1 ||
+             task->task_imp_base == IIT_NULL ||
+             pid_list == NULL ||
+             flags != TASK_IMP_LIST_DONATING_PIDS) {
+               return 0;
+       }
+       unsigned int pidcount = 0;
+       task_t temp_task;
+       ipc_importance_task_t task_imp = task->task_imp_base;
+       ipc_kmsg_t temp_kmsg;
+       ipc_importance_inherit_t temp_inherit;
+       ipc_importance_elem_t elem;
+       int target_pid;
+
+       queue_iterate(&task_imp->iit_inherits, temp_inherit, ipc_importance_inherit_t, iii_inheritance) {
+               /* check space in buffer */
+               if (pidcount >= max_count) 
+                       break;
+               target_pid = -1;
+
+               if (temp_inherit->iii_donating) {
+
+#if DEVELOPMENT || DEBUG
+                       target_pid = temp_inherit->iii_to_task->iit_bsd_pid;
+#else
+                       temp_task = temp_inherit->iii_to_task->iit_task;
+                       if (temp_task != TASK_NULL) {
+                               target_pid = audit_token_pid_from_task(temp_task);
+                       }
+#endif
+               }
+
+               if (target_pid != -1) {
+                       pid_list[pidcount++] = target_pid;
+               }
+
+       }
+
+       queue_iterate(&task_imp->iit_kmsgs, temp_kmsg, ipc_kmsg_t, ikm_inheritance) {
+               if (pidcount >= max_count)
+                       break;
+               target_pid = -1;
+               elem = temp_kmsg->ikm_importance;
+               temp_task = TASK_NULL;
+
+               if (elem == IIE_NULL) {
+                       continue;
+               }
+
+               if (!(temp_kmsg->ikm_header && MACH_MSGH_BITS_RAISED_IMPORTANCE(temp_kmsg->ikm_header->msgh_bits))) {
+                       continue;
+               }
+
+               if (IIE_TYPE_TASK == IIE_TYPE(elem) && 
+                       (((ipc_importance_task_t)elem)->iit_task != TASK_NULL)) {
+                       target_pid = audit_token_pid_from_task(((ipc_importance_task_t)elem)->iit_task);
+               } else {
+                       temp_inherit = (ipc_importance_inherit_t)elem;
+#if DEVELOPMENT || DEBUG
+                       target_pid = temp_inherit->iii_to_task->iit_bsd_pid;
+#else
+                       temp_task = temp_inherit->iii_to_task->iit_task;
+                       if (temp_task != TASK_NULL) {
+                               target_pid = audit_token_pid_from_task(temp_task);
+                       }
+#endif
+               }
+
+               if (target_pid != -1) {
+                       pid_list[pidcount++] = target_pid;
+               }
+       }
+
+       return pidcount;
+}
+