/
githubmirror
/
nvme-cli
Обзор
Документация
Войти
/
githubmirror
/
nvme-cli
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
libnvme/src/nvme/tree-linux.c
687 строк
14 KB
Martin Belanger
libnvme: rename sysfs to attrs and move generated files to a subdir
10 авг 2026, 11:04
10 авг 2026, 11:04
f3a13a9
Код
Авторство
О чём код?
// SPDX-License-Identifier: LGPL-2.1-or-later /* * This file is part of libnvme. * Copyright (c) 2020 Western Digital Corporation or its affiliates. * * Authors: Keith Busch <keith.busch@wdc.com> * Chaitanya Kulkarni <chaitanya.kulkarni@wdc.com> */ #include <errno.h> #include <fcntl.h> #include <libgen.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <time.h> #include <sys/stat.h> #include <sys/types.h> #include <ccan/endian/endian.h> #include <ccan/list/list.h> #include <compiler-attributes.h> #include <libnvme.h> #include "cleanup-linux.h" #include "cleanup.h" #include "private-fabrics.h" #include "private-tree.h" #include "private.h" #include "util.h" #define PATH_UUID_IBM "/proc/device-tree/ibm,partition-uuid" #define PATH_SYSFS_BLOCK "/sys/block" #define PATH_SYSFS_SLOTS "/sys/bus/pci/slots" #define PATH_SYSFS_NVME_SUBSYSTEM "/sys/class/nvme-subsystem" #define PATH_SYSFS_NVME "/sys/class/nvme" #define PATH_DMI_ENTRIES "/sys/firmware/dmi/entries" static const char *make_sysfs_dir(struct libnvme_global_ctx *ctx, const char *path) { char *str; if (!ctx || !ctx->test_sysfs_dir) return path; if (asprintf(&str, "%s%s", ctx->test_sysfs_dir, path) < 0) return NULL; return str; } const char *libnvme_subsys_sysfs_dir(struct libnvme_global_ctx *ctx) { static const char *str; if (str) return str; return str = make_sysfs_dir(ctx, PATH_SYSFS_NVME_SUBSYSTEM); } const char *libnvme_ctrl_sysfs_dir(struct libnvme_global_ctx *ctx) { static const char *str; if (str) return str; return str = make_sysfs_dir(ctx, PATH_SYSFS_NVME); } const char *libnvme_ns_sysfs_dir(struct libnvme_global_ctx *ctx) { static const char *str; if (str) return str; return str = make_sysfs_dir(ctx, PATH_SYSFS_BLOCK); } const char *libnvme_slots_sysfs_dir(struct libnvme_global_ctx *ctx) { static const char *str; if (str) return str; return str = make_sysfs_dir(ctx, PATH_SYSFS_SLOTS); } const char *libnvme_uuid_ibm_filename(void) { static const char *str; if (str) return str; return str = make_sysfs_dir(NULL, PATH_UUID_IBM); } const char *libnvme_dmi_entries_dir(void) { static const char *str; if (str) return str; return str = make_sysfs_dir(NULL, PATH_DMI_ENTRIES); } static int __nvme_set_attr(const char *path, const char *value) { __cleanup_fd int fd = -1; fd = open(path, O_WRONLY); if (fd < 0) { #if 0 libnvme_msg(LIBNVME_LOG_DEBUG, "Failed to open %s: %s\n", path, strerror(errno)); #endif return -errno; } return write(fd, value, strlen(value)); } int libnvme_set_attr(const char *dir, const char *attr, const char *value) { __cleanup_free char *path = NULL; int ret; ret = asprintf(&path, "%s/%s", dir, attr); if (ret < 0) return -ENOMEM; return __nvme_set_attr(path, value); } static char *__nvme_get_attr(const char *path) { char value[4096] = { 0 }; int ret, fd; int saved_errno; fd = open(path, O_RDONLY); if (fd < 0) return NULL; ret = read(fd, value, sizeof(value) - 1); saved_errno = errno; close(fd); if (ret < 0) { errno = saved_errno; return NULL; } errno = 0; if (!strlen(value)) return NULL; if (value[strlen(value) - 1] == '\n') value[strlen(value) - 1] = '\0'; while (strlen(value) > 0 && value[strlen(value) - 1] == ' ') value[strlen(value) - 1] = '\0'; return strlen(value) ? strdup(value) : NULL; } __shr_public char *libnvme_get_attr(const char *dir, const char *attr) { __cleanup_free char *path = NULL; int ret; ret = asprintf(&path, "%s/%s", dir, attr); if (ret < 0) return NULL; return __nvme_get_attr(path); } __shr_public char *libnvme_get_subsys_attr( libnvme_subsystem_t s, const char *attr) { return libnvme_get_attr(libnvme_subsystem_get_sysfs_dir(s), attr); } __shr_public char *libnvme_get_ctrl_attr(libnvme_ctrl_t c, const char *attr) { return libnvme_get_attr(libnvme_ctrl_get_sysfs_dir(c), attr); } __shr_public char *libnvme_get_ns_attr(libnvme_ns_t n, const char *attr) { return libnvme_get_attr(libnvme_ns_get_sysfs_dir(n), attr); } __shr_public char *libnvme_get_path_attr(libnvme_path_t p, const char *attr) { return libnvme_get_attr(libnvme_path_get_sysfs_dir(p), attr); } __shr_public const char *libnvme_ctrl_get_state(libnvme_ctrl_t c) { char *state = c->state; c->state = libnvme_get_ctrl_attr(c, "state"); free(state); return c->state; } static int libnvme_ctrl_lookup_subsystem_name(struct libnvme_global_ctx *ctx, const char *ctrl_name, char **name) { const char *subsys_dir = libnvme_subsys_sysfs_dir(ctx); __cleanup_dirents struct dirents subsys = {}; int i; subsys.num = libnvme_scan_subsystems(ctx, &subsys.ents); if (subsys.num < 0) return subsys.num; for (i = 0; i < subsys.num; i++) { struct stat st; __cleanup_free char *path = NULL; if (asprintf(&path, "%s/%s/%s", subsys_dir, subsys.ents[i]->d_name, ctrl_name) < 0) return -ENOMEM; libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "lookup subsystem %s\n", path); if (stat(path, &st) < 0) { continue; } *name = strdup(subsys.ents[i]->d_name); if (!*name) return -ENOMEM; return 0; } return -ENOENT; } int libnvme_reconfigure_ctrl(struct libnvme_global_ctx *ctx, libnvme_ctrl_t c, const char *path, const char *name) { DIR *d; /* * It's necesssary to release any resources first because a ctrl * can be reused. */ libnvme_ctrl_release_transport_handle(c); FREE_CTRL_ATTR(c->name); FREE_CTRL_ATTR(c->sysfs_dir); FREE_CTRL_ATTR(c->state); libnvme_ctrl_attrs_reset(c->attrs); d = opendir(path); if (!d) { libnvme_msg(ctx, LIBNVME_LOG_ERR, "Failed to open ctrl dir %s, error %d\n", path, errno); return -ENODEV; } closedir(d); c->hdl = NULL; c->name = shr_xstrdup(name); c->sysfs_dir = shr_xstrdup(path); c->state = libnvme_get_ctrl_attr(c, "state"); libnvmf_read_sysfs_fabrics_attrs(ctx, c); return 0; } __shr_public int libnvme_init_ctrl( libnvme_host_t h, libnvme_ctrl_t c, int instance) { __cleanup_free char *subsys_name = NULL, *name = NULL, *path = NULL; libnvme_subsystem_t s; int ret; ret = asprintf(&name, "nvme%d", instance); if (ret < 0) return -ENOMEM; ret = asprintf(&path, "%s/%s", libnvme_ctrl_sysfs_dir(h->ctx), name); if (ret < 0) return -ENOMEM; ret = libnvme_reconfigure_ctrl(h->ctx, c, path, name); if (ret < 0) return ret; c->address = libnvme_get_attr(path, "address"); if (!c->address && strcmp(c->transport, "loop")) return -ENVME_CONNECT_INVAL_TR; ret = libnvme_ctrl_lookup_subsystem_name(h->ctx, name, &subsys_name); if (ret) { libnvme_msg(h->ctx, LIBNVME_LOG_ERR, "Failed to lookup subsystem name for %s\n", c->name); return ENVME_CONNECT_LOOKUP_SUBSYS_NAME; } ret = libnvme_get_subsystem(h->ctx, h, subsys_name, c->subsysnqn, &s); if (ret) { libnvme_msg(h->ctx, LIBNVME_LOG_ERR, "Failed to lookup subsystem %s\n", subsys_name); return -ENVME_CONNECT_LOOKUP_SUBSYS; } if (s->subsystype && !strcmp(s->subsystype, "discovery")) c->discovery_ctrl = true; c->s = s; list_add_tail(&s->ctrls, &c->entry); return ret; } __shr_public int libnvme_scan_ctrl( struct libnvme_global_ctx *ctx, const char *name, libnvme_ctrl_t *cp) { __cleanup_free char *subsysnqn = NULL, *subsysname = NULL; __cleanup_free char *hostnqn = NULL, *hostid = NULL; __cleanup_free char *path = NULL; char *host_key; libnvme_host_t h; libnvme_subsystem_t s; libnvme_ctrl_t c; int ret; libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "scan controller %s\n", name); ret = asprintf(&path, "%s/%s", libnvme_ctrl_sysfs_dir(ctx), name); if (ret < 0) return -ENOMEM; hostnqn = libnvme_get_attr(path, "hostnqn"); hostid = libnvme_get_attr(path, "hostid"); if (!hostnqn) hostnqn = shr_xstrdup(ctx->hostnqn); if (!hostid) hostid = shr_xstrdup(ctx->hostid); ret = libnvme_get_host(ctx, hostnqn, hostid, &h); if (ret) return ret; host_key = libnvme_get_attr(path, "dhchap_secret"); if (host_key && strcmp(host_key, "none")) { free(h->dhchap_host_key); h->dhchap_host_key = host_key; host_key = NULL; } free(host_key); subsysnqn = libnvme_get_attr(path, "subsysnqn"); if (!subsysnqn) return -ENXIO; ret = libnvme_ctrl_lookup_subsystem_name(ctx, name, &subsysname); if (ret) { libnvme_msg(ctx, LIBNVME_LOG_DEBUG, "failed to lookup subsystem for controller %s\n", name); return ret; } ret = libnvme_get_subsystem(h->ctx, h, subsysname, subsysnqn, &s); if (ret) return ret; ret = libnvme_ctrl_alloc(ctx, s, path, name, &c); if (ret) return ret; ret = libnvme_ctrl_scan_paths(ctx, c); if (ret) { libnvme_free_ctrl(c); return ret; } ret = libnvme_ctrl_scan_namespaces(ctx, c); if (ret) { libnvme_free_ctrl(c); return ret; } *cp = c; return 0; } static int libnvme_strtou32(const char *str, void *res) { char *endptr; __u32 v; errno = 0; v = strtol(str, &endptr, 0); if (errno != 0) return -errno; if (endptr == str) { /* no digits found */ return -EINVAL; } *(__u32 *)res = v; return 0; } struct sysfs_attr_table { void *var; int (*parse)(const char *str, void *res); bool mandatory; const char *name; }; #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) static int parse_attrs(const char *path, struct sysfs_attr_table *tbl, int size) { char *str; int ret, i; for (i = 0; i < size; i++) { struct sysfs_attr_table *e = &tbl[i]; str = libnvme_get_attr(path, e->name); if (!str) { if (!e->mandatory) continue; return -ENOENT; } ret = e->parse(str, e->var); free(str); if (ret) return ret; } return 0; } int libnvme_ns_init(const char *path, struct libnvme_ns *ns) { int ret; struct sysfs_attr_table base[] = { { &ns->nsid, libnvme_strtou32, true, "nsid" }, }; ret = parse_attrs(path, base, ARRAY_SIZE(base)); if (ret) return ret; ns->attrs = libnvme_ns_attrs_alloc(); if (!ns->attrs) return -ENOMEM; return 0; } static void libnvme_ns_set_generic_name(struct libnvme_ns *n, const char *name) { char generic_name[PATH_MAX]; int instance, head_instance; int ret; ret = sscanf(name, "nvme%dn%d", &instance, &head_instance); if (ret != 2) return; sprintf(generic_name, "ng%dn%d", instance, head_instance); n->generic_name = strdup(generic_name); } int libnvme_ns_open(struct libnvme_global_ctx *ctx, const char *sys_path, const char *name, libnvme_ns_t *ns) { int ret; struct libnvme_ns *n; struct libnvme_ns_head *head; struct stat arg; __cleanup_free char *path = NULL; n = calloc(1, sizeof(*n)); if (!n) return -ENOMEM; head = calloc(1, sizeof(*head)); if (!head) { free(n); return -ENOMEM; } head->n = n; list_head_init(&head->paths); ret = asprintf(&path, "%s/%s", sys_path, "multipath"); if (ret < 0) { ret = -ENOMEM; goto free_ns_head; } /* * The sysfs-dir "multipath" is available only when nvme multipath * is configured and we're running kernel version >= 6.14. */ ret = stat(path, &arg); if (ret == 0) { head->sysfs_dir = path; path = NULL; } else head->sysfs_dir = NULL; n->ctx = ctx; n->head = head; n->hdl = NULL; n->name = strdup(name); libnvme_ns_set_generic_name(n, name); ret = libnvme_ns_init(sys_path, n); if (ret) goto free_ns; list_node_init(&n->entry); libnvme_ns_release_transport_handle(n); *ns = n; return 0; free_ns: free(n->generic_name); free(n->name); free_ns_head: free(head); free(n); return ret; } static inline bool libnvme_ns_is_generic(const char *name) { int instance, head_instance; if (sscanf(name, "ng%dn%d", &instance, &head_instance) != 2) return false; return true; } static char *libnvme_ns_generic_to_blkdev(const char *generic) { int instance, head_instance; char blkdev[PATH_MAX]; if (!libnvme_ns_is_generic(generic)) return strdup(generic); sscanf(generic, "ng%dn%d", &instance, &head_instance); sprintf(blkdev, "nvme%dn%d", instance, head_instance); return strdup(blkdev); } int __libnvme_scan_namespace(struct libnvme_global_ctx *ctx, const char *sysfs_dir, const char *name, libnvme_ns_t *ns) { __cleanup_free char *blkdev = NULL; __cleanup_free char *path = NULL; struct libnvme_ns *n = NULL; int ret; blkdev = libnvme_ns_generic_to_blkdev(name); if (!blkdev) return -ENOMEM; ret = asprintf(&path, "%s/%s", sysfs_dir, blkdev); if (ret < 0) return -ENOMEM; ret = libnvme_ns_open(ctx, path, blkdev, &n); if (ret) return ret; n->sysfs_dir = path; path = NULL; *ns = n; return 0; } int libnvme_get_ctrl_transport(__shr_unused struct libnvme_global_ctx *ctx, const char *path, const char *name, char **transport, char **traddr, char **addr, char **trsvcid, char **host_traddr, char **host_iface) { char *a = NULL, *e = NULL; *transport = libnvme_get_attr(path, "transport"); if (!*transport) return -ENXIO; /* Parse 'address' string into components */ *addr = libnvme_get_attr(path, "address"); if (!*addr) { __cleanup_free char *rpath = NULL; char *p = NULL, *_a = NULL; /* loop transport might not have an address */ if (!strcmp(*transport, "loop")) goto skip_address; /* Older kernels don't support pcie transport addresses */ if (strcmp(*transport, "pcie") && strcmp(*transport, "apple-nvme")) return -ENXIO; /* Figure out the PCI address from the attribute path */ rpath = realpath(path, NULL); if (!rpath) return -ENOMEM; a = strtok_r(rpath, "/", &e); while (a && strlen(a)) { if (_a) p = _a; _a = a; if (!strncmp(a, "nvme", 4)) break; a = strtok_r(NULL, "/", &e); } if (p) *addr = strdup(p); } else if (!strcmp(*transport, "pcie") || !strcmp(*transport, "apple-nvme")) { /* The 'address' string is the transport address */ *traddr = strdup(*addr); if (!*traddr) return -ENOMEM; } else { __cleanup_free char *address = strdup(*addr); if (!address) return -ENOMEM; a = strtok_r(address, ",", &e); while (a && strlen(a)) { if (!strncmp(a, "traddr=", 7)) *traddr = strdup(a + 7); else if (!strncmp(a, "trsvcid=", 8)) *trsvcid = strdup(a + 8); else if (!strncmp(a, "host_traddr=", 12)) *host_traddr = strdup(a + 12); else if (!strncmp(a, "host_iface=", 11)) *host_iface = strdup(a + 11); a = strtok_r(NULL, ",", &e); } } skip_address: return 0; } int libnvme_init_subsystem(libnvme_subsystem_t s, const char *name) { char *path; if (asprintf(&path, "%s/%s", libnvme_subsys_sysfs_dir(s->h->ctx), name) < 0) return -ENOMEM; s->subsystype = libnvme_get_attr(path, "subsystype"); if (!s->subsystype) { if (!strcmp(s->subsysnqn, NVME_DISC_SUBSYS_NAME)) s->subsystype = strdup("discovery"); else s->subsystype = strdup("nvm"); } s->name = strdup(name); s->sysfs_dir = (char *)path; return 0; }