linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/9] f2fs: new mount API conversion
@ 2024-08-14  2:39 Hongbo Li
  2024-08-14  2:39 ` [PATCH 1/9] f2fs: Add fs parameter specifications for mount options Hongbo Li
                   ` (11 more replies)
  0 siblings, 12 replies; 16+ messages in thread
From: Hongbo Li @ 2024-08-14  2:39 UTC (permalink / raw)
  To: jaegeuk, chao
  Cc: linux-f2fs-devel, brauner, lczerner, linux-fsdevel, lihongbo22

Since many filesystems have done the new mount API conversion,
we introduce the new mount API conversion in f2fs.

The series can be applied on top of the current mainline tree
and the work is based on the patches from Lukas Czerner (has
done this in ext4[1]). His patch give me a lot of ideas.

Here is a high level description of the patchset:

1. Prepare the f2fs mount parameters required by the new mount
API and use it for parsing, while still using the old API to
get mount options string. Split the parameter parsing and
validation of the parse_options helper into two separate
helpers.

  f2fs: Add fs parameter specifications for mount options
  f2fs: move the option parser into handle_mount_opt
  f2fs: move option validation into a separate helper

2. Remove the use of sb/sbi structure of f2fs from all the
parsing code, because with the new mount API the parsing is
going to be done before we even get the super block. In this
part, we introduce f2fs_fs_context to hold the temporary
options when parsing. For the simple options check, it has
to be done during parsing by using f2fs_fs_context structure.
For the check which needs sb/sbi, we do this during super
block filling.

  f2fs: Allow sbi to be NULL in f2fs_printk
  f2fs: Add f2fs_fs_context to record the mount options
  f2fs: separate the options parsing and options checking

3. Switch the f2fs to use the new mount API for mount and
remount.

  f2fs: introduce fs_context_operation structure
  f2fs: switch to the new mount api

4. Cleanup the old unused structures and helpers.

  f2fs: remove unused structure and functions

There is still a potential to do some cleanups and perhaps
refactoring. However that can be done later after the conversion
to the new mount API which is the main purpose of the patchset.

[1] https://lore.kernel.org/all/20211021114508.21407-1-lczerner@redhat.com/

Hongbo Li (9):
  f2fs: Add fs parameter specifications for mount options
  f2fs: move the option parser into handle_mount_opt
  f2fs: move option validation into a separate helper
  f2fs: Allow sbi to be NULL in f2fs_printk
  f2fs: Add f2fs_fs_context to record the mount options
  f2fs: separate the options parsing and options checking
  f2fs: introduce fs_context_operation structure
  f2fs: switch to the new mount api
  f2fs: remove unused structure and functions

 fs/f2fs/super.c | 2211 ++++++++++++++++++++++++++++-------------------
 1 file changed, 1341 insertions(+), 870 deletions(-)

-- 
2.34.1


^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 1/9] f2fs: Add fs parameter specifications for mount options
  2024-08-14  2:39 [PATCH 0/9] f2fs: new mount API conversion Hongbo Li
@ 2024-08-14  2:39 ` Hongbo Li
  2024-08-30 21:00   ` Eric Sandeen
  2024-08-14  2:39 ` [PATCH 2/9] f2fs: move the option parser into handle_mount_opt Hongbo Li
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 16+ messages in thread
From: Hongbo Li @ 2024-08-14  2:39 UTC (permalink / raw)
  To: jaegeuk, chao
  Cc: linux-f2fs-devel, brauner, lczerner, linux-fsdevel, lihongbo22

Use an array of `fs_parameter_spec` called f2fs_param_specs to
hold the mount option specifications for the new mount api.

Signed-off-by: Hongbo Li <lihongbo22@huawei.com>
---
 fs/f2fs/super.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 79 insertions(+)

diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 3959fd137cc9..1bd923a73c1f 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -28,6 +28,7 @@
 #include <linux/part_stat.h>
 #include <linux/zstd.h>
 #include <linux/lz4.h>
+#include <linux/fs_parser.h>
 
 #include "f2fs.h"
 #include "node.h"
@@ -189,9 +190,87 @@ enum {
 	Opt_memory_mode,
 	Opt_age_extent_cache,
 	Opt_errors,
+	Opt_jqfmt,
+	Opt_checkpoint,
 	Opt_err,
 };
 
+static const struct constant_table f2fs_param_jqfmt[] = {
+	{"vfsold",	QFMT_VFS_OLD},
+	{"vfsv0",	QFMT_VFS_V0},
+	{"vfsv1",	QFMT_VFS_V1},
+	{}
+};
+
+static const struct fs_parameter_spec f2fs_param_specs[] = {
+	fsparam_string("background_gc", Opt_gc_background),
+	fsparam_flag("disable_roll_forward", Opt_disable_roll_forward),
+	fsparam_flag("norecovery", Opt_norecovery),
+	fsparam_flag("discard", Opt_discard),
+	fsparam_flag("nodiscard", Opt_nodiscard),
+	fsparam_flag("no_heap", Opt_noheap),
+	fsparam_flag("heap", Opt_heap),
+	fsparam_flag("user_xattr", Opt_user_xattr),
+	fsparam_flag("nouser_xattr", Opt_nouser_xattr),
+	fsparam_flag("acl", Opt_acl),
+	fsparam_flag("noacl", Opt_noacl),
+	fsparam_s32("active_logs", Opt_active_logs),
+	fsparam_flag("disable_ext_identify", Opt_disable_ext_identify),
+	fsparam_flag("inline_xattr", Opt_inline_xattr),
+	fsparam_flag("noinline_xattr", Opt_noinline_xattr),
+	fsparam_s32("inline_xattr_size", Opt_inline_xattr_size),
+	fsparam_flag("inline_data", Opt_inline_data),
+	fsparam_flag("inline_dentry", Opt_inline_dentry),
+	fsparam_flag("noinline_dentry", Opt_noinline_dentry),
+	fsparam_flag("flush_merge", Opt_flush_merge),
+	fsparam_flag("noflush_merge", Opt_noflush_merge),
+	fsparam_flag("barrier", Opt_barrier),
+	fsparam_flag("nobarrier", Opt_nobarrier),
+	fsparam_flag("fastboot", Opt_fastboot),
+	fsparam_flag("extent_cache", Opt_extent_cache),
+	fsparam_flag("noextent_cache", Opt_noextent_cache),
+	fsparam_flag("noinline_data", Opt_noinline_data),
+	fsparam_flag("data_flush", Opt_data_flush),
+	fsparam_u32("reserve_root", Opt_reserve_root),
+	fsparam_u32("resgid", Opt_resgid),
+	fsparam_u32("resuid", Opt_resuid),
+	fsparam_string("mode", Opt_mode),
+	fsparam_s32("fault_injection", Opt_fault_injection),
+	fsparam_u32("fault_type", Opt_fault_type),
+	fsparam_flag("quota", Opt_quota),
+	fsparam_flag("noquota", Opt_noquota),
+	fsparam_flag("usrquota", Opt_usrquota),
+	fsparam_flag("grpquota", Opt_grpquota),
+	fsparam_flag("prjquota", Opt_prjquota),
+	fsparam_string_empty("usrjquota", Opt_usrjquota),
+	fsparam_string_empty("grpjquota", Opt_grpjquota),
+	fsparam_string_empty("prjjquota", Opt_prjjquota),
+	fsparam_enum("jqfmt", Opt_jqfmt, f2fs_param_jqfmt),
+	fsparam_string("alloc_mode", Opt_alloc),
+	fsparam_string("fsync_mode", Opt_fsync),
+	fsparam_string("test_dummy_encryption", Opt_test_dummy_encryption),
+	fsparam_flag("test_dummy_encryption", Opt_test_dummy_encryption),
+	fsparam_flag("inlinecrypt", Opt_inlinecrypt),
+	fsparam_string("checkpoint", Opt_checkpoint),
+	fsparam_flag("checkpoint_merge", Opt_checkpoint_merge),
+	fsparam_flag("nocheckpoint_merge", Opt_nocheckpoint_merge),
+	fsparam_string("compress_algorithm", Opt_compress_algorithm),
+	fsparam_u32("compress_log_size", Opt_compress_log_size),
+	fsparam_string("compress_extension", Opt_compress_extension),
+	fsparam_string("nocompress_extension", Opt_nocompress_extension),
+	fsparam_flag("compress_chksum", Opt_compress_chksum),
+	fsparam_string("compress_mode", Opt_compress_mode),
+	fsparam_flag("compress_cache", Opt_compress_cache),
+	fsparam_flag("atgc", Opt_atgc),
+	fsparam_flag("gc_merge", Opt_gc_merge),
+	fsparam_flag("nogc_merge", Opt_nogc_merge),
+	fsparam_string("discard_unit", Opt_discard_unit),
+	fsparam_string("memory", Opt_memory_mode),
+	fsparam_flag("age_extent_cache", Opt_age_extent_cache),
+	fsparam_string("errors", Opt_errors),
+	{}
+};
+
 static match_table_t f2fs_tokens = {
 	{Opt_gc_background, "background_gc=%s"},
 	{Opt_disable_roll_forward, "disable_roll_forward"},
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 2/9] f2fs: move the option parser into handle_mount_opt
  2024-08-14  2:39 [PATCH 0/9] f2fs: new mount API conversion Hongbo Li
  2024-08-14  2:39 ` [PATCH 1/9] f2fs: Add fs parameter specifications for mount options Hongbo Li
@ 2024-08-14  2:39 ` Hongbo Li
  2024-08-14  2:39 ` [PATCH 3/9] f2fs: move option validation into a separate helper Hongbo Li
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Hongbo Li @ 2024-08-14  2:39 UTC (permalink / raw)
  To: jaegeuk, chao
  Cc: linux-f2fs-devel, brauner, lczerner, linux-fsdevel, lihongbo22

In handle_mount_opt, we use fs_parameter to pass each options.
However we're still using the old API to get the options string.

There are some change about parsering options:
  1. For `active_logs`, `inline_xattr_size` and `fault_injection`,
  we use s32 type according the internal structure to record the
  option's value.
  2. Introduce constant_table for `jqfmt` options.
  3. Obtain the checkpoint disable cap (or percent) with private logic.

Signed-off-by: Hongbo Li <lihongbo22@huawei.com>
---
 fs/f2fs/super.c | 1191 ++++++++++++++++++++++++-----------------------
 1 file changed, 614 insertions(+), 577 deletions(-)

diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 1bd923a73c1f..013b1078653f 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -28,6 +28,7 @@
 #include <linux/part_stat.h>
 #include <linux/zstd.h>
 #include <linux/lz4.h>
+#include <linux/ctype.h>
 #include <linux/fs_parser.h>
 
 #include "f2fs.h"
