diff options
author | Jens Axboe <axboe@kernel.dk> | 2021-04-08 18:55:14 +0300 |
---|---|---|
committer | Jens Axboe <axboe@kernel.dk> | 2021-04-08 18:55:14 +0300 |
commit | ff917638359e2d0dc122acbb23b74be294ee3d77 (patch) | |
tree | dd884110605b753d99891fecf518efe6450871db | |
parent | 9c282c29a3aee7d439ea871bd21a3e58bc37175e (diff) | |
parent | 65aa97c4d2bfd76677c211b9d03ef05a98c6d68e (diff) | |
download | linux-ff917638359e2d0dc122acbb23b74be294ee3d77.tar.xz |
Merge branch 'md-next' of https://git.kernel.org/pub/scm/linux/kernel/git/song/md into for-5.13/drivers
Pull MD updates from Song:
"These patches fix a race condition with md_release() and md_open()."
* 'md-next' of https://git.kernel.org/pub/scm/linux/kernel/git/song/md:
md: split mddev_find
md: factor out a mddev_find_locked helper from mddev_find
md: md_open returns -EBUSY when entering racing area
-rw-r--r-- | drivers/md/md.c | 59 |
1 files changed, 39 insertions, 20 deletions
diff --git a/drivers/md/md.c b/drivers/md/md.c index 368cad6cd53a..3ce5f4e0f431 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -734,8 +734,35 @@ void mddev_init(struct mddev *mddev) } EXPORT_SYMBOL_GPL(mddev_init); +static struct mddev *mddev_find_locked(dev_t unit) +{ + struct mddev *mddev; + + list_for_each_entry(mddev, &all_mddevs, all_mddevs) + if (mddev->unit == unit) + return mddev; + + return NULL; +} + 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) @@ -745,13 +772,13 @@ static struct mddev *mddev_find(dev_t unit) spin_lock(&all_mddevs_lock); if (unit) { - list_for_each_entry(mddev, &all_mddevs, all_mddevs) - if (mddev->unit == unit) { - mddev_get(mddev); - spin_unlock(&all_mddevs_lock); - kfree(new); - return mddev; - } + mddev = mddev_find_locked(unit); + if (mddev) { + mddev_get(mddev); + spin_unlock(&all_mddevs_lock); + kfree(new); + return mddev; + } if (new) { list_add(&new->all_mddevs, &all_mddevs); @@ -777,12 +804,7 @@ static struct mddev *mddev_find(dev_t unit) return NULL; } - is_free = 1; - list_for_each_entry(mddev, &all_mddevs, all_mddevs) - if (mddev->unit == dev) { - is_free = 0; - break; - } + is_free = !mddev_find_locked(dev); } new->unit = dev; new->md_minor = MINOR(dev); @@ -5644,7 +5666,7 @@ static int md_alloc(dev_t dev, char *name) * 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; @@ -6524,11 +6546,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 @@ -7821,8 +7841,7 @@ static int md_open(struct block_device *bdev, fmode_t mode) /* Wait until bdev->bd_disk is definitely gone */ if (work_pending(&mddev->del_work)) flush_workqueue(md_misc_wq); - /* Then retry the open from the top */ - return -ERESTARTSYS; + return -EBUSY; } BUG_ON(mddev != bdev->bd_disk->private_data); |