void xpc_process_sent_chctl_flags(struct xpc_partition *part) { unsigned long irq_flags; union xpc_channel_ctl_flags chctl; struct xpc_channel *ch; int ch_number; u32 ch_flags; chctl.all_flags = xpc_arch_ops.get_chctl_all_flags(part); for (ch_number = 0; ch_number < part->nchannels; ch_number++) { ch = &part->channels[ch_number]; if (chctl.flags[ch_number] & XPC_OPENCLOSE_CHCTL_FLAGS) { xpc_process_openclose_chctl_flags(part, ch_number, chctl.flags[ch_number]); } ch_flags = ch->flags; if (ch_flags & XPC_C_DISCONNECTING) { spin_lock_irqsave(&ch->lock, irq_flags); xpc_process_disconnect(ch, &irq_flags); spin_unlock_irqrestore(&ch->lock, irq_flags); continue; } if (part->act_state == XPC_P_AS_DEACTIVATING) continue; if (!(ch_flags & XPC_C_CONNECTED)) { if (!(ch_flags & XPC_C_OPENREQUEST)) { DBUG_ON(ch_flags & XPC_C_SETUP); (void)xpc_connect_channel(ch); } continue; } if (chctl.flags[ch_number] & XPC_MSG_CHCTL_FLAGS) xpc_arch_ops.process_msg_chctl_flags(part, ch_number); } }
void xpc_process_sent_chctl_flags(struct xpc_partition *part) { unsigned long irq_flags; union xpc_channel_ctl_flags chctl; struct xpc_channel *ch; int ch_number; u32 ch_flags; chctl.all_flags = xpc_get_chctl_all_flags(part); /* * Initiate channel connections for registered channels. * * For each connected channel that has pending messages activate idle * kthreads and/or create new kthreads as needed. */ for (ch_number = 0; ch_number < part->nchannels; ch_number++) { ch = &part->channels[ch_number]; /* * Process any open or close related chctl flags, and then deal * with connecting or disconnecting the channel as required. */ if (chctl.flags[ch_number] & XPC_OPENCLOSE_CHCTL_FLAGS) { xpc_process_openclose_chctl_flags(part, ch_number, chctl.flags[ch_number]); } ch_flags = ch->flags; /* need an atomic snapshot of flags */ if (ch_flags & XPC_C_DISCONNECTING) { spin_lock_irqsave(&ch->lock, irq_flags); xpc_process_disconnect(ch, &irq_flags); spin_unlock_irqrestore(&ch->lock, irq_flags); continue; } if (part->act_state == XPC_P_AS_DEACTIVATING) continue; if (!(ch_flags & XPC_C_CONNECTED)) { if (!(ch_flags & XPC_C_OPENREQUEST)) { DBUG_ON(ch_flags & XPC_C_SETUP); (void)xpc_connect_channel(ch); } else { spin_lock_irqsave(&ch->lock, irq_flags); xpc_process_connect(ch, &irq_flags); spin_unlock_irqrestore(&ch->lock, irq_flags); } continue; } /* * Process any message related chctl flags, this may involve * the activation of kthreads to deliver any pending messages * sent from the other partition. */ if (chctl.flags[ch_number] & XPC_MSG_CHCTL_FLAGS) xpc_process_msg_chctl_flags(part, ch_number); } }
/* * Process a change in the channel's remote connection state. */ static void xpc_process_openclose_chctl_flags(struct xpc_partition *part, int ch_number, u8 chctl_flags) { unsigned long irq_flags; struct xpc_openclose_args *args = &part->remote_openclose_args[ch_number]; struct xpc_channel *ch = &part->channels[ch_number]; enum xp_retval reason; spin_lock_irqsave(&ch->lock, irq_flags); again: if ((ch->flags & XPC_C_DISCONNECTED) && (ch->flags & XPC_C_WDISCONNECT)) { /* * Delay processing chctl flags until thread waiting disconnect * has had a chance to see that the channel is disconnected. */ ch->delayed_chctl_flags |= chctl_flags; spin_unlock_irqrestore(&ch->lock, irq_flags); return; } if (chctl_flags & XPC_CHCTL_CLOSEREQUEST) { dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREQUEST (reason=%d) received " "from partid=%d, channel=%d\n", args->reason, ch->partid, ch->number); /* * If RCLOSEREQUEST is set, we're probably waiting for * RCLOSEREPLY. We should find it and a ROPENREQUEST packed * with this RCLOSEREQUEST in the chctl_flags. */ if (ch->flags & XPC_C_RCLOSEREQUEST) { DBUG_ON(!(ch->flags & XPC_C_DISCONNECTING)); DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST)); DBUG_ON(!(ch->flags & XPC_C_CLOSEREPLY)); DBUG_ON(ch->flags & XPC_C_RCLOSEREPLY); DBUG_ON(!(chctl_flags & XPC_CHCTL_CLOSEREPLY)); chctl_flags &= ~XPC_CHCTL_CLOSEREPLY; ch->flags |= XPC_C_RCLOSEREPLY; /* both sides have finished disconnecting */ xpc_process_disconnect(ch, &irq_flags); DBUG_ON(!(ch->flags & XPC_C_DISCONNECTED)); goto again; } if (ch->flags & XPC_C_DISCONNECTED) { if (!(chctl_flags & XPC_CHCTL_OPENREQUEST)) { if (part->chctl.flags[ch_number] & XPC_CHCTL_OPENREQUEST) { DBUG_ON(ch->delayed_chctl_flags != 0); spin_lock(&part->chctl_lock); part->chctl.flags[ch_number] |= XPC_CHCTL_CLOSEREQUEST; spin_unlock(&part->chctl_lock); } spin_unlock_irqrestore(&ch->lock, irq_flags); return; } XPC_SET_REASON(ch, 0, 0); ch->flags &= ~XPC_C_DISCONNECTED; atomic_inc(&part->nchannels_active); ch->flags |= (XPC_C_CONNECTING | XPC_C_ROPENREQUEST); } chctl_flags &= ~(XPC_CHCTL_OPENREQUEST | XPC_CHCTL_OPENREPLY); /* * The meaningful CLOSEREQUEST connection state fields are: * reason = reason connection is to be closed */ ch->flags |= XPC_C_RCLOSEREQUEST; if (!(ch->flags & XPC_C_DISCONNECTING)) { reason = args->reason; if (reason <= xpSuccess || reason > xpUnknownReason) reason = xpUnknownReason; else if (reason == xpUnregistering) reason = xpOtherUnregistering; XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags); DBUG_ON(chctl_flags & XPC_CHCTL_CLOSEREPLY); spin_unlock_irqrestore(&ch->lock, irq_flags); return; } xpc_process_disconnect(ch, &irq_flags); } if (chctl_flags & XPC_CHCTL_CLOSEREPLY) { dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREPLY received from partid=" "%d, channel=%d\n", ch->partid, ch->number); if (ch->flags & XPC_C_DISCONNECTED) { DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING); spin_unlock_irqrestore(&ch->lock, irq_flags); return; } DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST)); if (!(ch->flags & XPC_C_RCLOSEREQUEST)) { if (part->chctl.flags[ch_number] & XPC_CHCTL_CLOSEREQUEST) { DBUG_ON(ch->delayed_chctl_flags != 0); spin_lock(&part->chctl_lock); part->chctl.flags[ch_number] |= XPC_CHCTL_CLOSEREPLY; spin_unlock(&part->chctl_lock); } spin_unlock_irqrestore(&ch->lock, irq_flags); return; } ch->flags |= XPC_C_RCLOSEREPLY; if (ch->flags & XPC_C_CLOSEREPLY) { /* both sides have finished disconnecting */ xpc_process_disconnect(ch, &irq_flags); } } if (chctl_flags & XPC_CHCTL_OPENREQUEST) { dev_dbg(xpc_chan, "XPC_CHCTL_OPENREQUEST (entry_size=%d, " "local_nentries=%d) received from partid=%d, " "channel=%d\n", args->entry_size, args->local_nentries, ch->partid, ch->number); if (part->act_state == XPC_P_AS_DEACTIVATING || (ch->flags & XPC_C_ROPENREQUEST)) { spin_unlock_irqrestore(&ch->lock, irq_flags); return; } if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_WDISCONNECT)) { ch->delayed_chctl_flags |= XPC_CHCTL_OPENREQUEST; spin_unlock_irqrestore(&ch->lock, irq_flags); return; } DBUG_ON(!(ch->flags & (XPC_C_DISCONNECTED | XPC_C_OPENREQUEST))); DBUG_ON(ch->flags & (XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY | XPC_C_OPENREPLY | XPC_C_CONNECTED)); /* * The meaningful OPENREQUEST connection state fields are: * entry_size = size of channel's messages in bytes * local_nentries = remote partition's local_nentries */ if (args->entry_size == 0 || args->local_nentries == 0) { /* assume OPENREQUEST was delayed by mistake */ spin_unlock_irqrestore(&ch->lock, irq_flags); return; } ch->flags |= (XPC_C_ROPENREQUEST | XPC_C_CONNECTING); ch->remote_nentries = args->local_nentries; if (ch->flags & XPC_C_OPENREQUEST) { if (args->entry_size != ch->entry_size) { XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes, &irq_flags); spin_unlock_irqrestore(&ch->lock, irq_flags); return; } } else { ch->entry_size = args->entry_size; XPC_SET_REASON(ch, 0, 0); ch->flags &= ~XPC_C_DISCONNECTED; atomic_inc(&part->nchannels_active); } xpc_process_connect(ch, &irq_flags); } if (chctl_flags & XPC_CHCTL_OPENREPLY) { dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY (local_msgqueue_pa=" "0x%lx, local_nentries=%d, remote_nentries=%d) " "received from partid=%d, channel=%d\n", args->local_msgqueue_pa, args->local_nentries, args->remote_nentries, ch->partid, ch->number); if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) { spin_unlock_irqrestore(&ch->lock, irq_flags); return; } if (!(ch->flags & XPC_C_OPENREQUEST)) { XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError, &irq_flags); spin_unlock_irqrestore(&ch->lock, irq_flags); return; } DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST)); DBUG_ON(ch->flags & XPC_C_CONNECTED); /* * The meaningful OPENREPLY connection state fields are: * local_msgqueue_pa = physical address of remote * partition's local_msgqueue * local_nentries = remote partition's local_nentries * remote_nentries = remote partition's remote_nentries */ DBUG_ON(args->local_msgqueue_pa == 0); DBUG_ON(args->local_nentries == 0); DBUG_ON(args->remote_nentries == 0); ch->flags |= XPC_C_ROPENREPLY; xpc_save_remote_msgqueue_pa(ch, args->local_msgqueue_pa); if (args->local_nentries < ch->remote_nentries) { dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new " "remote_nentries=%d, old remote_nentries=%d, " "partid=%d, channel=%d\n", args->local_nentries, ch->remote_nentries, ch->partid, ch->number); ch->remote_nentries = args->local_nentries; } if (args->remote_nentries < ch->local_nentries) { dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new " "local_nentries=%d, old local_nentries=%d, " "partid=%d, channel=%d\n", args->remote_nentries, ch->local_nentries, ch->partid, ch->number); ch->local_nentries = args->remote_nentries; } xpc_process_connect(ch, &irq_flags); } spin_unlock_irqrestore(&ch->lock, irq_flags); }
static void xpc_process_openclose_chctl_flags(struct xpc_partition *part, int ch_number, u8 chctl_flags) { unsigned long irq_flags; struct xpc_openclose_args *args = &part->remote_openclose_args[ch_number]; struct xpc_channel *ch = &part->channels[ch_number]; enum xp_retval reason; enum xp_retval ret; int create_kthread = 0; spin_lock_irqsave(&ch->lock, irq_flags); again: if ((ch->flags & XPC_C_DISCONNECTED) && (ch->flags & XPC_C_WDISCONNECT)) { ch->delayed_chctl_flags |= chctl_flags; goto out; } if (chctl_flags & XPC_CHCTL_CLOSEREQUEST) { dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREQUEST (reason=%d) received " "from partid=%d, channel=%d\n", args->reason, ch->partid, ch->number); if (ch->flags & XPC_C_RCLOSEREQUEST) { DBUG_ON(!(ch->flags & XPC_C_DISCONNECTING)); DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST)); DBUG_ON(!(ch->flags & XPC_C_CLOSEREPLY)); DBUG_ON(ch->flags & XPC_C_RCLOSEREPLY); DBUG_ON(!(chctl_flags & XPC_CHCTL_CLOSEREPLY)); chctl_flags &= ~XPC_CHCTL_CLOSEREPLY; ch->flags |= XPC_C_RCLOSEREPLY; xpc_process_disconnect(ch, &irq_flags); DBUG_ON(!(ch->flags & XPC_C_DISCONNECTED)); goto again; } if (ch->flags & XPC_C_DISCONNECTED) { if (!(chctl_flags & XPC_CHCTL_OPENREQUEST)) { if (part->chctl.flags[ch_number] & XPC_CHCTL_OPENREQUEST) { DBUG_ON(ch->delayed_chctl_flags != 0); spin_lock(&part->chctl_lock); part->chctl.flags[ch_number] |= XPC_CHCTL_CLOSEREQUEST; spin_unlock(&part->chctl_lock); } goto out; } XPC_SET_REASON(ch, 0, 0); ch->flags &= ~XPC_C_DISCONNECTED; atomic_inc(&part->nchannels_active); ch->flags |= (XPC_C_CONNECTING | XPC_C_ROPENREQUEST); } chctl_flags &= ~(XPC_CHCTL_OPENREQUEST | XPC_CHCTL_OPENREPLY | XPC_CHCTL_OPENCOMPLETE); ch->flags |= XPC_C_RCLOSEREQUEST; if (!(ch->flags & XPC_C_DISCONNECTING)) { reason = args->reason; if (reason <= xpSuccess || reason > xpUnknownReason) reason = xpUnknownReason; else if (reason == xpUnregistering) reason = xpOtherUnregistering; XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags); DBUG_ON(chctl_flags & XPC_CHCTL_CLOSEREPLY); goto out; } xpc_process_disconnect(ch, &irq_flags); } if (chctl_flags & XPC_CHCTL_CLOSEREPLY) { dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREPLY received from partid=" "%d, channel=%d\n", ch->partid, ch->number); if (ch->flags & XPC_C_DISCONNECTED) { DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING); goto out; } DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST)); if (!(ch->flags & XPC_C_RCLOSEREQUEST)) { if (part->chctl.flags[ch_number] & XPC_CHCTL_CLOSEREQUEST) { DBUG_ON(ch->delayed_chctl_flags != 0); spin_lock(&part->chctl_lock); part->chctl.flags[ch_number] |= XPC_CHCTL_CLOSEREPLY; spin_unlock(&part->chctl_lock); } goto out; } ch->flags |= XPC_C_RCLOSEREPLY; if (ch->flags & XPC_C_CLOSEREPLY) { xpc_process_disconnect(ch, &irq_flags); } } if (chctl_flags & XPC_CHCTL_OPENREQUEST) { dev_dbg(xpc_chan, "XPC_CHCTL_OPENREQUEST (entry_size=%d, " "local_nentries=%d) received from partid=%d, " "channel=%d\n", args->entry_size, args->local_nentries, ch->partid, ch->number); if (part->act_state == XPC_P_AS_DEACTIVATING || (ch->flags & XPC_C_ROPENREQUEST)) { goto out; } if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_WDISCONNECT)) { ch->delayed_chctl_flags |= XPC_CHCTL_OPENREQUEST; goto out; } DBUG_ON(!(ch->flags & (XPC_C_DISCONNECTED | XPC_C_OPENREQUEST))); DBUG_ON(ch->flags & (XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY | XPC_C_OPENREPLY | XPC_C_CONNECTED)); if (args->entry_size == 0 || args->local_nentries == 0) { goto out; } ch->flags |= (XPC_C_ROPENREQUEST | XPC_C_CONNECTING); ch->remote_nentries = args->local_nentries; if (ch->flags & XPC_C_OPENREQUEST) { if (args->entry_size != ch->entry_size) { XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes, &irq_flags); goto out; } } else { ch->entry_size = args->entry_size; XPC_SET_REASON(ch, 0, 0); ch->flags &= ~XPC_C_DISCONNECTED; atomic_inc(&part->nchannels_active); } xpc_process_connect(ch, &irq_flags); } if (chctl_flags & XPC_CHCTL_OPENREPLY) { dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY (local_msgqueue_pa=" "0x%lx, local_nentries=%d, remote_nentries=%d) " "received from partid=%d, channel=%d\n", args->local_msgqueue_pa, args->local_nentries, args->remote_nentries, ch->partid, ch->number); if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) goto out; if (!(ch->flags & XPC_C_OPENREQUEST)) { XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError, &irq_flags); goto out; } DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST)); DBUG_ON(ch->flags & XPC_C_CONNECTED); DBUG_ON(args->local_msgqueue_pa == 0); DBUG_ON(args->local_nentries == 0); DBUG_ON(args->remote_nentries == 0); ret = xpc_arch_ops.save_remote_msgqueue_pa(ch, args->local_msgqueue_pa); if (ret != xpSuccess) { XPC_DISCONNECT_CHANNEL(ch, ret, &irq_flags); goto out; } ch->flags |= XPC_C_ROPENREPLY; if (args->local_nentries < ch->remote_nentries) { dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new " "remote_nentries=%d, old remote_nentries=%d, " "partid=%d, channel=%d\n", args->local_nentries, ch->remote_nentries, ch->partid, ch->number); ch->remote_nentries = args->local_nentries; } if (args->remote_nentries < ch->local_nentries) { dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new " "local_nentries=%d, old local_nentries=%d, " "partid=%d, channel=%d\n", args->remote_nentries, ch->local_nentries, ch->partid, ch->number); ch->local_nentries = args->remote_nentries; } xpc_process_connect(ch, &irq_flags); } if (chctl_flags & XPC_CHCTL_OPENCOMPLETE) { dev_dbg(xpc_chan, "XPC_CHCTL_OPENCOMPLETE received from " "partid=%d, channel=%d\n", ch->partid, ch->number); if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) goto out; if (!(ch->flags & XPC_C_OPENREQUEST) || !(ch->flags & XPC_C_OPENREPLY)) { XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError, &irq_flags); goto out; } DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST)); DBUG_ON(!(ch->flags & XPC_C_ROPENREPLY)); DBUG_ON(!(ch->flags & XPC_C_CONNECTED)); ch->flags |= XPC_C_ROPENCOMPLETE; xpc_process_connect(ch, &irq_flags); create_kthread = 1; } out: spin_unlock_irqrestore(&ch->lock, irq_flags); if (create_kthread) xpc_create_kthreads(ch, 1, 0); }