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>
This commit is contained in:
parent
5676e7b6db
commit
2da78092dd
@ -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;
|
||||||
@ -420,9 +420,13 @@ int blk_alloc_devt(struct hd_struct *part, dev_t *devt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* allocate ext devt */
|
/* allocate ext devt */
|
||||||
mutex_lock(&ext_devt_mutex);
|
idr_preload(GFP_KERNEL);
|
||||||
idx = idr_alloc(&ext_devt_idr, part, 0, NR_EXT_DEVT, GFP_KERNEL);
|
|
||||||
mutex_unlock(&ext_devt_mutex);
|
spin_lock(&ext_devt_lock);
|
||||||
|
idx = idr_alloc(&ext_devt_idr, part, 0, NR_EXT_DEVT, GFP_NOWAIT);
|
||||||
|
spin_unlock(&ext_devt_lock);
|
||||||
|
|
||||||
|
idr_preload_end();
|
||||||
if (idx < 0)
|
if (idx < 0)
|
||||||
return idx == -ENOSPC ? -EBUSY : idx;
|
return idx == -ENOSPC ? -EBUSY : idx;
|
||||||
|
|
||||||
@ -447,9 +451,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -665,7 +669,6 @@ void del_gendisk(struct gendisk *disk)
|
|||||||
sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk)));
|
sysfs_remove_link(block_depr, dev_name(disk_to_dev(disk)));
|
||||||
pm_runtime_set_memalloc_noio(disk_to_dev(disk), false);
|
pm_runtime_set_memalloc_noio(disk_to_dev(disk), false);
|
||||||
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);
|
||||||
|
|
||||||
@ -690,13 +693,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;
|
||||||
@ -1098,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);
|
||||||
|
@ -211,6 +211,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);
|
||||||
@ -253,7 +254,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);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user