/* * For each partition that XPC has established communications with, there is * a minimum of one kernel thread assigned to perform any operation that * may potentially sleep or block (basically the callouts to the asynchronous * functions registered via xpc_connect()). * * Additional kthreads are created and destroyed by XPC as the workload * demands. * * A kthread is assigned to one of the active channels that exists for a given * partition. */ void xpc_create_kthreads(struct xpc_channel *ch, int needed, int ignore_disconnecting) { unsigned long irq_flags; u64 args = XPC_PACK_ARGS(ch->partid, ch->number); struct xpc_partition *part = &xpc_partitions[ch->partid]; struct task_struct *kthread; while (needed-- > 0) { /* * The following is done on behalf of the newly created * 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 && atomic_inc_return(&part->nchannels_engaged) == 1) { xpc_indicate_partition_engaged(part); } (void)xpc_part_ref(part); xpc_msgqueue_ref(ch); kthread = kthread_run(xpc_kthread_start, (void *)args, "xpc%02dc%d", ch->partid, ch->number); if (IS_ERR(kthread)) { /* 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 xpDisconnecting callout that this * failed kthread_run() would have made. */ if (atomic_dec_return(&ch->kthreads_assigned) == 0 && atomic_dec_return(&part->nchannels_engaged) == 0) { xpc_indicate_partition_disengaged(part); } xpc_msgqueue_deref(ch); xpc_part_deref(part); if (atomic_read(&ch->kthreads_assigned) < ch->kthreads_idle_limit) { /* * Flag this as an error only if we have an * insufficient #of kthreads for the channel * to function. */ spin_lock_irqsave(&ch->lock, irq_flags); XPC_DISCONNECT_CHANNEL(ch, xpLackOfResources, &irq_flags); spin_unlock_irqrestore(&ch->lock, irq_flags); } break; } } }
void xpc_create_kthreads(struct xpc_channel *ch, int needed, int ignore_disconnecting) { unsigned long irq_flags; u64 args = XPC_PACK_ARGS(ch->partid, ch->number); struct xpc_partition *part = &xpc_partitions[ch->partid]; struct task_struct *kthread; void (*indicate_partition_disengaged) (struct xpc_partition *) = xpc_arch_ops.indicate_partition_disengaged; while (needed-- > 0) { /* */ if (ignore_disconnecting) { if (!atomic_inc_not_zero(&ch->kthreads_assigned)) { /* */ 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 && atomic_inc_return(&part->nchannels_engaged) == 1) { xpc_arch_ops.indicate_partition_engaged(part); } (void)xpc_part_ref(part); xpc_msgqueue_ref(ch); kthread = kthread_run(xpc_kthread_start, (void *)args, "xpc%02dc%d", ch->partid, ch->number); if (IS_ERR(kthread)) { /* */ /* */ if (atomic_dec_return(&ch->kthreads_assigned) == 0 && atomic_dec_return(&part->nchannels_engaged) == 0) { indicate_partition_disengaged(part); } xpc_msgqueue_deref(ch); xpc_part_deref(part); if (atomic_read(&ch->kthreads_assigned) < ch->kthreads_idle_limit) { /* */ spin_lock_irqsave(&ch->lock, irq_flags); XPC_DISCONNECT_CHANNEL(ch, xpLackOfResources, &irq_flags); spin_unlock_irqrestore(&ch->lock, irq_flags); } break; } } }