Btrfs: add a extent ref verify tool

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>
This commit is contained in:
Josef Bacik 2017-09-29 15:43:50 -04:00 committed by David Sterba
parent 84f7d8e624
commit fd708b81d9
7 changed files with 1138 additions and 0 deletions

View File

@ -91,3 +91,14 @@ config BTRFS_ASSERT
any of the assertions trip. This is meant for btrfs developers only. any of the assertions trip. This is meant for btrfs developers only.
If unsure, say N. 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.

View File

@ -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_POSIX_ACL) += acl.o
btrfs-$(CONFIG_BTRFS_FS_CHECK_INTEGRITY) += check-integrity.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 \ btrfs-$(CONFIG_BTRFS_FS_RUN_SANITY_TESTS) += tests/free-space-tests.o \
tests/extent-buffer-tests.o tests/btrfs-tests.o \ tests/extent-buffer-tests.o tests/btrfs-tests.o \

View File

@ -1097,6 +1097,11 @@ struct btrfs_fs_info {
u32 nodesize; u32 nodesize;
u32 sectorsize; u32 sectorsize;
u32 stripesize; 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) static inline struct btrfs_fs_info *btrfs_sb(struct super_block *sb)

View File

@ -51,6 +51,7 @@
#include "qgroup.h" #include "qgroup.h"
#include "compression.h" #include "compression.h"
#include "tree-checker.h" #include "tree-checker.h"
#include "ref-verify.h"
#ifdef CONFIG_X86 #ifdef CONFIG_X86
#include <asm/cpufeature.h> #include <asm/cpufeature.h>
@ -2509,6 +2510,7 @@ int open_ctree(struct super_block *sb,
/* readahead state */ /* readahead state */
INIT_RADIX_TREE(&fs_info->reada_tree, GFP_NOFS & ~__GFP_DIRECT_RECLAIM); INIT_RADIX_TREE(&fs_info->reada_tree, GFP_NOFS & ~__GFP_DIRECT_RECLAIM);
spin_lock_init(&fs_info->reada_lock); spin_lock_init(&fs_info->reada_lock);
btrfs_init_ref_verify(fs_info);
fs_info->thread_pool_size = min_t(unsigned long, fs_info->thread_pool_size = min_t(unsigned long,
num_online_cpus() + 2, 8); num_online_cpus() + 2, 8);
@ -2920,6 +2922,9 @@ int open_ctree(struct super_block *sb,
if (ret) if (ret)
goto fail_trans_kthread; 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 */ /* do not make disk changes in broken FS or nologreplay is given */
if (btrfs_super_log_root(disk_super) != 0 && if (btrfs_super_log_root(disk_super) != 0 &&
!btrfs_test_opt(fs_info, NOLOGREPLAY)) { !btrfs_test_opt(fs_info, NOLOGREPLAY)) {
@ -3785,6 +3790,7 @@ void close_ctree(struct btrfs_fs_info *fs_info)
cleanup_srcu_struct(&fs_info->subvol_srcu); cleanup_srcu_struct(&fs_info->subvol_srcu);
btrfs_free_stripe_hash_table(fs_info); btrfs_free_stripe_hash_table(fs_info);
btrfs_free_ref_cache(fs_info);
__btrfs_free_block_rsv(root->orphan_block_rsv); __btrfs_free_block_rsv(root->orphan_block_rsv);
root->orphan_block_rsv = NULL; root->orphan_block_rsv = NULL;

View File

@ -38,6 +38,7 @@
#include "math.h" #include "math.h"
#include "sysfs.h" #include "sysfs.h"
#include "qgroup.h" #include "qgroup.h"
#include "ref-verify.h"
#undef SCRAMBLE_DELAYED_REFS #undef SCRAMBLE_DELAYED_REFS
@ -2188,6 +2189,9 @@ int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
BUG_ON(owner < BTRFS_FIRST_FREE_OBJECTID && BUG_ON(owner < BTRFS_FIRST_FREE_OBJECTID &&
root_objectid == BTRFS_TREE_LOG_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) { if (owner < BTRFS_FIRST_FREE_OBJECTID) {
ret = btrfs_add_delayed_tree_ref(fs_info, trans, bytenr, ret = btrfs_add_delayed_tree_ref(fs_info, trans, bytenr,
num_bytes, parent, num_bytes, parent,
@ -7280,6 +7284,10 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans,
if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID) { if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID) {
int old_ref_mod, new_ref_mod; 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, ret = btrfs_add_delayed_tree_ref(fs_info, trans, buf->start,
buf->len, parent, buf->len, parent,
root->root_key.objectid, root->root_key.objectid,
@ -7343,6 +7351,11 @@ int btrfs_free_extent(struct btrfs_trans_handle *trans,
if (btrfs_is_testing(fs_info)) if (btrfs_is_testing(fs_info))
return 0; 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 log blocks never actually go into the extent allocation
* tree, just update pinning info and exit early. * tree, just update pinning info and exit early.
@ -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); 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, ret = btrfs_add_delayed_data_ref(fs_info, trans, ins->objectid,
ins->offset, 0, ins->offset, 0,
root->root_key.objectid, owner, root->root_key.objectid, owner,
@ -8542,6 +8559,9 @@ struct extent_buffer *btrfs_alloc_tree_block(struct btrfs_trans_handle *trans,
extent_op->is_data = false; extent_op->is_data = false;
extent_op->level = level; 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, ret = btrfs_add_delayed_tree_ref(fs_info, trans, ins.objectid,
ins.offset, parent, ins.offset, parent,
root_objectid, level, root_objectid, level,
@ -10391,6 +10411,8 @@ int btrfs_remove_block_group(struct btrfs_trans_handle *trans,
* remove it. * remove it.
*/ */
free_excluded_extents(fs_info, block_group); 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)); memcpy(&key, &block_group->key, sizeof(key));
index = get_block_group_index(block_group); index = get_block_group_index(block_group);

1031
fs/btrfs/ref-verify.c Normal file

File diff suppressed because it is too large Load Diff

62
fs/btrfs/ref-verify.h Normal file
View File

@ -0,0 +1,62 @@
/*
* Copyright (C) 2014 Facebook. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 021110-1307, USA.
*/
#ifndef __REF_VERIFY__
#define __REF_VERIFY__
#ifdef CONFIG_BTRFS_FS_REF_VERIFY
int btrfs_build_ref_tree(struct btrfs_fs_info *fs_info);
void btrfs_free_ref_cache(struct btrfs_fs_info *fs_info);
int btrfs_ref_tree_mod(struct btrfs_root *root, u64 bytenr, u64 num_bytes,
u64 parent, u64 ref_root, u64 owner, u64 offset,
int action);
void btrfs_free_ref_tree_range(struct btrfs_fs_info *fs_info, u64 start,
u64 len);
static inline void btrfs_init_ref_verify(struct btrfs_fs_info *fs_info)
{
spin_lock_init(&fs_info->ref_verify_lock);
fs_info->block_tree = RB_ROOT;
}
#else
static inline int btrfs_build_ref_tree(struct btrfs_fs_info *fs_info)
{
return 0;
}
static inline void btrfs_free_ref_cache(struct btrfs_fs_info *fs_info)
{
}
static inline int btrfs_ref_tree_mod(struct btrfs_root *root, u64 bytenr,
u64 num_bytes, u64 parent, u64 ref_root,
u64 owner, u64 offset, int action)
{
return 0;
}
static inline void btrfs_free_ref_tree_range(struct btrfs_fs_info *fs_info,
u64 start, u64 len)
{
}
static inline void btrfs_init_ref_verify(struct btrfs_fs_info *fs_info)
{
}
#endif /* CONFIG_BTRFS_FS_REF_VERIFY */
#endif /* _REF_VERIFY__ */