DRBD: replace strcpy with strscpy - #24
Conversation
|
Upstream branch: 8c2e52e |
ac8b12e to
9a69d4f
Compare
|
Upstream branch: bc9ff19 |
1f71e2e to
82d5aeb
Compare
9a69d4f to
e311dd9
Compare
|
Upstream branch: bc9ff19 |
82d5aeb to
876a1c6
Compare
e311dd9 to
b6b569e
Compare
|
Upstream branch: bc9ff19 |
876a1c6 to
9392c64
Compare
b6b569e to
ef2c9cd
Compare
|
Upstream branch: 40f92e7 |
9392c64 to
cf40108
Compare
ef2c9cd to
198825c
Compare
|
Upstream branch: 40f92e7 |
cf40108 to
f2f4131
Compare
198825c to
341e7ed
Compare
341e7ed to
81f31a4
Compare
|
Upstream branch: 89be9a8 |
f2f4131 to
968b7d7
Compare
81f31a4 to
87bbbbc
Compare
|
Upstream branch: 25fae0b |
968b7d7 to
02b0767
Compare
87bbbbc to
6637119
Compare
|
Upstream branch: 0905809 |
336bd7f to
314b7b2
Compare
Without the change `perf `hangs up on charaster devices. On my system
it's enough to run system-wide sampler for a few seconds to get the
hangup:
$ perf record -a -g --call-graph=dwarf
$ perf report
# hung
`strace` shows that hangup happens on reading on a character device
`/dev/dri/renderD128`
$ strace -y -f -p 2780484
strace: Process 2780484 attached
pread64(101</dev/dri/renderD128>, strace: Process 2780484 detached
It's call trace descends into `elfutils`:
$ gdb -p 2780484
(gdb) bt
#0 0x00007f5e508f04b7 in __libc_pread64 (fd=101, buf=0x7fff9df7edb0, count=0, offset=0)
at ../sysdeps/unix/sysv/linux/pread64.c:25
#1 0x00007f5e52b79515 in read_file () from /<<NIX>>/elfutils-0.192/lib/libelf.so.1
#2 0x00007f5e52b25666 in libdw_open_elf () from /<<NIX>>/elfutils-0.192/lib/libdw.so.1
#3 0x00007f5e52b25907 in __libdw_open_file () from /<<NIX>>/elfutils-0.192/lib/libdw.so.1
#4 0x00007f5e52b120a9 in dwfl_report_elf@@ELFUTILS_0.156 ()
from /<<NIX>>/elfutils-0.192/lib/libdw.so.1
#5 0x000000000068bf20 in __report_module (al=al@entry=0x7fff9df80010, ip=ip@entry=139803237033216, ui=ui@entry=0x5369b5e0)
at util/dso.h:537
#6 0x000000000068c3d1 in report_module (ip=139803237033216, ui=0x5369b5e0) at util/unwind-libdw.c:114
#7 frame_callback (state=0x535aef10, arg=0x5369b5e0) at util/unwind-libdw.c:242
#8 0x00007f5e52b261d3 in dwfl_thread_getframes () from /<<NIX>>/elfutils-0.192/lib/libdw.so.1
#9 0x00007f5e52b25bdb in get_one_thread_cb () from /<<NIX>>/elfutils-0.192/lib/libdw.so.1
#10 0x00007f5e52b25faa in dwfl_getthreads () from /<<NIX>>/elfutils-0.192/lib/libdw.so.1
#11 0x00007f5e52b26514 in dwfl_getthread_frames () from /<<NIX>>/elfutils-0.192/lib/libdw.so.1
#12 0x000000000068c6ce in unwind__get_entries (cb=cb@entry=0x5d4620 <unwind_entry>, arg=arg@entry=0x10cd5fa0,
thread=thread@entry=0x1076a290, data=data@entry=0x7fff9df80540, max_stack=max_stack@entry=127,
best_effort=best_effort@entry=false) at util/thread.h:152
#13 0x00000000005dae95 in thread__resolve_callchain_unwind (evsel=0x106006d0, thread=0x1076a290, cursor=0x10cd5fa0,
sample=0x7fff9df80540, max_stack=127, symbols=true) at util/machine.c:2939
#14 thread__resolve_callchain_unwind (thread=0x1076a290, cursor=0x10cd5fa0, evsel=0x106006d0, sample=0x7fff9df80540,
max_stack=127, symbols=true) at util/machine.c:2920
#15 __thread__resolve_callchain (thread=0x1076a290, cursor=0x10cd5fa0, evsel=0x106006d0, evsel@entry=0x7fff9df80440,
sample=0x7fff9df80540, parent=parent@entry=0x7fff9df804a0, root_al=root_al@entry=0x7fff9df80440, max_stack=127, symbols=true)
at util/machine.c:2970
#16 0x00000000005d0cb2 in thread__resolve_callchain (thread=<optimized out>, cursor=<optimized out>, evsel=0x7fff9df80440,
sample=<optimized out>, parent=0x7fff9df804a0, root_al=0x7fff9df80440, max_stack=127) at util/machine.h:198
#17 sample__resolve_callchain (sample=<optimized out>, cursor=<optimized out>, parent=parent@entry=0x7fff9df804a0,
evsel=evsel@entry=0x106006d0, al=al@entry=0x7fff9df80440, max_stack=max_stack@entry=127) at util/callchain.c:1127
#18 0x0000000000617e08 in hist_entry_iter__add (iter=iter@entry=0x7fff9df80480, al=al@entry=0x7fff9df80440, max_stack_depth=127,
arg=arg@entry=0x7fff9df81ae0) at util/hist.c:1255
#19 0x000000000045d2d0 in process_sample_event (tool=0x7fff9df81ae0, event=<optimized out>, sample=0x7fff9df80540,
evsel=0x106006d0, machine=<optimized out>) at builtin-report.c:334
#20 0x00000000005e3bb1 in perf_session__deliver_event (session=0x105ff2c0, event=0x7f5c7d735ca0, tool=0x7fff9df81ae0,
file_offset=2914716832, file_path=0x105ffbf0 "perf.data") at util/session.c:1367
#21 0x00000000005e8d93 in do_flush (oe=0x105ffa50, show_progress=false) at util/ordered-events.c:245
#22 __ordered_events__flush (oe=0x105ffa50, how=OE_FLUSH__ROUND, timestamp=<optimized out>) at util/ordered-events.c:324
#23 0x00000000005e1f64 in perf_session__process_user_event (session=0x105ff2c0, event=0x7f5c7d752b18, file_offset=2914835224,
file_path=0x105ffbf0 "perf.data") at util/session.c:1419
#24 0x00000000005e47c7 in reader__read_event (rd=rd@entry=0x7fff9df81260, session=session@entry=0x105ff2c0,
--Type <RET> for more, q to quit, c to continue without paging--
quit
prog=prog@entry=0x7fff9df81220) at util/session.c:2132
#25 0x00000000005e4b37 in reader__process_events (rd=0x7fff9df81260, session=0x105ff2c0, prog=0x7fff9df81220)
at util/session.c:2181
#26 __perf_session__process_events (session=0x105ff2c0) at util/session.c:2226
#27 perf_session__process_events (session=session@entry=0x105ff2c0) at util/session.c:2390
#28 0x0000000000460add in __cmd_report (rep=0x7fff9df81ae0) at builtin-report.c:1076
#29 cmd_report (argc=<optimized out>, argv=<optimized out>) at builtin-report.c:1827
#30 0x00000000004c5a40 in run_builtin (p=p@entry=0xd8f7f8 <commands+312>, argc=argc@entry=1, argv=argv@entry=0x7fff9df844b0)
at perf.c:351
#31 0x00000000004c5d63 in handle_internal_command (argc=argc@entry=1, argv=argv@entry=0x7fff9df844b0) at perf.c:404
#32 0x0000000000442de3 in run_argv (argcp=<synthetic pointer>, argv=<synthetic pointer>) at perf.c:448
#33 main (argc=<optimized out>, argv=0x7fff9df844b0) at perf.c:556
The hangup happens because nothing in` perf` or `elfutils` checks if a
mapped file is easily readable.
The change conservatively skips all non-regular files.
Signed-off-by: Sergei Trofimovich <slyich@gmail.com>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Link: https://lore.kernel.org/r/20250505174419.2814857-1-slyich@gmail.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
ed286d0 to
64b50fd
Compare
|
Upstream branch: a6923c0 |
314b7b2 to
c3b9839
Compare
64b50fd to
991e6ef
Compare
|
Upstream branch: eacf91b |
c3b9839 to
e815010
Compare
991e6ef to
65e573f
Compare
strcpy is deprecated due to lack of bounds checking. This patch replaces strcpy with strscpy, the recommended alternative for null terminated strings, to follow best practices. I had to do a small refactor for __drbd_send_protocol since it uses strlen anyways. so why not use that for strscpy. V2: - I forgot about null termination so i fixed it. Signed-off-by: Eslam Khafagy <eslam.medhat1993@gmail.com>
|
Upstream branch: 186f3ed |
e815010 to
1bca9ce
Compare
|
Upstream branch: 186f3ed |
1 similar comment
|
Upstream branch: 186f3ed |
|
Github failed to update this PR after force push. Close it. |
The xfstests' test-case generic/523 fails to execute correctly: FSTYP -- hfsplus PLATFORM -- Linux/x86_64 hfsplus-testing-0001 6.15.0-rc4+ #8 SMP PREEMPT_DYNAMIC Thu May 1 16:43:22 PDT 2025 MKFS_OPTIONS -- /dev/loop51 MOUNT_OPTIONS -- /dev/loop51 /mnt/scratch generic/523 - output mismatch (see xfstests-dev/results//generic/523.out.bad) The test-case expects to have '/' in the xattr name. However, HFS+ unicode logic makes conversion of '/' into ':'. In HFS+, a filename can contain '/' because ':' is the separator. The slash is a valid filename character on macOS. But on Linux, / is the path separator and it cannot appear in a filename component. But xattr name can contain any of these symbols. It means that this unicode logic conversion doesn't need to be executed for the case of xattr name. This patch adds distinguishing the regular and xattr names. If we have a regular name, then this conversion of special symbols will be executed. Otherwise, the conversion is skipped for the case of xattr names. sudo ./check -g auto FSTYP -- hfsplus PLATFORM -- Linux/x86_64 hfsplus-testing-0001 7.0.0-rc1+ #24 SMP PREEMPT_DYNAMIC Fri Mar 20 12:36:49 PDT 2026 MKFS_OPTIONS -- /dev/loop51 MOUNT_OPTIONS -- /dev/loop51 /mnt/scratch <skipped> generic/523 33s ... 25s <skipped> Closes: hfs-linux-kernel/hfs-linux-kernel#178 cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de> cc: Yangtao Li <frank.li@vivo.com> cc: linux-fsdevel@vger.kernel.org Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com> Link: https://lore.kernel.org/r/20260324003949.417048-2-slava@dubeyko.com Signed-off-by: Viacheslav Dubeyko <slava@dubeyko.com>
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 the backwards dependencies recorded here describe blocking that can not happen, and lockdep goes on to 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. 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). Annotating __bio_queue_enter() alone would not do anything there: that function is only reached once the tryget in bio_queue_enter() has already failed, and a REQ_NOWAIT bio then returns -EAGAIN from the retry, so practically every REQ_NOWAIT bio is annotated by the fast path in bio_queue_enter() instead. The value has to be normalized to 0 or 1. lock_acquire() stores it into a one bit wide bit field and REQ_NOWAIT is not bit 0, so passing bio->bi_opf & REQ_NOWAIT would truncate to 0 and silently disable the annotation. The fast path uses the expression inline instead of a local variable because rwsem_acquire_read() is compiled away without CONFIG_LOCKDEP and nothing else there would use the local. Hoisting the REQ_NOWAIT test out of the retry loop in __bio_queue_enter() also changes when bi_opf is read, it is now read once on entry instead of on every iteration. That is safe, nothing in the loop modifies bi_opf and the bio stays owned by the submitter across the wait, but unlike the rest of this patch it is a change in generated code and not only in lockdep annotation. One downside is worth spelling out. After this change nothing in the tree records an fs_reclaim -> q_usage_counter(queue) edge any more. blk_alloc_queue() only primes fs_reclaim -> q_usage_counter(io), and blk_freeze_acquire_lock() skips the io map while the disk is not live, which is exactly the situation in the scsi scan path above. So an allocation under a queue freeze is no longer detected for such queues. That detection is worth something, because such an allocation is a problem on its own. blk_mq_freeze_queue() wraps the freeze in a NOIO scope, i.e. allocations under a freeze are not supposed to be able to re-enter the queue, and scsi_realloc_sdev_budget_map() allocates the budget map inside the freeze anyway. It only gets away with it because the disk is not added yet, so nothing can reclaim onto that queue. No longer reporting it does not make it correct, it should be moved out of the freeze window regardless of this patch. Doing that would also silence this particular splat, but it would not fix the annotation. The bogus fs_reclaim -> q_usage_counter(queue) edge would still be recorded, and it would close a cycle again with any other lock that is taken under a queue freeze. Fixes: f1be178 ("block: model freeze & enter queue as lock for supporting lockdep") Claude:claude-opus-5 Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
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 the backwards dependencies recorded here describe blocking that can not happen, and lockdep goes on to 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. 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). Annotating __bio_queue_enter() alone would not do anything there: that function is only reached once the tryget in bio_queue_enter() has already failed, and a REQ_NOWAIT bio then returns -EAGAIN from the retry, so practically every REQ_NOWAIT bio is annotated by the fast path in bio_queue_enter() instead. The value has to be normalized to 0 or 1. lock_acquire() stores it into a one bit wide bit field and REQ_NOWAIT is not bit 0, so passing bio->bi_opf & REQ_NOWAIT would truncate to 0 and silently disable the annotation. The fast path uses the expression inline instead of a local variable because rwsem_acquire_read() is compiled away without CONFIG_LOCKDEP and nothing else there would use the local. Hoisting the REQ_NOWAIT test out of the retry loop in __bio_queue_enter() also changes when bi_opf is read, it is now read once on entry instead of on every iteration. That is safe, nothing in the loop modifies bi_opf and the bio stays owned by the submitter across the wait, but unlike the rest of this patch it is a change in generated code and not only in lockdep annotation. One downside is worth spelling out. After this change nothing in the tree records an fs_reclaim -> q_usage_counter(queue) edge any more. blk_alloc_queue() only primes fs_reclaim -> q_usage_counter(io), and blk_freeze_acquire_lock() skips the io map while the disk is not live, which is exactly the situation in the scsi scan path above. So an allocation under a queue freeze is no longer detected for such queues. That detection is worth something, because such an allocation is a problem on its own. blk_mq_freeze_queue() wraps the freeze in a NOIO scope, i.e. allocations under a freeze are not supposed to be able to re-enter the queue, and scsi_realloc_sdev_budget_map() allocates the budget map inside the freeze anyway. It only gets away with it because the disk is not added yet, so nothing can reclaim onto that queue. No longer reporting it does not make it correct, it should be moved out of the freeze window regardless of this patch. Doing that would also silence this particular splat, but it would not fix the annotation. The bogus fs_reclaim -> q_usage_counter(queue) edge would still be recorded, and it would close a cycle again with any other lock that is taken under a queue freeze. Fixes: f1be178 ("block: model freeze & enter queue as lock for supporting lockdep") Claude:claude-opus-5 Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
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 the backwards dependencies recorded here describe blocking that can not happen, and lockdep goes on to 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. 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). Annotating __bio_queue_enter() alone would not do anything there: that function is only reached once the tryget in bio_queue_enter() has already failed, and a REQ_NOWAIT bio then returns -EAGAIN from the retry, so practically every REQ_NOWAIT bio is annotated by the fast path in bio_queue_enter() instead. The value has to be normalized to 0 or 1. lock_acquire() stores it into a one bit wide bit field and REQ_NOWAIT is not bit 0, so passing bio->bi_opf & REQ_NOWAIT would truncate to 0 and silently disable the annotation. The fast path uses the expression inline instead of a local variable because rwsem_acquire_read() is compiled away without CONFIG_LOCKDEP and nothing else there would use the local. Hoisting the REQ_NOWAIT test out of the retry loop in __bio_queue_enter() also changes when bi_opf is read, it is now read once on entry instead of on every iteration. That is safe, nothing in the loop modifies bi_opf and the bio stays owned by the submitter across the wait, but unlike the rest of this patch it is a change in generated code and not only in lockdep annotation. One downside is worth spelling out. After this change nothing in the tree records an fs_reclaim -> q_usage_counter(queue) edge any more. blk_alloc_queue() only primes fs_reclaim -> q_usage_counter(io), and blk_freeze_acquire_lock() skips the io map while the disk is not live, which is exactly the situation in the scsi scan path above. So an allocation under a queue freeze is no longer detected for such queues. That detection is worth something, because such an allocation is a problem on its own. blk_mq_freeze_queue() wraps the freeze in a NOIO scope, i.e. allocations under a freeze are not supposed to be able to re-enter the queue, and scsi_realloc_sdev_budget_map() allocates the budget map inside the freeze anyway. It only gets away with it because the disk is not added yet, so nothing can reclaim onto that queue. No longer reporting it does not make it correct, it should be moved out of the freeze window regardless of this patch. Doing that would also silence this particular splat, but it would not fix the annotation. The bogus fs_reclaim -> q_usage_counter(queue) edge would still be recorded, and it would close a cycle again with any other lock that is taken under a queue freeze. Fixes: f1be178 ("block: model freeze & enter queue as lock for supporting lockdep") Claude:claude-opus-5 Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
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 the backwards dependencies recorded here describe blocking that can not happen, and lockdep goes on to 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. 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). Annotating __bio_queue_enter() alone would not do anything there: that function is only reached once the tryget in bio_queue_enter() has already failed, and a REQ_NOWAIT bio then returns -EAGAIN from the retry, so practically every REQ_NOWAIT bio is annotated by the fast path in bio_queue_enter() instead. The value has to be normalized to 0 or 1. lock_acquire() stores it into a one bit wide bit field and REQ_NOWAIT is not bit 0, so passing bio->bi_opf & REQ_NOWAIT would truncate to 0 and silently disable the annotation. The fast path uses the expression inline instead of a local variable because rwsem_acquire_read() is compiled away without CONFIG_LOCKDEP and nothing else there would use the local. Hoisting the REQ_NOWAIT test out of the retry loop in __bio_queue_enter() also changes when bi_opf is read, it is now read once on entry instead of on every iteration. That is safe, nothing in the loop modifies bi_opf and the bio stays owned by the submitter across the wait, but unlike the rest of this patch it is a change in generated code and not only in lockdep annotation. One downside is worth spelling out. After this change nothing in the tree records an fs_reclaim -> q_usage_counter(queue) edge any more. blk_alloc_queue() only primes fs_reclaim -> q_usage_counter(io), and blk_freeze_acquire_lock() skips the io map while the disk is not live, which is exactly the situation in the scsi scan path above. So an allocation under a queue freeze is no longer detected for such queues. That detection is worth something, because such an allocation is a problem on its own. blk_mq_freeze_queue() wraps the freeze in a NOIO scope, i.e. allocations under a freeze are not supposed to be able to re-enter the queue, and scsi_realloc_sdev_budget_map() allocates the budget map inside the freeze anyway. It only gets away with it because the disk is not added yet, so nothing can reclaim onto that queue. No longer reporting it does not make it correct, it should be moved out of the freeze window regardless of this patch. Doing that would also silence this particular splat, but it would not fix the annotation. The bogus fs_reclaim -> q_usage_counter(queue) edge would still be recorded, and it would close a cycle again with any other lock that is taken under a queue freeze. Fixes: f1be178 ("block: model freeze & enter queue as lock for supporting lockdep") Claude:claude-opus-5 Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
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 the backwards dependencies recorded here describe blocking that can not happen, and lockdep goes on to 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. 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). Annotating __bio_queue_enter() alone would not do anything there: that function is only reached once the tryget in bio_queue_enter() has already failed, and a REQ_NOWAIT bio then returns -EAGAIN from the retry, so practically every REQ_NOWAIT bio is annotated by the fast path in bio_queue_enter() instead. The value has to be normalized to 0 or 1. lock_acquire() stores it into a one bit wide bit field and REQ_NOWAIT is not bit 0, so passing bio->bi_opf & REQ_NOWAIT would truncate to 0 and silently disable the annotation. The fast path uses the expression inline instead of a local variable because rwsem_acquire_read() is compiled away without CONFIG_LOCKDEP and nothing else there would use the local. Hoisting the REQ_NOWAIT test out of the retry loop in __bio_queue_enter() also changes when bi_opf is read, it is now read once on entry instead of on every iteration. That is safe, nothing in the loop modifies bi_opf and the bio stays owned by the submitter across the wait, but unlike the rest of this patch it is a change in generated code and not only in lockdep annotation. One downside is worth spelling out. After this change nothing in the tree records an fs_reclaim -> q_usage_counter(queue) edge any more. blk_alloc_queue() only primes fs_reclaim -> q_usage_counter(io), and blk_freeze_acquire_lock() skips the io map while the disk is not live, which is exactly the situation in the scsi scan path above. So an allocation under a queue freeze is no longer detected for such queues. That detection is worth something, because such an allocation is a problem on its own. blk_mq_freeze_queue() wraps the freeze in a NOIO scope, i.e. allocations under a freeze are not supposed to be able to re-enter the queue, and scsi_realloc_sdev_budget_map() allocates the budget map inside the freeze anyway. It only gets away with it because the disk is not added yet, so nothing can reclaim onto that queue. No longer reporting it does not make it correct, it should be moved out of the freeze window regardless of this patch. Doing that would also silence this particular splat, but it would not fix the annotation. The bogus fs_reclaim -> q_usage_counter(queue) edge would still be recorded, and it would close a cycle again with any other lock that is taken under a queue freeze. Fixes: f1be178 ("block: model freeze & enter queue as lock for supporting lockdep") Claude:claude-opus-5 Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
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 the backwards dependencies recorded here describe blocking that can not happen, and lockdep goes on to 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. 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). Annotating __bio_queue_enter() alone would not do anything there: that function is only reached once the tryget in bio_queue_enter() has already failed, and a REQ_NOWAIT bio then returns -EAGAIN from the retry, so practically every REQ_NOWAIT bio is annotated by the fast path in bio_queue_enter() instead. The value has to be normalized to 0 or 1. lock_acquire() stores it into a one bit wide bit field and REQ_NOWAIT is not bit 0, so passing bio->bi_opf & REQ_NOWAIT would truncate to 0 and silently disable the annotation. The fast path uses the expression inline instead of a local variable because rwsem_acquire_read() is compiled away without CONFIG_LOCKDEP and nothing else there would use the local. Hoisting the REQ_NOWAIT test out of the retry loop in __bio_queue_enter() also changes when bi_opf is read, it is now read once on entry instead of on every iteration. That is safe, nothing in the loop modifies bi_opf and the bio stays owned by the submitter across the wait, but unlike the rest of this patch it is a change in generated code and not only in lockdep annotation. One downside is worth spelling out. After this change nothing in the tree records an fs_reclaim -> q_usage_counter(queue) edge any more. blk_alloc_queue() only primes fs_reclaim -> q_usage_counter(io), and blk_freeze_acquire_lock() skips the io map while the disk is not live, which is exactly the situation in the scsi scan path above. So an allocation under a queue freeze is no longer detected for such queues. That detection is worth something, because such an allocation is a problem on its own. blk_mq_freeze_queue() wraps the freeze in a NOIO scope, i.e. allocations under a freeze are not supposed to be able to re-enter the queue, and scsi_realloc_sdev_budget_map() allocates the budget map inside the freeze anyway. It only gets away with it because the disk is not added yet, so nothing can reclaim onto that queue. No longer reporting it does not make it correct, it should be moved out of the freeze window regardless of this patch. Doing that would also silence this particular splat, but it would not fix the annotation. The bogus fs_reclaim -> q_usage_counter(queue) edge would still be recorded, and it would close a cycle again with any other lock that is taken under a queue freeze. Fixes: f1be178 ("block: model freeze & enter queue as lock for supporting lockdep") Claude:claude-opus-5 Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
All block driver code except loop_set_dio() calls queue_limits_start_update() before it freezes the request queue. Make loop_set_dio() follow this convention. This patch fixes the following lockdep complaint: ====================================================== WARNING: possible circular locking dependency detected 7.2.0-rc5-dbg #11 Not tainted ------------------------------------------------------ losetup/2924 is trying to acquire lock: ffff88816c76da68 (&q->limits_lock){+.+.}-{4:4}, at: loop_set_dio+0x318/0x720 [loop] but task is already holding lock: ffff88816c76d430 (&q->q_usage_counter(io)#24){++++}-{0:0}, at: blk_mq_freeze_queue_nomemsave+0x1a/0x30 which lock already depends on the new lock. Cc: Keith Busch <kbusch@kernel.org> Fixes: 6c8dec2 ("loop: set dma_alignment from the backing file for direct I/O") Reported-by: syzbot+cc0de396bac84da51919@syzkaller.appspotmail.com Closes: https://lore.kernel.org/linux-block/6a7d5368.d5f0ebe7.22d851.0013.GAE@google.com/ Signed-off-by: Bart Van Assche <bvanassche@acm.org> Reviewed-by: Keith Busch <kbusch@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
All block driver code except loop_set_dio() calls queue_limits_start_update() before it freezes the request queue. Make loop_set_dio() follow this convention. This patch fixes the following lockdep complaint: ====================================================== WARNING: possible circular locking dependency detected 7.2.0-rc5-dbg #11 Not tainted ------------------------------------------------------ losetup/2924 is trying to acquire lock: ffff88816c76da68 (&q->limits_lock){+.+.}-{4:4}, at: loop_set_dio+0x318/0x720 [loop] but task is already holding lock: ffff88816c76d430 (&q->q_usage_counter(io)#24){++++}-{0:0}, at: blk_mq_freeze_queue_nomemsave+0x1a/0x30 which lock already depends on the new lock. Cc: Keith Busch <kbusch@kernel.org> Fixes: 6c8dec2 ("loop: set dma_alignment from the backing file for direct I/O") Reported-by: syzbot+cc0de396bac84da51919@syzkaller.appspotmail.com Closes: https://lore.kernel.org/linux-block/6a7d5368.d5f0ebe7.22d851.0013.GAE@google.com/ Signed-off-by: Bart Van Assche <bvanassche@acm.org> Reviewed-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Keith Busch <kbusch@kernel.org> Link: https://patch.msgid.link/d919f5285d16afbec6c51ecdf201692a484566e5.1786637565.git.bvanassche@acm.org Signed-off-by: Jens Axboe <axboe@kernel.dk>
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 the backwards dependencies recorded here describe blocking that can not happen, and lockdep goes on to 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. 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). Annotating __bio_queue_enter() alone would not do anything there: that function is only reached once the tryget in bio_queue_enter() has already failed, and a REQ_NOWAIT bio then returns -EAGAIN from the retry, so practically every REQ_NOWAIT bio is annotated by the fast path in bio_queue_enter() instead. The value has to be normalized to 0 or 1. lock_acquire() stores it into a one bit wide bit field and REQ_NOWAIT is not bit 0, so passing bio->bi_opf & REQ_NOWAIT would truncate to 0 and silently disable the annotation. The fast path uses the expression inline instead of a local variable because rwsem_acquire_read() is compiled away without CONFIG_LOCKDEP and nothing else there would use the local. Hoisting the REQ_NOWAIT test out of the retry loop in __bio_queue_enter() also changes when bi_opf is read, it is now read once on entry instead of on every iteration. That is safe, nothing in the loop modifies bi_opf and the bio stays owned by the submitter across the wait, but unlike the rest of this patch it is a change in generated code and not only in lockdep annotation. One downside is worth spelling out. After this change nothing in the tree records an fs_reclaim -> q_usage_counter(queue) edge any more. blk_alloc_queue() only primes fs_reclaim -> q_usage_counter(io), and blk_freeze_acquire_lock() skips the io map while the disk is not live, which is exactly the situation in the scsi scan path above. So an allocation under a queue freeze is no longer detected for such queues. That detection is worth something, because such an allocation is a problem on its own. blk_mq_freeze_queue() wraps the freeze in a NOIO scope, i.e. allocations under a freeze are not supposed to be able to re-enter the queue, and scsi_realloc_sdev_budget_map() allocates the budget map inside the freeze anyway. It only gets away with it because the disk is not added yet, so nothing can reclaim onto that queue. No longer reporting it does not make it correct, it should be moved out of the freeze window regardless of this patch. Doing that would also silence this particular splat, but it would not fix the annotation. The bogus fs_reclaim -> q_usage_counter(queue) edge would still be recorded, and it would close a cycle again with any other lock that is taken under a queue freeze. Fixes: f1be178 ("block: model freeze & enter queue as lock for supporting lockdep") Claude:claude-opus-5 Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
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 the backwards dependencies recorded here describe blocking that can not happen, and lockdep goes on to 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. 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). Annotating __bio_queue_enter() alone would not do anything there: that function is only reached once the tryget in bio_queue_enter() has already failed, and a REQ_NOWAIT bio then returns -EAGAIN from the retry, so practically every REQ_NOWAIT bio is annotated by the fast path in bio_queue_enter() instead. The value has to be normalized to 0 or 1. lock_acquire() stores it into a one bit wide bit field and REQ_NOWAIT is not bit 0, so passing bio->bi_opf & REQ_NOWAIT would truncate to 0 and silently disable the annotation. The fast path uses the expression inline instead of a local variable because rwsem_acquire_read() is compiled away without CONFIG_LOCKDEP and nothing else there would use the local. Hoisting the REQ_NOWAIT test out of the retry loop in __bio_queue_enter() also changes when bi_opf is read, it is now read once on entry instead of on every iteration. That is safe, nothing in the loop modifies bi_opf and the bio stays owned by the submitter across the wait, but unlike the rest of this patch it is a change in generated code and not only in lockdep annotation. One downside is worth spelling out. After this change nothing in the tree records an fs_reclaim -> q_usage_counter(queue) edge any more. blk_alloc_queue() only primes fs_reclaim -> q_usage_counter(io), and blk_freeze_acquire_lock() skips the io map while the disk is not live, which is exactly the situation in the scsi scan path above. So an allocation under a queue freeze is no longer detected for such queues. That detection is worth something, because such an allocation is a problem on its own. blk_mq_freeze_queue() wraps the freeze in a NOIO scope, i.e. allocations under a freeze are not supposed to be able to re-enter the queue, and scsi_realloc_sdev_budget_map() allocates the budget map inside the freeze anyway. It only gets away with it because the disk is not added yet, so nothing can reclaim onto that queue. No longer reporting it does not make it correct, it should be moved out of the freeze window regardless of this patch. Doing that would also silence this particular splat, but it would not fix the annotation. The bogus fs_reclaim -> q_usage_counter(queue) edge would still be recorded, and it would close a cycle again with any other lock that is taken under a queue freeze. Fixes: f1be178 ("block: model freeze & enter queue as lock for supporting lockdep") Claude:claude-opus-5 Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
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 the backwards dependencies recorded here describe blocking that can not happen, and lockdep goes on to 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. 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). Annotating __bio_queue_enter() alone would not do anything there: that function is only reached once the tryget in bio_queue_enter() has already failed, and a REQ_NOWAIT bio then returns -EAGAIN from the retry, so practically every REQ_NOWAIT bio is annotated by the fast path in bio_queue_enter() instead. The value has to be normalized to 0 or 1. lock_acquire() stores it into a one bit wide bit field and REQ_NOWAIT is not bit 0, so passing bio->bi_opf & REQ_NOWAIT would truncate to 0 and silently disable the annotation. The fast path uses the expression inline instead of a local variable because rwsem_acquire_read() is compiled away without CONFIG_LOCKDEP and nothing else there would use the local. Hoisting the REQ_NOWAIT test out of the retry loop in __bio_queue_enter() also changes when bi_opf is read, it is now read once on entry instead of on every iteration. That is safe, nothing in the loop modifies bi_opf and the bio stays owned by the submitter across the wait, but unlike the rest of this patch it is a change in generated code and not only in lockdep annotation. One downside is worth spelling out. After this change nothing in the tree records an fs_reclaim -> q_usage_counter(queue) edge any more. blk_alloc_queue() only primes fs_reclaim -> q_usage_counter(io), and blk_freeze_acquire_lock() skips the io map while the disk is not live, which is exactly the situation in the scsi scan path above. So an allocation under a queue freeze is no longer detected for such queues. That detection is worth something, because such an allocation is a problem on its own. blk_mq_freeze_queue() wraps the freeze in a NOIO scope, i.e. allocations under a freeze are not supposed to be able to re-enter the queue, and scsi_realloc_sdev_budget_map() allocates the budget map inside the freeze anyway. It only gets away with it because the disk is not added yet, so nothing can reclaim onto that queue. No longer reporting it does not make it correct, it should be moved out of the freeze window regardless of this patch. Doing that would also silence this particular splat, but it would not fix the annotation. The bogus fs_reclaim -> q_usage_counter(queue) edge would still be recorded, and it would close a cycle again with any other lock that is taken under a queue freeze. Fixes: f1be178 ("block: model freeze & enter queue as lock for supporting lockdep") Claude:claude-opus-5 Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com>
Pull request for series with
subject: DRBD: replace strcpy with strscpy
version: 2
url: https://patchwork.kernel.org/project/linux-block/list/?series=979363