]> err.no Git - linux-2.6/blobdiff - fs/nfs/inode.c
[PATCH] tightening hugetlb strict accounting
[linux-2.6] / fs / nfs / inode.c
index 5746dc841a6cf11e250d3307bf8dd2344028102a..937fbfc381bbd4746ddf55367df2d920d44dc0fe 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/unistd.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/stats.h>
+#include <linux/sunrpc/metrics.h>
 #include <linux/nfs_fs.h>
 #include <linux/nfs_mount.h>
 #include <linux/nfs4_mount.h>
@@ -42,6 +43,7 @@
 #include "nfs4_fs.h"
 #include "callback.h"
 #include "delegation.h"
+#include "iostat.h"
 
 #define NFSDBG_FACILITY                NFSDBG_VFS
 #define NFS_PARANOIA 1
@@ -63,8 +65,9 @@ static int nfs_write_inode(struct inode *,int);
 static void nfs_delete_inode(struct inode *);
 static void nfs_clear_inode(struct inode *);
 static void nfs_umount_begin(struct super_block *);
-static int  nfs_statfs(struct super_block *, struct kstatfs *);
+static int  nfs_statfs(struct dentry *, struct kstatfs *);
 static int  nfs_show_options(struct seq_file *, struct vfsmount *);
+static int  nfs_show_stats(struct seq_file *, struct vfsmount *);
 static void nfs_zap_acl_cache(struct inode *);
 
 static struct rpc_program      nfs_program;
@@ -78,6 +81,7 @@ static struct super_operations nfs_sops = {
        .clear_inode    = nfs_clear_inode,
        .umount_begin   = nfs_umount_begin,
        .show_options   = nfs_show_options,
+       .show_stats     = nfs_show_stats,
 };
 
 /*
@@ -103,7 +107,7 @@ static struct rpc_version * nfs_version[] = {
 static struct rpc_program      nfs_program = {
        .name                   = "nfs",
        .number                 = NFS_PROGRAM,
-       .nrvers                 = sizeof(nfs_version) / sizeof(nfs_version[0]),
+       .nrvers                 = ARRAY_SIZE(nfs_version),
        .version                = nfs_version,
        .stats                  = &nfs_rpcstat,
        .pipe_dir_name          = "/nfs",
@@ -118,7 +122,7 @@ static struct rpc_version * nfsacl_version[] = {
 struct rpc_program             nfsacl_program = {
        .name =                 "nfsacl",
        .number =               NFS_ACL_PROGRAM,
-       .nrvers =               sizeof(nfsacl_version) / sizeof(nfsacl_version[0]),
+       .nrvers =               ARRAY_SIZE(nfsacl_version),
        .version =              nfsacl_version,
        .stats =                &nfsacl_rpcstat,
 };
@@ -133,7 +137,7 @@ nfs_fattr_to_ino_t(struct nfs_fattr *fattr)
 static int
 nfs_write_inode(struct inode *inode, int sync)
 {
-       int flags = sync ? FLUSH_WAIT : 0;
+       int flags = sync ? FLUSH_SYNC : 0;
        int ret;
 
        ret = nfs_commit_inode(inode, flags);
@@ -237,7 +241,6 @@ static struct inode *
 nfs_get_root(struct super_block *sb, struct nfs_fh *rootfh, struct nfs_fsinfo *fsinfo)
 {
        struct nfs_server       *server = NFS_SB(sb);
-       struct inode *rooti;
        int                     error;
 
        error = server->rpc_ops->getroot(server, rootfh, fsinfo);
@@ -246,10 +249,7 @@ nfs_get_root(struct super_block *sb, struct nfs_fh *rootfh, struct nfs_fsinfo *f
                return ERR_PTR(error);
        }
 
-       rooti = nfs_fhget(sb, rootfh, fsinfo->fattr);
-       if (!rooti)
-               return ERR_PTR(-ENOMEM);
-       return rooti;
+       return nfs_fhget(sb, rootfh, fsinfo->fattr);
 }
 
 /*
@@ -277,6 +277,10 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor)
 
        sb->s_magic      = NFS_SUPER_MAGIC;
 
+       server->io_stats = nfs_alloc_iostats();
+       if (server->io_stats == NULL)
+               return -ENOMEM;
+
        root_inode = nfs_get_root(sb, &server->fh, &fsinfo);
        /* Did getting the root inode fail? */
        if (IS_ERR(root_inode)) {
@@ -290,6 +294,9 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor)
        }
        sb->s_root->d_op = server->rpc_ops->dentry_ops;
 
+       /* mount time stamp, in seconds */
+       server->mount_time = jiffies;
+
        /* Get some general file system info */
        if (server->namelen == 0 &&
            server->rpc_ops->pathconf(server, &server->fh, &pathinfo) >= 0)
@@ -396,6 +403,9 @@ nfs_create_client(struct nfs_server *server, const struct nfs_mount_data *data)
 
        nfs_init_timeout_values(&timeparms, proto, data->timeo, data->retrans);
 
+       server->retrans_timeo = timeparms.to_initval;
+       server->retrans_count = timeparms.to_retries;
+
        /* create transport and client */
        xprt = xprt_create_proto(proto, &server->addr, &timeparms);
        if (IS_ERR(xprt)) {
@@ -524,8 +534,9 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
 }
 
 static int
-nfs_statfs(struct super_block *sb, struct kstatfs *buf)
+nfs_statfs(struct dentry *dentry, struct kstatfs *buf)
 {
+       struct super_block *sb = dentry->d_sb;
        struct nfs_server *server = NFS_SB(sb);
        unsigned char blockbits;
        unsigned long blockres;
@@ -579,7 +590,7 @@ nfs_statfs(struct super_block *sb, struct kstatfs *buf)
 
 }
 
-static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
+static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss, int showdefaults)
 {
        static struct proc_nfs_info {
                int flag;
@@ -588,28 +599,26 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
        } nfs_info[] = {
                { NFS_MOUNT_SOFT, ",soft", ",hard" },
                { NFS_MOUNT_INTR, ",intr", "" },
-               { NFS_MOUNT_POSIX, ",posix", "" },
                { NFS_MOUNT_NOCTO, ",nocto", "" },
                { NFS_MOUNT_NOAC, ",noac", "" },
-               { NFS_MOUNT_NONLM, ",nolock", ",lock" },
+               { NFS_MOUNT_NONLM, ",nolock", "" },
                { NFS_MOUNT_NOACL, ",noacl", "" },
                { 0, NULL, NULL }
        };
        struct proc_nfs_info *nfs_infop;
-       struct nfs_server *nfss = NFS_SB(mnt->mnt_sb);
        char buf[12];
        char *proto;
 
-       seq_printf(m, ",v%d", nfss->rpc_ops->version);
+       seq_printf(m, ",vers=%d", nfss->rpc_ops->version);
        seq_printf(m, ",rsize=%d", nfss->rsize);
        seq_printf(m, ",wsize=%d", nfss->wsize);
-       if (nfss->acregmin != 3*HZ)
+       if (nfss->acregmin != 3*HZ || showdefaults)
                seq_printf(m, ",acregmin=%d", nfss->acregmin/HZ);
-       if (nfss->acregmax != 60*HZ)
+       if (nfss->acregmax != 60*HZ || showdefaults)
                seq_printf(m, ",acregmax=%d", nfss->acregmax/HZ);
-       if (nfss->acdirmin != 30*HZ)
+       if (nfss->acdirmin != 30*HZ || showdefaults)
                seq_printf(m, ",acdirmin=%d", nfss->acdirmin/HZ);
-       if (nfss->acdirmax != 60*HZ)
+       if (nfss->acdirmax != 60*HZ || showdefaults)
                seq_printf(m, ",acdirmax=%d", nfss->acdirmax/HZ);
        for (nfs_infop = nfs_info; nfs_infop->flag; nfs_infop++) {
                if (nfss->flags & nfs_infop->flag)
@@ -629,8 +638,93 @@ static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
                        proto = buf;
        }
        seq_printf(m, ",proto=%s", proto);
+       seq_printf(m, ",timeo=%lu", 10U * nfss->retrans_timeo / HZ);
+       seq_printf(m, ",retrans=%u", nfss->retrans_count);
+}
+
+static int nfs_show_options(struct seq_file *m, struct vfsmount *mnt)
+{
+       struct nfs_server *nfss = NFS_SB(mnt->mnt_sb);
+
+       nfs_show_mount_options(m, nfss, 0);
+
        seq_puts(m, ",addr=");
        seq_escape(m, nfss->hostname, " \t\n\\");
+
+       return 0;
+}
+
+static int nfs_show_stats(struct seq_file *m, struct vfsmount *mnt)
+{
+       int i, cpu;
+       struct nfs_server *nfss = NFS_SB(mnt->mnt_sb);
+       struct rpc_auth *auth = nfss->client->cl_auth;
+       struct nfs_iostats totals = { };
+
+       seq_printf(m, "statvers=%s", NFS_IOSTAT_VERS);
+
+       /*
+        * Display all mount option settings
+        */
+       seq_printf(m, "\n\topts:\t");
+       seq_puts(m, mnt->mnt_sb->s_flags & MS_RDONLY ? "ro" : "rw");
+       seq_puts(m, mnt->mnt_sb->s_flags & MS_SYNCHRONOUS ? ",sync" : "");
+       seq_puts(m, mnt->mnt_sb->s_flags & MS_NOATIME ? ",noatime" : "");
+       seq_puts(m, mnt->mnt_sb->s_flags & MS_NODIRATIME ? ",nodiratime" : "");
+       nfs_show_mount_options(m, nfss, 1);
+
+       seq_printf(m, "\n\tage:\t%lu", (jiffies - nfss->mount_time) / HZ);
+
+       seq_printf(m, "\n\tcaps:\t");
+       seq_printf(m, "caps=0x%x", nfss->caps);
+       seq_printf(m, ",wtmult=%d", nfss->wtmult);
+       seq_printf(m, ",dtsize=%d", nfss->dtsize);
+       seq_printf(m, ",bsize=%d", nfss->bsize);
+       seq_printf(m, ",namelen=%d", nfss->namelen);
+
+#ifdef CONFIG_NFS_V4
+       if (nfss->rpc_ops->version == 4) {
+               seq_printf(m, "\n\tnfsv4:\t");
+               seq_printf(m, "bm0=0x%x", nfss->attr_bitmask[0]);
+               seq_printf(m, ",bm1=0x%x", nfss->attr_bitmask[1]);
+               seq_printf(m, ",acl=0x%x", nfss->acl_bitmask);
+       }
+#endif
+
+       /*
+        * Display security flavor in effect for this mount
+        */
+       seq_printf(m, "\n\tsec:\tflavor=%d", auth->au_ops->au_flavor);
+       if (auth->au_flavor)
+               seq_printf(m, ",pseudoflavor=%d", auth->au_flavor);
+
+       /*
+        * Display superblock I/O counters
+        */
+       for_each_possible_cpu(cpu) {
+               struct nfs_iostats *stats;
+
+               preempt_disable();
+               stats = per_cpu_ptr(nfss->io_stats, cpu);
+
+               for (i = 0; i < __NFSIOS_COUNTSMAX; i++)
+                       totals.events[i] += stats->events[i];
+               for (i = 0; i < __NFSIOS_BYTESMAX; i++)
+                       totals.bytes[i] += stats->bytes[i];
+
+               preempt_enable();
+       }
+
+       seq_printf(m, "\n\tevents:\t");
+       for (i = 0; i < __NFSIOS_COUNTSMAX; i++)
+               seq_printf(m, "%lu ", totals.events[i]);
+       seq_printf(m, "\n\tbytes:\t");
+       for (i = 0; i < __NFSIOS_BYTESMAX; i++)
+               seq_printf(m, "%Lu ", totals.bytes[i]);
+       seq_printf(m, "\n");
+
+       rpc_print_iostats(m, nfss->client);
+
        return 0;
 }
 
@@ -660,6 +754,8 @@ static void nfs_zap_caches_locked(struct inode *inode)
        struct nfs_inode *nfsi = NFS_I(inode);
        int mode = inode->i_mode;
 
+       nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
+
        NFS_ATTRTIMEO(inode) = NFS_MINATTRTIMEO(inode);
        NFS_ATTRTIMEO_UPDATE(inode) = jiffies;
 
@@ -751,7 +847,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                .fh     = fh,
                .fattr  = fattr
        };
-       struct inode *inode = NULL;
+       struct inode *inode = ERR_PTR(-ENOENT);
        unsigned long hash;
 
        if ((fattr->valid & NFS_ATTR_FATTR) == 0)
@@ -764,8 +860,11 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
 
        hash = nfs_fattr_to_ino_t(fattr);
 
-       if (!(inode = iget5_locked(sb, hash, nfs_find_actor, nfs_init_locked, &desc)))
+       inode = iget5_locked(sb, hash, nfs_find_actor, nfs_init_locked, &desc);
+       if (inode == NULL) {
+               inode = ERR_PTR(-ENOMEM);
                goto out_no_inode;
+       }
 
        if (inode->i_state & I_NEW) {
                struct nfs_inode *nfsi = NFS_I(inode);
@@ -834,7 +933,7 @@ out:
        return inode;
 
 out_no_inode:
-       printk("nfs_fhget: iget failed\n");
+       dprintk("nfs_fhget: iget failed with error %ld\n", PTR_ERR(inode));
        goto out;
 }
 
@@ -847,6 +946,8 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
        struct nfs_fattr fattr;
        int error;
 
+       nfs_inc_stats(inode, NFSIOS_VFSSETATTR);
+
        if (attr->ia_valid & ATTR_SIZE) {
                if (!S_ISREG(inode->i_mode) || attr->ia_size == i_size_read(inode))
                        attr->ia_valid &= ~ATTR_SIZE;
@@ -900,6 +1001,7 @@ void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr)
                spin_unlock(&inode->i_lock);
        }
        if ((attr->ia_valid & ATTR_SIZE) != 0) {
+               nfs_inc_stats(inode, NFSIOS_SETATTRTRUNC);
                inode->i_size = attr->ia_size;
                vmtruncate(inode, attr->ia_size);
        }
@@ -947,7 +1049,7 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
        int err;
 
        /* Flush out writes to the server in order to update c/mtime */
-       nfs_sync_inode(inode, 0, 0, FLUSH_WAIT|FLUSH_NOCOMMIT);
+       nfs_sync_inode_wait(inode, 0, 0, FLUSH_NOCOMMIT);
 
        /*
         * We may force a getattr if the user cares about atime.
@@ -1185,6 +1287,7 @@ int nfs_attribute_timeout(struct inode *inode)
  */
 int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
 {
+       nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE);
        if (!(NFS_I(inode)->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))
                        && !nfs_attribute_timeout(inode))
                return NFS_STALE(inode) ? -ESTALE : 0;
@@ -1201,6 +1304,7 @@ void nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
        struct nfs_inode *nfsi = NFS_I(inode);
 
        if (nfsi->cache_validity & NFS_INO_INVALID_DATA) {
+               nfs_inc_stats(inode, NFSIOS_DATAINVALIDATE);
                if (S_ISREG(inode->i_mode))
                        nfs_sync_mapping(mapping);
                invalidate_inode_pages2(mapping);
@@ -1299,39 +1403,37 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat
        if ((fattr->valid & NFS_ATTR_FATTR) == 0)
                return 0;
 
+       /* Has the inode gone and changed behind our back? */
+       if (nfsi->fileid != fattr->fileid
+                       || (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) {
+               return -EIO;
+       }
+
        /* Are we in the process of updating data on the server? */
        data_unstable = nfs_caches_unstable(inode);
 
        /* Do atomic weak cache consistency updates */
        nfs_wcc_update_inode(inode, fattr);
 
-       if ((fattr->valid & NFS_ATTR_FATTR_V4) != 0 &&
-                       nfsi->change_attr != fattr->change_attr) {
+       if ((fattr->valid & NFS_ATTR_FATTR_V4) != 0) {
+               if (nfsi->change_attr == fattr->change_attr)
+                       goto out;
                nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (!data_unstable)
                        nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE;
        }
 
-       /* Has the inode gone and changed behind our back? */
-       if (nfsi->fileid != fattr->fileid
-                       || (inode->i_mode & S_IFMT) != (fattr->mode & S_IFMT)) {
-               return -EIO;
-       }
-
-       cur_size = i_size_read(inode);
-       new_isize = nfs_size_to_loff_t(fattr->size);
-
        /* Verify a few of the more important attributes */
        if (!timespec_equal(&inode->i_mtime, &fattr->mtime)) {
                nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (!data_unstable)
                        nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE;
        }
-       if (cur_size != new_isize) {
-               nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
-               if (nfsi->npages == 0)
-                       nfsi->cache_validity |= NFS_INO_REVAL_PAGECACHE;
-       }
+
+       cur_size = i_size_read(inode);
+       new_isize = nfs_size_to_loff_t(fattr->size);
+       if (cur_size != new_isize && nfsi->npages == 0)
+               nfsi->cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
 
        /* Have any file permissions changed? */
        if ((inode->i_mode & S_IALLUGO) != (fattr->mode & S_IALLUGO)
@@ -1343,6 +1445,7 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat
        if (inode->i_nlink != fattr->nlink)
                nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
 
+out:
        if (!timespec_equal(&inode->i_atime, &fattr->atime))
                nfsi->cache_validity |= NFS_INO_INVALID_ATIME;
 
@@ -1481,15 +1584,6 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                nfsi->cache_change_attribute = jiffies;
        }
 
-       if ((fattr->valid & NFS_ATTR_FATTR_V4)
-           && nfsi->change_attr != fattr->change_attr) {
-               dprintk("NFS: change_attr change on server for file %s/%ld\n",
-                      inode->i_sb->s_id, inode->i_ino);
-               nfsi->change_attr = fattr->change_attr;
-               invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
-               nfsi->cache_change_attribute = jiffies;
-       }
-
        /* If ctime has changed we should definitely clear access+acl caches */
        if (!timespec_equal(&inode->i_ctime, &fattr->ctime)) {
                invalid |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
@@ -1519,8 +1613,20 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                inode->i_blksize = fattr->du.nfs2.blocksize;
        }
 
+       if ((fattr->valid & NFS_ATTR_FATTR_V4)) {
+               if (nfsi->change_attr != fattr->change_attr) {
+                       dprintk("NFS: change_attr change on server for file %s/%ld\n",
+                                       inode->i_sb->s_id, inode->i_ino);
+                       nfsi->change_attr = fattr->change_attr;
+                       invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
+                       nfsi->cache_change_attribute = jiffies;
+               } else
+                       invalid &= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA);
+       }
+
        /* Update attrtimeo value if we're out of the unstable period */
        if (invalid & NFS_INO_INVALID_ATTR) {
+               nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
                nfsi->attrtimeo = NFS_MINATTRTIMEO(inode);
                nfsi->attrtimeo_timestamp = jiffies;
        } else if (time_after(jiffies, nfsi->attrtimeo_timestamp+nfsi->attrtimeo)) {
@@ -1585,8 +1691,8 @@ static int nfs_compare_super(struct super_block *sb, void *data)
        return !nfs_compare_fh(&old->fh, &server->fh);
 }
 
-static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
-       int flags, const char *dev_name, void *raw_data)
+static int nfs_get_sb(struct file_system_type *fs_type,
+       int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt)
 {
        int error;
        struct nfs_server *server = NULL;
@@ -1594,14 +1700,14 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
        struct nfs_fh *root;
        struct nfs_mount_data *data = raw_data;
 
-       s = ERR_PTR(-EINVAL);
+       error = -EINVAL;
        if (data == NULL) {
                dprintk("%s: missing data argument\n", __FUNCTION__);
-               goto out_err;
+               goto out_err_noserver;
        }
        if (data->version <= 0 || data->version > NFS_MOUNT_VERSION) {
                dprintk("%s: bad mount version\n", __FUNCTION__);
-               goto out_err;
+               goto out_err_noserver;
        }
        switch (data->version) {
                case 1:
@@ -1613,7 +1719,7 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
                                dprintk("%s: mount structure version %d does not support NFSv3\n",
                                                __FUNCTION__,
                                                data->version);
-                               goto out_err;
+                               goto out_err_noserver;
                        }
                        data->root.size = NFS2_FHSIZE;
                        memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE);
@@ -1622,25 +1728,24 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
                                dprintk("%s: mount structure version %d does not support strong security\n",
                                                __FUNCTION__,
                                                data->version);
-                               goto out_err;
+                               goto out_err_noserver;
                        }
                case 5:
                        memset(data->context, 0, sizeof(data->context));
        }
 #ifndef CONFIG_NFS_V3
        /* If NFSv3 is not compiled in, return -EPROTONOSUPPORT */
