ch->number, ch->partid);
 
        spin_unlock_irqrestore(&ch->lock, *irq_flags);
-       xpc_create_kthreads(ch, 1);
+       xpc_create_kthreads(ch, 1, 0);
        spin_lock_irqsave(&ch->lock, *irq_flags);
 }
 
 
        /* make sure all activity has settled down first */
 
-       if (atomic_read(&ch->references) > 0 ||
-                       ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) &&
-                       !(ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE))) {
+       if (atomic_read(&ch->kthreads_assigned) > 0 ||
+                               atomic_read(&ch->references) > 0) {
                return;
        }
-       DBUG_ON(atomic_read(&ch->kthreads_assigned) != 0);
+       DBUG_ON((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) &&
+                       !(ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE));
 
        if (part->act_state == XPC_P_DEACTIVATING) {
                /* can't proceed until the other side disengages from us */
        /* wake all idle kthreads so they can exit */
        if (atomic_read(&ch->kthreads_idle) > 0) {
                wake_up_all(&ch->idle_wq);
+
+       } else if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) &&
+                       !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) {
+               /* start a kthread that will do the xpcDisconnecting callout */
+               xpc_create_kthreads(ch, 1, 1);
        }
 
        /* wake those waiting to allocate an entry from the local msg queue */
 
        dev_dbg(xpc_chan, "create %d new kthreads, partid=%d, channel=%d\n",
                needed, ch->partid, ch->number);
 
-       xpc_create_kthreads(ch, needed);
+       xpc_create_kthreads(ch, needed, 0);
 }
 
 
                xpc_kthread_waitmsgs(part, ch);
        }
 
-       if (atomic_dec_return(&ch->kthreads_assigned) == 0) {
-               spin_lock_irqsave(&ch->lock, irq_flags);
-               if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) &&
-                               !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) {
-                       ch->flags |= XPC_C_DISCONNECTINGCALLOUT;
-                       spin_unlock_irqrestore(&ch->lock, irq_flags);
+       /* let registerer know that connection is disconnecting */
 
-                       xpc_disconnect_callout(ch, xpcDisconnecting);
-
-                       spin_lock_irqsave(&ch->lock, irq_flags);
-                       ch->flags |= XPC_C_DISCONNECTINGCALLOUT_MADE;
-               }
+       spin_lock_irqsave(&ch->lock, irq_flags);
+       if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) &&
+                       !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) {
+               ch->flags |= XPC_C_DISCONNECTINGCALLOUT;
                spin_unlock_irqrestore(&ch->lock, irq_flags);
+
+               xpc_disconnect_callout(ch, xpcDisconnecting);
+
+               spin_lock_irqsave(&ch->lock, irq_flags);
+               ch->flags |= XPC_C_DISCONNECTINGCALLOUT_MADE;
+       }
+       spin_unlock_irqrestore(&ch->lock, irq_flags);
+
+       if (atomic_dec_return(&ch->kthreads_assigned) == 0) {
                if (atomic_dec_return(&part->nchannels_engaged) == 0) {
                        xpc_mark_partition_disengaged(part);
                        xpc_IPI_send_disengage(part);
                }
        }
 
-
        xpc_msgqueue_deref(ch);
 
        dev_dbg(xpc_chan, "kthread exiting, partid=%d, channel=%d\n",
  * partition.
  */
 void
-xpc_create_kthreads(struct xpc_channel *ch, int needed)
+xpc_create_kthreads(struct xpc_channel *ch, int needed,
+                       int ignore_disconnecting)
 {
        unsigned long irq_flags;
        pid_t pid;
                 * kthread. That kthread is responsible for doing the
                 * counterpart to the following before it exits.
                 */
+               if (ignore_disconnecting) {
+                       if (!atomic_inc_not_zero(&ch->kthreads_assigned)) {
+                               /* kthreads assigned had gone to zero */
+                               BUG_ON(!(ch->flags &
+                                       XPC_C_DISCONNECTINGCALLOUT_MADE));
+                               break;
+                       }
+
+               } else if (ch->flags & XPC_C_DISCONNECTING) {
+                       break;
+
+               } else if (atomic_inc_return(&ch->kthreads_assigned) == 1) {
+                       if (atomic_inc_return(&part->nchannels_engaged) == 1)
+                               xpc_mark_partition_engaged(part);
+               }
                (void) xpc_part_ref(part);
                xpc_msgqueue_ref(ch);
-               if (atomic_inc_return(&ch->kthreads_assigned) == 1 &&
-                   atomic_inc_return(&part->nchannels_engaged) == 1) {
-                       xpc_mark_partition_engaged(part);
-               }
 
                pid = kernel_thread(xpc_daemonize_kthread, (void *) args, 0);
                if (pid < 0) {
                        /* the fork failed */
+
+                       /*
+                        * NOTE: if (ignore_disconnecting &&
+                        * !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) is true,
+                        * then we'll deadlock if all other kthreads assigned
+                        * to this channel are blocked in the channel's
+                        * registerer, because the only thing that will unblock
+                        * them is the xpcDisconnecting callout that this
+                        * failed kernel_thread would have made.
+                        */
+
                        if (atomic_dec_return(&ch->kthreads_assigned) == 0 &&
                            atomic_dec_return(&part->nchannels_engaged) == 0) {
                                xpc_mark_partition_disengaged(part);
                                 * Flag this as an error only if we have an
                                 * insufficient #of kthreads for the channel
                                 * to function.
-                                *
-                                * No xpc_msgqueue_ref() is needed here since
-                                * the channel mgr is doing this.
                                 */
                                spin_lock_irqsave(&ch->lock, irq_flags);
                                XPC_DISCONNECT_CHANNEL(ch, xpcLackOfResources,