18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
38c2ecf20Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
48c2ecf20Sopenharmony_ci * for more details.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Copyright (c) 2004-2009 Silicon Graphics, Inc.  All Rights Reserved.
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci/*
108c2ecf20Sopenharmony_ci * Cross Partition Communication (XPC) channel support.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci *	This is the part of XPC that manages the channels and
138c2ecf20Sopenharmony_ci *	sends/receives messages across them to/from other partitions.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <linux/device.h>
188c2ecf20Sopenharmony_ci#include "xpc.h"
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci/*
218c2ecf20Sopenharmony_ci * Process a connect message from a remote partition.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * Note: xpc_process_connect() is expecting to be called with the
248c2ecf20Sopenharmony_ci * spin_lock_irqsave held and will leave it locked upon return.
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_cistatic void
278c2ecf20Sopenharmony_cixpc_process_connect(struct xpc_channel *ch, unsigned long *irq_flags)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	enum xp_retval ret;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	lockdep_assert_held(&ch->lock);
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	if (!(ch->flags & XPC_C_OPENREQUEST) ||
348c2ecf20Sopenharmony_ci	    !(ch->flags & XPC_C_ROPENREQUEST)) {
358c2ecf20Sopenharmony_ci		/* nothing more to do for now */
368c2ecf20Sopenharmony_ci		return;
378c2ecf20Sopenharmony_ci	}
388c2ecf20Sopenharmony_ci	DBUG_ON(!(ch->flags & XPC_C_CONNECTING));
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	if (!(ch->flags & XPC_C_SETUP)) {
418c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ch->lock, *irq_flags);
428c2ecf20Sopenharmony_ci		ret = xpc_arch_ops.setup_msg_structures(ch);
438c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ch->lock, *irq_flags);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci		if (ret != xpSuccess)
468c2ecf20Sopenharmony_ci			XPC_DISCONNECT_CHANNEL(ch, ret, irq_flags);
478c2ecf20Sopenharmony_ci		else
488c2ecf20Sopenharmony_ci			ch->flags |= XPC_C_SETUP;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci		if (ch->flags & XPC_C_DISCONNECTING)
518c2ecf20Sopenharmony_ci			return;
528c2ecf20Sopenharmony_ci	}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	if (!(ch->flags & XPC_C_OPENREPLY)) {
558c2ecf20Sopenharmony_ci		ch->flags |= XPC_C_OPENREPLY;
568c2ecf20Sopenharmony_ci		xpc_arch_ops.send_chctl_openreply(ch, irq_flags);
578c2ecf20Sopenharmony_ci	}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	if (!(ch->flags & XPC_C_ROPENREPLY))
608c2ecf20Sopenharmony_ci		return;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	if (!(ch->flags & XPC_C_OPENCOMPLETE)) {
638c2ecf20Sopenharmony_ci		ch->flags |= (XPC_C_OPENCOMPLETE | XPC_C_CONNECTED);
648c2ecf20Sopenharmony_ci		xpc_arch_ops.send_chctl_opencomplete(ch, irq_flags);
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if (!(ch->flags & XPC_C_ROPENCOMPLETE))
688c2ecf20Sopenharmony_ci		return;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	dev_info(xpc_chan, "channel %d to partition %d connected\n",
718c2ecf20Sopenharmony_ci		 ch->number, ch->partid);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	ch->flags = (XPC_C_CONNECTED | XPC_C_SETUP);	/* clear all else */
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/*
778c2ecf20Sopenharmony_ci * spin_lock_irqsave() is expected to be held on entry.
788c2ecf20Sopenharmony_ci */
798c2ecf20Sopenharmony_cistatic void
808c2ecf20Sopenharmony_cixpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct xpc_partition *part = &xpc_partitions[ch->partid];
838c2ecf20Sopenharmony_ci	u32 channel_was_connected = (ch->flags & XPC_C_WASCONNECTED);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	lockdep_assert_held(&ch->lock);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (!(ch->flags & XPC_C_DISCONNECTING))
888c2ecf20Sopenharmony_ci		return;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/* make sure all activity has settled down first */
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if (atomic_read(&ch->kthreads_assigned) > 0 ||
958c2ecf20Sopenharmony_ci	    atomic_read(&ch->references) > 0) {
968c2ecf20Sopenharmony_ci		return;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci	DBUG_ON((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) &&
998c2ecf20Sopenharmony_ci		!(ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE));
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	if (part->act_state == XPC_P_AS_DEACTIVATING) {
1028c2ecf20Sopenharmony_ci		/* can't proceed until the other side disengages from us */
1038c2ecf20Sopenharmony_ci		if (xpc_arch_ops.partition_engaged(ch->partid))
1048c2ecf20Sopenharmony_ci			return;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	} else {
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci		/* as long as the other side is up do the full protocol */
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci		if (!(ch->flags & XPC_C_RCLOSEREQUEST))
1118c2ecf20Sopenharmony_ci			return;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci		if (!(ch->flags & XPC_C_CLOSEREPLY)) {
1148c2ecf20Sopenharmony_ci			ch->flags |= XPC_C_CLOSEREPLY;
1158c2ecf20Sopenharmony_ci			xpc_arch_ops.send_chctl_closereply(ch, irq_flags);
1168c2ecf20Sopenharmony_ci		}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci		if (!(ch->flags & XPC_C_RCLOSEREPLY))
1198c2ecf20Sopenharmony_ci			return;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	/* wake those waiting for notify completion */
1238c2ecf20Sopenharmony_ci	if (atomic_read(&ch->n_to_notify) > 0) {
1248c2ecf20Sopenharmony_ci		/* we do callout while holding ch->lock, callout can't block */
1258c2ecf20Sopenharmony_ci		xpc_arch_ops.notify_senders_of_disconnect(ch);
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	/* both sides are disconnected now */
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	if (ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE) {
1318c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ch->lock, *irq_flags);
1328c2ecf20Sopenharmony_ci		xpc_disconnect_callout(ch, xpDisconnected);
1338c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ch->lock, *irq_flags);
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	DBUG_ON(atomic_read(&ch->n_to_notify) != 0);
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	/* it's now safe to free the channel's message queues */
1398c2ecf20Sopenharmony_ci	xpc_arch_ops.teardown_msg_structures(ch);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	ch->func = NULL;
1428c2ecf20Sopenharmony_ci	ch->key = NULL;
1438c2ecf20Sopenharmony_ci	ch->entry_size = 0;
1448c2ecf20Sopenharmony_ci	ch->local_nentries = 0;
1458c2ecf20Sopenharmony_ci	ch->remote_nentries = 0;
1468c2ecf20Sopenharmony_ci	ch->kthreads_assigned_limit = 0;
1478c2ecf20Sopenharmony_ci	ch->kthreads_idle_limit = 0;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	/*
1508c2ecf20Sopenharmony_ci	 * Mark the channel disconnected and clear all other flags, including
1518c2ecf20Sopenharmony_ci	 * XPC_C_SETUP (because of call to
1528c2ecf20Sopenharmony_ci	 * xpc_arch_ops.teardown_msg_structures()) but not including
1538c2ecf20Sopenharmony_ci	 * XPC_C_WDISCONNECT (if it was set).
1548c2ecf20Sopenharmony_ci	 */
1558c2ecf20Sopenharmony_ci	ch->flags = (XPC_C_DISCONNECTED | (ch->flags & XPC_C_WDISCONNECT));
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	atomic_dec(&part->nchannels_active);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (channel_was_connected) {
1608c2ecf20Sopenharmony_ci		dev_info(xpc_chan, "channel %d to partition %d disconnected, "
1618c2ecf20Sopenharmony_ci			 "reason=%d\n", ch->number, ch->partid, ch->reason);
1628c2ecf20Sopenharmony_ci	}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	if (ch->flags & XPC_C_WDISCONNECT) {
1658c2ecf20Sopenharmony_ci		/* we won't lose the CPU since we're holding ch->lock */
1668c2ecf20Sopenharmony_ci		complete(&ch->wdisconnect_wait);
1678c2ecf20Sopenharmony_ci	} else if (ch->delayed_chctl_flags) {
1688c2ecf20Sopenharmony_ci		if (part->act_state != XPC_P_AS_DEACTIVATING) {
1698c2ecf20Sopenharmony_ci			/* time to take action on any delayed chctl flags */
1708c2ecf20Sopenharmony_ci			spin_lock(&part->chctl_lock);
1718c2ecf20Sopenharmony_ci			part->chctl.flags[ch->number] |=
1728c2ecf20Sopenharmony_ci			    ch->delayed_chctl_flags;
1738c2ecf20Sopenharmony_ci			spin_unlock(&part->chctl_lock);
1748c2ecf20Sopenharmony_ci		}
1758c2ecf20Sopenharmony_ci		ch->delayed_chctl_flags = 0;
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci/*
1808c2ecf20Sopenharmony_ci * Process a change in the channel's remote connection state.
1818c2ecf20Sopenharmony_ci */
1828c2ecf20Sopenharmony_cistatic void
1838c2ecf20Sopenharmony_cixpc_process_openclose_chctl_flags(struct xpc_partition *part, int ch_number,
1848c2ecf20Sopenharmony_ci				  u8 chctl_flags)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	unsigned long irq_flags;
1878c2ecf20Sopenharmony_ci	struct xpc_openclose_args *args =
1888c2ecf20Sopenharmony_ci	    &part->remote_openclose_args[ch_number];
1898c2ecf20Sopenharmony_ci	struct xpc_channel *ch = &part->channels[ch_number];
1908c2ecf20Sopenharmony_ci	enum xp_retval reason;
1918c2ecf20Sopenharmony_ci	enum xp_retval ret;
1928c2ecf20Sopenharmony_ci	int create_kthread = 0;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ch->lock, irq_flags);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ciagain:
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if ((ch->flags & XPC_C_DISCONNECTED) &&
1998c2ecf20Sopenharmony_ci	    (ch->flags & XPC_C_WDISCONNECT)) {
2008c2ecf20Sopenharmony_ci		/*
2018c2ecf20Sopenharmony_ci		 * Delay processing chctl flags until thread waiting disconnect
2028c2ecf20Sopenharmony_ci		 * has had a chance to see that the channel is disconnected.
2038c2ecf20Sopenharmony_ci		 */
2048c2ecf20Sopenharmony_ci		ch->delayed_chctl_flags |= chctl_flags;
2058c2ecf20Sopenharmony_ci		goto out;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	if (chctl_flags & XPC_CHCTL_CLOSEREQUEST) {
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci		dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREQUEST (reason=%d) received "
2118c2ecf20Sopenharmony_ci			"from partid=%d, channel=%d\n", args->reason,
2128c2ecf20Sopenharmony_ci			ch->partid, ch->number);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci		/*
2158c2ecf20Sopenharmony_ci		 * If RCLOSEREQUEST is set, we're probably waiting for
2168c2ecf20Sopenharmony_ci		 * RCLOSEREPLY. We should find it and a ROPENREQUEST packed
2178c2ecf20Sopenharmony_ci		 * with this RCLOSEREQUEST in the chctl_flags.
2188c2ecf20Sopenharmony_ci		 */
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		if (ch->flags & XPC_C_RCLOSEREQUEST) {
2218c2ecf20Sopenharmony_ci			DBUG_ON(!(ch->flags & XPC_C_DISCONNECTING));
2228c2ecf20Sopenharmony_ci			DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));
2238c2ecf20Sopenharmony_ci			DBUG_ON(!(ch->flags & XPC_C_CLOSEREPLY));
2248c2ecf20Sopenharmony_ci			DBUG_ON(ch->flags & XPC_C_RCLOSEREPLY);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci			DBUG_ON(!(chctl_flags & XPC_CHCTL_CLOSEREPLY));
2278c2ecf20Sopenharmony_ci			chctl_flags &= ~XPC_CHCTL_CLOSEREPLY;
2288c2ecf20Sopenharmony_ci			ch->flags |= XPC_C_RCLOSEREPLY;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci			/* both sides have finished disconnecting */
2318c2ecf20Sopenharmony_ci			xpc_process_disconnect(ch, &irq_flags);
2328c2ecf20Sopenharmony_ci			DBUG_ON(!(ch->flags & XPC_C_DISCONNECTED));
2338c2ecf20Sopenharmony_ci			goto again;
2348c2ecf20Sopenharmony_ci		}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci		if (ch->flags & XPC_C_DISCONNECTED) {
2378c2ecf20Sopenharmony_ci			if (!(chctl_flags & XPC_CHCTL_OPENREQUEST)) {
2388c2ecf20Sopenharmony_ci				if (part->chctl.flags[ch_number] &
2398c2ecf20Sopenharmony_ci				    XPC_CHCTL_OPENREQUEST) {
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci					DBUG_ON(ch->delayed_chctl_flags != 0);
2428c2ecf20Sopenharmony_ci					spin_lock(&part->chctl_lock);
2438c2ecf20Sopenharmony_ci					part->chctl.flags[ch_number] |=
2448c2ecf20Sopenharmony_ci					    XPC_CHCTL_CLOSEREQUEST;
2458c2ecf20Sopenharmony_ci					spin_unlock(&part->chctl_lock);
2468c2ecf20Sopenharmony_ci				}
2478c2ecf20Sopenharmony_ci				goto out;
2488c2ecf20Sopenharmony_ci			}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci			XPC_SET_REASON(ch, 0, 0);
2518c2ecf20Sopenharmony_ci			ch->flags &= ~XPC_C_DISCONNECTED;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci			atomic_inc(&part->nchannels_active);
2548c2ecf20Sopenharmony_ci			ch->flags |= (XPC_C_CONNECTING | XPC_C_ROPENREQUEST);
2558c2ecf20Sopenharmony_ci		}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci		chctl_flags &= ~(XPC_CHCTL_OPENREQUEST | XPC_CHCTL_OPENREPLY |
2588c2ecf20Sopenharmony_ci		    XPC_CHCTL_OPENCOMPLETE);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		/*
2618c2ecf20Sopenharmony_ci		 * The meaningful CLOSEREQUEST connection state fields are:
2628c2ecf20Sopenharmony_ci		 *      reason = reason connection is to be closed
2638c2ecf20Sopenharmony_ci		 */
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci		ch->flags |= XPC_C_RCLOSEREQUEST;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci		if (!(ch->flags & XPC_C_DISCONNECTING)) {
2688c2ecf20Sopenharmony_ci			reason = args->reason;
2698c2ecf20Sopenharmony_ci			if (reason <= xpSuccess || reason > xpUnknownReason)
2708c2ecf20Sopenharmony_ci				reason = xpUnknownReason;
2718c2ecf20Sopenharmony_ci			else if (reason == xpUnregistering)
2728c2ecf20Sopenharmony_ci				reason = xpOtherUnregistering;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci			XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci			DBUG_ON(chctl_flags & XPC_CHCTL_CLOSEREPLY);
2778c2ecf20Sopenharmony_ci			goto out;
2788c2ecf20Sopenharmony_ci		}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		xpc_process_disconnect(ch, &irq_flags);
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	if (chctl_flags & XPC_CHCTL_CLOSEREPLY) {
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci		dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREPLY received from partid="
2868c2ecf20Sopenharmony_ci			"%d, channel=%d\n", ch->partid, ch->number);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci		if (ch->flags & XPC_C_DISCONNECTED) {
2898c2ecf20Sopenharmony_ci			DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING);
2908c2ecf20Sopenharmony_ci			goto out;
2918c2ecf20Sopenharmony_ci		}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci		DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci		if (!(ch->flags & XPC_C_RCLOSEREQUEST)) {
2968c2ecf20Sopenharmony_ci			if (part->chctl.flags[ch_number] &
2978c2ecf20Sopenharmony_ci			    XPC_CHCTL_CLOSEREQUEST) {
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci				DBUG_ON(ch->delayed_chctl_flags != 0);
3008c2ecf20Sopenharmony_ci				spin_lock(&part->chctl_lock);
3018c2ecf20Sopenharmony_ci				part->chctl.flags[ch_number] |=
3028c2ecf20Sopenharmony_ci				    XPC_CHCTL_CLOSEREPLY;
3038c2ecf20Sopenharmony_ci				spin_unlock(&part->chctl_lock);
3048c2ecf20Sopenharmony_ci			}
3058c2ecf20Sopenharmony_ci			goto out;
3068c2ecf20Sopenharmony_ci		}
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci		ch->flags |= XPC_C_RCLOSEREPLY;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci		if (ch->flags & XPC_C_CLOSEREPLY) {
3118c2ecf20Sopenharmony_ci			/* both sides have finished disconnecting */
3128c2ecf20Sopenharmony_ci			xpc_process_disconnect(ch, &irq_flags);
3138c2ecf20Sopenharmony_ci		}
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (chctl_flags & XPC_CHCTL_OPENREQUEST) {
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci		dev_dbg(xpc_chan, "XPC_CHCTL_OPENREQUEST (entry_size=%d, "
3198c2ecf20Sopenharmony_ci			"local_nentries=%d) received from partid=%d, "
3208c2ecf20Sopenharmony_ci			"channel=%d\n", args->entry_size, args->local_nentries,
3218c2ecf20Sopenharmony_ci			ch->partid, ch->number);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci		if (part->act_state == XPC_P_AS_DEACTIVATING ||
3248c2ecf20Sopenharmony_ci		    (ch->flags & XPC_C_ROPENREQUEST)) {
3258c2ecf20Sopenharmony_ci			goto out;
3268c2ecf20Sopenharmony_ci		}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci		if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_WDISCONNECT)) {
3298c2ecf20Sopenharmony_ci			ch->delayed_chctl_flags |= XPC_CHCTL_OPENREQUEST;
3308c2ecf20Sopenharmony_ci			goto out;
3318c2ecf20Sopenharmony_ci		}
3328c2ecf20Sopenharmony_ci		DBUG_ON(!(ch->flags & (XPC_C_DISCONNECTED |
3338c2ecf20Sopenharmony_ci				       XPC_C_OPENREQUEST)));
3348c2ecf20Sopenharmony_ci		DBUG_ON(ch->flags & (XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY |
3358c2ecf20Sopenharmony_ci				     XPC_C_OPENREPLY | XPC_C_CONNECTED));
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci		/*
3388c2ecf20Sopenharmony_ci		 * The meaningful OPENREQUEST connection state fields are:
3398c2ecf20Sopenharmony_ci		 *      entry_size = size of channel's messages in bytes
3408c2ecf20Sopenharmony_ci		 *      local_nentries = remote partition's local_nentries
3418c2ecf20Sopenharmony_ci		 */
3428c2ecf20Sopenharmony_ci		if (args->entry_size == 0 || args->local_nentries == 0) {
3438c2ecf20Sopenharmony_ci			/* assume OPENREQUEST was delayed by mistake */
3448c2ecf20Sopenharmony_ci			goto out;
3458c2ecf20Sopenharmony_ci		}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci		ch->flags |= (XPC_C_ROPENREQUEST | XPC_C_CONNECTING);
3488c2ecf20Sopenharmony_ci		ch->remote_nentries = args->local_nentries;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci		if (ch->flags & XPC_C_OPENREQUEST) {
3518c2ecf20Sopenharmony_ci			if (args->entry_size != ch->entry_size) {
3528c2ecf20Sopenharmony_ci				XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes,
3538c2ecf20Sopenharmony_ci						       &irq_flags);
3548c2ecf20Sopenharmony_ci				goto out;
3558c2ecf20Sopenharmony_ci			}
3568c2ecf20Sopenharmony_ci		} else {
3578c2ecf20Sopenharmony_ci			ch->entry_size = args->entry_size;
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci			XPC_SET_REASON(ch, 0, 0);
3608c2ecf20Sopenharmony_ci			ch->flags &= ~XPC_C_DISCONNECTED;
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci			atomic_inc(&part->nchannels_active);
3638c2ecf20Sopenharmony_ci		}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		xpc_process_connect(ch, &irq_flags);
3668c2ecf20Sopenharmony_ci	}
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	if (chctl_flags & XPC_CHCTL_OPENREPLY) {
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci		dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY (local_msgqueue_pa="
3718c2ecf20Sopenharmony_ci			"0x%lx, local_nentries=%d, remote_nentries=%d) "
3728c2ecf20Sopenharmony_ci			"received from partid=%d, channel=%d\n",
3738c2ecf20Sopenharmony_ci			args->local_msgqueue_pa, args->local_nentries,
3748c2ecf20Sopenharmony_ci			args->remote_nentries, ch->partid, ch->number);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci		if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED))
3778c2ecf20Sopenharmony_ci			goto out;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci		if (!(ch->flags & XPC_C_OPENREQUEST)) {
3808c2ecf20Sopenharmony_ci			XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError,
3818c2ecf20Sopenharmony_ci					       &irq_flags);
3828c2ecf20Sopenharmony_ci			goto out;
3838c2ecf20Sopenharmony_ci		}
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci		DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST));
3868c2ecf20Sopenharmony_ci		DBUG_ON(ch->flags & XPC_C_CONNECTED);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci		/*
3898c2ecf20Sopenharmony_ci		 * The meaningful OPENREPLY connection state fields are:
3908c2ecf20Sopenharmony_ci		 *      local_msgqueue_pa = physical address of remote
3918c2ecf20Sopenharmony_ci		 *                          partition's local_msgqueue
3928c2ecf20Sopenharmony_ci		 *      local_nentries = remote partition's local_nentries
3938c2ecf20Sopenharmony_ci		 *      remote_nentries = remote partition's remote_nentries
3948c2ecf20Sopenharmony_ci		 */
3958c2ecf20Sopenharmony_ci		DBUG_ON(args->local_msgqueue_pa == 0);
3968c2ecf20Sopenharmony_ci		DBUG_ON(args->local_nentries == 0);
3978c2ecf20Sopenharmony_ci		DBUG_ON(args->remote_nentries == 0);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci		ret = xpc_arch_ops.save_remote_msgqueue_pa(ch,
4008c2ecf20Sopenharmony_ci						      args->local_msgqueue_pa);
4018c2ecf20Sopenharmony_ci		if (ret != xpSuccess) {
4028c2ecf20Sopenharmony_ci			XPC_DISCONNECT_CHANNEL(ch, ret, &irq_flags);
4038c2ecf20Sopenharmony_ci			goto out;
4048c2ecf20Sopenharmony_ci		}
4058c2ecf20Sopenharmony_ci		ch->flags |= XPC_C_ROPENREPLY;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci		if (args->local_nentries < ch->remote_nentries) {
4088c2ecf20Sopenharmony_ci			dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new "
4098c2ecf20Sopenharmony_ci				"remote_nentries=%d, old remote_nentries=%d, "
4108c2ecf20Sopenharmony_ci				"partid=%d, channel=%d\n",
4118c2ecf20Sopenharmony_ci				args->local_nentries, ch->remote_nentries,
4128c2ecf20Sopenharmony_ci				ch->partid, ch->number);
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci			ch->remote_nentries = args->local_nentries;
4158c2ecf20Sopenharmony_ci		}
4168c2ecf20Sopenharmony_ci		if (args->remote_nentries < ch->local_nentries) {
4178c2ecf20Sopenharmony_ci			dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new "
4188c2ecf20Sopenharmony_ci				"local_nentries=%d, old local_nentries=%d, "
4198c2ecf20Sopenharmony_ci				"partid=%d, channel=%d\n",
4208c2ecf20Sopenharmony_ci				args->remote_nentries, ch->local_nentries,
4218c2ecf20Sopenharmony_ci				ch->partid, ch->number);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci			ch->local_nentries = args->remote_nentries;
4248c2ecf20Sopenharmony_ci		}
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci		xpc_process_connect(ch, &irq_flags);
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	if (chctl_flags & XPC_CHCTL_OPENCOMPLETE) {
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci		dev_dbg(xpc_chan, "XPC_CHCTL_OPENCOMPLETE received from "
4328c2ecf20Sopenharmony_ci			"partid=%d, channel=%d\n", ch->partid, ch->number);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci		if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED))
4358c2ecf20Sopenharmony_ci			goto out;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci		if (!(ch->flags & XPC_C_OPENREQUEST) ||
4388c2ecf20Sopenharmony_ci		    !(ch->flags & XPC_C_OPENREPLY)) {
4398c2ecf20Sopenharmony_ci			XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError,
4408c2ecf20Sopenharmony_ci					       &irq_flags);
4418c2ecf20Sopenharmony_ci			goto out;
4428c2ecf20Sopenharmony_ci		}
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci		DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST));
4458c2ecf20Sopenharmony_ci		DBUG_ON(!(ch->flags & XPC_C_ROPENREPLY));
4468c2ecf20Sopenharmony_ci		DBUG_ON(!(ch->flags & XPC_C_CONNECTED));
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci		ch->flags |= XPC_C_ROPENCOMPLETE;
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci		xpc_process_connect(ch, &irq_flags);
4518c2ecf20Sopenharmony_ci		create_kthread = 1;
4528c2ecf20Sopenharmony_ci	}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ciout:
4558c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ch->lock, irq_flags);
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	if (create_kthread)
4588c2ecf20Sopenharmony_ci		xpc_create_kthreads(ch, 1, 0);
4598c2ecf20Sopenharmony_ci}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci/*
4628c2ecf20Sopenharmony_ci * Attempt to establish a channel connection to a remote partition.
4638c2ecf20Sopenharmony_ci */
4648c2ecf20Sopenharmony_cistatic enum xp_retval
4658c2ecf20Sopenharmony_cixpc_connect_channel(struct xpc_channel *ch)
4668c2ecf20Sopenharmony_ci{
4678c2ecf20Sopenharmony_ci	unsigned long irq_flags;
4688c2ecf20Sopenharmony_ci	struct xpc_registration *registration = &xpc_registrations[ch->number];
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	if (mutex_trylock(&registration->mutex) == 0)
4718c2ecf20Sopenharmony_ci		return xpRetry;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci	if (!XPC_CHANNEL_REGISTERED(ch->number)) {
4748c2ecf20Sopenharmony_ci		mutex_unlock(&registration->mutex);
4758c2ecf20Sopenharmony_ci		return xpUnregistered;
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ch->lock, irq_flags);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	DBUG_ON(ch->flags & XPC_C_CONNECTED);
4818c2ecf20Sopenharmony_ci	DBUG_ON(ch->flags & XPC_C_OPENREQUEST);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci	if (ch->flags & XPC_C_DISCONNECTING) {
4848c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ch->lock, irq_flags);
4858c2ecf20Sopenharmony_ci		mutex_unlock(&registration->mutex);
4868c2ecf20Sopenharmony_ci		return ch->reason;
4878c2ecf20Sopenharmony_ci	}
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	/* add info from the channel connect registration to the channel */
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	ch->kthreads_assigned_limit = registration->assigned_limit;
4928c2ecf20Sopenharmony_ci	ch->kthreads_idle_limit = registration->idle_limit;
4938c2ecf20Sopenharmony_ci	DBUG_ON(atomic_read(&ch->kthreads_assigned) != 0);
4948c2ecf20Sopenharmony_ci	DBUG_ON(atomic_read(&ch->kthreads_idle) != 0);
4958c2ecf20Sopenharmony_ci	DBUG_ON(atomic_read(&ch->kthreads_active) != 0);
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	ch->func = registration->func;
4988c2ecf20Sopenharmony_ci	DBUG_ON(registration->func == NULL);
4998c2ecf20Sopenharmony_ci	ch->key = registration->key;
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	ch->local_nentries = registration->nentries;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_ci	if (ch->flags & XPC_C_ROPENREQUEST) {
5048c2ecf20Sopenharmony_ci		if (registration->entry_size != ch->entry_size) {
5058c2ecf20Sopenharmony_ci			/* the local and remote sides aren't the same */
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci			/*
5088c2ecf20Sopenharmony_ci			 * Because XPC_DISCONNECT_CHANNEL() can block we're
5098c2ecf20Sopenharmony_ci			 * forced to up the registration sema before we unlock
5108c2ecf20Sopenharmony_ci			 * the channel lock. But that's okay here because we're
5118c2ecf20Sopenharmony_ci			 * done with the part that required the registration
5128c2ecf20Sopenharmony_ci			 * sema. XPC_DISCONNECT_CHANNEL() requires that the
5138c2ecf20Sopenharmony_ci			 * channel lock be locked and will unlock and relock
5148c2ecf20Sopenharmony_ci			 * the channel lock as needed.
5158c2ecf20Sopenharmony_ci			 */
5168c2ecf20Sopenharmony_ci			mutex_unlock(&registration->mutex);
5178c2ecf20Sopenharmony_ci			XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes,
5188c2ecf20Sopenharmony_ci					       &irq_flags);
5198c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&ch->lock, irq_flags);
5208c2ecf20Sopenharmony_ci			return xpUnequalMsgSizes;
5218c2ecf20Sopenharmony_ci		}
5228c2ecf20Sopenharmony_ci	} else {
5238c2ecf20Sopenharmony_ci		ch->entry_size = registration->entry_size;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci		XPC_SET_REASON(ch, 0, 0);
5268c2ecf20Sopenharmony_ci		ch->flags &= ~XPC_C_DISCONNECTED;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci		atomic_inc(&xpc_partitions[ch->partid].nchannels_active);
5298c2ecf20Sopenharmony_ci	}
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci	mutex_unlock(&registration->mutex);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci	/* initiate the connection */
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	ch->flags |= (XPC_C_OPENREQUEST | XPC_C_CONNECTING);
5368c2ecf20Sopenharmony_ci	xpc_arch_ops.send_chctl_openrequest(ch, &irq_flags);
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	xpc_process_connect(ch, &irq_flags);
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ch->lock, irq_flags);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	return xpSuccess;
5438c2ecf20Sopenharmony_ci}
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_civoid
5468c2ecf20Sopenharmony_cixpc_process_sent_chctl_flags(struct xpc_partition *part)
5478c2ecf20Sopenharmony_ci{
5488c2ecf20Sopenharmony_ci	unsigned long irq_flags;
5498c2ecf20Sopenharmony_ci	union xpc_channel_ctl_flags chctl;
5508c2ecf20Sopenharmony_ci	struct xpc_channel *ch;
5518c2ecf20Sopenharmony_ci	int ch_number;
5528c2ecf20Sopenharmony_ci	u32 ch_flags;
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	chctl.all_flags = xpc_arch_ops.get_chctl_all_flags(part);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	/*
5578c2ecf20Sopenharmony_ci	 * Initiate channel connections for registered channels.
5588c2ecf20Sopenharmony_ci	 *
5598c2ecf20Sopenharmony_ci	 * For each connected channel that has pending messages activate idle
5608c2ecf20Sopenharmony_ci	 * kthreads and/or create new kthreads as needed.
5618c2ecf20Sopenharmony_ci	 */
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	for (ch_number = 0; ch_number < part->nchannels; ch_number++) {
5648c2ecf20Sopenharmony_ci		ch = &part->channels[ch_number];
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci		/*
5678c2ecf20Sopenharmony_ci		 * Process any open or close related chctl flags, and then deal
5688c2ecf20Sopenharmony_ci		 * with connecting or disconnecting the channel as required.
5698c2ecf20Sopenharmony_ci		 */
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci		if (chctl.flags[ch_number] & XPC_OPENCLOSE_CHCTL_FLAGS) {
5728c2ecf20Sopenharmony_ci			xpc_process_openclose_chctl_flags(part, ch_number,
5738c2ecf20Sopenharmony_ci							chctl.flags[ch_number]);
5748c2ecf20Sopenharmony_ci		}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci		ch_flags = ch->flags;	/* need an atomic snapshot of flags */
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci		if (ch_flags & XPC_C_DISCONNECTING) {
5798c2ecf20Sopenharmony_ci			spin_lock_irqsave(&ch->lock, irq_flags);
5808c2ecf20Sopenharmony_ci			xpc_process_disconnect(ch, &irq_flags);
5818c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&ch->lock, irq_flags);
5828c2ecf20Sopenharmony_ci			continue;
5838c2ecf20Sopenharmony_ci		}
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci		if (part->act_state == XPC_P_AS_DEACTIVATING)
5868c2ecf20Sopenharmony_ci			continue;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci		if (!(ch_flags & XPC_C_CONNECTED)) {
5898c2ecf20Sopenharmony_ci			if (!(ch_flags & XPC_C_OPENREQUEST)) {
5908c2ecf20Sopenharmony_ci				DBUG_ON(ch_flags & XPC_C_SETUP);
5918c2ecf20Sopenharmony_ci				(void)xpc_connect_channel(ch);
5928c2ecf20Sopenharmony_ci			}
5938c2ecf20Sopenharmony_ci			continue;
5948c2ecf20Sopenharmony_ci		}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci		/*
5978c2ecf20Sopenharmony_ci		 * Process any message related chctl flags, this may involve
5988c2ecf20Sopenharmony_ci		 * the activation of kthreads to deliver any pending messages
5998c2ecf20Sopenharmony_ci		 * sent from the other partition.
6008c2ecf20Sopenharmony_ci		 */
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci		if (chctl.flags[ch_number] & XPC_MSG_CHCTL_FLAGS)
6038c2ecf20Sopenharmony_ci			xpc_arch_ops.process_msg_chctl_flags(part, ch_number);
6048c2ecf20Sopenharmony_ci	}
6058c2ecf20Sopenharmony_ci}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci/*
6088c2ecf20Sopenharmony_ci * XPC's heartbeat code calls this function to inform XPC that a partition is
6098c2ecf20Sopenharmony_ci * going down.  XPC responds by tearing down the XPartition Communication
6108c2ecf20Sopenharmony_ci * infrastructure used for the just downed partition.
6118c2ecf20Sopenharmony_ci *
6128c2ecf20Sopenharmony_ci * XPC's heartbeat code will never call this function and xpc_partition_up()
6138c2ecf20Sopenharmony_ci * at the same time. Nor will it ever make multiple calls to either function
6148c2ecf20Sopenharmony_ci * at the same time.
6158c2ecf20Sopenharmony_ci */
6168c2ecf20Sopenharmony_civoid
6178c2ecf20Sopenharmony_cixpc_partition_going_down(struct xpc_partition *part, enum xp_retval reason)
6188c2ecf20Sopenharmony_ci{
6198c2ecf20Sopenharmony_ci	unsigned long irq_flags;
6208c2ecf20Sopenharmony_ci	int ch_number;
6218c2ecf20Sopenharmony_ci	struct xpc_channel *ch;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	dev_dbg(xpc_chan, "deactivating partition %d, reason=%d\n",
6248c2ecf20Sopenharmony_ci		XPC_PARTID(part), reason);
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	if (!xpc_part_ref(part)) {
6278c2ecf20Sopenharmony_ci		/* infrastructure for this partition isn't currently set up */
6288c2ecf20Sopenharmony_ci		return;
6298c2ecf20Sopenharmony_ci	}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci	/* disconnect channels associated with the partition going down */
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	for (ch_number = 0; ch_number < part->nchannels; ch_number++) {
6348c2ecf20Sopenharmony_ci		ch = &part->channels[ch_number];
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci		xpc_msgqueue_ref(ch);
6378c2ecf20Sopenharmony_ci		spin_lock_irqsave(&ch->lock, irq_flags);
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci		XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags);
6408c2ecf20Sopenharmony_ci
6418c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&ch->lock, irq_flags);
6428c2ecf20Sopenharmony_ci		xpc_msgqueue_deref(ch);
6438c2ecf20Sopenharmony_ci	}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	xpc_wakeup_channel_mgr(part);
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	xpc_part_deref(part);
6488c2ecf20Sopenharmony_ci}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci/*
6518c2ecf20Sopenharmony_ci * Called by XP at the time of channel connection registration to cause
6528c2ecf20Sopenharmony_ci * XPC to establish connections to all currently active partitions.
6538c2ecf20Sopenharmony_ci */
6548c2ecf20Sopenharmony_civoid
6558c2ecf20Sopenharmony_cixpc_initiate_connect(int ch_number)
6568c2ecf20Sopenharmony_ci{
6578c2ecf20Sopenharmony_ci	short partid;
6588c2ecf20Sopenharmony_ci	struct xpc_partition *part;
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci	DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS);
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	for (partid = 0; partid < xp_max_npartitions; partid++) {
6638c2ecf20Sopenharmony_ci		part = &xpc_partitions[partid];
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci		if (xpc_part_ref(part)) {
6668c2ecf20Sopenharmony_ci			/*
6678c2ecf20Sopenharmony_ci			 * Initiate the establishment of a connection on the
6688c2ecf20Sopenharmony_ci			 * newly registered channel to the remote partition.
6698c2ecf20Sopenharmony_ci			 */
6708c2ecf20Sopenharmony_ci			xpc_wakeup_channel_mgr(part);
6718c2ecf20Sopenharmony_ci			xpc_part_deref(part);
6728c2ecf20Sopenharmony_ci		}
6738c2ecf20Sopenharmony_ci	}
6748c2ecf20Sopenharmony_ci}
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_civoid
6778c2ecf20Sopenharmony_cixpc_connected_callout(struct xpc_channel *ch)
6788c2ecf20Sopenharmony_ci{
6798c2ecf20Sopenharmony_ci	/* let the registerer know that a connection has been established */
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	if (ch->func != NULL) {
6828c2ecf20Sopenharmony_ci		dev_dbg(xpc_chan, "ch->func() called, reason=xpConnected, "
6838c2ecf20Sopenharmony_ci			"partid=%d, channel=%d\n", ch->partid, ch->number);
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci		ch->func(xpConnected, ch->partid, ch->number,
6868c2ecf20Sopenharmony_ci			 (void *)(u64)ch->local_nentries, ch->key);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci		dev_dbg(xpc_chan, "ch->func() returned, reason=xpConnected, "
6898c2ecf20Sopenharmony_ci			"partid=%d, channel=%d\n", ch->partid, ch->number);
6908c2ecf20Sopenharmony_ci	}
6918c2ecf20Sopenharmony_ci}
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci/*
6948c2ecf20Sopenharmony_ci * Called by XP at the time of channel connection unregistration to cause
6958c2ecf20Sopenharmony_ci * XPC to teardown all current connections for the specified channel.
6968c2ecf20Sopenharmony_ci *
6978c2ecf20Sopenharmony_ci * Before returning xpc_initiate_disconnect() will wait until all connections
6988c2ecf20Sopenharmony_ci * on the specified channel have been closed/torndown. So the caller can be
6998c2ecf20Sopenharmony_ci * assured that they will not be receiving any more callouts from XPC to the
7008c2ecf20Sopenharmony_ci * function they registered via xpc_connect().
7018c2ecf20Sopenharmony_ci *
7028c2ecf20Sopenharmony_ci * Arguments:
7038c2ecf20Sopenharmony_ci *
7048c2ecf20Sopenharmony_ci *	ch_number - channel # to unregister.
7058c2ecf20Sopenharmony_ci */
7068c2ecf20Sopenharmony_civoid
7078c2ecf20Sopenharmony_cixpc_initiate_disconnect(int ch_number)
7088c2ecf20Sopenharmony_ci{
7098c2ecf20Sopenharmony_ci	unsigned long irq_flags;
7108c2ecf20Sopenharmony_ci	short partid;
7118c2ecf20Sopenharmony_ci	struct xpc_partition *part;
7128c2ecf20Sopenharmony_ci	struct xpc_channel *ch;
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS);
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci	/* initiate the channel disconnect for every active partition */
7178c2ecf20Sopenharmony_ci	for (partid = 0; partid < xp_max_npartitions; partid++) {
7188c2ecf20Sopenharmony_ci		part = &xpc_partitions[partid];
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci		if (xpc_part_ref(part)) {
7218c2ecf20Sopenharmony_ci			ch = &part->channels[ch_number];
7228c2ecf20Sopenharmony_ci			xpc_msgqueue_ref(ch);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci			spin_lock_irqsave(&ch->lock, irq_flags);
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci			if (!(ch->flags & XPC_C_DISCONNECTED)) {
7278c2ecf20Sopenharmony_ci				ch->flags |= XPC_C_WDISCONNECT;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci				XPC_DISCONNECT_CHANNEL(ch, xpUnregistering,
7308c2ecf20Sopenharmony_ci						       &irq_flags);
7318c2ecf20Sopenharmony_ci			}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&ch->lock, irq_flags);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci			xpc_msgqueue_deref(ch);
7368c2ecf20Sopenharmony_ci			xpc_part_deref(part);
7378c2ecf20Sopenharmony_ci		}
7388c2ecf20Sopenharmony_ci	}
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	xpc_disconnect_wait(ch_number);
7418c2ecf20Sopenharmony_ci}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci/*
7448c2ecf20Sopenharmony_ci * To disconnect a channel, and reflect it back to all who may be waiting.
7458c2ecf20Sopenharmony_ci *
7468c2ecf20Sopenharmony_ci * An OPEN is not allowed until XPC_C_DISCONNECTING is cleared by
7478c2ecf20Sopenharmony_ci * xpc_process_disconnect(), and if set, XPC_C_WDISCONNECT is cleared by
7488c2ecf20Sopenharmony_ci * xpc_disconnect_wait().
7498c2ecf20Sopenharmony_ci *
7508c2ecf20Sopenharmony_ci * THE CHANNEL IS TO BE LOCKED BY THE CALLER AND WILL REMAIN LOCKED UPON RETURN.
7518c2ecf20Sopenharmony_ci */
7528c2ecf20Sopenharmony_civoid
7538c2ecf20Sopenharmony_cixpc_disconnect_channel(const int line, struct xpc_channel *ch,
7548c2ecf20Sopenharmony_ci		       enum xp_retval reason, unsigned long *irq_flags)
7558c2ecf20Sopenharmony_ci{
7568c2ecf20Sopenharmony_ci	u32 channel_was_connected = (ch->flags & XPC_C_CONNECTED);
7578c2ecf20Sopenharmony_ci
7588c2ecf20Sopenharmony_ci	lockdep_assert_held(&ch->lock);
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED))
7618c2ecf20Sopenharmony_ci		return;
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	DBUG_ON(!(ch->flags & (XPC_C_CONNECTING | XPC_C_CONNECTED)));
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	dev_dbg(xpc_chan, "reason=%d, line=%d, partid=%d, channel=%d\n",
7668c2ecf20Sopenharmony_ci		reason, line, ch->partid, ch->number);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	XPC_SET_REASON(ch, reason, line);
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ci	ch->flags |= (XPC_C_CLOSEREQUEST | XPC_C_DISCONNECTING);
7718c2ecf20Sopenharmony_ci	/* some of these may not have been set */
7728c2ecf20Sopenharmony_ci	ch->flags &= ~(XPC_C_OPENREQUEST | XPC_C_OPENREPLY |
7738c2ecf20Sopenharmony_ci		       XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY |
7748c2ecf20Sopenharmony_ci		       XPC_C_CONNECTING | XPC_C_CONNECTED);
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci	xpc_arch_ops.send_chctl_closerequest(ch, irq_flags);
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci	if (channel_was_connected)
7798c2ecf20Sopenharmony_ci		ch->flags |= XPC_C_WASCONNECTED;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&ch->lock, *irq_flags);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	/* wake all idle kthreads so they can exit */
7848c2ecf20Sopenharmony_ci	if (atomic_read(&ch->kthreads_idle) > 0) {
7858c2ecf20Sopenharmony_ci		wake_up_all(&ch->idle_wq);
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	} else if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) &&
7888c2ecf20Sopenharmony_ci		   !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) {
7898c2ecf20Sopenharmony_ci		/* start a kthread that will do the xpDisconnecting callout */
7908c2ecf20Sopenharmony_ci		xpc_create_kthreads(ch, 1, 1);
7918c2ecf20Sopenharmony_ci	}
7928c2ecf20Sopenharmony_ci
7938c2ecf20Sopenharmony_ci	/* wake those waiting to allocate an entry from the local msg queue */
7948c2ecf20Sopenharmony_ci	if (atomic_read(&ch->n_on_msg_allocate_wq) > 0)
7958c2ecf20Sopenharmony_ci		wake_up(&ch->msg_allocate_wq);
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci	spin_lock_irqsave(&ch->lock, *irq_flags);
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_civoid
8018c2ecf20Sopenharmony_cixpc_disconnect_callout(struct xpc_channel *ch, enum xp_retval reason)
8028c2ecf20Sopenharmony_ci{
8038c2ecf20Sopenharmony_ci	/*
8048c2ecf20Sopenharmony_ci	 * Let the channel's registerer know that the channel is being
8058c2ecf20Sopenharmony_ci	 * disconnected. We don't want to do this if the registerer was never
8068c2ecf20Sopenharmony_ci	 * informed of a connection being made.
8078c2ecf20Sopenharmony_ci	 */
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	if (ch->func != NULL) {
8108c2ecf20Sopenharmony_ci		dev_dbg(xpc_chan, "ch->func() called, reason=%d, partid=%d, "
8118c2ecf20Sopenharmony_ci			"channel=%d\n", reason, ch->partid, ch->number);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci		ch->func(reason, ch->partid, ch->number, NULL, ch->key);
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci		dev_dbg(xpc_chan, "ch->func() returned, reason=%d, partid=%d, "
8168c2ecf20Sopenharmony_ci			"channel=%d\n", reason, ch->partid, ch->number);
8178c2ecf20Sopenharmony_ci	}
8188c2ecf20Sopenharmony_ci}
8198c2ecf20Sopenharmony_ci
8208c2ecf20Sopenharmony_ci/*
8218c2ecf20Sopenharmony_ci * Wait for a message entry to become available for the specified channel,
8228c2ecf20Sopenharmony_ci * but don't wait any longer than 1 jiffy.
8238c2ecf20Sopenharmony_ci */
8248c2ecf20Sopenharmony_cienum xp_retval
8258c2ecf20Sopenharmony_cixpc_allocate_msg_wait(struct xpc_channel *ch)
8268c2ecf20Sopenharmony_ci{
8278c2ecf20Sopenharmony_ci	enum xp_retval ret;
8288c2ecf20Sopenharmony_ci	DEFINE_WAIT(wait);
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (ch->flags & XPC_C_DISCONNECTING) {
8318c2ecf20Sopenharmony_ci		DBUG_ON(ch->reason == xpInterrupted);
8328c2ecf20Sopenharmony_ci		return ch->reason;
8338c2ecf20Sopenharmony_ci	}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	atomic_inc(&ch->n_on_msg_allocate_wq);
8368c2ecf20Sopenharmony_ci	prepare_to_wait(&ch->msg_allocate_wq, &wait, TASK_INTERRUPTIBLE);
8378c2ecf20Sopenharmony_ci	ret = schedule_timeout(1);
8388c2ecf20Sopenharmony_ci	finish_wait(&ch->msg_allocate_wq, &wait);
8398c2ecf20Sopenharmony_ci	atomic_dec(&ch->n_on_msg_allocate_wq);
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	if (ch->flags & XPC_C_DISCONNECTING) {
8428c2ecf20Sopenharmony_ci		ret = ch->reason;
8438c2ecf20Sopenharmony_ci		DBUG_ON(ch->reason == xpInterrupted);
8448c2ecf20Sopenharmony_ci	} else if (ret == 0) {
8458c2ecf20Sopenharmony_ci		ret = xpTimeout;
8468c2ecf20Sopenharmony_ci	} else {
8478c2ecf20Sopenharmony_ci		ret = xpInterrupted;
8488c2ecf20Sopenharmony_ci	}
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci	return ret;
8518c2ecf20Sopenharmony_ci}
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci/*
8548c2ecf20Sopenharmony_ci * Send a message that contains the user's payload on the specified channel
8558c2ecf20Sopenharmony_ci * connected to the specified partition.
8568c2ecf20Sopenharmony_ci *
8578c2ecf20Sopenharmony_ci * NOTE that this routine can sleep waiting for a message entry to become
8588c2ecf20Sopenharmony_ci * available. To not sleep, pass in the XPC_NOWAIT flag.
8598c2ecf20Sopenharmony_ci *
8608c2ecf20Sopenharmony_ci * Once sent, this routine will not wait for the message to be received, nor
8618c2ecf20Sopenharmony_ci * will notification be given when it does happen.
8628c2ecf20Sopenharmony_ci *
8638c2ecf20Sopenharmony_ci * Arguments:
8648c2ecf20Sopenharmony_ci *
8658c2ecf20Sopenharmony_ci *	partid - ID of partition to which the channel is connected.
8668c2ecf20Sopenharmony_ci *	ch_number - channel # to send message on.
8678c2ecf20Sopenharmony_ci *	flags - see xp.h for valid flags.
8688c2ecf20Sopenharmony_ci *	payload - pointer to the payload which is to be sent.
8698c2ecf20Sopenharmony_ci *	payload_size - size of the payload in bytes.
8708c2ecf20Sopenharmony_ci */
8718c2ecf20Sopenharmony_cienum xp_retval
8728c2ecf20Sopenharmony_cixpc_initiate_send(short partid, int ch_number, u32 flags, void *payload,
8738c2ecf20Sopenharmony_ci		  u16 payload_size)
8748c2ecf20Sopenharmony_ci{
8758c2ecf20Sopenharmony_ci	struct xpc_partition *part = &xpc_partitions[partid];
8768c2ecf20Sopenharmony_ci	enum xp_retval ret = xpUnknownReason;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload,
8798c2ecf20Sopenharmony_ci		partid, ch_number);
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci	DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
8828c2ecf20Sopenharmony_ci	DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
8838c2ecf20Sopenharmony_ci	DBUG_ON(payload == NULL);
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	if (xpc_part_ref(part)) {
8868c2ecf20Sopenharmony_ci		ret = xpc_arch_ops.send_payload(&part->channels[ch_number],
8878c2ecf20Sopenharmony_ci				  flags, payload, payload_size, 0, NULL, NULL);
8888c2ecf20Sopenharmony_ci		xpc_part_deref(part);
8898c2ecf20Sopenharmony_ci	}
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	return ret;
8928c2ecf20Sopenharmony_ci}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci/*
8958c2ecf20Sopenharmony_ci * Send a message that contains the user's payload on the specified channel
8968c2ecf20Sopenharmony_ci * connected to the specified partition.
8978c2ecf20Sopenharmony_ci *
8988c2ecf20Sopenharmony_ci * NOTE that this routine can sleep waiting for a message entry to become
8998c2ecf20Sopenharmony_ci * available. To not sleep, pass in the XPC_NOWAIT flag.
9008c2ecf20Sopenharmony_ci *
9018c2ecf20Sopenharmony_ci * This routine will not wait for the message to be sent or received.
9028c2ecf20Sopenharmony_ci *
9038c2ecf20Sopenharmony_ci * Once the remote end of the channel has received the message, the function
9048c2ecf20Sopenharmony_ci * passed as an argument to xpc_initiate_send_notify() will be called. This
9058c2ecf20Sopenharmony_ci * allows the sender to free up or re-use any buffers referenced by the
9068c2ecf20Sopenharmony_ci * message, but does NOT mean the message has been processed at the remote
9078c2ecf20Sopenharmony_ci * end by a receiver.
9088c2ecf20Sopenharmony_ci *
9098c2ecf20Sopenharmony_ci * If this routine returns an error, the caller's function will NOT be called.
9108c2ecf20Sopenharmony_ci *
9118c2ecf20Sopenharmony_ci * Arguments:
9128c2ecf20Sopenharmony_ci *
9138c2ecf20Sopenharmony_ci *	partid - ID of partition to which the channel is connected.
9148c2ecf20Sopenharmony_ci *	ch_number - channel # to send message on.
9158c2ecf20Sopenharmony_ci *	flags - see xp.h for valid flags.
9168c2ecf20Sopenharmony_ci *	payload - pointer to the payload which is to be sent.
9178c2ecf20Sopenharmony_ci *	payload_size - size of the payload in bytes.
9188c2ecf20Sopenharmony_ci *	func - function to call with asynchronous notification of message
9198c2ecf20Sopenharmony_ci *		  receipt. THIS FUNCTION MUST BE NON-BLOCKING.
9208c2ecf20Sopenharmony_ci *	key - user-defined key to be passed to the function when it's called.
9218c2ecf20Sopenharmony_ci */
9228c2ecf20Sopenharmony_cienum xp_retval
9238c2ecf20Sopenharmony_cixpc_initiate_send_notify(short partid, int ch_number, u32 flags, void *payload,
9248c2ecf20Sopenharmony_ci			 u16 payload_size, xpc_notify_func func, void *key)
9258c2ecf20Sopenharmony_ci{
9268c2ecf20Sopenharmony_ci	struct xpc_partition *part = &xpc_partitions[partid];
9278c2ecf20Sopenharmony_ci	enum xp_retval ret = xpUnknownReason;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload,
9308c2ecf20Sopenharmony_ci		partid, ch_number);
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
9338c2ecf20Sopenharmony_ci	DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
9348c2ecf20Sopenharmony_ci	DBUG_ON(payload == NULL);
9358c2ecf20Sopenharmony_ci	DBUG_ON(func == NULL);
9368c2ecf20Sopenharmony_ci
9378c2ecf20Sopenharmony_ci	if (xpc_part_ref(part)) {
9388c2ecf20Sopenharmony_ci		ret = xpc_arch_ops.send_payload(&part->channels[ch_number],
9398c2ecf20Sopenharmony_ci			  flags, payload, payload_size, XPC_N_CALL, func, key);
9408c2ecf20Sopenharmony_ci		xpc_part_deref(part);
9418c2ecf20Sopenharmony_ci	}
9428c2ecf20Sopenharmony_ci	return ret;
9438c2ecf20Sopenharmony_ci}
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci/*
9468c2ecf20Sopenharmony_ci * Deliver a message's payload to its intended recipient.
9478c2ecf20Sopenharmony_ci */
9488c2ecf20Sopenharmony_civoid
9498c2ecf20Sopenharmony_cixpc_deliver_payload(struct xpc_channel *ch)
9508c2ecf20Sopenharmony_ci{
9518c2ecf20Sopenharmony_ci	void *payload;
9528c2ecf20Sopenharmony_ci
9538c2ecf20Sopenharmony_ci	payload = xpc_arch_ops.get_deliverable_payload(ch);
9548c2ecf20Sopenharmony_ci	if (payload != NULL) {
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci		/*
9578c2ecf20Sopenharmony_ci		 * This ref is taken to protect the payload itself from being
9588c2ecf20Sopenharmony_ci		 * freed before the user is finished with it, which the user
9598c2ecf20Sopenharmony_ci		 * indicates by calling xpc_initiate_received().
9608c2ecf20Sopenharmony_ci		 */
9618c2ecf20Sopenharmony_ci		xpc_msgqueue_ref(ch);
9628c2ecf20Sopenharmony_ci
9638c2ecf20Sopenharmony_ci		atomic_inc(&ch->kthreads_active);
9648c2ecf20Sopenharmony_ci
9658c2ecf20Sopenharmony_ci		if (ch->func != NULL) {
9668c2ecf20Sopenharmony_ci			dev_dbg(xpc_chan, "ch->func() called, payload=0x%p "
9678c2ecf20Sopenharmony_ci				"partid=%d channel=%d\n", payload, ch->partid,
9688c2ecf20Sopenharmony_ci				ch->number);
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci			/* deliver the message to its intended recipient */
9718c2ecf20Sopenharmony_ci			ch->func(xpMsgReceived, ch->partid, ch->number, payload,
9728c2ecf20Sopenharmony_ci				 ch->key);
9738c2ecf20Sopenharmony_ci
9748c2ecf20Sopenharmony_ci			dev_dbg(xpc_chan, "ch->func() returned, payload=0x%p "
9758c2ecf20Sopenharmony_ci				"partid=%d channel=%d\n", payload, ch->partid,
9768c2ecf20Sopenharmony_ci				ch->number);
9778c2ecf20Sopenharmony_ci		}
9788c2ecf20Sopenharmony_ci
9798c2ecf20Sopenharmony_ci		atomic_dec(&ch->kthreads_active);
9808c2ecf20Sopenharmony_ci	}
9818c2ecf20Sopenharmony_ci}
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci/*
9848c2ecf20Sopenharmony_ci * Acknowledge receipt of a delivered message's payload.
9858c2ecf20Sopenharmony_ci *
9868c2ecf20Sopenharmony_ci * This function, although called by users, does not call xpc_part_ref() to
9878c2ecf20Sopenharmony_ci * ensure that the partition infrastructure is in place. It relies on the
9888c2ecf20Sopenharmony_ci * fact that we called xpc_msgqueue_ref() in xpc_deliver_payload().
9898c2ecf20Sopenharmony_ci *
9908c2ecf20Sopenharmony_ci * Arguments:
9918c2ecf20Sopenharmony_ci *
9928c2ecf20Sopenharmony_ci *	partid - ID of partition to which the channel is connected.
9938c2ecf20Sopenharmony_ci *	ch_number - channel # message received on.
9948c2ecf20Sopenharmony_ci *	payload - pointer to the payload area allocated via
9958c2ecf20Sopenharmony_ci *			xpc_initiate_send() or xpc_initiate_send_notify().
9968c2ecf20Sopenharmony_ci */
9978c2ecf20Sopenharmony_civoid
9988c2ecf20Sopenharmony_cixpc_initiate_received(short partid, int ch_number, void *payload)
9998c2ecf20Sopenharmony_ci{
10008c2ecf20Sopenharmony_ci	struct xpc_partition *part = &xpc_partitions[partid];
10018c2ecf20Sopenharmony_ci	struct xpc_channel *ch;
10028c2ecf20Sopenharmony_ci
10038c2ecf20Sopenharmony_ci	DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
10048c2ecf20Sopenharmony_ci	DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	ch = &part->channels[ch_number];
10078c2ecf20Sopenharmony_ci	xpc_arch_ops.received_payload(ch, payload);
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	/* the call to xpc_msgqueue_ref() was done by xpc_deliver_payload()  */
10108c2ecf20Sopenharmony_ci	xpc_msgqueue_deref(ch);
10118c2ecf20Sopenharmony_ci}
1012