1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26/*
27 * sol_cma is a part of sol_ofs misc module. This file
28 * provides interfaces for supporting the communication
29 * management API defined in "rdma_cm.h". In-Kernel
30 * consumers of the "rdma_cm.h" API should link sol_ofs
31 * misc module using :
32 *	-N misc/sol_ofs
33 * Solaris uCMA (sol_ucma) driver is the current consumer for
34 * sol_cma.
35 */
36
37/* Standard driver includes */
38#include <sys/types.h>
39#include <sys/modctl.h>
40#include <sys/errno.h>
41#include <sys/stat.h>
42#include <sys/ddi.h>
43#include <sys/sunddi.h>
44#include <sys/modctl.h>
45
46#include <sys/ib/clients/of/ofed_kernel.h>
47#include <sys/ib/clients/of/rdma/ib_addr.h>
48
49#include <sys/ib/clients/of/sol_ofs/sol_cma.h>
50#include <sys/ib/clients/of/sol_ofs/sol_kverb_impl.h>
51
52/* Modload support */
53static struct modlmisc sol_ofs_modmisc	= {
54	&mod_miscops,
55	"Solaris OFS Misc module"
56};
57
58struct modlinkage sol_ofs_modlinkage = {
59	MODREV_1,
60	(void *)&sol_ofs_modmisc,
61	NULL
62};
63
64static ib_client_t	*sol_cma_ib_client;
65sol_cma_glbl_listen_t	sol_cma_glbl_listen;
66avl_tree_t		sol_cma_glbl_listen_tree;
67
68static void		sol_cma_add_dev(struct ib_device *);
69static void		sol_cma_rem_dev(struct ib_device *);
70
71static llist_head_t	sol_cma_dev_list = LLIST_HEAD_INIT(sol_cma_dev_list);
72kmutex_t		sol_cma_dev_mutex;
73kmutex_t		sol_cma_glob_mutex;
74
75char	*sol_rdmacm_dbg_str = "sol_rdmacm";
76char	*sol_ofs_dbg_str = "sol_ofs_mod";
77
78/*
79 * Local functions defines.
80 */
81int sol_cma_req_cmid_cmp(const void *p1, const void *p2);
82int sol_cma_cmid_cmp(const void *p1, const void *p2);
83int sol_cma_svc_cmp(const void *, const void *);
84
85static struct rdma_cm_id *cma_alloc_chan(rdma_cm_event_handler,
86    void *, enum rdma_port_space);
87static void cma_set_chan_state(sol_cma_chan_t *, cma_chan_state_t);
88static int cma_cas_chan_state(sol_cma_chan_t *, cma_chan_state_t,
89    cma_chan_state_t);
90static void cma_free_listen_list(struct rdma_cm_id *);
91static void cma_destroy_id(struct rdma_cm_id *);
92static void cma_handle_nomore_events(sol_cma_chan_t *);
93
94extern void sol_ofs_dprintf_init();
95extern void sol_ofs_dprintf_fini();
96
97cma_chan_state_t cma_get_chan_state(sol_cma_chan_t *);
98extern int ibcma_init_root_chan(sol_cma_chan_t *, sol_cma_glbl_listen_t *);
99extern int ibcma_fini_root_chan(sol_cma_chan_t *);
100extern void ibcma_copy_srv_hdl(sol_cma_chan_t *, sol_cma_glbl_listen_t *);
101extern int ibcma_fini_ep_chan(sol_cma_chan_t *);
102extern uint64_t ibcma_init_root_sid(sol_cma_chan_t *);
103extern void rdma_ib_destroy_id(struct rdma_cm_id *);
104extern int rdma_ib_bind_addr(struct rdma_cm_id *, struct sockaddr *);
105extern int rdma_ib_resolve_addr(struct rdma_cm_id *, struct sockaddr *,
106    struct sockaddr *, int);
107extern int rdma_ib_resolve_route(struct rdma_cm_id *, int);
108extern int rdma_ib_init_qp_attr(struct rdma_cm_id *, struct ib_qp_attr *,
109    int *);
110extern int rdma_ib_connect(struct rdma_cm_id *, struct rdma_conn_param *);
111extern int rdma_ib_listen(struct rdma_cm_id *, int);
112extern int rdma_ib_accept(struct rdma_cm_id *, struct rdma_conn_param *);
113extern int rdma_ib_reject(struct rdma_cm_id *, const void *, uint8_t);
114extern int rdma_ib_disconnect(struct rdma_cm_id *);
115extern int rdma_ib_join_multicast(struct rdma_cm_id *, struct sockaddr *,
116    void *);
117extern void rdma_ib_leave_multicast(struct rdma_cm_id *, struct sockaddr *);
118
119int
120_init(void)
121{
122	int		err;
123
124	sol_ofs_dprintf_init();
125	SOL_OFS_DPRINTF_L5(sol_ofs_dbg_str, "_init()");
126
127	mutex_init(&sol_cma_glob_mutex, NULL, MUTEX_DRIVER, NULL);
128	mutex_init(&sol_cma_dev_mutex, NULL, MUTEX_DRIVER, NULL);
129	avl_create(&sol_cma_glbl_listen_tree,
130	    sol_cma_svc_cmp, sizeof (sol_cma_glbl_listen_t),
131	    offsetof(sol_cma_glbl_listen_t, cma_listen_node));
132
133	sol_cma_ib_client = kmem_zalloc(sizeof (ib_client_t), KM_NOSLEEP);
134	if (!sol_cma_ib_client) {
135		SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str,
136		    "_init() - mem alloc failed");
137		avl_destroy(&sol_cma_glbl_listen_tree);
138		mutex_destroy(&sol_cma_dev_mutex);
139		mutex_destroy(&sol_cma_glob_mutex);
140		sol_ofs_dprintf_fini();
141		return (ENOMEM);
142	}
143
144	sol_cma_ib_client->name = "sol_ofs";
145	sol_cma_ib_client->add = sol_cma_add_dev;
146	sol_cma_ib_client->remove = sol_cma_rem_dev;
147	sol_cma_ib_client->dip = NULL;
148
149	if ((err = ib_register_client(sol_cma_ib_client)) != 0) {
150		SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str,
151		    "_init() ib_register_client() failed with err %d",
152		    err);
153		kmem_free(sol_cma_ib_client, sizeof (ib_client_t));
154		avl_destroy(&sol_cma_glbl_listen_tree);
155		mutex_destroy(&sol_cma_dev_mutex);
156		mutex_destroy(&sol_cma_glob_mutex);
157		sol_ofs_dprintf_fini();
158		return (err);
159	}
160
161	if ((err = mod_install(&sol_ofs_modlinkage)) != 0) {
162		SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str,
163		    "_init() - mod_install() failed");
164		ib_unregister_client(sol_cma_ib_client);
165		kmem_free(sol_cma_ib_client, sizeof (ib_client_t));
166		avl_destroy(&sol_cma_glbl_listen_tree);
167		mutex_destroy(&sol_cma_dev_mutex);
168		mutex_destroy(&sol_cma_glob_mutex);
169		sol_ofs_dprintf_fini();
170		return (err);
171	}
172
173	SOL_OFS_DPRINTF_L5(sol_ofs_dbg_str, "_init() - ret");
174	return (err);
175}
176
177int
178_fini(void)
179{
180	int		err;
181
182	SOL_OFS_DPRINTF_L5(sol_ofs_dbg_str, "_fini()");
183
184	if (avl_numnodes(&sol_cma_glbl_listen_tree)) {
185		SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, "_fini - "
186		    "listen CMIDs still active");
187		return (EBUSY);
188	}
189	if ((err = mod_remove(&sol_ofs_modlinkage)) != 0) {
190		SOL_OFS_DPRINTF_L3(sol_ofs_dbg_str,
191		    "_fini: mod_remove failed");
192		return (err);
193	}
194
195	ib_unregister_client(sol_cma_ib_client);
196	kmem_free(sol_cma_ib_client, sizeof (ib_client_t));
197	avl_destroy(&sol_cma_glbl_listen_tree);
198	mutex_destroy(&sol_cma_dev_mutex);
199	mutex_destroy(&sol_cma_glob_mutex);
200	SOL_OFS_DPRINTF_L5(sol_ofs_dbg_str, "_fini() - ret");
201	sol_ofs_dprintf_fini();
202	return (err);
203}
204
205int
206_info(struct modinfo *modinfop)
207{
208	return (mod_info(&sol_ofs_modlinkage, modinfop));
209}
210
211typedef struct cma_device {
212	kmutex_t		cma_mutex;
213	/* Ptr in the global sol_cma_dev_list */
214	llist_head_t		cma_list;
215	/* List of listeners for this device */
216	genlist_t		cma_epchan_list;
217	struct ib_device	*cma_device;
218	uint_t			cma_ref_count;
219	enum {
220		SOL_CMA_DEV_ADDED,
221		SOL_CMA_DEV_REM_IN_PROGRESS
222	} cma_dev_state;
223} cma_device_t;
224
225static void
226sol_cma_add_dev(struct ib_device *dev)
227{
228	cma_device_t	*new_device;
229
230	new_device = kmem_zalloc(sizeof (cma_device_t), KM_NOSLEEP);
231	if (!new_device) {
232		SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, "sol_cma_add_dev() "
233		    "alloc failed!!");
234		return;
235	}
236	mutex_init(&new_device->cma_mutex, NULL, MUTEX_DRIVER, NULL);
237	llist_head_init(&new_device->cma_list, new_device);
238	init_genlist(&new_device->cma_epchan_list);
239	new_device->cma_device = dev;
240
241	ib_set_client_data(dev, sol_cma_ib_client, new_device);
242
243	mutex_enter(&sol_cma_dev_mutex);
244	llist_add_tail(&new_device->cma_list, &sol_cma_dev_list);
245	mutex_exit(&sol_cma_dev_mutex);
246}
247
248static void
249sol_cma_rem_dev(struct ib_device *dev)
250{
251	cma_device_t	*rem_device;
252	genlist_entry_t	*entry;
253
254	SOL_OFS_DPRINTF_L5(sol_ofs_dbg_str, "sol_rem_dev(%p)", dev);
255
256	rem_device = (cma_device_t *)ib_get_client_data(dev, sol_cma_ib_client);
257	if (!rem_device) {
258		SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, "sol_cma_rem_dev() "
259		    "NULL cma_dev!!");
260		return;
261	}
262
263	mutex_enter(&rem_device->cma_mutex);
264	rem_device->cma_dev_state = SOL_CMA_DEV_REM_IN_PROGRESS;
265	if (rem_device->cma_ref_count) {
266		mutex_exit(&rem_device->cma_mutex);
267		SOL_OFS_DPRINTF_L3(sol_ofs_dbg_str, "sol_cma_rem_dev() "
268		    "BUSY cma_dev!!");
269		return;
270	}
271	entry = remove_genlist_head(&rem_device->cma_epchan_list);
272	while (entry) {
273		sol_cma_chan_t	*ep_chanp;
274
275		ep_chanp = (sol_cma_chan_t *)entry->data;
276		if (ibcma_fini_ep_chan(ep_chanp) == 0) {
277			genlist_entry_t	*entry1;
278			sol_cma_chan_t	*root_chanp;
279
280			ASSERT(ep_chanp->chan_listenp);
281			entry1 = ep_chanp->chan_listenp->listen_ep_root_entry;
282			root_chanp = (sol_cma_chan_t *)ep_chanp->listen_root;
283			root_chanp->chan_listenp->listen_eps--;
284			delete_genlist(&root_chanp->chan_listenp->listen_list,
285			    entry1);
286
287			kmem_free(ep_chanp, sizeof (sol_cma_chan_t));
288			kmem_free(entry, sizeof (genlist_entry_t));
289		}
290
291		entry = remove_genlist_head(&rem_device->cma_epchan_list);
292	}
293	mutex_exit(&rem_device->cma_mutex);
294
295	mutex_enter(&sol_cma_dev_mutex);
296	llist_del(&rem_device->cma_list);
297	mutex_exit(&sol_cma_dev_mutex);
298
299	kmem_free(rem_device, sizeof (cma_device_t));
300}
301
302struct ib_device *
303sol_cma_acquire_device(ib_guid_t hca_guid)
304{
305	llist_head_t	*entry;
306	cma_device_t	*cma_devp;
307
308	mutex_enter(&sol_cma_dev_mutex);
309	list_for_each(entry, &sol_cma_dev_list) {
310		cma_devp = (cma_device_t *)entry->ptr;
311
312		if (cma_devp->cma_device->node_guid != hca_guid)
313			continue;
314
315		mutex_enter(&cma_devp->cma_mutex);
316		if (cma_devp->cma_dev_state == SOL_CMA_DEV_REM_IN_PROGRESS) {
317			SOL_OFS_DPRINTF_L3(sol_ofs_dbg_str,
318			    "sol_cma_acquire_dev() - Device getting removed!!");
319			mutex_exit(&cma_devp->cma_mutex);
320			mutex_exit(&sol_cma_dev_mutex);
321			return (NULL);
322		}
323		cma_devp->cma_ref_count++;
324		mutex_exit(&cma_devp->cma_mutex);
325		mutex_exit(&sol_cma_dev_mutex);
326		return (cma_devp->cma_device);
327
328	}
329	mutex_exit(&sol_cma_dev_mutex);
330	return (NULL);
331}
332
333static void
334sol_cma_release_device(struct rdma_cm_id *id)
335{
336	ib_device_t	*device = id->device;
337	llist_head_t	*entry;
338	cma_device_t	*cma_devp;
339
340	mutex_enter(&sol_cma_dev_mutex);
341	list_for_each(entry, &sol_cma_dev_list) {
342		cma_devp = (cma_device_t *)entry->ptr;
343
344		if (cma_devp->cma_device != device)
345			continue;
346
347		mutex_enter(&cma_devp->cma_mutex);
348		cma_devp->cma_ref_count--;
349		if (cma_devp->cma_dev_state == SOL_CMA_DEV_REM_IN_PROGRESS &&
350		    cma_devp->cma_ref_count == 0) {
351			SOL_OFS_DPRINTF_L3(sol_ofs_dbg_str,
352			    "sol_cma_release_dev() - Device free removed!!");
353			mutex_exit(&cma_devp->cma_mutex);
354			llist_del(&cma_devp->cma_list);
355			kmem_free(cma_devp, sizeof (cma_device_t));
356			mutex_exit(&sol_cma_dev_mutex);
357			return;
358		}
359		mutex_exit(&cma_devp->cma_mutex);
360	}
361	mutex_exit(&sol_cma_dev_mutex);
362}
363
364void
365sol_cma_add_hca_list(sol_cma_chan_t *ep_chanp, ib_guid_t hca_guid)
366{
367	llist_head_t	*entry;
368	cma_device_t	*cma_devp;
369
370	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "add_hca_list(%p, %llx)",
371	    ep_chanp, hca_guid);
372	mutex_enter(&sol_cma_dev_mutex);
373	list_for_each(entry, &sol_cma_dev_list) {
374		cma_devp = (cma_device_t *)entry->ptr;
375
376		if ((cma_devp->cma_device)->node_guid != hca_guid)
377			continue;
378
379		mutex_enter(&cma_devp->cma_mutex);
380		ep_chanp->chan_listenp->listen_ep_dev_entry =
381		    add_genlist(&cma_devp->cma_epchan_list,
382		    (uintptr_t)ep_chanp, NULL);
383		ep_chanp->chan_listenp->listen_ep_device = cma_devp->cma_device;
384		mutex_exit(&cma_devp->cma_mutex);
385		mutex_exit(&sol_cma_dev_mutex);
386		return;
387	}
388	mutex_exit(&sol_cma_dev_mutex);
389	SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "add_hca_list(%p, %llx): "
390	    "No matching HCA in list!!", ep_chanp, hca_guid);
391}
392
393/*
394 * rdma_cm.h API functions.
395 */
396struct rdma_cm_id *
397rdma_create_id(rdma_cm_event_handler evt_hdlr, void *context,
398    enum rdma_port_space ps)
399{
400	struct rdma_cm_id 	*rdma_idp;
401
402	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_create_id(%p, %p, %x)",
403	    evt_hdlr, context, ps);
404
405	if (ps != RDMA_PS_TCP && ps != RDMA_PS_UDP && ps != RDMA_PS_IPOIB) {
406		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
407		    "rdma_create_id: unsupported protocol %x", ps);
408		return (NULL);
409	}
410
411	rdma_idp = cma_alloc_chan(evt_hdlr, context, ps);
412	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str,
413	    "rdma_create_id : ret %p", rdma_idp);
414
415	return (rdma_idp);
416}
417
418void
419rdma_map_id2clnthdl(struct rdma_cm_id *rdma_idp, void *ib_client_hdl,
420    void *iw_client_hdl)
421{
422	sol_cma_chan_t	*chanp = (sol_cma_chan_t *)rdma_idp;
423
424	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str,
425	    "rdma_map_id2clnthdl(%p, %p, %p)",
426	    rdma_idp, ib_client_hdl, iw_client_hdl);
427	ASSERT(ib_client_hdl != NULL || iw_client_hdl != NULL);
428	chanp->chan_ib_client_hdl = ib_client_hdl;
429	chanp->chan_iw_client_hdl = iw_client_hdl;
430}
431
432void
433rdma_map_id2qphdl(struct rdma_cm_id *rdma_idp, void *qp_hdl)
434{
435	sol_cma_chan_t	*chanp = (sol_cma_chan_t *)rdma_idp;
436
437	ASSERT(rdma_idp);
438	ASSERT(qp_hdl);
439	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_mapid2qphdl(%p, %p)",
440	    rdma_idp, qp_hdl);
441	chanp->chan_qp_hdl = qp_hdl;
442}
443
444
445void
446rdma_destroy_id(struct rdma_cm_id *rdma_idp)
447{
448	sol_cma_chan_t		*chanp, *root_chanp;
449	cma_chan_state_t	state;
450	int			rc, is_root_cmid, do_wait, is_passive;
451
452	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_destroy_id(%p)", rdma_idp);
453
454	if (!rdma_idp)
455		return;
456
457	is_root_cmid = do_wait = is_passive = 0;
458
459	chanp = (sol_cma_chan_t *)rdma_idp;
460	root_chanp = (sol_cma_chan_t *)chanp->listen_root;
461	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_destroy_id(%p), %p",
462	    rdma_idp, root_chanp);
463
464	mutex_enter(&chanp->chan_mutex);
465	chanp->chan_cmid_destroy_state |= SOL_CMA_CALLER_CMID_DESTROYED;
466
467	/*
468	 * Wait in destroy of CMID when rdma_resolve_addr() / rdma_listen()
469	 * rdma_resolve_route() API is in progress.
470	 */
471	while (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_API_PROGRESS)
472		cv_wait(&chanp->chan_destroy_cv, &chanp->chan_mutex);
473
474	/* Wait if Event is been notified to consumer */
475	while (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_EVENT_PROGRESS)
476		cv_wait(&chanp->chan_destroy_cv, &chanp->chan_mutex);
477
478	if (rdma_idp->device)
479		sol_cma_release_device(rdma_idp);
480
481	if (chanp->chan_listenp && chanp->chan_listenp->listen_is_root)
482		is_root_cmid = 1;
483	if (root_chanp == NULL && is_root_cmid == 0)
484		is_passive = 1;
485
486	/*
487	 * Skip Active side handling for passive CMIDs and listen CMID
488	 * for which REQ CMIDs have not been created.
489	 */
490	if (is_passive || (is_root_cmid && chanp->chan_req_state !=
491	    REQ_CMID_QUEUED)) {
492		SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_destroy_id: "
493		    "Skipping passive %p, %x, %x", chanp->chan_listenp,
494		    is_root_cmid, chanp->chan_req_state);
495		goto skip_passive_handling;
496	}
497
498	/*
499	 * destroy_id() called for listening CMID and there are REQ
500	 * CMIDs not yet notified. Reject such CMIDs and decrement
501	 * the count.
502	 */
503	if (is_root_cmid && chanp->chan_req_cnt) {
504		sol_cma_chan_t	*req_cmid_chan, *next_chan;
505
506		SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_destroy_id: "
507		    "not notified handling");
508		for (req_cmid_chan = (sol_cma_chan_t *)avl_first(
509		    &chanp->chan_req_avl_tree); req_cmid_chan &&
510		    chanp->chan_req_cnt; req_cmid_chan = next_chan) {
511			next_chan = AVL_NEXT(
512			    &chanp->chan_req_avl_tree, req_cmid_chan);
513			if (req_cmid_chan->chan_req_state ==
514			    REQ_CMID_NOTIFIED) {
515				avl_remove(&chanp->chan_req_avl_tree,
516				    req_cmid_chan);
517				chanp->chan_req_cnt--;
518				chanp->chan_req_total_cnt--;
519				mutex_exit(&chanp->chan_mutex);
520				mutex_enter(&req_cmid_chan->chan_mutex);
521				req_cmid_chan->chan_req_state =
522				    REQ_CMID_SERVER_NONE;
523				if (rdma_idp->ps == RDMA_PS_TCP)
524					cma_set_chan_state(req_cmid_chan,
525					    SOL_CMA_CHAN_DESTROY_PENDING);
526				mutex_exit(&req_cmid_chan->chan_mutex);
527				(void) rdma_disconnect(
528				    (struct rdma_cm_id *)req_cmid_chan);
529				mutex_enter(&chanp->chan_mutex);
530				if (rdma_idp->ps == RDMA_PS_TCP) {
531					mutex_enter(
532					    &req_cmid_chan->chan_mutex);
533					req_cmid_chan->listen_root =
534					    rdma_idp;
535					mutex_exit(
536					    &req_cmid_chan->chan_mutex);
537				} else {
538					mutex_destroy(
539					    &req_cmid_chan->chan_mutex);
540					cv_destroy(
541					    &req_cmid_chan->chan_destroy_cv);
542					kmem_free(req_cmid_chan,
543					    sizeof (sol_cma_chan_t));
544				}
545			}
546		}
547	}
548
549	/*
550	 * destroy_id() called for :
551	 * 	listening CMID and all REQ CMIDs destroy_id() called
552	 *	REQ CMID and 1 more REQ CMID not yet destroyed.
553	 * wait till the CMID is completly destroyed.
554	 */
555	if (is_root_cmid && chanp->chan_req_total_cnt == 0) {
556		SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_destroy_id: "
557		    "root idp waiting");
558		cma_set_chan_state(chanp, SOL_CMA_CHAN_DESTROY_WAIT);
559		cv_wait(&chanp->chan_destroy_cv, &chanp->chan_mutex);
560	}
561	mutex_exit(&chanp->chan_mutex);
562
563	if (root_chanp)
564		mutex_enter(&root_chanp->chan_mutex);
565	mutex_enter(&chanp->chan_mutex);
566#ifdef	DEBUG
567	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_destroy_id: "
568	    "root_idp %p, cnt %x, state %x", root_chanp,
569	    root_chanp ? root_chanp->chan_req_total_cnt : 0,
570	    root_chanp ? cma_get_chan_state(root_chanp) : 0);
571#endif
572
573	if (root_chanp && root_chanp->chan_req_total_cnt == 1 &&
574	    cma_get_chan_state(root_chanp) == SOL_CMA_CHAN_DESTROY_PENDING)
575		do_wait = 1;
576	if (root_chanp)
577		mutex_exit(&root_chanp->chan_mutex);
578
579skip_passive_handling :
580	state = cma_get_chan_state(chanp);
581	if (is_root_cmid == 0 && state != SOL_CMA_CHAN_DISCONNECT &&
582	    SOL_CMAID_CONNECTED(chanp)) {
583		/*
584		 * A connected CM ID has not been disconnected.
585		 * Call rdma_disconnect() to disconnect it.
586		 */
587		mutex_exit(&chanp->chan_mutex);
588		rc = rdma_disconnect(rdma_idp);
589		if (rc) {
590			SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
591			    "rdma_destroy_id(%p)- disconnect failed!!",
592			    rdma_idp);
593			return;
594		}
595		mutex_enter(&chanp->chan_mutex);
596		if (root_chanp && chanp->listen_root == NULL)
597			chanp->listen_root = (struct rdma_cm_id *)root_chanp;
598		SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str,
599		    "rdma_destroy_id(chanp %p, connect %x, ps %x)",
600		    chanp, chanp->chan_connect_flag, rdma_idp->ps);
601		if (SOL_CMAID_CONNECTED(chanp)) {
602			if (do_wait) {
603				cma_set_chan_state(chanp,
604				    SOL_CMA_CHAN_DESTROY_WAIT);
605				cv_wait(&chanp->chan_destroy_cv,
606				    &chanp->chan_mutex);
607				mutex_exit(&chanp->chan_mutex);
608				cma_destroy_id(rdma_idp);
609			} else {
610				cma_set_chan_state(chanp,
611				    SOL_CMA_CHAN_DESTROY_PENDING);
612				mutex_exit(&chanp->chan_mutex);
613			}
614		} else {
615			/*
616			 * No more callbacks are expected for this CMID.
617			 * Free this CMID.
618			 */
619			mutex_exit(&chanp->chan_mutex);
620			cma_destroy_id(rdma_idp);
621		}
622	} else if (is_root_cmid == 0 && state ==
623	    SOL_CMA_CHAN_DISCONNECT && SOL_CMAID_CONNECTED(chanp)) {
624		/*
625		 * CM ID was connected and disconnect is process.
626		 * Free of this CM ID is done for the DISCONNECT
627		 * notification for this CMID.
628		 */
629		cma_set_chan_state(chanp, SOL_CMA_CHAN_DESTROY_PENDING);
630		mutex_exit(&chanp->chan_mutex);
631	} else if (state != SOL_CMA_CHAN_DESTROY_PENDING) {
632		/* CM ID, not connected, just free it. */
633		mutex_exit(&chanp->chan_mutex);
634		cma_destroy_id(rdma_idp);
635	} else
636		mutex_exit(&chanp->chan_mutex);
637
638	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_destroy_id: ret");
639}
640
641/*
642 * State transitions for Address resolution :
643 *	Active Side (Client) :
644 *	1. CREATE_ID-->BIND_ADDR-->RESOLVE_ADDR-->RESOLVE_ROUTE
645 *
646 *	Passive Side (Server) :
647 *	2. CREATE_ID-->RESOLVE_ADDR-->RESOLVE_ROUTE
648 *	IF_ADDR_ANY can be passed as local address in RESOLVE_ADDR
649 */
650int
651rdma_bind_addr(struct rdma_cm_id *idp, struct sockaddr *addr)
652{
653	sol_cma_chan_t		*chanp;
654	struct rdma_addr	*addrp;
655	int			ret;
656
657	ASSERT(idp);
658	ASSERT(addr);
659	chanp = (sol_cma_chan_t *)idp;
660	addrp = &(idp->route.addr);
661	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_bind_addr(%p, %p)",
662	    idp, addr);
663
664	mutex_enter(&chanp->chan_mutex);
665	ret = cma_cas_chan_state(chanp, SOL_CMA_CHAN_IDLE, SOL_CMA_CHAN_BOUND);
666	if (ret) {
667		mutex_exit(&chanp->chan_mutex);
668		return (ret);
669	}
670	/* Copy the local address to rdma_id structure */
671	bcopy((void *)addr, (void *)&(addrp->src_addr),
672	    sizeof (struct sockaddr));
673	mutex_exit(&chanp->chan_mutex);
674
675	/*
676	 * First call rdma_ib_bind_addr() to bind this address.
677	 * Next call rdma_iw_bind_addr() to bind this address.
678	 * For IF_ADDR_ANY, IB address is given priority over
679	 * iWARP.
680	 */
681	if (chanp->chan_ib_client_hdl == NULL) {
682		ofs_client_t	*ofs_clnt;
683
684		ofs_clnt = (ofs_client_t *)sol_cma_ib_client->clnt_hdl;
685		chanp->chan_ib_client_hdl = ofs_clnt->ibt_hdl;
686	}
687	if (chanp->chan_ib_client_hdl && rdma_ib_bind_addr(idp, addr) == 0) {
688		SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str,
689		    "rdma_bind_addr: ret IB @");
690		return (0);
691#ifdef	IWARP_SUPPORT
692	} else if (chanp->chan_iw_client_hdl && rdma_iw_bind_addr(idp, addr)
693	    == 0) {
694		SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str,
695		    "rdma_bind_addr: ret iWARP @");
696		return (0);
697#endif	/* IWARP_SUPPORT */
698	}
699
700	mutex_enter(&chanp->chan_mutex);
701	cma_set_chan_state(chanp, SOL_CMA_CHAN_IDLE);
702	mutex_exit(&chanp->chan_mutex);
703	SOL_OFS_DPRINTF_L4(sol_rdmacm_dbg_str, "rdma_bind_addr: ret failure!");
704	return (EINVAL);
705}
706
707int
708rdma_resolve_addr(struct rdma_cm_id *idp, struct sockaddr *src_addr,
709    struct sockaddr *dst_addr, int timeout_ms)
710{
711	sol_cma_chan_t		*chanp;
712	struct rdma_addr	*addrp;
713	cma_chan_state_t	state;
714
715	ASSERT(idp);
716	chanp = (sol_cma_chan_t *)idp;
717	addrp = &(idp->route.addr);
718	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_resolve_addr(%p, %p, "
719	    "%p, %x)", idp, src_addr, dst_addr, timeout_ms);
720
721	mutex_enter(&chanp->chan_mutex);
722	state = cma_get_chan_state(chanp);
723	if (state != SOL_CMA_CHAN_IDLE && state != SOL_CMA_CHAN_BOUND) {
724		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
725		    "rdma_resolve_addr : invalid chan state %x", state);
726		mutex_exit(&chanp->chan_mutex);
727		return (EINVAL);
728	}
729	if (chanp->chan_cmid_destroy_state &
730	    SOL_CMA_CALLER_CMID_DESTROYED) {
731		SOL_OFS_DPRINTF_L3(sol_rdmacm_dbg_str,
732		    "rdma_resolve_addr : CMID %p, destroy called", chanp);
733		mutex_exit(&chanp->chan_mutex);
734		return (EINVAL);
735	}
736	chanp->chan_cmid_destroy_state |= SOL_CMA_CALLER_API_PROGRESS;
737
738	if (chanp->chan_xport_type == SOL_CMA_XPORT_NONE) {
739		bcopy((void *)src_addr, (void *)&(addrp->src_addr),
740		    sizeof (struct sockaddr));
741	}
742	bcopy((void *)dst_addr, (void *)&(addrp->dst_addr),
743	    sizeof (struct sockaddr));
744	mutex_exit(&chanp->chan_mutex);
745
746	/*
747	 * First resolve this as an @ corresponding to IB fabric
748	 * if this fails, resolve this as an @ corresponding to iWARP
749	 */
750	if (chanp->chan_ib_client_hdl == NULL) {
751		ofs_client_t	*ofs_clnt;
752
753		ofs_clnt = (ofs_client_t *)sol_cma_ib_client->clnt_hdl;
754		chanp->chan_ib_client_hdl = ofs_clnt->ibt_hdl;
755	}
756	if (chanp->chan_ib_client_hdl && rdma_ib_resolve_addr(idp, src_addr,
757	    dst_addr, timeout_ms) == 0) {
758		SOL_OFS_DPRINTF_L4(sol_rdmacm_dbg_str,
759		    "rdma_resolve_addr: ret IB @");
760#ifdef IWARP_SUPPORT
761	} else if (chanp->chan_iw_client_hdl && rdma_iw_resolve_addr(idp,
762	    src_addr, dst_addr, timeout_ms) == 0) {
763		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
764		    "rdma_resolve_addr: ret iWARP @");
765#endif	/* IWARP_SUPPORT */
766	} else {
767		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
768		    "rdma_resolve_addr: Invalid @");
769		return (EINVAL);
770	}
771	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_resolve_addr: ret 0");
772	return (0);
773}
774
775static void cma_generate_event_sync(struct rdma_cm_id *,
776    enum rdma_cm_event_type, int, struct rdma_conn_param *,
777    struct rdma_ud_param *);
778
779void
780cma_resolve_addr_callback(sol_cma_chan_t *chanp, int rc)
781{
782	enum rdma_cm_event_type	event;
783
784	mutex_enter(&chanp->chan_mutex);
785	if (chanp->chan_cmid_destroy_state &
786	    SOL_CMA_CALLER_CMID_DESTROYED) {
787		SOL_OFS_DPRINTF_L3(sol_rdmacm_dbg_str,
788		    "cma_resolve_addr : CMID %p, destroy called", chanp);
789		chanp->chan_cmid_destroy_state &=
790		    ~SOL_CMA_CALLER_API_PROGRESS;
791		cv_broadcast(&chanp->chan_destroy_cv);
792		mutex_exit(&chanp->chan_mutex);
793		return;
794	}
795	if (rc == 0) {
796		cma_set_chan_state(chanp, SOL_CMA_CHAN_ADDR_RESLVD);
797		event = RDMA_CM_EVENT_ADDR_RESOLVED;
798	} else
799		event = RDMA_CM_EVENT_ADDR_ERROR;
800
801	/*
802	 * Generate RDMA_CM_EVENT_ADDR_RESOLVED event
803	 * This will result in RDMA_USER_CM_CMD_RESOLVE_ROUTE in
804	 * userland.
805	 */
806	chanp->chan_cmid_destroy_state |= SOL_CMA_CALLER_EVENT_PROGRESS;
807	mutex_exit(&chanp->chan_mutex);
808	cma_generate_event_sync((struct rdma_cm_id *)chanp, event, 0,
809	    NULL, NULL);
810
811	mutex_enter(&chanp->chan_mutex);
812	chanp->chan_cmid_destroy_state &= ~SOL_CMA_CALLER_API_PROGRESS;
813	if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED)
814		cv_broadcast(&chanp->chan_destroy_cv);
815	mutex_exit(&chanp->chan_mutex);
816}
817
818int
819rdma_resolve_route(struct rdma_cm_id *idp, int timeout_ms)
820{
821	sol_cma_chan_t		*chanp;
822
823	ASSERT(idp);
824	chanp = (sol_cma_chan_t *)idp;
825	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "resolve_route(%p, %x)", idp,
826	    timeout_ms);
827
828	mutex_enter(&chanp->chan_mutex);
829	if (cma_cas_chan_state(chanp, SOL_CMA_CHAN_ADDR_RESLVD,
830	    SOL_CMA_CHAN_ROUTE_RESLVD) != 0) {
831		mutex_exit(&chanp->chan_mutex);
832		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
833		    "resolve_route: Invalid state");
834		return (EINVAL);
835	}
836	if (chanp->chan_cmid_destroy_state &
837	    SOL_CMA_CALLER_CMID_DESTROYED) {
838		SOL_OFS_DPRINTF_L3(sol_rdmacm_dbg_str,
839		    "rdma_resolve_route : CMID %p, destroy called", chanp);
840		mutex_exit(&chanp->chan_mutex);
841		return (EINVAL);
842	}
843	chanp->chan_cmid_destroy_state |= SOL_CMA_CALLER_API_PROGRESS;
844	mutex_exit(&chanp->chan_mutex);
845
846	/*
847	 * Generate RDMA_CM_EVENT_ROUTE_RESOLVED event
848	 * This will result in RDMA_USER_CM_CMD_RESOLVE_ROUTE in
849	 * userland
850	 */
851	cma_generate_event(idp, RDMA_CM_EVENT_ROUTE_RESOLVED, 0,
852	    NULL, NULL);
853
854	mutex_enter(&chanp->chan_mutex);
855	chanp->chan_cmid_destroy_state &= ~SOL_CMA_CALLER_API_PROGRESS;
856	if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED)
857		cv_broadcast(&chanp->chan_destroy_cv);
858	mutex_exit(&chanp->chan_mutex);
859
860	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "resolve_route: ret 0");
861	return (0);
862}
863
864/*
865 * Connect or Listen request should be send after Route is resolved
866 *
867 *	Active Side (Client) :
868 *	1. (State ROUTE_RESOLVED)-->CONNECT-->ACCEPT/REJECT-->DISCONNECT
869 *	       -->DESTROY_ID-->close(9E)
870 *	2. Same as (1), DESTROY_ID without DISCONNECT
871 *	3. Same as (1), close(9e) without DESTROY_ID.
872 *
873 *	Passive Side (Server) :
874 *	4. (State ROUTE_RESOLVED)-->LISTEN->DISCONNECT
875 *		-->DESTROY_ID-->close(9E)
876 *	5. Same as (4), DESTROY_ID without DISCONNECT
877 *	6. Same as (4), close(9e) without DESTROY_ID.
878 */
879int
880rdma_connect(struct rdma_cm_id *idp, struct rdma_conn_param *conn_param)
881{
882	sol_cma_chan_t		*chanp;
883	int			ret = EINVAL;
884
885	ASSERT(idp);
886	chanp = (sol_cma_chan_t *)idp;
887	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_connect(%p, %p)", idp,
888	    conn_param);
889
890	mutex_enter(&chanp->chan_mutex);
891	if (chanp->chan_xport_type == SOL_CMA_XPORT_NONE) {
892		mutex_exit(&chanp->chan_mutex);
893		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
894		    "rdma_connect, Invalid Xport");
895		return (EINVAL);
896	}
897	if (cma_cas_chan_state(chanp, SOL_CMA_CHAN_ROUTE_RESLVD,
898	    SOL_CMA_CHAN_CONNECT)) {
899		mutex_exit(&chanp->chan_mutex);
900		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
901		    "rdma_connect, Invalid state");
902		return (EINVAL);
903	}
904
905	if (chanp->chan_xport_type == SOL_CMA_XPORT_IB) {
906		ret = rdma_ib_connect(idp, conn_param);
907#ifdef	IWARP_SUPPORT
908	} else if (chanp->chan_xport_type == SOL_CMA_XPORT_IWARP) {
909		ret = rdma_iw_connect(idp, conn_param);
910#endif	/* IWARP_SUPPORT */
911	}
912	mutex_exit(&chanp->chan_mutex);
913
914	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_connect: ret %x", ret);
915	return (ret);
916}
917
918static int cma_init_listen_root(sol_cma_chan_t *);
919static void cma_fini_listen_root(sol_cma_chan_t *);
920
921int
922rdma_listen(struct rdma_cm_id *idp, int bklog)
923{
924	sol_cma_chan_t		*chanp;
925	int			ret = 0;
926	genlist_entry_t		*entry;
927	cma_chan_state_t	state;
928
929	ASSERT(idp);
930	chanp = (sol_cma_chan_t *)idp;
931	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_listen(%p, %x)",
932	    idp, bklog);
933
934	mutex_enter(&chanp->chan_mutex);
935	state = cma_get_chan_state(chanp);
936	if (state == SOL_CMA_CHAN_IDLE) {
937		mutex_exit(&chanp->chan_mutex);
938		return (EINVAL);
939	}
940	cma_set_chan_state(chanp, SOL_CMA_CHAN_LISTEN);
941
942	if (chanp->chan_cmid_destroy_state &
943	    SOL_CMA_CALLER_CMID_DESTROYED) {
944		SOL_OFS_DPRINTF_L3(sol_rdmacm_dbg_str,
945		    "rdma_listen : CMID %p, destroy called", chanp);
946		mutex_exit(&chanp->chan_mutex);
947		return (EINVAL);
948	}
949	chanp->chan_cmid_destroy_state |= SOL_CMA_CALLER_API_PROGRESS;
950
951	ASSERT(chanp->chan_listenp == NULL);
952
953	chanp->chan_listenp = kmem_zalloc(sizeof (sol_cma_listen_info_t),
954	    KM_SLEEP);
955	init_genlist(&(CHAN_LISTEN_LIST(chanp)));
956	(chanp->chan_listenp)->listen_is_root = 1;
957	ret = cma_init_listen_root(chanp);
958	if (ret) {
959		chanp->chan_listenp = NULL;
960		mutex_exit(&chanp->chan_mutex);
961		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "rdma_listen: "
962		    "cma_init_listen_root: failed");
963		kmem_free(chanp->chan_listenp,
964		    sizeof (sol_cma_listen_info_t));
965		return (EINVAL);
966	}
967
968	if (chanp->chan_xport_type == SOL_CMA_XPORT_NONE) {
969		ibcma_append_listen_list(idp);
970#ifdef IWARP_SUPPORT
971		iwcma_append_listen_list(idp);
972#endif
973	} else if (chanp->chan_xport_type == SOL_CMA_XPORT_IB) {
974		ibcma_append_listen_list(idp);
975#ifdef	IWARP_SUPPORT
976	} else if (chanp->chan_xport_type == SOL_CMA_XPORT_IWARP) {
977		iwcma_append_listen_list(idp);
978#endif	/* IWARP_SUPPORT */
979	}
980
981	if (genlist_empty(&(CHAN_LISTEN_LIST(chanp)))) {
982		cma_fini_listen_root(chanp);
983		kmem_free((void *)chanp->chan_listenp,
984		    sizeof (sol_cma_listen_info_t));
985		chanp->chan_listenp = NULL;
986		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "rdma_listen: "
987		    "No listeners");
988		mutex_exit(&chanp->chan_mutex);
989		return (0);
990	}
991
992	if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) {
993		chanp->chan_cmid_destroy_state &=
994		    ~SOL_CMA_CALLER_API_PROGRESS;
995		cv_broadcast(&chanp->chan_destroy_cv);
996	}
997
998	genlist_for_each(entry, &(CHAN_LISTEN_LIST(chanp))) {
999		struct rdma_cm_id	*ep_idp;
1000		sol_cma_chan_t		*ep_chanp;
1001
1002		ep_idp = (struct rdma_cm_id *)entry->data;
1003		ep_chanp = (sol_cma_chan_t *)ep_idp;
1004		if (ep_chanp->chan_xport_type == SOL_CMA_XPORT_IB)
1005			ret = rdma_ib_listen(ep_idp, bklog);
1006#ifdef IWARP_SUPPORT
1007		if (ep_chanp->chan_xport_type == SOL_CMA_XPORT_IWARP)
1008			ret = rdma_iw_listen(ep_idp, bklog);
1009#endif
1010		if (ret)
1011			break;
1012	}
1013
1014	chanp->chan_cmid_destroy_state &= ~SOL_CMA_CALLER_API_PROGRESS;
1015	if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED)
1016		cv_broadcast(&chanp->chan_destroy_cv);
1017	mutex_exit(&chanp->chan_mutex);
1018
1019	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_listen: ret %x", ret);
1020	return (ret);
1021}
1022
1023int
1024rdma_accept(struct rdma_cm_id *idp, struct rdma_conn_param *conn_param)
1025{
1026	struct rdma_cm_id	*root_idp;
1027	sol_cma_chan_t		*root_chanp, *chanp;
1028	int			ret = EINVAL;
1029
1030	ASSERT(idp);
1031	chanp = (sol_cma_chan_t *)idp;
1032	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_accept(%p, %p)",
1033	    idp, conn_param);
1034
1035	mutex_enter(&chanp->chan_mutex);
1036	if (cma_cas_chan_state(chanp, SOL_CMA_CHAN_LISTEN,
1037	    SOL_CMA_CHAN_ACCEPT) && cma_cas_chan_state(chanp,
1038	    SOL_CMA_CHAN_CONNECT, SOL_CMA_CHAN_ACCEPT)) {
1039		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
1040		    "rdma_accept, Invalid state");
1041		mutex_exit(&chanp->chan_mutex);
1042		return (EINVAL);
1043	}
1044	mutex_exit(&chanp->chan_mutex);
1045
1046	root_idp = CHAN_LISTEN_ROOT(chanp);
1047	root_chanp = (sol_cma_chan_t *)root_idp;
1048	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "accept: root_idp %p",
1049	    root_idp);
1050
1051	/* For TCP, delete from REQ AVL & insert to ACPT AVL */
1052	if (root_idp && root_idp->ps == RDMA_PS_TCP) {
1053		void		*find_ret;
1054		avl_index_t	where;
1055
1056		SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "accept: root_idp %p"
1057		    "REQ AVL remove %p", root_chanp, idp);
1058		mutex_enter(&root_chanp->chan_mutex);
1059		mutex_enter(&chanp->chan_mutex);
1060
1061		/*
1062		 * This CMID has been deleted, maybe because of timeout.
1063		 * Return EINVAL.
1064		 */
1065		if (chanp->chan_req_state != REQ_CMID_NOTIFIED) {
1066			mutex_exit(&chanp->chan_mutex);
1067			mutex_exit(&root_chanp->chan_mutex);
1068			SOL_OFS_DPRINTF_L3(sol_rdmacm_dbg_str,
1069			    "accept: root_idp %p chanp %p, not in REQ "
1070			    "AVL tree",  root_chanp, chanp);
1071			return (EINVAL);
1072		}
1073		ASSERT(cma_get_req_idp(root_idp, chanp->chan_session_id));
1074		avl_remove(&root_chanp->chan_req_avl_tree, idp);
1075
1076
1077		SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str,
1078		    "Add to ACPT AVL of %p IDP, idp %p, qp_hdl %p",
1079		    root_idp, idp, chanp->chan_qp_hdl);
1080		find_ret = avl_find(&root_chanp->chan_acpt_avl_tree,
1081		    (void *)chanp->chan_qp_hdl, &where);
1082		if (find_ret) {
1083			chanp->chan_req_state = REQ_CMID_SERVER_NONE;
1084			mutex_exit(&chanp->chan_mutex);
1085			mutex_exit(&root_chanp->chan_mutex);
1086			SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
1087			    "DUPLICATE ENTRY in ACPT AVL : root %p, "
1088			    "idp %p, qp_hdl %p",
1089			    root_idp, idp, chanp->chan_qp_hdl);
1090			return (EINVAL);
1091		}
1092		avl_insert(&root_chanp->chan_acpt_avl_tree,
1093		    (void *)idp, where);
1094		chanp->chan_req_state = REQ_CMID_ACCEPTED;
1095		mutex_exit(&chanp->chan_mutex);
1096		mutex_exit(&root_chanp->chan_mutex);
1097	}
1098
1099	if (root_idp && IS_UDP_CMID(root_idp)) {
1100		cma_chan_state_t	chan_state;
1101
1102		/*
1103		 * Accepting the connect request, no more events for this
1104		 * connection.
1105		 */
1106		cma_handle_nomore_events(chanp);
1107		mutex_enter(&chanp->chan_mutex);
1108		chan_state = cma_get_chan_state(chanp);
1109		mutex_exit(&chanp->chan_mutex);
1110		/* If rdma_destroy_id() was called, destroy CMID */
1111		if (chan_state == SOL_CMA_CHAN_DESTROY_PENDING) {
1112			cma_destroy_id((struct rdma_cm_id *)chanp);
1113			return (EINVAL);
1114		}
1115	}
1116
1117	if (chanp->chan_xport_type == SOL_CMA_XPORT_IB)
1118		ret = rdma_ib_accept(idp, conn_param);
1119#ifdef	IWARP_SUPPORT
1120	if (chanp->chan_xport_type == SOL_CMA_XPORT_IWARP)
1121		ret = rdma_iw_accept(idp, conn_param);
1122#endif	/* IWARP_SUPPORT */
1123
1124	if (ret && root_idp && idp->ps == RDMA_PS_TCP) {
1125		void		*find_ret;
1126		avl_index_t	where;
1127
1128		SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str,
1129		    "Delete from REQ AVL of %p IDP, idp %p",
1130		    root_idp, idp);
1131		mutex_enter(&root_chanp->chan_mutex);
1132		mutex_enter(&chanp->chan_mutex);
1133		if (chanp->chan_req_state == REQ_CMID_ACCEPTED) {
1134			ASSERT(cma_get_acpt_idp(root_idp,
1135			    chanp->chan_qp_hdl));
1136			avl_remove(&root_chanp->chan_acpt_avl_tree,
1137			    idp);
1138			find_ret = avl_find(&root_chanp->chan_req_avl_tree,
1139			    (void *)chanp->chan_qp_hdl, &where);
1140			if (find_ret) {
1141				chanp->chan_req_state = REQ_CMID_SERVER_NONE;
1142				mutex_exit(&chanp->chan_mutex);
1143				mutex_exit(&root_chanp->chan_mutex);
1144				SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
1145				    "DUPLICATE ENTRY in REQ AVL : root %p, "
1146				    "idp %p, session_id %p",
1147				    root_idp, idp, chanp->chan_session_id);
1148				return (EINVAL);
1149			}
1150			avl_insert(&root_chanp->chan_req_avl_tree, idp, where);
1151			chanp->chan_req_state = REQ_CMID_NOTIFIED;
1152		}
1153		mutex_exit(&chanp->chan_mutex);
1154		mutex_exit(&root_chanp->chan_mutex);
1155	}
1156
1157	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_accept: ret %x", ret);
1158	return (ret);
1159}
1160
1161int
1162rdma_notify(struct rdma_cm_id *idp, enum ib_event_type evt)
1163{
1164	sol_cma_chan_t		*chanp;
1165
1166	ASSERT(idp);
1167	chanp = (sol_cma_chan_t *)idp;
1168	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_notify(%p, %x)", idp, evt);
1169
1170	mutex_enter(&chanp->chan_mutex);
1171	if (cma_cas_chan_state(chanp, SOL_CMA_CHAN_ROUTE_RESLVD,
1172	    SOL_CMA_CHAN_EVENT_NOTIFIED)) {
1173		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
1174		    "rdma_notify, Invalid state");
1175		mutex_exit(&chanp->chan_mutex);
1176		return (EINVAL);
1177	}
1178	mutex_exit(&chanp->chan_mutex);
1179
1180	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_notify: ret 0");
1181	return (0);
1182}
1183
1184int
1185rdma_reject(struct rdma_cm_id *idp, const void *priv_data,
1186    uint8_t priv_data_len)
1187{
1188	struct rdma_cm_id	*root_idp;
1189	sol_cma_chan_t		*root_chanp, *chanp;
1190	int			ret = EINVAL;
1191
1192	ASSERT(idp);
1193	chanp = (sol_cma_chan_t *)idp;
1194	root_idp = CHAN_LISTEN_ROOT(chanp);
1195	root_chanp = (sol_cma_chan_t *)root_idp;
1196	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_reject(%p, %p)", idp,
1197	    priv_data, priv_data_len);
1198
1199	mutex_enter(&chanp->chan_mutex);
1200	if (cma_cas_chan_state(chanp, SOL_CMA_CHAN_LISTEN,
1201	    SOL_CMA_CHAN_REJECT)) {
1202		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
1203		    "rdma_accept, Invalid state");
1204		mutex_exit(&chanp->chan_mutex);
1205		return (EINVAL);
1206	}
1207	mutex_exit(&chanp->chan_mutex);
1208
1209	if (root_idp) {
1210		SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "reject: root_idp %p"
1211		    "REQ AVL remove %p", root_chanp, idp);
1212
1213		/*
1214		 * Remove from REQ AVL tree. If this CMID has been deleted,
1215		 * it maybe because of timeout. Return EINVAL.
1216		 */
1217		mutex_enter(&root_chanp->chan_mutex);
1218		mutex_enter(&chanp->chan_mutex);
1219		if (chanp->chan_req_state != REQ_CMID_NOTIFIED &&
1220		    chanp->chan_req_state != REQ_CMID_QUEUED) {
1221			mutex_exit(&chanp->chan_mutex);
1222			mutex_exit(&root_chanp->chan_mutex);
1223			SOL_OFS_DPRINTF_L3(sol_rdmacm_dbg_str,
1224			    "reject: root_idp %p chanp %p, not in REQ "
1225			    "AVL tree",  root_chanp, chanp);
1226			return (EINVAL);
1227		}
1228		ASSERT(cma_get_req_idp(root_idp, chanp->chan_session_id));
1229		avl_remove(&root_chanp->chan_req_avl_tree, idp);
1230		chanp->chan_req_state = REQ_CMID_SERVER_NONE;
1231		mutex_exit(&chanp->chan_mutex);
1232		mutex_exit(&root_chanp->chan_mutex);
1233	}
1234
1235	if (chanp->chan_xport_type == SOL_CMA_XPORT_IB)
1236		ret = rdma_ib_reject(idp, priv_data, priv_data_len);
1237#ifdef	IWARP_SUPPORT
1238	if (chanp->chan_xport_type == SOL_CMA_XPORT_IWARP)
1239		ret = rdma_iw_reject(idp, priv_data, priv_data_len);
1240#endif	/* IWARP_SUPPORT */
1241
1242
1243	if (!ret && root_idp) {
1244		cma_chan_state_t	chan_state;
1245
1246		/*
1247		 * Rejecting connect request, no more events for this
1248		 * connection.
1249		 */
1250		cma_handle_nomore_events(chanp);
1251		mutex_enter(&chanp->chan_mutex);
1252		chan_state = cma_get_chan_state(chanp);
1253		mutex_exit(&chanp->chan_mutex);
1254		/* If rdma_destroy_id() was called, destroy CMID */
1255		if (chan_state == SOL_CMA_CHAN_DESTROY_PENDING)
1256			cma_destroy_id((struct rdma_cm_id *)chanp);
1257	} else if (ret && root_idp) {
1258		avl_index_t	where;
1259
1260		SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str,
1261		    "reject fail: Add to Req AVL of %p IDP, idp %p,"
1262		    "session_id %p", root_idp, idp,
1263		    chanp->chan_session_id);
1264		mutex_enter(&root_chanp->chan_mutex);
1265		mutex_enter(&chanp->chan_mutex);
1266		if (chanp->chan_req_state == REQ_CMID_SERVER_NONE) {
1267			if (avl_find(&root_chanp->chan_req_avl_tree,
1268			    (void *)chanp->chan_session_id, &where)) {
1269				mutex_exit(&chanp->chan_mutex);
1270				mutex_exit(&root_chanp->chan_mutex);
1271				SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
1272				    "DUPLICATE ENTRY in REQ AVL : root %p, "
1273				    "idp %p, session_id %p",
1274				    root_idp, idp, chanp->chan_session_id);
1275				return (EINVAL);
1276			}
1277			avl_insert(&root_chanp->chan_req_avl_tree,
1278			    (void *)idp, where);
1279			chanp->chan_req_state = REQ_CMID_NOTIFIED;
1280		}
1281		mutex_exit(&chanp->chan_mutex);
1282		mutex_exit(&root_chanp->chan_mutex);
1283	}
1284
1285	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_reject: ret %x", ret);
1286	return (ret);
1287}
1288
1289int
1290rdma_disconnect(struct rdma_cm_id *idp)
1291{
1292	sol_cma_chan_t		*chanp;
1293	int			ret = EINVAL;
1294	cma_chan_state_t	state;
1295
1296	chanp = (sol_cma_chan_t *)idp;
1297	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_disconnect(%p)", idp);
1298
1299	if (!idp)
1300		return (0);
1301
1302	mutex_enter(&chanp->chan_mutex);
1303	if (!(SOL_CMAID_CONNECTED(chanp))) {
1304		SOL_OFS_DPRINTF_L3(sol_rdmacm_dbg_str,
1305		    "rdma_disconnect(%p) - Not connected!!", idp);
1306		mutex_exit(&chanp->chan_mutex);
1307		return (EINVAL);
1308	}
1309	state = cma_get_chan_state(chanp);
1310	cma_set_chan_state(chanp, SOL_CMA_CHAN_DISCONNECT);
1311	mutex_exit(&chanp->chan_mutex);
1312
1313	if (chanp->chan_xport_type == SOL_CMA_XPORT_IB) {
1314		ret = rdma_ib_disconnect(idp);
1315#ifdef	IWARP_SUPPORT
1316	} else if (chanp->chan_xport_type == SOL_CMA_XPORT_IWARP) {
1317		ret = rdma_iw_disconnect(idp);
1318#endif	/* IWARP_SUPPORT */
1319	}
1320
1321	if (ret) {
1322		mutex_enter(&chanp->chan_mutex);
1323		cma_set_chan_state(chanp, state);
1324		mutex_exit(&chanp->chan_mutex);
1325		return (ret);
1326	}
1327
1328	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_disconnect: ret %x", ret);
1329	return (ret);
1330}
1331
1332int
1333rdma_init_qp_attr(struct rdma_cm_id *idp, struct ib_qp_attr *qpattr,
1334    int *qp_attr_mask)
1335{
1336	sol_cma_chan_t		*chanp;
1337	int			ret = EINVAL;
1338
1339	ASSERT(idp);
1340	chanp = (sol_cma_chan_t *)idp;
1341	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_init_qp_attr(%p, %p, %p)",
1342	    idp, qpattr, qp_attr_mask);
1343
1344	if (chanp->chan_xport_type == SOL_CMA_XPORT_IB) {
1345		ret = rdma_ib_init_qp_attr(idp, qpattr, qp_attr_mask);
1346#ifdef	IWARP_SUPPORT
1347	} else if (chanp->chan_xport_type == SOL_CMA_XPORT_IWARP)
1348		ret = rdma_iw_init_qp_attr(idp, qpattr, qp_attr_mask);
1349#endif	/* IWARP_SUPPORT */
1350	} else {
1351		ret = EINVAL;
1352	}
1353
1354	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str,
1355	    "rdma_init_qp_attr: ret %x", ret);
1356
1357	return (ret);
1358}
1359
1360int
1361rdma_join_multicast(struct rdma_cm_id *idp, struct sockaddr *addr,
1362    void *context)
1363{
1364	sol_cma_chan_t		*chanp;
1365	int			ret = ENODEV;
1366	cma_chan_state_t	state;
1367
1368	ASSERT(idp);
1369	chanp = (sol_cma_chan_t *)idp;
1370	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str,
1371	    "rdma_join_multicast(%p, %p, %p)",
1372	    idp, addr, context);
1373
1374	mutex_enter(&chanp->chan_mutex);
1375	state = cma_get_chan_state(chanp);
1376	if (state != SOL_CMA_CHAN_BOUND &&
1377	    state != SOL_CMA_CHAN_ROUTE_RESLVD &&
1378	    state != SOL_CMA_CHAN_ADDR_RESLVD) {
1379		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
1380		    "rdma_join_multicast, Invalid state");
1381		mutex_exit(&chanp->chan_mutex);
1382		return (EINVAL);
1383	}
1384
1385	if (chanp->chan_xport_type == SOL_CMA_XPORT_IB)
1386		ret = rdma_ib_join_multicast(idp, addr, context);
1387#ifdef	IWARP_SUPPORT
1388	/* No support for Multicast on iWARP */
1389	else if (chanp->chan_xport_type == SOL_CMA_XPORT_IWARP)
1390		ret = ENOTSUP;
1391#endif	/* IWARP_SUPPORT */
1392	mutex_exit(&chanp->chan_mutex);
1393
1394	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str,
1395	    "rdma_join_multicast: ret %x", ret);
1396	return (ret);
1397}
1398
1399void
1400rdma_leave_multicast(struct rdma_cm_id *idp, struct sockaddr *addr)
1401{
1402	sol_cma_chan_t		*chanp;
1403	cma_chan_state_t	state;
1404
1405	ASSERT(idp);
1406	chanp = (sol_cma_chan_t *)idp;
1407	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_leave_multicast(%p, %p)",
1408	    idp, addr);
1409
1410	mutex_enter(&chanp->chan_mutex);
1411	state = cma_get_chan_state(chanp);
1412	if (state != SOL_CMA_CHAN_BOUND &&
1413	    state != SOL_CMA_CHAN_ROUTE_RESLVD &&
1414	    state != SOL_CMA_CHAN_ADDR_RESLVD) {
1415		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
1416		    "rdma_leave_multicast, Invalid state");
1417		mutex_exit(&chanp->chan_mutex);
1418		return;
1419	}
1420
1421	if (chanp->chan_xport_type == SOL_CMA_XPORT_IB)
1422		rdma_ib_leave_multicast(idp, addr);
1423#ifdef	IWARP_SUPPORT
1424	/* No support for Multicast on iWARP */
1425	else if (chanp->chan_xport_type == SOL_CMA_XPORT_IWARP)
1426		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
1427		    "rdma_leave_multicast, iWARP");
1428#endif	/* IWARP_SUPPORT */
1429	mutex_exit(&chanp->chan_mutex);
1430
1431	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_join_multicast: ret");
1432}
1433
1434/*
1435 * Functions to compare to rdma_cm_id *, used by AVL tree
1436 * routines.
1437 */
1438int
1439sol_cma_req_cmid_cmp(const void *p1, const void *p2)
1440{
1441	sol_cma_chan_t		*chanp;
1442
1443	chanp = (sol_cma_chan_t *)p2;
1444	if (chanp->chan_session_id > p1)
1445		return (+1);
1446	else if (chanp->chan_session_id < p1)
1447		return (-1);
1448	else
1449		return (0);
1450}
1451
1452int
1453sol_cma_cmid_cmp(const void *p1, const void *p2)
1454{
1455	sol_cma_chan_t		*chanp;
1456
1457	chanp = (sol_cma_chan_t *)p2;
1458	if (chanp->chan_qp_hdl > p1)
1459		return (+1);
1460	else if (chanp->chan_qp_hdl < p1)
1461		return (-1);
1462	else
1463		return (0);
1464}
1465
1466/*
1467 * Function to compare two sol_cma_glbl_listen_t *, used by
1468 * AVL tree routines.
1469 */
1470int
1471sol_cma_svc_cmp(const void *p1, const void *p2)
1472{
1473	sol_cma_glbl_listen_t	*listenp;
1474	uint64_t		sid;
1475
1476	sid = *(uint64_t *)p1;
1477	listenp = (sol_cma_glbl_listen_t *)p2;
1478	if (listenp->cma_listen_chan_sid > sid)
1479		return (+1);
1480	else if (listenp->cma_listen_chan_sid < sid)
1481		return (-1);
1482	else
1483		return (0);
1484}
1485
1486static int
1487cma_init_listen_root(sol_cma_chan_t *chanp)
1488{
1489	sol_cma_glbl_listen_t	*cma_listenp;
1490	sol_cma_listen_info_t	*chan_listenp;
1491	int			rc = 0;
1492	avl_index_t		where = 0;
1493	uint64_t		listen_sid;
1494
1495	ASSERT(chanp);
1496	ASSERT(chanp->chan_listenp);
1497	chan_listenp = chanp->chan_listenp;
1498
1499	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str,
1500	    "cma_init_listen_root(%p)", chanp);
1501
1502	/*
1503	 * First search for matching global listen_info for this SID.
1504	 * If found with the same client handle, reuse the service
1505	 * handle, if matching SID is found with different client
1506	 * handle, return EINVAL.
1507	 */
1508	listen_sid = ibcma_init_root_sid(chanp);
1509	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str,
1510	    "cma_init_listen_root: search SID 0x%llx",
1511	    listen_sid);
1512
1513	mutex_enter(&sol_cma_glob_mutex);
1514	cma_listenp = avl_find(&sol_cma_glbl_listen_tree,
1515	    (void *) &listen_sid, &where);
1516	if (cma_listenp && cma_listenp->cma_listen_clnt_hdl ==
1517	    chanp->chan_ib_client_hdl) {
1518		SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str,
1519		    "cma_init_listen_root: matching listenp %p SID 0x%llx",
1520		    cma_listenp, listen_sid);
1521		chan_listenp->listen_entry = add_genlist(
1522		    &cma_listenp->cma_listen_chan_list,
1523		    (uintptr_t)chanp, NULL);
1524		chan_listenp->chan_glbl_listen_info = cma_listenp;
1525		ibcma_copy_srv_hdl(chanp, cma_listenp);
1526		mutex_exit(&sol_cma_glob_mutex);
1527		return (0);
1528	} else if (cma_listenp) {
1529		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
1530		    "cma_init_listen_root: listenp %p, SID 0x%llx match, "
1531		    "client hdl prev %p, new %p mismatch",
1532		    cma_listenp, listen_sid,
1533		    cma_listenp->cma_listen_clnt_hdl,
1534		    chanp->chan_ib_client_hdl);
1535		mutex_exit(&sol_cma_glob_mutex);
1536		return (EINVAL);
1537	}
1538
1539	cma_listenp = kmem_zalloc(sizeof (sol_cma_glbl_listen_t), KM_SLEEP);
1540	init_genlist(&cma_listenp->cma_listen_chan_list);
1541	chan_listenp->listen_entry = add_genlist(
1542	    &cma_listenp->cma_listen_chan_list, (uintptr_t)chanp, NULL);
1543	chan_listenp->chan_glbl_listen_info = cma_listenp;
1544	cma_listenp->cma_listen_clnt_hdl = chanp->chan_ib_client_hdl;
1545	cma_listenp->cma_listen_chan_sid = listen_sid;
1546
1547	rc = ibcma_init_root_chan(chanp, cma_listenp);
1548	if (rc) {
1549		mutex_exit(&sol_cma_glob_mutex);
1550		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
1551		    "cma_init_listen_root: ibcma_init_root_chan failed!!");
1552		delete_genlist(&cma_listenp->cma_listen_chan_list,
1553		    chan_listenp->listen_entry);
1554		kmem_free(cma_listenp, sizeof (sol_cma_glbl_listen_t));
1555		return (rc);
1556	}
1557	avl_insert(&sol_cma_glbl_listen_tree, cma_listenp, where);
1558	mutex_exit(&sol_cma_glob_mutex);
1559	return (0);
1560}
1561
1562static void
1563cma_fini_listen_root(sol_cma_chan_t *chanp)
1564{
1565	sol_cma_glbl_listen_t	*cma_listenp;
1566	sol_cma_listen_info_t	*chan_listenp;
1567
1568	ASSERT(chanp);
1569	ASSERT(chanp->chan_listenp);
1570	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "cma_fini_listen_root(%p)",
1571	    chanp);
1572	chan_listenp = chanp->chan_listenp;
1573	cma_listenp = chan_listenp->chan_glbl_listen_info;
1574	ASSERT(cma_listenp);
1575	mutex_enter(&sol_cma_glob_mutex);
1576	delete_genlist(&cma_listenp->cma_listen_chan_list,
1577	    chan_listenp->listen_entry);
1578	if (genlist_empty(&cma_listenp->cma_listen_chan_list)) {
1579		if (ibcma_fini_root_chan(chanp) == 0) {
1580			avl_remove(&sol_cma_glbl_listen_tree,
1581			    cma_listenp);
1582			kmem_free(cma_listenp,
1583			    sizeof (sol_cma_glbl_listen_t));
1584		} else
1585			SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
1586			    "cma_fini_listen_root: "
1587			    "ibcma_fini_root_chan failed");
1588	}
1589
1590	mutex_exit(&sol_cma_glob_mutex);
1591}
1592
1593typedef struct cma_event_async_arg {
1594	struct rdma_cm_id	*idp;
1595	enum rdma_cm_event_type	event;
1596	int			status;
1597	union {
1598		struct rdma_conn_param	conn;
1599		struct rdma_ud_param	param;
1600	} un;
1601	struct rdma_conn_param	*conn_param;
1602	struct rdma_ud_param	*ud_paramp;
1603} cma_event_async_arg_t;
1604
1605static void cma_generate_event_sync(struct rdma_cm_id *,
1606    enum rdma_cm_event_type, int, struct rdma_conn_param *,
1607    struct rdma_ud_param *);
1608
1609void
1610cma_generate_event_thr(void *arg)
1611{
1612	cma_event_async_arg_t	*event_arg = (cma_event_async_arg_t *)arg;
1613
1614	cma_generate_event_sync(event_arg->idp, event_arg->event,
1615	    event_arg->status, event_arg->conn_param,
1616	    event_arg->ud_paramp);
1617
1618	if (event_arg->conn_param && event_arg->conn_param->private_data_len)
1619		kmem_free((void *)event_arg->conn_param->private_data,
1620		    event_arg->conn_param->private_data_len);
1621	if (event_arg->ud_paramp && event_arg->ud_paramp->private_data_len)
1622		kmem_free((void *)event_arg->ud_paramp->private_data,
1623		    event_arg->ud_paramp->private_data_len);
1624	kmem_free(arg, sizeof (cma_event_async_arg_t));
1625}
1626
1627void
1628cma_generate_event(struct rdma_cm_id *idp, enum rdma_cm_event_type event,
1629    int status, struct rdma_conn_param *conn_param,
1630    struct rdma_ud_param *ud_paramp)
1631{
1632	cma_event_async_arg_t	*event_arg;
1633	sol_cma_chan_t		*chanp = (sol_cma_chan_t *)idp;
1634
1635	/*
1636	 * Set SOL_CMA_CALLER_EVENT_PROGRESS to indicate event
1637	 * notification is in progress, so that races between
1638	 * rdma_destroy_id() and event notification is taken care.
1639	 *
1640	 * If rdma_destroy_id() has been called for this CMID, call
1641	 * cma_generate_event_sync() which skips notification to the
1642	 * consumer and handles the event.
1643	 */
1644	mutex_enter(&chanp->chan_mutex);
1645	chanp->chan_cmid_destroy_state |= SOL_CMA_CALLER_EVENT_PROGRESS;
1646	if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) {
1647		mutex_exit(&chanp->chan_mutex);
1648		cma_generate_event_sync(idp, event, status, conn_param,
1649		    ud_paramp);
1650		return;
1651	}
1652	mutex_exit(&chanp->chan_mutex);
1653
1654	event_arg = kmem_zalloc(sizeof (cma_event_async_arg_t), KM_SLEEP);
1655	event_arg->idp = idp;
1656	event_arg->event = event;
1657	event_arg->status = status;
1658	event_arg->conn_param = NULL;
1659	event_arg->ud_paramp = NULL;
1660	if (conn_param && conn_param->private_data_len) {
1661		bcopy(conn_param, &(event_arg->un.conn),
1662		    sizeof (struct rdma_conn_param));
1663		event_arg->conn_param = &(event_arg->un.conn);
1664		event_arg->conn_param->private_data = kmem_zalloc(
1665		    conn_param->private_data_len, KM_SLEEP);
1666		bcopy(conn_param->private_data,
1667		    (void *)event_arg->conn_param->private_data,
1668		    conn_param->private_data_len);
1669	} else if (conn_param && conn_param->private_data_len == 0) {
1670		bcopy(conn_param, &(event_arg->un.conn),
1671		    sizeof (struct rdma_conn_param));
1672	} else if (ud_paramp) {
1673		bcopy(ud_paramp, &(event_arg->un.param),
1674		    sizeof (struct rdma_ud_param));
1675		event_arg->ud_paramp = &(event_arg->un.param);
1676		if (ud_paramp->private_data_len) {
1677			event_arg->ud_paramp->private_data = kmem_zalloc(
1678			    ud_paramp->private_data_len, KM_SLEEP);
1679			bcopy(ud_paramp->private_data,
1680			    (void *)event_arg->ud_paramp->private_data,
1681			    ud_paramp->private_data_len);
1682		} else if (ud_paramp->private_data) {
1683			event_arg->ud_paramp->private_data =
1684			    ud_paramp->private_data;
1685		}
1686	}
1687
1688	if (taskq_dispatch(system_taskq, cma_generate_event_thr,
1689	    (void *)event_arg, TQ_SLEEP) == 0) {
1690		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
1691		    "generate_event_async: taskq_dispatch() failed!!");
1692		mutex_enter(&chanp->chan_mutex);
1693		chanp->chan_cmid_destroy_state &=
1694		    ~SOL_CMA_CALLER_EVENT_PROGRESS;
1695		if (chanp->chan_cmid_destroy_state &
1696		    SOL_CMA_CALLER_CMID_DESTROYED)
1697			cv_broadcast(&chanp->chan_destroy_cv);
1698		mutex_exit(&chanp->chan_mutex);
1699	}
1700}
1701
1702static void
1703cma_generate_event_sync(struct rdma_cm_id *idp, enum rdma_cm_event_type event,
1704    int status, struct rdma_conn_param *conn_param,
1705    struct rdma_ud_param *ud_paramp)
1706{
1707	struct rdma_cm_event	cm_event;
1708	sol_cma_chan_t		*chanp = (sol_cma_chan_t *)idp;
1709	struct rdma_cm_id	*root_idp = NULL;
1710	sol_cma_chan_t		*root_chanp;
1711	int			ret;
1712	cma_chan_state_t	chan_state;
1713
1714	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "generate_event_sync(%p, %x, "
1715	    "%x, %p, %p", idp, event, status, conn_param, ud_paramp);
1716
1717	bzero(&cm_event, sizeof (cm_event));
1718	cm_event.event = event;
1719	cm_event.status = status;
1720	if (conn_param)
1721		bcopy((void *)conn_param, (void *)(&(cm_event.param.conn)),
1722		    sizeof (struct rdma_conn_param));
1723	else if (ud_paramp)
1724		bcopy((void *)ud_paramp, (void *)(&(cm_event.param.ud)),
1725		    sizeof (struct rdma_ud_param));
1726
1727	/*
1728	 * If the consumer has destroyed the context for this CMID -
1729	 * do not notify, skip to handling the sol_ofs specific
1730	 * handling of the event.
1731	 */
1732	mutex_enter(&chanp->chan_mutex);
1733	if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) {
1734		mutex_exit(&chanp->chan_mutex);
1735		goto ofs_consume_event;
1736	}
1737	mutex_exit(&chanp->chan_mutex);
1738
1739	root_idp = CHAN_LISTEN_ROOT(chanp);
1740	root_chanp = (sol_cma_chan_t *)root_idp;
1741	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "gen_event: root_idp %p",
1742	    root_idp);
1743
1744	if (event == RDMA_CM_EVENT_CONNECT_REQUEST) {
1745		/*
1746		 * Update chan_req_state for the REQ CMID. Decrement
1747		 * count of REQ CMIDs not notifed to consumer.
1748		 */
1749		ASSERT(root_idp);
1750		mutex_enter(&root_chanp->chan_mutex);
1751		root_chanp->chan_req_cnt--;
1752#ifdef	DEBUG
1753		SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str,
1754		    "Dec req_cnt of %p IDP, idp %p, req_cnt %x",
1755		    root_idp, idp, root_chanp->chan_req_cnt);
1756#endif
1757		mutex_exit(&root_chanp->chan_mutex);
1758	}
1759
1760	/* Pass the event to the client */
1761	ret = (idp->event_handler) (idp, &cm_event);
1762
1763	if (ret) {
1764		/*
1765		 * If the consumer returned failure :
1766		 * 	CONNECT_REQUEST :
1767		 * 	1. rdma_disconnect() to disconnect connection.
1768		 * 	2. wakeup destroy, if destroy has been called
1769		 * 		for this CMID
1770		 * 	3. Destroy CMID if rdma_destroy has not been
1771		 * 		called.
1772		 * 	DISCONNECTED :
1773		 * 	1. call cma_handle_nomore_events() to cleanup
1774		 * 	Other Events :
1775		 * 	1. Client is expected to destroy the CMID.
1776		 */
1777		if (event == RDMA_CM_EVENT_CONNECT_REQUEST) {
1778			SOL_OFS_DPRINTF_L4(sol_rdmacm_dbg_str,
1779			    "cma_generate_event_async: consumer failed %d "
1780			    "event", event);
1781			if (rdma_disconnect(idp)) {
1782				SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
1783				    "generate_event_async: rdma_disconnect "
1784				    "failed");
1785			}
1786			mutex_enter(&chanp->chan_mutex);
1787			ASSERT(SOL_IS_SERVER_CMID(chanp));
1788			chanp->chan_req_state = REQ_CMID_SERVER_NONE;
1789			chanp->chan_cmid_destroy_state &=
1790			    ~SOL_CMA_CALLER_EVENT_PROGRESS;
1791			if (chanp->chan_cmid_destroy_state &
1792			    SOL_CMA_CALLER_CMID_DESTROYED) {
1793				cv_broadcast(&chanp->chan_destroy_cv);
1794				mutex_exit(&chanp->chan_mutex);
1795			} else {
1796				mutex_exit(&chanp->chan_mutex);
1797				rdma_destroy_id(idp);
1798			}
1799		} else if (event == RDMA_CM_EVENT_DISCONNECTED) {
1800			SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
1801			    "generate_event_async: consumer failed %d event",
1802			    event);
1803			cma_handle_nomore_events(chanp);
1804			mutex_enter(&chanp->chan_mutex);
1805			chan_state = cma_get_chan_state(chanp);
1806			chanp->chan_cmid_destroy_state &=
1807			    ~SOL_CMA_CALLER_EVENT_PROGRESS;
1808			if (chanp->chan_cmid_destroy_state &
1809			    SOL_CMA_CALLER_CMID_DESTROYED) {
1810				cv_broadcast(&chanp->chan_destroy_cv);
1811				mutex_exit(&chanp->chan_mutex);
1812			} else if (chan_state == SOL_CMA_CHAN_DESTROY_PENDING) {
1813				/* rdma_destroy_id() called: destroy CMID */
1814				mutex_exit(&chanp->chan_mutex);
1815				cma_destroy_id((struct rdma_cm_id *)chanp);
1816			} else
1817				mutex_exit(&chanp->chan_mutex);
1818		} else {
1819			SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
1820			    "generate_event_async: consumer failed %d event",
1821			    event);
1822		}
1823
1824		return;
1825	}
1826ofs_consume_event:
1827	if (event == RDMA_CM_EVENT_DISCONNECTED) {
1828		cma_chan_state_t	chan_state;
1829
1830		cma_handle_nomore_events(chanp);
1831		mutex_enter(&chanp->chan_mutex);
1832		chan_state = cma_get_chan_state(chanp);
1833		chanp->chan_cmid_destroy_state &=
1834		    ~SOL_CMA_CALLER_EVENT_PROGRESS;
1835		if (chanp->chan_cmid_destroy_state &
1836		    SOL_CMA_CALLER_CMID_DESTROYED) {
1837			cv_broadcast(&chanp->chan_destroy_cv);
1838			mutex_exit(&chanp->chan_mutex);
1839		} else if (chan_state == SOL_CMA_CHAN_DESTROY_PENDING) {
1840			/* If rdma_destroy_id() was called, destroy CMID */
1841			mutex_exit(&chanp->chan_mutex);
1842			cma_destroy_id((struct rdma_cm_id *)chanp);
1843		} else
1844			mutex_exit(&chanp->chan_mutex);
1845		return;
1846	} else if (IS_UDP_CMID(idp) && event == RDMA_CM_EVENT_UNREACHABLE) {
1847		/*
1848		 * If rdma_destroy_id() was called, destroy CMID
1849		 * If not chan_connect_flag/ chan_req_state has already been
1850		 * set to indicate that it can be deleted.
1851		 */
1852		mutex_enter(&chanp->chan_mutex);
1853		chan_state = cma_get_chan_state(chanp);
1854		chanp->chan_cmid_destroy_state &=
1855		    ~SOL_CMA_CALLER_EVENT_PROGRESS;
1856		if (chanp->chan_cmid_destroy_state &
1857		    SOL_CMA_CALLER_CMID_DESTROYED) {
1858			cv_broadcast(&chanp->chan_destroy_cv);
1859			mutex_exit(&chanp->chan_mutex);
1860		} else if (chan_state == SOL_CMA_CHAN_DESTROY_PENDING) {
1861			mutex_exit(&chanp->chan_mutex);
1862			cma_destroy_id(idp);
1863		} else
1864			mutex_exit(&chanp->chan_mutex);
1865		return;
1866	}
1867
1868	mutex_enter(&chanp->chan_mutex);
1869	chanp->chan_cmid_destroy_state &= ~SOL_CMA_CALLER_EVENT_PROGRESS;
1870	if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED)
1871		cv_broadcast(&chanp->chan_destroy_cv);
1872	mutex_exit(&chanp->chan_mutex);
1873}
1874
1875/* Local Static functions */
1876static struct rdma_cm_id *
1877cma_alloc_chan(rdma_cm_event_handler evt_hdlr, void *context,
1878    enum rdma_port_space ps)
1879{
1880	struct rdma_cm_id	*rdma_idp;
1881	sol_cma_chan_t		*chanp;
1882
1883	chanp = kmem_zalloc(sizeof (sol_cma_chan_t), KM_SLEEP);
1884	mutex_init(&chanp->chan_mutex, NULL, MUTEX_DRIVER, NULL);
1885	cv_init(&chanp->chan_destroy_cv, NULL, CV_DRIVER, NULL);
1886	rdma_idp = &(chanp->chan_rdma_cm);
1887	rdma_idp->context = context;
1888	rdma_idp->ps = ps;
1889	rdma_idp->event_handler = evt_hdlr;
1890	mutex_enter(&chanp->chan_mutex);
1891	cma_set_chan_state(chanp, SOL_CMA_CHAN_IDLE);
1892	avl_create(&chanp->chan_req_avl_tree, sol_cma_req_cmid_cmp,
1893	    sizeof (sol_cma_chan_t),
1894	    offsetof(sol_cma_chan_t, chan_req_avl_node));
1895	avl_create(&chanp->chan_acpt_avl_tree, sol_cma_cmid_cmp,
1896	    sizeof (sol_cma_chan_t),
1897	    offsetof(sol_cma_chan_t, chan_acpt_avl_node));
1898	mutex_exit(&chanp->chan_mutex);
1899
1900	return (rdma_idp);
1901}
1902
1903/* Change the state of sol_cma_chan_t */
1904static void
1905cma_set_chan_state(sol_cma_chan_t *chanp, cma_chan_state_t newstate)
1906{
1907	ASSERT(MUTEX_HELD(&chanp->chan_mutex));
1908	chanp->chan_state = newstate;
1909}
1910
1911cma_chan_state_t
1912cma_get_chan_state(sol_cma_chan_t *chanp)
1913{
1914	ASSERT(MUTEX_HELD(&chanp->chan_mutex));
1915	return (chanp->chan_state);
1916}
1917
1918/* Check & Swap the state of sol_ucma_chan_t */
1919static int
1920cma_cas_chan_state(sol_cma_chan_t *chanp, cma_chan_state_t prevstate,
1921    cma_chan_state_t newstate)
1922{
1923	int	ret = 0;
1924
1925	ASSERT(MUTEX_HELD(&chanp->chan_mutex));
1926	if (chanp->chan_state != prevstate)
1927		ret = -1;
1928	else
1929		chanp->chan_state = newstate;
1930
1931	return (ret);
1932}
1933
1934static void
1935cma_free_listen_list(struct rdma_cm_id *idp)
1936{
1937	genlist_entry_t	*entry;
1938	sol_cma_chan_t	*chanp = (sol_cma_chan_t *)idp;
1939
1940	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "cma_free_listen_list(%p)", idp);
1941	mutex_enter(&chanp->chan_mutex);
1942	entry = remove_genlist_head(&(CHAN_LISTEN_LIST(chanp)));
1943	mutex_exit(&chanp->chan_mutex);
1944	while (entry) {
1945		sol_cma_chan_t	*ep_chanp;
1946
1947		ep_chanp = (sol_cma_chan_t *)entry->data;
1948		SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "fini_ep_chan: %p",
1949		    ep_chanp);
1950		if (ibcma_fini_ep_chan(ep_chanp) == 0) {
1951			genlist_entry_t		*entry1;
1952			struct ib_device	*device;
1953			cma_device_t		*cma_device;
1954
1955			ASSERT(ep_chanp->chan_listenp);
1956			mutex_enter(&ep_chanp->chan_mutex);
1957			entry1 = ep_chanp->chan_listenp->listen_ep_dev_entry;
1958			device = ep_chanp->chan_listenp->listen_ep_device;
1959			ASSERT(device);
1960			cma_device = device->data;
1961			delete_genlist(&cma_device->cma_epchan_list,
1962			    entry1);
1963			sol_cma_release_device(
1964			    (struct rdma_cm_id *)ep_chanp);
1965			mutex_exit(&ep_chanp->chan_mutex);
1966			if (ep_chanp->chan_listenp)
1967				kmem_free(ep_chanp->chan_listenp,
1968				    sizeof (sol_cma_listen_info_t));
1969
1970			mutex_destroy(&ep_chanp->chan_mutex);
1971			cv_destroy(&ep_chanp->chan_destroy_cv);
1972			kmem_free(ep_chanp, sizeof (sol_cma_chan_t));
1973			kmem_free(entry, sizeof (genlist_entry_t));
1974		}
1975
1976		mutex_enter(&chanp->chan_mutex);
1977		entry = remove_genlist_head(&(CHAN_LISTEN_LIST(chanp)));
1978		mutex_exit(&chanp->chan_mutex);
1979	}
1980}
1981
1982/*
1983 * Destroy a listening CMID when :
1984 *	a. All CONNECTION REQUEST recieved have been rejected
1985 *	   or closed.
1986 *	b. No CONNECTION REQUEST recieved.
1987 * Do not destroy a listening CMID when :
1988 *	a. CONNECTION REQUEST has been recieved and not been
1989 *	   accepted from the passive / server side.
1990 *	b. CONNECTION REQUEST has been recieved and has been
1991 *	   accepted from the passive server side.
1992 *	Mark the listening CMID as destroy pending.
1993 *
1994 * For CMIDs created for rdma_connect() or created for a
1995 * CONNECT request, destroy the CMID only when :
1996 *       CONNECTION has been closed or rejected.
1997 *
1998 *       Mark the CMID as destroy pending.
1999 *
2000 * When a connection is rejected or closed :
2001 *	Check if flag indicates - destroy pending,
2002 *	cma_destroy_id() is called, this also does
2003 *
2004 *	If there is a listening CMID assosiated with it,
2005 *	   call cma_destroy_if(listen_cmid);
2006 */
2007void
2008cma_destroy_id(struct rdma_cm_id *idp)
2009{
2010	sol_cma_chan_t		*chanp = (sol_cma_chan_t *)idp;
2011	cma_chan_state_t	state;
2012	ulong_t			acpt_nodes, req_nodes;
2013
2014	mutex_enter(&chanp->chan_mutex);
2015	acpt_nodes = avl_numnodes(&chanp->chan_acpt_avl_tree);
2016	req_nodes = avl_numnodes(&chanp->chan_req_avl_tree);
2017	state = cma_get_chan_state(chanp);
2018	SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "cma_destroy_id(%p)- "
2019	    "est CMIDs %ld, req CMID %ld, listen_root %p, state %x, %x",
2020	    idp, acpt_nodes, req_nodes, chanp->listen_root,
2021	    state, chanp->chan_req_state);
2022
2023	/*
2024	 * If there are either REQ recieved or Established CMIDs just return.
2025	 * rdma_destroy() for these CMIDs can be called by client later.
2026	 */
2027	if (acpt_nodes || req_nodes) {
2028		cma_set_chan_state(chanp, SOL_CMA_CHAN_DESTROY_PENDING);
2029		mutex_exit(&chanp->chan_mutex);
2030		return;
2031	}
2032	cma_set_chan_state(chanp, SOL_CMA_CHAN_DESTROYING);
2033	avl_destroy(&chanp->chan_req_avl_tree);
2034	avl_destroy(&chanp->chan_acpt_avl_tree);
2035
2036	mutex_exit(&chanp->chan_mutex);
2037	if (idp->route.path_rec) {
2038		kmem_free(idp->route.path_rec,
2039		    sizeof (struct ib_sa_path_rec) * idp->route.num_paths);
2040		idp->route.path_rec = NULL;
2041	}
2042
2043	switch (chanp->chan_xport_type) {
2044	case SOL_CMA_XPORT_NONE :
2045		break;
2046	case SOL_CMA_XPORT_IB :
2047		rdma_ib_destroy_id(idp);
2048		break;
2049#ifdef	IWARP_SUPPORT
2050	case SOL_CMA_XPORT_IWARP :
2051		rdma_iw_destroy_id(idp);
2052		break;
2053#endif	/* IWARP_SUPPORT */
2054	default :
2055		SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str,
2056		    "cma_destroy_id: Unsupported xport type %x",
2057		    chanp->chan_xport_type);
2058		break;
2059	}
2060
2061	/*
2062	 * Flush out & Free all listeners wrt to this ID
2063	 * No locking is required as this code is executed
2064	 * all REQ CMIDs have been destroyed. listen_list
2065	 * will therefore not be modified during this loop.
2066	 */
2067	if (chanp->chan_listenp) {
2068		cma_free_listen_list(idp);
2069		cma_fini_listen_root(chanp);
2070		kmem_free((void *)chanp->chan_listenp,
2071		    sizeof (sol_cma_listen_info_t));
2072		chanp->chan_listenp = NULL;
2073	}
2074
2075	if (chanp->listen_root) {
2076		struct rdma_cm_id	*root_idp;
2077		sol_cma_chan_t		*root_chanp;
2078
2079		root_idp = chanp->listen_root;
2080		root_chanp = (sol_cma_chan_t *)root_idp;
2081		mutex_enter(&root_chanp->chan_mutex);
2082		state = cma_get_chan_state(root_chanp);
2083		acpt_nodes = avl_numnodes(&root_chanp->chan_acpt_avl_tree);
2084		req_nodes = avl_numnodes(&root_chanp->chan_req_avl_tree);
2085		mutex_exit(&root_chanp->chan_mutex);
2086		SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "cma_destroy_id(%p)-"
2087		    " root idp %p, state %x, acpt_nodes %ld, req_nodes %ld",
2088		    idp, root_idp, state, acpt_nodes, req_nodes);
2089
2090		if (state == SOL_CMA_CHAN_DESTROY_PENDING &&
2091		    req_nodes == 0UL && acpt_nodes == 0UL) {
2092			mutex_enter(&root_chanp->chan_mutex);
2093			root_chanp->chan_req_state = REQ_CMID_SERVER_NONE;
2094			mutex_exit(&root_chanp->chan_mutex);
2095			cma_destroy_id(root_idp);
2096		} else if (state == SOL_CMA_CHAN_DESTROY_WAIT &&
2097		    req_nodes == 0UL && acpt_nodes == 0UL) {
2098			mutex_enter(&root_chanp->chan_mutex);
2099			cma_set_chan_state(root_chanp,
2100			    SOL_CMA_CHAN_DESTROY_PENDING);
2101			root_chanp->chan_req_state = REQ_CMID_SERVER_NONE;
2102			cv_broadcast(&root_chanp->chan_destroy_cv);
2103			mutex_exit(&root_chanp->chan_mutex);
2104		}
2105	}
2106
2107	mutex_destroy(&chanp->chan_mutex);
2108	cv_destroy(&chanp->chan_destroy_cv);
2109	kmem_free(chanp, sizeof (sol_cma_chan_t));
2110}
2111
2112/*
2113 * Server TCP disconnect for an established channel.
2114 *	If destroy_id() has been called for the listening
2115 *	CMID and there are no more CMIDs with pending
2116 *	events corresponding to the listening CMID, free
2117 *	the listening CMID.
2118 *
2119 */
2120static void
2121cma_handle_nomore_events(sol_cma_chan_t *chanp)
2122{
2123	struct rdma_cm_id	*idp, *root_idp;
2124	sol_cma_chan_t		*root_chanp;
2125	cma_chan_state_t	state;
2126	ulong_t			req_nodes, acpt_nodes;
2127
2128	idp = (struct rdma_cm_id *)chanp;
2129	root_idp = CHAN_LISTEN_ROOT(chanp);
2130	root_chanp = (sol_cma_chan_t *)root_idp;
2131	if (!root_chanp)
2132		return;
2133
2134	mutex_enter(&root_chanp->chan_mutex);
2135	mutex_enter(&chanp->chan_mutex);
2136	CHAN_LISTEN_ROOT(chanp) = NULL;
2137	root_chanp->chan_req_total_cnt--;
2138
2139	/*
2140	 * Removal of CMID from the AVL trees should already have been done
2141	 * by now. Below code mainly as a  safety net.
2142	 */
2143	if (chanp->chan_req_state == REQ_CMID_ACCEPTED) {
2144		ASSERT(chanp->chan_qp_hdl);
2145		ASSERT(cma_get_acpt_idp(root_idp,
2146		    chanp->chan_qp_hdl));
2147		avl_remove(&root_chanp->chan_acpt_avl_tree, idp);
2148		chanp->chan_req_state = REQ_CMID_SERVER_NONE;
2149	}
2150	if (REQ_CMID_IN_REQ_AVL_TREE(chanp)) {
2151		ASSERT(chanp->chan_session_id);
2152		ASSERT(cma_get_req_idp(root_idp,
2153		    chanp->chan_session_id));
2154		avl_remove(&root_chanp->chan_req_avl_tree, idp);
2155		chanp->chan_req_state = REQ_CMID_SERVER_NONE;
2156	}
2157
2158	state = cma_get_chan_state(root_chanp);
2159	req_nodes = avl_numnodes(&root_chanp->chan_req_avl_tree);
2160	acpt_nodes = avl_numnodes(&root_chanp->chan_acpt_avl_tree);
2161	mutex_exit(&chanp->chan_mutex);
2162	mutex_exit(&root_chanp->chan_mutex);
2163	if (state == SOL_CMA_CHAN_DESTROY_PENDING && req_nodes == 0UL &&
2164	    acpt_nodes == 0UL)
2165		cma_destroy_id(root_idp);
2166}
2167
2168extern int ib_modify_qp(struct ib_qp *, struct ib_qp_attr *, int);
2169extern int rdma_init_qp_attr(struct rdma_cm_id *, struct ib_qp_attr *,
2170    int *);
2171
2172static int
2173cma_init_ud_qp(sol_cma_chan_t *chanp, struct ib_qp *qp)
2174{
2175	struct ib_qp_attr qp_attr;
2176	int qp_attr_mask, ret;
2177
2178	qp_attr.qp_state = IB_QPS_INIT;
2179	ret = rdma_init_qp_attr(&chanp->chan_rdma_cm, &qp_attr, &qp_attr_mask);
2180	if (ret)
2181		return (ret);
2182
2183	ret = ib_modify_qp(qp, &qp_attr, qp_attr_mask);
2184	if (ret)
2185		return (ret);
2186
2187	qp_attr.qp_state = IB_QPS_RTR;
2188	ret = ib_modify_qp(qp, &qp_attr, IB_QP_STATE);
2189	if (ret)
2190		return (ret);
2191
2192	qp_attr.qp_state = IB_QPS_RTS;
2193	qp_attr.sq_psn = 0;
2194	ret = ib_modify_qp(qp, &qp_attr, IB_QP_STATE | IB_QP_SQ_PSN);
2195
2196	return (ret);
2197}
2198
2199static int
2200cma_init_conn_qp(sol_cma_chan_t *chanp, struct ib_qp *qp)
2201{
2202	struct ib_qp_attr qp_attr;
2203	int qp_attr_mask, ret;
2204
2205	qp_attr.qp_state = IB_QPS_INIT;
2206	ret = rdma_init_qp_attr(&chanp->chan_rdma_cm, &qp_attr, &qp_attr_mask);
2207	if (ret)
2208		return (ret);
2209
2210	return (ib_modify_qp(qp, &qp_attr, qp_attr_mask));
2211}
2212
2213static inline int
2214cma_is_ud_ps(enum rdma_port_space ps)
2215{
2216	return (ps == RDMA_PS_UDP || ps == RDMA_PS_IPOIB);
2217}
2218
2219int
2220rdma_create_qp(struct rdma_cm_id *idp, struct ib_pd *pd,
2221    struct ib_qp_init_attr *qp_init_attr)
2222{
2223	sol_cma_chan_t	*chanp;
2224	struct ib_qp	*qp;
2225	int		ret;
2226	ofs_client_t	*dev_ofs_client;
2227
2228	ASSERT(idp);
2229	chanp = (sol_cma_chan_t *)idp;
2230	if (idp->device->node_guid != pd->device->node_guid)
2231		return (-EINVAL);
2232
2233	dev_ofs_client = (ofs_client_t *)pd->device->clnt_hdl;
2234	rdma_map_id2clnthdl(idp, dev_ofs_client->ibt_hdl, NULL);
2235
2236	qp = ib_create_qp(pd, qp_init_attr);
2237	if ((uintptr_t)qp >= (uintptr_t)-0xFFF) {
2238		return ((intptr_t)qp);
2239	}
2240	rdma_map_id2qphdl(idp, (void *)qp->ibt_qp);
2241
2242	if (cma_is_ud_ps(idp->ps)) {
2243		ret = cma_init_ud_qp(chanp, qp);
2244	} else {
2245		ret = cma_init_conn_qp(chanp, qp);
2246	}
2247
2248	if (ret) {
2249		goto err;
2250	}
2251
2252	idp->qp = qp;
2253	chanp->chan_qp_num = qp->qp_num;
2254	chanp->chan_is_srq = (qp->srq != NULL);
2255	return (0);
2256err:
2257	(void) ib_destroy_qp(qp);
2258	return (ret);
2259}
2260
2261void
2262rdma_destroy_qp(struct rdma_cm_id *idp)
2263{
2264	ASSERT(idp);
2265	(void) ib_destroy_qp(idp->qp);
2266	idp->qp = NULL;
2267}
2268