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:
parent
84f7d8e624
commit
fd708b81d9
7 changed files with 1138 additions and 0 deletions
|
@ -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.
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -2920,6 +2922,9 @@ retry_root_backup:
|
|||
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)) {
|
||||
|
@ -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;
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
#include "math.h"
|
||||
#include "sysfs.h"
|
||||
#include "qgroup.h"
|
||||
#include "ref-verify.h"
|
||||
|
||||
#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 &&
|
||||
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,
|
||||
|
@ -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,
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
|
1031
fs/btrfs/ref-verify.c
Normal file
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
62
fs/btrfs/ref-verify.h
Normal 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__ */
|
Loading…
Reference in a new issue