Skip to content

Commit

Permalink
Btrfs: add a extent ref verify tool
Browse files Browse the repository at this point in the history
We were having corruption issues that were tied back to problems with
the extent tree.  In order to track them down I built this tool to try
and find the culprit, which was pretty successful.  If you compile with
this tool on it will live verify every ref update that the fs makes and
make sure it is consistent and valid.  I've run this through with
xfstests and haven't gotten any false positives.  Thanks,

Signed-off-by: Josef Bacik <jbacik@fb.com>
Reviewed-by: David Sterba <dsterba@suse.com>
[ update error messages, add fixup from Dan Carpenter to handle errors
  of read_tree_block ]
Signed-off-by: David Sterba <dsterba@suse.com>
  • Loading branch information
josefbacik authored and kdave committed Oct 30, 2017
1 parent 84f7d8e commit fd708b8
Show file tree
Hide file tree
Showing 7 changed files with 1,138 additions and 0 deletions.
11 changes: 11 additions & 0 deletions fs/btrfs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,14 @@ config BTRFS_ASSERT
any of the assertions trip. This is meant for btrfs developers only.

If unsure, say N.

config BTRFS_FS_REF_VERIFY
bool "Btrfs with the ref verify tool compiled in"
depends on BTRFS_FS
default n
help
Enable run-time extent reference verification instrumentation. This
is meant to be used by btrfs developers for tracking down extent
reference problems or verifying they didn't break something.

If unsure, say N.
1 change: 1 addition & 0 deletions fs/btrfs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \

btrfs-$(CONFIG_BTRFS_FS_POSIX_ACL) += acl.o
btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.o
btrfs-$(CONFIG_BTRFS_FS_REF_VERIFY) += ref-verify.o

btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o \
tests/extent-buffer-tests.o tests/btrfs-tests.o \
Expand Down
5 changes: 5 additions & 0 deletions fs/btrfs/ctree.h
Original file line number Diff line number Diff line change
Expand Up @@ -1097,6 +1097,11 @@ struct btrfs_fs_info {
u32 nodesize;
u32 sectorsize;
u32 stripesize;

#ifdef CONFIG_BTRFS_FS_REF_VERIFY
spinlock_t ref_verify_lock;
struct rb_root block_tree;
#endif
};

static inline struct btrfs_fs_info *btrfs_sb(struct super_block *sb)
Expand Down
6 changes: 6 additions & 0 deletions fs/btrfs/disk-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
#include "qgroup.h"
#include "compression.h"
#include "tree-checker.h"
#include "ref-verify.h"

#ifdef CONFIG_X86
#include <asm/cpufeature.h>
Expand Down Expand Up @@ -2509,6 +2510,7 @@ int open_ctree(struct super_block *sb,
/* readahead state */
INIT_RADIX_TREE(&fs_info->reada_tree, GFP_NOFS & ~__GFP_DIRECT_RECLAIM);
spin_lock_init(&fs_info->reada_lock);
btrfs_init_ref_verify(fs_info);

fs_info->thread_pool_size = min_t(unsigned long,
num_online_cpus() + 2, 8);
Expand Down Expand Up @@ -2920,6 +2922,9 @@ int open_ctree(struct super_block *sb,
if (ret)
goto fail_trans_kthread;

if (btrfs_build_ref_tree(fs_info))
btrfs_err(fs_info, "couldn't build ref tree");

/* do not make disk changes in broken FS or nologreplay is given */
if (btrfs_super_log_root(disk_super) != 0 &&
!btrfs_test_opt(fs_info, NOLOGREPLAY)) {
Expand Down Expand Up @@ -3785,6 +3790,7 @@ void close_ctree(struct btrfs_fs_info *fs_info)
cleanup_srcu_struct(&fs_info->subvol_srcu);

btrfs_free_stripe_hash_table(fs_info);
btrfs_free_ref_cache(fs_info);

__btrfs_free_block_rsv(root->orphan_block_rsv);
root->orphan_block_rsv = NULL;
Expand Down
22 changes: 22 additions & 0 deletions fs/btrfs/extent-tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#include "math.h"
#include "sysfs.h"
#include "qgroup.h"
#include "ref-verify.h"

#undef SCRAMBLE_DELAYED_REFS

Expand Down Expand Up @@ -2188,6 +2189,9 @@ int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
BUG_ON(owner < BTRFS_FIRST_FREE_OBJECTID &&
root_objectid == BTRFS_TREE_LOG_OBJECTID);

btrfs_ref_tree_mod(root, bytenr, num_bytes, parent, root_objectid,
owner, offset, BTRFS_ADD_DELAYED_REF);

if (owner < BTRFS_FIRST_FREE_OBJECTID) {
ret = btrfs_add_delayed_tree_ref(fs_info, trans, bytenr,
num_bytes, parent,
Expand Down Expand Up @@ -7280,6 +7284,10 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans,
if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID) {
int old_ref_mod, new_ref_mod;

btrfs_ref_tree_mod(root, buf->start, buf->len, parent,
root->root_key.objectid,
btrfs_header_level(buf), 0,
BTRFS_DROP_DELAYED_REF);
ret = btrfs_add_delayed_tree_ref(fs_info, trans, buf->start,
buf->len, parent,
root->root_key.objectid,
Expand Down Expand Up @@ -7343,6 +7351,11 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans,
if (btrfs_is_testing(fs_info))
return 0;

if (root_objectid != BTRFS_TREE_LOG_OBJECTID)
btrfs_ref_tree_mod(root, bytenr, num_bytes, parent,
root_objectid, owner, offset,
BTRFS_DROP_DELAYED_REF);

/*
* tree log blocks never actually go into the extent allocation
* tree, just update pinning info and exit early.
Expand Down Expand Up @@ -8318,6 +8331,10 @@ int btrfs_alloc_reserved_file_extent(struct btrfs_trans_handle *trans,

BUG_ON(root->root_key.objectid == BTRFS_TREE_LOG_OBJECTID);

btrfs_ref_tree_mod(root, ins->objectid, ins->offset, 0,
root->root_key.objectid, owner, offset,
BTRFS_ADD_DELAYED_EXTENT);

ret = btrfs_add_delayed_data_ref(fs_info, trans, ins->objectid,
ins->offset, 0,
root->root_key.objectid, owner,
Expand Down Expand Up @@ -8542,6 +8559,9 @@ struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans,
extent_op->is_data = false;
extent_op->level = level;

btrfs_ref_tree_mod(root, ins.objectid, ins.offset, parent,
root_objectid, level, 0,
BTRFS_ADD_DELAYED_EXTENT);
ret = btrfs_add_delayed_tree_ref(fs_info, trans, ins.objectid,
ins.offset, parent,
root_objectid, level,
Expand Down Expand Up @@ -10391,6 +10411,8 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
* remove it.
*/
free_excluded_extents(fs_info, block_group);
btrfs_free_ref_tree_range(fs_info, block_group->key.objectid,
block_group->key.offset);

memcpy(&key, &block_group->key, sizeof(key));
index = get_block_group_index(block_group);
Expand Down
Loading

0 comments on commit fd708b8

Please sign in to comment.