[PATCH 3/6] md: don't wait for q->limits_lock in check_sb_changes()
From: Jack Wang <jinpu.wang@ionos.com>
Date: 2026-09-07 13:39:37
Also in:
linux-raid
Subsystem:
software raid (multiple disks) support, the rest · Maintainers:
Song Liu, Yu Kuai, Linus Torvalds
From: Jack Wang <jinpu.wang@cloud.ionos.com> check_sb_changes() activates a spare added by another node, with reconfig_mutex held, from md_reload_sb(). Stacking that leg's limits waits for q->limits_lock while holding the mutex, and the lock's holder waits for I/O that can be waiting for a superblock update needing the same mutex. Take the update without blocking and keep the activation inline. On a contended pass only remove; MD_RECOVERY_NEEDED is set below, so md_start_sync() does the add. Assisted-by: Claude:claude-opus-5 Signed-off-by: Jack Wang <jinpu.wang@ionos.com> --- drivers/md/md.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 28fc903ffeea..c7d1f9813f5d 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c@@ -6310,6 +6310,20 @@ int mddev_stack_rdev_into(struct mddev *mddev, struct md_rdev *rdev, } EXPORT_SYMBOL_GPL(mddev_stack_rdev_into); +/* for callers that must not wait for q->limits_lock, see md_start_sync() */ +static bool mddev_stack_limits_trylock(struct mddev *mddev, + struct queue_limits *lim) +{ + struct request_queue *q; + + if (mddev_is_dm(mddev)) + return false; + + q = mddev->gendisk->queue; + + return queue_limits_start_update_trylock(q, lim); +} + /* update the optimal I/O size after a reshape */ void mddev_update_io_opt(struct mddev *mddev, unsigned int nr_stripes) {
@@ -10829,6 +10843,7 @@ static int __init md_init(void) static void check_sb_changes(struct mddev *mddev, struct md_rdev *rdev) { + struct queue_limits lim; struct mdp_superblock_1 *sb = page_address(rdev->sb_page); struct md_rdev *rdev2, *tmp; int role, ret;
@@ -10885,7 +10900,24 @@ static void check_sb_changes(struct mddev *mddev, struct md_rdev *rdev) rdev2->saved_raid_disk = -1; else rdev2->saved_raid_disk = role; - ret = remove_and_add_spares(mddev, rdev2, NULL); + /* + * reconfig_mutex is held, so don't wait for + * q->limits_lock; MD_RECOVERY_NEEDED below + * leaves a skipped add to md_start_sync(). + */ + if (mddev_stack_limits_trylock(mddev, &lim)) { + struct request_queue *q = + mddev->gendisk->queue; + + ret = remove_and_add_spares(mddev, + rdev2, &lim); + queue_limits_commit_update(q, &lim); + } else if (mddev_is_dm(mddev)) { + ret = remove_and_add_spares(mddev, + rdev2, NULL); + } else { + ret = remove_spares(mddev, rdev2); + } pr_info("Activated spare: %pg\n", rdev2->bdev); /* wakeup mddev->thread here, so array could
--
2.43.0