aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/backref.c
diff options
context:
space:
mode:
authorWang Shilong <wangsl.fnst@cn.fujitsu.com>2014-01-23 00:47:48 -0500
committerChris Mason <clm@fb.com>2014-01-29 10:06:23 -0500
commit538f72cdf03cad1c21c551ea542c8ce7d9fa2d81 (patch)
treed4480e764bdebea4cac696ffb25f9025c61ea240 /fs/btrfs/backref.c
parent3c9665df0c5d3f471b07efc32181459386678ebd (diff)
Btrfs: fix protection between walking backrefs and root deletion
There is a race condition between resolving indirect ref and root deletion, and we should gurantee that root can not be destroyed to avoid accessing broken tree here. Here we fix it by holding @subvol_srcu, and we will release it as soon as we have held root node lock. Signed-off-by: Wang Shilong <wangsl.fnst@cn.fujitsu.com> Signed-off-by: Josef Bacik <jbacik@fb.com> Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs/btrfs/backref.c')
-rw-r--r--fs/btrfs/backref.c13
1 files changed, 12 insertions, 1 deletions
diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
index 15384968a84a..10ae5700ab1e 100644
--- a/fs/btrfs/backref.c
+++ b/fs/btrfs/backref.c
@@ -301,23 +301,34 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
301 int ret = 0; 301 int ret = 0;
302 int root_level; 302 int root_level;
303 int level = ref->level; 303 int level = ref->level;
304 int index;
304 305
305 root_key.objectid = ref->root_id; 306 root_key.objectid = ref->root_id;
306 root_key.type = BTRFS_ROOT_ITEM_KEY; 307 root_key.type = BTRFS_ROOT_ITEM_KEY;
307 root_key.offset = (u64)-1; 308 root_key.offset = (u64)-1;
309
310 index = srcu_read_lock(&fs_info->subvol_srcu);
311
308 root = btrfs_read_fs_root_no_name(fs_info, &root_key); 312 root = btrfs_read_fs_root_no_name(fs_info, &root_key);
309 if (IS_ERR(root)) { 313 if (IS_ERR(root)) {
314 srcu_read_unlock(&fs_info->subvol_srcu, index);
310 ret = PTR_ERR(root); 315 ret = PTR_ERR(root);
311 goto out; 316 goto out;
312 } 317 }
313 318
314 root_level = btrfs_old_root_level(root, time_seq); 319 root_level = btrfs_old_root_level(root, time_seq);
315 320
316 if (root_level + 1 == level) 321 if (root_level + 1 == level) {
322 srcu_read_unlock(&fs_info->subvol_srcu, index);
317 goto out; 323 goto out;
324 }
318 325
319 path->lowest_level = level; 326 path->lowest_level = level;
320 ret = btrfs_search_old_slot(root, &ref->key_for_search, path, time_seq); 327 ret = btrfs_search_old_slot(root, &ref->key_for_search, path, time_seq);
328
329 /* root node has been locked, we can release @subvol_srcu safely here */
330 srcu_read_unlock(&fs_info->subvol_srcu, index);
331
321 pr_debug("search slot in root %llu (level %d, ref count %d) returned " 332 pr_debug("search slot in root %llu (level %d, ref count %d) returned "
322 "%d for key (%llu %u %llu)\n", 333 "%d for key (%llu %u %llu)\n",
323 ref->root_id, level, ref->count, ret, 334 ref->root_id, level, ref->count, ret,