]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
Merge tag 'unicode-for-next-5.17' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 17 Jan 2022 03:40:02 +0000 (05:40 +0200)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 17 Jan 2022 03:40:02 +0000 (05:40 +0200)
Pull unicode updates from Gabriel Krisman Bertazi:
 "This includes patches from Christoph Hellwig to split the large data
  tables of the unicode subsystem into a loadable module, which allow
  users to not have them around if case-insensitive filesystems are not
  to be used. It also includes minor code fixes to unicode and its
  users, from the same author.

  All the patches here have been on linux-next releases for the past
  months"

* tag 'unicode-for-next-5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/krisman/unicode:
  unicode: only export internal symbols for the selftests
  unicode: Add utf8-data module
  unicode: cache the normalization tables in struct unicode_map
  unicode: move utf8cursor to utf8-selftest.c
  unicode: simplify utf8len
  unicode: remove the unused utf8{,n}age{min,max} functions
  unicode: pass a UNICODE_AGE() tripple to utf8_load
  unicode: mark the version field in struct unicode_map unsigned
  unicode: remove the charset field from struct unicode_map
  f2fs: simplify f2fs_sb_read_encoding
  ext4: simplify ext4_sb_read_encoding

1  2 
fs/ext4/super.c
fs/f2fs/super.c
fs/f2fs/sysfs.c

diff --combined fs/ext4/super.c
index 0343f682504d9cd74786ad29ccce34987a88a427,85e73093c61aa2cd3e3f9546633c9aa7e8d2bed6..db9fe48435293d40f5e1c9997ed126526a32e41e
@@@ -46,9 -46,6 +46,9 @@@
  #include <linux/part_stat.h>
  #include <linux/kthread.h>
  #include <linux/freezer.h>
 +#include <linux/fsnotify.h>
 +#include <linux/fs_context.h>
 +#include <linux/fs_parser.h>
  
  #include "ext4.h"
  #include "ext4_extents.h"     /* Needed for trace points definition */
@@@ -75,9 -72,12 +75,9 @@@ static int ext4_mark_recovery_complete(
  static int ext4_clear_journal_err(struct super_block *sb,
                                  struct ext4_super_block *es);
  static int ext4_sync_fs(struct super_block *sb, int wait);
 -static int ext4_remount(struct super_block *sb, int *flags, char *data);
  static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf);
  static int ext4_unfreeze(struct super_block *sb);
  static int ext4_freeze(struct super_block *sb);
 -static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags,
 -                     const char *dev_name, void *data);
  static inline int ext2_feature_set_ok(struct super_block *sb);
  static inline int ext3_feature_set_ok(struct super_block *sb);
  static void ext4_destroy_lazyinit_thread(void);
@@@ -85,16 -85,6 +85,16 @@@ static void ext4_unregister_li_request(
  static void ext4_clear_request_list(void);
  static struct inode *ext4_get_journal_inode(struct super_block *sb,
                                            unsigned int journal_inum);
 +static int ext4_validate_options(struct fs_context *fc);
 +static int ext4_check_opt_consistency(struct fs_context *fc,
 +                                    struct super_block *sb);
 +static int ext4_apply_options(struct fs_context *fc, struct super_block *sb);
 +static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param);
 +static int ext4_get_tree(struct fs_context *fc);
 +static int ext4_reconfigure(struct fs_context *fc);
 +static void ext4_fc_free(struct fs_context *fc);
 +static int ext4_init_fs_context(struct fs_context *fc);
 +static const struct fs_parameter_spec ext4_param_specs[];
  
  /*
   * Lock ordering
   * transaction start -> page lock(s) -> i_data_sem (rw)
   */
  
 +static const struct fs_context_operations ext4_context_ops = {
 +      .parse_param    = ext4_parse_param,
 +      .get_tree       = ext4_get_tree,
 +      .reconfigure    = ext4_reconfigure,
 +      .free           = ext4_fc_free,
 +};
 +
 +
  #if !defined(CONFIG_EXT2_FS) && !defined(CONFIG_EXT2_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT2)
  static struct file_system_type ext2_fs_type = {
 -      .owner          = THIS_MODULE,
 -      .name           = "ext2",
 -      .mount          = ext4_mount,
 -      .kill_sb        = kill_block_super,
 -      .fs_flags       = FS_REQUIRES_DEV,
 +      .owner                  = THIS_MODULE,
 +      .name                   = "ext2",
 +      .init_fs_context        = ext4_init_fs_context,
 +      .parameters             = ext4_param_specs,
 +      .kill_sb                = kill_block_super,
 +      .fs_flags               = FS_REQUIRES_DEV,
  };
  MODULE_ALIAS_FS("ext2");
  MODULE_ALIAS("ext2");
  
  
  static struct file_system_type ext3_fs_type = {
 -      .owner          = THIS_MODULE,
 -      .name           = "ext3",
 -      .mount          = ext4_mount,
 -      .kill_sb        = kill_block_super,
 -      .fs_flags       = FS_REQUIRES_DEV,
 +      .owner                  = THIS_MODULE,
 +      .name                   = "ext3",
 +      .init_fs_context        = ext4_init_fs_context,
 +      .parameters             = ext4_param_specs,
 +      .kill_sb                = kill_block_super,
 +      .fs_flags               = FS_REQUIRES_DEV,
  };
  MODULE_ALIAS_FS("ext3");
  MODULE_ALIAS("ext3");
@@@ -279,8 -259,8 +279,8 @@@ static int ext4_verify_csum_type(struc
        return es->s_checksum_type == EXT4_CRC32C_CHKSUM;
  }
  
 -static __le32 ext4_superblock_csum(struct super_block *sb,
 -                                 struct ext4_super_block *es)
 +__le32 ext4_superblock_csum(struct super_block *sb,
 +                          struct ext4_super_block *es)
  {
        struct ext4_sb_info *sbi = EXT4_SB(sb);
        int offset = offsetof(struct ext4_super_block, s_checksum);
@@@ -779,8 -759,6 +779,8 @@@ void __ext4_error(struct super_block *s
                       sb->s_id, function, line, current->comm, &vaf);
                va_end(args);
        }
 +      fsnotify_sb_error(sb, NULL, error ? error : EFSCORRUPTED);
 +
        ext4_handle_error(sb, force_ro, error, 0, block, function, line);
  }
  
@@@ -811,8 -789,6 +811,8 @@@ void __ext4_error_inode(struct inode *i
                               current->comm, &vaf);
                va_end(args);
        }
 +      fsnotify_sb_error(inode->i_sb, inode, error ? error : EFSCORRUPTED);
 +
        ext4_handle_error(inode->i_sb, false, error, inode->i_ino, block,
                          function, line);
  }
@@@ -851,8 -827,6 +851,8 @@@ void __ext4_error_file(struct file *fil
                               current->comm, path, &vaf);
                va_end(args);
        }
 +      fsnotify_sb_error(inode->i_sb, inode, EFSCORRUPTED);
 +
        ext4_handle_error(inode->i_sb, false, EFSCORRUPTED, inode->i_ino, block,
                          function, line);
  }
@@@ -920,7 -894,6 +920,7 @@@ void __ext4_std_error(struct super_bloc
                printk(KERN_CRIT "EXT4-fs error (device %s) in %s:%d: %s\n",
                       sb->s_id, function, line, errstr);
        }
 +      fsnotify_sb_error(sb, NULL, errno ? errno : EFSCORRUPTED);
  
        ext4_handle_error(sb, false, -errno, 0, 0, function, line);
  }
@@@ -931,20 -904,14 +931,20 @@@ void __ext4_msg(struct super_block *sb
        struct va_format vaf;
        va_list args;
  
 -      atomic_inc(&EXT4_SB(sb)->s_msg_count);
 -      if (!___ratelimit(&(EXT4_SB(sb)->s_msg_ratelimit_state), "EXT4-fs"))
 -              return;
 +      if (sb) {
 +              atomic_inc(&EXT4_SB(sb)->s_msg_count);
 +              if (!___ratelimit(&(EXT4_SB(sb)->s_msg_ratelimit_state),
 +                                "EXT4-fs"))
 +                      return;
 +      }
  
        va_start(args, fmt);
        vaf.fmt = fmt;
        vaf.va = &args;
 -      printk("%sEXT4-fs (%s): %pV\n", prefix, sb->s_id, &vaf);
 +      if (sb)
 +              printk("%sEXT4-fs (%s): %pV\n", prefix, sb->s_id, &vaf);
 +      else
 +              printk("%sEXT4-fs: %pV\n", prefix, &vaf);
        va_end(args);
  }
  
@@@ -1605,6 -1572,7 +1605,6 @@@ static const struct fscrypt_operations 
        .set_context            = ext4_set_context,
        .get_dummy_policy       = ext4_get_dummy_policy,
        .empty_dir              = ext4_empty_dir,
 -      .max_namelen            = EXT4_NAME_LEN,
        .has_stable_inodes      = ext4_has_stable_inodes,
        .get_ino_and_lblk_bits  = ext4_get_ino_and_lblk_bits,
  };
@@@ -1672,6 -1640,7 +1672,6 @@@ static const struct super_operations ex
        .freeze_fs      = ext4_freeze,
        .unfreeze_fs    = ext4_unfreeze,
        .statfs         = ext4_statfs,
 -      .remount_fs     = ext4_remount,
        .show_options   = ext4_show_options,
  #ifdef CONFIG_QUOTA
        .quota_read     = ext4_quota_read,
