Path: blob/master/drivers/misc/sgi-xp/xpc_channel.c
15111 views
/*1* This file is subject to the terms and conditions of the GNU General Public2* License. See the file "COPYING" in the main directory of this archive3* for more details.4*5* Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved.6*/78/*9* Cross Partition Communication (XPC) channel support.10*11* This is the part of XPC that manages the channels and12* sends/receives messages across them to/from other partitions.13*14*/1516#include <linux/device.h>17#include "xpc.h"1819/*20* Process a connect message from a remote partition.21*22* Note: xpc_process_connect() is expecting to be called with the23* spin_lock_irqsave held and will leave it locked upon return.24*/25static void26xpc_process_connect(struct xpc_channel *ch, unsigned long *irq_flags)27{28enum xp_retval ret;2930DBUG_ON(!spin_is_locked(&ch->lock));3132if (!(ch->flags & XPC_C_OPENREQUEST) ||33!(ch->flags & XPC_C_ROPENREQUEST)) {34/* nothing more to do for now */35return;36}37DBUG_ON(!(ch->flags & XPC_C_CONNECTING));3839if (!(ch->flags & XPC_C_SETUP)) {40spin_unlock_irqrestore(&ch->lock, *irq_flags);41ret = xpc_arch_ops.setup_msg_structures(ch);42spin_lock_irqsave(&ch->lock, *irq_flags);4344if (ret != xpSuccess)45XPC_DISCONNECT_CHANNEL(ch, ret, irq_flags);46else47ch->flags |= XPC_C_SETUP;4849if (ch->flags & XPC_C_DISCONNECTING)50return;51}5253if (!(ch->flags & XPC_C_OPENREPLY)) {54ch->flags |= XPC_C_OPENREPLY;55xpc_arch_ops.send_chctl_openreply(ch, irq_flags);56}5758if (!(ch->flags & XPC_C_ROPENREPLY))59return;6061if (!(ch->flags & XPC_C_OPENCOMPLETE)) {62ch->flags |= (XPC_C_OPENCOMPLETE | XPC_C_CONNECTED);63xpc_arch_ops.send_chctl_opencomplete(ch, irq_flags);64}6566if (!(ch->flags & XPC_C_ROPENCOMPLETE))67return;6869dev_info(xpc_chan, "channel %d to partition %d connected\n",70ch->number, ch->partid);7172ch->flags = (XPC_C_CONNECTED | XPC_C_SETUP); /* clear all else */73}7475/*76* spin_lock_irqsave() is expected to be held on entry.77*/78static void79xpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags)80{81struct xpc_partition *part = &xpc_partitions[ch->partid];82u32 channel_was_connected = (ch->flags & XPC_C_WASCONNECTED);8384DBUG_ON(!spin_is_locked(&ch->lock));8586if (!(ch->flags & XPC_C_DISCONNECTING))87return;8889DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));9091/* make sure all activity has settled down first */9293if (atomic_read(&ch->kthreads_assigned) > 0 ||94atomic_read(&ch->references) > 0) {95return;96}97DBUG_ON((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) &&98!(ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE));99100if (part->act_state == XPC_P_AS_DEACTIVATING) {101/* can't proceed until the other side disengages from us */102if (xpc_arch_ops.partition_engaged(ch->partid))103return;104105} else {106107/* as long as the other side is up do the full protocol */108109if (!(ch->flags & XPC_C_RCLOSEREQUEST))110return;111112if (!(ch->flags & XPC_C_CLOSEREPLY)) {113ch->flags |= XPC_C_CLOSEREPLY;114xpc_arch_ops.send_chctl_closereply(ch, irq_flags);115}116117if (!(ch->flags & XPC_C_RCLOSEREPLY))118return;119}120121/* wake those waiting for notify completion */122if (atomic_read(&ch->n_to_notify) > 0) {123/* we do callout while holding ch->lock, callout can't block */124xpc_arch_ops.notify_senders_of_disconnect(ch);125}126127/* both sides are disconnected now */128129if (ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE) {130spin_unlock_irqrestore(&ch->lock, *irq_flags);131xpc_disconnect_callout(ch, xpDisconnected);132spin_lock_irqsave(&ch->lock, *irq_flags);133}134135DBUG_ON(atomic_read(&ch->n_to_notify) != 0);136137/* it's now safe to free the channel's message queues */138xpc_arch_ops.teardown_msg_structures(ch);139140ch->func = NULL;141ch->key = NULL;142ch->entry_size = 0;143ch->local_nentries = 0;144ch->remote_nentries = 0;145ch->kthreads_assigned_limit = 0;146ch->kthreads_idle_limit = 0;147148/*149* Mark the channel disconnected and clear all other flags, including150* XPC_C_SETUP (because of call to151* xpc_arch_ops.teardown_msg_structures()) but not including152* XPC_C_WDISCONNECT (if it was set).153*/154ch->flags = (XPC_C_DISCONNECTED | (ch->flags & XPC_C_WDISCONNECT));155156atomic_dec(&part->nchannels_active);157158if (channel_was_connected) {159dev_info(xpc_chan, "channel %d to partition %d disconnected, "160"reason=%d\n", ch->number, ch->partid, ch->reason);161}162163if (ch->flags & XPC_C_WDISCONNECT) {164/* we won't lose the CPU since we're holding ch->lock */165complete(&ch->wdisconnect_wait);166} else if (ch->delayed_chctl_flags) {167if (part->act_state != XPC_P_AS_DEACTIVATING) {168/* time to take action on any delayed chctl flags */169spin_lock(&part->chctl_lock);170part->chctl.flags[ch->number] |=171ch->delayed_chctl_flags;172spin_unlock(&part->chctl_lock);173}174ch->delayed_chctl_flags = 0;175}176}177178/*179* Process a change in the channel's remote connection state.180*/181static void182xpc_process_openclose_chctl_flags(struct xpc_partition *part, int ch_number,183u8 chctl_flags)184{185unsigned long irq_flags;186struct xpc_openclose_args *args =187&part->remote_openclose_args[ch_number];188struct xpc_channel *ch = &part->channels[ch_number];189enum xp_retval reason;190enum xp_retval ret;191int create_kthread = 0;192193spin_lock_irqsave(&ch->lock, irq_flags);194195again:196197if ((ch->flags & XPC_C_DISCONNECTED) &&198(ch->flags & XPC_C_WDISCONNECT)) {199/*200* Delay processing chctl flags until thread waiting disconnect201* has had a chance to see that the channel is disconnected.202*/203ch->delayed_chctl_flags |= chctl_flags;204goto out;205}206207if (chctl_flags & XPC_CHCTL_CLOSEREQUEST) {208209dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREQUEST (reason=%d) received "210"from partid=%d, channel=%d\n", args->reason,211ch->partid, ch->number);212213/*214* If RCLOSEREQUEST is set, we're probably waiting for215* RCLOSEREPLY. We should find it and a ROPENREQUEST packed216* with this RCLOSEREQUEST in the chctl_flags.217*/218219if (ch->flags & XPC_C_RCLOSEREQUEST) {220DBUG_ON(!(ch->flags & XPC_C_DISCONNECTING));221DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));222DBUG_ON(!(ch->flags & XPC_C_CLOSEREPLY));223DBUG_ON(ch->flags & XPC_C_RCLOSEREPLY);224225DBUG_ON(!(chctl_flags & XPC_CHCTL_CLOSEREPLY));226chctl_flags &= ~XPC_CHCTL_CLOSEREPLY;227ch->flags |= XPC_C_RCLOSEREPLY;228229/* both sides have finished disconnecting */230xpc_process_disconnect(ch, &irq_flags);231DBUG_ON(!(ch->flags & XPC_C_DISCONNECTED));232goto again;233}234235if (ch->flags & XPC_C_DISCONNECTED) {236if (!(chctl_flags & XPC_CHCTL_OPENREQUEST)) {237if (part->chctl.flags[ch_number] &238XPC_CHCTL_OPENREQUEST) {239240DBUG_ON(ch->delayed_chctl_flags != 0);241spin_lock(&part->chctl_lock);242part->chctl.flags[ch_number] |=243XPC_CHCTL_CLOSEREQUEST;244spin_unlock(&part->chctl_lock);245}246goto out;247}248249XPC_SET_REASON(ch, 0, 0);250ch->flags &= ~XPC_C_DISCONNECTED;251252atomic_inc(&part->nchannels_active);253ch->flags |= (XPC_C_CONNECTING | XPC_C_ROPENREQUEST);254}255256chctl_flags &= ~(XPC_CHCTL_OPENREQUEST | XPC_CHCTL_OPENREPLY |257XPC_CHCTL_OPENCOMPLETE);258259/*260* The meaningful CLOSEREQUEST connection state fields are:261* reason = reason connection is to be closed262*/263264ch->flags |= XPC_C_RCLOSEREQUEST;265266if (!(ch->flags & XPC_C_DISCONNECTING)) {267reason = args->reason;268if (reason <= xpSuccess || reason > xpUnknownReason)269reason = xpUnknownReason;270else if (reason == xpUnregistering)271reason = xpOtherUnregistering;272273XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags);274275DBUG_ON(chctl_flags & XPC_CHCTL_CLOSEREPLY);276goto out;277}278279xpc_process_disconnect(ch, &irq_flags);280}281282if (chctl_flags & XPC_CHCTL_CLOSEREPLY) {283284dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREPLY received from partid="285"%d, channel=%d\n", ch->partid, ch->number);286287if (ch->flags & XPC_C_DISCONNECTED) {288DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING);289goto out;290}291292DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));293294if (!(ch->flags & XPC_C_RCLOSEREQUEST)) {295if (part->chctl.flags[ch_number] &296XPC_CHCTL_CLOSEREQUEST) {297298DBUG_ON(ch->delayed_chctl_flags != 0);299spin_lock(&part->chctl_lock);300part->chctl.flags[ch_number] |=301XPC_CHCTL_CLOSEREPLY;302spin_unlock(&part->chctl_lock);303}304goto out;305}306307ch->flags |= XPC_C_RCLOSEREPLY;308309if (ch->flags & XPC_C_CLOSEREPLY) {310/* both sides have finished disconnecting */311xpc_process_disconnect(ch, &irq_flags);312}313}314315if (chctl_flags & XPC_CHCTL_OPENREQUEST) {316317dev_dbg(xpc_chan, "XPC_CHCTL_OPENREQUEST (entry_size=%d, "318"local_nentries=%d) received from partid=%d, "319"channel=%d\n", args->entry_size, args->local_nentries,320ch->partid, ch->number);321322if (part->act_state == XPC_P_AS_DEACTIVATING ||323(ch->flags & XPC_C_ROPENREQUEST)) {324goto out;325}326327if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_WDISCONNECT)) {328ch->delayed_chctl_flags |= XPC_CHCTL_OPENREQUEST;329goto out;330}331DBUG_ON(!(ch->flags & (XPC_C_DISCONNECTED |332XPC_C_OPENREQUEST)));333DBUG_ON(ch->flags & (XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY |334XPC_C_OPENREPLY | XPC_C_CONNECTED));335336/*337* The meaningful OPENREQUEST connection state fields are:338* entry_size = size of channel's messages in bytes339* local_nentries = remote partition's local_nentries340*/341if (args->entry_size == 0 || args->local_nentries == 0) {342/* assume OPENREQUEST was delayed by mistake */343goto out;344}345346ch->flags |= (XPC_C_ROPENREQUEST | XPC_C_CONNECTING);347ch->remote_nentries = args->local_nentries;348349if (ch->flags & XPC_C_OPENREQUEST) {350if (args->entry_size != ch->entry_size) {351XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes,352&irq_flags);353goto out;354}355} else {356ch->entry_size = args->entry_size;357358XPC_SET_REASON(ch, 0, 0);359ch->flags &= ~XPC_C_DISCONNECTED;360361atomic_inc(&part->nchannels_active);362}363364xpc_process_connect(ch, &irq_flags);365}366367if (chctl_flags & XPC_CHCTL_OPENREPLY) {368369dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY (local_msgqueue_pa="370"0x%lx, local_nentries=%d, remote_nentries=%d) "371"received from partid=%d, channel=%d\n",372args->local_msgqueue_pa, args->local_nentries,373args->remote_nentries, ch->partid, ch->number);374375if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED))376goto out;377378if (!(ch->flags & XPC_C_OPENREQUEST)) {379XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError,380&irq_flags);381goto out;382}383384DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST));385DBUG_ON(ch->flags & XPC_C_CONNECTED);386387/*388* The meaningful OPENREPLY connection state fields are:389* local_msgqueue_pa = physical address of remote390* partition's local_msgqueue391* local_nentries = remote partition's local_nentries392* remote_nentries = remote partition's remote_nentries393*/394DBUG_ON(args->local_msgqueue_pa == 0);395DBUG_ON(args->local_nentries == 0);396DBUG_ON(args->remote_nentries == 0);397398ret = xpc_arch_ops.save_remote_msgqueue_pa(ch,399args->local_msgqueue_pa);400if (ret != xpSuccess) {401XPC_DISCONNECT_CHANNEL(ch, ret, &irq_flags);402goto out;403}404ch->flags |= XPC_C_ROPENREPLY;405406if (args->local_nentries < ch->remote_nentries) {407dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new "408"remote_nentries=%d, old remote_nentries=%d, "409"partid=%d, channel=%d\n",410args->local_nentries, ch->remote_nentries,411ch->partid, ch->number);412413ch->remote_nentries = args->local_nentries;414}415if (args->remote_nentries < ch->local_nentries) {416dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new "417"local_nentries=%d, old local_nentries=%d, "418"partid=%d, channel=%d\n",419args->remote_nentries, ch->local_nentries,420ch->partid, ch->number);421422ch->local_nentries = args->remote_nentries;423}424425xpc_process_connect(ch, &irq_flags);426}427428if (chctl_flags & XPC_CHCTL_OPENCOMPLETE) {429430dev_dbg(xpc_chan, "XPC_CHCTL_OPENCOMPLETE received from "431"partid=%d, channel=%d\n", ch->partid, ch->number);432433if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED))434goto out;435436if (!(ch->flags & XPC_C_OPENREQUEST) ||437!(ch->flags & XPC_C_OPENREPLY)) {438XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError,439&irq_flags);440goto out;441}442443DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST));444DBUG_ON(!(ch->flags & XPC_C_ROPENREPLY));445DBUG_ON(!(ch->flags & XPC_C_CONNECTED));446447ch->flags |= XPC_C_ROPENCOMPLETE;448449xpc_process_connect(ch, &irq_flags);450create_kthread = 1;451}452453out:454spin_unlock_irqrestore(&ch->lock, irq_flags);455456if (create_kthread)457xpc_create_kthreads(ch, 1, 0);458}459460/*461* Attempt to establish a channel connection to a remote partition.462*/463static enum xp_retval464xpc_connect_channel(struct xpc_channel *ch)465{466unsigned long irq_flags;467struct xpc_registration *registration = &xpc_registrations[ch->number];468469if (mutex_trylock(®istration->mutex) == 0)470return xpRetry;471472if (!XPC_CHANNEL_REGISTERED(ch->number)) {473mutex_unlock(®istration->mutex);474return xpUnregistered;475}476477spin_lock_irqsave(&ch->lock, irq_flags);478479DBUG_ON(ch->flags & XPC_C_CONNECTED);480DBUG_ON(ch->flags & XPC_C_OPENREQUEST);481482if (ch->flags & XPC_C_DISCONNECTING) {483spin_unlock_irqrestore(&ch->lock, irq_flags);484mutex_unlock(®istration->mutex);485return ch->reason;486}487488/* add info from the channel connect registration to the channel */489490ch->kthreads_assigned_limit = registration->assigned_limit;491ch->kthreads_idle_limit = registration->idle_limit;492DBUG_ON(atomic_read(&ch->kthreads_assigned) != 0);493DBUG_ON(atomic_read(&ch->kthreads_idle) != 0);494DBUG_ON(atomic_read(&ch->kthreads_active) != 0);495496ch->func = registration->func;497DBUG_ON(registration->func == NULL);498ch->key = registration->key;499500ch->local_nentries = registration->nentries;501502if (ch->flags & XPC_C_ROPENREQUEST) {503if (registration->entry_size != ch->entry_size) {504/* the local and remote sides aren't the same */505506/*507* Because XPC_DISCONNECT_CHANNEL() can block we're508* forced to up the registration sema before we unlock509* the channel lock. But that's okay here because we're510* done with the part that required the registration511* sema. XPC_DISCONNECT_CHANNEL() requires that the512* channel lock be locked and will unlock and relock513* the channel lock as needed.514*/515mutex_unlock(®istration->mutex);516XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes,517&irq_flags);518spin_unlock_irqrestore(&ch->lock, irq_flags);519return xpUnequalMsgSizes;520}521} else {522ch->entry_size = registration->entry_size;523524XPC_SET_REASON(ch, 0, 0);525ch->flags &= ~XPC_C_DISCONNECTED;526527atomic_inc(&xpc_partitions[ch->partid].nchannels_active);528}529530mutex_unlock(®istration->mutex);531532/* initiate the connection */533534ch->flags |= (XPC_C_OPENREQUEST | XPC_C_CONNECTING);535xpc_arch_ops.send_chctl_openrequest(ch, &irq_flags);536537xpc_process_connect(ch, &irq_flags);538539spin_unlock_irqrestore(&ch->lock, irq_flags);540541return xpSuccess;542}543544void545xpc_process_sent_chctl_flags(struct xpc_partition *part)546{547unsigned long irq_flags;548union xpc_channel_ctl_flags chctl;549struct xpc_channel *ch;550int ch_number;551u32 ch_flags;552553chctl.all_flags = xpc_arch_ops.get_chctl_all_flags(part);554555/*556* Initiate channel connections for registered channels.557*558* For each connected channel that has pending messages activate idle559* kthreads and/or create new kthreads as needed.560*/561562for (ch_number = 0; ch_number < part->nchannels; ch_number++) {563ch = &part->channels[ch_number];564565/*566* Process any open or close related chctl flags, and then deal567* with connecting or disconnecting the channel as required.568*/569570if (chctl.flags[ch_number] & XPC_OPENCLOSE_CHCTL_FLAGS) {571xpc_process_openclose_chctl_flags(part, ch_number,572chctl.flags[ch_number]);573}574575ch_flags = ch->flags; /* need an atomic snapshot of flags */576577if (ch_flags & XPC_C_DISCONNECTING) {578spin_lock_irqsave(&ch->lock, irq_flags);579xpc_process_disconnect(ch, &irq_flags);580spin_unlock_irqrestore(&ch->lock, irq_flags);581continue;582}583584if (part->act_state == XPC_P_AS_DEACTIVATING)585continue;586587if (!(ch_flags & XPC_C_CONNECTED)) {588if (!(ch_flags & XPC_C_OPENREQUEST)) {589DBUG_ON(ch_flags & XPC_C_SETUP);590(void)xpc_connect_channel(ch);591}592continue;593}594595/*596* Process any message related chctl flags, this may involve597* the activation of kthreads to deliver any pending messages598* sent from the other partition.599*/600601if (chctl.flags[ch_number] & XPC_MSG_CHCTL_FLAGS)602xpc_arch_ops.process_msg_chctl_flags(part, ch_number);603}604}605606/*607* XPC's heartbeat code calls this function to inform XPC that a partition is608* going down. XPC responds by tearing down the XPartition Communication609* infrastructure used for the just downed partition.610*611* XPC's heartbeat code will never call this function and xpc_partition_up()612* at the same time. Nor will it ever make multiple calls to either function613* at the same time.614*/615void616xpc_partition_going_down(struct xpc_partition *part, enum xp_retval reason)617{618unsigned long irq_flags;619int ch_number;620struct xpc_channel *ch;621622dev_dbg(xpc_chan, "deactivating partition %d, reason=%d\n",623XPC_PARTID(part), reason);624625if (!xpc_part_ref(part)) {626/* infrastructure for this partition isn't currently set up */627return;628}629630/* disconnect channels associated with the partition going down */631632for (ch_number = 0; ch_number < part->nchannels; ch_number++) {633ch = &part->channels[ch_number];634635xpc_msgqueue_ref(ch);636spin_lock_irqsave(&ch->lock, irq_flags);637638XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags);639640spin_unlock_irqrestore(&ch->lock, irq_flags);641xpc_msgqueue_deref(ch);642}643644xpc_wakeup_channel_mgr(part);645646xpc_part_deref(part);647}648649/*650* Called by XP at the time of channel connection registration to cause651* XPC to establish connections to all currently active partitions.652*/653void654xpc_initiate_connect(int ch_number)655{656short partid;657struct xpc_partition *part;658struct xpc_channel *ch;659660DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS);661662for (partid = 0; partid < xp_max_npartitions; partid++) {663part = &xpc_partitions[partid];664665if (xpc_part_ref(part)) {666ch = &part->channels[ch_number];667668/*669* Initiate the establishment of a connection on the670* newly registered channel to the remote partition.671*/672xpc_wakeup_channel_mgr(part);673xpc_part_deref(part);674}675}676}677678void679xpc_connected_callout(struct xpc_channel *ch)680{681/* let the registerer know that a connection has been established */682683if (ch->func != NULL) {684dev_dbg(xpc_chan, "ch->func() called, reason=xpConnected, "685"partid=%d, channel=%d\n", ch->partid, ch->number);686687ch->func(xpConnected, ch->partid, ch->number,688(void *)(u64)ch->local_nentries, ch->key);689690dev_dbg(xpc_chan, "ch->func() returned, reason=xpConnected, "691"partid=%d, channel=%d\n", ch->partid, ch->number);692}693}694695/*696* Called by XP at the time of channel connection unregistration to cause697* XPC to teardown all current connections for the specified channel.698*699* Before returning xpc_initiate_disconnect() will wait until all connections700* on the specified channel have been closed/torndown. So the caller can be701* assured that they will not be receiving any more callouts from XPC to the702* function they registered via xpc_connect().703*704* Arguments:705*706* ch_number - channel # to unregister.707*/708void709xpc_initiate_disconnect(int ch_number)710{711unsigned long irq_flags;712short partid;713struct xpc_partition *part;714struct xpc_channel *ch;715716DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS);717718/* initiate the channel disconnect for every active partition */719for (partid = 0; partid < xp_max_npartitions; partid++) {720part = &xpc_partitions[partid];721722if (xpc_part_ref(part)) {723ch = &part->channels[ch_number];724xpc_msgqueue_ref(ch);725726spin_lock_irqsave(&ch->lock, irq_flags);727728if (!(ch->flags & XPC_C_DISCONNECTED)) {729ch->flags |= XPC_C_WDISCONNECT;730731XPC_DISCONNECT_CHANNEL(ch, xpUnregistering,732&irq_flags);733}734735spin_unlock_irqrestore(&ch->lock, irq_flags);736737xpc_msgqueue_deref(ch);738xpc_part_deref(part);739}740}741742xpc_disconnect_wait(ch_number);743}744745/*746* To disconnect a channel, and reflect it back to all who may be waiting.747*748* An OPEN is not allowed until XPC_C_DISCONNECTING is cleared by749* xpc_process_disconnect(), and if set, XPC_C_WDISCONNECT is cleared by750* xpc_disconnect_wait().751*752* THE CHANNEL IS TO BE LOCKED BY THE CALLER AND WILL REMAIN LOCKED UPON RETURN.753*/754void755xpc_disconnect_channel(const int line, struct xpc_channel *ch,756enum xp_retval reason, unsigned long *irq_flags)757{758u32 channel_was_connected = (ch->flags & XPC_C_CONNECTED);759760DBUG_ON(!spin_is_locked(&ch->lock));761762if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED))763return;764765DBUG_ON(!(ch->flags & (XPC_C_CONNECTING | XPC_C_CONNECTED)));766767dev_dbg(xpc_chan, "reason=%d, line=%d, partid=%d, channel=%d\n",768reason, line, ch->partid, ch->number);769770XPC_SET_REASON(ch, reason, line);771772ch->flags |= (XPC_C_CLOSEREQUEST | XPC_C_DISCONNECTING);773/* some of these may not have been set */774ch->flags &= ~(XPC_C_OPENREQUEST | XPC_C_OPENREPLY |775XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY |776XPC_C_CONNECTING | XPC_C_CONNECTED);777778xpc_arch_ops.send_chctl_closerequest(ch, irq_flags);779780if (channel_was_connected)781ch->flags |= XPC_C_WASCONNECTED;782783spin_unlock_irqrestore(&ch->lock, *irq_flags);784785/* wake all idle kthreads so they can exit */786if (atomic_read(&ch->kthreads_idle) > 0) {787wake_up_all(&ch->idle_wq);788789} else if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) &&790!(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) {791/* start a kthread that will do the xpDisconnecting callout */792xpc_create_kthreads(ch, 1, 1);793}794795/* wake those waiting to allocate an entry from the local msg queue */796if (atomic_read(&ch->n_on_msg_allocate_wq) > 0)797wake_up(&ch->msg_allocate_wq);798799spin_lock_irqsave(&ch->lock, *irq_flags);800}801802void803xpc_disconnect_callout(struct xpc_channel *ch, enum xp_retval reason)804{805/*806* Let the channel's registerer know that the channel is being807* disconnected. We don't want to do this if the registerer was never808* informed of a connection being made.809*/810811if (ch->func != NULL) {812dev_dbg(xpc_chan, "ch->func() called, reason=%d, partid=%d, "813"channel=%d\n", reason, ch->partid, ch->number);814815ch->func(reason, ch->partid, ch->number, NULL, ch->key);816817dev_dbg(xpc_chan, "ch->func() returned, reason=%d, partid=%d, "818"channel=%d\n", reason, ch->partid, ch->number);819}820}821822/*823* Wait for a message entry to become available for the specified channel,824* but don't wait any longer than 1 jiffy.825*/826enum xp_retval827xpc_allocate_msg_wait(struct xpc_channel *ch)828{829enum xp_retval ret;830831if (ch->flags & XPC_C_DISCONNECTING) {832DBUG_ON(ch->reason == xpInterrupted);833return ch->reason;834}835836atomic_inc(&ch->n_on_msg_allocate_wq);837ret = interruptible_sleep_on_timeout(&ch->msg_allocate_wq, 1);838atomic_dec(&ch->n_on_msg_allocate_wq);839840if (ch->flags & XPC_C_DISCONNECTING) {841ret = ch->reason;842DBUG_ON(ch->reason == xpInterrupted);843} else if (ret == 0) {844ret = xpTimeout;845} else {846ret = xpInterrupted;847}848849return ret;850}851852/*853* Send a message that contains the user's payload on the specified channel854* connected to the specified partition.855*856* NOTE that this routine can sleep waiting for a message entry to become857* available. To not sleep, pass in the XPC_NOWAIT flag.858*859* Once sent, this routine will not wait for the message to be received, nor860* will notification be given when it does happen.861*862* Arguments:863*864* partid - ID of partition to which the channel is connected.865* ch_number - channel # to send message on.866* flags - see xp.h for valid flags.867* payload - pointer to the payload which is to be sent.868* payload_size - size of the payload in bytes.869*/870enum xp_retval871xpc_initiate_send(short partid, int ch_number, u32 flags, void *payload,872u16 payload_size)873{874struct xpc_partition *part = &xpc_partitions[partid];875enum xp_retval ret = xpUnknownReason;876877dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload,878partid, ch_number);879880DBUG_ON(partid < 0 || partid >= xp_max_npartitions);881DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);882DBUG_ON(payload == NULL);883884if (xpc_part_ref(part)) {885ret = xpc_arch_ops.send_payload(&part->channels[ch_number],886flags, payload, payload_size, 0, NULL, NULL);887xpc_part_deref(part);888}889890return ret;891}892893/*894* Send a message that contains the user's payload on the specified channel895* connected to the specified partition.896*897* NOTE that this routine can sleep waiting for a message entry to become898* available. To not sleep, pass in the XPC_NOWAIT flag.899*900* This routine will not wait for the message to be sent or received.901*902* Once the remote end of the channel has received the message, the function903* passed as an argument to xpc_initiate_send_notify() will be called. This904* allows the sender to free up or re-use any buffers referenced by the905* message, but does NOT mean the message has been processed at the remote906* end by a receiver.907*908* If this routine returns an error, the caller's function will NOT be called.909*910* Arguments:911*912* partid - ID of partition to which the channel is connected.913* ch_number - channel # to send message on.914* flags - see xp.h for valid flags.915* payload - pointer to the payload which is to be sent.916* payload_size - size of the payload in bytes.917* func - function to call with asynchronous notification of message918* receipt. THIS FUNCTION MUST BE NON-BLOCKING.919* key - user-defined key to be passed to the function when it's called.920*/921enum xp_retval922xpc_initiate_send_notify(short partid, int ch_number, u32 flags, void *payload,923u16 payload_size, xpc_notify_func func, void *key)924{925struct xpc_partition *part = &xpc_partitions[partid];926enum xp_retval ret = xpUnknownReason;927928dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload,929partid, ch_number);930931DBUG_ON(partid < 0 || partid >= xp_max_npartitions);932DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);933DBUG_ON(payload == NULL);934DBUG_ON(func == NULL);935936if (xpc_part_ref(part)) {937ret = xpc_arch_ops.send_payload(&part->channels[ch_number],938flags, payload, payload_size, XPC_N_CALL, func, key);939xpc_part_deref(part);940}941return ret;942}943944/*945* Deliver a message's payload to its intended recipient.946*/947void948xpc_deliver_payload(struct xpc_channel *ch)949{950void *payload;951952payload = xpc_arch_ops.get_deliverable_payload(ch);953if (payload != NULL) {954955/*956* This ref is taken to protect the payload itself from being957* freed before the user is finished with it, which the user958* indicates by calling xpc_initiate_received().959*/960xpc_msgqueue_ref(ch);961962atomic_inc(&ch->kthreads_active);963964if (ch->func != NULL) {965dev_dbg(xpc_chan, "ch->func() called, payload=0x%p "966"partid=%d channel=%d\n", payload, ch->partid,967ch->number);968969/* deliver the message to its intended recipient */970ch->func(xpMsgReceived, ch->partid, ch->number, payload,971ch->key);972973dev_dbg(xpc_chan, "ch->func() returned, payload=0x%p "974"partid=%d channel=%d\n", payload, ch->partid,975ch->number);976}977978atomic_dec(&ch->kthreads_active);979}980}981982/*983* Acknowledge receipt of a delivered message's payload.984*985* This function, although called by users, does not call xpc_part_ref() to986* ensure that the partition infrastructure is in place. It relies on the987* fact that we called xpc_msgqueue_ref() in xpc_deliver_payload().988*989* Arguments:990*991* partid - ID of partition to which the channel is connected.992* ch_number - channel # message received on.993* payload - pointer to the payload area allocated via994* xpc_initiate_send() or xpc_initiate_send_notify().995*/996void997xpc_initiate_received(short partid, int ch_number, void *payload)998{999struct xpc_partition *part = &xpc_partitions[partid];1000struct xpc_channel *ch;10011002DBUG_ON(partid < 0 || partid >= xp_max_npartitions);1003DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);10041005ch = &part->channels[ch_number];1006xpc_arch_ops.received_payload(ch, payload);10071008/* the call to xpc_msgqueue_ref() was done by xpc_deliver_payload() */1009xpc_msgqueue_deref(ch);1010}101110121013