Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5bc7247a authored by Miao Xie's avatar Miao Xie Committed by Josef Bacik
Browse files

Btrfs: fix broken nocow after balance



Balance will create reloc_root for each fs root, and it's going to
record last_snapshot to filter shared blocks.  The side effect of
setting last_snapshot is to break nocow attributes of files.

Since the extents are not shared by the relocation tree after the balance,
we can recover the old last_snapshot safely if no one snapshoted the
source tree. We fix the above problem by this way.

Reported-by: default avatarKyle Gates <kylegates@hotmail.com>
Signed-off-by: default avatarLiu Bo <bo.li.liu@oracle.com>
Signed-off-by: default avatarMiao Xie <miaox@cn.fujitsu.com>
Signed-off-by: default avatarJosef Bacik <jbacik@fusionio.com>
parent 8c2a1a30
Loading
Loading
Loading
Loading
+44 −0
Original line number Diff line number Diff line
@@ -1305,6 +1305,7 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
	struct extent_buffer *eb;
	struct btrfs_root_item *root_item;
	struct btrfs_key root_key;
	u64 last_snap = 0;
	int ret;

	root_item = kmalloc(sizeof(*root_item), GFP_NOFS);
@@ -1320,6 +1321,7 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
				      BTRFS_TREE_RELOC_OBJECTID);
		BUG_ON(ret);

		last_snap = btrfs_root_last_snapshot(&root->root_item);
		btrfs_set_root_last_snapshot(&root->root_item,
					     trans->transid - 1);
	} else {
@@ -1345,6 +1347,12 @@ static struct btrfs_root *create_reloc_root(struct btrfs_trans_handle *trans,
		memset(&root_item->drop_progress, 0,
		       sizeof(struct btrfs_disk_key));
		root_item->drop_level = 0;
		/*
		 * abuse rtransid, it is safe because it is impossible to
		 * receive data into a relocation tree.
		 */
		btrfs_set_root_rtransid(root_item, last_snap);
		btrfs_set_root_otransid(root_item, trans->transid);
	}

	btrfs_tree_unlock(eb);
@@ -2272,8 +2280,12 @@ void free_reloc_roots(struct list_head *list)
static noinline_for_stack
int merge_reloc_roots(struct reloc_control *rc)
{
	struct btrfs_trans_handle *trans;
	struct btrfs_root *root;
	struct btrfs_root *reloc_root;
	u64 last_snap;
	u64 otransid;
	u64 objectid;
	LIST_HEAD(reloc_roots);
	int found = 0;
	int ret = 0;
@@ -2307,12 +2319,44 @@ int merge_reloc_roots(struct reloc_control *rc)
		} else {
			list_del_init(&reloc_root->root_list);
		}

		/*
		 * we keep the old last snapshod transid in rtranid when we
		 * created the relocation tree.
		 */
		last_snap = btrfs_root_rtransid(&reloc_root->root_item);
		otransid = btrfs_root_otransid(&reloc_root->root_item);
		objectid = reloc_root->root_key.offset;

		ret = btrfs_drop_snapshot(reloc_root, rc->block_rsv, 0, 1);
		if (ret < 0) {
			if (list_empty(&reloc_root->root_list))
				list_add_tail(&reloc_root->root_list,
					      &reloc_roots);
			goto out;
		} else if (!ret) {
			/*
			 * recover the last snapshot tranid to avoid
			 * the space balance break NOCOW.
			 */
			root = read_fs_root(rc->extent_root->fs_info,
					    objectid);
			if (IS_ERR(root))
				continue;

			if (btrfs_root_refs(&root->root_item) == 0)
				continue;

			trans = btrfs_join_transaction(root);
			BUG_ON(IS_ERR(trans));

			/* Check if the fs/file tree was snapshoted or not. */
			if (btrfs_root_last_snapshot(&root->root_item) ==
			    otransid - 1)
				btrfs_set_root_last_snapshot(&root->root_item,
							     last_snap);
				
			btrfs_end_transaction(trans, root);
		}
	}