From: Dan Williams Date: Sat, 15 Sep 2018 00:34:35 +0000 (-0700) Subject: block: fix bdi vs gendisk lifetime mismatch X-Git-Tag: v4.1.12-124.31.3~448 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=8bd274934987c2af04c2fe862009c026c5ba4910;p=users%2Fjedix%2Flinux-maple.git block: fix bdi vs gendisk lifetime mismatch Orabug: 28645416 The name for a bdi of a gendisk is derived from the gendisk's devt. However, since the gendisk is destroyed before the bdi it leaves a window where a new gendisk could dynamically reuse the same devt while a bdi with the same name is still live. Arrange for the bdi to hold a reference against its "owner" disk device while it is registered. Otherwise we can hit sysfs duplicate name collisions like the following: WARNING: CPU: 10 PID: 2078 at fs/sysfs/dir.c:31 sysfs_warn_dup+0x64/0x80 sysfs: cannot create duplicate filename '/devices/virtual/bdi/259:1' Hardware name: HP ProLiant DL580 Gen8, BIOS P79 05/06/2015 0000000000000286 0000000002c04ad5 ffff88006f24f970 ffffffff8134caec ffff88006f24f9c0 0000000000000000 ffff88006f24f9b0 ffffffff8108c351 0000001f0000000c ffff88105d236000 ffff88105d1031e0 ffff8800357427f8 Call Trace: [] dump_stack+0x63/0x87 [] __warn+0xd1/0xf0 [] warn_slowpath_fmt+0x5f/0x80 [] sysfs_warn_dup+0x64/0x80 [] sysfs_create_dir_ns+0x7e/0x90 [] kobject_add_internal+0xaa/0x320 [] ? vsnprintf+0x34e/0x4d0 [] kobject_add+0x75/0xd0 [] ? mutex_lock+0x12/0x2f [] device_add+0x125/0x610 [] device_create_groups_vargs+0xd8/0x100 [] device_create_vargs+0x1c/0x20 [] bdi_register+0x8c/0x180 [] bdi_register_dev+0x27/0x30 [] add_disk+0x175/0x4a0 Cc: Reported-by: Yi Zhang Tested-by: Yi Zhang Signed-off-by: Dan Williams Fixed up missing 0 return in bdi_register_owner(). Signed-off-by: Jens Axboe (cherrypicked from commit df08c32ce3be5be138c1dbfcba203314a3a7cd6f) Conflicts: mm/backing-dev.c include/linux/backing-dev.h The patch breaks KABI as-is because of the introduction of a new "owner" field in struct backing_dev_info. To work around that, use the UEK_KABI_EXTEND() macro to wrap the "owner" filed with ifdef GENKSYMS. Signed-off-by: Ashish Samant Reviewed-by: Martin K. Petersen Signed-off-by: Brian Maly --- diff --git a/block/genhd.c b/block/genhd.c index 60db28598670..24fcb43e61ee 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -612,7 +612,7 @@ void add_disk(struct gendisk *disk) /* Register BDI before referencing it from bdev */ bdi = &disk->queue->backing_dev_info; - bdi_register_dev(bdi, disk_devt(disk)); + bdi_register_owner(bdi, disk_to_dev(disk)); blk_register_region(disk_devt(disk), disk->minors, NULL, exact_match, exact_lock, disk); diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index d87d8eced064..3cf1f7409a7c 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -19,6 +19,7 @@ #include #include #include +#include struct page; struct device; @@ -105,6 +106,7 @@ struct backing_dev_info { struct dentry *debug_dir; struct dentry *debug_stats; #endif + UEK_KABI_EXTEND(struct device *owner) }; struct backing_dev_info *inode_to_bdi(struct inode *inode); @@ -116,6 +118,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 __must_check bdi_setup_and_register(struct backing_dev_info *, char *); void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages, enum wb_reason reason); diff --git a/mm/backing-dev.c b/mm/backing-dev.c index 000e7b3b9896..d9ec4976f5f1 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -332,6 +332,20 @@ 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 rc; + + rc = bdi_register(bdi, NULL, "%u:%u", MAJOR(owner->devt), + MINOR(owner->devt)); + if (rc) + return rc; + bdi->owner = owner; + get_device(owner); + return 0; +} +EXPORT_SYMBOL(bdi_register_owner); + /* * Remove bdi from the global list and shutdown any threads we have running */ @@ -437,6 +451,11 @@ void bdi_destroy(struct backing_dev_info *bdi) bdi->dev = NULL; } + if (bdi->owner) { + put_device(bdi->owner); + bdi->owner = NULL; + } + for (i = 0; i < NR_BDI_STAT_ITEMS; i++) percpu_counter_destroy(&bdi->bdi_stat[i]); fprop_local_destroy_percpu(&bdi->completions);