]> www.infradead.org Git - users/sagi/libnvme.git/commitdiff
tree,fabrics: use topology information for discovery
authorHannes Reinecke <hare@suse.de>
Fri, 30 Apr 2021 08:23:54 +0000 (10:23 +0200)
committerHannes Reinecke <hare@suse.de>
Fri, 11 Jun 2021 10:12:54 +0000 (12:12 +0200)
Implement a topology tree containing all hosts, subsystems, and
controllers in the system, and use that information during
discovery.

Signed-off-by: Hannes Reinecke <hare@suse.de>
examples/discover-loop.c
src/nvme/fabrics.c
src/nvme/fabrics.h
src/nvme/tree.c
src/nvme/tree.h
test/test.c

index 2aaa9b6137c019a30e7d5952143f960d3519a4a9..028160912205c09a0f7df19802674037e567e838 100644 (file)
@@ -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;
 }
index 870bbb1d3d997bd18a9846288618a681e3048bae..ac5daa1ba36e48a894675105d2d131cbc22914c3 100644 (file)
@@ -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)
index cbf4e4872a9cb251dbe5e896fe078c3b4c4882e6..cfab4b515ff7bc623d2bc8115626ce13463cb97b 100644 (file)
 #include <stdint.h>
 #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.
  * @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 */
index eb554a3523127dd336a905c3ebc7f7b2247692d3..5643075fa05b8476843798b2bbad83517b77dcca 100644 (file)
@@ -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;
+}
index adb5bd063c2a99732b13c5789ceb7423f76d5d01..f27daa43a72a156750dfec118f313c3b77b3bfdf 100644 (file)
@@ -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:
index db5c019d0b8cc6492e3c6a6e6e492c1a6abd441e..aa4e82e72015b7eaa0db2fcb698d4bc18f1c5a5c 100644 (file)
@@ -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)