linux-fsdevel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/7 V2] f2fs: new mount API conversion
@ 2025-04-20 15:24 Eric Sandeen
  2025-04-20 15:25 ` [PATCH 1/7] f2fs: Add fs parameter specifications for mount options Eric Sandeen
                   ` (8 more replies)
  0 siblings, 9 replies; 20+ messages in thread
From: Eric Sandeen @ 2025-04-20 15:24 UTC (permalink / raw)
  To: linux-f2fs-devel; +Cc: linux-fsdevel, jaegeuk, chao, lihongbo22

This is a forward-port of Hongbo's original f2fs mount API conversion,
posted last August at 
https://lore.kernel.org/linux-f2fs-devel/20240814023912.3959299-1-lihongbo22@huawei.com/

I had been trying to approach this with a little less complexity,
but in the end I realized that Hongbo's approach (which follows
the ext4 approach) was a good one, and I was not making any progrss
myself. ;)

In addition to the forward-port, I have also fixed a couple bugs I found
during testing, and some improvements / style choices as well. Hongbo and
I have discussed most of this off-list already, so I'm presenting the
net result here.

This does pass my typical testing which does a large number of random
mounts/remounts with valid and invalid option sets, on f2fs filesystem
images with various features in the on-disk superblock. (I was not able
to test all of this completely, as some options or features require
hardware I dn't have.)

Thanks,
-Eric

(A recap of Hongbo's original cover letter is below, edited slightly for
this series:)

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

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

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



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

* [PATCH 1/7] f2fs: Add fs parameter specifications for mount options
  2025-04-20 15:24 [PATCH 0/7 V2] f2fs: new mount API conversion Eric Sandeen
@ 2025-04-20 15:25 ` Eric Sandeen
  2025-05-07  9:54   ` Chao Yu
  2025-04-20 15:25 ` [PATCH 2/7] f2fs: move the option parser into handle_mount_opt Eric Sandeen
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Eric Sandeen @ 2025-04-20 15:25 UTC (permalink / raw)
  To: linux-f2fs-devel; +Cc: linux-fsdevel, jaegeuk, chao, lihongbo22, Eric Sandeen

From: Hongbo Li <lihongbo22@huawei.com>

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

Add constant_table structures for several options to facilitate
parsing.

Signed-off-by: Hongbo Li <lihongbo22@huawei.com>
[sandeen: forward port, minor fixes and updates, more fsparam_enum]
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---
 fs/f2fs/super.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 122 insertions(+)

diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index f087b2b71c89..c3623e052cde 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -27,6 +27,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"
@@ -192,9 +193,130 @@ enum {
 	Opt_age_extent_cache,
 	Opt_errors,
 	Opt_nat_bits,
+	Opt_jqfmt,
+	Opt_checkpoint,
 	Opt_err,
 };
 
+static const struct constant_table f2fs_param_background_gc[] = {
+	{"on",		BGGC_MODE_ON},
+	{"off",		BGGC_MODE_OFF},
+	{"sync",	BGGC_MODE_SYNC},
+	{}
+};
+
+static const struct constant_table f2fs_param_mode[] = {
+	{"adaptive",		FS_MODE_ADAPTIVE},
+	{"lfs",			FS_MODE_LFS},
+	{"fragment:segment",	FS_MODE_FRAGMENT_SEG},
+	{"fragment:block",	FS_MODE_FRAGMENT_BLK},
+	{}
+};
+
+static const struct constant_table f2fs_param_jqfmt[] = {
+	{"vfsold",	QFMT_VFS_OLD},
+	{"vfsv0",	QFMT_VFS_V0},
+	{"vfsv1",	QFMT_VFS_V1},
+	{}
+};
+
+static const struct constant_table f2fs_param_alloc_mode[] = {
+	{"default",	ALLOC_MODE_DEFAULT},
+	{"reuse",	ALLOC_MODE_REUSE},
+	{}
+};
+static const struct constant_table f2fs_param_fsync_mode[] = {
+	{"posix",	FSYNC_MODE_POSIX},
+	{"strict",	FSYNC_MODE_STRICT},
+	{"nobarrier",	FSYNC_MODE_NOBARRIER},
+	{}
+};
+
+static const struct constant_table f2fs_param_compress_mode[] = {
+	{"fs",		COMPR_MODE_FS},
+	{"user",	COMPR_MODE_USER},
+	{}
+};
+
+static const struct constant_table f2fs_param_discard_unit[] = {
+	{"block",	DISCARD_UNIT_BLOCK},
+	{"segment",	DISCARD_UNIT_SEGMENT},
+	{"section",	DISCARD_UNIT_SECTION},
+	{}
+};
+
+static const struct constant_table f2fs_param_memory_mode[] = {
+	{"normal",	MEMORY_MODE_NORMAL},
+	{"low",		MEMORY_MODE_LOW},
+	{}
+};
+
+static const struct constant_table f2fs_param_errors[] = {
+	{"remount-ro",	MOUNT_ERRORS_READONLY},
+	{"continue",	MOUNT_ERRORS_CONTINUE},
+	{"panic",	MOUNT_ERRORS_PANIC},
+	{}
+};
+
+static const struct fs_parameter_spec f2fs_param_specs[] = {
+	fsparam_enum("background_gc", Opt_gc_background, f2fs_param_background_gc),
+	fsparam_flag("disable_roll_forward", Opt_disable_roll_forward),
+	fsparam_flag("norecovery", Opt_norecovery),
+	fsparam_flag_no("discard", Opt_discard),
+	fsparam_flag("no_heap", Opt_noheap),
+	fsparam_flag("heap", Opt_heap),
+	fsparam_flag_no("user_xattr", Opt_user_xattr),
+	fsparam_flag_no("acl", Opt_acl),
+	fsparam_s32("active_logs", Opt_active_logs),
+	fsparam_flag("disable_ext_identify", Opt_disable_ext_identify),
+	fsparam_flag_no("inline_xattr", Opt_inline_xattr),
+	fsparam_s32("inline_xattr_size", Opt_inline_xattr_size),
+	fsparam_flag_no("inline_data", Opt_inline_data),
+	fsparam_flag_no("inline_dentry", Opt_inline_dentry),
+	fsparam_flag_no("flush_merge", Opt_flush_merge),
+	fsparam_flag_no("barrier", Opt_barrier),
+	fsparam_flag("fastboot", Opt_fastboot),
+	fsparam_flag_no("extent_cache", Opt_extent_cache),
+	fsparam_flag("data_flush", Opt_data_flush),
+	fsparam_u32("reserve_root", Opt_reserve_root),
+	fsparam_gid("resgid", Opt_resgid),
+	fsparam_uid("resuid", Opt_resuid),
+	fsparam_enum("mode", Opt_mode, f2fs_param_mode),
+	fsparam_s32("fault_injection", Opt_fault_injection),
+	fsparam_u32("fault_type", Opt_fault_type),
+	fsparam_flag_no("lazytime", Opt_lazytime),
+	fsparam_flag_no("quota", Opt_quota),
+	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_flag("nat_bits", Opt_nat_bits),
+	fsparam_enum("jqfmt", Opt_jqfmt, f2fs_param_jqfmt),
+	fsparam_enum("alloc_mode", Opt_alloc, f2fs_param_alloc_mode),
+	fsparam_enum("fsync_mode", Opt_fsync, f2fs_param_fsync_mode),
+	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_no("checkpoint_merge", Opt_checkpoint_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_enum("compress_mode", Opt_compress_mode, f2fs_param_compress_mode),
+	fsparam_flag("compress_cache", Opt_compress_cache),
+	fsparam_flag("atgc", Opt_atgc),
+	fsparam_flag_no("gc_merge", Opt_gc_merge),
+	fsparam_enum("discard_unit", Opt_discard_unit, f2fs_param_discard_unit),
+	fsparam_enum("memory", Opt_memory_mode, f2fs_param_memory_mode),
+	fsparam_flag("age_extent_cache", Opt_age_extent_cache),
+	fsparam_enum("errors", Opt_errors, f2fs_param_errors),
+	{}
+};
+
 static match_table_t f2fs_tokens = {
 	{Opt_gc_background, "background_gc=%s"},
 	{Opt_disable_roll_forward, "disable_roll_forward"},
-- 
2.47.0


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

* [PATCH 2/7] f2fs: move the option parser into handle_mount_opt
  2025-04-20 15:24 [PATCH 0/7 V2] f2fs: new mount API conversion Eric Sandeen
  2025-04-20 15:25 ` [PATCH 1/7] f2fs: Add fs parameter specifications for mount options Eric Sandeen
@ 2025-04-20 15:25 ` Eric Sandeen
  2025-05-07 11:26   ` Chao Yu
  2025-04-20 15:25 ` [PATCH 3/7] f2fs: Allow sbi to be NULL in f2fs_printk Eric Sandeen
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Eric Sandeen @ 2025-04-20 15:25 UTC (permalink / raw)
  To: linux-f2fs-devel; +Cc: linux-fsdevel, jaegeuk, chao, lihongbo22, Eric Sandeen

From: Hongbo Li <lihongbo22@huawei.com>

In handle_mount_opt, we use fs_parameter to parse each option.
However we're still using the old API to get the options string.
Using fsparams parse_options allows us to remove many of the Opt_
enums, so remove them.

The checkpoint disable cap (or percent) involves rather complex
parsing; we retain the old match_table mechanism for this, which
handles it well.

There are some changes about parsing 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.

Signed-off-by: Hongbo Li <lihongbo22@huawei.com>
[sandeen: forward port, minor fixes and updates]
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---
 fs/f2fs/super.c | 1063 ++++++++++++++++++-----------------------------
 1 file changed, 407 insertions(+), 656 deletions(-)

diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index c3623e052cde..8343dc2a515d 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -27,6 +27,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"
@@ -122,29 +123,20 @@ enum {
 	Opt_disable_roll_forward,
 	Opt_norecovery,
 	Opt_discard,
-	Opt_nodiscard,
 	Opt_noheap,
 	Opt_heap,
 	Opt_user_xattr,
-	Opt_nouser_xattr,
 	Opt_acl,
-	Opt_noacl,
 	Opt_active_logs,
 	Opt_disable_ext_identify,
 	Opt_inline_xattr,
-	Opt_noinline_xattr,
 	Opt_inline_xattr_size,
 	Opt_inline_data,
 	Opt_inline_dentry,
-	Opt_noinline_dentry,
 	Opt_flush_merge,
-	Opt_noflush_merge,
 	Opt_barrier,
-	Opt_nobarrier,
 	Opt_fastboot,
 	Opt_extent_cache,
-	Opt_noextent_cache,
-	Opt_noinline_data,
 	Opt_data_flush,
 	Opt_reserve_root,
 	Opt_resgid,
@@ -153,21 +145,13 @@ enum {
 	Opt_fault_injection,
 	Opt_fault_type,
 	Opt_lazytime,
-	Opt_nolazytime,
 	Opt_quota,
-	Opt_noquota,
 	Opt_usrquota,
 	Opt_grpquota,
 	Opt_prjquota,
 	Opt_usrjquota,
 	Opt_grpjquota,
 	Opt_prjjquota,
-	Opt_offusrjquota,
-	Opt_offgrpjquota,
-	Opt_offprjjquota,
-	Opt_jqfmt_vfsold,
-	Opt_jqfmt_vfsv0,
-	Opt_jqfmt_vfsv1,
 	Opt_alloc,
 	Opt_fsync,
 	Opt_test_dummy_encryption,
@@ -177,17 +161,15 @@ enum {
 	Opt_checkpoint_disable_cap_perc,
 	Opt_checkpoint_enable,
 	Opt_checkpoint_merge,
-	Opt_nocheckpoint_merge,
 	Opt_compress_algorithm,
 	Opt_compress_log_size,
-	Opt_compress_extension,
 	Opt_nocompress_extension,
+	Opt_compress_extension,
 	Opt_compress_chksum,
 	Opt_compress_mode,
 	Opt_compress_cache,
 	Opt_atgc,
 	Opt_gc_merge,
-	Opt_nogc_merge,
 	Opt_discard_unit,
 	Opt_memory_mode,
 	Opt_age_extent_cache,
@@ -317,83 +299,12 @@ 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_lazytime, "lazytime"},
-	{Opt_nolazytime, "nolazytime"},
-	{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_nat_bits, "nat_bits"},
+/* Resort to a match_table for this interestingly formatted option */
+static match_table_t f2fs_checkpoint_tokens = {
+	{Opt_checkpoint_disable, "disable"},
+	{Opt_checkpoint_disable_cap, "disable:%u"},
+	{Opt_checkpoint_disable_cap_perc, "disable:%u%%"},
+	{Opt_checkpoint_enable, "enable"},
 	{Opt_err, NULL},
 };
 
@@ -509,7 +420,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 f2fs_sb_info *sbi, int qtype,
-							substring_t *args)
+			    struct fs_parameter *param)
 {
 	struct super_block *sb = sbi->sb;
 	char *qname;
@@ -524,7 +435,7 @@ static int f2fs_set_qf_name(struct f2fs_sb_info *sbi, 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;
@@ -609,14 +520,9 @@ static int f2fs_check_quota_options(struct f2fs_sb_info *sbi)
 #endif
 
 static int f2fs_set_test_dummy_encryption(struct f2fs_sb_info *sbi,
-					  const char *opt,
-					  const substring_t *arg,
+					  const struct fs_parameter *param,
 					  bool is_remount)
 {
-	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;
@@ -642,17 +548,17 @@ static int f2fs_set_test_dummy_encryption(struct f2fs_sb_info *sbi,
 		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");
@@ -795,373 +701,263 @@ static int f2fs_set_zstd_level(struct f2fs_sb_info *sbi, const char *str)
 #endif
 #endif
 
-static int parse_options(struct f2fs_sb_info *sbi, char *options, bool is_remount)
+static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 {
-	substring_t args[MAX_OPT_ARGS];
+	struct f2fs_sb_info *sbi = fc->s_fs_info;
 #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;
-	kuid_t uid;
-	kgid_t gid;
-	int ret;
+	substring_t args[MAX_OPT_ARGS];
+	struct fs_parse_result result;
+	int is_remount;
+	char *name;
+	int token, ret, arg;
 
-	if (!options)
-		return 0;
+	token = fs_parse(fc, f2fs_param_specs, param, &result);
+	if (token < 0)
+		return token;
 
-	while ((p = strsep(&options, ",")) != NULL) {
-		int token;
+	is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;
 
-		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")) {
-				if (f2fs_sb_has_blkzoned(sbi)) {
-					f2fs_warn(sbi, "zoned devices need bggc");
-					kfree(name);
-					return -EINVAL;
-				}
-				F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_OFF;
-			} else if (!strcmp(name, "sync")) {
-				F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_SYNC;
-			} else {
-				kfree(name);
+	switch (token) {
+	case Opt_gc_background:
+		F2FS_OPTION(sbi).bggc_mode = result.uint_32;
+		break;
+	case Opt_disable_roll_forward:
+		set_opt(sbi, DISABLE_ROLL_FORWARD);
+		break;
+	case Opt_norecovery:
+		/* requires ro mount, checked in f2fs_validate_options */
+		set_opt(sbi, NORECOVERY);
+		break;
+	case Opt_discard:
+		if (result.negated) {
+			if (f2fs_hw_should_discard(sbi)) {
+				f2fs_warn(sbi, "discard is required for zoned block devices");
 				return -EINVAL;
 			}
-			kfree(name);
-			break;
-		case Opt_disable_roll_forward:
-			set_opt(sbi, DISABLE_ROLL_FORWARD);
-			break;
-		case Opt_norecovery:
-			/* requires ro mount, checked in f2fs_default_check */
-			set_opt(sbi, NORECOVERY);
-			break;
-		case Opt_discard:
+			clear_opt(sbi, DISCARD);
+		} else {
 			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;
+		}
+		break;
+	case Opt_noheap:
+	case Opt_heap:
+		f2fs_warn(sbi, "heap/no_heap options were deprecated");
+		break;
 #ifdef CONFIG_F2FS_FS_XATTR
-		case Opt_user_xattr:
-			set_opt(sbi, XATTR_USER);
-			break;
-		case Opt_nouser_xattr:
+	case Opt_user_xattr:
+		if (result.negated)
 			clear_opt(sbi, XATTR_USER);
-			break;
-		case Opt_inline_xattr:
-			set_opt(sbi, INLINE_XATTR);
-			break;
-		case Opt_noinline_xattr:
+		else
+			set_opt(sbi, XATTR_USER);
+		break;
+	case Opt_inline_xattr:
+		if (result.negated)
 			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;
+		else
+			set_opt(sbi, INLINE_XATTR);
+		break;
+	case Opt_inline_xattr_size:
+		set_opt(sbi, INLINE_XATTR_SIZE);
+		F2FS_OPTION(sbi).inline_xattr_size = result.int_32;
+		break;
 #else
-		case Opt_user_xattr:
-		case Opt_nouser_xattr:
-		case Opt_inline_xattr:
-		case Opt_noinline_xattr:
-		case Opt_inline_xattr_size:
-			f2fs_info(sbi, "xattr options not supported");
-			break;
+	case Opt_user_xattr:
+	case Opt_inline_xattr:
+	case Opt_inline_xattr_size:
+		f2fs_info(sbi, "%s options not supported", param->key);
+		break;
 #endif
 #ifdef CONFIG_F2FS_FS_POSIX_ACL
-		case Opt_acl:
-			set_opt(sbi, POSIX_ACL);
-			break;
-		case Opt_noacl:
+	case Opt_acl:
+		if (result.negated)
 			clear_opt(sbi, POSIX_ACL);
-			break;
+		else
+			set_opt(sbi, POSIX_ACL);
+		break;
 #else
-		case Opt_acl:
-		case Opt_noacl:
-			f2fs_info(sbi, "acl options not supported");
-			break;
+	case Opt_acl:
+		f2fs_info(sbi, "%s options not supported", param->key);
+		break;
 #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:
+	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;
+		break;
+	case Opt_disable_ext_identify:
+		set_opt(sbi, DISABLE_EXT_IDENTIFY);
+		break;
+	case Opt_inline_data:
+		if (result.negated)
+			clear_opt(sbi, INLINE_DATA);
+		else
 			set_opt(sbi, INLINE_DATA);
-			break;
-		case Opt_inline_dentry:
-			set_opt(sbi, INLINE_DENTRY);
-			break;
-		case Opt_noinline_dentry:
+		break;
+	case Opt_inline_dentry:
+		if (result.negated)
 			clear_opt(sbi, INLINE_DENTRY);
-			break;
-		case Opt_flush_merge:
-			set_opt(sbi, FLUSH_MERGE);
-			break;
-		case Opt_noflush_merge:
+		else
+			set_opt(sbi, INLINE_DENTRY);
+		break;
+	case Opt_flush_merge:
+		if (result.negated)
 			clear_opt(sbi, FLUSH_MERGE);
-			break;
-		case Opt_nobarrier:
+		else
+			set_opt(sbi, FLUSH_MERGE);
+		break;
+	case Opt_barrier:
+		if (result.negated)
 			set_opt(sbi, NOBARRIER);
-			break;
-		case Opt_barrier:
+		else
 			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:
+		break;
+	case Opt_fastboot:
+		set_opt(sbi, FASTBOOT);
+		break;
+	case Opt_extent_cache:
+		if (result.negated) {
 			if (f2fs_sb_has_device_alias(sbi)) {
 				f2fs_err(sbi, "device aliasing requires extent cache");
 				return -EINVAL;
 			}
 			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;
-			}
-			kfree(name);
-			break;
+		} else
+			set_opt(sbi, READ_EXTENT_CACHE);
+		break;
+	case Opt_data_flush:
+		set_opt(sbi, DATA_FLUSH);
+		break;
+	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.int_32;
+			set_opt(sbi, RESERVE_ROOT);
+		}
+		break;
+	case Opt_resuid:
+		F2FS_OPTION(sbi).s_resuid = result.uid;
+		break;
+	case Opt_resgid:
+		F2FS_OPTION(sbi).s_resgid = result.gid;
+		break;
+	case Opt_mode:
+		F2FS_OPTION(sbi).fs_mode = result.uint_32;
+		break;
 #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);
+		break;
 
-		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.int_32))
+			return -EINVAL;
+		set_opt(sbi, FAULT_INJECTION);
+		break;
 #else
-		case Opt_fault_injection:
-		case Opt_fault_type:
-			f2fs_info(sbi, "fault injection options not supported");
-			break;
+	case Opt_fault_injection:
+	case Opt_fault_type:
+		f2fs_info(sbi, "%s options not supported", param->key);
+		break;
 #endif
-		case Opt_lazytime:
-			set_opt(sbi, LAZYTIME);
-			break;
-		case Opt_nolazytime:
+	case Opt_lazytime:
+		if (result.negated)
 			clear_opt(sbi, LAZYTIME);
-			break;
+		else
+			set_opt(sbi, LAZYTIME);
+		break;
 #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(sbi, USRQUOTA, &args[0]);
-			if (ret)
-				return ret;
-			break;
-		case Opt_grpjquota:
-			ret = f2fs_set_qf_name(sbi, GRPQUOTA, &args[0]);
-			if (ret)
-				return ret;
-			break;
-		case Opt_prjjquota:
-			ret = f2fs_set_qf_name(sbi, PRJQUOTA, &args[0]);
-			if (ret)
-				return ret;
-			break;
-		case Opt_offusrjquota:
-			ret = f2fs_clear_qf_name(sbi, USRQUOTA);
-			if (ret)
-				return ret;
-			break;
-		case Opt_offgrpjquota:
-			ret = f2fs_clear_qf_name(sbi, GRPQUOTA);
-			if (ret)
-				return ret;
-			break;
-		case Opt_offprjjquota:
-			ret = f2fs_clear_qf_name(sbi, 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:
+	case Opt_quota:
+		if (result.negated) {
 			clear_opt(sbi, QUOTA);
 			clear_opt(sbi, USRQUOTA);
 			clear_opt(sbi, GRPQUOTA);
 			clear_opt(sbi, PRJQUOTA);
-			break;
+		} else
+			set_opt(sbi, USRQUOTA);
+		break;
+	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:
+		if (!*param->string)
+			ret = f2fs_clear_qf_name(sbi, USRQUOTA);
+		else
+			ret = f2fs_set_qf_name(sbi, USRQUOTA, param);
+		if (ret)
+			return ret;
+		break;
+	case Opt_grpjquota:
+		if (!*param->string)
+			ret = f2fs_clear_qf_name(sbi, GRPQUOTA);
+		else
+			ret = f2fs_set_qf_name(sbi, GRPQUOTA, param);
+		if (ret)
+			return ret;
+		break;
+	case Opt_prjjquota:
+		if (!*param->string)
+			ret = f2fs_clear_qf_name(sbi, PRJQUOTA);
+		else
+			ret = f2fs_set_qf_name(sbi, PRJQUOTA, param);
+		if (ret)
+			return ret;
+		break;
+	case Opt_jqfmt:
+		F2FS_OPTION(sbi).s_jquota_fmt = result.uint_32;
+		break;
 #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:
+		f2fs_info(sbi, "quota operations not supported");
+		break;
 #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;
-			}
-			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;
-			}
-			kfree(name);
-			break;
-		case Opt_test_dummy_encryption:
-			ret = f2fs_set_test_dummy_encryption(sbi, p, &args[0],
-							     is_remount);
-			if (ret)
-				return ret;
-			break;
-		case Opt_inlinecrypt:
+	case Opt_alloc:
+		F2FS_OPTION(sbi).alloc_mode = result.uint_32;
+		break;
+	case Opt_fsync:
+		F2FS_OPTION(sbi).fsync_mode = result.uint_32;
+		break;
+	case Opt_test_dummy_encryption:
+		ret = f2fs_set_test_dummy_encryption(sbi, param, is_remount);
+		if (ret)
+			return ret;
+		break;
+	case Opt_inlinecrypt:
 #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
-			set_opt(sbi, INLINECRYPT);
+		set_opt(sbi, INLINECRYPT);
 #else
-			f2fs_info(sbi, "inline encryption not supported");
+		f2fs_info(sbi, "inline encryption not supported");
 #endif
-			break;
+		break;
+	case Opt_checkpoint:
+		/* revert to match_table for checkpoint= options */
+		token = match_token(param->string, f2fs_checkpoint_tokens, args);
+		switch (token) {
 		case Opt_checkpoint_disable_cap_perc:
 			if (args->from && match_int(args, &arg))
 				return -EINVAL;
@@ -1182,270 +978,225 @@ static int parse_options(struct f2fs_sb_info *sbi, char *options, bool is_remoun
 		case Opt_checkpoint_enable:
 			clear_opt(sbi, DISABLE_CHECKPOINT);
 			break;
-		case Opt_checkpoint_merge:
-			set_opt(sbi, MERGE_CHECKPOINT);
-			break;
-		case Opt_nocheckpoint_merge:
+		default:
+			return -EINVAL;
+		}
+		break;
+	case Opt_checkpoint_merge:
+		if (result.negated)
 			clear_opt(sbi, MERGE_CHECKPOINT);
-			break;
+		else
+			set_opt(sbi, MERGE_CHECKPOINT);
+		break;
 #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");
+			break;
+		}
+		name = param->string;
+		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)
+				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)
+				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;
-			}
-			kfree(name);
+		} else
+			return -EINVAL;
+		break;
+	case Opt_compress_log_size:
+		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;
+		}
+		if (result.uint_32 < MIN_COMPRESS_LOG_SIZE ||
+		    result.uint_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.uint_32;
+		break;
+	case Opt_compress_extension:
+		if (!f2fs_sb_has_compression(sbi)) {
+			f2fs_info(sbi, "Image doesn't support compression");
 			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;
-
-			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;
-			}
+		}
+		name = param->string;
+		ext = F2FS_OPTION(sbi).extensions;
+		ext_cnt = F2FS_OPTION(sbi).compress_ext_cnt;
 
-			if (is_compress_extension_exist(sbi, name, true)) {
-				kfree(name);
-				break;
-			}
+		if (strlen(name) >= F2FS_EXTENSION_LEN ||
+		    ext_cnt >= COMPRESS_EXT_NUM) {
+			f2fs_err(sbi, "invalid extension length/number");
+			return -EINVAL;
+		}
 
-			ret = strscpy(ext[ext_cnt], name);
-			if (ret < 0) {
-				kfree(name);
-				return ret;
-			}
-			F2FS_OPTION(sbi).compress_ext_cnt++;
-			kfree(name);
+		if (is_compress_extension_exist(sbi, name, true))
 			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;
 
-			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;
-			}
+		ret = strscpy(ext[ext_cnt], name, F2FS_EXTENSION_LEN);
+		if (ret < 0)
+			return ret;
+		F2FS_OPTION(sbi).compress_ext_cnt++;
+		break;
+	case Opt_nocompress_extension:
+		if (!f2fs_sb_has_compression(sbi)) {
+			f2fs_info(sbi, "Image doesn't support compression");
+			break;
+		}
+		name = param->string;
+		noext = F2FS_OPTION(sbi).noextensions;
+		noext_cnt = F2FS_OPTION(sbi).nocompress_ext_cnt;
 
-			if (is_compress_extension_exist(sbi, name, false)) {
-				kfree(name);
-				break;
-			}
+		if (strlen(name) >= F2FS_EXTENSION_LEN ||
+			noext_cnt >= COMPRESS_EXT_NUM) {
+			f2fs_err(sbi, "invalid extension length/number");
+			return -EINVAL;
+		}
 
-			ret = strscpy(noext[noext_cnt], name);
-			if (ret < 0) {
-				kfree(name);
-				return ret;
-			}
-			F2FS_OPTION(sbi).nocompress_ext_cnt++;
-			kfree(name);
+		if (is_compress_extension_exist(sbi, name, false))
 			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;
+
+		ret = strscpy(noext[noext_cnt], name, F2FS_EXTENSION_LEN);
+		if (ret < 0)
+			return ret;
+		F2FS_OPTION(sbi).nocompress_ext_cnt++;
+		break;
+	case Opt_compress_chksum:
+		if (!f2fs_sb_has_compression(sbi)) {
+			f2fs_info(sbi, "Image doesn't support compression");
 			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;
-			}
-			kfree(name);
+		}
+		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;
-		case Opt_compress_cache:
-			if (!f2fs_sb_has_compression(sbi)) {
-				f2fs_info(sbi, "Image doesn't support compression");
-				break;
-			}
-			set_opt(sbi, COMPRESS_CACHE);
+		}
+		F2FS_OPTION(sbi).compress_mode = result.uint_32;
+		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;
 #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");
+		break;
 #endif
-		case Opt_atgc:
-			set_opt(sbi, ATGC);
-			break;
-		case Opt_gc_merge:
-			set_opt(sbi, GC_MERGE);
-			break;
-		case Opt_nogc_merge:
+	case Opt_atgc:
+		set_opt(sbi, ATGC);
+		break;
+	case Opt_gc_merge:
+		if (result.negated)
 			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;
-			}
-			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;
-			}
-			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;
+		else
+			set_opt(sbi, GC_MERGE);
+		break;
+	case Opt_discard_unit:
+		F2FS_OPTION(sbi).discard_unit = result.uint_32;
+		break;
+	case Opt_memory_mode:
+		F2FS_OPTION(sbi).memory_mode = result.uint_32;
+		break;
+	case Opt_age_extent_cache:
+		set_opt(sbi, AGE_EXTENT_CACHE);
+		break;
+	case Opt_errors:
+		F2FS_OPTION(sbi).errors = result.uint_32;
+		break;
+	case Opt_nat_bits:
+		set_opt(sbi, NAT_BITS);
+		break;
+	}
+	return 0;
+}
+
+static int parse_options(struct f2fs_sb_info *sbi, char *options, bool is_remount)
+{
+	struct fs_parameter param;
+	struct fs_context fc;
+	char *key;
+	int ret;
+
+	if (!options)
+		return 0;
+
+	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;
 			}
-			kfree(name);
-			break;
-		case Opt_nat_bits:
-			set_opt(sbi, NAT_BITS);
-			break;
-		default:
-			f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value",
-				 p);
-			return -EINVAL;
+
+			param.key = key;
+			param.size = v_len;
+
+			ret = handle_mount_opt(&fc, &param);
+			kfree(param.string);
+			if (ret < 0)
+				return ret;
 		}
 	}
 	return 0;
 }
 
-static int f2fs_default_check(struct f2fs_sb_info *sbi)
+static int f2fs_validate_options(struct f2fs_sb_info *sbi)
 {
 #ifdef CONFIG_QUOTA
 	if (f2fs_check_quota_options(sbi))
@@ -2516,7 +2267,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 	}
 #endif
 
-	err = f2fs_default_check(sbi);
+	err = f2fs_validate_options(sbi);
 	if (err)
 		goto restore_opts;
 
@@ -4675,7 +4426,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 	if (err)
 		goto free_options;
 
-	err = f2fs_default_check(sbi);
+	err = f2fs_validate_options(sbi);
 	if (err)
 		goto free_options;
 
-- 
2.47.0


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

* [PATCH 3/7] f2fs: Allow sbi to be NULL in f2fs_printk
  2025-04-20 15:24 [PATCH 0/7 V2] f2fs: new mount API conversion Eric Sandeen
  2025-04-20 15:25 ` [PATCH 1/7] f2fs: Add fs parameter specifications for mount options Eric Sandeen
  2025-04-20 15:25 ` [PATCH 2/7] f2fs: move the option parser into handle_mount_opt Eric Sandeen
@ 2025-04-20 15:25 ` Eric Sandeen
  2025-05-07 11:29   ` Chao Yu
  2025-04-20 15:25 ` [PATCH 4/7] f2fs: Add f2fs_fs_context to record the mount options Eric Sandeen
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 20+ messages in thread
From: Eric Sandeen @ 2025-04-20 15:25 UTC (permalink / raw)
  To: linux-f2fs-devel; +Cc: linux-fsdevel, jaegeuk, chao, lihongbo22, Eric Sandeen

From: Hongbo Li <lihongbo22@huawei.com>

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>
[sandeen: forward port]
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---
 fs/f2fs/super.c | 90 +++++++++++++++++++++++++++----------------------
 1 file changed, 49 insertions(+), 41 deletions(-)

diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 8343dc2a515d..aeb8e9a48bf6 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -321,11 +321,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);
 }
