#include <sys/types.h>
#include <sys/stat.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/modctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/ib/clients/iser/iser.h>
iser_state_t *iser_state = NULL;
ddi_taskq_t *iser_taskq = NULL;
boolean_t iser_logging = B_FALSE;
static int iser_attach(dev_info_t *, ddi_attach_cmd_t);
static int iser_detach(dev_info_t *, ddi_detach_cmd_t);
static int iser_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int iser_open(dev_t *, int, int, cred_t *);
static int iser_close(dev_t, int, int, cred_t *);
static int iser_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
static struct cb_ops iser_cb_ops = {
iser_open,
iser_close,
nodev,
nodev,
nodev,
nodev,
nodev,
iser_ioctl,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
NULL,
D_MP,
CB_REV,
nodev,
nodev,
};
static struct dev_ops iser_ops = {
DEVO_REV,
0,
iser_getinfo,
nulldev,
nulldev,
iser_attach,
iser_detach,
nodev,
&iser_cb_ops,
NULL,
NULL,
ddi_quiesce_not_needed
};
#define ISER_NAME_VERSION "iSCSI Extensions for RDMA"
static struct modldrv iser_modldrv = {
&mod_driverops,
ISER_NAME_VERSION,
&iser_ops,
};
static struct modlinkage iser_modlinkage = {
MODREV_1,
&iser_modldrv,
NULL
};
int
_init(void)
{
int status;
iser_state = kmem_zalloc(sizeof (iser_state_t), KM_SLEEP);
status = mod_install(&iser_modlinkage);
if (status != DDI_SUCCESS) {
kmem_free(iser_state, sizeof (iser_state_t));
}
return (status);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&iser_modlinkage, modinfop));
}
int
_fini(void)
{
int status;
status = mod_remove(&iser_modlinkage);
if (status != DDI_SUCCESS) {
return (status);
}
kmem_free(iser_state, sizeof (iser_state_t));
return (DDI_SUCCESS);
}
static int
iser_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int instance;
int status;
switch (cmd) {
case DDI_ATTACH:
ISER_LOG(CE_CONT, "iser_attach: DDI_ATTACH");
instance = ddi_get_instance(dip);
iser_state->is_dip = dip;
iser_state->is_instance = instance;
iser_state->is_open_refcnt = 0;
mutex_init(&iser_state->is_refcnt_lock, NULL, MUTEX_DRIVER,
NULL);
iser_taskq = ddi_taskq_create(dip, "iser_taskq",
ISER_TASKQ_NTHREADS, TASKQ_DEFAULTPRI, 0);
if (iser_taskq == NULL) {
ISER_LOG(CE_CONT, "%s%d: failed to create taskq",
"iser", instance);
mutex_destroy(&iser_state->is_refcnt_lock);
return (DDI_FAILURE);
}
status = iser_ib_init();
if (status != DDI_SUCCESS) {
ddi_taskq_destroy(iser_taskq);
mutex_destroy(&iser_state->is_refcnt_lock);
ISER_LOG(CE_CONT, "%s%d: failed to initialize IB",
"iser", instance);
return (DDI_FAILURE);
}
status = ddi_create_minor_node(
dip, ddi_get_name(dip), S_IFCHR, instance,
DDI_PSEUDO, 0);
if (status != DDI_SUCCESS) {
(void) iser_ib_fini();
ddi_taskq_destroy(iser_taskq);
mutex_destroy(&iser_state->is_refcnt_lock);
ISER_LOG(CE_CONT, "%s%d: failed ddi_create_minor_node",
"iser", instance);
return (DDI_FAILURE);
}
ddi_report_dev(dip);
return (DDI_SUCCESS);
case DDI_RESUME:
ISER_LOG(CE_CONT, "iser_detach: DDI_RESUME unsupported");
return (DDI_FAILURE);
default:
ISER_LOG(CE_CONT, "%s%d: unknown cmd in attach (0x%x)", "iser",
instance, cmd);
return (DDI_FAILURE);
}
}
static int
iser_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
mutex_enter(&iser_state->is_refcnt_lock);
if (iser_state->is_open_refcnt > 0) {
mutex_exit(&iser_state->is_refcnt_lock);
return (DDI_FAILURE);
}
mutex_exit(&iser_state->is_refcnt_lock);
mutex_destroy(&iser_state->is_refcnt_lock);
switch (cmd) {
case DDI_DETACH:
ISER_LOG(CE_CONT, "iser_detach: DDI_DETACH");
if (iser_ib_fini() != DDI_SUCCESS) {
ISER_LOG(CE_CONT, "iser_ib_fini failed");
return (DDI_FAILURE);
}
if (iser_taskq != NULL) {
ddi_taskq_destroy(iser_taskq);
iser_taskq = NULL;
}
ddi_remove_minor_node(dip, NULL);
return (DDI_SUCCESS);
case DDI_SUSPEND:
ISER_LOG(CE_CONT, "iser_detach: DDI_SUSPEND unsupported");
return (DDI_FAILURE);
default:
ISER_LOG(CE_CONT, "iser: unknown cmd in detach (0x%x)", cmd);
return (DDI_FAILURE);
}
}
static int
iser_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
{
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
*result = (void *)iser_state->is_dip;
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
*result = NULL;
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
static int
iser_open(dev_t *devp, int flag, int otyp, cred_t *credp)
{
minor_t instance;
int status;
instance = getminor(*devp);
status = iser_idm_register();
if (status != DDI_SUCCESS) {
ISER_LOG(CE_CONT, "%s%d: failed to register with IDM",
"iser", instance);
return (ENXIO);
}
mutex_enter(&iser_state->is_refcnt_lock);
iser_state->is_open_refcnt++;
mutex_exit(&iser_state->is_refcnt_lock);
return (DDI_SUCCESS);
}
static int
iser_close(dev_t devp, int flag, int otyp, cred_t *credp)
{
ASSERT(iser_state->is_open_refcnt != 0);
mutex_enter(&iser_state->is_refcnt_lock);
iser_state->is_open_refcnt--;
mutex_exit(&iser_state->is_refcnt_lock);
return (DDI_SUCCESS);
}
iser_status_t
iser_register_service(idm_svc_t *idm_svc)
{
return (iser_ib_register_service(idm_svc));
}
iser_status_t
iser_bind_service(idm_svc_t *idm_svc)
{
return (iser_ib_bind_service(idm_svc));
}
void
iser_unbind_service(idm_svc_t *idm_svc)
{
iser_ib_unbind_service(idm_svc);
}
void
iser_deregister_service(idm_svc_t *idm_svc)
{
iser_ib_deregister_service(idm_svc);
}
boolean_t
iser_path_exists(idm_sockaddr_t *laddr, idm_sockaddr_t *raddr)
{
ibt_ip_addr_t remote_ip, local_ip;
ibt_path_info_t path;
int status;
iser_ib_conv_sockaddr2ibtaddr(raddr, &remote_ip);
iser_ib_conv_sockaddr2ibtaddr(laddr, &local_ip);
status = iser_ib_get_paths(&local_ip, &remote_ip, &path, NULL);
return ((status == IBT_SUCCESS) ? B_TRUE : B_FALSE);
}
iser_chan_t *
iser_channel_alloc(idm_sockaddr_t *laddr, idm_sockaddr_t *raddr)
{
ibt_ip_addr_t remote_ip, local_ip;
iser_ib_conv_sockaddr2ibtaddr(raddr, &remote_ip);
iser_ib_conv_sockaddr2ibtaddr(laddr, &local_ip);
return (iser_ib_alloc_channel_pathlookup(&local_ip, &remote_ip));
}
iser_status_t
iser_channel_open(iser_chan_t *chan)
{
return (iser_ib_open_rc_channel(chan));
}
void
iser_channel_close(iser_chan_t *chan)
{
iser_ib_close_rc_channel(chan);
}
void
iser_channel_free(iser_chan_t *chan)
{
iser_ib_free_rc_channel(chan);
}
static int
iser_ioctl(dev_t devp, int cmd, intptr_t arg, int mode, cred_t *credp,
int *rvalp)
{
return (DDI_SUCCESS);
}