📄 xpc_channel.c
字号:
/* disconnect channels associated with the partition going down */ for (ch_number = 0; ch_number < part->nchannels; ch_number++) { ch = &part->channels[ch_number]; xpc_msgqueue_ref(ch); spin_lock_irqsave(&ch->lock, irq_flags); XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags); spin_unlock_irqrestore(&ch->lock, irq_flags); xpc_msgqueue_deref(ch); } xpc_wakeup_channel_mgr(part); xpc_part_deref(part);}/* * Teardown the infrastructure necessary to support XPartition Communication * between the specified remote partition and the local one. */voidxpc_teardown_infrastructure(struct xpc_partition *part){ partid_t partid = XPC_PARTID(part); /* * We start off by making this partition inaccessible to local * processes by marking it as no longer setup. Then we make it * inaccessible to remote processes by clearing the XPC per partition * specific variable's magic # (which indicates that these variables * are no longer valid) and by ignoring all XPC notify IPIs sent to * this partition. */ DBUG_ON(atomic_read(&part->nchannels_engaged) != 0); DBUG_ON(atomic_read(&part->nchannels_active) != 0); DBUG_ON(part->setup_state != XPC_P_SETUP); part->setup_state = XPC_P_WTEARDOWN; xpc_vars_part[partid].magic = 0; free_irq(SGI_XPC_NOTIFY, (void *) (u64) partid); /* * Before proceeding with the teardown we have to wait until all * existing references cease. */ wait_event(part->teardown_wq, (atomic_read(&part->references) == 0)); /* now we can begin tearing down the infrastructure */ part->setup_state = XPC_P_TORNDOWN; /* in case we've still got outstanding timers registered... */ del_timer_sync(&part->dropped_IPI_timer); kfree(part->remote_openclose_args_base); part->remote_openclose_args = NULL; kfree(part->local_openclose_args_base); part->local_openclose_args = NULL; kfree(part->remote_GPs_base); part->remote_GPs = NULL; kfree(part->local_GPs_base); part->local_GPs = NULL; kfree(part->channels); part->channels = NULL; part->local_IPI_amo_va = NULL;}/* * Called by XP at the time of channel connection registration to cause * XPC to establish connections to all currently active partitions. */voidxpc_initiate_connect(int ch_number){ partid_t partid; struct xpc_partition *part; struct xpc_channel *ch; DBUG_ON(ch_number < 0 || ch_number >= XPC_NCHANNELS); for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) { part = &xpc_partitions[partid]; if (xpc_part_ref(part)) { ch = &part->channels[ch_number]; /* * Initiate the establishment of a connection on the * newly registered channel to the remote partition. */ xpc_wakeup_channel_mgr(part); xpc_part_deref(part); } }}voidxpc_connected_callout(struct xpc_channel *ch){ /* let the registerer know that a connection has been established */ if (ch->func != NULL) { dev_dbg(xpc_chan, "ch->func() called, reason=xpcConnected, " "partid=%d, channel=%d\n", ch->partid, ch->number); ch->func(xpcConnected, ch->partid, ch->number, (void *) (u64) ch->local_nentries, ch->key); dev_dbg(xpc_chan, "ch->func() returned, reason=xpcConnected, " "partid=%d, channel=%d\n", ch->partid, ch->number); }}/* * Called by XP at the time of channel connection unregistration to cause * XPC to teardown all current connections for the specified channel. * * Before returning xpc_initiate_disconnect() will wait until all connections * on the specified channel have been closed/torndown. So the caller can be * assured that they will not be receiving any more callouts from XPC to the * function they registered via xpc_connect(). * * Arguments: * * ch_number - channel # to unregister. */voidxpc_initiate_disconnect(int ch_number){ unsigned long irq_flags; partid_t partid; struct xpc_partition *part; struct xpc_channel *ch; DBUG_ON(ch_number < 0 || ch_number >= XPC_NCHANNELS); /* initiate the channel disconnect for every active partition */ for (partid = 1; partid < XP_MAX_PARTITIONS; partid++) { part = &xpc_partitions[partid]; if (xpc_part_ref(part)) { ch = &part->channels[ch_number]; xpc_msgqueue_ref(ch); spin_lock_irqsave(&ch->lock, irq_flags); if (!(ch->flags & XPC_C_DISCONNECTED)) { ch->flags |= XPC_C_WDISCONNECT; XPC_DISCONNECT_CHANNEL(ch, xpcUnregistering, &irq_flags); } spin_unlock_irqrestore(&ch->lock, irq_flags); xpc_msgqueue_deref(ch); xpc_part_deref(part); } } xpc_disconnect_wait(ch_number);}/* * To disconnect a channel, and reflect it back to all who may be waiting. * * An OPEN is not allowed until XPC_C_DISCONNECTING is cleared by * xpc_process_disconnect(), and if set, XPC_C_WDISCONNECT is cleared by * xpc_disconnect_wait(). * * THE CHANNEL IS TO BE LOCKED BY THE CALLER AND WILL REMAIN LOCKED UPON RETURN. */voidxpc_disconnect_channel(const int line, struct xpc_channel *ch, enum xpc_retval reason, unsigned long *irq_flags){ u32 channel_was_connected = (ch->flags & XPC_C_CONNECTED); DBUG_ON(!spin_is_locked(&ch->lock)); if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) { return; } DBUG_ON(!(ch->flags & (XPC_C_CONNECTING | XPC_C_CONNECTED))); dev_dbg(xpc_chan, "reason=%d, line=%d, partid=%d, channel=%d\n", reason, line, ch->partid, ch->number); XPC_SET_REASON(ch, reason, line); ch->flags |= (XPC_C_CLOSEREQUEST | XPC_C_DISCONNECTING); /* some of these may not have been set */ ch->flags &= ~(XPC_C_OPENREQUEST | XPC_C_OPENREPLY | XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY | XPC_C_CONNECTING | XPC_C_CONNECTED); xpc_IPI_send_closerequest(ch, irq_flags); if (channel_was_connected) { ch->flags |= XPC_C_WASCONNECTED; } spin_unlock_irqrestore(&ch->lock, *irq_flags); /* 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 */ if (atomic_read(&ch->n_on_msg_allocate_wq) > 0) { wake_up(&ch->msg_allocate_wq); } spin_lock_irqsave(&ch->lock, *irq_flags);}voidxpc_disconnect_callout(struct xpc_channel *ch, enum xpc_retval reason){ /* * Let the channel's registerer know that the channel is being * disconnected. We don't want to do this if the registerer was never * informed of a connection being made. */ if (ch->func != NULL) { dev_dbg(xpc_chan, "ch->func() called, reason=%d, partid=%d, " "channel=%d\n", reason, ch->partid, ch->number); ch->func(reason, ch->partid, ch->number, NULL, ch->key); dev_dbg(xpc_chan, "ch->func() returned, reason=%d, partid=%d, " "channel=%d\n", reason, ch->partid, ch->number); }}/* * Wait for a message entry to become available for the specified channel, * but don't wait any longer than 1 jiffy. */static enum xpc_retvalxpc_allocate_msg_wait(struct xpc_channel *ch){ enum xpc_retval ret; if (ch->flags & XPC_C_DISCONNECTING) { DBUG_ON(ch->reason == xpcInterrupted); // >>> Is this true? return ch->reason; } atomic_inc(&ch->n_on_msg_allocate_wq); ret = interruptible_sleep_on_timeout(&ch->msg_allocate_wq, 1); atomic_dec(&ch->n_on_msg_allocate_wq); if (ch->flags & XPC_C_DISCONNECTING) { ret = ch->reason; DBUG_ON(ch->reason == xpcInterrupted); // >>> Is this true? } else if (ret == 0) { ret = xpcTimeout; } else { ret = xpcInterrupted; } return ret;}/* * Allocate an entry for a message from the message queue associated with the * specified channel. */static enum xpc_retvalxpc_allocate_msg(struct xpc_channel *ch, u32 flags, struct xpc_msg **address_of_msg){ struct xpc_msg *msg; enum xpc_retval ret; s64 put; /* this reference will be dropped in xpc_send_msg() */ xpc_msgqueue_ref(ch); if (ch->flags & XPC_C_DISCONNECTING) { xpc_msgqueue_deref(ch); return ch->reason; } if (!(ch->flags & XPC_C_CONNECTED)) { xpc_msgqueue_deref(ch); return xpcNotConnected; } /* * Get the next available message entry from the local message queue. * If none are available, we'll make sure that we grab the latest * GP values. */ ret = xpcTimeout; while (1) { put = (volatile s64) ch->w_local_GP.put; if (put - (volatile s64) ch->w_remote_GP.get < ch->local_nentries) { /* There are available message entries. We need to try * to secure one for ourselves. We'll do this by trying * to increment w_local_GP.put as long as someone else * doesn't beat us to it. If they do, we'll have to * try again. */ if (cmpxchg(&ch->w_local_GP.put, put, put + 1) == put) { /* we got the entry referenced by put */ break; } continue; /* try again */ } /* * There aren't any available msg entries at this time. * * In waiting for a message entry to become available, * we set a timeout in case the other side is not * sending completion IPIs. This lets us fake an IPI * that will cause the IPI handler to fetch the latest * GP values as if an IPI was sent by the other side. */ if (ret == xpcTimeout) { xpc_IPI_send_local_msgrequest(ch); } if (flags & XPC_NOWAIT) { xpc_msgqueue_deref(ch); return xpcNoWait; } ret = xpc_allocate_msg_wait(ch); if (ret != xpcInterrupted && ret != xpcTimeout) { xpc_msgqueue_deref(ch); return ret; } } /* get the message's address and initialize it */ msg = (struct xpc_msg *) ((u64) ch->local_msgqueue + (put % ch->local_nentries) * ch->msg_size); DBUG_ON(msg->flags != 0); msg->number = put; dev_dbg(xpc_chan, "w_local_GP.put changed to %ld; msg=0x%p, " "msg_number=%ld, partid=%d, channel=%d\n", put + 1, (void *) msg, msg->number, ch->partid, ch->number); *address_of_msg = msg; return xpcSuccess;}/* * Allocate an entry for a message from the message queue associated with the * specified channel. NOTE that this routine can sleep waiting for a message * entry to become available. To not sleep, pass in the XPC_NOWAIT flag. * * Arguments: * * partid - ID of partition to which the channel is connected. * ch_number - channel #. * flags - see xpc.h for valid flags. * payload - address of the allocated payload area pointer (filled in on * return) in which the user-defined message is constructed. */enum xpc_retvalxpc_initiate_allocate(partid_t partid, int ch_number, u32 flags, void **payload){ struct xpc_partition *part = &xpc_partitions[partid]; enum xpc_retval ret = xpcUnknownReason; struct xpc_msg *msg = NULL; DBUG_ON(partid <= 0 || partid >= XP_MAX_PARTITIONS); DBUG_ON(ch_number < 0 || ch_number >= part->nchannels); *payload = NULL; if (xpc_part_ref(part)) { ret = xpc_allocate_msg(&part->channels[ch_number], flags, &msg); xpc_part_deref(part); if (msg != NULL) { *payload = &msg->payload; } } return ret;}/* * Now we actually send the messages that are ready to be sent by advancing * the local message queue's Put value and then send an IPI to the recipient * partition. */static voidxpc_send_msgs(struct xpc_channel *ch, s64 initial_put){ struct xpc_msg *msg; s64 put = initial_put + 1; int send_IPI = 0; while (1) { while (1) { if (put == (volatile s64) ch->w_local_GP.put) { break; } msg = (struct xpc_msg *) ((u64) ch->local_msgqueue + (put % ch->local_nentries) * ch->msg_size); if (!(msg->flags & XPC_M_READY)) { break; } put++; } if (put == initial_put) { /* nothing's changed */ break; } if (cmpxchg_rel(&ch->local_GP->put, initial_put, put) != initial_put) { /* someone else beat us to it */ DBUG_ON((volatile s64) ch->local_GP->put < initial_put); break; } /* we just set the new value of local_GP->put */ dev_dbg(xpc_chan, "local_GP->put changed to %ld, partid=%d, "
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -