Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F102837889
D44728.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
8 KB
Referenced Files
None
Subscribers
None
D44728.diff
View Options
diff --git a/usr.sbin/ctladm/ctladm.8 b/usr.sbin/ctladm/ctladm.8
--- a/usr.sbin/ctladm/ctladm.8
+++ b/usr.sbin/ctladm/ctladm.8
@@ -35,7 +35,7 @@
.\"
.\" $Id: //depot/users/kenm/FreeBSD-test2/usr.sbin/ctladm/ctladm.8#3 $
.\"
-.Dd December 27, 2023
+.Dd May 2, 2024
.Dt CTLADM 8
.Os
.Sh NAME
@@ -198,6 +198,10 @@
.Ic isterminate
.Aq Fl a | Fl c Ar connection-id | Fl i Ar name | Fl p Ar portal
.Nm
+.Ic nvlist
+.Op Fl v
+.Op Fl x
+.Nm
.Ic help
.Sh DESCRIPTION
The
@@ -861,6 +865,17 @@
.It Fl p
Specify initiator portal (hostname or IP address).
.El
+.It Ic nvlist
+Get a list of currently running NVMeoF associations.
+This includes host and controller names and the unique controller IDs.
+.Bl -tag -width 11n
+.It Fl v
+Verbose mode.
+.It Fl x
+Dump the raw XML.
+The sessions list information from the kernel comes in XML format, and this
+option allows the display of the raw XML data.
+.El
.It Ic help
Display
.Nm
diff --git a/usr.sbin/ctladm/ctladm.c b/usr.sbin/ctladm/ctladm.c
--- a/usr.sbin/ctladm/ctladm.c
+++ b/usr.sbin/ctladm/ctladm.c
@@ -71,6 +71,7 @@
#include <cam/ctl/ctl_ioctl.h>
#include <cam/ctl/ctl_util.h>
#include <cam/ctl/ctl_scsi_all.h>
+#include <dev/nvmf/nvmf_proto.h>
#include <camlib.h>
#include <libutil.h>
#include "ctladm.h"
@@ -113,7 +114,8 @@
CTLADM_CMD_ISLIST,
CTLADM_CMD_ISLOGOUT,
CTLADM_CMD_ISTERMINATE,
- CTLADM_CMD_LUNMAP
+ CTLADM_CMD_LUNMAP,
+ CTLADM_CMD_NVLIST
} ctladm_cmdfunction;
typedef enum {
@@ -179,6 +181,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:"},
+ {"nvlist", CTLADM_CMD_NVLIST, CTLADM_ARG_NONE, "vx"},
{"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:"},
@@ -3818,6 +3821,251 @@
return (retval);
}
+struct cctl_nvlist_conn {
+ int connection_id;
+ char *hostnqn;
+ char *subnqn;
+ int trtype;
+ STAILQ_ENTRY(cctl_nvlist_conn) links;
+};
+
+struct cctl_nvlist_data {
+ int num_conns;
+ STAILQ_HEAD(,cctl_nvlist_conn) conn_list;
+ struct cctl_nvlist_conn *cur_conn;
+ u_int level;
+ struct sbuf *cur_sb[32];
+};
+
+static void
+cctl_nvlist_start_element(void *user_data, const char *name, const char **attr)
+{
+ int i;
+ struct cctl_nvlist_data *nvlist;
+ struct cctl_nvlist_conn *cur_conn;
+
+ nvlist = (struct cctl_nvlist_data *)user_data;
+ cur_conn = nvlist->cur_conn;
+ nvlist->level++;
+ if ((u_int)nvlist->level >= nitems(nvlist->cur_sb))
+ errx(1, "%s: too many nesting levels, %zd max", __func__,
+ nitems(nvlist->cur_sb));
+
+ nvlist->cur_sb[nvlist->level] = sbuf_new_auto();
+ if (nvlist->cur_sb[nvlist->level] == NULL)
+ err(1, "%s: Unable to allocate sbuf", __func__);
+
+ if (strcmp(name, "connection") == 0) {
+ if (cur_conn != NULL)
+ errx(1, "%s: improper connection element nesting",
+ __func__);
+
+ cur_conn = calloc(1, sizeof(*cur_conn));
+ if (cur_conn == NULL)
+ err(1, "%s: cannot allocate %zd bytes", __func__,
+ sizeof(*cur_conn));
+
+ nvlist->num_conns++;
+ nvlist->cur_conn = cur_conn;
+
+ STAILQ_INSERT_TAIL(&nvlist->conn_list, cur_conn, links);
+
+ for (i = 0; attr[i] != NULL; i += 2) {
+ if (strcmp(attr[i], "id") == 0) {
+ cur_conn->connection_id =
+ strtoull(attr[i+1], NULL, 0);
+ } else {
+ errx(1,
+ "%s: invalid connection attribute %s = %s",
+ __func__, attr[i], attr[i+1]);
+ }
+ }
+ }
+}
+
+static void
+cctl_nvlist_end_element(void *user_data, const char *name)
+{
+ struct cctl_nvlist_data *nvlist;
+ struct cctl_nvlist_conn *cur_conn;
+ char *str;
+
+ nvlist = (struct cctl_nvlist_data *)user_data;
+ cur_conn = nvlist->cur_conn;
+
+ if ((cur_conn == NULL) && (strcmp(name, "ctlnvmflist") != 0))
+ errx(1, "%s: cur_conn == NULL! (name = %s)", __func__, name);
+
+ if (nvlist->cur_sb[nvlist->level] == NULL)
+ errx(1, "%s: no valid sbuf at level %d (name %s)", __func__,
+ nvlist->level, name);
+
+ sbuf_finish(nvlist->cur_sb[nvlist->level]);
+ str = strdup(sbuf_data(nvlist->cur_sb[nvlist->level]));
+ if (str == NULL)
+ err(1, "%s can't allocate %zd bytes for string", __func__,
+ sbuf_len(nvlist->cur_sb[nvlist->level]));
+
+ sbuf_delete(nvlist->cur_sb[nvlist->level]);
+ nvlist->cur_sb[nvlist->level] = NULL;
+ nvlist->level--;
+
+ if (strcmp(name, "hostnqn") == 0) {
+ cur_conn->hostnqn = str;
+ str = NULL;
+ } else if (strcmp(name, "subnqn") == 0) {
+ cur_conn->subnqn = str;
+ str = NULL;
+ } else if (strcmp(name, "trtype") == 0) {
+ cur_conn->trtype = atoi(str);
+ str = NULL;
+ } else if (strcmp(name, "connection") == 0) {
+ nvlist->cur_conn = NULL;
+ } else if (strcmp(name, "ctlnvmflist") == 0) {
+ /* Nothing. */
+ } else {
+ /*
+ * Unknown element; ignore it for forward compatibility.
+ */
+ }
+
+ free(str);
+}
+
+static void
+cctl_nvlist_char_handler(void *user_data, const XML_Char *str, int len)
+{
+ struct cctl_nvlist_data *nvlist;
+
+ nvlist = (struct cctl_nvlist_data *)user_data;
+
+ sbuf_bcat(nvlist->cur_sb[nvlist->level], str, len);
+}
+
+static const char *
+nvmf_transport_descr(u_int trtype)
+{
+ static char buf[16];
+
+ switch (trtype) {
+ case NVMF_TRTYPE_RDMA:
+ return ("RDMA");
+ case NVMF_TRTYPE_FC:
+ return ("Fibre Channel");
+ case NVMF_TRTYPE_TCP:
+ return ("TCP");
+ default:
+ snprintf(buf, sizeof(buf), "%#x", trtype);
+ return (buf);
+ }
+}
+
+static int
+cctl_nvlist(int fd, int argc, char **argv, char *combinedopt)
+{
+ struct ctl_nvmf req;
+ struct cctl_nvlist_data nvlist;
+ struct cctl_nvlist_conn *conn;
+ XML_Parser parser;
+ char *conn_str;
+ int conn_len;
+ int dump_xml = 0;
+ int c, retval, verbose = 0;
+
+ retval = 0;
+ conn_len = 4096;
+
+ bzero(&nvlist, sizeof(nvlist));
+ STAILQ_INIT(&nvlist.conn_list);
+
+ while ((c = getopt(argc, argv, combinedopt)) != -1) {
+ switch (c) {
+ case 'v':
+ verbose = 1;
+ break;
+ case 'x':
+ dump_xml = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+retry:
+ conn_str = malloc(conn_len);
+
+ bzero(&req, sizeof(req));
+ req.type = CTL_NVMF_LIST;
+ req.data.list.alloc_len = conn_len;
+ req.data.list.conn_xml = conn_str;
+
+ if (ioctl(fd, CTL_NVMF, &req) == -1) {
+ warn("%s: error issuing CTL_NVMF ioctl", __func__);
+ retval = 1;
+ goto bailout;
+ }
+
+ if (req.status == CTL_NVMF_ERROR) {
+ warnx("%s: error returned from CTL_NVMF ioctl:\n%s",
+ __func__, req.error_str);
+ } else if (req.status == CTL_NVMF_LIST_NEED_MORE_SPACE) {
+ conn_len = conn_len << 1;
+ goto retry;
+ }
+
+ if (dump_xml != 0) {
+ printf("%s", conn_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, &nvlist);
+ XML_SetElementHandler(parser, cctl_nvlist_start_element,
+ cctl_nvlist_end_element);
+ XML_SetCharacterDataHandler(parser, cctl_nvlist_char_handler);
+
+ retval = XML_Parse(parser, conn_str, strlen(conn_str), 1);
+ if (retval != 1) {
+ warnx("%s: Unable to parse XML: Error %d", __func__,
+ XML_GetErrorCode(parser));
+ XML_ParserFree(parser);
+ retval = 1;
+ goto bailout;
+ }
+ retval = 0;
+ XML_ParserFree(parser);
+
+ if (verbose != 0) {
+ STAILQ_FOREACH(conn, &nvlist.conn_list, links) {
+ printf("%-25s %d\n", "Controller ID:", conn->connection_id);
+ printf("%-25s %s\n", "Host NQN:", conn->hostnqn);
+ printf("%-25s %s\n", "Subsystem NQN:", conn->subnqn);
+ printf("%-25s %s\n", "Transport:",
+ nvmf_transport_descr(conn->trtype));
+ printf("\n");
+ }
+ } else {
+ printf("%4s %-16s %-36s %-36s\n", "ID", "Transport", "HostNQN",
+ "SubNQN");
+ STAILQ_FOREACH(conn, &nvlist.conn_list, links) {
+ printf("%4u %-16s %-36s %-36s\n",
+ conn->connection_id,
+ nvmf_transport_descr(conn->trtype),
+ conn->hostnqn, conn->subnqn);
+ }
+ }
+bailout:
+ free(conn_str);
+
+ return (retval);
+}
+
void
usage(int error)
{
@@ -3864,6 +4112,7 @@
" 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"
+" ctladm nvlist [-v | -x]\n"
" ctladm dumpooa\n"
" ctladm dumpstructs\n"
" ctladm help\n"
@@ -4260,6 +4509,9 @@
case CTLADM_CMD_ISTERMINATE:
retval = cctl_isterminate(fd, argc, argv, combinedopt);
break;
+ case CTLADM_CMD_NVLIST:
+ retval = cctl_nvlist(fd, argc, argv, combinedopt);
+ break;
case CTLADM_CMD_HELP:
default:
usage(retval);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Nov 18, 7:55 PM (21 h, 10 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
14703830
Default Alt Text
D44728.diff (8 KB)
Attached To
Mode
D44728: ctladm: Add nvlist command to list active NVMeoF associations
Attached
Detach File
Event Timeline
Log In to Comment