From: Luis Chamberlain mcgrof@kernel.org
mainline inclusion from mainline-v5.15-rc1 commit 83cbce9574462c6b4eed6797bdaf18fae6859ab3 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I81XCK
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
----------------------------------------
Properly unwind on errors in device_add_disk. This is the initial work as drivers are not converted yet, which will follow in separate patches.
Signed-off-by: Luis Chamberlain mcgrof@kernel.org [hch: major rebase. All bugs are probably mine] Signed-off-by: Christoph Hellwig hch@lst.de Reviewed-by: Hannes Reinecke hare@suse.de Link: https://lore.kernel.org/r/20210818144542.19305-10-hch@lst.de Signed-off-by: Jens Axboe axboe@kernel.dk conflicts: block/genhd.c include/linux/genhd.h Signed-off-by: Zhong Jinghua zhongjinghua@huawei.com Signed-off-by: Li Nan linan122@huawei.com --- include/linux/genhd.h | 6 +-- block/genhd.c | 95 +++++++++++++++++++++++++++++-------------- 2 files changed, 67 insertions(+), 34 deletions(-)
diff --git a/include/linux/genhd.h b/include/linux/genhd.h index 61b5e9359152..bd44031cf35b 100644 --- a/include/linux/genhd.h +++ b/include/linux/genhd.h @@ -305,11 +305,11 @@ extern bool disk_has_partitions(struct gendisk *disk);
/* block/genhd.c */ extern unsigned int part_in_flight(struct hd_struct *part); -extern void device_add_disk(struct device *parent, struct gendisk *disk, +extern int device_add_disk(struct device *parent, struct gendisk *disk, const struct attribute_group **groups); -static inline void add_disk(struct gendisk *disk) +static inline int add_disk(struct gendisk *disk) { - device_add_disk(NULL, disk, NULL); + return device_add_disk(NULL, disk, NULL); } extern void device_add_disk_no_queue_reg(struct device *parent, struct gendisk *disk); static inline void add_disk_no_queue_reg(struct gendisk *disk) diff --git a/block/genhd.c b/block/genhd.c index c620597cfc0d..599885e5bf64 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -761,10 +761,8 @@ static void disk_init_partition(struct gendisk *disk) * * This function registers the partitioning information in @disk * with the kernel. - * - * FIXME: error handling */ -static void __device_add_disk(struct device *parent, struct gendisk *disk, +static int __device_add_disk(struct device *parent, struct gendisk *disk, const struct attribute_group **groups, bool register_queue) { @@ -790,14 +788,14 @@ static void __device_add_disk(struct device *parent, struct gendisk *disk, !(disk->flags & (GENHD_FL_EXT_DEVT | GENHD_FL_HIDDEN)));
retval = blk_alloc_devt(&disk->part0, &devt); - if (retval) { - WARN_ON(1); - return; - } + if (retval) + return retval; disk->major = MAJOR(devt); disk->first_minor = MINOR(devt);
- disk_alloc_events(disk); + retval = disk_alloc_events(disk); + if (retval) + goto out_free_ext_minor;
if (disk->flags & GENHD_FL_HIDDEN) { /* @@ -808,15 +806,17 @@ static void __device_add_disk(struct device *parent, struct gendisk *disk, disk->flags |= GENHD_FL_NO_PART_SCAN; } else { struct backing_dev_info *bdi = disk->queue->backing_dev_info; - int ret;
/* Register BDI before referencing it from bdev */ ddev->devt = devt; - ret = bdi_register(bdi, "%u:%u", MAJOR(devt), MINOR(devt)); - WARN_ON(ret); + retval = bdi_register(bdi, "%u:%u", MAJOR(devt), MINOR(devt)); + if (retval) + goto out_disk_release_events; bdi_set_owner(bdi, ddev); - blk_register_region(disk_devt(disk), disk->minors, NULL, - exact_match, exact_lock, disk); + retval = blk_register_region(disk_devt(disk), disk->minors, + NULL, exact_match, exact_lock, disk); + if (retval) + goto out_unregister_bdi; }
/* delay uevents, until we scanned partition table */ @@ -828,15 +828,14 @@ static void __device_add_disk(struct device *parent, struct gendisk *disk, ddev->groups = groups; } dev_set_name(ddev, "%s", disk->disk_name); - if (device_add(ddev)) - return; + retval = device_add(ddev); + if (retval) + goto out_unregister_region; if (!sysfs_deprecated) { retval = sysfs_create_link(block_depr, &ddev->kobj, - kobject_name(&ddev->kobj)); - if (retval) { - device_del(ddev); - return; - } + kobject_name(&ddev->kobj)); + if (retval) + goto out_device_del; }
/* @@ -846,23 +845,30 @@ static void __device_add_disk(struct device *parent, struct gendisk *disk, */ pm_runtime_set_memalloc_noio(ddev, true);
- blk_integrity_add(disk); + retval = blk_integrity_add(disk); + if (retval) + goto out_del_block_link;
disk->part0.holder_dir = kobject_create_and_add("holders", &ddev->kobj); + if (!disk->part0.holder_dir) + goto out_del_integrity; disk->slave_dir = kobject_create_and_add("slaves", &ddev->kobj); + if (!disk->slave_dir) + goto out_put_holder_dir;
if (!(disk->flags & GENHD_FL_HIDDEN)) { - if (disk->queue->backing_dev_info->dev) { - retval = sysfs_create_link(&ddev->kobj, - &disk->queue->backing_dev_info->dev->kobj, - "bdi"); - WARN_ON(retval); - } + retval = sysfs_create_link(&ddev->kobj, + &disk->queue->backing_dev_info->dev->kobj, "bdi"); + if (retval) + goto out_put_slave_dir; }
- if (register_queue) - blk_register_queue(disk); + if (register_queue) { + retval = blk_register_queue(disk); + if (retval) + goto out_del_bdi_sysfs_link; + }
/* * Take an extra ref on queue which will be put on disk_release() @@ -882,13 +888,40 @@ static void __device_add_disk(struct device *parent, struct gendisk *disk, */ disk->flags |= GENHD_FL_UP; disk_init_partition(disk); + return 0; + +out_del_bdi_sysfs_link: + if (!(disk->flags & GENHD_FL_HIDDEN)) + sysfs_remove_link(&ddev->kobj, "bdi"); +out_put_slave_dir: + kobject_put(disk->slave_dir); +out_put_holder_dir: + kobject_put(disk->part0.holder_dir); +out_del_integrity: + blk_integrity_del(disk); +out_del_block_link: + if (!sysfs_deprecated) + sysfs_remove_link(block_depr, kobject_name(&ddev->kobj)); +out_device_del: + device_del(ddev); +out_unregister_region: + if (!(disk->flags & GENHD_FL_HIDDEN)) + blk_unregister_region(disk_devt(disk), disk->minors); +out_unregister_bdi: + if (!(disk->flags & GENHD_FL_HIDDEN)) + bdi_unregister(disk->queue->backing_dev_info); +out_disk_release_events: + disk_release_events(disk); +out_free_ext_minor: + blk_free_devt(devt); + return WARN_ON_ONCE(retval); /* keep until all callers handle errors */ }
-void device_add_disk(struct device *parent, struct gendisk *disk, +int device_add_disk(struct device *parent, struct gendisk *disk, const struct attribute_group **groups)
{ - __device_add_disk(parent, disk, groups, true); + return __device_add_disk(parent, disk, groups, true); } EXPORT_SYMBOL(device_add_disk);