@@@ -1689,7 -1658,7 +1689,7 @@@ static const struct export_operations e
  
  enum {
        Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid,
 -      Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro,
 +      Opt_resgid, Opt_resuid, Opt_sb,
        Opt_nouid32, Opt_debug, Opt_removed,
        Opt_user_xattr, Opt_nouser_xattr, Opt_acl, Opt_noacl,
        Opt_auto_da_alloc, Opt_noauto_da_alloc, Opt_noload,
        Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback,
        Opt_data_err_abort, Opt_data_err_ignore, Opt_test_dummy_encryption,
        Opt_inlinecrypt,
 -      Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota,
 -      Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_quota,
 +      Opt_usrjquota, Opt_grpjquota, Opt_quota,
        Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err,
        Opt_usrquota, Opt_grpquota, Opt_prjquota, Opt_i_version,
        Opt_dax, Opt_dax_always, Opt_dax_inode, Opt_dax_never,
        Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_warn_on_error,
 -      Opt_nowarn_on_error, Opt_mblk_io_submit,
 -      Opt_lazytime, Opt_nolazytime, Opt_debug_want_extra_isize,
 +      Opt_nowarn_on_error, Opt_mblk_io_submit, Opt_debug_want_extra_isize,
        Opt_nomblk_io_submit, Opt_block_validity, Opt_noblock_validity,
        Opt_inode_readahead_blks, Opt_journal_ioprio,
        Opt_dioread_nolock, Opt_dioread_lock,
        Opt_discard, Opt_nodiscard, Opt_init_itable, Opt_noinit_itable,
        Opt_max_dir_size_kb, Opt_nojournal_checksum, Opt_nombcache,
        Opt_no_prefetch_block_bitmaps, Opt_mb_optimize_scan,
 +      Opt_errors, Opt_data, Opt_data_err, Opt_jqfmt, Opt_dax_type,
  #ifdef CONFIG_EXT4_DEBUG
        Opt_fc_debug_max_replay, Opt_fc_debug_force
  #endif
  };
  
 -static const match_table_t tokens = {
 -      {Opt_bsd_df, "bsddf"},
 -      {Opt_minix_df, "minixdf"},
 -      {Opt_grpid, "grpid"},
 -      {Opt_grpid, "bsdgroups"},
 -      {Opt_nogrpid, "nogrpid"},
 -      {Opt_nogrpid, "sysvgroups"},
 -      {Opt_resgid, "resgid=%u"},
 -      {Opt_resuid, "resuid=%u"},
 -      {Opt_sb, "sb=%u"},
 -      {Opt_err_cont, "errors=continue"},
 -      {Opt_err_panic, "errors=panic"},
 -      {Opt_err_ro, "errors=remount-ro"},
 -      {Opt_nouid32, "nouid32"},
 -      {Opt_debug, "debug"},
 -      {Opt_removed, "oldalloc"},
 -      {Opt_removed, "orlov"},
 -      {Opt_user_xattr, "user_xattr"},
 -      {Opt_nouser_xattr, "nouser_xattr"},
 -      {Opt_acl, "acl"},
 -      {Opt_noacl, "noacl"},
 -      {Opt_noload, "norecovery"},
 -      {Opt_noload, "noload"},
 -      {Opt_removed, "nobh"},
 -      {Opt_removed, "bh"},
 -      {Opt_commit, "commit=%u"},
 -      {Opt_min_batch_time, "min_batch_time=%u"},
 -      {Opt_max_batch_time, "max_batch_time=%u"},
 -      {Opt_journal_dev, "journal_dev=%u"},
 -      {Opt_journal_path, "journal_path=%s"},
 -      {Opt_journal_checksum, "journal_checksum"},
 -      {Opt_nojournal_checksum, "nojournal_checksum"},
 -      {Opt_journal_async_commit, "journal_async_commit"},
 -      {Opt_abort, "abort"},
 -      {Opt_data_journal, "data=journal"},
 -      {Opt_data_ordered, "data=ordered"},
 -      {Opt_data_writeback, "data=writeback"},
 -      {Opt_data_err_abort, "data_err=abort"},
 -      {Opt_data_err_ignore, "data_err=ignore"},
 -      {Opt_offusrjquota, "usrjquota="},
 -      {Opt_usrjquota, "usrjquota=%s"},
 -      {Opt_offgrpjquota, "grpjquota="},
 -      {Opt_grpjquota, "grpjquota=%s"},
 -      {Opt_jqfmt_vfsold, "jqfmt=vfsold"},
 -      {Opt_jqfmt_vfsv0, "jqfmt=vfsv0"},
 -      {Opt_jqfmt_vfsv1, "jqfmt=vfsv1"},
 -      {Opt_grpquota, "grpquota"},
 -      {Opt_noquota, "noquota"},
 -      {Opt_quota, "quota"},
 -      {Opt_usrquota, "usrquota"},
 -      {Opt_prjquota, "prjquota"},
 -      {Opt_barrier, "barrier=%u"},
 -      {Opt_barrier, "barrier"},
 -      {Opt_nobarrier, "nobarrier"},
 -      {Opt_i_version, "i_version"},
 -      {Opt_dax, "dax"},
 -      {Opt_dax_always, "dax=always"},
 -      {Opt_dax_inode, "dax=inode"},
 -      {Opt_dax_never, "dax=never"},
 -      {Opt_stripe, "stripe=%u"},
 -      {Opt_delalloc, "delalloc"},
 -      {Opt_warn_on_error, "warn_on_error"},
 -      {Opt_nowarn_on_error, "nowarn_on_error"},
 -      {Opt_lazytime, "lazytime"},
 -      {Opt_nolazytime, "nolazytime"},
 -      {Opt_debug_want_extra_isize, "debug_want_extra_isize=%u"},
 -      {Opt_nodelalloc, "nodelalloc"},
 -      {Opt_removed, "mblk_io_submit"},
 -      {Opt_removed, "nomblk_io_submit"},
 -      {Opt_block_validity, "block_validity"},
 -      {Opt_noblock_validity, "noblock_validity"},
 -      {Opt_inode_readahead_blks, "inode_readahead_blks=%u"},
 -      {Opt_journal_ioprio, "journal_ioprio=%u"},
 -      {Opt_auto_da_alloc, "auto_da_alloc=%u"},
 -      {Opt_auto_da_alloc, "auto_da_alloc"},
 -      {Opt_noauto_da_alloc, "noauto_da_alloc"},
 -      {Opt_dioread_nolock, "dioread_nolock"},
 -      {Opt_dioread_lock, "nodioread_nolock"},
 -      {Opt_dioread_lock, "dioread_lock"},
 -      {Opt_discard, "discard"},
 -      {Opt_nodiscard, "nodiscard"},
 -      {Opt_init_itable, "init_itable=%u"},
 -      {Opt_init_itable, "init_itable"},
 -      {Opt_noinit_itable, "noinit_itable"},
 -#ifdef CONFIG_EXT4_DEBUG
 -      {Opt_fc_debug_force, "fc_debug_force"},
 -      {Opt_fc_debug_max_replay, "fc_debug_max_replay=%u"},
 -#endif
 -      {Opt_max_dir_size_kb, "max_dir_size_kb=%u"},
 -      {Opt_test_dummy_encryption, "test_dummy_encryption=%s"},
 -      {Opt_test_dummy_encryption, "test_dummy_encryption"},
 -      {Opt_inlinecrypt, "inlinecrypt"},
 -      {Opt_nombcache, "nombcache"},
 -      {Opt_nombcache, "no_mbcache"},  /* for backward compatibility */
 -      {Opt_removed, "prefetch_block_bitmaps"},
 -      {Opt_no_prefetch_block_bitmaps, "no_prefetch_block_bitmaps"},
 -      {Opt_mb_optimize_scan, "mb_optimize_scan=%d"},
 -      {Opt_removed, "check=none"},    /* mount option from ext2/3 */
 -      {Opt_removed, "nocheck"},       /* mount option from ext2/3 */
 -      {Opt_removed, "reservation"},   /* mount option from ext2/3 */
 -      {Opt_removed, "noreservation"}, /* mount option from ext2/3 */
 -      {Opt_removed, "journal=%u"},    /* mount option from ext2/3 */
 -      {Opt_err, NULL},
 +static const struct constant_table ext4_param_errors[] = {
 +      {"continue",    EXT4_MOUNT_ERRORS_CONT},
 +      {"panic",       EXT4_MOUNT_ERRORS_PANIC},
 +      {"remount-ro",  EXT4_MOUNT_ERRORS_RO},
 +      {}
  };
  
 -static ext4_fsblk_t get_sb_block(void **data)
 -{
 -      ext4_fsblk_t    sb_block;
 -      char            *options = (char *) *data;
 +static const struct constant_table ext4_param_data[] = {
 +      {"journal",     EXT4_MOUNT_JOURNAL_DATA},
 +      {"ordered",     EXT4_MOUNT_ORDERED_DATA},
 +      {"writeback",   EXT4_MOUNT_WRITEBACK_DATA},
 +      {}
 +};
  
 -      if (!options || strncmp(options, "sb=", 3) != 0)
 -              return 1;       /* Default location */
 +static const struct constant_table ext4_param_data_err[] = {
 +      {"abort",       Opt_data_err_abort},
 +      {"ignore",      Opt_data_err_ignore},
 +      {}
 +};
  
 -      options += 3;
 -      /* TODO: use simple_strtoll with >32bit ext4 */
 -      sb_block = simple_strtoul(options, &options, 0);
 -      if (*options && *options != ',') {
 -              printk(KERN_ERR "EXT4-fs: Invalid sb specification: %s\n",
 -                     (char *) *data);
 -              return 1;
 -      }
 -      if (*options == ',')
 -              options++;
 -      *data = (void *) options;
 +static const struct constant_table ext4_param_jqfmt[] = {
 +      {"vfsold",      QFMT_VFS_OLD},
 +      {"vfsv0",       QFMT_VFS_V0},
 +      {"vfsv1",       QFMT_VFS_V1},
 +      {}
 +};
  
 -      return sb_block;
 -}
 +static const struct constant_table ext4_param_dax[] = {
 +      {"always",      Opt_dax_always},
 +      {"inode",       Opt_dax_inode},
 +      {"never",       Opt_dax_never},
 +      {}
 +};
 +
 +/* String parameter that allows empty argument */
 +#define fsparam_string_empty(NAME, OPT) \
 +      __fsparam(fs_param_is_string, NAME, OPT, fs_param_can_be_empty, NULL)
 +
 +/*
 + * Mount option specification
 + * We don't use fsparam_flag_no because of the way we set the
 + * options and the way we show them in _ext4_show_options(). To
 + * keep the changes to a minimum, let's keep the negative options
 + * separate for now.
 + */
 +static const struct fs_parameter_spec ext4_param_specs[] = {
 +      fsparam_flag    ("bsddf",               Opt_bsd_df),
 +      fsparam_flag    ("minixdf",             Opt_minix_df),
 +      fsparam_flag    ("grpid",               Opt_grpid),
 +      fsparam_flag    ("bsdgroups",           Opt_grpid),
 +      fsparam_flag    ("nogrpid",             Opt_nogrpid),
 +      fsparam_flag    ("sysvgroups",          Opt_nogrpid),
 +      fsparam_u32     ("resgid",              Opt_resgid),
 +      fsparam_u32     ("resuid",              Opt_resuid),
 +      fsparam_u32     ("sb",                  Opt_sb),
 +      fsparam_enum    ("errors",              Opt_errors, ext4_param_errors),
 +      fsparam_flag    ("nouid32",             Opt_nouid32),
 +      fsparam_flag    ("debug",               Opt_debug),
 +      fsparam_flag    ("oldalloc",            Opt_removed),
 +      fsparam_flag    ("orlov",               Opt_removed),
 +      fsparam_flag    ("user_xattr",          Opt_user_xattr),
 +      fsparam_flag    ("nouser_xattr",        Opt_nouser_xattr),
 +      fsparam_flag    ("acl",                 Opt_acl),
 +      fsparam_flag    ("noacl",               Opt_noacl),
 +      fsparam_flag    ("norecovery",          Opt_noload),
 +      fsparam_flag    ("noload",              Opt_noload),
 +      fsparam_flag    ("bh",                  Opt_removed),
 +      fsparam_flag    ("nobh",                Opt_removed),
 +      fsparam_u32     ("commit",              Opt_commit),
 +      fsparam_u32     ("min_batch_time",      Opt_min_batch_time),
 +      fsparam_u32     ("max_batch_time",      Opt_max_batch_time),
 +      fsparam_u32     ("journal_dev",         Opt_journal_dev),
 +      fsparam_bdev    ("journal_path",        Opt_journal_path),
 +      fsparam_flag    ("journal_checksum",    Opt_journal_checksum),
 +      fsparam_flag    ("nojournal_checksum",  Opt_nojournal_checksum),
 +      fsparam_flag    ("journal_async_commit",Opt_journal_async_commit),
 +      fsparam_flag    ("abort",               Opt_abort),
 +      fsparam_enum    ("data",                Opt_data, ext4_param_data),
 +      fsparam_enum    ("data_err",            Opt_data_err,
 +                                              ext4_param_data_err),
 +      fsparam_string_empty
 +                      ("usrjquota",           Opt_usrjquota),
 +      fsparam_string_empty
 +                      ("grpjquota",           Opt_grpjquota),
 +      fsparam_enum    ("jqfmt",               Opt_jqfmt, ext4_param_jqfmt),
 +      fsparam_flag    ("grpquota",            Opt_grpquota),
 +      fsparam_flag    ("quota",               Opt_quota),
 +      fsparam_flag    ("noquota",             Opt_noquota),
 +      fsparam_flag    ("usrquota",            Opt_usrquota),
 +      fsparam_flag    ("prjquota",            Opt_prjquota),
 +      fsparam_flag    ("barrier",             Opt_barrier),
 +      fsparam_u32     ("barrier",             Opt_barrier),
 +      fsparam_flag    ("nobarrier",           Opt_nobarrier),
 +      fsparam_flag    ("i_version",           Opt_i_version),
 +      fsparam_flag    ("dax",                 Opt_dax),
 +      fsparam_enum    ("dax",                 Opt_dax_type, ext4_param_dax),
 +      fsparam_u32     ("stripe",              Opt_stripe),
 +      fsparam_flag    ("delalloc",            Opt_delalloc),
 +      fsparam_flag    ("nodelalloc",          Opt_nodelalloc),
 +      fsparam_flag    ("warn_on_error",       Opt_warn_on_error),
 +      fsparam_flag    ("nowarn_on_error",     Opt_nowarn_on_error),
 +      fsparam_u32     ("debug_want_extra_isize",
 +                                              Opt_debug_want_extra_isize),
 +      fsparam_flag    ("mblk_io_submit",      Opt_removed),
 +      fsparam_flag    ("nomblk_io_submit",    Opt_removed),
 +      fsparam_flag    ("block_validity",      Opt_block_validity),
 +      fsparam_flag    ("noblock_validity",    Opt_noblock_validity),
 +      fsparam_u32     ("inode_readahead_blks",
 +                                              Opt_inode_readahead_blks),
 +      fsparam_u32     ("journal_ioprio",      Opt_journal_ioprio),
 +      fsparam_u32     ("auto_da_alloc",       Opt_auto_da_alloc),
 +      fsparam_flag    ("auto_da_alloc",       Opt_auto_da_alloc),
 +      fsparam_flag    ("noauto_da_alloc",     Opt_noauto_da_alloc),
 +      fsparam_flag    ("dioread_nolock",      Opt_dioread_nolock),
 +      fsparam_flag    ("nodioread_nolock",    Opt_dioread_lock),
 +      fsparam_flag    ("dioread_lock",        Opt_dioread_lock),
 +      fsparam_flag    ("discard",             Opt_discard),
 +      fsparam_flag    ("nodiscard",           Opt_nodiscard),
 +      fsparam_u32     ("init_itable",         Opt_init_itable),
 +      fsparam_flag    ("init_itable",         Opt_init_itable),
 +      fsparam_flag    ("noinit_itable",       Opt_noinit_itable),
 +#ifdef CONFIG_EXT4_DEBUG
 +      fsparam_flag    ("fc_debug_force",      Opt_fc_debug_force),
 +      fsparam_u32     ("fc_debug_max_replay", Opt_fc_debug_max_replay),
 +#endif
 +      fsparam_u32     ("max_dir_size_kb",     Opt_max_dir_size_kb),
 +      fsparam_flag    ("test_dummy_encryption",
 +                                              Opt_test_dummy_encryption),
 +      fsparam_string  ("test_dummy_encryption",
 +                                              Opt_test_dummy_encryption),
 +      fsparam_flag    ("inlinecrypt",         Opt_inlinecrypt),
 +      fsparam_flag    ("nombcache",           Opt_nombcache),
 +      fsparam_flag    ("no_mbcache",          Opt_nombcache), /* for backward compatibility */
 +      fsparam_flag    ("prefetch_block_bitmaps",
 +                                              Opt_removed),
 +      fsparam_flag    ("no_prefetch_block_bitmaps",
 +                                              Opt_no_prefetch_block_bitmaps),
 +      fsparam_s32     ("mb_optimize_scan",    Opt_mb_optimize_scan),
 +      fsparam_string  ("check",               Opt_removed),   /* mount option from ext2/3 */
 +      fsparam_flag    ("nocheck",             Opt_removed),   /* mount option from ext2/3 */
 +      fsparam_flag    ("reservation",         Opt_removed),   /* mount option from ext2/3 */
 +      fsparam_flag    ("noreservation",       Opt_removed),   /* mount option from ext2/3 */
 +      fsparam_u32     ("journal",             Opt_removed),   /* mount option from ext2/3 */
 +      {}
 +};
  
  #define DEFAULT_JOURNAL_IOPRIO (IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 3))
  #define DEFAULT_MB_OPTIMIZE_SCAN      (-1)
@@@ -1869,22 -1821,90 +1869,22 @@@ static const char deprecated_msg[] 
        "Mount option \"%s\" will be removed by %s\n"
        "Contact linux-ext4@vger.kernel.org if you think we should keep it.\n";
  
 -#ifdef CONFIG_QUOTA
 -static int set_qf_name(struct super_block *sb, int qtype, substring_t *args)
 -{
 -      struct ext4_sb_info *sbi = EXT4_SB(sb);
 -      char *qname, *old_qname = get_qf_name(sb, sbi, qtype);
 -      int ret = -1;
 -
 -      if (sb_any_quota_loaded(sb) && !old_qname) {
 -              ext4_msg(sb, KERN_ERR,
 -                      "Cannot change journaled "
 -                      "quota options when quota turned on");
 -              return -1;
 -      }
 -      if (ext4_has_feature_quota(sb)) {
 -              ext4_msg(sb, KERN_INFO, "Journaled quota options "
 -                       "ignored when QUOTA feature is enabled");
 -              return 1;
 -      }
 -      qname = match_strdup(args);
 -      if (!qname) {
 -              ext4_msg(sb, KERN_ERR,
 -                      "Not enough memory for storing quotafile name");
 -              return -1;
 -      }
 -      if (old_qname) {
 -              if (strcmp(old_qname, qname) == 0)
 -                      ret = 1;
 -              else
 -                      ext4_msg(sb, KERN_ERR,
 -                               "%s quota file already specified",
 -                               QTYPE2NAME(qtype));
 -              goto errout;
 -      }
 -      if (strchr(qname, '/')) {
 -              ext4_msg(sb, KERN_ERR,
 -                      "quotafile must be on filesystem root");
 -              goto errout;
 -      }
 -      rcu_assign_pointer(sbi->s_qf_names[qtype], qname);
 -      set_opt(sb, QUOTA);
 -      return 1;
 -errout:
 -      kfree(qname);
 -      return ret;
 -}
 -
 -static int clear_qf_name(struct super_block *sb, int qtype)
 -{
 -
 -      struct ext4_sb_info *sbi = EXT4_SB(sb);
 -      char *old_qname = get_qf_name(sb, sbi, qtype);
 -
 -      if (sb_any_quota_loaded(sb) && old_qname) {
 -              ext4_msg(sb, KERN_ERR, "Cannot change journaled quota options"
 -                      " when quota turned on");
 -              return -1;
 -      }
 -      rcu_assign_pointer(sbi->s_qf_names[qtype], NULL);
 -      synchronize_rcu();
 -      kfree(old_qname);
 -      return 1;
 -}
 -#endif
 -
  #define MOPT_SET      0x0001
  #define MOPT_CLEAR    0x0002
  #define MOPT_NOSUPPORT        0x0004
  #define MOPT_EXPLICIT 0x0008
 -#define MOPT_CLEAR_ERR        0x0010
 -#define MOPT_GTE0     0x0020
  #ifdef CONFIG_QUOTA
  #define MOPT_Q                0
 -#define MOPT_QFMT     0x0040
 +#define MOPT_QFMT     0x0010
  #else
  #define MOPT_Q                MOPT_NOSUPPORT
  #define MOPT_QFMT     MOPT_NOSUPPORT
  #endif
 -#define MOPT_DATAJ    0x0080
 -#define MOPT_NO_EXT2  0x0100
 -#define MOPT_NO_EXT3  0x0200
 +#define MOPT_NO_EXT2  0x0020
 +#define MOPT_NO_EXT3  0x0040
  #define MOPT_EXT4_ONLY        (MOPT_NO_EXT2 | MOPT_NO_EXT3)
 -#define MOPT_STRING   0x0400
 -#define MOPT_SKIP     0x0800
 -#define       MOPT_2          0x1000
 +#define MOPT_SKIP     0x0080
 +#define       MOPT_2          0x0100
  
  static const struct mount_opts {
        int     token;
                                    EXT4_MOUNT_JOURNAL_CHECKSUM),
         MOPT_EXT4_ONLY | MOPT_SET | MOPT_EXPLICIT},
        {Opt_noload, EXT4_MOUNT_NOLOAD, MOPT_NO_EXT2 | MOPT_SET},
 -      {Opt_err_panic, EXT4_MOUNT_ERRORS_PANIC, MOPT_SET | MOPT_CLEAR_ERR},
 -      {Opt_err_ro, EXT4_MOUNT_ERRORS_RO, MOPT_SET | MOPT_CLEAR_ERR},
 -      {Opt_err_cont, EXT4_MOUNT_ERRORS_CONT, MOPT_SET | MOPT_CLEAR_ERR},
 -      {Opt_data_err_abort, EXT4_MOUNT_DATA_ERR_ABORT,
 -       MOPT_NO_EXT2},
 -      {Opt_data_err_ignore, EXT4_MOUNT_DATA_ERR_ABORT,
 -       MOPT_NO_EXT2},
 +      {Opt_data_err, EXT4_MOUNT_DATA_ERR_ABORT, MOPT_NO_EXT2},
        {Opt_barrier, EXT4_MOUNT_BARRIER, MOPT_SET},
        {Opt_nobarrier, EXT4_MOUNT_BARRIER, MOPT_CLEAR},
        {Opt_noauto_da_alloc, EXT4_MOUNT_NO_AUTO_DA_ALLOC, MOPT_SET},
        {Opt_auto_da_alloc, EXT4_MOUNT_NO_AUTO_DA_ALLOC, MOPT_CLEAR},
        {Opt_noinit_itable, EXT4_MOUNT_INIT_INODE_TABLE, MOPT_CLEAR},
 -      {Opt_commit, 0, MOPT_GTE0},
 -      {Opt_max_batch_time, 0, MOPT_GTE0},
 -      {Opt_min_batch_time, 0, MOPT_GTE0},
 -      {Opt_inode_readahead_blks, 0, MOPT_GTE0},
 -      {Opt_init_itable, 0, MOPT_GTE0},
 -      {Opt_dax, EXT4_MOUNT_DAX_ALWAYS, MOPT_SET | MOPT_SKIP},
 -      {Opt_dax_always, EXT4_MOUNT_DAX_ALWAYS,
 -              MOPT_EXT4_ONLY | MOPT_SET | MOPT_SKIP},
 -      {Opt_dax_inode, EXT4_MOUNT2_DAX_INODE,
 -              MOPT_EXT4_ONLY | MOPT_SET | MOPT_SKIP},
 -      {Opt_dax_never, EXT4_MOUNT2_DAX_NEVER,
 -              MOPT_EXT4_ONLY | MOPT_SET | MOPT_SKIP},
 -      {Opt_stripe, 0, MOPT_GTE0},
 -      {Opt_resuid, 0, MOPT_GTE0},
 -      {Opt_resgid, 0, MOPT_GTE0},
 -      {Opt_journal_dev, 0, MOPT_NO_EXT2 | MOPT_GTE0},
 -      {Opt_journal_path, 0, MOPT_NO_EXT2 | MOPT_STRING},
 -      {Opt_journal_ioprio, 0, MOPT_NO_EXT2 | MOPT_GTE0},
 -      {Opt_data_journal, EXT4_MOUNT_JOURNAL_DATA, MOPT_NO_EXT2 | MOPT_DATAJ},
 -      {Opt_data_ordered, EXT4_MOUNT_ORDERED_DATA, MOPT_NO_EXT2 | MOPT_DATAJ},
 -      {Opt_data_writeback, EXT4_MOUNT_WRITEBACK_DATA,
 -       MOPT_NO_EXT2 | MOPT_DATAJ},
 +      {Opt_dax_type, 0, MOPT_EXT4_ONLY},
 +      {Opt_journal_dev, 0, MOPT_NO_EXT2},
 +      {Opt_journal_path, 0, MOPT_NO_EXT2},
 +      {Opt_journal_ioprio, 0, MOPT_NO_EXT2},
 +      {Opt_data, 0, MOPT_NO_EXT2},
        {Opt_user_xattr, EXT4_MOUNT_XATTR_USER, MOPT_SET},
        {Opt_nouser_xattr, EXT4_MOUNT_XATTR_USER, MOPT_CLEAR},
  #ifdef CONFIG_EXT4_FS_POSIX_ACL
  #endif
        {Opt_nouid32, EXT4_MOUNT_NO_UID32, MOPT_SET},
        {Opt_debug, EXT4_MOUNT_DEBUG, MOPT_SET},
 -      {Opt_debug_want_extra_isize, 0, MOPT_GTE0},
        {Opt_quota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA, MOPT_SET | MOPT_Q},
        {Opt_usrquota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA,
                                                        MOPT_SET | MOPT_Q},
        {Opt_noquota, (EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA |
                       EXT4_MOUNT_GRPQUOTA | EXT4_MOUNT_PRJQUOTA),
                                                        MOPT_CLEAR | MOPT_Q},
 -      {Opt_usrjquota, 0, MOPT_Q | MOPT_STRING},
 -      {Opt_grpjquota, 0, MOPT_Q | MOPT_STRING},
 -      {Opt_offusrjquota, 0, MOPT_Q},
 -      {Opt_offgrpjquota, 0, MOPT_Q},
 -      {Opt_jqfmt_vfsold, QFMT_VFS_OLD, MOPT_QFMT},
 -      {Opt_jqfmt_vfsv0, QFMT_VFS_V0, MOPT_QFMT},
 -      {Opt_jqfmt_vfsv1, QFMT_VFS_V1, MOPT_QFMT},
 -      {Opt_max_dir_size_kb, 0, MOPT_GTE0},
 -      {Opt_test_dummy_encryption, 0, MOPT_STRING},
 +      {Opt_usrjquota, 0, MOPT_Q},
 +      {Opt_grpjquota, 0, MOPT_Q},
 +      {Opt_jqfmt, 0, MOPT_QFMT},
        {Opt_nombcache, EXT4_MOUNT_NO_MBCACHE, MOPT_SET},
        {Opt_no_prefetch_block_bitmaps, EXT4_MOUNT_NO_PREFETCH_BLOCK_BITMAPS,
         MOPT_SET},
 -      {Opt_mb_optimize_scan, EXT4_MOUNT2_MB_OPTIMIZE_SCAN, MOPT_GTE0},
  #ifdef CONFIG_EXT4_DEBUG
        {Opt_fc_debug_force, EXT4_MOUNT2_JOURNAL_FAST_COMMIT,
         MOPT_SET | MOPT_2 | MOPT_EXT4_ONLY},
 -      {Opt_fc_debug_max_replay, 0, MOPT_GTE0},
  #endif
        {Opt_err, 0, 0}
  };
  static const struct ext4_sb_encodings {
        __u16 magic;
        char *name;
-       char *version;
+       unsigned int version;
  } ext4_sb_encoding_map[] = {
-       {EXT4_ENC_UTF8_12_1, "utf8", "12.1.0"},
+       {EXT4_ENC_UTF8_12_1, "utf8", UNICODE_AGE(12, 1, 0)},
  };
  
