diff mbox series

[RFC,7/8] btrfs: Convert to fs_context

Message ID 20200812163654.17080-8-marcos@mpdesouza.com (mailing list archive)
State New, archived
Headers show
Series btrfs: convert to fscontext | expand

Commit Message

Marcos Paulo de Souza Aug. 12, 2020, 4:36 p.m. UTC
From: Marcos Paulo de Souza <mpdesouza@suse.com>

This commit makes btrfs full use of the fs_context API. It's heavily
based in David Howells POC when fs_context was introduced[1]. As
fs_context divides the mount procedure into different steps, the btrfs
code needed to be rearranged in order to store the mount points and
other properties without having a fs_info already allocated.

The btrfs_fs_context struct is the responsible struct for storing the
options used to mount the fs, and these options are set to
fs_info->mount_opts in btrfs_apply_configuration.

Some notable changes:
* There is no need for a second file_system_type anymore. Now we use a
  special flag in btrfs_fs_context to identify the mount of the root
  subvol before mounting the specified subvol.
* With the introduction of fs_context all mount options are parsed
  _before_ doing the mount procedure. In our case, btrfs_fc_parse_param
  is the function being called to parse all options, making
  btrfs_parse_device_options, btrfs_parse_subvol_options
  btrfs_parse_options obsolete.
* Function btrfs_mount_root was renamed to btrfs_root_get_tree to
  reflect that fs_context is being used.
* Function btrfs_remount was renamed to btrfs_reconfigure by the same
  reason from above.
* Function btrfs_mount was renamed to btrfs_get_tree, and this function
  is called by vfs after all the mount options are parsed.
* There is no need for btrfs_root_fs_type anymore, and so fs_info->bdev_holder
  can also be removed.

Functions like open_ctree and btrfs_fill_super are now using a
fs_context argument instead of other btrfs related structs, since
the fs_context->fs_private contains a btrfs_fs_context that holds
the necessary data.

[1]: https://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git/commit/?h=Q46&id=554cb2019cda83e1aba10bd9eea485afd2ddb983

Signed-off-by: Marcos Paulo de Souza <mpdesouza@suse.com>
---
 fs/btrfs/ctree.h       |   1 +
 fs/btrfs/dev-replace.c |   2 +-
 fs/btrfs/disk-io.c     |  10 +-
 fs/btrfs/disk-io.h     |   4 +-
 fs/btrfs/super.c       | 347 ++++++++++++++++++++---------------------
 fs/btrfs/volumes.c     |   6 +-
 6 files changed, 178 insertions(+), 192 deletions(-)

Comments

David Sterba Aug. 17, 2020, 1:26 p.m. UTC | #1
On Wed, Aug 12, 2020 at 01:36:53PM -0300, Marcos Paulo de Souza wrote:
> From: Marcos Paulo de Souza <mpdesouza@suse.com>
> 
> This commit makes btrfs full use of the fs_context API. It's heavily
> based in David Howells POC when fs_context was introduced[1]. As
> fs_context divides the mount procedure into different steps, the btrfs
> code needed to be rearranged in order to store the mount points and
> other properties without having a fs_info already allocated.
> 
> The btrfs_fs_context struct is the responsible struct for storing the
> options used to mount the fs, and these options are set to
> fs_info->mount_opts in btrfs_apply_configuration.
> 
> Some notable changes:
> * There is no need for a second file_system_type anymore. Now we use a
>   special flag in btrfs_fs_context to identify the mount of the root
>   subvol before mounting the specified subvol.
> * With the introduction of fs_context all mount options are parsed
>   _before_ doing the mount procedure. In our case, btrfs_fc_parse_param
>   is the function being called to parse all options, making
>   btrfs_parse_device_options, btrfs_parse_subvol_options
>   btrfs_parse_options obsolete.
> * Function btrfs_mount_root was renamed to btrfs_root_get_tree to
>   reflect that fs_context is being used.
> * Function btrfs_remount was renamed to btrfs_reconfigure by the same
>   reason from above.
> * Function btrfs_mount was renamed to btrfs_get_tree, and this function
>   is called by vfs after all the mount options are parsed.
> * There is no need for btrfs_root_fs_type anymore, and so fs_info->bdev_holder
>   can also be removed.
> 
> Functions like open_ctree and btrfs_fill_super are now using a
> fs_context argument instead of other btrfs related structs, since
> the fs_context->fs_private contains a btrfs_fs_context that holds
> the necessary data.
> 
> [1]: https://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs.git/commit/?h=Q46&id=554cb2019cda83e1aba10bd9eea485afd2ddb983
> 
> Signed-off-by: Marcos Paulo de Souza <mpdesouza@suse.com>
> ---
>  fs/btrfs/ctree.h       |   1 +
>  fs/btrfs/dev-replace.c |   2 +-
>  fs/btrfs/disk-io.c     |  10 +-
>  fs/btrfs/disk-io.h     |   4 +-
>  fs/btrfs/super.c       | 347 ++++++++++++++++++++---------------------
>  fs/btrfs/volumes.c     |   6 +-
>  6 files changed, 178 insertions(+), 192 deletions(-)
> 
> diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
> index d96bce2ea5bb..9060be6a6c6e 100644
> --- a/fs/btrfs/ctree.h
> +++ b/fs/btrfs/ctree.h
> @@ -3100,6 +3100,7 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
>  			struct btrfs_root *root);
>  
>  /* super.c */
> +extern struct file_system_type btrfs_fs_type;

This and the related changes shouldn't be in this patch.

>  int btrfs_parse_options(struct btrfs_fs_info *info, char *options,
>  			unsigned long new_flags);
>  int btrfs_apply_configuration(struct fs_context *fc, struct super_block *sb);
> diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c
> index db93909b25e0..9a3275845b13 100644
> --- a/fs/btrfs/dev-replace.c
> +++ b/fs/btrfs/dev-replace.c
> @@ -236,7 +236,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);

