Commit a288cafe authored by Keith Busch's avatar Keith Busch Committed by Ben Hutchings

block: Fix dev_t minor allocation lifetime

commit 2da78092 upstream.

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: default avatarKeith Busch <keith.busch@intel.com>
Signed-off-by: default avatarJens Axboe <axboe@fb.com>
[bwh: Backported to 3.2:
 - Adjust filename
 - idr insertion API is different, and blk_alloc_devt() is preallocating
   a node in a different way]
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
parent 1cc8e216
...@@ -28,10 +28,10 @@ struct kobject *block_depr; ...@@ -28,10 +28,10 @@ struct kobject *block_depr;
/* for extended dynamic devt allocation, currently only one major is used */ /* for extended dynamic devt allocation, currently only one major is used */
#define NR_EXT_DEVT (1 << MINORBITS) #define NR_EXT_DEVT (1 << MINORBITS)
/* For extended devt allocation. ext_devt_mutex prevents look up /* For extended devt allocation. ext_devt_lock prevents look up
* results from going away underneath its user. * results from going away underneath its user.
*/ */
static DEFINE_MUTEX(ext_devt_mutex); static DEFINE_SPINLOCK(ext_devt_lock);
static DEFINE_IDR(ext_devt_idr); static DEFINE_IDR(ext_devt_idr);
static struct device_type disk_type; static struct device_type disk_type;
...@@ -421,13 +421,13 @@ int blk_alloc_devt(struct hd_struct *part, dev_t *devt) ...@@ -421,13 +421,13 @@ int blk_alloc_devt(struct hd_struct *part, dev_t *devt)
do { do {
if (!idr_pre_get(&ext_devt_idr, GFP_KERNEL)) if (!idr_pre_get(&ext_devt_idr, GFP_KERNEL))
return -ENOMEM; return -ENOMEM;
mutex_lock(&ext_devt_mutex); spin_lock(&ext_devt_lock);
rc = idr_get_new(&ext_devt_idr, part, &idx); rc = idr_get_new(&ext_devt_idr, part, &idx);
if (!rc && idx >= NR_EXT_DEVT) { if (!rc && idx >= NR_EXT_DEVT) {
idr_remove(&ext_devt_idr, idx); idr_remove(&ext_devt_idr, idx);
rc = -EBUSY; rc = -EBUSY;
} }
mutex_unlock(&ext_devt_mutex); spin_unlock(&ext_devt_lock);
} while (rc == -EAGAIN); } while (rc == -EAGAIN);
if (rc) if (rc)
...@@ -454,9 +454,9 @@ void blk_free_devt(dev_t devt) ...@@ -454,9 +454,9 @@ void blk_free_devt(dev_t devt)
return; return;
if (MAJOR(devt) == BLOCK_EXT_MAJOR) { if (MAJOR(devt) == BLOCK_EXT_MAJOR) {
mutex_lock(&ext_devt_mutex); spin_lock(&ext_devt_lock);
idr_remove(&ext_devt_idr, blk_mangle_minor(MINOR(devt))); idr_remove(&ext_devt_idr, blk_mangle_minor(MINOR(devt)));
mutex_unlock(&ext_devt_mutex); spin_unlock(&ext_devt_lock);
} }
} }
...@@ -663,7 +663,6 @@ void del_gendisk(struct gendisk *disk) ...@@ -663,7 +663,6 @@ void del_gendisk(struct gendisk *disk)
if (!sysfs_deprecated) if (!sysfs_deprecated)
sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk))); sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk)));
device_del(disk_to_dev(disk)); device_del(disk_to_dev(disk));
blk_free_devt(disk_to_dev(disk)->devt);
} }
EXPORT_SYMBOL(del_gendisk); EXPORT_SYMBOL(del_gendisk);
...@@ -688,13 +687,13 @@ struct gendisk *get_gendisk(dev_t devt, int *partno) ...@@ -688,13 +687,13 @@ struct gendisk *get_gendisk(dev_t devt, int *partno)
} else { } else {
struct hd_struct *part; struct hd_struct *part;
mutex_lock(&ext_devt_mutex); spin_lock(&ext_devt_lock);
part = idr_find(&ext_devt_idr, blk_mangle_minor(MINOR(devt))); part = idr_find(&ext_devt_idr, blk_mangle_minor(MINOR(devt)));
if (part && get_disk(part_to_disk(part))) { if (part && get_disk(part_to_disk(part))) {
*partno = part->partno; *partno = part->partno;
disk = part_to_disk(part); disk = part_to_disk(part);
} }
mutex_unlock(&ext_devt_mutex); spin_unlock(&ext_devt_lock);
} }
return disk; return disk;
...@@ -1102,6 +1101,7 @@ static void disk_release(struct device *dev) ...@@ -1102,6 +1101,7 @@ static void disk_release(struct device *dev)
{ {
struct gendisk *disk = dev_to_disk(dev); struct gendisk *disk = dev_to_disk(dev);
blk_free_devt(dev->devt);
disk_release_events(disk); disk_release_events(disk);
kfree(disk->random); kfree(disk->random);
disk_replace_part_tbl(disk, NULL); disk_replace_part_tbl(disk, NULL);
......
...@@ -361,6 +361,7 @@ static const struct attribute_group *part_attr_groups[] = { ...@@ -361,6 +361,7 @@ static const struct attribute_group *part_attr_groups[] = {
static void part_release(struct device *dev) static void part_release(struct device *dev)
{ {
struct hd_struct *p = dev_to_part(dev); struct hd_struct *p = dev_to_part(dev);
blk_free_devt(dev->devt);
free_part_stats(p); free_part_stats(p);
free_part_info(p); free_part_info(p);
kfree(p); kfree(p);
...@@ -403,7 +404,6 @@ void delete_partition(struct gendisk *disk, int partno) ...@@ -403,7 +404,6 @@ void delete_partition(struct gendisk *disk, int partno)
rcu_assign_pointer(ptbl->last_lookup, NULL); rcu_assign_pointer(ptbl->last_lookup, NULL);
kobject_put(part->holder_dir); kobject_put(part->holder_dir);
device_del(part_to_dev(part)); device_del(part_to_dev(part));
blk_free_devt(part_devt(part));
hd_struct_put(part); hd_struct_put(part);
} }
......
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