From 9d031be3cfe87a80ff62f0b2af2235581f529a5c Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 13 Mar 2026 18:32:38 +0100 Subject: [PATCH 01/12] nvme: help compiler to figure out figuring init of variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With some more aggressive CFLAGS/LDFLAGS optimized for size the compiler gets confused and can't figure out if ms is initialized or not. ../nvme.c:8567:20: error: ‘ms’ may be used uninitialized [-Werror=maybe-uninitialized] 8567 | if (ms && cfg.metadata_size < mbuffer_size) | ^ ../nvme.c:8356:15: note: ‘ms’ was declared here 8356 | __u16 ms; | ^~ Signed-off-by: Daniel Wagner --- nvme.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/nvme.c b/nvme.c index 11a2ef0290..f23a297551 100644 --- a/nvme.c +++ b/nvme.c @@ -8351,9 +8351,9 @@ static int submit_io(int opcode, char *command, const char *desc, int argc, char __u32 dsmgmt = 0; int mode = 0644; void *buffer; + __u16 ms = 0; int err = 0; int flags; - __u16 ms; const char *start_block_addr = "64-bit addr of first block to access"; const char *block_size = "if specified, logical block size in bytes;\n" @@ -8530,13 +8530,7 @@ static int submit_io(int opcode, char *command, const char *desc, int argc, char } else { err = get_pi_info(hdl, cfg.nsid, cfg.prinfo, cfg.ilbrt, cfg.lbst, &logical_block_size, &ms); - if (err) { - logical_block_size = 0; - ms = 0; - pi_available = false; - } else { - pi_available = true; - } + pi_available = err == 0; } buffer_size = ((long long)cfg.block_count + 1) * logical_block_size; From 32f3c5322ce8c6b52760fc517adbb373a56b029f Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 13 Mar 2026 18:34:49 +0100 Subject: [PATCH 02/12] tree: always initialize ns variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With some more aggressive CFLAGS/LDFLAGS optimized for size the compiler gets confused and can't figure out if 'n' is initialized or not. ../libnvme/src/nvme/tree.c: In function ‘__nvme_scan_namespace’: ../libnvme/src/nvme/tree.c:2598:22: error: ‘n’ may be used uninitialized [-Werror=maybe-uninitialized] 2598 | n->sysfs_dir = path; | ~~~~~~~~~~~~~^~~~~~ ../libnvme/src/nvme/tree.c:2583:25: note: ‘n’ was declared here 2583 | struct nvme_ns *n; | ^ Signed-off-by: Daniel Wagner --- libnvme/src/nvme/tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnvme/src/nvme/tree.c b/libnvme/src/nvme/tree.c index 0f7b0fc2f7..d310e45b31 100644 --- a/libnvme/src/nvme/tree.c +++ b/libnvme/src/nvme/tree.c @@ -2573,7 +2573,7 @@ static int __nvme_scan_namespace(struct nvme_global_ctx *ctx, { _cleanup_free_ char *blkdev = NULL; _cleanup_free_ char *path = NULL; - struct nvme_ns *n; + struct nvme_ns *n = NULL; int ret; blkdev = nvme_ns_generic_to_blkdev(name); From d76bd2ff01a82228f55795ad8da347d732e449ac Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 13 Mar 2026 18:44:05 +0100 Subject: [PATCH 03/12] util/types: include missing header for time_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The musl build fails due to a missing include: In file included from ../unit/test-uint128.c:8: ../unit/../util/types.h:60:16: error: unknown type name ‘time_t’ 60 | int convert_ts(time_t time, char *ts_buf); | ^~~~~~ ../unit/../util/types.h:11:1: note: ‘time_t’ is defined in header ‘’; this is probably fixable by adding ‘#include ’ 10 | #include +++ |+#include 11 | Signed-off-by: Daniel Wagner --- util/types.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util/types.h b/util/types.h index 4046d5c18c..c009f2f1fa 100644 --- a/util/types.h +++ b/util/types.h @@ -5,6 +5,8 @@ /* type conversion helpers */ #include +#include + #include #include From 214185c1ffd8fa364f9efd7a57afe6bdf8a5031c Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 13 Mar 2026 18:58:37 +0100 Subject: [PATCH 04/12] no-json: update function arguments When the nvme_dump_config function arguments got updated, the no-json version was forgotten. Fixes: 74b30d2556c3 ("json: update nvme_dump_config to a file descriptor") Signed-off-by: Daniel Wagner --- libnvme/src/nvme/no-json.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnvme/src/nvme/no-json.c b/libnvme/src/nvme/no-json.c index 2315b729e9..1060c071c1 100644 --- a/libnvme/src/nvme/no-json.c +++ b/libnvme/src/nvme/no-json.c @@ -15,7 +15,7 @@ int json_read_config(struct nvme_global_ctx *ctx, const char *config_file) return -ENOTSUP; } -int json_update_config(struct nvme_global_ctx *ctx, const char *config_file) +int json_update_config(struct nvme_global_ctx *ctx, int fd) { return -ENOTSUP; } From 66d4766971b8658f2bf4a5b6636077531ca47f71 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 13 Mar 2026 19:08:28 +0100 Subject: [PATCH 05/12] nbft: move nbft_file_entry to nbft header The nbft_info is defined in nbft thus move the linked list also there. This decouples the dependencies between nbft.h and fabrics.h Signed-off-by: Daniel Wagner --- libnvme/src/nvme/fabrics.h | 10 +--------- libnvme/src/nvme/nbft.h | 11 +++++++++++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/libnvme/src/nvme/fabrics.h b/libnvme/src/nvme/fabrics.h index 9f9549dc51..ba4281e022 100644 --- a/libnvme/src/nvme/fabrics.h +++ b/libnvme/src/nvme/fabrics.h @@ -645,15 +645,7 @@ int nvmf_connect_config_json(struct nvme_global_ctx *ctx, int nvmf_config_modify(struct nvme_global_ctx *ctx, struct nvmf_context *fctx); -/** - * struct nbft_file_entry - Linked list entry for NBFT files - * @next: Pointer to next entry - * @nbft: Pointer to NBFT info structure - */ -struct nbft_file_entry { - struct nbft_file_entry *next; - struct nbft_info *nbft; -}; +struct nbft_file_entry; /** * nvmf_nbft_read_files() - Read NBFT files from path diff --git a/libnvme/src/nvme/nbft.h b/libnvme/src/nvme/nbft.h index f14331ac72..16d1606c7a 100644 --- a/libnvme/src/nvme/nbft.h +++ b/libnvme/src/nvme/nbft.h @@ -1254,3 +1254,14 @@ int nvme_nbft_read(struct nvme_global_ctx *ctx, struct nbft_info **nbft, * @nbft: Parsed NBFT table data. */ void nvme_nbft_free(struct nvme_global_ctx *ctx, struct nbft_info *nbft); + +/** + * struct nbft_file_entry - Linked list entry for NBFT files + * @next: Pointer to next entry + * @nbft: Pointer to NBFT info structure + */ +struct nbft_file_entry { + struct nbft_file_entry *next; + struct nbft_info *nbft; +}; + From 7e0da27d3b3562172fc766479c9d7e82382677c7 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 13 Mar 2026 19:06:41 +0100 Subject: [PATCH 06/12] fabrics: move nvmf_default_config to tree The fabrics configuration data struct is always present, even the fabrics code is disabled. Thus the struct needs always to be initialized Signed-off-by: Daniel Wagner --- libnvme/src/nvme/fabrics.c | 7 ------- libnvme/src/nvme/tree.c | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/libnvme/src/nvme/fabrics.c b/libnvme/src/nvme/fabrics.c index df754a71a1..7d53020dbf 100644 --- a/libnvme/src/nvme/fabrics.c +++ b/libnvme/src/nvme/fabrics.c @@ -338,13 +338,6 @@ static const struct nvme_fabric_options default_supported_options = { .trsvcid = true, }; -void nvmf_default_config(struct nvme_fabrics_config *cfg) -{ - memset(cfg, 0, sizeof(*cfg)); - cfg->tos = -1; - cfg->ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO; -} - #define MERGE_CFG_OPTION(c, n, o, d) \ if ((c)->o == d) (c)->o = (n)->o static struct nvme_fabrics_config *merge_config(nvme_ctrl_t c, diff --git a/libnvme/src/nvme/tree.c b/libnvme/src/nvme/tree.c index d310e45b31..cce9615a5d 100644 --- a/libnvme/src/nvme/tree.c +++ b/libnvme/src/nvme/tree.c @@ -1118,6 +1118,13 @@ static bool traddr_is_hostname(const char *transport, const char *traddr) return true; } +void nvmf_default_config(struct nvme_fabrics_config *cfg) +{ + memset(cfg, 0, sizeof(*cfg)); + cfg->tos = -1; + cfg->ctrl_loss_tmo = NVMF_DEF_CTRL_LOSS_TMO; +} + int nvme_create_ctrl(struct nvme_global_ctx *ctx, const char *subsysnqn, const char *transport, const char *traddr, const char *host_traddr, From 43b3ba70ad3617d017c5e372a1b663d28bf69670 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 12 Mar 2026 10:20:56 +0100 Subject: [PATCH 07/12] linux: move the hostid/hostnqn function The hostid and hostnqn are used through out the library and are not fabric specific. Because tree.c depends on it move it to linux.c, so it's possible to make the fabrics code optional. Signed-off-by: Daniel Wagner --- libnvme/libnvme/nvme.i | 4 +- libnvme/src/libnvme.ld | 10 +- libnvme/src/nvme/fabrics.c | 247 +------------------------------------ libnvme/src/nvme/fabrics.h | 51 -------- libnvme/src/nvme/linux.c | 243 ++++++++++++++++++++++++++++++++++++ libnvme/src/nvme/linux.h | 52 ++++++++ libnvme/src/nvme/tree.c | 8 +- nvme.c | 12 +- 8 files changed, 314 insertions(+), 313 deletions(-) diff --git a/libnvme/libnvme/nvme.i b/libnvme/libnvme/nvme.i index c606532b33..460500e06b 100644 --- a/libnvme/libnvme/nvme.i +++ b/libnvme/libnvme/nvme.i @@ -39,13 +39,13 @@ Py_XDECREF(val); /* .. therefore decrement ref. count. */ } PyObject *hostnqn_from_file() { - char * val = nvmf_hostnqn_from_file(); + char * val = nvme_hostnqn_from_file(); PyObject * obj = PyUnicode_FromString(val); free(val); return obj; } PyObject *hostid_from_file() { - char * val = nvmf_hostid_from_file(); + char * val = nvme_hostid_from_file(); PyObject * obj = PyUnicode_FromString(val); free(val); return obj; diff --git a/libnvme/src/libnvme.ld b/libnvme/src/libnvme.ld index 4072407de9..314682d7ec 100644 --- a/libnvme/src/libnvme.ld +++ b/libnvme/src/libnvme.ld @@ -108,6 +108,11 @@ LIBNVME_2_0 { nvme_host_set_dhchap_key; nvme_host_set_hostsymname; nvme_host_set_pdc_enabled; + nvme_hostid_from_file; + nvme_hostid_generate; + nvme_hostnqn_from_file; + nvme_hostnqn_generate; + nvme_hostnqn_generate_from_hostid; nvme_import_tls_key; nvme_import_tls_key_versioned; nvme_init_copy_range; @@ -320,11 +325,6 @@ LIBNVME_2_0 { nvmf_get_default_trsvcid; nvmf_get_discovery_log; nvmf_get_discovery_wargs; - nvmf_hostid_from_file; - nvmf_hostid_generate; - nvmf_hostnqn_from_file; - nvmf_hostnqn_generate; - nvmf_hostnqn_generate_from_hostid; nvmf_is_registration_supported; nvmf_nbft_free; nvmf_nbft_read_files; diff --git a/libnvme/src/nvme/fabrics.c b/libnvme/src/nvme/fabrics.c index 7d53020dbf..fe0fb064ca 100644 --- a/libnvme/src/nvme/fabrics.c +++ b/libnvme/src/nvme/fabrics.c @@ -37,11 +37,6 @@ #include "cleanup.h" #include "private.h" -#define NVMF_HOSTID_SIZE 37 - -#define NVMF_HOSTNQN_FILE SYSCONFDIR "/nvme/hostnqn" -#define NVMF_HOSTID_FILE SYSCONFDIR "/nvme/hostid" - const char *nvmf_dev = "/dev/nvme-fabrics"; /** @@ -1384,244 +1379,6 @@ int nvmf_get_discovery_wargs(struct nvme_get_discovery_args *args, return 0; } -static int uuid_from_device_tree(char *system_uuid) -{ - _cleanup_fd_ int f = -1; - ssize_t len; - - f = open(nvme_uuid_ibm_filename(), O_RDONLY); - if (f < 0) - return -ENXIO; - - memset(system_uuid, 0, NVME_UUID_LEN_STRING); - len = read(f, system_uuid, NVME_UUID_LEN_STRING - 1); - if (len < 0) - return -ENXIO; - - return strlen(system_uuid) ? 0 : -ENXIO; -} - -/* - * See System Management BIOS (SMBIOS) Reference Specification - * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.2.0.pdf - */ -#define DMI_SYSTEM_INFORMATION 1 - -static bool is_dmi_uuid_valid(const char *buf, size_t len) -{ - int i; - - /* UUID bytes are from byte 8 to 23 */ - if (len < 24) - return false; - - /* Test it's a invalid UUID with all zeros */ - for (i = 8; i < 24; i++) { - if (buf[i]) - break; - } - if (i == 24) - return false; - - return true; -} - -static int uuid_from_dmi_entries(char *system_uuid) -{ - _cleanup_dir_ DIR *d = NULL; - const char *entries_dir = nvme_dmi_entries_dir(); - int f; - struct dirent *de; - char buf[512] = {0}; - - system_uuid[0] = '\0'; - d = opendir(entries_dir); - if (!d) - return -ENXIO; - while ((de = readdir(d))) { - char filename[PATH_MAX]; - int len, type; - - if (de->d_name[0] == '.') - continue; - sprintf(filename, "%s/%s/type", entries_dir, de->d_name); - f = open(filename, O_RDONLY); - if (f < 0) - continue; - len = read(f, buf, 512); - close(f); - if (len <= 0) - continue; - if (sscanf(buf, "%d", &type) != 1) - continue; - if (type != DMI_SYSTEM_INFORMATION) - continue; - sprintf(filename, "%s/%s/raw", entries_dir, de->d_name); - f = open(filename, O_RDONLY); - if (f < 0) - continue; - len = read(f, buf, 512); - close(f); - if (len <= 0) - continue; - - if (!is_dmi_uuid_valid(buf, len)) - continue; - - /* Sigh. https://en.wikipedia.org/wiki/Overengineering */ - /* DMTF SMBIOS 3.0 Section 7.2.1 System UUID */ - sprintf(system_uuid, - "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" - "%02x%02x%02x%02x%02x%02x", - (uint8_t)buf[8 + 3], (uint8_t)buf[8 + 2], - (uint8_t)buf[8 + 1], (uint8_t)buf[8 + 0], - (uint8_t)buf[8 + 5], (uint8_t)buf[8 + 4], - (uint8_t)buf[8 + 7], (uint8_t)buf[8 + 6], - (uint8_t)buf[8 + 8], (uint8_t)buf[8 + 9], - (uint8_t)buf[8 + 10], (uint8_t)buf[8 + 11], - (uint8_t)buf[8 + 12], (uint8_t)buf[8 + 13], - (uint8_t)buf[8 + 14], (uint8_t)buf[8 + 15]); - break; - } - return strlen(system_uuid) ? 0 : -ENXIO; -} - -#define PATH_DMI_PROD_UUID "/sys/class/dmi/id/product_uuid" - -/** - * uuid_from_product_uuid() - Get system UUID from product_uuid - * @system_uuid: Where to save the system UUID. - * - * Return: 0 on success, -ENXIO otherwise. - */ -static int uuid_from_product_uuid(char *system_uuid) -{ - _cleanup_file_ FILE *stream = NULL; - ssize_t nread; - _cleanup_free_ char *line = NULL; - size_t len = 0; - - stream = fopen(PATH_DMI_PROD_UUID, "re"); - if (!stream) - return -ENXIO; - system_uuid[0] = '\0'; - - nread = getline(&line, &len, stream); - if (nread != NVME_UUID_LEN_STRING) - return -ENXIO; - - /* The kernel is handling the byte swapping according DMTF - * SMBIOS 3.0 Section 7.2.1 System UUID */ - - memcpy(system_uuid, line, NVME_UUID_LEN_STRING - 1); - system_uuid[NVME_UUID_LEN_STRING - 1] = '\0'; - - return 0; -} - -/** - * uuid_from_dmi() - read system UUID - * @system_uuid: buffer for the UUID - * - * The system UUID can be read from two different locations: - * - * 1) /sys/class/dmi/id/product_uuid - * 2) /sys/firmware/dmi/entries - * - * Note that the second location is not present on Debian-based systems. - * - * Return: 0 on success, negative errno otherwise. - */ -static int uuid_from_dmi(char *system_uuid) -{ - int ret = uuid_from_product_uuid(system_uuid); - if (ret != 0) - ret = uuid_from_dmi_entries(system_uuid); - return ret; -} - -char *nvmf_hostid_generate() -{ - int ret; - char uuid_str[NVME_UUID_LEN_STRING]; - unsigned char uuid[NVME_UUID_LEN]; - - ret = uuid_from_dmi(uuid_str); - if (ret < 0) - ret = uuid_from_device_tree(uuid_str); - if (ret < 0) { - if (nvme_uuid_random(uuid) < 0) - memset(uuid, 0, NVME_UUID_LEN); - nvme_uuid_to_string(uuid, uuid_str); - } - - return strdup(uuid_str); -} - -char *nvmf_hostnqn_generate_from_hostid(char *hostid) -{ - char *hid = NULL; - char *hostnqn; - int ret; - - if (!hostid) - hostid = hid = nvmf_hostid_generate(); - - ret = asprintf(&hostnqn, "nqn.2014-08.org.nvmexpress:uuid:%s", hostid); - free(hid); - - return (ret < 0) ? NULL : hostnqn; -} - -char *nvmf_hostnqn_generate() -{ - return nvmf_hostnqn_generate_from_hostid(NULL); -} - -static char *nvmf_read_file(const char *f, int len) -{ - char buf[len]; - _cleanup_fd_ int fd = -1; - int ret; - - fd = open(f, O_RDONLY); - if (fd < 0) - return NULL; - - memset(buf, 0, len); - ret = read(fd, buf, len - 1); - - if (ret < 0 || !strlen(buf)) - return NULL; - return strndup(buf, strcspn(buf, "\n")); -} - -char *nvmf_hostnqn_from_file() -{ - char *hostnqn = getenv("LIBNVME_HOSTNQN"); - - if (hostnqn) { - if (!strcmp(hostnqn, "")) - return NULL; - return strdup(hostnqn); - } - - return nvmf_read_file(NVMF_HOSTNQN_FILE, NVMF_NQN_SIZE); -} - -char *nvmf_hostid_from_file() -{ - char *hostid = getenv("LIBNVME_HOSTID"); - - if (hostid) { - if (!strcmp(hostid, "")) - return NULL; - return strdup(hostid); - } - - return nvmf_read_file(NVMF_HOSTID_FILE, NVMF_HOSTID_SIZE); -} - /** * nvmf_get_tel() - Calculate the amount of memory needed for a DIE. * @hostsymname: Symbolic name (may be NULL) @@ -2668,9 +2425,9 @@ int nvmf_config_modify(struct nvme_global_ctx *ctx, struct nvme_ctrl *c; if (!fctx->hostnqn) - fctx->hostnqn = hnqn = nvmf_hostnqn_from_file(); + fctx->hostnqn = hnqn = nvme_hostnqn_from_file(); if (!fctx->hostid && hnqn) - fctx->hostid = hid = nvmf_hostid_from_file(); + fctx->hostid = hid = nvme_hostid_from_file(); h = nvme_lookup_host(ctx, fctx->hostnqn, fctx->hostid); if (!h) { diff --git a/libnvme/src/nvme/fabrics.h b/libnvme/src/nvme/fabrics.h index ba4281e022..5a441ec2a0 100644 --- a/libnvme/src/nvme/fabrics.h +++ b/libnvme/src/nvme/fabrics.h @@ -283,57 +283,6 @@ struct nvme_get_discovery_args { int nvmf_get_discovery_wargs(struct nvme_get_discovery_args *args, struct nvmf_discovery_log **log); -/** - * nvmf_hostnqn_generate() - Generate a machine specific host nqn - * Returns: An nvm namespace qualified name string based on the machine - * identifier, or NULL if not successful. - */ -char *nvmf_hostnqn_generate(); - -/** - * nvmf_hostnqn_generate_from_hostid() - Generate a host nqn from host identifier - * @hostid: Host identifier - * - * If @hostid is NULL, the function generates it based on the machine - * identifier. - * - * Return: On success, an NVMe Qualified Name for host identification. This - * name is based on the given host identifier. On failure, NULL. - */ -char *nvmf_hostnqn_generate_from_hostid(char *hostid); - -/** - * nvmf_hostid_generate() - Generate a machine specific host identifier - * - * Return: On success, an identifier string based on the machine identifier to - * be used as NVMe Host Identifier, or NULL on failure. - */ -char *nvmf_hostid_generate(); - -/** - * nvmf_hostnqn_from_file() - Reads the host nvm qualified name from the config - * default location - * - * Retrieve the qualified name from the config file located in $SYSCONFIDR/nvme. - * $SYSCONFDIR is usually /etc. - * - * Return: The host nqn, or NULL if unsuccessful. If found, the caller - * is responsible to free the string. - */ -char *nvmf_hostnqn_from_file(); - -/** - * nvmf_hostid_from_file() - Reads the host identifier from the config default - * location - * - * Retrieve the host idenditifer from the config file located in $SYSCONFDIR/nvme/. - * $SYSCONFDIR is usually /etc. - * - * Return: The host identifier, or NULL if unsuccessful. If found, the caller - * is responsible to free the string. - */ -char *nvmf_hostid_from_file(); - /** * nvmf_is_registration_supported - check whether registration can be performed. * @c: Controller instance diff --git a/libnvme/src/nvme/linux.c b/libnvme/src/nvme/linux.c index 52416bef37..80ce135ca1 100644 --- a/libnvme/src/nvme/linux.c +++ b/libnvme/src/nvme/linux.c @@ -48,6 +48,11 @@ #include "cleanup.h" #include "private.h" +#define NVMF_HOSTID_SIZE 37 + +#define NVMF_HOSTNQN_FILE SYSCONFDIR "/nvme/hostnqn" +#define NVMF_HOSTID_FILE SYSCONFDIR "/nvme/hostid" + static int __nvme_set_attr(const char *path, const char *value) { _cleanup_fd_ int fd = -1; @@ -1561,3 +1566,241 @@ int nvme_import_tls_key(struct nvme_global_ctx *ctx, const char *encoded_key, *keyp = psk; return 0; } + +static int uuid_from_device_tree(char *system_uuid) +{ + _cleanup_fd_ int f = -1; + ssize_t len; + + f = open(nvme_uuid_ibm_filename(), O_RDONLY); + if (f < 0) + return -ENXIO; + + memset(system_uuid, 0, NVME_UUID_LEN_STRING); + len = read(f, system_uuid, NVME_UUID_LEN_STRING - 1); + if (len < 0) + return -ENXIO; + + return strlen(system_uuid) ? 0 : -ENXIO; +} + +/* + * See System Management BIOS (SMBIOS) Reference Specification + * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.2.0.pdf + */ +#define DMI_SYSTEM_INFORMATION 1 + +static bool is_dmi_uuid_valid(const char *buf, size_t len) +{ + int i; + + /* UUID bytes are from byte 8 to 23 */ + if (len < 24) + return false; + + /* Test it's a invalid UUID with all zeros */ + for (i = 8; i < 24; i++) { + if (buf[i]) + break; + } + if (i == 24) + return false; + + return true; +} + +static int uuid_from_dmi_entries(char *system_uuid) +{ + _cleanup_dir_ DIR *d = NULL; + const char *entries_dir = nvme_dmi_entries_dir(); + int f; + struct dirent *de; + char buf[512] = {0}; + + system_uuid[0] = '\0'; + d = opendir(entries_dir); + if (!d) + return -ENXIO; + while ((de = readdir(d))) { + char filename[PATH_MAX]; + int len, type; + + if (de->d_name[0] == '.') + continue; + sprintf(filename, "%s/%s/type", entries_dir, de->d_name); + f = open(filename, O_RDONLY); + if (f < 0) + continue; + len = read(f, buf, 512); + close(f); + if (len <= 0) + continue; + if (sscanf(buf, "%d", &type) != 1) + continue; + if (type != DMI_SYSTEM_INFORMATION) + continue; + sprintf(filename, "%s/%s/raw", entries_dir, de->d_name); + f = open(filename, O_RDONLY); + if (f < 0) + continue; + len = read(f, buf, 512); + close(f); + if (len <= 0) + continue; + + if (!is_dmi_uuid_valid(buf, len)) + continue; + + /* Sigh. https://en.wikipedia.org/wiki/Overengineering */ + /* DMTF SMBIOS 3.0 Section 7.2.1 System UUID */ + sprintf(system_uuid, + "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-" + "%02x%02x%02x%02x%02x%02x", + (uint8_t)buf[8 + 3], (uint8_t)buf[8 + 2], + (uint8_t)buf[8 + 1], (uint8_t)buf[8 + 0], + (uint8_t)buf[8 + 5], (uint8_t)buf[8 + 4], + (uint8_t)buf[8 + 7], (uint8_t)buf[8 + 6], + (uint8_t)buf[8 + 8], (uint8_t)buf[8 + 9], + (uint8_t)buf[8 + 10], (uint8_t)buf[8 + 11], + (uint8_t)buf[8 + 12], (uint8_t)buf[8 + 13], + (uint8_t)buf[8 + 14], (uint8_t)buf[8 + 15]); + break; + } + return strlen(system_uuid) ? 0 : -ENXIO; +} + +#define PATH_DMI_PROD_UUID "/sys/class/dmi/id/product_uuid" + +/** + * uuid_from_product_uuid() - Get system UUID from product_uuid + * @system_uuid: Where to save the system UUID. + * + * Return: 0 on success, -ENXIO otherwise. + */ +static int uuid_from_product_uuid(char *system_uuid) +{ + _cleanup_file_ FILE *stream = NULL; + ssize_t nread; + _cleanup_free_ char *line = NULL; + size_t len = 0; + + stream = fopen(PATH_DMI_PROD_UUID, "re"); + if (!stream) + return -ENXIO; + system_uuid[0] = '\0'; + + nread = getline(&line, &len, stream); + if (nread != NVME_UUID_LEN_STRING) + return -ENXIO; + + /* The kernel is handling the byte swapping according DMTF + * SMBIOS 3.0 Section 7.2.1 System UUID */ + + memcpy(system_uuid, line, NVME_UUID_LEN_STRING - 1); + system_uuid[NVME_UUID_LEN_STRING - 1] = '\0'; + + return 0; +} + +/** + * uuid_from_dmi() - read system UUID + * @system_uuid: buffer for the UUID + * + * The system UUID can be read from two different locations: + * + * 1) /sys/class/dmi/id/product_uuid + * 2) /sys/firmware/dmi/entries + * + * Note that the second location is not present on Debian-based systems. + * + * Return: 0 on success, negative errno otherwise. + */ +static int uuid_from_dmi(char *system_uuid) +{ + int ret = uuid_from_product_uuid(system_uuid); + if (ret != 0) + ret = uuid_from_dmi_entries(system_uuid); + return ret; +} + +char *nvme_hostid_generate() +{ + int ret; + char uuid_str[NVME_UUID_LEN_STRING]; + unsigned char uuid[NVME_UUID_LEN]; + + ret = uuid_from_dmi(uuid_str); + if (ret < 0) + ret = uuid_from_device_tree(uuid_str); + if (ret < 0) { + if (nvme_uuid_random(uuid) < 0) + memset(uuid, 0, NVME_UUID_LEN); + nvme_uuid_to_string(uuid, uuid_str); + } + + return strdup(uuid_str); +} + +char *nvme_hostnqn_generate_from_hostid(char *hostid) +{ + char *hid = NULL; + char *hostnqn; + int ret; + + if (!hostid) + hostid = hid = nvme_hostid_generate(); + + ret = asprintf(&hostnqn, "nqn.2014-08.org.nvmexpress:uuid:%s", hostid); + free(hid); + + return (ret < 0) ? NULL : hostnqn; +} + +char *nvme_hostnqn_generate() +{ + return nvme_hostnqn_generate_from_hostid(NULL); +} + +static char *nvmf_read_file(const char *f, int len) +{ + char buf[len]; + _cleanup_fd_ int fd = -1; + int ret; + + fd = open(f, O_RDONLY); + if (fd < 0) + return NULL; + + memset(buf, 0, len); + ret = read(fd, buf, len - 1); + + if (ret < 0 || !strlen(buf)) + return NULL; + return strndup(buf, strcspn(buf, "\n")); +} + +char *nvme_hostnqn_from_file() +{ + char *hostnqn = getenv("LIBNVME_HOSTNQN"); + + if (hostnqn) { + if (!strcmp(hostnqn, "")) + return NULL; + return strdup(hostnqn); + } + + return nvmf_read_file(NVMF_HOSTNQN_FILE, NVMF_NQN_SIZE); +} + +char *nvme_hostid_from_file() +{ + char *hostid = getenv("LIBNVME_HOSTID"); + + if (hostid) { + if (!strcmp(hostid, "")) + return NULL; + return strdup(hostid); + } + + return nvmf_read_file(NVMF_HOSTID_FILE, NVMF_HOSTID_SIZE); +} diff --git a/libnvme/src/nvme/linux.h b/libnvme/src/nvme/linux.h index 7c947f0fbf..d1fd83ac30 100644 --- a/libnvme/src/nvme/linux.h +++ b/libnvme/src/nvme/linux.h @@ -393,3 +393,55 @@ int nvme_import_tls_key(struct nvme_global_ctx *ctx, const char *encoded_key, int nvme_import_tls_key_versioned(struct nvme_global_ctx *ctx, const char *encoded_key, unsigned char *version, unsigned char *hmac, size_t *key_len, unsigned char **key); + +/** + * nvme_hostnqn_generate() - Generate a machine specific host nqn + * Returns: An nvm namespace qualified name string based on the machine + * identifier, or NULL if not successful. + */ +char *nvme_hostnqn_generate(); + +/** + * nvme_hostnqn_generate_from_hostid() - Generate a host nqn from + * host identifier + * @hostid: Host identifier + * + * If @hostid is NULL, the function generates it based on the machine + * identifier. + * + * Return: On success, an NVMe Qualified Name for host identification. This + * name is based on the given host identifier. On failure, NULL. + */ +char *nvme_hostnqn_generate_from_hostid(char *hostid); + +/** + * nvme_hostid_generate() - Generate a machine specific host identifier + * + * Return: On success, an identifier string based on the machine identifier to + * be used as NVMe Host Identifier, or NULL on failure. + */ +char *nvme_hostid_generate(); + +/** + * nvme_hostnqn_from_file() - Reads the host nvm qualified name from the config + * default location + * + * Retrieve the qualified name from the config file located in $SYSCONFIDR/nvme. + * $SYSCONFDIR is usually /etc. + * + * Return: The host nqn, or NULL if unsuccessful. If found, the caller + * is responsible to free the string. + */ +char *nvme_hostnqn_from_file(); + +/** + * nvme_hostid_from_file() - Reads the host identifier from the config default + * location + * + * Retrieve the host idenditifer from the config file located in + * $SYSCONFDIR/nvme/. $SYSCONFDIR is usually /etc. + * + * Return: The host identifier, or NULL if unsuccessful. If found, the caller + * is responsible to free the string. + */ +char *nvme_hostid_from_file(); diff --git a/libnvme/src/nvme/tree.c b/libnvme/src/nvme/tree.c index cce9615a5d..fc207aea5c 100644 --- a/libnvme/src/nvme/tree.c +++ b/libnvme/src/nvme/tree.c @@ -144,9 +144,9 @@ int nvme_host_get_ids(struct nvme_global_ctx *ctx, /* /etc/nvme/hostid and/or /etc/nvme/hostnqn */ if (!hid) - hid = nvmf_hostid_from_file(); + hid = nvme_hostid_from_file(); if (!hnqn) - hnqn = nvmf_hostnqn_from_file(); + hnqn = nvme_hostnqn_from_file(); /* incomplete configuration, thus derive hostid from hostnqn */ if (!hid && hnqn) @@ -157,7 +157,7 @@ int nvme_host_get_ids(struct nvme_global_ctx *ctx, * fails generate one */ if (!hid) { - hid = nvmf_hostid_generate(); + hid = nvme_hostid_generate(); if (!hid) return -ENOMEM; @@ -167,7 +167,7 @@ int nvme_host_get_ids(struct nvme_global_ctx *ctx, /* incomplete configuration, thus derive hostnqn from hostid */ if (!hnqn) { - hnqn = nvmf_hostnqn_generate_from_hostid(hid); + hnqn = nvme_hostnqn_generate_from_hostid(hid); if (!hnqn) return -ENOMEM; } diff --git a/nvme.c b/nvme.c index f23a297551..9cafa209c4 100644 --- a/nvme.c +++ b/nvme.c @@ -9522,7 +9522,7 @@ static int gen_hostnqn_cmd(int argc, char **argv, struct command *acmd, struct p { char *hostnqn; - hostnqn = nvmf_hostnqn_generate(); + hostnqn = nvme_hostnqn_generate(); if (!hostnqn) { nvme_show_error("\"%s\" not supported. Install lib uuid and rebuild.", acmd->name); @@ -9537,9 +9537,9 @@ static int show_hostnqn_cmd(int argc, char **argv, struct command *acmd, struct { char *hostnqn; - hostnqn = nvmf_hostnqn_from_file(); + hostnqn = nvme_hostnqn_from_file(); if (!hostnqn) - hostnqn = nvmf_hostnqn_generate(); + hostnqn = nvme_hostnqn_generate(); if (!hostnqn) { nvme_show_error("hostnqn is not available -- use nvme gen-hostnqn"); @@ -9667,7 +9667,7 @@ static int gen_dhchap_key(int argc, char **argv, struct command *acmd, struct pl } if (!cfg.nqn) { - cfg.nqn = hnqn = nvmf_hostnqn_from_file(); + cfg.nqn = hnqn = nvme_hostnqn_from_file(); if (!cfg.nqn) { nvme_show_error("Could not read host NQN"); return -ENOENT; @@ -9931,7 +9931,7 @@ static int gen_tls_key(int argc, char **argv, struct command *acmd, struct plugi return -EINVAL; } if (!cfg.hostnqn) { - cfg.hostnqn = hnqn = nvmf_hostnqn_from_file(); + cfg.hostnqn = hnqn = nvme_hostnqn_from_file(); if (!cfg.hostnqn) { nvme_show_error("Failed to read host NQN"); return -EINVAL; @@ -10093,7 +10093,7 @@ static int check_tls_key(int argc, char **argv, struct command *acmd, struct plu if (cfg.subsysnqn) { if (!cfg.hostnqn) { - cfg.hostnqn = hnqn = nvmf_hostnqn_from_file(); + cfg.hostnqn = hnqn = nvme_hostnqn_from_file(); if (!cfg.hostnqn) { nvme_show_error("Failed to read host NQN"); return -EINVAL; From 2c56eadfe0c395b1a1b89c4246e3827d4908cc19 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 12 Mar 2026 10:23:28 +0100 Subject: [PATCH 08/12] lib: remove the unreleased linker section The symbol versioning never really worked, thus it's adding unnecessary complexity. Thus the guarantee is that no existing symbols gets changed. Signed-off-by: Daniel Wagner --- libnvme/src/libnvme.ld | 4 +--- scripts/release.sh | 35 ----------------------------------- 2 files changed, 1 insertion(+), 38 deletions(-) diff --git a/libnvme/src/libnvme.ld b/libnvme/src/libnvme.ld index 314682d7ec..d198f237c4 100644 --- a/libnvme/src/libnvme.ld +++ b/libnvme/src/libnvme.ld @@ -1,8 +1,6 @@ # SPDX-License-Identifier: LGPL-2.1-or-later -LIBNVME_UNRELEASED { -}; -LIBNVME_2_0 { +LIBNVME_3 { global: nvme_clear_etdas; nvme_close; diff --git a/scripts/release.sh b/scripts/release.sh index c05a2dc10d..5efeec2f4d 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -108,41 +108,6 @@ if [ "$build_doc" = true ]; then git commit -s -m "doc: Regenerate all docs for $VERSION" fi -declare -A ldscripts -ldscripts=( - [libnvme/src/libnvme.ld]=LIBNVME -) - -lib_ver="${ver//./_}" - -for ld_file in "${!ldscripts[@]}" -do - lib_name=${ldscripts[$ld_file]} - - if [ ! -f "${ld_file}" ]; then - continue - fi - - lib_unreleased="${lib_name}_UNRELEASED" - - # Check if UNRELEASED has symbols - if ! awk -v lib_unreleased="$lib_unreleased" ' - $0 ~ "^"lib_unreleased { in_section = 1; next } - in_section && $0 ~ /\}/ { exit } - in_section && $0 !~ /^[[:space:]]*($|\/|\/\*|\*|#)/ { found = 1; exit } - END { exit !found } - ' "${ld_file}"; then - continue - fi - - sed -i \ - -e "s/^${lib_unreleased}\s*{/&\n};\n\n${lib_name}_${lib_ver} {/" \ - "$ld_file" - - git add "${ld_file}" - echo "${ld_file} updated." -done - # update meson.build sed -i -e "0,/[ \t]version: /s/\([ \t]version: \).*/\1\'$ver\',/" meson.build git add meson.build From 2a3e2b1950bf4caddbea8969598b3db55151664a Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Thu, 12 Mar 2026 10:37:19 +0100 Subject: [PATCH 09/12] libnvme: make fabrics optional The fabrics part is rather large and usually is not necessary for embedded use cases. Also it is very Linux specific and makes any porting attempts really hard. Thus make the fabrics part optional. Signed-off-by: Daniel Wagner --- libnvme/examples/meson.build | 20 ++++--- libnvme/src/{libnvme.h => libnvme.h.in} | 3 +- libnvme/src/libnvme.ld | 64 ---------------------- libnvme/src/libnvmf.ld | 71 ++++++++++++++++++++++++ libnvme/src/meson.build | 73 +++++++++++++++++-------- libnvme/test/ioctl/meson.build | 24 ++++---- libnvme/test/meson.build | 24 ++++---- meson.build | 19 +++++-- meson_options.txt | 6 ++ nvme-builtin.h | 6 +- nvme-print-json.c | 9 +++ nvme-print-stdout.c | 9 +++ nvme.c | 2 + plugins/meson.build | 5 +- util/cleanup.h | 2 + 15 files changed, 209 insertions(+), 128 deletions(-) rename libnvme/src/{libnvme.h => libnvme.h.in} (92%) create mode 100644 libnvme/src/libnvmf.ld diff --git a/libnvme/examples/meson.build b/libnvme/examples/meson.build index 5d3e5fb237..31db6a7e17 100644 --- a/libnvme/examples/meson.build +++ b/libnvme/examples/meson.build @@ -35,15 +35,17 @@ executable( ], ) -executable( - 'discover-loop', - ['discover-loop.c'], - dependencies: [ - config_dep, - ccan_dep, - libnvme_dep, - ], -) +if want_fabrics + executable( + 'discover-loop', + ['discover-loop.c'], + dependencies: [ + config_dep, + ccan_dep, + libnvme_dep, + ], + ) +endif executable( 'mi-mctp', diff --git a/libnvme/src/libnvme.h b/libnvme/src/libnvme.h.in similarity index 92% rename from libnvme/src/libnvme.h rename to libnvme/src/libnvme.h.in index 56416dcd6e..8168a69933 100644 --- a/libnvme/src/libnvme.h +++ b/libnvme/src/libnvme.h.in @@ -15,13 +15,12 @@ extern "C" { #endif #include -#include +@FABRICS_INCLUDE@ #include #include #include #include #include -#include #include #include #include diff --git a/libnvme/src/libnvme.ld b/libnvme/src/libnvme.ld index d198f237c4..46ab0dcb3f 100644 --- a/libnvme/src/libnvme.ld +++ b/libnvme/src/libnvme.ld @@ -270,70 +270,6 @@ LIBNVME_3 { nvme_uuid_from_string; nvme_uuid_random; nvme_uuid_to_string; - nvmf_add_ctrl; - nvmf_adrfam_str; - nvmf_cms_str; - nvmf_config_modify; - nvmf_connect; - nvmf_connect_config_json; - nvmf_connect_ctrl; - nvmf_context_create; - nvmf_context_set_connection; - nvmf_context_set_crypto; - nvmf_context_set_device; - nvmf_context_set_discovery_cbs; - nvmf_context_set_discovery_defaults; - nvmf_context_set_fabrics_config; - nvmf_context_set_hostnqn; - nvmf_context_set_persistent; - nvmf_default_config; - nvmf_discovery; - nvmf_discovery_config_file; - nvmf_discovery_config_json; - nvmf_discovery_ctx_already_connected_set; - nvmf_discovery_ctx_connected_set; - nvmf_discovery_ctx_create; - nvmf_discovery_ctx_ctrlkey_set; - nvmf_discovery_ctx_decide_retry_set; - nvmf_discovery_ctx_default_fabrics_config_set; - nvmf_discovery_ctx_device_set; - nvmf_discovery_ctx_discovery_log_set; - nvmf_discovery_ctx_host_iface_set; - nvmf_discovery_ctx_host_traddr_set; - nvmf_discovery_ctx_hostid_set; - nvmf_discovery_ctx_hostkey_set; - nvmf_discovery_ctx_hostnqn_set; - nvmf_discovery_ctx_keep_alive_timeout; - nvmf_discovery_ctx_keyring_set; - nvmf_discovery_ctx_max_retries; - nvmf_discovery_ctx_parser_cleanup_set; - nvmf_discovery_ctx_parser_init_set; - nvmf_discovery_ctx_parser_next_line_set; - nvmf_discovery_ctx_persistent_set; - nvmf_discovery_ctx_subsysnqn_set; - nvmf_discovery_ctx_tls_key_identity_set; - nvmf_discovery_ctx_tls_key_set; - nvmf_discovery_ctx_traddr_set; - nvmf_discovery_ctx_transport_set; - nvmf_discovery_ctx_trsvcid_set; - nvmf_discovery_nbft; - nvmf_eflags_str; - nvmf_exat_ptr_next; - nvmf_free_uri; - nvmf_get_default_trsvcid; - nvmf_get_discovery_log; - nvmf_get_discovery_wargs; - nvmf_is_registration_supported; - nvmf_nbft_free; - nvmf_nbft_read_files; - nvmf_prtype_str; - nvmf_qptype_str; - nvmf_register_ctrl; - nvmf_sectype_str; - nvmf_subtype_str; - nvmf_treq_str; - nvmf_trtype_str; - nvmf_update_config; local: *; }; diff --git a/libnvme/src/libnvmf.ld b/libnvme/src/libnvmf.ld new file mode 100644 index 0000000000..976f300be5 --- /dev/null +++ b/libnvme/src/libnvmf.ld @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +LIBNVMF_3 { + global: + nvmf_add_ctrl; + nvmf_adrfam_str; + nvmf_cms_str; + nvmf_config_modify; + nvmf_connect; + nvmf_connect_config_json; + nvmf_connect_ctrl; + nvmf_context_create; + nvmf_context_set_connection; + nvmf_context_set_crypto; + nvmf_context_set_device; + nvmf_context_set_discovery_cbs; + nvmf_context_set_discovery_defaults; + nvmf_context_set_fabrics_config; + nvmf_context_set_hostnqn; + nvmf_context_set_persistent; + nvmf_default_config; + nvmf_discovery; + nvmf_discovery_config_file; + nvmf_discovery_config_json; + nvmf_discovery_ctx_already_connected_set; + nvmf_discovery_ctx_connected_set; + nvmf_discovery_ctx_create; + nvmf_discovery_ctx_ctrlkey_set; + nvmf_discovery_ctx_decide_retry_set; + nvmf_discovery_ctx_default_fabrics_config_set; + nvmf_discovery_ctx_device_set; + nvmf_discovery_ctx_discovery_log_set; + nvmf_discovery_ctx_host_iface_set; + nvmf_discovery_ctx_host_traddr_set; + nvmf_discovery_ctx_hostid_set; + nvmf_discovery_ctx_hostkey_set; + nvmf_discovery_ctx_hostnqn_set; + nvmf_discovery_ctx_keep_alive_timeout; + nvmf_discovery_ctx_keyring_set; + nvmf_discovery_ctx_max_retries; + nvmf_discovery_ctx_parser_cleanup_set; + nvmf_discovery_ctx_parser_init_set; + nvmf_discovery_ctx_parser_next_line_set; + nvmf_discovery_ctx_persistent_set; + nvmf_discovery_ctx_subsysnqn_set; + nvmf_discovery_ctx_tls_key_identity_set; + nvmf_discovery_ctx_tls_key_set; + nvmf_discovery_ctx_traddr_set; + nvmf_discovery_ctx_transport_set; + nvmf_discovery_ctx_trsvcid_set; + nvmf_discovery_nbft; + nvmf_eflags_str; + nvmf_exat_ptr_next; + nvmf_free_uri; + nvmf_get_default_trsvcid; + nvmf_get_discovery_log; + nvmf_get_discovery_wargs; + nvmf_is_registration_supported; + nvmf_nbft_free; + nvmf_nbft_read_files; + nvmf_prtype_str; + nvmf_qptype_str; + nvmf_register_ctrl; + nvmf_sectype_str; + nvmf_subtype_str; + nvmf_treq_str; + nvmf_trtype_str; + nvmf_update_config; + local: + *; +}; diff --git a/libnvme/src/meson.build b/libnvme/src/meson.build index 86c76870d6..4e7c9b7327 100644 --- a/libnvme/src/meson.build +++ b/libnvme/src/meson.build @@ -10,7 +10,6 @@ sources = [ 'nvme/base64.c', 'nvme/cmds.c', 'nvme/crc32.c', - 'nvme/fabrics.c', 'nvme/filters.c', 'nvme/ioctl.c', 'nvme/lib.c', @@ -18,11 +17,34 @@ sources = [ 'nvme/log.c', 'nvme/mi-mctp.c', 'nvme/mi.c', - 'nvme/nbft.c', 'nvme/sysfs.c', 'nvme/tree.c', 'nvme/util.c', ] +headers = [ + 'nvme/accessors.h', + 'nvme/cmds.h', + 'nvme/filters.h', + 'nvme/ioctl.h', + 'nvme/lib-types.h', + 'nvme/lib.h', + 'nvme/linux.h', + 'nvme/mi.h', + 'nvme/tree.h', + 'nvme/types.h', + 'nvme/util.h', +] + +if want_fabrics + sources += [ + 'nvme/fabrics.c', + 'nvme/nbft.c', + ] + headers += [ + 'nvme/fabrics.h', + 'nvme/nbft.h', + ] +endif if json_c_dep.found() sources += 'nvme/json.c' @@ -42,16 +64,36 @@ deps = [ openssl_dep, ] -ldfile = 'libnvme.ld' +nvme_ld = meson.current_source_dir() / 'libnvme.ld' +nvmf_ld = meson.current_source_dir() / 'libnvmf.ld' + +link_args = [ + '-Wl,--version-script=@0@'.format(nvme_ld), + '-Wl,--version-script=@0@'.format(accessors_ld_full_path), +] + +libconf = configuration_data() +if want_fabrics + link_args += '-Wl,--version-script=@0@'.format(nvmf_ld) + libconf.set('FABRICS_INCLUDE', + '#include \n#include ') +else + libconf.set('FABRICS_INCLUDE', '') +endif + +libnvme_header = configure_file( + input: 'libnvme.h.in', + output: 'libnvme.h', + configuration: libconf, + install: true, + install_dir: prefixdir / get_option('includedir') +) libnvme = library( 'nvme', # produces libnvme.so sources, version: libnvme_so_version, - link_args: [ - '-Wl,--version-script=@0@'.format(meson.current_source_dir() / ldfile), - '-Wl,--version-script=@0@'.format(accessors_ld_full_path), - ], + link_args: link_args, dependencies: deps, install: true, ) @@ -90,27 +132,12 @@ libnvme_test_dep = declare_dependency( mode = 'rw-r--r--' install_headers( [ - 'libnvme.h', 'libnvme-mi.h', ], install_mode: mode, ) install_headers( - [ - 'nvme/accessors.h', - 'nvme/cmds.h', - 'nvme/fabrics.h', - 'nvme/filters.h', - 'nvme/ioctl.h', - 'nvme/lib-types.h', - 'nvme/lib.h', - 'nvme/linux.h', - 'nvme/mi.h', - 'nvme/nbft.h', - 'nvme/tree.h', - 'nvme/types.h', - 'nvme/util.h', - ], + headers, subdir: 'nvme', install_mode: mode, ) diff --git a/libnvme/test/ioctl/meson.build b/libnvme/test/ioctl/meson.build index 4e0f80cebe..7aec1f3810 100644 --- a/libnvme/test/ioctl/meson.build +++ b/libnvme/test/ioctl/meson.build @@ -52,17 +52,19 @@ ana = executable( ) test('libnvme - ana', ana, env: mock_ioctl_env) -discovery = executable( - 'test-discovery', - 'discovery.c', - dependencies: [ - config_dep, - ccan_dep, - libnvme_dep, - ], - link_with: mock_ioctl, -) -test('libnvme - discovery', discovery, env: mock_ioctl_env) +if want_fabrics + discovery = executable( + 'test-discovery', + 'discovery.c', + dependencies: [ + config_dep, + ccan_dep, + libnvme_dep, + ], + link_with: mock_ioctl, + ) + test('libnvme - discovery', discovery, env: mock_ioctl_env) +endif features = executable( 'test-features', diff --git a/libnvme/test/meson.build b/libnvme/test/meson.build index 4c4d0b0529..5f952a3509 100644 --- a/libnvme/test/meson.build +++ b/libnvme/test/meson.build @@ -101,17 +101,19 @@ uuid = executable( test('libnvme - uuid', uuid) -uriparser = executable( - 'test-uriparser', - ['uriparser.c'], - dependencies: [ - config_dep, - ccan_dep, - libnvme_dep, - ], -) +if want_fabrics + uriparser = executable( + 'test-uriparser', + ['uriparser.c'], + dependencies: [ + config_dep, + ccan_dep, + libnvme_dep, + ], + ) test('libnvme - uriparser', uriparser) +endif if conf.get('HAVE_NETDB') mock_ifaddrs = library( @@ -162,7 +164,9 @@ psk = executable( test('libnvme - psk', psk) subdir('ioctl') -subdir('nbft') +if want_fabrics + subdir('nbft') +endif if json_c_dep.found() subdir('sysfs') diff --git a/meson.build b/meson.build index c81e8e2d6c..1c1d87351c 100644 --- a/meson.build +++ b/meson.build @@ -51,11 +51,12 @@ cxx_available = add_languages('cpp', required: false, native: false) # dependencies are present. Also, -Dpython=enabled forces -Dlibnvme=enabled. want_nvme = get_option('nvme').disabled() == false want_libnvme = get_option('libnvme').disabled() == false +want_fabrics = get_option('fabrics').disabled() == false want_docs = get_option('docs') want_docs_build = get_option('docs-build') feature_python = get_option('python') -if feature_python.disabled() +if not want_fabrics or feature_python.disabled() py3_dep = dependency('', required: false) # Needed for muon want_python = false else @@ -128,6 +129,8 @@ endif conf.set('SYSCONFDIR', '"@0@"'.format(sysconfdir)) conf.set('RUNDIR', '"@0@"'.format(rundir)) +conf.set('CONFIG_FABRICS', want_fabrics, description: 'Is fabrics enabled') + # Check for libjson-c availability if get_option('json-c').disabled() json_c_dep = dependency('', required: false) @@ -277,14 +280,14 @@ else endif -if get_option('liburing').disabled() +if not want_fabrics or get_option('liburing').disabled() liburing_dep = dependency('', required: false) else liburing_dep = dependency('liburing', version: '>=2.2', required: get_option('liburing')) endif conf.set('CONFIG_LIBURING', liburing_dep.found(), description: 'Is liburing available?') -if get_option('openssl').disabled() +if not want_fabrics or get_option('openssl').disabled() openssl_dep = dependency('', required: false) else openssl_dep = dependency( @@ -313,14 +316,14 @@ if openssl_dep.found() endif conf.set('CONFIG_OPENSSL', openssl_dep.found(), description: 'Is OpenSSL/LibreSSL available?') -if get_option('keyutils').disabled() +if not want_fabrics or get_option('keyutils').disabled() keyutils_dep = dependency('', required: false) else keyutils_dep = dependency('libkeyutils', required : get_option('keyutils')) endif conf.set('CONFIG_KEYUTILS', keyutils_dep.found(), description: 'Is libkeyutils available?') -if get_option('libdbus').disabled() +if not want_fabrics or get_option('libdbus').disabled() libdbus_dep = dependency('', required: false) else # Check for libdbus availability. Optional, only required for MCTP dbus scan @@ -469,7 +472,6 @@ if want_nvme subdir('util') # declares: util_sources sources = [ - 'fabrics.c', 'nvme.c', 'nvme-models.c', 'nvme-print.c', @@ -480,6 +482,10 @@ if want_nvme 'libnvme-wrap.c', 'logging.c', ] + if want_fabrics + sources += 'fabrics.c' + endif + if json_c_dep.found() sources += [ 'nvme-print-json.c', @@ -630,6 +636,7 @@ summary(dep_dict, section: 'Dependencies', bool_yn: true) wanted_dict = { 'nvme': want_nvme, 'libnvme': want_libnvme, + 'fabrics': want_fabrics, 'python bindings ': want_python, 'build docs': want_docs_build, } diff --git a/meson_options.txt b/meson_options.txt index 6fd51daf77..88c8f364cb 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -11,6 +11,12 @@ option( value: 'enabled', description: 'Build libnvme library' ) +option( + 'fabrics', + type : 'feature', + value: 'enabled', + description : 'NVMeoF support' +) option( 'python', type : 'feature', diff --git a/nvme-builtin.h b/nvme-builtin.h index 585174d9b2..3f3d82a497 100644 --- a/nvme-builtin.h +++ b/nvme-builtin.h @@ -102,12 +102,15 @@ COMMAND_LIST( ENTRY("show-regs", "Shows the controller registers or properties. Requires character device", show_registers) ENTRY("set-reg", "Set a register and show the resulting value", set_register) ENTRY("get-reg", "Get a register and show the resulting value", get_register) +#ifdef CONFIG_FABRICS ENTRY("discover", "Discover NVMeoF subsystems", discover_cmd) ENTRY("connect-all", "Discover and Connect to NVMeoF subsystems", connect_all_cmd) ENTRY("connect", "Connect to NVMeoF subsystem", connect_cmd) ENTRY("disconnect", "Disconnect from NVMeoF subsystem", disconnect_cmd) ENTRY("disconnect-all", "Disconnect from all connected NVMeoF subsystems", disconnect_all_cmd) ENTRY("config", "Configuration of NVMeoF subsystems", config_cmd) + ENTRY("dim", "Send Discovery Information Management command to a Discovery Controller", dim_cmd) +#endif ENTRY("gen-hostnqn", "Generate NVMeoF host NQN", gen_hostnqn_cmd) ENTRY("show-hostnqn", "Show NVMeoF host NQN", show_hostnqn_cmd) ENTRY("gen-dhchap-key", "Generate NVMeoF DH-HMAC-CHAP host key", gen_dhchap_key) @@ -120,8 +123,7 @@ COMMAND_LIST( ENTRY("virt-mgmt", "Manage Flexible Resources between Primary and Secondary Controller", virtual_mgmt) ENTRY("rpmb", "Replay Protection Memory Block commands", rpmb_cmd) ENTRY("lockdown", "Submit a Lockdown command,return result", lockdown_cmd) - ENTRY("dim", "Send Discovery Information Management command to a Discovery Controller", dim_cmd) \ - ENTRY("show-topology", "Show the topology", show_topology_cmd) \ + ENTRY("show-topology", "Show the topology", show_topology_cmd) ENTRY("io-mgmt-recv", "I/O Management Receive", io_mgmt_recv) ENTRY("io-mgmt-send", "I/O Management Send", io_mgmt_send) ENTRY("nvme-mi-recv", "Submit a NVMe-MI Receive command, return results", nmi_recv) diff --git a/nvme-print-json.c b/nvme-print-json.c index 5ac54164a3..5413d31c05 100644 --- a/nvme-print-json.c +++ b/nvme-print-json.c @@ -5201,6 +5201,7 @@ static void json_directive_show(__u8 type, __u8 oper, __u16 spec, __u32 nsid, __ json_print(r); } +#ifdef CONFIG_FABRICS static void json_discovery_log(struct nvmf_discovery_log *log, int numrec) { struct json_object *r = json_create_object(); @@ -5242,6 +5243,9 @@ static void json_discovery_log(struct nvmf_discovery_log *log, int numrec) json_print(r); } +#else +static void json_discovery_log(struct nvmf_discovery_log *log, int numrec) {} +#endif static void json_connect_msg(nvme_ctrl_t c) { @@ -5568,6 +5572,7 @@ static void json_reachability_associations_log(struct nvme_reachability_associat json_print(r); } +#ifdef CONFIG_FABRICS static void json_host_discovery_log(struct nvme_host_discover_log *log) { struct json_object *r = json_create_object(); @@ -5695,6 +5700,10 @@ static void json_ave_discovery_log(struct nvme_ave_discover_log *log) obj_add_obj(r, json_str, adlpe_o); } } +#else +static void json_host_discovery_log(struct nvme_host_discover_log *log) {} +static void json_ave_discovery_log(struct nvme_ave_discover_log *log) {} +#endif static void json_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *log) { diff --git a/nvme-print-stdout.c b/nvme-print-stdout.c index 394f457d84..ec5ae5c050 100644 --- a/nvme-print-stdout.c +++ b/nvme-print-stdout.c @@ -6354,6 +6354,7 @@ static void stdout_key_value(const char *key, const char *val, va_list ap) printf("%s: %s\n", key, value ? value : alloc_error); } +#ifdef CONFIG_FABRICS static void stdout_discovery_log(struct nvmf_discovery_log *log, int numrec) { int i; @@ -6396,6 +6397,9 @@ static void stdout_discovery_log(struct nvmf_discovery_log *log, int numrec) } } } +#else +static void stdout_discovery_log(struct nvmf_discovery_log *log, int numrec) {} +#endif static void stdout_connect_msg(nvme_ctrl_t c) { @@ -6490,6 +6494,7 @@ static void stdout_reachability_associations_log(struct nvme_reachability_associ } } +#ifdef CONFIG_FABRICS static void stdout_host_discovery_log(struct nvme_host_discover_log *log) { __u32 i; @@ -6600,6 +6605,10 @@ static void stdout_ave_discovery_log(struct nvme_ave_discover_log *log) } } } +#else +static void stdout_host_discovery_log(struct nvme_host_discover_log *log) {} +static void stdout_ave_discovery_log(struct nvme_ave_discover_log *log) {} +#endif static void stdout_pull_model_ddc_req_log(struct nvme_pull_model_ddc_req_log *log) { diff --git a/nvme.c b/nvme.c index 9cafa209c4..7539e937a7 100644 --- a/nvme.c +++ b/nvme.c @@ -10427,6 +10427,7 @@ static int show_topology_cmd(int argc, char **argv, struct command *acmd, struct return err; } +#ifdef CONFIG_FABRICS static int discover_cmd(int argc, char **argv, struct command *acmd, struct plugin *plugin) { const char *desc = "Send Get Log Page request to Discovery Controller."; @@ -10477,6 +10478,7 @@ static int dim_cmd(int argc, char **argv, struct command *acmd, struct plugin *p return fabrics_dim(desc, argc, argv); } +#endif static int nvme_mi(int argc, char **argv, __u8 admin_opcode, const char *desc) { diff --git a/plugins/meson.build b/plugins/meson.build index 63014b667a..0702954376 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -15,7 +15,6 @@ all_plugins = { 'mangoboost': ['plugins/mangoboost/mangoboost-nvme.c'], 'memblaze': ['plugins/memblaze/memblaze-nvme.c'], 'micron': ['plugins/micron/micron-nvme.c'], - 'nbft': ['plugins/nbft/nbft-plugin.c'], 'netapp': ['plugins/netapp/netapp-nvme.c'], 'nvidia': ['plugins/nvidia/nvidia-nvme.c'], 'sandisk': ['plugins/sandisk/sandisk-nvme.c', 'plugins/sandisk/sandisk-utils.c'], @@ -43,6 +42,10 @@ foreach plugin_name : selected_plugins endif endforeach +if want_fabrics and 'nbft' in selected_plugins + plugin_sources += ['plugins/nbft/nbft-plugin.c'] +endif + if 'feat' in selected_plugins subdir('feat') endif diff --git a/util/cleanup.h b/util/cleanup.h index 891490512a..6ef3e87673 100644 --- a/util/cleanup.h +++ b/util/cleanup.h @@ -45,12 +45,14 @@ static inline void cleanup_nvme_global_ctx(struct nvme_global_ctx **ctx) static inline DEFINE_CLEANUP_FUNC(cleanup_nvme_ctrl, nvme_ctrl_t, nvme_free_ctrl) #define _cleanup_nvme_ctrl_ __cleanup__(cleanup_nvme_ctrl) +#ifdef CONFIG_FABRICS static inline void free_uri(struct nvme_fabrics_uri **uri) { if (*uri) nvmf_free_uri(*uri); } #define _cleanup_uri_ __cleanup__(free_uri) +#endif static inline DEFINE_CLEANUP_FUNC(cleanup_file, FILE *, fclose) #define _cleanup_file_ __cleanup__(cleanup_file) From 18431c92c2965d72e3733f7328ea8d7350285f18 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 13 Mar 2026 18:09:20 +0100 Subject: [PATCH 10/12] build: use meson static build to test fabrics=disabled Add a CI test for building without fabrics code. While at it also use some non standard CFLAGS and LDFLAGS. Signed-off-by: Daniel Wagner --- .github/workflows/build.yml | 13 ++++++++++++ scripts/build.sh | 41 +++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0bf62ade4e..76ccdc5ec7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -187,6 +187,19 @@ jobs: run: | CC=musl-gcc scripts/build.sh musl + build-musl-minimal-static: + name: musl libc minimal static build on Debian + runs-on: ubuntu-latest + container: + image: ghcr.io/linux-nvme/debian:latest + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Mark repo as safe for git + run: git config --global --add safe.directory "$GITHUB_WORKSPACE" + - name: build + run: | + scripts/build.sh minimal_static + build-alpine: name: musl libc build on Alpine runs-on: ubuntu-latest diff --git a/scripts/build.sh b/scripts/build.sh index 223790f113..7aba496529 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -34,6 +34,7 @@ usage() { echo " html_docs build html documentation only" echo " rst_docs build rst documentation only" echo " static build a static binary" + echo " minimal_static build a static binary without fabrics support" echo " libnvme build only libnvme" echo "" echo "configs with muon:" @@ -194,6 +195,46 @@ config_meson_static() { "${BUILDDIR}" } +config_meson_minimal_static() { + local cflags=( + -U_GNU_SOURCE + -idirafter /usr/include + -idirafter /usr/include/x86_64-linux-gnu + -Oz + -flto + -ffunction-sections + -fdata-sections + -fno-unwind-tables + -fno-asynchronous-unwind-tables + -fno-stack-protector + ) + local ldflags=( + -flto + -Wl,--gc-sections + -s + -Wl,--build-id=none + -static + ) + local cflags_str="${cflags[*]}" + local ldflags_str="${ldflags[*]}" + + CC=musl-gcc + + CC="${CC}" "${MESON}" setup \ + --werror \ + --buildtype=release \ + --default-library=static \ + --prefix=/usr \ + -Dplugins="sed,lm,feat,zns,fdp" \ + -Dc_args="${cflags_str}" \ + -Dc_link_args="${ldflags_str}" \ + -Dfabrics=disabled \ + -Djson-c=disabled \ + -Dtests=false \ + -Dexamples=false \ + "${BUILDDIR}" +} + config_meson_libnvme() { CC="${CC}" "${MESON}" setup \ --werror \ From 61e553ff7d020e37ae591aaf217280a3ca67e2da Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 13 Mar 2026 18:50:00 +0100 Subject: [PATCH 11/12] build: use cflags array instead string Use a local array for the CFLAGS to improve readability. Signed-off-by: Daniel Wagner --- scripts/build.sh | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/build.sh b/scripts/build.sh index 7aba496529..feced9279f 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -91,14 +91,17 @@ config_meson_default() { } config_meson_musl() { - local c_args="-U_GNU_SOURCE \ --idirafter /usr/include -idirafter \ -/usr/include/x86_64-linux-gnu" + local cflags=( + -U_GNU_SOURCE + -idirafter /usr/include + -idirafter /usr/include/x86_64-linux-gnu + ) + local cflags_str="${cflags[*]}" CC="${CC}" "${MESON}" setup \ --werror \ --buildtype="${BUILDTYPE}" \ - -Dc_args="${c_args}" \ + -Dc_args="${cflags_str}" \ -Ddefault_library=static \ -Djson-c=disabled \ -Dopenssl=disabled \ From aff7afda147c2bb48b33de39765b9486803b7019 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Fri, 13 Mar 2026 19:24:42 +0100 Subject: [PATCH 12/12] linux: fix typos in documentation Fix a couple of typos. Signed-off-by: Daniel Wagner --- libnvme/src/nvme/linux.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libnvme/src/nvme/linux.h b/libnvme/src/nvme/linux.h index d1fd83ac30..6b9da6ae17 100644 --- a/libnvme/src/nvme/linux.h +++ b/libnvme/src/nvme/linux.h @@ -426,7 +426,7 @@ char *nvme_hostid_generate(); * nvme_hostnqn_from_file() - Reads the host nvm qualified name from the config * default location * - * Retrieve the qualified name from the config file located in $SYSCONFIDR/nvme. + * Retrieve the qualified name from the config file located in $SYSCONFDIR/nvme. * $SYSCONFDIR is usually /etc. * * Return: The host nqn, or NULL if unsuccessful. If found, the caller