]> www.infradead.org Git - users/sagi/libnvme.git/commitdiff
tree: rework topology for multiple hosts
authorHannes Reinecke <hare@suse.de>
Fri, 13 Aug 2021 09:58:08 +0000 (11:58 +0200)
committerHannes Reinecke <hare@suse.de>
Fri, 13 Aug 2021 10:23:17 +0000 (12:23 +0200)
Our tree structure host->subsys->ctrl doesn't match the underlying
topology; the subsystem actually just a container for controllers,
and the hosts are specified on the controller level, not the subsystem
level.
So to flatten that into our tree structure we need to duplicate the
subsystem objects for each host, and filter out the controllers such
that only the controllers with the correct hostnqn will show up
in our internal tree.

Signed-off-by: Hannes Reinecke <hare@suse.de>
src/nvme/filters.c
src/nvme/filters.h
src/nvme/tree.c

index 06137f5f1a84eb90ccc2f47961e55203aa514bdb..0f6202f9e537f5de5878fac37012521dcfa8fc0d 100644 (file)
@@ -87,6 +87,12 @@ int nvme_subsys_filter(const struct dirent *d)
        return 0;
 }
 
+int nvme_scan_ctrls(struct dirent ***ctrls)
+{
+       return scandir(nvme_ctrl_sysfs_dir, ctrls, nvme_ctrls_filter,
+                      alphasort);
+}
+
 int nvme_scan_subsystems(struct dirent ***subsys)
 {
        return scandir(nvme_subsys_sysfs_dir, subsys, nvme_subsys_filter,
index 52318311b0999e93a2ca79168c2ad011715ec65f..6ff847356541c2870040534482efce862d887c3f 100644 (file)
@@ -36,6 +36,14 @@ int nvme_paths_filter(const struct dirent *d);
  */
 int nvme_ctrls_filter(const struct dirent *d);
 
+/**
+ * nvme_scan_ctrls() -
+ * @ctrls:
+ *
+ * Return: 
+ */
+int nvme_scan_ctrls(struct dirent ***ctrls);
+
 /**
  * nvme_subsys_filter() -
  * @d:
index 264cdf35896b7fab1dfcbf00155a4cd8a0cd4217..149c8ec0454ede1e8f47ff16c48d9ccb8c6cb3fa 100644 (file)
 static struct nvme_host *default_host;
 
 static void __nvme_free_host(nvme_host_t h);
+static void __nvme_free_subsystem(nvme_subsystem_t c);
 static void __nvme_free_ctrl(nvme_ctrl_t c);
 static int nvme_subsystem_scan_namespace(struct nvme_subsystem *s, char *name);
-static int nvme_scan_subsystem(struct nvme_root *r, char *name,
+static int nvme_scan_subsystem(struct nvme_host *h, char *name,
                               nvme_scan_filter_t f);
 static int nvme_subsystem_scan_ctrl(struct nvme_subsystem *s, char *name);
 static int nvme_ctrl_scan_namespace(struct nvme_ctrl *c, char *name);
@@ -67,19 +68,65 @@ nvme_host_t nvme_default_host(nvme_root_t r)
        return h;
 }
 
+static int nvme_scan_hosts(struct nvme_root *r)
+{
+       struct dirent **ctrls;
+       int ret, i;
+
+       ret = nvme_scan_ctrls(&ctrls);
+       if (ret < 0)
+               return ret;
+
+       for (i = 0; i < ret; i++) {
+               char *path, *hostnqn, *hostid;
+
+               if (asprintf(&path, "%s/%s", nvme_ctrl_sysfs_dir,
+                            ctrls[i]->d_name) < 0) {
+                       errno = ENOMEM;
+                       return -1;
+               }
+               hostnqn = nvme_get_attr(path, "hostnqn");
+               hostid = nvme_get_attr(path, "hostid");
+               nvme_lookup_host(r, hostnqn, hostid);
+               free(hostnqn);
+               if (hostid)
+                       free(hostid);
+               free(path);
+       }
+       return 0;
+}
+
 static int nvme_scan_topology(struct nvme_root *r, nvme_scan_filter_t f)
 {
        struct dirent **subsys;
        int i, ret;
+       struct nvme_host *h, *_h;
 
+       ret = nvme_scan_hosts(r);
+       if (ret < 0)
+               return ret;
        ret = nvme_scan_subsystems(&subsys);
        if (ret < 0)
                return ret;
 
-       for (i = 0; i < ret; i++)
-               nvme_scan_subsystem(r, subsys[i]->d_name, f);
+       nvme_for_each_host(r, h) {
+               for (i = 0; i < ret; i++)
+                       nvme_scan_subsystem(h, subsys[i]->d_name, f);
+       }
 
-       nvme_free_dirents(subsys, i);
+       nvme_free_dirents(subsys, ret);
+
+       /* Prune hosts with empty subsystems */
+       nvme_for_each_host_safe(r, h, _h) {
+               nvme_subsystem_t s, _s;
+               nvme_for_each_subsystem_safe(h, s, _s) {
+                       if (list_empty(&s->ctrls) &&
+                           list_empty(&s->namespaces))
+                               __nvme_free_subsystem(s);
+               }
+               if (list_empty(&h->subsystems))
+                       __nvme_free_host(h);
+       }
        return 0;
 }
 
@@ -395,33 +442,33 @@ static int nvme_init_subsystem(nvme_subsystem_t s, const char *name,
        return 0;
 }
 
-static int nvme_scan_subsystem(struct nvme_root *r, char *name,
+/*
+ * nvme_scan_subsystem
+ *
+ * This is slightly non-obvious, as the underlying topology is
+ * _not_ a tree.
+ * Rather we're having 'hosts' which have several 'controllers',
+ * each controller is exposed by a 'subsystem'.
+ * Note that each 'subsystem' may expose several 'controllers',
+ * but these do not necessarily belong to the same 'host'.
+ * In our abstraction we have a tree host->subsystem->controller,
+ * so to flatten the underlying topology into our tree we have
+ * to duplicate the 'subsystem' objects, one for each host.
+ * But that doesn't matter as the 'subsystem' list is per-host
+ * anyway, so the duplicate objects will never be seen by
+ * other hosts.
+ */
+static int nvme_scan_subsystem(struct nvme_host *h, char *name,
                               nvme_scan_filter_t f)
 {
        struct nvme_subsystem *s;
        char *path, *subsysnqn;
-       char *hostnqn, *hostid = NULL;
-       nvme_host_t h = NULL;
        int ret;
 
        ret = asprintf(&path, "%s/%s", nvme_subsys_sysfs_dir, name);
        if (ret < 0)
                return ret;
 
-       hostnqn = nvme_get_attr(path, "hostnqn");
-       if (hostnqn) {
-               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) {
-               errno = ENOMEM;
-               return -1;
-       }
        subsysnqn = nvme_get_attr(path, "subsysnqn");
        if (!subsysnqn) {
                errno = ENODEV;
@@ -1203,13 +1250,34 @@ nvme_ctrl_t nvme_scan_ctrl(nvme_root_t r, const char *name)
 static int nvme_subsystem_scan_ctrl(struct nvme_subsystem *s, char *name)
 {
        nvme_ctrl_t c;
-       char *path;
+       char *path, *hostnqn, *hostid;
 
        if (asprintf(&path, "%s/%s", s->sysfs_dir, name) < 0) {
                errno = ENOMEM;
                return -1;
        }
-
+       hostnqn = nvme_get_attr(path, "hostnqn");
+       if (hostnqn) {
+               if (strcmp(s->h->hostnqn, hostnqn)) {
+                       nvme_msg(LOG_DEBUG,
+                                "%s: skip ctrl %s for non-matching host %s\n",
+                                __func__, name, s->h->hostnqn);
+                       free(path);
+                       return 0;
+               }
+               free(hostnqn);
+       }
+       hostid = nvme_get_attr(path, "hostid");
+       if (hostid) {
+               if (s->h->hostid && strcmp(s->h->hostid, hostid)) {
+                       nvme_msg(LOG_DEBUG,
+                                "%s: skip ctrl %s for non-matching host %s\n",
+                                __func__, name, s->h->hostid);
+                       free(path);
+                       return 0;
+               }
+               free(hostid);
+       }
        c = nvme_ctrl_alloc(s, path, name);
        if (!c) {
                free(path);
@@ -1217,7 +1285,7 @@ static int nvme_subsystem_scan_ctrl(struct nvme_subsystem *s, char *name)
        }
        nvme_ctrl_scan_namespaces(c);
        nvme_ctrl_scan_paths(c);
-
+       free(path);
        return 0;
 }