btrfs: factor out space reservation code from btrfs_buffered_write()

Inside the main loop of btrfs_buffered_write(), we have a complex data
and metadata space reservation code, which tries to reserve space for
a COW write, if failed then fallback to check if we can do a NOCOW
write.

Factor out that part of code into a dedicated helper, reserve_space(),
to make the main loop a little easier to read.

Reviewed-by: Johannes Thumshirn <johannes.thumshirn@wdc.com>
Reviewed-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
This commit is contained in:
Qu Wenruo 2025-03-20 13:30:59 +10:30 committed by David Sterba
parent afe990fb59
commit af821cba72

View File

@ -1094,6 +1094,61 @@ static void release_space(struct btrfs_inode *inode, struct extent_changeset *da
}
}
/*
* Reserve data and metadata space for this buffered write range.
*
* Return >0 for the number of bytes reserved, which is always block aligned.
* Return <0 for error.
*/
static ssize_t reserve_space(struct btrfs_inode *inode,
struct extent_changeset **data_reserved,
u64 start, size_t *len, bool nowait,
bool *only_release_metadata)
{
const struct btrfs_fs_info *fs_info = inode->root->fs_info;
const unsigned int block_offset = (start & (fs_info->sectorsize - 1));
size_t reserve_bytes;
int ret;
ret = btrfs_check_data_free_space(inode, data_reserved, start, *len, nowait);
if (ret < 0) {
int can_nocow;
if (nowait && (ret == -ENOSPC || ret == -EAGAIN))
return -EAGAIN;
/*
* If we don't have to COW at the offset, reserve metadata only.
* write_bytes may get smaller than requested here.
*/
can_nocow = btrfs_check_nocow_lock(inode, start, len, nowait);
if (can_nocow < 0)
ret = can_nocow;
if (can_nocow > 0)
ret = 0;
if (ret)
return ret;
*only_release_metadata = true;
}
reserve_bytes = round_up(*len + block_offset, fs_info->sectorsize);
WARN_ON(reserve_bytes == 0);
ret = btrfs_delalloc_reserve_metadata(inode, reserve_bytes,
reserve_bytes, nowait);
if (ret) {
if (!*only_release_metadata)
btrfs_free_reserved_data_space(inode, *data_reserved,
start, *len);
else
btrfs_check_nocow_unlock(inode);
if (nowait && ret == -ENOSPC)
ret = -EAGAIN;
return ret;
}
return reserve_bytes;
}
ssize_t btrfs_buffered_write(struct kiocb *iocb, struct iov_iter *i)
{
struct file *file = iocb->ki_filp;
@ -1159,52 +1214,11 @@ ssize_t btrfs_buffered_write(struct kiocb *iocb, struct iov_iter *i)
sector_offset = pos & (fs_info->sectorsize - 1);
extent_changeset_release(data_reserved);
ret = btrfs_check_data_free_space(BTRFS_I(inode),
&data_reserved, pos,
write_bytes, nowait);
if (ret < 0) {
int can_nocow;
if (nowait && (ret == -ENOSPC || ret == -EAGAIN)) {
ret = -EAGAIN;
break;
}
/*
* If we don't have to COW at the offset, reserve
* metadata only. write_bytes may get smaller than
* requested here.
*/
can_nocow = btrfs_check_nocow_lock(BTRFS_I(inode), pos,
&write_bytes, nowait);
if (can_nocow < 0)
ret = can_nocow;
if (can_nocow > 0)
ret = 0;
if (ret)
break;
only_release_metadata = true;
}
reserve_bytes = round_up(write_bytes + sector_offset,
fs_info->sectorsize);
WARN_ON(reserve_bytes == 0);
ret = btrfs_delalloc_reserve_metadata(BTRFS_I(inode),
reserve_bytes,
reserve_bytes, nowait);
if (ret) {
if (!only_release_metadata)
btrfs_free_reserved_data_space(BTRFS_I(inode),
data_reserved, pos,
write_bytes);
else
btrfs_check_nocow_unlock(BTRFS_I(inode));
if (nowait && ret == -ENOSPC)
ret = -EAGAIN;
ret = reserve_space(BTRFS_I(inode), &data_reserved, pos,
&write_bytes, nowait, &only_release_metadata);
if (ret < 0)
break;
}
reserve_bytes = ret;
release_bytes = reserve_bytes;
again:
ret = balance_dirty_pages_ratelimited_flags(inode->i_mapping, bdp_flags);