Btrfs: add a extent ref verify tool
PATCH v2 1/1 Btrfs: add a extent ref verify tool
From: Josef Bacik <[email protected]>
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 <[email protected]>
---v1->v2:
- fix compile errors caught by buildbot
fs/btrfs/Kconfig | 10 + fs/btrfs/Makefile | 1 + fs/btrfs/ctree.c | 2 +- fs/btrfs/ctree.h | 14 +- fs/btrfs/disk-io.c | 15 + fs/btrfs/extent-tree.c | 44 ++- fs/btrfs/file.c | 10 +- fs/btrfs/inode.c | 9 +- fs/btrfs/ioctl.c | 2 +- fs/btrfs/ref-verify.c | 1019 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/btrfs/ref-verify.h | 51 +++ fs/btrfs/relocation.c | 14 +- fs/btrfs/super.c | 17 + fs/btrfs/tree-log.c | 2 +- 14 files changed, 1178 insertions(+), 32 deletions(-) create mode 100644 fs/btrfs/ref-verify.c create mode 100644 fs/btrfs/ref-verify.h
fs/btrfs/Kconfig +10 -0
@@ -89,3 +89,13 @@ 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 + 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.
fs/btrfs/Makefile +1 -0
@@ -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 \
fs/btrfs/ctree.c +1 -1
@@ -192,7 +192,7 @@ struct extent_buffer *btrfs_lock_root_node(struct btrfs_root *root)* tree until you end up with a lock on the root. A locked buffer * is returned, with a reference held. */ -static struct extent_buffer *btrfs_read_lock_root_node (struct btrfs_root *root) +struct extent_buffer *btrfs_read_lock_root_node (struct btrfs_root *root) {struct extent_buffer *eb;
fs/btrfs/ctree.h +11 -3
@@ -1098,6 +1098,12 @@ 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; +bool ref_verify_enabled; +#endif };static inline struct btrfs_fs_info *btrfs_sb (struct super_block *sb)
@@ -1336,6 +1342,7 @@ static inline u32 BTRFS_MAX_XATTR_SIZE(const struct btrfs_fs_info *info)#define BTRFS_MOUNT_FRAGMENT_METADATA (1 << 25)#define BTRFS_MOUNT_FREE_SPACE_TREE (1 << 26)#define BTRFS_MOUNT_NOLOGREPLAY (1 << 27) +#define BTRFS_MOUNT_REF_VERIFY (1 << 28)#define BTRFS_DEFAULT_COMMIT_INTERVAL (30)#define BTRFS_DEFAULT_MAX_INLINE (2048)
@@ -2627,7 +2634,7 @@ void btrfs_free_tree_block(struct btrfs_trans_handle *trans,struct extent_buffer *buf, u64 parent, int last_ref);int btrfs_alloc_reserved_file_extent (struct btrfs_trans_handle *trans, -u64 root_objectid,u64 owner, +struct btrfs_root *root,u64 owner,u64 offset,u64 ram_bytes,struct btrfs_key *ins); int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans,
@@ -2646,7 +2653,7 @@ int btrfs_set_disk_extent_flags(struct btrfs_trans_handle *trans, u64 bytenr, u64 num_bytes, u64 flags, int level, int is_data);int btrfs_free_extent (struct btrfs_trans_handle *trans, -struct btrfs_fs_info *fs_info, +struct btrfs_root *root,u64 bytenr,u64 num_bytes,u64 parent,u64 root_objectid,u64 owner,u64 offset);
@@ -2658,7 +2665,7 @@ void btrfs_prepare_extent_commit(struct btrfs_fs_info *fs_info);int btrfs_finish_extent_commit (struct btrfs_trans_handle *trans,struct btrfs_fs_info *fs_info);int btrfs_inc_extent_ref (struct btrfs_trans_handle *trans, -struct btrfs_fs_info *fs_info, +struct btrfs_root *root,u64 bytenr,u64 num_bytes,u64 parent,u64 root_objectid,u64 owner,u64 offset);
@@ -2803,6 +2810,7 @@ void btrfs_set_item_key_safe(struct btrfs_fs_info *fs_info, const struct btrfs_key *new_key);struct extent_buffer *btrfs_root_node (struct btrfs_root *root);struct extent_buffer *btrfs_lock_root_node (struct btrfs_root *root); +struct extent_buffer *btrfs_read_lock_root_node (struct btrfs_root *root);int btrfs_find_next_key (struct btrfs_root *root,struct btrfs_path *path,struct btrfs_key *key,int lowest_level,u64 min_trans);
fs/btrfs/disk-io.c +15 -0
@@ -50,6 +50,7 @@#include "sysfs.h" #include "qgroup.h" #include "compression.h" +#include "ref-verify.h" #ifdef CONFIG_X86 #include <asm/cpufeature.h>
@@ -2664,6 +2665,12 @@ int open_ctree(struct super_block *sb,INIT_RADIX_TREE (&fs_info->reada_tree,GFP_NOFS & ~__GFP_DIRECT_RECLAIM);spin_lock_init (&fs_info->reada_lock); +#ifdef CONFIG_BTRFS_FS_REF_VERIFY +spin_lock_init (&fs_info->ref_verify_lock); + fs_info->block_tree =RB_ROOT ; + fs_info->ref_verify_enabled = true; +#endif + fs_info->thread_pool_size = min_t(unsigned long,num_online_cpus () +2 ,8 );
@@ -3079,6 +3086,13 @@ int open_ctree(struct super_block *sb,if (ret) goto fail_trans_kthread; +#ifdef CONFIG_BTRFS_FS_REF_VERIFY +if (btrfs_build_ref_tree (fs_info)) { + fs_info->ref_verify_enabled = false; +printk (KERN_ERR "BTRFS: couldn't build ref tree\n" ); + } +#endif +/* 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 )) {
@@ -3936,6 +3950,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 ;
fs/btrfs/extent-tree.c +34 -10
135 lines not on this page · raw message
fs/btrfs/file.c +5 -5
@@ -856,7 +856,7 @@ int __btrfs_drop_extents(struct btrfs_trans_handle *trans,btrfs_mark_buffer_dirty (leaf);if (update_refs && disk_bytenr >0 ) { - ret = btrfs_inc_extent_ref(trans, fs_info, + ret = btrfs_inc_extent_ref(trans, root, disk_bytenr, num_bytes, 0, root->root_key.objectid, new_key.objectid,
@@ -940,7 +940,7 @@ int __btrfs_drop_extents(struct btrfs_trans_handle *trans, extent_end =ALIGN (extent_end, fs_info->sectorsize); }else if (update_refs && disk_bytenr >0 ) { - ret = btrfs_free_extent(trans, fs_info, + ret = btrfs_free_extent(trans, root, disk_bytenr, num_bytes, 0, root->root_key.objectid, key.objectid, key.offset -
@@ -1234,7 +1234,7 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, extent_end - split);btrfs_mark_buffer_dirty (leaf); - ret =btrfs_inc_extent_ref (trans, fs_info, bytenr, num_bytes, + ret =btrfs_inc_extent_ref (trans, root, bytenr, num_bytes,0 , root->root_key.objectid, ino, orig_offset);if (ret) {
@@ -1268,7 +1268,7 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, extent_end = other_end; del_slot = path->slots[0 ] +1 ; del_nr++; - ret =btrfs_free_extent (trans, fs_info, bytenr, num_bytes, + ret =btrfs_free_extent (trans, root, bytenr, num_bytes,0 , root->root_key.objectid, ino, orig_offset);if (ret) {
@@ -1288,7 +1288,7 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, key.offset = other_start; del_slot = path->slots[0 ]; del_nr++; - ret =btrfs_free_extent (trans, fs_info, bytenr, num_bytes, + ret =btrfs_free_extent (trans, root, bytenr, num_bytes,0 , root->root_key.objectid, ino, orig_offset);if (ret) {
fs/btrfs/inode.c +5 -4
@@ -2215,8 +2215,9 @@ static int insert_reserved_file_extent(struct btrfs_trans_handle *trans,if (ret <0 ) goto out; qg_released = ret; - ret =btrfs_alloc_reserved_file_extent (trans, root->root_key.objectid, -btrfs_ino (BTRFS_I (inode)), file_pos, qg_released, &ins); + ret =btrfs_alloc_reserved_file_extent (trans, root, +btrfs_ino (BTRFS_I (inode)), + file_pos, qg_released, &ins); out:btrfs_free_path (path);
@@ -2668,7 +2669,7 @@ static noinline int relink_extent_backref(struct btrfs_path *path,inode_add_bytes (inode, len);btrfs_release_path (path); - ret =btrfs_inc_extent_ref (trans, fs_info, new->bytenr, + ret =btrfs_inc_extent_ref (trans, root, new->bytenr, new->disk_len,0 , backref->root_id, backref->inum, new->file_pos);/* start - extent_offset */
@@ -4659,7 +4660,7 @@ int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans, root == fs_info->tree_root)) {btrfs_set_path_blocking (path); bytes_deleted += extent_num_bytes; - ret =btrfs_free_extent (trans, fs_info, extent_start, + ret =btrfs_free_extent (trans, root, extent_start, extent_num_bytes,0 ,btrfs_header_owner (leaf), ino, extent_offset);
fs/btrfs/ioctl.c +1 -1
@@ -3704,7 +3704,7 @@ static int btrfs_clone(struct inode *src, struct inode *inode,if (disko) {inode_add_bytes (inode, datal); ret = btrfs_inc_extent_ref(trans, - fs_info, + root, disko, diskl, 0, root->root_key.objectid, btrfs_ino(BTRFS_I(inode)),
fs/btrfs/ref-verify.c new file +1019 -0
1,019 lines not on this page · raw message
fs/btrfs/ref-verify.h new file +51 -0
51 lines not on this page · raw message
fs/btrfs/relocation.c +7 -7
@@ -1733,7 +1733,7 @@ int replace_file_extents(struct btrfs_trans_handle *trans, dirty =1 ; key.offset -=btrfs_file_extent_offset (leaf, fi); - ret =btrfs_inc_extent_ref (trans, fs_info, new_bytenr, + ret =btrfs_inc_extent_ref (trans, root, new_bytenr, num_bytes, parent,btrfs_header_owner (leaf), key.objectid, key.offset);
@@ -1742,7 +1742,7 @@ int replace_file_extents(struct btrfs_trans_handle *trans,break ; } - ret =btrfs_free_extent (trans, fs_info, bytenr, num_bytes, + ret =btrfs_free_extent (trans, root, bytenr, num_bytes, parent,btrfs_header_owner (leaf), key.objectid, key.offset);if (ret) {
@@ -1943,21 +1943,21 @@ int replace_path(struct btrfs_trans_handle *trans, path->slots[level], old_ptr_gen);btrfs_mark_buffer_dirty (path->nodes[level]); - ret =btrfs_inc_extent_ref (trans, fs_info, old_bytenr, + ret =btrfs_inc_extent_ref (trans, src, old_bytenr, blocksize, path->nodes[level]->start, src->root_key.objectid, level -1 ,0 );BUG_ON (ret); - ret =btrfs_inc_extent_ref (trans, fs_info, new_bytenr, + ret =btrfs_inc_extent_ref (trans, dest, new_bytenr, blocksize,0 , dest->root_key.objectid, level -1 ,0 );BUG_ON (ret); - ret =btrfs_free_extent (trans, fs_info, new_bytenr, blocksize, + ret =btrfs_free_extent (trans, src, new_bytenr, blocksize, path->nodes[level]->start, src->root_key.objectid, level -1 ,0 );BUG_ON (ret); - ret =btrfs_free_extent (trans, fs_info, old_bytenr, blocksize, + ret =btrfs_free_extent (trans, dest, old_bytenr, blocksize,0 , dest->root_key.objectid, level -1 ,0 );BUG_ON (ret);
@@ -2799,7 +2799,7 @@ static int do_relocation(struct btrfs_trans_handle *trans, trans->transid);btrfs_mark_buffer_dirty (upper->eb); - ret = btrfs_inc_extent_ref(trans, root->fs_info, + ret = btrfs_inc_extent_ref(trans, root, node->eb->start, blocksize, upper->eb->start, btrfs_header_owner(upper->eb),
fs/btrfs/super.c +17 -0
@@ -326,6 +326,9 @@ enum {#ifdef CONFIG_BTRFS_DEBUG Opt_fragment_data, Opt_fragment_metadata, Opt_fragment_all, #endif +#ifdef CONFIG_BTRFS_FS_REF_VERIFY + Opt_ref_verify, +#endif Opt_err, };
@@ -387,6 +390,9 @@ static const match_table_t tokens = { {Opt_fragment_metadata, "fragment=metadata"}, {Opt_fragment_all, "fragment=all"}, #endif +#ifdef CONFIG_BTRFS_FS_REF_VERIFY + {Opt_ref_verify, "ref_verify"}, +#endif {Opt_err, NULL}, };
@@ -817,6 +823,13 @@ int btrfs_parse_options(struct btrfs_fs_info *info, char *options,btrfs_set_opt (info->mount_opt,FRAGMENT_DATA );break ;#endif +#ifdef CONFIG_BTRFS_FS_REF_VERIFY +case Opt_ref_verify: +btrfs_info (info,"doing ref verification" ); +btrfs_set_opt (info->mount_opt, +REF_VERIFY ); +break ; +#endif case Opt_err:btrfs_info (info,"unrecognized mount option '%s'" , p); ret = -EINVAL ;
@@ -1295,6 +1308,10 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)if (btrfs_test_opt (info,FRAGMENT_METADATA ))seq_puts (seq,",fragment=metadata" );#endif +#ifdef CONFIG_BTRFS_FS_REF_VERIFY +if (btrfs_test_opt (info,REF_VERIFY )) +seq_puts (seq,",ref_verify" ); +#endif seq_printf (seq,",subvolid=%llu" ,BTRFS_I (d_inode (dentry))->root->root_key.objectid);seq_puts (seq,",subvol=" );
fs/btrfs/tree-log.c +1 -1
@@ -717,7 +717,7 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans, ret =btrfs_lookup_data_extent (fs_info, ins.objectid, ins.offset);if (ret ==0 ) { - ret =btrfs_inc_extent_ref (trans, fs_info, + ret =btrfs_inc_extent_ref (trans, root, ins.objectid, ins.offset,0 , root->root_key.objectid, key->objectid, offset);
Signature · 1 line
2.7.4