[PATCH 13/15] btrfs: remove the free space inode special cases from the COW paths
From: Tal Zussman
Date: Mon Sep 07 2026 - 21:24:11 EST
Free space inodes are never written anymore, so drop the special cases
for them in cow_file_range(), fallback_to_cow(),
can_nocow_file_extent() and btrfs_finish_one_ordered().
Assisted-by: Claude:claude-fable-5-1
Signed-off-by: Tal Zussman <tz2294@xxxxxxxxxxxx>
---
fs/btrfs/inode.c | 58 +++++++++++++++-----------------------------------------
1 file changed, 15 insertions(+), 43 deletions(-)
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 5e4ecd1fe194..137d2c1d9c97 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -1366,11 +1366,6 @@ static noinline int cow_file_range(struct btrfs_inode *inode,
goto out_unlock;
}
- if (btrfs_is_free_space_inode(inode)) {
- ret = -EINVAL;
- goto out_unlock;
- }
-
num_bytes = ALIGN(end - start + 1, blocksize);
num_bytes = max(blocksize, num_bytes);
ASSERT(num_bytes <= btrfs_super_total_bytes(fs_info->super_copy));
@@ -1680,7 +1675,6 @@ static int fallback_to_cow(struct btrfs_inode *inode,
struct folio *locked_folio, const u64 start,
const u64 end)
{
- const bool is_space_ino = btrfs_is_free_space_inode(inode);
const bool is_reloc_ino = btrfs_is_data_reloc_root(inode->root);
const u64 range_bytes = end + 1 - start;
struct extent_io_tree *io_tree = &inode->io_tree;
@@ -1713,23 +1707,22 @@ static int fallback_to_cow(struct btrfs_inode *inode,
* extent_clear_unlock_delalloc()) the bytes_may_use counter of the
* data space info, which we incremented in the step above.
*
- * If we need to fallback to cow and the inode corresponds to a free
- * space cache inode or an inode of the data relocation tree, we must
- * also increment bytes_may_use of the data space_info for the same
- * reason. Space caches and relocated data extents always get a prealloc
- * extent for them, however scrub or balance may have set the block
- * group that contains that extent to RO mode and therefore force COW
- * when starting writeback.
+ * If we need to fallback to cow and the inode is in the data relocation
+ * tree, we must also increment bytes_may_use of the data space_info for
+ * the same reason. Relocated data extents always get a prealloc extent,
+ * however scrub or balance may have set the block group that contains
+ * that extent to RO mode and therefore force COW when starting
+ * writeback.
*/
btrfs_lock_extent(io_tree, start, end, &cached_state);
count = btrfs_count_range_bits(io_tree, &range_start, end, range_bytes,
EXTENT_NORESERVE, false, NULL);
- if (count > 0 || is_space_ino || is_reloc_ino) {
+ if (count > 0 || is_reloc_ino) {
u64 bytes = count;
struct btrfs_fs_info *fs_info = inode->root->fs_info;
struct btrfs_space_info *sinfo = fs_info->data_sinfo;
- if (is_space_ino || is_reloc_ino)
+ if (is_reloc_ino)
bytes = range_bytes;
spin_lock(&sinfo->lock);
@@ -1794,7 +1787,6 @@ static int can_nocow_file_extent(struct btrfs_path *path,
struct btrfs_inode *inode,
struct can_nocow_file_extent_args *args)
{
- const bool is_freespace_inode = btrfs_is_free_space_inode(inode);
struct extent_buffer *leaf = path->nodes[0];
struct btrfs_root *root = inode->root;
struct btrfs_file_extent_item *fi;
@@ -1807,8 +1799,7 @@ static int can_nocow_file_extent(struct btrfs_path *path,
bool nowait = path->nowait;
/* If there are pending snapshots for this root, we must do COW. */
- if (args->writeback_path && !is_freespace_inode &&
- atomic_read(&root->snapshot_force_cow))
+ if (args->writeback_path && atomic_read(&root->snapshot_force_cow))
goto out;
fi = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_file_extent_item);
@@ -1857,7 +1848,6 @@ static int can_nocow_file_extent(struct btrfs_path *path,
ret = btrfs_cross_ref_exist(inode, key->offset - args->file_extent.offset,
args->file_extent.disk_bytenr, path);
- WARN_ON_ONCE(ret > 0 && is_freespace_inode);
if (ret != 0)
goto out;
@@ -1892,7 +1882,6 @@ static int can_nocow_file_extent(struct btrfs_path *path,
ret = btrfs_lookup_csums_list(csum_root, io_start,
io_start + args->file_extent.num_bytes - 1,
NULL, nowait);
- WARN_ON_ONCE(ret > 0 && is_freespace_inode);
if (ret != 0)
goto out;
@@ -3203,6 +3192,7 @@ int btrfs_finish_one_ordered(struct btrfs_ordered_extent *ordered_extent)
int compress_type = 0;
int ret = 0;
u64 logical_len = ordered_extent->num_bytes;
+ u64 unwritten_start;
bool truncated = false;
bool clear_reserved_extent = true;
unsigned int clear_bits = 0;
@@ -3364,29 +3354,11 @@ int btrfs_finish_one_ordered(struct btrfs_ordered_extent *ordered_extent)
if (ret)
btrfs_mark_ordered_extent_error(ordered_extent);
- /*
- * Drop extent maps for the part of the extent we didn't write.
- *
- * We have an exception here for the free_space_inode, this is
- * because when we do btrfs_get_extent() on the free space inode
- * we will search the commit root. If this is a new block group
- * we won't find anything, and we will trip over the assert in
- * writepage where we do ASSERT(em->block_start !=
- * EXTENT_MAP_HOLE).
- *
- * Theoretically we could also skip this for any NOCOW extent as
- * we don't mess with the extent map tree in the NOCOW case, but
- * for now simply skip this if we are the free space inode.
- */
- if (!btrfs_is_free_space_inode(inode)) {
- u64 unwritten_start = start;
-
- if (truncated)
- unwritten_start += logical_len;
-
- btrfs_drop_extent_map_range(inode, unwritten_start,
- end, false);
- }
+ /* Drop extent maps for the part of the extent we didn't write. */
+ unwritten_start = start;
+ if (truncated)
+ unwritten_start += logical_len;
+ btrfs_drop_extent_map_range(inode, unwritten_start, end, false);
/*
* If the ordered extent had an IOERR or something else went
--
2.39.5