block: split bdev_yield_claim() out of bdev_fput()

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.

Link: https://patch.msgid.link/20260616-work-super-freeze_deny_upstream-v2-2-b3567c7f994b@kernel.org
Reviewed-by: Jan Kara <jack@suse.cz>
Reviewd-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
Christian Brauner
2026-06-16 13:58:15 +02:00
parent ea4e4cc263
commit 822d87bc52
2 changed files with 35 additions and 16 deletions

View File

@@ -1195,6 +1195,39 @@ void bdev_release(struct file *bdev_file)
blkdev_put_no_open(bdev);
}
/**
* bdev_yield_claim - give up the holder claim on an open block device
* @bdev_file: open block device
*
* 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_yield_claim(struct file *bdev_file)
{
struct block_device *bdev;
struct gendisk *disk;
if (!bdev_file->private_data)
return;
bdev = file_bdev(bdev_file);
disk = bdev->bd_disk;
mutex_lock(&disk->open_mutex);
bdev_yield_write_access(bdev_file);
bd_yield_claim(bdev_file);
/*
* Tell release we already gave up our hold on the
* device and if write restrictions are available that
* we already gave up write access to the device.
*/
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
@@ -1208,22 +1241,7 @@ void bdev_fput(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;
mutex_lock(&disk->open_mutex);
bdev_yield_write_access(bdev_file);
bd_yield_claim(bdev_file);
/*
* Tell release we already gave up our hold on the
* device and if write restrictions are available that
* we already gave up write access to the device.
*/
bdev_file->private_data = BDEV_I(bdev_file->f_mapping->host);
mutex_unlock(&disk->open_mutex);
}
bdev_yield_claim(bdev_file);
fput(bdev_file);
}
EXPORT_SYMBOL(bdev_fput);

View File

@@ -1840,6 +1840,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;