aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/ctree.c
diff options
context:
space:
mode:
authorJan Schmidt <list.btrfs@jan-o-sch.net>2012-06-04 10:54:57 -0400
committerJan Schmidt <list.btrfs@jan-o-sch.net>2012-06-14 12:44:21 -0400
commit8ba97a15e7d4f70b9af71fa1db86a28dd17ad1b2 (patch)
tree08a2e49743eee382eca3e4b12415b372f898e3d8 /fs/btrfs/ctree.c
parentf617e2fd52484fb74236a597d0f9068ec7d9d2dd (diff)
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: Jan Schmidt <list.btrfs@jan-o-sch.net>
Diffstat (limited to 'fs/btrfs/ctree.c')
-rw-r--r--fs/btrfs/ctree.c20
1 files changed, 16 insertions, 4 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 836e4e03edca..2cde7b0a0106 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -1143,6 +1143,13 @@ tree_mod_log_rewind(struct btrfs_fs_info *fs_info, struct extent_buffer *eb,
1143 return eb_rewin; 1143 return eb_rewin;
1144} 1144}
1145 1145
1146/*
1147 * get_old_root() rewinds the state of @root's root node to the given @time_seq
1148 * value. If there are no changes, the current root->root_node is returned. If
1149 * anything changed in between, there's a fresh buffer allocated on which the
1150 * rewind operations are done. In any case, the returned buffer is read locked.
1151 * Returns NULL on error (with no locks held).
1152 */
1146static inline struct extent_buffer * 1153static inline struct extent_buffer *
1147get_old_root(struct btrfs_root *root, u64 time_seq) 1154get_old_root(struct btrfs_root *root, u64 time_seq)
1148{ 1155{
@@ -1151,6 +1158,7 @@ get_old_root(struct btrfs_root *root, u64 time_seq)
1151 struct tree_mod_root *old_root; 1158 struct tree_mod_root *old_root;
1152 u64 old_generation; 1159 u64 old_generation;
1153 1160
1161 eb = btrfs_read_lock_root_node(root);
1154 tm = __tree_mod_log_oldest_root(root->fs_info, root, time_seq); 1162 tm = __tree_mod_log_oldest_root(root->fs_info, root, time_seq);
1155 if (!tm) 1163 if (!tm)
1156 return root->node; 1164 return root->node;
@@ -1173,15 +1181,21 @@ get_old_root(struct btrfs_root *root, u64 time_seq)
1173 /* there's a root replace operation for the current root */ 1181 /* there's a root replace operation for the current root */
1174 eb = alloc_dummy_extent_buffer(tm->index << PAGE_CACHE_SHIFT, 1182 eb = alloc_dummy_extent_buffer(tm->index << PAGE_CACHE_SHIFT,
1175 root->nodesize); 1183 root->nodesize);
1184 }
1185 btrfs_tree_read_unlock(root->node);
1186 free_extent_buffer(root->node);
1187 if (!eb)
1188 return NULL;
1189 btrfs_tree_read_lock(eb);
1190 if (old_root->logical != root->node->start) {
1176 btrfs_set_header_bytenr(eb, eb->start); 1191 btrfs_set_header_bytenr(eb, eb->start);
1177 btrfs_set_header_backref_rev(eb, BTRFS_MIXED_BACKREF_REV); 1192 btrfs_set_header_backref_rev(eb, BTRFS_MIXED_BACKREF_REV);
1178 btrfs_set_header_owner(eb, root->root_key.objectid); 1193 btrfs_set_header_owner(eb, root->root_key.objectid);
1179 } 1194 }
1180 if (!eb)
1181 return NULL;
1182 btrfs_set_header_level(eb, old_root->level); 1195 btrfs_set_header_level(eb, old_root->level);
1183 btrfs_set_header_generation(eb, old_generation); 1196 btrfs_set_header_generation(eb, old_generation);
1184 __tree_mod_log_rewind(eb, time_seq, tm); 1197 __tree_mod_log_rewind(eb, time_seq, tm);
1198 extent_buffer_get(eb);
1185 1199
1186 return eb; 1200 return eb;
1187} 1201}
@@ -2612,9 +2626,7 @@ int btrfs_search_old_slot(struct btrfs_root *root, struct btrfs_key *key,
2612 2626
2613again: 2627again:
2614 b = get_old_root(root, time_seq); 2628 b = get_old_root(root, time_seq);
2615 extent_buffer_get(b);
2616 level = btrfs_header_level(b); 2629 level = btrfs_header_level(b);
2617 btrfs_tree_read_lock(b);
2618 p->locks[level] = BTRFS_READ_LOCK; 2630 p->locks[level] = BTRFS_READ_LOCK;
2619 2631
2620 while (b) { 2632 while (b) {