162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public 362306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive 462306a36Sopenharmony_ci * for more details. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci/* 1062306a36Sopenharmony_ci * Cross Partition Communication (XPC) channel support. 1162306a36Sopenharmony_ci * 1262306a36Sopenharmony_ci * This is the part of XPC that manages the channels and 1362306a36Sopenharmony_ci * sends/receives messages across them to/from other partitions. 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/device.h> 1862306a36Sopenharmony_ci#include "xpc.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * Process a connect message from a remote partition. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * Note: xpc_process_connect() is expecting to be called with the 2462306a36Sopenharmony_ci * spin_lock_irqsave held and will leave it locked upon return. 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_cistatic void 2762306a36Sopenharmony_cixpc_process_connect(struct xpc_channel *ch, unsigned long *irq_flags) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci enum xp_retval ret; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci lockdep_assert_held(&ch->lock); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci if (!(ch->flags & XPC_C_OPENREQUEST) || 3462306a36Sopenharmony_ci !(ch->flags & XPC_C_ROPENREQUEST)) { 3562306a36Sopenharmony_ci /* nothing more to do for now */ 3662306a36Sopenharmony_ci return; 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci DBUG_ON(!(ch->flags & XPC_C_CONNECTING)); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (!(ch->flags & XPC_C_SETUP)) { 4162306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, *irq_flags); 4262306a36Sopenharmony_ci ret = xpc_arch_ops.setup_msg_structures(ch); 4362306a36Sopenharmony_ci spin_lock_irqsave(&ch->lock, *irq_flags); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci if (ret != xpSuccess) 4662306a36Sopenharmony_ci XPC_DISCONNECT_CHANNEL(ch, ret, irq_flags); 4762306a36Sopenharmony_ci else 4862306a36Sopenharmony_ci ch->flags |= XPC_C_SETUP; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci if (ch->flags & XPC_C_DISCONNECTING) 5162306a36Sopenharmony_ci return; 5262306a36Sopenharmony_ci } 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (!(ch->flags & XPC_C_OPENREPLY)) { 5562306a36Sopenharmony_ci ch->flags |= XPC_C_OPENREPLY; 5662306a36Sopenharmony_ci xpc_arch_ops.send_chctl_openreply(ch, irq_flags); 5762306a36Sopenharmony_ci } 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (!(ch->flags & XPC_C_ROPENREPLY)) 6062306a36Sopenharmony_ci return; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (!(ch->flags & XPC_C_OPENCOMPLETE)) { 6362306a36Sopenharmony_ci ch->flags |= (XPC_C_OPENCOMPLETE | XPC_C_CONNECTED); 6462306a36Sopenharmony_ci xpc_arch_ops.send_chctl_opencomplete(ch, irq_flags); 6562306a36Sopenharmony_ci } 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (!(ch->flags & XPC_C_ROPENCOMPLETE)) 6862306a36Sopenharmony_ci return; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci dev_info(xpc_chan, "channel %d to partition %d connected\n", 7162306a36Sopenharmony_ci ch->number, ch->partid); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci ch->flags = (XPC_C_CONNECTED | XPC_C_SETUP); /* clear all else */ 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/* 7762306a36Sopenharmony_ci * spin_lock_irqsave() is expected to be held on entry. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_cistatic void 8062306a36Sopenharmony_cixpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci struct xpc_partition *part = &xpc_partitions[ch->partid]; 8362306a36Sopenharmony_ci u32 channel_was_connected = (ch->flags & XPC_C_WASCONNECTED); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci lockdep_assert_held(&ch->lock); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (!(ch->flags & XPC_C_DISCONNECTING)) 8862306a36Sopenharmony_ci return; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST)); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci /* make sure all activity has settled down first */ 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (atomic_read(&ch->kthreads_assigned) > 0 || 9562306a36Sopenharmony_ci atomic_read(&ch->references) > 0) { 9662306a36Sopenharmony_ci return; 9762306a36Sopenharmony_ci } 9862306a36Sopenharmony_ci DBUG_ON((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) && 9962306a36Sopenharmony_ci !(ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE)); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (part->act_state == XPC_P_AS_DEACTIVATING) { 10262306a36Sopenharmony_ci /* can't proceed until the other side disengages from us */ 10362306a36Sopenharmony_ci if (xpc_arch_ops.partition_engaged(ch->partid)) 10462306a36Sopenharmony_ci return; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci } else { 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* as long as the other side is up do the full protocol */ 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (!(ch->flags & XPC_C_RCLOSEREQUEST)) 11162306a36Sopenharmony_ci return; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (!(ch->flags & XPC_C_CLOSEREPLY)) { 11462306a36Sopenharmony_ci ch->flags |= XPC_C_CLOSEREPLY; 11562306a36Sopenharmony_ci xpc_arch_ops.send_chctl_closereply(ch, irq_flags); 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (!(ch->flags & XPC_C_RCLOSEREPLY)) 11962306a36Sopenharmony_ci return; 12062306a36Sopenharmony_ci } 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci /* wake those waiting for notify completion */ 12362306a36Sopenharmony_ci if (atomic_read(&ch->n_to_notify) > 0) { 12462306a36Sopenharmony_ci /* we do callout while holding ch->lock, callout can't block */ 12562306a36Sopenharmony_ci xpc_arch_ops.notify_senders_of_disconnect(ch); 12662306a36Sopenharmony_ci } 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* both sides are disconnected now */ 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE) { 13162306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, *irq_flags); 13262306a36Sopenharmony_ci xpc_disconnect_callout(ch, xpDisconnected); 13362306a36Sopenharmony_ci spin_lock_irqsave(&ch->lock, *irq_flags); 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci DBUG_ON(atomic_read(&ch->n_to_notify) != 0); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* it's now safe to free the channel's message queues */ 13962306a36Sopenharmony_ci xpc_arch_ops.teardown_msg_structures(ch); 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci ch->func = NULL; 14262306a36Sopenharmony_ci ch->key = NULL; 14362306a36Sopenharmony_ci ch->entry_size = 0; 14462306a36Sopenharmony_ci ch->local_nentries = 0; 14562306a36Sopenharmony_ci ch->remote_nentries = 0; 14662306a36Sopenharmony_ci ch->kthreads_assigned_limit = 0; 14762306a36Sopenharmony_ci ch->kthreads_idle_limit = 0; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci /* 15062306a36Sopenharmony_ci * Mark the channel disconnected and clear all other flags, including 15162306a36Sopenharmony_ci * XPC_C_SETUP (because of call to 15262306a36Sopenharmony_ci * xpc_arch_ops.teardown_msg_structures()) but not including 15362306a36Sopenharmony_ci * XPC_C_WDISCONNECT (if it was set). 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_ci ch->flags = (XPC_C_DISCONNECTED | (ch->flags & XPC_C_WDISCONNECT)); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci atomic_dec(&part->nchannels_active); 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (channel_was_connected) { 16062306a36Sopenharmony_ci dev_info(xpc_chan, "channel %d to partition %d disconnected, " 16162306a36Sopenharmony_ci "reason=%d\n", ch->number, ch->partid, ch->reason); 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci if (ch->flags & XPC_C_WDISCONNECT) { 16562306a36Sopenharmony_ci /* we won't lose the CPU since we're holding ch->lock */ 16662306a36Sopenharmony_ci complete(&ch->wdisconnect_wait); 16762306a36Sopenharmony_ci } else if (ch->delayed_chctl_flags) { 16862306a36Sopenharmony_ci if (part->act_state != XPC_P_AS_DEACTIVATING) { 16962306a36Sopenharmony_ci /* time to take action on any delayed chctl flags */ 17062306a36Sopenharmony_ci spin_lock(&part->chctl_lock); 17162306a36Sopenharmony_ci part->chctl.flags[ch->number] |= 17262306a36Sopenharmony_ci ch->delayed_chctl_flags; 17362306a36Sopenharmony_ci spin_unlock(&part->chctl_lock); 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci ch->delayed_chctl_flags = 0; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/* 18062306a36Sopenharmony_ci * Process a change in the channel's remote connection state. 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_cistatic void 18362306a36Sopenharmony_cixpc_process_openclose_chctl_flags(struct xpc_partition *part, int ch_number, 18462306a36Sopenharmony_ci u8 chctl_flags) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci unsigned long irq_flags; 18762306a36Sopenharmony_ci struct xpc_openclose_args *args = 18862306a36Sopenharmony_ci &part->remote_openclose_args[ch_number]; 18962306a36Sopenharmony_ci struct xpc_channel *ch = &part->channels[ch_number]; 19062306a36Sopenharmony_ci enum xp_retval reason; 19162306a36Sopenharmony_ci enum xp_retval ret; 19262306a36Sopenharmony_ci int create_kthread = 0; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci spin_lock_irqsave(&ch->lock, irq_flags); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ciagain: 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if ((ch->flags & XPC_C_DISCONNECTED) && 19962306a36Sopenharmony_ci (ch->flags & XPC_C_WDISCONNECT)) { 20062306a36Sopenharmony_ci /* 20162306a36Sopenharmony_ci * Delay processing chctl flags until thread waiting disconnect 20262306a36Sopenharmony_ci * has had a chance to see that the channel is disconnected. 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci ch->delayed_chctl_flags |= chctl_flags; 20562306a36Sopenharmony_ci goto out; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci if (chctl_flags & XPC_CHCTL_CLOSEREQUEST) { 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREQUEST (reason=%d) received " 21162306a36Sopenharmony_ci "from partid=%d, channel=%d\n", args->reason, 21262306a36Sopenharmony_ci ch->partid, ch->number); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci /* 21562306a36Sopenharmony_ci * If RCLOSEREQUEST is set, we're probably waiting for 21662306a36Sopenharmony_ci * RCLOSEREPLY. We should find it and a ROPENREQUEST packed 21762306a36Sopenharmony_ci * with this RCLOSEREQUEST in the chctl_flags. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci if (ch->flags & XPC_C_RCLOSEREQUEST) { 22162306a36Sopenharmony_ci DBUG_ON(!(ch->flags & XPC_C_DISCONNECTING)); 22262306a36Sopenharmony_ci DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST)); 22362306a36Sopenharmony_ci DBUG_ON(!(ch->flags & XPC_C_CLOSEREPLY)); 22462306a36Sopenharmony_ci DBUG_ON(ch->flags & XPC_C_RCLOSEREPLY); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci DBUG_ON(!(chctl_flags & XPC_CHCTL_CLOSEREPLY)); 22762306a36Sopenharmony_ci chctl_flags &= ~XPC_CHCTL_CLOSEREPLY; 22862306a36Sopenharmony_ci ch->flags |= XPC_C_RCLOSEREPLY; 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* both sides have finished disconnecting */ 23162306a36Sopenharmony_ci xpc_process_disconnect(ch, &irq_flags); 23262306a36Sopenharmony_ci DBUG_ON(!(ch->flags & XPC_C_DISCONNECTED)); 23362306a36Sopenharmony_ci goto again; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (ch->flags & XPC_C_DISCONNECTED) { 23762306a36Sopenharmony_ci if (!(chctl_flags & XPC_CHCTL_OPENREQUEST)) { 23862306a36Sopenharmony_ci if (part->chctl.flags[ch_number] & 23962306a36Sopenharmony_ci XPC_CHCTL_OPENREQUEST) { 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci DBUG_ON(ch->delayed_chctl_flags != 0); 24262306a36Sopenharmony_ci spin_lock(&part->chctl_lock); 24362306a36Sopenharmony_ci part->chctl.flags[ch_number] |= 24462306a36Sopenharmony_ci XPC_CHCTL_CLOSEREQUEST; 24562306a36Sopenharmony_ci spin_unlock(&part->chctl_lock); 24662306a36Sopenharmony_ci } 24762306a36Sopenharmony_ci goto out; 24862306a36Sopenharmony_ci } 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci XPC_SET_REASON(ch, 0, 0); 25162306a36Sopenharmony_ci ch->flags &= ~XPC_C_DISCONNECTED; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci atomic_inc(&part->nchannels_active); 25462306a36Sopenharmony_ci ch->flags |= (XPC_C_CONNECTING | XPC_C_ROPENREQUEST); 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci chctl_flags &= ~(XPC_CHCTL_OPENREQUEST | XPC_CHCTL_OPENREPLY | 25862306a36Sopenharmony_ci XPC_CHCTL_OPENCOMPLETE); 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* 26162306a36Sopenharmony_ci * The meaningful CLOSEREQUEST connection state fields are: 26262306a36Sopenharmony_ci * reason = reason connection is to be closed 26362306a36Sopenharmony_ci */ 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci ch->flags |= XPC_C_RCLOSEREQUEST; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (!(ch->flags & XPC_C_DISCONNECTING)) { 26862306a36Sopenharmony_ci reason = args->reason; 26962306a36Sopenharmony_ci if (reason <= xpSuccess || reason > xpUnknownReason) 27062306a36Sopenharmony_ci reason = xpUnknownReason; 27162306a36Sopenharmony_ci else if (reason == xpUnregistering) 27262306a36Sopenharmony_ci reason = xpOtherUnregistering; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci DBUG_ON(chctl_flags & XPC_CHCTL_CLOSEREPLY); 27762306a36Sopenharmony_ci goto out; 27862306a36Sopenharmony_ci } 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci xpc_process_disconnect(ch, &irq_flags); 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci if (chctl_flags & XPC_CHCTL_CLOSEREPLY) { 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREPLY received from partid=" 28662306a36Sopenharmony_ci "%d, channel=%d\n", ch->partid, ch->number); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci if (ch->flags & XPC_C_DISCONNECTED) { 28962306a36Sopenharmony_ci DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING); 29062306a36Sopenharmony_ci goto out; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST)); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci if (!(ch->flags & XPC_C_RCLOSEREQUEST)) { 29662306a36Sopenharmony_ci if (part->chctl.flags[ch_number] & 29762306a36Sopenharmony_ci XPC_CHCTL_CLOSEREQUEST) { 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci DBUG_ON(ch->delayed_chctl_flags != 0); 30062306a36Sopenharmony_ci spin_lock(&part->chctl_lock); 30162306a36Sopenharmony_ci part->chctl.flags[ch_number] |= 30262306a36Sopenharmony_ci XPC_CHCTL_CLOSEREPLY; 30362306a36Sopenharmony_ci spin_unlock(&part->chctl_lock); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci goto out; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci ch->flags |= XPC_C_RCLOSEREPLY; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci if (ch->flags & XPC_C_CLOSEREPLY) { 31162306a36Sopenharmony_ci /* both sides have finished disconnecting */ 31262306a36Sopenharmony_ci xpc_process_disconnect(ch, &irq_flags); 31362306a36Sopenharmony_ci } 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (chctl_flags & XPC_CHCTL_OPENREQUEST) { 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci dev_dbg(xpc_chan, "XPC_CHCTL_OPENREQUEST (entry_size=%d, " 31962306a36Sopenharmony_ci "local_nentries=%d) received from partid=%d, " 32062306a36Sopenharmony_ci "channel=%d\n", args->entry_size, args->local_nentries, 32162306a36Sopenharmony_ci ch->partid, ch->number); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (part->act_state == XPC_P_AS_DEACTIVATING || 32462306a36Sopenharmony_ci (ch->flags & XPC_C_ROPENREQUEST)) { 32562306a36Sopenharmony_ci goto out; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_WDISCONNECT)) { 32962306a36Sopenharmony_ci ch->delayed_chctl_flags |= XPC_CHCTL_OPENREQUEST; 33062306a36Sopenharmony_ci goto out; 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci DBUG_ON(!(ch->flags & (XPC_C_DISCONNECTED | 33362306a36Sopenharmony_ci XPC_C_OPENREQUEST))); 33462306a36Sopenharmony_ci DBUG_ON(ch->flags & (XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY | 33562306a36Sopenharmony_ci XPC_C_OPENREPLY | XPC_C_CONNECTED)); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* 33862306a36Sopenharmony_ci * The meaningful OPENREQUEST connection state fields are: 33962306a36Sopenharmony_ci * entry_size = size of channel's messages in bytes 34062306a36Sopenharmony_ci * local_nentries = remote partition's local_nentries 34162306a36Sopenharmony_ci */ 34262306a36Sopenharmony_ci if (args->entry_size == 0 || args->local_nentries == 0) { 34362306a36Sopenharmony_ci /* assume OPENREQUEST was delayed by mistake */ 34462306a36Sopenharmony_ci goto out; 34562306a36Sopenharmony_ci } 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci ch->flags |= (XPC_C_ROPENREQUEST | XPC_C_CONNECTING); 34862306a36Sopenharmony_ci ch->remote_nentries = args->local_nentries; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci if (ch->flags & XPC_C_OPENREQUEST) { 35162306a36Sopenharmony_ci if (args->entry_size != ch->entry_size) { 35262306a36Sopenharmony_ci XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes, 35362306a36Sopenharmony_ci &irq_flags); 35462306a36Sopenharmony_ci goto out; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci } else { 35762306a36Sopenharmony_ci ch->entry_size = args->entry_size; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci XPC_SET_REASON(ch, 0, 0); 36062306a36Sopenharmony_ci ch->flags &= ~XPC_C_DISCONNECTED; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci atomic_inc(&part->nchannels_active); 36362306a36Sopenharmony_ci } 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci xpc_process_connect(ch, &irq_flags); 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (chctl_flags & XPC_CHCTL_OPENREPLY) { 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY (local_msgqueue_pa=" 37162306a36Sopenharmony_ci "0x%lx, local_nentries=%d, remote_nentries=%d) " 37262306a36Sopenharmony_ci "received from partid=%d, channel=%d\n", 37362306a36Sopenharmony_ci args->local_msgqueue_pa, args->local_nentries, 37462306a36Sopenharmony_ci args->remote_nentries, ch->partid, ch->number); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) 37762306a36Sopenharmony_ci goto out; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci if (!(ch->flags & XPC_C_OPENREQUEST)) { 38062306a36Sopenharmony_ci XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError, 38162306a36Sopenharmony_ci &irq_flags); 38262306a36Sopenharmony_ci goto out; 38362306a36Sopenharmony_ci } 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST)); 38662306a36Sopenharmony_ci DBUG_ON(ch->flags & XPC_C_CONNECTED); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* 38962306a36Sopenharmony_ci * The meaningful OPENREPLY connection state fields are: 39062306a36Sopenharmony_ci * local_msgqueue_pa = physical address of remote 39162306a36Sopenharmony_ci * partition's local_msgqueue 39262306a36Sopenharmony_ci * local_nentries = remote partition's local_nentries 39362306a36Sopenharmony_ci * remote_nentries = remote partition's remote_nentries 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_ci DBUG_ON(args->local_msgqueue_pa == 0); 39662306a36Sopenharmony_ci DBUG_ON(args->local_nentries == 0); 39762306a36Sopenharmony_ci DBUG_ON(args->remote_nentries == 0); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci ret = xpc_arch_ops.save_remote_msgqueue_pa(ch, 40062306a36Sopenharmony_ci args->local_msgqueue_pa); 40162306a36Sopenharmony_ci if (ret != xpSuccess) { 40262306a36Sopenharmony_ci XPC_DISCONNECT_CHANNEL(ch, ret, &irq_flags); 40362306a36Sopenharmony_ci goto out; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci ch->flags |= XPC_C_ROPENREPLY; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci if (args->local_nentries < ch->remote_nentries) { 40862306a36Sopenharmony_ci dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new " 40962306a36Sopenharmony_ci "remote_nentries=%d, old remote_nentries=%d, " 41062306a36Sopenharmony_ci "partid=%d, channel=%d\n", 41162306a36Sopenharmony_ci args->local_nentries, ch->remote_nentries, 41262306a36Sopenharmony_ci ch->partid, ch->number); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci ch->remote_nentries = args->local_nentries; 41562306a36Sopenharmony_ci } 41662306a36Sopenharmony_ci if (args->remote_nentries < ch->local_nentries) { 41762306a36Sopenharmony_ci dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new " 41862306a36Sopenharmony_ci "local_nentries=%d, old local_nentries=%d, " 41962306a36Sopenharmony_ci "partid=%d, channel=%d\n", 42062306a36Sopenharmony_ci args->remote_nentries, ch->local_nentries, 42162306a36Sopenharmony_ci ch->partid, ch->number); 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci ch->local_nentries = args->remote_nentries; 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci xpc_process_connect(ch, &irq_flags); 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (chctl_flags & XPC_CHCTL_OPENCOMPLETE) { 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci dev_dbg(xpc_chan, "XPC_CHCTL_OPENCOMPLETE received from " 43262306a36Sopenharmony_ci "partid=%d, channel=%d\n", ch->partid, ch->number); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) 43562306a36Sopenharmony_ci goto out; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (!(ch->flags & XPC_C_OPENREQUEST) || 43862306a36Sopenharmony_ci !(ch->flags & XPC_C_OPENREPLY)) { 43962306a36Sopenharmony_ci XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError, 44062306a36Sopenharmony_ci &irq_flags); 44162306a36Sopenharmony_ci goto out; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST)); 44562306a36Sopenharmony_ci DBUG_ON(!(ch->flags & XPC_C_ROPENREPLY)); 44662306a36Sopenharmony_ci DBUG_ON(!(ch->flags & XPC_C_CONNECTED)); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci ch->flags |= XPC_C_ROPENCOMPLETE; 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci xpc_process_connect(ch, &irq_flags); 45162306a36Sopenharmony_ci create_kthread = 1; 45262306a36Sopenharmony_ci } 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_ciout: 45562306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, irq_flags); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci if (create_kthread) 45862306a36Sopenharmony_ci xpc_create_kthreads(ch, 1, 0); 45962306a36Sopenharmony_ci} 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci/* 46262306a36Sopenharmony_ci * Attempt to establish a channel connection to a remote partition. 46362306a36Sopenharmony_ci */ 46462306a36Sopenharmony_cistatic enum xp_retval 46562306a36Sopenharmony_cixpc_connect_channel(struct xpc_channel *ch) 46662306a36Sopenharmony_ci{ 46762306a36Sopenharmony_ci unsigned long irq_flags; 46862306a36Sopenharmony_ci struct xpc_registration *registration = &xpc_registrations[ch->number]; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci if (mutex_trylock(®istration->mutex) == 0) 47162306a36Sopenharmony_ci return xpRetry; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (!XPC_CHANNEL_REGISTERED(ch->number)) { 47462306a36Sopenharmony_ci mutex_unlock(®istration->mutex); 47562306a36Sopenharmony_ci return xpUnregistered; 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci spin_lock_irqsave(&ch->lock, irq_flags); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci DBUG_ON(ch->flags & XPC_C_CONNECTED); 48162306a36Sopenharmony_ci DBUG_ON(ch->flags & XPC_C_OPENREQUEST); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (ch->flags & XPC_C_DISCONNECTING) { 48462306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, irq_flags); 48562306a36Sopenharmony_ci mutex_unlock(®istration->mutex); 48662306a36Sopenharmony_ci return ch->reason; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci /* add info from the channel connect registration to the channel */ 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci ch->kthreads_assigned_limit = registration->assigned_limit; 49262306a36Sopenharmony_ci ch->kthreads_idle_limit = registration->idle_limit; 49362306a36Sopenharmony_ci DBUG_ON(atomic_read(&ch->kthreads_assigned) != 0); 49462306a36Sopenharmony_ci DBUG_ON(atomic_read(&ch->kthreads_idle) != 0); 49562306a36Sopenharmony_ci DBUG_ON(atomic_read(&ch->kthreads_active) != 0); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci ch->func = registration->func; 49862306a36Sopenharmony_ci DBUG_ON(registration->func == NULL); 49962306a36Sopenharmony_ci ch->key = registration->key; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci ch->local_nentries = registration->nentries; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci if (ch->flags & XPC_C_ROPENREQUEST) { 50462306a36Sopenharmony_ci if (registration->entry_size != ch->entry_size) { 50562306a36Sopenharmony_ci /* the local and remote sides aren't the same */ 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci /* 50862306a36Sopenharmony_ci * Because XPC_DISCONNECT_CHANNEL() can block we're 50962306a36Sopenharmony_ci * forced to up the registration sema before we unlock 51062306a36Sopenharmony_ci * the channel lock. But that's okay here because we're 51162306a36Sopenharmony_ci * done with the part that required the registration 51262306a36Sopenharmony_ci * sema. XPC_DISCONNECT_CHANNEL() requires that the 51362306a36Sopenharmony_ci * channel lock be locked and will unlock and relock 51462306a36Sopenharmony_ci * the channel lock as needed. 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_ci mutex_unlock(®istration->mutex); 51762306a36Sopenharmony_ci XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes, 51862306a36Sopenharmony_ci &irq_flags); 51962306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, irq_flags); 52062306a36Sopenharmony_ci return xpUnequalMsgSizes; 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci } else { 52362306a36Sopenharmony_ci ch->entry_size = registration->entry_size; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci XPC_SET_REASON(ch, 0, 0); 52662306a36Sopenharmony_ci ch->flags &= ~XPC_C_DISCONNECTED; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci atomic_inc(&xpc_partitions[ch->partid].nchannels_active); 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci mutex_unlock(®istration->mutex); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* initiate the connection */ 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci ch->flags |= (XPC_C_OPENREQUEST | XPC_C_CONNECTING); 53662306a36Sopenharmony_ci xpc_arch_ops.send_chctl_openrequest(ch, &irq_flags); 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci xpc_process_connect(ch, &irq_flags); 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, irq_flags); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci return xpSuccess; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_civoid 54662306a36Sopenharmony_cixpc_process_sent_chctl_flags(struct xpc_partition *part) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci unsigned long irq_flags; 54962306a36Sopenharmony_ci union xpc_channel_ctl_flags chctl; 55062306a36Sopenharmony_ci struct xpc_channel *ch; 55162306a36Sopenharmony_ci int ch_number; 55262306a36Sopenharmony_ci u32 ch_flags; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci chctl.all_flags = xpc_arch_ops.get_chctl_all_flags(part); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci /* 55762306a36Sopenharmony_ci * Initiate channel connections for registered channels. 55862306a36Sopenharmony_ci * 55962306a36Sopenharmony_ci * For each connected channel that has pending messages activate idle 56062306a36Sopenharmony_ci * kthreads and/or create new kthreads as needed. 56162306a36Sopenharmony_ci */ 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci for (ch_number = 0; ch_number < part->nchannels; ch_number++) { 56462306a36Sopenharmony_ci ch = &part->channels[ch_number]; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* 56762306a36Sopenharmony_ci * Process any open or close related chctl flags, and then deal 56862306a36Sopenharmony_ci * with connecting or disconnecting the channel as required. 56962306a36Sopenharmony_ci */ 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci if (chctl.flags[ch_number] & XPC_OPENCLOSE_CHCTL_FLAGS) { 57262306a36Sopenharmony_ci xpc_process_openclose_chctl_flags(part, ch_number, 57362306a36Sopenharmony_ci chctl.flags[ch_number]); 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci ch_flags = ch->flags; /* need an atomic snapshot of flags */ 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci if (ch_flags & XPC_C_DISCONNECTING) { 57962306a36Sopenharmony_ci spin_lock_irqsave(&ch->lock, irq_flags); 58062306a36Sopenharmony_ci xpc_process_disconnect(ch, &irq_flags); 58162306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, irq_flags); 58262306a36Sopenharmony_ci continue; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci if (part->act_state == XPC_P_AS_DEACTIVATING) 58662306a36Sopenharmony_ci continue; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci if (!(ch_flags & XPC_C_CONNECTED)) { 58962306a36Sopenharmony_ci if (!(ch_flags & XPC_C_OPENREQUEST)) { 59062306a36Sopenharmony_ci DBUG_ON(ch_flags & XPC_C_SETUP); 59162306a36Sopenharmony_ci (void)xpc_connect_channel(ch); 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci continue; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci /* 59762306a36Sopenharmony_ci * Process any message related chctl flags, this may involve 59862306a36Sopenharmony_ci * the activation of kthreads to deliver any pending messages 59962306a36Sopenharmony_ci * sent from the other partition. 60062306a36Sopenharmony_ci */ 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci if (chctl.flags[ch_number] & XPC_MSG_CHCTL_FLAGS) 60362306a36Sopenharmony_ci xpc_arch_ops.process_msg_chctl_flags(part, ch_number); 60462306a36Sopenharmony_ci } 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci/* 60862306a36Sopenharmony_ci * XPC's heartbeat code calls this function to inform XPC that a partition is 60962306a36Sopenharmony_ci * going down. XPC responds by tearing down the XPartition Communication 61062306a36Sopenharmony_ci * infrastructure used for the just downed partition. 61162306a36Sopenharmony_ci * 61262306a36Sopenharmony_ci * XPC's heartbeat code will never call this function and xpc_partition_up() 61362306a36Sopenharmony_ci * at the same time. Nor will it ever make multiple calls to either function 61462306a36Sopenharmony_ci * at the same time. 61562306a36Sopenharmony_ci */ 61662306a36Sopenharmony_civoid 61762306a36Sopenharmony_cixpc_partition_going_down(struct xpc_partition *part, enum xp_retval reason) 61862306a36Sopenharmony_ci{ 61962306a36Sopenharmony_ci unsigned long irq_flags; 62062306a36Sopenharmony_ci int ch_number; 62162306a36Sopenharmony_ci struct xpc_channel *ch; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci dev_dbg(xpc_chan, "deactivating partition %d, reason=%d\n", 62462306a36Sopenharmony_ci XPC_PARTID(part), reason); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci if (!xpc_part_ref(part)) { 62762306a36Sopenharmony_ci /* infrastructure for this partition isn't currently set up */ 62862306a36Sopenharmony_ci return; 62962306a36Sopenharmony_ci } 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* disconnect channels associated with the partition going down */ 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci for (ch_number = 0; ch_number < part->nchannels; ch_number++) { 63462306a36Sopenharmony_ci ch = &part->channels[ch_number]; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci xpc_msgqueue_ref(ch); 63762306a36Sopenharmony_ci spin_lock_irqsave(&ch->lock, irq_flags); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags); 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, irq_flags); 64262306a36Sopenharmony_ci xpc_msgqueue_deref(ch); 64362306a36Sopenharmony_ci } 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci xpc_wakeup_channel_mgr(part); 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci xpc_part_deref(part); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci/* 65162306a36Sopenharmony_ci * Called by XP at the time of channel connection registration to cause 65262306a36Sopenharmony_ci * XPC to establish connections to all currently active partitions. 65362306a36Sopenharmony_ci */ 65462306a36Sopenharmony_civoid 65562306a36Sopenharmony_cixpc_initiate_connect(int ch_number) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci short partid; 65862306a36Sopenharmony_ci struct xpc_partition *part; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS); 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci for (partid = 0; partid < xp_max_npartitions; partid++) { 66362306a36Sopenharmony_ci part = &xpc_partitions[partid]; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci if (xpc_part_ref(part)) { 66662306a36Sopenharmony_ci /* 66762306a36Sopenharmony_ci * Initiate the establishment of a connection on the 66862306a36Sopenharmony_ci * newly registered channel to the remote partition. 66962306a36Sopenharmony_ci */ 67062306a36Sopenharmony_ci xpc_wakeup_channel_mgr(part); 67162306a36Sopenharmony_ci xpc_part_deref(part); 67262306a36Sopenharmony_ci } 67362306a36Sopenharmony_ci } 67462306a36Sopenharmony_ci} 67562306a36Sopenharmony_ci 67662306a36Sopenharmony_civoid 67762306a36Sopenharmony_cixpc_connected_callout(struct xpc_channel *ch) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci /* let the registerer know that a connection has been established */ 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci if (ch->func != NULL) { 68262306a36Sopenharmony_ci dev_dbg(xpc_chan, "ch->func() called, reason=xpConnected, " 68362306a36Sopenharmony_ci "partid=%d, channel=%d\n", ch->partid, ch->number); 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci ch->func(xpConnected, ch->partid, ch->number, 68662306a36Sopenharmony_ci (void *)(u64)ch->local_nentries, ch->key); 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci dev_dbg(xpc_chan, "ch->func() returned, reason=xpConnected, " 68962306a36Sopenharmony_ci "partid=%d, channel=%d\n", ch->partid, ch->number); 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci/* 69462306a36Sopenharmony_ci * Called by XP at the time of channel connection unregistration to cause 69562306a36Sopenharmony_ci * XPC to teardown all current connections for the specified channel. 69662306a36Sopenharmony_ci * 69762306a36Sopenharmony_ci * Before returning xpc_initiate_disconnect() will wait until all connections 69862306a36Sopenharmony_ci * on the specified channel have been closed/torndown. So the caller can be 69962306a36Sopenharmony_ci * assured that they will not be receiving any more callouts from XPC to the 70062306a36Sopenharmony_ci * function they registered via xpc_connect(). 70162306a36Sopenharmony_ci * 70262306a36Sopenharmony_ci * Arguments: 70362306a36Sopenharmony_ci * 70462306a36Sopenharmony_ci * ch_number - channel # to unregister. 70562306a36Sopenharmony_ci */ 70662306a36Sopenharmony_civoid 70762306a36Sopenharmony_cixpc_initiate_disconnect(int ch_number) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci unsigned long irq_flags; 71062306a36Sopenharmony_ci short partid; 71162306a36Sopenharmony_ci struct xpc_partition *part; 71262306a36Sopenharmony_ci struct xpc_channel *ch; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci /* initiate the channel disconnect for every active partition */ 71762306a36Sopenharmony_ci for (partid = 0; partid < xp_max_npartitions; partid++) { 71862306a36Sopenharmony_ci part = &xpc_partitions[partid]; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (xpc_part_ref(part)) { 72162306a36Sopenharmony_ci ch = &part->channels[ch_number]; 72262306a36Sopenharmony_ci xpc_msgqueue_ref(ch); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci spin_lock_irqsave(&ch->lock, irq_flags); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci if (!(ch->flags & XPC_C_DISCONNECTED)) { 72762306a36Sopenharmony_ci ch->flags |= XPC_C_WDISCONNECT; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci XPC_DISCONNECT_CHANNEL(ch, xpUnregistering, 73062306a36Sopenharmony_ci &irq_flags); 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, irq_flags); 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci xpc_msgqueue_deref(ch); 73662306a36Sopenharmony_ci xpc_part_deref(part); 73762306a36Sopenharmony_ci } 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci xpc_disconnect_wait(ch_number); 74162306a36Sopenharmony_ci} 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci/* 74462306a36Sopenharmony_ci * To disconnect a channel, and reflect it back to all who may be waiting. 74562306a36Sopenharmony_ci * 74662306a36Sopenharmony_ci * An OPEN is not allowed until XPC_C_DISCONNECTING is cleared by 74762306a36Sopenharmony_ci * xpc_process_disconnect(), and if set, XPC_C_WDISCONNECT is cleared by 74862306a36Sopenharmony_ci * xpc_disconnect_wait(). 74962306a36Sopenharmony_ci * 75062306a36Sopenharmony_ci * THE CHANNEL IS TO BE LOCKED BY THE CALLER AND WILL REMAIN LOCKED UPON RETURN. 75162306a36Sopenharmony_ci */ 75262306a36Sopenharmony_civoid 75362306a36Sopenharmony_cixpc_disconnect_channel(const int line, struct xpc_channel *ch, 75462306a36Sopenharmony_ci enum xp_retval reason, unsigned long *irq_flags) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci u32 channel_was_connected = (ch->flags & XPC_C_CONNECTED); 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci lockdep_assert_held(&ch->lock); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED)) 76162306a36Sopenharmony_ci return; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci DBUG_ON(!(ch->flags & (XPC_C_CONNECTING | XPC_C_CONNECTED))); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci dev_dbg(xpc_chan, "reason=%d, line=%d, partid=%d, channel=%d\n", 76662306a36Sopenharmony_ci reason, line, ch->partid, ch->number); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci XPC_SET_REASON(ch, reason, line); 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci ch->flags |= (XPC_C_CLOSEREQUEST | XPC_C_DISCONNECTING); 77162306a36Sopenharmony_ci /* some of these may not have been set */ 77262306a36Sopenharmony_ci ch->flags &= ~(XPC_C_OPENREQUEST | XPC_C_OPENREPLY | 77362306a36Sopenharmony_ci XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY | 77462306a36Sopenharmony_ci XPC_C_CONNECTING | XPC_C_CONNECTED); 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci xpc_arch_ops.send_chctl_closerequest(ch, irq_flags); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci if (channel_was_connected) 77962306a36Sopenharmony_ci ch->flags |= XPC_C_WASCONNECTED; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci spin_unlock_irqrestore(&ch->lock, *irq_flags); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci /* wake all idle kthreads so they can exit */ 78462306a36Sopenharmony_ci if (atomic_read(&ch->kthreads_idle) > 0) { 78562306a36Sopenharmony_ci wake_up_all(&ch->idle_wq); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci } else if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) && 78862306a36Sopenharmony_ci !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) { 78962306a36Sopenharmony_ci /* start a kthread that will do the xpDisconnecting callout */ 79062306a36Sopenharmony_ci xpc_create_kthreads(ch, 1, 1); 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* wake those waiting to allocate an entry from the local msg queue */ 79462306a36Sopenharmony_ci if (atomic_read(&ch->n_on_msg_allocate_wq) > 0) 79562306a36Sopenharmony_ci wake_up(&ch->msg_allocate_wq); 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci spin_lock_irqsave(&ch->lock, *irq_flags); 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_civoid 80162306a36Sopenharmony_cixpc_disconnect_callout(struct xpc_channel *ch, enum xp_retval reason) 80262306a36Sopenharmony_ci{ 80362306a36Sopenharmony_ci /* 80462306a36Sopenharmony_ci * Let the channel's registerer know that the channel is being 80562306a36Sopenharmony_ci * disconnected. We don't want to do this if the registerer was never 80662306a36Sopenharmony_ci * informed of a connection being made. 80762306a36Sopenharmony_ci */ 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci if (ch->func != NULL) { 81062306a36Sopenharmony_ci dev_dbg(xpc_chan, "ch->func() called, reason=%d, partid=%d, " 81162306a36Sopenharmony_ci "channel=%d\n", reason, ch->partid, ch->number); 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci ch->func(reason, ch->partid, ch->number, NULL, ch->key); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci dev_dbg(xpc_chan, "ch->func() returned, reason=%d, partid=%d, " 81662306a36Sopenharmony_ci "channel=%d\n", reason, ch->partid, ch->number); 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci/* 82162306a36Sopenharmony_ci * Wait for a message entry to become available for the specified channel, 82262306a36Sopenharmony_ci * but don't wait any longer than 1 jiffy. 82362306a36Sopenharmony_ci */ 82462306a36Sopenharmony_cienum xp_retval 82562306a36Sopenharmony_cixpc_allocate_msg_wait(struct xpc_channel *ch) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci enum xp_retval ret; 82862306a36Sopenharmony_ci DEFINE_WAIT(wait); 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (ch->flags & XPC_C_DISCONNECTING) { 83162306a36Sopenharmony_ci DBUG_ON(ch->reason == xpInterrupted); 83262306a36Sopenharmony_ci return ch->reason; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci atomic_inc(&ch->n_on_msg_allocate_wq); 83662306a36Sopenharmony_ci prepare_to_wait(&ch->msg_allocate_wq, &wait, TASK_INTERRUPTIBLE); 83762306a36Sopenharmony_ci ret = schedule_timeout(1); 83862306a36Sopenharmony_ci finish_wait(&ch->msg_allocate_wq, &wait); 83962306a36Sopenharmony_ci atomic_dec(&ch->n_on_msg_allocate_wq); 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci if (ch->flags & XPC_C_DISCONNECTING) { 84262306a36Sopenharmony_ci ret = ch->reason; 84362306a36Sopenharmony_ci DBUG_ON(ch->reason == xpInterrupted); 84462306a36Sopenharmony_ci } else if (ret == 0) { 84562306a36Sopenharmony_ci ret = xpTimeout; 84662306a36Sopenharmony_ci } else { 84762306a36Sopenharmony_ci ret = xpInterrupted; 84862306a36Sopenharmony_ci } 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci return ret; 85162306a36Sopenharmony_ci} 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci/* 85462306a36Sopenharmony_ci * Send a message that contains the user's payload on the specified channel 85562306a36Sopenharmony_ci * connected to the specified partition. 85662306a36Sopenharmony_ci * 85762306a36Sopenharmony_ci * NOTE that this routine can sleep waiting for a message entry to become 85862306a36Sopenharmony_ci * available. To not sleep, pass in the XPC_NOWAIT flag. 85962306a36Sopenharmony_ci * 86062306a36Sopenharmony_ci * Once sent, this routine will not wait for the message to be received, nor 86162306a36Sopenharmony_ci * will notification be given when it does happen. 86262306a36Sopenharmony_ci * 86362306a36Sopenharmony_ci * Arguments: 86462306a36Sopenharmony_ci * 86562306a36Sopenharmony_ci * partid - ID of partition to which the channel is connected. 86662306a36Sopenharmony_ci * ch_number - channel # to send message on. 86762306a36Sopenharmony_ci * flags - see xp.h for valid flags. 86862306a36Sopenharmony_ci * payload - pointer to the payload which is to be sent. 86962306a36Sopenharmony_ci * payload_size - size of the payload in bytes. 87062306a36Sopenharmony_ci */ 87162306a36Sopenharmony_cienum xp_retval 87262306a36Sopenharmony_cixpc_initiate_send(short partid, int ch_number, u32 flags, void *payload, 87362306a36Sopenharmony_ci u16 payload_size) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci struct xpc_partition *part = &xpc_partitions[partid]; 87662306a36Sopenharmony_ci enum xp_retval ret = xpUnknownReason; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload, 87962306a36Sopenharmony_ci partid, ch_number); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci DBUG_ON(partid < 0 || partid >= xp_max_npartitions); 88262306a36Sopenharmony_ci DBUG_ON(ch_number < 0 || ch_number >= part->nchannels); 88362306a36Sopenharmony_ci DBUG_ON(payload == NULL); 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (xpc_part_ref(part)) { 88662306a36Sopenharmony_ci ret = xpc_arch_ops.send_payload(&part->channels[ch_number], 88762306a36Sopenharmony_ci flags, payload, payload_size, 0, NULL, NULL); 88862306a36Sopenharmony_ci xpc_part_deref(part); 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci return ret; 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci/* 89562306a36Sopenharmony_ci * Send a message that contains the user's payload on the specified channel 89662306a36Sopenharmony_ci * connected to the specified partition. 89762306a36Sopenharmony_ci * 89862306a36Sopenharmony_ci * NOTE that this routine can sleep waiting for a message entry to become 89962306a36Sopenharmony_ci * available. To not sleep, pass in the XPC_NOWAIT flag. 90062306a36Sopenharmony_ci * 90162306a36Sopenharmony_ci * This routine will not wait for the message to be sent or received. 90262306a36Sopenharmony_ci * 90362306a36Sopenharmony_ci * Once the remote end of the channel has received the message, the function 90462306a36Sopenharmony_ci * passed as an argument to xpc_initiate_send_notify() will be called. This 90562306a36Sopenharmony_ci * allows the sender to free up or re-use any buffers referenced by the 90662306a36Sopenharmony_ci * message, but does NOT mean the message has been processed at the remote 90762306a36Sopenharmony_ci * end by a receiver. 90862306a36Sopenharmony_ci * 90962306a36Sopenharmony_ci * If this routine returns an error, the caller's function will NOT be called. 91062306a36Sopenharmony_ci * 91162306a36Sopenharmony_ci * Arguments: 91262306a36Sopenharmony_ci * 91362306a36Sopenharmony_ci * partid - ID of partition to which the channel is connected. 91462306a36Sopenharmony_ci * ch_number - channel # to send message on. 91562306a36Sopenharmony_ci * flags - see xp.h for valid flags. 91662306a36Sopenharmony_ci * payload - pointer to the payload which is to be sent. 91762306a36Sopenharmony_ci * payload_size - size of the payload in bytes. 91862306a36Sopenharmony_ci * func - function to call with asynchronous notification of message 91962306a36Sopenharmony_ci * receipt. THIS FUNCTION MUST BE NON-BLOCKING. 92062306a36Sopenharmony_ci * key - user-defined key to be passed to the function when it's called. 92162306a36Sopenharmony_ci */ 92262306a36Sopenharmony_cienum xp_retval 92362306a36Sopenharmony_cixpc_initiate_send_notify(short partid, int ch_number, u32 flags, void *payload, 92462306a36Sopenharmony_ci u16 payload_size, xpc_notify_func func, void *key) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci struct xpc_partition *part = &xpc_partitions[partid]; 92762306a36Sopenharmony_ci enum xp_retval ret = xpUnknownReason; 92862306a36Sopenharmony_ci 92962306a36Sopenharmony_ci dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload, 93062306a36Sopenharmony_ci partid, ch_number); 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci DBUG_ON(partid < 0 || partid >= xp_max_npartitions); 93362306a36Sopenharmony_ci DBUG_ON(ch_number < 0 || ch_number >= part->nchannels); 93462306a36Sopenharmony_ci DBUG_ON(payload == NULL); 93562306a36Sopenharmony_ci DBUG_ON(func == NULL); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci if (xpc_part_ref(part)) { 93862306a36Sopenharmony_ci ret = xpc_arch_ops.send_payload(&part->channels[ch_number], 93962306a36Sopenharmony_ci flags, payload, payload_size, XPC_N_CALL, func, key); 94062306a36Sopenharmony_ci xpc_part_deref(part); 94162306a36Sopenharmony_ci } 94262306a36Sopenharmony_ci return ret; 94362306a36Sopenharmony_ci} 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci/* 94662306a36Sopenharmony_ci * Deliver a message's payload to its intended recipient. 94762306a36Sopenharmony_ci */ 94862306a36Sopenharmony_civoid 94962306a36Sopenharmony_cixpc_deliver_payload(struct xpc_channel *ch) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci void *payload; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci payload = xpc_arch_ops.get_deliverable_payload(ch); 95462306a36Sopenharmony_ci if (payload != NULL) { 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci /* 95762306a36Sopenharmony_ci * This ref is taken to protect the payload itself from being 95862306a36Sopenharmony_ci * freed before the user is finished with it, which the user 95962306a36Sopenharmony_ci * indicates by calling xpc_initiate_received(). 96062306a36Sopenharmony_ci */ 96162306a36Sopenharmony_ci xpc_msgqueue_ref(ch); 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci atomic_inc(&ch->kthreads_active); 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci if (ch->func != NULL) { 96662306a36Sopenharmony_ci dev_dbg(xpc_chan, "ch->func() called, payload=0x%p " 96762306a36Sopenharmony_ci "partid=%d channel=%d\n", payload, ch->partid, 96862306a36Sopenharmony_ci ch->number); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci /* deliver the message to its intended recipient */ 97162306a36Sopenharmony_ci ch->func(xpMsgReceived, ch->partid, ch->number, payload, 97262306a36Sopenharmony_ci ch->key); 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci dev_dbg(xpc_chan, "ch->func() returned, payload=0x%p " 97562306a36Sopenharmony_ci "partid=%d channel=%d\n", payload, ch->partid, 97662306a36Sopenharmony_ci ch->number); 97762306a36Sopenharmony_ci } 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci atomic_dec(&ch->kthreads_active); 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci/* 98462306a36Sopenharmony_ci * Acknowledge receipt of a delivered message's payload. 98562306a36Sopenharmony_ci * 98662306a36Sopenharmony_ci * This function, although called by users, does not call xpc_part_ref() to 98762306a36Sopenharmony_ci * ensure that the partition infrastructure is in place. It relies on the 98862306a36Sopenharmony_ci * fact that we called xpc_msgqueue_ref() in xpc_deliver_payload(). 98962306a36Sopenharmony_ci * 99062306a36Sopenharmony_ci * Arguments: 99162306a36Sopenharmony_ci * 99262306a36Sopenharmony_ci * partid - ID of partition to which the channel is connected. 99362306a36Sopenharmony_ci * ch_number - channel # message received on. 99462306a36Sopenharmony_ci * payload - pointer to the payload area allocated via 99562306a36Sopenharmony_ci * xpc_initiate_send() or xpc_initiate_send_notify(). 99662306a36Sopenharmony_ci */ 99762306a36Sopenharmony_civoid 99862306a36Sopenharmony_cixpc_initiate_received(short partid, int ch_number, void *payload) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci struct xpc_partition *part = &xpc_partitions[partid]; 100162306a36Sopenharmony_ci struct xpc_channel *ch; 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci DBUG_ON(partid < 0 || partid >= xp_max_npartitions); 100462306a36Sopenharmony_ci DBUG_ON(ch_number < 0 || ch_number >= part->nchannels); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci ch = &part->channels[ch_number]; 100762306a36Sopenharmony_ci xpc_arch_ops.received_payload(ch, payload); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci /* the call to xpc_msgqueue_ref() was done by xpc_deliver_payload() */ 101062306a36Sopenharmony_ci xpc_msgqueue_deref(ch); 101162306a36Sopenharmony_ci} 1012