So clean the bdev_holder in a separate patch.

>  	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 c850d7f44fbe..f2e7543e9913 100644
> --- a/fs/btrfs/disk-io.c
> +++ b/fs/btrfs/disk-io.c
> @@ -2894,8 +2894,7 @@ static int btrfs_check_uuid_tree(struct btrfs_fs_info *fs_info)
>  	return 0;
>  }
>  
> -int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_devices,
> -		      char *options)
> +int __cold open_ctree(struct fs_context *fc, struct super_block *sb)
>  {
>  	u32 sectorsize;
>  	u32 nodesize;
> @@ -2905,6 +2904,7 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
>  	u16 csum_type;
>  	struct btrfs_super_block *disk_super;
>  	struct btrfs_fs_info *fs_info = btrfs_sb(sb);
> +	struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
>  	struct btrfs_root *tree_root;
>  	struct btrfs_root *chunk_root;
>  	int ret;
> @@ -3030,11 +3030,9 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
>  	 */
>  	fs_info->compress_type = BTRFS_COMPRESS_ZLIB;
>  
> -	ret = btrfs_parse_options(fs_info, options, sb->s_flags);
> -	if (ret) {
> -		err = ret;
> +	err = btrfs_apply_configuration(fc, sb);
> +	if (err)
>  		goto fail_alloc;
> -	}
>  
>  	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 00dc39d47ed3..712ea61ffdfe 100644
> --- a/fs/btrfs/disk-io.h
> +++ b/fs/btrfs/disk-io.h
> @@ -50,9 +50,7 @@ struct extent_buffer *btrfs_find_create_tree_block(
>  						struct btrfs_fs_info *fs_info,
>  						u64 bytenr);
>  void btrfs_clean_tree_block(struct extent_buffer *buf);
> -int __cold open_ctree(struct super_block *sb,
> -	       struct btrfs_fs_devices *fs_devices,
> -	       char *options);
> +int __cold open_ctree(struct fs_context *fc, struct super_block *sb);
>  void __cold close_ctree(struct btrfs_fs_info *fs_info);
>  int write_all_supers(struct btrfs_fs_info *fs_info, int max_mirrors);
>  struct btrfs_super_block *btrfs_read_dev_super(struct block_device *bdev);
> diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
> index 5bbf4b947125..d9a0faea8c88 100644
> --- a/fs/btrfs/super.c
> +++ b/fs/btrfs/super.c
> @@ -63,10 +63,9 @@ static const struct super_operations btrfs_super_ops;
>   *
>   * 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);
> +static int btrfs_reconfigure(struct fs_context *fc);
>  
>  /*
>   * Generally the error codes correspond to their respective errors, but there
> @@ -1542,7 +1541,7 @@ static int btrfs_parse_device_options(const char *options, fmode_t flags,
>  				goto out;
>  			}
>  			device = btrfs_scan_one_device(device_name, flags,
> -					holder);
> +							&btrfs_fs_type);
>  			kfree(device_name);
>  			if (IS_ERR(device)) {
>  				error = PTR_ERR(device);
> @@ -1795,13 +1794,13 @@ 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)
>  {
> +	int err;
>  	struct inode *inode;
>  	struct btrfs_fs_info *fs_info = btrfs_sb(sb);
> -	int err;
>  
>  	sb->s_maxbytes = MAX_LFS_FILESIZE;
>  	sb->s_magic = BTRFS_SUPER_MAGIC;
> @@ -1810,9 +1809,6 @@ static int btrfs_fill_super(struct super_block *sb,
>  	sb->s_export_op = &btrfs_export_ops;
>  	sb->s_xattr = btrfs_xattr_handlers;
>  	sb->s_time_gran = 1;
> -#ifdef CONFIG_BTRFS_FS_POSIX_ACL
> -	sb->s_flags |= SB_POSIXACL;
> -#endif
>  	sb->s_flags |= SB_I_VERSION;
>  	sb->s_iflags |= SB_I_CGROUPWB;
>  
> @@ -1822,7 +1818,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);
>  	if (err) {
>  		btrfs_err(fs_info, "open_ctree failed");
>  		return err;
> @@ -1951,19 +1947,20 @@ int btrfs_apply_configuration(struct fs_context *fc,
>  #endif
>  
>  #ifdef CONFIG_BTRFS_FS_REF_VERIFY
> -       if (btrfs_test_exp_opt(ctx, REF_VERIFY))
> -               btrfs_info(info, "doing ref verification");
> +	if (btrfs_test_exp_opt(ctx, REF_VERIFY))
> +		btrfs_info(info, "doing ref verification");
>  #endif
>  
> +	info->pending_changes = ctx->pending_changes;
>  	if (btrfs_test_exp_opt(ctx, INODE_MAP_CACHE)) {
> -		if (btrfs_opt_map[Opt_inode_cache].enabled) {
> -		       btrfs_clear_pending_and_info(info, INODE_MAP_CACHE,
> -				       "disabling inode map caching");
> +		if (!btrfs_opt_map[Opt_inode_cache].enabled) {
> +			btrfs_clear_pending_and_info(info, INODE_MAP_CACHE,
> +						"disabling inode map caching");
>  		} else {
>  			btrfs_info(info,
>  				"the 'inode_cache' option is deprecated and will have no effect from 5.11");
>  			btrfs_set_pending_and_info(info, INODE_MAP_CACHE,
> -				       "enabling inode map caching");
> +						"enabling inode map caching");

Whitespace-only change

>  		}
>  	}
>  
> @@ -2015,10 +2012,9 @@ int btrfs_apply_configuration(struct fs_context *fc,
>  	if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) {
>  		bool opt_clear = btrfs_test_opt(ctx, CLEAR_CACHE);
>  		bool opt_cache_v1 = btrfs_test_opt(ctx, SPACE_CACHE);
> -		bool no_space_cache = ctx->nospace_cache;
>  
> -		if ((no_space_cache && !opt_clear) || (opt_cache_v1 && !opt_clear)) {
> -			btrfs_err(info, "cannot disable free space tree XX");
> +		if ((ctx->nospace_cache && !opt_clear) || (opt_cache_v1 && !opt_clear)) {
> +			btrfs_err(info, "cannot disable free space tree");
>  			return -EINVAL;
>  		}
>  	}
> @@ -2063,7 +2059,6 @@ int btrfs_apply_configuration(struct fs_context *fc,
>  
>  	info->compress_type = ctx->compress_type;
>  	info->compress_level = ctx->compress_level;
> -	info->pending_changes = ctx->pending_changes;
>  	info->mount_opt = ctx->mount_opt;
>  
>  	return 0;
> @@ -2213,9 +2208,9 @@ static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)
>  	return 0;
>  }
>  
> -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;
> @@ -2239,34 +2234,34 @@ static inline int is_subvolume_inode(struct inode *inode)
>  	return 0;
>  }
>  
> -static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid,
> -				   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->subvolid) {
>  			ret = get_default_subvol_objectid(btrfs_sb(mnt->mnt_sb),
> -							  &subvol_objectid);
> -			if (ret) {
> -				root = ERR_PTR(ret);
> -				goto out;
> -			}
> +							  &ctx->subvolid);
> +			if (ret)
> +				return ret;
>  		}
>  		subvol_name = btrfs_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;
> -		}
> +							btrfs_sb(mnt->mnt_sb),
> +							ctx->subvolid);
> +		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;
> @@ -2277,10 +2272,10 @@ 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->subvolid && root_objectid != ctx->subvolid) {
>  			/*
>  			 * This will also catch a race condition where a
>  			 * subvolume which was passed by ID is renamed and
> @@ -2288,20 +2283,23 @@ 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->subvolid);
>  			ret = -EINVAL;
>  		}
>  		if (ret) {
>  			dput(root);
> -			root = ERR_PTR(ret);
>  			deactivate_locked_super(s);
> +			goto out;
>  		}
> +	} else {
> +		return PTR_ERR(root);
>  	}
>  
> +	fc->root = root;
> +	ret = 0;
> +
>  out:
> -	mntput(mnt);
> -	kfree(subvol_name);
> -	return root;
> +	return ret;
>  }
>  
>  /*
> @@ -2310,27 +2308,20 @@ static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid,
>   * 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)

The new naming is confusing as we use 'get' for reference counting in
connection with 'root' and 'tree'. You can keep btrfs_mount_root as it's
clear it's for mount.

>  {
> -	struct block_device *bdev = NULL;
> +	struct block_device *bdev;
>  	struct super_block *s;
> +	struct btrfs_fs_context *ctx = fc->fs_private;
>  	struct btrfs_device *device = NULL;
>  	struct btrfs_fs_devices *fs_devices = NULL;
>  	struct btrfs_fs_info *fs_info = NULL;
> -	void *new_sec_opts = NULL;
>  	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;
>  
> -	if (data) {
> -		error = security_sb_eat_lsm_opts(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
> @@ -2340,10 +2331,9 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
>  	 * superblock with our given fs_devices later on at sget() time.
>  	 */
>  	fs_info = kvzalloc(sizeof(struct btrfs_fs_info), GFP_KERNEL);
> -	if (!fs_info) {
> -		error = -ENOMEM;
> -		goto error_sec_opts;
> -	}
> +	if (!fs_info)
> +		return -ENOMEM;
> +
>  	btrfs_init_fs_info(fs_info);
>  
>  	fs_info->super_copy = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_KERNEL);
> @@ -2354,13 +2344,20 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
>  	}
>  
>  	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,
> +							&btrfs_fs_type);
> +			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, &btrfs_fs_type);
>  	if (IS_ERR(device)) {
>  		mutex_unlock(&uuid_mutex);
>  		error = PTR_ERR(device);
> @@ -2370,53 +2367,49 @@ 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, &btrfs_fs_type);
>  	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;
>  	}
>  
>  	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;
>  	}
>  
> +	 /* sget_fc returned a previously allocated superblocl. */

