FAILED: patch "[PATCH] workqueue: Fix false positive stall reports" failed to apply to 5.15-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 5.15-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-5.15.y
git checkout FETCH_HEAD
git cherry-pick -x c7f27a8ab9f2f43570f0725256597a0d7abe2c5b
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026040738-stack-private-1bad@gregkh' --subject-prefix 'PATCH 5.15.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From c7f27a8ab9f2f43570f0725256597a0d7abe2c5b Mon Sep 17 00:00:00 2001
From: Song Liu <[email protected]>
Date: Sat, 21 Mar 2026 20:30:45 -0700
Subject: [PATCH] workqueue: Fix false positive stall reports
On weakly ordered architectures (e.g., arm64), the lockless check in wq_watchdog_timer_fn() can observe a reordering between the worklist insertion and the last_progress_ts update. Specifically, the watchdog can see a non-empty worklist (from a list_add) while reading a stale last_progress_ts value, causing a false positive stall report.
This was confirmed by reading pool->last_progress_ts again after holding pool->lock in wq_watchdog_timer_fn():
workqueue watchdog: pool 7 false positive detected!
lockless_ts=4784580465 locked_ts=4785033728
diff=453263ms worklist_empty=0To avoid slowing down the hot path (queue_work, etc.), recheck last_progress_ts with pool->lock held. This will eliminate the false positive with minimal overhead.
Remove two extra empty lines in wq_watchdog_timer_fn() as we are on it.
kernel/workqueue.c +21 -3
@@ -7699,8 +7699,28 @@ static void wq_watchdog_timer_fn(struct timer_list *unused)else ts = touched; -/* did we stall? */ +/* +* Did we stall? +* +* Do a lockless check first. On weakly ordered +* architectures, the lockless check can observe a +* reordering between worklist insert_work() and +* last_progress_ts update from __queue_work(). Since +* __queue_work() is a much hotter path than the timer +* function, we handle false positive here by reading +* last_progress_ts again with pool->lock held. +*/ if (time_after (now, ts + thresh)) { + scoped_guard(raw_spinlock_irqsave, &pool->lock) { + pool_ts = pool->last_progress_ts; +if (time_after (pool_ts, touched)) + ts = pool_ts; +else + ts = touched; + } +if (!time_after (now, ts + thresh)) +continue ; + lockup_detected = true; stall_time =jiffies_to_msecs (now - pool_ts) /1000 ; max_stall_time =max (max_stall_time, stall_time);
@@ -7712,8 +7732,6 @@ static void wq_watchdog_timer_fn(struct timer_list *unused)pr_cont_pool_info (pool);pr_cont (" stuck for %us!\n" , stall_time); } - - }if (lockup_detected)