aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/send.c
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/send.c
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/send.c')
-rw-r--r--fs/btrfs/send.c57
1 files changed, 2 insertions, 55 deletions
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