[PATCH] dm mpath: cap queue_if_no_path_timeout_secs
From: Yingjie Wang
Date: Thu Nov 05 2026 - 10:57:47 EST
enable_nopath_timeout() arms the no-path timer with
jiffies + queue_if_no_path_timeout_secs * HZ. The module parameter is an
unbounded unsigned long, so the delay can exceed what mod_timer()
handles: a delay above LONG_MAX is treated as already expired, and
values above ULONG_MAX / HZ also wrap in the multiplication. The timer
then clears queue_if_no_path right away, and I/O queued after the last
path fails gets -EIO instead of waiting for the configured time.
On x86-64 with HZ=1000, 9223372036854776 seconds failed a write after
124 ms while one second less remained queued throughout a 4.2-second
observation window. A value of 18446744073709552 seconds wrapped to 384
jiffies and failed the write after 455 ms. On 32-bit kernels with
HZ=1000 the delay passes LONG_MAX above 2147483 seconds, a little under
25 days.
Cap the seconds value at the largest whole-second delay not exceeding
MAX_JIFFY_OFFSET before multiplying by HZ. This prevents signed-delta
overflow and multiplication wrap. The timer wheel's existing handling
of very long delays is unchanged.
Fixes: be240ff5e402 ("dm mpath: Add timeout mechanism for queue_if_no_path")
Cc: stable@xxxxxxxxxxxxxxx
Signed-off-by: Yingjie Wang <x-professor@xxxxxxxxxxx>
---
Notes:
Tested on x86-64 (HZ=1000) in QEMU with a single-path multipath table
over a loop device, failing the path with "fail_path" and writing with
O_DIRECT. With the patch both values above stayed queued for the 4.2 s
observation window (three boots for 9223372036854776), and a 1-second
control still failed after about 1.1 s as on the base kernel. Also built
for i386 with W=1.
drivers/md/dm-mpath.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 2f871a23a..d2e94b96d 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -797,7 +797,8 @@ static void queue_if_no_path_timeout_work(struct timer_list *t)
static void enable_nopath_timeout(struct multipath *m)
{
unsigned long queue_if_no_path_timeout =
- READ_ONCE(queue_if_no_path_timeout_secs) * HZ;
+ min_t(unsigned long, READ_ONCE(queue_if_no_path_timeout_secs),
+ MAX_JIFFY_OFFSET / HZ) * HZ;
lockdep_assert_held(&m->lock);
base-commit: 875a7161fe1574205293c36f7bbd0815023e48a1
--
2.43.0