Repository navigation
Conversation
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues are reported for the NVMe-oF compatibility update.
No open issues 🎉
What changed in this PR
Updates dracut’s NVMe-oF module to support nvme-cli 3 while preserving nvme-cli 2 compatibility.
Changes:
- Uses long nvme option names and adds installation, detection, and cleanup of
nvme-fabrics.confand its drop-ins. - Writes command-line discovery settings in the supported format and creates
/etc/nvmefor non-hostonly initrds. - Supports both legacy and renamed NetApp udev rules.
| File | Description |
|---|---|
modules.d/74nvmf/parse-nvmf-boot-connections.sh |
Updates NBFT options, handles both discovery formats, and ensures the configuration directory exists. |
modules.d/74nvmf/nvmf-autoconnect.sh |
Recognizes fabrics configuration files and drop-ins when selecting autoconnection. |
modules.d/74nvmf/module-setup.sh |
Detects and installs fabrics configuration, updates nvme options, and installs both NetApp rule names. |
Review effort: Balanced
mwilck
left a comment
There was a problem hiding this comment.
One question, otherwise LGTM.
| for _conf in "${dracutsysrootdir-}"/etc/nvme/nvme-fabrics.conf.d/*.conf; do | ||
| [[ -f $_conf ]] && inst_simple -H "${_conf#"${dracutsysrootdir-}"}" | ||
| done | ||
| inst_rules 71-nvmf-iopolicy-netapp.rules 71-nvmf-netapp.rules |
There was a problem hiding this comment.
What if both files exist, e.g. one in /usr/lib/udev/rules.d and one in /etc/udev/rules.d? This would cause duplicate work in udev rules. Does upstream (iow, nvme-cli itself) make sure that this can't happen? If yes, a comment might be in order.
There was a problem hiding this comment.
Not sure what you ask here, because I am sure you know that udevd only executes only the first match. nvme-cli is not used in the rule either. It's
# Set appropriate iopolicy for NetApp ONTAP and NetApp E-Series
ACTION=="add", SUBSYSTEM=="nvme-subsystem", ATTR{subsystype}=="nvm", ATTR{model}=="NetApp ONTAP Controller", ATTR{iopolicy}="queue-depth"
ACTION=="add", SUBSYSTEM=="nvme-subsystem", ATTR{subsystype}=="nvm", ATTR{model}=="NetApp E-Series", ATTR{iopolicy}="round-robin"
# Set ctrl_loss_tmo to -1 for NetApp ONTAP NVMe/TCP
ACTION!="remove", SUBSYSTEM=="nvme", KERNEL=="nvme*", ATTR{transport}=="tcp", ATTR{model}=="NetApp ONTAP Controller", ATTR{ctrl_loss_tmo}="-1"
I'ts about setting some of the sysfs entries.
There was a problem hiding this comment.
udevd only executes only the first match
That holds only for rules with the same file name in different directories, not for different file names.
What I meant with "nvme-cli itself" was that some tooling (perhaps in the package update logic) would notice that both files are present and delete or disable the old one. Sorry for being unclear.
There was a problem hiding this comment.
Honestly, I'm not sure why this rule is part of dracut in the first place. It was added in 2f03d69 ("95nvmf: add module for NVMe-oF"), and the rule itself only landed in nvme-cli three weeks later as 6eafcf96f315 ("nvmf-autoconnect: add udev rules to set iopolicy for certain NetApp devices"), without any explanation.
@hreinecke, do you happen to remember why you added this rule here?
This change only makes the module work with both nvme-cli 2 and nvme-cli 3 again. Upstream nvme-cli ships just one version of the rule, and distro packages normally remove the old file when they install the new one. So I don't think having both files around is a real problem in practice.
FWIW, the real root re-applies these settings after switch_root anyway, because systemd-udev-trigger.service runs:
udevadm trigger --type=all --action=add
Without the rule, the initrd runs with the default iopolicy (numa) and the default ctrl_loss_tmo (600s). A path would have to stay down for 10 minutes during the initrd before the missing -1 mattered.
nvme-cli also ships the same kind of rules for HPE Alletra and VAST Data, and those aren't included here either. That raises the question: do we want this rule in the initrd at all? And if we do, shouldn't we handle it generically for all vendors?
What do you think is the right way forward?
There was a problem hiding this comment.
Honestly, I'm not sure why this rule is part of dracut in the first place.
Good point. I don't see why it'd be necessary, either.
So I don't think having both files around is a real problem in practice.
True. Duplicate rules would possibly result if a user had modified the old rule, copying it to /etc/udev/rules.d. I tend to dislike this kind of bitrot, but we can't avoid it in all possible circumstances.
I thought you could use something like the INI trick to decide which file you want to install. But maybe I am overthinking this.
There was a problem hiding this comment.
fwiw, all udev rules can be dropped in future with nvme-cli >= 3.3. and the pre-OS PR #2825
| # connect-all also connects the NBFT's subsystems unless told not to | ||
| command -v getargbool > /dev/null || . /lib/dracut-lib.sh | ||
| if [ "$NBFT_SUPPORTED" ] && getargbool 0 rd.nvmf.nonbft; then | ||
| /usr/sbin/nvme connect-all --no-nbft |
There was a problem hiding this comment.
This is correct from a consistency point of view.
But I am unsure if it's the Right Thing™ to do. Timeout handling means that all else has failed. No root file system has been found anywhere. This is a last-resort action before dropping to the emergency prompt, which is always a pain to work with. IMO it makes sense to try anything that might provide a usable root file system at this stage.
Most of the time there will be a root=UUID=... parameter, thus the likelihood of booting a wrong or malicious root file system from NBFT despite rd.nvmf.nonbft given is very low.
I may be overlooking something of course. Opinions?
There was a problem hiding this comment.
But if the user chooses 'nonbft', why shouldn't it honor it? I would follow the principle of least surprise.
edit: and yes, this stands out in the test scenarios I currently run
There was a problem hiding this comment.
Well I suppose in that situation all bets are off. It's ok.
9ae0cf7 to
8611d16
Compare
|
I updated the branch (force-push, 9ae0cf7 → 8611d16):
If you'd rather review the two network fixes separately, I can move them to their own PR. Testing of 8611d16, with the boot setup described above (nvme-lab): the initrd boots an NVMe/TCP root through this module with nvme-cli 2.8, 2.13, 2.16, 3.2 and master. The cases cover |
The expected failures of the shutdown scenarios and the docs named the fixing nvme-cli branch, autoconnect-fixes, which is not published. The fixes are out for review as linux-nvme/nvme-cli#4405: name the PR, and its lab tree pr-4405. 2026-11-11, pr-4405 (9a9ae9006), dracut-ng/dracut#2820 at 8611d162: shutdown 8 PASS + 4 XFAIL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nvme-cli#4405 is merged: master loads nvme-fabrics for nvme-discoverd, its legacy units skip themselves while nvme-discoverd runs, and nvmf-autoconnect disconnects at shutdown. The shutdown scenarios that expected a failure on suite-master XPASSed with master 527a6ce5a. Drop suite-master from their xfail_on. 2026-11-11, dracut-ng/dracut#2820 at 8611d162: pr-4383 (7050f4087) 34 PASS + 4 XFAIL; master, 3.2, 2.16, 2.13, 2.8 as expected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nvme-cli 3.2 dropped -H as the short name of "nvme nbft show --hfi", because it collides with a global option. "-H" now selects that global option, which is deprecated, prints a warning and will be removed. The NBFT is still shown only because "nbft show" without a section option shows every section. Once the global option is gone, "nvme nbft show -H" fails, and parse-nvmf-boot-connections.sh skips the NBFT: the boot interfaces get no network configuration. Use the long option names, which are stable across releases. nvme-cli has had --hfi, --subsystem and --output-format since 2.5, the release that introduced "nbft show". Signed-off-by: Daniel Wagner <dwagner@suse.de>
nvme-cli 2.7 renamed 71-nvmf-iopolicy-netapp.rules to 71-nvmf-netapp.rules. inst_rules skips a rule that does not exist, so with any newer nvme-cli the rule, which sets the I/O policy for NetApp ONTAP controllers, is not in the initrd. Install the rule under both names. Signed-off-by: Daniel Wagner <dwagner@suse.de>
nvme-cli 3 configures the connections in /etc/nvme/nvme-fabrics.conf and the drop-ins in /etc/nvme/nvme-fabrics.conf.d/. It reads the legacy discovery.conf and config.json only as long as there is no nvme-fabrics.conf, and converts them into it. The nvmf module neither installs these files nor looks for them: - A host configured with nvme-cli 3 gets no configured connections in the initrd, and check() fails with "No discovery arguments present" unless there is an NBFT or FC. - Once nvme-fabrics.conf is in the initrd, the rd.nvmf.discover connections, which are appended to discovery.conf, are ignored. Install nvme-fabrics.conf and its drop-ins, and take them into account in check(), in nvmf-autoconnect.sh and for rd.nvmf.nostatic and rd.nvmf.discover=fc,auto. With nvme-cli 3, write each rd.nvmf.discover connection as a [Discovery Controller] section of a drop-in. nvme-cli 3 describes its --config option as an "INI configuration file", which tells it apart from nvme-cli 2. nvme-cli 2 still gets discovery.conf. Signed-off-by: Daniel Wagner <dwagner@suse.de>
parse-nvmf-boot-connections.sh writes rd.nvmf.hostnqn and rd.nvmf.hostid to /etc/nvme/hostnqn and /etc/nvme/hostid, and appends rd.nvmf.discover to /etc/nvme/discovery.conf. Only a hostonly initrd has /etc/nvme, from the host's files. In any other initrd, the writes fail with "No such file or directory": the connections are made with a generated host NQN, which a target with an access list refuses, and with nvme-cli 2 the rd.nvmf.discover connections are lost. Create the directory first. Signed-off-by: Daniel Wagner <dwagner@suse.de>
rd.nvmf.nonbft disables connecting to the targets of the NBFT. The NBFT is not parsed then, but once the initqueue times out, nvmf-autoconnect.sh runs a plain "nvme connect-all", and nvme-cli 2.5 and later connect the NBFT's subsystems as well unless --no-nbft is given. With a network up anyway (DHCP), the system then boots from the NBFT's root despite rd.nvmf.nonbft. Pass --no-nbft in the timeout case too when rd.nvmf.nonbft is set and nvme-cli supports it. Signed-off-by: Daniel Wagner <dwagner@suse.de>
ip= takes the MAC address of an interface in the BOOTIF form (52-54-00-12-34-56, ip= splits at ':'), and ifup resolves it. But parse-ip-opts keeps that form in /tmp/net.ifaces, while ifup marks the interface set up and up under its name and its address as sysfs shows it, /tmp/net.52:54:00:12:34:56.did-setup and .up. So the wait hook of the interface, all_ifaces_up() and all_ifaces_setup() never see it, and the bootdev check of the automatic DHCP never matches. Keep the address in the sysfs form, as for BOOTIF=, and don't warn at the command line hook that it doesn't exist as an interface. With systemd and a network root, the wait hook also ends when the root device appears, which hid it; iscsiroot's all_ifaces_setup() checks and an initramfs without systemd see it. Signed-off-by: Daniel Wagner <dwagner@suse.de>
systemd-network-generator reads ip=, vlan=, ifname= etc. from the kernel command line only, and it runs before the cmdline hooks. What the hooks write to /etc/cmdline.d never reaches networkd: the ip= and vlan= that nvmf writes for an NBFT HFI, or those of the iBFT. With no .network generated, networkd-config.sh adds the default one, DHCP on every interface. An NBFT boot with a static HFI only worked where a DHCP server happened to hand out the same address (QEMU's user network does: 10.0.2.15 via 10.0.2.2). Run the generator again with dracut's command line (SYSTEMD_PROC_CMDLINE, its override of /proc/cmdline), which has the kernel's and /etc/cmdline.d's. Found with an NBFT boot of a guest on a tap device without DHCP: the HFI's address was never set. Signed-off-by: Daniel Wagner <dwagner@suse.de>
The code to check the `nvme` command to support `--nbft` does not work
with busybox:
```sh
$ /usr/sbin/nvme connect-all --help 2>&1 | busybox sed -n '/[[:space:]]--nbft[[:space:]]/q1;$q0'
sed: unsupported command ;
```
Remove support for nvme-cli < 2.5 because all containers have a recent
enough version (except for opensuse where `nvme` is not installed):
```
$ containers=(alpine:busybox-edge alpine:edge arch:latest azurelinux:latest centos:latest debian:latest debian:sid fedora:latest fedora:rawhide gentoo:amd64-openrc gentoo:latest opensuse:latest ubuntu:devel ubuntu:rolling void:latest)
$ for container in "${containers[@]}"; do podman run -it --rm "ghcr.io/dracut-ng/$container" /usr/sbin/nvme connect-all --help | grep -q -- --nbft || echo "$container"; done
Error: runc: runc create failed: unable to start container process: error during container init: exec: "/usr/sbin/nvme": stat /usr/sbin/nvme: no such file or directory: OCI runtime attempted to invoke a command that was not found
opensuse:latest
```
[dwagner: also drop the check from the rd.nvmf.nonbft handling of the
initqueue timeout]
8611d16 to
23534d8
Compare
|
I updated the branch (force-push, 8611d16 → 23534d8):
The boot matrix (nvme-cli master, 3.2, 2.16, 2.13, 2.8) passes: 134 pass, 2 expected failures, no failures. The description has the details. |
This pull request makes the nvmf module work with nvme-cli 3, and keeps it working with nvme-cli 2.
nvme-cli 3 changed what the module relies on:
nbft show -H: nvme-cli 3.2 dropped-Has the short name of--hfi, because it collides with a global option.-Hnow selects that deprecated global option and only works becausenbft showwithout a section option shows every section. Once the global option is removed,nvme nbft show -Hfails, the NBFT is skipped, and the boot interfaces get no network configuration. All nvme calls now use the long option names, which exist since nvme-cli 2.5.nvme-fabrics.conf: nvme-cli 3 configures the connections in/etc/nvme/nvme-fabrics.confand its drop-ins innvme-fabrics.conf.d/, and readsdiscovery.conf/config.jsononly as long as there is nonvme-fabrics.conf. The module neither installs these files nor looks for them, and oncenvme-fabrics.confis in the initrd, therd.nvmf.discover=connections, which go todiscovery.conf, are ignored. The module now installs and checks them, and with nvme-cli 3 it writes eachrd.nvmf.discover=connection as a[Discovery Controller]section of a drop-in. With nvme-cli 2 it still writesdiscovery.conf.More fixes found while testing:
/etc/nvmein a non-hostonly initrd: only a hostonly initrd has/etc/nvme, sord.nvmf.hostnqn=,rd.nvmf.hostid=and (with nvme-cli 2)rd.nvmf.discover=were not written. The initrd connected with a generated host NQN, which a target with an access list refuses.The NetApp udev rule: nvme-cli 2.7 renamed
71-nvmf-iopolicy-netapp.rulesto71-nvmf-netapp.rules, so the rule was no longer in the initrd. It is installed under both names now.rd.nvmf.nonbftafter the initqueue timeout: withrd.nvmf.nonbftthe NBFT is not parsed, but the timeout fallback runs a plainnvme connect-all, which (nvme-cli 2.5 and later) connects the NBFT's subsystems too. With a network up anyway (DHCP), the system booted from the NBFT's root despiterd.nvmf.nonbft. The fallback now passes--no-nbftwhenrd.nvmf.nonbftis set.systemd-networkd and the
ip=of the hooks: systemd-network-generator reads the kernel command line only, so theip=/vlan=this module writes to/etc/cmdline.dfor an NBFT HFI never reached systemd-networkd. A static HFI only worked where DHCP happened to give the same address.networkd-config.shnow runs the generator again with dracut's command line.network-legacy and
ip=by MAC address: the interface's state files use the sysfs form of the address, while parse-ip-opts kept the BOOTIF form, so the wait hooks never saw such an interface come up.nvme-cli < 2.5 support dropped (Benjamin Drung's commit from chore(nvmf): drop supporting nvmi-cli < 2.5 #2759, with his authorship): the
--nbftcheck ofnvme connect-all --helpusedsed -n '...q1;$q0', which busybox'ssedrejects, so with busybox the module acted as if nvme-cli were older than 2.5. No distribution that ships a current dracut has an older nvme-cli (table in chore(nvmf): drop supporting nvmi-cli < 2.5 #2759). The check is also gone from therd.nvmf.nonbfttimeout handling above.Related nvme-cli fixes (not needed for this PR):
connect-all --nbftconnects as the NBFT's host NQN again, and the conversion of adiscovery.confwith--host-traddr=none(as this module writes it for nvme-cli 2) no longer produces annvme-fabrics.confthat fails to parse: linux-nvme/nvme-cli#4384Testing
Booted a QEMU guest whose root is an ext4 file system on an NVMe/TCP namespace (Linux nvmet target), with initrds built on openSUSE Tumbleweed (dracut 112, kernel 7.3.1, systemd + systemd-networkd, static
ip=), with this module dropped in:rd.nvmf.discover=/etc/nvme)discovery.confrd.nvmf.discover=nvme-fabrics.confdiscovery.confrd.nvmf.discover=,nvme-fabrics.confshfmt and shellcheck (also
-s busybox) are clean.NBFT boot
QEMU's firmware writes no NBFT, but QEMU hands any ACPI table to the guest (
-acpitable file=). The test setup now generates an NBFT (layout of the NVM Express Boot Specification 1.4, as libnvme parses it; checked withnvme nbft show): its HFI is the guest's NIC, its SSNS the root subsystem. The guest then boots with noip=and nord.nvmf.*target on the kernel command line: dracut sets upnbft0from the HFI andnvme connect-all --nbftconnects the root. This covers the OS side of an NBFT boot, not firmware booting from NVMe/TCP. A second subsystem on the target (no root on it) serves the cases with more than one subsystem. After switch-root the root's/initlists the NVMe controllers it finds, and each case checks whether the guest boots and which subsystems have controllers.Same setup as above, nvme-cli 3.2+ (git 43b0ee22d), this branch at 9ae0cf7:
rd.nvmf.discover=rd.nvmf.discover=×2SSNS 2: no controller foundin the log)rd.nvmf.nonbftThe
nvme-fabrics.conf(hostonly) anddiscovery.conf(hostonly) cases of the table above were repeated with the same nvme-cli on b63b9f1 (the branch before the last commit, which only changes the timeout fallback) and pass as well.Boot matrix (2026-11-13)
Rebased on main 04e04cf. nvme-lab boot scenarios (QEMU guests with an NVMe/TCP root through this module:
rd.nvmf.discover=,nvme-fabrics.conf, NBFT with static and DHCP HFIs, VLAN, tap networks without DHCP, multipath, refused paths, network-legacy, NetworkManager), with nvme-cli master, 3.2, 2.16, 2.13 and 2.8: 136 scenarios, 134 pass, 2 expected failures (3.2 connects an NBFT as the system's host NQN, fixed in master; 2.8 ignores NBFT discovery descriptors), no failures.Checklist