From 73a49f3e9933a359f5e77a20bcc733ce7bfcdaf9 Mon Sep 17 00:00:00 2001 From: Tomasz Zawadzki Date: Mon, 29 Jun 2020 09:04:18 -0400 Subject: [PATCH] add path to nvme list In SPDK project (spdk.io) it is possible to represent devices using CUSE as character devices in "/dev/spdk/" directory. No sysfs entries are generated for those devices. nvme-cli default behavior is to scan the sysfs for entries on newer kernels. Previously legacy_list() was used to determine the topology, but that worked only on "/dev/" path. To enable listing SPDK devices following changes were made: - pass path parameter to list command - before checking sysfs, check the path using legacy_list() - expand arguments of scan_subsystems and related functions to accept the path Example: nvme list -d /dev/spdk/ Signed-off-by: Tomasz Zawadzki --- fabrics.c | 2 +- nvme-print.c | 12 ++++++------ nvme-topology.c | 24 +++++++++++++++--------- nvme.c | 8 ++++++-- nvme.h | 3 ++- 5 files changed, 30 insertions(+), 19 deletions(-) diff --git a/fabrics.c b/fabrics.c index f8a055b7..87e6b57e 100644 --- a/fabrics.c +++ b/fabrics.c @@ -1687,7 +1687,7 @@ int fabrics_disconnect_all(const char *desc, int argc, char **argv) if (err) goto out; - err = scan_subsystems(&t, NULL, 0); + err = scan_subsystems(&t, NULL, 0, NULL); if (err) { fprintf(stderr, "Failed to scan namespaces\n"); goto out; diff --git a/nvme-print.c b/nvme-print.c index e7eac50e..ccb2f5a4 100644 --- a/nvme-print.c +++ b/nvme-print.c @@ -2334,7 +2334,7 @@ void nvme_show_relatives(const char *name) free(path); return; } - err = scan_subsystems(&t, subsysnqn, 0); + err = scan_subsystems(&t, subsysnqn, 0, NULL); if (err || t.nr_subsystems != 1) { free(subsysnqn); free(path); @@ -5404,7 +5404,7 @@ static void nvme_show_list_item(struct nvme_namespace *n) struct stat st; int ret; - sprintf(path, "/dev/%s", n->name); + sprintf(path, "%s%s", n->ctrl->path, n->name); ret = stat(path, &st); if (ret < 0) return; @@ -5413,7 +5413,7 @@ static void nvme_show_list_item(struct nvme_namespace *n) nsze, s_suffix); sprintf(format,"%3.0f %2sB + %2d B", (double)lba, l_suffix, le16_to_cpu(n->ns.lbaf[(n->ns.flbas & 0x0f)].ms)); - printf("/dev/%-11s %-*.*s %-*.*s %-9d %-26s %-16s %-.*s\n", n->name, + printf("%-21s %-*.*s %-*.*s %-9d %-26s %-16s %-.*s\n", path, (int)sizeof(n->ctrl->id.sn), (int)sizeof(n->ctrl->id.sn), n->ctrl->id.sn, (int)sizeof(n->ctrl->id.mn), (int)sizeof(n->ctrl->id.mn), n->ctrl->id.mn, n->nsid, usage, format, (int)sizeof(n->ctrl->id.fr), n->ctrl->id.fr); @@ -5423,9 +5423,9 @@ static void nvme_show_simple_list(struct nvme_topology *t) { int i, j, k; - printf("%-16s %-20s %-40s %-9s %-26s %-16s %-8s\n", + printf("%-21s %-20s %-40s %-9s %-26s %-16s %-8s\n", "Node", "SN", "Model", "Namespace", "Usage", "Format", "FW Rev"); - printf("%-.16s %-.20s %-.40s %-.9s %-.26s %-.16s %-.8s\n", dash, dash, + printf("%-.21s %-.20s %-.40s %-.9s %-.26s %-.16s %-.8s\n", dash, dash, dash, dash, dash, dash, dash); for (i = 0; i < t->nr_subsystems; i++) { @@ -5672,7 +5672,7 @@ static void json_simple_ns(struct nvme_namespace *n, struct json_array *devices) long long lba; char *devnode; - if (asprintf(&devnode, "/dev/%s", n->name) < 0) + if (asprintf(&devnode, "%s%s", n->ctrl->path, n->name) < 0) return; device_attrs = json_create_object(); diff --git a/nvme-topology.c b/nvme-topology.c index 94a4c563..202a8087 100644 --- a/nvme-topology.c +++ b/nvme-topology.c @@ -144,7 +144,7 @@ static int scan_namespace(struct nvme_namespace *n) int ret, fd; char *path; - ret = asprintf(&path, "%s%s", dev, n->name); + ret = asprintf(&path, "%s%s", n->ctrl->path, n->name); if (ret < 0) return ret; @@ -295,7 +295,7 @@ static int scan_ctrl(struct nvme_ctrl *c, char *p, __u32 ns_instance) free(ns); free(path); - ret = asprintf(&path, "%s%s", dev, c->name); + ret = asprintf(&path, "%s%s", c->path, c->name); if (ret < 0) return ret; @@ -338,6 +338,7 @@ static int scan_subsystem(struct nvme_subsystem *s, __u32 ns_instance) for (i = 0; i < s->nr_ctrls; i++) { c = &s->ctrls[i]; c->name = strdup(ctrls[i]->d_name); + c->path = strdup(dev); c->subsys = s; scan_ctrl(c, path, ns_instance); } @@ -381,7 +382,7 @@ static int verify_legacy_ns(struct nvme_namespace *n) char *path; int ret, fd; - ret = asprintf(&path, "%s%s", dev, n->name); + ret = asprintf(&path, "%s%s", n->ctrl->path, n->name); if (ret < 0) return ret; @@ -420,7 +421,7 @@ static int verify_legacy_ns(struct nvme_namespace *n) * is the controller to nvme0n1 for such older kernels. We will also assume * every controller is its own subsystem. */ -static int legacy_list(struct nvme_topology *t) +static int legacy_list(struct nvme_topology *t, char *dev_dir) { struct nvme_ctrl *c; struct nvme_subsystem *s; @@ -429,7 +430,7 @@ static int legacy_list(struct nvme_topology *t) int ret = 0, fd, i; char *path; - t->nr_subsystems = scandir(dev, &devices, scan_ctrls_filter, alphasort); + t->nr_subsystems = scandir(dev_dir, &devices, scan_ctrls_filter, alphasort); if (t->nr_subsystems < 0) { fprintf(stderr, "no NVMe device(s) detected.\n"); return t->nr_subsystems; @@ -449,7 +450,8 @@ static int legacy_list(struct nvme_topology *t) c = s->ctrls; c->name = strdup(s->name); sscanf(c->name, "nvme%d", ¤t_index); - c->nr_namespaces = scandir(dev, &namespaces, scan_dev_filter, + c->path = strdup(dev_dir); + c->nr_namespaces = scandir(c->path, &namespaces, scan_dev_filter, alphasort); c->namespaces = calloc(c->nr_namespaces, sizeof(*n)); if (!c->namespaces) { @@ -459,7 +461,7 @@ static int legacy_list(struct nvme_topology *t) continue; } - ret = asprintf(&path, "%s%s", dev, c->name); + ret = asprintf(&path, "%s%s", c->path, c->name); if (ret < 0) continue; ret = 0; @@ -501,6 +503,7 @@ static void free_ctrl(struct nvme_ctrl *c) free(n->name); } free(c->name); + free(c->path); free(c->transport); free(c->address); free(c->state); @@ -527,16 +530,19 @@ static void free_subsystem(struct nvme_subsystem *s) } int scan_subsystems(struct nvme_topology *t, const char *subsysnqn, - __u32 ns_instance) + __u32 ns_instance, char *dev_dir) { struct nvme_subsystem *s; struct dirent **subsys; int i, j = 0; + if (dev_dir != NULL) + return legacy_list(t, dev_dir); + t->nr_subsystems = scandir(subsys_dir, &subsys, scan_subsys_filter, alphasort); if (t->nr_subsystems < 0) - return legacy_list(t); + return legacy_list(t, (char *)dev); t->subsystems = calloc(t->nr_subsystems, sizeof(*s)); for (i = 0; i < t->nr_subsystems; i++) { diff --git a/nvme.c b/nvme.c index f84eef10..d84b6774 100644 --- a/nvme.c +++ b/nvme.c @@ -1589,7 +1589,7 @@ static int list_subsys(int argc, char **argv, struct command *cmd, if (cfg.verbose) flags |= VERBOSE; - err = scan_subsystems(&t, subsysnqn, ns_instance); + err = scan_subsystems(&t, subsysnqn, ns_instance, NULL); if (err) { fprintf(stderr, "Failed to scan namespaces\n"); goto free; @@ -1606,22 +1606,26 @@ ret: static int list(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Retrieve basic information for all NVMe namespaces"; + const char *device_dir = "Directory to search for devices"; const char *verbose = "Increase output verbosity"; struct nvme_topology t = { }; enum nvme_print_flags flags; int err = 0; struct config { + char *device_dir; char *output_format; int verbose; }; struct config cfg = { + .device_dir = NULL, .output_format = "normal", .verbose = 0, }; OPT_ARGS(opts) = { + OPT_STRING("directory", 'd', "DIR", &cfg.device_dir, device_dir), OPT_FMT("output-format", 'o', &cfg.output_format, output_format_no_binary), OPT_FLAG("verbose", 'v', &cfg.verbose, verbose), OPT_END() @@ -1641,7 +1645,7 @@ static int list(int argc, char **argv, struct command *cmd, struct plugin *plugi if (cfg.verbose) flags |= VERBOSE; - err = scan_subsystems(&t, NULL, 0); + err = scan_subsystems(&t, NULL, 0, cfg.device_dir); if (err) { fprintf(stderr, "Failed to scan namespaces\n"); return err; diff --git a/nvme.h b/nvme.h index 9a54bdb1..3fb10607 100644 --- a/nvme.h +++ b/nvme.h @@ -47,6 +47,7 @@ struct nvme_namespace { struct nvme_ctrl { char *name; + char *path; struct nvme_subsystem *subsys; char *address; @@ -105,7 +106,7 @@ int scan_subsys_filter(const struct dirent *d); int scan_dev_filter(const struct dirent *d); int scan_subsystems(struct nvme_topology *t, const char *subsysnqn, - __u32 ns_instance); + __u32 ns_instance, char *dev_dir); void free_topology(struct nvme_topology *t); char *get_nvme_subsnqn(char *path); char *nvme_get_ctrl_attr(char *path, const char *attr); -- 2.50.1