Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Josef Whiter | 4529 | 89.86% | 12 | 17.65% |
Qu Wenruo | 105 | 2.08% | 1 | 1.47% |
Chris Mason | 82 | 1.63% | 17 | 25.00% |
Josef Bacik | 53 | 1.05% | 5 | 7.35% |
Liu Bo | 46 | 0.91% | 2 | 2.94% |
Li Dongyang | 32 | 0.63% | 1 | 1.47% |
Wenwen Wang | 30 | 0.60% | 1 | 1.47% |
Jeff Mahoney | 25 | 0.50% | 5 | 7.35% |
David Sterba | 23 | 0.46% | 4 | 5.88% |
Miao Xie | 22 | 0.44% | 4 | 5.88% |
Zheng Yan | 22 | 0.44% | 2 | 2.94% |
Boris Burkov | 17 | 0.34% | 2 | 2.94% |
Thomas Gleixner | 12 | 0.24% | 1 | 1.47% |
Bragatheswaran Manickavel | 10 | 0.20% | 1 | 1.47% |
Tom Rix | 8 | 0.16% | 1 | 1.47% |
Dinghao Liu | 5 | 0.10% | 1 | 1.47% |
Filipe David Borba Manana | 5 | 0.10% | 1 | 1.47% |
Andrea Gelmini | 3 | 0.06% | 1 | 1.47% |
Ingo Molnar | 2 | 0.04% | 1 | 1.47% |
Arne Jansen | 2 | 0.04% | 1 | 1.47% |
Randy Dunlap | 2 | 0.04% | 1 | 1.47% |
Marcos Paulo de Souza | 2 | 0.04% | 1 | 1.47% |
Yan Zheng | 2 | 0.04% | 1 | 1.47% |
Zach Brown | 1 | 0.02% | 1 | 1.47% |
Total | 5040 | 68 |
// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2014 Facebook. All rights reserved. */ #include <linux/sched.h> #include <linux/stacktrace.h> #include "messages.h" #include "ctree.h" #include "disk-io.h" #include "locking.h" #include "delayed-ref.h" #include "ref-verify.h" #include "fs.h" #include "accessors.h" /* * Used to keep track the roots and number of refs each root has for a given * bytenr. This just tracks the number of direct references, no shared * references. */ struct root_entry { u64 root_objectid; u64 num_refs; struct rb_node node; }; /* * These are meant to represent what should exist in the extent tree, these can * be used to verify the extent tree is consistent as these should all match * what the extent tree says. */ struct ref_entry { u64 root_objectid; u64 parent; u64 owner; u64 offset; u64 num_refs; struct rb_node node; }; #define MAX_TRACE 16 /* * Whenever we add/remove a reference we record the action. The action maps * back to the delayed ref action. We hold the ref we are changing in the * action so we can account for the history properly, and we record the root we * were called with since it could be different from ref_root. We also store * stack traces because that's how I roll. */ struct ref_action { int action; u64 root; struct ref_entry ref; struct list_head list; unsigned long trace[MAX_TRACE]; unsigned int trace_len; }; /* * One of these for every block we reference, it holds the roots and references * to it as well as all of the ref actions that have occurred to it. We never * free it until we unmount the file system in order to make sure re-allocations * are happening properly. */ struct block_entry { u64 bytenr; u64 len; u64 num_refs; int metadata; int from_disk; struct rb_root roots; struct rb_root refs; struct rb_node node; struct list_head actions; }; static struct block_entry *insert_block_entry(struct rb_root *root, struct block_entry *be) { struct rb_node **p = &root->rb_node; struct rb_node *parent_node = NULL; struct block_entry *entry; while (*p) { parent_node = *p; entry = rb_entry(parent_node, struct block_entry, node); if (entry->bytenr > be->bytenr) p = &(*p)->rb_left; else if (entry->bytenr < be->bytenr) p = &(*p)->rb_right; else return entry; } rb_link_node(&be->node, parent_node, p); rb_insert_color(&be->node, root); return NULL; } static struct block_entry *lookup_block_entry(struct rb_root *root, u64 bytenr) { struct rb_node *n; struct block_entry *entry = NULL; n = root->rb_node; while (n) { entry = rb_entry(n, struct block_entry, node); if (entry->bytenr < bytenr) n = n->rb_right; else if (entry->bytenr > bytenr) n = n->rb_left; else return entry; } return NULL; } static struct root_entry *insert_root_entry(struct rb_root *root, struct root_entry *re) { struct rb_node **p = &root->rb_node; struct rb_node *parent_node = NULL; struct root_entry *entry; while (*p) { parent_node = *p; entry = rb_entry(parent_node, struct root_entry, node); if (entry->root_objectid > re->root_objectid) p = &(*p)->rb_left; else if (entry->root_objectid < re->root_objectid) p = &(*p)->rb_right; else return entry; } rb_link_node(&re->node, parent_node, p); rb_insert_color(&re->node, root); return NULL; } static int comp_refs(struct ref_entry *ref1, struct ref_entry *ref2) { if (ref1->root_objectid < ref2->root_objectid) return -1; if (ref1->root_objectid > ref2->root_objectid) return 1; if (ref1->parent < ref2->parent) return -1; if (ref1->parent > ref2->parent) return 1; if (ref1->owner < ref2->owner) return -1; if (ref1->owner > ref2->owner) return 1; if (ref1->offset < ref2->offset) return -1; if (ref1->offset > ref2->offset) return 1; return 0; } static struct ref_entry *insert_ref_entry(struct rb_root *root, struct ref_entry *ref) { struct rb_node **p = &root->rb_node; struct rb_node *parent_node = NULL; struct ref_entry *entry; int cmp; while (*p) { parent_node = *p; entry = rb_entry(parent_node, struct ref_entry, node); cmp = comp_refs(entry, ref); if (cmp > 0) p = &(*p)->rb_left; else if (cmp < 0) p = &(*p)->rb_right; else return entry; } rb_link_node(&ref->node, parent_node, p); rb_insert_color(&ref->node, root); return NULL; } static struct root_entry *lookup_root_entry(struct rb_root *root, u64 objectid) { struct rb_node *n; struct root_entry *entry = NULL; n = root->rb_node; while (n) { entry = rb_entry(n, struct root_entry, node); if (entry->root_objectid < objectid) n = n->rb_right; else if (entry->root_objectid > objectid) n = n->rb_left; else return entry; } return NULL; } #ifdef CONFIG_STACKTRACE static void __save_stack_trace(struct ref_action *ra) { ra->trace_len = stack_trace_save(ra->trace, MAX_TRACE, 2); } static void __print_stack_trace(struct btrfs_fs_info *fs_info, struct ref_action *ra) { if (ra->trace_len == 0) { btrfs_err(fs_info, " ref-verify: no stacktrace"); return; } stack_trace_print(ra->trace, ra->trace_len, 2); } #else static inline void __save_stack_trace(struct ref_action *ra) { } static inline void __print_stack_trace(struct btrfs_fs_info *fs_info, struct ref_action *ra) { btrfs_err(fs_info, " ref-verify: no stacktrace support"); } #endif static void free_block_entry(struct block_entry *be) { struct root_entry *re; struct ref_entry *ref; struct ref_action *ra; struct rb_node *n; while ((n = rb_first(&be->roots))) { re = rb_entry(n, struct root_entry, node); rb_erase(&re->node, &be->roots); kfree(re); } while((n = rb_first(&be->refs))) { ref = rb_entry(n, struct ref_entry, node); rb_erase(&ref->node, &be->refs); kfree(ref); } while (!list_empty(&be->actions)) { ra = list_first_entry(&be->actions, struct ref_action, list); list_del(&ra->list); kfree(ra); } kfree(be); } static struct block_entry *add_block_entry(struct btrfs_fs_info *fs_info, u64 bytenr, u64 len, u64 root_objectid) { struct block_entry *be = NULL, *exist; struct root_entry *re = NULL; re = kzalloc(sizeof(struct root_entry), GFP_NOFS); be = kzalloc(sizeof(struct block_entry), GFP_NOFS); if (!be || !re) { kfree(re); kfree(be); return ERR_PTR(-ENOMEM); } be->bytenr = bytenr; be->len = len; re->root_objectid = root_objectid; re->num_refs = 0; spin_lock(&fs_info->ref_verify_lock); exist = insert_block_entry(&fs_info->block_tree, be); if (exist) { if (root_objectid) { struct root_entry *exist_re; exist_re = insert_root_entry(&exist->roots, re); if (exist_re) kfree(re); } else { kfree(re); } kfree(be); return exist; } be->num_refs = 0; be->metadata = 0; be->from_disk = 0; be->roots = RB_ROOT; be->refs = RB_ROOT; INIT_LIST_HEAD(&be->actions); if (root_objectid) insert_root_entry(&be->roots, re); else kfree(re); return be; } static int add_tree_block(struct btrfs_fs_info *fs_info, u64 ref_root, u64 parent, u64 bytenr, int level) { struct block_entry *be; struct root_entry *re; struct ref_entry *ref = NULL, *exist; ref = kmalloc(sizeof(struct ref_entry), GFP_NOFS); if (!ref) return -ENOMEM; if (parent) ref->root_objectid = 0; else ref->root_objectid = ref_root; ref->parent = parent; ref->owner = level; ref->offset = 0; ref->num_refs = 1; be = add_block_entry(fs_info, bytenr, fs_info->nodesize, ref_root); if (IS_ERR(be)) { kfree(ref); return PTR_ERR(be); } be->num_refs++; be->from_disk = 1; be->metadata = 1; if (!parent) { ASSERT(ref_root); re = lookup_root_entry(&be->roots, ref_root); ASSERT(re); re->num_refs++; } exist = insert_ref_entry(&be->refs, ref); if (exist) { exist->num_refs++; kfree(ref); } spin_unlock(&fs_info->ref_verify_lock); return 0; } static int add_shared_data_ref(struct btrfs_fs_info *fs_info, u64 parent, u32 num_refs, u64 bytenr, u64 num_bytes) { struct block_entry *be; struct ref_entry *ref; ref = kzalloc(sizeof(struct ref_entry), GFP_NOFS); if (!ref) return -ENOMEM; be = add_block_entry(fs_info, bytenr, num_bytes, 0); if (IS_ERR(be)) { kfree(ref); return PTR_ERR(be); } be->num_refs += num_refs; ref->parent = parent; ref->num_refs = num_refs; if (insert_ref_entry(&be->refs, ref)) { spin_unlock(&fs_info->ref_verify_lock); btrfs_err(fs_info, "existing shared ref when reading from disk?"); kfree(ref); return -EINVAL; } spin_unlock(&fs_info->ref_verify_lock); return 0; } static int add_extent_data_ref(struct btrfs_fs_info *fs_info, struct extent_buffer *leaf, struct btrfs_extent_data_ref *dref, u64 bytenr, u64 num_bytes) { struct block_entry *be; struct ref_entry *ref; struct root_entry *re; u64 ref_root = btrfs_extent_data_ref_root(leaf, dref); u64 owner = btrfs_extent_data_ref_objectid(leaf, dref); u64 offset = btrfs_extent_data_ref_offset(leaf, dref); u32 num_refs = btrfs_extent_data_ref_count(leaf, dref); ref = kzalloc(sizeof(struct ref_entry), GFP_NOFS); if (!ref) return -ENOMEM; be = add_block_entry(fs_info, bytenr, num_bytes, ref_root); if (IS_ERR(be)) { kfree(ref); return PTR_ERR(be); } be->num_refs += num_refs; ref->parent = 0; ref->owner = owner; ref->root_objectid = ref_root; ref->offset = offset; ref->num_refs = num_refs; if (insert_ref_entry(&be->refs, ref)) { spin_unlock(&fs_info->ref_verify_lock); btrfs_err(fs_info, "existing ref when reading from disk?"); kfree(ref); return -EINVAL; } re = lookup_root_entry(&be->roots, ref_root); if (!re) { spin_unlock(&fs_info->ref_verify_lock); btrfs_err(fs_info, "missing root in new block entry?"); return -EINVAL; } re->num_refs += num_refs; spin_unlock(&fs_info->ref_verify_lock); return 0; } static int process_extent_item(struct btrfs_fs_info *fs_info, struct btrfs_path *path, struct btrfs_key *key, int slot, int *tree_block_level) { struct btrfs_extent_item *ei; struct btrfs_extent_inline_ref *iref; struct btrfs_extent_data_ref *dref; struct btrfs_shared_data_ref *sref; struct extent_buffer *leaf = path->nodes[0]; u32 item_size = btrfs_item_size(leaf, slot); unsigned long end, ptr; u64 offset, flags, count; int type, ret; ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item); flags = btrfs_extent_flags(leaf, ei); if ((key->type == BTRFS_EXTENT_ITEM_KEY) && flags & BTRFS_EXTENT_FLAG_TREE_BLOCK) { struct btrfs_tree_block_info *info; info = (struct btrfs_tree_block_info *)(ei + 1); *tree_block_level = btrfs_tree_block_level(leaf, info); iref = (struct btrfs_extent_inline_ref *)(info + 1); } else { if (key->type == BTRFS_METADATA_ITEM_KEY) *tree_block_level = key->offset; iref = (struct btrfs_extent_inline_ref *)(ei + 1); } 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); switch (type) { case BTRFS_TREE_BLOCK_REF_KEY: ret = add_tree_block(fs_info, offset, 0, key->objectid, *tree_block_level); break; case BTRFS_SHARED_BLOCK_REF_KEY: ret = add_tree_block(fs_info, 0, offset, key->objectid, *tree_block_level); break; case BTRFS_EXTENT_DATA_REF_KEY: dref = (struct btrfs_extent_data_ref *)(&iref->offset); ret = add_extent_data_ref(fs_info, leaf, dref, key->objectid, key->offset); break; case BTRFS_SHARED_DATA_REF_KEY: sref = (struct btrfs_shared_data_ref *)(iref + 1); count = btrfs_shared_data_ref_count(leaf, sref); ret = add_shared_data_ref(fs_info, offset, count, key->objectid, key->offset); break; case BTRFS_EXTENT_OWNER_REF_KEY: WARN_ON(!btrfs_fs_incompat(fs_info, SIMPLE_QUOTA)); break; default: btrfs_err(fs_info, "invalid key type in iref"); ret = -EINVAL; break; } if (ret) break; ptr += btrfs_extent_inline_ref_size(type); } return ret; } static int process_leaf(struct btrfs_root *root, struct btrfs_path *path, u64 *bytenr, u64 *num_bytes, int *tree_block_level) { struct btrfs_fs_info *fs_info = root->fs_info; struct extent_buffer *leaf = path->nodes[0]; struct btrfs_extent_data_ref *dref; struct btrfs_shared_data_ref *sref; u32 count; int i = 0, ret = 0; struct btrfs_key key; int nritems = btrfs_header_nritems(leaf); for (i = 0; i < nritems; i++) { btrfs_item_key_to_cpu(leaf, &key, i); switch (key.type) { case BTRFS_EXTENT_ITEM_KEY: *num_bytes = key.offset; fallthrough; case BTRFS_METADATA_ITEM_KEY: *bytenr = key.objectid; ret = process_extent_item(fs_info, path, &key, i, tree_block_level); break; case BTRFS_TREE_BLOCK_REF_KEY: ret = add_tree_block(fs_info, key.offset, 0, key.objectid, *tree_block_level); break; case BTRFS_SHARED_BLOCK_REF_KEY: ret = add_tree_block(fs_info, 0, key.offset, key.objectid, *tree_block_level); break; case BTRFS_EXTENT_DATA_REF_KEY: dref = btrfs_item_ptr(leaf, i, struct btrfs_extent_data_ref); ret = add_extent_data_ref(fs_info, leaf, dref, *bytenr, *num_bytes); break; case BTRFS_SHARED_DATA_REF_KEY: sref = btrfs_item_ptr(leaf, i, struct btrfs_shared_data_ref); count = btrfs_shared_data_ref_count(leaf, sref); ret = add_shared_data_ref(fs_info, key.offset, count, *bytenr, *num_bytes); break; default: break; } if (ret) break; } return ret; } /* Walk down to the leaf from the given level */ static int walk_down_tree(struct btrfs_root *root, struct btrfs_path *path, int level, u64 *bytenr, u64 *num_bytes, int *tree_block_level) { struct extent_buffer *eb; int ret = 0; while (level >= 0) { if (level) { eb = btrfs_read_node_slot(path->nodes[level], path->slots[level]); if (IS_ERR(eb)) return PTR_ERR(eb); btrfs_tree_read_lock(eb); path->nodes[level-1] = eb; path->slots[level-1] = 0; path->locks[level-1] = BTRFS_READ_LOCK; } else { ret = process_leaf(root, path, bytenr, num_bytes, tree_block_level); if (ret) break; } level--; } return ret; } /* Walk up to the next node that needs to be processed */ static int walk_up_tree(struct btrfs_path *path, int *level) { int l; for (l = 0; l < BTRFS_MAX_LEVEL; l++) { if (!path->nodes[l]) continue; if (l) { path->slots[l]++; if (path->slots[l] < btrfs_header_nritems(path->nodes[l])) { *level = l; return 0; } } btrfs_tree_unlock_rw(path->nodes[l], path->locks[l]); free_extent_buffer(path->nodes[l]); path->nodes[l] = NULL; path->slots[l] = 0; path->locks[l] = 0; } return 1; } static void dump_ref_action(struct btrfs_fs_info *fs_info, struct ref_action *ra) { btrfs_err(fs_info, " Ref action %d, root %llu, ref_root %llu, parent %llu, owner %llu, offset %llu, num_refs %llu", ra->action, ra->root, ra->ref.root_objectid, ra->ref.parent, ra->ref.owner, ra->ref.offset, ra->ref.num_refs); __print_stack_trace(fs_info, ra); } /* * Dumps all the information from the block entry to printk, it's going to be * awesome. */ static void dump_block_entry(struct btrfs_fs_info *fs_info, struct block_entry *be) { struct ref_entry *ref; struct root_entry *re; struct ref_action *ra; struct rb_node *n; btrfs_err(fs_info, "dumping block entry [%llu %llu], num_refs %llu, metadata %d, from disk %d", be->bytenr, be->len, be->num_refs, be->metadata, be->from_disk); for (n = rb_first(&be->refs); n; n = rb_next(n)) { ref = rb_entry(n, struct ref_entry, node); btrfs_err(fs_info, " ref root %llu, parent %llu, owner %llu, offset %llu, num_refs %llu", ref->root_objectid, ref->parent, ref->owner, ref->offset, ref->num_refs); } for (n = rb_first(&be->roots); n; n = rb_next(n)) { re = rb_entry(n, struct root_entry, node); btrfs_err(fs_info, " root entry %llu, num_refs %llu", re->root_objectid, re->num_refs); } list_for_each_entry(ra, &be->actions, list) dump_ref_action(fs_info, ra); } /* * Called when we modify a ref for a bytenr. * * This will add an action item to the given bytenr and do sanity checks to make * sure we haven't messed something up. If we are making a new allocation and * this block entry has history we will delete all previous actions as long as * our sanity checks pass as they are no longer needed. */ int btrfs_ref_tree_mod(struct btrfs_fs_info *fs_info, struct btrfs_ref *generic_ref) { struct ref_entry *ref = NULL, *exist; struct ref_action *ra = NULL; struct block_entry *be = NULL; struct root_entry *re = NULL; int action = generic_ref->action; int ret = 0; bool metadata; u64 bytenr = generic_ref->bytenr; u64 num_bytes = generic_ref->len; u64 parent = generic_ref->parent; u64 ref_root = 0; u64 owner = 0; u64 offset = 0; if (!btrfs_test_opt(fs_info, REF_VERIFY)) return 0; if (generic_ref->type == BTRFS_REF_METADATA) { if (!parent) ref_root = generic_ref->tree_ref.ref_root; owner = generic_ref->tree_ref.level; } else if (!parent) { ref_root = generic_ref->data_ref.ref_root; owner = generic_ref->data_ref.ino; offset = generic_ref->data_ref.offset; } metadata = owner < BTRFS_FIRST_FREE_OBJECTID; ref = kzalloc(sizeof(struct ref_entry), GFP_NOFS); ra = kmalloc(sizeof(struct ref_action), GFP_NOFS); if (!ra || !ref) { kfree(ref); kfree(ra); ret = -ENOMEM; goto out; } ref->parent = parent; ref->owner = owner; ref->root_objectid = ref_root; ref->offset = offset; ref->num_refs = (action == BTRFS_DROP_DELAYED_REF) ? -1 : 1; memcpy(&ra->ref, ref, sizeof(struct ref_entry)); /* * Save the extra info from the delayed ref in the ref action to make it * easier to figure out what is happening. The real ref's we add to the * ref tree need to reflect what we save on disk so it matches any * on-disk refs we pre-loaded. */ ra->ref.owner = owner; ra->ref.offset = offset; ra->ref.root_objectid = ref_root; __save_stack_trace(ra); INIT_LIST_HEAD(&ra->list); ra->action = action; ra->root = generic_ref->real_root; /* * This is an allocation, preallocate the block_entry in case we haven't * used it before. */ ret = -EINVAL; if (action == BTRFS_ADD_DELAYED_EXTENT) { /* * For subvol_create we'll just pass in whatever the parent root * is and the new root objectid, so let's not treat the passed * in root as if it really has a ref for this bytenr. */ be = add_block_entry(fs_info, bytenr, num_bytes, ref_root); if (IS_ERR(be)) { kfree(ref); kfree(ra); ret = PTR_ERR(be); goto out; } be->num_refs++; if (metadata) be->metadata = 1; if (be->num_refs != 1) { btrfs_err(fs_info, "re-allocated a block that still has references to it!"); dump_block_entry(fs_info, be); dump_ref_action(fs_info, ra); kfree(ref); kfree(ra); goto out_unlock; } while (!list_empty(&be->actions)) { struct ref_action *tmp; tmp = list_first_entry(&be->actions, struct ref_action, list); list_del(&tmp->list); kfree(tmp); } } else { struct root_entry *tmp; if (!parent) { re = kmalloc(sizeof(struct root_entry), GFP_NOFS); if (!re) { kfree(ref); kfree(ra); ret = -ENOMEM; goto out; } /* * This is the root that is modifying us, so it's the * one we want to lookup below when we modify the * re->num_refs. */ ref_root = generic_ref->real_root; re->root_objectid = generic_ref->real_root; re->num_refs = 0; } spin_lock(&fs_info->ref_verify_lock); be = lookup_block_entry(&fs_info->block_tree, bytenr); if (!be) { btrfs_err(fs_info, "trying to do action %d to bytenr %llu num_bytes %llu but there is no existing entry!", action, bytenr, num_bytes); dump_ref_action(fs_info, ra); kfree(ref); kfree(ra); kfree(re); goto out_unlock; } else if (be->num_refs == 0) { btrfs_err(fs_info, "trying to do action %d for a bytenr that has 0 total references", action); dump_block_entry(fs_info, be); dump_ref_action(fs_info, ra); kfree(ref); kfree(ra); kfree(re); goto out_unlock; } if (!parent) { tmp = insert_root_entry(&be->roots, re); if (tmp) { kfree(re); re = tmp; } } } exist = insert_ref_entry(&be->refs, ref); if (exist) { if (action == BTRFS_DROP_DELAYED_REF) { if (exist->num_refs == 0) { btrfs_err(fs_info, "dropping a ref for a existing root that doesn't have a ref on the block"); dump_block_entry(fs_info, be); dump_ref_action(fs_info, ra); kfree(ref); kfree(ra); goto out_unlock; } exist->num_refs--; if (exist->num_refs == 0) { rb_erase(&exist->node, &be->refs); kfree(exist); } } else if (!be->metadata) { exist->num_refs++; } else { btrfs_err(fs_info, "attempting to add another ref for an existing ref on a tree block"); dump_block_entry(fs_info, be); dump_ref_action(fs_info, ra); kfree(ref); kfree(ra); goto out_unlock; } kfree(ref); } else { if (action == BTRFS_DROP_DELAYED_REF) { btrfs_err(fs_info, "dropping a ref for a root that doesn't have a ref on the block"); dump_block_entry(fs_info, be); dump_ref_action(fs_info, ra); kfree(ref); kfree(ra); goto out_unlock; } } if (!parent && !re) { re = lookup_root_entry(&be->roots, ref_root); if (!re) { /* * This shouldn't happen because we will add our re * above when we lookup the be with !parent, but just in * case catch this case so we don't panic because I * didn't think of some other corner case. */ btrfs_err(fs_info, "failed to find root %llu for %llu", generic_ref->real_root, be->bytenr); dump_block_entry(fs_info, be); dump_ref_action(fs_info, ra); kfree(ra); goto out_unlock; } } if (action == BTRFS_DROP_DELAYED_REF) { if (re) re->num_refs--; be->num_refs--; } else if (action == BTRFS_ADD_DELAYED_REF) { be->num_refs++; if (re) re->num_refs++; } list_add_tail(&ra->list, &be->actions); ret = 0; out_unlock: spin_unlock(&fs_info->ref_verify_lock); out: if (ret) btrfs_clear_opt(fs_info->mount_opt, REF_VERIFY); return ret; } /* Free up the ref cache */ void btrfs_free_ref_cache(struct btrfs_fs_info *fs_info) { struct block_entry *be; struct rb_node *n; if (!btrfs_test_opt(fs_info, REF_VERIFY)) return; spin_lock(&fs_info->ref_verify_lock); while ((n = rb_first(&fs_info->block_tree))) { be = rb_entry(n, struct block_entry, node); rb_erase(&be->node, &fs_info->block_tree); free_block_entry(be); cond_resched_lock(&fs_info->ref_verify_lock); } spin_unlock(&fs_info->ref_verify_lock); } void btrfs_free_ref_tree_range(struct btrfs_fs_info *fs_info, u64 start, u64 len) { struct block_entry *be = NULL, *entry; struct rb_node *n; if (!btrfs_test_opt(fs_info, REF_VERIFY)) return; spin_lock(&fs_info->ref_verify_lock); n = fs_info->block_tree.rb_node; while (n) { entry = rb_entry(n, struct block_entry, node); if (entry->bytenr < start) { n = n->rb_right; } else if (entry->bytenr > start) { n = n->rb_left; } else { be = entry; break; } /* We want to get as close to start as possible */ if (be == NULL || (entry->bytenr < start && be->bytenr > start) || (entry->bytenr < start && entry->bytenr > be->bytenr)) be = entry; } /* * Could have an empty block group, maybe have something to check for * this case to verify we were actually empty? */ if (!be) { spin_unlock(&fs_info->ref_verify_lock); return; } n = &be->node; while (n) { be = rb_entry(n, struct block_entry, node); n = rb_next(n); if (be->bytenr < start && be->bytenr + be->len > start) { btrfs_err(fs_info, "block entry overlaps a block group [%llu,%llu]!", start, len); dump_block_entry(fs_info, be); continue; } if (be->bytenr < start) continue; if (be->bytenr >= start + len) break; if (be->bytenr + be->len > start + len) { btrfs_err(fs_info, "block entry overlaps a block group [%llu,%llu]!", start, len); dump_block_entry(fs_info, be); } rb_erase(&be->node, &fs_info->block_tree); free_block_entry(be); } spin_unlock(&fs_info->ref_verify_lock); } /* Walk down all roots and build the ref tree, meant to be called at mount */ int btrfs_build_ref_tree(struct btrfs_fs_info *fs_info) { struct btrfs_root *extent_root; struct btrfs_path *path; struct extent_buffer *eb; int tree_block_level = 0; u64 bytenr = 0, num_bytes = 0; int ret, level; if (!btrfs_test_opt(fs_info, REF_VERIFY)) return 0; path = btrfs_alloc_path(); if (!path) return -ENOMEM; extent_root = btrfs_extent_root(fs_info, 0); eb = btrfs_read_lock_root_node(extent_root); level = btrfs_header_level(eb); path->nodes[level] = eb; path->slots[level] = 0; path->locks[level] = BTRFS_READ_LOCK; while (1) { /* * We have to keep track of the bytenr/num_bytes we last hit * because we could have run out of space for an inline ref, and * would have had to added a ref key item which may appear on a * different leaf from the original extent item. */ ret = walk_down_tree(extent_root, path, level, &bytenr, &num_bytes, &tree_block_level); if (ret) break; ret = walk_up_tree(path, &level); if (ret < 0) break; if (ret > 0) { ret = 0; break; } } if (ret) { btrfs_clear_opt(fs_info->mount_opt, REF_VERIFY); btrfs_free_ref_cache(fs_info); } btrfs_free_path(path); return ret; }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1