@@ -460,7 +461,7 @@ static void init_once(void *foo)
 static const char * const quotatypes[] = INITQFNAMES;
 #define QTYPE2NAME(t) (quotatypes[t])
 static int f2fs_set_qf_name(struct super_block *sb, int qtype,
-							substring_t *args)
+			    struct fs_parameter *param)
 {
 	struct f2fs_sb_info *sbi = F2FS_SB(sb);
 	char *qname;
@@ -475,7 +476,7 @@ static int f2fs_set_qf_name(struct super_block *sb, int qtype,
 		return 0;
 	}
 
-	qname = match_strdup(args);
+	qname = kmemdup_nul(param->string, param->size, GFP_KERNEL);
 	if (!qname) {
 		f2fs_err(sbi, "Not enough memory for storing quotafile name");
 		return -ENOMEM;
@@ -560,15 +561,10 @@ static int f2fs_check_quota_options(struct f2fs_sb_info *sbi)
 #endif
 
 static int f2fs_set_test_dummy_encryption(struct super_block *sb,
-					  const char *opt,
-					  const substring_t *arg,
+					  const struct fs_parameter *param,
 					  bool is_remount)
 {
 	struct f2fs_sb_info *sbi = F2FS_SB(sb);
-	struct fs_parameter param = {
-		.type = fs_value_is_string,
-		.string = arg->from ? arg->from : "",
-	};
 	struct fscrypt_dummy_policy *policy =
 		&F2FS_OPTION(sbi).dummy_enc_policy;
 	int err;
@@ -594,17 +590,17 @@ static int f2fs_set_test_dummy_encryption(struct super_block *sb,
 		return -EINVAL;
 	}
 
-	err = fscrypt_parse_test_dummy_encryption(&param, policy);
+	err = fscrypt_parse_test_dummy_encryption(param, policy);
 	if (err) {
 		if (err == -EEXIST)
 			f2fs_warn(sbi,
 				  "Can't change test_dummy_encryption on remount");
 		else if (err == -EINVAL)
 			f2fs_warn(sbi, "Value of option \"%s\" is unrecognized",
-				  opt);
+				  param->key);
 		else
 			f2fs_warn(sbi, "Error processing option \"%s\" [%d]",
-				  opt, err);
+				  param->key, err);
 		return -EINVAL;
 	}
 	f2fs_warn(sbi, "Test dummy encryption mode enabled");
@@ -747,639 +743,680 @@ static int f2fs_set_zstd_level(struct f2fs_sb_info *sbi, const char *str)
 #endif
 #endif
 
-static int parse_options(struct super_block *sb, char *options, bool is_remount)
+static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 {
-	struct f2fs_sb_info *sbi = F2FS_SB(sb);
-	substring_t args[MAX_OPT_ARGS];
+	struct f2fs_sb_info *sbi = fc->s_fs_info;
+	struct super_block *sb = sbi->sb;
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 	unsigned char (*ext)[F2FS_EXTENSION_LEN];
 	unsigned char (*noext)[F2FS_EXTENSION_LEN];
 	int ext_cnt, noext_cnt;
 #endif
-	char *p, *name;
-	int arg = 0;
+	struct fs_parse_result result;
+	int is_remount;
+	char *name;
 	kuid_t uid;
 	kgid_t gid;
-	int ret;
-
-	if (!options)
-		goto default_check;
-
-	while ((p = strsep(&options, ",")) != NULL) {
-		int token;
-
-		if (!*p)
-			continue;
-		/*
-		 * Initialize args struct so we know whether arg was
-		 * found; some options take optional arguments.
-		 */
-		args[0].to = args[0].from = NULL;
-		token = match_token(p, f2fs_tokens, args);
-
-		switch (token) {
-		case Opt_gc_background:
-			name = match_strdup(&args[0]);
-
-			if (!name)
-				return -ENOMEM;
-			if (!strcmp(name, "on")) {
-				F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_ON;
-			} else if (!strcmp(name, "off")) {
-				F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_OFF;
-			} else if (!strcmp(name, "sync")) {
-				F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_SYNC;
-			} else {
-				kfree(name);
-				return -EINVAL;
-			}
+	int token, ret;
+
+	token = fs_parse(fc, f2fs_param_specs, param, &result);
+	if (token < 0)
+		return token;
+
+	is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;
+
+	switch (token) {
+	case Opt_gc_background:
+		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
+
+		if (!name)
+			return -ENOMEM;
+		if (!strcmp(name, "on")) {
+			F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_ON;
+		} else if (!strcmp(name, "off")) {
+			F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_OFF;
+		} else if (!strcmp(name, "sync")) {
+			F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_SYNC;
+		} else {
 			kfree(name);
-			break;
-		case Opt_disable_roll_forward:
-			set_opt(sbi, DISABLE_ROLL_FORWARD);
-			break;
-		case Opt_norecovery:
-			/* this option mounts f2fs with ro */
-			set_opt(sbi, NORECOVERY);
-			if (!f2fs_readonly(sb))
-				return -EINVAL;
-			break;
-		case Opt_discard:
-			if (!f2fs_hw_support_discard(sbi)) {
-				f2fs_warn(sbi, "device does not support discard");
-				break;
-			}
-			set_opt(sbi, DISCARD);
-			break;
-		case Opt_nodiscard:
-			if (f2fs_hw_should_discard(sbi)) {
-				f2fs_warn(sbi, "discard is required for zoned block devices");
-				return -EINVAL;
-			}
-			clear_opt(sbi, DISCARD);
-			break;
-		case Opt_noheap:
-		case Opt_heap:
-			f2fs_warn(sbi, "heap/no_heap options were deprecated");
-			break;
+			return -EINVAL;
+		}
+		kfree(name);
+		return 0;
+	case Opt_disable_roll_forward:
+		set_opt(sbi, DISABLE_ROLL_FORWARD);
+		return 0;
+	case Opt_norecovery:
+		/* this option mounts f2fs with ro */
+		set_opt(sbi, NORECOVERY);
+		if (!f2fs_readonly(sb))
+			return -EINVAL;
+		return 0;
+	case Opt_discard:
+		if (!f2fs_hw_support_discard(sbi)) {
+			f2fs_warn(sbi, "device does not support discard");
+			return 0;
+		}
+		set_opt(sbi, DISCARD);
+		return 0;
+	case Opt_nodiscard:
+		if (f2fs_hw_should_discard(sbi)) {
+			f2fs_warn(sbi, "discard is required for zoned block devices");
+			return -EINVAL;
+		}
+		clear_opt(sbi, DISCARD);
+		return 0;
+	case Opt_noheap:
+	case Opt_heap:
+		f2fs_warn(sbi, "heap/no_heap options were deprecated");
+		return 0;
 #ifdef CONFIG_F2FS_FS_XATTR
-		case Opt_user_xattr:
-			set_opt(sbi, XATTR_USER);
-			break;
-		case Opt_nouser_xattr:
-			clear_opt(sbi, XATTR_USER);
-			break;
-		case Opt_inline_xattr:
-			set_opt(sbi, INLINE_XATTR);
-			break;
-		case Opt_noinline_xattr:
-			clear_opt(sbi, INLINE_XATTR);
-			break;
-		case Opt_inline_xattr_size:
-			if (args->from && match_int(args, &arg))
-				return -EINVAL;
-			set_opt(sbi, INLINE_XATTR_SIZE);
-			F2FS_OPTION(sbi).inline_xattr_size = arg;
-			break;
+	case Opt_user_xattr:
+		set_opt(sbi, XATTR_USER);
+		return 0;
+	case Opt_nouser_xattr:
+		clear_opt(sbi, XATTR_USER);
+		return 0;
+	case Opt_inline_xattr:
+		set_opt(sbi, INLINE_XATTR);
+		return 0;
+	case Opt_noinline_xattr:
+		clear_opt(sbi, INLINE_XATTR);
+		return 0;
+	case Opt_inline_xattr_size:
+		set_opt(sbi, INLINE_XATTR_SIZE);
+		F2FS_OPTION(sbi).inline_xattr_size = result.int_32;
+		return 0;
 #else
-		case Opt_user_xattr:
-			f2fs_info(sbi, "user_xattr options not supported");
-			break;
-		case Opt_nouser_xattr:
-			f2fs_info(sbi, "nouser_xattr options not supported");
-			break;
-		case Opt_inline_xattr:
-			f2fs_info(sbi, "inline_xattr options not supported");
-			break;
-		case Opt_noinline_xattr:
-			f2fs_info(sbi, "noinline_xattr options not supported");
-			break;
+	case Opt_user_xattr:
+		f2fs_info(sbi, "user_xattr options not supported");
+		return 0;
+	case Opt_nouser_xattr:
+		f2fs_info(sbi, "nouser_xattr options not supported");
+		return 0;
+	case Opt_inline_xattr:
+		f2fs_info(sbi, "inline_xattr options not supported");
+		return 0;
+	case Opt_noinline_xattr:
+		f2fs_info(sbi, "noinline_xattr options not supported");
+		return 0;
 #endif
 #ifdef CONFIG_F2FS_FS_POSIX_ACL
-		case Opt_acl:
-			set_opt(sbi, POSIX_ACL);
-			break;
-		case Opt_noacl:
-			clear_opt(sbi, POSIX_ACL);
-			break;
+	case Opt_acl:
+		set_opt(sbi, POSIX_ACL);
+		return 0;
+	case Opt_noacl:
+		clear_opt(sbi, POSIX_ACL);
+		return 0;
 #else
-		case Opt_acl:
-			f2fs_info(sbi, "acl options not supported");
-			break;
-		case Opt_noacl:
-			f2fs_info(sbi, "noacl options not supported");
-			break;
+	case Opt_acl:
+		f2fs_info(sbi, "acl options not supported");
+		return 0;
+	case Opt_noacl:
+		f2fs_info(sbi, "noacl options not supported");
+		return 0;
 #endif
-		case Opt_active_logs:
-			if (args->from && match_int(args, &arg))
-				return -EINVAL;
-			if (arg != 2 && arg != 4 &&
-				arg != NR_CURSEG_PERSIST_TYPE)
-				return -EINVAL;
-			F2FS_OPTION(sbi).active_logs = arg;
-			break;
-		case Opt_disable_ext_identify:
-			set_opt(sbi, DISABLE_EXT_IDENTIFY);
-			break;
-		case Opt_inline_data:
-			set_opt(sbi, INLINE_DATA);
-			break;
-		case Opt_inline_dentry:
-			set_opt(sbi, INLINE_DENTRY);
-			break;
-		case Opt_noinline_dentry:
-			clear_opt(sbi, INLINE_DENTRY);
-			break;
-		case Opt_flush_merge:
-			set_opt(sbi, FLUSH_MERGE);
-			break;
-		case Opt_noflush_merge:
-			clear_opt(sbi, FLUSH_MERGE);
-			break;
-		case Opt_nobarrier:
-			set_opt(sbi, NOBARRIER);
-			break;
-		case Opt_barrier:
-			clear_opt(sbi, NOBARRIER);
-			break;
-		case Opt_fastboot:
-			set_opt(sbi, FASTBOOT);
-			break;
-		case Opt_extent_cache:
-			set_opt(sbi, READ_EXTENT_CACHE);
-			break;
-		case Opt_noextent_cache:
-			clear_opt(sbi, READ_EXTENT_CACHE);
-			break;
-		case Opt_noinline_data:
-			clear_opt(sbi, INLINE_DATA);
-			break;
-		case Opt_data_flush:
-			set_opt(sbi, DATA_FLUSH);
-			break;
-		case Opt_reserve_root:
-			if (args->from && match_int(args, &arg))
-				return -EINVAL;
-			if (test_opt(sbi, RESERVE_ROOT)) {
-				f2fs_info(sbi, "Preserve previous reserve_root=%u",
-					  F2FS_OPTION(sbi).root_reserved_blocks);
-			} else {
-				F2FS_OPTION(sbi).root_reserved_blocks = arg;
-				set_opt(sbi, RESERVE_ROOT);
-			}
-			break;
-		case Opt_resuid:
-			if (args->from && match_int(args, &arg))
-				return -EINVAL;
-			uid = make_kuid(current_user_ns(), arg);
-			if (!uid_valid(uid)) {
-				f2fs_err(sbi, "Invalid uid value %d", arg);
-				return -EINVAL;
-			}
-			F2FS_OPTION(sbi).s_resuid = uid;
-			break;
-		case Opt_resgid:
-			if (args->from && match_int(args, &arg))
-				return -EINVAL;
-			gid = make_kgid(current_user_ns(), arg);
-			if (!gid_valid(gid)) {
-				f2fs_err(sbi, "Invalid gid value %d", arg);
-				return -EINVAL;
-			}
-			F2FS_OPTION(sbi).s_resgid = gid;
-			break;
-		case Opt_mode:
-			name = match_strdup(&args[0]);
-
-			if (!name)
-				return -ENOMEM;
-			if (!strcmp(name, "adaptive")) {
-				F2FS_OPTION(sbi).fs_mode = FS_MODE_ADAPTIVE;
-			} else if (!strcmp(name, "lfs")) {
-				F2FS_OPTION(sbi).fs_mode = FS_MODE_LFS;
-			} else if (!strcmp(name, "fragment:segment")) {
-				F2FS_OPTION(sbi).fs_mode = FS_MODE_FRAGMENT_SEG;
-			} else if (!strcmp(name, "fragment:block")) {
-				F2FS_OPTION(sbi).fs_mode = FS_MODE_FRAGMENT_BLK;
-			} else {
-				kfree(name);
-				return -EINVAL;
-			}
+	case Opt_active_logs:
+		if (result.int_32 != 2 && result.int_32 != 4 &&
+			result.int_32 != NR_CURSEG_PERSIST_TYPE)
+			return -EINVAL;
+		F2FS_OPTION(sbi).active_logs = result.int_32;
+		return 0;
+	case Opt_disable_ext_identify:
+		set_opt(sbi, DISABLE_EXT_IDENTIFY);
+		return 0;
+	case Opt_inline_data:
+		set_opt(sbi, INLINE_DATA);
+		return 0;
+	case Opt_inline_dentry:
+		set_opt(sbi, INLINE_DENTRY);
+		return 0;
+	case Opt_noinline_dentry:
+		clear_opt(sbi, INLINE_DENTRY);
+		return 0;
+	case Opt_flush_merge:
+		set_opt(sbi, FLUSH_MERGE);
+		return 0;
+	case Opt_noflush_merge:
+		clear_opt(sbi, FLUSH_MERGE);
+		return 0;
+	case Opt_nobarrier:
+		set_opt(sbi, NOBARRIER);
+		return 0;
+	case Opt_barrier:
+		clear_opt(sbi, NOBARRIER);
+		return 0;
+	case Opt_fastboot:
+		set_opt(sbi, FASTBOOT);
+		return 0;
+	case Opt_extent_cache:
+		set_opt(sbi, READ_EXTENT_CACHE);
+		return 0;
+	case Opt_noextent_cache:
+		clear_opt(sbi, READ_EXTENT_CACHE);
+		return 0;
+	case Opt_noinline_data:
+		clear_opt(sbi, INLINE_DATA);
+		return 0;
+	case Opt_data_flush:
+		set_opt(sbi, DATA_FLUSH);
+		return 0;
+	case Opt_reserve_root:
+		if (test_opt(sbi, RESERVE_ROOT)) {
+			f2fs_info(sbi, "Preserve previous reserve_root=%u",
+				  F2FS_OPTION(sbi).root_reserved_blocks);
+		} else {
+			F2FS_OPTION(sbi).root_reserved_blocks = result.uint_32;
+			set_opt(sbi, RESERVE_ROOT);
+		}
+		return 0;
+	case Opt_resuid:
+		uid = make_kuid(current_user_ns(), result.uint_32);
+		if (!uid_valid(uid)) {
+			f2fs_err(sbi, "Invalid uid value %u", result.uint_32);
+			return -EINVAL;
+		}
+		F2FS_OPTION(sbi).s_resuid = uid;
+		return 0;
+	case Opt_resgid:
+		gid = make_kgid(current_user_ns(), result.uint_32);
+		if (!gid_valid(gid)) {
+			f2fs_err(sbi, "Invalid gid value %u", result.uint_32);
+			return -EINVAL;
+		}
+		F2FS_OPTION(sbi).s_resgid = gid;
+		return 0;
+	case Opt_mode:
+		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
+
+		if (!name)
+			return -ENOMEM;
+		if (!strcmp(name, "adaptive")) {
+			F2FS_OPTION(sbi).fs_mode = FS_MODE_ADAPTIVE;
+		} else if (!strcmp(name, "lfs")) {
+			F2FS_OPTION(sbi).fs_mode = FS_MODE_LFS;
+		} else if (!strcmp(name, "fragment:segment")) {
+			F2FS_OPTION(sbi).fs_mode = FS_MODE_FRAGMENT_SEG;
+		} else if (!strcmp(name, "fragment:block")) {
+			F2FS_OPTION(sbi).fs_mode = FS_MODE_FRAGMENT_BLK;
+		} else {
 			kfree(name);
-			break;
+			return -EINVAL;
+		}
+		kfree(name);
+		return 0;
 #ifdef CONFIG_F2FS_FAULT_INJECTION
-		case Opt_fault_injection:
-			if (args->from && match_int(args, &arg))
-				return -EINVAL;
-			if (f2fs_build_fault_attr(sbi, arg,
-					F2FS_ALL_FAULT_TYPE))
-				return -EINVAL;
-			set_opt(sbi, FAULT_INJECTION);
-			break;
+	case Opt_fault_injection:
+		if (f2fs_build_fault_attr(sbi, result.int_32,
+				F2FS_ALL_FAULT_TYPE))
+			return -EINVAL;
+		set_opt(sbi, FAULT_INJECTION);
+		return 0;
 
-		case Opt_fault_type:
-			if (args->from && match_int(args, &arg))
-				return -EINVAL;
-			if (f2fs_build_fault_attr(sbi, 0, arg))
-				return -EINVAL;
-			set_opt(sbi, FAULT_INJECTION);
-			break;
+	case Opt_fault_type:
+		if (f2fs_build_fault_attr(sbi, 0, result.uint_32))
+			return -EINVAL;
+		set_opt(sbi, FAULT_INJECTION);
+		return 0;
 #else
-		case Opt_fault_injection:
-			f2fs_info(sbi, "fault_injection options not supported");
-			break;
+	case Opt_fault_injection:
+		f2fs_info(sbi, "fault_injection options not supported");
+		return 0;
 
-		case Opt_fault_type:
-			f2fs_info(sbi, "fault_type options not supported");
-			break;
+	case Opt_fault_type:
+		f2fs_info(sbi, "fault_type options not supported");
+		return 0;
 #endif
 #ifdef CONFIG_QUOTA
-		case Opt_quota:
-		case Opt_usrquota:
-			set_opt(sbi, USRQUOTA);
-			break;
-		case Opt_grpquota:
-			set_opt(sbi, GRPQUOTA);
-			break;
-		case Opt_prjquota:
-			set_opt(sbi, PRJQUOTA);
-			break;
-		case Opt_usrjquota:
-			ret = f2fs_set_qf_name(sb, USRQUOTA, &args[0]);
-			if (ret)
-				return ret;
-			break;
-		case Opt_grpjquota:
-			ret = f2fs_set_qf_name(sb, GRPQUOTA, &args[0]);
-			if (ret)
-				return ret;
-			break;
-		case Opt_prjjquota:
-			ret = f2fs_set_qf_name(sb, PRJQUOTA, &args[0]);
-			if (ret)
-				return ret;
-			break;
-		case Opt_offusrjquota:
+	case Opt_quota:
+	case Opt_usrquota:
+		set_opt(sbi, USRQUOTA);
+		return 0;
+	case Opt_grpquota:
+		set_opt(sbi, GRPQUOTA);
+		return 0;
+	case Opt_prjquota:
+		set_opt(sbi, PRJQUOTA);
+		return 0;
+	case Opt_usrjquota:
+		if (!*param->string)
 			ret = f2fs_clear_qf_name(sb, USRQUOTA);
-			if (ret)
-				return ret;
-			break;
-		case Opt_offgrpjquota:
+		else
+			ret = f2fs_set_qf_name(sb, USRQUOTA, param);
+		if (ret)
+			return ret;
+		return 0;
+	case Opt_grpjquota:
+		if (!*param->string)
 			ret = f2fs_clear_qf_name(sb, GRPQUOTA);
-			if (ret)
-				return ret;
-			break;
-		case Opt_offprjjquota:
+		else
+			ret = f2fs_set_qf_name(sb, GRPQUOTA, param);
+		if (ret)
+			return ret;
+		return 0;
+	case Opt_prjjquota:
+		if (!*param->string)
 			ret = f2fs_clear_qf_name(sb, PRJQUOTA);
-			if (ret)
-				return ret;
-			break;
-		case Opt_jqfmt_vfsold:
-			F2FS_OPTION(sbi).s_jquota_fmt = QFMT_VFS_OLD;
-			break;
-		case Opt_jqfmt_vfsv0:
-			F2FS_OPTION(sbi).s_jquota_fmt = QFMT_VFS_V0;
-			break;
-		case Opt_jqfmt_vfsv1:
-			F2FS_OPTION(sbi).s_jquota_fmt = QFMT_VFS_V1;
-			break;
-		case Opt_noquota:
-			clear_opt(sbi, QUOTA);
-			clear_opt(sbi, USRQUOTA);
-			clear_opt(sbi, GRPQUOTA);
-			clear_opt(sbi, PRJQUOTA);
-			break;
+		else
+			ret = f2fs_set_qf_name(sb, PRJQUOTA, param);
+		if (ret)
+			return ret;
+		return 0;
+	case Opt_jqfmt:
+		F2FS_OPTION(sbi).s_jquota_fmt = result.uint_32;
+		return 0;
+	case Opt_noquota:
+		clear_opt(sbi, QUOTA);
+		clear_opt(sbi, USRQUOTA);
+		clear_opt(sbi, GRPQUOTA);
+		clear_opt(sbi, PRJQUOTA);
+		return 0;
 #else
-		case Opt_quota:
-		case Opt_usrquota:
-		case Opt_grpquota:
-		case Opt_prjquota:
-		case Opt_usrjquota:
-		case Opt_grpjquota:
-		case Opt_prjjquota:
-		case Opt_offusrjquota:
-		case Opt_offgrpjquota:
-		case Opt_offprjjquota:
-		case Opt_jqfmt_vfsold:
-		case Opt_jqfmt_vfsv0:
-		case Opt_jqfmt_vfsv1:
-		case Opt_noquota:
-			f2fs_info(sbi, "quota operations not supported");
-			break;
+	case Opt_quota:
+	case Opt_usrquota:
+	case Opt_grpquota:
+	case Opt_prjquota:
+	case Opt_usrjquota:
+	case Opt_grpjquota:
+	case Opt_prjjquota:
+	case Opt_offusrjquota:
+	case Opt_offgrpjquota:
+	case Opt_offprjjquota:
+	case Opt_jqfmt_vfsold:
+	case Opt_jqfmt_vfsv0:
+	case Opt_jqfmt_vfsv1:
+	case Opt_noquota:
+		f2fs_info(sbi, "quota operations not supported");
+		return 0;
 #endif
-		case Opt_alloc:
-			name = match_strdup(&args[0]);
-			if (!name)
-				return -ENOMEM;
-
-			if (!strcmp(name, "default")) {
-				F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_DEFAULT;
-			} else if (!strcmp(name, "reuse")) {
-				F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_REUSE;
-			} else {
-				kfree(name);
-				return -EINVAL;
-			}
+	case Opt_alloc:
+		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
+		if (!name)
+			return -ENOMEM;
+
+		if (!strcmp(name, "default")) {
+			F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_DEFAULT;
+		} else if (!strcmp(name, "reuse")) {
+			F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_REUSE;
+		} else {
 			kfree(name);
-			break;
-		case Opt_fsync:
-			name = match_strdup(&args[0]);
-			if (!name)
-				return -ENOMEM;
-			if (!strcmp(name, "posix")) {
-				F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_POSIX;
-			} else if (!strcmp(name, "strict")) {
-				F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_STRICT;
-			} else if (!strcmp(name, "nobarrier")) {
-				F2FS_OPTION(sbi).fsync_mode =
-							FSYNC_MODE_NOBARRIER;
-			} else {
-				kfree(name);
-				return -EINVAL;
-			}
+			return -EINVAL;
+		}
+		kfree(name);
+		return 0;
+	case Opt_fsync:
+		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
+		if (!name)
+			return -ENOMEM;
+		if (!strcmp(name, "posix")) {
+			F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_POSIX;
+		} else if (!strcmp(name, "strict")) {
+			F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_STRICT;
+		} else if (!strcmp(name, "nobarrier")) {
+			F2FS_OPTION(sbi).fsync_mode =
+						FSYNC_MODE_NOBARRIER;
+		} else {
 			kfree(name);
-			break;
-		case Opt_test_dummy_encryption:
-			ret = f2fs_set_test_dummy_encryption(sb, p, &args[0],
-							     is_remount);
-			if (ret)
-				return ret;
-			break;
-		case Opt_inlinecrypt:
+			return -EINVAL;
+		}
+		kfree(name);
+		return 0;
+	case Opt_test_dummy_encryption:
+		ret = f2fs_set_test_dummy_encryption(sb, param, is_remount);
+		if (ret)
+			return ret;
+		return 0;
+	case Opt_inlinecrypt:
 #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
-			sb->s_flags |= SB_INLINECRYPT;
+		sb->s_flags |= SB_INLINECRYPT;
 #else
-			f2fs_info(sbi, "inline encryption not supported");
+		f2fs_info(sbi, "inline encryption not supported");
 #endif
-			break;
-		case Opt_checkpoint_disable_cap_perc:
-			if (args->from && match_int(args, &arg))
-				return -EINVAL;
-			if (arg < 0 || arg > 100)
-				return -EINVAL;
-			F2FS_OPTION(sbi).unusable_cap_perc = arg;
-			set_opt(sbi, DISABLE_CHECKPOINT);
-			break;
-		case Opt_checkpoint_disable_cap:
-			if (args->from && match_int(args, &arg))
-				return -EINVAL;
-			F2FS_OPTION(sbi).unusable_cap = arg;
+		return 0;
+	case Opt_checkpoint:
+	{
+		const char *prefix = "disable:";
+		size_t len = strlen(prefix);
+		const char *cp = param->string + len;
+		char *endp = (char *)cp;
+		unsigned long cap = 0;
+
+		if (!strcmp(param->string, "enable")) {
+			clear_opt(sbi, DISABLE_CHECKPOINT);
+			return 0;
+		}
+
+		if (!strcmp(param->string, "disable")) {
 			set_opt(sbi, DISABLE_CHECKPOINT);
-			break;
-		case Opt_checkpoint_disable:
+			return 0;
+		}
+
+		if (strlen(param->string) <= len)
+			return -EINVAL;
+		if (strncmp(prefix, param->string, len))
+			return -EINVAL;
+
+		while (isdigit(*endp)) {
+			cap = cap * 10 + (*endp - '0');
+			endp++;
+		}
+		if (!strcmp(cp, endp))
+			return -EINVAL;
+		if (strlen(endp) == 0) {
+			F2FS_OPTION(sbi).unusable_cap = cap;
 			set_opt(sbi, DISABLE_CHECKPOINT);
-			break;
-		case Opt_checkpoint_enable:
-			clear_opt(sbi, DISABLE_CHECKPOINT);
-			break;
-		case Opt_checkpoint_merge:
-			set_opt(sbi, MERGE_CHECKPOINT);
-			break;
-		case Opt_nocheckpoint_merge:
-			clear_opt(sbi, MERGE_CHECKPOINT);
-			break;
+			return 0;
+		}
+		if (strcmp(endp, "%"))
+			return -EINVAL;
+		if (cap > 100)
+			return -EINVAL;
+		F2FS_OPTION(sbi).unusable_cap_perc = cap;
+		set_opt(sbi, DISABLE_CHECKPOINT);
+		return 0;
+	}
+	case Opt_checkpoint_merge:
+		set_opt(sbi, MERGE_CHECKPOINT);
+		return 0;
+	case Opt_nocheckpoint_merge:
+		clear_opt(sbi, MERGE_CHECKPOINT);
+		return 0;
 #ifdef CONFIG_F2FS_FS_COMPRESSION
-		case Opt_compress_algorithm:
-			if (!f2fs_sb_has_compression(sbi)) {
-				f2fs_info(sbi, "Image doesn't support compression");
-				break;
-			}
-			name = match_strdup(&args[0]);
-			if (!name)
-				return -ENOMEM;
-			if (!strcmp(name, "lzo")) {
+	case Opt_compress_algorithm:
+		if (!f2fs_sb_has_compression(sbi)) {
+			f2fs_info(sbi, "Image doesn't support compression");
+			return 0;
+		}
+		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
+		if (!name)
+			return -ENOMEM;
+		if (!strcmp(name, "lzo")) {
 #ifdef CONFIG_F2FS_FS_LZO
-				F2FS_OPTION(sbi).compress_level = 0;
-				F2FS_OPTION(sbi).compress_algorithm =
-								COMPRESS_LZO;
+			F2FS_OPTION(sbi).compress_level = 0;
+			F2FS_OPTION(sbi).compress_algorithm =
+							COMPRESS_LZO;
 #else
-				f2fs_info(sbi, "kernel doesn't support lzo compression");
+			f2fs_info(sbi, "kernel doesn't support lzo compression");
 #endif
-			} else if (!strncmp(name, "lz4", 3)) {
+		} else if (!strncmp(name, "lz4", 3)) {
 #ifdef CONFIG_F2FS_FS_LZ4
-				ret = f2fs_set_lz4hc_level(sbi, name);
-				if (ret) {
-					kfree(name);
-					return -EINVAL;
-				}
-				F2FS_OPTION(sbi).compress_algorithm =
-								COMPRESS_LZ4;
+			ret = f2fs_set_lz4hc_level(sbi, name);
+			if (ret) {
+				kfree(name);
+				return -EINVAL;
+			}
+			F2FS_OPTION(sbi).compress_algorithm =
+							COMPRESS_LZ4;
 #else
-				f2fs_info(sbi, "kernel doesn't support lz4 compression");
+			f2fs_info(sbi, "kernel doesn't support lz4 compression");
 #endif
-			} else if (!strncmp(name, "zstd", 4)) {
+		} else if (!strncmp(name, "zstd", 4)) {
 #ifdef CONFIG_F2FS_FS_ZSTD
-				ret = f2fs_set_zstd_level(sbi, name);
-				if (ret) {
-					kfree(name);
-					return -EINVAL;
-				}
-				F2FS_OPTION(sbi).compress_algorithm =
-								COMPRESS_ZSTD;
+			ret = f2fs_set_zstd_level(sbi, name);
+			if (ret) {
+				kfree(name);
+				return -EINVAL;
+			}
+			F2FS_OPTION(sbi).compress_algorithm =
+							COMPRESS_ZSTD;
 #else
-				f2fs_info(sbi, "kernel doesn't support zstd compression");
+			f2fs_info(sbi, "kernel doesn't support zstd compression");
 #endif
-			} else if (!strcmp(name, "lzo-rle")) {
+		} else if (!strcmp(name, "lzo-rle")) {
 #ifdef CONFIG_F2FS_FS_LZORLE
-				F2FS_OPTION(sbi).compress_level = 0;
-				F2FS_OPTION(sbi).compress_algorithm =
-								COMPRESS_LZORLE;
+			F2FS_OPTION(sbi).compress_level = 0;
+			F2FS_OPTION(sbi).compress_algorithm =
+							COMPRESS_LZORLE;
 #else
-				f2fs_info(sbi, "kernel doesn't support lzorle compression");
+			f2fs_info(sbi, "kernel doesn't support lzorle compression");
 #endif
-			} else {
-				kfree(name);
-				return -EINVAL;
-			}
+		} else {
 			kfree(name);
+			return -EINVAL;
+		}
+		kfree(name);
+		return 0;
+	case Opt_compress_log_size:
+		if (!f2fs_sb_has_compression(sbi)) {
+			f2fs_info(sbi, "Image doesn't support compression");
+			return 0;
+		}
+		if (result.int_32 < MIN_COMPRESS_LOG_SIZE ||
+			result.int_32 > MAX_COMPRESS_LOG_SIZE) {
+			f2fs_err(sbi,
+				"Compress cluster log size is out of range");
+			return -EINVAL;
+		}
+		F2FS_OPTION(sbi).compress_log_size = result.int_32;
+		return 0;
+	case Opt_compress_extension:
+		if (!f2fs_sb_has_compression(sbi)) {
+			f2fs_info(sbi, "Image doesn't support compression");
 			break;
-		case Opt_compress_log_size:
-			if (!f2fs_sb_has_compression(sbi)) {
-				f2fs_info(sbi, "Image doesn't support compression");
-				break;
-			}
-			if (args->from && match_int(args, &arg))
-				return -EINVAL;
-			if (arg < MIN_COMPRESS_LOG_SIZE ||
-				arg > MAX_COMPRESS_LOG_SIZE) {
-				f2fs_err(sbi,
-					"Compress cluster log size is out of range");
-				return -EINVAL;
-			}
-			F2FS_OPTION(sbi).compress_log_size = arg;
-			break;
-		case Opt_compress_extension:
-			if (!f2fs_sb_has_compression(sbi)) {
-				f2fs_info(sbi, "Image doesn't support compression");
-				break;
-			}
-			name = match_strdup(&args[0]);
-			if (!name)
-				return -ENOMEM;
+		}
+		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
+		if (!name)
+			return -ENOMEM;
 
-			ext = F2FS_OPTION(sbi).extensions;
-			ext_cnt = F2FS_OPTION(sbi).compress_ext_cnt;
+		ext = F2FS_OPTION(sbi).extensions;
+		ext_cnt = F2FS_OPTION(sbi).compress_ext_cnt;
 
-			if (strlen(name) >= F2FS_EXTENSION_LEN ||
-				ext_cnt >= COMPRESS_EXT_NUM) {
-				f2fs_err(sbi,
-					"invalid extension length/number");
-				kfree(name);
-				return -EINVAL;
-			}
+		if (strlen(name) >= F2FS_EXTENSION_LEN ||
+			ext_cnt >= COMPRESS_EXT_NUM) {
+			f2fs_err(sbi,
+				"invalid extension length/number");
+			kfree(name);
+			return -EINVAL;
+		}
 
-			if (is_compress_extension_exist(sbi, name, true)) {
-				kfree(name);
-				break;
-			}
+		if (is_compress_extension_exist(sbi, name, true)) {
+			kfree(name);
+			return 0;
+		}
 
-			strcpy(ext[ext_cnt], name);
-			F2FS_OPTION(sbi).compress_ext_cnt++;
+		ret = strscpy(ext[ext_cnt], name, F2FS_EXTENSION_LEN);
+		if (ret < 0) {
 			kfree(name);
-			break;
-		case Opt_nocompress_extension:
-			if (!f2fs_sb_has_compression(sbi)) {
-				f2fs_info(sbi, "Image doesn't support compression");
-				break;
-			}
-			name = match_strdup(&args[0]);
-			if (!name)
-				return -ENOMEM;
+			return ret;
+		}
+		F2FS_OPTION(sbi).compress_ext_cnt++;
+		kfree(name);
+		return 0;
+	case Opt_nocompress_extension:
+		if (!f2fs_sb_has_compression(sbi)) {
+			f2fs_info(sbi, "Image doesn't support compression");
+			return 0;
+		}
+		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
+		if (!name)
+			return -ENOMEM;
 
-			noext = F2FS_OPTION(sbi).noextensions;
-			noext_cnt = F2FS_OPTION(sbi).nocompress_ext_cnt;
+		noext = F2FS_OPTION(sbi).noextensions;
+		noext_cnt = F2FS_OPTION(sbi).nocompress_ext_cnt;
 
-			if (strlen(name) >= F2FS_EXTENSION_LEN ||
-				noext_cnt >= COMPRESS_EXT_NUM) {
-				f2fs_err(sbi,
-					"invalid extension length/number");
-				kfree(name);
-				return -EINVAL;
-			}
+		if (strlen(name) >= F2FS_EXTENSION_LEN ||
+			noext_cnt >= COMPRESS_EXT_NUM) {
+			f2fs_err(sbi,
+				"invalid extension length/number");
+			kfree(name);
+			return -EINVAL;
+		}
 
-			if (is_compress_extension_exist(sbi, name, false)) {
-				kfree(name);
-				break;
-			}
+		if (is_compress_extension_exist(sbi, name, false)) {
+			kfree(name);
+			return 0;
+		}
 
-			strcpy(noext[noext_cnt], name);
-			F2FS_OPTION(sbi).nocompress_ext_cnt++;
+		ret = strscpy(noext[noext_cnt], name, F2FS_EXTENSION_LEN);
+		if (ret < 0) {
 			kfree(name);
-			break;
-		case Opt_compress_chksum:
-			if (!f2fs_sb_has_compression(sbi)) {
-				f2fs_info(sbi, "Image doesn't support compression");
-				break;
-			}
-			F2FS_OPTION(sbi).compress_chksum = true;
-			break;
-		case Opt_compress_mode:
-			if (!f2fs_sb_has_compression(sbi)) {
-				f2fs_info(sbi, "Image doesn't support compression");
-				break;
-			}
-			name = match_strdup(&args[0]);
-			if (!name)
-				return -ENOMEM;
-			if (!strcmp(name, "fs")) {
-				F2FS_OPTION(sbi).compress_mode = COMPR_MODE_FS;
-			} else if (!strcmp(name, "user")) {
-				F2FS_OPTION(sbi).compress_mode = COMPR_MODE_USER;
-			} else {
-				kfree(name);
-				return -EINVAL;
-			}
+			return ret;
+		}
+		F2FS_OPTION(sbi).nocompress_ext_cnt++;
+		kfree(name);
+		return 0;
+	case Opt_compress_chksum:
+		if (!f2fs_sb_has_compression(sbi)) {
+			f2fs_info(sbi, "Image doesn't support compression");
+			return 0;
+		}
+		F2FS_OPTION(sbi).compress_chksum = true;
+		return 0;
+	case Opt_compress_mode:
+		if (!f2fs_sb_has_compression(sbi)) {
+			f2fs_info(sbi, "Image doesn't support compression");
+			return 0;
+		}
+		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
+		if (!name)
+			return -ENOMEM;
+		if (!strcmp(name, "fs")) {
+			F2FS_OPTION(sbi).compress_mode = COMPR_MODE_FS;
+		} else if (!strcmp(name, "user")) {
+			F2FS_OPTION(sbi).compress_mode = COMPR_MODE_USER;
+		} else {
 			kfree(name);
-			break;
-		case Opt_compress_cache:
-			if (!f2fs_sb_has_compression(sbi)) {
-				f2fs_info(sbi, "Image doesn't support compression");
-				break;
-			}
-			set_opt(sbi, COMPRESS_CACHE);
-			break;
+			return -EINVAL;
+		}
+		kfree(name);
+		return 0;
+	case Opt_compress_cache:
+		if (!f2fs_sb_has_compression(sbi)) {
+			f2fs_info(sbi, "Image doesn't support compression");
+			return 0;
+		}
+		set_opt(sbi, COMPRESS_CACHE);
+		return 0;
 #else
-		case Opt_compress_algorithm:
-		case Opt_compress_log_size:
-		case Opt_compress_extension:
-		case Opt_nocompress_extension:
-		case Opt_compress_chksum:
-		case Opt_compress_mode:
-		case Opt_compress_cache:
-			f2fs_info(sbi, "compression options not supported");
-			break;
+	case Opt_compress_algorithm:
+	case Opt_compress_log_size:
+	case Opt_compress_extension:
+	case Opt_nocompress_extension:
+	case Opt_compress_chksum:
+	case Opt_compress_mode:
+	case Opt_compress_cache:
+		f2fs_info(sbi, "compression options not supported");
+		return 0;
 #endif
-		case Opt_atgc:
-			set_opt(sbi, ATGC);
-			break;
-		case Opt_gc_merge:
-			set_opt(sbi, GC_MERGE);
-			break;
-		case Opt_nogc_merge:
-			clear_opt(sbi, GC_MERGE);
-			break;
-		case Opt_discard_unit:
-			name = match_strdup(&args[0]);
-			if (!name)
-				return -ENOMEM;
-			if (!strcmp(name, "block")) {
-				F2FS_OPTION(sbi).discard_unit =
-						DISCARD_UNIT_BLOCK;
-			} else if (!strcmp(name, "segment")) {
-				F2FS_OPTION(sbi).discard_unit =
-						DISCARD_UNIT_SEGMENT;
-			} else if (!strcmp(name, "section")) {
-				F2FS_OPTION(sbi).discard_unit =
-						DISCARD_UNIT_SECTION;
-			} else {
-				kfree(name);
-				return -EINVAL;
-			}
+	case Opt_atgc:
+		set_opt(sbi, ATGC);
+		return 0;
+	case Opt_gc_merge:
+		set_opt(sbi, GC_MERGE);
+		return 0;
+	case Opt_nogc_merge:
+		clear_opt(sbi, GC_MERGE);
+		return 0;
+	case Opt_discard_unit:
+		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
+		if (!name)
+			return -ENOMEM;
+		if (!strcmp(name, "block")) {
+			F2FS_OPTION(sbi).discard_unit =
+					DISCARD_UNIT_BLOCK;
+		} else if (!strcmp(name, "segment")) {
+			F2FS_OPTION(sbi).discard_unit =
+					DISCARD_UNIT_SEGMENT;
+		} else if (!strcmp(name, "section")) {
+			F2FS_OPTION(sbi).discard_unit =
+					DISCARD_UNIT_SECTION;
+		} else {
 			kfree(name);
-			break;
-		case Opt_memory_mode:
-			name = match_strdup(&args[0]);
-			if (!name)
-				return -ENOMEM;
-			if (!strcmp(name, "normal")) {
-				F2FS_OPTION(sbi).memory_mode =
-						MEMORY_MODE_NORMAL;
-			} else if (!strcmp(name, "low")) {
-				F2FS_OPTION(sbi).memory_mode =
-						MEMORY_MODE_LOW;
-			} else {
-				kfree(name);
-				return -EINVAL;
-			}
+			return -EINVAL;
+		}
+		kfree(name);
+		return 0;
+	case Opt_memory_mode:
+		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
+		if (!name)
+			return -ENOMEM;
+		if (!strcmp(name, "normal")) {
+			F2FS_OPTION(sbi).memory_mode =
+					MEMORY_MODE_NORMAL;
+		} else if (!strcmp(name, "low")) {
+			F2FS_OPTION(sbi).memory_mode =
+					MEMORY_MODE_LOW;
+		} else {
 			kfree(name);
-			break;
-		case Opt_age_extent_cache:
-			set_opt(sbi, AGE_EXTENT_CACHE);
-			break;
-		case Opt_errors:
-			name = match_strdup(&args[0]);
-			if (!name)
-				return -ENOMEM;
-			if (!strcmp(name, "remount-ro")) {
-				F2FS_OPTION(sbi).errors =
-						MOUNT_ERRORS_READONLY;
-			} else if (!strcmp(name, "continue")) {
-				F2FS_OPTION(sbi).errors =
-						MOUNT_ERRORS_CONTINUE;
-			} else if (!strcmp(name, "panic")) {
-				F2FS_OPTION(sbi).errors =
-						MOUNT_ERRORS_PANIC;
-			} else {
-				kfree(name);
-				return -EINVAL;
-			}
+			return -EINVAL;
+		}
+		kfree(name);
+		return 0;
+	case Opt_age_extent_cache:
+		set_opt(sbi, AGE_EXTENT_CACHE);
+		return 0;
+	case Opt_errors:
+		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
+		if (!name)
+			return -ENOMEM;
+		if (!strcmp(name, "remount-ro")) {
+			F2FS_OPTION(sbi).errors =
+					MOUNT_ERRORS_READONLY;
+		} else if (!strcmp(name, "continue")) {
+			F2FS_OPTION(sbi).errors =
+					MOUNT_ERRORS_CONTINUE;
+		} else if (!strcmp(name, "panic")) {
+			F2FS_OPTION(sbi).errors =
+					MOUNT_ERRORS_PANIC;
+		} else {
 			kfree(name);
-			break;
-		default:
-			f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value",
-				 p);
 			return -EINVAL;
 		}
+		kfree(name);
+		return 0;
+	default:
+		f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value",
+			 param->key);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int parse_options(struct super_block *sb, char *options, bool is_remount)
+{
+	struct f2fs_sb_info *sbi = F2FS_SB(sb);
+	struct fs_parameter param;
+	struct fs_context fc;
+	char *key;
+	int ret;
+
+	if (!options)
+		goto default_check;
+
+	memset(&fc, 0, sizeof(fc));
+	fc.s_fs_info = sbi;
+	if (is_remount)
+		fc.purpose = FS_CONTEXT_FOR_RECONFIGURE;
+
+	while ((key = strsep(&options, ",")) != NULL) {
+		if (*key) {
+			size_t v_len = 0;
+			char *value = strchr(key, '=');
+
+			param.type = fs_value_is_flag;
+			param.string = NULL;
+
+			if (value) {
+				if (value == key)
+					continue;
+
+				*value++ = 0;
+				v_len = strlen(value);
+				param.string = kmemdup_nul(value, v_len, GFP_KERNEL);
+				if (!param.string)
+					return -ENOMEM;
+				param.type = fs_value_is_string;
+			}
+
+			param.key = key;
+			param.size = v_len;
+
+			ret = handle_mount_opt(&fc, &param);
+			kfree(param.string);
+			if (ret < 0)
+				return ret;
+		}
 	}
 default_check:
 #ifdef CONFIG_QUOTA
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 3/9] f2fs: move option validation into a separate helper
  2024-08-14  2:39 [PATCH 0/9] f2fs: new mount API conversion Hongbo Li
  2024-08-14  2:39 ` [PATCH 1/9] f2fs: Add fs parameter specifications for mount options Hongbo Li
  2024-08-14  2:39 ` [PATCH 2/9] f2fs: move the option parser into handle_mount_opt Hongbo Li
@ 2024-08-14  2:39 ` Hongbo Li
  2024-08-14  2:39 ` [PATCH 4/9] f2fs: Allow sbi to be NULL in f2fs_printk Hongbo Li
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Hongbo Li @ 2024-08-14  2:39 UTC (permalink / raw)
  To: jaegeuk, chao
  Cc: linux-f2fs-devel, brauner, lczerner, linux-fsdevel, lihongbo22

Just move option validation out of parse_options(), and the
validation logic is enclosed within f2fs_validate_options.

Signed-off-by: Hongbo Li <lihongbo22@huawei.com>
---
 fs/f2fs/super.c | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 013b1078653f..8c8cd06a6d9c 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -41,6 +41,8 @@
 #define CREATE_TRACE_POINTS
 #include <trace/events/f2fs.h>
 
+static int f2fs_validate_options(struct super_block *sb);
+
 static struct kmem_cache *f2fs_inode_cachep;
 
 #ifdef CONFIG_F2FS_FAULT_INJECTION
@@ -1418,7 +1420,15 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
 				return ret;
 		}
 	}
+
 default_check:
+	return f2fs_validate_options(sb);
+}
+
+static int f2fs_validate_options(struct super_block *sb)
+{
+	struct f2fs_sb_info *sbi = F2FS_SB(sb);
+
 #ifdef CONFIG_QUOTA
 	if (f2fs_check_quota_options(sbi))
 		return -EINVAL;
@@ -1432,13 +1442,13 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
 		return -EINVAL;
 	}
 #endif
-
-	if (!IS_ENABLED(CONFIG_UNICODE) && f2fs_sb_has_casefold(sbi)) {
+#if !IS_ENABLED(CONFIG_UNICODE)
+	if (f2fs_sb_has_casefold(sbi)) {
 		f2fs_err(sbi,
 			"Filesystem with casefold feature cannot be mounted without CONFIG_UNICODE");
 		return -EINVAL;
 	}
-
+#endif
 	/*
 	 * The BLKZONED feature indicates that the drive was formatted with
 	 * zone alignment optimization. This is optional for host-aware
@@ -1458,10 +1468,10 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
 			return -EINVAL;
 		}
 #else
-		f2fs_err(sbi, "Zoned block device support is not enabled");
-		return -EINVAL;
+			f2fs_err(sbi, "Zoned block device support is not enabled");
+			return -EINVAL;
 #endif
-	}
+		}
 
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 	if (f2fs_test_compress_extension(sbi)) {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 4/9] f2fs: Allow sbi to be NULL in f2fs_printk
  2024-08-14  2:39 [PATCH 0/9] f2fs: new mount API conversion Hongbo Li
                   ` (2 preceding siblings ...)
  2024-08-14  2:39 ` [PATCH 3/9] f2fs: move option validation into a separate helper Hongbo Li
@ 2024-08-14  2:39 ` Hongbo Li
  2024-08-14  2:39 ` [PATCH 5/9] f2fs: Add f2fs_fs_context to record the mount options Hongbo Li
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Hongbo Li @ 2024-08-14  2:39 UTC (permalink / raw)
  To: jaegeuk, chao
  Cc: linux-f2fs-devel, brauner, lczerner, linux-fsdevel, lihongbo22

At the parsing phase of the new mount api, sbi will not be
available. So here allows sbi to be NULL in f2fs log helpers
and use that in handle_mount_opt().

Signed-off-by: Hongbo Li <lihongbo22@huawei.com>
---
 fs/f2fs/super.c | 106 ++++++++++++++++++++++++++----------------------
 1 file changed, 57 insertions(+), 49 deletions(-)

diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 8c8cd06a6d9c..2a06444e7e02 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -364,11 +364,19 @@ void f2fs_printk(struct f2fs_sb_info *sbi, bool limit_rate,
 	vaf.fmt = printk_skip_level(fmt);
 	vaf.va = &args;
 	if (limit_rate)
-		printk_ratelimited("%c%cF2FS-fs (%s): %pV\n",
-			KERN_SOH_ASCII, level, sbi->sb->s_id, &vaf);
+		if (sbi)
+			printk_ratelimited("%c%cF2FS-fs (%s): %pV\n",
+				KERN_SOH_ASCII, level, sbi->sb->s_id, &vaf);
+		else
+			printk_ratelimited("%c%cF2FS-fs: %pV\n",
+				KERN_SOH_ASCII, level, &vaf);
 	else
-		printk("%c%cF2FS-fs (%s): %pV\n",
-			KERN_SOH_ASCII, level, sbi->sb->s_id, &vaf);
+		if (sbi)
+			printk("%c%cF2FS-fs (%s): %pV\n",
+				KERN_SOH_ASCII, level, sbi->sb->s_id, &vaf);
+		else
+			printk("%c%cF2FS-fs: %pV\n",
+				KERN_SOH_ASCII, level, &vaf);
 
 	va_end(args);
 }
@@ -796,21 +804,21 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		return 0;
 	case Opt_discard:
 		if (!f2fs_hw_support_discard(sbi)) {
-			f2fs_warn(sbi, "device does not support discard");
+			f2fs_warn(NULL, "device does not support discard");
 			return 0;
 		}
 		set_opt(sbi, DISCARD);
 		return 0;
 	case Opt_nodiscard:
 		if (f2fs_hw_should_discard(sbi)) {
-			f2fs_warn(sbi, "discard is required for zoned block devices");
+			f2fs_warn(NULL, "discard is required for zoned block devices");
 			return -EINVAL;
 		}
 		clear_opt(sbi, DISCARD);
 		return 0;
 	case Opt_noheap:
 	case Opt_heap:
-		f2fs_warn(sbi, "heap/no_heap options were deprecated");
+		f2fs_warn(NULL, "heap/no_heap options were deprecated");
 		return 0;
 #ifdef CONFIG_F2FS_FS_XATTR
 	case Opt_user_xattr:
@@ -831,16 +839,16 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		return 0;
 #else
 	case Opt_user_xattr:
-		f2fs_info(sbi, "user_xattr options not supported");
+		f2fs_info(NULL, "user_xattr options not supported");
 		return 0;
 	case Opt_nouser_xattr:
-		f2fs_info(sbi, "nouser_xattr options not supported");
+		f2fs_info(NULL, "nouser_xattr options not supported");
 		return 0;
 	case Opt_inline_xattr:
-		f2fs_info(sbi, "inline_xattr options not supported");
+		f2fs_info(NULL, "inline_xattr options not supported");
 		return 0;
 	case Opt_noinline_xattr:
-		f2fs_info(sbi, "noinline_xattr options not supported");
+		f2fs_info(NULL, "noinline_xattr options not supported");
 		return 0;
 #endif
 #ifdef CONFIG_F2FS_FS_POSIX_ACL
@@ -852,10 +860,10 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		return 0;
 #else
 	case Opt_acl:
-		f2fs_info(sbi, "acl options not supported");
+		f2fs_info(NULL, "acl options not supported");
 		return 0;
 	case Opt_noacl:
-		f2fs_info(sbi, "noacl options not supported");
+		f2fs_info(NULL, "noacl options not supported");
 		return 0;
 #endif
 	case Opt_active_logs:
@@ -905,7 +913,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		return 0;
 	case Opt_reserve_root:
 		if (test_opt(sbi, RESERVE_ROOT)) {
-			f2fs_info(sbi, "Preserve previous reserve_root=%u",
+			f2fs_info(NULL, "Preserve previous reserve_root=%u",
 				  F2FS_OPTION(sbi).root_reserved_blocks);
 		} else {
 			F2FS_OPTION(sbi).root_reserved_blocks = result.uint_32;
@@ -915,7 +923,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 	case Opt_resuid:
 		uid = make_kuid(current_user_ns(), result.uint_32);
 		if (!uid_valid(uid)) {
-			f2fs_err(sbi, "Invalid uid value %u", result.uint_32);
+			f2fs_err(NULL, "Invalid uid value %u", result.uint_32);
 			return -EINVAL;
 		}
 		F2FS_OPTION(sbi).s_resuid = uid;
@@ -923,7 +931,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 	case Opt_resgid:
 		gid = make_kgid(current_user_ns(), result.uint_32);
 		if (!gid_valid(gid)) {
-			f2fs_err(sbi, "Invalid gid value %u", result.uint_32);
+			f2fs_err(NULL, "Invalid gid value %u", result.uint_32);
 			return -EINVAL;
 		}
 		F2FS_OPTION(sbi).s_resgid = gid;
@@ -962,11 +970,11 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		return 0;
 #else
 	case Opt_fault_injection:
-		f2fs_info(sbi, "fault_injection options not supported");
+		f2fs_info(NULL, "fault_injection options not supported");
 		return 0;
 
 	case Opt_fault_type:
-		f2fs_info(sbi, "fault_type options not supported");
+		f2fs_info(NULL, "fault_type options not supported");
 		return 0;
 #endif
 #ifdef CONFIG_QUOTA
@@ -1028,7 +1036,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 	case Opt_jqfmt_vfsv0:
 	case Opt_jqfmt_vfsv1:
 	case Opt_noquota:
-		f2fs_info(sbi, "quota operations not supported");
+		f2fs_info(NULL, "quota operations not supported");
 		return 0;
 #endif
 	case Opt_alloc:
@@ -1072,7 +1080,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
 		sb->s_flags |= SB_INLINECRYPT;
 #else
-		f2fs_info(sbi, "inline encryption not supported");
+		f2fs_info(NULL, "inline encryption not supported");
 #endif
 		return 0;
 	case Opt_checkpoint:
@@ -1126,7 +1134,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 	case Opt_compress_algorithm:
 		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(sbi, "Image doesn't support compression");
+			f2fs_info(NULL, "Image doesn't support compression");
 			return 0;
 		}
 		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
@@ -1138,7 +1146,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			F2FS_OPTION(sbi).compress_algorithm =
 							COMPRESS_LZO;
 #else
-			f2fs_info(sbi, "kernel doesn't support lzo compression");
+			f2fs_info(NULL, "kernel doesn't support lzo compression");
 #endif
 		} else if (!strncmp(name, "lz4", 3)) {
 #ifdef CONFIG_F2FS_FS_LZ4
@@ -1150,7 +1158,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			F2FS_OPTION(sbi).compress_algorithm =
 							COMPRESS_LZ4;
 #else
-			f2fs_info(sbi, "kernel doesn't support lz4 compression");
+			f2fs_info(NULL, "kernel doesn't support lz4 compression");
 #endif
 		} else if (!strncmp(name, "zstd", 4)) {
 #ifdef CONFIG_F2FS_FS_ZSTD
@@ -1162,7 +1170,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			F2FS_OPTION(sbi).compress_algorithm =
 							COMPRESS_ZSTD;
 #else
-			f2fs_info(sbi, "kernel doesn't support zstd compression");
+			f2fs_info(NULL, "kernel doesn't support zstd compression");
 #endif
 		} else if (!strcmp(name, "lzo-rle")) {
 #ifdef CONFIG_F2FS_FS_LZORLE
@@ -1170,7 +1178,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			F2FS_OPTION(sbi).compress_algorithm =
 							COMPRESS_LZORLE;
 #else
-			f2fs_info(sbi, "kernel doesn't support lzorle compression");
+			f2fs_info(NULL, "kernel doesn't support lzorle compression");
 #endif
 		} else {
 			kfree(name);
@@ -1180,12 +1188,12 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		return 0;
 	case Opt_compress_log_size:
 		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(sbi, "Image doesn't support compression");
+			f2fs_info(NULL, "Image doesn't support compression");
 			return 0;
 		}
 		if (result.int_32 < MIN_COMPRESS_LOG_SIZE ||
 			result.int_32 > MAX_COMPRESS_LOG_SIZE) {
-			f2fs_err(sbi,
+			f2fs_err(NULL,
 				"Compress cluster log size is out of range");
 			return -EINVAL;
 		}
@@ -1193,7 +1201,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		return 0;
 	case Opt_compress_extension:
 		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(sbi, "Image doesn't support compression");
+			f2fs_info(NULL, "Image doesn't support compression");
 			break;
 		}
 		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
@@ -1205,7 +1213,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 
 		if (strlen(name) >= F2FS_EXTENSION_LEN ||
 			ext_cnt >= COMPRESS_EXT_NUM) {
-			f2fs_err(sbi,
+			f2fs_err(NULL,
 				"invalid extension length/number");
 			kfree(name);
 			return -EINVAL;
@@ -1226,7 +1234,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		return 0;
 	case Opt_nocompress_extension:
 		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(sbi, "Image doesn't support compression");
+			f2fs_info(NULL, "Image doesn't support compression");
 			return 0;
 		}
 		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
@@ -1238,7 +1246,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 
 		if (strlen(name) >= F2FS_EXTENSION_LEN ||
 			noext_cnt >= COMPRESS_EXT_NUM) {
-			f2fs_err(sbi,
+			f2fs_err(NULL,
 				"invalid extension length/number");
 			kfree(name);
 			return -EINVAL;
@@ -1259,14 +1267,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		return 0;
 	case Opt_compress_chksum:
 		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(sbi, "Image doesn't support compression");
+			f2fs_info(NULL, "Image doesn't support compression");
 			return 0;
 		}
 		F2FS_OPTION(sbi).compress_chksum = true;
 		return 0;
 	case Opt_compress_mode:
 		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(sbi, "Image doesn't support compression");
+			f2fs_info(NULL, "Image doesn't support compression");
 			return 0;
 		}
 		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
@@ -1284,7 +1292,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		return 0;
 	case Opt_compress_cache:
 		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(sbi, "Image doesn't support compression");
+			f2fs_info(NULL, "Image doesn't support compression");
 			return 0;
 		}
 		set_opt(sbi, COMPRESS_CACHE);
@@ -1297,7 +1305,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 	case Opt_compress_chksum:
 	case Opt_compress_mode:
 	case Opt_compress_cache:
-		f2fs_info(sbi, "compression options not supported");
+		f2fs_info(NULL, "compression options not supported");
 		return 0;
 #endif
 	case Opt_atgc:
@@ -1367,7 +1375,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		kfree(name);
 		return 0;
 	default:
-		f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value",
+		f2fs_err(NULL, "Unrecognized mount option \"%s\" or missing value",
 			 param->key);
 		return -EINVAL;
 	}
@@ -1434,17 +1442,17 @@ static int f2fs_validate_options(struct super_block *sb)
 		return -EINVAL;
 #else
 	if (f2fs_sb_has_quota_ino(sbi) && !f2fs_readonly(sbi->sb)) {
-		f2fs_info(sbi, "Filesystem with quota feature cannot be mounted RDWR without CONFIG_QUOTA");
+		f2fs_info(NULL, "Filesystem with quota feature cannot be mounted RDWR without CONFIG_QUOTA");
 		return -EINVAL;
 	}
 	if (f2fs_sb_has_project_quota(sbi) && !f2fs_readonly(sbi->sb)) {
-		f2fs_err(sbi, "Filesystem with project quota feature cannot be mounted RDWR without CONFIG_QUOTA");
+		f2fs_err(NULL, "Filesystem with project quota feature cannot be mounted RDWR without CONFIG_QUOTA");
 		return -EINVAL;
 	}
 #endif
 #if !IS_ENABLED(CONFIG_UNICODE)
 	if (f2fs_sb_has_casefold(sbi)) {
-		f2fs_err(sbi,
+		f2fs_err(NULL,
 			"Filesystem with casefold feature cannot be mounted without CONFIG_UNICODE");
 		return -EINVAL;
 	}
@@ -1458,24 +1466,24 @@ static int f2fs_validate_options(struct super_block *sb)
 #ifdef CONFIG_BLK_DEV_ZONED
 		if (F2FS_OPTION(sbi).discard_unit !=
 						DISCARD_UNIT_SECTION) {
-			f2fs_info(sbi, "Zoned block device doesn't need small discard, set discard_unit=section by default");
+			f2fs_info(NULL, "Zoned block device doesn't need small discard, set discard_unit=section by default");
 			F2FS_OPTION(sbi).discard_unit =
 					DISCARD_UNIT_SECTION;
 		}
 
 		if (F2FS_OPTION(sbi).fs_mode != FS_MODE_LFS) {
-			f2fs_info(sbi, "Only lfs mode is allowed with zoned block device feature");
+			f2fs_info(NULL, "Only lfs mode is allowed with zoned block device feature");
 			return -EINVAL;
 		}
 #else
-			f2fs_err(sbi, "Zoned block device support is not enabled");
+			f2fs_err(NULL, "Zoned block device support is not enabled");
 			return -EINVAL;
 #endif
 		}
 
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 	if (f2fs_test_compress_extension(sbi)) {
-		f2fs_err(sbi, "invalid compress or nocompress extension");
+		f2fs_err(NULL, "invalid compress or nocompress extension");
 		return -EINVAL;
 	}
 #endif
@@ -1485,11 +1493,11 @@ static int f2fs_validate_options(struct super_block *sb)
 
 		if (!f2fs_sb_has_extra_attr(sbi) ||
 			!f2fs_sb_has_flexible_inline_xattr(sbi)) {
-			f2fs_err(sbi, "extra_attr or flexible_inline_xattr feature is off");
+			f2fs_err(NULL, "extra_attr or flexible_inline_xattr feature is off");
 			return -EINVAL;
 		}
 		if (!test_opt(sbi, INLINE_XATTR)) {
-			f2fs_err(sbi, "inline_xattr_size option should be set with inline_xattr option");
+			f2fs_err(NULL, "inline_xattr_size option should be set with inline_xattr option");
 			return -EINVAL;
 		}
 
@@ -1498,24 +1506,24 @@ static int f2fs_validate_options(struct super_block *sb)
 
 		if (F2FS_OPTION(sbi).inline_xattr_size < min_size ||
 				F2FS_OPTION(sbi).inline_xattr_size > max_size) {
-			f2fs_err(sbi, "inline xattr size is out of range: %d ~ %d",
+			f2fs_err(NULL, "inline xattr size is out of range: %d ~ %d",
 				 min_size, max_size);
 			return -EINVAL;
 		}
 	}
 
 	if (test_opt(sbi, ATGC) && f2fs_lfs_mode(sbi)) {
-		f2fs_err(sbi, "LFS is not compatible with ATGC");
+		f2fs_err(NULL, "LFS is not compatible with ATGC");
 		return -EINVAL;
 	}
 
 	if (f2fs_is_readonly(sbi) && test_opt(sbi, FLUSH_MERGE)) {
-		f2fs_err(sbi, "FLUSH_MERGE not compatible with readonly mode");
+		f2fs_err(NULL, "FLUSH_MERGE not compatible with readonly mode");
 		return -EINVAL;
 	}
 
 	if (f2fs_sb_has_readonly(sbi) && !f2fs_readonly(sbi->sb)) {
-		f2fs_err(sbi, "Allow to mount readonly mode only");
+		f2fs_err(NULL, "Allow to mount readonly mode only");
 		return -EROFS;
 	}
 	return 0;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 5/9] f2fs: Add f2fs_fs_context to record the mount options
  2024-08-14  2:39 [PATCH 0/9] f2fs: new mount API conversion Hongbo Li
                   ` (3 preceding siblings ...)
  2024-08-14  2:39 ` [PATCH 4/9] f2fs: Allow sbi to be NULL in f2fs_printk Hongbo Li
@ 2024-08-14  2:39 ` Hongbo Li
  2024-08-14  2:39 ` [PATCH 6/9] f2fs: separate the options parsing and options checking Hongbo Li
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Hongbo Li @ 2024-08-14  2:39 UTC (permalink / raw)
  To: jaegeuk, chao
  Cc: linux-f2fs-devel, brauner, lczerner, linux-fsdevel, lihongbo22

At the parsing phase of mouont in the new mount api, options
value will be recorded with the context, and then it will be
used in fill_super and other helpers.

Notes that, this is a temporary status, we want remove the sb
and sbi usages in handle_mount_opt. So here the f2fs_fs_context
only records the mount options, it will be copied in sb/sbi in
later process.

Signed-off-by: Hongbo Li <lihongbo22@huawei.com>
---
 fs/f2fs/super.c | 409 ++++++++++++++++++++++++++++++++++--------------
 1 file changed, 288 insertions(+), 121 deletions(-)

diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 2a06444e7e02..b6cc020f8d6f 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -351,8 +351,65 @@ static match_table_t f2fs_tokens = {
 	{Opt_err, NULL},
 };
 
+#define F2FS_SPEC_background_gc			(1 << 0)
+#define F2FS_SPEC_inline_xattr_size		(1 << 1)
+#define F2FS_SPEC_active_logs			(1 << 2)
+#define F2FS_SPEC_reserve_root			(1 << 3)
+#define F2FS_SPEC_resgid			(1 << 4)
+#define F2FS_SPEC_resuid			(1 << 5)
+#define F2FS_SPEC_mode				(1 << 6)
+#define F2FS_SPEC_fault_injection		(1 << 7)
+#define F2FS_SPEC_fault_type			(1 << 8)
+#define F2FS_SPEC_jqfmt				(1 << 9)
+#define F2FS_SPEC_alloc_mode			(1 << 10)
+#define F2FS_SPEC_fsync_mode			(1 << 11)
+#define F2FS_SPEC_checkpoint_disable_cap	(1 << 12)
+#define F2FS_SPEC_checkpoint_disable_cap_perc	(1 << 13)
+#define F2FS_SPEC_compress_level		(1 << 14)
+#define F2FS_SPEC_compress_algorithm		(1 << 15)
+#define F2FS_SPEC_compress_log_size		(1 << 16)
+#define F2FS_SPEC_compress_extension		(1 << 17)
+#define F2FS_SPEC_nocompress_extension		(1 << 18)
+#define F2FS_SPEC_compress_chksum		(1 << 19)
+#define F2FS_SPEC_compress_mode			(1 << 20)
+#define F2FS_SPEC_discard_unit			(1 << 21)
+#define F2FS_SPEC_memory_mode			(1 << 22)
+#define F2FS_SPEC_errors			(1 << 23)
+
+struct f2fs_fs_context {
+	struct f2fs_mount_info info;
+	unsigned int	opt_mask;	/* Bits changed */
+	unsigned int	spec_mask;
+	unsigned short	qname_mask;
+	unsigned long	sflags;
+	unsigned long	sflags_mask;
+};
+
+#define F2FS_CTX_INFO(ctx)	((ctx)->info)
+
+static inline void ctx_set_opt(struct f2fs_fs_context *ctx,
+			       unsigned int flag)
+{
+	ctx->info.opt |= flag;
+	ctx->opt_mask |= flag;
+}
+
+static inline void ctx_clear_opt(struct f2fs_fs_context *ctx,
+				 unsigned int flag)
+{
+	ctx->info.opt &= ~flag;
+	ctx->opt_mask |= flag;
+}
+
+static inline void ctx_set_flags(struct f2fs_fs_context *ctx,
+				 unsigned int flag)
+{
+	ctx->sflags |= flag;
+	ctx->sflags_mask |= flag;
+}
+
 void f2fs_printk(struct f2fs_sb_info *sbi, bool limit_rate,
-						const char *fmt, ...)
+					const char *fmt, ...)
 {
 	struct va_format vaf;
 	va_list args;
@@ -524,6 +581,56 @@ static int f2fs_clear_qf_name(struct super_block *sb, int qtype)
 	return 0;
 }
 
+/*
+ * Note the name of the specified quota file.
+ */
+static int f2fs_note_qf_name(struct fs_context *fc, int qtype,
+			     struct fs_parameter *param)
+{
+	struct f2fs_fs_context *ctx = fc->fs_private;
+	char *qname;
+
+	if (param->size < 1) {
+		f2fs_err(NULL, "Missing quota name");
+		return -EINVAL;
+	}
+	if (strchr(param->string, '/')) {
+		f2fs_err(NULL, "quotafile must be on filesystem root");
+		return -EINVAL;
+	}
+	if (ctx->info.s_qf_names[qtype]) {
+		if (strcmp(ctx->info.s_qf_names[qtype], param->string) != 0) {
+			f2fs_err(NULL, "Quota file already specified");
+			return -EINVAL;
+		}
+		return 0;
+	}
+
+	qname = kmemdup_nul(param->string, param->size, GFP_KERNEL);
+	if (!qname) {
+		f2fs_err(NULL, "Not enough memory for storing quotafile name");
+		return -ENOMEM;
+	}
+	F2FS_CTX_INFO(ctx).s_qf_names[qtype] = qname;
+	ctx->qname_mask |= 1 << qtype;
+	return 0;
+}
+
+/*
+ * Clear the name of the specified quota file.
+ */
+static int f2fs_unnote_qf_name(struct fs_context *fc, int qtype)
+{
+	struct f2fs_fs_context *ctx = fc->fs_private;
+
+	if (ctx->info.s_qf_names[qtype])
+		kfree(ctx->info.s_qf_names[qtype]);
+
+	ctx->info.s_qf_names[qtype] = NULL;
+	ctx->qname_mask |= 1 << qtype;
+	return 0;
+}
+
 static int f2fs_check_quota_options(struct f2fs_sb_info *sbi)
 {
 	/*
@@ -570,6 +677,31 @@ static int f2fs_check_quota_options(struct f2fs_sb_info *sbi)
 }
 #endif
 
+static int f2fs_parse_test_dummy_encryption(const struct fs_parameter *param,
+					    struct f2fs_fs_context *ctx)
+{
+	int err;
+
+	if (!IS_ENABLED(CONFIG_FS_ENCRYPTION)) {
+		f2fs_warn(NULL, "test_dummy_encryption option not supported");
+		return -EINVAL;
+	}
+	err = fscrypt_parse_test_dummy_encryption(param,
+					&ctx->info.dummy_enc_policy);
+	if (err) {
+		if (err == -EINVAL)
+			f2fs_warn(NULL, "Value of option \"%s\" is unrecognized",
+				  param->key);
+		else if (err == -EEXIST)
+			f2fs_warn(NULL, "Conflicting test_dummy_encryption options");
+		else
+			f2fs_warn(NULL, "Error processing option \"%s\" [%d]",
+				  param->key, err);
+		return -EINVAL;
+	}
+	return 0;
+}
+
 static int f2fs_set_test_dummy_encryption(struct super_block *sb,
 					  const struct fs_parameter *param,
 					  bool is_remount)
@@ -618,7 +750,7 @@ static int f2fs_set_test_dummy_encryption(struct super_block *sb,
 }
 
 #ifdef CONFIG_F2FS_FS_COMPRESSION
-static bool is_compress_extension_exist(struct f2fs_sb_info *sbi,
+static bool is_compress_extension_exist(struct f2fs_mount_info *info,
 					const char *new_ext, bool is_ext)
 {
 	unsigned char (*ext)[F2FS_EXTENSION_LEN];
@@ -626,11 +758,11 @@ static bool is_compress_extension_exist(struct f2fs_sb_info *sbi,
 	int i;
 
 	if (is_ext) {
-		ext = F2FS_OPTION(sbi).extensions;
-		ext_cnt = F2FS_OPTION(sbi).compress_ext_cnt;
+		ext = info->extensions;
+		ext_cnt = info->compress_ext_cnt;
 	} else {
-		ext = F2FS_OPTION(sbi).noextensions;
-		ext_cnt = F2FS_OPTION(sbi).nocompress_ext_cnt;
+		ext = info->noextensions;
+		ext_cnt = info->nocompress_ext_cnt;
 	}
 
 	for (i = 0; i < ext_cnt; i++) {
@@ -679,58 +811,62 @@ static int f2fs_test_compress_extension(struct f2fs_sb_info *sbi)
 }
 
 #ifdef CONFIG_F2FS_FS_LZ4
-static int f2fs_set_lz4hc_level(struct f2fs_sb_info *sbi, const char *str)
+static int f2fs_set_lz4hc_level(struct f2fs_fs_context *ctx, const char *str)
 {
 #ifdef CONFIG_F2FS_FS_LZ4HC
 	unsigned int level;
 
 	if (strlen(str) == 3) {
-		F2FS_OPTION(sbi).compress_level = 0;
+		F2FS_CTX_INFO(ctx).compress_level = 0;
+		ctx->spec_mask |= F2FS_SPEC_compress_level;
 		return 0;
 	}
 
 	str += 3;
 
 	if (str[0] != ':') {
-		f2fs_info(sbi, "wrong format, e.g. <alg_name>:<compr_level>");
+		f2fs_info(NULL, "wrong format, e.g. <alg_name>:<compr_level>");
 		return -EINVAL;
 	}
 	if (kstrtouint(str + 1, 10, &level))
 		return -EINVAL;
 
 	if (!f2fs_is_compress_level_valid(COMPRESS_LZ4, level)) {
-		f2fs_info(sbi, "invalid lz4hc compress level: %d", level);
+		f2fs_info(NULL, "invalid lz4hc compress level: %d", level);
 		return -EINVAL;
 	}
 
-	F2FS_OPTION(sbi).compress_level = level;
+	F2FS_CTX_INFO(ctx).compress_level = level;
+	ctx->spec_mask |= F2FS_SPEC_compress_level;
 	return 0;
 #else
 	if (strlen(str) == 3) {
-		F2FS_OPTION(sbi).compress_level = 0;
+		F2FS_CTX_INFO(ctx).compress_level = 0;
+		ctx->spec_mask |= F2FS_SPEC_compress_level;
 		return 0;
 	}
-	f2fs_info(sbi, "kernel doesn't support lz4hc compression");
+	f2fs_info(NULL, "kernel doesn't support lz4hc compression");
 	return -EINVAL;
 #endif
 }
 #endif
 
 #ifdef CONFIG_F2FS_FS_ZSTD
-static int f2fs_set_zstd_level(struct f2fs_sb_info *sbi, const char *str)
+static int f2fs_set_zstd_level(struct f2fs_fs_context *ctx, const char *str)
 {
 	int level;
 	int len = 4;
 
 	if (strlen(str) == len) {
-		F2FS_OPTION(sbi).compress_level = F2FS_ZSTD_DEFAULT_CLEVEL;
+		F2FS_CTX_INFO(ctx).compress_level = F2FS_ZSTD_DEFAULT_CLEVEL;
+		ctx->spec_mask |= F2FS_SPEC_compress_level;
 		return 0;
 	}
 
 	str += len;
 
 	if (str[0] != ':') {
-		f2fs_info(sbi, "wrong format, e.g. <alg_name>:<compr_level>");
+		f2fs_info(NULL, "wrong format, e.g. <alg_name>:<compr_level>");
 		return -EINVAL;
 	}
 	if (kstrtoint(str + 1, 10, &level))
@@ -738,16 +874,17 @@ static int f2fs_set_zstd_level(struct f2fs_sb_info *sbi, const char *str)
 
 	/* f2fs does not support negative compress level now */
 	if (level < 0) {
-		f2fs_info(sbi, "do not support negative compress level: %d", level);
+		f2fs_info(NULL, "do not support negative compress level: %d", level);
 		return -ERANGE;
 	}
 
 	if (!f2fs_is_compress_level_valid(COMPRESS_ZSTD, level)) {
-		f2fs_info(sbi, "invalid zstd compress level: %d", level);
+		f2fs_info(NULL, "invalid zstd compress level: %d", level);
 		return -EINVAL;
 	}
 
-	F2FS_OPTION(sbi).compress_level = level;
+	F2FS_CTX_INFO(ctx).compress_level = level;
+	ctx->spec_mask |= F2FS_SPEC_compress_level;
 	return 0;
 }
 #endif
@@ -755,6 +892,7 @@ static int f2fs_set_zstd_level(struct f2fs_sb_info *sbi, const char *str)
 
 static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 {
+	struct f2fs_fs_context *ctx = fc->fs_private;
 	struct f2fs_sb_info *sbi = fc->s_fs_info;
 	struct super_block *sb = sbi->sb;
 #ifdef CONFIG_F2FS_FS_COMPRESSION
@@ -782,23 +920,24 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		if (!name)
 			return -ENOMEM;
 		if (!strcmp(name, "on")) {
-			F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_ON;
+			F2FS_CTX_INFO(ctx).bggc_mode = BGGC_MODE_ON;
 		} else if (!strcmp(name, "off")) {
-			F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_OFF;
+			F2FS_CTX_INFO(ctx).bggc_mode = BGGC_MODE_OFF;
 		} else if (!strcmp(name, "sync")) {
-			F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_SYNC;
+			F2FS_CTX_INFO(ctx).bggc_mode = BGGC_MODE_SYNC;
 		} else {
 			kfree(name);
 			return -EINVAL;
 		}
+		ctx->spec_mask |= F2FS_SPEC_background_gc;
 		kfree(name);
 		return 0;
 	case Opt_disable_roll_forward:
-		set_opt(sbi, DISABLE_ROLL_FORWARD);
+		ctx_set_opt(ctx, F2FS_MOUNT_DISABLE_ROLL_FORWARD);
 		return 0;
 	case Opt_norecovery:
 		/* this option mounts f2fs with ro */
-		set_opt(sbi, NORECOVERY);
+		ctx_set_opt(ctx, F2FS_MOUNT_NORECOVERY);
 		if (!f2fs_readonly(sb))
 			return -EINVAL;
 		return 0;
@@ -807,14 +946,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			f2fs_warn(NULL, "device does not support discard");
 			return 0;
 		}
-		set_opt(sbi, DISCARD);
+		ctx_set_opt(ctx, F2FS_MOUNT_DISCARD);
 		return 0;
 	case Opt_nodiscard:
 		if (f2fs_hw_should_discard(sbi)) {
 			f2fs_warn(NULL, "discard is required for zoned block devices");
 			return -EINVAL;
 		}
-		clear_opt(sbi, DISCARD);
+		ctx_clear_opt(ctx, F2FS_MOUNT_DISCARD);
 		return 0;
 	case Opt_noheap:
 	case Opt_heap:
@@ -822,20 +961,21 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		return 0;
 #ifdef CONFIG_F2FS_FS_XATTR
 	case Opt_user_xattr:
-		set_opt(sbi, XATTR_USER);
+		ctx_set_opt(ctx, F2FS_MOUNT_XATTR_USER);
 		return 0;
 	case Opt_nouser_xattr:
-		clear_opt(sbi, XATTR_USER);
+		ctx_clear_opt(ctx, F2FS_MOUNT_XATTR_USER);
 		return 0;
 	case Opt_inline_xattr:
-		set_opt(sbi, INLINE_XATTR);
+		ctx_set_opt(ctx, F2FS_MOUNT_INLINE_XATTR);
 		return 0;
 	case Opt_noinline_xattr:
-		clear_opt(sbi, INLINE_XATTR);
+		ctx_clear_opt(ctx, F2FS_MOUNT_INLINE_XATTR);
 		return 0;
 	case Opt_inline_xattr_size:
-		set_opt(sbi, INLINE_XATTR_SIZE);
-		F2FS_OPTION(sbi).inline_xattr_size = result.int_32;
+		ctx_set_opt(ctx, F2FS_MOUNT_INLINE_XATTR_SIZE);
+		F2FS_CTX_INFO(ctx).inline_xattr_size = result.int_32;
+		ctx->spec_mask |= F2FS_SPEC_inline_xattr_size;
 		return 0;
 #else
 	case Opt_user_xattr:
@@ -853,10 +993,10 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 #endif
 #ifdef CONFIG_F2FS_FS_POSIX_ACL
 	case Opt_acl:
-		set_opt(sbi, POSIX_ACL);
+		ctx_set_opt(ctx, F2FS_MOUNT_POSIX_ACL);
 		return 0;
 	case Opt_noacl:
-		clear_opt(sbi, POSIX_ACL);
+		ctx_clear_opt(ctx, F2FS_MOUNT_POSIX_ACL);
 		return 0;
 #else
 	case Opt_acl:
@@ -870,54 +1010,56 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		if (result.int_32 != 2 && result.int_32 != 4 &&
 			result.int_32 != NR_CURSEG_PERSIST_TYPE)
 			return -EINVAL;
-		F2FS_OPTION(sbi).active_logs = result.int_32;
+		F2FS_CTX_INFO(ctx).active_logs = result.int_32;
+		ctx->spec_mask |= F2FS_SPEC_active_logs;
 		return 0;
 	case Opt_disable_ext_identify:
-		set_opt(sbi, DISABLE_EXT_IDENTIFY);
+		ctx_set_opt(ctx, F2FS_MOUNT_DISABLE_EXT_IDENTIFY);
 		return 0;
 	case Opt_inline_data:
-		set_opt(sbi, INLINE_DATA);
+		ctx_set_opt(ctx, F2FS_MOUNT_INLINE_DATA);
 		return 0;
 	case Opt_inline_dentry:
-		set_opt(sbi, INLINE_DENTRY);
+		ctx_set_opt(ctx, F2FS_MOUNT_INLINE_DENTRY);
 		return 0;
 	case Opt_noinline_dentry:
-		clear_opt(sbi, INLINE_DENTRY);
+		ctx_clear_opt(ctx, F2FS_MOUNT_INLINE_DENTRY);
 		return 0;
 	case Opt_flush_merge:
-		set_opt(sbi, FLUSH_MERGE);
+		ctx_set_opt(ctx, F2FS_MOUNT_FLUSH_MERGE);
 		return 0;
 	case Opt_noflush_merge:
-		clear_opt(sbi, FLUSH_MERGE);
+		ctx_clear_opt(ctx, F2FS_MOUNT_FLUSH_MERGE);
 		return 0;
 	case Opt_nobarrier:
-		set_opt(sbi, NOBARRIER);
+		ctx_set_opt(ctx, F2FS_MOUNT_NOBARRIER);
 		return 0;
 	case Opt_barrier:
-		clear_opt(sbi, NOBARRIER);
+		ctx_clear_opt(ctx, F2FS_MOUNT_NOBARRIER);
 		return 0;
 	case Opt_fastboot:
-		set_opt(sbi, FASTBOOT);
+		ctx_set_opt(ctx, F2FS_MOUNT_FASTBOOT);
 		return 0;
 	case Opt_extent_cache:
-		set_opt(sbi, READ_EXTENT_CACHE);
+		ctx_set_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE);
 		return 0;
 	case Opt_noextent_cache:
-		clear_opt(sbi, READ_EXTENT_CACHE);
+		ctx_clear_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE);
 		return 0;
 	case Opt_noinline_data:
-		clear_opt(sbi, INLINE_DATA);
+		ctx_clear_opt(ctx, F2FS_MOUNT_INLINE_DATA);
 		return 0;
 	case Opt_data_flush:
-		set_opt(sbi, DATA_FLUSH);
+		ctx_set_opt(ctx, F2FS_MOUNT_DATA_FLUSH);
 		return 0;
 	case Opt_reserve_root:
 		if (test_opt(sbi, RESERVE_ROOT)) {
 			f2fs_info(NULL, "Preserve previous reserve_root=%u",
 				  F2FS_OPTION(sbi).root_reserved_blocks);
 		} else {
-			F2FS_OPTION(sbi).root_reserved_blocks = result.uint_32;
-			set_opt(sbi, RESERVE_ROOT);
+			ctx_set_opt(ctx, F2FS_MOUNT_RESERVE_ROOT);
+			F2FS_CTX_INFO(ctx).root_reserved_blocks = result.uint_32;
+			ctx->spec_mask |= F2FS_SPEC_reserve_root;
 		}
 		return 0;
 	case Opt_resuid:
@@ -926,7 +1068,8 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			f2fs_err(NULL, "Invalid uid value %u", result.uint_32);
 			return -EINVAL;
 		}
-		F2FS_OPTION(sbi).s_resuid = uid;
+		F2FS_CTX_INFO(ctx).s_resuid = uid;
+		ctx->spec_mask |= F2FS_SPEC_resuid;
 		return 0;
 	case Opt_resgid:
 		gid = make_kgid(current_user_ns(), result.uint_32);
@@ -934,7 +1077,8 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			f2fs_err(NULL, "Invalid gid value %u", result.uint_32);
 			return -EINVAL;
 		}
-		F2FS_OPTION(sbi).s_resgid = gid;
+		F2FS_CTX_INFO(ctx).s_resgid = gid;
+		ctx->spec_mask |= F2FS_SPEC_resgid;
 		return 0;
 	case Opt_mode:
 		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
@@ -942,17 +1086,18 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		if (!name)
 			return -ENOMEM;
 		if (!strcmp(name, "adaptive")) {
-			F2FS_OPTION(sbi).fs_mode = FS_MODE_ADAPTIVE;
+			F2FS_CTX_INFO(ctx).fs_mode = FS_MODE_ADAPTIVE;
 		} else if (!strcmp(name, "lfs")) {
-			F2FS_OPTION(sbi).fs_mode = FS_MODE_LFS;
+			F2FS_CTX_INFO(ctx).fs_mode = FS_MODE_LFS;
 		} else if (!strcmp(name, "fragment:segment")) {
-			F2FS_OPTION(sbi).fs_mode = FS_MODE_FRAGMENT_SEG;
+			F2FS_CTX_INFO(ctx).fs_mode = FS_MODE_FRAGMENT_SEG;
 		} else if (!strcmp(name, "fragment:block")) {
-			F2FS_OPTION(sbi).fs_mode = FS_MODE_FRAGMENT_BLK;
+			F2FS_CTX_INFO(ctx).fs_mode = FS_MODE_FRAGMENT_BLK;
 		} else {
 			kfree(name);
 			return -EINVAL;
 		}
+		ctx->spec_mask |= F2FS_SPEC_mode;
 		kfree(name);
 		return 0;
 #ifdef CONFIG_F2FS_FAULT_INJECTION
@@ -960,13 +1105,17 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		if (f2fs_build_fault_attr(sbi, result.int_32,
 				F2FS_ALL_FAULT_TYPE))
 			return -EINVAL;
-		set_opt(sbi, FAULT_INJECTION);
+		F2FS_CTX_INFO(ctx).fault_info.inject_rate = result.int_32;
+		ctx->spec_mask |= F2FS_SPEC_fault_injection;
+		ctx_set_opt(ctx, F2FS_MOUNT_FAULT_INJECTION);
 		return 0;
 
 	case Opt_fault_type:
 		if (f2fs_build_fault_attr(sbi, 0, result.uint_32))
 			return -EINVAL;
-		set_opt(sbi, FAULT_INJECTION);
+		F2FS_CTX_INFO(ctx).fault_info.inject_type = result.uint_32;
+		ctx->spec_mask |= F2FS_SPEC_fault_type;
+		ctx_set_opt(ctx, F2FS_MOUNT_FAULT_INJECTION);
 		return 0;
 #else
 	case Opt_fault_injection:
@@ -980,46 +1129,47 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 #ifdef CONFIG_QUOTA
 	case Opt_quota:
 	case Opt_usrquota:
-		set_opt(sbi, USRQUOTA);
+		ctx_set_opt(ctx, F2FS_MOUNT_USRQUOTA);
 		return 0;
 	case Opt_grpquota:
-		set_opt(sbi, GRPQUOTA);
+		ctx_set_opt(ctx, F2FS_MOUNT_GRPQUOTA);
 		return 0;
 	case Opt_prjquota:
-		set_opt(sbi, PRJQUOTA);
+		ctx_set_opt(ctx, F2FS_MOUNT_PRJQUOTA);
 		return 0;
 	case Opt_usrjquota:
 		if (!*param->string)
-			ret = f2fs_clear_qf_name(sb, USRQUOTA);
+			ret = f2fs_unnote_qf_name(fc, USRQUOTA);
 		else
-			ret = f2fs_set_qf_name(sb, USRQUOTA, param);
+			ret = f2fs_note_qf_name(fc, USRQUOTA, param);
 		if (ret)
 			return ret;
 		return 0;
 	case Opt_grpjquota:
 		if (!*param->string)
-			ret = f2fs_clear_qf_name(sb, GRPQUOTA);
+			ret = f2fs_unnote_qf_name(fc, GRPQUOTA);
 		else
-			ret = f2fs_set_qf_name(sb, GRPQUOTA, param);
+			ret = f2fs_note_qf_name(fc, GRPQUOTA, param);
 		if (ret)
 			return ret;
 		return 0;
 	case Opt_prjjquota:
 		if (!*param->string)
-			ret = f2fs_clear_qf_name(sb, PRJQUOTA);
+			ret = f2fs_unnote_qf_name(fc, PRJQUOTA);
 		else
-			ret = f2fs_set_qf_name(sb, PRJQUOTA, param);
+			ret = f2fs_note_qf_name(fc, PRJQUOTA, param);
 		if (ret)
 			return ret;
 		return 0;
 	case Opt_jqfmt:
-		F2FS_OPTION(sbi).s_jquota_fmt = result.uint_32;
+		F2FS_CTX_INFO(ctx).s_jquota_fmt = result.int_32;
+		ctx->spec_mask |= F2FS_SPEC_jqfmt;
 		return 0;
 	case Opt_noquota:
-		clear_opt(sbi, QUOTA);
-		clear_opt(sbi, USRQUOTA);
-		clear_opt(sbi, GRPQUOTA);
-		clear_opt(sbi, PRJQUOTA);
+		ctx_clear_opt(ctx, F2FS_MOUNT_QUOTA);
+		ctx_clear_opt(ctx, F2FS_MOUNT_USRQUOTA);
+		ctx_clear_opt(ctx, F2FS_MOUNT_GRPQUOTA);
+		ctx_clear_opt(ctx, F2FS_MOUNT_PRJQUOTA);
 		return 0;
 #else
 	case Opt_quota:
@@ -1045,13 +1195,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			return -ENOMEM;
 
 		if (!strcmp(name, "default")) {
-			F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_DEFAULT;
+			F2FS_CTX_INFO(ctx).alloc_mode = ALLOC_MODE_DEFAULT;
 		} else if (!strcmp(name, "reuse")) {
-			F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_REUSE;
+			F2FS_CTX_INFO(ctx).alloc_mode = ALLOC_MODE_REUSE;
 		} else {
 			kfree(name);
 			return -EINVAL;
 		}
+		ctx->spec_mask |= F2FS_SPEC_alloc_mode;
 		kfree(name);
 		return 0;
 	case Opt_fsync:
@@ -1059,26 +1210,26 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		if (!name)
 			return -ENOMEM;
 		if (!strcmp(name, "posix")) {
-			F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_POSIX;
+			F2FS_CTX_INFO(ctx).fsync_mode = FSYNC_MODE_POSIX;
 		} else if (!strcmp(name, "strict")) {
-			F2FS_OPTION(sbi).fsync_mode = FSYNC_MODE_STRICT;
+			F2FS_CTX_INFO(ctx).fsync_mode = FSYNC_MODE_STRICT;
 		} else if (!strcmp(name, "nobarrier")) {
-			F2FS_OPTION(sbi).fsync_mode =
-						FSYNC_MODE_NOBARRIER;
+			F2FS_CTX_INFO(ctx).fsync_mode = FSYNC_MODE_NOBARRIER;
 		} else {
 			kfree(name);
 			return -EINVAL;
 		}
+		ctx->spec_mask |= F2FS_SPEC_fsync_mode;
 		kfree(name);
 		return 0;
 	case Opt_test_dummy_encryption:
-		ret = f2fs_set_test_dummy_encryption(sb, param, is_remount);
+		ret = f2fs_parse_test_dummy_encryption(param, ctx);
 		if (ret)
 			return ret;
 		return 0;
 	case Opt_inlinecrypt:
 #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
-		sb->s_flags |= SB_INLINECRYPT;
+		ctx_set_flags(ctx, SB_INLINECRYPT);
 #else
 		f2fs_info(NULL, "inline encryption not supported");
 #endif
@@ -1092,12 +1243,12 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		unsigned long cap = 0;
 
 		if (!strcmp(param->string, "enable")) {
-			clear_opt(sbi, DISABLE_CHECKPOINT);
+			ctx_clear_opt(ctx, F2FS_MOUNT_DISABLE_CHECKPOINT);
 			return 0;
 		}
 
 		if (!strcmp(param->string, "disable")) {
-			set_opt(sbi, DISABLE_CHECKPOINT);
+			ctx_set_opt(ctx, F2FS_MOUNT_DISABLE_CHECKPOINT);
 			return 0;
 		}
 
@@ -1113,23 +1264,25 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		if (!strcmp(cp, endp))
 			return -EINVAL;
 		if (strlen(endp) == 0) {
-			F2FS_OPTION(sbi).unusable_cap = cap;
-			set_opt(sbi, DISABLE_CHECKPOINT);
+			F2FS_CTX_INFO(ctx).unusable_cap = cap;
+			ctx->spec_mask |= F2FS_SPEC_checkpoint_disable_cap;
+			ctx_set_opt(ctx, F2FS_MOUNT_DISABLE_CHECKPOINT);
 			return 0;
 		}
 		if (strcmp(endp, "%"))
 			return -EINVAL;
 		if (cap > 100)
 			return -EINVAL;
-		F2FS_OPTION(sbi).unusable_cap_perc = cap;
-		set_opt(sbi, DISABLE_CHECKPOINT);
+		F2FS_CTX_INFO(ctx).unusable_cap_perc = cap;
+		ctx->spec_mask |= F2FS_SPEC_checkpoint_disable_cap_perc;
+		ctx_set_opt(ctx, F2FS_MOUNT_DISABLE_CHECKPOINT);
 		return 0;
 	}
 	case Opt_checkpoint_merge:
-		set_opt(sbi, MERGE_CHECKPOINT);
+		ctx_set_opt(ctx, F2FS_MOUNT_MERGE_CHECKPOINT);
 		return 0;
 	case Opt_nocheckpoint_merge:
-		clear_opt(sbi, MERGE_CHECKPOINT);
+		ctx_clear_opt(ctx, F2FS_MOUNT_MERGE_CHECKPOINT);
 		return 0;
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 	case Opt_compress_algorithm:
@@ -1142,41 +1295,47 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			return -ENOMEM;
 		if (!strcmp(name, "lzo")) {
 #ifdef CONFIG_F2FS_FS_LZO
-			F2FS_OPTION(sbi).compress_level = 0;
-			F2FS_OPTION(sbi).compress_algorithm =
+			F2FS_CTX_INFO(ctx).compress_level = 0;
+			F2FS_CTX_INFO(ctx).compress_algorithm =
 							COMPRESS_LZO;
+			ctx->spec_mask |= F2FS_SPEC_compress_level;
+			ctx->spec_mask |= F2FS_SPEC_compress_algorithm;
 #else
 			f2fs_info(NULL, "kernel doesn't support lzo compression");
 #endif
 		} else if (!strncmp(name, "lz4", 3)) {
 #ifdef CONFIG_F2FS_FS_LZ4
-			ret = f2fs_set_lz4hc_level(sbi, name);
+			ret = f2fs_set_lz4hc_level(ctx, name);
 			if (ret) {
 				kfree(name);
 				return -EINVAL;
 			}
-			F2FS_OPTION(sbi).compress_algorithm =
+			F2FS_CTX_INFO(ctx).compress_algorithm =
 							COMPRESS_LZ4;
+			ctx->spec_mask |= F2FS_SPEC_compress_algorithm;
 #else
 			f2fs_info(NULL, "kernel doesn't support lz4 compression");
 #endif
 		} else if (!strncmp(name, "zstd", 4)) {
 #ifdef CONFIG_F2FS_FS_ZSTD
-			ret = f2fs_set_zstd_level(sbi, name);
+			ret = f2fs_set_zstd_level(ctx, name);
 			if (ret) {
 				kfree(name);
 				return -EINVAL;
 			}
-			F2FS_OPTION(sbi).compress_algorithm =
+			F2FS_CTX_INFO(ctx).compress_algorithm =
 							COMPRESS_ZSTD;
+			ctx->spec_mask |= F2FS_SPEC_compress_algorithm;
 #else
 			f2fs_info(NULL, "kernel doesn't support zstd compression");
 #endif
 		} else if (!strcmp(name, "lzo-rle")) {
 #ifdef CONFIG_F2FS_FS_LZORLE
-			F2FS_OPTION(sbi).compress_level = 0;
-			F2FS_OPTION(sbi).compress_algorithm =
+			F2FS_CTX_INFO(ctx).compress_level = 0;
+			F2FS_CTX_INFO(ctx).compress_algorithm =
 							COMPRESS_LZORLE;
+			ctx->spec_mask |= F2FS_SPEC_compress_level;
+			ctx->spec_mask |= F2FS_SPEC_compress_algorithm;
 #else
 			f2fs_info(NULL, "kernel doesn't support lzorle compression");
 #endif
@@ -1197,7 +1356,8 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 				"Compress cluster log size is out of range");
 			return -EINVAL;
 		}
-		F2FS_OPTION(sbi).compress_log_size = result.int_32;
+		F2FS_CTX_INFO(ctx).compress_log_size = result.int_32;
+		ctx->spec_mask |= F2FS_SPEC_compress_log_size;
 		return 0;
 	case Opt_compress_extension:
 		if (!f2fs_sb_has_compression(sbi)) {
@@ -1208,8 +1368,8 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		if (!name)
 			return -ENOMEM;
 
-		ext = F2FS_OPTION(sbi).extensions;
-		ext_cnt = F2FS_OPTION(sbi).compress_ext_cnt;
+		ext = F2FS_CTX_INFO(ctx).extensions;
+		ext_cnt = F2FS_CTX_INFO(ctx).compress_ext_cnt;
 
 		if (strlen(name) >= F2FS_EXTENSION_LEN ||
 			ext_cnt >= COMPRESS_EXT_NUM) {
@@ -1219,7 +1379,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			return -EINVAL;
 		}
 
-		if (is_compress_extension_exist(sbi, name, true)) {
+		if (is_compress_extension_exist(&ctx->info, name, true)) {
 			kfree(name);
 			return 0;
 		}
@@ -1229,7 +1389,8 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			kfree(name);
 			return ret;
 		}
-		F2FS_OPTION(sbi).compress_ext_cnt++;
+		F2FS_CTX_INFO(ctx).compress_ext_cnt++;
+		ctx->spec_mask |= F2FS_SPEC_compress_extension;
 		kfree(name);
 		return 0;
 	case Opt_nocompress_extension:
@@ -1241,8 +1402,8 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		if (!name)
 			return -ENOMEM;
 
-		noext = F2FS_OPTION(sbi).noextensions;
-		noext_cnt = F2FS_OPTION(sbi).nocompress_ext_cnt;
+		noext = F2FS_CTX_INFO(ctx).noextensions;
+		noext_cnt = F2FS_CTX_INFO(ctx).nocompress_ext_cnt;
 
 		if (strlen(name) >= F2FS_EXTENSION_LEN ||
 			noext_cnt >= COMPRESS_EXT_NUM) {
@@ -1252,7 +1413,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			return -EINVAL;
 		}
 
-		if (is_compress_extension_exist(sbi, name, false)) {
+		if (is_compress_extension_exist(&ctx->info, name, false)) {
 			kfree(name);
 			return 0;
 		}
@@ -1262,7 +1423,8 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			kfree(name);
 			return ret;
 		}
-		F2FS_OPTION(sbi).nocompress_ext_cnt++;
+		F2FS_CTX_INFO(ctx).nocompress_ext_cnt++;
+		ctx->spec_mask |= F2FS_SPEC_nocompress_extension;
 		kfree(name);
 		return 0;
 	case Opt_compress_chksum:
@@ -1270,7 +1432,8 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			f2fs_info(NULL, "Image doesn't support compression");
 			return 0;
 		}
-		F2FS_OPTION(sbi).compress_chksum = true;
+		F2FS_CTX_INFO(ctx).compress_chksum = true;
+		ctx->spec_mask |= F2FS_SPEC_compress_chksum;
 		return 0;
 	case Opt_compress_mode:
 		if (!f2fs_sb_has_compression(sbi)) {
@@ -1281,13 +1444,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		if (!name)
 			return -ENOMEM;
 		if (!strcmp(name, "fs")) {
-			F2FS_OPTION(sbi).compress_mode = COMPR_MODE_FS;
+			F2FS_CTX_INFO(ctx).compress_mode = COMPR_MODE_FS;
 		} else if (!strcmp(name, "user")) {
-			F2FS_OPTION(sbi).compress_mode = COMPR_MODE_USER;
+			F2FS_CTX_INFO(ctx).compress_mode = COMPR_MODE_USER;
 		} else {
 			kfree(name);
 			return -EINVAL;
 		}
+		ctx->spec_mask |= F2FS_SPEC_compress_mode;
 		kfree(name);
 		return 0;
 	case Opt_compress_cache:
@@ -1295,7 +1459,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			f2fs_info(NULL, "Image doesn't support compression");
 			return 0;
 		}
-		set_opt(sbi, COMPRESS_CACHE);
+		ctx_set_opt(ctx, F2FS_MOUNT_COMPRESS_CACHE);
 		return 0;
 #else
 	case Opt_compress_algorithm:
@@ -1309,31 +1473,32 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		return 0;
 #endif
 	case Opt_atgc:
-		set_opt(sbi, ATGC);
+		ctx_set_opt(ctx, F2FS_MOUNT_ATGC);
 		return 0;
 	case Opt_gc_merge:
-		set_opt(sbi, GC_MERGE);
+		ctx_set_opt(ctx, F2FS_MOUNT_GC_MERGE);
 		return 0;
 	case Opt_nogc_merge:
-		clear_opt(sbi, GC_MERGE);
+		ctx_clear_opt(ctx, F2FS_MOUNT_GC_MERGE);
 		return 0;
 	case Opt_discard_unit:
 		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
 		if (!name)
 			return -ENOMEM;
 		if (!strcmp(name, "block")) {
-			F2FS_OPTION(sbi).discard_unit =
+			F2FS_CTX_INFO(ctx).discard_unit =
 					DISCARD_UNIT_BLOCK;
 		} else if (!strcmp(name, "segment")) {
-			F2FS_OPTION(sbi).discard_unit =
+			F2FS_CTX_INFO(ctx).discard_unit =
 					DISCARD_UNIT_SEGMENT;
 		} else if (!strcmp(name, "section")) {
-			F2FS_OPTION(sbi).discard_unit =
+			F2FS_CTX_INFO(ctx).discard_unit =
 					DISCARD_UNIT_SECTION;
 		} else {
 			kfree(name);
 			return -EINVAL;
 		}
+		ctx->spec_mask |= F2FS_SPEC_discard_unit;
 		kfree(name);
 		return 0;
 	case Opt_memory_mode:
@@ -1341,37 +1506,39 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		if (!name)
 			return -ENOMEM;
 		if (!strcmp(name, "normal")) {
-			F2FS_OPTION(sbi).memory_mode =
+			F2FS_CTX_INFO(ctx).memory_mode =
 					MEMORY_MODE_NORMAL;
 		} else if (!strcmp(name, "low")) {
-			F2FS_OPTION(sbi).memory_mode =
+			F2FS_CTX_INFO(ctx).memory_mode =
 					MEMORY_MODE_LOW;
 		} else {
 			kfree(name);
 			return -EINVAL;
 		}
+		ctx->spec_mask |= F2FS_SPEC_memory_mode;
 		kfree(name);
 		return 0;
 	case Opt_age_extent_cache:
-		set_opt(sbi, AGE_EXTENT_CACHE);
+		ctx_set_opt(ctx, F2FS_MOUNT_AGE_EXTENT_CACHE);
 		return 0;
 	case Opt_errors:
 		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
 		if (!name)
 			return -ENOMEM;
 		if (!strcmp(name, "remount-ro")) {
-			F2FS_OPTION(sbi).errors =
+			F2FS_CTX_INFO(ctx).errors =
 					MOUNT_ERRORS_READONLY;
 		} else if (!strcmp(name, "continue")) {
-			F2FS_OPTION(sbi).errors =
+			F2FS_CTX_INFO(ctx).errors =
 					MOUNT_ERRORS_CONTINUE;
 		} else if (!strcmp(name, "panic")) {
-			F2FS_OPTION(sbi).errors =
+			F2FS_CTX_INFO(ctx).errors =
 					MOUNT_ERRORS_PANIC;
 		} else {
 			kfree(name);
 			return -EINVAL;
 		}
+		ctx->spec_mask |= F2FS_SPEC_errors;
 		kfree(name);
 		return 0;
 	default:
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 6/9] f2fs: separate the options parsing and options checking
  2024-08-14  2:39 [PATCH 0/9] f2fs: new mount API conversion Hongbo Li
                   ` (4 preceding siblings ...)
  2024-08-14  2:39 ` [PATCH 5/9] f2fs: Add f2fs_fs_context to record the mount options Hongbo Li
@ 2024-08-14  2:39 ` Hongbo Li
  2024-08-14  2:39 ` [PATCH 7/9] f2fs: introduce fs_context_operation structure Hongbo Li
                   ` (5 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Hongbo Li @ 2024-08-14  2:39 UTC (permalink / raw)
  To: jaegeuk, chao
  Cc: linux-f2fs-devel, brauner, lczerner, linux-fsdevel, lihongbo22

The new mount api separates option parsing and super block setup
into two distinc steps and so we need to separate the options
parsing out of the parse_options().

In order to achieve this, here we handle the mount options with
three steps:
  - Firstly, we move sb/sbi out of handle_mount_opt.
    As the former patch introduce f2fs_fs_context, so we record
    the changed mount options in this context. In handle_mount_opt,
    sb/sbi is null, so we should move all relative code out of
    handle_mount_opt (thus, some check case which use sb/sbi should
    move out).
  - Secondly, we introduce the some check helpers to keep the option
  consistent.
    During filling superblock period, sb/sbi are ready. So we check
    the f2fs_fs_context which holds the mount options base on sb/sbi.
  - Thirdly, we apply the new mount options to sb/sbi.
    After checking the f2fs_fs_context, all changed on mount options
    are valid. So we can apply them to sb/sbi directly.

After do these, option parsing and super block setting have been
decoupled. Also it should have retained the original execution
flow.

Signed-off-by: Hongbo Li <lihongbo22@huawei.com>
---
 fs/f2fs/super.c | 665 +++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 538 insertions(+), 127 deletions(-)

diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index b6cc020f8d6f..f658d75f4bb7 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -41,7 +41,7 @@
 #define CREATE_TRACE_POINTS
 #include <trace/events/f2fs.h>
 
-static int f2fs_validate_options(struct super_block *sb);
+static int f2fs_validate_options(struct fs_context *fc);
 
 static struct kmem_cache *f2fs_inode_cachep;
 
@@ -401,6 +401,12 @@ static inline void ctx_clear_opt(struct f2fs_fs_context *ctx,
 	ctx->opt_mask |= flag;
 }
 
+static inline bool ctx_test_opt(struct f2fs_fs_context *ctx,
+				unsigned int flag)
+{
+	return ctx->info.opt & flag;
+}
+
 static inline void ctx_set_flags(struct f2fs_fs_context *ctx,
 				 unsigned int flag)
 {
@@ -780,28 +786,28 @@ static bool is_compress_extension_exist(struct f2fs_mount_info *info,
  * extension will be treated as special cases and will not be compressed.
  * 3. Don't allow the non-compress extension specifies all files.
  */
-static int f2fs_test_compress_extension(struct f2fs_sb_info *sbi)
+static int f2fs_test_compress_extension(unsigned char (*noext)[F2FS_EXTENSION_LEN],
+					int noext_cnt,
+					unsigned char (*ext)[F2FS_EXTENSION_LEN],
+					int ext_cnt)
 {
-	unsigned char (*ext)[F2FS_EXTENSION_LEN];
-	unsigned char (*noext)[F2FS_EXTENSION_LEN];
-	int ext_cnt, noext_cnt, index = 0, no_index = 0;
-
-	ext = F2FS_OPTION(sbi).extensions;
-	ext_cnt = F2FS_OPTION(sbi).compress_ext_cnt;
-	noext = F2FS_OPTION(sbi).noextensions;
-	noext_cnt = F2FS_OPTION(sbi).nocompress_ext_cnt;
+	int index = 0, no_index = 0;
 
 	if (!noext_cnt)
 		return 0;
 
 	for (no_index = 0; no_index < noext_cnt; no_index++) {
+		if (strlen(noext[no_index]) == 0)
+			continue;
 		if (!strcasecmp("*", noext[no_index])) {
-			f2fs_info(sbi, "Don't allow the nocompress extension specifies all files");
+			f2fs_info(NULL, "Don't allow the nocompress extension specifies all files");
 			return -EINVAL;
 		}
 		for (index = 0; index < ext_cnt; index++) {
+			if (strlen(ext[index]) == 0)
+				continue;
 			if (!strcasecmp(ext[index], noext[no_index])) {
-				f2fs_info(sbi, "Don't allow the same extension %s appear in both compress and nocompress extension",
+				f2fs_info(NULL, "Don't allow the same extension %s appear in both compress and nocompress extension",
 						ext[index]);
 				return -EINVAL;
 			}
@@ -893,15 +899,12 @@ static int f2fs_set_zstd_level(struct f2fs_fs_context *ctx, const char *str)
 static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 {
 	struct f2fs_fs_context *ctx = fc->fs_private;
-	struct f2fs_sb_info *sbi = fc->s_fs_info;
-	struct super_block *sb = sbi->sb;
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 	unsigned char (*ext)[F2FS_EXTENSION_LEN];
 	unsigned char (*noext)[F2FS_EXTENSION_LEN];
 	int ext_cnt, noext_cnt;
 #endif
 	struct fs_parse_result result;
-	int is_remount;
 	char *name;
 	kuid_t uid;
 	kgid_t gid;
@@ -911,8 +914,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 	if (token < 0)
 		return token;
 
-	is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;
-
 	switch (token) {
 	case Opt_gc_background:
 		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
@@ -936,23 +937,12 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		ctx_set_opt(ctx, F2FS_MOUNT_DISABLE_ROLL_FORWARD);
 		return 0;
 	case Opt_norecovery:
-		/* this option mounts f2fs with ro */
 		ctx_set_opt(ctx, F2FS_MOUNT_NORECOVERY);
-		if (!f2fs_readonly(sb))
-			return -EINVAL;
 		return 0;
 	case Opt_discard:
-		if (!f2fs_hw_support_discard(sbi)) {
-			f2fs_warn(NULL, "device does not support discard");
-			return 0;
-		}
 		ctx_set_opt(ctx, F2FS_MOUNT_DISCARD);
 		return 0;
 	case Opt_nodiscard:
-		if (f2fs_hw_should_discard(sbi)) {
-			f2fs_warn(NULL, "discard is required for zoned block devices");
-			return -EINVAL;
-		}
 		ctx_clear_opt(ctx, F2FS_MOUNT_DISCARD);
 		return 0;
 	case Opt_noheap:
@@ -973,6 +963,12 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		ctx_clear_opt(ctx, F2FS_MOUNT_INLINE_XATTR);
 		return 0;
 	case Opt_inline_xattr_size:
+		if (result.int_32 < MIN_INLINE_XATTR_SIZE ||
+			result.int_32 > MAX_INLINE_XATTR_SIZE) {
+			f2fs_err(NULL, "inline xattr size is out of range: %lu ~ %lu",
+				MIN_INLINE_XATTR_SIZE, MAX_INLINE_XATTR_SIZE);
+			return -EINVAL;
+		}
 		ctx_set_opt(ctx, F2FS_MOUNT_INLINE_XATTR_SIZE);
 		F2FS_CTX_INFO(ctx).inline_xattr_size = result.int_32;
 		ctx->spec_mask |= F2FS_SPEC_inline_xattr_size;
@@ -1053,14 +1049,9 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		ctx_set_opt(ctx, F2FS_MOUNT_DATA_FLUSH);
 		return 0;
 	case Opt_reserve_root:
-		if (test_opt(sbi, RESERVE_ROOT)) {
-			f2fs_info(NULL, "Preserve previous reserve_root=%u",
-				  F2FS_OPTION(sbi).root_reserved_blocks);
-		} else {
-			ctx_set_opt(ctx, F2FS_MOUNT_RESERVE_ROOT);
-			F2FS_CTX_INFO(ctx).root_reserved_blocks = result.uint_32;
-			ctx->spec_mask |= F2FS_SPEC_reserve_root;
-		}
+		ctx_set_opt(ctx, F2FS_MOUNT_RESERVE_ROOT);
+		F2FS_CTX_INFO(ctx).root_reserved_blocks = result.uint_32;
+		ctx->spec_mask |= F2FS_SPEC_reserve_root;
 		return 0;
 	case Opt_resuid:
 		uid = make_kuid(current_user_ns(), result.uint_32);
@@ -1102,8 +1093,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		return 0;
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 	case Opt_fault_injection:
-		if (f2fs_build_fault_attr(sbi, result.int_32,
-				F2FS_ALL_FAULT_TYPE))
+		if (result.int_32 > INT_MAX)
 			return -EINVAL;
 		F2FS_CTX_INFO(ctx).fault_info.inject_rate = result.int_32;
 		ctx->spec_mask |= F2FS_SPEC_fault_injection;
@@ -1111,7 +1101,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		return 0;
 
 	case Opt_fault_type:
-		if (f2fs_build_fault_attr(sbi, 0, result.uint_32))
+		if (result.uint_32 > BIT(FAULT_MAX))
 			return -EINVAL;
 		F2FS_CTX_INFO(ctx).fault_info.inject_type = result.uint_32;
 		ctx->spec_mask |= F2FS_SPEC_fault_type;
@@ -1286,10 +1276,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		return 0;
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 	case Opt_compress_algorithm:
-		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(NULL, "Image doesn't support compression");
-			return 0;
-		}
 		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
 		if (!name)
 			return -ENOMEM;
@@ -1346,10 +1332,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		kfree(name);
 		return 0;
 	case Opt_compress_log_size:
-		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(NULL, "Image doesn't support compression");
-			return 0;
-		}
 		if (result.int_32 < MIN_COMPRESS_LOG_SIZE ||
 			result.int_32 > MAX_COMPRESS_LOG_SIZE) {
 			f2fs_err(NULL,
@@ -1360,10 +1342,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		ctx->spec_mask |= F2FS_SPEC_compress_log_size;
 		return 0;
 	case Opt_compress_extension:
-		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(NULL, "Image doesn't support compression");
-			break;
-		}
 		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
 		if (!name)
 			return -ENOMEM;
@@ -1394,10 +1372,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		kfree(name);
 		return 0;
 	case Opt_nocompress_extension:
-		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(NULL, "Image doesn't support compression");
-			return 0;
-		}
 		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
 		if (!name)
 			return -ENOMEM;
@@ -1428,18 +1402,10 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		kfree(name);
 		return 0;
 	case Opt_compress_chksum:
-		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(NULL, "Image doesn't support compression");
-			return 0;
-		}
 		F2FS_CTX_INFO(ctx).compress_chksum = true;
 		ctx->spec_mask |= F2FS_SPEC_compress_chksum;
 		return 0;
 	case Opt_compress_mode:
-		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(NULL, "Image doesn't support compression");
-			return 0;
-		}
 		name = kmemdup_nul(param->string, param->size, GFP_KERNEL);
 		if (!name)
 			return -ENOMEM;
@@ -1455,10 +1421,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		kfree(name);
 		return 0;
 	case Opt_compress_cache:
-		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(NULL, "Image doesn't support compression");
-			return 0;
-		}
 		ctx_set_opt(ctx, F2FS_MOUNT_COMPRESS_CACHE);
 		return 0;
 #else
@@ -1550,22 +1512,15 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 	return 0;
 }
 
-static int parse_options(struct super_block *sb, char *options, bool is_remount)
+static int parse_options(struct fs_context *fc, char *options)
 {
-	struct f2fs_sb_info *sbi = F2FS_SB(sb);
 	struct fs_parameter param;
-	struct fs_context fc;
 	char *key;
 	int ret;
 
 	if (!options)
 		goto default_check;
 
-	memset(&fc, 0, sizeof(fc));
-	fc.s_fs_info = sbi;
-	if (is_remount)
-		fc.purpose = FS_CONTEXT_FOR_RECONFIGURE;
-
 	while ((key = strsep(&options, ",")) != NULL) {
 		if (*key) {
 			size_t v_len = 0;
@@ -1589,7 +1544,7 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
 			param.key = key;
 			param.size = v_len;
 
-			ret = handle_mount_opt(&fc, &param);
+			ret = handle_mount_opt(fc, &param);
 			kfree(param.string);
 			if (ret < 0)
 				return ret;
@@ -1597,33 +1552,324 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
 	}
 
 default_check:
-	return f2fs_validate_options(sb);
+	return f2fs_validate_options(fc);
 }
 
-static int f2fs_validate_options(struct super_block *sb)
+static int f2fs_validate_options(struct fs_context *fc)
 {
-	struct f2fs_sb_info *sbi = F2FS_SB(sb);
+#ifdef CONFIG_QUOTA
+	struct f2fs_fs_context *ctx = fc->fs_private;
+	char *usr_qf_name, *grp_qf_name, *prj_qf_name;
+
+	usr_qf_name = F2FS_CTX_INFO(ctx).s_qf_names[USRQUOTA];
+	grp_qf_name = F2FS_CTX_INFO(ctx).s_qf_names[GRPQUOTA];
+	prj_qf_name = F2FS_CTX_INFO(ctx).s_qf_names[PRJQUOTA];
+
+	if (usr_qf_name || grp_qf_name || prj_qf_name) {
+		if (ctx_test_opt(ctx, F2FS_MOUNT_USRQUOTA) && usr_qf_name)
+			ctx_clear_opt(ctx, F2FS_MOUNT_USRQUOTA);
+
+		if (ctx_test_opt(ctx, F2FS_MOUNT_GRPQUOTA) && grp_qf_name)
+			ctx_clear_opt(ctx, F2FS_MOUNT_GRPQUOTA);
+
+		if (ctx_test_opt(ctx, F2FS_MOUNT_PRJQUOTA) && prj_qf_name)
+			ctx_clear_opt(ctx, F2FS_MOUNT_PRJQUOTA);
+
+		if (ctx_test_opt(ctx, F2FS_MOUNT_USRQUOTA) ||
+			ctx_test_opt(ctx, F2FS_MOUNT_GRPQUOTA) ||
+			ctx_test_opt(ctx, F2FS_MOUNT_PRJQUOTA)) {
+			f2fs_err(NULL, "old and new quota format mixing");
+			return -EINVAL;
+		}
+	}
+#endif
+	return 0;
+}
 
+/*
+ * Check quota settings consistency.
+ */
+static int f2fs_check_quota_consistency(struct fs_context *fc,
+					struct super_block *sb)
+{
 #ifdef CONFIG_QUOTA
-	if (f2fs_check_quota_options(sbi))
+	struct f2fs_fs_context *ctx = fc->fs_private;
+	struct f2fs_sb_info *sbi = F2FS_SB(sb);
+	bool quota_feature = f2fs_sb_has_quota_ino(sbi);
+	bool quota_turnon = sb_any_quota_loaded(sb);
+	char *old_qname, *new_qname;
+	bool usr_qf_name, grp_qf_name, prj_qf_name, usrquota, grpquota, prjquota;
+	int i;
+
+	/*
+	 * We do the test below only for project quotas. 'usrquota' and
+	 * 'grpquota' mount options are allowed even without quota feature
+	 * to support legacy quotas in quota files.
+	 */
+	if (ctx_test_opt(ctx, F2FS_MOUNT_PRJQUOTA) &&
+			!f2fs_sb_has_project_quota(sbi)) {
+		f2fs_err(sbi, "Project quota feature not enabled. Cannot enable project quota enforcement.");
 		return -EINVAL;
+	}
+
+	if (ctx->qname_mask) {
+		for (i = 0; i < MAXQUOTAS; i++) {
+			if (!(ctx->qname_mask & (1 << i)))
+				continue;
+
+			old_qname = F2FS_OPTION(sbi).s_qf_names[i];
+			new_qname = F2FS_CTX_INFO(ctx).s_qf_names[i];
+			if (quota_turnon &&
+				!!old_qname != !!new_qname)
+				goto err_jquota_change;
+
+			if (old_qname) {
+				if (strcmp(old_qname, new_qname) == 0) {
+					ctx->qname_mask &= ~(1 << i);
+					continue;
+				}
+				goto err_jquota_specified;
+			}
+
+			if (quota_feature) {
+				f2fs_info(sbi, "QUOTA feature is enabled, so ignore qf_name");
+				ctx->qname_mask &= ~(1 << i);
+			}
+		}
+	}
+
+	/* Make sure we don't mix old and new quota format */
+	usr_qf_name = F2FS_OPTION(sbi).s_qf_names[USRQUOTA] ||
+			F2FS_CTX_INFO(ctx).s_qf_names[USRQUOTA];
+	grp_qf_name = F2FS_OPTION(sbi).s_qf_names[GRPQUOTA] ||
+			F2FS_CTX_INFO(ctx).s_qf_names[GRPQUOTA];
+	prj_qf_name = F2FS_OPTION(sbi).s_qf_names[PRJQUOTA] ||
+			F2FS_CTX_INFO(ctx).s_qf_names[PRJQUOTA];
+	usrquota = test_opt(sbi, USRQUOTA) ||
+			ctx_test_opt(ctx, F2FS_MOUNT_USRQUOTA);
+	grpquota = test_opt(sbi, GRPQUOTA) ||
+			ctx_test_opt(ctx, F2FS_MOUNT_GRPQUOTA);
+	prjquota = test_opt(sbi, PRJQUOTA) ||
+			ctx_test_opt(ctx, F2FS_MOUNT_PRJQUOTA);
+
+	if (usr_qf_name) {
+		ctx_clear_opt(ctx, F2FS_MOUNT_USRQUOTA);
+		usrquota = false;
+	}
+	if (grp_qf_name) {
+		ctx_clear_opt(ctx, F2FS_MOUNT_GRPQUOTA);
+		grpquota = false;
+	}
+	if (prj_qf_name) {
+		ctx_clear_opt(ctx, F2FS_MOUNT_PRJQUOTA);
+		prjquota = false;
+	}
+	if (usr_qf_name || grp_qf_name || prj_qf_name) {
+		if (grpquota || usrquota || prjquota) {
+			f2fs_err(sbi, "old and new quota format mixing");
+			return -EINVAL;
+		}
+		if (!(ctx->spec_mask & F2FS_SPEC_jqfmt ||
+				F2FS_OPTION(sbi).s_jquota_fmt)) {
+			f2fs_err(sbi, "journaled quota format not specified");
+			return -EINVAL;
+		}
+	}
+	return 0;
+
+err_jquota_change:
+	f2fs_err(sbi, "Cannot change journaled quota options when quota turned on");
+	return -EINVAL;
+err_jquota_specified:
+	f2fs_err(sbi, "%s quota file already specified",
+		 QTYPE2NAME(i));
+	return -EINVAL;
+
 #else
-	if (f2fs_sb_has_quota_ino(sbi) && !f2fs_readonly(sbi->sb)) {
-		f2fs_info(NULL, "Filesystem with quota feature cannot be mounted RDWR without CONFIG_QUOTA");
+	if (f2fs_readonly(sbi->sb))
+		return 0;
+	if (f2fs_sb_has_quota_ino(sbi)) {
+		f2fs_info(sbi, "Filesystem with quota feature cannot be mounted RDWR without CONFIG_QUOTA");
 		return -EINVAL;
 	}
-	if (f2fs_sb_has_project_quota(sbi) && !f2fs_readonly(sbi->sb)) {
-		f2fs_err(NULL, "Filesystem with project quota feature cannot be mounted RDWR without CONFIG_QUOTA");
+	if (f2fs_sb_has_project_quota(sbi)) {
+		f2fs_err(sbi, "Filesystem with project quota feature cannot be mounted RDWR without CONFIG_QUOTA");
 		return -EINVAL;
 	}
+
+	return 0;
 #endif
-#if !IS_ENABLED(CONFIG_UNICODE)
-	if (f2fs_sb_has_casefold(sbi)) {
-		f2fs_err(NULL,
-			"Filesystem with casefold feature cannot be mounted without CONFIG_UNICODE");
+}
+
+static int f2fs_check_test_dummy_encryption(struct fs_context *fc,
+					    struct super_block *sb)
+{
+	struct f2fs_fs_context *ctx = fc->fs_private;
+	struct f2fs_sb_info *sbi = F2FS_SB(sb);
+
+	if (!fscrypt_is_dummy_policy_set(&F2FS_CTX_INFO(ctx).dummy_enc_policy))
+		return 0;
+
+	if (!f2fs_sb_has_encrypt(sbi)) {
+		f2fs_err(sbi, "Encrypt feature is off");
+		return -EINVAL;
+	}
+
+	/*
+	 * This mount option is just for testing, and it's not worthwhile to
+	 * implement the extra complexity (e.g. RCU protection) that would be
+	 * needed to allow it to be set or changed during remount.  We do allow
+	 * it to be specified during remount, but only if there is no change.
+	 */
+	if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
+		if (fscrypt_dummy_policies_equal(&F2FS_OPTION(sbi).dummy_enc_policy,
+				&F2FS_CTX_INFO(ctx).dummy_enc_policy))
+			return 0;
+		f2fs_warn(sbi, "Can't set or change test_dummy_encryption on remount");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static inline bool test_compression_spec(unsigned int mask)
+{
+	return mask & (F2FS_SPEC_compress_algorithm
+			| F2FS_SPEC_compress_log_size
+			| F2FS_SPEC_compress_extension
+			| F2FS_SPEC_nocompress_extension
+			| F2FS_SPEC_compress_chksum
+			| F2FS_SPEC_compress_mode);
+}
+
+static inline void clear_compression_spec(struct f2fs_fs_context *ctx)
+{
+	ctx->spec_mask &= ~(F2FS_SPEC_compress_algorithm
+						| F2FS_SPEC_compress_log_size
+						| F2FS_SPEC_compress_extension
+						| F2FS_SPEC_nocompress_extension
+						| F2FS_SPEC_compress_chksum
+						| F2FS_SPEC_compress_mode);
+}
+
+static int f2fs_check_compression(struct fs_context *fc,
+				  struct super_block *sb)
+{
+#ifdef CONFIG_F2FS_FS_COMPRESSION
+	struct f2fs_fs_context *ctx = fc->fs_private;
+	struct f2fs_sb_info *sbi = F2FS_SB(sb);
+	int i, cnt;
+
+	if (!f2fs_sb_has_compression(sbi)) {
+		if (test_compression_spec(ctx->opt_mask) ||
+			ctx_test_opt(ctx, F2FS_MOUNT_COMPRESS_CACHE))
+			f2fs_info(sbi, "Image doesn't support compression");
+		clear_compression_spec(ctx);
+		ctx->opt_mask &= ~F2FS_MOUNT_COMPRESS_CACHE;
+		return 0;
+	}
+	if (ctx->spec_mask & F2FS_SPEC_compress_extension) {
+		cnt = F2FS_CTX_INFO(ctx).compress_ext_cnt;
+		for (i = 0; i < F2FS_CTX_INFO(ctx).compress_ext_cnt; i++) {
+			if (is_compress_extension_exist(&F2FS_OPTION(sbi),
+					F2FS_CTX_INFO(ctx).extensions[i], true)) {
+				F2FS_CTX_INFO(ctx).extensions[i][0] = '\0';
+				cnt--;
+			}
+		}
+		if (F2FS_OPTION(sbi).compress_ext_cnt + cnt > COMPRESS_EXT_NUM) {
+			f2fs_err(sbi, "invalid extension length/number");
+			return -EINVAL;
+		}
+	}
+	if (ctx->spec_mask & F2FS_SPEC_nocompress_extension) {
+		cnt = F2FS_CTX_INFO(ctx).nocompress_ext_cnt;
+		for (i = 0; i < F2FS_CTX_INFO(ctx).nocompress_ext_cnt; i++) {
+			if (is_compress_extension_exist(&F2FS_OPTION(sbi),
+					F2FS_CTX_INFO(ctx).noextensions[i], false)) {
+				F2FS_CTX_INFO(ctx).noextensions[i][0] = '\0';
+				cnt--;
+			}
+		}
+		if (F2FS_OPTION(sbi).nocompress_ext_cnt + cnt > COMPRESS_EXT_NUM) {
+			f2fs_err(sbi, "invalid noextension length/number");
+			return -EINVAL;
+		}
+	}
+
+	if (f2fs_test_compress_extension(F2FS_CTX_INFO(ctx).noextensions,
+				F2FS_CTX_INFO(ctx).nocompress_ext_cnt,
+				F2FS_CTX_INFO(ctx).extensions,
+				F2FS_CTX_INFO(ctx).compress_ext_cnt)) {
+		f2fs_err(sbi, "invalid compress or nocompress extension");
+		return -EINVAL;
+	}
+	if (f2fs_test_compress_extension(F2FS_CTX_INFO(ctx).noextensions,
+				F2FS_CTX_INFO(ctx).nocompress_ext_cnt,
+				F2FS_OPTION(sbi).extensions,
+				F2FS_OPTION(sbi).compress_ext_cnt)) {
+		f2fs_err(sbi, "invalid compress or nocompress extension");
+		return -EINVAL;
+	}
+	if (f2fs_test_compress_extension(F2FS_OPTION(sbi).noextensions,
+				F2FS_OPTION(sbi).nocompress_ext_cnt,
+				F2FS_CTX_INFO(ctx).extensions,
+				F2FS_CTX_INFO(ctx).compress_ext_cnt)) {
+		f2fs_err(sbi, "invalid compress or nocompress extension");
 		return -EINVAL;
 	}
 #endif
+	return 0;
+}
+
+static int f2fs_check_opt_consistency(struct fs_context *fc,
+				      struct super_block *sb)
+{
+	struct f2fs_fs_context *ctx = fc->fs_private;
+	struct f2fs_sb_info *sbi = F2FS_SB(sb);
+	int err;
+
+	if (ctx_test_opt(ctx, F2FS_MOUNT_NORECOVERY) && !f2fs_readonly(sb))
+		return -EINVAL;
+
+	if (f2fs_hw_should_discard(sbi) && (ctx->opt_mask & F2FS_MOUNT_DISCARD)
+				&& !ctx_test_opt(ctx, F2FS_MOUNT_DISCARD)) {
+		f2fs_warn(sbi, "discard is required for zoned block devices");
+		return -EINVAL;
+	}
+
+	if (!f2fs_hw_support_discard(sbi) && (ctx->opt_mask & F2FS_MOUNT_DISCARD)
+				&& ctx_test_opt(ctx, F2FS_MOUNT_DISCARD)) {
+		f2fs_warn(sbi, "device does not support discard");
+		ctx_clear_opt(ctx, F2FS_MOUNT_DISCARD);
+		ctx->opt_mask &= ~F2FS_MOUNT_DISCARD;
+	}
+
+	if (test_opt(sbi, RESERVE_ROOT) && (ctx->opt_mask & F2FS_MOUNT_RESERVE_ROOT)
+				&& ctx_test_opt(ctx, F2FS_MOUNT_RESERVE_ROOT)) {
+		f2fs_info(sbi, "Preserve previous reserve_root=%u",
+			F2FS_OPTION(sbi).root_reserved_blocks);
+		ctx_clear_opt(ctx, F2FS_MOUNT_RESERVE_ROOT);
+		ctx->opt_mask &= ~F2FS_MOUNT_RESERVE_ROOT;
+	}
+
+	err = f2fs_check_test_dummy_encryption(fc, sb);
+	if (err)
+		return err;
+
+	err = f2fs_check_compression(fc, sb);
+	if (err)
+		return err;
+
+	err = f2fs_check_quota_consistency(fc, sb);
+	if (err)
+		return err;
+
+	if (!IS_ENABLED(CONFIG_UNICODE) && f2fs_sb_has_casefold(sbi)) {
+		f2fs_err(sbi,
+			"Filesystem with casefold feature cannot be mounted without CONFIG_UNICODE");
+		return -EINVAL;
+	}
+
 	/*
 	 * The BLKZONED feature indicates that the drive was formatted with
 	 * zone alignment optimization. This is optional for host-aware
@@ -1631,71 +1877,212 @@ static int f2fs_validate_options(struct super_block *sb)
 	 */
 	if (f2fs_sb_has_blkzoned(sbi)) {
 #ifdef CONFIG_BLK_DEV_ZONED
-		if (F2FS_OPTION(sbi).discard_unit !=
-						DISCARD_UNIT_SECTION) {
-			f2fs_info(NULL, "Zoned block device doesn't need small discard, set discard_unit=section by default");
-			F2FS_OPTION(sbi).discard_unit =
-					DISCARD_UNIT_SECTION;
+		if ((ctx->spec_mask & F2FS_SPEC_discard_unit) &&
+		F2FS_CTX_INFO(ctx).discard_unit != DISCARD_UNIT_SECTION) {
+			f2fs_info(sbi, "Zoned block device doesn't need small discard, set discard_unit=section by default");
+			F2FS_CTX_INFO(ctx).discard_unit = DISCARD_UNIT_SECTION;
 		}
 
-		if (F2FS_OPTION(sbi).fs_mode != FS_MODE_LFS) {
-			f2fs_info(NULL, "Only lfs mode is allowed with zoned block device feature");
+		if ((ctx->spec_mask & F2FS_SPEC_mode) &&
+		F2FS_CTX_INFO(ctx).fs_mode != FS_MODE_LFS) {
+			f2fs_info(sbi, "Only lfs mode is allowed with zoned block device feature");
 			return -EINVAL;
 		}
 #else
-			f2fs_err(NULL, "Zoned block device support is not enabled");
-			return -EINVAL;
-#endif
-		}
-
-#ifdef CONFIG_F2FS_FS_COMPRESSION
-	if (f2fs_test_compress_extension(sbi)) {
-		f2fs_err(NULL, "invalid compress or nocompress extension");
+		f2fs_err(sbi, "Zoned block device support is not enabled");
 		return -EINVAL;
-	}
 #endif
+	}
 
-	if (test_opt(sbi, INLINE_XATTR_SIZE)) {
-		int min_size, max_size;
-
+	if (ctx_test_opt(ctx, F2FS_MOUNT_INLINE_XATTR_SIZE)) {
 		if (!f2fs_sb_has_extra_attr(sbi) ||
 			!f2fs_sb_has_flexible_inline_xattr(sbi)) {
-			f2fs_err(NULL, "extra_attr or flexible_inline_xattr feature is off");
+			f2fs_err(sbi, "extra_attr or flexible_inline_xattr feature is off");
 			return -EINVAL;
 		}
-		if (!test_opt(sbi, INLINE_XATTR)) {
-			f2fs_err(NULL, "inline_xattr_size option should be set with inline_xattr option");
-			return -EINVAL;
-		}
-
-		min_size = MIN_INLINE_XATTR_SIZE;
-		max_size = MAX_INLINE_XATTR_SIZE;
-
-		if (F2FS_OPTION(sbi).inline_xattr_size < min_size ||
-				F2FS_OPTION(sbi).inline_xattr_size > max_size) {
-			f2fs_err(NULL, "inline xattr size is out of range: %d ~ %d",
-				 min_size, max_size);
+		if (!ctx_test_opt(ctx, F2FS_MOUNT_INLINE_XATTR)) {
+			f2fs_err(sbi, "inline_xattr_size option should be set with inline_xattr option");
 			return -EINVAL;
 		}
 	}
 
-	if (test_opt(sbi, ATGC) && f2fs_lfs_mode(sbi)) {
-		f2fs_err(NULL, "LFS is not compatible with ATGC");
+	if (ctx_test_opt(ctx, F2FS_MOUNT_ATGC) && f2fs_lfs_mode(sbi)) {
+		f2fs_err(sbi, "LFS is not compatible with ATGC");
 		return -EINVAL;
 	}
 
-	if (f2fs_is_readonly(sbi) && test_opt(sbi, FLUSH_MERGE)) {
-		f2fs_err(NULL, "FLUSH_MERGE not compatible with readonly mode");
+	if (f2fs_is_readonly(sbi) && ctx_test_opt(ctx, F2FS_MOUNT_FLUSH_MERGE)) {
+		f2fs_err(sbi, "FLUSH_MERGE not compatible with readonly mode");
 		return -EINVAL;
 	}
 
 	if (f2fs_sb_has_readonly(sbi) && !f2fs_readonly(sbi->sb)) {
-		f2fs_err(NULL, "Allow to mount readonly mode only");
+		f2fs_err(sbi, "Allow to mount readonly mode only");
 		return -EROFS;
 	}
 	return 0;
 }
 
+static void f2fs_apply_quota_options(struct fs_context *fc,
+				     struct super_block *sb)
+{
+#ifdef CONFIG_QUOTA
+	struct f2fs_fs_context *ctx = fc->fs_private;
+	struct f2fs_sb_info *sbi = F2FS_SB(sb);
+	bool quota_feature = f2fs_sb_has_quota_ino(sbi);
+	char *qname;
+	int i;
+
+	if (quota_feature)
+		return;
+
+	for (i = 0; i < MAXQUOTAS; i++) {
+		if (!(ctx->qname_mask & (1 << i)))
+			continue;
+
+		qname = F2FS_CTX_INFO(ctx).s_qf_names[i];
+		if (qname)
+			set_opt(sbi, QUOTA);
+		F2FS_OPTION(sbi).s_qf_names[i] = qname;
+		F2FS_CTX_INFO(ctx).s_qf_names[i] = NULL;
+	}
+
+	if (ctx->spec_mask & F2FS_SPEC_jqfmt)
+		F2FS_OPTION(sbi).s_jquota_fmt = F2FS_CTX_INFO(ctx).s_jquota_fmt;
+
+	if (quota_feature && F2FS_OPTION(sbi).s_jquota_fmt) {
+		f2fs_info(sbi, "QUOTA feature is enabled, so ignore jquota_fmt");
+		F2FS_OPTION(sbi).s_jquota_fmt = 0;
+	}
+#endif
+}
+
+static void f2fs_apply_test_dummy_encryption(struct fs_context *fc,
+					     struct super_block *sb)
+{
+	struct f2fs_fs_context *ctx = fc->fs_private;
+	struct f2fs_sb_info *sbi = F2FS_SB(sb);
+
+	if (!fscrypt_is_dummy_policy_set(&F2FS_CTX_INFO(ctx).dummy_enc_policy) ||
+		/* if already set, it was already verified to be the same */
+		fscrypt_is_dummy_policy_set(&F2FS_OPTION(sbi).dummy_enc_policy))
+		return;
+	F2FS_OPTION(sbi).dummy_enc_policy = F2FS_CTX_INFO(ctx).dummy_enc_policy;
+	memset(&F2FS_CTX_INFO(ctx).dummy_enc_policy, 0,
+		sizeof(F2FS_CTX_INFO(ctx).dummy_enc_policy));
+	f2fs_warn(sbi, "Test dummy encryption mode enabled");
+}
+
+static void f2fs_apply_compression(struct fs_context *fc,
+				   struct super_block *sb)
+{
+#ifdef CONFIG_F2FS_FS_COMPRESSION
+	struct f2fs_fs_context *ctx = fc->fs_private;
+	struct f2fs_sb_info *sbi = F2FS_SB(sb);
+	unsigned char (*ctx_ext)[F2FS_EXTENSION_LEN];
+	unsigned char (*sbi_ext)[F2FS_EXTENSION_LEN];
+	int ctx_cnt, sbi_cnt, i;
+
+	if (ctx->spec_mask & F2FS_SPEC_compress_level)
+		F2FS_OPTION(sbi).compress_level =
+					F2FS_CTX_INFO(ctx).compress_level;
+	if (ctx->spec_mask & F2FS_SPEC_compress_algorithm)
+		F2FS_OPTION(sbi).compress_algorithm =
+					F2FS_CTX_INFO(ctx).compress_algorithm;
+	if (ctx->spec_mask & F2FS_SPEC_compress_log_size)
+		F2FS_OPTION(sbi).compress_log_size =
+					F2FS_CTX_INFO(ctx).compress_log_size;
+	if (ctx->spec_mask & F2FS_SPEC_compress_chksum)
+		F2FS_OPTION(sbi).compress_chksum =
+					F2FS_CTX_INFO(ctx).compress_chksum;
+	if (ctx->spec_mask & F2FS_SPEC_compress_mode)
+		F2FS_OPTION(sbi).compress_mode =
+					F2FS_CTX_INFO(ctx).compress_mode;
+	if (ctx->spec_mask & F2FS_SPEC_compress_extension) {
+		ctx_ext = F2FS_CTX_INFO(ctx).extensions;
+		ctx_cnt = F2FS_CTX_INFO(ctx).compress_ext_cnt;
+		sbi_ext = F2FS_OPTION(sbi).extensions;
+		sbi_cnt = F2FS_OPTION(sbi).compress_ext_cnt;
+		for (i = 0; i < ctx_cnt; i++) {
+			if (strlen(ctx_ext[i]) == 0)
+				continue;
+			strscpy(sbi_ext[sbi_cnt], ctx_ext[i]);
+			sbi_cnt++;
+		}
+		F2FS_OPTION(sbi).compress_ext_cnt = sbi_cnt;
+	}
+	if (ctx->spec_mask & F2FS_SPEC_nocompress_extension) {
+		ctx_ext = F2FS_CTX_INFO(ctx).noextensions;
+		ctx_cnt = F2FS_CTX_INFO(ctx).nocompress_ext_cnt;
+		sbi_ext = F2FS_OPTION(sbi).noextensions;
+		sbi_cnt = F2FS_OPTION(sbi).nocompress_ext_cnt;
+		for (i = 0; i < ctx_cnt; i++) {
+			if (strlen(ctx_ext[i]) == 0)
+				continue;
+			strscpy(sbi_ext[sbi_cnt], ctx_ext[i]);
+			sbi_cnt++;
+		}
+		F2FS_OPTION(sbi).nocompress_ext_cnt = sbi_cnt;
+	}
+#endif
+}
+
+
+static void f2fs_apply_options(struct fs_context *fc, struct super_block *sb)
+{
+	struct f2fs_fs_context *ctx = fc->fs_private;
+	struct f2fs_sb_info *sbi = F2FS_SB(sb);
+
+	F2FS_OPTION(sbi).opt &= ~ctx->opt_mask;
+	F2FS_OPTION(sbi).opt |= F2FS_CTX_INFO(ctx).opt;
+	sb->s_flags &= ~ctx->sflags_mask;
+	sb->s_flags |= ctx->sflags;
+
+	if (ctx->spec_mask & F2FS_SPEC_background_gc)
+		F2FS_OPTION(sbi).bggc_mode = F2FS_CTX_INFO(ctx).bggc_mode;
+	if (ctx->spec_mask & F2FS_SPEC_inline_xattr_size)
+		F2FS_OPTION(sbi).inline_xattr_size =
+					F2FS_CTX_INFO(ctx).inline_xattr_size;
+	if (ctx->spec_mask & F2FS_SPEC_active_logs)
+		F2FS_OPTION(sbi).active_logs = F2FS_CTX_INFO(ctx).active_logs;
+	if (ctx->spec_mask & F2FS_SPEC_reserve_root)
+		F2FS_OPTION(sbi).root_reserved_blocks =
+					F2FS_CTX_INFO(ctx).root_reserved_blocks;
+	if (ctx->spec_mask & F2FS_SPEC_resgid)
+		F2FS_OPTION(sbi).s_resgid = F2FS_CTX_INFO(ctx).s_resgid;
+	if (ctx->spec_mask & F2FS_SPEC_resuid)
+		F2FS_OPTION(sbi).s_resuid = F2FS_CTX_INFO(ctx).s_resuid;
+	if (ctx->spec_mask & F2FS_SPEC_mode)
+		F2FS_OPTION(sbi).fs_mode = F2FS_CTX_INFO(ctx).fs_mode;
+#ifdef CONFIG_F2FS_FAULT_INJECTION
+	if (ctx->spec_mask & F2FS_SPEC_fault_injection)
+		(void)f2fs_build_fault_attr(sbi,
+		F2FS_CTX_INFO(ctx).fault_info.inject_rate, F2FS_ALL_FAULT_TYPE);
+	if (ctx->spec_mask & F2FS_SPEC_fault_type)
+		(void)f2fs_build_fault_attr(sbi,
+			0, F2FS_CTX_INFO(ctx).fault_info.inject_type);
+#endif
+	if (ctx->spec_mask & F2FS_SPEC_alloc_mode)
+		F2FS_OPTION(sbi).alloc_mode = F2FS_CTX_INFO(ctx).alloc_mode;
+	if (ctx->spec_mask & F2FS_SPEC_fsync_mode)
+		F2FS_OPTION(sbi).fsync_mode = F2FS_CTX_INFO(ctx).fsync_mode;
+	if (ctx->spec_mask & F2FS_SPEC_checkpoint_disable_cap)
+		F2FS_OPTION(sbi).unusable_cap = F2FS_CTX_INFO(ctx).unusable_cap;
+	if (ctx->spec_mask & F2FS_SPEC_checkpoint_disable_cap_perc)
+		F2FS_OPTION(sbi).unusable_cap_perc =
+					F2FS_CTX_INFO(ctx).unusable_cap_perc;
+	if (ctx->spec_mask & F2FS_SPEC_discard_unit)
+		F2FS_OPTION(sbi).discard_unit = F2FS_CTX_INFO(ctx).discard_unit;
+	if (ctx->spec_mask & F2FS_SPEC_memory_mode)
+		F2FS_OPTION(sbi).memory_mode = F2FS_CTX_INFO(ctx).memory_mode;
+	if (ctx->spec_mask & F2FS_SPEC_errors)
+		F2FS_OPTION(sbi).errors = F2FS_CTX_INFO(ctx).errors;
+
+	f2fs_apply_compression(fc, sb);
+	f2fs_apply_test_dummy_encryption(fc, sb);
+	f2fs_apply_quota_options(fc, sb);
+}
+
 static struct inode *f2fs_alloc_inode(struct super_block *sb)
 {
 	struct f2fs_inode_info *fi;
@@ -2570,6 +2957,8 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 {
 	struct f2fs_sb_info *sbi = F2FS_SB(sb);
 	struct f2fs_mount_info org_mount_opt;
+	struct f2fs_fs_context ctx;
+	struct fs_context fc;
 	unsigned long old_sb_flags;
 	int err;
 	bool need_restart_gc = false, need_stop_gc = false;
@@ -2623,11 +3012,22 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 
 	default_options(sbi, true);
 
+	memset(&fc, 0, sizeof(fc));
+	memset(&ctx, 0, sizeof(ctx));
+	fc.fs_private = &ctx;
+	fc.purpose = FS_CONTEXT_FOR_RECONFIGURE;
+
 	/* parse mount options */
-	err = parse_options(sb, data, true);
+	err = parse_options(&fc, data);
 	if (err)
 		goto restore_opts;
 
+	err = f2fs_check_opt_consistency(&fc, sb);
+	if (err < 0)
+		goto restore_opts;
+
+	f2fs_apply_options(&fc, sb);
+
 #ifdef CONFIG_BLK_DEV_ZONED
 	if (f2fs_sb_has_blkzoned(sbi) &&
 		sbi->max_open_zones < F2FS_OPTION(sbi).active_logs) {
@@ -4651,6 +5051,8 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 {
 	struct f2fs_sb_info *sbi;
 	struct f2fs_super_block *raw_super;
+	struct f2fs_fs_context ctx;
+	struct fs_context fc;
 	struct inode *root;
 	int err;
 	bool skip_recovery = false, need_fsck = false;
@@ -4667,6 +5069,9 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 	raw_super = NULL;
 	valid_super_block = -1;
 	recovery = 0;
+	memset(&fc, 0, sizeof(fc));
+	memset(&ctx, 0, sizeof(ctx));
+	fc.fs_private = &ctx;
 
 	/* allocate memory for f2fs-specific super block info */
 	sbi = kzalloc(sizeof(struct f2fs_sb_info), GFP_KERNEL);
@@ -4733,10 +5138,16 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 		goto free_sb_buf;
 	}
 
-	err = parse_options(sb, options, false);
+	err = parse_options(&fc, options);
 	if (err)
 		goto free_options;
 
+	err = f2fs_check_opt_consistency(&fc, sb);
+	if (err < 0)
+		goto free_options;
+
+	f2fs_apply_options(&fc, sb);
+
 	sb->s_maxbytes = max_file_blocks(NULL) <<
 				le32_to_cpu(raw_super->log_blocksize);
 	sb->s_max_links = F2FS_LINK_MAX;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 7/9] f2fs: introduce fs_context_operation structure
  2024-08-14  2:39 [PATCH 0/9] f2fs: new mount API conversion Hongbo Li
                   ` (5 preceding siblings ...)
  2024-08-14  2:39 ` [PATCH 6/9] f2fs: separate the options parsing and options checking Hongbo Li
@ 2024-08-14  2:39 ` Hongbo Li
  2024-08-14  2:39 ` [PATCH 8/9] f2fs: switch to the new mount api Hongbo Li
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Hongbo Li @ 2024-08-14  2:39 UTC (permalink / raw)
  To: jaegeuk, chao
  Cc: linux-f2fs-devel, brauner, lczerner, linux-fsdevel, lihongbo22

The handle_mount_opt() helper is used to parse mount parameters,
and so we can rename this function to f2fs_parse_param() and set
it as .param_param in fs_context_operations.

Signed-off-by: Hongbo Li <lihongbo22@huawei.com>
---
 fs/f2fs/super.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index f658d75f4bb7..8c43fbe092ff 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -896,7 +896,7 @@ static int f2fs_set_zstd_level(struct f2fs_fs_context *ctx, const char *str)
 #endif
 #endif
 
-static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
+static int f2fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
 {
 	struct f2fs_fs_context *ctx = fc->fs_private;
 #ifdef CONFIG_F2FS_FS_COMPRESSION
@@ -1544,7 +1544,7 @@ static int parse_options(struct fs_context *fc, char *options)
 			param.key = key;
 			param.size = v_len;
 
-			ret = handle_mount_opt(fc, &param);
+			ret = f2fs_parse_param(fc, &param);
 			kfree(param.string);
 			if (ret < 0)
 				return ret;
@@ -5577,6 +5577,10 @@ static struct dentry *f2fs_mount(struct file_system_type *fs_type, int flags,
 	return mount_bdev(fs_type, flags, dev_name, data, f2fs_fill_super);
 }
 
+static const struct fs_context_operations f2fs_context_ops = {
+	.parse_param	= f2fs_parse_param,
+};
+
 static void kill_f2fs_super(struct super_block *sb)
 {
 	struct f2fs_sb_info *sbi = F2FS_SB(sb);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 8/9] f2fs: switch to the new mount api
  2024-08-14  2:39 [PATCH 0/9] f2fs: new mount API conversion Hongbo Li
                   ` (6 preceding siblings ...)
  2024-08-14  2:39 ` [PATCH 7/9] f2fs: introduce fs_context_operation structure Hongbo Li
@ 2024-08-14  2:39 ` Hongbo Li
  2024-08-14  2:39 ` [PATCH 9/9] f2fs: remove unused structure and functions Hongbo Li
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Hongbo Li @ 2024-08-14  2:39 UTC (permalink / raw)
  To: jaegeuk, chao
  Cc: linux-f2fs-devel, brauner, lczerner, linux-fsdevel, lihongbo22

The new mount api will excute .parse_param, .init_fs_context, .get_tree
and will call .remount if remount happened. So we add the necessary
functions for the fs_context_operations. If .init_fs_context is added,
the old .mount should remove.

See Documentation/filesystems/mount_api.rst for more information.

Signed-off-by: Hongbo Li <lihongbo22@huawei.com>
---
 fs/f2fs/super.c | 125 ++++++++++++++++++++++++++++--------------------
 1 file changed, 72 insertions(+), 53 deletions(-)

diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 8c43fbe092ff..86acb7354d95 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -2953,13 +2953,12 @@ static void f2fs_enable_checkpoint(struct f2fs_sb_info *sbi)
 	f2fs_flush_ckpt_thread(sbi);
 }
 
-static int f2fs_remount(struct super_block *sb, int *flags, char *data)
+static int __f2fs_remount(struct fs_context *fc, struct super_block *sb)
 {
 	struct f2fs_sb_info *sbi = F2FS_SB(sb);
 	struct f2fs_mount_info org_mount_opt;
-	struct f2fs_fs_context ctx;
-	struct fs_context fc;
 	unsigned long old_sb_flags;
+	unsigned int flags = fc->sb_flags;
 	int err;
 	bool need_restart_gc = false, need_stop_gc = false;
 	bool need_restart_flush = false, need_stop_flush = false;
@@ -2976,6 +2975,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 	int i, j;
 #endif
 
+	/* check the mount options legality in ctx first */
+	err = f2fs_validate_options(fc);
+	if (err)
+		return -EINVAL;
+
 	/*
 	 * Save the old mount options in case we
 	 * need to restore them.
@@ -3002,7 +3006,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 #endif
 
 	/* recover superblocks we couldn't write due to previous RO mount */
-	if (!(*flags & SB_RDONLY) && is_sbi_flag_set(sbi, SBI_NEED_SB_WRITE)) {
+	if (!(flags & SB_RDONLY) && is_sbi_flag_set(sbi, SBI_NEED_SB_WRITE)) {
 		err = f2fs_commit_super(sbi, false);
 		f2fs_info(sbi, "Try to recover all the superblocks, ret: %d",
 			  err);
@@ -3012,21 +3016,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 
 	default_options(sbi, true);
 
-	memset(&fc, 0, sizeof(fc));
-	memset(&ctx, 0, sizeof(ctx));
-	fc.fs_private = &ctx;
-	fc.purpose = FS_CONTEXT_FOR_RECONFIGURE;
-
-	/* parse mount options */
-	err = parse_options(&fc, data);
-	if (err)
-		goto restore_opts;
-
-	err = f2fs_check_opt_consistency(&fc, sb);
+	err = f2fs_check_opt_consistency(fc, sb);
 	if (err < 0)
 		goto restore_opts;
 
-	f2fs_apply_options(&fc, sb);
+	f2fs_apply_options(fc, sb);
 
 #ifdef CONFIG_BLK_DEV_ZONED
 	if (f2fs_sb_has_blkzoned(sbi) &&
@@ -3046,20 +3040,20 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 	 * Previous and new state of filesystem is RO,
 	 * so skip checking GC and FLUSH_MERGE conditions.
 	 */
-	if (f2fs_readonly(sb) && (*flags & SB_RDONLY))
+	if (f2fs_readonly(sb) && (flags & SB_RDONLY))
 		goto skip;
 
-	if (f2fs_dev_is_readonly(sbi) && !(*flags & SB_RDONLY)) {
+	if (f2fs_dev_is_readonly(sbi) && !(flags & SB_RDONLY)) {
 		err = -EROFS;
 		goto restore_opts;
 	}
 
 #ifdef CONFIG_QUOTA
-	if (!f2fs_readonly(sb) && (*flags & SB_RDONLY)) {
+	if (!f2fs_readonly(sb) && (flags & SB_RDONLY)) {
 		err = dquot_suspend(sb, -1);
 		if (err < 0)
 			goto restore_opts;
-	} else if (f2fs_readonly(sb) && !(*flags & SB_RDONLY)) {
+	} else if (f2fs_readonly(sb) && !(flags & SB_RDONLY)) {
 		/* dquot_resume needs RW */
 		sb->s_flags &= ~SB_RDONLY;
 		if (sb_any_quota_suspended(sb)) {
@@ -3109,7 +3103,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 		goto restore_opts;
 	}
 
-	if ((*flags & SB_RDONLY) && test_opt(sbi, DISABLE_CHECKPOINT)) {
+	if ((flags & SB_RDONLY) && test_opt(sbi, DISABLE_CHECKPOINT)) {
 		err = -EINVAL;
 		f2fs_warn(sbi, "disabling checkpoint not compatible with read-only");
 		goto restore_opts;
@@ -3120,7 +3114,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 	 * or if background_gc = off is passed in mount
 	 * option. Also sync the filesystem.
 	 */
-	if ((*flags & SB_RDONLY) ||
+	if ((flags & SB_RDONLY) ||
 			(F2FS_OPTION(sbi).bggc_mode == BGGC_MODE_OFF &&
 			!test_opt(sbi, GC_MERGE))) {
 		if (sbi->gc_thread) {
@@ -3134,7 +3128,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 		need_stop_gc = true;
 	}
 
-	if (*flags & SB_RDONLY) {
+	if (flags & SB_RDONLY) {
 		sync_inodes_sb(sb);
 
 		set_sbi_flag(sbi, SBI_IS_DIRTY);
@@ -3147,7 +3141,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 	 * We stop issue flush thread if FS is mounted as RO
 	 * or if flush_merge is not passed in mount option.
 	 */
-	if ((*flags & SB_RDONLY) || !test_opt(sbi, FLUSH_MERGE)) {
+	if ((flags & SB_RDONLY) || !test_opt(sbi, FLUSH_MERGE)) {
 		clear_opt(sbi, FLUSH_MERGE);
 		f2fs_destroy_flush_cmd_control(sbi, false);
 		need_restart_flush = true;
@@ -3188,7 +3182,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 	 * triggered while remount and we need to take care of it before
 	 * returning from remount.
 	 */
-	if ((*flags & SB_RDONLY) || test_opt(sbi, DISABLE_CHECKPOINT) ||
+	if ((flags & SB_RDONLY) || test_opt(sbi, DISABLE_CHECKPOINT) ||
 			!test_opt(sbi, MERGE_CHECKPOINT)) {
 		f2fs_stop_ckpt_thread(sbi);
 	} else {
@@ -3216,7 +3210,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 
 	limit_reserve_root(sbi);
 	adjust_unusable_cap_perc(sbi);
-	*flags = (*flags & ~SB_LAZYTIME) | (sb->s_flags & SB_LAZYTIME);
+	fc->sb_flags = (flags & ~SB_LAZYTIME) | (sb->s_flags & SB_LAZYTIME);
 	return 0;
 restore_checkpoint:
 	if (need_enable_checkpoint) {
@@ -3863,7 +3857,6 @@ static const struct super_operations f2fs_sops = {
 	.freeze_fs	= f2fs_freeze,
 	.unfreeze_fs	= f2fs_unfreeze,
 	.statfs		= f2fs_statfs,
-	.remount_fs	= f2fs_remount,
 	.shutdown	= f2fs_shutdown,
 };
 
@@ -5047,16 +5040,13 @@ static void f2fs_tuning_parameters(struct f2fs_sb_info *sbi)
 	sbi->readdir_ra = true;
 }
 
-static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
+static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	struct f2fs_sb_info *sbi;
 	struct f2fs_super_block *raw_super;
-	struct f2fs_fs_context ctx;
-	struct fs_context fc;
 	struct inode *root;
 	int err;
 	bool skip_recovery = false, need_fsck = false;
-	char *options = NULL;
 	int recovery, i, valid_super_block;
 	struct curseg_info *seg_i;
 	int retry_cnt = 1;
@@ -5069,9 +5059,11 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 	raw_super = NULL;
 	valid_super_block = -1;
 	recovery = 0;
-	memset(&fc, 0, sizeof(fc));
-	memset(&ctx, 0, sizeof(ctx));
-	fc.fs_private = &ctx;
+
+	/* check the mount options legality in ctx first */
+	err = f2fs_validate_options(fc);
+	if (err)
+		return -EINVAL;
 
 	/* allocate memory for f2fs-specific super block info */
 	sbi = kzalloc(sizeof(struct f2fs_sb_info), GFP_KERNEL);
@@ -5131,22 +5123,12 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 						sizeof(raw_super->uuid));
 
 	default_options(sbi, false);
-	/* parse mount options */
-	options = kstrdup((const char *)data, GFP_KERNEL);
-	if (data && !options) {
-		err = -ENOMEM;
-		goto free_sb_buf;
-	}
-
-	err = parse_options(&fc, options);
-	if (err)
-		goto free_options;
 
-	err = f2fs_check_opt_consistency(&fc, sb);
+	err = f2fs_check_opt_consistency(fc, sb);
 	if (err < 0)
-		goto free_options;
+		goto free_sb_buf;
 
-	f2fs_apply_options(&fc, sb);
+	f2fs_apply_options(fc, sb);
 
 	sb->s_maxbytes = max_file_blocks(NULL) <<
 				le32_to_cpu(raw_super->log_blocksize);
@@ -5460,7 +5442,6 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 		if (err)
 			goto sync_free_meta;
 	}
-	kvfree(options);
 
 	/* recover broken superblock */
 	if (recovery) {
@@ -5553,7 +5534,6 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 		kfree(F2FS_OPTION(sbi).s_qf_names[i]);
 #endif
 	fscrypt_free_dummy_policy(&F2FS_OPTION(sbi).dummy_enc_policy);
-	kvfree(options);
 free_sb_buf:
 	kfree(raw_super);
 free_sbi:
@@ -5571,14 +5551,39 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 	return err;
 }
 
-static struct dentry *f2fs_mount(struct file_system_type *fs_type, int flags,
-			const char *dev_name, void *data)
+static int f2fs_get_tree(struct fs_context *fc)
 {
-	return mount_bdev(fs_type, flags, dev_name, data, f2fs_fill_super);
+	return get_tree_bdev(fc, f2fs_fill_super);
+}
+
+static int f2fs_reconfigure(struct fs_context *fc)
+{
+	struct super_block *sb = fc->root->d_sb;
+
+	return __f2fs_remount(fc, sb);
+}
+
+static void f2fs_fc_free(struct fs_context *fc)
+{
+	struct f2fs_fs_context *ctx = fc->fs_private;
+	int i;
+
+	if (!ctx)
+		return;
+
+#ifdef CONFIG_QUOTA
+	for (i = 0; i < MAXQUOTAS; i++)
+		kfree(F2FS_CTX_INFO(ctx).s_qf_names[i]);
+#endif
+	fscrypt_free_dummy_policy(&F2FS_CTX_INFO(ctx).dummy_enc_policy);
+	kfree(ctx);
 }
 
 static const struct fs_context_operations f2fs_context_ops = {
 	.parse_param	= f2fs_parse_param,
+	.get_tree	= f2fs_get_tree,
+	.reconfigure = f2fs_reconfigure,
+	.free	= f2fs_fc_free,
 };
 
 static void kill_f2fs_super(struct super_block *sb)
@@ -5620,10 +5625,24 @@ static void kill_f2fs_super(struct super_block *sb)
 	}
 }
 
+static int f2fs_init_fs_context(struct fs_context *fc)
+{
+	struct f2fs_fs_context *ctx;
+
+	ctx = kzalloc(sizeof(struct f2fs_fs_context), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	fc->fs_private = ctx;
+	fc->ops = &f2fs_context_ops;
+
+	return 0;
+}
+
 static struct file_system_type f2fs_fs_type = {
 	.owner		= THIS_MODULE,
 	.name		= "f2fs",
-	.mount		= f2fs_mount,
+	.init_fs_context = f2fs_init_fs_context,
 	.kill_sb	= kill_f2fs_super,
 	.fs_flags	= FS_REQUIRES_DEV | FS_ALLOW_IDMAP,
 };
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 9/9] f2fs: remove unused structure and functions
  2024-08-14  2:39 [PATCH 0/9] f2fs: new mount API conversion Hongbo Li
                   ` (7 preceding siblings ...)
  2024-08-14  2:39 ` [PATCH 8/9] f2fs: switch to the new mount api Hongbo Li
@ 2024-08-14  2:39 ` Hongbo Li
  2024-08-27 11:47 ` [PATCH 0/9] f2fs: new mount API conversion Hongbo Li
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 16+ messages in thread
From: Hongbo Li @ 2024-08-14  2:39 UTC (permalink / raw)
  To: jaegeuk, chao
  Cc: linux-f2fs-devel, brauner, lczerner, linux-fsdevel, lihongbo22

The match_table_t and other some functions (which have been replaced
during reconstructure) now are not used, so we can remove them.

Signed-off-by: Hongbo Li <lihongbo22@huawei.com>
---
 fs/f2fs/super.c | 264 ------------------------------------------------
 1 file changed, 264 deletions(-)

diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 86acb7354d95..46c0d2087966 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -274,83 +274,6 @@ static const struct fs_parameter_spec f2fs_param_specs[] = {
 	{}
 };
 
-static match_table_t f2fs_tokens = {
-	{Opt_gc_background, "background_gc=%s"},
-	{Opt_disable_roll_forward, "disable_roll_forward"},
-	{Opt_norecovery, "norecovery"},
-	{Opt_discard, "discard"},
-	{Opt_nodiscard, "nodiscard"},
-	{Opt_noheap, "no_heap"},
-	{Opt_heap, "heap"},
-	{Opt_user_xattr, "user_xattr"},
-	{Opt_nouser_xattr, "nouser_xattr"},
-	{Opt_acl, "acl"},
-	{Opt_noacl, "noacl"},
-	{Opt_active_logs, "active_logs=%u"},
-	{Opt_disable_ext_identify, "disable_ext_identify"},
-	{Opt_inline_xattr, "inline_xattr"},
-	{Opt_noinline_xattr, "noinline_xattr"},
-	{Opt_inline_xattr_size, "inline_xattr_size=%u"},
-	{Opt_inline_data, "inline_data"},
-	{Opt_inline_dentry, "inline_dentry"},
-	{Opt_noinline_dentry, "noinline_dentry"},
-	{Opt_flush_merge, "flush_merge"},
-	{Opt_noflush_merge, "noflush_merge"},
-	{Opt_barrier, "barrier"},
-	{Opt_nobarrier, "nobarrier"},
-	{Opt_fastboot, "fastboot"},
-	{Opt_extent_cache, "extent_cache"},
-	{Opt_noextent_cache, "noextent_cache"},
-	{Opt_noinline_data, "noinline_data"},
-	{Opt_data_flush, "data_flush"},
-	{Opt_reserve_root, "reserve_root=%u"},
-	{Opt_resgid, "resgid=%u"},
-	{Opt_resuid, "resuid=%u"},
-	{Opt_mode, "mode=%s"},
-	{Opt_fault_injection, "fault_injection=%u"},
-	{Opt_fault_type, "fault_type=%u"},
-	{Opt_quota, "quota"},
-	{Opt_noquota, "noquota"},
-	{Opt_usrquota, "usrquota"},
-	{Opt_grpquota, "grpquota"},
-	{Opt_prjquota, "prjquota"},
-	{Opt_usrjquota, "usrjquota=%s"},
-	{Opt_grpjquota, "grpjquota=%s"},
-	{Opt_prjjquota, "prjjquota=%s"},
-	{Opt_offusrjquota, "usrjquota="},
-	{Opt_offgrpjquota, "grpjquota="},
-	{Opt_offprjjquota, "prjjquota="},
-	{Opt_jqfmt_vfsold, "jqfmt=vfsold"},
-	{Opt_jqfmt_vfsv0, "jqfmt=vfsv0"},
-	{Opt_jqfmt_vfsv1, "jqfmt=vfsv1"},
-	{Opt_alloc, "alloc_mode=%s"},
-	{Opt_fsync, "fsync_mode=%s"},
-	{Opt_test_dummy_encryption, "test_dummy_encryption=%s"},
-	{Opt_test_dummy_encryption, "test_dummy_encryption"},
-	{Opt_inlinecrypt, "inlinecrypt"},
-	{Opt_checkpoint_disable, "checkpoint=disable"},
-	{Opt_checkpoint_disable_cap, "checkpoint=disable:%u"},
-	{Opt_checkpoint_disable_cap_perc, "checkpoint=disable:%u%%"},
-	{Opt_checkpoint_enable, "checkpoint=enable"},
-	{Opt_checkpoint_merge, "checkpoint_merge"},
-	{Opt_nocheckpoint_merge, "nocheckpoint_merge"},
-	{Opt_compress_algorithm, "compress_algorithm=%s"},
-	{Opt_compress_log_size, "compress_log_size=%u"},
-	{Opt_compress_extension, "compress_extension=%s"},
-	{Opt_nocompress_extension, "nocompress_extension=%s"},
-	{Opt_compress_chksum, "compress_chksum"},
-	{Opt_compress_mode, "compress_mode=%s"},
-	{Opt_compress_cache, "compress_cache"},
-	{Opt_atgc, "atgc"},
-	{Opt_gc_merge, "gc_merge"},
-	{Opt_nogc_merge, "nogc_merge"},
-	{Opt_discard_unit, "discard_unit=%s"},
-	{Opt_memory_mode, "memory=%s"},
-	{Opt_age_extent_cache, "age_extent_cache"},
-	{Opt_errors, "errors=%s"},
-	{Opt_err, NULL},
-};
-
 #define F2FS_SPEC_background_gc			(1 << 0)
 #define F2FS_SPEC_inline_xattr_size		(1 << 1)
 #define F2FS_SPEC_active_logs			(1 << 2)
@@ -533,59 +456,6 @@ static void init_once(void *foo)
 #ifdef CONFIG_QUOTA
 static const char * const quotatypes[] = INITQFNAMES;
 #define QTYPE2NAME(t) (quotatypes[t])
-static int f2fs_set_qf_name(struct super_block *sb, int qtype,
-			    struct fs_parameter *param)
-{
-	struct f2fs_sb_info *sbi = F2FS_SB(sb);
-	char *qname;
-	int ret = -EINVAL;
-
-	if (sb_any_quota_loaded(sb) && !F2FS_OPTION(sbi).s_qf_names[qtype]) {
-		f2fs_err(sbi, "Cannot change journaled quota options when quota turned on");
-		return -EINVAL;
-	}
-	if (f2fs_sb_has_quota_ino(sbi)) {
-		f2fs_info(sbi, "QUOTA feature is enabled, so ignore qf_name");
-		return 0;
-	}
-
-	qname = kmemdup_nul(param->string, param->size, GFP_KERNEL);
-	if (!qname) {
-		f2fs_err(sbi, "Not enough memory for storing quotafile name");
-		return -ENOMEM;
-	}
-	if (F2FS_OPTION(sbi).s_qf_names[qtype]) {
-		if (strcmp(F2FS_OPTION(sbi).s_qf_names[qtype], qname) == 0)
-			ret = 0;
-		else
-			f2fs_err(sbi, "%s quota file already specified",
-				 QTYPE2NAME(qtype));
-		goto errout;
-	}
-	if (strchr(qname, '/')) {
-		f2fs_err(sbi, "quotafile must be on filesystem root");
-		goto errout;
-	}
-	F2FS_OPTION(sbi).s_qf_names[qtype] = qname;
-	set_opt(sbi, QUOTA);
-	return 0;
-errout:
-	kfree(qname);
-	return ret;
-}
-
-static int f2fs_clear_qf_name(struct super_block *sb, int qtype)
-{
-	struct f2fs_sb_info *sbi = F2FS_SB(sb);
-
-	if (sb_any_quota_loaded(sb) && F2FS_OPTION(sbi).s_qf_names[qtype]) {
-		f2fs_err(sbi, "Cannot change journaled quota options when quota turned on");
-		return -EINVAL;
-	}
-	kfree(F2FS_OPTION(sbi).s_qf_names[qtype]);
-	F2FS_OPTION(sbi).s_qf_names[qtype] = NULL;
-	return 0;
-}
 
 /*
  * Note the name of the specified quota file.
@@ -637,50 +507,6 @@ static int f2fs_unnote_qf_name(struct fs_context *fc, int qtype)
 	return 0;
 }
 
-static int f2fs_check_quota_options(struct f2fs_sb_info *sbi)
-{
-	/*
-	 * We do the test below only for project quotas. 'usrquota' and
-	 * 'grpquota' mount options are allowed even without quota feature
-	 * to support legacy quotas in quota files.
-	 */
-	if (test_opt(sbi, PRJQUOTA) && !f2fs_sb_has_project_quota(sbi)) {
-		f2fs_err(sbi, "Project quota feature not enabled. Cannot enable project quota enforcement.");
-		return -1;
-	}
-	if (F2FS_OPTION(sbi).s_qf_names[USRQUOTA] ||
-			F2FS_OPTION(sbi).s_qf_names[GRPQUOTA] ||
-			F2FS_OPTION(sbi).s_qf_names[PRJQUOTA]) {
-		if (test_opt(sbi, USRQUOTA) &&
-				F2FS_OPTION(sbi).s_qf_names[USRQUOTA])
-			clear_opt(sbi, USRQUOTA);
-
-		if (test_opt(sbi, GRPQUOTA) &&
-				F2FS_OPTION(sbi).s_qf_names[GRPQUOTA])
-			clear_opt(sbi, GRPQUOTA);
-
-		if (test_opt(sbi, PRJQUOTA) &&
-				F2FS_OPTION(sbi).s_qf_names[PRJQUOTA])
-			clear_opt(sbi, PRJQUOTA);
-
-		if (test_opt(sbi, GRPQUOTA) || test_opt(sbi, USRQUOTA) ||
-				test_opt(sbi, PRJQUOTA)) {
-			f2fs_err(sbi, "old and new quota format mixing");
-			return -1;
-		}
-
-		if (!F2FS_OPTION(sbi).s_jquota_fmt) {
-			f2fs_err(sbi, "journaled quota format not specified");
-			return -1;
-		}
-	}
-
-	if (f2fs_sb_has_quota_ino(sbi) && F2FS_OPTION(sbi).s_jquota_fmt) {
-		f2fs_info(sbi, "QUOTA feature is enabled, so ignore jquota_fmt");
-		F2FS_OPTION(sbi).s_jquota_fmt = 0;
-	}
-	return 0;
-}
 #endif
 
 static int f2fs_parse_test_dummy_encryption(const struct fs_parameter *param,
@@ -708,53 +534,6 @@ static int f2fs_parse_test_dummy_encryption(const struct fs_parameter *param,
 	return 0;
 }
 
-static int f2fs_set_test_dummy_encryption(struct super_block *sb,
-					  const struct fs_parameter *param,
-					  bool is_remount)
-{
-	struct f2fs_sb_info *sbi = F2FS_SB(sb);
-	struct fscrypt_dummy_policy *policy =
-		&F2FS_OPTION(sbi).dummy_enc_policy;
-	int err;
-
-	if (!IS_ENABLED(CONFIG_FS_ENCRYPTION)) {
-		f2fs_warn(sbi, "test_dummy_encryption option not supported");
-		return -EINVAL;
-	}
-
-	if (!f2fs_sb_has_encrypt(sbi)) {
-		f2fs_err(sbi, "Encrypt feature is off");
-		return -EINVAL;
-	}
-
-	/*
-	 * This mount option is just for testing, and it's not worthwhile to
-	 * implement the extra complexity (e.g. RCU protection) that would be
-	 * needed to allow it to be set or changed during remount.  We do allow
-	 * it to be specified during remount, but only if there is no change.
-	 */
-	if (is_remount && !fscrypt_is_dummy_policy_set(policy)) {
-		f2fs_warn(sbi, "Can't set test_dummy_encryption on remount");
-		return -EINVAL;
-	}
-
-	err = fscrypt_parse_test_dummy_encryption(param, policy);
-	if (err) {
-		if (err == -EEXIST)
-			f2fs_warn(sbi,
-				  "Can't change test_dummy_encryption on remount");
-		else if (err == -EINVAL)
-			f2fs_warn(sbi, "Value of option \"%s\" is unrecognized",
-				  param->key);
-		else
-			f2fs_warn(sbi, "Error processing option \"%s\" [%d]",
-				  param->key, err);
-		return -EINVAL;
-	}
-	f2fs_warn(sbi, "Test dummy encryption mode enabled");
-	return 0;
-}
-
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 static bool is_compress_extension_exist(struct f2fs_mount_info *info,
 					const char *new_ext, bool is_ext)
@@ -1512,49 +1291,6 @@ static int f2fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
 	return 0;
 }
 
-static int parse_options(struct fs_context *fc, char *options)
-{
-	struct fs_parameter param;
-	char *key;
-	int ret;
-
-	if (!options)
-		goto default_check;
-
-	while ((key = strsep(&options, ",")) != NULL) {
-		if (*key) {
-			size_t v_len = 0;
-			char *value = strchr(key, '=');
-
-			param.type = fs_value_is_flag;
-			param.string = NULL;
-
-			if (value) {
-				if (value == key)
-					continue;
-
-				*value++ = 0;
-				v_len = strlen(value);
-				param.string = kmemdup_nul(value, v_len, GFP_KERNEL);
-				if (!param.string)
-					return -ENOMEM;
-				param.type = fs_value_is_string;
-			}
-
-			param.key = key;
-			param.size = v_len;
-
-			ret = f2fs_parse_param(fc, &param);
-			kfree(param.string);
-			if (ret < 0)
-				return ret;
-		}
-	}
-
-default_check:
-	return f2fs_validate_options(fc);
-}
-
 static int f2fs_validate_options(struct fs_context *fc)
 {
 #ifdef CONFIG_QUOTA
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH 0/9] f2fs: new mount API conversion
  2024-08-14  2:39 [PATCH 0/9] f2fs: new mount API conversion Hongbo Li
                   ` (8 preceding siblings ...)
  2024-08-14  2:39 ` [PATCH 9/9] f2fs: remove unused structure and functions Hongbo Li
@ 2024-08-27 11:47 ` Hongbo Li
  2024-08-30 17:07   ` Eric Sandeen
  2024-09-11  2:04 ` Hongbo Li
  2025-07-11 16:30 ` [f2fs-dev] " patchwork-bot+f2fs
  11 siblings, 1 reply; 16+ messages in thread
From: Hongbo Li @ 2024-08-27 11:47 UTC (permalink / raw)
  To: jaegeuk, chao; +Cc: linux-f2fs-devel, brauner, lczerner, linux-fsdevel

Does there exist CI test for f2fs? I can only write the mount test for 
f2fs refer to tests/ext4/053. And I have tested this in local.

Thanks,
Hongbo

On 2024/8/14 10:39, Hongbo Li wrote:
> Since many filesystems have done the new mount API conversion,
> we introduce the new mount API conversion in f2fs.
> 
> The series can be applied on top of the current mainline tree
> and the work is based on the patches from Lukas Czerner (has
> done this in ext4[1]). His patch give me a lot of ideas.
> 
> Here is a high level description of the patchset:
> 
> 1. Prepare the f2fs mount parameters required by the new mount
> API and use it for parsing, while still using the old API to
> get mount options string. Split the parameter parsing and
> validation of the parse_options helper into two separate
> helpers.
> 
>    f2fs: Add fs parameter specifications for mount options
>    f2fs: move the option parser into handle_mount_opt
>    f2fs: move option validation into a separate helper
> 
> 2. Remove the use of sb/sbi structure of f2fs from all the
> parsing code, because with the new mount API the parsing is
> going to be done before we even get the super block. In this
> part, we introduce f2fs_fs_context to hold the temporary
> options when parsing. For the simple options check, it has
> to be done during parsing by using f2fs_fs_context structure.
> For the check which needs sb/sbi, we do this during super
> block filling.
> 
>    f2fs: Allow sbi to be NULL in f2fs_printk
>    f2fs: Add f2fs_fs_context to record the mount options
>    f2fs: separate the options parsing and options checking
> 
> 3. Switch the f2fs to use the new mount API for mount and
> remount.
> 
>    f2fs: introduce fs_context_operation structure
>    f2fs: switch to the new mount api
> 
> 4. Cleanup the old unused structures and helpers.
> 
>    f2fs: remove unused structure and functions
> 
> There is still a potential to do some cleanups and perhaps
> refactoring. However that can be done later after the conversion
> to the new mount API which is the main purpose of the patchset.
> 
> [1] https://lore.kernel.org/all/20211021114508.21407-1-lczerner@redhat.com/
> 
> Hongbo Li (9):
>    f2fs: Add fs parameter specifications for mount options
>    f2fs: move the option parser into handle_mount_opt
>    f2fs: move option validation into a separate helper
>    f2fs: Allow sbi to be NULL in f2fs_printk
>    f2fs: Add f2fs_fs_context to record the mount options
>    f2fs: separate the options parsing and options checking
>    f2fs: introduce fs_context_operation structure
>    f2fs: switch to the new mount api
>    f2fs: remove unused structure and functions
> 
>   fs/f2fs/super.c | 2211 ++++++++++++++++++++++++++++-------------------
>   1 file changed, 1341 insertions(+), 870 deletions(-)
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 0/9] f2fs: new mount API conversion
  2024-08-27 11:47 ` [PATCH 0/9] f2fs: new mount API conversion Hongbo Li
