vfs: trivial __d_lookup_rcu() cleanups
[linux-2.6.git] / fs / nfs / inode.c
index 443772d..f649fba 100644 (file)
@@ -36,6 +36,9 @@
 #include <linux/vfs.h>
 #include <linux/inet.h>
 #include <linux/nfs_xdr.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/freezer.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include "internal.h"
 #include "fscache.h"
 #include "dns_resolve.h"
+#include "pnfs.h"
 
 #define NFSDBG_FACILITY                NFSDBG_VFS
 
 #define NFS_64_BIT_INODE_NUMBERS_ENABLED       1
 
 /* Default is to see 64-bit inode numbers */
-static int enable_ino64 = NFS_64_BIT_INODE_NUMBERS_ENABLED;
+static bool enable_ino64 = NFS_64_BIT_INODE_NUMBERS_ENABLED;
 
 static void nfs_invalidate_inode(struct inode *);
 static int nfs_update_inode(struct inode *, struct nfs_fattr *);
@@ -74,7 +78,7 @@ int nfs_wait_bit_killable(void *word)
 {
        if (fatal_signal_pending(current))
                return -ERESTARTSYS;
-       schedule();
+       freezable_schedule();
        return 0;
 }
 
@@ -87,7 +91,11 @@ int nfs_wait_bit_killable(void *word)
  */
 u64 nfs_compat_user_ino64(u64 fileid)
 {
-       int ino;
+#ifdef CONFIG_COMPAT
+       compat_ulong_t ino;
+#else  
+       unsigned long ino;
+#endif
 
        if (enable_ino64)
                return fileid;
@@ -97,7 +105,7 @@ u64 nfs_compat_user_ino64(u64 fileid)
        return ino;
 }
 
-void nfs_clear_inode(struct inode *inode)
+static void nfs_clear_inode(struct inode *inode)
 {
        /*
         * The following should never happen...
@@ -109,21 +117,24 @@ void nfs_clear_inode(struct inode *inode)
        nfs_fscache_release_inode_cookie(inode);
 }
 
+void nfs_evict_inode(struct inode *inode)
+{
+       truncate_inode_pages(&inode->i_data, 0);
+       end_writeback(inode);
+       nfs_clear_inode(inode);
+}
+
 /**
  * nfs_sync_mapping - helper to flush all mmapped dirty data to disk
  */
 int nfs_sync_mapping(struct address_space *mapping)
 {
-       int ret;
+       int ret = 0;
 
-       if (mapping->nrpages == 0)
-               return 0;
-       unmap_mapping_range(mapping, 0, 0, 0);
-       ret = filemap_write_and_wait(mapping);
-       if (ret != 0)
-               goto out;
-       ret = nfs_wb_all(mapping->host);
-out:
+       if (mapping->nrpages != 0) {
+               unmap_mapping_range(mapping, 0, 0, 0);
+               ret = nfs_wb_all(mapping->host);
+       }
        return ret;
 }
 
@@ -230,9 +241,6 @@ nfs_init_locked(struct inode *inode, void *opaque)
        return 0;
 }
 
-/* Don't use READDIRPLUS on directories that we believe are too large */
-#define NFS_LIMIT_READDIRPLUS (8*PAGE_SIZE)
-
 /*
  * This is our front-end to iget that looks up inodes by file handle
  * instead of inode number.
@@ -247,7 +255,10 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
        struct inode *inode = ERR_PTR(-ENOENT);
        unsigned long hash;
 
-       if ((fattr->valid & NFS_ATTR_FATTR_FILEID) == 0)
+       nfs_attr_check_mountpoint(sb, fattr);
+
+       if (((fattr->valid & NFS_ATTR_FATTR_FILEID) == 0) &&
+           !nfs_attr_use_mounted_on_fileid(fattr))
                goto out_no_inode;
        if ((fattr->valid & NFS_ATTR_FATTR_TYPE) == 0)
                goto out_no_inode;
@@ -281,24 +292,24 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                 */
                inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->file_inode_ops;
                if (S_ISREG(inode->i_mode)) {
-                       inode->i_fop = &nfs_file_operations;
+                       inode->i_fop = NFS_SB(sb)->nfs_client->rpc_ops->file_ops;
                        inode->i_data.a_ops = &nfs_file_aops;
                        inode->i_data.backing_dev_info = &NFS_SB(sb)->backing_dev_info;
                } else if (S_ISDIR(inode->i_mode)) {
                        inode->i_op = NFS_SB(sb)->nfs_client->rpc_ops->dir_inode_ops;
                        inode->i_fop = &nfs_dir_operations;
-                       if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS)
-                           && fattr->size <= NFS_LIMIT_READDIRPLUS)
+                       inode->i_data.a_ops = &nfs_dir_aops;
+                       if (nfs_server_capable(inode, NFS_CAP_READDIRPLUS))
                                set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
                        /* Deal with crossing mountpoints */
