NEWS

September 18, 2026 ยท View on GitHub

Changes in 3.1 (2026-09-18)

Feature removals and incompatible changes

  • The minimum required Meson version is now 1.0.0, up from 0.62.0. Meson 1.0.0 was released in December 2022, so any reasonably current build environment should already meet it.

  • nvme gen-hostnqn must now be run as root. The DMI and device tree files it reads are readable by root only. An unprivileged run returned a random identifier instead. See nvme-gen-hostnqn(1).

libnvme

  • libnvmf_generate_hostid() now falls back to the local machine ID, after DMI and the device tree and before a random UUID. The machine ID itself is never published. The UUID is derived from it with HMAC-SHA256 and a fixed application identifier.

  • A system UUID of all zeros or all ones is now discarded as invalid.

  • A UUID-format host NQN read out of firmware-supplied NBFT data now has its UUID lower-cased on entry if the firmware got that wrong. All NQN comparisons remain plain, byte-exact, locale-independent string comparisons, per the Base Specification; only the value is normalized, at the one point it enters libnvme, not the comparison.

  • NVMe-oF URI parsing now normalizes the nvme+<transport> scheme to lower case regardless of how it was spelled, matching the Boot Specification's requirement for that token. DHCP root-path and NBFT data are not consistent about its case.

  • The API name NVME_CAP_NSSRC() changed to NVME_CAP_NSSRS(). Since the CAP property NVM Subsystem Reset Supported bit symbol name is described as NSSRS by the spec then the API name was not correct.

Build

  • -Ddocs=man and -Ddocs=all install the man pages shipped in Documentation/ unless -Ddocs-build=true is also given. The section 5 and 8 pages were never written there, so those builds failed to configure. They are regenerated from now on, and a release aborts if any page is missing.

  • Building the man pages now requires xmlto. It was optional, so a build with -Ddocs=man -Ddocs-build=true and no xmlto installed succeeded and produced no man pages at all.

Changes in 3.0 (2026-09-07)