- static int ext4_sb_read_encoding(const struct ext4_super_block *es,
-                                const struct ext4_sb_encodings **encoding,
-                                __u16 *flags)
+ static const struct ext4_sb_encodings *
+ ext4_sb_read_encoding(const struct ext4_super_block *es)
  {
        __u16 magic = le16_to_cpu(es->s_encoding);
        int i;
  
        for (i = 0; i < ARRAY_SIZE(ext4_sb_encoding_map); i++)
                if (magic == ext4_sb_encoding_map[i].magic)
-                       break;
-       if (i >= ARRAY_SIZE(ext4_sb_encoding_map))
-               return -EINVAL;
+                       return &ext4_sb_encoding_map[i];
  
-       *encoding = &ext4_sb_encoding_map[i];
-       *flags = le16_to_cpu(es->s_encoding_flags);
-       return 0;
+       return NULL;
  }
  #endif
  
 -static int ext4_set_test_dummy_encryption(struct super_block *sb,
 -                                        const char *opt,
 -                                        const substring_t *arg,
 -                                        bool is_remount)
 +static int ext4_set_test_dummy_encryption(struct super_block *sb, char *arg)
  {
  #ifdef CONFIG_FS_ENCRYPTION
        struct ext4_sb_info *sbi = EXT4_SB(sb);
        int err;
  
 -      /*
 -       * 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 && !sbi->s_dummy_enc_policy.policy) {
 -              ext4_msg(sb, KERN_WARNING,
 -                       "Can't set test_dummy_encryption on remount");
 -              return -1;
 -      }
 -      err = fscrypt_set_test_dummy_encryption(sb, arg->from,
 +      err = fscrypt_set_test_dummy_encryption(sb, arg,
                                                &sbi->s_dummy_enc_policy);
        if (err) {
 -              if (err == -EEXIST)
 -                      ext4_msg(sb, KERN_WARNING,
 -                               "Can't change test_dummy_encryption on remount");
 -              else if (err == -EINVAL)
 -                      ext4_msg(sb, KERN_WARNING,
 -                               "Value of option \"%s\" is unrecognized", opt);
 -              else
 -                      ext4_msg(sb, KERN_WARNING,
 -                               "Error processing option \"%s\" [%d]",
 -                               opt, err);
 -              return -1;
 +              ext4_msg(sb, KERN_WARNING,
 +                       "Error while setting test dummy encryption [%d]", err);
 +              return err;
        }
        ext4_msg(sb, KERN_WARNING, "Test dummy encryption mode enabled");
 -#else
 -      ext4_msg(sb, KERN_WARNING,
 -               "Test dummy encryption mount option ignored");
  #endif
 -      return 1;
 +      return 0;
  }
  
 -struct ext4_parsed_options {
 -      unsigned long journal_devnum;
 -      unsigned int journal_ioprio;
 -      int mb_optimize_scan;
 +#define EXT4_SPEC_JQUOTA                      (1 <<  0)
 +#define EXT4_SPEC_JQFMT                               (1 <<  1)
 +#define EXT4_SPEC_DATAJ                               (1 <<  2)
 +#define EXT4_SPEC_SB_BLOCK                    (1 <<  3)
 +#define EXT4_SPEC_JOURNAL_DEV                 (1 <<  4)
 +#define EXT4_SPEC_JOURNAL_IOPRIO              (1 <<  5)
 +#define EXT4_SPEC_DUMMY_ENCRYPTION            (1 <<  6)
 +#define EXT4_SPEC_s_want_extra_isize          (1 <<  7)
 +#define EXT4_SPEC_s_max_batch_time            (1 <<  8)
 +#define EXT4_SPEC_s_min_batch_time            (1 <<  9)
 +#define EXT4_SPEC_s_inode_readahead_blks      (1 << 10)
 +#define EXT4_SPEC_s_li_wait_mult              (1 << 11)
 +#define EXT4_SPEC_s_max_dir_size_kb           (1 << 12)
 +#define EXT4_SPEC_s_stripe                    (1 << 13)
 +#define EXT4_SPEC_s_resuid                    (1 << 14)
 +#define EXT4_SPEC_s_resgid                    (1 << 15)
 +#define EXT4_SPEC_s_commit_interval           (1 << 16)
 +#define EXT4_SPEC_s_fc_debug_max_replay               (1 << 17)
 +#define EXT4_SPEC_s_sb_block                  (1 << 18)
 +
 +struct ext4_fs_context {
 +      char            *s_qf_names[EXT4_MAXQUOTAS];
 +      char            *test_dummy_enc_arg;
 +      int             s_jquota_fmt;   /* Format of quota to use */
 +      int             mb_optimize_scan;
 +#ifdef CONFIG_EXT4_DEBUG
 +      int s_fc_debug_max_replay;
 +#endif
 +      unsigned short  qname_spec;
 +      unsigned long   vals_s_flags;   /* Bits to set in s_flags */
 +      unsigned long   mask_s_flags;   /* Bits changed in s_flags */
 +      unsigned long   journal_devnum;
 +      unsigned long   s_commit_interval;
 +      unsigned long   s_stripe;
 +      unsigned int    s_inode_readahead_blks;
 +      unsigned int    s_want_extra_isize;
 +      unsigned int    s_li_wait_mult;
 +      unsigned int    s_max_dir_size_kb;
 +      unsigned int    journal_ioprio;
 +      unsigned int    vals_s_mount_opt;
 +      unsigned int    mask_s_mount_opt;
 +      unsigned int    vals_s_mount_opt2;
 +      unsigned int    mask_s_mount_opt2;
 +      unsigned int    vals_s_mount_flags;
 +      unsigned int    mask_s_mount_flags;
 +      unsigned int    opt_flags;      /* MOPT flags */
 +      unsigned int    spec;
 +      u32             s_max_batch_time;
 +      u32             s_min_batch_time;
 +      kuid_t          s_resuid;
 +      kgid_t          s_resgid;
 +      ext4_fsblk_t    s_sb_block;
  };
  
 -static int handle_mount_opt(struct super_block *sb, char *opt, int token,
 -                          substring_t *args, struct ext4_parsed_options *parsed_opts,
 -                          int is_remount)
 +static void ext4_fc_free(struct fs_context *fc)
  {
 -      struct ext4_sb_info *sbi = EXT4_SB(sb);
 +      struct ext4_fs_context *ctx = fc->fs_private;
 +      int i;
 +
 +      if (!ctx)
 +              return;
 +
 +      for (i = 0; i < EXT4_MAXQUOTAS; i++)
 +              kfree(ctx->s_qf_names[i]);
 +
 +      kfree(ctx->test_dummy_enc_arg);
 +      kfree(ctx);
 +}
 +
 +int ext4_init_fs_context(struct fs_context *fc)
 +{
 +      struct ext4_fs_context *ctx;
 +
 +      ctx = kzalloc(sizeof(struct ext4_fs_context), GFP_KERNEL);
 +      if (!ctx)
 +              return -ENOMEM;
 +
 +      fc->fs_private = ctx;
 +      fc->ops = &ext4_context_ops;
 +
 +      return 0;
 +}
 +
 +#ifdef CONFIG_QUOTA
 +/*
 + * Note the name of the specified quota file.
 + */
 +static int note_qf_name(struct fs_context *fc, int qtype,
 +                     struct fs_parameter *param)
 +{
 +      struct ext4_fs_context *ctx = fc->fs_private;
 +      char *qname;
 +
 +      if (param->size < 1) {
 +              ext4_msg(NULL, KERN_ERR, "Missing quota name");
 +              return -EINVAL;
 +      }
 +      if (strchr(param->string, '/')) {
 +              ext4_msg(NULL, KERN_ERR,
 +                       "quotafile must be on filesystem root");
 +              return -EINVAL;
 +      }
 +      if (ctx->s_qf_names[qtype]) {
 +              if (strcmp(ctx->s_qf_names[qtype], param->string) != 0) {
 +                      ext4_msg(NULL, KERN_ERR,
 +                               "%s quota file already specified",
 +                               QTYPE2NAME(qtype));
 +                      return -EINVAL;
 +              }
 +              return 0;
 +      }
 +
 +      qname = kmemdup_nul(param->string, param->size, GFP_KERNEL);
 +      if (!qname) {
 +              ext4_msg(NULL, KERN_ERR,
 +                       "Not enough memory for storing quotafile name");
 +              return -ENOMEM;
 +      }
 +      ctx->s_qf_names[qtype] = qname;
 +      ctx->qname_spec |= 1 << qtype;
 +      ctx->spec |= EXT4_SPEC_JQUOTA;
 +      return 0;
 +}
 +
 +/*
 + * Clear the name of the specified quota file.
 + */
 +static int unnote_qf_name(struct fs_context *fc, int qtype)
 +{
 +      struct ext4_fs_context *ctx = fc->fs_private;
 +
 +      if (ctx->s_qf_names[qtype])
 +              kfree(ctx->s_qf_names[qtype]);
 +
 +      ctx->s_qf_names[qtype] = NULL;
 +      ctx->qname_spec |= 1 << qtype;
 +      ctx->spec |= EXT4_SPEC_JQUOTA;
 +      return 0;
 +}
 +#endif
 +
 +#define EXT4_SET_CTX(name)                                            \
 +static inline void ctx_set_##name(struct ext4_fs_context *ctx,                \
 +                                unsigned long flag)                   \
 +{                                                                     \
 +      ctx->mask_s_##name |= flag;                                     \
 +      ctx->vals_s_##name |= flag;                                     \
 +}                                                                     \
 +static inline void ctx_clear_##name(struct ext4_fs_context *ctx,      \
 +                                  unsigned long flag)                 \
 +{                                                                     \
 +      ctx->mask_s_##name |= flag;                                     \
 +      ctx->vals_s_##name &= ~flag;                                    \
 +}                                                                     \
 +static inline unsigned long                                           \
 +ctx_test_##name(struct ext4_fs_context *ctx, unsigned long flag)      \
 +{                                                                     \
 +      return (ctx->vals_s_##name & flag);                             \
 +}                                                                     \
 +
 +EXT4_SET_CTX(flags);
 +EXT4_SET_CTX(mount_opt);
 +EXT4_SET_CTX(mount_opt2);
 +EXT4_SET_CTX(mount_flags);
 +
 +static int ext4_parse_param(struct fs_context *fc, struct fs_parameter *param)
 +{
 +      struct ext4_fs_context *ctx = fc->fs_private;
 +      struct fs_parse_result result;
        const struct mount_opts *m;
 +      int is_remount;
        kuid_t uid;
        kgid_t gid;
 -      int arg = 0;
 +      int token;
  
 -#ifdef CONFIG_QUOTA
 -      if (token == Opt_usrjquota)
 -              return set_qf_name(sb, USRQUOTA, &args[0]);
 -      else if (token == Opt_grpjquota)
 -              return set_qf_name(sb, GRPQUOTA, &args[0]);
 -      else if (token == Opt_offusrjquota)
 -              return clear_qf_name(sb, USRQUOTA);
 -      else if (token == Opt_offgrpjquota)
 -              return clear_qf_name(sb, GRPQUOTA);
 -#endif
 -      switch (token) {
 -      case Opt_noacl:
 -      case Opt_nouser_xattr:
 -              ext4_msg(sb, KERN_WARNING, deprecated_msg, opt, "3.5");
 -              break;
 -      case Opt_sb:
 -              return 1;       /* handled by get_sb_block() */
 -      case Opt_removed:
 -              ext4_msg(sb, KERN_WARNING, "Ignoring removed %s option", opt);
 -              return 1;
 -      case Opt_abort:
 -              ext4_set_mount_flag(sb, EXT4_MF_FS_ABORTED);
 -              return 1;
 -      case Opt_i_version:
 -              sb->s_flags |= SB_I_VERSION;
 -              return 1;
 -      case Opt_lazytime:
 -              sb->s_flags |= SB_LAZYTIME;
 -              return 1;
 -      case Opt_nolazytime:
 -              sb->s_flags &= ~SB_LAZYTIME;
 -              return 1;
 -      case Opt_inlinecrypt:
 -#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
 -              sb->s_flags |= SB_INLINECRYPT;
 -#else
 -              ext4_msg(sb, KERN_ERR, "inline encryption not supported");
 -#endif
 -              return 1;
 -      }
 +      token = fs_parse(fc, ext4_param_specs, param, &result);
 +      if (token < 0)
 +              return token;
 +      is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;
  
        for (m = ext4_mount_opts; m->token != Opt_err; m++)
                if (token == m->token)
                        break;
  
 -      if (m->token == Opt_err) {
 -              ext4_msg(sb, KERN_ERR, "Unrecognized mount option \"%s\" "
 -                       "or missing value", opt);
 -              return -1;
 -      }
 -
 -      if ((m->flags & MOPT_NO_EXT2) && IS_EXT2_SB(sb)) {
 -              ext4_msg(sb, KERN_ERR,
 -                       "Mount option \"%s\" incompatible with ext2", opt);
 -              return -1;
 -      }
 -      if ((m->flags & MOPT_NO_EXT3) && IS_EXT3_SB(sb)) {
 -              ext4_msg(sb, KERN_ERR,
 -                       "Mount option \"%s\" incompatible with ext3", opt);
 -              return -1;
 -      }
 +      ctx->opt_flags |= m->flags;
  
 -      if (args->from && !(m->flags & MOPT_STRING) && match_int(args, &arg))
 -              return -1;
 -      if (args->from && (m->flags & MOPT_GTE0) && (arg < 0))
 -              return -1;
        if (m->flags & MOPT_EXPLICIT) {
                if (m->mount_opt & EXT4_MOUNT_DELALLOC) {
 -                      set_opt2(sb, EXPLICIT_DELALLOC);
 +                      ctx_set_mount_opt2(ctx, EXT4_MOUNT2_EXPLICIT_DELALLOC);
                } else if (m->mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) {
 -                      set_opt2(sb, EXPLICIT_JOURNAL_CHECKSUM);
 +                      ctx_set_mount_opt2(ctx,
 +                                     EXT4_MOUNT2_EXPLICIT_JOURNAL_CHECKSUM);
                } else
 -                      return -1;
 -      }
 -      if (m->flags & MOPT_CLEAR_ERR)
 -              clear_opt(sb, ERRORS_MASK);
 -      if (token == Opt_noquota && sb_any_quota_loaded(sb)) {
 -              ext4_msg(sb, KERN_ERR, "Cannot change quota "
 -                       "options when quota turned on");
 -              return -1;
 +                      return -EINVAL;
        }
  
        if (m->flags & MOPT_NOSUPPORT) {
 -              ext4_msg(sb, KERN_ERR, "%s option not supported", opt);
 -      } else if (token == Opt_commit) {
 -              if (arg == 0)
 -                      arg = JBD2_DEFAULT_MAX_COMMIT_AGE;
 -              else if (arg > INT_MAX / HZ) {
 -                      ext4_msg(sb, KERN_ERR,
 +              ext4_msg(NULL, KERN_ERR, "%s option not supported",
 +                       param->key);
 +              return 0;
 +      }
 +
 +      switch (token) {
 +#ifdef CONFIG_QUOTA
 +      case Opt_usrjquota:
 +              if (!*param->string)
 +                      return unnote_qf_name(fc, USRQUOTA);
 +              else
 +                      return note_qf_name(fc, USRQUOTA, param);
 +      case Opt_grpjquota:
 +              if (!*param->string)
 +                      return unnote_qf_name(fc, GRPQUOTA);
 +              else
 +                      return note_qf_name(fc, GRPQUOTA, param);
 +#endif
 +      case Opt_noacl:
 +      case Opt_nouser_xattr:
 +              ext4_msg(NULL, KERN_WARNING, deprecated_msg, param->key, "3.5");
 +              break;
 +      case Opt_sb:
 +              if (fc->purpose == FS_CONTEXT_FOR_RECONFIGURE) {
 +                      ext4_msg(NULL, KERN_WARNING,
 +                               "Ignoring %s option on remount", param->key);
 +              } else {
 +                      ctx->s_sb_block = result.uint_32;
 +                      ctx->spec |= EXT4_SPEC_s_sb_block;
 +              }
 +              return 0;
 +      case Opt_removed:
 +              ext4_msg(NULL, KERN_WARNING, "Ignoring removed %s option",
 +                       param->key);
 +              return 0;
 +      case Opt_abort:
 +              ctx_set_mount_flags(ctx, EXT4_MF_FS_ABORTED);
 +              return 0;
 +      case Opt_i_version:
 +              ext4_msg(NULL, KERN_WARNING, deprecated_msg, param->key, "5.20");
 +              ext4_msg(NULL, KERN_WARNING, "Use iversion instead\n");
 +              ctx_set_flags(ctx, SB_I_VERSION);
 +              return 0;
 +      case Opt_inlinecrypt:
 +#ifdef CONFIG_FS_ENCRYPTION_INLINE_CRYPT
 +              ctx_set_flags(ctx, SB_INLINECRYPT);
 +#else
 +              ext4_msg(NULL, KERN_ERR, "inline encryption not supported");
 +#endif
 +              return 0;
 +      case Opt_errors:
 +              ctx_clear_mount_opt(ctx, EXT4_MOUNT_ERRORS_MASK);
 +              ctx_set_mount_opt(ctx, result.uint_32);
 +              return 0;
 +#ifdef CONFIG_QUOTA
 +      case Opt_jqfmt:
 +              ctx->s_jquota_fmt = result.uint_32;
 +              ctx->spec |= EXT4_SPEC_JQFMT;
 +              return 0;
 +#endif
 +      case Opt_data:
 +              ctx_clear_mount_opt(ctx, EXT4_MOUNT_DATA_FLAGS);
 +              ctx_set_mount_opt(ctx, result.uint_32);
 +              ctx->spec |= EXT4_SPEC_DATAJ;
 +              return 0;
 +      case Opt_commit:
 +              if (result.uint_32 == 0)
 +                      ctx->s_commit_interval = JBD2_DEFAULT_MAX_COMMIT_AGE;
 +              else if (result.uint_32 > INT_MAX / HZ) {
 +                      ext4_msg(NULL, KERN_ERR,
                                 "Invalid commit interval %d, "
                                 "must be smaller than %d",
 -                               arg, INT_MAX / HZ);
 -                      return -1;
 +                               result.uint_32, INT_MAX / HZ);
 +                      return -EINVAL;
                }
 -              sbi->s_commit_interval = HZ * arg;
 -      } else if (token == Opt_debug_want_extra_isize) {
 -              if ((arg & 1) ||
 -                  (arg < 4) ||
 -                  (arg > (sbi->s_inode_size - EXT4_GOOD_OLD_INODE_SIZE))) {
 -                      ext4_msg(sb, KERN_ERR,
 -                               "Invalid want_extra_isize %d", arg);
 -                      return -1;
 +              ctx->s_commit_interval = HZ * result.uint_32;
 +              ctx->spec |= EXT4_SPEC_s_commit_interval;
 +              return 0;
 +      case Opt_debug_want_extra_isize:
 +              if ((result.uint_32 & 1) || (result.uint_32 < 4)) {
 +                      ext4_msg(NULL, KERN_ERR,
 +                               "Invalid want_extra_isize %d", result.uint_32);
 +                      return -EINVAL;
                }
 -              sbi->s_want_extra_isize = arg;
 -      } else if (token == Opt_max_batch_time) {
 -              sbi->s_max_batch_time = arg;
 -      } else if (token == Opt_min_batch_time) {
 -              sbi->s_min_batch_time = arg;
 -      } else if (token == Opt_inode_readahead_blks) {
 -              if (arg && (arg > (1 << 30) || !is_power_of_2(arg))) {
 -                      ext4_msg(sb, KERN_ERR,
 +              ctx->s_want_extra_isize = result.uint_32;
 +              ctx->spec |= EXT4_SPEC_s_want_extra_isize;
 +              return 0;
 +      case Opt_max_batch_time:
 +              ctx->s_max_batch_time = result.uint_32;
 +              ctx->spec |= EXT4_SPEC_s_max_batch_time;
 +              return 0;
 +      case Opt_min_batch_time:
 +              ctx->s_min_batch_time = result.uint_32;
 +              ctx->spec |= EXT4_SPEC_s_min_batch_time;
 +              return 0;
 +      case Opt_inode_readahead_blks:
 +              if (result.uint_32 &&
 +                  (result.uint_32 > (1 << 30) ||
 +                   !is_power_of_2(result.uint_32))) {
 +                      ext4_msg(NULL, KERN_ERR,
                                 "EXT4-fs: inode_readahead_blks must be "
                                 "0 or a power of 2 smaller than 2^31");
 -                      return -1;
 +                      return -EINVAL;
                }
 -              sbi->s_inode_readahead_blks = arg;
 -      } else if (token == Opt_init_itable) {
 -              set_opt(sb, INIT_INODE_TABLE);
 -              if (!args->from)
 -                      arg = EXT4_DEF_LI_WAIT_MULT;
 -              sbi->s_li_wait_mult = arg;
 -      } else if (token == Opt_max_dir_size_kb) {
 -              sbi->s_max_dir_size_kb = arg;
 +              ctx->s_inode_readahead_blks = result.uint_32;
 +              ctx->spec |= EXT4_SPEC_s_inode_readahead_blks;
 +              return 0;
 +      case Opt_init_itable:
 +              ctx_set_mount_opt(ctx, EXT4_MOUNT_INIT_INODE_TABLE);
 +              ctx->s_li_wait_mult = EXT4_DEF_LI_WAIT_MULT;
 +              if (param->type == fs_value_is_string)
 +                      ctx->s_li_wait_mult = result.uint_32;
 +              ctx->spec |= EXT4_SPEC_s_li_wait_mult;
 +              return 0;
 +      case Opt_max_dir_size_kb:
 +              ctx->s_max_dir_size_kb = result.uint_32;
 +              ctx->spec |= EXT4_SPEC_s_max_dir_size_kb;
 +              return 0;
  #ifdef CONFIG_EXT4_DEBUG
 -      } else if (token == Opt_fc_debug_max_replay) {
 -              sbi->s_fc_debug_max_replay = arg;
 +      case Opt_fc_debug_max_replay:
 +              ctx->s_fc_debug_max_replay = result.uint_32;
 +              ctx->spec |= EXT4_SPEC_s_fc_debug_max_replay;
 +              return 0;
  #endif
 -      } else if (token == Opt_stripe) {
 -              sbi->s_stripe = arg;
 -      } else if (token == Opt_resuid) {
 -              uid = make_kuid(current_user_ns(), arg);
 +      case Opt_stripe:
 +              ctx->s_stripe = result.uint_32;
 +              ctx->spec |= EXT4_SPEC_s_stripe;
 +              return 0;
 +      case Opt_resuid:
 +              uid = make_kuid(current_user_ns(), result.uint_32);
                if (!uid_valid(uid)) {
 -                      ext4_msg(sb, KERN_ERR, "Invalid uid value %d", arg);
 -                      return -1;
 +                      ext4_msg(NULL, KERN_ERR, "Invalid uid value %d",
 +                               result.uint_32);
 +                      return -EINVAL;
                }
 -              sbi->s_resuid = uid;
 -      } else if (token == Opt_resgid) {
 -              gid = make_kgid(current_user_ns(), arg);
 +              ctx->s_resuid = uid;
 +              ctx->spec |= EXT4_SPEC_s_resuid;
 +              return 0;
 +      case Opt_resgid:
 +              gid = make_kgid(current_user_ns(), result.uint_32);
                if (!gid_valid(gid)) {
 -                      ext4_msg(sb, KERN_ERR, "Invalid gid value %d", arg);
 -                      return -1;
 +                      ext4_msg(NULL, KERN_ERR, "Invalid gid value %d",
 +                               result.uint_32);
 +                      return -EINVAL;
                }
 -              sbi->s_resgid = gid;
 -      } else if (token == Opt_journal_dev) {
 +              ctx->s_resgid = gid;
 +              ctx->spec |= EXT4_SPEC_s_resgid;
 +              return 0;
 +      case Opt_journal_dev:
                if (is_remount) {
 -                      ext4_msg(sb, KERN_ERR,
 +                      ext4_msg(NULL, KERN_ERR,
                                 "Cannot specify journal on remount");
 -                      return -1;
 +                      return -EINVAL;
                }
 -              parsed_opts->journal_devnum = arg;
 -      } else if (token == Opt_journal_path) {
 -              char *journal_path;
 +              ctx->journal_devnum = result.uint_32;
 +              ctx->spec |= EXT4_SPEC_JOURNAL_DEV;
 +              return 0;
 +      case Opt_journal_path:
 +      {
                struct inode *journal_inode;
                struct path path;
                int error;
  
                if (is_remount) {
 -                      ext4_msg(sb, KERN_ERR,
 +                      ext4_msg(NULL, KERN_ERR,
                                 "Cannot specify journal on remount");
 -                      return -1;
 -              }
 -              journal_path = match_strdup(&args[0]);
 -              if (!journal_path) {
 -                      ext4_msg(sb, KERN_ERR, "error: could not dup "
 -                              "journal device string");
 -                      return -1;
 +                      return -EINVAL;
                }
  
 -              error = kern_path(journal_path, LOOKUP_FOLLOW, &path);
 +              error = fs_lookup_param(fc, param, 1, &path);
                if (error) {
 -                      ext4_msg(sb, KERN_ERR, "error: could not find "
 -                              "journal device path: error %d", error);
 -                      kfree(journal_path);
 -                      return -1;
 +                      ext4_msg(NULL, KERN_ERR, "error: could not find "
 +                               "journal device path");
 +                      return -EINVAL;
                }
  
                journal_inode = d_inode(path.dentry);
 -              if (!S_ISBLK(journal_inode->i_mode)) {
 -                      ext4_msg(sb, KERN_ERR, "error: journal path %s "
 -                              "is not a block device", journal_path);
 -                      path_put(&path);
 -                      kfree(journal_path);
 -                      return -1;
 -              }
 -
 -              parsed_opts->journal_devnum = new_encode_dev(journal_inode->i_rdev);
 +              ctx->journal_devnum = new_encode_dev(journal_inode->i_rdev);
 +              ctx->spec |= EXT4_SPEC_JOURNAL_DEV;
                path_put(&path);
 -              kfree(journal_path);
 -      } else if (token == Opt_journal_ioprio) {
 -              if (arg > 7) {
 -                      ext4_msg(sb, KERN_ERR, "Invalid journal IO priority"
 +              return 0;
 +      }
 +      case Opt_journal_ioprio:
 +              if (result.uint_32 > 7) {
 +                      ext4_msg(NULL, KERN_ERR, "Invalid journal IO priority"
                                 " (must be 0-7)");
 -                      return -1;
 -              }
 -              parsed_opts->journal_ioprio =
 -                      IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, arg);
 -      } else if (token == Opt_test_dummy_encryption) {
 -              return ext4_set_test_dummy_encryption(sb, opt, &args[0],
 -                                                    is_remount);
 -      } else if (m->flags & MOPT_DATAJ) {
 -              if (is_remount) {
 -                      if (!sbi->s_journal)
 -                              ext4_msg(sb, KERN_WARNING, "Remounting file system with no journal so ignoring journalled data option");
 -                      else if (test_opt(sb, DATA_FLAGS) != m->mount_opt) {
 -                              ext4_msg(sb, KERN_ERR,
 -                                       "Cannot change data mode on remount");
 -                              return -1;
 -                      }
 -              } else {
 -                      clear_opt(sb, DATA_FLAGS);
 -                      sbi->s_mount_opt |= m->mount_opt;
 +                      return -EINVAL;
                }
 -#ifdef CONFIG_QUOTA
 -      } else if (m->flags & MOPT_QFMT) {
 -              if (sb_any_quota_loaded(sb) &&
 -                  sbi->s_jquota_fmt != m->mount_opt) {
 -                      ext4_msg(sb, KERN_ERR, "Cannot change journaled "
 -                               "quota options when quota turned on");
 -                      return -1;
 +              ctx->journal_ioprio =
 +                      IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, result.uint_32);
 +              ctx->spec |= EXT4_SPEC_JOURNAL_IOPRIO;
 +              return 0;
 +      case Opt_test_dummy_encryption:
 +#ifdef CONFIG_FS_ENCRYPTION
 +              if (param->type == fs_value_is_flag) {
 +                      ctx->spec |= EXT4_SPEC_DUMMY_ENCRYPTION;
 +                      ctx->test_dummy_enc_arg = NULL;
 +                      return 0;
                }
 -              if (ext4_has_feature_quota(sb)) {
 -                      ext4_msg(sb, KERN_INFO,
 -                               "Quota format mount options ignored "
 -                               "when QUOTA feature is enabled");
 -                      return 1;
 +              if (*param->string &&
 +                  !(!strcmp(param->string, "v1") ||
 +                    !strcmp(param->string, "v2"))) {
 +                      ext4_msg(NULL, KERN_WARNING,
 +                               "Value of option \"%s\" is unrecognized",
 +                               param->key);
 +                      return -EINVAL;
                }
 -              sbi->s_jquota_fmt = m->mount_opt;
 +              ctx->spec |= EXT4_SPEC_DUMMY_ENCRYPTION;
 +              ctx->test_dummy_enc_arg = kmemdup_nul(param->string, param->size,
 +                                                    GFP_KERNEL);
 +#else
 +              ext4_msg(NULL, KERN_WARNING,
 +                       "Test dummy encryption mount option ignored");
  #endif
 -      } else if (token == Opt_dax || token == Opt_dax_always ||
 -                 token == Opt_dax_inode || token == Opt_dax_never) {
 +              return 0;
 +      case Opt_dax:
 +      case Opt_dax_type:
  #ifdef CONFIG_FS_DAX
 -              switch (token) {
 +      {
 +              int type = (token == Opt_dax) ?
 +                         Opt_dax : result.uint_32;
 +
 +              switch (type) {
                case Opt_dax:
                case Opt_dax_always:
 -                      if (is_remount &&
 -                          (!(sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) ||
 -                           (sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER))) {
 -                      fail_dax_change_remount:
 -                              ext4_msg(sb, KERN_ERR, "can't change "
 -                                       "dax mount option while remounting");
 -                              return -1;
 -                      }
 -                      if (is_remount &&
 -                          (test_opt(sb, DATA_FLAGS) ==
 -                           EXT4_MOUNT_JOURNAL_DATA)) {
 -                                  ext4_msg(sb, KERN_ERR, "can't mount with "
 -                                           "both data=journal and dax");
 -                                  return -1;
 -                      }
 -                      ext4_msg(sb, KERN_WARNING,
 -                              "DAX enabled. Warning: EXPERIMENTAL, use at your own risk");
 -                      sbi->s_mount_opt |= EXT4_MOUNT_DAX_ALWAYS;
 -                      sbi->s_mount_opt2 &= ~EXT4_MOUNT2_DAX_NEVER;
 +                      ctx_set_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS);
 +                      ctx_clear_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER);
                        break;
                case Opt_dax_never:
 -                      if (is_remount &&
 -                          (!(sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER) ||
 -                           (sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS)))
 -                              goto fail_dax_change_remount;
 -                      sbi->s_mount_opt2 |= EXT4_MOUNT2_DAX_NEVER;
 -                      sbi->s_mount_opt &= ~EXT4_MOUNT_DAX_ALWAYS;
 +                      ctx_set_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER);
 +                      ctx_clear_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS);
                        break;
                case Opt_dax_inode:
 -                      if (is_remount &&
 -                          ((sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) ||
 -                           (sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER) ||
 -                           !(sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_INODE)))
 -                              goto fail_dax_change_remount;
 -                      sbi->s_mount_opt &= ~EXT4_MOUNT_DAX_ALWAYS;
 -                      sbi->s_mount_opt2 &= ~EXT4_MOUNT2_DAX_NEVER;
 +                      ctx_clear_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS);
 +                      ctx_clear_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER);
                        /* Strictly for printing options */
 -                      sbi->s_mount_opt2 |= EXT4_MOUNT2_DAX_INODE;
 +                      ctx_set_mount_opt2(ctx, EXT4_MOUNT2_DAX_INODE);
                        break;
                }
 +              return 0;
 +      }
  #else
 -              ext4_msg(sb, KERN_INFO, "dax option not supported");
 -              sbi->s_mount_opt2 |= EXT4_MOUNT2_DAX_NEVER;
 -              sbi->s_mount_opt &= ~EXT4_MOUNT_DAX_ALWAYS;
 -              return -1;
 +              ext4_msg(NULL, KERN_INFO, "dax option not supported");
 +              return -EINVAL;
  #endif
 -      } else if (token == Opt_data_err_abort) {
 -              sbi->s_mount_opt |= m->mount_opt;
 -      } else if (token == Opt_data_err_ignore) {
 -              sbi->s_mount_opt &= ~m->mount_opt;
 -      } else if (token == Opt_mb_optimize_scan) {
 -              if (arg != 0 && arg != 1) {
 -                      ext4_msg(sb, KERN_WARNING,
 +      case Opt_data_err:
 +              if (result.uint_32 == Opt_data_err_abort)
 +                      ctx_set_mount_opt(ctx, m->mount_opt);
 +              else if (result.uint_32 == Opt_data_err_ignore)
 +                      ctx_clear_mount_opt(ctx, m->mount_opt);
 +              return 0;
 +      case Opt_mb_optimize_scan:
 +              if (result.int_32 != 0 && result.int_32 != 1) {
 +                      ext4_msg(NULL, KERN_WARNING,
                                 "mb_optimize_scan should be set to 0 or 1.");
 -                      return -1;
 +                      return -EINVAL;
                }
 -              parsed_opts->mb_optimize_scan = arg;
 -      } else {
 -              if (!args->from)
 -                      arg = 1;
 +              ctx->mb_optimize_scan = result.int_32;
 +              return 0;
 +      }
 +
 +      /*
 +       * At this point we should only be getting options requiring MOPT_SET,
 +       * or MOPT_CLEAR. Anything else is a bug
 +       */
 +      if (m->token == Opt_err) {
 +              ext4_msg(NULL, KERN_WARNING, "buggy handling of option %s",
 +                       param->key);
 +              WARN_ON(1);
 +              return -EINVAL;
 +      }
 +
 +      else {
 +              unsigned int set = 0;
 +
 +              if ((param->type == fs_value_is_flag) ||
 +                  result.uint_32 > 0)
 +                      set = 1;
 +
                if (m->flags & MOPT_CLEAR)
 -                      arg = !arg;
 +                      set = !set;
                else if (unlikely(!(m->flags & MOPT_SET))) {
 -                      ext4_msg(sb, KERN_WARNING,
 -                               "buggy handling of option %s", opt);
 +                      ext4_msg(NULL, KERN_WARNING,
 +                               "buggy handling of option %s",
 +                               param->key);
                        WARN_ON(1);
 -                      return -1;
 +                      return -EINVAL;
                }
                if (m->flags & MOPT_2) {
 -                      if (arg != 0)
 -                              sbi->s_mount_opt2 |= m->mount_opt;
 +                      if (set != 0)
 +                              ctx_set_mount_opt2(ctx, m->mount_opt);
                        else
 -                              sbi->s_mount_opt2 &= ~m->mount_opt;
 +                              ctx_clear_mount_opt2(ctx, m->mount_opt);
                } else {
 -                      if (arg != 0)
 -                              sbi->s_mount_opt |= m->mount_opt;
 +                      if (set != 0)
 +                              ctx_set_mount_opt(ctx, m->mount_opt);
                        else
 -                              sbi->s_mount_opt &= ~m->mount_opt;
 +                              ctx_clear_mount_opt(ctx, m->mount_opt);
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +static int parse_options(struct fs_context *fc, char *options)
 +{
 +      struct fs_parameter param;
 +      int ret;
 +      char *key;
 +
 +      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 = ext4_parse_param(fc, &param);
 +                      if (param.string)
 +                              kfree(param.string);
 +                      if (ret < 0)
 +                              return ret;
 +              }
 +      }
 +
 +      ret = ext4_validate_options(fc);
 +      if (ret < 0)
 +              return ret;
 +
 +      return 0;
 +}
 +
 +static int parse_apply_sb_mount_options(struct super_block *sb,
 +                                      struct ext4_fs_context *m_ctx)
 +{
 +      struct ext4_sb_info *sbi = EXT4_SB(sb);
 +      char *s_mount_opts = NULL;
 +      struct ext4_fs_context *s_ctx = NULL;
 +      struct fs_context *fc = NULL;
 +      int ret = -ENOMEM;
 +
 +      if (!sbi->s_es->s_mount_opts[0])
 +              return 0;
 +
 +      s_mount_opts = kstrndup(sbi->s_es->s_mount_opts,
 +                              sizeof(sbi->s_es->s_mount_opts),
 +                              GFP_KERNEL);
 +      if (!s_mount_opts)
 +              return ret;
 +
 +      fc = kzalloc(sizeof(struct fs_context), GFP_KERNEL);
 +      if (!fc)
 +              goto out_free;
 +
 +      s_ctx = kzalloc(sizeof(struct ext4_fs_context), GFP_KERNEL);
 +      if (!s_ctx)
 +              goto out_free;
 +
 +      fc->fs_private = s_ctx;
 +      fc->s_fs_info = sbi;
 +
 +      ret = parse_options(fc, s_mount_opts);
 +      if (ret < 0)
 +              goto parse_failed;
 +
 +      ret = ext4_check_opt_consistency(fc, sb);
 +      if (ret < 0) {
 +parse_failed:
 +              ext4_msg(sb, KERN_WARNING,
 +                       "failed to parse options in superblock: %s",
 +                       s_mount_opts);
 +              ret = 0;
 +              goto out_free;
 +      }
 +
 +      if (s_ctx->spec & EXT4_SPEC_JOURNAL_DEV)
 +              m_ctx->journal_devnum = s_ctx->journal_devnum;
 +      if (s_ctx->spec & EXT4_SPEC_JOURNAL_IOPRIO)
 +              m_ctx->journal_ioprio = s_ctx->journal_ioprio;
 +
 +      ret = ext4_apply_options(fc, sb);
 +
 +out_free:
 +      kfree(s_ctx);
 +      kfree(fc);
 +      kfree(s_mount_opts);
 +      return ret;
 +}
 +
 +static void ext4_apply_quota_options(struct fs_context *fc,
 +                                   struct super_block *sb)
 +{
 +#ifdef CONFIG_QUOTA
 +      bool quota_feature = ext4_has_feature_quota(sb);
 +      struct ext4_fs_context *ctx = fc->fs_private;
 +      struct ext4_sb_info *sbi = EXT4_SB(sb);
 +      char *qname;
 +      int i;
 +
 +      if (quota_feature)
 +              return;
 +
 +      if (ctx->spec & EXT4_SPEC_JQUOTA) {
 +              for (i = 0; i < EXT4_MAXQUOTAS; i++) {
 +                      if (!(ctx->qname_spec & (1 << i)))
 +                              continue;
 +
 +                      qname = ctx->s_qf_names[i]; /* May be NULL */
 +                      if (qname)
 +                              set_opt(sb, QUOTA);
 +                      ctx->s_qf_names[i] = NULL;
 +                      qname = rcu_replace_pointer(sbi->s_qf_names[i], qname,
 +                                              lockdep_is_held(&sb->s_umount));
 +                      if (qname)
 +                              kfree_rcu(qname);
                }
        }
 -      return 1;
 +
 +      if (ctx->spec & EXT4_SPEC_JQFMT)
 +              sbi->s_jquota_fmt = ctx->s_jquota_fmt;
 +#endif
  }
  
 -static int parse_options(char *options, struct super_block *sb,
 -                       struct ext4_parsed_options *ret_opts,
 -                       int is_remount)
 +/*
 + * Check quota settings consistency.
 + */
 +static int ext4_check_quota_consistency(struct fs_context *fc,
 +                                      struct super_block *sb)
  {
 -      struct ext4_sb_info __maybe_unused *sbi = EXT4_SB(sb);
 -      char *p, __maybe_unused *usr_qf_name, __maybe_unused *grp_qf_name;
 -      substring_t args[MAX_OPT_ARGS];
 -      int token;
 -
 -      if (!options)
 -              return 1;
 -
 -      while ((p = strsep(&options, ",")) != NULL) {
 -              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, tokens, args);
 -              if (handle_mount_opt(sb, p, token, args, ret_opts,
 -                                   is_remount) < 0)
 -                      return 0;
 -      }
  #ifdef CONFIG_QUOTA
 +      struct ext4_fs_context *ctx = fc->fs_private;
 +      struct ext4_sb_info *sbi = EXT4_SB(sb);
 +      bool quota_feature = ext4_has_feature_quota(sb);
 +      bool quota_loaded = sb_any_quota_loaded(sb);
 +      bool usr_qf_name, grp_qf_name, usrquota, grpquota;
 +      int quota_flags, 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 (test_opt(sb, PRJQUOTA) && !ext4_has_feature_project(sb)) {
 -              ext4_msg(sb, KERN_ERR, "Project quota feature not enabled. "
 +      if (ctx_test_mount_opt(ctx, EXT4_MOUNT_PRJQUOTA) &&
 +          !ext4_has_feature_project(sb)) {
 +              ext4_msg(NULL, KERN_ERR, "Project quota feature not enabled. "
                         "Cannot enable project quota enforcement.");
 -              return 0;
 +              return -EINVAL;
        }
 -      usr_qf_name = get_qf_name(sb, sbi, USRQUOTA);
 -      grp_qf_name = get_qf_name(sb, sbi, GRPQUOTA);
 -      if (usr_qf_name || grp_qf_name) {
 -              if (test_opt(sb, USRQUOTA) && usr_qf_name)
 -                      clear_opt(sb, USRQUOTA);
  
 -              if (test_opt(sb, GRPQUOTA) && grp_qf_name)
 -                      clear_opt(sb, GRPQUOTA);
 +      quota_flags = EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA |
 +                    EXT4_MOUNT_GRPQUOTA | EXT4_MOUNT_PRJQUOTA;
 +      if (quota_loaded &&
 +          ctx->mask_s_mount_opt & quota_flags &&
 +          !ctx_test_mount_opt(ctx, quota_flags))
 +              goto err_quota_change;
 +
 +      if (ctx->spec & EXT4_SPEC_JQUOTA) {
 +
 +              for (i = 0; i < EXT4_MAXQUOTAS; i++) {
 +                      if (!(ctx->qname_spec & (1 << i)))
 +                              continue;
 +
 +                      if (quota_loaded &&
 +                          !!sbi->s_qf_names[i] != !!ctx->s_qf_names[i])
 +                              goto err_jquota_change;
 +
 +                      if (sbi->s_qf_names[i] && ctx->s_qf_names[i] &&
 +                          strcmp(get_qf_name(sb, sbi, i),
 +                                 ctx->s_qf_names[i]) != 0)
 +                              goto err_jquota_specified;
 +              }
  
 -              if (test_opt(sb, GRPQUOTA) || test_opt(sb, USRQUOTA)) {
 -                      ext4_msg(sb, KERN_ERR, "old and new quota "
 -                                      "format mixing");
 +              if (quota_feature) {
 +                      ext4_msg(NULL, KERN_INFO,
 +                               "Journaled quota options ignored when "
 +                               "QUOTA feature is enabled");
                        return 0;
                }
 +      }
  
 -              if (!sbi->s_jquota_fmt) {
 -                      ext4_msg(sb, KERN_ERR, "journaled quota format "
 -                                      "not specified");
 +      if (ctx->spec & EXT4_SPEC_JQFMT) {
 +              if (sbi->s_jquota_fmt != ctx->s_jquota_fmt && quota_loaded)
 +                      goto err_jquota_change;
 +              if (quota_feature) {
 +                      ext4_msg(NULL, KERN_INFO, "Quota format mount options "
 +                               "ignored when QUOTA feature is enabled");
                        return 0;
                }
        }
 +
 +      /* Make sure we don't mix old and new quota format */
 +      usr_qf_name = (get_qf_name(sb, sbi, USRQUOTA) ||
 +                     ctx->s_qf_names[USRQUOTA]);
 +      grp_qf_name = (get_qf_name(sb, sbi, GRPQUOTA) ||
 +                     ctx->s_qf_names[GRPQUOTA]);
 +
 +      usrquota = (ctx_test_mount_opt(ctx, EXT4_MOUNT_USRQUOTA) ||
 +                  test_opt(sb, USRQUOTA));
 +
 +      grpquota = (ctx_test_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA) ||
 +                  test_opt(sb, GRPQUOTA));
 +
 +      if (usr_qf_name) {
 +              ctx_clear_mount_opt(ctx, EXT4_MOUNT_USRQUOTA);
 +              usrquota = false;
 +      }
 +      if (grp_qf_name) {
 +              ctx_clear_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA);
 +              grpquota = false;
 +      }
 +
 +      if (usr_qf_name || grp_qf_name) {
 +              if (usrquota || grpquota) {
 +                      ext4_msg(NULL, KERN_ERR, "old and new quota "
 +                               "format mixing");
 +                      return -EINVAL;
 +              }
 +
 +              if (!(ctx->spec & EXT4_SPEC_JQFMT || sbi->s_jquota_fmt)) {
 +                      ext4_msg(NULL, KERN_ERR, "journaled quota format "
 +                               "not specified");
 +                      return -EINVAL;
 +              }
 +      }
 +
 +      return 0;
 +
 +err_quota_change:
 +      ext4_msg(NULL, KERN_ERR,
 +               "Cannot change quota options when quota turned on");
 +      return -EINVAL;
 +err_jquota_change:
 +      ext4_msg(NULL, KERN_ERR, "Cannot change journaled quota "
 +               "options when quota turned on");
 +      return -EINVAL;
 +err_jquota_specified:
 +      ext4_msg(NULL, KERN_ERR, "%s quota file already specified",
 +               QTYPE2NAME(i));
 +      return -EINVAL;
 +#else
 +      return 0;
  #endif
 -      if (test_opt(sb, DIOREAD_NOLOCK)) {
 +}
 +
 +static int ext4_check_opt_consistency(struct fs_context *fc,
 +                                    struct super_block *sb)
 +{
 +      struct ext4_fs_context *ctx = fc->fs_private;
 +      struct ext4_sb_info *sbi = fc->s_fs_info;
 +      int is_remount = fc->purpose == FS_CONTEXT_FOR_RECONFIGURE;
 +
 +      if ((ctx->opt_flags & MOPT_NO_EXT2) && IS_EXT2_SB(sb)) {
 +              ext4_msg(NULL, KERN_ERR,
 +                       "Mount option(s) incompatible with ext2");
 +              return -EINVAL;
 +      }
 +      if ((ctx->opt_flags & MOPT_NO_EXT3) && IS_EXT3_SB(sb)) {
 +              ext4_msg(NULL, KERN_ERR,
 +                       "Mount option(s) incompatible with ext3");
 +              return -EINVAL;
 +      }
 +
 +      if (ctx->s_want_extra_isize >
 +          (sbi->s_inode_size - EXT4_GOOD_OLD_INODE_SIZE)) {
 +              ext4_msg(NULL, KERN_ERR,
 +                       "Invalid want_extra_isize %d",
 +                       ctx->s_want_extra_isize);
 +              return -EINVAL;
 +      }
 +
 +      if (ctx_test_mount_opt(ctx, EXT4_MOUNT_DIOREAD_NOLOCK)) {
                int blocksize =
                        BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);
                if (blocksize < PAGE_SIZE)
 -                      ext4_msg(sb, KERN_WARNING, "Warning: mounting with an "
 +                      ext4_msg(NULL, KERN_WARNING, "Warning: mounting with an "
                                 "experimental mount option 'dioread_nolock' "
                                 "for blocksize < PAGE_SIZE");
        }
 +
 +#ifdef CONFIG_FS_ENCRYPTION
 +      /*
 +       * 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 ((ctx->spec & EXT4_SPEC_DUMMY_ENCRYPTION) &&
 +          is_remount && !sbi->s_dummy_enc_policy.policy) {
 +              ext4_msg(NULL, KERN_WARNING,
 +                       "Can't set test_dummy_encryption on remount");
 +              return -1;
 +      }
 +#endif
 +
 +      if ((ctx->spec & EXT4_SPEC_DATAJ) && is_remount) {
 +              if (!sbi->s_journal) {
 +                      ext4_msg(NULL, KERN_WARNING,
 +                               "Remounting file system with no journal "
 +                               "so ignoring journalled data option");
 +                      ctx_clear_mount_opt(ctx, EXT4_MOUNT_DATA_FLAGS);
 +              } else if (ctx_test_mount_opt(ctx, EXT4_MOUNT_DATA_FLAGS) !=
 +                         test_opt(sb, DATA_FLAGS)) {
 +                      ext4_msg(NULL, KERN_ERR, "Cannot change data mode "
 +                               "on remount");
 +                      return -EINVAL;
 +              }
 +      }
 +
 +      if (is_remount) {
 +              if (ctx_test_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS) &&
 +                  (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)) {
 +                      ext4_msg(NULL, KERN_ERR, "can't mount with "
 +                               "both data=journal and dax");
 +                      return -EINVAL;
 +              }
 +
 +              if (ctx_test_mount_opt(ctx, EXT4_MOUNT_DAX_ALWAYS) &&
 +                  (!(sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) ||
 +                   (sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER))) {
 +fail_dax_change_remount:
 +                      ext4_msg(NULL, KERN_ERR, "can't change "
 +                               "dax mount option while remounting");
 +                      return -EINVAL;
 +              } else if (ctx_test_mount_opt2(ctx, EXT4_MOUNT2_DAX_NEVER) &&
 +                       (!(sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER) ||
 +                        (sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS))) {
 +                      goto fail_dax_change_remount;
 +              } else if (ctx_test_mount_opt2(ctx, EXT4_MOUNT2_DAX_INODE) &&
 +                         ((sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) ||
 +                          (sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_NEVER) ||
 +                          !(sbi->s_mount_opt2 & EXT4_MOUNT2_DAX_INODE))) {
 +                      goto fail_dax_change_remount;
 +              }
 +      }
 +
 +      return ext4_check_quota_consistency(fc, sb);
 +}
 +
 +static int ext4_apply_options(struct fs_context *fc, struct super_block *sb)
 +{
 +      struct ext4_fs_context *ctx = fc->fs_private;
 +      struct ext4_sb_info *sbi = fc->s_fs_info;
 +      int ret = 0;
 +
 +      sbi->s_mount_opt &= ~ctx->mask_s_mount_opt;
 +      sbi->s_mount_opt |= ctx->vals_s_mount_opt;
 +      sbi->s_mount_opt2 &= ~ctx->mask_s_mount_opt2;
 +      sbi->s_mount_opt2 |= ctx->vals_s_mount_opt2;
 +      sbi->s_mount_flags &= ~ctx->mask_s_mount_flags;
 +      sbi->s_mount_flags |= ctx->vals_s_mount_flags;
 +      sb->s_flags &= ~ctx->mask_s_flags;
 +      sb->s_flags |= ctx->vals_s_flags;
 +
 +      /*
 +       * i_version differs from common mount option iversion so we have
 +       * to let vfs know that it was set, otherwise it would get cleared
 +       * on remount
 +       */
 +      if (ctx->mask_s_flags & SB_I_VERSION)
 +              fc->sb_flags |= SB_I_VERSION;
 +
 +#define APPLY(X) ({ if (ctx->spec & EXT4_SPEC_##X) sbi->X = ctx->X; })
 +      APPLY(s_commit_interval);
 +      APPLY(s_stripe);
 +      APPLY(s_max_batch_time);
 +      APPLY(s_min_batch_time);
 +      APPLY(s_want_extra_isize);
 +      APPLY(s_inode_readahead_blks);
 +      APPLY(s_max_dir_size_kb);
 +      APPLY(s_li_wait_mult);
 +      APPLY(s_resgid);
 +      APPLY(s_resuid);
 +
 +#ifdef CONFIG_EXT4_DEBUG
 +      APPLY(s_fc_debug_max_replay);
 +#endif
 +
 +      ext4_apply_quota_options(fc, sb);
 +
 +      if (ctx->spec & EXT4_SPEC_DUMMY_ENCRYPTION)
 +              ret = ext4_set_test_dummy_encryption(sb, ctx->test_dummy_enc_arg);
 +
 +      return ret;
 +}
 +
 +
 +static int ext4_validate_options(struct fs_context *fc)
 +{
 +#ifdef CONFIG_QUOTA
 +      struct ext4_fs_context *ctx = fc->fs_private;
 +      char *usr_qf_name, *grp_qf_name;
 +
 +      usr_qf_name = ctx->s_qf_names[USRQUOTA];
 +      grp_qf_name = ctx->s_qf_names[GRPQUOTA];
 +
 +      if (usr_qf_name || grp_qf_name) {
 +              if (ctx_test_mount_opt(ctx, EXT4_MOUNT_USRQUOTA) && usr_qf_name)
 +                      ctx_clear_mount_opt(ctx, EXT4_MOUNT_USRQUOTA);
 +
 +              if (ctx_test_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA) && grp_qf_name)
 +                      ctx_clear_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA);
 +
 +              if (ctx_test_mount_opt(ctx, EXT4_MOUNT_USRQUOTA) ||
 +                  ctx_test_mount_opt(ctx, EXT4_MOUNT_GRPQUOTA)) {
 +                      ext4_msg(NULL, KERN_ERR, "old and new quota "
 +                               "format mixing");
 +                      return -EINVAL;
 +              }
 +      }
 +#endif
        return 1;
  }
  
@@@ -2977,12 -2519,12 +2970,12 @@@ static inline void ext4_show_quota_opti
  
  static const char *token2str(int token)
  {
 -      const struct match_token *t;
 +      const struct fs_parameter_spec *spec;
  
 -      for (t = tokens; t->token != Opt_err; t++)
 -              if (t->token == token && !strchr(t->pattern, '='))
 +      for (spec = ext4_param_specs; spec->name != NULL; spec++)
 +              if (spec->opt == token && !spec->type)
                        break;
 -      return t->pattern;
 +      return spec->name;
  }
  
  /*
@@@ -3008,7 -2550,7 +3001,7 @@@ static int _ext4_show_options(struct se
        for (m = ext4_mount_opts; m->token != Opt_err; m++) {
                int want_set = m->flags & MOPT_SET;
                if (((m->flags & (MOPT_SET|MOPT_CLEAR)) == 0) ||
 -                  (m->flags & MOPT_CLEAR_ERR) || m->flags & MOPT_SKIP)
 +                  m->flags & MOPT_SKIP)
                        continue;
                if (!nodefs && !(m->mount_opt & (sbi->s_mount_opt ^ def_mount_opt)))
                        continue; /* skip if same as the default */
@@@ -3714,9 -3256,9 +3707,9 @@@ static int ext4_run_li_request(struct e
        struct super_block *sb = elr->lr_super;
        ext4_group_t ngroups = EXT4_SB(sb)->s_groups_count;
        ext4_group_t group = elr->lr_next_group;
 -      unsigned long timeout = 0;
        unsigned int prefetch_ios = 0;
        int ret = 0;
 +      u64 start_time;
  
        if (elr->lr_mode == EXT4_LI_MODE_PREFETCH_BBITMAP) {
                elr->lr_next_group = ext4_mb_prefetch(sb, group,
                ret = 1;
  
        if (!ret) {
 -              timeout = jiffies;
 +              start_time = ktime_get_real_ns();
                ret = ext4_init_inode_table(sb, group,
                                            elr->lr_timeout ? 0 : 1);
                trace_ext4_lazy_itable_init(sb, group);
                if (elr->lr_timeout == 0) {
 -                      timeout = (jiffies - timeout) *
 -                              EXT4_SB(elr->lr_super)->s_li_wait_mult;
 -                      elr->lr_timeout = timeout;
 +                      elr->lr_timeout = nsecs_to_jiffies((ktime_get_real_ns() - start_time) *
 +                              EXT4_SB(elr->lr_super)->s_li_wait_mult);
                }
                elr->lr_next_sched = jiffies + elr->lr_timeout;
                elr->lr_next_group = group + 1;
@@@ -4320,52 -3863,21 +4313,52 @@@ static void ext4_setup_csum_trigger(str
        sbi->s_journal_triggers[type].tr_triggers.t_frozen = trigger;
  }
  
 -static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 +static void ext4_free_sbi(struct ext4_sb_info *sbi)
 +{
 +      if (!sbi)
 +              return;
 +
 +      kfree(sbi->s_blockgroup_lock);
 +      fs_put_dax(sbi->s_daxdev);
 +      kfree(sbi);
 +}
 +
 +static struct ext4_sb_info *ext4_alloc_sbi(struct super_block *sb)
 +{
 +      struct ext4_sb_info *sbi;
 +
 +      sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
 +      if (!sbi)
 +              return NULL;
 +
 +      sbi->s_daxdev = fs_dax_get_by_bdev(sb->s_bdev, &sbi->s_dax_part_off);
 +
 +      sbi->s_blockgroup_lock =
 +              kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL);
 +
 +      if (!sbi->s_blockgroup_lock)
 +              goto err_out;
 +
 +      sb->s_fs_info = sbi;
 +      sbi->s_sb = sb;
 +      return sbi;
 +err_out:
 +      fs_put_dax(sbi->s_daxdev);
 +      kfree(sbi);
 +      return NULL;
 +}
 +
 +static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
  {
 -      struct dax_device *dax_dev = fs_dax_get_by_bdev(sb->s_bdev);
 -      char *orig_data = kstrdup(data, GFP_KERNEL);
        struct buffer_head *bh, **group_desc;
        struct ext4_super_block *es = NULL;
 -      struct ext4_sb_info *sbi = kzalloc(sizeof(*sbi), GFP_KERNEL);
 +      struct ext4_sb_info *sbi = EXT4_SB(sb);
        struct flex_groups **flex_groups;
        ext4_fsblk_t block;
 -      ext4_fsblk_t sb_block = get_sb_block(&data);
        ext4_fsblk_t logical_sb_block;
        unsigned long offset = 0;
        unsigned long def_mount_opts;
        struct inode *root;
 -      const char *descr;
        int ret = -ENOMEM;
        int blocksize, clustersize;
        unsigned int db_count;
        __u64 blocks_count;
        int err = 0;
        ext4_group_t first_not_zeroed;
 -      struct ext4_parsed_options parsed_opts;
 +      struct ext4_fs_context *ctx = fc->fs_private;
 +      int silent = fc->sb_flags & SB_SILENT;
  
        /* Set defaults for the variables that will be set during parsing */
 -      parsed_opts.journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
 -      parsed_opts.journal_devnum = 0;
 -      parsed_opts.mb_optimize_scan = DEFAULT_MB_OPTIMIZE_SCAN;
 +      ctx->journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
 +      ctx->mb_optimize_scan = DEFAULT_MB_OPTIMIZE_SCAN;
  
 -      if ((data && !orig_data) || !sbi)
 -              goto out_free_base;
 -
 -      sbi->s_daxdev = dax_dev;
 -      sbi->s_blockgroup_lock =
 -              kzalloc(sizeof(struct blockgroup_lock), GFP_KERNEL);
 -      if (!sbi->s_blockgroup_lock)
 -              goto out_free_base;
 -
 -      sb->s_fs_info = sbi;
 -      sbi->s_sb = sb;
        sbi->s_inode_readahead_blks = EXT4_DEF_INODE_READAHEAD_BLKS;
 -      sbi->s_sb_block = sb_block;
        sbi->s_sectors_written_start =
                part_stat_read(sb->s_bdev, sectors[STAT_WRITE]);
  
 -      /* Cleanup superblock name */
 -      strreplace(sb->s_id, '/', '!');
 -
        /* -EINVAL is default */
        ret = -EINVAL;
        blocksize = sb_min_blocksize(sb, EXT4_MIN_BLOCK_SIZE);
         * block sizes.  We need to calculate the offset from buffer start.
         */
        if (blocksize != EXT4_MIN_BLOCK_SIZE) {
 -              logical_sb_block = sb_block * EXT4_MIN_BLOCK_SIZE;
 +              logical_sb_block = sbi->s_sb_block * EXT4_MIN_BLOCK_SIZE;
                offset = do_div(logical_sb_block, blocksize);
        } else {
 -              logical_sb_block = sb_block;
 +              logical_sb_block = sbi->s_sb_block;
        }
  
        bh = ext4_sb_bread_unmovable(sb, logical_sb_block);
                }
        }
  
 -      if (sbi->s_es->s_mount_opts[0]) {
 -              char *s_mount_opts = kstrndup(sbi->s_es->s_mount_opts,
 -                                            sizeof(sbi->s_es->s_mount_opts),
 -                                            GFP_KERNEL);
 -              if (!s_mount_opts)
 -                      goto failed_mount;
 -              if (!parse_options(s_mount_opts, sb, &parsed_opts, 0)) {
 -                      ext4_msg(sb, KERN_WARNING,
 -                               "failed to parse options in superblock: %s",
 -                               s_mount_opts);
 -              }
 -              kfree(s_mount_opts);
 -      }
 +      err = parse_apply_sb_mount_options(sb, ctx);
 +      if (err < 0)
 +              goto failed_mount;
 +
        sbi->s_def_mount_opt = sbi->s_mount_opt;
 -      if (!parse_options((char *) data, sb, &parsed_opts, 0))
 +
 +      err = ext4_check_opt_consistency(fc, sb);
 +      if (err < 0)
 +              goto failed_mount;
 +
 +      err = ext4_apply_options(fc, sb);
 +      if (err < 0)
                goto failed_mount;
  
  #ifdef CONFIG_UNICODE
        if (ext4_has_feature_casefold(sb) && !sb->s_encoding) {
                const struct ext4_sb_encodings *encoding_info;
                struct unicode_map *encoding;
-               __u16 encoding_flags;
+               __u16 encoding_flags = le16_to_cpu(es->s_encoding_flags);
  
-               if (ext4_sb_read_encoding(es, &encoding_info,
-                                         &encoding_flags)) {
+               encoding_info = ext4_sb_read_encoding(es);
+               if (!encoding_info) {
                        ext4_msg(sb, KERN_ERR,
                                 "Encoding requested by superblock is unknown");
                        goto failed_mount;
                encoding = utf8_load(encoding_info->version);
                if (IS_ERR(encoding)) {
                        ext4_msg(sb, KERN_ERR,
-                                "can't mount with superblock charset: %s-%s "
+                                "can't mount with superblock charset: %s-%u.%u.%u "
                                 "not supported by the kernel. flags: 0x%x.",
-                                encoding_info->name, encoding_info->version,
+                                encoding_info->name,
+                                unicode_major(encoding_info->version),
+                                unicode_minor(encoding_info->version),
+                                unicode_rev(encoding_info->version),
                                 encoding_flags);
                        goto failed_mount;
                }
                ext4_msg(sb, KERN_INFO,"Using encoding defined by superblock: "
-                        "%s-%s with flags 0x%hx", encoding_info->name,
-                        encoding_info->version?:"\b", encoding_flags);
+                        "%s-%u.%u.%u with flags 0x%hx", encoding_info->name,
+                        unicode_major(encoding_info->version),
+                        unicode_minor(encoding_info->version),
+                        unicode_rev(encoding_info->version),
+                        encoding_flags);
  
                sb->s_encoding = encoding;
                sb->s_encoding_flags = encoding_flags;
                goto failed_mount;
        }
  
 -      if (dax_supported(dax_dev, sb->s_bdev, blocksize, 0,
 -                      bdev_nr_sectors(sb->s_bdev)))
 -              set_bit(EXT4_FLAGS_BDEV_IS_DAX, &sbi->s_ext4_flags);
 +      if (sbi->s_daxdev) {
 +              if (blocksize == PAGE_SIZE)
 +                      set_bit(EXT4_FLAGS_BDEV_IS_DAX, &sbi->s_ext4_flags);
 +              else
 +                      ext4_msg(sb, KERN_ERR, "unsupported blocksize for DAX\n");
 +      }
  
        if (sbi->s_mount_opt & EXT4_MOUNT_DAX_ALWAYS) {
                if (ext4_has_feature_inline_data(sb)) {
                        goto failed_mount;
                }
  
 -              logical_sb_block = sb_block * EXT4_MIN_BLOCK_SIZE;
 +              logical_sb_block = sbi->s_sb_block * EXT4_MIN_BLOCK_SIZE;
                offset = do_div(logical_sb_block, blocksize);
                bh = ext4_sb_bread_unmovable(sb, logical_sb_block);
                if (IS_ERR(bh)) {
                goto cantfind_ext4;
  
        /* check blocks count against device size */
 -      blocks_count = sb->s_bdev->bd_inode->i_size >> sb->s_blocksize_bits;
 +      blocks_count = sb_bdev_nr_blocks(sb);
        if (blocks_count && ext4_blocks_count(es) > blocks_count) {
                ext4_msg(sb, KERN_WARNING, "bad geometry: block count %llu "
                       "exceeds size of device (%llu blocks)",
  
        /* Initialize fast commit stuff */
        atomic_set(&sbi->s_fc_subtid, 0);
 -      atomic_set(&sbi->s_fc_ineligible_updates, 0);
        INIT_LIST_HEAD(&sbi->s_fc_q[FC_Q_MAIN]);
        INIT_LIST_HEAD(&sbi->s_fc_q[FC_Q_STAGING]);
        INIT_LIST_HEAD(&sbi->s_fc_dentry_q[FC_Q_MAIN]);
         * root first: it may be modified in the journal!
         */
        if (!test_opt(sb, NOLOAD) && ext4_has_feature_journal(sb)) {
 -              err = ext4_load_journal(sb, es, parsed_opts.journal_devnum);
 +              err = ext4_load_journal(sb, es, ctx->journal_devnum);
                if (err)
                        goto failed_mount3a;
        } else if (test_opt(sb, NOLOAD) && !sb_rdonly(sb) &&
                goto failed_mount_wq;
        }
  
 -      set_task_ioprio(sbi->s_journal->j_task, parsed_opts.journal_ioprio);
 +      set_task_ioprio(sbi->s_journal->j_task, ctx->journal_ioprio);
  
        sbi->s_journal->j_submit_inode_data_buffers =
                ext4_journal_submit_inode_data_buffers;
@@@ -5324,9 -4858,9 +5323,9 @@@ no_journal
         * turned off by passing "mb_optimize_scan=0". This can also be
         * turned on forcefully by passing "mb_optimize_scan=1".
         */
 -      if (parsed_opts.mb_optimize_scan == 1)
 +      if (ctx->mb_optimize_scan == 1)
                set_opt2(sb, MB_OPTIMIZE_SCAN);
 -      else if (parsed_opts.mb_optimize_scan == 0)
 +      else if (ctx->mb_optimize_scan == 0)
                clear_opt2(sb, MB_OPTIMIZE_SCAN);
        else if (sbi->s_groups_count >= MB_DEFAULT_LINEAR_SCAN_THRESHOLD)
                set_opt2(sb, MB_OPTIMIZE_SCAN);
                if (err)
                        goto failed_mount9;
        }
 -      if (EXT4_SB(sb)->s_journal) {
 -              if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)
 -                      descr = " journalled data mode";
 -              else if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA)
 -                      descr = " ordered data mode";
 -              else
 -                      descr = " writeback data mode";
 -      } else
 -              descr = "out journal";
  
        if (test_opt(sb, DISCARD)) {
                struct request_queue *q = bdev_get_queue(sb->s_bdev);
                                 "the device does not support discard");
        }
  
 -      if (___ratelimit(&ext4_mount_msg_ratelimit, "EXT4-fs mount"))
 -              ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
 -                       "Opts: %.*s%s%s. Quota mode: %s.", descr,
 -                       (int) sizeof(sbi->s_es->s_mount_opts),
 -                       sbi->s_es->s_mount_opts,
 -                       *sbi->s_es->s_mount_opts ? "; " : "", orig_data,
 -                       ext4_quota_mode(sb));
 -
        if (es->s_error_count)
                mod_timer(&sbi->s_err_report, jiffies + 300*HZ); /* 5 minutes */
  
        atomic_set(&sbi->s_warning_count, 0);
        atomic_set(&sbi->s_msg_count, 0);
  
 -      kfree(orig_data);
        return 0;
  
  cantfind_ext4:
@@@ -5532,60 -5084,14 +5531,60 @@@ failed_mount
        ext4_blkdev_remove(sbi);
  out_fail:
        sb->s_fs_info = NULL;
 -      kfree(sbi->s_blockgroup_lock);
 -out_free_base:
 -      kfree(sbi);
 -      kfree(orig_data);
 -      fs_put_dax(dax_dev);
        return err ? err : ret;
  }
  
 +static int ext4_fill_super(struct super_block *sb, struct fs_context *fc)
 +{
 +      struct ext4_fs_context *ctx = fc->fs_private;
 +      struct ext4_sb_info *sbi;
 +      const char *descr;
 +      int ret;
 +
 +      sbi = ext4_alloc_sbi(sb);
 +      if (!sbi)
 +              ret = -ENOMEM;
 +
 +      fc->s_fs_info = sbi;
 +
 +      /* Cleanup superblock name */
 +      strreplace(sb->s_id, '/', '!');
 +
 +      sbi->s_sb_block = 1;    /* Default super block location */
 +      if (ctx->spec & EXT4_SPEC_s_sb_block)
 +              sbi->s_sb_block = ctx->s_sb_block;
 +
 +      ret = __ext4_fill_super(fc, sb);
 +      if (ret < 0)
 +              goto free_sbi;
 +
 +      if (sbi->s_journal) {
 +              if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)
 +                      descr = " journalled data mode";
 +              else if (test_opt(sb, DATA_FLAGS) == EXT4_MOUNT_ORDERED_DATA)
 +                      descr = " ordered data mode";
 +              else
 +                      descr = " writeback data mode";
 +      } else
 +              descr = "out journal";
 +
 +      if (___ratelimit(&ext4_mount_msg_ratelimit, "EXT4-fs mount"))
 +              ext4_msg(sb, KERN_INFO, "mounted filesystem with%s. "
 +                       "Quota mode: %s.", descr, ext4_quota_mode(sb));
 +
 +      return 0;
 +
 +free_sbi:
 +      ext4_free_sbi(sbi);
 +      fc->s_fs_info = NULL;
 +      return ret;
 +}
 +
 +static int ext4_get_tree(struct fs_context *fc)
 +{
 +      return get_tree_bdev(fc, ext4_fill_super);
 +}
 +
  /*
   * Setup any per-fs journal parameters now.  We'll do this both on
   * initial mount, once the journal has been initialised but before we've
@@@ -6214,22 -5720,27 +6213,22 @@@ struct ext4_mount_options 
  #endif
  };
  
 -static int ext4_remount(struct super_block *sb, int *flags, char *data)
 +static int __ext4_remount(struct fs_context *fc, struct super_block *sb)
  {
 +      struct ext4_fs_context *ctx = fc->fs_private;
        struct ext4_super_block *es;
        struct ext4_sb_info *sbi = EXT4_SB(sb);
 -      unsigned long old_sb_flags, vfs_flags;
 +      unsigned long old_sb_flags;
        struct ext4_mount_options old_opts;
 -      int enable_quota = 0;
        ext4_group_t g;
        int err = 0;
  #ifdef CONFIG_QUOTA
 +      int enable_quota = 0;
        int i, j;
        char *to_free[EXT4_MAXQUOTAS];
  #endif
 -      char *orig_data = kstrdup(data, GFP_KERNEL);
 -      struct ext4_parsed_options parsed_opts;
 -
 -      parsed_opts.journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
 -      parsed_opts.journal_devnum = 0;
  
 -      if (data && !orig_data)
 -              return -ENOMEM;
 +      ctx->journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
  
        /* Store the original options */
        old_sb_flags = sb->s_flags;
                        if (!old_opts.s_qf_names[i]) {
                                for (j = 0; j < i; j++)
                                        kfree(old_opts.s_qf_names[j]);
 -                              kfree(orig_data);
                                return -ENOMEM;
                        }
                } else
                        old_opts.s_qf_names[i] = NULL;
  #endif
        if (sbi->s_journal && sbi->s_journal->j_task->io_context)
 -              parsed_opts.journal_ioprio =
 +              ctx->journal_ioprio =
                        sbi->s_journal->j_task->io_context->ioprio;
  
 -      /*
 -       * Some options can be enabled by ext4 and/or by VFS mount flag
 -       * either way we need to make sure it matches in both *flags and
 -       * s_flags. Copy those selected flags from *flags to s_flags
 -       */
 -      vfs_flags = SB_LAZYTIME | SB_I_VERSION;
 -      sb->s_flags = (sb->s_flags & ~vfs_flags) | (*flags & vfs_flags);
 -
 -      if (!parse_options(data, sb, &parsed_opts, 1)) {
 -              err = -EINVAL;
 -              goto restore_opts;
 -      }
 +      ext4_apply_options(fc, sb);
  
        if ((old_opts.s_mount_opt & EXT4_MOUNT_JOURNAL_CHECKSUM) ^
            test_opt(sb, JOURNAL_CHECKSUM)) {
        }
  
        if (ext4_test_mount_flag(sb, EXT4_MF_FS_ABORTED))
 -              ext4_abort(sb, EXT4_ERR_ESHUTDOWN, "Abort forced by user");
 +              ext4_abort(sb, ESHUTDOWN, "Abort forced by user");
  
        sb->s_flags = (sb->s_flags & ~SB_POSIXACL) |
                (test_opt(sb, POSIX_ACL) ? SB_POSIXACL : 0);
  
        if (sbi->s_journal) {
                ext4_init_journal_params(sb, sbi->s_journal);
 -              set_task_ioprio(sbi->s_journal->j_task, parsed_opts.journal_ioprio);
 +              set_task_ioprio(sbi->s_journal->j_task, ctx->journal_ioprio);
        }
  
        /* Flush outstanding errors before changing fs state */
        flush_work(&sbi->s_error_work);
  
 -      if ((bool)(*flags & SB_RDONLY) != sb_rdonly(sb)) {
 +      if ((bool)(fc->sb_flags & SB_RDONLY) != sb_rdonly(sb)) {
                if (ext4_test_mount_flag(sb, EXT4_MF_FS_ABORTED)) {
                        err = -EROFS;
                        goto restore_opts;
                }
  
 -              if (*flags & SB_RDONLY) {
 +              if (fc->sb_flags & SB_RDONLY) {
                        err = sync_filesystem(sb);
                        if (err < 0)
                                goto restore_opts;
                                        err = -EROFS;
                                        goto restore_opts;
                                }
 +#ifdef CONFIG_QUOTA
                        enable_quota = 1;
 +#endif
                }
        }
  
        if (!ext4_has_feature_mmp(sb) || sb_rdonly(sb))
                ext4_stop_mmpd(sbi);
  
 -      /*
 -       * Some options can be enabled by ext4 and/or by VFS mount flag
 -       * either way we need to make sure it matches in both *flags and
 -       * s_flags. Copy those selected flags from s_flags to *flags
 -       */
 -      *flags = (*flags & ~vfs_flags) | (sb->s_flags & vfs_flags);
 -
 -      ext4_msg(sb, KERN_INFO, "re-mounted. Opts: %s. Quota mode: %s.",
 -               orig_data, ext4_quota_mode(sb));
 -      kfree(orig_data);
        return 0;
  
  restore_opts:
  #endif
        if (!ext4_has_feature_mmp(sb) || sb_rdonly(sb))
                ext4_stop_mmpd(sbi);
 -      kfree(orig_data);
        return err;
  }
  
 +static int ext4_reconfigure(struct fs_context *fc)
 +{
 +      struct super_block *sb = fc->root->d_sb;
 +      int ret;
 +
 +      fc->s_fs_info = EXT4_SB(sb);
 +
 +      ret = ext4_check_opt_consistency(fc, sb);
 +      if (ret < 0)
 +              return ret;
 +
 +      ret = __ext4_remount(fc, sb);
 +      if (ret < 0)
 +              return ret;
 +
 +      ext4_msg(sb, KERN_INFO, "re-mounted. Quota mode: %s.",
 +               ext4_quota_mode(sb));
 +
 +      return 0;
 +}
 +
  #ifdef CONFIG_QUOTA
  static int ext4_statfs_project(struct super_block *sb,
                               kprojid_t projid, struct kstatfs *buf)
@@@ -6755,7 -6266,10 +6754,7 @@@ static int ext4_quota_on(struct super_b
  
        lockdep_set_quota_inode(path->dentry->d_inode, I_DATA_SEM_QUOTA);
        err = dquot_quota_on(sb, type, format_id, path);
 -      if (err) {
 -              lockdep_set_quota_inode(path->dentry->d_inode,
 -                                           I_DATA_SEM_NORMAL);
 -      } else {
 +      if (!err) {
                struct inode *inode = d_inode(path->dentry);
                handle_t *handle;
  
                ext4_journal_stop(handle);
        unlock_inode:
                inode_unlock(inode);
 +              if (err)
 +                      dquot_quota_off(sb, type);
        }
 +      if (err)
 +              lockdep_set_quota_inode(path->dentry->d_inode,
 +                                           I_DATA_SEM_NORMAL);
        return err;
  }
  
@@@ -6843,19 -6352,8 +6842,19 @@@ int ext4_enable_quotas(struct super_blo
                                        "Failed to enable quota tracking "
                                        "(type=%d, err=%d). Please run "
                                        "e2fsck to fix.", type, err);
 -                              for (type--; type >= 0; type--)
 +                              for (type--; type >= 0; type--) {
 +                                      struct inode *inode;
 +
 +                                      inode = sb_dqopt(sb)->files[type];
 +                                      if (inode)
 +                                              inode = igrab(inode);
                                        dquot_quota_off(sb, type);
 +                                      if (inode) {
 +                                              lockdep_set_quota_inode(inode,
 +                                                      I_DATA_SEM_NORMAL);
 +                                              iput(inode);
 +                                      }
 +                              }
  
                                return err;
                        }
@@@ -6959,7 -6457,7 +6958,7 @@@ static ssize_t ext4_quota_write(struct 
        struct buffer_head *bh;
        handle_t *handle = journal_current_handle();
  
 -      if (EXT4_SB(sb)->s_journal && !handle) {
 +      if (!handle) {
                ext4_msg(sb, KERN_WARNING, "Quota write (off=%llu, len=%llu)"
                        " cancelled because transaction is not started",
                        (unsigned long long)off, (unsigned long long)len);
@@@ -7010,6 -6508,12 +7009,6 @@@ out
  }
  #endif
  
 -static struct dentry *ext4_mount(struct file_system_type *fs_type, int flags,
 -                     const char *dev_name, void *data)
 -{
 -      return mount_bdev(fs_type, flags, dev_name, data, ext4_fill_super);
 -}
 -
  #if !defined(CONFIG_EXT2_FS) && !defined(CONFIG_EXT2_FS_MODULE) && defined(CONFIG_EXT4_USE_FOR_EXT2)
  static inline void register_as_ext2(void)
  {
@@@ -7067,12 -6571,11 +7066,12 @@@ static inline int ext3_feature_set_ok(s
  }
  
  static struct file_system_type ext4_fs_type = {
 -      .owner          = THIS_MODULE,
 -      .name           = "ext4",
 -      .mount          = ext4_mount,
 -      .kill_sb        = kill_block_super,
 -      .fs_flags       = FS_REQUIRES_DEV | FS_ALLOW_IDMAP,
 +      .owner                  = THIS_MODULE,
 +      .name                   = "ext4",
 +      .init_fs_context        = ext4_init_fs_context,
 +      .parameters             = ext4_param_specs,
 +      .kill_sb                = kill_block_super,
 +      .fs_flags               = FS_REQUIRES_DEV | FS_ALLOW_IDMAP,
  };
  MODULE_ALIAS_FS("ext4");
  
@@@ -7137,7 -6640,6 +7136,7 @@@ static int __init ext4_init_fs(void
  out:
        unregister_as_ext2();
        unregister_as_ext3();
 +      ext4_fc_destroy_dentry_cache();
  out05:
        destroy_inodecache();
  out1:
@@@ -7164,7 -6666,6 +7163,7 @@@ static void __exit ext4_exit_fs(void
        unregister_as_ext2();
        unregister_as_ext3();
        unregister_filesystem(&ext4_fs_type);
 +      ext4_fc_destroy_dentry_cache();
        destroy_inodecache();
        ext4_exit_mballoc();
        ext4_exit_sysfs();
diff --combined fs/f2fs/super.c
index 3bace24f880052eb9980481fb4e9c46837e95756,3029f71bf2b74a9ea1a85c92d5c27cf15e99d0f2..15f12ece0ac63b156c9ef37e15b457893af6ba4b
@@@ -8,9 -8,9 +8,9 @@@
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/fs.h>
 +#include <linux/sched/mm.h>
  #include <linux/statfs.h>
  #include <linux/buffer_head.h>
 -#include <linux/backing-dev.h>
  #include <linux/kthread.h>
  #include <linux/parser.h>
  #include <linux/mount.h>
@@@ -58,7 -58,6 +58,7 @@@ const char *f2fs_fault_name[FAULT_MAX] 
        [FAULT_DISCARD]         = "discard error",
        [FAULT_WRITE_IO]        = "write IO error",
        [FAULT_SLAB_ALLOC]      = "slab alloc",
 +      [FAULT_DQUOT_INIT]      = "dquot initialize",
  };
  
  void f2fs_build_fault_attr(struct f2fs_sb_info *sbi, unsigned int rate,
@@@ -260,29 -259,22 +260,22 @@@ void f2fs_printk(struct f2fs_sb_info *s
  static const struct f2fs_sb_encodings {
        __u16 magic;
        char *name;
-       char *version;
+       unsigned int version;
  } f2fs_sb_encoding_map[] = {
-       {F2FS_ENC_UTF8_12_1, "utf8", "12.1.0"},
+       {F2FS_ENC_UTF8_12_1, "utf8", UNICODE_AGE(12, 1, 0)},
  };
  
- static int f2fs_sb_read_encoding(const struct f2fs_super_block *sb,
-                                const struct f2fs_sb_encodings **encoding,
-                                __u16 *flags)
+ static const struct f2fs_sb_encodings *
+ f2fs_sb_read_encoding(const struct f2fs_super_block *sb)
  {
        __u16 magic = le16_to_cpu(sb->s_encoding);
        int i;
  
        for (i = 0; i < ARRAY_SIZE(f2fs_sb_encoding_map); i++)
                if (magic == f2fs_sb_encoding_map[i].magic)
-                       break;
-       if (i >= ARRAY_SIZE(f2fs_sb_encoding_map))
-               return -EINVAL;
+                       return &f2fs_sb_encoding_map[i];
  
-       *encoding = &f2fs_sb_encoding_map[i];
-       *flags = le16_to_cpu(sb->s_encoding_flags);
-       return 0;
+       return NULL;
  }
  
  struct kmem_cache *f2fs_cf_name_slab;
@@@ -593,7 -585,7 +586,7 @@@ static int f2fs_set_zstd_level(struct f
        if (kstrtouint(str + 1, 10, &level))
                return -EINVAL;
  
 -      if (!level || level > ZSTD_maxCLevel()) {
 +      if (!level || level > zstd_max_clevel()) {
                f2fs_info(sbi, "invalid zstd compress level: %d", level);
                return -EINVAL;
        }
@@@ -818,10 -810,6 +811,10 @@@ static int parse_options(struct super_b
                                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;
@@@ -1297,7 -1285,7 +1290,7 @@@ default_check
        /* Not pass down write hints if the number of active logs is lesser
         * than NR_CURSEG_PERSIST_TYPE.
         */
 -      if (F2FS_OPTION(sbi).active_logs != NR_CURSEG_TYPE)
 +      if (F2FS_OPTION(sbi).active_logs != NR_CURSEG_PERSIST_TYPE)
                F2FS_OPTION(sbi).whint_mode = WHINT_MODE_OFF;
  
        if (f2fs_sb_has_readonly(sbi) && !f2fs_readonly(sbi->sb)) {
@@@ -1901,10 -1889,6 +1894,10 @@@ static int f2fs_show_options(struct seq
                seq_puts(seq, "adaptive");
        else if (F2FS_OPTION(sbi).fs_mode == FS_MODE_LFS)
                seq_puts(seq, "lfs");
 +      else if (F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_SEG)
 +              seq_puts(seq, "fragment:segment");
 +      else if (F2FS_OPTION(sbi).fs_mode == FS_MODE_FRAGMENT_BLK)
 +              seq_puts(seq, "fragment:block");
        seq_printf(seq, ",active_logs=%u", F2FS_OPTION(sbi).active_logs);
        if (test_opt(sbi, RESERVE_ROOT))
                seq_printf(seq, ",reserve_root=%u,resuid=%u,resgid=%u",
@@@ -2415,7 -2399,8 +2408,7 @@@ repeat
                page = read_cache_page_gfp(mapping, blkidx, GFP_NOFS);
                if (IS_ERR(page)) {
                        if (PTR_ERR(page) == -ENOMEM) {
 -                              congestion_wait(BLK_RW_ASYNC,
 -                                              DEFAULT_IO_TIMEOUT);
 +                              memalloc_retry_wait(GFP_NOFS);
                                goto repeat;
                        }
                        set_sbi_flag(F2FS_SB(sb), SBI_QUOTA_NEED_REPAIR);
@@@ -2499,16 -2484,6 +2492,16 @@@ retry
        return len - towrite;
  }
  
 +int f2fs_dquot_initialize(struct inode *inode)
 +{
 +      if (time_to_inject(F2FS_I_SB(inode), FAULT_DQUOT_INIT)) {
 +              f2fs_show_injection_info(F2FS_I_SB(inode), FAULT_DQUOT_INIT);
 +              return -ESRCH;
 +      }
 +
 +      return dquot_initialize(inode);
 +}
 +
  static struct dquot **f2fs_get_dquots(struct inode *inode)
  {
        return F2FS_I(inode)->i_dquot;
@@@ -2893,11 -2868,6 +2886,11 @@@ static const struct quotactl_ops f2fs_q
        .get_nextdqblk  = dquot_get_next_dqblk,
  };
  #else
 +int f2fs_dquot_initialize(struct inode *inode)
 +{
 +      return 0;
 +}
 +
  int f2fs_quota_sync(struct super_block *sb, int type)
  {
        return 0;
@@@ -2999,6 -2969,7 +2992,6 @@@ static const struct fscrypt_operations 
        .set_context            = f2fs_set_context,
        .get_dummy_policy       = f2fs_get_dummy_policy,
        .empty_dir              = f2fs_empty_dir,
 -      .max_namelen            = F2FS_NAME_LEN,
        .has_stable_inodes      = f2fs_has_stable_inodes,
        .get_ino_and_lblk_bits  = f2fs_get_ino_and_lblk_bits,
        .get_num_devices        = f2fs_get_num_devices,
@@@ -3509,7 -3480,7 +3502,7 @@@ skip_cross
                NR_CURSEG_PERSIST_TYPE + nat_bits_blocks >= blocks_per_seg)) {
                f2fs_warn(sbi, "Insane cp_payload: %u, nat_bits_blocks: %u)",
                          cp_payload, nat_bits_blocks);
 -              return -EFSCORRUPTED;
 +              return 1;
        }
  
        if (unlikely(f2fs_cp_error(sbi))) {
@@@ -3545,8 -3516,6 +3538,8 @@@ static void init_sb_info(struct f2fs_sb
        sbi->max_victim_search = DEF_MAX_VICTIM_SEARCH;
        sbi->migration_granularity = sbi->segs_per_sec;
        sbi->seq_file_ra_mul = MIN_RA_MUL;
 +      sbi->max_fragment_chunk = DEF_FRAGMENT_SIZE;
 +      sbi->max_fragment_hole = DEF_FRAGMENT_SIZE;
  
        sbi->dir_level = DEF_DIR_LEVEL;
        sbi->interval_time[CP_TIME] = DEF_CP_INTERVAL;
@@@ -3771,7 -3740,6 +3764,7 @@@ static int f2fs_scan_devices(struct f2f
  {
        struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
        unsigned int max_devices = MAX_DEVICES;
 +      unsigned int logical_blksize;
        int i;
  
        /* Initialize single device information */
        if (!sbi->devs)
                return -ENOMEM;
  
 +      logical_blksize = bdev_logical_block_size(sbi->sb->s_bdev);
 +      sbi->aligned_blksize = true;
 +
        for (i = 0; i < max_devices; i++) {
  
                if (i > 0 && !RDEV(i).path[0])
                /* to release errored devices */
                sbi->s_ndevs = i + 1;
  
 +              if (logical_blksize != bdev_logical_block_size(FDEV(i).bdev))
 +                      sbi->aligned_blksize = false;
 +
  #ifdef CONFIG_BLK_DEV_ZONED
                if (bdev_zoned_model(FDEV(i).bdev) == BLK_ZONED_HM &&
                                !f2fs_sb_has_blkzoned(sbi)) {
@@@ -3874,25 -3836,32 +3867,32 @@@ static int f2fs_setup_casefold(struct f
                struct unicode_map *encoding;
                __u16 encoding_flags;
  
-               if (f2fs_sb_read_encoding(sbi->raw_super, &encoding_info,
-                                         &encoding_flags)) {
+               encoding_info = f2fs_sb_read_encoding(sbi->raw_super);
+               if (!encoding_info) {
                        f2fs_err(sbi,
                                 "Encoding requested by superblock is unknown");
                        return -EINVAL;
                }
  
+               encoding_flags = le16_to_cpu(sbi->raw_super->s_encoding_flags);
                encoding = utf8_load(encoding_info->version);
                if (IS_ERR(encoding)) {
                        f2fs_err(sbi,
-                                "can't mount with superblock charset: %s-%s "
+                                "can't mount with superblock charset: %s-%u.%u.%u "
                                 "not supported by the kernel. flags: 0x%x.",
-                                encoding_info->name, encoding_info->version,
+                                encoding_info->name,
+                                unicode_major(encoding_info->version),
+                                unicode_minor(encoding_info->version),
+                                unicode_rev(encoding_info->version),
                                 encoding_flags);
                        return PTR_ERR(encoding);
                }
                f2fs_info(sbi, "Using encoding defined by superblock: "
-                        "%s-%s with flags 0x%hx", encoding_info->name,
-                        encoding_info->version?:"\b", encoding_flags);
+                        "%s-%u.%u.%u with flags 0x%hx", encoding_info->name,
+                        unicode_major(encoding_info->version),
+                        unicode_minor(encoding_info->version),
+                        unicode_rev(encoding_info->version),
+                        encoding_flags);
  
                sbi->sb->s_encoding = encoding;
                sbi->sb->s_encoding_flags = encoding_flags;
@@@ -4383,8 -4352,6 +4383,8 @@@ free_node_inode
  free_stats:
        f2fs_destroy_stats(sbi);
  free_nm:
 +      /* stop discard thread before destroying node manager */
 +      f2fs_stop_discard_thread(sbi);
        f2fs_destroy_node_manager(sbi);
  free_sm:
        f2fs_destroy_segment_manager(sbi);
diff --combined fs/f2fs/sysfs.c
index 7d289249cd7ebd7917ed1a5d178665446ffca918,650e84398f74426d5e836c95e9c5318cbda63cd0..8408f77764e856fde8a8b877d406dd36bb440929
@@@ -196,8 -196,7 +196,7 @@@ static ssize_t encoding_show(struct f2f
        struct super_block *sb = sbi->sb;
  
        if (f2fs_sb_has_casefold(sbi))
-               return sysfs_emit(buf, "%s (%d.%d.%d)\n",
-                       sb->s_encoding->charset,
 -              return snprintf(buf, PAGE_SIZE, "UTF-8 (%d.%d.%d)\n",
++              return sysfs_emit(buf, "UTF-8 (%d.%d.%d)\n",
                        (sb->s_encoding->version >> 16) & 0xff,
                        (sb->s_encoding->version >> 8) & 0xff,
                        sb->s_encoding->version & 0xff);
@@@ -245,7 -244,7 +244,7 @@@ static ssize_t avg_vblocks_show(struct 
  static ssize_t main_blkaddr_show(struct f2fs_attr *a,
                                struct f2fs_sb_info *sbi, char *buf)
  {
 -      return snprintf(buf, PAGE_SIZE, "%llu\n",
 +      return sysfs_emit(buf, "%llu\n",
                        (unsigned long long)MAIN_BLKADDR(sbi));
  }
  
@@@ -551,22 -550,6 +550,22 @@@ out
                return count;
        }
  
 +      if (!strcmp(a->attr.name, "max_fragment_chunk")) {
 +              if (t >= MIN_FRAGMENT_SIZE && t <= MAX_FRAGMENT_SIZE)
 +                      sbi->max_fragment_chunk = t;
 +              else
 +                      return -EINVAL;
 +              return count;
 +      }
 +
 +      if (!strcmp(a->attr.name, "max_fragment_hole")) {
 +              if (t >= MIN_FRAGMENT_SIZE && t <= MAX_FRAGMENT_SIZE)
 +                      sbi->max_fragment_hole = t;
 +              else
 +                      return -EINVAL;
 +              return count;
 +      }
 +
        *ui = (unsigned int)t;
  
        return count;
@@@ -797,8 -780,6 +796,8 @@@ F2FS_RW_ATTR(ATGC_INFO, atgc_management
  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, seq_file_ra_mul, seq_file_ra_mul);
  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_segment_mode, gc_segment_mode);
  F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_reclaimed_segments, gc_reclaimed_segs);
 +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_fragment_chunk, max_fragment_chunk);
 +F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_fragment_hole, max_fragment_hole);
  
  #define ATTR_LIST(name) (&f2fs_attr_##name.attr)
  static struct attribute *f2fs_attrs[] = {
        ATTR_LIST(seq_file_ra_mul),
        ATTR_LIST(gc_segment_mode),
        ATTR_LIST(gc_reclaimed_segments),
 +      ATTR_LIST(max_fragment_chunk),
 +      ATTR_LIST(max_fragment_hole),
        NULL,
  };
  ATTRIBUTE_GROUPS(f2fs);