-                       if ((fattr->valid & NFS_ATTR_FATTR_FSID)
-                                       && !nfs_fsid_equal(&NFS_SB(sb)->fsid, &fattr->fsid)) {
+                       if (fattr->valid & NFS_ATTR_FATTR_MOUNTPOINT ||
+                                       fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) {
                                if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL)
                                        inode->i_op = &nfs_referral_inode_operations;
                                else
                                        inode->i_op = &nfs_mountpoint_inode_operations;
                                inode->i_fop = NULL;
-                               set_bit(NFS_INO_MOUNTPOINT, &nfsi->flags);
+                               inode->i_flags |= S_AUTOMOUNT;
                        }
                } else if (S_ISLNK(inode->i_mode))
                        inode->i_op = &nfs_symlink_inode_operations;
@@ -308,9 +319,9 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                memset(&inode->i_atime, 0, sizeof(inode->i_atime));
                memset(&inode->i_mtime, 0, sizeof(inode->i_mtime));
                memset(&inode->i_ctime, 0, sizeof(inode->i_ctime));
-               nfsi->change_attr = 0;
+               inode->i_version = 0;
                inode->i_size = 0;
-               inode->i_nlink = 0;
+               clear_nlink(inode);
                inode->i_uid = -2;
                inode->i_gid = -2;
                inode->i_blocks = 0;
@@ -334,7 +345,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                                | NFS_INO_INVALID_ACCESS
                                | NFS_INO_INVALID_ACL;
                if (fattr->valid & NFS_ATTR_FATTR_CHANGE)
-                       nfsi->change_attr = fattr->change_attr;
+                       inode->i_version = fattr->change_attr;
                else if (nfs_server_capable(inode, NFS_CAP_CHANGE_ATTR))
                        nfsi->cache_validity |= NFS_INO_INVALID_ATTR
                                | NFS_INO_INVALID_DATA;
@@ -345,7 +356,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
                                | NFS_INO_INVALID_DATA
                                | NFS_INO_REVAL_PAGECACHE;
                if (fattr->valid & NFS_ATTR_FATTR_NLINK)
-                       inode->i_nlink = fattr->nlink;
+                       set_nlink(inode, fattr->nlink);
                else if (nfs_server_capable(inode, NFS_CAP_NLINK))
                        nfsi->cache_validity |= NFS_INO_INVALID_ATTR;
                if (fattr->valid & NFS_ATTR_FATTR_OWNER)
