From: Jack Wang <jinpu.wang@ionos.com>
To: Song Liu <song@kernel.org>, Yu Kuai <yukuai@fygo.io>,
linux-raid@vger.kernel.org, Nilay Shroff <nilay@linux.ibm.com>,
abd.masalkhi@gmail.com
Cc: linux-block@vger.kernel.org, Jens Axboe <axboe@kernel.dk>,
Christoph Hellwig <hch@lst.de>,
Damien Le Moal <dlemoal@kernel.org>,
Ming Lei <tom.leiming@gmail.com>, Xiao Ni <xiao@kernel.org>,
Li Nan <magiclinan@didiglobal.com>,
Mike Snitzer <snitzer@kernel.org>,
Mikulas Patocka <mpatocka@redhat.com>,
dm-devel@lists.linux.dev, linux-kernel@vger.kernel.org,
Jack Wang <jinpu.wang@cloud.ionos.com>
Subject: [PATCH v2 0/8] md: don't wait for q->limits_lock while md holds back I/O
Date: Thu, 10 Sep 2026 10:11:05 +0200 [thread overview]
Message-ID: <20260910081114.1605746-1-jinpu.wang@ionos.com> (raw)
From: Jack Wang <jinpu.wang@cloud.ionos.com>
Writing to a queue limits attribute of an md array while a spare is
being re-added deadlocks the array. I reported this earlier here:
https://lore.kernel.org/linux-raid/CAMGffE=heGA3y8FjQ0Sm1jj-kd-=H9Y54WozKASSEZhc9UNKjA@mail.gmail.com/
Four tasks, one array:
udev-worker queue_attr_store() holds q->limits_lock, waits in
blk_mq_freeze_queue() for q_usage_counter to drain
fio holds a q_usage_counter reference, parked in
md_handle_request()'s is_suspended() loop
mdadm suspended the array, waits for reconfig_mutex
md_start_sync holds reconfig_mutex, waits for q->limits_lock
The last leg is mddev_stack_new_rdev() from ->hot_add_disk(). Since
commit c99f66e4084a ("block: fix queue freeze vs limits lock order in
sysfs store methods") the sysfs store holds q->limits_lock across the
freeze, so md must not block on that lock while it is holding back the
I/O the freeze waits for. That is the same hazard mddev_suspend()
already documents for reconfig_mutex.
v1 kept q->limits_lock outermost where it could and used a trylock
everywhere else. Christoph asked for the lock ordering to be fixed
instead, so v2 drops the trylock and the block patch that added it.
The rule v2 applies is that q->limits_lock nests outside reconfig_mutex
and the suspend everywhere, with no exceptions to paper over. The two
callers that cannot own an update do not need one: check_sb_changes()
and md_check_recovery() only ever re-add a device that is already a
member, so its limits are stacked and the update is a refresh, and they
now take the no-stack path unconditionally. mddev_update_io_opt() is
not an add and does need the update, so patch 4 defers it to a work
item that takes the lock with nothing held.
Making the hoists unconditional exposed a second cycle that the trylock
had been hiding, which Nilay hit with lockdep on v1:
q->limits_lock -> reconfig_mutex added by this series
reconfig_mutex -> disk->open_mutex pre-existing, md opens legs
under reconfig_mutex
disk->open_mutex -> q->limits_lock pre-existing, sd_open() ->
sd_revalidate_disk()
Only the middle edge can go, so legs are now opened before any md lock
is taken. Patch 7 does the opens and patch 8 the holder links, which
take disk->open_mutex too and were easy to miss because the release
side only takes blk_holder_mutex. Opening without reconfig_mutex means
the superblock format fields have to be snapshotted and rechecked once
the array is locked.
Patches 1, 3 and 6 are plumbing with no functional change.
Two callers still take q->limits_lock inside reconfig_mutex, both with
the array suspended, and patch 5 says why: ->start_reshape() from
action_store(), which suspends before flushing sync_work, and
raid*_run() -> queue_limits_set() from level_store(), which already
hangs on its own because it freezes the queue while suspended. Both
need more restructuring than belongs here.
The patches are based on v7.3-rc2.
v1: https://lore.kernel.org/linux-raid/20260909063029.GC29874@lst.de/T/#t
Changes since v1:
- Drop the trylock and the block patch adding
queue_limits_start_update_trylock(), per Christoph.
- check_sb_changes() and md_check_recovery() take the no-stack path
unconditionally instead of trying the lock first.
- Defer the io_opt update to a work item rather than skipping it on a
contended pass (patch 4, was "md: don't wait for q->limits_lock in
mddev_update_io_opt()").
- New patches 6-8 to close the reconfig_mutex -> disk->open_mutex
edge that lockdep reported on v1: pass a queue_limits through
->run(), open new legs before locking the array, and link their
holders there too.
- Rebased on v7.3-rc2.
Testing, on v7.3-rc2 with lockdep (PROVE_LOCKING, DEBUG_LOCK_ALLOC):
- the reproducer below, 20 fail/remove/add cycles with fio in flight
and a loop writing queue/max_sectors_kb, where the same test wedges
the array before the series
- array start at raid0, raid1, raid5, raid10 and linear, spare add,
fail and remove, the rdev sysfs stores, ADD_NEW_DISK, array_state
transitions, a level change and a raid5 3 -> 4 reshape to
completion, which is what exercises patch 4's work item: io_opt
goes from 1048576 to 1572864 once end_reshape() runs
- HOT_ADD_DISK of an undersized leg, so bind_rdev_to_array() fails
and patch 8's release path runs
No lockdep reports, and debug_locks stayed 1 throughout. Every patch
builds on its own.
A blktests case covering this deadlock will be sent separately.
The reproducer, for anyone who wants it:
mdadm -C /dev/md111 --force -e 1.2 --assume-clean -l 1 \
--bitmap=internal -n 2 /dev/ram0 /dev/ram1
fio --direct=1 --rw=randrw --ioengine=libaio --iodepth=32 --numjobs=4 \
--time_based=1 --runtime=180 --filename=/dev/md111 --name=repro &
while :; do
echo 128 > /sys/block/md111/queue/max_sectors_kb 2>/dev/null
done &
for i in $(seq 20); do
mdadm /dev/md111 --fail /dev/ram0
mdadm /dev/md111 --remove /dev/ram0
mdadm /dev/md111 --add /dev/ram0
mdadm --wait /dev/md111
done
Jack Wang (8):
md: pass a queue_limits down to ->hot_add_disk()
md: don't wait for q->limits_lock in check_sb_changes()
md: pass a queue_limits through the rdev sysfs stores
md: defer the io_opt update out of the sync thread
md: take q->limits_lock before locking and suspending the array
md: pass a queue_limits through ->run()
md: open new legs before locking the array
md: link a new leg's holder before locking the array
drivers/md/dm-raid.c | 4 +-
drivers/md/md-autodetect.c | 38 ++-
drivers/md/md-linear.c | 30 +-
drivers/md/md.c | 661 +++++++++++++++++++++++++++++--------
drivers/md/md.h | 56 +++-
drivers/md/raid0.c | 16 +-
drivers/md/raid1.c | 26 +-
drivers/md/raid10.c | 37 ++-
drivers/md/raid5.c | 54 ++-
9 files changed, 741 insertions(+), 181 deletions(-)
--
2.43.0
next reply other threads:[~2026-09-10 8:11 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-10 8:11 Jack Wang [this message]
2026-09-10 8:11 ` [PATCH v2 1/8] md: pass a queue_limits down to ->hot_add_disk() Jack Wang
2026-09-11 10:46 ` Nilay Shroff
2026-09-10 8:11 ` [PATCH v2 2/8] md: don't wait for q->limits_lock in check_sb_changes() Jack Wang
2026-09-10 8:11 ` [PATCH v2 3/8] md: pass a queue_limits through the rdev sysfs stores Jack Wang
2026-09-10 8:11 ` [PATCH v2 4/8] md: defer the io_opt update out of the sync thread Jack Wang
2026-09-10 8:11 ` [PATCH v2 5/8] md: take q->limits_lock before locking and suspending the array Jack Wang
2026-09-10 8:11 ` [PATCH v2 6/8] md: pass a queue_limits through ->run() Jack Wang
2026-09-11 10:54 ` Nilay Shroff
2026-09-10 8:11 ` [PATCH v2 7/8] md: open new legs before locking the array Jack Wang
2026-09-10 8:11 ` [PATCH v2 8/8] md: link a new leg's holder " Jack Wang
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=20260910081114.1605746-1-jinpu.wang@ionos.com \
--to=jinpu.wang@ionos.com \
--cc=abd.masalkhi@gmail.com \
--cc=axboe@kernel.dk \
--cc=dlemoal@kernel.org \
--cc=dm-devel@lists.linux.dev \
--cc=hch@lst.de \
--cc=jinpu.wang@cloud.ionos.com \
--cc=linux-block@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-raid@vger.kernel.org \
--cc=magiclinan@didiglobal.com \
--cc=mpatocka@redhat.com \
--cc=nilay@linux.ibm.com \
--cc=snitzer@kernel.org \
--cc=song@kernel.org \
--cc=tom.leiming@gmail.com \
--cc=xiao@kernel.org \
--cc=yukuai@fygo.io \
/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