+/*
+ * Routine: ipc_importance_inherit_from_task
+ * Purpose:
+ * Create a reference for an importance attribute representing
+ * an inheritance between the to_task and from_task. The iii
+ * created will be marked as III_FLAGS_FOR_OTHERS.
+ *
+ * It will not dedup any iii which are not marked as III_FLAGS_FOR_OTHERS.
+ *
+ * If the task is inactive, there isn't any need to return a new reference.
+ * Conditions:
+ * Nothing locked on entry. May block.
+ * It should not be called from voucher subsystem.
+ */
+static ipc_importance_inherit_t
+ipc_importance_inherit_from_task(
+ task_t from_task,
+ task_t to_task)
+{
+ ipc_importance_task_t to_task_imp = IIT_NULL;
+ ipc_importance_task_t from_task_imp = IIT_NULL;
+ ipc_importance_elem_t from_elem = IIE_NULL;
+
+ ipc_importance_inherit_t inherit = III_NULL;
+ ipc_importance_inherit_t alloc = III_NULL;
+ boolean_t donating;
+ uint32_t depth = 1;
+
+ to_task_imp = ipc_importance_for_task(to_task, FALSE);
+ from_task_imp = ipc_importance_for_task(from_task, FALSE);
+ from_elem = (ipc_importance_elem_t)from_task_imp;
+
+ ipc_importance_lock();
+
+ if (IIT_NULL == to_task_imp || IIT_NULL == from_task_imp) {
+ goto out_locked;
+ }
+
+ /*
+ * No need to set up an inherit linkage if the to_task or from_task
+ * isn't a receiver of one type or the other.
+ */
+ if (!ipc_importance_task_is_any_receiver_type(to_task_imp) ||
+ !ipc_importance_task_is_any_receiver_type(from_task_imp)) {
+ goto out_locked;
+ }
+
+ /* Do not allow to create a linkage to self */
+ if (to_task_imp == from_task_imp) {
+ goto out_locked;
+ }
+
+ incr_ref_counter(to_task_imp->iit_elem.iie_task_refs_added_inherit_from);
+ incr_ref_counter(from_elem->iie_kmsg_refs_added);
+
+ /*
+ * 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) {
+ inherit = ipc_importance_inherit_find(from_elem, to_task_imp, depth);
+
+ /* 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 = ipc_importance_task_is_donor(from_task_imp);
+
+ if (III_NULL != inherit) {
+ /* We found one, piggyback on that */
+ assert(0 < III_REFS(inherit));
+ assert(0 < IIE_REFS(inherit->iii_from_elem));
+
+ /* Take a reference for inherit */
+ 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) {
+ to_task_imp->iit_externcnt += inherit->iii_externcnt;
+ to_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++;
+ } else {
+ /* initialize the previously allocated space */
+ inherit = alloc;
+ inherit->iii_bits = IIE_TYPE_INHERIT | 1;
+ inherit->iii_made = 0;
+ inherit->iii_externcnt = 1;
+ inherit->iii_externdrop = 0;
+ inherit->iii_depth = depth;
+ inherit->iii_to_task = to_task_imp;
+ inherit->iii_from_elem = IIE_NULL;
+ queue_init(&inherit->iii_kmsgs);
+
+ if (donating) {
+ inherit->iii_donating = TRUE;
+ } 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:
+
+ /* If found inherit and donating, reflect that in the task externcnt */
+ if (III_NULL != inherit && donating) {
+ to_task_imp->iit_externcnt++;
+ /* take the internal assertion */
+ ipc_importance_task_hold_internal_assertion_locked(to_task_imp, 1);
+ /* may have dropped and retaken importance lock */
+ }
+
+ /* 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 != to_task_imp) {
+ if (III_NULL != inherit) {
+ incr_ref_counter(to_task_imp->iit_elem.iie_task_refs_coalesced);
+ }
+ ipc_importance_task_release(to_task_imp);
+ }
+
+ if (III_NULL != alloc) {
+ zfree(ipc_importance_inherit_zone, alloc);
+ }
+ } else {
+ /* from_elem and to_task_imp references transferred to new inherit */
+ ipc_importance_unlock();
+ }
+
+ return inherit;
+}
+