@@ -396,8 +407,8 @@ int
 nfs_setattr(struct dentry *dentry, struct iattr *attr)
 {
        struct inode *inode = dentry->d_inode;
-       struct nfs_fattr fattr;
-       int error;
+       struct nfs_fattr *fattr;
+       int error = -ENOMEM;
 
        nfs_inc_stats(inode, NFSIOS_VFSSETATTR);
 
@@ -416,18 +427,22 @@ nfs_setattr(struct dentry *dentry, struct iattr *attr)
                return 0;
 
        /* Write all dirty data */
-       if (S_ISREG(inode->i_mode)) {
-               filemap_write_and_wait(inode->i_mapping);
+       if (S_ISREG(inode->i_mode))
                nfs_wb_all(inode);
-       }
+
+       fattr = nfs_alloc_fattr();
+       if (fattr == NULL)
+               goto out;
        /*
         * Return any delegations if we're going to change ACLs
         */
        if ((attr->ia_valid & (ATTR_MODE|ATTR_UID|ATTR_GID)) != 0)
                nfs_inode_return_delegation(inode);
-       error = NFS_PROTO(inode)->setattr(dentry, &fattr, attr);
+       error = NFS_PROTO(inode)->setattr(dentry, fattr, attr);
        if (error == 0)
-               nfs_refresh_inode(inode, &fattr);
+               nfs_refresh_inode(inode, fattr);
+       nfs_free_fattr(fattr);
+out:
        return error;
 }
 
@@ -527,6 +542,68 @@ out:
        return err;
 }
 
+static void nfs_init_lock_context(struct nfs_lock_context *l_ctx)
+{
+       atomic_set(&l_ctx->count, 1);
+       l_ctx->lockowner = current->files;
+       l_ctx->pid = current->tgid;
+       INIT_LIST_HEAD(&l_ctx->list);
+}
+
+static struct nfs_lock_context *__nfs_find_lock_context(struct nfs_open_context *ctx)
+{
+       struct nfs_lock_context *pos;
+
+       list_for_each_entry(pos, &ctx->lock_context.list, list) {
+               if (pos->lockowner != current->files)
+                       continue;
+               if (pos->pid != current->tgid)
+                       continue;
+               atomic_inc(&pos->count);
+               return pos;
+       }
+       return NULL;
+}
+
+struct nfs_lock_context *nfs_get_lock_context(struct nfs_open_context *ctx)
+{
+       struct nfs_lock_context *res, *new = NULL;
+       struct inode *inode = ctx->dentry->d_inode;
+
+       spin_lock(&inode->i_lock);
+       res = __nfs_find_lock_context(ctx);
+       if (res == NULL) {
+               spin_unlock(&inode->i_lock);
+               new = kmalloc(sizeof(*new), GFP_KERNEL);
+               if (new == NULL)
+                       return NULL;
+               nfs_init_lock_context(new);
+               spin_lock(&inode->i_lock);
+               res = __nfs_find_lock_context(ctx);
+               if (res == NULL) {
+                       list_add_tail(&new->list, &ctx->lock_context.list);
+                       new->open_context = ctx;
+                       res = new;
+                       new = NULL;
+               }
+       }
+       spin_unlock(&inode->i_lock);
+       kfree(new);
+       return res;
+}
+
+void nfs_put_lock_context(struct nfs_lock_context *l_ctx)
+{
+       struct nfs_open_context *ctx = l_ctx->open_context;
+       struct inode *inode = ctx->dentry->d_inode;
+
+       if (!atomic_dec_and_lock(&l_ctx->count, &inode->i_lock))
+               return;
+       list_del(&l_ctx->list);
+       spin_unlock(&inode->i_lock);
+       kfree(l_ctx);
+}
+
 /**
  * nfs_close_context - Common close_context() routine NFSv2/v3
  * @ctx: pointer to context
@@ -544,7 +621,7 @@ void nfs_close_context(struct nfs_open_context *ctx, int is_sync)
                return;
        if (!is_sync)
                return;
-       inode = ctx->path.dentry->d_inode;
+       inode = ctx->dentry->d_inode;
        if (!list_empty(&NFS_I(inode)->open_files))
                return;
        server = NFS_SERVER(inode);
@@ -553,44 +630,56 @@ void nfs_close_context(struct nfs_open_context *ctx, int is_sync)
        nfs_revalidate_inode(server, inode);
 }
 
-static struct nfs_open_context *alloc_nfs_open_context(struct path *path, struct rpc_cred *cred)
+struct nfs_open_context *alloc_nfs_open_context(struct dentry *dentry, fmode_t f_mode)
 {
        struct nfs_open_context *ctx;
+       struct rpc_cred *cred = rpc_lookup_cred();
+       if (IS_ERR(cred))
+               return ERR_CAST(cred);
 
        ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
-       if (ctx != NULL) {
-               ctx->path = *path;
-               path_get(&ctx->path);
-               ctx->cred = get_rpccred(cred);
-               ctx->state = NULL;
-               ctx->lockowner = current->files;
-               ctx->flags = 0;
-               ctx->error = 0;
-               ctx->dir_cookie = 0;
-               atomic_set(&ctx->count, 1);
+       if (!ctx) {
+               put_rpccred(cred);
+               return ERR_PTR(-ENOMEM);
        }
+       nfs_sb_active(dentry->d_sb);
+       ctx->dentry = dget(dentry);
+       ctx->cred = cred;
+       ctx->state = NULL;
+       ctx->mode = f_mode;
+       ctx->flags = 0;
+       ctx->error = 0;
+       nfs_init_lock_context(&ctx->lock_context);
+       ctx->lock_context.open_context = ctx;
+       INIT_LIST_HEAD(&ctx->list);
        return ctx;
 }
 
 struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx)
 {
        if (ctx != NULL)
-               atomic_inc(&ctx->count);
+               atomic_inc(&ctx->lock_context.count);
        return ctx;
 }
 
 static void __put_nfs_open_context(struct nfs_open_context *ctx, int is_sync)
 {
-       struct inode *inode = ctx->path.dentry->d_inode;
+       struct inode *inode = ctx->dentry->d_inode;
+       struct super_block *sb = ctx->dentry->d_sb;
 
-       if (!atomic_dec_and_lock(&ctx->count, &inode->i_lock))
+       if (!list_empty(&ctx->list)) {
+               if (!atomic_dec_and_lock(&ctx->lock_context.count, &inode->i_lock))
+                       return;
+               list_del(&ctx->list);
+               spin_unlock(&inode->i_lock);
+       } else if (!atomic_dec_and_test(&ctx->lock_context.count))
                return;
-       list_del(&ctx->list);
-       spin_unlock(&inode->i_lock);
-       NFS_PROTO(inode)->close_context(ctx, is_sync);
+       if (inode != NULL)
+               NFS_PROTO(inode)->close_context(ctx, is_sync);
        if (ctx->cred != NULL)
                put_rpccred(ctx->cred);
-       path_put(&ctx->path);
+       dput(ctx->dentry);
+       nfs_sb_deactive(sb);
        kfree(ctx);
 }
 
@@ -599,16 +688,11 @@ void put_nfs_open_context(struct nfs_open_context *ctx)
        __put_nfs_open_context(ctx, 0);
 }
 
-static void put_nfs_open_context_sync(struct nfs_open_context *ctx)
-{
-       __put_nfs_open_context(ctx, 1);
-}
-
 /*
  * Ensure that mmap has a recent RPC credential for use when writing out
  * shared pages
  */
-static void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx)
+void nfs_file_set_open_context(struct file *filp, struct nfs_open_context *ctx)
 {
        struct inode *inode = filp->f_path.dentry->d_inode;
        struct nfs_inode *nfsi = NFS_I(inode);
@@ -631,10 +715,10 @@ struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_c
        list_for_each_entry(pos, &nfsi->open_files, list) {
                if (cred != NULL && pos->cred != cred)
                        continue;
-               if ((pos->mode & mode) == mode) {
-                       ctx = get_nfs_open_context(pos);
-                       break;
-               }
+               if ((pos->mode & (FMODE_READ|FMODE_WRITE)) != mode)
+                       continue;
+               ctx = get_nfs_open_context(pos);
+               break;
        }
        spin_unlock(&inode->i_lock);
        return ctx;
@@ -650,7 +734,7 @@ static void nfs_file_clear_open_context(struct file *filp)
                spin_lock(&inode->i_lock);
                list_move_tail(&ctx->list, &NFS_I(inode)->open_files);
                spin_unlock(&inode->i_lock);
-               put_nfs_open_context_sync(ctx);
+               __put_nfs_open_context(ctx, filp->f_flags & O_DIRECT ? 0 : 1);
        }
 }
 
@@ -660,16 +744,10 @@ static void nfs_file_clear_open_context(struct file *filp)
 int nfs_open(struct inode *inode, struct file *filp)
 {
        struct nfs_open_context *ctx;
-       struct rpc_cred *cred;
 
-       cred = rpc_lookup_cred();
-       if (IS_ERR(cred))
-               return PTR_ERR(cred);
-       ctx = alloc_nfs_open_context(&filp->f_path, cred);
-       put_rpccred(cred);
-       if (ctx == NULL)
-               return -ENOMEM;
-       ctx->mode = filp->f_mode;
+       ctx = alloc_nfs_open_context(filp->f_path.dentry, filp->f_mode);
+       if (IS_ERR(ctx))
+               return PTR_ERR(ctx);
        nfs_file_set_open_context(filp, ctx);
        put_nfs_open_context(ctx);
        nfs_fscache_set_inode_cookie(inode, filp);
@@ -690,7 +768,7 @@ int
 __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
 {
        int              status = -ESTALE;
-       struct nfs_fattr fattr;
+       struct nfs_fattr *fattr = NULL;
        struct nfs_inode *nfsi = NFS_I(inode);
 
        dfprintk(PAGECACHE, "NFS: revalidating (%s/%Ld)\n",
@@ -701,8 +779,13 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
        if (NFS_STALE(inode))
                goto out;
 
+       status = -ENOMEM;
+       fattr = nfs_alloc_fattr();
+       if (fattr == NULL)
+               goto out;
+
        nfs_inc_stats(inode, NFSIOS_INODEREVALIDATE);
-       status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), &fattr);
+       status = NFS_PROTO(inode)->getattr(server, NFS_FH(inode), fattr);
        if (status != 0) {
                dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) getattr failed, error=%d\n",
                         inode->i_sb->s_id,
@@ -715,7 +798,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
                goto out;
        }
 
