Skip to content

Commit

Permalink
block: refactor rescan_partitions
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Christoph Hellwig authored and Jens Axboe committed Nov 14, 2019
1 parent d410035 commit f902b02
Showing 1 changed file with 94 additions and 78 deletions.
172 changes: 94 additions & 78 deletions block/partition-generic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit f902b02

Please sign in to comment.