From: Hannes Reinecke Date: Fri, 13 Aug 2021 09:58:08 +0000 (+0200) Subject: tree: rework topology for multiple hosts X-Git-Tag: v1.0-rc0~110^2~2 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=aad3881a446560e6add56202e8029183d5dd825d;p=users%2Fsagi%2Flibnvme.git tree: rework topology for multiple hosts 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 --- diff --git a/src/nvme/filters.c b/src/nvme/filters.c index 06137f5f..0f6202f9 100644 --- a/src/nvme/filters.c +++ b/src/nvme/filters.c @@ -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, diff --git a/src/nvme/filters.h b/src/nvme/filters.h index 52318311..6ff84735 100644 --- a/src/nvme/filters.h +++ b/src/nvme/filters.h @@ -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: diff --git a/src/nvme/tree.c b/src/nvme/tree.c index 264cdf35..149c8ec0 100644 --- a/src/nvme/tree.c +++ b/src/nvme/tree.c @@ -34,9 +34,10 @@ 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; }