On 7/8/25 10:56, Hongbo Li wrote:
> On 2025/7/7 20:17, Chao Yu wrote:
>> On 6/2/25 17:02, Hongbo Li wrote:G
>>> 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 <lihongb...@huawei.com>
>>> [sandeen: forward port, minor fixes and updates]
>>> Signed-off-by: Eric Sandeen <sand...@redhat.com>
>>> [hongbo: minor fixes]
>>> Signed-off-by: Hongbo Li <lihongb...@huawei.com>
>>> ---
>>>   fs/f2fs/super.c | 736 +++++++++++++++++++++++++++++++++++-------------
>>>   1 file changed, 543 insertions(+), 193 deletions(-)
>>>
>>> diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
>>> index 87db6eb9ef07..9835e7cd1071 100644
>>> --- a/fs/f2fs/super.c
>>> +++ b/fs/f2fs/super.c
>>> @@ -362,6 +362,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)
>>>   {
>>> @@ -535,51 +541,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,
>>> @@ -638,28 +599,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;
>>>               }
>>> @@ -751,7 +712,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];
>>> @@ -760,15 +720,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;
>>> -    bool 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;
>>> @@ -782,19 +739,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:
>>> @@ -814,6 +762,12 @@ static int handle_mount_opt(struct fs_context *fc, 
>>> struct fs_parameter *param)
>>>               ctx_set_opt(ctx, F2FS_MOUNT_INLINE_XATTR);
>>>           break;
>>>       case Opt_inline_xattr_size:
>>> +        if (result.int_32 < MIN_INLINE_XATTR_SIZE ||
>>> +            result.int_32 > MAX_INLINE_XATTR_SIZE) {
>>> +            f2fs_err(NULL, "inline xattr size is out of range: %lu ~ %lu",
>>> +                MIN_INLINE_XATTR_SIZE, MAX_INLINE_XATTR_SIZE);
>>> +            return -EINVAL;
>>> +        }
>>>           ctx_set_opt(ctx, F2FS_MOUNT_INLINE_XATTR_SIZE);
>>>           F2FS_CTX_INFO(ctx).inline_xattr_size = result.int_32;
>>>           ctx->spec_mask |= F2FS_SPEC_inline_xattr_size;
>>> @@ -875,27 +829,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;
>>> @@ -911,15 +856,13 @@ 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))
>>> -            return -EINVAL;
>>>           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))
>>> +        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;
>>> @@ -1060,10 +1003,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
>>> @@ -1107,10 +1046,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,
>>> @@ -1121,10 +1056,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;
>>> @@ -1145,10 +1076,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;
>>> @@ -1169,26 +1096,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
>>> @@ -1233,24 +1148,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;
>>> @@ -1274,7 +1180,7 @@ static int parse_options(struct f2fs_sb_info *sbi, 
>>> char *options, bool is_remoun
>>>               param.key = key;
>>>               param.size = v_len;
>>>   -            ret = handle_mount_opt(&fc, &param);
>>> +            ret = handle_mount_opt(fc, &param);
>>>               kfree(param.string);
>>>               if (ret < 0)
>>>                   return ret;
>>> @@ -1283,24 +1189,298 @@ 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->spec_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, "new noextensions conflicts with 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, "new noextensions conflicts with old extensions");
>>> +        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, "new extensions conflicts with old noextensions");
>>> +        return -EINVAL;
>>> +    }
>>> +#endif
>>> +    return 0;
>>> +}
>>> +
>>> +static int f2fs_check_opt_consistency(struct fs_context *fc,
>>> +                      struct super_block *sb)
>>> +{
>>> +    struct f2fs_fs_context *ctx = fc->fs_private;
>>> +    struct f2fs_sb_info *sbi = F2FS_SB(sb);
>>> +    int err;
>>> +
>>> +    if (ctx_test_opt(ctx, F2FS_MOUNT_NORECOVERY) && !f2fs_readonly(sb))
>>> +        return -EINVAL;
>>> +
>>> +    if (f2fs_hw_should_discard(sbi) &&
>>> +            (ctx->opt_mask & F2FS_MOUNT_DISCARD) &&
>>> +            !ctx_test_opt(ctx, F2FS_MOUNT_DISCARD)) {
>>> +        f2fs_warn(sbi, "discard is required for zoned block devices");
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    if (!f2fs_hw_support_discard(sbi) &&
>>> +            (ctx->opt_mask & F2FS_MOUNT_DISCARD) &&
>>> +            ctx_test_opt(ctx, F2FS_MOUNT_DISCARD)) {
>>> +        f2fs_warn(sbi, "device does not support discard");
>>> +        ctx_clear_opt(ctx, F2FS_MOUNT_DISCARD);
>>> +        ctx->opt_mask &= ~F2FS_MOUNT_DISCARD;
>>> +    }
>>> +
>>> +    if (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;
>>> +    }
>>> +
>>> +    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;
>>>       }
>>> @@ -1312,74 +1492,236 @@ 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");
>>> +            f2fs_err(sbi, "extra_attr or flexible_inline_xattr feature is 
>>> off");
>>>               return -EINVAL;
>>>           }
>>> -        if (!test_opt(sbi, INLINE_XATTR)) {
>>> -            f2fs_err(NULL, "inline_xattr_size option should be set with 
>>> inline_xattr option");
>>> -            return -EINVAL;
>>> -        }
>>> -
>>> -        min_size = MIN_INLINE_XATTR_SIZE;
>>> -        max_size = MAX_INLINE_XATTR_SIZE;
>>> -
>>> -        if (F2FS_OPTION(sbi).inline_xattr_size < min_size ||
>>> -                F2FS_OPTION(sbi).inline_xattr_size > max_size) {
>>> -            f2fs_err(NULL, "inline xattr size is out of range: %d ~ %d",
>>> -                 min_size, max_size);
>>> +        if (!ctx_test_opt(ctx, F2FS_MOUNT_INLINE_XATTR) && !test_opt(sbi, 
>>> 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;
>>> +}
>>> +
>>> +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) {
>>> +            qname = kstrdup(F2FS_CTX_INFO(ctx).s_qf_names[i],
>>> +                    GFP_KERNEL | __GFP_NOFAIL);
>>> +            set_opt(sbi, QUOTA);
>>> +        }
>>> +        F2FS_OPTION(sbi).s_qf_names[i] = qname;
>>> +    }
>>> +
>>> +    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;
>>> +    swap(F2FS_OPTION(sbi).dummy_enc_policy, 
>>> 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);
>>>   -    if (test_opt(sbi, NORECOVERY) && !f2fs_readonly(sbi->sb)) {
>>> -        f2fs_err(sbi, "norecovery requires readonly mount");
>>> +    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 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;
>>>   }
>>>   @@ -2290,6 +2632,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;
>>> @@ -2346,23 +2690,23 @@ 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;
>>>   -#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_check_opt_consistency(&fc, sb);
>>> +    if (err)
>>>           goto restore_opts;
>>> -    }
>>> -#endif
>>>   -    err = f2fs_validate_options(sbi);
>>> +    f2fs_apply_options(&fc, sb);
>>> +
>>> +    err = f2fs_sanity_check_options(sbi, true);
>>>       if (err)
>>>           goto restore_opts;
>>>   @@ -2398,12 +2742,6 @@ static int f2fs_remount(struct super_block *sb, 
>>> int *flags, char *data)
>>>           }
>>>       }
>>>   #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;
>>> @@ -4483,6 +4821,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;
>>> @@ -4499,6 +4839,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);
>>> @@ -4556,11 +4899,17 @@ 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);
>>> +    err = f2fs_check_opt_consistency(&fc, sb);
>>> +    if (err)
>>> +        goto free_options;
>>> +
>>> +    f2fs_apply_options(&fc, sb);
>>> +
>>> +    err = f2fs_sanity_check_options(sbi, false);
>>>       if (err)
>>>           goto free_options;
>>>   @@ -4981,7 +5330,8 @@ static int f2fs_fill_super(struct super_block *sb, 
>>> void *data, int silent)
>>>       for (i = 0; i < MAXQUOTAS; i++)
>>>           kfree(F2FS_OPTION(sbi).s_qf_names[i]);
>>>   #endif
>>> -    fscrypt_free_dummy_policy(&F2FS_OPTION(sbi).dummy_enc_policy);
>>> +    /* no need to free dummy_enc_policy, we just keep it in ctx when 
>>> failed */
>>> +    swap(F2FS_CTX_INFO(&ctx).dummy_enc_policy, 
>>> F2FS_OPTION(sbi).dummy_enc_policy);
>>
>> I can see ext4 will free dummy_enc_policy in error path of fill_super, is
>> there any difference?
>>
> 
> We can not free dummy_enc_policy in f2fs, due to the try_onemore logical. So 
> here we rollback the assign and keep the dummy_enc_policy in ctx, and free it 
> only when ctx is no longer used.

Got it, thanks for your explanation.

Thanks,

> 
> Thanks,
> Hongbo
> 
>> Thanks,
>>
>>>       kvfree(options);
>>>   free_sb_buf:
>>>       kfree(raw_super);
>>
>>



_______________________________________________
Linux-f2fs-devel mailing list
Linux-f2fs-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/linux-f2fs-devel

Reply via email to