Feature removals and incompatible changes

  • A new INI-format connection configuration parser and writer have been added (nvme-fabrics.conf + nvme-fabrics.conf.d/ drop-ins, replacing config.json/discovery.conf). See libnvme/design/CONFIG.md for the format. Existing configuration can be explicietly converted by 'nvme config convert' or implicitly by the first call of any fabric command.

  • Key management has moved into a new nvme keys plugin. gen-dhchap-key, check-dhchap-key, gen-tls-key, check-tls-key, and tls-key are now nvme keys gen-kxchap-secret, check-kxchap-secret, gen-tls-psk, check-tls-psk, insert-tls-psk, import, export, and revoke. The old commands still work as deprecated aliases, except check-tls-key --insert, which has no equivalent there. Use nvme keys insert-tls-psk instead. The DH-HMAC-CHAP naming is also renamed to KX-HMAC-CHAP throughout, matching TP4201. See nvme-keys-gen-kxchap-secret(1) and nvme-keys-check-kxchap-secret(1).

  • NVMe Base Specification 2.4 (ECN122) renames the Reservation Report data structures from "Registered Controller" to "Registrant" throughout. struct nvme_registered_ctrl and struct nvme_registered_ctrl_ext are now struct nvme_registrant and struct nvme_registrant_ext; the regctl/regctl_ds/ regctl_eds fields of struct nvme_resv_status are now regstrnt/registrant_ds/registrant_eds. nvme resv-report output changes to match: the regctl/regctls/regctlext labels and JSON keys are now regstrnt/registrants/registrantext. Update any code or scripts that reference the old names.

  • NVMe Base Specification 2.4 (TP4150) renames the "Namespace Attribute Changed" asynchronous event to "Attached Namespace Attribute Changed", the "Changed Namespace List" log page to "Changed Attached Namespace List", and the "Namespace Attribute Notices" notice to "Attached Namespace Attribute Notices" (to distinguish them from the new Allocated Namespace variants added alongside them). NVME_AER_NOTICE_NS_CHANGED is now NVME_AER_NOTICE_ATTACHED_NS_CHANGED, NVME_LOG_LID_CHANGED_NS is now NVME_LOG_LID_CHANGED_ATTACHED_NS, and NVME_CTRL_OAES_NA* is now NVME_CTRL_OAES_NSAN* (matching the spec's new OAES mnemonic). nvme discover --log-id=changed-ns still works as a deprecated alias for the new changed-attached-ns.

  • NVMe Base Specification 2.4 (TP4193) renames the "Create Exported NVM Subsystem" and "Manage Exported NVM Subsystem" admin commands to "Manage Exported NVM Subsystem Receive" and "...Send" respectively. nvme_admin_create_export_nvms is now nvme_admin_manage_export_nvms_receive, and nvme_admin_manage_export_nvms is now nvme_admin_manage_export_nvms_send. The opcode values (0x2a, 0x2d) are unchanged.

  • NVMe Management Interface Specification 2.2 (TP6039) adds a Controller Health Status Changed Flags field to the Controller Health Data Structure, and renames the NVM Subsystem Health Data Structure's Composite Controller Status field to Composite Controller Status Flags, to distinguish per-bit change flags from the status/state bits they report. In libnvme, struct nvme_mi_ctrl_health_status gains a chscf field (and enum nvme_mi_chscf) carved out of what was previously trailing reserved space, so its size is unchanged; and struct nvme_mi_nvm_ss_health_status's ccs field/enum nvme_mi_ccs are renamed to ccsf/enum nvme_mi_ccsf with bit names getting an F suffix (e.g. NVME_MI_CCS_RDY is now NVME_MI_CCSF_RDYF) and a new TCIDAF bit.

  • nvme gen-dhchap-key emits the secret it was given rather than the key transformed from it, so its output differs whenever --hmac is non-zero. --nqn/-n fed only that transform and is now accepted with a warning and ignored. See nvme-gen-dhchap-key(1).

  • nvme gen-dhchap-key rejects a --secret with an odd number of hexadecimal characters. nvme-cli 2.x padded the trailing one with a zero and emitted a secret that was never given: 63 characters ending ...bababa produced ...ababab0a.

  • nvme check-tls-key used to print the TLS PSK identity as the only line on stdout; it now prints Configured PSK is valid (HMAC <id>, length <len>) first, the identity second, and whether that identity is already in the keyring third, so a script reading the identity with head -1 gets the wrong line. Its --insert/--keyfile are gone; use nvme keys insert-tls-psk. See nvme-check-tls-key(1).

  • nvme gen-tls-key rejects a --secret with an odd number of hexadecimal characters, reports Inserted TLS PSK <serial> where 2.x said Inserted TLS key <serial>, and prints its --secret diagnostics -- including Skipping excess secret bytes -- to stdout ahead of the key rather than to stderr. See nvme-gen-tls-key(1).

  • nvme check-dhchap-key reports on the secret rather than on the key: Key is valid, Key is loaded and Key is not loaded are now Secret is ..., and the validation errors say secret where they said key. Exit codes are unchanged, but a script matching the old wording needs updating. See nvme-check-dhchap-key(1).

  • All nvme *-log commands (smart-log, ana-log, error-log, fw-log, telemetry-log, self-test-log, sanitize-log, and the rest of the get-log-page family, get-log excepted) have moved into a new nvme log plugin, dropping the redundant -log suffix, e.g. nvme smart-log is now nvme log smart. The old top-level commands still work as deprecated aliases.

  • All nvme id-*/nvm-id-*/list-* Identify commands (id-ctrl, id-ns, id-ns-granularity, id-ns-lba-format, list-ns, list-ctrl, nvm-id-ctrl, nvm-id-ns, nvm-id-ns-lba-format, primary-ctrl-caps, list-secondary, cmdset-ind-id-ns, ns-descs, id-nvmset, id-uuid, id-iocs, id-domain, and list-endgrp) have moved into a new nvme id plugin, e.g. nvme id-ctrl is now nvme id ctrl. The old top-level commands still work as deprecated aliases.

  • Namespace management commands (create-ns, delete-ns, attach-ns, detach-ns, get-ns-id) have moved into a new nvme ns plugin, e.g. nvme create-ns is now nvme ns create. The old top-level commands still work as deprecated aliases.

  • nvme ns create, nvme ns delete, nvme ns attach, and nvme ns detach (and their deprecated create-ns/delete-ns/ attach-ns/detach-ns aliases) no longer print a confirmation message on success by default; nvme ns create also no longer prints the assigned nsid. Both are now gated behind -v/--verbose. The exit code alone indicates success/failure; the assigned nsid can still be found with nvme ns list or nvme id ctrl. See nvme-ns-create(1).

  • Reservation commands (resv-acquire, resv-register, resv-release, resv-report) have moved into a new nvme resv plugin, e.g. nvme resv-acquire is now nvme resv acquire. The old top-level commands still work as deprecated aliases.

  • NVMe-MI commands (nvme-mi-recv, nvme-mi-send) have moved into a new nvme nvme-mi plugin, e.g. nvme nvme-mi-recv is now nvme nvme-mi recv. The old top-level commands still work as deprecated aliases.

  • I/O Management commands (io-mgmt-recv, io-mgmt-send) have moved into a new nvme io-mgmt plugin, e.g. nvme io-mgmt-recv is now nvme io-mgmt recv. The old top-level commands still work as deprecated aliases.

  • Directive commands (dir-receive, dir-send) have moved into a new nvme dir plugin, e.g. nvme dir-receive is now nvme dir receive. The old top-level commands still work as deprecated aliases.

  • Security Send/Receive commands (security-send, security-recv) have moved into a new nvme security plugin, e.g. nvme security-send is now nvme security send. The old top-level commands still work as deprecated aliases.

  • Firmware commands (fw-commit, fw-download) have moved into a new nvme fw plugin, e.g. nvme fw-download is now nvme fw download. The old top-level commands still work as deprecated aliases.

  • The RPMB command has moved into a new nvme rpmb plugin and, since RPMB is not part of the NVMe base specification, has been split into one subcommand per action instead of a --cmd=<action> flag: nvme rpmb --cmd=info is now nvme rpmb info, and likewise program-key, read-counter, read-data, write-data, read-config, and write-config. Unlike the other command moves above, the old nvme rpmb --cmd=... form no longer works at all; there is no deprecated alias. See nvme-rpmb-info(1) and friends.

  • Deprecated commands (currently the old nvme keys, nvme log, nvme id, nvme ns, nvme resv, nvme nvme-mi, nvme io-mgmt, nvme dir, nvme security, and nvme fw aliases above) are enabled by default; they can be turned off at build time with -Ddeprecated-cmds=disabled. nvme help now lists them in a separate "deprecated" section instead of alongside current commands, and running one prints a warning that it will be removed in the next major version.

  • The project now builds with -std=gnu11 instead of -std=gnu99, raising the minimum required compiler support from C99 to C11. A toolchain new enough for C11 is now required to build nvme-cli and libnvme.

  • nvme disconnect-all with no options no longer disconnects every fabric controller. It now only disconnects controllers with no recorded owner in the new ownership registry. A controller owned by another orchestrator is silently skipped. To restore the old disconnect-everything behavior.

  • libnvme itself no longer resolves hostnames. libnvmf_add_ctrl() and libnvmf_connect_ctrl() now fail immediately on a hostname traddr/host_traddr instead of resolving it internally. The caller is responsible for resolving first.

  • --dump-config has been removed from nvme discover, connect-all, and connect.

  • In libnvme, the per-command nvme_<cmd>() wrapper functions and their struct nvme_<cmd>_args argument bundles have been replaced by an nvme_init_<cmd>() helper that fills a caller-owned struct libnvme_passthru_cmd, submitted separately with libnvme_exec_admin_passthru() / libnvme_exec_io_passthru() (or the async equivalents).

  • The public NBFT parsing API has moved from the libnvme_* namespace to libnvmf_*, and every function now takes the global context as its first argument (for example, libnvmf_read_nbft(ctx, ...)). Update both the prefix and the call signature.

  • The -Dpdc-enabled meson build option has been removed. Whether a discovery controller connection is kept persistent is now decided at runtime, via --persistent/nvme-fabrics.conf, not at build time.

New: Windows support

  • nvme-cli and libnvme now build and run on Windows (MSYS2 UCRT64). Direct PCIe access via ioctl works; NVMe-oF fabrics, MI, nvme-discoverd, and nvme top are not yet available on this platform.

New: nvme top

  • nvme top is a new interactive, top-like dashboard for NVMe devices. It lists subsystems and lets you drill into one to see live namespace, path, and controller stats, including command retry/error counts and multipath failover/reconnect counts.

New: ownership registry and exclusion list

  • A new ownership registry (nvme registry, backed by /run/nvme/registry/) records which orchestrator owns each connected controller. This is what lets disconnect-all (above) and future orchestrator tooling avoid tearing down a connection another component depends on. See libnvme/design/REGISTRY.md.

  • A new system-wide exclusion list (nvme exclusion, backed by /etc/nvme/exclusions.conf and exclusions.conf.d/ drop-ins) lets an administrator block specific controls from being auto-connected. It's aimed primarily at auto-discovered controllers, which have no config entry to remove in order to suppress an unwanted connection. See libnvme/design/EXCLUSIONS.md.

nvme-cli

  • A new global config file, /etc/nvme/nvme-cli.conf, sets machine-wide defaults for global options like --timeout, --output-format, --no-retries, and --no-ioctl-probing. See nvme-cli.conf(5).

  • nvme disconnect now accepts the same identifying options as nvme connect (--nqn, --transport, --traddr, --host-iface, --hostnqn, --hostid). A new -x/--exclude also writes a matching entry to the exclusion list before disconnecting. See nvme-disconnect(1).

  • nvme utils dump-command-metadata prints the full command and option tree as JSON. It is meant to drive shell-completion generation.

  • nvme connect gained --idempotent and --devid-file. --idempotent makes connecting to an already-connected controller succeed instead of erroring. --devid-file writes the resulting nvmeX device name to the given file on success, so a caller that doesn't know the device name at connect time (for example, a systemd unit spawned before the device exists) can look it up afterward instead of scraping dmesg or polling sysfs.

  • nvme config-convert converts the legacy config.json and discovery.conf files to the new INI-format nvme-fabrics.conf. By default it reads the system paths and writes to /etc/nvme/nvme-fabrics.conf. --config overrides the config.json path, --output the destination, and --force allows overwriting an existing target. On success each converted legacy file is renamed to <name>.converted so running the command again is a safe no-op. See nvme-config-convert(1).

libnvme

  • The library has been renamed from libnvme to libnvme3, and the previously separate libnvme-mi has been merged into it. The shared library SONAME is libnvme3.so.1. Headers install under include/libnvme3/ and man pages under a versioned path, enabling parallel installation alongside libnvme v1 packages. The Python binding is now named libnvme3. Packages, build systems, and Python scripts that reference the library, its headers, or the Python module by name must update.

  • nvme-fabrics.conf entries can now record a persistent setting per discovery controller, matching the --persistent CLI option.

  • New diagnostic accessors report per-path, per-namespace, and per-controller command retry/error counts, multipath failover count, and controller reset/reconnect counts. These are always read live rather than cached, and back nvme top.

  • The Python bindings' object-oriented surface has been polished: connected() and is_registration_supported() are now read-only properties (connected, registration_supported), registration_ctrl() is renamed registration_control(), and set_symname() has been removed. Set host.hostsymname directly instead.

  • The NBFT parser has been updated for NVMe Boot Specification rev. 1.3, including Security Profile Descriptors, and hardened against malformed or malicious NBFT ACPI tables.

  • ioctl version probing is now deferred until the first command on a transport handle, instead of happening eagerly on open. The probe prefers the 64-bit ioctl and falls back to the 32-bit one only if it isn't supported; the result is cached per handle. The new --no-ioctl-probing global option skips probing and forces the 32-bit ioctl.

  • Controller and namespace sysfs attributes are now read lazily, on first access, instead of all at once when the topology tree is scanned. This cuts overhead when scanning large fabric setups.

  • Connect and discover now share a common struct libnvmf_context internally, replacing the old discovery-only context. Relevant to developers extending the fabrics API, not to CLI end users.

  • A new internal shared/ static library holds code with no natural home in nvme-cli, libnvme, nvme-discoverd, or the Python bindings (INI parsing, base64, CRC32, filesystem/network helpers).

  • Read-only Python bindings for the new config format (config_read(), config_validate()) are available now for early adopters (e.g. nvme-stas) that want to start reading the new format ahead of the CLI's own switch-over.

  • The accessor generator that produces libnvme's getter/setter boilerplate (accessors.c/.h) has been rewritten in Python, with the struct annotations it reads now living in-source in private.h instead of a separate spec file. Relevant to developers extending libnvme's public structs, not to CLI end users.

  • <nvme/nvme-cmds.h> and <nvme/nvme-types.h> have each been split into per-spec-section sub-headers (nvme-cmds-base.h, nvme-cmds-fabrics.h, nvme-cmds-mi.h, similarly for nvme-types). The top-level headers still exist and include all sub-headers, so code that includes them is unaffected. Code that already included a sub-header by its old combined-file path must update the include path.

  • nvme_root_t has been replaced by struct libnvme_global_ctx * throughout the public API. Code that stored or passed nvme_root_t must update to the new type.

  • A new transport handle abstraction (struct nvme_transport_handle, libnvme_transport_handle_*) decouples command issuing from the underlying transport. A handle may wrap either a direct ioctl file descriptor or an MI endpoint.

  • An explicit async passthru API has been added. libnvme_submit_admin_passthru() / libnvme_submit_io_passthru() submit a command without waiting; libnvme_wait_passthru() / libnvme_reap_passthru() collect the result later. The synchronous path is libnvme_exec_admin_passthru() / libnvme_exec_io_passthru().

  • libnvmf_host_get_ids() has been promoted from an internal helper to a public API. It resolves the hostnqn and hostid from all available sources and is the recommended way for callers to obtain host identity before connect or discover.

  • The implicit hostnqn/hostid lookup inside the fabrics connect and discover code paths has been removed. Callers must now resolve host identity before connecting; use the newly public libnvmf_host_get_ids() helper or set values explicitly with libnvme_global_ctx_set_hostnqn() / libnvme_global_ctx_set_hostid().

  • The sizeof_args backward-compatibility macro and the old struct-based command-argument shim have been removed. The macro returned incorrect values on 32-bit architectures; there is no intention to fix it.

  • The platform-specific filter helpers (nvme_filter_*) are no longer exported from libnvme. Only the libnvme_scan_* interfaces remain public. Plugins that called filter helpers directly must be updated to use the scan API.

  • libnvme_ctrl_match_config() has been removed. It was exported but had no callers outside the library.

  • libnvmf_ctrl_get_fabrics_config() has been removed. The individual field accessors generated by the new accessor machinery provide equivalent access without exposing the internal nested struct.

  • nvme_mi_ctrl_t has been removed. MI functionality is now accessed through struct nvme_transport_handle, the same abstraction used for direct ioctl access; the transport is chosen at handle construction. nvme_mi_admin_identify() and nvme_mi_admin_identify_partial() are gone; use libnvme_ctrl_identify() with a transport handle opened for MI.

  • Several deprecated identifiers have been removed for 3.0: the duplicate NVME_SC_FEAT_IOCS_COMBINATION_REJECTED define, the old notification-mask enum names, MI backward-compatibility #defines, and nvme_cmd_get_log_telemetry_host_lsp (renamed to nvme_log_telemetry_host_lsp). Update any code using these names.

  • The environment-variable configuration knobs for libnvme have been removed. LIBNVME_MI_PROBE_ENABLED, LIBNVME_TEST_BASE_DIR, and similar variables no longer have any effect. Replace them with explicit setter calls on the global context: libnvme_set_probe_enabled(), libnvme_set_test_base_dir(), libnvme_set_test_sysfs_dir(), libnvme_set_force_4k(), etc.