Re: [PATCH] blk-mq: dispatch kworker to housekeeping CPU when hctx cpumask is empty

From: Aaron Tomlin

Date: Mon Oct 12 2026 - 12:35:36 EST


On Sun, Oct 11, 2026 at 09:51:25PM -0500, Ming Lei wrote:
> > 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?

[Adding Tejun Heo]

No, because almost all other subsystems passing WORK_CPU_UNBOUND do so on
unbound workqueues (e.g. system_unbound_wq).

In kernel/workqueue.c, when WORK_CPU_UNBOUND is queued on a WQ_UNBOUND
workqueue, workqueue core already handles CPU isolation automatically via
wq_select_unbound_cpu(), which respects wq_unbound_cpumask and steers the
work item to a housekeeping CPU.

kblockd_workqueue is unique in that it is allocated as WQ_PERCPU to
preserve per-CPU locality for multi-queue devices. For WQ_PERCPU
workqueues, workqueue core cannot silently migrate WORK_CPU_UNBOUND to a
remote CPU because callers of bound per-CPU workqueues expect local per-CPU
execution. Thus, blk-mq is in the unusual position of passing
WORK_CPU_UNBOUND to a bound per-CPU workqueue with the expectation that it
avoids isolated cores.

> > 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.

When a task submits I/O, synchronous submission occurs directly in syscall
context. Note, in the following example, IOCB_DIRECT is applied since the
file was opened with file->f_flags & O_DIRECT:

__x64_sys_write
ksys_write
vfs_write
else if (file->f_op->write_iter)
new_sync_write(file, buf, count, pos)
// filp->f_op->write_iter(&kiocb, &iter)
ext4_file_write_iter
if (iocb->ki_flags & IOCB_DIRECT)
ext4_dio_write_iter(iocb, from)
iomap_dio_rw(iocb, from, &ext4_iomap_ops,
&ext4_dio_write_ops, dio_flags, NULL, 0)
__iomap_dio_rw
...
iomap_dio_submit_bio
blk_crypto_submit_bio(bio)
if (!bio_has_crypt_ctx(bio) ...)
submit_bio(bio)
...
__submit_bio
blk_mq_submit_bio(bio)
// blk_mq_run_dispatch_ops(q, blk_mq_try_issue_directly(hctx, rq))
__blk_mq_run_dispatch_ops(q, true blk_mq_try_issue_directly(hctx, rq))
blk_mq_try_issue_directly(hctx, rq)
__blk_mq_issue_directly(hctx, rq, true)
// q->mq_ops->queue_rq(hctx, &bd)


However, if dispatch must be deferred (e.g. BLK_STS_RESOURCE),
blk-mq queues &hctx->run_work. Now, if &hctx->run_work is queued on the
isolated CPU, it must be executed by the local kworker. Because that
kworker runs under SCHED_NORMAL, the user's SCHED_FIFO task completely
starves the kworker. As a result, blk_mq_run_work_fn() never executes,
hardware dispatch stalls, and the I/O pipeline hangs indefinitely.
Albeit, I believe an enabled Fair server, under the EEVDF scheduler, will
prevent this stall.

Anyhow, offloading deferred dispatch to an online housekeeping CPU allows
the kworker to run immediately and complete dispatch without priority
inversion. As noted in commit a46c27026da1:
"it isn't expected to let block IO disturb these CPUs. blk-mq kworker
shouldn't be scheduled on isolated CPUs."


Kind regards,
--
Aaron Tomlin