Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

btrfs: apply first key check for readahead when possible

Currently for tree block readahead we never pass a
btrfs_tree_parent_check with @has_first_key set.

Without @has_first_key set, btrfs will skip the following extra
checks:

- Header generation check
This is a minor one.

- Empty leaf/node checks
This is more serious, for certain trees like the csum tree, they are
allowed to be empty, thus an empty leaf can pass the tree checker.
But if there is a parent node for such an empty leaf, it indicates
corruption.

Without @has_first_key set, we can no longer detect such a problem.

In fact there is already a fuzzed image report that a corrupted csum
leaf which has zero nritems but still has a parent node can trigger
a BUG_ON() during csum deletion.

However there are only two call sites of btrfs_readahead_tree_block():

- Inside relocate_tree_blocks()
At this call site we are trying to grab the first key of the tree
block, thus we are not able to pass a @first_key parameter.

- Inside btrfs_readahead_node_child()
This is the more common call site, where we have the parent node and
want to readahead the child tree blocks.

In this case we can easily grab the node key and pass it for checks.

Add a new parameter @first_key to btrfs_readahead_tree_block() and pass
the node key to it inside btrfs_readahead_node_child().

This should plug the gap in empty leaf detection during readahead.

Link: https://lore.kernel.org/linux-btrfs/20260409071255.3358044-1-gality369@gmail.com/
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: Qu Wenruo <wqu@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>

authored by

Qu Wenruo and committed by
David Sterba
a86a2834 73db0fad

+15 -4
+12 -2
fs/btrfs/extent_io.c
··· 4641 4641 * to read the block we will not block on anything. 4642 4642 */ 4643 4643 void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info, 4644 - u64 bytenr, u64 owner_root, u64 gen, int level) 4644 + u64 bytenr, u64 owner_root, u64 gen, int level, 4645 + const struct btrfs_key *first_key) 4645 4646 { 4646 4647 struct btrfs_tree_parent_check check = { 4647 4648 .level = level, ··· 4650 4649 }; 4651 4650 struct extent_buffer *eb; 4652 4651 int ret; 4652 + 4653 + if (first_key) { 4654 + memcpy(&check.first_key, first_key, sizeof(struct btrfs_key)); 4655 + check.has_first_key = true; 4656 + } 4653 4657 4654 4658 eb = btrfs_find_create_tree_block(fs_info, bytenr, owner_root, level); 4655 4659 if (IS_ERR(eb)) ··· 4683 4677 */ 4684 4678 void btrfs_readahead_node_child(struct extent_buffer *node, int slot) 4685 4679 { 4680 + struct btrfs_key node_key; 4681 + 4682 + btrfs_node_key_to_cpu(node, &node_key, slot); 4686 4683 btrfs_readahead_tree_block(node->fs_info, 4687 4684 btrfs_node_blockptr(node, slot), 4688 4685 btrfs_header_owner(node), 4689 4686 btrfs_node_ptr_generation(node, slot), 4690 - btrfs_header_level(node) - 1); 4687 + btrfs_header_level(node) - 1, 4688 + &node_key); 4691 4689 }
+2 -1
fs/btrfs/extent_io.h
··· 287 287 } 288 288 289 289 void btrfs_readahead_tree_block(struct btrfs_fs_info *fs_info, 290 - u64 bytenr, u64 owner_root, u64 gen, int level); 290 + u64 bytenr, u64 owner_root, u64 gen, int level, 291 + const struct btrfs_key *first_key); 291 292 void btrfs_readahead_node_child(struct extent_buffer *node, int slot); 292 293 293 294 /* Note: this can be used in for loops without caching the value in a variable. */
+1 -1
fs/btrfs/relocation.c
··· 2607 2607 if (!block->key_ready) 2608 2608 btrfs_readahead_tree_block(fs_info, block->bytenr, 2609 2609 block->owner, 0, 2610 - block->level); 2610 + block->level, NULL); 2611 2611 } 2612 2612 2613 2613 /* Get first keys */