Index: sys/cam/ctl/ctl.h =================================================================== --- sys/cam/ctl/ctl.h +++ sys/cam/ctl/ctl.h @@ -196,24 +196,6 @@ 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 */ Index: sys/cam/ctl/ctl.c =================================================================== --- sys/cam/ctl/ctl.c +++ sys/cam/ctl/ctl.c @@ -4,6 +4,8 @@ * Copyright (c) 2003-2009 Silicon Graphics International Corp. * Copyright (c) 2012 The FreeBSD Foundation * Copyright (c) 2014-2017 Alexander Motin + * Copyright (c) 2017 Jakub Wojciech Klama + * Copyright (c) 2018 Marcelo Araujo * All rights reserved. * * Portions of this software were developed by Edward Tomasz Napierala @@ -65,6 +67,8 @@ #include #include #include +#include +#include #include #include @@ -1869,6 +1873,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ if (retval != 0) break; } - STAILQ_FOREACH(opt, &lun->be_lun->options, links) { - retval = sbuf_printf(sb, "\t<%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, "\n", name); } retval = sbuf_printf(sb, "\n"); @@ -3259,8 +3214,12 @@ 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 @@ "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 @@ 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 @@ if (retval != 0) break; } - STAILQ_FOREACH(opt, &port->options, links) { - retval = sbuf_printf(sb, "\t<%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, "\n", name); } if (port->lun_map != NULL) { @@ -4180,8 +4189,8 @@ 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 @@ 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 @@ 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 @@ &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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ { 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ * 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 @@ 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 @@ * 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)); Index: sys/cam/ctl/ctl_backend.h =================================================================== --- sys/cam/ctl/ctl_backend.h +++ sys/cam/ctl/ctl_backend.h @@ -43,6 +43,7 @@ #define _CTL_BACKEND_H_ #include +#include typedef enum { CTL_LUN_SERSEQ_OFF, @@ -175,7 +176,7 @@ 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 */ }; Index: sys/cam/ctl/ctl_backend.c =================================================================== --- sys/cam/ctl/ctl_backend.c +++ sys/cam/ctl/ctl_backend.c @@ -141,93 +141,3 @@ 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)); -} Index: sys/cam/ctl/ctl_backend_block.c =================================================================== --- sys/cam/ctl/ctl_backend_block.c +++ sys/cam/ctl/ctl_backend_block.c @@ -78,6 +78,8 @@ #include #include #include +#include +#include #include @@ -1817,7 +1819,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ { 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 @@ } 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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; Index: sys/cam/ctl/ctl_backend_ramdisk.c =================================================================== --- sys/cam/ctl/ctl_backend_ramdisk.c +++ sys/cam/ctl/ctl_backend_ramdisk.c @@ -62,6 +62,8 @@ #include #include #include +#include +#include #include #include @@ -956,7 +958,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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; Index: sys/cam/ctl/ctl_frontend.h =================================================================== --- sys/cam/ctl/ctl_frontend.h +++ sys/cam/ctl/ctl_frontend.h @@ -43,6 +43,7 @@ #define _CTL_FRONTEND_H_ #include +#include typedef enum { CTL_PORT_STATUS_NONE = 0x00, @@ -237,7 +238,7 @@ 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 */ Index: sys/cam/ctl/ctl_frontend.c =================================================================== --- sys/cam/ctl/ctl_frontend.c +++ sys/cam/ctl/ctl_frontend.c @@ -52,6 +52,8 @@ #include #include #include +#include +#include #include #include @@ -200,8 +202,8 @@ } 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 @@ 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 @@ 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 Index: sys/cam/ctl/ctl_frontend_ioctl.c =================================================================== --- sys/cam/ctl/ctl_frontend_ioctl.c +++ 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 + * Copyright (c) 2017 Jakub Wojciech Klama * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -41,6 +44,8 @@ #include #include #include +#include +#include #include #include @@ -68,22 +73,41 @@ 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 @@ 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,15 +148,187 @@ 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); +} + /* * Data movement routine for the CTL ioctl frontend port. */ @@ -389,18 +590,26 @@ 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 @@ /* * 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); } Index: sys/cam/ctl/ctl_frontend_iscsi.c =================================================================== --- sys/cam/ctl/ctl_frontend_iscsi.c +++ sys/cam/ctl/ctl_frontend_iscsi.c @@ -56,6 +56,8 @@ #include #include #include +#include +#include #include #include @@ -2105,30 +2107,30 @@ { 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 @@ "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 @@ /* 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,10 +2160,8 @@ port->fe_datamove = cfiscsi_datamove; port->fe_done = cfiscsi_done; port->targ_port = -1; + port->options = nvlist_clone(req->args_nvl); - port->options = opts; - STAILQ_INIT(&opts); - /* Generate Port ID. */ idlen = strlen(target) + strlen(",t,0x0001") + 1; idlen = roundup2(idlen, 4); @@ -2193,7 +2192,6 @@ 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 @@ 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); Index: sys/cam/ctl/ctl_ioctl.h =================================================================== --- sys/cam/ctl/ctl_ioctl.h +++ sys/cam/ctl/ctl_ioctl.h @@ -48,6 +48,7 @@ #endif #include +#include #define CTL_DEFAULT_DEV "/dev/cam/ctl" /* @@ -316,39 +317,6 @@ #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 @@ 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 @@ 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; }; /* Index: sys/cam/ctl/ctl_tpc.c =================================================================== --- sys/cam/ctl/ctl_tpc.c +++ sys/cam/ctl/ctl_tpc.c @@ -41,6 +41,8 @@ #include #include #include +#include +#include #include #include @@ -1668,7 +1670,7 @@ 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 @@ 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 @@ 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 @@ 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 Index: sys/sys/param.h =================================================================== --- sys/sys/param.h +++ sys/sys/param.h @@ -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, Index: usr.sbin/ctladm/Makefile =================================================================== --- usr.sbin/ctladm/Makefile +++ usr.sbin/ctladm/Makefile @@ -16,7 +16,7 @@ WARNS?= 3 .endif -LIBADD= cam sbuf bsdxml util +LIBADD= cam sbuf bsdxml util nv MAN= ctladm.8 .if ${MK_ISCSI} != "no" Index: usr.sbin/ctladm/ctladm.8 =================================================================== --- usr.sbin/ctladm/ctladm.8 +++ usr.sbin/ctladm/ctladm.8 @@ -1,6 +1,7 @@ .\" .\" Copyright (c) 2003 Silicon Graphics International Corp. .\" Copyright (c) 2015 Alexander Motin +.\" Copyright (c) 2018 Marcelo Araujo .\" 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 Apr 13, 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 , Index: usr.sbin/ctladm/ctladm.c =================================================================== --- usr.sbin/ctladm/ctladm.c +++ usr.sbin/ctladm/ctladm.c @@ -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 * All rights reserved. * * Portions of this software were developed by Edward Tomasz Napierala @@ -50,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -178,7 +182,7 @@ {"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 @@ 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 @@ 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 @@ 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 @@ 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 @@ } } + 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_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 @@ } bailout: - + nvlist_destroy(req.args_nvl); + free(driver); return (retval); bailout_badarg: @@ -2271,14 +2378,6 @@ 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 @@ 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 @@ lun_id_set = 1; break; case 'o': { - struct cctl_req_option *option; char *tmpstr; char *name, *value; @@ -2332,21 +2431,8 @@ 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 @@ 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 @@ 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 @@ 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 @@ lun_id_set = 1; break; case 'o': { - struct cctl_req_option *option; char *tmpstr; char *name, *value; @@ -2528,21 +2588,8 @@ 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 @@ 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 @@ printf("LUN %d removed successfully\n", lun_id); bailout: + nvlist_destroy(req.args_nvl); return (retval); } @@ -2635,11 +2656,13 @@ 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 @@ lun_id_set = 1; break; case 'o': { - struct cctl_req_option *option; char *tmpstr; char *name, *value; @@ -2669,21 +2691,8 @@ 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 @@ 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 @@ 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 @@ printf("LUN %d modified successfully\n", lun_id); bailout: + nvlist_destroy(req.args_nvl); return (retval); } @@ -3661,7 +3644,7 @@ 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 @@ } 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 @@ " [-s len fmt [args]] [-c] [-d delete_id]\n" " ctladm port <-o | [-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 @@ "-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" Index: usr.sbin/ctld/Makefile =================================================================== --- usr.sbin/ctld/Makefile +++ usr.sbin/ctld/Makefile @@ -15,7 +15,7 @@ #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 Index: usr.sbin/ctld/ctld.h =================================================================== --- usr.sbin/ctld/ctld.h +++ usr.sbin/ctld/ctld.h @@ -152,6 +152,9 @@ 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 @@ 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); Index: usr.sbin/ctld/ctld.c =================================================================== --- usr.sbin/ctld/ctld.c +++ usr.sbin/ctld/ctld.c @@ -1234,6 +1234,7 @@ 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; @@ -1243,6 +1244,51 @@ } 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) { struct port *port; @@ -1627,9 +1673,9 @@ 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 @@ 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) { Index: usr.sbin/ctld/kernel.c =================================================================== --- usr.sbin/ctld/kernel.c +++ usr.sbin/ctld/kernel.c @@ -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 * All rights reserved. * * Portions of this software were developed by Edward Tomasz Napierala @@ -47,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -73,6 +75,8 @@ #include #endif +#define NVLIST_BUFSIZE 1024 + extern bool proxy_mode; static int ctl_fd = 0; @@ -652,23 +656,12 @@ 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 @@ 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 @@ { struct option *o; struct ctl_lun_req req; - int error, i, num_options; + int error; bzero(&req, sizeof(req)); @@ -786,29 +776,26 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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); Index: usr.sbin/ctld/parse.y =================================================================== --- usr.sbin/ctld/parse.y +++ usr.sbin/ctld/parse.y @@ -768,28 +768,41 @@ { 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); + 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); + } } - 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); } ; Index: usr.sbin/ctld/uclparse.c =================================================================== --- usr.sbin/ctld/uclparse.c +++ usr.sbin/ctld/uclparse.c @@ -758,6 +758,19 @@ 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) {