diff options
Diffstat (limited to 'fs/block_dev.c')
| -rw-r--r-- | fs/block_dev.c | 102 | 
1 files changed, 87 insertions, 15 deletions
| diff --git a/fs/block_dev.c b/fs/block_dev.c index 9633a490dab0..37534573960b 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c @@ -739,7 +739,7 @@ static int bd_claim_by_kobject(struct block_device *bdev, void *holder,  	if (!bo)  		return -ENOMEM; -	mutex_lock(&bdev->bd_mutex); +	mutex_lock_nested(&bdev->bd_mutex, BD_MUTEX_PARTITION);  	res = bd_claim(bdev, holder);  	if (res || !add_bd_holder(bdev, bo))  		free_bd_holder(bo); @@ -764,7 +764,7 @@ static void bd_release_from_kobject(struct block_device *bdev,  	if (!kobj)  		return; -	mutex_lock(&bdev->bd_mutex); +	mutex_lock_nested(&bdev->bd_mutex, BD_MUTEX_PARTITION);  	bd_release(bdev);  	if ((bo = del_bd_holder(bdev, kobj)))  		free_bd_holder(bo); @@ -822,6 +822,22 @@ struct block_device *open_by_devnum(dev_t dev, unsigned mode)  EXPORT_SYMBOL(open_by_devnum); +static int +blkdev_get_partition(struct block_device *bdev, mode_t mode, unsigned flags); + +struct block_device *open_partition_by_devnum(dev_t dev, unsigned mode) +{ +	struct block_device *bdev = bdget(dev); +	int err = -ENOMEM; +	int flags = mode & FMODE_WRITE ? O_RDWR : O_RDONLY; +	if (bdev) +		err = blkdev_get_partition(bdev, mode, flags); +	return err ? ERR_PTR(err) : bdev; +} + +EXPORT_SYMBOL(open_partition_by_devnum); + +  /*   * This routine checks whether a removable media has been changed,   * and invalidates all buffer-cache-entries in that case. This @@ -868,7 +884,11 @@ void bd_set_size(struct block_device *bdev, loff_t size)  }  EXPORT_SYMBOL(bd_set_size); -static int do_open(struct block_device *bdev, struct file *file) +static int +blkdev_get_whole(struct block_device *bdev, mode_t mode, unsigned flags); + +static int +do_open(struct block_device *bdev, struct file *file, unsigned int subclass)  {  	struct module *owner = NULL;  	struct gendisk *disk; @@ -885,7 +905,8 @@ static int do_open(struct block_device *bdev, struct file *file)  	}  	owner = disk->fops->owner; -	mutex_lock(&bdev->bd_mutex); +	mutex_lock_nested(&bdev->bd_mutex, subclass); +  	if (!bdev->bd_openers) {  		bdev->bd_disk = disk;  		bdev->bd_contains = bdev; @@ -912,11 +933,11 @@ static int do_open(struct block_device *bdev, struct file *file)  			ret = -ENOMEM;  			if (!whole)  				goto out_first; -			ret = blkdev_get(whole, file->f_mode, file->f_flags); +			ret = blkdev_get_whole(whole, file->f_mode, file->f_flags);  			if (ret)  				goto out_first;  			bdev->bd_contains = whole; -			mutex_lock(&whole->bd_mutex); +			mutex_lock_nested(&whole->bd_mutex, BD_MUTEX_WHOLE);  			whole->bd_part_count++;  			p = disk->part[part - 1];  			bdev->bd_inode->i_data.backing_dev_info = @@ -944,7 +965,8 @@ static int do_open(struct block_device *bdev, struct file *file)  			if (bdev->bd_invalidated)  				rescan_partitions(bdev->bd_disk, bdev);  		} else { -			mutex_lock(&bdev->bd_contains->bd_mutex); +			mutex_lock_nested(&bdev->bd_contains->bd_mutex, +					  BD_MUTEX_PARTITION);  			bdev->bd_contains->bd_part_count++;  			mutex_unlock(&bdev->bd_contains->bd_mutex);  		} @@ -985,11 +1007,49 @@ int blkdev_get(struct block_device *bdev, mode_t mode, unsigned flags)  	fake_file.f_dentry = &fake_dentry;  	fake_dentry.d_inode = bdev->bd_inode; -	return do_open(bdev, &fake_file); +	return do_open(bdev, &fake_file, BD_MUTEX_NORMAL);  }  EXPORT_SYMBOL(blkdev_get); +static int +blkdev_get_whole(struct block_device *bdev, mode_t mode, unsigned flags) +{ +	/* +	 * This crockload is due to bad choice of ->open() type. +	 * It will go away. +	 * For now, block device ->open() routine must _not_ +	 * examine anything in 'inode' argument except ->i_rdev. +	 */ +	struct file fake_file = {}; +	struct dentry fake_dentry = {}; +	fake_file.f_mode = mode; +	fake_file.f_flags = flags; +	fake_file.f_dentry = &fake_dentry; +	fake_dentry.d_inode = bdev->bd_inode; + +	return do_open(bdev, &fake_file, BD_MUTEX_WHOLE); +} + +static int +blkdev_get_partition(struct block_device *bdev, mode_t mode, unsigned flags) +{ +	/* +	 * This crockload is due to bad choice of ->open() type. +	 * It will go away. +	 * For now, block device ->open() routine must _not_ +	 * examine anything in 'inode' argument except ->i_rdev. +	 */ +	struct file fake_file = {}; +	struct dentry fake_dentry = {}; +	fake_file.f_mode = mode; +	fake_file.f_flags = flags; +	fake_file.f_dentry = &fake_dentry; +	fake_dentry.d_inode = bdev->bd_inode; + +	return do_open(bdev, &fake_file, BD_MUTEX_PARTITION); +} +  static int blkdev_open(struct inode * inode, struct file * filp)  {  	struct block_device *bdev; @@ -1005,7 +1065,7 @@ static int blkdev_open(struct inode * inode, struct file * filp)  	bdev = bd_acquire(inode); -	res = do_open(bdev, filp); +	res = do_open(bdev, filp, BD_MUTEX_NORMAL);  	if (res)  		return res; @@ -1019,13 +1079,13 @@ static int blkdev_open(struct inode * inode, struct file * filp)  	return res;  } -int blkdev_put(struct block_device *bdev) +static int __blkdev_put(struct block_device *bdev, unsigned int subclass)  {  	int ret = 0;  	struct inode *bd_inode = bdev->bd_inode;  	struct gendisk *disk = bdev->bd_disk; -	mutex_lock(&bdev->bd_mutex); +	mutex_lock_nested(&bdev->bd_mutex, subclass);  	lock_kernel();  	if (!--bdev->bd_openers) {  		sync_blockdev(bdev); @@ -1035,7 +1095,8 @@ int blkdev_put(struct block_device *bdev)  		if (disk->fops->release)  			ret = disk->fops->release(bd_inode, NULL);  	} else { -		mutex_lock(&bdev->bd_contains->bd_mutex); +		mutex_lock_nested(&bdev->bd_contains->bd_mutex, +				  subclass + 1);  		bdev->bd_contains->bd_part_count--;  		mutex_unlock(&bdev->bd_contains->bd_mutex);  	} @@ -1051,9 +1112,8 @@ int blkdev_put(struct block_device *bdev)  		}  		bdev->bd_disk = NULL;  		bdev->bd_inode->i_data.backing_dev_info = &default_backing_dev_info; -		if (bdev != bdev->bd_contains) { -			blkdev_put(bdev->bd_contains); -		} +		if (bdev != bdev->bd_contains) +			__blkdev_put(bdev->bd_contains, subclass + 1);  		bdev->bd_contains = NULL;  	}  	unlock_kernel(); @@ -1062,8 +1122,20 @@ int blkdev_put(struct block_device *bdev)  	return ret;  } +int blkdev_put(struct block_device *bdev) +{ +	return __blkdev_put(bdev, BD_MUTEX_NORMAL); +} +  EXPORT_SYMBOL(blkdev_put); +int blkdev_put_partition(struct block_device *bdev) +{ +	return __blkdev_put(bdev, BD_MUTEX_PARTITION); +} + +EXPORT_SYMBOL(blkdev_put_partition); +  static int blkdev_close(struct inode * inode, struct file * filp)  {  	struct block_device *bdev = I_BDEV(filp->f_mapping->host); | 
