/*
* 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
*/
/*
*/
/*
* The sol_ucma driver provides the API for librdmacm library for RDMACM
* functionality.
*
* sol_uverbs will create a minor node with prefix ":ucma",
* which can be opened only by the kernel (cred == kcred).
*
* sol_cma driver will open and close the sol_uverb minor
* device using the Layered Driver Interfaces (See PSARC
* 2001/769).
*/
/* Standard driver includes */
/* Common header files */
/* Kernel Headers for User rdma_cm API */
/* Kernel rdma_cm API */
/* sol_ucma internal Header files */
/* entry point function prototype declarations */
/* Driver entry points */
sol_ucma_open, /* open */
sol_ucma_close, /* close */
nodev, /* strategy (block) */
nodev, /* print (block) */
nodev, /* dump (block) */
nodev, /* read */
sol_ucma_write, /* write */
nodev, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
sol_ucma_poll, /* chpoll */
ddi_prop_op, /* prop_op */
NULL, /* streams */
CB_REV /* rev */
};
/* Driver operations */
DEVO_REV, /* struct rev */
0, /* refcnt */
sol_ucma_getinfo, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
sol_ucma_attach, /* attach */
sol_ucma_detach, /* detach */
nodev, /* reset */
&sol_ucma_cb_ops, /* cb_ops */
NULL, /* bus_ops */
nodev, /* power */
ddi_quiesce_not_needed /* quiesce */
};
/* Module Driver Info */
"Solaris User RDMACM driver",
};
/* Module Linkage */
NULL,
};
/* Function pointers for uverbs functions */
/* Global Variables */
/* RDMACM Functions */
/*
* Event callback from sol_cma
*/
/*
* Internal functions.
*/
static sol_ucma_file_t *
static sol_ucma_chan_t *
static void
ucma_free_chan(sol_ucma_chan_t *, int);
static int
static void
static void
static void
static void
static void sol_ucma_user_objs_init();
static void sol_ucma_user_objs_fini();
int
_init(void)
{
int error;
&sol_ucma.ucma_ldi_ident)) != 0) {
"ldi_ident_from_mod() failed");
return (error);
}
if (error) {
return (error);
}
return (error);
}
int
{
}
int
_fini(void)
{
int ret;
"sol_ucma, _fini : mod_remove failed");
return (ret);
}
return (DDI_SUCCESS);
}
static int
{
int rval;
switch (cmd) {
case DDI_ATTACH:
"attach: failed, > 1 instance");
return (DDI_FAILURE);
}
0, DDI_PSEUDO, 0);
if (rval != DDI_SUCCESS) {
"attach: ddi_create_minor_node failed");
return (DDI_FAILURE);
}
"attach : DDI_ATTACH success");
return (DDI_SUCCESS);
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
{
switch (cmd) {
case DDI_DETACH:
if (sol_ucma.ucma_num_file) {
"detach : %x files not closed",
return (DDI_FAILURE);
}
"detach : DDI_DETACH success");
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/*ARGSUSED*/
static int
void **resultp)
{
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
*resultp = (void *)0;
return (DDI_SUCCESS);
default :
return (DDI_FAILURE);
}
}
static int
{
return (EAGAIN);
/*
* For the first open, ensure that the sol_uverbs driver is attached.
* Also get the function pointers for uverbs API functions using
* ddi_modopen() and ddi_modsym() for the sol_uverbs driver.
*
* ldi_open() is done to ensure that sol_uverbs driver is attached,
* even though ddi_modopen is sufficient to get the function pointers
* for the uverbs APIs
*/
sol_ucma.ucma_ldi_ident)) != 0) {
"ldi_open_by_name(%s, ...) failed with rval %x",
return (ENODEV);
}
"ddi_modopen(%s, ...) failed", "drv/sol_uverbs");
return (ret_errno);
}
== NULL) {
"ddi_modsym(%s, ...) failed",
return (ret_errno);
}
== NULL) {
"ddi_modsym(%s, ...) failed",
return (ret_errno);
}
"ddi_modsym(%s, ...) failed",
return (ret_errno);
}
if ((uverbs_uqpn_cq_ctrl_fp =
"ddi_modsym(%s, ...) failed",
return (ret_errno);
}
if ((uverbs_set_qp_free_state_fp =
"ddi_modsym(%s, ...) failed",
return (ret_errno);
}
if ((uverbs_flush_qp_fp =
"ddi_modsym(%s, ...) failed",
return (ret_errno);
}
"uverbs_get_clnt_hdl failed");
return (ENODEV);
}
} else if (sol_ucma.ucma_clnt_hdl_flag ==
}
return (0);
}
static int
{
if (!filep) {
dev);
return (0);
}
/* Disable further event handling for this CM event channel */
}
/*
* Destroy CM IDs which have not been destroyed.
* For CMIDs which have been connected, call
* uverbs_set_qp_free_state(SOL_UVERBS2UCMA_ENABLE_QP_FREE)
* so that QP free will be done when appropriate,
*/
while (entry) {
void *qphdl;
if (chanp->chan_rdma_id)
if (qphdl)
}
/* Flush out any events that have not been acknowledged. */
if (filep->file_pending_evt_cnt) {
"close : %d Events not reported to userland",
while (entry) {
};
}
/*
* Module close for sol_uverbs when the last file is closed.
* Set the function pointers to sol_uverbs API to NULL
* ddi_modclose() and ldi_close() - sol_uverbs driver
*/
}
return (0);
}
typedef struct sol_ucma_cmd_table_s {
sizeof (sol_ucma_create_id_t),
sizeof (sol_ucma_create_id_resp_t),
sizeof (sol_ucma_destroy_id_t),
sizeof (sol_ucma_destroy_id_resp_t),
sizeof (sol_ucma_bind_addr_t),
0,
sizeof (sol_ucma_resolve_addr_t),
0,
sizeof (sol_ucma_resolve_route_t),
0,
sizeof (sol_ucma_query_route_t),
sizeof (sol_ucma_query_route_resp_t),
sizeof (sol_ucma_connect_t),
0,
sizeof (sol_ucma_listen_t),
0,
sizeof (sol_ucma_accept_t),
0,
sizeof (sol_ucma_reject_t),
0,
sizeof (sol_ucma_disconnect_t),
0,
sizeof (sol_ucma_init_qp_attr_t),
sizeof (struct ib_uverbs_qp_attr),
sizeof (sol_ucma_get_event_t),
sizeof (sol_ucma_event_resp_t),
0,
0,
sizeof (sol_ucma_set_option_t),
0,
sizeof (sol_ucma_notify_t),
0,
sizeof (sol_ucma_join_mcast_t),
sizeof (sol_ucma_create_id_resp_t),
sizeof (sol_ucma_destroy_id_t),
sizeof (sol_ucma_destroy_id_resp_t)
};
static int
{
int ret;
if (ret != 0) {
return (ret);
}
sizeof (sol_ucma_cmd_table) / sizeof (sol_ucma_cmd_table_t)) {
return (EINVAL);
}
return (EINVAL);
}
/*
* Check the user passed IN-OUT buffer length, with expected lengths
*/
"write : Invalid Input length cmd %x, in %x expected %x",
return (EINVAL);
}
"write : Invalid Output length cmd %x, in %x expected %x",
return (EINVAL);
}
sizeof (sol_ucma_cmd_hdr_t));
}
/* If the command fails, set back the uio_resid */
if (ret)
return (ret);
}
static int
{
return (EINVAL);
if (filep->file_pending_evt_cnt) {
} else {
*reventsp = 0;
if (!anyyet)
}
return (0);
}
/*
* RDMACM functions.
*/
/*ARGSUSED*/
static int
{
minor);
"create_id: No free Channel");
return (ENODEV);
}
#ifdef _LP64
sizeof (sol_ucma_create_id_resp_t))) {
#else
sizeof (sol_ucma_create_id_resp_t))) {
#endif
"create_id: copyout fault");
return (EFAULT);
}
/* */
"create_id: rdma_create_id failed");
return (EINVAL);
}
return (0);
}
/*ARGSUSED*/
static int
{
if (!filep) {
"destroy_id : filep NULL");
return (EINVAL);
}
} else {
"ucma_id %x invalid", ucma_id);
return (0);
}
/*
* Event handling, Flush out events pending
* return the number of events that were acked. Free events not acked.
*/
if (filep->file_pending_evt_cnt != 0) {
"destroy_id: pending events");
while (entry) {
sizeof (sol_ucma_event_t));
};
filep->file_pending_evt_cnt = 0;
}
if (chanp) {
} else {
id_resp.events_reported = 0;
}
#ifdef _LP64
sizeof (sol_ucma_destroy_id_resp_t))) {
#else
sizeof (sol_ucma_destroy_id_resp_t))) {
#endif
"destroy_id: copyout fault");
return (EFAULT);
}
/* */
if (chanp) {
if (chanp->chan_rdma_id)
}
return (0);
}
/*ARGSUSED*/
static int
{
int ret;
return (EINVAL);
return (ret);
}
/*ARGSUSED*/
static int
{
int ret;
"resolve_addr: ucma_id %x invalid", ucma_id);
return (EINVAL);
}
return (ret);
}
/*ARGSUSED*/
static int
{
int ret;
return (EINVAL);
chanp);
return (ret);
}
/*ARGSUSED*/
static int
{
return (EINVAL);
#ifdef _LP64
sizeof (sol_ucma_query_route_resp_t))) {
#else
sizeof (sol_ucma_query_route_resp_t))) {
#endif
"query_route: copyout fault");
return (EFAULT);
}
return (0);
}
/*ARGSUSED*/
static int
{
int ret;
void *qphdl;
return (EINVAL);
return (EINVAL);
}
/*
* rdma_connect() initiated for this CMID, disable sol_uverbs to
* free the QP assosiated with this CM ID.
*/
NULL);
}
return (ret);
}
/*ARGSUSED*/
static int
{
int ret;
return (EINVAL);
return (ret);
}
/*ARGSUSED*/
static int
{
int ret;
void *qphdl;
return (EINVAL);
return (EINVAL);
}
} else
return (ret);
}
/*ARGSUSED*/
static int
{
int ret;
return (EINVAL);
return (ret);
}
/*ARGSUSED*/
static int
{
int ret;
return (EINVAL);
(int *)&qp_attr_mask)) != 0) {
return (EINVAL);
}
ret, qp_attr_mask);
}
#ifdef _LP64
sizeof (uverbs_qp_attr))) {
#else
sizeof (uverbs_qp_attr))) {
#endif
"failed");
return (EFAULT);
}
return (0);
}
static int
{
minor);
while (filep->file_pending_evt_cnt == 0) {
"get_event: No events, nonblocking");
return (EAGAIN);
}
"get_event: Got Sig");
return (EINTR);
}
}
#ifdef _LP64
if (copyout((void *)user_evt_resp,
sizeof (sol_ucma_event_resp_t))) {
#else
if (copyout((void *)user_evt_resp,
sizeof (sol_ucma_event_resp_t))) {
#endif
"failed");
return (EFAULT);
}
if (queued_evt->event_mcast)
if (evt_chanp) {
/*
* If the event is RDMA_CM_EVENT_CONNECT_RESPONSE or
* RDMA_CM_EVENT_ESTABLISHED and the CM ID is for RC,
* enable completion notifications for the QP.
*/
int rc;
rc = (*uverbs_uqpn_cq_ctrl_fp)(
if (rc) {
"uverbs_uqpn_cq_ctrl_fp(%X) "
"failed!!",
return (EIO);
}
}
}
/* Bump up backlog for CONNECT_REQUEST events */
}
return (0);
}
/*
* This is used when ULP wants to set the QOS option. This is *not*
* supported by Solaris IB stack, return failure.
*/
/*ARGSUSED*/
static int
{
return (EINVAL);
}
/*
* This is used when ULP uses librdmacm but uses out of band connection for CM.
*/
/*ARGSUSED*/
static int
{
int ret;
return (EINVAL);
if (ret)
else
return (ret);
}
/*ARGSUSED*/
static int
{
int rc;
return (EINVAL);
sizeof (struct sockaddr));
return (ENOMEM);
}
if (rc) {
"join_mcast: rdma_join_multicast ret %x", rc);
(void) sol_ofs_uobj_remove(&ucma_mcast_uo_tbl,
&mcastp->mcast_uobj);
return (rc);
}
#ifdef _LP64
sizeof (sol_ucma_create_id_resp_t))) {
#else
sizeof (sol_ucma_create_id_resp_t))) {
#endif
"failed");
(void) sol_ofs_uobj_remove(&ucma_mcast_uo_tbl,
&mcastp->mcast_uobj);
return (EFAULT);
}
return (0);
}
/*ARGSUSED*/
static int
{
ucma_id);
"ID %x", ucma_id);
return (EINVAL);
}
#ifdef _LP64
sizeof (sol_ucma_destroy_id_resp_t))) {
#else
sizeof (sol_ucma_destroy_id_resp_t))) {
#endif
"fault");
return (EFAULT);
}
return (0);
}
/*ARGSUSED*/
static int
{
int ret;
return (EINVAL);
/*
* For a TCP CMID, which has got the DISCONNECT event, call
* ibt_flush_qp(), to transition QP to error state.
*/
} else
return (ret);
}
/*
* RDMA ID Event handler
*/
int
{
if (!chan) {
"after destroy - %p", idp);
return (0);
}
if (!file) {
"after file destroy - idp %p", idp);
return (0);
}
"after file close - idp %p", idp);
return (0);
}
/*
* If the event is RDMA_CM_EVENT_CONNECT_REQUEST, allocate a
* new chan. The rdma_cm_id for this chan has already been
* allocated by sol_ofs.
*/
if (!chan->chan_backlog) {
"backlog exceeded");
return (-1);
}
chan->chan_backlog--;
"evt hdlr: No free Channel");
return (-1);
}
void *qphdl;
/*
* Connection has been rejected or disconnected,
* Enable uverbs to free QP, if it had been disabled
* before. sol_uverbs will free the QP appropriately.
*/
}
else
} else {
}
return (0);
}
/*
* Local Functions
*/
static sol_ucma_file_t *
{
return (NULL);
}
MUTEX_DRIVER, NULL);
NULL);
NULL);
KM_SLEEP);
return (new_file);
}
static sol_ucma_chan_t *
{
return (NULL);
}
return (new_chanp);
}
static void
{
if (delete_list) {
}
}
static int
{
if (filep)
if (chanp)
ucma_id);
if (flag_err)
else
return (-1);
}
if (filep)
if (chanp)
return (0);
}
static void
struct ib_user_path_rec *usr_path)
{
}
static void
{
int i;
}
sizeof (struct sockaddr_in6));
sizeof (struct sockaddr_in6));
}
}
static void
struct rdma_conn_param *conn_paramp)
{
}
static void
struct rdma_ucm_conn_param *usr_conn_paramp)
{
if (conn_paramp->private_data)
}
static void
{
if (ud_paramp->private_data)
}
static void
{
}
static void
{
}