int truncate_bdev_range(struct block_device *bdev, fmode_t mode,
loff_t lstart, loff_t lend)
{
- struct block_device *claimed_bdev = NULL;
- int err;
-
/*
* If we don't hold exclusive handle for the device, upgrade to it
* while we discard the buffer cache to avoid discarding buffers
* under live filesystem.
*/
if (!(mode & FMODE_EXCL)) {
- claimed_bdev = bdev->bd_contains;
- err = bd_prepare_to_claim(bdev, claimed_bdev,
- truncate_bdev_range);
+ int err = bd_prepare_to_claim(bdev, truncate_bdev_range);
if (err)
return err;
}
+
truncate_inode_pages_range(bdev->bd_inode->i_mapping, lstart, lend);
- if (claimed_bdev)
- bd_abort_claiming(bdev, claimed_bdev, truncate_bdev_range);
+ if (!(mode & FMODE_EXCL))
+ bd_abort_claiming(bdev, truncate_bdev_range);
return 0;
}
EXPORT_SYMBOL(truncate_bdev_range);
blockdev_superblock = bd_mnt->mnt_sb; /* For writeback */
}
-static struct block_device *bdget(dev_t dev)
+struct block_device *bdev_alloc(struct gendisk *disk, u8 partno)
{
struct block_device *bdev;
struct inode *inode;
- inode = iget_locked(blockdev_superblock, dev);
+ inode = new_inode(blockdev_superblock);
if (!inode)
return NULL;
-
- bdev = &BDEV_I(inode)->bdev;
-
- if (inode->i_state & I_NEW) {
- spin_lock_init(&bdev->bd_size_lock);
- bdev->bd_contains = NULL;
- bdev->bd_super = NULL;
- bdev->bd_inode = inode;
- bdev->bd_part_count = 0;
- bdev->bd_dev = dev;
- inode->i_mode = S_IFBLK;
- inode->i_rdev = dev;
- inode->i_data.a_ops = &def_blk_aops;
- mapping_set_gfp_mask(&inode->i_data, GFP_USER);
- unlock_new_inode(inode);
- }
+ inode->i_mode = S_IFBLK;
+ inode->i_rdev = 0;
+ inode->i_data.a_ops = &def_blk_aops;
+ mapping_set_gfp_mask(&inode->i_data, GFP_USER);
+
+ bdev = I_BDEV(inode);
+ spin_lock_init(&bdev->bd_size_lock);
+ bdev->bd_disk = disk;
+ bdev->bd_partno = partno;
+ bdev->bd_super = NULL;
+ bdev->bd_inode = inode;
+ bdev->bd_part_count = 0;
return bdev;
}
+void bdev_add(struct block_device *bdev, dev_t dev)
+{
+ bdev->bd_dev = dev;
+ bdev->bd_inode->i_rdev = dev;
+ bdev->bd_inode->i_ino = dev;
+ insert_inode_hash(bdev->bd_inode);
+}
+
+static struct block_device *bdget(dev_t dev)
+{
+ struct inode *inode;
+
+ inode = ilookup(blockdev_superblock, dev);
+ if (!inode)
+ return NULL;
+ return &BDEV_I(inode)->bdev;
+}
+
/**
* bdgrab -- Grab a reference to an already referenced block device
* @bdev: Block device to grab a reference to.
/**
* bd_prepare_to_claim - claim a block device
* @bdev: block device of interest
- * @whole: the whole device containing @bdev, may equal @bdev
* @holder: holder trying to claim @bdev
*
* Claim @bdev. This function fails if @bdev is already claimed by another
* RETURNS:
* 0 if @bdev can be claimed, -EBUSY otherwise.
*/
-int bd_prepare_to_claim(struct block_device *bdev, struct block_device *whole,
- void *holder)
+int bd_prepare_to_claim(struct block_device *bdev, void *holder)
{
+ struct block_device *whole = bdev_whole(bdev);
+
+ if (WARN_ON_ONCE(!holder))
+ return -EINVAL;
retry:
spin_lock(&bdev_lock);
/* if someone else claimed, fail */
}
EXPORT_SYMBOL_GPL(bd_prepare_to_claim); /* only for the loop driver */
-static struct gendisk *bdev_get_gendisk(struct block_device *bdev, int *partno)
-{
- struct gendisk *disk = get_gendisk(bdev->bd_dev, partno);
-
- if (!disk)
- return NULL;
- /*
- * Now that we hold gendisk reference we make sure bdev we looked up is
- * not stale. If it is, it means device got removed and created before
- * we looked up gendisk and we fail open in such case. Associating
- * unhashed bdev with newly created gendisk could lead to two bdevs
- * (and thus two independent caches) being associated with one device
- * which is bad.
- */
- if (inode_unhashed(bdev->bd_inode)) {
- put_disk_and_module(disk);
- return NULL;
- }
- return disk;
-}
-
static void bd_clear_claiming(struct block_device *whole, void *holder)
{
lockdep_assert_held(&bdev_lock);
/**
* bd_finish_claiming - finish claiming of a block device
* @bdev: block device of interest
- * @whole: whole block device
* @holder: holder that has claimed @bdev
*
* Finish exclusive open of a block device. Mark the device as exlusively
* open by the holder and wake up all waiters for exclusive open to finish.
*/
-static void bd_finish_claiming(struct block_device *bdev,
- struct block_device *whole, void *holder)
+static void bd_finish_claiming(struct block_device *bdev, void *holder)
{
+ struct block_device *whole = bdev_whole(bdev);
+
spin_lock(&bdev_lock);
BUG_ON(!bd_may_claim(bdev, whole, holder));
/*
* also used when exclusive open is not actually desired and we just needed
* to block other exclusive openers for a while.
*/
-void bd_abort_claiming(struct block_device *bdev, struct block_device *whole,
- void *holder)
+void bd_abort_claiming(struct block_device *bdev, void *holder)
{
spin_lock(&bdev_lock);
- bd_clear_claiming(whole, holder);
+ bd_clear_claiming(bdev_whole(bdev), holder);
spin_unlock(&bdev_lock);
}
EXPORT_SYMBOL(bd_abort_claiming);
* mutex_lock(part->bd_mutex)
* mutex_lock_nested(whole->bd_mutex, 1)
*/
-static int __blkdev_get(struct block_device *bdev, struct gendisk *disk,
- int partno, fmode_t mode)
+static int __blkdev_get(struct block_device *bdev, fmode_t mode)
{
+ struct gendisk *disk = bdev->bd_disk;
int ret;
if (!bdev->bd_openers) {
- bdev->bd_disk = disk;
- bdev->bd_contains = bdev;
- bdev->bd_partno = partno;
-
- if (!partno) {
+ if (!bdev_is_partition(bdev)) {
ret = -ENXIO;
- bdev->bd_part = disk_get_part(disk, partno);
+ bdev->bd_part = disk_get_part(disk, 0);
if (!bdev->bd_part)
goto out_clear;
struct block_device *whole = bdget_disk(disk, 0);
mutex_lock_nested(&whole->bd_mutex, 1);
- ret = __blkdev_get(whole, disk, 0, mode);
+ ret = __blkdev_get(whole, mode);
if (ret) {
mutex_unlock(&whole->bd_mutex);
bdput(whole);
whole->bd_part_count++;
mutex_unlock(&whole->bd_mutex);
- bdev->bd_contains = whole;
- bdev->bd_part = disk_get_part(disk, partno);
+ bdev->bd_part = disk_get_part(disk, bdev->bd_partno);
if (!(disk->flags & GENHD_FL_UP) ||
!bdev->bd_part || !bdev->bd_part->nr_sects) {
__blkdev_put(whole, mode, 1);
if (bdev->bd_bdi == &noop_backing_dev_info)
bdev->bd_bdi = bdi_get(disk->queue->backing_dev_info);
} else {
- if (bdev->bd_contains == bdev) {
+ if (!bdev_is_partition(bdev)) {
ret = 0;
if (bdev->bd_disk->fops->open)
ret = bdev->bd_disk->fops->open(bdev, mode);
out_clear:
disk_put_part(bdev->bd_part);
- bdev->bd_disk = NULL;
bdev->bd_part = NULL;
- bdev->bd_contains = NULL;
return ret;
}
+struct block_device *blkdev_get_no_open(dev_t dev)
+{
+ struct block_device *bdev;
+ struct gendisk *disk;
+
+ down_read(&bdev_lookup_sem);
+ bdev = bdget(dev);
+ if (!bdev) {
+ up_read(&bdev_lookup_sem);
+ blk_request_module(dev);
+ down_read(&bdev_lookup_sem);
+
+ bdev = bdget(dev);
+ if (!bdev)
+ goto unlock;
+ }
+
+ disk = bdev->bd_disk;
+ if (!kobject_get_unless_zero(&disk_to_dev(disk)->kobj))
+ goto bdput;
+ if ((disk->flags & (GENHD_FL_UP | GENHD_FL_HIDDEN)) != GENHD_FL_UP)
+ goto put_disk;
+ if (!try_module_get(bdev->bd_disk->fops->owner))
+ goto put_disk;
+ up_read(&bdev_lookup_sem);
+ return bdev;
+put_disk:
+ put_disk(disk);
+bdput:
+ bdput(bdev);
+unlock:
+ up_read(&bdev_lookup_sem);
+ return NULL;
+}
+
+void blkdev_put_no_open(struct block_device *bdev)
+{
+ module_put(bdev->bd_disk->fops->owner);
+ put_disk(bdev->bd_disk);
+ bdput(bdev);
+}
+
/**
* blkdev_get_by_dev - open a block device by device number
* @dev: device number of block device to open
*/
struct block_device *blkdev_get_by_dev(dev_t dev, fmode_t mode, void *holder)
{
- struct block_device *claiming;
bool unblock_events = true;
struct block_device *bdev;
struct gendisk *disk;
- int partno;
int ret;
ret = devcgroup_check_permission(DEVCG_DEV_BLOCK,
if (ret)
return ERR_PTR(ret);
- bdev = bdget(dev);
- if (!bdev)
- return ERR_PTR(-ENOMEM);
-
/*
* If we lost a race with 'disk' being deleted, try again. See md.c.
*/
retry:
- ret = -ENXIO;
- disk = bdev_get_gendisk(bdev, &partno);
- if (!disk)
- goto bdput;
+ bdev = blkdev_get_no_open(dev);
+ if (!bdev)
+ return ERR_PTR(-ENXIO);
+ disk = bdev->bd_disk;
if (mode & FMODE_EXCL) {
- WARN_ON_ONCE(!holder);
-
- ret = -ENOMEM;
- claiming = bdget_disk(disk, 0);
- if (!claiming)
- goto put_disk;
- ret = bd_prepare_to_claim(bdev, claiming, holder);
+ ret = bd_prepare_to_claim(bdev, holder);
if (ret)
- goto put_claiming;
+ goto put_blkdev;
}
disk_block_events(disk);
mutex_lock(&bdev->bd_mutex);
- ret =__blkdev_get(bdev, disk, partno, mode);
- if (!(mode & FMODE_EXCL)) {
- ; /* nothing to do here */
- } else if (ret) {
- bd_abort_claiming(bdev, claiming, holder);
- } else {
- bd_finish_claiming(bdev, claiming, holder);
+ ret =__blkdev_get(bdev, mode);
+ if (ret)
+ goto abort_claiming;
+ if (mode & FMODE_EXCL) {
+ bd_finish_claiming(bdev, holder);
/*
* Block event polling for write claims if requested. Any write
if (unblock_events)
disk_unblock_events(disk);
+ return bdev;
-put_claiming:
+abort_claiming:
if (mode & FMODE_EXCL)
- bdput(claiming);
-put_disk:
- if (ret)
- put_disk_and_module(disk);
+ bd_abort_claiming(bdev, holder);
+ mutex_unlock(&bdev->bd_mutex);
+ disk_unblock_events(disk);
+put_blkdev:
+ blkdev_put_no_open(bdev);
if (ret == -ERESTARTSYS)
goto retry;
-bdput:
- if (ret) {
- bdput(bdev);
- return ERR_PTR(ret);
- }
- return bdev;
+ return ERR_PTR(ret);
}
EXPORT_SYMBOL(blkdev_get_by_dev);
disk_put_part(bdev->bd_part);
bdev->bd_part = NULL;
- bdev->bd_disk = NULL;
if (bdev_is_partition(bdev))
- victim = bdev->bd_contains;
- bdev->bd_contains = NULL;
+ victim = bdev_whole(bdev);
} else {
if (!bdev_is_partition(bdev) && disk->fops->release)
disk->fops->release(disk, mode);
mutex_lock(&bdev->bd_mutex);
if (mode & FMODE_EXCL) {
+ struct block_device *whole = bdev_whole(bdev);
bool bdev_free;
/*
spin_lock(&bdev_lock);
WARN_ON_ONCE(--bdev->bd_holders < 0);
- WARN_ON_ONCE(--bdev->bd_contains->bd_holders < 0);
+ WARN_ON_ONCE(--whole->bd_holders < 0);
- /* bd_contains might point to self, check in a separate step */
if ((bdev_free = !bdev->bd_holders))
bdev->bd_holder = NULL;
- if (!bdev->bd_contains->bd_holders)
- bdev->bd_contains->bd_holder = NULL;
+ if (!whole->bd_holders)
+ whole->bd_holder = NULL;
spin_unlock(&bdev_lock);
* from userland - e.g. eject(1).
*/
disk_flush_events(disk, DISK_EVENT_MEDIA_CHANGE);
-
mutex_unlock(&bdev->bd_mutex);
__blkdev_put(bdev, mode, 0);
- bdput(bdev);
- put_disk_and_module(disk);
+ blkdev_put_no_open(bdev);
}
EXPORT_SYMBOL(blkdev_put);