-/*
- * Helper function for later fs/subvol tree check. To determine if a tree
- * block should be checked.
- * This function will ensure only the direct referencer with lowest rootid to
- * check a fs/subvolume tree block.
- *
- * Backref check at extent tree would detect errors like missing subvolume
- * tree, so we can do aggressive check to reduce duplicated checks.
- */
-static int should_check(struct btrfs_root *root, struct extent_buffer *eb)
-{
- struct btrfs_root *extent_root = root->fs_info->extent_root;
- struct btrfs_key key;
- struct btrfs_path path;
- struct extent_buffer *leaf;
- int slot;
- struct btrfs_extent_item *ei;
- unsigned long ptr;
- unsigned long end;
- int type;
- u32 item_size;
- u64 offset;
- struct btrfs_extent_inline_ref *iref;
- int ret;
-
- btrfs_init_path(&path);
- key.objectid = btrfs_header_bytenr(eb);
- key.type = BTRFS_METADATA_ITEM_KEY;
- key.offset = (u64)-1;
-
- /*
- * Any failure in backref resolving means we can't determine
- * whom the tree block belongs to.
- * So in that case, we need to check that tree block
- */
- ret = btrfs_search_slot(NULL, extent_root, &key, &path, 0, 0);
- if (ret < 0)
- goto need_check;
-
- ret = btrfs_previous_extent_item(extent_root, &path,
- btrfs_header_bytenr(eb));
- if (ret)
- goto need_check;
-
- leaf = path.nodes[0];
- slot = path.slots[0];
- btrfs_item_key_to_cpu(leaf, &key, slot);
- ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item);
-
- if (key.type == BTRFS_METADATA_ITEM_KEY) {
- iref = (struct btrfs_extent_inline_ref *)(ei + 1);
- } else {
- struct btrfs_tree_block_info *info;
-
- info = (struct btrfs_tree_block_info *)(ei + 1);
- iref = (struct btrfs_extent_inline_ref *)(info + 1);
- }
-
- item_size = btrfs_item_size_nr(leaf, slot);
- ptr = (unsigned long)iref;
- end = (unsigned long)ei + item_size;
- while (ptr < end) {
- iref = (struct btrfs_extent_inline_ref *)ptr;
- type = btrfs_extent_inline_ref_type(leaf, iref);
- offset = btrfs_extent_inline_ref_offset(leaf, iref);
-
- /*
- * We only check the tree block if current root is
- * the lowest referencer of it.
- */
- if (type == BTRFS_TREE_BLOCK_REF_KEY &&
- offset < root->objectid) {
- btrfs_release_path(&path);
- return 0;
- }
-
- ptr += btrfs_extent_inline_ref_size(type);
- }
- /*
- * Normally we should also check keyed tree block ref, but that may be
- * very time consuming. Inlined ref should already make us skip a lot
- * of refs now. So skip search keyed tree block ref.
- */
-
-need_check:
- btrfs_release_path(&path);
- return 1;
-}
-
-/*
- * Traversal function for tree block. We will do:
- * 1) Skip shared fs/subvolume tree blocks
- * 2) Update related bytes accounting
- * 3) Pre-order traversal
- */
-static int traverse_tree_block(struct btrfs_root *root,
- struct extent_buffer *node)
-{
- struct extent_buffer *eb;
- struct btrfs_key key;
- struct btrfs_key drop_key;
- int level;
- u64 nr;
- int i;
- int err = 0;
- int ret;
-
- /*
- * Skip shared fs/subvolume tree block, in that case they will
- * be checked by referencer with lowest rootid
- */
- if (is_fstree(root->objectid) && !should_check(root, node))
- return 0;
-
- /* Update bytes accounting */
- total_btree_bytes += node->len;
- if (fs_root_objectid(btrfs_header_owner(node)))
- total_fs_tree_bytes += node->len;
- if (btrfs_header_owner(node) == BTRFS_EXTENT_TREE_OBJECTID)
- total_extent_tree_bytes += node->len;
-
- /* pre-order tranversal, check itself first */
- level = btrfs_header_level(node);
- ret = check_tree_block_ref(root, node, btrfs_header_bytenr(node),
- btrfs_header_level(node),
- btrfs_header_owner(node));
- err |= ret;
- if (err)
- error(
- "check %s failed root %llu bytenr %llu level %d, force continue check",
- level ? "node":"leaf", root->objectid,
- btrfs_header_bytenr(node), btrfs_header_level(node));
-
- if (!level) {
- btree_space_waste += btrfs_leaf_free_space(root, node);
- ret = check_leaf_items(root, node);
- err |= ret;
- return err;
- }
-
- nr = btrfs_header_nritems(node);
- btrfs_disk_key_to_cpu(&drop_key, &root->root_item.drop_progress);
- btree_space_waste += (BTRFS_NODEPTRS_PER_BLOCK(root) - nr) *
- sizeof(struct btrfs_key_ptr);
-
- /* Then check all its children */
- for (i = 0; i < nr; i++) {
- u64 blocknr = btrfs_node_blockptr(node, i);
-
- btrfs_node_key_to_cpu(node, &key, i);
- if (level == root->root_item.drop_level &&
- is_dropped_key(&key, &drop_key))
- continue;
-
- /*
- * As a btrfs tree has most 8 levels (0..7), so it's quite safe
- * to call the function itself.
- */
- eb = read_tree_block(root->fs_info, blocknr, 0);
- if (extent_buffer_uptodate(eb)) {
- ret = traverse_tree_block(root, eb);
- err |= ret;
- }
- free_extent_buffer(eb);
- }
-
- return err;
-}