Blame nvme-topology.c

Packit dd4ba5
#include <stdio.h>
Packit dd4ba5
#include <stdlib.h>
Packit dd4ba5
#include <errno.h>
Packit dd4ba5
#include <unistd.h>
Packit dd4ba5
#include <fcntl.h>
Packit dd4ba5
#include <sys/stat.h>
Packit dd4ba5
#include <sys/mman.h>
Packit dd4ba5
Packit dd4ba5
#include "nvme.h"
Packit dd4ba5
#include "nvme-ioctl.h"
Packit dd4ba5
Packit dd4ba5
static const char *dev = "/dev/";
Packit dd4ba5
static const char *subsys_dir = "/sys/class/nvme-subsystem/";
Packit dd4ba5
Packit dd4ba5
char *get_nvme_subsnqn(char *path)
Packit dd4ba5
{
Packit dd4ba5
	char sspath[320], *subsysnqn;
Packit dd4ba5
	int fd, ret;
Packit dd4ba5
Packit dd4ba5
	snprintf(sspath, sizeof(sspath), "%s/subsysnqn", path);
Packit dd4ba5
Packit dd4ba5
	fd = open(sspath, O_RDONLY);
Packit dd4ba5
	if (fd < 0) {
Packit dd4ba5
		fprintf(stderr, "Failed to open %s: %s\n",
Packit dd4ba5
				sspath, strerror(errno));
Packit dd4ba5
		return NULL;
Packit dd4ba5
	}
Packit dd4ba5
Packit dd4ba5
	subsysnqn = calloc(1, 256);
Packit dd4ba5
	if (!subsysnqn)
Packit dd4ba5
		goto close_fd;
Packit dd4ba5
Packit dd4ba5
	ret = read(fd, subsysnqn, 256);
Packit dd4ba5
	if (ret < 0) {
Packit dd4ba5
		fprintf(stderr, "Failed to read %s: %s\n", sspath,
Packit dd4ba5
				strerror(errno));
Packit dd4ba5
		free(subsysnqn);
Packit dd4ba5
		subsysnqn = NULL;
Packit dd4ba5
	} else if (subsysnqn[strlen(subsysnqn) - 1] == '\n') {
Packit dd4ba5
		subsysnqn[strlen(subsysnqn) - 1] = '\0';
Packit dd4ba5
	}
Packit dd4ba5
Packit dd4ba5
close_fd:
Packit dd4ba5
	close(fd);
Packit dd4ba5
	return subsysnqn;
Packit dd4ba5
}
Packit dd4ba5
Packit dd4ba5
char *nvme_get_ctrl_attr(char *path, const char *attr)
Packit dd4ba5
{
Packit dd4ba5
	char *attrpath, *value;
Packit dd4ba5
	ssize_t ret;
Packit dd4ba5
	int fd, i;
Packit dd4ba5
Packit dd4ba5
	ret = asprintf(&attrpath, "%s/%s", path, attr);
Packit dd4ba5
	if (ret < 0)
Packit dd4ba5
		return NULL;
Packit dd4ba5
Packit dd4ba5
	value = calloc(1, 1024);
Packit dd4ba5
	if (!value)
Packit dd4ba5
		goto err_free_path;
Packit dd4ba5
Packit dd4ba5
	fd = open(attrpath, O_RDONLY);
Packit dd4ba5
	if (fd < 0)
Packit dd4ba5
		goto err_free_value;
Packit dd4ba5
Packit dd4ba5
	ret = read(fd, value, 1024);
Packit dd4ba5
	if (ret < 0) {
Packit dd4ba5
		fprintf(stderr, "read :%s :%s\n", attrpath, strerror(errno));
Packit dd4ba5
		goto err_close_fd;
Packit dd4ba5
	}
Packit dd4ba5
Packit dd4ba5
	if (value[strlen(value) - 1] == '\n')
Packit dd4ba5
		value[strlen(value) - 1] = '\0';
Packit dd4ba5
Packit dd4ba5
	for (i = 0; i < strlen(value); i++) {
Packit dd4ba5
		if (value[i] == ',' )
Packit dd4ba5
			value[i] = ' ';
Packit dd4ba5
	}
Packit dd4ba5
Packit dd4ba5
	close(fd);
Packit dd4ba5
	free(attrpath);
Packit dd4ba5
	return value;
Packit dd4ba5
err_close_fd:
Packit dd4ba5
	close(fd);
Packit dd4ba5
err_free_value:
Packit dd4ba5
	free(value);
Packit dd4ba5
err_free_path:
Packit dd4ba5
	free(attrpath);
Packit dd4ba5
	return NULL;
Packit dd4ba5
}
Packit dd4ba5
Packit dd4ba5
static char *path_trim_last(char *path, char needle)
Packit dd4ba5
{
Packit dd4ba5
	int i;
Packit dd4ba5
	i = strlen(path);
Packit dd4ba5
	if (i>0 && path[i-1] == needle)		// remove trailing slash
Packit dd4ba5
		path[--i] = 0;
Packit dd4ba5
	for (; i>0; i--)
Packit dd4ba5
		if (path[i] == needle) {
Packit dd4ba5
			path[i] = 0;
Packit dd4ba5
			return path+i+1;
Packit dd4ba5
	}
Packit dd4ba5
	return NULL;
Packit dd4ba5
}
Packit dd4ba5
Packit dd4ba5
static void legacy_get_pci_bdf(char *node, char *bdf)
Packit dd4ba5
{
Packit dd4ba5
	int ret;
Packit dd4ba5
	char path[264], nodetmp[264];
Packit dd4ba5
	struct stat st;
Packit dd4ba5
	char *p, *__path = path;
Packit dd4ba5
Packit dd4ba5
	bdf[0] = 0;
Packit dd4ba5
	strcpy(nodetmp, node);
Packit dd4ba5
	p = path_trim_last(nodetmp, '/');
Packit dd4ba5
	sprintf(path, "/sys/block/%s/device", p);
Packit dd4ba5
	ret = readlink(path, nodetmp, sizeof(nodetmp));
Packit dd4ba5
	if (ret <= 0)
Packit dd4ba5
		return;
Packit dd4ba5
	nodetmp[ret] = 0;
Packit dd4ba5
	/* The link value is either "device -> ../../../0000:86:00.0" or "device -> ../../nvme0" */
Packit dd4ba5
	(void) path_trim_last(path, '/');
Packit dd4ba5
	sprintf(path+strlen(path), "/%s/device", nodetmp);
Packit dd4ba5
	ret = stat(path, &st);
Packit dd4ba5
	if (ret < 0)
Packit dd4ba5
		return;
Packit dd4ba5
	if ((st.st_mode & S_IFLNK) == 0) {
Packit dd4ba5
		/* follow the second link to get the PCI address */
Packit dd4ba5
		ret = readlink(path, __path, sizeof(path));
Packit dd4ba5
		if (ret <= 0)
Packit dd4ba5
			return;
Packit dd4ba5
		path[ret] = 0;
Packit dd4ba5
	}
Packit dd4ba5
	else
Packit dd4ba5
		(void) path_trim_last(path, '/');
Packit dd4ba5
Packit dd4ba5
	p = path_trim_last(path, '/');
Packit dd4ba5
	if (p && strlen(p) == 12)
Packit dd4ba5
		strcpy(bdf, p);
Packit dd4ba5
}
Packit dd4ba5
Packit dd4ba5
static int scan_namespace(struct nvme_namespace *n)
Packit dd4ba5
{
Packit dd4ba5
	int ret, fd;
Packit dd4ba5
	char *path;
Packit dd4ba5
Packit dd4ba5
	ret = asprintf(&path, "%s%s", dev, n->name);
Packit dd4ba5
	if (ret < 0)
Packit dd4ba5
		return ret;
Packit dd4ba5
Packit dd4ba5
	fd = open(path, O_RDONLY);
Packit dd4ba5
	if (fd < 0)
Packit dd4ba5
		goto free;
Packit dd4ba5
Packit dd4ba5
	n->nsid = nvme_get_nsid(fd);
Packit dd4ba5
	if (n->nsid < 0)
Packit dd4ba5
		goto close_fd;
Packit dd4ba5
Packit dd4ba5
	ret = nvme_identify_ns(fd, n->nsid, 0, &n->ns);
Packit dd4ba5
	if (ret < 0)
Packit dd4ba5
		goto close_fd;
Packit dd4ba5
close_fd:
Packit dd4ba5
	close(fd);
Packit dd4ba5
free:
Packit dd4ba5
	free(path);
Packit dd4ba5
	return 0;
Packit dd4ba5
}
Packit dd4ba5
Packit dd4ba5
static char *get_nvme_ctrl_path_ana_state(char *path, int nsid)
Packit dd4ba5
{
Packit dd4ba5
	struct dirent **paths;
Packit dd4ba5
	char *ana_state;
Packit dd4ba5
	int i, n;
Packit dd4ba5
Packit dd4ba5
	ana_state = calloc(1, 16);
Packit dd4ba5
	if (!ana_state)
Packit dd4ba5
		return NULL;
Packit dd4ba5
Packit dd4ba5
	n = scandir(path, &paths, scan_ctrl_paths_filter, alphasort);
Packit dd4ba5
	if (n <= 0) {
Packit dd4ba5
		free(ana_state);
Packit dd4ba5
		return NULL;
Packit dd4ba5
	}
Packit dd4ba5
	for (i = 0; i < n; i++) {
Packit dd4ba5
		int id, cntlid, ns, fd;
Packit dd4ba5
		char *ctrl_path;
Packit dd4ba5
		ssize_t ret;
Packit dd4ba5
Packit dd4ba5
		if (sscanf(paths[i]->d_name, "nvme%dc%dn%d",
Packit dd4ba5
			   &id, &cntlid, &ns) != 3) {
Packit dd4ba5
			if (sscanf(paths[i]->d_name, "nvme%dn%d",
Packit dd4ba5
				   &id, &ns) != 2) {
Packit dd4ba5
				continue;
Packit dd4ba5
			}
Packit dd4ba5
		}
Packit dd4ba5
		if (ns != nsid)
Packit dd4ba5
			continue;
Packit dd4ba5
Packit dd4ba5
		ret = asprintf(&ctrl_path, "%s/%s/ana_state",
Packit dd4ba5
			       path, paths[i]->d_name);
Packit dd4ba5
		if (ret < 0) {
Packit dd4ba5
			free(ana_state);
Packit dd4ba5
			ana_state = NULL;
Packit dd4ba5
			break;
Packit dd4ba5
		}
Packit dd4ba5
		fd = open(ctrl_path, O_RDONLY);
Packit dd4ba5
		if (fd < 0) {
Packit dd4ba5
			free(ctrl_path);
Packit dd4ba5
			free(ana_state);
Packit dd4ba5
			ana_state = NULL;
Packit dd4ba5
			break;
Packit dd4ba5
		}
Packit dd4ba5
		ret = read(fd, ana_state, 16);
Packit dd4ba5
		if (ret < 0) {
Packit dd4ba5
			fprintf(stderr, "Failed to read ANA state from %s\n",
Packit dd4ba5
				ctrl_path);
Packit dd4ba5
			free(ana_state);
Packit dd4ba5
			ana_state = NULL;
Packit dd4ba5
		} else if (ana_state[strlen(ana_state) - 1] == '\n')
Packit dd4ba5
			ana_state[strlen(ana_state) - 1] = '\0';
Packit dd4ba5
		close(fd);
Packit dd4ba5
		free(ctrl_path);
Packit dd4ba5
		break;
Packit dd4ba5
	}
Packit dd4ba5
	for (i = 0; i < n; i++)
Packit dd4ba5
		free(paths[i]);
Packit dd4ba5
	free(paths);
Packit dd4ba5
	return ana_state;
Packit dd4ba5
}
Packit dd4ba5
Packit dd4ba5
static int scan_ctrl(struct nvme_ctrl *c, char *p, __u32 ns_instance)
Packit dd4ba5
{
Packit dd4ba5
	struct nvme_namespace *n;
Packit dd4ba5
	struct dirent **ns;
Packit dd4ba5
	char *path;
Packit dd4ba5
	int i, fd, ret;
Packit dd4ba5
Packit dd4ba5
	ret = asprintf(&path, "%s/%s", p, c->name);
Packit dd4ba5
	if (ret < 0)
Packit dd4ba5
		return ret;
Packit dd4ba5
Packit dd4ba5
	c->address = nvme_get_ctrl_attr(path, "address");
Packit dd4ba5
	c->transport = nvme_get_ctrl_attr(path, "transport");
Packit dd4ba5
	c->state = nvme_get_ctrl_attr(path, "state");
Packit dd4ba5
	c->hostnqn = nvme_get_ctrl_attr(path, "hostnqn");
Packit dd4ba5
	c->hostid = nvme_get_ctrl_attr(path, "hostid");
Packit dd4ba5
Packit dd4ba5
	if (ns_instance)
Packit dd4ba5
		c->ana_state = get_nvme_ctrl_path_ana_state(path, ns_instance);
Packit dd4ba5
Packit dd4ba5
	ret = scandir(path, &ns, scan_namespace_filter, alphasort);
Packit dd4ba5
	if (ret == -1) {
Packit dd4ba5
		fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
Packit dd4ba5
		return errno;
Packit dd4ba5
	}
Packit dd4ba5
Packit dd4ba5
	c->nr_namespaces = ret;
Packit dd4ba5
	c->namespaces = calloc(c->nr_namespaces, sizeof(*n));
Packit dd4ba5
	for (i = 0; i < c->nr_namespaces; i++) {
Packit dd4ba5
		n = &c->namespaces[i];
Packit dd4ba5
		n->name = strdup(ns[i]->d_name);
Packit dd4ba5
		n->ctrl = c;
Packit dd4ba5
		scan_namespace(n);
Packit dd4ba5
	}
Packit dd4ba5
Packit dd4ba5
	while (i--)
Packit dd4ba5
		free(ns[i]);
Packit dd4ba5
	free(ns);
Packit dd4ba5
	free(path);
Packit dd4ba5
Packit dd4ba5
	ret = asprintf(&path, "%s%s", dev, c->name);
Packit dd4ba5
	if (ret < 0)
Packit dd4ba5
		return ret;
Packit dd4ba5
Packit dd4ba5
	fd = open(path, O_RDONLY);
Packit dd4ba5
	if (fd < 0) {
Packit dd4ba5
		fprintf(stderr, "Failed to open %s\n", path);
Packit dd4ba5
		goto free;
Packit dd4ba5
	}
Packit dd4ba5
Packit dd4ba5
	ret = nvme_identify_ctrl(fd, &c->id);
Packit dd4ba5
	if (ret < 0)
Packit dd4ba5
		goto close_fd;
Packit dd4ba5
close_fd:
Packit dd4ba5
	close(fd);
Packit dd4ba5
free:
Packit dd4ba5
	free(path);
Packit dd4ba5
	return 0;
Packit dd4ba5
}
Packit dd4ba5
Packit dd4ba5
static int scan_subsystem(struct nvme_subsystem *s, __u32 ns_instance)
Packit dd4ba5
{
Packit dd4ba5
	struct dirent **ctrls, **ns;
Packit dd4ba5
	struct nvme_namespace *n;
Packit dd4ba5
	struct nvme_ctrl *c;
Packit dd4ba5
	int i, ret;
Packit dd4ba5
	char *path;
Packit dd4ba5
Packit dd4ba5
	ret = asprintf(&path, "%s%s", subsys_dir, s->name);
Packit dd4ba5
	if (ret < 0)
Packit dd4ba5
		return ret;
Packit dd4ba5
Packit dd4ba5
	s->subsysnqn = get_nvme_subsnqn(path);
Packit dd4ba5
	ret = scandir(path, &ctrls, scan_ctrls_filter, alphasort);
Packit dd4ba5
	if (ret == -1) {
Packit dd4ba5
		fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
Packit dd4ba5
		return errno;
Packit dd4ba5
	}
Packit dd4ba5
	s->nr_ctrls = ret;
Packit dd4ba5
	s->ctrls = calloc(s->nr_ctrls, sizeof(*c));
Packit dd4ba5
	for (i = 0; i < s->nr_ctrls; i++) {
Packit dd4ba5
		c = &s->ctrls[i];
Packit dd4ba5
		c->name = strdup(ctrls[i]->d_name);
Packit dd4ba5
		c->subsys = s;
Packit dd4ba5
		scan_ctrl(c, path, ns_instance);
Packit dd4ba5
	}
Packit dd4ba5
Packit dd4ba5
	while (i--)
Packit dd4ba5
		free(ctrls[i]);
Packit dd4ba5
	free(ctrls);
Packit dd4ba5
Packit dd4ba5
	ret = scandir(path, &ns, scan_namespace_filter, alphasort);
Packit dd4ba5
	if (ret == -1) {
Packit dd4ba5
		fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
Packit dd4ba5
		return errno;
Packit dd4ba5
	}
Packit dd4ba5
Packit dd4ba5
	s->nr_namespaces = ret;
Packit dd4ba5
	s->namespaces = calloc(s->nr_namespaces, sizeof(*n));
Packit dd4ba5
	for (i = 0; i < s->nr_namespaces; i++) {
Packit dd4ba5
		n = &s->namespaces[i];
Packit dd4ba5
		n->name = strdup(ns[i]->d_name);
Packit dd4ba5
		n->ctrl = &s->ctrls[0];
Packit dd4ba5
		scan_namespace(n);
Packit dd4ba5
	}
Packit dd4ba5
Packit dd4ba5
	while (i--)
Packit dd4ba5
		free(ns[i]);
Packit dd4ba5
	free(ns);
Packit dd4ba5
Packit dd4ba5
	free(path);
Packit dd4ba5
	return 0;
Packit dd4ba5
}
Packit dd4ba5
Packit dd4ba5
static int verify_legacy_ns(struct nvme_namespace *n)
Packit dd4ba5
{
Packit dd4ba5
	struct nvme_ctrl *c = n->ctrl;
Packit dd4ba5
	struct nvme_id_ctrl id;
Packit dd4ba5
	char *path;
Packit dd4ba5
	int ret, fd;
Packit dd4ba5
Packit dd4ba5
	ret = asprintf(&path, "%s%s", dev, n->name);
Packit dd4ba5
	if (ret < 0)
Packit dd4ba5
		return ret;
Packit dd4ba5
Packit dd4ba5
	if (!n->ctrl->transport && !n->ctrl->address) {
Packit dd4ba5
		char tmp_address[64] = "";
Packit dd4ba5
		legacy_get_pci_bdf(path, tmp_address);
Packit dd4ba5
		if (tmp_address[0]) {
Packit dd4ba5
			if (asprintf(&n->ctrl->transport, "pcie") < 0)
Packit dd4ba5
				return -1;
Packit dd4ba5
			if (asprintf(&n->ctrl->address, "%s", tmp_address) < 0)
Packit dd4ba5
				return -1;
Packit dd4ba5
		}
Packit dd4ba5
	}
Packit dd4ba5
Packit dd4ba5
	fd = open(path, O_RDONLY);
Packit dd4ba5
	free (path);
Packit dd4ba5
Packit dd4ba5
	if (fd < 0)
Packit dd4ba5
		return fd;
Packit dd4ba5
Packit dd4ba5
	ret = nvme_identify_ctrl(fd, &id;;
Packit dd4ba5
	close(fd);
Packit dd4ba5
Packit dd4ba5
	if (ret)
Packit dd4ba5
		return ret;
Packit dd4ba5
Packit dd4ba5
	if (memcmp(id.mn, c->id.mn, sizeof(id.mn)) ||
Packit dd4ba5
	    memcmp(id.sn, c->id.sn, sizeof(id.sn)))
Packit dd4ba5
		return -ENODEV;
Packit dd4ba5
	return 0;
Packit dd4ba5
}
Packit dd4ba5
Packit dd4ba5
/*
Packit dd4ba5
 * For pre-subsystem enabled kernel. Topology information is limited, but we can
Packit dd4ba5
 * assume controller names are always a prefix to their namespaces, i.e. nvme0
Packit dd4ba5
 * is the controller to nvme0n1 for such older kernels. We will also assume
Packit dd4ba5
 * every controller is its own subsystem.
Packit dd4ba5
 */
Packit dd4ba5
static int legacy_list(struct nvme_topology *t)
Packit dd4ba5
{
Packit dd4ba5
	struct nvme_ctrl *c;
Packit dd4ba5
	struct nvme_subsystem *s;
Packit dd4ba5
	struct nvme_namespace *n;
Packit dd4ba5
	struct dirent **devices, **namespaces;
Packit dd4ba5
	int ret = 0, fd, i;
Packit dd4ba5
	char *path;
Packit dd4ba5
Packit dd4ba5
	t->nr_subsystems = scandir(dev, &devices, scan_ctrls_filter, alphasort);
Packit dd4ba5
	if (t->nr_subsystems < 0) {
Packit dd4ba5
		fprintf(stderr, "no NVMe device(s) detected.\n");
Packit dd4ba5
		return t->nr_subsystems;
Packit dd4ba5
	}
Packit dd4ba5
Packit dd4ba5
	t->subsystems = calloc(t->nr_subsystems, sizeof(*s));
Packit dd4ba5
	for (i = 0; i < t->nr_subsystems; i++) {
Packit dd4ba5
		int j;
Packit dd4ba5
Packit dd4ba5
		s = &t->subsystems[i];
Packit dd4ba5
		s->nr_ctrls = 1;
Packit dd4ba5
		s->ctrls = calloc(s->nr_ctrls, sizeof(*c));
Packit dd4ba5
		s->name = strdup(devices[i]->d_name);
Packit dd4ba5
		s->subsysnqn = strdup(s->name);
Packit dd4ba5
		s->nr_namespaces = 0;
Packit dd4ba5
Packit dd4ba5
		c = s->ctrls;
Packit dd4ba5
		c->name = strdup(s->name);
Packit dd4ba5
		sscanf(c->name, "nvme%d", &current_index);
Packit dd4ba5
		c->nr_namespaces = scandir(dev, &namespaces, scan_dev_filter,
Packit dd4ba5
					   alphasort);
Packit dd4ba5
		c->namespaces = calloc(c->nr_namespaces, sizeof(*n));
Packit dd4ba5
Packit dd4ba5
		ret = asprintf(&path, "%s%s", dev, c->name);
Packit dd4ba5
		if (ret < 0)
Packit dd4ba5
			continue;
Packit dd4ba5
		ret = 0;
Packit dd4ba5
Packit dd4ba5
		fd = open(path, O_RDONLY);
Packit dd4ba5
		if (fd > 0) {
Packit dd4ba5
			nvme_identify_ctrl(fd, &c->id);
Packit dd4ba5
			close(fd);
Packit dd4ba5
		}
Packit dd4ba5
		free(path);
Packit dd4ba5
Packit dd4ba5
		for (j = 0; j < c->nr_namespaces; j++) {
Packit dd4ba5
			n = &c->namespaces[j];
Packit dd4ba5
			n->name = strdup(namespaces[j]->d_name);
Packit dd4ba5
			n->ctrl = c;
Packit dd4ba5
			scan_namespace(n);
Packit dd4ba5
			ret = verify_legacy_ns(n);
Packit dd4ba5
			if (ret)
Packit dd4ba5
				goto free;
Packit dd4ba5
		}
Packit dd4ba5
		while (j--)
Packit dd4ba5
			free(namespaces[j]);
Packit dd4ba5
		free(namespaces);
Packit dd4ba5
	}
Packit dd4ba5
Packit dd4ba5
free:
Packit dd4ba5
	while (i--)
Packit dd4ba5
		free(devices[i]);
Packit dd4ba5
	free(devices);
Packit dd4ba5
	return ret;
Packit dd4ba5
}
Packit dd4ba5
Packit dd4ba5
static void free_ctrl(struct nvme_ctrl *c)
Packit dd4ba5
{
Packit dd4ba5
	int i;
Packit dd4ba5
Packit dd4ba5
	for (i = 0; i < c->nr_namespaces; i++) {
Packit dd4ba5
		struct nvme_namespace *n = &c->namespaces[i];
Packit dd4ba5
		free(n->name);
Packit dd4ba5
	}
Packit dd4ba5
	free(c->name);
Packit dd4ba5
	free(c->transport);
Packit dd4ba5
	free(c->address);
Packit dd4ba5
	free(c->state);
Packit dd4ba5
	free(c->hostnqn);
Packit dd4ba5
	free(c->hostid);
Packit dd4ba5
	free(c->ana_state);
Packit dd4ba5
	free(c->namespaces);
Packit dd4ba5
}
Packit dd4ba5
Packit dd4ba5
static void free_subsystem(struct nvme_subsystem *s)
Packit dd4ba5
{
Packit dd4ba5
	int i;
Packit dd4ba5
Packit dd4ba5
	for (i = 0; i < s->nr_ctrls; i++)
Packit dd4ba5
		free_ctrl(&s->ctrls[i]);
Packit dd4ba5
	for (i = 0; i < s->nr_namespaces; i++) {
Packit dd4ba5
		struct nvme_namespace *n = &s->namespaces[i];
Packit dd4ba5
		free(n->name);
Packit dd4ba5
	}
Packit dd4ba5
	free(s->name);
Packit dd4ba5
	free(s->subsysnqn);
Packit dd4ba5
	free(s->ctrls);
Packit dd4ba5
	free(s->namespaces);
Packit dd4ba5
}
Packit dd4ba5
Packit dd4ba5
int scan_subsystems(struct nvme_topology *t, const char *subsysnqn,
Packit dd4ba5
		    __u32 ns_instance)
Packit dd4ba5
{
Packit dd4ba5
	struct nvme_subsystem *s;
Packit dd4ba5
	struct dirent **subsys;
Packit dd4ba5
	int i, j = 0;
Packit dd4ba5
Packit dd4ba5
	t->nr_subsystems = scandir(subsys_dir, &subsys, scan_subsys_filter,
Packit dd4ba5
				   alphasort);
Packit dd4ba5
	if (t->nr_subsystems < 0)
Packit dd4ba5
		return legacy_list(t);
Packit dd4ba5
Packit dd4ba5
	t->subsystems = calloc(t->nr_subsystems, sizeof(*s));
Packit dd4ba5
	for (i = 0; i < t->nr_subsystems; i++) {
Packit dd4ba5
		s = &t->subsystems[j];
Packit dd4ba5
		s->name = strdup(subsys[i]->d_name);
Packit dd4ba5
		scan_subsystem(s, ns_instance);
Packit dd4ba5
Packit dd4ba5
		if (!subsysnqn || !strcmp(s->subsysnqn, subsysnqn))
Packit dd4ba5
			j++;
Packit dd4ba5
		else
Packit dd4ba5
			free_subsystem(s);
Packit dd4ba5
	}
Packit dd4ba5
	t->nr_subsystems = j;
Packit dd4ba5
Packit dd4ba5
	while (i--)
Packit dd4ba5
		free(subsys[i]);
Packit dd4ba5
	free(subsys);
Packit dd4ba5
	return 0;
Packit dd4ba5
}
Packit dd4ba5
Packit dd4ba5
void free_topology(struct nvme_topology *t)
Packit dd4ba5
{
Packit dd4ba5
	int i;
Packit dd4ba5
Packit dd4ba5
	for (i = 0; i < t->nr_subsystems; i++)
Packit dd4ba5
		free_subsystem(&t->subsystems[i]);
Packit dd4ba5
	free(t->subsystems);
Packit dd4ba5
}
Packit dd4ba5
Packit dd4ba5
char *nvme_char_from_block(char *dev)
Packit dd4ba5
{
Packit dd4ba5
	char *path = NULL;
Packit dd4ba5
	char buf[256] = {0};
Packit dd4ba5
	int ret, id, nsid;
Packit dd4ba5
Packit dd4ba5
	ret = sscanf(dev, "nvme%dn%d", &id, &nsid;;
Packit dd4ba5
	switch (ret) {
Packit dd4ba5
	case 1:
Packit dd4ba5
		return strdup(dev);
Packit dd4ba5
		break;
Packit dd4ba5
	case 2:
Packit dd4ba5
		if (asprintf(&path, "/sys/block/%s/device", dev) < 0)
Packit dd4ba5
			path = NULL;
Packit dd4ba5
		break;
Packit dd4ba5
	default:
Packit dd4ba5
		fprintf(stderr, "%s is not an nvme device\n", dev);
Packit dd4ba5
		return NULL;
Packit dd4ba5
	}
Packit dd4ba5
Packit dd4ba5
	if (!path)
Packit dd4ba5
		return NULL;
Packit dd4ba5
Packit dd4ba5
	ret = readlink(path, buf, sizeof(buf));
Packit dd4ba5
	if (ret > 0) {
Packit dd4ba5
		char *r = strdup(basename(buf));
Packit dd4ba5
Packit dd4ba5
		free(path);
Packit dd4ba5
		if (sscanf(r, "nvme%d", &id) != 1) {
Packit dd4ba5
			fprintf(stderr, "%s is not a physical nvme controller\n", r);
Packit dd4ba5
			free(r);
Packit dd4ba5
			r = NULL;
Packit dd4ba5
		}
Packit dd4ba5
		return r;
Packit dd4ba5
	}
Packit dd4ba5
Packit dd4ba5
	free(path);
Packit dd4ba5
	ret = asprintf(&path, "nvme%d", id);
Packit dd4ba5
	if (ret < 0)
Packit dd4ba5
		return NULL;
Packit dd4ba5
	return path;
Packit dd4ba5
}
Packit dd4ba5
Packit dd4ba5
void *mmap_registers(const char *dev)
Packit dd4ba5
{
Packit dd4ba5
	int fd;
Packit dd4ba5
	char *base, path[512];
Packit dd4ba5
	void *membase;
Packit dd4ba5
Packit dd4ba5
	base = nvme_char_from_block((char *)dev);
Packit dd4ba5
	if (!base)
Packit dd4ba5
		return NULL;
Packit dd4ba5
Packit dd4ba5
	sprintf(path, "/sys/class/nvme/%s/device/resource0", base);
Packit dd4ba5
	fd = open(path, O_RDONLY);
Packit dd4ba5
	if (fd < 0) {
Packit dd4ba5
		sprintf(path, "/sys/class/misc/%s/device/resource0", base);
Packit dd4ba5
		fd = open(path, O_RDONLY);
Packit dd4ba5
	}
Packit dd4ba5
	if (fd < 0) {
Packit dd4ba5
		fprintf(stderr, "%s did not find a pci resource, open failed %s\n",
Packit dd4ba5
				base, strerror(errno));
Packit dd4ba5
		free(base);
Packit dd4ba5
		return NULL;
Packit dd4ba5
	}
Packit dd4ba5
Packit dd4ba5
	membase = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd, 0);
Packit dd4ba5
	if (membase == MAP_FAILED) {
Packit dd4ba5
		fprintf(stderr, "%s failed to map. ", base);
Packit dd4ba5
		fprintf(stderr, "Did your kernel enable CONFIG_IO_STRICT_DEVMEM?\n");
Packit dd4ba5
		membase = NULL;
Packit dd4ba5
	}
Packit dd4ba5
Packit dd4ba5
	free(base);
Packit dd4ba5
	close(fd);
Packit dd4ba5
	return membase;
Packit dd4ba5
}
Packit dd4ba5