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
This commit is contained in:
Marcelo Araujo 2018-05-10 03:50:20 +00:00
parent 3429b518c9
commit 8951f05525
22 changed files with 932 additions and 710 deletions

View File

@ -4,6 +4,8 @@
* Copyright (c) 2003-2009 Silicon Graphics International Corp.
* Copyright (c) 2012 The FreeBSD Foundation
* Copyright (c) 2014-2017 Alexander Motin <mav@FreeBSD.org>
* Copyright (c) 2017 Jakub Wojciech Klama <jceel@FreeBSD.org>
* Copyright (c) 2018 Marcelo Araujo <araujo@FreeBSD.org>
* All rights reserved.
*
* Portions of this software were developed by Edward Tomasz Napierala
@ -65,6 +67,8 @@ __FBSDID("$FreeBSD$");
#include <sys/smp.h>
#include <sys/endian.h>
#include <sys/sysctl.h>
#include <sys/nv.h>
#include <sys/dnv.h>
#include <vm/uma.h>
#include <cam/cam.h>
@ -1869,6 +1873,7 @@ ctl_init(void)
args.mda_gid = GID_OPERATOR;
args.mda_mode = 0600;
args.mda_si_drv1 = softc;
args.mda_si_drv2 = NULL;
error = make_dev_s(&args, &softc->dev, "cam/ctl");
if (error != 0) {
free(softc, M_DEVBUF);
@ -2468,105 +2473,6 @@ ctl_copyin_alloc(void *user_addr, unsigned int len, char *error_str,
return (kptr);
}
static void
ctl_free_args(int num_args, struct ctl_be_arg *args)
{
int i;
if (args == NULL)
return;
for (i = 0; i < num_args; i++) {
free(args[i].kname, M_CTL);
free(args[i].kvalue, M_CTL);
}
free(args, M_CTL);
}
static struct ctl_be_arg *
ctl_copyin_args(int num_args, struct ctl_be_arg *uargs,
char *error_str, size_t error_str_len)
{
struct ctl_be_arg *args;
int i;
args = ctl_copyin_alloc(uargs, num_args * sizeof(*args),
error_str, error_str_len);
if (args == NULL)
goto bailout;
for (i = 0; i < num_args; i++) {
args[i].kname = NULL;
args[i].kvalue = NULL;
}
for (i = 0; i < num_args; i++) {
uint8_t *tmpptr;
if (args[i].namelen == 0) {
snprintf(error_str, error_str_len, "Argument %d "
"name length is zero", i);
goto bailout;
}
args[i].kname = ctl_copyin_alloc(args[i].name,
args[i].namelen, error_str, error_str_len);
if (args[i].kname == NULL)
goto bailout;
if (args[i].kname[args[i].namelen - 1] != '\0') {
snprintf(error_str, error_str_len, "Argument %d "
"name is not NUL-terminated", i);
goto bailout;
}
if (args[i].flags & CTL_BEARG_RD) {
if (args[i].vallen == 0) {
snprintf(error_str, error_str_len, "Argument %d "
"value length is zero", i);
goto bailout;
}
tmpptr = ctl_copyin_alloc(args[i].value,
args[i].vallen, error_str, error_str_len);
if (tmpptr == NULL)
goto bailout;
if ((args[i].flags & CTL_BEARG_ASCII)
&& (tmpptr[args[i].vallen - 1] != '\0')) {
snprintf(error_str, error_str_len, "Argument "
"%d value is not NUL-terminated", i);
free(tmpptr, M_CTL);
goto bailout;
}
args[i].kvalue = tmpptr;
} else {
args[i].kvalue = malloc(args[i].vallen,
M_CTL, M_WAITOK | M_ZERO);
}
}
return (args);
bailout:
ctl_free_args(num_args, args);
return (NULL);
}
static void
ctl_copyout_args(int num_args, struct ctl_be_arg *args)
{
int i;
for (i = 0; i < num_args; i++) {
if (args[i].flags & CTL_BEARG_WR)
copyout(args[i].kvalue, args[i].value, args[i].vallen);
}
}
/*
* Escape characters that are illegal or not recommended in XML.
*/
@ -3038,8 +2944,12 @@ ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
case CTL_LUN_REQ: {
struct ctl_lun_req *lun_req;
struct ctl_backend_driver *backend;
void *packed;
nvlist_t *tmp_args_nvl;
size_t packed_len;
lun_req = (struct ctl_lun_req *)addr;
tmp_args_nvl = lun_req->args_nvl;
backend = ctl_backend_find(lun_req->backend);
if (backend == NULL) {
@ -3050,32 +2960,68 @@ ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
lun_req->backend);
break;
}
if (lun_req->num_be_args > 0) {
lun_req->kern_be_args = ctl_copyin_args(
lun_req->num_be_args,
lun_req->be_args,
lun_req->error_str,
sizeof(lun_req->error_str));
if (lun_req->kern_be_args == NULL) {
if (lun_req->args != NULL) {
lun_req->args_nvl = nvlist_unpack(lun_req->args,
lun_req->args_len, 0);
if (lun_req->args_nvl == NULL) {
lun_req->status = CTL_LUN_ERROR;
snprintf(lun_req->error_str, sizeof(lun_req->error_str),
"Cannot unpack args nvlist.");
break;
}
}
} else
lun_req->args_nvl = nvlist_create(0);
retval = backend->ioctl(dev, cmd, addr, flag, td);
nvlist_destroy(lun_req->args_nvl);
lun_req->args_nvl = tmp_args_nvl;
if (lun_req->num_be_args > 0) {
ctl_copyout_args(lun_req->num_be_args,
lun_req->kern_be_args);
ctl_free_args(lun_req->num_be_args,
lun_req->kern_be_args);
if (lun_req->result_nvl != NULL) {
if (lun_req->result != NULL) {
packed = nvlist_pack(lun_req->result_nvl,
&packed_len);
if (packed == NULL) {
lun_req->status = CTL_LUN_ERROR;
snprintf(lun_req->error_str,
sizeof(lun_req->error_str),
"Cannot pack result nvlist.");
break;
}
if (packed_len > lun_req->result_len) {
lun_req->status = CTL_LUN_ERROR;
snprintf(lun_req->error_str,
sizeof(lun_req->error_str),
"Result nvlist too large.");
free(packed, M_NVLIST);
break;
}
if (copyout(packed, lun_req->result, packed_len)) {
lun_req->status = CTL_LUN_ERROR;
snprintf(lun_req->error_str,
sizeof(lun_req->error_str),
"Cannot copyout() the result.");
free(packed, M_NVLIST);
break;
}
lun_req->result_len = packed_len;
free(packed, M_NVLIST);
}
nvlist_destroy(lun_req->result_nvl);
}
break;
}
case CTL_LUN_LIST: {
struct sbuf *sb;
struct ctl_lun_list *list;
struct ctl_option *opt;
const char *name, *value;
void *cookie;
int type;
list = (struct ctl_lun_list *)addr;
@ -3201,11 +3147,20 @@ ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
if (retval != 0)
break;
}
STAILQ_FOREACH(opt, &lun->be_lun->options, links) {
retval = sbuf_printf(sb, "\t<%s>%s</%s>\n",
opt->name, opt->value, opt->name);
if (retval != 0)
break;
cookie = NULL;
while ((name = nvlist_next(lun->be_lun->options, &type,
&cookie)) != NULL) {
sbuf_printf(sb, "\t<%s>", name);
if (type == NV_TYPE_STRING) {
value = dnvlist_get_string(
lun->be_lun->options, name, NULL);
if (value != NULL)
sbuf_printf(sb, "%s", value);
}
sbuf_printf(sb, "</%s>\n", name);
}
retval = sbuf_printf(sb, "</lun>\n");
@ -3259,8 +3214,12 @@ ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
case CTL_PORT_REQ: {
struct ctl_req *req;
struct ctl_frontend *fe;
void *packed;
nvlist_t *tmp_args_nvl;
size_t packed_len;
req = (struct ctl_req *)addr;
tmp_args_nvl = req->args_nvl;
fe = ctl_frontend_find(req->driver);
if (fe == NULL) {
@ -3269,23 +3228,63 @@ ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
"Frontend \"%s\" not found.", req->driver);
break;
}
if (req->num_args > 0) {
req->kern_args = ctl_copyin_args(req->num_args,
req->args, req->error_str, sizeof(req->error_str));
if (req->kern_args == NULL) {
if (req->args != NULL) {
req->args_nvl = nvlist_unpack(req->args,
req->args_len, 0);
if (req->args_nvl == NULL) {
req->status = CTL_LUN_ERROR;
snprintf(req->error_str, sizeof(req->error_str),
"Cannot unpack args nvlist.");
break;
}
}
} else
req->args_nvl = nvlist_create(0);
if (fe->ioctl)
retval = fe->ioctl(dev, cmd, addr, flag, td);
else
retval = ENODEV;
if (req->num_args > 0) {
ctl_copyout_args(req->num_args, req->kern_args);
ctl_free_args(req->num_args, req->kern_args);
nvlist_destroy(req->args_nvl);
req->args_nvl = tmp_args_nvl;
if (req->result_nvl != NULL) {
if (req->result != NULL) {
packed = nvlist_pack(req->result_nvl,
&packed_len);
if (packed == NULL) {
req->status = CTL_LUN_ERROR;
snprintf(req->error_str,
sizeof(req->error_str),
"Cannot pack result nvlist.");
break;
}
if (packed_len > req->result_len) {
req->status = CTL_LUN_ERROR;
snprintf(req->error_str,
sizeof(req->error_str),
"Result nvlist too large.");
free(packed, M_NVLIST);
break;
}
if (copyout(packed, req->result, packed_len)) {
req->status = CTL_LUN_ERROR;
snprintf(req->error_str,
sizeof(req->error_str),
"Cannot copyout() the result.");
free(packed, M_NVLIST);
break;
}
req->result_len = packed_len;
free(packed, M_NVLIST);
}
nvlist_destroy(req->result_nvl);
}
break;
}
@ -3293,8 +3292,9 @@ ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
struct sbuf *sb;
struct ctl_port *port;
struct ctl_lun_list *list;
struct ctl_option *opt;
int j;
const char *name, *value;
void *cookie;
int j, type;
uint32_t plun;
list = (struct ctl_lun_list *)addr;
@ -3369,11 +3369,20 @@ ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag,
if (retval != 0)
break;
}
STAILQ_FOREACH(opt, &port->options, links) {
retval = sbuf_printf(sb, "\t<%s>%s</%s>\n",
opt->name, opt->value, opt->name);
if (retval != 0)
break;
cookie = NULL;
while ((name = nvlist_next(port->options, &type,
&cookie)) != NULL) {
sbuf_printf(sb, "\t<%s>", name);
if (type == NV_TYPE_STRING) {
value = dnvlist_get_string(port->options,
name, NULL);
if (value != NULL)
sbuf_printf(sb, "%s", value);
}
sbuf_printf(sb, "</%s>\n", name);
}
if (port->lun_map != NULL) {
@ -4180,8 +4189,8 @@ ctl_init_page_index(struct ctl_lun *lun)
CTL_PAGE_DEFAULT];
scsi_ulto3b(cylinders, rigid_disk_page->cylinders);
if ((value = ctl_get_opt(&lun->be_lun->options,
"rpm")) != NULL) {
if ((value = dnvlist_get_string(lun->be_lun->options,
"rpm", NULL)) != NULL) {
scsi_ulto2b(strtol(value, NULL, 0),
rigid_disk_page->rotation_rate);
}
@ -4234,10 +4243,12 @@ ctl_init_page_index(struct ctl_lun *lun)
sizeof(caching_page_default));
caching_page = &lun->mode_pages.caching_page[
CTL_PAGE_SAVED];
value = ctl_get_opt(&lun->be_lun->options, "writecache");
value = dnvlist_get_string(lun->be_lun->options,
"writecache", NULL);
if (value != NULL && strcmp(value, "off") == 0)
caching_page->flags1 &= ~SCP_WCE;
value = ctl_get_opt(&lun->be_lun->options, "readcache");
value = dnvlist_get_string(lun->be_lun->options,
"readcache", NULL);
if (value != NULL && strcmp(value, "off") == 0)
caching_page->flags1 |= SCP_RCD;
memcpy(&lun->mode_pages.caching_page[CTL_PAGE_CURRENT],
@ -4266,8 +4277,8 @@ ctl_init_page_index(struct ctl_lun *lun)
sizeof(control_page_default));
control_page = &lun->mode_pages.control_page[
CTL_PAGE_SAVED];
value = ctl_get_opt(&lun->be_lun->options,
"reordering");
value = dnvlist_get_string(lun->be_lun->options,
"reordering", NULL);
if (value != NULL &&
strcmp(value, "unrestricted") == 0) {
control_page->queue_flags &=
@ -4342,8 +4353,8 @@ ctl_init_page_index(struct ctl_lun *lun)
&lbp_page_default,
sizeof(lbp_page_default));
page = &lun->mode_pages.lbp_page[CTL_PAGE_SAVED];
value = ctl_get_opt(&lun->be_lun->options,
"avail-threshold");
value = dnvlist_get_string(lun->be_lun->options,
"avail-threshold", NULL);
if (value != NULL &&
ctl_expand_number(value, &ival) == 0) {
page->descr[0].flags |= SLBPPD_ENABLED |
@ -4355,8 +4366,8 @@ ctl_init_page_index(struct ctl_lun *lun)
scsi_ulto4b(ival >> CTL_LBP_EXPONENT,
page->descr[0].count);
}
value = ctl_get_opt(&lun->be_lun->options,
"used-threshold");
value = dnvlist_get_string(lun->be_lun->options,
"used-threshold", NULL);
if (value != NULL &&
ctl_expand_number(value, &ival) == 0) {
page->descr[1].flags |= SLBPPD_ENABLED |
@ -4368,8 +4379,8 @@ ctl_init_page_index(struct ctl_lun *lun)
scsi_ulto4b(ival >> CTL_LBP_EXPONENT,
page->descr[1].count);
}
value = ctl_get_opt(&lun->be_lun->options,
"pool-avail-threshold");
value = dnvlist_get_string(lun->be_lun->options,
"pool-avail-threshold", NULL);
if (value != NULL &&
ctl_expand_number(value, &ival) == 0) {
page->descr[2].flags |= SLBPPD_ENABLED |
@ -4381,8 +4392,8 @@ ctl_init_page_index(struct ctl_lun *lun)
scsi_ulto4b(ival >> CTL_LBP_EXPONENT,
page->descr[2].count);
}
value = ctl_get_opt(&lun->be_lun->options,
"pool-used-threshold");
value = dnvlist_get_string(lun->be_lun->options,
"pool-used-threshold", NULL);
if (value != NULL &&
ctl_expand_number(value, &ival) == 0) {
page->descr[3].flags |= SLBPPD_ENABLED |
@ -4581,20 +4592,20 @@ ctl_alloc_lun(struct ctl_softc *ctl_softc, struct ctl_lun *ctl_lun,
strnlen(be_lun->device_id, CTL_DEVID_LEN));
idlen1 = sizeof(*t10id) + devidlen;
len = sizeof(struct scsi_vpd_id_descriptor) + idlen1;
scsiname = ctl_get_opt(&be_lun->options, "scsiname");
scsiname = dnvlist_get_string(be_lun->options, "scsiname", NULL);
if (scsiname != NULL) {
idlen2 = roundup2(strlen(scsiname) + 1, 4);
len += sizeof(struct scsi_vpd_id_descriptor) + idlen2;
}
eui = ctl_get_opt(&be_lun->options, "eui");
eui = dnvlist_get_string(be_lun->options, "eui", NULL);
if (eui != NULL) {
len += sizeof(struct scsi_vpd_id_descriptor) + 16;
}
naa = ctl_get_opt(&be_lun->options, "naa");
naa = dnvlist_get_string(be_lun->options, "naa", NULL);
if (naa != NULL) {
len += sizeof(struct scsi_vpd_id_descriptor) + 16;
}
uuid = ctl_get_opt(&be_lun->options, "uuid");
uuid = dnvlist_get_string(be_lun->options, "uuid", NULL);
if (uuid != NULL) {
len += sizeof(struct scsi_vpd_id_descriptor) + 18;
}
@ -4606,7 +4617,7 @@ ctl_alloc_lun(struct ctl_softc *ctl_softc, struct ctl_lun *ctl_lun,
desc->length = idlen1;
t10id = (struct scsi_vpd_id_t10 *)&desc->identifier[0];
memset(t10id->vendor, ' ', sizeof(t10id->vendor));
if ((vendor = ctl_get_opt(&be_lun->options, "vendor")) == NULL) {
if ((vendor = dnvlist_get_string(be_lun->options, "vendor", NULL)) == NULL) {
strncpy((char *)t10id->vendor, CTL_VENDOR, sizeof(t10id->vendor));
} else {
strncpy(t10id->vendor, vendor,
@ -4719,7 +4730,7 @@ ctl_alloc_lun(struct ctl_softc *ctl_softc, struct ctl_lun *ctl_lun,
if (be_lun->flags & CTL_LUN_FLAG_PRIMARY)
lun->flags |= CTL_LUN_PRIMARY_SC;
value = ctl_get_opt(&be_lun->options, "removable");
value = dnvlist_get_string(be_lun->options, "removable", NULL);
if (value != NULL) {
if (strcmp(value, "on") == 0)
lun->flags |= CTL_LUN_REMOVABLE;
@ -9772,6 +9783,7 @@ ctl_inquiry_evpd_block_limits(struct ctl_scsiio *ctsio, int alloc_len)
{
struct ctl_lun *lun = CTL_LUN(ctsio);
struct scsi_vpd_block_limits *bl_ptr;
const char *val;
uint64_t ival;
ctsio->kern_data_ptr = malloc(sizeof(*bl_ptr), M_CTL, M_WAITOK | M_ZERO);
@ -9801,12 +9813,16 @@ ctl_inquiry_evpd_block_limits(struct ctl_scsiio *ctsio, int alloc_len)
scsi_ulto4b(lun->be_lun->opttxferlen, bl_ptr->opt_txfer_len);
if (lun->be_lun->flags & CTL_LUN_FLAG_UNMAP) {
ival = 0xffffffff;
ctl_get_opt_number(&lun->be_lun->options,
"unmap_max_lba", &ival);
val = dnvlist_get_string(lun->be_lun->options,
"unmap_max_lba", NULL);
if (val != NULL)
ctl_expand_number(val, &ival);
scsi_ulto4b(ival, bl_ptr->max_unmap_lba_cnt);
ival = 0xffffffff;
ctl_get_opt_number(&lun->be_lun->options,
"unmap_max_descr", &ival);
val = dnvlist_get_string(lun->be_lun->options,
"unmap_max_descr", NULL);
if (val != NULL)
ctl_expand_number(val, &ival);
scsi_ulto4b(ival, bl_ptr->max_unmap_blk_cnt);
if (lun->be_lun->ublockexp != 0) {
scsi_ulto4b((1 << lun->be_lun->ublockexp),
@ -9822,7 +9838,10 @@ ctl_inquiry_evpd_block_limits(struct ctl_scsiio *ctsio, int alloc_len)
scsi_ulto4b(0, bl_ptr->max_atomic_transfer_length_with_atomic_boundary);
scsi_ulto4b(0, bl_ptr->max_atomic_boundary_size);
ival = UINT64_MAX;
ctl_get_opt_number(&lun->be_lun->options, "write_same_max_lba", &ival);
val = dnvlist_get_string(lun->be_lun->options,
"write_same_max_lba", NULL);
if (val != NULL)
ctl_expand_number(val, &ival);
scsi_u64to8b(ival, bl_ptr->max_write_same_length);
}
@ -9861,13 +9880,13 @@ ctl_inquiry_evpd_bdc(struct ctl_scsiio *ctsio, int alloc_len)
bdc_ptr->page_code = SVPD_BDC;
scsi_ulto2b(sizeof(*bdc_ptr) - 4, bdc_ptr->page_length);
if (lun != NULL &&
(value = ctl_get_opt(&lun->be_lun->options, "rpm")) != NULL)
(value = dnvlist_get_string(lun->be_lun->options, "rpm", NULL)) != NULL)
i = strtol(value, NULL, 0);
else
i = CTL_DEFAULT_ROTATION_RATE;
scsi_ulto2b(i, bdc_ptr->medium_rotation_rate);
if (lun != NULL &&
(value = ctl_get_opt(&lun->be_lun->options, "formfactor")) != NULL)
(value = dnvlist_get_string(lun->be_lun->options, "formfactor", NULL)) != NULL)
i = strtol(value, NULL, 0);
else
i = 0;
@ -9912,7 +9931,8 @@ ctl_inquiry_evpd_lbp(struct ctl_scsiio *ctsio, int alloc_len)
if (lun != NULL && lun->be_lun->flags & CTL_LUN_FLAG_UNMAP) {
lbp_ptr->flags = SVPD_LBP_UNMAP | SVPD_LBP_WS16 |
SVPD_LBP_WS10 | SVPD_LBP_RZ | SVPD_LBP_ANC_SUP;
value = ctl_get_opt(&lun->be_lun->options, "provisioning_type");
value = dnvlist_get_string(lun->be_lun->options,
"provisioning_type", NULL);
if (value != NULL) {
if (strcmp(value, "resource") == 0)
lbp_ptr->prov_type = SVPD_LBP_RESOURCE;
@ -10006,7 +10026,7 @@ ctl_inquiry_std(struct ctl_scsiio *ctsio)
struct ctl_lun *lun = CTL_LUN(ctsio);
struct scsi_inquiry_data *inq_ptr;
struct scsi_inquiry *cdb;
char *val;
const char *val;
uint32_t alloc_len, data_len;
ctl_port_type port_type;
@ -10084,8 +10104,8 @@ ctl_inquiry_std(struct ctl_scsiio *ctsio)
* We have 8 bytes for the vendor name, and 16 bytes for the device
* name and 4 bytes for the revision.
*/
if (lun == NULL || (val = ctl_get_opt(&lun->be_lun->options,
"vendor")) == NULL) {
if (lun == NULL || (val = dnvlist_get_string(lun->be_lun->options,
"vendor", NULL)) == NULL) {
strncpy(inq_ptr->vendor, CTL_VENDOR, sizeof(inq_ptr->vendor));
} else {
memset(inq_ptr->vendor, ' ', sizeof(inq_ptr->vendor));
@ -10095,7 +10115,8 @@ ctl_inquiry_std(struct ctl_scsiio *ctsio)
if (lun == NULL) {
strncpy(inq_ptr->product, CTL_DIRECT_PRODUCT,
sizeof(inq_ptr->product));
} else if ((val = ctl_get_opt(&lun->be_lun->options, "product")) == NULL) {
} else if ((val = dnvlist_get_string(lun->be_lun->options, "product",
NULL)) == NULL) {
switch (lun->be_lun->lun_type) {
case T_DIRECT:
strncpy(inq_ptr->product, CTL_DIRECT_PRODUCT,
@ -10124,8 +10145,8 @@ ctl_inquiry_std(struct ctl_scsiio *ctsio)
* XXX make this a macro somewhere so it automatically gets
* incremented when we make changes.
*/
if (lun == NULL || (val = ctl_get_opt(&lun->be_lun->options,
"revision")) == NULL) {
if (lun == NULL || (val = dnvlist_get_string(lun->be_lun->options,
"revision", NULL)) == NULL) {
strncpy(inq_ptr->revision, "0001", sizeof(inq_ptr->revision));
} else {
memset(inq_ptr->revision, ' ', sizeof(inq_ptr->revision));

View File

@ -196,24 +196,6 @@ void ctl_isc_announce_iid(struct ctl_port *port, int iid);
void ctl_isc_announce_mode(struct ctl_lun *lun, uint32_t initidx,
uint8_t page, uint8_t subpage);
/*
* KPI to manipulate LUN/port options
*/
struct ctl_option {
STAILQ_ENTRY(ctl_option) links;
char *name;
char *value;
};
typedef STAILQ_HEAD(ctl_options, ctl_option) ctl_options_t;
struct ctl_be_arg;
void ctl_init_opts(ctl_options_t *opts, int num_args, struct ctl_be_arg *args);
void ctl_update_opts(ctl_options_t *opts, int num_args,
struct ctl_be_arg *args);
void ctl_free_opts(ctl_options_t *opts);
char * ctl_get_opt(ctl_options_t *opts, const char *name);
int ctl_get_opt_number(ctl_options_t *opts, const char *name, uint64_t *num);
int ctl_expand_number(const char *buf, uint64_t *num);
#endif /* _KERNEL */

View File

@ -141,93 +141,3 @@ ctl_backend_find(char *backend_name)
return (NULL);
}
void
ctl_init_opts(ctl_options_t *opts, int num_args, struct ctl_be_arg *args)
{
struct ctl_option *opt;
int i;
STAILQ_INIT(opts);
for (i = 0; i < num_args; i++) {
if ((args[i].flags & CTL_BEARG_RD) == 0)
continue;
if ((args[i].flags & CTL_BEARG_ASCII) == 0)
continue;
opt = malloc(sizeof(*opt), M_CTL, M_WAITOK);
opt->name = strdup(args[i].kname, M_CTL);
opt->value = strdup(args[i].kvalue, M_CTL);
STAILQ_INSERT_TAIL(opts, opt, links);
}
}
void
ctl_update_opts(ctl_options_t *opts, int num_args, struct ctl_be_arg *args)
{
struct ctl_option *opt;
int i;
for (i = 0; i < num_args; i++) {
if ((args[i].flags & CTL_BEARG_RD) == 0)
continue;
if ((args[i].flags & CTL_BEARG_ASCII) == 0)
continue;
STAILQ_FOREACH(opt, opts, links) {
if (strcmp(opt->name, args[i].kname) == 0)
break;
}
if (args[i].kvalue != NULL &&
((char *)args[i].kvalue)[0] != 0) {
if (opt) {
free(opt->value, M_CTL);
opt->value = strdup(args[i].kvalue, M_CTL);
} else {
opt = malloc(sizeof(*opt), M_CTL, M_WAITOK);
opt->name = strdup(args[i].kname, M_CTL);
opt->value = strdup(args[i].kvalue, M_CTL);
STAILQ_INSERT_TAIL(opts, opt, links);
}
} else if (opt) {
STAILQ_REMOVE(opts, opt, ctl_option, links);
free(opt->name, M_CTL);
free(opt->value, M_CTL);
free(opt, M_CTL);
}
}
}
void
ctl_free_opts(ctl_options_t *opts)
{
struct ctl_option *opt;
while ((opt = STAILQ_FIRST(opts)) != NULL) {
STAILQ_REMOVE_HEAD(opts, links);
free(opt->name, M_CTL);
free(opt->value, M_CTL);
free(opt, M_CTL);
}
}
char *
ctl_get_opt(ctl_options_t *opts, const char *name)
{
struct ctl_option *opt;
STAILQ_FOREACH(opt, opts, links) {
if (strcmp(opt->name, name) == 0) {
return (opt->value);
}
}
return (NULL);
}
int
ctl_get_opt_number(ctl_options_t *opts, const char *name, uint64_t *val)
{
const char *value;
value = ctl_get_opt(opts, name);
if (value == NULL)
return (-2);
return (ctl_expand_number(value, val));
}

View File

@ -43,6 +43,7 @@
#define _CTL_BACKEND_H_
#include <cam/ctl/ctl_ioctl.h>
#include <sys/nv.h>
typedef enum {
CTL_LUN_SERSEQ_OFF,
@ -175,7 +176,7 @@ struct ctl_be_lun {
be_lun_config_t lun_config_status; /* passed to CTL */
struct ctl_backend_driver *be; /* passed to CTL */
void *ctl_lun; /* used by CTL */
ctl_options_t options; /* passed to CTL */
nvlist_t *options; /* passed to CTL */
STAILQ_ENTRY(ctl_be_lun) links; /* used by CTL */
};

View File

@ -78,6 +78,8 @@ __FBSDID("$FreeBSD$");
#include <sys/sdt.h>
#include <sys/devicestat.h>
#include <sys/sysctl.h>
#include <sys/nv.h>
#include <sys/dnv.h>
#include <geom/geom.h>
@ -1817,7 +1819,7 @@ ctl_be_block_open_file(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req)
struct ctl_be_lun *cbe_lun;
struct ctl_be_block_filedata *file_data;
struct ctl_lun_create_params *params;
char *value;
const char *value;
struct vattr vattr;
off_t ps, pss, po, pos, us, uss, uo, uos;
int error;
@ -1867,10 +1869,10 @@ ctl_be_block_open_file(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req)
us = ps = vattr.va_blocksize;
uo = po = 0;
value = ctl_get_opt(&cbe_lun->options, "pblocksize");
value = dnvlist_get_string(cbe_lun->options, "pblocksize", NULL);
if (value != NULL)
ctl_expand_number(value, &ps);
value = ctl_get_opt(&cbe_lun->options, "pblockoffset");
value = dnvlist_get_string(cbe_lun->options, "pblockoffset", NULL);
if (value != NULL)
ctl_expand_number(value, &po);
pss = ps / cbe_lun->blocksize;
@ -1881,10 +1883,10 @@ ctl_be_block_open_file(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req)
cbe_lun->pblockoff = (pss - pos) % pss;
}
value = ctl_get_opt(&cbe_lun->options, "ublocksize");
value = dnvlist_get_string(cbe_lun->options, "ublocksize", NULL);
if (value != NULL)
ctl_expand_number(value, &us);
value = ctl_get_opt(&cbe_lun->options, "ublockoffset");
value = dnvlist_get_string(cbe_lun->options, "ublockoffset", NULL);
if (value != NULL)
ctl_expand_number(value, &uo);
uss = us / cbe_lun->blocksize;
@ -1917,7 +1919,7 @@ ctl_be_block_open_dev(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req)
struct ctl_lun_create_params *params;
struct cdevsw *csw;
struct cdev *dev;
char *value;
const char *value;
int error, atomic, maxio, ref, unmap, tmp;
off_t ps, pss, po, pos, us, uss, uo, uos, otmp;
@ -2033,10 +2035,10 @@ ctl_be_block_open_dev(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req)
us = ps;
uo = po;
value = ctl_get_opt(&cbe_lun->options, "pblocksize");
value = dnvlist_get_string(cbe_lun->options, "pblocksize", NULL);
if (value != NULL)
ctl_expand_number(value, &ps);
value = ctl_get_opt(&cbe_lun->options, "pblockoffset");
value = dnvlist_get_string(cbe_lun->options, "pblockoffset", NULL);
if (value != NULL)
ctl_expand_number(value, &po);
pss = ps / cbe_lun->blocksize;
@ -2047,10 +2049,10 @@ ctl_be_block_open_dev(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req)
cbe_lun->pblockoff = (pss - pos) % pss;
}
value = ctl_get_opt(&cbe_lun->options, "ublocksize");
value = dnvlist_get_string(cbe_lun->options, "ublocksize", NULL);
if (value != NULL)
ctl_expand_number(value, &us);
value = ctl_get_opt(&cbe_lun->options, "ublockoffset");
value = dnvlist_get_string(cbe_lun->options, "ublockoffset", NULL);
if (value != NULL)
ctl_expand_number(value, &uo);
uss = us / cbe_lun->blocksize;
@ -2075,7 +2077,7 @@ ctl_be_block_open_dev(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req)
curthread);
unmap = (error == 0) ? arg.value.i : 0;
}
value = ctl_get_opt(&cbe_lun->options, "unmap");
value = dnvlist_get_string(cbe_lun->options, "unmap", NULL);
if (value != NULL)
unmap = (strcmp(value, "on") == 0);
if (unmap)
@ -2125,7 +2127,7 @@ ctl_be_block_open(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req)
{
struct ctl_be_lun *cbe_lun = &be_lun->cbe_lun;
struct nameidata nd;
char *value;
const char *value;
int error, flags;
error = 0;
@ -2136,7 +2138,7 @@ ctl_be_block_open(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req)
}
pwd_ensure_dirs();
value = ctl_get_opt(&cbe_lun->options, "file");
value = dnvlist_get_string(cbe_lun->options, "file", NULL);
if (value == NULL) {
snprintf(req->error_str, sizeof(req->error_str),
"no file argument specified");
@ -2146,7 +2148,7 @@ ctl_be_block_open(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req)
be_lun->dev_path = strdup(value, M_CTLBLK);
flags = FREAD;
value = ctl_get_opt(&cbe_lun->options, "readonly");
value = dnvlist_get_string(cbe_lun->options, "readonly", NULL);
if (value != NULL) {
if (strcmp(value, "on") != 0)
flags |= FWRITE;
@ -2205,7 +2207,7 @@ ctl_be_block_open(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req)
cbe_lun->serseq = CTL_LUN_SERSEQ_OFF;
if (be_lun->dispatch != ctl_be_block_dispatch_dev)
cbe_lun->serseq = CTL_LUN_SERSEQ_READ;
value = ctl_get_opt(&cbe_lun->options, "serseq");
value = dnvlist_get_string(cbe_lun->options, "serseq", NULL);
if (value != NULL && strcmp(value, "on") == 0)
cbe_lun->serseq = CTL_LUN_SERSEQ_ON;
else if (value != NULL && strcmp(value, "read") == 0)
@ -2223,7 +2225,7 @@ ctl_be_block_create(struct ctl_be_block_softc *softc, struct ctl_lun_req *req)
struct ctl_lun_create_params *params;
char num_thread_str[16];
char tmpstr[32];
char *value;
const char *value;
int retval, num_threads;
int tmp_num_threads;
@ -2243,8 +2245,7 @@ ctl_be_block_create(struct ctl_be_block_softc *softc, struct ctl_lun_req *req)
sprintf(be_lun->lunname, "cblk%d", softc->num_luns);
mtx_init(&be_lun->io_lock, "cblk io lock", NULL, MTX_DEF);
mtx_init(&be_lun->queue_lock, "cblk queue lock", NULL, MTX_DEF);
ctl_init_opts(&cbe_lun->options,
req->num_be_args, req->kern_be_args);
cbe_lun->options = nvlist_clone(req->args_nvl);
be_lun->lun_zone = uma_zcreate(be_lun->lunname, CTLBLK_MAX_SEG,
NULL, NULL, NULL, NULL, /*align*/ 0, /*flags*/0);
if (be_lun->lun_zone == NULL) {
@ -2259,7 +2260,7 @@ ctl_be_block_create(struct ctl_be_block_softc *softc, struct ctl_lun_req *req)
cbe_lun->lun_type = T_DIRECT;
be_lun->flags = CTL_BE_BLOCK_LUN_UNCONFIGURED;
cbe_lun->flags = 0;
value = ctl_get_opt(&cbe_lun->options, "ha_role");
value = dnvlist_get_string(cbe_lun->options, "ha_role", NULL);
if (value != NULL) {
if (strcmp(value, "primary") == 0)
cbe_lun->flags |= CTL_LUN_FLAG_PRIMARY;
@ -2292,7 +2293,7 @@ ctl_be_block_create(struct ctl_be_block_softc *softc, struct ctl_lun_req *req)
num_threads = 1;
}
value = ctl_get_opt(&cbe_lun->options, "num_threads");
value = dnvlist_get_string(cbe_lun->options, "num_threads", NULL);
if (value != NULL) {
tmp_num_threads = strtol(value, NULL, 0);
@ -2457,7 +2458,7 @@ ctl_be_block_create(struct ctl_be_block_softc *softc, struct ctl_lun_req *req)
free(be_lun->dev_path, M_CTLBLK);
if (be_lun->lun_zone != NULL)
uma_zdestroy(be_lun->lun_zone);
ctl_free_opts(&cbe_lun->options);
nvlist_destroy(cbe_lun->options);
mtx_destroy(&be_lun->queue_lock);
mtx_destroy(&be_lun->io_lock);
free(be_lun, M_CTLBLK);
@ -2541,7 +2542,7 @@ ctl_be_block_rm(struct ctl_be_block_softc *softc, struct ctl_lun_req *req)
uma_zdestroy(be_lun->lun_zone);
ctl_free_opts(&cbe_lun->options);
nvlist_destroy(cbe_lun->options);
free(be_lun->dev_path, M_CTLBLK);
mtx_destroy(&be_lun->queue_lock);
mtx_destroy(&be_lun->io_lock);
@ -2561,7 +2562,7 @@ ctl_be_block_modify(struct ctl_be_block_softc *softc, struct ctl_lun_req *req)
struct ctl_lun_modify_params *params;
struct ctl_be_block_lun *be_lun;
struct ctl_be_lun *cbe_lun;
char *value;
const char *value;
uint64_t oldsize;
int error, wasprim;
@ -2583,10 +2584,12 @@ ctl_be_block_modify(struct ctl_be_block_softc *softc, struct ctl_lun_req *req)
if (params->lun_size_bytes != 0)
be_lun->params.lun_size_bytes = params->lun_size_bytes;
ctl_update_opts(&cbe_lun->options, req->num_be_args, req->kern_be_args);
nvlist_destroy(cbe_lun->options);
cbe_lun->options = nvlist_clone(req->args_nvl);
wasprim = (cbe_lun->flags & CTL_LUN_FLAG_PRIMARY);
value = ctl_get_opt(&cbe_lun->options, "ha_role");
value = dnvlist_get_string(cbe_lun->options, "ha_role", NULL);
if (value != NULL) {
if (strcmp(value, "primary") == 0)
cbe_lun->flags |= CTL_LUN_FLAG_PRIMARY;

View File

@ -62,6 +62,8 @@ __FBSDID("$FreeBSD$");
#include <sys/ioccom.h>
#include <sys/module.h>
#include <sys/sysctl.h>
#include <sys/nv.h>
#include <sys/dnv.h>
#include <cam/scsi/scsi_all.h>
#include <cam/scsi/scsi_da.h>
@ -956,7 +958,7 @@ ctl_backend_ramdisk_rm(struct ctl_be_ramdisk_softc *softc,
if (retval == 0) {
taskqueue_drain_all(be_lun->io_taskqueue);
taskqueue_free(be_lun->io_taskqueue);
ctl_free_opts(&be_lun->cbe_lun.options);
nvlist_destroy(be_lun->cbe_lun.options);
free(be_lun->zero_page, M_RAMDISK);
ctl_backend_ramdisk_freeallpages(be_lun->pages, be_lun->indir);
sx_destroy(&be_lun->page_lock);
@ -979,7 +981,7 @@ ctl_backend_ramdisk_create(struct ctl_be_ramdisk_softc *softc,
struct ctl_be_ramdisk_lun *be_lun;
struct ctl_be_lun *cbe_lun;
struct ctl_lun_create_params *params;
char *value;
const char *value;
char tmpstr[32];
uint64_t t;
int retval;
@ -990,10 +992,10 @@ ctl_backend_ramdisk_create(struct ctl_be_ramdisk_softc *softc,
be_lun = malloc(sizeof(*be_lun), M_RAMDISK, M_ZERO | M_WAITOK);
cbe_lun = &be_lun->cbe_lun;
cbe_lun->be_lun = be_lun;
cbe_lun->options = nvlist_clone(req->args_nvl);
be_lun->params = req->reqdata.create;
be_lun->softc = softc;
sprintf(be_lun->lunname, "cram%d", softc->num_luns);
ctl_init_opts(&cbe_lun->options, req->num_be_args, req->kern_be_args);
if (params->flags & CTL_LUN_FLAG_DEV_TYPE)
cbe_lun->lun_type = params->device_type;
@ -1001,7 +1003,7 @@ ctl_backend_ramdisk_create(struct ctl_be_ramdisk_softc *softc,
cbe_lun->lun_type = T_DIRECT;
be_lun->flags = CTL_BE_RAMDISK_LUN_UNCONFIGURED;
cbe_lun->flags = 0;
value = ctl_get_opt(&cbe_lun->options, "ha_role");
value = dnvlist_get_string(cbe_lun->options, "ha_role", NULL);
if (value != NULL) {
if (strcmp(value, "primary") == 0)
cbe_lun->flags |= CTL_LUN_FLAG_PRIMARY;
@ -1009,7 +1011,7 @@ ctl_backend_ramdisk_create(struct ctl_be_ramdisk_softc *softc,
cbe_lun->flags |= CTL_LUN_FLAG_PRIMARY;
be_lun->pblocksize = PAGE_SIZE;
value = ctl_get_opt(&cbe_lun->options, "pblocksize");
value = dnvlist_get_string(cbe_lun->options, "pblocksize", NULL);
if (value != NULL) {
ctl_expand_number(value, &t);
be_lun->pblocksize = t;
@ -1058,7 +1060,7 @@ ctl_backend_ramdisk_create(struct ctl_be_ramdisk_softc *softc,
cbe_lun->ublockoff = 0;
cbe_lun->atomicblock = be_lun->pblocksize;
cbe_lun->opttxferlen = SGPP * be_lun->pblocksize;
value = ctl_get_opt(&cbe_lun->options, "capacity");
value = dnvlist_get_string(cbe_lun->options, "capacity", NULL);
if (value != NULL)
ctl_expand_number(value, &be_lun->cap_bytes);
} else {
@ -1070,17 +1072,17 @@ ctl_backend_ramdisk_create(struct ctl_be_ramdisk_softc *softc,
params->blocksize_bytes = cbe_lun->blocksize;
params->lun_size_bytes = be_lun->size_bytes;
value = ctl_get_opt(&cbe_lun->options, "unmap");
if (value == NULL || strcmp(value, "off") != 0)
value = dnvlist_get_string(cbe_lun->options, "unmap", NULL);
if (value != NULL && strcmp(value, "off") != 0)
cbe_lun->flags |= CTL_LUN_FLAG_UNMAP;
value = ctl_get_opt(&cbe_lun->options, "readonly");
value = dnvlist_get_string(cbe_lun->options, "readonly", NULL);
if (value != NULL) {
if (strcmp(value, "on") == 0)
cbe_lun->flags |= CTL_LUN_FLAG_READONLY;
} else if (cbe_lun->lun_type != T_DIRECT)
cbe_lun->flags |= CTL_LUN_FLAG_READONLY;
cbe_lun->serseq = CTL_LUN_SERSEQ_OFF;
value = ctl_get_opt(&cbe_lun->options, "serseq");
value = dnvlist_get_string(cbe_lun->options, "serseq", NULL);
if (value != NULL && strcmp(value, "on") == 0)
cbe_lun->serseq = CTL_LUN_SERSEQ_ON;
else if (value != NULL && strcmp(value, "read") == 0)
@ -1209,7 +1211,7 @@ ctl_backend_ramdisk_create(struct ctl_be_ramdisk_softc *softc,
if (be_lun != NULL) {
if (be_lun->io_taskqueue != NULL)
taskqueue_free(be_lun->io_taskqueue);
ctl_free_opts(&cbe_lun->options);
nvlist_destroy(cbe_lun->options);
free(be_lun->zero_page, M_RAMDISK);
ctl_backend_ramdisk_freeallpages(be_lun->pages, be_lun->indir);
sx_destroy(&be_lun->page_lock);
@ -1226,7 +1228,7 @@ ctl_backend_ramdisk_modify(struct ctl_be_ramdisk_softc *softc,
struct ctl_be_ramdisk_lun *be_lun;
struct ctl_be_lun *cbe_lun;
struct ctl_lun_modify_params *params;
char *value;
const char *value;
uint32_t blocksize;
int wasprim;
@ -1248,10 +1250,12 @@ ctl_backend_ramdisk_modify(struct ctl_be_ramdisk_softc *softc,
if (params->lun_size_bytes != 0)
be_lun->params.lun_size_bytes = params->lun_size_bytes;
ctl_update_opts(&cbe_lun->options, req->num_be_args, req->kern_be_args);
nvlist_destroy(cbe_lun->options);
cbe_lun->options = nvlist_clone(req->args_nvl);
wasprim = (cbe_lun->flags & CTL_LUN_FLAG_PRIMARY);
value = ctl_get_opt(&cbe_lun->options, "ha_role");
value = dnvlist_get_string(cbe_lun->options, "ha_role", NULL);
if (value != NULL) {
if (strcmp(value, "primary") == 0)
cbe_lun->flags |= CTL_LUN_FLAG_PRIMARY;

View File

@ -52,6 +52,8 @@ __FBSDID("$FreeBSD$");
#include <sys/endian.h>
#include <sys/queue.h>
#include <sys/sysctl.h>
#include <sys/nv.h>
#include <sys/dnv.h>
#include <cam/scsi/scsi_all.h>
#include <cam/scsi/scsi_da.h>
@ -200,8 +202,8 @@ ctl_port_register(struct ctl_port *port)
}
port->targ_port = port_num;
port->ctl_pool_ref = pool;
if (port->options.stqh_first == NULL)
STAILQ_INIT(&port->options);
if (port->options == NULL)
port->options = nvlist_create(0);
port->stats.item = port_num;
mtx_init(&port->port_lock, "CTL port", NULL, MTX_DEF);
@ -240,7 +242,7 @@ ctl_port_deregister(struct ctl_port *port)
mtx_unlock(&softc->ctl_lock);
ctl_pool_free(pool);
ctl_free_opts(&port->options);
nvlist_destroy(port->options);
ctl_lun_map_deinit(port);
free(port->port_devid, M_CTL);
@ -333,7 +335,7 @@ ctl_port_online(struct ctl_port *port)
port->port_online(port->onoff_arg);
mtx_lock(&softc->ctl_lock);
if (softc->is_single == 0) {
value = ctl_get_opt(&port->options, "ha_shared");
value = dnvlist_get_string(port->options, "ha_shared", NULL);
if (value != NULL && strcmp(value, "on") == 0)
port->status |= CTL_PORT_STATUS_HA_SHARED;
else

View File

@ -43,6 +43,7 @@
#define _CTL_FRONTEND_H_
#include <cam/ctl/ctl_ioctl.h>
#include <sys/nv.h>
typedef enum {
CTL_PORT_STATUS_NONE = 0x00,
@ -237,7 +238,7 @@ struct ctl_port {
uint64_t wwnn; /* set by CTL before online */
uint64_t wwpn; /* set by CTL before online */
ctl_port_status status; /* used by CTL */
ctl_options_t options; /* passed to CTL */
nvlist_t *options; /* passed to CTL */
struct ctl_devid *port_devid; /* passed to CTL */
struct ctl_devid *target_devid; /* passed to CTL */
struct ctl_devid *init_devid; /* passed to CTL */

View File

@ -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;
ctl_port_offline(port);
if ((error = ctl_port_deregister(port)) != 0)
printf("%s: ioctl port deregistration failed\n", __func__);
return (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);
}
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);
}

View File

@ -56,6 +56,8 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/unistd.h>
#include <sys/nv.h>
#include <sys/dnv.h>
#include <vm/uma.h>
#include <cam/scsi/scsi_all.h>
@ -2105,30 +2107,30 @@ cfiscsi_ioctl_port_create(struct ctl_req *req)
{
struct cfiscsi_target *ct;
struct ctl_port *port;
const char *target, *alias, *tags;
const char *target, *alias, *val;
struct scsi_vpd_id_descriptor *desc;
ctl_options_t opts;
int retval, len, idlen;
uint16_t tag;
ctl_init_opts(&opts, req->num_args, req->kern_args);
target = ctl_get_opt(&opts, "cfiscsi_target");
alias = ctl_get_opt(&opts, "cfiscsi_target_alias");
tags = ctl_get_opt(&opts, "cfiscsi_portal_group_tag");
if (target == NULL || tags == NULL) {
target = dnvlist_get_string(req->args_nvl, "cfiscsi_target", NULL);
alias = dnvlist_get_string(req->args_nvl, "cfiscsi_target_alias", NULL);
val = dnvlist_get_string(req->args_nvl, "cfiscsi_portal_group_tag",
NULL);
if (target == NULL || val == NULL) {
req->status = CTL_LUN_ERROR;
snprintf(req->error_str, sizeof(req->error_str),
"Missing required argument");
ctl_free_opts(&opts);
return;
}
tag = strtol(tags, (char **)NULL, 10);
tag = strtoul(val, NULL, 0);
ct = cfiscsi_target_find_or_create(&cfiscsi_softc, target, alias, tag);
if (ct == NULL) {
req->status = CTL_LUN_ERROR;
snprintf(req->error_str, sizeof(req->error_str),
"failed to create target \"%s\"", target);
ctl_free_opts(&opts);
return;
}
if (ct->ct_state == CFISCSI_TARGET_STATE_ACTIVE) {
@ -2137,7 +2139,6 @@ cfiscsi_ioctl_port_create(struct ctl_req *req)
"target \"%s\" for portal group tag %u already exists",
target, tag);
cfiscsi_target_release(ct);
ctl_free_opts(&opts);
return;
}
port = &ct->ct_port;
@ -2150,7 +2151,7 @@ cfiscsi_ioctl_port_create(struct ctl_req *req)
/* XXX KDM what should the real number be here? */
port->num_requested_ctl_io = 4096;
port->port_name = "iscsi";
port->physical_port = tag;
port->physical_port = (int)tag;
port->virtual_port = ct->ct_target_id;
port->port_online = cfiscsi_online;
port->port_offline = cfiscsi_offline;
@ -2159,9 +2160,7 @@ cfiscsi_ioctl_port_create(struct ctl_req *req)
port->fe_datamove = cfiscsi_datamove;
port->fe_done = cfiscsi_done;
port->targ_port = -1;
port->options = opts;
STAILQ_INIT(&opts);
port->options = nvlist_clone(req->args_nvl);
/* Generate Port ID. */
idlen = strlen(target) + strlen(",t,0x0001") + 1;
@ -2193,7 +2192,6 @@ cfiscsi_ioctl_port_create(struct ctl_req *req)
retval = ctl_port_register(port);
if (retval != 0) {
ctl_free_opts(&port->options);
free(port->port_devid, M_CFISCSI);
free(port->target_devid, M_CFISCSI);
cfiscsi_target_release(ct);
@ -2205,45 +2203,42 @@ cfiscsi_ioctl_port_create(struct ctl_req *req)
done:
ct->ct_state = CFISCSI_TARGET_STATE_ACTIVE;
req->status = CTL_LUN_OK;
memcpy(req->kern_args[0].kvalue, &port->targ_port,
sizeof(port->targ_port)); //XXX
req->result_nvl = nvlist_create(0);
nvlist_add_number(req->result_nvl, "port_id", port->targ_port);
}
static void
cfiscsi_ioctl_port_remove(struct ctl_req *req)
{
struct cfiscsi_target *ct;
const char *target, *tags;
ctl_options_t opts;
const char *target, *val;
uint16_t tag;
ctl_init_opts(&opts, req->num_args, req->kern_args);
target = ctl_get_opt(&opts, "cfiscsi_target");
tags = ctl_get_opt(&opts, "cfiscsi_portal_group_tag");
if (target == NULL || tags == NULL) {
ctl_free_opts(&opts);
target = dnvlist_get_string(req->args_nvl, "cfiscsi_target", NULL);
val = dnvlist_get_string(req->args_nvl, "cfiscsi_portal_group_tag",
NULL);
if (target == NULL || val == NULL) {
req->status = CTL_LUN_ERROR;
snprintf(req->error_str, sizeof(req->error_str),
"Missing required argument");
return;
}
tag = strtol(tags, (char **)NULL, 10);
tag = strtoul(val, NULL, 0);
ct = cfiscsi_target_find(&cfiscsi_softc, target, tag);
if (ct == NULL) {
ctl_free_opts(&opts);
req->status = CTL_LUN_ERROR;
snprintf(req->error_str, sizeof(req->error_str),
"can't find target \"%s\"", target);
return;
}
if (ct->ct_state != CFISCSI_TARGET_STATE_ACTIVE) {
ctl_free_opts(&opts);
req->status = CTL_LUN_ERROR;
snprintf(req->error_str, sizeof(req->error_str),
"target \"%s\" is already dying", target);
return;
}
ctl_free_opts(&opts);
ct->ct_state = CFISCSI_TARGET_STATE_DYING;
ctl_port_offline(&ct->ct_port);

View File

@ -48,6 +48,7 @@
#endif
#include <sys/ioccom.h>
#include <sys/nv.h>
#define CTL_DEFAULT_DEV "/dev/cam/ctl"
/*
@ -316,39 +317,6 @@ typedef enum {
#define CTL_ERROR_STR_LEN 160
#define CTL_BEARG_RD 0x01
#define CTL_BEARG_WR 0x02
#define CTL_BEARG_RW (CTL_BEARG_RD|CTL_BEARG_WR)
#define CTL_BEARG_ASCII 0x04
/*
* Backend Argument:
*
* namelen: Length of the name field, including the terminating NUL.
*
* name: Name of the parameter. This must be NUL-terminated.
*
* flags: Flags for the parameter, see above for values.
*
* vallen: Length of the value in bytes, including the terminating NUL.
*
* value: Value to be set/fetched. This must be NUL-terminated.
*
* kname: For kernel use only.
*
* kvalue: For kernel use only.
*/
struct ctl_be_arg {
unsigned int namelen;
char *name;
int flags;
unsigned int vallen;
void *value;
char *kname;
void *kvalue;
};
typedef enum {
CTL_LUNREQ_CREATE,
CTL_LUNREQ_RM,
@ -524,11 +492,14 @@ struct ctl_lun_req {
char backend[CTL_BE_NAME_LEN];
ctl_lunreq_type reqtype;
union ctl_lunreq_data reqdata;
int num_be_args;
struct ctl_be_arg *be_args;
void * args;
nvlist_t * args_nvl;
size_t args_len;
void * result;
nvlist_t * result_nvl;
size_t result_len;
ctl_lun_status status;
char error_str[CTL_ERROR_STR_LEN];
struct ctl_be_arg *kern_be_args;
};
/*
@ -617,11 +588,14 @@ typedef enum {
struct ctl_req {
char driver[CTL_DRIVER_NAME_LEN];
ctl_req_type reqtype;
int num_args;
struct ctl_be_arg *args;
void * args;
nvlist_t * args_nvl;
size_t args_len;
void * result;
nvlist_t * result_nvl;
size_t result_len;
ctl_lun_status status;
char error_str[CTL_ERROR_STR_LEN];
struct ctl_be_arg *kern_args;
};
/*

View File

@ -41,6 +41,8 @@ __FBSDID("$FreeBSD$");
#include <sys/conf.h>
#include <sys/queue.h>
#include <sys/sysctl.h>
#include <sys/nv.h>
#include <sys/dnv.h>
#include <machine/atomic.h>
#include <cam/cam.h>
@ -1668,7 +1670,7 @@ ctl_extended_copy_lid1(struct ctl_scsiio *ctsio)
struct scsi_ec_segment *seg;
struct tpc_list *list, *tlist;
uint8_t *ptr;
char *value;
const char *value;
int len, off, lencscd, lenseg, leninl, nseg;
CTL_DEBUG_PRINT(("ctl_extended_copy_lid1\n"));
@ -1731,7 +1733,7 @@ ctl_extended_copy_lid1(struct ctl_scsiio *ctsio)
list = malloc(sizeof(struct tpc_list), M_CTL, M_WAITOK | M_ZERO);
list->service_action = cdb->service_action;
value = ctl_get_opt(&lun->be_lun->options, "insecure_tpc");
value = dnvlist_get_string(lun->be_lun->options, "insecure_tpc", NULL);
if (value != NULL && strcmp(value, "on") == 0)
list->init_port = -1;
else
@ -1822,7 +1824,7 @@ ctl_extended_copy_lid4(struct ctl_scsiio *ctsio)
struct scsi_ec_segment *seg;
struct tpc_list *list, *tlist;
uint8_t *ptr;
char *value;
const char *value;
int len, off, lencscd, lenseg, leninl, nseg;
CTL_DEBUG_PRINT(("ctl_extended_copy_lid4\n"));
@ -1885,7 +1887,7 @@ ctl_extended_copy_lid4(struct ctl_scsiio *ctsio)
list = malloc(sizeof(struct tpc_list), M_CTL, M_WAITOK | M_ZERO);
list->service_action = cdb->service_action;
value = ctl_get_opt(&lun->be_lun->options, "insecure_tpc");
value = dnvlist_get_string(lun->be_lun->options, "insecure_tpc", NULL);
if (value != NULL && strcmp(value, "on") == 0)
list->init_port = -1;
else

View File

@ -60,7 +60,7 @@
* in the range 5 to 9.
*/
#undef __FreeBSD_version
#define __FreeBSD_version 1200062 /* Master, propagated to newvers */
#define __FreeBSD_version 1200063 /* Master, propagated to newvers */
/*
* __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD,

View File

@ -16,7 +16,7 @@ CFLAGS+= -I${SDIR}
WARNS?= 3
.endif
LIBADD= cam sbuf bsdxml util
LIBADD= cam sbuf bsdxml util nv
MAN= ctladm.8
.if ${MK_ISCSI} != "no"

View File

@ -1,6 +1,7 @@
.\"
.\" Copyright (c) 2003 Silicon Graphics International Corp.
.\" Copyright (c) 2015 Alexander Motin <mav@FreeBSD.org>
.\" Copyright (c) 2018 Marcelo Araujo <araujo@FreeBSD.org>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
@ -35,7 +36,7 @@
.\" $Id: //depot/users/kenm/FreeBSD-test2/usr.sbin/ctladm/ctladm.8#3 $
.\" $FreeBSD$
.\"
.Dd July 15, 2017
.Dd May 10, 2018
.Dt CTLADM 8
.Os
.Sh NAME
@ -161,10 +162,13 @@
.Op Fl x
.Nm
.Ic port
.Op Fl c
.Op Fl o Ar on|off
.Op Fl w Ar wwpn
.Op Fl W Ar wwnn
.Op Fl O Ar pp|vp
.Op Fl p Ar targ_port
.Op Fl r Ar targ_port
.Op Fl t Ar fe_type
.Nm
.Ic portlist
@ -593,13 +597,21 @@ must be specified.
The WWNN and WWPN may both be specified at the same time, but cannot be
combined with enabling/disabling or listing ports.
.Bl -tag -width 12n
.It Fl c
Create new frontend port using free pp and vp=0.
.It Fl o Ar on|off
Turn the specified CTL frontend ports off or on.
Turn the specified CTL frontend ports on or off.
If no port number or port type is specified, all ports are turned on or
off.
.It Fl O Ar pp|vp
Specify generic options on the ioctl frontend port.
At present, only pp and vp port numbers can be set.
.It Fl p Ar targ_port
Specify the frontend port number.
The port numbers can be found in the frontend port list.
.It Fl r
Remove port specified with
.Pq Fl p Ar targ_port .
.It Fl t Ar fe_type
Specify the frontend type.
Currently defined port types are
@ -950,29 +962,26 @@ The default value is zero, that disables backing store completely,
making all writes go to nowhere, while all reads return zeroes.
.El
.Sh EXAMPLES
.Dl ctladm tur 1
.Pp
Send a
.Tn SCSI
TEST UNIT READY command to LUN 1.
.Pp
.Dl ctladm modesense 1 -l
.Dl ctladm tur 1
.Pp
Display the list of mode pages supported by LUN 1.
.Pp
.Dl ctladm modesense 0 -m 10 -P 3 -d -c 10
.Dl ctladm modesense 1 -l
.Pp
Display the saved version of the Control mode page (page 10) on LUN 0.
Disable fetching block descriptors, and use a 10 byte MODE SENSE command
instead of the default 6 byte command.
.Bd -literal
ctladm read 2 -l 0 -d 1 -b 512 -f - > foo
.Ed
.Pp
.Dl ctladm modesense 0 -m 10 -P 3 -d -c 10
.Pp
Read the first 512 byte block from LUN 2 and dump it to the file
.Pa foo .
.Bd -literal
ctladm write 3 -l 0xff432140 -d 20 -b 512 -f /tmp/bar
.Dl ctladm read 2 -l 0 -d 1 -b 512 -f - > foo
.Ed
.Pp
Read 10240 bytes from the file
@ -980,7 +989,9 @@ Read 10240 bytes from the file
and write it to LUN 3.
starting at LBA 0xff432140.
.Pp
.Dl ctladm create -b ramdisk -s 10485760000000000
.Bd -literal
.Dl ctladm write 3 -l 0xff432140 -d 20 -b 512 -f /tmp/bar
.Ed
.Pp
Create a LUN with the
.Dq fake
@ -988,20 +999,20 @@ ramdisk as a backing store.
The LUN will claim to have a size of approximately 10 terabytes,
while having no real data store (all written data are lost).
.Pp
.Dl ctladm create -b ramdisk -s 10T -o capacity=10G
.Dl ctladm create -b ramdisk -s 10485760000000000
.Pp
Create a thin provisioned LUN with a ramdisk as a backing store.
The LUN will have maximal backing store capacity of 10 gigabytes,
while reporting size of 10 terabytes,
.Pp
.Dl ctladm create -b block -o file=src/usr.sbin/ctladm/ctladm.8
.Dl ctladm create -b ramdisk -s 10T -o capacity=10G
.Pp
Create a LUN using the block backend, and specify the file
.Pa src/usr.sbin/ctladm/ctladm.8
as the backing store.
The size of the LUN will be derived from the size of the file.
.Pp
.Dl ctladm create -b block -o file=src/usr.sbin/ctladm/ctladm.8 -S MYSERIAL321 -d MYDEVID123
.Dl ctladm create -b block -o file=src/usr.sbin/ctladm/ctladm.8
.Pp
Create a LUN using the block backend, specify the file
.Pa src/usr.sbin/ctladm/ctladm.8
@ -1012,33 +1023,46 @@ VPD page 0x80 and 0x83 serial number
and device ID
.Fl ( d ) .
.Pp
.Dl ctladm remove -b block -l 12
.Dl ctladm create -b block -o file=src/usr.sbin/ctladm/ctladm.8 -S MYSERIAL321 -d MYDEVID123
.Pp
Use to specify generic options on ioctl frontend port, now it is
only possible to set pp and/or vp port number.
.Pp
.Dl ctladm port -c -O pp=11 -O vp=12
.Pp
Remove specified targ_port.
.Pp
.Dl ctladm port -r -p 4
.Pp
.Pp
Remove LUN 12, which is handled by the block backend, from the system.
.Pp
.Dl ctladm devlist
.Dl ctladm remove -b block -l 12
.Pp
List configured LUNs in the system, along with their backend and serial
number.
This works when the Front End Target Drivers are enabled or disabled.
.Pp
.Dl ctladm lunlist
.Dl ctladm devlist
.Pp
List all LUNs in the system, along with their inquiry data and device type.
This only works when the FETDs are enabled, since the commands go through the
ioctl port.
.Pp
.Dl ctladm inject 6 -i mediumerr -p read -r 0,512 -c
.Dl ctladm lunlist
.Pp
Inject a medium error on LUN 6 for every read that covers the first 512
blocks of the LUN.
.Bd -literal -offset indent
ctladm inject 6 -i custom -p tur -s 18 "f0 0 02 s12 04 02"
.Ed
.Pp
.Dl ctladm inject 6 -i mediumerr -p read -r 0,512 -c
.Pp
Inject a custom error on LUN 6 for the next TEST UNIT READY command only.
This will result in a sense key of NOT READY (0x02), and an ASC/ASCQ of
0x04,0x02 ("Logical unit not ready, initializing command required").
.Pp
.Bd -literal -offset indent
ctladm inject 6 -i custom -p tur -s 18 "f0 0 02 s12 04 02"
.Ed
.Sh SEE ALSO
.Xr cam 3 ,
.Xr cam_cdbparse 3 ,

View File

@ -1,7 +1,10 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2003, 2004 Silicon Graphics International Corp.
* Copyright (c) 1997-2007 Kenneth D. Merry
* Copyright (c) 2012 The FreeBSD Foundation
* Copyright (c) 2018 Marcelo Araujo <araujo@FreeBSD.org>
* All rights reserved.
*
* Portions of this software were developed by Edward Tomasz Napierala
@ -50,6 +53,7 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/queue.h>
#include <sys/sbuf.h>
#include <sys/nv.h>
#include <sys/stat.h>
#include <bsdxml.h>
#include <ctype.h>
@ -178,7 +182,7 @@ static struct ctladm_opts option_table[] = {
{"lunmap", CTLADM_CMD_LUNMAP, CTLADM_ARG_NONE, "p:l:L:"},
{"modesense", CTLADM_CMD_MODESENSE, CTLADM_ARG_NEED_TL, "P:S:dlm:c:"},
{"modify", CTLADM_CMD_MODIFY, CTLADM_ARG_NONE, "b:l:o:s:"},
{"port", CTLADM_CMD_PORT, CTLADM_ARG_NONE, "lo:p:qt:w:W:x"},
{"port", CTLADM_CMD_PORT, CTLADM_ARG_NONE, "lo:O:d:crp:qt:w:W:x"},
{"portlist", CTLADM_CMD_PORTLIST, CTLADM_ARG_NONE, "f:ilp:qvx"},
{"prin", CTLADM_CMD_PRES_IN, CTLADM_ARG_NEED_TL, "a:"},
{"prout", CTLADM_CMD_PRES_OUT, CTLADM_ARG_NEED_TL, "a:k:r:s:"},
@ -369,7 +373,9 @@ typedef enum {
CCTL_PORT_MODE_LIST,
CCTL_PORT_MODE_SET,
CCTL_PORT_MODE_ON,
CCTL_PORT_MODE_OFF
CCTL_PORT_MODE_OFF,
CCTL_PORT_MODE_CREATE,
CCTL_PORT_MODE_REMOVE
} cctl_port_mode;
static struct ctladm_opts cctl_fe_table[] = {
@ -392,9 +398,16 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt)
uint64_t wwnn = 0, wwpn = 0;
cctl_port_mode port_mode = CCTL_PORT_MODE_NONE;
struct ctl_port_entry entry;
struct ctl_req req;
char *driver = NULL;
nvlist_t *option_list;
ctl_port_type port_type = CTL_PORT_NONE;
int quiet = 0, xml = 0;
option_list = nvlist_create(0);
if (option_list == NULL)
err(1, "%s: unable to allocate nvlist", __func__);
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch (c) {
case 'l':
@ -403,6 +416,12 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt)
port_mode = CCTL_PORT_MODE_LIST;
break;
case 'c':
port_mode = CCTL_PORT_MODE_CREATE;
break;
case 'r':
port_mode = CCTL_PORT_MODE_REMOVE;
break;
case 'o':
if (port_mode != CCTL_PORT_MODE_NONE)
goto bailout_badarg;
@ -419,6 +438,40 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt)
goto bailout;
}
break;
case 'O': {
char *tmpstr;
char *name, *value;
tmpstr = strdup(optarg);
name = strsep(&tmpstr, "=");
if (name == NULL) {
warnx("%s: option -O takes \"name=value\""
"argument", __func__);
retval = 1;
goto bailout;
}
value = strsep(&tmpstr, "=");
if (value == NULL) {
warnx("%s: option -O takes \"name=value\""
"argument", __func__);
retval = 1;
goto bailout;
}
free(tmpstr);
nvlist_add_string(option_list, name, value);
break;
}
case 'd':
if (driver != NULL) {
warnx("%s: option -d cannot be specified twice",
__func__);
retval = 1;
goto bailout;
}
driver = strdup(optarg);
break;
case 'p':
targ_port = strtol(optarg, NULL, 0);
break;
@ -474,6 +527,9 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt)
}
}
if (driver == NULL)
driver = strdup("ioctl");
/*
* The user can specify either one or more frontend types (-t), or
* a specific frontend, but not both.
@ -515,6 +571,56 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt)
cctl_portlist(fd, argcx, argvx, opts);
break;
}
case CCTL_PORT_MODE_REMOVE:
if (targ_port == -1) {
warnx("%s: -r require -p", __func__);
retval = 1;
goto bailout;
}
case CCTL_PORT_MODE_CREATE: {
bzero(&req, sizeof(req));
strlcpy(req.driver, driver, sizeof(req.driver));
if (port_mode == CCTL_PORT_MODE_REMOVE) {
req.reqtype = CTL_REQ_REMOVE;
nvlist_add_stringf(option_list, "port_id", "%d",
targ_port);
} else
req.reqtype = CTL_REQ_CREATE;
req.args = nvlist_pack(option_list, &req.args_len);
if (req.args == NULL) {
warn("%s: error packing nvlist", __func__);
retval = 1;
goto bailout;
}
retval = ioctl(fd, CTL_PORT_REQ, &req);
free(req.args);
if (retval == -1) {
warn("%s: CTL_PORT_REQ ioctl failed", __func__);
retval = 1;
goto bailout;
}
switch (req.status) {
case CTL_LUN_ERROR:
warnx("error: %s", req.error_str);
retval = 1;
goto bailout;
case CTL_LUN_WARNING:
warnx("warning: %s", req.error_str);
break;
case CTL_LUN_OK:
break;
default:
warnx("unknown status: %d", req.status);
retval = 1;
goto bailout;
}
break;
}
case CCTL_PORT_MODE_SET:
if (targ_port == -1) {
warnx("%s: -w and -W require -n", __func__);
@ -561,7 +667,8 @@ cctl_port(int fd, int argc, char **argv, char *combinedopt)
}
bailout:
nvlist_destroy(req.args_nvl);
free(driver);
return (retval);
bailout_badarg:
@ -2271,14 +2378,6 @@ cctl_persistent_reserve_out(int fd, int lun, int iid,
return (retval);
}
struct cctl_req_option {
char *name;
int namelen;
char *value;
int vallen;
STAILQ_ENTRY(cctl_req_option) links;
};
static int
cctl_create_lun(int fd, int argc, char **argv, char *combinedopt)
{
@ -2290,11 +2389,12 @@ cctl_create_lun(int fd, int argc, char **argv, char *combinedopt)
char *device_id = NULL;
int lun_size_set = 0, blocksize_set = 0, lun_id_set = 0;
char *backend_name = NULL;
STAILQ_HEAD(, cctl_req_option) option_list;
int num_options = 0;
nvlist_t *option_list;
int retval = 0, c;
STAILQ_INIT(&option_list);
option_list = nvlist_create(0);
if (option_list == NULL)
err(1, "%s: unable to allocate nvlist", __func__);
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch (c) {
@ -2313,7 +2413,6 @@ cctl_create_lun(int fd, int argc, char **argv, char *combinedopt)
lun_id_set = 1;
break;
case 'o': {
struct cctl_req_option *option;
char *tmpstr;
char *name, *value;
@ -2332,21 +2431,8 @@ cctl_create_lun(int fd, int argc, char **argv, char *combinedopt)
retval = 1;
goto bailout;
}
option = malloc(sizeof(*option));
if (option == NULL) {
warn("%s: error allocating %zd bytes",
__func__, sizeof(*option));
retval = 1;
goto bailout;
}
option->name = strdup(name);
option->namelen = strlen(name) + 1;
option->value = strdup(value);
option->vallen = strlen(value) + 1;
free(tmpstr);
STAILQ_INSERT_TAIL(&option_list, option, links);
num_options++;
nvlist_add_string(option_list, name, value);
break;
}
case 's':
@ -2413,43 +2499,16 @@ cctl_create_lun(int fd, int argc, char **argv, char *combinedopt)
req.reqdata.create.flags |= CTL_LUN_FLAG_DEVID;
}
req.num_be_args = num_options;
if (num_options > 0) {
struct cctl_req_option *option, *next_option;
int i;
req.be_args = malloc(num_options * sizeof(*req.be_args));
if (req.be_args == NULL) {
warn("%s: error allocating %zd bytes", __func__,
num_options * sizeof(*req.be_args));
retval = 1;
goto bailout;
}
for (i = 0, option = STAILQ_FIRST(&option_list);
i < num_options; i++, option = next_option) {
next_option = STAILQ_NEXT(option, links);
req.be_args[i].namelen = option->namelen;
req.be_args[i].name = strdup(option->name);
req.be_args[i].vallen = option->vallen;
req.be_args[i].value = strdup(option->value);
/*
* XXX KDM do we want a way to specify a writeable
* flag of some sort? Do we want a way to specify
* binary data?
*/
req.be_args[i].flags = CTL_BEARG_ASCII | CTL_BEARG_RD;
STAILQ_REMOVE(&option_list, option, cctl_req_option,
links);
free(option->name);
free(option->value);
free(option);
}
req.args = nvlist_pack(option_list, &req.args_len);
if (req.args == NULL) {
warn("%s: error packing nvlist", __func__);
retval = 1;
goto bailout;
}
if (ioctl(fd, CTL_LUN_REQ, &req) == -1) {
retval = ioctl(fd, CTL_LUN_REQ, &req);
free(req.args);
if (retval == -1) {
warn("%s: error issuing CTL_LUN_REQ ioctl", __func__);
retval = 1;
goto bailout;
@ -2480,9 +2539,10 @@ cctl_create_lun(int fd, int argc, char **argv, char *combinedopt)
req.reqdata.create.blocksize_bytes);
fprintf(stdout, "LUN ID: %d\n", req.reqdata.create.req_lun_id);
fprintf(stdout, "Serial Number: %s\n", req.reqdata.create.serial_num);
fprintf(stdout, "Device ID; %s\n", req.reqdata.create.device_id);
fprintf(stdout, "Device ID: %s\n", req.reqdata.create.device_id);
bailout:
nvlist_destroy(req.args_nvl);
return (retval);
}
@ -2493,11 +2553,12 @@ cctl_rm_lun(int fd, int argc, char **argv, char *combinedopt)
uint32_t lun_id = 0;
int lun_id_set = 0;
char *backend_name = NULL;
STAILQ_HEAD(, cctl_req_option) option_list;
int num_options = 0;
nvlist_t *option_list;
int retval = 0, c;
STAILQ_INIT(&option_list);
option_list = nvlist_create(0);
if (option_list == NULL)
err(1, "%s: unable to allocate nvlist", __func__);
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch (c) {
@ -2509,7 +2570,6 @@ cctl_rm_lun(int fd, int argc, char **argv, char *combinedopt)
lun_id_set = 1;
break;
case 'o': {
struct cctl_req_option *option;
char *tmpstr;
char *name, *value;
@ -2528,21 +2588,8 @@ cctl_rm_lun(int fd, int argc, char **argv, char *combinedopt)
retval = 1;
goto bailout;
}
option = malloc(sizeof(*option));
if (option == NULL) {
warn("%s: error allocating %zd bytes",
__func__, sizeof(*option));
retval = 1;
goto bailout;
}
option->name = strdup(name);
option->namelen = strlen(name) + 1;
option->value = strdup(value);
option->vallen = strlen(value) + 1;
free(tmpstr);
STAILQ_INSERT_TAIL(&option_list, option, links);
num_options++;
nvlist_add_string(option_list, name, value);
break;
}
default:
@ -2562,44 +2609,17 @@ cctl_rm_lun(int fd, int argc, char **argv, char *combinedopt)
req.reqtype = CTL_LUNREQ_RM;
req.reqdata.rm.lun_id = lun_id;
req.num_be_args = num_options;
if (num_options > 0) {
struct cctl_req_option *option, *next_option;
int i;
req.be_args = malloc(num_options * sizeof(*req.be_args));
if (req.be_args == NULL) {
warn("%s: error allocating %zd bytes", __func__,
num_options * sizeof(*req.be_args));
retval = 1;
goto bailout;
}
for (i = 0, option = STAILQ_FIRST(&option_list);
i < num_options; i++, option = next_option) {
next_option = STAILQ_NEXT(option, links);
req.be_args[i].namelen = option->namelen;
req.be_args[i].name = strdup(option->name);
req.be_args[i].vallen = option->vallen;
req.be_args[i].value = strdup(option->value);
/*
* XXX KDM do we want a way to specify a writeable
* flag of some sort? Do we want a way to specify
* binary data?
*/
req.be_args[i].flags = CTL_BEARG_ASCII | CTL_BEARG_RD;
STAILQ_REMOVE(&option_list, option, cctl_req_option,
links);
free(option->name);
free(option->value);
free(option);
}
req.args = nvlist_pack(option_list, &req.args_len);
if (req.args == NULL) {
warn("%s: error packing nvlist", __func__);
retval = 1;
goto bailout;
}
if (ioctl(fd, CTL_LUN_REQ, &req) == -1) {
retval = ioctl(fd, CTL_LUN_REQ, &req);
free(req.args);
if (retval == -1) {
warn("%s: error issuing CTL_LUN_REQ ioctl", __func__);
retval = 1;
goto bailout;
@ -2624,6 +2644,7 @@ cctl_rm_lun(int fd, int argc, char **argv, char *combinedopt)
printf("LUN %d removed successfully\n", lun_id);
bailout:
nvlist_destroy(req.args_nvl);
return (retval);
}
@ -2635,11 +2656,13 @@ cctl_modify_lun(int fd, int argc, char **argv, char *combinedopt)
uint32_t lun_id = 0;
int lun_id_set = 0, lun_size_set = 0;
char *backend_name = NULL;
STAILQ_HEAD(, cctl_req_option) option_list;
int num_options = 0;
nvlist_t *option_list;
int retval = 0, c;
STAILQ_INIT(&option_list);
option_list = nvlist_create(0);
if (option_list == NULL)
err(1, "%s: unable to allocate nvlist", __func__);
while ((c = getopt(argc, argv, combinedopt)) != -1) {
switch (c) {
case 'b':
@ -2650,7 +2673,6 @@ cctl_modify_lun(int fd, int argc, char **argv, char *combinedopt)
lun_id_set = 1;
break;
case 'o': {
struct cctl_req_option *option;
char *tmpstr;
char *name, *value;
@ -2669,21 +2691,8 @@ cctl_modify_lun(int fd, int argc, char **argv, char *combinedopt)
retval = 1;
goto bailout;
}
option = malloc(sizeof(*option));
if (option == NULL) {
warn("%s: error allocating %zd bytes",
__func__, sizeof(*option));
retval = 1;
goto bailout;
}
option->name = strdup(name);
option->namelen = strlen(name) + 1;
option->value = strdup(value);
option->vallen = strlen(value) + 1;
free(tmpstr);
STAILQ_INSERT_TAIL(&option_list, option, links);
num_options++;
nvlist_add_string(option_list, name, value);
break;
}
case 's':
@ -2709,7 +2718,7 @@ cctl_modify_lun(int fd, int argc, char **argv, char *combinedopt)
if (lun_id_set == 0)
errx(1, "%s: LUN id (-l) must be specified", __func__);
if (lun_size_set == 0 && num_options == 0)
if (lun_size_set == 0 && nvlist_empty(option_list))
errx(1, "%s: size (-s) or options (-o) must be specified",
__func__);
@ -2721,43 +2730,16 @@ cctl_modify_lun(int fd, int argc, char **argv, char *combinedopt)
req.reqdata.modify.lun_id = lun_id;
req.reqdata.modify.lun_size_bytes = lun_size;
req.num_be_args = num_options;
if (num_options > 0) {
struct cctl_req_option *option, *next_option;
int i;
req.be_args = malloc(num_options * sizeof(*req.be_args));
if (req.be_args == NULL) {
warn("%s: error allocating %zd bytes", __func__,
num_options * sizeof(*req.be_args));
retval = 1;
goto bailout;
}
for (i = 0, option = STAILQ_FIRST(&option_list);
i < num_options; i++, option = next_option) {
next_option = STAILQ_NEXT(option, links);
req.be_args[i].namelen = option->namelen;
req.be_args[i].name = strdup(option->name);
req.be_args[i].vallen = option->vallen;
req.be_args[i].value = strdup(option->value);
/*
* XXX KDM do we want a way to specify a writeable
* flag of some sort? Do we want a way to specify
* binary data?
*/
req.be_args[i].flags = CTL_BEARG_ASCII | CTL_BEARG_RD;
STAILQ_REMOVE(&option_list, option, cctl_req_option,
links);
free(option->name);
free(option->value);
free(option);
}
req.args = nvlist_pack(option_list, &req.args_len);
if (req.args == NULL) {
warn("%s: error packing nvlist", __func__);
retval = 1;
goto bailout;
}
if (ioctl(fd, CTL_LUN_REQ, &req) == -1) {
retval = ioctl(fd, CTL_LUN_REQ, &req);
free(req.args);
if (retval == -1) {
warn("%s: error issuing CTL_LUN_REQ ioctl", __func__);
retval = 1;
goto bailout;
@ -2782,6 +2764,7 @@ cctl_modify_lun(int fd, int argc, char **argv, char *combinedopt)
printf("LUN %d modified successfully\n", lun_id);
bailout:
nvlist_destroy(req.args_nvl);
return (retval);
}
@ -3661,7 +3644,7 @@ cctl_portlist(int fd, int argc, char **argv, char *combinedopt)
struct cctl_portlist_data portlist;
struct cctl_port *port;
XML_Parser parser;
char *port_str;
char *port_str = NULL;
int port_len;
int dump_xml = 0;
int retval, c;
@ -3704,7 +3687,7 @@ cctl_portlist(int fd, int argc, char **argv, char *combinedopt)
}
retry:
port_str = malloc(port_len);
port_str = (char *)realloc(port_str, port_len);
bzero(&list, sizeof(list));
list.alloc_len = port_len;
@ -3876,6 +3859,8 @@ usage(int error)
" [-s len fmt [args]] [-c] [-d delete_id]\n"
" ctladm port <-o <on|off> | [-w wwnn][-W wwpn]>\n"
" [-p targ_port] [-t port_type]\n"
" <-c> [-d driver] [-O name=value]\n"
" <-r> <-p targ_port>\n"
" ctladm portlist [-f frontend] [-i] [-p targ_port] [-q] [-v] [-x]\n"
" ctladm islist [-v | -x]\n"
" ctladm islogout <-a | -c connection-id | -i name | -p portal>\n"
@ -3954,12 +3939,16 @@ usage(int error)
"-c : continuous operation\n"
"-d delete_id : error id to delete\n"
"port options:\n"
"-c : create new ioctl or iscsi frontend port\n"
"-d : specify ioctl or iscsi frontend type\n"
"-l : list frontend ports\n"
"-o on|off : turn frontend ports on or off\n"
"-O pp|vp : create new frontend port using pp and/or vp\n"
"-w wwnn : set WWNN for one frontend\n"
"-W wwpn : set WWPN for one frontend\n"
"-t port_type : specify fc, scsi, ioctl, internal frontend type\n"
"-p targ_port : specify target port number\n"
"-r : remove frontend port\n"
"-q : omit header in list output\n"
"-x : output port list in XML format\n"
"portlist options:\n"

View File

@ -15,7 +15,7 @@ CFLAGS+= -I${SRCTOP}/sys/dev/iscsi
#CFLAGS+= -DICL_KERNEL_PROXY
MAN= ctld.8 ctl.conf.5
LIBADD= bsdxml l md sbuf util ucl m
LIBADD= bsdxml l md sbuf util ucl m nv
YFLAGS+= -v
CLEANFILES= y.tab.c y.tab.h y.output

View File

@ -1234,6 +1234,7 @@ port_new(struct conf *conf, struct target *target, struct portal_group *pg)
log_err(1, "calloc");
port->p_conf = conf;
port->p_name = name;
port->p_ioctl_port = 0;
TAILQ_INSERT_TAIL(&conf->conf_ports, port, p_next);
TAILQ_INSERT_TAIL(&target->t_ports, port, p_ts);
port->p_target = target;
@ -1242,6 +1243,51 @@ port_new(struct conf *conf, struct target *target, struct portal_group *pg)
return (port);
}
struct port *
port_new_ioctl(struct conf *conf, struct target *target, int pp, int vp)
{
struct pport *pport;
struct port *port;
char *pname;
char *name;
int ret;
ret = asprintf(&pname, "ioctl/%d/%d", pp, vp);
if (ret <= 0) {
log_err(1, "asprintf");
return (NULL);
}
pport = pport_find(conf, pname);
if (pport != NULL) {
free(pname);
return (port_new_pp(conf, target, pport));
}
ret = asprintf(&name, "%s-%s", pname, target->t_name);
free(pname);
if (ret <= 0)
log_err(1, "asprintf");
if (port_find(conf, name) != NULL) {
log_warnx("duplicate port \"%s\"", name);
free(name);
return (NULL);
}
port = calloc(1, sizeof(*port));
if (port == NULL)
log_err(1, "calloc");
port->p_conf = conf;
port->p_name = name;
port->p_ioctl_port = 1;
port->p_ioctl_pp = pp;
port->p_ioctl_vp = vp;
TAILQ_INSERT_TAIL(&conf->conf_ports, port, p_next);
TAILQ_INSERT_TAIL(&target->t_ports, port, p_ts);
port->p_target = target;
return (port);
}
struct port *
port_new_pp(struct conf *conf, struct target *target, struct pport *pp)
{
@ -1627,9 +1673,9 @@ conf_print(struct conf *conf)
TAILQ_FOREACH(auth_name, &ag->ag_names, an_next)
fprintf(stderr, "\t initiator-name %s\n",
auth_name->an_initator_name);
TAILQ_FOREACH(auth_portal, &ag->ag_portals, an_next)
TAILQ_FOREACH(auth_portal, &ag->ag_portals, ap_next)
fprintf(stderr, "\t initiator-portal %s\n",
auth_portal->an_initator_portal);
auth_portal->ap_initator_portal);
fprintf(stderr, "}\n");
}
TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) {
@ -1643,7 +1689,7 @@ conf_print(struct conf *conf)
fprintf(stderr, "\t\tpath %s\n", lun->l_path);
TAILQ_FOREACH(o, &lun->l_options, o_next)
fprintf(stderr, "\t\toption %s %s\n",
lo->o_name, lo->o_value);
o->o_name, o->o_value);
fprintf(stderr, "\t}\n");
}
TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {

View File

@ -152,6 +152,9 @@ struct port {
struct pport *p_pport;
struct target *p_target;
int p_ioctl_port;
int p_ioctl_pp;
int p_ioctl_vp;
uint32_t p_ctl_port;
};
@ -368,6 +371,8 @@ void pport_delete(struct pport *pport);
struct port *port_new(struct conf *conf, struct target *target,
struct portal_group *pg);
struct port *port_new_ioctl(struct conf *conf, struct target *target,
int pp, int vp);
struct port *port_new_pp(struct conf *conf, struct target *target,
struct pport *pp);
struct port *port_find(const struct conf *conf, const char *name);

View File

@ -4,6 +4,7 @@
* Copyright (c) 2003, 2004 Silicon Graphics International Corp.
* Copyright (c) 1997-2007 Kenneth D. Merry
* Copyright (c) 2012 The FreeBSD Foundation
* Copyright (c) 2017 Jakub Wojciech Klama <jceel@FreeBSD.org>
* All rights reserved.
*
* Portions of this software were developed by Edward Tomasz Napierala
@ -47,6 +48,7 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/queue.h>
#include <sys/sbuf.h>
#include <sys/nv.h>
#include <sys/stat.h>
#include <assert.h>
#include <bsdxml.h>
@ -73,6 +75,8 @@ __FBSDID("$FreeBSD$");
#include <netdb.h>
#endif
#define NVLIST_BUFSIZE 1024
extern bool proxy_mode;
static int ctl_fd = 0;
@ -652,23 +656,12 @@ conf_new_from_kernel(void)
return (conf);
}
static void
str_arg(struct ctl_be_arg *arg, const char *name, const char *value)
{
arg->namelen = strlen(name) + 1;
arg->name = __DECONST(char *, name);
arg->vallen = strlen(value) + 1;
arg->value = __DECONST(char *, value);
arg->flags = CTL_BEARG_ASCII | CTL_BEARG_RD;
}
int
kernel_lun_add(struct lun *lun)
{
struct option *o;
struct ctl_lun_req req;
int error, i, num_options;
int error;
bzero(&req, sizeof(req));
@ -724,29 +717,26 @@ kernel_lun_add(struct lun *lun)
assert(o != NULL);
}
num_options = 0;
TAILQ_FOREACH(o, &lun->l_options, o_next)
num_options++;
req.num_be_args = num_options;
if (num_options > 0) {
req.be_args = malloc(num_options * sizeof(*req.be_args));
if (req.be_args == NULL) {
log_warn("error allocating %zd bytes",
num_options * sizeof(*req.be_args));
if (!TAILQ_EMPTY(&lun->l_options)) {
req.args_nvl = nvlist_create(0);
if (req.args_nvl == NULL) {
log_warn("error allocating nvlist");
return (1);
}
i = 0;
TAILQ_FOREACH(o, &lun->l_options, o_next) {
str_arg(&req.be_args[i], o->o_name, o->o_value);
i++;
TAILQ_FOREACH(o, &lun->l_options, o_next)
nvlist_add_string(req.args_nvl, o->o_name, o->o_value);
req.args = nvlist_pack(req.args_nvl, &req.args_len);
if (req.args == NULL) {
log_warn("error packing nvlist");
return (1);
}
assert(i == num_options);
}
error = ioctl(ctl_fd, CTL_LUN_REQ, &req);
free(req.be_args);
nvlist_destroy(req.args_nvl);
if (error != 0) {
log_warn("error issuing CTL_LUN_REQ ioctl");
return (1);
@ -776,7 +766,7 @@ kernel_lun_modify(struct lun *lun)
{
struct option *o;
struct ctl_lun_req req;
int error, i, num_options;
int error;
bzero(&req, sizeof(req));
@ -786,29 +776,26 @@ kernel_lun_modify(struct lun *lun)
req.reqdata.modify.lun_id = lun->l_ctl_lun;
req.reqdata.modify.lun_size_bytes = lun->l_size;
num_options = 0;
TAILQ_FOREACH(o, &lun->l_options, o_next)
num_options++;
req.num_be_args = num_options;
if (num_options > 0) {
req.be_args = malloc(num_options * sizeof(*req.be_args));
if (req.be_args == NULL) {
log_warn("error allocating %zd bytes",
num_options * sizeof(*req.be_args));
if (!TAILQ_EMPTY(&lun->l_options)) {
req.args_nvl = nvlist_create(0);
if (req.args_nvl == NULL) {
log_warn("error allocating nvlist");
return (1);
}
i = 0;
TAILQ_FOREACH(o, &lun->l_options, o_next) {
str_arg(&req.be_args[i], o->o_name, o->o_value);
i++;
TAILQ_FOREACH(o, &lun->l_options, o_next)
nvlist_add_string(req.args_nvl, o->o_name, o->o_value);
req.args = nvlist_pack(req.args_nvl, &req.args_len);
if (req.args == NULL) {
log_warn("error packing nvlist");
return (1);
}
assert(i == num_options);
}
error = ioctl(ctl_fd, CTL_LUN_REQ, &req);
free(req.be_args);
nvlist_destroy(req.args_nvl);
if (error != 0) {
log_warn("error issuing CTL_LUN_REQ ioctl");
return (1);
@ -988,37 +975,54 @@ kernel_port_add(struct port *port)
struct ctl_lun_map lm;
struct target *targ = port->p_target;
struct portal_group *pg = port->p_portal_group;
char tagstr[16];
int error, i, n;
char result_buf[NVLIST_BUFSIZE];
int error, i;
/* Create iSCSI port. */
if (port->p_portal_group) {
if (port->p_portal_group || port->p_ioctl_port) {
bzero(&req, sizeof(req));
strlcpy(req.driver, "iscsi", sizeof(req.driver));
req.reqtype = CTL_REQ_CREATE;
req.num_args = 5;
TAILQ_FOREACH(o, &pg->pg_options, o_next)
req.num_args++;
req.args = malloc(req.num_args * sizeof(*req.args));
if (req.args == NULL)
log_err(1, "malloc");
n = 0;
req.args[n].namelen = sizeof("port_id");
req.args[n].name = __DECONST(char *, "port_id");
req.args[n].vallen = sizeof(port->p_ctl_port);
req.args[n].value = &port->p_ctl_port;
req.args[n++].flags = CTL_BEARG_WR;
str_arg(&req.args[n++], "cfiscsi_target", targ->t_name);
snprintf(tagstr, sizeof(tagstr), "%d", pg->pg_tag);
str_arg(&req.args[n++], "cfiscsi_portal_group_tag", tagstr);
if (targ->t_alias)
str_arg(&req.args[n++], "cfiscsi_target_alias", targ->t_alias);
str_arg(&req.args[n++], "ctld_portal_group_name", pg->pg_name);
TAILQ_FOREACH(o, &pg->pg_options, o_next)
str_arg(&req.args[n++], o->o_name, o->o_value);
req.num_args = n;
if (port->p_portal_group) {
strlcpy(req.driver, "iscsi", sizeof(req.driver));
req.args_nvl = nvlist_create(0);
nvlist_add_string(req.args_nvl, "cfiscsi_target",
targ->t_name);
nvlist_add_string(req.args_nvl,
"ctld_portal_group_name", pg->pg_name);
nvlist_add_stringf(req.args_nvl,
"cfiscsi_portal_group_tag", "%u", pg->pg_tag);
if (targ->t_alias) {
nvlist_add_string(req.args_nvl,
"cfiscsi_target_alias", targ->t_alias);
}
TAILQ_FOREACH(o, &pg->pg_options, o_next)
nvlist_add_string(req.args_nvl, o->o_name,
o->o_value);
}
if (port->p_ioctl_port) {
strlcpy(req.driver, "ioctl", sizeof(req.driver));
req.args_nvl = nvlist_create(0);
nvlist_add_stringf(req.args_nvl, "pp", "%d",
port->p_ioctl_pp);
nvlist_add_stringf(req.args_nvl, "vp", "%d",
port->p_ioctl_vp);
}
req.args = nvlist_pack(req.args_nvl, &req.args_len);
if (req.args == NULL) {
log_warn("error packing nvlist");
return (1);
}
req.result = result_buf;
req.result_len = sizeof(result_buf);
error = ioctl(ctl_fd, CTL_PORT_REQ, &req);
free(req.args);
nvlist_destroy(req.args_nvl);
if (error != 0) {
log_warn("error issuing CTL_PORT_REQ ioctl");
return (1);
@ -1033,6 +1037,15 @@ kernel_port_add(struct port *port)
req.status);
return (1);
}
req.result_nvl = nvlist_unpack(result_buf, req.result_len, 0);
if (req.result_nvl == NULL) {
log_warnx("error unpacking result nvlist");
return (1);
}
port->p_ctl_port = nvlist_get_number(req.result_nvl, "port_id");
nvlist_destroy(req.result_nvl);
} else if (port->p_pport) {
port->p_ctl_port = port->p_pport->pp_ctl_port;
@ -1116,7 +1129,6 @@ kernel_port_remove(struct port *port)
struct ctl_port_entry entry;
struct ctl_lun_map lm;
struct ctl_req req;
char tagstr[16];
struct target *targ = port->p_target;
struct portal_group *pg = port->p_portal_group;
int error;
@ -1130,20 +1142,35 @@ kernel_port_remove(struct port *port)
return (-1);
}
/* Remove iSCSI port. */
if (port->p_portal_group) {
/* Remove iSCSI or ioctl port. */
if (port->p_portal_group || port->p_ioctl_port) {
bzero(&req, sizeof(req));
strlcpy(req.driver, "iscsi", sizeof(req.driver));
strlcpy(req.driver, port->p_ioctl_port ? "ioctl" : "iscsi",
sizeof(req.driver));
req.reqtype = CTL_REQ_REMOVE;
req.num_args = 2;
req.args = malloc(req.num_args * sizeof(*req.args));
if (req.args == NULL)
log_err(1, "malloc");
str_arg(&req.args[0], "cfiscsi_target", targ->t_name);
snprintf(tagstr, sizeof(tagstr), "%d", pg->pg_tag);
str_arg(&req.args[1], "cfiscsi_portal_group_tag", tagstr);
req.args_nvl = nvlist_create(0);
if (req.args_nvl == NULL)
log_err(1, "nvlist_create");
if (port->p_ioctl_port)
nvlist_add_stringf(req.args_nvl, "port_id", "%d",
port->p_ctl_port);
else {
nvlist_add_string(req.args_nvl, "cfiscsi_target",
targ->t_name);
nvlist_add_stringf(req.args_nvl,
"cfiscsi_portal_group_tag", "%u", pg->pg_tag);
}
req.args = nvlist_pack(req.args_nvl, &req.args_len);
if (req.args == NULL) {
log_warn("error packing nvlist");
return (1);
}
error = ioctl(ctl_fd, CTL_PORT_REQ, &req);
free(req.args);
nvlist_destroy(req.args_nvl);
if (error != 0) {
log_warn("error issuing CTL_PORT_REQ ioctl");
return (1);

View File

@ -768,28 +768,41 @@ target_port: PORT STR
{
struct pport *pp;
struct port *tp;
int ret, i_pp, i_vp = 0;
pp = pport_find(conf, $2);
if (pp == NULL) {
log_warnx("unknown port \"%s\" for target \"%s\"",
$2, target->t_name);
free($2);
return (1);
}
if (!TAILQ_EMPTY(&pp->pp_ports)) {
log_warnx("can't link port \"%s\" to target \"%s\", "
"port already linked to some target",
$2, target->t_name);
free($2);
return (1);
}
tp = port_new_pp(conf, target, pp);
if (tp == NULL) {
log_warnx("can't link port \"%s\" to target \"%s\"",
$2, target->t_name);
free($2);
return (1);
ret = sscanf($2, "ioctl/%d/%d", &i_pp, &i_vp);
if (ret > 0) {
tp = port_new_ioctl(conf, target, i_pp, i_vp);
if (tp == NULL) {
log_warnx("can't create new ioctl port for "
"target \"%s\"", target->t_name);
free($2);
return (1);
}
} else {
pp = pport_find(conf, $2);
if (pp == NULL) {
log_warnx("unknown port \"%s\" for target \"%s\"",
$2, target->t_name);
free($2);
return (1);
}
if (!TAILQ_EMPTY(&pp->pp_ports)) {
log_warnx("can't link port \"%s\" to target \"%s\", "
"port already linked to some target",
$2, target->t_name);
free($2);
return (1);
}
tp = port_new_pp(conf, target, pp);
if (tp == NULL) {
log_warnx("can't link port \"%s\" to target \"%s\"",
$2, target->t_name);
free($2);
return (1);
}
}
free($2);
}
;

View File

@ -758,6 +758,19 @@ uclparse_target(const char *name, const ucl_object_t *top)
struct pport *pp;
struct port *tp;
const char *value = ucl_object_tostring(obj);
int ret, i_pp, i_vp = 0;
ret = sscanf(value, "ioctl/%d/%d", &i_pp, &i_vp);
if (ret > 0) {
tp = port_new_ioctl(conf, target, i_pp, i_vp);
if (tp == NULL) {
log_warnx("can't create new ioctl port "
"for target \"%s\"", target->t_name);
return (1);
}
return (0);
}
pp = pport_find(conf, value);
if (pp == NULL) {