Commit 30b0463a authored by Jan Schmidt's avatar Jan Schmidt Committed by Josef Bacik

Btrfs: fix accessing the root pointer in tree mod log functions

The tree mod log functions were accessing root->node->... directly, without
use of btrfs_root_node() or explicit rcu locking. This could lead to an
extent buffer reference being leaked and another reference being freed too
early when preemtion was enabled.
Signed-off-by: default avatarJan Schmidt <list.btrfs@jan-o-sch.net>
Signed-off-by: default avatarJosef Bacik <jbacik@fusionio.com>
parent 90f8d62e
...@@ -1069,11 +1069,11 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans, ...@@ -1069,11 +1069,11 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans,
*/ */
static struct tree_mod_elem * static struct tree_mod_elem *
__tree_mod_log_oldest_root(struct btrfs_fs_info *fs_info, __tree_mod_log_oldest_root(struct btrfs_fs_info *fs_info,
struct btrfs_root *root, u64 time_seq) struct extent_buffer *eb_root, u64 time_seq)
{ {
struct tree_mod_elem *tm; struct tree_mod_elem *tm;
struct tree_mod_elem *found = NULL; struct tree_mod_elem *found = NULL;
u64 root_logical = root->node->start; u64 root_logical = eb_root->start;
int looped = 0; int looped = 0;
if (!time_seq) if (!time_seq)
...@@ -1107,7 +1107,6 @@ __tree_mod_log_oldest_root(struct btrfs_fs_info *fs_info, ...@@ -1107,7 +1107,6 @@ __tree_mod_log_oldest_root(struct btrfs_fs_info *fs_info,
found = tm; found = tm;
root_logical = tm->old_root.logical; root_logical = tm->old_root.logical;
BUG_ON(root_logical == root->node->start);
looped = 1; looped = 1;
} }
...@@ -1245,30 +1244,31 @@ static inline struct extent_buffer * ...@@ -1245,30 +1244,31 @@ static inline struct extent_buffer *
get_old_root(struct btrfs_root *root, u64 time_seq) get_old_root(struct btrfs_root *root, u64 time_seq)
{ {
struct tree_mod_elem *tm; struct tree_mod_elem *tm;
struct extent_buffer *eb; struct extent_buffer *eb = NULL;
struct extent_buffer *eb_root;
struct extent_buffer *old; struct extent_buffer *old;
struct tree_mod_root *old_root = NULL; struct tree_mod_root *old_root = NULL;
u64 old_generation = 0; u64 old_generation = 0;
u64 logical; u64 logical;
u32 blocksize; u32 blocksize;
eb = btrfs_read_lock_root_node(root); eb_root = btrfs_read_lock_root_node(root);
tm = __tree_mod_log_oldest_root(root->fs_info, root, time_seq); tm = __tree_mod_log_oldest_root(root->fs_info, eb_root, time_seq);
if (!tm) if (!tm)
return root->node; return eb_root;
if (tm->op == MOD_LOG_ROOT_REPLACE) { if (tm->op == MOD_LOG_ROOT_REPLACE) {
old_root = &tm->old_root; old_root = &tm->old_root;
old_generation = tm->generation; old_generation = tm->generation;
logical = old_root->logical; logical = old_root->logical;
} else { } else {
logical = root->node->start; logical = eb_root->start;
} }
tm = tree_mod_log_search(root->fs_info, logical, time_seq); tm = tree_mod_log_search(root->fs_info, logical, time_seq);
if (old_root && tm && tm->op != MOD_LOG_KEY_REMOVE_WHILE_FREEING) { if (old_root && tm && tm->op != MOD_LOG_KEY_REMOVE_WHILE_FREEING) {
btrfs_tree_read_unlock(root->node); btrfs_tree_read_unlock(eb_root);
free_extent_buffer(root->node); free_extent_buffer(eb_root);
blocksize = btrfs_level_size(root, old_root->level); blocksize = btrfs_level_size(root, old_root->level);
old = read_tree_block(root, logical, blocksize, 0); old = read_tree_block(root, logical, blocksize, 0);
if (!old) { if (!old) {
...@@ -1280,13 +1280,13 @@ get_old_root(struct btrfs_root *root, u64 time_seq) ...@@ -1280,13 +1280,13 @@ get_old_root(struct btrfs_root *root, u64 time_seq)
free_extent_buffer(old); free_extent_buffer(old);
} }
} else if (old_root) { } else if (old_root) {
btrfs_tree_read_unlock(root->node); btrfs_tree_read_unlock(eb_root);
free_extent_buffer(root->node); free_extent_buffer(eb_root);
eb = alloc_dummy_extent_buffer(logical, root->nodesize); eb = alloc_dummy_extent_buffer(logical, root->nodesize);
} else { } else {
eb = btrfs_clone_extent_buffer(root->node); eb = btrfs_clone_extent_buffer(eb_root);
btrfs_tree_read_unlock(root->node); btrfs_tree_read_unlock(eb_root);
free_extent_buffer(root->node); free_extent_buffer(eb_root);
} }
if (!eb) if (!eb)
...@@ -1296,7 +1296,7 @@ get_old_root(struct btrfs_root *root, u64 time_seq) ...@@ -1296,7 +1296,7 @@ get_old_root(struct btrfs_root *root, u64 time_seq)
if (old_root) { if (old_root) {
btrfs_set_header_bytenr(eb, eb->start); btrfs_set_header_bytenr(eb, eb->start);
btrfs_set_header_backref_rev(eb, BTRFS_MIXED_BACKREF_REV); btrfs_set_header_backref_rev(eb, BTRFS_MIXED_BACKREF_REV);
btrfs_set_header_owner(eb, root->root_key.objectid); btrfs_set_header_owner(eb, btrfs_header_owner(eb_root));
btrfs_set_header_level(eb, old_root->level); btrfs_set_header_level(eb, old_root->level);
btrfs_set_header_generation(eb, old_generation); btrfs_set_header_generation(eb, old_generation);
} }
...@@ -1313,15 +1313,15 @@ int btrfs_old_root_level(struct btrfs_root *root, u64 time_seq) ...@@ -1313,15 +1313,15 @@ int btrfs_old_root_level(struct btrfs_root *root, u64 time_seq)
{ {
struct tree_mod_elem *tm; struct tree_mod_elem *tm;
int level; int level;
struct extent_buffer *eb_root = btrfs_root_node(root);
tm = __tree_mod_log_oldest_root(root->fs_info, root, time_seq); tm = __tree_mod_log_oldest_root(root->fs_info, eb_root, time_seq);
if (tm && tm->op == MOD_LOG_ROOT_REPLACE) { if (tm && tm->op == MOD_LOG_ROOT_REPLACE) {
level = tm->old_root.level; level = tm->old_root.level;
} else { } else {
rcu_read_lock(); level = btrfs_header_level(eb_root);
level = btrfs_header_level(root->node);
rcu_read_unlock();
} }
free_extent_buffer(eb_root);
return level; return level;
} }
......
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