At shutdown, an NVMe controller is told that the system is going down by
setting the shutdown notification bits (CC.SHN) in its configuration
register; it then flushes its caches and reports completion in its status
register (CSTS.SHST). nvme_ctrlr_shutdown() sets the bits and waits for
that report, and the kernel runs the devices' shutdown methods one after
the other, so with several controllers the waits add up: about 1.3 s for
each PM1725a, 2.6 s for two of them in series, more on a server with many
drives.
Send the notification to every initialized, non-failed controller from a
shutdown_final handler registered at SHUTDOWN_PRI_FIRST, before any
device's shutdown method runs, so the controllers do their shutdown work
at the same time; the shutdown methods then only wait for what is left.
The wait becomes the slowest drive's instead of the sum.
This is safe here: shutdown_final runs after the file systems are synced
and the buffer cache is flushed, and before any device is shut down, so no
I/O is in flight and the order of the device shutdowns (children before
parents) is unchanged; and the nvme shutdown method submits no commands
after setting CC.SHN. The latter matters because the specification lets
a controller stop processing commands once CC.SHN is set.
Linux has the same serial wait (device_shutdown() calls each driver's
.shutdown in turn). A 2022 patch split its nvme shutdown into a "set
CC.SHN" and a "wait" phase like this one; it was dropped because Linux's
nvme driver deletes its I/O queues with admin commands before it sets
CC.SHN, so the early notification is not safe there, and the maintainers
preferred a mechanism in the driver core that benefits every driver. That
replacement ("shut down devices asynchronously", v21 in September 2026)
runs each device's shutdown method in its own task with device links for
the ordering and leaves the nvme driver alone. FreeBSD's driver submits
nothing at shutdown and this change stays inside it.
Signed-off-by: Wanpeng Qian <wanpengqian@gmail.com>
Sponsored by: keelos.dev