📄 xpc_channel.c
字号:
if (ch->notify_queue == NULL) { kfree(ch->local_msgqueue_base); ch->local_msgqueue = NULL; continue; } spin_lock_irqsave(&ch->lock, irq_flags); if (nentries < ch->local_nentries) { dev_dbg(xpc_chan, "nentries=%d local_nentries=%d, " "partid=%d, channel=%d\n", nentries, ch->local_nentries, ch->partid, ch->number); ch->local_nentries = nentries; } spin_unlock_irqrestore(&ch->lock, irq_flags); return xpcSuccess; } dev_dbg(xpc_chan, "can't get memory for local message queue and notify " "queue, partid=%d, channel=%d\n", ch->partid, ch->number); return xpcNoMemory;}/* * Allocate the cached remote message queue. */static enum xpc_retvalxpc_allocate_remote_msgqueue(struct xpc_channel *ch){ unsigned long irq_flags; int nentries; size_t nbytes; DBUG_ON(ch->remote_nentries <= 0); // >>> may want to check for ch->flags & XPC_C_DISCONNECTING between // >>> iterations of the for-loop, bail if set? // >>> should we impose a minimum #of entries? like 4 or 8? for (nentries = ch->remote_nentries; nentries > 0; nentries--) { nbytes = nentries * ch->msg_size; ch->remote_msgqueue = xpc_kzalloc_cacheline_aligned(nbytes, GFP_KERNEL, &ch->remote_msgqueue_base); if (ch->remote_msgqueue == NULL) { continue; } spin_lock_irqsave(&ch->lock, irq_flags); if (nentries < ch->remote_nentries) { dev_dbg(xpc_chan, "nentries=%d remote_nentries=%d, " "partid=%d, channel=%d\n", nentries, ch->remote_nentries, ch->partid, ch->number); ch->remote_nentries = nentries; } spin_unlock_irqrestore(&ch->lock, irq_flags); return xpcSuccess; } dev_dbg(xpc_chan, "can't get memory for cached remote message queue, " "partid=%d, channel=%d\n", ch->partid, ch->number); return xpcNoMemory;}/* * Allocate message queues and other stuff associated with a channel. * * Note: Assumes all of the channel sizes are filled in. */static enum xpc_retvalxpc_allocate_msgqueues(struct xpc_channel *ch){ unsigned long irq_flags; enum xpc_retval ret; DBUG_ON(ch->flags & XPC_C_SETUP); if ((ret = xpc_allocate_local_msgqueue(ch)) != xpcSuccess) { return ret; } if ((ret = xpc_allocate_remote_msgqueue(ch)) != xpcSuccess) { kfree(ch->local_msgqueue_base); ch->local_msgqueue = NULL; kfree(ch->notify_queue); ch->notify_queue = NULL; return ret; } spin_lock_irqsave(&ch->lock, irq_flags); ch->flags |= XPC_C_SETUP; spin_unlock_irqrestore(&ch->lock, irq_flags); return xpcSuccess;}/* * Process a connect message from a remote partition. * * Note: xpc_process_connect() is expecting to be called with the * spin_lock_irqsave held and will leave it locked upon return. */static voidxpc_process_connect(struct xpc_channel *ch, unsigned long *irq_flags){ enum xpc_retval ret; DBUG_ON(!spin_is_locked(&ch->lock)); if (!(ch->flags & XPC_C_OPENREQUEST) || !(ch->flags & XPC_C_ROPENREQUEST)) { /* nothing more to do for now */ return; } DBUG_ON(!(ch->flags & XPC_C_CONNECTING)); if (!(ch->flags & XPC_C_SETUP)) { spin_unlock_irqrestore(&ch->lock, *irq_flags); ret = xpc_allocate_msgqueues(ch); spin_lock_irqsave(&ch->lock, *irq_flags); if (ret != xpcSuccess) { XPC_DISCONNECT_CHANNEL(ch, ret, irq_flags); } if (ch->flags & (XPC_C_CONNECTED | XPC_C_DISCONNECTING)) { return; } DBUG_ON(!(ch->flags & XPC_C_SETUP)); DBUG_ON(ch->local_msgqueue == NULL); DBUG_ON(ch->remote_msgqueue == NULL); } if (!(ch->flags & XPC_C_OPENREPLY)) { ch->flags |= XPC_C_OPENREPLY; xpc_IPI_send_openreply(ch, irq_flags); } if (!(ch->flags & XPC_C_ROPENREPLY)) { return; } DBUG_ON(ch->remote_msgqueue_pa == 0); ch->flags = (XPC_C_CONNECTED | XPC_C_SETUP); /* clear all else */ dev_info(xpc_chan, "channel %d to partition %d connected\n", ch->number, ch->partid); spin_unlock_irqrestore(&ch->lock, *irq_flags); xpc_create_kthreads(ch, 1, 0); spin_lock_irqsave(&ch->lock, *irq_flags);}/* * Notify those who wanted to be notified upon delivery of their message. */static voidxpc_notify_senders(struct xpc_channel *ch, enum xpc_retval reason, s64 put){ struct xpc_notify *notify; u8 notify_type; s64 get = ch->w_remote_GP.get - 1; while (++get < put && atomic_read(&ch->n_to_notify) > 0) { notify = &ch->notify_queue[get % ch->local_nentries]; /* * See if the notify entry indicates it was associated with * a message who's sender wants to be notified. It is possible * that it is, but someone else is doing or has done the * notification. */ notify_type = notify->type; if (notify_type == 0 || cmpxchg(¬ify->type, notify_type, 0) != notify_type) { continue; } DBUG_ON(notify_type != XPC_N_CALL); atomic_dec(&ch->n_to_notify); if (notify->func != NULL) { dev_dbg(xpc_chan, "notify->func() called, notify=0x%p, " "msg_number=%ld, partid=%d, channel=%d\n", (void *) notify, get, ch->partid, ch->number); notify->func(reason, ch->partid, ch->number, notify->key); dev_dbg(xpc_chan, "notify->func() returned, " "notify=0x%p, msg_number=%ld, partid=%d, " "channel=%d\n", (void *) notify, get, ch->partid, ch->number); } }}/* * Free up message queues and other stuff that were allocated for the specified * channel. * * Note: ch->reason and ch->reason_line are left set for debugging purposes, * they're cleared when XPC_C_DISCONNECTED is cleared. */static voidxpc_free_msgqueues(struct xpc_channel *ch){ DBUG_ON(!spin_is_locked(&ch->lock)); DBUG_ON(atomic_read(&ch->n_to_notify) != 0); ch->remote_msgqueue_pa = 0; ch->func = NULL; ch->key = NULL; ch->msg_size = 0; ch->local_nentries = 0; ch->remote_nentries = 0; ch->kthreads_assigned_limit = 0; ch->kthreads_idle_limit = 0; ch->local_GP->get = 0; ch->local_GP->put = 0; ch->remote_GP.get = 0; ch->remote_GP.put = 0; ch->w_local_GP.get = 0; ch->w_local_GP.put = 0; ch->w_remote_GP.get = 0; ch->w_remote_GP.put = 0; ch->next_msg_to_pull = 0; if (ch->flags & XPC_C_SETUP) { ch->flags &= ~XPC_C_SETUP; dev_dbg(xpc_chan, "ch->flags=0x%x, partid=%d, channel=%d\n", ch->flags, ch->partid, ch->number); kfree(ch->local_msgqueue_base); ch->local_msgqueue = NULL; kfree(ch->remote_msgqueue_base); ch->remote_msgqueue = NULL; kfree(ch->notify_queue); ch->notify_queue = NULL; }}/* * spin_lock_irqsave() is expected to be held on entry. */static voidxpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags){ struct xpc_partition *part = &xpc_partitions[ch->partid]; u32 channel_was_connected = (ch->flags & XPC_C_WASCONNECTED); DBUG_ON(!spin_is_locked(&ch->lock)); if (!(ch->flags & XPC_C_DISCONNECTING)) { return; } DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST)); /* make sure all activity has settled down first */ if (atomic_read(&ch->kthreads_assigned) > 0 || atomic_read(&ch->references) > 0) { return; } 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 */ if (xpc_partition_engaged(1UL << ch->partid)) { return; } } else { /* as long as the other side is up do the full protocol */ if (!(ch->flags & XPC_C_RCLOSEREQUEST)) { return; } if (!(ch->flags & XPC_C_CLOSEREPLY)) { ch->flags |= XPC_C_CLOSEREPLY; xpc_IPI_send_closereply(ch, irq_flags); } if (!(ch->flags & XPC_C_RCLOSEREPLY)) { return; } } /* wake those waiting for notify completion */ if (atomic_read(&ch->n_to_notify) > 0) { /* >>> we do callout while holding ch->lock */ xpc_notify_senders(ch, ch->reason, ch->w_local_GP.put); } /* both sides are disconnected now */ if (ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE) { spin_unlock_irqrestore(&ch->lock, *irq_flags); xpc_disconnect_callout(ch, xpcDisconnected); spin_lock_irqsave(&ch->lock, *irq_flags); } /* it's now safe to free the channel's message queues */ xpc_free_msgqueues(ch); /* mark disconnected, clear all other flags except XPC_C_WDISCONNECT */ ch->flags = (XPC_C_DISCONNECTED | (ch->flags & XPC_C_WDISCONNECT)); atomic_dec(&part->nchannels_active); if (channel_was_connected) { dev_info(xpc_chan, "channel %d to partition %d disconnected, " "reason=%d\n", ch->number, ch->partid, ch->reason); } if (ch->flags & XPC_C_WDISCONNECT) { /* we won't lose the CPU since we're holding ch->lock */ complete(&ch->wdisconnect_wait); } else if (ch->delayed_IPI_flags) { if (part->act_state != XPC_P_DEACTIVATING) { /* time to take action on any delayed IPI flags */ spin_lock(&part->IPI_lock); XPC_SET_IPI_FLAGS(part->local_IPI_amo, ch->number, ch->delayed_IPI_flags); spin_unlock(&part->IPI_lock); } ch->delayed_IPI_flags = 0; }}/* * Process a change in the channel's remote connection state. */static voidxpc_process_openclose_IPI(struct xpc_partition *part, int ch_number, u8 IPI_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 xpc_retval reason; spin_lock_irqsave(&ch->lock, irq_flags);again: if ((ch->flags & XPC_C_DISCONNECTED) && (ch->flags & XPC_C_WDISCONNECT)) { /* * Delay processing IPI flags until thread waiting disconnect * has had a chance to see that the channel is disconnected. */ ch->delayed_IPI_flags |= IPI_flags; spin_unlock_irqrestore(&ch->lock, irq_flags); return; } if (IPI_flags & XPC_IPI_CLOSEREQUEST) { dev_dbg(xpc_chan, "XPC_IPI_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 IPI_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(!(IPI_flags & XPC_IPI_CLOSEREPLY)); IPI_flags &= ~XPC_IPI_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 (!(IPI_flags & XPC_IPI_OPENREQUEST)) { if ((XPC_GET_IPI_FLAGS(part->local_IPI_amo, ch_number) & XPC_IPI_OPENREQUEST)) { DBUG_ON(ch->delayed_IPI_flags != 0); spin_lock(&part->IPI_lock); XPC_SET_IPI_FLAGS(part->local_IPI_amo, ch_number, XPC_IPI_CLOSEREQUEST); spin_unlock(&part->IPI_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); } IPI_flags &= ~(XPC_IPI_OPENREQUEST | XPC_IPI_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 <= xpcSuccess || reason > xpcUnknownReason) { reason = xpcUnknownReason; } else if (reason == xpcUnregistering) { reason = xpcOtherUnregistering; } XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags); DBUG_ON(IPI_flags & XPC_IPI_CLOSEREPLY); spin_unlock_irqrestore(&ch->lock, irq_flags); return; } xpc_process_disconnect(ch, &irq_flags); } if (IPI_flags & XPC_IPI_CLOSEREPLY) { dev_dbg(xpc_chan, "XPC_IPI_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_DEACTIVATING); spin_unlock_irqrestore(&ch->lock, irq_flags); return; } DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -