From 7da5c8a0d63c1123eaaef5b9ebab7e35430f88da Mon Sep 17 00:00:00 2001 From: mav Date: Tue, 15 Jul 2014 16:59:46 +0000 Subject: MFC r268284: Introduce new IOCTL CTL_PORT_LIST reporting in more flexible XML format. Leave old CTL_GET_PORT_LIST in place so far. Garbage-collect it later. --- sys/cam/ctl/ctl.c | 98 +++++++++++++++++ sys/cam/ctl/ctl_ioctl.h | 1 + usr.sbin/ctladm/ctladm.c | 271 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 369 insertions(+), 1 deletion(-) diff --git a/sys/cam/ctl/ctl.c b/sys/cam/ctl/ctl.c index f2867f5..2415066 100644 --- a/sys/cam/ctl/ctl.c +++ b/sys/cam/ctl/ctl.c @@ -3140,6 +3140,104 @@ ctl_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flag, retval = fe->ioctl(dev, cmd, addr, flag, td); break; } + case CTL_PORT_LIST: { + struct sbuf *sb; + struct ctl_port *port; + struct ctl_lun_list *list; +// struct ctl_option *opt; + + list = (struct ctl_lun_list *)addr; + + sb = sbuf_new(NULL, NULL, list->alloc_len, SBUF_FIXEDLEN); + if (sb == NULL) { + list->status = CTL_LUN_LIST_ERROR; + snprintf(list->error_str, sizeof(list->error_str), + "Unable to allocate %d bytes for LUN list", + list->alloc_len); + break; + } + + sbuf_printf(sb, "\n"); + + mtx_lock(&softc->ctl_lock); + STAILQ_FOREACH(port, &softc->port_list, links) { + retval = sbuf_printf(sb, "\n", + (uintmax_t)port->targ_port); + + /* + * Bail out as soon as we see that we've overfilled + * the buffer. + */ + if (retval != 0) + break; + + retval = sbuf_printf(sb, "\t%s" + "\n", port->frontend->name); + if (retval != 0) + break; + + retval = sbuf_printf(sb, "\t%d\n", + port->port_type); + if (retval != 0) + break; + + retval = sbuf_printf(sb, "\t%s\n", + (port->status & CTL_PORT_STATUS_ONLINE) ? "YES" : "NO"); + if (retval != 0) + break; + + retval = sbuf_printf(sb, "\t%s\n", + port->port_name); + if (retval != 0) + break; + + retval = sbuf_printf(sb, "\t%d\n", + port->physical_port); + if (retval != 0) + break; + + retval = sbuf_printf(sb, "\t%d\n", + port->virtual_port); + if (retval != 0) + break; + + retval = sbuf_printf(sb, "\t%#jx\n", + (uintmax_t)port->wwnn); + if (retval != 0) + break; + + retval = sbuf_printf(sb, "\t%#jx\n", + (uintmax_t)port->wwpn); + if (retval != 0) + break; + + retval = sbuf_printf(sb, "\n"); + if (retval != 0) + break; + } + mtx_unlock(&softc->ctl_lock); + + if ((retval != 0) + || ((retval = sbuf_printf(sb, "\n")) != 0)) { + retval = 0; + sbuf_delete(sb); + list->status = CTL_LUN_LIST_NEED_MORE_SPACE; + snprintf(list->error_str, sizeof(list->error_str), + "Out of space, %d bytes is too small", + list->alloc_len); + break; + } + + sbuf_finish(sb); + + retval = copyout(sbuf_data(sb), list->lun_xml, + sbuf_len(sb) + 1); + + list->fill_len = sbuf_len(sb) + 1; + list->status = CTL_LUN_LIST_OK; + sbuf_delete(sb); + break; + } default: { /* XXX KDM should we fix this? */ #if 0 diff --git a/sys/cam/ctl/ctl_ioctl.h b/sys/cam/ctl/ctl_ioctl.h index 93dc492..1144b75 100644 --- a/sys/cam/ctl/ctl_ioctl.h +++ b/sys/cam/ctl/ctl_ioctl.h @@ -789,6 +789,7 @@ struct ctl_iscsi { #define CTL_ERROR_INJECT_DELETE _IOW(CTL_MINOR, 0x23, struct ctl_error_desc) #define CTL_SET_PORT_WWNS _IOW(CTL_MINOR, 0x24, struct ctl_port_entry) #define CTL_ISCSI _IOWR(CTL_MINOR, 0x25, struct ctl_iscsi) +#define CTL_PORT_LIST _IOWR(CTL_MINOR, 0x26, struct ctl_lun_list) #endif /* _CTL_IOCTL_H_ */ diff --git a/usr.sbin/ctladm/ctladm.c b/usr.sbin/ctladm/ctladm.c index 7833155..e550efc 100644 --- a/usr.sbin/ctladm/ctladm.c +++ b/usr.sbin/ctladm/ctladm.c @@ -95,6 +95,7 @@ typedef enum { CTLADM_CMD_READ, CTLADM_CMD_WRITE, CTLADM_CMD_PORT, + CTLADM_CMD_PORTLIST, CTLADM_CMD_READCAPACITY, CTLADM_CMD_MODESENSE, CTLADM_CMD_DUMPOOA, @@ -190,6 +191,7 @@ static struct ctladm_opts option_table[] = { {"modesense", CTLADM_CMD_MODESENSE, CTLADM_ARG_NEED_TL, "P:S:dlm:c:"}, {"modify", CTLADM_CMD_MODIFY, CTLADM_ARG_NONE, "b:l:s:"}, {"port", CTLADM_CMD_PORT, CTLADM_ARG_NONE, "lo:p:qt:w:W:x"}, + {"portlist", CTLADM_CMD_PORTLIST, CTLADM_ARG_NONE, "f:vx"}, {"prin", CTLADM_CMD_PRES_IN, CTLADM_ARG_NEED_TL, "a:"}, {"prout", CTLADM_CMD_PRES_OUT, CTLADM_ARG_NEED_TL, "a:k:r:s:"}, {"read", CTLADM_CMD_READ, CTLADM_ARG_NEED_TL, rw_opts}, @@ -4073,6 +4075,269 @@ bailout: return (retval); } +/* + * Port information. + */ +struct cctl_port { + uint64_t port_id; + char *online; + char *frontend_type; + char *name; + int pp, vp; + char *wwnn, *wwpn; + STAILQ_HEAD(,cctl_lun_nv) attr_list; + STAILQ_ENTRY(cctl_port) links; +}; + +struct cctl_portlist_data { + int num_ports; + STAILQ_HEAD(,cctl_port) port_list; + struct cctl_port *cur_port; + int level; + struct sbuf *cur_sb[32]; +}; + +static void +cctl_start_pelement(void *user_data, const char *name, const char **attr) +{ + int i; + struct cctl_portlist_data *portlist; + struct cctl_port *cur_port; + + portlist = (struct cctl_portlist_data *)user_data; + cur_port = portlist->cur_port; + portlist->level++; + if ((u_int)portlist->level >= (sizeof(portlist->cur_sb) / + sizeof(portlist->cur_sb[0]))) + errx(1, "%s: too many nesting levels, %zd max", __func__, + sizeof(portlist->cur_sb) / sizeof(portlist->cur_sb[0])); + + portlist->cur_sb[portlist->level] = sbuf_new_auto(); + if (portlist->cur_sb[portlist->level] == NULL) + err(1, "%s: Unable to allocate sbuf", __func__); + + if (strcmp(name, "targ_port") == 0) { + if (cur_port != NULL) + errx(1, "%s: improper port element nesting", __func__); + + cur_port = calloc(1, sizeof(*cur_port)); + if (cur_port == NULL) + err(1, "%s: cannot allocate %zd bytes", __func__, + sizeof(*cur_port)); + + portlist->num_ports++; + portlist->cur_port = cur_port; + + STAILQ_INIT(&cur_port->attr_list); + STAILQ_INSERT_TAIL(&portlist->port_list, cur_port, links); + + for (i = 0; attr[i] != NULL; i += 2) { + if (strcmp(attr[i], "id") == 0) { + cur_port->port_id = strtoull(attr[i+1], NULL, 0); + } else { + errx(1, "%s: invalid LUN attribute %s = %s", + __func__, attr[i], attr[i+1]); + } + } + } +} + +static void +cctl_end_pelement(void *user_data, const char *name) +{ + struct cctl_portlist_data *portlist; + struct cctl_port *cur_port; + char *str; + + portlist = (struct cctl_portlist_data *)user_data; + cur_port = portlist->cur_port; + + if ((cur_port == NULL) + && (strcmp(name, "ctlportlist") != 0)) + errx(1, "%s: cur_port == NULL! (name = %s)", __func__, name); + + if (portlist->cur_sb[portlist->level] == NULL) + errx(1, "%s: no valid sbuf at level %d (name %s)", __func__, + portlist->level, name); + + if (sbuf_finish(portlist->cur_sb[portlist->level]) != 0) + err(1, "%s: sbuf_finish", __func__); + str = strdup(sbuf_data(portlist->cur_sb[portlist->level])); + if (str == NULL) + err(1, "%s can't allocate %zd bytes for string", __func__, + sbuf_len(portlist->cur_sb[portlist->level])); + + if (strlen(str) == 0) { + free(str); + str = NULL; + } + + sbuf_delete(portlist->cur_sb[portlist->level]); + portlist->cur_sb[portlist->level] = NULL; + portlist->level--; + + if (strcmp(name, "frontend_type") == 0) { + cur_port->frontend_type = str; + str = NULL; + } else if (strcmp(name, "port_name") == 0) { + cur_port->name = str; + str = NULL; + } else if (strcmp(name, "online") == 0) { + cur_port->online = str; + str = NULL; + } else if (strcmp(name, "physical_port") == 0) { + cur_port->pp = strtoull(str, NULL, 0); + } else if (strcmp(name, "virtual_port") == 0) { + cur_port->pp = strtoull(str, NULL, 0); + } else if (strcmp(name, "wwnn") == 0) { + cur_port->wwnn = str; + str = NULL; + } else if (strcmp(name, "wwpn") == 0) { + cur_port->wwpn = str; + str = NULL; + } else if (strcmp(name, "targ_port") == 0) { + portlist->cur_port = NULL; + } else if (strcmp(name, "ctlportlist") == 0) { + + } else { + struct cctl_lun_nv *nv; + + nv = calloc(1, sizeof(*nv)); + if (nv == NULL) + err(1, "%s: can't allocate %zd bytes for nv pair", + __func__, sizeof(*nv)); + + nv->name = strdup(name); + if (nv->name == NULL) + err(1, "%s: can't allocated %zd bytes for string", + __func__, strlen(name)); + + nv->value = str; + str = NULL; + STAILQ_INSERT_TAIL(&cur_port->attr_list, nv, links); + } + + free(str); +} + +static void +cctl_char_phandler(void *user_data, const XML_Char *str, int len) +{ + struct cctl_portlist_data *portlist; + + portlist = (struct cctl_portlist_data *)user_data; + + sbuf_bcat(portlist->cur_sb[portlist->level], str, len); +} + +static int +cctl_portlist(int fd, int argc, char **argv, char *combinedopt) +{ + struct ctl_lun_list list; + struct cctl_portlist_data portlist; + struct cctl_port *port; + XML_Parser parser; + char *port_str; + int port_len; + int dump_xml = 0; + int retval, c; + char *frontend = NULL; + int verbose = 0; + + retval = 0; + port_len = 4096; + + bzero(&portlist, sizeof(portlist)); + STAILQ_INIT(&portlist.port_list); + + while ((c = getopt(argc, argv, combinedopt)) != -1) { + switch (c) { + case 'f': + frontend = strdup(optarg); + break; + case 'v': + verbose++; + break; + case 'x': + dump_xml = 1; + break; + default: + break; + } + } + +retry: + port_str = malloc(port_len); + + bzero(&list, sizeof(list)); + list.alloc_len = port_len; + list.status = CTL_LUN_LIST_NONE; + list.lun_xml = port_str; + + if (ioctl(fd, CTL_PORT_LIST, &list) == -1) { + warn("%s: error issuing CTL_PORT_LIST ioctl", __func__); + retval = 1; + goto bailout; + } + + if (list.status == CTL_LUN_LIST_ERROR) { + warnx("%s: error returned from CTL_PORT_LIST ioctl:\n%s", + __func__, list.error_str); + } else if (list.status == CTL_LUN_LIST_NEED_MORE_SPACE) { + port_len = port_len << 1; + goto retry; + } + + if (dump_xml != 0) { + printf("%s", port_str); + goto bailout; + } + + parser = XML_ParserCreate(NULL); + if (parser == NULL) { + warn("%s: Unable to create XML parser", __func__); + retval = 1; + goto bailout; + } + + XML_SetUserData(parser, &portlist); + XML_SetElementHandler(parser, cctl_start_pelement, cctl_end_pelement); + XML_SetCharacterDataHandler(parser, cctl_char_phandler); + + retval = XML_Parse(parser, port_str, strlen(port_str), 1); + XML_ParserFree(parser); + if (retval != 1) { + retval = 1; + goto bailout; + } + + printf("Port Online Frontend %-12s pp vp %-18s %-18s\n", + "Name", "WWNN", "WWPN"); + STAILQ_FOREACH(port, &portlist.port_list, links) { + struct cctl_lun_nv *nv; + + if ((frontend != NULL) + && (strcmp(port->frontend_type, frontend) != 0)) + continue; + + printf("%-4ju %-6s %-8s %-12s %-2d %-2d %-18s %-18s\n", + (uintmax_t)port->port_id, port->online, + port->frontend_type, port->name, port->pp, port->vp, + port->wwnn, port->wwpn); + + if (verbose == 0) + continue; + + STAILQ_FOREACH(nv, &port->attr_list, links) { + printf(" %s=%s\n", nv->name, nv->value); + } + } +bailout: + free(port_str); + + return (retval); +} + void usage(int error) { @@ -4104,7 +4369,7 @@ usage(int error) " [-S serial_num] [-t dev_type]\n" " ctladm remove <-b backend> <-l lun_id> [-o name=value]\n" " ctladm modify <-b backend> <-l lun_id> <-s size_bytes>\n" -" ctladm devlist [-b][-v][-x]\n" +" ctladm devlist [-b backend] [-v] [-x]\n" " ctladm shutdown\n" " ctladm startup\n" " ctladm hardstop\n" @@ -4120,6 +4385,7 @@ usage(int error) " [-s len fmt [args]] [-c] [-d delete_id]\n" " ctladm port <-l | -o | [-w wwnn][-W wwpn]>\n" " [-p targ_port] [-t port_type] [-q] [-x]\n" +" ctladm portlist [-f frontend] [-v] [-x]\n" " ctladm islist [-v | -x]\n" " ctladm islogout <-a | -c connection-id | -i name | -p portal>\n" " ctladm isterminate <-a | -c connection-id | -i name | -p portal>\n" @@ -4429,6 +4695,9 @@ main(int argc, char **argv) case CTLADM_CMD_PORT: retval = cctl_port(fd, argc, argv, combinedopt); break; + case CTLADM_CMD_PORTLIST: + retval = cctl_portlist(fd, argc, argv, combinedopt); + break; case CTLADM_CMD_READCAPACITY: retval = cctl_read_capacity(fd, target, lun, initid, retries, argc, argv, combinedopt); -- cgit v1.1