aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJosef Bacik <jbacik@fb.com>2014-12-16 11:54:43 -0500
committerChris Mason <clm@fb.com>2015-01-21 20:35:49 -0500
commite7070be198b34c26f39bd9010a29ce6462dc4f3e (patch)
treea51e6fa1170a2a5b3a3c0aa3e38626287904070f /fs
parentec6f34e5b552fb0a52e6aae1a5afbbb1605cc6cc (diff)
Btrfs: change how we track dirty roots
I've been overloading root->dirty_list to keep track of dirty roots and which roots need to have their commit roots switched at transaction commit time. This could cause us to lose an update to the root which could corrupt the file system. To fix this use a state bit to know if the root is dirty, and if it isn't set we go ahead and move the root to the dirty list. This way if we re-dirty the root after adding it to the switch_commit list we make sure to update it. This also makes it so that the extent root is always the last root on the dirty list to try and keep the amount of churn down at this point in the commit. Thanks, Signed-off-by: Josef Bacik <jbacik@fb.com> Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/btrfs/ctree.c16
-rw-r--r--fs/btrfs/ctree.h1
-rw-r--r--fs/btrfs/transaction.c9
3 files changed, 21 insertions, 5 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 14a72ed14ef7..97a98fc07cfc 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -213,11 +213,19 @@ static struct extent_buffer *btrfs_read_lock_root_node(struct btrfs_root *root)
213 */ 213 */
214static void add_root_to_dirty_list(struct btrfs_root *root) 214static void add_root_to_dirty_list(struct btrfs_root *root)
215{ 215{
216 if (test_bit(BTRFS_ROOT_DIRTY, &root->state) ||
217 !test_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state))
218 return;
219
216 spin_lock(&root->fs_info->trans_lock); 220 spin_lock(&root->fs_info->trans_lock);
217 if (test_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state) && 221 if (!test_and_set_bit(BTRFS_ROOT_DIRTY, &root->state)) {
218 list_empty(&root->dirty_list)) { 222 /* Want the extent tree to be the last on the list */
219 list_add(&root->dirty_list, 223 if (root->objectid == BTRFS_EXTENT_TREE_OBJECTID)
220 &root->fs_info->dirty_cowonly_roots); 224 list_move_tail(&root->dirty_list,
225 &root->fs_info->dirty_cowonly_roots);
226 else
227 list_move(&root->dirty_list,
228 &root->fs_info->dirty_cowonly_roots);
221 } 229 }
222 spin_unlock(&root->fs_info->trans_lock); 230 spin_unlock(&root->fs_info->trans_lock);
223} 231}
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 7e607416755a..45ed4dc6a0ce 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -1775,6 +1775,7 @@ struct btrfs_subvolume_writers {
1775#define BTRFS_ROOT_DEFRAG_RUNNING 6 1775#define BTRFS_ROOT_DEFRAG_RUNNING 6
1776#define BTRFS_ROOT_FORCE_COW 7 1776#define BTRFS_ROOT_FORCE_COW 7
1777#define BTRFS_ROOT_MULTI_LOG_TASKS 8 1777#define BTRFS_ROOT_MULTI_LOG_TASKS 8
1778#define BTRFS_ROOT_DIRTY 9
1778 1779
1779/* 1780/*
1780 * in ram representation of the tree. extent_root is used for all allocations 1781 * in ram representation of the tree. extent_root is used for all allocations
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index a605d4e2f2bc..aa2219ebecc9 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -1020,6 +1020,7 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,
1020 u64 old_root_bytenr; 1020 u64 old_root_bytenr;
1021 u64 old_root_used; 1021 u64 old_root_used;
1022 struct btrfs_root *tree_root = root->fs_info->tree_root; 1022 struct btrfs_root *tree_root = root->fs_info->tree_root;
1023 bool extent_root = (root->objectid == BTRFS_EXTENT_TREE_OBJECTID);
1023 1024
1024 old_root_used = btrfs_root_used(&root->root_item); 1025 old_root_used = btrfs_root_used(&root->root_item);
1025 btrfs_write_dirty_block_groups(trans, root); 1026 btrfs_write_dirty_block_groups(trans, root);
@@ -1038,7 +1039,12 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,
1038 return ret; 1039 return ret;
1039 1040
1040 old_root_used = btrfs_root_used(&root->root_item); 1041 old_root_used = btrfs_root_used(&root->root_item);
1041 ret = btrfs_write_dirty_block_groups(trans, root); 1042 if (extent_root) {
1043 ret = btrfs_write_dirty_block_groups(trans, root);
1044 if (ret)
1045 return ret;
1046 }
1047 ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
1042 if (ret) 1048 if (ret)
1043 return ret; 1049 return ret;
1044 } 1050 }
@@ -1097,6 +1103,7 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,
1097 next = fs_info->dirty_cowonly_roots.next; 1103 next = fs_info->dirty_cowonly_roots.next;
1098 list_del_init(next); 1104 list_del_init(next);
1099 root = list_entry(next, struct btrfs_root, dirty_list); 1105 root = list_entry(next, struct btrfs_root, dirty_list);
1106 clear_bit(BTRFS_ROOT_DIRTY, &root->state);
1100 1107
1101 if (root != fs_info->extent_root) 1108 if (root != fs_info->extent_root)
1102 list_add_tail(&root->dirty_list, 1109 list_add_tail(&root->dirty_list,