* [PATCH] blk-mq: dispatch kworker to housekeeping CPU when hctx cpumask is empty
@ 2026-10-12 1:29 Aaron Tomlin
2026-10-12 1:56 ` Ming Lei
0 siblings, 1 reply; 4+ messages in thread
From: Aaron Tomlin @ 2026-10-12 1:29 UTC (permalink / raw)
To: axboe; +Cc: tom.leiming, hare, atomlin, sean, linux-block, linux-kernel, stable
Commit a46c27026da1 ("blk-mq: don't schedule block kworker on isolated
CPUs") excluded isolated CPUs from hctx->cpumask to prevent worker
threads from executing block work on isolated CPUs. When all CPUs mapped
to a hardware queue are isolated (i.e. blk_mq_hctx_empty_cpumask(hctx)
evaluates to true), blk_mq_hctx_next_cpu() returns WORK_CPU_UNBOUND with
the intention of offloading execution.
However, kblockd_workqueue is allocated as a per-CPU workqueue
(WQ_PERCPU). When WORK_CPU_UNBOUND is queued on a per-CPU workqueue, the
workqueue core resolves the target CPU via raw_smp_processor_id().
Consequently, if a task executing on an isolated CPU submits I/O and
triggers an asynchronous queue dispatch with zero delay, &hctx->run_work
is queued directly onto the local isolated CPU's worker pool. This
causes the local per-CPU high-priority kworker
(e.g. "kworker/6:1H-kblockd") to be scheduled on the isolated CPU,
introducing unwanted latency jitter and execution delays, thereby
defeating the original objective of commit a46c27026da1.
Rectify this by selecting an online housekeeping CPU via
housekeeping_any_cpu(HK_TYPE_TIMER) when blk_mq_hctx_empty_cpumask(hctx)
is true. This ensures that:
1. The delayed work is cleanly offloaded to a housekeeping CPU.
2. The per-CPU high-priority kworker on the isolated CPU is not
scheduled.
3. Hardware queues with valid mapped CPUs preserve their per-CPU
locality and batch round-robin behaviour.
Fixes: a46c27026da1 ("blk-mq: don't schedule block kworker on isolated CPUs")
Cc: Ming Lei <ming.lei@redhat.com>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: stable@vger.kernel.org
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
---
block/blk-mq.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 025a799f35b9..9480c3aac898 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -2252,8 +2252,11 @@ static int blk_mq_hctx_next_cpu(struct blk_mq_hw_ctx *hctx)
bool tried = false;
int next_cpu = hctx->next_cpu;
- /* Switch to unbound if no allowable CPUs in this hctx */
- if (hctx->queue->nr_hw_queues == 1 || blk_mq_hctx_empty_cpumask(hctx))
+ /* Switch to housekeeping CPU if no allowable CPUs in this hctx */
+ if (blk_mq_hctx_empty_cpumask(hctx))
+ return housekeeping_any_cpu(HK_TYPE_TIMER);
+
+ if (hctx->queue->nr_hw_queues == 1)
return WORK_CPU_UNBOUND;
if (--hctx->next_cpu_batch <= 0) {
--
2.55.0
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH] blk-mq: dispatch kworker to housekeeping CPU when hctx cpumask is empty 2026-10-12 1:29 [PATCH] blk-mq: dispatch kworker to housekeeping CPU when hctx cpumask is empty Aaron Tomlin @ 2026-10-12 1:56 ` Ming Lei 2026-10-12 2:21 ` Aaron Tomlin 0 siblings, 1 reply; 4+ messages in thread From: Ming Lei @ 2026-10-12 1:56 UTC (permalink / raw) To: Aaron Tomlin; +Cc: axboe, hare, sean, linux-block, linux-kernel, stable On Sun, Oct 11, 2026 at 8:29 PM Aaron Tomlin <atomlin@atomlin.com> wrote: > > Commit a46c27026da1 ("blk-mq: don't schedule block kworker on isolated > CPUs") excluded isolated CPUs from hctx->cpumask to prevent worker > threads from executing block work on isolated CPUs. When all CPUs mapped > to a hardware queue are isolated (i.e. blk_mq_hctx_empty_cpumask(hctx) > evaluates to true), blk_mq_hctx_next_cpu() returns WORK_CPU_UNBOUND with > the intention of offloading execution. > > However, kblockd_workqueue is allocated as a per-CPU workqueue > (WQ_PERCPU). When WORK_CPU_UNBOUND is queued on a per-CPU workqueue, the > workqueue core resolves the target CPU via raw_smp_processor_id(). Then it looks like you need to fix WORK_CPU_UNBOUND, which has more wide uses, if you think it is one problem. > Consequently, if a task executing on an isolated CPU submits I/O and > triggers an asynchronous queue dispatch with zero delay, &hctx->run_work > is queued directly onto the local isolated CPU's worker pool. This If your theory is correct, why do botter to change to housekeeping_any_cpu(HK_TYPE_TIMER) if the user is submitting IO from an isolated CPU? > causes the local per-CPU high-priority kworker > (e.g. "kworker/6:1H-kblockd") to be scheduled on the isolated CPU, > introducing unwanted latency jitter and execution delays, thereby > defeating the original objective of commit a46c27026da1. > > Rectify this by selecting an online housekeeping CPU via > housekeeping_any_cpu(HK_TYPE_TIMER) when blk_mq_hctx_empty_cpumask(hctx) > is true. This ensures that: > 1. The delayed work is cleanly offloaded to a housekeeping CPU. > > 2. The per-CPU high-priority kworker on the isolated CPU is not > scheduled. > > 3. Hardware queues with valid mapped CPUs preserve their per-CPU > locality and batch round-robin behaviour. > > Fixes: a46c27026da1 ("blk-mq: don't schedule block kworker on isolated CPUs") > Cc: Ming Lei <ming.lei@redhat.com> > Cc: Jens Axboe <axboe@kernel.dk> > Cc: stable@vger.kernel.org > Signed-off-by: Aaron Tomlin <atomlin@atomlin.com> > --- > block/blk-mq.c | 7 +++++-- > 1 file changed, 5 insertions(+), 2 deletions(-) > > diff --git a/block/blk-mq.c b/block/blk-mq.c > index 025a799f35b9..9480c3aac898 100644 > --- a/block/blk-mq.c > +++ b/block/blk-mq.c > @@ -2252,8 +2252,11 @@ static int blk_mq_hctx_next_cpu(struct blk_mq_hw_ctx *hctx) > bool tried = false; > int next_cpu = hctx->next_cpu; > > - /* Switch to unbound if no allowable CPUs in this hctx */ > - if (hctx->queue->nr_hw_queues == 1 || blk_mq_hctx_empty_cpumask(hctx)) > + /* Switch to housekeeping CPU if no allowable CPUs in this hctx */ > + if (blk_mq_hctx_empty_cpumask(hctx)) > + return housekeeping_any_cpu(HK_TYPE_TIMER); > + > + if (hctx->queue->nr_hw_queues == 1) > return WORK_CPU_UNBOUND; Why is SQ so special here to keep WORK_CPU_UNBOUND? Thanks, Ming Lei ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] blk-mq: dispatch kworker to housekeeping CPU when hctx cpumask is empty 2026-10-12 1:56 ` Ming Lei @ 2026-10-12 2:21 ` Aaron Tomlin 2026-10-12 2:51 ` Ming Lei 0 siblings, 1 reply; 4+ messages in thread From: Aaron Tomlin @ 2026-10-12 2:21 UTC (permalink / raw) To: Ming Lei; +Cc: axboe, hare, sean, linux-block, linux-kernel, stable On Sun, Oct 11, 2026 at 08:56:07PM -0500, Ming Lei wrote: > On Sun, Oct 11, 2026 at 8:29 PM Aaron Tomlin <atomlin@atomlin.com> wrote: > > > > Commit a46c27026da1 ("blk-mq: don't schedule block kworker on isolated > > CPUs") excluded isolated CPUs from hctx->cpumask to prevent worker > > threads from executing block work on isolated CPUs. When all CPUs mapped > > to a hardware queue are isolated (i.e. blk_mq_hctx_empty_cpumask(hctx) > > evaluates to true), blk_mq_hctx_next_cpu() returns WORK_CPU_UNBOUND with > > the intention of offloading execution. > > > > However, kblockd_workqueue is allocated as a per-CPU workqueue > > (WQ_PERCPU). When WORK_CPU_UNBOUND is queued on a per-CPU workqueue, the > > workqueue core resolves the target CPU via raw_smp_processor_id(). > > Then it looks like you need to fix WORK_CPU_UNBOUND, which has more wide uses, > if you think it is one problem. Hi Ming, Since kblockd_workqueue is WQ_PERCPU, the responsibility rests with blk-mq to provide a non-isolated CPU when dispatching &hctx->run_work, no? > > Consequently, if a task executing on an isolated CPU submits I/O and > > triggers an asynchronous queue dispatch with zero delay, &hctx->run_work > > is queued directly onto the local isolated CPU's worker pool. This > > If your theory is correct, why do botter to change to > housekeeping_any_cpu(HK_TYPE_TIMER) > if the user is submitting IO from an isolated CPU? I suspect, steering the asynchronous dispatch to a housekeeping CPU is both architecturally necessary and fully aligned with the rest of the kernel, no? When a queue is plugged, throttled, or returns BLK_STS_RESOURCE, blk-mq defers execution to a background workqueue. That work runs asynchronously at some indeterminate time in the future, long after the thread has returned to its time-critical user loop. Having a kworker suddenly preempt the thread later is likely not desired. > > diff --git a/block/blk-mq.c b/block/blk-mq.c > > index 025a799f35b9..9480c3aac898 100644 > > --- a/block/blk-mq.c > > +++ b/block/blk-mq.c > > @@ -2252,8 +2252,11 @@ static int blk_mq_hctx_next_cpu(struct blk_mq_hw_ctx *hctx) > > bool tried = false; > > int next_cpu = hctx->next_cpu; > > > > - /* Switch to unbound if no allowable CPUs in this hctx */ > > - if (hctx->queue->nr_hw_queues == 1 || blk_mq_hctx_empty_cpumask(hctx)) > > + /* Switch to housekeeping CPU if no allowable CPUs in this hctx */ > > + if (blk_mq_hctx_empty_cpumask(hctx)) > > + return housekeeping_any_cpu(HK_TYPE_TIMER); > > + > > + if (hctx->queue->nr_hw_queues == 1) > > return WORK_CPU_UNBOUND; > > Why is SQ so special here to keep WORK_CPU_UNBOUND? Indeed, it is not special here, and neither is the offline CPU fallback path (!cpu_online(next_cpu)). Both currently return WORK_CPU_UNBOUND and suffer from the exact same issue if triggered from an isolated CPU. To address this consistently without penalising non-isolated CPUs with unnecessary cross-CPU bounces, we can define a small helper. Furthermore, the following will address the valid issue raised by Sashiko [1] comprehensively, using the new helper: 1. When executing on an isolated CPU cpu_is_isolated(raw_smp_processor_id()) evaluates to true. It redirects to an online housekeeping CPU (housekeeping_any_cpu(HK_TYPE_TIMER)), preventing kworkers from running on isolated cores across all three cases: - Single-queue devices (nr_hw_queues == 1) - Hardware queues with empty cpumasks (blk_mq_hctx_empty_cpumask(hctx)) - Offline CPU fallback paths (!cpu_online(next_cpu)) 2. When executing on a housekeeping CPU cpu_is_isolated() is false. It returns WORK_CPU_UNBOUND, which kblockd_workqueue executes on the local CPU (raw_smp_processor_id()), preserving optimal cache locality with zero CPU bouncing. 3. When CPU isolation is not configured cpu_is_isolated() uses a static branch (static_branch_unlikely(&housekeeping_overridden)), compiling to a NOP that returns WORK_CPU_UNBOUND with zero overhead. [1]: https://sashiko.dev/#/patchset/20261012012905.267920-1-atomlin%40atomlin.com diff --git a/block/blk-mq.c b/block/blk-mq.c index e28c467a18bb..9fb1a93b2bb1 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -2244,6 +2244,13 @@ static bool blk_mq_hctx_empty_cpumask(struct blk_mq_hw_ctx *hctx) return hctx->next_cpu >= nr_cpu_ids; } +static inline int blk_mq_hctx_unbound_cpu(void) +{ + if (cpu_is_isolated(raw_smp_processor_id())) + return housekeeping_any_cpu(HK_TYPE_TIMER); + return WORK_CPU_UNBOUND; +} + /* * It'd be great if the workqueue API had a way to pass * in a mask and had some smarts for more clever placement. @@ -2256,7 +2263,7 @@ static int blk_mq_hctx_next_cpu(struct blk_mq_hw_ctx *hctx) /* Switch to unbound if no allowable CPUs in this hctx */ if (hctx->queue->nr_hw_queues == 1 || blk_mq_hctx_empty_cpumask(hctx)) - return WORK_CPU_UNBOUND; + return blk_mq_hctx_unbound_cpu(); if (--hctx->next_cpu_batch <= 0) { select_cpu: @@ -2281,7 +2288,7 @@ static int blk_mq_hctx_next_cpu(struct blk_mq_hw_ctx *hctx) */ hctx->next_cpu = next_cpu; hctx->next_cpu_batch = 1; - return WORK_CPU_UNBOUND; + return blk_mq_hctx_unbound_cpu(); } hctx->next_cpu = next_cpu; Kind regards, -- Aaron Tomlin ^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH] blk-mq: dispatch kworker to housekeeping CPU when hctx cpumask is empty 2026-10-12 2:21 ` Aaron Tomlin @ 2026-10-12 2:51 ` Ming Lei 0 siblings, 0 replies; 4+ messages in thread From: Ming Lei @ 2026-10-12 2:51 UTC (permalink / raw) To: Aaron Tomlin; +Cc: axboe, hare, sean, linux-block, linux-kernel, stable On Sun, Oct 11, 2026 at 10:21:01PM -0400, Aaron Tomlin wrote: > On Sun, Oct 11, 2026 at 08:56:07PM -0500, Ming Lei wrote: > > On Sun, Oct 11, 2026 at 8:29 PM Aaron Tomlin <atomlin@atomlin.com> wrote: > > > > > > Commit a46c27026da1 ("blk-mq: don't schedule block kworker on isolated > > > CPUs") excluded isolated CPUs from hctx->cpumask to prevent worker > > > threads from executing block work on isolated CPUs. When all CPUs mapped > > > to a hardware queue are isolated (i.e. blk_mq_hctx_empty_cpumask(hctx) > > > evaluates to true), blk_mq_hctx_next_cpu() returns WORK_CPU_UNBOUND with > > > the intention of offloading execution. > > > > > > However, kblockd_workqueue is allocated as a per-CPU workqueue > > > (WQ_PERCPU). When WORK_CPU_UNBOUND is queued on a per-CPU workqueue, the > > > workqueue core resolves the target CPU via raw_smp_processor_id(). > > > > Then it looks like you need to fix WORK_CPU_UNBOUND, which has more wide uses, > > if you think it is one problem. > > Hi Ming, > > Since kblockd_workqueue is WQ_PERCPU, the responsibility rests with blk-mq > to provide a non-isolated CPU when dispatching &hctx->run_work, no? Does every WORK_CPU_UNBOUND user have to handle it? > > > > Consequently, if a task executing on an isolated CPU submits I/O and > > > triggers an asynchronous queue dispatch with zero delay, &hctx->run_work > > > is queued directly onto the local isolated CPU's worker pool. This > > > > If your theory is correct, why do botter to change to > > housekeeping_any_cpu(HK_TYPE_TIMER) > > if the user is submitting IO from an isolated CPU? > > I suspect, steering the asynchronous dispatch to a housekeeping CPU is both > architecturally necessary and fully aligned with the rest of the kernel, > no? When a queue is plugged, throttled, or returns BLK_STS_RESOURCE, blk-mq > defers execution to a background workqueue. That work runs asynchronously > at some indeterminate time in the future, long after the thread has > returned to its time-critical user loop. Having a kworker suddenly preempt > the thread later is likely not desired. We are talking about `if a task executing on an isolated CPU submits I/O `. If the task has been submitting IO on isolated CPU, why can't we keep scheduling WQ on isolated CPU for running blk-mq queue? I am not sure your change makes any difference from user viewpoint. Thanks, Ming ^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-10-12 2:51 UTC | newest] Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-10-12 1:29 [PATCH] blk-mq: dispatch kworker to housekeeping CPU when hctx cpumask is empty Aaron Tomlin 2026-10-12 1:56 ` Ming Lei 2026-10-12 2:21 ` Aaron Tomlin 2026-10-12 2:51 ` Ming Lei
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®