From: Hannes Reinecke Date: Fri, 30 Apr 2021 08:23:54 +0000 (+0200) Subject: tree,fabrics: use topology information for discovery X-Git-Tag: v1.0-rc0~129^2~8 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=1637f8ccc525ddf94add8886fb47dd6119aa0beb;p=users%2Fsagi%2Flibnvme.git tree,fabrics: use topology information for discovery Implement a topology tree containing all hosts, subsystems, and controllers in the system, and use that information during discovery. Signed-off-by: Hannes Reinecke --- diff --git a/examples/discover-loop.c b/examples/discover-loop.c index 2aaa9b61..02816091 100644 --- a/examples/discover-loop.c +++ b/examples/discover-loop.c @@ -47,21 +47,31 @@ static void print_discover_log(struct nvmf_discovery_log *log) int main() { struct nvmf_discovery_log *log = NULL; + nvme_root_t r; + nvme_host_t h; nvme_ctrl_t c; - char *hnqn; + char *hnqn, *hid; int ret; struct nvme_fabrics_config cfg = { - .nqn = NVME_DISC_SUBSYS_NAME, - .transport = "loop", .tos = -1, }; - hnqn = nvmf_hostnqn_from_file(), - cfg.hostnqn = hnqn; - - c = nvmf_add_ctrl(&cfg); + r = nvme_scan(); + hnqn = nvmf_hostnqn_from_file(); + hid = nvmf_hostid_from_file(); + h = nvme_lookup_host(r, hnqn, hid); + if (!h) { + fprintf(stderr, "Failed to allocated memory\n"); + return ENOMEM; + } + c = nvme_create_ctrl(NVME_DISC_SUBSYS_NAME, "loop", NULL, NULL, NULL); if (!c) { + fprintf(stderr, "Failed to allocate memory\n"); + return ENOMEM; + } + ret = nvmf_add_ctrl(h, c, &cfg); + if (ret < 0) { fprintf(stderr, "no controller found\n"); return errno; } @@ -75,6 +85,7 @@ int main() else print_discover_log(log); + nvme_free_tree(r); free(hnqn); return 0; } diff --git a/src/nvme/fabrics.c b/src/nvme/fabrics.c index 870bbb1d..ac5daa1b 100644 --- a/src/nvme/fabrics.c +++ b/src/nvme/fabrics.c @@ -184,34 +184,55 @@ static int add_argument(char **argstr, const char *tok, const char *arg) return 0; } -static int build_options(char **argstr, struct nvme_fabrics_config *cfg) +static int build_options(nvme_ctrl_t c, char **argstr, + const struct nvme_fabrics_config *cfg) { + const char *transport = nvme_ctrl_get_transport(c); + /* always specify nqn as first arg - this will init the string */ - if (asprintf(argstr, "nqn=%s", cfg->nqn) < 0) { + if (asprintf(argstr, "nqn=%s", + nvme_ctrl_get_subsysnqn(c)) < 0) { errno = ENOMEM; return -1; } - if (add_argument(argstr, "transport", cfg->transport) || - add_argument(argstr, "traddr", cfg->traddr) || - add_argument(argstr, "host_traddr", cfg->host_traddr) || - add_argument(argstr, "trsvcid", cfg->trsvcid) || - add_argument(argstr, "hostnqn", cfg->hostnqn) || - add_argument(argstr, "hostid", cfg->hostid) || - add_int_argument(argstr, "nr_write_queues", cfg->nr_write_queues, false) || - add_int_argument(argstr, "nr_poll_queues", cfg->nr_poll_queues, false) || - add_int_argument(argstr, "reconnect_delay", cfg->reconnect_delay, false) || - add_int_argument(argstr, "ctrl_loss_tmo", cfg->ctrl_loss_tmo, false) || + if (add_argument(argstr, "transport", transport) || + add_argument(argstr, "traddr", + nvme_ctrl_get_traddr(c)) || + add_argument(argstr, "host_traddr", + nvme_ctrl_get_host_traddr(c)) || + add_argument(argstr, "trsvcid", + nvme_ctrl_get_trsvcid(c)) || + add_argument(argstr, "hostnqn", + nvme_ctrl_get_hostnqn(c)) || + add_argument(argstr, "hostid", + nvme_ctrl_get_hostid(c)) || + add_int_argument(argstr, "nr_write_queues", + cfg->nr_write_queues, false) || + add_int_argument(argstr, "nr_poll_queues", + cfg->nr_poll_queues, false) || + add_int_argument(argstr, "reconnect_delay", + cfg->reconnect_delay, false) || + (strcmp(transport, "loop") && + add_int_argument(argstr, "ctrl_loss_tmo", + cfg->ctrl_loss_tmo, false)) || add_int_argument(argstr, "tos", cfg->tos, true) || - add_bool_argument(argstr, "duplicate_connect", cfg->duplicate_connect) || - add_bool_argument(argstr, "disable_sqflow", cfg->disable_sqflow) || - add_bool_argument(argstr, "hdr_digest", cfg->hdr_digest) || - add_bool_argument(argstr, "data_digest", cfg->data_digest) || + add_bool_argument(argstr, "duplicate_connect", + cfg->duplicate_connect) || + add_bool_argument(argstr, "disable_sqflow", + cfg->disable_sqflow) || + (!strcmp(transport, "tcp") && + add_bool_argument(argstr, "hdr_digest", cfg->hdr_digest)) || + (!strcmp(transport, "tcp") && + add_bool_argument(argstr, "data_digest", cfg->data_digest)) || add_int_argument(argstr, "queue_size", cfg->queue_size, false) || - add_int_argument(argstr, "keep_alive_tmo", cfg->keep_alive_tmo, false) || - add_int_argument(argstr, "nr_io_queues", cfg->nr_io_queues, false)) { + add_int_argument(argstr, "keep_alive_tmo", + cfg->keep_alive_tmo, false) || + add_int_argument(argstr, "nr_io_queues", + cfg->nr_io_queues, false)) { free(*argstr); + errno = ENOMEM; return -1; } @@ -255,12 +276,12 @@ out_close: return ret; } -int nvmf_add_ctrl_opts(struct nvme_fabrics_config *cfg) +int nvmf_add_ctrl_opts(nvme_ctrl_t c, const struct nvme_fabrics_config *cfg) { char *argstr; int ret; - ret = build_options(&argstr, cfg); + ret = build_options(c, &argstr, cfg); if (ret) return ret; @@ -269,27 +290,34 @@ int nvmf_add_ctrl_opts(struct nvme_fabrics_config *cfg) return ret; } -nvme_ctrl_t nvmf_add_ctrl(struct nvme_fabrics_config *cfg) +int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c, + const struct nvme_fabrics_config *cfg) { - char d[32] = { 0 }; + char *argstr; int ret; - ret = nvmf_add_ctrl_opts(cfg); - if (ret < 0) - return NULL; + ret = build_options(c, &argstr, cfg); + if (ret) + return ret; - if (snprintf(d, sizeof(d), "nvme%d", ret) < 0) - return NULL; + ret = __nvmf_add_ctrl(argstr); + free(argstr); + if (ret < 0) + return ret; - return nvme_scan_ctrl(d); + return nvme_init_ctrl(h, c, ret); } -nvme_ctrl_t nvmf_connect_disc_entry(struct nvmf_disc_log_entry *e, +nvme_ctrl_t nvmf_connect_disc_entry(nvme_host_t h, + struct nvmf_disc_log_entry *e, const struct nvme_fabrics_config *defcfg, bool *discover) { struct nvme_fabrics_config cfg = { 0 }; + const char *transport; + char *traddr = NULL, *trsvcid = NULL; nvme_ctrl_t c; + int ret; memcpy(&cfg, defcfg, sizeof(cfg)); switch (e->subtype) { @@ -312,8 +340,8 @@ nvme_ctrl_t nvmf_connect_disc_entry(struct nvmf_disc_log_entry *e, case NVMF_ADDR_FAMILY_IP6: nvme_chomp(e->traddr, NVMF_TRADDR_SIZE); nvme_chomp(e->trsvcid, NVMF_TRSVCID_SIZE); - cfg.traddr = e->traddr; - cfg.trsvcid = e->trsvcid; + traddr = e->traddr; + trsvcid = e->trsvcid; break; default: errno = EINVAL; @@ -324,29 +352,42 @@ nvme_ctrl_t nvmf_connect_disc_entry(struct nvmf_disc_log_entry *e, switch (e->adrfam) { case NVMF_ADDR_FAMILY_FC: nvme_chomp(e->traddr, NVMF_TRADDR_SIZE), - cfg.traddr = e->traddr; - cfg.trsvcid = NULL; + traddr = e->traddr; + trsvcid = NULL; break; } + case NVMF_TRTYPE_LOOP: + break; default: errno = EINVAL; return NULL; } - cfg.transport = nvmf_trtype_str(e->trtype); - cfg.nqn = e->subnqn; + transport = nvmf_trtype_str(e->trtype); + c = nvme_create_ctrl(e->subnqn, transport, traddr, + NULL, trsvcid); + if (!c) { + errno = ENOMEM; + return NULL; + } + if (e->treq & NVMF_TREQ_DISABLE_SQFLOW) cfg.disable_sqflow = true; - c = nvmf_add_ctrl(&cfg); - if (!c && errno == EINVAL && cfg.disable_sqflow) { + ret = nvmf_add_ctrl(h, c, &cfg); + if (!ret) + return c; + + if (errno == EINVAL && cfg.disable_sqflow) { errno = 0; /* disable_sqflow is unrecognized option on older kernels */ cfg.disable_sqflow = false; - c = nvmf_add_ctrl(&cfg); + ret = nvmf_add_ctrl(h, c, &cfg); + if (!ret) + return c; } - - return c; + nvme_free_ctrl(c); + return NULL; } static int nvme_discovery_log(int fd, __u32 len, struct nvmf_discovery_log *log) diff --git a/src/nvme/fabrics.h b/src/nvme/fabrics.h index cbf4e487..cfab4b51 100644 --- a/src/nvme/fabrics.h +++ b/src/nvme/fabrics.h @@ -13,15 +13,11 @@ #include #include "tree.h" +/* default to 600 seconds of reconnect attempts before giving up */ +#define NVMF_DEF_CTRL_LOSS_TMO 600 + /** * struct nvme_fabrics_config - Defines all linux nvme fabrics initiator options - * @transport: The fabric transport to use, either loop, fc, tcp, or rdma - * @traddr: Transport Address for the target, format specific to transport type - * @trsvcid: Transport Service Identifier, specific to the transport type - * @nqn: Target NVMe Qualified Name - * @hostnqn: Host NVMe Qualified Name - * @host_traddr: Host Transport Address - * @hostid: Host Identifier * @queue_size: Number of IO queue entries * @nr_io_queues: Number of controller IO queues to establish * @reconnect_delay: Time between two consecutive reconnect attempts. @@ -36,14 +32,6 @@ * @data_digest: Generate/verify data digest (TCP) */ struct nvme_fabrics_config { - const char *transport; - const char *traddr; - const char *trsvcid; - const char *nqn; - const char *hostnqn; - const char *host_traddr; - const char *hostid; - int queue_size; int nr_io_queues; int reconnect_delay; @@ -125,19 +113,23 @@ const char *nvmf_cms_str(__u8 cms); /** * nvmf_add_ctrl_opts() - + * @c: * @cfg: * * Return: */ -int nvmf_add_ctrl_opts(struct nvme_fabrics_config *cfg); +int nvmf_add_ctrl_opts(nvme_ctrl_t c, const struct nvme_fabrics_config *cfg); /** * nvmf_add_ctrl() - + * @h: + * @c: * @cfg: * * Return: */ -nvme_ctrl_t nvmf_add_ctrl(struct nvme_fabrics_config *cfg); +int nvmf_add_ctrl(nvme_host_t h, nvme_ctrl_t c, + const struct nvme_fabrics_config *cfg); /** * nvmf_get_discovery_log() - @@ -175,13 +167,15 @@ char *nvmf_hostid_from_file(); /** * nvmf_connect_disc_entry() - + * @h: * @e: * @defcfg: * @discover: * * Return: An */ -nvme_ctrl_t nvmf_connect_disc_entry(struct nvmf_disc_log_entry *e, +nvme_ctrl_t nvmf_connect_disc_entry(nvme_host_t h, + struct nvmf_disc_log_entry *e, const struct nvme_fabrics_config *defcfg, bool *discover); #endif /* _LIBNVME_FABRICS_H */ diff --git a/src/nvme/tree.c b/src/nvme/tree.c index eb554a35..5643075f 100644 --- a/src/nvme/tree.c +++ b/src/nvme/tree.c @@ -27,6 +27,9 @@ /* XXX: Make a place for private declarations */ extern int nvme_set_attr(const char *dir, const char *attr, const char *value); +static struct nvme_host *default_host; + +nvme_host_t nvme_default_host(nvme_root_t r); static void nvme_free_host(struct nvme_host *h); static void nvme_free_subsystem(struct nvme_subsystem *s); static int nvme_subsystem_scan_namespace(struct nvme_subsystem *s, char *name); @@ -95,6 +98,8 @@ struct nvme_ctrl { char *queue_count; char *serial; char *sqsize; + char *hostnqn; + char *hostid; char *transport; char *subsysnqn; char *traddr; @@ -111,6 +116,9 @@ struct nvme_subsystem { char *name; char *sysfs_dir; char *subsysnqn; + char *model; + char *serial; + char *firmware; }; struct nvme_host { @@ -135,18 +143,16 @@ static inline void nvme_free_dirents(struct dirent **d, int i) nvme_host_t nvme_default_host(nvme_root_t r) { - struct nvme_host *h = calloc(1, sizeof(*h)); + struct nvme_host *h; + char *hostnqn, *hostid; - if (!h) { - errno = ENOMEM; - return NULL; - } + hostnqn = nvmf_hostnqn_from_file(); + hostid = nvmf_hostid_from_file(); - h->hostnqn = nvmf_hostnqn_from_file(); - h->hostid = nvmf_hostid_from_file(); - list_head_init(&h->subsystems); - list_add(&r->hosts, &h->entry); - h->r = r; + h = nvme_lookup_host(r, hostnqn, hostid); + default_host = h; + free(hostnqn); + free(hostid); return h; } @@ -318,10 +324,42 @@ static void nvme_free_subsystem(struct nvme_subsystem *s) free(s->name); free(s->sysfs_dir); free(s->subsysnqn); + if (s->model) + free(s->model); + if (s->serial) + free(s->serial); + if (s->firmware) + free(s->firmware); free(s); } -void nvme_free_host(struct nvme_host *h) +struct nvme_subsystem *nvme_lookup_subsystem(struct nvme_host *h, + const char *name, + const char *subsysnqn) +{ + struct nvme_subsystem *s; + + nvme_for_each_subsystem(h, s) { + if (strcmp(s->subsysnqn, subsysnqn)) + continue; + if (name && s->name && + strcmp(s->name, name)) + continue; + return s; + } + s = calloc(1, sizeof(*s)); + if (!s) + return NULL; + + s->h = h; + s->subsysnqn = strdup(subsysnqn); + list_head_init(&s->ctrls); + list_head_init(&s->namespaces); + list_add(&h->subsystems, &s->entry); + return s; +} + +static void nvme_free_host(struct nvme_host *h) { struct nvme_subsystem *s, *_s; @@ -334,6 +372,33 @@ void nvme_free_host(struct nvme_host *h) free(h); } +struct nvme_host *nvme_lookup_host(nvme_root_t r, const char *hostnqn, + const char *hostid) +{ + struct nvme_host *h; + + nvme_for_each_host(r, h) { + if (strcmp(h->hostnqn, hostnqn)) + continue; + if (hostid && + strcmp(h->hostid, hostid)) + continue; + return h; + } + h = calloc(1,sizeof(*h)); + if (!h) + return NULL; + h->hostnqn = strdup(hostnqn); + if (hostid) + h->hostid = strdup(hostid); + list_head_init(&h->subsystems); + list_node_init(&h->entry); + h->r = r; + list_add(&r->hosts, &h->entry); + + return h; +} + static int nvme_subsystem_scan_namespaces(struct nvme_subsystem *s) { struct dirent **namespaces; @@ -366,30 +431,49 @@ static int nvme_subsystem_scan_ctrls(struct nvme_subsystem *s) return 0; } +int nvme_init_subsystem(nvme_subsystem_t s, const char *name, const char *path) +{ + s->model = nvme_get_attr(path, "model"); + if (!s->model) { + errno = ENODEV; + return -1; + } + s->serial = nvme_get_attr(path, "serial"); + s->firmware = nvme_get_attr(path, "firmware_rev"); + s->name = strdup(name); + s->sysfs_dir = (char *)path; + + return 0; +} + static int nvme_scan_subsystem(struct nvme_host *h, char *name, nvme_scan_filter_t f) { struct nvme_subsystem *s; - char *path; + char *path, *subsysnqn; int ret; ret = asprintf(&path, "%s/%s", nvme_subsys_sysfs_dir, name); if (ret < 0) return ret; - s = calloc(1, sizeof(*s)); + subsysnqn = nvme_get_attr(path, "subsysnqn"); + if (!subsysnqn) { + errno = ENODEV; + goto free_path; + } + s = nvme_lookup_subsystem(h, name, subsysnqn); if (!s) { + free(subsysnqn); errno = ENOMEM; goto free_path; } - - s->h = h; - s->name = strdup(name); - s->sysfs_dir = path; - s->subsysnqn = nvme_get_subsys_attr(s, "subsysnqn"); - list_head_init(&s->ctrls); - list_head_init(&s->namespaces); - list_add(&h->subsystems, &s->entry); + free(subsysnqn); + if (!s->name) { + ret = nvme_init_subsystem(s, name, path); + if (ret < 0) + return ret; + } nvme_subsystem_scan_namespaces(s); nvme_subsystem_scan_ctrls(s); @@ -520,7 +604,7 @@ const char *nvme_ctrl_get_sysfs_dir(nvme_ctrl_t c) const char *nvme_ctrl_get_subsysnqn(nvme_ctrl_t c) { - return c->subsysnqn; + return c->s ? c->s->subsysnqn : c->subsysnqn; } const char *nvme_ctrl_get_address(nvme_ctrl_t c) @@ -583,6 +667,20 @@ const char *nvme_ctrl_get_host_traddr(nvme_ctrl_t c) return c->host_traddr; } +const char *nvme_ctrl_get_hostnqn(nvme_ctrl_t c) +{ + if (!c->s) + return default_host->hostnqn; + return c->s->h->hostnqn; +} + +const char *nvme_ctrl_get_hostid(nvme_ctrl_t c) +{ + if (!c->s) + return default_host->hostid; + return c->s->h->hostid; +} + int nvme_ctrl_identify(nvme_ctrl_t c, struct nvme_id_ctrl *id) { return nvme_identify_ctrl(nvme_ctrl_get_fd(c), id); @@ -608,10 +706,33 @@ nvme_path_t nvme_ctrl_next_path(nvme_ctrl_t c, nvme_path_t p) return p ? list_next(&c->paths, p, entry) : NULL; } +#define FREE_CTRL_ATTR(a) \ + do { if (a) { free(a); (a) = NULL; } } while (0) int nvme_ctrl_disconnect(nvme_ctrl_t c) { - return nvme_set_attr(nvme_ctrl_get_sysfs_dir(c), - "delete_controller", "1"); + int ret; + + ret = nvme_set_attr(nvme_ctrl_get_sysfs_dir(c), + "delete_controller", "1"); + if (ret < 0) + return ret; + + if (c->fd >= 0) { + close(c->fd); + c->fd = -1; + } + FREE_CTRL_ATTR(c->name); + FREE_CTRL_ATTR(c->sysfs_dir); + FREE_CTRL_ATTR(c->firmware); + FREE_CTRL_ATTR(c->model); + FREE_CTRL_ATTR(c->state); + FREE_CTRL_ATTR(c->numa_node); + FREE_CTRL_ATTR(c->queue_count); + FREE_CTRL_ATTR(c->serial); + FREE_CTRL_ATTR(c->sqsize); + FREE_CTRL_ATTR(c->address); + + return 0; } void nvme_unlink_ctrl(nvme_ctrl_t c) @@ -633,12 +754,16 @@ void nvme_free_ctrl(nvme_ctrl_t c) nvme_ctrl_for_each_ns_safe(c, n, _n) nvme_free_ns(n); - close(c->fd); - free(c->name); - free(c->sysfs_dir); - free(c->subsysnqn); - free(c->address); - free(c->traddr); + if (c->fd >= 0) + close(c->fd); + if (c->name) + free(c->name); + if (c->sysfs_dir) + free(c->sysfs_dir); + if (c->address) + free(c->address); + if (c->traddr) + free(c->traddr); if (c->trsvcid) free(c->trsvcid); if (c->host_traddr) @@ -654,6 +779,69 @@ void nvme_free_ctrl(nvme_ctrl_t c) free(c); } +struct nvme_ctrl *nvme_create_ctrl(const char *subsysnqn, + const char *transport, const char *traddr, + const char *host_traddr, const char *trsvcid) +{ + struct nvme_ctrl *c; + + if (!transport) + return NULL; + c = calloc(1, sizeof(*c)); + c->fd = -1; + list_head_init(&c->namespaces); + list_head_init(&c->paths); + list_node_init(&c->entry); + c->transport = strdup(transport); + if (subsysnqn) + c->subsysnqn = strdup(subsysnqn); + if (traddr) + c->traddr = strdup(traddr); + else + c->traddr = strdup("none"); + if (host_traddr) + c->host_traddr = strdup(host_traddr); + else + c->host_traddr = strdup("none"); + if (trsvcid) + c->trsvcid = strdup(trsvcid); + else + c->trsvcid = strdup("none"); + + return c; +} + +struct nvme_ctrl *nvme_lookup_ctrl(struct nvme_subsystem *s, + const char *transport, const char *traddr, + const char *host_traddr, const char *trsvcid) +{ + struct nvme_ctrl *c; + + if (!transport) + return NULL; + nvme_subsystem_for_each_ctrl(s, c) { + if (strcmp(c->transport, transport)) + continue; + if (traddr && + strcmp(c->traddr, traddr)) + continue; + if (host_traddr && + strcmp(c->host_traddr, host_traddr)) + continue; + if (trsvcid && + strcmp(c->trsvcid, trsvcid)) + continue; + return c; + } + c = nvme_create_ctrl(s->subsysnqn, transport, + traddr, host_traddr, trsvcid); + if (c) { + c->s = s; + list_add(&s->ctrls, &c->entry); + } + return c; +} + static int nvme_ctrl_scan_paths(struct nvme_ctrl *c) { struct dirent **paths; @@ -683,35 +871,48 @@ static int nvme_ctrl_scan_namespaces(struct nvme_ctrl *c) return 0; } -static nvme_ctrl_t __nvme_ctrl_alloc(const char *path, const char *name) +static char *nvme_ctrl_lookup_subsystem_name(nvme_ctrl_t c) { + struct dirent **subsys; + char *subsys_name = NULL; DIR *d; - nvme_ctrl_t c; - char *addr, *a, *e; - char *traddr = NULL, *trsvcid = NULL, *host_traddr = NULL; + int ret, i; + char path[PATH_MAX]; - d = opendir(path); - if (!d) + ret = nvme_scan_subsystems(&subsys); + if (ret < 0) return NULL; - closedir(d); + for (i = 0; i < ret; i++) { + sprintf(path, "%s/%s/%s", nvme_subsys_sysfs_dir, + subsys[i]->d_name, c->name); + d = opendir(path); + if (!d) + continue; + subsys_name = strdup(subsys[i]->d_name); + closedir(d); + break; + } + nvme_free_dirents(subsys, i); + return subsys_name; +} - c = calloc(1, sizeof(*c)); - if (!c) { - errno = ENOMEM; - return NULL; +static int __nvme_ctrl_init(nvme_ctrl_t c, const char *path, const char *name) +{ + DIR *d; + + d = opendir(path); + if (!d) { + errno = ENODEV; + return -1; } + closedir(d); c->fd = nvme_open(name); if (c->fd < 0) - goto free_ctrl; + return c->fd; - list_head_init(&c->namespaces); - list_head_init(&c->paths); - list_node_init(&c->entry); c->name = strdup(name); c->sysfs_dir = (char *)path; - c->subsysnqn = nvme_get_ctrl_attr(c, "subsysnqn"); - c->address = nvme_get_ctrl_attr(c, "address"); c->firmware = nvme_get_ctrl_attr(c, "firmware_rev"); c->model = nvme_get_ctrl_attr(c, "model"); c->state = nvme_get_ctrl_attr(c, "state"); @@ -719,9 +920,67 @@ static nvme_ctrl_t __nvme_ctrl_alloc(const char *path, const char *name) c->queue_count = nvme_get_ctrl_attr(c, "queue_count"); c->serial = nvme_get_ctrl_attr(c, "serial"); c->sqsize = nvme_get_ctrl_attr(c, "sqsize"); - c->transport = nvme_get_ctrl_attr(c, "transport"); + return 0; +} + +int nvme_init_ctrl(nvme_host_t h, nvme_ctrl_t c, int instance) +{ + nvme_subsystem_t s; + const char *subsys_name; + char *path, *name; + int ret; + + ret = asprintf(&name, "nvme%d", instance); + if (ret < 0) { + errno = ENOMEM; + return -1; + } + ret = asprintf(&path, "%s/nvme%d", nvme_ctrl_sysfs_dir, instance); + if (ret < 0) { + free(name); + errno = ENOMEM; + return -1; + } + + ret = __nvme_ctrl_init(c, path, name); + if (ret < 0) + free(path); + + c->address = nvme_get_attr(path, "address"); + if (!c->address) { + free(path); + free(name); + errno = -ENXIO; + return -1; + } + subsys_name = nvme_ctrl_lookup_subsystem_name(c); + s = nvme_lookup_subsystem(h, subsys_name, c->subsysnqn); + if (!s) { + errno = ENXIO; + ret = -1; + } + c->s = s; + list_add(&s->ctrls, &c->entry); + free(name); + return ret; +} + +static nvme_ctrl_t nvme_ctrl_alloc(nvme_subsystem_t s, const char *path, + const char *name) +{ + nvme_ctrl_t c; + char *addr, *address, *a, *e; + char *transport, *traddr = NULL, *trsvcid = NULL, *host_traddr = NULL; + int ret; + + transport = nvme_get_attr(path, "transport"); + if (!transport) { + errno = ENXIO; + return NULL; + } /* Parse 'address' string into components */ - addr = strdup(c->address); + addr = nvme_get_attr(path, "address"); + address = strdup(addr); a = strtok_r(addr, ",", &e); while (a && strlen(a)) { if (!strncmp(a, "traddr=", 7)) @@ -733,55 +992,88 @@ static nvme_ctrl_t __nvme_ctrl_alloc(const char *path, const char *name) a = strtok_r(NULL, ",", &e); } - if (traddr) - c->traddr = strdup(traddr); - if (trsvcid) - c->trsvcid = strdup(trsvcid); - if (host_traddr) - c->host_traddr = strdup(host_traddr); + c = nvme_lookup_ctrl(s, transport, traddr, host_traddr, trsvcid); free(addr); - return c; - -free_ctrl: - free(c); - return NULL; + if (!c) { + errno = ENOMEM; + return NULL; + } + c->address = address; + ret = __nvme_ctrl_init(c, path, name); + return (ret < 0) ? NULL : c; } -static nvme_ctrl_t nvme_ctrl_alloc(const char *sysfs, const char *name) +nvme_ctrl_t nvme_scan_ctrl(nvme_root_t r, const char *name) { + nvme_host_t h; + nvme_subsystem_t s; nvme_ctrl_t c; char *path; + char *hostnqn, *hostid, *subsysnqn; int ret; - ret = asprintf(&path, "%s/%s", sysfs, name); + ret = asprintf(&path, "%s/%s", nvme_ctrl_sysfs_dir, name); if (ret < 0) { errno = ENOMEM; return NULL; } - c = __nvme_ctrl_alloc(path, name); + hostnqn = nvme_get_attr(path, "hostnqn"); + if (!hostnqn) { + free(path); + errno = ENODEV; + return NULL; + } + hostid = nvme_get_attr(path, "hostid"); + h = nvme_lookup_host(r, hostnqn, hostid); + free(hostnqn); + if (hostid) + free(hostid); + if (!h) { + h = nvme_default_host(r); + if (!h) { + free(path); + errno = ENOMEM; + return NULL; + } + } + + subsysnqn = nvme_get_attr(path, "subsysnqn"); + if (!subsysnqn) { + free(path); + errno = ENXIO; + return NULL; + } + s = nvme_lookup_subsystem(h, NULL, subsysnqn); + if (!s) { + free(path); + errno = ENOMEM; + return NULL; + } + c = nvme_ctrl_alloc(s, path, name); if (!c) free(path); - return c; -} -nvme_ctrl_t nvme_scan_ctrl(const char *name) -{ - return nvme_ctrl_alloc(nvme_ctrl_sysfs_dir, name); + return c; } static int nvme_subsystem_scan_ctrl(struct nvme_subsystem *s, char *name) { nvme_ctrl_t c; + char *path; - c = nvme_ctrl_alloc(s->sysfs_dir, name); - if (!c) + if (asprintf(&path, "%s/%s", s->sysfs_dir, name) < 0) { + errno = ENOMEM; return -1; + } - c->s = s; + c = nvme_ctrl_alloc(s, path, name); + if (!c) { + free(path); + return -1; + } nvme_ctrl_scan_namespaces(c); nvme_ctrl_scan_paths(c); - list_add(&s->ctrls, &c->entry); return 0; } @@ -832,6 +1124,21 @@ const char *nvme_ns_get_name(nvme_ns_t n) return n->name; } +const char *nvme_ns_get_model(nvme_ns_t n) +{ + return n->c ? n->c->model : n->s->model; +} + +const char *nvme_ns_get_serial(nvme_ns_t n) +{ + return n->c ? n->c->serial : n->s->serial; +} + +const char *nvme_ns_get_firmware(nvme_ns_t n) +{ + return n->c ? n->c->firmware : n->s->firmware; +} + int nvme_ns_get_lba_size(nvme_ns_t n) { return n->lba_size; @@ -1104,3 +1411,24 @@ static int nvme_subsystem_scan_namespace(struct nvme_subsystem *s, char *name) list_add(&s->namespaces, &n->entry); return 0; } + +struct nvme_ns *nvme_subsystem_lookup_namespace(struct nvme_subsystem *s, + __u32 nsid) +{ + struct nvme_ns *n; + char *name; + int ret; + + ret = asprintf(&name, "%sn%u", s->name, nsid); + if (ret < 0) + return NULL; + n = __nvme_scan_namespace(s->sysfs_dir, name); + if (!n) { + free(name); + return NULL; + } + + n->s = s; + list_add(&s->namespaces, &n->entry); + return n; +} diff --git a/src/nvme/tree.h b/src/nvme/tree.h index adb5bd06..f27daa43 100644 --- a/src/nvme/tree.h +++ b/src/nvme/tree.h @@ -84,6 +84,15 @@ nvme_host_t nvme_next_host(nvme_root_t r, nvme_host_t h); */ nvme_root_t nvme_host_get_root(nvme_host_t h); +/** + * nvme_lookup_host() - + * @r: + * + * Return: + */ +nvme_host_t nvme_lookup_host(nvme_root_t r, const char *hostnqn, + const char *hostid); + /** * nvme_host_get_hostnqn() - * @h: @@ -125,6 +134,18 @@ nvme_subsystem_t nvme_first_subsystem(nvme_host_t h); */ nvme_subsystem_t nvme_next_subsystem(nvme_host_t h, nvme_subsystem_t s); +/** + * nvme_lookup_subsystem() - + * @h: + * @name: + * @subsysnqn: + * + * Return: + */ +nvme_subsystem_t nvme_lookup_subsystem(struct nvme_host *h, + const char *name, + const char *subsysnqn); + /** * nvme_subsystem_get_host() - * @s: @@ -184,6 +205,36 @@ nvme_ctrl_t nvme_subsystem_first_ctrl(nvme_subsystem_t s); */ nvme_ctrl_t nvme_subsystem_next_ctrl(nvme_subsystem_t s, nvme_ctrl_t c); +/** + * nvme_lookup_ctrl() - + * @s: + * @transport: + * @traddr: + * @host_traddr: + * @trsvcid: + * + * Return: + */ +nvme_ctrl_t nvme_lookup_ctrl(nvme_subsystem_t s, const char *transport, + const char *traddr, const char *host_traddr, + const char *trsvcid); + + +/** + * nvme_create_ctrl() - + * @subsysnqn: + * @transport: + * @traddr: + * @host_traddr: + * @trsvcid: + * + * Return: + */ +nvme_ctrl_t nvme_create_ctrl(const char *subsysnqn, const char *transport, + const char *traddr, const char *host_traddr, + const char *trsvcid); + + /** * nvme_subsystem_first_ns() - * @s: @@ -397,6 +448,30 @@ const char *nvme_ns_get_sysfs_dir(nvme_ns_t n); */ const char *nvme_ns_get_name(nvme_ns_t n); +/** + * nvme_ns_get_firmware() - + * @n: + * + * Return: + */ +const char *nvme_ns_get_firmware(nvme_ns_t n); + +/** + * nvme_ns_get_serial() - + * @n: + * + * Return: + */ +const char *nvme_ns_get_serial(nvme_ns_t n); + +/** + * nvme_ns_get_model() - + * @n: + * + * Return: + */ +const char *nvme_ns_get_model(nvme_ns_t n); + /** * nvme_ns_get_subsystem() - * @n: @@ -660,6 +735,22 @@ const char *nvme_ctrl_get_nqn(nvme_ctrl_t c); */ const char *nvme_ctrl_get_subsysnqn(nvme_ctrl_t c); +/** + * nvme_ctrl_get_hostnqn() - + * @c: + * + * Return: + */ +const char *nvme_ctrl_get_hostnqn(nvme_ctrl_t c); + +/** + * nvme_ctrl_get_hostid() - + * @c: + * + * Return: + */ +const char *nvme_ctrl_get_hostid(nvme_ctrl_t c); + /** * nvme_ctrl_get_subsystem() - * @c: @@ -715,7 +806,17 @@ int nvme_ctrl_disconnect(nvme_ctrl_t c); * * Return: */ -nvme_ctrl_t nvme_scan_ctrl(const char *name); +nvme_ctrl_t nvme_scan_ctrl(nvme_root_t r, const char *name); + +/** + * nvme_init_ctrl() - + * @h: + * @c: + * @instance: + * + * Return: + */ +int nvme_init_ctrl(nvme_host_t h, nvme_ctrl_t c, int instance); /** * nvme_free_ctrl() - @@ -761,12 +862,28 @@ const char *nvme_subsystem_get_name(nvme_subsystem_t s); */ nvme_root_t nvme_scan_filter(nvme_scan_filter_t f); +/** + * nvme_host_get_hostnqn() - + * @h: + * + * Return: + */ +const char *nvme_host_get_hostnqn(nvme_host_t h); + +/** + * nvme_host_get_hostid() - + * @h: + * + * Return: + */ +const char *nvme_host_get_hostid(nvme_host_t h); + /** * nvme_scan() - * * Return: */ -nvme_root_t nvme_scan(); +nvme_root_t nvme_scan(void); /** * nvme_refresh_topology() - @@ -822,6 +939,8 @@ char *nvme_get_ctrl_attr(nvme_ctrl_t c, const char *attr); */ char *nvme_get_ns_attr(nvme_ns_t n, const char *attr); +nvme_ns_t nvme_subsystem_lookup_namespace(struct nvme_subsystem *s, + __u32 nsid); /** * nvme_get_path_attr() - * @p: diff --git a/test/test.c b/test/test.c index db5c019d..aa4e82e7 100644 --- a/test/test.c +++ b/test/test.c @@ -326,13 +326,12 @@ int main(int argc, char **argv) } } printf("\n"); - nvme_free_tree(r); if (argc > 1) ctrl = argv[1]; printf("Test scan specific controller\n"); - c = nvme_scan_ctrl(ctrl); + c = nvme_scan_ctrl(r, ctrl); if (c) { printf("%s %s %s %s\n", nvme_ctrl_get_name(c), nvme_ctrl_get_transport(c), @@ -341,6 +340,7 @@ int main(int argc, char **argv) nvme_free_ctrl(c); } printf("\n"); + nvme_free_tree(r); r = nvme_scan(); if (!r)