bdev_fput() yields the holder claim and then closes the file, which is a deferred operation. Split the yield half into bdev_yield_claim() so a caller can give up the holder while the file - and therefore the block device - is still open, act on the device, and only then bdev_fput(). A filesystem that made a device unfreezable for a membership change with bdev_deny_freeze() undoes the deny on release with bdev_yield_claim(bdev_file); bdev_allow_freeze(file_bdev(bdev_file)); bdev_fput(bdev_file); Re-allowing only after the holder is yielded avoids stranding the filesystem on a racing freeze, and doing it while the file is still open avoids touching the block device after bdev_fput(). bdev_fput() yields again, which is a no-op once the claim has already been given up. Signed-off-by: Christian Brauner (Amutable) --- block/bdev.c | 30 ++++++++++++++++++++++-------- include/linux/blkdev.h | 1 + 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/block/bdev.c b/block/bdev.c index 939dec351772..e59052c2a081 100644 --- a/block/bdev.c +++ b/block/bdev.c @@ -1199,18 +1199,16 @@ void bdev_release(struct file *bdev_file) } /** - * bdev_fput - yield claim to the block device and put the file + * bdev_yield_claim - give up the holder claim on an open block device * @bdev_file: open block device * - * Yield claim on the block device and put the file. Ensure that the - * block device can be reclaimed before the file is closed which is a - * deferred operation. + * Yield the holder and any write access for @bdev_file without closing it, so + * the caller can still act on the device - e.g. bdev_allow_freeze() it - before + * the final bdev_fput(). bdev_fput() yields too, so calling it afterwards is + * safe. */ -void bdev_fput(struct file *bdev_file) +void bdev_yield_claim(struct file *bdev_file) { - if (WARN_ON_ONCE(bdev_file->f_op != &def_blk_fops)) - return; - if (bdev_file->private_data) { struct block_device *bdev = file_bdev(bdev_file); struct gendisk *disk = bdev->bd_disk; @@ -1226,7 +1224,23 @@ void bdev_fput(struct file *bdev_file) bdev_file->private_data = BDEV_I(bdev_file->f_mapping->host); mutex_unlock(&disk->open_mutex); } +} +EXPORT_SYMBOL_GPL(bdev_yield_claim); + +/** + * bdev_fput - yield claim to the block device and put the file + * @bdev_file: open block device + * + * Yield claim on the block device and put the file. Ensure that the + * block device can be reclaimed before the file is closed which is a + * deferred operation. + */ +void bdev_fput(struct file *bdev_file) +{ + if (WARN_ON_ONCE(bdev_file->f_op != &def_blk_fops)) + return; + bdev_yield_claim(bdev_file); fput(bdev_file); } EXPORT_SYMBOL(bdev_fput); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index cf1951caadb2..9fc16e3c8075 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1832,6 +1832,7 @@ int bdev_thaw(struct block_device *bdev); int bdev_deny_freeze(struct block_device *bdev); void bdev_allow_freeze(struct block_device *bdev); void bdev_fput(struct file *bdev_file); +void bdev_yield_claim(struct file *bdev_file); struct io_comp_batch { struct rq_list req_list; -- 2.47.3