#include <sys/open.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/conf.h>
#include <sys/modctl.h>
#include <sys/sysmacros.h>
#include <sys/ib/ibtl/ibti.h>
#include <sys/ib/mgt/ibmf/ibmf.h>
#include <sys/ib/mgt/ibmf/ibmf_rmpp.h>
#include <sys/types.h>
#include <sys/ib/clients/of/ofed_kernel.h>
#include <sys/ib/clients/of/sol_ofs/sol_ofs_common.h>
#include <sys/ib/clients/of/rdma/ib_user_mad.h>
#include <sys/ib/clients/of/sol_umad/sol_umad.h>
#include <sys/policy.h>
#include <sys/priv_const.h>
#define MAX_NAME_LEN 32
#if defined(DEBUG)
static char *sol_umad_dbg_str = "sol_umad";
#endif
static void *umad_statep;
static struct cb_ops umad_cb_ops = {
.cb_open = umad_open,
.cb_close = umad_close,
.cb_strategy = nodev,
.cb_print = nodev,
.cb_dump = nodev,
.cb_read = umad_read,
.cb_write = umad_write,
.cb_ioctl = umad_ioctl,
.cb_devmap = nodev,
.cb_mmap = nodev,
.cb_segmap = nodev,
.cb_chpoll = umad_poll,
.cb_prop_op = umad_prop_op,
.cb_str = NULL,
.cb_flag = D_NEW | D_MP,
.cb_rev = CB_REV,
.cb_aread = nodev,
.cb_awrite = nodev
};
static struct dev_ops umad_dev_ops = {
.devo_rev = DEVO_REV,
.devo_refcnt = 0,
.devo_getinfo = umad_getinfo,
.devo_identify = nulldev,
.devo_probe = nulldev,
.devo_attach = umad_attach,
.devo_detach = umad_detach,
.devo_reset = nodev,
.devo_cb_ops = &umad_cb_ops,
.devo_bus_ops = NULL,
.devo_power = nodev,
.devo_quiesce = ddi_quiesce_not_needed
};
static struct modldrv umad_modldrv = {
.drv_modops = &mod_driverops,
.drv_linkinfo = "Solaris IB user MAD kernel driver",
.drv_dev_ops = &umad_dev_ops
};
static struct modlinkage modlinkage = {
.ml_rev = MODREV_1,
.ml_linkage = {
[0] = &umad_modldrv,
[1] = NULL,
}
};
static ibt_clnt_modinfo_t ibt_clnt_modinfo = {
.mi_ibt_version = IBTI_V_CURR,
.mi_clnt_class = IBT_USER,
.mi_async_handler = umad_async_handler,
.mi_reserved = NULL,
.mi_clnt_name = "sol_umad"
};
#define MAX_MAD_TO_IBMF_MAPPINGS 4
const struct ibmf_class_to_mad_type {
enum _ibmf_client_type_t ibmf_class;
uint8_t mad_types[MAX_MAD_TO_IBMF_MAPPINGS];
} ibmf_class_to_mad_types[] = {
{SUBN_MANAGER,
{MAD_MGMT_CLASS_SUBN_LID_ROUTED,
MAD_MGMT_CLASS_SUBN_DIRECT_ROUTE,
0}},
{0,
{0}}
};
const enum _ibmf_client_type_t umad_type_to_ibmf_class[256] = {
0,
SUBN_MANAGER,
0,
SUBN_ADM_AGENT,
PERF_MANAGER,
BM_AGENT,
DEV_MGT_AGENT,
COMM_MGT_MANAGER_AGENT,
SNMP_MANAGER_AGENT,
VENDOR_09_MANAGER_AGENT,
VENDOR_0A_MANAGER_AGENT,
VENDOR_0B_MANAGER_AGENT,
VENDOR_0C_MANAGER_AGENT,
VENDOR_0D_MANAGER_AGENT,
VENDOR_0E_MANAGER_AGENT,
VENDOR_0F_MANAGER_AGENT,
APPLICATION_10_MANAGER_AGENT,
APPLICATION_11_MANAGER_AGENT,
APPLICATION_12_MANAGER_AGENT,
APPLICATION_13_MANAGER_AGENT,
APPLICATION_14_MANAGER_AGENT,
APPLICATION_15_MANAGER_AGENT,
APPLICATION_16_MANAGER_AGENT,
APPLICATION_17_MANAGER_AGENT,
APPLICATION_18_MANAGER_AGENT,
APPLICATION_19_MANAGER_AGENT,
APPLICATION_1A_MANAGER_AGENT,
APPLICATION_1B_MANAGER_AGENT,
APPLICATION_1C_MANAGER_AGENT,
APPLICATION_1D_MANAGER_AGENT,
APPLICATION_1E_MANAGER_AGENT,
APPLICATION_1F_MANAGER_AGENT,
APPLICATION_20_MANAGER_AGENT,
APPLICATION_21_MANAGER_AGENT,
APPLICATION_22_MANAGER_AGENT,
APPLICATION_23_MANAGER_AGENT,
APPLICATION_24_MANAGER_AGENT,
APPLICATION_25_MANAGER_AGENT,
APPLICATION_26_MANAGER_AGENT,
APPLICATION_27_MANAGER_AGENT,
APPLICATION_28_MANAGER_AGENT,
APPLICATION_29_MANAGER_AGENT,
APPLICATION_2A_MANAGER_AGENT,
APPLICATION_2B_MANAGER_AGENT,
APPLICATION_2C_MANAGER_AGENT,
APPLICATION_2D_MANAGER_AGENT,
APPLICATION_2E_MANAGER_AGENT,
APPLICATION_2F_MANAGER_AGENT,
VENDOR_30_MANAGER_AGENT,
VENDOR_31_MANAGER_AGENT,
VENDOR_32_MANAGER_AGENT,
VENDOR_33_MANAGER_AGENT,
VENDOR_34_MANAGER_AGENT,
VENDOR_35_MANAGER_AGENT,
VENDOR_36_MANAGER_AGENT,
VENDOR_37_MANAGER_AGENT,
VENDOR_38_MANAGER_AGENT,
VENDOR_39_MANAGER_AGENT,
VENDOR_3A_MANAGER_AGENT,
VENDOR_3B_MANAGER_AGENT,
VENDOR_3C_MANAGER_AGENT,
VENDOR_3D_MANAGER_AGENT,
VENDOR_3E_MANAGER_AGENT,
VENDOR_3F_MANAGER_AGENT,
VENDOR_40_MANAGER_AGENT,
VENDOR_41_MANAGER_AGENT,
VENDOR_42_MANAGER_AGENT,
VENDOR_43_MANAGER_AGENT,
VENDOR_44_MANAGER_AGENT,
VENDOR_45_MANAGER_AGENT,
VENDOR_46_MANAGER_AGENT,
VENDOR_47_MANAGER_AGENT,
VENDOR_48_MANAGER_AGENT,
VENDOR_49_MANAGER_AGENT,
VENDOR_4A_MANAGER_AGENT,
VENDOR_4B_MANAGER_AGENT,
VENDOR_4C_MANAGER_AGENT,
VENDOR_4D_MANAGER_AGENT,
VENDOR_4E_MANAGER_AGENT,
VENDOR_4F_MANAGER_AGENT,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
SUBN_MANAGER,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
};
static void
umad_init_port_info(const umad_hca_info_t *hca, umad_port_info_t *port)
{
port->port_hca = hca;
llist_head_init(&port->port_ibmf_regs, NULL);
mutex_init(&port->port_lock, NULL, MUTEX_DRIVER, NULL);
}
static void
umad_release_hca_info(umad_hca_info_t *hca)
{
unsigned int j;
umad_port_info_t *port;
#if defined(DEBUG)
ibt_status_t rc;
#endif
if (hca->hca_ports) {
for (j = 0; j < hca->hca_nports; j++) {
port = &(hca->hca_ports[j]);
if (port->port_num)
mutex_destroy(&port->port_lock);
}
kmem_free(hca->hca_ports, hca->hca_nports *
sizeof (umad_port_info_t));
hca->hca_ports = NULL;
}
if (hca->hca_handle) {
#if defined(DEBUG)
rc = ibt_close_hca(hca->hca_handle);
if (rc != IBT_SUCCESS) {
SOL_OFS_DPRINTF_L5(sol_umad_dbg_str,
"umad_release_hca: ibt_close_hca() returned %d\n",
rc);
}
#else
(void) ibt_close_hca(hca->hca_handle);
#endif
hca->hca_handle = 0;
}
hca->hca_guid = 0;
}
static int
umad_init_hca_info(const umad_info_t *info, umad_hca_info_t *hca)
{
int rc;
unsigned int j;
umad_port_info_t *port;
rc = ibt_open_hca(info->info_clnt_hdl, hca->hca_guid, &hca->hca_handle);
if (rc != IBT_SUCCESS)
goto error;
rc = ibt_query_hca(hca->hca_handle, &hca->hca_attr);
if (rc != IBT_SUCCESS)
goto error;
hca->hca_nports = hca->hca_attr.hca_nports;
hca->hca_ports =
kmem_zalloc(sizeof (umad_port_info_t) * hca->hca_nports, KM_SLEEP);
for (j = 0; j < hca->hca_nports; j++) {
port = &hca->hca_ports[j];
umad_init_port_info(hca, port);
port->port_num = j + 1;
}
error:
if (rc)
umad_release_hca_info(hca);
return (rc);
}
static ibt_status_t
umad_init_driver_info(umad_info_t *info)
{
ibt_status_t rc;
#if defined(DEBUG)
ibt_status_t rc2;
#endif
unsigned int i;
uint32_t hca_count;
ib_guid_t *hca_guids = NULL;
umad_hca_info_t *hca;
info->info_hca_count = 0;
info->info_clnt_hdl = NULL;
info->info_hcas = NULL;
rc = ibt_attach(&ibt_clnt_modinfo, info->info_dip, info,
&info->info_clnt_hdl);
if (rc != IBT_SUCCESS)
goto err1;
hca_count = info->info_hca_count = ibt_get_hca_list(&hca_guids);
if (hca_count == 0) {
rc = IBT_HCA_INVALID;
goto err2;
}
info->info_hcas = kmem_zalloc(sizeof (umad_hca_info_t) * hca_count,
KM_SLEEP);
for (i = 0; i < hca_count; i++) {
hca = &info->info_hcas[i];
hca->hca_guid = hca_guids[i];
rc = umad_init_hca_info(info, hca);
if (rc)
goto err3;
}
ibt_free_hca_list(hca_guids, hca_count);
return (0);
err3:
for (i = 0; i < info->info_hca_count; i++) {
hca = &info->info_hcas[i];
if (hca->hca_guid)
umad_release_hca_info(hca);
}
kmem_free(info->info_hcas,
info->info_hca_count * sizeof (umad_hca_info_t));
info->info_hcas = NULL;
if (hca_guids)
ibt_free_hca_list(hca_guids, hca_count);
err2:
#if defined(DEBUG)
rc2 = ibt_detach(info->info_clnt_hdl);
if (rc2 != IBT_SUCCESS) {
SOL_OFS_DPRINTF_L5(sol_umad_dbg_str,
"umad_init_driver_info: ibt_detach failed: %d\n", rc2);
}
#else
(void) ibt_detach(info->info_clnt_hdl);
#endif
info->info_clnt_hdl = NULL;
err1:
return (rc);
}
static void
umad_context_destroy(dev_info_t *dip, umad_info_t *info)
{
unsigned int i;
unsigned int j;
size_t n;
for (i = 0; i < info->info_hca_count; i++) {
umad_hca_info_t *hca = &info->info_hcas[i];
if (! hca->hca_guid)
continue;
for (j = 0; j < hca->hca_nports; j++) {
umad_port_info_t *port = &hca->hca_ports[j];
char name[MAX_NAME_LEN];
if (port->port_has_umad_minor_node) {
n = snprintf(name, sizeof (name),
"umad%d", port->port_minor_name);
#if defined(DEBUG)
if (n > sizeof (name)) {
SOL_OFS_DPRINTF_L5(sol_umad_dbg_str,
"umad_context_destroy:"
" minor name \"%s\": is longer than"
" %d characters!\n",
name, MAX_NAME_LEN);
}
#endif
ddi_remove_minor_node(dip, name);
}
if (port->port_has_issm_minor_node) {
n = snprintf(name, sizeof (name),
"issm%d", port->port_minor_name);
#if defined(DEBUG)
if (n > sizeof (name)) {
SOL_OFS_DPRINTF_L5(sol_umad_dbg_str,
"umad_context_destroy:"
" minor name \"%s\" is longer than"
" %d characters!\n",
name, MAX_NAME_LEN);
}
#endif
ddi_remove_minor_node(dip, name);
}
}
umad_release_hca_info(hca);
}
if (info->info_hcas) {
kmem_free(info->info_hcas,
info->info_hca_count * sizeof (umad_hca_info_t));
info->info_hca_count = 0;
info->info_hcas = NULL;
}
if (info->info_clnt_hdl != NULL) {
(void) ibt_detach(info->info_clnt_hdl);
info->info_clnt_hdl = NULL;
}
mutex_destroy(&info->info_mutex);
}
int
_init(void)
{
int rc;
rc = ddi_soft_state_init(&umad_statep, sizeof (umad_info_t), 0);
if (rc != 0)
goto err;
rc = mod_install(&modlinkage);
if (rc != 0)
ddi_soft_state_fini(&umad_statep);
err:
return (rc);
}
int
_info(struct modinfo *modinfop)
{
int rc;
rc = mod_info(&modlinkage, modinfop);
return (rc);
}
int
_fini(void)
{
int rc;
if ((rc = mod_remove(&modlinkage)) == 0)
ddi_soft_state_fini(&umad_statep);
return (rc);
}
static int
umad_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int rc;
unsigned int i;
unsigned int j;
umad_hca_info_t hca;
umad_info_t *info;
char name[MAX_NAME_LEN];
unsigned int minor_name;
switch (cmd) {
case DDI_ATTACH:
if (ddi_soft_state_zalloc(umad_statep, UMAD_INSTANCE)
!= DDI_SUCCESS)
goto err1;
info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE);
if (info == NULL)
goto err2;
info->info_dip = dip;
mutex_init(&info->info_mutex, NULL, MUTEX_DRIVER, NULL);
rc = umad_init_driver_info(info);
if (rc != 0)
goto err3;
rc = ddi_prop_update_int(DDI_DEV_T_NONE, dip,
"abi_version", IB_USER_MAD_ABI_VERSION);
if (rc != 0)
goto err3;
minor_name = 0;
for (i = 0; i < info->info_hca_count; i++) {
hca = info->info_hcas[i];
for (j = 0; j < hca.hca_nports; j++) {
size_t n;
dev_t minor_dev;
umad_port_info_t *port = &hca.hca_ports[j];
port->port_minor_name = minor_name;
n = snprintf(name, sizeof (name), "umad%d",
minor_name);
#if defined(DEBUG)
if (n > sizeof (name)) {
SOL_OFS_DPRINTF_L5(sol_umad_dbg_str,
"umad_attach: "
"name \"%s\" longer than %d!\n",
name, MAX_NAME_LEN);
}
#endif
rc = ddi_create_minor_node(dip, name, S_IFCHR,
GET_UMAD_MINOR(i, j), DDI_PSEUDO, 0);
if (rc != DDI_SUCCESS)
goto err3;
minor_dev = makedevice(ddi_driver_major(dip),
GET_UMAD_MINOR(i, j));
rc = ddi_prop_update_int(minor_dev, dip,
"vendor-id", hca.hca_attr.hca_vendor_id);
if (rc != DDI_SUCCESS)
goto err3;
rc = ddi_prop_update_int(minor_dev, dip,
"device-id", hca.hca_attr.hca_device_id);
if (rc != DDI_SUCCESS)
goto err3;
rc = ddi_prop_update_int(minor_dev, dip,
"hca-instance", i);
if (rc != DDI_SUCCESS)
goto err3;
rc = ddi_prop_update_int(minor_dev, dip,
"port", j + 1);
if (rc != DDI_SUCCESS)
goto err3;
port->port_has_umad_minor_node = 1;
n = snprintf(name, sizeof (name), "issm%d",
minor_name);
#if defined(DEBUG)
if (n > sizeof (name)) {
SOL_OFS_DPRINTF_L5(sol_umad_dbg_str,
"umad_attach: "
"name \"%s\" longer than %d!\n",
name, MAX_NAME_LEN);
}
#endif
rc = ddi_create_minor_node(dip, name, S_IFCHR,
GET_ISSM_MINOR(i, j), DDI_PSEUDO, 0);
if (rc != DDI_SUCCESS)
goto err3;
minor_dev = makedevice(ddi_driver_major(dip),
GET_ISSM_MINOR(i, j));
rc = ddi_prop_update_int(minor_dev, dip,
"vendor-id", hca.hca_attr.hca_vendor_id);
if (rc != DDI_SUCCESS)
goto err3;
rc = ddi_prop_update_int(minor_dev, dip,
"device-id", hca.hca_attr.hca_device_id);
if (rc != DDI_SUCCESS)
goto err3;
rc = ddi_prop_update_int(minor_dev, dip,
"hca-instance", i);
if (rc != DDI_SUCCESS)
goto err3;
rc = ddi_prop_update_int(minor_dev, dip,
"port", j + 1);
if (rc != DDI_SUCCESS)
goto err3;
port->port_has_issm_minor_node = 1;
minor_name++;
}
}
ddi_report_dev(dip);
break;
default:
goto err1;
}
rc = DDI_SUCCESS;
return (rc);
err3:
umad_context_destroy(dip, info);
err2:
ddi_soft_state_free(umad_statep, UMAD_INSTANCE);
err1:
rc = DDI_FAILURE;
return (rc);
}
static int
umad_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
int rc = DDI_SUCCESS;
umad_info_t *info;
switch (cmd) {
case DDI_DETACH:
info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE);
umad_context_destroy(dip, info);
ddi_soft_state_free(umad_statep, UMAD_INSTANCE);
break;
default:
rc = DDI_FAILURE;
break;
}
return (rc);
}
static int
umad_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
{
int rc;
#if defined(__lint)
extern void dummy2(void *);
dummy2(arg);
#endif
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
*resultp = (void *)dip;
break;
case DDI_INFO_DEVT2INSTANCE:
*resultp = (void *)UMAD_INSTANCE;
rc = DDI_SUCCESS;
break;
default:
rc = DDI_FAILURE;
break;
}
return (rc);
}
static int
umad_prop_op(
dev_t dev,
dev_info_t *dip,
ddi_prop_op_t prop_op,
int flags,
char *name,
caddr_t valuep,
int *lengthp)
{
int rc;
rc = ddi_prop_op(dev, dip, prop_op, flags, name, valuep, lengthp);
return (rc);
}
static const uint8_t *
umad_get_mad_classes_by_ibmf_class(enum _ibmf_client_type_t ibmf_class)
{
const struct ibmf_class_to_mad_type *entry;
for (entry = &ibmf_class_to_mad_types[0];
entry->ibmf_class != 0;
++entry) {
if (ibmf_class == entry->ibmf_class)
return (entry->mad_types);
}
return (NULL);
}
static umad_agent_t *
umad_get_agent_by_id(umad_uctx_t *uctx, uint32_t agent_id)
{
umad_agent_t *agent;
llist_head_t *entry;
ASSERT(MUTEX_HELD(&uctx->uctx_lock));
list_for_each(entry, &uctx->uctx_agent_list) {
agent = entry->ptr;
if (agent_id == agent->agent_req.id)
return (agent);
}
return (NULL);
}
static umad_agent_t *
umad_get_agent_by_class(umad_uctx_t *uctx, uint8_t agent_class)
{
umad_agent_t *agent;
llist_head_t *entry;
ASSERT(MUTEX_HELD(&uctx->uctx_lock));
list_for_each(entry, &uctx->uctx_agent_list) {
agent = entry->ptr;
if (agent_class == agent->agent_req.mgmt_class)
return (agent);
}
return (NULL);
}
static int
umad_register_agent(struct umad_agent_s *agent)
{
uint8_t mgmt_class_num = agent->agent_req.mgmt_class;
umad_port_info_t *port = agent->agent_uctx->uctx_port;
const umad_hca_info_t *hca = port->port_hca;
int rc;
ibmf_register_info_t reg_info = {0, };
ibmf_impl_caps_t impl_caps = {0, };
uint_t flags = 0;
enum _ibmf_client_type_t ibmf_class;
const uint8_t *umad_types;
struct ibmf_reg_info *ibmf_info;
llist_head_t *entry;
boolean_t found = B_FALSE;
ASSERT(MUTEX_HELD(&agent->agent_uctx->uctx_lock));
ibmf_class = umad_type_to_ibmf_class[mgmt_class_num];
if (ibmf_class == 0) {
rc = EINVAL;
goto done;
}
umad_types = umad_get_mad_classes_by_ibmf_class(ibmf_class);
if (umad_types != NULL) {
struct umad_agent_s *other_agent;
for (; *umad_types != 0; ++umad_types) {
other_agent = umad_get_agent_by_class(agent->agent_uctx,
*umad_types);
if (other_agent != NULL) {
struct ibmf_reg_info *ibmf_reg;
ibmf_reg = other_agent->agent_reg;
agent->agent_reg = ibmf_reg;
if (other_agent->agent_flags
& UMAD_HANDLING_ASYNC) {
agent->agent_flags |=
UMAD_HANDLING_ASYNC;
}
mutex_enter(&ibmf_reg->ibmf_reg_lock);
while (ibmf_reg->ibmf_flags
& UMAD_IBMF_UNREGISTERING) {
cv_wait(&ibmf_reg->ibmf_cv,
&ibmf_reg->ibmf_reg_lock);
}
ibmf_reg->ibmf_reg_refcnt++;
mutex_exit(&ibmf_reg->ibmf_reg_lock);
return (0);
}
}
}
mutex_enter(&port->port_lock);
if (! llist_empty(&port->port_ibmf_regs)) {
list_for_each(entry, &port->port_ibmf_regs) {
ibmf_info = (struct ibmf_reg_info *)entry->ptr;
if (ibmf_info->ibmf_class == ibmf_class) {
found = B_TRUE;
break;
}
}
}
mutex_exit(&port->port_lock);
if (found) {
mutex_enter(&ibmf_info->ibmf_reg_lock);
ibmf_info->ibmf_reg_refcnt++;
agent->agent_reg = ibmf_info;
mutex_exit(&ibmf_info->ibmf_reg_lock);
return (0);
}
ibmf_info = kmem_zalloc(sizeof (struct ibmf_reg_info), KM_SLEEP);
mutex_init(&ibmf_info->ibmf_reg_lock, NULL, MUTEX_DRIVER, NULL);
cv_init(&ibmf_info->ibmf_cv, NULL, CV_DRIVER, NULL);
if (agent->agent_req.rmpp_version)
flags = IBMF_REG_FLAG_RMPP;
reg_info.ir_ci_guid = hca->hca_guid;
reg_info.ir_port_num = port->port_num;
reg_info.ir_client_class = ibmf_class;
mutex_enter(&ibmf_info->ibmf_reg_lock);
rc = ibmf_register(®_info, IBMF_VERSION, flags, NULL, NULL,
&ibmf_info->ibmf_reg_handle, &impl_caps);
if (rc != IBMF_SUCCESS) {
mutex_exit(&ibmf_info->ibmf_reg_lock);
kmem_free(ibmf_info, sizeof (*ibmf_info));
} else {
rc = ibmf_setup_async_cb(ibmf_info->ibmf_reg_handle,
IBMF_QP_HANDLE_DEFAULT, umad_unsolicited_cb,
(void *)ibmf_info, 0);
if (rc != IBMF_SUCCESS) {
(void) ibmf_unregister(&ibmf_info->ibmf_reg_handle, 0);
mutex_exit(&ibmf_info->ibmf_reg_lock);
kmem_free(ibmf_info, sizeof (*ibmf_info));
} else {
ibmf_info->ibmf_reg_refcnt++;
ibmf_info->ibmf_reg_uctx = agent->agent_uctx;
ibmf_info->ibmf_class = ibmf_class;
agent->agent_reg = ibmf_info;
agent->agent_flags |= UMAD_HANDLING_ASYNC;
mutex_exit(&ibmf_info->ibmf_reg_lock);
entry = kmem_zalloc(sizeof (llist_head_t), KM_SLEEP);
entry->ptr = ibmf_info;
mutex_enter(&port->port_lock);
llist_add(entry, &port->port_ibmf_regs);
mutex_exit(&port->port_lock);
}
}
done:
return (rc);
}
static int
umad_queue_mad_msg(struct umad_agent_s *agent, ibmf_msg_t *ibmf_msg)
{
int rc;
ib_umad_msg_t *umad_msg;
umad_uctx_t *uctx = agent->agent_uctx;
if (agent->agent_uctx == NULL) {
rc = ENOENT;
goto err1;
}
umad_msg = kmem_zalloc(sizeof (*umad_msg), KM_NOSLEEP);
if (umad_msg == NULL) {
rc = ENOMEM;
goto err1;
}
umad_msg->umad_msg_hdr.id = agent->agent_req.id;
umad_msg->umad_msg_hdr.status = ibmf_msg->im_msg_status;
umad_msg->umad_msg_hdr.length = IB_MGMT_MAD_HDR +
ibmf_msg->im_msgbufs_recv.im_bufs_cl_hdr_len +
ibmf_msg->im_msgbufs_recv.im_bufs_cl_data_len;
umad_msg->umad_msg_hdr.qpn =
htonl(ibmf_msg->im_local_addr.ia_remote_qno);
umad_msg->umad_msg_hdr.lid =
htons(ibmf_msg->im_local_addr.ia_remote_lid);
umad_msg->umad_msg_hdr.sl =
htonl(ibmf_msg->im_local_addr.ia_service_level);
umad_msg->umad_msg_ibmf_msg = ibmf_msg;
mutex_enter(&uctx->uctx_recv_lock);
if (! add_genlist(&uctx->uctx_recv_list, (uintptr_t)umad_msg, agent)) {
kmem_free(umad_msg, sizeof (*umad_msg));
mutex_exit(&uctx->uctx_recv_lock);
rc = ENOMEM;
goto err1;
}
mutex_exit(&uctx->uctx_recv_lock);
cv_broadcast(&uctx->uctx_recv_cv);
pollwakeup(&uctx->uctx_pollhead, POLLIN | POLLRDNORM);
rc = 0;
err1:
return (rc);
}
static void
umad_release_uctx(umad_uctx_t *uctx)
{
ASSERT(genlist_empty(&uctx->uctx_recv_list));
ASSERT(llist_empty(&uctx->uctx_agent_list));
cv_destroy(&uctx->uctx_recv_cv);
mutex_destroy(&uctx->uctx_lock);
mutex_destroy(&uctx->uctx_recv_lock);
}
static int
umad_open(dev_t *dev, int flag, int otyp, cred_t *cred)
{
umad_info_t *info;
minor_t minor;
minor_t ctx_minor;
int node_id, port_num;
int rc = DDI_SUCCESS;
umad_hca_info_t *hca;
umad_port_info_t *port;
umad_uctx_t *uctx;
#if defined(__lint)
extern void dummy(int);
dummy(flag);
#endif
rc = priv_policy(cred, PRIV_SYS_NET_CONFIG, B_FALSE, EACCES, NULL);
if (rc != 0)
return (rc);
info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE);
if (info == NULL) {
rc = ENXIO;
goto err1;
}
if (otyp != OTYP_CHR)
return (EINVAL);
minor = getminor(*dev);
node_id = GET_NODE(minor);
port_num = GET_PORT(minor);
hca = &info->info_hcas[node_id];
port = &hca->hca_ports[port_num];
if (ISSM_MINOR(minor)) {
ibt_status_t rc;
mutex_enter(&port->port_lock);
if (port->port_issm_open_cnt) {
mutex_exit(&port->port_lock);
rc = EBUSY;
goto err1;
}
port->port_issm_open_cnt++;
mutex_exit(&port->port_lock);
rc = ibt_modify_port(hca->hca_handle, port->port_num,
IBT_PORT_SET_SM, 0);
if (rc) {
mutex_enter(&port->port_lock);
port->port_issm_open_cnt--;
mutex_exit(&port->port_lock);
goto err1;
}
} else {
unsigned int uctx_num;
uctx = kmem_zalloc(sizeof (umad_uctx_t), KM_SLEEP);
mutex_init(&uctx->uctx_lock, NULL, MUTEX_DRIVER, NULL);
cv_init(&uctx->uctx_recv_cv, NULL, CV_DRIVER, NULL);
init_genlist(&uctx->uctx_recv_list);
mutex_init(&uctx->uctx_recv_lock, NULL, MUTEX_DRIVER, NULL);
llist_head_init(&uctx->uctx_agent_list, NULL);
uctx->uctx_port = port;
mutex_enter(&info->info_mutex);
mutex_enter(&port->port_lock);
for (uctx_num = 0; uctx_num < MAX_UCTX; uctx_num++) {
if (info->info_uctx[uctx_num] == NULL)
break;
}
if (uctx_num == MAX_UCTX) {
mutex_exit(&port->port_lock);
mutex_exit(&info->info_mutex);
umad_release_uctx(uctx);
rc = EBUSY;
goto err1;
}
ctx_minor = GET_NEW_UCTX_MINOR(minor, uctx_num);
info->info_uctx[uctx_num] = uctx;
*dev = makedevice(getmajor(*dev), ctx_minor);
mutex_exit(&port->port_lock);
mutex_exit(&info->info_mutex);
}
err1:
return (rc);
}
static int
umad_close(dev_t dev, int flag, int otyp, cred_t *cred)
{
umad_info_t *info;
minor_t minor;
int rc = DDI_SUCCESS;
umad_port_info_t *port;
umad_uctx_t *uctx;
llist_head_t *lentry;
llist_head_t *lentry_temp;
umad_agent_t *agent;
int port_num;
umad_hca_info_t *hca;
int node_id;
info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE);
if (info == NULL) {
rc = ENXIO;
goto err1;
}
minor = getminor(dev);
node_id = GET_NODE(minor);
port_num = GET_PORT(minor);
hca = &info->info_hcas[node_id];
port = &hca->hca_ports[port_num];
ASSERT(port != NULL);
if (ISSM_MINOR(minor)) {
(void) ibt_modify_port(hca->hca_handle, port->port_num,
IBT_PORT_RESET_SM, 0);
mutex_enter(&port->port_lock);
port->port_issm_open_cnt--;
mutex_exit(&port->port_lock);
ASSERT(port->port_issm_open_cnt == 0);
} else {
mutex_enter(&info->info_mutex);
uctx = info->info_uctx[GET_UCTX(minor)];
ASSERT(uctx != NULL);
mutex_enter(&uctx->uctx_lock);
lentry = uctx->uctx_agent_list.nxt;
lentry_temp = lentry->nxt;
while (lentry != &uctx->uctx_agent_list) {
ASSERT(lentry);
agent = lentry->ptr;
(void) umad_unregister(&agent->agent_req, uctx);
lentry = lentry_temp;
lentry_temp = lentry->nxt;
}
mutex_exit(&uctx->uctx_lock);
umad_release_uctx(uctx);
kmem_free(uctx, sizeof (umad_uctx_t));
info->info_uctx[GET_UCTX(minor)] = NULL;
mutex_exit(&info->info_mutex);
}
err1:
return (rc);
}
static int
umad_get_mad_clhdr_offset(uint8_t mgmt_class)
{
switch (mgmt_class) {
case MAD_MGMT_CLASS_SUBN_LID_ROUTED:
case MAD_MGMT_CLASS_SUBN_DIRECT_ROUTE:
case MAD_MGMT_CLASS_PERF:
case MAD_MGMT_CLASS_BM:
case MAD_MGMT_CLASS_DEV_MGT:
case MAD_MGMT_CLASS_COMM_MGT:
return (IB_MGMT_MAD_HDR);
case MAD_MGMT_CLASS_SUBN_ADM:
return (IB_MGMT_RMPP_HDR);
case MAD_MGMT_CLASS_SNMP:
return (IB_MGMT_SNMP_HDR);
default:
if (((mgmt_class >= MAD_MGMT_CLASS_VENDOR_START) &&
(mgmt_class <= MAD_MGMT_CLASS_VENDOR_END)) ||
((mgmt_class >= MAD_MGMT_CLASS_APPLICATION_START) &&
(mgmt_class <= MAD_MGMT_CLASS_APPLICATION_END)))
return (IB_MGMT_MAD_HDR);
else if ((mgmt_class >= MAD_MGMT_CLASS_VENDOR2_START) &&
(mgmt_class <= MAD_MGMT_CLASS_VENDOR2_END))
return (IB_MGMT_RMPP_HDR);
else {
#if defined(DEBUG)
SOL_OFS_DPRINTF_L5(sol_umad_dbg_str,
"umad_get_mad_clhdr_offset:"
" got illegal management class %d", mgmt_class);
#endif
return (0);
}
}
}
static int
umad_get_mad_data_offset(uint8_t mgmt_class)
{
switch (mgmt_class) {
case MAD_MGMT_CLASS_SUBN_LID_ROUTED:
case MAD_MGMT_CLASS_SUBN_DIRECT_ROUTE:
case MAD_MGMT_CLASS_PERF:
case MAD_MGMT_CLASS_BM:
case MAD_MGMT_CLASS_DEV_MGT:
case MAD_MGMT_CLASS_COMM_MGT:
return (IB_MGMT_MAD_HDR);
case MAD_MGMT_CLASS_SUBN_ADM:
return (IB_MGMT_SA_HDR);
case MAD_MGMT_CLASS_SNMP:
return (IB_MGMT_SNMP_DATA);
default:
if (((mgmt_class >= MAD_MGMT_CLASS_VENDOR_START) &&
(mgmt_class <= MAD_MGMT_CLASS_VENDOR_END)) ||
((mgmt_class >= MAD_MGMT_CLASS_APPLICATION_START) &&
(mgmt_class <= MAD_MGMT_CLASS_APPLICATION_END)))
return (IB_MGMT_MAD_HDR);
else if ((mgmt_class >= MAD_MGMT_CLASS_VENDOR2_START) &&
(mgmt_class <= MAD_MGMT_CLASS_VENDOR2_END))
return (IB_MGMT_VENDOR_HDR);
else {
#if defined(DEBUG)
SOL_OFS_DPRINTF_L5(sol_umad_dbg_str,
"umad_get_mad_clhdr_offset:"
" got illegal management class %d", mgmt_class);
#endif
return (0);
}
}
}
static int
umad_read(dev_t dev, struct uio *uiop, cred_t *credp)
{
int minor;
size_t data_len;
int rc = 0;
umad_port_info_t *port;
umad_info_t *info;
umad_uctx_t *uctx;
genlist_entry_t *entry;
ib_umad_msg_t *umad_msg;
ibmf_msg_t *ibmf_msg;
struct umad_agent_s *agent;
ib_mad_hdr_t *ib_mad_hdr;
ssize_t start_resid;
info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE);
if (info == NULL) {
rc = ENXIO;
goto err1;
}
minor = getminor(dev);
if (ISSM_MINOR(minor)) {
rc = ENXIO;
goto err1;
}
mutex_enter(&info->info_mutex);
uctx = info->info_uctx[GET_UCTX(minor)];
mutex_exit(&info->info_mutex);
ASSERT(uctx != NULL);
port = uctx->uctx_port;
ASSERT(port != NULL);
start_resid = uiop->uio_resid;
while (rc == 0 && uiop->uio_resid > 0) {
mutex_enter(&uctx->uctx_recv_lock);
if (! (uiop->uio_fmode & (FNDELAY | FNONBLOCK))) {
while (genlist_empty(&uctx->uctx_recv_list)) {
if (cv_wait_sig(&uctx->uctx_recv_cv,
&uctx->uctx_recv_lock) == 0) {
mutex_exit(&uctx->uctx_recv_lock);
return (EINTR);
}
}
} else if (genlist_empty(&uctx->uctx_recv_list)) {
mutex_exit(&uctx->uctx_recv_lock);
if (uiop->uio_resid != start_resid)
return (0);
return (EAGAIN);
}
entry = remove_genlist_head(&uctx->uctx_recv_list);
mutex_exit(&uctx->uctx_recv_lock);
ASSERT(entry != NULL);
agent = entry->data_context;
umad_msg = (ib_umad_msg_t *)entry->data;
ibmf_msg = (ibmf_msg_t *)umad_msg->umad_msg_ibmf_msg;
data_len = min(uiop->uio_resid, sizeof (struct ib_user_mad));
rc = uiomove(umad_msg, data_len, UIO_READ, uiop);
if (rc)
goto err2;
if (ibmf_msg->im_msg_status == IBMF_SUCCESS) {
ib_mad_hdr = (ib_mad_hdr_t *)
ibmf_msg->im_msgbufs_recv.im_bufs_mad_hdr;
data_len =
umad_get_mad_clhdr_offset(ib_mad_hdr->MgmtClass);
data_len = min(uiop->uio_resid, data_len);
rc = uiomove(ibmf_msg->im_msgbufs_recv.im_bufs_mad_hdr,
data_len, UIO_READ, uiop);
if (rc)
goto err2;
data_len = min(uiop->uio_resid,
ibmf_msg->im_msgbufs_recv.im_bufs_cl_hdr_len);
rc = uiomove(ibmf_msg->im_msgbufs_recv.im_bufs_cl_hdr,
data_len, UIO_READ, uiop);
if (rc)
goto err2;
data_len = min(uiop->uio_resid,
ibmf_msg->im_msgbufs_recv.im_bufs_cl_data_len);
rc = uiomove(ibmf_msg->im_msgbufs_recv.im_bufs_cl_data,
data_len, UIO_READ, uiop);
if (rc)
goto err2;
}
rc = ibmf_free_msg(agent->agent_reg->ibmf_reg_handle,
&ibmf_msg);
kmem_free(umad_msg, sizeof (*umad_msg));
if (rc != IBMF_SUCCESS) {
#if defined(DEBUG)
SOL_OFS_DPRINTF_L5(sol_umad_dbg_str,
"umad_read:"
" ibmf_free_msg failed %d", rc);
#endif
goto err1;
}
}
err2:
if (rc) {
rc = ibmf_free_msg(agent->agent_reg->ibmf_reg_handle,
&ibmf_msg);
kmem_free(umad_msg, sizeof (*umad_msg));
if (rc != IBMF_SUCCESS) {
#if defined(DEBUG)
SOL_OFS_DPRINTF_L5(sol_umad_dbg_str,
"umad_read:"
" ibmf_free_msg failed %d", rc);
#endif
}
}
err1:
return (rc);
}
static void
umad_solicited_cb(ibmf_handle_t ibmf_handle, ibmf_msg_t *msgp, void *args)
{
struct umad_send *umad_ctx = (struct umad_send *)args;
umad_agent_t *agent = umad_ctx->send_agent;
int rc;
#if defined(__lint)
ibmf_handle = 0;
#endif
msgp->im_msgbufs_send.im_bufs_mad_hdr = NULL;
msgp->im_msgbufs_send.im_bufs_cl_hdr = NULL;
msgp->im_msgbufs_send.im_bufs_cl_hdr_len = 0;
msgp->im_msgbufs_send.im_bufs_cl_data = NULL;
msgp->im_msgbufs_send.im_bufs_cl_data_len = 0;
kmem_free(umad_ctx, umad_ctx->send_len);
mutex_enter(&agent->agent_lock);
agent->agent_outstanding_msgs--;
ASSERT(agent->agent_outstanding_msgs >= 0);
if (agent->agent_flags & UMAD_AGENT_UNREGISTERING) {
if (agent->agent_outstanding_msgs == 0)
cv_signal(&agent->agent_cv);
}
mutex_exit(&agent->agent_lock);
if (umad_queue_mad_msg(agent, msgp))
goto bad;
return;
bad:
rc = ibmf_free_msg(agent->agent_reg->ibmf_reg_handle, &msgp);
ASSERT(rc == IBMF_SUCCESS);
}
static int
umad_write(dev_t dev, struct uio *uiop, cred_t *credp)
{
int rc, rc2;
int mad_offset, flags = 0;
int hdr_len;
size_t len = uiop->uio_resid;
minor_t minor;
ibmf_retrans_t mad_retrans;
umad_info_t *info;
umad_port_info_t *port;
umad_uctx_t *uctx;
umad_agent_t *agent;
struct ib_user_mad *user_mad;
ibmf_msg_t *ibmf_msg;
ib_mad_hdr_t *ib_mad_hdr;
struct umad_send *umad_ctx;
boolean_t need_callback;
ibt_status_t status;
ib_pkey_t pkey;
info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE);
if (info == NULL) {
rc = ENXIO;
goto err1;
}
minor = getminor(dev);
if (ISSM_MINOR(minor)) {
rc = ENXIO;
goto err1;
}
mutex_enter(&info->info_mutex);
uctx = info->info_uctx[GET_UCTX(minor)];
mutex_exit(&info->info_mutex);
ASSERT(uctx != NULL);
port = uctx->uctx_port;
ASSERT(port != NULL);
umad_ctx = kmem_zalloc(sizeof (struct umad_send) + len, KM_SLEEP);
umad_ctx->send_len = sizeof (struct umad_send) + len;
user_mad = (struct ib_user_mad *)umad_ctx->send_umad;
rc = uiomove(user_mad, len, UIO_WRITE, uiop);
if (rc != 0)
goto err3;
mutex_enter(&uctx->uctx_lock);
agent = umad_get_agent_by_id(uctx, user_mad->hdr.id);
mutex_exit(&uctx->uctx_lock);
if (! agent) {
rc = EINVAL;
goto err3;
}
mutex_enter(&agent->agent_lock);
if (agent->agent_flags & UMAD_AGENT_UNREGISTERING) {
mutex_exit(&agent->agent_lock);
rc = EINVAL;
goto err3;
}
rc = ibmf_alloc_msg(agent->agent_reg->ibmf_reg_handle,
IBMF_ALLOC_NOSLEEP, &ibmf_msg);
if (rc != IBMF_SUCCESS) {
mutex_exit(&agent->agent_lock);
goto err3;
}
ib_mad_hdr = (ib_mad_hdr_t *)user_mad->data;
hdr_len = umad_get_mad_data_offset(ib_mad_hdr->MgmtClass);
#if defined(__FUTURE_FEATURE__)
if (mad.addr.grh_present) {
memcpy(&ibmf_msg->im_global_addr.ig_recver_gid, mad.addr.gid,
16);
im_global_addr.ig_sender_gid = get_gid(umad->addr.gid_index);
ibmf_msg->im_global_addr.ig_tclass = mad.addr.traffic_class;
ibmf_msg->im_global_addr.ig_hop_limit = mad.addr.hop_limit;
ibmf_msg->im_global_addr.ig_flow_label = mad.addr.flow_label;
}
#endif
ibmf_msg->im_local_addr.ia_local_lid = port->port_lid;
ibmf_msg->im_local_addr.ia_remote_lid = ntohs(user_mad->hdr.lid);
ibmf_msg->im_local_addr.ia_remote_qno = ntohl(user_mad->hdr.qpn);
ibmf_msg->im_local_addr.ia_q_key = ntohl(user_mad->hdr.qkey);
ibmf_msg->im_local_addr.ia_service_level = user_mad->hdr.sl;
status = ibt_index2pkey(port->port_hca->hca_handle,
port->port_num, user_mad->hdr.pkey_index, &pkey);
if (status != IBT_SUCCESS) {
#if defined(DEBUG)
SOL_OFS_DPRINTF_L5(sol_umad_dbg_str,
"umad_write: ibt_index2pkey failed %d",
status);
#endif
}
else
ibmf_msg->im_local_addr.ia_p_key = ntohs(pkey);
if ((ib_mad_hdr->R_Method & 0x80) == 0)
flags = IBMF_MSG_TRANS_FLAG_SEQ;
if (agent->agent_req.rmpp_version > 0) {
ibmf_rmpp_hdr_t *rmpp_hdr;
rmpp_hdr = (ibmf_rmpp_hdr_t *)(ib_mad_hdr + 1);
if (rmpp_hdr->rmpp_flags != 0)
flags |= IBMF_MSG_TRANS_FLAG_RMPP;
}
ibmf_msg->im_msgbufs_send.im_bufs_mad_hdr = ib_mad_hdr;
hdr_len = umad_get_mad_clhdr_offset(ib_mad_hdr->MgmtClass);
mad_offset = umad_get_mad_data_offset(ib_mad_hdr->MgmtClass);
ibmf_msg->im_msgbufs_send.im_bufs_cl_hdr =
(unsigned char *)user_mad +
offsetof(struct ib_user_mad, data) + hdr_len;
ibmf_msg->im_msgbufs_send.im_bufs_cl_hdr_len =
mad_offset - hdr_len;
ibmf_msg->im_msgbufs_send.im_bufs_cl_data =
(unsigned char *) user_mad + (sizeof (struct ib_user_mad) +
mad_offset);
ibmf_msg->im_msgbufs_send.im_bufs_cl_data_len =
len - sizeof (struct ib_user_mad) - mad_offset;
mad_retrans.retrans_retries = user_mad->hdr.retries;
mad_retrans.retrans_rtv = 0;
mad_retrans.retrans_rttv = 0;
mad_retrans.retrans_trans_to = 0;
umad_ctx->send_agent = agent;
need_callback = (flags & IBMF_MSG_TRANS_FLAG_SEQ) != 0;
if (need_callback)
agent->agent_outstanding_msgs++;
mutex_exit(&agent->agent_lock);
rc = ibmf_msg_transport(agent->agent_reg->ibmf_reg_handle,
IBMF_QP_HANDLE_DEFAULT,
ibmf_msg, &mad_retrans,
need_callback ? umad_solicited_cb : NULL,
umad_ctx, flags);
if (! need_callback) {
rc2 = ibmf_free_msg(agent->agent_reg->ibmf_reg_handle,
&ibmf_msg);
ASSERT(rc2 == IBMF_SUCCESS);
if (rc != IBMF_SUCCESS) {
rc = EIO;
goto err3;
}
} else if (rc != IBMF_SUCCESS) {
mutex_enter(&agent->agent_lock);
agent->agent_outstanding_msgs--;
ASSERT(agent->agent_outstanding_msgs >= 0);
if (agent->agent_flags & UMAD_AGENT_UNREGISTERING) {
if (agent->agent_outstanding_msgs == 0)
cv_signal(&agent->agent_cv);
}
mutex_exit(&agent->agent_lock);
rc2 = ibmf_free_msg(agent->agent_reg->ibmf_reg_handle,
&ibmf_msg);
ASSERT(rc2 == IBMF_SUCCESS);
rc = EIO;
goto err3;
}
return (0);
err3:
kmem_free(umad_ctx, umad_ctx->send_len);
err1:
return (rc);
}
static void
umad_async_handler(
void *private,
ibt_hca_hdl_t hca_hdl,
ibt_async_code_t code,
ibt_async_event_t *event)
{
}
static int
umad_pkey_enable()
{
return (DDI_SUCCESS);
}
static int
umad_ioctl(
dev_t dev,
int cmd,
intptr_t arg,
int mode,
cred_t *credp,
int *rvalp)
{
int rc = 0;
int minor;
umad_info_t *info;
umad_port_info_t *port;
umad_uctx_t *uctx;
struct ib_user_mad_reg_req req = {0};
info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE);
if (info == NULL) {
rc = ENXIO;
goto err1;
}
minor = getminor(dev);
if (ISSM_MINOR(minor)) {
rc = ENXIO;
goto err1;
}
mutex_enter(&info->info_mutex);
uctx = info->info_uctx[GET_UCTX(minor)];
mutex_exit(&info->info_mutex);
ASSERT(uctx != NULL);
port = uctx->uctx_port;
ASSERT(port != NULL);
if (cmd == IB_USER_MAD_ENABLE_PKEY)
return (umad_pkey_enable());
if (ddi_copyin((void *) arg, &req, sizeof (req), mode) != 0) {
rc = EFAULT;
goto err1;
}
switch (cmd) {
case IB_USER_MAD_REGISTER_AGENT:
mutex_enter(&uctx->uctx_lock);
rc = umad_register(&req, uctx);
mutex_exit(&uctx->uctx_lock);
if (rc)
goto err1;
rc = ddi_copyout(&req, (void *) arg, sizeof (req), mode);
if (rc) {
mutex_enter(&uctx->uctx_lock);
(void) umad_unregister(&req, uctx);
mutex_exit(&uctx->uctx_lock);
rc = EFAULT;
goto err1;
}
break;
case IB_USER_MAD_UNREGISTER_AGENT:
mutex_enter(&uctx->uctx_lock);
rc = umad_unregister(&req, uctx);
mutex_exit(&uctx->uctx_lock);
break;
default:
rc = DDI_FAILURE;
}
err1:
return (rc);
}
static int
umad_get_new_agent_id(umad_uctx_t *uctx)
{
boolean_t found;
unsigned int agent_id;
llist_head_t *entry;
agent_id = 0;
ASSERT(MUTEX_HELD(&uctx->uctx_lock));
for (;;) {
found = B_FALSE;
list_for_each(entry, &uctx->uctx_agent_list) {
umad_agent_t *agent = entry->ptr;
if (agent_id == agent->agent_req.id) {
found = B_TRUE;
break;
}
}
if (! found)
break;
agent_id++;
}
return (agent_id);
}
static int
umad_register(struct ib_user_mad_reg_req *req, umad_uctx_t *uctx)
{
int rc = IBMF_SUCCESS;
umad_agent_t *agent = NULL;
umad_port_info_t *port;
if ((req->qpn != 0) && (req->qpn != 1)) {
rc = EINVAL;
goto err1;
}
ASSERT(MUTEX_HELD(&uctx->uctx_lock));
port = uctx->uctx_port;
ASSERT(port != NULL);
agent = umad_get_agent_by_class(uctx, req->mgmt_class);
if (agent != NULL)
return (IBMF_PORT_IN_USE);
agent = kmem_zalloc(sizeof (umad_agent_t), KM_SLEEP);
mutex_init(&agent->agent_lock, NULL, MUTEX_DRIVER, NULL);
cv_init(&agent->agent_cv, NULL, CV_DRIVER, NULL);
agent->agent_req = *req;
agent->agent_uctx = uctx;
llist_head_init(&agent->agent_list, agent);
agent->agent_req.id = req->id = umad_get_new_agent_id(uctx);
rc = umad_register_agent(agent);
if (rc)
goto err1;
llist_add(&agent->agent_list, &uctx->uctx_agent_list);
return (0);
err1:
if (rc) {
if (agent) {
cv_destroy(&agent->agent_cv);
mutex_destroy(&agent->agent_lock);
kmem_free(agent, sizeof (umad_agent_t));
}
}
return (rc);
}
static int
umad_unregister(struct ib_user_mad_reg_req *req, umad_uctx_t *uctx)
{
int agent_id = req->id;
umad_agent_t *agent;
int rc;
genlist_entry_t *entry;
struct ibmf_reg_info *ibmf_info;
boolean_t did_ibmf_unregister;
umad_port_info_t *port;
ASSERT(MUTEX_HELD(&uctx->uctx_lock));
agent = umad_get_agent_by_id(uctx, agent_id);
if (agent == NULL) {
rc = EINVAL;
goto done;
}
mutex_enter(&agent->agent_lock);
while (agent->agent_outstanding_msgs != 0) {
agent->agent_flags |= UMAD_AGENT_UNREGISTERING;
cv_wait(&agent->agent_cv, &agent->agent_lock);
}
if (agent->agent_flags & UMAD_HANDLING_ASYNC)
agent->agent_reg->ibmf_reg_uctx = NULL;
mutex_exit(&agent->agent_lock);
llist_del(&agent->agent_list);
ibmf_info = agent->agent_reg;
mutex_enter(&ibmf_info->ibmf_reg_lock);
mutex_enter(&uctx->uctx_recv_lock);
while ((entry = remove_genlist_head(&uctx->uctx_recv_list))) {
ib_umad_msg_t *msg;
ibmf_msg_t *ibmf_msg;
mutex_exit(&uctx->uctx_recv_lock);
msg = (ib_umad_msg_t *)entry->data;
ibmf_msg = msg->umad_msg_ibmf_msg;
rc = ibmf_free_msg(ibmf_info->ibmf_reg_handle, &ibmf_msg);
ASSERT(rc == IBMF_SUCCESS);
kmem_free(msg, sizeof (*msg));
mutex_enter(&uctx->uctx_recv_lock);
}
mutex_exit(&uctx->uctx_recv_lock);
if (--ibmf_info->ibmf_reg_refcnt == 0) {
ibmf_info->ibmf_flags |= UMAD_IBMF_UNREGISTERING;
mutex_exit(&ibmf_info->ibmf_reg_lock);
rc = ibmf_tear_down_async_cb(ibmf_info->ibmf_reg_handle,
IBMF_QP_HANDLE_DEFAULT, 0);
#if defined(DEBUG)
if (rc) {
SOL_OFS_DPRINTF_L5(sol_umad_dbg_str,
"umad_unregister: failed "
"ibmf_tear_down_async_cb() error %d\n", rc);
}
#endif
mutex_enter(&uctx->uctx_recv_lock);
while ((entry = remove_genlist_head(&uctx->uctx_recv_list))) {
ib_umad_msg_t *msg;
ibmf_msg_t *ibmf_msg;
mutex_exit(&uctx->uctx_recv_lock);
msg = (ib_umad_msg_t *)entry->data;
ibmf_msg = msg->umad_msg_ibmf_msg;
rc = ibmf_free_msg(ibmf_info->ibmf_reg_handle,
&ibmf_msg);
ASSERT(rc == IBMF_SUCCESS);
kmem_free(msg, sizeof (*msg));
mutex_enter(&uctx->uctx_recv_lock);
}
mutex_exit(&uctx->uctx_recv_lock);
rc = ibmf_unregister(&ibmf_info->ibmf_reg_handle, 0);
#if defined(DEBUG)
if (rc) {
SOL_OFS_DPRINTF_L5(sol_umad_dbg_str,
"umad_unregister: failed "
"ibmf_unregister() error %d\n", rc);
}
#endif
mutex_enter(&ibmf_info->ibmf_reg_lock);
ibmf_info->ibmf_flags &= ~UMAD_IBMF_UNREGISTERING;
cv_signal(&ibmf_info->ibmf_cv);
mutex_exit(&ibmf_info->ibmf_reg_lock);
did_ibmf_unregister = B_TRUE;
} else {
mutex_exit(&ibmf_info->ibmf_reg_lock);
did_ibmf_unregister = B_FALSE;
}
if (did_ibmf_unregister) {
llist_head_t *entry;
struct ibmf_reg_info *ibmf_entry = NULL;
#if defined(DEBUG)
boolean_t found = B_FALSE;
#endif
port = uctx->uctx_port;
mutex_enter(&port->port_lock);
list_for_each(entry, &port->port_ibmf_regs) {
ibmf_entry = entry->ptr;
if (ibmf_info == ibmf_entry) {
#if defined(DEBUG)
found = B_TRUE;
#endif
break;
}
}
ASSERT(found);
llist_del(entry);
kmem_free(entry, sizeof (*entry));
mutex_exit(&port->port_lock);
kmem_free(ibmf_info, sizeof (*ibmf_info));
}
agent->agent_uctx = NULL;
cv_destroy(&agent->agent_cv);
mutex_destroy(&agent->agent_lock);
kmem_free(agent, sizeof (*agent));
rc = 0;
done:
return (rc);
}
static int
umad_poll(dev_t dev, short events, int anyyet, short *reventsp,
struct pollhead **phpp)
{
int minor;
umad_uctx_t *uctx;
umad_info_t *info;
short revent = 0;
info = ddi_get_soft_state(umad_statep, UMAD_INSTANCE);
if (info == NULL) {
return (ENXIO);
}
minor = getminor(dev);
if (ISSM_MINOR(minor)) {
return (ENXIO);
}
mutex_enter(&info->info_mutex);
uctx = info->info_uctx[GET_UCTX(minor)];
mutex_exit(&info->info_mutex);
ASSERT(uctx != NULL);
ASSERT(uctx->uctx_port != NULL);
if (events & POLLOUT) {
revent = POLLOUT;
} else if (events & (POLLIN | POLLRDNORM)) {
mutex_enter(&uctx->uctx_recv_lock);
if (! genlist_empty(&uctx->uctx_recv_list)) {
revent |= POLLIN | POLLRDNORM;
}
mutex_exit(&uctx->uctx_recv_lock);
}
if ((revent == 0 && !anyyet) || (events & POLLET)) {
*phpp = &uctx->uctx_pollhead;
}
*reventsp = revent;
return (0);
}
static void
umad_unsolicited_cb(ibmf_handle_t ibmf_handle, ibmf_msg_t *msgp, void *args)
{
struct ibmf_reg_info *ibmf_info = (struct ibmf_reg_info *)args;
struct umad_agent_s *agent;
ib_mad_hdr_t *mad_hdr;
int rc;
#if defined(__lint)
ibmf_handle = 0;
#endif
ASSERT(msgp->im_msgbufs_send.im_bufs_mad_hdr == NULL);
ASSERT(msgp->im_msgbufs_send.im_bufs_cl_data == NULL);
ASSERT(msgp->im_msgbufs_send.im_bufs_cl_data_len == 0);
mad_hdr = msgp->im_msgbufs_recv.im_bufs_mad_hdr;
mutex_enter(&ibmf_info->ibmf_reg_lock);
if (ibmf_info->ibmf_reg_uctx == NULL)
goto reject;
mutex_enter(&ibmf_info->ibmf_reg_uctx->uctx_lock);
agent = umad_get_agent_by_class(ibmf_info->ibmf_reg_uctx,
mad_hdr->MgmtClass);
mutex_exit(&ibmf_info->ibmf_reg_uctx->uctx_lock);
if (agent == NULL)
goto reject;
if (mad_hdr->ClassVersion != agent->agent_req.mgmt_class_version)
goto reject;
if (! is_supported_mad_method(mad_hdr->R_Method & MAD_METHOD_MASK,
agent->agent_req.method_mask))
goto reject;
if (umad_queue_mad_msg(agent, msgp))
goto reject;
mutex_exit(&ibmf_info->ibmf_reg_lock);
return;
reject:
rc = ibmf_free_msg(ibmf_info->ibmf_reg_handle, &msgp);
ASSERT(rc == IBMF_SUCCESS);
mutex_exit(&ibmf_info->ibmf_reg_lock);
}
#if defined(__lint)
void
lint_function(llist_head_t *a, llist_head_t *b)
{
(void) llist_is_last(a, b);
llist_add_tail(a, b);
(void) ib_width_enum_to_int(IB_WIDTH_1X);
}
#endif