aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorJosef Bacik <jbacik@fb.com>2014-03-13 15:42:13 -0400
committerChris Mason <clm@fb.com>2014-04-06 20:39:30 -0400
commit9e351cc862b098d8ec8f8022d110932490794925 (patch)
tree97320881e91cfc1190a35516bb4bcfb2c95698e4 /fs/btrfs
parenta26e8c9f75b0bfd8cccc9e8f110737b136eb5994 (diff)
Btrfs: remove transaction from send
Lets try this again. We can deadlock the box if we send on a box and try to write onto the same fs with the app that is trying to listen to the send pipe. This is because the writer could get stuck waiting for a transaction commit which is being blocked by the send. So fix this by making sure looking at the commit roots is always going to be consistent. We do this by keeping track of which roots need to have their commit roots swapped during commit, and then taking the commit_root_sem and swapping them all at once. Then make sure we take a read lock on the commit_root_sem in cases where we search the commit root to make sure we're always looking at a consistent view of the commit roots. Previously we had problems with this because we would swap a fs tree commit root and then swap the extent tree commit root independently which would cause the backref walking code to screw up sometimes. With this patch we no longer deadlock and pass all the weird send/receive corner cases. Thanks, Reportedy-by: Hugo Mills <hugo@carfax.org.uk> Signed-off-by: Josef Bacik <jbacik@fb.com> Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/backref.c33
-rw-r--r--fs/btrfs/ctree.c88
-rw-r--r--fs/btrfs/ctree.h3
-rw-r--r--fs/btrfs/disk-io.c3
-rw-r--r--fs/btrfs/extent-tree.c20
-rw-r--r--fs/btrfs/inode-map.c14
-rw-r--r--fs/btrfs/send.c57
-rw-r--r--fs/btrfs/transaction.c45
-rw-r--r--fs/btrfs/transaction.h1
9 files changed, 77 insertions, 187 deletions
diff --git a/fs/btrfs/backref.c b/fs/btrfs/backref.c
index aad7201ad11b..10db21fa0926 100644
--- a/fs/btrfs/backref.c
+++ b/fs/btrfs/backref.c
@@ -330,7 +330,10 @@ static int __resolve_indirect_ref(struct btrfs_fs_info *fs_info,
330 goto out; 330 goto out;
331 } 331 }
332 332
333 root_level = btrfs_old_root_level(root, time_seq); 333 if (path->search_commit_root)
334 root_level = btrfs_header_level(root->commit_root);
335 else
336 root_level = btrfs_old_root_level(root, time_seq);
334 337
335 if (root_level + 1 == level) { 338 if (root_level + 1 == level) {
336 srcu_read_unlock(&fs_info->subvol_srcu, index); 339 srcu_read_unlock(&fs_info->subvol_srcu, index);
@@ -1099,9 +1102,9 @@ static int btrfs_find_all_leafs(struct btrfs_trans_handle *trans,
1099 * 1102 *
1100 * returns 0 on success, < 0 on error. 1103 * returns 0 on success, < 0 on error.
1101 */ 1104 */
1102int btrfs_find_all_roots(struct btrfs_trans_handle *trans, 1105static int __btrfs_find_all_roots(struct btrfs_trans_handle *trans,
1103 struct btrfs_fs_info *fs_info, u64 bytenr, 1106 struct btrfs_fs_info *fs_info, u64 bytenr,
1104 u64 time_seq, struct ulist **roots) 1107 u64 time_seq, struct ulist **roots)
1105{ 1108{
1106 struct ulist *tmp; 1109 struct ulist *tmp;
1107 struct ulist_node *node = NULL; 1110 struct ulist_node *node = NULL;
@@ -1137,6 +1140,20 @@ int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
1137 return 0; 1140 return 0;
1138} 1141}
1139 1142
1143int btrfs_find_all_roots(struct btrfs_trans_handle *trans,
1144 struct btrfs_fs_info *fs_info, u64 bytenr,
1145 u64 time_seq, struct ulist **roots)
1146{
1147 int ret;
1148
1149 if (!trans)
1150 down_read(&fs_info->commit_root_sem);
1151 ret = __btrfs_find_all_roots(trans, fs_info, bytenr, time_seq, roots);
1152 if (!trans)
1153 up_read(&fs_info->commit_root_sem);
1154 return ret;
1155}
1156
1140/* 1157/*
1141 * this makes the path point to (inum INODE_ITEM ioff) 1158 * this makes the path point to (inum INODE_ITEM ioff)
1142 */ 1159 */
@@ -1516,6 +1533,8 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
1516 if (IS_ERR(trans)) 1533 if (IS_ERR(trans))
1517 return PTR_ERR(trans); 1534 return PTR_ERR(trans);
1518 btrfs_get_tree_mod_seq(fs_info, &tree_mod_seq_elem); 1535 btrfs_get_tree_mod_seq(fs_info, &tree_mod_seq_elem);
1536 } else {
1537 down_read(&fs_info->commit_root_sem);
1519 } 1538 }
1520 1539
1521 ret = btrfs_find_all_leafs(trans, fs_info, extent_item_objectid, 1540 ret = btrfs_find_all_leafs(trans, fs_info, extent_item_objectid,
@@ -1526,8 +1545,8 @@ int iterate_extent_inodes(struct btrfs_fs_info *fs_info,
1526 1545
1527 ULIST_ITER_INIT(&ref_uiter); 1546 ULIST_ITER_INIT(&ref_uiter);
1528 while (!ret && (ref_node = ulist_next(refs, &ref_uiter))) { 1547 while (!ret && (ref_node = ulist_next(refs, &ref_uiter))) {
1529 ret = btrfs_find_all_roots(trans, fs_info, ref_node->val, 1548 ret = __btrfs_find_all_roots(trans, fs_info, ref_node->val,
1530 tree_mod_seq_elem.seq, &roots); 1549 tree_mod_seq_elem.seq, &roots);
1531 if (ret) 1550 if (ret)
1532 break; 1551 break;
1533 ULIST_ITER_INIT(&root_uiter); 1552 ULIST_ITER_INIT(&root_uiter);
@@ -1549,6 +1568,8 @@ out:
1549 if (!search_commit_root) { 1568 if (!search_commit_root) {
1550 btrfs_put_tree_mod_seq(fs_info, &tree_mod_seq_elem); 1569 btrfs_put_tree_mod_seq(fs_info, &tree_mod_seq_elem);
1551 btrfs_end_transaction(trans, fs_info->extent_root); 1570 btrfs_end_transaction(trans, fs_info->extent_root);
1571 } else {
1572 up_read(&fs_info->commit_root_sem);
1552 } 1573 }
1553 1574
1554 return ret; 1575 return ret;
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 88d1b1eedc9c..9d89c1648e8e 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -5360,7 +5360,6 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
5360{ 5360{
5361 int ret; 5361 int ret;
5362 int cmp; 5362 int cmp;
5363 struct btrfs_trans_handle *trans = NULL;
5364 struct btrfs_path *left_path = NULL; 5363 struct btrfs_path *left_path = NULL;
5365 struct btrfs_path *right_path = NULL; 5364 struct btrfs_path *right_path = NULL;
5366 struct btrfs_key left_key; 5365 struct btrfs_key left_key;
@@ -5378,9 +5377,6 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
5378 u64 right_blockptr; 5377 u64 right_blockptr;
5379 u64 left_gen; 5378 u64 left_gen;
5380 u64 right_gen; 5379 u64 right_gen;
5381 u64 left_start_ctransid;
5382 u64 right_start_ctransid;
5383 u64 ctransid;
5384 5380
5385 left_path = btrfs_alloc_path(); 5381 left_path = btrfs_alloc_path();
5386 if (!left_path) { 5382 if (!left_path) {
@@ -5404,21 +5400,6 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
5404 right_path->search_commit_root = 1; 5400 right_path->search_commit_root = 1;
5405 right_path->skip_locking = 1; 5401 right_path->skip_locking = 1;
5406 5402
5407 spin_lock(&left_root->root_item_lock);
5408 left_start_ctransid = btrfs_root_ctransid(&left_root->root_item);
5409 spin_unlock(&left_root->root_item_lock);
5410
5411 spin_lock(&right_root->root_item_lock);
5412 right_start_ctransid = btrfs_root_ctransid(&right_root->root_item);
5413 spin_unlock(&right_root->root_item_lock);
5414
5415 trans = btrfs_join_transaction(left_root);
5416 if (IS_ERR(trans)) {
5417 ret = PTR_ERR(trans);
5418 trans = NULL;
5419 goto out;
5420 }
5421
5422 /* 5403 /*
5423 * Strategy: Go to the first items of both trees. Then do 5404 * Strategy: Go to the first items of both trees. Then do
5424 * 5405 *
@@ -5482,67 +5463,6 @@ int btrfs_compare_trees(struct btrfs_root *left_root,
5482 advance_left = advance_right = 0; 5463 advance_left = advance_right = 0;
5483 5464
5484 while (1) { 5465 while (1) {
5485 /*
5486 * We need to make sure the transaction does not get committed
5487 * while we do anything on commit roots. This means, we need to
5488 * join and leave transactions for every item that we process.
5489 */
5490 if (trans && btrfs_should_end_transaction(trans, left_root)) {
5491 btrfs_release_path(left_path);
5492 btrfs_release_path(right_path);
5493
5494 ret = btrfs_end_transaction(trans, left_root);
5495 trans = NULL;
5496 if (ret < 0)
5497 goto out;
5498 }
5499 /* now rejoin the transaction */
5500 if (!trans) {
5501 trans = btrfs_join_transaction(left_root);
5502 if (IS_ERR(trans)) {
5503 ret = PTR_ERR(trans);
5504 trans = NULL;
5505 goto out;
5506 }
5507
5508 spin_lock(&left_root->root_item_lock);
5509 ctransid = btrfs_root_ctransid(&left_root->root_item);
5510 spin_unlock(&left_root->root_item_lock);
5511 if (ctransid != left_start_ctransid)
5512 left_start_ctransid = 0;
5513
5514 spin_lock(&right_root->root_item_lock);
5515 ctransid = btrfs_root_ctransid(&right_root->root_item);
5516 spin_unlock(&right_root->root_item_lock);
5517 if (ctransid != right_start_ctransid)
5518 right_start_ctransid = 0;
5519
5520 if (!left_start_ctransid || !right_start_ctransid) {
5521 WARN(1, KERN_WARNING
5522 "BTRFS: btrfs_compare_tree detected "
5523 "a change in one of the trees while "
5524 "iterating. This is probably a "
5525 "bug.\n");
5526 ret = -EIO;
5527 goto out;
5528 }
5529
5530 /*
5531 * the commit root may have changed, so start again
5532 * where we stopped
5533 */
5534 left_path->lowest_level = left_level;
5535 right_path->lowest_level = right_level;
5536 ret = btrfs_search_slot(NULL, left_root,
5537 &left_key, left_path, 0, 0);
5538 if (ret < 0)
5539 goto out;
5540 ret = btrfs_search_slot(NULL, right_root,
5541 &right_key, right_path, 0, 0);
5542 if (ret < 0)
5543 goto out;
5544 }
5545
5546 if (advance_left && !left_end_reached) { 5466 if (advance_left && !left_end_reached) {
5547 ret = tree_advance(left_root, left_path, &left_level, 5467 ret = tree_advance(left_root, left_path, &left_level,
5548 left_root_level, 5468 left_root_level,
@@ -5672,14 +5592,6 @@ out:
5672 btrfs_free_path(left_path); 5592 btrfs_free_path(left_path);
5673 btrfs_free_path(right_path); 5593 btrfs_free_path(right_path);
5674 kfree(tmp_buf); 5594 kfree(tmp_buf);
5675
5676 if (trans) {
5677 if (!ret)
5678 ret = btrfs_end_transaction(trans, left_root);
5679 else
5680 btrfs_end_transaction(trans, left_root);
5681 }
5682
5683 return ret; 5595 return ret;
5684} 5596}
5685 5597
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 2a9d32e193a5..4253ab257c9c 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -1440,7 +1440,7 @@ struct btrfs_fs_info {
1440 */ 1440 */
1441 struct mutex ordered_extent_flush_mutex; 1441 struct mutex ordered_extent_flush_mutex;
1442 1442
1443 struct rw_semaphore extent_commit_sem; 1443 struct rw_semaphore commit_root_sem;
1444 1444
1445 struct rw_semaphore cleanup_work_sem; 1445 struct rw_semaphore cleanup_work_sem;
1446 1446
@@ -1711,7 +1711,6 @@ struct btrfs_root {
1711 struct btrfs_block_rsv *block_rsv; 1711 struct btrfs_block_rsv *block_rsv;
1712 1712
1713 /* free ino cache stuff */ 1713 /* free ino cache stuff */
1714 struct mutex fs_commit_mutex;
1715 struct btrfs_free_space_ctl *free_ino_ctl; 1714 struct btrfs_free_space_ctl *free_ino_ctl;
1716 enum btrfs_caching_type cached; 1715 enum btrfs_caching_type cached;
1717 spinlock_t cache_lock; 1716 spinlock_t cache_lock;
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 98fe70193397..6d1ac7d46f81 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -1567,7 +1567,6 @@ int btrfs_init_fs_root(struct btrfs_root *root)
1567 root->subv_writers = writers; 1567 root->subv_writers = writers;
1568 1568
1569 btrfs_init_free_ino_ctl(root); 1569 btrfs_init_free_ino_ctl(root);
1570 mutex_init(&root->fs_commit_mutex);
1571 spin_lock_init(&root->cache_lock); 1570 spin_lock_init(&root->cache_lock);
1572 init_waitqueue_head(&root->cache_wait); 1571 init_waitqueue_head(&root->cache_wait);
1573 1572
@@ -2345,7 +2344,7 @@ int open_ctree(struct super_block *sb,
2345 mutex_init(&fs_info->transaction_kthread_mutex); 2344 mutex_init(&fs_info->transaction_kthread_mutex);
2346 mutex_init(&fs_info->cleaner_mutex); 2345 mutex_init(&fs_info->cleaner_mutex);
2347 mutex_init(&fs_info->volume_mutex); 2346 mutex_init(&fs_info->volume_mutex);
2348 init_rwsem(&fs_info->extent_commit_sem); 2347 init_rwsem(&fs_info->commit_root_sem);
2349 init_rwsem(&fs_info->cleanup_work_sem); 2348 init_rwsem(&fs_info->cleanup_work_sem);
2350 init_rwsem(&fs_info->subvol_sem); 2349 init_rwsem(&fs_info->subvol_sem);
2351 sema_init(&fs_info->uuid_tree_rescan_sem, 1); 2350 sema_init(&fs_info->uuid_tree_rescan_sem, 1);
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index e09db2c2f3b4..4d2508bbf6f0 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -419,7 +419,7 @@ static noinline void caching_thread(struct btrfs_work *work)
419again: 419again:
420 mutex_lock(&caching_ctl->mutex); 420 mutex_lock(&caching_ctl->mutex);
421 /* need to make sure the commit_root doesn't disappear */ 421 /* need to make sure the commit_root doesn't disappear */
422 down_read(&fs_info->extent_commit_sem); 422 down_read(&fs_info->commit_root_sem);
423 423
424next: 424next:
425 ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0); 425 ret = btrfs_search_slot(NULL, extent_root, &key, path, 0, 0);
@@ -443,10 +443,10 @@ next:
443 break; 443 break;
444 444
445 if (need_resched() || 445 if (need_resched() ||
446 rwsem_is_contended(&fs_info->extent_commit_sem)) { 446 rwsem_is_contended(&fs_info->commit_root_sem)) {
447 caching_ctl->progress = last; 447 caching_ctl->progress = last;
448 btrfs_release_path(path); 448 btrfs_release_path(path);
449 up_read(&fs_info->extent_commit_sem); 449 up_read(&fs_info->commit_root_sem);
450 mutex_unlock(&caching_ctl->mutex); 450 mutex_unlock(&caching_ctl->mutex);
451 cond_resched(); 451 cond_resched();
452 goto again; 452 goto again;
@@ -513,7 +513,7 @@ next:
513 513
514err: 514err:
515 btrfs_free_path(path); 515 btrfs_free_path(path);
516 up_read(&fs_info->extent_commit_sem); 516 up_read(&fs_info->commit_root_sem);
517 517
518 free_excluded_extents(extent_root, block_group); 518 free_excluded_extents(extent_root, block_group);
519 519
@@ -633,10 +633,10 @@ static int cache_block_group(struct btrfs_block_group_cache *cache,
633 return 0; 633 return 0;
634 } 634 }
635 635
636 down_write(&fs_info->extent_commit_sem); 636 down_write(&fs_info->commit_root_sem);
637 atomic_inc(&caching_ctl->count); 637 atomic_inc(&caching_ctl->count);
638 list_add_tail(&caching_ctl->list, &fs_info->caching_block_groups); 638 list_add_tail(&caching_ctl->list, &fs_info->caching_block_groups);
639 up_write(&fs_info->extent_commit_sem); 639 up_write(&fs_info->commit_root_sem);
640 640
641 btrfs_get_block_group(cache); 641 btrfs_get_block_group(cache);
642 642
@@ -5471,7 +5471,7 @@ void btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans,
5471 struct btrfs_block_group_cache *cache; 5471 struct btrfs_block_group_cache *cache;
5472 struct btrfs_space_info *space_info; 5472 struct btrfs_space_info *space_info;
5473 5473
5474 down_write(&fs_info->extent_commit_sem); 5474 down_write(&fs_info->commit_root_sem);
5475 5475
5476 list_for_each_entry_safe(caching_ctl, next, 5476 list_for_each_entry_safe(caching_ctl, next,
5477 &fs_info->caching_block_groups, list) { 5477 &fs_info->caching_block_groups, list) {
@@ -5490,7 +5490,7 @@ void btrfs_prepare_extent_commit(struct btrfs_trans_handle *trans,
5490 else 5490 else
5491 fs_info->pinned_extents = &fs_info->freed_extents[0]; 5491 fs_info->pinned_extents = &fs_info->freed_extents[0];
5492 5492
5493 up_write(&fs_info->extent_commit_sem); 5493 up_write(&fs_info->commit_root_sem);
5494 5494
5495 list_for_each_entry_rcu(space_info, &fs_info->space_info, list) 5495 list_for_each_entry_rcu(space_info, &fs_info->space_info, list)
5496 percpu_counter_set(&space_info->total_bytes_pinned, 0); 5496 percpu_counter_set(&space_info->total_bytes_pinned, 0);
@@ -8256,14 +8256,14 @@ int btrfs_free_block_groups(struct btrfs_fs_info *info)
8256 struct btrfs_caching_control *caching_ctl; 8256 struct btrfs_caching_control *caching_ctl;
8257 struct rb_node *n; 8257 struct rb_node *n;
8258 8258
8259 down_write(&info->extent_commit_sem); 8259 down_write(&info->commit_root_sem);
8260 while (!list_empty(&info->caching_block_groups)) { 8260 while (!list_empty(&info->caching_block_groups)) {
8261 caching_ctl = list_entry(info->caching_block_groups.next, 8261 caching_ctl = list_entry(info->caching_block_groups.next,
8262 struct btrfs_caching_control, list); 8262 struct btrfs_caching_control, list);
8263 list_del(&caching_ctl->list); 8263 list_del(&caching_ctl->list);
8264 put_caching_control(caching_ctl); 8264 put_caching_control(caching_ctl);
8265 } 8265 }
8266 up_write(&info->extent_commit_sem); 8266 up_write(&info->commit_root_sem);
8267 8267
8268 spin_lock(&info->block_group_cache_lock); 8268 spin_lock(&info->block_group_cache_lock);
8269 while ((n = rb_last(&info->block_group_cache_tree)) != NULL) { 8269 while ((n = rb_last(&info->block_group_cache_tree)) != NULL) {
diff --git a/fs/btrfs/inode-map.c b/fs/btrfs/inode-map.c
index ab485e57b6fe..cc8ca193d830 100644
--- a/fs/btrfs/inode-map.c
+++ b/fs/btrfs/inode-map.c
@@ -55,7 +55,7 @@ static int caching_kthread(void *data)
55 key.type = BTRFS_INODE_ITEM_KEY; 55 key.type = BTRFS_INODE_ITEM_KEY;
56again: 56again:
57 /* need to make sure the commit_root doesn't disappear */ 57 /* need to make sure the commit_root doesn't disappear */
58 mutex_lock(&root->fs_commit_mutex); 58 down_read(&fs_info->commit_root_sem);
59 59
60 ret = btrfs_search_slot(NULL, root, &key, path, 0, 0); 60 ret = btrfs_search_slot(NULL, root, &key, path, 0, 0);
61 if (ret < 0) 61 if (ret < 0)
@@ -88,7 +88,7 @@ again:
88 btrfs_item_key_to_cpu(leaf, &key, 0); 88 btrfs_item_key_to_cpu(leaf, &key, 0);
89 btrfs_release_path(path); 89 btrfs_release_path(path);
90 root->cache_progress = last; 90 root->cache_progress = last;
91 mutex_unlock(&root->fs_commit_mutex); 91 up_read(&fs_info->commit_root_sem);
92 schedule_timeout(1); 92 schedule_timeout(1);
93 goto again; 93 goto again;
94 } else 94 } else
@@ -127,7 +127,7 @@ next:
127 btrfs_unpin_free_ino(root); 127 btrfs_unpin_free_ino(root);
128out: 128out:
129 wake_up(&root->cache_wait); 129 wake_up(&root->cache_wait);
130 mutex_unlock(&root->fs_commit_mutex); 130 up_read(&fs_info->commit_root_sem);
131 131
132 btrfs_free_path(path); 132 btrfs_free_path(path);
133 133
@@ -223,11 +223,11 @@ again:
223 * or the caching work is done. 223 * or the caching work is done.
224 */ 224 */
225 225
226 mutex_lock(&root->fs_commit_mutex); 226 down_write(&root->fs_info->commit_root_sem);
227 spin_lock(&root->cache_lock); 227 spin_lock(&root->cache_lock);
228 if (root->cached == BTRFS_CACHE_FINISHED) { 228 if (root->cached == BTRFS_CACHE_FINISHED) {
229 spin_unlock(&root->cache_lock); 229 spin_unlock(&root->cache_lock);
230 mutex_unlock(&root->fs_commit_mutex); 230 up_write(&root->fs_info->commit_root_sem);
231 goto again; 231 goto again;
232 } 232 }
233 spin_unlock(&root->cache_lock); 233 spin_unlock(&root->cache_lock);
@@ -240,7 +240,7 @@ again:
240 else 240 else
241 __btrfs_add_free_space(pinned, objectid, 1); 241 __btrfs_add_free_space(pinned, objectid, 1);
242 242
243 mutex_unlock(&root->fs_commit_mutex); 243 up_write(&root->fs_info->commit_root_sem);
244 } 244 }
245} 245}
246 246
@@ -250,7 +250,7 @@ again:
250 * and others will just be dropped, because the commit root we were 250 * and others will just be dropped, because the commit root we were
251 * searching has changed. 251 * searching has changed.
252 * 252 *
253 * Must be called with root->fs_commit_mutex held 253 * Must be called with root->fs_info->commit_root_sem held
254 */ 254 */
255void btrfs_unpin_free_ino(struct btrfs_root *root) 255void btrfs_unpin_free_ino(struct btrfs_root *root)
256{ 256{
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index d00534b78382..6b5f13659317 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -1268,8 +1268,10 @@ static int find_extent_clone(struct send_ctx *sctx,
1268 } 1268 }
1269 logical = disk_byte + btrfs_file_extent_offset(eb, fi); 1269 logical = disk_byte + btrfs_file_extent_offset(eb, fi);
1270 1270
1271 down_read(&sctx->send_root->fs_info->commit_root_sem);
1271 ret = extent_from_logical(sctx->send_root->fs_info, disk_byte, tmp_path, 1272 ret = extent_from_logical(sctx->send_root->fs_info, disk_byte, tmp_path,
1272 &found_key, &flags); 1273 &found_key, &flags);
1274 up_read(&sctx->send_root->fs_info->commit_root_sem);
1273 btrfs_release_path(tmp_path); 1275 btrfs_release_path(tmp_path);
1274 1276
1275 if (ret < 0) 1277 if (ret < 0)
@@ -5367,57 +5369,21 @@ out:
5367static int full_send_tree(struct send_ctx *sctx) 5369static int full_send_tree(struct send_ctx *sctx)
5368{ 5370{
5369 int ret; 5371 int ret;
5370 struct btrfs_trans_handle *trans = NULL;
5371 struct btrfs_root *send_root = sctx->send_root; 5372 struct btrfs_root *send_root = sctx->send_root;
5372 struct btrfs_key key; 5373 struct btrfs_key key;
5373 struct btrfs_key found_key; 5374 struct btrfs_key found_key;
5374 struct btrfs_path *path; 5375 struct btrfs_path *path;
5375 struct extent_buffer *eb; 5376 struct extent_buffer *eb;
5376 int slot; 5377 int slot;
5377 u64 start_ctransid;
5378 u64 ctransid;
5379 5378
5380 path = alloc_path_for_send(); 5379 path = alloc_path_for_send();
5381 if (!path) 5380 if (!path)
5382 return -ENOMEM; 5381 return -ENOMEM;
5383 5382
5384 spin_lock(&send_root->root_item_lock);
5385 start_ctransid = btrfs_root_ctransid(&send_root->root_item);
5386 spin_unlock(&send_root->root_item_lock);
5387
5388 key.objectid = BTRFS_FIRST_FREE_OBJECTID; 5383 key.objectid = BTRFS_FIRST_FREE_OBJECTID;
5389 key.type = BTRFS_INODE_ITEM_KEY; 5384 key.type = BTRFS_INODE_ITEM_KEY;
5390 key.offset = 0; 5385 key.offset = 0;
5391 5386
5392join_trans:
5393 /*
5394 * We need to make sure the transaction does not get committed
5395 * while we do anything on commit roots. Join a transaction to prevent
5396 * this.
5397 */
5398 trans = btrfs_join_transaction(send_root);
5399 if (IS_ERR(trans)) {
5400 ret = PTR_ERR(trans);
5401 trans = NULL;
5402 goto out;
5403 }
5404
5405 /*
5406 * Make sure the tree has not changed after re-joining. We detect this
5407 * by comparing start_ctransid and ctransid. They should always match.
5408 */
5409 spin_lock(&send_root->root_item_lock);
5410 ctransid = btrfs_root_ctransid(&send_root->root_item);
5411 spin_unlock(&send_root->root_item_lock);
5412
5413 if (ctransid != start_ctransid) {
5414 WARN(1, KERN_WARNING "BTRFS: the root that you're trying to "
5415 "send was modified in between. This is "
5416 "probably a bug.\n");
5417 ret = -EIO;
5418 goto out;
5419 }
5420
5421 ret = btrfs_search_slot_for_read(send_root, &key, path, 1, 0); 5387 ret = btrfs_search_slot_for_read(send_root, &key, path, 1, 0);
5422 if (ret < 0) 5388 if (ret < 0)
5423 goto out; 5389 goto out;
@@ -5425,19 +5391,6 @@ join_trans:
5425 goto out_finish; 5391 goto out_finish;
5426 5392
5427 while (1) { 5393 while (1) {
5428 /*
5429 * When someone want to commit while we iterate, end the
5430 * joined transaction and rejoin.
5431 */
5432 if (btrfs_should_end_transaction(trans, send_root)) {
5433 ret = btrfs_end_transaction(trans, send_root);
5434 trans = NULL;
5435 if (ret < 0)
5436 goto out;
5437 btrfs_release_path(path);
5438 goto join_trans;
5439 }
5440
5441 eb = path->nodes[0]; 5394 eb = path->nodes[0];
5442 slot = path->slots[0]; 5395 slot = path->slots[0];
5443 btrfs_item_key_to_cpu(eb, &found_key, slot); 5396 btrfs_item_key_to_cpu(eb, &found_key, slot);
@@ -5465,12 +5418,6 @@ out_finish:
5465 5418
5466out: 5419out:
5467 btrfs_free_path(path); 5420 btrfs_free_path(path);
5468 if (trans) {
5469 if (!ret)
5470 ret = btrfs_end_transaction(trans, send_root);
5471 else
5472 btrfs_end_transaction(trans, send_root);
5473 }
5474 return ret; 5421 return ret;
5475} 5422}
5476 5423
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index 038177cfabbb..7579f6d0b854 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -75,10 +75,21 @@ void btrfs_put_transaction(struct btrfs_transaction *transaction)
75 } 75 }
76} 76}
77 77
78static noinline void switch_commit_root(struct btrfs_root *root) 78static noinline void switch_commit_roots(struct btrfs_transaction *trans,
79 struct btrfs_fs_info *fs_info)
79{ 80{
80 free_extent_buffer(root->commit_root); 81 struct btrfs_root *root, *tmp;
81 root->commit_root = btrfs_root_node(root); 82
83 down_write(&fs_info->commit_root_sem);
84 list_for_each_entry_safe(root, tmp, &trans->switch_commits,
85 dirty_list) {
86 list_del_init(&root->dirty_list);
87 free_extent_buffer(root->commit_root);
88 root->commit_root = btrfs_root_node(root);
89 if (is_fstree(root->objectid))
90 btrfs_unpin_free_ino(root);
91 }
92 up_write(&fs_info->commit_root_sem);
82} 93}
83 94
84static inline void extwriter_counter_inc(struct btrfs_transaction *trans, 95static inline void extwriter_counter_inc(struct btrfs_transaction *trans,
@@ -208,6 +219,7 @@ loop:
208 INIT_LIST_HEAD(&cur_trans->pending_snapshots); 219 INIT_LIST_HEAD(&cur_trans->pending_snapshots);
209 INIT_LIST_HEAD(&cur_trans->ordered_operations); 220 INIT_LIST_HEAD(&cur_trans->ordered_operations);
210 INIT_LIST_HEAD(&cur_trans->pending_chunks); 221 INIT_LIST_HEAD(&cur_trans->pending_chunks);
222 INIT_LIST_HEAD(&cur_trans->switch_commits);
211 list_add_tail(&cur_trans->list, &fs_info->trans_list); 223 list_add_tail(&cur_trans->list, &fs_info->trans_list);
212 extent_io_tree_init(&cur_trans->dirty_pages, 224 extent_io_tree_init(&cur_trans->dirty_pages,
213 fs_info->btree_inode->i_mapping); 225 fs_info->btree_inode->i_mapping);
@@ -920,9 +932,6 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,
920 return ret; 932 return ret;
921 } 933 }
922 934
923 if (root != root->fs_info->extent_root)
924 switch_commit_root(root);
925
926 return 0; 935 return 0;
927} 936}
928 937
@@ -978,15 +987,16 @@ static noinline int commit_cowonly_roots(struct btrfs_trans_handle *trans,
978 list_del_init(next); 987 list_del_init(next);
979 root = list_entry(next, struct btrfs_root, dirty_list); 988 root = list_entry(next, struct btrfs_root, dirty_list);
980 989
990 if (root != fs_info->extent_root)
991 list_add_tail(&root->dirty_list,
992 &trans->transaction->switch_commits);
981 ret = update_cowonly_root(trans, root); 993 ret = update_cowonly_root(trans, root);
982 if (ret) 994 if (ret)
983 return ret; 995 return ret;
984 } 996 }
985 997
986 down_write(&fs_info->extent_commit_sem); 998 list_add_tail(&fs_info->extent_root->dirty_list,
987 switch_commit_root(fs_info->extent_root); 999 &trans->transaction->switch_commits);
988 up_write(&fs_info->extent_commit_sem);
989
990 btrfs_after_dev_replace_commit(fs_info); 1000 btrfs_after_dev_replace_commit(fs_info);
991 1001
992 return 0; 1002 return 0;
@@ -1043,11 +1053,8 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans,
1043 smp_wmb(); 1053 smp_wmb();
1044 1054
1045 if (root->commit_root != root->node) { 1055 if (root->commit_root != root->node) {
1046 mutex_lock(&root->fs_commit_mutex); 1056 list_add_tail(&root->dirty_list,
1047 switch_commit_root(root); 1057 &trans->transaction->switch_commits);
1048 btrfs_unpin_free_ino(root);
1049 mutex_unlock(&root->fs_commit_mutex);
1050
1051 btrfs_set_root_node(&root->root_item, 1058 btrfs_set_root_node(&root->root_item,
1052 root->node); 1059 root->node);
1053 } 1060 }
@@ -1858,11 +1865,15 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
1858 1865
1859 btrfs_set_root_node(&root->fs_info->tree_root->root_item, 1866 btrfs_set_root_node(&root->fs_info->tree_root->root_item,
1860 root->fs_info->tree_root->node); 1867 root->fs_info->tree_root->node);
1861 switch_commit_root(root->fs_info->tree_root); 1868 list_add_tail(&root->fs_info->tree_root->dirty_list,
1869 &cur_trans->switch_commits);
1862 1870
1863 btrfs_set_root_node(&root->fs_info->chunk_root->root_item, 1871 btrfs_set_root_node(&root->fs_info->chunk_root->root_item,
1864 root->fs_info->chunk_root->node); 1872 root->fs_info->chunk_root->node);
1865 switch_commit_root(root->fs_info->chunk_root); 1873 list_add_tail(&root->fs_info->chunk_root->dirty_list,
1874 &cur_trans->switch_commits);
1875
1876 switch_commit_roots(cur_trans, root->fs_info);
1866 1877
1867 assert_qgroups_uptodate(trans); 1878 assert_qgroups_uptodate(trans);
1868 update_super_roots(root); 1879 update_super_roots(root);
diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h
index 2bcba89d952e..b57b924e8e03 100644
--- a/fs/btrfs/transaction.h
+++ b/fs/btrfs/transaction.h
@@ -57,6 +57,7 @@ struct btrfs_transaction {
57 struct list_head pending_snapshots; 57 struct list_head pending_snapshots;
58 struct list_head ordered_operations; 58 struct list_head ordered_operations;
59 struct list_head pending_chunks; 59 struct list_head pending_chunks;
60 struct list_head switch_commits;
60 struct btrfs_delayed_ref_root delayed_refs; 61 struct btrfs_delayed_ref_root delayed_refs;
61 int aborted; 62 int aborted;
62}; 63};