-       status = nfs_refresh_inode(inode, &fattr);
+       status = nfs_refresh_inode(inode, fattr);
        if (status) {
                dfprintk(PAGECACHE, "nfs_revalidate_inode: (%s/%Ld) refresh failed, error=%d\n",
                         inode->i_sb->s_id,
@@ -731,6 +814,7 @@ __nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
                (long long)NFS_FILEID(inode));
 
  out:
+       nfs_free_fattr(fattr);
        return status;
 }
 
@@ -738,11 +822,16 @@ 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_in_range_open(jiffies, nfsi->read_cache_jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo);
 }
 
+static int nfs_attribute_cache_expired(struct inode *inode)
+{
+       if (nfs_have_delegated_attributes(inode))
+               return 0;
+       return nfs_attribute_timeout(inode);
+}
+
 /**
  * nfs_revalidate_inode - Revalidate the inode attributes
  * @server - pointer to nfs_server struct
@@ -753,12 +842,12 @@ int nfs_attribute_timeout(struct inode *inode)
 int nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
 {
        if (!(NFS_I(inode)->cache_validity & NFS_INO_INVALID_ATTR)
-                       && !nfs_attribute_timeout(inode))
+                       && !nfs_attribute_cache_expired(inode))
                return NFS_STALE(inode) ? -ESTALE : 0;
        return __nfs_revalidate_inode(server, inode);
 }
 
-static int nfs_invalidate_mapping_nolock(struct inode *inode, struct address_space *mapping)
+static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
        
@@ -779,49 +868,10 @@ static int nfs_invalidate_mapping_nolock(struct inode *inode, struct address_spa
        return 0;
 }
 
-static int nfs_invalidate_mapping(struct inode *inode, struct address_space *mapping)
-{
-       int ret = 0;
-
-       mutex_lock(&inode->i_mutex);
-       if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_DATA) {
-               ret = nfs_sync_mapping(mapping);
-               if (ret == 0)
-                       ret = nfs_invalidate_mapping_nolock(inode, mapping);
-       }
-       mutex_unlock(&inode->i_mutex);
-       return ret;
-}
-
-/**
- * nfs_revalidate_mapping_nolock - Revalidate the pagecache
- * @inode - pointer to host inode
- * @mapping - pointer to mapping
- */
-int nfs_revalidate_mapping_nolock(struct inode *inode, struct address_space *mapping)
-{
-       struct nfs_inode *nfsi = NFS_I(inode);
-       int ret = 0;
-
-       if ((nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE)
-                       || nfs_attribute_timeout(inode) || NFS_STALE(inode)) {
-               ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
-               if (ret < 0)
-                       goto out;
-       }
-       if (nfsi->cache_validity & NFS_INO_INVALID_DATA)
-               ret = nfs_invalidate_mapping_nolock(inode, mapping);
-out:
-       return ret;
-}
-
 /**
  * nfs_revalidate_mapping - Revalidate the pagecache
  * @inode - pointer to host inode
  * @mapping - pointer to mapping
- *
- * This version of the function will take the inode->i_mutex and attempt to
- * flush out all dirty data if it needs to invalidate the page cache.
  */
 int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
 {
@@ -829,7 +879,8 @@ int nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping)
        int ret = 0;
 
        if ((nfsi->cache_validity & NFS_INO_REVAL_PAGECACHE)
-                       || nfs_attribute_timeout(inode) || NFS_STALE(inode)) {
+                       || nfs_attribute_cache_expired(inode)
+                       || NFS_STALE(inode)) {
                ret = __nfs_revalidate_inode(NFS_SERVER(inode), inode);
                if (ret < 0)
                        goto out;
@@ -840,35 +891,43 @@ out:
        return ret;
 }
 
-static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
+static unsigned long nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
+       unsigned long ret = 0;
 
        if ((fattr->valid & NFS_ATTR_FATTR_PRECHANGE)
                        && (fattr->valid & NFS_ATTR_FATTR_CHANGE)
-                       && nfsi->change_attr == fattr->pre_change_attr) {
-               nfsi->change_attr = fattr->change_attr;
+                       && inode->i_version == fattr->pre_change_attr) {
+               inode->i_version = fattr->change_attr;
                if (S_ISDIR(inode->i_mode))
                        nfsi->cache_validity |= NFS_INO_INVALID_DATA;
+               ret |= NFS_INO_INVALID_ATTR;
        }
        /* If we have atomic WCC data, we may update some attributes */
        if ((fattr->valid & NFS_ATTR_FATTR_PRECTIME)
                        && (fattr->valid & NFS_ATTR_FATTR_CTIME)
-                       && timespec_equal(&inode->i_ctime, &fattr->pre_ctime))
-                       memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
+                       && timespec_equal(&inode->i_ctime, &fattr->pre_ctime)) {
+               memcpy(&inode->i_ctime, &fattr->ctime, sizeof(inode->i_ctime));
+               ret |= NFS_INO_INVALID_ATTR;
+       }
 
        if ((fattr->valid & NFS_ATTR_FATTR_PREMTIME)
                        && (fattr->valid & NFS_ATTR_FATTR_MTIME)
                        && timespec_equal(&inode->i_mtime, &fattr->pre_mtime)) {
-                       memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
-                       if (S_ISDIR(inode->i_mode))
-                               nfsi->cache_validity |= NFS_INO_INVALID_DATA;
+               memcpy(&inode->i_mtime, &fattr->mtime, sizeof(inode->i_mtime));
+               if (S_ISDIR(inode->i_mode))
+                       nfsi->cache_validity |= NFS_INO_INVALID_DATA;
+               ret |= NFS_INO_INVALID_ATTR;
        }
        if ((fattr->valid & NFS_ATTR_FATTR_PRESIZE)
                        && (fattr->valid & NFS_ATTR_FATTR_SIZE)
                        && i_size_read(inode) == nfs_size_to_loff_t(fattr->pre_size)
-                       && nfsi->npages == 0)
-                       i_size_write(inode, nfs_size_to_loff_t(fattr->size));
+                       && nfsi->npages == 0) {
+               i_size_write(inode, nfs_size_to_loff_t(fattr->size));
+               ret |= NFS_INO_INVALID_ATTR;
+       }
+       return ret;
 }
 
 /**
@@ -894,7 +953,7 @@ static int nfs_check_inode_attributes(struct inode *inode, struct nfs_fattr *fat
                return -EIO;
 
        if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) != 0 &&
-                       nfsi->change_attr != fattr->change_attr)
+                       inode->i_version != fattr->change_attr)
                invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
 
        /* Verify a few of the more important attributes */
