aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosef Bacik <jbacik@fusionio.com>2013-05-20 11:26:50 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-07-20 17:16:08 -0700
commit83bcee5c1c067de22f4830382cbe9483f01ffeaf (patch)
treee1d35abab84fc550474dff33212fa9479f10885f
parenta3d56dfedb4ac5e8cdc6e6a0a54ed33ae2253c83 (diff)
Btrfs: fix estale with btrfs send
commit 139f807a1eba1e484941a98fb93ee32ad859a6a1 upstream. This fixes bugzilla 57491. If we take a snapshot of a fs with a unlink ongoing and then try to send that root we will run into problems. When comparing with a parent root we will search the parents and the send roots commit_root, which if we've just created the snapshot will include the file that needs to be evicted by the orphan cleanup. So when we find a changed extent we will try and copy that info into the send stream, but when we lookup the inode we use the normal root, which no longer has the inode because the orphan cleanup deleted it. The best solution I have for this is to check our otransid with the generation of the commit root and if they match just commit the transaction again, that way we get the changes from the orphan cleanup. With this patch the reproducer I made for this bugzilla no longer returns ESTALE when trying to do the send. Thanks, Reported-by: Chris Wilson <jakdaw@gmail.com> Signed-off-by: Josef Bacik <jbacik@fusionio.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--fs/btrfs/send.c35
1 files changed, 35 insertions, 0 deletions
diff --git a/fs/btrfs/send.c b/fs/btrfs/send.c
index c85e7c6b4598..b89fd8748f41 100644
--- a/fs/btrfs/send.c
+++ b/fs/btrfs/send.c
@@ -4579,6 +4579,41 @@ long btrfs_ioctl_send(struct file *mnt_file, void __user *arg_)
send_root = BTRFS_I(file_inode(mnt_file))->root;
fs_info = send_root->fs_info;
+ /*
+ * This is done when we lookup the root, it should already be complete
+ * by the time we get here.
+ */
+ WARN_ON(send_root->orphan_cleanup_state != ORPHAN_CLEANUP_DONE);
+
+ /*
+ * If we just created this root we need to make sure that the orphan
+ * cleanup has been done and committed since we search the commit root,
+ * so check its commit root transid with our otransid and if they match
+ * commit the transaction to make sure everything is updated.
+ */
+ down_read(&send_root->fs_info->extent_commit_sem);
+ if (btrfs_header_generation(send_root->commit_root) ==
+ btrfs_root_otransid(&send_root->root_item)) {
+ struct btrfs_trans_handle *trans;
+
+ up_read(&send_root->fs_info->extent_commit_sem);
+
+ trans = btrfs_attach_transaction_barrier(send_root);
+ if (IS_ERR(trans)) {
+ if (PTR_ERR(trans) != -ENOENT) {
+ ret = PTR_ERR(trans);
+ goto out;
+ }
+ /* ENOENT means theres no transaction */
+ } else {
+ ret = btrfs_commit_transaction(trans, send_root);
+ if (ret)
+ goto out;
+ }
+ } else {
+ up_read(&send_root->fs_info->extent_commit_sem);
+ }
+
arg = memdup_user(arg_, sizeof(*arg));
if (IS_ERR(arg)) {
ret = PTR_ERR(arg);