diff options
author | Jan Schmidt <list.btrfs@jan-o-sch.net> | 2012-06-04 10:54:57 -0400 |
---|---|---|
committer | Jan Schmidt <list.btrfs@jan-o-sch.net> | 2012-06-14 12:44:21 -0400 |
commit | 8ba97a15e7d4f70b9af71fa1db86a28dd17ad1b2 (patch) | |
tree | 08a2e49743eee382eca3e4b12415b372f898e3d8 /fs/btrfs/ctree.c | |
parent | f617e2fd52484fb74236a597d0f9068ec7d9d2dd (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.c | 20 |
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 | */ | ||
1146 | static inline struct extent_buffer * | 1153 | static inline struct extent_buffer * |
1147 | get_old_root(struct btrfs_root *root, u64 time_seq) | 1154 | get_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 | ||
2613 | again: | 2627 | again: |
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) { |