forked from mirrors/linux
		
	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
					
				
					 2 changed files with 15 additions and 11 deletions
				
			
		|  | @ -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 a new issue
	
	 Keith Busch
						Keith Busch