block: refactor rescan_partitions

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>
This commit is contained in:
Christoph Hellwig 2019-11-14 15:34:32 +01:00 committed by Jens Axboe
parent d41003513e
commit f902b02600

View file

@ -459,128 +459,144 @@ static int drop_partitions(struct gendisk *disk, struct block_device *bdev)
return 0; 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; struct hd_struct *part;
int p, highest, res;
rescan: if (!size)
if (state && !IS_ERR(state)) { return true;
free_partitions(state);
state = NULL; 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 (from + size > get_capacity(disk)) {
if (res) printk(KERN_WARNING
return res; "%s: p%d size %llu extends beyond EOD, ",
disk->disk_name, p, (unsigned long long) size);
if (disk->fops->revalidate_disk) if (disk_unlock_native_capacity(disk))
disk->fops->revalidate_disk(disk); return false;
check_disk_size_change(disk, bdev, true);
bdev->bd_invalidated = 0; /*
if (!get_capacity(disk) || !(state = check_partition(disk, bdev))) * 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; return 0;
if (IS_ERR(state)) { if (IS_ERR(state)) {
/* /*
* I/O error reading the partition table. If any * I/O error reading the partition table. If we tried to read
* partition code tried to read beyond EOD, retry * beyond EOD, retry after unlocking the native capacity.
* after unlocking native capacity.
*/ */
if (PTR_ERR(state) == -ENOSPC) { if (PTR_ERR(state) == -ENOSPC) {
printk(KERN_WARNING "%s: partition table beyond EOD, ", printk(KERN_WARNING "%s: partition table beyond EOD, ",
disk->disk_name); disk->disk_name);
if (disk_unlock_native_capacity(disk)) if (disk_unlock_native_capacity(disk))
goto rescan; return -EAGAIN;
} }
return -EIO; return -EIO;
} }
/* Partitions are not supported on zoned block devices */ /*
* Partitions are not supported on zoned block devices.
*/
if (bdev_is_zoned(bdev)) { if (bdev_is_zoned(bdev)) {
pr_warn("%s: ignoring partition table on zoned block device\n", pr_warn("%s: ignoring partition table on zoned block device\n",
disk->disk_name); disk->disk_name);
goto out; ret = 0;
goto out_free_state;
} }
/* /*
* If any partition code tried to read beyond EOD, try * If we read beyond EOD, try unlocking native capacity even if the
* unlocking native capacity even if partition table is * partition table was successfully read as we could be missing some
* successfully read as we could be missing some partitions. * partitions.
*/ */
if (state->access_beyond_eod) { if (state->access_beyond_eod) {
printk(KERN_WARNING printk(KERN_WARNING
"%s: partition table partially beyond EOD, ", "%s: partition table partially beyond EOD, ",
disk->disk_name); disk->disk_name);
if (disk_unlock_native_capacity(disk)) if (disk_unlock_native_capacity(disk))
goto rescan; goto out_free_state;
} }
/* tell userspace that the media / partition table may have changed */ /* tell userspace that the media / partition table may have changed */
kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE); 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 * Detect the highest partition number and preallocate disk->part_tbl.
* necessary. * This is an optimization and not strictly necessary.
*/ */
for (p = 1, highest = 0; p < state->limit; p++) for (p = 1, highest = 0; p < state->limit; p++)
if (state->parts[p].size) if (state->parts[p].size)
highest = p; highest = p;
disk_expand_part_tbl(disk, highest); disk_expand_part_tbl(disk, highest);
/* add partitions */ for (p = 1; p < state->limit; p++)
for (p = 1; p < state->limit; p++) { if (!blk_add_partition(disk, bdev, state, p))
sector_t size, from; goto out_free_state;
size = state->parts[p].size; ret = 0;
if (!size) out_free_state:
continue;
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;
}
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;
}
}
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); free_partitions(state);
return 0; return ret;
}
int rescan_partitions(struct gendisk *disk, struct block_device *bdev)
{
int ret;
rescan:
ret = drop_partitions(disk, bdev);
if (ret)
return ret;
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) int invalidate_partitions(struct gendisk *disk, struct block_device *bdev)