btrfs: subpage: reject tree blocks which are not nodesize aligned
authorQu Wenruo <wqu@suse.com>
Wed, 23 Apr 2025 07:06:14 +0000 (16:36 +0930)
committerDavid Sterba <dsterba@suse.com>
Thu, 15 May 2025 12:30:49 +0000 (14:30 +0200)
When btrfs subpage support (fs block < page size) was introduced, a
subpage filesystem will only reject tree blocks which cross page
boundaries.

This used to be a compromise to simplify the tree block handling and
still allowing subpage cases to read some old converted filesystems
which did not have proper chunk alignment.

But in practice, suppose we have the following unaligned tree block on a
64K page sized system:

  0                           32K           44K             60K  64K
  |                                         |///////////////|    |

Although btrfs has no problem reading the tree block at [44K, 60K), if
extent allocator is allocating another tree block, it may choose the
range [60K, 74K), as extent allocator has no awareness if it's a subpage
metadata request or not.

Then we'd get -EINVAL from the following sequence:

 btrfs_alloc_tree_block()
 |- btrfs_reserve_extent()
 |  Which returned range [60K, 74K)
 |- btrfs_init_new_buffer()
    |- btrfs_find_create_tree_block()
       |- alloc_extent_buffer()
          |- check_eb_alignment()
     Which returned -EINVAL, because the range crosses page
     boundary.

This situation will not fix itself and should mostly mark the fs
read-only.

Thankfully we didn't really get such reports in the real world because:

- The original unaligned tree block is only caused by older
  btrfs-convert
  It's before the btrfs-convert rework was done in v4.6, where converted
  btrfs filesystem can have metadata block groups which are not aligned
  to nodesize nor stripe size (64K).

  But after btrfs-progs v4.6, all chunks allocated will be stripe (64K)
  aligned, thus no more such problem.

Considering how old the fix is (v4.6 was released almost 10 years ago),
subpage support for btrfs was introduced in v5.15, it should be safe to
reject those unaligned tree blocks.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/extent_io.c

index a231d03d51c7e134de884849c0550292ce09449b..dc8db26c821c2dd762f4c85c3835017e240861ae 100644 (file)
@@ -3068,10 +3068,9 @@ static bool check_eb_alignment(struct btrfs_fs_info *fs_info, u64 start)
                return true;
        }
 
-       if (fs_info->nodesize < PAGE_SIZE &&
-           offset_in_page(start) + fs_info->nodesize > PAGE_SIZE) {
+       if (fs_info->nodesize < PAGE_SIZE && !IS_ALIGNED(start, fs_info->nodesize)) {
                btrfs_err(fs_info,
-               "tree block crosses page boundary, start %llu nodesize %u",
+               "tree block is not nodesize aligned, start %llu nodesize %u",
                          start, fs_info->nodesize);
                return true;
        }