* [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®