block: change the refcounting for partitions

Instead of acquiring an inode reference on open make sure partitions
always hold device model references to the disk while alive, and switch
open to grab only a device model reference to the opened block device.
If that is a partition the disk reference is transitively held by the
partition already.

Link: https://lore.kernel.org/r/20210722075402.983367-6-hch@lst.de
Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
Christoph Hellwig 2021-07-22 09:53:58 +02:00 committed by Jens Axboe
parent 0468c53234
commit 9d3b881389
2 changed files with 31 additions and 38 deletions

View File

@ -261,6 +261,7 @@ static void part_release(struct device *dev)
{
if (MAJOR(dev->devt) == BLOCK_EXT_MAJOR)
blk_free_ext_minor(MINOR(dev->devt));
put_disk(dev_to_bdev(dev)->bd_disk);
bdput(dev_to_bdev(dev));
}
@ -349,9 +350,13 @@ static struct block_device *add_partition(struct gendisk *disk, int partno,
if (xa_load(&disk->part_tbl, partno))
return ERR_PTR(-EBUSY);
/* ensure we always have a reference to the whole disk */
get_device(disk_to_dev(disk));
err = -ENOMEM;
bdev = bdev_alloc(disk, partno);
if (!bdev)
return ERR_PTR(-ENOMEM);
goto out_put_disk;
bdev->bd_start_sect = start;
bdev_set_nr_sectors(bdev, len);
@ -420,6 +425,8 @@ out_del:
device_del(pdev);
out_put:
put_device(pdev);
out_put_disk:
put_disk(disk);
return ERR_PTR(err);
}

View File

@ -921,16 +921,6 @@ void bdev_add(struct block_device *bdev, dev_t 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.
@ -1282,16 +1272,14 @@ static void blkdev_put_whole(struct block_device *bdev, fmode_t mode)
static int blkdev_get_part(struct block_device *part, fmode_t mode)
{
struct gendisk *disk = part->bd_disk;
struct block_device *whole;
int ret;
if (part->bd_openers)
goto done;
whole = bdgrab(disk->part0);
ret = blkdev_get_whole(whole, mode);
ret = blkdev_get_whole(bdev_whole(part), mode);
if (ret)
goto out_put_whole;
return ret;
ret = -ENXIO;
if (!bdev_nr_sectors(part))
@ -1306,9 +1294,7 @@ done:
return 0;
out_blkdev_put:
blkdev_put_whole(whole, mode);
out_put_whole:
bdput(whole);
blkdev_put_whole(bdev_whole(part), mode);
return ret;
}
@ -1321,42 +1307,42 @@ static void blkdev_put_part(struct block_device *part, fmode_t mode)
blkdev_flush_mapping(part);
whole->bd_disk->open_partitions--;
blkdev_put_whole(whole, mode);
bdput(whole);
}
struct block_device *blkdev_get_no_open(dev_t dev)
{
struct block_device *bdev;
struct gendisk *disk;
struct inode *inode;
bdev = bdget(dev);
if (!bdev) {
inode = ilookup(blockdev_superblock, dev);
if (!inode) {
blk_request_module(dev);
bdev = bdget(dev);
if (!bdev)
inode = ilookup(blockdev_superblock, dev);
if (!inode)
return NULL;
}
disk = bdev->bd_disk;
if (!kobject_get_unless_zero(&disk_to_dev(disk)->kobj))
goto bdput;
if (disk->flags & GENHD_FL_HIDDEN)
goto put_disk;
if (!try_module_get(bdev->bd_disk->fops->owner))
goto put_disk;
/* switch from the inode reference to a device mode one: */
bdev = &BDEV_I(inode)->bdev;
if (!kobject_get_unless_zero(&bdev->bd_device.kobj))
bdev = NULL;
iput(inode);
if (!bdev)
return NULL;
if ((bdev->bd_disk->flags & GENHD_FL_HIDDEN) ||
!try_module_get(bdev->bd_disk->fops->owner)) {
put_device(&bdev->bd_device);
return NULL;
}
return bdev;
put_disk:
put_disk(disk);
bdput:
bdput(bdev);
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);
put_device(&bdev->bd_device);
}
/**