⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 xpc_channel.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
		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 proceding 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);	}	/* 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_disconnecting_callout(struct xpc_channel *ch){	/*	 * 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=xpcDisconnecting,"			" partid=%d, channel=%d\n", ch->partid, ch->number);		ch->func(xpcDisconnecting, ch->partid, ch->number, NULL,								ch->key);		dev_dbg(xpc_chan, "ch->func() returned, reason="			"xpcDisconnecting, partid=%d, channel=%d\n",			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;	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, "			"channel=%d\n", put, ch->partid, ch->number);		send_IPI = 1;		/*

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -