btrfs: Convert to using fs_context
diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c
index 2955a4e..73afa20 100644
--- a/fs/btrfs/compression.c
+++ b/fs/btrfs/compression.c
@@ -1588,15 +1588,3 @@ int btrfs_compress_heuristic(struct inode *inode, u64 start, u64 end)
 	__free_workspace(0, ws_list, true);
 	return ret;
 }
-
-unsigned int btrfs_compress_str2level(const char *str)
-{
-	if (strncmp(str, "zlib", 4) != 0)
-		return 0;
-
-	/* Accepted form: zlib:1 up to zlib:9 and nothing left after the number */
-	if (str[4] == ':' && '1' <= str[5] && str[5] <= '9' && str[6] == 0)
-		return str[5] - '0';
-
-	return BTRFS_ZLIB_DEFAULT_LEVEL;
-}
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index e204336..4e46ff4 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -31,6 +31,34 @@
 #include "extent_io.h"
 #include "extent_map.h"
 #include "async-thread.h"
+#include "compression.h"
+
+struct btrfs_fs_context {
+	/* Track the options that were specified */
+	unsigned long specified[256 / (sizeof(long) * 8)];
+
+	char				**devices;
+	char				*subvol_name;
+	u64				subvol_objectid;
+
+	unsigned int			nr_devices;
+	unsigned int			mount_opt;
+	unsigned int			mount_opt_mask;		/* Bits to be changed */
+	unsigned int			mount_opt_explicit;	/* Changes set by params */
+	enum btrfs_compression_type	compress_type;
+	unsigned int			compress_level;
+
+	u64				max_inline;
+	u32				metadata_ratio;
+	u32				thread_pool_size;
+	u32				commit_interval;
+#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY
+	u32				check_integrity_print_mask;
+#endif
+
+	struct vfsmount			*root_mnt;
+	bool				root;
+};
 
 struct btrfs_trans_handle;
 struct btrfs_transaction;
@@ -1017,8 +1045,6 @@ struct btrfs_fs_info {
 	u32 data_chunk_allocations;
 	u32 metadata_ratio;
 
-	void *bdev_holder;
-
 	/* private scrub information */
 	struct mutex scrub_lock;
 	atomic_t scrubs_running;
@@ -1100,9 +1126,6 @@ struct btrfs_fs_info {
 	struct mutex unused_bg_unpin_mutex;
 	struct mutex delete_unused_bgs_mutex;
 
-	/* For btrfs to record security options */
-	struct security_mnt_opts security_opts;
-
 	/*
 	 * Chunks that can't be freed yet (under a trim/discard operation)
 	 * and will be latter freed. Protected by fs_info->chunk_mutex.
@@ -1325,58 +1348,59 @@ static inline u32 BTRFS_MAX_XATTR_SIZE(const struct btrfs_fs_info *info)
  * Note: don't forget to add new options to btrfs_alloc_fs_info() and
  * btrfs_show_options().
  */
-#define BTRFS_MOUNT_DATASUM		(1 << 0)
-#define BTRFS_MOUNT_DATACOW		(1 << 1)
-#define BTRFS_MOUNT_BARRIER		(1 << 2)
-#define BTRFS_MOUNT_SSD			(1 << 3)
-#define BTRFS_MOUNT_DEGRADED		(1 << 4)
-#define BTRFS_MOUNT_COMPRESS		(1 << 5)
-#define BTRFS_MOUNT_TREELOG		(1 << 6)
-#define BTRFS_MOUNT_FLUSHONCOMMIT	(1 << 7)
-#define BTRFS_MOUNT_SSD_SPREAD		(1 << 8)
-#define BTRFS_MOUNT_NOSSD		(1 << 9)
-#define BTRFS_MOUNT_DISCARD		(1 << 10)
-#define BTRFS_MOUNT_FORCE_COMPRESS	(1 << 11)
-#define BTRFS_MOUNT_SPACE_CACHE		(1 << 12)
-#define BTRFS_MOUNT_CLEAR_CACHE		(1 << 13)
-#define BTRFS_MOUNT_USER_SUBVOL_RM_ALLOWED (1 << 14)
-#define BTRFS_MOUNT_ENOSPC_DEBUG	 (1 << 15)
-#define BTRFS_MOUNT_AUTO_DEFRAG		(1 << 16)
-#define BTRFS_MOUNT_INODE_MAP_CACHE	(1 << 17)
-#define BTRFS_MOUNT_USEBACKUPROOT	(1 << 18)
-#define BTRFS_MOUNT_SKIP_BALANCE	(1 << 19)
-#define BTRFS_MOUNT_CHECK_INTEGRITY	(1 << 20)
-#define BTRFS_MOUNT_CHECK_INTEGRITY_INCLUDING_EXTENT_DATA (1 << 21)
-#define BTRFS_MOUNT_PANIC_ON_FATAL_ERROR	(1 << 22)
-#define BTRFS_MOUNT_RESCAN_UUID_TREE	(1 << 23)
-#define BTRFS_MOUNT_FRAGMENT_DATA	(1 << 24)
-#define BTRFS_MOUNT_FRAGMENT_METADATA	(1 << 25)
-#define BTRFS_MOUNT_FREE_SPACE_TREE	(1 << 26)
-#define BTRFS_MOUNT_NOLOGREPLAY		(1 << 27)
-#define BTRFS_MOUNT_REF_VERIFY		(1 << 28)
+enum btrfs_opt {
+	BTRFS_MOUNT_DATASUM				= 0,
+	BTRFS_MOUNT_DATACOW				= 1,
+	BTRFS_MOUNT_BARRIER				= 2,
+	BTRFS_MOUNT_SSD					= 3,
+	BTRFS_MOUNT_DEGRADED				= 4,
+	BTRFS_MOUNT_COMPRESS				= 5,
+	BTRFS_MOUNT_TREELOG				= 6,
+	BTRFS_MOUNT_FLUSHONCOMMIT       		= 7,
+	BTRFS_MOUNT_SSD_SPREAD				= 8,
+	BTRFS_MOUNT_NOSSD				= 9,
+	BTRFS_MOUNT_DISCARD				= 10,
+	BTRFS_MOUNT_FORCE_COMPRESS      		= 11,
+	BTRFS_MOUNT_SPACE_CACHE				= 12,
+	BTRFS_MOUNT_CLEAR_CACHE				= 13,
+	BTRFS_MOUNT_USER_SUBVOL_RM_ALLOWED 		= 14,
+	BTRFS_MOUNT_ENOSPC_DEBUG	 		= 15,
+	BTRFS_MOUNT_AUTO_DEFRAG				= 16,
+	BTRFS_MOUNT_INODE_MAP_CACHE			= 17,
+	BTRFS_MOUNT_USEBACKUPROOT			= 18,
+	BTRFS_MOUNT_SKIP_BALANCE			= 19,
+	BTRFS_MOUNT_CHECK_INTEGRITY			= 20,
+	BTRFS_MOUNT_CHECK_INTEGRITY_INCLUDING_EXTENT_DATA = 21,
+	BTRFS_MOUNT_PANIC_ON_FATAL_ERROR		= 22,
+	BTRFS_MOUNT_RESCAN_UUID_TREE			= 23,
+	BTRFS_MOUNT_FRAGMENT_DATA			= 24,
+	BTRFS_MOUNT_FRAGMENT_METADATA			= 25,
+	BTRFS_MOUNT_FREE_SPACE_TREE			= 26,
+	BTRFS_MOUNT_NOLOGREPLAY				= 27,
+	BTRFS_MOUNT_REF_VERIFY				= 28,
+};
 
 #define BTRFS_DEFAULT_COMMIT_INTERVAL	(30)
 #define BTRFS_DEFAULT_MAX_INLINE	(2048)
 
-#define btrfs_clear_opt(o, opt)		((o) &= ~BTRFS_MOUNT_##opt)
-#define btrfs_set_opt(o, opt)		((o) |= BTRFS_MOUNT_##opt)
-#define btrfs_raw_test_opt(o, opt)	((o) & BTRFS_MOUNT_##opt)
-#define btrfs_test_opt(fs_info, opt)	((fs_info)->mount_opt & \
-					 BTRFS_MOUNT_##opt)
+#define btrfs_clear_opt(o, opt)		((o) &= ~(1 << BTRFS_MOUNT_##opt))
+#define btrfs_set_opt(o, opt)		((o) |= (1 << BTRFS_MOUNT_##opt))
+#define btrfs_raw_test_opt(o, opt)	((o) & (1 << BTRFS_MOUNT_##opt))
+#define btrfs_test_opt(fs_info, opt)	btrfs_raw_test_opt((fs_info)->mount_opt, opt)
 
 #define btrfs_set_and_info(fs_info, opt, fmt, args...)			\
-{									\
+do {									\
 	if (!btrfs_test_opt(fs_info, opt))				\
 		btrfs_info(fs_info, fmt, ##args);			\
 	btrfs_set_opt(fs_info->mount_opt, opt);				\
-}
+} while (0)
 
 #define btrfs_clear_and_info(fs_info, opt, fmt, args...)		\
-{									\
+do {									\
 	if (btrfs_test_opt(fs_info, opt))				\
 		btrfs_info(fs_info, fmt, ##args);			\
 	btrfs_clear_opt(fs_info->mount_opt, opt);			\
-}
+} while (0)
 
 #ifdef CONFIG_BTRFS_DEBUG
 static inline int
@@ -1418,20 +1442,20 @@ btrfs_should_fragment_free_space(struct btrfs_block_group_cache *block_group)
  */
 #define btrfs_set_pending_and_info(info, opt, fmt, args...)            \
 do {                                                                   \
-       if (!btrfs_raw_test_opt((info)->mount_opt, opt)) {              \
-               btrfs_info((info), fmt, ##args);                        \
-               btrfs_set_pending((info), SET_##opt);                   \
-               btrfs_clear_pending((info), CLEAR_##opt);               \
-       }                                                               \
+	if (!btrfs_test_opt((info), opt)) {			       \
+		btrfs_info((info), fmt, ##args);		       \
+		btrfs_set_pending((info), SET_##opt);		       \
+		btrfs_clear_pending((info), CLEAR_##opt);	       \
+	}							       \
 } while(0)
 
 #define btrfs_clear_pending_and_info(info, opt, fmt, args...)          \
 do {                                                                   \
-       if (btrfs_raw_test_opt((info)->mount_opt, opt)) {               \
-               btrfs_info((info), fmt, ##args);                        \
-               btrfs_set_pending((info), CLEAR_##opt);                 \
-               btrfs_clear_pending((info), SET_##opt);                 \
-       }                                                               \
+	if (btrfs_test_opt((info), opt)) {			       \
+		btrfs_info((info), fmt, ##args);		       \
+		btrfs_set_pending((info), CLEAR_##opt);		       \
+		btrfs_clear_pending((info), SET_##opt);		       \
+	}							       \
 } while(0)
 
 /*
@@ -2960,7 +2984,6 @@ static inline void free_fs_info(struct btrfs_fs_info *fs_info)
 	kfree(fs_info->free_space_root);
 	kfree(fs_info->super_copy);
 	kfree(fs_info->super_for_commit);
-	security_free_mnt_opts(&fs_info->security_opts);
 	kvfree(fs_info);
 }
 
@@ -3246,14 +3269,15 @@ int btrfs_sysfs_add_mounted(struct btrfs_fs_info *fs_info);
 void btrfs_sysfs_remove_mounted(struct btrfs_fs_info *fs_info);
 
 /* fs_params.c */
-int btrfs_parse_options(struct btrfs_fs_info *info, char *options,
-			unsigned long new_flags);
-int btrfs_parse_device_options(const char *options, fmode_t flags, void *holder);
-int btrfs_parse_subvol_options(const char *options, char **subvol_name,
-			       u64 *subvol_objectid);
+extern const struct fs_parameter_description btrfs_fs_parameters;
+int btrfs_init_fs_context(struct fs_context *fc, struct dentry *reference);
+void btrfs_apply_configuration(struct fs_context *fc, struct super_block *sb);
 int btrfs_show_options(struct seq_file *seq, struct dentry *dentry);
 
 /* super.c */
+extern struct file_system_type btrfs_fs_type;
+int btrfs_get_tree(struct fs_context *fc);
+int btrfs_reconfigure(struct fs_context *fc);
 int btrfs_sync_fs(struct super_block *sb, int wait);
 
 static inline __printf(2, 3) __cold
diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c
index 2aa48aec..efd4d61 100644
--- a/fs/btrfs/dev-replace.c
+++ b/fs/btrfs/dev-replace.c
@@ -196,7 +196,7 @@ static int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
 	}
 
 	bdev = blkdev_get_by_path(device_path, FMODE_WRITE | FMODE_EXCL,
-				  fs_info->bdev_holder);
+				  &btrfs_fs_type);
 	if (IS_ERR(bdev)) {
 		btrfs_err(fs_info, "target device %s is invalid!", device_path);
 		return PTR_ERR(bdev);
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 99bea5c..0e5c37f 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -2597,9 +2597,9 @@ static int btrfs_validate_write_super(struct btrfs_fs_info *fs_info,
 	return ret;
 }
 
-int open_ctree(struct super_block *sb,
-	       struct btrfs_fs_devices *fs_devices,
-	       char *options)
+int open_ctree(struct fs_context *fc,
+	       struct super_block *sb,
+	       struct btrfs_fs_devices *fs_devices)
 {
 	u32 sectorsize;
 	u32 nodesize;
@@ -2698,21 +2698,15 @@ int open_ctree(struct super_block *sb,
 	atomic_set(&fs_info->reada_works_cnt, 0);
 	atomic64_set(&fs_info->tree_mod_seq, 0);
 	fs_info->sb = sb;
-	fs_info->max_inline = BTRFS_DEFAULT_MAX_INLINE;
-	fs_info->metadata_ratio = 0;
 	fs_info->defrag_inodes = RB_ROOT;
 	atomic64_set(&fs_info->free_chunk_space, 0);
 	fs_info->tree_mod_log = RB_ROOT;
-	fs_info->commit_interval = BTRFS_DEFAULT_COMMIT_INTERVAL;
 	fs_info->avg_delayed_ref_runtime = NSEC_PER_SEC >> 6; /* div by 64 */
 	/* readahead state */
 	INIT_RADIX_TREE(&fs_info->reada_tree, GFP_NOFS & ~__GFP_DIRECT_RECLAIM);
 	spin_lock_init(&fs_info->reada_lock);
 	btrfs_init_ref_verify(fs_info);
 
-	fs_info->thread_pool_size = min_t(unsigned long,
-					  num_online_cpus() + 2, 8);
-
 	INIT_LIST_HEAD(&fs_info->ordered_roots);
 	spin_lock_init(&fs_info->ordered_root_lock);
 
@@ -2730,11 +2724,7 @@ int open_ctree(struct super_block *sb,
 		goto fail_iput;
 	}
 	btrfs_init_delayed_root(fs_info->delayed_root);
-
 	btrfs_init_scrub(fs_info);
-#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY
-	fs_info->check_integrity_print_mask = 0;
-#endif
 	btrfs_init_balance(fs_info);
 	btrfs_init_async_reclaim_work(&fs_info->async_reclaim_work);
 
@@ -2845,17 +2835,12 @@ int open_ctree(struct super_block *sb,
 	generation = btrfs_super_generation(disk_super);
 	find_oldest_super_backup(fs_info, generation);
 
-	/*
-	 * In the long term, we'll store the compression type in the super
-	 * block, and it'll be used for per file compression control.
-	 */
-	fs_info->compress_type = BTRFS_COMPRESS_ZLIB;
+	if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE))
+		btrfs_set_opt(fs_info->mount_opt, FREE_SPACE_TREE);
+	else if (generation)
+		btrfs_set_opt(fs_info->mount_opt, SPACE_CACHE);
 
-	ret = btrfs_parse_options(fs_info, options, sb->s_flags);
-	if (ret) {
-		err = ret;
-		goto fail_alloc;
-	}
+	btrfs_apply_configuration(fc, sb);
 
 	features = btrfs_super_incompat_flags(disk_super) &
 		~BTRFS_FEATURE_INCOMPAT_SUPP;
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index 4cccba2..73115b8 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -49,9 +49,8 @@ struct extent_buffer *btrfs_find_create_tree_block(
 						struct btrfs_fs_info *fs_info,
 						u64 bytenr);
 void clean_tree_block(struct btrfs_fs_info *fs_info, struct extent_buffer *buf);
-int open_ctree(struct super_block *sb,
-	       struct btrfs_fs_devices *fs_devices,
-	       char *options);
+int open_ctree(struct fs_context *fc, struct super_block *sb,
+	       struct btrfs_fs_devices *fs_devices);
 void close_ctree(struct btrfs_fs_info *fs_info);
 int write_all_supers(struct btrfs_fs_info *fs_info, int max_mirrors);
 struct buffer_head *btrfs_read_dev_super(struct block_device *bdev);
diff --git a/fs/btrfs/fs_params.c b/fs/btrfs/fs_params.c
index a09bb1f..b91bcc3 100644
--- a/fs/btrfs/fs_params.c
+++ b/fs/btrfs/fs_params.c
@@ -1,719 +1,885 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
+ * Filesystem parameterisation.
+ *
  * Copyright (C) 2007 Oracle.  All rights reserved.
+ * Copyright (C) 2018 Red Hat, Inc.  All rights reserved.
  */
 
-#include <linux/kernel.h>
-#include <linux/parser.h>
+#include <linux/mount.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include "ctree.h"
-#include "btrfs_inode.h"
-#include "compression.h"
 #include "volumes.h"
+#include "btrfs_inode.h"
+#include "rcu-string.h"
 
-enum {
-	Opt_acl, Opt_noacl,
+enum btrfs_param {
+	Opt_acl,
+	Opt_alloc_start,
+	Opt_autodefrag,
+	Opt_barrier,
+	Opt_check_integrity,
+	Opt_check_integrity_including_extent_data,
+	Opt_check_integrity_print_mask,
 	Opt_clear_cache,
 	Opt_commit_interval,
 	Opt_compress,
 	Opt_compress_force,
-	Opt_compress_force_type,
-	Opt_compress_type,
+	Opt_datacow,
+	Opt_datasum,
 	Opt_degraded,
 	Opt_device,
+	Opt_discard,
+	Opt_enospc_debug,
 	Opt_fatal_errors,
-	Opt_flushoncommit, Opt_noflushoncommit,
-	Opt_inode_cache, Opt_noinode_cache,
+	Opt_flushoncommit,
+	Opt_fragment,
+	Opt_inode_cache,
 	Opt_max_inline,
-	Opt_barrier, Opt_nobarrier,
-	Opt_datacow, Opt_nodatacow,
-	Opt_datasum, Opt_nodatasum,
-	Opt_defrag, Opt_nodefrag,
-	Opt_discard, Opt_nodiscard,
+	Opt_metadata_ratio,
 	Opt_nologreplay,
-	Opt_norecovery,
-	Opt_ratio,
+	Opt_nossd,
+	Opt_recovery,
+	Opt_ref_verify,
 	Opt_rescan_uuid_tree,
 	Opt_skip_balance,
-	Opt_space_cache, Opt_no_space_cache,
-	Opt_space_cache_version,
-	Opt_ssd, Opt_nossd,
-	Opt_ssd_spread, Opt_nossd_spread,
+	Opt_source,
+	Opt_space_cache,
+	Opt_ssd,
+	Opt_ssd_spread,
 	Opt_subvol,
-	Opt_subvol_empty,
 	Opt_subvolid,
+	Opt_subvolrootid,
 	Opt_thread_pool,
-	Opt_treelog, Opt_notreelog,
+	Opt_treelog,
 	Opt_usebackuproot,
 	Opt_user_subvol_rm_allowed,
-
-	/* Deprecated options */
-	Opt_alloc_start,
-	Opt_recovery,
-	Opt_subvolrootid,
-
-	/* Debugging options */
-	Opt_check_integrity,
-	Opt_check_integrity_including_extent_data,
-	Opt_check_integrity_print_mask,
-	Opt_enospc_debug, Opt_noenospc_debug,
-#ifdef CONFIG_BTRFS_DEBUG
-	Opt_fragment_data, Opt_fragment_metadata, Opt_fragment_all,
-#endif
-#ifdef CONFIG_BTRFS_FS_REF_VERIFY
-	Opt_ref_verify,
-#endif
-	Opt_err,
+	nr__btrfs_params
 };
 
-static const match_table_t tokens = {
-	{Opt_acl, "acl"},
-	{Opt_noacl, "noacl"},
-	{Opt_clear_cache, "clear_cache"},
-	{Opt_commit_interval, "commit=%u"},
-	{Opt_compress, "compress"},
-	{Opt_compress_type, "compress=%s"},
-	{Opt_compress_force, "compress-force"},
-	{Opt_compress_force_type, "compress-force=%s"},
-	{Opt_degraded, "degraded"},
-	{Opt_device, "device=%s"},
-	{Opt_fatal_errors, "fatal_errors=%s"},
-	{Opt_flushoncommit, "flushoncommit"},
-	{Opt_noflushoncommit, "noflushoncommit"},
-	{Opt_inode_cache, "inode_cache"},
-	{Opt_noinode_cache, "noinode_cache"},
-	{Opt_max_inline, "max_inline=%s"},
-	{Opt_barrier, "barrier"},
-	{Opt_nobarrier, "nobarrier"},
-	{Opt_datacow, "datacow"},
-	{Opt_nodatacow, "nodatacow"},
-	{Opt_datasum, "datasum"},
-	{Opt_nodatasum, "nodatasum"},
-	{Opt_defrag, "autodefrag"},
-	{Opt_nodefrag, "noautodefrag"},
-	{Opt_discard, "discard"},
-	{Opt_nodiscard, "nodiscard"},
-	{Opt_nologreplay, "nologreplay"},
-	{Opt_norecovery, "norecovery"},
-	{Opt_ratio, "metadata_ratio=%u"},
-	{Opt_rescan_uuid_tree, "rescan_uuid_tree"},
-	{Opt_skip_balance, "skip_balance"},
-	{Opt_space_cache, "space_cache"},
-	{Opt_no_space_cache, "nospace_cache"},
-	{Opt_space_cache_version, "space_cache=%s"},
-	{Opt_ssd, "ssd"},
-	{Opt_nossd, "nossd"},
-	{Opt_ssd_spread, "ssd_spread"},
-	{Opt_nossd_spread, "nossd_spread"},
-	{Opt_subvol, "subvol=%s"},
-	{Opt_subvol_empty, "subvol="},
-	{Opt_subvolid, "subvolid=%s"},
-	{Opt_thread_pool, "thread_pool=%u"},
-	{Opt_treelog, "treelog"},
-	{Opt_notreelog, "notreelog"},
-	{Opt_usebackuproot, "usebackuproot"},
-	{Opt_user_subvol_rm_allowed, "user_subvol_rm_allowed"},
+enum {
+	Opt_compress__zlib	= 0, /* Arg is optional; default is 0 */
+	Opt_compress__zlib_1,
+	Opt_compress__zlib_2,
+	Opt_compress__zlib_3,
+	Opt_compress__zlib_4,
+	Opt_compress__zlib_5,
+	Opt_compress__zlib_6,
+	Opt_compress__zlib_7,
+	Opt_compress__zlib_8,
+	Opt_compress__zlib_9,
+	Opt_compress__lzo,
+	Opt_compress__zstd,
+	Opt_compress__no,
+};
 
-	/* Deprecated options */
-	{Opt_alloc_start, "alloc_start=%s"},
-	{Opt_recovery, "recovery"},
-	{Opt_subvolrootid, "subvolrootid=%d"},
+enum btrfs_space_cache {
+	Opt_space_cache__v1	= 0,	/* Arg is optional; default is 0 */
+	Opt_space_cache__v2,
+	Opt_space_cache__no,
+};
 
-	/* Debugging options */
-	{Opt_check_integrity, "check_int"},
-	{Opt_check_integrity_including_extent_data, "check_int_data"},
-	{Opt_check_integrity_print_mask, "check_int_print_mask=%u"},
-	{Opt_enospc_debug, "enospc_debug"},
-	{Opt_noenospc_debug, "noenospc_debug"},
-#ifdef CONFIG_BTRFS_DEBUG
-	{Opt_fragment_data, "fragment=data"},
-	{Opt_fragment_metadata, "fragment=metadata"},
-	{Opt_fragment_all, "fragment=all"},
-#endif
-#ifdef CONFIG_BTRFS_FS_REF_VERIFY
-	{Opt_ref_verify, "ref_verify"},
-#endif
-	{Opt_err, NULL},
+enum {
+	Opt_fatal_errors__bug,
+	Opt_fatal_errors__panic,
+};
+
+enum {
+	Opt_fragment__all,
+	Opt_fragment__data,
+	Opt_fragment__metadata,
+};
+
+static const struct fs_parameter_spec btrfs_param_specs[nr__btrfs_params] = {
+	[Opt_acl]			= { fs_param_is_flag, fs_param_neg_with_no },
+	[Opt_alloc_start]		= { fs_param_is_string, fs_param_deprecated },
+	[Opt_autodefrag]		= { fs_param_is_flag, fs_param_neg_with_no },
+	[Opt_barrier]			= { fs_param_is_flag, fs_param_neg_with_no },
+	[Opt_check_integrity]		= { fs_param_is_flag },
+	[Opt_check_integrity_including_extent_data]= { fs_param_is_flag },
+	[Opt_check_integrity_print_mask]= { fs_param_is_u32 },
+	[Opt_clear_cache]		= { fs_param_is_flag },
+	[Opt_commit_interval]		= { fs_param_is_u32 },
+	[Opt_compress]			= { fs_param_is_enum, fs_param_v_optional },
+	[Opt_compress_force]		= { fs_param_is_enum, fs_param_v_optional },
+	[Opt_datacow]			= { fs_param_is_flag, fs_param_neg_with_no },
+	[Opt_datasum]			= { fs_param_is_flag, fs_param_neg_with_no },
+	[Opt_degraded]			= { fs_param_is_flag },
+	[Opt_device]			= { fs_param_is_string },
+	[Opt_discard]			= { fs_param_is_flag, fs_param_neg_with_no },
+	[Opt_enospc_debug]		= { fs_param_is_flag, fs_param_neg_with_no },
+	[Opt_fatal_errors]		= { fs_param_is_enum },
+	[Opt_flushoncommit]		= { fs_param_is_flag, fs_param_neg_with_no },
+	[Opt_fragment]			= { fs_param_is_enum },
+	[Opt_inode_cache]		= { fs_param_is_flag, fs_param_neg_with_no },
+	[Opt_max_inline]		= { fs_param_is_string },
+	[Opt_metadata_ratio]		= { fs_param_is_u32 },
+	[Opt_nologreplay]		= { fs_param_is_flag },
+	[Opt_nossd]			= { fs_param_is_flag },
+	[Opt_recovery]			= { fs_param_is_flag, fs_param_neg_with_no },
+	[Opt_ref_verify]		= { fs_param_is_flag },
+	[Opt_rescan_uuid_tree]		= { fs_param_is_flag },
+	[Opt_skip_balance]		= { fs_param_is_flag },
+	[Opt_source]			= { fs_param_is_string },
+	[Opt_space_cache]		= { fs_param_is_enum,
+					    fs_param_v_optional | fs_param_neg_with_no },
+	[Opt_ssd]			= { fs_param_is_flag },
+	[Opt_ssd_spread]		= { fs_param_is_flag },
+	[Opt_subvol]			= { fs_param_is_string, fs_param_v_optional },
+	[Opt_subvolid]			= { fs_param_is_u64 },
+	[Opt_subvolrootid]		= { fs_param_is_s32, fs_param_deprecated },
+	[Opt_thread_pool]		= { fs_param_is_u32 },
+	[Opt_treelog]			= { fs_param_is_flag, fs_param_neg_with_no },
+	[Opt_usebackuproot]		= { fs_param_is_flag },
+	[Opt_user_subvol_rm_allowed]	= { fs_param_is_flag },
+};
+
+static const char *const btrfs_param_keys[nr__btrfs_params] = {
+	[Opt_acl]			= "acl",
+	[Opt_alloc_start]		= "alloc_start",
+	[Opt_autodefrag]		= "autodefrag",
+	[Opt_barrier]			= "barrier",
+	[Opt_check_integrity]		= "check_int",
+	[Opt_check_integrity_including_extent_data] = "check_int_data",
+	[Opt_check_integrity_print_mask] = "check_int_print_mask",
+	[Opt_clear_cache]		= "clear_cache",
+	[Opt_commit_interval]		= "commit",
+	[Opt_compress]			= "compress",
+	[Opt_compress_force]		= "compress-force",
+	[Opt_datacow]			= "datacow",
+	[Opt_datasum]			= "datasum",
+	[Opt_degraded]			= "degraded",
+	[Opt_device]			= "device",
+	[Opt_discard]			= "discard",
+	[Opt_enospc_debug]		= "enospc_debug",
+	[Opt_fatal_errors]		= "fatal_errors",
+	[Opt_flushoncommit]		= "flushoncommit",
+	[Opt_fragment]			= "fragment",
+	[Opt_inode_cache]		= "inode_cache",
+	[Opt_max_inline]		= "max_inline",
+	[Opt_metadata_ratio]		= "metadata_ratio",
+	[Opt_nologreplay]		= "nologreplay",
+	[Opt_nossd]			= "nossd",
+	[Opt_recovery]			= "recovery",
+	[Opt_ref_verify]		= "ref_verify",
+	[Opt_rescan_uuid_tree]		= "rescan_uuid_tree",
+	[Opt_skip_balance]		= "skip_balance",
+	[Opt_source]			= "source",
+	[Opt_space_cache]		= "space_cache",
+	[Opt_ssd]			= "ssd",
+	[Opt_ssd_spread]		= "ssd_spread",
+	[Opt_subvol]			= "subvol",
+	[Opt_subvolid]			= "subvolid",
+	[Opt_subvolrootid]		= "subvolrootid",
+	[Opt_thread_pool]		= "thread_pool",
+	[Opt_treelog]			= "treelog",
+	[Opt_usebackuproot]		= "usebackuproot",
+	[Opt_user_subvol_rm_allowed]	= "user_subvol_rm_allowed",
+};
+
+static const struct fs_parameter_enum btrfs_param_enums[] = {
+	{ Opt_fragment,		"all",		Opt_fragment__all },
+	{ Opt_fragment,		"data",		Opt_fragment__data },
+	{ Opt_fragment,		"metadata",	Opt_fragment__metadata },
+	{ Opt_compress,		"zlib",		Opt_compress__zlib },
+	{ Opt_compress,		"zlib:1",	Opt_compress__zlib_1 },
+	{ Opt_compress,		"zlib:2",	Opt_compress__zlib_2 },
+	{ Opt_compress,		"zlib:3",	Opt_compress__zlib_3 },
+	{ Opt_compress,		"zlib:4",	Opt_compress__zlib_4 },
+	{ Opt_compress,		"zlib:5",	Opt_compress__zlib_5 },
+	{ Opt_compress,		"zlib:6",	Opt_compress__zlib_6 },
+	{ Opt_compress,		"zlib:7",	Opt_compress__zlib_7 },
+	{ Opt_compress,		"zlib:8",	Opt_compress__zlib_8 },
+	{ Opt_compress,		"zlib:9",	Opt_compress__zlib_9 },
+	{ Opt_compress,		"lzo",		Opt_compress__lzo },
+	{ Opt_compress,		"zstd",		Opt_compress__zstd },
+	{ Opt_compress,		"no",		Opt_compress__no },
+	{ Opt_compress_force,	"zlib",		Opt_compress__zlib },
+	{ Opt_compress_force,	"zlib:1",	Opt_compress__zlib_1 },
+	{ Opt_compress_force,	"zlib:2",	Opt_compress__zlib_2 },
+	{ Opt_compress_force,	"zlib:3",	Opt_compress__zlib_3 },
+	{ Opt_compress_force,	"zlib:4",	Opt_compress__zlib_4 },
+	{ Opt_compress_force,	"zlib:5",	Opt_compress__zlib_5 },
+	{ Opt_compress_force,	"zlib:6",	Opt_compress__zlib_6 },
+	{ Opt_compress_force,	"zlib:7",	Opt_compress__zlib_7 },
+	{ Opt_compress_force,	"zlib:8",	Opt_compress__zlib_8 },
+	{ Opt_compress_force,	"zlib:9",	Opt_compress__zlib_9 },
+	{ Opt_compress_force,	"lzo",		Opt_compress__lzo },
+	{ Opt_compress_force,	"zstd",		Opt_compress__zstd },
+	{ Opt_compress_force,	"no",		Opt_compress__no },
+	{ Opt_space_cache,	"v1",		Opt_space_cache__v1 },
+	{ Opt_space_cache,	"v2",		Opt_space_cache__v2 },
+	{ Opt_fatal_errors,	"bug",		Opt_fatal_errors__bug },
+	{ Opt_fatal_errors,	"panic",	Opt_fatal_errors__panic },
+};
+
+const struct fs_parameter_description btrfs_fs_parameters = {
+	.name		= "btrfs",
+	.nr_params	= nr__btrfs_params,
+	.nr_enums	= ARRAY_SIZE(btrfs_param_enums),
+	.source_param	= Opt_source,
+	.keys		= btrfs_param_keys,
+	.specs		= btrfs_param_specs,
+	.enums		= btrfs_param_enums,
+};
+
+#define btrfs_mparam_set(ctx, opt)				\
+	do {							\
+		ctx->mount_opt |= BTRFS_MOUNT_##opt;		\
+		ctx->mount_opt_mask |= BTRFS_MOUNT_##opt;	\
+	} while (0)
+
+#define btrfs_mparam_clear(ctx, opt)				\
+	do {							\
+		ctx->mount_opt &= ~BTRFS_MOUNT_##opt;		\
+		ctx->mount_opt_mask |= BTRFS_MOUNT_##opt;	\
+	} while (0)
+
+struct btrfs_flag_map {
+	u8			bit;
+#define BTRFS_FLAG_MAP_PRESENT	0x80	/* There is a rule here */
+};
+
+#define mount_opt(x)     { BTRFS_MOUNT_##x | BTRFS_FLAG_MAP_PRESENT }
+
+static const struct btrfs_flag_map btrfs_flag_map[nr__btrfs_params] = {
+	[Opt_barrier]		= mount_opt(BARRIER),
+	[Opt_check_integrity]	= mount_opt(CHECK_INTEGRITY),
+	[Opt_check_integrity_including_extent_data] =
+	mount_opt(CHECK_INTEGRITY_INCLUDING_EXTENT_DATA),
+	[Opt_clear_cache]	= mount_opt(CLEAR_CACHE),
+	[Opt_compress]		= mount_opt(COMPRESS),
+	[Opt_compress_force]	= mount_opt(FORCE_COMPRESS),
+	[Opt_datacow]		= mount_opt(DATACOW),
+	[Opt_datasum]		= mount_opt(DATASUM),
+	[Opt_autodefrag]		= mount_opt(AUTO_DEFRAG),
+	[Opt_degraded]		= mount_opt(DEGRADED),
+	[Opt_discard]		= mount_opt(DISCARD),
+	[Opt_enospc_debug]	= mount_opt(ENOSPC_DEBUG),
+	[Opt_flushoncommit]	= mount_opt(FLUSHONCOMMIT),
+	[Opt_inode_cache]	= mount_opt(INODE_MAP_CACHE),
+	[Opt_nologreplay]	= mount_opt(NOLOGREPLAY),
+	[Opt_nossd]		= mount_opt(NOSSD),
+	[Opt_ref_verify]	= mount_opt(REF_VERIFY),
+	[Opt_rescan_uuid_tree]	= mount_opt(RESCAN_UUID_TREE),
+	[Opt_skip_balance]	= mount_opt(SKIP_BALANCE),
+	[Opt_ssd]		= mount_opt(SSD),
+	[Opt_ssd_spread]	= mount_opt(SSD_SPREAD),
+	[Opt_treelog]		= mount_opt(TREELOG),
+	[Opt_usebackuproot]	= mount_opt(USEBACKUPROOT),
+	[Opt_user_subvol_rm_allowed] = mount_opt(USER_SUBVOL_RM_ALLOWED),
 };
 
 /*
- * Regular mount options parser.  Everything that is needed only when
- * reading in a new superblock is parsed here.
- * XXX JDM: This needs to be cleaned up for remount.
+ * Implicit rules of option relationships.
+ *
+ * "implies" means turning on option $key turns on $target; turning off $target
+ * turns off $key.  Note that we don't automatically calculate transitivity,
+ * but rather transitive relationships must be explicitly listed.
+ *
+ * "contradicts" means turning on option $key turns off $target and vice versa.
+ *
+ * The op is mentioned specifically just in case we want to add other options
+ * later.
  */
-int btrfs_parse_options(struct btrfs_fs_info *info, char *options,
-			unsigned long new_flags)
+struct btrfs_implicit_rule {
+	u8	key;
+	u8	target;
+};
+
+#define _(key, op, target_key) { key, target_key }
+static const struct btrfs_implicit_rule btrfs_opt_dependencies[] = {
+	_(Opt_datasum,		DEPENDS_ON,	Opt_datacow),
+	_(Opt_compress,		DEPENDS_ON,	Opt_datacow),
+	_(Opt_compress,		DEPENDS_ON,	Opt_datasum),
+	_(Opt_compress_force,	DEPENDS_ON,	Opt_datacow),
+	_(Opt_compress_force,	DEPENDS_ON,	Opt_datasum),
+	_(Opt_compress_force,	DEPENDS_ON,	Opt_compress),
+	_(Opt_ssd_spread,	DEPENDS_ON,	Opt_ssd),
+	_(Opt_check_integrity_including_extent_data, DEPENDS_ON, Opt_check_integrity),
+};
+
+static const struct btrfs_implicit_rule btrfs_opt_contradictions[] = {
+	_(Opt_ssd,		CONTRADICTS,	Opt_nossd),
+	_(Opt_ssd_spread,	CONTRADICTS,	Opt_nossd),
+	_(Opt_nossd,		CONTRADICTS,	Opt_ssd),
+	_(Opt_nossd,		CONTRADICTS,	Opt_ssd_spread),
+};
+
+/*
+ * Parse the mount options into a summary of what is explicitly changed.
+ *
+ * This is slightly complicated for BtrFS compared to something like NFS as we
+ * superblock creation process starts by reading the configuration from disk
+ * and then applies any parameter changes on top of that.
+ *
+ * This means that we need to track what changes we're going to make and how
+ * they interact - but we have to bear in mind that parameters are not
+ * commutative.
+ */
+static int btrfs_parse_param(struct fs_context *fc, struct fs_parameter *param)
 {
-	substring_t args[MAX_OPT_ARGS];
-	char *p, *num;
-	u64 cache_gen;
-	int intarg;
-	int ret = 0;
-	char *compress_type;
-	bool compress_force = false;
-	enum btrfs_compression_type saved_compress_type;
-	bool saved_compress_force;
-	int no_compress = 0;
+	struct fs_parse_result result;
+	struct btrfs_fs_context *ctx = fc->fs_private;
+	unsigned int mask;
+	char **devs;
+	bool enabled;
+	int opt, i;
+	u8 bit;
 
-	cache_gen = btrfs_super_cache_generation(info->super_copy);
-	if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE))
-		btrfs_set_opt(info->mount_opt, FREE_SPACE_TREE);
-	else if (cache_gen)
-		btrfs_set_opt(info->mount_opt, SPACE_CACHE);
+	pr_notice("PARM[%s] = %s\n", param->key, param->string);
 
-	/*
-	 * Even the options are empty, we still need to do extra check
-	 * against new flags
+	opt = fs_parse(fc, &btrfs_fs_parameters, param, &result);
+	if (opt < 0)
+		return opt;
+	enabled = !result.negated;
+	__set_bit(opt, ctx->specified);
+
+	/* Do some initial weeding out of options that might not be or might no
+	 * longer be supported.
 	 */
-	if (!options)
-		goto check;
-
-	while ((p = strsep(&options, ",")) != NULL) {
-		int token;
-		if (!*p)
-			continue;
-
-		token = match_token(p, tokens, args);
-		switch (token) {
-		case Opt_degraded:
-			btrfs_info(info, "allowing degraded mounts");
-			btrfs_set_opt(info->mount_opt, DEGRADED);
+	switch (opt) {
+	case Opt_source:
+	case Opt_device:
+	case Opt_subvol:
+	case Opt_subvolid:
+	case Opt_subvolrootid:
+		switch (fc->purpose) {
+		case FS_CONTEXT_FOR_USER_MOUNT:
+		case FS_CONTEXT_FOR_KERNEL_MOUNT:
+		case FS_CONTEXT_FOR_SUBMOUNT:
 			break;
-		case Opt_subvol:
-		case Opt_subvol_empty:
-		case Opt_subvolid:
-		case Opt_subvolrootid:
-		case Opt_device:
-			/*
-			 * These are parsed by btrfs_parse_subvol_options or
-			 * btrfs_parse_device_options and can be ignored here.
-			 */
-			break;
-		case Opt_nodatasum:
-			btrfs_clear_and_info(info, DATASUM,
-					     "setting nodatasum");
-			break;
-		case Opt_datasum:
-			if (!btrfs_test_opt(info, DATASUM)) {
-				if (!btrfs_test_opt(info, DATACOW))
-					btrfs_info(info,
-						   "setting datasum, datacow enabled");
-				else
-					btrfs_info(info, "setting datasum");
-			}
-			btrfs_set_opt(info->mount_opt, DATACOW);
-			btrfs_set_opt(info->mount_opt, DATASUM);
-			break;
-		case Opt_nodatacow:
-			if (btrfs_test_opt(info, DATACOW)) {
-				if (!btrfs_test_opt(info, COMPRESS) ||
-				    !btrfs_test_opt(info, FORCE_COMPRESS)) {
-					btrfs_info(info,
-						   "setting nodatacow, compression disabled");
-				} else {
-					btrfs_info(info, "setting nodatacow");
-				}
-			}
-			btrfs_clear_opt(info->mount_opt, COMPRESS);
-			btrfs_clear_opt(info->mount_opt, FORCE_COMPRESS);
-			btrfs_clear_opt(info->mount_opt, DATACOW);
-			btrfs_clear_opt(info->mount_opt, DATASUM);
-			break;
-		case Opt_datacow:
-			btrfs_set_and_info(info, DATACOW,
-					   "setting datacow");
-			break;
-		case Opt_compress_force:
-		case Opt_compress_force_type:
-			compress_force = true;
-			/* Fallthrough */
-		case Opt_compress:
-		case Opt_compress_type:
-			saved_compress_type = btrfs_test_opt(info,
-							     COMPRESS) ?
-				info->compress_type : BTRFS_COMPRESS_NONE;
-			saved_compress_force =
-				btrfs_test_opt(info, FORCE_COMPRESS);
-			if (token == Opt_compress ||
-			    token == Opt_compress_force ||
-			    strncmp(args[0].from, "zlib", 4) == 0) {
-				compress_type = "zlib";
-
-				info->compress_type = BTRFS_COMPRESS_ZLIB;
-				info->compress_level = BTRFS_ZLIB_DEFAULT_LEVEL;
-				/*
-				 * args[0] contains uninitialized data since
-				 * for these tokens we don't expect any
-				 * parameter.
-				 */
-				if (token != Opt_compress &&
-				    token != Opt_compress_force)
-					info->compress_level =
-					  btrfs_compress_str2level(args[0].from);
-				btrfs_set_opt(info->mount_opt, COMPRESS);
-				btrfs_set_opt(info->mount_opt, DATACOW);
-				btrfs_set_opt(info->mount_opt, DATASUM);
-				no_compress = 0;
-			} else if (strncmp(args[0].from, "lzo", 3) == 0) {
-				compress_type = "lzo";
-				info->compress_type = BTRFS_COMPRESS_LZO;
-				btrfs_set_opt(info->mount_opt, COMPRESS);
-				btrfs_set_opt(info->mount_opt, DATACOW);
-				btrfs_set_opt(info->mount_opt, DATASUM);
-				btrfs_set_fs_incompat(info, COMPRESS_LZO);
-				no_compress = 0;
-			} else if (strcmp(args[0].from, "zstd") == 0) {
-				compress_type = "zstd";
-				info->compress_type = BTRFS_COMPRESS_ZSTD;
-				btrfs_set_opt(info->mount_opt, COMPRESS);
-				btrfs_set_opt(info->mount_opt, DATACOW);
-				btrfs_set_opt(info->mount_opt, DATASUM);
-				btrfs_set_fs_incompat(info, COMPRESS_ZSTD);
-				no_compress = 0;
-			} else if (strncmp(args[0].from, "no", 2) == 0) {
-				compress_type = "no";
-				btrfs_clear_opt(info->mount_opt, COMPRESS);
-				btrfs_clear_opt(info->mount_opt, FORCE_COMPRESS);
-				compress_force = false;
-				no_compress++;
-			} else {
-				ret = -EINVAL;
-				goto out;
-			}
-
-			if (compress_force) {
-				btrfs_set_opt(info->mount_opt, FORCE_COMPRESS);
-			} else {
-				/*
-				 * If we remount from compress-force=xxx to
-				 * compress=xxx, we need clear FORCE_COMPRESS
-				 * flag, otherwise, there is no way for users
-				 * to disable forcible compression separately.
-				 */
-				btrfs_clear_opt(info->mount_opt, FORCE_COMPRESS);
-			}
-			if ((btrfs_test_opt(info, COMPRESS) &&
-			     (info->compress_type != saved_compress_type ||
-			      compress_force != saved_compress_force)) ||
-			    (!btrfs_test_opt(info, COMPRESS) &&
-			     no_compress == 1)) {
-				btrfs_info(info, "%s %s compression, level %d",
-					   (compress_force) ? "force" : "use",
-					   compress_type, info->compress_level);
-			}
-			compress_force = false;
-			break;
-		case Opt_ssd:
-			btrfs_set_and_info(info, SSD,
-					   "enabling ssd optimizations");
-			btrfs_clear_opt(info->mount_opt, NOSSD);
-			break;
-		case Opt_ssd_spread:
-			btrfs_set_and_info(info, SSD,
-					   "enabling ssd optimizations");
-			btrfs_set_and_info(info, SSD_SPREAD,
-					   "using spread ssd allocation scheme");
-			btrfs_clear_opt(info->mount_opt, NOSSD);
-			break;
-		case Opt_nossd:
-			btrfs_set_opt(info->mount_opt, NOSSD);
-			btrfs_clear_and_info(info, SSD,
-					     "not using ssd optimizations");
-			/* Fallthrough */
-		case Opt_nossd_spread:
-			btrfs_clear_and_info(info, SSD_SPREAD,
-					     "not using spread ssd allocation scheme");
-			break;
-		case Opt_barrier:
-			btrfs_set_and_info(info, BARRIER,
-					     "turning on barriers");
-			break;
-		case Opt_nobarrier:
-			btrfs_clear_and_info(info, BARRIER,
-					   "turning off barriers");
-			break;
-		case Opt_thread_pool:
-			ret = match_int(&args[0], &intarg);
-			if (ret) {
-				goto out;
-			} else if (intarg == 0) {
-				ret = -EINVAL;
-				goto out;
-			}
-			info->thread_pool_size = intarg;
-			break;
-		case Opt_max_inline:
-			num = match_strdup(&args[0]);
-			if (num) {
-				info->max_inline = memparse(num, NULL);
-				kfree(num);
-
-				if (info->max_inline) {
-					info->max_inline = min_t(u64,
-						info->max_inline,
-						info->sectorsize);
-				}
-				btrfs_info(info, "max_inline at %llu",
-					   info->max_inline);
-			} else {
-				ret = -ENOMEM;
-				goto out;
-			}
-			break;
-		case Opt_alloc_start:
-			btrfs_info(info,
-				"option alloc_start is obsolete, ignored");
-			break;
-		case Opt_acl:
-#ifdef CONFIG_BTRFS_FS_POSIX_ACL
-			info->sb->s_flags |= SB_POSIXACL;
-			break;
-#else
-			btrfs_err(info, "support for ACL not compiled in!");
-			ret = -EINVAL;
-			goto out;
+		default:
+			return invalf(fc, "btrfs: %s prohibited for remount",
+				      param->key);
+		}
+#ifndef CONFIG_BTRFS_FS_POSIX_ACL
+	case Opt_acl:
+		if (!enabled)
+			return 0;
+		return invalf(fc, "btrfs: Support for 'acl' not compiled in!");
 #endif
-		case Opt_noacl:
-			info->sb->s_flags &= ~SB_POSIXACL;
+	case Opt_compress:
+	case Opt_compress_force:
+		if (result.uint_32 == Opt_compress__no) {
+			result.negated = true;
+			enabled = false;
+		}
+		break;
+	case Opt_space_cache:
+		if (result.negated)
+			result.uint_32 = Opt_space_cache__no;
+		break;
+	case Opt_recovery:
+		if (enabled) {
+			warnf(fc, "btrfs: 'recovery' is deprecated, use 'usebackuproot' instead");
+			result.key = Opt_usebackuproot;
+		} else {
+			btrfs_set_opt(ctx->mount_opt_mask, NOLOGREPLAY);
+			btrfs_set_opt(ctx->mount_opt, NOLOGREPLAY);
+		}
+		break;
+#ifndef CONFIG_BTRFS_FS_CHECK_INTEGRITY
+	case Opt_check_integrity_including_extent_data:
+	case Opt_check_integrity:
+	case Opt_check_integrity_print_mask:
+		return invalf(fc, "btrfs: Support for 'check_int*' not compiled in!");
+#endif
+#ifndef CONFIG_BTRFS_FS_REF_VERIFY
+	case Opt_ref_verify:
+		return 0;
+#endif
+#ifndef CONFIG_BTRFS_DEBUG
+	case Opt_fragment:
+		return invalf(fc, "btrfs: Support for 'fragment' not compiled in!");
+#endif
+	}
+
+	/* Map simple option-switching flags into 1-bit changes. */
+	bit = btrfs_flag_map[result.key].bit;
+	if (bit) {
+		bit &= ~BTRFS_FLAG_MAP_PRESENT;
+		mask = 1 << bit;
+		ctx->mount_opt_mask |= mask;
+		ctx->mount_opt_explicit |= mask;
+		ctx->mount_opt &= ~mask;
+		if (enabled)
+			ctx->mount_opt |= mask;
+	}
+
+	if (enabled) {
+		/* Turn on all the options that this one depends on. */
+		for (i = 0; i < ARRAY_SIZE(btrfs_opt_dependencies); i++) {
+			unsigned int target = btrfs_opt_dependencies[i].target;
+
+			if (btrfs_opt_dependencies[i].key != result.key)
+				continue;
+			bit = btrfs_flag_map[target].bit;
+			bit &= ~BTRFS_FLAG_MAP_PRESENT;
+			mask = 1 << bit;
+			if (!(ctx->mount_opt & mask))
+				ctx->mount_opt_explicit &= ~mask;
+			ctx->mount_opt_mask |= mask;
+			ctx->mount_opt |= mask;
+		}
+
+		/* Turn off all contradictory options. */
+		for (i = 0; i < ARRAY_SIZE(btrfs_opt_contradictions); i++) {
+			unsigned int target = btrfs_opt_contradictions[i].target;
+
+			if (btrfs_opt_contradictions[i].key != result.key)
+				continue;
+			bit = btrfs_flag_map[target].bit;
+			bit &= ~BTRFS_FLAG_MAP_PRESENT;
+			mask = 1 << bit;
+			if (!(ctx->mount_opt & mask))
+				ctx->mount_opt_explicit &= ~mask;
+			ctx->mount_opt_mask |= mask;
+			ctx->mount_opt &= ~mask;
+		}
+	} else {
+		/* Cancel all options that would implicitly turn this on */
+		for (i = 0; i < ARRAY_SIZE(btrfs_opt_dependencies); i++) {
+			unsigned int dep = btrfs_opt_dependencies[i].key;
+
+			if (btrfs_opt_dependencies[i].target != result.key)
+				continue;
+			bit = btrfs_flag_map[dep].bit;
+			bit &= ~BTRFS_FLAG_MAP_PRESENT;
+			mask = 1 << bit;
+			if (ctx->mount_opt & mask)
+				ctx->mount_opt_explicit &= ~mask;
+			ctx->mount_opt_mask |= mask;
+			ctx->mount_opt &= ~mask;
+		}
+	}
+
+	switch (result.key) {
+	case Opt_source:
+		if (fc->source)
+			return invalf(fc, "btrfs: Multiple sources specified");
+		fc->source = param->string;
+		param->string = NULL;
+		break;
+
+	case Opt_device:
+		devs = krealloc(ctx->devices,
+				sizeof(char *) * (ctx->nr_devices + 1),
+				GFP_KERNEL);
+		if (!devs)
+			return -ENOMEM;
+		devs[ctx->nr_devices] = param->string;
+		param->string = NULL;
+		ctx->devices = devs;
+		ctx->nr_devices++;
+		break;
+
+	case Opt_subvol:
+		kfree(ctx->subvol_name);
+		ctx->subvol_name = param->string; /* NULL if negative */
+		param->string = NULL;
+		break;
+	case Opt_subvolid:
+		/* we want the original fs_tree */
+		if (result.uint_64 == 0)
+			ctx->subvol_objectid = BTRFS_FS_TREE_OBJECTID;
+		else
+			ctx->subvol_objectid = result.uint_64;
+		break;
+	case Opt_subvolrootid:
+		break;
+
+	case Opt_compress:
+	case Opt_compress_force:
+		switch (result.uint_32) {
+		case Opt_compress__zlib_1 ... Opt_compress__zlib_9:
+			ctx->compress_type = BTRFS_COMPRESS_ZLIB;
+			ctx->compress_level =
+				result.uint_32 - Opt_compress__zlib_1 + 1;
 			break;
-		case Opt_notreelog:
-			btrfs_clear_and_info(info, TREELOG,
-					     "disabling tree log");
+		case Opt_compress__zlib:
+			ctx->compress_type = BTRFS_COMPRESS_ZLIB;
+			ctx->compress_level = BTRFS_ZLIB_DEFAULT_LEVEL;
 			break;
-		case Opt_treelog:
-			btrfs_set_and_info(info, TREELOG,
-					   "enabling tree log");
+		case Opt_compress__lzo:
+			ctx->compress_type = BTRFS_COMPRESS_LZO;
 			break;
-		case Opt_norecovery:
-		case Opt_nologreplay:
-			btrfs_set_and_info(info, NOLOGREPLAY,
-					   "disabling log replay at mount time");
+		case Opt_compress__zstd:
+			ctx->compress_type = BTRFS_COMPRESS_ZSTD;
 			break;
-		case Opt_flushoncommit:
-			btrfs_set_and_info(info, FLUSHONCOMMIT,
-					   "turning on flush-on-commit");
+		case Opt_compress__no:
+			ctx->compress_type = BTRFS_COMPRESS_NONE;
 			break;
-		case Opt_noflushoncommit:
-			btrfs_clear_and_info(info, FLUSHONCOMMIT,
-					     "turning off flush-on-commit");
+		}
+		break;
+
+	case Opt_thread_pool:
+		if (result.uint_32 == 0)
+			return invalf(fc, "btrfs: %s: Bad value", param->key);
+		ctx->thread_pool_size = result.uint_32;
+		break;
+	case Opt_max_inline:
+		ctx->max_inline = memparse(param->string, NULL);
+		break;
+	case Opt_alloc_start:
+		infof(fc, "btrfs: Option alloc_start is obsolete, ignored");
+		break;
+	case Opt_metadata_ratio:
+		ctx->metadata_ratio = result.uint_32;
+		infof(fc, "btrfs: metadata ratio %u", ctx->metadata_ratio);
+		break;
+
+	case Opt_space_cache:
+		btrfs_set_opt(ctx->mount_opt_mask, SPACE_CACHE);
+		btrfs_set_opt(ctx->mount_opt_mask, FREE_SPACE_TREE);
+		switch (result.uint_32) {
+		case Opt_space_cache__no:
+			btrfs_set_opt(ctx->mount_opt_explicit, SPACE_CACHE);
+			btrfs_set_opt(ctx->mount_opt_explicit, FREE_SPACE_TREE);
+			btrfs_clear_opt(ctx->mount_opt, SPACE_CACHE);
+			btrfs_clear_opt(ctx->mount_opt, FREE_SPACE_TREE);
 			break;
-		case Opt_ratio:
-			ret = match_int(&args[0], &intarg);
-			if (ret)
-				goto out;
-			info->metadata_ratio = intarg;
-			btrfs_info(info, "metadata ratio %u",
-				   info->metadata_ratio);
+		case Opt_space_cache__v1:
+			btrfs_set_opt(ctx->mount_opt_explicit, SPACE_CACHE);
+			btrfs_set_opt(ctx->mount_opt, SPACE_CACHE);
+			btrfs_clear_opt(ctx->mount_opt_explicit, FREE_SPACE_TREE);
+			btrfs_clear_opt(ctx->mount_opt, FREE_SPACE_TREE);
 			break;
-		case Opt_discard:
-			btrfs_set_and_info(info, DISCARD,
-					   "turning on discard");
+		case Opt_space_cache__v2:
+			btrfs_clear_opt(ctx->mount_opt_explicit, SPACE_CACHE);
+			btrfs_clear_opt(ctx->mount_opt, SPACE_CACHE);
+			btrfs_set_opt(ctx->mount_opt_explicit, FREE_SPACE_TREE);
+			btrfs_set_opt(ctx->mount_opt, FREE_SPACE_TREE);
 			break;
-		case Opt_nodiscard:
-			btrfs_clear_and_info(info, DISCARD,
-					     "turning off discard");
-			break;
-		case Opt_space_cache:
-		case Opt_space_cache_version:
-			if (token == Opt_space_cache ||
-			    strcmp(args[0].from, "v1") == 0) {
-				btrfs_clear_opt(info->mount_opt,
-						FREE_SPACE_TREE);
-				btrfs_set_and_info(info, SPACE_CACHE,
-					   "enabling disk space caching");
-			} else if (strcmp(args[0].from, "v2") == 0) {
-				btrfs_clear_opt(info->mount_opt,
-						SPACE_CACHE);
-				btrfs_set_and_info(info, FREE_SPACE_TREE,
-						   "enabling free space tree");
-			} else {
-				ret = -EINVAL;
-				goto out;
-			}
-			break;
-		case Opt_rescan_uuid_tree:
-			btrfs_set_opt(info->mount_opt, RESCAN_UUID_TREE);
-			break;
-		case Opt_no_space_cache:
-			if (btrfs_test_opt(info, SPACE_CACHE)) {
-				btrfs_clear_and_info(info, SPACE_CACHE,
-					     "disabling disk space caching");
-			}
-			if (btrfs_test_opt(info, FREE_SPACE_TREE)) {
-				btrfs_clear_and_info(info, FREE_SPACE_TREE,
-					     "disabling free space tree");
-			}
-			break;
-		case Opt_inode_cache:
-			btrfs_set_pending_and_info(info, INODE_MAP_CACHE,
-					   "enabling inode map caching");
-			break;
-		case Opt_noinode_cache:
-			btrfs_clear_pending_and_info(info, INODE_MAP_CACHE,
-					     "disabling inode map caching");
-			break;
-		case Opt_clear_cache:
-			btrfs_set_and_info(info, CLEAR_CACHE,
-					   "force clearing of disk cache");
-			break;
-		case Opt_user_subvol_rm_allowed:
-			btrfs_set_opt(info->mount_opt, USER_SUBVOL_RM_ALLOWED);
-			break;
-		case Opt_enospc_debug:
-			btrfs_set_opt(info->mount_opt, ENOSPC_DEBUG);
-			break;
-		case Opt_noenospc_debug:
-			btrfs_clear_opt(info->mount_opt, ENOSPC_DEBUG);
-			break;
-		case Opt_defrag:
-			btrfs_set_and_info(info, AUTO_DEFRAG,
-					   "enabling auto defrag");
-			break;
-		case Opt_nodefrag:
-			btrfs_clear_and_info(info, AUTO_DEFRAG,
-					     "disabling auto defrag");
-			break;
-		case Opt_recovery:
-			btrfs_warn(info,
-				   "'recovery' is deprecated, use 'usebackuproot' instead");
-			/* fall through */
-		case Opt_usebackuproot:
-			btrfs_info(info,
-				   "trying to use backup root at mount time");
-			btrfs_set_opt(info->mount_opt, USEBACKUPROOT);
-			break;
-		case Opt_skip_balance:
-			btrfs_set_opt(info->mount_opt, SKIP_BALANCE);
-			break;
+		}
+		break;
+
+	case Opt_acl:
+		if (enabled)
+			fc->sb_flags |= SB_POSIXACL;
+		else
+			fc->sb_flags &= ~SB_POSIXACL;
+		fc->sb_flags_mask |= SB_POSIXACL;
+		break;
+
 #ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY
-		case Opt_check_integrity_including_extent_data:
-			btrfs_info(info,
-				   "enabling check integrity including extent data");
-			btrfs_set_opt(info->mount_opt,
-				      CHECK_INTEGRITY_INCLUDING_EXTENT_DATA);
-			btrfs_set_opt(info->mount_opt, CHECK_INTEGRITY);
-			break;
-		case Opt_check_integrity:
-			btrfs_info(info, "enabling check integrity");
-			btrfs_set_opt(info->mount_opt, CHECK_INTEGRITY);
-			break;
-		case Opt_check_integrity_print_mask:
-			ret = match_int(&args[0], &intarg);
-			if (ret)
-				goto out;
-			info->check_integrity_print_mask = intarg;
-			btrfs_info(info, "check_integrity_print_mask 0x%x",
-				   info->check_integrity_print_mask);
-			break;
-#else
-		case Opt_check_integrity_including_extent_data:
-		case Opt_check_integrity:
-		case Opt_check_integrity_print_mask:
-			btrfs_err(info,
-				  "support for check_integrity* not compiled in!");
-			ret = -EINVAL;
-			goto out;
+	case Opt_check_integrity_print_mask:
+		ctx->check_integrity_print_mask = result.uint_32;
+		break;
 #endif
-		case Opt_fatal_errors:
-			if (strcmp(args[0].from, "panic") == 0)
-				btrfs_set_opt(info->mount_opt,
-					      PANIC_ON_FATAL_ERROR);
-			else if (strcmp(args[0].from, "bug") == 0)
-				btrfs_clear_opt(info->mount_opt,
-					      PANIC_ON_FATAL_ERROR);
-			else {
-				ret = -EINVAL;
-				goto out;
-			}
+
+	case Opt_fatal_errors:
+		ctx->mount_opt_explicit |= BTRFS_MOUNT_PANIC_ON_FATAL_ERROR;
+		switch (result.uint_32) {
+		case Opt_fatal_errors__bug:
+			ctx->mount_opt &= ~BTRFS_MOUNT_PANIC_ON_FATAL_ERROR;
 			break;
-		case Opt_commit_interval:
-			intarg = 0;
-			ret = match_int(&args[0], &intarg);
-			if (ret)
-				goto out;
-			if (intarg == 0) {
-				btrfs_info(info,
-					   "using default commit interval %us",
-					   BTRFS_DEFAULT_COMMIT_INTERVAL);
-				intarg = BTRFS_DEFAULT_COMMIT_INTERVAL;
-			} else if (intarg > 300) {
-				btrfs_warn(info, "excessive commit interval %d",
-					   intarg);
-			}
-			info->commit_interval = intarg;
+		case Opt_fatal_errors__panic:
+			ctx->mount_opt |= BTRFS_MOUNT_PANIC_ON_FATAL_ERROR;
 			break;
+		}
+		break;
+	case Opt_commit_interval:
+		if (result.uint_32 == 0) {
+			infof(fc, "btrfs: Using default commit interval %us",
+			      BTRFS_DEFAULT_COMMIT_INTERVAL);
+			result.uint_32 = BTRFS_DEFAULT_COMMIT_INTERVAL;
+		} else if (result.uint_32 > 300) {
+			warnf(fc, "btrfs: Excessive commit interval %u",
+			      result.uint_32);
+		}
+		ctx->commit_interval = result.uint_32;
+		break;
+
 #ifdef CONFIG_BTRFS_DEBUG
-		case Opt_fragment_all:
-			btrfs_info(info, "fragmenting all space");
-			btrfs_set_opt(info->mount_opt, FRAGMENT_DATA);
-			btrfs_set_opt(info->mount_opt, FRAGMENT_METADATA);
+	case Opt_fragment:
+		mask = 0;
+		switch (result.uint_32) {
+		case Opt_fragment__all:
+			mask  = BTRFS_MOUNT_FRAGMENT_DATA;
+			mask |= BTRFS_MOUNT_FRAGMENT_METADATA;
 			break;
-		case Opt_fragment_metadata:
-			btrfs_info(info, "fragmenting metadata");
-			btrfs_set_opt(info->mount_opt,
-				      FRAGMENT_METADATA);
+		case Opt_fragment__metadata:
+			mask = BTRFS_MOUNT_FRAGMENT_METADATA;
 			break;
-		case Opt_fragment_data:
-			btrfs_info(info, "fragmenting data");
-			btrfs_set_opt(info->mount_opt, FRAGMENT_DATA);
-			break;
-#endif
-#ifdef CONFIG_BTRFS_FS_REF_VERIFY
-		case Opt_ref_verify:
-			btrfs_info(info, "doing ref verification");
-			btrfs_set_opt(info->mount_opt, REF_VERIFY);
-			break;
-#endif
-		case Opt_err:
-			btrfs_info(info, "unrecognized mount option '%s'", p);
-			ret = -EINVAL;
-			goto out;
-		default:
+		case Opt_fragment__data:
+			mask |= BTRFS_MOUNT_FRAGMENT_DATA;
 			break;
 		}
+		ctx->mount_opt |= mask;
+		ctx->mount_opt_mask |= mask;
+		ctx->mount_opt_explicit |= mask;
+		break;
+#endif
+	default:
+		break;
 	}
-check:
-	/*
-	 * Extra check for current option against current flag
-	 */
-	if (btrfs_test_opt(info, NOLOGREPLAY) && !(new_flags & SB_RDONLY)) {
-		btrfs_err(info,
-			  "nologreplay must be used with ro mount option");
-		ret = -EINVAL;
-	}
-out:
-	if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE) &&
-	    !btrfs_test_opt(info, FREE_SPACE_TREE) &&
-	    !btrfs_test_opt(info, CLEAR_CACHE)) {
-		btrfs_err(info, "cannot disable free space tree");
-		ret = -EINVAL;
 
+	return 0;
+}
+
+static int btrfs_validate(struct fs_context *fc)
+{
+	struct btrfs_fs_context *ctx = fc->fs_private;
+
+	if (btrfs_raw_test_opt(ctx->mount_opt_mask, NOLOGREPLAY) &&
+	    btrfs_raw_test_opt(ctx->mount_opt, NOLOGREPLAY) &&
+	    !(fc->sb_flags & SB_RDONLY))
+		return invalf(fc, "nologreplay must be used with ro mount option");
+	return 0;
+}
+
+static int btrfs_dup_context(struct fs_context *fc, struct fs_context *src_fc)
+{
+	struct btrfs_fs_context *ctx, *src = src_fc->fs_private;
+	int i;
+
+	ctx = kmemdup(src, sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+	ctx->subvol_name = NULL;
+	ctx->devices = NULL;
+	ctx->root_mnt = NULL;
+
+	if (src->subvol_name) {
+		ctx->subvol_name = kstrdup(src->subvol_name, GFP_KERNEL);
+		if (!ctx->subvol_name)
+			goto nomem_ctx;
 	}
-	if (!ret && btrfs_test_opt(info, SPACE_CACHE))
-		btrfs_info(info, "disk space caching is enabled");
-	if (!ret && btrfs_test_opt(info, FREE_SPACE_TREE))
-		btrfs_info(info, "using free space tree");
-	return ret;
+
+	if (ctx->nr_devices) {
+		ctx->devices = kcalloc(ctx->nr_devices, sizeof(char *), GFP_KERNEL);
+		if (!ctx->devices)
+			goto nomem_sub;
+		for (i = 0; i < ctx->nr_devices; i++) {
+			ctx->devices[i] = kstrdup(src->devices[i], GFP_KERNEL);
+			if (!ctx->devices[i])
+				goto nomem_devs;
+		}
+	}
+
+	if (src_fc->source) {
+		fc->source = kstrdup(src_fc->source, GFP_KERNEL);
+		if (!fc->source)
+			goto nomem_devs;
+	}
+
+	fc->fs_private = ctx;
+	return 0;
+
+nomem_devs:
+	for (i = 0; i < ctx->nr_devices; i++)
+		kfree(ctx->devices[i]);
+	kfree(ctx->devices);
+nomem_sub:
+	kfree(ctx->subvol_name);
+nomem_ctx:
+	kfree(ctx);
+	return -ENOMEM;
+}
+
+static void btrfs_free_context(struct fs_context *fc)
+{
+	struct btrfs_fs_context *ctx = fc->fs_private;
+	struct btrfs_fs_info *info = fc->s_fs_info;
+	int i;
+
+	if (info)
+		free_fs_info(info);
+	if (ctx) {
+		mntput(ctx->root_mnt);
+		if (ctx->devices) {
+			for (i = 0; i < ctx->nr_devices; i++)
+				kfree(ctx->devices[i]);
+			kfree(ctx->devices);
+		}
+		kfree(ctx->subvol_name);
+		kfree(ctx);
+	}
+}
+
+static struct fs_context_operations btrfs_context_ops = {
+	.free		= btrfs_free_context,
+	.dup		= btrfs_dup_context,
+	.parse_param	= btrfs_parse_param,
+	.validate	= btrfs_validate,
+	.get_tree	= btrfs_get_tree,
+	.reconfigure	= btrfs_reconfigure,
+};
+
+/*
+ * Set up the filesystem configuration context.
+ */
+int btrfs_init_fs_context(struct fs_context *fc, struct dentry *reference)
+{
+	struct btrfs_fs_context *ctx;
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	fc->ops = &btrfs_context_ops;
+	fc->fs_private = ctx;
+	return 0;
+}
+
+static const char *btrfs_options[32] = {
+	[BTRFS_MOUNT_AUTO_DEFRAG]		= "auto defrag",
+	[BTRFS_MOUNT_BARRIER]			= "barriers",
+	[BTRFS_MOUNT_DATACOW]			= "datacow",
+	[BTRFS_MOUNT_DATASUM]			= "datasum",
+	[BTRFS_MOUNT_DEGRADED]			= "degraded mounts",
+	[BTRFS_MOUNT_DISCARD]			= "discard",
+	[BTRFS_MOUNT_FLUSHONCOMMIT]       	= "flush-on-commit",
+	[BTRFS_MOUNT_FREE_SPACE_TREE]		= "free space tree",
+	[BTRFS_MOUNT_INODE_MAP_CACHE]		= "inode map caching",
+	[BTRFS_MOUNT_NOLOGREPLAY]		= "no log replay at mount time",
+	[BTRFS_MOUNT_REF_VERIFY]		= "ref verification",
+	[BTRFS_MOUNT_SPACE_CACHE]		= "disk space caching",
+	[BTRFS_MOUNT_SSD]			= "ssd optimizations",
+	[BTRFS_MOUNT_SSD_SPREAD]		= "spread ssd allocation scheme",
+	[BTRFS_MOUNT_TREELOG]           	= "tree log",
+};
+
+/*
+ * Apply the configuration to a superblock.
+ */
+void btrfs_apply_configuration(struct fs_context *fc, struct super_block *sb)
+{
+	struct btrfs_fs_context *ctx = fc->fs_private;
+	struct btrfs_fs_info *info = sb->s_fs_info;
+	unsigned int changes = (ctx->mount_opt ^ info->mount_opt) & ctx->mount_opt_mask;
+	unsigned int explicit_changes = changes & ctx->mount_opt_explicit;
+	unsigned int implicit_changes = changes & ~ctx->mount_opt_explicit;
+	unsigned int mask, tmp;
+	int i;
+
+	for (tmp = explicit_changes; tmp; tmp &= ~(1 << i)) {
+		i = __ffs(tmp);
+		if (!btrfs_options[i])
+			continue;
+		if (ctx->mount_opt & (1 << i ))
+			btrfs_info(info, "Enabling %s", btrfs_options[i]);
+		else
+			btrfs_info(info, "Disabling %s", btrfs_options[i]);
+	}
+
+	for (tmp = implicit_changes; tmp; tmp &= ~(1 << i)) {
+		i = __ffs(tmp);
+		if (!btrfs_options[i])
+			continue;
+		if (ctx->mount_opt & (1 << i ))
+			btrfs_info(info, "Implicitly enabling %s", btrfs_options[i]);
+		else
+			btrfs_info(info, "Implicitly disabling %s", btrfs_options[i]);
+	}
+
+	if (btrfs_raw_test_opt(changes, COMPRESS) ||
+	    btrfs_raw_test_opt(changes, FORCE_COMPRESS)) {
+		if (btrfs_raw_test_opt(ctx->mount_opt, COMPRESS))
+			btrfs_info(info, "Enabling %s%s compression, level %d",
+				   btrfs_raw_test_opt(changes, FORCE_COMPRESS) ?
+				   "Forced " : "",
+				   btrfs_compress_type2str(ctx->compress_type),
+				   ctx->compress_level);
+		else if (btrfs_raw_test_opt(explicit_changes, COMPRESS))
+			btrfs_info(info, "Disabling compression");
+		else
+			btrfs_info(info, "Implicitly disabling compression");
+	}
+
+	if (test_bit(Opt_max_inline, ctx->specified)) {
+		u64 max_inline = min_t(u64, ctx->max_inline, info->sectorsize);
+
+		ctx->max_inline = max_inline;
+		if (max_inline != info->max_inline)
+			btrfs_info(info, "max_inline at %llu", ctx->max_inline);
+	}
+
+	if (test_bit(Opt_metadata_ratio, ctx->specified) &&
+	    ctx->metadata_ratio != info->metadata_ratio)
+		btrfs_info(info, "Metadata ratio %u", info->metadata_ratio);
+
+	if (btrfs_raw_test_opt(ctx->mount_opt & explicit_changes, CLEAR_CACHE))
+		btrfs_info(info, "Force clearing of disk cache");
+	if (btrfs_raw_test_opt(ctx->mount_opt & explicit_changes, USEBACKUPROOT))
+		btrfs_info(info, "Trying to use backup root at mount time");
+
+#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY
+	switch (ctx->mount_opt & explicit_changes &
+		((1 << BTRFS_MOUNT_CHECK_INTEGRITY) |
+		 (1 << BTRFS_MOUNT_CHECK_INTEGRITY_INCLUDING_EXTENT_DATA))) {
+	case (1 << BTRFS_MOUNT_CHECK_INTEGRITY):
+		btrfs_info(info, "Enabling check integrity");
+		break;
+	case ((1 << BTRFS_MOUNT_CHECK_INTEGRITY) |
+	      (1 << BTRFS_MOUNT_CHECK_INTEGRITY_INCLUDING_EXTENT_DATA)):
+		btrfs_info(info, "Enabling check integrity including extent data");
+		break;
+	}
+	if (test_bit(Opt_check_integrity_print_mask, ctx->specified) &&
+	    ctx->check_integrity_print_mask != info->check_integrity_print_mask)
+		btrfs_info(info, "check_integrity_print_mask 0x%x",
+			   ctx->check_integrity_print_mask);
+#endif
+
+#ifdef CONFIG_BTRFS_DEBUG
+	switch (ctx->mount_opt & explicit_changes &
+		((1 << BTRFS_MOUNT_FRAGMENT_DATA) |
+		 (1 << BTRFS_MOUNT_FRAGMENT_METADATA))) {
+	case 0:
+		break;
+	case (1 << BTRFS_MOUNT_FRAGMENT_DATA):
+		btrfs_info(info, "fragmenting data");
+		break;
+	case (1 << BTRFS_MOUNT_FRAGMENT_METADATA):
+		btrfs_info(info, "fragmenting metadata");
+		break;
+	default:
+		btrfs_info(info, "fragmenting all space");
+		break;
+	}
+#endif
+
+	/* Actually apply the options */
+	mask = changes;
+	mask &= ~(1 << BTRFS_MOUNT_INODE_MAP_CACHE);
+	tmp = READ_ONCE(info->mount_opt);
+	tmp &= ~mask;
+	tmp |= ctx->mount_opt & mask;
+
+	info->compress_level = ctx->compress_level;
+	info->compress_type = ctx->compress_type;
+	WRITE_ONCE(info->mount_opt, tmp);
+
+	if (btrfs_raw_test_opt(explicit_changes, INODE_MAP_CACHE)) {
+		if (btrfs_test_opt(info, INODE_MAP_CACHE)) {
+			btrfs_set_pending((info), SET_INODE_MAP_CACHE);
+			btrfs_clear_pending((info), CLEAR_INODE_MAP_CACHE);
+		} else {
+			btrfs_set_pending((info), CLEAR_INODE_MAP_CACHE);
+			btrfs_clear_pending((info), SET_INODE_MAP_CACHE);
+		}
+	}
+
+	if (test_bit(Opt_commit_interval, ctx->specified))
+		info->commit_interval = ctx->commit_interval;
+	if (test_bit(Opt_thread_pool, ctx->specified))
+		info->thread_pool_size = ctx->thread_pool_size;
+#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY
+	if (test_bit(Opt_check_integrity_print_mask, ctx->specified))
+		info->check_integrity_print_mask = ctx->check_integrity_print_mask;
+#endif
+	if (test_bit(Opt_metadata_ratio, ctx->specified))
+		info->metadata_ratio = info->metadata_ratio;
+	if (test_bit(Opt_max_inline, ctx->specified))
+		info->max_inline = ctx->max_inline;
 }
 
 /*
- * Parse mount options that are required early in the mount process.
- *
- * All other options will be parsed on much later in the mount process and
- * only when we need to allocate a new super block.
+ * Display the options for /proc/mounts
  */
-int btrfs_parse_device_options(const char *options, fmode_t flags, void *holder)
-{
-	substring_t args[MAX_OPT_ARGS];
-	char *device_name, *opts, *orig, *p;
-	struct btrfs_device *device = NULL;
-	int error = 0;
-
-	lockdep_assert_held(&uuid_mutex);
-
-	if (!options)
-		return 0;
-
-	/*
-	 * strsep changes the string, duplicate it because btrfs_parse_options
-	 * gets called later
-	 */
-	opts = kstrdup(options, GFP_KERNEL);
-	if (!opts)
-		return -ENOMEM;
-	orig = opts;
-
-	while ((p = strsep(&opts, ",")) != NULL) {
-		int token;
-
-		if (!*p)
-			continue;
-
-		token = match_token(p, tokens, args);
-		if (token == Opt_device) {
-			device_name = match_strdup(&args[0]);
-			if (!device_name) {
-				error = -ENOMEM;
-				goto out;
-			}
-			device = btrfs_scan_one_device(device_name, flags,
-					holder);
-			kfree(device_name);
-			if (IS_ERR(device)) {
-				error = PTR_ERR(device);
-				goto out;
-			}
-		}
-	}
-
-out:
-	kfree(orig);
-	return error;
-}
-
-/*
- * Parse mount options that are related to subvolume id
- *
- * The value is later passed to mount_subvol()
- */
-int btrfs_parse_subvol_options(const char *options, char **subvol_name,
-			       u64 *subvol_objectid)
-{
-	substring_t args[MAX_OPT_ARGS];
-	char *opts, *orig, *p;
-	int error = 0;
-	u64 subvolid;
-
-	if (!options)
-		return 0;
-
-	/*
-	 * strsep changes the string, duplicate it because
-	 * btrfs_parse_device_options gets called later
-	 */
-	opts = kstrdup(options, GFP_KERNEL);
-	if (!opts)
-		return -ENOMEM;
-	orig = opts;
-
-	while ((p = strsep(&opts, ",")) != NULL) {
-		int token;
-		if (!*p)
-			continue;
-
-		token = match_token(p, tokens, args);
-		switch (token) {
-		case Opt_subvol:
-			kfree(*subvol_name);
-			*subvol_name = match_strdup(&args[0]);
-			if (!*subvol_name) {
-				error = -ENOMEM;
-				goto out;
-			}
-			break;
-		case Opt_subvolid:
-			error = match_u64(&args[0], &subvolid);
-			if (error)
-				goto out;
-
-			/* we want the original fs_tree */
-			if (subvolid == 0)
-				subvolid = BTRFS_FS_TREE_OBJECTID;
-
-			*subvol_objectid = subvolid;
-			break;
-		case Opt_subvolrootid:
-			pr_warn("BTRFS: 'subvolrootid' mount option is deprecated and has no effect\n");
-			break;
-		default:
-			break;
-		}
-	}
-
-out:
-	kfree(orig);
-	return error;
-}
-
 int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)
 {
 	struct btrfs_fs_info *info = btrfs_sb(dentry->d_sb);
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index fc3eda2..2ac2fd2 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -17,7 +17,8 @@
 #include <linux/writeback.h>
 #include <linux/statfs.h>
 #include <linux/compat.h>
-#include <linux/parser.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/ctype.h>
 #include <linux/namei.h>
 #include <linux/miscdevice.h>
@@ -50,19 +51,6 @@
 
 static const struct super_operations btrfs_super_ops;
 
-/*
- * Types for mounting the default subvolume and a subvolume explicitly
- * requested by subvol=/path. That way the callchain is straightforward and we
- * don't have to play tricks with the mount options and recursive calls to
- * btrfs_mount.
- *
- * The new btrfs_root_fs_type also servers as a tag for the bdev_holder.
- */
-static struct file_system_type btrfs_fs_type;
-static struct file_system_type btrfs_root_fs_type;
-
-static int btrfs_remount(struct super_block *sb, int *flags, char *data);
-
 const char *btrfs_decode_error(int errno)
 {
 	char *errstr = "unknown";
@@ -468,9 +456,9 @@ static int get_default_subvol_objectid(struct btrfs_fs_info *fs_info, u64 *objec
 	return 0;
 }
 
-static int btrfs_fill_super(struct super_block *sb,
-			    struct btrfs_fs_devices *fs_devices,
-			    void *data)
+static int btrfs_fill_super(struct fs_context *fc,
+			    struct super_block *sb,
+			    struct btrfs_fs_devices *fs_devices)
 {
 	struct inode *inode;
 	struct btrfs_fs_info *fs_info = btrfs_sb(sb);
@@ -496,7 +484,7 @@ static int btrfs_fill_super(struct super_block *sb,
 		return err;
 	}
 
-	err = open_ctree(sb, fs_devices, (char *)data);
+	err = open_ctree(fc, sb, fs_devices);
 	if (err) {
 		btrfs_err(fs_info, "open_ctree failed");
 		return err;
@@ -569,22 +557,14 @@ int btrfs_sync_fs(struct super_block *sb, int wait)
 	return btrfs_commit_transaction(trans);
 }
 
-static int btrfs_test_super(struct super_block *s, void *data)
+static int btrfs_test_super(struct super_block *s, struct fs_context *fc)
 {
-	struct btrfs_fs_info *p = data;
+	struct btrfs_fs_info *p = fc->s_fs_info;
 	struct btrfs_fs_info *fs_info = btrfs_sb(s);
 
 	return fs_info->fs_devices == p->fs_devices;
 }
 
-static int btrfs_set_super(struct super_block *s, void *data)
-{
-	int err = set_anon_super(s, data);
-	if (!err)
-		s->s_fs_info = data;
-	return err;
-}
-
 /*
  * subvolumes are identified by ino 256
  */
@@ -595,34 +575,32 @@ static inline int is_subvolume_inode(struct inode *inode)
 	return 0;
 }
 
-static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid,
-				   const char *device_name, struct vfsmount *mnt)
+static int mount_subvol(struct fs_context *fc)
 {
+	struct btrfs_fs_context *ctx = fc->fs_private;
+	struct vfsmount *mnt = ctx->root_mnt;
 	struct dentry *root;
 	int ret;
 
-	if (!subvol_name) {
-		if (!subvol_objectid) {
+	if (!ctx->subvol_name) {
+		char *subvol_name;
+
+		if (!ctx->subvol_objectid) {
 			ret = get_default_subvol_objectid(btrfs_sb(mnt->mnt_sb),
-							  &subvol_objectid);
-			if (ret) {
-				root = ERR_PTR(ret);
-				goto out;
-			}
+							  &ctx->subvol_objectid);
+			if (ret < 0)
+				return ret;
 		}
 		subvol_name = get_subvol_name_from_objectid(btrfs_sb(mnt->mnt_sb),
-							    subvol_objectid);
-		if (IS_ERR(subvol_name)) {
-			root = ERR_CAST(subvol_name);
-			subvol_name = NULL;
-			goto out;
-		}
-
+							    ctx->subvol_objectid);
+		if (IS_ERR(subvol_name))
+			return PTR_ERR(subvol_name);
+		ctx->subvol_name = subvol_name;
 	}
 
-	root = mount_subtree(mnt, subvol_name);
-	/* mount_subtree() drops our reference on the vfsmount. */
-	mnt = NULL;
+	root = mount_subtree(mnt, ctx->subvol_name);
+	/* mount_subtree() dropped our reference on the vfsmount. */
+	ctx->root_mnt = NULL;
 
 	if (!IS_ERR(root)) {
 		struct super_block *s = root->d_sb;
@@ -633,10 +611,11 @@ static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid,
 		ret = 0;
 		if (!is_subvolume_inode(root_inode)) {
 			btrfs_err(fs_info, "'%s' is not a valid subvolume",
-			       subvol_name);
+				  ctx->subvol_name);
 			ret = -EINVAL;
 		}
-		if (subvol_objectid && root_objectid != subvol_objectid) {
+		if (ctx->subvol_objectid &&
+		    root_objectid != ctx->subvol_objectid) {
 			/*
 			 * This will also catch a race condition where a
 			 * subvolume which was passed by ID is renamed and
@@ -644,56 +623,19 @@ static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid,
 			 */
 			btrfs_err(fs_info,
 				  "subvol '%s' does not match subvolid %llu",
-				  subvol_name, subvol_objectid);
+				  ctx->subvol_name, ctx->subvol_objectid);
 			ret = -EINVAL;
 		}
-		if (ret) {
+		if (ret < 0) {
 			dput(root);
-			root = ERR_PTR(ret);
 			deactivate_locked_super(s);
+			goto out;
 		}
 	}
 
+	fc->root = root;
+	ret = 0;
 out:
-	mntput(mnt);
-	kfree(subvol_name);
-	return root;
-}
-
-static int parse_security_options(char *orig_opts,
-				  struct security_mnt_opts *sec_opts)
-{
-	return security_sb_eat_lsm_opts(orig_opts, sec_opts);
-}
-
-static int setup_security_options(struct btrfs_fs_info *fs_info,
-				  struct super_block *sb,
-				  struct security_mnt_opts *sec_opts)
-{
-	int ret = 0;
-
-	/*
-	 * Call security_sb_set_mnt_opts() to check whether new sec_opts
-	 * is valid.
-	 */
-	ret = security_sb_set_mnt_opts(sb, sec_opts, 0, NULL);
-	if (ret)
-		return ret;
-
-#ifdef CONFIG_SECURITY
-	if (!fs_info->security_opts.num_mnt_opts) {
-		/* first time security setup, copy sec_opts to fs_info */
-		memcpy(&fs_info->security_opts, sec_opts, sizeof(*sec_opts));
-	} else {
-		/*
-		 * Since SELinux (the only one supporting security_mnt_opts)
-		 * does NOT support changing context during remount/mount of
-		 * the same sb, this must be the same or part of the same
-		 * security options, just free it.
-		 */
-		security_free_mnt_opts(sec_opts);
-	}
-#endif
 	return ret;
 }
 
@@ -711,6 +653,21 @@ static struct btrfs_fs_info *btrfs_alloc_fs_info(void)
 		btrfs_set_opt(fs_info->mount_opt, DATACOW);
 		btrfs_set_opt(fs_info->mount_opt, BARRIER);
 		btrfs_set_opt(fs_info->mount_opt, TREELOG);
+
+		fs_info->commit_interval = BTRFS_DEFAULT_COMMIT_INTERVAL;
+		fs_info->max_inline = BTRFS_DEFAULT_MAX_INLINE;
+		fs_info->metadata_ratio = 0;
+		fs_info->thread_pool_size = min_t(unsigned long,
+						  num_online_cpus() + 2, 8);
+#ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY
+		fs_info->check_integrity_print_mask = 0;
+#endif
+
+		/*
+		 * In the long term, we'll store the compression type in the super
+		 * block, and it'll be used for per file compression control.
+		 */
+		fs_info->compress_type = BTRFS_COMPRESS_ZLIB;
 	}
 
 	return fs_info;
@@ -722,28 +679,20 @@ static struct btrfs_fs_info *btrfs_alloc_fs_info(void)
  * Note: This is based on mount_bdev from fs/super.c with a few additions
  *       for multiple device setup.  Make sure to keep it in sync.
  */
-static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
-		int flags, const char *device_name, void *data)
+static int btrfs_root_get_tree(struct fs_context *fc)
 {
+	struct btrfs_fs_context *ctx = fc->fs_private;
 	struct block_device *bdev = NULL;
 	struct super_block *s;
 	struct btrfs_device *device = NULL;
 	struct btrfs_fs_devices *fs_devices = NULL;
 	struct btrfs_fs_info *fs_info = NULL;
-	struct security_mnt_opts new_sec_opts;
 	fmode_t mode = FMODE_READ;
-	int error = 0;
+	int error = 0, i;
 
-	if (!(flags & SB_RDONLY))
+	if (!(fc->sb_flags & SB_RDONLY))
 		mode |= FMODE_WRITE;
 
-	security_init_mnt_opts(&new_sec_opts);
-	if (data) {
-		error = parse_security_options(data, &new_sec_opts);
-		if (error)
-			return ERR_PTR(error);
-	}
-
 	/*
 	 * Setup a dummy root and fs_info for test/set super.  This is because
 	 * we don't actually fill this stuff out until open_ctree, but we need
@@ -753,25 +702,30 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
 	fs_info = btrfs_alloc_fs_info();
 	if (!fs_info) {
 		error = -ENOMEM;
-		goto error_sec_opts;
+		goto error;
 	}
 
 	fs_info->super_copy = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_KERNEL);
 	fs_info->super_for_commit = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_KERNEL);
-	security_init_mnt_opts(&fs_info->security_opts);
 	if (!fs_info->super_copy || !fs_info->super_for_commit) {
 		error = -ENOMEM;
 		goto error_fs_info;
 	}
 
 	mutex_lock(&uuid_mutex);
-	error = btrfs_parse_device_options(data, mode, fs_type);
-	if (error) {
-		mutex_unlock(&uuid_mutex);
-		goto error_fs_info;
+
+	if (ctx->devices) {
+		for (i = 0; i < ctx->nr_devices; i++) {
+			device = btrfs_scan_one_device(ctx->devices[i], mode);
+			if (IS_ERR(device)) {
+				mutex_unlock(&uuid_mutex);
+				error = PTR_ERR(device);
+				goto error_fs_info;
+			}
+		}
 	}
 
-	device = btrfs_scan_one_device(device_name, mode, fs_type);
+	device = btrfs_scan_one_device(fc->source, mode);
 	if (IS_ERR(device)) {
 		mutex_unlock(&uuid_mutex);
 		error = PTR_ERR(device);
@@ -781,59 +735,117 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
 	fs_devices = device->fs_devices;
 	fs_info->fs_devices = fs_devices;
 
-	error = btrfs_open_devices(fs_devices, mode, fs_type);
+	error = btrfs_open_devices(fs_devices, mode);
 	mutex_unlock(&uuid_mutex);
 	if (error)
 		goto error_fs_info;
 
-	if (!(flags & SB_RDONLY) && fs_devices->rw_devices == 0) {
+	if (!(fc->sb_flags & SB_RDONLY) && fs_devices->rw_devices == 0) {
 		error = -EACCES;
 		goto error_close_devices;
 	}
 
+	if (btrfs_fs_compat_ro(fs_info, FREE_SPACE_TREE) &&
+	    !btrfs_test_opt(fs_info, FREE_SPACE_TREE) &&
+	    !btrfs_test_opt(fs_info, CLEAR_CACHE)) {
+		btrfs_err(fs_info, "cannot disable free space tree");
+		error = -EINVAL;
+		goto error_close_devices;
+	}
+	if (btrfs_test_opt(fs_info, SPACE_CACHE))
+		btrfs_info(fs_info, "disk space caching is enabled");
+	if (btrfs_test_opt(fs_info, FREE_SPACE_TREE))
+		btrfs_info(fs_info, "using free space tree");
+
 	bdev = fs_devices->latest_bdev;
-	s = sget(fs_type, btrfs_test_super, btrfs_set_super, flags | SB_NOSEC,
-		 fs_info);
+	fc->s_fs_info = fs_info;
+	s = sget_fc(fc, btrfs_test_super, set_anon_super_fc);
 	if (IS_ERR(s)) {
 		error = PTR_ERR(s);
 		goto error_close_devices;
 	}
 
 	if (s->s_root) {
-		btrfs_close_devices(fs_devices);
-		free_fs_info(fs_info);
-		if ((flags ^ s->s_flags) & SB_RDONLY)
+		btrfs_close_devices(fs_info->fs_devices);
+		if ((fc->sb_flags ^ s->s_flags) & SB_RDONLY)
 			error = -EBUSY;
 	} else {
 		snprintf(s->s_id, sizeof(s->s_id), "%pg", bdev);
-		btrfs_sb(s)->bdev_holder = fs_type;
-		error = btrfs_fill_super(s, fs_devices, data);
+		error = btrfs_fill_super(fc, s, fs_devices);
 	}
-	if (error) {
-		deactivate_locked_super(s);
-		goto error_sec_opts;
-	}
+	if (error)
+		goto error_super;
 
-	fs_info = btrfs_sb(s);
-	error = setup_security_options(fs_info, s, &new_sec_opts);
-	if (error) {
-		deactivate_locked_super(s);
-		goto error_sec_opts;
-	}
+	fc->root = dget(s->s_root);
+	return 0;
 
-	return dget(s->s_root);
-
+error_super:
+	deactivate_locked_super(s);
 error_close_devices:
 	btrfs_close_devices(fs_devices);
 error_fs_info:
 	free_fs_info(fs_info);
-error_sec_opts:
-	security_free_mnt_opts(&new_sec_opts);
-	return ERR_PTR(error);
+error:
+	return error;
+}
+
+static int btrfs_mount_root(struct fs_context *fc, unsigned int rdonly)
+{
+	struct btrfs_fs_context *ctx, *root_ctx;
+	struct fs_context *root_fc;
+	struct vfsmount *root_mnt;
+	int ret;
+
+	root_fc = vfs_dup_fs_context(fc, FS_CONTEXT_FOR_ROOT_MOUNT);
+	if (IS_ERR(root_fc))
+		return PTR_ERR(root_fc);
+
+	root_fc->sb_flags &= ~SB_RDONLY;
+	root_fc->sb_flags |= rdonly | SB_NOSEC;
+	root_ctx = root_fc->fs_private;
+	root_ctx->root_mnt = NULL;
+	root_ctx->root = true;
+
+	ret = vfs_get_tree(root_fc);
+	if (ret < 0)
+		goto error_fc;
+
+	root_mnt = vfs_create_mount(root_fc, 0);
+	if (IS_ERR(root_mnt)) {
+		ret = PTR_ERR(root_mnt);
+		goto error_fc;
+	}
+
+	ctx = fc->fs_private;
+	ctx->root_mnt = root_mnt;
+	ret = 0;
+
+error_fc:
+	put_fs_context(root_fc);
+	return ret;
+}
+
+static int btrfs_reconfigure_root_to_rw(struct fs_context *fc,
+					struct super_block *sb)
+{
+	int error;
+	struct fs_context root_fc = {
+		.purpose	= FS_CONTEXT_FOR_RECONFIGURE,
+		.fs_type	= sb->s_type,
+		.root		= sb->s_root,
+		.log		= fc->log,
+		.sb_flags	= 0,
+		.sb_flags_mask	= SB_RDONLY,
+	};
+
+	down_write(&sb->s_umount);
+	error = btrfs_reconfigure(&root_fc);
+	up_write(&sb->s_umount);
+	return error;
 }
 
 /*
- * Mount function which is called by VFS layer.
+ * Superblock creation function which is called by VFS layer.
  *
  * In order to allow mounting a subvolume directly, btrfs uses mount_subtree()
  * which needs vfsmount* of device's root (/).  This means device's root has to
@@ -842,77 +854,49 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
  * Operation flow:
  *   1. Parse subvol id related options for later use in mount_subvol().
  *
- *   2. Mount device's root (/) by calling vfs_kern_mount().
- *
- *      NOTE: vfs_kern_mount() is used by VFS to call btrfs_mount() in the
- *      first place. In order to avoid calling btrfs_mount() again, we use
- *      different file_system_type which is not registered to VFS by
- *      register_filesystem() (btrfs_root_fs_type). As a result,
- *      btrfs_mount_root() is called. The return value will be used by
- *      mount_subtree() in mount_subvol().
+ *   2. Mount device's root (/).
  *
  *   3. Call mount_subvol() to get the dentry of subvolume. Since there is
  *      "btrfs subvolume set-default", mount_subvol() is called always.
  */
-static struct dentry *btrfs_mount(struct file_system_type *fs_type, int flags,
-		const char *device_name, void *data)
+int btrfs_get_tree(struct fs_context *fc)
 {
-	struct vfsmount *mnt_root;
-	struct dentry *root;
+	struct btrfs_fs_context *ctx = fc->fs_private;
 	fmode_t mode = FMODE_READ;
-	char *subvol_name = NULL;
-	u64 subvol_objectid = 0;
 	int error = 0;
 
-	if (!(flags & SB_RDONLY))
+	if (ctx->root)
+		return btrfs_root_get_tree(fc);
+	
+	if (!(fc->sb_flags & SB_RDONLY))
 		mode |= FMODE_WRITE;
 
-	error = btrfs_parse_subvol_options(data, &subvol_name,
-					&subvol_objectid);
-	if (error) {
-		kfree(subvol_name);
-		return ERR_PTR(error);
-	}
-
 	/* mount device's root (/) */
-	mnt_root = vfs_kern_mount(&btrfs_root_fs_type, flags, device_name, data);
-	if (PTR_ERR_OR_ZERO(mnt_root) == -EBUSY) {
-		if (flags & SB_RDONLY) {
-			mnt_root = vfs_kern_mount(&btrfs_root_fs_type,
-				flags & ~SB_RDONLY, device_name, data);
-		} else {
-			mnt_root = vfs_kern_mount(&btrfs_root_fs_type,
-				flags | SB_RDONLY, device_name, data);
-			if (IS_ERR(mnt_root)) {
-				root = ERR_CAST(mnt_root);
-				goto out;
-			}
+	error = btrfs_mount_root(fc, fc->sb_flags & SB_RDONLY);
+	if (error < 0) {
+		if (error != -EBUSY)
+			return error;
 
-			down_write(&mnt_root->mnt_sb->s_umount);
-			error = btrfs_remount(mnt_root->mnt_sb, &flags, NULL);
-			up_write(&mnt_root->mnt_sb->s_umount);
-			if (error < 0) {
-				root = ERR_PTR(error);
-				mntput(mnt_root);
-				goto out;
-			}
+		error = btrfs_mount_root(
+			fc, (fc->sb_flags & SB_RDONLY) ^ SB_RDONLY);
+		if (error < 0)
+			return error;
+
+		if (!(fc->sb_flags & SB_RDONLY)) {
+			error = btrfs_reconfigure_root_to_rw(fc, ctx->root_mnt->mnt_sb);
+			if (error < 0)
+				return error;
 		}
 	}
-	if (IS_ERR(mnt_root)) {
-		root = ERR_CAST(mnt_root);
-		goto out;
-	}
 
-	/* mount_subvol() will free subvol_name and mnt_root */
-	root = mount_subvol(subvol_name, subvol_objectid, device_name, mnt_root);
-
-out:
-	return root;
+	return mount_subvol(fc);
 }
 
 static void btrfs_resize_thread_pool(struct btrfs_fs_info *fs_info,
-				     u32 new_pool_size, u32 old_pool_size)
+				     u32 new_pool_size)
 {
+	u32 old_pool_size = fs_info->thread_pool_size;
+
 	if (new_pool_size == old_pool_size)
 		return;
 
@@ -943,15 +927,16 @@ static inline void btrfs_remount_prepare(struct btrfs_fs_info *fs_info)
 }
 
 static inline void btrfs_remount_begin(struct btrfs_fs_info *fs_info,
-				       unsigned long old_opts, int flags)
+				       unsigned long old_opts,
+				       struct fs_context *fc)
 {
 	if (btrfs_raw_test_opt(old_opts, AUTO_DEFRAG) &&
 	    (!btrfs_raw_test_opt(fs_info->mount_opt, AUTO_DEFRAG) ||
-	     (flags & SB_RDONLY))) {
+	     (fc->sb_flags & SB_RDONLY))) {
 		/* wait for any defraggers to finish */
 		wait_event(fs_info->transaction_wait,
 			   (atomic_read(&fs_info->defrag_running) == 0));
-		if (flags & SB_RDONLY)
+		if (fc->sb_flags & SB_RDONLY)
 			sync_filesystem(fs_info->sb);
 	}
 }
@@ -971,8 +956,16 @@ static inline void btrfs_remount_cleanup(struct btrfs_fs_info *fs_info,
 	clear_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state);
 }
 
-static int btrfs_remount(struct super_block *sb, int *flags, char *data)
+/*
+ * Change the configuration of an active superblock according to the supplied
+ * parameters.  Note that the parameter pointer (fc->fs_private) may be NULL in
+ * the case of umount detach, emergency remount R/O and get_tree remounting as
+ * R/W.
+ */
+int btrfs_reconfigure(struct fs_context *fc)
 {
+	struct btrfs_fs_context *ctx = fc->fs_private;
+	struct super_block *sb = fc->root->d_sb;
 	struct btrfs_fs_info *fs_info = btrfs_sb(sb);
 	struct btrfs_root *root = fs_info->tree_root;
 	unsigned old_flags = sb->s_flags;
@@ -985,34 +978,16 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
 
 	sync_filesystem(sb);
 	btrfs_remount_prepare(fs_info);
+	if (ctx)
+		btrfs_apply_configuration(fc, sb);
+	btrfs_remount_begin(fs_info, old_opts, fc);
+	if (ctx)
+		btrfs_resize_thread_pool(fs_info, ctx->thread_pool_size);
 
-	if (data) {
-		struct security_mnt_opts new_sec_opts;
-
-		security_init_mnt_opts(&new_sec_opts);
-		ret = parse_security_options(data, &new_sec_opts);
-		if (ret)
-			goto restore;
-		ret = setup_security_options(fs_info, sb,
-					     &new_sec_opts);
-		if (ret) {
-			security_free_mnt_opts(&new_sec_opts);
-			goto restore;
-		}
-	}
-
-	ret = btrfs_parse_options(fs_info, data, *flags);
-	if (ret)
-		goto restore;
-
-	btrfs_remount_begin(fs_info, old_opts, *flags);
-	btrfs_resize_thread_pool(fs_info,
-		fs_info->thread_pool_size, old_thread_pool_size);
-
-	if ((bool)(*flags & SB_RDONLY) == sb_rdonly(sb))
+	if ((bool)(fc->sb_flags & SB_RDONLY) == sb_rdonly(sb))
 		goto out;
 
-	if (*flags & SB_RDONLY) {
+	if (fc->sb_flags & SB_RDONLY) {
 		/*
 		 * this also happens on 'umount -rf' or on shutdown, when
 		 * the filesystem is busy.
@@ -1113,11 +1088,10 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
 	if (sb_rdonly(sb))
 		old_flags |= SB_RDONLY;
 	sb->s_flags = old_flags;
-	fs_info->mount_opt = old_opts;
+	WRITE_ONCE(fs_info->mount_opt, old_opts);
 	fs_info->compress_type = old_compress_type;
 	fs_info->max_inline = old_max_inline;
-	btrfs_resize_thread_pool(fs_info,
-		old_thread_pool_size, fs_info->thread_pool_size);
+	btrfs_resize_thread_pool(fs_info, old_thread_pool_size);
 	fs_info->metadata_ratio = old_metadata_ratio;
 	btrfs_remount_cleanup(fs_info, old_opts);
 	return ret;
@@ -1400,20 +1374,13 @@ static void btrfs_kill_super(struct super_block *sb)
 	free_fs_info(fs_info);
 }
 
-static struct file_system_type btrfs_fs_type = {
-	.owner		= THIS_MODULE,
-	.name		= "btrfs",
-	.mount		= btrfs_mount,
-	.kill_sb	= btrfs_kill_super,
-	.fs_flags	= FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA,
-};
-
-static struct file_system_type btrfs_root_fs_type = {
-	.owner		= THIS_MODULE,
-	.name		= "btrfs",
-	.mount		= btrfs_mount_root,
-	.kill_sb	= btrfs_kill_super,
-	.fs_flags	= FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA,
+struct file_system_type btrfs_fs_type = {
+	.owner			= THIS_MODULE,
+	.name			= "btrfs",
+	.init_fs_context	= btrfs_init_fs_context,
+	.parameters		= &btrfs_fs_parameters,
+	.kill_sb		= btrfs_kill_super,
+	.fs_flags		= FS_REQUIRES_DEV,
 };
 
 MODULE_ALIAS_FS("btrfs");
@@ -1449,15 +1416,13 @@ static long btrfs_control_ioctl(struct file *file, unsigned int cmd,
 	switch (cmd) {
 	case BTRFS_IOC_SCAN_DEV:
 		mutex_lock(&uuid_mutex);
-		device = btrfs_scan_one_device(vol->name, FMODE_READ,
-					       &btrfs_root_fs_type);
+		device = btrfs_scan_one_device(vol->name, FMODE_READ);
 		ret = PTR_ERR_OR_ZERO(device);
 		mutex_unlock(&uuid_mutex);
 		break;
 	case BTRFS_IOC_DEVICES_READY:
 		mutex_lock(&uuid_mutex);
-		device = btrfs_scan_one_device(vol->name, FMODE_READ,
-					       &btrfs_root_fs_type);
+		device = btrfs_scan_one_device(vol->name, FMODE_READ);
 		if (IS_ERR(device)) {
 			mutex_unlock(&uuid_mutex);
 			ret = PTR_ERR(device);
@@ -1554,7 +1519,6 @@ static const struct super_operations btrfs_super_ops = {
 	.alloc_inode	= btrfs_alloc_inode,
 	.destroy_inode	= btrfs_destroy_inode,
 	.statfs		= btrfs_statfs,
-	.remount_fs	= btrfs_remount,
 	.freeze_fs	= btrfs_freeze,
 	.unfreeze_fs	= btrfs_unfreeze,
 };
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index f435d39..7178c33 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -380,13 +380,13 @@ static noinline struct btrfs_fs_devices *find_fsid(u8 *fsid)
 }
 
 static int
-btrfs_get_bdev_and_sb(const char *device_path, fmode_t flags, void *holder,
+btrfs_get_bdev_and_sb(const char *device_path, fmode_t flags,
 		      int flush, struct block_device **bdev,
 		      struct buffer_head **bh)
 {
 	int ret;
 
-	*bdev = blkdev_get_by_path(device_path, flags, holder);
+	*bdev = blkdev_get_by_path(device_path, flags, &btrfs_fs_type);
 
 	if (IS_ERR(*bdev)) {
 		ret = PTR_ERR(*bdev);
@@ -678,8 +678,7 @@ static void btrfs_free_stale_devices(const char *path,
 }
 
 static int btrfs_open_one_device(struct btrfs_fs_devices *fs_devices,
-			struct btrfs_device *device, fmode_t flags,
-			void *holder)
+				 struct btrfs_device *device, fmode_t flags)
 {
 	struct request_queue *q;
 	struct block_device *bdev;
@@ -693,8 +692,7 @@ static int btrfs_open_one_device(struct btrfs_fs_devices *fs_devices,
 	if (!device->name)
 		return -EINVAL;
 
-	ret = btrfs_get_bdev_and_sb(device->name->str, flags, holder, 1,
-				    &bdev, &bh);
+	ret = btrfs_get_bdev_and_sb(device->name->str, flags, 1, &bdev, &bh);
 	if (ret)
 		return ret;
 
@@ -1094,8 +1092,7 @@ int btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
 	return ret;
 }
 
-static int open_fs_devices(struct btrfs_fs_devices *fs_devices,
-				fmode_t flags, void *holder)
+static int open_fs_devices(struct btrfs_fs_devices *fs_devices, fmode_t flags)
 {
 	struct btrfs_device *device;
 	struct btrfs_device *latest_dev = NULL;
@@ -1105,7 +1102,7 @@ static int open_fs_devices(struct btrfs_fs_devices *fs_devices,
 
 	list_for_each_entry(device, &fs_devices->devices, dev_list) {
 		/* Just open everything we can; ignore failures here */
-		if (btrfs_open_one_device(fs_devices, device, flags, holder))
+		if (btrfs_open_one_device(fs_devices, device, flags))
 			continue;
 
 		if (!latest_dev ||
@@ -1137,8 +1134,7 @@ static int devid_cmp(void *priv, struct list_head *a, struct list_head *b)
 	return 0;
 }
 
-int btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
-		       fmode_t flags, void *holder)
+int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, fmode_t flags)
 {
 	int ret;
 
@@ -1150,7 +1146,7 @@ int btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
 		ret = 0;
 	} else {
 		list_sort(NULL, &fs_devices->devices, devid_cmp);
-		ret = open_fs_devices(fs_devices, flags, holder);
+		ret = open_fs_devices(fs_devices, flags);
 	}
 	mutex_unlock(&fs_devices->device_list_mutex);
 
@@ -1213,8 +1209,7 @@ static int btrfs_read_disk_super(struct block_device *bdev, u64 bytenr,
  * and we are not allowed to call set_blocksize during the scan. The superblock
  * is read via pagecache
  */
-struct btrfs_device *btrfs_scan_one_device(const char *path, fmode_t flags,
-					   void *holder)
+struct btrfs_device *btrfs_scan_one_device(const char *path, fmode_t flags)
 {
 	struct btrfs_super_block *disk_super;
 	bool new_device_added = false;
@@ -1234,7 +1229,7 @@ struct btrfs_device *btrfs_scan_one_device(const char *path, fmode_t flags,
 	bytenr = btrfs_sb_offset(0);
 	flags |= FMODE_EXCL;
 
-	bdev = blkdev_get_by_path(path, flags, holder);
+	bdev = blkdev_get_by_path(path, flags, &btrfs_fs_type);
 	if (IS_ERR(bdev))
 		return ERR_CAST(bdev);
 
@@ -2125,8 +2120,7 @@ static struct btrfs_device *btrfs_find_device_by_path(
 	struct buffer_head *bh;
 	struct btrfs_device *device;
 
-	ret = btrfs_get_bdev_and_sb(device_path, FMODE_READ,
-				    fs_info->bdev_holder, 0, &bdev, &bh);
+	ret = btrfs_get_bdev_and_sb(device_path, FMODE_READ, 0, &bdev, &bh);
 	if (ret)
 		return ERR_PTR(ret);
 	disk_super = (struct btrfs_super_block *)bh->b_data;
@@ -2343,7 +2337,7 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
 		return -EROFS;
 
 	bdev = blkdev_get_by_path(device_path, FMODE_WRITE | FMODE_EXCL,
-				  fs_info->bdev_holder);
+				  &btrfs_fs_type);
 	if (IS_ERR(bdev))
 		return PTR_ERR(bdev);
 
@@ -6592,7 +6586,7 @@ static struct btrfs_fs_devices *open_seed_devices(struct btrfs_fs_info *fs_info,
 	if (IS_ERR(fs_devices))
 		return fs_devices;
 
-	ret = open_fs_devices(fs_devices, FMODE_READ, fs_info->bdev_holder);
+	ret = open_fs_devices(fs_devices, FMODE_READ);
 	if (ret) {
 		free_fs_devices(fs_devices);
 		fs_devices = ERR_PTR(ret);
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index aefce89..6d359d9 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -402,10 +402,8 @@ void btrfs_mapping_init(struct btrfs_mapping_tree *tree);
 void btrfs_mapping_tree_free(struct btrfs_mapping_tree *tree);
 blk_status_t btrfs_map_bio(struct btrfs_fs_info *fs_info, struct bio *bio,
 			   int mirror_num, int async_submit);
-int btrfs_open_devices(struct btrfs_fs_devices *fs_devices,
-		       fmode_t flags, void *holder);
-struct btrfs_device *btrfs_scan_one_device(const char *path,
-					   fmode_t flags, void *holder);
+int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, fmode_t flags);
+struct btrfs_device *btrfs_scan_one_device(const char *path, fmode_t flags);
 int btrfs_close_devices(struct btrfs_fs_devices *fs_devices);
 void btrfs_free_extra_devids(struct btrfs_fs_devices *fs_devices, int step);
 void btrfs_assign_next_active_device(struct btrfs_device *device,