aboutsummaryrefslogtreecommitdiffstats
path: root/block
diff options
context:
space:
mode:
authorKeith Busch <keith.busch@intel.com>2014-08-26 11:05:36 -0400
committerJens Axboe <axboe@fb.com>2014-09-03 17:01:02 -0400
commit2da78092dda13f1efd26edbbf99a567776913750 (patch)
tree9f245a022542d626c948ebfb886106ce3f7e62ae /block
parent5676e7b6db02b80eafc2e3ad316d5f2fee817ecb (diff)
block: Fix dev_t minor allocation lifetime
Releases the dev_t minor when all references are closed to prevent another device from acquiring the same major/minor. Since the partition's release may be invoked from call_rcu's soft-irq context, the ext_dev_idr's mutex had to be replaced with a spinlock so as not so sleep. Signed-off-by: Keith Busch <keith.busch@intel.com> Cc: stable@kernel.org Signed-off-by: Jens Axboe <axboe@fb.com>
Diffstat (limited to 'block')
-rw-r--r--block/genhd.c24
-rw-r--r--block/partition-generic.c2
2 files changed, 15 insertions, 11 deletions
diff --git a/block/genhd.c b/block/genhd.c
index 791f41943132..09da5e4a8e03 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -28,10 +28,10 @@ struct kobject *block_depr;
28/* for extended dynamic devt allocation, currently only one major is used */ 28/* for extended dynamic devt allocation, currently only one major is used */
29#define NR_EXT_DEVT (1 << MINORBITS) 29#define NR_EXT_DEVT (1 << MINORBITS)
30 30
31/* For extended devt allocation. ext_devt_mutex prevents look up 31/* For extended devt allocation. ext_devt_lock prevents look up
32 * results from going away underneath its user. 32 * results from going away underneath its user.
33 */ 33 */
34static DEFINE_MUTEX(ext_devt_mutex); 34static DEFINE_SPINLOCK(ext_devt_lock);
35static DEFINE_IDR(ext_devt_idr); 35static DEFINE_IDR(ext_devt_idr);
36 36
37static struct device_type disk_type; 37static struct device_type disk_type;
@@ -420,9 +420,13 @@ int blk_alloc_devt(struct hd_struct *part, dev_t *devt)
420 } 420 }
421 421
422 /* allocate ext devt */ 422 /* allocate ext devt */
423 mutex_lock(&ext_devt_mutex); 423 idr_preload(GFP_KERNEL);
424 idx = idr_alloc(&ext_devt_idr, part, 0, NR_EXT_DEVT, GFP_KERNEL); 424
425 mutex_unlock(&ext_devt_mutex); 425 spin_lock(&ext_devt_lock);
426 idx = idr_alloc(&ext_devt_idr, part, 0, NR_EXT_DEVT, GFP_NOWAIT);
427 spin_unlock(&ext_devt_lock);
428
429 idr_preload_end();
426 if (idx < 0) 430 if (idx < 0)
427 return idx == -ENOSPC ? -EBUSY : idx; 431 return idx == -ENOSPC ? -EBUSY : idx;
428 432
@@ -447,9 +451,9 @@ void blk_free_devt(dev_t devt)
447 return; 451 return;
448 452
449 if (MAJOR(devt) == BLOCK_EXT_MAJOR) { 453 if (MAJOR(devt) == BLOCK_EXT_MAJOR) {
450 mutex_lock(&ext_devt_mutex); 454 spin_lock(&ext_devt_lock);
451 idr_remove(&ext_devt_idr, blk_mangle_minor(MINOR(devt))); 455 idr_remove(&ext_devt_idr, blk_mangle_minor(MINOR(devt)));
452 mutex_unlock(&ext_devt_mutex); 456 spin_unlock(&ext_devt_lock);
453 } 457 }
454} 458}
455 459
@@ -665,7 +669,6 @@ void del_gendisk(struct gendisk *disk)
665 sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk))); 669 sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk)));
666 pm_runtime_set_memalloc_noio(disk_to_dev(disk), false); 670 pm_runtime_set_memalloc_noio(disk_to_dev(disk), false);
667 device_del(disk_to_dev(disk)); 671 device_del(disk_to_dev(disk));
668 blk_free_devt(disk_to_dev(disk)->devt);
669} 672}
670EXPORT_SYMBOL(del_gendisk); 673EXPORT_SYMBOL(del_gendisk);
671 674
@@ -690,13 +693,13 @@ struct gendisk *get_gendisk(dev_t devt, int *partno)
690 } else { 693 } else {
691 struct hd_struct *part; 694 struct hd_struct *part;
692 695
693 mutex_lock(&ext_devt_mutex); 696 spin_lock(&ext_devt_lock);
694 part = idr_find(&ext_devt_idr, blk_mangle_minor(MINOR(devt))); 697 part = idr_find(&ext_devt_idr, blk_mangle_minor(MINOR(devt)));
695 if (part && get_disk(part_to_disk(part))) { 698 if (part && get_disk(part_to_disk(part))) {
696 *partno = part->partno; 699 *partno = part->partno;
697 disk = part_to_disk(part); 700 disk = part_to_disk(part);
698 } 701 }
699 mutex_unlock(&ext_devt_mutex); 702 spin_unlock(&ext_devt_lock);
700 } 703 }
701 704
702 return disk; 705 return disk;
@@ -1098,6 +1101,7 @@ static void disk_release(struct device *dev)
1098{ 1101{
1099 struct gendisk *disk = dev_to_disk(dev); 1102 struct gendisk *disk = dev_to_disk(dev);
1100 1103
1104 blk_free_devt(dev->devt);
1101 disk_release_events(disk); 1105 disk_release_events(disk);
1102 kfree(disk->random); 1106 kfree(disk->random);
1103 disk_replace_part_tbl(disk, NULL); 1107 disk_replace_part_tbl(disk, NULL);
diff --git a/block/partition-generic.c b/block/partition-generic.c
index 789cdea05893..0d9e5f97f0a8 100644
--- a/block/partition-generic.c
+++ b/block/partition-generic.c
@@ -211,6 +211,7 @@ static const struct attribute_group *part_attr_groups[] = {
211static void part_release(struct device *dev) 211static void part_release(struct device *dev)
212{ 212{
213 struct hd_struct *p = dev_to_part(dev); 213 struct hd_struct *p = dev_to_part(dev);
214 blk_free_devt(dev->devt);
214 free_part_stats(p); 215 free_part_stats(p);
215 free_part_info(p); 216 free_part_info(p);
216 kfree(p); 217 kfree(p);
@@ -253,7 +254,6 @@ void delete_partition(struct gendisk *disk, int partno)
253 rcu_assign_pointer(ptbl->last_lookup, NULL); 254 rcu_assign_pointer(ptbl->last_lookup, NULL);
254 kobject_put(part->holder_dir); 255 kobject_put(part->holder_dir);
255 device_del(part_to_dev(part)); 256 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}