vserver 1.9.3
[linux-2.6.git] / fs / nfs / inode.c
index 7f25c31..60b3074 100644 (file)
@@ -40,6 +40,8 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
+#include "delegation.h"
+
 #define NFSDBG_FACILITY                NFSDBG_VFS
 #define NFS_PARANOIA 1
 
@@ -56,9 +58,8 @@ static int nfs_update_inode(struct inode *, struct nfs_fattr *, unsigned long);
 
 static struct inode *nfs_alloc_inode(struct super_block *sb);
 static void nfs_destroy_inode(struct inode *);
-static void nfs_write_inode(struct inode *,int);
+static int nfs_write_inode(struct inode *,int);
 static void nfs_delete_inode(struct inode *);
-static void nfs_put_super(struct super_block *);
 static void nfs_clear_inode(struct inode *);
 static void nfs_umount_begin(struct super_block *);
 static int  nfs_statfs(struct super_block *, struct kstatfs *);
@@ -69,7 +70,6 @@ static struct super_operations nfs_sops = {
        .destroy_inode  = nfs_destroy_inode,
        .write_inode    = nfs_write_inode,
        .delete_inode   = nfs_delete_inode,
-       .put_super      = nfs_put_super,
        .statfs         = nfs_statfs,
        .clear_inode    = nfs_clear_inode,
        .umount_begin   = nfs_umount_begin,
@@ -111,12 +111,16 @@ nfs_fattr_to_ino_t(struct nfs_fattr *fattr)
        return nfs_fileid_to_ino_t(fattr->fileid);
 }
 
-static void
+static int
 nfs_write_inode(struct inode *inode, int sync)
 {
        int flags = sync ? FLUSH_WAIT : 0;
+       int ret;
 
-       nfs_commit_inode(inode, 0, 0, flags);
+       ret = nfs_commit_inode(inode, 0, 0, flags);
+       if (ret < 0)
+               return ret;
+       return 0;
 }
 
 static void
@@ -124,8 +128,9 @@ nfs_delete_inode(struct inode * inode)
 {
        dprintk("NFS: delete_inode(%s/%ld)\n", inode->i_sb->s_id, inode->i_ino);
 
+       nfs_wb_all(inode);
        /*
-        * The following can never actually happen...
+        * The following should never happen...
         */
        if (nfs_have_writebacks(inode)) {
                printk(KERN_ERR "nfs_delete_inode: inode %ld has pending RPC requests\n", inode->i_ino);
@@ -142,37 +147,16 @@ static void
 nfs_clear_inode(struct inode *inode)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
-       struct rpc_cred *cred = nfsi->mm_cred;
+       struct rpc_cred *cred;
 
-       if (cred)
-               put_rpccred(cred);
+       nfs_wb_all(inode);
+       BUG_ON (!list_empty(&nfsi->open_files));
        cred = nfsi->cache_access.cred;
        if (cred)
                put_rpccred(cred);
        BUG_ON(atomic_read(&nfsi->data_updates) != 0);
 }
 
-void
-nfs_put_super(struct super_block *sb)
-{
-       struct nfs_server *server = NFS_SB(sb);
-
-       nfs4_renewd_prepare_shutdown(server);
-
-       if (server->client != NULL)
-               rpc_shutdown_client(server->client);
-       if (server->client_sys != NULL)
-               rpc_shutdown_client(server->client_sys);
-
-       if (!(server->flags & NFS_MOUNT_NONLM))
-               lockd_down();   /* release rpc.lockd */
-       rpciod_down();          /* release rpciod */
-
-       destroy_nfsv4_state(server);
-
-       kfree(server->hostname);
-}
-
 void
 nfs_umount_begin(struct super_block *sb)
 {
@@ -294,14 +278,6 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor)
                server->rsize = nfs_block_size(fsinfo.rtpref, NULL);
        if (server->wsize == 0)
                server->wsize = nfs_block_size(fsinfo.wtpref, NULL);
-       if (sb->s_blocksize == 0) {
-               if (fsinfo.wtmult == 0) {
-                       sb->s_blocksize = 512;
-                       sb->s_blocksize_bits = 9;
-               } else
-                       sb->s_blocksize = nfs_block_bits(fsinfo.wtmult,
-                                                        &sb->s_blocksize_bits);
-       }
 
        if (fsinfo.rtmax >= 512 && server->rsize > fsinfo.rtmax)
                server->rsize = nfs_block_size(fsinfo.rtmax, NULL);
@@ -320,6 +296,11 @@ nfs_sb_init(struct super_block *sb, rpc_authflavor_t authflavor)
                 server->wsize = server->wpages << PAGE_CACHE_SHIFT;
        }
 
