ext4: don't use kfree() on rcu protected pointer sbi->s_qf_names
[linux-2.6-microblaze.git] / fs / ext4 / super.c
index 4e33b5e..72e4dfc 100644 (file)
@@ -47,6 +47,8 @@
 #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 */
@@ -73,12 +75,9 @@ static int ext4_mark_recovery_complete(struct super_block *sb,
 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);
@@ -86,6 +85,16 @@ static void ext4_unregister_li_request(struct super_block *sb);
 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
@@ -113,13 +122,22 @@ static struct inode *ext4_get_journal_inode(struct super_block *sb,
  * 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");
@@ -130,11 +148,12 @@ 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");
@@ -912,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);
 }
 
@@ -1647,7 +1672,6 @@ static const struct super_operations ext4_sops = {
        .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,
@@ -1665,7 +1689,7 @@ static const struct export_operations ext4_export_ops = {
 
 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,
@@ -1674,152 +1698,169 @@ enum {
        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)
@@ -1828,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;
@@ -1944,40 +1917,17 @@ static const struct mount_opts {
                                    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
@@ -1989,7 +1939,6 @@ static const struct mount_opts {
 #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},
@@ -2000,23 +1949,15 @@ static const struct mount_opts {
        {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}
 };
@@ -2051,448 +1992,950 @@ static int ext4_sb_read_encoding(const struct ext4_super_block *es,
 }
 #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 xfs_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 1;
+
+       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 int parse_options(char *options, struct super_block *sb,
-                        struct ext4_parsed_options *ret_opts,
-                        int is_remount)
+static void ext4_apply_quota_options(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;
+#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 (!options)
-               return 1;
+       if (quota_feature)
+               return;
 
-       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;
+       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 */
+                       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);
+                       set_opt(sb, QUOTA);
+               }
        }
+
+       if (ctx->spec & EXT4_SPEC_JQFMT)
+               sbi->s_jquota_fmt = ctx->s_jquota_fmt;
+#endif
+}
+
+/*
+ * Check quota settings consistency.
+ */
+static int ext4_check_quota_consistency(struct fs_context *fc,
+                                       struct super_block *sb)
+{
 #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 (test_opt(sb, GRPQUOTA) || test_opt(sb, USRQUOTA)) {
-                       ext4_msg(sb, KERN_ERR, "old and new quota "
-                                       "format mixing");
+                       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 (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;
 }
 
@@ -2533,12 +2976,12 @@ static inline void ext4_show_quota_options(struct seq_file *seq,
 
 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;
 }
 
 /*
@@ -2564,7 +3007,7 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb,
        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 */
@@ -3876,21 +4319,52 @@ static void ext4_setup_csum_trigger(struct super_block *sb,
        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_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;
@@ -3899,32 +4373,17 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
        __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;
-
-       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;
+       ctx->journal_ioprio = DEFAULT_JOURNAL_IOPRIO;
+       ctx->mb_optimize_scan = DEFAULT_MB_OPTIMIZE_SCAN;
 
-       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);
@@ -3938,10 +4397,10 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
         * 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);
@@ -4146,21 +4605,18 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                }
        }
 
-       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
@@ -4299,7 +4755,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                goto failed_mount;
        }
 
-       if (dax_supported(dax_dev, sb->s_bdev, blocksize, 0,
+       if (dax_supported(sbi->s_daxdev, sb->s_bdev, blocksize, 0,
                        bdev_nr_sectors(sb->s_bdev)))
                set_bit(EXT4_FLAGS_BDEV_IS_DAX, &sbi->s_ext4_flags);
 
@@ -4337,7 +4793,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                        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)) {
@@ -4620,7 +5076,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
 
        /* 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]);
@@ -4653,7 +5108,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
         * 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) &&
@@ -4753,7 +5208,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
                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;
@@ -4865,9 +5320,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);
@@ -4969,15 +5424,6 @@ no_journal:
                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);
@@ -4987,14 +5433,6 @@ no_journal:
                                 "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 */
 
@@ -5005,7 +5443,6 @@ no_journal:
        atomic_set(&sbi->s_warning_count, 0);
        atomic_set(&sbi->s_msg_count, 0);
 
-       kfree(orig_data);
        return 0;
 
 cantfind_ext4:
@@ -5091,14 +5528,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
@@ -5727,11 +6210,12 @@ 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;
        ext4_group_t g;
        int err = 0;
@@ -5740,14 +6224,8 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
        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;
@@ -5768,28 +6246,16 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
                        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)) {
@@ -5836,19 +6302,19 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
 
        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;
@@ -5996,16 +6462,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
        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:
@@ -6031,10 +6487,30 @@ 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)
@@ -6275,10 +6751,7 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id,
 
        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;
 
@@ -6298,7 +6771,12 @@ static int ext4_quota_on(struct super_block *sb, int type, int format_id,
                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;
 }
 
@@ -6361,8 +6839,19 @@ int ext4_enable_quotas(struct super_block *sb)
                                        "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;
                        }
@@ -6466,7 +6955,7 @@ static ssize_t ext4_quota_write(struct super_block *sb, int type,
        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);
@@ -6517,12 +7006,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)
 {
@@ -6580,11 +7063,12 @@ static inline int ext3_feature_set_ok(struct super_block *sb)
 }
 
 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");
 
@@ -6649,6 +7133,7 @@ static int __init ext4_init_fs(void)
 out:
        unregister_as_ext2();
        unregister_as_ext3();
+       ext4_fc_destroy_dentry_cache();
 out05:
        destroy_inodecache();
 out1:
@@ -6675,6 +7160,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();