diff options
author | Tomas Henzl <thenzl@redhat.com> | 2013-02-27 20:03:32 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-27 22:10:12 -0500 |
commit | 7b74e912785a11572da43292786ed07ada7e3e0c (patch) | |
tree | 58cf6c0f890c9655a6058b2179c695ca5a2825b5 | |
parent | 8c333ac2e4946a673b54f974d75397c947569c29 (diff) |
block: fix ext_devt_idr handling
While adding and removing a lot of disks disks and partitions this
sometimes shows up:
WARNING: at fs/sysfs/dir.c:512 sysfs_add_one+0xc9/0x130() (Not tainted)
Hardware name:
sysfs: cannot create duplicate filename '/dev/block/259:751'
Modules linked in: raid1 autofs4 bnx2fc cnic uio fcoe libfcoe libfc 8021q scsi_transport_fc scsi_tgt garp stp llc sunrpc cpufreq_ondemand powernow_k8 freq_table mperf ipv6 dm_mirror dm_region_hash dm_log power_meter microcode dcdbas serio_raw amd64_edac_mod edac_core edac_mce_amd i2c_piix4 i2c_core k10temp bnx2 sg ixgbe dca mdio ext4 mbcache jbd2 dm_round_robin sr_mod cdrom sd_mod crc_t10dif ata_generic pata_acpi pata_atiixp ahci mptsas mptscsih mptbase scsi_transport_sas dm_multipath dm_mod [last unloaded: scsi_wait_scan]
Pid: 44103, comm: async/16 Not tainted 2.6.32-195.el6.x86_64 #1
Call Trace:
warn_slowpath_common+0x87/0xc0
warn_slowpath_fmt+0x46/0x50
sysfs_add_one+0xc9/0x130
sysfs_do_create_link+0x12b/0x170
sysfs_create_link+0x13/0x20
device_add+0x317/0x650
idr_get_new+0x13/0x50
add_partition+0x21c/0x390
rescan_partitions+0x32b/0x470
sd_open+0x81/0x1f0 [sd_mod]
__blkdev_get+0x1b6/0x3c0
blkdev_get+0x10/0x20
register_disk+0x155/0x170
add_disk+0xa6/0x160
sd_probe_async+0x13b/0x210 [sd_mod]
add_wait_queue+0x46/0x60
async_thread+0x102/0x250
default_wake_function+0x0/0x20
async_thread+0x0/0x250
kthread+0x96/0xa0
child_rip+0xa/0x20
kthread+0x0/0xa0
child_rip+0x0/0x20
This most likely happens because dev_t is freed while the number is
still used and idr_get_new() is not protected on every use. The fix
adds a mutex where it wasn't before and moves the dev_t free function so
it is called after device del.
Signed-off-by: Tomas Henzl <thenzl@redhat.com>
Cc: Jens Axboe <axboe@kernel.dk>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | block/genhd.c | 6 | ||||
-rw-r--r-- | block/partition-generic.c | 2 |
2 files changed, 6 insertions, 2 deletions
diff --git a/block/genhd.c b/block/genhd.c index 5f73c2435fde..cd65b97e84d4 100644 --- a/block/genhd.c +++ b/block/genhd.c | |||
@@ -423,14 +423,18 @@ int blk_alloc_devt(struct hd_struct *part, dev_t *devt) | |||
423 | do { | 423 | do { |
424 | if (!idr_pre_get(&ext_devt_idr, GFP_KERNEL)) | 424 | if (!idr_pre_get(&ext_devt_idr, GFP_KERNEL)) |
425 | return -ENOMEM; | 425 | return -ENOMEM; |
426 | mutex_lock(&ext_devt_mutex); | ||
426 | rc = idr_get_new(&ext_devt_idr, part, &idx); | 427 | rc = idr_get_new(&ext_devt_idr, part, &idx); |
428 | mutex_unlock(&ext_devt_mutex); | ||
427 | } while (rc == -EAGAIN); | 429 | } while (rc == -EAGAIN); |
428 | 430 | ||
429 | if (rc) | 431 | if (rc) |
430 | return rc; | 432 | return rc; |
431 | 433 | ||
432 | if (idx > MAX_EXT_DEVT) { | 434 | if (idx > MAX_EXT_DEVT) { |
435 | mutex_lock(&ext_devt_mutex); | ||
433 | idr_remove(&ext_devt_idr, idx); | 436 | idr_remove(&ext_devt_idr, idx); |
437 | mutex_unlock(&ext_devt_mutex); | ||
434 | return -EBUSY; | 438 | return -EBUSY; |
435 | } | 439 | } |
436 | 440 | ||
@@ -655,7 +659,6 @@ void del_gendisk(struct gendisk *disk) | |||
655 | disk_part_iter_exit(&piter); | 659 | disk_part_iter_exit(&piter); |
656 | 660 | ||
657 | invalidate_partition(disk, 0); | 661 | invalidate_partition(disk, 0); |
658 | blk_free_devt(disk_to_dev(disk)->devt); | ||
659 | set_capacity(disk, 0); | 662 | set_capacity(disk, 0); |
660 | disk->flags &= ~GENHD_FL_UP; | 663 | disk->flags &= ~GENHD_FL_UP; |
661 | 664 | ||
@@ -674,6 +677,7 @@ void del_gendisk(struct gendisk *disk) | |||
674 | sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk))); | 677 | sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk))); |
675 | pm_runtime_set_memalloc_noio(disk_to_dev(disk), false); | 678 | pm_runtime_set_memalloc_noio(disk_to_dev(disk), false); |
676 | device_del(disk_to_dev(disk)); | 679 | device_del(disk_to_dev(disk)); |
680 | blk_free_devt(disk_to_dev(disk)->devt); | ||
677 | } | 681 | } |
678 | EXPORT_SYMBOL(del_gendisk); | 682 | EXPORT_SYMBOL(del_gendisk); |
679 | 683 | ||
diff --git a/block/partition-generic.c b/block/partition-generic.c index f1d14519cc04..1cb4deca1324 100644 --- a/block/partition-generic.c +++ b/block/partition-generic.c | |||
@@ -249,11 +249,11 @@ void delete_partition(struct gendisk *disk, int partno) | |||
249 | if (!part) | 249 | if (!part) |
250 | return; | 250 | return; |
251 | 251 | ||
252 | blk_free_devt(part_devt(part)); | ||
253 | rcu_assign_pointer(ptbl->part[partno], NULL); | 252 | rcu_assign_pointer(ptbl->part[partno], NULL); |
254 | rcu_assign_pointer(ptbl->last_lookup, NULL); | 253 | rcu_assign_pointer(ptbl->last_lookup, NULL); |
255 | kobject_put(part->holder_dir); | 254 | kobject_put(part->holder_dir); |
256 | device_del(part_to_dev(part)); | 255 | device_del(part_to_dev(part)); |
256 | blk_free_devt(part_devt(part)); | ||
257 | 257 | ||
258 | hd_struct_put(part); | 258 | hd_struct_put(part); |
259 | } | 259 | } |