mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-01 06:33:07 +00:00
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:
parent
d41003513e
commit
f902b02600
1 changed files with 96 additions and 80 deletions
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue