btrfs: Convert to using fs_context
Convert the btrfs filesystem to using an fs_context to parameterise mount
creation and reconfiguration.
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
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 464b083..0c5a9ee 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;
@@ -1322,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
@@ -1415,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)
/*
@@ -3242,14 +3269,16 @@ 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);
+void btrfs_apply_configuration(struct fs_context *fc, struct super_block *sb);
int btrfs_show_options(struct seq_file *seq, struct dentry *dentry);
+int btrfs_validate(struct fs_context *fc);
/* 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..d598eb4 100644
--- a/fs/btrfs/fs_params.c
+++ b/fs/btrfs/fs_params.c
@@ -1,719 +1,878 @@
// 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);
- 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;
+ switch (opt) {
+ case Opt_source:
+ case Opt_device:
+ case Opt_subvol:
+ case Opt_subvolid:
+ case Opt_subvolrootid:
+ if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE)
+ 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;
+}
+
+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,
+ .get_tree = btrfs_get_tree,
+ .reconfigure = btrfs_reconfigure,
+};
+
+/*
+ * Set up the filesystem configuration context.
+ */
+int btrfs_init_fs_context(struct fs_context *fc)
+{
+ 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 4e4d88a..3bcaa0c 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,20 +623,20 @@ 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;
+ return ret;
}
/*
@@ -674,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;
@@ -685,27 +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;
- 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
@@ -715,7 +702,7 @@ 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);
@@ -726,13 +713,19 @@ 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);
+ 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);
@@ -742,55 +735,113 @@ 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)
- 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);
- }
+ if (error)
+ goto error_super;
- return dget(s->s_root);
+ 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:
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);
+ 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;
+
+ root_mnt = fc_mount(root_fc);
+ 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
@@ -799,77 +850,42 @@ 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;
- fmode_t mode = FMODE_READ;
- char *subvol_name = NULL;
- u64 subvol_objectid = 0;
- int error = 0;
+ struct btrfs_fs_context *ctx = fc->fs_private;
+ int error = btrfs_validate(fc);
- if (!(flags & SB_RDONLY))
- mode |= FMODE_WRITE;
+ if (error)
+ return error;
- error = btrfs_parse_subvol_options(data, &subvol_name,
- &subvol_objectid);
- if (error) {
- kfree(subvol_name);
- return ERR_PTR(error);
- }
+ if (ctx->root)
+ return btrfs_root_get_tree(fc);
/* 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;
- }
-
- 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);
+ if (error == -EBUSY) {
+ error = btrfs_mount_root(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);
- goto out;
- }
+ if (error < 0)
+ return error;
- /* 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;
@@ -900,15 +916,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);
}
}
@@ -928,8 +945,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;
@@ -938,34 +963,23 @@ 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_validate(fc);
+
+ if (ret)
+ return ret;
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) {
- 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 (ret)
- 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.
@@ -1066,11 +1080,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;
@@ -1353,20 +1366,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");
@@ -1402,15 +1408,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);
@@ -1507,7 +1511,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,