Commit 8ba97a15 authored by Jan Schmidt's avatar Jan Schmidt

Btrfs: use btrfs_read_lock_root_node in get_old_root

get_old_root could race with root node updates because we weren't locking
the node early enough. Use btrfs_read_lock_root_node to grab the root locked
in the very beginning and release the lock as soon as possible (just like
btrfs_search_slot does).
Signed-off-by: default avatarJan Schmidt <list.btrfs@jan-o-sch.net>
parent f617e2fd
...@@ -1143,6 +1143,13 @@ tree_mod_log_rewind(struct btrfs_fs_info *fs_info, struct extent_buffer *eb, ...@@ -1143,6 +1143,13 @@ tree_mod_log_rewind(struct btrfs_fs_info *fs_info, struct extent_buffer *eb,
return eb_rewin; return eb_rewin;
} }
/*
* get_old_root() rewinds the state of @root's root node to the given @time_seq
* value. If there are no changes, the current root->root_node is returned. If
* anything changed in between, there's a fresh buffer allocated on which the
* rewind operations are done. In any case, the returned buffer is read locked.
* Returns NULL on error (with no locks held).
*/
static inline struct extent_buffer * 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)
{ {
...@@ -1151,6 +1158,7 @@ get_old_root(struct btrfs_root *root, u64 time_seq) ...@@ -1151,6 +1158,7 @@ get_old_root(struct btrfs_root *root, u64 time_seq)
struct tree_mod_root *old_root; struct tree_mod_root *old_root;
u64 old_generation; u64 old_generation;
eb = 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, root, time_seq);
if (!tm) if (!tm)
return root->node; return root->node;
...@@ -1173,15 +1181,21 @@ get_old_root(struct btrfs_root *root, u64 time_seq) ...@@ -1173,15 +1181,21 @@ get_old_root(struct btrfs_root *root, u64 time_seq)
/* there's a root replace operation for the current root */ /* there's a root replace operation for the current root */
eb = alloc_dummy_extent_buffer(tm->index << PAGE_CACHE_SHIFT, eb = alloc_dummy_extent_buffer(tm->index << PAGE_CACHE_SHIFT,
root->nodesize); root->nodesize);
}
btrfs_tree_read_unlock(root->node);
free_extent_buffer(root->node);
if (!eb)
return NULL;
btrfs_tree_read_lock(eb);
if (old_root->logical != root->node->start) {
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, root->root_key.objectid);
} }
if (!eb)
return NULL;
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);
__tree_mod_log_rewind(eb, time_seq, tm); __tree_mod_log_rewind(eb, time_seq, tm);
extent_buffer_get(eb);
return eb; return eb;
} }
...@@ -2612,9 +2626,7 @@ int btrfs_search_old_slot(struct btrfs_root *root, struct btrfs_key *key, ...@@ -2612,9 +2626,7 @@ int btrfs_search_old_slot(struct btrfs_root *root, struct btrfs_key *key,
again: again:
b = get_old_root(root, time_seq); b = get_old_root(root, time_seq);
extent_buffer_get(b);
level = btrfs_header_level(b); level = btrfs_header_level(b);
btrfs_tree_read_lock(b);
p->locks[level] = BTRFS_READ_LOCK; p->locks[level] = BTRFS_READ_LOCK;
while (b) { while (b) {
......
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