#include <sys/cpuvar.h>
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
#include <sys/sysmacros.h>
#include <sys/sdt.h>
#include <sys/taskq.h>
#include <sys/stmf.h>
#include <sys/stmf_ioctl.h>
#include <sys/portif.h>
#include "srp.h"
#include "srpt_impl.h"
#include "srpt_ioc.h"
#include "srpt_stp.h"
#include "srpt_cm.h"
#include "srpt_ioctl.h"
#include "srpt_common.h"
#define SRPT_NAME_VERSION "COMSTAR SRP Target"
boolean_t srpt_enable_by_default = B_TRUE;
uint16_t srpt_send_msg_depth = SRPT_DEFAULT_SEND_MSG_DEPTH;
uint_t srpt_errlevel = SRPT_LOG_DEFAULT_LEVEL;
uint32_t srpt_iu_size = SRPT_DEFAULT_SEND_MSG_SIZE;
srpt_ctxt_t *srpt_ctxt;
static int srpt_drv_attach(dev_info_t *, ddi_attach_cmd_t);
static int srpt_drv_detach(dev_info_t *, ddi_detach_cmd_t);
static int srpt_drv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int srpt_drv_open(dev_t *, int, int, cred_t *);
static int srpt_drv_close(dev_t, int, int, cred_t *);
static int srpt_drv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
static int srpt_disable_srp_services(void);
static int srpt_enable_srp_services(void);
static int srpt_ibdma_ops_load(srpt_ibdma_ops_t *);
static void srpt_ibdma_ops_unload(srpt_ibdma_ops_t *);
extern struct mod_ops mod_miscops;
static struct cb_ops srpt_cb_ops = {
srpt_drv_open,
srpt_drv_close,
nodev,
nodev,
nodev,
nodev,
nodev,
srpt_drv_ioctl,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
NULL,
D_MP,
CB_REV,
nodev,
nodev,
};
static struct dev_ops srpt_dev_ops = {
DEVO_REV,
0,
srpt_drv_getinfo,
nulldev,
nulldev,
srpt_drv_attach,
srpt_drv_detach,
nodev,
&srpt_cb_ops,
NULL,
NULL,
ddi_quiesce_not_needed,
};
static struct modldrv modldrv = {
&mod_driverops,
SRPT_NAME_VERSION,
&srpt_dev_ops,
};
static struct modlinkage srpt_modlinkage = {
MODREV_1,
&modldrv,
NULL,
};
static char srpt_pp_name[] = "srpt";
static void srpt_pp_cb(stmf_port_provider_t *, int, void *, uint32_t);
int
_init(void)
{
int status;
srpt_ctxt = kmem_zalloc(sizeof (srpt_ctxt_t), KM_SLEEP);
ASSERT(srpt_ctxt != NULL);
rw_init(&srpt_ctxt->sc_rwlock, NULL, RW_DRIVER, NULL);
srpt_ctxt->sc_svc_state = SRPT_SVC_DISABLED;
list_create(&srpt_ctxt->sc_ioc_list, sizeof (srpt_ioc_t),
offsetof(srpt_ioc_t, ioc_node));
list_create(&srpt_ctxt->sc_ioc_list, sizeof (srpt_ioc_t),
offsetof(srpt_ioc_t, ioc_node));
status = mod_install(&srpt_modlinkage);
if (status != DDI_SUCCESS) {
cmn_err(CE_CONT, "_init, failed mod_install %d", status);
rw_destroy(&srpt_ctxt->sc_rwlock);
kmem_free(srpt_ctxt, sizeof (srpt_ctxt_t));
srpt_ctxt = NULL;
}
return (status);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&srpt_modlinkage, modinfop));
}
int
_fini(void)
{
int status;
status = mod_remove(&srpt_modlinkage);
if (status != DDI_SUCCESS) {
return (status);
}
list_destroy(&srpt_ctxt->sc_ioc_list);
rw_destroy(&srpt_ctxt->sc_rwlock);
kmem_free(srpt_ctxt, sizeof (srpt_ctxt_t));
srpt_ctxt = NULL;
return (status);
}
static int
srpt_drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
{
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
*result = srpt_ctxt->sc_dip;
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
*result = NULL;
return (DDI_SUCCESS);
default:
break;
}
return (DDI_FAILURE);
}
static int
srpt_drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int status;
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (ddi_get_instance(dip) != 0) {
SRPT_DPRINTF_L1("drv_attach, error non-zero instance");
return (DDI_FAILURE);
}
status = ddi_create_minor_node(dip, ddi_get_name(dip),
S_IFCHR, 0, DDI_PSEUDO, 0);
if (status != DDI_SUCCESS) {
SRPT_DPRINTF_L1("drv_attach, minor node creation error (%d)",
status);
return (DDI_FAILURE);
}
rw_enter(&srpt_ctxt->sc_rwlock, RW_WRITER);
srpt_ctxt->sc_dip = dip;
rw_exit(&srpt_ctxt->sc_rwlock);
return (DDI_SUCCESS);
}
static int
srpt_enable_srp_services(void)
{
int status;
ASSERT((rw_read_locked(&srpt_ctxt->sc_rwlock)) == 0);
SRPT_DPRINTF_L3("srpt_enable_srp_services");
srpt_ctxt->sc_pp = (stmf_port_provider_t *)
stmf_alloc(STMF_STRUCT_PORT_PROVIDER, 0, 0);
srpt_ctxt->sc_pp->pp_portif_rev = PORTIF_REV_1;
srpt_ctxt->sc_pp->pp_name = srpt_pp_name;
srpt_ctxt->sc_pp->pp_cb = srpt_pp_cb;
status = stmf_register_port_provider(srpt_ctxt->sc_pp);
if (status != STMF_SUCCESS) {
SRPT_DPRINTF_L1("enable_srp: SRP port_provider registration"
" failed(%d)", status);
goto err_exit_1;
}
status = srpt_ioc_attach();
if (status != DDI_SUCCESS) {
SRPT_DPRINTF_L1("enable_srp: error attach I/O"
" Controllers (%d)", status);
goto err_exit_2;
}
if (srpt_ctxt->sc_num_iocs == 0) {
SRPT_DPRINTF_L2("enable_srp: no IB I/O Controllers found");
return (DDI_SUCCESS);
}
return (DDI_SUCCESS);
err_exit_2:
(void) stmf_deregister_port_provider(srpt_ctxt->sc_pp);
err_exit_1:
stmf_free(srpt_ctxt->sc_pp);
srpt_ctxt->sc_pp = NULL;
return (status);
}
static int
srpt_drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
switch (cmd) {
case DDI_DETACH:
rw_enter(&srpt_ctxt->sc_rwlock, RW_WRITER);
if (srpt_ctxt->sc_svc_state != SRPT_SVC_DISABLED) {
rw_exit(&srpt_ctxt->sc_rwlock);
return (DDI_FAILURE);
}
ddi_remove_minor_node(dip, NULL);
srpt_ctxt->sc_dip = NULL;
if (srpt_ctxt->sc_cfg_hca_nv != NULL) {
nvlist_free(srpt_ctxt->sc_cfg_hca_nv);
srpt_ctxt->sc_cfg_hca_nv = NULL;
}
rw_exit(&srpt_ctxt->sc_rwlock);
break;
case DDI_SUSPEND:
return (DDI_FAILURE);
default:
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
srpt_disable_srp_services(void)
{
stmf_status_t stmf_status;
srpt_ioc_t *ioc;
srpt_target_port_t *tgt;
int ret_status = 0;
ASSERT((rw_read_locked(&srpt_ctxt->sc_rwlock)) == 0);
ioc = list_head(&srpt_ctxt->sc_ioc_list);
while (ioc != NULL) {
rw_enter(&ioc->ioc_rwlock, RW_WRITER);
tgt = ioc->ioc_tgt_port;
if (tgt != NULL) {
stmf_status = srpt_stp_destroy_port(tgt);
if (stmf_status == STMF_SUCCESS) {
ioc->ioc_tgt_port = NULL;
(void) srpt_stp_free_port(tgt);
} else {
ret_status = DDI_FAILURE;
break;
}
}
rw_exit(&ioc->ioc_rwlock);
ioc = list_next(&srpt_ctxt->sc_ioc_list, ioc);
}
if (ret_status != 0) {
return (ret_status);
}
srpt_ioc_detach();
(void) stmf_deregister_port_provider(srpt_ctxt->sc_pp);
stmf_free(srpt_ctxt->sc_pp);
srpt_ctxt->sc_pp = NULL;
return (0);
}
static int
srpt_drv_open(dev_t *devp, int flag, int otyp, cred_t *credp)
{
SRPT_DPRINTF_L3("drv_open, invoked");
return (0);
}
static int
srpt_drv_close(dev_t dev, int flag, int otyp, cred_t *credp)
{
SRPT_DPRINTF_L3("drv_close, invoked");
return (0);
}
static int
srpt_drv_ioctl(dev_t drv, int cmd, intptr_t argp, int flag, cred_t *cred,
int *retval)
{
int ret = 0;
SRPT_DPRINTF_L3("drv_ioctl, invoked, cmd = %d", cmd);
if (drv_priv(cred) != 0) {
return (EPERM);
}
rw_enter(&srpt_ctxt->sc_rwlock, RW_WRITER);
switch (cmd) {
case SRPT_IOC_ENABLE_SVC:
if (srpt_ctxt->sc_svc_state != SRPT_SVC_DISABLED) {
break;
}
ret = srpt_ibdma_ops_load(&srpt_ctxt->sc_ibdma_ops);
if (ret != 0) {
break;
}
ret = srpt_enable_srp_services();
if (ret == 0) {
srpt_ctxt->sc_svc_state = SRPT_SVC_ENABLED;
}
break;
case SRPT_IOC_DISABLE_SVC:
if (srpt_ctxt->sc_svc_state != SRPT_SVC_ENABLED) {
break;
}
ret = srpt_disable_srp_services();
if (ret == 0) {
srpt_ctxt->sc_svc_state = SRPT_SVC_DISABLED;
}
srpt_ibdma_ops_unload(&srpt_ctxt->sc_ibdma_ops);
break;
default:
ret = EFAULT;
break;
}
rw_exit(&srpt_ctxt->sc_rwlock);
return (ret);
}
static void
srpt_pp_cb(stmf_port_provider_t *pp, int cmd, void *arg, uint32_t flags)
{
int ret;
nvlist_t *in_nvl = (nvlist_t *)arg;
nvlist_t *nvl = NULL;
nvlist_t *hcalist;
nvlist_t *ctxt_nvl;
boolean_t defaultEnabled = B_TRUE;
boolean_t called_by_reg = B_TRUE;
SRPT_DPRINTF_L2("srpt_pp_cb, invoked (%d)", cmd);
if (cmd != STMF_PROVIDER_DATA_UPDATED) {
return;
}
if (!(flags & STMF_PCB_PREG_COMPLETE)) {
SRPT_DPRINTF_L2(
"srpt_pp_cb: called after registration");
called_by_reg = B_FALSE;
rw_enter(&srpt_ctxt->sc_rwlock, RW_WRITER);
} else {
called_by_reg = B_TRUE;
SRPT_DPRINTF_L2(
"srpt_pp_cb: called as part of registration");
}
if (in_nvl != NULL) {
ret = nvlist_lookup_nvlist(in_nvl, SRPT_PROP_HCALIST, &hcalist);
if (ret != 0) {
SRPT_DPRINTF_L1(
"srpt_pp_cb: Could not read hca config, err=%d",
ret);
return;
}
ret = nvlist_dup(hcalist, &nvl, 0);
if (ret != 0) {
SRPT_DPRINTF_L1(
"srpt_pp_cb: Could not copy hca config, err=%d",
ret);
return;
}
if (nvlist_lookup_boolean_value(in_nvl,
SRPT_PROP_DEFAULT_ENABLED, &defaultEnabled) == 0) {
SRPT_DPRINTF_L2(
"srpt_pp_cb: setting default enabled = %d\n",
(int)defaultEnabled);
srpt_enable_by_default = defaultEnabled;
}
} else {
SRPT_DPRINTF_L2(
"srpt_pp_cb: null config received");
}
ctxt_nvl = srpt_ctxt->sc_cfg_hca_nv;
srpt_ctxt->sc_cfg_hca_nv = nvl;
if (ctxt_nvl != NULL) {
nvlist_free(ctxt_nvl);
}
if (called_by_reg) {
return;
}
srpt_ioc_update();
rw_exit(&srpt_ctxt->sc_rwlock);
}
static int
srpt_ibdma_ops_load(srpt_ibdma_ops_t *ops)
{
int ibdma_err = 0;
ASSERT(ops != NULL);
ops->ibdmah = ddi_modopen("ibdma", KRTLD_MODE_FIRST, &ibdma_err);
if (ops->ibdmah == NULL) {
SRPT_DPRINTF_L0("failed to open ibdma driver, error = %d",
ibdma_err);
return (ibdma_err);
}
ops->ibdma_register = (ibdma_hdl_t (*)())ddi_modsym(ops->ibdmah,
"ibdma_ioc_register", &ibdma_err);
if (ops->ibdma_register == NULL) {
SRPT_DPRINTF_L0(
"failed to modsym ibdma_ioc_register, error = %d",
ibdma_err);
goto done;
}
ops->ibdma_unregister = (ibdma_status_t (*)())ddi_modsym(ops->ibdmah,
"ibdma_ioc_unregister", &ibdma_err);
if (ops->ibdma_unregister == NULL) {
SRPT_DPRINTF_L0(
"failed to modsym ibdma_ioc_unregister, error = %d",
ibdma_err);
goto done;
}
ops->ibdma_update = (ibdma_status_t (*)())ddi_modsym(ops->ibdmah,
"ibdma_ioc_update", &ibdma_err);
if (ops->ibdma_update == NULL) {
SRPT_DPRINTF_L0(
"failed to modsym ibdma_ioc_update, error = %d",
ibdma_err);
}
done:
if (ibdma_err != 0) {
srpt_ibdma_ops_unload(ops);
}
return (ibdma_err);
}
static void
srpt_ibdma_ops_unload(srpt_ibdma_ops_t *ops)
{
if (ops != NULL) {
if (ops->ibdmah != NULL) {
(void) ddi_modclose(ops->ibdmah);
}
bzero(ops, sizeof (srpt_ibdma_ops_t));
}
}