@@ -735,13 +743,13 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 	case Opt_discard:
 		if (result.negated) {
 			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);
 		} else {
 			if (!f2fs_hw_support_discard(sbi)) {
-				f2fs_warn(sbi, "device does not support discard");
+				f2fs_warn(NULL, "device does not support discard");
 				break;
 			}
 			set_opt(sbi, DISCARD);
@@ -749,7 +757,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		break;
 	case Opt_noheap:
 	case Opt_heap:
-		f2fs_warn(sbi, "heap/no_heap options were deprecated");
+		f2fs_warn(NULL, "heap/no_heap options were deprecated");
 		break;
 #ifdef CONFIG_F2FS_FS_XATTR
 	case Opt_user_xattr:
@@ -772,7 +780,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 	case Opt_user_xattr:
 	case Opt_inline_xattr:
 	case Opt_inline_xattr_size:
-		f2fs_info(sbi, "%s options not supported", param->key);
+		f2fs_info(NULL, "%s options not supported", param->key);
 		break;
 #endif
 #ifdef CONFIG_F2FS_FS_POSIX_ACL
@@ -784,7 +792,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		break;
 #else
 	case Opt_acl:
-		f2fs_info(sbi, "%s options not supported", param->key);
+		f2fs_info(NULL, "%s options not supported", param->key);
 		break;
 #endif
 	case Opt_active_logs:
@@ -838,7 +846,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		break;
 	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.int_32;
@@ -870,7 +878,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 #else
 	case Opt_fault_injection:
 	case Opt_fault_type:
-		f2fs_info(sbi, "%s options not supported", param->key);
+		f2fs_info(NULL, "%s options not supported", param->key);
 		break;
 #endif
 	case Opt_lazytime:
@@ -933,7 +941,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 	case Opt_usrjquota:
 	case Opt_grpjquota:
 	case Opt_prjjquota:
-		f2fs_info(sbi, "quota operations not supported");
+		f2fs_info(NULL, "quota operations not supported");
 		break;
 #endif
 	case Opt_alloc:
@@ -951,7 +959,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
 		set_opt(sbi, INLINECRYPT);
 #else
-		f2fs_info(sbi, "inline encryption not supported");
+		f2fs_info(NULL, "inline encryption not supported");
 #endif
 		break;
 	case Opt_checkpoint:
@@ -991,7 +999,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");
 			break;
 		}
 		name = param->string;
@@ -1000,7 +1008,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			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(NULL, "kernel doesn't support lzo compression");
 #endif
 		} else if (!strncmp(name, "lz4", 3)) {
 #ifdef CONFIG_F2FS_FS_LZ4
@@ -1009,7 +1017,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 				return -EINVAL;
 			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
@@ -1018,26 +1026,26 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 				return -EINVAL;
 			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
 			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(NULL, "kernel doesn't support lzorle compression");
 #endif
 		} else
 			return -EINVAL;
 		break;
 	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");
 			break;
 		}
 		if (result.uint_32 < MIN_COMPRESS_LOG_SIZE ||
 		    result.uint_32 > MAX_COMPRESS_LOG_SIZE) {
-			f2fs_err(sbi,
+			f2fs_err(NULL,
 				"Compress cluster log size is out of range");
 			return -EINVAL;
 		}
@@ -1045,7 +1053,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		break;
 	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 = param->string;
@@ -1054,7 +1062,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, "invalid extension length/number");
+			f2fs_err(NULL, "invalid extension length/number");
 			return -EINVAL;
 		}
 
@@ -1068,7 +1076,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		break;
 	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");
 			break;
 		}
 		name = param->string;
@@ -1077,7 +1085,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, "invalid extension length/number");
+			f2fs_err(NULL, "invalid extension length/number");
 			return -EINVAL;
 		}
 
@@ -1091,21 +1099,21 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		break;
 	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");
 			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");
+			f2fs_info(NULL, "Image doesn't support compression");
 			break;
 		}
 		F2FS_OPTION(sbi).compress_mode = result.uint_32;
 		break;
 	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");
 			break;
 		}
 		set_opt(sbi, COMPRESS_CACHE);
@@ -1118,7 +1126,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");
 		break;
 #endif
 	case Opt_atgc:
@@ -1203,17 +1211,17 @@ static int f2fs_validate_options(struct f2fs_sb_info *sbi)
 		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) && f2fs_sb_has_casefold(sbi)) {
-		f2fs_err(sbi,
+		f2fs_err(NULL,
 			"Filesystem with casefold feature cannot be mounted without CONFIG_UNICODE");
 		return -EINVAL;
 	}
@@ -1227,24 +1235,24 @@ static int f2fs_validate_options(struct f2fs_sb_info *sbi)
 #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
@@ -1254,11 +1262,11 @@ static int f2fs_validate_options(struct f2fs_sb_info *sbi)
 
 		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;
 		}
 
@@ -1267,24 +1275,24 @@ static int f2fs_validate_options(struct f2fs_sb_info *sbi)
 
 		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;
 	}
 
-- 
2.47.0


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

* [PATCH 4/7] f2fs: Add f2fs_fs_context to record the mount options
  2025-04-20 15:24 [PATCH 0/7 V2] f2fs: new mount API conversion Eric Sandeen
                   ` (2 preceding siblings ...)
  2025-04-20 15:25 ` [PATCH 3/7] f2fs: Allow sbi to be NULL in f2fs_printk Eric Sandeen
@ 2025-04-20 15:25 ` Eric Sandeen
  2025-04-20 15:25 ` [PATCH 5/7] f2fs: separate the options parsing and options checking Eric Sandeen
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 20+ messages in thread
From: Eric Sandeen @ 2025-04-20 15:25 UTC (permalink / raw)
  To: linux-f2fs-devel; +Cc: linux-fsdevel, jaegeuk, chao, lihongbo22, Eric Sandeen

From: Hongbo Li <lihongbo22@huawei.com>

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.

Note 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. (At this point in the series, mount options are
temporarily not set during mount.)

Signed-off-by: Hongbo Li <lihongbo22@huawei.com>
[sandeen: forward port, minor fixes and updates]
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---
 fs/f2fs/super.c | 419 ++++++++++++++++++++++++++++--------------------
 1 file changed, 244 insertions(+), 175 deletions(-)

diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index aeb8e9a48bf6..11bf936b0d3c 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -308,8 +308,65 @@ static match_table_t f2fs_checkpoint_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;
@@ -427,57 +484,51 @@ 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 f2fs_sb_info *sbi, int qtype,
-			    struct fs_parameter *param)
+/*
+ * 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 super_block *sb = sbi->sb;
+	struct f2fs_fs_context *ctx = fc->fs_private;
 	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");
+	if (param->size < 1) {
+		f2fs_err(NULL, "Missing quota name");
 		return -EINVAL;
 	}
-	if (f2fs_sb_has_quota_ino(sbi)) {
-		f2fs_info(sbi, "QUOTA feature is enabled, so ignore qf_name");
+	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(sbi, "Not enough memory for storing quotafile name");
+		f2fs_err(NULL, "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);
+	F2FS_CTX_INFO(ctx).s_qf_names[qtype] = qname;
+	ctx->qname_mask |= 1 << qtype;
 	return 0;
-errout:
-	kfree(qname);
-	return ret;
 }
 
-static int f2fs_clear_qf_name(struct f2fs_sb_info *sbi, int qtype)
+/*
+ * Clear the name of the specified quota file.
+ */
+static int f2fs_unnote_qf_name(struct fs_context *fc, int qtype)
 {
-	struct super_block *sb = sbi->sb;
+	struct f2fs_fs_context *ctx = fc->fs_private;
 
-	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;
+	kfree(ctx->info.s_qf_names[qtype]);
+	ctx->info.s_qf_names[qtype] = NULL;
+	ctx->qname_mask |= 1 << qtype;
 	return 0;
 }
 
@@ -527,54 +578,33 @@ static int f2fs_check_quota_options(struct f2fs_sb_info *sbi)
 }
 #endif
 
-static int f2fs_set_test_dummy_encryption(struct f2fs_sb_info *sbi,
-					  const struct fs_parameter *param,
-					  bool is_remount)
+static int f2fs_parse_test_dummy_encryption(const struct fs_parameter *param,
+					    struct f2fs_fs_context *ctx)
 {
-	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");
+		f2fs_warn(NULL, "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);
+	err = fscrypt_parse_test_dummy_encryption(param,
+					&ctx->info.dummy_enc_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",
+		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(sbi, "Error processing option \"%s\" [%d]",
+			f2fs_warn(NULL, "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_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];
@@ -582,11 +612,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++) {
@@ -635,58 +665,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))
@@ -694,16 +728,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
@@ -711,6 +746,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;
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 	unsigned char (*ext)[F2FS_EXTENSION_LEN];
@@ -731,14 +767,15 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 
 	switch (token) {
 	case Opt_gc_background:
-		F2FS_OPTION(sbi).bggc_mode = result.uint_32;
+		F2FS_CTX_INFO(ctx).bggc_mode = result.uint_32;
+		ctx->spec_mask |= F2FS_SPEC_background_gc;
 		break;
 	case Opt_disable_roll_forward:
-		set_opt(sbi, DISABLE_ROLL_FORWARD);
+		ctx_set_opt(ctx, F2FS_MOUNT_DISABLE_ROLL_FORWARD);
 		break;
 	case Opt_norecovery:
 		/* requires ro mount, checked in f2fs_validate_options */
-		set_opt(sbi, NORECOVERY);
+		ctx_set_opt(ctx, F2FS_MOUNT_NORECOVERY);
 		break;
 	case Opt_discard:
 		if (result.negated) {
@@ -746,13 +783,13 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 				f2fs_warn(NULL, "discard is required for zoned block devices");
 				return -EINVAL;
 			}
-			clear_opt(sbi, DISCARD);
+			ctx_clear_opt(ctx, F2FS_MOUNT_DISCARD);
 		} else {
 			if (!f2fs_hw_support_discard(sbi)) {
 				f2fs_warn(NULL, "device does not support discard");
 				break;
 			}
-			set_opt(sbi, DISCARD);
+			ctx_set_opt(ctx, F2FS_MOUNT_DISCARD);
 		}
 		break;
 	case Opt_noheap:
@@ -762,19 +799,20 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 #ifdef CONFIG_F2FS_FS_XATTR
 	case Opt_user_xattr:
 		if (result.negated)
-			clear_opt(sbi, XATTR_USER);
+			ctx_clear_opt(ctx, F2FS_MOUNT_XATTR_USER);
 		else
-			set_opt(sbi, XATTR_USER);
+			ctx_set_opt(ctx, F2FS_MOUNT_XATTR_USER);
 		break;
 	case Opt_inline_xattr:
 		if (result.negated)
-			clear_opt(sbi, INLINE_XATTR);
+			ctx_clear_opt(ctx, F2FS_MOUNT_INLINE_XATTR);
 		else
-			set_opt(sbi, INLINE_XATTR);
+			ctx_set_opt(ctx, F2FS_MOUNT_INLINE_XATTR);
 		break;
 	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;
 		break;
 #else
 	case Opt_user_xattr:
@@ -786,9 +824,9 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 #ifdef CONFIG_F2FS_FS_POSIX_ACL
 	case Opt_acl:
 		if (result.negated)
-			clear_opt(sbi, POSIX_ACL);
+			ctx_clear_opt(ctx, F2FS_MOUNT_POSIX_ACL);
 		else
-			set_opt(sbi, POSIX_ACL);
+			ctx_set_opt(ctx, F2FS_MOUNT_POSIX_ACL);
 		break;
 #else
 	case Opt_acl:
@@ -799,37 +837,38 @@ 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;
+		ctx->spec_mask |= F2FS_SPEC_active_logs;
+		F2FS_CTX_INFO(ctx).active_logs = result.int_32;
 		break;
 	case Opt_disable_ext_identify:
-		set_opt(sbi, DISABLE_EXT_IDENTIFY);
+		ctx_set_opt(ctx, F2FS_MOUNT_DISABLE_EXT_IDENTIFY);
 		break;
 	case Opt_inline_data:
 		if (result.negated)
-			clear_opt(sbi, INLINE_DATA);
+			ctx_clear_opt(ctx, F2FS_MOUNT_INLINE_DATA);
 		else
-			set_opt(sbi, INLINE_DATA);
+			ctx_set_opt(ctx, F2FS_MOUNT_INLINE_DATA);
 		break;
 	case Opt_inline_dentry:
 		if (result.negated)
-			clear_opt(sbi, INLINE_DENTRY);
+			ctx_clear_opt(ctx, F2FS_MOUNT_INLINE_DENTRY);
 		else
-			set_opt(sbi, INLINE_DENTRY);
+			ctx_set_opt(ctx, F2FS_MOUNT_INLINE_DENTRY);
 		break;
 	case Opt_flush_merge:
 		if (result.negated)
-			clear_opt(sbi, FLUSH_MERGE);
+			ctx_clear_opt(ctx, F2FS_MOUNT_FLUSH_MERGE);
 		else
-			set_opt(sbi, FLUSH_MERGE);
+			ctx_set_opt(ctx, F2FS_MOUNT_FLUSH_MERGE);
 		break;
 	case Opt_barrier:
 		if (result.negated)
-			set_opt(sbi, NOBARRIER);
+			ctx_set_opt(ctx, F2FS_MOUNT_NOBARRIER);
 		else
-			clear_opt(sbi, NOBARRIER);
+			ctx_clear_opt(ctx, F2FS_MOUNT_NOBARRIER);
 		break;
 	case Opt_fastboot:
-		set_opt(sbi, FASTBOOT);
+		ctx_set_opt(ctx, F2FS_MOUNT_FASTBOOT);
 		break;
 	case Opt_extent_cache:
 		if (result.negated) {
@@ -837,43 +876,51 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 				f2fs_err(sbi, "device aliasing requires extent cache");
 				return -EINVAL;
 			}
-			clear_opt(sbi, READ_EXTENT_CACHE);
+			ctx_clear_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE);
 		} else
-			set_opt(sbi, READ_EXTENT_CACHE);
+			ctx_set_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE);
 		break;
 	case Opt_data_flush:
-		set_opt(sbi, DATA_FLUSH);
+		ctx_set_opt(ctx, F2FS_MOUNT_DATA_FLUSH);
 		break;
 	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.int_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;
 		}
 		break;
 	case Opt_resuid:
-		F2FS_OPTION(sbi).s_resuid = result.uid;
+		F2FS_CTX_INFO(ctx).s_resuid = result.uid;
+		ctx->spec_mask |= F2FS_SPEC_resuid;
 		break;
 	case Opt_resgid:
-		F2FS_OPTION(sbi).s_resgid = result.gid;
+		F2FS_CTX_INFO(ctx).s_resgid = result.gid;
+		ctx->spec_mask |= F2FS_SPEC_resgid;
 		break;
 	case Opt_mode:
-		F2FS_OPTION(sbi).fs_mode = result.uint_32;
+		F2FS_CTX_INFO(ctx).fs_mode = result.uint_32;
+		ctx->spec_mask |= F2FS_SPEC_mode;
 		break;
 #ifdef CONFIG_F2FS_FAULT_INJECTION
 	case Opt_fault_injection:
 		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);
 		break;
 
 	case Opt_fault_type:
 		if (f2fs_build_fault_attr(sbi, 0, result.int_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);
 		break;
 #else
 	case Opt_fault_injection:
@@ -883,55 +930,56 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 #endif
 	case Opt_lazytime:
 		if (result.negated)
-			clear_opt(sbi, LAZYTIME);
+			ctx_clear_opt(ctx, F2FS_MOUNT_LAZYTIME);
 		else
-			set_opt(sbi, LAZYTIME);
+			ctx_set_opt(ctx, F2FS_MOUNT_LAZYTIME);
 		break;
 #ifdef CONFIG_QUOTA
 	case Opt_quota:
 		if (result.negated) {
-			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);
 		} else
-			set_opt(sbi, USRQUOTA);
+			ctx_set_opt(ctx, F2FS_MOUNT_USRQUOTA);
 		break;
 	case Opt_usrquota:
-		set_opt(sbi, USRQUOTA);
+		ctx_set_opt(ctx, F2FS_MOUNT_USRQUOTA);
 		break;
 	case Opt_grpquota:
-		set_opt(sbi, GRPQUOTA);
+		ctx_set_opt(ctx, F2FS_MOUNT_GRPQUOTA);
 		break;
 	case Opt_prjquota:
-		set_opt(sbi, PRJQUOTA);
+		ctx_set_opt(ctx, F2FS_MOUNT_PRJQUOTA);
 		break;
 	case Opt_usrjquota:
 		if (!*param->string)
-			ret = f2fs_clear_qf_name(sbi, USRQUOTA);
+			ret = f2fs_unnote_qf_name(fc, USRQUOTA);
 		else
-			ret = f2fs_set_qf_name(sbi, USRQUOTA, param);
+			ret = f2fs_note_qf_name(fc, USRQUOTA, param);
 		if (ret)
 			return ret;
 		break;
 	case Opt_grpjquota:
 		if (!*param->string)
-			ret = f2fs_clear_qf_name(sbi, GRPQUOTA);
+			ret = f2fs_unnote_qf_name(fc, GRPQUOTA);
 		else
-			ret = f2fs_set_qf_name(sbi, GRPQUOTA, param);
+			ret = f2fs_note_qf_name(fc, GRPQUOTA, param);
 		if (ret)
 			return ret;
 		break;
 	case Opt_prjjquota:
 		if (!*param->string)
-			ret = f2fs_clear_qf_name(sbi, PRJQUOTA);
+			ret = f2fs_unnote_qf_name(fc, PRJQUOTA);
 		else
-			ret = f2fs_set_qf_name(sbi, PRJQUOTA, param);
+			ret = f2fs_note_qf_name(fc, PRJQUOTA, param);
 		if (ret)
 			return ret;
 		break;
 	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;
 		break;
 #else
 	case Opt_quota:
@@ -945,19 +993,21 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		break;
 #endif
 	case Opt_alloc:
-		F2FS_OPTION(sbi).alloc_mode = result.uint_32;
+		F2FS_CTX_INFO(ctx).alloc_mode = result.uint_32;
+		ctx->spec_mask |= F2FS_SPEC_alloc_mode;
 		break;
 	case Opt_fsync:
-		F2FS_OPTION(sbi).fsync_mode = result.uint_32;
+		F2FS_CTX_INFO(ctx).fsync_mode = result.uint_32;
+		ctx->spec_mask |= F2FS_SPEC_fsync_mode;
 		break;
 	case Opt_test_dummy_encryption:
-		ret = f2fs_set_test_dummy_encryption(sbi, param, is_remount);
+		ret = f2fs_parse_test_dummy_encryption(param, ctx);
 		if (ret)
 			return ret;
 		break;
 	case Opt_inlinecrypt:
 #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
-		set_opt(sbi, INLINECRYPT);
+		ctx_set_opt(ctx, F2FS_MOUNT_INLINECRYPT);
 #else
 		f2fs_info(NULL, "inline encryption not supported");
 #endif
@@ -971,20 +1021,22 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 				return -EINVAL;
 			if (arg < 0 || arg > 100)
 				return -EINVAL;
-			F2FS_OPTION(sbi).unusable_cap_perc = arg;
-			set_opt(sbi, DISABLE_CHECKPOINT);
+			F2FS_CTX_INFO(ctx).unusable_cap_perc = arg;
+			ctx->spec_mask |= F2FS_SPEC_checkpoint_disable_cap_perc;
+			ctx_set_opt(ctx, F2FS_MOUNT_DISABLE_CHECKPOINT);
 			break;
 		case Opt_checkpoint_disable_cap:
 			if (args->from && match_int(args, &arg))
 				return -EINVAL;
-			F2FS_OPTION(sbi).unusable_cap = arg;
-			set_opt(sbi, DISABLE_CHECKPOINT);
+			F2FS_CTX_INFO(ctx).unusable_cap = arg;
+			ctx->spec_mask |= F2FS_SPEC_checkpoint_disable_cap;
+			ctx_set_opt(ctx, F2FS_MOUNT_DISABLE_CHECKPOINT);
 			break;
 		case Opt_checkpoint_disable:
-			set_opt(sbi, DISABLE_CHECKPOINT);
+			ctx_set_opt(ctx, F2FS_MOUNT_DISABLE_CHECKPOINT);
 			break;
 		case Opt_checkpoint_enable:
-			clear_opt(sbi, DISABLE_CHECKPOINT);
+			ctx_clear_opt(ctx, F2FS_MOUNT_DISABLE_CHECKPOINT);
 			break;
 		default:
 			return -EINVAL;
@@ -992,9 +1044,9 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		break;
 	case Opt_checkpoint_merge:
 		if (result.negated)
-			clear_opt(sbi, MERGE_CHECKPOINT);
+			ctx_clear_opt(ctx, F2FS_MOUNT_MERGE_CHECKPOINT);
 		else
-			set_opt(sbi, MERGE_CHECKPOINT);
+			ctx_set_opt(ctx, F2FS_MOUNT_MERGE_CHECKPOINT);
 		break;
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 	case Opt_compress_algorithm:
@@ -1005,33 +1057,39 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		name = param->string;
 		if (!strcmp(name, "lzo")) {
 #ifdef CONFIG_F2FS_FS_LZO
-			F2FS_OPTION(sbi).compress_level = 0;
-			F2FS_OPTION(sbi).compress_algorithm = COMPRESS_LZO;
+			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)
 				return -EINVAL;
-			F2FS_OPTION(sbi).compress_algorithm = COMPRESS_LZ4;
+			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)
 				return -EINVAL;
-			F2FS_OPTION(sbi).compress_algorithm = COMPRESS_ZSTD;
+			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 = COMPRESS_LZORLE;
+			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
@@ -1049,7 +1107,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.uint_32;
+		F2FS_CTX_INFO(ctx).compress_log_size = result.uint_32;
+		ctx->spec_mask |= F2FS_SPEC_compress_log_size;
 		break;
 	case Opt_compress_extension:
 		if (!f2fs_sb_has_compression(sbi)) {
@@ -1057,8 +1116,8 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			break;
 		}
 		name = param->string;
-		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) {
@@ -1066,13 +1125,14 @@ 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))
 			break;
 
 		ret = strscpy(ext[ext_cnt], name, F2FS_EXTENSION_LEN);
 		if (ret < 0)
 			return ret;
-		F2FS_OPTION(sbi).compress_ext_cnt++;
+		F2FS_CTX_INFO(ctx).compress_ext_cnt++;
+		ctx->spec_mask |= F2FS_SPEC_compress_extension;
 		break;
 	case Opt_nocompress_extension:
 		if (!f2fs_sb_has_compression(sbi)) {
@@ -1080,8 +1140,8 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			break;
 		}
 		name = param->string;
-		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) {
@@ -1089,34 +1149,37 @@ 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))
 			break;
 
 		ret = strscpy(noext[noext_cnt], name, F2FS_EXTENSION_LEN);
 		if (ret < 0)
 			return ret;
-		F2FS_OPTION(sbi).nocompress_ext_cnt++;
+		F2FS_CTX_INFO(ctx).nocompress_ext_cnt++;
+		ctx->spec_mask |= F2FS_SPEC_nocompress_extension;
 		break;
 	case Opt_compress_chksum:
 		if (!f2fs_sb_has_compression(sbi)) {
 			f2fs_info(NULL, "Image doesn't support compression");
 			break;
 		}
-		F2FS_OPTION(sbi).compress_chksum = true;
+		F2FS_CTX_INFO(ctx).compress_chksum = true;
+		ctx->spec_mask |= F2FS_SPEC_compress_chksum;
 		break;
 	case Opt_compress_mode:
 		if (!f2fs_sb_has_compression(sbi)) {
 			f2fs_info(NULL, "Image doesn't support compression");
 			break;
 		}
-		F2FS_OPTION(sbi).compress_mode = result.uint_32;
+		F2FS_CTX_INFO(ctx).compress_mode = result.uint_32;
+		ctx->spec_mask |= F2FS_SPEC_compress_mode;
 		break;
 	case Opt_compress_cache:
 		if (!f2fs_sb_has_compression(sbi)) {
 			f2fs_info(NULL, "Image doesn't support compression");
 			break;
 		}
-		set_opt(sbi, COMPRESS_CACHE);
+		ctx_set_opt(ctx, F2FS_MOUNT_COMPRESS_CACHE);
 		break;
 #else
 	case Opt_compress_algorithm:
@@ -1130,28 +1193,31 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		break;
 #endif
 	case Opt_atgc:
-		set_opt(sbi, ATGC);
+		ctx_set_opt(ctx, F2FS_MOUNT_ATGC);
 		break;
 	case Opt_gc_merge:
 		if (result.negated)
-			clear_opt(sbi, GC_MERGE);
+			ctx_clear_opt(ctx, F2FS_MOUNT_GC_MERGE);
 		else
-			set_opt(sbi, GC_MERGE);
+			ctx_set_opt(ctx, F2FS_MOUNT_GC_MERGE);
 		break;
 	case Opt_discard_unit:
-		F2FS_OPTION(sbi).discard_unit = result.uint_32;
+		F2FS_CTX_INFO(ctx).discard_unit = result.uint_32;
+		ctx->spec_mask |= F2FS_SPEC_discard_unit;
 		break;
 	case Opt_memory_mode:
-		F2FS_OPTION(sbi).memory_mode = result.uint_32;
+		F2FS_CTX_INFO(ctx).memory_mode = result.uint_32;
+		ctx->spec_mask |= F2FS_SPEC_memory_mode;
 		break;
 	case Opt_age_extent_cache:
-		set_opt(sbi, AGE_EXTENT_CACHE);
+		ctx_set_opt(ctx, F2FS_MOUNT_AGE_EXTENT_CACHE);
 		break;
 	case Opt_errors:
-		F2FS_OPTION(sbi).errors = result.uint_32;
+		F2FS_CTX_INFO(ctx).errors = result.uint_32;
+		ctx->spec_mask |= F2FS_SPEC_errors;
 		break;
 	case Opt_nat_bits:
-		set_opt(sbi, NAT_BITS);
+		ctx_set_opt(ctx, F2FS_MOUNT_NAT_BITS);
 		break;
 	}
 	return 0;
@@ -1161,6 +1227,7 @@ static int parse_options(struct f2fs_sb_info *sbi, char *options, bool is_remoun
 {
 	struct fs_parameter param;
 	struct fs_context fc;
+	struct f2fs_fs_context ctx;
 	char *key;
 	int ret;
 
@@ -1169,6 +1236,8 @@ static int parse_options(struct f2fs_sb_info *sbi, char *options, bool is_remoun
 
 	memset(&fc, 0, sizeof(fc));
 	fc.s_fs_info = sbi;
+	fc.fs_private = &ctx;
+
 	if (is_remount)
 		fc.purpose = FS_CONTEXT_FOR_RECONFIGURE;
 
-- 
2.47.0


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

* [PATCH 5/7] f2fs: separate the options parsing and options checking
  2025-04-20 15:24 [PATCH 0/7 V2] f2fs: new mount API conversion Eric Sandeen
                   ` (3 preceding siblings ...)
  2025-04-20 15:25 ` [PATCH 4/7] f2fs: Add f2fs_fs_context to record the mount options Eric Sandeen
@ 2025-04-20 15:25 ` Eric Sandeen
  2025-04-21 16:34   ` kernel test robot
                     ` (2 more replies)
  2025-04-20 15:25 ` [PATCH 6/7] f2fs: introduce fs_context_operation structure Eric Sandeen
                   ` (3 subsequent siblings)
  8 siblings, 3 replies; 20+ messages in thread
From: Eric Sandeen @ 2025-04-20 15:25 UTC (permalink / raw)
  To: linux-f2fs-devel; +Cc: linux-fsdevel, jaegeuk, chao, lihongbo22, Eric Sandeen

From: Hongbo Li <lihongbo22@huawei.com>

The new mount api separates option parsing and super block setup
into two distinct 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 introduced 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>
[sandeen: forward port, minor fixes and updates]
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---
 fs/f2fs/super.c | 694 +++++++++++++++++++++++++++++++++++-------------
 1 file changed, 510 insertions(+), 184 deletions(-)

diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 11bf936b0d3c..818db1e9549b 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -358,6 +358,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)
 {
@@ -531,51 +537,6 @@ static int f2fs_unnote_qf_name(struct fs_context *fc, int qtype)
 	ctx->qname_mask |= 1 << 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,
@@ -634,28 +595,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;
 			}
@@ -747,7 +708,6 @@ 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;
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 	unsigned char (*ext)[F2FS_EXTENSION_LEN];
 	unsigned char (*noext)[F2FS_EXTENSION_LEN];
@@ -755,7 +715,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 #endif
 	substring_t args[MAX_OPT_ARGS];
 	struct fs_parse_result result;
-	int is_remount;
 	char *name;
 	int token, ret, arg;
 
@@ -763,8 +722,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:
 		F2FS_CTX_INFO(ctx).bggc_mode = result.uint_32;
@@ -778,19 +735,10 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		ctx_set_opt(ctx, F2FS_MOUNT_NORECOVERY);
 		break;
 	case Opt_discard:
-		if (result.negated) {
-			if (f2fs_hw_should_discard(sbi)) {
-				f2fs_warn(NULL, "discard is required for zoned block devices");
-				return -EINVAL;
-			}
+		if (result.negated)
 			ctx_clear_opt(ctx, F2FS_MOUNT_DISCARD);
-		} else {
-			if (!f2fs_hw_support_discard(sbi)) {
-				f2fs_warn(NULL, "device does not support discard");
-				break;
-			}
+		else
 			ctx_set_opt(ctx, F2FS_MOUNT_DISCARD);
-		}
 		break;
 	case Opt_noheap:
 	case Opt_heap:
@@ -810,6 +758,12 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			ctx_set_opt(ctx, F2FS_MOUNT_INLINE_XATTR);
 		break;
 	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;
@@ -871,27 +825,18 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		ctx_set_opt(ctx, F2FS_MOUNT_FASTBOOT);
 		break;
 	case Opt_extent_cache:
-		if (result.negated) {
-			if (f2fs_sb_has_device_alias(sbi)) {
-				f2fs_err(sbi, "device aliasing requires extent cache");
-				return -EINVAL;
-			}
+		if (result.negated)
 			ctx_clear_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE);
-		} else
+		else
 			ctx_set_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE);
 		break;
 	case Opt_data_flush:
 		ctx_set_opt(ctx, F2FS_MOUNT_DATA_FLUSH);
 		break;
 	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;
 		break;
 	case Opt_resuid:
 		F2FS_CTX_INFO(ctx).s_resuid = result.uid;
@@ -907,8 +852,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		break;
 #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;
@@ -916,7 +860,7 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		break;
 
 	case Opt_fault_type:
-		if (f2fs_build_fault_attr(sbi, 0, result.int_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;
@@ -1050,10 +994,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		break;
 #ifdef CONFIG_F2FS_FS_COMPRESSION
 	case Opt_compress_algorithm:
-		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(NULL, "Image doesn't support compression");
-			break;
-		}
 		name = param->string;
 		if (!strcmp(name, "lzo")) {
 #ifdef CONFIG_F2FS_FS_LZO
@@ -1097,10 +1037,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 			return -EINVAL;
 		break;
 	case Opt_compress_log_size:
-		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(NULL, "Image doesn't support compression");
-			break;
-		}
 		if (result.uint_32 < MIN_COMPRESS_LOG_SIZE ||
 		    result.uint_32 > MAX_COMPRESS_LOG_SIZE) {
 			f2fs_err(NULL,
@@ -1111,10 +1047,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		ctx->spec_mask |= F2FS_SPEC_compress_log_size;
 		break;
 	case Opt_compress_extension:
-		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(NULL, "Image doesn't support compression");
-			break;
-		}
 		name = param->string;
 		ext = F2FS_CTX_INFO(ctx).extensions;
 		ext_cnt = F2FS_CTX_INFO(ctx).compress_ext_cnt;
@@ -1135,10 +1067,6 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		ctx->spec_mask |= F2FS_SPEC_compress_extension;
 		break;
 	case Opt_nocompress_extension:
-		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(NULL, "Image doesn't support compression");
-			break;
-		}
 		name = param->string;
 		noext = F2FS_CTX_INFO(ctx).noextensions;
 		noext_cnt = F2FS_CTX_INFO(ctx).nocompress_ext_cnt;
@@ -1159,26 +1087,14 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 		ctx->spec_mask |= F2FS_SPEC_nocompress_extension;
 		break;
 	case Opt_compress_chksum:
-		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(NULL, "Image doesn't support compression");
-			break;
-		}
 		F2FS_CTX_INFO(ctx).compress_chksum = true;
 		ctx->spec_mask |= F2FS_SPEC_compress_chksum;
 		break;
 	case Opt_compress_mode:
-		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(NULL, "Image doesn't support compression");
-			break;
-		}
 		F2FS_CTX_INFO(ctx).compress_mode = result.uint_32;
 		ctx->spec_mask |= F2FS_SPEC_compress_mode;
 		break;
 	case Opt_compress_cache:
-		if (!f2fs_sb_has_compression(sbi)) {
-			f2fs_info(NULL, "Image doesn't support compression");
-			break;
-		}
 		ctx_set_opt(ctx, F2FS_MOUNT_COMPRESS_CACHE);
 		break;
 #else
@@ -1223,24 +1139,15 @@ static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
 	return 0;
 }
 
-static int parse_options(struct f2fs_sb_info *sbi, char *options, bool is_remount)
+static int parse_options(struct fs_context *fc, char *options)
 {
 	struct fs_parameter param;
-	struct fs_context fc;
-	struct f2fs_fs_context ctx;
 	char *key;
 	int ret;
 
 	if (!options)
 		return 0;
 
-	memset(&fc, 0, sizeof(fc));
-	fc.s_fs_info = sbi;
-	fc.fs_private = &ctx;
-
-	if (is_remount)
-		fc.purpose = FS_CONTEXT_FOR_RECONFIGURE;
-
 	while ((key = strsep(&options, ",")) != NULL) {
 		if (*key) {
 			size_t v_len = 0;
@@ -1264,7 +1171,7 @@ static int parse_options(struct f2fs_sb_info *sbi, char *options, bool is_remoun
 			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;
@@ -1273,24 +1180,293 @@ static int parse_options(struct f2fs_sb_info *sbi, char *options, bool is_remoun
 	return 0;
 }
 
-static int f2fs_validate_options(struct f2fs_sb_info *sbi)
+/*
+ * 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))
+ #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);
+	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);
+				kfree(F2FS_CTX_INFO(ctx).s_qf_names[i]);
+				F2FS_CTX_INFO(ctx).s_qf_names[i] = NULL;
+			}
+		}
+	}
+
+	/* 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
+}
+
+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_sb_has_device_alias(sbi)) {
+		f2fs_err(sbi, "device aliasing requires extent cache");
+		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(NULL,
+		f2fs_err(sbi,
 			"Filesystem with casefold feature cannot be mounted without CONFIG_UNICODE");
 		return -EINVAL;
 	}
@@ -1302,75 +1478,210 @@ static int f2fs_validate_options(struct f2fs_sb_info *sbi)
 	 */
 	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");
+		f2fs_err(sbi, "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");
-		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");
-			return -EINVAL;
-		}
-		if (!test_opt(sbi, INLINE_XATTR)) {
-			f2fs_err(NULL, "inline_xattr_size option should be set with inline_xattr option");
+			f2fs_err(sbi, "extra_attr or flexible_inline_xattr feature is off");
 			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_CTX_INFO(ctx).fs_mode == FS_MODE_LFS) {
+		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;
+}
 
-	if (test_opt(sbi, NORECOVERY) && !f2fs_readonly(sbi->sb)) {
-		f2fs_err(sbi, "norecovery requires readonly mount");
-		return -EINVAL;
+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;
 	}
 
-	return 0;
+	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)
@@ -2272,6 +2583,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;
@@ -2328,11 +2641,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(sbi, 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) {
@@ -2343,11 +2667,6 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 		goto restore_opts;
 	}
 #endif
-
-	err = f2fs_validate_options(sbi);
-	if (err)
-		goto restore_opts;
-
 	/* flush outstanding errors before changing fs state */
 	flush_work(&sbi->s_error_work);
 
@@ -4426,6 +4745,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;
@@ -4442,6 +4763,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);
@@ -4499,14 +4823,16 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
 		goto free_sb_buf;
 	}
 
-	err = parse_options(sbi, options, false);
+	err = parse_options(&fc, options);
 	if (err)
 		goto free_options;
 
-	err = f2fs_validate_options(sbi);
-	if (err)
+	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.47.0


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

* [PATCH 6/7] f2fs: introduce fs_context_operation structure
  2025-04-20 15:24 [PATCH 0/7 V2] f2fs: new mount API conversion Eric Sandeen
                   ` (4 preceding siblings ...)
  2025-04-20 15:25 ` [PATCH 5/7] f2fs: separate the options parsing and options checking Eric Sandeen
@ 2025-04-20 15:25 ` Eric Sandeen
  2025-04-20 15:25 ` [PATCH 7/7] f2fs: switch to the new mount api Eric Sandeen
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 20+ messages in thread
From: Eric Sandeen @ 2025-04-20 15:25 UTC (permalink / raw)
  To: linux-f2fs-devel; +Cc: linux-fsdevel, jaegeuk, chao, lihongbo22, Eric Sandeen

From: Hongbo Li <lihongbo22@huawei.com>

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>
[sandeen: forward port]
Signed-off-by: Eric Sandeen <sandeen@redhat.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 818db1e9549b..21042a02459f 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -705,7 +705,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
@@ -1171,7 +1171,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;
@@ -5273,6 +5273,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.47.0


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

* [PATCH 7/7] f2fs: switch to the new mount api
  2025-04-20 15:24 [PATCH 0/7 V2] f2fs: new mount API conversion Eric Sandeen
                   ` (5 preceding siblings ...)
  2025-04-20 15:25 ` [PATCH 6/7] f2fs: introduce fs_context_operation structure Eric Sandeen
@ 2025-04-20 15:25 ` Eric Sandeen
  2025-04-22  2:39 ` [PATCH 0/7 V2] f2fs: new mount API conversion Eric Sandeen
  2025-07-11 16:30 ` [f2fs-dev] " patchwork-bot+f2fs
  8 siblings, 0 replies; 20+ messages in thread
From: Eric Sandeen @ 2025-04-20 15:25 UTC (permalink / raw)
  To: linux-f2fs-devel; +Cc: linux-fsdevel, jaegeuk, chao, lihongbo22, Eric Sandeen

From: Hongbo Li <lihongbo22@huawei.com>

The new mount api will execute .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>
[sandeen: forward port]
Signed-off-by: Eric Sandeen <sandeen@redhat.com>
---
 fs/f2fs/super.c | 156 +++++++++++++++++++-----------------------------
 1 file changed, 62 insertions(+), 94 deletions(-)

diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 21042a02459f..28a7aa01d009 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -1139,47 +1139,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)
-		return 0;
-
-	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;
-		}
-	}
-	return 0;
-}
-
 /*
  * Check quota settings consistency.
  */
@@ -2579,13 +2538,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;
@@ -2631,7 +2589,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);
@@ -2641,21 +2599,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) &&
@@ -2674,20 +2622,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)) {
@@ -2743,7 +2691,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;
@@ -2754,7 +2702,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) {
@@ -2768,7 +2716,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);
@@ -2781,7 +2729,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;
@@ -2823,7 +2771,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 {
@@ -2850,7 +2798,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
 		(test_opt(sbi, POSIX_ACL) ? SB_POSIXACL : 0);
 
 	limit_reserve_root(sbi);
-	*flags = (*flags & ~SB_LAZYTIME) | (sb->s_flags & SB_LAZYTIME);
+	fc->sb_flags = (flags & ~SB_LAZYTIME) | (sb->s_flags & SB_LAZYTIME);
 
 	sbi->umount_lock_holder = NULL;
 	return 0;
@@ -3519,7 +3467,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,
 };
 
@@ -4741,16 +4688,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;
@@ -4763,9 +4707,6 @@ 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);
@@ -4816,22 +4757,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);
@@ -5156,7 +5087,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) {
@@ -5251,7 +5181,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:
@@ -5267,14 +5196,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)
@@ -5318,10 +5272,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.47.0


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

* Re: [PATCH 5/7] f2fs: separate the options parsing and options checking
  2025-04-20 15:25 ` [PATCH 5/7] f2fs: separate the options parsing and options checking Eric Sandeen
@ 2025-04-21 16:34   ` kernel test robot
  2025-04-21 17:37   ` kernel test robot
  2025-04-21 17:37   ` kernel test robot
  2 siblings, 0 replies; 20+ messages in thread
From: kernel test robot @ 2025-04-21 16:34 UTC (permalink / raw)
  To: Eric Sandeen, linux-f2fs-devel
  Cc: oe-kbuild-all, linux-fsdevel, jaegeuk, chao, lihongbo22,
	Eric Sandeen

Hi Eric,

kernel test robot noticed the following build warnings:

[auto build test WARNING on v6.15-rc3]
[also build test WARNING on linus/master]
[cannot apply to jaegeuk-f2fs/dev-test jaegeuk-f2fs/dev next-20250417]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Eric-Sandeen/f2fs-Add-fs-parameter-specifications-for-mount-options/20250421-220156
base:   v6.15-rc3
patch link:    https://lore.kernel.org/r/20250420154647.1233033-6-sandeen%40redhat.com
patch subject: [PATCH 5/7] f2fs: separate the options parsing and options checking
config: arc-randconfig-001-20250421 (https://download.01.org/0day-ci/archive/20250422/202504220033.8EDCfvWU-lkp@intel.com/config)
compiler: arc-linux-gcc (GCC) 11.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250422/202504220033.8EDCfvWU-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202504220033.8EDCfvWU-lkp@intel.com/

All warnings (new ones prefixed by >>):

   In file included from include/linux/printk.h:7,
                    from include/asm-generic/bug.h:22,
                    from arch/arc/include/asm/bug.h:30,
                    from include/linux/bug.h:5,
                    from include/linux/thread_info.h:13,
                    from include/asm-generic/preempt.h:5,
                    from ./arch/arc/include/generated/asm/preempt.h:1,
                    from include/linux/preempt.h:79,
                    from include/linux/spinlock.h:56,
                    from include/linux/mmzone.h:8,
                    from include/linux/gfp.h:7,
                    from include/linux/umh.h:4,
                    from include/linux/kmod.h:9,
                    from include/linux/module.h:17,
                    from fs/f2fs/super.c:8:
   fs/f2fs/super.c: In function 'handle_mount_opt':
>> include/linux/kern_levels.h:5:25: warning: format '%lu' expects argument of type 'long unsigned int', but argument 4 has type 'unsigned int' [-Wformat=]
       5 | #define KERN_SOH        "\001"          /* ASCII Start Of Header */
         |                         ^~~~~~
   include/linux/kern_levels.h:11:25: note: in expansion of macro 'KERN_SOH'
      11 | #define KERN_ERR        KERN_SOH "3"    /* error conditions */
         |                         ^~~~~~~~
   fs/f2fs/f2fs.h:1871:33: note: in expansion of macro 'KERN_ERR'
    1871 |         f2fs_printk(sbi, false, KERN_ERR fmt, ##__VA_ARGS__)
         |                                 ^~~~~~~~
   fs/f2fs/super.c:763:25: note: in expansion of macro 'f2fs_err'
     763 |                         f2fs_err(NULL, "inline xattr size is out of range: %lu ~ %lu",
         |                         ^~~~~~~~
   fs/f2fs/super.c:718:15: warning: unused variable 'name' [-Wunused-variable]
     718 |         char *name;
         |               ^~~~


vim +5 include/linux/kern_levels.h

314ba3520e513a7 Joe Perches 2012-07-30  4  
04d2c8c83d0e3ac Joe Perches 2012-07-30 @5  #define KERN_SOH	"\001"		/* ASCII Start Of Header */
04d2c8c83d0e3ac Joe Perches 2012-07-30  6  #define KERN_SOH_ASCII	'\001'
04d2c8c83d0e3ac Joe Perches 2012-07-30  7  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* Re: [PATCH 5/7] f2fs: separate the options parsing and options checking
  2025-04-20 15:25 ` [PATCH 5/7] f2fs: separate the options parsing and options checking Eric Sandeen
  2025-04-21 16:34   ` kernel test robot
@ 2025-04-21 17:37   ` kernel test robot
  2025-04-21 17:37   ` kernel test robot
  2 siblings, 0 replies; 20+ messages in thread
From: kernel test robot @ 2025-04-21 17:37 UTC (permalink / raw)
  To: Eric Sandeen, linux-f2fs-devel
  Cc: llvm, oe-kbuild-all, linux-fsdevel, jaegeuk, chao, lihongbo22,
	Eric Sandeen

Hi Eric,

kernel test robot noticed the following build warnings:

[auto build test WARNING on v6.15-rc3]
[also build test WARNING on linus/master]
[cannot apply to jaegeuk-f2fs/dev-test jaegeuk-f2fs/dev next-20250417]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Eric-Sandeen/f2fs-Add-fs-parameter-specifications-for-mount-options/20250421-220156
base:   v6.15-rc3
patch link:    https://lore.kernel.org/r/20250420154647.1233033-6-sandeen%40redhat.com
patch subject: [PATCH 5/7] f2fs: separate the options parsing and options checking
config: i386-buildonly-randconfig-003-20250421 (https://download.01.org/0day-ci/archive/20250422/202504220117.vULD83Cm-lkp@intel.com/config)
compiler: clang version 20.1.2 (https://github.com/llvm/llvm-project 58df0ef89dd64126512e4ee27b4ac3fd8ddf6247)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250422/202504220117.vULD83Cm-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202504220117.vULD83Cm-lkp@intel.com/

All warnings (new ones prefixed by >>):

>> fs/f2fs/super.c:764:5: warning: format specifies type 'unsigned long' but the argument has type 'unsigned int' [-Wformat]
     763 |                         f2fs_err(NULL, "inline xattr size is out of range: %lu ~ %lu",
         |                                                                            ~~~
         |                                                                            %u
     764 |                                 MIN_INLINE_XATTR_SIZE, MAX_INLINE_XATTR_SIZE);
         |                                 ^~~~~~~~~~~~~~~~~~~~~
   fs/f2fs/f2fs.h:1871:42: note: expanded from macro 'f2fs_err'
    1871 |         f2fs_printk(sbi, false, KERN_ERR fmt, ##__VA_ARGS__)
         |                                          ~~~    ^~~~~~~~~~~
   fs/f2fs/xattr.h:86:31: note: expanded from macro 'MIN_INLINE_XATTR_SIZE'
      86 | #define MIN_INLINE_XATTR_SIZE (sizeof(struct f2fs_xattr_header) / sizeof(__le32))
         |                               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   fs/f2fs/super.c:964:8: warning: variable 'arg' is used uninitialized whenever '&&' condition is false [-Wsometimes-uninitialized]
     964 |                         if (args->from && match_int(args, &arg))
         |                             ^~~~~~~~~~
   fs/f2fs/super.c:966:8: note: uninitialized use occurs here
     966 |                         if (arg < 0 || arg > 100)
         |                             ^~~
   fs/f2fs/super.c:964:8: note: remove the '&&' if its condition is always true
     964 |                         if (args->from && match_int(args, &arg))
         |                             ^~~~~~~~~~~~~
   fs/f2fs/super.c:719:21: note: initialize the variable 'arg' to silence this warning
     719 |         int token, ret, arg;
         |                            ^
         |                             = 0
   fs/f2fs/super.c:1285:20: error: use of undeclared identifier 'sbi'
    1285 |         if (f2fs_readonly(sbi->sb))
         |                           ^
   fs/f2fs/super.c:1287:28: error: use of undeclared identifier 'sbi'; did you mean 'sb'?
    1287 |         if (f2fs_sb_has_quota_ino(sbi)) {
         |                                   ^~~
         |                                   sb
   fs/f2fs/super.c:1187:26: note: 'sb' declared here
    1187 |                                         struct super_block *sb)
         |                                                             ^
   fs/f2fs/super.c:1288:13: error: use of undeclared identifier 'sbi'; did you mean 'sb'?
    1288 |                 f2fs_info(sbi, "Filesystem with quota feature cannot be mounted RDWR without CONFIG_QUOTA");
         |                           ^~~
         |                           sb
   fs/f2fs/f2fs.h:1877:14: note: expanded from macro 'f2fs_info'
    1877 |         f2fs_printk(sbi, false, KERN_INFO fmt, ##__VA_ARGS__)
         |                     ^
   fs/f2fs/super.c:1187:26: note: 'sb' declared here
    1187 |                                         struct super_block *sb)
         |                                                             ^
   fs/f2fs/super.c:1291:32: error: use of undeclared identifier 'sbi'; did you mean 'sb'?
    1291 |         if (f2fs_sb_has_project_quota(sbi)) {
         |                                       ^~~
         |                                       sb
   fs/f2fs/super.c:1187:26: note: 'sb' declared here
    1187 |                                         struct super_block *sb)
         |                                                             ^
   fs/f2fs/super.c:1292:12: error: use of undeclared identifier 'sbi'; did you mean 'sb'?
    1292 |                 f2fs_err(sbi, "Filesystem with project quota feature cannot be mounted RDWR without CONFIG_QUOTA");
         |                          ^~~
         |                          sb
   fs/f2fs/f2fs.h:1871:14: note: expanded from macro 'f2fs_err'
    1871 |         f2fs_printk(sbi, false, KERN_ERR fmt, ##__VA_ARGS__)
         |                     ^
   fs/f2fs/super.c:1187:26: note: 'sb' declared here
    1187 |                                         struct super_block *sb)
         |                                                             ^
   2 warnings and 5 errors generated.


vim +764 fs/f2fs/super.c

   707	
   708	static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
   709	{
   710		struct f2fs_fs_context *ctx = fc->fs_private;
   711	#ifdef CONFIG_F2FS_FS_COMPRESSION
   712		unsigned char (*ext)[F2FS_EXTENSION_LEN];
   713		unsigned char (*noext)[F2FS_EXTENSION_LEN];
   714		int ext_cnt, noext_cnt;
   715	#endif
   716		substring_t args[MAX_OPT_ARGS];
   717		struct fs_parse_result result;
   718		char *name;
   719		int token, ret, arg;
   720	
   721		token = fs_parse(fc, f2fs_param_specs, param, &result);
   722		if (token < 0)
   723			return token;
   724	
   725		switch (token) {
   726		case Opt_gc_background:
   727			F2FS_CTX_INFO(ctx).bggc_mode = result.uint_32;
   728			ctx->spec_mask |= F2FS_SPEC_background_gc;
   729			break;
   730		case Opt_disable_roll_forward:
   731			ctx_set_opt(ctx, F2FS_MOUNT_DISABLE_ROLL_FORWARD);
   732			break;
   733		case Opt_norecovery:
   734			/* requires ro mount, checked in f2fs_validate_options */
   735			ctx_set_opt(ctx, F2FS_MOUNT_NORECOVERY);
   736			break;
   737		case Opt_discard:
   738			if (result.negated)
   739				ctx_clear_opt(ctx, F2FS_MOUNT_DISCARD);
   740			else
   741				ctx_set_opt(ctx, F2FS_MOUNT_DISCARD);
   742			break;
   743		case Opt_noheap:
   744		case Opt_heap:
   745			f2fs_warn(NULL, "heap/no_heap options were deprecated");
   746			break;
   747	#ifdef CONFIG_F2FS_FS_XATTR
   748		case Opt_user_xattr:
   749			if (result.negated)
   750				ctx_clear_opt(ctx, F2FS_MOUNT_XATTR_USER);
   751			else
   752				ctx_set_opt(ctx, F2FS_MOUNT_XATTR_USER);
   753			break;
   754		case Opt_inline_xattr:
   755			if (result.negated)
   756				ctx_clear_opt(ctx, F2FS_MOUNT_INLINE_XATTR);
   757			else
   758				ctx_set_opt(ctx, F2FS_MOUNT_INLINE_XATTR);
   759			break;
   760		case Opt_inline_xattr_size:
   761			if (result.int_32 < MIN_INLINE_XATTR_SIZE ||
   762				result.int_32 > MAX_INLINE_XATTR_SIZE) {
   763				f2fs_err(NULL, "inline xattr size is out of range: %lu ~ %lu",
 > 764					MIN_INLINE_XATTR_SIZE, MAX_INLINE_XATTR_SIZE);
   765				return -EINVAL;
   766			}
   767			ctx_set_opt(ctx, F2FS_MOUNT_INLINE_XATTR_SIZE);
   768			F2FS_CTX_INFO(ctx).inline_xattr_size = result.int_32;
   769			ctx->spec_mask |= F2FS_SPEC_inline_xattr_size;
   770			break;
   771	#else
   772		case Opt_user_xattr:
   773		case Opt_inline_xattr:
   774		case Opt_inline_xattr_size:
   775			f2fs_info(NULL, "%s options not supported", param->key);
   776			break;
   777	#endif
   778	#ifdef CONFIG_F2FS_FS_POSIX_ACL
   779		case Opt_acl:
   780			if (result.negated)
   781				ctx_clear_opt(ctx, F2FS_MOUNT_POSIX_ACL);
   782			else
   783				ctx_set_opt(ctx, F2FS_MOUNT_POSIX_ACL);
   784			break;
   785	#else
   786		case Opt_acl:
   787			f2fs_info(NULL, "%s options not supported", param->key);
   788			break;
   789	#endif
   790		case Opt_active_logs:
   791			if (result.int_32 != 2 && result.int_32 != 4 &&
   792				result.int_32 != NR_CURSEG_PERSIST_TYPE)
   793				return -EINVAL;
   794			ctx->spec_mask |= F2FS_SPEC_active_logs;
   795			F2FS_CTX_INFO(ctx).active_logs = result.int_32;
   796			break;
   797		case Opt_disable_ext_identify:
   798			ctx_set_opt(ctx, F2FS_MOUNT_DISABLE_EXT_IDENTIFY);
   799			break;
   800		case Opt_inline_data:
   801			if (result.negated)
   802				ctx_clear_opt(ctx, F2FS_MOUNT_INLINE_DATA);
   803			else
   804				ctx_set_opt(ctx, F2FS_MOUNT_INLINE_DATA);
   805			break;
   806		case Opt_inline_dentry:
   807			if (result.negated)
   808				ctx_clear_opt(ctx, F2FS_MOUNT_INLINE_DENTRY);
   809			else
   810				ctx_set_opt(ctx, F2FS_MOUNT_INLINE_DENTRY);
   811			break;
   812		case Opt_flush_merge:
   813			if (result.negated)
   814				ctx_clear_opt(ctx, F2FS_MOUNT_FLUSH_MERGE);
   815			else
   816				ctx_set_opt(ctx, F2FS_MOUNT_FLUSH_MERGE);
   817			break;
   818		case Opt_barrier:
   819			if (result.negated)
   820				ctx_set_opt(ctx, F2FS_MOUNT_NOBARRIER);
   821			else
   822				ctx_clear_opt(ctx, F2FS_MOUNT_NOBARRIER);
   823			break;
   824		case Opt_fastboot:
   825			ctx_set_opt(ctx, F2FS_MOUNT_FASTBOOT);
   826			break;
   827		case Opt_extent_cache:
   828			if (result.negated)
   829				ctx_clear_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE);
   830			else
   831				ctx_set_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE);
   832			break;
   833		case Opt_data_flush:
   834			ctx_set_opt(ctx, F2FS_MOUNT_DATA_FLUSH);
   835			break;
   836		case Opt_reserve_root:
   837			ctx_set_opt(ctx, F2FS_MOUNT_RESERVE_ROOT);
   838			F2FS_CTX_INFO(ctx).root_reserved_blocks = result.uint_32;
   839			ctx->spec_mask |= F2FS_SPEC_reserve_root;
   840			break;
   841		case Opt_resuid:
   842			F2FS_CTX_INFO(ctx).s_resuid = result.uid;
   843			ctx->spec_mask |= F2FS_SPEC_resuid;
   844			break;
   845		case Opt_resgid:
   846			F2FS_CTX_INFO(ctx).s_resgid = result.gid;
   847			ctx->spec_mask |= F2FS_SPEC_resgid;
   848			break;
   849		case Opt_mode:
   850			F2FS_CTX_INFO(ctx).fs_mode = result.uint_32;
   851			ctx->spec_mask |= F2FS_SPEC_mode;
   852			break;
   853	#ifdef CONFIG_F2FS_FAULT_INJECTION
   854		case Opt_fault_injection:
   855			if (result.int_32 > INT_MAX)
   856				return -EINVAL;
   857			F2FS_CTX_INFO(ctx).fault_info.inject_rate = result.int_32;
   858			ctx->spec_mask |= F2FS_SPEC_fault_injection;
   859			ctx_set_opt(ctx, F2FS_MOUNT_FAULT_INJECTION);
   860			break;
   861	
   862		case Opt_fault_type:
   863			if (result.uint_32 > BIT(FAULT_MAX))
   864				return -EINVAL;
   865			F2FS_CTX_INFO(ctx).fault_info.inject_type = result.uint_32;
   866			ctx->spec_mask |= F2FS_SPEC_fault_type;
   867			ctx_set_opt(ctx, F2FS_MOUNT_FAULT_INJECTION);
   868			break;
   869	#else
   870		case Opt_fault_injection:
   871		case Opt_fault_type:
   872			f2fs_info(NULL, "%s options not supported", param->key);
   873			break;
   874	#endif
   875		case Opt_lazytime:
   876			if (result.negated)
   877				ctx_clear_opt(ctx, F2FS_MOUNT_LAZYTIME);
   878			else
   879				ctx_set_opt(ctx, F2FS_MOUNT_LAZYTIME);
   880			break;
   881	#ifdef CONFIG_QUOTA
   882		case Opt_quota:
   883			if (result.negated) {
   884				ctx_clear_opt(ctx, F2FS_MOUNT_QUOTA);
   885				ctx_clear_opt(ctx, F2FS_MOUNT_USRQUOTA);
   886				ctx_clear_opt(ctx, F2FS_MOUNT_GRPQUOTA);
   887				ctx_clear_opt(ctx, F2FS_MOUNT_PRJQUOTA);
   888			} else
   889				ctx_set_opt(ctx, F2FS_MOUNT_USRQUOTA);
   890			break;
   891		case Opt_usrquota:
   892			ctx_set_opt(ctx, F2FS_MOUNT_USRQUOTA);
   893			break;
   894		case Opt_grpquota:
   895			ctx_set_opt(ctx, F2FS_MOUNT_GRPQUOTA);
   896			break;
   897		case Opt_prjquota:
   898			ctx_set_opt(ctx, F2FS_MOUNT_PRJQUOTA);
   899			break;
   900		case Opt_usrjquota:
   901			if (!*param->string)
   902				ret = f2fs_unnote_qf_name(fc, USRQUOTA);
   903			else
   904				ret = f2fs_note_qf_name(fc, USRQUOTA, param);
   905			if (ret)
   906				return ret;
   907			break;
   908		case Opt_grpjquota:
   909			if (!*param->string)
   910				ret = f2fs_unnote_qf_name(fc, GRPQUOTA);
   911			else
   912				ret = f2fs_note_qf_name(fc, GRPQUOTA, param);
   913			if (ret)
   914				return ret;
   915			break;
   916		case Opt_prjjquota:
   917			if (!*param->string)
   918				ret = f2fs_unnote_qf_name(fc, PRJQUOTA);
   919			else
   920				ret = f2fs_note_qf_name(fc, PRJQUOTA, param);
   921			if (ret)
   922				return ret;
   923			break;
   924		case Opt_jqfmt:
   925			F2FS_CTX_INFO(ctx).s_jquota_fmt = result.int_32;
   926			ctx->spec_mask |= F2FS_SPEC_jqfmt;
   927			break;
   928	#else
   929		case Opt_quota:
   930		case Opt_usrquota:
   931		case Opt_grpquota:
   932		case Opt_prjquota:
   933		case Opt_usrjquota:
   934		case Opt_grpjquota:
   935		case Opt_prjjquota:
   936			f2fs_info(NULL, "quota operations not supported");
   937			break;
   938	#endif
   939		case Opt_alloc:
   940			F2FS_CTX_INFO(ctx).alloc_mode = result.uint_32;
   941			ctx->spec_mask |= F2FS_SPEC_alloc_mode;
   942			break;
   943		case Opt_fsync:
   944			F2FS_CTX_INFO(ctx).fsync_mode = result.uint_32;
   945			ctx->spec_mask |= F2FS_SPEC_fsync_mode;
   946			break;
   947		case Opt_test_dummy_encryption:
   948			ret = f2fs_parse_test_dummy_encryption(param, ctx);
   949			if (ret)
   950				return ret;
   951			break;
   952		case Opt_inlinecrypt:
   953	#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
   954			ctx_set_opt(ctx, F2FS_MOUNT_INLINECRYPT);
   955	#else
   956			f2fs_info(NULL, "inline encryption not supported");
   957	#endif
   958			break;
   959		case Opt_checkpoint:
   960			/* revert to match_table for checkpoint= options */
   961			token = match_token(param->string, f2fs_checkpoint_tokens, args);
   962			switch (token) {
   963			case Opt_checkpoint_disable_cap_perc:
   964				if (args->from && match_int(args, &arg))
   965					return -EINVAL;
   966				if (arg < 0 || arg > 100)
   967					return -EINVAL;
   968				F2FS_CTX_INFO(ctx).unusable_cap_perc = arg;
   969				ctx->spec_mask |= F2FS_SPEC_checkpoint_disable_cap_perc;
   970				ctx_set_opt(ctx, F2FS_MOUNT_DISABLE_CHECKPOINT);
   971				break;
   972			case Opt_checkpoint_disable_cap:
   973				if (args->from && match_int(args, &arg))
   974					return -EINVAL;
   975				F2FS_CTX_INFO(ctx).unusable_cap = arg;
   976				ctx->spec_mask |= F2FS_SPEC_checkpoint_disable_cap;
   977				ctx_set_opt(ctx, F2FS_MOUNT_DISABLE_CHECKPOINT);
   978				break;
   979			case Opt_checkpoint_disable:
   980				ctx_set_opt(ctx, F2FS_MOUNT_DISABLE_CHECKPOINT);
   981				break;
   982			case Opt_checkpoint_enable:
   983				ctx_clear_opt(ctx, F2FS_MOUNT_DISABLE_CHECKPOINT);
   984				break;
   985			default:
   986				return -EINVAL;
   987			}
   988			break;
   989		case Opt_checkpoint_merge:
   990			if (result.negated)
   991				ctx_clear_opt(ctx, F2FS_MOUNT_MERGE_CHECKPOINT);
   992			else
   993				ctx_set_opt(ctx, F2FS_MOUNT_MERGE_CHECKPOINT);
   994			break;
   995	#ifdef CONFIG_F2FS_FS_COMPRESSION
   996		case Opt_compress_algorithm:
   997			name = param->string;
   998			if (!strcmp(name, "lzo")) {
   999	#ifdef CONFIG_F2FS_FS_LZO
  1000				F2FS_CTX_INFO(ctx).compress_level = 0;
  1001				F2FS_CTX_INFO(ctx).compress_algorithm = COMPRESS_LZO;
  1002				ctx->spec_mask |= F2FS_SPEC_compress_level;
  1003				ctx->spec_mask |= F2FS_SPEC_compress_algorithm;
  1004	#else
  1005				f2fs_info(NULL, "kernel doesn't support lzo compression");
  1006	#endif
  1007			} else if (!strncmp(name, "lz4", 3)) {
  1008	#ifdef CONFIG_F2FS_FS_LZ4
  1009				ret = f2fs_set_lz4hc_level(ctx, name);
  1010				if (ret)
  1011					return -EINVAL;
  1012				F2FS_CTX_INFO(ctx).compress_algorithm = COMPRESS_LZ4;
  1013				ctx->spec_mask |= F2FS_SPEC_compress_algorithm;
  1014	#else
  1015				f2fs_info(NULL, "kernel doesn't support lz4 compression");
  1016	#endif
  1017			} else if (!strncmp(name, "zstd", 4)) {
  1018	#ifdef CONFIG_F2FS_FS_ZSTD
  1019				ret = f2fs_set_zstd_level(ctx, name);
  1020				if (ret)
  1021					return -EINVAL;
  1022				F2FS_CTX_INFO(ctx).compress_algorithm = COMPRESS_ZSTD;
  1023				ctx->spec_mask |= F2FS_SPEC_compress_algorithm;
  1024	#else
  1025				f2fs_info(NULL, "kernel doesn't support zstd compression");
  1026	#endif
  1027			} else if (!strcmp(name, "lzo-rle")) {
  1028	#ifdef CONFIG_F2FS_FS_LZORLE
  1029				F2FS_CTX_INFO(ctx).compress_level = 0;
  1030				F2FS_CTX_INFO(ctx).compress_algorithm = COMPRESS_LZORLE;
  1031				ctx->spec_mask |= F2FS_SPEC_compress_level;
  1032				ctx->spec_mask |= F2FS_SPEC_compress_algorithm;
  1033	#else
  1034				f2fs_info(NULL, "kernel doesn't support lzorle compression");
  1035	#endif
  1036			} else
  1037				return -EINVAL;
  1038			break;
  1039		case Opt_compress_log_size:
  1040			if (result.uint_32 < MIN_COMPRESS_LOG_SIZE ||
  1041			    result.uint_32 > MAX_COMPRESS_LOG_SIZE) {
  1042				f2fs_err(NULL,
  1043					"Compress cluster log size is out of range");
  1044				return -EINVAL;
  1045			}
  1046			F2FS_CTX_INFO(ctx).compress_log_size = result.uint_32;
  1047			ctx->spec_mask |= F2FS_SPEC_compress_log_size;
  1048			break;
  1049		case Opt_compress_extension:
  1050			name = param->string;
  1051			ext = F2FS_CTX_INFO(ctx).extensions;
  1052			ext_cnt = F2FS_CTX_INFO(ctx).compress_ext_cnt;
  1053	
  1054			if (strlen(name) >= F2FS_EXTENSION_LEN ||
  1055			    ext_cnt >= COMPRESS_EXT_NUM) {
  1056				f2fs_err(NULL, "invalid extension length/number");
  1057				return -EINVAL;
  1058			}
  1059	
  1060			if (is_compress_extension_exist(&ctx->info, name, true))
  1061				break;
  1062	
  1063			ret = strscpy(ext[ext_cnt], name, F2FS_EXTENSION_LEN);
  1064			if (ret < 0)
  1065				return ret;
  1066			F2FS_CTX_INFO(ctx).compress_ext_cnt++;
  1067			ctx->spec_mask |= F2FS_SPEC_compress_extension;
  1068			break;
  1069		case Opt_nocompress_extension:
  1070			name = param->string;
  1071			noext = F2FS_CTX_INFO(ctx).noextensions;
  1072			noext_cnt = F2FS_CTX_INFO(ctx).nocompress_ext_cnt;
  1073	
  1074			if (strlen(name) >= F2FS_EXTENSION_LEN ||
  1075				noext_cnt >= COMPRESS_EXT_NUM) {
  1076				f2fs_err(NULL, "invalid extension length/number");
  1077				return -EINVAL;
  1078			}
  1079	
  1080			if (is_compress_extension_exist(&ctx->info, name, false))
  1081				break;
  1082	
  1083			ret = strscpy(noext[noext_cnt], name, F2FS_EXTENSION_LEN);
  1084			if (ret < 0)
  1085				return ret;
  1086			F2FS_CTX_INFO(ctx).nocompress_ext_cnt++;
  1087			ctx->spec_mask |= F2FS_SPEC_nocompress_extension;
  1088			break;
  1089		case Opt_compress_chksum:
  1090			F2FS_CTX_INFO(ctx).compress_chksum = true;
  1091			ctx->spec_mask |= F2FS_SPEC_compress_chksum;
  1092			break;
  1093		case Opt_compress_mode:
  1094			F2FS_CTX_INFO(ctx).compress_mode = result.uint_32;
  1095			ctx->spec_mask |= F2FS_SPEC_compress_mode;
  1096			break;
  1097		case Opt_compress_cache:
  1098			ctx_set_opt(ctx, F2FS_MOUNT_COMPRESS_CACHE);
  1099			break;
  1100	#else
  1101		case Opt_compress_algorithm:
  1102		case Opt_compress_log_size:
  1103		case Opt_compress_extension:
  1104		case Opt_nocompress_extension:
  1105		case Opt_compress_chksum:
  1106		case Opt_compress_mode:
  1107		case Opt_compress_cache:
  1108			f2fs_info(NULL, "compression options not supported");
  1109			break;
  1110	#endif
  1111		case Opt_atgc:
  1112			ctx_set_opt(ctx, F2FS_MOUNT_ATGC);
  1113			break;
  1114		case Opt_gc_merge:
  1115			if (result.negated)
  1116				ctx_clear_opt(ctx, F2FS_MOUNT_GC_MERGE);
  1117			else
  1118				ctx_set_opt(ctx, F2FS_MOUNT_GC_MERGE);
  1119			break;
  1120		case Opt_discard_unit:
  1121			F2FS_CTX_INFO(ctx).discard_unit = result.uint_32;
  1122			ctx->spec_mask |= F2FS_SPEC_discard_unit;
  1123			break;
  1124		case Opt_memory_mode:
  1125			F2FS_CTX_INFO(ctx).memory_mode = result.uint_32;
  1126			ctx->spec_mask |= F2FS_SPEC_memory_mode;
  1127			break;
  1128		case Opt_age_extent_cache:
  1129			ctx_set_opt(ctx, F2FS_MOUNT_AGE_EXTENT_CACHE);
  1130			break;
  1131		case Opt_errors:
  1132			F2FS_CTX_INFO(ctx).errors = result.uint_32;
  1133			ctx->spec_mask |= F2FS_SPEC_errors;
  1134			break;
  1135		case Opt_nat_bits:
  1136			ctx_set_opt(ctx, F2FS_MOUNT_NAT_BITS);
  1137			break;
  1138		}
  1139		return 0;
  1140	}
  1141	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* Re: [PATCH 5/7] f2fs: separate the options parsing and options checking
  2025-04-20 15:25 ` [PATCH 5/7] f2fs: separate the options parsing and options checking Eric Sandeen
  2025-04-21 16:34   ` kernel test robot
  2025-04-21 17:37   ` kernel test robot
@ 2025-04-21 17:37   ` kernel test robot
  2 siblings, 0 replies; 20+ messages in thread
From: kernel test robot @ 2025-04-21 17:37 UTC (permalink / raw)
  To: Eric Sandeen, linux-f2fs-devel
  Cc: oe-kbuild-all, linux-fsdevel, jaegeuk, chao, lihongbo22,
	Eric Sandeen

Hi Eric,

kernel test robot noticed the following build errors:

[auto build test ERROR on v6.15-rc3]
[also build test ERROR on linus/master]
[cannot apply to jaegeuk-f2fs/dev-test jaegeuk-f2fs/dev next-20250417]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Eric-Sandeen/f2fs-Add-fs-parameter-specifications-for-mount-options/20250421-220156
base:   v6.15-rc3
patch link:    https://lore.kernel.org/r/20250420154647.1233033-6-sandeen%40redhat.com
patch subject: [PATCH 5/7] f2fs: separate the options parsing and options checking
config: i386-buildonly-randconfig-005-20250421 (https://download.01.org/0day-ci/archive/20250422/202504220139.19wajgtO-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250422/202504220139.19wajgtO-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202504220139.19wajgtO-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from include/linux/printk.h:7,
                    from include/linux/kernel.h:31,
                    from include/linux/cpumask.h:11,
                    from arch/x86/include/asm/cpumask.h:5,
                    from arch/x86/include/asm/msr.h:11,
                    from arch/x86/include/asm/tsc.h:10,
                    from arch/x86/include/asm/timex.h:6,
                    from include/linux/timex.h:67,
                    from include/linux/time32.h:13,
                    from include/linux/time.h:60,
                    from include/linux/stat.h:19,
                    from include/linux/module.h:13,
                    from fs/f2fs/super.c:8:
   fs/f2fs/super.c: In function 'handle_mount_opt':
   include/linux/kern_levels.h:5:25: warning: format '%lu' expects argument of type 'long unsigned int', but argument 4 has type 'unsigned int' [-Wformat=]
       5 | #define KERN_SOH        "\001"          /* ASCII Start Of Header */
         |                         ^~~~~~
   include/linux/kern_levels.h:11:25: note: in expansion of macro 'KERN_SOH'
      11 | #define KERN_ERR        KERN_SOH "3"    /* error conditions */
         |                         ^~~~~~~~
   fs/f2fs/f2fs.h:1871:33: note: in expansion of macro 'KERN_ERR'
    1871 |         f2fs_printk(sbi, false, KERN_ERR fmt, ##__VA_ARGS__)
         |                                 ^~~~~~~~
   fs/f2fs/super.c:763:25: note: in expansion of macro 'f2fs_err'
     763 |                         f2fs_err(NULL, "inline xattr size is out of range: %lu ~ %lu",
         |                         ^~~~~~~~
   fs/f2fs/super.c: In function 'f2fs_check_quota_consistency':
>> fs/f2fs/super.c:1285:27: error: 'sbi' undeclared (first use in this function); did you mean 'sb'?
    1285 |         if (f2fs_readonly(sbi->sb))
         |                           ^~~
         |                           sb
   fs/f2fs/super.c:1285:27: note: each undeclared identifier is reported only once for each function it appears in


vim +1285 fs/f2fs/super.c

  1182	
  1183	/*
  1184	 * Check quota settings consistency.
  1185	 */
  1186	static int f2fs_check_quota_consistency(struct fs_context *fc,
  1187						struct super_block *sb)
  1188	{
  1189	 #ifdef CONFIG_QUOTA
  1190		struct f2fs_fs_context *ctx = fc->fs_private;
  1191		struct f2fs_sb_info *sbi = F2FS_SB(sb);
  1192		bool quota_feature = f2fs_sb_has_quota_ino(sbi);
  1193		bool quota_turnon = sb_any_quota_loaded(sb);
  1194		char *old_qname, *new_qname;
  1195		bool usr_qf_name, grp_qf_name, prj_qf_name, usrquota, grpquota, prjquota;
  1196		int i;
  1197	
  1198		/*
  1199		 * We do the test below only for project quotas. 'usrquota' and
  1200		 * 'grpquota' mount options are allowed even without quota feature
  1201		 * to support legacy quotas in quota files.
  1202		 */
  1203		if (ctx_test_opt(ctx, F2FS_MOUNT_PRJQUOTA) &&
  1204				!f2fs_sb_has_project_quota(sbi)) {
  1205			f2fs_err(sbi, "Project quota feature not enabled. Cannot enable project quota enforcement.");
  1206			return -EINVAL;
  1207		}
  1208	
  1209		if (ctx->qname_mask) {
  1210			for (i = 0; i < MAXQUOTAS; i++) {
  1211				if (!(ctx->qname_mask & (1 << i)))
  1212					continue;
  1213	
  1214				old_qname = F2FS_OPTION(sbi).s_qf_names[i];
  1215				new_qname = F2FS_CTX_INFO(ctx).s_qf_names[i];
  1216				if (quota_turnon &&
  1217					!!old_qname != !!new_qname)
  1218					goto err_jquota_change;
  1219	
  1220				if (old_qname) {
  1221					if (strcmp(old_qname, new_qname) == 0) {
  1222						ctx->qname_mask &= ~(1 << i);
  1223						continue;
  1224					}
  1225					goto err_jquota_specified;
  1226				}
  1227	
  1228				if (quota_feature) {
  1229					f2fs_info(sbi, "QUOTA feature is enabled, so ignore qf_name");
  1230					ctx->qname_mask &= ~(1 << i);
  1231					kfree(F2FS_CTX_INFO(ctx).s_qf_names[i]);
  1232					F2FS_CTX_INFO(ctx).s_qf_names[i] = NULL;
  1233				}
  1234			}
  1235		}
  1236	
  1237		/* Make sure we don't mix old and new quota format */
  1238		usr_qf_name = F2FS_OPTION(sbi).s_qf_names[USRQUOTA] ||
  1239				F2FS_CTX_INFO(ctx).s_qf_names[USRQUOTA];
  1240		grp_qf_name = F2FS_OPTION(sbi).s_qf_names[GRPQUOTA] ||
  1241				F2FS_CTX_INFO(ctx).s_qf_names[GRPQUOTA];
  1242		prj_qf_name = F2FS_OPTION(sbi).s_qf_names[PRJQUOTA] ||
  1243				F2FS_CTX_INFO(ctx).s_qf_names[PRJQUOTA];
  1244		usrquota = test_opt(sbi, USRQUOTA) ||
  1245				ctx_test_opt(ctx, F2FS_MOUNT_USRQUOTA);
  1246		grpquota = test_opt(sbi, GRPQUOTA) ||
  1247				ctx_test_opt(ctx, F2FS_MOUNT_GRPQUOTA);
  1248		prjquota = test_opt(sbi, PRJQUOTA) ||
  1249				ctx_test_opt(ctx, F2FS_MOUNT_PRJQUOTA);
  1250	
  1251		if (usr_qf_name) {
  1252			ctx_clear_opt(ctx, F2FS_MOUNT_USRQUOTA);
  1253			usrquota = false;
  1254		}
  1255		if (grp_qf_name) {
  1256			ctx_clear_opt(ctx, F2FS_MOUNT_GRPQUOTA);
  1257			grpquota = false;
  1258		}
  1259		if (prj_qf_name) {
  1260			ctx_clear_opt(ctx, F2FS_MOUNT_PRJQUOTA);
  1261			prjquota = false;
  1262		}
  1263		if (usr_qf_name || grp_qf_name || prj_qf_name) {
  1264			if (grpquota || usrquota || prjquota) {
  1265				f2fs_err(sbi, "old and new quota format mixing");
  1266				return -EINVAL;
  1267			}
  1268			if (!(ctx->spec_mask & F2FS_SPEC_jqfmt ||
  1269					F2FS_OPTION(sbi).s_jquota_fmt)) {
  1270				f2fs_err(sbi, "journaled quota format not specified");
  1271				return -EINVAL;
  1272			}
  1273		}
  1274		return 0;
  1275	
  1276	err_jquota_change:
  1277		f2fs_err(sbi, "Cannot change journaled quota options when quota turned on");
  1278		return -EINVAL;
  1279	err_jquota_specified:
  1280		f2fs_err(sbi, "%s quota file already specified",
  1281			 QTYPE2NAME(i));
  1282		return -EINVAL;
  1283	
  1284	#else
> 1285		if (f2fs_readonly(sbi->sb))
  1286			return 0;
  1287		if (f2fs_sb_has_quota_ino(sbi)) {
  1288			f2fs_info(sbi, "Filesystem with quota feature cannot be mounted RDWR without CONFIG_QUOTA");
  1289			return -EINVAL;
  1290		}
  1291		if (f2fs_sb_has_project_quota(sbi)) {
  1292			f2fs_err(sbi, "Filesystem with project quota feature cannot be mounted RDWR without CONFIG_QUOTA");
  1293			return -EINVAL;
  1294		}
  1295	
  1296		return 0;
  1297	#endif
  1298	}
  1299	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* Re: [PATCH 0/7 V2] f2fs: new mount API conversion
  2025-04-20 15:24 [PATCH 0/7 V2] f2fs: new mount API conversion Eric Sandeen
                   ` (6 preceding siblings ...)
  2025-04-20 15:25 ` [PATCH 7/7] f2fs: switch to the new mount api Eric Sandeen
@ 2025-04-22  2:39 ` Eric Sandeen
  2025-07-11 16:30 ` [f2fs-dev] " patchwork-bot+f2fs
  8 siblings, 0 replies; 20+ messages in thread
From: Eric Sandeen @ 2025-04-22  2:39 UTC (permalink / raw)
  To: linux-f2fs-devel; +Cc: linux-fsdevel, jaegeuk, chao, lihongbo22

On 4/20/25 10:24 AM, Eric Sandeen wrote:
> This is a forward-port of Hongbo's original f2fs mount API conversion,
> posted last August at 
> https://lore.kernel.org/linux-f2fs-devel/20240814023912.3959299-1-lihongbo22@huawei.com/

I'll rebase this onto jaegeuk's dev tree and send a V3.

-Eric


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

* Re: [PATCH 1/7] f2fs: Add fs parameter specifications for mount options
  2025-04-20 15:25 ` [PATCH 1/7] f2fs: Add fs parameter specifications for mount options Eric Sandeen
@ 2025-05-07  9:54   ` Chao Yu
  0 siblings, 0 replies; 20+ messages in thread
From: Chao Yu @ 2025-05-07  9:54 UTC (permalink / raw)
  To: Eric Sandeen, linux-f2fs-devel; +Cc: chao, linux-fsdevel, jaegeuk, lihongbo22

On 4/20/25 23:25, Eric Sandeen wrote:
> From: Hongbo Li <lihongbo22@huawei.com>
> 
> Use an array of `fs_parameter_spec` called f2fs_param_specs to
> hold the mount option specifications for the new mount api.
> 
> Add constant_table structures for several options to facilitate
> parsing.
> 
> Signed-off-by: Hongbo Li <lihongbo22@huawei.com>
> [sandeen: forward port, minor fixes and updates, more fsparam_enum]
> Signed-off-by: Eric Sandeen <sandeen@redhat.com>

Reviewed-by: Chao Yu <chao@kernel.org>

Thanks,

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

* Re: [PATCH 2/7] f2fs: move the option parser into handle_mount_opt
  2025-04-20 15:25 ` [PATCH 2/7] f2fs: move the option parser into handle_mount_opt Eric Sandeen
@ 2025-05-07 11:26   ` Chao Yu
  2025-05-07 12:31     ` Eric Sandeen
  0 siblings, 1 reply; 20+ messages in thread
From: Chao Yu @ 2025-05-07 11:26 UTC (permalink / raw)
  To: Eric Sandeen, linux-f2fs-devel; +Cc: chao, linux-fsdevel, jaegeuk, lihongbo22

On 4/20/25 23:25, Eric Sandeen wrote:
> From: Hongbo Li <lihongbo22@huawei.com>
> 
> In handle_mount_opt, we use fs_parameter to parse each option.
> However we're still using the old API to get the options string.
> Using fsparams parse_options allows us to remove many of the Opt_
> enums, so remove them.
> 
> The checkpoint disable cap (or percent) involves rather complex
> parsing; we retain the old match_table mechanism for this, which
> handles it well.
> 
> There are some changes about parsing 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.

We'd better to use u32 type for these options, as they should never
be negative.

Can you please update based on below patch?

https://lore.kernel.org/linux-f2fs-devel/20250507112425.939246-1-chao@kernel.org

Thanks,

> 
> Signed-off-by: Hongbo Li <lihongbo22@huawei.com>
> [sandeen: forward port, minor fixes and updates]
> Signed-off-by: Eric Sandeen <sandeen@redhat.com>
> ---
>  fs/f2fs/super.c | 1063 ++++++++++++++++++-----------------------------
>  1 file changed, 407 insertions(+), 656 deletions(-)
> 
> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> index c3623e052cde..8343dc2a515d 100644
> --- a/fs/f2fs/super.c
> +++ b/fs/f2fs/super.c
> @@ -27,6 +27,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"
> @@ -122,29 +123,20 @@ enum {
>  	Opt_disable_roll_forward,
>  	Opt_norecovery,
>  	Opt_discard,
> -	Opt_nodiscard,
>  	Opt_noheap,
>  	Opt_heap,
>  	Opt_user_xattr,
> -	Opt_nouser_xattr,
>  	Opt_acl,
> -	Opt_noacl,
>  	Opt_active_logs,
>  	Opt_disable_ext_identify,
>  	Opt_inline_xattr,
> -	Opt_noinline_xattr,
>  	Opt_inline_xattr_size,
>  	Opt_inline_data,
>  	Opt_inline_dentry,
> -	Opt_noinline_dentry,
>  	Opt_flush_merge,
> -	Opt_noflush_merge,
>  	Opt_barrier,
> -	Opt_nobarrier,
>  	Opt_fastboot,
>  	Opt_extent_cache,
> -	Opt_noextent_cache,
> -	Opt_noinline_data,
>  	Opt_data_flush,
>  	Opt_reserve_root,
>  	Opt_resgid,
> @@ -153,21 +145,13 @@ enum {
>  	Opt_fault_injection,
>  	Opt_fault_type,
>  	Opt_lazytime,
> -	Opt_nolazytime,
>  	Opt_quota,
> -	Opt_noquota,
>  	Opt_usrquota,
>  	Opt_grpquota,
>  	Opt_prjquota,
>  	Opt_usrjquota,
>  	Opt_grpjquota,
>  	Opt_prjjquota,
> -	Opt_offusrjquota,
> -	Opt_offgrpjquota,
> -	Opt_offprjjquota,
> -	Opt_jqfmt_vfsold,
> -	Opt_jqfmt_vfsv0,
> -	Opt_jqfmt_vfsv1,
>  	Opt_alloc,
>  	Opt_fsync,
>  	Opt_test_dummy_encryption,
> @@ -177,17 +161,15 @@ enum {
>  	Opt_checkpoint_disable_cap_perc,
>  	Opt_checkpoint_enable,
>  	Opt_checkpoint_merge,
> -	Opt_nocheckpoint_merge,
>  	Opt_compress_algorithm,
>  	Opt_compress_log_size,
> -	Opt_compress_extension,
>  	Opt_nocompress_extension,
> +	Opt_compress_extension,
>  	Opt_compress_chksum,
>  	Opt_compress_mode,
>  	Opt_compress_cache,
>  	Opt_atgc,
>  	Opt_gc_merge,
> -	Opt_nogc_merge,
>  	Opt_discard_unit,
>  	Opt_memory_mode,
>  	Opt_age_extent_cache,
> @@ -317,83 +299,12 @@ 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_lazytime, "lazytime"},
> -	{Opt_nolazytime, "nolazytime"},
> -	{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_nat_bits, "nat_bits"},
> +/* Resort to a match_table for this interestingly formatted option */
> +static match_table_t f2fs_checkpoint_tokens = {
> +	{Opt_checkpoint_disable, "disable"},
> +	{Opt_checkpoint_disable_cap, "disable:%u"},
> +	{Opt_checkpoint_disable_cap_perc, "disable:%u%%"},
> +	{Opt_checkpoint_enable, "enable"},
>  	{Opt_err, NULL},
>  };
>  
> @@ -509,7 +420,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 f2fs_sb_info *sbi, int qtype,
> -							substring_t *args)
> +			    struct fs_parameter *param)
>  {
>  	struct super_block *sb = sbi->sb;
>  	char *qname;
> @@ -524,7 +435,7 @@ static int f2fs_set_qf_name(struct f2fs_sb_info *sbi, 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;
> @@ -609,14 +520,9 @@ static int f2fs_check_quota_options(struct f2fs_sb_info *sbi)
>  #endif
>  
>  static int f2fs_set_test_dummy_encryption(struct f2fs_sb_info *sbi,
> -					  const char *opt,
> -					  const substring_t *arg,
> +					  const struct fs_parameter *param,
>  					  bool is_remount)
>  {
> -	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;
> @@ -642,17 +548,17 @@ static int f2fs_set_test_dummy_encryption(struct f2fs_sb_info *sbi,
>  		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");
> @@ -795,373 +701,263 @@ static int f2fs_set_zstd_level(struct f2fs_sb_info *sbi, const char *str)
>  #endif
>  #endif
>  
> -static int parse_options(struct f2fs_sb_info *sbi, char *options, bool is_remount)
> +static int handle_mount_opt(struct fs_context *fc, struct fs_parameter *param)
>  {
> -	substring_t args[MAX_OPT_ARGS];
> +	struct f2fs_sb_info *sbi = fc->s_fs_info;
>  #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;
> -	kuid_t uid;
> -	kgid_t gid;
> -	int ret;
> +	substring_t args[MAX_OPT_ARGS];
> +	struct fs_parse_result result;
> +	int is_remount;
> +	char *name;
> +	int token, ret, arg;
>  
> -	if (!options)
> -		return 0;
> +	token = fs_parse(fc, f2fs_param_specs, param, &result);
> +	if (token < 0)
> +		return token;
>  
> -	while ((p = strsep(&options, ",")) != NULL) {
> -		int token;
> +	is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;
>  
> -		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")) {
> -				if (f2fs_sb_has_blkzoned(sbi)) {
> -					f2fs_warn(sbi, "zoned devices need bggc");
> -					kfree(name);
> -					return -EINVAL;
> -				}
> -				F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_OFF;
> -			} else if (!strcmp(name, "sync")) {
> -				F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_SYNC;
> -			} else {
> -				kfree(name);
> +	switch (token) {
> +	case Opt_gc_background:
> +		F2FS_OPTION(sbi).bggc_mode = result.uint_32;
> +		break;
> +	case Opt_disable_roll_forward:
> +		set_opt(sbi, DISABLE_ROLL_FORWARD);
> +		break;
> +	case Opt_norecovery:
> +		/* requires ro mount, checked in f2fs_validate_options */
> +		set_opt(sbi, NORECOVERY);
> +		break;
> +	case Opt_discard:
> +		if (result.negated) {
> +			if (f2fs_hw_should_discard(sbi)) {
> +				f2fs_warn(sbi, "discard is required for zoned block devices");
>  				return -EINVAL;
>  			}
> -			kfree(name);
> -			break;
> -		case Opt_disable_roll_forward:
> -			set_opt(sbi, DISABLE_ROLL_FORWARD);
> -			break;
> -		case Opt_norecovery:
> -			/* requires ro mount, checked in f2fs_default_check */
> -			set_opt(sbi, NORECOVERY);
> -			break;
> -		case Opt_discard:
> +			clear_opt(sbi, DISCARD);
> +		} else {
>  			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;
> +		}
> +		break;
> +	case Opt_noheap:
> +	case Opt_heap:
> +		f2fs_warn(sbi, "heap/no_heap options were deprecated");
> +		break;
>  #ifdef CONFIG_F2FS_FS_XATTR
> -		case Opt_user_xattr:
> -			set_opt(sbi, XATTR_USER);
> -			break;
> -		case Opt_nouser_xattr:
> +	case Opt_user_xattr:
> +		if (result.negated)
>  			clear_opt(sbi, XATTR_USER);
> -			break;
> -		case Opt_inline_xattr:
> -			set_opt(sbi, INLINE_XATTR);
> -			break;
> -		case Opt_noinline_xattr:
> +		else
> +			set_opt(sbi, XATTR_USER);
> +		break;
> +	case Opt_inline_xattr:
> +		if (result.negated)
>  			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;
> +		else
> +			set_opt(sbi, INLINE_XATTR);
> +		break;
> +	case Opt_inline_xattr_size:
> +		set_opt(sbi, INLINE_XATTR_SIZE);
> +		F2FS_OPTION(sbi).inline_xattr_size = result.int_32;
> +		break;
>  #else
> -		case Opt_user_xattr:
> -		case Opt_nouser_xattr:
> -		case Opt_inline_xattr:
> -		case Opt_noinline_xattr:
> -		case Opt_inline_xattr_size:
> -			f2fs_info(sbi, "xattr options not supported");
> -			break;
> +	case Opt_user_xattr:
> +	case Opt_inline_xattr:
> +	case Opt_inline_xattr_size:
> +		f2fs_info(sbi, "%s options not supported", param->key);
> +		break;
>  #endif
>  #ifdef CONFIG_F2FS_FS_POSIX_ACL
> -		case Opt_acl:
> -			set_opt(sbi, POSIX_ACL);
> -			break;
> -		case Opt_noacl:
> +	case Opt_acl:
> +		if (result.negated)
>  			clear_opt(sbi, POSIX_ACL);
> -			break;
> +		else
> +			set_opt(sbi, POSIX_ACL);
> +		break;
>  #else
> -		case Opt_acl:
> -		case Opt_noacl:
> -			f2fs_info(sbi, "acl options not supported");
> -			break;
> +	case Opt_acl:
> +		f2fs_info(sbi, "%s options not supported", param->key);
> +		break;
>  #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:
> +	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;
> +		break;
> +	case Opt_disable_ext_identify:
> +		set_opt(sbi, DISABLE_EXT_IDENTIFY);
> +		break;
> +	case Opt_inline_data:
> +		if (result.negated)
> +			clear_opt(sbi, INLINE_DATA);
> +		else
>  			set_opt(sbi, INLINE_DATA);
> -			break;
> -		case Opt_inline_dentry:
> -			set_opt(sbi, INLINE_DENTRY);
> -			break;
> -		case Opt_noinline_dentry:
> +		break;
> +	case Opt_inline_dentry:
> +		if (result.negated)
>  			clear_opt(sbi, INLINE_DENTRY);
> -			break;
> -		case Opt_flush_merge:
> -			set_opt(sbi, FLUSH_MERGE);
> -			break;
> -		case Opt_noflush_merge:
> +		else
> +			set_opt(sbi, INLINE_DENTRY);
> +		break;
> +	case Opt_flush_merge:
> +		if (result.negated)
>  			clear_opt(sbi, FLUSH_MERGE);
> -			break;
> -		case Opt_nobarrier:
> +		else
> +			set_opt(sbi, FLUSH_MERGE);
> +		break;
> +	case Opt_barrier:
> +		if (result.negated)
>  			set_opt(sbi, NOBARRIER);
> -			break;
> -		case Opt_barrier:
> +		else
>  			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:
> +		break;
> +	case Opt_fastboot:
> +		set_opt(sbi, FASTBOOT);
> +		break;
> +	case Opt_extent_cache:
> +		if (result.negated) {
>  			if (f2fs_sb_has_device_alias(sbi)) {
>  				f2fs_err(sbi, "device aliasing requires extent cache");
>  				return -EINVAL;
>  			}
>  			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;
> -			}
> -			kfree(name);
> -			break;
> +		} else
> +			set_opt(sbi, READ_EXTENT_CACHE);
> +		break;
> +	case Opt_data_flush:
> +		set_opt(sbi, DATA_FLUSH);
> +		break;
> +	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.int_32;
> +			set_opt(sbi, RESERVE_ROOT);
> +		}
> +		break;
> +	case Opt_resuid:
> +		F2FS_OPTION(sbi).s_resuid = result.uid;
> +		break;
> +	case Opt_resgid:
> +		F2FS_OPTION(sbi).s_resgid = result.gid;
> +		break;
> +	case Opt_mode:
> +		F2FS_OPTION(sbi).fs_mode = result.uint_32;
> +		break;
>  #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);
> +		break;
>  
> -		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.int_32))
> +			return -EINVAL;
> +		set_opt(sbi, FAULT_INJECTION);
> +		break;
>  #else
> -		case Opt_fault_injection:
> -		case Opt_fault_type:
> -			f2fs_info(sbi, "fault injection options not supported");
> -			break;
> +	case Opt_fault_injection:
> +	case Opt_fault_type:
> +		f2fs_info(sbi, "%s options not supported", param->key);
> +		break;
>  #endif
> -		case Opt_lazytime:
> -			set_opt(sbi, LAZYTIME);
> -			break;
> -		case Opt_nolazytime:
> +	case Opt_lazytime:
> +		if (result.negated)
>  			clear_opt(sbi, LAZYTIME);
> -			break;
> +		else
> +			set_opt(sbi, LAZYTIME);
> +		break;
>  #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(sbi, USRQUOTA, &args[0]);
> -			if (ret)
> -				return ret;
> -			break;
> -		case Opt_grpjquota:
> -			ret = f2fs_set_qf_name(sbi, GRPQUOTA, &args[0]);
> -			if (ret)
> -				return ret;
> -			break;
> -		case Opt_prjjquota:
> -			ret = f2fs_set_qf_name(sbi, PRJQUOTA, &args[0]);
> -			if (ret)
> -				return ret;
> -			break;
> -		case Opt_offusrjquota:
> -			ret = f2fs_clear_qf_name(sbi, USRQUOTA);
> -			if (ret)
> -				return ret;
> -			break;
> -		case Opt_offgrpjquota:
> -			ret = f2fs_clear_qf_name(sbi, GRPQUOTA);
> -			if (ret)
> -				return ret;
> -			break;
> -		case Opt_offprjjquota:
> -			ret = f2fs_clear_qf_name(sbi, 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:
> +	case Opt_quota:
> +		if (result.negated) {
>  			clear_opt(sbi, QUOTA);
>  			clear_opt(sbi, USRQUOTA);
>  			clear_opt(sbi, GRPQUOTA);
>  			clear_opt(sbi, PRJQUOTA);
> -			break;
> +		} else
> +			set_opt(sbi, USRQUOTA);
> +		break;
> +	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:
> +		if (!*param->string)
> +			ret = f2fs_clear_qf_name(sbi, USRQUOTA);
> +		else
> +			ret = f2fs_set_qf_name(sbi, USRQUOTA, param);
> +		if (ret)
> +			return ret;
> +		break;
> +	case Opt_grpjquota:
> +		if (!*param->string)
> +			ret = f2fs_clear_qf_name(sbi, GRPQUOTA);
> +		else
> +			ret = f2fs_set_qf_name(sbi, GRPQUOTA, param);
> +		if (ret)
> +			return ret;
> +		break;
> +	case Opt_prjjquota:
> +		if (!*param->string)
> +			ret = f2fs_clear_qf_name(sbi, PRJQUOTA);
> +		else
> +			ret = f2fs_set_qf_name(sbi, PRJQUOTA, param);
> +		if (ret)
> +			return ret;
> +		break;
> +	case Opt_jqfmt:
> +		F2FS_OPTION(sbi).s_jquota_fmt = result.uint_32;
> +		break;
>  #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:
> +		f2fs_info(sbi, "quota operations not supported");
> +		break;
>  #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;
> -			}
> -			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;
> -			}
> -			kfree(name);
> -			break;
> -		case Opt_test_dummy_encryption:
> -			ret = f2fs_set_test_dummy_encryption(sbi, p, &args[0],
> -							     is_remount);
> -			if (ret)
> -				return ret;
> -			break;
> -		case Opt_inlinecrypt:
> +	case Opt_alloc:
> +		F2FS_OPTION(sbi).alloc_mode = result.uint_32;
> +		break;
> +	case Opt_fsync:
> +		F2FS_OPTION(sbi).fsync_mode = result.uint_32;
> +		break;
> +	case Opt_test_dummy_encryption:
> +		ret = f2fs_set_test_dummy_encryption(sbi, param, is_remount);
> +		if (ret)
> +			return ret;
> +		break;
> +	case Opt_inlinecrypt:
>  #ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
> -			set_opt(sbi, INLINECRYPT);
> +		set_opt(sbi, INLINECRYPT);
>  #else
> -			f2fs_info(sbi, "inline encryption not supported");
> +		f2fs_info(sbi, "inline encryption not supported");
>  #endif
> -			break;
> +		break;
> +	case Opt_checkpoint:
> +		/* revert to match_table for checkpoint= options */
> +		token = match_token(param->string, f2fs_checkpoint_tokens, args);
> +		switch (token) {
>  		case Opt_checkpoint_disable_cap_perc:
>  			if (args->from && match_int(args, &arg))
>  				return -EINVAL;
> @@ -1182,270 +978,225 @@ static int parse_options(struct f2fs_sb_info *sbi, char *options, bool is_remoun
>  		case Opt_checkpoint_enable:
>  			clear_opt(sbi, DISABLE_CHECKPOINT);
>  			break;
> -		case Opt_checkpoint_merge:
> -			set_opt(sbi, MERGE_CHECKPOINT);
> -			break;
> -		case Opt_nocheckpoint_merge:
> +		default:
> +			return -EINVAL;
> +		}
> +		break;
> +	case Opt_checkpoint_merge:
> +		if (result.negated)
>  			clear_opt(sbi, MERGE_CHECKPOINT);
> -			break;
> +		else
> +			set_opt(sbi, MERGE_CHECKPOINT);
> +		break;
>  #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");
> +			break;
> +		}
> +		name = param->string;
> +		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)
> +				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)
> +				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;
> -			}
> -			kfree(name);
> +		} else
> +			return -EINVAL;
> +		break;
> +	case Opt_compress_log_size:
> +		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;
> +		}
> +		if (result.uint_32 < MIN_COMPRESS_LOG_SIZE ||
> +		    result.uint_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.uint_32;
> +		break;
> +	case Opt_compress_extension:
> +		if (!f2fs_sb_has_compression(sbi)) {
> +			f2fs_info(sbi, "Image doesn't support compression");
>  			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;
> -
> -			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;
> -			}
> +		}
> +		name = param->string;
> +		ext = F2FS_OPTION(sbi).extensions;
> +		ext_cnt = F2FS_OPTION(sbi).compress_ext_cnt;
>  
> -			if (is_compress_extension_exist(sbi, name, true)) {
> -				kfree(name);
> -				break;
> -			}
> +		if (strlen(name) >= F2FS_EXTENSION_LEN ||
> +		    ext_cnt >= COMPRESS_EXT_NUM) {
> +			f2fs_err(sbi, "invalid extension length/number");
> +			return -EINVAL;
> +		}
>  
> -			ret = strscpy(ext[ext_cnt], name);
> -			if (ret < 0) {
> -				kfree(name);
> -				return ret;
> -			}
> -			F2FS_OPTION(sbi).compress_ext_cnt++;
> -			kfree(name);
> +		if (is_compress_extension_exist(sbi, name, true))
>  			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;
>  
> -			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;
> -			}
> +		ret = strscpy(ext[ext_cnt], name, F2FS_EXTENSION_LEN);
> +		if (ret < 0)
> +			return ret;
> +		F2FS_OPTION(sbi).compress_ext_cnt++;
> +		break;
> +	case Opt_nocompress_extension:
> +		if (!f2fs_sb_has_compression(sbi)) {
> +			f2fs_info(sbi, "Image doesn't support compression");
> +			break;
> +		}
> +		name = param->string;
> +		noext = F2FS_OPTION(sbi).noextensions;
> +		noext_cnt = F2FS_OPTION(sbi).nocompress_ext_cnt;
>  
> -			if (is_compress_extension_exist(sbi, name, false)) {
> -				kfree(name);
> -				break;
> -			}
> +		if (strlen(name) >= F2FS_EXTENSION_LEN ||
> +			noext_cnt >= COMPRESS_EXT_NUM) {
> +			f2fs_err(sbi, "invalid extension length/number");
> +			return -EINVAL;
> +		}
>  
> -			ret = strscpy(noext[noext_cnt], name);
> -			if (ret < 0) {
> -				kfree(name);
> -				return ret;
> -			}
> -			F2FS_OPTION(sbi).nocompress_ext_cnt++;
> -			kfree(name);
> +		if (is_compress_extension_exist(sbi, name, false))
>  			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;
> +
> +		ret = strscpy(noext[noext_cnt], name, F2FS_EXTENSION_LEN);
> +		if (ret < 0)
> +			return ret;
> +		F2FS_OPTION(sbi).nocompress_ext_cnt++;
> +		break;
> +	case Opt_compress_chksum:
> +		if (!f2fs_sb_has_compression(sbi)) {
> +			f2fs_info(sbi, "Image doesn't support compression");
>  			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;
> -			}
> -			kfree(name);
> +		}
> +		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;
> -		case Opt_compress_cache:
> -			if (!f2fs_sb_has_compression(sbi)) {
> -				f2fs_info(sbi, "Image doesn't support compression");
> -				break;
> -			}
> -			set_opt(sbi, COMPRESS_CACHE);
> +		}
> +		F2FS_OPTION(sbi).compress_mode = result.uint_32;
> +		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;
>  #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");
> +		break;
>  #endif
> -		case Opt_atgc:
> -			set_opt(sbi, ATGC);
> -			break;
> -		case Opt_gc_merge:
> -			set_opt(sbi, GC_MERGE);
> -			break;
> -		case Opt_nogc_merge:
> +	case Opt_atgc:
> +		set_opt(sbi, ATGC);
> +		break;
> +	case Opt_gc_merge:
> +		if (result.negated)
>  			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;
> -			}
> -			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;
> -			}
> -			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;
> +		else
> +			set_opt(sbi, GC_MERGE);
> +		break;
> +	case Opt_discard_unit:
> +		F2FS_OPTION(sbi).discard_unit = result.uint_32;
> +		break;
> +	case Opt_memory_mode:
> +		F2FS_OPTION(sbi).memory_mode = result.uint_32;
> +		break;
> +	case Opt_age_extent_cache:
> +		set_opt(sbi, AGE_EXTENT_CACHE);
> +		break;
> +	case Opt_errors:
> +		F2FS_OPTION(sbi).errors = result.uint_32;
> +		break;
> +	case Opt_nat_bits:
> +		set_opt(sbi, NAT_BITS);
> +		break;
> +	}
> +	return 0;
> +}
> +
> +static int parse_options(struct f2fs_sb_info *sbi, char *options, bool is_remount)
> +{
> +	struct fs_parameter param;
> +	struct fs_context fc;
> +	char *key;
> +	int ret;
> +
> +	if (!options)
> +		return 0;
> +
> +	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;
>  			}
> -			kfree(name);
> -			break;
> -		case Opt_nat_bits:
> -			set_opt(sbi, NAT_BITS);
> -			break;
> -		default:
> -			f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value",
> -				 p);
> -			return -EINVAL;
> +
> +			param.key = key;
> +			param.size = v_len;
> +
> +			ret = handle_mount_opt(&fc, &param);
> +			kfree(param.string);
> +			if (ret < 0)
> +				return ret;
>  		}
>  	}
>  	return 0;
>  }
>  
> -static int f2fs_default_check(struct f2fs_sb_info *sbi)
> +static int f2fs_validate_options(struct f2fs_sb_info *sbi)
>  {
>  #ifdef CONFIG_QUOTA
>  	if (f2fs_check_quota_options(sbi))
> @@ -2516,7 +2267,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
>  	}
>  #endif
>  
> -	err = f2fs_default_check(sbi);
> +	err = f2fs_validate_options(sbi);
>  	if (err)
>  		goto restore_opts;
>  
> @@ -4675,7 +4426,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
>  	if (err)
>  		goto free_options;
>  
> -	err = f2fs_default_check(sbi);
> +	err = f2fs_validate_options(sbi);
>  	if (err)
>  		goto free_options;
>  


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

* Re: [PATCH 3/7] f2fs: Allow sbi to be NULL in f2fs_printk
  2025-04-20 15:25 ` [PATCH 3/7] f2fs: Allow sbi to be NULL in f2fs_printk Eric Sandeen
@ 2025-05-07 11:29   ` Chao Yu
  0 siblings, 0 replies; 20+ messages in thread
From: Chao Yu @ 2025-05-07 11:29 UTC (permalink / raw)
  To: Eric Sandeen, linux-f2fs-devel; +Cc: chao, linux-fsdevel, jaegeuk, lihongbo22

On 4/20/25 23:25, Eric Sandeen wrote:
> From: Hongbo Li <lihongbo22@huawei.com>
> 
> 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>
> [sandeen: forward port]
> Signed-off-by: Eric Sandeen <sandeen@redhat.com>

Reviewed-by: Chao Yu <chao@kernel.org>

Thanks,

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

* Re: [PATCH 2/7] f2fs: move the option parser into handle_mount_opt
  2025-05-07 11:26   ` Chao Yu
@ 2025-05-07 12:31     ` Eric Sandeen
  2025-05-07 15:02       ` Jaegeuk Kim
  2025-05-08  3:28       ` Chao Yu
  0 siblings, 2 replies; 20+ messages in thread
From: Eric Sandeen @ 2025-05-07 12:31 UTC (permalink / raw)
  To: Chao Yu, linux-f2fs-devel; +Cc: linux-fsdevel, jaegeuk, lihongbo22

On 5/7/25 6:26 AM, Chao Yu wrote:
> On 4/20/25 23:25, Eric Sandeen wrote:
>> From: Hongbo Li <lihongbo22@huawei.com>
>>
>> In handle_mount_opt, we use fs_parameter to parse each option.
>> However we're still using the old API to get the options string.
>> Using fsparams parse_options allows us to remove many of the Opt_
>> enums, so remove them.
>>
>> The checkpoint disable cap (or percent) involves rather complex
>> parsing; we retain the old match_table mechanism for this, which
>> handles it well.
>>
>> There are some changes about parsing 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.
> 
> We'd better to use u32 type for these options, as they should never
> be negative.
> 
> Can you please update based on below patch?
> 
> https://lore.kernel.org/linux-f2fs-devel/20250507112425.939246-1-chao@kernel.org

Hi Chao - I agree that that patch makes sense, but maybe there is a timing
issue now? At the moment, there is a mix of signed and unsigned handling
for these options. I agree that the conversion series probably should have
left the parsing type as unsigned, but it was a mix internally, so it was
difficult to know for sure.

For your patch above, if it is to stand alone or be merged first, it 
should probably also change the current parsing to match_uint. (this would
also make it backportable to -stable kernels, if you want to).

Otherwise, I would suggest that if it is merged after the mount API series,
then your patch to clean up internal types could fix the (new mount API)
parsing from %s to %u at the same time?

Happy to do it either way but your patch should probably be internally
consistent, changing the parsing types at the same time.

(I suppose we could incorporate your patch into the mount API series too,
though it'd be a little strange to have a minor bugfix like this buried
in the series.)

Thanks,
-Eric


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

* Re: [PATCH 2/7] f2fs: move the option parser into handle_mount_opt
  2025-05-07 12:31     ` Eric Sandeen
@ 2025-05-07 15:02       ` Jaegeuk Kim
  2025-05-08  3:29         ` Chao Yu
  2025-05-08  3:28       ` Chao Yu
  1 sibling, 1 reply; 20+ messages in thread
From: Jaegeuk Kim @ 2025-05-07 15:02 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: Chao Yu, linux-f2fs-devel, linux-fsdevel, lihongbo22

On 05/07, Eric Sandeen wrote:
> On 5/7/25 6:26 AM, Chao Yu wrote:
> > On 4/20/25 23:25, Eric Sandeen wrote:
> >> From: Hongbo Li <lihongbo22@huawei.com>
> >>
> >> In handle_mount_opt, we use fs_parameter to parse each option.
> >> However we're still using the old API to get the options string.
> >> Using fsparams parse_options allows us to remove many of the Opt_
> >> enums, so remove them.
> >>
> >> The checkpoint disable cap (or percent) involves rather complex
> >> parsing; we retain the old match_table mechanism for this, which
> >> handles it well.
> >>
> >> There are some changes about parsing 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.
> > 
> > We'd better to use u32 type for these options, as they should never
> > be negative.
> > 
> > Can you please update based on below patch?
> > 
> > https://lore.kernel.org/linux-f2fs-devel/20250507112425.939246-1-chao@kernel.org
> 
> Hi Chao - I agree that that patch makes sense, but maybe there is a timing
> issue now? At the moment, there is a mix of signed and unsigned handling
> for these options. I agree that the conversion series probably should have
> left the parsing type as unsigned, but it was a mix internally, so it was
> difficult to know for sure.
> 
> For your patch above, if it is to stand alone or be merged first, it 
> should probably also change the current parsing to match_uint. (this would
> also make it backportable to -stable kernels, if you want to).
> 
> Otherwise, I would suggest that if it is merged after the mount API series,
> then your patch to clean up internal types could fix the (new mount API)
> parsing from %s to %u at the same time?

Yeah, agreed we'd better applying the type change later, once mount API is
successfully landed. Chao, let's keep checking any missing cases. :)

> 
> Happy to do it either way but your patch should probably be internally
> consistent, changing the parsing types at the same time.
> 
> (I suppose we could incorporate your patch into the mount API series too,
> though it'd be a little strange to have a minor bugfix like this buried
> in the series.)
> 
> Thanks,
> -Eric

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

* Re: [PATCH 2/7] f2fs: move the option parser into handle_mount_opt
  2025-05-07 12:31     ` Eric Sandeen
  2025-05-07 15:02       ` Jaegeuk Kim
@ 2025-05-08  3:28       ` Chao Yu
  1 sibling, 0 replies; 20+ messages in thread
From: Chao Yu @ 2025-05-08  3:28 UTC (permalink / raw)
  To: Eric Sandeen, linux-f2fs-devel; +Cc: chao, linux-fsdevel, jaegeuk, lihongbo22

On 5/7/25 20:31, Eric Sandeen wrote:
> On 5/7/25 6:26 AM, Chao Yu wrote:
>> On 4/20/25 23:25, Eric Sandeen wrote:
>>> From: Hongbo Li <lihongbo22@huawei.com>
>>>
>>> In handle_mount_opt, we use fs_parameter to parse each option.
>>> However we're still using the old API to get the options string.
>>> Using fsparams parse_options allows us to remove many of the Opt_
>>> enums, so remove them.
>>>
>>> The checkpoint disable cap (or percent) involves rather complex
>>> parsing; we retain the old match_table mechanism for this, which
>>> handles it well.
>>>
>>> There are some changes about parsing 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.
>>
>> We'd better to use u32 type for these options, as they should never
>> be negative.
>>
>> Can you please update based on below patch?
>>
>> https://lore.kernel.org/linux-f2fs-devel/20250507112425.939246-1-chao@kernel.org
> 
> Hi Chao - I agree that that patch makes sense, but maybe there is a timing
> issue now? At the moment, there is a mix of signed and unsigned handling
> for these options. I agree that the conversion series probably should have
> left the parsing type as unsigned, but it was a mix internally, so it was
> difficult to know for sure.
> 
> For your patch above, if it is to stand alone or be merged first, it 
> should probably also change the current parsing to match_uint. (this would
> also make it backportable to -stable kernels, if you want to).
> 
> Otherwise, I would suggest that if it is merged after the mount API series,
> then your patch to clean up internal types could fix the (new mount API)
> parsing from %s to %u at the same time?
> 
> Happy to do it either way but your patch should probably be internally
> consistent, changing the parsing types at the same time.
> 
> (I suppose we could incorporate your patch into the mount API series too,
> though it'd be a little strange to have a minor bugfix like this buried
> in the series.)

Eric,

Yeah, it's a little bit strange to include that patch into mount API series,
now, I realize that rebasing huge change to a minor fix is not necessary,
anyway, let me focus on reviewing, and pickup that patch after mount API series
be merged. :P

Thanks,

> 
> Thanks,
> -Eric
> 


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

* Re: [PATCH 2/7] f2fs: move the option parser into handle_mount_opt
  2025-05-07 15:02       ` Jaegeuk Kim
@ 2025-05-08  3:29         ` Chao Yu
  0 siblings, 0 replies; 20+ messages in thread
From: Chao Yu @ 2025-05-08  3:29 UTC (permalink / raw)
  To: Jaegeuk Kim, Eric Sandeen
  Cc: chao, linux-f2fs-devel, linux-fsdevel, lihongbo22

On 5/7/25 23:02, Jaegeuk Kim wrote:
> On 05/07, Eric Sandeen wrote:
>> On 5/7/25 6:26 AM, Chao Yu wrote:
>>> On 4/20/25 23:25, Eric Sandeen wrote:
>>>> From: Hongbo Li <lihongbo22@huawei.com>
>>>>
>>>> In handle_mount_opt, we use fs_parameter to parse each option.
>>>> However we're still using the old API to get the options string.
>>>> Using fsparams parse_options allows us to remove many of the Opt_
>>>> enums, so remove them.
>>>>
>>>> The checkpoint disable cap (or percent) involves rather complex
>>>> parsing; we retain the old match_table mechanism for this, which
>>>> handles it well.
>>>>
>>>> There are some changes about parsing 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.
>>>
>>> We'd better to use u32 type for these options, as they should never
>>> be negative.
>>>
>>> Can you please update based on below patch?
>>>
>>> https://lore.kernel.org/linux-f2fs-devel/20250507112425.939246-1-chao@kernel.org
>>
>> Hi Chao - I agree that that patch makes sense, but maybe there is a timing
>> issue now? At the moment, there is a mix of signed and unsigned handling
>> for these options. I agree that the conversion series probably should have
>> left the parsing type as unsigned, but it was a mix internally, so it was
>> difficult to know for sure.
>>
>> For your patch above, if it is to stand alone or be merged first, it 
>> should probably also change the current parsing to match_uint. (this would
>> also make it backportable to -stable kernels, if you want to).
>>
>> Otherwise, I would suggest that if it is merged after the mount API series,
>> then your patch to clean up internal types could fix the (new mount API)
>> parsing from %s to %u at the same time?
> 
> Yeah, agreed we'd better applying the type change later, once mount API is
> successfully landed. Chao, let's keep checking any missing cases. :)

Sure, let me check left patches today. :)

Thanks,

> 
>>
>> Happy to do it either way but your patch should probably be internally
>> consistent, changing the parsing types at the same time.
>>
>> (I suppose we could incorporate your patch into the mount API series too,
>> though it'd be a little strange to have a minor bugfix like this buried
>> in the series.)
>>
>> Thanks,
>> -Eric


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

* Re: [f2fs-dev] [PATCH 0/7 V2] f2fs: new mount API conversion
  2025-04-20 15:24 [PATCH 0/7 V2] f2fs: new mount API conversion Eric Sandeen
                   ` (7 preceding siblings ...)
  2025-04-22  2:39 ` [PATCH 0/7 V2] f2fs: new mount API conversion Eric Sandeen
@ 2025-07-11 16:30 ` patchwork-bot+f2fs
  8 siblings, 0 replies; 20+ messages in thread
From: patchwork-bot+f2fs @ 2025-07-11 16:30 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: linux-f2fs-devel, linux-fsdevel, jaegeuk, lihongbo22

Hello:

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

On Sun, 20 Apr 2025 10:24:59 -0500 you wrote:
> This is a forward-port of Hongbo's original f2fs mount API conversion,
> posted last August at
> https://lore.kernel.org/linux-f2fs-devel/20240814023912.3959299-1-lihongbo22@huawei.com/
> 
> I had been trying to approach this with a little less complexity,
> but in the end I realized that Hongbo's approach (which follows
> the ext4 approach) was a good one, and I was not making any progrss
> myself. ;)
> 
> [...]

Here is the summary with links:
  - [f2fs-dev,1/7] f2fs: Add fs parameter specifications for mount options
    https://git.kernel.org/jaegeuk/f2fs/c/a3277c98b64f
  - [f2fs-dev,2/7] f2fs: move the option parser into handle_mount_opt
    (no matching commit)
  - [f2fs-dev,3/7] f2fs: Allow sbi to be NULL in f2fs_printk
    https://git.kernel.org/jaegeuk/f2fs/c/405e5e00bfee
  - [f2fs-dev,4/7] f2fs: Add f2fs_fs_context to record the mount options
    (no matching commit)
  - [f2fs-dev,5/7] f2fs: separate the options parsing and options checking
    (no matching commit)
  - [f2fs-dev,6/7] f2fs: introduce fs_context_operation structure
    https://git.kernel.org/jaegeuk/f2fs/c/54e12a4e0209
  - [f2fs-dev,7/7] f2fs: switch to the new mount api
    (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] 20+ messages in thread

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

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-04-20 15:24 [PATCH 0/7 V2] f2fs: new mount API conversion Eric Sandeen
2025-04-20 15:25 ` [PATCH 1/7] f2fs: Add fs parameter specifications for mount options Eric Sandeen
2025-05-07  9:54   ` Chao Yu
2025-04-20 15:25 ` [PATCH 2/7] f2fs: move the option parser into handle_mount_opt Eric Sandeen
2025-05-07 11:26   ` Chao Yu
2025-05-07 12:31     ` Eric Sandeen
2025-05-07 15:02       ` Jaegeuk Kim
2025-05-08  3:29         ` Chao Yu
2025-05-08  3:28       ` Chao Yu
2025-04-20 15:25 ` [PATCH 3/7] f2fs: Allow sbi to be NULL in f2fs_printk Eric Sandeen
2025-05-07 11:29   ` Chao Yu
2025-04-20 15:25 ` [PATCH 4/7] f2fs: Add f2fs_fs_context to record the mount options Eric Sandeen
2025-04-20 15:25 ` [PATCH 5/7] f2fs: separate the options parsing and options checking Eric Sandeen
2025-04-21 16:34   ` kernel test robot
2025-04-21 17:37   ` kernel test robot
2025-04-21 17:37   ` kernel test robot
2025-04-20 15:25 ` [PATCH 6/7] f2fs: introduce fs_context_operation structure Eric Sandeen
2025-04-20 15:25 ` [PATCH 7/7] f2fs: switch to the new mount api Eric Sandeen
2025-04-22  2:39 ` [PATCH 0/7 V2] f2fs: new mount API conversion Eric Sandeen
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).