@@ -961,6 +1020,28 @@ void nfs_fattr_init(struct nfs_fattr *fattr)
        fattr->valid = 0;
        fattr->time_start = jiffies;
        fattr->gencount = nfs_inc_attr_generation_counter();
+       fattr->owner_name = NULL;
+       fattr->group_name = NULL;
+}
+
+struct nfs_fattr *nfs_alloc_fattr(void)
+{
+       struct nfs_fattr *fattr;
+
+       fattr = kmalloc(sizeof(*fattr), GFP_NOFS);
+       if (fattr != NULL)
+               nfs_fattr_init(fattr);
+       return fattr;
+}
+
+struct nfs_fh *nfs_alloc_fhandle(void)
+{
+       struct nfs_fh *fh;
+
+       fh = kmalloc(sizeof(struct nfs_fh), GFP_NOFS);
+       if (fh != NULL)
+               fh->size = 0;
+       return fh;
 }
 
 /**
@@ -1085,7 +1166,7 @@ int nfs_post_op_update_inode_force_wcc(struct inode *inode, struct nfs_fattr *fa
        }
        if ((fattr->valid & NFS_ATTR_FATTR_CHANGE) != 0 &&
                        (fattr->valid & NFS_ATTR_FATTR_PRECHANGE) == 0) {
-               fattr->pre_change_attr = NFS_I(inode)->change_attr;
+               fattr->pre_change_attr = inode->i_version;
                fattr->valid |= NFS_ATTR_FATTR_PRECHANGE;
        }
        if ((fattr->valid & NFS_ATTR_FATTR_CTIME) != 0 &&
@@ -1147,7 +1228,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
        /* Update the fsid? */
        if (S_ISDIR(inode->i_mode) && (fattr->valid & NFS_ATTR_FATTR_FSID) &&
                        !nfs_fsid_equal(&server->fsid, &fattr->fsid) &&
-                       !test_bit(NFS_INO_MOUNTPOINT, &nfsi->flags))
+                       !IS_AUTOMOUNT(inode))
                server->fsid = fattr->fsid;
 
        /*
@@ -1162,17 +1243,17 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                        | NFS_INO_REVAL_PAGECACHE);
 
        /* Do atomic weak cache consistency updates */
