From: Jack Wang end_reshape() calls this from the sync thread, and md_reap_sync_thread() waits for that thread with reconfig_mutex held. Waiting for q->limits_lock here hangs a finishing reshape when the lock's holder is waiting for I/O that only md_check_recovery() can complete. Use the trylock and skip the change on a contended pass; io_opt is a hint. Callers that own an update pass it in and have it changed in place. Only the lock leg is addressed; the same cycle also runs through the mddev_suspend() below, which this function has always done from the sync thread. Assisted-by: Claude:claude-opus-5 Signed-off-by: Jack Wang --- drivers/md/md.c | 33 +++++++++++++++++++++++++++------ drivers/md/md.h | 3 ++- drivers/md/raid10.c | 2 +- drivers/md/raid5.c | 2 +- 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/drivers/md/md.c b/drivers/md/md.c index 8ad6fe178e96..6af11a74db57 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -6344,19 +6344,40 @@ static bool mddev_stack_limits_trylock(struct mddev *mddev, } /* update the optimal I/O size after a reshape */ -void mddev_update_io_opt(struct mddev *mddev, unsigned int nr_stripes) +void mddev_update_io_opt(struct mddev *mddev, unsigned int nr_stripes, + struct queue_limits *lim) { - struct queue_limits lim; + struct queue_limits own; if (mddev_is_dm(mddev)) return; + /* + * With an update owned by the caller just change it in place; it is + * committed, and the array resumed, by whoever started it. Taking + * q->limits_lock here would nest it inside reconfig_mutex and the + * suspend, which deadlocks, see md_start_sync(). + */ + if (lim) { + lim->io_opt = lim->io_min * nr_stripes; + return; + } + + /* + * Called from the sync thread, which md_reap_sync_thread() waits for + * with reconfig_mutex held, so don't wait for q->limits_lock here. + * io_opt is a hint, skipping it on a contended pass is fine. + */ + if (!mddev_stack_limits_trylock(mddev, &own)) + return; + /* don't bother updating io_opt if we can't suspend the array */ - if (mddev_suspend(mddev, false) < 0) + if (mddev_suspend(mddev, false) < 0) { + queue_limits_cancel_update(mddev->gendisk->queue); return; - lim = queue_limits_start_update(mddev->gendisk->queue); - lim.io_opt = lim.io_min * nr_stripes; - queue_limits_commit_update(mddev->gendisk->queue, &lim); + } + own.io_opt = own.io_min * nr_stripes; + queue_limits_commit_update(mddev->gendisk->queue, &own); mddev_resume(mddev); } EXPORT_SYMBOL_GPL(mddev_update_io_opt); diff --git a/drivers/md/md.h b/drivers/md/md.h index 39b95951cc17..9e3bd5ab5519 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -1050,7 +1050,8 @@ int mddev_stack_rdev_limits(struct mddev *mddev, struct queue_limits *lim, int mddev_stack_new_rdev(struct mddev *mddev, struct md_rdev *rdev); int mddev_stack_rdev_into(struct mddev *mddev, struct md_rdev *rdev, struct queue_limits *lim); -void mddev_update_io_opt(struct mddev *mddev, unsigned int nr_stripes); +void mddev_update_io_opt(struct mddev *mddev, unsigned int nr_stripes, + struct queue_limits *lim); extern const struct block_device_operations md_fops; diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index a5b65f377d04..641619328a6c 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -4928,7 +4928,7 @@ static void end_reshape(struct r10conf *conf) conf->reshape_safe = MaxSector; spin_unlock_irq(&conf->device_lock); - mddev_update_io_opt(conf->mddev, raid10_nr_stripes(conf)); + mddev_update_io_opt(conf->mddev, raid10_nr_stripes(conf), NULL); conf->fullsync = 0; } diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 0ec555ada64a..3faa2a94c03b 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -8800,7 +8800,7 @@ static void end_reshape(struct r5conf *conf) wake_up(&conf->wait_for_reshape); mddev_update_io_opt(conf->mddev, - conf->raid_disks - conf->max_degraded); + conf->raid_disks - conf->max_degraded, NULL); } } -- 2.43.0