Typo 'superblocl'.

>  	if (s->s_root) {
> -		btrfs_close_devices(fs_devices);
> -		btrfs_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;
> -		if (!strstr(crc32c_impl(), "generic"))
> -			set_bit(BTRFS_FS_CSUM_IMPL_FAST, &fs_info->flags);
> -		error = btrfs_fill_super(s, fs_devices, data);
> -	}
> -	if (!error)
> -		error = security_sb_set_mnt_opts(s, new_sec_opts, 0, NULL);
> -	security_free_mnt_opts(&new_sec_opts);
> -	if (error) {
> -		deactivate_locked_super(s);
> -		return ERR_PTR(error);
> +		error = btrfs_fill_super(fc, s, fs_devices);
>  	}
>  
> -	return dget(s->s_root);
> +	if (error)
> +		goto error_super;
> +
> +	fc->root = dget(s->s_root);
> +	return 0;
>  
> +error_super:
> +	deactivate_locked_super(s);
>  error_close_devices:
>  	btrfs_close_devices(fs_devices);
>  error_fs_info:
> -	btrfs_free_fs_info(fs_info);
> -error_sec_opts:
> -	security_free_mnt_opts(&new_sec_opts);
> -	return ERR_PTR(error);
> +	/*btrfs_free_fs_info(fs_info);*/

Stray comment

