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