-       nfs_wcc_update_inode(inode, fattr);
+       invalid |= nfs_wcc_update_inode(inode, fattr);
 
        /* More cache consistency checks */
        if (fattr->valid & NFS_ATTR_FATTR_CHANGE) {
-               if (nfsi->change_attr != fattr->change_attr) {
+               if (inode->i_version != fattr->change_attr) {
                        dprintk("NFS: change_attr change on server for file %s/%ld\n",
                                        inode->i_sb->s_id, inode->i_ino);
                        invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
                        if (S_ISDIR(inode->i_mode))
                                nfs_force_lookup_revalidate(inode);
-                       nfsi->change_attr = fattr->change_attr;
+                       inode->i_version = fattr->change_attr;
                }
        } else if (server->caps & NFS_CAP_CHANGE_ATTR)
                invalid |= save_cache_validity;
@@ -1219,12 +1300,17 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                if (new_isize != cur_isize) {
                        /* Do we perhaps have any outstanding writes, or has
                         * the file grown beyond our last write? */
-                       if (nfsi->npages == 0 || new_isize > cur_isize) {
+                       if ((nfsi->npages == 0 && !test_bit(NFS_INO_LAYOUTCOMMIT, &nfsi->flags)) ||
+                            new_isize > cur_isize) {
                                i_size_write(inode, new_isize);
                                invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
                        }
-                       dprintk("NFS: isize change on server for file %s/%ld\n",
-                                       inode->i_sb->s_id, inode->i_ino);
+                       dprintk("NFS: isize change on server for file %s/%ld "
+                                       "(%Ld to %Ld)\n",
+                                       inode->i_sb->s_id,
+                                       inode->i_ino,
+                                       (long long)cur_isize,
+                                       (long long)new_isize);
                }
        } else
                invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
@@ -1278,7 +1364,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                        invalid |= NFS_INO_INVALID_ATTR;
                        if (S_ISDIR(inode->i_mode))
                                invalid |= NFS_INO_INVALID_DATA;
-                       inode->i_nlink = fattr->nlink;
+                       set_nlink(inode, fattr->nlink);
                }
        } else if (server->caps & NFS_CAP_NLINK)
                invalid |= save_cache_validity & (NFS_INO_INVALID_ATTR
@@ -1347,8 +1433,12 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
  * to open() calls that passed nfs_atomic_lookup, but failed to call
  * nfs_open().
  */
-void nfs4_clear_inode(struct inode *inode)
+void nfs4_evict_inode(struct inode *inode)
 {
+       truncate_inode_pages(&inode->i_data, 0);
+       end_writeback(inode);
+       pnfs_return_layout(inode);
+       pnfs_destroy_layout(NFS_I(inode));
        /* If we are holding a delegation, return it! */
        nfs_inode_return_delegation_noreclaim(inode);
        /* First call standard NFS clear_inode() code */
@@ -1374,11 +1464,17 @@ struct inode *nfs_alloc_inode(struct super_block *sb)
        return &nfsi->vfs_inode;
 }
 
-void nfs_destroy_inode(struct inode *inode)
+static void nfs_i_callback(struct rcu_head *head)
 {
+       struct inode *inode = container_of(head, struct inode, i_rcu);
        kmem_cache_free(nfs_inode_cachep, NFS_I(inode));
 }
 
+void nfs_destroy_inode(struct inode *inode)
+{
+       call_rcu(&inode->i_rcu, nfs_i_callback);
+}
+
 static inline void nfs4_init_once(struct nfs_inode *nfsi)
 {
 #ifdef CONFIG_NFS_V4
@@ -1386,6 +1482,8 @@ static inline void nfs4_init_once(struct nfs_inode *nfsi)
        nfsi->delegation = NULL;
        nfsi->delegation_state = 0;
        init_rwsem(&nfsi->rwsem);
+       nfsi->layout = NULL;
+       atomic_set(&nfsi->commits_outstanding, 0);
 #endif
 }
 
@@ -1433,7 +1531,7 @@ static int nfsiod_start(void)
 {
        struct workqueue_struct *wq;
        dprintk("RPC:       creating workqueue nfsiod\n");
-       wq = create_singlethread_workqueue("nfsiod");
+       wq = alloc_workqueue("nfsiod", WQ_MEM_RECLAIM, 0);
        if (wq == NULL)
                return -ENOMEM;
        nfsiod_workqueue = wq;
@@ -1461,6 +1559,10 @@ static int __init init_nfs_fs(void)
 {
        int err;
 
+       err = nfs_idmap_init();
+       if (err < 0)
+               goto out9;
+
        err = nfs_dns_resolver_init();
        if (err < 0)
                goto out8;
@@ -1525,6 +1627,8 @@ out6:
 out7:
        nfs_dns_resolver_destroy();
 out8:
+       nfs_idmap_quit();
+out9:
        return err;
 }
 
@@ -1537,9 +1641,11 @@ static void __exit exit_nfs_fs(void)
        nfs_destroy_nfspagecache();
        nfs_fscache_unregister();
        nfs_dns_resolver_destroy();
+       nfs_idmap_quit();
 #ifdef CONFIG_PROC_FS
        rpc_proc_unregister("nfs");
 #endif
+       nfs_cleanup_cb_ident_idr();
        unregister_nfs_fs();
        nfs_fs_proc_exit();
        nfsiod_stop();