Skip to content

Commit

Permalink
Btrfs: ensure send always works on roots without orphans
Browse files Browse the repository at this point in the history
Move the logic from the snapshot creation ioctl into send. This avoids
doing the transaction commit if send isn't used, and ensures that if
a crash/reboot happens after the transaction commit that created the
snapshot and before the transaction commit that switched the commit
root, send will not get a commit root that differs from the main root
(that has orphan items).

Signed-off-by: Filipe Manana <fdmanana@suse.com>
Signed-off-by: Chris Mason <clm@fb.com>
  • Loading branch information
fdmanana authored and masoncl committed Nov 25, 2014
1 parent 758eb51 commit e5fa8f8
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 29 deletions.
29 changes: 0 additions & 29 deletions fs/btrfs/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -717,35 +717,6 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
if (ret)
goto fail;

/*
* If orphan cleanup did remove any orphans, it means the tree was
* modified and therefore the commit root is not the same as the
* current root anymore. This is a problem, because send uses the
* commit root and therefore can see inode items that don't exist
* in the current root anymore, and for example make calls to
* btrfs_iget, which will do tree lookups based on the current root
* and not on the commit root. Those lookups will fail, returning a
* -ESTALE error, and making send fail with that error. So make sure
* a send does not see any orphans we have just removed, and that it
* will see the same inodes regardless of whether a transaction
* commit happened before it started (meaning that the commit root
* will be the same as the current root) or not.
*/
if (readonly && pending_snapshot->snap->node !=
pending_snapshot->snap->commit_root) {
trans = btrfs_join_transaction(pending_snapshot->snap);
if (IS_ERR(trans) && PTR_ERR(trans) != -ENOENT) {
ret = PTR_ERR(trans);
goto fail;
}
if (!IS_ERR(trans)) {
ret = btrfs_commit_transaction(trans,
pending_snapshot->snap);
if (ret)
goto fail;
}
}

inode = btrfs_lookup_dentry(dentry->d_parent->d_inode, dentry);
if (IS_ERR(inode)) {
ret = PTR_ERR(inode);
Expand Down
49 changes: 49 additions & 0 deletions fs/btrfs/send.c
Original file line number Diff line number Diff line change
Expand Up @@ -5507,6 +5507,51 @@ static int send_subvol(struct send_ctx *sctx)
return ret;
}

/*
* If orphan cleanup did remove any orphans from a root, it means the tree
* was modified and therefore the commit root is not the same as the current
* root anymore. This is a problem, because send uses the commit root and
* therefore can see inode items that don't exist in the current root anymore,
* and for example make calls to btrfs_iget, which will do tree lookups based
* on the current root and not on the commit root. Those lookups will fail,
* returning a -ESTALE error, and making send fail with that error. So make
* sure a send does not see any orphans we have just removed, and that it will
* see the same inodes regardless of whether a transaction commit happened
* before it started (meaning that the commit root will be the same as the
* current root) or not.
*/
static int ensure_commit_roots_uptodate(struct send_ctx *sctx)
{
int i;
struct btrfs_trans_handle *trans = NULL;

again:
if (sctx->parent_root &&
sctx->parent_root->node != sctx->parent_root->commit_root)
goto commit_trans;

for (i = 0; i < sctx->clone_roots_cnt; i++)
if (sctx->clone_roots[i].root->node !=
sctx->clone_roots[i].root->commit_root)
goto commit_trans;

if (trans)
return btrfs_end_transaction(trans, sctx->send_root);

return 0;

commit_trans:
/* Use any root, all fs roots will get their commit roots updated. */
if (!trans) {
trans = btrfs_join_transaction(sctx->send_root);
if (IS_ERR(trans))
return PTR_ERR(trans);
goto again;
}

return btrfs_commit_transaction(trans, sctx->send_root);
}

static void btrfs_root_dec_send_in_progress(struct btrfs_root* root)
{
spin_lock(&root->root_item_lock);
Expand Down Expand Up @@ -5728,6 +5773,10 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
NULL);
sort_clone_roots = 1;

ret = ensure_commit_roots_uptodate(sctx);
if (ret)
goto out;

current->journal_info = BTRFS_SEND_TRANS_STUB;
ret = send_subvol(sctx);
current->journal_info = NULL;
Expand Down

0 comments on commit e5fa8f8

Please sign in to comment.