[PATCH] nvme: make firmware activation poll interval configurable
From: guzebing
Date: Fri Jun 26 2026 - 21:06:49 EST
From: Guzebing <guzebing@xxxxxxxxxxxxx>
nvme_fw_act_work() polls the controller processing-paused status every
100 ms while firmware activation is pending. Some devices can complete
online activation in only a few hundred milliseconds, so the fixed
interval can add noticeable latency before the driver observes
completion.
Add an nvme_core.fw_act_poll_interval_ms module parameter to make the
poll interval tunable. Keep the default at 100 ms to preserve existing
behavior, and accept values from 10 ms to 100 ms so systems that need
faster completion detection can opt in to a shorter interval.
Signed-off-by: Guzebing <guzebing@xxxxxxxxxxxxx>
---
We recently observed this issue while performing online firmware
updates for Gen5 NVMe SSDs in a production environment.
During firmware activation, the kernel quiesces I/O. Detecting the end
of firmware activation earlier lets the driver unquiesce I/O earlier,
which is important for the long-tail I/O latency of production
workloads.
Operation steps:
nvme fw-download /dev/nvme13n1 \
-f ./Inventec_G238B_NVME_LDD5902Q_MZWL61T9HFLT-00B07.bin
nvme fw-commit /dev/nvme13n1 -s 0 -a 3
nvme fw-log /dev/nvme13n1
Operation device:
Samsung PM9D3a Gen5 NVMe SSD, model MZWL67T6HBLC-00B07
Firmware update: LDD5702Q -> LDD5902Q
Operation results:
100 ms polling interval: 3 polls, 311 ms
10 ms polling interval: 24 polls, 266 ms
drivers/nvme/host/core.c | 24 +++++++++++++++++++++++-
1 file changed, 23 insertions(+), 1 deletion(-)
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c
index 3b7a8f7a35426..784da06fee559 100644
--- a/drivers/nvme/host/core.c
+++ b/drivers/nvme/host/core.c
@@ -58,6 +58,21 @@ module_param_named(io_timeout, nvme_io_timeout, uint, 0644);
MODULE_PARM_DESC(io_timeout, "timeout in seconds for I/O");
EXPORT_SYMBOL_GPL(nvme_io_timeout);
+#define NVME_FW_ACT_POLL_INTERVAL_MIN_MS 10
+#define NVME_FW_ACT_POLL_INTERVAL_MAX_MS 100
+static int fw_act_poll_interval_ms_set(const char *val,
+ const struct kernel_param *kp);
+static const struct kernel_param_ops fw_act_poll_interval_ms_ops = {
+ .set = fw_act_poll_interval_ms_set,
+ .get = param_get_uint,
+};
+
+static unsigned int fw_act_poll_interval_ms = 100;
+module_param_cb(fw_act_poll_interval_ms, &fw_act_poll_interval_ms_ops,
+ &fw_act_poll_interval_ms, 0644);
+MODULE_PARM_DESC(fw_act_poll_interval_ms,
+ "firmware activation polling interval in ms (10-100)");
+
static unsigned char shutdown_timeout = 5;
module_param(shutdown_timeout, byte, 0644);
MODULE_PARM_DESC(shutdown_timeout, "timeout in seconds for controller shutdown");
@@ -106,6 +121,13 @@ module_param(disable_pi_offsets, bool, 0444);
MODULE_PARM_DESC(disable_pi_offsets,
"disable protection information if it has an offset");
+static int fw_act_poll_interval_ms_set(const char *val,
+ const struct kernel_param *kp)
+{
+ return param_set_uint_minmax(val, kp, NVME_FW_ACT_POLL_INTERVAL_MIN_MS,
+ NVME_FW_ACT_POLL_INTERVAL_MAX_MS);
+}
+
/*
* nvme_wq - hosts nvme related works that are not reset or delete
* nvme_reset_wq - hosts nvme reset works
@@ -4798,7 +4820,7 @@ static void nvme_fw_act_work(struct work_struct *work)
nvme_try_sched_reset(ctrl);
return;
}
- msleep(100);
+ msleep(READ_ONCE(fw_act_poll_interval_ms));
}
if (!nvme_change_ctrl_state(ctrl, NVME_CTRL_CONNECTING) ||
--
2.20.1