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;
}
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;
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) {
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;
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)
/* 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);
char *queue_count;
char *serial;
char *sqsize;
+ char *hostnqn;
+ char *hostid;
char *transport;
char *subsysnqn;
char *traddr;
char *name;
char *sysfs_dir;
char *subsysnqn;
+ char *model;
+ char *serial;
+ char *firmware;
};
struct nvme_host {
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;
}
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;
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;
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);
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)
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);
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)
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)
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;
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");
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))
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;
}
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;
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;
+}