]> www.infradead.org Git - users/hch/block.git/commitdiff
md: only delete entries from all_mddevs when the disk is freed
authorChristoph Hellwig <hch@lst.de>
Fri, 8 Jul 2022 12:35:37 +0000 (14:35 +0200)
committerChristoph Hellwig <hch@lst.de>
Tue, 12 Jul 2022 06:59:16 +0000 (08:59 +0200)
This ensures device names don't get prematurely reused.  Instead add a
deleted flag to skip already deleted devices in mddev_get and other
places that only want to see live mddevs.

Reported-by; Logan Gunthorpe <logang@deltatee.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
drivers/md/md.c
drivers/md/md.h

index f15bc6bb65f2d933ab9affdb2d140d9401690af3..9afc438a08e4dc1151ad5843c12b21fe9aedff43 100644 (file)
@@ -625,6 +625,10 @@ EXPORT_SYMBOL(md_flush_request);
 
 static inline struct mddev *mddev_get(struct mddev *mddev)
 {
+       lockdep_assert_held(&all_mddevs_lock);
+
+       if (mddev->deleted)
+               return NULL;
        atomic_inc(&mddev->active);
        return mddev;
 }
@@ -639,7 +643,7 @@ static void mddev_put(struct mddev *mddev)
            mddev->ctime == 0 && !mddev->hold_active) {
                /* Array is not configured at all, and not held active,
                 * so destroy it */
-               list_del_init(&mddev->all_mddevs);
+               mddev->deleted = true;
 
                /*
                 * Call queue_work inside the spinlock so that
@@ -720,8 +724,8 @@ static struct mddev *mddev_find(dev_t unit)
 
        spin_lock(&all_mddevs_lock);
        mddev = mddev_find_locked(unit);
-       if (mddev)
-               mddev_get(mddev);
+       if (mddev && !mddev_get(mddev))
+               mddev = NULL;
        spin_unlock(&all_mddevs_lock);
 
        return mddev;
@@ -3330,6 +3334,8 @@ static bool md_rdev_overlaps(struct md_rdev *rdev)
 
        spin_lock(&all_mddevs_lock);
        list_for_each_entry(mddev, &all_mddevs, all_mddevs) {
+               if (mddev->deleted)
+                       continue;
                rdev_for_each(rdev2, mddev) {
                        if (rdev != rdev2 && rdev->bdev == rdev2->bdev &&
                            md_rdevs_overlap(rdev, rdev2)) {
@@ -5504,11 +5510,10 @@ md_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
        if (!entry->show)
                return -EIO;
        spin_lock(&all_mddevs_lock);
-       if (list_empty(&mddev->all_mddevs)) {
+       if (!mddev_get(mddev)) {
                spin_unlock(&all_mddevs_lock);
                return -EBUSY;
        }
-       mddev_get(mddev);
        spin_unlock(&all_mddevs_lock);
 
        rv = entry->show(mddev, page);
@@ -5529,11 +5534,10 @@ md_attr_store(struct kobject *kobj, struct attribute *attr,
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
        spin_lock(&all_mddevs_lock);
-       if (list_empty(&mddev->all_mddevs)) {
+       if (!mddev_get(mddev)) {
                spin_unlock(&all_mddevs_lock);
                return -EBUSY;
        }
-       mddev_get(mddev);
        spin_unlock(&all_mddevs_lock);
        rv = entry->store(mddev, page, length);
        mddev_put(mddev);
@@ -7815,6 +7819,10 @@ static void md_free_disk(struct gendisk *disk)
 {
        struct mddev *mddev = disk->private_data;
 
+       spin_lock(&all_mddevs_lock);
+       list_del_init(&mddev->all_mddevs);
+       spin_unlock(&all_mddevs_lock);
+
        percpu_ref_exit(&mddev->writes_pending);
        bioset_exit(&mddev->bio_set);
        bioset_exit(&mddev->sync_set);
@@ -8131,6 +8139,8 @@ static void *md_seq_start(struct seq_file *seq, loff_t *pos)
                if (!l--) {
                        mddev = list_entry(tmp, struct mddev, all_mddevs);
                        mddev_get(mddev);
+                       if (!mddev_get(mddev))
+                               continue;
                        spin_unlock(&all_mddevs_lock);
                        return mddev;
                }
@@ -8144,25 +8154,35 @@ static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 {
        struct list_head *tmp;
        struct mddev *next_mddev, *mddev = v;
+       struct mddev *to_put = NULL;
 
        ++*pos;
        if (v == (void*)2)
                return NULL;
 
        spin_lock(&all_mddevs_lock);
-       if (v == (void*)1)
+       if (v == (void*)1) {
                tmp = all_mddevs.next;
-       else
+       } else {
+               to_put = mddev;
                tmp = mddev->all_mddevs.next;
-       if (tmp != &all_mddevs)
-               next_mddev = mddev_get(list_entry(tmp,struct mddev,all_mddevs));
-       else {
-               next_mddev = (void*)2;
-               *pos = 0x10000;
        }
+
+       for (;;) {
+               if (tmp == &all_mddevs) {
+                       next_mddev = (void*)2;
+                       *pos = 0x10000;
+                       break;
+               }
+               next_mddev = list_entry(tmp, struct mddev, all_mddevs);
+               if (mddev_get(next_mddev))
+                       break;
+               mddev = next_mddev;
+               tmp = mddev->all_mddevs.next;
+       };
        spin_unlock(&all_mddevs_lock);
 
-       if (v != (void*)1)
+       if (to_put)
                mddev_put(mddev);
        return next_mddev;
 
@@ -8732,6 +8752,8 @@ void md_do_sync(struct md_thread *thread)
                        goto skip;
                spin_lock(&all_mddevs_lock);
                list_for_each_entry(mddev2, &all_mddevs, all_mddevs) {
+                       if (mddev2->deleted)
+                               continue;
                        if (mddev2 == mddev)
                                continue;
                        if (!mddev->parallel_resync
@@ -9530,7 +9552,8 @@ static int md_notify_reboot(struct notifier_block *this,
 
        spin_lock(&all_mddevs_lock);
        list_for_each_entry_safe(mddev, n, &all_mddevs, all_mddevs) {
-               mddev_get(mddev);
+               if (!mddev_get(mddev))
+                       continue;
                spin_unlock(&all_mddevs_lock);
                if (mddev_trylock(mddev)) {
                        if (mddev->pers)
@@ -9885,7 +9908,8 @@ static __exit void md_exit(void)
 
        spin_lock(&all_mddevs_lock);
        list_for_each_entry_safe(mddev, n, &all_mddevs, all_mddevs) {
-               mddev_get(mddev);
+               if (!mddev_get(mddev))
+                       continue;
                spin_unlock(&all_mddevs_lock);
                export_array(mddev);
                mddev->ctime = 0;
index cf2cbb17acbd423ccebf7d1a008fc47ae07ed6a5..e731a2fdc6ac190a2f74fc2f4d57397c14608596 100644 (file)
@@ -488,6 +488,7 @@ struct mddev {
 
        atomic_t                        max_corr_read_errors; /* max read retries */
        struct list_head                all_mddevs;
+       bool                            deleted;
 
        const struct attribute_group    *to_remove;