blob: 2eb3abff0e3a82d5035bd7f8c2830f1cf9ec945d [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);
Dean Nelson5b8669d2008-07-29 22:34:18 -070042 ret = xpc_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;
Dean Nelson7fb5e592008-07-29 22:34:10 -070056 xpc_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);
64 xpc_send_chctl_opencomplete(ch, irq_flags);
65 }
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 Nelsona607c3892005-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 Nelsona607c3892005-09-01 14:01:37 -0500102 /* can't proceed until the other side disengages from us */
Dean Nelsona47d5da2008-07-29 22:34:09 -0700103 if (xpc_partition_engaged(ch->partid))
Dean Nelsona607c3892005-09-01 14:01:37 -0500104 return;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700105
Dean Nelsona607c3892005-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;
Dean Nelson7fb5e592008-07-29 22:34:10 -0700115 xpc_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 Nelsona607c3892005-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 */
Dean Nelsona47d5da2008-07-29 22:34:09 -0700125 xpc_notify_senders_of_disconnect(ch);
Dean Nelsona607c3892005-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 Nelsona607c3892005-09-01 14:01:37 -0500138 /* it's now safe to free the channel's message queues */
Dean Nelson5b8669d2008-07-29 22:34:18 -0700139 xpc_teardown_msg_structures(ch);
140
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 Nelsona607c3892005-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
Dean Nelson5b8669d2008-07-29 22:34:18 -0700151 * XPC_C_SETUP (because of call to xpc_teardown_msg_structures()) but
152 * not including XPC_C_WDISCONNECT (if it was set).
Dean Nelson185c3a12008-07-29 22:34:11 -0700153 */
Dean Nelsona607c3892005-09-01 14:01:37 -0500154 ch->flags = (XPC_C_DISCONNECTED | (ch->flags & XPC_C_WDISCONNECT));
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700155
156 atomic_dec(&part->nchannels_active);
157
Dean Nelsona607c3892005-09-01 14:01:37 -0500158 if (channel_was_connected) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700159 dev_info(xpc_chan, "channel %d to partition %d disconnected, "
Dean Nelson35190502008-04-22 14:48:55 -0500160 "reason=%d\n", ch->number, ch->partid, ch->reason);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700161 }
Dean Nelsona607c3892005-09-01 14:01:37 -0500162
Dean Nelsona607c3892005-09-01 14:01:37 -0500163 if (ch->flags & XPC_C_WDISCONNECT) {
Jes Sorensenf9e505a2006-01-17 12:52:21 -0500164 /* we won't lose the CPU since we're holding ch->lock */
165 complete(&ch->wdisconnect_wait);
Dean Nelson7fb5e592008-07-29 22:34:10 -0700166 } else if (ch->delayed_chctl_flags) {
Dean Nelson83469b52008-07-29 22:34:18 -0700167 if (part->act_state != XPC_P_AS_DEACTIVATING) {
Dean Nelson7fb5e592008-07-29 22:34:10 -0700168 /* time to take action on any delayed chctl flags */
169 spin_lock(&part->chctl_lock);
170 part->chctl.flags[ch->number] |=
171 ch->delayed_chctl_flags;
172 spin_unlock(&part->chctl_lock);
Dean Nelsone54af722005-10-25 14:07:43 -0500173 }
Dean Nelson7fb5e592008-07-29 22:34:10 -0700174 ch->delayed_chctl_flags = 0;
Dean Nelsona607c3892005-09-01 14:01:37 -0500175 }
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700176}
177
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700178/*
179 * Process a change in the channel's remote connection state.
180 */
181static void
Dean Nelson7fb5e592008-07-29 22:34:10 -0700182xpc_process_openclose_chctl_flags(struct xpc_partition *part, int ch_number,
183 u8 chctl_flags)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700184{
185 unsigned long irq_flags;
186 struct xpc_openclose_args *args =
Dean Nelson35190502008-04-22 14:48:55 -0500187 &part->remote_openclose_args[ch_number];
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700188 struct xpc_channel *ch = &part->channels[ch_number];
Dean Nelson65c17b82008-05-12 14:02:02 -0700189 enum xp_retval reason;
Jack Steiner6f2584f2009-04-02 16:59:10 -0700190 enum xp_retval ret;
Robin Holtefdd06e2009-04-13 14:40:19 -0700191 int create_kthread = 0;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700192
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700193 spin_lock_irqsave(&ch->lock, irq_flags);
194
Dean Nelson2c2b94f2008-04-22 14:50:17 -0500195again:
Dean Nelsone54af722005-10-25 14:07:43 -0500196
Dean Nelson2c2b94f2008-04-22 14:50:17 -0500197 if ((ch->flags & XPC_C_DISCONNECTED) &&
198 (ch->flags & XPC_C_WDISCONNECT)) {
Dean Nelsone54af722005-10-25 14:07:43 -0500199 /*
Dean Nelson7fb5e592008-07-29 22:34:10 -0700200 * Delay processing chctl flags until thread waiting disconnect
Dean Nelsone54af722005-10-25 14:07:43 -0500201 * has had a chance to see that the channel is disconnected.
202 */
Dean Nelson7fb5e592008-07-29 22:34:10 -0700203 ch->delayed_chctl_flags |= chctl_flags;
Robin Holtefdd06e2009-04-13 14:40:19 -0700204 goto out;
Dean Nelsone54af722005-10-25 14:07:43 -0500205 }
206
Dean Nelson7fb5e592008-07-29 22:34:10 -0700207 if (chctl_flags & XPC_CHCTL_CLOSEREQUEST) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700208
Dean Nelson7fb5e592008-07-29 22:34:10 -0700209 dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREQUEST (reason=%d) received "
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700210 "from partid=%d, channel=%d\n", args->reason,
211 ch->partid, ch->number);
212
213 /*
214 * If RCLOSEREQUEST is set, we're probably waiting for
215 * RCLOSEREPLY. We should find it and a ROPENREQUEST packed
Dean Nelson7fb5e592008-07-29 22:34:10 -0700216 * with this RCLOSEREQUEST in the chctl_flags.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700217 */
218
219 if (ch->flags & XPC_C_RCLOSEREQUEST) {
220 DBUG_ON(!(ch->flags & XPC_C_DISCONNECTING));
221 DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));
222 DBUG_ON(!(ch->flags & XPC_C_CLOSEREPLY));
223 DBUG_ON(ch->flags & XPC_C_RCLOSEREPLY);
224
Dean Nelson7fb5e592008-07-29 22:34:10 -0700225 DBUG_ON(!(chctl_flags & XPC_CHCTL_CLOSEREPLY));
226 chctl_flags &= ~XPC_CHCTL_CLOSEREPLY;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700227 ch->flags |= XPC_C_RCLOSEREPLY;
228
229 /* both sides have finished disconnecting */
230 xpc_process_disconnect(ch, &irq_flags);
Dean Nelsone54af722005-10-25 14:07:43 -0500231 DBUG_ON(!(ch->flags & XPC_C_DISCONNECTED));
232 goto again;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700233 }
234
235 if (ch->flags & XPC_C_DISCONNECTED) {
Dean Nelson7fb5e592008-07-29 22:34:10 -0700236 if (!(chctl_flags & XPC_CHCTL_OPENREQUEST)) {
237 if (part->chctl.flags[ch_number] &
238 XPC_CHCTL_OPENREQUEST) {
Dean Nelsone54af722005-10-25 14:07:43 -0500239
Dean Nelson7fb5e592008-07-29 22:34:10 -0700240 DBUG_ON(ch->delayed_chctl_flags != 0);
241 spin_lock(&part->chctl_lock);
242 part->chctl.flags[ch_number] |=
243 XPC_CHCTL_CLOSEREQUEST;
244 spin_unlock(&part->chctl_lock);
Dean Nelsone54af722005-10-25 14:07:43 -0500245 }
Robin Holtefdd06e2009-04-13 14:40:19 -0700246 goto out;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700247 }
248
249 XPC_SET_REASON(ch, 0, 0);
250 ch->flags &= ~XPC_C_DISCONNECTED;
251
252 atomic_inc(&part->nchannels_active);
253 ch->flags |= (XPC_C_CONNECTING | XPC_C_ROPENREQUEST);
254 }
255
Robin Holtefdd06e2009-04-13 14:40:19 -0700256 chctl_flags &= ~(XPC_CHCTL_OPENREQUEST | XPC_CHCTL_OPENREPLY |
257 XPC_CHCTL_OPENCOMPLETE);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700258
259 /*
260 * The meaningful CLOSEREQUEST connection state fields are:
261 * reason = reason connection is to be closed
262 */
263
264 ch->flags |= XPC_C_RCLOSEREQUEST;
265
266 if (!(ch->flags & XPC_C_DISCONNECTING)) {
267 reason = args->reason;
Dean Nelson65c17b82008-05-12 14:02:02 -0700268 if (reason <= xpSuccess || reason > xpUnknownReason)
269 reason = xpUnknownReason;
270 else if (reason == xpUnregistering)
271 reason = xpOtherUnregistering;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700272
273 XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags);
Dean Nelsone54af722005-10-25 14:07:43 -0500274
Dean Nelson7fb5e592008-07-29 22:34:10 -0700275 DBUG_ON(chctl_flags & XPC_CHCTL_CLOSEREPLY);
Robin Holtefdd06e2009-04-13 14:40:19 -0700276 goto out;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700277 }
Dean Nelsone54af722005-10-25 14:07:43 -0500278
279 xpc_process_disconnect(ch, &irq_flags);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700280 }
281
Dean Nelson7fb5e592008-07-29 22:34:10 -0700282 if (chctl_flags & XPC_CHCTL_CLOSEREPLY) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700283
Dean Nelson7fb5e592008-07-29 22:34:10 -0700284 dev_dbg(xpc_chan, "XPC_CHCTL_CLOSEREPLY received from partid="
285 "%d, channel=%d\n", ch->partid, ch->number);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700286
287 if (ch->flags & XPC_C_DISCONNECTED) {
Dean Nelson83469b52008-07-29 22:34:18 -0700288 DBUG_ON(part->act_state != XPC_P_AS_DEACTIVATING);
Robin Holtefdd06e2009-04-13 14:40:19 -0700289 goto out;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700290 }
291
292 DBUG_ON(!(ch->flags & XPC_C_CLOSEREQUEST));
Dean Nelsone54af722005-10-25 14:07:43 -0500293
294 if (!(ch->flags & XPC_C_RCLOSEREQUEST)) {
Dean Nelson7fb5e592008-07-29 22:34:10 -0700295 if (part->chctl.flags[ch_number] &
296 XPC_CHCTL_CLOSEREQUEST) {
Dean Nelsone54af722005-10-25 14:07:43 -0500297
Dean Nelson7fb5e592008-07-29 22:34:10 -0700298 DBUG_ON(ch->delayed_chctl_flags != 0);
299 spin_lock(&part->chctl_lock);
300 part->chctl.flags[ch_number] |=
301 XPC_CHCTL_CLOSEREPLY;
302 spin_unlock(&part->chctl_lock);
Dean Nelsone54af722005-10-25 14:07:43 -0500303 }
Robin Holtefdd06e2009-04-13 14:40:19 -0700304 goto out;
Dean Nelsone54af722005-10-25 14:07:43 -0500305 }
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700306
307 ch->flags |= XPC_C_RCLOSEREPLY;
308
309 if (ch->flags & XPC_C_CLOSEREPLY) {
310 /* both sides have finished disconnecting */
311 xpc_process_disconnect(ch, &irq_flags);
312 }
313 }
314
Dean Nelson7fb5e592008-07-29 22:34:10 -0700315 if (chctl_flags & XPC_CHCTL_OPENREQUEST) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700316
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700317 dev_dbg(xpc_chan, "XPC_CHCTL_OPENREQUEST (entry_size=%d, "
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700318 "local_nentries=%d) received from partid=%d, "
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700319 "channel=%d\n", args->entry_size, args->local_nentries,
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700320 ch->partid, ch->number);
321
Dean Nelson83469b52008-07-29 22:34:18 -0700322 if (part->act_state == XPC_P_AS_DEACTIVATING ||
Dean Nelson35190502008-04-22 14:48:55 -0500323 (ch->flags & XPC_C_ROPENREQUEST)) {
Robin Holtefdd06e2009-04-13 14:40:19 -0700324 goto out;
Dean Nelsone54af722005-10-25 14:07:43 -0500325 }
326
327 if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_WDISCONNECT)) {
Dean Nelson7fb5e592008-07-29 22:34:10 -0700328 ch->delayed_chctl_flags |= XPC_CHCTL_OPENREQUEST;
Robin Holtefdd06e2009-04-13 14:40:19 -0700329 goto out;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700330 }
331 DBUG_ON(!(ch->flags & (XPC_C_DISCONNECTED |
Dean Nelson35190502008-04-22 14:48:55 -0500332 XPC_C_OPENREQUEST)));
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700333 DBUG_ON(ch->flags & (XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY |
Dean Nelson35190502008-04-22 14:48:55 -0500334 XPC_C_OPENREPLY | XPC_C_CONNECTED));
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700335
336 /*
337 * The meaningful OPENREQUEST connection state fields are:
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700338 * entry_size = size of channel's messages in bytes
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700339 * local_nentries = remote partition's local_nentries
340 */
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700341 if (args->entry_size == 0 || args->local_nentries == 0) {
Dean Nelsone54af722005-10-25 14:07:43 -0500342 /* assume OPENREQUEST was delayed by mistake */
Robin Holtefdd06e2009-04-13 14:40:19 -0700343 goto out;
Dean Nelsone54af722005-10-25 14:07:43 -0500344 }
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700345
346 ch->flags |= (XPC_C_ROPENREQUEST | XPC_C_CONNECTING);
347 ch->remote_nentries = args->local_nentries;
348
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700349 if (ch->flags & XPC_C_OPENREQUEST) {
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700350 if (args->entry_size != ch->entry_size) {
Dean Nelson65c17b82008-05-12 14:02:02 -0700351 XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes,
Dean Nelson35190502008-04-22 14:48:55 -0500352 &irq_flags);
Robin Holtefdd06e2009-04-13 14:40:19 -0700353 goto out;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700354 }
355 } else {
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700356 ch->entry_size = args->entry_size;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700357
358 XPC_SET_REASON(ch, 0, 0);
359 ch->flags &= ~XPC_C_DISCONNECTED;
360
361 atomic_inc(&part->nchannels_active);
362 }
363
364 xpc_process_connect(ch, &irq_flags);
365 }
366
Dean Nelson7fb5e592008-07-29 22:34:10 -0700367 if (chctl_flags & XPC_CHCTL_OPENREPLY) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700368
Dean Nelson7fb5e592008-07-29 22:34:10 -0700369 dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY (local_msgqueue_pa="
370 "0x%lx, local_nentries=%d, remote_nentries=%d) "
371 "received from partid=%d, channel=%d\n",
Dean Nelsona812dcc2008-07-29 22:34:16 -0700372 args->local_msgqueue_pa, args->local_nentries,
373 args->remote_nentries, ch->partid, ch->number);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700374
Robin Holtefdd06e2009-04-13 14:40:19 -0700375 if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED))
376 goto out;
377
Dean Nelsone54af722005-10-25 14:07:43 -0500378 if (!(ch->flags & XPC_C_OPENREQUEST)) {
Dean Nelson65c17b82008-05-12 14:02:02 -0700379 XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError,
Dean Nelson35190502008-04-22 14:48:55 -0500380 &irq_flags);
Robin Holtefdd06e2009-04-13 14:40:19 -0700381 goto out;
Dean Nelsone54af722005-10-25 14:07:43 -0500382 }
383
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700384 DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST));
385 DBUG_ON(ch->flags & XPC_C_CONNECTED);
386
387 /*
388 * The meaningful OPENREPLY connection state fields are:
389 * local_msgqueue_pa = physical address of remote
Dean Nelson35190502008-04-22 14:48:55 -0500390 * partition's local_msgqueue
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700391 * local_nentries = remote partition's local_nentries
392 * remote_nentries = remote partition's remote_nentries
393 */
394 DBUG_ON(args->local_msgqueue_pa == 0);
395 DBUG_ON(args->local_nentries == 0);
396 DBUG_ON(args->remote_nentries == 0);
397
Jack Steiner6f2584f2009-04-02 16:59:10 -0700398 ret = xpc_save_remote_msgqueue_pa(ch, args->local_msgqueue_pa);
399 if (ret != xpSuccess) {
400 XPC_DISCONNECT_CHANNEL(ch, ret, &irq_flags);
Robin Holtefdd06e2009-04-13 14:40:19 -0700401 goto out;
Jack Steiner6f2584f2009-04-02 16:59:10 -0700402 }
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700403 ch->flags |= XPC_C_ROPENREPLY;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700404
405 if (args->local_nentries < ch->remote_nentries) {
Dean Nelson7fb5e592008-07-29 22:34:10 -0700406 dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new "
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700407 "remote_nentries=%d, old remote_nentries=%d, "
408 "partid=%d, channel=%d\n",
409 args->local_nentries, ch->remote_nentries,
410 ch->partid, ch->number);
411
412 ch->remote_nentries = args->local_nentries;
413 }
414 if (args->remote_nentries < ch->local_nentries) {
Dean Nelson7fb5e592008-07-29 22:34:10 -0700415 dev_dbg(xpc_chan, "XPC_CHCTL_OPENREPLY: new "
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700416 "local_nentries=%d, old local_nentries=%d, "
417 "partid=%d, channel=%d\n",
418 args->remote_nentries, ch->local_nentries,
419 ch->partid, ch->number);
420
421 ch->local_nentries = args->remote_nentries;
422 }
423
424 xpc_process_connect(ch, &irq_flags);
425 }
426
Robin Holtefdd06e2009-04-13 14:40:19 -0700427 if (chctl_flags & XPC_CHCTL_OPENCOMPLETE) {
428
429 dev_dbg(xpc_chan, "XPC_CHCTL_OPENCOMPLETE received from "
430 "partid=%d, channel=%d\n", ch->partid, ch->number);
431
432 if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED))
433 goto out;
434
435 if (!(ch->flags & XPC_C_OPENREQUEST) ||
436 !(ch->flags & XPC_C_OPENREPLY)) {
437 XPC_DISCONNECT_CHANNEL(ch, xpOpenCloseError,
438 &irq_flags);
439 goto out;
440 }
441
442 DBUG_ON(!(ch->flags & XPC_C_ROPENREQUEST));
443 DBUG_ON(!(ch->flags & XPC_C_ROPENREPLY));
444 DBUG_ON(!(ch->flags & XPC_C_CONNECTED));
445
446 ch->flags |= XPC_C_ROPENCOMPLETE;
447
448 xpc_process_connect(ch, &irq_flags);
449 create_kthread = 1;
450 }
451
452out:
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700453 spin_unlock_irqrestore(&ch->lock, irq_flags);
Robin Holtefdd06e2009-04-13 14:40:19 -0700454
455 if (create_kthread)
456 xpc_create_kthreads(ch, 1, 0);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700457}
458
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700459/*
460 * Attempt to establish a channel connection to a remote partition.
461 */
Dean Nelson65c17b82008-05-12 14:02:02 -0700462static enum xp_retval
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700463xpc_connect_channel(struct xpc_channel *ch)
464{
465 unsigned long irq_flags;
466 struct xpc_registration *registration = &xpc_registrations[ch->number];
467
Dean Nelson2c2b94f2008-04-22 14:50:17 -0500468 if (mutex_trylock(&registration->mutex) == 0)
Dean Nelson65c17b82008-05-12 14:02:02 -0700469 return xpRetry;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700470
471 if (!XPC_CHANNEL_REGISTERED(ch->number)) {
Jes Sorensenf9e505a2006-01-17 12:52:21 -0500472 mutex_unlock(&registration->mutex);
Dean Nelson65c17b82008-05-12 14:02:02 -0700473 return xpUnregistered;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700474 }
475
476 spin_lock_irqsave(&ch->lock, irq_flags);
477
478 DBUG_ON(ch->flags & XPC_C_CONNECTED);
479 DBUG_ON(ch->flags & XPC_C_OPENREQUEST);
480
481 if (ch->flags & XPC_C_DISCONNECTING) {
482 spin_unlock_irqrestore(&ch->lock, irq_flags);
Jes Sorensenf9e505a2006-01-17 12:52:21 -0500483 mutex_unlock(&registration->mutex);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700484 return ch->reason;
485 }
486
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700487 /* add info from the channel connect registration to the channel */
488
489 ch->kthreads_assigned_limit = registration->assigned_limit;
490 ch->kthreads_idle_limit = registration->idle_limit;
491 DBUG_ON(atomic_read(&ch->kthreads_assigned) != 0);
492 DBUG_ON(atomic_read(&ch->kthreads_idle) != 0);
493 DBUG_ON(atomic_read(&ch->kthreads_active) != 0);
494
495 ch->func = registration->func;
496 DBUG_ON(registration->func == NULL);
497 ch->key = registration->key;
498
499 ch->local_nentries = registration->nentries;
500
501 if (ch->flags & XPC_C_ROPENREQUEST) {
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700502 if (registration->entry_size != ch->entry_size) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700503 /* the local and remote sides aren't the same */
504
505 /*
506 * Because XPC_DISCONNECT_CHANNEL() can block we're
507 * forced to up the registration sema before we unlock
508 * the channel lock. But that's okay here because we're
509 * done with the part that required the registration
510 * sema. XPC_DISCONNECT_CHANNEL() requires that the
511 * channel lock be locked and will unlock and relock
512 * the channel lock as needed.
513 */
Jes Sorensenf9e505a2006-01-17 12:52:21 -0500514 mutex_unlock(&registration->mutex);
Dean Nelson65c17b82008-05-12 14:02:02 -0700515 XPC_DISCONNECT_CHANNEL(ch, xpUnequalMsgSizes,
Dean Nelson35190502008-04-22 14:48:55 -0500516 &irq_flags);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700517 spin_unlock_irqrestore(&ch->lock, irq_flags);
Dean Nelson65c17b82008-05-12 14:02:02 -0700518 return xpUnequalMsgSizes;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700519 }
520 } else {
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700521 ch->entry_size = registration->entry_size;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700522
523 XPC_SET_REASON(ch, 0, 0);
524 ch->flags &= ~XPC_C_DISCONNECTED;
525
526 atomic_inc(&xpc_partitions[ch->partid].nchannels_active);
527 }
528
Jes Sorensenf9e505a2006-01-17 12:52:21 -0500529 mutex_unlock(&registration->mutex);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700530
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700531 /* initiate the connection */
532
533 ch->flags |= (XPC_C_OPENREQUEST | XPC_C_CONNECTING);
Dean Nelson7fb5e592008-07-29 22:34:10 -0700534 xpc_send_chctl_openrequest(ch, &irq_flags);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700535
536 xpc_process_connect(ch, &irq_flags);
537
538 spin_unlock_irqrestore(&ch->lock, irq_flags);
539
Dean Nelson65c17b82008-05-12 14:02:02 -0700540 return xpSuccess;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700541}
542
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700543void
Dean Nelson7fb5e592008-07-29 22:34:10 -0700544xpc_process_sent_chctl_flags(struct xpc_partition *part)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700545{
546 unsigned long irq_flags;
Dean Nelson7fb5e592008-07-29 22:34:10 -0700547 union xpc_channel_ctl_flags chctl;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700548 struct xpc_channel *ch;
549 int ch_number;
Dean Nelsona607c3892005-09-01 14:01:37 -0500550 u32 ch_flags;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700551
Dean Nelson7fb5e592008-07-29 22:34:10 -0700552 chctl.all_flags = xpc_get_chctl_all_flags(part);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700553
554 /*
555 * Initiate channel connections for registered channels.
556 *
557 * For each connected channel that has pending messages activate idle
558 * kthreads and/or create new kthreads as needed.
559 */
560
561 for (ch_number = 0; ch_number < part->nchannels; ch_number++) {
562 ch = &part->channels[ch_number];
563
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700564 /*
Dean Nelson7fb5e592008-07-29 22:34:10 -0700565 * Process any open or close related chctl flags, and then deal
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700566 * with connecting or disconnecting the channel as required.
567 */
568
Dean Nelson7fb5e592008-07-29 22:34:10 -0700569 if (chctl.flags[ch_number] & XPC_OPENCLOSE_CHCTL_FLAGS) {
570 xpc_process_openclose_chctl_flags(part, ch_number,
571 chctl.flags[ch_number]);
572 }
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700573
Dean Nelsona607c3892005-09-01 14:01:37 -0500574 ch_flags = ch->flags; /* need an atomic snapshot of flags */
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700575
Dean Nelsona607c3892005-09-01 14:01:37 -0500576 if (ch_flags & XPC_C_DISCONNECTING) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700577 spin_lock_irqsave(&ch->lock, irq_flags);
578 xpc_process_disconnect(ch, &irq_flags);
579 spin_unlock_irqrestore(&ch->lock, irq_flags);
580 continue;
581 }
582
Dean Nelson83469b52008-07-29 22:34:18 -0700583 if (part->act_state == XPC_P_AS_DEACTIVATING)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700584 continue;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700585
Dean Nelsona607c3892005-09-01 14:01:37 -0500586 if (!(ch_flags & XPC_C_CONNECTED)) {
587 if (!(ch_flags & XPC_C_OPENREQUEST)) {
588 DBUG_ON(ch_flags & XPC_C_SETUP);
Dean Nelson35190502008-04-22 14:48:55 -0500589 (void)xpc_connect_channel(ch);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700590 }
591 continue;
592 }
593
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700594 /*
Dean Nelson7fb5e592008-07-29 22:34:10 -0700595 * Process any message related chctl flags, this may involve
596 * the activation of kthreads to deliver any pending messages
597 * sent from the other partition.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700598 */
599
Dean Nelson7fb5e592008-07-29 22:34:10 -0700600 if (chctl.flags[ch_number] & XPC_MSG_CHCTL_FLAGS)
601 xpc_process_msg_chctl_flags(part, ch_number);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700602 }
603}
604
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700605/*
Dean Nelsona607c3892005-09-01 14:01:37 -0500606 * XPC's heartbeat code calls this function to inform XPC that a partition is
607 * going down. XPC responds by tearing down the XPartition Communication
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700608 * infrastructure used for the just downed partition.
609 *
610 * XPC's heartbeat code will never call this function and xpc_partition_up()
611 * at the same time. Nor will it ever make multiple calls to either function
612 * at the same time.
613 */
614void
Dean Nelson65c17b82008-05-12 14:02:02 -0700615xpc_partition_going_down(struct xpc_partition *part, enum xp_retval reason)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700616{
617 unsigned long irq_flags;
618 int ch_number;
619 struct xpc_channel *ch;
620
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700621 dev_dbg(xpc_chan, "deactivating partition %d, reason=%d\n",
622 XPC_PARTID(part), reason);
623
624 if (!xpc_part_ref(part)) {
625 /* infrastructure for this partition isn't currently set up */
626 return;
627 }
628
Dean Nelsona607c3892005-09-01 14:01:37 -0500629 /* disconnect channels associated with the partition going down */
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700630
631 for (ch_number = 0; ch_number < part->nchannels; ch_number++) {
632 ch = &part->channels[ch_number];
633
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700634 xpc_msgqueue_ref(ch);
635 spin_lock_irqsave(&ch->lock, irq_flags);
636
637 XPC_DISCONNECT_CHANNEL(ch, reason, &irq_flags);
638
639 spin_unlock_irqrestore(&ch->lock, irq_flags);
640 xpc_msgqueue_deref(ch);
641 }
642
643 xpc_wakeup_channel_mgr(part);
644
645 xpc_part_deref(part);
646}
647
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700648/*
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700649 * Called by XP at the time of channel connection registration to cause
650 * XPC to establish connections to all currently active partitions.
651 */
652void
653xpc_initiate_connect(int ch_number)
654{
Dean Nelson64d032b2008-05-12 14:02:03 -0700655 short partid;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700656 struct xpc_partition *part;
657 struct xpc_channel *ch;
658
Dean Nelsonbc63d382008-07-29 22:34:04 -0700659 DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700660
Dean Nelsonbc63d382008-07-29 22:34:04 -0700661 for (partid = 0; partid < xp_max_npartitions; partid++) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700662 part = &xpc_partitions[partid];
663
664 if (xpc_part_ref(part)) {
665 ch = &part->channels[ch_number];
666
Dean Nelsone54af722005-10-25 14:07:43 -0500667 /*
668 * Initiate the establishment of a connection on the
669 * newly registered channel to the remote partition.
670 */
671 xpc_wakeup_channel_mgr(part);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700672 xpc_part_deref(part);
673 }
674 }
675}
676
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700677void
678xpc_connected_callout(struct xpc_channel *ch)
679{
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700680 /* let the registerer know that a connection has been established */
681
682 if (ch->func != NULL) {
Dean Nelson65c17b82008-05-12 14:02:02 -0700683 dev_dbg(xpc_chan, "ch->func() called, reason=xpConnected, "
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700684 "partid=%d, channel=%d\n", ch->partid, ch->number);
685
Dean Nelson65c17b82008-05-12 14:02:02 -0700686 ch->func(xpConnected, ch->partid, ch->number,
Dean Nelson35190502008-04-22 14:48:55 -0500687 (void *)(u64)ch->local_nentries, ch->key);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700688
Dean Nelson65c17b82008-05-12 14:02:02 -0700689 dev_dbg(xpc_chan, "ch->func() returned, reason=xpConnected, "
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700690 "partid=%d, channel=%d\n", ch->partid, ch->number);
691 }
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700692}
693
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700694/*
695 * Called by XP at the time of channel connection unregistration to cause
696 * XPC to teardown all current connections for the specified channel.
697 *
698 * Before returning xpc_initiate_disconnect() will wait until all connections
699 * on the specified channel have been closed/torndown. So the caller can be
700 * assured that they will not be receiving any more callouts from XPC to the
701 * function they registered via xpc_connect().
702 *
703 * Arguments:
704 *
705 * ch_number - channel # to unregister.
706 */
707void
708xpc_initiate_disconnect(int ch_number)
709{
710 unsigned long irq_flags;
Dean Nelson64d032b2008-05-12 14:02:03 -0700711 short partid;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700712 struct xpc_partition *part;
713 struct xpc_channel *ch;
714
Dean Nelsonbc63d382008-07-29 22:34:04 -0700715 DBUG_ON(ch_number < 0 || ch_number >= XPC_MAX_NCHANNELS);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700716
717 /* initiate the channel disconnect for every active partition */
Dean Nelsonbc63d382008-07-29 22:34:04 -0700718 for (partid = 0; partid < xp_max_npartitions; partid++) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700719 part = &xpc_partitions[partid];
720
721 if (xpc_part_ref(part)) {
722 ch = &part->channels[ch_number];
723 xpc_msgqueue_ref(ch);
724
725 spin_lock_irqsave(&ch->lock, irq_flags);
726
Dean Nelsona607c3892005-09-01 14:01:37 -0500727 if (!(ch->flags & XPC_C_DISCONNECTED)) {
728 ch->flags |= XPC_C_WDISCONNECT;
729
Dean Nelson65c17b82008-05-12 14:02:02 -0700730 XPC_DISCONNECT_CHANNEL(ch, xpUnregistering,
Dean Nelson35190502008-04-22 14:48:55 -0500731 &irq_flags);
Dean Nelsona607c3892005-09-01 14:01:37 -0500732 }
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700733
734 spin_unlock_irqrestore(&ch->lock, irq_flags);
735
736 xpc_msgqueue_deref(ch);
737 xpc_part_deref(part);
738 }
739 }
740
741 xpc_disconnect_wait(ch_number);
742}
743
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700744/*
745 * To disconnect a channel, and reflect it back to all who may be waiting.
746 *
Dean Nelsona607c3892005-09-01 14:01:37 -0500747 * An OPEN is not allowed until XPC_C_DISCONNECTING is cleared by
748 * xpc_process_disconnect(), and if set, XPC_C_WDISCONNECT is cleared by
749 * xpc_disconnect_wait().
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700750 *
751 * THE CHANNEL IS TO BE LOCKED BY THE CALLER AND WILL REMAIN LOCKED UPON RETURN.
752 */
753void
754xpc_disconnect_channel(const int line, struct xpc_channel *ch,
Dean Nelson65c17b82008-05-12 14:02:02 -0700755 enum xp_retval reason, unsigned long *irq_flags)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700756{
Dean Nelsona607c3892005-09-01 14:01:37 -0500757 u32 channel_was_connected = (ch->flags & XPC_C_CONNECTED);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700758
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700759 DBUG_ON(!spin_is_locked(&ch->lock));
760
Dean Nelson2c2b94f2008-04-22 14:50:17 -0500761 if (ch->flags & (XPC_C_DISCONNECTING | XPC_C_DISCONNECTED))
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700762 return;
Dean Nelson2c2b94f2008-04-22 14:50:17 -0500763
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700764 DBUG_ON(!(ch->flags & (XPC_C_CONNECTING | XPC_C_CONNECTED)));
765
766 dev_dbg(xpc_chan, "reason=%d, line=%d, partid=%d, channel=%d\n",
767 reason, line, ch->partid, ch->number);
768
769 XPC_SET_REASON(ch, reason, line);
770
Dean Nelsona607c3892005-09-01 14:01:37 -0500771 ch->flags |= (XPC_C_CLOSEREQUEST | XPC_C_DISCONNECTING);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700772 /* some of these may not have been set */
773 ch->flags &= ~(XPC_C_OPENREQUEST | XPC_C_OPENREPLY |
Dean Nelson35190502008-04-22 14:48:55 -0500774 XPC_C_ROPENREQUEST | XPC_C_ROPENREPLY |
775 XPC_C_CONNECTING | XPC_C_CONNECTED);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700776
Dean Nelson7fb5e592008-07-29 22:34:10 -0700777 xpc_send_chctl_closerequest(ch, irq_flags);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700778
Dean Nelson2c2b94f2008-04-22 14:50:17 -0500779 if (channel_was_connected)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700780 ch->flags |= XPC_C_WASCONNECTED;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700781
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700782 spin_unlock_irqrestore(&ch->lock, *irq_flags);
783
Dean Nelsona607c3892005-09-01 14:01:37 -0500784 /* wake all idle kthreads so they can exit */
785 if (atomic_read(&ch->kthreads_idle) > 0) {
786 wake_up_all(&ch->idle_wq);
Dean Nelsona460ef82006-11-22 08:25:00 -0600787
788 } else if ((ch->flags & XPC_C_CONNECTEDCALLOUT_MADE) &&
Dean Nelson35190502008-04-22 14:48:55 -0500789 !(ch->flags & XPC_C_DISCONNECTINGCALLOUT)) {
Dean Nelson65c17b82008-05-12 14:02:02 -0700790 /* start a kthread that will do the xpDisconnecting callout */
Dean Nelsona460ef82006-11-22 08:25:00 -0600791 xpc_create_kthreads(ch, 1, 1);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700792 }
793
Dean Nelsona607c3892005-09-01 14:01:37 -0500794 /* wake those waiting to allocate an entry from the local msg queue */
Dean Nelson2c2b94f2008-04-22 14:50:17 -0500795 if (atomic_read(&ch->n_on_msg_allocate_wq) > 0)
Dean Nelsona607c3892005-09-01 14:01:37 -0500796 wake_up(&ch->msg_allocate_wq);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700797
798 spin_lock_irqsave(&ch->lock, *irq_flags);
799}
800
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700801void
Dean Nelson65c17b82008-05-12 14:02:02 -0700802xpc_disconnect_callout(struct xpc_channel *ch, enum xp_retval reason)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700803{
804 /*
Dean Nelsona607c3892005-09-01 14:01:37 -0500805 * Let the channel's registerer know that the channel is being
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700806 * disconnected. We don't want to do this if the registerer was never
Dean Nelsona607c3892005-09-01 14:01:37 -0500807 * informed of a connection being made.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700808 */
809
810 if (ch->func != NULL) {
Dean Nelson246c7e32005-12-22 14:32:56 -0600811 dev_dbg(xpc_chan, "ch->func() called, reason=%d, partid=%d, "
812 "channel=%d\n", reason, ch->partid, ch->number);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700813
Dean Nelson246c7e32005-12-22 14:32:56 -0600814 ch->func(reason, ch->partid, ch->number, NULL, ch->key);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700815
Dean Nelson246c7e32005-12-22 14:32:56 -0600816 dev_dbg(xpc_chan, "ch->func() returned, reason=%d, partid=%d, "
817 "channel=%d\n", reason, ch->partid, ch->number);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700818 }
819}
820
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700821/*
822 * Wait for a message entry to become available for the specified channel,
823 * but don't wait any longer than 1 jiffy.
824 */
Dean Nelson33ba3c72008-07-29 22:34:07 -0700825enum xp_retval
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700826xpc_allocate_msg_wait(struct xpc_channel *ch)
827{
Dean Nelson65c17b82008-05-12 14:02:02 -0700828 enum xp_retval ret;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700829
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700830 if (ch->flags & XPC_C_DISCONNECTING) {
Dean Nelson65c17b82008-05-12 14:02:02 -0700831 DBUG_ON(ch->reason == xpInterrupted);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700832 return ch->reason;
833 }
834
835 atomic_inc(&ch->n_on_msg_allocate_wq);
836 ret = interruptible_sleep_on_timeout(&ch->msg_allocate_wq, 1);
837 atomic_dec(&ch->n_on_msg_allocate_wq);
838
839 if (ch->flags & XPC_C_DISCONNECTING) {
840 ret = ch->reason;
Dean Nelson65c17b82008-05-12 14:02:02 -0700841 DBUG_ON(ch->reason == xpInterrupted);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700842 } else if (ret == 0) {
Dean Nelson65c17b82008-05-12 14:02:02 -0700843 ret = xpTimeout;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700844 } else {
Dean Nelson65c17b82008-05-12 14:02:02 -0700845 ret = xpInterrupted;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700846 }
847
848 return ret;
849}
850
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700851/*
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700852 * Send a message that contains the user's payload on the specified channel
853 * connected to the specified partition.
854 *
855 * NOTE that this routine can sleep waiting for a message entry to become
856 * available. To not sleep, pass in the XPC_NOWAIT flag.
857 *
858 * Once sent, this routine will not wait for the message to be received, nor
859 * will notification be given when it does happen.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700860 *
861 * Arguments:
862 *
863 * partid - ID of partition to which the channel is connected.
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700864 * ch_number - channel # to send message on.
865 * flags - see xp.h for valid flags.
866 * payload - pointer to the payload which is to be sent.
867 * payload_size - size of the payload in bytes.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700868 */
Dean Nelson65c17b82008-05-12 14:02:02 -0700869enum xp_retval
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700870xpc_initiate_send(short partid, int ch_number, u32 flags, void *payload,
871 u16 payload_size)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700872{
873 struct xpc_partition *part = &xpc_partitions[partid];
Dean Nelson65c17b82008-05-12 14:02:02 -0700874 enum xp_retval ret = xpUnknownReason;
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700875
876 dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload,
877 partid, ch_number);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700878
Dean Nelsonbc63d382008-07-29 22:34:04 -0700879 DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700880 DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700881 DBUG_ON(payload == NULL);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700882
883 if (xpc_part_ref(part)) {
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700884 ret = xpc_send_payload(&part->channels[ch_number], flags,
885 payload, payload_size, 0, NULL, NULL);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700886 xpc_part_deref(part);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700887 }
888
889 return ret;
890}
891
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700892/*
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700893 * Send a message that contains the user's payload on the specified channel
894 * connected to the specified partition.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700895 *
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700896 * NOTE that this routine can sleep waiting for a message entry to become
897 * available. To not sleep, pass in the XPC_NOWAIT flag.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700898 *
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700899 * This routine will not wait for the message to be sent or received.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700900 *
901 * Once the remote end of the channel has received the message, the function
902 * passed as an argument to xpc_initiate_send_notify() will be called. This
903 * allows the sender to free up or re-use any buffers referenced by the
904 * message, but does NOT mean the message has been processed at the remote
905 * end by a receiver.
906 *
907 * If this routine returns an error, the caller's function will NOT be called.
908 *
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700909 * Arguments:
910 *
911 * partid - ID of partition to which the channel is connected.
912 * ch_number - channel # to send message on.
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700913 * flags - see xp.h for valid flags.
914 * payload - pointer to the payload which is to be sent.
915 * payload_size - size of the payload in bytes.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700916 * func - function to call with asynchronous notification of message
917 * receipt. THIS FUNCTION MUST BE NON-BLOCKING.
918 * key - user-defined key to be passed to the function when it's called.
919 */
Dean Nelson65c17b82008-05-12 14:02:02 -0700920enum xp_retval
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700921xpc_initiate_send_notify(short partid, int ch_number, u32 flags, void *payload,
922 u16 payload_size, xpc_notify_func func, void *key)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700923{
924 struct xpc_partition *part = &xpc_partitions[partid];
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700925 enum xp_retval ret = xpUnknownReason;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700926
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700927 dev_dbg(xpc_chan, "payload=0x%p, partid=%d, channel=%d\n", payload,
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700928 partid, ch_number);
929
Dean Nelsonbc63d382008-07-29 22:34:04 -0700930 DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700931 DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700932 DBUG_ON(payload == NULL);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700933 DBUG_ON(func == NULL);
934
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700935 if (xpc_part_ref(part)) {
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700936 ret = xpc_send_payload(&part->channels[ch_number], flags,
937 payload, payload_size, XPC_N_CALL, func,
938 key);
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700939 xpc_part_deref(part);
940 }
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700941 return ret;
942}
943
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700944/*
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700945 * Deliver a message's payload to its intended recipient.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700946 */
947void
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700948xpc_deliver_payload(struct xpc_channel *ch)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700949{
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700950 void *payload;
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700951
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700952 payload = xpc_get_deliverable_payload(ch);
953 if (payload != NULL) {
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700954
955 /*
956 * This ref is taken to protect the payload itself from being
957 * freed before the user is finished with it, which the user
958 * indicates by calling xpc_initiate_received().
959 */
960 xpc_msgqueue_ref(ch);
961
962 atomic_inc(&ch->kthreads_active);
963
964 if (ch->func != NULL) {
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700965 dev_dbg(xpc_chan, "ch->func() called, payload=0x%p "
966 "partid=%d channel=%d\n", payload, ch->partid,
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700967 ch->number);
968
969 /* deliver the message to its intended recipient */
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700970 ch->func(xpMsgReceived, ch->partid, ch->number, payload,
971 ch->key);
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700972
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700973 dev_dbg(xpc_chan, "ch->func() returned, payload=0x%p "
974 "partid=%d channel=%d\n", payload, ch->partid,
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700975 ch->number);
976 }
977
978 atomic_dec(&ch->kthreads_active);
979 }
980}
981
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700982/*
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700983 * Acknowledge receipt of a delivered message's payload.
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700984 *
985 * This function, although called by users, does not call xpc_part_ref() to
986 * ensure that the partition infrastructure is in place. It relies on the
Dean Nelsonbd3e64c2008-07-29 22:34:19 -0700987 * fact that we called xpc_msgqueue_ref() in xpc_deliver_payload().
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700988 *
989 * Arguments:
990 *
991 * partid - ID of partition to which the channel is connected.
992 * ch_number - channel # message received on.
993 * payload - pointer to the payload area allocated via
Dean Nelson97bf1aa2008-07-29 22:34:08 -0700994 * xpc_initiate_send() or xpc_initiate_send_notify().
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700995 */
996void
Dean Nelson64d032b2008-05-12 14:02:03 -0700997xpc_initiate_received(short partid, int ch_number, void *payload)
Dean Nelson89eb8eb2005-03-23 19:50:00 -0700998{
999 struct xpc_partition *part = &xpc_partitions[partid];
1000 struct xpc_channel *ch;
Dean Nelson89eb8eb2005-03-23 19:50:00 -07001001
Dean Nelsonbc63d382008-07-29 22:34:04 -07001002 DBUG_ON(partid < 0 || partid >= xp_max_npartitions);
Dean Nelson89eb8eb2005-03-23 19:50:00 -07001003 DBUG_ON(ch_number < 0 || ch_number >= part->nchannels);
1004
1005 ch = &part->channels[ch_number];
Dean Nelsonbd3e64c2008-07-29 22:34:19 -07001006 xpc_received_payload(ch, payload);
Dean Nelson89eb8eb2005-03-23 19:50:00 -07001007
Dean Nelsonbd3e64c2008-07-29 22:34:19 -07001008 /* the call to xpc_msgqueue_ref() was done by xpc_deliver_payload() */
Dean Nelson89eb8eb2005-03-23 19:50:00 -07001009 xpc_msgqueue_deref(ch);
1010}