-       s = ERR_PTR(-EPROTONOSUPPORT);
+       error = -EPROTONOSUPPORT;
        if (data->flags & NFS_MOUNT_VER3) {
                dprintk("%s: NFSv3 not compiled into kernel\n", __FUNCTION__);
-               goto out_err;
+               goto out_err_noserver;
        }
 #endif /* CONFIG_NFS_V3 */
 
-       s = ERR_PTR(-ENOMEM);
-       server = kmalloc(sizeof(struct nfs_server), GFP_KERNEL);
+       error = -ENOMEM;
+       server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL);
        if (!server)
-               goto out_err;
-       memset(server, 0, sizeof(struct nfs_server));
+               goto out_err_noserver;
        /* Zero out the NFS state stuff */
        init_nfsv4_state(server);
        server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL);
@@ -1650,7 +1755,7 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
                root->size = data->root.size;
        else
                root->size = NFS2_FHSIZE;
-       s = ERR_PTR(-EINVAL);
+       error = -EINVAL;
        if (root->size > sizeof(root->data)) {
                dprintk("%s: invalid root filehandle\n", __FUNCTION__);
                goto out_err;
@@ -1666,32 +1771,44 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
        }
 
        /* Fire up rpciod if not yet running */
-       s = ERR_PTR(rpciod_up());
-       if (IS_ERR(s)) {
-               dprintk("%s: couldn't start rpciod! Error = %ld\n",
-                               __FUNCTION__, PTR_ERR(s));
+       error = rpciod_up();
+       if (error < 0) {
+               dprintk("%s: couldn't start rpciod! Error = %d\n",
+                               __FUNCTION__, error);
                goto out_err;
        }
 
        s = sget(fs_type, nfs_compare_super, nfs_set_super, server);
-       if (IS_ERR(s) || s->s_root)
+       if (IS_ERR(s)) {
+               error = PTR_ERR(s);
+               goto out_err_rpciod;
+       }
+
+       if (s->s_root)
                goto out_rpciod_down;
 
        s->s_flags = flags;
 
-       error = nfs_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
+       error = nfs_fill_super(s, data, flags & MS_SILENT ? 1 : 0);
        if (error) {
                up_write(&s->s_umount);
                deactivate_super(s);
-               return ERR_PTR(error);
+               return error;
        }
        s->s_flags |= MS_ACTIVE;
-       return s;
+       return simple_set_mnt(mnt, s);
+
 out_rpciod_down:
        rpciod_down();
+       kfree(server);
+       return simple_set_mnt(mnt, s);
+
+out_err_rpciod:
+       rpciod_down();
 out_err:
        kfree(server);
-       return s;
+out_err_noserver:
+       return error;
 }
 
 static void nfs_kill_super(struct super_block *s)
@@ -1712,6 +1829,7 @@ static void nfs_kill_super(struct super_block *s)
 
        rpciod_down();          /* release rpciod */
 
+       nfs_free_iostats(server->io_stats);
        kfree(server->hostname);
        kfree(server);
 }
@@ -1738,6 +1856,7 @@ static struct super_operations nfs4_sops = {
        .clear_inode    = nfs4_clear_inode,
        .umount_begin   = nfs_umount_begin,
        .show_options   = nfs_show_options,
+       .show_stats     = nfs_show_stats,
 };
 
 /*
@@ -1800,6 +1919,9 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
 
        nfs_init_timeout_values(&timeparms, data->proto, data->timeo, data->retrans);
 
+       server->retrans_timeo = timeparms.to_initval;
+       server->retrans_count = timeparms.to_retries;
+
        clp = nfs4_get_client(&server->addr.sin_addr);
        if (!clp) {
                dprintk("%s: failed to create NFS4 client.\n", __FUNCTION__);
@@ -1923,8 +2045,8 @@ nfs_copy_user_string(char *dst, struct nfs_string *src, int maxlen)
        return dst;
 }
 
-static struct super_block *nfs4_get_sb(struct file_system_type *fs_type,
-       int flags, const char *dev_name, void *raw_data)
+static int nfs4_get_sb(struct file_system_type *fs_type,
+       int flags, const char *dev_name, void *raw_data, struct vfsmount *mnt)
 {
        int error;
        struct nfs_server *server;
@@ -1934,17 +2056,16 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type,
 
        if (data == NULL) {
                dprintk("%s: missing data argument\n", __FUNCTION__);
-               return ERR_PTR(-EINVAL);
+               return -EINVAL;
        }
        if (data->version <= 0 || data->version > NFS4_MOUNT_VERSION) {
                dprintk("%s: bad mount version\n", __FUNCTION__);
-               return ERR_PTR(-EINVAL);
+               return -EINVAL;
        }
 
-       server = kmalloc(sizeof(struct nfs_server), GFP_KERNEL);
+       server = kzalloc(sizeof(struct nfs_server), GFP_KERNEL);
        if (!server)
-               return ERR_PTR(-ENOMEM);
-       memset(server, 0, sizeof(struct nfs_server));
+               return -ENOMEM;
        /* Zero out the NFS state stuff */
        init_nfsv4_state(server);
        server->client = server->client_sys = server->client_acl = ERR_PTR(-EINVAL);
