1178784Skmacy/*
2178784Skmacy * Copyright (c) 2004, 2005 Intel Corporation.  All rights reserved.
3178784Skmacy * Copyright (c) 2004 Topspin Corporation.  All rights reserved.
4178784Skmacy * Copyright (c) 2004, 2005 Voltaire Corporation.  All rights reserved.
5178784Skmacy * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved.
6178784Skmacy * Copyright (c) 2005 Open Grid Computing, Inc. All rights reserved.
7178784Skmacy * Copyright (c) 2005 Network Appliance, Inc. All rights reserved.
8178784Skmacy *
9178784Skmacy * This software is available to you under a choice of one of two
10178784Skmacy * licenses.  You may choose to be licensed under the terms of the GNU
11178784Skmacy * General Public License (GPL) Version 2, available from the file
12178784Skmacy * COPYING in the main directory of this source tree, or the
13178784Skmacy * OpenIB.org BSD license below:
14178784Skmacy *
15178784Skmacy *     Redistribution and use in source and binary forms, with or
16178784Skmacy *     without modification, are permitted provided that the following
17178784Skmacy *     conditions are met:
18178784Skmacy *
19178784Skmacy *      - Redistributions of source code must retain the above
20178784Skmacy *        copyright notice, this list of conditions and the following
21178784Skmacy *        disclaimer.
22178784Skmacy *
23178784Skmacy *      - Redistributions in binary form must reproduce the above
24178784Skmacy *        copyright notice, this list of conditions and the following
25178784Skmacy *        disclaimer in the documentation and/or other materials
26178784Skmacy *        provided with the distribution.
27178784Skmacy *
28178784Skmacy * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29178784Skmacy * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30178784Skmacy * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31178784Skmacy * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
32178784Skmacy * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
33178784Skmacy * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
34178784Skmacy * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35178784Skmacy * SOFTWARE.
36178784Skmacy *
37178784Skmacy */
38178784Skmacy
39178784Skmacy#include <sys/cdefs.h>
40178784Skmacy__FBSDID("$FreeBSD$");
41178784Skmacy
42178784Skmacy#include <sys/types.h>
43178784Skmacy#include <sys/param.h>
44178784Skmacy#include <sys/systm.h>
45178784Skmacy#include <sys/proc.h>
46178784Skmacy#include <sys/kernel.h>
47178784Skmacy#include <sys/libkern.h>
48178784Skmacy#include <sys/socket.h>
49178784Skmacy#include <sys/socketvar.h>
50178784Skmacy#include <sys/module.h>
51178784Skmacy#include <sys/lock.h>
52178784Skmacy#include <sys/mutex.h>
53178784Skmacy#include <sys/rwlock.h>
54178784Skmacy#include <sys/queue.h>
55178784Skmacy#include <sys/taskqueue.h>
56178784Skmacy#include <sys/priv.h>
57178784Skmacy#include <sys/syslog.h>
58178784Skmacy#include <sys/malloc.h>
59178784Skmacy
60178784Skmacy#include <netinet/in.h>
61178784Skmacy#include <netinet/in_pcb.h>
62178784Skmacy
63178784Skmacy#include <contrib/rdma/iw_cm.h>
64178784Skmacy
65178784Skmacyenum iw_cm_state {
66178784Skmacy	IW_CM_STATE_IDLE,             /* unbound, inactive */
67178784Skmacy	IW_CM_STATE_LISTEN,           /* listen waiting for connect */
68178784Skmacy	IW_CM_STATE_CONN_RECV,        /* inbound waiting for user accept */
69178784Skmacy	IW_CM_STATE_CONN_SENT,        /* outbound waiting for peer accept */
70178784Skmacy	IW_CM_STATE_ESTABLISHED,      /* established */
71178784Skmacy	IW_CM_STATE_CLOSING,	      /* disconnect */
72178784Skmacy	IW_CM_STATE_DESTROYING        /* object being deleted */
73178784Skmacy};
74178784Skmacy
75178784Skmacystruct iwcm_id_private {
76178784Skmacy	struct iw_cm_id	id;
77178784Skmacy	enum iw_cm_state state;
78178784Skmacy	unsigned long flags;
79178784Skmacy	struct ib_qp *qp;
80178784Skmacy	void * destroy_comp;
81178784Skmacy	void * connect_wait;
82178784Skmacy	TAILQ_HEAD(, iwcm_work) work_list;
83178784Skmacy	struct mtx lock;
84178784Skmacy	volatile int refcount;
85178784Skmacy	TAILQ_HEAD(, iwcm_work) work_free_list;
86178784Skmacy};
87178784Skmacy
88178784Skmacy#define IWCM_F_CALLBACK_DESTROY   1
89178784Skmacy#define IWCM_F_CONNECT_WAIT       2
90178784Skmacy
91178784Skmacystatic struct taskqueue *iwcm_wq;
92178784Skmacystruct iwcm_work {
93178784Skmacy	struct task task;
94178784Skmacy	struct iwcm_id_private *cm_id;
95178784Skmacy	TAILQ_ENTRY(iwcm_work) list;
96178784Skmacy	struct iw_cm_event event;
97178784Skmacy	TAILQ_ENTRY(iwcm_work) free_list;
98178784Skmacy};
99178784Skmacy
100178784Skmacy/*
101178784Skmacy * The following services provide a mechanism for pre-allocating iwcm_work
102178784Skmacy * elements.  The design pre-allocates them  based on the cm_id type:
103178784Skmacy *	LISTENING IDS: 	Get enough elements preallocated to handle the
104178784Skmacy *			listen backlog.
105178784Skmacy *	ACTIVE IDS:	4: CONNECT_REPLY, ESTABLISHED, DISCONNECT, CLOSE
106178784Skmacy *	PASSIVE IDS:	3: ESTABLISHED, DISCONNECT, CLOSE
107178784Skmacy *
108178784Skmacy * Allocating them in connect and listen avoids having to deal
109178784Skmacy * with allocation failures on the event upcall from the provider (which
110178784Skmacy * is called in the interrupt context).
111178784Skmacy *
112178784Skmacy * One exception is when creating the cm_id for incoming connection requests.
113178784Skmacy * There are two cases:
114178784Skmacy * 1) in the event upcall, cm_event_handler(), for a listening cm_id.  If
115178784Skmacy *    the backlog is exceeded, then no more connection request events will
116178784Skmacy *    be processed.  cm_event_handler() returns ENOMEM in this case.  Its up
117178784Skmacy *    to the provider to reject the connection request.
118178784Skmacy * 2) in the connection request workqueue handler, cm_conn_req_handler().
119178784Skmacy *    If work elements cannot be allocated for the new connect request cm_id,
120178784Skmacy *    then IWCM will call the provider reject method.  This is ok since
121178784Skmacy *    cm_conn_req_handler() runs in the workqueue thread context.
122178784Skmacy */
123178784Skmacy
124178784Skmacystatic struct iwcm_work *get_work(struct iwcm_id_private *cm_id_priv)
125178784Skmacy{
126178784Skmacy	struct iwcm_work *work;
127178784Skmacy
128178784Skmacy	if (TAILQ_EMPTY(&cm_id_priv->work_free_list))
129178784Skmacy		return NULL;
130178784Skmacy	work = TAILQ_FIRST(&cm_id_priv->work_free_list);
131178784Skmacy	TAILQ_REMOVE(&cm_id_priv->work_free_list, work, free_list);
132178784Skmacy	return work;
133178784Skmacy}
134178784Skmacy
135178784Skmacystatic void put_work(struct iwcm_work *work)
136178784Skmacy{
137178784Skmacy	TAILQ_INSERT_HEAD(&work->cm_id->work_free_list, work, free_list);
138178784Skmacy}
139178784Skmacy
140178784Skmacystatic void dealloc_work_entries(struct iwcm_id_private *cm_id_priv)
141178784Skmacy{
142178784Skmacy	struct iwcm_work *e, *tmp;
143178784Skmacy
144178784Skmacy	TAILQ_FOREACH_SAFE(e, &cm_id_priv->work_free_list, free_list, tmp)
145178784Skmacy		free(e, M_DEVBUF);
146178784Skmacy}
147178784Skmacy
148178784Skmacystatic int alloc_work_entries(struct iwcm_id_private *cm_id_priv, int count)
149178784Skmacy{
150178784Skmacy	struct iwcm_work *work;
151178784Skmacy
152178784Skmacy	PANIC_IF(!TAILQ_EMPTY(&cm_id_priv->work_free_list));
153178784Skmacy	while (count--) {
154178784Skmacy		work = malloc(sizeof(struct iwcm_work), M_DEVBUF, M_NOWAIT);
155178784Skmacy		if (!work) {
156178784Skmacy			dealloc_work_entries(cm_id_priv);
157178784Skmacy			return (ENOMEM);
158178784Skmacy		}
159178784Skmacy		work->cm_id = cm_id_priv;
160178784Skmacy		put_work(work);
161178784Skmacy	}
162178784Skmacy	return 0;
163178784Skmacy}
164178784Skmacy
165178784Skmacy/*
166178784Skmacy * Save private data from incoming connection requests to
167178784Skmacy * iw_cm_event, so the low level driver doesn't have to. Adjust
168178784Skmacy * the event ptr to point to the local copy.
169178784Skmacy */
170178784Skmacystatic int copy_private_data(struct iw_cm_event *event)
171178784Skmacy{
172178784Skmacy	void *p;
173178784Skmacy
174178784Skmacy	p = malloc(event->private_data_len, M_DEVBUF, M_NOWAIT);
175178784Skmacy	if (!p)
176178784Skmacy		return (ENOMEM);
177178784Skmacy	bcopy(event->private_data, p, event->private_data_len);
178178784Skmacy	event->private_data = p;
179178784Skmacy	return 0;
180178784Skmacy}
181178784Skmacy
182178784Skmacystatic void free_cm_id(struct iwcm_id_private *cm_id_priv)
183178784Skmacy{
184178784Skmacy	dealloc_work_entries(cm_id_priv);
185178784Skmacy	free(cm_id_priv, M_DEVBUF);
186178784Skmacy}
187178784Skmacy
188178784Skmacy/*
189178784Skmacy * Release a reference on cm_id. If the last reference is being
190178784Skmacy * released, enable the waiting thread (in iw_destroy_cm_id) to
191178784Skmacy * get woken up, and return 1 if a thread is already waiting.
192178784Skmacy */
193178784Skmacystatic int iwcm_deref_id(struct iwcm_id_private *cm_id_priv)
194178784Skmacy{
195178784Skmacy	mtx_lock(&cm_id_priv->lock);
196178784Skmacy	PANIC_IF(atomic_load_acq_int(&cm_id_priv->refcount)==0);
197178784Skmacy	if (atomic_fetchadd_int(&cm_id_priv->refcount, -1) == 1) {
198178784Skmacy		PANIC_IF(!TAILQ_EMPTY(&cm_id_priv->work_list));
199178784Skmacy		wakeup(&cm_id_priv->destroy_comp);
200178784Skmacy		mtx_unlock(&cm_id_priv->lock);
201178784Skmacy		return 1;
202178784Skmacy	}
203178784Skmacy	mtx_unlock(&cm_id_priv->lock);
204178784Skmacy
205178784Skmacy	return 0;
206178784Skmacy}
207178784Skmacy
208178784Skmacystatic void add_ref(struct iw_cm_id *cm_id)
209178784Skmacy{
210178784Skmacy	struct iwcm_id_private *cm_id_priv;
211178784Skmacy	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
212178784Skmacy	mtx_lock(&cm_id_priv->lock);
213178784Skmacy	atomic_add_int(&cm_id_priv->refcount, 1);
214178784Skmacy	mtx_unlock(&cm_id_priv->lock);
215178784Skmacy}
216178784Skmacy
217178784Skmacystatic void rem_ref(struct iw_cm_id *cm_id)
218178784Skmacy{
219178784Skmacy	struct iwcm_id_private *cm_id_priv;
220178784Skmacy	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
221178784Skmacy	if (iwcm_deref_id(cm_id_priv) &&
222178784Skmacy	    isset(&cm_id_priv->flags, IWCM_F_CALLBACK_DESTROY)) {
223178784Skmacy		PANIC_IF(!TAILQ_EMPTY(&cm_id_priv->work_list));
224178784Skmacy		free_cm_id(cm_id_priv);
225178784Skmacy	}
226178784Skmacy}
227178784Skmacy
228178784Skmacystatic int cm_event_handler(struct iw_cm_id *cm_id, struct iw_cm_event *event);
229178784Skmacy
230178784Skmacystruct iw_cm_id *iw_create_cm_id(struct ib_device *device,
231178784Skmacy				 struct socket *so,
232178784Skmacy				 iw_cm_handler cm_handler,
233178784Skmacy				 void *context)
234178784Skmacy{
235178784Skmacy	struct iwcm_id_private *cm_id_priv;
236178784Skmacy
237178784Skmacy	KASSERT(so, ("iw_create_cm_id called with NULL socket!"));
238178784Skmacy	cm_id_priv = malloc(sizeof(*cm_id_priv), M_DEVBUF, M_NOWAIT);
239178784Skmacy	if (!cm_id_priv)
240178784Skmacy		return ERR_PTR(ENOMEM);
241178784Skmacy	bzero(cm_id_priv, sizeof *cm_id_priv);
242178784Skmacy
243178784Skmacy	cm_id_priv->state = IW_CM_STATE_IDLE;
244178784Skmacy	cm_id_priv->id.device = device;
245178784Skmacy	cm_id_priv->id.cm_handler = cm_handler;
246178784Skmacy	cm_id_priv->id.context = context;
247178784Skmacy	cm_id_priv->id.event_handler = cm_event_handler;
248178784Skmacy	cm_id_priv->id.add_ref = add_ref;
249178784Skmacy	cm_id_priv->id.rem_ref = rem_ref;
250178784Skmacy	cm_id_priv->id.so = so;
251178784Skmacy	mtx_init(&cm_id_priv->lock, "cm_id_priv", NULL, MTX_DUPOK|MTX_DEF);
252178784Skmacy	atomic_store_rel_int(&cm_id_priv->refcount, 1);
253178784Skmacy	TAILQ_INIT(&cm_id_priv->work_list);
254178784Skmacy	TAILQ_INIT(&cm_id_priv->work_free_list);
255178784Skmacy
256178784Skmacy	return &cm_id_priv->id;
257178784Skmacy}
258178784Skmacy
259178784Skmacy
260178784Skmacystatic int iwcm_modify_qp_err(struct ib_qp *qp)
261178784Skmacy{
262178784Skmacy	struct ib_qp_attr qp_attr;
263178784Skmacy
264178784Skmacy	if (!qp)
265178784Skmacy		return (EINVAL);
266178784Skmacy
267178784Skmacy	qp_attr.qp_state = IB_QPS_ERR;
268178784Skmacy	return ib_modify_qp(qp, &qp_attr, IB_QP_STATE);
269178784Skmacy}
270178784Skmacy
271178784Skmacy/*
272178784Skmacy * This is really the RDMAC CLOSING state. It is most similar to the
273178784Skmacy * IB SQD QP state.
274178784Skmacy */
275178784Skmacystatic int iwcm_modify_qp_sqd(struct ib_qp *qp)
276178784Skmacy{
277178784Skmacy	struct ib_qp_attr qp_attr;
278178784Skmacy
279178784Skmacy	PANIC_IF(qp == NULL);
280178784Skmacy	qp_attr.qp_state = IB_QPS_SQD;
281178784Skmacy	return ib_modify_qp(qp, &qp_attr, IB_QP_STATE);
282178784Skmacy}
283178784Skmacy
284178784Skmacy/*
285178784Skmacy * CM_ID <-- CLOSING
286178784Skmacy *
287178784Skmacy * Block if a passive or active connection is currently being processed. Then
288178784Skmacy * process the event as follows:
289178784Skmacy * - If we are ESTABLISHED, move to CLOSING and modify the QP state
290178784Skmacy *   based on the abrupt flag
291178784Skmacy * - If the connection is already in the CLOSING or IDLE state, the peer is
292178784Skmacy *   disconnecting concurrently with us and we've already seen the
293178784Skmacy *   DISCONNECT event -- ignore the request and return 0
294178784Skmacy * - Disconnect on a listening endpoint returns EINVAL
295178784Skmacy */
296178784Skmacyint iw_cm_disconnect(struct iw_cm_id *cm_id, int abrupt)
297178784Skmacy{
298178784Skmacy	struct iwcm_id_private *cm_id_priv;
299178784Skmacy	int ret = 0;
300178784Skmacy	struct ib_qp *qp = NULL;
301178784Skmacy
302178784Skmacy	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
303178784Skmacy	/* Wait if we're currently in a connect or accept downcall */
304178784Skmacy	mtx_lock(&cm_id_priv->lock);
305178784Skmacy	if (isset(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT))
306178784Skmacy		msleep(&cm_id_priv->connect_wait, &cm_id_priv->lock, 0, "iwcm connect1", 0);
307178784Skmacy
308178784Skmacy	switch (cm_id_priv->state) {
309178784Skmacy	case IW_CM_STATE_ESTABLISHED:
310178784Skmacy		cm_id_priv->state = IW_CM_STATE_CLOSING;
311178784Skmacy
312178784Skmacy		/* QP could be <nul> for user-mode client */
313178784Skmacy		if (cm_id_priv->qp)
314178784Skmacy			qp = cm_id_priv->qp;
315178784Skmacy		else
316178784Skmacy			ret = EINVAL;
317178784Skmacy		break;
318178784Skmacy	case IW_CM_STATE_LISTEN:
319178784Skmacy		ret = EINVAL;
320178784Skmacy		break;
321178784Skmacy	case IW_CM_STATE_CLOSING:
322178784Skmacy		/* remote peer closed first */
323178784Skmacy	case IW_CM_STATE_IDLE:
324178784Skmacy		/* accept or connect returned !0 */
325178784Skmacy		break;
326178784Skmacy	case IW_CM_STATE_CONN_RECV:
327178784Skmacy		/*
328178784Skmacy		 * App called disconnect before/without calling accept after
329178784Skmacy		 * connect_request event delivered.
330178784Skmacy		 */
331178784Skmacy		break;
332178784Skmacy	case IW_CM_STATE_CONN_SENT:
333178784Skmacy		/* Can only get here if wait above fails */
334178784Skmacy	default:
335178784Skmacy		panic("just cuz");
336178784Skmacy	}
337178784Skmacy	mtx_unlock(&cm_id_priv->lock);
338178784Skmacy
339178784Skmacy	if (qp) {
340178784Skmacy		if (abrupt)
341178784Skmacy			ret = iwcm_modify_qp_err(qp);
342178784Skmacy		else
343178784Skmacy			ret = iwcm_modify_qp_sqd(qp);
344178784Skmacy
345178784Skmacy		/*
346178784Skmacy		 * If both sides are disconnecting the QP could
347178784Skmacy		 * already be in ERR or SQD states
348178784Skmacy		 */
349178784Skmacy		ret = 0;
350178784Skmacy	}
351178784Skmacy
352178784Skmacy	return ret;
353178784Skmacy}
354178784Skmacy
355178784Skmacy/*
356178784Skmacy * CM_ID <-- DESTROYING
357178784Skmacy *
358178784Skmacy * Clean up all resources associated with the connection and release
359178784Skmacy * the initial reference taken by iw_create_cm_id.
360178784Skmacy */
361178784Skmacystatic void destroy_cm_id(struct iw_cm_id *cm_id)
362178784Skmacy{
363178784Skmacy	struct iwcm_id_private *cm_id_priv;
364178784Skmacy	int ret;
365178784Skmacy
366178784Skmacy	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
367178784Skmacy	/*
368178784Skmacy	 * Wait if we're currently in a connect or accept downcall. A
369178784Skmacy	 * listening endpoint should never block here.
370178784Skmacy	 */
371178784Skmacy	mtx_lock(&cm_id_priv->lock);
372178784Skmacy	if (isset(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT))
373178784Skmacy		msleep(&cm_id_priv->connect_wait, &cm_id_priv->lock, 0, "iwcm connect2", 0);
374178784Skmacy
375178784Skmacy	switch (cm_id_priv->state) {
376178784Skmacy	case IW_CM_STATE_LISTEN:
377178784Skmacy		cm_id_priv->state = IW_CM_STATE_DESTROYING;
378178784Skmacy		mtx_unlock(&cm_id_priv->lock);
379178784Skmacy		/* destroy the listening endpoint */
380178784Skmacy		ret = cm_id->device->iwcm->destroy_listen(cm_id);
381178784Skmacy		mtx_lock(&cm_id_priv->lock);
382178784Skmacy		break;
383178784Skmacy	case IW_CM_STATE_ESTABLISHED:
384178784Skmacy		cm_id_priv->state = IW_CM_STATE_DESTROYING;
385178784Skmacy		mtx_unlock(&cm_id_priv->lock);
386178784Skmacy		/* Abrupt close of the connection */
387178784Skmacy		(void)iwcm_modify_qp_err(cm_id_priv->qp);
388178784Skmacy		mtx_lock(&cm_id_priv->lock);
389178784Skmacy		break;
390178784Skmacy	case IW_CM_STATE_IDLE:
391178784Skmacy	case IW_CM_STATE_CLOSING:
392178784Skmacy		cm_id_priv->state = IW_CM_STATE_DESTROYING;
393178784Skmacy		break;
394178784Skmacy	case IW_CM_STATE_CONN_RECV:
395178784Skmacy		/*
396178784Skmacy		 * App called destroy before/without calling accept after
397178784Skmacy		 * receiving connection request event notification or
398178784Skmacy		 * returned non zero from the event callback function.
399178784Skmacy		 * In either case, must tell the provider to reject.
400178784Skmacy		 */
401178784Skmacy		cm_id_priv->state = IW_CM_STATE_DESTROYING;
402178784Skmacy		break;
403178784Skmacy	case IW_CM_STATE_CONN_SENT:
404178784Skmacy	case IW_CM_STATE_DESTROYING:
405178784Skmacy	default:
406178784Skmacy		panic("just cuz");
407178784Skmacy		break;
408178784Skmacy	}
409178784Skmacy	if (cm_id_priv->qp) {
410178784Skmacy		cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp);
411178784Skmacy		cm_id_priv->qp = NULL;
412178784Skmacy	}
413178784Skmacy	mtx_unlock(&cm_id_priv->lock);
414178784Skmacy
415178784Skmacy	(void)iwcm_deref_id(cm_id_priv);
416178784Skmacy}
417178784Skmacy
418178784Skmacy/*
419178784Skmacy * This function is only called by the application thread and cannot
420178784Skmacy * be called by the event thread. The function will wait for all
421178784Skmacy * references to be released on the cm_id and then free the cm_id
422178784Skmacy * object.
423178784Skmacy */
424178784Skmacyvoid iw_destroy_cm_id(struct iw_cm_id *cm_id)
425178784Skmacy{
426178784Skmacy	struct iwcm_id_private *cm_id_priv;
427178784Skmacy
428178784Skmacy	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
429178784Skmacy	PANIC_IF(isset(&cm_id_priv->flags, IWCM_F_CALLBACK_DESTROY));
430178784Skmacy
431178784Skmacy	destroy_cm_id(cm_id);
432178784Skmacy
433178784Skmacy	mtx_lock(&cm_id_priv->lock);
434178784Skmacy	if (atomic_load_acq_int(&cm_id_priv->refcount))
435178784Skmacy		msleep(&cm_id_priv->destroy_comp, &cm_id_priv->lock, 0, "iwcm destroy", 0);
436178784Skmacy	mtx_unlock(&cm_id_priv->lock);
437178784Skmacy
438178784Skmacy	free_cm_id(cm_id_priv);
439178784Skmacy}
440178784Skmacy
441178784Skmacy/*
442178784Skmacy * CM_ID <-- LISTEN
443178784Skmacy *
444178784Skmacy * Start listening for connect requests. Generates one CONNECT_REQUEST
445178784Skmacy * event for each inbound connect request.
446178784Skmacy */
447178784Skmacyint iw_cm_listen(struct iw_cm_id *cm_id, int backlog)
448178784Skmacy{
449178784Skmacy	struct iwcm_id_private *cm_id_priv;
450178784Skmacy	int ret;
451178784Skmacy
452178784Skmacy	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
453178784Skmacy
454178784Skmacy	ret = alloc_work_entries(cm_id_priv, backlog);
455178784Skmacy	if (ret)
456178784Skmacy		return ret;
457178784Skmacy
458178784Skmacy	mtx_lock(&cm_id_priv->lock);
459178784Skmacy	switch (cm_id_priv->state) {
460178784Skmacy	case IW_CM_STATE_IDLE:
461178784Skmacy		cm_id_priv->state = IW_CM_STATE_LISTEN;
462178784Skmacy		mtx_unlock(&cm_id_priv->lock);
463178784Skmacy		ret = cm_id->device->iwcm->create_listen(cm_id, backlog);
464178784Skmacy		if (ret)
465178784Skmacy			cm_id_priv->state = IW_CM_STATE_IDLE;
466178784Skmacy		mtx_lock(&cm_id_priv->lock);
467178784Skmacy		break;
468178784Skmacy	default:
469178784Skmacy		ret = EINVAL;
470178784Skmacy	}
471178784Skmacy	mtx_unlock(&cm_id_priv->lock);
472178784Skmacy
473178784Skmacy	return ret;
474178784Skmacy}
475178784Skmacy
476178784Skmacy/*
477178784Skmacy * CM_ID <-- IDLE
478178784Skmacy *
479178784Skmacy * Rejects an inbound connection request. No events are generated.
480178784Skmacy */
481178784Skmacyint iw_cm_reject(struct iw_cm_id *cm_id,
482178784Skmacy		 const void *private_data,
483178784Skmacy		 u8 private_data_len)
484178784Skmacy{
485178784Skmacy	struct iwcm_id_private *cm_id_priv;
486178784Skmacy	int ret;
487178784Skmacy
488178784Skmacy	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
489178784Skmacy	setbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT);
490178784Skmacy
491178784Skmacy	mtx_lock(&cm_id_priv->lock);
492178784Skmacy	if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) {
493178784Skmacy		clrbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT);
494178784Skmacy		wakeup(&cm_id_priv->connect_wait);
495178784Skmacy		mtx_unlock(&cm_id_priv->lock);
496178784Skmacy		return (EINVAL);
497178784Skmacy	}
498178784Skmacy	cm_id_priv->state = IW_CM_STATE_IDLE;
499178784Skmacy	mtx_unlock(&cm_id_priv->lock);
500178784Skmacy
501178784Skmacy	ret = cm_id->device->iwcm->reject(cm_id, private_data,
502178784Skmacy					  private_data_len);
503178784Skmacy
504178784Skmacy	mtx_lock(&cm_id_priv->lock);
505178784Skmacy	clrbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT);
506178784Skmacy	wakeup(&cm_id_priv->connect_wait);
507178784Skmacy	mtx_unlock(&cm_id_priv->lock);
508178784Skmacy
509178784Skmacy	return ret;
510178784Skmacy}
511178784Skmacy
512178784Skmacy/*
513178784Skmacy * CM_ID <-- ESTABLISHED
514178784Skmacy *
515178784Skmacy * Accepts an inbound connection request and generates an ESTABLISHED
516178784Skmacy * event. Callers of iw_cm_disconnect and iw_destroy_cm_id will block
517178784Skmacy * until the ESTABLISHED event is received from the provider.
518178784Skmacy */
519178784Skmacyint iw_cm_accept(struct iw_cm_id *cm_id,
520178784Skmacy		 struct iw_cm_conn_param *iw_param)
521178784Skmacy{
522178784Skmacy	struct iwcm_id_private *cm_id_priv;
523178784Skmacy	struct ib_qp *qp;
524178784Skmacy	int ret;
525178784Skmacy
526178784Skmacy	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
527178784Skmacy	setbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT);
528178784Skmacy
529178784Skmacy	mtx_lock(&cm_id_priv->lock);
530178784Skmacy	if (cm_id_priv->state != IW_CM_STATE_CONN_RECV) {
531178784Skmacy		clrbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT);
532178784Skmacy		wakeup(&cm_id_priv->connect_wait);
533178784Skmacy		mtx_unlock(&cm_id_priv->lock);
534178784Skmacy
535178784Skmacy		return (EINVAL);
536178784Skmacy	}
537178784Skmacy	/* Get the ib_qp given the QPN */
538178784Skmacy	qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn);
539178784Skmacy	if (!qp) {
540178784Skmacy		mtx_unlock(&cm_id_priv->lock);
541178784Skmacy		return (EINVAL);
542178784Skmacy	}
543178784Skmacy	cm_id->device->iwcm->add_ref(qp);
544178784Skmacy	cm_id_priv->qp = qp;
545178784Skmacy	mtx_unlock(&cm_id_priv->lock);
546178784Skmacy
547178784Skmacy	ret = cm_id->device->iwcm->accept(cm_id, iw_param);
548178784Skmacy	if (ret) {
549178784Skmacy		/* An error on accept precludes provider events */
550178784Skmacy		PANIC_IF(cm_id_priv->state != IW_CM_STATE_CONN_RECV);
551178784Skmacy		cm_id_priv->state = IW_CM_STATE_IDLE;
552178784Skmacy		mtx_lock(&cm_id_priv->lock);
553178784Skmacy		if (cm_id_priv->qp) {
554178784Skmacy			cm_id->device->iwcm->rem_ref(qp);
555178784Skmacy			cm_id_priv->qp = NULL;
556178784Skmacy		}
557178784Skmacy		clrbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT);
558178784Skmacy		wakeup(&cm_id_priv->connect_wait);
559178784Skmacy		mtx_unlock(&cm_id_priv->lock);
560178784Skmacy	}
561178784Skmacy
562178784Skmacy	return ret;
563178784Skmacy}
564178784Skmacy
565178784Skmacy/*
566178784Skmacy * Active Side: CM_ID <-- CONN_SENT
567178784Skmacy *
568178784Skmacy * If successful, results in the generation of a CONNECT_REPLY
569178784Skmacy * event. iw_cm_disconnect and iw_cm_destroy will block until the
570178784Skmacy * CONNECT_REPLY event is received from the provider.
571178784Skmacy */
572178784Skmacyint iw_cm_connect(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
573178784Skmacy{
574178784Skmacy	struct iwcm_id_private *cm_id_priv;
575178784Skmacy	int ret;
576178784Skmacy	struct ib_qp *qp;
577178784Skmacy
578178784Skmacy	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
579178784Skmacy
580178784Skmacy	ret = alloc_work_entries(cm_id_priv, 4);
581178784Skmacy	if (ret)
582178784Skmacy		return ret;
583178784Skmacy
584178784Skmacy	setbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT);
585178784Skmacy	mtx_lock(&cm_id_priv->lock);
586178784Skmacy
587178784Skmacy	if (cm_id_priv->state != IW_CM_STATE_IDLE) {
588178784Skmacy		clrbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT);
589178784Skmacy		wakeup(&cm_id_priv->connect_wait);
590178784Skmacy		mtx_unlock(&cm_id_priv->lock);
591178784Skmacy
592178784Skmacy		return (EINVAL);
593178784Skmacy	}
594178784Skmacy
595178784Skmacy	/* Get the ib_qp given the QPN */
596178784Skmacy	qp = cm_id->device->iwcm->get_qp(cm_id->device, iw_param->qpn);
597178784Skmacy	if (!qp) {
598178784Skmacy		mtx_unlock(&cm_id_priv->lock);
599178784Skmacy		return (EINVAL);
600178784Skmacy	}
601178784Skmacy	cm_id->device->iwcm->add_ref(qp);
602178784Skmacy	cm_id_priv->qp = qp;
603178784Skmacy	cm_id_priv->state = IW_CM_STATE_CONN_SENT;
604178784Skmacy	mtx_unlock(&cm_id_priv->lock);
605178784Skmacy
606178784Skmacy	ret = cm_id->device->iwcm->connect(cm_id, iw_param);
607178784Skmacy	if (ret) {
608178784Skmacy		mtx_lock(&cm_id_priv->lock);
609178784Skmacy		if (cm_id_priv->qp) {
610178784Skmacy			cm_id->device->iwcm->rem_ref(qp);
611178784Skmacy			cm_id_priv->qp = NULL;
612178784Skmacy		}
613178784Skmacy		PANIC_IF(cm_id_priv->state != IW_CM_STATE_CONN_SENT);
614178784Skmacy		cm_id_priv->state = IW_CM_STATE_IDLE;
615178784Skmacy		clrbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT);
616178784Skmacy		wakeup(&cm_id_priv->connect_wait);
617178784Skmacy		mtx_unlock(&cm_id_priv->lock);
618178784Skmacy
619178784Skmacy	}
620178784Skmacy
621178784Skmacy	return ret;
622178784Skmacy}
623178784Skmacy
624178784Skmacy/*
625178784Skmacy * Passive Side: new CM_ID <-- CONN_RECV
626178784Skmacy *
627178784Skmacy * Handles an inbound connect request. The function creates a new
628178784Skmacy * iw_cm_id to represent the new connection and inherits the client
629178784Skmacy * callback function and other attributes from the listening parent.
630178784Skmacy *
631178784Skmacy * The work item contains a pointer to the listen_cm_id and the event. The
632178784Skmacy * listen_cm_id contains the client cm_handler, context and
633178784Skmacy * device. These are copied when the device is cloned. The event
634178784Skmacy * contains the new four tuple.
635178784Skmacy *
636178784Skmacy * An error on the child should not affect the parent, so this
637178784Skmacy * function does not return a value.
638178784Skmacy */
639178784Skmacystatic void cm_conn_req_handler(struct iwcm_id_private *listen_id_priv,
640178784Skmacy				struct iw_cm_event *iw_event)
641178784Skmacy{
642178784Skmacy	struct iw_cm_id *cm_id;
643178784Skmacy	struct iwcm_id_private *cm_id_priv;
644178784Skmacy	int ret;
645178784Skmacy
646178784Skmacy	/*
647178784Skmacy	 * The provider should never generate a connection request
648178784Skmacy	 * event with a bad status.
649178784Skmacy	 */
650178784Skmacy	PANIC_IF(iw_event->status);
651178784Skmacy
652178784Skmacy	/*
653178784Skmacy	 * We could be destroying the listening id. If so, ignore this
654178784Skmacy	 * upcall.
655178784Skmacy	 */
656178784Skmacy	mtx_lock(&listen_id_priv->lock);
657178784Skmacy	if (listen_id_priv->state != IW_CM_STATE_LISTEN) {
658178784Skmacy		mtx_unlock(&listen_id_priv->lock);
659178784Skmacy		goto out;
660178784Skmacy	}
661178784Skmacy	mtx_unlock(&listen_id_priv->lock);
662178784Skmacy
663178784Skmacy	cm_id = iw_create_cm_id(listen_id_priv->id.device,
664178784Skmacy				iw_event->so,
665178784Skmacy				listen_id_priv->id.cm_handler,
666178784Skmacy				listen_id_priv->id.context);
667178784Skmacy	/* If the cm_id could not be created, ignore the request */
668178784Skmacy	if (IS_ERR(cm_id))
669178784Skmacy		goto out;
670178784Skmacy
671178784Skmacy	cm_id->provider_data = iw_event->provider_data;
672178784Skmacy	cm_id->local_addr = iw_event->local_addr;
673178784Skmacy	cm_id->remote_addr = iw_event->remote_addr;
674178784Skmacy
675178784Skmacy	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
676178784Skmacy	cm_id_priv->state = IW_CM_STATE_CONN_RECV;
677178784Skmacy
678178784Skmacy	ret = alloc_work_entries(cm_id_priv, 3);
679178784Skmacy	if (ret) {
680178784Skmacy		iw_cm_reject(cm_id, NULL, 0);
681178784Skmacy		iw_destroy_cm_id(cm_id);
682178784Skmacy		goto out;
683178784Skmacy	}
684178784Skmacy
685178784Skmacy	/* Call the client CM handler */
686178784Skmacy	ret = cm_id->cm_handler(cm_id, iw_event);
687178784Skmacy	if (ret) {
688178784Skmacy		iw_cm_reject(cm_id, NULL, 0);
689178784Skmacy		setbit(&cm_id_priv->flags, IWCM_F_CALLBACK_DESTROY);
690178784Skmacy
691178784Skmacy		destroy_cm_id(cm_id);
692178784Skmacy		if (atomic_load_acq_int(&cm_id_priv->refcount)==0)
693178784Skmacy			free_cm_id(cm_id_priv);
694178784Skmacy	}
695178784Skmacy
696178784Skmacyout:
697178784Skmacy	if (iw_event->private_data_len)
698178784Skmacy		free(iw_event->private_data, M_DEVBUF);
699178784Skmacy}
700178784Skmacy
701178784Skmacy/*
702178784Skmacy * Passive Side: CM_ID <-- ESTABLISHED
703178784Skmacy *
704178784Skmacy * The provider generated an ESTABLISHED event which means that
705178784Skmacy * the MPA negotion has completed successfully and we are now in MPA
706178784Skmacy * FPDU mode.
707178784Skmacy *
708178784Skmacy * This event can only be received in the CONN_RECV state. If the
709178784Skmacy * remote peer closed, the ESTABLISHED event would be received followed
710178784Skmacy * by the CLOSE event. If the app closes, it will block until we wake
711178784Skmacy * it up after processing this event.
712178784Skmacy */
713178784Skmacystatic int cm_conn_est_handler(struct iwcm_id_private *cm_id_priv,
714178784Skmacy			       struct iw_cm_event *iw_event)
715178784Skmacy{
716178784Skmacy	int ret;
717178784Skmacy
718178784Skmacy	mtx_lock(&cm_id_priv->lock);
719178784Skmacy
720178784Skmacy	/*
721178784Skmacy	 * We clear the CONNECT_WAIT bit here to allow the callback
722178784Skmacy	 * function to call iw_cm_disconnect. Calling iw_destroy_cm_id
723178784Skmacy	 * from a callback handler is not allowed.
724178784Skmacy	 */
725178784Skmacy	clrbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT);
726178784Skmacy	PANIC_IF(cm_id_priv->state != IW_CM_STATE_CONN_RECV);
727178784Skmacy	cm_id_priv->state = IW_CM_STATE_ESTABLISHED;
728178784Skmacy	ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event);
729178784Skmacy	wakeup(&cm_id_priv->connect_wait);
730178784Skmacy	mtx_unlock(&cm_id_priv->lock);
731178784Skmacy
732178784Skmacy	return ret;
733178784Skmacy}
734178784Skmacy
735178784Skmacy/*
736178784Skmacy * Active Side: CM_ID <-- ESTABLISHED
737178784Skmacy *
738178784Skmacy * The app has called connect and is waiting for the established event to
739178784Skmacy * post it's requests to the server. This event will wake up anyone
740178784Skmacy * blocked in iw_cm_disconnect or iw_destroy_id.
741178784Skmacy */
742178784Skmacystatic int cm_conn_rep_handler(struct iwcm_id_private *cm_id_priv,
743178784Skmacy			       struct iw_cm_event *iw_event)
744178784Skmacy{
745178784Skmacy	int ret;
746178784Skmacy
747178784Skmacy	mtx_lock(&cm_id_priv->lock);
748178784Skmacy	/*
749178784Skmacy	 * Clear the connect wait bit so a callback function calling
750178784Skmacy	 * iw_cm_disconnect will not wait and deadlock this thread
751178784Skmacy	 */
752178784Skmacy	clrbit(&cm_id_priv->flags, IWCM_F_CONNECT_WAIT);
753178784Skmacy	PANIC_IF(cm_id_priv->state != IW_CM_STATE_CONN_SENT);
754178784Skmacy	if (iw_event->status == IW_CM_EVENT_STATUS_ACCEPTED) {
755178784Skmacy		cm_id_priv->id.local_addr = iw_event->local_addr;
756178784Skmacy		cm_id_priv->id.remote_addr = iw_event->remote_addr;
757178784Skmacy		cm_id_priv->state = IW_CM_STATE_ESTABLISHED;
758178784Skmacy	} else {
759178784Skmacy		/* REJECTED or RESET */
760178784Skmacy		cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp);
761178784Skmacy		cm_id_priv->qp = NULL;
762178784Skmacy		cm_id_priv->state = IW_CM_STATE_IDLE;
763178784Skmacy	}
764178784Skmacy	mtx_unlock(&cm_id_priv->lock);
765178784Skmacy	ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event);
766178784Skmacy
767178784Skmacy	mtx_lock(&cm_id_priv->lock);
768178784Skmacy	if (iw_event->private_data_len)
769178784Skmacy		free(iw_event->private_data, M_DEVBUF);
770178784Skmacy
771178784Skmacy	/* Wake up waiters on connect complete */
772178784Skmacy	wakeup(&cm_id_priv->connect_wait);
773178784Skmacy	mtx_unlock(&cm_id_priv->lock);
774178784Skmacy
775178784Skmacy	return ret;
776178784Skmacy}
777178784Skmacy
778178784Skmacy/*
779178784Skmacy * CM_ID <-- CLOSING
780178784Skmacy *
781178784Skmacy * If in the ESTABLISHED state, move to CLOSING.
782178784Skmacy */
783178784Skmacystatic void cm_disconnect_handler(struct iwcm_id_private *cm_id_priv,
784178784Skmacy				  struct iw_cm_event *iw_event)
785178784Skmacy{
786178784Skmacy
787178784Skmacy	mtx_lock(&cm_id_priv->lock);
788178784Skmacy	if (cm_id_priv->state == IW_CM_STATE_ESTABLISHED)
789178784Skmacy		cm_id_priv->state = IW_CM_STATE_CLOSING;
790178784Skmacy	mtx_unlock(&cm_id_priv->lock);
791178784Skmacy}
792178784Skmacy
793178784Skmacy/*
794178784Skmacy * CM_ID <-- IDLE
795178784Skmacy *
796178784Skmacy * If in the ESTBLISHED or CLOSING states, the QP will have have been
797178784Skmacy * moved by the provider to the ERR state. Disassociate the CM_ID from
798178784Skmacy * the QP,  move to IDLE, and remove the 'connected' reference.
799178784Skmacy *
800178784Skmacy * If in some other state, the cm_id was destroyed asynchronously.
801178784Skmacy * This is the last reference that will result in waking up
802178784Skmacy * the app thread blocked in iw_destroy_cm_id.
803178784Skmacy */
804178784Skmacystatic int cm_close_handler(struct iwcm_id_private *cm_id_priv,
805178784Skmacy				  struct iw_cm_event *iw_event)
806178784Skmacy{
807178784Skmacy	int ret = 0;
808178784Skmacy	mtx_lock(&cm_id_priv->lock);
809178784Skmacy
810178784Skmacy	if (cm_id_priv->qp) {
811178784Skmacy		cm_id_priv->id.device->iwcm->rem_ref(cm_id_priv->qp);
812178784Skmacy		cm_id_priv->qp = NULL;
813178784Skmacy	}
814178784Skmacy	switch (cm_id_priv->state) {
815178784Skmacy	case IW_CM_STATE_ESTABLISHED:
816178784Skmacy	case IW_CM_STATE_CLOSING:
817178784Skmacy		cm_id_priv->state = IW_CM_STATE_IDLE;
818178784Skmacy		mtx_unlock(&cm_id_priv->lock);
819178784Skmacy		ret = cm_id_priv->id.cm_handler(&cm_id_priv->id, iw_event);
820178784Skmacy		mtx_lock(&cm_id_priv->lock);
821178784Skmacy		break;
822178784Skmacy	case IW_CM_STATE_DESTROYING:
823178784Skmacy		break;
824178784Skmacy	default:
825178784Skmacy		panic("just cuz");
826178784Skmacy	}
827178784Skmacy	mtx_unlock(&cm_id_priv->lock);
828178784Skmacy
829178784Skmacy	return ret;
830178784Skmacy}
831178784Skmacy
832178784Skmacystatic int process_event(struct iwcm_id_private *cm_id_priv,
833178784Skmacy			 struct iw_cm_event *iw_event)
834178784Skmacy{
835178784Skmacy	int ret = 0;
836178784Skmacy
837178784Skmacy	switch (iw_event->event) {
838178784Skmacy	case IW_CM_EVENT_CONNECT_REQUEST:
839178784Skmacy		cm_conn_req_handler(cm_id_priv, iw_event);
840178784Skmacy		break;
841178784Skmacy	case IW_CM_EVENT_CONNECT_REPLY:
842178784Skmacy		ret = cm_conn_rep_handler(cm_id_priv, iw_event);
843178784Skmacy		break;
844178784Skmacy	case IW_CM_EVENT_ESTABLISHED:
845178784Skmacy		ret = cm_conn_est_handler(cm_id_priv, iw_event);
846178784Skmacy		break;
847178784Skmacy	case IW_CM_EVENT_DISCONNECT:
848178784Skmacy		cm_disconnect_handler(cm_id_priv, iw_event);
849178784Skmacy		break;
850178784Skmacy	case IW_CM_EVENT_CLOSE:
851178784Skmacy		ret = cm_close_handler(cm_id_priv, iw_event);
852178784Skmacy		break;
853178784Skmacy	default:
854178784Skmacy		panic("just cuz");
855178784Skmacy	}
856178784Skmacy
857178784Skmacy	return ret;
858178784Skmacy}
859178784Skmacy
860178784Skmacy/*
861178784Skmacy * Process events on the work_list for the cm_id. If the callback
862178784Skmacy * function requests that the cm_id be deleted, a flag is set in the
863178784Skmacy * cm_id flags to indicate that when the last reference is
864178784Skmacy * removed, the cm_id is to be destroyed. This is necessary to
865178784Skmacy * distinguish between an object that will be destroyed by the app
866178784Skmacy * thread asleep on the destroy_comp list vs. an object destroyed
867178784Skmacy * here synchronously when the last reference is removed.
868178784Skmacy */
869178784Skmacystatic void cm_work_handler(void *context, int pending)
870178784Skmacy{
871178784Skmacy	struct iwcm_work *work = context;
872178784Skmacy	struct iw_cm_event levent;
873178784Skmacy	struct iwcm_id_private *cm_id_priv = work->cm_id;
874178784Skmacy	int empty;
875178784Skmacy	int ret = 0;
876178784Skmacy
877178784Skmacy	mtx_lock(&cm_id_priv->lock);
878178784Skmacy	empty = TAILQ_EMPTY(&cm_id_priv->work_list);
879178784Skmacy	while (!empty) {
880178784Skmacy		work = TAILQ_FIRST(&cm_id_priv->work_list);
881178784Skmacy		TAILQ_REMOVE(&cm_id_priv->work_list, work, list);
882178784Skmacy		empty = TAILQ_EMPTY(&cm_id_priv->work_list);
883178784Skmacy		levent = work->event;
884178784Skmacy		put_work(work);
885178784Skmacy		mtx_unlock(&cm_id_priv->lock);
886178784Skmacy
887178784Skmacy		ret = process_event(cm_id_priv, &levent);
888178784Skmacy		if (ret) {
889178784Skmacy			setbit(&cm_id_priv->flags, IWCM_F_CALLBACK_DESTROY);
890178784Skmacy			destroy_cm_id(&cm_id_priv->id);
891178784Skmacy		}
892178784Skmacy		PANIC_IF(atomic_load_acq_int(&cm_id_priv->refcount)==0);
893178784Skmacy		if (iwcm_deref_id(cm_id_priv)) {
894178784Skmacy			if (isset(&cm_id_priv->flags,
895178784Skmacy				IWCM_F_CALLBACK_DESTROY)) {
896178784Skmacy				PANIC_IF(!TAILQ_EMPTY(&cm_id_priv->work_list));
897178784Skmacy				free_cm_id(cm_id_priv);
898178784Skmacy			}
899178784Skmacy			return;
900178784Skmacy		}
901178784Skmacy		mtx_lock(&cm_id_priv->lock);
902178784Skmacy	}
903178784Skmacy	mtx_unlock(&cm_id_priv->lock);
904178784Skmacy}
905178784Skmacy
906178784Skmacy/*
907178784Skmacy * This function is called on interrupt context. Schedule events on
908178784Skmacy * the iwcm_wq thread to allow callback functions to downcall into
909178784Skmacy * the CM and/or block.  Events are queued to a per-CM_ID
910178784Skmacy * work_list. If this is the first event on the work_list, the work
911178784Skmacy * element is also queued on the iwcm_wq thread.
912178784Skmacy *
913178784Skmacy * Each event holds a reference on the cm_id. Until the last posted
914178784Skmacy * event has been delivered and processed, the cm_id cannot be
915178784Skmacy * deleted.
916178784Skmacy *
917178784Skmacy * Returns:
918178784Skmacy * 	      0	- the event was handled.
919178784Skmacy *	ENOMEM	- the event was not handled due to lack of resources.
920178784Skmacy */
921178784Skmacystatic int cm_event_handler(struct iw_cm_id *cm_id,
922178784Skmacy			     struct iw_cm_event *iw_event)
923178784Skmacy{
924178784Skmacy	struct iwcm_work *work;
925178784Skmacy	struct iwcm_id_private *cm_id_priv;
926178784Skmacy	int ret = 0;
927178784Skmacy
928178784Skmacy	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
929178784Skmacy
930178784Skmacy	mtx_lock(&cm_id_priv->lock);
931178784Skmacy	work = get_work(cm_id_priv);
932178784Skmacy	if (!work) {
933178784Skmacy		ret = ENOMEM;
934178784Skmacy		goto out;
935178784Skmacy	}
936178784Skmacy
937178784Skmacy        TASK_INIT(&work->task, 0, cm_work_handler, work);
938178784Skmacy	work->cm_id = cm_id_priv;
939178784Skmacy	work->event = *iw_event;
940178784Skmacy
941178784Skmacy	if ((work->event.event == IW_CM_EVENT_CONNECT_REQUEST ||
942178784Skmacy	     work->event.event == IW_CM_EVENT_CONNECT_REPLY) &&
943178784Skmacy	    work->event.private_data_len) {
944178784Skmacy		ret = copy_private_data(&work->event);
945178784Skmacy		if (ret) {
946178784Skmacy			put_work(work);
947178784Skmacy			goto out;
948178784Skmacy		}
949178784Skmacy	}
950178784Skmacy
951178784Skmacy	atomic_add_acq_int(&cm_id_priv->refcount, 1);
952178784Skmacy	if (TAILQ_EMPTY(&cm_id_priv->work_list)) {
953178784Skmacy		TAILQ_INSERT_TAIL(&cm_id_priv->work_list, work, list);
954178784Skmacy		taskqueue_enqueue(iwcm_wq, &work->task);
955178784Skmacy	} else
956178784Skmacy		TAILQ_INSERT_TAIL(&cm_id_priv->work_list, work, list);
957178784Skmacyout:
958178784Skmacy	mtx_unlock(&cm_id_priv->lock);
959178784Skmacy	return ret;
960178784Skmacy}
961178784Skmacy
962178784Skmacystatic int iwcm_init_qp_init_attr(struct iwcm_id_private *cm_id_priv,
963178784Skmacy				  struct ib_qp_attr *qp_attr,
964178784Skmacy				  int *qp_attr_mask)
965178784Skmacy{
966178784Skmacy	int ret;
967178784Skmacy
968178784Skmacy	mtx_lock(&cm_id_priv->lock);
969178784Skmacy	switch (cm_id_priv->state) {
970178784Skmacy	case IW_CM_STATE_IDLE:
971178784Skmacy	case IW_CM_STATE_CONN_SENT:
972178784Skmacy	case IW_CM_STATE_CONN_RECV:
973178784Skmacy	case IW_CM_STATE_ESTABLISHED:
974178784Skmacy		*qp_attr_mask = IB_QP_STATE | IB_QP_ACCESS_FLAGS;
975178784Skmacy		qp_attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE |
976178784Skmacy					   IB_ACCESS_REMOTE_WRITE|
977178784Skmacy					   IB_ACCESS_REMOTE_READ;
978178784Skmacy		ret = 0;
979178784Skmacy		break;
980178784Skmacy	default:
981178784Skmacy		ret = EINVAL;
982178784Skmacy		break;
983178784Skmacy	}
984178784Skmacy	mtx_unlock(&cm_id_priv->lock);
985178784Skmacy	return ret;
986178784Skmacy}
987178784Skmacy
988178784Skmacystatic int iwcm_init_qp_rts_attr(struct iwcm_id_private *cm_id_priv,
989178784Skmacy				  struct ib_qp_attr *qp_attr,
990178784Skmacy				  int *qp_attr_mask)
991178784Skmacy{
992178784Skmacy	int ret;
993178784Skmacy
994178784Skmacy	mtx_lock(&cm_id_priv->lock);
995178784Skmacy	switch (cm_id_priv->state) {
996178784Skmacy	case IW_CM_STATE_IDLE:
997178784Skmacy	case IW_CM_STATE_CONN_SENT:
998178784Skmacy	case IW_CM_STATE_CONN_RECV:
999178784Skmacy	case IW_CM_STATE_ESTABLISHED:
1000178784Skmacy		*qp_attr_mask = 0;
1001178784Skmacy		ret = 0;
1002178784Skmacy		break;
1003178784Skmacy	default:
1004178784Skmacy		ret = EINVAL;
1005178784Skmacy		break;
1006178784Skmacy	}
1007178784Skmacy	mtx_unlock(&cm_id_priv->lock);
1008178784Skmacy	return ret;
1009178784Skmacy}
1010178784Skmacy
1011178784Skmacyint iw_cm_init_qp_attr(struct iw_cm_id *cm_id,
1012178784Skmacy		       struct ib_qp_attr *qp_attr,
1013178784Skmacy		       int *qp_attr_mask)
1014178784Skmacy{
1015178784Skmacy	struct iwcm_id_private *cm_id_priv;
1016178784Skmacy	int ret;
1017178784Skmacy
1018178784Skmacy	cm_id_priv = container_of(cm_id, struct iwcm_id_private, id);
1019178784Skmacy	switch (qp_attr->qp_state) {
1020178784Skmacy	case IB_QPS_INIT:
1021178784Skmacy	case IB_QPS_RTR:
1022178784Skmacy		ret = iwcm_init_qp_init_attr(cm_id_priv,
1023178784Skmacy					     qp_attr, qp_attr_mask);
1024178784Skmacy		break;
1025178784Skmacy	case IB_QPS_RTS:
1026178784Skmacy		ret = iwcm_init_qp_rts_attr(cm_id_priv,
1027178784Skmacy					    qp_attr, qp_attr_mask);
1028178784Skmacy		break;
1029178784Skmacy	default:
1030178784Skmacy		ret = EINVAL;
1031178784Skmacy		break;
1032178784Skmacy	}
1033178784Skmacy	return ret;
1034178784Skmacy}
1035178784Skmacy
1036178784Skmacystatic int iw_cm_init(void)
1037178784Skmacy{
1038178784Skmacy	iwcm_wq = taskqueue_create("iw_cm_wq", M_NOWAIT, taskqueue_thread_enqueue, &iwcm_wq);
1039178784Skmacy	if (!iwcm_wq)
1040178784Skmacy		return (ENOMEM);
1041178784Skmacy
1042178784Skmacy	taskqueue_start_threads(&iwcm_wq, 1, PI_NET, "iw_cm_wq thread");
1043178784Skmacy	return 0;
1044178784Skmacy}
1045178784Skmacy
1046178784Skmacystatic void iw_cm_cleanup(void)
1047178784Skmacy{
1048178784Skmacy	taskqueue_free(iwcm_wq);
1049178784Skmacy}
1050178784Skmacy
1051178784Skmacystatic int
1052178784Skmacyiw_cm_load(module_t mod, int cmd, void *arg)
1053178784Skmacy{
1054178784Skmacy        int err = 0;
1055178784Skmacy
1056178784Skmacy        switch (cmd) {
1057178784Skmacy        case MOD_LOAD:
1058178784Skmacy                printf("Loading rdma_iwcm.\n");
1059178784Skmacy
1060178784Skmacy                iw_cm_init();
1061178784Skmacy                break;
1062178784Skmacy        case MOD_QUIESCE:
1063178784Skmacy                break;
1064178784Skmacy        case MOD_UNLOAD:
1065178784Skmacy                printf("Unloading rdma_iwcm.\n");
1066178784Skmacy		iw_cm_cleanup();
1067178784Skmacy                break;
1068178784Skmacy        case MOD_SHUTDOWN:
1069178784Skmacy                break;
1070178784Skmacy        default:
1071178784Skmacy                err = EOPNOTSUPP;
1072178784Skmacy                break;
1073178784Skmacy        }
1074178784Skmacy
1075178784Skmacy        return (err);
1076178784Skmacy}
1077178784Skmacy
1078178784Skmacystatic moduledata_t mod_data = {
1079178784Skmacy	"rdma_iwcm",
1080178784Skmacy	iw_cm_load,
1081178784Skmacy	0
1082178784Skmacy};
1083178784Skmacy
1084178784SkmacyMODULE_VERSION(rdma_iwcm, 1);
1085178784SkmacyMODULE_DEPEND(rdma_iwcm, rdma_core, 1, 1, 1);
1086178784SkmacyDECLARE_MODULE(rdma_iwcm, mod_data, SI_SUB_EXEC, SI_ORDER_ANY);
1087