Commit fd708b81 authored by Josef Bacik's avatar Josef Bacik Committed by David Sterba

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: default avatarJosef Bacik <jbacik@fb.com>
Reviewed-by: default avatarDavid Sterba <dsterba@suse.com>
[ update error messages, add fixup from Dan Carpenter to handle errors
  of read_tree_block ]
Signed-off-by: default avatarDavid Sterba <dsterba@suse.com>
parent 84f7d8e6
...@@ -91,3 +91,14 @@ config BTRFS_ASSERT ...@@ -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.
...@@ -13,6 +13,7 @@ btrfs-y += super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \ ...@@ -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 \
......
...@@ -1097,6 +1097,11 @@ struct btrfs_fs_info { ...@@ -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)
......
...@@ -51,6 +51,7 @@ ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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;
......
...@@ -38,6 +38,7 @@ ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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, ...@@ -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);
......
This diff is collapsed.
/*
* 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__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment