blob: 128d5615c8040ce2c1225487cfc53f0d4d7fed97 [file] [log] [blame]
Dean Nelson89eb8eb2005-03-23 19:50:00 -07001/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
Robin Holtefdd06e2009-04-13 14:40:19 -07006 * Copyright (c) 2004-2009 Silicon Graphics, Inc. All Rights Reserved.
Dean Nelson89eb8eb2005-03-23 19:50:00 -07007 */
8
Dean Nelson89eb8eb2005-03-23 19:50:00 -07009/*
10 * Cross Partition Communication (XPC) channel support.
11 *
12 * This is the part of XPC that manages the channels and
13 * sends/receives messages across them to/from other partitions.
14 *
15 */
16
Dean Nelson261f3b42008-07-29 22:34:16 -070017#include <linux/device.h>
Dean Nelson45d9ca42008-04-22 14:46:56 -050018#include "xpc.h"
Dean Nelson89eb8eb2005-03-23 19:50:00 -070019
Dean Nelson89eb8eb2005-03-23 19:50:00 -070020/*
Dean Nelson89eb8eb2005-03-23 19:50:00 -070021 * Process a connect message from a remote partition.
22 *
23 * Note: xpc_process_connect() is expecting to be called with the
24 * spin_lock_irqsave held and will leave it locked upon return.
25 */
26static void
27xpc_process_connect(struct xpc_channel *ch, unsigned long *irq_flags)
28{
Dean Nelson65c17b82008-05-12 14:02:02 -070029 enum xp_retval ret;
Dean Nelson89eb8eb2005-03-23 19:50:00 -070030
Dean Nelson89eb8eb2005-03-23 19:50:00 -070031 DBUG_ON(!spin_is_locked(&ch->lock));
32
33 if (!(ch->flags & XPC_C_OPENREQUEST) ||
Dean Nelson35190502008-04-22 14:48:55 -050034 !(ch->flags & XPC_C_ROPENREQUEST)) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -070035 /* nothing more to do for now */
36 return;
37 }
38 DBUG_ON(!(ch->flags & XPC_C_CONNECTING));
39
40 if (!(ch->flags & XPC_C_SETUP)) {
41 spin_unlock_irqrestore(&ch->lock, *irq_flags);
Robin Holta7665b02009-04-13 14:40:19 -070042 ret = xpc_arch_ops.setup_msg_structures(ch);
Dean Nelson89eb8eb2005-03-23 19:50:00 -070043 spin_lock_irqsave(&ch->lock, *irq_flags);
44
Dean Nelson65c17b82008-05-12 14:02:02 -070045 if (ret != xpSuccess)
Dean Nelson89eb8eb2005-03-23 19:50:00 -070046 XPC_DISCONNECT_CHANNEL(ch, ret, irq_flags);
Robin Holtefdd06e2009-04-13 14:40:19 -070047 else
48 ch->flags |= XPC_C_SETUP;
Dean Nelson2c2b94f2008-04-22 14:50:17 -050049
Robin Holtefdd06e2009-04-13 14:40:19 -070050 if (ch->flags & XPC_C_DISCONNECTING)
Dean Nelson89eb8eb2005-03-23 19:50:00 -070051 return;
Dean Nelson89eb8eb2005-03-23 19:50:00 -070052 }
53
54 if (!(ch->flags & XPC_C_OPENREPLY)) {
55 ch->flags |= XPC_C_OPENREPLY;
Robin Holta7665b02009-04-13 14:40:19 -070056 xpc_arch_ops.send_chctl_openreply(ch, irq_flags);
Dean Nelson89eb8eb2005-03-23 19:50:00 -070057 }
58
Dean Nelson2c2b94f2008-04-22 14:50:17 -050059 if (!(ch->flags & XPC_C_ROPENREPLY))
Dean Nelson89eb8eb2005-03-23 19:50:00 -070060 return;
Dean Nelson89eb8eb2005-03-23 19:50:00 -070061
Robin Holtefdd06e2009-04-13 14:40:19 -070062 if (!(ch->flags & XPC_C_OPENCOMPLETE)) {
63 ch->flags |= (XPC_C_OPENCOMPLETE | XPC_C_CONNECTED);
Robin Holta7665b02009-04-13 14:40:19 -070064 xpc_arch_ops.send_chctl_opencomplete(ch, irq_flags);
Robin Holtefdd06e2009-04-13 14:40:19 -070065 }
66
67 if (!(ch->flags & XPC_C_ROPENCOMPLETE))
68 return;
Dean Nelson89eb8eb2005-03-23 19:50:00 -070069
70 dev_info(xpc_chan, "channel %d to partition %d connected\n",
Dean Nelson35190502008-04-22 14:48:55 -050071 ch->number, ch->partid);
Dean Nelson89eb8eb2005-03-23 19:50:00 -070072
Robin Holtefdd06e2009-04-13 14:40:19 -070073 ch->flags = (XPC_C_CONNECTED | XPC_C_SETUP); /* clear all else */
Dean Nelson89eb8eb2005-03-23 19:50:00 -070074}
75
Dean Nelson89eb8eb2005-03-23 19:50:00 -070076/*
Dean Nelson89eb8eb2005-03-23 19:50:00 -070077 * spin_lock_irqsave() is expected to be held on entry.
78 */
79static void
80xpc_process_disconnect(struct xpc_channel *ch, unsigned long *irq_flags)
81{
82 struct xpc_partition *part = &xpc_partitions[ch->partid];
Dean Nelsona607c382005-09-01 14:01:37 -050083 u32 channel_was_connected = (ch->flags & XPC_C_WASCONNECTED);
Dean Nelson89eb8eb2005-03-23 19:50:00 -070084
Dean Nelson89eb8eb2005-03-23 19:50:00 -070085 DBUG_ON(!spin_is_locked(&ch->lock));
86
Dean Nelson2c2b94f2008-04-22 14:50:17 -050087 if (!(ch->flags & XPC_C_DISCONNECTING))
Dean Nelson89eb8eb2005-03-23 19:50:00 -070088 return;
Dean Nelson89eb8eb2005-03-23 19:50:00 -070089
90 DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));
91
92 /* make sure all activity has settled down first */
93
Dean Nelsona460ef82006-11-22 08:25:00 -060094 if (atomic_read(&ch->kthreads_assigned) > 0 ||
Dean Nelson35190502008-04-22 14:48:55 -050095 atomic_read(&ch->references) > 0) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -070096 return;
97 }
Dean Nelsona460ef82006-11-22 08:25:00 -060098 DBUG_ON((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) &&
Dean Nelson35190502008-04-22 14:48:55 -050099 !(ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE));
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700100
Dean Nelson83469b52008-07-29 22:34:18 -0700101 if (part->act_state == XPC_P_AS_DEACTIVATING) {
Dean Nelsona607c382005-09-01 14:01:37 -0500102 /* can't proceed until the other side disengages from us */
Robin Holta7665b02009-04-13 14:40:19 -0700103 if (xpc_arch_ops.partition_engaged(ch->partid))
Dean Nelsona607c382005-09-01 14:01:37 -0500104 return;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700105
Dean Nelsona607c382005-09-01 14:01:37 -0500106 } else {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700107
108 /* as long as the other side is up do the full protocol */
109
Dean Nelson2c2b94f2008-04-22 14:50:17 -0500110 if (!(ch->flags & XPC_C_RCLOSEREQUEST))
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700111 return;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700112
113 if (!(ch->flags & XPC_C_CLOSEREPLY)) {
114 ch->flags |= XPC_C_CLOSEREPLY;
Robin Holta7665b02009-04-13 14:40:19 -0700115 xpc_arch_ops.send_chctl_closereply(ch, irq_flags);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700116 }
117
Dean Nelson2c2b94f2008-04-22 14:50:17 -0500118 if (!(ch->flags & XPC_C_RCLOSEREPLY))
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700119 return;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700120 }
121
Dean Nelsona607c382005-09-01 14:01:37 -0500122 /* wake those waiting for notify completion */
123 if (atomic_read(&ch->n_to_notify) > 0) {
Dean Nelsonea57f802008-07-29 22:34:14 -0700124 /* we do callout while holding ch->lock, callout can't block */
Robin Holta7665b02009-04-13 14:40:19 -0700125 xpc_arch_ops.notify_senders_of_disconnect(ch);
Dean Nelsona607c382005-09-01 14:01:37 -0500126 }
127
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700128 /* both sides are disconnected now */
129
Dean Nelson4c2cd962006-02-15 08:02:21 -0600130 if (ch->flags & XPC_C_DISCONNECTINGCALLOUT_MADE) {
Dean Nelson246c7e32005-12-22 14:32:56 -0600131 spin_unlock_irqrestore(&ch->lock, *irq_flags);
Dean Nelson65c17b82008-05-12 14:02:02 -0700132 xpc_disconnect_callout(ch, xpDisconnected);
Dean Nelson246c7e32005-12-22 14:32:56 -0600133 spin_lock_irqsave(&ch->lock, *irq_flags);
134 }
135
Dean Nelson5b8669d2008-07-29 22:34:18 -0700136 DBUG_ON(atomic_read(&ch->n_to_notify) != 0);
137
Dean Nelsona607c382005-09-01 14:01:37 -0500138 /* it's now safe to free the channel's message queues */
Robin Holta7665b02009-04-13 14:40:19 -0700139 xpc_arch_ops.teardown_msg_structures(ch);
Dean Nelson5b8669d2008-07-29 22:34:18 -0700140
141 ch->func = NULL;
142 ch->key = NULL;
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700143 ch->entry_size = 0;
Dean Nelson5b8669d2008-07-29 22:34:18 -0700144 ch->local_nentries = 0;
145 ch->remote_nentries = 0;
146 ch->kthreads_assigned_limit = 0;
147 ch->kthreads_idle_limit = 0;
Dean Nelsona607c382005-09-01 14:01:37 -0500148
Dean Nelson185c3a12008-07-29 22:34:11 -0700149 /*
150 * Mark the channel disconnected and clear all other flags, including
Robin Holta7665b02009-04-13 14:40:19 -0700151 * XPC_C_SETUP (because of call to
152 * xpc_arch_ops.teardown_msg_structures()) but not including
153 * XPC_C_WDISCONNECT (if it was set).
Dean Nelson185c3a12008-07-29 22:34:11 -0700154 */
Dean Nelsona607c382005-09-01 14:01:37 -0500155 ch->flags = (XPC_C_DISCONNECTED | (ch->flags & XPC_C_WDISCONNECT));
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700156
157 atomic_dec(&part->nchannels_active);
158
Dean Nelsona607c382005-09-01 14:01:37 -0500159 if (channel_was_connected) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700160 dev_info(xpc_chan, "channel %d to partition %d disconnected, "
Dean Nelson35190502008-04-22 14:48:55 -0500161 "reason=%d\n", ch->number, ch->partid, ch->reason);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700162 }
Dean Nelsona607c382005-09-01 14:01:37 -0500163
Dean Nelsona607c382005-09-01 14:01:37 -0500164 if (ch->flags & XPC_C_WDISCONNECT) {
Jes Sorensenf9e505a2006-01-17 12:52:21 -0500165 /* we won't lose the CPU since we're holding ch->lock */
166 complete(&ch->wdisconnect_wait);
Dean Nelson7fb5e592008-07-29 22:34:10 -0700167 } else if (ch->delayed_chctl_flags) {
Dean Nelson83469b52008-07-29 22:34:18 -0700168 if (part->act_state != XPC_P_AS_DEACTIVATING) {
Dean Nelson7fb5e592008-07-29 22:34:10 -0700169 /* time to take action on any delayed chctl flags */
170 spin_lock(&part->chctl_lock);
171 part->chctl.flags[ch->number] |=
172 ch->delayed_chctl_flags;
173 spin_unlock(&part->chctl_lock);
Dean Nelsone54af722005-10-25 14:07:43 -0500174 }
Dean Nelson7fb5e592008-07-29 22:34:10 -0700175 ch->delayed_chctl_flags = 0;
Dean Nelsona607c382005-09-01 14:01:37 -0500176 }
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700177}
178
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700179/*
180 * Process a change in the channel's remote connection state.
181 */
182static void
Dean Nelson7fb5e592008-07-29 22:34:10 -0700183xpc_process_openclose_chctl_flags(struct xpc_partition *part, int ch_number,
184 u8 chctl_flags)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700185{
186 unsigned long irq_flags;
187 struct xpc_openclose_args *args =
Dean Nelson35190502008-04-22 14:48:55 -0500188 &part->remote_openclose_args[ch_number];
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700189 struct xpc_channel *ch = &part->channels[ch_number];
Dean Nelson65c17b82008-05-12 14:02:02 -0700190 enum xp_retval reason;
Jack Steiner6f2584f2009-04-02 16:59:10 -0700191 enum xp_retval ret;
Robin Holtefdd06e2009-04-13 14:40:19 -0700192 int create_kthread = 0;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700193
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700194 spin_lock_irqsave(&ch->lock, irq_flags);
195
Dean Nelson2c2b94f2008-04-22 14:50:17 -0500196again:
Dean Nelsone54af722005-10-25 14:07:43 -0500197
Dean Nelson2c2b94f2008-04-22 14:50:17 -0500198 if ((ch->flags & XPC_C_DISCONNECTED) &&
199 (ch->flags & XPC_C_WDISCONNECT)) {
Dean Nelsone54af722005-10-25 14:07:43 -0500200 /*
Dean Nelson7fb5e592008-07-29 22:34:10 -0700201 * Delay processing chctl flags until thread waiting disconnect
Dean Nelsone54af722005-10-25 14:07:43 -0500202 * has had a chance to see that the channel is disconnected.
203 */
Dean Nelson7fb5e592008-07-29 22:34:10 -0700204 ch->delayed_chctl_flags |= chctl_flags;
Robin Holtefdd06e2009-04-13 14:40:19 -0700205 goto out;
Dean Nelsone54af722005-10-25 14:07:43 -0500206 }
207
Dean Nelson7fb5e592008-07-29 22:34:10 -0700208 if (chctl_flags & XPC_CHCTL_CLOSEREQUEST) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700209
Dean Nelson7fb5e592008-07-29 22:34:10 -0700210 dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREQUEST (reason=%d) received "
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700211 "from partid=%d, channel=%d\n", args->reason,
212 ch->partid, ch->number);
213
214 /*
215 * If RCLOSEREQUEST is set, we're probably waiting for
216 * RCLOSEREPLY. We should find it and a ROPENREQUEST packed
Dean Nelson7fb5e592008-07-29 22:34:10 -0700217 * with this RCLOSEREQUEST in the chctl_flags.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700218 */
219
220 if (ch->flags & XPC_C_RCLOSEREQUEST) {
221 DBUG_ON(!(ch->flags & XPC_C_DISCONNECTING));
222 DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));
223 DBUG_ON(!(ch->flags & XPC_C_CLOSEREPLY));
224 DBUG_ON(ch->flags & XPC_C_RCLOSEREPLY);
225
Dean Nelson7fb5e592008-07-29 22:34:10 -0700226 DBUG_ON(!(chctl_flags & XPC_CHCTL_CLOSEREPLY));
227 chctl_flags &= ~XPC_CHCTL_CLOSEREPLY;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700228 ch->flags |= XPC_C_RCLOSEREPLY;
229
230 /* both sides have finished disconnecting */
231 xpc_process_disconnect(ch, &irq_flags);
Dean Nelsone54af722005-10-25 14:07:43 -0500232 DBUG_ON(!(ch->flags & XPC_C_DISCONNECTED));
233 goto again;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700234 }
235
236 if (ch->flags & XPC_C_DISCONNECTED) {
Dean Nelson7fb5e592008-07-29 22:34:10 -0700237 if (!(chctl_flags & XPC_CHCTL_OPENREQUEST)) {
238 if (part->chctl.flags[ch_number] &
239 XPC_CHCTL_OPENREQUEST) {
Dean Nelsone54af722005-10-25 14:07:43 -0500240
Dean Nelson7fb5e592008-07-29 22:34:10 -0700241 DBUG_ON(ch->delayed_chctl_flags != 0);
242 spin_lock(&part->chctl_lock);
243 part->chctl.flags[ch_number] |=
244 XPC_CHCTL_CLOSEREQUEST;
245 spin_unlock(&part->chctl_lock);
Dean Nelsone54af722005-10-25 14:07:43 -0500246 }
Robin Holtefdd06e2009-04-13 14:40:19 -0700247 goto out;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700248 }
249
250 XPC_SET_REASON(ch, 0, 0);
251 ch->flags &= ~XPC_C_DISCONNECTED;
252
253 atomic_inc(&part->nchannels_active);
254 ch->flags |= (XPC_C_CONNECTING | XPC_C_ROPENREQUEST);
255 }
256
Robin Holtefdd06e2009-04-13 14:40:19 -0700257 chctl_flags &= ~(XPC_CHCTL_OPENREQUEST | XPC_CHCTL_OPENREPLY |
258 XPC_CHCTL_OPENCOMPLETE);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700259
260 /*
261 * The meaningful CLOSEREQUEST connection state fields are:
262 * reason = reason connection is to be closed
263 */
264
265 ch->flags |= XPC_C_RCLOSEREQUEST;
266
267 if (!(ch->flags & XPC_C_DISCONNECTING)) {
268 reason = args->reason;
Dean Nelson65c17b82008-05-12 14:02:02 -0700269 if (reason <= xpSuccess || reason > xpUnknownReason)
270 reason = xpUnknownReason;
271 else if (reason == xpUnregistering)
272 reason = xpOtherUnregistering;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700273
274 XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags);
Dean Nelsone54af722005-10-25 14:07:43 -0500275
Dean Nelson7fb5e592008-07-29 22:34:10 -0700276 DBUG_ON(chctl_flags & XPC_CHCTL_CLOSEREPLY);
Robin Holtefdd06e2009-04-13 14:40:19 -0700277 goto out;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700278 }
Dean Nelsone54af722005-10-25 14:07:43 -0500279
280 xpc_process_disconnect(ch, &irq_flags);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700281 }
282
Dean Nelson7fb5e592008-07-29 22:34:10 -0700283 if (chctl_flags & XPC_CHCTL_CLOSEREPLY) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700284
Dean Nelson7fb5e592008-07-29 22:34:10 -0700285 dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREPLY received from partid="
286 "%d, channel=%d\n", ch->partid, ch->number);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700287
288 if (ch->flags & XPC_C_DISCONNECTED) {
Dean Nelson83469b52008-07-29 22:34:18 -0700289 DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING);
Robin Holtefdd06e2009-04-13 14:40:19 -0700290 goto out;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700291 }
292
293 DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));
Dean Nelsone54af722005-10-25 14:07:43 -0500294
295 if (!(ch->flags & XPC_C_RCLOSEREQUEST)) {
Dean Nelson7fb5e592008-07-29 22:34:10 -0700296 if (part->chctl.flags[ch_number] &
297 XPC_CHCTL_CLOSEREQUEST) {
Dean Nelsone54af722005-10-25 14:07:43 -0500298
Dean Nelson7fb5e592008-07-29 22:34:10 -0700299 DBUG_ON(ch->delayed_chctl_flags != 0);
300 spin_lock(&part->chctl_lock);
301 part->chctl.flags[ch_number] |=
302 XPC_CHCTL_CLOSEREPLY;
303 spin_unlock(&part->chctl_lock);
Dean Nelsone54af722005-10-25 14:07:43 -0500304 }
Robin Holtefdd06e2009-04-13 14:40:19 -0700305 goto out;
Dean Nelsone54af722005-10-25 14:07:43 -0500306 }
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700307
308 ch->flags |= XPC_C_RCLOSEREPLY;
309
310 if (ch->flags & XPC_C_CLOSEREPLY) {
311 /* both sides have finished disconnecting */
312 xpc_process_disconnect(ch, &irq_flags);
313 }
314 }
315
Dean Nelson7fb5e592008-07-29 22:34:10 -0700316 if (chctl_flags & XPC_CHCTL_OPENREQUEST) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700317
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700318 dev_dbg(xpc_chan, "XPC_CHCTL_OPENREQUEST (entry_size=%d, "
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700319 "local_nentries=%d) received from partid=%d, "
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700320 "channel=%d\n", args->entry_size, args->local_nentries,
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700321 ch->partid, ch->number);
322
Dean Nelson83469b52008-07-29 22:34:18 -0700323 if (part->act_state == XPC_P_AS_DEACTIVATING ||
Dean Nelson35190502008-04-22 14:48:55 -0500324 (ch->flags & XPC_C_ROPENREQUEST)) {
Robin Holtefdd06e2009-04-13 14:40:19 -0700325 goto out;
Dean Nelsone54af722005-10-25 14:07:43 -0500326 }
327
328 if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_WDISCONNECT)) {
Dean Nelson7fb5e592008-07-29 22:34:10 -0700329 ch->delayed_chctl_flags |= XPC_CHCTL_OPENREQUEST;
Robin Holtefdd06e2009-04-13 14:40:19 -0700330 goto out;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700331 }
332 DBUG_ON(!(ch->flags & (XPC_C_DISCONNECTED |
Dean Nelson35190502008-04-22 14:48:55 -0500333 XPC_C_OPENREQUEST)));
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700334 DBUG_ON(ch->flags & (XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY |
Dean Nelson35190502008-04-22 14:48:55 -0500335 XPC_C_OPENREPLY | XPC_C_CONNECTED));
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700336
337 /*
338 * The meaningful OPENREQUEST connection state fields are:
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700339 * entry_size = size of channel's messages in bytes
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700340 * local_nentries = remote partition's local_nentries
341 */
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700342 if (args->entry_size == 0 || args->local_nentries == 0) {
Dean Nelsone54af722005-10-25 14:07:43 -0500343 /* assume OPENREQUEST was delayed by mistake */
Robin Holtefdd06e2009-04-13 14:40:19 -0700344 goto out;
Dean Nelsone54af722005-10-25 14:07:43 -0500345 }
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700346
347 ch->flags |= (XPC_C_ROPENREQUEST | XPC_C_CONNECTING);
348 ch->remote_nentries = args->local_nentries;
349
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700350 if (ch->flags & XPC_C_OPENREQUEST) {
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700351 if (args->entry_size != ch->entry_size) {
Dean Nelson65c17b82008-05-12 14:02:02 -0700352 XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes,
Dean Nelson35190502008-04-22 14:48:55 -0500353 &irq_flags);
Robin Holtefdd06e2009-04-13 14:40:19 -0700354 goto out;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700355 }
356 } else {
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700357 ch->entry_size = args->entry_size;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700358
359 XPC_SET_REASON(ch, 0, 0);
360 ch->flags &= ~XPC_C_DISCONNECTED;
361
362 atomic_inc(&part->nchannels_active);
363 }
364
365 xpc_process_connect(ch, &irq_flags);
366 }
367
Dean Nelson7fb5e592008-07-29 22:34:10 -0700368 if (chctl_flags & XPC_CHCTL_OPENREPLY) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700369
Dean Nelson7fb5e592008-07-29 22:34:10 -0700370 dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY (local_msgqueue_pa="
371 "0x%lx, local_nentries=%d, remote_nentries=%d) "
372 "received from partid=%d, channel=%d\n",
Dean Nelsona812dcc2008-07-29 22:34:16 -0700373 args->local_msgqueue_pa, args->local_nentries,
374 args->remote_nentries, ch->partid, ch->number);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700375
Robin Holtefdd06e2009-04-13 14:40:19 -0700376 if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED))
377 goto out;
378
Dean Nelsone54af722005-10-25 14:07:43 -0500379 if (!(ch->flags & XPC_C_OPENREQUEST)) {
Dean Nelson65c17b82008-05-12 14:02:02 -0700380 XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError,
Dean Nelson35190502008-04-22 14:48:55 -0500381 &irq_flags);
Robin Holtefdd06e2009-04-13 14:40:19 -0700382 goto out;
Dean Nelsone54af722005-10-25 14:07:43 -0500383 }
384
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700385 DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST));
386 DBUG_ON(ch->flags & XPC_C_CONNECTED);
387
388 /*
389 * The meaningful OPENREPLY connection state fields are:
390 * local_msgqueue_pa = physical address of remote
Dean Nelson35190502008-04-22 14:48:55 -0500391 * partition's local_msgqueue
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700392 * local_nentries = remote partition's local_nentries
393 * remote_nentries = remote partition's remote_nentries
394 */
395 DBUG_ON(args->local_msgqueue_pa == 0);
396 DBUG_ON(args->local_nentries == 0);
397 DBUG_ON(args->remote_nentries == 0);
398
Robin Holta7665b02009-04-13 14:40:19 -0700399 ret = xpc_arch_ops.save_remote_msgqueue_pa(ch,
400 args->local_msgqueue_pa);
Jack Steiner6f2584f2009-04-02 16:59:10 -0700401 if (ret != xpSuccess) {
402 XPC_DISCONNECT_CHANNEL(ch, ret, &irq_flags);
Robin Holtefdd06e2009-04-13 14:40:19 -0700403 goto out;
Jack Steiner6f2584f2009-04-02 16:59:10 -0700404 }
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700405 ch->flags |= XPC_C_ROPENREPLY;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700406
407 if (args->local_nentries < ch->remote_nentries) {
Dean Nelson7fb5e592008-07-29 22:34:10 -0700408 dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new "
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700409 "remote_nentries=%d, old remote_nentries=%d, "
410 "partid=%d, channel=%d\n",
411 args->local_nentries, ch->remote_nentries,
412 ch->partid, ch->number);
413
414 ch->remote_nentries = args->local_nentries;
415 }
416 if (args->remote_nentries < ch->local_nentries) {
Dean Nelson7fb5e592008-07-29 22:34:10 -0700417 dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new "
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700418 "local_nentries=%d, old local_nentries=%d, "
419 "partid=%d, channel=%d\n",
420 args->remote_nentries, ch->local_nentries,
421 ch->partid, ch->number);
422
423 ch->local_nentries = args->remote_nentries;
424 }
425
426 xpc_process_connect(ch, &irq_flags);
427 }
428
Robin Holtefdd06e2009-04-13 14:40:19 -0700429 if (chctl_flags & XPC_CHCTL_OPENCOMPLETE) {
430
431 dev_dbg(xpc_chan, "XPC_CHCTL_OPENCOMPLETE received from "
432 "partid=%d, channel=%d\n", ch->partid, ch->number);
433
434 if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED))
435 goto out;
436
437 if (!(ch->flags & XPC_C_OPENREQUEST) ||
438 !(ch->flags & XPC_C_OPENREPLY)) {
439 XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError,
440 &irq_flags);
441 goto out;
442 }
443
444 DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST));
445 DBUG_ON(!(ch->flags & XPC_C_ROPENREPLY));
446 DBUG_ON(!(ch->flags & XPC_C_CONNECTED));
447
448 ch->flags |= XPC_C_ROPENCOMPLETE;
449
450 xpc_process_connect(ch, &irq_flags);
451 create_kthread = 1;
452 }
453
454out:
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700455 spin_unlock_irqrestore(&ch->lock, irq_flags);
Robin Holtefdd06e2009-04-13 14:40:19 -0700456
457 if (create_kthread)
458 xpc_create_kthreads(ch, 1, 0);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700459}
460
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700461/*
462 * Attempt to establish a channel connection to a remote partition.
463 */
Dean Nelson65c17b82008-05-12 14:02:02 -0700464static enum xp_retval
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700465xpc_connect_channel(struct xpc_channel *ch)
466{
467 unsigned long irq_flags;
468 struct xpc_registration *registration = &xpc_registrations[ch->number];
469
Dean Nelson2c2b94f2008-04-22 14:50:17 -0500470 if (mutex_trylock(&registration->mutex) == 0)
Dean Nelson65c17b82008-05-12 14:02:02 -0700471 return xpRetry;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700472
473 if (!XPC_CHANNEL_REGISTERED(ch->number)) {
Jes Sorensenf9e505a2006-01-17 12:52:21 -0500474 mutex_unlock(&registration->mutex);
Dean Nelson65c17b82008-05-12 14:02:02 -0700475 return xpUnregistered;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700476 }
477
478 spin_lock_irqsave(&ch->lock, irq_flags);
479
480 DBUG_ON(ch->flags & XPC_C_CONNECTED);
481 DBUG_ON(ch->flags & XPC_C_OPENREQUEST);
482
483 if (ch->flags & XPC_C_DISCONNECTING) {
484 spin_unlock_irqrestore(&ch->lock, irq_flags);
Jes Sorensenf9e505a2006-01-17 12:52:21 -0500485 mutex_unlock(&registration->mutex);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700486 return ch->reason;
487 }
488
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700489 /* add info from the channel connect registration to the channel */
490
491 ch->kthreads_assigned_limit = registration->assigned_limit;
492 ch->kthreads_idle_limit = registration->idle_limit;
493 DBUG_ON(atomic_read(&ch->kthreads_assigned) != 0);
494 DBUG_ON(atomic_read(&ch->kthreads_idle) != 0);
495 DBUG_ON(atomic_read(&ch->kthreads_active) != 0);
496
497 ch->func = registration->func;
498 DBUG_ON(registration->func == NULL);
499 ch->key = registration->key;
500
501 ch->local_nentries = registration->nentries;
502
503 if (ch->flags & XPC_C_ROPENREQUEST) {
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700504 if (registration->entry_size != ch->entry_size) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700505 /* the local and remote sides aren't the same */
506
507 /*
508 * Because XPC_DISCONNECT_CHANNEL() can block we're
509 * forced to up the registration sema before we unlock
510 * the channel lock. But that's okay here because we're
511 * done with the part that required the registration
512 * sema. XPC_DISCONNECT_CHANNEL() requires that the
513 * channel lock be locked and will unlock and relock
514 * the channel lock as needed.
515 */
Jes Sorensenf9e505a2006-01-17 12:52:21 -0500516 mutex_unlock(&registration->mutex);
Dean Nelson65c17b82008-05-12 14:02:02 -0700517 XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes,
Dean Nelson35190502008-04-22 14:48:55 -0500518 &irq_flags);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700519 spin_unlock_irqrestore(&ch->lock, irq_flags);
Dean Nelson65c17b82008-05-12 14:02:02 -0700520 return xpUnequalMsgSizes;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700521 }
522 } else {
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700523 ch->entry_size = registration->entry_size;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700524
525 XPC_SET_REASON(ch, 0, 0);
526 ch->flags &= ~XPC_C_DISCONNECTED;
527
528 atomic_inc(&xpc_partitions[ch->partid].nchannels_active);
529 }
530
Jes Sorensenf9e505a2006-01-17 12:52:21 -0500531 mutex_unlock(&registration->mutex);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700532
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700533 /* initiate the connection */
534
535 ch->flags |= (XPC_C_OPENREQUEST | XPC_C_CONNECTING);
Robin Holta7665b02009-04-13 14:40:19 -0700536 xpc_arch_ops.send_chctl_openrequest(ch, &irq_flags);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700537
538 xpc_process_connect(ch, &irq_flags);
539
540 spin_unlock_irqrestore(&ch->lock, irq_flags);
541
Dean Nelson65c17b82008-05-12 14:02:02 -0700542 return xpSuccess;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700543}
544
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700545void
Dean Nelson7fb5e592008-07-29 22:34:10 -0700546xpc_process_sent_chctl_flags(struct xpc_partition *part)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700547{
548 unsigned long irq_flags;
Dean Nelson7fb5e592008-07-29 22:34:10 -0700549 union xpc_channel_ctl_flags chctl;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700550 struct xpc_channel *ch;
551 int ch_number;
Dean Nelsona607c382005-09-01 14:01:37 -0500552 u32 ch_flags;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700553
Robin Holta7665b02009-04-13 14:40:19 -0700554 chctl.all_flags = xpc_arch_ops.get_chctl_all_flags(part);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700555
556 /*
557 * Initiate channel connections for registered channels.
558 *
559 * For each connected channel that has pending messages activate idle
560 * kthreads and/or create new kthreads as needed.
561 */
562
563 for (ch_number = 0; ch_number < part->nchannels; ch_number++) {
564 ch = &part->channels[ch_number];
565
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700566 /*
Dean Nelson7fb5e592008-07-29 22:34:10 -0700567 * Process any open or close related chctl flags, and then deal
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700568 * with connecting or disconnecting the channel as required.
569 */
570
Dean Nelson7fb5e592008-07-29 22:34:10 -0700571 if (chctl.flags[ch_number] & XPC_OPENCLOSE_CHCTL_FLAGS) {
572 xpc_process_openclose_chctl_flags(part, ch_number,
573 chctl.flags[ch_number]);
574 }
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700575
Dean Nelsona607c382005-09-01 14:01:37 -0500576 ch_flags = ch->flags; /* need an atomic snapshot of flags */
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700577
Dean Nelsona607c382005-09-01 14:01:37 -0500578 if (ch_flags & XPC_C_DISCONNECTING) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700579 spin_lock_irqsave(&ch->lock, irq_flags);
580 xpc_process_disconnect(ch, &irq_flags);
581 spin_unlock_irqrestore(&ch->lock, irq_flags);
582 continue;
583 }
584
Dean Nelson83469b52008-07-29 22:34:18 -0700585 if (part->act_state == XPC_P_AS_DEACTIVATING)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700586 continue;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700587
Dean Nelsona607c382005-09-01 14:01:37 -0500588 if (!(ch_flags & XPC_C_CONNECTED)) {
589 if (!(ch_flags & XPC_C_OPENREQUEST)) {
590 DBUG_ON(ch_flags & XPC_C_SETUP);
Dean Nelson35190502008-04-22 14:48:55 -0500591 (void)xpc_connect_channel(ch);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700592 }
593 continue;
594 }
595
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700596 /*
Dean Nelson7fb5e592008-07-29 22:34:10 -0700597 * Process any message related chctl flags, this may involve
598 * the activation of kthreads to deliver any pending messages
599 * sent from the other partition.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700600 */
601
Dean Nelson7fb5e592008-07-29 22:34:10 -0700602 if (chctl.flags[ch_number] & XPC_MSG_CHCTL_FLAGS)
Robin Holta7665b02009-04-13 14:40:19 -0700603 xpc_arch_ops.process_msg_chctl_flags(part, ch_number);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700604 }
605}
606
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700607/*
Dean Nelsona607c382005-09-01 14:01:37 -0500608 * XPC's heartbeat code calls this function to inform XPC that a partition is
609 * going down. XPC responds by tearing down the XPartition Communication
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700610 * infrastructure used for the just downed partition.
611 *
612 * XPC's heartbeat code will never call this function and xpc_partition_up()
613 * at the same time. Nor will it ever make multiple calls to either function
614 * at the same time.
615 */
616void
Dean Nelson65c17b82008-05-12 14:02:02 -0700617xpc_partition_going_down(struct xpc_partition *part, enum xp_retval reason)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700618{
619 unsigned long irq_flags;
620 int ch_number;
621 struct xpc_channel *ch;
622
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700623 dev_dbg(xpc_chan, "deactivating partition %d, reason=%d\n",
624 XPC_PARTID(part), reason);
625
626 if (!xpc_part_ref(part)) {
627 /* infrastructure for this partition isn't currently set up */
628 return;
629 }
630
Dean Nelsona607c382005-09-01 14:01:37 -0500631 /* disconnect channels associated with the partition going down */
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700632
633 for (ch_number = 0; ch_number < part->nchannels; ch_number++) {
634 ch = &part->channels[ch_number];
635
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700636 xpc_msgqueue_ref(ch);
637 spin_lock_irqsave(&ch->lock, irq_flags);
638
639 XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags);
640
641 spin_unlock_irqrestore(&ch->lock, irq_flags);
642 xpc_msgqueue_deref(ch);
643 }
644
645 xpc_wakeup_channel_mgr(part);
646
647 xpc_part_deref(part);
648}
649
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700650/*
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700651 * Called by XP at the time of channel connection registration to cause
652 * XPC to establish connections to all currently active partitions.
653 */
654void
655xpc_initiate_connect(int ch_number)
656{
Dean Nelson64d032b2008-05-12 14:02:03 -0700657 short partid;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700658 struct xpc_partition *part;
659 struct xpc_channel *ch;
660
Dean Nelsonbc63d382008-07-29 22:34:04 -0700661 DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700662
Dean Nelsonbc63d382008-07-29 22:34:04 -0700663 for (partid = 0; partid < xp_max_npartitions; partid++) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700664 part = &xpc_partitions[partid];
665
666 if (xpc_part_ref(part)) {
667 ch = &part->channels[ch_number];
668
Dean Nelsone54af722005-10-25 14:07:43 -0500669 /*
670 * Initiate the establishment of a connection on the
671 * newly registered channel to the remote partition.
672 */
673 xpc_wakeup_channel_mgr(part);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700674 xpc_part_deref(part);
675 }
676 }
677}
678
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700679void
680xpc_connected_callout(struct xpc_channel *ch)
681{
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700682 /* let the registerer know that a connection has been established */
683
684 if (ch->func != NULL) {
Dean Nelson65c17b82008-05-12 14:02:02 -0700685 dev_dbg(xpc_chan, "ch->func() called, reason=xpConnected, "
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700686 "partid=%d, channel=%d\n", ch->partid, ch->number);
687
Dean Nelson65c17b82008-05-12 14:02:02 -0700688 ch->func(xpConnected, ch->partid, ch->number,
Dean Nelson35190502008-04-22 14:48:55 -0500689 (void *)(u64)ch->local_nentries, ch->key);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700690
Dean Nelson65c17b82008-05-12 14:02:02 -0700691 dev_dbg(xpc_chan, "ch->func() returned, reason=xpConnected, "
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700692 "partid=%d, channel=%d\n", ch->partid, ch->number);
693 }
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700694}
695
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700696/*
697 * Called by XP at the time of channel connection unregistration to cause
698 * XPC to teardown all current connections for the specified channel.
699 *
700 * Before returning xpc_initiate_disconnect() will wait until all connections
701 * on the specified channel have been closed/torndown. So the caller can be
702 * assured that they will not be receiving any more callouts from XPC to the
703 * function they registered via xpc_connect().
704 *
705 * Arguments:
706 *
707 * ch_number - channel # to unregister.
708 */
709void
710xpc_initiate_disconnect(int ch_number)
711{
712 unsigned long irq_flags;
Dean Nelson64d032b2008-05-12 14:02:03 -0700713 short partid;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700714 struct xpc_partition *part;
715 struct xpc_channel *ch;
716
Dean Nelsonbc63d382008-07-29 22:34:04 -0700717 DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700718
719 /* initiate the channel disconnect for every active partition */
Dean Nelsonbc63d382008-07-29 22:34:04 -0700720 for (partid = 0; partid < xp_max_npartitions; partid++) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700721 part = &xpc_partitions[partid];
722
723 if (xpc_part_ref(part)) {
724 ch = &part->channels[ch_number];
725 xpc_msgqueue_ref(ch);
726
727 spin_lock_irqsave(&ch->lock, irq_flags);
728
Dean Nelsona607c382005-09-01 14:01:37 -0500729 if (!(ch->flags & XPC_C_DISCONNECTED)) {
730 ch->flags |= XPC_C_WDISCONNECT;
731
Dean Nelson65c17b82008-05-12 14:02:02 -0700732 XPC_DISCONNECT_CHANNEL(ch, xpUnregistering,
Dean Nelson35190502008-04-22 14:48:55 -0500733 &irq_flags);
Dean Nelsona607c382005-09-01 14:01:37 -0500734 }
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700735
736 spin_unlock_irqrestore(&ch->lock, irq_flags);
737
738 xpc_msgqueue_deref(ch);
739 xpc_part_deref(part);
740 }
741 }
742
743 xpc_disconnect_wait(ch_number);
744}
745
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700746/*
747 * To disconnect a channel, and reflect it back to all who may be waiting.
748 *
Dean Nelsona607c382005-09-01 14:01:37 -0500749 * An OPEN is not allowed until XPC_C_DISCONNECTING is cleared by
750 * xpc_process_disconnect(), and if set, XPC_C_WDISCONNECT is cleared by
751 * xpc_disconnect_wait().
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700752 *
753 * THE CHANNEL IS TO BE LOCKED BY THE CALLER AND WILL REMAIN LOCKED UPON RETURN.
754 */
755void
756xpc_disconnect_channel(const int line, struct xpc_channel *ch,
Dean Nelson65c17b82008-05-12 14:02:02 -0700757 enum xp_retval reason, unsigned long *irq_flags)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700758{
Dean Nelsona607c382005-09-01 14:01:37 -0500759 u32 channel_was_connected = (ch->flags & XPC_C_CONNECTED);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700760
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700761 DBUG_ON(!spin_is_locked(&ch->lock));
762
Dean Nelson2c2b94f2008-04-22 14:50:17 -0500763 if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED))
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700764 return;
Dean Nelson2c2b94f2008-04-22 14:50:17 -0500765
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700766 DBUG_ON(!(ch->flags & (XPC_C_CONNECTING | XPC_C_CONNECTED)));
767
768 dev_dbg(xpc_chan, "reason=%d, line=%d, partid=%d, channel=%d\n",
769 reason, line, ch->partid, ch->number);
770
771 XPC_SET_REASON(ch, reason, line);
772
Dean Nelsona607c382005-09-01 14:01:37 -0500773 ch->flags |= (XPC_C_CLOSEREQUEST | XPC_C_DISCONNECTING);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700774 /* some of these may not have been set */
775 ch->flags &= ~(XPC_C_OPENREQUEST | XPC_C_OPENREPLY |
Dean Nelson35190502008-04-22 14:48:55 -0500776 XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY |
777 XPC_C_CONNECTING | XPC_C_CONNECTED);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700778
Robin Holta7665b02009-04-13 14:40:19 -0700779 xpc_arch_ops.send_chctl_closerequest(ch, irq_flags);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700780
Dean Nelson2c2b94f2008-04-22 14:50:17 -0500781 if (channel_was_connected)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700782 ch->flags |= XPC_C_WASCONNECTED;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700783
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700784 spin_unlock_irqrestore(&ch->lock, *irq_flags);
785
Dean Nelsona607c382005-09-01 14:01:37 -0500786 /* wake all idle kthreads so they can exit */
787 if (atomic_read(&ch->kthreads_idle) > 0) {
788 wake_up_all(&ch->idle_wq);
Dean Nelsona460ef82006-11-22 08:25:00 -0600789
790 } else if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) &&
Dean Nelson35190502008-04-22 14:48:55 -0500791 !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) {
Dean Nelson65c17b82008-05-12 14:02:02 -0700792 /* start a kthread that will do the xpDisconnecting callout */
Dean Nelsona460ef82006-11-22 08:25:00 -0600793 xpc_create_kthreads(ch, 1, 1);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700794 }
795
Dean Nelsona607c382005-09-01 14:01:37 -0500796 /* wake those waiting to allocate an entry from the local msg queue */
Dean Nelson2c2b94f2008-04-22 14:50:17 -0500797 if (atomic_read(&ch->n_on_msg_allocate_wq) > 0)
Dean Nelsona607c382005-09-01 14:01:37 -0500798 wake_up(&ch->msg_allocate_wq);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700799
800 spin_lock_irqsave(&ch->lock, *irq_flags);
801}
802
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700803void
Dean Nelson65c17b82008-05-12 14:02:02 -0700804xpc_disconnect_callout(struct xpc_channel *ch, enum xp_retval reason)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700805{
806 /*
Dean Nelsona607c382005-09-01 14:01:37 -0500807 * Let the channel's registerer know that the channel is being
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700808 * disconnected. We don't want to do this if the registerer was never
Dean Nelsona607c382005-09-01 14:01:37 -0500809 * informed of a connection being made.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700810 */
811
812 if (ch->func != NULL) {
Dean Nelson246c7e32005-12-22 14:32:56 -0600813 dev_dbg(xpc_chan, "ch->func() called, reason=%d, partid=%d, "
814 "channel=%d\n", reason, ch->partid, ch->number);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700815
Dean Nelson246c7e32005-12-22 14:32:56 -0600816 ch->func(reason, ch->partid, ch->number, NULL, ch->key);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700817
Dean Nelson246c7e32005-12-22 14:32:56 -0600818 dev_dbg(xpc_chan, "ch->func() returned, reason=%d, partid=%d, "
819 "channel=%d\n", reason, ch->partid, ch->number);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700820 }
821}
822
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700823/*
824 * Wait for a message entry to become available for the specified channel,
825 * but don't wait any longer than 1 jiffy.
826 */
Dean Nelson33ba3c72008-07-29 22:34:07 -0700827enum xp_retval
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700828xpc_allocate_msg_wait(struct xpc_channel *ch)
829{
Dean Nelson65c17b82008-05-12 14:02:02 -0700830 enum xp_retval ret;
Arnd Bergmann11d5ceb2014-01-02 13:07:52 +0100831 DEFINE_WAIT(wait);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700832
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700833 if (ch->flags & XPC_C_DISCONNECTING) {
Dean Nelson65c17b82008-05-12 14:02:02 -0700834 DBUG_ON(ch->reason == xpInterrupted);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700835 return ch->reason;
836 }
837
838 atomic_inc(&ch->n_on_msg_allocate_wq);
Arnd Bergmann11d5ceb2014-01-02 13:07:52 +0100839 prepare_to_wait(&ch->msg_allocate_wq, &wait, TASK_INTERRUPTIBLE);
840 ret = schedule_timeout(1);
841 finish_wait(&ch->msg_allocate_wq, &wait);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700842 atomic_dec(&ch->n_on_msg_allocate_wq);
843
844 if (ch->flags & XPC_C_DISCONNECTING) {
845 ret = ch->reason;
Dean Nelson65c17b82008-05-12 14:02:02 -0700846 DBUG_ON(ch->reason == xpInterrupted);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700847 } else if (ret == 0) {
Dean Nelson65c17b82008-05-12 14:02:02 -0700848 ret = xpTimeout;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700849 } else {
Dean Nelson65c17b82008-05-12 14:02:02 -0700850 ret = xpInterrupted;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700851 }
852
853 return ret;
854}
855
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700856/*
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700857 * Send a message that contains the user's payload on the specified channel
858 * connected to the specified partition.
859 *
860 * NOTE that this routine can sleep waiting for a message entry to become
861 * available. To not sleep, pass in the XPC_NOWAIT flag.
862 *
863 * Once sent, this routine will not wait for the message to be received, nor
864 * will notification be given when it does happen.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700865 *
866 * Arguments:
867 *
868 * partid - ID of partition to which the channel is connected.
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700869 * ch_number - channel # to send message on.
870 * flags - see xp.h for valid flags.
871 * payload - pointer to the payload which is to be sent.
872 * payload_size - size of the payload in bytes.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700873 */
Dean Nelson65c17b82008-05-12 14:02:02 -0700874enum xp_retval
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700875xpc_initiate_send(short partid, int ch_number, u32 flags, void *payload,
876 u16 payload_size)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700877{
878 struct xpc_partition *part = &xpc_partitions[partid];
Dean Nelson65c17b82008-05-12 14:02:02 -0700879 enum xp_retval ret = xpUnknownReason;
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700880
881 dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload,
882 partid, ch_number);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700883
Dean Nelsonbc63d382008-07-29 22:34:04 -0700884 DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700885 DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700886 DBUG_ON(payload == NULL);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700887
888 if (xpc_part_ref(part)) {
Robin Holta7665b02009-04-13 14:40:19 -0700889 ret = xpc_arch_ops.send_payload(&part->channels[ch_number],
890 flags, payload, payload_size, 0, NULL, NULL);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700891 xpc_part_deref(part);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700892 }
893
894 return ret;
895}
896
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700897/*
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700898 * Send a message that contains the user's payload on the specified channel
899 * connected to the specified partition.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700900 *
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700901 * NOTE that this routine can sleep waiting for a message entry to become
902 * available. To not sleep, pass in the XPC_NOWAIT flag.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700903 *
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700904 * This routine will not wait for the message to be sent or received.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700905 *
906 * Once the remote end of the channel has received the message, the function
907 * passed as an argument to xpc_initiate_send_notify() will be called. This
908 * allows the sender to free up or re-use any buffers referenced by the
909 * message, but does NOT mean the message has been processed at the remote
910 * end by a receiver.
911 *
912 * If this routine returns an error, the caller's function will NOT be called.
913 *
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700914 * Arguments:
915 *
916 * partid - ID of partition to which the channel is connected.
917 * ch_number - channel # to send message on.
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700918 * flags - see xp.h for valid flags.
919 * payload - pointer to the payload which is to be sent.
920 * payload_size - size of the payload in bytes.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700921 * func - function to call with asynchronous notification of message
922 * receipt. THIS FUNCTION MUST BE NON-BLOCKING.
923 * key - user-defined key to be passed to the function when it's called.
924 */
Dean Nelson65c17b82008-05-12 14:02:02 -0700925enum xp_retval
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700926xpc_initiate_send_notify(short partid, int ch_number, u32 flags, void *payload,
927 u16 payload_size, xpc_notify_func func, void *key)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700928{
929 struct xpc_partition *part = &xpc_partitions[partid];
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700930 enum xp_retval ret = xpUnknownReason;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700931
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700932 dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload,
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700933 partid, ch_number);
934
Dean Nelsonbc63d382008-07-29 22:34:04 -0700935 DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700936 DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700937 DBUG_ON(payload == NULL);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700938 DBUG_ON(func == NULL);
939
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700940 if (xpc_part_ref(part)) {
Robin Holta7665b02009-04-13 14:40:19 -0700941 ret = xpc_arch_ops.send_payload(&part->channels[ch_number],
942 flags, payload, payload_size, XPC_N_CALL, func, key);
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700943 xpc_part_deref(part);
944 }
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700945 return ret;
946}
947
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700948/*
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700949 * Deliver a message's payload to its intended recipient.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700950 */
951void
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700952xpc_deliver_payload(struct xpc_channel *ch)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700953{
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700954 void *payload;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700955
Robin Holta7665b02009-04-13 14:40:19 -0700956 payload = xpc_arch_ops.get_deliverable_payload(ch);
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700957 if (payload != NULL) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700958
959 /*
960 * This ref is taken to protect the payload itself from being
961 * freed before the user is finished with it, which the user
962 * indicates by calling xpc_initiate_received().
963 */
964 xpc_msgqueue_ref(ch);
965
966 atomic_inc(&ch->kthreads_active);
967
968 if (ch->func != NULL) {
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700969 dev_dbg(xpc_chan, "ch->func() called, payload=0x%p "
970 "partid=%d channel=%d\n", payload, ch->partid,
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700971 ch->number);
972
973 /* deliver the message to its intended recipient */
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700974 ch->func(xpMsgReceived, ch->partid, ch->number, payload,
975 ch->key);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700976
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700977 dev_dbg(xpc_chan, "ch->func() returned, payload=0x%p "
978 "partid=%d channel=%d\n", payload, ch->partid,
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700979 ch->number);
980 }
981
982 atomic_dec(&ch->kthreads_active);
983 }
984}
985
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700986/*
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700987 * Acknowledge receipt of a delivered message's payload.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700988 *
989 * This function, although called by users, does not call xpc_part_ref() to
990 * ensure that the partition infrastructure is in place. It relies on the
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700991 * fact that we called xpc_msgqueue_ref() in xpc_deliver_payload().
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700992 *
993 * Arguments:
994 *
995 * partid - ID of partition to which the channel is connected.
996 * ch_number - channel # message received on.
997 * payload - pointer to the payload area allocated via
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700998 * xpc_initiate_send() or xpc_initiate_send_notify().
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700999 */
1000void
Dean Nelson64d032b2008-05-12 14:02:03 -07001001xpc_initiate_received(short partid, int ch_number, void *payload)
Dean Nelson89eb8eb2005-03-23 19:50:00 -07001002{
1003 struct xpc_partition *part = &xpc_partitions[partid];
1004 struct xpc_channel *ch;
Dean Nelson89eb8eb2005-03-23 19:50:00 -07001005
Dean Nelsonbc63d382008-07-29 22:34:04 -07001006 DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
Dean Nelson89eb8eb2005-03-23 19:50:00 -07001007 DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
1008
1009 ch = &part->channels[ch_number];
Robin Holta7665b02009-04-13 14:40:19 -07001010 xpc_arch_ops.received_payload(ch, payload);
Dean Nelson89eb8eb2005-03-23 19:50:00 -07001011
Dean Nelsonbd3e64c2008-07-29 22:34:19 -07001012 /* the call to xpc_msgqueue_ref() was done by xpc_deliver_payload() */
Dean Nelson89eb8eb2005-03-23 19:50:00 -07001013 xpc_msgqueue_deref(ch);
1014}