Commit 8adf1a71 authored by Dan Williams's avatar Dan Williams Committed by Kamal Mostafa

block: fix bdi vs gendisk lifetime mismatch

BugLink: http://bugs.launchpad.net/bugs/1615620

commit df08c32c upstream.

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:
  [<ffffffff8134caec>] dump_stack+0x63/0x87
  [<ffffffff8108c351>] __warn+0xd1/0xf0
  [<ffffffff8108c3cf>] warn_slowpath_fmt+0x5f/0x80
  [<ffffffff812a0d34>] sysfs_warn_dup+0x64/0x80
  [<ffffffff812a0e1e>] sysfs_create_dir_ns+0x7e/0x90
  [<ffffffff8134faaa>] kobject_add_internal+0xaa/0x320
  [<ffffffff81358d4e>] ? vsnprintf+0x34e/0x4d0
  [<ffffffff8134ff55>] kobject_add+0x75/0xd0
  [<ffffffff816e66b2>] ? mutex_lock+0x12/0x2f
  [<ffffffff8148b0a5>] device_add+0x125/0x610
  [<ffffffff8148b788>] device_create_groups_vargs+0xd8/0x100
  [<ffffffff8148b7cc>] device_create_vargs+0x1c/0x20
  [<ffffffff811b775c>] bdi_register+0x8c/0x180
  [<ffffffff811b7877>] bdi_register_dev+0x27/0x30
  [<ffffffff813317f5>] add_disk+0x175/0x4a0
Reported-by: default avatarYi Zhang <yizhan@redhat.com>
Tested-by: default avatarYi Zhang <yizhan@redhat.com>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>

Fixed up missing 0 return in bdi_register_owner().
Signed-off-by: default avatarJens Axboe <axboe@fb.com>
Signed-off-by: default avatarTim Gardner <tim.gardner@canonical.com>
Signed-off-by: default avatarKamal Mostafa <kamal@canonical.com>
parent 9145ae89
...@@ -612,7 +612,7 @@ void add_disk(struct gendisk *disk) ...@@ -612,7 +612,7 @@ void add_disk(struct gendisk *disk)
/* Register BDI before referencing it from bdev */ /* Register BDI before referencing it from bdev */
bdi = &disk->queue->backing_dev_info; 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, blk_register_region(disk_devt(disk), disk->minors, NULL,
exact_match, exact_lock, disk); exact_match, exact_lock, disk);
......
...@@ -163,6 +163,7 @@ struct backing_dev_info { ...@@ -163,6 +163,7 @@ struct backing_dev_info {
wait_queue_head_t wb_waitq; wait_queue_head_t wb_waitq;
struct device *dev; struct device *dev;
struct device *owner;
struct timer_list laptop_mode_wb_timer; struct timer_list laptop_mode_wb_timer;
......
...@@ -24,6 +24,7 @@ __printf(3, 4) ...@@ -24,6 +24,7 @@ __printf(3, 4)
int bdi_register(struct backing_dev_info *bdi, struct device *parent, int bdi_register(struct backing_dev_info *bdi, struct device *parent,
const char *fmt, ...); const char *fmt, ...);
int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev); int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev);
int bdi_register_owner(struct backing_dev_info *bdi, struct device *owner);
void bdi_unregister(struct backing_dev_info *bdi); void bdi_unregister(struct backing_dev_info *bdi);
int __must_check bdi_setup_and_register(struct backing_dev_info *, char *); int __must_check bdi_setup_and_register(struct backing_dev_info *, char *);
......
...@@ -825,6 +825,20 @@ int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev) ...@@ -825,6 +825,20 @@ int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev)
} }
EXPORT_SYMBOL(bdi_register_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 bdi_list, and ensure that it is no longer visible * Remove bdi from bdi_list, and ensure that it is no longer visible
*/ */
...@@ -849,6 +863,11 @@ void bdi_unregister(struct backing_dev_info *bdi) ...@@ -849,6 +863,11 @@ void bdi_unregister(struct backing_dev_info *bdi)
device_unregister(bdi->dev); device_unregister(bdi->dev);
bdi->dev = NULL; bdi->dev = NULL;
} }
if (bdi->owner) {
put_device(bdi->owner);
bdi->owner = NULL;
}
} }
void bdi_exit(struct backing_dev_info *bdi) void bdi_exit(struct backing_dev_info *bdi)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment