Commit e7070be1 authored by Josef Bacik's avatar Josef Bacik Committed by Chris Mason

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: default avatarJosef Bacik <jbacik@fb.com>
Signed-off-by: default avatarChris Mason <clm@fb.com>
parent ec6f34e5
...@@ -213,10 +213,18 @@ static struct extent_buffer *btrfs_read_lock_root_node(struct btrfs_root *root) ...@@ -213,10 +213,18 @@ static struct extent_buffer *btrfs_read_lock_root_node(struct btrfs_root *root)
*/ */
static void add_root_to_dirty_list(struct btrfs_root *root) static void add_root_to_dirty_list(struct btrfs_root *root)
{ {
if (test_bit(BTRFS_ROOT_DIRTY, &root->state) ||
!test_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state))
return;
spin_lock(&root->fs_info->trans_lock); spin_lock(&root->fs_info->trans_lock);
if (test_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state) && if (!test_and_set_bit(BTRFS_ROOT_DIRTY, &root->state)) {
list_empty(&root->dirty_list)) { /* Want the extent tree to be the last on the list */
list_add(&root->dirty_list, if (root->objectid == BTRFS_EXTENT_TREE_OBJECTID)
list_move_tail(&root->dirty_list,
&root->fs_info->dirty_cowonly_roots);
else
list_move(&root->dirty_list,
&root->fs_info->dirty_cowonly_roots); &root->fs_info->dirty_cowonly_roots);
} }
spin_unlock(&root->fs_info->trans_lock); spin_unlock(&root->fs_info->trans_lock);
......
...@@ -1775,6 +1775,7 @@ struct btrfs_subvolume_writers { ...@@ -1775,6 +1775,7 @@ struct btrfs_subvolume_writers {
#define BTRFS_ROOT_DEFRAG_RUNNING 6 #define BTRFS_ROOT_DEFRAG_RUNNING 6
#define BTRFS_ROOT_FORCE_COW 7 #define BTRFS_ROOT_FORCE_COW 7
#define BTRFS_ROOT_MULTI_LOG_TASKS 8 #define BTRFS_ROOT_MULTI_LOG_TASKS 8
#define BTRFS_ROOT_DIRTY 9
/* /*
* in ram representation of the tree. extent_root is used for all allocations * in ram representation of the tree. extent_root is used for all allocations
......
...@@ -1020,6 +1020,7 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans, ...@@ -1020,6 +1020,7 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,
u64 old_root_bytenr; u64 old_root_bytenr;
u64 old_root_used; u64 old_root_used;
struct btrfs_root *tree_root = root->fs_info->tree_root; struct btrfs_root *tree_root = root->fs_info->tree_root;
bool extent_root = (root->objectid == BTRFS_EXTENT_TREE_OBJECTID);
old_root_used = btrfs_root_used(&root->root_item); old_root_used = btrfs_root_used(&root->root_item);
btrfs_write_dirty_block_groups(trans, root); btrfs_write_dirty_block_groups(trans, root);
...@@ -1038,10 +1039,15 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans, ...@@ -1038,10 +1039,15 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,
return ret; return ret;
old_root_used = btrfs_root_used(&root->root_item); old_root_used = btrfs_root_used(&root->root_item);
if (extent_root) {
ret = btrfs_write_dirty_block_groups(trans, root); ret = btrfs_write_dirty_block_groups(trans, root);
if (ret) if (ret)
return ret; return ret;
} }
ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
if (ret)
return ret;
}
return 0; return 0;
} }
...@@ -1097,6 +1103,7 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans, ...@@ -1097,6 +1103,7 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,
next = fs_info->dirty_cowonly_roots.next; next = fs_info->dirty_cowonly_roots.next;
list_del_init(next); list_del_init(next);
root = list_entry(next, struct btrfs_root, dirty_list); root = list_entry(next, struct btrfs_root, dirty_list);
clear_bit(BTRFS_ROOT_DIRTY, &root->state);
if (root != fs_info->extent_root) if (root != fs_info->extent_root)
list_add_tail(&root->dirty_list, list_add_tail(&root->dirty_list,
......
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