Commit 9d3b8813 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Jens Axboe

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.deSigned-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 0468c532
...@@ -261,6 +261,7 @@ static void part_release(struct device *dev) ...@@ -261,6 +261,7 @@ static void part_release(struct device *dev)
{ {
if (MAJOR(dev->devt) == BLOCK_EXT_MAJOR) if (MAJOR(dev->devt) == BLOCK_EXT_MAJOR)
blk_free_ext_minor(MINOR(dev->devt)); blk_free_ext_minor(MINOR(dev->devt));
put_disk(dev_to_bdev(dev)->bd_disk);
bdput(dev_to_bdev(dev)); bdput(dev_to_bdev(dev));
} }
...@@ -349,9 +350,13 @@ static struct block_device *add_partition(struct gendisk *disk, int partno, ...@@ -349,9 +350,13 @@ static struct block_device *add_partition(struct gendisk *disk, int partno,
if (xa_load(&disk->part_tbl, partno)) if (xa_load(&disk->part_tbl, partno))
return ERR_PTR(-EBUSY); 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); bdev = bdev_alloc(disk, partno);
if (!bdev) if (!bdev)
return ERR_PTR(-ENOMEM); goto out_put_disk;
bdev->bd_start_sect = start; bdev->bd_start_sect = start;
bdev_set_nr_sectors(bdev, len); bdev_set_nr_sectors(bdev, len);
...@@ -420,6 +425,8 @@ static struct block_device *add_partition(struct gendisk *disk, int partno, ...@@ -420,6 +425,8 @@ static struct block_device *add_partition(struct gendisk *disk, int partno,
device_del(pdev); device_del(pdev);
out_put: out_put:
put_device(pdev); put_device(pdev);
out_put_disk:
put_disk(disk);
return ERR_PTR(err); return ERR_PTR(err);
} }
......
...@@ -921,16 +921,6 @@ void bdev_add(struct block_device *bdev, dev_t dev) ...@@ -921,16 +921,6 @@ void bdev_add(struct block_device *bdev, dev_t dev)
insert_inode_hash(bdev->bd_inode); 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 * bdgrab -- Grab a reference to an already referenced block device
* @bdev: Block device to grab a reference to. * @bdev: Block device to grab a reference to.
...@@ -1282,16 +1272,14 @@ static void blkdev_put_whole(struct block_device *bdev, fmode_t mode) ...@@ -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) static int blkdev_get_part(struct block_device *part, fmode_t mode)
{ {
struct gendisk *disk = part->bd_disk; struct gendisk *disk = part->bd_disk;
struct block_device *whole;
int ret; int ret;
if (part->bd_openers) if (part->bd_openers)
goto done; goto done;
whole = bdgrab(disk->part0); ret = blkdev_get_whole(bdev_whole(part), mode);
ret = blkdev_get_whole(whole, mode);
if (ret) if (ret)
goto out_put_whole; return ret;
ret = -ENXIO; ret = -ENXIO;
if (!bdev_nr_sectors(part)) if (!bdev_nr_sectors(part))
...@@ -1306,9 +1294,7 @@ static int blkdev_get_part(struct block_device *part, fmode_t mode) ...@@ -1306,9 +1294,7 @@ static int blkdev_get_part(struct block_device *part, fmode_t mode)
return 0; return 0;
out_blkdev_put: out_blkdev_put:
blkdev_put_whole(whole, mode); blkdev_put_whole(bdev_whole(part), mode);
out_put_whole:
bdput(whole);
return ret; return ret;
} }
...@@ -1321,42 +1307,42 @@ static void blkdev_put_part(struct block_device *part, fmode_t mode) ...@@ -1321,42 +1307,42 @@ static void blkdev_put_part(struct block_device *part, fmode_t mode)
blkdev_flush_mapping(part); blkdev_flush_mapping(part);
whole->bd_disk->open_partitions--; whole->bd_disk->open_partitions--;
blkdev_put_whole(whole, mode); blkdev_put_whole(whole, mode);
bdput(whole);
} }
struct block_device *blkdev_get_no_open(dev_t dev) struct block_device *blkdev_get_no_open(dev_t dev)
{ {
struct block_device *bdev; struct block_device *bdev;
struct gendisk *disk; struct inode *inode;
bdev = bdget(dev); inode = ilookup(blockdev_superblock, dev);
if (!bdev) { if (!inode) {
blk_request_module(dev); blk_request_module(dev);
bdev = bdget(dev); inode = ilookup(blockdev_superblock, dev);
if (!bdev) if (!inode)
return NULL; return NULL;
} }
disk = bdev->bd_disk; /* switch from the inode reference to a device mode one: */
if (!kobject_get_unless_zero(&disk_to_dev(disk)->kobj)) bdev = &BDEV_I(inode)->bdev;
goto bdput; if (!kobject_get_unless_zero(&bdev->bd_device.kobj))
if (disk->flags & GENHD_FL_HIDDEN) bdev = NULL;
goto put_disk; iput(inode);
if (!try_module_get(bdev->bd_disk->fops->owner))
goto put_disk; 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; return bdev;
put_disk:
put_disk(disk);
bdput:
bdput(bdev);
return NULL;
} }
void blkdev_put_no_open(struct block_device *bdev) void blkdev_put_no_open(struct block_device *bdev)
{ {
module_put(bdev->bd_disk->fops->owner); module_put(bdev->bd_disk->fops->owner);
put_disk(bdev->bd_disk); put_device(&bdev->bd_device);
bdput(bdev);
} }
/** /**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment