diff --git a/check/main.c b/check/main.c
index 99ff3bc3..45e3442d 100644
--- a/check/main.c
+++ b/check/main.c
@@ -63,6 +63,7 @@
 #include "check/qgroup-verify.h"
 #include "check/clear-cache.h"
 #include "kernel-shared/uapi/btrfs.h"
+#include "kernel-lib/bitops.h"
 
 /* Global context variables */
 struct btrfs_fs_info *gfs_info;
@@ -467,7 +468,7 @@ static void record_root_in_trans(struct btrfs_trans_handle *trans,
 				 struct btrfs_root *root)
 {
 	if (root->last_trans != trans->transid) {
-		root->track_dirty = 1;
+		set_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state);
 		root->last_trans = trans->transid;
 		root->commit_root = root->node;
 		extent_buffer_get(root->node);
diff --git a/kernel-shared/ctree.c b/kernel-shared/ctree.c
index 911ec51c..cfbcc689 100644
--- a/kernel-shared/ctree.c
+++ b/kernel-shared/ctree.c
@@ -20,6 +20,7 @@
 #include "kernel-shared/disk-io.h"
 #include "kernel-shared/transaction.h"
 #include "kernel-shared/print-tree.h"
+#include "kernel-lib/bitops.h"
 #include "crypto/crc32c.h"
 #include "common/internal.h"
 #include "common/messages.h"
@@ -119,7 +120,8 @@ void btrfs_release_path(struct btrfs_path *p)
 
 void add_root_to_dirty_list(struct btrfs_root *root)
 {
-	if (root->track_dirty && list_empty(&root->dirty_list)) {
+	if (test_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state) &&
+	    list_empty(&root->dirty_list)) {
 		list_add(&root->dirty_list,
 			 &root->fs_info->dirty_cowonly_roots);
 	}
@@ -155,9 +157,10 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
 	memcpy(new_root, root, sizeof(*new_root));
 	new_root->root_key.objectid = new_root_objectid;
 
-	WARN_ON(root->ref_cows && trans->transid !=
-		root->fs_info->running_transaction->transid);
-	WARN_ON(root->ref_cows && trans->transid != root->last_trans);
+	WARN_ON(test_bit(BTRFS_ROOT_SHAREABLE, &root->state) &&
+		trans->transid != root->fs_info->running_transaction->transid);
+	WARN_ON(test_bit(BTRFS_ROOT_SHAREABLE, &root->state) &&
+		trans->transid != root->last_trans);
 
 	level = btrfs_header_level(buf);
 	if (level == 0)
@@ -219,7 +222,7 @@ int btrfs_create_root(struct btrfs_trans_handle *trans,
 
 	btrfs_setup_root(new_root, fs_info, objectid);
 	if (!is_fstree(objectid))
-		new_root->track_dirty = 1;
+		set_bit(BTRFS_ROOT_TRACK_DIRTY, &new_root->state);
 	add_root_to_dirty_list(new_root);
 
 	new_root->objectid = objectid;
@@ -332,7 +335,7 @@ static int btrfs_block_can_be_shared(struct btrfs_root *root,
 	 * snapshot and the block was not allocated by tree relocation,
 	 * we know the block is not shared.
 	 */
-	if (root->ref_cows &&
+	if (test_bit(BTRFS_ROOT_SHAREABLE, &root->state) &&
 	    buf != root->node && buf != root->commit_root &&
 	    (btrfs_header_generation(buf) <=
 	     btrfs_root_last_snapshot(&root->root_item) ||
@@ -446,9 +449,10 @@ int __btrfs_cow_block(struct btrfs_trans_handle *trans,
 	struct btrfs_disk_key disk_key;
 	int level;
 
-	WARN_ON(root->ref_cows && trans->transid !=
-		root->fs_info->running_transaction->transid);
-	WARN_ON(root->ref_cows && trans->transid != root->last_trans);
+	WARN_ON(test_bit(BTRFS_ROOT_SHAREABLE, &root->state) &&
+		trans->transid != root->fs_info->running_transaction->transid);
+	WARN_ON(test_bit(BTRFS_ROOT_SHAREABLE, &root->state) &&
+		trans->transid != root->last_trans);
 
 	level = btrfs_header_level(buf);
 
diff --git a/kernel-shared/ctree.h b/kernel-shared/ctree.h
index 8de1fba4..84acefef 100644
--- a/kernel-shared/ctree.h
+++ b/kernel-shared/ctree.h
@@ -388,6 +388,68 @@ static inline bool btrfs_is_zoned(const struct btrfs_fs_info *fs_info)
 	return fs_info->zoned != 0;
 }
 
+/*
+ * The state of btrfs root
+ */
+enum {
+	/*
+	 * btrfs_record_root_in_trans is a multi-step process, and it can race
+	 * with the balancing code.   But the race is very small, and only the
+	 * first time the root is added to each transaction.  So IN_TRANS_SETUP
+	 * is used to tell us when more checks are required
+	 */
+	BTRFS_ROOT_IN_TRANS_SETUP,
+
+	/*
+	 * Set if tree blocks of this root can be shared by other roots.
+	 * Only subvolume trees and their reloc trees have this bit set.
+	 * Conflicts with TRACK_DIRTY bit.
+	 *
+	 * This affects two things:
+	 *
+	 * - How balance works
+	 *   For shareable roots, we need to use reloc tree and do path
+	 *   replacement for balance, and need various pre/post hooks for
+	 *   snapshot creation to handle them.
+	 *
+	 *   While for non-shareable trees, we just simply do a tree search
+	 *   with COW.
+	 *
+	 * - How dirty roots are tracked
+	 *   For shareable roots, btrfs_record_root_in_trans() is needed to
+	 *   track them, while non-subvolume roots have TRACK_DIRTY bit, they
+	 *   don't need to set this manually.
+	 */
+	BTRFS_ROOT_SHAREABLE,
+	BTRFS_ROOT_TRACK_DIRTY,
+	BTRFS_ROOT_IN_RADIX,
+	BTRFS_ROOT_ORPHAN_ITEM_INSERTED,
+	BTRFS_ROOT_DEFRAG_RUNNING,
+	BTRFS_ROOT_FORCE_COW,
+	BTRFS_ROOT_MULTI_LOG_TASKS,
+	BTRFS_ROOT_DIRTY,
+	BTRFS_ROOT_DELETING,
+
+	/*
+	 * Reloc tree is orphan, only kept here for qgroup delayed subtree scan
+	 *
+	 * Set for the subvolume tree owning the reloc tree.
+	 */
+	BTRFS_ROOT_DEAD_RELOC_TREE,
+	/* Mark dead root stored on device whose cleanup needs to be resumed */
+	BTRFS_ROOT_DEAD_TREE,
+	/* The root has a log tree. Used for subvolume roots and the tree root. */
+	BTRFS_ROOT_HAS_LOG_TREE,
+	/* Qgroup flushing is in progress */
+	BTRFS_ROOT_QGROUP_FLUSHING,
+	/* We started the orphan cleanup for this root. */
+	BTRFS_ROOT_ORPHAN_CLEANUP,
+	/* This root has a drop operation that was started previously. */
+	BTRFS_ROOT_UNFINISHED_DROP,
+	/* This reloc root needs to have its buffers lockdep class reset. */
+	BTRFS_ROOT_RESET_LOCKDEP_CLASS,
+};
+
 /*
  * in ram representation of the tree.  extent_root is used for all allocations
  * and for the extent tree extent_root root.
@@ -401,9 +463,7 @@ struct btrfs_root {
 	u64 objectid;
 	u64 last_trans;
 
-	int ref_cows;
-	int track_dirty;
-
+	unsigned long state;
 
 	u32 type;
 	u64 last_inode_alloc;
diff --git a/kernel-shared/disk-io.c b/kernel-shared/disk-io.c
index 030bc780..44462d8f 100644
--- a/kernel-shared/disk-io.c
+++ b/kernel-shared/disk-io.c
@@ -32,6 +32,7 @@
 #include "crypto/crc32c.h"
 #include "common/utils.h"
 #include "kernel-shared/print-tree.h"
+#include "kernel-lib/bitops.h"
 #include "common/rbtree-utils.h"
 #include "common/device-scan.h"
 #include "common/device-utils.h"
@@ -491,8 +492,7 @@ void btrfs_setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info,
 {
 	root->node = NULL;
 	root->commit_root = NULL;
-	root->ref_cows = 0;
-	root->track_dirty = 0;
+	root->state = 0;
 
 	root->fs_info = fs_info;
 	root->objectid = objectid;
@@ -654,9 +654,9 @@ out:
 	}
 insert:
 	if (root->root_key.objectid != BTRFS_TREE_LOG_OBJECTID)
-		root->track_dirty = 1;
+		set_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state);
 	if (is_fstree(root->root_key.objectid))
-		root->ref_cows = 1;
+		set_bit(BTRFS_ROOT_SHAREABLE, &root->state);
 	return root;
 }
 
@@ -1062,7 +1062,7 @@ static int load_global_roots_objectid(struct btrfs_fs_info *fs_info,
 			free(root);
 			break;
 		}
-		root->track_dirty = 1;
+		set_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state);
 
 		ret = btrfs_global_root_insert(fs_info, root);
 		if (ret) {
@@ -1099,7 +1099,7 @@ static int load_global_roots_objectid(struct btrfs_fs_info *fs_info,
 			root->root_key.objectid = objectid;
 			root->root_key.type = BTRFS_ROOT_ITEM_KEY;
 			root->root_key.offset = i;
-			root->track_dirty = 1;
+			set_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state);
 			root->node = btrfs_find_create_tree_block(fs_info, 0);
 			if (!root->node) {
 				free(root);
@@ -1232,7 +1232,8 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
 			error("couldn't load block group tree");
 			return -EIO;
 		}
-		fs_info->block_group_root->track_dirty = 1;
+		set_bit(BTRFS_ROOT_TRACK_DIRTY,
+			&fs_info->block_group_root->state);
 	}
 
 	ret = btrfs_find_and_setup_root(root, fs_info,
@@ -1242,7 +1243,7 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
 		printk("Couldn't setup device tree\n");
 		return -EIO;
 	}
-	fs_info->dev_root->track_dirty = 1;
+	set_bit(BTRFS_ROOT_TRACK_DIRTY, &fs_info->dev_root->state);
 
 	ret = btrfs_find_and_setup_root(root, fs_info,
 					BTRFS_UUID_TREE_OBJECTID,
@@ -1251,7 +1252,7 @@ int btrfs_setup_all_roots(struct btrfs_fs_info *fs_info, u64 root_tree_bytenr,
 		free(fs_info->uuid_root);
 		fs_info->uuid_root = NULL;
 	} else {
-		fs_info->uuid_root->track_dirty = 1;
+		set_bit(BTRFS_ROOT_TRACK_DIRTY, &fs_info->uuid_root->state);
 	}
 
 	ret = btrfs_find_and_setup_root(root, fs_info,
@@ -2355,7 +2356,7 @@ struct btrfs_root *btrfs_create_tree(struct btrfs_trans_handle *trans,
 
 	extent_buffer_get(root->node);
 	root->commit_root = root->node;
-	root->track_dirty = 1;
+	set_bit(BTRFS_ROOT_TRACK_DIRTY, &root->state);
 
 	root->root_item.flags = 0;
 	root->root_item.byte_limit = 0;
diff --git a/kernel-shared/extent-tree.c b/kernel-shared/extent-tree.c
index 2a71b97a..8f3477bf 100644
--- a/kernel-shared/extent-tree.c
+++ b/kernel-shared/extent-tree.c
@@ -1471,7 +1471,7 @@ static int __btrfs_mod_ref(struct btrfs_trans_handle *trans,
 	nritems = btrfs_header_nritems(buf);
 	level = btrfs_header_level(buf);
 
-	if (!root->ref_cows && level == 0)
+	if (!test_bit(BTRFS_ROOT_SHAREABLE, &root->state) && level == 0)
 		return 0;
 
 	if (inc)
@@ -2359,11 +2359,11 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
 	}
 
 	/*
-	 * Also preallocate metadata for csum tree and fs trees (root->ref_cows
-	 * already set), as they can consume a lot of metadata space.
-	 * Pre-allocate to avoid unexpected ENOSPC.
+	 * Also preallocate metadata for csum tree and fs trees
+	 * (BTRFS_ROOT_SHAREABLE already set) as they can consume a lot of
+	 * metadata space.  Pre-allocate to avoid unexpected ENOSPC.
 	 */
-	if (root->ref_cows ||
+	if (test_bit(BTRFS_ROOT_SHAREABLE, &root->state) ||
 	    root->root_key.objectid == BTRFS_CSUM_TREE_OBJECTID) {
 		if (!(profile & BTRFS_BLOCK_GROUP_METADATA)) {
 			ret = do_chunk_alloc(trans, info,
