Add mount context support to NFS, parsing the options in advance and
attaching the information to the mount context.  The highlights are:

 (*) Define a new nfs_mount_context struct that merges together
     nfs_parsed_mount_data, nfs_mount_info and nfs_clone_mount.  This
     structure represents NFS's mount context.

 (*) Split out the mount option parsing routines from super.c into their
     own mount.c.  The VFS then provides a parser for when the options are
     in the form of a comma separated list of key[=val] items.

 (*) Provide a small buffer in the NFS mount context for copying numbers
     into prior to parsing them to avoid match_strdup() calls.

 (*) Pin the NFS protocol module in the mount context.

 (*) Return error information in mc->error.  This has the downside that
     these strings must be static and can't be formatted.

 (*) Remove the auxiliary file_system_type structs since the information
     necessary can be conveyed in the nfs_mount_context struct instead.

 (*) Root mounts are made by duplicating the context for the requested
     mount so as to have the same parameters.  Submounts pick up their
     parameters from the parent superblock.

Signed-off-by: David Howells <dhowe...@redhat.com>
---

 fs/nfs/Makefile         |    2 
 fs/nfs/client.c         |   74 +-
 fs/nfs/internal.h       |  136 ++--
 fs/nfs/mount.c          | 1502 ++++++++++++++++++++++++++++++++++++++++
 fs/nfs/namespace.c      |   76 +-
 fs/nfs/nfs3_fs.h        |    2 
 fs/nfs/nfs3client.c     |    6 
 fs/nfs/nfs3proc.c       |    1 
 fs/nfs/nfs4_fs.h        |    4 
 fs/nfs/nfs4client.c     |   82 +-
 fs/nfs/nfs4namespace.c  |  208 +++---
 fs/nfs/nfs4proc.c       |    1 
 fs/nfs/nfs4super.c      |  184 ++---
 fs/nfs/proc.c           |    1 
 fs/nfs/super.c          | 1761 +++--------------------------------------------
 include/linux/nfs_xdr.h |    7 
 16 files changed, 2052 insertions(+), 1995 deletions(-)
 create mode 100644 fs/nfs/mount.c

diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
index 6abdda209642..655fd5e72e60 100644
--- a/fs/nfs/Makefile
+++ b/fs/nfs/Makefile
@@ -7,7 +7,7 @@ obj-$(CONFIG_NFS_FS) += nfs.o
 CFLAGS_nfstrace.o += -I$(src)
 nfs-y                  := client.o dir.o file.o getroot.o inode.o super.o \
                           io.o direct.o pagelist.o read.o symlink.o unlink.o \
-                          write.o namespace.o mount_clnt.o nfstrace.o
+                          write.o namespace.o mount_clnt.o nfstrace.o mount.o
 nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
 nfs-$(CONFIG_SYSCTL)   += sysctl.o
 nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 04d15a0045e3..e5e5b40abcf9 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -652,17 +652,16 @@ EXPORT_SYMBOL_GPL(nfs_init_client);
  * Create a version 2 or 3 client
  */
 static int nfs_init_server(struct nfs_server *server,
-                          const struct nfs_parsed_mount_data *data,
-                          struct nfs_subversion *nfs_mod)
+                          const struct nfs_sb_config *cfg)
 {
        struct rpc_timeout timeparms;
        struct nfs_client_initdata cl_init = {
-               .hostname = data->nfs_server.hostname,
-               .addr = (const struct sockaddr *)&data->nfs_server.address,
-               .addrlen = data->nfs_server.addrlen,
-               .nfs_mod = nfs_mod,
-               .proto = data->nfs_server.protocol,
-               .net = data->net,
+               .hostname = cfg->nfs_server.hostname,
+               .addr = (const struct sockaddr *)&cfg->nfs_server.address,
+               .addrlen = cfg->nfs_server.addrlen,
+               .nfs_mod = cfg->nfs_mod,
+               .proto = cfg->nfs_server.protocol,
+               .net = cfg->sc.net_ns,
                .timeparms = &timeparms,
        };
        struct nfs_client *clp;
@@ -670,9 +669,9 @@ static int nfs_init_server(struct nfs_server *server,
 
        dprintk("--> nfs_init_server()\n");
 
-       nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
-                       data->timeo, data->retrans);
-       if (data->flags & NFS_MOUNT_NORESVPORT)
+       nfs_init_timeout_values(&timeparms, cfg->nfs_server.protocol,
+                       cfg->timeo, cfg->retrans);
+       if (cfg->flags & NFS_MOUNT_NORESVPORT)
                set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);
 
        /* Allocate or find a client reference we can use */
@@ -685,46 +684,46 @@ static int nfs_init_server(struct nfs_server *server,
        server->nfs_client = clp;
 
        /* Initialise the client representation from the mount data */
-       server->flags = data->flags;
-       server->options = data->options;
+       server->flags = cfg->flags;
+       server->options = cfg->options;
        server->caps |= NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID|
                NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|NFS_CAP_OWNER_GROUP|
                NFS_CAP_ATIME|NFS_CAP_CTIME|NFS_CAP_MTIME;
 
-       if (data->rsize)
-               server->rsize = nfs_block_size(data->rsize, NULL);
-       if (data->wsize)
-               server->wsize = nfs_block_size(data->wsize, NULL);
+       if (cfg->rsize)
+               server->rsize = nfs_block_size(cfg->rsize, NULL);
+       if (cfg->wsize)
+               server->wsize = nfs_block_size(cfg->wsize, NULL);
 
-       server->acregmin = data->acregmin * HZ;
-       server->acregmax = data->acregmax * HZ;
-       server->acdirmin = data->acdirmin * HZ;
-       server->acdirmax = data->acdirmax * HZ;
+       server->acregmin = cfg->acregmin * HZ;
+       server->acregmax = cfg->acregmax * HZ;
+       server->acdirmin = cfg->acdirmin * HZ;
+       server->acdirmax = cfg->acdirmax * HZ;
 
        /* Start lockd here, before we might error out */
        error = nfs_start_lockd(server);
        if (error < 0)
                goto error;
 
-       server->port = data->nfs_server.port;
-       server->auth_info = data->auth_info;
+       server->port = cfg->nfs_server.port;
+       server->auth_info = cfg->auth_info;
 
        error = nfs_init_server_rpcclient(server, &timeparms,
-                                         data->selected_flavor);
+                                         cfg->selected_flavor);
        if (error < 0)
                goto error;
 
        /* Preserve the values of mount_server-related mount options */
