From a3b3e93286a30852b7973a524b47ef35d42aa6cf Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Wed, 28 Aug 2019 13:06:37 -0600 Subject: [PATCH] nvme-cli: Code reorg Move code lifted from 3rd party programs into new 'util' directory. Add new file to define all nvme specific scan filter funtions and relocate those functions to this file. Add new file consolidating all linux directory hierarchy knowledge and implement topology building functions there. Finally, fix all the broken nvme block-char relationships. Signed-off-by: Keith Busch --- Makefile | 24 +- fabrics.c | 53 +- nvme-filters.c | 88 +++ nvme-print.c | 75 ++- nvme-print.h | 3 +- nvme-topology.c | 704 ++++++++++++++++++++++++ nvme.c | 914 +------------------------------- nvme.h | 23 +- plugin.c | 2 +- plugins/huawei/huawei-nvme.c | 27 +- plugins/wdc/wdc-nvme.c | 1 + argconfig.c => util/argconfig.c | 0 argconfig.h => util/argconfig.h | 0 json.c => util/json.c | 0 json.h => util/json.h | 0 parser.c => util/parser.c | 0 parser.h => util/parser.h | 0 suffix.c => util/suffix.c | 0 suffix.h => util/suffix.h | 0 19 files changed, 974 insertions(+), 940 deletions(-) create mode 100644 nvme-filters.c create mode 100644 nvme-topology.c rename argconfig.c => util/argconfig.c (100%) rename argconfig.h => util/argconfig.h (100%) rename json.c => util/json.c (100%) rename json.h => util/json.h (100%) rename parser.c => util/parser.c (100%) rename parser.h => util/parser.h (100%) rename suffix.c => util/suffix.c (100%) rename suffix.h => util/suffix.h (100%) diff --git a/Makefile b/Makefile index ff525643..393f3e81 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,8 @@ ifeq ($(LIBUUID),0) override LIB_DEPENDS += uuid endif +INC=-Iutil + RPMBUILD = rpmbuild TAR = tar RM = rm -f @@ -35,9 +37,11 @@ override CFLAGS += -DNVME_VERSION='"$(NVME_VERSION)"' NVME_DPKG_VERSION=1~`lsb_release -sc` -OBJS := argconfig.o suffix.o parser.o nvme-print.o nvme-ioctl.o \ - nvme-lightnvm.o fabrics.o json.o nvme-models.o plugin.o \ - nvme-status.o +OBJS := nvme-print.o nvme-ioctl.o \ + nvme-lightnvm.o fabrics.o nvme-models.o plugin.o \ + nvme-status.o nvme-filters.o nvme-topology.o + +UTIL_OBJS := util/argconfig.o util/suffix.o util/json.o util/parser.o PLUGIN_OBJS := \ plugins/intel/intel-nvme.o \ @@ -53,17 +57,17 @@ PLUGIN_OBJS := \ plugins/virtium/virtium-nvme.o \ plugins/shannon/shannon-nvme.o -nvme: nvme.c nvme.h $(OBJS) $(PLUGIN_OBJS) NVME-VERSION-FILE - $(CC) $(CPPFLAGS) $(CFLAGS) nvme.c -o $(NVME) $(OBJS) $(PLUGIN_OBJS) $(LDFLAGS) +nvme: nvme.c nvme.h $(OBJS) $(PLUGIN_OBJS) $(UTIL_OBJS) NVME-VERSION-FILE + $(CC) $(CPPFLAGS) $(CFLAGS) $(INC) nvme.c -o $(NVME) $(OBJS) $(PLUGIN_OBJS) $(UTIL_OBJS) $(LDFLAGS) verify-no-dep: nvme.c nvme.h $(OBJS) NVME-VERSION-FILE $(CC) $(CPPFLAGS) $(CFLAGS) nvme.c -o $@ $(OBJS) $(LDFLAGS) -nvme.o: nvme.c nvme.h nvme-print.h nvme-ioctl.h argconfig.h suffix.h nvme-lightnvm.h fabrics.h - $(CC) $(CPPFLAGS) $(CFLAGS) -c $< +nvme.o: nvme.c nvme.h nvme-print.h nvme-ioctl.h util/argconfig.h util/suffix.h nvme-lightnvm.h fabrics.h + $(CC) $(CPPFLAGS) $(CFLAGS) $(INC) -c $< -%.o: %.c %.h nvme.h linux/nvme_ioctl.h nvme-ioctl.h nvme-print.h argconfig.h - $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $< +%.o: %.c %.h nvme.h linux/nvme_ioctl.h nvme-ioctl.h nvme-print.h util/argconfig.h + $(CC) $(CPPFLAGS) $(CFLAGS) $(INC) -o $@ -c $< doc: $(NVME) $(MAKE) -C Documentation @@ -74,7 +78,7 @@ test: all: doc clean: - $(RM) $(NVME) $(OBJS) $(PLUGIN_OBJS) *~ a.out NVME-VERSION-FILE *.tar* nvme.spec version control nvme-*.deb + $(RM) $(NVME) $(OBJS) $(notdir $(PLUGIN_OBJS) $(UTIL_OBJS)) *~ a.out NVME-VERSION-FILE *.tar* nvme.spec version control nvme-*.deb $(MAKE) -C Documentation clean $(RM) tests/*.pyc $(RM) verify-no-dep diff --git a/fabrics.c b/fabrics.c index 333669f8..a3c95d5f 100644 --- a/fabrics.c +++ b/fabrics.c @@ -33,13 +33,13 @@ #include #include -#include "parser.h" +#include "util/parser.h" #include "nvme-ioctl.h" #include "nvme-status.h" #include "fabrics.h" #include "nvme.h" -#include "argconfig.h" +#include "util/argconfig.h" #include "common.h" @@ -190,6 +190,55 @@ static const char *cms_str(__u8 cm) static int do_discover(char *argstr, bool connect); +/* + * parse strings with connect arguments to find a particular field. + * If field found, return string containing field value. If field + * not found, return an empty string. + */ +char *__parse_connect_arg(char *conargs, const char delim, const char *fieldnm) +{ + char *s, *e; + size_t cnt; + + /* + * There are field name overlaps: traddr and host_traddr. + * By chance, both connect arg strings are set up to + * have traddr field followed by host_traddr field. Thus field + * name matching doesn't overlap in the searches. Technically, + * as is, the loop and delimiter checking isn't necessary. + * However, better to be prepared. + */ + do { + s = strstr(conargs, fieldnm); + if (!s) + goto empty_field; + /* validate prior character is delimiter */ + if (s == conargs || *(s - 1) == delim) { + /* match requires next character to be assignment */ + s += strlen(fieldnm); + if (*s == '=') + /* match */ + break; + } + /* field overlap: seek to delimiter and keep looking */ + conargs = strchr(s, delim); + if (!conargs) + goto empty_field; + conargs++; /* skip delimiter */ + } while (1); + s++; /* skip assignment character */ + e = strchr(s, delim); + if (e) + cnt = e - s; + else + cnt = strlen(s); + + return strndup(s, cnt); + +empty_field: + return strdup("\0"); +} + static int ctrl_instance(char *device) { char d[64]; diff --git a/nvme-filters.c b/nvme-filters.c new file mode 100644 index 00000000..a4133f8f --- /dev/null +++ b/nvme-filters.c @@ -0,0 +1,88 @@ +#include +#include +#include + +#include "nvme.h" + +/* global, used for controller specific namespace filter */ +int current_index; + +int scan_namespace_filter(const struct dirent *d) +{ + int i, n; + + if (d->d_name[0] == '.') + return 0; + + if (strstr(d->d_name, "nvme")) + if (sscanf(d->d_name, "nvme%dn%d", &i, &n) == 2) + return 1; + return 0; +} + +int scan_ctrl_paths_filter(const struct dirent *d) +{ + int id, cntlid, nsid; + + if (d->d_name[0] == '.') + return 0; + + if (strstr(d->d_name, "nvme")) { + if (sscanf(d->d_name, "nvme%dc%dn%d", &id, &cntlid, &nsid) == 3) + return 1; + if (sscanf(d->d_name, "nvme%dn%d", &id, &nsid) == 2) + return 1; + } + + return 0; +} + +int scan_ctrls_filter(const struct dirent *d) +{ + int id, nsid; + + if (d->d_name[0] == '.') + return 0; + + if (strstr(d->d_name, "nvme")) { + if (sscanf(d->d_name, "nvme%dn%d", &id, &nsid) == 2) + return 0; + if (sscanf(d->d_name, "nvme%dn", &id) == 1) + return 1; + return 0; + } + + return 0; +} + +int scan_subsys_filter(const struct dirent *d) +{ + int id; + + if (d->d_name[0] == '.') + return 0; + + if (strstr(d->d_name, "nvme-subsys")) { + if (sscanf(d->d_name, "nvme-subsys%d", &id) != 1) + return 0; + return 1; + } + + return 0; +} + +int scan_dev_filter(const struct dirent *d) +{ + int ctrl, ns, part; + + if (d->d_name[0] == '.') + return 0; + + if (strstr(d->d_name, "nvme")) { + if (sscanf(d->d_name, "nvme%dn%dp%d", &ctrl, &ns, &part) == 3) + return 0; + if (sscanf(d->d_name, "nvme%dn%d", &ctrl, &ns) == 2) + return ctrl == current_index; + } + return 0; +} diff --git a/nvme-print.c b/nvme-print.c index a8cb1c74..3fec6552 100644 --- a/nvme-print.c +++ b/nvme-print.c @@ -5,9 +5,9 @@ #include #include "nvme-print.h" -#include "json.h" +#include "util/json.h" #include "nvme-models.h" -#include "suffix.h" +#include "util/suffix.h" #include "common.h" static const uint8_t zero_uuid[16] = { @@ -3950,3 +3950,74 @@ void show_single_property(int offset, uint64_t value64, int human) break; } } + +void show_relatives(const char *name) +{ + unsigned id, i, nsid = NVME_NSID_ALL; + char *path = NULL; + bool block = true; + int ret; + + ret = sscanf(name, "nvme%dn%d", &id, &nsid); + switch (ret) { + case 1: + asprintf(&path, "/sys/class/nvme/%s", name); + block = false; + break; + case 2: + asprintf(&path, "/sys/block/%s/device", name); + break; + default: + return; + } + if (!path) + return; + + if (block) { + char *subsysnqn; + struct subsys_list_item *slist; + int subcnt = 0; + + subsysnqn = get_nvme_subsnqn(path); + if (!subsysnqn) + return; + slist = get_subsys_list(&subcnt, subsysnqn, nsid); + if (subcnt != 1) { + free(subsysnqn); + free(path); + return; + } + + fprintf(stderr, "Namespace %s has parent controller(s):", name); + for (i = 0; i < slist[0].nctrls; i++) + fprintf(stderr, "%s%s", i ? ", " : "", slist[0].ctrls[i].name); + fprintf(stderr, "\n\n"); + free(subsysnqn); + } else { + struct dirent **paths; + bool comma = false; + int n, ns, cntlid; + + n = scandir(path, &paths, scan_ctrl_paths_filter, alphasort); + if (n < 0) { + free(path); + return; + } + + fprintf(stderr, "Controller %s has child namespace(s):", name); + for (i = 0; i < n; i++) { + if (sscanf(paths[i]->d_name, "nvme%dc%dn%d", + &id, &cntlid, &ns) != 3) { + if (sscanf(paths[i]->d_name, "nvme%dn%d", + &id, &ns) != 2) { + continue; + } + } + fprintf(stderr, "%snvme%dn%d", comma ? ", " : "", id, ns); + comma = true; + } + fprintf(stderr, "\n\n"); + free(paths); + } + free(path); +} diff --git a/nvme-print.h b/nvme-print.h index daccb0b6..980b80a8 100644 --- a/nvme-print.h +++ b/nvme-print.h @@ -2,7 +2,7 @@ #define NVME_PRINT_H #include "nvme.h" -#include "json.h" +#include "util/json.h" #include enum { @@ -51,6 +51,7 @@ const char *nvme_select_to_string(int sel); const char *nvme_feature_to_string(int feature); const char *nvme_register_to_string(int reg); void nvme_show_select_result(__u32 result); +void show_relatives(const char *name); void json_nvme_id_ctrl(struct nvme_id_ctrl *ctrl, unsigned int mode, void (*vendor_show)(__u8 *vs, struct json_object *root)); void json_nvme_id_ns(struct nvme_id_ns *ns, unsigned int flags); diff --git a/nvme-topology.c b/nvme-topology.c new file mode 100644 index 00000000..225d908d --- /dev/null +++ b/nvme-topology.c @@ -0,0 +1,704 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "nvme.h" +#include "nvme-ioctl.h" + +static const char delim_space = ' '; + +int get_nsid(int fd) +{ + int nsid = nvme_get_nsid(fd); + + if (nsid <= 0) { + fprintf(stderr, + "%s: failed to return namespace id\n", + devicename); + } + return nsid < 0 ? 0 : nsid; +} + +char *get_nvme_subsnqn(char *path) +{ + char sspath[320]; + char *subsysnqn; + int fd; + int ret; + + snprintf(sspath, sizeof(sspath), "%s/subsysnqn", path); + + fd = open(sspath, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Failed to open %s: %s\n", + sspath, strerror(errno)); + return NULL; + } + + subsysnqn = calloc(1, 256); + if (!subsysnqn) + goto close_fd; + + ret = read(fd, subsysnqn, 256); + if (ret < 0) { + fprintf(stderr, "Failed to read %s: %s\n", sspath, + strerror(errno)); + free(subsysnqn); + subsysnqn = NULL; + } else if (subsysnqn[strlen(subsysnqn) - 1] == '\n') { + subsysnqn[strlen(subsysnqn) - 1] = '\0'; + } + +close_fd: + close(fd); + + return subsysnqn; +} + +char *get_nvme_ctrl_attr(char *path, const char *attr) +{ + char *attrpath; + char *value; + int fd; + ssize_t ret; + int i; + + ret = asprintf(&attrpath, "%s/%s", path, attr); + if (ret < 0) + return NULL; + + value = calloc(1, 1024); + if (!value) + goto err_free_path; + + fd = open(attrpath, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Failed to open %s: %s\n", + attrpath, strerror(errno)); + goto err_free_value; + } + + ret = read(fd, value, 1024); + if (ret < 0) { + fprintf(stderr, "read :%s :%s\n", attrpath, strerror(errno)); + goto err_close_fd; + } + + if (value[strlen(value) - 1] == '\n') + value[strlen(value) - 1] = '\0'; + + for (i = 0; i < strlen(value); i++) { + if (value[i] == ',' ) + value[i] = ' '; + } + + close(fd); + free(attrpath); + + return value; + +err_close_fd: + close(fd); +err_free_value: + free(value); +err_free_path: + free(attrpath); + + return NULL; +} + +static int scan_namespace(struct nvme_namespace *n) +{ + int ret, fd; + char *path; + + ret = asprintf(&path, "%s%s", dev, n->name); + if (ret < 0) + return ret; + + fd = open(path, O_RDONLY); + if (fd < 0) + goto free; + + n->nsid = get_nsid(fd); + ret = nvme_identify_ns(fd, n->nsid, 0, &n->ns); + if (ret < 0) + goto close_fd; +close_fd: + close(fd); +free: + free(path); + return 0; +} + +static int scan_ctrl(struct nvme_ctrl *c, char *p) +{ + struct nvme_namespace *n; + struct dirent **ns; + char *path; + int i, fd, ret; + + ret = asprintf(&path, "%s/%s", p, c->name); + if (ret < 0) + return ret; + + c->address = get_nvme_ctrl_attr(path, "address"); + c->transport = get_nvme_ctrl_attr(path, "transport"); + c->state = get_nvme_ctrl_attr(path, "state"); + + c->nr_namespaces = scandir(path, &ns, scan_namespace_filter, alphasort); + c->namespaces = calloc(c->nr_namespaces, sizeof(*n)); + for (i = 0; i < c->nr_namespaces; i++) { + n = &c->namespaces[i]; + n->name = strdup(ns[i]->d_name); + n->ctrl = c; + scan_namespace(n); + } + + while (i--) + free(ns[i]); + free(ns); + free(path); + + ret = asprintf(&path, "%s%s", dev, c->name); + if (ret < 0) + return ret; + + fd = open(path, O_RDONLY); + if (fd < 0) + goto free; + + ret = nvme_identify_ctrl(fd, &c->id); + if (ret < 0) + goto close_fd; +close_fd: + close(fd); +free: + free(path); + return 0; +} + +static int scan_subsystem(struct nvme_subsystem *s) +{ + struct dirent **ctrls, **ns; + struct nvme_namespace *n; + struct nvme_ctrl *c; + int i, ret; + char *path; + + ret = asprintf(&path, "%s%s", subsys_dir, s->name); + if (ret < 0) + return ret; + + s->subsysnqn = get_nvme_subsnqn(path); + s->nr_ctrls = scandir(path, &ctrls, scan_ctrls_filter, alphasort); + s->ctrls = calloc(s->nr_ctrls, sizeof(*c)); + for (i = 0; i < s->nr_ctrls; i++) { + c = &s->ctrls[i]; + c->name = strdup(ctrls[i]->d_name); + c->subsys = s; + scan_ctrl(c, path); + } + + while (i--) + free(ctrls[i]); + free(ctrls); + + s->nr_namespaces = scandir(path, &ns, scan_namespace_filter, alphasort); + s->namespaces = calloc(s->nr_namespaces, sizeof(*n)); + for (i = 0; i < s->nr_namespaces; i++) { + n = &s->namespaces[i]; + n->name = strdup(ns[i]->d_name); + n->ctrl = &s->ctrls[0]; + scan_namespace(n); + } + while (i--) + free(ns[i]); + free(ns); + + free(path); + return 0; +} + +static int verify_legacy_ns(struct nvme_namespace *n) +{ + struct nvme_ctrl *c = n->ctrl; + struct nvme_id_ctrl id; + char *path; + int ret, fd; + + ret = asprintf(&path, "%s%s", dev, n->name); + if (ret < 0) + return ret; + + fd = open(path, O_RDONLY); + free (path); + + if (fd < 0) + return fd; + + ret = nvme_identify_ctrl(fd, &id); + close(fd); + + if (ret) + return ret; + + if (memcmp(id.mn, c->id.mn, sizeof(id.mn)) || + memcmp(id.sn, c->id.mn, sizeof(id.sn))) + return -ENODEV; + return 0; +} + +/* + * For pre-subsystem enabled kernel. Topology information is limited, but we can + * assume controller names are always a prefix to their namespaces, i.e. nvme0 + * 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) +{ + struct nvme_ctrl *c; + struct nvme_subsystem *s; + struct nvme_namespace *n; + struct dirent **devices, **namespaces; + int ret = 0, fd, i; + char *path; + + t->nr_subsystems = scandir(dev, &devices, scan_ctrls_filter, alphasort); + if (t->nr_subsystems < 0) { + fprintf(stderr, "no NVMe device(s) detected.\n"); + return t->nr_subsystems; + } + + t->subsystems = calloc(t->nr_subsystems, sizeof(*s)); + for (i = 0; i < t->nr_subsystems; i++) { + int j; + + s = &t->subsystems[i]; + s->nr_ctrls = 1; + s->ctrls = calloc(s->nr_ctrls, sizeof(*c)); + s->name = strdup(devices[i]->d_name); + s->subsysnqn = strdup(s->name); + s->nr_namespaces = 0; + + c = s->ctrls; + c->name = strdup(s->name); + sscanf(c->name, "nvme%d", ¤t_index); + c->nr_namespaces = scandir(dev, &namespaces, scan_dev_filter, + alphasort); + c->namespaces = calloc(c->nr_namespaces, sizeof(*n)); + + for (j = 0; j < c->nr_namespaces; j++) { + n = &c->namespaces[j]; + n->name = strdup(namespaces[j]->d_name); + n->ctrl = c; + scan_namespace(n); + ret = verify_legacy_ns(n); + if (ret) + goto free; + } + while (j--) + free(namespaces[j]); + free(namespaces); + + ret = asprintf(&path, "%s%s", dev, c->name); + if (ret < 0) + continue; + ret = 0; + + fd = open(path, O_RDONLY); + if (fd > 0) { + nvme_identify_ctrl(fd, &c->id); + close(fd); + } + free(path); + } + +free: + while (i--) + free(devices[i]); + free(devices); + return ret; +} + +int scan_subsystems(struct nvme_topology *t) +{ + struct nvme_subsystem *s; + struct dirent **subsys; + int i; + + t->nr_subsystems = scandir(subsys_dir, &subsys, scan_subsys_filter, alphasort); + if (t->nr_subsystems < 0) + return legacy_list(t); + + t->subsystems = calloc(t->nr_subsystems, sizeof(*s)); + for (i = 0; i < t->nr_subsystems; i++) { + s = &t->subsystems[i]; + s->name = strdup(subsys[i]->d_name); + scan_subsystem(s); + } + + while (i--) + free(subsys[i]); + free(subsys); + return 0; +} + +void free_topology(struct nvme_topology *t) +{ + int i, j, k; + + for (i = 0; i < t->nr_subsystems; i++) { + struct nvme_subsystem *s = &t->subsystems[i]; + + for (j = 0; j < s->nr_ctrls; j++) { + struct nvme_ctrl *c = &s->ctrls[j]; + + for (k = 0; k < c->nr_namespaces; k++) { + struct nvme_namespace *n = &c->namespaces[k]; + free(n->name); + } + free(c->name); + if (c->transport) + free(c->transport); + if (c->address) + free(c->address); + if (c->state) + free(c->state); + if (c->namespaces) + free(c->namespaces); + } + free(s->name); + free(s->subsysnqn); + free(s->ctrls); + free(s->namespaces); + } + free(t->subsystems); +} + +static char *get_nvme_ctrl_path_ana_state(char *path, int nsid) +{ + struct dirent **paths; + char *ana_state; + int i, n; + + ana_state = calloc(1, 16); + if (!ana_state) + return NULL; + + n = scandir(path, &paths, scan_ctrl_paths_filter, alphasort); + if (n <= 0) { + free(ana_state); + return NULL; + } + for (i = 0; i < n; i++) { + int id, cntlid, ns, fd; + ssize_t ret; + char *ctrl_path; + + if (sscanf(paths[i]->d_name, "nvme%dc%dn%d", + &id, &cntlid, &ns) != 3) { + if (sscanf(paths[i]->d_name, "nvme%dn%d", + &id, &ns) != 2) { + continue; + } + } + if (ns != nsid) + continue; + + ret = asprintf(&ctrl_path, "%s/%s/ana_state", + path, paths[i]->d_name); + if (ret < 0) { + free(ana_state); + ana_state = NULL; + break; + } + fd = open(ctrl_path, O_RDONLY); + if (fd < 0) { + free(ctrl_path); + free(ana_state); + ana_state = NULL; + break; + } + ret = read(fd, ana_state, 16); + if (ret < 0) { + fprintf(stderr, "Failed to read ANA state from %s\n", + ctrl_path); + free(ana_state); + ana_state = NULL; + } else if (ana_state[strlen(ana_state) - 1] == '\n') + ana_state[strlen(ana_state) - 1] = '\0'; + close(fd); + free(ctrl_path); + break; + } + for (i = 0; i < n; i++) + free(paths[i]); + free(paths); + return ana_state; +} + +void free_ctrl_list_item(struct ctrl_list_item *ctrls) +{ + free(ctrls->name); + free(ctrls->transport); + free(ctrls->address); + free(ctrls->state); + free(ctrls->ana_state); + free(ctrls->subsysnqn); + free(ctrls->traddr); + free(ctrls->trsvcid); + free(ctrls->host_traddr); +} + +int get_nvme_ctrl_info(char *name, char *path, struct ctrl_list_item *ctrl, + __u32 nsid) +{ + char ctrl_path[512]; + + ctrl->name = strdup(name); + + snprintf(ctrl_path, sizeof(ctrl_path), "%s/%s", path, ctrl->name); + + ctrl->address = get_nvme_ctrl_attr(ctrl_path, "address"); + if (!ctrl->address) { + fprintf(stderr, "%s: failed to get controller address.\n", + ctrl->name); + goto free_ctrl_items; + } + + ctrl->transport = get_nvme_ctrl_attr(ctrl_path, "transport"); + if (!ctrl->transport) { + fprintf(stderr, "%s: failed to get controller transport.\n", + ctrl->name); + goto free_ctrl_items; + } + + ctrl->state = get_nvme_ctrl_attr(ctrl_path, "state"); + if (!ctrl->state) { + fprintf(stderr, "%s: failed to get controller state.\n", + ctrl->name); + goto free_ctrl_items; + } + + if (nsid != NVME_NSID_ALL) + ctrl->ana_state = get_nvme_ctrl_path_ana_state(ctrl_path, nsid); + + ctrl->subsysnqn = get_nvme_ctrl_attr(ctrl_path, "subsysnqn"); + if (!ctrl->subsysnqn) { + fprintf(stderr, "%s: failed to get controller subsysnqn.\n", + ctrl->name); + goto free_ctrl_items; + } + + ctrl->traddr = __parse_connect_arg(ctrl->address, delim_space, + conarg_traddr); + ctrl->trsvcid = __parse_connect_arg(ctrl->address, delim_space, + conarg_trsvcid); + ctrl->host_traddr = __parse_connect_arg(ctrl->address, delim_space, + conarg_host_traddr); + + return 0; /* success */ + +free_ctrl_items: + free_ctrl_list_item(ctrl); + + return 1; /* failure */ +} + +static int get_nvme_subsystem_info(char *name, char *path, + struct subsys_list_item *item, __u32 nsid) +{ + struct dirent **ctrls; + int n, i, ret = 1, ccnt = 0; + + item->subsysnqn = get_nvme_subsnqn(path); + if (!item->subsysnqn) { + fprintf(stderr, "failed to get subsystem nqn.\n"); + return ret; + } + + item->name = strdup(name); + + n = scandir(path, &ctrls, scan_ctrls_filter, alphasort); + if (n < 0) { + fprintf(stderr, "failed to scan controller(s).\n"); + return ret; + } + + item->ctrls = calloc(n, sizeof(struct ctrl_list_item)); + if (!item->ctrls) { + fprintf(stderr, "failed to allocate subsystem controller(s)\n"); + goto free_ctrls; + } + + item->nctrls = n; + + for (i = 0; i < n; i++) { + if (get_nvme_ctrl_info(ctrls[i]->d_name, path, + &item->ctrls[ccnt], nsid)) { + fprintf(stderr, "failed to get controller[%d] info.\n", + i); + } + ccnt++; + } + + item->nctrls = ccnt; + + ret = 0; + +free_ctrls: + for (i = 0; i < n; i++) + free(ctrls[i]); + free(ctrls); + + return ret; + +} + +static void free_subsys_list_item(struct subsys_list_item *item) +{ + int i; + + for (i = 0; i < item->nctrls; i++) + free_ctrl_list_item(&item->ctrls[i]); + + free(item->ctrls); + free(item->subsysnqn); + free(item->name); +} + +void free_subsys_list(struct subsys_list_item *slist, int n) +{ + int i; + + for (i = 0; i < n; i++) + free_subsys_list_item(&slist[i]); + + free(slist); +} + +struct subsys_list_item *get_subsys_list(int *subcnt, char *subsysnqn, + __u32 nsid) +{ + char path[310]; + struct dirent **subsys; + struct subsys_list_item *slist; + int n, i, ret = 0; + + n = scandir(subsys_dir, &subsys, scan_subsys_filter, alphasort); + if (n < 0) { + fprintf(stderr, "no NVMe subsystem(s) detected.\n"); + return NULL; + } + + slist = calloc(n, sizeof(struct subsys_list_item)); + if (!slist) + goto free_subsys; + + for (i = 0; i < n; i++) { + snprintf(path, sizeof(path), "%s%s", subsys_dir, + subsys[i]->d_name); + ret = get_nvme_subsystem_info(subsys[i]->d_name, path, + &slist[*subcnt], nsid); + if (ret) { + fprintf(stderr, + "%s: failed to get subsystem info: %s\n", + path, strerror(errno)); + free_subsys_list_item(&slist[*subcnt]); + } else if (subsysnqn && + strncmp(slist[*subcnt].subsysnqn, subsysnqn, 255)) + free_subsys_list_item(&slist[*subcnt]); + else + (*subcnt)++; + } + +free_subsys: + for (i = 0; i < n; i++) + free(subsys[i]); + free(subsys); + + return slist; +} + +char *nvme_char_from_block(char *dev) +{ + char *path = NULL; + char buf[256] = {0}; + int ret, id, nsid; + + ret = sscanf(dev, "nvme%dn%d", &id, &nsid); + switch (ret) { + case 1: + return strdup(dev); + break; + case 2: + asprintf(&path, "/sys/block/%s/device", dev); + break; + default: + fprintf(stderr, "%s is not an nvme device\n", dev); + return NULL; + } + + if (!path) + return NULL; + + ret = readlink(path, buf, sizeof(buf)); + if (ret > 0) { + char *r = strdup(basename(buf)); + + free(path); + if (sscanf(r, "nvme%d", &id) != 1) { + fprintf(stderr, "%s is not a physical nvme controller\n", r); + free(r); + r = NULL; + } + return r; + } + + free(path); + ret = asprintf(&path, "nvme%d", id); + if (ret < 0) + return NULL; + return path; +} + +void *mmap_registers(const char *dev) +{ + int fd; + char *base, path[512]; + void *membase; + + base = nvme_char_from_block((char *)dev); + if (!base) + return NULL; + + sprintf(path, "/sys/class/nvme/%s/device/resource0", base); + fd = open(path, O_RDONLY); + if (fd < 0) { + sprintf(path, "/sys/class/misc/%s/device/resource0", base); + fd = open(path, O_RDONLY); + } + if (fd < 0) { + fprintf(stderr, "%s did not find a pci resource, open failed %s\n", + base, strerror(errno)); + free(base); + return NULL; + } + + membase = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd, 0); + if (membase == MAP_FAILED) { + fprintf(stderr, "%s failed to map. ", base); + fprintf(stderr, "Did your kernel enable CONFIG_IO_STRICT_DEVMEM?\n"); + membase = NULL; + } + + free(base); + close(fd); + return membase; +} + diff --git a/nvme.c b/nvme.c index 3990a083..9968cfc0 100644 --- a/nvme.c +++ b/nvme.c @@ -53,7 +53,6 @@ #include "plugin.h" #include "argconfig.h" - #include "fabrics.h" static struct stat nvme_stat; @@ -61,7 +60,6 @@ const char *devicename; static const char nvme_version_string[] = NVME_VERSION; - #define CREATE_CMD #include "nvme-builtin.h" @@ -83,24 +81,16 @@ static struct program nvme = { .extensions = &builtin, }; -static const char *dev = "/dev/"; -static const char *subsys_dir = "/sys/class/nvme-subsystem/"; + static const char *output_format = "Output format: normal|json|binary"; -static const char delim_space = ' '; const char *conarg_nqn = "nqn"; const char *conarg_transport = "transport"; const char *conarg_traddr = "traddr"; const char *conarg_trsvcid = "trsvcid"; const char *conarg_host_traddr = "host_traddr"; - -static unsigned long long elapsed_utime(struct timeval start_time, - struct timeval end_time) -{ - unsigned long long ret = (end_time.tv_sec - start_time.tv_sec) * 1000000 + - (end_time.tv_usec - start_time.tv_usec); - return ret; -} +const char *dev = "/dev/"; +const char *subsys_dir = "/sys/class/nvme-subsystem/"; static int open_dev(char *dev) { @@ -1047,18 +1037,6 @@ ret: return nvme_status_to_errno(err, false); } -static int get_nsid(int fd) -{ - int nsid = nvme_get_nsid(fd); - - if (nsid <= 0) { - fprintf(stderr, - "%s: failed to return namespace id\n", - devicename); - } - return nsid < 0 ? 0 : nsid; -} - static int delete_ns(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Delete the given namespace by "\ @@ -1157,15 +1135,13 @@ static int nvme_attach_ns(int argc, char **argv, int attach, const char *desc, s goto close_fd; } - num = argconfig_parse_comma_sep_array(cfg.cntlist, - list, 2047); - - if (num == -1) { + num = argconfig_parse_comma_sep_array(cfg.cntlist, list, 2047); + if (num == -1) { fprintf(stderr, "%s: controller id list is required\n", cmd->name); err = -EINVAL; goto close_fd; - } + } for (i = 0; i < num; i++) ctrlist[i] = (uint16_t)list[i]; @@ -1318,508 +1294,6 @@ ret: return nvme_status_to_errno(err, false); } -char *nvme_char_from_block(char *block) -{ - char slen[16]; - unsigned len; - - if (strncmp("nvme", block, 4)) { - fprintf(stderr, "Device %s is not a nvme device.", block); - return NULL; - } - - sscanf(block, "nvme%d", &len); - sprintf(slen, "%d", len); - block[4 + strlen(slen)] = 0; - - return block; -} - -static void *get_registers(void) -{ - int fd; - char *base, path[512]; - void *membase; - - base = nvme_char_from_block((char *)devicename); - sprintf(path, "/sys/class/nvme/%s/device/resource0", base); - fd = open(path, O_RDONLY); - if (fd < 0) { - sprintf(path, "/sys/class/misc/%s/device/resource0", base); - fd = open(path, O_RDONLY); - } - if (fd < 0) { - fprintf(stderr, "%s did not find a pci resource, open failed %s\n", - base, strerror(errno)); - return NULL; - } - - membase = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd, 0); - if (membase == MAP_FAILED) { - fprintf(stderr, "%s failed to map\n", base); - membase = NULL; - } - - close(fd); - return membase; -} - -static char *get_nvme_subsnqn(char *path) -{ - char sspath[320]; - char *subsysnqn; - int fd; - int ret; - - snprintf(sspath, sizeof(sspath), "%s/subsysnqn", path); - - fd = open(sspath, O_RDONLY); - if (fd < 0) { - fprintf(stderr, "Failed to open %s: %s\n", - sspath, strerror(errno)); - return NULL; - } - - subsysnqn = calloc(1, 256); - if (!subsysnqn) - goto close_fd; - - ret = read(fd, subsysnqn, 256); - if (ret < 0) { - fprintf(stderr, "Failed to read %s: %s\n", sspath, - strerror(errno)); - free(subsysnqn); - subsysnqn = NULL; - } else if (subsysnqn[strlen(subsysnqn) - 1] == '\n') { - subsysnqn[strlen(subsysnqn) - 1] = '\0'; - } - -close_fd: - close(fd); - - return subsysnqn; -} - -static char *get_nvme_ctrl_attr(char *path, const char *attr) -{ - char *attrpath; - char *value; - int fd; - ssize_t ret; - int i; - - ret = asprintf(&attrpath, "%s/%s", path, attr); - if (ret < 0) - return NULL; - - value = calloc(1, 1024); - if (!value) - goto err_free_path; - - fd = open(attrpath, O_RDONLY); - if (fd < 0) { - fprintf(stderr, "Failed to open %s: %s\n", - attrpath, strerror(errno)); - goto err_free_value; - } - - ret = read(fd, value, 1024); - if (ret < 0) { - fprintf(stderr, "read :%s :%s\n", attrpath, strerror(errno)); - goto err_close_fd; - } - - if (value[strlen(value) - 1] == '\n') - value[strlen(value) - 1] = '\0'; - - for (i = 0; i < strlen(value); i++) { - if (value[i] == ',' ) - value[i] = ' '; - } - - close(fd); - free(attrpath); - - return value; - -err_close_fd: - close(fd); -err_free_value: - free(value); -err_free_path: - free(attrpath); - - return NULL; -} - -static int scan_namespace_filter(const struct dirent *d) -{ - int i, n; - - if (d->d_name[0] == '.') - return 0; - - if (strstr(d->d_name, "nvme")) - if (sscanf(d->d_name, "nvme%dn%d", &i, &n) == 2) - return 1; - return 0; -} - -static int scan_ctrl_paths_filter(const struct dirent *d) -{ - int id, cntlid, nsid; - - if (d->d_name[0] == '.') - return 0; - - if (strstr(d->d_name, "nvme")) { - if (sscanf(d->d_name, "nvme%dc%dn%d", &id, &cntlid, &nsid) == 3) - return 1; - if (sscanf(d->d_name, "nvme%dn%d", &id, &nsid) == 2) - return 1; - } - - return 0; -} - -static char *get_nvme_ctrl_path_ana_state(char *path, int nsid) -{ - struct dirent **paths; - char *ana_state; - int i, n; - - ana_state = calloc(1, 16); - if (!ana_state) - return NULL; - - n = scandir(path, &paths, scan_ctrl_paths_filter, alphasort); - if (n <= 0) { - free(ana_state); - return NULL; - } - for (i = 0; i < n; i++) { - int id, cntlid, ns, fd; - ssize_t ret; - char *ctrl_path; - - if (sscanf(paths[i]->d_name, "nvme%dc%dn%d", - &id, &cntlid, &ns) != 3) { - if (sscanf(paths[i]->d_name, "nvme%dn%d", - &id, &ns) != 2) { - continue; - } - } - if (ns != nsid) - continue; - - ret = asprintf(&ctrl_path, "%s/%s/ana_state", - path, paths[i]->d_name); - if (ret < 0) { - free(ana_state); - ana_state = NULL; - break; - } - fd = open(ctrl_path, O_RDONLY); - if (fd < 0) { - free(ctrl_path); - free(ana_state); - ana_state = NULL; - break; - } - ret = read(fd, ana_state, 16); - if (ret < 0) { - fprintf(stderr, "Failed to read ANA state from %s\n", - ctrl_path); - free(ana_state); - ana_state = NULL; - } else if (ana_state[strlen(ana_state) - 1] == '\n') - ana_state[strlen(ana_state) - 1] = '\0'; - close(fd); - free(ctrl_path); - break; - } - for (i = 0; i < n; i++) - free(paths[i]); - free(paths); - return ana_state; -} - -static int scan_ctrls_filter(const struct dirent *d) -{ - int id, nsid; - - if (d->d_name[0] == '.') - return 0; - - if (strstr(d->d_name, "nvme")) { - if (sscanf(d->d_name, "nvme%dn%d", &id, &nsid) == 2) - return 0; - if (sscanf(d->d_name, "nvme%dn", &id) == 1) - return 1; - return 0; - } - - return 0; -} - -static void free_ctrl_list_item(struct ctrl_list_item *ctrls) -{ - free(ctrls->name); - free(ctrls->transport); - free(ctrls->address); - free(ctrls->state); - free(ctrls->ana_state); - free(ctrls->subsysnqn); - free(ctrls->traddr); - free(ctrls->trsvcid); - free(ctrls->host_traddr); -} - -/* - * parse strings with connect arguments to find a particular field. - * If field found, return string containing field value. If field - * not found, return an empty string. - */ -char *__parse_connect_arg(char *conargs, const char delim, const char *fieldnm) -{ - char *s, *e; - size_t cnt; - - /* - * There are field name overlaps: traddr and host_traddr. - * By chance, both connect arg strings are set up to - * have traddr field followed by host_traddr field. Thus field - * name matching doesn't overlap in the searches. Technically, - * as is, the loop and delimiter checking isn't necessary. - * However, better to be prepared. - */ - do { - s = strstr(conargs, fieldnm); - if (!s) - goto empty_field; - /* validate prior character is delimiter */ - if (s == conargs || *(s - 1) == delim) { - /* match requires next character to be assignment */ - s += strlen(fieldnm); - if (*s == '=') - /* match */ - break; - } - /* field overlap: seek to delimiter and keep looking */ - conargs = strchr(s, delim); - if (!conargs) - goto empty_field; - conargs++; /* skip delimiter */ - } while (1); - s++; /* skip assignment character */ - e = strchr(s, delim); - if (e) - cnt = e - s; - else - cnt = strlen(s); - - return strndup(s, cnt); - -empty_field: - return strdup("\0"); -} - -static int get_nvme_ctrl_info(char *name, char *path, - struct ctrl_list_item *ctrl, __u32 nsid) -{ - char ctrl_path[512]; - - ctrl->name = strdup(name); - - snprintf(ctrl_path, sizeof(ctrl_path), "%s/%s", path, ctrl->name); - - ctrl->address = get_nvme_ctrl_attr(ctrl_path, "address"); - if (!ctrl->address) { - fprintf(stderr, "%s: failed to get controller address.\n", - ctrl->name); - goto free_ctrl_items; - } - - ctrl->transport = get_nvme_ctrl_attr(ctrl_path, "transport"); - if (!ctrl->transport) { - fprintf(stderr, "%s: failed to get controller transport.\n", - ctrl->name); - goto free_ctrl_items; - } - - ctrl->state = get_nvme_ctrl_attr(ctrl_path, "state"); - if (!ctrl->state) { - fprintf(stderr, "%s: failed to get controller state.\n", - ctrl->name); - goto free_ctrl_items; - } - - if (nsid != NVME_NSID_ALL) - ctrl->ana_state = get_nvme_ctrl_path_ana_state(ctrl_path, nsid); - - ctrl->subsysnqn = get_nvme_ctrl_attr(ctrl_path, "subsysnqn"); - if (!ctrl->subsysnqn) { - fprintf(stderr, "%s: failed to get controller subsysnqn.\n", - ctrl->name); - goto free_ctrl_items; - } - - ctrl->traddr = __parse_connect_arg(ctrl->address, delim_space, - conarg_traddr); - ctrl->trsvcid = __parse_connect_arg(ctrl->address, delim_space, - conarg_trsvcid); - ctrl->host_traddr = __parse_connect_arg(ctrl->address, delim_space, - conarg_host_traddr); - - return 0; /* success */ - -free_ctrl_items: - free_ctrl_list_item(ctrl); - - return 1; /* failure */ -} - -static int get_nvme_subsystem_info(char *name, char *path, - struct subsys_list_item *item, __u32 nsid) -{ - struct dirent **ctrls; - int n, i, ret = 1, ccnt = 0; - - item->subsysnqn = get_nvme_subsnqn(path); - if (!item->subsysnqn) { - fprintf(stderr, "failed to get subsystem nqn.\n"); - return ret; - } - - item->name = strdup(name); - - n = scandir(path, &ctrls, scan_ctrls_filter, alphasort); - if (n < 0) { - fprintf(stderr, "failed to scan controller(s).\n"); - return ret; - } - - item->ctrls = calloc(n, sizeof(struct ctrl_list_item)); - if (!item->ctrls) { - fprintf(stderr, "failed to allocate subsystem controller(s)\n"); - goto free_ctrls; - } - - item->nctrls = n; - - for (i = 0; i < n; i++) { - if (get_nvme_ctrl_info(ctrls[i]->d_name, path, - &item->ctrls[ccnt], nsid)) { - fprintf(stderr, "failed to get controller[%d] info.\n", - i); - } - ccnt++; - } - - item->nctrls = ccnt; - - ret = 0; - -free_ctrls: - for (i = 0; i < n; i++) - free(ctrls[i]); - free(ctrls); - - return ret; - -} - -static int scan_subsys_filter(const struct dirent *d) -{ - char path[310]; - struct stat ss; - int id; - int tmp; - - if (d->d_name[0] == '.') - return 0; - - /* sanity checking, probably unneeded */ - if (strstr(d->d_name, "nvme-subsys")) { - snprintf(path, sizeof(path), "%s%s", subsys_dir, d->d_name); - if (stat(path, &ss)) - return 0; - if (!S_ISDIR(ss.st_mode)) - return 0; - tmp = sscanf(d->d_name, "nvme-subsys%d", &id); - if (tmp != 1) - return 0; - return 1; - } - - return 0; -} - -static void free_subsys_list_item(struct subsys_list_item *item) -{ - int i; - - for (i = 0; i < item->nctrls; i++) - free_ctrl_list_item(&item->ctrls[i]); - - free(item->ctrls); - free(item->subsysnqn); - free(item->name); -} - -void free_subsys_list(struct subsys_list_item *slist, int n) -{ - int i; - - for (i = 0; i < n; i++) - free_subsys_list_item(&slist[i]); - - free(slist); -} - -struct subsys_list_item *get_subsys_list(int *subcnt, char *subsysnqn, - __u32 nsid) -{ - char path[310]; - struct dirent **subsys; - struct subsys_list_item *slist; - int n, i, ret = 0; - - n = scandir(subsys_dir, &subsys, scan_subsys_filter, alphasort); - if (n < 0) { - fprintf(stderr, "no NVMe subsystem(s) detected.\n"); - return NULL; - } - - slist = calloc(n, sizeof(struct subsys_list_item)); - if (!slist) - goto free_subsys; - - for (i = 0; i < n; i++) { - snprintf(path, sizeof(path), "%s%s", subsys_dir, - subsys[i]->d_name); - ret = get_nvme_subsystem_info(subsys[i]->d_name, path, - &slist[*subcnt], nsid); - if (ret) { - fprintf(stderr, - "%s: failed to get subsystem info: %s\n", - path, strerror(errno)); - free_subsys_list_item(&slist[*subcnt]); - } else if (subsysnqn && - strncmp(slist[*subcnt].subsysnqn, subsysnqn, 255)) - free_subsys_list_item(&slist[*subcnt]); - else - (*subcnt)++; - } - -free_subsys: - for (i = 0; i < n; i++) - free(subsys[i]); - free(subsys); - - return slist; -} - static int list_subsys(int argc, char **argv, struct command *cmd, struct plugin *plugin) { @@ -1899,295 +1373,6 @@ ret: return nvme_status_to_errno(ret, false); } -static int scan_namespace(struct nvme_namespace *n) -{ - int ret, fd; - char *path; - - ret = asprintf(&path, "%s%s", dev, n->name); - if (ret < 0) - return ret; - - fd = open(path, O_RDONLY); - if (fd < 0) - goto free; - - n->nsid = get_nsid(fd); - ret = nvme_identify_ns(fd, n->nsid, 0, &n->ns); - if (ret < 0) - goto close_fd; -close_fd: - close(fd); -free: - free(path); - return 0; -} - -static int scan_ctrl(struct nvme_ctrl *c, char *p) -{ - struct nvme_namespace *n; - struct dirent **ns; - char *path; - int i, fd, ret; - - ret = asprintf(&path, "%s/%s", p, c->name); - if (ret < 0) - return ret; - - c->address = get_nvme_ctrl_attr(path, "address"); - c->transport = get_nvme_ctrl_attr(path, "transport"); - c->state = get_nvme_ctrl_attr(path, "state"); - - c->nr_namespaces = scandir(path, &ns, scan_namespace_filter, alphasort); - c->namespaces = calloc(c->nr_namespaces, sizeof(*n)); - for (i = 0; i < c->nr_namespaces; i++) { - n = &c->namespaces[i]; - n->name = strdup(ns[i]->d_name); - n->ctrl = c; - scan_namespace(n); - } - - while (i--) - free(ns[i]); - free(ns); - free(path); - - ret = asprintf(&path, "%s%s", dev, c->name); - if (ret < 0) - return ret; - - fd = open(path, O_RDONLY); - if (fd < 0) - goto free; - - ret = nvme_identify_ctrl(fd, &c->id); - if (ret < 0) - goto close_fd; -close_fd: - close(fd); -free: - free(path); - return 0; -} - -static int scan_subsystem(struct nvme_subsystem *s) -{ - struct dirent **ctrls, **ns; - struct nvme_namespace *n; - struct nvme_ctrl *c; - int i, ret; - char *path; - - ret = asprintf(&path, "%s%s", subsys_dir, s->name); - if (ret < 0) - return ret; - - s->subsysnqn = get_nvme_subsnqn(path); - s->nr_ctrls = scandir(path, &ctrls, scan_ctrls_filter, alphasort); - s->ctrls = calloc(s->nr_ctrls, sizeof(*c)); - for (i = 0; i < s->nr_ctrls; i++) { - c = &s->ctrls[i]; - c->name = strdup(ctrls[i]->d_name); - c->subsys = s; - scan_ctrl(c, path); - } - - while (i--) - free(ctrls[i]); - free(ctrls); - - s->nr_namespaces = scandir(path, &ns, scan_namespace_filter, alphasort); - s->namespaces = calloc(s->nr_namespaces, sizeof(*n)); - for (i = 0; i < s->nr_namespaces; i++) { - n = &s->namespaces[i]; - n->name = strdup(ns[i]->d_name); - n->ctrl = &s->ctrls[0]; - scan_namespace(n); - } - while (i--) - free(ns[i]); - free(ns); - - free(path); - return 0; -} - -/* global, used for controller specific namespace filter */ -static int current_index; - -static int scan_dev_filter(const struct dirent *d) -{ - int ctrl, ns, part; - - if (d->d_name[0] == '.') - return 0; - - if (strstr(d->d_name, "nvme")) { - if (sscanf(d->d_name, "nvme%dn%dp%d", &ctrl, &ns, &part) == 3) - return 0; - if (sscanf(d->d_name, "nvme%dn%d", &ctrl, &ns) == 2) - return ctrl == current_index; - } - return 0; -} - -/* Sanity check that this namespace's parent matches itself */ -static int verify_legacy_ns(struct nvme_namespace *n) -{ - struct nvme_ctrl *c = n->ctrl; - struct nvme_id_ctrl id; - char *path; - int ret, fd; - - ret = asprintf(&path, "%s%s", dev, n->name); - if (ret < 0) - return ret; - - fd = open(path, O_RDONLY); - free (path); - - if (fd < 0) - return fd; - - ret = nvme_identify_ctrl(fd, &id); - close(fd); - - if (ret) - return ret; - - if (memcmp(id.mn, c->id.mn, sizeof(id.mn)) || - memcmp(id.sn, c->id.mn, sizeof(id.sn))) - return -ENODEV; - return 0; -} - -/* - * For pre-subsystem enabled kernel. Topology information is limited, but we can - * assume controller names are always a prefix to their namespaces, i.e. nvme0 - * 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) -{ - struct nvme_ctrl *c; - struct nvme_subsystem *s; - struct nvme_namespace *n; - struct dirent **devices, **namespaces; - int ret = 0, fd, i; - char *path; - - t->nr_subsystems = scandir(dev, &devices, scan_ctrls_filter, alphasort); - if (t->nr_subsystems < 0) { - fprintf(stderr, "no NVMe device(s) detected.\n"); - return t->nr_subsystems; - } - - t->subsystems = calloc(t->nr_subsystems, sizeof(*s)); - for (i = 0; i < t->nr_subsystems; i++) { - int j; - - s = &t->subsystems[i]; - s->nr_ctrls = 1; - s->ctrls = calloc(s->nr_ctrls, sizeof(*c)); - s->name = strdup(devices[i]->d_name); - s->subsysnqn = strdup(s->name); - s->nr_namespaces = 0; - - c = s->ctrls; - c->name = strdup(s->name); - sscanf(c->name, "nvme%d", ¤t_index); - c->nr_namespaces = scandir(dev, &namespaces, scan_dev_filter, - alphasort); - c->namespaces = calloc(c->nr_namespaces, sizeof(*n)); - - for (j = 0; j < c->nr_namespaces; j++) { - n = &c->namespaces[j]; - n->name = strdup(namespaces[j]->d_name); - n->ctrl = c; - scan_namespace(n); - ret = verify_legacy_ns(n); - if (ret) - goto free; - } - while (j--) - free(namespaces[j]); - free(namespaces); - - ret = asprintf(&path, "%s%s", dev, c->name); - if (ret < 0) - continue; - ret = 0; - - fd = open(path, O_RDONLY); - if (fd > 0) { - nvme_identify_ctrl(fd, &c->id); - close(fd); - } - free(path); - } - -free: - while (i--) - free(devices[i]); - free(devices); - return ret; -} - -static int scan_subsystems(struct nvme_topology *t) -{ - struct nvme_subsystem *s; - struct dirent **subsys; - int i; - - t->nr_subsystems = scandir(subsys_dir, &subsys, scan_subsys_filter, alphasort); - if (t->nr_subsystems < 0) - return legacy_list(t); - - t->subsystems = calloc(t->nr_subsystems, sizeof(*s)); - for (i = 0; i < t->nr_subsystems; i++) { - s = &t->subsystems[i]; - s->name = strdup(subsys[i]->d_name); - scan_subsystem(s); - } - - while (i--) - free(subsys[i]); - free(subsys); - return 0; -} - -static void free_topology(struct nvme_topology *t) -{ - int i, j, k; - - for (i = 0; i < t->nr_subsystems; i++) { - struct nvme_subsystem *s = &t->subsystems[i]; - - for (j = 0; j < s->nr_ctrls; j++) { - struct nvme_ctrl *c = &s->ctrls[j]; - - for (k = 0; k < c->nr_namespaces; k++) { - struct nvme_namespace *n = &c->namespaces[k]; - free(n->name); - } - free(c->name); - if (c->transport) - free(c->transport); - if (c->address) - free(c->address); - if (c->state) - free(c->state); - if (c->namespaces) - free(c->namespaces); - } - free(s->name); - free(s->subsysnqn); - free(s->ctrls); - free(s->namespaces); - } - free(t->subsystems); -} - static int list(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Retrieve basic information for all NVMe namespaces"; @@ -2228,6 +1413,7 @@ static int list(int argc, char **argv, struct command *cmd, struct plugin *plugi json_print_list_items(&t, cfg.verbose); else show_list_items(&t, cfg.verbose); + free_topology(&t); return 0; } @@ -2562,7 +1748,7 @@ static int id_ns_granularity(int argc, char **argv, struct command *cmd, struct "in either human-readable or binary format."; int err, fmt, fd; unsigned int flags = 0; - struct nvme_id_ns_granularity_list *granularity_list; + struct nvme_id_ns_granularity_list *granularity_list; struct config { char *output_format; @@ -3570,7 +2756,7 @@ static int show_registers(int argc, char **argv, struct command *cmd, struct plu err = nvme_get_properties(fd, &bar); if (err) { - bar = get_registers(); + bar = mmap_registers(devicename); fabrics = false; if (bar) err = 0; @@ -3707,78 +2893,6 @@ ret: return nvme_status_to_errno(err, false); } -/* Requires global 'devicename' was set */ -static void print_relatives() -{ - unsigned id, i, nsid = NVME_NSID_ALL; - char *path = NULL; - bool block = true; - int ret; - - ret = sscanf(devicename, "nvme%dn%d", &id, &nsid); - switch (ret) { - case 1: - asprintf(&path, "/sys/class/nvme/%s", devicename); - block = false; - break; - case 2: - asprintf(&path, "/sys/block/%s/device", devicename); - break; - default: - return; - } - if (!path) - return; - - if (block) { - char *subsysnqn; - struct subsys_list_item *slist; - int subcnt = 0; - - subsysnqn = get_nvme_subsnqn(path); - if (!subsysnqn) - return; - slist = get_subsys_list(&subcnt, subsysnqn, nsid); - if (subcnt != 1) { - free(subsysnqn); - free(path); - return; - } - - fprintf(stderr, "Namespace %s has parent controller(s):", devicename); - for (i = 0; i < slist[0].nctrls; i++) - fprintf(stderr, "%s%s", i ? ", " : "", slist[0].ctrls[i].name); - fprintf(stderr, "\n\n"); - free(subsysnqn); - } else { - struct dirent **paths; - bool comma = false; - int n, ns, cntlid; - - n = scandir(path, &paths, scan_ctrl_paths_filter, alphasort); - if (n < 0) { - free(path); - return; - } - - fprintf(stderr, "Controller %s has child namespace(s):", devicename); - for (i = 0; i < n; i++) { - if (sscanf(paths[i]->d_name, "nvme%dc%dn%d", - &id, &cntlid, &ns) != 3) { - if (sscanf(paths[i]->d_name, "nvme%dn%d", - &id, &ns) != 2) { - continue; - } - } - fprintf(stderr, "%snvme%dn%d", comma ? ", " : "", id, ns); - comma = true; - } - fprintf(stderr, "\n\n"); - free(paths); - } - free(path); -} - static int format(int argc, char **argv, struct command *cmd, struct plugin *plugin) { const char *desc = "Re-format a specified namespace on the "\ @@ -3957,7 +3071,7 @@ static int format(int argc, char **argv, struct command *cmd, struct plugin *plu fprintf(stderr, "You are about to format %s, namespace %#x%s.\n", devicename, cfg.namespace_id, cfg.namespace_id == NVME_NSID_ALL ? "(ALL namespaces)" : ""); - print_relatives(); + show_relatives(devicename); fprintf(stderr, "WARNING: Format may irrevocably delete this device's data.\n" "You have 10 seconds to press Ctrl-C to cancel this operation.\n\n" "Use the force [--force|-f] option to suppress this warning.\n"); @@ -4998,6 +4112,14 @@ ret: return nvme_status_to_errno(err, false); } +static unsigned long long elapsed_utime(struct timeval start_time, + struct timeval end_time) +{ + unsigned long long ret = (end_time.tv_sec - start_time.tv_sec) * 1000000 + + (end_time.tv_usec - start_time.tv_usec); + return ret; +} + static int submit_io(int opcode, char *command, const char *desc, int argc, char **argv) { diff --git a/nvme.h b/nvme.h index f7b9c314..2564e760 100644 --- a/nvme.h +++ b/nvme.h @@ -15,11 +15,12 @@ #ifndef _NVME_H #define _NVME_H +#include #include #include #include #include "plugin.h" -#include "json.h" +#include "util/json.h" #define unlikely(x) x @@ -237,10 +238,12 @@ extern const char *conarg_transport; extern const char *conarg_traddr; extern const char *conarg_trsvcid; extern const char *conarg_host_traddr; +extern const char *dev; +extern const char *subsys_dir; void register_extension(struct plugin *plugin); -#include "argconfig.h" +#include "util/argconfig.h" int parse_and_open(int argc, char **argv, const char *desc, const struct argconfig_commandline_options *clo, void *cfg, size_t size); @@ -249,9 +252,25 @@ extern const char *devicename; int __id_ctrl(int argc, char **argv, struct command *cmd, struct plugin *plugin, void (*vs)(__u8 *vs, struct json_object *root)); int validate_output_format(char *format); +int get_nvme_ctrl_info(char *name, char *path, struct ctrl_list_item *ctrl, + __u32 nsid); struct subsys_list_item *get_subsys_list(int *subcnt, char *subsysnqn, __u32 nsid); void free_subsys_list(struct subsys_list_item *slist, int n); char *nvme_char_from_block(char *block); +int get_nsid(int fd); +void free_ctrl_list_item(struct ctrl_list_item *ctrls); +void *mmap_registers(const char *dev); + +extern int current_index; +int scan_namespace_filter(const struct dirent *d); +int scan_ctrl_paths_filter(const struct dirent *d); +int scan_ctrls_filter(const struct dirent *d); +int scan_subsys_filter(const struct dirent *d); +int scan_dev_filter(const struct dirent *d); + +int scan_subsystems(struct nvme_topology *t); +void free_topology(struct nvme_topology *t); +char *get_nvme_subsnqn(char *path); /* * is_64bit_reg - It checks whether given offset of the controller register is diff --git a/plugin.c b/plugin.c index 4750df6b..c7d6b2e1 100644 --- a/plugin.c +++ b/plugin.c @@ -4,7 +4,7 @@ #include #include "plugin.h" -#include "argconfig.h" +#include "util/argconfig.h" static int version(struct plugin *plugin) { diff --git a/plugins/huawei/huawei-nvme.c b/plugins/huawei/huawei-nvme.c index 3130607a..d2976b80 100644 --- a/plugins/huawei/huawei-nvme.c +++ b/plugins/huawei/huawei-nvme.c @@ -66,31 +66,6 @@ struct huawei_list_element_len { unsigned int array_name; }; -static const char *dev = "/dev/"; - -/* Assume every block device starting with /dev/nvme is an nvme namespace */ -static int huawei_scan_dev_filter(const struct dirent *d) -{ - char path[264]; - struct stat bd; - int ctrl, ns, part; - - if (d->d_name[0] == '.') - return 0; - - if (strstr(d->d_name, "nvme")) { - snprintf(path, sizeof(path), "%s%s", dev, d->d_name); - if (stat(path, &bd)) - return 0; - if (!S_ISBLK(bd.st_mode)) - return 0; - if (sscanf(d->d_name, "nvme%dn%dp%d", &ctrl, &ns, &part) == 3) - return 0; - return 1; - } - return 0; -} - static int huawei_get_nvme_info(int fd, struct huawei_list_item *item, const char *node) { int err; @@ -346,7 +321,7 @@ static int huawei_list(int argc, char **argv, struct command *command, if (fmt != JSON && fmt != NORMAL) return -EINVAL; - n = scandir(dev, &devices, huawei_scan_dev_filter, alphasort); + n = scandir(dev, &devices, scan_dev_filter, alphasort); if (n <= 0) return n; diff --git a/plugins/wdc/wdc-nvme.c b/plugins/wdc/wdc-nvme.c index af453ed8..d563fc5f 100644 --- a/plugins/wdc/wdc-nvme.c +++ b/plugins/wdc/wdc-nvme.c @@ -701,6 +701,7 @@ static int wdc_get_pci_ids(uint32_t *device_id, uint32_t *vendor_id) close_fd: close(fd); free_id: + free(block); free(id); return ret; } diff --git a/argconfig.c b/util/argconfig.c similarity index 100% rename from argconfig.c rename to util/argconfig.c diff --git a/argconfig.h b/util/argconfig.h similarity index 100% rename from argconfig.h rename to util/argconfig.h diff --git a/json.c b/util/json.c similarity index 100% rename from json.c rename to util/json.c diff --git a/json.h b/util/json.h similarity index 100% rename from json.h rename to util/json.h diff --git a/parser.c b/util/parser.c similarity index 100% rename from parser.c rename to util/parser.c diff --git a/parser.h b/util/parser.h similarity index 100% rename from parser.h rename to util/parser.h diff --git a/suffix.c b/util/suffix.c similarity index 100% rename from suffix.c rename to util/suffix.c diff --git a/suffix.h b/util/suffix.h similarity index 100% rename from suffix.h rename to util/suffix.h -- 2.50.1