> +	return error;
>  }
>  
>  /*
> @@ -2424,7 +2417,7 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
>   * btrfs_get_tree will be called recursively, but then will check for the
>   * ctx->root being set and call btrfs_root_get_tree.
>   */
> -static int btrfs_mount_root_fc(struct fs_context *fc, unsigned int rdonly)
> +int btrfs_mount_root_fc(struct fs_context *fc, unsigned int rdonly)
>  {
>  	struct btrfs_fs_context *ctx, *root_ctx;
>  	struct fs_context *root_fc;
> @@ -2460,81 +2453,76 @@ static int btrfs_mount_root_fc(struct fs_context *fc, unsigned int rdonly)
>  	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.
> + * Mount function which is called by VFS layer after the argument parsing.
>   *
>   * 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
>   * be mounted internally in any case.
>   *
>   * Operation flow:
> - *   1. Parse subvol id related options for later use in mount_subvol().
> - *
> - *   2. Mount device's root (/) by calling vfs_kern_mount().
> + *   1. Mount device's root (/) by calling btrfs_mount_root_fc.
>   *
> - *      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. btrfs_get_tree is called recursively by btrfs_mount_fc -> fc_mount ->
> + *      btrfs_get_tree, because we use the same file_system_type for both root and
> + *      subvol mounts. We avoid the infinite recursive calls by checking the
> + *      ctx->root member being set, which shows that we are trying to mount the
> + *      root fs.
>   *
>   *   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)
> +static int btrfs_get_tree(struct fs_context *fc)

Please keep 'mount' somewhere in the function name.

>  {
> -	struct vfsmount *mnt_root;
> -	struct dentry *root;
> -	char *subvol_name = NULL;
> -	u64 subvol_objectid = 0;
> -	int error = 0;
> +	struct btrfs_fs_context *ctx = fc->fs_private;
> +	int error = btrfs_fc_validate(fc);
>  
> -	error = btrfs_parse_subvol_options(data, &subvol_name,
> -					&subvol_objectid);
> -	if (error) {
> -		kfree(subvol_name);
> -		return ERR_PTR(error);
> -	}
> +	if (error)
> +		return 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);
> -				kfree(subvol_name);
> -				goto out;
> -			}
> +	if (!fc->source)
> +		return invalf(fc, "No source specified");
>  
> -			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);
> -				kfree(subvol_name);
> -				goto out;
> -			}
> +	if (ctx->root)
> +		return btrfs_root_get_tree(fc);
> +
> +	/* mount device's root (/) */
> +	error = btrfs_mount_root_fc(fc, fc->sb_flags & SB_RDONLY);
> +	if (error == -EBUSY) {
> +		/*
> +		 * If returned EBUSY, try again the mount inverting the rdonly
> +		 * argument of btrfs_mount_root.
> +		 */
> +		error = btrfs_mount_root_fc(fc, (fc->sb_flags & SB_RDONLY) ^ SB_RDONLY);
> +		if (!error && !(fc->sb_flags & SB_RDONLY)) {
> +			error = btrfs_reconfigure_root_to_rw(fc,
> +						ctx->root_mnt->mnt_sb);
>  		}
>  	}
> -	if (IS_ERR(mnt_root)) {
> -		root = ERR_CAST(mnt_root);
> -		kfree(subvol_name);
> -		goto out;
> -	}
>  
> -	/* mount_subvol() will free subvol_name and mnt_root */
> -	root = mount_subvol(subvol_name, subvol_objectid, mnt_root);
> +	if (error < 0)
> +		return error;
>  
> -out:
> -	return root;
> +	return mount_subvol(fc);
>  }
>  
>  static void btrfs_resize_thread_pool(struct btrfs_fs_info *fs_info,
> @@ -2598,8 +2586,16 @@ static inline void btrfs_remount_cleanup(struct btrfs_fs_info *fs_info,
>  		btrfs_discard_cleanup(fs_info);
>  }
>  
> -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)

For some reason I don't like referring to mount-related things by terms
with overloaded meaning. Keeping btrfs_remount would be just fine.

>  {
> +	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;
> @@ -2608,34 +2604,28 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
>  	u64 old_max_inline = fs_info->max_inline;
>  	u32 old_thread_pool_size = fs_info->thread_pool_size;
>  	u32 old_metadata_ratio = fs_info->metadata_ratio;
> -	int ret;
> +	int ret = btrfs_fc_validate(fc);
> +
> +	if (ret)
> +		return ret;
>  
>  	sync_filesystem(sb);
>  	set_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state);
> -
> -	if (data) {
> -		void *new_sec_opts = NULL;
> -
> -		ret = security_sb_eat_lsm_opts(data, &new_sec_opts);
> -		if (!ret)
> -			ret = security_sb_remount(sb, new_sec_opts);
> -		security_free_mnt_opts(&new_sec_opts);
> +	if (ctx) {
> +		ret = btrfs_apply_configuration(fc, sb);
>  		if (ret)
>  			goto restore;
>  	}
>  
> -	ret = btrfs_parse_options(fs_info, data, *flags);
> -	if (ret)
> -		goto restore;
> +	btrfs_remount_begin(fs_info, old_opts, fc->sb_flags);
> +	if (ctx && ctx->thread_pool_size)
> +		btrfs_resize_thread_pool(fs_info, ctx->thread_pool_size,
> +					old_thread_pool_size);
>  
> -	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.
> @@ -2735,7 +2725,7 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
>  	 * We need to set SB_I_VERSION here otherwise it'll get cleared by VFS,
>  	 * since the absence of the flag means it can be toggled off by remount.
>  	 */
> -	*flags |= SB_I_VERSION;
> +	fc->sb_flags |= SB_I_VERSION;
>  
>  	wake_up_process(fs_info->transaction_kthread);
>  	btrfs_remount_cleanup(fs_info, old_opts);
> @@ -3058,9 +3048,11 @@ static void btrfs_fc_free(struct fs_context *fc)
>  }
>  
>  static const struct fs_context_operations btrfs_context_ops = {
> -	.dup = btrfs_dup_fc,
> -	.free = btrfs_fc_free,
> -	.parse_param = btrfs_fc_parse_param,
> +	.dup		= btrfs_dup_fc,
> +	.free		= btrfs_fc_free,
> +	.get_tree	= btrfs_get_tree,
> +	.parse_param	= btrfs_fc_parse_param,
> +	.reconfigure	= btrfs_reconfigure,

You should use the final indentation when the callbacks are added and
not at the end.

>  };
>  
>  static int btrfs_init_fs_context(struct fs_context *fc)
> @@ -3072,7 +3064,6 @@ static int btrfs_init_fs_context(struct fs_context *fc)
>  		return -ENOMEM;
>  
>  	/* currently default options */
> -	btrfs_set_opt(ctx->mount_opt, SPACE_CACHE);
>  #ifdef CONFIG_BTRFS_FS_POSIX_ACL
>  	fc->sb_flags |= SB_POSIXACL;
>  #endif
> @@ -3083,19 +3074,18 @@ static int btrfs_init_fs_context(struct fs_context *fc)
>  	return 0;
>  }
>  
> -static struct file_system_type btrfs_fs_type = {
> +struct file_system_type btrfs_fs_type = {
>  	.owner		= THIS_MODULE,
>  	.name		= "btrfs",
> -	.mount		= btrfs_mount,
>  	.parameters	= btrfs_fs_parameters,
> +	.init_fs_context = btrfs_init_fs_context,
>  	.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,
>  	.parameters	= btrfs_fs_parameters,
>  	.kill_sb	= btrfs_kill_super,
>  	.fs_flags	= FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA,
> @@ -3136,7 +3126,7 @@ static long btrfs_control_ioctl(struct file *file, unsigned int cmd,
>  	case BTRFS_IOC_SCAN_DEV:
>  		mutex_lock(&uuid_mutex);
>  		device = btrfs_scan_one_device(vol->name, FMODE_READ,
> -					       &btrfs_root_fs_type);
> +							&btrfs_fs_type);
>  		ret = PTR_ERR_OR_ZERO(device);
>  		mutex_unlock(&uuid_mutex);
>  		break;
> @@ -3146,7 +3136,7 @@ static long btrfs_control_ioctl(struct file *file, unsigned int cmd,
>  	case BTRFS_IOC_DEVICES_READY:
>  		mutex_lock(&uuid_mutex);
>  		device = btrfs_scan_one_device(vol->name, FMODE_READ,
> -					       &btrfs_root_fs_type);
> +							&btrfs_fs_type);
>  		if (IS_ERR(device)) {
>  			mutex_unlock(&uuid_mutex);
>  			ret = PTR_ERR(device);
> @@ -3237,7 +3227,6 @@ static const struct super_operations btrfs_super_ops = {
>  	.destroy_inode	= btrfs_destroy_inode,
>  	.free_inode	= btrfs_free_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 d7670e2a9f39..59ba27e82615 100644
> --- a/fs/btrfs/volumes.c
> +++ b/fs/btrfs/volumes.c
> @@ -2304,7 +2304,7 @@ static struct btrfs_device *btrfs_find_device_by_path(
>  	struct btrfs_device *device;
>  
>  	ret = btrfs_get_bdev_and_sb(device_path, FMODE_READ,
> -				    fs_info->bdev_holder, 0, &bdev, &disk_super);
> +				    &btrfs_fs_type, 0, &bdev, &disk_super);
>  	if (ret)
>  		return ERR_PTR(ret);
>  
> @@ -2516,7 +2516,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);
>  
> @@ -6739,7 +6739,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, &btrfs_fs_type);
>  	if (ret) {
>  		free_fs_devices(fs_devices);
>  		fs_devices = ERR_PTR(ret);
> -- 
> 2.28.0
diff mbox series

Patch

diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index d96bce2ea5bb..9060be6a6c6e 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -3100,6 +3100,7 @@  int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
 			struct btrfs_root *root);
 
 /* super.c */
+extern struct file_system_type btrfs_fs_type;
 int btrfs_parse_options(struct btrfs_fs_info *info, char *options,
 			unsigned long new_flags);
 int btrfs_apply_configuration(struct fs_context *fc, struct super_block *sb);
diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c
index db93909b25e0..9a3275845b13 100644
--- a/fs/btrfs/dev-replace.c
+++ b/fs/btrfs/dev-replace.c
@@ -236,7 +236,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 c850d7f44fbe..f2e7543e9913 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -2894,8 +2894,7 @@  static int btrfs_check_uuid_tree(struct btrfs_fs_info *fs_info)
 	return 0;
 }
 
-int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_devices,
-		      char *options)
+int __cold open_ctree(struct fs_context *fc, struct super_block *sb)
 {
 	u32 sectorsize;
 	u32 nodesize;
@@ -2905,6 +2904,7 @@  int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
 	u16 csum_type;
 	struct btrfs_super_block *disk_super;
 	struct btrfs_fs_info *fs_info = btrfs_sb(sb);
+	struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
 	struct btrfs_root *tree_root;
 	struct btrfs_root *chunk_root;
 	int ret;
@@ -3030,11 +3030,9 @@  int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
 	 */
 	fs_info->compress_type = BTRFS_COMPRESS_ZLIB;
 
-	ret = btrfs_parse_options(fs_info, options, sb->s_flags);
-	if (ret) {
-		err = ret;
+	err = btrfs_apply_configuration(fc, sb);
+	if (err)
 		goto fail_alloc;
-	}
 
 	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 00dc39d47ed3..712ea61ffdfe 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -50,9 +50,7 @@  struct extent_buffer *btrfs_find_create_tree_block(
 						struct btrfs_fs_info *fs_info,
 						u64 bytenr);
 void btrfs_clean_tree_block(struct extent_buffer *buf);
-int __cold open_ctree(struct super_block *sb,
-	       struct btrfs_fs_devices *fs_devices,
-	       char *options);
+int __cold open_ctree(struct fs_context *fc, struct super_block *sb);
 void __cold close_ctree(struct btrfs_fs_info *fs_info);
 int write_all_supers(struct btrfs_fs_info *fs_info, int max_mirrors);
 struct btrfs_super_block *btrfs_read_dev_super(struct block_device *bdev);
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 5bbf4b947125..d9a0faea8c88 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -63,10 +63,9 @@  static const struct super_operations btrfs_super_ops;
  *
  * 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);
+static int btrfs_reconfigure(struct fs_context *fc);
 
 /*
  * Generally the error codes correspond to their respective errors, but there
@@ -1542,7 +1541,7 @@  static int btrfs_parse_device_options(const char *options, fmode_t flags,
 				goto out;
 			}
 			device = btrfs_scan_one_device(device_name, flags,
-					holder);
+							&btrfs_fs_type);
 			kfree(device_name);
 			if (IS_ERR(device)) {
 				error = PTR_ERR(device);
@@ -1795,13 +1794,13 @@  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)
 {
+	int err;
 	struct inode *inode;
 	struct btrfs_fs_info *fs_info = btrfs_sb(sb);
-	int err;
 
 	sb->s_maxbytes = MAX_LFS_FILESIZE;
 	sb->s_magic = BTRFS_SUPER_MAGIC;
@@ -1810,9 +1809,6 @@  static int btrfs_fill_super(struct super_block *sb,
 	sb->s_export_op = &btrfs_export_ops;
 	sb->s_xattr = btrfs_xattr_handlers;
 	sb->s_time_gran = 1;
-#ifdef CONFIG_BTRFS_FS_POSIX_ACL
-	sb->s_flags |= SB_POSIXACL;
-#endif
 	sb->s_flags |= SB_I_VERSION;
 	sb->s_iflags |= SB_I_CGROUPWB;
 
@@ -1822,7 +1818,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);
 	if (err) {
 		btrfs_err(fs_info, "open_ctree failed");
 		return err;
@@ -1951,19 +1947,20 @@  int btrfs_apply_configuration(struct fs_context *fc,
 #endif
 
 #ifdef CONFIG_BTRFS_FS_REF_VERIFY
-       if (btrfs_test_exp_opt(ctx, REF_VERIFY))
-               btrfs_info(info, "doing ref verification");
+	if (btrfs_test_exp_opt(ctx, REF_VERIFY))
+		btrfs_info(info, "doing ref verification");
 #endif
 
+	info->pending_changes = ctx->pending_changes;
 	if (btrfs_test_exp_opt(ctx, INODE_MAP_CACHE)) {
-		if (btrfs_opt_map[Opt_inode_cache].enabled) {
-		       btrfs_clear_pending_and_info(info, INODE_MAP_CACHE,
-				       "disabling inode map caching");
+		if (!btrfs_opt_map[Opt_inode_cache].enabled) {
+			btrfs_clear_pending_and_info(info, INODE_MAP_CACHE,
+						"disabling inode map caching");
 		} else {
 			btrfs_info(info,
 				"the 'inode_cache' option is deprecated and will have no effect from 5.11");
 			btrfs_set_pending_and_info(info, INODE_MAP_CACHE,
-				       "enabling inode map caching");
+						"enabling inode map caching");
 		}
 	}
 
@@ -2015,10 +2012,9 @@  int btrfs_apply_configuration(struct fs_context *fc,
 	if (btrfs_fs_compat_ro(info, FREE_SPACE_TREE)) {
 		bool opt_clear = btrfs_test_opt(ctx, CLEAR_CACHE);
 		bool opt_cache_v1 = btrfs_test_opt(ctx, SPACE_CACHE);
-		bool no_space_cache = ctx->nospace_cache;
 
-		if ((no_space_cache && !opt_clear) || (opt_cache_v1 && !opt_clear)) {
-			btrfs_err(info, "cannot disable free space tree XX");
+		if ((ctx->nospace_cache && !opt_clear) || (opt_cache_v1 && !opt_clear)) {
+			btrfs_err(info, "cannot disable free space tree");
 			return -EINVAL;
 		}
 	}
@@ -2063,7 +2059,6 @@  int btrfs_apply_configuration(struct fs_context *fc,
 
 	info->compress_type = ctx->compress_type;
 	info->compress_level = ctx->compress_level;
-	info->pending_changes = ctx->pending_changes;
 	info->mount_opt = ctx->mount_opt;
 
 	return 0;
@@ -2213,9 +2208,9 @@  static int btrfs_show_options(struct seq_file *seq, struct dentry *dentry)
 	return 0;
 }
 
-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;
@@ -2239,34 +2234,34 @@  static inline int is_subvolume_inode(struct inode *inode)
 	return 0;
 }
 
-static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid,
-				   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->subvolid) {
 			ret = get_default_subvol_objectid(btrfs_sb(mnt->mnt_sb),
-							  &subvol_objectid);
-			if (ret) {
-				root = ERR_PTR(ret);
-				goto out;
-			}
+							  &ctx->subvolid);
+			if (ret)
+				return ret;
 		}
 		subvol_name = btrfs_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;
-		}
+							btrfs_sb(mnt->mnt_sb),
+							ctx->subvolid);
+		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;
@@ -2277,10 +2272,10 @@  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->subvolid && root_objectid != ctx->subvolid) {
 			/*
 			 * This will also catch a race condition where a
 			 * subvolume which was passed by ID is renamed and
@@ -2288,20 +2283,23 @@  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->subvolid);
 			ret = -EINVAL;
 		}
 		if (ret) {
 			dput(root);
-			root = ERR_PTR(ret);
 			deactivate_locked_super(s);
+			goto out;
 		}
+	} else {
+		return PTR_ERR(root);
 	}
 
+	fc->root = root;
+	ret = 0;
+
 out:
-	mntput(mnt);
-	kfree(subvol_name);
-	return root;
+	return ret;
 }
 
 /*
@@ -2310,27 +2308,20 @@  static struct dentry *mount_subvol(const char *subvol_name, u64 subvol_objectid,
  * 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 block_device *bdev = NULL;
+	struct block_device *bdev;
 	struct super_block *s;
+	struct btrfs_fs_context *ctx = fc->fs_private;
 	struct btrfs_device *device = NULL;
 	struct btrfs_fs_devices *fs_devices = NULL;
 	struct btrfs_fs_info *fs_info = NULL;
-	void *new_sec_opts = NULL;
 	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;
 
-	if (data) {
-		error = security_sb_eat_lsm_opts(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
@@ -2340,10 +2331,9 @@  static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
 	 * superblock with our given fs_devices later on at sget() time.
 	 */
 	fs_info = kvzalloc(sizeof(struct btrfs_fs_info), GFP_KERNEL);