@@ -1966,51 +2087,59 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type,
 
        /* We now require that the mount process passes the remote address */
        if (data->host_addrlen != sizeof(server->addr)) {
-               s = ERR_PTR(-EINVAL);
+               error = -EINVAL;
                goto out_free;
        }
        if (copy_from_user(&server->addr, data->host_addr, sizeof(server->addr))) {
-               s = ERR_PTR(-EFAULT);
+               error = -EFAULT;
                goto out_free;
        }
        if (server->addr.sin_family != AF_INET ||
            server->addr.sin_addr.s_addr == INADDR_ANY) {
                dprintk("%s: mount program didn't pass remote IP address!\n",
                                __FUNCTION__);
-               s = ERR_PTR(-EINVAL);
+               error = -EINVAL;
                goto out_free;
        }
 
        /* Fire up rpciod if not yet running */
-       s = ERR_PTR(rpciod_up());
-       if (IS_ERR(s)) {
-               dprintk("%s: couldn't start rpciod! Error = %ld\n",
-                               __FUNCTION__, PTR_ERR(s));
+       error = rpciod_up();
+       if (error < 0) {
+               dprintk("%s: couldn't start rpciod! Error = %d\n",
+                               __FUNCTION__, error);
                goto out_free;
        }
 
        s = sget(fs_type, nfs4_compare_super, nfs_set_super, server);
-
-       if (IS_ERR(s) || s->s_root)
+       if (IS_ERR(s)) {
+               error = PTR_ERR(s);
                goto out_free;
+       }
+
+       if (s->s_root) {
+               kfree(server->mnt_path);
+               kfree(server->hostname);
+               kfree(server);
+               return simple_set_mnt(mnt, s);
+       }
 
        s->s_flags = flags;
 
-       error = nfs4_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
+       error = nfs4_fill_super(s, data, flags & MS_SILENT ? 1 : 0);
        if (error) {
                up_write(&s->s_umount);
                deactivate_super(s);
-               return ERR_PTR(error);
+               return error;
        }
        s->s_flags |= MS_ACTIVE;
-       return s;
+       return simple_set_mnt(mnt, s);
 out_err:
-       s = (struct super_block *)p;
+       error = PTR_ERR(p);
 out_free:
        kfree(server->mnt_path);
        kfree(server->hostname);
        kfree(server);
-       return s;
+       return error;
 }
 
 static void nfs4_kill_super(struct super_block *sb)
@@ -2024,10 +2153,12 @@ static void nfs4_kill_super(struct super_block *sb)
 
        if (server->client != NULL && !IS_ERR(server->client))
                rpc_shutdown_client(server->client);
-       rpciod_down();          /* release rpciod */
 
        destroy_nfsv4_state(server);
 
+       rpciod_down();
+
+       nfs_free_iostats(server->io_stats);
        kfree(server->hostname);
        kfree(server);
 }
@@ -2163,7 +2294,8 @@ static int nfs_init_inodecache(void)
 {
        nfs_inode_cachep = kmem_cache_create("nfs_inode_cache",
                                             sizeof(struct nfs_inode),
-                                            0, SLAB_RECLAIM_ACCOUNT,
+                                            0, (SLAB_RECLAIM_ACCOUNT|
+                                               SLAB_MEM_SPREAD),
                                             init_once, NULL);
        if (nfs_inode_cachep == NULL)
                return -ENOMEM;