diff options
author | Tom Tucker <tom@opengridcomputing.com> | 2006-08-03 17:02:40 -0400 |
---|---|---|
committer | Roland Dreier <rolandd@cisco.com> | 2006-09-22 18:22:46 -0400 |
commit | 922a8e9fb2e0711212badce47a41137e2ca04cb3 (patch) | |
tree | 54af57ac9f2ddcaf0e6fdead4d9175eecd9e06e2 | |
parent | 3cd965646b7cb75ae84dd0daf6258adf20e4f169 (diff) |
RDMA: iWARP Connection Manager.
Add an iWARP Connection Manager (CM), which abstracts connection
management for iWARP devices (RNICs). It is a logical instance of the
xx_cm where xx is the transport type (ib or iw). The symbols exported
are used by the transport independent rdma_cm module, and are
available also for transport dependent ULPs.
Signed-off-by: Tom Tucker <tom@opengridcomputing.com>
Signed-off-by: Steve Wise <swise@opengridcomputing.com>
Signed-off-by: Roland Dreier <rolandd@cisco.com>
-rw-r--r-- | drivers/infiniband/core/iwcm.c | 1019 | ||||
-rw-r--r-- | drivers/infiniband/core/iwcm.h | 62 | ||||
-rw-r--r-- | include/rdma/iw_cm.h | 258 |
3 files changed, 1339 insertions, 0 deletions
diff --git a/drivers/infiniband/core/iwcm.c b/drivers/infiniband/core/iwcm.c new file mode 100644 index 000000000000..c3fb304a4e86 --- /dev/null +++ b/drivers/infiniband/core/iwcm.c | |||
@@ -0,0 +1,1019 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2004, 2005 Intel Corporation. All rights reserved. | ||
3 | * Copyright (c) 2004 Topspin Corporation. All rights reserved. | ||
4 | * Copyright (c) 2004, 2005 Voltaire Corporation. All rights reserved. | ||
5 | * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. | ||
6 | * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. | ||
7 | * Copyright (c) 2005 Network Appliance, Inc. All rights reserved. | ||
8 | * | ||
9 | * This software is available to you under a choice of one of two | ||
10 | * licenses. You may choose to be licensed under the terms of the GNU | ||
11 | * General Public License (GPL) Version 2, available from the file | ||
12 | * COPYING in the main directory of this source tree, or the | ||
13 | * OpenIB.org BSD license below: | ||
14 | * | ||
15 | * Redistribution and use in source and binary forms, with or | ||
16 | * without modification, are permitted provided that the following | ||
17 | * conditions are met: | ||
18 | * | ||
19 | * - Redistributions of source code must retain the above | ||
20 | * copyright notice, this list of conditions and the following | ||
21 | * disclaimer. | ||
22 | * | ||
23 | * - Redistributions in binary form must reproduce the above | ||
24 | * copyright notice, this list of conditions and the following | ||
25 | * disclaimer in the documentation and/or other materials | ||
26 | * provided with the distribution. | ||
27 | * | ||
28 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
29 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
30 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
31 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
32 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
33 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
34 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
35 | * SOFTWARE. | ||
36 | * | ||
37 | */ | ||
38 | #include <linux/dma-mapping.h> | ||
39 | #include <linux/err.h> | ||
40 | #include <linux/idr.h> | ||
41 | #include <linux/interrupt.h> | ||
42 | #include <linux/pci.h> | ||
43 | #include <linux/rbtree.h> | ||
44 | #include <linux/spinlock.h> | ||
45 | #include <linux/workqueue.h> | ||
46 | #include <linux/completion.h> | ||
47 | |||
48 | #include <rdma/iw_cm.h> | ||
49 | #include <rdma/ib_addr.h> | ||
50 | |||
51 | #include "iwcm.h" | ||
52 | |||
53 | MODULE_AUTHOR("Tom Tucker"); | ||
54 | MODULE_DESCRIPTION("iWARP CM"); | ||
55 | MODULE_LICENSE("Dual BSD/GPL"); | ||
56 | |||
57 | static struct workqueue_struct *iwcm_wq; | ||
58 | struct iwcm_work { | ||
59 | struct work_struct work; | ||
60 | struct iwcm_id_private *cm_id; | ||
61 | struct list_head list; | ||
62 | struct iw_cm_event event; | ||
63 | struct list_head free_list; | ||
64 | }; | ||
65 | |||
66 | /* | ||
67 | * The following services provide a mechanism for pre-allocating iwcm_work | ||
68 | * elements. The design pre-allocates them based on the cm_id type: | ||
69 | * LISTENING IDS: Get enough elements preallocated to handle the | ||
70 | * listen backlog. | ||
71 | * ACTIVE IDS: 4: CONNECT_REPLY, ESTABLISHED, DISCONNECT, CLOSE | ||
72 | * PASSIVE IDS: 3: ESTABLISHED, DISCONNECT, CLOSE | ||
73 | * | ||
74 | * Allocating them in connect and listen avoids having to deal | ||
75 | * with allocation failures on the event upcall from the provider (which | ||
76 | * is called in the interrupt context). | ||
77 | * | ||
78 | * One exception is when creating the cm_id for incoming connection requests. | ||
79 | * There are two cases: | ||
80 | * 1) in the event upcall, cm_event_handler(), for a listening cm_id. If | ||
81 | * the backlog is exceeded, then no more connection request events will | ||
82 | * be processed. cm_event_handler() returns -ENOMEM in this case. Its up | ||
83 | * to the provider to reject the connectino request. | ||
84 | * 2) in the connection request workqueue handler, cm_conn_req_handler(). | ||
85 | * If work elements cannot be allocated for the new connect request cm_id, | ||
86 | * then IWCM will call the provider reject method. This is ok since | ||
87 | * cm_conn_req_handler() runs in the workqueue thread context. | ||
88 | */ | ||
89 | |||
90 | static struct iwcm_work *get_work(struct iwcm_id_private *cm_id_priv) | ||
91 | { | ||
92 | struct iwcm_work *work; | ||
93 | |||
94 | if (list_empty(&cm_id_priv->work_free_list)) | ||
95 | return NULL; | ||
96 | work = list_entry(cm_id_priv->work_free_list.next, struct iwcm_work, | ||
97 | free_list); | ||
98 | list_del_init(&work->free_list); | ||
99 | return work; | ||
100 | } | ||
101 | |||
102 | static void put_work(struct iwcm_work *work) | ||
103 | { | ||
104 | list_add(&work->free_list, &work->cm_id->work_free_list); | ||
105 | } | ||
106 | |||
107 | static void dealloc_work_entries(struct iwcm_id_private *cm_id_priv) | ||
108 | { | ||
109 | struct list_head *e, *tmp; | ||
110 | |||
111 | list_for_each_safe(e, tmp, &cm_id_priv->work_free_list) | ||
112 | kfree(list_entry(e, struct iwcm_work, free_list)); | ||
113 | } | ||
114 | |||
115 | static int alloc_work_entries(struct iwcm_id_private *cm_id_priv, int count) | ||
116 | { | ||
117 | struct iwcm_work *work; | ||
118 | |||
119 | BUG_ON(!list_empty(&cm_id_priv->work_free_list)); | ||
120 | while (count--) { | ||
121 | work = kmalloc(sizeof(struct iwcm_work), GFP_KERNEL); | ||
122 | if (!work) { | ||
123 | dealloc_work_entries(cm_id_priv); | ||
124 | return -ENOMEM; | ||
125 | } | ||
126 | work->cm_id = cm_id_priv; | ||
127 | INIT_LIST_HEAD(&work->list); | ||
128 | put_work(work); | ||
129 | } | ||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * Save private data from incoming connection requests in the | ||
135 | * cm_id_priv so the low level driver doesn't have to. Adjust | ||
136 | * the event ptr to point to the local copy. | ||
137 | */ | ||
138 | static int copy_private_data(struct iwcm_id_private *cm_id_priv, | ||
139 | struct iw_cm_event *event) | ||
140 | { | ||
141 | void *p; | ||
142 | |||
143 | p = kmalloc(event->private_data_len, GFP_ATOMIC); | ||
144 | if (!p) | ||
145 | return -ENOMEM; | ||
146 | memcpy(p, event->private_data, event->private_data_len); | ||
147 | event->private_data = p; | ||
148 | return 0; | ||
149 | } | ||
150 | |||
151 | /* | ||
152 | * Release a reference on cm_id. If the last reference is being removed | ||
153 | * and iw_destroy_cm_id is waiting, wake up the waiting thread. | ||
154 | */ | ||
155 | static int iwcm_deref_id(struct iwcm_id_private *cm_id_priv) | ||
156 | { | ||
157 | int ret = 0; | ||
158 | |||
159 | BUG_ON(atomic_read(&cm_id_priv->refcount)==0); | ||
160 | if (atomic_dec_and_test(&cm_id_priv->refcount)) { | ||
161 | BUG_ON(!list_empty(&cm_id_priv->work_list)); | ||
162 | if (waitqueue_active(&cm_id_priv->destroy_comp.wait)) { | ||
163 | BUG_ON(cm_id_priv->state != IW_CM_STATE_DESTROYING); | ||
164 | BUG_ON(test_bit(IWCM_F_CALLBACK_DESTROY, | ||
165 | &cm_id_priv->flags)); | ||
166 | ret = 1; | ||
167 | } | ||
168 | complete(&cm_id_priv->destroy_comp); | ||
169 | } | ||
170 | |||
171 | return ret; | ||
172 | } | ||
173 | |||
174 | static void add_ref(struct iw_cm_id *cm_id) | ||
175 | { | ||
176 | struct iwcm_id_private *cm_id_priv; | ||
177 | cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); | ||
178 | atomic_inc(&cm_id_priv->refcount); | ||
179 | } | ||
180 | |||
181 | static void rem_ref(struct iw_cm_id *cm_id) | ||
182 | { | ||
183 | struct iwcm_id_private *cm_id_priv; | ||
184 | cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); | ||
185 | iwcm_deref_id(cm_id_priv); | ||
186 | } | ||
187 | |||
188 | static int cm_event_handler(struct iw_cm_id *cm_id, struct iw_cm_event *event); | ||
189 | |||
190 | struct iw_cm_id *iw_create_cm_id(struct ib_device *device, | ||
191 | iw_cm_handler cm_handler, | ||
192 | void *context) | ||
193 | { | ||
194 | struct iwcm_id_private *cm_id_priv; | ||
195 | |||
196 | cm_id_priv = kzalloc(sizeof(*cm_id_priv), GFP_KERNEL); | ||
197 | if (!cm_id_priv) | ||
198 | return ERR_PTR(-ENOMEM); | ||
199 | |||
200 | cm_id_priv->state = IW_CM_STATE_IDLE; | ||
201 | cm_id_priv->id.device = device; | ||
202 | cm_id_priv->id.cm_handler = cm_handler; | ||
203 | cm_id_priv->id.context = context; | ||
204 | cm_id_priv->id.event_handler = cm_event_handler; | ||
205 | cm_id_priv->id.add_ref = add_ref; | ||
206 | cm_id_priv->id.rem_ref = rem_ref; | ||
207 | spin_lock_init(&cm_id_priv->lock); | ||
208 | atomic_set(&cm_id_priv->refcount, 1); | ||
209 | init_waitqueue_head(&cm_id_priv->connect_wait); | ||
210 | init_completion(&cm_id_priv->destroy_comp); | ||
211 | INIT_LIST_HEAD(&cm_id_priv->work_list); | ||
212 | INIT_LIST_HEAD(&cm_id_priv->work_free_list); | ||
213 | |||
214 | return &cm_id_priv->id; | ||
215 | } | ||
216 | EXPORT_SYMBOL(iw_create_cm_id); | ||
217 | |||
218 | |||
219 | static int iwcm_modify_qp_err(struct ib_qp *qp) | ||
220 | { | ||
221 | struct ib_qp_attr qp_attr; | ||
222 | |||
223 | if (!qp) | ||
224 | return -EINVAL; | ||
225 | |||
226 | qp_attr.qp_state = IB_QPS_ERR; | ||
227 | return ib_modify_qp(qp, &qp_attr, IB_QP_STATE); | ||
228 | } | ||
229 | |||
230 | /* | ||
231 | * This is really the RDMAC CLOSING state. It is most similar to the | ||
232 | * IB SQD QP state. | ||
233 | */ | ||
234 | static int iwcm_modify_qp_sqd(struct ib_qp *qp) | ||
235 | { | ||
236 | struct ib_qp_attr qp_attr; | ||
237 | |||
238 | BUG_ON(qp == NULL); | ||
239 | qp_attr.qp_state = IB_QPS_SQD; | ||
240 | return ib_modify_qp(qp, &qp_attr, IB_QP_STATE); | ||
241 | } | ||
242 | |||
243 | /* | ||
244 | * CM_ID <-- CLOSING | ||
245 | * | ||
246 | * Block if a passive or active connection is currenlty being processed. Then | ||
247 | * process the event as follows: | ||
248 | * - If we are ESTABLISHED, move to CLOSING and modify the QP state | ||
249 | * based on the abrupt flag | ||
250 | * - If the connection is already in the CLOSING or IDLE state, the peer is | ||
251 | * disconnecting concurrently with us and we've already seen the | ||
252 | * DISCONNECT event -- ignore the request and return 0 | ||
253 | * - Disconnect on a listening endpoint returns -EINVAL | ||
254 | */ | ||
255 | int iw_cm_disconnect(struct iw_cm_id *cm_id, int abrupt) | ||
256 | { | ||
257 | struct iwcm_id_private *cm_id_priv; | ||
258 | unsigned long flags; | ||
259 | int ret = 0; | ||
260 | struct ib_qp *qp = NULL; | ||
261 | |||
262 | cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); | ||
263 | /* Wait if we're currently in a connect or accept downcall */ | ||
264 | wait_event(cm_id_priv->connect_wait, | ||
265 | !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags)); | ||
266 | |||
267 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
268 | switch (cm_id_priv->state) { | ||
269 | case IW_CM_STATE_ESTABLISHED: | ||
270 | cm_id_priv->state = IW_CM_STATE_CLOSING; | ||
271 | |||
272 | /* QP could be <nul> for user-mode client */ | ||
273 | if (cm_id_priv->qp) | ||
274 | qp = cm_id_priv->qp; | ||
275 | else | ||
276 | ret = -EINVAL; | ||
277 | break; | ||
278 | case IW_CM_STATE_LISTEN: | ||
279 | ret = -EINVAL; | ||
280 | break; | ||
281 | case IW_CM_STATE_CLOSING: | ||
282 | /* remote peer closed first */ | ||
283 | case IW_CM_STATE_IDLE: | ||
284 | /* accept or connect returned !0 */ | ||
285 | break; | ||
286 | case IW_CM_STATE_CONN_RECV: | ||
287 | /* | ||
288 | * App called disconnect before/without calling accept after | ||
289 | * connect_request event delivered. | ||
290 | */ | ||
291 | break; | ||
292 | case IW_CM_STATE_CONN_SENT: | ||
293 | /* Can only get here if wait above fails */ | ||
294 | default: | ||
295 | BUG(); | ||
296 | } | ||
297 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
298 | |||
299 | if (qp) { | ||
300 | if (abrupt) | ||
301 | ret = iwcm_modify_qp_err(qp); | ||
302 | else | ||
303 | ret = iwcm_modify_qp_sqd(qp); | ||
304 | |||
305 | /* | ||
306 | * If both sides are disconnecting the QP could | ||
307 | * already be in ERR or SQD states | ||
308 | */ | ||
309 | ret = 0; | ||
310 | } | ||
311 | |||
312 | return ret; | ||
313 | } | ||
314 | EXPORT_SYMBOL(iw_cm_disconnect); | ||
315 | |||
316 | /* | ||
317 | * CM_ID <-- DESTROYING | ||
318 | * | ||
319 | * Clean up all resources associated with the connection and release | ||
320 | * the initial reference taken by iw_create_cm_id. | ||
321 | */ | ||
322 | static void destroy_cm_id(struct iw_cm_id *cm_id) | ||
323 | { | ||
324 | struct iwcm_id_private *cm_id_priv; | ||
325 | unsigned long flags; | ||
326 | int ret; | ||
327 | |||
328 | cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); | ||
329 | /* | ||
330 | * Wait if we're currently in a connect or accept downcall. A | ||
331 | * listening endpoint should never block here. | ||
332 | */ | ||
333 | wait_event(cm_id_priv->connect_wait, | ||
334 | !test_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags)); | ||
335 | |||
336 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
337 | switch (cm_id_priv->state) { | ||
338 | case IW_CM_STATE_LISTEN: | ||
339 | cm_id_priv->state = IW_CM_STATE_DESTROYING; | ||
340 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
341 | /* destroy the listening endpoint */ | ||
342 | ret = cm_id->device->iwcm->destroy_listen(cm_id); | ||
343 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
344 | break; | ||
345 | case IW_CM_STATE_ESTABLISHED: | ||
346 | cm_id_priv->state = IW_CM_STATE_DESTROYING; | ||
347 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
348 | /* Abrupt close of the connection */ | ||
349 | (void)iwcm_modify_qp_err(cm_id_priv->qp); | ||
350 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
351 | break; | ||
352 | case IW_CM_STATE_IDLE: | ||
353 | case IW_CM_STATE_CLOSING: | ||
354 | cm_id_priv->state = IW_CM_STATE_DESTROYING; | ||
355 | break; | ||
356 | case IW_CM_STATE_CONN_RECV: | ||
357 | /* | ||
358 | * App called destroy before/without calling accept after | ||
359 | * receiving connection request event notification. | ||
360 | */ | ||
361 | cm_id_priv->state = IW_CM_STATE_DESTROYING; | ||
362 | break; | ||
363 | case IW_CM_STATE_CONN_SENT: | ||
364 | case IW_CM_STATE_DESTROYING: | ||
365 | default: | ||
366 | BUG(); | ||
367 | break; | ||
368 | } | ||
369 | if (cm_id_priv->qp) { | ||
370 | cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp); | ||
371 | cm_id_priv->qp = NULL; | ||
372 | } | ||
373 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
374 | |||
375 | (void)iwcm_deref_id(cm_id_priv); | ||
376 | } | ||
377 | |||
378 | /* | ||
379 | * This function is only called by the application thread and cannot | ||
380 | * be called by the event thread. The function will wait for all | ||
381 | * references to be released on the cm_id and then kfree the cm_id | ||
382 | * object. | ||
383 | */ | ||
384 | void iw_destroy_cm_id(struct iw_cm_id *cm_id) | ||
385 | { | ||
386 | struct iwcm_id_private *cm_id_priv; | ||
387 | |||
388 | cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); | ||
389 | BUG_ON(test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags)); | ||
390 | |||
391 | destroy_cm_id(cm_id); | ||
392 | |||
393 | wait_for_completion(&cm_id_priv->destroy_comp); | ||
394 | |||
395 | dealloc_work_entries(cm_id_priv); | ||
396 | |||
397 | kfree(cm_id_priv); | ||
398 | } | ||
399 | EXPORT_SYMBOL(iw_destroy_cm_id); | ||
400 | |||
401 | /* | ||
402 | * CM_ID <-- LISTEN | ||
403 | * | ||
404 | * Start listening for connect requests. Generates one CONNECT_REQUEST | ||
405 | * event for each inbound connect request. | ||
406 | */ | ||
407 | int iw_cm_listen(struct iw_cm_id *cm_id, int backlog) | ||
408 | { | ||
409 | struct iwcm_id_private *cm_id_priv; | ||
410 | unsigned long flags; | ||
411 | int ret = 0; | ||
412 | |||
413 | cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); | ||
414 | |||
415 | ret = alloc_work_entries(cm_id_priv, backlog); | ||
416 | if (ret) | ||
417 | return ret; | ||
418 | |||
419 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
420 | switch (cm_id_priv->state) { | ||
421 | case IW_CM_STATE_IDLE: | ||
422 | cm_id_priv->state = IW_CM_STATE_LISTEN; | ||
423 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
424 | ret = cm_id->device->iwcm->create_listen(cm_id, backlog); | ||
425 | if (ret) | ||
426 | cm_id_priv->state = IW_CM_STATE_IDLE; | ||
427 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
428 | break; | ||
429 | default: | ||
430 | ret = -EINVAL; | ||
431 | } | ||
432 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
433 | |||
434 | return ret; | ||
435 | } | ||
436 | EXPORT_SYMBOL(iw_cm_listen); | ||
437 | |||
438 | /* | ||
439 | * CM_ID <-- IDLE | ||
440 | * | ||
441 | * Rejects an inbound connection request. No events are generated. | ||
442 | */ | ||
443 | int iw_cm_reject(struct iw_cm_id *cm_id, | ||
444 | const void *private_data, | ||
445 | u8 private_data_len) | ||
446 | { | ||
447 | struct iwcm_id_private *cm_id_priv; | ||
448 | unsigned long flags; | ||
449 | int ret; | ||
450 | |||
451 | cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); | ||
452 | set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); | ||
453 | |||
454 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
455 | if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) { | ||
456 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
457 | clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); | ||
458 | wake_up_all(&cm_id_priv->connect_wait); | ||
459 | return -EINVAL; | ||
460 | } | ||
461 | cm_id_priv->state = IW_CM_STATE_IDLE; | ||
462 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
463 | |||
464 | ret = cm_id->device->iwcm->reject(cm_id, private_data, | ||
465 | private_data_len); | ||
466 | |||
467 | clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); | ||
468 | wake_up_all(&cm_id_priv->connect_wait); | ||
469 | |||
470 | return ret; | ||
471 | } | ||
472 | EXPORT_SYMBOL(iw_cm_reject); | ||
473 | |||
474 | /* | ||
475 | * CM_ID <-- ESTABLISHED | ||
476 | * | ||
477 | * Accepts an inbound connection request and generates an ESTABLISHED | ||
478 | * event. Callers of iw_cm_disconnect and iw_destroy_cm_id will block | ||
479 | * until the ESTABLISHED event is received from the provider. | ||
480 | */ | ||
481 | int iw_cm_accept(struct iw_cm_id *cm_id, | ||
482 | struct iw_cm_conn_param *iw_param) | ||
483 | { | ||
484 | struct iwcm_id_private *cm_id_priv; | ||
485 | struct ib_qp *qp; | ||
486 | unsigned long flags; | ||
487 | int ret; | ||
488 | |||
489 | cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); | ||
490 | set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); | ||
491 | |||
492 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
493 | if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) { | ||
494 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
495 | clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); | ||
496 | wake_up_all(&cm_id_priv->connect_wait); | ||
497 | return -EINVAL; | ||
498 | } | ||
499 | /* Get the ib_qp given the QPN */ | ||
500 | qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn); | ||
501 | if (!qp) { | ||
502 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
503 | return -EINVAL; | ||
504 | } | ||
505 | cm_id->device->iwcm->add_ref(qp); | ||
506 | cm_id_priv->qp = qp; | ||
507 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
508 | |||
509 | ret = cm_id->device->iwcm->accept(cm_id, iw_param); | ||
510 | if (ret) { | ||
511 | /* An error on accept precludes provider events */ | ||
512 | BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_RECV); | ||
513 | cm_id_priv->state = IW_CM_STATE_IDLE; | ||
514 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
515 | if (cm_id_priv->qp) { | ||
516 | cm_id->device->iwcm->rem_ref(qp); | ||
517 | cm_id_priv->qp = NULL; | ||
518 | } | ||
519 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
520 | clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); | ||
521 | wake_up_all(&cm_id_priv->connect_wait); | ||
522 | } | ||
523 | |||
524 | return ret; | ||
525 | } | ||
526 | EXPORT_SYMBOL(iw_cm_accept); | ||
527 | |||
528 | /* | ||
529 | * Active Side: CM_ID <-- CONN_SENT | ||
530 | * | ||
531 | * If successful, results in the generation of a CONNECT_REPLY | ||
532 | * event. iw_cm_disconnect and iw_cm_destroy will block until the | ||
533 | * CONNECT_REPLY event is received from the provider. | ||
534 | */ | ||
535 | int iw_cm_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param) | ||
536 | { | ||
537 | struct iwcm_id_private *cm_id_priv; | ||
538 | int ret = 0; | ||
539 | unsigned long flags; | ||
540 | struct ib_qp *qp; | ||
541 | |||
542 | cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); | ||
543 | |||
544 | ret = alloc_work_entries(cm_id_priv, 4); | ||
545 | if (ret) | ||
546 | return ret; | ||
547 | |||
548 | set_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); | ||
549 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
550 | |||
551 | if (cm_id_priv->state != IW_CM_STATE_IDLE) { | ||
552 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
553 | clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); | ||
554 | wake_up_all(&cm_id_priv->connect_wait); | ||
555 | return -EINVAL; | ||
556 | } | ||
557 | |||
558 | /* Get the ib_qp given the QPN */ | ||
559 | qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn); | ||
560 | if (!qp) { | ||
561 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
562 | return -EINVAL; | ||
563 | } | ||
564 | cm_id->device->iwcm->add_ref(qp); | ||
565 | cm_id_priv->qp = qp; | ||
566 | cm_id_priv->state = IW_CM_STATE_CONN_SENT; | ||
567 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
568 | |||
569 | ret = cm_id->device->iwcm->connect(cm_id, iw_param); | ||
570 | if (ret) { | ||
571 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
572 | if (cm_id_priv->qp) { | ||
573 | cm_id->device->iwcm->rem_ref(qp); | ||
574 | cm_id_priv->qp = NULL; | ||
575 | } | ||
576 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
577 | BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_SENT); | ||
578 | cm_id_priv->state = IW_CM_STATE_IDLE; | ||
579 | clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); | ||
580 | wake_up_all(&cm_id_priv->connect_wait); | ||
581 | } | ||
582 | |||
583 | return ret; | ||
584 | } | ||
585 | EXPORT_SYMBOL(iw_cm_connect); | ||
586 | |||
587 | /* | ||
588 | * Passive Side: new CM_ID <-- CONN_RECV | ||
589 | * | ||
590 | * Handles an inbound connect request. The function creates a new | ||
591 | * iw_cm_id to represent the new connection and inherits the client | ||
592 | * callback function and other attributes from the listening parent. | ||
593 | * | ||
594 | * The work item contains a pointer to the listen_cm_id and the event. The | ||
595 | * listen_cm_id contains the client cm_handler, context and | ||
596 | * device. These are copied when the device is cloned. The event | ||
597 | * contains the new four tuple. | ||
598 | * | ||
599 | * An error on the child should not affect the parent, so this | ||
600 | * function does not return a value. | ||
601 | */ | ||
602 | static void cm_conn_req_handler(struct iwcm_id_private *listen_id_priv, | ||
603 | struct iw_cm_event *iw_event) | ||
604 | { | ||
605 | unsigned long flags; | ||
606 | struct iw_cm_id *cm_id; | ||
607 | struct iwcm_id_private *cm_id_priv; | ||
608 | int ret; | ||
609 | |||
610 | /* | ||
611 | * The provider should never generate a connection request | ||
612 | * event with a bad status. | ||
613 | */ | ||
614 | BUG_ON(iw_event->status); | ||
615 | |||
616 | /* | ||
617 | * We could be destroying the listening id. If so, ignore this | ||
618 | * upcall. | ||
619 | */ | ||
620 | spin_lock_irqsave(&listen_id_priv->lock, flags); | ||
621 | if (listen_id_priv->state != IW_CM_STATE_LISTEN) { | ||
622 | spin_unlock_irqrestore(&listen_id_priv->lock, flags); | ||
623 | return; | ||
624 | } | ||
625 | spin_unlock_irqrestore(&listen_id_priv->lock, flags); | ||
626 | |||
627 | cm_id = iw_create_cm_id(listen_id_priv->id.device, | ||
628 | listen_id_priv->id.cm_handler, | ||
629 | listen_id_priv->id.context); | ||
630 | /* If the cm_id could not be created, ignore the request */ | ||
631 | if (IS_ERR(cm_id)) | ||
632 | return; | ||
633 | |||
634 | cm_id->provider_data = iw_event->provider_data; | ||
635 | cm_id->local_addr = iw_event->local_addr; | ||
636 | cm_id->remote_addr = iw_event->remote_addr; | ||
637 | |||
638 | cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); | ||
639 | cm_id_priv->state = IW_CM_STATE_CONN_RECV; | ||
640 | |||
641 | ret = alloc_work_entries(cm_id_priv, 3); | ||
642 | if (ret) { | ||
643 | iw_cm_reject(cm_id, NULL, 0); | ||
644 | iw_destroy_cm_id(cm_id); | ||
645 | return; | ||
646 | } | ||
647 | |||
648 | /* Call the client CM handler */ | ||
649 | ret = cm_id->cm_handler(cm_id, iw_event); | ||
650 | if (ret) { | ||
651 | set_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags); | ||
652 | destroy_cm_id(cm_id); | ||
653 | if (atomic_read(&cm_id_priv->refcount)==0) | ||
654 | kfree(cm_id); | ||
655 | } | ||
656 | |||
657 | if (iw_event->private_data_len) | ||
658 | kfree(iw_event->private_data); | ||
659 | } | ||
660 | |||
661 | /* | ||
662 | * Passive Side: CM_ID <-- ESTABLISHED | ||
663 | * | ||
664 | * The provider generated an ESTABLISHED event which means that | ||
665 | * the MPA negotion has completed successfully and we are now in MPA | ||
666 | * FPDU mode. | ||
667 | * | ||
668 | * This event can only be received in the CONN_RECV state. If the | ||
669 | * remote peer closed, the ESTABLISHED event would be received followed | ||
670 | * by the CLOSE event. If the app closes, it will block until we wake | ||
671 | * it up after processing this event. | ||
672 | */ | ||
673 | static int cm_conn_est_handler(struct iwcm_id_private *cm_id_priv, | ||
674 | struct iw_cm_event *iw_event) | ||
675 | { | ||
676 | unsigned long flags; | ||
677 | int ret = 0; | ||
678 | |||
679 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
680 | |||
681 | /* | ||
682 | * We clear the CONNECT_WAIT bit here to allow the callback | ||
683 | * function to call iw_cm_disconnect. Calling iw_destroy_cm_id | ||
684 | * from a callback handler is not allowed. | ||
685 | */ | ||
686 | clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); | ||
687 | BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_RECV); | ||
688 | cm_id_priv->state = IW_CM_STATE_ESTABLISHED; | ||
689 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
690 | ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event); | ||
691 | wake_up_all(&cm_id_priv->connect_wait); | ||
692 | |||
693 | return ret; | ||
694 | } | ||
695 | |||
696 | /* | ||
697 | * Active Side: CM_ID <-- ESTABLISHED | ||
698 | * | ||
699 | * The app has called connect and is waiting for the established event to | ||
700 | * post it's requests to the server. This event will wake up anyone | ||
701 | * blocked in iw_cm_disconnect or iw_destroy_id. | ||
702 | */ | ||
703 | static int cm_conn_rep_handler(struct iwcm_id_private *cm_id_priv, | ||
704 | struct iw_cm_event *iw_event) | ||
705 | { | ||
706 | unsigned long flags; | ||
707 | int ret = 0; | ||
708 | |||
709 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
710 | /* | ||
711 | * Clear the connect wait bit so a callback function calling | ||
712 | * iw_cm_disconnect will not wait and deadlock this thread | ||
713 | */ | ||
714 | clear_bit(IWCM_F_CONNECT_WAIT, &cm_id_priv->flags); | ||
715 | BUG_ON(cm_id_priv->state != IW_CM_STATE_CONN_SENT); | ||
716 | if (iw_event->status == IW_CM_EVENT_STATUS_ACCEPTED) { | ||
717 | cm_id_priv->id.local_addr = iw_event->local_addr; | ||
718 | cm_id_priv->id.remote_addr = iw_event->remote_addr; | ||
719 | cm_id_priv->state = IW_CM_STATE_ESTABLISHED; | ||
720 | } else { | ||
721 | /* REJECTED or RESET */ | ||
722 | cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp); | ||
723 | cm_id_priv->qp = NULL; | ||
724 | cm_id_priv->state = IW_CM_STATE_IDLE; | ||
725 | } | ||
726 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
727 | ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event); | ||
728 | |||
729 | if (iw_event->private_data_len) | ||
730 | kfree(iw_event->private_data); | ||
731 | |||
732 | /* Wake up waiters on connect complete */ | ||
733 | wake_up_all(&cm_id_priv->connect_wait); | ||
734 | |||
735 | return ret; | ||
736 | } | ||
737 | |||
738 | /* | ||
739 | * CM_ID <-- CLOSING | ||
740 | * | ||
741 | * If in the ESTABLISHED state, move to CLOSING. | ||
742 | */ | ||
743 | static void cm_disconnect_handler(struct iwcm_id_private *cm_id_priv, | ||
744 | struct iw_cm_event *iw_event) | ||
745 | { | ||
746 | unsigned long flags; | ||
747 | |||
748 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
749 | if (cm_id_priv->state == IW_CM_STATE_ESTABLISHED) | ||
750 | cm_id_priv->state = IW_CM_STATE_CLOSING; | ||
751 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
752 | } | ||
753 | |||
754 | /* | ||
755 | * CM_ID <-- IDLE | ||
756 | * | ||
757 | * If in the ESTBLISHED or CLOSING states, the QP will have have been | ||
758 | * moved by the provider to the ERR state. Disassociate the CM_ID from | ||
759 | * the QP, move to IDLE, and remove the 'connected' reference. | ||
760 | * | ||
761 | * If in some other state, the cm_id was destroyed asynchronously. | ||
762 | * This is the last reference that will result in waking up | ||
763 | * the app thread blocked in iw_destroy_cm_id. | ||
764 | */ | ||
765 | static int cm_close_handler(struct iwcm_id_private *cm_id_priv, | ||
766 | struct iw_cm_event *iw_event) | ||
767 | { | ||
768 | unsigned long flags; | ||
769 | int ret = 0; | ||
770 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
771 | |||
772 | if (cm_id_priv->qp) { | ||
773 | cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp); | ||
774 | cm_id_priv->qp = NULL; | ||
775 | } | ||
776 | switch (cm_id_priv->state) { | ||
777 | case IW_CM_STATE_ESTABLISHED: | ||
778 | case IW_CM_STATE_CLOSING: | ||
779 | cm_id_priv->state = IW_CM_STATE_IDLE; | ||
780 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
781 | ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event); | ||
782 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
783 | break; | ||
784 | case IW_CM_STATE_DESTROYING: | ||
785 | break; | ||
786 | default: | ||
787 | BUG(); | ||
788 | } | ||
789 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
790 | |||
791 | return ret; | ||
792 | } | ||
793 | |||
794 | static int process_event(struct iwcm_id_private *cm_id_priv, | ||
795 | struct iw_cm_event *iw_event) | ||
796 | { | ||
797 | int ret = 0; | ||
798 | |||
799 | switch (iw_event->event) { | ||
800 | case IW_CM_EVENT_CONNECT_REQUEST: | ||
801 | cm_conn_req_handler(cm_id_priv, iw_event); | ||
802 | break; | ||
803 | case IW_CM_EVENT_CONNECT_REPLY: | ||
804 | ret = cm_conn_rep_handler(cm_id_priv, iw_event); | ||
805 | break; | ||
806 | case IW_CM_EVENT_ESTABLISHED: | ||
807 | ret = cm_conn_est_handler(cm_id_priv, iw_event); | ||
808 | break; | ||
809 | case IW_CM_EVENT_DISCONNECT: | ||
810 | cm_disconnect_handler(cm_id_priv, iw_event); | ||
811 | break; | ||
812 | case IW_CM_EVENT_CLOSE: | ||
813 | ret = cm_close_handler(cm_id_priv, iw_event); | ||
814 | break; | ||
815 | default: | ||
816 | BUG(); | ||
817 | } | ||
818 | |||
819 | return ret; | ||
820 | } | ||
821 | |||
822 | /* | ||
823 | * Process events on the work_list for the cm_id. If the callback | ||
824 | * function requests that the cm_id be deleted, a flag is set in the | ||
825 | * cm_id flags to indicate that when the last reference is | ||
826 | * removed, the cm_id is to be destroyed. This is necessary to | ||
827 | * distinguish between an object that will be destroyed by the app | ||
828 | * thread asleep on the destroy_comp list vs. an object destroyed | ||
829 | * here synchronously when the last reference is removed. | ||
830 | */ | ||
831 | static void cm_work_handler(void *arg) | ||
832 | { | ||
833 | struct iwcm_work *work = arg, lwork; | ||
834 | struct iwcm_id_private *cm_id_priv = work->cm_id; | ||
835 | unsigned long flags; | ||
836 | int empty; | ||
837 | int ret = 0; | ||
838 | |||
839 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
840 | empty = list_empty(&cm_id_priv->work_list); | ||
841 | while (!empty) { | ||
842 | work = list_entry(cm_id_priv->work_list.next, | ||
843 | struct iwcm_work, list); | ||
844 | list_del_init(&work->list); | ||
845 | empty = list_empty(&cm_id_priv->work_list); | ||
846 | lwork = *work; | ||
847 | put_work(work); | ||
848 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
849 | |||
850 | ret = process_event(cm_id_priv, &work->event); | ||
851 | if (ret) { | ||
852 | set_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags); | ||
853 | destroy_cm_id(&cm_id_priv->id); | ||
854 | } | ||
855 | BUG_ON(atomic_read(&cm_id_priv->refcount)==0); | ||
856 | if (iwcm_deref_id(cm_id_priv)) | ||
857 | return; | ||
858 | |||
859 | if (atomic_read(&cm_id_priv->refcount)==0 && | ||
860 | test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags)) { | ||
861 | dealloc_work_entries(cm_id_priv); | ||
862 | kfree(cm_id_priv); | ||
863 | return; | ||
864 | } | ||
865 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
866 | } | ||
867 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
868 | } | ||
869 | |||
870 | /* | ||
871 | * This function is called on interrupt context. Schedule events on | ||
872 | * the iwcm_wq thread to allow callback functions to downcall into | ||
873 | * the CM and/or block. Events are queued to a per-CM_ID | ||
874 | * work_list. If this is the first event on the work_list, the work | ||
875 | * element is also queued on the iwcm_wq thread. | ||
876 | * | ||
877 | * Each event holds a reference on the cm_id. Until the last posted | ||
878 | * event has been delivered and processed, the cm_id cannot be | ||
879 | * deleted. | ||
880 | * | ||
881 | * Returns: | ||
882 | * 0 - the event was handled. | ||
883 | * -ENOMEM - the event was not handled due to lack of resources. | ||
884 | */ | ||
885 | static int cm_event_handler(struct iw_cm_id *cm_id, | ||
886 | struct iw_cm_event *iw_event) | ||
887 | { | ||
888 | struct iwcm_work *work; | ||
889 | struct iwcm_id_private *cm_id_priv; | ||
890 | unsigned long flags; | ||
891 | int ret = 0; | ||
892 | |||
893 | cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); | ||
894 | |||
895 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
896 | work = get_work(cm_id_priv); | ||
897 | if (!work) { | ||
898 | ret = -ENOMEM; | ||
899 | goto out; | ||
900 | } | ||
901 | |||
902 | INIT_WORK(&work->work, cm_work_handler, work); | ||
903 | work->cm_id = cm_id_priv; | ||
904 | work->event = *iw_event; | ||
905 | |||
906 | if ((work->event.event == IW_CM_EVENT_CONNECT_REQUEST || | ||
907 | work->event.event == IW_CM_EVENT_CONNECT_REPLY) && | ||
908 | work->event.private_data_len) { | ||
909 | ret = copy_private_data(cm_id_priv, &work->event); | ||
910 | if (ret) { | ||
911 | put_work(work); | ||
912 | goto out; | ||
913 | } | ||
914 | } | ||
915 | |||
916 | atomic_inc(&cm_id_priv->refcount); | ||
917 | if (list_empty(&cm_id_priv->work_list)) { | ||
918 | list_add_tail(&work->list, &cm_id_priv->work_list); | ||
919 | queue_work(iwcm_wq, &work->work); | ||
920 | } else | ||
921 | list_add_tail(&work->list, &cm_id_priv->work_list); | ||
922 | out: | ||
923 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
924 | return ret; | ||
925 | } | ||
926 | |||
927 | static int iwcm_init_qp_init_attr(struct iwcm_id_private *cm_id_priv, | ||
928 | struct ib_qp_attr *qp_attr, | ||
929 | int *qp_attr_mask) | ||
930 | { | ||
931 | unsigned long flags; | ||
932 | int ret; | ||
933 | |||
934 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
935 | switch (cm_id_priv->state) { | ||
936 | case IW_CM_STATE_IDLE: | ||
937 | case IW_CM_STATE_CONN_SENT: | ||
938 | case IW_CM_STATE_CONN_RECV: | ||
939 | case IW_CM_STATE_ESTABLISHED: | ||
940 | *qp_attr_mask = IB_QP_STATE | IB_QP_ACCESS_FLAGS; | ||
941 | qp_attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE | | ||
942 | IB_ACCESS_REMOTE_WRITE| | ||
943 | IB_ACCESS_REMOTE_READ; | ||
944 | ret = 0; | ||
945 | break; | ||
946 | default: | ||
947 | ret = -EINVAL; | ||
948 | break; | ||
949 | } | ||
950 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
951 | return ret; | ||
952 | } | ||
953 | |||
954 | static int iwcm_init_qp_rts_attr(struct iwcm_id_private *cm_id_priv, | ||
955 | struct ib_qp_attr *qp_attr, | ||
956 | int *qp_attr_mask) | ||
957 | { | ||
958 | unsigned long flags; | ||
959 | int ret; | ||
960 | |||
961 | spin_lock_irqsave(&cm_id_priv->lock, flags); | ||
962 | switch (cm_id_priv->state) { | ||
963 | case IW_CM_STATE_IDLE: | ||
964 | case IW_CM_STATE_CONN_SENT: | ||
965 | case IW_CM_STATE_CONN_RECV: | ||
966 | case IW_CM_STATE_ESTABLISHED: | ||
967 | *qp_attr_mask = 0; | ||
968 | ret = 0; | ||
969 | break; | ||
970 | default: | ||
971 | ret = -EINVAL; | ||
972 | break; | ||
973 | } | ||
974 | spin_unlock_irqrestore(&cm_id_priv->lock, flags); | ||
975 | return ret; | ||
976 | } | ||
977 | |||
978 | int iw_cm_init_qp_attr(struct iw_cm_id *cm_id, | ||
979 | struct ib_qp_attr *qp_attr, | ||
980 | int *qp_attr_mask) | ||
981 | { | ||
982 | struct iwcm_id_private *cm_id_priv; | ||
983 | int ret; | ||
984 | |||
985 | cm_id_priv = container_of(cm_id, struct iwcm_id_private, id); | ||
986 | switch (qp_attr->qp_state) { | ||
987 | case IB_QPS_INIT: | ||
988 | case IB_QPS_RTR: | ||
989 | ret = iwcm_init_qp_init_attr(cm_id_priv, | ||
990 | qp_attr, qp_attr_mask); | ||
991 | break; | ||
992 | case IB_QPS_RTS: | ||
993 | ret = iwcm_init_qp_rts_attr(cm_id_priv, | ||
994 | qp_attr, qp_attr_mask); | ||
995 | break; | ||
996 | default: | ||
997 | ret = -EINVAL; | ||
998 | break; | ||
999 | } | ||
1000 | return ret; | ||
1001 | } | ||
1002 | EXPORT_SYMBOL(iw_cm_init_qp_attr); | ||
1003 | |||
1004 | static int __init iw_cm_init(void) | ||
1005 | { | ||
1006 | iwcm_wq = create_singlethread_workqueue("iw_cm_wq"); | ||
1007 | if (!iwcm_wq) | ||
1008 | return -ENOMEM; | ||
1009 | |||
1010 | return 0; | ||
1011 | } | ||
1012 | |||
1013 | static void __exit iw_cm_cleanup(void) | ||
1014 | { | ||
1015 | destroy_workqueue(iwcm_wq); | ||
1016 | } | ||
1017 | |||
1018 | module_init(iw_cm_init); | ||
1019 | module_exit(iw_cm_cleanup); | ||
diff --git a/drivers/infiniband/core/iwcm.h b/drivers/infiniband/core/iwcm.h new file mode 100644 index 000000000000..3f6cc82564c8 --- /dev/null +++ b/drivers/infiniband/core/iwcm.h | |||
@@ -0,0 +1,62 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2005 Network Appliance, Inc. All rights reserved. | ||
3 | * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. | ||
4 | * | ||
5 | * This software is available to you under a choice of one of two | ||
6 | * licenses. You may choose to be licensed under the terms of the GNU | ||
7 | * General Public License (GPL) Version 2, available from the file | ||
8 | * COPYING in the main directory of this source tree, or the | ||
9 | * OpenIB.org BSD license below: | ||
10 | * | ||
11 | * Redistribution and use in source and binary forms, with or | ||
12 | * without modification, are permitted provided that the following | ||
13 | * conditions are met: | ||
14 | * | ||
15 | * - Redistributions of source code must retain the above | ||
16 | * copyright notice, this list of conditions and the following | ||
17 | * disclaimer. | ||
18 | * | ||
19 | * - Redistributions in binary form must reproduce the above | ||
20 | * copyright notice, this list of conditions and the following | ||
21 | * disclaimer in the documentation and/or other materials | ||
22 | * provided with the distribution. | ||
23 | * | ||
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
31 | * SOFTWARE. | ||
32 | */ | ||
33 | #ifndef IWCM_H | ||
34 | #define IWCM_H | ||
35 | |||
36 | enum iw_cm_state { | ||
37 | IW_CM_STATE_IDLE, /* unbound, inactive */ | ||
38 | IW_CM_STATE_LISTEN, /* listen waiting for connect */ | ||
39 | IW_CM_STATE_CONN_RECV, /* inbound waiting for user accept */ | ||
40 | IW_CM_STATE_CONN_SENT, /* outbound waiting for peer accept */ | ||
41 | IW_CM_STATE_ESTABLISHED, /* established */ | ||
42 | IW_CM_STATE_CLOSING, /* disconnect */ | ||
43 | IW_CM_STATE_DESTROYING /* object being deleted */ | ||
44 | }; | ||
45 | |||
46 | struct iwcm_id_private { | ||
47 | struct iw_cm_id id; | ||
48 | enum iw_cm_state state; | ||
49 | unsigned long flags; | ||
50 | struct ib_qp *qp; | ||
51 | struct completion destroy_comp; | ||
52 | wait_queue_head_t connect_wait; | ||
53 | struct list_head work_list; | ||
54 | spinlock_t lock; | ||
55 | atomic_t refcount; | ||
56 | struct list_head work_free_list; | ||
57 | }; | ||
58 | |||
59 | #define IWCM_F_CALLBACK_DESTROY 1 | ||
60 | #define IWCM_F_CONNECT_WAIT 2 | ||
61 | |||
62 | #endif /* IWCM_H */ | ||
diff --git a/include/rdma/iw_cm.h b/include/rdma/iw_cm.h new file mode 100644 index 000000000000..aeefa9b740dc --- /dev/null +++ b/include/rdma/iw_cm.h | |||
@@ -0,0 +1,258 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2005 Network Appliance, Inc. All rights reserved. | ||
3 | * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved. | ||
4 | * | ||
5 | * This software is available to you under a choice of one of two | ||
6 | * licenses. You may choose to be licensed under the terms of the GNU | ||
7 | * General Public License (GPL) Version 2, available from the file | ||
8 | * COPYING in the main directory of this source tree, or the | ||
9 | * OpenIB.org BSD license below: | ||
10 | * | ||
11 | * Redistribution and use in source and binary forms, with or | ||
12 | * without modification, are permitted provided that the following | ||
13 | * conditions are met: | ||
14 | * | ||
15 | * - Redistributions of source code must retain the above | ||
16 | * copyright notice, this list of conditions and the following | ||
17 | * disclaimer. | ||
18 | * | ||
19 | * - Redistributions in binary form must reproduce the above | ||
20 | * copyright notice, this list of conditions and the following | ||
21 | * disclaimer in the documentation and/or other materials | ||
22 | * provided with the distribution. | ||
23 | * | ||
24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
25 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
26 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
27 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | ||
28 | * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | ||
29 | * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | ||
30 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
31 | * SOFTWARE. | ||
32 | */ | ||
33 | #ifndef IW_CM_H | ||
34 | #define IW_CM_H | ||
35 | |||
36 | #include <linux/in.h> | ||
37 | #include <rdma/ib_cm.h> | ||
38 | |||
39 | struct iw_cm_id; | ||
40 | |||
41 | enum iw_cm_event_type { | ||
42 | IW_CM_EVENT_CONNECT_REQUEST = 1, /* connect request received */ | ||
43 | IW_CM_EVENT_CONNECT_REPLY, /* reply from active connect request */ | ||
44 | IW_CM_EVENT_ESTABLISHED, /* passive side accept successful */ | ||
45 | IW_CM_EVENT_DISCONNECT, /* orderly shutdown */ | ||
46 | IW_CM_EVENT_CLOSE /* close complete */ | ||
47 | }; | ||
48 | |||
49 | enum iw_cm_event_status { | ||
50 | IW_CM_EVENT_STATUS_OK = 0, /* request successful */ | ||
51 | IW_CM_EVENT_STATUS_ACCEPTED = 0, /* connect request accepted */ | ||
52 | IW_CM_EVENT_STATUS_REJECTED, /* connect request rejected */ | ||
53 | IW_CM_EVENT_STATUS_TIMEOUT, /* the operation timed out */ | ||
54 | IW_CM_EVENT_STATUS_RESET, /* reset from remote peer */ | ||
55 | IW_CM_EVENT_STATUS_EINVAL, /* asynchronous failure for bad parm */ | ||
56 | }; | ||
57 | |||
58 | struct iw_cm_event { | ||
59 | enum iw_cm_event_type event; | ||
60 | enum iw_cm_event_status status; | ||
61 | struct sockaddr_in local_addr; | ||
62 | struct sockaddr_in remote_addr; | ||
63 | void *private_data; | ||
64 | u8 private_data_len; | ||
65 | void* provider_data; | ||
66 | }; | ||
67 | |||
68 | /** | ||
69 | * iw_cm_handler - Function to be called by the IW CM when delivering events | ||
70 | * to the client. | ||
71 | * | ||
72 | * @cm_id: The IW CM identifier associated with the event. | ||
73 | * @event: Pointer to the event structure. | ||
74 | */ | ||
75 | typedef int (*iw_cm_handler)(struct iw_cm_id *cm_id, | ||
76 | struct iw_cm_event *event); | ||
77 | |||
78 | /** | ||
79 | * iw_event_handler - Function called by the provider when delivering provider | ||
80 | * events to the IW CM. Returns either 0 indicating the event was processed | ||
81 | * or -errno if the event could not be processed. | ||
82 | * | ||
83 | * @cm_id: The IW CM identifier associated with the event. | ||
84 | * @event: Pointer to the event structure. | ||
85 | */ | ||
86 | typedef int (*iw_event_handler)(struct iw_cm_id *cm_id, | ||
87 | struct iw_cm_event *event); | ||
88 | |||
89 | struct iw_cm_id { | ||
90 | iw_cm_handler cm_handler; /* client callback function */ | ||
91 | void *context; /* client cb context */ | ||
92 | struct ib_device *device; | ||
93 | struct sockaddr_in local_addr; | ||
94 | struct sockaddr_in remote_addr; | ||
95 | void *provider_data; /* provider private data */ | ||
96 | iw_event_handler event_handler; /* cb for provider | ||
97 | events */ | ||
98 | /* Used by provider to add and remove refs on IW cm_id */ | ||
99 | void (*add_ref)(struct iw_cm_id *); | ||
100 | void (*rem_ref)(struct iw_cm_id *); | ||
101 | }; | ||
102 | |||
103 | struct iw_cm_conn_param { | ||
104 | const void *private_data; | ||
105 | u16 private_data_len; | ||
106 | u32 ord; | ||
107 | u32 ird; | ||
108 | u32 qpn; | ||
109 | }; | ||
110 | |||
111 | struct iw_cm_verbs { | ||
112 | void (*add_ref)(struct ib_qp *qp); | ||
113 | |||
114 | void (*rem_ref)(struct ib_qp *qp); | ||
115 | |||
116 | struct ib_qp * (*get_qp)(struct ib_device *device, | ||
117 | int qpn); | ||
118 | |||
119 | int (*connect)(struct iw_cm_id *cm_id, | ||
120 | struct iw_cm_conn_param *conn_param); | ||
121 | |||
122 | int (*accept)(struct iw_cm_id *cm_id, | ||
123 | struct iw_cm_conn_param *conn_param); | ||
124 | |||
125 | int (*reject)(struct iw_cm_id *cm_id, | ||
126 | const void *pdata, u8 pdata_len); | ||
127 | |||
128 | int (*create_listen)(struct iw_cm_id *cm_id, | ||
129 | int backlog); | ||
130 | |||
131 | int (*destroy_listen)(struct iw_cm_id *cm_id); | ||
132 | }; | ||
133 | |||
134 | /** | ||
135 | * iw_create_cm_id - Create an IW CM identifier. | ||
136 | * | ||
137 | * @device: The IB device on which to create the IW CM identier. | ||
138 | * @event_handler: User callback invoked to report events associated with the | ||
139 | * returned IW CM identifier. | ||
140 | * @context: User specified context associated with the id. | ||
141 | */ | ||
142 | struct iw_cm_id *iw_create_cm_id(struct ib_device *device, | ||
143 | iw_cm_handler cm_handler, void *context); | ||
144 | |||
145 | /** | ||
146 | * iw_destroy_cm_id - Destroy an IW CM identifier. | ||
147 | * | ||
148 | * @cm_id: The previously created IW CM identifier to destroy. | ||
149 | * | ||
150 | * The client can assume that no events will be delivered for the CM ID after | ||
151 | * this function returns. | ||
152 | */ | ||
153 | void iw_destroy_cm_id(struct iw_cm_id *cm_id); | ||
154 | |||
155 | /** | ||
156 | * iw_cm_bind_qp - Unbind the specified IW CM identifier and QP | ||
157 | * | ||
158 | * @cm_id: The IW CM idenfier to unbind from the QP. | ||
159 | * @qp: The QP | ||
160 | * | ||
161 | * This is called by the provider when destroying the QP to ensure | ||
162 | * that any references held by the IWCM are released. It may also | ||
163 | * be called by the IWCM when destroying a CM_ID to that any | ||
164 | * references held by the provider are released. | ||
165 | */ | ||
166 | void iw_cm_unbind_qp(struct iw_cm_id *cm_id, struct ib_qp *qp); | ||
167 | |||
168 | /** | ||
169 | * iw_cm_get_qp - Return the ib_qp associated with a QPN | ||
170 | * | ||
171 | * @ib_device: The IB device | ||
172 | * @qpn: The queue pair number | ||
173 | */ | ||
174 | struct ib_qp *iw_cm_get_qp(struct ib_device *device, int qpn); | ||
175 | |||
176 | /** | ||
177 | * iw_cm_listen - Listen for incoming connection requests on the | ||
178 | * specified IW CM id. | ||
179 | * | ||
180 | * @cm_id: The IW CM identifier. | ||
181 | * @backlog: The maximum number of outstanding un-accepted inbound listen | ||
182 | * requests to queue. | ||
183 | * | ||
184 | * The source address and port number are specified in the IW CM identifier | ||
185 | * structure. | ||
186 | */ | ||
187 | int iw_cm_listen(struct iw_cm_id *cm_id, int backlog); | ||
188 | |||
189 | /** | ||
190 | * iw_cm_accept - Called to accept an incoming connect request. | ||
191 | * | ||
192 | * @cm_id: The IW CM identifier associated with the connection request. | ||
193 | * @iw_param: Pointer to a structure containing connection establishment | ||
194 | * parameters. | ||
195 | * | ||
196 | * The specified cm_id will have been provided in the event data for a | ||
197 | * CONNECT_REQUEST event. Subsequent events related to this connection will be | ||
198 | * delivered to the specified IW CM identifier prior and may occur prior to | ||
199 | * the return of this function. If this function returns a non-zero value, the | ||
200 | * client can assume that no events will be delivered to the specified IW CM | ||
201 | * identifier. | ||
202 | */ | ||
203 | int iw_cm_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param); | ||
204 | |||
205 | /** | ||
206 | * iw_cm_reject - Reject an incoming connection request. | ||
207 | * | ||
208 | * @cm_id: Connection identifier associated with the request. | ||
209 | * @private_daa: Pointer to data to deliver to the remote peer as part of the | ||
210 | * reject message. | ||
211 | * @private_data_len: The number of bytes in the private_data parameter. | ||
212 | * | ||
213 | * The client can assume that no events will be delivered to the specified IW | ||
214 | * CM identifier following the return of this function. The private_data | ||
215 | * buffer is available for reuse when this function returns. | ||
216 | */ | ||
217 | int iw_cm_reject(struct iw_cm_id *cm_id, const void *private_data, | ||
218 | u8 private_data_len); | ||
219 | |||
220 | /** | ||
221 | * iw_cm_connect - Called to request a connection to a remote peer. | ||
222 | * | ||
223 | * @cm_id: The IW CM identifier for the connection. | ||
224 | * @iw_param: Pointer to a structure containing connection establishment | ||
225 | * parameters. | ||
226 | * | ||
227 | * Events may be delivered to the specified IW CM identifier prior to the | ||
228 | * return of this function. If this function returns a non-zero value, the | ||
229 | * client can assume that no events will be delivered to the specified IW CM | ||
230 | * identifier. | ||
231 | */ | ||
232 | int iw_cm_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param); | ||
233 | |||
234 | /** | ||
235 | * iw_cm_disconnect - Close the specified connection. | ||
236 | * | ||
237 | * @cm_id: The IW CM identifier to close. | ||
238 | * @abrupt: If 0, the connection will be closed gracefully, otherwise, the | ||
239 | * connection will be reset. | ||
240 | * | ||
241 | * The IW CM identifier is still active until the IW_CM_EVENT_CLOSE event is | ||
242 | * delivered. | ||
243 | */ | ||
244 | int iw_cm_disconnect(struct iw_cm_id *cm_id, int abrupt); | ||
245 | |||
246 | /** | ||
247 | * iw_cm_init_qp_attr - Called to initialize the attributes of the QP | ||
248 | * associated with a IW CM identifier. | ||
249 | * | ||
250 | * @cm_id: The IW CM identifier associated with the QP | ||
251 | * @qp_attr: Pointer to the QP attributes structure. | ||
252 | * @qp_attr_mask: Pointer to a bit vector specifying which QP attributes are | ||
253 | * valid. | ||
254 | */ | ||
255 | int iw_cm_init_qp_attr(struct iw_cm_id *cm_id, struct ib_qp_attr *qp_attr, | ||
256 | int *qp_attr_mask); | ||
257 | |||
258 | #endif /* IW_CM_H */ | ||