+       if (sb->s_blocksize == 0)
+               sb->s_blocksize = nfs_block_bits(server->wsize,
+                                                        &sb->s_blocksize_bits);
+       server->wtmult = nfs_block_bits(fsinfo.wtmult, NULL);
+
        server->dtsize = nfs_block_size(fsinfo.dtpref, NULL);
        if (server->dtsize > PAGE_CACHE_SIZE)
                server->dtsize = PAGE_CACHE_SIZE;
@@ -410,7 +391,6 @@ static int
 nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
 {
        struct nfs_server       *server;
-       int                     err = -EIO;
        rpc_authflavor_t        authflavor;
 
        server           = NFS_SB(sb);
@@ -429,10 +409,14 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
        server->acdirmin = data->acdirmin*HZ;
        server->acdirmax = data->acdirmax*HZ;
 
+       /* Start lockd here, before we might error out */
+       if (!(server->flags & NFS_MOUNT_NONLM))
+               lockd_up();
+
        server->namelen  = data->namlen;
        server->hostname = kmalloc(strlen(data->hostname) + 1, GFP_KERNEL);
        if (!server->hostname)
-               goto out_fail;
+               return -ENOMEM;
        strcpy(server->hostname, data->hostname);
 
        /* Check NFS protocol revision and initialize RPC op vector
@@ -443,11 +427,11 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
                server->caps |= NFS_CAP_READDIRPLUS;
                if (data->version < 4) {
                        printk(KERN_NOTICE "NFS: NFSv3 not supported by mount program.\n");
-                       goto out_fail;
+                       return -EIO;
                }
 #else
                printk(KERN_NOTICE "NFS: NFSv3 not supported.\n");
-               goto out_fail;
+               return -EIO;
 #endif
        } else {
                server->rpc_ops = &nfs_v2_clientops;
@@ -462,30 +446,19 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
        /* Create RPC client handles */
        server->client = nfs_create_client(server, data);
        if (IS_ERR(server->client))
-               goto out_fail;
+               return PTR_ERR(server->client);
        /* RFC 2623, sec 2.3.2 */
        if (authflavor != RPC_AUTH_UNIX) {
                server->client_sys = rpc_clone_client(server->client);
-               if (server->client_sys == NULL)
-                       goto out_shutdown;
+               if (IS_ERR(server->client_sys))
+                       return PTR_ERR(server->client_sys);
                if (!rpcauth_create(RPC_AUTH_UNIX, server->client_sys))
-                       goto out_shutdown;
+                       return -ENOMEM;
        } else {
                atomic_inc(&server->client->cl_count);
                server->client_sys = server->client;
        }
 
-       /* Fire up rpciod if not yet running */
-       if (rpciod_up() != 0) {
-               printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
-               goto out_shutdown;
-       }
-
-       sb->s_op = &nfs_sops;
-       err = nfs_sb_init(sb, authflavor);
-       if (err != 0)
-               goto out_noinit;
-
        if (server->flags & NFS_MOUNT_VER3) {
                if (server->namelen == 0 || server->namelen > NFS3_MAXNAMLEN)
                        server->namelen = NFS3_MAXNAMLEN;
@@ -494,21 +467,8 @@ nfs_fill_super(struct super_block *sb, struct nfs_mount_data *data, int silent)
                        server->namelen = NFS2_MAXNAMLEN;
        }
 
-       /* Check whether to start the lockd process */
-       if (!(server->flags & NFS_MOUNT_NONLM))
-               lockd_up();
-       return 0;
-out_noinit:
-       rpciod_down();
-out_shutdown:
-       if (server->client)
-               rpc_shutdown_client(server->client);
-       if (server->client_sys)
-               rpc_shutdown_client(server->client_sys);
-out_fail:
-       if (server->hostname)
-               kfree(server->hostname);
-       return err;
+       sb->s_op = &nfs_sops;
+       return nfs_sb_init(sb, authflavor);
 }
 
 static int
@@ -531,6 +491,7 @@ nfs_statfs(struct super_block *sb, struct kstatfs *buf)
        if (error < 0)
                goto out_err;
 
+       buf->f_frsize = server->wtmult;
        buf->f_bsize = sb->s_blocksize;
        blockbits = sb->s_blocksize_bits;
        blockres = (1 << blockbits) - 1;
@@ -648,7 +609,7 @@ nfs_find_actor(struct inode *inode, void *opaque)
 
        if (NFS_FILEID(inode) != fattr->fileid)
                return 0;
-       if (memcmp(NFS_FH(inode), fh, sizeof(struct nfs_fh)) != 0)
+       if (nfs_compare_fh(NFS_FH(inode), fh))
                return 0;
        if (is_bad_inode(inode))
                return 0;
@@ -659,11 +620,10 @@ static int
 nfs_init_locked(struct inode *inode, void *opaque)
 {
        struct nfs_find_desc    *desc = (struct nfs_find_desc *)opaque;
-       struct nfs_fh           *fh = desc->fh;
        struct nfs_fattr        *fattr = desc->fattr;
 
        NFS_FILEID(inode) = fattr->fileid;
-       memcpy(NFS_FH(inode), fh, sizeof(struct nfs_fh));
+       nfs_copy_fh(NFS_FH(inode), desc->fh);
        return 0;
 }
 
@@ -765,8 +725,8 @@ out:
        return inode;
 /*
 fail_dlim:
-        make_bad_inode(inode);
-        iput(inode);
+       make_bad_inode(inode);
+       iput(inode);
        inode = NULL;
 */
 out_no_inode:
@@ -774,8 +734,7 @@ out_no_inode:
        goto out;
 }
 
-#define NFS_VALID_ATTRS (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_XID|ATTR_SIZE|\
-                        ATTR_ATIME|ATTR_ATIME_SET|ATTR_MTIME|ATTR_MTIME_SET)
+#define NFS_VALID_ATTRS (ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_SIZE|ATTR_ATIME|ATTR_ATIME_SET|ATTR_MTIME|ATTR_MTIME_SET)
 
 int
 nfs_setattr(struct dentry *dentry, struct iattr *attr)
@@ -875,53 +834,114 @@ int nfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat)
        return err;
 }
 
+struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, struct rpc_cred *cred)
+{
+       struct nfs_open_context *ctx;
+
+       ctx = (struct nfs_open_context *)kmalloc(sizeof(*ctx), GFP_KERNEL);
+       if (ctx != NULL) {
+               atomic_set(&ctx->count, 1);
+               ctx->dentry = dget(dentry);
+               ctx->cred = get_rpccred(cred);
+               ctx->state = NULL;
+               ctx->lockowner = current->files;
+               ctx->error = 0;
+               init_waitqueue_head(&ctx->waitq);
+       }
+       return ctx;
+}
+
+struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx)
+{
+       if (ctx != NULL)
+               atomic_inc(&ctx->count);
+       return ctx;
+}
+
+void put_nfs_open_context(struct nfs_open_context *ctx)
+{
+       if (atomic_dec_and_test(&ctx->count)) {
+               if (ctx->state != NULL)
+                       nfs4_close_state(ctx->state, ctx->mode);
+               if (ctx->cred != NULL)
+                       put_rpccred(ctx->cred);
+               dput(ctx->dentry);
+               kfree(ctx);
+       }
+}
+
 /*
  * Ensure that mmap has a recent RPC credential for use when writing out
  * shared pages
  */
-void
-nfs_set_mmcred(struct inode *inode, struct rpc_cred *cred)
+void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx)
+{
+       struct inode *inode = filp->f_dentry->d_inode;
+       struct nfs_inode *nfsi = NFS_I(inode);
+
+       filp->private_data = get_nfs_open_context(ctx);
+       spin_lock(&inode->i_lock);
+       list_add(&ctx->list, &nfsi->open_files);
+       spin_unlock(&inode->i_lock);
+}
+
+struct nfs_open_context *nfs_find_open_context(struct inode *inode, int mode)
 {
-       struct rpc_cred **p = &NFS_I(inode)->mm_cred,
-                       *oldcred = *p;
+       struct nfs_inode *nfsi = NFS_I(inode);
+       struct nfs_open_context *pos, *ctx = NULL;
+
+       spin_lock(&inode->i_lock);
+       list_for_each_entry(pos, &nfsi->open_files, list) {
+               if ((pos->mode & mode) == mode) {
+                       ctx = get_nfs_open_context(pos);
+                       break;
+               }
+       }
+       spin_unlock(&inode->i_lock);
+       return ctx;
+}
 
-       *p = get_rpccred(cred);
-       if (oldcred)
-               put_rpccred(oldcred);
+void nfs_file_clear_open_context(struct file *filp)
+{
+       struct inode *inode = filp->f_dentry->d_inode;
+       struct nfs_open_context *ctx = (struct nfs_open_context *)filp->private_data;
+
+       if (ctx) {
+               filp->private_data = NULL;
+               spin_lock(&inode->i_lock);
+               list_del(&ctx->list);
+               spin_unlock(&inode->i_lock);
+               put_nfs_open_context(ctx);
+       }
 }
 
 /*
- * These are probably going to contain hooks for
- * allocating and releasing RPC credentials for
- * the file. I'll have to think about Tronds patch
- * a bit more..
+ * These allocate and release file read/write context information.
  */
 int nfs_open(struct inode *inode, struct file *filp)
 {
-       struct rpc_auth *auth;
+       struct nfs_open_context *ctx;
        struct rpc_cred *cred;
 
-       auth = NFS_CLIENT(inode)->cl_auth;
-       cred = rpcauth_lookupcred(auth, 0);
-       filp->private_data = cred;
-       if ((filp->f_mode & FMODE_WRITE) != 0) {
-               nfs_set_mmcred(inode, cred);
+       if ((cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0)) == NULL)
+               return -ENOMEM;
+       ctx = alloc_nfs_open_context(filp->f_dentry, cred);
+       put_rpccred(cred);
+       if (ctx == NULL)
+               return -ENOMEM;
+       ctx->mode = filp->f_mode;
+       nfs_file_set_open_context(filp, ctx);
+       put_nfs_open_context(ctx);
+       if ((filp->f_mode & FMODE_WRITE) != 0)
                nfs_begin_data_update(inode);
-       }
        return 0;
 }
 
 int nfs_release(struct inode *inode, struct file *filp)
 {
-       struct rpc_cred *cred;
-
-       lock_kernel();
        if ((filp->f_mode & FMODE_WRITE) != 0)
                nfs_end_data_update(inode);
-       cred = nfs_file_cred(filp);
-       if (cred)
-               put_rpccred(cred);
-       unlock_kernel();
+       nfs_file_clear_open_context(filp);
        return 0;
 }
 
@@ -962,7 +982,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
 
        /* Protect against RPC races by saving the change attribute */
        verifier = nfs_save_change_attribute(inode);
-       status = NFS_PROTO(inode)->getattr(inode, &fattr);
+       status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), &fattr);
        if (status) {
                dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n",
                         inode->i_sb->s_id,
@@ -1018,6 +1038,30 @@ out:
        return status;
 }
 
+int nfs_attribute_timeout(struct inode *inode)
+{
+       struct nfs_inode *nfsi = NFS_I(inode);
+
+       if (nfs_have_delegation(inode, FMODE_READ))
+               return 0;
+       return time_after(jiffies, nfsi->read_cache_jiffies+nfsi->attrtimeo);
+}
+
+/**
+ * nfs_revalidate_inode - Revalidate the inode attributes
+ * @server - pointer to nfs_server struct
+ * @inode - pointer to inode struct
+ *
+ * Updates inode attribute information by retrieving the data from the server.
+ */
+int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
+{
+       if (!(NFS_FLAGS(inode) & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA))
+                       && !nfs_attribute_timeout(inode))
+               return NFS_STALE(inode) ? -ESTALE : 0;
+       return __nfs_revalidate_inode(server, inode);
+}
+
 /**
  * nfs_begin_data_update
  * @inode - pointer to inode
@@ -1039,11 +1083,13 @@ void nfs_end_data_update(struct inode *inode)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
 
-       /* Mark the attribute cache for revalidation */
-       nfsi->flags |= NFS_INO_INVALID_ATTR;
-       /* Directories and symlinks: invalidate page cache too */
-       if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
-               nfsi->flags |= NFS_INO_INVALID_DATA;
+       if (!nfs_have_delegation(inode, FMODE_READ)) {
+               /* Mark the attribute cache for revalidation */
+               nfsi->flags |= NFS_INO_INVALID_ATTR;
+               /* Directories and symlinks: invalidate page cache too */
+               if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
+                       nfsi->flags |= NFS_INO_INVALID_DATA;
+       }
        nfsi->cache_change_attribute ++;
        atomic_dec(&nfsi->data_updates);
 }
@@ -1087,6 +1133,10 @@ int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
        gid_t gid;
        xid_t xid = 0;
 
+       /* Do we hold a delegation? */
+       if (nfs_have_delegation(inode, FMODE_READ))
+               return 0;
+
        /* Are we in the process of updating data on the server? */
        data_unstable = nfs_caches_unstable(inode);
 
@@ -1298,7 +1348,8 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr, unsign
        if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)
                                || S_ISLNK(inode->i_mode)))
                invalid &= ~NFS_INO_INVALID_DATA;
-       nfsi->flags |= invalid;
+       if (!nfs_have_delegation(inode, FMODE_READ))
+               nfsi->flags |= invalid;
 
        return 0;
  out_changed:
@@ -1338,7 +1389,7 @@ static int nfs_compare_super(struct super_block *sb, void *data)
                return 0;
        if (old->addr.sin_port != server->addr.sin_port)
                return 0;
-       return !memcmp(&old->fh, &server->fh, sizeof(struct nfs_fh));
+       return !nfs_compare_fh(&old->fh, &server->fh);
 }
 
 static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
@@ -1362,11 +1413,6 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
        /* Zero out the NFS state stuff */
        init_nfsv4_state(server);
 
-       root = &server->fh;
-       memcpy(root, &data->root, sizeof(*root));
-       if (root->size < sizeof(root->data))
-               memset(root->data+root->size, 0, sizeof(root->data)-root->size);
-
        if (data->version != NFS_MOUNT_VERSION) {
                printk("nfs warning: mount version %s than kernel\n",
                        data->version < NFS_MOUNT_VERSION ? "older" : "newer");
@@ -1376,19 +1422,25 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
                        data->bsize  = 0;
                if (data->version < 4) {
                        data->flags &= ~NFS_MOUNT_VER3;
-                       memset(root, 0, sizeof(*root));
-                       root->size = NFS2_FHSIZE;
-                       memcpy(root->data, data->old_root.data, NFS2_FHSIZE);
+                       data->root.size = NFS2_FHSIZE;
+                       memcpy(data->root.data, data->old_root.data, NFS2_FHSIZE);
                }
                if (data->version < 5)
                        data->flags &= ~NFS_MOUNT_SECFLAVOUR;
        }
 
+       root = &server->fh;
+       if (data->flags & NFS_MOUNT_VER3)
+               root->size = data->root.size;
+       else
+               root->size = NFS2_FHSIZE;
        if (root->size > sizeof(root->data)) {
                printk("nfs_get_sb: invalid root filehandle\n");
                kfree(server);
                return ERR_PTR(-EINVAL);
        }
+       memcpy(root->data, data->root.data, root->size);
+
        /* We now require that the mount process passes the remote address */
        memcpy(&server->addr, &data->addr, sizeof(server->addr));
        if (server->addr.sin_addr.s_addr == INADDR_ANY) {
@@ -1406,6 +1458,13 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
 
        s->s_flags = flags;
 
+       /* Fire up rpciod if not yet running */
+       if (rpciod_up() != 0) {
+               printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
+               kfree(server);
+               return ERR_PTR(-EIO);
+       }
+
        error = nfs_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
        if (error) {
                up_write(&s->s_umount);
@@ -1419,7 +1478,25 @@ static struct super_block *nfs_get_sb(struct file_system_type *fs_type,
 static void nfs_kill_super(struct super_block *s)
 {
        struct nfs_server *server = NFS_SB(s);
+
        kill_anon_super(s);
+
+       nfs4_renewd_prepare_shutdown(server);
+
+       if (server->client != NULL && !IS_ERR(server->client))
+               rpc_shutdown_client(server->client);
+       if (server->client_sys != NULL && !IS_ERR(server->client_sys))
+               rpc_shutdown_client(server->client_sys);
+
+       if (!(server->flags & NFS_MOUNT_NONLM))
+               lockd_down();   /* release rpc.lockd */
+
+       rpciod_down();          /* release rpciod */
+
+       destroy_nfsv4_state(server);
+
+       if (server->hostname != NULL)
+               kfree(server->hostname);
        kfree(server);
 }
 
@@ -1435,12 +1512,12 @@ static struct file_system_type nfs_fs_type = {
 
 static void nfs4_clear_inode(struct inode *);
 
+
 static struct super_operations nfs4_sops = { 
        .alloc_inode    = nfs_alloc_inode,
        .destroy_inode  = nfs_destroy_inode,
        .write_inode    = nfs_write_inode,
        .delete_inode   = nfs_delete_inode,
-       .put_super      = nfs_put_super,
        .statfs         = nfs_statfs,
        .clear_inode    = nfs4_clear_inode,
        .umount_begin   = nfs_umount_begin,
@@ -1456,6 +1533,12 @@ static void nfs4_clear_inode(struct inode *inode)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
 
+       /* If we are holding a delegation, return it! */
+       if (nfsi->delegation != NULL)
+               nfs_inode_return_delegation(inode);
+       /* First call standard NFS clear_inode() code */
+       nfs_clear_inode(inode);
+       /* Now clear out any remaining state */
        while (!list_empty(&nfsi->open_states)) {
                struct nfs4_state *state;
                
@@ -1470,8 +1553,6 @@ static void nfs4_clear_inode(struct inode *inode)
                BUG_ON(atomic_read(&state->count) != 1);
                nfs4_close_state(state, state->state);
        }
-       /* Now call standard NFS clear_inode() code */
-       nfs_clear_inode(inode);
 }
 
 
@@ -1531,7 +1612,7 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
        clp = nfs4_get_client(&server->addr.sin_addr);
        if (!clp) {
                printk(KERN_WARNING "NFS: failed to create NFS4 client.\n");
-               goto out_fail;
+               return -EIO;
        }
 
        /* Now create transport and client */
@@ -1569,8 +1650,13 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
                memcpy(clp->cl_ipaddr, server->ip_addr, sizeof(clp->cl_ipaddr));
                nfs_idmap_new(clp);
        }
-       if (list_empty(&clp->cl_superblocks))
-               clear_bit(NFS4CLNT_OK, &clp->cl_state);
+       if (list_empty(&clp->cl_superblocks)) {
+               err = nfs4_init_client(clp);
+               if (err != 0) {
+                       up_write(&clp->cl_sem);
+                       goto out_fail;
+               }
+       }
        list_add_tail(&server->nfs4_siblings, &clp->cl_superblocks);
        clnt = rpc_clone_client(clp->cl_rpcclient);
        if (!IS_ERR(clnt))
@@ -1580,45 +1666,29 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data,
 
        if (IS_ERR(clnt)) {
                printk(KERN_WARNING "NFS: cannot create RPC client.\n");
-               err = PTR_ERR(clnt);
-               goto out_remove_list;
+               return PTR_ERR(clnt);
        }
 
        clnt->cl_intr     = (server->flags & NFS4_MOUNT_INTR) ? 1 : 0;
        clnt->cl_softrtry = (server->flags & NFS4_MOUNT_SOFT) ? 1 : 0;
        server->client    = clnt;
 
-       err = -ENOMEM;
        if (server->nfs4_state->cl_idmap == NULL) {
                printk(KERN_WARNING "NFS: failed to create idmapper.\n");
-               goto out_shutdown;
+               return -ENOMEM;
        }
 
        if (clnt->cl_auth->au_flavor != authflavour) {
                if (rpcauth_create(authflavour, clnt) == NULL) {
                        printk(KERN_WARNING "NFS: couldn't create credcache!\n");
-                       goto out_shutdown;
+                       return -ENOMEM;
                }
        }
 
-       /* Fire up rpciod if not yet running */
-       if (rpciod_up() != 0) {
-               printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
-               goto out_shutdown;
-       }
-
        sb->s_op = &nfs4_sops;
        err = nfs_sb_init(sb, authflavour);
        if (err == 0)
                return 0;
-       rpciod_down();
-out_shutdown:
-       rpc_shutdown_client(server->client);
-out_remove_list:
-       down_write(&server->nfs4_state->cl_sem);
-       list_del_init(&server->nfs4_siblings);
-       up_write(&server->nfs4_state->cl_sem);
-       destroy_nfsv4_state(server);
 out_fail:
        if (clp)
                nfs4_put_client(clp);
@@ -1724,6 +1794,13 @@ static struct super_block *nfs4_get_sb(struct file_system_type *fs_type,
 
        s->s_flags = flags;
 
+       /* Fire up rpciod if not yet running */
+       if (rpciod_up() != 0) {
+               printk(KERN_WARNING "NFS: couldn't start rpciod!\n");
+               s = ERR_PTR(-EIO);
+               goto out_free;
+       }
+
        error = nfs4_fill_super(s, data, flags & MS_VERBOSE ? 1 : 0);
        if (error) {
                up_write(&s->s_umount);
@@ -1743,22 +1820,31 @@ out_free:
        return s;
 }
 
+static void nfs4_kill_super(struct super_block *sb)
+{
+       nfs_return_all_delegations(sb);
+       nfs_kill_super(sb);
+}
+
 static struct file_system_type nfs4_fs_type = {
        .owner          = THIS_MODULE,
        .name           = "nfs4",
        .get_sb         = nfs4_get_sb,
-       .kill_sb        = nfs_kill_super,
+       .kill_sb        = nfs4_kill_super,
        .fs_flags       = FS_ODD_RENAME|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
 };
 
-#define nfs4_zero_state(nfsi) \
+#define nfs4_init_once(nfsi) \
        do { \
                INIT_LIST_HEAD(&(nfsi)->open_states); \
+               nfsi->delegation = NULL; \
+               nfsi->delegation_state = 0; \
+               init_rwsem(&nfsi->rwsem); \
        } while(0)
 #define register_nfs4fs() register_filesystem(&nfs4_fs_type)
 #define unregister_nfs4fs() unregister_filesystem(&nfs4_fs_type)
 #else
-#define nfs4_zero_state(nfsi) \
+#define nfs4_init_once(nfsi) \
        do { } while (0)
 #define register_nfs4fs() (0)
 #define unregister_nfs4fs()
@@ -1780,8 +1866,6 @@ static struct inode *nfs_alloc_inode(struct super_block *sb)
        if (!nfsi)
                return NULL;
        nfsi->flags = 0;
-       nfsi->mm_cred = NULL;
-       nfs4_zero_state(nfsi);
        return &nfsi->vfs_inode;
 }
 
@@ -1797,14 +1881,17 @@ static void init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
        if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
            SLAB_CTOR_CONSTRUCTOR) {
                inode_init_once(&nfsi->vfs_inode);
+               spin_lock_init(&nfsi->req_lock);
                INIT_LIST_HEAD(&nfsi->dirty);
                INIT_LIST_HEAD(&nfsi->commit);
+               INIT_LIST_HEAD(&nfsi->open_files);
                INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC);
                atomic_set(&nfsi->data_updates, 0);
                nfsi->ndirty = 0;
                nfsi->ncommit = 0;
                nfsi->npages = 0;
                init_waitqueue_head(&nfsi->nfs_i_wait);
+               nfs4_init_once(nfsi);
        }
 }
  
@@ -1812,7 +1899,7 @@ int nfs_init_inodecache(void)
 {
        nfs_inode_cachep = kmem_cache_create("nfs_inode_cache",
                                             sizeof(struct nfs_inode),
-                                            0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT,
+                                            0, SLAB_RECLAIM_ACCOUNT,
                                             init_once, NULL);
        if (nfs_inode_cachep == NULL)
                return -ENOMEM;