/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. */ /* * sol_cma is a part of sol_ofs misc module. This file * provides interfaces for supporting the communication * management API defined in "rdma_cm.h". In-Kernel * consumers of the "rdma_cm.h" API should link sol_ofs * misc module using : * -N misc/sol_ofs * Solaris uCMA (sol_ucma) driver is the current consumer for * sol_cma. */ /* Standard driver includes */ #include #include #include #include #include #include #include #include #include #include #include /* Modload support */ static struct modlmisc sol_ofs_modmisc = { &mod_miscops, "Solaris OFS Misc module" }; struct modlinkage sol_ofs_modlinkage = { MODREV_1, (void *)&sol_ofs_modmisc, NULL }; static ib_client_t *sol_cma_ib_client; sol_cma_glbl_listen_t sol_cma_glbl_listen; avl_tree_t sol_cma_glbl_listen_tree; static void sol_cma_add_dev(struct ib_device *); static void sol_cma_rem_dev(struct ib_device *); static llist_head_t sol_cma_dev_list = LLIST_HEAD_INIT(sol_cma_dev_list); kmutex_t sol_cma_dev_mutex; kmutex_t sol_cma_glob_mutex; char *sol_rdmacm_dbg_str = "sol_rdmacm"; char *sol_ofs_dbg_str = "sol_ofs_mod"; /* * Local functions defines. */ int sol_cma_req_cmid_cmp(const void *p1, const void *p2); int sol_cma_cmid_cmp(const void *p1, const void *p2); int sol_cma_svc_cmp(const void *, const void *); static struct rdma_cm_id *cma_alloc_chan(rdma_cm_event_handler, void *, enum rdma_port_space); static void cma_set_chan_state(sol_cma_chan_t *, cma_chan_state_t); static int cma_cas_chan_state(sol_cma_chan_t *, cma_chan_state_t, cma_chan_state_t); static void cma_free_listen_list(struct rdma_cm_id *); static void cma_destroy_id(struct rdma_cm_id *); static void cma_handle_nomore_events(sol_cma_chan_t *); extern void sol_ofs_dprintf_init(); extern void sol_ofs_dprintf_fini(); cma_chan_state_t cma_get_chan_state(sol_cma_chan_t *); extern int ibcma_init_root_chan(sol_cma_chan_t *, sol_cma_glbl_listen_t *); extern int ibcma_fini_root_chan(sol_cma_chan_t *); extern void ibcma_copy_srv_hdl(sol_cma_chan_t *, sol_cma_glbl_listen_t *); extern int ibcma_fini_ep_chan(sol_cma_chan_t *); extern uint64_t ibcma_init_root_sid(sol_cma_chan_t *); extern void rdma_ib_destroy_id(struct rdma_cm_id *); extern int rdma_ib_bind_addr(struct rdma_cm_id *, struct sockaddr *); extern int rdma_ib_resolve_addr(struct rdma_cm_id *, struct sockaddr *, struct sockaddr *, int); extern int rdma_ib_resolve_route(struct rdma_cm_id *, int); extern int rdma_ib_init_qp_attr(struct rdma_cm_id *, struct ib_qp_attr *, int *); extern int rdma_ib_connect(struct rdma_cm_id *, struct rdma_conn_param *); extern int rdma_ib_listen(struct rdma_cm_id *, int); extern int rdma_ib_accept(struct rdma_cm_id *, struct rdma_conn_param *); extern int rdma_ib_reject(struct rdma_cm_id *, const void *, uint8_t); extern int rdma_ib_disconnect(struct rdma_cm_id *); extern int rdma_ib_join_multicast(struct rdma_cm_id *, struct sockaddr *, void *); extern void rdma_ib_leave_multicast(struct rdma_cm_id *, struct sockaddr *); int _init(void) { int err; sol_ofs_dprintf_init(); SOL_OFS_DPRINTF_L5(sol_ofs_dbg_str, "_init()"); mutex_init(&sol_cma_glob_mutex, NULL, MUTEX_DRIVER, NULL); mutex_init(&sol_cma_dev_mutex, NULL, MUTEX_DRIVER, NULL); avl_create(&sol_cma_glbl_listen_tree, sol_cma_svc_cmp, sizeof (sol_cma_glbl_listen_t), offsetof(sol_cma_glbl_listen_t, cma_listen_node)); sol_cma_ib_client = kmem_zalloc(sizeof (ib_client_t), KM_NOSLEEP); if (!sol_cma_ib_client) { SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, "_init() - mem alloc failed"); avl_destroy(&sol_cma_glbl_listen_tree); mutex_destroy(&sol_cma_dev_mutex); mutex_destroy(&sol_cma_glob_mutex); sol_ofs_dprintf_fini(); return (ENOMEM); } sol_cma_ib_client->name = "sol_ofs"; sol_cma_ib_client->add = sol_cma_add_dev; sol_cma_ib_client->remove = sol_cma_rem_dev; sol_cma_ib_client->dip = NULL; if ((err = ib_register_client(sol_cma_ib_client)) != 0) { SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, "_init() ib_register_client() failed with err %d", err); kmem_free(sol_cma_ib_client, sizeof (ib_client_t)); avl_destroy(&sol_cma_glbl_listen_tree); mutex_destroy(&sol_cma_dev_mutex); mutex_destroy(&sol_cma_glob_mutex); sol_ofs_dprintf_fini(); return (err); } if ((err = mod_install(&sol_ofs_modlinkage)) != 0) { SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, "_init() - mod_install() failed"); ib_unregister_client(sol_cma_ib_client); kmem_free(sol_cma_ib_client, sizeof (ib_client_t)); avl_destroy(&sol_cma_glbl_listen_tree); mutex_destroy(&sol_cma_dev_mutex); mutex_destroy(&sol_cma_glob_mutex); sol_ofs_dprintf_fini(); return (err); } SOL_OFS_DPRINTF_L5(sol_ofs_dbg_str, "_init() - ret"); return (err); } int _fini(void) { int err; SOL_OFS_DPRINTF_L5(sol_ofs_dbg_str, "_fini()"); if (avl_numnodes(&sol_cma_glbl_listen_tree)) { SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, "_fini - " "listen CMIDs still active"); return (EBUSY); } if ((err = mod_remove(&sol_ofs_modlinkage)) != 0) { SOL_OFS_DPRINTF_L3(sol_ofs_dbg_str, "_fini: mod_remove failed"); return (err); } ib_unregister_client(sol_cma_ib_client); kmem_free(sol_cma_ib_client, sizeof (ib_client_t)); avl_destroy(&sol_cma_glbl_listen_tree); mutex_destroy(&sol_cma_dev_mutex); mutex_destroy(&sol_cma_glob_mutex); SOL_OFS_DPRINTF_L5(sol_ofs_dbg_str, "_fini() - ret"); sol_ofs_dprintf_fini(); return (err); } int _info(struct modinfo *modinfop) { return (mod_info(&sol_ofs_modlinkage, modinfop)); } typedef struct cma_device { kmutex_t cma_mutex; /* Ptr in the global sol_cma_dev_list */ llist_head_t cma_list; /* List of listeners for this device */ genlist_t cma_epchan_list; struct ib_device *cma_device; uint_t cma_ref_count; enum { SOL_CMA_DEV_ADDED, SOL_CMA_DEV_REM_IN_PROGRESS } cma_dev_state; } cma_device_t; static void sol_cma_add_dev(struct ib_device *dev) { cma_device_t *new_device; new_device = kmem_zalloc(sizeof (cma_device_t), KM_NOSLEEP); if (!new_device) { SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, "sol_cma_add_dev() " "alloc failed!!"); return; } mutex_init(&new_device->cma_mutex, NULL, MUTEX_DRIVER, NULL); llist_head_init(&new_device->cma_list, new_device); init_genlist(&new_device->cma_epchan_list); new_device->cma_device = dev; ib_set_client_data(dev, sol_cma_ib_client, new_device); mutex_enter(&sol_cma_dev_mutex); llist_add_tail(&new_device->cma_list, &sol_cma_dev_list); mutex_exit(&sol_cma_dev_mutex); } static void sol_cma_rem_dev(struct ib_device *dev) { cma_device_t *rem_device; genlist_entry_t *entry; SOL_OFS_DPRINTF_L5(sol_ofs_dbg_str, "sol_rem_dev(%p)", dev); rem_device = (cma_device_t *)ib_get_client_data(dev, sol_cma_ib_client); if (!rem_device) { SOL_OFS_DPRINTF_L2(sol_ofs_dbg_str, "sol_cma_rem_dev() " "NULL cma_dev!!"); return; } mutex_enter(&rem_device->cma_mutex); rem_device->cma_dev_state = SOL_CMA_DEV_REM_IN_PROGRESS; if (rem_device->cma_ref_count) { mutex_exit(&rem_device->cma_mutex); SOL_OFS_DPRINTF_L3(sol_ofs_dbg_str, "sol_cma_rem_dev() " "BUSY cma_dev!!"); return; } entry = remove_genlist_head(&rem_device->cma_epchan_list); while (entry) { sol_cma_chan_t *ep_chanp; ep_chanp = (sol_cma_chan_t *)entry->data; if (ibcma_fini_ep_chan(ep_chanp) == 0) { genlist_entry_t *entry1; sol_cma_chan_t *root_chanp; ASSERT(ep_chanp->chan_listenp); entry1 = ep_chanp->chan_listenp->listen_ep_root_entry; root_chanp = (sol_cma_chan_t *)ep_chanp->listen_root; root_chanp->chan_listenp->listen_eps--; delete_genlist(&root_chanp->chan_listenp->listen_list, entry1); kmem_free(ep_chanp, sizeof (sol_cma_chan_t)); kmem_free(entry, sizeof (genlist_entry_t)); } entry = remove_genlist_head(&rem_device->cma_epchan_list); } mutex_exit(&rem_device->cma_mutex); mutex_enter(&sol_cma_dev_mutex); llist_del(&rem_device->cma_list); mutex_exit(&sol_cma_dev_mutex); kmem_free(rem_device, sizeof (cma_device_t)); } struct ib_device * sol_cma_acquire_device(ib_guid_t hca_guid) { llist_head_t *entry; cma_device_t *cma_devp; mutex_enter(&sol_cma_dev_mutex); list_for_each(entry, &sol_cma_dev_list) { cma_devp = (cma_device_t *)entry->ptr; if (cma_devp->cma_device->node_guid != hca_guid) continue; mutex_enter(&cma_devp->cma_mutex); if (cma_devp->cma_dev_state == SOL_CMA_DEV_REM_IN_PROGRESS) { SOL_OFS_DPRINTF_L3(sol_ofs_dbg_str, "sol_cma_acquire_dev() - Device getting removed!!"); mutex_exit(&cma_devp->cma_mutex); mutex_exit(&sol_cma_dev_mutex); return (NULL); } cma_devp->cma_ref_count++; mutex_exit(&cma_devp->cma_mutex); mutex_exit(&sol_cma_dev_mutex); return (cma_devp->cma_device); } mutex_exit(&sol_cma_dev_mutex); return (NULL); } static void sol_cma_release_device(struct rdma_cm_id *id) { ib_device_t *device = id->device; llist_head_t *entry; cma_device_t *cma_devp; mutex_enter(&sol_cma_dev_mutex); list_for_each(entry, &sol_cma_dev_list) { cma_devp = (cma_device_t *)entry->ptr; if (cma_devp->cma_device != device) continue; mutex_enter(&cma_devp->cma_mutex); cma_devp->cma_ref_count--; if (cma_devp->cma_dev_state == SOL_CMA_DEV_REM_IN_PROGRESS && cma_devp->cma_ref_count == 0) { SOL_OFS_DPRINTF_L3(sol_ofs_dbg_str, "sol_cma_release_dev() - Device free removed!!"); mutex_exit(&cma_devp->cma_mutex); llist_del(&cma_devp->cma_list); kmem_free(cma_devp, sizeof (cma_device_t)); mutex_exit(&sol_cma_dev_mutex); return; } mutex_exit(&cma_devp->cma_mutex); } mutex_exit(&sol_cma_dev_mutex); } void sol_cma_add_hca_list(sol_cma_chan_t *ep_chanp, ib_guid_t hca_guid) { llist_head_t *entry; cma_device_t *cma_devp; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "add_hca_list(%p, %llx)", ep_chanp, hca_guid); mutex_enter(&sol_cma_dev_mutex); list_for_each(entry, &sol_cma_dev_list) { cma_devp = (cma_device_t *)entry->ptr; if ((cma_devp->cma_device)->node_guid != hca_guid) continue; mutex_enter(&cma_devp->cma_mutex); ep_chanp->chan_listenp->listen_ep_dev_entry = add_genlist(&cma_devp->cma_epchan_list, (uintptr_t)ep_chanp, NULL); ep_chanp->chan_listenp->listen_ep_device = cma_devp->cma_device; mutex_exit(&cma_devp->cma_mutex); mutex_exit(&sol_cma_dev_mutex); return; } mutex_exit(&sol_cma_dev_mutex); SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "add_hca_list(%p, %llx): " "No matching HCA in list!!", ep_chanp, hca_guid); } /* * rdma_cm.h API functions. */ struct rdma_cm_id * rdma_create_id(rdma_cm_event_handler evt_hdlr, void *context, enum rdma_port_space ps) { struct rdma_cm_id *rdma_idp; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_create_id(%p, %p, %x)", evt_hdlr, context, ps); if (ps != RDMA_PS_TCP && ps != RDMA_PS_UDP && ps != RDMA_PS_IPOIB) { SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "rdma_create_id: unsupported protocol %x", ps); return (NULL); } rdma_idp = cma_alloc_chan(evt_hdlr, context, ps); SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_create_id : ret %p", rdma_idp); return (rdma_idp); } void rdma_map_id2clnthdl(struct rdma_cm_id *rdma_idp, void *ib_client_hdl, void *iw_client_hdl) { sol_cma_chan_t *chanp = (sol_cma_chan_t *)rdma_idp; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_map_id2clnthdl(%p, %p, %p)", rdma_idp, ib_client_hdl, iw_client_hdl); ASSERT(ib_client_hdl != NULL || iw_client_hdl != NULL); chanp->chan_ib_client_hdl = ib_client_hdl; chanp->chan_iw_client_hdl = iw_client_hdl; } void rdma_map_id2qphdl(struct rdma_cm_id *rdma_idp, void *qp_hdl) { sol_cma_chan_t *chanp = (sol_cma_chan_t *)rdma_idp; ASSERT(rdma_idp); ASSERT(qp_hdl); SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_mapid2qphdl(%p, %p)", rdma_idp, qp_hdl); chanp->chan_qp_hdl = qp_hdl; } void rdma_destroy_id(struct rdma_cm_id *rdma_idp) { sol_cma_chan_t *chanp, *root_chanp; cma_chan_state_t state; int rc, is_root_cmid, do_wait, is_passive; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_destroy_id(%p)", rdma_idp); if (!rdma_idp) return; is_root_cmid = do_wait = is_passive = 0; chanp = (sol_cma_chan_t *)rdma_idp; root_chanp = (sol_cma_chan_t *)chanp->listen_root; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_destroy_id(%p), %p", rdma_idp, root_chanp); mutex_enter(&chanp->chan_mutex); chanp->chan_cmid_destroy_state |= SOL_CMA_CALLER_CMID_DESTROYED; /* * Wait in destroy of CMID when rdma_resolve_addr() / rdma_listen() * rdma_resolve_route() API is in progress. */ while (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_API_PROGRESS) cv_wait(&chanp->chan_destroy_cv, &chanp->chan_mutex); /* Wait if Event is been notified to consumer */ while (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_EVENT_PROGRESS) cv_wait(&chanp->chan_destroy_cv, &chanp->chan_mutex); if (rdma_idp->device) sol_cma_release_device(rdma_idp); if (chanp->chan_listenp && chanp->chan_listenp->listen_is_root) is_root_cmid = 1; if (root_chanp == NULL && is_root_cmid == 0) is_passive = 1; /* * Skip Active side handling for passive CMIDs and listen CMID * for which REQ CMIDs have not been created. */ if (is_passive || (is_root_cmid && chanp->chan_req_state != REQ_CMID_QUEUED)) { SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_destroy_id: " "Skipping passive %p, %x, %x", chanp->chan_listenp, is_root_cmid, chanp->chan_req_state); goto skip_passive_handling; } /* * destroy_id() called for listening CMID and there are REQ * CMIDs not yet notified. Reject such CMIDs and decrement * the count. */ if (is_root_cmid && chanp->chan_req_cnt) { sol_cma_chan_t *req_cmid_chan, *next_chan; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_destroy_id: " "not notified handling"); for (req_cmid_chan = (sol_cma_chan_t *)avl_first( &chanp->chan_req_avl_tree); req_cmid_chan && chanp->chan_req_cnt; req_cmid_chan = next_chan) { next_chan = AVL_NEXT( &chanp->chan_req_avl_tree, req_cmid_chan); if (req_cmid_chan->chan_req_state == REQ_CMID_NOTIFIED) { avl_remove(&chanp->chan_req_avl_tree, req_cmid_chan); chanp->chan_req_cnt--; chanp->chan_req_total_cnt--; mutex_exit(&chanp->chan_mutex); mutex_enter(&req_cmid_chan->chan_mutex); req_cmid_chan->chan_req_state = REQ_CMID_SERVER_NONE; if (rdma_idp->ps == RDMA_PS_TCP) cma_set_chan_state(req_cmid_chan, SOL_CMA_CHAN_DESTROY_PENDING); mutex_exit(&req_cmid_chan->chan_mutex); (void) rdma_disconnect( (struct rdma_cm_id *)req_cmid_chan); mutex_enter(&chanp->chan_mutex); if (rdma_idp->ps == RDMA_PS_TCP) { mutex_enter( &req_cmid_chan->chan_mutex); req_cmid_chan->listen_root = rdma_idp; mutex_exit( &req_cmid_chan->chan_mutex); } else { mutex_destroy( &req_cmid_chan->chan_mutex); cv_destroy( &req_cmid_chan->chan_destroy_cv); kmem_free(req_cmid_chan, sizeof (sol_cma_chan_t)); } } } } /* * destroy_id() called for : * listening CMID and all REQ CMIDs destroy_id() called * REQ CMID and 1 more REQ CMID not yet destroyed. * wait till the CMID is completly destroyed. */ if (is_root_cmid && chanp->chan_req_total_cnt == 0) { SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_destroy_id: " "root idp waiting"); cma_set_chan_state(chanp, SOL_CMA_CHAN_DESTROY_WAIT); cv_wait(&chanp->chan_destroy_cv, &chanp->chan_mutex); } mutex_exit(&chanp->chan_mutex); if (root_chanp) mutex_enter(&root_chanp->chan_mutex); mutex_enter(&chanp->chan_mutex); #ifdef DEBUG SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_destroy_id: " "root_idp %p, cnt %x, state %x", root_chanp, root_chanp ? root_chanp->chan_req_total_cnt : 0, root_chanp ? cma_get_chan_state(root_chanp) : 0); #endif if (root_chanp && root_chanp->chan_req_total_cnt == 1 && cma_get_chan_state(root_chanp) == SOL_CMA_CHAN_DESTROY_PENDING) do_wait = 1; if (root_chanp) mutex_exit(&root_chanp->chan_mutex); skip_passive_handling : state = cma_get_chan_state(chanp); if (is_root_cmid == 0 && state != SOL_CMA_CHAN_DISCONNECT && SOL_CMAID_CONNECTED(chanp)) { /* * A connected CM ID has not been disconnected. * Call rdma_disconnect() to disconnect it. */ mutex_exit(&chanp->chan_mutex); rc = rdma_disconnect(rdma_idp); if (rc) { SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "rdma_destroy_id(%p)- disconnect failed!!", rdma_idp); return; } mutex_enter(&chanp->chan_mutex); if (root_chanp && chanp->listen_root == NULL) chanp->listen_root = (struct rdma_cm_id *)root_chanp; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_destroy_id(chanp %p, connect %x, ps %x)", chanp, chanp->chan_connect_flag, rdma_idp->ps); if (SOL_CMAID_CONNECTED(chanp)) { if (do_wait) { cma_set_chan_state(chanp, SOL_CMA_CHAN_DESTROY_WAIT); cv_wait(&chanp->chan_destroy_cv, &chanp->chan_mutex); mutex_exit(&chanp->chan_mutex); cma_destroy_id(rdma_idp); } else { cma_set_chan_state(chanp, SOL_CMA_CHAN_DESTROY_PENDING); mutex_exit(&chanp->chan_mutex); } } else { /* * No more callbacks are expected for this CMID. * Free this CMID. */ mutex_exit(&chanp->chan_mutex); cma_destroy_id(rdma_idp); } } else if (is_root_cmid == 0 && state == SOL_CMA_CHAN_DISCONNECT && SOL_CMAID_CONNECTED(chanp)) { /* * CM ID was connected and disconnect is process. * Free of this CM ID is done for the DISCONNECT * notification for this CMID. */ cma_set_chan_state(chanp, SOL_CMA_CHAN_DESTROY_PENDING); mutex_exit(&chanp->chan_mutex); } else if (state != SOL_CMA_CHAN_DESTROY_PENDING) { /* CM ID, not connected, just free it. */ mutex_exit(&chanp->chan_mutex); cma_destroy_id(rdma_idp); } else mutex_exit(&chanp->chan_mutex); SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_destroy_id: ret"); } /* * State transitions for Address resolution : * Active Side (Client) : * 1. CREATE_ID-->BIND_ADDR-->RESOLVE_ADDR-->RESOLVE_ROUTE * * Passive Side (Server) : * 2. CREATE_ID-->RESOLVE_ADDR-->RESOLVE_ROUTE * IF_ADDR_ANY can be passed as local address in RESOLVE_ADDR */ int rdma_bind_addr(struct rdma_cm_id *idp, struct sockaddr *addr) { sol_cma_chan_t *chanp; struct rdma_addr *addrp; int ret; ASSERT(idp); ASSERT(addr); chanp = (sol_cma_chan_t *)idp; addrp = &(idp->route.addr); SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_bind_addr(%p, %p)", idp, addr); mutex_enter(&chanp->chan_mutex); ret = cma_cas_chan_state(chanp, SOL_CMA_CHAN_IDLE, SOL_CMA_CHAN_BOUND); if (ret) { mutex_exit(&chanp->chan_mutex); return (ret); } /* Copy the local address to rdma_id structure */ bcopy((void *)addr, (void *)&(addrp->src_addr), sizeof (struct sockaddr)); mutex_exit(&chanp->chan_mutex); /* * First call rdma_ib_bind_addr() to bind this address. * Next call rdma_iw_bind_addr() to bind this address. * For IF_ADDR_ANY, IB address is given priority over * iWARP. */ if (chanp->chan_ib_client_hdl == NULL) { ofs_client_t *ofs_clnt; ofs_clnt = (ofs_client_t *)sol_cma_ib_client->clnt_hdl; chanp->chan_ib_client_hdl = ofs_clnt->ibt_hdl; } if (chanp->chan_ib_client_hdl && rdma_ib_bind_addr(idp, addr) == 0) { SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_bind_addr: ret IB @"); return (0); #ifdef IWARP_SUPPORT } else if (chanp->chan_iw_client_hdl && rdma_iw_bind_addr(idp, addr) == 0) { SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_bind_addr: ret iWARP @"); return (0); #endif /* IWARP_SUPPORT */ } mutex_enter(&chanp->chan_mutex); cma_set_chan_state(chanp, SOL_CMA_CHAN_IDLE); mutex_exit(&chanp->chan_mutex); SOL_OFS_DPRINTF_L4(sol_rdmacm_dbg_str, "rdma_bind_addr: ret failure!"); return (EINVAL); } int rdma_resolve_addr(struct rdma_cm_id *idp, struct sockaddr *src_addr, struct sockaddr *dst_addr, int timeout_ms) { sol_cma_chan_t *chanp; struct rdma_addr *addrp; cma_chan_state_t state; ASSERT(idp); chanp = (sol_cma_chan_t *)idp; addrp = &(idp->route.addr); SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_resolve_addr(%p, %p, " "%p, %x)", idp, src_addr, dst_addr, timeout_ms); mutex_enter(&chanp->chan_mutex); state = cma_get_chan_state(chanp); if (state != SOL_CMA_CHAN_IDLE && state != SOL_CMA_CHAN_BOUND) { SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "rdma_resolve_addr : invalid chan state %x", state); mutex_exit(&chanp->chan_mutex); return (EINVAL); } if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) { SOL_OFS_DPRINTF_L3(sol_rdmacm_dbg_str, "rdma_resolve_addr : CMID %p, destroy called", chanp); mutex_exit(&chanp->chan_mutex); return (EINVAL); } chanp->chan_cmid_destroy_state |= SOL_CMA_CALLER_API_PROGRESS; if (chanp->chan_xport_type == SOL_CMA_XPORT_NONE) { bcopy((void *)src_addr, (void *)&(addrp->src_addr), sizeof (struct sockaddr)); } bcopy((void *)dst_addr, (void *)&(addrp->dst_addr), sizeof (struct sockaddr)); mutex_exit(&chanp->chan_mutex); /* * First resolve this as an @ corresponding to IB fabric * if this fails, resolve this as an @ corresponding to iWARP */ if (chanp->chan_ib_client_hdl == NULL) { ofs_client_t *ofs_clnt; ofs_clnt = (ofs_client_t *)sol_cma_ib_client->clnt_hdl; chanp->chan_ib_client_hdl = ofs_clnt->ibt_hdl; } if (chanp->chan_ib_client_hdl && rdma_ib_resolve_addr(idp, src_addr, dst_addr, timeout_ms) == 0) { SOL_OFS_DPRINTF_L4(sol_rdmacm_dbg_str, "rdma_resolve_addr: ret IB @"); #ifdef IWARP_SUPPORT } else if (chanp->chan_iw_client_hdl && rdma_iw_resolve_addr(idp, src_addr, dst_addr, timeout_ms) == 0) { SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "rdma_resolve_addr: ret iWARP @"); #endif /* IWARP_SUPPORT */ } else { SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "rdma_resolve_addr: Invalid @"); return (EINVAL); } SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_resolve_addr: ret 0"); return (0); } static void cma_generate_event_sync(struct rdma_cm_id *, enum rdma_cm_event_type, int, struct rdma_conn_param *, struct rdma_ud_param *); void cma_resolve_addr_callback(sol_cma_chan_t *chanp, int rc) { enum rdma_cm_event_type event; mutex_enter(&chanp->chan_mutex); if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) { SOL_OFS_DPRINTF_L3(sol_rdmacm_dbg_str, "cma_resolve_addr : CMID %p, destroy called", chanp); chanp->chan_cmid_destroy_state &= ~SOL_CMA_CALLER_API_PROGRESS; cv_broadcast(&chanp->chan_destroy_cv); mutex_exit(&chanp->chan_mutex); return; } if (rc == 0) { cma_set_chan_state(chanp, SOL_CMA_CHAN_ADDR_RESLVD); event = RDMA_CM_EVENT_ADDR_RESOLVED; } else event = RDMA_CM_EVENT_ADDR_ERROR; /* * Generate RDMA_CM_EVENT_ADDR_RESOLVED event * This will result in RDMA_USER_CM_CMD_RESOLVE_ROUTE in * userland. */ chanp->chan_cmid_destroy_state |= SOL_CMA_CALLER_EVENT_PROGRESS; mutex_exit(&chanp->chan_mutex); cma_generate_event_sync((struct rdma_cm_id *)chanp, event, 0, NULL, NULL); mutex_enter(&chanp->chan_mutex); chanp->chan_cmid_destroy_state &= ~SOL_CMA_CALLER_API_PROGRESS; if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) cv_broadcast(&chanp->chan_destroy_cv); mutex_exit(&chanp->chan_mutex); } int rdma_resolve_route(struct rdma_cm_id *idp, int timeout_ms) { sol_cma_chan_t *chanp; ASSERT(idp); chanp = (sol_cma_chan_t *)idp; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "resolve_route(%p, %x)", idp, timeout_ms); mutex_enter(&chanp->chan_mutex); if (cma_cas_chan_state(chanp, SOL_CMA_CHAN_ADDR_RESLVD, SOL_CMA_CHAN_ROUTE_RESLVD) != 0) { mutex_exit(&chanp->chan_mutex); SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "resolve_route: Invalid state"); return (EINVAL); } if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) { SOL_OFS_DPRINTF_L3(sol_rdmacm_dbg_str, "rdma_resolve_route : CMID %p, destroy called", chanp); mutex_exit(&chanp->chan_mutex); return (EINVAL); } chanp->chan_cmid_destroy_state |= SOL_CMA_CALLER_API_PROGRESS; mutex_exit(&chanp->chan_mutex); /* * Generate RDMA_CM_EVENT_ROUTE_RESOLVED event * This will result in RDMA_USER_CM_CMD_RESOLVE_ROUTE in * userland */ cma_generate_event(idp, RDMA_CM_EVENT_ROUTE_RESOLVED, 0, NULL, NULL); mutex_enter(&chanp->chan_mutex); chanp->chan_cmid_destroy_state &= ~SOL_CMA_CALLER_API_PROGRESS; if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) cv_broadcast(&chanp->chan_destroy_cv); mutex_exit(&chanp->chan_mutex); SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "resolve_route: ret 0"); return (0); } /* * Connect or Listen request should be send after Route is resolved * * Active Side (Client) : * 1. (State ROUTE_RESOLVED)-->CONNECT-->ACCEPT/REJECT-->DISCONNECT * -->DESTROY_ID-->close(9E) * 2. Same as (1), DESTROY_ID without DISCONNECT * 3. Same as (1), close(9e) without DESTROY_ID. * * Passive Side (Server) : * 4. (State ROUTE_RESOLVED)-->LISTEN->DISCONNECT * -->DESTROY_ID-->close(9E) * 5. Same as (4), DESTROY_ID without DISCONNECT * 6. Same as (4), close(9e) without DESTROY_ID. */ int rdma_connect(struct rdma_cm_id *idp, struct rdma_conn_param *conn_param) { sol_cma_chan_t *chanp; int ret = EINVAL; ASSERT(idp); chanp = (sol_cma_chan_t *)idp; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_connect(%p, %p)", idp, conn_param); mutex_enter(&chanp->chan_mutex); if (chanp->chan_xport_type == SOL_CMA_XPORT_NONE) { mutex_exit(&chanp->chan_mutex); SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "rdma_connect, Invalid Xport"); return (EINVAL); } if (cma_cas_chan_state(chanp, SOL_CMA_CHAN_ROUTE_RESLVD, SOL_CMA_CHAN_CONNECT)) { mutex_exit(&chanp->chan_mutex); SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "rdma_connect, Invalid state"); return (EINVAL); } if (chanp->chan_xport_type == SOL_CMA_XPORT_IB) { ret = rdma_ib_connect(idp, conn_param); #ifdef IWARP_SUPPORT } else if (chanp->chan_xport_type == SOL_CMA_XPORT_IWARP) { ret = rdma_iw_connect(idp, conn_param); #endif /* IWARP_SUPPORT */ } mutex_exit(&chanp->chan_mutex); SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_connect: ret %x", ret); return (ret); } static int cma_init_listen_root(sol_cma_chan_t *); static void cma_fini_listen_root(sol_cma_chan_t *); int rdma_listen(struct rdma_cm_id *idp, int bklog) { sol_cma_chan_t *chanp; int ret = 0; genlist_entry_t *entry; cma_chan_state_t state; ASSERT(idp); chanp = (sol_cma_chan_t *)idp; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_listen(%p, %x)", idp, bklog); mutex_enter(&chanp->chan_mutex); state = cma_get_chan_state(chanp); if (state == SOL_CMA_CHAN_IDLE) { mutex_exit(&chanp->chan_mutex); return (EINVAL); } cma_set_chan_state(chanp, SOL_CMA_CHAN_LISTEN); if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) { SOL_OFS_DPRINTF_L3(sol_rdmacm_dbg_str, "rdma_listen : CMID %p, destroy called", chanp); mutex_exit(&chanp->chan_mutex); return (EINVAL); } chanp->chan_cmid_destroy_state |= SOL_CMA_CALLER_API_PROGRESS; ASSERT(chanp->chan_listenp == NULL); chanp->chan_listenp = kmem_zalloc(sizeof (sol_cma_listen_info_t), KM_SLEEP); init_genlist(&(CHAN_LISTEN_LIST(chanp))); (chanp->chan_listenp)->listen_is_root = 1; ret = cma_init_listen_root(chanp); if (ret) { chanp->chan_listenp = NULL; mutex_exit(&chanp->chan_mutex); SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "rdma_listen: " "cma_init_listen_root: failed"); kmem_free(chanp->chan_listenp, sizeof (sol_cma_listen_info_t)); return (EINVAL); } if (chanp->chan_xport_type == SOL_CMA_XPORT_NONE) { ibcma_append_listen_list(idp); #ifdef IWARP_SUPPORT iwcma_append_listen_list(idp); #endif } else if (chanp->chan_xport_type == SOL_CMA_XPORT_IB) { ibcma_append_listen_list(idp); #ifdef IWARP_SUPPORT } else if (chanp->chan_xport_type == SOL_CMA_XPORT_IWARP) { iwcma_append_listen_list(idp); #endif /* IWARP_SUPPORT */ } if (genlist_empty(&(CHAN_LISTEN_LIST(chanp)))) { cma_fini_listen_root(chanp); kmem_free((void *)chanp->chan_listenp, sizeof (sol_cma_listen_info_t)); chanp->chan_listenp = NULL; SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "rdma_listen: " "No listeners"); mutex_exit(&chanp->chan_mutex); return (0); } if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) { chanp->chan_cmid_destroy_state &= ~SOL_CMA_CALLER_API_PROGRESS; cv_broadcast(&chanp->chan_destroy_cv); } genlist_for_each(entry, &(CHAN_LISTEN_LIST(chanp))) { struct rdma_cm_id *ep_idp; sol_cma_chan_t *ep_chanp; ep_idp = (struct rdma_cm_id *)entry->data; ep_chanp = (sol_cma_chan_t *)ep_idp; if (ep_chanp->chan_xport_type == SOL_CMA_XPORT_IB) ret = rdma_ib_listen(ep_idp, bklog); #ifdef IWARP_SUPPORT if (ep_chanp->chan_xport_type == SOL_CMA_XPORT_IWARP) ret = rdma_iw_listen(ep_idp, bklog); #endif if (ret) break; } chanp->chan_cmid_destroy_state &= ~SOL_CMA_CALLER_API_PROGRESS; if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) cv_broadcast(&chanp->chan_destroy_cv); mutex_exit(&chanp->chan_mutex); SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_listen: ret %x", ret); return (ret); } int rdma_accept(struct rdma_cm_id *idp, struct rdma_conn_param *conn_param) { struct rdma_cm_id *root_idp; sol_cma_chan_t *root_chanp, *chanp; int ret = EINVAL; ASSERT(idp); chanp = (sol_cma_chan_t *)idp; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_accept(%p, %p)", idp, conn_param); mutex_enter(&chanp->chan_mutex); if (cma_cas_chan_state(chanp, SOL_CMA_CHAN_LISTEN, SOL_CMA_CHAN_ACCEPT) && cma_cas_chan_state(chanp, SOL_CMA_CHAN_CONNECT, SOL_CMA_CHAN_ACCEPT)) { SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "rdma_accept, Invalid state"); mutex_exit(&chanp->chan_mutex); return (EINVAL); } mutex_exit(&chanp->chan_mutex); root_idp = CHAN_LISTEN_ROOT(chanp); root_chanp = (sol_cma_chan_t *)root_idp; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "accept: root_idp %p", root_idp); /* For TCP, delete from REQ AVL & insert to ACPT AVL */ if (root_idp && root_idp->ps == RDMA_PS_TCP) { void *find_ret; avl_index_t where; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "accept: root_idp %p" "REQ AVL remove %p", root_chanp, idp); mutex_enter(&root_chanp->chan_mutex); mutex_enter(&chanp->chan_mutex); /* * This CMID has been deleted, maybe because of timeout. * Return EINVAL. */ if (chanp->chan_req_state != REQ_CMID_NOTIFIED) { mutex_exit(&chanp->chan_mutex); mutex_exit(&root_chanp->chan_mutex); SOL_OFS_DPRINTF_L3(sol_rdmacm_dbg_str, "accept: root_idp %p chanp %p, not in REQ " "AVL tree", root_chanp, chanp); return (EINVAL); } ASSERT(cma_get_req_idp(root_idp, chanp->chan_session_id)); avl_remove(&root_chanp->chan_req_avl_tree, idp); SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "Add to ACPT AVL of %p IDP, idp %p, qp_hdl %p", root_idp, idp, chanp->chan_qp_hdl); find_ret = avl_find(&root_chanp->chan_acpt_avl_tree, (void *)chanp->chan_qp_hdl, &where); if (find_ret) { chanp->chan_req_state = REQ_CMID_SERVER_NONE; mutex_exit(&chanp->chan_mutex); mutex_exit(&root_chanp->chan_mutex); SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "DUPLICATE ENTRY in ACPT AVL : root %p, " "idp %p, qp_hdl %p", root_idp, idp, chanp->chan_qp_hdl); return (EINVAL); } avl_insert(&root_chanp->chan_acpt_avl_tree, (void *)idp, where); chanp->chan_req_state = REQ_CMID_ACCEPTED; mutex_exit(&chanp->chan_mutex); mutex_exit(&root_chanp->chan_mutex); } if (root_idp && IS_UDP_CMID(root_idp)) { cma_chan_state_t chan_state; /* * Accepting the connect request, no more events for this * connection. */ cma_handle_nomore_events(chanp); mutex_enter(&chanp->chan_mutex); chan_state = cma_get_chan_state(chanp); mutex_exit(&chanp->chan_mutex); /* If rdma_destroy_id() was called, destroy CMID */ if (chan_state == SOL_CMA_CHAN_DESTROY_PENDING) { cma_destroy_id((struct rdma_cm_id *)chanp); return (EINVAL); } } if (chanp->chan_xport_type == SOL_CMA_XPORT_IB) ret = rdma_ib_accept(idp, conn_param); #ifdef IWARP_SUPPORT if (chanp->chan_xport_type == SOL_CMA_XPORT_IWARP) ret = rdma_iw_accept(idp, conn_param); #endif /* IWARP_SUPPORT */ if (ret && root_idp && idp->ps == RDMA_PS_TCP) { void *find_ret; avl_index_t where; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "Delete from REQ AVL of %p IDP, idp %p", root_idp, idp); mutex_enter(&root_chanp->chan_mutex); mutex_enter(&chanp->chan_mutex); if (chanp->chan_req_state == REQ_CMID_ACCEPTED) { ASSERT(cma_get_acpt_idp(root_idp, chanp->chan_qp_hdl)); avl_remove(&root_chanp->chan_acpt_avl_tree, idp); find_ret = avl_find(&root_chanp->chan_req_avl_tree, (void *)chanp->chan_qp_hdl, &where); if (find_ret) { chanp->chan_req_state = REQ_CMID_SERVER_NONE; mutex_exit(&chanp->chan_mutex); mutex_exit(&root_chanp->chan_mutex); SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "DUPLICATE ENTRY in REQ AVL : root %p, " "idp %p, session_id %p", root_idp, idp, chanp->chan_session_id); return (EINVAL); } avl_insert(&root_chanp->chan_req_avl_tree, idp, where); chanp->chan_req_state = REQ_CMID_NOTIFIED; } mutex_exit(&chanp->chan_mutex); mutex_exit(&root_chanp->chan_mutex); } SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_accept: ret %x", ret); return (ret); } int rdma_notify(struct rdma_cm_id *idp, enum ib_event_type evt) { sol_cma_chan_t *chanp; ASSERT(idp); chanp = (sol_cma_chan_t *)idp; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_notify(%p, %x)", idp, evt); mutex_enter(&chanp->chan_mutex); if (cma_cas_chan_state(chanp, SOL_CMA_CHAN_ROUTE_RESLVD, SOL_CMA_CHAN_EVENT_NOTIFIED)) { SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "rdma_notify, Invalid state"); mutex_exit(&chanp->chan_mutex); return (EINVAL); } mutex_exit(&chanp->chan_mutex); SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_notify: ret 0"); return (0); } int rdma_reject(struct rdma_cm_id *idp, const void *priv_data, uint8_t priv_data_len) { struct rdma_cm_id *root_idp; sol_cma_chan_t *root_chanp, *chanp; int ret = EINVAL; ASSERT(idp); chanp = (sol_cma_chan_t *)idp; root_idp = CHAN_LISTEN_ROOT(chanp); root_chanp = (sol_cma_chan_t *)root_idp; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_reject(%p, %p)", idp, priv_data, priv_data_len); mutex_enter(&chanp->chan_mutex); if (cma_cas_chan_state(chanp, SOL_CMA_CHAN_LISTEN, SOL_CMA_CHAN_REJECT)) { SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "rdma_accept, Invalid state"); mutex_exit(&chanp->chan_mutex); return (EINVAL); } mutex_exit(&chanp->chan_mutex); if (root_idp) { SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "reject: root_idp %p" "REQ AVL remove %p", root_chanp, idp); /* * Remove from REQ AVL tree. If this CMID has been deleted, * it maybe because of timeout. Return EINVAL. */ mutex_enter(&root_chanp->chan_mutex); mutex_enter(&chanp->chan_mutex); if (chanp->chan_req_state != REQ_CMID_NOTIFIED && chanp->chan_req_state != REQ_CMID_QUEUED) { mutex_exit(&chanp->chan_mutex); mutex_exit(&root_chanp->chan_mutex); SOL_OFS_DPRINTF_L3(sol_rdmacm_dbg_str, "reject: root_idp %p chanp %p, not in REQ " "AVL tree", root_chanp, chanp); return (EINVAL); } ASSERT(cma_get_req_idp(root_idp, chanp->chan_session_id)); avl_remove(&root_chanp->chan_req_avl_tree, idp); chanp->chan_req_state = REQ_CMID_SERVER_NONE; mutex_exit(&chanp->chan_mutex); mutex_exit(&root_chanp->chan_mutex); } if (chanp->chan_xport_type == SOL_CMA_XPORT_IB) ret = rdma_ib_reject(idp, priv_data, priv_data_len); #ifdef IWARP_SUPPORT if (chanp->chan_xport_type == SOL_CMA_XPORT_IWARP) ret = rdma_iw_reject(idp, priv_data, priv_data_len); #endif /* IWARP_SUPPORT */ if (!ret && root_idp) { cma_chan_state_t chan_state; /* * Rejecting connect request, no more events for this * connection. */ cma_handle_nomore_events(chanp); mutex_enter(&chanp->chan_mutex); chan_state = cma_get_chan_state(chanp); mutex_exit(&chanp->chan_mutex); /* If rdma_destroy_id() was called, destroy CMID */ if (chan_state == SOL_CMA_CHAN_DESTROY_PENDING) cma_destroy_id((struct rdma_cm_id *)chanp); } else if (ret && root_idp) { avl_index_t where; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "reject fail: Add to Req AVL of %p IDP, idp %p," "session_id %p", root_idp, idp, chanp->chan_session_id); mutex_enter(&root_chanp->chan_mutex); mutex_enter(&chanp->chan_mutex); if (chanp->chan_req_state == REQ_CMID_SERVER_NONE) { if (avl_find(&root_chanp->chan_req_avl_tree, (void *)chanp->chan_session_id, &where)) { mutex_exit(&chanp->chan_mutex); mutex_exit(&root_chanp->chan_mutex); SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "DUPLICATE ENTRY in REQ AVL : root %p, " "idp %p, session_id %p", root_idp, idp, chanp->chan_session_id); return (EINVAL); } avl_insert(&root_chanp->chan_req_avl_tree, (void *)idp, where); chanp->chan_req_state = REQ_CMID_NOTIFIED; } mutex_exit(&chanp->chan_mutex); mutex_exit(&root_chanp->chan_mutex); } SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_reject: ret %x", ret); return (ret); } int rdma_disconnect(struct rdma_cm_id *idp) { sol_cma_chan_t *chanp; int ret = EINVAL; cma_chan_state_t state; chanp = (sol_cma_chan_t *)idp; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_disconnect(%p)", idp); if (!idp) return (0); mutex_enter(&chanp->chan_mutex); if (!(SOL_CMAID_CONNECTED(chanp))) { SOL_OFS_DPRINTF_L3(sol_rdmacm_dbg_str, "rdma_disconnect(%p) - Not connected!!", idp); mutex_exit(&chanp->chan_mutex); return (EINVAL); } state = cma_get_chan_state(chanp); cma_set_chan_state(chanp, SOL_CMA_CHAN_DISCONNECT); mutex_exit(&chanp->chan_mutex); if (chanp->chan_xport_type == SOL_CMA_XPORT_IB) { ret = rdma_ib_disconnect(idp); #ifdef IWARP_SUPPORT } else if (chanp->chan_xport_type == SOL_CMA_XPORT_IWARP) { ret = rdma_iw_disconnect(idp); #endif /* IWARP_SUPPORT */ } if (ret) { mutex_enter(&chanp->chan_mutex); cma_set_chan_state(chanp, state); mutex_exit(&chanp->chan_mutex); return (ret); } SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_disconnect: ret %x", ret); return (ret); } int rdma_init_qp_attr(struct rdma_cm_id *idp, struct ib_qp_attr *qpattr, int *qp_attr_mask) { sol_cma_chan_t *chanp; int ret = EINVAL; ASSERT(idp); chanp = (sol_cma_chan_t *)idp; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_init_qp_attr(%p, %p, %p)", idp, qpattr, qp_attr_mask); if (chanp->chan_xport_type == SOL_CMA_XPORT_IB) { ret = rdma_ib_init_qp_attr(idp, qpattr, qp_attr_mask); #ifdef IWARP_SUPPORT } else if (chanp->chan_xport_type == SOL_CMA_XPORT_IWARP) ret = rdma_iw_init_qp_attr(idp, qpattr, qp_attr_mask); #endif /* IWARP_SUPPORT */ } else { ret = EINVAL; } SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_init_qp_attr: ret %x", ret); return (ret); } int rdma_join_multicast(struct rdma_cm_id *idp, struct sockaddr *addr, void *context) { sol_cma_chan_t *chanp; int ret = ENODEV; cma_chan_state_t state; ASSERT(idp); chanp = (sol_cma_chan_t *)idp; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_join_multicast(%p, %p, %p)", idp, addr, context); mutex_enter(&chanp->chan_mutex); state = cma_get_chan_state(chanp); if (state != SOL_CMA_CHAN_BOUND && state != SOL_CMA_CHAN_ROUTE_RESLVD && state != SOL_CMA_CHAN_ADDR_RESLVD) { SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "rdma_join_multicast, Invalid state"); mutex_exit(&chanp->chan_mutex); return (EINVAL); } if (chanp->chan_xport_type == SOL_CMA_XPORT_IB) ret = rdma_ib_join_multicast(idp, addr, context); #ifdef IWARP_SUPPORT /* No support for Multicast on iWARP */ else if (chanp->chan_xport_type == SOL_CMA_XPORT_IWARP) ret = ENOTSUP; #endif /* IWARP_SUPPORT */ mutex_exit(&chanp->chan_mutex); SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_join_multicast: ret %x", ret); return (ret); } void rdma_leave_multicast(struct rdma_cm_id *idp, struct sockaddr *addr) { sol_cma_chan_t *chanp; cma_chan_state_t state; ASSERT(idp); chanp = (sol_cma_chan_t *)idp; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_leave_multicast(%p, %p)", idp, addr); mutex_enter(&chanp->chan_mutex); state = cma_get_chan_state(chanp); if (state != SOL_CMA_CHAN_BOUND && state != SOL_CMA_CHAN_ROUTE_RESLVD && state != SOL_CMA_CHAN_ADDR_RESLVD) { SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "rdma_leave_multicast, Invalid state"); mutex_exit(&chanp->chan_mutex); return; } if (chanp->chan_xport_type == SOL_CMA_XPORT_IB) rdma_ib_leave_multicast(idp, addr); #ifdef IWARP_SUPPORT /* No support for Multicast on iWARP */ else if (chanp->chan_xport_type == SOL_CMA_XPORT_IWARP) SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "rdma_leave_multicast, iWARP"); #endif /* IWARP_SUPPORT */ mutex_exit(&chanp->chan_mutex); SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "rdma_join_multicast: ret"); } /* * Functions to compare to rdma_cm_id *, used by AVL tree * routines. */ int sol_cma_req_cmid_cmp(const void *p1, const void *p2) { sol_cma_chan_t *chanp; chanp = (sol_cma_chan_t *)p2; if (chanp->chan_session_id > p1) return (+1); else if (chanp->chan_session_id < p1) return (-1); else return (0); } int sol_cma_cmid_cmp(const void *p1, const void *p2) { sol_cma_chan_t *chanp; chanp = (sol_cma_chan_t *)p2; if (chanp->chan_qp_hdl > p1) return (+1); else if (chanp->chan_qp_hdl < p1) return (-1); else return (0); } /* * Function to compare two sol_cma_glbl_listen_t *, used by * AVL tree routines. */ int sol_cma_svc_cmp(const void *p1, const void *p2) { sol_cma_glbl_listen_t *listenp; uint64_t sid; sid = *(uint64_t *)p1; listenp = (sol_cma_glbl_listen_t *)p2; if (listenp->cma_listen_chan_sid > sid) return (+1); else if (listenp->cma_listen_chan_sid < sid) return (-1); else return (0); } static int cma_init_listen_root(sol_cma_chan_t *chanp) { sol_cma_glbl_listen_t *cma_listenp; sol_cma_listen_info_t *chan_listenp; int rc = 0; avl_index_t where = 0; uint64_t listen_sid; ASSERT(chanp); ASSERT(chanp->chan_listenp); chan_listenp = chanp->chan_listenp; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "cma_init_listen_root(%p)", chanp); /* * First search for matching global listen_info for this SID. * If found with the same client handle, reuse the service * handle, if matching SID is found with different client * handle, return EINVAL. */ listen_sid = ibcma_init_root_sid(chanp); SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "cma_init_listen_root: search SID 0x%llx", listen_sid); mutex_enter(&sol_cma_glob_mutex); cma_listenp = avl_find(&sol_cma_glbl_listen_tree, (void *) &listen_sid, &where); if (cma_listenp && cma_listenp->cma_listen_clnt_hdl == chanp->chan_ib_client_hdl) { SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "cma_init_listen_root: matching listenp %p SID 0x%llx", cma_listenp, listen_sid); chan_listenp->listen_entry = add_genlist( &cma_listenp->cma_listen_chan_list, (uintptr_t)chanp, NULL); chan_listenp->chan_glbl_listen_info = cma_listenp; ibcma_copy_srv_hdl(chanp, cma_listenp); mutex_exit(&sol_cma_glob_mutex); return (0); } else if (cma_listenp) { SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "cma_init_listen_root: listenp %p, SID 0x%llx match, " "client hdl prev %p, new %p mismatch", cma_listenp, listen_sid, cma_listenp->cma_listen_clnt_hdl, chanp->chan_ib_client_hdl); mutex_exit(&sol_cma_glob_mutex); return (EINVAL); } cma_listenp = kmem_zalloc(sizeof (sol_cma_glbl_listen_t), KM_SLEEP); init_genlist(&cma_listenp->cma_listen_chan_list); chan_listenp->listen_entry = add_genlist( &cma_listenp->cma_listen_chan_list, (uintptr_t)chanp, NULL); chan_listenp->chan_glbl_listen_info = cma_listenp; cma_listenp->cma_listen_clnt_hdl = chanp->chan_ib_client_hdl; cma_listenp->cma_listen_chan_sid = listen_sid; rc = ibcma_init_root_chan(chanp, cma_listenp); if (rc) { mutex_exit(&sol_cma_glob_mutex); SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "cma_init_listen_root: ibcma_init_root_chan failed!!"); delete_genlist(&cma_listenp->cma_listen_chan_list, chan_listenp->listen_entry); kmem_free(cma_listenp, sizeof (sol_cma_glbl_listen_t)); return (rc); } avl_insert(&sol_cma_glbl_listen_tree, cma_listenp, where); mutex_exit(&sol_cma_glob_mutex); return (0); } static void cma_fini_listen_root(sol_cma_chan_t *chanp) { sol_cma_glbl_listen_t *cma_listenp; sol_cma_listen_info_t *chan_listenp; ASSERT(chanp); ASSERT(chanp->chan_listenp); SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "cma_fini_listen_root(%p)", chanp); chan_listenp = chanp->chan_listenp; cma_listenp = chan_listenp->chan_glbl_listen_info; ASSERT(cma_listenp); mutex_enter(&sol_cma_glob_mutex); delete_genlist(&cma_listenp->cma_listen_chan_list, chan_listenp->listen_entry); if (genlist_empty(&cma_listenp->cma_listen_chan_list)) { if (ibcma_fini_root_chan(chanp) == 0) { avl_remove(&sol_cma_glbl_listen_tree, cma_listenp); kmem_free(cma_listenp, sizeof (sol_cma_glbl_listen_t)); } else SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "cma_fini_listen_root: " "ibcma_fini_root_chan failed"); } mutex_exit(&sol_cma_glob_mutex); } typedef struct cma_event_async_arg { struct rdma_cm_id *idp; enum rdma_cm_event_type event; int status; union { struct rdma_conn_param conn; struct rdma_ud_param param; } un; struct rdma_conn_param *conn_param; struct rdma_ud_param *ud_paramp; } cma_event_async_arg_t; static void cma_generate_event_sync(struct rdma_cm_id *, enum rdma_cm_event_type, int, struct rdma_conn_param *, struct rdma_ud_param *); void cma_generate_event_thr(void *arg) { cma_event_async_arg_t *event_arg = (cma_event_async_arg_t *)arg; cma_generate_event_sync(event_arg->idp, event_arg->event, event_arg->status, event_arg->conn_param, event_arg->ud_paramp); if (event_arg->conn_param && event_arg->conn_param->private_data_len) kmem_free((void *)event_arg->conn_param->private_data, event_arg->conn_param->private_data_len); if (event_arg->ud_paramp && event_arg->ud_paramp->private_data_len) kmem_free((void *)event_arg->ud_paramp->private_data, event_arg->ud_paramp->private_data_len); kmem_free(arg, sizeof (cma_event_async_arg_t)); } void cma_generate_event(struct rdma_cm_id *idp, enum rdma_cm_event_type event, int status, struct rdma_conn_param *conn_param, struct rdma_ud_param *ud_paramp) { cma_event_async_arg_t *event_arg; sol_cma_chan_t *chanp = (sol_cma_chan_t *)idp; /* * Set SOL_CMA_CALLER_EVENT_PROGRESS to indicate event * notification is in progress, so that races between * rdma_destroy_id() and event notification is taken care. * * If rdma_destroy_id() has been called for this CMID, call * cma_generate_event_sync() which skips notification to the * consumer and handles the event. */ mutex_enter(&chanp->chan_mutex); chanp->chan_cmid_destroy_state |= SOL_CMA_CALLER_EVENT_PROGRESS; if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) { mutex_exit(&chanp->chan_mutex); cma_generate_event_sync(idp, event, status, conn_param, ud_paramp); return; } mutex_exit(&chanp->chan_mutex); event_arg = kmem_zalloc(sizeof (cma_event_async_arg_t), KM_SLEEP); event_arg->idp = idp; event_arg->event = event; event_arg->status = status; event_arg->conn_param = NULL; event_arg->ud_paramp = NULL; if (conn_param && conn_param->private_data_len) { bcopy(conn_param, &(event_arg->un.conn), sizeof (struct rdma_conn_param)); event_arg->conn_param = &(event_arg->un.conn); event_arg->conn_param->private_data = kmem_zalloc( conn_param->private_data_len, KM_SLEEP); bcopy(conn_param->private_data, (void *)event_arg->conn_param->private_data, conn_param->private_data_len); } else if (conn_param && conn_param->private_data_len == 0) { bcopy(conn_param, &(event_arg->un.conn), sizeof (struct rdma_conn_param)); } else if (ud_paramp) { bcopy(ud_paramp, &(event_arg->un.param), sizeof (struct rdma_ud_param)); event_arg->ud_paramp = &(event_arg->un.param); if (ud_paramp->private_data_len) { event_arg->ud_paramp->private_data = kmem_zalloc( ud_paramp->private_data_len, KM_SLEEP); bcopy(ud_paramp->private_data, (void *)event_arg->ud_paramp->private_data, ud_paramp->private_data_len); } else if (ud_paramp->private_data) { event_arg->ud_paramp->private_data = ud_paramp->private_data; } } if (taskq_dispatch(system_taskq, cma_generate_event_thr, (void *)event_arg, TQ_SLEEP) == TASKQID_INVALID) { SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "generate_event_async: taskq_dispatch() failed!!"); mutex_enter(&chanp->chan_mutex); chanp->chan_cmid_destroy_state &= ~SOL_CMA_CALLER_EVENT_PROGRESS; if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) cv_broadcast(&chanp->chan_destroy_cv); mutex_exit(&chanp->chan_mutex); } } static void cma_generate_event_sync(struct rdma_cm_id *idp, enum rdma_cm_event_type event, int status, struct rdma_conn_param *conn_param, struct rdma_ud_param *ud_paramp) { struct rdma_cm_event cm_event; sol_cma_chan_t *chanp = (sol_cma_chan_t *)idp; struct rdma_cm_id *root_idp = NULL; sol_cma_chan_t *root_chanp; int ret; cma_chan_state_t chan_state; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "generate_event_sync(%p, %x, " "%x, %p, %p", idp, event, status, conn_param, ud_paramp); bzero(&cm_event, sizeof (cm_event)); cm_event.event = event; cm_event.status = status; if (conn_param) bcopy((void *)conn_param, (void *)(&(cm_event.param.conn)), sizeof (struct rdma_conn_param)); else if (ud_paramp) bcopy((void *)ud_paramp, (void *)(&(cm_event.param.ud)), sizeof (struct rdma_ud_param)); /* * If the consumer has destroyed the context for this CMID - * do not notify, skip to handling the sol_ofs specific * handling of the event. */ mutex_enter(&chanp->chan_mutex); if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) { mutex_exit(&chanp->chan_mutex); goto ofs_consume_event; } mutex_exit(&chanp->chan_mutex); root_idp = CHAN_LISTEN_ROOT(chanp); root_chanp = (sol_cma_chan_t *)root_idp; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "gen_event: root_idp %p", root_idp); if (event == RDMA_CM_EVENT_CONNECT_REQUEST) { /* * Update chan_req_state for the REQ CMID. Decrement * count of REQ CMIDs not notifed to consumer. */ ASSERT(root_idp); mutex_enter(&root_chanp->chan_mutex); root_chanp->chan_req_cnt--; #ifdef DEBUG SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "Dec req_cnt of %p IDP, idp %p, req_cnt %x", root_idp, idp, root_chanp->chan_req_cnt); #endif mutex_exit(&root_chanp->chan_mutex); } /* Pass the event to the client */ ret = (idp->event_handler) (idp, &cm_event); if (ret) { /* * If the consumer returned failure : * CONNECT_REQUEST : * 1. rdma_disconnect() to disconnect connection. * 2. wakeup destroy, if destroy has been called * for this CMID * 3. Destroy CMID if rdma_destroy has not been * called. * DISCONNECTED : * 1. call cma_handle_nomore_events() to cleanup * Other Events : * 1. Client is expected to destroy the CMID. */ if (event == RDMA_CM_EVENT_CONNECT_REQUEST) { SOL_OFS_DPRINTF_L4(sol_rdmacm_dbg_str, "cma_generate_event_async: consumer failed %d " "event", event); if (rdma_disconnect(idp)) { SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "generate_event_async: rdma_disconnect " "failed"); } mutex_enter(&chanp->chan_mutex); ASSERT(SOL_IS_SERVER_CMID(chanp)); chanp->chan_req_state = REQ_CMID_SERVER_NONE; chanp->chan_cmid_destroy_state &= ~SOL_CMA_CALLER_EVENT_PROGRESS; if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) { cv_broadcast(&chanp->chan_destroy_cv); mutex_exit(&chanp->chan_mutex); } else { mutex_exit(&chanp->chan_mutex); rdma_destroy_id(idp); } } else if (event == RDMA_CM_EVENT_DISCONNECTED) { SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "generate_event_async: consumer failed %d event", event); cma_handle_nomore_events(chanp); mutex_enter(&chanp->chan_mutex); chan_state = cma_get_chan_state(chanp); chanp->chan_cmid_destroy_state &= ~SOL_CMA_CALLER_EVENT_PROGRESS; if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) { cv_broadcast(&chanp->chan_destroy_cv); mutex_exit(&chanp->chan_mutex); } else if (chan_state == SOL_CMA_CHAN_DESTROY_PENDING) { /* rdma_destroy_id() called: destroy CMID */ mutex_exit(&chanp->chan_mutex); cma_destroy_id((struct rdma_cm_id *)chanp); } else mutex_exit(&chanp->chan_mutex); } else { SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "generate_event_async: consumer failed %d event", event); } return; } ofs_consume_event: if (event == RDMA_CM_EVENT_DISCONNECTED) { cma_chan_state_t chan_state; cma_handle_nomore_events(chanp); mutex_enter(&chanp->chan_mutex); chan_state = cma_get_chan_state(chanp); chanp->chan_cmid_destroy_state &= ~SOL_CMA_CALLER_EVENT_PROGRESS; if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) { cv_broadcast(&chanp->chan_destroy_cv); mutex_exit(&chanp->chan_mutex); } else if (chan_state == SOL_CMA_CHAN_DESTROY_PENDING) { /* If rdma_destroy_id() was called, destroy CMID */ mutex_exit(&chanp->chan_mutex); cma_destroy_id((struct rdma_cm_id *)chanp); } else mutex_exit(&chanp->chan_mutex); return; } else if (IS_UDP_CMID(idp) && event == RDMA_CM_EVENT_UNREACHABLE) { /* * If rdma_destroy_id() was called, destroy CMID * If not chan_connect_flag/ chan_req_state has already been * set to indicate that it can be deleted. */ mutex_enter(&chanp->chan_mutex); chan_state = cma_get_chan_state(chanp); chanp->chan_cmid_destroy_state &= ~SOL_CMA_CALLER_EVENT_PROGRESS; if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) { cv_broadcast(&chanp->chan_destroy_cv); mutex_exit(&chanp->chan_mutex); } else if (chan_state == SOL_CMA_CHAN_DESTROY_PENDING) { mutex_exit(&chanp->chan_mutex); cma_destroy_id(idp); } else mutex_exit(&chanp->chan_mutex); return; } mutex_enter(&chanp->chan_mutex); chanp->chan_cmid_destroy_state &= ~SOL_CMA_CALLER_EVENT_PROGRESS; if (chanp->chan_cmid_destroy_state & SOL_CMA_CALLER_CMID_DESTROYED) cv_broadcast(&chanp->chan_destroy_cv); mutex_exit(&chanp->chan_mutex); } /* Local Static functions */ static struct rdma_cm_id * cma_alloc_chan(rdma_cm_event_handler evt_hdlr, void *context, enum rdma_port_space ps) { struct rdma_cm_id *rdma_idp; sol_cma_chan_t *chanp; chanp = kmem_zalloc(sizeof (sol_cma_chan_t), KM_SLEEP); mutex_init(&chanp->chan_mutex, NULL, MUTEX_DRIVER, NULL); cv_init(&chanp->chan_destroy_cv, NULL, CV_DRIVER, NULL); rdma_idp = &(chanp->chan_rdma_cm); rdma_idp->context = context; rdma_idp->ps = ps; rdma_idp->event_handler = evt_hdlr; mutex_enter(&chanp->chan_mutex); cma_set_chan_state(chanp, SOL_CMA_CHAN_IDLE); avl_create(&chanp->chan_req_avl_tree, sol_cma_req_cmid_cmp, sizeof (sol_cma_chan_t), offsetof(sol_cma_chan_t, chan_req_avl_node)); avl_create(&chanp->chan_acpt_avl_tree, sol_cma_cmid_cmp, sizeof (sol_cma_chan_t), offsetof(sol_cma_chan_t, chan_acpt_avl_node)); mutex_exit(&chanp->chan_mutex); return (rdma_idp); } /* Change the state of sol_cma_chan_t */ static void cma_set_chan_state(sol_cma_chan_t *chanp, cma_chan_state_t newstate) { ASSERT(MUTEX_HELD(&chanp->chan_mutex)); chanp->chan_state = newstate; } cma_chan_state_t cma_get_chan_state(sol_cma_chan_t *chanp) { ASSERT(MUTEX_HELD(&chanp->chan_mutex)); return (chanp->chan_state); } /* Check & Swap the state of sol_ucma_chan_t */ static int cma_cas_chan_state(sol_cma_chan_t *chanp, cma_chan_state_t prevstate, cma_chan_state_t newstate) { int ret = 0; ASSERT(MUTEX_HELD(&chanp->chan_mutex)); if (chanp->chan_state != prevstate) ret = -1; else chanp->chan_state = newstate; return (ret); } static void cma_free_listen_list(struct rdma_cm_id *idp) { genlist_entry_t *entry; sol_cma_chan_t *chanp = (sol_cma_chan_t *)idp; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "cma_free_listen_list(%p)", idp); mutex_enter(&chanp->chan_mutex); entry = remove_genlist_head(&(CHAN_LISTEN_LIST(chanp))); mutex_exit(&chanp->chan_mutex); while (entry) { sol_cma_chan_t *ep_chanp; ep_chanp = (sol_cma_chan_t *)entry->data; SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "fini_ep_chan: %p", ep_chanp); if (ibcma_fini_ep_chan(ep_chanp) == 0) { genlist_entry_t *entry1; struct ib_device *device; cma_device_t *cma_device; ASSERT(ep_chanp->chan_listenp); mutex_enter(&ep_chanp->chan_mutex); entry1 = ep_chanp->chan_listenp->listen_ep_dev_entry; device = ep_chanp->chan_listenp->listen_ep_device; ASSERT(device); cma_device = device->data; delete_genlist(&cma_device->cma_epchan_list, entry1); sol_cma_release_device( (struct rdma_cm_id *)ep_chanp); mutex_exit(&ep_chanp->chan_mutex); if (ep_chanp->chan_listenp) kmem_free(ep_chanp->chan_listenp, sizeof (sol_cma_listen_info_t)); mutex_destroy(&ep_chanp->chan_mutex); cv_destroy(&ep_chanp->chan_destroy_cv); kmem_free(ep_chanp, sizeof (sol_cma_chan_t)); kmem_free(entry, sizeof (genlist_entry_t)); } mutex_enter(&chanp->chan_mutex); entry = remove_genlist_head(&(CHAN_LISTEN_LIST(chanp))); mutex_exit(&chanp->chan_mutex); } } /* * Destroy a listening CMID when : * a. All CONNECTION REQUEST recieved have been rejected * or closed. * b. No CONNECTION REQUEST recieved. * Do not destroy a listening CMID when : * a. CONNECTION REQUEST has been recieved and not been * accepted from the passive / server side. * b. CONNECTION REQUEST has been recieved and has been * accepted from the passive server side. * Mark the listening CMID as destroy pending. * * For CMIDs created for rdma_connect() or created for a * CONNECT request, destroy the CMID only when : * CONNECTION has been closed or rejected. * * Mark the CMID as destroy pending. * * When a connection is rejected or closed : * Check if flag indicates - destroy pending, * cma_destroy_id() is called, this also does * * If there is a listening CMID assosiated with it, * call cma_destroy_if(listen_cmid); */ void cma_destroy_id(struct rdma_cm_id *idp) { sol_cma_chan_t *chanp = (sol_cma_chan_t *)idp; cma_chan_state_t state; ulong_t acpt_nodes, req_nodes; mutex_enter(&chanp->chan_mutex); acpt_nodes = avl_numnodes(&chanp->chan_acpt_avl_tree); req_nodes = avl_numnodes(&chanp->chan_req_avl_tree); state = cma_get_chan_state(chanp); SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "cma_destroy_id(%p)- " "est CMIDs %ld, req CMID %ld, listen_root %p, state %x, %x", idp, acpt_nodes, req_nodes, chanp->listen_root, state, chanp->chan_req_state); /* * If there are either REQ recieved or Established CMIDs just return. * rdma_destroy() for these CMIDs can be called by client later. */ if (acpt_nodes || req_nodes) { cma_set_chan_state(chanp, SOL_CMA_CHAN_DESTROY_PENDING); mutex_exit(&chanp->chan_mutex); return; } cma_set_chan_state(chanp, SOL_CMA_CHAN_DESTROYING); avl_destroy(&chanp->chan_req_avl_tree); avl_destroy(&chanp->chan_acpt_avl_tree); mutex_exit(&chanp->chan_mutex); if (idp->route.path_rec) { kmem_free(idp->route.path_rec, sizeof (struct ib_sa_path_rec) * idp->route.num_paths); idp->route.path_rec = NULL; } switch (chanp->chan_xport_type) { case SOL_CMA_XPORT_NONE : break; case SOL_CMA_XPORT_IB : rdma_ib_destroy_id(idp); break; #ifdef IWARP_SUPPORT case SOL_CMA_XPORT_IWARP : rdma_iw_destroy_id(idp); break; #endif /* IWARP_SUPPORT */ default : SOL_OFS_DPRINTF_L2(sol_rdmacm_dbg_str, "cma_destroy_id: Unsupported xport type %x", chanp->chan_xport_type); break; } /* * Flush out & Free all listeners wrt to this ID * No locking is required as this code is executed * all REQ CMIDs have been destroyed. listen_list * will therefore not be modified during this loop. */ if (chanp->chan_listenp) { cma_free_listen_list(idp); cma_fini_listen_root(chanp); kmem_free((void *)chanp->chan_listenp, sizeof (sol_cma_listen_info_t)); chanp->chan_listenp = NULL; } if (chanp->listen_root) { struct rdma_cm_id *root_idp; sol_cma_chan_t *root_chanp; root_idp = chanp->listen_root; root_chanp = (sol_cma_chan_t *)root_idp; mutex_enter(&root_chanp->chan_mutex); state = cma_get_chan_state(root_chanp); acpt_nodes = avl_numnodes(&root_chanp->chan_acpt_avl_tree); req_nodes = avl_numnodes(&root_chanp->chan_req_avl_tree); mutex_exit(&root_chanp->chan_mutex); SOL_OFS_DPRINTF_L5(sol_rdmacm_dbg_str, "cma_destroy_id(%p)-" " root idp %p, state %x, acpt_nodes %ld, req_nodes %ld", idp, root_idp, state, acpt_nodes, req_nodes); if (state == SOL_CMA_CHAN_DESTROY_PENDING && req_nodes == 0UL && acpt_nodes == 0UL) { mutex_enter(&root_chanp->chan_mutex); root_chanp->chan_req_state = REQ_CMID_SERVER_NONE; mutex_exit(&root_chanp->chan_mutex); cma_destroy_id(root_idp); } else if (state == SOL_CMA_CHAN_DESTROY_WAIT && req_nodes == 0UL && acpt_nodes == 0UL) { mutex_enter(&root_chanp->chan_mutex); cma_set_chan_state(root_chanp, SOL_CMA_CHAN_DESTROY_PENDING); root_chanp->chan_req_state = REQ_CMID_SERVER_NONE; cv_broadcast(&root_chanp->chan_destroy_cv); mutex_exit(&root_chanp->chan_mutex); } } mutex_destroy(&chanp->chan_mutex); cv_destroy(&chanp->chan_destroy_cv); kmem_free(chanp, sizeof (sol_cma_chan_t)); } /* * Server TCP disconnect for an established channel. * If destroy_id() has been called for the listening * CMID and there are no more CMIDs with pending * events corresponding to the listening CMID, free * the listening CMID. * */ static void cma_handle_nomore_events(sol_cma_chan_t *chanp) { struct rdma_cm_id *idp, *root_idp; sol_cma_chan_t *root_chanp; cma_chan_state_t state; ulong_t req_nodes, acpt_nodes; idp = (struct rdma_cm_id *)chanp; root_idp = CHAN_LISTEN_ROOT(chanp); root_chanp = (sol_cma_chan_t *)root_idp; if (!root_chanp) return; mutex_enter(&root_chanp->chan_mutex); mutex_enter(&chanp->chan_mutex); CHAN_LISTEN_ROOT(chanp) = NULL; root_chanp->chan_req_total_cnt--; /* * Removal of CMID from the AVL trees should already have been done * by now. Below code mainly as a safety net. */ if (chanp->chan_req_state == REQ_CMID_ACCEPTED) { ASSERT(chanp->chan_qp_hdl); ASSERT(cma_get_acpt_idp(root_idp, chanp->chan_qp_hdl)); avl_remove(&root_chanp->chan_acpt_avl_tree, idp); chanp->chan_req_state = REQ_CMID_SERVER_NONE; } if (REQ_CMID_IN_REQ_AVL_TREE(chanp)) { ASSERT(chanp->chan_session_id); ASSERT(cma_get_req_idp(root_idp, chanp->chan_session_id)); avl_remove(&root_chanp->chan_req_avl_tree, idp); chanp->chan_req_state = REQ_CMID_SERVER_NONE; } state = cma_get_chan_state(root_chanp); req_nodes = avl_numnodes(&root_chanp->chan_req_avl_tree); acpt_nodes = avl_numnodes(&root_chanp->chan_acpt_avl_tree); mutex_exit(&chanp->chan_mutex); mutex_exit(&root_chanp->chan_mutex); if (state == SOL_CMA_CHAN_DESTROY_PENDING && req_nodes == 0UL && acpt_nodes == 0UL) cma_destroy_id(root_idp); } extern int ib_modify_qp(struct ib_qp *, struct ib_qp_attr *, int); extern int rdma_init_qp_attr(struct rdma_cm_id *, struct ib_qp_attr *, int *); static int cma_init_ud_qp(sol_cma_chan_t *chanp, struct ib_qp *qp) { struct ib_qp_attr qp_attr; int qp_attr_mask, ret; qp_attr.qp_state = IB_QPS_INIT; ret = rdma_init_qp_attr(&chanp->chan_rdma_cm, &qp_attr, &qp_attr_mask); if (ret) return (ret); ret = ib_modify_qp(qp, &qp_attr, qp_attr_mask); if (ret) return (ret); qp_attr.qp_state = IB_QPS_RTR; ret = ib_modify_qp(qp, &qp_attr, IB_QP_STATE); if (ret) return (ret); qp_attr.qp_state = IB_QPS_RTS; qp_attr.sq_psn = 0; ret = ib_modify_qp(qp, &qp_attr, IB_QP_STATE | IB_QP_SQ_PSN); return (ret); } static int cma_init_conn_qp(sol_cma_chan_t *chanp, struct ib_qp *qp) { struct ib_qp_attr qp_attr; int qp_attr_mask, ret; qp_attr.qp_state = IB_QPS_INIT; ret = rdma_init_qp_attr(&chanp->chan_rdma_cm, &qp_attr, &qp_attr_mask); if (ret) return (ret); return (ib_modify_qp(qp, &qp_attr, qp_attr_mask)); } static inline int cma_is_ud_ps(enum rdma_port_space ps) { return (ps == RDMA_PS_UDP || ps == RDMA_PS_IPOIB); } int rdma_create_qp(struct rdma_cm_id *idp, struct ib_pd *pd, struct ib_qp_init_attr *qp_init_attr) { sol_cma_chan_t *chanp; struct ib_qp *qp; int ret; ofs_client_t *dev_ofs_client; ASSERT(idp); chanp = (sol_cma_chan_t *)idp; if (idp->device->node_guid != pd->device->node_guid) return (-EINVAL); dev_ofs_client = (ofs_client_t *)pd->device->clnt_hdl; rdma_map_id2clnthdl(idp, dev_ofs_client->ibt_hdl, NULL); qp = ib_create_qp(pd, qp_init_attr); if ((uintptr_t)qp >= (uintptr_t)-0xFFF) { return ((intptr_t)qp); } rdma_map_id2qphdl(idp, (void *)qp->ibt_qp); if (cma_is_ud_ps(idp->ps)) { ret = cma_init_ud_qp(chanp, qp); } else { ret = cma_init_conn_qp(chanp, qp); } if (ret) { goto err; } idp->qp = qp; chanp->chan_qp_num = qp->qp_num; chanp->chan_is_srq = (qp->srq != NULL); return (0); err: (void) ib_destroy_qp(qp); return (ret); } void rdma_destroy_qp(struct rdma_cm_id *idp) { ASSERT(idp); (void) ib_destroy_qp(idp->qp); idp->qp = NULL; }