-	if (!fs_info) {
-		error = -ENOMEM;
-		goto error_sec_opts;
-	}
+	if (!fs_info)
+		return -ENOMEM;
+
 	btrfs_init_fs_info(fs_info);
 
 	fs_info->super_copy = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_KERNEL);
@@ -2354,13 +2344,20 @@  static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
 	}
 
 	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,
+							&btrfs_fs_type);
+			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, &btrfs_fs_type);
 	if (IS_ERR(device)) {
 		mutex_unlock(&uuid_mutex);
 		error = PTR_ERR(device);
@@ -2370,53 +2367,49 @@  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, &btrfs_fs_type);
 	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;
 	}
 
 	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;
 	}
 
+	 /* sget_fc returned a previously allocated superblocl. */
 	if (s->s_root) {
-		btrfs_close_devices(fs_devices);
-		btrfs_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;
-		if (!strstr(crc32c_impl(), "generic"))
-			set_bit(BTRFS_FS_CSUM_IMPL_FAST, &fs_info->flags);
-		error = btrfs_fill_super(s, fs_devices, data);
-	}
-	if (!error)
-		error = security_sb_set_mnt_opts(s, new_sec_opts, 0, NULL);
-	security_free_mnt_opts(&new_sec_opts);
-	if (error) {
-		deactivate_locked_super(s);
-		return ERR_PTR(error);
+		error = btrfs_fill_super(fc, s, fs_devices);
 	}
 
-	return dget(s->s_root);
+	if (error)
+		goto error_super;
+
+	fc->root = dget(s->s_root);
+	return 0;
 
+error_super:
+	deactivate_locked_super(s);
 error_close_devices:
 	btrfs_close_devices(fs_devices);
 error_fs_info:
-	btrfs_free_fs_info(fs_info);
-error_sec_opts:
-	security_free_mnt_opts(&new_sec_opts);
-	return ERR_PTR(error);
+	/*btrfs_free_fs_info(fs_info);*/
+	return error;
 }
 
 /*
@@ -2424,7 +2417,7 @@  static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
  * btrfs_get_tree will be called recursively, but then will check for the
  * ctx->root being set and call btrfs_root_get_tree.
  */
-static int btrfs_mount_root_fc(struct fs_context *fc, unsigned int rdonly)
+int btrfs_mount_root_fc(struct fs_context *fc, unsigned int rdonly)
 {
 	struct btrfs_fs_context *ctx, *root_ctx;
 	struct fs_context *root_fc;
@@ -2460,81 +2453,76 @@  static int btrfs_mount_root_fc(struct fs_context *fc, unsigned int rdonly)
 	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.
+ * Mount function which is called by VFS layer after the argument parsing.
  *
  * 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
  * be mounted internally in any case.
  *
  * Operation flow:
- *   1. Parse subvol id related options for later use in mount_subvol().
- *
- *   2. Mount device's root (/) by calling vfs_kern_mount().
+ *   1. Mount device's root (/) by calling btrfs_mount_root_fc.
  *
- *      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. btrfs_get_tree is called recursively by btrfs_mount_fc -> fc_mount ->
+ *      btrfs_get_tree, because we use the same file_system_type for both root and
+ *      subvol mounts. We avoid the infinite recursive calls by checking the
+ *      ctx->root member being set, which shows that we are trying to mount the
+ *      root fs.
  *
  *   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)
+static int btrfs_get_tree(struct fs_context *fc)
 {
-	struct vfsmount *mnt_root;
-	struct dentry *root;
-	char *subvol_name = NULL;
-	u64 subvol_objectid = 0;
-	int error = 0;
+	struct btrfs_fs_context *ctx = fc->fs_private;
+	int error = btrfs_fc_validate(fc);
 
-	error = btrfs_parse_subvol_options(data, &subvol_name,
-					&subvol_objectid);
-	if (error) {
-		kfree(subvol_name);
-		return ERR_PTR(error);
-	}
+	if (error)
+		return 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);
-				kfree(subvol_name);
-				goto out;
-			}
+	if (!fc->source)
+		return invalf(fc, "No source specified");
 
-			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);
-				kfree(subvol_name);
-				goto out;
-			}
+	if (ctx->root)
+		return btrfs_root_get_tree(fc);
+
+	/* mount device's root (/) */
+	error = btrfs_mount_root_fc(fc, fc->sb_flags & SB_RDONLY);
+	if (error == -EBUSY) {
+		/*
+		 * If returned EBUSY, try again the mount inverting the rdonly
+		 * argument of btrfs_mount_root.
+		 */
+		error = btrfs_mount_root_fc(fc, (fc->sb_flags & SB_RDONLY) ^ SB_RDONLY);
+		if (!error && !(fc->sb_flags & SB_RDONLY)) {
+			error = btrfs_reconfigure_root_to_rw(fc,
+						ctx->root_mnt->mnt_sb);
 		}
 	}
-	if (IS_ERR(mnt_root)) {
-		root = ERR_CAST(mnt_root);
-		kfree(subvol_name);
-		goto out;
-	}
 
-	/* mount_subvol() will free subvol_name and mnt_root */
-	root = mount_subvol(subvol_name, subvol_objectid, mnt_root);
+	if (error < 0)
+		return error;
 
-out:
-	return root;
+	return mount_subvol(fc);
 }
 
 static void btrfs_resize_thread_pool(struct btrfs_fs_info *fs_info,
@@ -2598,8 +2586,16 @@  static inline void btrfs_remount_cleanup(struct btrfs_fs_info *fs_info,
 		btrfs_discard_cleanup(fs_info);
 }
 
-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;
@@ -2608,34 +2604,28 @@  static int btrfs_remount(struct super_block *sb, int *flags, char *data)
 	u64 old_max_inline = fs_info->max_inline;
 	u32 old_thread_pool_size = fs_info->thread_pool_size;
 	u32 old_metadata_ratio = fs_info->metadata_ratio;
-	int ret;
+	int ret = btrfs_fc_validate(fc);
+
+	if (ret)
+		return ret;
 
 	sync_filesystem(sb);
 	set_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state);
-
-	if (data) {
-		void *new_sec_opts = NULL;
-
-		ret = security_sb_eat_lsm_opts(data, &new_sec_opts);
-		if (!ret)
-			ret = security_sb_remount(sb, new_sec_opts);
-		security_free_mnt_opts(&new_sec_opts);
+	if (ctx) {
+		ret = btrfs_apply_configuration(fc, sb);
 		if (ret)
 			goto restore;
 	}
 
-	ret = btrfs_parse_options(fs_info, data, *flags);
-	if (ret)
-		goto restore;
+	btrfs_remount_begin(fs_info, old_opts, fc->sb_flags);
+	if (ctx && ctx->thread_pool_size)
+		btrfs_resize_thread_pool(fs_info, ctx->thread_pool_size,
+					old_thread_pool_size);
 
-	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.
@@ -2735,7 +2725,7 @@  static int btrfs_remount(struct super_block *sb, int *flags, char *data)
 	 * We need to set SB_I_VERSION here otherwise it'll get cleared by VFS,
 	 * since the absence of the flag means it can be toggled off by remount.
 	 */
-	*flags |= SB_I_VERSION;
+	fc->sb_flags |= SB_I_VERSION;
 
 	wake_up_process(fs_info->transaction_kthread);
 	btrfs_remount_cleanup(fs_info, old_opts);
@@ -3058,9 +3048,11 @@  static void btrfs_fc_free(struct fs_context *fc)
 }
 
 static const struct fs_context_operations btrfs_context_ops = {
-	.dup = btrfs_dup_fc,
-	.free = btrfs_fc_free,
-	.parse_param = btrfs_fc_parse_param,
+	.dup		= btrfs_dup_fc,
+	.free		= btrfs_fc_free,
+	.get_tree	= btrfs_get_tree,
+	.parse_param	= btrfs_fc_parse_param,
+	.reconfigure	= btrfs_reconfigure,
 };
 
 static int btrfs_init_fs_context(struct fs_context *fc)
@@ -3072,7 +3064,6 @@  static int btrfs_init_fs_context(struct fs_context *fc)
 		return -ENOMEM;
 
 	/* currently default options */
-	btrfs_set_opt(ctx->mount_opt, SPACE_CACHE);
 #ifdef CONFIG_BTRFS_FS_POSIX_ACL
 	fc->sb_flags |= SB_POSIXACL;
 #endif
@@ -3083,19 +3074,18 @@  static int btrfs_init_fs_context(struct fs_context *fc)
 	return 0;
 }
 
-static struct file_system_type btrfs_fs_type = {
+struct file_system_type btrfs_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "btrfs",
-	.mount		= btrfs_mount,
 	.parameters	= btrfs_fs_parameters,
+	.init_fs_context = btrfs_init_fs_context,
 	.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,
 	.parameters	= btrfs_fs_parameters,
 	.kill_sb	= btrfs_kill_super,
 	.fs_flags	= FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA,
@@ -3136,7 +3126,7 @@  static long btrfs_control_ioctl(struct file *file, unsigned int cmd,
 	case BTRFS_IOC_SCAN_DEV:
 		mutex_lock(&uuid_mutex);
 		device = btrfs_scan_one_device(vol->name, FMODE_READ,
-					       &btrfs_root_fs_type);
+							&btrfs_fs_type);
 		ret = PTR_ERR_OR_ZERO(device);
 		mutex_unlock(&uuid_mutex);
 		break;
@@ -3146,7 +3136,7 @@  static long btrfs_control_ioctl(struct file *file, unsigned int cmd,
 	case BTRFS_IOC_DEVICES_READY:
 		mutex_lock(&uuid_mutex);
 		device = btrfs_scan_one_device(vol->name, FMODE_READ,
-					       &btrfs_root_fs_type);
+							&btrfs_fs_type);
 		if (IS_ERR(device)) {
 			mutex_unlock(&uuid_mutex);
 			ret = PTR_ERR(device);
@@ -3237,7 +3227,6 @@  static const struct super_operations btrfs_super_ops = {
 	.destroy_inode	= btrfs_destroy_inode,
 	.free_inode	= btrfs_free_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 d7670e2a9f39..59ba27e82615 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -2304,7 +2304,7 @@  static struct btrfs_device *btrfs_find_device_by_path(
 	struct btrfs_device *device;
 
 	ret = btrfs_get_bdev_and_sb(device_path, FMODE_READ,
-				    fs_info->bdev_holder, 0, &bdev, &disk_super);
+				    &btrfs_fs_type, 0, &bdev, &disk_super);
 	if (ret)
 		return ERR_PTR(ret);
 
@@ -2516,7 +2516,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);
 
@@ -6739,7 +6739,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, &btrfs_fs_type);
 	if (ret) {
 		free_fs_devices(fs_devices);
 		fs_devices = ERR_PTR(ret);