* [PATCH V3 1/7] f2fs: Add fs parameter specifications for mount options
2025-04-23 17:08 [PATCH V3 0/7] f2fs: new mount API conversion Eric Sandeen
@ 2025-04-23 17:08 ` Eric Sandeen
2025-05-08 2:40 ` Hongbo Li
` (2 more replies)
2025-04-23 17:08 ` [PATCH V3 2/7] f2fs: move the option parser into handle_mount_opt Eric Sandeen
` (8 subsequent siblings)
9 siblings, 3 replies; 58+ messages in thread
From: Eric Sandeen @ 2025-04-23 17:08 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 22f26871b7aa..ebea03bba054 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"
@@ -194,9 +195,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.49.0
^ permalink raw reply related [flat|nested] 58+ messages in thread
* Re: [PATCH V3 1/7] f2fs: Add fs parameter specifications for mount options
2025-04-23 17:08 ` [PATCH V3 1/7] f2fs: Add fs parameter specifications for mount options Eric Sandeen
@ 2025-05-08 2:40 ` Hongbo Li
2025-05-08 5:24 ` Chao Yu
2025-07-11 16:30 ` [f2fs-dev] " patchwork-bot+f2fs
2 siblings, 0 replies; 58+ messages in thread
From: Hongbo Li @ 2025-05-08 2:40 UTC (permalink / raw)
To: Eric Sandeen, linux-f2fs-devel; +Cc: linux-fsdevel, jaegeuk, chao
On 2025/4/24 1:08, 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>
Thanks, I have checked this.
Reviewed-by: Hongbo Li <lihongbo22@huawei.com>
> ---
> fs/f2fs/super.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 122 insertions(+)
>
> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> index 22f26871b7aa..ebea03bba054 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"
> @@ -194,9 +195,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"},
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 1/7] f2fs: Add fs parameter specifications for mount options
2025-04-23 17:08 ` [PATCH V3 1/7] f2fs: Add fs parameter specifications for mount options Eric Sandeen
2025-05-08 2:40 ` Hongbo Li
@ 2025-05-08 5:24 ` Chao Yu
2025-07-11 16:30 ` [f2fs-dev] " patchwork-bot+f2fs
2 siblings, 0 replies; 58+ messages in thread
From: Chao Yu @ 2025-05-08 5:24 UTC (permalink / raw)
To: Eric Sandeen, linux-f2fs-devel; +Cc: chao, linux-fsdevel, jaegeuk, lihongbo22
On 4/24/25 01:08, 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] 58+ messages in thread
* Re: [f2fs-dev] [PATCH V3 1/7] f2fs: Add fs parameter specifications for mount options
2025-04-23 17:08 ` [PATCH V3 1/7] f2fs: Add fs parameter specifications for mount options Eric Sandeen
2025-05-08 2:40 ` Hongbo Li
2025-05-08 5:24 ` Chao Yu
@ 2025-07-11 16:30 ` patchwork-bot+f2fs
2 siblings, 0 replies; 58+ 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 Wed, 23 Apr 2025 12:08:45 -0500 you 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.
>
> [...]
Here is the summary with links:
- [f2fs-dev,V3,1/7] f2fs: Add fs parameter specifications for mount options
https://git.kernel.org/jaegeuk/f2fs/c/a3277c98b64f
- [f2fs-dev,V3,2/7] f2fs: move the option parser into handle_mount_opt
(no matching commit)
- [f2fs-dev,V3,3/7] f2fs: Allow sbi to be NULL in f2fs_printk
https://git.kernel.org/jaegeuk/f2fs/c/405e5e00bfee
- [f2fs-dev,V3,4/7] f2fs: Add f2fs_fs_context to record the mount options
(no matching commit)
- [f2fs-dev,V3,5/7] f2fs: separate the options parsing and options checking
(no matching commit)
- [f2fs-dev,V3,6/7] f2fs: introduce fs_context_operation structure
https://git.kernel.org/jaegeuk/f2fs/c/54e12a4e0209
- [f2fs-dev,V3,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] 58+ messages in thread
* [PATCH V3 2/7] f2fs: move the option parser into handle_mount_opt
2025-04-23 17:08 [PATCH V3 0/7] f2fs: new mount API conversion Eric Sandeen
2025-04-23 17:08 ` [PATCH V3 1/7] f2fs: Add fs parameter specifications for mount options Eric Sandeen
@ 2025-04-23 17:08 ` Eric Sandeen
2025-05-06 20:24 ` Jaegeuk Kim
2025-05-08 5:30 ` Chao Yu
2025-04-23 17:08 ` [PATCH V3 3/7] f2fs: Allow sbi to be NULL in f2fs_printk Eric Sandeen
` (7 subsequent siblings)
9 siblings, 2 replies; 58+ messages in thread
From: Eric Sandeen @ 2025-04-23 17:08 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 | 1061 ++++++++++++++++++-----------------------------
1 file changed, 406 insertions(+), 655 deletions(-)
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index ebea03bba054..20dee7c40d59 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"
@@ -124,29 +125,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,
@@ -155,21 +147,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,
@@ -179,17 +163,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,
@@ -319,83 +301,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},
};
@@ -511,7 +422,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;
@@ -526,7 +437,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;
@@ -611,14 +522,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;
@@ -644,17 +550,17 @@ static int f2fs_set_test_dummy_encryption(struct f2fs_sb_info *sbi,
return -EINVAL;
}
- err = fscrypt_parse_test_dummy_encryption(¶m, 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");
@@ -797,372 +703,262 @@ 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;
+ char *name;
#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;
+ 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, 0, FAULT_RATE))
- return -EINVAL;
- set_opt(sbi, FAULT_INJECTION);
- break;
+ case Opt_fault_injection:
+ if (f2fs_build_fault_attr(sbi, result.int_32, 0, FAULT_RATE))
+ 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, FAULT_TYPE))
- return -EINVAL;
- set_opt(sbi, FAULT_INJECTION);
- break;
+ case Opt_fault_type:
+ if (f2fs_build_fault_attr(sbi, 0, result.int_32, FAULT_TYPE))
+ 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;
@@ -1183,270 +979,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, ¶m);
+ 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))
@@ -2519,7 +2270,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;
@@ -4678,7 +4429,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.49.0
^ permalink raw reply related [flat|nested] 58+ messages in thread
* Re: [PATCH V3 2/7] f2fs: move the option parser into handle_mount_opt
2025-04-23 17:08 ` [PATCH V3 2/7] f2fs: move the option parser into handle_mount_opt Eric Sandeen
@ 2025-05-06 20:24 ` Jaegeuk Kim
2025-05-08 5:30 ` Chao Yu
1 sibling, 0 replies; 58+ messages in thread
From: Jaegeuk Kim @ 2025-05-06 20:24 UTC (permalink / raw)
To: Eric Sandeen; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 04/23, 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.
>
> 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 | 1061 ++++++++++++++++++-----------------------------
> 1 file changed, 406 insertions(+), 655 deletions(-)
>
> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> index ebea03bba054..20dee7c40d59 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"
> @@ -124,29 +125,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,
> @@ -155,21 +147,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,
> @@ -179,17 +163,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,
> @@ -319,83 +301,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},
> };
>
> @@ -511,7 +422,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;
> @@ -526,7 +437,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;
> @@ -611,14 +522,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;
> @@ -644,17 +550,17 @@ static int f2fs_set_test_dummy_encryption(struct f2fs_sb_info *sbi,
> return -EINVAL;
> }
>
> - err = fscrypt_parse_test_dummy_encryption(¶m, 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");
> @@ -797,372 +703,262 @@ 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;
> + char *name;
> #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;
> + 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, 0, FAULT_RATE))
> - return -EINVAL;
> - set_opt(sbi, FAULT_INJECTION);
> - break;
> + case Opt_fault_injection:
> + if (f2fs_build_fault_attr(sbi, result.int_32, 0, FAULT_RATE))
> + 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, FAULT_TYPE))
> - return -EINVAL;
> - set_opt(sbi, FAULT_INJECTION);
> - break;
> + case Opt_fault_type:
> + if (f2fs_build_fault_attr(sbi, 0, result.int_32, FAULT_TYPE))
> + 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:
I applied the below to avoid build errors.
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -960,6 +960,13 @@ static int f2fs_parse_param(struct fs_context *fc, struct fs_parameter *param)
#endif
break;
case Opt_checkpoint:
+ /*
+ * Initialize args struct so we know whether arg was
+ * found; some options take optional arguments.
+ */
+ args[0].from = args[0].to = NULL;
+ arg = 0;
+
/* revert to match_table for checkpoint= options */
token = match_token(param->string, f2fs_checkpoint_tokens, args);
switch (token) {
> + /* 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;
> @@ -1183,270 +979,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, ¶m);
> + 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))
> @@ -2519,7 +2270,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;
>
> @@ -4678,7 +4429,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.49.0
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 2/7] f2fs: move the option parser into handle_mount_opt
2025-04-23 17:08 ` [PATCH V3 2/7] f2fs: move the option parser into handle_mount_opt Eric Sandeen
2025-05-06 20:24 ` Jaegeuk Kim
@ 2025-05-08 5:30 ` Chao Yu
1 sibling, 0 replies; 58+ messages in thread
From: Chao Yu @ 2025-05-08 5:30 UTC (permalink / raw)
To: Eric Sandeen, linux-f2fs-devel; +Cc: chao, linux-fsdevel, jaegeuk, lihongbo22
On 4/24/25 01:08, 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.
>
> 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 | 1061 ++++++++++++++++++-----------------------------
> 1 file changed, 406 insertions(+), 655 deletions(-)
>
> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> index ebea03bba054..20dee7c40d59 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"
> @@ -124,29 +125,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,
> @@ -155,21 +147,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,
> @@ -179,17 +163,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,
> @@ -319,83 +301,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},
> };
>
> @@ -511,7 +422,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;
> @@ -526,7 +437,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;
> @@ -611,14 +522,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;
> @@ -644,17 +550,17 @@ static int f2fs_set_test_dummy_encryption(struct f2fs_sb_info *sbi,
> return -EINVAL;
> }
>
> - err = fscrypt_parse_test_dummy_encryption(¶m, 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");
> @@ -797,372 +703,262 @@ 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;
> + char *name;
> #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;
bool is_remount;
It's minor, anyway, looks good to me.
Reviewed-by: Chao Yu <chao@kernel.org>
Thanks,
> + 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, 0, FAULT_RATE))
> - return -EINVAL;
> - set_opt(sbi, FAULT_INJECTION);
> - break;
> + case Opt_fault_injection:
> + if (f2fs_build_fault_attr(sbi, result.int_32, 0, FAULT_RATE))
> + 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, FAULT_TYPE))
> - return -EINVAL;
> - set_opt(sbi, FAULT_INJECTION);
> - break;
> + case Opt_fault_type:
> + if (f2fs_build_fault_attr(sbi, 0, result.int_32, FAULT_TYPE))
> + 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;
> @@ -1183,270 +979,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, ¶m);
> + 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))
> @@ -2519,7 +2270,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;
>
> @@ -4678,7 +4429,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] 58+ messages in thread
* [PATCH V3 3/7] f2fs: Allow sbi to be NULL in f2fs_printk
2025-04-23 17:08 [PATCH V3 0/7] f2fs: new mount API conversion Eric Sandeen
2025-04-23 17:08 ` [PATCH V3 1/7] f2fs: Add fs parameter specifications for mount options Eric Sandeen
2025-04-23 17:08 ` [PATCH V3 2/7] f2fs: move the option parser into handle_mount_opt Eric Sandeen
@ 2025-04-23 17:08 ` Eric Sandeen
2025-05-08 5:30 ` Chao Yu
2025-04-23 17:08 ` [PATCH V3 4/7] f2fs: Add f2fs_fs_context to record the mount options Eric Sandeen
` (6 subsequent siblings)
9 siblings, 1 reply; 58+ messages in thread
From: Eric Sandeen @ 2025-04-23 17:08 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 20dee7c40d59..35190db4501c 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -323,11 +323,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);
}
@@ -737,13 +745,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);
@@ -751,7 +759,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:
@@ -774,7 +782,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
@@ -786,7 +794,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:
@@ -840,7 +848,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;
@@ -871,7 +879,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:
@@ -934,7 +942,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:
@@ -952,7 +960,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:
@@ -992,7 +1000,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;
@@ -1001,7 +1009,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
@@ -1010,7 +1018,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
@@ -1019,26 +1027,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;
}
@@ -1046,7 +1054,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;
@@ -1055,7 +1063,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;
}
@@ -1069,7 +1077,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;
@@ -1078,7 +1086,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;
}
@@ -1092,21 +1100,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);
@@ -1119,7 +1127,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:
@@ -1204,17 +1212,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;
}
@@ -1228,24 +1236,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
@@ -1255,11 +1263,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;
}
@@ -1268,24 +1276,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.49.0
^ permalink raw reply related [flat|nested] 58+ messages in thread
* [PATCH V3 4/7] f2fs: Add f2fs_fs_context to record the mount options
2025-04-23 17:08 [PATCH V3 0/7] f2fs: new mount API conversion Eric Sandeen
` (2 preceding siblings ...)
2025-04-23 17:08 ` [PATCH V3 3/7] f2fs: Allow sbi to be NULL in f2fs_printk Eric Sandeen
@ 2025-04-23 17:08 ` Eric Sandeen
2025-05-08 6:34 ` Chao Yu
2025-04-23 17:08 ` [PATCH V3 5/7] f2fs: separate the options parsing and options checking Eric Sandeen
` (5 subsequent siblings)
9 siblings, 1 reply; 58+ messages in thread
From: Eric Sandeen @ 2025-04-23 17:08 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 35190db4501c..15befeb45c94 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -310,8 +310,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;
@@ -429,57 +486,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;
}
@@ -529,54 +580,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];
@@ -584,11 +614,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++) {
@@ -637,58 +667,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))
@@ -696,16 +730,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
@@ -713,6 +748,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];
@@ -733,14 +769,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) {
@@ -748,13 +785,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:
@@ -764,19 +801,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:
@@ -788,9 +826,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:
@@ -801,37 +839,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) {
@@ -839,42 +878,50 @@ 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, 0, FAULT_RATE))
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, FAULT_TYPE))
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:
@@ -884,55 +931,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:
@@ -946,19 +994,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
@@ -972,20 +1022,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;
@@ -993,9 +1045,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:
@@ -1006,33 +1058,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
@@ -1050,7 +1108,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)) {
@@ -1058,8 +1117,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) {
@@ -1067,13 +1126,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)) {
@@ -1081,8 +1141,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) {
@@ -1090,34 +1150,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:
@@ -1131,28 +1194,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;
@@ -1162,6 +1228,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;
@@ -1170,6 +1237,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.49.0
^ permalink raw reply related [flat|nested] 58+ messages in thread
* Re: [PATCH V3 4/7] f2fs: Add f2fs_fs_context to record the mount options
2025-04-23 17:08 ` [PATCH V3 4/7] f2fs: Add f2fs_fs_context to record the mount options Eric Sandeen
@ 2025-05-08 6:34 ` Chao Yu
0 siblings, 0 replies; 58+ messages in thread
From: Chao Yu @ 2025-05-08 6:34 UTC (permalink / raw)
To: Eric Sandeen, linux-f2fs-devel; +Cc: chao, linux-fsdevel, jaegeuk, lihongbo22
On 4/24/25 01:08, Eric Sandeen wrote:
> 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>
Reviewed-by: Chao Yu <chao@kernel.org>
Thanks,
^ permalink raw reply [flat|nested] 58+ messages in thread
* [PATCH V3 5/7] f2fs: separate the options parsing and options checking
2025-04-23 17:08 [PATCH V3 0/7] f2fs: new mount API conversion Eric Sandeen
` (3 preceding siblings ...)
2025-04-23 17:08 ` [PATCH V3 4/7] f2fs: Add f2fs_fs_context to record the mount options Eric Sandeen
@ 2025-04-23 17:08 ` Eric Sandeen
2025-05-06 22:01 ` Jaegeuk Kim
` (2 more replies)
2025-04-23 17:08 ` [PATCH V3 6/7] f2fs: introduce fs_context_operation structure Eric Sandeen
` (4 subsequent siblings)
9 siblings, 3 replies; 58+ messages in thread
From: Eric Sandeen @ 2025-04-23 17:08 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 | 693 +++++++++++++++++++++++++++++++++++-------------
1 file changed, 510 insertions(+), 183 deletions(-)
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 15befeb45c94..149134775870 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -360,6 +360,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)
{
@@ -533,51 +539,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,
@@ -636,28 +597,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;
}
@@ -749,7 +710,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];
@@ -758,15 +718,12 @@ 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;
int token, ret, arg;
token = fs_parse(fc, f2fs_param_specs, param, &result);
if (token < 0)
return token;
- is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;
-
switch (token) {
case Opt_gc_background:
F2FS_CTX_INFO(ctx).bggc_mode = result.uint_32;
@@ -780,19 +737,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:
@@ -812,6 +760,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: %zu ~ %zu",
+ 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;
@@ -873,27 +827,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;
@@ -909,7 +854,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, 0, FAULT_RATE))
+ 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;
@@ -917,7 +862,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, FAULT_TYPE))
+ 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;
@@ -1051,10 +996,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
@@ -1098,10 +1039,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,
@@ -1112,10 +1049,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;
@@ -1136,10 +1069,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;
@@ -1160,26 +1089,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
@@ -1224,24 +1141,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;
@@ -1265,7 +1173,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, ¶m);
+ ret = handle_mount_opt(fc, ¶m);
kfree(param.string);
if (ret < 0)
return ret;
@@ -1274,24 +1182,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))
+ struct f2fs_sb_info *sbi = F2FS_SB(sb);
+ #ifdef CONFIG_QUOTA
+ struct f2fs_fs_context *ctx = fc->fs_private;
+ 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;
}
@@ -1303,75 +1480,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, 0, FAULT_RATE);
+ if (ctx->spec_mask & F2FS_SPEC_fault_type)
+ (void)f2fs_build_fault_attr(sbi, 0,
+ F2FS_CTX_INFO(ctx).fault_info.inject_type, FAULT_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)
@@ -2275,6 +2587,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;
@@ -2331,11 +2645,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) {
@@ -2346,11 +2671,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);
@@ -4429,6 +4749,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;
@@ -4445,6 +4767,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);
@@ -4502,14 +4827,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.49.0
^ permalink raw reply related [flat|nested] 58+ messages in thread
* Re: [PATCH V3 5/7] f2fs: separate the options parsing and options checking
2025-04-23 17:08 ` [PATCH V3 5/7] f2fs: separate the options parsing and options checking Eric Sandeen
@ 2025-05-06 22:01 ` Jaegeuk Kim
2025-05-06 22:52 ` Eric Sandeen
2025-05-08 8:13 ` Chao Yu
2025-05-13 2:15 ` Jaegeuk Kim
2 siblings, 1 reply; 58+ messages in thread
From: Jaegeuk Kim @ 2025-05-06 22:01 UTC (permalink / raw)
To: Eric Sandeen; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 04/23, Eric Sandeen wrote:
> 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 | 693 +++++++++++++++++++++++++++++++++++-------------
> 1 file changed, 510 insertions(+), 183 deletions(-)
>
> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> index 15befeb45c94..149134775870 100644
> --- a/fs/f2fs/super.c
> +++ b/fs/f2fs/super.c
> @@ -360,6 +360,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)
> {
> @@ -533,51 +539,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,
> @@ -636,28 +597,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;
> }
> @@ -749,7 +710,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];
> @@ -758,15 +718,12 @@ 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;
> int token, ret, arg;
>
> token = fs_parse(fc, f2fs_param_specs, param, &result);
> if (token < 0)
> return token;
>
> - is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;
> -
> switch (token) {
> case Opt_gc_background:
> F2FS_CTX_INFO(ctx).bggc_mode = result.uint_32;
> @@ -780,19 +737,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:
> @@ -812,6 +760,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: %zu ~ %zu",
> + 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;
> @@ -873,27 +827,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;
> @@ -909,7 +854,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, 0, FAULT_RATE))
> + 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;
> @@ -917,7 +862,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, FAULT_TYPE))
> + 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;
> @@ -1051,10 +996,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
> @@ -1098,10 +1039,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,
> @@ -1112,10 +1049,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;
> @@ -1136,10 +1069,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;
> @@ -1160,26 +1089,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
> @@ -1224,24 +1141,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;
> @@ -1265,7 +1173,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, ¶m);
> + ret = handle_mount_opt(fc, ¶m);
> kfree(param.string);
> if (ret < 0)
> return ret;
> @@ -1274,24 +1182,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))
> + struct f2fs_sb_info *sbi = F2FS_SB(sb);
> + #ifdef CONFIG_QUOTA
> + struct f2fs_fs_context *ctx = fc->fs_private;
> + 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)) {
Applied.
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)) {
Shouldn't this be?
if (f2fs_sb_has_device_alias(sbi) &&
!ctx_test_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE)) {
> + 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)) {
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)) {
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;
> }
> @@ -1303,75 +1480,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, 0, FAULT_RATE);
> + if (ctx->spec_mask & F2FS_SPEC_fault_type)
> + (void)f2fs_build_fault_attr(sbi, 0,
> + F2FS_CTX_INFO(ctx).fault_info.inject_type, FAULT_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)
> @@ -2275,6 +2587,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;
> @@ -2331,11 +2645,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) {
> @@ -2346,11 +2671,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);
>
> @@ -4429,6 +4749,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;
> @@ -4445,6 +4767,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);
> @@ -4502,14 +4827,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.49.0
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 5/7] f2fs: separate the options parsing and options checking
2025-05-06 22:01 ` Jaegeuk Kim
@ 2025-05-06 22:52 ` Eric Sandeen
2025-05-06 23:30 ` Jaegeuk Kim
0 siblings, 1 reply; 58+ messages in thread
From: Eric Sandeen @ 2025-05-06 22:52 UTC (permalink / raw)
To: Jaegeuk Kim; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 5/6/25 5:01 PM, Jaegeuk Kim wrote:
<snip>
>> +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)) {
> Applied.
>
> if (f2fs_hw_should_discard(sbi) &&
> (ctx->opt_mask & F2FS_MOUNT_DISCARD) &&
> !ctx_test_opt(ctx, F2FS_MOUNT_DISCARD)) {
>
yes that's nicer
>> + f2fs_warn(sbi, "discard is required for zoned block devices");
>> + return -EINVAL;
>> + }
>> +
>> + if (f2fs_sb_has_device_alias(sbi)) {
> Shouldn't this be?
>
> if (f2fs_sb_has_device_alias(sbi) &&
> !ctx_test_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE)) {
>
Whoops, I don't know how I missed that, or how my testing missed it, sorry.
And maybe it should be later in the function so it doesn't interrupt the=
discard cases.
>> + 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)) {
> if (!f2fs_hw_support_discard(sbi) &&
> (ctx->opt_mask & F2FS_MOUNT_DISCARD) &&
> ctx_test_opt(ctx, F2FS_MOUNT_DISCARD)) {
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 5/7] f2fs: separate the options parsing and options checking
2025-05-06 22:52 ` Eric Sandeen
@ 2025-05-06 23:30 ` Jaegeuk Kim
0 siblings, 0 replies; 58+ messages in thread
From: Jaegeuk Kim @ 2025-05-06 23:30 UTC (permalink / raw)
To: Eric Sandeen; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 05/06, Eric Sandeen wrote:
> On 5/6/25 5:01 PM, Jaegeuk Kim wrote:
>
> <snip>
>
> >> +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)) {
> > Applied.
> >
> > if (f2fs_hw_should_discard(sbi) &&
> > (ctx->opt_mask & F2FS_MOUNT_DISCARD) &&
> > !ctx_test_opt(ctx, F2FS_MOUNT_DISCARD)) {
> >
>
> yes that's nicer
>
> >> + f2fs_warn(sbi, "discard is required for zoned block devices");
> >> + return -EINVAL;
> >> + }
> >> +
> >> + if (f2fs_sb_has_device_alias(sbi)) {
> > Shouldn't this be?
> >
> > if (f2fs_sb_has_device_alias(sbi) &&
> > !ctx_test_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE)) {
> >
>
> Whoops, I don't know how I missed that, or how my testing missed it, sorry.
> And maybe it should be later in the function so it doesn't interrupt the=
> discard cases.
No worries. I applied the check after doing the discard cases.
Thanks,
>
> >> + 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)) {
> > if (!f2fs_hw_support_discard(sbi) &&
> > (ctx->opt_mask & F2FS_MOUNT_DISCARD) &&
> > ctx_test_opt(ctx, F2FS_MOUNT_DISCARD)) {
> >
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 5/7] f2fs: separate the options parsing and options checking
2025-04-23 17:08 ` [PATCH V3 5/7] f2fs: separate the options parsing and options checking Eric Sandeen
2025-05-06 22:01 ` Jaegeuk Kim
@ 2025-05-08 8:13 ` Chao Yu
2025-05-08 15:52 ` Eric Sandeen
2025-05-13 2:15 ` Jaegeuk Kim
2 siblings, 1 reply; 58+ messages in thread
From: Chao Yu @ 2025-05-08 8:13 UTC (permalink / raw)
To: Eric Sandeen, linux-f2fs-devel; +Cc: chao, linux-fsdevel, jaegeuk, lihongbo22
On 4/24/25 01:08, Eric Sandeen wrote:
> 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 | 693 +++++++++++++++++++++++++++++++++++-------------
> 1 file changed, 510 insertions(+), 183 deletions(-)
>
> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> index 15befeb45c94..149134775870 100644
> --- a/fs/f2fs/super.c
> +++ b/fs/f2fs/super.c
> @@ -360,6 +360,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)
> {
> @@ -533,51 +539,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,
> @@ -636,28 +597,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;
> }
> @@ -749,7 +710,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];
> @@ -758,15 +718,12 @@ 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;
> int token, ret, arg;
>
> token = fs_parse(fc, f2fs_param_specs, param, &result);
> if (token < 0)
> return token;
>
> - is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;
> -
> switch (token) {
> case Opt_gc_background:
> F2FS_CTX_INFO(ctx).bggc_mode = result.uint_32;
> @@ -780,19 +737,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:
> @@ -812,6 +760,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: %zu ~ %zu",
> + 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;
> @@ -873,27 +827,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;
> @@ -909,7 +854,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, 0, FAULT_RATE))
> + 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;
> @@ -917,7 +862,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, FAULT_TYPE))
> + 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;
> @@ -1051,10 +996,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
> @@ -1098,10 +1039,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,
> @@ -1112,10 +1049,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;
> @@ -1136,10 +1069,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;
> @@ -1160,26 +1089,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
> @@ -1224,24 +1141,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;
> @@ -1265,7 +1173,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, ¶m);
> + ret = handle_mount_opt(fc, ¶m);
> kfree(param.string);
> if (ret < 0)
> return ret;
> @@ -1274,24 +1182,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))
> + struct f2fs_sb_info *sbi = F2FS_SB(sb);
> + #ifdef CONFIG_QUOTA
> + struct f2fs_fs_context *ctx = fc->fs_private;
> + 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);
Needs to free and nully F2FS_CTX_INFO(ctx).s_qf_names[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);
How about add a macro to include all compression macros, and use it to clean
up above codes?
> +}
> +
> +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");
Can you please describe what is detailed confliction in the log? e.g. new
noext conflicts w/ new ext...
> + 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");
Ditto,
> + 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");
Ditto,
Thanks,
> + 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;
> }
> @@ -1303,75 +1480,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, 0, FAULT_RATE);
> + if (ctx->spec_mask & F2FS_SPEC_fault_type)
> + (void)f2fs_build_fault_attr(sbi, 0,
> + F2FS_CTX_INFO(ctx).fault_info.inject_type, FAULT_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)
> @@ -2275,6 +2587,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;
> @@ -2331,11 +2645,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) {
> @@ -2346,11 +2671,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);
>
> @@ -4429,6 +4749,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;
> @@ -4445,6 +4767,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);
> @@ -4502,14 +4827,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;
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 5/7] f2fs: separate the options parsing and options checking
2025-05-08 8:13 ` Chao Yu
@ 2025-05-08 15:52 ` Eric Sandeen
2025-05-12 3:32 ` Chao Yu
2025-05-14 1:03 ` Hongbo Li
0 siblings, 2 replies; 58+ messages in thread
From: Eric Sandeen @ 2025-05-08 15:52 UTC (permalink / raw)
To: Chao Yu, linux-f2fs-devel; +Cc: linux-fsdevel, jaegeuk, lihongbo22
On 5/8/25 3:13 AM, Chao Yu wrote:
> On 4/24/25 01:08, Eric Sandeen wrote:
>> From: Hongbo Li <lihongbo22@huawei.com>
...
>> + 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);
>
> Needs to free and nully F2FS_CTX_INFO(ctx).s_qf_names[i]?
>
I will have to look into this. If s_qf_names are used/applied, they get
transferred to the sbi in f2fs_apply_quota_options and are freed in the
normal course of the fiesystem lifetime, i.e at unmount in f2fs_put_super.
That's the normal non-error lifecycle of the strings.
If they do not get transferred to the sbi in f2fs_apply_quota_options, they
remain on the ctx, and should get freed in f2fs_fc_free:
for (i = 0; i < MAXQUOTAS; i++)
kfree(F2FS_CTX_INFO(ctx).s_qf_names[i]);
It is possible to free them before f2fs_fc_free of course and that might
be an inconsistency in this function, because we do that in the other case
in the check_consistency function:
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;
}
I'll have to look at it a bit more. But this is modeled on ext4's
ext4_check_quota_consistency(), and it does not do any freeing in that
function; it leaves freeing in error cases to when the fc / ctx gets freed.
But tl;dr: I think we can remove the kfree and "= NULL" in this function,
and defer the freeing in the error case.
>> +
>> +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);
>
> How about add a macro to include all compression macros, and use it to clean
> up above codes?
That's a good idea and probably easy enough to do without rebase pain.
>> +
>> + 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");
>
> Can you please describe what is detailed confliction in the log? e.g. new
> noext conflicts w/ new ext...
Hmm, let me think about this. I had not noticed it was calling
f2fs_test_compress_extension 3 times, I wonder if there is a better option.
I need to understand this approach better. Maybe Hongbo has thoughts.
>> + 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");
>
> Ditto,
>
>> + 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");
>
> Ditto,
thanks,
-Eric
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 5/7] f2fs: separate the options parsing and options checking
2025-05-08 15:52 ` Eric Sandeen
@ 2025-05-12 3:32 ` Chao Yu
2025-05-14 1:10 ` Hongbo Li
2025-05-14 1:03 ` Hongbo Li
1 sibling, 1 reply; 58+ messages in thread
From: Chao Yu @ 2025-05-12 3:32 UTC (permalink / raw)
To: Eric Sandeen, linux-f2fs-devel; +Cc: chao, linux-fsdevel, jaegeuk, lihongbo22
On 5/8/25 23:52, Eric Sandeen wrote:
> On 5/8/25 3:13 AM, Chao Yu wrote:
>> On 4/24/25 01:08, Eric Sandeen wrote:
>>> From: Hongbo Li <lihongbo22@huawei.com>
>
> ...
>
>>> + 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);
>>
>> Needs to free and nully F2FS_CTX_INFO(ctx).s_qf_names[i]?
>>
>
> I will have to look into this. If s_qf_names are used/applied, they get
> transferred to the sbi in f2fs_apply_quota_options and are freed in the
> normal course of the fiesystem lifetime, i.e at unmount in f2fs_put_super.
> That's the normal non-error lifecycle of the strings.
>
> If they do not get transferred to the sbi in f2fs_apply_quota_options, they
> remain on the ctx, and should get freed in f2fs_fc_free:
>
> for (i = 0; i < MAXQUOTAS; i++)
> kfree(F2FS_CTX_INFO(ctx).s_qf_names[i]);
>
> It is possible to free them before f2fs_fc_free of course and that might
> be an inconsistency in this function, because we do that in the other case
> in the check_consistency function:
>
> 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;
> }
Yes, I noticed such inconsistency, and I'm wondering why we handle ctx.s_qf_names
w/ different ways.
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;
}
For "quota_feature is on" case, as opt.s_qf_names is NULL, so if it doesn't
nully ctx.s_qf_names, it will fail below check which is not as expected. So
I doubt it should be handled separately.
/* 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;
}
}
>
> I'll have to look at it a bit more. But this is modeled on ext4's
> ext4_check_quota_consistency(), and it does not do any freeing in that
> function; it leaves freeing in error cases to when the fc / ctx gets freed.
>
> But tl;dr: I think we can remove the kfree and "= NULL" in this function,
> and defer the freeing in the error case.
>
>>> +
>>> +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);
>>
>> How about add a macro to include all compression macros, and use it to clean
>> up above codes?
>
> That's a good idea and probably easy enough to do without rebase pain.
>
>>> +
>>> + 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");
>>
>> Can you please describe what is detailed confliction in the log? e.g. new
>> noext conflicts w/ new ext...
>
> Hmm, let me think about this. I had not noticed it was calling
> f2fs_test_compress_extension 3 times, I wonder if there is a better option.
> I need to understand this approach better. Maybe Hongbo has thoughts.
Maybe:
f2fs_err(sbi, "new noextensions conflicts w/ new extensions");
>
>>> + 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");
f2fs_err(sbi, "new noextensions conflicts w/ old extensions");
>>
>> Ditto,
>>
>>> + 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");
f2fs_err(sbi, "new extensions conflicts w/ old noextensions");
Then, user may get enough hint from log to update conflicted {no,}extensions
for mount.
Thanks,
>>
>> Ditto,
>
> thanks,
> -Eric
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 5/7] f2fs: separate the options parsing and options checking
2025-05-12 3:32 ` Chao Yu
@ 2025-05-14 1:10 ` Hongbo Li
0 siblings, 0 replies; 58+ messages in thread
From: Hongbo Li @ 2025-05-14 1:10 UTC (permalink / raw)
To: Chao Yu, Eric Sandeen, linux-f2fs-devel; +Cc: linux-fsdevel, jaegeuk
On 2025/5/12 11:32, Chao Yu wrote:
> On 5/8/25 23:52, Eric Sandeen wrote:
>> On 5/8/25 3:13 AM, Chao Yu wrote:
>>> On 4/24/25 01:08, Eric Sandeen wrote:
>>>> From: Hongbo Li <lihongbo22@huawei.com>
>>
>> ...
>>
>>>> + 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);
>>>
>>> Needs to free and nully F2FS_CTX_INFO(ctx).s_qf_names[i]?
>>>
>>
>> I will have to look into this. If s_qf_names are used/applied, they get
>> transferred to the sbi in f2fs_apply_quota_options and are freed in the
>> normal course of the fiesystem lifetime, i.e at unmount in f2fs_put_super.
>> That's the normal non-error lifecycle of the strings.
>>
>> If they do not get transferred to the sbi in f2fs_apply_quota_options, they
>> remain on the ctx, and should get freed in f2fs_fc_free:
>>
>> for (i = 0; i < MAXQUOTAS; i++)
>> kfree(F2FS_CTX_INFO(ctx).s_qf_names[i]);
>>
>> It is possible to free them before f2fs_fc_free of course and that might
>> be an inconsistency in this function, because we do that in the other case
>> in the check_consistency function:
>>
>> 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;
>> }
>
> Yes, I noticed such inconsistency, and I'm wondering why we handle ctx.s_qf_names
> w/ different ways.
>
> 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;
> }
>
> For "quota_feature is on" case, as opt.s_qf_names is NULL, so if it doesn't
> nully ctx.s_qf_names, it will fail below check which is not as expected. So
> I doubt it should be handled separately.
>
> /* 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;
> }
> }
>
>>
>> I'll have to look at it a bit more. But this is modeled on ext4's
>> ext4_check_quota_consistency(), and it does not do any freeing in that
>> function; it leaves freeing in error cases to when the fc / ctx gets freed.
>>
>> But tl;dr: I think we can remove the kfree and "= NULL" in this function,
>> and defer the freeing in the error case.
>>
>>>> +
>>>> +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);
>>>
>>> How about add a macro to include all compression macros, and use it to clean
>>> up above codes?
>>
>> That's a good idea and probably easy enough to do without rebase pain.
>>
>>>> +
>>>> + 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");
>>>
>>> Can you please describe what is detailed confliction in the log? e.g. new
>>> noext conflicts w/ new ext...
>>
>> Hmm, let me think about this. I had not noticed it was calling
>> f2fs_test_compress_extension 3 times, I wonder if there is a better option.
>> I need to understand this approach better. Maybe Hongbo has thoughts.
>
> Maybe:
>
> f2fs_err(sbi, "new noextensions conflicts w/ new extensions");
>
>>
>>>> + 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");
>
> f2fs_err(sbi, "new noextensions conflicts w/ old extensions");
>
>>>
>>> Ditto,
>>>
>>>> + 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");
>
> f2fs_err(sbi, "new extensions conflicts w/ old noextensions");
>
Yeah, this makes it clearer. And don't think anyone would use the old
log as the criterion.
Thanks,
Hongbo
> Then, user may get enough hint from log to update conflicted {no,}extensions
> for mount.
>
> Thanks,
>
>>>
>>> Ditto,
>>
>> thanks,
>> -Eric
>>
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 5/7] f2fs: separate the options parsing and options checking
2025-05-08 15:52 ` Eric Sandeen
2025-05-12 3:32 ` Chao Yu
@ 2025-05-14 1:03 ` Hongbo Li
1 sibling, 0 replies; 58+ messages in thread
From: Hongbo Li @ 2025-05-14 1:03 UTC (permalink / raw)
To: Eric Sandeen, Chao Yu, linux-f2fs-devel; +Cc: linux-fsdevel, jaegeuk
On 2025/5/8 23:52, Eric Sandeen wrote:
> On 5/8/25 3:13 AM, Chao Yu wrote:
>> On 4/24/25 01:08, Eric Sandeen wrote:
>>> From: Hongbo Li <lihongbo22@huawei.com>
>
> ...
>
>>> + 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);
>>
>> Needs to free and nully F2FS_CTX_INFO(ctx).s_qf_names[i]?
>>
>
> I will have to look into this. If s_qf_names are used/applied, they get
> transferred to the sbi in f2fs_apply_quota_options and are freed in the
> normal course of the fiesystem lifetime, i.e at unmount in f2fs_put_super.
> That's the normal non-error lifecycle of the strings.
>
> If they do not get transferred to the sbi in f2fs_apply_quota_options, they
> remain on the ctx, and should get freed in f2fs_fc_free:
>
> for (i = 0; i < MAXQUOTAS; i++)
> kfree(F2FS_CTX_INFO(ctx).s_qf_names[i]);
>
> It is possible to free them before f2fs_fc_free of course and that might
> be an inconsistency in this function, because we do that in the other case
> in the check_consistency function:
>
> 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;
> }
>
> I'll have to look at it a bit more. But this is modeled on ext4's
> ext4_check_quota_consistency(), and it does not do any freeing in that
> function; it leaves freeing in error cases to when the fc / ctx gets freed.
>
> But tl;dr: I think we can remove the kfree and "= NULL" in this function,
> and defer the freeing in the error case.
>
>>> +
>>> +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);
>>
>> How about add a macro to include all compression macros, and use it to clean
>> up above codes?
>
> That's a good idea and probably easy enough to do without rebase pain.
>
>>> +
>>> + 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");
>>
>> Can you please describe what is detailed confliction in the log? e.g. new
>> noext conflicts w/ new ext...
>
> Hmm, let me think about this. I had not noticed it was calling
> f2fs_test_compress_extension 3 times, I wonder if there is a better option.
> I need to understand this approach better. Maybe Hongbo has thoughts.
>
ah, I kept it with the older format. And they should check the unmatched
conflict between ctx and sbi.
Thanks,
Hongbo
>>> + 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");
>>
>> Ditto,
>>
>>> + 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");
>>
>> Ditto,
>
> thanks,
> -Eric
>
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 5/7] f2fs: separate the options parsing and options checking
2025-04-23 17:08 ` [PATCH V3 5/7] f2fs: separate the options parsing and options checking Eric Sandeen
2025-05-06 22:01 ` Jaegeuk Kim
2025-05-08 8:13 ` Chao Yu
@ 2025-05-13 2:15 ` Jaegeuk Kim
2 siblings, 0 replies; 58+ messages in thread
From: Jaegeuk Kim @ 2025-05-13 2:15 UTC (permalink / raw)
To: Eric Sandeen; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 04/23, Eric Sandeen wrote:
> 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 | 693 +++++++++++++++++++++++++++++++++++-------------
> 1 file changed, 510 insertions(+), 183 deletions(-)
>
> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> index 15befeb45c94..149134775870 100644
> --- a/fs/f2fs/super.c
> +++ b/fs/f2fs/super.c
> @@ -360,6 +360,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)
> {
> @@ -533,51 +539,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,
> @@ -636,28 +597,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;
> }
> @@ -749,7 +710,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];
> @@ -758,15 +718,12 @@ 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;
> int token, ret, arg;
>
> token = fs_parse(fc, f2fs_param_specs, param, &result);
> if (token < 0)
> return token;
>
> - is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;
> -
> switch (token) {
> case Opt_gc_background:
> F2FS_CTX_INFO(ctx).bggc_mode = result.uint_32;
> @@ -780,19 +737,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:
> @@ -812,6 +760,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: %zu ~ %zu",
I fixed this to %lu ~ %lu to avoid build warning.
> + 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;
> @@ -873,27 +827,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;
> @@ -909,7 +854,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, 0, FAULT_RATE))
> + 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;
> @@ -917,7 +862,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, FAULT_TYPE))
> + 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;
> @@ -1051,10 +996,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
> @@ -1098,10 +1039,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,
> @@ -1112,10 +1049,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;
> @@ -1136,10 +1069,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;
> @@ -1160,26 +1089,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
> @@ -1224,24 +1141,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;
> @@ -1265,7 +1173,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, ¶m);
> + ret = handle_mount_opt(fc, ¶m);
> kfree(param.string);
> if (ret < 0)
> return ret;
> @@ -1274,24 +1182,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))
> + struct f2fs_sb_info *sbi = F2FS_SB(sb);
> + #ifdef CONFIG_QUOTA
> + struct f2fs_fs_context *ctx = fc->fs_private;
> + 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;
> }
> @@ -1303,75 +1480,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, 0, FAULT_RATE);
> + if (ctx->spec_mask & F2FS_SPEC_fault_type)
> + (void)f2fs_build_fault_attr(sbi, 0,
> + F2FS_CTX_INFO(ctx).fault_info.inject_type, FAULT_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)
> @@ -2275,6 +2587,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;
> @@ -2331,11 +2645,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) {
> @@ -2346,11 +2671,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);
>
> @@ -4429,6 +4749,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;
> @@ -4445,6 +4767,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);
> @@ -4502,14 +4827,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.49.0
^ permalink raw reply [flat|nested] 58+ messages in thread
* [PATCH V3 6/7] f2fs: introduce fs_context_operation structure
2025-04-23 17:08 [PATCH V3 0/7] f2fs: new mount API conversion Eric Sandeen
` (4 preceding siblings ...)
2025-04-23 17:08 ` [PATCH V3 5/7] f2fs: separate the options parsing and options checking Eric Sandeen
@ 2025-04-23 17:08 ` Eric Sandeen
2025-05-08 8:14 ` Chao Yu
2025-04-23 17:08 ` [PATCH V3 7/7] f2fs: switch to the new mount api Eric Sandeen
` (3 subsequent siblings)
9 siblings, 1 reply; 58+ messages in thread
From: Eric Sandeen @ 2025-04-23 17:08 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 149134775870..37497fd80bb9 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -707,7 +707,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
@@ -1173,7 +1173,7 @@ static int parse_options(struct fs_context *fc, char *options)
param.key = key;
param.size = v_len;
- ret = handle_mount_opt(fc, ¶m);
+ ret = f2fs_parse_param(fc, ¶m);
kfree(param.string);
if (ret < 0)
return ret;
@@ -5277,6 +5277,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.49.0
^ permalink raw reply related [flat|nested] 58+ messages in thread
* [PATCH V3 7/7] f2fs: switch to the new mount api
2025-04-23 17:08 [PATCH V3 0/7] f2fs: new mount API conversion Eric Sandeen
` (5 preceding siblings ...)
2025-04-23 17:08 ` [PATCH V3 6/7] f2fs: introduce fs_context_operation structure Eric Sandeen
@ 2025-04-23 17:08 ` Eric Sandeen
2025-05-08 9:19 ` Chao Yu
` (2 more replies)
2025-05-06 2:18 ` [PATCH V3 0/7] f2fs: new mount API conversion Eric Sandeen
` (2 subsequent siblings)
9 siblings, 3 replies; 58+ messages in thread
From: Eric Sandeen @ 2025-04-23 17:08 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 37497fd80bb9..041bd6c482a0 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -1141,47 +1141,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, ¶m);
- kfree(param.string);
- if (ret < 0)
- return ret;
- }
- }
- return 0;
-}
-
/*
* Check quota settings consistency.
*/
@@ -2583,13 +2542,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;
@@ -2635,7 +2593,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);
@@ -2645,21 +2603,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) &&
@@ -2678,20 +2626,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)) {
@@ -2747,7 +2695,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;
@@ -2758,7 +2706,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) {
@@ -2772,7 +2720,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);
@@ -2785,7 +2733,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;
@@ -2827,7 +2775,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 {
@@ -2854,7 +2802,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;
@@ -3523,7 +3471,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,
};
@@ -4745,16 +4692,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;
@@ -4767,9 +4711,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);
@@ -4820,22 +4761,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);
@@ -5160,7 +5091,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) {
@@ -5255,7 +5185,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:
@@ -5271,14 +5200,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)
@@ -5322,10 +5276,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.49.0
^ permalink raw reply related [flat|nested] 58+ messages in thread
* Re: [PATCH V3 7/7] f2fs: switch to the new mount api
2025-04-23 17:08 ` [PATCH V3 7/7] f2fs: switch to the new mount api Eric Sandeen
@ 2025-05-08 9:19 ` Chao Yu
2025-05-08 15:59 ` Eric Sandeen
2025-05-13 8:59 ` Chao Yu
2025-05-13 16:11 ` Jaegeuk Kim
2 siblings, 1 reply; 58+ messages in thread
From: Chao Yu @ 2025-05-08 9:19 UTC (permalink / raw)
To: Eric Sandeen, linux-f2fs-devel; +Cc: chao, linux-fsdevel, jaegeuk, lihongbo22
On 4/24/25 01:08, Eric Sandeen wrote:
> 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 37497fd80bb9..041bd6c482a0 100644
> --- a/fs/f2fs/super.c
> +++ b/fs/f2fs/super.c
> @@ -1141,47 +1141,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, ¶m);
> - kfree(param.string);
> - if (ret < 0)
> - return ret;
> - }
> - }
> - return 0;
> -}
> -
> /*
> * Check quota settings consistency.
> */
> @@ -2583,13 +2542,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;
> @@ -2635,7 +2593,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);
> @@ -2645,21 +2603,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;
There is a retry flow during f2fs_fill_super(), I intenionally inject a
fault into f2fs_fill_super() to trigger the retry flow, it turns out that
mount option may be missed w/ below testcase:
- mkfs.f2fs -f -O encrypt /dev/vdb
- mount -o test_dummy_encryption /dev/vdb /mnt/f2fs/
: return success
- dmesg -c
[ 83.619982] f2fs_fill_super, retry_cnt:1
[ 83.620914] F2FS-fs (vdb): Test dummy encryption mode enabled
[ 83.668380] f2fs_fill_super, retry_cnt:0
[ 83.671601] F2FS-fs (vdb): Mounted with checkpoint version = 7a8dfca5
- mount|grep f2fs
/dev/vdb on /mnt/f2fs type f2fs (rw,relatime,lazytime,background_gc=on,nogc_merge,
discard,discard_unit=block,user_xattr,inline_xattr,acl,inline_data,inline_dentry,
flush_merge,barrier,extent_cache,mode=adaptive,active_logs=6,alloc_mode=reuse,
checkpoint_merge,fsync_mode=posix,memory=normal,errors=continue)
The reason may be it has cleared F2FS_CTX_INFO(ctx).dummy_enc_policy in
f2fs_apply_test_dummy_encryption().
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");
}
Can we save old mount_info from sbi or ctx from fc, and try to recover it
before we retry mount flow?
Thanks,
> -
> - 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) &&
> @@ -2678,20 +2626,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)) {
> @@ -2747,7 +2695,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;
> @@ -2758,7 +2706,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) {
> @@ -2772,7 +2720,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);
> @@ -2785,7 +2733,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;
> @@ -2827,7 +2775,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 {
> @@ -2854,7 +2802,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;
> @@ -3523,7 +3471,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,
> };
>
> @@ -4745,16 +4692,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;
> @@ -4767,9 +4711,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);
> @@ -4820,22 +4761,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);
> @@ -5160,7 +5091,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) {
> @@ -5255,7 +5185,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:
> @@ -5271,14 +5200,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)
> @@ -5322,10 +5276,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,
> };
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 7/7] f2fs: switch to the new mount api
2025-05-08 9:19 ` Chao Yu
@ 2025-05-08 15:59 ` Eric Sandeen
2025-05-12 3:43 ` Chao Yu
0 siblings, 1 reply; 58+ messages in thread
From: Eric Sandeen @ 2025-05-08 15:59 UTC (permalink / raw)
To: Chao Yu, linux-f2fs-devel; +Cc: linux-fsdevel, jaegeuk, lihongbo22
On 5/8/25 4:19 AM, Chao Yu wrote:
>> @@ -2645,21 +2603,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;
> There is a retry flow during f2fs_fill_super(), I intenionally inject a
> fault into f2fs_fill_super() to trigger the retry flow, it turns out that
> mount option may be missed w/ below testcase:
I never did understand that retry logic (introduced in ed2e621a95d long
ago). What errors does it expect to be able to retry, with success?
Anyway ...
Can you show me (as a patch) exactly what you did to trigger the retry,
just so we are looking at the same thing?
> - mkfs.f2fs -f -O encrypt /dev/vdb
> - mount -o test_dummy_encryption /dev/vdb /mnt/f2fs/
> : return success
> - dmesg -c
>
> [ 83.619982] f2fs_fill_super, retry_cnt:1
> [ 83.620914] F2FS-fs (vdb): Test dummy encryption mode enabled
> [ 83.668380] f2fs_fill_super, retry_cnt:0
> [ 83.671601] F2FS-fs (vdb): Mounted with checkpoint version = 7a8dfca5
>
> - mount|grep f2fs
> /dev/vdb on /mnt/f2fs type f2fs (rw,relatime,lazytime,background_gc=on,nogc_merge,
> discard,discard_unit=block,user_xattr,inline_xattr,acl,inline_data,inline_dentry,
> flush_merge,barrier,extent_cache,mode=adaptive,active_logs=6,alloc_mode=reuse,
> checkpoint_merge,fsync_mode=posix,memory=normal,errors=continue)
>
> The reason may be it has cleared F2FS_CTX_INFO(ctx).dummy_enc_policy in
> f2fs_apply_test_dummy_encryption().
>
> 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");
> }
>
> Can we save old mount_info from sbi or ctx from fc, and try to recover it
> before we retry mount flow?
I'll have to take more time to understand this concern. But thanks for pointing
it out.
-Eric
> Thanks,
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 7/7] f2fs: switch to the new mount api
2025-05-08 15:59 ` Eric Sandeen
@ 2025-05-12 3:43 ` Chao Yu
2025-05-13 2:19 ` Eric Sandeen
0 siblings, 1 reply; 58+ messages in thread
From: Chao Yu @ 2025-05-12 3:43 UTC (permalink / raw)
To: Eric Sandeen, linux-f2fs-devel; +Cc: chao, linux-fsdevel, jaegeuk, lihongbo22
On 5/8/25 23:59, Eric Sandeen wrote:
> On 5/8/25 4:19 AM, Chao Yu wrote:
>>> @@ -2645,21 +2603,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;
>> There is a retry flow during f2fs_fill_super(), I intenionally inject a
>> fault into f2fs_fill_super() to trigger the retry flow, it turns out that
>> mount option may be missed w/ below testcase:
>
> I never did understand that retry logic (introduced in ed2e621a95d long
> ago). What errors does it expect to be able to retry, with success?
IIRC, it will retry mount if there is recovery failure due to inconsistent
metadata.
>
> Anyway ...
>
> Can you show me (as a patch) exactly what you did to trigger the retry,
> just so we are looking at the same thing?
You can try this?
---
fs/f2fs/super.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index 0ee783224953..10f0e66059f8 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -5066,6 +5066,12 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc)
goto reset_checkpoint;
}
+ if (retry_cnt) {
+ err = -EIO;
+ skip_recovery = true;
+ goto free_meta;
+ }
+
/* recover fsynced data */
if (!test_opt(sbi, DISABLE_ROLL_FORWARD) &&
!test_opt(sbi, NORECOVERY)) {
--
2.49.0
Thanks,
>
>> - mkfs.f2fs -f -O encrypt /dev/vdb
>> - mount -o test_dummy_encryption /dev/vdb /mnt/f2fs/
>> : return success
>> - dmesg -c
>>
>> [ 83.619982] f2fs_fill_super, retry_cnt:1
>> [ 83.620914] F2FS-fs (vdb): Test dummy encryption mode enabled
>> [ 83.668380] f2fs_fill_super, retry_cnt:0
>> [ 83.671601] F2FS-fs (vdb): Mounted with checkpoint version = 7a8dfca5
>>
>> - mount|grep f2fs
>> /dev/vdb on /mnt/f2fs type f2fs (rw,relatime,lazytime,background_gc=on,nogc_merge,
>> discard,discard_unit=block,user_xattr,inline_xattr,acl,inline_data,inline_dentry,
>> flush_merge,barrier,extent_cache,mode=adaptive,active_logs=6,alloc_mode=reuse,
>> checkpoint_merge,fsync_mode=posix,memory=normal,errors=continue)
>>
>> The reason may be it has cleared F2FS_CTX_INFO(ctx).dummy_enc_policy in
>> f2fs_apply_test_dummy_encryption().
>>
>> 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");
>> }
>>
>> Can we save old mount_info from sbi or ctx from fc, and try to recover it
>> before we retry mount flow?
>
> I'll have to take more time to understand this concern. But thanks for pointing
> it out.
>
> -Eric
>
>> Thanks,
>
^ permalink raw reply related [flat|nested] 58+ messages in thread
* Re: [PATCH V3 7/7] f2fs: switch to the new mount api
2025-05-12 3:43 ` Chao Yu
@ 2025-05-13 2:19 ` Eric Sandeen
2025-05-13 2:48 ` Chao Yu
0 siblings, 1 reply; 58+ messages in thread
From: Eric Sandeen @ 2025-05-13 2:19 UTC (permalink / raw)
To: Chao Yu, linux-f2fs-devel; +Cc: jaegeuk, lihongbo22, linux-fsdevel
On 5/11/25 10:43 PM, Chao Yu wrote:
> On 5/8/25 23:59, Eric Sandeen wrote:
>> On 5/8/25 4:19 AM, Chao Yu wrote:
>>>> @@ -2645,21 +2603,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;
>>> There is a retry flow during f2fs_fill_super(), I intenionally
>>> inject a fault into f2fs_fill_super() to trigger the retry flow,
>>> it turns out that mount option may be missed w/ below testcase:
>>
>> I never did understand that retry logic (introduced in ed2e621a95d
>> long ago). What errors does it expect to be able to retry, with
>> success?
>
> IIRC, it will retry mount if there is recovery failure due to
> inconsistent metadata.
Sure, I just wonder what would cause inconsistent metadata to become consistent
after 1 retry ...
>>
>> Anyway ...
>>
>> Can you show me (as a patch) exactly what you did to trigger the
>> retry, just so we are looking at the same thing?
>
> You can try this?
Ok, thanks!
-Eric
> --- fs/f2fs/super.c | 6 ++++++ 1 file changed, 6 insertions(+)
>
> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index
> 0ee783224953..10f0e66059f8 100644 --- a/fs/f2fs/super.c +++ b/fs/
> f2fs/super.c @@ -5066,6 +5066,12 @@ static int
> f2fs_fill_super(struct super_block *sb, struct fs_context *fc) goto
> reset_checkpoint; }
>
> + if (retry_cnt) { + err = -EIO; + skip_recovery = true; + goto
> free_meta; + } + /* recover fsynced data */ if (!test_opt(sbi,
> DISABLE_ROLL_FORWARD) && !test_opt(sbi, NORECOVERY)) {
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 7/7] f2fs: switch to the new mount api
2025-05-13 2:19 ` Eric Sandeen
@ 2025-05-13 2:48 ` Chao Yu
2025-05-13 15:36 ` Jaegeuk Kim
0 siblings, 1 reply; 58+ messages in thread
From: Chao Yu @ 2025-05-13 2:48 UTC (permalink / raw)
To: Eric Sandeen, linux-f2fs-devel; +Cc: chao, jaegeuk, lihongbo22, linux-fsdevel
On 5/13/25 10:19, Eric Sandeen wrote:
> On 5/11/25 10:43 PM, Chao Yu wrote:
>> On 5/8/25 23:59, Eric Sandeen wrote:
>>> On 5/8/25 4:19 AM, Chao Yu wrote:
>>>>> @@ -2645,21 +2603,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;
>>>> There is a retry flow during f2fs_fill_super(), I intenionally
>>>> inject a fault into f2fs_fill_super() to trigger the retry flow,
>>>> it turns out that mount option may be missed w/ below testcase:
>>>
>>> I never did understand that retry logic (introduced in ed2e621a95d
>>> long ago). What errors does it expect to be able to retry, with
>>> success?
>>
>> IIRC, it will retry mount if there is recovery failure due to
>> inconsistent metadata.
>
> Sure, I just wonder what would cause inconsistent metadata to become consistent
> after 1 retry ...
I don't remember, Jaegeuk, do you remember?
Thanks,
>
>>>
>>> Anyway ...
>>>
>>> Can you show me (as a patch) exactly what you did to trigger the
>>> retry, just so we are looking at the same thing?
>>
>> You can try this?
>
> Ok, thanks!
> -Eric
>
>> --- fs/f2fs/super.c | 6 ++++++ 1 file changed, 6 insertions(+)
>>
>> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index
>> 0ee783224953..10f0e66059f8 100644 --- a/fs/f2fs/super.c +++ b/fs/
>> f2fs/super.c @@ -5066,6 +5066,12 @@ static int
>> f2fs_fill_super(struct super_block *sb, struct fs_context *fc) goto
>> reset_checkpoint; }
>>
>> + if (retry_cnt) { + err = -EIO; + skip_recovery = true; + goto
>> free_meta; + } + /* recover fsynced data */ if (!test_opt(sbi,
>> DISABLE_ROLL_FORWARD) && !test_opt(sbi, NORECOVERY)) {
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 7/7] f2fs: switch to the new mount api
2025-05-13 2:48 ` Chao Yu
@ 2025-05-13 15:36 ` Jaegeuk Kim
0 siblings, 0 replies; 58+ messages in thread
From: Jaegeuk Kim @ 2025-05-13 15:36 UTC (permalink / raw)
To: Chao Yu; +Cc: Eric Sandeen, linux-f2fs-devel, lihongbo22, linux-fsdevel
On 05/13, Chao Yu wrote:
> On 5/13/25 10:19, Eric Sandeen wrote:
> > On 5/11/25 10:43 PM, Chao Yu wrote:
> >> On 5/8/25 23:59, Eric Sandeen wrote:
> >>> On 5/8/25 4:19 AM, Chao Yu wrote:
> >>>>> @@ -2645,21 +2603,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;
> >>>> There is a retry flow during f2fs_fill_super(), I intenionally
> >>>> inject a fault into f2fs_fill_super() to trigger the retry flow,
> >>>> it turns out that mount option may be missed w/ below testcase:
> >>>
> >>> I never did understand that retry logic (introduced in ed2e621a95d
> >>> long ago). What errors does it expect to be able to retry, with
> >>> success?
> >>
> >> IIRC, it will retry mount if there is recovery failure due to
> >> inconsistent metadata.
> >
> > Sure, I just wonder what would cause inconsistent metadata to become consistent
> > after 1 retry ...
>
> I don't remember, Jaegeuk, do you remember?
I remember, if the roll-forward recovery ended up with an error, we had better
retry mount() as we may have some online fixes.
>
> Thanks,
>
> >
> >>>
> >>> Anyway ...
> >>>
> >>> Can you show me (as a patch) exactly what you did to trigger the
> >>> retry, just so we are looking at the same thing?
> >>
> >> You can try this?
> >
> > Ok, thanks!
> > -Eric
> >
> >> --- fs/f2fs/super.c | 6 ++++++ 1 file changed, 6 insertions(+)
> >>
> >> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index
> >> 0ee783224953..10f0e66059f8 100644 --- a/fs/f2fs/super.c +++ b/fs/
> >> f2fs/super.c @@ -5066,6 +5066,12 @@ static int
> >> f2fs_fill_super(struct super_block *sb, struct fs_context *fc) goto
> >> reset_checkpoint; }
> >>
> >> + if (retry_cnt) { + err = -EIO; + skip_recovery = true; + goto
> >> free_meta; + } + /* recover fsynced data */ if (!test_opt(sbi,
> >> DISABLE_ROLL_FORWARD) && !test_opt(sbi, NORECOVERY)) {
> >
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 7/7] f2fs: switch to the new mount api
2025-04-23 17:08 ` [PATCH V3 7/7] f2fs: switch to the new mount api Eric Sandeen
2025-05-08 9:19 ` Chao Yu
@ 2025-05-13 8:59 ` Chao Yu
2025-05-14 2:33 ` Hongbo Li
2025-05-13 16:11 ` Jaegeuk Kim
2 siblings, 1 reply; 58+ messages in thread
From: Chao Yu @ 2025-05-13 8:59 UTC (permalink / raw)
To: Eric Sandeen, lihongbo22; +Cc: chao, linux-fsdevel, jaegeuk, linux-f2fs-devel
On 4/24/25 01:08, Eric Sandeen wrote:
> 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.
mkfs.f2fs -f -O extra_attr,flexible_inline_xattr /dev/vdb
mount -o inline_xattr_size=512 /dev/vdb /mnt/f2fs
mount: /mnt/f2fs: wrong fs type, bad option, bad superblock on /dev/vdb, missing codepage or helper program, or other error.
dmesg(1) may have more information after failed mount system call.
dmesg
[ 1758.202282] F2FS-fs (vdb): Image doesn't support compression
[ 1758.202286] F2FS-fs (vdb): inline_xattr_size option should be set with inline_xattr option
Eric, Hongbo, can you please take a look at this issue?
Thanks,
>
> 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 37497fd80bb9..041bd6c482a0 100644
> --- a/fs/f2fs/super.c
> +++ b/fs/f2fs/super.c
> @@ -1141,47 +1141,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, ¶m);
> - kfree(param.string);
> - if (ret < 0)
> - return ret;
> - }
> - }
> - return 0;
> -}
> -
> /*
> * Check quota settings consistency.
> */
> @@ -2583,13 +2542,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;
> @@ -2635,7 +2593,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);
> @@ -2645,21 +2603,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) &&
> @@ -2678,20 +2626,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)) {
> @@ -2747,7 +2695,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;
> @@ -2758,7 +2706,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) {
> @@ -2772,7 +2720,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);
> @@ -2785,7 +2733,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;
> @@ -2827,7 +2775,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 {
> @@ -2854,7 +2802,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;
> @@ -3523,7 +3471,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,
> };
>
> @@ -4745,16 +4692,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;
> @@ -4767,9 +4711,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);
> @@ -4820,22 +4761,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);
> @@ -5160,7 +5091,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) {
> @@ -5255,7 +5185,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:
> @@ -5271,14 +5200,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)
> @@ -5322,10 +5276,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,
> };
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 7/7] f2fs: switch to the new mount api
2025-05-13 8:59 ` Chao Yu
@ 2025-05-14 2:33 ` Hongbo Li
2025-05-14 4:03 ` Chao Yu
0 siblings, 1 reply; 58+ messages in thread
From: Hongbo Li @ 2025-05-14 2:33 UTC (permalink / raw)
To: Chao Yu, Eric Sandeen; +Cc: linux-fsdevel, jaegeuk, linux-f2fs-devel
On 2025/5/13 16:59, Chao Yu wrote:
> On 4/24/25 01:08, Eric Sandeen wrote:
>> 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.
>
> mkfs.f2fs -f -O extra_attr,flexible_inline_xattr /dev/vdb
> mount -o inline_xattr_size=512 /dev/vdb /mnt/f2fs
> mount: /mnt/f2fs: wrong fs type, bad option, bad superblock on /dev/vdb, missing codepage or helper program, or other error.
> dmesg(1) may have more information after failed mount system call.
> dmesg
> [ 1758.202282] F2FS-fs (vdb): Image doesn't support compression
> [ 1758.202286] F2FS-fs (vdb): inline_xattr_size option should be set with inline_xattr option
>
> Eric, Hongbo, can you please take a look at this issue?
>
Sorry, we only check the option hold in ctx, we should do the double
check in sbi. Or other elegant approaches.
For the "support compression", is it also the error in this testcase?
Thanks,
Hongbo
> Thanks,
>
>>
>> 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 37497fd80bb9..041bd6c482a0 100644
>> --- a/fs/f2fs/super.c
>> +++ b/fs/f2fs/super.c
>> @@ -1141,47 +1141,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, ¶m);
>> - kfree(param.string);
>> - if (ret < 0)
>> - return ret;
>> - }
>> - }
>> - return 0;
>> -}
>> -
>> /*
>> * Check quota settings consistency.
>> */
>> @@ -2583,13 +2542,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;
>> @@ -2635,7 +2593,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);
>> @@ -2645,21 +2603,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) &&
>> @@ -2678,20 +2626,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)) {
>> @@ -2747,7 +2695,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;
>> @@ -2758,7 +2706,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) {
>> @@ -2772,7 +2720,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);
>> @@ -2785,7 +2733,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;
>> @@ -2827,7 +2775,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 {
>> @@ -2854,7 +2802,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;
>> @@ -3523,7 +3471,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,
>> };
>>
>> @@ -4745,16 +4692,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;
>> @@ -4767,9 +4711,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);
>> @@ -4820,22 +4761,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);
>> @@ -5160,7 +5091,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) {
>> @@ -5255,7 +5185,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:
>> @@ -5271,14 +5200,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)
>> @@ -5322,10 +5276,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,
>> };
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 7/7] f2fs: switch to the new mount api
2025-05-14 2:33 ` Hongbo Li
@ 2025-05-14 4:03 ` Chao Yu
2025-05-14 6:15 ` Hongbo Li
0 siblings, 1 reply; 58+ messages in thread
From: Chao Yu @ 2025-05-14 4:03 UTC (permalink / raw)
To: Hongbo Li, Eric Sandeen; +Cc: chao, linux-fsdevel, jaegeuk, linux-f2fs-devel
On 5/14/25 10:33, Hongbo Li wrote:
>
>
> On 2025/5/13 16:59, Chao Yu wrote:
>> On 4/24/25 01:08, Eric Sandeen wrote:
>>> 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.
>>
>> mkfs.f2fs -f -O extra_attr,flexible_inline_xattr /dev/vdb
>> mount -o inline_xattr_size=512 /dev/vdb /mnt/f2fs
>> mount: /mnt/f2fs: wrong fs type, bad option, bad superblock on /dev/vdb, missing codepage or helper program, or other error.
>> dmesg(1) may have more information after failed mount system call.
>> dmesg
>> [ 1758.202282] F2FS-fs (vdb): Image doesn't support compression
>> [ 1758.202286] F2FS-fs (vdb): inline_xattr_size option should be set with inline_xattr option
>>
>> Eric, Hongbo, can you please take a look at this issue?
>>
> Sorry, we only check the option hold in ctx, we should do the double check in sbi. Or other elegant approaches.
>
> For the "support compression", is it also the error in this testcase?
I think so, I checked this w/ additional logs, and found this:
#define F2FS_MOUNT_INLINE_XATTR_SIZE 0x00080000
#define F2FS_SPEC_compress_chksum (1 << 19) /* equal to 0x00080000)
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;
}
We should use test_compression_spec(ctx->spec_mask) instead of
test_compression_spec(ctx->opt_mask) to check special mask of compression
option?
Thanks,
>
> Thanks,
> Hongbo
>
>> Thanks,
>>
>>>
>>> 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 37497fd80bb9..041bd6c482a0 100644
>>> --- a/fs/f2fs/super.c
>>> +++ b/fs/f2fs/super.c
>>> @@ -1141,47 +1141,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, ¶m);
>>> - kfree(param.string);
>>> - if (ret < 0)
>>> - return ret;
>>> - }
>>> - }
>>> - return 0;
>>> -}
>>> -
>>> /*
>>> * Check quota settings consistency.
>>> */
>>> @@ -2583,13 +2542,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;
>>> @@ -2635,7 +2593,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);
>>> @@ -2645,21 +2603,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) &&
>>> @@ -2678,20 +2626,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)) {
>>> @@ -2747,7 +2695,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;
>>> @@ -2758,7 +2706,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) {
>>> @@ -2772,7 +2720,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);
>>> @@ -2785,7 +2733,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;
>>> @@ -2827,7 +2775,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 {
>>> @@ -2854,7 +2802,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;
>>> @@ -3523,7 +3471,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,
>>> };
>>> @@ -4745,16 +4692,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;
>>> @@ -4767,9 +4711,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);
>>> @@ -4820,22 +4761,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);
>>> @@ -5160,7 +5091,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) {
>>> @@ -5255,7 +5185,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:
>>> @@ -5271,14 +5200,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)
>>> @@ -5322,10 +5276,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,
>>> };
>>
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 7/7] f2fs: switch to the new mount api
2025-05-14 4:03 ` Chao Yu
@ 2025-05-14 6:15 ` Hongbo Li
2025-05-14 15:30 ` Jaegeuk Kim
0 siblings, 1 reply; 58+ messages in thread
From: Hongbo Li @ 2025-05-14 6:15 UTC (permalink / raw)
To: Chao Yu, Eric Sandeen; +Cc: linux-fsdevel, jaegeuk, linux-f2fs-devel
On 2025/5/14 12:03, Chao Yu wrote:
> On 5/14/25 10:33, Hongbo Li wrote:
>>
>>
>> On 2025/5/13 16:59, Chao Yu wrote:
>>> On 4/24/25 01:08, Eric Sandeen wrote:
>>>> 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.
>>>
>>> mkfs.f2fs -f -O extra_attr,flexible_inline_xattr /dev/vdb
>>> mount -o inline_xattr_size=512 /dev/vdb /mnt/f2fs
>>> mount: /mnt/f2fs: wrong fs type, bad option, bad superblock on /dev/vdb, missing codepage or helper program, or other error.
>>> dmesg(1) may have more information after failed mount system call.
>>> dmesg
>>> [ 1758.202282] F2FS-fs (vdb): Image doesn't support compression
>>> [ 1758.202286] F2FS-fs (vdb): inline_xattr_size option should be set with inline_xattr option
>>>
>>> Eric, Hongbo, can you please take a look at this issue?
>>>
>> Sorry, we only check the option hold in ctx, we should do the double check in sbi. Or other elegant approaches.
>>
>> For the "support compression", is it also the error in this testcase?
>
> I think so, I checked this w/ additional logs, and found this:
>
> #define F2FS_MOUNT_INLINE_XATTR_SIZE 0x00080000
> #define F2FS_SPEC_compress_chksum (1 << 19) /* equal to 0x00080000)
>
> 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;
> }
>
> We should use test_compression_spec(ctx->spec_mask) instead of
> test_compression_spec(ctx->opt_mask) to check special mask of compression
> option?
>
Yeah, you're right. test_compression_spec is used to check spec_mask,
and we got it wrong.
Thanks,
Hongbo
> Thanks,
>
>>
>> Thanks,
>> Hongbo
>>
>>> Thanks,
>>>
>>>>
>>>> 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 37497fd80bb9..041bd6c482a0 100644
>>>> --- a/fs/f2fs/super.c
>>>> +++ b/fs/f2fs/super.c
>>>> @@ -1141,47 +1141,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, ¶m);
>>>> - kfree(param.string);
>>>> - if (ret < 0)
>>>> - return ret;
>>>> - }
>>>> - }
>>>> - return 0;
>>>> -}
>>>> -
>>>> /*
>>>> * Check quota settings consistency.
>>>> */
>>>> @@ -2583,13 +2542,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;
>>>> @@ -2635,7 +2593,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);
>>>> @@ -2645,21 +2603,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) &&
>>>> @@ -2678,20 +2626,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)) {
>>>> @@ -2747,7 +2695,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;
>>>> @@ -2758,7 +2706,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) {
>>>> @@ -2772,7 +2720,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);
>>>> @@ -2785,7 +2733,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;
>>>> @@ -2827,7 +2775,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 {
>>>> @@ -2854,7 +2802,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;
>>>> @@ -3523,7 +3471,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,
>>>> };
>>>> @@ -4745,16 +4692,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;
>>>> @@ -4767,9 +4711,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);
>>>> @@ -4820,22 +4761,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);
>>>> @@ -5160,7 +5091,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) {
>>>> @@ -5255,7 +5185,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:
>>>> @@ -5271,14 +5200,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)
>>>> @@ -5322,10 +5276,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,
>>>> };
>>>
>
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 7/7] f2fs: switch to the new mount api
2025-05-14 6:15 ` Hongbo Li
@ 2025-05-14 15:30 ` Jaegeuk Kim
2025-05-14 15:46 ` Eric Sandeen
` (2 more replies)
0 siblings, 3 replies; 58+ messages in thread
From: Jaegeuk Kim @ 2025-05-14 15:30 UTC (permalink / raw)
To: Hongbo Li; +Cc: Chao Yu, Eric Sandeen, linux-fsdevel, linux-f2fs-devel
Hi, Hongbo,
It seems we're getting more issues in the patch set. May I ask for some
help sending the new patch series having all the fixes that I made as well
as addressing the concerns? You can get the patches from [1].
[1] https://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git/log/?h=dev-test
On 05/14, Hongbo Li wrote:
>
>
> On 2025/5/14 12:03, Chao Yu wrote:
> > On 5/14/25 10:33, Hongbo Li wrote:
> > >
> > >
> > > On 2025/5/13 16:59, Chao Yu wrote:
> > > > On 4/24/25 01:08, Eric Sandeen wrote:
> > > > > 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.
> > > >
> > > > mkfs.f2fs -f -O extra_attr,flexible_inline_xattr /dev/vdb
> > > > mount -o inline_xattr_size=512 /dev/vdb /mnt/f2fs
> > > > mount: /mnt/f2fs: wrong fs type, bad option, bad superblock on /dev/vdb, missing codepage or helper program, or other error.
> > > > dmesg(1) may have more information after failed mount system call.
> > > > dmesg
> > > > [ 1758.202282] F2FS-fs (vdb): Image doesn't support compression
> > > > [ 1758.202286] F2FS-fs (vdb): inline_xattr_size option should be set with inline_xattr option
> > > >
> > > > Eric, Hongbo, can you please take a look at this issue?
> > > >
> > > Sorry, we only check the option hold in ctx, we should do the double check in sbi. Or other elegant approaches.
> > >
> > > For the "support compression", is it also the error in this testcase?
> >
> > I think so, I checked this w/ additional logs, and found this:
> >
> > #define F2FS_MOUNT_INLINE_XATTR_SIZE 0x00080000
> > #define F2FS_SPEC_compress_chksum (1 << 19) /* equal to 0x00080000)
> >
> > 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;
> > }
> >
> > We should use test_compression_spec(ctx->spec_mask) instead of
> > test_compression_spec(ctx->opt_mask) to check special mask of compression
> > option?
> >
>
> Yeah, you're right. test_compression_spec is used to check spec_mask, and we
> got it wrong.
>
> Thanks,
> Hongbo
>
> > Thanks,
> >
> > >
> > > Thanks,
> > > Hongbo
> > >
> > > > Thanks,
> > > >
> > > > >
> > > > > 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 37497fd80bb9..041bd6c482a0 100644
> > > > > --- a/fs/f2fs/super.c
> > > > > +++ b/fs/f2fs/super.c
> > > > > @@ -1141,47 +1141,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, ¶m);
> > > > > - kfree(param.string);
> > > > > - if (ret < 0)
> > > > > - return ret;
> > > > > - }
> > > > > - }
> > > > > - return 0;
> > > > > -}
> > > > > -
> > > > > /*
> > > > > * Check quota settings consistency.
> > > > > */
> > > > > @@ -2583,13 +2542,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;
> > > > > @@ -2635,7 +2593,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);
> > > > > @@ -2645,21 +2603,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) &&
> > > > > @@ -2678,20 +2626,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)) {
> > > > > @@ -2747,7 +2695,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;
> > > > > @@ -2758,7 +2706,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) {
> > > > > @@ -2772,7 +2720,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);
> > > > > @@ -2785,7 +2733,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;
> > > > > @@ -2827,7 +2775,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 {
> > > > > @@ -2854,7 +2802,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;
> > > > > @@ -3523,7 +3471,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,
> > > > > };
> > > > > @@ -4745,16 +4692,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;
> > > > > @@ -4767,9 +4711,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);
> > > > > @@ -4820,22 +4761,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);
> > > > > @@ -5160,7 +5091,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) {
> > > > > @@ -5255,7 +5185,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:
> > > > > @@ -5271,14 +5200,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)
> > > > > @@ -5322,10 +5276,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,
> > > > > };
> > > >
> >
> >
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 7/7] f2fs: switch to the new mount api
2025-05-14 15:30 ` Jaegeuk Kim
@ 2025-05-14 15:46 ` Eric Sandeen
2025-05-15 1:17 ` Hongbo Li
2025-05-16 2:01 ` Hongbo Li
2 siblings, 0 replies; 58+ messages in thread
From: Eric Sandeen @ 2025-05-14 15:46 UTC (permalink / raw)
To: Jaegeuk Kim, Hongbo Li; +Cc: Chao Yu, linux-fsdevel, linux-f2fs-devel
On 5/14/25 10:30 AM, Jaegeuk Kim wrote:
> Hi, Hongbo,
>
> It seems we're getting more issues in the patch set. May I ask for some
> help sending the new patch series having all the fixes that I made as well
> as addressing the concerns? You can get the patches from [1].
>
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git/log/?h=dev-test
Apologies for being a little absent, some urgent things came up internally
for me at work and I've had less time for this. I'm planning to get
back to it!
It's been a long thread, perhaps we could summarize the remaining questions and
concerns?
And I'm sorry for the errors that got through, I really thought my testing was
fairly exhaustive, but it appears that I missed several cases.
(To be fair, f2fs has far more mount options than any other filesystem, and
combining that with on-disk feature variants, it is a very big test matrix.)
Thanks,
-Eric
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 7/7] f2fs: switch to the new mount api
2025-05-14 15:30 ` Jaegeuk Kim
2025-05-14 15:46 ` Eric Sandeen
@ 2025-05-15 1:17 ` Hongbo Li
2025-05-16 2:01 ` Hongbo Li
2 siblings, 0 replies; 58+ messages in thread
From: Hongbo Li @ 2025-05-15 1:17 UTC (permalink / raw)
To: Jaegeuk Kim; +Cc: Chao Yu, Eric Sandeen, linux-fsdevel, linux-f2fs-devel
On 2025/5/14 23:30, Jaegeuk Kim wrote:
> Hi, Hongbo,
>
> It seems we're getting more issues in the patch set. May I ask for some
> help sending the new patch series having all the fixes that I made as well
> as addressing the concerns? You can get the patches from [1].
Ok, thank you and Chao for reviewing the code. And apologies for the
errors in the patch set. I will solve the issues pointed in the thead
with Eric in later version.
Thanks,
Hongbo
>
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git/log/?h=dev-test
>
> On 05/14, Hongbo Li wrote:
>>
>>
>> On 2025/5/14 12:03, Chao Yu wrote:
>>> On 5/14/25 10:33, Hongbo Li wrote:
>>>>
>>>>
>>>> On 2025/5/13 16:59, Chao Yu wrote:
>>>>> On 4/24/25 01:08, Eric Sandeen wrote:
>>>>>> 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.
>>>>>
>>>>> mkfs.f2fs -f -O extra_attr,flexible_inline_xattr /dev/vdb
>>>>> mount -o inline_xattr_size=512 /dev/vdb /mnt/f2fs
>>>>> mount: /mnt/f2fs: wrong fs type, bad option, bad superblock on /dev/vdb, missing codepage or helper program, or other error.
>>>>> dmesg(1) may have more information after failed mount system call.
>>>>> dmesg
>>>>> [ 1758.202282] F2FS-fs (vdb): Image doesn't support compression
>>>>> [ 1758.202286] F2FS-fs (vdb): inline_xattr_size option should be set with inline_xattr option
>>>>>
>>>>> Eric, Hongbo, can you please take a look at this issue?
>>>>>
>>>> Sorry, we only check the option hold in ctx, we should do the double check in sbi. Or other elegant approaches.
>>>>
>>>> For the "support compression", is it also the error in this testcase?
>>>
>>> I think so, I checked this w/ additional logs, and found this:
>>>
>>> #define F2FS_MOUNT_INLINE_XATTR_SIZE 0x00080000
>>> #define F2FS_SPEC_compress_chksum (1 << 19) /* equal to 0x00080000)
>>>
>>> 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;
>>> }
>>>
>>> We should use test_compression_spec(ctx->spec_mask) instead of
>>> test_compression_spec(ctx->opt_mask) to check special mask of compression
>>> option?
>>>
>>
>> Yeah, you're right. test_compression_spec is used to check spec_mask, and we
>> got it wrong.
>>
>> Thanks,
>> Hongbo
>>
>>> Thanks,
>>>
>>>>
>>>> Thanks,
>>>> Hongbo
>>>>
>>>>> Thanks,
>>>>>
>>>>>>
>>>>>> 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 37497fd80bb9..041bd6c482a0 100644
>>>>>> --- a/fs/f2fs/super.c
>>>>>> +++ b/fs/f2fs/super.c
>>>>>> @@ -1141,47 +1141,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, ¶m);
>>>>>> - kfree(param.string);
>>>>>> - if (ret < 0)
>>>>>> - return ret;
>>>>>> - }
>>>>>> - }
>>>>>> - return 0;
>>>>>> -}
>>>>>> -
>>>>>> /*
>>>>>> * Check quota settings consistency.
>>>>>> */
>>>>>> @@ -2583,13 +2542,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;
>>>>>> @@ -2635,7 +2593,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);
>>>>>> @@ -2645,21 +2603,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) &&
>>>>>> @@ -2678,20 +2626,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)) {
>>>>>> @@ -2747,7 +2695,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;
>>>>>> @@ -2758,7 +2706,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) {
>>>>>> @@ -2772,7 +2720,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);
>>>>>> @@ -2785,7 +2733,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;
>>>>>> @@ -2827,7 +2775,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 {
>>>>>> @@ -2854,7 +2802,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;
>>>>>> @@ -3523,7 +3471,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,
>>>>>> };
>>>>>> @@ -4745,16 +4692,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;
>>>>>> @@ -4767,9 +4711,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);
>>>>>> @@ -4820,22 +4761,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);
>>>>>> @@ -5160,7 +5091,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) {
>>>>>> @@ -5255,7 +5185,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:
>>>>>> @@ -5271,14 +5200,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)
>>>>>> @@ -5322,10 +5276,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,
>>>>>> };
>>>>>
>>>
>>>
>
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 7/7] f2fs: switch to the new mount api
2025-05-14 15:30 ` Jaegeuk Kim
2025-05-14 15:46 ` Eric Sandeen
2025-05-15 1:17 ` Hongbo Li
@ 2025-05-16 2:01 ` Hongbo Li
2025-05-16 17:35 ` Jaegeuk Kim
2 siblings, 1 reply; 58+ messages in thread
From: Hongbo Li @ 2025-05-16 2:01 UTC (permalink / raw)
To: Jaegeuk Kim; +Cc: Chao Yu, Eric Sandeen, linux-fsdevel, linux-f2fs-devel
On 2025/5/14 23:30, Jaegeuk Kim wrote:
> Hi, Hongbo,
>
> It seems we're getting more issues in the patch set. May I ask for some
> help sending the new patch series having all the fixes that I made as well
> as addressing the concerns? You can get the patches from [1].
>
> [1] https://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git/log/?h=dev-test
>
Hi, Jaegeuk
I will discuss these issues with Eric. It may take some time, but not
too long. When we send the next version, should we resend this patch
series based on dev-test after modifying the code, only removing your
S-O-B? (You'll ultimately add your S-O-B back yourself)
Thanks,
Hongbo
> On 05/14, Hongbo Li wrote:
>>
>>
>> On 2025/5/14 12:03, Chao Yu wrote:
>>> On 5/14/25 10:33, Hongbo Li wrote:
>>>>
>>>>
>>>> On 2025/5/13 16:59, Chao Yu wrote:
>>>>> On 4/24/25 01:08, Eric Sandeen wrote:
>>>>>> 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.
>>>>>
>>>>> mkfs.f2fs -f -O extra_attr,flexible_inline_xattr /dev/vdb
>>>>> mount -o inline_xattr_size=512 /dev/vdb /mnt/f2fs
>>>>> mount: /mnt/f2fs: wrong fs type, bad option, bad superblock on /dev/vdb, missing codepage or helper program, or other error.
>>>>> dmesg(1) may have more information after failed mount system call.
>>>>> dmesg
>>>>> [ 1758.202282] F2FS-fs (vdb): Image doesn't support compression
>>>>> [ 1758.202286] F2FS-fs (vdb): inline_xattr_size option should be set with inline_xattr option
>>>>>
>>>>> Eric, Hongbo, can you please take a look at this issue?
>>>>>
>>>> Sorry, we only check the option hold in ctx, we should do the double check in sbi. Or other elegant approaches.
>>>>
>>>> For the "support compression", is it also the error in this testcase?
>>>
>>> I think so, I checked this w/ additional logs, and found this:
>>>
>>> #define F2FS_MOUNT_INLINE_XATTR_SIZE 0x00080000
>>> #define F2FS_SPEC_compress_chksum (1 << 19) /* equal to 0x00080000)
>>>
>>> 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;
>>> }
>>>
>>> We should use test_compression_spec(ctx->spec_mask) instead of
>>> test_compression_spec(ctx->opt_mask) to check special mask of compression
>>> option?
>>>
>>
>> Yeah, you're right. test_compression_spec is used to check spec_mask, and we
>> got it wrong.
>>
>> Thanks,
>> Hongbo
>>
>>> Thanks,
>>>
>>>>
>>>> Thanks,
>>>> Hongbo
>>>>
>>>>> Thanks,
>>>>>
>>>>>>
>>>>>> 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 37497fd80bb9..041bd6c482a0 100644
>>>>>> --- a/fs/f2fs/super.c
>>>>>> +++ b/fs/f2fs/super.c
>>>>>> @@ -1141,47 +1141,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, ¶m);
>>>>>> - kfree(param.string);
>>>>>> - if (ret < 0)
>>>>>> - return ret;
>>>>>> - }
>>>>>> - }
>>>>>> - return 0;
>>>>>> -}
>>>>>> -
>>>>>> /*
>>>>>> * Check quota settings consistency.
>>>>>> */
>>>>>> @@ -2583,13 +2542,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;
>>>>>> @@ -2635,7 +2593,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);
>>>>>> @@ -2645,21 +2603,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) &&
>>>>>> @@ -2678,20 +2626,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)) {
>>>>>> @@ -2747,7 +2695,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;
>>>>>> @@ -2758,7 +2706,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) {
>>>>>> @@ -2772,7 +2720,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);
>>>>>> @@ -2785,7 +2733,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;
>>>>>> @@ -2827,7 +2775,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 {
>>>>>> @@ -2854,7 +2802,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;
>>>>>> @@ -3523,7 +3471,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,
>>>>>> };
>>>>>> @@ -4745,16 +4692,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;
>>>>>> @@ -4767,9 +4711,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);
>>>>>> @@ -4820,22 +4761,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);
>>>>>> @@ -5160,7 +5091,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) {
>>>>>> @@ -5255,7 +5185,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:
>>>>>> @@ -5271,14 +5200,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)
>>>>>> @@ -5322,10 +5276,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,
>>>>>> };
>>>>>
>>>
>>>
>
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 7/7] f2fs: switch to the new mount api
2025-05-16 2:01 ` Hongbo Li
@ 2025-05-16 17:35 ` Jaegeuk Kim
2025-05-19 2:38 ` Hongbo Li
0 siblings, 1 reply; 58+ messages in thread
From: Jaegeuk Kim @ 2025-05-16 17:35 UTC (permalink / raw)
To: Hongbo Li; +Cc: Chao Yu, Eric Sandeen, linux-fsdevel, linux-f2fs-devel
On 05/16, Hongbo Li wrote:
>
>
> On 2025/5/14 23:30, Jaegeuk Kim wrote:
> > Hi, Hongbo,
> >
> > It seems we're getting more issues in the patch set. May I ask for some
> > help sending the new patch series having all the fixes that I made as well
> > as addressing the concerns? You can get the patches from [1].
> >
> > [1] https://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git/log/?h=dev-test
> >
>
> Hi, Jaegeuk
>
> I will discuss these issues with Eric. It may take some time, but not too
> long. When we send the next version, should we resend this patch series
> based on dev-test after modifying the code, only removing your S-O-B?
> (You'll ultimately add your S-O-B back yourself)
Hi Hongbo,
Thank you for this hard work. Could you please resend the entire patch-set based
on dev-test w/o my SOB? I'm going to dequeue the series from dev-test. Instead,
I rebased the latest version onto [2].
Please also consider another report, [1].
[1] https://lore.kernel.org/linux-f2fs-devel/2f16981b-0e13-4c64-83a8-d0e0b4297348@suswa.mountain/T/#u
[2] https://github.com/jaegeuk/f2fs/commits/mount/
>
> Thanks,
> Hongbo
>
> > On 05/14, Hongbo Li wrote:
> > >
> > >
> > > On 2025/5/14 12:03, Chao Yu wrote:
> > > > On 5/14/25 10:33, Hongbo Li wrote:
> > > > >
> > > > >
> > > > > On 2025/5/13 16:59, Chao Yu wrote:
> > > > > > On 4/24/25 01:08, Eric Sandeen wrote:
> > > > > > > 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.
> > > > > >
> > > > > > mkfs.f2fs -f -O extra_attr,flexible_inline_xattr /dev/vdb
> > > > > > mount -o inline_xattr_size=512 /dev/vdb /mnt/f2fs
> > > > > > mount: /mnt/f2fs: wrong fs type, bad option, bad superblock on /dev/vdb, missing codepage or helper program, or other error.
> > > > > > dmesg(1) may have more information after failed mount system call.
> > > > > > dmesg
> > > > > > [ 1758.202282] F2FS-fs (vdb): Image doesn't support compression
> > > > > > [ 1758.202286] F2FS-fs (vdb): inline_xattr_size option should be set with inline_xattr option
> > > > > >
> > > > > > Eric, Hongbo, can you please take a look at this issue?
> > > > > >
> > > > > Sorry, we only check the option hold in ctx, we should do the double check in sbi. Or other elegant approaches.
> > > > >
> > > > > For the "support compression", is it also the error in this testcase?
> > > >
> > > > I think so, I checked this w/ additional logs, and found this:
> > > >
> > > > #define F2FS_MOUNT_INLINE_XATTR_SIZE 0x00080000
> > > > #define F2FS_SPEC_compress_chksum (1 << 19) /* equal to 0x00080000)
> > > >
> > > > 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;
> > > > }
> > > >
> > > > We should use test_compression_spec(ctx->spec_mask) instead of
> > > > test_compression_spec(ctx->opt_mask) to check special mask of compression
> > > > option?
> > > >
> > >
> > > Yeah, you're right. test_compression_spec is used to check spec_mask, and we
> > > got it wrong.
> > >
> > > Thanks,
> > > Hongbo
> > >
> > > > Thanks,
> > > >
> > > > >
> > > > > Thanks,
> > > > > Hongbo
> > > > >
> > > > > > Thanks,
> > > > > >
> > > > > > >
> > > > > > > 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 37497fd80bb9..041bd6c482a0 100644
> > > > > > > --- a/fs/f2fs/super.c
> > > > > > > +++ b/fs/f2fs/super.c
> > > > > > > @@ -1141,47 +1141,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, ¶m);
> > > > > > > - kfree(param.string);
> > > > > > > - if (ret < 0)
> > > > > > > - return ret;
> > > > > > > - }
> > > > > > > - }
> > > > > > > - return 0;
> > > > > > > -}
> > > > > > > -
> > > > > > > /*
> > > > > > > * Check quota settings consistency.
> > > > > > > */
> > > > > > > @@ -2583,13 +2542,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;
> > > > > > > @@ -2635,7 +2593,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);
> > > > > > > @@ -2645,21 +2603,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) &&
> > > > > > > @@ -2678,20 +2626,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)) {
> > > > > > > @@ -2747,7 +2695,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;
> > > > > > > @@ -2758,7 +2706,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) {
> > > > > > > @@ -2772,7 +2720,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);
> > > > > > > @@ -2785,7 +2733,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;
> > > > > > > @@ -2827,7 +2775,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 {
> > > > > > > @@ -2854,7 +2802,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;
> > > > > > > @@ -3523,7 +3471,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,
> > > > > > > };
> > > > > > > @@ -4745,16 +4692,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;
> > > > > > > @@ -4767,9 +4711,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);
> > > > > > > @@ -4820,22 +4761,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);
> > > > > > > @@ -5160,7 +5091,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) {
> > > > > > > @@ -5255,7 +5185,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:
> > > > > > > @@ -5271,14 +5200,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)
> > > > > > > @@ -5322,10 +5276,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,
> > > > > > > };
> > > > > >
> > > >
> > > >
> >
> >
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 7/7] f2fs: switch to the new mount api
2025-05-16 17:35 ` Jaegeuk Kim
@ 2025-05-19 2:38 ` Hongbo Li
0 siblings, 0 replies; 58+ messages in thread
From: Hongbo Li @ 2025-05-19 2:38 UTC (permalink / raw)
To: Jaegeuk Kim; +Cc: Chao Yu, Eric Sandeen, linux-fsdevel, linux-f2fs-devel
On 2025/5/17 1:35, Jaegeuk Kim wrote:
> On 05/16, Hongbo Li wrote:
>>
>>
>> On 2025/5/14 23:30, Jaegeuk Kim wrote:
>>> Hi, Hongbo,
>>>
>>> It seems we're getting more issues in the patch set. May I ask for some
>>> help sending the new patch series having all the fixes that I made as well
>>> as addressing the concerns? You can get the patches from [1].
>>>
>>> [1] https://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git/log/?h=dev-test
>>>
>>
>> Hi, Jaegeuk
>>
>> I will discuss these issues with Eric. It may take some time, but not too
>> long. When we send the next version, should we resend this patch series
>> based on dev-test after modifying the code, only removing your S-O-B?
>> (You'll ultimately add your S-O-B back yourself)
>
> Hi Hongbo,
>
> Thank you for this hard work. Could you please resend the entire patch-set based
> on dev-test w/o my SOB? I'm going to dequeue the series from dev-test. Instead,
> I rebased the latest version onto [2].
>
> Please also consider another report, [1].
>
Ok, got it!
Thanks,
Hongbo
> [1] https://lore.kernel.org/linux-f2fs-devel/2f16981b-0e13-4c64-83a8-d0e0b4297348@suswa.mountain/T/#u
> [2] https://github.com/jaegeuk/f2fs/commits/mount/
>
>>
>> Thanks,
>> Hongbo
>>
>>> On 05/14, Hongbo Li wrote:
>>>>
>>>>
>>>> On 2025/5/14 12:03, Chao Yu wrote:
>>>>> On 5/14/25 10:33, Hongbo Li wrote:
>>>>>>
>>>>>>
>>>>>> On 2025/5/13 16:59, Chao Yu wrote:
>>>>>>> On 4/24/25 01:08, Eric Sandeen wrote:
>>>>>>>> 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.
>>>>>>>
>>>>>>> mkfs.f2fs -f -O extra_attr,flexible_inline_xattr /dev/vdb
>>>>>>> mount -o inline_xattr_size=512 /dev/vdb /mnt/f2fs
>>>>>>> mount: /mnt/f2fs: wrong fs type, bad option, bad superblock on /dev/vdb, missing codepage or helper program, or other error.
>>>>>>> dmesg(1) may have more information after failed mount system call.
>>>>>>> dmesg
>>>>>>> [ 1758.202282] F2FS-fs (vdb): Image doesn't support compression
>>>>>>> [ 1758.202286] F2FS-fs (vdb): inline_xattr_size option should be set with inline_xattr option
>>>>>>>
>>>>>>> Eric, Hongbo, can you please take a look at this issue?
>>>>>>>
>>>>>> Sorry, we only check the option hold in ctx, we should do the double check in sbi. Or other elegant approaches.
>>>>>>
>>>>>> For the "support compression", is it also the error in this testcase?
>>>>>
>>>>> I think so, I checked this w/ additional logs, and found this:
>>>>>
>>>>> #define F2FS_MOUNT_INLINE_XATTR_SIZE 0x00080000
>>>>> #define F2FS_SPEC_compress_chksum (1 << 19) /* equal to 0x00080000)
>>>>>
>>>>> 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;
>>>>> }
>>>>>
>>>>> We should use test_compression_spec(ctx->spec_mask) instead of
>>>>> test_compression_spec(ctx->opt_mask) to check special mask of compression
>>>>> option?
>>>>>
>>>>
>>>> Yeah, you're right. test_compression_spec is used to check spec_mask, and we
>>>> got it wrong.
>>>>
>>>> Thanks,
>>>> Hongbo
>>>>
>>>>> Thanks,
>>>>>
>>>>>>
>>>>>> Thanks,
>>>>>> Hongbo
>>>>>>
>>>>>>> Thanks,
>>>>>>>
>>>>>>>>
>>>>>>>> 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 37497fd80bb9..041bd6c482a0 100644
>>>>>>>> --- a/fs/f2fs/super.c
>>>>>>>> +++ b/fs/f2fs/super.c
>>>>>>>> @@ -1141,47 +1141,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, ¶m);
>>>>>>>> - kfree(param.string);
>>>>>>>> - if (ret < 0)
>>>>>>>> - return ret;
>>>>>>>> - }
>>>>>>>> - }
>>>>>>>> - return 0;
>>>>>>>> -}
>>>>>>>> -
>>>>>>>> /*
>>>>>>>> * Check quota settings consistency.
>>>>>>>> */
>>>>>>>> @@ -2583,13 +2542,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;
>>>>>>>> @@ -2635,7 +2593,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);
>>>>>>>> @@ -2645,21 +2603,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) &&
>>>>>>>> @@ -2678,20 +2626,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)) {
>>>>>>>> @@ -2747,7 +2695,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;
>>>>>>>> @@ -2758,7 +2706,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) {
>>>>>>>> @@ -2772,7 +2720,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);
>>>>>>>> @@ -2785,7 +2733,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;
>>>>>>>> @@ -2827,7 +2775,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 {
>>>>>>>> @@ -2854,7 +2802,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;
>>>>>>>> @@ -3523,7 +3471,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,
>>>>>>>> };
>>>>>>>> @@ -4745,16 +4692,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;
>>>>>>>> @@ -4767,9 +4711,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);
>>>>>>>> @@ -4820,22 +4761,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);
>>>>>>>> @@ -5160,7 +5091,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) {
>>>>>>>> @@ -5255,7 +5185,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:
>>>>>>>> @@ -5271,14 +5200,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)
>>>>>>>> @@ -5322,10 +5276,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,
>>>>>>>> };
>>>>>>>
>>>>>
>>>>>
>>>
>>>
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 7/7] f2fs: switch to the new mount api
2025-04-23 17:08 ` [PATCH V3 7/7] f2fs: switch to the new mount api Eric Sandeen
2025-05-08 9:19 ` Chao Yu
2025-05-13 8:59 ` Chao Yu
@ 2025-05-13 16:11 ` Jaegeuk Kim
2 siblings, 0 replies; 58+ messages in thread
From: Jaegeuk Kim @ 2025-05-13 16:11 UTC (permalink / raw)
To: Eric Sandeen; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 04/23, Eric Sandeen wrote:
> 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 37497fd80bb9..041bd6c482a0 100644
> --- a/fs/f2fs/super.c
> +++ b/fs/f2fs/super.c
> @@ -1141,47 +1141,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, ¶m);
> - kfree(param.string);
> - if (ret < 0)
> - return ret;
> - }
> - }
> - return 0;
> -}
> -
> /*
> * Check quota settings consistency.
> */
> @@ -2583,13 +2542,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;
> @@ -2635,7 +2593,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);
> @@ -2645,21 +2603,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) &&
> @@ -2678,20 +2626,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)) {
> @@ -2747,7 +2695,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;
> @@ -2758,7 +2706,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) {
> @@ -2772,7 +2720,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);
> @@ -2785,7 +2733,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;
> @@ -2827,7 +2775,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 {
> @@ -2854,7 +2802,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;
> @@ -3523,7 +3471,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,
> };
>
> @@ -4745,16 +4692,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;
> @@ -4767,9 +4711,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);
> @@ -4820,22 +4761,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);
> @@ -5160,7 +5091,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) {
> @@ -5255,7 +5185,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:
> @@ -5271,14 +5200,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);
> }
Applied the below to include "int i" in CONFIG_QUOTA.
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -540,6 +540,14 @@ static int f2fs_unnote_qf_name(struct fs_context *fc, int qtype)
ctx->qname_mask |= 1 << qtype;
return 0;
}
+
+static void f2fs_unnote_qf_name_all(struct fs_context *fc)
+{
+ int i;
+
+ for (i = 0; i < MAXQUOTAS; i++)
+ f2fs_unnote_qf_name(fc, i);
+}
#endif
static int f2fs_parse_test_dummy_encryption(const struct fs_parameter *param,
@@ -5286,14 +5294,12 @@ static int f2fs_reconfigure(struct fs_context *fc)
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]);
+ f2fs_unnote_qf_name_all(fc);
#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)
> @@ -5322,10 +5276,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.49.0
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 0/7] f2fs: new mount API conversion
2025-04-23 17:08 [PATCH V3 0/7] f2fs: new mount API conversion Eric Sandeen
` (6 preceding siblings ...)
2025-04-23 17:08 ` [PATCH V3 7/7] f2fs: switch to the new mount api Eric Sandeen
@ 2025-05-06 2:18 ` Eric Sandeen
2025-05-06 16:02 ` Jaegeuk Kim
2025-05-06 23:55 ` Ian Kent
2025-05-07 0:35 ` Jaegeuk Kim
9 siblings, 1 reply; 58+ messages in thread
From: Eric Sandeen @ 2025-05-06 2:18 UTC (permalink / raw)
To: linux-f2fs-devel; +Cc: linux-fsdevel, jaegeuk, chao, lihongbo22
Hi all - it would be nice to get some review or feedback on this;
seems that these patches tend to go stale fairly quickly as f2fs
evolves. :)
Thanks,
-Eric
On 4/23/25 12:08 PM, Eric Sandeen wrote:
> V3:
> - Rebase onto git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git
> dev branch
> - Fix up some 0day robot warnings
>
> 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] 58+ messages in thread
* Re: [PATCH V3 0/7] f2fs: new mount API conversion
2025-05-06 2:18 ` [PATCH V3 0/7] f2fs: new mount API conversion Eric Sandeen
@ 2025-05-06 16:02 ` Jaegeuk Kim
2025-05-06 22:53 ` Eric Sandeen
0 siblings, 1 reply; 58+ messages in thread
From: Jaegeuk Kim @ 2025-05-06 16:02 UTC (permalink / raw)
To: Eric Sandeen; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 05/05, Eric Sandeen wrote:
> Hi all - it would be nice to get some review or feedback on this;
> seems that these patches tend to go stale fairly quickly as f2fs
> evolves. :)
Thank you so much for the work! Let me queue this series into dev-test for
tests. If I find any issue, let me ping to the thread. So, you don't need
to worry about rebasing it. :)
Thanks,
>
> Thanks,
> -Eric
>
> On 4/23/25 12:08 PM, Eric Sandeen wrote:
> > V3:
> > - Rebase onto git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git
> > dev branch
> > - Fix up some 0day robot warnings
> >
> > 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] 58+ messages in thread
* Re: [PATCH V3 0/7] f2fs: new mount API conversion
2025-05-06 16:02 ` Jaegeuk Kim
@ 2025-05-06 22:53 ` Eric Sandeen
0 siblings, 0 replies; 58+ messages in thread
From: Eric Sandeen @ 2025-05-06 22:53 UTC (permalink / raw)
To: Jaegeuk Kim; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 5/6/25 11:02 AM, Jaegeuk Kim wrote:
> On 05/05, Eric Sandeen wrote:
>> Hi all - it would be nice to get some review or feedback on this;
>> seems that these patches tend to go stale fairly quickly as f2fs
>> evolves. :)
>
> Thank you so much for the work! Let me queue this series into dev-test for
> tests. If I find any issue, let me ping to the thread. So, you don't need
> to worry about rebasing it. :)
Thank you for queuing it, and Hongbo for the original series. Please reach
out if you encounter any problems.
-Eric
> Thanks,
>
>>
>> Thanks,
>> -Eric
>>
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 0/7] f2fs: new mount API conversion
2025-04-23 17:08 [PATCH V3 0/7] f2fs: new mount API conversion Eric Sandeen
` (7 preceding siblings ...)
2025-05-06 2:18 ` [PATCH V3 0/7] f2fs: new mount API conversion Eric Sandeen
@ 2025-05-06 23:55 ` Ian Kent
2025-05-07 0:35 ` Jaegeuk Kim
9 siblings, 0 replies; 58+ messages in thread
From: Ian Kent @ 2025-05-06 23:55 UTC (permalink / raw)
To: Eric Sandeen, linux-f2fs-devel; +Cc: linux-fsdevel, jaegeuk, chao, lihongbo22
On 24/4/25 01:08, Eric Sandeen wrote:
> V3:
> - Rebase onto git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git
> dev branch
> - Fix up some 0day robot warnings
>
> 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/
>
>
The option parsing for f2fs is quite complex so obviously we need Hongbo
to review
these as well (at least).
Apart from that all the mount API bits I expect to see are present and
Eric notes
the option parsing complexity ends up being unavoidable so it looks good
to me.
Reviewed-by: Ian Kent <raven@themaw.net>
Ian
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 0/7] f2fs: new mount API conversion
2025-04-23 17:08 [PATCH V3 0/7] f2fs: new mount API conversion Eric Sandeen
` (8 preceding siblings ...)
2025-05-06 23:55 ` Ian Kent
@ 2025-05-07 0:35 ` Jaegeuk Kim
2025-05-07 0:51 ` Eric Sandeen
9 siblings, 1 reply; 58+ messages in thread
From: Jaegeuk Kim @ 2025-05-07 0:35 UTC (permalink / raw)
To: Eric Sandeen; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
Hmm, I had to drop the series at the moment, since it seems needing more
work to deal with default_options(), which breaks my device setup.
For example, set_opt(sbi, READ_EXTENT_CACHE) in default_options is not propagating
to the below logics. In this case, do we need ctx_set_opt() if user doesn't set?
On 04/23, Eric Sandeen wrote:
> V3:
> - Rebase onto git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs.git
> dev branch
> - Fix up some 0day robot warnings
>
> 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] 58+ messages in thread
* Re: [PATCH V3 0/7] f2fs: new mount API conversion
2025-05-07 0:35 ` Jaegeuk Kim
@ 2025-05-07 0:51 ` Eric Sandeen
2025-05-07 1:23 ` Jaegeuk Kim
0 siblings, 1 reply; 58+ messages in thread
From: Eric Sandeen @ 2025-05-07 0:51 UTC (permalink / raw)
To: Jaegeuk Kim; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 5/6/25 7:35 PM, Jaegeuk Kim wrote:
> Hmm, I had to drop the series at the moment, since it seems needing more
> work to deal with default_options(), which breaks my device setup.
> For example, set_opt(sbi, READ_EXTENT_CACHE) in default_options is not propagating
> to the below logics. In this case, do we need ctx_set_opt() if user doesn't set?
Hm, can you describe the test or environment that fails for you?
(I'm afraid that I may not have all the right hardware to test everything,
so may have missed some cases)
However, from a quick test here, a loopback mount of an f2fs image file does
set extent_cache properly, so maybe I don't understand the problem:
# mount -o loop f2fsfile.img mnt
# mount | grep -o extent_cache
extent_cache
#
I'm happy to try to look into it though. Maybe you can put the patches
back on a temporary branch for me to pull and test?
Thanks,
- Eric
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 0/7] f2fs: new mount API conversion
2025-05-07 0:51 ` Eric Sandeen
@ 2025-05-07 1:23 ` Jaegeuk Kim
2025-05-07 2:56 ` Eric Sandeen
0 siblings, 1 reply; 58+ messages in thread
From: Jaegeuk Kim @ 2025-05-07 1:23 UTC (permalink / raw)
To: Eric Sandeen; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 05/06, Eric Sandeen wrote:
> On 5/6/25 7:35 PM, Jaegeuk Kim wrote:
> > Hmm, I had to drop the series at the moment, since it seems needing more
> > work to deal with default_options(), which breaks my device setup.
> > For example, set_opt(sbi, READ_EXTENT_CACHE) in default_options is not propagating
> > to the below logics. In this case, do we need ctx_set_opt() if user doesn't set?
>
> Hm, can you describe the test or environment that fails for you?
> (I'm afraid that I may not have all the right hardware to test everything,
> so may have missed some cases)
>
> However, from a quick test here, a loopback mount of an f2fs image file does
> set extent_cache properly, so maybe I don't understand the problem:
>
> # mount -o loop f2fsfile.img mnt
> # mount | grep -o extent_cache
> extent_cache
> #
>
> I'm happy to try to look into it though. Maybe you can put the patches
> back on a temporary branch for me to pull and test?
Thank you! I pushed here the last version.
https://github.com/jaegeuk/f2fs/commits/mount/
What about:
# mount -o loop,noextent_cache f2fsfile.img mnt
In this case, 1) ctx_clear_opt(), 2) set_opt() in default_options,
3) clear_opt since mask is set?
And, device_aliasing check is still failing, since it does not understand
test_opt(). Probably it's the only case?
>
> Thanks,
> - Eric
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 0/7] f2fs: new mount API conversion
2025-05-07 1:23 ` Jaegeuk Kim
@ 2025-05-07 2:56 ` Eric Sandeen
2025-05-07 3:45 ` Eric Sandeen
0 siblings, 1 reply; 58+ messages in thread
From: Eric Sandeen @ 2025-05-07 2:56 UTC (permalink / raw)
To: Jaegeuk Kim; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 5/6/25 8:23 PM, Jaegeuk Kim wrote:
> On 05/06, Eric Sandeen wrote:
>> On 5/6/25 7:35 PM, Jaegeuk Kim wrote:
>>> Hmm, I had to drop the series at the moment, since it seems needing more
>>> work to deal with default_options(), which breaks my device setup.
>>> For example, set_opt(sbi, READ_EXTENT_CACHE) in default_options is not propagating
>>> to the below logics. In this case, do we need ctx_set_opt() if user doesn't set?
>>
>> Hm, can you describe the test or environment that fails for you?
>> (I'm afraid that I may not have all the right hardware to test everything,
>> so may have missed some cases)
>>
>> However, from a quick test here, a loopback mount of an f2fs image file does
>> set extent_cache properly, so maybe I don't understand the problem:
>>
>> # mount -o loop f2fsfile.img mnt
>> # mount | grep -o extent_cache
>> extent_cache
>> #
>>
>> I'm happy to try to look into it though. Maybe you can put the patches
>> back on a temporary branch for me to pull and test?
>
> Thank you! I pushed here the last version.
>
> https://github.com/jaegeuk/f2fs/commits/mount/
>
> What about:
> # mount -o loop,noextent_cache f2fsfile.img mnt
>
> In this case, 1) ctx_clear_opt(), 2) set_opt() in default_options,
> 3) clear_opt since mask is set?
Not sure what I'm missing, it seems to work properly here but I haven't
pulled your (slightly) modified patches yet:
# mount -o loop,extent_cache f2fsfile.img mnt
# mount | grep -wo extent_cache
extent_cache
# umount mnt
# mount -o loop,noextent_cache f2fsfile.img mnt
# mount | grep -wo noextent_cache
noextent_cache
#
this looks right?
I'll check your tree tomorrow, though it doesn't sound like you made many
changes.
> And, device_aliasing check is still failing, since it does not understand
> test_opt(). Probably it's the only case?
I think you can blame me, not Hongbo on that one ;) - I will look into it.
-Eric
>>
>> Thanks,
>> - Eric
>>
>
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 0/7] f2fs: new mount API conversion
2025-05-07 2:56 ` Eric Sandeen
@ 2025-05-07 3:45 ` Eric Sandeen
2025-05-07 14:46 ` Jaegeuk Kim
0 siblings, 1 reply; 58+ messages in thread
From: Eric Sandeen @ 2025-05-07 3:45 UTC (permalink / raw)
To: Jaegeuk Kim; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 5/6/25 9:56 PM, Eric Sandeen wrote:
> On 5/6/25 8:23 PM, Jaegeuk Kim wrote:
...
>> What about:
>> # mount -o loop,noextent_cache f2fsfile.img mnt
>>
>> In this case, 1) ctx_clear_opt(), 2) set_opt() in default_options,
>> 3) clear_opt since mask is set?
>
> Not sure what I'm missing, it seems to work properly here but I haven't
> pulled your (slightly) modified patches yet:
>
> # mount -o loop,extent_cache f2fsfile.img mnt
> # mount | grep -wo extent_cache
> extent_cache
> # umount mnt
>
> # mount -o loop,noextent_cache f2fsfile.img mnt
> # mount | grep -wo noextent_cache
> noextent_cache
> #
>
> this looks right?
>
> I'll check your tree tomorrow, though it doesn't sound like you made many
> changes.
Hmm, I checked tonight and I see the same (correct?) behavior in your tree.
>> And, device_aliasing check is still failing, since it does not understand
>> test_opt(). Probably it's the only case?
Again, in your tree (I had to use a git version of f2fs-tools to make device
aliasing work - maybe time for a release?) ;)
# mkfs.ext4 /dev/vdc
# mkfs/mkfs.f2fs -c /dev/vdc@vdc.file /dev/vdb
# mount -o noextent_cache /dev/vdb mnt
# dmesg | tail -n 1
[ 581.924604] F2FS-fs (vdb): device aliasing requires extent cache
# mount -o extent_cache /dev/vdb mnt
# mount | grep -wo extent_cache
extent_cache
#
Maybe you can show me exactly what's not working for you?
-Eric
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 0/7] f2fs: new mount API conversion
2025-05-07 3:45 ` Eric Sandeen
@ 2025-05-07 14:46 ` Jaegeuk Kim
2025-05-07 17:11 ` Eric Sandeen
0 siblings, 1 reply; 58+ messages in thread
From: Jaegeuk Kim @ 2025-05-07 14:46 UTC (permalink / raw)
To: Eric Sandeen; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 05/06, Eric Sandeen wrote:
> On 5/6/25 9:56 PM, Eric Sandeen wrote:
> > On 5/6/25 8:23 PM, Jaegeuk Kim wrote:
>
> ...
>
> >> What about:
> >> # mount -o loop,noextent_cache f2fsfile.img mnt
> >>
> >> In this case, 1) ctx_clear_opt(), 2) set_opt() in default_options,
> >> 3) clear_opt since mask is set?
> >
> > Not sure what I'm missing, it seems to work properly here but I haven't
> > pulled your (slightly) modified patches yet:
> >
> > # mount -o loop,extent_cache f2fsfile.img mnt
> > # mount | grep -wo extent_cache
> > extent_cache
> > # umount mnt
> >
> > # mount -o loop,noextent_cache f2fsfile.img mnt
> > # mount | grep -wo noextent_cache
> > noextent_cache
> > #
> >
> > this looks right?
> >
> > I'll check your tree tomorrow, though it doesn't sound like you made many
> > changes.
>
> Hmm, I checked tonight and I see the same (correct?) behavior in your tree.
>
> >> And, device_aliasing check is still failing, since it does not understand
> >> test_opt(). Probably it's the only case?
>
> Again, in your tree (I had to use a git version of f2fs-tools to make device
> aliasing work - maybe time for a release?) ;)
>
> # mkfs.ext4 /dev/vdc
> # mkfs/mkfs.f2fs -c /dev/vdc@vdc.file /dev/vdb
> # mount -o noextent_cache /dev/vdb mnt
> # dmesg | tail -n 1
> [ 581.924604] F2FS-fs (vdb): device aliasing requires extent cache
> # mount -o extent_cache /dev/vdb mnt
> # mount | grep -wo extent_cache
> extent_cache
> #
I meant:
# mkfs/mkfs.f2fs -c /dev/vdc@vdc.file /dev/vdb
# mount /dev/vdb mnt
It's supposed to be successful, since extent_cache is enabled by default.
>
> Maybe you can show me exactly what's not working for you?
>
> -Eric
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 0/7] f2fs: new mount API conversion
2025-05-07 14:46 ` Jaegeuk Kim
@ 2025-05-07 17:11 ` Eric Sandeen
2025-05-07 19:48 ` Jaegeuk Kim
0 siblings, 1 reply; 58+ messages in thread
From: Eric Sandeen @ 2025-05-07 17:11 UTC (permalink / raw)
To: Jaegeuk Kim; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 5/7/25 9:46 AM, Jaegeuk Kim wrote:
> I meant:
>
> # mkfs/mkfs.f2fs -c /dev/vdc@vdc.file /dev/vdb
> # mount /dev/vdb mnt
>
> It's supposed to be successful, since extent_cache is enabled by default.
I'm sorry, clearly I was too sleepy last night. This fixes it for me.
We have to test the mask to see if the option was explisitly set (either
extent_cache or noextent_cache) at mount time.
If it was not specified at all, it will be set by the default f'n and
remain in the sbi, and it will pass this consistency check.
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index d89b9ede221e..e178796ce9a7 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -1412,7 +1414,8 @@ static int f2fs_check_opt_consistency(struct fs_context *fc,
}
if (f2fs_sb_has_device_alias(sbi) &&
- !ctx_test_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE)) {
+ (ctx->opt_mask & F2FS_MOUNT_READ_EXTENT_CACHE) &&
+ !ctx_test_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE)) {
f2fs_err(sbi, "device aliasing requires extent cache");
return -EINVAL;
}
^ permalink raw reply related [flat|nested] 58+ messages in thread
* Re: [PATCH V3 0/7] f2fs: new mount API conversion
2025-05-07 17:11 ` Eric Sandeen
@ 2025-05-07 19:48 ` Jaegeuk Kim
2025-05-07 20:19 ` Eric Sandeen
0 siblings, 1 reply; 58+ messages in thread
From: Jaegeuk Kim @ 2025-05-07 19:48 UTC (permalink / raw)
To: Eric Sandeen; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 05/07, Eric Sandeen wrote:
> On 5/7/25 9:46 AM, Jaegeuk Kim wrote:
>
> > I meant:
> >
> > # mkfs/mkfs.f2fs -c /dev/vdc@vdc.file /dev/vdb
> > # mount /dev/vdb mnt
> >
> > It's supposed to be successful, since extent_cache is enabled by default.
>
> I'm sorry, clearly I was too sleepy last night. This fixes it for me.
>
> We have to test the mask to see if the option was explisitly set (either
> extent_cache or noextent_cache) at mount time.
>
> If it was not specified at all, it will be set by the default f'n and
> remain in the sbi, and it will pass this consistency check.
>
> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> index d89b9ede221e..e178796ce9a7 100644
> --- a/fs/f2fs/super.c
> +++ b/fs/f2fs/super.c
> @@ -1412,7 +1414,8 @@ static int f2fs_check_opt_consistency(struct fs_context *fc,
> }
>
> if (f2fs_sb_has_device_alias(sbi) &&
> - !ctx_test_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE)) {
> + (ctx->opt_mask & F2FS_MOUNT_READ_EXTENT_CACHE) &&
> + !ctx_test_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE)) {
> f2fs_err(sbi, "device aliasing requires extent cache");
> return -EINVAL;
> }
I think that will cover the user-given options only, but we'd better check the
final options as well. Can we apply like this?
---
fs/f2fs/super.c | 50 ++++++++++++++++++++++++++++++++-----------------
1 file changed, 33 insertions(+), 17 deletions(-)
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index d89b9ede221e..270a9bf9773d 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -1412,6 +1412,7 @@ static int f2fs_check_opt_consistency(struct fs_context *fc,
}
if (f2fs_sb_has_device_alias(sbi) &&
+ (ctx->opt_mask & F2FS_MOUNT_READ_EXTENT_CACHE) &&
!ctx_test_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE)) {
f2fs_err(sbi, "device aliasing requires extent cache");
return -EINVAL;
@@ -1657,6 +1658,29 @@ static void f2fs_apply_options(struct fs_context *fc, struct super_block *sb)
f2fs_apply_quota_options(fc, sb);
}
+static int f2fs_sanity_check_options(struct f2fs_sb_info *sbi)
+{
+ if (f2fs_sb_has_device_alias(sbi) &&
+ !test_opt(sbi, READ_EXTENT_CACHE)) {
+ f2fs_err(sbi, "device aliasing requires extent cache");
+ return -EINVAL;
+ }
+#ifdef CONFIG_BLK_DEV_ZONED
+ if (f2fs_sb_has_blkzoned(sbi) &&
+ sbi->max_open_zones < F2FS_OPTION(sbi).active_logs) {
+ f2fs_err(sbi,
+ "zoned: max open zones %u is too small, need at least %u open zones",
+ sbi->max_open_zones, F2FS_OPTION(sbi).active_logs);
+ return -EINVAL;
+ }
+#endif
+ if (f2fs_lfs_mode(sbi) && !IS_F2FS_IPU_DISABLE(sbi)) {
+ f2fs_warn(sbi, "LFS is not compatible with IPU");
+ return -EINVAL;
+ }
+ return 0;
+}
+
static struct inode *f2fs_alloc_inode(struct super_block *sb)
{
struct f2fs_inode_info *fi;
@@ -2616,21 +2640,15 @@ static int __f2fs_remount(struct fs_context *fc, struct super_block *sb)
default_options(sbi, true);
err = f2fs_check_opt_consistency(fc, sb);
- if (err < 0)
+ if (err)
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) {
- f2fs_err(sbi,
- "zoned: max open zones %u is too small, need at least %u open zones",
- sbi->max_open_zones, F2FS_OPTION(sbi).active_logs);
- err = -EINVAL;
+ err = f2fs_sanity_check_options(sbi);
+ if (err)
goto restore_opts;
- }
-#endif
+
/* flush outstanding errors before changing fs state */
flush_work(&sbi->s_error_work);
@@ -2663,12 +2681,6 @@ static int __f2fs_remount(struct fs_context *fc, struct super_block *sb)
}
}
#endif
- if (f2fs_lfs_mode(sbi) && !IS_F2FS_IPU_DISABLE(sbi)) {
- err = -EINVAL;
- f2fs_warn(sbi, "LFS is not compatible with IPU");
- goto restore_opts;
- }
-
/* disallow enable atgc dynamically */
if (no_atgc == !!test_opt(sbi, ATGC)) {
err = -EINVAL;
@@ -4808,11 +4820,15 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc)
default_options(sbi, false);
err = f2fs_check_opt_consistency(fc, sb);
- if (err < 0)
+ if (err)
goto free_sb_buf;
f2fs_apply_options(fc, sb);
+ err = f2fs_sanity_check_options(sbi);
+ if (err)
+ goto free_options;
+
sb->s_maxbytes = max_file_blocks(NULL) <<
le32_to_cpu(raw_super->log_blocksize);
sb->s_max_links = F2FS_LINK_MAX;
--
2.49.0.1015.ga840276032-goog
^ permalink raw reply related [flat|nested] 58+ messages in thread
* Re: [PATCH V3 0/7] f2fs: new mount API conversion
2025-05-07 19:48 ` Jaegeuk Kim
@ 2025-05-07 20:19 ` Eric Sandeen
2025-05-07 20:28 ` Jaegeuk Kim
0 siblings, 1 reply; 58+ messages in thread
From: Eric Sandeen @ 2025-05-07 20:19 UTC (permalink / raw)
To: Jaegeuk Kim; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 5/7/25 2:48 PM, Jaegeuk Kim wrote:
> On 05/07, Eric Sandeen wrote:
>> On 5/7/25 9:46 AM, Jaegeuk Kim wrote:
>>
>>> I meant:
>>>
>>> # mkfs/mkfs.f2fs -c /dev/vdc@vdc.file /dev/vdb
>>> # mount /dev/vdb mnt
>>>
>>> It's supposed to be successful, since extent_cache is enabled by default.
>>
>> I'm sorry, clearly I was too sleepy last night. This fixes it for me.
>>
>> We have to test the mask to see if the option was explisitly set (either
>> extent_cache or noextent_cache) at mount time.
>>
>> If it was not specified at all, it will be set by the default f'n and
>> remain in the sbi, and it will pass this consistency check.
>>
>> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
>> index d89b9ede221e..e178796ce9a7 100644
>> --- a/fs/f2fs/super.c
>> +++ b/fs/f2fs/super.c
>> @@ -1412,7 +1414,8 @@ static int f2fs_check_opt_consistency(struct fs_context *fc,
>> }
>>
>> if (f2fs_sb_has_device_alias(sbi) &&
>> - !ctx_test_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE)) {
>> + (ctx->opt_mask & F2FS_MOUNT_READ_EXTENT_CACHE) &&
>> + !ctx_test_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE)) {
>> f2fs_err(sbi, "device aliasing requires extent cache");
>> return -EINVAL;
>> }
>
> I think that will cover the user-given options only, but we'd better check the
> final options as well. Can we apply like this?
I'm sorry, I'm not sure I understand what situation this additional
changes will cover...
It looks like this adds the f2fs_sanity_check_options() to the remount
path to explicitly (re-)check a few things.
But as far as I can tell, at least for the extent cache, remount is handled
properly already (with the hunk above):
# mkfs/mkfs.f2fs -c /dev/vdc@vdc.file /dev/vdb
# mount /dev/vdb mnt
# mount -o remount,noextent_cache mnt
mount: /root/mnt: mount point not mounted or bad option.
dmesg(1) may have more information after failed mount system call.
# dmesg | tail -n 1
[60012.364941] F2FS-fs (vdb): device aliasing requires extent cache
#
I haven't tested with i.e. blkzoned devices though, is there a testcase
that fails for you?
Thanks,
-Eric
> ---
> fs/f2fs/super.c | 50 ++++++++++++++++++++++++++++++++-----------------
> 1 file changed, 33 insertions(+), 17 deletions(-)
>
> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> index d89b9ede221e..270a9bf9773d 100644
> --- a/fs/f2fs/super.c
> +++ b/fs/f2fs/super.c
> @@ -1412,6 +1412,7 @@ static int f2fs_check_opt_consistency(struct fs_context *fc,
> }
>
> if (f2fs_sb_has_device_alias(sbi) &&
> + (ctx->opt_mask & F2FS_MOUNT_READ_EXTENT_CACHE) &&
> !ctx_test_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE)) {
> f2fs_err(sbi, "device aliasing requires extent cache");
> return -EINVAL;
> @@ -1657,6 +1658,29 @@ static void f2fs_apply_options(struct fs_context *fc, struct super_block *sb)
> f2fs_apply_quota_options(fc, sb);
> }
>
> +static int f2fs_sanity_check_options(struct f2fs_sb_info *sbi)
> +{
> + if (f2fs_sb_has_device_alias(sbi) &&
> + !test_opt(sbi, READ_EXTENT_CACHE)) {
> + f2fs_err(sbi, "device aliasing requires extent cache");
> + return -EINVAL;
> + }
> +#ifdef CONFIG_BLK_DEV_ZONED
> + if (f2fs_sb_has_blkzoned(sbi) &&
> + sbi->max_open_zones < F2FS_OPTION(sbi).active_logs) {
> + f2fs_err(sbi,
> + "zoned: max open zones %u is too small, need at least %u open zones",
> + sbi->max_open_zones, F2FS_OPTION(sbi).active_logs);
> + return -EINVAL;
> + }
> +#endif
> + if (f2fs_lfs_mode(sbi) && !IS_F2FS_IPU_DISABLE(sbi)) {
> + f2fs_warn(sbi, "LFS is not compatible with IPU");
> + return -EINVAL;
> + }
> + return 0;
> +}
> +
> static struct inode *f2fs_alloc_inode(struct super_block *sb)
> {
> struct f2fs_inode_info *fi;
> @@ -2616,21 +2640,15 @@ static int __f2fs_remount(struct fs_context *fc, struct super_block *sb)
> default_options(sbi, true);
>
> err = f2fs_check_opt_consistency(fc, sb);
> - if (err < 0)
> + if (err)
> 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) {
> - f2fs_err(sbi,
> - "zoned: max open zones %u is too small, need at least %u open zones",
> - sbi->max_open_zones, F2FS_OPTION(sbi).active_logs);
> - err = -EINVAL;
> + err = f2fs_sanity_check_options(sbi);
> + if (err)
> goto restore_opts;
> - }
> -#endif
> +
> /* flush outstanding errors before changing fs state */
> flush_work(&sbi->s_error_work);
>
> @@ -2663,12 +2681,6 @@ static int __f2fs_remount(struct fs_context *fc, struct super_block *sb)
> }
> }
> #endif
> - if (f2fs_lfs_mode(sbi) && !IS_F2FS_IPU_DISABLE(sbi)) {
> - err = -EINVAL;
> - f2fs_warn(sbi, "LFS is not compatible with IPU");
> - goto restore_opts;
> - }
> -
> /* disallow enable atgc dynamically */
> if (no_atgc == !!test_opt(sbi, ATGC)) {
> err = -EINVAL;
> @@ -4808,11 +4820,15 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc)
> default_options(sbi, false);
>
> err = f2fs_check_opt_consistency(fc, sb);
> - if (err < 0)
> + if (err)
> goto free_sb_buf;
>
> f2fs_apply_options(fc, sb);
>
> + err = f2fs_sanity_check_options(sbi);
> + if (err)
> + goto free_options;
> +
> sb->s_maxbytes = max_file_blocks(NULL) <<
> le32_to_cpu(raw_super->log_blocksize);
> sb->s_max_links = F2FS_LINK_MAX;
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 0/7] f2fs: new mount API conversion
2025-05-07 20:19 ` Eric Sandeen
@ 2025-05-07 20:28 ` Jaegeuk Kim
2025-05-07 20:46 ` Eric Sandeen
0 siblings, 1 reply; 58+ messages in thread
From: Jaegeuk Kim @ 2025-05-07 20:28 UTC (permalink / raw)
To: Eric Sandeen; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 05/07, Eric Sandeen wrote:
> On 5/7/25 2:48 PM, Jaegeuk Kim wrote:
> > On 05/07, Eric Sandeen wrote:
> >> On 5/7/25 9:46 AM, Jaegeuk Kim wrote:
> >>
> >>> I meant:
> >>>
> >>> # mkfs/mkfs.f2fs -c /dev/vdc@vdc.file /dev/vdb
> >>> # mount /dev/vdb mnt
> >>>
> >>> It's supposed to be successful, since extent_cache is enabled by default.
> >>
> >> I'm sorry, clearly I was too sleepy last night. This fixes it for me.
> >>
> >> We have to test the mask to see if the option was explisitly set (either
> >> extent_cache or noextent_cache) at mount time.
> >>
> >> If it was not specified at all, it will be set by the default f'n and
> >> remain in the sbi, and it will pass this consistency check.
> >>
> >> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> >> index d89b9ede221e..e178796ce9a7 100644
> >> --- a/fs/f2fs/super.c
> >> +++ b/fs/f2fs/super.c
> >> @@ -1412,7 +1414,8 @@ static int f2fs_check_opt_consistency(struct fs_context *fc,
> >> }
> >>
> >> if (f2fs_sb_has_device_alias(sbi) &&
> >> - !ctx_test_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE)) {
> >> + (ctx->opt_mask & F2FS_MOUNT_READ_EXTENT_CACHE) &&
> >> + !ctx_test_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE)) {
> >> f2fs_err(sbi, "device aliasing requires extent cache");
> >> return -EINVAL;
> >> }
> >
> > I think that will cover the user-given options only, but we'd better check the
> > final options as well. Can we apply like this?
>
> I'm sorry, I'm not sure I understand what situation this additional
> changes will cover...
>
> It looks like this adds the f2fs_sanity_check_options() to the remount
> path to explicitly (re-)check a few things.
>
> But as far as I can tell, at least for the extent cache, remount is handled
> properly already (with the hunk above):
>
> # mkfs/mkfs.f2fs -c /dev/vdc@vdc.file /dev/vdb
> # mount /dev/vdb mnt
> # mount -o remount,noextent_cache mnt
> mount: /root/mnt: mount point not mounted or bad option.
> dmesg(1) may have more information after failed mount system call.
> # dmesg | tail -n 1
> [60012.364941] F2FS-fs (vdb): device aliasing requires extent cache
> #
>
> I haven't tested with i.e. blkzoned devices though, is there a testcase
> that fails for you?
I'm worrying about any missing case to check options enabled by default_options.
For example, in the case of device_aliasing, we rely on enabling extent_cache
by default_options, which was not caught by f2fs_check_opt_consistency.
I was thinking that we'd need a post sanity check.
>
> Thanks,
> -Eric
>
> > ---
> > fs/f2fs/super.c | 50 ++++++++++++++++++++++++++++++++-----------------
> > 1 file changed, 33 insertions(+), 17 deletions(-)
> >
> > diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
> > index d89b9ede221e..270a9bf9773d 100644
> > --- a/fs/f2fs/super.c
> > +++ b/fs/f2fs/super.c
> > @@ -1412,6 +1412,7 @@ static int f2fs_check_opt_consistency(struct fs_context *fc,
> > }
> >
> > if (f2fs_sb_has_device_alias(sbi) &&
> > + (ctx->opt_mask & F2FS_MOUNT_READ_EXTENT_CACHE) &&
> > !ctx_test_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE)) {
> > f2fs_err(sbi, "device aliasing requires extent cache");
> > return -EINVAL;
> > @@ -1657,6 +1658,29 @@ static void f2fs_apply_options(struct fs_context *fc, struct super_block *sb)
> > f2fs_apply_quota_options(fc, sb);
> > }
> >
> > +static int f2fs_sanity_check_options(struct f2fs_sb_info *sbi)
> > +{
> > + if (f2fs_sb_has_device_alias(sbi) &&
> > + !test_opt(sbi, READ_EXTENT_CACHE)) {
> > + f2fs_err(sbi, "device aliasing requires extent cache");
> > + return -EINVAL;
> > + }
> > +#ifdef CONFIG_BLK_DEV_ZONED
> > + if (f2fs_sb_has_blkzoned(sbi) &&
> > + sbi->max_open_zones < F2FS_OPTION(sbi).active_logs) {
> > + f2fs_err(sbi,
> > + "zoned: max open zones %u is too small, need at least %u open zones",
> > + sbi->max_open_zones, F2FS_OPTION(sbi).active_logs);
> > + return -EINVAL;
> > + }
> > +#endif
> > + if (f2fs_lfs_mode(sbi) && !IS_F2FS_IPU_DISABLE(sbi)) {
> > + f2fs_warn(sbi, "LFS is not compatible with IPU");
> > + return -EINVAL;
> > + }
> > + return 0;
> > +}
> > +
> > static struct inode *f2fs_alloc_inode(struct super_block *sb)
> > {
> > struct f2fs_inode_info *fi;
> > @@ -2616,21 +2640,15 @@ static int __f2fs_remount(struct fs_context *fc, struct super_block *sb)
> > default_options(sbi, true);
> >
> > err = f2fs_check_opt_consistency(fc, sb);
> > - if (err < 0)
> > + if (err)
> > 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) {
> > - f2fs_err(sbi,
> > - "zoned: max open zones %u is too small, need at least %u open zones",
> > - sbi->max_open_zones, F2FS_OPTION(sbi).active_logs);
> > - err = -EINVAL;
> > + err = f2fs_sanity_check_options(sbi);
> > + if (err)
> > goto restore_opts;
> > - }
> > -#endif
> > +
> > /* flush outstanding errors before changing fs state */
> > flush_work(&sbi->s_error_work);
> >
> > @@ -2663,12 +2681,6 @@ static int __f2fs_remount(struct fs_context *fc, struct super_block *sb)
> > }
> > }
> > #endif
> > - if (f2fs_lfs_mode(sbi) && !IS_F2FS_IPU_DISABLE(sbi)) {
> > - err = -EINVAL;
> > - f2fs_warn(sbi, "LFS is not compatible with IPU");
> > - goto restore_opts;
> > - }
> > -
> > /* disallow enable atgc dynamically */
> > if (no_atgc == !!test_opt(sbi, ATGC)) {
> > err = -EINVAL;
> > @@ -4808,11 +4820,15 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc)
> > default_options(sbi, false);
> >
> > err = f2fs_check_opt_consistency(fc, sb);
> > - if (err < 0)
> > + if (err)
> > goto free_sb_buf;
> >
> > f2fs_apply_options(fc, sb);
> >
> > + err = f2fs_sanity_check_options(sbi);
> > + if (err)
> > + goto free_options;
> > +
> > sb->s_maxbytes = max_file_blocks(NULL) <<
> > le32_to_cpu(raw_super->log_blocksize);
> > sb->s_max_links = F2FS_LINK_MAX;
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 0/7] f2fs: new mount API conversion
2025-05-07 20:28 ` Jaegeuk Kim
@ 2025-05-07 20:46 ` Eric Sandeen
2025-05-07 21:36 ` Jaegeuk Kim
0 siblings, 1 reply; 58+ messages in thread
From: Eric Sandeen @ 2025-05-07 20:46 UTC (permalink / raw)
To: Jaegeuk Kim; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 5/7/25 3:28 PM, Jaegeuk Kim wrote:
>> But as far as I can tell, at least for the extent cache, remount is handled
>> properly already (with the hunk above):
>>
>> # mkfs/mkfs.f2fs -c /dev/vdc@vdc.file /dev/vdb
>> # mount /dev/vdb mnt
>> # mount -o remount,noextent_cache mnt
>> mount: /root/mnt: mount point not mounted or bad option.
>> dmesg(1) may have more information after failed mount system call.
>> # dmesg | tail -n 1
>> [60012.364941] F2FS-fs (vdb): device aliasing requires extent cache
>> #
>>
>> I haven't tested with i.e. blkzoned devices though, is there a testcase
>> that fails for you?
> I'm worrying about any missing case to check options enabled by default_options.
> For example, in the case of device_aliasing, we rely on enabling extent_cache
> by default_options, which was not caught by f2fs_check_opt_consistency.
>
> I was thinking that we'd need a post sanity check.
I see. If you want a "belt and suspenders" approach and it works for
you, no argument from me :)
-Eric
^ permalink raw reply [flat|nested] 58+ messages in thread
* Re: [PATCH V3 0/7] f2fs: new mount API conversion
2025-05-07 20:46 ` Eric Sandeen
@ 2025-05-07 21:36 ` Jaegeuk Kim
0 siblings, 0 replies; 58+ messages in thread
From: Jaegeuk Kim @ 2025-05-07 21:36 UTC (permalink / raw)
To: Eric Sandeen; +Cc: linux-f2fs-devel, linux-fsdevel, chao, lihongbo22
On 05/07, Eric Sandeen wrote:
> On 5/7/25 3:28 PM, Jaegeuk Kim wrote:
> >> But as far as I can tell, at least for the extent cache, remount is handled
> >> properly already (with the hunk above):
> >>
> >> # mkfs/mkfs.f2fs -c /dev/vdc@vdc.file /dev/vdb
> >> # mount /dev/vdb mnt
> >> # mount -o remount,noextent_cache mnt
> >> mount: /root/mnt: mount point not mounted or bad option.
> >> dmesg(1) may have more information after failed mount system call.
> >> # dmesg | tail -n 1
> >> [60012.364941] F2FS-fs (vdb): device aliasing requires extent cache
> >> #
> >>
> >> I haven't tested with i.e. blkzoned devices though, is there a testcase
> >> that fails for you?
> > I'm worrying about any missing case to check options enabled by default_options.
> > For example, in the case of device_aliasing, we rely on enabling extent_cache
> > by default_options, which was not caught by f2fs_check_opt_consistency.
> >
> > I was thinking that we'd need a post sanity check.
>
> I see. If you want a "belt and suspenders" approach and it works for
> you, no argument from me :)
Thanks. :)
I just found that I had to check it's from remount or not. And, this change does
not break my setup having a specific options. Let me queue the series back and
wait for further review from Chao.
---
fs/f2fs/super.c | 54 +++++++++++++++++++++++++++++++++----------------
1 file changed, 37 insertions(+), 17 deletions(-)
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
index d89b9ede221e..0ee783224953 100644
--- a/fs/f2fs/super.c
+++ b/fs/f2fs/super.c
@@ -1412,6 +1412,7 @@ static int f2fs_check_opt_consistency(struct fs_context *fc,
}
if (f2fs_sb_has_device_alias(sbi) &&
+ (ctx->opt_mask & F2FS_MOUNT_READ_EXTENT_CACHE) &&
!ctx_test_opt(ctx, F2FS_MOUNT_READ_EXTENT_CACHE)) {
f2fs_err(sbi, "device aliasing requires extent cache");
return -EINVAL;
@@ -1657,6 +1658,33 @@ static void f2fs_apply_options(struct fs_context *fc, struct super_block *sb)
f2fs_apply_quota_options(fc, sb);
}
+static int f2fs_sanity_check_options(struct f2fs_sb_info *sbi, bool remount)
+{
+ if (f2fs_sb_has_device_alias(sbi) &&
+ !test_opt(sbi, READ_EXTENT_CACHE)) {
+ f2fs_err(sbi, "device aliasing requires extent cache");
+ return -EINVAL;
+ }
+
+ if (!remount)
+ return 0;
+
+#ifdef CONFIG_BLK_DEV_ZONED
+ if (f2fs_sb_has_blkzoned(sbi) &&
+ sbi->max_open_zones < F2FS_OPTION(sbi).active_logs) {
+ f2fs_err(sbi,
+ "zoned: max open zones %u is too small, need at least %u open zones",
+ sbi->max_open_zones, F2FS_OPTION(sbi).active_logs);
+ return -EINVAL;
+ }
+#endif
+ if (f2fs_lfs_mode(sbi) && !IS_F2FS_IPU_DISABLE(sbi)) {
+ f2fs_warn(sbi, "LFS is not compatible with IPU");
+ return -EINVAL;
+ }
+ return 0;
+}
+
static struct inode *f2fs_alloc_inode(struct super_block *sb)
{
struct f2fs_inode_info *fi;
@@ -2616,21 +2644,15 @@ static int __f2fs_remount(struct fs_context *fc, struct super_block *sb)
default_options(sbi, true);
err = f2fs_check_opt_consistency(fc, sb);
- if (err < 0)
+ if (err)
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) {
- f2fs_err(sbi,
- "zoned: max open zones %u is too small, need at least %u open zones",
- sbi->max_open_zones, F2FS_OPTION(sbi).active_logs);
- err = -EINVAL;
+ err = f2fs_sanity_check_options(sbi, true);
+ if (err)
goto restore_opts;
- }
-#endif
+
/* flush outstanding errors before changing fs state */
flush_work(&sbi->s_error_work);
@@ -2663,12 +2685,6 @@ static int __f2fs_remount(struct fs_context *fc, struct super_block *sb)
}
}
#endif
- if (f2fs_lfs_mode(sbi) && !IS_F2FS_IPU_DISABLE(sbi)) {
- err = -EINVAL;
- f2fs_warn(sbi, "LFS is not compatible with IPU");
- goto restore_opts;
- }
-
/* disallow enable atgc dynamically */
if (no_atgc == !!test_opt(sbi, ATGC)) {
err = -EINVAL;
@@ -4808,11 +4824,15 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc)
default_options(sbi, false);
err = f2fs_check_opt_consistency(fc, sb);
- if (err < 0)
+ if (err)
goto free_sb_buf;
f2fs_apply_options(fc, sb);
+ err = f2fs_sanity_check_options(sbi, false);
+ if (err)
+ goto free_options;
+
sb->s_maxbytes = max_file_blocks(NULL) <<
le32_to_cpu(raw_super->log_blocksize);
sb->s_max_links = F2FS_LINK_MAX;
--
2.49.0.1015.ga840276032-goog
^ permalink raw reply related [flat|nested] 58+ messages in thread