aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorChris Mason <clm@fb.com>2014-10-15 16:50:56 -0400
committerChris Mason <clm@fb.com>2014-10-17 05:40:59 -0400
commitd37973082b453ba6b89ec07eb7b84305895d35e1 (patch)
treef9e444a57d37d65fe2458134249cc086bf92d6de /fs/btrfs
parenta43bb39b5c710e79e921fb76341bacc418cfde78 (diff)
Revert "Btrfs: race free update of commit root for ro snapshots"
This reverts commit 9c3b306e1c9e6be4be09e99a8fe2227d1005effc. Switching only one commit root during a transaction is wrong because it leads the fs into an inconsistent state. All commit roots should be switched at once, at transaction commit time, otherwise backref walking can often miss important references that were only accessible through the old commit root. Plus, the root item for the snapshot's root wasn't getting updated and preventing the next transaction commit to do it. This made several users get into random corruption issues after creation of readonly snapshots. A regression test for xfstests will follow soon. Cc: stable@vger.kernel.org # 3.17 Signed-off-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Chris Mason <clm@fb.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/inode.c36
-rw-r--r--fs/btrfs/ioctl.c33
2 files changed, 33 insertions, 36 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index fc9c0439caa3..d23362f4464e 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -5261,42 +5261,6 @@ struct inode *btrfs_lookup_dentry(struct inode *dir, struct dentry *dentry)
5261 iput(inode); 5261 iput(inode);
5262 inode = ERR_PTR(ret); 5262 inode = ERR_PTR(ret);
5263 } 5263 }
5264 /*
5265 * If orphan cleanup did remove any orphans, it means the tree
5266 * was modified and therefore the commit root is not the same as
5267 * the current root anymore. This is a problem, because send
5268 * uses the commit root and therefore can see inode items that
5269 * don't exist in the current root anymore, and for example make
5270 * calls to btrfs_iget, which will do tree lookups based on the
5271 * current root and not on the commit root. Those lookups will
5272 * fail, returning a -ESTALE error, and making send fail with
5273 * that error. So make sure a send does not see any orphans we
5274 * have just removed, and that it will see the same inodes
5275 * regardless of whether a transaction commit happened before
5276 * it started (meaning that the commit root will be the same as
5277 * the current root) or not.
5278 */
5279 if (sub_root->node != sub_root->commit_root) {
5280 u64 sub_flags = btrfs_root_flags(&sub_root->root_item);
5281
5282 if (sub_flags & BTRFS_ROOT_SUBVOL_RDONLY) {
5283 struct extent_buffer *eb;
5284
5285 /*
5286 * Assert we can't have races between dentry
5287 * lookup called through the snapshot creation
5288 * ioctl and the VFS.
5289 */
5290 ASSERT(mutex_is_locked(&dir->i_mutex));
5291
5292 down_write(&root->fs_info->commit_root_sem);
5293 eb = sub_root->commit_root;
5294 sub_root->commit_root =
5295 btrfs_root_node(sub_root);
5296 up_write(&root->fs_info->commit_root_sem);
5297 free_extent_buffer(eb);
5298 }
5299 }
5300 } 5264 }
5301 5265
5302 return inode; 5266 return inode;
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index e732274f1afd..33c80f560f98 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -713,6 +713,39 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
713 if (ret) 713 if (ret)
714 goto fail; 714 goto fail;
715 715
716 ret = btrfs_orphan_cleanup(pending_snapshot->snap);
717 if (ret)
718 goto fail;
719
720 /*
721 * If orphan cleanup did remove any orphans, it means the tree was
722 * modified and therefore the commit root is not the same as the
723 * current root anymore. This is a problem, because send uses the
724 * commit root and therefore can see inode items that don't exist
725 * in the current root anymore, and for example make calls to
726 * btrfs_iget, which will do tree lookups based on the current root
727 * and not on the commit root. Those lookups will fail, returning a
728 * -ESTALE error, and making send fail with that error. So make sure
729 * a send does not see any orphans we have just removed, and that it
730 * will see the same inodes regardless of whether a transaction
731 * commit happened before it started (meaning that the commit root
732 * will be the same as the current root) or not.
733 */
734 if (readonly && pending_snapshot->snap->node !=
735 pending_snapshot->snap->commit_root) {
736 trans = btrfs_join_transaction(pending_snapshot->snap);
737 if (IS_ERR(trans) && PTR_ERR(trans) != -ENOENT) {
738 ret = PTR_ERR(trans);
739 goto fail;
740 }
741 if (!IS_ERR(trans)) {
742 ret = btrfs_commit_transaction(trans,
743 pending_snapshot->snap);
744 if (ret)
745 goto fail;
746 }
747 }
748
716 inode = btrfs_lookup_dentry(dentry->d_parent->d_inode, dentry); 749 inode = btrfs_lookup_dentry(dentry->d_parent->d_inode, dentry);
717 if (IS_ERR(inode)) { 750 if (IS_ERR(inode)) {
718 ret = PTR_ERR(inode); 751 ret = PTR_ERR(inode);