/*
* 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.
*/
/*
* 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 */
#include <sys/types.h>
#include <sys/modctl.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/file.h>
#include <sys/errno.h>
#include <sys/open.h>
#include <sys/cred.h>
#include <sys/stat.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/conf.h>
#include <sys/uio.h>
#include <sys/sunldi.h>
#include <sys/modctl.h>
/* Common header files */
#include <sys/ib/clients/of/sol_ofs/sol_ofs_common.h>
#include <sys/ib/clients/of/sol_uverbs/sol_uverbs2ucma.h>
#include <sys/ib/clients/of/ofed_kernel.h>
/* Kernel Headers for User rdma_cm API */
#include <sys/ib/clients/of/rdma/ib_addr.h>
#include <sys/ib/clients/of/rdma/rdma_user_cm.h>
/* Kernel rdma_cm API */
#include <sys/ib/clients/of/rdma/rdma_cm.h>
/* sol_ucma internal Header files */
#include <sys/ib/clients/of/sol_ucma/sol_ucma.h>
/* entry point function prototype declarations */
static int sol_ucma_attach(dev_info_t *, ddi_attach_cmd_t);
static int sol_ucma_detach(dev_info_t *, ddi_detach_cmd_t);
static int sol_ucma_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int sol_ucma_open(dev_t *, int, int, cred_t *);
static int sol_ucma_close(dev_t, int, int, cred_t *);
static int sol_ucma_write(dev_t, struct uio *, cred_t *);
static int sol_ucma_poll(dev_t, short, int, short *, struct pollhead **);
/* Driver entry points */
static struct cb_ops sol_ucma_cb_ops = {
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 */
D_NEW | D_MP | D_64BIT, /* flags */
CB_REV /* rev */
};
/* Driver operations */
static struct dev_ops sol_ucma_dev_ops = {
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 */
static struct modldrv sol_ucma_modldrv = {
&mod_driverops,
"Solaris User RDMACM driver",
&sol_ucma_dev_ops
};
/* Module Linkage */
static struct modlinkage sol_ucma_modlinkage = {
MODREV_1,
&sol_ucma_modldrv,
NULL,
};
static char *sol_ucma_dbg_str = "sol_ucma";
sol_ofs_uobj_table_t ucma_file_uo_tbl;
sol_ofs_uobj_table_t ucma_ctx_uo_tbl;
sol_ofs_uobj_table_t ucma_mcast_uo_tbl;
/* Function pointers for uverbs functions */
static uverbs_get_clnt_hdl_t uverbs_get_hdl_fp = NULL;
static uverbs_qpnum2qphdl_t uverbs_qpnum2qphdl_fp = NULL;
static uverbs_disable_uqpn_mod_t uverbs_disable_uqpn_modify_fp = NULL;
static uverbs_uqpn_cq_ctrl_t uverbs_uqpn_cq_ctrl_fp = NULL;
static uverbs_set_qp_free_state_t uverbs_set_qp_free_state_fp = NULL;
static uverbs_flush_qp_t uverbs_flush_qp_fp = NULL;
/* Global Variables */
sol_ucma_t sol_ucma;
/* RDMACM Functions */
static int sol_ucma_create_id(dev_t, void *, struct uio *);
static int sol_ucma_destroy_id(dev_t, void *, struct uio *);
static int sol_ucma_bind_addr(dev_t, void *, struct uio *);
static int sol_ucma_resolve_addr(dev_t, void *, struct uio *);
static int sol_ucma_resolve_route(dev_t, void *, struct uio *);
static int sol_ucma_query_route(dev_t, void *, struct uio *);
static int sol_ucma_connect(dev_t, void *, struct uio *);
static int sol_ucma_listen(dev_t, void *, struct uio *);
static int sol_ucma_accept(dev_t, void *, struct uio *);
static int sol_ucma_reject(dev_t, void *, struct uio *);
static int sol_ucma_disconnect(dev_t, void *, struct uio *);
static int sol_ucma_init_qp_attr(dev_t, void *, struct uio *);
static int sol_ucma_get_event(dev_t, void *, struct uio *);
static int sol_ucma_set_option(dev_t, void *, struct uio *);
static int sol_ucma_notify(dev_t, void *, struct uio *);
static int sol_ucma_join_mcast(dev_t, void *, struct uio *);
static int sol_ucma_leave_mcast(dev_t, void *, struct uio *);
/*
* Event callback from sol_cma
*/
int sol_ucma_evt_hdlr(struct rdma_cm_id *, struct rdma_cm_event *);
/*
* Internal functions.
*/
static sol_ucma_file_t *
ucma_alloc_file(minor_t *);
static sol_ucma_chan_t *
ucma_alloc_chan(sol_ucma_file_t *, sol_ucma_create_id_t *);
static void
ucma_free_chan(sol_ucma_chan_t *, int);
static int
get_file_chan(uint32_t, sol_ucma_file_t **, sol_ucma_chan_t **, char *, int);
static void
rdma2usr_route(struct rdma_cm_id *, sol_ucma_query_route_resp_t *);
static void
usr2rdma_conn_param(struct rdma_ucm_conn_param *, struct rdma_conn_param *);
static void
rdma2usr_conn_param(struct rdma_conn_param *, struct rdma_ucm_conn_param *);
static void
rdma2usr_ud_param(struct rdma_ud_param *, sol_ucma_ud_param_t *);
static void sol_ucma_user_objs_init();
static void sol_ucma_user_objs_fini();
int
_init(void)
{
int error;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "_init()");
sol_ucma_user_objs_init();
mutex_init(&sol_ucma.ucma_mutex, NULL, MUTEX_DRIVER, NULL);
cv_init(&sol_ucma.ucma_open_cv, NULL, CV_DRIVER, NULL);
if ((error = ldi_ident_from_mod(&sol_ucma_modlinkage,
&sol_ucma.ucma_ldi_ident)) != 0) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"ldi_ident_from_mod() failed");
mutex_destroy(&sol_ucma.ucma_mutex);
cv_destroy(&sol_ucma.ucma_open_cv);
sol_ucma_user_objs_fini();
return (error);
}
sol_ucma.ucma_clnt_hdl_flag = SOL_UCMA_CLNT_HDL_UNINITIALIZED;
error = mod_install(&sol_ucma_modlinkage);
if (error) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str, "mod_install() failed");
ldi_ident_release(sol_ucma.ucma_ldi_ident);
mutex_destroy(&sol_ucma.ucma_mutex);
cv_destroy(&sol_ucma.ucma_open_cv);
sol_ucma_user_objs_fini();
return (error);
}
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "_init(): ret");
return (error);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&sol_ucma_modlinkage, modinfop));
}
int
_fini(void)
{
int ret;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "_fini()");
if ((ret = mod_remove(&sol_ucma_modlinkage)) != 0) {
SOL_OFS_DPRINTF_L3(sol_ucma_dbg_str,
"sol_ucma, _fini : mod_remove failed");
return (ret);
}
ldi_ident_release(sol_ucma.ucma_ldi_ident);
mutex_destroy(&sol_ucma.ucma_mutex);
cv_destroy(&sol_ucma.ucma_open_cv);
sol_ucma_user_objs_fini();
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "_fini(): ret");
return (DDI_SUCCESS);
}
static int
sol_ucma_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int rval;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "attach(%p, %x)", dip, cmd);
switch (cmd) {
case DDI_ATTACH:
mutex_enter(&sol_ucma.ucma_mutex);
if (sol_ucma.ucma_dip != NULL) {
mutex_exit(&sol_ucma.ucma_mutex);
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"attach: failed, > 1 instance");
return (DDI_FAILURE);
}
sol_ucma.ucma_dip = dip;
mutex_exit(&sol_ucma.ucma_mutex);
rval = ddi_create_minor_node(dip, "sol_ucma", S_IFCHR,
0, DDI_PSEUDO, 0);
if (rval != DDI_SUCCESS) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"attach: ddi_create_minor_node failed");
mutex_enter(&sol_ucma.ucma_mutex);
sol_ucma.ucma_dip = NULL;
mutex_exit(&sol_ucma.ucma_mutex);
return (DDI_FAILURE);
}
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str,
"attach : DDI_ATTACH success");
return (DDI_SUCCESS);
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
sol_ucma_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "detach(%p, %x)", dip, cmd);
switch (cmd) {
case DDI_DETACH:
mutex_enter(&sol_ucma.ucma_mutex);
if (sol_ucma.ucma_num_file) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"detach : %x files not closed",
sol_ucma.ucma_num_file);
mutex_exit(&sol_ucma.ucma_mutex);
return (DDI_FAILURE);
}
sol_ucma.ucma_dip = NULL;
mutex_exit(&sol_ucma.ucma_mutex);
ddi_remove_minor_node(dip, "sol_ucma");
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str,
"detach : DDI_DETACH success");
return (DDI_SUCCESS);
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/*ARGSUSED*/
static int
sol_ucma_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
void **resultp)
{
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
*resultp = (void *)sol_ucma.ucma_dip;
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
*resultp = (void *)0;
return (DDI_SUCCESS);
default :
return (DDI_FAILURE);
}
}
static int
sol_ucma_open(dev_t *devp, int flag, int otype, cred_t *credp)
{
sol_ucma_file_t *new_filep;
minor_t new_minor;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "open(%p, %x, %x, %p)",
devp, flag, otype, credp);
new_filep = ucma_alloc_file(&new_minor);
if (new_filep == NULL)
return (EAGAIN);
SOL_OFS_DPRINTF_L4(sol_ucma_dbg_str, "sol_ucma new minor %x",
new_minor);
/*
* 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
*/
mutex_enter(&sol_ucma.ucma_mutex);
if (sol_ucma.ucma_clnt_hdl_flag == SOL_UCMA_CLNT_HDL_UNINITIALIZED) {
int rval, ret_errno;
sol_ucma.ucma_clnt_hdl_flag =
SOL_UCMA_CLNT_HDL_INITIALIZING;
if ((rval = ldi_open_by_name(SOL_UCMA_UVERBS_PATH,
FREAD | FWRITE, kcred, &sol_ucma.ucma_ldi_hdl,
sol_ucma.ucma_ldi_ident)) != 0) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"ldi_open_by_name(%s, ...) failed with rval %x",
SOL_UCMA_UVERBS_PATH, rval);
sol_ofs_uobj_free(&new_filep->file_uobj);
sol_ucma.ucma_clnt_hdl_flag =
SOL_UCMA_CLNT_HDL_UNINITIALIZED;
mutex_exit(&sol_ucma.ucma_mutex);
return (ENODEV);
}
if ((sol_ucma.ucma_mod_hdl = ddi_modopen("drv/sol_uverbs",
KRTLD_MODE_FIRST, &ret_errno)) == NULL) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"ddi_modopen(%s, ...) failed", "drv/sol_uverbs");
(void) ldi_close(sol_ucma.ucma_ldi_hdl,
FREAD | FWRITE, kcred);
sol_ofs_uobj_free(&new_filep->file_uobj);
sol_ucma.ucma_clnt_hdl_flag =
SOL_UCMA_CLNT_HDL_UNINITIALIZED;
mutex_exit(&sol_ucma.ucma_mutex);
return (ret_errno);
}
if ((uverbs_get_hdl_fp = (uverbs_get_clnt_hdl_t)ddi_modsym(
sol_ucma.ucma_mod_hdl, SOL_UVERBS_GET_CLNT_HDL, &ret_errno))
== NULL) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"ddi_modsym(%s, ...) failed",
SOL_UVERBS_GET_CLNT_HDL);
(void) ddi_modclose(sol_ucma.ucma_mod_hdl);
(void) ldi_close(sol_ucma.ucma_ldi_hdl,
FREAD | FWRITE, kcred);
sol_ofs_uobj_free(&new_filep->file_uobj);
sol_ucma.ucma_clnt_hdl_flag =
SOL_UCMA_CLNT_HDL_UNINITIALIZED;
mutex_exit(&sol_ucma.ucma_mutex);
return (ret_errno);
}
if ((uverbs_qpnum2qphdl_fp = (uverbs_qpnum2qphdl_t)ddi_modsym(
sol_ucma.ucma_mod_hdl, SOL_UVERBS_QPNUM2QPHDL, &ret_errno))
== NULL) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"ddi_modsym(%s, ...) failed",
SOL_UVERBS_QPNUM2QPHDL);
(void) ddi_modclose(sol_ucma.ucma_mod_hdl);
(void) ldi_close(sol_ucma.ucma_ldi_hdl,
FREAD | FWRITE, kcred);
sol_ofs_uobj_free(&new_filep->file_uobj);
sol_ucma.ucma_clnt_hdl_flag =
SOL_UCMA_CLNT_HDL_UNINITIALIZED;
mutex_exit(&sol_ucma.ucma_mutex);
return (ret_errno);
}
if ((uverbs_disable_uqpn_modify_fp =
(uverbs_disable_uqpn_mod_t)ddi_modsym(
sol_ucma.ucma_mod_hdl, SOL_UVERBS_DISABLE_UQPN_MODIFY,
&ret_errno)) == NULL) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"ddi_modsym(%s, ...) failed",
SOL_UVERBS_DISABLE_UQPN_MODIFY);
(void) ddi_modclose(sol_ucma.ucma_mod_hdl);
(void) ldi_close(sol_ucma.ucma_ldi_hdl,
FREAD | FWRITE, kcred);
sol_ofs_uobj_free(&new_filep->file_uobj);
sol_ucma.ucma_clnt_hdl_flag =
SOL_UCMA_CLNT_HDL_UNINITIALIZED;
mutex_exit(&sol_ucma.ucma_mutex);
return (ret_errno);
}
if ((uverbs_uqpn_cq_ctrl_fp =
(uverbs_uqpn_cq_ctrl_t)ddi_modsym(
sol_ucma.ucma_mod_hdl, SOL_UVERBS_UQPN_CQ_CTRL,
&ret_errno)) == NULL) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"ddi_modsym(%s, ...) failed",
SOL_UVERBS_UQPN_CQ_CTRL);
(void) ddi_modclose(sol_ucma.ucma_mod_hdl);
(void) ldi_close(sol_ucma.ucma_ldi_hdl,
FREAD | FWRITE, kcred);
sol_ofs_uobj_free(&new_filep->file_uobj);
sol_ucma.ucma_clnt_hdl_flag =
SOL_UCMA_CLNT_HDL_UNINITIALIZED;
mutex_exit(&sol_ucma.ucma_mutex);
return (ret_errno);
}
if ((uverbs_set_qp_free_state_fp =
(uverbs_set_qp_free_state_t)ddi_modsym(
sol_ucma.ucma_mod_hdl, SOL_UVERBS_SET_QPFREE_STATE,
&ret_errno)) == NULL) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"ddi_modsym(%s, ...) failed",
SOL_UVERBS_SET_QPFREE_STATE);
(void) ddi_modclose(sol_ucma.ucma_mod_hdl);
(void) ldi_close(sol_ucma.ucma_ldi_hdl,
FREAD | FWRITE, kcred);
sol_ofs_uobj_free(&new_filep->file_uobj);
sol_ucma.ucma_clnt_hdl_flag =
SOL_UCMA_CLNT_HDL_UNINITIALIZED;
mutex_exit(&sol_ucma.ucma_mutex);
return (ret_errno);
}
if ((uverbs_flush_qp_fp =
(uverbs_flush_qp_t)ddi_modsym(
sol_ucma.ucma_mod_hdl, SOL_UVERBS_FLUSH_QP,
&ret_errno)) == NULL) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"ddi_modsym(%s, ...) failed",
SOL_UVERBS_FLUSH_QP);
(void) ddi_modclose(sol_ucma.ucma_mod_hdl);
(void) ldi_close(sol_ucma.ucma_ldi_hdl,
FREAD | FWRITE, kcred);
sol_ofs_uobj_free(&new_filep->file_uobj);
sol_ucma.ucma_clnt_hdl_flag =
SOL_UCMA_CLNT_HDL_UNINITIALIZED;
mutex_exit(&sol_ucma.ucma_mutex);
return (ret_errno);
}
(*uverbs_get_hdl_fp) (&sol_ucma.ucma_ib_clnt_hdl,
&sol_ucma.ucma_iw_clnt_hdl);
if (sol_ucma.ucma_ib_clnt_hdl == NULL &&
sol_ucma.ucma_iw_clnt_hdl == NULL) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"uverbs_get_clnt_hdl failed");
(void) ddi_modclose(sol_ucma.ucma_mod_hdl);
(void) ldi_close(sol_ucma.ucma_ldi_hdl,
FREAD | FWRITE, kcred);
sol_ofs_uobj_free(&new_filep->file_uobj);
sol_ucma.ucma_clnt_hdl_flag =
SOL_UCMA_CLNT_HDL_UNINITIALIZED;
mutex_exit(&sol_ucma.ucma_mutex);
return (ENODEV);
}
sol_ucma.ucma_clnt_hdl_flag =
SOL_UCMA_CLNT_HDL_INITIALIZED;
cv_broadcast(&sol_ucma.ucma_open_cv);
} else if (sol_ucma.ucma_clnt_hdl_flag ==
SOL_UCMA_CLNT_HDL_INITIALIZING) {
cv_wait(&sol_ucma.ucma_open_cv, &sol_ucma.ucma_mutex);
}
mutex_exit(&sol_ucma.ucma_mutex);
*devp = makedevice(getmajor(*devp), new_minor);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "open Success");
return (0);
}
static int
sol_ucma_close(dev_t dev, int flag, int otype, cred_t *credp)
{
minor_t minor;
sol_ucma_file_t *filep;
genlist_entry_t *entry;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "close(%x, %x, %x, %p)",
dev, flag, otype, credp);
minor = getminor(dev);
filep = (sol_ucma_file_t *)sol_ofs_uobj_get_read(
&ucma_file_uo_tbl, minor);
if (!filep) {
SOL_OFS_DPRINTF_L4(sol_ucma_dbg_str, "close, no dev_t %x",
dev);
return (0);
}
/* Disable further event handling for this CM event channel */
mutex_enter(&filep->file_mutex);
if (filep->file_evt_close_flag == SOL_UCMA_EVT_PROGRESS) {
cv_wait(&filep->file_evt_close_cv, &filep->file_mutex);
}
filep->file_evt_close_flag = SOL_UCMA_EVT_DISABLED;
mutex_exit(&filep->file_mutex);
/*
* 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,
*/
entry = remove_genlist_head(&filep->file_id_list);
while (entry) {
sol_ucma_chan_t *chanp;
void *qphdl;
chanp = (sol_ucma_chan_t *)entry->data;
mutex_enter(&chanp->chan_mutex);
if (chanp->chan_rdma_id)
(chanp->chan_rdma_id)->context = NULL;
mutex_exit(&chanp->chan_mutex);
rdma_destroy_id(chanp->chan_rdma_id);
mutex_enter(&chanp->chan_mutex);
qphdl = chanp->chan_qp_hdl;
chanp->chan_qp_hdl = NULL;
mutex_exit(&chanp->chan_mutex);
if (qphdl)
(*uverbs_set_qp_free_state_fp) (
SOL_UVERBS2UCMA_ENABLE_QP_FREE, 0, qphdl);
ucma_free_chan(chanp, 1);
entry = remove_genlist_head(&filep->file_id_list);
}
/* Flush out any events that have not been acknowledged. */
mutex_enter(&filep->file_mutex);
if (filep->file_pending_evt_cnt) {
sol_ucma_event_t *evtp;
SOL_OFS_DPRINTF_L3(sol_ucma_dbg_str,
"close : %d Events not reported to userland",
filep->file_pending_evt_cnt);
entry = remove_genlist_head(&filep->file_evt_list);
while (entry) {
evtp = (sol_ucma_event_t *)entry->data;
kmem_free(evtp, sizeof (sol_ucma_event_t));
kmem_free(entry, sizeof (genlist_entry_t));
entry = remove_genlist_head(&filep->file_evt_list);
};
mutex_exit(&filep->file_mutex);
}
/*
* 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
*/
mutex_enter(&sol_ucma.ucma_mutex);
if (sol_ucma.ucma_num_file == 1) {
sol_ucma.ucma_clnt_hdl_flag =
SOL_UCMA_CLNT_HDL_UNINITIALIZED;
uverbs_get_hdl_fp = NULL;
uverbs_qpnum2qphdl_fp = NULL;
uverbs_disable_uqpn_modify_fp = NULL;
uverbs_uqpn_cq_ctrl_fp = NULL;
uverbs_uqpn_cq_ctrl_fp = NULL;
uverbs_set_qp_free_state_fp = NULL;
uverbs_flush_qp_fp = NULL;
sol_ucma.ucma_ib_clnt_hdl = NULL;
sol_ucma.ucma_iw_clnt_hdl = NULL;
(void) ddi_modclose(sol_ucma.ucma_mod_hdl);
(void) ldi_close(sol_ucma.ucma_ldi_hdl,
FREAD | FWRITE, kcred);
}
sol_ucma.ucma_num_file--;
mutex_exit(&sol_ucma.ucma_mutex);
kmem_free(filep->file_pollhead, sizeof (struct pollhead));
sol_ofs_uobj_put(&filep->file_uobj);
mutex_destroy(&filep->file_mutex);
cv_destroy(&filep->file_evt_cv);
cv_destroy(&filep->file_evt_close_cv);
rw_enter(&(filep->file_uobj.uo_lock), RW_WRITER);
(void) sol_ofs_uobj_remove(&ucma_file_uo_tbl, &(filep->file_uobj));
rw_exit(&(filep->file_uobj.uo_lock));
sol_ofs_uobj_free(&(filep->file_uobj));
return (0);
}
typedef struct sol_ucma_cmd_table_s {
int (*sol_ucma_cmd_fnc) (dev_t, void *, struct uio *);
uint16_t sol_ucma_in_len;
uint16_t sol_ucma_out_len;
} sol_ucma_cmd_table_t;
static sol_ucma_cmd_table_t sol_ucma_cmd_table[] = {
[RDMA_USER_CM_CMD_CREATE_ID] = sol_ucma_create_id,
sizeof (sol_ucma_create_id_t),
sizeof (sol_ucma_create_id_resp_t),
[RDMA_USER_CM_CMD_DESTROY_ID] = sol_ucma_destroy_id,
sizeof (sol_ucma_destroy_id_t),
sizeof (sol_ucma_destroy_id_resp_t),
[RDMA_USER_CM_CMD_BIND_ADDR] = sol_ucma_bind_addr,
sizeof (sol_ucma_bind_addr_t),
0,
[RDMA_USER_CM_CMD_RESOLVE_ADDR] = sol_ucma_resolve_addr,
sizeof (sol_ucma_resolve_addr_t),
0,
[RDMA_USER_CM_CMD_RESOLVE_ROUTE] = sol_ucma_resolve_route,
sizeof (sol_ucma_resolve_route_t),
0,
[RDMA_USER_CM_CMD_QUERY_ROUTE] = sol_ucma_query_route,
sizeof (sol_ucma_query_route_t),
sizeof (sol_ucma_query_route_resp_t),
[RDMA_USER_CM_CMD_CONNECT] = sol_ucma_connect,
sizeof (sol_ucma_connect_t),
0,
[RDMA_USER_CM_CMD_LISTEN] = sol_ucma_listen,
sizeof (sol_ucma_listen_t),
0,
[RDMA_USER_CM_CMD_ACCEPT] = sol_ucma_accept,
sizeof (sol_ucma_accept_t),
0,
[RDMA_USER_CM_CMD_REJECT] = sol_ucma_reject,
sizeof (sol_ucma_reject_t),
0,
[RDMA_USER_CM_CMD_DISCONNECT] = sol_ucma_disconnect,
sizeof (sol_ucma_disconnect_t),
0,
[RDMA_USER_CM_CMD_INIT_QP_ATTR] = sol_ucma_init_qp_attr,
sizeof (sol_ucma_init_qp_attr_t),
sizeof (struct ib_uverbs_qp_attr),
[RDMA_USER_CM_CMD_GET_EVENT] = sol_ucma_get_event,
sizeof (sol_ucma_get_event_t),
sizeof (sol_ucma_event_resp_t),
[RDMA_USER_CM_CMD_GET_OPTION] = NULL,
0,
0,
[RDMA_USER_CM_CMD_SET_OPTION] = sol_ucma_set_option,
sizeof (sol_ucma_set_option_t),
0,
[RDMA_USER_CM_CMD_NOTIFY] = sol_ucma_notify,
sizeof (sol_ucma_notify_t),
0,
[RDMA_USER_CM_CMD_JOIN_MCAST] = sol_ucma_join_mcast,
sizeof (sol_ucma_join_mcast_t),
sizeof (sol_ucma_create_id_resp_t),
[RDMA_USER_CM_CMD_LEAVE_MCAST] = sol_ucma_leave_mcast,
sizeof (sol_ucma_destroy_id_t),
sizeof (sol_ucma_destroy_id_resp_t)
};
#define SOL_UCMA_MAX_CMD_DATA 512
static int
sol_ucma_write(dev_t dev, struct uio *uio, cred_t *credp)
{
sol_ucma_cmd_hdr_t *user_hdrp;
int ret;
void *data_buf = NULL;
char uio_data[SOL_UCMA_MAX_CMD_DATA];
size_t uio_data_len = uio->uio_resid;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "write(%x, %p, %p)",
dev, uio, credp);
ret = uiomove((caddr_t)&uio_data, uio_data_len, UIO_WRITE, uio);
user_hdrp = (sol_ucma_cmd_hdr_t *)uio_data;
if (ret != 0) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str, "write: uiomove failed");
return (ret);
}
if (user_hdrp->cmd >=
sizeof (sol_ucma_cmd_table) / sizeof (sol_ucma_cmd_table_t)) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"open : cmd out of bound 0x%x", user_hdrp->cmd);
return (EINVAL);
}
if (!(sol_ucma_cmd_table[user_hdrp->cmd].sol_ucma_cmd_fnc)) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"open : Unsupported cmd 0x%x", user_hdrp->cmd);
return (EINVAL);
}
/*
* Check the user passed IN-OUT buffer length, with expected lengths
*/
if (sol_ucma_cmd_table[user_hdrp->cmd].sol_ucma_in_len !=
(user_hdrp->in)) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"write : Invalid Input length cmd %x, in %x expected %x",
user_hdrp->cmd, user_hdrp->in,
sol_ucma_cmd_table[user_hdrp->cmd].sol_ucma_in_len);
return (EINVAL);
}
if (sol_ucma_cmd_table[user_hdrp->cmd].sol_ucma_out_len !=
(user_hdrp->out)) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"write : Invalid Output length cmd %x, in %x expected %x",
user_hdrp->cmd, user_hdrp->out,
sol_ucma_cmd_table[user_hdrp->cmd].sol_ucma_out_len);
return (EINVAL);
}
if (user_hdrp->in) {
data_buf = (void *)((char *)uio_data +
sizeof (sol_ucma_cmd_hdr_t));
}
ret = (sol_ucma_cmd_table[user_hdrp->cmd].sol_ucma_cmd_fnc)
(dev, data_buf, uio);
/* If the command fails, set back the uio_resid */
if (ret)
uio->uio_resid += uio_data_len;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "write : ret %x", ret);
return (ret);
}
static int
sol_ucma_poll(dev_t dev, short events, int anyyet,
short *reventsp, struct pollhead **phpp)
{
minor_t minor = getminor(dev);
sol_ucma_file_t *filep;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "poll(%x, %x)",
dev, events);
if (!(events & (POLLIN | POLLRDNORM)))
return (EINVAL);
filep = (sol_ucma_file_t *)sol_ofs_uobj_get_read(
&ucma_file_uo_tbl, minor);
ASSERT(filep);
if (filep->file_pending_evt_cnt) {
*reventsp = POLLIN | POLLRDNORM;
} else {
*reventsp = 0;
if (!anyyet)
*phpp = filep->file_pollhead;
}
sol_ofs_uobj_put(&filep->file_uobj);
return (0);
}
/*
* RDMACM functions.
*/
/*ARGSUSED*/
static int
sol_ucma_create_id(dev_t dev, void *io_buf, struct uio *uio)
{
minor_t minor = getminor(dev);
sol_ucma_file_t *filep;
sol_ucma_chan_t *chanp;
sol_ucma_create_id_t *ucma_id_inp;
sol_ucma_create_id_resp_t ucma_id_resp;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "create_id(%x, %p), minor %x",
dev, io_buf, minor);
ucma_id_inp = (sol_ucma_create_id_t *)io_buf;
ASSERT(ucma_id_inp);
ASSERT(ucma_id_inp->response.r_laddr);
filep = (sol_ucma_file_t *)sol_ofs_uobj_get_read(&ucma_file_uo_tbl,
minor);
ASSERT(filep);
chanp = ucma_alloc_chan(filep, ucma_id_inp);
if (chanp == NULL) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"create_id: No free Channel");
sol_ofs_uobj_put(&filep->file_uobj);
return (ENODEV);
}
ucma_id_resp.id = chanp->chan_id;
#ifdef _LP64
if (copyout(&ucma_id_resp, (void *)(ucma_id_inp->response.r_laddr),
sizeof (sol_ucma_create_id_resp_t))) {
#else
if (copyout(&ucma_id_resp, (void *)(ucma_id_inp->response.r_addr),
sizeof (sol_ucma_create_id_resp_t))) {
#endif
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"create_id: copyout fault");
ucma_free_chan(chanp, 1);
sol_ofs_uobj_put(&filep->file_uobj);
return (EFAULT);
}
/* */
chanp->chan_rdma_id = rdma_create_id(sol_ucma_evt_hdlr,
chanp, ucma_id_inp->ps);
if (chanp->chan_rdma_id == NULL) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"create_id: rdma_create_id failed");
ucma_free_chan(chanp, 1);
sol_ofs_uobj_put(&filep->file_uobj);
return (EINVAL);
}
mutex_enter(&chanp->chan_mutex);
(chanp->chan_rdma_id)->context = chanp;
mutex_exit(&chanp->chan_mutex);
rdma_map_id2clnthdl(chanp->chan_rdma_id, sol_ucma.ucma_ib_clnt_hdl,
sol_ucma.ucma_iw_clnt_hdl);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "create_id: Return SUCCESS");
sol_ofs_uobj_put(&filep->file_uobj);
return (0);
}
/*ARGSUSED*/
static int
sol_ucma_destroy_id(dev_t dev, void *io_buf, struct uio *uio)
{
sol_ucma_chan_t *chanp;
uint32_t ucma_id;
sol_ucma_file_t *filep;
sol_ucma_destroy_id_t *id_inp;
minor_t minor;
genlist_entry_t *entry;
sol_ucma_destroy_id_resp_t id_resp;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "destroy_id(%x, %p)",
dev, io_buf);
id_inp = (sol_ucma_destroy_id_t *)io_buf;
ucma_id = id_inp->id;
if (!get_file_chan(ucma_id, &filep, &chanp, "destroy_id", 0)) {
minor = getminor(dev);
filep = (sol_ucma_file_t *)sol_ofs_uobj_get_read(
&ucma_file_uo_tbl, minor);
if (!filep) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"destroy_id : filep NULL");
return (EINVAL);
}
} else {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str, "destroy_id : "
"ucma_id %x invalid", ucma_id);
return (0);
}
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "destroy_id: chanp %p", chanp);
/*
* Event handling, Flush out events pending
* return the number of events that were acked. Free events not acked.
*/
ASSERT(filep);
mutex_enter(&filep->file_mutex);
if (filep->file_pending_evt_cnt != 0) {
SOL_OFS_DPRINTF_L4(sol_ucma_dbg_str,
"destroy_id: pending events");
entry = remove_genlist_head(&filep->file_evt_list);
while (entry) {
kmem_free((void *) (entry->data),
sizeof (sol_ucma_event_t));
kmem_free(entry, sizeof (genlist_entry_t));
entry = remove_genlist_head(&filep->file_evt_list);
};
filep->file_pending_evt_cnt = 0;
}
if (chanp) {
mutex_enter(&chanp->chan_mutex);
id_resp.events_reported = chanp->chan_evt_cnt;
mutex_exit(&chanp->chan_mutex);
} else {
id_resp.events_reported = 0;
}
mutex_exit(&filep->file_mutex);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "destroy_id : chanp %p, "
"evts %x", chanp, id_resp.events_reported);
#ifdef _LP64
if (copyout(&id_resp, (void *) (id_inp->response.r_laddr),
sizeof (sol_ucma_destroy_id_resp_t))) {
#else
if (copyout(&id_resp, (void *) (id_inp->response.r_addr),
sizeof (sol_ucma_destroy_id_resp_t))) {
#endif
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"destroy_id: copyout fault");
sol_ofs_uobj_put(&filep->file_uobj);
return (EFAULT);
}
/* */
if (chanp) {
mutex_enter(&chanp->chan_mutex);
if (chanp->chan_rdma_id)
(chanp->chan_rdma_id)->context = NULL;
mutex_exit(&chanp->chan_mutex);
rdma_destroy_id(chanp->chan_rdma_id);
ucma_free_chan(chanp, 1);
}
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "destroy_id: Success");
sol_ofs_uobj_put(&filep->file_uobj);
return (0);
}
/*ARGSUSED*/
static int
sol_ucma_bind_addr(dev_t dev, void *io_buf, struct uio *uio)
{
int ret;
sol_ucma_chan_t *chanp;
uint32_t ucma_id;
sol_ucma_bind_addr_t *bind_addrp;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "bind_addr(%x, %p)",
dev, io_buf);
bind_addrp = (sol_ucma_bind_addr_t *)io_buf;
ucma_id = bind_addrp->id;
if (get_file_chan(ucma_id, NULL, &chanp, "bind_addr", 1))
return (EINVAL);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "bind_addr - chanp %p", chanp);
ret = rdma_bind_addr(chanp->chan_rdma_id,
(struct sockaddr *)&bind_addrp->addr);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "bind_addr: ret %x", ret);
return (ret);
}
/*ARGSUSED*/
static int
sol_ucma_resolve_addr(dev_t dev, void *io_buf, struct uio *uio)
{
sol_ucma_chan_t *chanp;
uint32_t ucma_id;
int ret;
sol_ucma_resolve_addr_t *resolve_addrp;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "resolve_addr(%x, %p)",
dev, io_buf);
resolve_addrp = (sol_ucma_resolve_addr_t *)io_buf;
ucma_id = resolve_addrp->id;
if (get_file_chan(ucma_id, NULL, &chanp, "resolve_addr", 1)) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"resolve_addr: ucma_id %x invalid", ucma_id);
return (EINVAL);
}
ASSERT(chanp);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "resolve_addr - chanp %p", chanp);
ret = rdma_resolve_addr(chanp->chan_rdma_id,
(struct sockaddr *)&resolve_addrp->src_addr,
(struct sockaddr *)&resolve_addrp->dst_addr,
resolve_addrp->timeout_ms);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "resolve_addr: ret %x", ret);
return (ret);
}
/*ARGSUSED*/
static int
sol_ucma_resolve_route(dev_t dev, void *io_buf, struct uio *uio)
{
sol_ucma_chan_t *chanp;
uint32_t ucma_id;
int ret;
sol_ucma_resolve_route_t *resolve_routep;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str,
"resolve_route(%x, %p)", dev, io_buf);
resolve_routep = (sol_ucma_resolve_route_t *)io_buf;
ucma_id = resolve_routep->id;
if (get_file_chan(ucma_id, NULL, &chanp, "resolve_route", 1))
return (EINVAL);
ASSERT(chanp);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "resolve_route - chanp %p",
chanp);
ret = rdma_resolve_route(chanp->chan_rdma_id,
resolve_routep->timeout_ms);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "resolve_route: ret %x", ret);
return (ret);
}
/*ARGSUSED*/
static int
sol_ucma_query_route(dev_t dev, void *io_buf, struct uio *uio)
{
sol_ucma_chan_t *chanp;
uint32_t ucma_id;
struct rdma_cm_id *idp;
sol_ucma_query_route_t *query_routep;
sol_ucma_query_route_resp_t route_info;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "query_route(%x, %p)",
dev, io_buf);
query_routep = (sol_ucma_query_route_t *)io_buf;
ucma_id = query_routep->id;
if (get_file_chan(ucma_id, NULL, &chanp, "query_route", 1))
return (EINVAL);
ASSERT(chanp);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "query_route - chanp %p", chanp);
idp = chanp->chan_rdma_id;
bzero(&route_info, sizeof (route_info));
rdma2usr_route(idp, &route_info);
#ifdef _LP64
if (copyout(&route_info, (void *) (query_routep->response.r_laddr),
sizeof (sol_ucma_query_route_resp_t))) {
#else
if (copyout(&route_info, (void *) (query_routep->response.r_addr),
sizeof (sol_ucma_query_route_resp_t))) {
#endif
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"query_route: copyout fault");
return (EFAULT);
}
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "query_route: Succcess");
return (0);
}
/*ARGSUSED*/
static int
sol_ucma_connect(dev_t dev, void *io_buf, struct uio *uio)
{
sol_ucma_chan_t *chanp;
uint32_t ucma_id;
int ret;
void *qphdl;
sol_ucma_connect_t *connectp;
struct rdma_conn_param conn_param;
struct rdma_cm_id *idp;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "connect(%x, %p)",
dev, io_buf);
connectp = (sol_ucma_connect_t *)io_buf;
ucma_id = connectp->id;
if (get_file_chan(ucma_id, NULL, &chanp, "connect", 1))
return (EINVAL);
ASSERT(chanp);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "connect - chanp %p", chanp);
usr2rdma_conn_param(&(connectp->conn_param), &conn_param);
ASSERT(uverbs_qpnum2qphdl_fp);
ASSERT(uverbs_disable_uqpn_modify_fp);
ASSERT(uverbs_uqpn_cq_ctrl_fp);
qphdl = (*uverbs_qpnum2qphdl_fp) (conn_param.qp_num);
if (qphdl == NULL) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str, "connect: "
"invalid QPNum %x", conn_param.qp_num);
return (EINVAL);
}
(*uverbs_disable_uqpn_modify_fp) (conn_param.qp_num);
rdma_map_id2qphdl(chanp->chan_rdma_id, qphdl);
idp = chanp->chan_rdma_id;
if (idp->ps == RDMA_PS_TCP)
(void) (*uverbs_uqpn_cq_ctrl_fp) (conn_param.qp_num,
SOL_UVERBS2UCMA_CQ_NOTIFY_DISABLE);
chanp->chan_qp_num = conn_param.qp_num;
ret = rdma_connect(chanp->chan_rdma_id, &conn_param);
/*
* rdma_connect() initiated for this CMID, disable sol_uverbs to
* free the QP assosiated with this CM ID.
*/
if (ret == 0 && idp->ps == RDMA_PS_TCP) {
mutex_enter(&chanp->chan_mutex);
chanp->chan_qp_hdl = qphdl;
chanp->chan_flags |= SOL_UCMA_CHAN_CONNECT_FLAG;
mutex_exit(&chanp->chan_mutex);
(*uverbs_set_qp_free_state_fp) (
SOL_UVERBS2UCMA_DISABLE_QP_FREE, conn_param.qp_num,
NULL);
}
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "connect: ret %x", ret);
return (ret);
}
/*ARGSUSED*/
static int
sol_ucma_listen(dev_t dev, void *io_buf, struct uio *uio)
{
sol_ucma_chan_t *chanp;
uint32_t ucma_id;
int ret;
sol_ucma_listen_t *listenp;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "listen(%x, %p)",
dev, io_buf);
listenp = (sol_ucma_listen_t *)io_buf;
ucma_id = listenp->id;
if (get_file_chan(ucma_id, NULL, &chanp, "listen", 1))
return (EINVAL);
ASSERT(chanp);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "listen - chanp %p", chanp);
listenp->backlog = (listenp->backlog == 0 ||
listenp->backlog > SOL_UCMA_MAX_LISTEN) ?
SOL_UCMA_MAX_LISTEN : listenp->backlog;
chanp->chan_backlog = listenp->backlog;
ret = rdma_listen(chanp->chan_rdma_id, listenp->backlog);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "listen: ret %x", ret);
return (ret);
}
/*ARGSUSED*/
static int
sol_ucma_accept(dev_t dev, void *io_buf, struct uio *uio)
{
int ret;
uint32_t ucma_id;
sol_ucma_chan_t *chanp;
void *qphdl;
sol_ucma_accept_t *acpt;
struct rdma_conn_param conn_param;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "accept(%x, %p)",
dev, io_buf);
acpt = (sol_ucma_accept_t *)io_buf;
ucma_id = acpt->id;
if (get_file_chan(ucma_id, NULL, &chanp, "accept", 1))
return (EINVAL);
ASSERT(chanp);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "accept - chanp %p", chanp);
if ((acpt->conn_param).valid) {
struct rdma_cm_id *idp;
chanp->chan_user_id = acpt->uid;
usr2rdma_conn_param(&acpt->conn_param, &conn_param);
ASSERT(uverbs_qpnum2qphdl_fp);
qphdl = (*uverbs_qpnum2qphdl_fp) (conn_param.qp_num);
if (qphdl == NULL) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str, "accept: "
"invalid QPNum %x", conn_param.qp_num);
return (EINVAL);
}
(*uverbs_disable_uqpn_modify_fp) (conn_param.qp_num);
rdma_map_id2qphdl(chanp->chan_rdma_id, qphdl);
idp = chanp->chan_rdma_id;
if (idp->ps == RDMA_PS_TCP)
(void) (*uverbs_uqpn_cq_ctrl_fp) (conn_param.qp_num,
SOL_UVERBS2UCMA_CQ_NOTIFY_DISABLE);
chanp->chan_qp_num = conn_param.qp_num;
ret = rdma_accept(chanp->chan_rdma_id, &conn_param);
} else
ret = rdma_accept(chanp->chan_rdma_id, NULL);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "accept: ret %x", ret);
return (ret);
}
/*ARGSUSED*/
static int
sol_ucma_reject(dev_t dev, void *io_buf, struct uio *uio)
{
int ret;
uint32_t ucma_id;
sol_ucma_chan_t *chanp;
sol_ucma_reject_t *rjct;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "reject(%x, %p)", dev, io_buf);
rjct = (sol_ucma_reject_t *)io_buf;
ucma_id = rjct->id;
if (get_file_chan(ucma_id, NULL, &chanp, "reject", 1))
return (EINVAL);
ASSERT(chanp);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "reject - chanp %p", chanp);
ret = rdma_reject(chanp->chan_rdma_id, rjct->private_data,
rjct->private_data_len);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "reject: ret %x", ret);
return (ret);
}
/*ARGSUSED*/
static int
sol_ucma_init_qp_attr(dev_t dev, void *io_buf, struct uio *uio)
{
int ret;
uint32_t ucma_id;
uint32_t qp_attr_mask;
sol_ucma_chan_t *chanp;
sol_ucma_init_qp_attr_t *qp_attr_inp;
struct ib_uverbs_qp_attr uverbs_qp_attr;
struct ib_qp_attr qp_attr;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "init_qp_attr(%x, %p)",
dev, io_buf);
qp_attr_inp = (sol_ucma_init_qp_attr_t *)io_buf;
ucma_id = qp_attr_inp->id;
if (get_file_chan(ucma_id, NULL, &chanp, "init_qp_attr", 1))
return (EINVAL);
ASSERT(chanp);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "init_qp_attr - chanp %p", chanp);
qp_attr.qp_state = qp_attr_inp->qp_state;
if ((ret = rdma_init_qp_attr(chanp->chan_rdma_id, &qp_attr,
(int *)&qp_attr_mask)) != 0) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str, "init_qp_attr: ret %x, "
"mask %x", ret, qp_attr_mask);
return (EINVAL);
}
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "init_qp_attr: ret %x, mask %x",
ret, qp_attr_mask);
bzero(&uverbs_qp_attr, sizeof (uverbs_qp_attr));
uverbs_qp_attr.qp_attr_mask = qp_attr_mask;
uverbs_qp_attr.qp_state = qp_attr.qp_state;
uverbs_qp_attr.pkey_index = qp_attr.pkey_index;
uverbs_qp_attr.port_num = qp_attr.port_num;
uverbs_qp_attr.qp_access_flags = qp_attr.qp_access_flags;
uverbs_qp_attr.qkey = qp_attr.qkey;
uverbs_qp_attr.path_mtu = qp_attr.path_mtu;
uverbs_qp_attr.dest_qp_num = qp_attr.dest_qp_num;
uverbs_qp_attr.rq_psn = qp_attr.rq_psn;
uverbs_qp_attr.max_dest_rd_atomic = qp_attr.max_dest_rd_atomic;
uverbs_qp_attr.min_rnr_timer = qp_attr.min_rnr_timer;
uverbs_qp_attr.ah_attr.dlid = qp_attr.ah_attr.dlid;
if (qp_attr.ah_attr.ah_flags) {
uverbs_qp_attr.ah_attr.is_global = 1;
bcopy(&(qp_attr.ah_attr.grh.dgid),
&(uverbs_qp_attr.ah_attr.grh.dgid), 16);
uverbs_qp_attr.ah_attr.grh.flow_label =
qp_attr.ah_attr.grh.flow_label;
uverbs_qp_attr.ah_attr.grh.sgid_index =
qp_attr.ah_attr.grh.sgid_index;
uverbs_qp_attr.ah_attr.grh.hop_limit =
qp_attr.ah_attr.grh.hop_limit;
uverbs_qp_attr.ah_attr.grh.traffic_class =
qp_attr.ah_attr.grh.traffic_class;
}
uverbs_qp_attr.ah_attr.sl = qp_attr.ah_attr.sl;
uverbs_qp_attr.ah_attr.src_path_bits = qp_attr.ah_attr.src_path_bits;
uverbs_qp_attr.ah_attr.static_rate = qp_attr.ah_attr.static_rate;
uverbs_qp_attr.ah_attr.port_num = qp_attr.ah_attr.port_num;
#ifdef _LP64
if (copyout(&uverbs_qp_attr, (void *) (qp_attr_inp->response.r_laddr),
sizeof (uverbs_qp_attr))) {
#else
if (copyout(&uverbs_qp_attr, (void *) (qp_attr_inp->response.r_addr),
sizeof (uverbs_qp_attr))) {
#endif
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str, "init_qp_attr : copyout "
"failed");
return (EFAULT);
}
return (0);
}
static int
sol_ucma_get_event(dev_t dev, void *io_buf, struct uio *uio)
{
minor_t minor;
sol_ucma_file_t *filep;
sol_ucma_chan_t *evt_chanp;
genlist_entry_t *entry;
struct rdma_ucm_get_event *user_evt_inp;
sol_ucma_event_t *queued_evt;
struct rdma_ucm_event_resp *user_evt_resp;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "get_event(%x, %p)", dev, io_buf);
user_evt_inp = (struct rdma_ucm_get_event *)io_buf;
minor = getminor(dev);
filep = (sol_ucma_file_t *)sol_ofs_uobj_get_read(&ucma_file_uo_tbl,
minor);
ASSERT(filep);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "get_event fmode %x",
uio->uio_fmode);
mutex_enter(&filep->file_mutex);
while (filep->file_pending_evt_cnt == 0) {
SOL_OFS_DPRINTF_L4(sol_ucma_dbg_str, "get_event: No events");
if (uio->uio_fmode & (FNONBLOCK | FNDELAY)) {
mutex_exit(&filep->file_mutex);
sol_ofs_uobj_put(&filep->file_uobj);
SOL_OFS_DPRINTF_L4(sol_ucma_dbg_str,
"get_event: No events, nonblocking");
return (EAGAIN);
}
if (!cv_wait_sig(&filep->file_evt_cv, &filep->file_mutex)) {
mutex_exit(&filep->file_mutex);
sol_ofs_uobj_put(&filep->file_uobj);
SOL_OFS_DPRINTF_L3(sol_ucma_dbg_str,
"get_event: Got Sig");
return (EINTR);
}
}
entry = remove_genlist_head(&filep->file_evt_list);
mutex_exit(&filep->file_mutex);
ASSERT(entry);
queued_evt = (sol_ucma_event_t *)entry->data;
ASSERT(queued_evt);
user_evt_resp = &queued_evt->event_resp;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "event2usr "
"uid %llx, id %x, event %x, status %x", user_evt_resp->uid,
user_evt_resp->id, user_evt_resp->event, user_evt_resp->status);
#ifdef _LP64
if (copyout((void *)user_evt_resp,
(void *)(user_evt_inp->response.r_laddr),
sizeof (sol_ucma_event_resp_t))) {
#else
if (copyout((void *)user_evt_resp,
(void *)(user_evt_inp->response.r_addr),
sizeof (sol_ucma_event_resp_t))) {
#endif
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "get_event: copyout "
"failed");
sol_ofs_uobj_put(&filep->file_uobj);
kmem_free(entry, sizeof (genlist_entry_t));
return (EFAULT);
}
mutex_enter(&filep->file_mutex);
filep->file_pending_evt_cnt--;
if (queued_evt->event_mcast)
(queued_evt->event_mcast)->mcast_events++;
evt_chanp = queued_evt->event_chan;
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.
*/
if (user_evt_resp->event == RDMA_CM_EVENT_CONNECT_RESPONSE ||
user_evt_resp->event == RDMA_CM_EVENT_ESTABLISHED) {
struct rdma_cm_id *idp;
int rc;
idp = evt_chanp->chan_rdma_id;
if (idp->ps == RDMA_PS_TCP) {
ASSERT(uverbs_uqpn_cq_ctrl_fp);
rc = (*uverbs_uqpn_cq_ctrl_fp)(
evt_chanp->chan_qp_num,
SOL_UVERBS2UCMA_CQ_NOTIFY_ENABLE);
if (rc) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"uverbs_uqpn_cq_ctrl_fp(%X) "
"failed!!",
evt_chanp->chan_qp_num);
mutex_exit(&filep->file_mutex);
filep->file_pending_evt_cnt++;
return (EIO);
}
}
}
/* Bump up backlog for CONNECT_REQUEST events */
mutex_enter(&evt_chanp->chan_mutex);
if (user_evt_resp->event == RDMA_CM_EVENT_CONNECT_REQUEST)
evt_chanp->chan_backlog++;
evt_chanp->chan_evt_cnt++;
mutex_exit(&evt_chanp->chan_mutex);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "get_event : "
"chan %p, cnt %x", evt_chanp, evt_chanp->chan_evt_cnt);
}
mutex_exit(&filep->file_mutex);
kmem_free(entry, sizeof (genlist_entry_t));
kmem_free(queued_evt, sizeof (sol_ucma_event_t));
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "get_event: Success");
sol_ofs_uobj_put(&filep->file_uobj);
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
sol_ucma_set_option(dev_t dev, void *io_buf, struct uio *uio)
{
return (EINVAL);
}
/*
* This is used when ULP uses librdmacm but uses out of band connection for CM.
*/
/*ARGSUSED*/
static int
sol_ucma_notify(dev_t dev, void *io_buf, struct uio *uio)
{
sol_ucma_notify_t *notifyp;
uint32_t ucma_id;
sol_ucma_chan_t *chan;
int ret;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "notify(%x, %p)", dev, io_buf);
notifyp = (sol_ucma_notify_t *)io_buf;
ucma_id = notifyp->id;
if (get_file_chan(ucma_id, NULL, &chan, "notify", 1))
return (EINVAL);
ASSERT(chan);
ret = rdma_notify(chan->chan_rdma_id, notifyp->event);
if (ret)
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str, "notify failed %x", ret);
else
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "notify Success");
return (ret);
}
/*ARGSUSED*/
static int
sol_ucma_join_mcast(dev_t dev, void *io_buf, struct uio *uio)
{
sol_ucma_join_mcast_t *join_buf;
sol_ucma_create_id_resp_t join_resp;
sol_ucma_chan_t *chanp;
sol_ucma_mcast_t *mcastp;
int rc;
uint32_t ucma_id;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "join_mcast(%x, %p)",
dev, io_buf);
join_buf = (sol_ucma_join_mcast_t *)io_buf;
ucma_id = join_buf->id;
if (get_file_chan(ucma_id, NULL, &chanp, "join_mcast", 1))
return (EINVAL);
mcastp = kmem_zalloc(sizeof (sol_ucma_mcast_t), KM_SLEEP);
bcopy((void *)(&(join_buf->addr)), (void *)(&(mcastp->mcast_addr)),
sizeof (struct sockaddr));
mcastp->mcast_chan = chanp;
sol_ofs_uobj_init(&mcastp->mcast_uobj, NULL, SOL_UCMA_MCAST_TYPE);
if (sol_ofs_uobj_add(&ucma_mcast_uo_tbl, &mcastp->mcast_uobj) != 0) {
sol_ofs_uobj_free(&mcastp->mcast_uobj);
return (ENOMEM);
}
mcastp->mcast_uobj.uo_live = 1;
mcastp->mcast_id = join_resp.id = mcastp->mcast_uobj.uo_id;
mcastp->mcast_uid = join_buf->uid;
rc = rdma_join_multicast(chanp->chan_rdma_id,
(struct sockaddr *)(&(join_buf->addr)), mcastp);
if (rc) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"join_mcast: rdma_join_multicast ret %x", rc);
rw_enter(&(mcastp->mcast_uobj.uo_lock), RW_WRITER);
(void) sol_ofs_uobj_remove(&ucma_mcast_uo_tbl,
&mcastp->mcast_uobj);
rw_exit(&(mcastp->mcast_uobj.uo_lock));
sol_ofs_uobj_free(&mcastp->mcast_uobj);
return (rc);
}
#ifdef _LP64
if (copyout(&join_resp, (void *) (join_buf->response.r_laddr),
sizeof (sol_ucma_create_id_resp_t))) {
#else
if (copyout(&join_resp, (void *) (join_buf->response.r_addr),
sizeof (sol_ucma_create_id_resp_t))) {
#endif
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "join_mcast: copyout "
"failed");
rdma_leave_multicast(chanp->chan_rdma_id,
(struct sockaddr *)(&(join_buf->addr)));
rw_enter(&(mcastp->mcast_uobj.uo_lock), RW_WRITER);
(void) sol_ofs_uobj_remove(&ucma_mcast_uo_tbl,
&mcastp->mcast_uobj);
rw_exit(&(mcastp->mcast_uobj.uo_lock));
sol_ofs_uobj_free(&mcastp->mcast_uobj);
return (EFAULT);
}
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "join_mcast: Return Success");
return (0);
}
/*ARGSUSED*/
static int
sol_ucma_leave_mcast(dev_t dev, void *io_buf, struct uio *uio)
{
sol_ucma_destroy_id_t *id_inp;
sol_ucma_destroy_id_resp_t id_resp;
sol_ucma_mcast_t *mcastp;
sol_ucma_chan_t *chanp;
uint32_t ucma_id;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "leave_mcast(%x, %p)",
dev, io_buf);
id_inp = (sol_ucma_destroy_id_t *)io_buf;
ucma_id = id_inp->id;
mcastp = (sol_ucma_mcast_t *)sol_ofs_uobj_get_read(&ucma_mcast_uo_tbl,
ucma_id);
if (mcastp == NULL) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str, "leave_mcast: invalid "
"ID %x", ucma_id);
return (EINVAL);
}
chanp = mcastp->mcast_chan;
rdma_leave_multicast(chanp->chan_rdma_id, &mcastp->mcast_addr);
id_resp.events_reported = mcastp->mcast_events;
#ifdef _LP64
if (copyout(&id_resp, (void *) (id_inp->response.r_laddr),
sizeof (sol_ucma_destroy_id_resp_t))) {
#else
if (copyout(&id_resp, (void *) (id_inp->response.r_addr),
sizeof (sol_ucma_destroy_id_resp_t))) {
#endif
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str, "leave_mcast: copyout "
"fault");
sol_ofs_uobj_put(&mcastp->mcast_uobj);
return (EFAULT);
}
sol_ofs_uobj_put(&mcastp->mcast_uobj);
rw_enter(&(mcastp->mcast_uobj.uo_lock), RW_WRITER);
(void) sol_ofs_uobj_remove(&ucma_mcast_uo_tbl, &mcastp->mcast_uobj);
rw_exit(&(mcastp->mcast_uobj.uo_lock));
sol_ofs_uobj_free(&mcastp->mcast_uobj);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "leave_mcast: ret 0");
return (0);
}
/*ARGSUSED*/
static int
sol_ucma_disconnect(dev_t dev, void *io_buf, struct uio *uio)
{
sol_ucma_disconnect_t *disconnectp;
uint32_t ucma_id;
sol_ucma_chan_t *chan;
int ret;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "disconnect(%x, %p)",
dev, io_buf);
disconnectp = (sol_ucma_disconnect_t *)io_buf;
ucma_id = disconnectp->id;
if (get_file_chan(ucma_id, NULL, &chan, "disconnect", 1))
return (EINVAL);
ASSERT(chan);
/*
* For a TCP CMID, which has got the DISCONNECT event, call
* ibt_flush_qp(), to transition QP to error state.
*/
mutex_enter(&chan->chan_mutex);
if (chan->chan_flush_qp_flag == SOL_UCMA_FLUSH_QP_PENDING) {
chan->chan_flush_qp_flag = SOL_UCMA_FLUSH_QP_DONE;
mutex_exit(&chan->chan_mutex);
(*uverbs_flush_qp_fp)(chan->chan_qp_num);
} else
mutex_exit(&chan->chan_mutex);
ret = rdma_disconnect(chan->chan_rdma_id);
mutex_enter(&chan->chan_mutex);
chan->chan_flush_qp_flag = SOL_UCMA_FLUSH_QP_DONE;
mutex_exit(&chan->chan_mutex);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "disconnect: ret %x", ret);
return (ret);
}
/*
* RDMA ID Event handler
*/
int
sol_ucma_evt_hdlr(struct rdma_cm_id *idp, struct rdma_cm_event *eventp)
{
sol_ucma_chan_t *chan, *req_chan;
sol_ucma_file_t *file;
sol_ucma_event_t *ucma_evt;
sol_ucma_create_id_t ucma_create_id;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "ucma_evt_hdlr(%p, %p), "
"event %x, status %x", idp, eventp, eventp->event,
eventp->status);
chan = (sol_ucma_chan_t *)idp->context;
if (!chan) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str, "ucma_evt_hdlr() - "
"after destroy - %p", idp);
return (0);
}
mutex_enter(&chan->chan_mutex);
file = chan->chan_file;
if (!file) {
SOL_OFS_DPRINTF_L3(sol_ucma_dbg_str, "ucma_evt_hdlr() - "
"after file destroy - idp %p", idp);
mutex_exit(&chan->chan_mutex);
return (0);
}
mutex_exit(&chan->chan_mutex);
mutex_enter(&file->file_mutex);
if (file->file_evt_close_flag == SOL_UCMA_EVT_DISABLED) {
SOL_OFS_DPRINTF_L3(sol_ucma_dbg_str, "ucma_evt_hdlr() - "
"after file close - idp %p", idp);
mutex_exit(&file->file_mutex);
return (0);
}
file->file_evt_close_flag = SOL_UCMA_EVT_PROGRESS;
mutex_exit(&file->file_mutex);
/*
* 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.
*/
ucma_evt = kmem_zalloc(sizeof (sol_ucma_event_t), KM_SLEEP);
ucma_evt->event_chan = chan;
if (eventp->event == RDMA_CM_EVENT_CONNECT_REQUEST) {
mutex_enter(&chan->chan_mutex);
if (!chan->chan_backlog) {
SOL_OFS_DPRINTF_L3(sol_ucma_dbg_str,
"backlog exceeded");
mutex_exit(&chan->chan_mutex);
mutex_enter(&file->file_mutex);
file->file_evt_close_flag = SOL_UCMA_EVT_NONE;
cv_broadcast(&file->file_evt_close_cv);
mutex_exit(&file->file_mutex);
kmem_free(ucma_evt, sizeof (sol_ucma_event_t));
return (-1);
}
chan->chan_backlog--;
mutex_exit(&chan->chan_mutex);
ucma_create_id.uid = chan->chan_user_id;
req_chan = ucma_alloc_chan(file, &ucma_create_id);
if (req_chan == NULL) {
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"evt hdlr: No free Channel");
sol_ofs_uobj_put(&file->file_uobj);
mutex_enter(&file->file_mutex);
file->file_evt_close_flag = SOL_UCMA_EVT_NONE;
cv_broadcast(&file->file_evt_close_cv);
mutex_exit(&file->file_mutex);
return (-1);
}
req_chan->chan_rdma_id = idp;
mutex_enter(&req_chan->chan_mutex);
idp->context = req_chan;
mutex_exit(&req_chan->chan_mutex);
chan = req_chan;
} else if (eventp->event == RDMA_CM_EVENT_DISCONNECTED ||
eventp->event == RDMA_CM_EVENT_REJECTED) {
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.
*/
mutex_enter(&chan->chan_mutex);
qphdl = chan->chan_qp_hdl;
chan->chan_qp_hdl = NULL;
if (idp->ps == RDMA_PS_TCP &&
chan->chan_flush_qp_flag != SOL_UCMA_FLUSH_QP_DONE &&
eventp->event == RDMA_CM_EVENT_DISCONNECTED) {
chan->chan_flush_qp_flag =
SOL_UCMA_FLUSH_QP_PENDING;
}
mutex_exit(&chan->chan_mutex);
if (idp->ps == RDMA_PS_TCP && qphdl)
(*uverbs_set_qp_free_state_fp) (
SOL_UVERBS2UCMA_ENABLE_QP_FREE, 0, qphdl);
} else if (eventp->event == RDMA_CM_EVENT_ESTABLISHED &&
chan->chan_flags & SOL_UCMA_CHAN_CONNECT_FLAG)
eventp->event = RDMA_CM_EVENT_CONNECT_RESPONSE;
ucma_evt->event_resp.event = eventp->event;
ucma_evt->event_resp.status = eventp->status;
if (idp->ps == RDMA_PS_UDP || idp->ps == RDMA_PS_IPOIB)
rdma2usr_ud_param(&(eventp->param.ud),
&(ucma_evt->event_resp.param.ud));
else
rdma2usr_conn_param(&(eventp->param.conn),
&(ucma_evt->event_resp.param.conn));
if (eventp->event == RDMA_CM_EVENT_MULTICAST_JOIN || eventp->event ==
RDMA_CM_EVENT_MULTICAST_ERROR) {
ucma_evt->event_mcast = (sol_ucma_mcast_t *)
eventp->param.ud.private_data;
ucma_evt->event_resp.uid = (ucma_evt->event_mcast)->mcast_uid;
ucma_evt->event_resp.id = (ucma_evt->event_mcast)->mcast_id;
} else {
ucma_evt->event_resp.uid = chan->chan_user_id;
ucma_evt->event_resp.id = chan->chan_id;
}
mutex_enter(&file->file_mutex);
(void) add_genlist(&file->file_evt_list, (uintptr_t)ucma_evt, NULL);
file->file_pending_evt_cnt++;
mutex_exit(&file->file_mutex);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "ucma_evt_hdlr-pollwakeup");
pollwakeup(file->file_pollhead, POLLIN | POLLRDNORM);
mutex_enter(&file->file_mutex);
cv_broadcast(&file->file_evt_cv);
mutex_exit(&file->file_mutex);
mutex_enter(&file->file_mutex);
file->file_evt_close_flag = SOL_UCMA_EVT_NONE;
cv_broadcast(&file->file_evt_close_cv);
mutex_exit(&file->file_mutex);
return (0);
}
/*
* Local Functions
*/
static sol_ucma_file_t *
ucma_alloc_file(minor_t *new_minorp)
{
sol_ucma_file_t *new_file;
new_file = kmem_zalloc(sizeof (sol_ucma_file_t), KM_SLEEP);
sol_ofs_uobj_init(&new_file->file_uobj, NULL, SOL_UCMA_EVT_FILE_TYPE);
if (sol_ofs_uobj_add(&ucma_file_uo_tbl, &new_file->file_uobj) != 0) {
sol_ofs_uobj_free(&new_file->file_uobj);
return (NULL);
}
new_file->file_uobj.uo_live = 1;
init_genlist(&new_file->file_id_list);
init_genlist(&new_file->file_evt_list);
mutex_enter(&sol_ucma.ucma_mutex);
sol_ucma.ucma_num_file++;
mutex_exit(&sol_ucma.ucma_mutex);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "new file num %x, %p",
(new_file->file_uobj).uo_id, new_file);
mutex_init(&new_file->file_mutex, NULL,
MUTEX_DRIVER, NULL);
cv_init(&new_file->file_evt_cv, NULL, CV_DRIVER,
NULL);
cv_init(&new_file->file_evt_close_cv, NULL, CV_DRIVER,
NULL);
new_file->file_pollhead = kmem_zalloc(sizeof (struct pollhead),
KM_SLEEP);
*new_minorp = (minor_t)((new_file->file_uobj).uo_id);
return (new_file);
}
static sol_ucma_chan_t *
ucma_alloc_chan(sol_ucma_file_t *filep, sol_ucma_create_id_t *create_id_inp)
{
sol_ucma_chan_t *new_chanp;
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "_alloc_chan(%p, %p)",
filep, create_id_inp);
new_chanp = kmem_zalloc(sizeof (sol_ucma_chan_t), KM_SLEEP);
sol_ofs_uobj_init(&new_chanp->chan_uobj, NULL, SOL_UCMA_CM_ID_TYPE);
if (sol_ofs_uobj_add(&ucma_ctx_uo_tbl, &new_chanp->chan_uobj) != 0) {
sol_ofs_uobj_free(&new_chanp->chan_uobj);
return (NULL);
}
mutex_init(&new_chanp->chan_mutex, NULL, MUTEX_DRIVER, NULL);
new_chanp->chan_uobj.uo_live = 1;
mutex_enter(&filep->file_mutex);
new_chanp->chan_list_ent = add_genlist(&filep->file_id_list,
(uintptr_t)new_chanp, NULL);
mutex_exit(&filep->file_mutex);
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str, "_alloc_chan - filep %p, "
"chan_num %x, new_chan %p", filep, (new_chanp->chan_uobj).uo_id,
new_chanp);
new_chanp->chan_file = filep;
new_chanp->chan_user_id = create_id_inp->uid;
new_chanp->chan_id = (new_chanp->chan_uobj).uo_id;
return (new_chanp);
}
static void
ucma_free_chan(sol_ucma_chan_t *chanp, int delete_list)
{
sol_ucma_file_t *filep;
ASSERT(chanp);
if (delete_list) {
filep = chanp->chan_file;
ASSERT(filep);
mutex_enter(&filep->file_mutex);
delete_genlist(&filep->file_id_list, chanp->chan_list_ent);
mutex_exit(&filep->file_mutex);
}
mutex_destroy(&chanp->chan_mutex);
rw_enter(&(chanp->chan_uobj.uo_lock), RW_WRITER);
(void) sol_ofs_uobj_remove(&ucma_ctx_uo_tbl, &(chanp->chan_uobj));
rw_exit(&(chanp->chan_uobj.uo_lock));
sol_ofs_uobj_free(&(chanp->chan_uobj));
}
static int
get_file_chan(uint32_t ucma_id, sol_ucma_file_t **filep,
sol_ucma_chan_t **chanp, char *caller, int flag_err)
{
sol_ucma_chan_t *chan;
if (filep)
*filep = NULL;
if (chanp)
*chanp = NULL;
chan = (sol_ucma_chan_t *)sol_ofs_uobj_get_read(&ucma_ctx_uo_tbl,
ucma_id);
if (chan == NULL) {
if (flag_err)
SOL_OFS_DPRINTF_L2(sol_ucma_dbg_str,
"%s, ucma_id %x invalid", caller, ucma_id);
else
SOL_OFS_DPRINTF_L5(sol_ucma_dbg_str,
"%s, ucma_id %x invalid", caller, ucma_id);
return (-1);
}
if (filep)
*filep = chan->chan_file;
if (chanp)
*chanp = chan;
sol_ofs_uobj_put(&chan->chan_uobj);
return (0);
}
static void
rdma2usr_pathrec(struct ib_sa_path_rec *kern_path,
struct ib_user_path_rec *usr_path)
{
bcopy(&kern_path->dgid, &usr_path->dgid, 16);
bcopy(&kern_path->sgid, &usr_path->sgid, 16);
usr_path->dlid = kern_path->dlid;
usr_path->slid = kern_path->slid;
usr_path->raw_traffic = kern_path->raw_traffic;
usr_path->flow_label = kern_path->flow_label;
usr_path->reversible = kern_path->reversible;
usr_path->mtu = kern_path->mtu;
usr_path->pkey = kern_path->pkey;
usr_path->hop_limit = kern_path->hop_limit;
usr_path->traffic_class = kern_path->traffic_class;
usr_path->sl = kern_path->sl;
usr_path->mtu_selector = kern_path->mtu_selector;
usr_path->rate_selector = kern_path->rate_selector;
usr_path->rate = kern_path->rate;
usr_path->packet_life_time_selector =
kern_path->packet_life_time_selector;
usr_path->packet_life_time = kern_path->packet_life_time;
usr_path->preference = kern_path->preference;
usr_path->numb_path = kern_path->numb_path;
}
static void
rdma2usr_route(struct rdma_cm_id *idp, sol_ucma_query_route_resp_t *resp)
{
struct rdma_route *routep;
int i;
routep = &(idp->route);
if (idp->device) {
resp->node_guid = idp->device->node_guid;
resp->port_num = idp->port_num;
}
bcopy(&(routep->addr.src_addr), &resp->src_addr,
sizeof (struct sockaddr_in6));
bcopy(&(routep->addr.dst_addr), &resp->dst_addr,
sizeof (struct sockaddr_in6));
resp->num_paths = routep->num_paths;
for (i = 0; i < resp->num_paths; i++) {
rdma2usr_pathrec(&(routep->path_rec[i]),
&(resp->ib_route[i]));
}
}
static void
usr2rdma_conn_param(struct rdma_ucm_conn_param *usr_conn_paramp,
struct rdma_conn_param *conn_paramp)
{
conn_paramp->private_data = usr_conn_paramp->private_data;
conn_paramp->private_data_len = usr_conn_paramp->private_data_len;
conn_paramp->responder_resources = usr_conn_paramp->responder_resources;
conn_paramp->initiator_depth = usr_conn_paramp->initiator_depth;
conn_paramp->flow_control = usr_conn_paramp->flow_control;
conn_paramp->retry_count = usr_conn_paramp->retry_count;
conn_paramp->rnr_retry_count = usr_conn_paramp->rnr_retry_count;
conn_paramp->srq = usr_conn_paramp->srq;
conn_paramp->qp_num = usr_conn_paramp->qp_num;
}
static void
rdma2usr_conn_param(struct rdma_conn_param *conn_paramp,
struct rdma_ucm_conn_param *usr_conn_paramp)
{
usr_conn_paramp->private_data_len = conn_paramp->private_data_len;
bzero(usr_conn_paramp->private_data, RDMA_MAX_PRIVATE_DATA);
if (conn_paramp->private_data)
bcopy(conn_paramp->private_data,
usr_conn_paramp->private_data,
usr_conn_paramp->private_data_len);
usr_conn_paramp->responder_resources = conn_paramp->responder_resources;
usr_conn_paramp->initiator_depth = conn_paramp->initiator_depth;
usr_conn_paramp->flow_control = conn_paramp->flow_control;
usr_conn_paramp->retry_count = conn_paramp->retry_count;
usr_conn_paramp->rnr_retry_count = conn_paramp->rnr_retry_count;
usr_conn_paramp->srq = conn_paramp->srq;
usr_conn_paramp->qp_num = conn_paramp->qp_num;
}
static void
rdma2usr_ud_param(struct rdma_ud_param *ud_paramp,
sol_ucma_ud_param_t *usr_ud_paramp)
{
struct ib_ah_attr *ah_attrp;
struct ib_uverbs_ah_attr *usr_ah_attrp;
usr_ud_paramp->private_data_len = ud_paramp->private_data_len;
bzero(usr_ud_paramp->private_data, RDMA_MAX_PRIVATE_DATA);
if (ud_paramp->private_data)
bcopy(ud_paramp->private_data,
usr_ud_paramp->private_data,
usr_ud_paramp->private_data_len);
usr_ud_paramp->qp_num = ud_paramp->qp_num;
usr_ud_paramp->qkey = ud_paramp->qkey;
ah_attrp = &(ud_paramp->ah_attr);
usr_ah_attrp = &(usr_ud_paramp->ah_attr);
bcopy(&(ah_attrp->grh.dgid), &(usr_ah_attrp->grh.dgid[0]), 16);
usr_ah_attrp->grh.flow_label = ah_attrp->grh.flow_label;
usr_ah_attrp->grh.sgid_index = ah_attrp->grh.sgid_index;
usr_ah_attrp->grh.hop_limit = ah_attrp->grh.hop_limit;
usr_ah_attrp->grh.traffic_class = ah_attrp->grh.traffic_class;
usr_ah_attrp->dlid = ah_attrp->dlid;
usr_ah_attrp->sl = ah_attrp->sl;
usr_ah_attrp->src_path_bits = ah_attrp->src_path_bits;
usr_ah_attrp->static_rate = ah_attrp->static_rate;
usr_ah_attrp->is_global = ah_attrp->ah_flags;
usr_ah_attrp->port_num = ah_attrp->port_num;
}
static void
sol_ucma_user_objs_init()
{
sol_ofs_uobj_tbl_init(&ucma_file_uo_tbl, sizeof (sol_ucma_file_t));
sol_ofs_uobj_tbl_init(&ucma_ctx_uo_tbl, sizeof (sol_ucma_chan_t));
sol_ofs_uobj_tbl_init(&ucma_mcast_uo_tbl, sizeof (sol_ucma_mcast_t));
}
static void
sol_ucma_user_objs_fini()
{
sol_ofs_uobj_tbl_fini(&ucma_file_uo_tbl);
sol_ofs_uobj_tbl_fini(&ucma_ctx_uo_tbl);
sol_ofs_uobj_tbl_fini(&ucma_mcast_uo_tbl);
}