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.
This commit is contained in:
parent
2cfbcb9b3a
commit
92782c33a6
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=268284
@ -3138,6 +3138,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, "<ctlportlist>\n");
|
||||
|
||||
mtx_lock(&softc->ctl_lock);
|
||||
STAILQ_FOREACH(port, &softc->port_list, links) {
|
||||
retval = sbuf_printf(sb, "<targ_port id=\"%ju\">\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<frontend_type>%s"
|
||||
"</frontend_type>\n", port->frontend->name);
|
||||
if (retval != 0)
|
||||
break;
|
||||
|
||||
retval = sbuf_printf(sb, "\t<port_type>%d</port_type>\n",
|
||||
port->port_type);
|
||||
if (retval != 0)
|
||||
break;
|
||||
|
||||
retval = sbuf_printf(sb, "\t<online>%s</online>\n",
|
||||
(port->status & CTL_PORT_STATUS_ONLINE) ? "YES" : "NO");
|
||||
if (retval != 0)
|
||||
break;
|
||||
|
||||
retval = sbuf_printf(sb, "\t<port_name>%s</port_name>\n",
|
||||
port->port_name);
|
||||
if (retval != 0)
|
||||
break;
|
||||
|
||||
retval = sbuf_printf(sb, "\t<physical_port>%d</physical_port>\n",
|
||||
port->physical_port);
|
||||
if (retval != 0)
|
||||
break;
|
||||
|
||||
retval = sbuf_printf(sb, "\t<virtual_port>%d</virtual_port>\n",
|
||||
port->virtual_port);
|
||||
if (retval != 0)
|
||||
break;
|
||||
|
||||
retval = sbuf_printf(sb, "\t<wwnn>%#jx</wwnn>\n",
|
||||
(uintmax_t)port->wwnn);
|
||||
if (retval != 0)
|
||||
break;
|
||||
|
||||
retval = sbuf_printf(sb, "\t<wwpn>%#jx</wwpn>\n",
|
||||
(uintmax_t)port->wwpn);
|
||||
if (retval != 0)
|
||||
break;
|
||||
|
||||
retval = sbuf_printf(sb, "</targ_port>\n");
|
||||
if (retval != 0)
|
||||
break;
|
||||
}
|
||||
mtx_unlock(&softc->ctl_lock);
|
||||
|
||||
if ((retval != 0)
|
||||
|| ((retval = sbuf_printf(sb, "</ctlportlist>\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
|
||||
|
@ -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_ */
|
||||
|
||||
|
@ -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 @@ cctl_devlist(int fd, int argc, char **argv, char *combinedopt)
|
||||
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 <on|off> | [-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);
|
||||
|
Loading…
Reference in New Issue
Block a user