Changeset View
Changeset View
Standalone View
Standalone View
sys/cam/ctl/ctl_frontend_ioctl.c
Show All 35 Lines | |||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/condvar.h> | #include <sys/condvar.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/conf.h> | #include <sys/conf.h> | ||||
#include <sys/queue.h> | #include <sys/queue.h> | ||||
#include <sys/sysctl.h> | #include <sys/sysctl.h> | ||||
#include <sys/nv.h> | |||||
#include <sys/dnv.h> | |||||
#include <cam/cam.h> | #include <cam/cam.h> | ||||
#include <cam/scsi/scsi_all.h> | #include <cam/scsi/scsi_all.h> | ||||
#include <cam/scsi/scsi_da.h> | #include <cam/scsi/scsi_da.h> | ||||
#include <cam/ctl/ctl_io.h> | #include <cam/ctl/ctl_io.h> | ||||
#include <cam/ctl/ctl.h> | #include <cam/ctl/ctl.h> | ||||
#include <cam/ctl/ctl_frontend.h> | #include <cam/ctl/ctl_frontend.h> | ||||
#include <cam/ctl/ctl_util.h> | #include <cam/ctl/ctl_util.h> | ||||
Show All 11 Lines | |||||
} ctl_fe_ioctl_state; | } ctl_fe_ioctl_state; | ||||
struct ctl_fe_ioctl_params { | struct ctl_fe_ioctl_params { | ||||
struct cv sem; | struct cv sem; | ||||
struct mtx ioctl_mtx; | struct mtx ioctl_mtx; | ||||
ctl_fe_ioctl_state state; | ctl_fe_ioctl_state state; | ||||
}; | }; | ||||
struct cfi_softc { | struct cfi_port { | ||||
TAILQ_ENTRY(cfi_port) link; | |||||
uint32_t cur_tag_num; | uint32_t cur_tag_num; | ||||
struct cdev * dev; | |||||
struct ctl_port port; | struct ctl_port port; | ||||
}; | }; | ||||
struct cfi_softc { | |||||
TAILQ_HEAD(, cfi_port) ports; | |||||
}; | |||||
static struct cfi_softc cfi_softc; | static struct cfi_softc cfi_softc; | ||||
static int cfi_init(void); | static int cfi_init(void); | ||||
static int cfi_shutdown(void); | static int cfi_shutdown(void); | ||||
static void cfi_datamove(union ctl_io *io); | static void cfi_datamove(union ctl_io *io); | ||||
static void cfi_done(union ctl_io *io); | static void cfi_done(union ctl_io *io); | ||||
static int cfi_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, | |||||
struct thread *td); | |||||
static void cfi_ioctl_port_create(struct ctl_req *req); | |||||
static void cfi_ioctl_port_remove(struct ctl_req *req); | |||||
static struct cdevsw cfi_cdevsw = { | |||||
.d_version = D_VERSION, | |||||
.d_flags = 0, | |||||
.d_ioctl = ctl_ioctl_io | |||||
}; | |||||
static struct ctl_frontend cfi_frontend = | static struct ctl_frontend cfi_frontend = | ||||
{ | { | ||||
.name = "ioctl", | .name = "ioctl", | ||||
.init = cfi_init, | .init = cfi_init, | ||||
.ioctl = cfi_ioctl, | |||||
.shutdown = cfi_shutdown, | .shutdown = cfi_shutdown, | ||||
}; | }; | ||||
CTL_FRONTEND_DECLARE(ctlioctl, cfi_frontend); | CTL_FRONTEND_DECLARE(ctlioctl, cfi_frontend); | ||||
static int | static int | ||||
cfi_init(void) | cfi_init(void) | ||||
{ | { | ||||
struct cfi_softc *isoftc = &cfi_softc; | struct cfi_softc *isoftc = &cfi_softc; | ||||
struct cfi_port *cfi; | |||||
struct ctl_port *port; | struct ctl_port *port; | ||||
int error = 0; | int error = 0; | ||||
memset(isoftc, 0, sizeof(*isoftc)); | memset(isoftc, 0, sizeof(*isoftc)); | ||||
TAILQ_INIT(&isoftc->ports); | |||||
port = &isoftc->port; | cfi = malloc(sizeof(*cfi), M_CTL, M_WAITOK | M_ZERO); | ||||
port = &cfi->port; | |||||
port->frontend = &cfi_frontend; | port->frontend = &cfi_frontend; | ||||
port->port_type = CTL_PORT_IOCTL; | port->port_type = CTL_PORT_IOCTL; | ||||
port->num_requested_ctl_io = 100; | port->num_requested_ctl_io = 100; | ||||
port->port_name = "ioctl"; | port->port_name = "ioctl"; | ||||
port->fe_datamove = cfi_datamove; | port->fe_datamove = cfi_datamove; | ||||
port->fe_done = cfi_done; | port->fe_done = cfi_done; | ||||
port->physical_port = 0; | |||||
port->targ_port = -1; | port->targ_port = -1; | ||||
port->max_initiators = 1; | |||||
if ((error = ctl_port_register(port)) != 0) { | if ((error = ctl_port_register(port)) != 0) { | ||||
printf("%s: ioctl port registration failed\n", __func__); | printf("%s: ioctl port registration failed\n", __func__); | ||||
return (error); | return (error); | ||||
} | } | ||||
ctl_port_online(port); | ctl_port_online(port); | ||||
TAILQ_INSERT_TAIL(&isoftc->ports, cfi, link); | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
cfi_shutdown(void) | cfi_shutdown(void) | ||||
{ | { | ||||
struct cfi_softc *isoftc = &cfi_softc; | struct cfi_softc *isoftc = &cfi_softc; | ||||
struct ctl_port *port = &isoftc->port; | struct cfi_port *cfi, *temp; | ||||
int error = 0; | struct ctl_port *port; | ||||
int error; | |||||
TAILQ_FOREACH_SAFE(cfi, &isoftc->ports, link, temp) { | |||||
port = &cfi->port; | |||||
ctl_port_offline(port); | ctl_port_offline(port); | ||||
if ((error = ctl_port_deregister(port)) != 0) | error = ctl_port_deregister(port); | ||||
mav: This code is incomplete. It will leak different resources. | |||||
printf("%s: ioctl port deregistration failed\n", __func__); | if (error != 0) { | ||||
printf("%s: ctl_frontend_deregister() failed\n", | |||||
__func__); | |||||
return (error); | return (error); | ||||
} | } | ||||
TAILQ_REMOVE(&isoftc->ports, cfi, link); | |||||
free(cfi, M_CTL); | |||||
} | |||||
return (0); | |||||
} | |||||
static void | |||||
cfi_ioctl_port_create(struct ctl_req *req) | |||||
{ | |||||
struct cfi_softc *isoftc = &cfi_softc; | |||||
struct cfi_port *cfi; | |||||
struct ctl_port *port; | |||||
struct make_dev_args args; | |||||
const char *val; | |||||
int retval; | |||||
int pp = -1, vp = 0; | |||||
val = dnvlist_get_string(req->args_nvl, "pp", NULL); | |||||
if (val != NULL) | |||||
pp = strtol(val, NULL, 10); | |||||
val = dnvlist_get_string(req->args_nvl, "vp", NULL); | |||||
if (val != NULL) | |||||
vp = strtol(val, NULL, 10); | |||||
if (pp != -1) { | |||||
/* Check for duplicates */ | |||||
TAILQ_FOREACH(cfi, &isoftc->ports, link) { | |||||
if (pp == cfi->port.physical_port && | |||||
vp == cfi->port.virtual_port) { | |||||
req->status = CTL_LUN_ERROR; | |||||
snprintf(req->error_str, sizeof(req->error_str), | |||||
"port %d already exists", pp); | |||||
return; | |||||
} | |||||
} | |||||
} else { | |||||
/* Find free port number */ | |||||
TAILQ_FOREACH(cfi, &isoftc->ports, link) { | |||||
pp = MAX(pp, cfi->port.physical_port); | |||||
} | |||||
pp++; | |||||
} | |||||
cfi = malloc(sizeof(*cfi), M_CTL, M_WAITOK | M_ZERO); | |||||
port = &cfi->port; | |||||
port->frontend = &cfi_frontend; | |||||
port->port_type = CTL_PORT_IOCTL; | |||||
port->num_requested_ctl_io = 100; | |||||
port->port_name = "ioctl"; | |||||
port->fe_datamove = cfi_datamove; | |||||
port->fe_done = cfi_done; | |||||
port->physical_port = pp; | |||||
port->virtual_port = vp; | |||||
port->targ_port = -1; | |||||
retval = ctl_port_register(port); | |||||
if (retval != 0) { | |||||
req->status = CTL_LUN_ERROR; | |||||
snprintf(req->error_str, sizeof(req->error_str), | |||||
"ctl_port_register() failed with error %d", retval); | |||||
free(port, M_CTL); | |||||
return; | |||||
} | |||||
req->result_nvl = nvlist_create(0); | |||||
nvlist_add_number(req->result_nvl, "port_id", port->targ_port); | |||||
ctl_port_online(port); | |||||
make_dev_args_init(&args); | |||||
args.mda_devsw = &cfi_cdevsw; | |||||
args.mda_uid = UID_ROOT; | |||||
args.mda_gid = GID_OPERATOR; | |||||
args.mda_mode = 0600; | |||||
args.mda_si_drv1 = NULL; | |||||
args.mda_si_drv2 = cfi; | |||||
retval = make_dev_s(&args, &cfi->dev, "cam/ctl%d.%d", pp, vp); | |||||
if (retval != 0) { | |||||
req->status = CTL_LUN_ERROR; | |||||
snprintf(req->error_str, sizeof(req->error_str), | |||||
"make_dev_s() failed with error %d", retval); | |||||
free(port, M_CTL); | |||||
return; | |||||
} | |||||
req->status = CTL_LUN_OK; | |||||
TAILQ_INSERT_TAIL(&isoftc->ports, cfi, link); | |||||
} | |||||
static void | |||||
cfi_ioctl_port_remove(struct ctl_req *req) | |||||
{ | |||||
struct cfi_softc *isoftc = &cfi_softc; | |||||
struct cfi_port *cfi = NULL; | |||||
const char *val; | |||||
int port_id = -1; | |||||
val = dnvlist_get_string(req->args_nvl, "port_id", NULL); | |||||
if (val != NULL) | |||||
port_id = strtol(val, NULL, 10); | |||||
if (port_id == -1) { | |||||
req->status = CTL_LUN_ERROR; | |||||
snprintf(req->error_str, sizeof(req->error_str), | |||||
"port_id not provided"); | |||||
return; | |||||
} | |||||
TAILQ_FOREACH(cfi, &isoftc->ports, link) { | |||||
if (cfi->port.targ_port == port_id) | |||||
break; | |||||
} | |||||
if (cfi == NULL) { | |||||
req->status = CTL_LUN_ERROR; | |||||
snprintf(req->error_str, sizeof(req->error_str), | |||||
"cannot find port %d", port_id); | |||||
return; | |||||
} | |||||
if (cfi->port.physical_port == 0 && cfi->port.virtual_port == 0) { | |||||
Done Inline ActionsWhat happen if somebody try to destroy default ioctl port? It has no dedicated device to destroy, and without that there may be running requests during port destruction. Also attempt to recreate that port will cause behavior different from original. mav: What happen if somebody try to destroy default ioctl port? It has no dedicated device to… | |||||
req->status = CTL_LUN_ERROR; | |||||
snprintf(req->error_str, sizeof(req->error_str), | |||||
"cannot destroy default ioctl port"); | |||||
return; | |||||
} | |||||
ctl_port_offline(&cfi->port); | |||||
ctl_port_deregister(&cfi->port); | |||||
TAILQ_REMOVE(&isoftc->ports, cfi, link); | |||||
destroy_dev(cfi->dev); | |||||
free(cfi, M_CTL); | |||||
req->status = CTL_LUN_OK; | |||||
} | |||||
static int | |||||
cfi_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, | |||||
struct thread *td) | |||||
{ | |||||
struct ctl_req *req; | |||||
if (cmd == CTL_PORT_REQ) { | |||||
req = (struct ctl_req *)addr; | |||||
switch (req->reqtype) { | |||||
case CTL_REQ_CREATE: | |||||
cfi_ioctl_port_create(req); | |||||
break; | |||||
case CTL_REQ_REMOVE: | |||||
cfi_ioctl_port_remove(req); | |||||
break; | |||||
default: | |||||
req->status = CTL_LUN_ERROR; | |||||
snprintf(req->error_str, sizeof(req->error_str), | |||||
"Unsupported request type %d", req->reqtype); | |||||
} | |||||
return (0); | |||||
} | |||||
return (ENOTTY); | |||||
} | |||||
/* | /* | ||||
* Data movement routine for the CTL ioctl frontend port. | * Data movement routine for the CTL ioctl frontend port. | ||||
*/ | */ | ||||
static int | static int | ||||
ctl_ioctl_do_datamove(struct ctl_scsiio *ctsio) | ctl_ioctl_do_datamove(struct ctl_scsiio *ctsio) | ||||
{ | { | ||||
struct ctl_sg_entry *ext_sglist, *kern_sglist; | struct ctl_sg_entry *ext_sglist, *kern_sglist; | ||||
struct ctl_sg_entry ext_entry, kern_entry; | struct ctl_sg_entry ext_entry, kern_entry; | ||||
▲ Show 20 Lines • Show All 243 Lines • ▼ Show 20 Lines | cfi_submit_wait(union ctl_io *io) | ||||
mtx_destroy(¶ms.ioctl_mtx); | mtx_destroy(¶ms.ioctl_mtx); | ||||
cv_destroy(¶ms.sem); | cv_destroy(¶ms.sem); | ||||
return (CTL_RETVAL_COMPLETE); | return (CTL_RETVAL_COMPLETE); | ||||
} | } | ||||
int | int | ||||
ctl_ioctl_io(struct cdev *dev, u_long cmd, caddr_t addr, int flag, | ctl_ioctl_io(struct cdev *dev, u_long cmd, caddr_t addr, int flag, | ||||
struct thread *td) | struct thread *td) | ||||
Done Inline ActionsThis won't be correct if we allow default port to be destroyed. mav: This won't be correct if we allow default port to be destroyed. | |||||
{ | { | ||||
struct cfi_port *cfi; | |||||
union ctl_io *io; | union ctl_io *io; | ||||
void *pool_tmp, *sc_tmp; | void *pool_tmp, *sc_tmp; | ||||
int retval = 0; | int retval = 0; | ||||
if (cmd != CTL_IO) | |||||
return (ENOTTY); | |||||
cfi = dev->si_drv2 == NULL | |||||
? TAILQ_FIRST(&cfi_softc.ports) | |||||
: dev->si_drv2; | |||||
/* | /* | ||||
* If we haven't been "enabled", don't allow any SCSI I/O | * If we haven't been "enabled", don't allow any SCSI I/O | ||||
* to this FETD. | * to this FETD. | ||||
*/ | */ | ||||
if ((cfi_softc.port.status & CTL_PORT_STATUS_ONLINE) == 0) | if ((cfi->port.status & CTL_PORT_STATUS_ONLINE) == 0) | ||||
return (EPERM); | return (EPERM); | ||||
io = ctl_alloc_io(cfi_softc.port.ctl_pool_ref); | io = ctl_alloc_io(cfi->port.ctl_pool_ref); | ||||
/* | /* | ||||
* Need to save the pool reference so it doesn't get | * Need to save the pool reference so it doesn't get | ||||
* spammed by the user's ctl_io. | * spammed by the user's ctl_io. | ||||
Not Done Inline ActionsWe'll need to invent some way to pass tag value from user level. Though that can be done later. mav: We'll need to invent some way to pass tag value from user level. Though that can be done later. | |||||
*/ | */ | ||||
pool_tmp = io->io_hdr.pool; | pool_tmp = io->io_hdr.pool; | ||||
sc_tmp = CTL_SOFTC(io); | sc_tmp = CTL_SOFTC(io); | ||||
memcpy(io, (void *)addr, sizeof(*io)); | memcpy(io, (void *)addr, sizeof(*io)); | ||||
io->io_hdr.pool = pool_tmp; | io->io_hdr.pool = pool_tmp; | ||||
CTL_SOFTC(io) = sc_tmp; | CTL_SOFTC(io) = sc_tmp; | ||||
/* | /* | ||||
* No status yet, so make sure the status is set properly. | * No status yet, so make sure the status is set properly. | ||||
*/ | */ | ||||
io->io_hdr.status = CTL_STATUS_NONE; | io->io_hdr.status = CTL_STATUS_NONE; | ||||
/* | /* | ||||
* The user sets the initiator ID, target and LUN IDs. | * The user sets the initiator ID, target and LUN IDs. | ||||
*/ | */ | ||||
io->io_hdr.nexus.targ_port = cfi_softc.port.targ_port; | io->io_hdr.nexus.targ_port = cfi->port.targ_port; | ||||
io->io_hdr.flags |= CTL_FLAG_USER_REQ; | io->io_hdr.flags |= CTL_FLAG_USER_REQ; | ||||
if ((io->io_hdr.io_type == CTL_IO_SCSI) && | if ((io->io_hdr.io_type == CTL_IO_SCSI) && | ||||
(io->scsiio.tag_type != CTL_TAG_UNTAGGED)) | (io->scsiio.tag_type != CTL_TAG_UNTAGGED)) | ||||
io->scsiio.tag_num = cfi_softc.cur_tag_num++; | io->scsiio.tag_num = cfi->cur_tag_num++; | ||||
retval = cfi_submit_wait(io); | retval = cfi_submit_wait(io); | ||||
if (retval == 0) | if (retval == 0) | ||||
memcpy((void *)addr, io, sizeof(*io)); | memcpy((void *)addr, io, sizeof(*io)); | ||||
ctl_free_io(io); | ctl_free_io(io); | ||||
return (retval); | return (retval); | ||||
} | } |
This code is incomplete. It will leak different resources.