block: extend the lifetime of disk parent devices From: Dan Williams Commit df08c32ce3be "block: fix bdi vs gendisk lifetime mismatch" fixed the case where the bdi is named from a gendisk with a dynamically allocated devt. However this leaves the bug in place hole for drivers using a static devt like sd. James observes that for scsi we can address the issue by building on the initial fix to extend the lifetime of the disk device until the bdi is released. This effectively makes the lifetime of a statically allocated devt identical to that of a dynamically allocated devt. However, this leaves a hole for block device drivers that use a statically allocated devt, but do not specify a parent device. Those drivers if they exist, should move to dynamically allocated devt, or register a parent device if they are on a hotplug capable bus. Cc: Bart Van Assche Cc: Jens Axboe Cc: "Martin K. Petersen" , Cc: Christoph Hellwig Cc: Tejun Heo Cc: Dave Hansen , Suggested-by: James Bottomley Signed-off-by: Dan Williams --- block/genhd.c | 5 ++++- include/linux/backing-dev.h | 2 +- mm/backing-dev.c | 13 +++++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/block/genhd.c b/block/genhd.c index fcd6d4fae657..4c3ca8424535 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -614,7 +614,7 @@ void device_add_disk(struct device *parent, struct gendisk *disk) /* Register BDI before referencing it from bdev */ bdi = &disk->queue->backing_dev_info; - bdi_register_owner(bdi, disk_to_dev(disk)); + bdi_register_disk(bdi, disk); blk_register_region(disk_devt(disk), disk->minors, NULL, exact_match, exact_lock, disk); @@ -1144,6 +1144,9 @@ static void disk_release(struct device *dev) hd_free_part(&disk->part0); if (disk->queue) blk_put_queue(disk->queue); + + /* see bdi_register_disk() */ + put_device(dev->parent); kfree(disk); } struct class block_class = { diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index 43b93a947e61..df9e1a766157 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -24,7 +24,7 @@ __printf(3, 4) int bdi_register(struct backing_dev_info *bdi, struct device *parent, const char *fmt, ...); int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev); -int bdi_register_owner(struct backing_dev_info *bdi, struct device *owner); +int bdi_register_disk(struct backing_dev_info *bdi, struct gendisk *disk); void bdi_unregister(struct backing_dev_info *bdi); int __must_check bdi_setup_and_register(struct backing_dev_info *, char *); diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 8fde443f36d7..b621c8e8cd68 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -825,8 +825,9 @@ int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev) } EXPORT_SYMBOL(bdi_register_dev); -int bdi_register_owner(struct backing_dev_info *bdi, struct device *owner) +int bdi_register_disk(struct backing_dev_info *bdi, struct gendisk *disk) { + struct device *owner = disk_to_dev(disk); int rc; rc = bdi_register(bdi, NULL, "%u:%u", MAJOR(owner->devt), @@ -835,9 +836,17 @@ int bdi_register_owner(struct backing_dev_info *bdi, struct device *owner) return rc; bdi->owner = owner; get_device(owner); + + /* + * For statically allocated devt disks, like scsi, the disk's + * parent holds the lifetime for the devt. Prevent the parent + * from releasing the devt for reuse until the disk is released. + */ + get_device(owner->parent); + return 0; } -EXPORT_SYMBOL(bdi_register_owner); +EXPORT_SYMBOL(bdi_register_disk); /* * Remove bdi from bdi_list, and ensure that it is no longer visible