From: Christian Borntraeger <borntraeger@linux.ibm.com>
To: Jens Axboe <axboe@kernel.dk>
Cc: Ming Lei <ming.lei@redhat.com>,
linux-block@vger.kernel.org, linux-kernel@vger.kernel.org,
Christian Borntraeger <borntraeger@linux.ibm.com>
Subject: [PATCH] block: model nowait as trylock for non-blocking queue enter
Date: Thu, 30 Jul 2026 20:30:05 +0200 [thread overview]
Message-ID: <20260730183005.444273-1-borntraeger@linux.ibm.com> (raw)
blk_queue_enter() and __bio_queue_enter() annotate a successful queue
enter as a read acquisition of q->q_lockdep_map resp. q->io_lockdep_map,
so that lockdep can catch deadlocks between blk_mq_freeze_queue() and
tasks waiting to enter a frozen queue.
The annotation always passes trylock == 0, even when the caller asked
for BLK_MQ_REQ_NOWAIT / REQ_NOWAIT. Such a caller never waits for the
queue to be unfrozen, it bails out with -EAGAIN, so it can not take part
in a deadlock cycle. Recording a full backwards dependency for it makes
lockdep report cycles that can not happen.
Here is a real life example on s390x:
WARNING: possible circular locking dependency detected
7.2.0-20260729.rc5.git5.98379cf6109c.300.fc44.s390x+debug #1 Not tainted
kswapd0/235 is trying to acquire lock:
0000014323281478 (&q->q_usage_counter(queue)#24){++++}-{0:0}, at: blk_mq_alloc_request+0x1b4/0x200
but task is already holding lock:
00000249ca69e528 (fs_reclaim){+.+.}-{0:0}, at: balance_pgdat+0x552/0xc60
which lock already depends on the new lock.
the existing dependency chain (in reverse order) is:
-> #2 (fs_reclaim){+.+.}-{0:0}:
lock_acquire+0x150/0x3f0
__fs_reclaim_acquire+0x44/0x50
fs_reclaim_acquire+0xbe/0x100
__kmalloc_noprof+0xda/0x7d0
pcpu_alloc_chunk+0x30/0x170
pcpu_create_chunk+0x22/0x120
pcpu_alloc_noprof+0xcf2/0xd70
do_kmem_cache_create+0xce/0x500
__kmem_cache_create_args+0x33c/0x480
kmem_cache_create_usercopy.constprop.0+0x44/0x68
skb_init+0x7c/0x90
sock_init+0x3c/0x108
do_one_initcall+0x82/0x3b0
do_initcalls+0x14c/0x170
kernel_init_freeable+0x226/0x2a0
kernel_init+0x2e/0x188
__ret_from_fork+0x4c/0x340
ret_from_fork+0xa/0x30
-> #1 (pcpu_alloc_mutex){+.+.}-{3:3}:
lock_acquire+0x150/0x3f0
__mutex_lock+0xba/0xdc0
_mutex_lock_killable+0x36/0x50
pcpu_alloc_noprof+0x882/0xd70
sbitmap_init_node+0x11c/0x240
scsi_realloc_sdev_budget_map+0xee/0x1e0
scsi_add_lun+0x538/0x730
scsi_probe_and_add_lun+0x1a8/0x4d0
scsi_report_lun_scan+0x352/0x3f0
__scsi_scan_target+0x104/0x250
scsi_scan_target+0xe4/0x100
fc_scsi_scan_rport+0xc2/0xd0 [scsi_transport_fc]
process_one_work+0x2ba/0x800
worker_thread+0x21a/0x400
kthread+0x164/0x190
__ret_from_fork+0x4c/0x340
ret_from_fork+0xa/0x30
-> #0 (&q->q_usage_counter(queue)#24){++++}-{0:0}:
check_prev_add+0x160/0xf40
__lock_acquire+0x12aa/0x15a0
lock_acquire+0x150/0x3f0
blk_queue_enter+0xee/0x2d0
blk_mq_alloc_request+0x1b4/0x200
multipath_clone_and_map+0xb6/0x260 [dm_multipath]
map_request+0x62/0x230
dm_mq_queue_rq+0xc4/0x260
blk_mq_dispatch_rq_list+0x19c/0x540
__blk_mq_do_dispatch_sched+0x3ce/0x3e0
__blk_mq_sched_dispatch_requests+0x198/0x1e0
blk_mq_sched_dispatch_requests+0x3c/0x90
blk_mq_run_hw_queue+0x1a4/0x2c0
blk_mq_dispatch_list+0x222/0x4a0
blk_mq_flush_plug_list+0x7c/0x1d0
__blk_flush_plug+0x128/0x180
blk_finish_plug+0x42/0x60
shrink_lruvec+0x252/0x410
shrink_node_memcgs+0x202/0x290
shrink_node+0xa0/0x410
balance_pgdat+0x4cc/0xc60
kswapd+0x148/0x2c0
kthread+0x164/0x190
__ret_from_fork+0x4c/0x340
ret_from_fork+0xa/0x30
other info that might help us debug this:
Chain exists of:
&q->q_usage_counter(queue)#24 --> pcpu_alloc_mutex --> fs_reclaim
Possible unsafe locking scenario:
CPU0 CPU1
---- ----
lock(fs_reclaim);
lock(pcpu_alloc_mutex);
lock(fs_reclaim);
rlock(&q->q_usage_counter(queue)#24);
*** DEADLOCK ***
2 locks held by kswapd0/235:
#0: 00000249ca69e528 (fs_reclaim){+.+.}-{0:0}, at: balance_pgdat+0x552/0xc60
#1: 00000249ca64a568 (rcu_read_lock){....}-{1:2}, at: blk_mq_run_hw_queue+0x160/0x2c0
stack backtrace:
CPU: 10 UID: 0 PID: 235 Comm: kswapd0 Not tainted 7.2.0-20260729.rc5.git5.98379cf6109c.300.fc44.s390x+debug #1 PREEMPT
Hardware name: IBM 3906 M04 701 (LPAR)
Call Trace:
[<00000249c86a0e3e>] dump_stack_lvl+0xae/0x108
[<00000249c87aaf24>] print_circular_bug+0x1a4/0x230
[<00000249c87ab13c>] check_noncircular+0x18c/0x1b0
[<00000249c87ac510>] check_prev_add+0x160/0xf40
[<00000249c87b008a>] __lock_acquire+0x12aa/0x15a0
[<00000249c87b04d0>] lock_acquire+0x150/0x3f0
[<00000249c90fdc3e>] blk_queue_enter+0xee/0x2d0
[<00000249c910de84>] blk_mq_alloc_request+0x1b4/0x200
[<0000024948754336>] multipath_clone_and_map+0xb6/0x260 [dm_multipath]
[<00000249c943c3a2>] map_request+0x62/0x230
[<00000249c943c634>] dm_mq_queue_rq+0xc4/0x260
[<00000249c911527c>] blk_mq_dispatch_rq_list+0x19c/0x540
[<00000249c911dc5e>] __blk_mq_do_dispatch_sched+0x3ce/0x3e0
[<00000249c911e1e8>] __blk_mq_sched_dispatch_requests+0x198/0x1e0
[<00000249c911e28c>] blk_mq_sched_dispatch_requests+0x3c/0x90
[<00000249c910fe34>] blk_mq_run_hw_queue+0x1a4/0x2c0
[<00000249c91148e2>] blk_mq_dispatch_list+0x222/0x4a0
[<00000249c911569c>] blk_mq_flush_plug_list+0x7c/0x1d0
[<00000249c90ff948>] __blk_flush_plug+0x128/0x180
[<00000249c90ffc42>] blk_finish_plug+0x42/0x60
[<00000249c8a48522>] shrink_lruvec+0x252/0x410
[<00000249c8a488e2>] shrink_node_memcgs+0x202/0x290
[<00000249c8a48d70>] shrink_node+0xa0/0x410
[<00000249c8a4a94c>] balance_pgdat+0x4cc/0xc60
[<00000249c8a4b228>] kswapd+0x148/0x2c0
[<00000249c8737f64>] kthread+0x164/0x190
[<00000249c86a47bc>] __ret_from_fork+0x4c/0x340
[<00000249c9837aca>] ret_from_fork+0xa/0x30
INFO: lockdep is turned off.
Chain exists of:
&q->q_usage_counter(queue)#24 --> pcpu_alloc_mutex --> fs_reclaim
Here the fs_reclaim -> q_usage_counter(queue) edge comes from
kswapd -> blk_finish_plug -> ... -> dm_mq_queue_rq
-> multipath_clone_and_map
-> blk_mq_alloc_request(BLK_MQ_REQ_NOWAIT)
and the q_usage_counter(queue) -> pcpu_alloc_mutex edge from
scsi_realloc_sdev_budget_map(), which allocates the budget map percpu
alloc hint while the queue is frozen.
kswapd can not deadlock there, it just gets -EAGAIN and requeues,
but the splat still turns lockdep off for the rest of the boot.
Tell lockdep that a NOWAIT enter is a trylock.
While the lockdep splat only is about q_usage_counter(queue), do
the same fix for q_usage_counter(io).
Fixes: f1be1788a32e ("block: model freeze & enter queue as lock for supporting lockdep")
Assisted-by: claude
Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
---
block/blk-core.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/block/blk-core.c b/block/blk-core.c
index 365641266c9e..e5db54596e3d 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -347,9 +347,10 @@ bool blk_queue_start_drain(struct request_queue *q)
int blk_queue_enter(struct request_queue *q, blk_mq_req_flags_t flags)
{
const bool pm = flags & BLK_MQ_REQ_PM;
+ const bool nowait = flags & BLK_MQ_REQ_NOWAIT;
while (!blk_try_enter_queue(q, pm)) {
- if (flags & BLK_MQ_REQ_NOWAIT)
+ if (nowait)
return -EAGAIN;
/*
@@ -368,17 +369,19 @@ int blk_queue_enter(struct request_queue *q, blk_mq_req_flags_t flags)
return -ENODEV;
}
- rwsem_acquire_read(&q->q_lockdep_map, 0, 0, _RET_IP_);
+ rwsem_acquire_read(&q->q_lockdep_map, 0, nowait, _RET_IP_);
rwsem_release(&q->q_lockdep_map, _RET_IP_);
return 0;
}
int __bio_queue_enter(struct request_queue *q, struct bio *bio)
{
+ const bool nowait = bio->bi_opf & REQ_NOWAIT;
+
while (!blk_try_enter_queue(q, false)) {
struct gendisk *disk = bio->bi_bdev->bd_disk;
- if (bio->bi_opf & REQ_NOWAIT) {
+ if (nowait) {
if (test_bit(GD_DEAD, &disk->state))
goto dead;
bio_wouldblock_error(bio);
@@ -401,7 +404,7 @@ int __bio_queue_enter(struct request_queue *q, struct bio *bio)
goto dead;
}
- rwsem_acquire_read(&q->io_lockdep_map, 0, 0, _RET_IP_);
+ rwsem_acquire_read(&q->io_lockdep_map, 0, nowait, _RET_IP_);
rwsem_release(&q->io_lockdep_map, _RET_IP_);
return 0;
dead:
--
2.53.0
next reply other threads:[~2026-07-30 18:30 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-30 18:30 Christian Borntraeger [this message]
2026-07-30 19:37 ` [PATCH] block: model nowait as trylock for non-blocking queue enter Christian Borntraeger
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260730183005.444273-1-borntraeger@linux.ibm.com \
--to=borntraeger@linux.ibm.com \
--cc=axboe@kernel.dk \
--cc=linux-block@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=ming.lei@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox