block: refactor rescan_partitions
authorChristoph Hellwig <hch@lst.de>
Thu, 14 Nov 2019 14:34:32 +0000 (15:34 +0100)
committerJens Axboe <axboe@kernel.dk>
Thu, 14 Nov 2019 14:40:55 +0000 (07:40 -0700)
Split out a helper that adds one single partition, and another one
calling that dealing with the parsed_partitions state.  This makes
it much more obvious how we clean up all state and start again when
using the rescan label.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Jan Kara <jack@suse.cz>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
block/partition-generic.c

index 31bff3fb28af82d800e116d27e67fd87827b33bd..7eabb67f99b5f1ab00664576c5294ac14e6e03ea 100644 (file)
@@ -459,128 +459,144 @@ static int drop_partitions(struct gendisk *disk, struct block_device *bdev)
        return 0;
 }
 
-int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
+static bool blk_add_partition(struct gendisk *disk, struct block_device *bdev,
+               struct parsed_partitions *state, int p)
 {
-       struct parsed_partitions *state = NULL;
+       sector_t size = state->parts[p].size;
+       sector_t from = state->parts[p].from;
        struct hd_struct *part;
-       int p, highest, res;
-rescan:
-       if (state && !IS_ERR(state)) {
-               free_partitions(state);
-               state = NULL;
+
+       if (!size)
+               return true;
+
+       if (from >= get_capacity(disk)) {
+               printk(KERN_WARNING
+                      "%s: p%d start %llu is beyond EOD, ",
+                      disk->disk_name, p, (unsigned long long) from);
+               if (disk_unlock_native_capacity(disk))
+                       return false;
+               return true;
        }
 
-       res = drop_partitions(disk, bdev);
-       if (res)
-               return res;
+       if (from + size > get_capacity(disk)) {
+               printk(KERN_WARNING
+                      "%s: p%d size %llu extends beyond EOD, ",
+                      disk->disk_name, p, (unsigned long long) size);
 
-       if (disk->fops->revalidate_disk)
-               disk->fops->revalidate_disk(disk);
-       check_disk_size_change(disk, bdev, true);
-       bdev->bd_invalidated = 0;
-       if (!get_capacity(disk) || !(state = check_partition(disk, bdev)))
+               if (disk_unlock_native_capacity(disk))
+                       return false;
+
+               /*
+                * We can not ignore partitions of broken tables created by for
+                * example camera firmware, but we limit them to the end of the
+                * disk to avoid creating invalid block devices.
+                */
+               size = get_capacity(disk) - from;
+       }
+
+       part = add_partition(disk, p, from, size, state->parts[p].flags,
+                            &state->parts[p].info);
+       if (IS_ERR(part)) {
+               printk(KERN_ERR " %s: p%d could not be added: %ld\n",
+                      disk->disk_name, p, -PTR_ERR(part));
+               return true;
+       }
+
+#ifdef CONFIG_BLK_DEV_MD
+       if (state->parts[p].flags & ADDPART_FLAG_RAID)
+               md_autodetect_dev(part_to_dev(part)->devt);
+#endif
+       return true;
+}
+
+static int blk_add_partitions(struct gendisk *disk, struct block_device *bdev)
+{
+       struct parsed_partitions *state;
+       int ret = -EAGAIN, p, highest;
+
+       state = check_partition(disk, bdev);
+       if (!state)
                return 0;
        if (IS_ERR(state)) {
                /*
-                * I/O error reading the partition table.  If any
-                * partition code tried to read beyond EOD, retry
-                * after unlocking native capacity.
+                * I/O error reading the partition table.  If we tried to read
+                * beyond EOD, retry after unlocking the native capacity.
                 */
                if (PTR_ERR(state) == -ENOSPC) {
                        printk(KERN_WARNING "%s: partition table beyond EOD, ",
                               disk->disk_name);
                        if (disk_unlock_native_capacity(disk))
-                               goto rescan;
+                               return -EAGAIN;
                }
                return -EIO;
        }
 
-       /* Partitions are not supported on zoned block devices */
+       /*
+        * Partitions are not supported on zoned block devices.
+        */
        if (bdev_is_zoned(bdev)) {
                pr_warn("%s: ignoring partition table on zoned block device\n",
                        disk->disk_name);
-               goto out;
+               ret = 0;
+               goto out_free_state;
        }
 
        /*
-        * If any partition code tried to read beyond EOD, try
-        * unlocking native capacity even if partition table is
-        * successfully read as we could be missing some partitions.
+        * If we read beyond EOD, try unlocking native capacity even if the
+        * partition table was successfully read as we could be missing some
+        * partitions.
         */
        if (state->access_beyond_eod) {
                printk(KERN_WARNING
                       "%s: partition table partially beyond EOD, ",
                       disk->disk_name);
                if (disk_unlock_native_capacity(disk))
-                       goto rescan;
+                       goto out_free_state;
        }
 
        /* tell userspace that the media / partition table may have changed */
        kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE);
 
-       /* Detect the highest partition number and preallocate
-        * disk->part_tbl.  This is an optimization and not strictly
-        * necessary.
+       /*
+        * Detect the highest partition number and preallocate disk->part_tbl.
+        * This is an optimization and not strictly necessary.
         */
        for (p = 1, highest = 0; p < state->limit; p++)
                if (state->parts[p].size)
                        highest = p;
-
        disk_expand_part_tbl(disk, highest);
 
-       /* add partitions */
-       for (p = 1; p < state->limit; p++) {
-               sector_t size, from;
+       for (p = 1; p < state->limit; p++)
+               if (!blk_add_partition(disk, bdev, state, p))
+                       goto out_free_state;
 
-               size = state->parts[p].size;
-               if (!size)
-                       continue;
+       ret = 0;
+out_free_state:
+       free_partitions(state);
+       return ret;
+}
 
-               from = state->parts[p].from;
-               if (from >= get_capacity(disk)) {
-                       printk(KERN_WARNING
-                              "%s: p%d start %llu is beyond EOD, ",
-                              disk->disk_name, p, (unsigned long long) from);
-                       if (disk_unlock_native_capacity(disk))
-                               goto rescan;
-                       continue;
-               }
+int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
+{
+       int ret;
 
-               if (from + size > get_capacity(disk)) {
-                       printk(KERN_WARNING
-                              "%s: p%d size %llu extends beyond EOD, ",
-                              disk->disk_name, p, (unsigned long long) size);
-
-                       if (disk_unlock_native_capacity(disk)) {
-                               /* free state and restart */
-                               goto rescan;
-                       } else {
-                               /*
-                                * we can not ignore partitions of broken tables
-                                * created by for example camera firmware, but
-                                * we limit them to the end of the disk to avoid
-                                * creating invalid block devices
-                                */
-                               size = get_capacity(disk) - from;
-                       }
-               }
+rescan:
+       ret = drop_partitions(disk, bdev);
+       if (ret)
+               return ret;
 
-               part = add_partition(disk, p, from, size,
-                                    state->parts[p].flags,
-                                    &state->parts[p].info);
-               if (IS_ERR(part)) {
-                       printk(KERN_ERR " %s: p%d could not be added: %ld\n",
-                              disk->disk_name, p, -PTR_ERR(part));
-                       continue;
-               }
-#ifdef CONFIG_BLK_DEV_MD
-               if (state->parts[p].flags & ADDPART_FLAG_RAID)
-                       md_autodetect_dev(part_to_dev(part)->devt);
-#endif
-       }
-out:
-       free_partitions(state);
-       return 0;
+       if (disk->fops->revalidate_disk)
+               disk->fops->revalidate_disk(disk);
+       check_disk_size_change(disk, bdev, true);
+       bdev->bd_invalidated = 0;
+
+       if (!get_capacity(disk))
+               return 0;
+
+       ret = blk_add_partitions(disk, bdev);
+       if (ret == -EAGAIN)
+               goto rescan;
+       return ret;
 }
 
 int invalidate_partitions(struct gendisk *disk, struct block_device *bdev)