diff options
Diffstat (limited to 'block')
-rw-r--r-- | block/ioctl.c | 37 | ||||
-rw-r--r-- | block/partition-generic.c | 50 |
2 files changed, 77 insertions, 10 deletions
diff --git a/block/ioctl.c b/block/ioctl.c index 127194b9f9bd..be9ecbae4f13 100644 --- a/block/ioctl.c +++ b/block/ioctl.c @@ -187,21 +187,48 @@ static int compat_blkpg_ioctl(struct block_device *bdev, } #endif -static int blkdev_reread_part(struct block_device *bdev) +/* + * This is an exported API for the block driver, and will not + * acquire bd_mutex. This API should be used in case that + * caller has held bd_mutex already. + */ +int __blkdev_reread_part(struct block_device *bdev) { - int ret; + struct gendisk *disk = bdev->bd_disk; - if (!disk_part_scan_enabled(bdev->bd_disk) || bdev != bdev->bd_contains) + if (!disk_part_scan_enabled(disk) || bdev != bdev->bd_contains) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EACCES; + lockdep_assert_held(&bdev->bd_mutex); + + return rescan_partitions(disk, bdev); +} +EXPORT_SYMBOL(__blkdev_reread_part); + +/* + * This is an exported API for the block driver, and will + * try to acquire bd_mutex. If bd_mutex has been held already + * in current context, please call __blkdev_reread_part(). + * + * Make sure the held locks in current context aren't required + * in open()/close() handler and I/O path for avoiding ABBA deadlock: + * - bd_mutex is held before calling block driver's open/close + * handler + * - reading partition table may submit I/O to the block device + */ +int blkdev_reread_part(struct block_device *bdev) +{ + int res; + mutex_lock(&bdev->bd_mutex); - ret = bdev_disk_changed(bdev, false); + res = __blkdev_reread_part(bdev); mutex_unlock(&bdev->bd_mutex); - return ret; + return res; } +EXPORT_SYMBOL(blkdev_reread_part); static int blk_ioctl_discard(struct block_device *bdev, fmode_t mode, unsigned long arg, unsigned long flags) diff --git a/block/partition-generic.c b/block/partition-generic.c index 8a7906fa96fd..1f51aa8dcc06 100644 --- a/block/partition-generic.c +++ b/block/partition-generic.c @@ -460,7 +460,7 @@ static bool disk_unlock_native_capacity(struct gendisk *disk) } } -int blk_drop_partitions(struct gendisk *disk, struct block_device *bdev) +static int drop_partitions(struct gendisk *disk, struct block_device *bdev) { struct disk_part_iter piter; struct hd_struct *part; @@ -532,14 +532,11 @@ static bool blk_add_partition(struct gendisk *disk, struct block_device *bdev, return true; } -int blk_add_partitions(struct gendisk *disk, struct block_device *bdev) +static int blk_add_partitions(struct gendisk *disk, struct block_device *bdev) { struct parsed_partitions *state; int ret = -EAGAIN, p, highest; - if (!disk_part_scan_enabled(disk)) - return 0; - state = check_partition(disk, bdev); if (!state) return 0; @@ -602,6 +599,49 @@ out_free_state: 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 res; + + if (!bdev->bd_invalidated) + return 0; + + res = drop_partitions(disk, bdev); + if (res) + return res; + + set_capacity(disk, 0); + check_disk_size_change(disk, bdev, false); + bdev->bd_invalidated = 0; + /* tell userspace that the media / partition table may have changed */ + kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE); + + return 0; +} + unsigned char *read_dev_sector(struct block_device *bdev, sector_t n, Sector *p) { struct address_space *mapping = bdev->bd_inode->i_mapping; |