aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/ctree.c
diff options
context:
space:
mode:
authorJan Schmidt <list.btrfs@jan-o-sch.net>2013-04-13 09:19:53 -0400
committerJosef Bacik <jbacik@fusionio.com>2013-05-06 15:54:47 -0400
commit90f8d62ebb55e2188c1618b650378f9857f9e9a4 (patch)
tree6b1c532506eeec5998422e7c3d253e129582eb17 /fs/btrfs/ctree.c
parentceda08642459e31673d24d7968d864390d2ce5fa (diff)
Btrfs: fix tree mod log regression on root split operations
Commit d9abbf1c changed tree mod log locking around ROOT_REPLACE operations. When a tree root is split, however, we were logging removal of all elements from the root node before logging removal of half of the elements for the split operation. This leads to a BUG_ON when rewinding. This commit removes the erroneous logging of removal of all elements. Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net> Signed-off-by: Josef Bacik <jbacik@fusionio.com>
Diffstat (limited to 'fs/btrfs/ctree.c')
-rw-r--r--fs/btrfs/ctree.c55
1 files changed, 29 insertions, 26 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 9ca0f6aefa22..1180209965db 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -643,7 +643,8 @@ __tree_mod_log_free_eb(struct btrfs_fs_info *fs_info, struct extent_buffer *eb)
643static noinline int 643static noinline int
644tree_mod_log_insert_root(struct btrfs_fs_info *fs_info, 644tree_mod_log_insert_root(struct btrfs_fs_info *fs_info,
645 struct extent_buffer *old_root, 645 struct extent_buffer *old_root,
646 struct extent_buffer *new_root, gfp_t flags) 646 struct extent_buffer *new_root, gfp_t flags,
647 int log_removal)
647{ 648{
648 struct tree_mod_elem *tm; 649 struct tree_mod_elem *tm;
649 int ret; 650 int ret;
@@ -651,7 +652,8 @@ tree_mod_log_insert_root(struct btrfs_fs_info *fs_info,
651 if (tree_mod_dont_log(fs_info, NULL)) 652 if (tree_mod_dont_log(fs_info, NULL))
652 return 0; 653 return 0;
653 654
654 __tree_mod_log_free_eb(fs_info, old_root); 655 if (log_removal)
656 __tree_mod_log_free_eb(fs_info, old_root);
655 657
656 ret = tree_mod_alloc(fs_info, flags, &tm); 658 ret = tree_mod_alloc(fs_info, flags, &tm);
657 if (ret < 0) 659 if (ret < 0)
@@ -738,7 +740,7 @@ tree_mod_log_search(struct btrfs_fs_info *fs_info, u64 start, u64 min_seq)
738static noinline void 740static noinline void
739tree_mod_log_eb_copy(struct btrfs_fs_info *fs_info, struct extent_buffer *dst, 741tree_mod_log_eb_copy(struct btrfs_fs_info *fs_info, struct extent_buffer *dst,
740 struct extent_buffer *src, unsigned long dst_offset, 742 struct extent_buffer *src, unsigned long dst_offset,
741 unsigned long src_offset, int nr_items, int log_removal) 743 unsigned long src_offset, int nr_items)
742{ 744{
743 int ret; 745 int ret;
744 int i; 746 int i;
@@ -752,12 +754,10 @@ tree_mod_log_eb_copy(struct btrfs_fs_info *fs_info, struct extent_buffer *dst,
752 } 754 }
753 755
754 for (i = 0; i < nr_items; i++) { 756 for (i = 0; i < nr_items; i++) {
755 if (log_removal) { 757 ret = tree_mod_log_insert_key_locked(fs_info, src,
756 ret = tree_mod_log_insert_key_locked(fs_info, src, 758 i + src_offset,
757 i + src_offset, 759 MOD_LOG_KEY_REMOVE);
758 MOD_LOG_KEY_REMOVE); 760 BUG_ON(ret < 0);
759 BUG_ON(ret < 0);
760 }
761 ret = tree_mod_log_insert_key_locked(fs_info, dst, 761 ret = tree_mod_log_insert_key_locked(fs_info, dst,
762 i + dst_offset, 762 i + dst_offset,
763 MOD_LOG_KEY_ADD); 763 MOD_LOG_KEY_ADD);
@@ -802,11 +802,12 @@ tree_mod_log_free_eb(struct btrfs_fs_info *fs_info, struct extent_buffer *eb)
802 802
803static noinline void 803static noinline void
804tree_mod_log_set_root_pointer(struct btrfs_root *root, 804tree_mod_log_set_root_pointer(struct btrfs_root *root,
805 struct extent_buffer *new_root_node) 805 struct extent_buffer *new_root_node,
806 int log_removal)
806{ 807{
807 int ret; 808 int ret;
808 ret = tree_mod_log_insert_root(root->fs_info, root->node, 809 ret = tree_mod_log_insert_root(root->fs_info, root->node,
809 new_root_node, GFP_NOFS); 810 new_root_node, GFP_NOFS, log_removal);
810 BUG_ON(ret < 0); 811 BUG_ON(ret < 0);
811} 812}
812 813
@@ -1029,7 +1030,7 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
1029 parent_start = 0; 1030 parent_start = 0;
1030 1031
1031 extent_buffer_get(cow); 1032 extent_buffer_get(cow);
1032 tree_mod_log_set_root_pointer(root, cow); 1033 tree_mod_log_set_root_pointer(root, cow, 1);
1033 rcu_assign_pointer(root->node, cow); 1034 rcu_assign_pointer(root->node, cow);
1034 1035
1035 btrfs_free_tree_block(trans, root, buf, parent_start, 1036 btrfs_free_tree_block(trans, root, buf, parent_start,
@@ -1755,7 +1756,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
1755 goto enospc; 1756 goto enospc;
1756 } 1757 }
1757 1758
1758 tree_mod_log_set_root_pointer(root, child); 1759 tree_mod_log_set_root_pointer(root, child, 1);
1759 rcu_assign_pointer(root->node, child); 1760 rcu_assign_pointer(root->node, child);
1760 1761
1761 add_root_to_dirty_list(root); 1762 add_root_to_dirty_list(root);
@@ -2996,7 +2997,7 @@ static int push_node_left(struct btrfs_trans_handle *trans,
2996 push_items = min(src_nritems - 8, push_items); 2997 push_items = min(src_nritems - 8, push_items);
2997 2998
2998 tree_mod_log_eb_copy(root->fs_info, dst, src, dst_nritems, 0, 2999 tree_mod_log_eb_copy(root->fs_info, dst, src, dst_nritems, 0,
2999 push_items, 1); 3000 push_items);
3000 copy_extent_buffer(dst, src, 3001 copy_extent_buffer(dst, src,
3001 btrfs_node_key_ptr_offset(dst_nritems), 3002 btrfs_node_key_ptr_offset(dst_nritems),
3002 btrfs_node_key_ptr_offset(0), 3003 btrfs_node_key_ptr_offset(0),
@@ -3067,7 +3068,7 @@ static int balance_node_right(struct btrfs_trans_handle *trans,
3067 sizeof(struct btrfs_key_ptr)); 3068 sizeof(struct btrfs_key_ptr));
3068 3069
3069 tree_mod_log_eb_copy(root->fs_info, dst, src, 0, 3070 tree_mod_log_eb_copy(root->fs_info, dst, src, 0,
3070 src_nritems - push_items, push_items, 1); 3071 src_nritems - push_items, push_items);
3071 copy_extent_buffer(dst, src, 3072 copy_extent_buffer(dst, src,
3072 btrfs_node_key_ptr_offset(0), 3073 btrfs_node_key_ptr_offset(0),
3073 btrfs_node_key_ptr_offset(src_nritems - push_items), 3074 btrfs_node_key_ptr_offset(src_nritems - push_items),
@@ -3091,7 +3092,7 @@ static int balance_node_right(struct btrfs_trans_handle *trans,
3091 */ 3092 */
3092static noinline int insert_new_root(struct btrfs_trans_handle *trans, 3093static noinline int insert_new_root(struct btrfs_trans_handle *trans,
3093 struct btrfs_root *root, 3094 struct btrfs_root *root,
3094 struct btrfs_path *path, int level) 3095 struct btrfs_path *path, int level, int log_removal)
3095{ 3096{
3096 u64 lower_gen; 3097 u64 lower_gen;
3097 struct extent_buffer *lower; 3098 struct extent_buffer *lower;
@@ -3142,7 +3143,7 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans,
3142 btrfs_mark_buffer_dirty(c); 3143 btrfs_mark_buffer_dirty(c);
3143 3144
3144 old = root->node; 3145 old = root->node;
3145 tree_mod_log_set_root_pointer(root, c); 3146 tree_mod_log_set_root_pointer(root, c, log_removal);
3146 rcu_assign_pointer(root->node, c); 3147 rcu_assign_pointer(root->node, c);
3147 3148
3148 /* the super has an extra ref to root->node */ 3149 /* the super has an extra ref to root->node */
@@ -3219,18 +3220,21 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
3219 int mid; 3220 int mid;
3220 int ret; 3221 int ret;
3221 u32 c_nritems; 3222 u32 c_nritems;
3222 int tree_mod_log_removal = 1;
3223 3223
3224 c = path->nodes[level]; 3224 c = path->nodes[level];
3225 WARN_ON(btrfs_header_generation(c) != trans->transid); 3225 WARN_ON(btrfs_header_generation(c) != trans->transid);
3226 if (c == root->node) { 3226 if (c == root->node) {
3227 /* trying to split the root, lets make a new one */
3228 ret = insert_new_root(trans, root, path, level + 1);
3229 /* 3227 /*
3230 * removal of root nodes has been logged by 3228 * trying to split the root, lets make a new one
3231 * tree_mod_log_set_root_pointer due to locking 3229 *
3230 * tree mod log: We pass 0 as log_removal parameter to
3231 * insert_new_root, because that root buffer will be kept as a
3232 * normal node. We are going to log removal of half of the
3233 * elements below with tree_mod_log_eb_copy. We're holding a
3234 * tree lock on the buffer, which is why we cannot race with
3235 * other tree_mod_log users.
3232 */ 3236 */
3233 tree_mod_log_removal = 0; 3237 ret = insert_new_root(trans, root, path, level + 1, 0);
3234 if (ret) 3238 if (ret)
3235 return ret; 3239 return ret;
3236 } else { 3240 } else {
@@ -3268,8 +3272,7 @@ static noinline int split_node(struct btrfs_trans_handle *trans,
3268 (unsigned long)btrfs_header_chunk_tree_uuid(split), 3272 (unsigned long)btrfs_header_chunk_tree_uuid(split),
3269 BTRFS_UUID_SIZE); 3273 BTRFS_UUID_SIZE);
3270 3274
3271 tree_mod_log_eb_copy(root->fs_info, split, c, 0, mid, c_nritems - mid, 3275 tree_mod_log_eb_copy(root->fs_info, split, c, 0, mid, c_nritems - mid);
3272 tree_mod_log_removal);
3273 copy_extent_buffer(split, c, 3276 copy_extent_buffer(split, c,
3274 btrfs_node_key_ptr_offset(0), 3277 btrfs_node_key_ptr_offset(0),
3275 btrfs_node_key_ptr_offset(mid), 3278 btrfs_node_key_ptr_offset(mid),
@@ -3951,7 +3954,7 @@ static noinline int split_leaf(struct btrfs_trans_handle *trans,
3951 } 3954 }
3952 3955
3953 if (!path->nodes[1]) { 3956 if (!path->nodes[1]) {
3954 ret = insert_new_root(trans, root, path, 1); 3957 ret = insert_new_root(trans, root, path, 1, 1);
3955 if (ret) 3958 if (ret)
3956 return ret; 3959 return ret;
3957 } 3960 }