@ 2024-08-30 17:07   ` Eric Sandeen
  2024-08-31  1:45     ` Hongbo Li
  0 siblings, 1 reply; 16+ messages in thread
From: Eric Sandeen @ 2024-08-30 17:07 UTC (permalink / raw)
  To: Hongbo Li, jaegeuk, chao
  Cc: linux-f2fs-devel, brauner, lczerner, linux-fsdevel

Just FWIW - 

I had missed this thread when I got temporarily unsubscribed from fsdevel.
I have a series that I was hacking on for this same work, at
https://git.kernel.org/pub/scm/linux/kernel/git/sandeen/linux.git/commit/?h=f2fs-mount-api
but it's very rough and almost certainly contains bugs. It may or may not
be of any help to you, but just FYI.

I'll try to help review/test your series since I tried to solve this as
well, but I never completed the work. :)

Thanks,
-Eric

On 8/27/24 6:47 AM, Hongbo Li wrote:
> Does there exist CI test for f2fs? I can only write the mount test for f2fs refer to tests/ext4/053. And I have tested this in local.
> 
> Thanks,
> Hongbo
> 
> On 2024/8/14 10:39, Hongbo Li wrote:
>> Since many filesystems have done the new mount API conversion,
>> we introduce the new mount API conversion in f2fs.
>>
>> The series can be applied on top of the current mainline tree
>> and the work is based on the patches from Lukas Czerner (has
>> done this in ext4[1]). His patch give me a lot of ideas.
>>
>> Here is a high level description of the patchset:
>>
>> 1. Prepare the f2fs mount parameters required by the new mount
>> API and use it for parsing, while still using the old API to
>> get mount options string. Split the parameter parsing and
>> validation of the parse_options helper into two separate
>> helpers.
>>
>>    f2fs: Add fs parameter specifications for mount options
>>    f2fs: move the option parser into handle_mount_opt
>>    f2fs: move option validation into a separate helper
>>
>> 2. Remove the use of sb/sbi structure of f2fs from all the
>> parsing code, because with the new mount API the parsing is
>> going to be done before we even get the super block. In this
>> part, we introduce f2fs_fs_context to hold the temporary
>> options when parsing. For the simple options check, it has
>> to be done during parsing by using f2fs_fs_context structure.
>> For the check which needs sb/sbi, we do this during super
>> block filling.
>>
>>    f2fs: Allow sbi to be NULL in f2fs_printk
>>    f2fs: Add f2fs_fs_context to record the mount options
>>    f2fs: separate the options parsing and options checking
>>
>> 3. Switch the f2fs to use the new mount API for mount and
>> remount.
>>
>>    f2fs: introduce fs_context_operation structure
>>    f2fs: switch to the new mount api
>>
>> 4. Cleanup the old unused structures and helpers.
>>
>>    f2fs: remove unused structure and functions
>>
>> There is still a potential to do some cleanups and perhaps
>> refactoring. However that can be done later after the conversion
>> to the new mount API which is the main purpose of the patchset.
>>
>> [1] https://lore.kernel.org/all/20211021114508.21407-1-lczerner@redhat.com/
>>
>> Hongbo Li (9):
>>    f2fs: Add fs parameter specifications for mount options
>>    f2fs: move the option parser into handle_mount_opt
>>    f2fs: move option validation into a separate helper
>>    f2fs: Allow sbi to be NULL in f2fs_printk
>>    f2fs: Add f2fs_fs_context to record the mount options
>>    f2fs: separate the options parsing and options checking
>>    f2fs: introduce fs_context_operation structure
>>    f2fs: switch to the new mount api
>>    f2fs: remove unused structure and functions
>>
>>   fs/f2fs/super.c | 2211 ++++++++++++++++++++++++++++-------------------
>>   1 file changed, 1341 insertions(+), 870 deletions(-)
>>
> 


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 1/9] f2fs: Add fs parameter specifications for mount options
  2024-08-14  2:39 ` [PATCH 1/9] f2fs: Add fs parameter specifications for mount options Hongbo Li
@ 2024-08-30 21:00   ` Eric Sandeen
  0 siblings, 0 replies; 16+ messages in thread
