xfs: move xfs_fc_parse_param() above xfs_fc_get_tree()
authorIan Kent <raven@themaw.net>
Mon, 4 Nov 2019 21:58:48 +0000 (13:58 -0800)
committerDarrick J. Wong <darrick.wong@oracle.com>
Tue, 5 Nov 2019 16:28:27 +0000 (08:28 -0800)
Grouping the options parsing and mount handling functions above the
struct fs_context_operations but below the struct super_operations
should improve (some) the grouping of the super operations while also
improving the grouping of the options parsing and mount handling code.

Lastly move xfs_fc_parse_param() and related functions down to above
xfs_fc_get_tree() and it's related functions.

But leave the options enum, struct fs_parameter_spec and the struct
fs_parameter_description declarations at the top since that's the
logical place for them.

This is a straight code move, there aren't any functional changes.

Signed-off-by: Ian Kent <raven@themaw.net>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
fs/xfs/xfs_super.c

index 33a6eea..d42c231 100644 (file)
@@ -111,264 +111,6 @@ static const struct fs_parameter_description xfs_fs_parameters = {
        .specs          = xfs_param_specs,
 };
 
-static int
-suffix_kstrtoint(
-       const char      *s,
-       unsigned int    base,
-       int             *res)
-{
-       int             last, shift_left_factor = 0, _res;
-       char            *value;
-       int             ret = 0;
-
-       value = kstrdup(s, GFP_KERNEL);
-       if (!value)
-               return -ENOMEM;
-
-       last = strlen(value) - 1;
-       if (value[last] == 'K' || value[last] == 'k') {
-               shift_left_factor = 10;
-               value[last] = '\0';
-       }
-       if (value[last] == 'M' || value[last] == 'm') {
-               shift_left_factor = 20;
-               value[last] = '\0';
-       }
-       if (value[last] == 'G' || value[last] == 'g') {
-               shift_left_factor = 30;
-               value[last] = '\0';
-       }
-
-       if (kstrtoint(value, base, &_res))
-               ret = -EINVAL;
-       kfree(value);
-       *res = _res << shift_left_factor;
-       return ret;
-}
-
-/*
- * Set mount state from a mount option.
- *
- * NOTE: mp->m_super is NULL here!
- */
-static int
-xfs_fc_parse_param(
-       struct fs_context       *fc,
-       struct fs_parameter     *param)
-{
-       struct xfs_mount        *mp = fc->s_fs_info;
-       struct fs_parse_result  result;
-       int                     size = 0;
-       int                     opt;
-
-       opt = fs_parse(fc, &xfs_fs_parameters, param, &result);
-       if (opt < 0)
-               return opt;
-
-       switch (opt) {
-       case Opt_logbufs:
-               mp->m_logbufs = result.uint_32;
-               return 0;
-       case Opt_logbsize:
-               if (suffix_kstrtoint(param->string, 10, &mp->m_logbsize))
-                       return -EINVAL;
-               return 0;
-       case Opt_logdev:
-               kfree(mp->m_logname);
-               mp->m_logname = kstrdup(param->string, GFP_KERNEL);
-               if (!mp->m_logname)
-                       return -ENOMEM;
-               return 0;
-       case Opt_rtdev:
-               kfree(mp->m_rtname);
-               mp->m_rtname = kstrdup(param->string, GFP_KERNEL);
-               if (!mp->m_rtname)
-                       return -ENOMEM;
-               return 0;
-       case Opt_allocsize:
-               if (suffix_kstrtoint(param->string, 10, &size))
-                       return -EINVAL;
-               mp->m_allocsize_log = ffs(size) - 1;
-               mp->m_flags |= XFS_MOUNT_ALLOCSIZE;
-               return 0;
-       case Opt_grpid:
-       case Opt_bsdgroups:
-               mp->m_flags |= XFS_MOUNT_GRPID;
-               return 0;
-       case Opt_nogrpid:
-       case Opt_sysvgroups:
-               mp->m_flags &= ~XFS_MOUNT_GRPID;
-               return 0;
-       case Opt_wsync:
-               mp->m_flags |= XFS_MOUNT_WSYNC;
-               return 0;
-       case Opt_norecovery:
-               mp->m_flags |= XFS_MOUNT_NORECOVERY;
-               return 0;
-       case Opt_noalign:
-               mp->m_flags |= XFS_MOUNT_NOALIGN;
-               return 0;
-       case Opt_swalloc:
-               mp->m_flags |= XFS_MOUNT_SWALLOC;
-               return 0;
-       case Opt_sunit:
-               mp->m_dalign = result.uint_32;
-               return 0;
-       case Opt_swidth:
-               mp->m_swidth = result.uint_32;
-               return 0;
-       case Opt_inode32:
-               mp->m_flags |= XFS_MOUNT_SMALL_INUMS;
-               return 0;
-       case Opt_inode64:
-               mp->m_flags &= ~XFS_MOUNT_SMALL_INUMS;
-               return 0;
-       case Opt_nouuid:
-               mp->m_flags |= XFS_MOUNT_NOUUID;
-               return 0;
-       case Opt_ikeep:
-               mp->m_flags |= XFS_MOUNT_IKEEP;
-               return 0;
-       case Opt_noikeep:
-               mp->m_flags &= ~XFS_MOUNT_IKEEP;
-               return 0;
-       case Opt_largeio:
-               mp->m_flags |= XFS_MOUNT_LARGEIO;
-               return 0;
-       case Opt_nolargeio:
-               mp->m_flags &= ~XFS_MOUNT_LARGEIO;
-               return 0;
-       case Opt_attr2:
-               mp->m_flags |= XFS_MOUNT_ATTR2;
-               return 0;
-       case Opt_noattr2:
-               mp->m_flags &= ~XFS_MOUNT_ATTR2;
-               mp->m_flags |= XFS_MOUNT_NOATTR2;
-               return 0;
-       case Opt_filestreams:
-               mp->m_flags |= XFS_MOUNT_FILESTREAMS;
-               return 0;
-       case Opt_noquota:
-               mp->m_qflags &= ~XFS_ALL_QUOTA_ACCT;
-               mp->m_qflags &= ~XFS_ALL_QUOTA_ENFD;
-               mp->m_qflags &= ~XFS_ALL_QUOTA_ACTIVE;
-               return 0;
-       case Opt_quota:
-       case Opt_uquota:
-       case Opt_usrquota:
-               mp->m_qflags |= (XFS_UQUOTA_ACCT | XFS_UQUOTA_ACTIVE |
-                                XFS_UQUOTA_ENFD);
-               return 0;
-       case Opt_qnoenforce:
-       case Opt_uqnoenforce:
-               mp->m_qflags |= (XFS_UQUOTA_ACCT | XFS_UQUOTA_ACTIVE);
-               mp->m_qflags &= ~XFS_UQUOTA_ENFD;
-               return 0;
-       case Opt_pquota:
-       case Opt_prjquota:
-               mp->m_qflags |= (XFS_PQUOTA_ACCT | XFS_PQUOTA_ACTIVE |
-                                XFS_PQUOTA_ENFD);
-               return 0;
-       case Opt_pqnoenforce:
-               mp->m_qflags |= (XFS_PQUOTA_ACCT | XFS_PQUOTA_ACTIVE);
-               mp->m_qflags &= ~XFS_PQUOTA_ENFD;
-               return 0;
-       case Opt_gquota:
-       case Opt_grpquota:
-               mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE |
-                                XFS_GQUOTA_ENFD);
-               return 0;
-       case Opt_gqnoenforce:
-               mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE);
-               mp->m_qflags &= ~XFS_GQUOTA_ENFD;
-               return 0;
-       case Opt_discard:
-               mp->m_flags |= XFS_MOUNT_DISCARD;
-               return 0;
-       case Opt_nodiscard:
-               mp->m_flags &= ~XFS_MOUNT_DISCARD;
-               return 0;
-#ifdef CONFIG_FS_DAX
-       case Opt_dax:
-               mp->m_flags |= XFS_MOUNT_DAX;
-               return 0;
-#endif
-       default:
-               xfs_warn(mp, "unknown mount option [%s].", param->key);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static int
-xfs_fc_validate_params(
-       struct xfs_mount        *mp)
-{
-       /*
-        * no recovery flag requires a read-only mount
-        */
-       if ((mp->m_flags & XFS_MOUNT_NORECOVERY) &&
-           !(mp->m_flags & XFS_MOUNT_RDONLY)) {
-               xfs_warn(mp, "no-recovery mounts must be read-only.");
-               return -EINVAL;
-       }
-
-       if ((mp->m_flags & XFS_MOUNT_NOALIGN) &&
-           (mp->m_dalign || mp->m_swidth)) {
-               xfs_warn(mp,
-       "sunit and swidth options incompatible with the noalign option");
-               return -EINVAL;
-       }
-
-       if (!IS_ENABLED(CONFIG_XFS_QUOTA) && mp->m_qflags != 0) {
-               xfs_warn(mp, "quota support not available in this kernel.");
-               return -EINVAL;
-       }
-
-       if ((mp->m_dalign && !mp->m_swidth) ||
-           (!mp->m_dalign && mp->m_swidth)) {
-               xfs_warn(mp, "sunit and swidth must be specified together");
-               return -EINVAL;
-       }
-
-       if (mp->m_dalign && (mp->m_swidth % mp->m_dalign != 0)) {
-               xfs_warn(mp,
-       "stripe width (%d) must be a multiple of the stripe unit (%d)",
-                       mp->m_swidth, mp->m_dalign);
-               return -EINVAL;
-       }
-
-       if (mp->m_logbufs != -1 &&
-           mp->m_logbufs != 0 &&
-           (mp->m_logbufs < XLOG_MIN_ICLOGS ||
-            mp->m_logbufs > XLOG_MAX_ICLOGS)) {
-               xfs_warn(mp, "invalid logbufs value: %d [not %d-%d]",
-                       mp->m_logbufs, XLOG_MIN_ICLOGS, XLOG_MAX_ICLOGS);
-               return -EINVAL;
-       }
-       if (mp->m_logbsize != -1 &&
-           mp->m_logbsize !=  0 &&
-           (mp->m_logbsize < XLOG_MIN_RECORD_BSIZE ||
-            mp->m_logbsize > XLOG_MAX_RECORD_BSIZE ||
-            !is_power_of_2(mp->m_logbsize))) {
-               xfs_warn(mp,
-                       "invalid logbufsize: %d [not 16k,32k,64k,128k or 256k]",
-                       mp->m_logbsize);
-               return -EINVAL;
-       }
-
-       if ((mp->m_flags & XFS_MOUNT_ALLOCSIZE) &&
-           (mp->m_allocsize_log > XFS_MAX_IO_LOG ||
-            mp->m_allocsize_log < XFS_MIN_IO_LOG)) {
-               xfs_warn(mp, "invalid log iosize: %d [not %d-%d]",
-                       mp->m_allocsize_log, XFS_MIN_IO_LOG, XFS_MAX_IO_LOG);
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
 struct proc_xfs_info {
        uint64_t        flag;
        char            *str;
@@ -1102,285 +844,544 @@ xfs_restore_resvblks(struct xfs_mount *mp)
        xfs_reserve_blocks(mp, &resblks, NULL);
 }
 
-/*
- * Trigger writeback of all the dirty metadata in the file system.
- *
- * This ensures that the metadata is written to their location on disk rather
- * than just existing in transactions in the log. This means after a quiesce
- * there is no log replay required to write the inodes to disk - this is the
- * primary difference between a sync and a quiesce.
- *
- * Note: xfs_log_quiesce() stops background log work - the callers must ensure
- * it is started again when appropriate.
- */
-void
-xfs_quiesce_attr(
-       struct xfs_mount        *mp)
+/*
+ * Trigger writeback of all the dirty metadata in the file system.
+ *
+ * This ensures that the metadata is written to their location on disk rather
+ * than just existing in transactions in the log. This means after a quiesce
+ * there is no log replay required to write the inodes to disk - this is the
+ * primary difference between a sync and a quiesce.
+ *
+ * Note: xfs_log_quiesce() stops background log work - the callers must ensure
+ * it is started again when appropriate.
+ */
+void
+xfs_quiesce_attr(
+       struct xfs_mount        *mp)
+{
+       int     error = 0;
+
+       /* wait for all modifications to complete */
+       while (atomic_read(&mp->m_active_trans) > 0)
+               delay(100);
+
+       /* force the log to unpin objects from the now complete transactions */
+       xfs_log_force(mp, XFS_LOG_SYNC);
+
+       /* reclaim inodes to do any IO before the freeze completes */
+       xfs_reclaim_inodes(mp, 0);
+       xfs_reclaim_inodes(mp, SYNC_WAIT);
+
+       /* Push the superblock and write an unmount record */
+       error = xfs_log_sbcount(mp);
+       if (error)
+               xfs_warn(mp, "xfs_attr_quiesce: failed to log sb changes. "
+                               "Frozen image may not be consistent.");
+       /*
+        * Just warn here till VFS can correctly support
+        * read-only remount without racing.
+        */
+       WARN_ON(atomic_read(&mp->m_active_trans) != 0);
+
+       xfs_log_quiesce(mp);
+}
+
+/*
+ * Second stage of a freeze. The data is already frozen so we only
+ * need to take care of the metadata. Once that's done sync the superblock
+ * to the log to dirty it in case of a crash while frozen. This ensures that we
+ * will recover the unlinked inode lists on the next mount.
+ */
+STATIC int
+xfs_fs_freeze(
+       struct super_block      *sb)
+{
+       struct xfs_mount        *mp = XFS_M(sb);
+
+       xfs_stop_block_reaping(mp);
+       xfs_save_resvblks(mp);
+       xfs_quiesce_attr(mp);
+       return xfs_sync_sb(mp, true);
+}
+
+STATIC int
+xfs_fs_unfreeze(
+       struct super_block      *sb)
+{
+       struct xfs_mount        *mp = XFS_M(sb);
+
+       xfs_restore_resvblks(mp);
+       xfs_log_work_queue(mp);
+       xfs_start_block_reaping(mp);
+       return 0;
+}
+
+/*
+ * This function fills in xfs_mount_t fields based on mount args.
+ * Note: the superblock _has_ now been read in.
+ */
+STATIC int
+xfs_finish_flags(
+       struct xfs_mount        *mp)
+{
+       int                     ronly = (mp->m_flags & XFS_MOUNT_RDONLY);
+
+       /* Fail a mount where the logbuf is smaller than the log stripe */
+       if (xfs_sb_version_haslogv2(&mp->m_sb)) {
+               if (mp->m_logbsize <= 0 &&
+                   mp->m_sb.sb_logsunit > XLOG_BIG_RECORD_BSIZE) {
+                       mp->m_logbsize = mp->m_sb.sb_logsunit;
+               } else if (mp->m_logbsize > 0 &&
+                          mp->m_logbsize < mp->m_sb.sb_logsunit) {
+                       xfs_warn(mp,
+               "logbuf size must be greater than or equal to log stripe size");
+                       return -EINVAL;
+               }
+       } else {
+               /* Fail a mount if the logbuf is larger than 32K */
+               if (mp->m_logbsize > XLOG_BIG_RECORD_BSIZE) {
+                       xfs_warn(mp,
+               "logbuf size for version 1 logs must be 16K or 32K");
+                       return -EINVAL;
+               }
+       }
+
+       /*
+        * V5 filesystems always use attr2 format for attributes.
+        */
+       if (xfs_sb_version_hascrc(&mp->m_sb) &&
+           (mp->m_flags & XFS_MOUNT_NOATTR2)) {
+               xfs_warn(mp, "Cannot mount a V5 filesystem as noattr2. "
+                            "attr2 is always enabled for V5 filesystems.");
+               return -EINVAL;
+       }
+
+       /*
+        * mkfs'ed attr2 will turn on attr2 mount unless explicitly
+        * told by noattr2 to turn it off
+        */
+       if (xfs_sb_version_hasattr2(&mp->m_sb) &&
+           !(mp->m_flags & XFS_MOUNT_NOATTR2))
+               mp->m_flags |= XFS_MOUNT_ATTR2;
+
+       /*
+        * prohibit r/w mounts of read-only filesystems
+        */
+       if ((mp->m_sb.sb_flags & XFS_SBF_READONLY) && !ronly) {
+               xfs_warn(mp,
+                       "cannot mount a read-only filesystem as read-write");
+               return -EROFS;
+       }
+
+       if ((mp->m_qflags & (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE)) &&
+           (mp->m_qflags & (XFS_PQUOTA_ACCT | XFS_PQUOTA_ACTIVE)) &&
+           !xfs_sb_version_has_pquotino(&mp->m_sb)) {
+               xfs_warn(mp,
+                 "Super block does not support project and group quota together");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int
+xfs_init_percpu_counters(
+       struct xfs_mount        *mp)
+{
+       int             error;
+
+       error = percpu_counter_init(&mp->m_icount, 0, GFP_KERNEL);
+       if (error)
+               return -ENOMEM;
+
+       error = percpu_counter_init(&mp->m_ifree, 0, GFP_KERNEL);
+       if (error)
+               goto free_icount;
+
+       error = percpu_counter_init(&mp->m_fdblocks, 0, GFP_KERNEL);
+       if (error)
+               goto free_ifree;
+
+       error = percpu_counter_init(&mp->m_delalloc_blks, 0, GFP_KERNEL);
+       if (error)
+               goto free_fdblocks;
+
+       return 0;
+
+free_fdblocks:
+       percpu_counter_destroy(&mp->m_fdblocks);
+free_ifree:
+       percpu_counter_destroy(&mp->m_ifree);
+free_icount:
+       percpu_counter_destroy(&mp->m_icount);
+       return -ENOMEM;
+}
+
+void
+xfs_reinit_percpu_counters(
+       struct xfs_mount        *mp)
+{
+       percpu_counter_set(&mp->m_icount, mp->m_sb.sb_icount);
+       percpu_counter_set(&mp->m_ifree, mp->m_sb.sb_ifree);
+       percpu_counter_set(&mp->m_fdblocks, mp->m_sb.sb_fdblocks);
+}
+
+static void
+xfs_destroy_percpu_counters(
+       struct xfs_mount        *mp)
+{
+       percpu_counter_destroy(&mp->m_icount);
+       percpu_counter_destroy(&mp->m_ifree);
+       percpu_counter_destroy(&mp->m_fdblocks);
+       ASSERT(XFS_FORCED_SHUTDOWN(mp) ||
+              percpu_counter_sum(&mp->m_delalloc_blks) == 0);
+       percpu_counter_destroy(&mp->m_delalloc_blks);
+}
+
+static void
+xfs_fs_put_super(
+       struct super_block      *sb)
+{
+       struct xfs_mount        *mp = XFS_M(sb);
+
+       /* if ->fill_super failed, we have no mount to tear down */
+       if (!sb->s_fs_info)
+               return;
+
+       xfs_notice(mp, "Unmounting Filesystem");
+       xfs_filestream_unmount(mp);
+       xfs_unmountfs(mp);
+
+       xfs_freesb(mp);
+       free_percpu(mp->m_stats.xs_stats);
+       xfs_destroy_percpu_counters(mp);
+       xfs_destroy_mount_workqueues(mp);
+       xfs_close_devices(mp);
+
+       sb->s_fs_info = NULL;
+       xfs_mount_free(mp);
+}
+
+static long
+xfs_fs_nr_cached_objects(
+       struct super_block      *sb,
+       struct shrink_control   *sc)
+{
+       /* Paranoia: catch incorrect calls during mount setup or teardown */
+       if (WARN_ON_ONCE(!sb->s_fs_info))
+               return 0;
+       return xfs_reclaim_inodes_count(XFS_M(sb));
+}
+
+static long
+xfs_fs_free_cached_objects(
+       struct super_block      *sb,
+       struct shrink_control   *sc)
 {
-       int     error = 0;
+       return xfs_reclaim_inodes_nr(XFS_M(sb), sc->nr_to_scan);
+}
 
-       /* wait for all modifications to complete */
-       while (atomic_read(&mp->m_active_trans) > 0)
-               delay(100);
+static const struct super_operations xfs_super_operations = {
+       .alloc_inode            = xfs_fs_alloc_inode,
+       .destroy_inode          = xfs_fs_destroy_inode,
+       .dirty_inode            = xfs_fs_dirty_inode,
+       .drop_inode             = xfs_fs_drop_inode,
+       .put_super              = xfs_fs_put_super,
+       .sync_fs                = xfs_fs_sync_fs,
+       .freeze_fs              = xfs_fs_freeze,
+       .unfreeze_fs            = xfs_fs_unfreeze,
+       .statfs                 = xfs_fs_statfs,
+       .show_options           = xfs_fs_show_options,
+       .nr_cached_objects      = xfs_fs_nr_cached_objects,
+       .free_cached_objects    = xfs_fs_free_cached_objects,
+};
 
-       /* force the log to unpin objects from the now complete transactions */
-       xfs_log_force(mp, XFS_LOG_SYNC);
+static struct xfs_mount *
+xfs_mount_alloc(void)
+{
+       struct xfs_mount        *mp;
 
-       /* reclaim inodes to do any IO before the freeze completes */
-       xfs_reclaim_inodes(mp, 0);
-       xfs_reclaim_inodes(mp, SYNC_WAIT);
+       mp = kmem_alloc(sizeof(struct xfs_mount), KM_ZERO);
+       if (!mp)
+               return NULL;
 
-       /* Push the superblock and write an unmount record */
-       error = xfs_log_sbcount(mp);
-       if (error)
-               xfs_warn(mp, "xfs_attr_quiesce: failed to log sb changes. "
-                               "Frozen image may not be consistent.");
+       spin_lock_init(&mp->m_sb_lock);
+       spin_lock_init(&mp->m_agirotor_lock);
+       INIT_RADIX_TREE(&mp->m_perag_tree, GFP_ATOMIC);
+       spin_lock_init(&mp->m_perag_lock);
+       mutex_init(&mp->m_growlock);
+       atomic_set(&mp->m_active_trans, 0);
+       INIT_DELAYED_WORK(&mp->m_reclaim_work, xfs_reclaim_worker);
+       INIT_DELAYED_WORK(&mp->m_eofblocks_work, xfs_eofblocks_worker);
+       INIT_DELAYED_WORK(&mp->m_cowblocks_work, xfs_cowblocks_worker);
+       mp->m_kobj.kobject.kset = xfs_kset;
        /*
-        * Just warn here till VFS can correctly support
-        * read-only remount without racing.
+        * We don't create the finobt per-ag space reservation until after log
+        * recovery, so we must set this to true so that an ifree transaction
+        * started during log recovery will not depend on space reservations
+        * for finobt expansion.
         */
-       WARN_ON(atomic_read(&mp->m_active_trans) != 0);
+       mp->m_finobt_nores = true;
+       return mp;
+}
 
-       xfs_log_quiesce(mp);
+static int
+suffix_kstrtoint(
+       const char      *s,
+       unsigned int    base,
+       int             *res)
+{
+       int             last, shift_left_factor = 0, _res;
+       char            *value;
+       int             ret = 0;
+
+       value = kstrdup(s, GFP_KERNEL);
+       if (!value)
+               return -ENOMEM;
+
+       last = strlen(value) - 1;
+       if (value[last] == 'K' || value[last] == 'k') {
+               shift_left_factor = 10;
+               value[last] = '\0';
+       }
+       if (value[last] == 'M' || value[last] == 'm') {
+               shift_left_factor = 20;
+               value[last] = '\0';
+       }
+       if (value[last] == 'G' || value[last] == 'g') {
+               shift_left_factor = 30;
+               value[last] = '\0';
+       }
+
+       if (kstrtoint(value, base, &_res))
+               ret = -EINVAL;
+       kfree(value);
+       *res = _res << shift_left_factor;
+       return ret;
 }
 
 /*
- * Second stage of a freeze. The data is already frozen so we only
- * need to take care of the metadata. Once that's done sync the superblock
- * to the log to dirty it in case of a crash while frozen. This ensures that we
- * will recover the unlinked inode lists on the next mount.
+ * Set mount state from a mount option.
+ *
+ * NOTE: mp->m_super is NULL here!
  */
-STATIC int
-xfs_fs_freeze(
-       struct super_block      *sb)
+static int
+xfs_fc_parse_param(
+       struct fs_context       *fc,
+       struct fs_parameter     *param)
 {
-       struct xfs_mount        *mp = XFS_M(sb);
+       struct xfs_mount        *mp = fc->s_fs_info;
+       struct fs_parse_result  result;
+       int                     size = 0;
+       int                     opt;
 
-       xfs_stop_block_reaping(mp);
-       xfs_save_resvblks(mp);
-       xfs_quiesce_attr(mp);
-       return xfs_sync_sb(mp, true);
-}
+       opt = fs_parse(fc, &xfs_fs_parameters, param, &result);
+       if (opt < 0)
+               return opt;
 
-STATIC int
-xfs_fs_unfreeze(
-       struct super_block      *sb)
-{
-       struct xfs_mount        *mp = XFS_M(sb);
+       switch (opt) {
+       case Opt_logbufs:
+               mp->m_logbufs = result.uint_32;
+               return 0;
+       case Opt_logbsize:
+               if (suffix_kstrtoint(param->string, 10, &mp->m_logbsize))
+                       return -EINVAL;
+               return 0;
+       case Opt_logdev:
+               kfree(mp->m_logname);
+               mp->m_logname = kstrdup(param->string, GFP_KERNEL);
+               if (!mp->m_logname)
+                       return -ENOMEM;
+               return 0;
+       case Opt_rtdev:
+               kfree(mp->m_rtname);
+               mp->m_rtname = kstrdup(param->string, GFP_KERNEL);
+               if (!mp->m_rtname)
+                       return -ENOMEM;
+               return 0;
+       case Opt_allocsize:
+               if (suffix_kstrtoint(param->string, 10, &size))
+                       return -EINVAL;
+               mp->m_allocsize_log = ffs(size) - 1;
+               mp->m_flags |= XFS_MOUNT_ALLOCSIZE;
+               return 0;
+       case Opt_grpid:
+       case Opt_bsdgroups:
+               mp->m_flags |= XFS_MOUNT_GRPID;
+               return 0;
+       case Opt_nogrpid:
+       case Opt_sysvgroups:
+               mp->m_flags &= ~XFS_MOUNT_GRPID;
+               return 0;
+       case Opt_wsync:
+               mp->m_flags |= XFS_MOUNT_WSYNC;
+               return 0;
+       case Opt_norecovery:
+               mp->m_flags |= XFS_MOUNT_NORECOVERY;
+               return 0;
+       case Opt_noalign:
+               mp->m_flags |= XFS_MOUNT_NOALIGN;
+               return 0;
+       case Opt_swalloc:
+               mp->m_flags |= XFS_MOUNT_SWALLOC;
+               return 0;
+       case Opt_sunit:
+               mp->m_dalign = result.uint_32;
+               return 0;
+       case Opt_swidth:
+               mp->m_swidth = result.uint_32;
+               return 0;
+       case Opt_inode32:
+               mp->m_flags |= XFS_MOUNT_SMALL_INUMS;
+               return 0;
+       case Opt_inode64:
+               mp->m_flags &= ~XFS_MOUNT_SMALL_INUMS;
+               return 0;
+       case Opt_nouuid:
+               mp->m_flags |= XFS_MOUNT_NOUUID;
+               return 0;
+       case Opt_ikeep:
+               mp->m_flags |= XFS_MOUNT_IKEEP;
+               return 0;
+       case Opt_noikeep:
+               mp->m_flags &= ~XFS_MOUNT_IKEEP;
+               return 0;
+       case Opt_largeio:
+               mp->m_flags |= XFS_MOUNT_LARGEIO;
+               return 0;
+       case Opt_nolargeio:
+               mp->m_flags &= ~XFS_MOUNT_LARGEIO;
+               return 0;
+       case Opt_attr2:
+               mp->m_flags |= XFS_MOUNT_ATTR2;
+               return 0;
+       case Opt_noattr2:
+               mp->m_flags &= ~XFS_MOUNT_ATTR2;
+               mp->m_flags |= XFS_MOUNT_NOATTR2;
+               return 0;
+       case Opt_filestreams:
+               mp->m_flags |= XFS_MOUNT_FILESTREAMS;
+               return 0;
+       case Opt_noquota:
+               mp->m_qflags &= ~XFS_ALL_QUOTA_ACCT;
+               mp->m_qflags &= ~XFS_ALL_QUOTA_ENFD;
+               mp->m_qflags &= ~XFS_ALL_QUOTA_ACTIVE;
+               return 0;
+       case Opt_quota:
+       case Opt_uquota:
+       case Opt_usrquota:
+               mp->m_qflags |= (XFS_UQUOTA_ACCT | XFS_UQUOTA_ACTIVE |
+                                XFS_UQUOTA_ENFD);
+               return 0;
+       case Opt_qnoenforce:
+       case Opt_uqnoenforce:
+               mp->m_qflags |= (XFS_UQUOTA_ACCT | XFS_UQUOTA_ACTIVE);
+               mp->m_qflags &= ~XFS_UQUOTA_ENFD;
+               return 0;
+       case Opt_pquota:
+       case Opt_prjquota:
+               mp->m_qflags |= (XFS_PQUOTA_ACCT | XFS_PQUOTA_ACTIVE |
+                                XFS_PQUOTA_ENFD);
+               return 0;
+       case Opt_pqnoenforce:
+               mp->m_qflags |= (XFS_PQUOTA_ACCT | XFS_PQUOTA_ACTIVE);
+               mp->m_qflags &= ~XFS_PQUOTA_ENFD;
+               return 0;
+       case Opt_gquota:
+       case Opt_grpquota:
+               mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE |
+                                XFS_GQUOTA_ENFD);
+               return 0;
+       case Opt_gqnoenforce:
+               mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE);
+               mp->m_qflags &= ~XFS_GQUOTA_ENFD;
+               return 0;
+       case Opt_discard:
+               mp->m_flags |= XFS_MOUNT_DISCARD;
+               return 0;
+       case Opt_nodiscard:
+               mp->m_flags &= ~XFS_MOUNT_DISCARD;
+               return 0;
+#ifdef CONFIG_FS_DAX
+       case Opt_dax:
+               mp->m_flags |= XFS_MOUNT_DAX;
+               return 0;
+#endif
+       default:
+               xfs_warn(mp, "unknown mount option [%s].", param->key);
+               return -EINVAL;
+       }
 
-       xfs_restore_resvblks(mp);
-       xfs_log_work_queue(mp);
-       xfs_start_block_reaping(mp);
        return 0;
 }
 
-/*
- * This function fills in xfs_mount_t fields based on mount args.
- * Note: the superblock _has_ now been read in.
- */
-STATIC int
-xfs_finish_flags(
+static int
+xfs_fc_validate_params(
        struct xfs_mount        *mp)
 {
-       int                     ronly = (mp->m_flags & XFS_MOUNT_RDONLY);
-
-       /* Fail a mount where the logbuf is smaller than the log stripe */
-       if (xfs_sb_version_haslogv2(&mp->m_sb)) {
-               if (mp->m_logbsize <= 0 &&
-                   mp->m_sb.sb_logsunit > XLOG_BIG_RECORD_BSIZE) {
-                       mp->m_logbsize = mp->m_sb.sb_logsunit;
-               } else if (mp->m_logbsize > 0 &&
-                          mp->m_logbsize < mp->m_sb.sb_logsunit) {
-                       xfs_warn(mp,
-               "logbuf size must be greater than or equal to log stripe size");
-                       return -EINVAL;
-               }
-       } else {
-               /* Fail a mount if the logbuf is larger than 32K */
-               if (mp->m_logbsize > XLOG_BIG_RECORD_BSIZE) {
-                       xfs_warn(mp,
-               "logbuf size for version 1 logs must be 16K or 32K");
-                       return -EINVAL;
-               }
-       }
-
        /*
-        * V5 filesystems always use attr2 format for attributes.
+        * no recovery flag requires a read-only mount
         */
-       if (xfs_sb_version_hascrc(&mp->m_sb) &&
-           (mp->m_flags & XFS_MOUNT_NOATTR2)) {
-               xfs_warn(mp, "Cannot mount a V5 filesystem as noattr2. "
-                            "attr2 is always enabled for V5 filesystems.");
+       if ((mp->m_flags & XFS_MOUNT_NORECOVERY) &&
+           !(mp->m_flags & XFS_MOUNT_RDONLY)) {
+               xfs_warn(mp, "no-recovery mounts must be read-only.");
                return -EINVAL;
        }
 
-       /*
-        * mkfs'ed attr2 will turn on attr2 mount unless explicitly
-        * told by noattr2 to turn it off
-        */
-       if (xfs_sb_version_hasattr2(&mp->m_sb) &&
-           !(mp->m_flags & XFS_MOUNT_NOATTR2))
-               mp->m_flags |= XFS_MOUNT_ATTR2;
-
-       /*
-        * prohibit r/w mounts of read-only filesystems
-        */
-       if ((mp->m_sb.sb_flags & XFS_SBF_READONLY) && !ronly) {
+       if ((mp->m_flags & XFS_MOUNT_NOALIGN) &&
+           (mp->m_dalign || mp->m_swidth)) {
                xfs_warn(mp,
-                       "cannot mount a read-only filesystem as read-write");
-               return -EROFS;
+       "sunit and swidth options incompatible with the noalign option");
+               return -EINVAL;
        }
 
-       if ((mp->m_qflags & (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE)) &&
-           (mp->m_qflags & (XFS_PQUOTA_ACCT | XFS_PQUOTA_ACTIVE)) &&
-           !xfs_sb_version_has_pquotino(&mp->m_sb)) {
-               xfs_warn(mp,
-                 "Super block does not support project and group quota together");
+       if (!IS_ENABLED(CONFIG_XFS_QUOTA) && mp->m_qflags != 0) {
+               xfs_warn(mp, "quota support not available in this kernel.");
                return -EINVAL;
        }
 
-       return 0;
-}
-
-static int
-xfs_init_percpu_counters(
-       struct xfs_mount        *mp)
-{
-       int             error;
+       if ((mp->m_dalign && !mp->m_swidth) ||
+           (!mp->m_dalign && mp->m_swidth)) {
+               xfs_warn(mp, "sunit and swidth must be specified together");
+               return -EINVAL;
+       }
 
-       error = percpu_counter_init(&mp->m_icount, 0, GFP_KERNEL);
-       if (error)
-               return -ENOMEM;
+       if (mp->m_dalign && (mp->m_swidth % mp->m_dalign != 0)) {
+               xfs_warn(mp,
+       "stripe width (%d) must be a multiple of the stripe unit (%d)",
+                       mp->m_swidth, mp->m_dalign);
+               return -EINVAL;
+       }
 
-       error = percpu_counter_init(&mp->m_ifree, 0, GFP_KERNEL);
-       if (error)
-               goto free_icount;
+       if (mp->m_logbufs != -1 &&
+           mp->m_logbufs != 0 &&
+           (mp->m_logbufs < XLOG_MIN_ICLOGS ||
+            mp->m_logbufs > XLOG_MAX_ICLOGS)) {
+               xfs_warn(mp, "invalid logbufs value: %d [not %d-%d]",
+                       mp->m_logbufs, XLOG_MIN_ICLOGS, XLOG_MAX_ICLOGS);
+               return -EINVAL;
+       }
 
-       error = percpu_counter_init(&mp->m_fdblocks, 0, GFP_KERNEL);
-       if (error)
-               goto free_ifree;
+       if (mp->m_logbsize != -1 &&
+           mp->m_logbsize !=  0 &&
+           (mp->m_logbsize < XLOG_MIN_RECORD_BSIZE ||
+            mp->m_logbsize > XLOG_MAX_RECORD_BSIZE ||
+            !is_power_of_2(mp->m_logbsize))) {
+               xfs_warn(mp,
+                       "invalid logbufsize: %d [not 16k,32k,64k,128k or 256k]",
+                       mp->m_logbsize);
+               return -EINVAL;
+       }
 
-       error = percpu_counter_init(&mp->m_delalloc_blks, 0, GFP_KERNEL);
-       if (error)
-               goto free_fdblocks;
+       if ((mp->m_flags & XFS_MOUNT_ALLOCSIZE) &&
+           (mp->m_allocsize_log > XFS_MAX_IO_LOG ||
+            mp->m_allocsize_log < XFS_MIN_IO_LOG)) {
+               xfs_warn(mp, "invalid log iosize: %d [not %d-%d]",
+                       mp->m_allocsize_log, XFS_MIN_IO_LOG, XFS_MAX_IO_LOG);
+               return -EINVAL;
+       }
 
        return 0;
-
-free_fdblocks:
-       percpu_counter_destroy(&mp->m_fdblocks);
-free_ifree:
-       percpu_counter_destroy(&mp->m_ifree);
-free_icount:
-       percpu_counter_destroy(&mp->m_icount);
-       return -ENOMEM;
-}
-
-void
-xfs_reinit_percpu_counters(
-       struct xfs_mount        *mp)
-{
-       percpu_counter_set(&mp->m_icount, mp->m_sb.sb_icount);
-       percpu_counter_set(&mp->m_ifree, mp->m_sb.sb_ifree);
-       percpu_counter_set(&mp->m_fdblocks, mp->m_sb.sb_fdblocks);
-}
-
-static void
-xfs_destroy_percpu_counters(
-       struct xfs_mount        *mp)
-{
-       percpu_counter_destroy(&mp->m_icount);
-       percpu_counter_destroy(&mp->m_ifree);
-       percpu_counter_destroy(&mp->m_fdblocks);
-       ASSERT(XFS_FORCED_SHUTDOWN(mp) ||
-              percpu_counter_sum(&mp->m_delalloc_blks) == 0);
-       percpu_counter_destroy(&mp->m_delalloc_blks);
-}
-
-static void
-xfs_fs_put_super(
-       struct super_block      *sb)
-{
-       struct xfs_mount        *mp = XFS_M(sb);
-
-       /* if ->fill_super failed, we have no mount to tear down */
-       if (!sb->s_fs_info)
-               return;
-
-       xfs_notice(mp, "Unmounting Filesystem");
-       xfs_filestream_unmount(mp);
-       xfs_unmountfs(mp);
-
-       xfs_freesb(mp);
-       free_percpu(mp->m_stats.xs_stats);
-       xfs_destroy_percpu_counters(mp);
-       xfs_destroy_mount_workqueues(mp);
-       xfs_close_devices(mp);
-
-       sb->s_fs_info = NULL;
-       xfs_mount_free(mp);
-}
-
-static long
-xfs_fs_nr_cached_objects(
-       struct super_block      *sb,
-       struct shrink_control   *sc)
-{
-       /* Paranoia: catch incorrect calls during mount setup or teardown */
-       if (WARN_ON_ONCE(!sb->s_fs_info))
-               return 0;
-       return xfs_reclaim_inodes_count(XFS_M(sb));
-}
-
-static long
-xfs_fs_free_cached_objects(
-       struct super_block      *sb,
-       struct shrink_control   *sc)
-{
-       return xfs_reclaim_inodes_nr(XFS_M(sb), sc->nr_to_scan);
-}
-
-static const struct super_operations xfs_super_operations = {
-       .alloc_inode            = xfs_fs_alloc_inode,
-       .destroy_inode          = xfs_fs_destroy_inode,
-       .dirty_inode            = xfs_fs_dirty_inode,
-       .drop_inode             = xfs_fs_drop_inode,
-       .put_super              = xfs_fs_put_super,
-       .sync_fs                = xfs_fs_sync_fs,
-       .freeze_fs              = xfs_fs_freeze,
-       .unfreeze_fs            = xfs_fs_unfreeze,
-       .statfs                 = xfs_fs_statfs,
-       .show_options           = xfs_fs_show_options,
-       .nr_cached_objects      = xfs_fs_nr_cached_objects,
-       .free_cached_objects    = xfs_fs_free_cached_objects,
-};
-
-static struct xfs_mount *
-xfs_mount_alloc(void)
-{
-       struct xfs_mount        *mp;
-
-       mp = kmem_alloc(sizeof(struct xfs_mount), KM_ZERO);
-       if (!mp)
-               return NULL;
-
-       spin_lock_init(&mp->m_sb_lock);
-       spin_lock_init(&mp->m_agirotor_lock);
-       INIT_RADIX_TREE(&mp->m_perag_tree, GFP_ATOMIC);
-       spin_lock_init(&mp->m_perag_lock);
-       mutex_init(&mp->m_growlock);
-       atomic_set(&mp->m_active_trans, 0);
-       INIT_DELAYED_WORK(&mp->m_reclaim_work, xfs_reclaim_worker);
-       INIT_DELAYED_WORK(&mp->m_eofblocks_work, xfs_eofblocks_worker);
-       INIT_DELAYED_WORK(&mp->m_cowblocks_work, xfs_cowblocks_worker);
-       mp->m_kobj.kobject.kset = xfs_kset;
-       /*
-        * We don't create the finobt per-ag space reservation until after log
-        * recovery, so we must set this to true so that an ifree transaction
-        * started during log recovery will not depend on space reservations
-        * for finobt expansion.
-        */
-       mp->m_finobt_nores = true;
-       return mp;
 }
 
 static int