aboutsummaryrefslogtreecommitdiff
path: root/sys/cam/ctl/ctl_frontend_ioctl.c
diff options
context:
space:
mode:
authorMarcelo Araujo <araujo@FreeBSD.org>2018-05-10 03:50:20 +0000
committerMarcelo Araujo <araujo@FreeBSD.org>2018-05-10 03:50:20 +0000
commit8951f05525ee4e9a93cc568dccd154405aae7419 (patch)
tree503e8ca76afeb0231224fd5f2522663ca24ca5f6 /sys/cam/ctl/ctl_frontend_ioctl.c
parent3429b518c9bfbce5a16d76949d75a843567ba2de (diff)
downloadsrc-8951f05525ee4e9a93cc568dccd154405aae7419.tar.gz
src-8951f05525ee4e9a93cc568dccd154405aae7419.zip
Rework CTL frontend & backend options to use nv(3), allow creating multiple
ioctl frontend ports. This revision introduces two changes to CTL: - Changes the way options are passed to CTL_LUN_REQ and CTL_PORT_REQ ioctls. Removes ctl_be_arg structure and associated logic and replaces it with nv(3)-based logic for passing in and out arguments. - Allows creating multiple ioctl frontend ports using either ctladm(8) or ctld(8). New frontend ports are represented by /dev/cam/ctl<pp>.<vp> nodes, eg /dev/cam/ctl5.3. Those device nodes respond only to CTL_IO ioctl. New command-line options for ctladm: # creates new ioctl frontend port with using free pp and vp=0 ctladm port -c # creates new ioctl frontend port with pp=10 and vp=0 ctladm port -c -O pp=10 # creates new ioctl frontend port with pp=11 and vp=12 ctladm port -c -O pp=11 -O vp=12 # removes port with number 4 (it's a "targ_port" number, not pp number) ctladm port -r -p 4 New syntax for ctl.conf: target ... { port ioctl/<pp> ... } target ... { port ioctl/<pp>/<vp> ... Note: Most of this work was made by jceel@, thank you. Submitted by: jceel Reworked by: myself Reviewed by: mav (earlier versions and recently during the rework) Obtained from: FreeNAS and TrueOS Relnotes: Yes Sponsored by: iXsystems Inc. Differential Revision: https://reviews.freebsd.org/D9299
Notes
Notes: svn path=/head/; revision=333446
Diffstat (limited to 'sys/cam/ctl/ctl_frontend_ioctl.c')
-rw-r--r--sys/cam/ctl/ctl_frontend_ioctl.c236
1 files changed, 223 insertions, 13 deletions
diff --git a/sys/cam/ctl/ctl_frontend_ioctl.c b/sys/cam/ctl/ctl_frontend_ioctl.c
index 63a9504016ef..221d912714b7 100644
--- a/sys/cam/ctl/ctl_frontend_ioctl.c
+++ b/sys/cam/ctl/ctl_frontend_ioctl.c
@@ -1,7 +1,10 @@
/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
* Copyright (c) 2003-2009 Silicon Graphics International Corp.
* Copyright (c) 2012 The FreeBSD Foundation
* Copyright (c) 2015 Alexander Motin <mav@FreeBSD.org>
+ * Copyright (c) 2017 Jakub Wojciech Klama <jceel@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -41,6 +44,8 @@ __FBSDID("$FreeBSD$");
#include <sys/conf.h>
#include <sys/queue.h>
#include <sys/sysctl.h>
+#include <sys/nv.h>
+#include <sys/dnv.h>
#include <cam/cam.h>
#include <cam/scsi/scsi_all.h>
@@ -68,22 +73,41 @@ struct ctl_fe_ioctl_params {
ctl_fe_ioctl_state state;
};
-struct cfi_softc {
+struct cfi_port {
+ TAILQ_ENTRY(cfi_port) link;
uint32_t cur_tag_num;
+ struct cdev * dev;
struct ctl_port port;
};
+struct cfi_softc {
+ TAILQ_HEAD(, cfi_port) ports;
+};
+
+
static struct cfi_softc cfi_softc;
+
static int cfi_init(void);
static int cfi_shutdown(void);
static void cfi_datamove(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 =
{
.name = "ioctl",
.init = cfi_init,
+ .ioctl = cfi_ioctl,
.shutdown = cfi_shutdown,
};
CTL_FRONTEND_DECLARE(ctlioctl, cfi_frontend);
@@ -92,26 +116,31 @@ static int
cfi_init(void)
{
struct cfi_softc *isoftc = &cfi_softc;
+ struct cfi_port *cfi;
struct ctl_port *port;
int error = 0;
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->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 = 0;
port->targ_port = -1;
- port->max_initiators = 1;
if ((error = ctl_port_register(port)) != 0) {
printf("%s: ioctl port registration failed\n", __func__);
return (error);
}
+
ctl_port_online(port);
+ TAILQ_INSERT_TAIL(&isoftc->ports, cfi, link);
return (0);
}
@@ -119,13 +148,185 @@ static int
cfi_shutdown(void)
{
struct cfi_softc *isoftc = &cfi_softc;
- struct ctl_port *port = &isoftc->port;
- int error = 0;
+ struct cfi_port *cfi, *temp;
+ struct ctl_port *port;
+ int error;
+
+ TAILQ_FOREACH_SAFE(cfi, &isoftc->ports, link, temp) {
+ port = &cfi->port;
+ ctl_port_offline(port);
+ error = ctl_port_deregister(port);
+ if (error != 0) {
+ printf("%s: ctl_frontend_deregister() failed\n",
+ __func__);
+ return (error);
+ }
- ctl_port_offline(port);
- if ((error = ctl_port_deregister(port)) != 0)
- printf("%s: ioctl port deregistration failed\n", __func__);
- 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) {
+ 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);
}
/*
@@ -389,18 +590,26 @@ int
ctl_ioctl_io(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
struct thread *td)
{
+ struct cfi_port *cfi;
union ctl_io *io;
void *pool_tmp, *sc_tmp;
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
* to this FETD.
*/
- if ((cfi_softc.port.status & CTL_PORT_STATUS_ONLINE) == 0)
+ if ((cfi->port.status & CTL_PORT_STATUS_ONLINE) == 0)
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
@@ -420,15 +629,16 @@ ctl_ioctl_io(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
/*
* 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;
if ((io->io_hdr.io_type == CTL_IO_SCSI) &&
(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);
if (retval == 0)
memcpy((void *)addr, io, sizeof(*io));
+
ctl_free_io(io);
return (retval);
}