From: Eric Sandeen @ 2024-08-30 21:00 UTC (permalink / raw)
  To: Hongbo Li, jaegeuk, chao; +Cc: linux-f2fs-devel, brauner, linux-fsdevel

On 8/13/24 9:39 PM, Hongbo Li wrote:
> Use an array of `fs_parameter_spec` called f2fs_param_specs to
> hold the mount option specifications for the new mount api.
> 
> Signed-off-by: Hongbo Li <lihongbo22@huawei.com>
> ---
>  fs/f2fs/super.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 79 insertions(+)
> 
> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> index 3959fd137cc9..1bd923a73c1f 100644
> --- a/fs/f2fs/super.c
> +++ b/fs/f2fs/super.c
> @@ -28,6 +28,7 @@
>  #include <linux/part_stat.h>
>  #include <linux/zstd.h>
>  #include <linux/lz4.h>
> +#include <linux/fs_parser.h>
>  
>  #include "f2fs.h"
>  #include "node.h"
> @@ -189,9 +190,87 @@ enum {
>  	Opt_memory_mode,
>  	Opt_age_extent_cache,
>  	Opt_errors,
> +	Opt_jqfmt,
> +	Opt_checkpoint,

If adding an opt_jqfmt to use with an enum, you can/should remove
Opt_jqfmt_vfsold Opt_jqfmt_vfsv0, and Opt_jqfmt_vfsv1, because they
are no longer used.

Similarly for Opt_checkpoint_disable_* symbols.

>  	Opt_err,
>  };
>  
> +static const struct constant_table f2fs_param_jqfmt[] = {
> +	{"vfsold",	QFMT_VFS_OLD},
> +	{"vfsv0",	QFMT_VFS_V0},
> +	{"vfsv1",	QFMT_VFS_V1},
> +	{}
> +};
> +
> +static const struct fs_parameter_spec f2fs_param_specs[] = {
> +	fsparam_string("background_gc", Opt_gc_background),
> +	fsparam_flag("disable_roll_forward", Opt_disable_roll_forward),
> +	fsparam_flag("norecovery", Opt_norecovery),

Many/most other filesystems tab-align the param_spec, like

...
+	fsparam_string	("background_gc",	Opt_gc_background),
+	fsparam_flag	("disable_roll_forward",Opt_disable_roll_forward),
+	fsparam_flag	("norecovery",		Opt_norecovery),
...

but that's just a style thing, up to you and the maintainers.

I'd also suggest making more use of enums (as you did for f2fs_param_jqfmt).
I think it can simplify parsing in the long run if you choose to. It avoids
the "if strcmp() else if strcmp() else if strcmp()... pattern, for example
you can do:

static const struct constant_table f2fs_param_background_gc[] = {
	{"on",		BGGC_MODE_ON},
	{"off",		BGGC_MODE_OFF},
	{"sync",	BGGC_MODE_SYNC},
	{}
};

...

	fsparam_enum	("background_gc",	Opt_gc_background, f2fs_param_background_gc),

...

and then parsing becomes simply:

	case Opt_gc_background:
		F2FS_CTX_INFO(ctx).bggc_mode = result.uint_32;
		ctx->spec_mask |= F2FS_SPEC_background_gc;
		break;

When I tried this I made a lot of use of enums, see
https://git.kernel.org/pub/scm/linux/kernel/git/sandeen/linux.git/tree/fs/f2fs/super.c?h=f2fs-mount-api#n182
and see what you think?

Thanks,
-Eric


^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 0/9] f2fs: new mount API conversion
  2024-08-30 17:07   ` Eric Sandeen
@ 2024-08-31  1:45     ` Hongbo Li
  0 siblings, 0 replies; 16+ messages in thread
From: Hongbo Li @ 2024-08-31  1:45 UTC (permalink / raw)
  To: Eric Sandeen, jaegeuk, chao
  Cc: linux-f2fs-devel, brauner, lczerner, linux-fsdevel



On 2024/8/31 1:07, Eric Sandeen wrote:
> Just FWIW -
> 
> I had missed this thread when I got temporarily unsubscribed from fsdevel.
> I have a series that I was hacking on for this same work, at
> https://git.kernel.org/pub/scm/linux/kernel/git/sandeen/linux.git/commit/?h=f2fs-mount-api
> but it's very rough and almost certainly contains bugs. It may or may not
> be of any help to you, but just FYI.
> 
> I'll try to help review/test your series since I tried to solve this as
> well, but I never completed the work. :)

That will be great! Thank you very much!
There is still a lot of refactoring that can be done. At the time, the 
consideration was to make the smallest possible changes, so many places 
were essentially preserved. We can work together to make this better.

Thanks,
Hongbo

> 
> Thanks,
> -Eric
> 
> On 8/27/24 6:47 AM, Hongbo Li wrote:
>> Does there exist CI test for f2fs? I can only write the mount test for f2fs refer to tests/ext4/053. And I have tested this in local.
>>
>> Thanks,
>> Hongbo
>>
>> On 2024/8/14 10:39, Hongbo Li wrote:
>>> Since many filesystems have done the new mount API conversion,
>>> we introduce the new mount API conversion in f2fs.
>>>
>>> The series can be applied on top of the current mainline tree
>>> and the work is based on the patches from Lukas Czerner (has
>>> done this in ext4[1]). His patch give me a lot of ideas.
>>>
>>> Here is a high level description of the patchset:
>>>
>>> 1. Prepare the f2fs mount parameters required by the new mount
>>> API and use it for parsing, while still using the old API to
>>> get mount options string. Split the parameter parsing and
>>> validation of the parse_options helper into two separate
>>> helpers.
>>>
>>>     f2fs: Add fs parameter specifications for mount options
>>>     f2fs: move the option parser into handle_mount_opt
>>>     f2fs: move option validation into a separate helper
>>>
>>> 2. Remove the use of sb/sbi structure of f2fs from all the
>>> parsing code, because with the new mount API the parsing is
>>> going to be done before we even get the super block. In this
>>> part, we introduce f2fs_fs_context to hold the temporary
>>> options when parsing. For the simple options check, it has
>>> to be done during parsing by using f2fs_fs_context structure.
>>> For the check which needs sb/sbi, we do this during super
>>> block filling.
>>>
>>>     f2fs: Allow sbi to be NULL in f2fs_printk
>>>     f2fs: Add f2fs_fs_context to record the mount options
>>>     f2fs: separate the options parsing and options checking
>>>
>>> 3. Switch the f2fs to use the new mount API for mount and
>>> remount.
>>>
>>>     f2fs: introduce fs_context_operation structure
>>>     f2fs: switch to the new mount api
>>>
>>> 4. Cleanup the old unused structures and helpers.
>>>
>>>     f2fs: remove unused structure and functions
>>>
>>> There is still a potential to do some cleanups and perhaps
>>> refactoring. However that can be done later after the conversion
>>> to the new mount API which is the main purpose of the patchset.
>>>
>>> [1] https://lore.kernel.org/all/20211021114508.21407-1-lczerner@redhat.com/
>>>
>>> Hongbo Li (9):
>>>     f2fs: Add fs parameter specifications for mount options
>>>     f2fs: move the option parser into handle_mount_opt
>>>     f2fs: move option validation into a separate helper
>>>     f2fs: Allow sbi to be NULL in f2fs_printk
>>>     f2fs: Add f2fs_fs_context to record the mount options
>>>     f2fs: separate the options parsing and options checking
>>>     f2fs: introduce fs_context_operation structure
>>>     f2fs: switch to the new mount api
>>>     f2fs: remove unused structure and functions
>>>
>>>    fs/f2fs/super.c | 2211 ++++++++++++++++++++++++++++-------------------
>>>    1 file changed, 1341 insertions(+), 870 deletions(-)
>>>
>>
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH 0/9] f2fs: new mount API conversion
  2024-08-14  2:39 [PATCH 0/9] f2fs: new mount API conversion Hongbo Li
                   ` (9 preceding siblings ...)
  2024-08-27 11:47 ` [PATCH 0/9] f2fs: new mount API conversion Hongbo Li
@ 2024-09-11  2:04 ` Hongbo Li
  2025-07-11 16:30 ` [f2fs-dev] " patchwork-bot+f2fs
  11 siblings, 0 replies; 16+ messages in thread
From: Hongbo Li @ 2024-09-11  2:04 UTC (permalink / raw)
  To: jaegeuk, chao
  Cc: linux-f2fs-devel, brauner, lczerner, linux-fsdevel, Eric Sandeen

Is the subject of the email required to be [f2fs-dev][PATCH]?

Thanks,
Hongbo

On 2024/8/14 10:39, Hongbo Li wrote:
> Since many filesystems have done the new mount API conversion,
> we introduce the new mount API conversion in f2fs.
> 
> The series can be applied on top of the current mainline tree
> and the work is based on the patches from Lukas Czerner (has
> done this in ext4[1]). His patch give me a lot of ideas.
> 
> Here is a high level description of the patchset:
> 
> 1. Prepare the f2fs mount parameters required by the new mount
> API and use it for parsing, while still using the old API to
> get mount options string. Split the parameter parsing and
> validation of the parse_options helper into two separate
> helpers.
> 
>    f2fs: Add fs parameter specifications for mount options
>    f2fs: move the option parser into handle_mount_opt
>    f2fs: move option validation into a separate helper
> 
> 2. Remove the use of sb/sbi structure of f2fs from all the
> parsing code, because with the new mount API the parsing is
> going to be done before we even get the super block. In this
> part, we introduce f2fs_fs_context to hold the temporary
> options when parsing. For the simple options check, it has
> to be done during parsing by using f2fs_fs_context structure.
> For the check which needs sb/sbi, we do this during super
> block filling.
> 
>    f2fs: Allow sbi to be NULL in f2fs_printk
>    f2fs: Add f2fs_fs_context to record the mount options
>    f2fs: separate the options parsing and options checking
> 
> 3. Switch the f2fs to use the new mount API for mount and
> remount.
> 
>    f2fs: introduce fs_context_operation structure
>    f2fs: switch to the new mount api
> 
> 4. Cleanup the old unused structures and helpers.
> 
>    f2fs: remove unused structure and functions
> 
> There is still a potential to do some cleanups and perhaps
> refactoring. However that can be done later after the conversion
> to the new mount API which is the main purpose of the patchset.
> 
> [1] https://lore.kernel.org/all/20211021114508.21407-1-lczerner@redhat.com/
> 
> Hongbo Li (9):
>    f2fs: Add fs parameter specifications for mount options
>    f2fs: move the option parser into handle_mount_opt
>    f2fs: move option validation into a separate helper
>    f2fs: Allow sbi to be NULL in f2fs_printk
>    f2fs: Add f2fs_fs_context to record the mount options
>    f2fs: separate the options parsing and options checking
>    f2fs: introduce fs_context_operation structure
>    f2fs: switch to the new mount api
>    f2fs: remove unused structure and functions
> 
>   fs/f2fs/super.c | 2211 ++++++++++++++++++++++++++++-------------------
>   1 file changed, 1341 insertions(+), 870 deletions(-)
> 

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [f2fs-dev] [PATCH 0/9] f2fs: new mount API conversion
  2024-08-14  2:39 [PATCH 0/9] f2fs: new mount API conversion Hongbo Li
                   ` (10 preceding siblings ...)
  2024-09-11  2:04 ` Hongbo Li
@ 2025-07-11 16:30 ` patchwork-bot+f2fs
  11 siblings, 0 replies; 16+ messages in thread
From: patchwork-bot+f2fs @ 2025-07-11 16:30 UTC (permalink / raw)
  To: Hongbo Li
  Cc: jaegeuk, chao, linux-fsdevel, lczerner, brauner, linux-f2fs-devel

Hello:

This series was applied to jaegeuk/f2fs.git (dev)
by Jaegeuk Kim <jaegeuk@kernel.org>:

On Wed, 14 Aug 2024 10:39:03 +0800 you wrote:
> Since many filesystems have done the new mount API conversion,
> we introduce the new mount API conversion in f2fs.
> 
> The series can be applied on top of the current mainline tree
> and the work is based on the patches from Lukas Czerner (has
> done this in ext4[1]). His patch give me a lot of ideas.
> 
> [...]

Here is the summary with links:
  - [f2fs-dev,1/9] f2fs: Add fs parameter specifications for mount options
    (no matching commit)
  - [f2fs-dev,2/9] f2fs: move the option parser into handle_mount_opt
    (no matching commit)
  - [f2fs-dev,3/9] f2fs: move option validation into a separate helper
    (no matching commit)
  - [f2fs-dev,4/9] f2fs: Allow sbi to be NULL in f2fs_printk
    (no matching commit)
  - [f2fs-dev,5/9] f2fs: Add f2fs_fs_context to record the mount options
    (no matching commit)
  - [f2fs-dev,6/9] f2fs: separate the options parsing and options checking
    (no matching commit)
  - [f2fs-dev,7/9] f2fs: introduce fs_context_operation structure
    https://git.kernel.org/jaegeuk/f2fs/c/54e12a4e0209
  - [f2fs-dev,8/9] f2fs: switch to the new mount api
    (no matching commit)
  - [f2fs-dev,9/9] f2fs: remove unused structure and functions
    (no matching commit)

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2025-07-11 16:29 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-08-14  2:39 [PATCH 0/9] f2fs: new mount API conversion Hongbo Li
2024-08-14  2:39 ` [PATCH 1/9] f2fs: Add fs parameter specifications for mount options Hongbo Li
2024-08-30 21:00   ` Eric Sandeen
2024-08-14  2:39 ` [PATCH 2/9] f2fs: move the option parser into handle_mount_opt Hongbo Li
2024-08-14  2:39 ` [PATCH 3/9] f2fs: move option validation into a separate helper Hongbo Li
2024-08-14  2:39 ` [PATCH 4/9] f2fs: Allow sbi to be NULL in f2fs_printk Hongbo Li
2024-08-14  2:39 ` [PATCH 5/9] f2fs: Add f2fs_fs_context to record the mount options Hongbo Li
2024-08-14  2:39 ` [PATCH 6/9] f2fs: separate the options parsing and options checking Hongbo Li
2024-08-14  2:39 ` [PATCH 7/9] f2fs: introduce fs_context_operation structure Hongbo Li
2024-08-14  2:39 ` [PATCH 8/9] f2fs: switch to the new mount api Hongbo Li
2024-08-14  2:39 ` [PATCH 9/9] f2fs: remove unused structure and functions Hongbo Li
2024-08-27 11:47 ` [PATCH 0/9] f2fs: new mount API conversion Hongbo Li
2024-08-30 17:07   ` Eric Sandeen
2024-08-31  1:45     ` Hongbo Li
2024-09-11  2:04 ` Hongbo Li
2025-07-11 16:30 ` [f2fs-dev] " patchwork-bot+f2fs

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).