| From 65aa97c4d2bfd76677c211b9d03ef05a98c6d68e Mon Sep 17 00:00:00 2001 |
| From: Christoph Hellwig <hch@lst.de> |
| Date: Sat, 3 Apr 2021 18:15:29 +0200 |
| Subject: md: split mddev_find |
| |
| From: Christoph Hellwig <hch@lst.de> |
| |
| commit 65aa97c4d2bfd76677c211b9d03ef05a98c6d68e upstream. |
| |
| Split mddev_find into a simple mddev_find that just finds an existing |
| mddev by the unit number, and a more complicated mddev_find that deals |
| with find or allocating a mddev. |
| |
| This turns out to fix this bug reported by Zhao Heming. |
| |
| ----------------------------- snip ------------------------------ |
| commit d3374825ce57 ("md: make devices disappear when they are no longer |
| needed.") introduced protection between mddev creating & removing. The |
| md_open shouldn't create mddev when all_mddevs list doesn't contain |
| mddev. With currently code logic, there will be very easy to trigger |
| soft lockup in non-preempt env. |
| |
| --- |
| drivers/md/md.c | 24 +++++++++++++++++++----- |
| 1 file changed, 19 insertions(+), 5 deletions(-) |
| |
| --- a/drivers/md/md.c |
| +++ b/drivers/md/md.c |
| @@ -754,6 +754,22 @@ EXPORT_SYMBOL_GPL(mddev_init); |
| |
| static struct mddev *mddev_find(dev_t unit) |
| { |
| + struct mddev *mddev; |
| + |
| + if (MAJOR(unit) != MD_MAJOR) |
| + unit &= ~((1 << MdpMinorShift) - 1); |
| + |
| + spin_lock(&all_mddevs_lock); |
| + mddev = mddev_find_locked(unit); |
| + if (mddev) |
| + mddev_get(mddev); |
| + spin_unlock(&all_mddevs_lock); |
| + |
| + return mddev; |
| +} |
| + |
| +static struct mddev *mddev_find_or_alloc(dev_t unit) |
| +{ |
| struct mddev *mddev, *new = NULL; |
| |
| if (unit && MAJOR(unit) != MD_MAJOR) |
| @@ -5657,7 +5673,7 @@ static int md_alloc(dev_t dev, char *nam |
| * writing to /sys/module/md_mod/parameters/new_array. |
| */ |
| static DEFINE_MUTEX(disks_mutex); |
| - struct mddev *mddev = mddev_find(dev); |
| + struct mddev *mddev = mddev_find_or_alloc(dev); |
| struct gendisk *disk; |
| int partitioned; |
| int shift; |
| @@ -6539,11 +6555,9 @@ static void autorun_devices(int part) |
| |
| md_probe(dev); |
| mddev = mddev_find(dev); |
| - if (!mddev || !mddev->gendisk) { |
| - if (mddev) |
| - mddev_put(mddev); |
| + if (!mddev) |
| break; |
| - } |
| + |
| if (mddev_lock(mddev)) |
| pr_warn("md: %s locked, cannot run\n", mdname(mddev)); |
| else if (mddev->raid_disks || mddev->major_version |