Changeset View
Changeset View
Standalone View
Standalone View
sys/cam/ctl/ctl_frontend_ioctl.c
Context not available. | |||||
#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> | ||||
Context not available. | |||||
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); | ||||
Context not available. | |||||
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; | ||||
Context not available. | |||||
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); | ||||
} | } | ||||
Context not available. | |||||
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; | ||||
int error = 0; | struct ctl_port *port; | ||||
int error; | |||||
ctl_port_offline(port); | TAILQ_FOREACH(cfi, &isoftc->ports, link) { | ||||
if ((error = ctl_port_deregister(port)) != 0) | port = &cfi->port; | ||||
printf("%s: ioctl port deregistration failed\n", __func__); | ctl_port_offline(port); | ||||
return (error); | error = ctl_port_deregister(port); | ||||
mav: This code is incomplete. It will leak different resources. | |||||
if (error != 0) { | |||||
printf("%s: ctl_frontend_deregister() failed\n", | |||||
__func__); | |||||
return (error); | |||||
} | |||||
} | |||||
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; | |||||
int retval; | |||||
int pp, vp; | |||||
pp = (int)dnvlist_get_number(req->args_nvl, "pp", -1); | |||||
vp = (int)dnvlist_get_number(req->args_nvl, "vp", 0); | |||||
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; | |||||
int port_id; | |||||
port_id = (int)dnvlist_get_number(req->args_nvl, "port_id", -1); | |||||
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; | |||||
} | |||||
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; | |||||
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… | |||||
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. | ||||
*/ | */ | ||||
Context not available. | |||||
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) | ||||
{ | { | ||||
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) | |||||
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. | |||||
: 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 | ||||
Context not available. | |||||
/* | /* | ||||
* 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++; | ||||
mavUnsubmitted 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. | |||||
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); | ||||
} | } | ||||
Context not available. |
This code is incomplete. It will leak different resources.