-       if (data->mount_server.addrlen) {
-               memcpy(&server->mountd_address, &data->mount_server.address,
-                       data->mount_server.addrlen);
-               server->mountd_addrlen = data->mount_server.addrlen;
+       if (cfg->mount_server.addrlen) {
+               memcpy(&server->mountd_address, &cfg->mount_server.address,
+                       cfg->mount_server.addrlen);
+               server->mountd_addrlen = cfg->mount_server.addrlen;
        }
-       server->mountd_version = data->mount_server.version;
-       server->mountd_port = data->mount_server.port;
-       server->mountd_protocol = data->mount_server.protocol;
+       server->mountd_version = cfg->mount_server.version;
+       server->mountd_port = cfg->mount_server.port;
+       server->mountd_protocol = cfg->mount_server.protocol;
 
-       server->namelen  = data->namlen;
+       server->namelen  = cfg->namlen;
        dprintk("<-- nfs_init_server() = 0 [new %p]\n", clp);
        return 0;
 
@@ -954,8 +953,7 @@ EXPORT_SYMBOL_GPL(nfs_free_server);
  * Create a version 2 or 3 volume record
  * - keyed on server and FSID
  */
-struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info,
-                                    struct nfs_subversion *nfs_mod)
+struct nfs_server *nfs_create_server(struct nfs_sb_config *cfg)
 {
        struct nfs_server *server;
        struct nfs_fattr *fattr;
@@ -971,18 +969,18 @@ struct nfs_server *nfs_create_server(struct 
nfs_mount_info *mount_info,
                goto error;
 
        /* Get a client representation */
-       error = nfs_init_server(server, mount_info->parsed, nfs_mod);
+       error = nfs_init_server(server, cfg);
        if (error < 0)
                goto error;
 
        /* Probe the root fh to retrieve its FSID */
-       error = nfs_probe_fsinfo(server, mount_info->mntfh, fattr);
+       error = nfs_probe_fsinfo(server, cfg->mntfh, fattr);
        if (error < 0)
                goto error;
        if (server->nfs_client->rpc_ops->version == 3) {
                if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN)
                        server->namelen = NFS3_MAXNAMLEN;
-               if (!(mount_info->parsed->flags & NFS_MOUNT_NORDIRPLUS))
+               if (!(cfg->flags & NFS_MOUNT_NORDIRPLUS))
                        server->caps |= NFS_CAP_READDIRPLUS;
        } else {
                if (server->namelen == 0 || server->namelen > NFS2_MAXNAMLEN)
@@ -990,7 +988,7 @@ struct nfs_server *nfs_create_server(struct nfs_mount_info 
*mount_info,
        }
 
        if (!(fattr->valid & NFS_ATTR_FATTR)) {
-               error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh, 
fattr, NULL);
+               error = cfg->nfs_mod->rpc_ops->getattr(server, cfg->mntfh, 
fattr, NULL);
                if (error < 0) {
                        dprintk("nfs_create_server: getattr error = %d\n", 
-error);
                        goto error;
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 9dc65d7ae754..f6a1c4876c2b 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -3,9 +3,10 @@
  */
 
 #include "nfs4_fs.h"
-#include <linux/mount.h>
+#include <linux/sb_config.h>
 #include <linux/security.h>
 #include <linux/crc32.h>
+#include <linux/sunrpc/addr.h>
 #include <linux/nfs_page.h>
 
 #define NFS_MS_MASK (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_SYNCHRONOUS)
@@ -35,18 +36,6 @@ static inline int nfs_attr_use_mounted_on_fileid(struct 
nfs_fattr *fattr)
        return 1;
 }
 
-struct nfs_clone_mount {
-       const struct super_block *sb;
-       const struct dentry *dentry;
-       struct nfs_fh *fh;
-       struct nfs_fattr *fattr;
-       char *hostname;
-       char *mnt_path;
-       struct sockaddr *addr;
-       size_t addrlen;
-       rpc_authflavor_t authflavor;
-};
-
 /*
  * Note: RFC 1813 doesn't limit the number of auth flavors that
  * a server can return, so make something up.
@@ -81,15 +70,27 @@ struct nfs_client_initdata {
        const struct rpc_timeout *timeparms;
 };
 
+enum nfs_mount_type {
+       NFS_MOUNT_ORDINARY,
+       NFS_MOUNT_CROSS_DEV,
+       NFS4_MOUNT_REMOTE,
+       NFS4_MOUNT_REFERRAL,
+       NFS4_MOUNT_REMOTE_REFERRAL,
+};
+
 /*
  * In-kernel mount arguments
  */
-struct nfs_parsed_mount_data {
-       int                     flags;
+struct nfs_sb_config {
+       struct sb_config        sc;
+       enum nfs_mount_type     mount_type : 8;
+       bool                    skip_remount_option_check;
+       bool                    need_mount;
+       unsigned int            flags;          /* NFS{,4}_MOUNT_* flags */
        unsigned int            rsize, wsize;
        unsigned int            timeo, retrans;
-       unsigned int            acregmin, acregmax,
-                               acdirmin, acdirmax;
+       unsigned int            acregmin, acregmax;
+       unsigned int            acdirmin, acdirmax;
        unsigned int            namlen;
        unsigned int            options;
        unsigned int            bsize;
@@ -99,10 +100,14 @@ struct nfs_parsed_mount_data {
        unsigned int            version;
        unsigned int            minorversion;
        char                    *fscache_uniq;
-       bool                    need_mount;
+       unsigned short          protofamily;
+       unsigned short          mountfamily;
 
        struct {
-               struct sockaddr_storage address;
+               union {
+                       struct sockaddr address;
+                       struct sockaddr_storage _address;
+               };
                size_t                  addrlen;
                char                    *hostname;
                u32                     version;
@@ -111,16 +116,33 @@ struct nfs_parsed_mount_data {
        } mount_server;
 
        struct {
-               struct sockaddr_storage address;
+               union {
+                       struct sockaddr address;
+                       struct sockaddr_storage _address;
+               };
                size_t                  addrlen;
                char                    *hostname;
                char                    *export_path;
                int                     port;
                unsigned short          protocol;
+               unsigned short          export_path_len;
        } nfs_server;
 
-       struct security_mnt_opts lsm_opts;
-       struct net              *net;
+       struct nfs_fh           *mntfh;
+       struct nfs_subversion   *nfs_mod;
+
+       int (*set_security)(struct super_block *, struct dentry *,
+                           struct nfs_sb_config *);
+
+       /* Information for a cloned mount. */
+       struct nfs_clone_mount {
+               struct super_block      *sb;
+               struct dentry           *dentry;
+               struct nfs_fattr        *fattr;
+               bool                    cloned;
+       } clone_data;
+
+       char                    buf[32];        /* Parse buffer */
 };
 
 /* mount_clnt.c */
@@ -138,17 +160,19 @@ struct nfs_mount_request {
        struct net              *net;
 };
 
-struct nfs_mount_info {
-       int (*fill_super)(struct super_block *, struct nfs_mount_info *);
-       int (*set_security)(struct super_block *, struct dentry *, struct 
nfs_mount_info *);
-       struct nfs_parsed_mount_data *parsed;
-       struct nfs_clone_mount *cloned;
-       struct nfs_fh *mntfh;
-};
-
 extern int nfs_mount(struct nfs_mount_request *info);
 extern void nfs_umount(const struct nfs_mount_request *info);
 
+static inline void nfs_cfg_error(struct nfs_sb_config *cfg, const char *msg)
+{
+       sb_cfg_error(&cfg->sc, msg);
+}
+
+static inline int nfs_cfg_inval(struct nfs_sb_config *cfg, const char *msg)
+{
+       return sb_cfg_inval(&cfg->sc, msg);
+}
+
 /* client.c */
 extern const struct rpc_program nfs_program;
 extern void nfs_clients_init(struct net *net);
@@ -171,13 +195,9 @@ extern struct nfs_client *nfs4_find_client_ident(struct 
net *, int);
 extern struct nfs_client *
 nfs4_find_client_sessionid(struct net *, const struct sockaddr *,
                                struct nfs4_sessionid *, u32);
-extern struct nfs_server *nfs_create_server(struct nfs_mount_info *,
-                                       struct nfs_subversion *);
-extern struct nfs_server *nfs4_create_server(
-                                       struct nfs_mount_info *,
-                                       struct nfs_subversion *);
-extern struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *,
-                                                     struct nfs_fh *);
+extern struct nfs_server *nfs_create_server(struct nfs_sb_config *);
+extern struct nfs_server *nfs4_create_server(struct nfs_sb_config *);
+extern struct nfs_server *nfs4_create_referral_server(struct nfs_sb_config *);
 extern int nfs4_update_server(struct nfs_server *server, const char *hostname,
                                        struct sockaddr *sap, size_t salen,
                                        struct net *net);
@@ -229,6 +249,11 @@ extern struct svc_version nfs4_callback_version1;
 extern struct svc_version nfs4_callback_version4;
 
 struct nfs_pageio_descriptor;
+
+/* mount.c */
+extern const char nfs_slash[];
+extern struct dentry *nfs_general_mount(struct nfs_sb_config *ctx);
+
 /* pagelist.c */
 extern int __init nfs_init_nfspagecache(void);
 extern void nfs_destroy_nfspagecache(void);
@@ -390,24 +415,13 @@ extern int nfs_wait_atomic_killable(atomic_t *p);
 /* super.c */
 extern const struct super_operations nfs_sops;
 extern struct file_system_type nfs_fs_type;
-extern struct file_system_type nfs_xdev_fs_type;
-#if IS_ENABLED(CONFIG_NFS_V4)
-extern struct file_system_type nfs4_xdev_fs_type;
-extern struct file_system_type nfs4_referral_fs_type;
-#endif
 bool nfs_auth_info_match(const struct nfs_auth_info *, rpc_authflavor_t);
-struct dentry *nfs_try_mount(int, const char *, struct nfs_mount_info *,
-                       struct nfs_subversion *);
-void nfs_initialise_sb(struct super_block *);
-int nfs_set_sb_security(struct super_block *, struct dentry *, struct 
nfs_mount_info *);
-int nfs_clone_sb_security(struct super_block *, struct dentry *, struct 
nfs_mount_info *);
-struct dentry *nfs_fs_mount_common(struct nfs_server *, int, const char *,
-                                  struct nfs_mount_info *, struct 
nfs_subversion *);
-struct dentry *nfs_fs_mount(struct file_system_type *, int, const char *, void 
*);
-struct dentry * nfs_xdev_mount_common(struct file_system_type *, int,
-               const char *, struct nfs_mount_info *);
+struct dentry *nfs_try_mount(struct nfs_sb_config *);
+int nfs_set_sb_security(struct super_block *, struct dentry *, struct 
nfs_sb_config *);
+int nfs_clone_sb_security(struct super_block *, struct dentry *, struct 
nfs_sb_config *);
+struct dentry *nfs_fs_mount_common(struct nfs_server *, struct nfs_sb_config 
*);
 void nfs_kill_super(struct super_block *);
-int nfs_fill_super(struct super_block *, struct nfs_mount_info *);
+int nfs_fill_super(struct super_block *, struct sb_config *);
 
 extern struct rpc_stat nfs_rpcstat;
 
@@ -458,14 +472,13 @@ extern void nfs_read_prepare(struct rpc_task *task, void 
*calldata);
 extern void nfs_pageio_reset_read_mds(struct nfs_pageio_descriptor *pgio);
 
 /* super.c */
-int nfs_clone_super(struct super_block *, struct nfs_mount_info *);
 void nfs_umount_begin(struct super_block *);
 int  nfs_statfs(struct dentry *, struct kstatfs *);
 int  nfs_show_options(struct seq_file *, struct dentry *);
 int  nfs_show_devname(struct seq_file *, struct dentry *);
 int  nfs_show_path(struct seq_file *, struct dentry *);
 int  nfs_show_stats(struct seq_file *, struct dentry *);
-int nfs_remount(struct super_block *sb, int *flags, char *raw_data);
+int nfs_remount(struct super_block *sb, struct sb_config *sc);
 
 /* write.c */
 extern void nfs_pageio_init_write(struct nfs_pageio_descriptor *pgio,
@@ -765,3 +778,16 @@ static inline bool nfs_error_is_fatal(int err)
                return false;
        }
 }
+
+/*
+ * Select between a default port value and a user-specified port value.
+ * If a zero value is set, then autobind will be used.
+ */
+static inline void nfs_set_port(struct sockaddr *sap, int *port,
+                               const unsigned short default_port)
+{
+       if (*port == NFS_UNSPEC_PORT)
+               *port = default_port;
+
+       rpc_set_port(sap, *port);
+}
diff --git a/fs/nfs/mount.c b/fs/nfs/mount.c
new file mode 100644
index 000000000000..180e05e83633
--- /dev/null
+++ b/fs/nfs/mount.c
@@ -0,0 +1,1502 @@
+/* NFS mount handling.
+ *
+ * Copyright (C) 2017 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowe...@redhat.com)
+ *
+ * Split from fs/nfs/super.c:
+ *
+ *  Copyright (C) 1992  Rick Sladkey
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/parser.h>
+#include <linux/nfs_fs.h>
+#include <linux/nfs_mount.h>
+#include <linux/nfs4_mount.h>
+#include "nfs.h"
+#include "internal.h"
+
+#define NFSDBG_FACILITY                NFSDBG_VFS
+
+#if IS_ENABLED(CONFIG_NFS_V3)
+#define NFS_DEFAULT_VERSION 3
+#else
+#define NFS_DEFAULT_VERSION 2
+#endif
+
+enum {
+       /* Mount options that take no arguments */
+       Opt_soft, Opt_hard,
+       Opt_posix, Opt_noposix,
+       Opt_cto, Opt_nocto,
+       Opt_ac, Opt_noac,
+       Opt_lock, Opt_nolock,
+       Opt_udp, Opt_tcp, Opt_rdma,
+       Opt_acl, Opt_noacl,
+       Opt_rdirplus, Opt_nordirplus,
+       Opt_sharecache, Opt_nosharecache,
+       Opt_resvport, Opt_noresvport,
+       Opt_fscache, Opt_nofscache,
+       Opt_migration, Opt_nomigration,
+
+       /* Mount options that take integer arguments */
+       Opt_port,
+       Opt_rsize, Opt_wsize, Opt_bsize,
+       Opt_timeo, Opt_retrans,
+       Opt_acregmin, Opt_acregmax,
+       Opt_acdirmin, Opt_acdirmax,
+       Opt_actimeo,
+       Opt_namelen,
+       Opt_mountport,
+       Opt_mountvers,
+       Opt_minorversion,
+
+       /* Mount options that take string arguments */
+       Opt_nfsvers,
+       Opt_sec, Opt_proto, Opt_mountproto, Opt_mounthost,
+       Opt_addr, Opt_mountaddr, Opt_clientaddr,
+       Opt_lookupcache,
+       Opt_fscache_uniq,
+       Opt_local_lock,
+
+       /* Special mount options */
+       Opt_userspace, Opt_deprecated, Opt_sloppy,
+
+       Opt_err
+};
+
+static const match_table_t nfs_mount_option_tokens = {
+       { Opt_userspace, "bg" },
+       { Opt_userspace, "fg" },
+       { Opt_userspace, "retry=%s" },
+
+       { Opt_sloppy, "sloppy" },
+
+       { Opt_soft, "soft" },
+       { Opt_hard, "hard" },
+       { Opt_deprecated, "intr" },
+       { Opt_deprecated, "nointr" },
+       { Opt_posix, "posix" },
+       { Opt_noposix, "noposix" },
+       { Opt_cto, "cto" },
+       { Opt_nocto, "nocto" },
+       { Opt_ac, "ac" },
+       { Opt_noac, "noac" },
+       { Opt_lock, "lock" },
+       { Opt_nolock, "nolock" },
+       { Opt_udp, "udp" },
+       { Opt_tcp, "tcp" },
+       { Opt_rdma, "rdma" },
+       { Opt_acl, "acl" },
+       { Opt_noacl, "noacl" },
+       { Opt_rdirplus, "rdirplus" },
+       { Opt_nordirplus, "nordirplus" },
+       { Opt_sharecache, "sharecache" },
+       { Opt_nosharecache, "nosharecache" },
+       { Opt_resvport, "resvport" },
+       { Opt_noresvport, "noresvport" },
+       { Opt_fscache, "fsc" },
+       { Opt_nofscache, "nofsc" },
+       { Opt_migration, "migration" },
+       { Opt_nomigration, "nomigration" },
+
+       { Opt_port, "port=%s" },
+       { Opt_rsize, "rsize=%s" },
+       { Opt_wsize, "wsize=%s" },
+       { Opt_bsize, "bsize=%s" },
+       { Opt_timeo, "timeo=%s" },
+       { Opt_retrans, "retrans=%s" },
+       { Opt_acregmin, "acregmin=%s" },
+       { Opt_acregmax, "acregmax=%s" },
+       { Opt_acdirmin, "acdirmin=%s" },
+       { Opt_acdirmax, "acdirmax=%s" },
+       { Opt_actimeo, "actimeo=%s" },
+       { Opt_namelen, "namlen=%s" },
+       { Opt_mountport, "mountport=%s" },
+       { Opt_mountvers, "mountvers=%s" },
+       { Opt_minorversion, "minorversion=%s" },
+
+       { Opt_nfsvers, "nfsvers=%s" },
+       { Opt_nfsvers, "vers=%s" },
+
+       { Opt_sec, "sec=%s" },
+       { Opt_proto, "proto=%s" },
+       { Opt_mountproto, "mountproto=%s" },
+       { Opt_addr, "addr=%s" },
+       { Opt_clientaddr, "clientaddr=%s" },
+       { Opt_mounthost, "mounthost=%s" },
+       { Opt_mountaddr, "mountaddr=%s" },
+
+       { Opt_lookupcache, "lookupcache=%s" },
+       { Opt_fscache_uniq, "fsc=%s" },
+       { Opt_local_lock, "local_lock=%s" },
+
+       /* The following needs to be listed after all other options */
+       { Opt_nfsvers, "v%s" },
+
+       { Opt_err, NULL }
+};
+
+enum {
+       Opt_xprt_udp, Opt_xprt_udp6, Opt_xprt_tcp, Opt_xprt_tcp6, Opt_xprt_rdma,
+       Opt_xprt_rdma6,
+
+       Opt_xprt_err
+};
+
+static const match_table_t nfs_xprt_protocol_tokens = {
+       { Opt_xprt_udp, "udp" },
+       { Opt_xprt_udp6, "udp6" },
+       { Opt_xprt_tcp, "tcp" },
+       { Opt_xprt_tcp6, "tcp6" },
+       { Opt_xprt_rdma, "rdma" },
+       { Opt_xprt_rdma6, "rdma6" },
+
+       { Opt_xprt_err, NULL }
+};
+
+enum {
+       Opt_sec_none, Opt_sec_sys,
+       Opt_sec_krb5, Opt_sec_krb5i, Opt_sec_krb5p,
+       Opt_sec_lkey, Opt_sec_lkeyi, Opt_sec_lkeyp,
+       Opt_sec_spkm, Opt_sec_spkmi, Opt_sec_spkmp,
+
+       Opt_sec_err
+};
+
+static const match_table_t nfs_secflavor_tokens = {
+       { Opt_sec_none, "none" },
+       { Opt_sec_none, "null" },
+       { Opt_sec_sys, "sys" },
+
+       { Opt_sec_krb5, "krb5" },
+       { Opt_sec_krb5i, "krb5i" },
+       { Opt_sec_krb5p, "krb5p" },
+
+       { Opt_sec_lkey, "lkey" },
+       { Opt_sec_lkeyi, "lkeyi" },
+       { Opt_sec_lkeyp, "lkeyp" },
+
+       { Opt_sec_spkm, "spkm3" },
+       { Opt_sec_spkmi, "spkm3i" },
+       { Opt_sec_spkmp, "spkm3p" },
+
+       { Opt_sec_err, NULL }
+};
+
+enum {
+       Opt_lookupcache_all, Opt_lookupcache_positive,
+       Opt_lookupcache_none,
+
+       Opt_lookupcache_err
+};
+
+static const match_table_t nfs_lookupcache_tokens = {
+       { Opt_lookupcache_all, "all" },
+       { Opt_lookupcache_positive, "pos" },
+       { Opt_lookupcache_positive, "positive" },
+       { Opt_lookupcache_none, "none" },
+
+       { Opt_lookupcache_err, NULL }
+};
+
+enum {
+       Opt_local_lock_all, Opt_local_lock_flock, Opt_local_lock_posix,
+       Opt_local_lock_none,
+
+       Opt_local_lock_err
+};
+
+static const match_table_t nfs_local_lock_tokens = {
+       { Opt_local_lock_all, "all" },
+       { Opt_local_lock_flock, "flock" },
+       { Opt_local_lock_posix, "posix" },
+       { Opt_local_lock_none, "none" },
+
+       { Opt_local_lock_err, NULL }
+};
+
+enum {
+       Opt_vers_2, Opt_vers_3, Opt_vers_4, Opt_vers_4_0,
+       Opt_vers_4_1, Opt_vers_4_2,
+
+       Opt_vers_err
+};
+
+static const match_table_t nfs_vers_tokens = {
+       { Opt_vers_2, "2" },
+       { Opt_vers_3, "3" },
+       { Opt_vers_4, "4" },
+       { Opt_vers_4_0, "4.0" },
+       { Opt_vers_4_1, "4.1" },
+       { Opt_vers_4_2, "4.2" },
+
+       { Opt_vers_err, NULL }
+};
+
+const char nfs_slash[] = "/";
+EXPORT_SYMBOL_GPL(nfs_slash);
+
+/*
+ * Sanity-check a server address provided by the mount command.
+ *
+ * Address family must be initialized, and address must not be
+ * the ANY address for that family.
+ */
+static int nfs_verify_server_address(struct sockaddr *addr)
+{
+       switch (addr->sa_family) {
+       case AF_INET: {
+               struct sockaddr_in *sa = (struct sockaddr_in *)addr;
+               return sa->sin_addr.s_addr != htonl(INADDR_ANY);
+       }
+       case AF_INET6: {
+               struct in6_addr *sa = &((struct sockaddr_in6 *)addr)->sin6_addr;
+               return !ipv6_addr_any(sa);
+       }
+       }
+
+       dfprintk(MOUNT, "NFS: Invalid IP address specified\n");
+       return 0;
+}
+
+/*
+ * Sanity check the NFS transport protocol.
+ *
+ */
+static void nfs_validate_transport_protocol(struct nfs_sb_config *mnt)
+{
+       switch (mnt->nfs_server.protocol) {
+       case XPRT_TRANSPORT_UDP:
+       case XPRT_TRANSPORT_TCP:
+       case XPRT_TRANSPORT_RDMA:
+               break;
+       default:
+               mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP;
+       }
+}
+
+/*
+ * For text based NFSv2/v3 mounts, the mount protocol transport default
+ * settings should depend upon the specified NFS transport.
+ */
+static void nfs_set_mount_transport_protocol(struct nfs_sb_config *mnt)
+{
+       nfs_validate_transport_protocol(mnt);
+
+       if (mnt->mount_server.protocol == XPRT_TRANSPORT_UDP ||
+           mnt->mount_server.protocol == XPRT_TRANSPORT_TCP)
+                       return;
+       switch (mnt->nfs_server.protocol) {
+       case XPRT_TRANSPORT_UDP:
+               mnt->mount_server.protocol = XPRT_TRANSPORT_UDP;
+               break;
+       case XPRT_TRANSPORT_TCP:
+       case XPRT_TRANSPORT_RDMA:
+               mnt->mount_server.protocol = XPRT_TRANSPORT_TCP;
+       }
+}
+
+/*
+ * Add 'flavor' to 'auth_info' if not already present.
+ * Returns true if 'flavor' ends up in the list, false otherwise
+ */
+static int nfs_auth_info_add(struct nfs_sb_config *cfg,
+                            struct nfs_auth_info *auth_info,
+                            rpc_authflavor_t flavor)
+{
+       unsigned int i;
+       unsigned int max_flavor_len = ARRAY_SIZE(auth_info->flavors);
+
+       /* make sure this flavor isn't already in the list */
+       for (i = 0; i < auth_info->flavor_len; i++) {
+               if (flavor == auth_info->flavors[i])
+                       return 0;
+       }
+
+       if (auth_info->flavor_len + 1 >= max_flavor_len)
+               return nfs_cfg_inval(cfg, "NFS: too many sec= flavors");
+
+       auth_info->flavors[auth_info->flavor_len++] = flavor;
+       return 0;
+}
+
+/*
+ * Parse the value of the 'sec=' option.
+ */
+static int nfs_parse_security_flavors(struct nfs_sb_config *cfg, char *value)
+{
+       substring_t args[MAX_OPT_ARGS];
+       rpc_authflavor_t pseudoflavor;
+       char *p;
+       int ret;
+
+       dfprintk(MOUNT, "NFS: parsing sec=%s option\n", value);
+
+       while ((p = strsep(&value, ":")) != NULL) {
+               switch (match_token(p, nfs_secflavor_tokens, args)) {
+               case Opt_sec_none:
+                       pseudoflavor = RPC_AUTH_NULL;
+                       break;
+               case Opt_sec_sys:
+                       pseudoflavor = RPC_AUTH_UNIX;
+                       break;
+               case Opt_sec_krb5:
+                       pseudoflavor = RPC_AUTH_GSS_KRB5;
+                       break;
+               case Opt_sec_krb5i:
+                       pseudoflavor = RPC_AUTH_GSS_KRB5I;
+                       break;
+               case Opt_sec_krb5p:
+                       pseudoflavor = RPC_AUTH_GSS_KRB5P;
+                       break;
+               case Opt_sec_lkey:
+                       pseudoflavor = RPC_AUTH_GSS_LKEY;
+                       break;
+               case Opt_sec_lkeyi:
+                       pseudoflavor = RPC_AUTH_GSS_LKEYI;
+                       break;
+               case Opt_sec_lkeyp:
+                       pseudoflavor = RPC_AUTH_GSS_LKEYP;
+                       break;
+               case Opt_sec_spkm:
+                       pseudoflavor = RPC_AUTH_GSS_SPKM;
+                       break;
+               case Opt_sec_spkmi:
+                       pseudoflavor = RPC_AUTH_GSS_SPKMI;
+                       break;
+               case Opt_sec_spkmp:
+                       pseudoflavor = RPC_AUTH_GSS_SPKMP;
+                       break;
+               default:
+                       return nfs_cfg_inval(cfg, "NFS: sec= option not 
recognized");
+               }
+
+               ret = nfs_auth_info_add(cfg, &cfg->auth_info, pseudoflavor);
+               if (ret < 0)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int nfs_parse_version_string(struct nfs_sb_config *cfg,
+                                   char *string,
+                                   substring_t *args)
+{
+       cfg->flags &= ~NFS_MOUNT_VER3;
+       switch (match_token(string, nfs_vers_tokens, args)) {
+       case Opt_vers_2:
+               cfg->version = 2;
+               break;
+       case Opt_vers_3:
+               cfg->flags |= NFS_MOUNT_VER3;
+               cfg->version = 3;
+               break;
+       case Opt_vers_4:
+               /* Backward compatibility option. In future,
+                * the mount program should always supply
+                * a NFSv4 minor version number.
+                */
+               cfg->version = 4;
+               break;
+       case Opt_vers_4_0:
+               cfg->version = 4;
+               cfg->minorversion = 0;
+               break;
+       case Opt_vers_4_1:
+               cfg->version = 4;
+               cfg->minorversion = 1;
+               break;
+       case Opt_vers_4_2:
+               cfg->version = 4;
+               cfg->minorversion = 2;
+               break;
+       default:
+               return nfs_cfg_inval(cfg, "NFS: Unsupported NFS version");
+       }
+       return 0;
+}
+
+static int nfs_get_option_str(substring_t args[], char **option)
+{
+       kfree(*option);
+       *option = match_strdup(args);
+       return !*option;
+}
+
+static int nfs_get_option_ui(struct nfs_sb_config *cfg,
+                            substring_t args[], unsigned int *option)
+{
+       match_strlcpy(cfg->buf, args, sizeof(cfg->buf));
+       return kstrtouint(cfg->buf, 10, option);
+}
+
+static int nfs_get_option_ui_bound(struct nfs_sb_config *cfg,
+                                  substring_t args[], unsigned int *option,
+                                  unsigned int l_bound, unsigned u_bound)
+{
+       int ret;
+
+       match_strlcpy(cfg->buf, args, sizeof(cfg->buf));
+       ret = kstrtouint(cfg->buf, 10, option);
+       if (ret < 0)
+               return ret;
+       if (*option < l_bound || *option > u_bound)
+               return -ERANGE;
+       return 0;
+}
+
+/*
+ * Split sc->device into "hostname:export_path".
+ *
+ * The leftmost colon demarks the split between the server's hostname
+ * and the export path.  If the hostname starts with a left square
+ * bracket, then it may contain colons.
+ *
+ * Note: caller frees hostname and export path, even on error.
+ */
+static int nfs_parse_devname(struct nfs_sb_config *cfg,
+                            size_t maxnamlen, size_t maxpathlen)
+{
+       char *dev_name = cfg->sc.device;
+       size_t len;
+       char *end;
+
+       /* Is the host name protected with square brakcets? */
+       if (*dev_name == '[') {
+               end = strchr(++dev_name, ']');
+               if (end == NULL || end[1] != ':')
+                       goto out_bad_devname;
+
+               len = end - dev_name;
+               end++;
+       } else {
+               char *comma;
+
+               end = strchr(dev_name, ':');
+               if (end == NULL)
+                       goto out_bad_devname;
+               len = end - dev_name;
+
+               /* kill possible hostname list: not supported */
+               comma = strchr(dev_name, ',');
+               if (comma != NULL && comma < end)
+                       *comma = 0;
+       }
+
+       if (len > maxnamlen)
+               goto out_hostname;
+
+       /* N.B. caller will free nfs_server.hostname in all cases */
+       cfg->nfs_server.hostname = kmemdup_nul(dev_name, len, GFP_KERNEL);
+       if (!cfg->nfs_server.hostname)
+               goto out_nomem;
+       len = strlen(++end);
+       if (len > maxpathlen)
+               goto out_path;
+       cfg->nfs_server.export_path = kmemdup_nul(end, len, GFP_KERNEL);
+       if (!cfg->nfs_server.export_path)
+               goto out_nomem;
+
+       dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", cfg->nfs_server.export_path);
+       return 0;
+
+out_bad_devname:
+       return nfs_cfg_inval(cfg, "NFS: device name not in host:path format");
+out_nomem:
+       nfs_cfg_error(cfg, "NFS: not enough memory to parse device name");
+       return -ENOMEM;
+out_hostname:
+       nfs_cfg_error(cfg, "NFS: server hostname too long");
+       return -ENAMETOOLONG;
+out_path:
+       nfs_cfg_error(cfg, "NFS: export pathname too long");
+       return -ENAMETOOLONG;
+}
+
+/*
+ * Parse monolithic NFS2/NFS3 mount data
+ * - fills in the mount root filehandle
+ *
+ * For option strings, user space handles the following behaviors:
+ *
+ * + DNS: mapping server host name to IP address ("addr=" option)
+ *
+ * + failure mode: how to behave if a mount request can't be handled
+ *   immediately ("fg/bg" option)
+ *
+ * + retry: how often to retry a mount request ("retry=" option)
+ *
+ * + breaking back: trying proto=udp after proto=tcp, v2 after v3,
+ *   mountproto=tcp after mountproto=udp, and so on
+ */
+static int nfs23_monolithic_mount_data(struct sb_config *sc,
+                                      struct nfs_mount_data *data)
+{
+       struct nfs_sb_config *cfg = container_of(sc, struct nfs_sb_config, sc);
+       struct nfs_fh *mntfh = cfg->mntfh;
+       struct sockaddr *sap = (struct sockaddr *)&cfg->nfs_server.address;
+       int extra_flags = NFS_MOUNT_LEGACY_INTERFACE;
+       int ret;
+
+       if (data == NULL)
+               goto out_no_data;
+
+       cfg->version = NFS_DEFAULT_VERSION;
+       switch (data->version) {
+       case 1:
+               data->namlen = 0;
+       case 2:
+               data->bsize = 0;
+       case 3:
+               if (data->flags & NFS_MOUNT_VER3)
+                       goto out_no_v3;
+               data->root.size = NFS2_FHSIZE;
+               memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE);
+               /* Turn off security negotiation */
+               extra_flags |= NFS_MOUNT_SECFLAVOUR;
+       case 4:
+               if (data->flags & NFS_MOUNT_SECFLAVOUR)
+                       goto out_no_sec;
+       case 5:
+               memset(data->context, 0, sizeof(data->context));
+       case 6:
+               if (data->flags & NFS_MOUNT_VER3) {
+                       if (data->root.size > NFS3_FHSIZE || data->root.size == 
0)
+                               goto out_invalid_fh;
+                       mntfh->size = data->root.size;
+                       cfg->version = 3;
+               } else {
+                       mntfh->size = NFS2_FHSIZE;
+                       cfg->version = 2;
+               }
+
+               memcpy(mntfh->data, data->root.data, mntfh->size);
+               if (mntfh->size < sizeof(mntfh->data))
+                       memset(mntfh->data + mntfh->size, 0,
+                              sizeof(mntfh->data) - mntfh->size);
+
+               /*
+                * Translate to nfs_sb_config, which nfs_fill_super
+                * can deal with.
+                */
+               cfg->flags      = data->flags & NFS_MOUNT_FLAGMASK;
+               cfg->flags      |= extra_flags;
+               cfg->rsize      = data->rsize;
+               cfg->wsize      = data->wsize;
+               cfg->timeo      = data->timeo;
+               cfg->retrans    = data->retrans;
+               cfg->acregmin   = data->acregmin;
+               cfg->acregmax   = data->acregmax;
+               cfg->acdirmin   = data->acdirmin;
+               cfg->acdirmax   = data->acdirmax;
+               cfg->need_mount = false;
+
+               memcpy(sap, &data->addr, sizeof(data->addr));
+               cfg->nfs_server.addrlen = sizeof(data->addr);
+               cfg->nfs_server.port = ntohs(data->addr.sin_port);
+               if (!nfs_verify_server_address(sap))
+                       goto out_no_address;
+
+               if (!(data->flags & NFS_MOUNT_TCP))
+                       cfg->nfs_server.protocol = XPRT_TRANSPORT_UDP;
+               /* N.B. caller will free nfs_server.hostname in all cases */
+               cfg->nfs_server.hostname = kstrdup(data->hostname, GFP_KERNEL);
+               if (!cfg->nfs_server.hostname)
+                       goto out_nomem;
+
+               cfg->namlen     = data->namlen;
+               cfg->bsize      = data->bsize;
+
+               if (data->flags & NFS_MOUNT_SECFLAVOUR)
+                       cfg->selected_flavor = data->pseudoflavor;
+               else
+                       cfg->selected_flavor = RPC_AUTH_UNIX;
+
+               if (!(data->flags & NFS_MOUNT_NONLM))
+                       cfg->flags &= ~(NFS_MOUNT_LOCAL_FLOCK |
+                                        NFS_MOUNT_LOCAL_FCNTL);
+               else
+                       cfg->flags |= (NFS_MOUNT_LOCAL_FLOCK |
+                                       NFS_MOUNT_LOCAL_FCNTL);
+
+               /* The legacy version 6 binary mount data from userspace has a
+                * field used only to transport selinux information into the
+                * the kernel.  To continue to support that functionality we
+                * have a touch of selinux knowledge here in the NFS code. The
+                * userspace code converted context=blah to just blah so we are
+                * converting back to the full string selinux understands.
+                */
+               if (data->context[0]){
+#ifdef CONFIG_SECURITY_SELINUX
+                       char *opts_str = kmalloc(sizeof(data->context) + 8, 
GFP_KERNEL);
+                       if (!opts_str)
+                               return -ENOMEM;
+                       strcpy(opts_str, "context=");
+                       data->context[NFS_MAX_CONTEXT_LEN] = '\0';
+                       strcat(opts_str, &data->context[0]);
+                       ret = vfs_parse_mount_option(sc, opts_str);
+                       kfree(opts_str);
+                       if (ret)
+                               return ret;
+#else
+                       return -EINVAL;
+#endif
+               }
+
+               break;
+       default:
+               return generic_monolithic_mount_data(sc, data);
+       }
+
+       cfg->skip_remount_option_check = true;
+       return 0;
+
+out_no_data:
+       if (sc->ms_flags & MS_REMOUNT) {
+               cfg->skip_remount_option_check = true;
+               return 0;
+       }
+       return nfs_cfg_inval(cfg, "NFS: mount program didn't pass any mount 
data");
+
+out_no_v3:
+       return nfs_cfg_inval(cfg, "NFS: nfs_mount_data version does not support 
v3");
+
+out_no_sec:
+       return nfs_cfg_inval(cfg, "NFS: nfs_mount_data version supports only 
AUTH_SYS");
+
+out_nomem:
+       dfprintk(MOUNT, "NFS: not enough memory to handle mount options");
+       return -ENOMEM;
+
+out_no_address:
+       return nfs_cfg_inval(cfg, "NFS: mount program didn't pass remote 
address");
+
+out_invalid_fh:
+       return nfs_cfg_inval(cfg, "NFS: invalid root filehandle");
+}
+
+/*
+ * Validate NFSv4 mount options
+ */
+static int nfs4_monolithic_mount_data(struct sb_config *sc,
+                                     struct nfs4_mount_data *data)
+{
+       struct nfs_sb_config *cfg = container_of(sc, struct nfs_sb_config, sc);
+       struct sockaddr *sap = (struct sockaddr *)&cfg->nfs_server.address;
+       char *c;
+
+       if (data == NULL)
+               goto out_no_data;
+
+       cfg->version = 4;
+
+       switch (data->version) {
+       case 1:
+               if (data->host_addrlen > sizeof(cfg->nfs_server.address))
+                       goto out_no_address;
+               if (data->host_addrlen == 0)
+                       goto out_no_address;
+               cfg->nfs_server.addrlen = data->host_addrlen;
+               if (copy_from_user(sap, data->host_addr, data->host_addrlen))
+                       return -EFAULT;
+               if (!nfs_verify_server_address(sap))
+                       goto out_no_address;
+               cfg->nfs_server.port = ntohs(((struct sockaddr_in 
*)sap)->sin_port);
+
+               if (data->auth_flavourlen) {
+                       rpc_authflavor_t pseudoflavor;
+                       if (data->auth_flavourlen > 1)
+                               goto out_inval_auth;
+                       if (copy_from_user(&pseudoflavor,
+                                          data->auth_flavours,
+                                          sizeof(pseudoflavor)))
+                               return -EFAULT;
+                       cfg->selected_flavor = pseudoflavor;
+               } else
+                       cfg->selected_flavor = RPC_AUTH_UNIX;
+
+               c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN);
+               if (IS_ERR(c))
+                       return PTR_ERR(c);
+               cfg->nfs_server.hostname = c;
+
+               c = strndup_user(data->mnt_path.data, NFS4_MAXPATHLEN);
+               if (IS_ERR(c))
+                       return PTR_ERR(c);
+               cfg->nfs_server.export_path = c;
+               dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", c);
+
+               c = strndup_user(data->client_addr.data, 16);
+               if (IS_ERR(c))
+                       return PTR_ERR(c);
+               cfg->client_address = c;
+
+               /*
+                * Translate to nfs_sb_config, which nfs4_fill_super
+                * can deal with.
+                */
+
+               cfg->flags      = data->flags & NFS4_MOUNT_FLAGMASK;
+               cfg->rsize      = data->rsize;
+               cfg->wsize      = data->wsize;
+               cfg->timeo      = data->timeo;
+               cfg->retrans    = data->retrans;
+               cfg->acregmin   = data->acregmin;
+               cfg->acregmax   = data->acregmax;
+               cfg->acdirmin   = data->acdirmin;
+               cfg->acdirmax   = data->acdirmax;
+               cfg->nfs_server.protocol = data->proto;
+               nfs_validate_transport_protocol(cfg);
+               if (cfg->nfs_server.protocol == XPRT_TRANSPORT_UDP)
+                       goto out_invalid_transport_udp;
+
+               break;
+       default:
+               return generic_monolithic_mount_data(sc, data);
+       }
+
+       cfg->skip_remount_option_check = true;
+       return 0;
+
+out_no_data:
+       if (sc->ms_flags & MS_REMOUNT) {
+               cfg->skip_remount_option_check = true;
+               return 0;
+       }
+       return nfs_cfg_inval(cfg, "NFS4: mount program didn't pass any mount 
data");
+
+out_inval_auth:
+       return nfs_cfg_inval(cfg, "NFS4: Invalid number of RPC auth flavours");
+
+out_no_address:
+       return nfs_cfg_inval(cfg, "NFS4: mount program didn't pass remote 
address");
+
+out_invalid_transport_udp:
+       return nfs_cfg_inval(cfg, "NFSv4: Unsupported transport protocol udp");
+}
+
+/*
+ * Parse a monolithic block of data from sys_mount().
+ */
+static int nfs_monolithic_mount_data(struct sb_config *sc, void *data)
+{
+       struct nfs_sb_config *cfg = container_of(sc, struct nfs_sb_config, sc);
+
+       if (sc->fs_type == &nfs_fs_type)
+               return nfs23_monolithic_mount_data(sc, data);
+
+#if IS_ENABLED(CONFIG_NFS_V4)
+       if (sc->fs_type == &nfs4_fs_type)
+               return nfs4_monolithic_mount_data(sc, data);
+#endif
+
+       return nfs_cfg_inval(cfg, "NFS: Unsupported monolithic data version");
+}
+
+/*
+ * Parse a single mount option in "key[=val]" form.
+ */
+static int nfs_sb_config_parse_option(struct sb_config *sc, char *p)
+{
+       struct nfs_sb_config *cfg = container_of(sc, struct nfs_sb_config, sc);
+       substring_t args[MAX_OPT_ARGS];
+       char *string;
+       int ret, token;
+
+       dfprintk(MOUNT, "NFS:   parsing nfs mount option '%s'\n", p);
+
+       token = match_token(p, nfs_mount_option_tokens, args);
+       switch (token) {
+               /*
+                * boolean options:  foo/nofoo
+                */
+       case Opt_soft:
+               cfg->flags |= NFS_MOUNT_SOFT;
+               break;
+       case Opt_hard:
+               cfg->flags &= ~NFS_MOUNT_SOFT;
+               break;
+       case Opt_posix:
+               cfg->flags |= NFS_MOUNT_POSIX;
+               break;
+       case Opt_noposix:
+               cfg->flags &= ~NFS_MOUNT_POSIX;
+               break;
+       case Opt_cto:
+               cfg->flags &= ~NFS_MOUNT_NOCTO;
+               break;
+       case Opt_nocto:
+               cfg->flags |= NFS_MOUNT_NOCTO;
+               break;
+       case Opt_ac:
+               cfg->flags &= ~NFS_MOUNT_NOAC;
+               break;
+       case Opt_noac:
+               cfg->flags |= NFS_MOUNT_NOAC;
+               break;
+       case Opt_lock:
+               cfg->flags &= ~NFS_MOUNT_NONLM;
+               cfg->flags &= ~(NFS_MOUNT_LOCAL_FLOCK | NFS_MOUNT_LOCAL_FCNTL);
+               break;
+       case Opt_nolock:
+               cfg->flags |= NFS_MOUNT_NONLM;
+               cfg->flags |= (NFS_MOUNT_LOCAL_FLOCK | NFS_MOUNT_LOCAL_FCNTL);
+               break;
+       case Opt_udp:
+               cfg->flags &= ~NFS_MOUNT_TCP;
+               cfg->nfs_server.protocol = XPRT_TRANSPORT_UDP;
+               break;
+       case Opt_tcp:
+               cfg->flags |= NFS_MOUNT_TCP;
+               cfg->nfs_server.protocol = XPRT_TRANSPORT_TCP;
+               break;
+       case Opt_rdma:
+               cfg->flags |= NFS_MOUNT_TCP; /* for side protocols */
+               cfg->nfs_server.protocol = XPRT_TRANSPORT_RDMA;
+               xprt_load_transport(p);
+               break;
+       case Opt_acl:
+               cfg->flags &= ~NFS_MOUNT_NOACL;
+               break;
+       case Opt_noacl:
+               cfg->flags |= NFS_MOUNT_NOACL;
+               break;
+       case Opt_rdirplus:
+               cfg->flags &= ~NFS_MOUNT_NORDIRPLUS;
+               break;
+       case Opt_nordirplus:
+               cfg->flags |= NFS_MOUNT_NORDIRPLUS;
+               break;
+       case Opt_sharecache:
+               cfg->flags &= ~NFS_MOUNT_UNSHARED;
+               break;
+       case Opt_nosharecache:
+               cfg->flags |= NFS_MOUNT_UNSHARED;
+               break;
+       case Opt_resvport:
+               cfg->flags &= ~NFS_MOUNT_NORESVPORT;
+               break;
+       case Opt_noresvport:
+               cfg->flags |= NFS_MOUNT_NORESVPORT;
+               break;
+       case Opt_fscache:
+               cfg->options |= NFS_OPTION_FSCACHE;
+               kfree(cfg->fscache_uniq);
+               cfg->fscache_uniq = NULL;
+               break;
+       case Opt_nofscache:
+               cfg->options &= ~NFS_OPTION_FSCACHE;
+               kfree(cfg->fscache_uniq);
+               cfg->fscache_uniq = NULL;
+               break;
+       case Opt_migration:
+               cfg->options |= NFS_OPTION_MIGRATION;
+               break;
+       case Opt_nomigration:
+               cfg->options &= NFS_OPTION_MIGRATION;
+               break;
+
+               /*
+                * options that take numeric values
+                */
+       case Opt_port:
+               if (nfs_get_option_ui_bound(cfg, args, &cfg->nfs_server.port,
+                                           0, USHRT_MAX))
+                       goto out_invalid_value;
+               break;
+       case Opt_rsize:
+               if (nfs_get_option_ui(cfg, args, &cfg->rsize))
+                       goto out_invalid_value;
+               break;
+       case Opt_wsize:
+               if (nfs_get_option_ui(cfg, args, &cfg->wsize))
+                       goto out_invalid_value;
+               break;
+       case Opt_bsize:
+               if (nfs_get_option_ui(cfg, args, &cfg->bsize))
+                       goto out_invalid_value;
+               break;
+       case Opt_timeo:
+               if (nfs_get_option_ui_bound(cfg, args, &cfg->timeo, 1, INT_MAX))
+                       goto out_invalid_value;
+               break;
+       case Opt_retrans:
+               if (nfs_get_option_ui_bound(cfg, args, &cfg->retrans, 0, 
INT_MAX))
+                       goto out_invalid_value;
+               break;
+       case Opt_acregmin:
+               if (nfs_get_option_ui(cfg, args, &cfg->acregmin))
+                       goto out_invalid_value;
+               break;
+       case Opt_acregmax:
+               if (nfs_get_option_ui(cfg, args, &cfg->acregmax))
+                       goto out_invalid_value;
+               break;
+       case Opt_acdirmin:
+               if (nfs_get_option_ui(cfg, args, &cfg->acdirmin))
+                       goto out_invalid_value;
+               break;
+       case Opt_acdirmax:
+               if (nfs_get_option_ui(cfg, args, &cfg->acdirmax))
+                       goto out_invalid_value;
+               break;
+       case Opt_actimeo:
+               if (nfs_get_option_ui(cfg, args, &cfg->acdirmax))
+                       goto out_invalid_value;
+               cfg->acregmin = cfg->acregmax =
+                       cfg->acdirmin = cfg->acdirmax;
+               break;
+       case Opt_namelen:
+               if (nfs_get_option_ui(cfg, args, &cfg->namlen))
+                       goto out_invalid_value;
+               break;
+       case Opt_mountport:
+               if (nfs_get_option_ui_bound(cfg, args, &cfg->mount_server.port,
+                                           0, USHRT_MAX))
+                       goto out_invalid_value;
+               break;
+       case Opt_mountvers:
+               if (nfs_get_option_ui_bound(cfg, args, 
&cfg->mount_server.version,
+                                           NFS_MNT_VERSION, NFS_MNT3_VERSION))
+                       goto out_invalid_value;
+               break;
+       case Opt_minorversion:
+               if (nfs_get_option_ui_bound(cfg, args, &cfg->minorversion,
+                                           0, NFS4_MAX_MINOR_VERSION))
+                       goto out_invalid_value;
+               break;
+
+               /*
+                * options that take text values
+                */
+       case Opt_nfsvers:
+               string = match_strdup(args);
+               if (string == NULL)
+                       goto out_nomem;
+               ret = nfs_parse_version_string(cfg, string, args);
+               kfree(string);
+               if (ret < 0)
+                       return ret;
+               break;
+       case Opt_sec:
+               string = match_strdup(args);
+               if (string == NULL)
+                       goto out_nomem;
+               ret = nfs_parse_security_flavors(cfg, string);
+               kfree(string);
+               if (ret < 0)
+                       return ret;
+               break;
+       case Opt_proto:
+               string = match_strdup(args);
+               if (string == NULL)
+                       goto out_nomem;
+               token = match_token(string, nfs_xprt_protocol_tokens, args);
+
+               cfg->protofamily = AF_INET;
+               switch (token) {
+               case Opt_xprt_udp6:
+                       cfg->protofamily = AF_INET6;
+               case Opt_xprt_udp:
+                       cfg->flags &= ~NFS_MOUNT_TCP;
+                       cfg->nfs_server.protocol = XPRT_TRANSPORT_UDP;
+                       break;
+               case Opt_xprt_tcp6:
+                       cfg->protofamily = AF_INET6;
+               case Opt_xprt_tcp:
+                       cfg->flags |= NFS_MOUNT_TCP;
+                       cfg->nfs_server.protocol = XPRT_TRANSPORT_TCP;
+                       break;
+               case Opt_xprt_rdma6:
+                       cfg->protofamily = AF_INET6;
+               case Opt_xprt_rdma:
+                       /* vector side protocols to TCP */
+                       cfg->flags |= NFS_MOUNT_TCP;
+                       cfg->nfs_server.protocol = XPRT_TRANSPORT_RDMA;
+                       xprt_load_transport(string);
+                       break;
+               default:
+                       kfree(string);
+                       return nfs_cfg_inval(cfg, "NFS: Unrecognized transport 
protocol");
+               }
+               kfree(string);
+               break;
+       case Opt_mountproto:
+               string = match_strdup(args);
+               if (string == NULL)
+                       goto out_nomem;
+               token = match_token(string, nfs_xprt_protocol_tokens, args);
+               kfree(string);
+
+               cfg->mountfamily = AF_INET;
+               switch (token) {
+               case Opt_xprt_udp6:
+                       cfg->mountfamily = AF_INET6;
+               case Opt_xprt_udp:
+                       cfg->mount_server.protocol = XPRT_TRANSPORT_UDP;
+                       break;
+               case Opt_xprt_tcp6:
+                       cfg->mountfamily = AF_INET6;
+               case Opt_xprt_tcp:
+                       cfg->mount_server.protocol = XPRT_TRANSPORT_TCP;
+                       break;
+               case Opt_xprt_rdma: /* not used for side protocols */
+               default:
+                       return nfs_cfg_inval(cfg, "NFS: Unrecognized transport 
protocol");
+               }
+               break;
+       case Opt_addr:
+               string = match_strdup(args);
+               if (string == NULL)
+                       goto out_nomem;
+               cfg->nfs_server.addrlen =
+                       rpc_pton(sc->net_ns, string, strlen(string),
+                                (struct sockaddr *)&cfg->nfs_server.address,
+                                sizeof(cfg->nfs_server._address));
+               kfree(string);
+               if (cfg->nfs_server.addrlen == 0)
+                       goto out_invalid_address;
+               break;
+       case Opt_clientaddr:
+               if (nfs_get_option_str(args, &cfg->client_address))
+                       goto out_nomem;
+               break;
+       case Opt_mounthost:
+               if (nfs_get_option_str(args, &cfg->mount_server.hostname))
+                       goto out_nomem;
+               break;
+       case Opt_mountaddr:
+               string = match_strdup(args);
+               if (string == NULL)
+                       goto out_nomem;
+               cfg->mount_server.addrlen =
+                       rpc_pton(sc->net_ns, string, strlen(string),
+                                (struct sockaddr *)&cfg->mount_server.address,
+                                sizeof(cfg->mount_server._address));
+               kfree(string);
+               if (cfg->mount_server.addrlen == 0)
+                       goto out_invalid_address;
+               break;
+       case Opt_lookupcache:
+               string = match_strdup(args);
+               if (string == NULL)
+                       goto out_nomem;
+               token = match_token(string, nfs_lookupcache_tokens, args);
+               kfree(string);
+               switch (token) {
+               case Opt_lookupcache_all:
+                       cfg->flags &= 
~(NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE);
+                       break;
+               case Opt_lookupcache_positive:
+                       cfg->flags &= ~NFS_MOUNT_LOOKUP_CACHE_NONE;
+                       cfg->flags |= NFS_MOUNT_LOOKUP_CACHE_NONEG;
+                       break;
+               case Opt_lookupcache_none:
+                       cfg->flags |= 
NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE;
+                       break;
+               default:
+                       return nfs_cfg_inval(cfg, "NFS: Invalid lookupcache 
argument");
+               }
+               break;
+       case Opt_fscache_uniq:
+               if (nfs_get_option_str(args, &cfg->fscache_uniq))
+                       goto out_nomem;
+               cfg->options |= NFS_OPTION_FSCACHE;
+               break;
+       case Opt_local_lock:
+               string = match_strdup(args);
+               if (string == NULL)
+                       goto out_nomem;
+               token = match_token(string, nfs_local_lock_tokens, args);
+               kfree(string);
+               switch (token) {
+               case Opt_local_lock_all:
+                       cfg->flags |= (NFS_MOUNT_LOCAL_FLOCK |
+                                      NFS_MOUNT_LOCAL_FCNTL);
+                       break;
+               case Opt_local_lock_flock:
+                       cfg->flags |= NFS_MOUNT_LOCAL_FLOCK;
+                       break;
+               case Opt_local_lock_posix:
+                       cfg->flags |= NFS_MOUNT_LOCAL_FCNTL;
+                       break;
+               case Opt_local_lock_none:
+                       cfg->flags &= ~(NFS_MOUNT_LOCAL_FLOCK |
+                                       NFS_MOUNT_LOCAL_FCNTL);
+                       break;
+               default:
+                       return nfs_cfg_inval(cfg, "NFS: invalid local_lock 
argument");
+               }
+               break;
+
+               /*
+                * Special options
+                */
+       case Opt_sloppy:
+               sc->sloppy = 1;
+               dfprintk(MOUNT, "NFS:   relaxing parsing rules\n");
+               break;
+       case Opt_userspace:
+       case Opt_deprecated:
+               dfprintk(MOUNT, "NFS:   ignoring mount option '%s'\n", p);
+               break;
+
+       default:
+               dfprintk(MOUNT, "NFS:   unrecognized mount option '%s'\n", p);
+               if (!sc->sloppy)
+                       return nfs_cfg_inval(cfg, "NFS: Unrecognized mount 
option");
+               break;
+       }
+
+       return 0;
+
+out_nomem:
+       printk(KERN_INFO "NFS: not enough memory to parse option\n");
+       return -ENOMEM;
+out_invalid_value:
+       return nfs_cfg_inval(cfg, "NFS: Bad mount option value specified");
+out_invalid_address:
+       return nfs_cfg_inval(cfg, "NFS: Bad IP address specified");
+}
+
+/*
+ * Validate the preparsed information in the mount context.
+ */
+static int nfs_sb_config_validate(struct sb_config *sc)
+{
+       struct nfs_sb_config *cfg = container_of(sc, struct nfs_sb_config, sc);
+       struct nfs_subversion *nfs_mod;
+       struct sockaddr *sap = (struct sockaddr *)&cfg->nfs_server.address;
+       int max_namelen = PAGE_SIZE;
+       int max_pathlen = NFS_MAXPATHLEN;
+       int port = 0;
+       int ret;
+
+       if (cfg->sc.purpose == SB_CONFIG_FOR_REMOUNT)
+               return 0;
+
+       if (!cfg->sc.device)
+               goto out_no_device_name;
+
+       /* Check for sanity first. */
+       if (cfg->minorversion && cfg->version != 4)
+               goto out_minorversion_mismatch;
+
+       if (cfg->options & NFS_OPTION_MIGRATION &&
+           (cfg->version != 4 || cfg->minorversion != 0))
+               goto out_migration_misuse;
+
+       /* Verify that any proto=/mountproto= options match the address
+        * families in the addr=/mountaddr= options.
+        */
+       if (cfg->protofamily != AF_UNSPEC &&
+           cfg->protofamily != cfg->nfs_server.address.sa_family)
+               goto out_proto_mismatch;
+
+       if (cfg->mountfamily != AF_UNSPEC) {
+               if (cfg->mount_server.addrlen) {
+                       if (cfg->mountfamily != 
cfg->mount_server.address.sa_family)
+                               goto out_mountproto_mismatch;
+               } else {
+                       if (cfg->mountfamily != 
cfg->nfs_server.address.sa_family)
+                               goto out_mountproto_mismatch;
+               }
+       }
+
+       if (!nfs_verify_server_address(sap))
+               goto out_no_address;
+
+       if (cfg->version == 4) {
+               if (IS_ENABLED(CONFIG_NFS_V4)) {
+                       port = NFS_PORT;
+                       max_namelen = NFS4_MAXNAMLEN;
+                       max_pathlen = NFS4_MAXPATHLEN;
+                       nfs_validate_transport_protocol(cfg);
+                       if (cfg->nfs_server.protocol == XPRT_TRANSPORT_UDP)
+                               goto out_invalid_transport_udp;
+                       cfg->flags &= ~(NFS_MOUNT_NONLM | NFS_MOUNT_NOACL |
+                                        NFS_MOUNT_VER3 | NFS_MOUNT_LOCAL_FLOCK 
|
+                                        NFS_MOUNT_LOCAL_FCNTL);
+               } else {
+                       goto out_v4_not_compiled;
+               }
+       } else {
+               nfs_set_mount_transport_protocol(cfg);
+       }
+
+       nfs_set_port(sap, &cfg->nfs_server.port, port);
+
+       ret = nfs_parse_devname(cfg, max_namelen, max_pathlen);
+       if (ret < 0)
+               return ret;
+
+       /* Load the NFS protocol module if we haven't done so yet */
+       if (!cfg->nfs_mod) {
+               nfs_mod = get_nfs_version(cfg->version);
+               if (IS_ERR(nfs_mod)) {
+                       ret = PTR_ERR(nfs_mod);
+                       goto out_version_unavailable;
+               }
+               cfg->nfs_mod = nfs_mod;
+       }
+       return 0;
+
+out_no_device_name:
+       return nfs_cfg_inval(cfg, "NFS: Device name not specified");
+out_v4_not_compiled:
+       nfs_cfg_error(cfg, "NFS: NFSv4 is not compiled into kernel");
+       return -EPROTONOSUPPORT;
+out_invalid_transport_udp:
+       return nfs_cfg_inval(cfg, "NFSv4: Unsupported transport protocol udp");
+out_no_address:
+       return nfs_cfg_inval(cfg, "NFS: mount program didn't pass remote 
address");
+out_mountproto_mismatch:
+       return nfs_cfg_inval(cfg, "NFS: Mount server address does not match 
mountproto= option");
+out_proto_mismatch:
+       return nfs_cfg_inval(cfg, "NFS: Server address does not match proto= 
option");
+out_minorversion_mismatch:
+       return nfs_cfg_inval(cfg, "NFS: Mount option does not support 
minorversion");
+out_migration_misuse:
+       return nfs_cfg_inval(cfg, "NFS: 'Migration' not supported for this NFS 
version");
+out_version_unavailable:
+       nfs_cfg_inval(cfg, "NFS: Version unavailable");
+       return ret;
+}
+
+/*
+ * Use the preparsed information in the mount context to effect a mount.
+ */
+static struct dentry *nfs_ordinary_mount(struct nfs_sb_config *cfg)
+{
+       cfg->set_security = nfs_set_sb_security;
+
+       return cfg->nfs_mod->rpc_ops->try_mount(cfg);
+}
+
+/*
+ * Clone an NFS2/3/4 server record on xdev traversal (FSID-change)
+ */
+static struct dentry *nfs_xdev_mount(struct nfs_sb_config *cfg)
+{
+       struct nfs_server *server;
+       struct dentry *mntroot = ERR_PTR(-ENOMEM);
+
+       dprintk("--> nfs_xdev_mount()\n");
+
+       cfg->set_security = nfs_clone_sb_security;
+
+       /* create a new volume representation */
+       server = cfg->nfs_mod->rpc_ops->clone_server(NFS_SB(cfg->clone_data.sb),
+                                                    cfg->mntfh,
+                                                    cfg->clone_data.fattr,
+                                                    cfg->selected_flavor);
+
+       if (IS_ERR(server))
+               mntroot = ERR_CAST(server);
+       else
+               mntroot = nfs_fs_mount_common(server, cfg);
+
+       dprintk("<-- nfs_xdev_mount() = %ld\n",
+                       IS_ERR(mntroot) ? PTR_ERR(mntroot) : 0L);
+       return mntroot;
+}
+
+/*
+ * Handle ordinary mounts inspired by the user and cross-FSID mounts.
+ */
+struct dentry *nfs_general_mount(struct nfs_sb_config *cfg)
+{
+       switch (cfg->mount_type) {
+       case NFS_MOUNT_ORDINARY:
+               return nfs_ordinary_mount(cfg);
+
+       case NFS_MOUNT_CROSS_DEV:
+               return nfs_xdev_mount(cfg);
+
+       default:
+               nfs_cfg_error(cfg, "NFS: Unknown mount type");
+               return ERR_PTR(-ENOTSUPP);
+       }
+}
+EXPORT_SYMBOL_GPL(nfs_general_mount);
+
+static struct dentry *nfs_fs_mount(struct sb_config *sc)
+{
+       struct nfs_sb_config *cfg = container_of(sc, struct nfs_sb_config, sc);
+
+       if (!cfg->nfs_mod) {
+               pr_warn("Missing nfs_mod\n");
+               return ERR_PTR(-EINVAL);
+       }
+       if (!cfg->nfs_mod->rpc_ops) {
+               pr_warn("Missing rpc_ops\n");
+               return ERR_PTR(-EINVAL);
+       }
+       if (!cfg->nfs_mod->rpc_ops->mount) {
+               pr_warn("Missing mount\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       return cfg->nfs_mod->rpc_ops->mount(cfg);
+}
+
+/*
+ * Handle duplication of a mount context.  The caller copied *src into *mc, but
+ * it can't deal with resource pointers in the filesystem context, so we have
+ * to do that.  We need to clear pointers, copy data or get extra refs as
+ * appropriate.
+ */
+static int nfs_sb_config_dup(struct sb_config *sc, struct sb_config *src)
+{
+       struct nfs_sb_config *cfg = container_of(sc, struct nfs_sb_config, sc);
+
+       __module_get(cfg->nfs_mod->owner);
+       cfg->client_address             = NULL;
+       cfg->mount_server.hostname      = NULL;
+       cfg->nfs_server.export_path     = NULL;
+       cfg->nfs_server.hostname        = NULL;
+       cfg->fscache_uniq               = NULL;
+
+       cfg->mntfh = nfs_alloc_fhandle();
+       if (!cfg->mntfh)
+               return -ENOMEM;
+       return 0;
+}
+
+static void nfs_sb_config_free(struct sb_config *sc)
+{
+       struct nfs_sb_config *cfg = container_of(sc, struct nfs_sb_config, sc);
+
+       if (cfg->nfs_mod)
+               put_nfs_version(cfg->nfs_mod);
+       kfree(cfg->client_address);
+       kfree(cfg->mount_server.hostname);
+       if (cfg->nfs_server.export_path != nfs_slash)
+               kfree(cfg->nfs_server.export_path);
+       kfree(cfg->nfs_server.hostname);
+       kfree(cfg->fscache_uniq);
+       nfs_free_fhandle(cfg->mntfh);
+}
+
+static const struct sb_config_operations nfs_sb_config_ops = {
+       .free                   = nfs_sb_config_free,
+       .dup                    = nfs_sb_config_dup,
+       .parse_option           = nfs_sb_config_parse_option,
+       .monolithic_mount_data  = nfs_monolithic_mount_data,
+       .validate               = nfs_sb_config_validate,
+       .mount                  = nfs_fs_mount,
+       .fill_super             = nfs_fill_super,
+};
+
+/*
+ * Initialise a mount context from an extant superblock for remounting.
+ */
+static int nfs_mount_init_from_sb(struct sb_config *sc,
+                                 struct super_block *sb)
+{
+       struct nfs_sb_config *cfg = container_of(sc, struct nfs_sb_config, sc);
+       struct nfs_server *nfss = sb->s_fs_info;
+       struct net *net = nfss->nfs_client->cl_net;
+
+       cfg->flags              = nfss->flags;
+       cfg->rsize              = nfss->rsize;
+       cfg->wsize              = nfss->wsize;
+       cfg->retrans            = nfss->client->cl_timeout->to_retries;
+       cfg->selected_flavor    = nfss->client->cl_auth->au_flavor;
+       cfg->acregmin           = nfss->acregmin / HZ;
+       cfg->acregmax           = nfss->acregmax / HZ;
+       cfg->acdirmin           = nfss->acdirmin / HZ;
+       cfg->acdirmax           = nfss->acdirmax / HZ;
+       cfg->timeo              = 10U * nfss->client->cl_timeout->to_initval / 
HZ;
+       cfg->nfs_server.port    = nfss->port;
+       cfg->nfs_server.addrlen = nfss->nfs_client->cl_addrlen;
+       cfg->version            = nfss->nfs_client->rpc_ops->version;
+       cfg->minorversion       = nfss->nfs_client->cl_minorversion;
+
+       memcpy(&cfg->nfs_server.address, &nfss->nfs_client->cl_addr,
+               cfg->nfs_server.addrlen);
+
+       if (cfg->sc.net_ns != net) {
+               put_net(cfg->sc.net_ns);
+               cfg->sc.net_ns = get_net(net);
+       }
+
+       cfg->nfs_mod = nfss->nfs_client->cl_nfs_mod;
+       if (!try_module_get(cfg->nfs_mod->owner)) {
+               cfg->nfs_mod = NULL;
+               nfs_cfg_error(cfg, "NFS: Protocol module not available");
+               return -ENOENT;
+       }
+
+       return 0;
+}
+
+/*
+ * Prepare mount context.  We use the namespaces attached to the context.  This
+ * may be the current process's namespaces, or it may be a container's
+ * namespaces.
+ */
+static int nfs_init_sb_config(struct sb_config *sc, struct super_block *src_sb)
+{
+       struct nfs_sb_config *cfg = container_of(sc, struct nfs_sb_config, sc);
+
+       cfg->mntfh = nfs_alloc_fhandle();
+       if (!cfg->mntfh)
+               return -ENOMEM;
+
+       cfg->sc.ops             = &nfs_sb_config_ops;
+       cfg->mount_type         = NFS_MOUNT_ORDINARY;
+       cfg->protofamily        = AF_UNSPEC;
+       cfg->mountfamily        = AF_UNSPEC;
+       cfg->mount_server.port  = NFS_UNSPEC_PORT;
+
+       if (!src_sb) {
+               cfg->timeo              = NFS_UNSPEC_TIMEO;
+               cfg->retrans            = NFS_UNSPEC_RETRANS;
+               cfg->acregmin           = NFS_DEF_ACREGMIN;
+               cfg->acregmax           = NFS_DEF_ACREGMAX;
+               cfg->acdirmin           = NFS_DEF_ACDIRMIN;
+               cfg->acdirmax           = NFS_DEF_ACDIRMAX;
+               cfg->nfs_server.port    = NFS_UNSPEC_PORT;
+               cfg->nfs_server.protocol = XPRT_TRANSPORT_TCP;
+               cfg->selected_flavor    = RPC_AUTH_MAXFLAVOR;
+               cfg->minorversion       = 0;
+               cfg->need_mount         = true;
+               return 0;
+       }
+
+       return nfs_mount_init_from_sb(sc, src_sb);
+}
+
+struct file_system_type nfs_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "nfs",
+       .init_sb_config = nfs_init_sb_config,
+       .sb_config_size = sizeof(struct nfs_sb_config),
+       .kill_sb        = nfs_kill_super,
+       .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
+};
+MODULE_ALIAS_FS("nfs");
+EXPORT_SYMBOL_GPL(nfs_fs_type);
+
+#if IS_ENABLED(CONFIG_NFS_V4)
+struct file_system_type nfs4_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "nfs4",
+       .sb_config_size = sizeof(struct nfs_sb_config),
+       .init_sb_config = nfs_init_sb_config,
+       .kill_sb        = nfs_kill_super,
+       .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
+};
+MODULE_ALIAS_FS("nfs4");
+MODULE_ALIAS("nfs4");
+EXPORT_SYMBOL_GPL(nfs4_fs_type);
+#endif /* CONFIG_NFS_V4 */
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c
index 786f17580582..2d0caaa28c58 100644
--- a/fs/nfs/namespace.c
+++ b/fs/nfs/namespace.c
@@ -18,6 +18,7 @@
 #include <linux/vfs.h>
 #include <linux/sunrpc/gss_api.h>
 #include "internal.h"
+#include "nfs.h"
 
 #define NFSDBG_FACILITY                NFSDBG_VFS
 
@@ -224,10 +225,9 @@ void nfs_release_automount_timer(void)
  * Clone a mountpoint of the appropriate type
  */
 static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server,
-                                          const char *devname,
-                                          struct nfs_clone_mount *mountdata)
+                                          struct nfs_sb_config *cfg)
 {
-       return vfs_submount(mountdata->dentry, &nfs_xdev_fs_type, devname, 
mountdata);
+       return vfs_submount_sc(cfg->clone_data.dentry, &cfg->sc);
 }
 
 /**
@@ -241,33 +241,57 @@ static struct vfsmount *nfs_do_clone_mount(struct 
nfs_server *server,
 struct vfsmount *nfs_do_submount(struct dentry *dentry, struct nfs_fh *fh,
                                 struct nfs_fattr *fattr, rpc_authflavor_t 
authflavor)
 {
-       struct nfs_clone_mount mountdata = {
-               .sb = dentry->d_sb,
-               .dentry = dentry,
-               .fh = fh,
-               .fattr = fattr,
-               .authflavor = authflavor,
-       };
-       struct vfsmount *mnt = ERR_PTR(-ENOMEM);
-       char *page = (char *) __get_free_page(GFP_USER);
-       char *devname;
+       struct nfs_sb_config *cfg;
+       struct sb_config *sc;
+       struct vfsmount *mnt;
+       char *buffer, *p;
+
+       /* Open a new mount context, transferring parameters from the parent
+        * superblock, including the network namespace.
+        */
+       sc = __vfs_new_sb_config(&nfs_fs_type, dentry->d_sb, 0,
+                                SB_CONFIG_FOR_SUBMOUNT);
+       if (IS_ERR(sc))
+               return ERR_CAST(sc);
+       cfg = container_of(sc, struct nfs_sb_config, sc);
+
+       mnt = ERR_PTR(-ENOMEM);
+       buffer = kmalloc(4096, GFP_USER);
+       if (!buffer)
+               goto err_sc;
+
+       cfg->mount_type         = NFS_MOUNT_CROSS_DEV;
+       cfg->selected_flavor    = authflavor;
+       cfg->clone_data.sb      = dentry->d_sb;
+       cfg->clone_data.dentry  = dentry;
+       cfg->clone_data.fattr   = fattr;
+       cfg->clone_data.cloned  = true;
+
+       nfs_copy_fh(cfg->mntfh, fh);
 
        dprintk("--> nfs_do_submount()\n");
+       dprintk("%s: submounting on %pd2\n", __func__, dentry);
 
-       dprintk("%s: submounting on %pd2\n", __func__,
-                       dentry);
-       if (page == NULL)
-               goto out;
-       devname = nfs_devname(dentry, page, PAGE_SIZE);
-       mnt = (struct vfsmount *)devname;
-       if (IS_ERR(devname))
-               goto free_page;
-       mnt = nfs_do_clone_mount(NFS_SB(dentry->d_sb), devname, &mountdata);
-free_page:
-       free_page((unsigned long)page);
-out:
-       dprintk("%s: done\n", __func__);
+       p = nfs_devname(dentry, buffer, 4096);
+       if (IS_ERR(p)) {
+               nfs_cfg_error(cfg, "NFS: Couldn't determine submount pathname");
+               mnt = ERR_CAST(p);
+               goto err_buffer;
+       }
 
+       cfg->sc.device = kmemdup(p, buffer + 4096 - p, GFP_KERNEL);
+       if (!cfg->sc.device)
+               goto err_buffer;
+       kfree(buffer);
+
+       mnt = nfs_do_clone_mount(NFS_SB(dentry->d_sb), cfg);
+       goto err_sc;
+
+err_buffer:
+       kfree(buffer);
+err_sc:
+       put_sb_config(sc);
+       dprintk("%s: done\n", __func__);
        dprintk("<-- nfs_do_submount() = %p\n", mnt);
        return mnt;
 }
diff --git a/fs/nfs/nfs3_fs.h b/fs/nfs/nfs3_fs.h
index e134d6548ab7..7187e2e28a65 100644
--- a/fs/nfs/nfs3_fs.h
+++ b/fs/nfs/nfs3_fs.h
@@ -26,7 +26,7 @@ static inline int nfs3_proc_setacls(struct inode *inode, 
struct posix_acl *acl,
 #endif /* CONFIG_NFS_V3_ACL */
 
 /* nfs3client.c */
-struct nfs_server *nfs3_create_server(struct nfs_mount_info *, struct 
nfs_subversion *);
+struct nfs_server *nfs3_create_server(struct nfs_sb_config *);
 struct nfs_server *nfs3_clone_server(struct nfs_server *, struct nfs_fh *,
                                     struct nfs_fattr *, rpc_authflavor_t);
 
diff --git a/fs/nfs/nfs3client.c b/fs/nfs/nfs3client.c
index 7879f2a0fcfd..8b688d71c37c 100644
--- a/fs/nfs/nfs3client.c
+++ b/fs/nfs/nfs3client.c
@@ -45,10 +45,10 @@ static inline void nfs_init_server_aclclient(struct 
nfs_server *server)
 }
 #endif
 
-struct nfs_server *nfs3_create_server(struct nfs_mount_info *mount_info,
-                                     struct nfs_subversion *nfs_mod)
+struct nfs_server *nfs3_create_server(struct nfs_sb_config *cfg)
 {
-       struct nfs_server *server = nfs_create_server(mount_info, nfs_mod);
+       struct nfs_server *server = nfs_create_server(cfg);
+
        /* Create a client RPC handle for the NFS v3 ACL management interface */
        if (!IS_ERR(server))
                nfs_init_server_aclclient(server);
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index dc925b531f32..1e0349fc33ec 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -922,6 +922,7 @@ const struct nfs_rpc_ops nfs_v3_clientops = {
        .file_inode_ops = &nfs3_file_inode_operations,
        .file_ops       = &nfs_file_operations,
        .getroot        = nfs3_proc_get_root,
+       .mount          = nfs_general_mount,
        .submount       = nfs_submount,
        .try_mount      = nfs_try_mount,
        .getattr        = nfs3_proc_getattr,
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index af285cc27ccf..05769b633b76 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -467,7 +467,6 @@ extern const nfs4_stateid zero_stateid;
 /* nfs4super.c */
 struct nfs_mount_info;
 extern struct nfs_subversion nfs_v4;
-struct dentry *nfs4_try_mount(int, const char *, struct nfs_mount_info *, 
struct nfs_subversion *);
 extern bool nfs4_disable_idmapping;
 extern unsigned short max_session_slots;
 extern unsigned short max_session_cb_slots;
@@ -477,6 +476,9 @@ extern bool recover_lost_locks;
 #define NFS4_CLIENT_ID_UNIQ_LEN                (64)
 extern char nfs4_client_id_uniquifier[NFS4_CLIENT_ID_UNIQ_LEN];
 
+extern struct dentry *nfs4_try_mount(struct nfs_sb_config *);
+extern struct dentry *nfs4_mount(struct nfs_sb_config *);
+
 /* nfs4sysctl.c */
 #ifdef CONFIG_SYSCTL
 int nfs4_register_sysctl(void);
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index 8346ccbf2d52..a564483d51e7 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -1093,56 +1093,56 @@ static int nfs4_server_common_setup(struct nfs_server 
*server,
  * Create a version 4 volume record
  */
 static int nfs4_init_server(struct nfs_server *server,
-               struct nfs_parsed_mount_data *data)
+                           struct nfs_sb_config *cfg)
 {
        struct rpc_timeout timeparms;
        int error;
 
        dprintk("--> nfs4_init_server()\n");
 
-       nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
-                       data->timeo, data->retrans);
+       nfs_init_timeout_values(&timeparms, cfg->nfs_server.protocol,
+                               cfg->timeo, cfg->retrans);
 
        /* Initialise the client representation from the mount data */
-       server->flags = data->flags;
-       server->options = data->options;
-       server->auth_info = data->auth_info;
+       server->flags = cfg->flags;
+       server->options = cfg->options;
+       server->auth_info = cfg->auth_info;
 
        /* Use the first specified auth flavor. If this flavor isn't
         * allowed by the server, use the SECINFO path to try the
         * other specified flavors */
-       if (data->auth_info.flavor_len >= 1)
-               data->selected_flavor = data->auth_info.flavors[0];
+       if (cfg->auth_info.flavor_len >= 1)
+               cfg->selected_flavor = cfg->auth_info.flavors[0];
        else
-               data->selected_flavor = RPC_AUTH_UNIX;
+               cfg->selected_flavor = RPC_AUTH_UNIX;
 
        /* Get a client record */
        error = nfs4_set_client(server,
-                       data->nfs_server.hostname,
-                       (const struct sockaddr *)&data->nfs_server.address,
-                       data->nfs_server.addrlen,
-                       data->client_address,
-                       data->nfs_server.protocol,
-                       &timeparms,
-                       data->minorversion,
-                       data->net);
+                               cfg->nfs_server.hostname,
+                               &cfg->nfs_server.address,
+                               cfg->nfs_server.addrlen,
+                               cfg->client_address,
+                               cfg->nfs_server.protocol,
+                               &timeparms,
+                               cfg->minorversion,
+                               cfg->sc.net_ns);
        if (error < 0)
                goto error;
 
-       if (data->rsize)
-               server->rsize = nfs_block_size(data->rsize, NULL);
-       if (data->wsize)
-               server->wsize = nfs_block_size(data->wsize, NULL);
+       if (cfg->rsize)
+               server->rsize = nfs_block_size(cfg->rsize, NULL);
+       if (cfg->wsize)
+               server->wsize = nfs_block_size(cfg->wsize, NULL);
 
-       server->acregmin = data->acregmin * HZ;
-       server->acregmax = data->acregmax * HZ;
-       server->acdirmin = data->acdirmin * HZ;
-       server->acdirmax = data->acdirmax * HZ;
+       server->acregmin = cfg->acregmin * HZ;
+       server->acregmax = cfg->acregmax * HZ;
+       server->acdirmin = cfg->acdirmin * HZ;
+       server->acdirmax = cfg->acdirmax * HZ;
 
-       server->port = data->nfs_server.port;
+       server->port = cfg->nfs_server.port;
 
        error = nfs_init_server_rpcclient(server, &timeparms,
-                                         data->selected_flavor);
+                                         cfg->selected_flavor);
 
 error:
        /* Done */
@@ -1154,10 +1154,7 @@ static int nfs4_init_server(struct nfs_server *server,
  * Create a version 4 volume record
  * - keyed on server and FSID
  */
-/*struct nfs_server *nfs4_create_server(const struct nfs_parsed_mount_data 
*data,
-                                     struct nfs_fh *mntfh)*/
-struct nfs_server *nfs4_create_server(struct nfs_mount_info *mount_info,
-                                     struct nfs_subversion *nfs_mod)
+struct nfs_server *nfs4_create_server(struct nfs_sb_config *cfg)
 {
        struct nfs_server *server;
        bool auth_probe;
@@ -1169,14 +1166,14 @@ struct nfs_server *nfs4_create_server(struct 
nfs_mount_info *mount_info,
        if (!server)
                return ERR_PTR(-ENOMEM);
 
-       auth_probe = mount_info->parsed->auth_info.flavor_len < 1;
+       auth_probe = cfg->auth_info.flavor_len < 1;
 
        /* set up the general RPC client */
-       error = nfs4_init_server(server, mount_info->parsed);
+       error = nfs4_init_server(server, cfg);
        if (error < 0)
                goto error;
 
-       error = nfs4_server_common_setup(server, mount_info->mntfh, auth_probe);
+       error = nfs4_server_common_setup(server, cfg->mntfh, auth_probe);
        if (error < 0)
                goto error;
 
@@ -1192,8 +1189,7 @@ struct nfs_server *nfs4_create_server(struct 
nfs_mount_info *mount_info,
 /*
  * Create an NFS4 referral server record
  */
-struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
-                                              struct nfs_fh *mntfh)
+struct nfs_server *nfs4_create_referral_server(struct nfs_sb_config *cfg)
 {
        struct nfs_client *parent_client;
        struct nfs_server *server, *parent_server;
@@ -1206,7 +1202,7 @@ struct nfs_server *nfs4_create_referral_server(struct 
nfs_clone_mount *data,
        if (!server)
                return ERR_PTR(-ENOMEM);
 
-       parent_server = NFS_SB(data->sb);
+       parent_server = NFS_SB(cfg->clone_data.sb);
        parent_client = parent_server->nfs_client;
 
        /* Initialise the client representation from the parent server */
@@ -1214,9 +1210,10 @@ struct nfs_server *nfs4_create_referral_server(struct 
nfs_clone_mount *data,
 
        /* Get a client representation.
         * Note: NFSv4 always uses TCP, */
-       error = nfs4_set_client(server, data->hostname,
-                               data->addr,
-                               data->addrlen,
+       error = nfs4_set_client(server,
+                               cfg->nfs_server.hostname,
+                               &cfg->nfs_server.address,
+                               cfg->nfs_server.addrlen,
                                parent_client->cl_ipaddr,
                                rpc_protocol(parent_server->client),
                                parent_server->client->cl_timeout,
@@ -1225,13 +1222,14 @@ struct nfs_server *nfs4_create_referral_server(struct 
nfs_clone_mount *data,
        if (error < 0)
                goto error;
 
-       error = nfs_init_server_rpcclient(server, 
parent_server->client->cl_timeout, data->authflavor);
+       error = nfs_init_server_rpcclient(server, 
parent_server->client->cl_timeout,
+                                         cfg->selected_flavor);
        if (error < 0)
                goto error;
 
        auth_probe = parent_server->auth_info.flavor_len < 1;
 
-       error = nfs4_server_common_setup(server, mntfh, auth_probe);
+       error = nfs4_server_common_setup(server, cfg->mntfh, auth_probe);
        if (error < 0)
                goto error;
 
diff --git a/fs/nfs/nfs4namespace.c b/fs/nfs/nfs4namespace.c
index d8b040bd9814..d4e50d51554b 100644
--- a/fs/nfs/nfs4namespace.c
+++ b/fs/nfs/nfs4namespace.c
@@ -7,6 +7,7 @@
  * NFSv4 namespace
  */
 
+#include <linux/module.h>
 #include <linux/dcache.h>
 #include <linux/mount.h>
 #include <linux/namei.h>
@@ -20,37 +21,64 @@
 #include <linux/inet.h>
 #include "internal.h"
 #include "nfs4_fs.h"
+#include "nfs.h"
 #include "dns_resolve.h"
 
 #define NFSDBG_FACILITY                NFSDBG_VFS
 
 /*
+ * Work out the length that an NFSv4 path would render to as a standard posix
+ * path, with a leading slash but no terminating slash.
+ */
+static ssize_t nfs4_pathname_len(const struct nfs4_pathname *pathname)
+{
+       ssize_t len;
+       int i;
+
+       for (i = 0; i < pathname->ncomponents; i++) {
+               const struct nfs4_string *component = &pathname->components[i];
+
+               if (component->len > NAME_MAX)
+                       goto too_long;
+               len += 1 + component->len; /* Adding "/foo" */
+               if (len > PATH_MAX)
+                       goto too_long;
+       }
+       return len;
+
+too_long:
+       return -ENAMETOOLONG;
+}
+
+/*
  * Convert the NFSv4 pathname components into a standard posix path.
- *
- * Note that the resulting string will be placed at the end of the buffer
  */
-static inline char *nfs4_pathname_string(const struct nfs4_pathname *pathname,
-                                        char *buffer, ssize_t buflen)
+static char *nfs4_pathname_string(const struct nfs4_pathname *pathname,
+                                 unsigned short *_len)
 {
-       char *end = buffer + buflen;
-       int n;
+       ssize_t len;
+       char *buf, *p;
+       int i;
 
-       *--end = '\0';
-       buflen--;
-
-       n = pathname->ncomponents;
-       while (--n >= 0) {
-               const struct nfs4_string *component = &pathname->components[n];
-               buflen -= component->len + 1;
-               if (buflen < 0)
-                       goto Elong;
-               end -= component->len;
-               memcpy(end, component->data, component->len);
-               *--end = '/';
+       len = nfs4_pathname_len(pathname);
+       if (len < 0)
+               return ERR_PTR(len);
+       *_len = len;
+
+       p = buf = kmalloc(len + 1, GFP_KERNEL);
+       if (!buf)
+               return ERR_PTR(-ENOMEM);
+
+       for (i = 0; i < pathname->ncomponents; i++) {
+               const struct nfs4_string *component = &pathname->components[i];
+
+               *p++ = '/';
+               memcpy(p, component->data, component->len);
+               p += component->len;
        }
-       return end;
-Elong:
-       return ERR_PTR(-ENAMETOOLONG);
+
+       *p = 0;
+       return buf;
 }
 
 /*
@@ -99,21 +127,25 @@ static char *nfs4_path(struct dentry *dentry, char 
*buffer, ssize_t buflen)
  */
 static int nfs4_validate_fspath(struct dentry *dentry,
                                const struct nfs4_fs_locations *locations,
-                               char *page, char *page2)
+                               struct nfs_sb_config *ctx)
 {
-       const char *path, *fs_path;
+       const char *path;
+       char *buf;
+       int n;
 
-       path = nfs4_path(dentry, page, PAGE_SIZE);
-       if (IS_ERR(path))
+       buf = kmalloc(4096, GFP_KERNEL);
+       path = nfs4_path(dentry, buf, 4096);
+       if (IS_ERR(path)) {
+               kfree(buf);
                return PTR_ERR(path);
+       }
 
-       fs_path = nfs4_pathname_string(&locations->fs_path, page2, PAGE_SIZE);
-       if (IS_ERR(fs_path))
-               return PTR_ERR(fs_path);
-
-       if (strncmp(path, fs_path, strlen(fs_path)) != 0) {
+       n = strncmp(path, ctx->nfs_server.export_path,
+                   ctx->nfs_server.export_path_len);
+       kfree(buf);
+       if (n != 0) {
                dprintk("%s: path %s does not begin with fsroot %s\n",
-                       __func__, path, fs_path);
+                       __func__, path, ctx->nfs_server.export_path);
                return -ENOENT;
        }
 
@@ -234,56 +266,66 @@ nfs4_negotiate_security(struct rpc_clnt *clnt, struct 
inode *inode,
        return new;
 }
 
-static struct vfsmount *try_location(struct nfs_clone_mount *mountdata,
-                                    char *page, char *page2,
+static struct vfsmount *try_location(struct dentry *dentry,
+                                    struct nfs_sb_config *ctx,
                                     const struct nfs4_fs_location *location)
 {
-       const size_t addr_bufsize = sizeof(struct sockaddr_storage);
-       struct net *net = rpc_net_ns(NFS_SB(mountdata->sb)->client);
+       struct net *net = rpc_net_ns(NFS_SB(dentry->d_sb)->client);
        struct vfsmount *mnt = ERR_PTR(-ENOENT);
-       char *mnt_path;
-       unsigned int maxbuflen;
-       unsigned int s;
+       unsigned int len, s;
+       char *p;
 
-       mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE);
-       if (IS_ERR(mnt_path))
-               return ERR_CAST(mnt_path);
-       mountdata->mnt_path = mnt_path;
-       maxbuflen = mnt_path - 1 - page2;
+       /* Allocate a buffer big enough to hold any of the hostnames plus a
+        * terminating char and also a buffer big enough to hold the hostname
+        * plus a colon plus the path.
+        */
+       len = 0;
+       for (s = 0; s < location->nservers; s++) {
+               const struct nfs4_string *buf = &location->servers[s];
+               if (buf->len > len)
+                       len = buf->len;
+       }
 
-       mountdata->addr = kmalloc(addr_bufsize, GFP_KERNEL);
-       if (mountdata->addr == NULL)
+       ctx->nfs_server.hostname = kmalloc(len + 1, GFP_KERNEL);
+       if (!ctx->nfs_server.hostname)
                return ERR_PTR(-ENOMEM);
 
+       ctx->sc.device = kmalloc(len + 1 + ctx->nfs_server.export_path_len + 1,
+                                GFP_KERNEL);
+       if (!ctx->sc.device)
+               return ERR_PTR(-ENOMEM);
+       
        for (s = 0; s < location->nservers; s++) {
                const struct nfs4_string *buf = &location->servers[s];
 
-               if (buf->len <= 0 || buf->len >= maxbuflen)
-                       continue;
-
                if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len))
                        continue;
 
-               mountdata->addrlen = nfs_parse_server_name(buf->data, buf->len,
-                               mountdata->addr, addr_bufsize, net);
-               if (mountdata->addrlen == 0)
+               ctx->nfs_server.addrlen =
+                       nfs_parse_server_name(buf->data, buf->len,
+                                             &ctx->nfs_server.address,
+                                             sizeof(ctx->nfs_server._address),
+                                             net);
+               if (ctx->nfs_server.addrlen == 0)
                        continue;
 
-               rpc_set_port(mountdata->addr, NFS_PORT);
+               rpc_set_port(&ctx->nfs_server.address, NFS_PORT);
 
-               memcpy(page2, buf->data, buf->len);
-               page2[buf->len] = '\0';
-               mountdata->hostname = page2;
+               memcpy(ctx->nfs_server.hostname, buf->data, buf->len);
+               ctx->nfs_server.hostname[buf->len] = '\0';
 
-               snprintf(page, PAGE_SIZE, "%s:%s",
-                               mountdata->hostname,
-                               mountdata->mnt_path);
+               p = ctx->sc.device;
+               memcpy(p, buf->data, buf->len);
+               p += buf->len;
+               *p++ = ':';
+               memcpy(p, ctx->nfs_server.export_path, 
ctx->nfs_server.export_path_len);
+               p += ctx->nfs_server.export_path_len;
+               *p = 0;
 
-               mnt = vfs_submount(mountdata->dentry, &nfs4_referral_fs_type, 
page, mountdata);
+               mnt = vfs_submount_sc(ctx->clone_data.dentry, &ctx->sc);
                if (!IS_ERR(mnt))
                        break;
        }
-       kfree(mountdata->addr);
        return mnt;
 }
 
@@ -296,33 +338,43 @@ static struct vfsmount *try_location(struct 
nfs_clone_mount *mountdata,
 static struct vfsmount *nfs_follow_referral(struct dentry *dentry,
                                            const struct nfs4_fs_locations 
*locations)
 {
-       struct vfsmount *mnt = ERR_PTR(-ENOENT);
-       struct nfs_clone_mount mountdata = {
-               .sb = dentry->d_sb,
-               .dentry = dentry,
-               .authflavor = NFS_SB(dentry->d_sb)->client->cl_auth->au_flavor,
-       };
-       char *page = NULL, *page2 = NULL;
+       struct nfs_sb_config *cfg;
+       struct sb_config *sc;
+       struct vfsmount *mnt;
+       char *export_path;
        int loc, error;
 
        if (locations == NULL || locations->nlocations <= 0)
                goto out;
 
+       sc = __vfs_new_sb_config(&nfs4_fs_type, dentry->d_sb, 0,
+                                SB_CONFIG_FOR_SUBMOUNT);
+       if (IS_ERR(sc)) {
+               mnt = ERR_CAST(sc);
+               goto out;
+       }
+       cfg = container_of(sc, struct nfs_sb_config, sc);
+
        dprintk("%s: referral at %pd2\n", __func__, dentry);
 
-       page = (char *) __get_free_page(GFP_USER);
-       if (!page)
-               goto out;
+       cfg->mount_type         = NFS4_MOUNT_REFERRAL;
+       cfg->clone_data.sb      = dentry->d_sb;
+       cfg->clone_data.dentry  = dentry;
+       cfg->clone_data.cloned  = true;
 
-       page2 = (char *) __get_free_page(GFP_USER);
-       if (!page2)
-               goto out;
+       export_path = nfs4_pathname_string(&locations->fs_path,
+                                          &cfg->nfs_server.export_path_len);
+       if (IS_ERR(export_path)) {
+               mnt = ERR_CAST(export_path);
+               goto out_sc;
+       }
+       cfg->nfs_server.export_path = export_path;
 
        /* Ensure fs path is a prefix of current dentry path */
-       error = nfs4_validate_fspath(dentry, locations, page, page2);
+       error = nfs4_validate_fspath(dentry, locations, cfg);
        if (error < 0) {
                mnt = ERR_PTR(error);
-               goto out;
+               goto out_sc;
        }
 
        for (loc = 0; loc < locations->nlocations; loc++) {
@@ -332,14 +384,14 @@ static struct vfsmount *nfs_follow_referral(struct dentry 
*dentry,
                    location->rootpath.ncomponents == 0)
                        continue;
 
-               mnt = try_location(&mountdata, page, page2, location);
+               mnt = try_location(cfg->clone_data.dentry, cfg, location);
                if (!IS_ERR(mnt))
                        break;
        }
 
+out_sc:
+       put_sb_config(sc);
 out:
-       free_page((unsigned long) page);
-       free_page((unsigned long) page2);
        dprintk("%s: done\n", __func__);
        return mnt;
 }
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 201ca3f2c4ba..32d8c10bc45e 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -9318,6 +9318,7 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
        .file_inode_ops = &nfs4_file_inode_operations,
        .file_ops       = &nfs4_file_operations,
        .getroot        = nfs4_proc_get_root,
+       .mount          = nfs4_mount,
        .submount       = nfs4_submount,
        .try_mount      = nfs4_try_mount,
        .getattr        = nfs4_proc_getattr,
diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c
index 967fa04d5c76..7bc27a28d5da 100644
--- a/fs/nfs/nfs4super.c
+++ b/fs/nfs/nfs4super.c
@@ -18,36 +18,9 @@
 
 static int nfs4_write_inode(struct inode *inode, struct writeback_control 
*wbc);
 static void nfs4_evict_inode(struct inode *inode);
-static struct dentry *nfs4_remote_mount(struct file_system_type *fs_type,
-       int flags, const char *dev_name, void *raw_data);
-static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
-       int flags, const char *dev_name, void *raw_data);
-static struct dentry *nfs4_remote_referral_mount(struct file_system_type 
*fs_type,
-       int flags, const char *dev_name, void *raw_data);
-
-static struct file_system_type nfs4_remote_fs_type = {
-       .owner          = THIS_MODULE,
-       .name           = "nfs4",
-       .mount          = nfs4_remote_mount,
-       .kill_sb        = nfs_kill_super,
-       .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
-};
-
-static struct file_system_type nfs4_remote_referral_fs_type = {
-       .owner          = THIS_MODULE,
-       .name           = "nfs4",
-       .mount          = nfs4_remote_referral_mount,
-       .kill_sb        = nfs_kill_super,
-       .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
-};
-
-struct file_system_type nfs4_referral_fs_type = {
-       .owner          = THIS_MODULE,
-       .name           = "nfs4",
-       .mount          = nfs4_referral_mount,
-       .kill_sb        = nfs_kill_super,
-       .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
-};
+static struct dentry *nfs4_remote_mount(struct nfs_sb_config *cfg);
+static struct dentry *nfs4_referral_mount(struct nfs_sb_config *cfg);
+static struct dentry *nfs4_remote_referral_mount(struct nfs_sb_config *cfg);
 
 static const struct super_operations nfs4_sops = {
        .alloc_inode    = nfs_alloc_inode,
@@ -61,16 +34,16 @@ static const struct super_operations nfs4_sops = {
        .show_devname   = nfs_show_devname,
        .show_path      = nfs_show_path,
        .show_stats     = nfs_show_stats,
-       .remount_fs     = nfs_remount,
+       .remount_fs_sc  = nfs_remount,
 };
 
 struct nfs_subversion nfs_v4 = {
-       .owner = THIS_MODULE,
-       .nfs_fs   = &nfs4_fs_type,
-       .rpc_vers = &nfs_version4,
-       .rpc_ops  = &nfs_v4_clientops,
-       .sops     = &nfs4_sops,
-       .xattr    = nfs4_xattr_handlers,
+       .owner          = THIS_MODULE,
+       .nfs_fs         = &nfs4_fs_type,
+       .rpc_vers       = &nfs_version4,
+       .rpc_ops        = &nfs_v4_clientops,
+       .sops           = &nfs4_sops,
+       .xattr          = nfs4_xattr_handlers,
 };
 
 static int nfs4_write_inode(struct inode *inode, struct writeback_control *wbc)
@@ -104,47 +77,58 @@ static void nfs4_evict_inode(struct inode *inode)
 /*
  * Get the superblock for the NFS4 root partition
  */
-static struct dentry *
-nfs4_remote_mount(struct file_system_type *fs_type, int flags,
-                 const char *dev_name, void *info)
+static struct dentry *nfs4_remote_mount(struct nfs_sb_config *cfg)
 {
-       struct nfs_mount_info *mount_info = info;
        struct nfs_server *server;
-       struct dentry *mntroot = ERR_PTR(-ENOMEM);
 
-       mount_info->set_security = nfs_set_sb_security;
+       cfg->set_security = nfs_set_sb_security;
 
        /* Get a volume representation */
-       server = nfs4_create_server(mount_info, &nfs_v4);
-       if (IS_ERR(server)) {
-               mntroot = ERR_CAST(server);
-               goto out;
-       }
-
-       mntroot = nfs_fs_mount_common(server, flags, dev_name, mount_info, 
&nfs_v4);
+       server = nfs4_create_server(cfg);
+       if (IS_ERR(server))
+               return ERR_CAST(server);
 
-out:
-       return mntroot;
+       return nfs_fs_mount_common(server, cfg);
 }
 
-static struct vfsmount *nfs_do_root_mount(struct file_system_type *fs_type,
-               int flags, void *data, const char *hostname)
+/*
+ * Create a mount for the root of the server.  We copy the mount context we
+ * have for the parameters and set its hostname, path and type.
+ */
+static struct vfsmount *nfs_do_root_mount(struct nfs_sb_config *cfg,
+                                         const char *hostname,
+                                         enum nfs_mount_type type)
 {
+       struct nfs_sb_config *root_cfg;
+       struct sb_config *root_sc;
        struct vfsmount *root_mnt;
        char *root_devname;
        size_t len;
 
+       root_sc = vfs_dup_sb_config(&cfg->sc);
+       if (IS_ERR(root_sc))
+               return ERR_CAST(root_sc);
+       root_cfg = container_of(root_sc, struct nfs_sb_config, sc);
+
+       root_cfg->mount_type = type;
+       root_cfg->nfs_server.export_path = (char *)nfs_slash;
+
        len = strlen(hostname) + 5;
+       root_mnt = ERR_PTR(-ENOMEM);
        root_devname = kmalloc(len, GFP_KERNEL);
        if (root_devname == NULL)
-               return ERR_PTR(-ENOMEM);
+               goto out_sc;
+
        /* Does hostname needs to be enclosed in brackets? */
        if (strchr(hostname, ':'))
                snprintf(root_devname, len, "[%s]:/", hostname);
        else
                snprintf(root_devname, len, "%s:/", hostname);
-       root_mnt = vfs_kern_mount(fs_type, flags, root_devname, data);
-       kfree(root_devname);
+       root_cfg->sc.device = root_devname;
+
+       root_mnt = vfs_kern_mount_sc(&root_cfg->sc);
+out_sc:
+       put_sb_config(root_sc);
        return root_mnt;
 }
 
@@ -235,24 +219,24 @@ static struct dentry *nfs_follow_remote_path(struct 
vfsmount *root_mnt,
        return dentry;
 }
 
-struct dentry *nfs4_try_mount(int flags, const char *dev_name,
-                             struct nfs_mount_info *mount_info,
-                             struct nfs_subversion *nfs_mod)
+struct dentry *nfs4_try_mount(struct nfs_sb_config *cfg)
 {
-       char *export_path;
        struct vfsmount *root_mnt;
        struct dentry *res;
-       struct nfs_parsed_mount_data *data = mount_info->parsed;
 
        dfprintk(MOUNT, "--> nfs4_try_mount()\n");
 
-       export_path = data->nfs_server.export_path;
-       data->nfs_server.export_path = "/";
-       root_mnt = nfs_do_root_mount(&nfs4_remote_fs_type, flags, mount_info,
-                       data->nfs_server.hostname);
-       data->nfs_server.export_path = export_path;
+       /* We create a mount for the server's root, walk to the requested
+        * location and then create another mount for that.
+        */
+       root_mnt = nfs_do_root_mount(cfg, cfg->nfs_server.hostname,
+                                    NFS4_MOUNT_REMOTE);
+       if (IS_ERR(root_mnt))
+               return ERR_CAST(root_mnt);
 
-       res = nfs_follow_remote_path(root_mnt, export_path);
+       res = nfs_follow_remote_path(root_mnt, cfg->nfs_server.export_path);
+       if (IS_ERR(res))
+               nfs_cfg_error(cfg, "NFS4: Couldn't follow remote path");
 
        dfprintk(MOUNT, "<-- nfs4_try_mount() = %d%s\n",
                 PTR_ERR_OR_ZERO(res),
@@ -260,64 +244,64 @@ struct dentry *nfs4_try_mount(int flags, const char 
*dev_name,
        return res;
 }
 
-static struct dentry *
-nfs4_remote_referral_mount(struct file_system_type *fs_type, int flags,
-                          const char *dev_name, void *raw_data)
+static struct dentry *nfs4_remote_referral_mount(struct nfs_sb_config *cfg)
 {
-       struct nfs_mount_info mount_info = {
-               .fill_super = nfs_fill_super,
-               .set_security = nfs_clone_sb_security,
-               .cloned = raw_data,
-       };
        struct nfs_server *server;
-       struct dentry *mntroot = ERR_PTR(-ENOMEM);
 
        dprintk("--> nfs4_referral_get_sb()\n");
 
-       mount_info.mntfh = nfs_alloc_fhandle();
-       if (mount_info.cloned == NULL || mount_info.mntfh == NULL)
-               goto out;
+       cfg->set_security = nfs_clone_sb_security;
+
+       if (!cfg->clone_data.cloned)
+               return ERR_PTR(-EINVAL);
 
        /* create a new volume representation */
-       server = nfs4_create_referral_server(mount_info.cloned, 
mount_info.mntfh);
-       if (IS_ERR(server)) {
-               mntroot = ERR_CAST(server);
-               goto out;
-       }
+       server = nfs4_create_referral_server(cfg);
+       if (IS_ERR(server))
+               return ERR_CAST(server);
 
-       mntroot = nfs_fs_mount_common(server, flags, dev_name, &mount_info, 
&nfs_v4);
-out:
-       nfs_free_fhandle(mount_info.mntfh);
-       return mntroot;
+       return nfs_fs_mount_common(server, cfg);
 }
 
 /*
  * Create an NFS4 server record on referral traversal
  */
-static struct dentry *nfs4_referral_mount(struct file_system_type *fs_type,
-               int flags, const char *dev_name, void *raw_data)
+static struct dentry *nfs4_referral_mount(struct nfs_sb_config *cfg)
 {
-       struct nfs_clone_mount *data = raw_data;
-       char *export_path;
        struct vfsmount *root_mnt;
        struct dentry *res;
 
        dprintk("--> nfs4_referral_mount()\n");
 
-       export_path = data->mnt_path;
-       data->mnt_path = "/";
-
-       root_mnt = nfs_do_root_mount(&nfs4_remote_referral_fs_type,
-                       flags, data, data->hostname);
-       data->mnt_path = export_path;
+       root_mnt = nfs_do_root_mount(cfg, cfg->nfs_server.hostname,
+                                    NFS4_MOUNT_REMOTE_REFERRAL);
 
-       res = nfs_follow_remote_path(root_mnt, export_path);
+       res = nfs_follow_remote_path(root_mnt, cfg->nfs_server.export_path);
        dprintk("<-- nfs4_referral_mount() = %d%s\n",
                PTR_ERR_OR_ZERO(res),
                IS_ERR(res) ? " [error]" : "");
        return res;
 }
 
+/*
+ * Handle special NFS4 mount types.
+ */
+struct dentry *nfs4_mount(struct nfs_sb_config *cfg)
+{
+       switch (cfg->mount_type) {
+       case NFS4_MOUNT_REMOTE:
+               return nfs4_remote_mount(cfg);
+
+       case NFS4_MOUNT_REFERRAL:
+               return nfs4_referral_mount(cfg);
+
+       case NFS4_MOUNT_REMOTE_REFERRAL:
+               return nfs4_remote_referral_mount(cfg);
+
+       default:
+               return nfs_general_mount(cfg);
+       }
+}
 
 static int __init init_nfs_v4(void)
 {
diff --git a/fs/nfs/proc.c b/fs/nfs/proc.c
index b7bca8303989..edae9cd50412 100644
--- a/fs/nfs/proc.c
+++ b/fs/nfs/proc.c
@@ -704,6 +704,7 @@ const struct nfs_rpc_ops nfs_v2_clientops = {
        .file_inode_ops = &nfs_file_inode_operations,
        .file_ops       = &nfs_file_operations,
        .getroot        = nfs_proc_get_root,
+       .mount          = nfs_general_mount,
        .submount       = nfs_submount,
        .try_mount      = nfs_try_mount,
        .getattr        = nfs_proc_getattr,
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index dc69314d455e..354044fec350 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -68,244 +68,6 @@
 #include "nfs.h"
 
 #define NFSDBG_FACILITY                NFSDBG_VFS
-#define NFS_TEXT_DATA          1
-
-#if IS_ENABLED(CONFIG_NFS_V3)
-#define NFS_DEFAULT_VERSION 3
-#else
-#define NFS_DEFAULT_VERSION 2
-#endif
-
-enum {
-       /* Mount options that take no arguments */
-       Opt_soft, Opt_hard,
-       Opt_posix, Opt_noposix,
-       Opt_cto, Opt_nocto,
-       Opt_ac, Opt_noac,
-       Opt_lock, Opt_nolock,
-       Opt_udp, Opt_tcp, Opt_rdma,
-       Opt_acl, Opt_noacl,
-       Opt_rdirplus, Opt_nordirplus,
-       Opt_sharecache, Opt_nosharecache,
-       Opt_resvport, Opt_noresvport,
-       Opt_fscache, Opt_nofscache,
-       Opt_migration, Opt_nomigration,
-
-       /* Mount options that take integer arguments */
-       Opt_port,
-       Opt_rsize, Opt_wsize, Opt_bsize,
-       Opt_timeo, Opt_retrans,
-       Opt_acregmin, Opt_acregmax,
-       Opt_acdirmin, Opt_acdirmax,
-       Opt_actimeo,
-       Opt_namelen,
-       Opt_mountport,
-       Opt_mountvers,
-       Opt_minorversion,
-
-       /* Mount options that take string arguments */
-       Opt_nfsvers,
-       Opt_sec, Opt_proto, Opt_mountproto, Opt_mounthost,
-       Opt_addr, Opt_mountaddr, Opt_clientaddr,
-       Opt_lookupcache,
-       Opt_fscache_uniq,
-       Opt_local_lock,
-
-       /* Special mount options */
-       Opt_userspace, Opt_deprecated, Opt_sloppy,
-
-       Opt_err
-};
-
-static const match_table_t nfs_mount_option_tokens = {
-       { Opt_userspace, "bg" },
-       { Opt_userspace, "fg" },
-       { Opt_userspace, "retry=%s" },
-
-       { Opt_sloppy, "sloppy" },
-
-       { Opt_soft, "soft" },
-       { Opt_hard, "hard" },
-       { Opt_deprecated, "intr" },
-       { Opt_deprecated, "nointr" },
-       { Opt_posix, "posix" },
-       { Opt_noposix, "noposix" },
-       { Opt_cto, "cto" },
-       { Opt_nocto, "nocto" },
-       { Opt_ac, "ac" },
-       { Opt_noac, "noac" },
-       { Opt_lock, "lock" },
-       { Opt_nolock, "nolock" },
-       { Opt_udp, "udp" },
-       { Opt_tcp, "tcp" },
-       { Opt_rdma, "rdma" },
-       { Opt_acl, "acl" },
-       { Opt_noacl, "noacl" },
-       { Opt_rdirplus, "rdirplus" },
-       { Opt_nordirplus, "nordirplus" },
-       { Opt_sharecache, "sharecache" },
-       { Opt_nosharecache, "nosharecache" },
-       { Opt_resvport, "resvport" },
-       { Opt_noresvport, "noresvport" },
-       { Opt_fscache, "fsc" },
-       { Opt_nofscache, "nofsc" },
-       { Opt_migration, "migration" },
-       { Opt_nomigration, "nomigration" },
-
-       { Opt_port, "port=%s" },
-       { Opt_rsize, "rsize=%s" },
-       { Opt_wsize, "wsize=%s" },
-       { Opt_bsize, "bsize=%s" },
-       { Opt_timeo, "timeo=%s" },
-       { Opt_retrans, "retrans=%s" },
-       { Opt_acregmin, "acregmin=%s" },
-       { Opt_acregmax, "acregmax=%s" },
-       { Opt_acdirmin, "acdirmin=%s" },
-       { Opt_acdirmax, "acdirmax=%s" },
-       { Opt_actimeo, "actimeo=%s" },
-       { Opt_namelen, "namlen=%s" },
-       { Opt_mountport, "mountport=%s" },
-       { Opt_mountvers, "mountvers=%s" },
-       { Opt_minorversion, "minorversion=%s" },
-
-       { Opt_nfsvers, "nfsvers=%s" },
-       { Opt_nfsvers, "vers=%s" },
-
-       { Opt_sec, "sec=%s" },
-       { Opt_proto, "proto=%s" },
-       { Opt_mountproto, "mountproto=%s" },
-       { Opt_addr, "addr=%s" },
-       { Opt_clientaddr, "clientaddr=%s" },
-       { Opt_mounthost, "mounthost=%s" },
-       { Opt_mountaddr, "mountaddr=%s" },
-
-       { Opt_lookupcache, "lookupcache=%s" },
-       { Opt_fscache_uniq, "fsc=%s" },
-       { Opt_local_lock, "local_lock=%s" },
-
-       /* The following needs to be listed after all other options */
-       { Opt_nfsvers, "v%s" },
-
-       { Opt_err, NULL }
-};
-
-enum {
-       Opt_xprt_udp, Opt_xprt_udp6, Opt_xprt_tcp, Opt_xprt_tcp6, Opt_xprt_rdma,
-       Opt_xprt_rdma6,
-
-       Opt_xprt_err
-};
-
-static const match_table_t nfs_xprt_protocol_tokens = {
-       { Opt_xprt_udp, "udp" },
-       { Opt_xprt_udp6, "udp6" },
-       { Opt_xprt_tcp, "tcp" },
-       { Opt_xprt_tcp6, "tcp6" },
-       { Opt_xprt_rdma, "rdma" },
-       { Opt_xprt_rdma6, "rdma6" },
-
-       { Opt_xprt_err, NULL }
-};
-
-enum {
-       Opt_sec_none, Opt_sec_sys,
-       Opt_sec_krb5, Opt_sec_krb5i, Opt_sec_krb5p,
-       Opt_sec_lkey, Opt_sec_lkeyi, Opt_sec_lkeyp,
-       Opt_sec_spkm, Opt_sec_spkmi, Opt_sec_spkmp,
-
-       Opt_sec_err
-};
-
-static const match_table_t nfs_secflavor_tokens = {
-       { Opt_sec_none, "none" },
-       { Opt_sec_none, "null" },
-       { Opt_sec_sys, "sys" },
-
-       { Opt_sec_krb5, "krb5" },
-       { Opt_sec_krb5i, "krb5i" },
-       { Opt_sec_krb5p, "krb5p" },
-
-       { Opt_sec_lkey, "lkey" },
-       { Opt_sec_lkeyi, "lkeyi" },
-       { Opt_sec_lkeyp, "lkeyp" },
-
-       { Opt_sec_spkm, "spkm3" },
-       { Opt_sec_spkmi, "spkm3i" },
-       { Opt_sec_spkmp, "spkm3p" },
-
-       { Opt_sec_err, NULL }
-};
-
-enum {
-       Opt_lookupcache_all, Opt_lookupcache_positive,
-       Opt_lookupcache_none,
-
-       Opt_lookupcache_err
-};
-
-static match_table_t nfs_lookupcache_tokens = {
-       { Opt_lookupcache_all, "all" },
-       { Opt_lookupcache_positive, "pos" },
-       { Opt_lookupcache_positive, "positive" },
-       { Opt_lookupcache_none, "none" },
-
-       { Opt_lookupcache_err, NULL }
-};
-
-enum {
-       Opt_local_lock_all, Opt_local_lock_flock, Opt_local_lock_posix,
-       Opt_local_lock_none,
-
-       Opt_local_lock_err
-};
-
-static match_table_t nfs_local_lock_tokens = {
-       { Opt_local_lock_all, "all" },
-       { Opt_local_lock_flock, "flock" },
-       { Opt_local_lock_posix, "posix" },
-       { Opt_local_lock_none, "none" },
-
-       { Opt_local_lock_err, NULL }
-};
-
-enum {
-       Opt_vers_2, Opt_vers_3, Opt_vers_4, Opt_vers_4_0,
-       Opt_vers_4_1, Opt_vers_4_2,
-
-       Opt_vers_err
-};
-
-static match_table_t nfs_vers_tokens = {
-       { Opt_vers_2, "2" },
-       { Opt_vers_3, "3" },
-       { Opt_vers_4, "4" },
-       { Opt_vers_4_0, "4.0" },
-       { Opt_vers_4_1, "4.1" },
-       { Opt_vers_4_2, "4.2" },
-
-       { Opt_vers_err, NULL }
-};
-
-static struct dentry *nfs_xdev_mount(struct file_system_type *fs_type,
-               int flags, const char *dev_name, void *raw_data);
-
-struct file_system_type nfs_fs_type = {
-       .owner          = THIS_MODULE,
-       .name           = "nfs",
-       .mount          = nfs_fs_mount,
-       .kill_sb        = nfs_kill_super,
-       .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
-};
-MODULE_ALIAS_FS("nfs");
-EXPORT_SYMBOL_GPL(nfs_fs_type);
-
-struct file_system_type nfs_xdev_fs_type = {
-       .owner          = THIS_MODULE,
-       .name           = "nfs",
-       .mount          = nfs_xdev_mount,
-       .kill_sb        = nfs_kill_super,
-       .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
-};
 
 const struct super_operations nfs_sops = {
        .alloc_inode    = nfs_alloc_inode,
@@ -319,26 +81,11 @@ const struct super_operations nfs_sops = {
        .show_devname   = nfs_show_devname,
        .show_path      = nfs_show_path,
        .show_stats     = nfs_show_stats,
-       .remount_fs     = nfs_remount,
+       .remount_fs_sc  = nfs_remount,
 };
 EXPORT_SYMBOL_GPL(nfs_sops);
 
 #if IS_ENABLED(CONFIG_NFS_V4)
-static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *);
-static int nfs4_validate_mount_data(void *options,
-       struct nfs_parsed_mount_data *args, const char *dev_name);
-
-struct file_system_type nfs4_fs_type = {
-       .owner          = THIS_MODULE,
-       .name           = "nfs4",
-       .mount          = nfs_fs_mount,
-       .kill_sb        = nfs_kill_super,
-       .fs_flags       = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
-};
-MODULE_ALIAS_FS("nfs4");
-MODULE_ALIAS("nfs4");
-EXPORT_SYMBOL_GPL(nfs4_fs_type);
-
 static int __init register_nfs4_fs(void)
 {
        return register_filesystem(&nfs4_fs_type);
@@ -910,141 +657,6 @@ void nfs_umount_begin(struct super_block *sb)
 }
 EXPORT_SYMBOL_GPL(nfs_umount_begin);
 
-static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(void)
-{
-       struct nfs_parsed_mount_data *data;
-
-       data = kzalloc(sizeof(*data), GFP_KERNEL);
-       if (data) {
-               data->timeo             = NFS_UNSPEC_TIMEO;
-               data->retrans           = NFS_UNSPEC_RETRANS;
-               data->acregmin          = NFS_DEF_ACREGMIN;
-               data->acregmax          = NFS_DEF_ACREGMAX;
-               data->acdirmin          = NFS_DEF_ACDIRMIN;
-               data->acdirmax          = NFS_DEF_ACDIRMAX;
-               data->mount_server.port = NFS_UNSPEC_PORT;
-               data->nfs_server.port   = NFS_UNSPEC_PORT;
-               data->nfs_server.protocol = XPRT_TRANSPORT_TCP;
-               data->selected_flavor   = RPC_AUTH_MAXFLAVOR;
-               data->minorversion      = 0;
-               data->need_mount        = true;
-               data->net               = current->nsproxy->net_ns;
-               security_init_mnt_opts(&data->lsm_opts);
-       }
-       return data;
-}
-
-static void nfs_free_parsed_mount_data(struct nfs_parsed_mount_data *data)
-{
-       if (data) {
-               kfree(data->client_address);
-               kfree(data->mount_server.hostname);
-               kfree(data->nfs_server.export_path);
-               kfree(data->nfs_server.hostname);
-               kfree(data->fscache_uniq);
-               security_free_mnt_opts(&data->lsm_opts);
-               kfree(data);
-       }
-}
-
-/*
- * Sanity-check a server address provided by the mount command.
- *
- * Address family must be initialized, and address must not be
- * the ANY address for that family.
- */
-static int nfs_verify_server_address(struct sockaddr *addr)
-{
-       switch (addr->sa_family) {
-       case AF_INET: {
-               struct sockaddr_in *sa = (struct sockaddr_in *)addr;
-               return sa->sin_addr.s_addr != htonl(INADDR_ANY);
-       }
-       case AF_INET6: {
-               struct in6_addr *sa = &((struct sockaddr_in6 *)addr)->sin6_addr;
-               return !ipv6_addr_any(sa);
-       }
-       }
-
-       dfprintk(MOUNT, "NFS: Invalid IP address specified\n");
-       return 0;
-}
-
-/*
- * Select between a default port value and a user-specified port value.
- * If a zero value is set, then autobind will be used.
- */
-static void nfs_set_port(struct sockaddr *sap, int *port,
-                                const unsigned short default_port)
-{
-       if (*port == NFS_UNSPEC_PORT)
-               *port = default_port;
-
-       rpc_set_port(sap, *port);
-}
-
-/*
- * Sanity check the NFS transport protocol.
- *
- */
-static void nfs_validate_transport_protocol(struct nfs_parsed_mount_data *mnt)
-{
-       switch (mnt->nfs_server.protocol) {
-       case XPRT_TRANSPORT_UDP:
-       case XPRT_TRANSPORT_TCP:
-       case XPRT_TRANSPORT_RDMA:
-               break;
-       default:
-               mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP;
-       }
-}
-
-/*
- * For text based NFSv2/v3 mounts, the mount protocol transport default
- * settings should depend upon the specified NFS transport.
- */
-static void nfs_set_mount_transport_protocol(struct nfs_parsed_mount_data *mnt)
-{
-       nfs_validate_transport_protocol(mnt);
-
-       if (mnt->mount_server.protocol == XPRT_TRANSPORT_UDP ||
-           mnt->mount_server.protocol == XPRT_TRANSPORT_TCP)
-                       return;
-       switch (mnt->nfs_server.protocol) {
-       case XPRT_TRANSPORT_UDP:
-               mnt->mount_server.protocol = XPRT_TRANSPORT_UDP;
-               break;
-       case XPRT_TRANSPORT_TCP:
-       case XPRT_TRANSPORT_RDMA:
-               mnt->mount_server.protocol = XPRT_TRANSPORT_TCP;
-       }
-}
-
-/*
- * Add 'flavor' to 'auth_info' if not already present.
- * Returns true if 'flavor' ends up in the list, false otherwise
- */
-static bool nfs_auth_info_add(struct nfs_auth_info *auth_info,
-                             rpc_authflavor_t flavor)
-{
-       unsigned int i;
-       unsigned int max_flavor_len = ARRAY_SIZE(auth_info->flavors);
-
-       /* make sure this flavor isn't already in the list */
-       for (i = 0; i < auth_info->flavor_len; i++) {
-               if (flavor == auth_info->flavors[i])
-                       return true;
-       }
-
-       if (auth_info->flavor_len + 1 >= max_flavor_len) {
-               dfprintk(MOUNT, "NFS: too many sec= flavors\n");
-               return false;
-       }
-
-       auth_info->flavors[auth_info->flavor_len++] = flavor;
-       return true;
-}
-
 /*
  * Return true if 'match' is in auth_info or auth_info is empty.
  * Return false otherwise.
@@ -1066,628 +678,11 @@ bool nfs_auth_info_match(const struct nfs_auth_info 
*auth_info,
 EXPORT_SYMBOL_GPL(nfs_auth_info_match);
 
 /*
- * Parse the value of the 'sec=' option.
- */
-static int nfs_parse_security_flavors(char *value,
-                                     struct nfs_parsed_mount_data *mnt)
-{
-       substring_t args[MAX_OPT_ARGS];
-       rpc_authflavor_t pseudoflavor;
-       char *p;
-
-       dfprintk(MOUNT, "NFS: parsing sec=%s option\n", value);
-
-       while ((p = strsep(&value, ":")) != NULL) {
-               switch (match_token(p, nfs_secflavor_tokens, args)) {
-               case Opt_sec_none:
-                       pseudoflavor = RPC_AUTH_NULL;
-                       break;
-               case Opt_sec_sys:
-                       pseudoflavor = RPC_AUTH_UNIX;
-                       break;
-               case Opt_sec_krb5:
-                       pseudoflavor = RPC_AUTH_GSS_KRB5;
-                       break;
-               case Opt_sec_krb5i:
-                       pseudoflavor = RPC_AUTH_GSS_KRB5I;
-                       break;
-               case Opt_sec_krb5p:
-                       pseudoflavor = RPC_AUTH_GSS_KRB5P;
-                       break;
-               case Opt_sec_lkey:
-                       pseudoflavor = RPC_AUTH_GSS_LKEY;
-                       break;
-               case Opt_sec_lkeyi:
-                       pseudoflavor = RPC_AUTH_GSS_LKEYI;
-                       break;
-               case Opt_sec_lkeyp:
-                       pseudoflavor = RPC_AUTH_GSS_LKEYP;
-                       break;
-               case Opt_sec_spkm:
-                       pseudoflavor = RPC_AUTH_GSS_SPKM;
-                       break;
-               case Opt_sec_spkmi:
-                       pseudoflavor = RPC_AUTH_GSS_SPKMI;
-                       break;
-               case Opt_sec_spkmp:
-                       pseudoflavor = RPC_AUTH_GSS_SPKMP;
-                       break;
-               default:
-                       dfprintk(MOUNT,
-                                "NFS: sec= option '%s' not recognized\n", p);
-                       return 0;
-               }
-
-               if (!nfs_auth_info_add(&mnt->auth_info, pseudoflavor))
-                       return 0;
-       }
-
-       return 1;
-}
-
-static int nfs_parse_version_string(char *string,
-               struct nfs_parsed_mount_data *mnt,
-               substring_t *args)
-{
-       mnt->flags &= ~NFS_MOUNT_VER3;
-       switch (match_token(string, nfs_vers_tokens, args)) {
-       case Opt_vers_2:
-               mnt->version = 2;
-               break;
-       case Opt_vers_3:
-               mnt->flags |= NFS_MOUNT_VER3;
-               mnt->version = 3;
-               break;
-       case Opt_vers_4:
-               /* Backward compatibility option. In future,
-                * the mount program should always supply
-                * a NFSv4 minor version number.
-                */
-               mnt->version = 4;
-               break;
-       case Opt_vers_4_0:
-               mnt->version = 4;
-               mnt->minorversion = 0;
-               break;
-       case Opt_vers_4_1:
-               mnt->version = 4;
-               mnt->minorversion = 1;
-               break;
-       case Opt_vers_4_2:
-               mnt->version = 4;
-               mnt->minorversion = 2;
-               break;
-       default:
-               return 0;
-       }
-       return 1;
-}
-
-static int nfs_get_option_str(substring_t args[], char **option)
-{
-       kfree(*option);
-       *option = match_strdup(args);
-       return !*option;
-}
-
-static int nfs_get_option_ul(substring_t args[], unsigned long *option)
-{
-       int rc;
-       char *string;
-
-       string = match_strdup(args);
-       if (string == NULL)
-               return -ENOMEM;
-       rc = kstrtoul(string, 10, option);
-       kfree(string);
-
-       return rc;
-}
-
-static int nfs_get_option_ul_bound(substring_t args[], unsigned long *option,
-               unsigned long l_bound, unsigned long u_bound)
-{
-       int ret;
-
-       ret = nfs_get_option_ul(args, option);
-       if (ret != 0)
-               return ret;
-       if (*option < l_bound || *option > u_bound)
-               return -ERANGE;
-       return 0;
-}
-
-/*
- * Error-check and convert a string of mount options from user space into
- * a data structure.  The whole mount string is processed; bad options are
- * skipped as they are encountered.  If there were no errors, return 1;
- * otherwise return 0 (zero).
- */
-static int nfs_parse_mount_options(char *raw,
-                                  struct nfs_parsed_mount_data *mnt)
-{
-       char *p, *string, *secdata;
-       int rc, sloppy = 0, invalid_option = 0;
-       unsigned short protofamily = AF_UNSPEC;
-       unsigned short mountfamily = AF_UNSPEC;
-
-       if (!raw) {
-               dfprintk(MOUNT, "NFS: mount options string was NULL.\n");
-               return 1;
-       }
-       dfprintk(MOUNT, "NFS: nfs mount opts='%s'\n", raw);
-
-       secdata = alloc_secdata();
-       if (!secdata)
-               goto out_nomem;
-
-       rc = security_sb_copy_data(raw, secdata);
-       if (rc)
-               goto out_security_failure;
-
-       rc = security_sb_parse_opts_str(secdata, &mnt->lsm_opts);
-       if (rc)
-               goto out_security_failure;
-
-       free_secdata(secdata);
-
-       while ((p = strsep(&raw, ",")) != NULL) {
-               substring_t args[MAX_OPT_ARGS];
-               unsigned long option;
-               int token;
-
-               if (!*p)
-                       continue;
-
-               dfprintk(MOUNT, "NFS:   parsing nfs mount option '%s'\n", p);
-
-               token = match_token(p, nfs_mount_option_tokens, args);
-               switch (token) {
-
-               /*
-                * boolean options:  foo/nofoo
-                */
-               case Opt_soft:
-                       mnt->flags |= NFS_MOUNT_SOFT;
-                       break;
-               case Opt_hard:
-                       mnt->flags &= ~NFS_MOUNT_SOFT;
-                       break;
-               case Opt_posix:
-                       mnt->flags |= NFS_MOUNT_POSIX;
-                       break;
-               case Opt_noposix:
-                       mnt->flags &= ~NFS_MOUNT_POSIX;
-                       break;
-               case Opt_cto:
-                       mnt->flags &= ~NFS_MOUNT_NOCTO;
-                       break;
-               case Opt_nocto:
-                       mnt->flags |= NFS_MOUNT_NOCTO;
-                       break;
-               case Opt_ac:
-                       mnt->flags &= ~NFS_MOUNT_NOAC;
-                       break;
-               case Opt_noac:
-                       mnt->flags |= NFS_MOUNT_NOAC;
-                       break;
-               case Opt_lock:
-                       mnt->flags &= ~NFS_MOUNT_NONLM;
-                       mnt->flags &= ~(NFS_MOUNT_LOCAL_FLOCK |
-                                       NFS_MOUNT_LOCAL_FCNTL);
-                       break;
-               case Opt_nolock:
-                       mnt->flags |= NFS_MOUNT_NONLM;
-                       mnt->flags |= (NFS_MOUNT_LOCAL_FLOCK |
-                                      NFS_MOUNT_LOCAL_FCNTL);
-                       break;
-               case Opt_udp:
-                       mnt->flags &= ~NFS_MOUNT_TCP;
-                       mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP;
-                       break;
-               case Opt_tcp:
-                       mnt->flags |= NFS_MOUNT_TCP;
-                       mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP;
-                       break;
-               case Opt_rdma:
-                       mnt->flags |= NFS_MOUNT_TCP; /* for side protocols */
-                       mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA;
-                       xprt_load_transport(p);
-                       break;
-               case Opt_acl:
-                       mnt->flags &= ~NFS_MOUNT_NOACL;
-                       break;
-               case Opt_noacl:
-                       mnt->flags |= NFS_MOUNT_NOACL;
-                       break;
-               case Opt_rdirplus:
-                       mnt->flags &= ~NFS_MOUNT_NORDIRPLUS;
-                       break;
-               case Opt_nordirplus:
-                       mnt->flags |= NFS_MOUNT_NORDIRPLUS;
-                       break;
-               case Opt_sharecache:
-                       mnt->flags &= ~NFS_MOUNT_UNSHARED;
-                       break;
-               case Opt_nosharecache:
-                       mnt->flags |= NFS_MOUNT_UNSHARED;
-                       break;
-               case Opt_resvport:
-                       mnt->flags &= ~NFS_MOUNT_NORESVPORT;
-                       break;
-               case Opt_noresvport:
-                       mnt->flags |= NFS_MOUNT_NORESVPORT;
-                       break;
-               case Opt_fscache:
-                       mnt->options |= NFS_OPTION_FSCACHE;
-                       kfree(mnt->fscache_uniq);
-                       mnt->fscache_uniq = NULL;
-                       break;
-               case Opt_nofscache:
-                       mnt->options &= ~NFS_OPTION_FSCACHE;
-                       kfree(mnt->fscache_uniq);
-                       mnt->fscache_uniq = NULL;
-                       break;
-               case Opt_migration:
-                       mnt->options |= NFS_OPTION_MIGRATION;
-                       break;
-               case Opt_nomigration:
-                       mnt->options &= NFS_OPTION_MIGRATION;
-                       break;
-
-               /*
-                * options that take numeric values
-                */
-               case Opt_port:
-                       if (nfs_get_option_ul(args, &option) ||
-                           option > USHRT_MAX)
-                               goto out_invalid_value;
-                       mnt->nfs_server.port = option;
-                       break;
-               case Opt_rsize:
-                       if (nfs_get_option_ul(args, &option))
-                               goto out_invalid_value;
-                       mnt->rsize = option;
-                       break;
-               case Opt_wsize:
-                       if (nfs_get_option_ul(args, &option))
-                               goto out_invalid_value;
-                       mnt->wsize = option;
-                       break;
-               case Opt_bsize:
-                       if (nfs_get_option_ul(args, &option))
-                               goto out_invalid_value;
-                       mnt->bsize = option;
-                       break;
-               case Opt_timeo:
-                       if (nfs_get_option_ul_bound(args, &option, 1, INT_MAX))
-                               goto out_invalid_value;
-                       mnt->timeo = option;
-                       break;
-               case Opt_retrans:
-                       if (nfs_get_option_ul_bound(args, &option, 0, INT_MAX))
-                               goto out_invalid_value;
-                       mnt->retrans = option;
-                       break;
-               case Opt_acregmin:
-                       if (nfs_get_option_ul(args, &option))
-                               goto out_invalid_value;
-                       mnt->acregmin = option;
-                       break;
-               case Opt_acregmax:
-                       if (nfs_get_option_ul(args, &option))
-                               goto out_invalid_value;
-                       mnt->acregmax = option;
-                       break;
-               case Opt_acdirmin:
-                       if (nfs_get_option_ul(args, &option))
-                               goto out_invalid_value;
-                       mnt->acdirmin = option;
-                       break;
-               case Opt_acdirmax:
-                       if (nfs_get_option_ul(args, &option))
-                               goto out_invalid_value;
-                       mnt->acdirmax = option;
-                       break;
-               case Opt_actimeo:
-                       if (nfs_get_option_ul(args, &option))
-                               goto out_invalid_value;
-                       mnt->acregmin = mnt->acregmax =
-                       mnt->acdirmin = mnt->acdirmax = option;
-                       break;
-               case Opt_namelen:
-                       if (nfs_get_option_ul(args, &option))
-                               goto out_invalid_value;
-                       mnt->namlen = option;
-                       break;
-               case Opt_mountport:
-                       if (nfs_get_option_ul(args, &option) ||
-                           option > USHRT_MAX)
-                               goto out_invalid_value;
-                       mnt->mount_server.port = option;
-                       break;
-               case Opt_mountvers:
-                       if (nfs_get_option_ul(args, &option) ||
-                           option < NFS_MNT_VERSION ||
-                           option > NFS_MNT3_VERSION)
-                               goto out_invalid_value;
-                       mnt->mount_server.version = option;
-                       break;
-               case Opt_minorversion:
-                       if (nfs_get_option_ul(args, &option))
-                               goto out_invalid_value;
-                       if (option > NFS4_MAX_MINOR_VERSION)
-                               goto out_invalid_value;
-                       mnt->minorversion = option;
-                       break;
-
-               /*
-                * options that take text values
-                */
-               case Opt_nfsvers:
-                       string = match_strdup(args);
-                       if (string == NULL)
-                               goto out_nomem;
-                       rc = nfs_parse_version_string(string, mnt, args);
-                       kfree(string);
-                       if (!rc)
-                               goto out_invalid_value;
-                       break;
-               case Opt_sec:
-                       string = match_strdup(args);
-                       if (string == NULL)
-                               goto out_nomem;
-                       rc = nfs_parse_security_flavors(string, mnt);
-                       kfree(string);
-                       if (!rc) {
-                               dfprintk(MOUNT, "NFS:   unrecognized "
-                                               "security flavor\n");
-                               return 0;
-                       }
-                       break;
-               case Opt_proto:
-                       string = match_strdup(args);
-                       if (string == NULL)
-                               goto out_nomem;
-                       token = match_token(string,
-                                           nfs_xprt_protocol_tokens, args);
-
-                       protofamily = AF_INET;
-                       switch (token) {
-                       case Opt_xprt_udp6:
-                               protofamily = AF_INET6;
-                       case Opt_xprt_udp:
-                               mnt->flags &= ~NFS_MOUNT_TCP;
-                               mnt->nfs_server.protocol = XPRT_TRANSPORT_UDP;
-                               break;
-                       case Opt_xprt_tcp6:
-                               protofamily = AF_INET6;
-                       case Opt_xprt_tcp:
-                               mnt->flags |= NFS_MOUNT_TCP;
-                               mnt->nfs_server.protocol = XPRT_TRANSPORT_TCP;
-                               break;
-                       case Opt_xprt_rdma6:
-                               protofamily = AF_INET6;
-                       case Opt_xprt_rdma:
-                               /* vector side protocols to TCP */
-                               mnt->flags |= NFS_MOUNT_TCP;
-                               mnt->nfs_server.protocol = XPRT_TRANSPORT_RDMA;
-                               xprt_load_transport(string);
-                               break;
-                       default:
-                               dfprintk(MOUNT, "NFS:   unrecognized "
-                                               "transport protocol\n");
-                               kfree(string);
-                               return 0;
-                       }
-                       kfree(string);
-                       break;
-               case Opt_mountproto:
-                       string = match_strdup(args);
-                       if (string == NULL)
-                               goto out_nomem;
-                       token = match_token(string,
-                                           nfs_xprt_protocol_tokens, args);
-                       kfree(string);
-
-                       mountfamily = AF_INET;
-                       switch (token) {
-                       case Opt_xprt_udp6:
-                               mountfamily = AF_INET6;
-                       case Opt_xprt_udp:
-                               mnt->mount_server.protocol = XPRT_TRANSPORT_UDP;
-                               break;
-                       case Opt_xprt_tcp6:
-                               mountfamily = AF_INET6;
-                       case Opt_xprt_tcp:
-                               mnt->mount_server.protocol = XPRT_TRANSPORT_TCP;
-                               break;
-                       case Opt_xprt_rdma: /* not used for side protocols */
-                       default:
-                               dfprintk(MOUNT, "NFS:   unrecognized "
-                                               "transport protocol\n");
-                               return 0;
-                       }
-                       break;
-               case Opt_addr:
-                       string = match_strdup(args);
-                       if (string == NULL)
-                               goto out_nomem;
-                       mnt->nfs_server.addrlen =
-                               rpc_pton(mnt->net, string, strlen(string),
-                                       (struct sockaddr *)
-                                       &mnt->nfs_server.address,
-                                       sizeof(mnt->nfs_server.address));
-                       kfree(string);
-                       if (mnt->nfs_server.addrlen == 0)
-                               goto out_invalid_address;
-                       break;
-               case Opt_clientaddr:
-                       if (nfs_get_option_str(args, &mnt->client_address))
-                               goto out_nomem;
-                       break;
-               case Opt_mounthost:
-                       if (nfs_get_option_str(args,
-                                              &mnt->mount_server.hostname))
-                               goto out_nomem;
-                       break;
-               case Opt_mountaddr:
-                       string = match_strdup(args);
-                       if (string == NULL)
-                               goto out_nomem;
-                       mnt->mount_server.addrlen =
-                               rpc_pton(mnt->net, string, strlen(string),
-                                       (struct sockaddr *)
-                                       &mnt->mount_server.address,
-                                       sizeof(mnt->mount_server.address));
-                       kfree(string);
-                       if (mnt->mount_server.addrlen == 0)
-                               goto out_invalid_address;
-                       break;
-               case Opt_lookupcache:
-                       string = match_strdup(args);
-                       if (string == NULL)
-                               goto out_nomem;
-                       token = match_token(string,
-                                       nfs_lookupcache_tokens, args);
-                       kfree(string);
-                       switch (token) {
-                               case Opt_lookupcache_all:
-                                       mnt->flags &= 
~(NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE);
-                                       break;
-                               case Opt_lookupcache_positive:
-                                       mnt->flags &= 
~NFS_MOUNT_LOOKUP_CACHE_NONE;
-                                       mnt->flags |= 
NFS_MOUNT_LOOKUP_CACHE_NONEG;
-                                       break;
-                               case Opt_lookupcache_none:
-                                       mnt->flags |= 
NFS_MOUNT_LOOKUP_CACHE_NONEG|NFS_MOUNT_LOOKUP_CACHE_NONE;
-                                       break;
-                               default:
-                                       dfprintk(MOUNT, "NFS:   invalid "
-                                                       "lookupcache 
argument\n");
-                                       return 0;
-                       };
-                       break;
-               case Opt_fscache_uniq:
-                       if (nfs_get_option_str(args, &mnt->fscache_uniq))
-                               goto out_nomem;
-                       mnt->options |= NFS_OPTION_FSCACHE;
-                       break;
-               case Opt_local_lock:
-                       string = match_strdup(args);
-                       if (string == NULL)
-                               goto out_nomem;
-                       token = match_token(string, nfs_local_lock_tokens,
-                                       args);
-                       kfree(string);
-                       switch (token) {
-                       case Opt_local_lock_all:
-                               mnt->flags |= (NFS_MOUNT_LOCAL_FLOCK |
-                                              NFS_MOUNT_LOCAL_FCNTL);
-                               break;
-                       case Opt_local_lock_flock:
-                               mnt->flags |= NFS_MOUNT_LOCAL_FLOCK;
-                               break;
-                       case Opt_local_lock_posix:
-                               mnt->flags |= NFS_MOUNT_LOCAL_FCNTL;
-                               break;
-                       case Opt_local_lock_none:
-                               mnt->flags &= ~(NFS_MOUNT_LOCAL_FLOCK |
-                                               NFS_MOUNT_LOCAL_FCNTL);
-                               break;
-                       default:
-                               dfprintk(MOUNT, "NFS:   invalid "
-                                               "local_lock argument\n");
-                               return 0;
-                       };
-                       break;
-
-               /*
-                * Special options
-                */
-               case Opt_sloppy:
-                       sloppy = 1;
-                       dfprintk(MOUNT, "NFS:   relaxing parsing rules\n");
-                       break;
-               case Opt_userspace:
-               case Opt_deprecated:
-                       dfprintk(MOUNT, "NFS:   ignoring mount option "
-                                       "'%s'\n", p);
-                       break;
-
-               default:
-                       invalid_option = 1;
-                       dfprintk(MOUNT, "NFS:   unrecognized mount option "
-                                       "'%s'\n", p);
-               }
-       }
-
-       if (!sloppy && invalid_option)
-               return 0;
-
-       if (mnt->minorversion && mnt->version != 4)
-               goto out_minorversion_mismatch;
-
-       if (mnt->options & NFS_OPTION_MIGRATION &&
-           (mnt->version != 4 || mnt->minorversion != 0))
-               goto out_migration_misuse;
-
-       /*
-        * verify that any proto=/mountproto= options match the address
-        * families in the addr=/mountaddr= options.
-        */
-       if (protofamily != AF_UNSPEC &&
-           protofamily != mnt->nfs_server.address.ss_family)
-               goto out_proto_mismatch;
-
-       if (mountfamily != AF_UNSPEC) {
-               if (mnt->mount_server.addrlen) {
-                       if (mountfamily != mnt->mount_server.address.ss_family)
-                               goto out_mountproto_mismatch;
-               } else {
-                       if (mountfamily != mnt->nfs_server.address.ss_family)
-                               goto out_mountproto_mismatch;
-               }
-       }
-
-       return 1;
-
-out_mountproto_mismatch:
-       printk(KERN_INFO "NFS: mount server address does not match mountproto= "
-                        "option\n");
-       return 0;
-out_proto_mismatch:
-       printk(KERN_INFO "NFS: server address does not match proto= option\n");
-       return 0;
-out_invalid_address:
-       printk(KERN_INFO "NFS: bad IP address specified: %s\n", p);
-       return 0;
-out_invalid_value:
-       printk(KERN_INFO "NFS: bad mount option value specified: %s\n", p);
-       return 0;
-out_minorversion_mismatch:
-       printk(KERN_INFO "NFS: mount option vers=%u does not support "
-                        "minorversion=%u\n", mnt->version, mnt->minorversion);
-       return 0;
-out_migration_misuse:
-       printk(KERN_INFO
-               "NFS: 'migration' not supported for this NFS version\n");
-       return 0;
-out_nomem:
-       printk(KERN_INFO "NFS: not enough memory to parse option\n");
-       return 0;
-out_security_failure:
-       free_secdata(secdata);
-       printk(KERN_INFO "NFS: security options invalid: %d\n", rc);
-       return 0;
-}
-
-/*
  * Ensure that a specified authtype in args->auth_info is supported by
  * the server. Returns 0 and sets args->selected_flavor if it's ok, and
  * -EACCES if not.
  */
-static int nfs_verify_authflavors(struct nfs_parsed_mount_data *args,
+static int nfs_verify_authflavors(struct nfs_sb_config *args,
                        rpc_authflavor_t *server_authlist, unsigned int count)
 {
        rpc_authflavor_t flavor = RPC_AUTH_MAXFLAVOR;
@@ -1731,50 +726,50 @@ static int nfs_verify_authflavors(struct 
nfs_parsed_mount_data *args,
  * Use the remote server's MOUNT service to request the NFS file handle
  * corresponding to the provided path.
  */
-static int nfs_request_mount(struct nfs_parsed_mount_data *args,
+static int nfs_request_mount(struct nfs_sb_config *cfg,
                             struct nfs_fh *root_fh,
                             rpc_authflavor_t *server_authlist,
                             unsigned int *server_authlist_len)
 {
        struct nfs_mount_request request = {
                .sap            = (struct sockaddr *)
-                                               &args->mount_server.address,
-               .dirpath        = args->nfs_server.export_path,
-               .protocol       = args->mount_server.protocol,
+                                               &cfg->mount_server.address,
+               .dirpath        = cfg->nfs_server.export_path,
+               .protocol       = cfg->mount_server.protocol,
                .fh             = root_fh,
-               .noresvport     = args->flags & NFS_MOUNT_NORESVPORT,
+               .noresvport     = cfg->flags & NFS_MOUNT_NORESVPORT,
                .auth_flav_len  = server_authlist_len,
                .auth_flavs     = server_authlist,
-               .net            = args->net,
+               .net            = cfg->sc.net_ns,
        };
        int status;
 
-       if (args->mount_server.version == 0) {
-               switch (args->version) {
+       if (cfg->mount_server.version == 0) {
+               switch (cfg->version) {
                        default:
-                               args->mount_server.version = NFS_MNT3_VERSION;
+                               cfg->mount_server.version = NFS_MNT3_VERSION;
                                break;
                        case 2:
-                               args->mount_server.version = NFS_MNT_VERSION;
+                               cfg->mount_server.version = NFS_MNT_VERSION;
                }
        }
-       request.version = args->mount_server.version;
+       request.version = cfg->mount_server.version;
 
-       if (args->mount_server.hostname)
-               request.hostname = args->mount_server.hostname;
+       if (cfg->mount_server.hostname)
+               request.hostname = cfg->mount_server.hostname;
        else
-               request.hostname = args->nfs_server.hostname;
+               request.hostname = cfg->nfs_server.hostname;
 
        /*
         * Construct the mount server's address.
         */
-       if (args->mount_server.address.ss_family == AF_UNSPEC) {
-               memcpy(request.sap, &args->nfs_server.address,
-                      args->nfs_server.addrlen);
-               args->mount_server.addrlen = args->nfs_server.addrlen;
+       if (cfg->mount_server.address.sa_family == AF_UNSPEC) {
+               memcpy(request.sap, &cfg->nfs_server.address,
+                      cfg->nfs_server.addrlen);
+               cfg->mount_server.addrlen = cfg->nfs_server.addrlen;
        }
-       request.salen = args->mount_server.addrlen;
-       nfs_set_port(request.sap, &args->mount_server.port, 0);
+       request.salen = cfg->mount_server.addrlen;
+       nfs_set_port(request.sap, &cfg->mount_server.port, 0);
 
        /*
         * Now ask the mount server to map our export path
@@ -1790,20 +785,17 @@ static int nfs_request_mount(struct 
nfs_parsed_mount_data *args,
        return 0;
 }
 
-static struct nfs_server *nfs_try_mount_request(struct nfs_mount_info 
*mount_info,
-                                       struct nfs_subversion *nfs_mod)
+static struct nfs_server *nfs_try_mount_request(struct nfs_sb_config *cfg)
 {
        int status;
        unsigned int i;
        bool tried_auth_unix = false;
        bool auth_null_in_list = false;
        struct nfs_server *server = ERR_PTR(-EACCES);
-       struct nfs_parsed_mount_data *args = mount_info->parsed;
        rpc_authflavor_t authlist[NFS_MAX_SECFLAVORS];
        unsigned int authlist_len = ARRAY_SIZE(authlist);
 
-       status = nfs_request_mount(args, mount_info->mntfh, authlist,
-                                       &authlist_len);
+       status = nfs_request_mount(cfg, cfg->mntfh, authlist, &authlist_len);
        if (status)
                return ERR_PTR(status);
 
@@ -1811,13 +803,13 @@ static struct nfs_server *nfs_try_mount_request(struct 
nfs_mount_info *mount_inf
         * Was a sec= authflavor specified in the options? First, verify
         * whether the server supports it, and then just try to use it if so.
         */
-       if (args->auth_info.flavor_len > 0) {
-               status = nfs_verify_authflavors(args, authlist, authlist_len);
+       if (cfg->auth_info.flavor_len > 0) {
+               status = nfs_verify_authflavors(cfg, authlist, authlist_len);
                dfprintk(MOUNT, "NFS: using auth flavor %u\n",
-                        args->selected_flavor);
+                        cfg->selected_flavor);
                if (status)
                        return ERR_PTR(status);
-               return nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);
+               return cfg->nfs_mod->rpc_ops->create_server(cfg);
        }
 
        /*
@@ -1843,8 +835,8 @@ static struct nfs_server *nfs_try_mount_request(struct 
nfs_mount_info *mount_inf
                        /* Fallthrough */
                }
                dfprintk(MOUNT, "NFS: attempting to use auth flavor %u\n", 
flavor);
-               args->selected_flavor = flavor;
-               server = nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);
+               cfg->selected_flavor = flavor;
+               server = cfg->nfs_mod->rpc_ops->create_server(cfg);
                if (!IS_ERR(server))
                        return server;
        }
@@ -1859,338 +851,28 @@ static struct nfs_server *nfs_try_mount_request(struct 
nfs_mount_info *mount_inf
 
        /* Last chance! Try AUTH_UNIX */
        dfprintk(MOUNT, "NFS: attempting to use auth flavor %u\n", 
RPC_AUTH_UNIX);
-       args->selected_flavor = RPC_AUTH_UNIX;
-       return nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);
+       cfg->selected_flavor = RPC_AUTH_UNIX;
+       return cfg->nfs_mod->rpc_ops->create_server(cfg);
 }
 
-struct dentry *nfs_try_mount(int flags, const char *dev_name,
-                            struct nfs_mount_info *mount_info,
-                            struct nfs_subversion *nfs_mod)
+struct dentry *nfs_try_mount(struct nfs_sb_config *cfg)
 {
        struct nfs_server *server;
 
-       if (mount_info->parsed->need_mount)
-               server = nfs_try_mount_request(mount_info, nfs_mod);
+       if (cfg->need_mount)
+               server = nfs_try_mount_request(cfg);
        else
-               server = nfs_mod->rpc_ops->create_server(mount_info, nfs_mod);
+               server = cfg->nfs_mod->rpc_ops->create_server(cfg);
 
-       if (IS_ERR(server))
+       if (IS_ERR(server)) {
+               nfs_cfg_error(cfg, "NFS: Couldn't create server");
                return ERR_CAST(server);
-
-       return nfs_fs_mount_common(server, flags, dev_name, mount_info, 
nfs_mod);
-}
-EXPORT_SYMBOL_GPL(nfs_try_mount);
-
-/*
- * Split "dev_name" into "hostname:export_path".
- *
- * The leftmost colon demarks the split between the server's hostname
- * and the export path.  If the hostname starts with a left square
- * bracket, then it may contain colons.
- *
- * Note: caller frees hostname and export path, even on error.
- */
-static int nfs_parse_devname(const char *dev_name,
-                            char **hostname, size_t maxnamlen,
-                            char **export_path, size_t maxpathlen)
-{
-       size_t len;
-       char *end;
-
-       /* Is the host name protected with square brakcets? */
-       if (*dev_name == '[') {
-               end = strchr(++dev_name, ']');
-               if (end == NULL || end[1] != ':')
-                       goto out_bad_devname;
-
-               len = end - dev_name;
-               end++;
-       } else {
-               char *comma;
-
-               end = strchr(dev_name, ':');
-               if (end == NULL)
-                       goto out_bad_devname;
-               len = end - dev_name;
-
-               /* kill possible hostname list: not supported */
-               comma = strchr(dev_name, ',');
-               if (comma != NULL && comma < end)
-                       *comma = 0;
        }
 
-       if (len > maxnamlen)
-               goto out_hostname;
-
-       /* N.B. caller will free nfs_server.hostname in all cases */
-       *hostname = kstrndup(dev_name, len, GFP_KERNEL);
-       if (*hostname == NULL)
-               goto out_nomem;
-       len = strlen(++end);
-       if (len > maxpathlen)
-               goto out_path;
-       *export_path = kstrndup(end, len, GFP_KERNEL);
-       if (!*export_path)
-               goto out_nomem;
-
-       dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", *export_path);
-       return 0;
-
-out_bad_devname:
-       dfprintk(MOUNT, "NFS: device name not in host:path format\n");
-       return -EINVAL;
-
-out_nomem:
-       dfprintk(MOUNT, "NFS: not enough memory to parse device name\n");
-       return -ENOMEM;
-
-out_hostname:
-       dfprintk(MOUNT, "NFS: server hostname too long\n");
-       return -ENAMETOOLONG;
-
-out_path:
-       dfprintk(MOUNT, "NFS: export pathname too long\n");
-       return -ENAMETOOLONG;
+       return nfs_fs_mount_common(server, cfg);
 }
+EXPORT_SYMBOL_GPL(nfs_try_mount);
 
-/*
- * Validate the NFS2/NFS3 mount data
- * - fills in the mount root filehandle
- *
- * For option strings, user space handles the following behaviors:
- *
- * + DNS: mapping server host name to IP address ("addr=" option)
- *
- * + failure mode: how to behave if a mount request can't be handled
- *   immediately ("fg/bg" option)
- *
- * + retry: how often to retry a mount request ("retry=" option)
- *
- * + breaking back: trying proto=udp after proto=tcp, v2 after v3,
- *   mountproto=tcp after mountproto=udp, and so on
- */
-static int nfs23_validate_mount_data(void *options,
-                                    struct nfs_parsed_mount_data *args,
-                                    struct nfs_fh *mntfh,
-                                    const char *dev_name)
-{
-       struct nfs_mount_data *data = (struct nfs_mount_data *)options;
-       struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address;
-       int extra_flags = NFS_MOUNT_LEGACY_INTERFACE;
-
-       if (data == NULL)
-               goto out_no_data;
-
-       args->version = NFS_DEFAULT_VERSION;
-       switch (data->version) {
-       case 1:
-               data->namlen = 0;
-       case 2:
-               data->bsize = 0;
-       case 3:
-               if (data->flags & NFS_MOUNT_VER3)
-                       goto out_no_v3;
-               data->root.size = NFS2_FHSIZE;
-               memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE);
-               /* Turn off security negotiation */
-               extra_flags |= NFS_MOUNT_SECFLAVOUR;
-       case 4:
-               if (data->flags & NFS_MOUNT_SECFLAVOUR)
-                       goto out_no_sec;
-       case 5:
-               memset(data->context, 0, sizeof(data->context));
-       case 6:
-               if (data->flags & NFS_MOUNT_VER3) {
-                       if (data->root.size > NFS3_FHSIZE || data->root.size == 
0)
-                               goto out_invalid_fh;
-                       mntfh->size = data->root.size;
-                       args->version = 3;
-               } else {
-                       mntfh->size = NFS2_FHSIZE;
-                       args->version = 2;
-               }
-
-
-               memcpy(mntfh->data, data->root.data, mntfh->size);
-               if (mntfh->size < sizeof(mntfh->data))
-                       memset(mntfh->data + mntfh->size, 0,
-                              sizeof(mntfh->data) - mntfh->size);
-
-               /*
-                * Translate to nfs_parsed_mount_data, which nfs_fill_super
-                * can deal with.
-                */
-               args->flags             = data->flags & NFS_MOUNT_FLAGMASK;
-               args->flags             |= extra_flags;
-               args->rsize             = data->rsize;
-               args->wsize             = data->wsize;
-               args->timeo             = data->timeo;
-               args->retrans           = data->retrans;
-               args->acregmin          = data->acregmin;
-               args->acregmax          = data->acregmax;
-               args->acdirmin          = data->acdirmin;
-               args->acdirmax          = data->acdirmax;
-               args->need_mount        = false;
-
-               memcpy(sap, &data->addr, sizeof(data->addr));
-               args->nfs_server.addrlen = sizeof(data->addr);
-               args->nfs_server.port = ntohs(data->addr.sin_port);
-               if (!nfs_verify_server_address(sap))
-                       goto out_no_address;
-
-               if (!(data->flags & NFS_MOUNT_TCP))
-                       args->nfs_server.protocol = XPRT_TRANSPORT_UDP;
-               /* N.B. caller will free nfs_server.hostname in all cases */
-               args->nfs_server.hostname = kstrdup(data->hostname, GFP_KERNEL);
-               args->namlen            = data->namlen;
-               args->bsize             = data->bsize;
-
-               if (data->flags & NFS_MOUNT_SECFLAVOUR)
-                       args->selected_flavor = data->pseudoflavor;
-               else
-                       args->selected_flavor = RPC_AUTH_UNIX;
-               if (!args->nfs_server.hostname)
-                       goto out_nomem;
-
-               if (!(data->flags & NFS_MOUNT_NONLM))
-                       args->flags &= ~(NFS_MOUNT_LOCAL_FLOCK|
-                                        NFS_MOUNT_LOCAL_FCNTL);
-               else
-                       args->flags |= (NFS_MOUNT_LOCAL_FLOCK|
-                                       NFS_MOUNT_LOCAL_FCNTL);
-               /*
-                * The legacy version 6 binary mount data from userspace has a
-                * field used only to transport selinux information into the
-                * the kernel.  To continue to support that functionality we
-                * have a touch of selinux knowledge here in the NFS code. The
-                * userspace code converted context=blah to just blah so we are
-                * converting back to the full string selinux understands.
-                */
-               if (data->context[0]){
-#ifdef CONFIG_SECURITY_SELINUX
-                       int rc;
-                       char *opts_str = kmalloc(sizeof(data->context) + 8, 
GFP_KERNEL);
-                       if (!opts_str)
-                               return -ENOMEM;
-                       strcpy(opts_str, "context=");
-                       data->context[NFS_MAX_CONTEXT_LEN] = '\0';
-                       strcat(opts_str, &data->context[0]);
-                       rc = security_sb_parse_opts_str(opts_str, 
&args->lsm_opts);
-                       kfree(opts_str);
-                       if (rc)
-                               return rc;
-#else
-                       return -EINVAL;
-#endif
-               }
-
-               break;
-       default:
-               return NFS_TEXT_DATA;
-       }
-
-       return 0;
-
-out_no_data:
-       dfprintk(MOUNT, "NFS: mount program didn't pass any mount data\n");
-       return -EINVAL;
-
-out_no_v3:
-       dfprintk(MOUNT, "NFS: nfs_mount_data version %d does not support v3\n",
-                data->version);
-       return -EINVAL;
-
-out_no_sec:
-       dfprintk(MOUNT, "NFS: nfs_mount_data version supports only AUTH_SYS\n");
-       return -EINVAL;
-
-out_nomem:
-       dfprintk(MOUNT, "NFS: not enough memory to handle mount options\n");
-       return -ENOMEM;
-
-out_no_address:
-       dfprintk(MOUNT, "NFS: mount program didn't pass remote address\n");
-       return -EINVAL;
-
-out_invalid_fh:
-       dfprintk(MOUNT, "NFS: invalid root filehandle\n");
-       return -EINVAL;
-}
-
-#if IS_ENABLED(CONFIG_NFS_V4)
-static int nfs_validate_mount_data(struct file_system_type *fs_type,
-                                  void *options,
-                                  struct nfs_parsed_mount_data *args,
-                                  struct nfs_fh *mntfh,
-                                  const char *dev_name)
-{
-       if (fs_type == &nfs_fs_type)
-               return nfs23_validate_mount_data(options, args, mntfh, 
dev_name);
-       return nfs4_validate_mount_data(options, args, dev_name);
-}
-#else
-static int nfs_validate_mount_data(struct file_system_type *fs_type,
-                                  void *options,
-                                  struct nfs_parsed_mount_data *args,
-                                  struct nfs_fh *mntfh,
-                                  const char *dev_name)
-{
-       return nfs23_validate_mount_data(options, args, mntfh, dev_name);
-}
-#endif
-
-static int nfs_validate_text_mount_data(void *options,
-                                       struct nfs_parsed_mount_data *args,
-                                       const char *dev_name)
-{
-       int port = 0;
-       int max_namelen = PAGE_SIZE;
-       int max_pathlen = NFS_MAXPATHLEN;
-       struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address;
-
-       if (nfs_parse_mount_options((char *)options, args) == 0)
-               return -EINVAL;
-
-       if (!nfs_verify_server_address(sap))
-               goto out_no_address;
-
-       if (args->version == 4) {
-#if IS_ENABLED(CONFIG_NFS_V4)
-               port = NFS_PORT;
-               max_namelen = NFS4_MAXNAMLEN;
-               max_pathlen = NFS4_MAXPATHLEN;
-               nfs_validate_transport_protocol(args);
-               if (args->nfs_server.protocol == XPRT_TRANSPORT_UDP)
-                       goto out_invalid_transport_udp;
-               nfs4_validate_mount_flags(args);
-#else
-               goto out_v4_not_compiled;
-#endif /* CONFIG_NFS_V4 */
-       } else
-               nfs_set_mount_transport_protocol(args);
-
-       nfs_set_port(sap, &args->nfs_server.port, port);
-
-       return nfs_parse_devname(dev_name,
-                                  &args->nfs_server.hostname,
-                                  max_namelen,
-                                  &args->nfs_server.export_path,
-                                  max_pathlen);
-
-#if !IS_ENABLED(CONFIG_NFS_V4)
-out_v4_not_compiled:
-       dfprintk(MOUNT, "NFS: NFSv4 is not compiled into kernel\n");
-       return -EPROTONOSUPPORT;
-#else
-out_invalid_transport_udp:
-       dfprintk(MOUNT, "NFSv4: Unsupported transport protocol udp\n");
-       return -EINVAL;
-#endif /* !CONFIG_NFS_V4 */
-
-out_no_address:
-       dfprintk(MOUNT, "NFS: mount program didn't pass remote address\n");
-       return -EINVAL;
-}
 
 #define NFS_REMOUNT_CMP_FLAGMASK ~(NFS_MOUNT_INTR \
                | NFS_MOUNT_SECURE \
@@ -2207,7 +889,7 @@ static int nfs_validate_text_mount_data(void *options,
 
 static int
 nfs_compare_remount_data(struct nfs_server *nfss,
-                        struct nfs_parsed_mount_data *data)
+                        struct nfs_sb_config *data)
 {
        if ((data->flags ^ nfss->flags) & NFS_REMOUNT_CMP_FLAGMASK ||
            data->rsize != nfss->rsize ||
@@ -2230,15 +912,11 @@ nfs_compare_remount_data(struct nfs_server *nfss,
        return 0;
 }
 
-int
-nfs_remount(struct super_block *sb, int *flags, char *raw_data)
+int nfs_remount(struct super_block *sb, struct sb_config *sc)
 {
-       int error;
+       struct nfs_sb_config *cfg =
+               container_of(sc, struct nfs_sb_config, sc);
        struct nfs_server *nfss = sb->s_fs_info;
-       struct nfs_parsed_mount_data *data;
-       struct nfs_mount_data *options = (struct nfs_mount_data *)raw_data;
-       struct nfs4_mount_data *options4 = (struct nfs4_mount_data *)raw_data;
-       u32 nfsvers = nfss->nfs_client->rpc_ops->version;
 
        sync_filesystem(sb);
 
@@ -2248,60 +926,27 @@ nfs_remount(struct super_block *sb, int *flags, char 
*raw_data)
         * ones were explicitly specified. Fall back to legacy behavior and
         * just return success.
         */
-       if ((nfsvers == 4 && (!options4 || options4->version == 1)) ||
-           (nfsvers <= 3 && (!options || (options->version >= 1 &&
-                                          options->version <= 6))))
+       if (cfg->skip_remount_option_check)
                return 0;
 
-       data = kzalloc(sizeof(*data), GFP_KERNEL);
-       if (data == NULL)
-               return -ENOMEM;
-
-       /* fill out struct with values from existing mount */
-       data->flags = nfss->flags;
-       data->rsize = nfss->rsize;
-       data->wsize = nfss->wsize;
-       data->retrans = nfss->client->cl_timeout->to_retries;
-       data->selected_flavor = nfss->client->cl_auth->au_flavor;
-       data->acregmin = nfss->acregmin / HZ;
-       data->acregmax = nfss->acregmax / HZ;
-       data->acdirmin = nfss->acdirmin / HZ;
-       data->acdirmax = nfss->acdirmax / HZ;
-       data->timeo = 10U * nfss->client->cl_timeout->to_initval / HZ;
-       data->nfs_server.port = nfss->port;
-       data->nfs_server.addrlen = nfss->nfs_client->cl_addrlen;
-       data->version = nfsvers;
-       data->minorversion = nfss->nfs_client->cl_minorversion;
-       data->net = current->nsproxy->net_ns;
-       memcpy(&data->nfs_server.address, &nfss->nfs_client->cl_addr,
-               data->nfs_server.addrlen);
-
-       /* overwrite those values with any that were specified */
-       error = -EINVAL;
-       if (!nfs_parse_mount_options((char *)options, data))
-               goto out;
-
        /*
         * noac is a special case. It implies -o sync, but that's not
         * necessarily reflected in the mtab options. do_remount_sb
         * will clear MS_SYNCHRONOUS if -o sync wasn't specified in the
         * remount options, so we have to explicitly reset it.
         */
-       if (data->flags & NFS_MOUNT_NOAC)
-               *flags |= MS_SYNCHRONOUS;
+       if (cfg->flags & NFS_MOUNT_NOAC)
+               cfg->sc.ms_flags |= MS_SYNCHRONOUS;
 
        /* compare new mount options with old ones */
-       error = nfs_compare_remount_data(nfss, data);
-out:
-       kfree(data);
-       return error;
+       return nfs_compare_remount_data(nfss, cfg);
 }
 EXPORT_SYMBOL_GPL(nfs_remount);
 
 /*
  * Initialise the common bits of the superblock
  */
-inline void nfs_initialise_sb(struct super_block *sb)
+static inline void nfs_initialise_sb(struct super_block *sb)
 {
        struct nfs_server *server = NFS_SB(sb);
 
@@ -2319,69 +964,75 @@ inline void nfs_initialise_sb(struct super_block *sb)
 }
 
 /*
- * Finish setting up an NFS2/3 superblock
+ * Finish setting up a cloned NFS2/3/4 superblock
  */
-int nfs_fill_super(struct super_block *sb, struct nfs_mount_info *mount_info)
+static int nfs_clone_super(struct super_block *sb, struct sb_config *sc)
 {
-       struct nfs_parsed_mount_data *data = mount_info->parsed;
+       struct nfs_sb_config *cfg =
+               container_of(sc, struct nfs_sb_config, sc);
+       const struct super_block *old_sb = cfg->clone_data.sb;
        struct nfs_server *server = NFS_SB(sb);
-       int ret;
 
-       sb->s_blocksize_bits = 0;
-       sb->s_blocksize = 0;
-       sb->s_xattr = server->nfs_client->cl_nfs_mod->xattr;
-       sb->s_op = server->nfs_client->cl_nfs_mod->sops;
-       if (data && data->bsize)
-               sb->s_blocksize = nfs_block_size(data->bsize, 
&sb->s_blocksize_bits);
+       sb->s_blocksize_bits = old_sb->s_blocksize_bits;
+       sb->s_blocksize = old_sb->s_blocksize;
+       sb->s_maxbytes = old_sb->s_maxbytes;
+       sb->s_xattr = old_sb->s_xattr;
+       sb->s_op = old_sb->s_op;
+       sb->s_time_gran = 1;
 
        if (server->nfs_client->rpc_ops->version != 2) {
                /* The VFS shouldn't apply the umask to mode bits. We will do
                 * so ourselves when necessary.
                 */
                sb->s_flags |= MS_POSIXACL;
-               sb->s_time_gran = 1;
        }
 
        nfs_initialise_sb(sb);
 
-       ret = super_setup_bdi_name(sb, "%u:%u", MAJOR(server->s_dev),
-                                  MINOR(server->s_dev));
-       if (ret)
-               return ret;
-       sb->s_bdi->ra_pages = server->rpages * NFS_MAX_READAHEAD;
-       return 0;
+       sb->s_bdi = bdi_get(old_sb->s_bdi);
 
+       return 0;
 }
-EXPORT_SYMBOL_GPL(nfs_fill_super);
 
 /*
- * Finish setting up a cloned NFS2/3/4 superblock
+ * Finish setting up an NFS2/3 superblock
  */
-int nfs_clone_super(struct super_block *sb, struct nfs_mount_info *mount_info)
+int nfs_fill_super(struct super_block *sb, struct sb_config *sc)
 {
-       const struct super_block *old_sb = mount_info->cloned->sb;
+       struct nfs_sb_config *cfg =
+               container_of(sc, struct nfs_sb_config, sc);
        struct nfs_server *server = NFS_SB(sb);
+       int ret;
 
-       sb->s_blocksize_bits = old_sb->s_blocksize_bits;
-       sb->s_blocksize = old_sb->s_blocksize;
-       sb->s_maxbytes = old_sb->s_maxbytes;
-       sb->s_xattr = old_sb->s_xattr;
-       sb->s_op = old_sb->s_op;
-       sb->s_time_gran = 1;
+       if (cfg->clone_data.sb)
+               return nfs_clone_super(sb, sc);
+       
+       sb->s_blocksize_bits = 0;
+       sb->s_blocksize = 0;
+       sb->s_xattr = server->nfs_client->cl_nfs_mod->xattr;
+       sb->s_op = server->nfs_client->cl_nfs_mod->sops;
+       if (cfg->bsize)
+               sb->s_blocksize = nfs_block_size(cfg->bsize, 
&sb->s_blocksize_bits);
 
        if (server->nfs_client->rpc_ops->version != 2) {
                /* The VFS shouldn't apply the umask to mode bits. We will do
                 * so ourselves when necessary.
                 */
                sb->s_flags |= MS_POSIXACL;
+               sb->s_time_gran = 1;
        }
 
        nfs_initialise_sb(sb);
 
-       sb->s_bdi = bdi_get(old_sb->s_bdi);
-
+       ret = super_setup_bdi_name(sb, "%u:%u", MAJOR(server->s_dev),
+                                  MINOR(server->s_dev));
+       if (ret)
+               return ret;
+       sb->s_bdi->ra_pages = server->rpages * NFS_MAX_READAHEAD;
        return 0;
+
 }
+EXPORT_SYMBOL_GPL(nfs_fill_super);
 
 static int nfs_compare_mount_options(const struct super_block *s, const struct 
nfs_server *b, int flags)
 {
@@ -2495,8 +1146,7 @@ static int nfs_compare_super(struct super_block *sb, void 
*data)
 
 #ifdef CONFIG_NFS_FSCACHE
 static void nfs_get_cache_cookie(struct super_block *sb,
-                                struct nfs_parsed_mount_data *parsed,
-                                struct nfs_clone_mount *cloned)
+                                struct nfs_sb_config *cfg)
 {
        struct nfs_server *nfss = NFS_SB(sb);
        char *uniq = NULL;
@@ -2505,21 +1155,21 @@ static void nfs_get_cache_cookie(struct super_block *sb,
        nfss->fscache_key = NULL;
        nfss->fscache = NULL;
 
-       if (parsed) {
-               if (!(parsed->options & NFS_OPTION_FSCACHE))
+       if (cfg) {
+               if (!(cfg->options & NFS_OPTION_FSCACHE))
                        return;
-               if (parsed->fscache_uniq) {
-                       uniq = parsed->fscache_uniq;
-                       ulen = strlen(parsed->fscache_uniq);
+               if (cfg->fscache_uniq) {
+                       uniq = cfg->fscache_uniq;
+                       ulen = strlen(cfg->fscache_uniq);
                }
-       } else if (cloned) {
-               struct nfs_server *mnt_s = NFS_SB(cloned->sb);
+       } else if (cfg->clone_data.cloned) {
+               struct nfs_server *mnt_s = NFS_SB(cfg->clone_data.sb);
                if (!(mnt_s->options & NFS_OPTION_FSCACHE))
                        return;
                if (mnt_s->fscache_key) {
                        uniq = mnt_s->fscache_key->key.uniquifier;
                        ulen = mnt_s->fscache_key->key.uniq_len;
-               };
+               }
        } else
                return;
 
@@ -2527,22 +1177,22 @@ static void nfs_get_cache_cookie(struct super_block *sb,
 }
 #else
 static void nfs_get_cache_cookie(struct super_block *sb,
-                                struct nfs_parsed_mount_data *parsed,
-                                struct nfs_clone_mount *cloned)
+                                struct nfs_sb_config *cfg)
 {
 }
 #endif
 
 int nfs_set_sb_security(struct super_block *s, struct dentry *mntroot,
-                       struct nfs_mount_info *mount_info)
+                       struct nfs_sb_config *cfg)
 {
        int error;
        unsigned long kflags = 0, kflags_out = 0;
+
        if (NFS_SB(s)->caps & NFS_CAP_SECURITY_LABEL)
                kflags |= SECURITY_LSM_NATIVE_LABELS;
 
-       error = security_sb_set_mnt_opts(s, &mount_info->parsed->lsm_opts,
-                                               kflags, &kflags_out);
+       error = security_sb_set_mnt_opts(s, cfg->sc.security,
+                                        kflags, &kflags_out);
        if (error)
                goto err;
 
@@ -2555,25 +1205,23 @@ int nfs_set_sb_security(struct super_block *s, struct 
dentry *mntroot,
 EXPORT_SYMBOL_GPL(nfs_set_sb_security);
 
 int nfs_clone_sb_security(struct super_block *s, struct dentry *mntroot,
-                         struct nfs_mount_info *mount_info)
+                         struct nfs_sb_config *cfg)
 {
        /* clone any lsm security options from the parent to the new sb */
        if (d_inode(mntroot)->i_op != 
NFS_SB(s)->nfs_client->rpc_ops->dir_inode_ops)
                return -ESTALE;
-       return security_sb_clone_mnt_opts(mount_info->cloned->sb, s);
+       return security_sb_clone_mnt_opts(cfg->clone_data.sb, s);
 }
 EXPORT_SYMBOL_GPL(nfs_clone_sb_security);
 
 struct dentry *nfs_fs_mount_common(struct nfs_server *server,
-                                  int flags, const char *dev_name,
-                                  struct nfs_mount_info *mount_info,
-                                  struct nfs_subversion *nfs_mod)
+                                  struct nfs_sb_config *cfg)
 {
        struct super_block *s;
        struct dentry *mntroot = ERR_PTR(-ENOMEM);
        int (*compare_super)(struct super_block *, void *) = nfs_compare_super;
        struct nfs_sb_mountdata sb_mntdata = {
-               .mntflags = flags,
+               .mntflags = cfg->sc.ms_flags,
                .server = server,
        };
        int error;
@@ -2585,14 +1233,16 @@ struct dentry *nfs_fs_mount_common(struct nfs_server 
*server,
        if (server->flags & NFS_MOUNT_NOAC)
                sb_mntdata.mntflags |= MS_SYNCHRONOUS;
 
-       if (mount_info->cloned != NULL && mount_info->cloned->sb != NULL)
-               if (mount_info->cloned->sb->s_flags & MS_SYNCHRONOUS)
+       if (cfg->clone_data.cloned && cfg->clone_data.sb != NULL)
+               if (cfg->clone_data.sb->s_flags & MS_SYNCHRONOUS)
                        sb_mntdata.mntflags |= MS_SYNCHRONOUS;
 
        /* Get a superblock - note that we may end up sharing one that already 
exists */
-       s = sget(nfs_mod->nfs_fs, compare_super, nfs_set_super, flags, 
&sb_mntdata);
+       s = sget(cfg->nfs_mod->nfs_fs, compare_super, nfs_set_super, 
cfg->sc.ms_flags,
+                &sb_mntdata);
        if (IS_ERR(s)) {
                mntroot = ERR_CAST(s);
+               nfs_cfg_error(cfg, "NFS: Couldn't get superblock");
                goto out_err_nosb;
        }
 
@@ -2605,17 +1255,17 @@ struct dentry *nfs_fs_mount_common(struct nfs_server 
*server,
 
        if (!s->s_root) {
                /* initial superblock/root creation */
-               error = mount_info->fill_super(s, mount_info);
-               if (error)
-                       goto error_splat_super;
-               nfs_get_cache_cookie(s, mount_info->parsed, mount_info->cloned);
+               cfg->sc.ops->fill_super(s, &cfg->sc);
+               nfs_get_cache_cookie(s, cfg);
        }
 
-       mntroot = nfs_get_root(s, mount_info->mntfh, dev_name);
-       if (IS_ERR(mntroot))
+       mntroot = nfs_get_root(s, cfg->mntfh, cfg->sc.device);
+       if (IS_ERR(mntroot)) {
+               nfs_cfg_error(cfg, "NFS: Couldn't get root dentry");
                goto error_splat_super;
+       }
 
-       error = mount_info->set_security(s, mntroot, mount_info);
+       error = cfg->set_security(s, mntroot, cfg);
        if (error)
                goto error_splat_root;
 
@@ -2637,47 +1287,6 @@ struct dentry *nfs_fs_mount_common(struct nfs_server 
*server,
 }
 EXPORT_SYMBOL_GPL(nfs_fs_mount_common);
 
-struct dentry *nfs_fs_mount(struct file_system_type *fs_type,
-       int flags, const char *dev_name, void *raw_data)
-{
-       struct nfs_mount_info mount_info = {
-               .fill_super = nfs_fill_super,
-               .set_security = nfs_set_sb_security,
-       };
-       struct dentry *mntroot = ERR_PTR(-ENOMEM);
-       struct nfs_subversion *nfs_mod;
-       int error;
-
-       mount_info.parsed = nfs_alloc_parsed_mount_data();
-       mount_info.mntfh = nfs_alloc_fhandle();
-       if (mount_info.parsed == NULL || mount_info.mntfh == NULL)
-               goto out;
-
-       /* Validate the mount data */
-       error = nfs_validate_mount_data(fs_type, raw_data, mount_info.parsed, 
mount_info.mntfh, dev_name);
-       if (error == NFS_TEXT_DATA)
-               error = nfs_validate_text_mount_data(raw_data, 
mount_info.parsed, dev_name);
-       if (error < 0) {
-               mntroot = ERR_PTR(error);
-               goto out;
-       }
-
-       nfs_mod = get_nfs_version(mount_info.parsed->version);
-       if (IS_ERR(nfs_mod)) {
-               mntroot = ERR_CAST(nfs_mod);
-               goto out;
-       }
-
-       mntroot = nfs_mod->rpc_ops->try_mount(flags, dev_name, &mount_info, 
nfs_mod);
-
-       put_nfs_version(nfs_mod);
-out:
-       nfs_free_parsed_mount_data(mount_info.parsed);
-       nfs_free_fhandle(mount_info.mntfh);
-       return mntroot;
-}
-EXPORT_SYMBOL_GPL(nfs_fs_mount);
-
 /*
  * Destroy an NFS2/3 superblock
  */
@@ -2695,150 +1304,8 @@ void nfs_kill_super(struct super_block *s)
 }
 EXPORT_SYMBOL_GPL(nfs_kill_super);
 
-/*
- * Clone an NFS2/3/4 server record on xdev traversal (FSID-change)
- */
-static struct dentry *
-nfs_xdev_mount(struct file_system_type *fs_type, int flags,
-               const char *dev_name, void *raw_data)
-{
-       struct nfs_clone_mount *data = raw_data;
-       struct nfs_mount_info mount_info = {
-               .fill_super = nfs_clone_super,
-               .set_security = nfs_clone_sb_security,
-               .cloned = data,
-       };
-       struct nfs_server *server;
-       struct dentry *mntroot = ERR_PTR(-ENOMEM);
-       struct nfs_subversion *nfs_mod = 
NFS_SB(data->sb)->nfs_client->cl_nfs_mod;
-
-       dprintk("--> nfs_xdev_mount()\n");
-
-       mount_info.mntfh = mount_info.cloned->fh;
-
-       /* create a new volume representation */
-       server = nfs_mod->rpc_ops->clone_server(NFS_SB(data->sb), data->fh, 
data->fattr, data->authflavor);
-
-       if (IS_ERR(server))
-               mntroot = ERR_CAST(server);
-       else
-               mntroot = nfs_fs_mount_common(server, flags,
-                               dev_name, &mount_info, nfs_mod);
-
-       dprintk("<-- nfs_xdev_mount() = %ld\n",
-                       IS_ERR(mntroot) ? PTR_ERR(mntroot) : 0L);
-       return mntroot;
-}
-
 #if IS_ENABLED(CONFIG_NFS_V4)
 
-static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *args)
-{
-       args->flags &= ~(NFS_MOUNT_NONLM|NFS_MOUNT_NOACL|NFS_MOUNT_VER3|
-                        NFS_MOUNT_LOCAL_FLOCK|NFS_MOUNT_LOCAL_FCNTL);
-}
-
-/*
- * Validate NFSv4 mount options
- */
-static int nfs4_validate_mount_data(void *options,
-                                   struct nfs_parsed_mount_data *args,
-                                   const char *dev_name)
-{
-       struct sockaddr *sap = (struct sockaddr *)&args->nfs_server.address;
-       struct nfs4_mount_data *data = (struct nfs4_mount_data *)options;
-       char *c;
-
-       if (data == NULL)
-               goto out_no_data;
-
-       args->version = 4;
-
-       switch (data->version) {
-       case 1:
-               if (data->host_addrlen > sizeof(args->nfs_server.address))
-                       goto out_no_address;
-               if (data->host_addrlen == 0)
-                       goto out_no_address;
-               args->nfs_server.addrlen = data->host_addrlen;
-               if (copy_from_user(sap, data->host_addr, data->host_addrlen))
-                       return -EFAULT;
-               if (!nfs_verify_server_address(sap))
-                       goto out_no_address;
-               args->nfs_server.port = ntohs(((struct sockaddr_in 
*)sap)->sin_port);
-
-               if (data->auth_flavourlen) {
-                       rpc_authflavor_t pseudoflavor;
-                       if (data->auth_flavourlen > 1)
-                               goto out_inval_auth;
-                       if (copy_from_user(&pseudoflavor,
-                                          data->auth_flavours,
-                                          sizeof(pseudoflavor)))
-                               return -EFAULT;
-                       args->selected_flavor = pseudoflavor;
-               } else
-                       args->selected_flavor = RPC_AUTH_UNIX;
-
-               c = strndup_user(data->hostname.data, NFS4_MAXNAMLEN);
-               if (IS_ERR(c))
-                       return PTR_ERR(c);
-               args->nfs_server.hostname = c;
-
-               c = strndup_user(data->mnt_path.data, NFS4_MAXPATHLEN);
-               if (IS_ERR(c))
-                       return PTR_ERR(c);
-               args->nfs_server.export_path = c;
-               dfprintk(MOUNT, "NFS: MNTPATH: '%s'\n", c);
-
-               c = strndup_user(data->client_addr.data, 16);
-               if (IS_ERR(c))
-                       return PTR_ERR(c);
-               args->client_address = c;
-
-               /*
-                * Translate to nfs_parsed_mount_data, which nfs4_fill_super
-                * can deal with.
-                */
-
-               args->flags     = data->flags & NFS4_MOUNT_FLAGMASK;
-               args->rsize     = data->rsize;
-               args->wsize     = data->wsize;
-               args->timeo     = data->timeo;
-               args->retrans   = data->retrans;
-               args->acregmin  = data->acregmin;
-               args->acregmax  = data->acregmax;
-               args->acdirmin  = data->acdirmin;
-               args->acdirmax  = data->acdirmax;
-               args->nfs_server.protocol = data->proto;
-               nfs_validate_transport_protocol(args);
-               if (args->nfs_server.protocol == XPRT_TRANSPORT_UDP)
-                       goto out_invalid_transport_udp;
-
-               break;
-       default:
-               return NFS_TEXT_DATA;
-       }
-
-       return 0;
-
-out_no_data:
-       dfprintk(MOUNT, "NFS4: mount program didn't pass any mount data\n");
-       return -EINVAL;
-
-out_inval_auth:
-       dfprintk(MOUNT, "NFS4: Invalid number of RPC auth flavours %d\n",
-                data->auth_flavourlen);
-       return -EINVAL;
-
-out_no_address:
-       dfprintk(MOUNT, "NFS4: mount program didn't pass remote address\n");
-       return -EINVAL;
-
-out_invalid_transport_udp:
-       dfprintk(MOUNT, "NFSv4: Unsupported transport protocol udp\n");
-       return -EINVAL;
-}
-
 /*
  * NFS v4 module parameters need to stay in the
  * NFS client for backwards compatibility
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 348f7c158084..9377afd5ecc8 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -1540,6 +1540,7 @@ struct nfs_subversion;
 struct nfs_mount_info;
 struct nfs_client_initdata;
 struct nfs_pageio_descriptor;
+struct nfs_sb_config;
 
 /*
  * RPC procedure vector for NFSv2/NFSv3 demuxing
@@ -1553,10 +1554,10 @@ struct nfs_rpc_ops {
 
        int     (*getroot) (struct nfs_server *, struct nfs_fh *,
                            struct nfs_fsinfo *);
+       struct dentry *(*mount)(struct nfs_sb_config *);
        struct vfsmount *(*submount) (struct nfs_server *, struct dentry *,
                                      struct nfs_fh *, struct nfs_fattr *);
-       struct dentry *(*try_mount) (int, const char *, struct nfs_mount_info *,
-                                    struct nfs_subversion *);
+       struct dentry *(*try_mount) (struct nfs_sb_config *);
        int     (*getattr) (struct nfs_server *, struct nfs_fh *,
                            struct nfs_fattr *, struct nfs4_label *);
        int     (*setattr) (struct dentry *, struct nfs_fattr *,
@@ -1617,7 +1618,7 @@ struct nfs_rpc_ops {
        struct nfs_client *(*init_client) (struct nfs_client *,
                                const struct nfs_client_initdata *);
        void    (*free_client) (struct nfs_client *);
-       struct nfs_server *(*create_server)(struct nfs_mount_info *, struct 
nfs_subversion *);
+       struct nfs_server *(*create_server)(struct nfs_sb_config *);
        struct nfs_server *(*clone_server)(struct nfs_server *, struct nfs_fh *,
                                           struct nfs_fattr *, 
rpc_authflavor_t);
 };

Reply via email to