/*
* 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
* 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
*/
/*
*/
/*
* 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 :
* Solaris uCMA (sol_ucma) driver is the current consumer for
* sol_cma.
*/
/* Standard driver includes */
/* Modload support */
"Solaris OFS Misc module"
};
(void *)&sol_ofs_modmisc,
};
static void sol_cma_add_dev(struct ib_device *);
static void sol_cma_rem_dev(struct ib_device *);
/*
* Local functions defines.
*/
int sol_cma_svc_cmp(const void *, const void *);
void *, enum rdma_port_space);
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();
extern int ibcma_fini_root_chan(sol_cma_chan_t *);
extern int ibcma_fini_ep_chan(sol_cma_chan_t *);
extern void rdma_ib_destroy_id(struct rdma_cm_id *);
struct sockaddr *, int);
extern int rdma_ib_resolve_route(struct rdma_cm_id *, int);
int *);
extern int rdma_ib_listen(struct rdma_cm_id *, int);
extern int rdma_ib_disconnect(struct rdma_cm_id *);
void *);
int
_init(void)
{
int err;
sol_cma_svc_cmp, sizeof (sol_cma_glbl_listen_t),
if (!sol_cma_ib_client) {
"_init() - mem alloc failed");
return (ENOMEM);
}
"_init() ib_register_client() failed with err %d",
err);
return (err);
}
"_init() - mod_install() failed");
return (err);
}
return (err);
}
int
_fini(void)
{
int err;
if (avl_numnodes(&sol_cma_glbl_listen_tree)) {
"listen CMIDs still active");
return (EBUSY);
}
"_fini: mod_remove failed");
return (err);
}
return (err);
}
int
{
}
typedef struct cma_device {
/* Ptr in the global sol_cma_dev_list */
/* List of listeners for this device */
enum {
} cma_device_t;
static void
{
if (!new_device) {
"alloc failed!!");
return;
}
}
static void
{
if (!rem_device) {
"NULL cma_dev!!");
return;
}
if (rem_device->cma_ref_count) {
"BUSY cma_dev!!");
return;
}
while (entry) {
if (ibcma_fini_ep_chan(ep_chanp) == 0) {
entry1);
}
}
}
struct ib_device *
{
continue;
"sol_cma_acquire_dev() - Device getting removed!!");
return (NULL);
}
return (cma_devp->cma_device);
}
return (NULL);
}
static void
{
continue;
cma_devp->cma_ref_count == 0) {
"sol_cma_release_dev() - Device free removed!!");
return;
}
}
}
void
{
continue;
return;
}
}
/*
* rdma_cm.h API functions.
*/
struct rdma_cm_id *
enum rdma_port_space ps)
{
"rdma_create_id: unsupported protocol %x", ps);
return (NULL);
}
"rdma_create_id : ret %p", rdma_idp);
return (rdma_idp);
}
void
void *iw_client_hdl)
{
"rdma_map_id2clnthdl(%p, %p, %p)",
}
void
{
}
void
{
if (!rdma_idp)
return;
/*
* Wait in destroy of CMID when rdma_resolve_addr() / rdma_listen()
* rdma_resolve_route() API is in progress.
*/
/* Wait if Event is been notified to consumer */
is_root_cmid = 1;
is_passive = 1;
/*
* Skip Active side handling for passive CMIDs and listen CMID
* for which REQ CMIDs have not been created.
*/
REQ_CMID_QUEUED)) {
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.
*/
"not notified handling");
if (req_cmid_chan->chan_req_state ==
chanp->chan_req_cnt--;
(void) rdma_disconnect(
(struct rdma_cm_id *)req_cmid_chan);
} else {
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.
*/
"root idp waiting");
}
if (root_chanp)
#ifdef DEBUG
"root_idp %p, cnt %x, state %x", root_chanp,
#endif
do_wait = 1;
if (root_chanp)
/*
* A connected CM ID has not been disconnected.
* Call rdma_disconnect() to disconnect it.
*/
if (rc) {
"rdma_destroy_id(%p)- disconnect failed!!",
rdma_idp);
return;
}
"rdma_destroy_id(chanp %p, connect %x, ps %x)",
if (SOL_CMAID_CONNECTED(chanp)) {
if (do_wait) {
&chanp->chan_mutex);
} else {
}
} else {
/*
* No more callbacks are expected for this CMID.
* Free this CMID.
*/
}
} else if (is_root_cmid == 0 && state ==
/*
* CM ID was connected and disconnect is process.
* Free of this CM ID is done for the DISCONNECT
* notification for this CMID.
*/
} else if (state != SOL_CMA_CHAN_DESTROY_PENDING) {
/* CM ID, not connected, just free it. */
} else
}
/*
* 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
{
int ret;
if (ret) {
return (ret);
}
/* Copy the local address to rdma_id structure */
sizeof (struct sockaddr));
/*
* 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.
*/
}
"rdma_bind_addr: ret IB @");
return (0);
#ifdef IWARP_SUPPORT
== 0) {
"rdma_bind_addr: ret iWARP @");
return (0);
#endif /* IWARP_SUPPORT */
}
return (EINVAL);
}
int
{
"rdma_resolve_addr : invalid chan state %x", state);
return (EINVAL);
}
if (chanp->chan_cmid_destroy_state &
"rdma_resolve_addr : CMID %p, destroy called", chanp);
return (EINVAL);
}
sizeof (struct sockaddr));
}
sizeof (struct sockaddr));
/*
* First resolve this as an @ corresponding to IB fabric
* if this fails, resolve this as an @ corresponding to iWARP
*/
}
dst_addr, timeout_ms) == 0) {
"rdma_resolve_addr: ret IB @");
#ifdef IWARP_SUPPORT
"rdma_resolve_addr: ret iWARP @");
#endif /* IWARP_SUPPORT */
} else {
"rdma_resolve_addr: Invalid @");
return (EINVAL);
}
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
{
if (chanp->chan_cmid_destroy_state &
"cma_resolve_addr : CMID %p, destroy called", chanp);
return;
}
if (rc == 0) {
} else
/*
* Generate RDMA_CM_EVENT_ADDR_RESOLVED event
* This will result in RDMA_USER_CM_CMD_RESOLVE_ROUTE in
* userland.
*/
}
int
{
SOL_CMA_CHAN_ROUTE_RESLVD) != 0) {
"resolve_route: Invalid state");
return (EINVAL);
}
if (chanp->chan_cmid_destroy_state &
"rdma_resolve_route : CMID %p, destroy called", chanp);
return (EINVAL);
}
/*
* Generate RDMA_CM_EVENT_ROUTE_RESOLVED event
* This will result in RDMA_USER_CM_CMD_RESOLVE_ROUTE in
* userland
*/
return (0);
}
/*
* Connect or Listen request should be send after Route is resolved
*
* Active Side (Client) :
* -->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, Invalid Xport");
return (EINVAL);
}
"rdma_connect, Invalid state");
return (EINVAL);
}
#ifdef IWARP_SUPPORT
#endif /* IWARP_SUPPORT */
}
return (ret);
}
static int cma_init_listen_root(sol_cma_chan_t *);
static void cma_fini_listen_root(sol_cma_chan_t *);
int
{
int ret = 0;
if (state == SOL_CMA_CHAN_IDLE) {
return (EINVAL);
}
if (chanp->chan_cmid_destroy_state &
"rdma_listen : CMID %p, destroy called", chanp);
return (EINVAL);
}
KM_SLEEP);
if (ret) {
"cma_init_listen_root: failed");
sizeof (sol_cma_listen_info_t));
return (EINVAL);
}
#ifdef IWARP_SUPPORT
#endif
#ifdef IWARP_SUPPORT
#endif /* IWARP_SUPPORT */
}
sizeof (sol_cma_listen_info_t));
"No listeners");
return (0);
}
}
#ifdef IWARP_SUPPORT
#endif
if (ret)
break;
}
return (ret);
}
int
{
idp, conn_param);
"rdma_accept, Invalid state");
return (EINVAL);
}
root_idp);
/* For TCP, delete from REQ AVL & insert to ACPT AVL */
void *find_ret;
/*
* This CMID has been deleted, maybe because of timeout.
* Return EINVAL.
*/
"accept: root_idp %p chanp %p, not in REQ "
return (EINVAL);
}
"Add to ACPT AVL of %p IDP, idp %p, qp_hdl %p",
if (find_ret) {
"DUPLICATE ENTRY in ACPT AVL : root %p, "
"idp %p, qp_hdl %p",
return (EINVAL);
}
}
/*
* Accepting the connect request, no more events for this
* connection.
*/
/* If rdma_destroy_id() was called, destroy CMID */
if (chan_state == SOL_CMA_CHAN_DESTROY_PENDING) {
return (EINVAL);
}
}
#ifdef IWARP_SUPPORT
#endif /* IWARP_SUPPORT */
void *find_ret;
"Delete from REQ AVL of %p IDP, idp %p",
chanp->chan_qp_hdl));
idp);
if (find_ret) {
"DUPLICATE ENTRY in REQ AVL : root %p, "
"idp %p, session_id %p",
return (EINVAL);
}
}
}
return (ret);
}
int
{
"rdma_notify, Invalid state");
return (EINVAL);
}
return (0);
}
int
{
"rdma_accept, Invalid state");
return (EINVAL);
}
if (root_idp) {
/*
* Remove from REQ AVL tree. If this CMID has been deleted,
* it maybe because of timeout. Return EINVAL.
*/
"reject: root_idp %p chanp %p, not in REQ "
return (EINVAL);
}
}
#ifdef IWARP_SUPPORT
#endif /* IWARP_SUPPORT */
/*
* Rejecting connect request, no more events for this
* connection.
*/
/* If rdma_destroy_id() was called, destroy CMID */
"reject fail: Add to Req AVL of %p IDP, idp %p,"
"DUPLICATE ENTRY in REQ AVL : root %p, "
"idp %p, session_id %p",
return (EINVAL);
}
}
}
return (ret);
}
int
{
if (!idp)
return (0);
if (!(SOL_CMAID_CONNECTED(chanp))) {
"rdma_disconnect(%p) - Not connected!!", idp);
return (EINVAL);
}
#ifdef IWARP_SUPPORT
#endif /* IWARP_SUPPORT */
}
if (ret) {
return (ret);
}
return (ret);
}
int
int *qp_attr_mask)
{
#ifdef IWARP_SUPPORT
#endif /* IWARP_SUPPORT */
} else {
}
"rdma_init_qp_attr: ret %x", ret);
return (ret);
}
int
void *context)
{
"rdma_join_multicast(%p, %p, %p)",
if (state != SOL_CMA_CHAN_BOUND &&
"rdma_join_multicast, Invalid state");
return (EINVAL);
}
#ifdef IWARP_SUPPORT
/* No support for Multicast on iWARP */
#endif /* IWARP_SUPPORT */
"rdma_join_multicast: ret %x", ret);
return (ret);
}
void
{
if (state != SOL_CMA_CHAN_BOUND &&
"rdma_leave_multicast, Invalid state");
return;
}
#ifdef IWARP_SUPPORT
/* No support for Multicast on iWARP */
"rdma_leave_multicast, iWARP");
#endif /* IWARP_SUPPORT */
}
/*
* Functions to compare to rdma_cm_id *, used by AVL tree
* routines.
*/
int
{
return (+1);
return (-1);
else
return (0);
}
int
{
return (+1);
return (-1);
else
return (0);
}
/*
* Function to compare two sol_cma_glbl_listen_t *, used by
* AVL tree routines.
*/
int
{
return (+1);
return (-1);
else
return (0);
}
static int
{
int rc = 0;
avl_index_t where = 0;
"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.
*/
"cma_init_listen_root: search SID 0x%llx",
(void *) &listen_sid, &where);
"cma_init_listen_root: matching listenp %p SID 0x%llx",
return (0);
} else if (cma_listenp) {
"cma_init_listen_root: listenp %p, SID 0x%llx match, "
"client hdl prev %p, new %p mismatch",
return (EINVAL);
}
if (rc) {
"cma_init_listen_root: ibcma_init_root_chan failed!!");
return (rc);
}
return (0);
}
static void
{
chanp);
if (ibcma_fini_root_chan(chanp) == 0) {
sizeof (sol_cma_glbl_listen_t));
} else
"cma_fini_listen_root: "
"ibcma_fini_root_chan failed");
}
}
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;
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)
{
}
void
struct rdma_ud_param *ud_paramp)
{
/*
* 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.
*/
return;
}
sizeof (struct rdma_conn_param));
sizeof (struct rdma_conn_param));
} else if (ud_paramp) {
sizeof (struct rdma_ud_param));
if (ud_paramp->private_data_len) {
} else if (ud_paramp->private_data) {
}
}
"generate_event_async: taskq_dispatch() failed!!");
if (chanp->chan_cmid_destroy_state &
}
}
static void
struct rdma_ud_param *ud_paramp)
{
struct rdma_cm_event cm_event;
int ret;
if (conn_param)
sizeof (struct rdma_conn_param));
else if (ud_paramp)
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.
*/
goto ofs_consume_event;
}
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.
*/
#ifdef DEBUG
"Dec req_cnt of %p IDP, idp %p, req_cnt %x",
#endif
}
/* Pass the event to the client */
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) {
"cma_generate_event_async: consumer failed %d "
"event", event);
if (rdma_disconnect(idp)) {
"generate_event_async: rdma_disconnect "
"failed");
}
if (chanp->chan_cmid_destroy_state &
} else {
}
} else if (event == RDMA_CM_EVENT_DISCONNECTED) {
"generate_event_async: consumer failed %d event",
event);
if (chanp->chan_cmid_destroy_state &
} else if (chan_state == SOL_CMA_CHAN_DESTROY_PENDING) {
/* rdma_destroy_id() called: destroy CMID */
} else
} else {
"generate_event_async: consumer failed %d event",
event);
}
return;
}
if (event == RDMA_CM_EVENT_DISCONNECTED) {
if (chanp->chan_cmid_destroy_state &
} else if (chan_state == SOL_CMA_CHAN_DESTROY_PENDING) {
/* If rdma_destroy_id() was called, destroy CMID */
} else
return;
/*
* 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.
*/
if (chanp->chan_cmid_destroy_state &
} else if (chan_state == SOL_CMA_CHAN_DESTROY_PENDING) {
} else
return;
}
}
/* Local Static functions */
static struct rdma_cm_id *
enum rdma_port_space ps)
{
struct rdma_cm_id *rdma_idp;
sizeof (sol_cma_chan_t),
sizeof (sol_cma_chan_t),
return (rdma_idp);
}
/* Change the state of sol_cma_chan_t */
static void
{
}
{
return (chanp->chan_state);
}
/* Check & Swap the state of sol_ucma_chan_t */
static int
{
int ret = 0;
ret = -1;
else
return (ret);
}
static void
{
while (entry) {
ep_chanp);
if (ibcma_fini_ep_chan(ep_chanp) == 0) {
entry1);
(struct rdma_cm_id *)ep_chanp);
if (ep_chanp->chan_listenp)
sizeof (sol_cma_listen_info_t));
}
}
}
/*
* 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
{
"est CMIDs %ld, req CMID %ld, listen_root %p, state %x, %x",
/*
* 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) {
return;
}
}
switch (chanp->chan_xport_type) {
case SOL_CMA_XPORT_NONE :
break;
case SOL_CMA_XPORT_IB :
break;
#ifdef IWARP_SUPPORT
case SOL_CMA_XPORT_IWARP :
break;
#endif /* IWARP_SUPPORT */
default :
"cma_destroy_id: Unsupported xport type %x",
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) {
sizeof (sol_cma_listen_info_t));
}
if (chanp->listen_root) {
struct rdma_cm_id *root_idp;
" root idp %p, state %x, acpt_nodes %ld, req_nodes %ld",
if (state == SOL_CMA_CHAN_DESTROY_PENDING &&
} else if (state == SOL_CMA_CHAN_DESTROY_WAIT &&
}
}
}
/*
* 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
{
if (!root_chanp)
return;
/*
* Removal of CMID from the AVL trees should already have been done
* by now. Below code mainly as a safety net.
*/
chanp->chan_qp_hdl));
}
if (REQ_CMID_IN_REQ_AVL_TREE(chanp)) {
chanp->chan_session_id));
}
acpt_nodes == 0UL)
}
int *);
static int
{
struct ib_qp_attr qp_attr;
int qp_attr_mask, ret;
if (ret)
return (ret);
if (ret)
return (ret);
if (ret)
return (ret);
return (ret);
}
static int
{
struct ib_qp_attr qp_attr;
int qp_attr_mask, ret;
if (ret)
return (ret);
}
static inline int
{
}
int
struct ib_qp_init_attr *qp_init_attr)
{
int ret;
return (-EINVAL);
}
} else {
}
if (ret) {
goto err;
}
return (0);
err:
(void) ib_destroy_qp(qp);
return (ret);
}
void
{
}