diff options
author | Jan Schmidt <list.btrfs@jan-o-sch.net> | 2013-04-13 09:19:54 -0400 |
---|---|---|
committer | Josef Bacik <jbacik@fusionio.com> | 2013-05-06 15:54:47 -0400 |
commit | 30b0463a9394d9e41596e96def5461fe33222f13 (patch) | |
tree | da71470b17140319065525250062db98f0bf5e15 /fs/btrfs/ctree.c | |
parent | 90f8d62ebb55e2188c1618b650378f9857f9e9a4 (diff) |
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: Jan Schmidt <list.btrfs@jan-o-sch.net>
Signed-off-by: Josef Bacik <jbacik@fusionio.com>
Diffstat (limited to 'fs/btrfs/ctree.c')
-rw-r--r-- | fs/btrfs/ctree.c | 40 |
1 files changed, 20 insertions, 20 deletions
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c index 1180209965db..d7e0576038fb 100644 --- a/fs/btrfs/ctree.c +++ b/fs/btrfs/ctree.c | |||
@@ -1069,11 +1069,11 @@ static noinline int __btrfs_cow_block(struct btrfs_trans_handle *trans, | |||
1069 | */ | 1069 | */ |
1070 | static struct tree_mod_elem * | 1070 | static struct tree_mod_elem * |
1071 | __tree_mod_log_oldest_root(struct btrfs_fs_info *fs_info, | 1071 | __tree_mod_log_oldest_root(struct btrfs_fs_info *fs_info, |
1072 | struct btrfs_root *root, u64 time_seq) | 1072 | struct extent_buffer *eb_root, u64 time_seq) |
1073 | { | 1073 | { |
1074 | struct tree_mod_elem *tm; | 1074 | struct tree_mod_elem *tm; |
1075 | struct tree_mod_elem *found = NULL; | 1075 | struct tree_mod_elem *found = NULL; |
1076 | u64 root_logical = root->node->start; | 1076 | u64 root_logical = eb_root->start; |
1077 | int looped = 0; | 1077 | int looped = 0; |
1078 | 1078 | ||
1079 | if (!time_seq) | 1079 | if (!time_seq) |
@@ -1107,7 +1107,6 @@ __tree_mod_log_oldest_root(struct btrfs_fs_info *fs_info, | |||
1107 | 1107 | ||
1108 | found = tm; | 1108 | found = tm; |
1109 | root_logical = tm->old_root.logical; | 1109 | root_logical = tm->old_root.logical; |
1110 | BUG_ON(root_logical == root->node->start); | ||
1111 | looped = 1; | 1110 | looped = 1; |
1112 | } | 1111 | } |
1113 | 1112 | ||
@@ -1245,30 +1244,31 @@ static inline struct extent_buffer * | |||
1245 | get_old_root(struct btrfs_root *root, u64 time_seq) | 1244 | get_old_root(struct btrfs_root *root, u64 time_seq) |
1246 | { | 1245 | { |
1247 | struct tree_mod_elem *tm; | 1246 | struct tree_mod_elem *tm; |
1248 | struct extent_buffer *eb; | 1247 | struct extent_buffer *eb = NULL; |
1248 | struct extent_buffer *eb_root; | ||
1249 | struct extent_buffer *old; | 1249 | struct extent_buffer *old; |
1250 | struct tree_mod_root *old_root = NULL; | 1250 | struct tree_mod_root *old_root = NULL; |
1251 | u64 old_generation = 0; | 1251 | u64 old_generation = 0; |
1252 | u64 logical; | 1252 | u64 logical; |
1253 | u32 blocksize; | 1253 | u32 blocksize; |
1254 | 1254 | ||
1255 | eb = btrfs_read_lock_root_node(root); | 1255 | eb_root = btrfs_read_lock_root_node(root); |
1256 | tm = __tree_mod_log_oldest_root(root->fs_info, root, time_seq); | 1256 | tm = __tree_mod_log_oldest_root(root->fs_info, eb_root, time_seq); |
1257 | if (!tm) | 1257 | if (!tm) |
1258 | return root->node; | 1258 | return eb_root; |
1259 | 1259 | ||
1260 | if (tm->op == MOD_LOG_ROOT_REPLACE) { | 1260 | if (tm->op == MOD_LOG_ROOT_REPLACE) { |
1261 | old_root = &tm->old_root; | 1261 | old_root = &tm->old_root; |
1262 | old_generation = tm->generation; | 1262 | old_generation = tm->generation; |
1263 | logical = old_root->logical; | 1263 | logical = old_root->logical; |
1264 | } else { | 1264 | } else { |
1265 | logical = root->node->start; | 1265 | logical = eb_root->start; |
1266 | } | 1266 | } |
1267 | 1267 | ||
1268 | tm = tree_mod_log_search(root->fs_info, logical, time_seq); | 1268 | tm = tree_mod_log_search(root->fs_info, logical, time_seq); |
1269 | if (old_root && tm && tm->op != MOD_LOG_KEY_REMOVE_WHILE_FREEING) { | 1269 | if (old_root && tm && tm->op != MOD_LOG_KEY_REMOVE_WHILE_FREEING) { |
1270 | btrfs_tree_read_unlock(root->node); | 1270 | btrfs_tree_read_unlock(eb_root); |
1271 | free_extent_buffer(root->node); | 1271 | free_extent_buffer(eb_root); |
1272 | blocksize = btrfs_level_size(root, old_root->level); | 1272 | blocksize = btrfs_level_size(root, old_root->level); |
1273 | old = read_tree_block(root, logical, blocksize, 0); | 1273 | old = read_tree_block(root, logical, blocksize, 0); |
1274 | if (!old) { | 1274 | if (!old) { |
@@ -1280,13 +1280,13 @@ get_old_root(struct btrfs_root *root, u64 time_seq) | |||
1280 | free_extent_buffer(old); | 1280 | free_extent_buffer(old); |
1281 | } | 1281 | } |
1282 | } else if (old_root) { | 1282 | } else if (old_root) { |
1283 | btrfs_tree_read_unlock(root->node); | 1283 | btrfs_tree_read_unlock(eb_root); |
1284 | free_extent_buffer(root->node); | 1284 | free_extent_buffer(eb_root); |
1285 | eb = alloc_dummy_extent_buffer(logical, root->nodesize); | 1285 | eb = alloc_dummy_extent_buffer(logical, root->nodesize); |
1286 | } else { | 1286 | } else { |
1287 | eb = btrfs_clone_extent_buffer(root->node); | 1287 | eb = btrfs_clone_extent_buffer(eb_root); |
1288 | btrfs_tree_read_unlock(root->node); | 1288 | btrfs_tree_read_unlock(eb_root); |
1289 | free_extent_buffer(root->node); | 1289 | free_extent_buffer(eb_root); |
1290 | } | 1290 | } |
1291 | 1291 | ||
1292 | if (!eb) | 1292 | if (!eb) |
@@ -1296,7 +1296,7 @@ get_old_root(struct btrfs_root *root, u64 time_seq) | |||
1296 | if (old_root) { | 1296 | if (old_root) { |
1297 | btrfs_set_header_bytenr(eb, eb->start); | 1297 | btrfs_set_header_bytenr(eb, eb->start); |
1298 | btrfs_set_header_backref_rev(eb, BTRFS_MIXED_BACKREF_REV); | 1298 | btrfs_set_header_backref_rev(eb, BTRFS_MIXED_BACKREF_REV); |
1299 | btrfs_set_header_owner(eb, root->root_key.objectid); | 1299 | btrfs_set_header_owner(eb, btrfs_header_owner(eb_root)); |
1300 | btrfs_set_header_level(eb, old_root->level); | 1300 | btrfs_set_header_level(eb, old_root->level); |
1301 | btrfs_set_header_generation(eb, old_generation); | 1301 | btrfs_set_header_generation(eb, old_generation); |
1302 | } | 1302 | } |
@@ -1313,15 +1313,15 @@ int btrfs_old_root_level(struct btrfs_root *root, u64 time_seq) | |||
1313 | { | 1313 | { |
1314 | struct tree_mod_elem *tm; | 1314 | struct tree_mod_elem *tm; |
1315 | int level; | 1315 | int level; |
1316 | struct extent_buffer *eb_root = btrfs_root_node(root); | ||
1316 | 1317 | ||
1317 | tm = __tree_mod_log_oldest_root(root->fs_info, root, time_seq); | 1318 | tm = __tree_mod_log_oldest_root(root->fs_info, eb_root, time_seq); |
1318 | if (tm && tm->op == MOD_LOG_ROOT_REPLACE) { | 1319 | if (tm && tm->op == MOD_LOG_ROOT_REPLACE) { |
1319 | level = tm->old_root.level; | 1320 | level = tm->old_root.level; |
1320 | } else { | 1321 | } else { |
1321 | rcu_read_lock(); | 1322 | level = btrfs_header_level(eb_root); |
1322 | level = btrfs_header_level(root->node); | ||
1323 | rcu_read_unlock(); | ||
1324 | } | 1323 | } |
1324 | free_extent_buffer(eb_root); | ||
1325 | 1325 | ||
1326 | return level; | 1326 | return level; |
1327 | } | 1327 | } |