drop unused dentry argument to ->fsync
[safe/jmp/linux-2.6] / fs / nfs / dir.c
index e275a6e..782b431 100644 (file)
@@ -29,7 +29,6 @@
 #include <linux/nfs_fs.h>
 #include <linux/nfs_mount.h>
 #include <linux/pagemap.h>
-#include <linux/smp_lock.h>
 #include <linux/pagevec.h>
 #include <linux/namei.h>
 #include <linux/mount.h>
@@ -38,6 +37,7 @@
 #include "nfs4_fs.h"
 #include "delegation.h"
 #include "iostat.h"
+#include "internal.h"
 
 /* #define NFS_DEBUG_VERBOSE 1 */
 
@@ -53,7 +53,7 @@ static int nfs_link(struct dentry *, struct inode *, struct dentry *);
 static int nfs_mknod(struct inode *, struct dentry *, int, dev_t);
 static int nfs_rename(struct inode *, struct dentry *,
                      struct inode *, struct dentry *);
-static int nfs_fsync_dir(struct file *, struct dentry *, int);
+static int nfs_fsync_dir(struct file *, int);
 static loff_t nfs_llseek_dir(struct file *, loff_t, int);
 
 const struct file_operations nfs_dir_operations = {
@@ -132,13 +132,14 @@ nfs_opendir(struct inode *inode, struct file *filp)
 {
        int res;
 
-       dfprintk(VFS, "NFS: opendir(%s/%ld)\n",
-                       inode->i_sb->s_id, inode->i_ino);
+       dfprintk(FILE, "NFS: open dir(%s/%s)\n",
+                       filp->f_path.dentry->d_parent->d_name.name,
+                       filp->f_path.dentry->d_name.name);
+
+       nfs_inc_stats(inode, NFSIOS_VFSOPEN);
 
-       lock_kernel();
        /* Call generic open code in order to cache credentials */
        res = nfs_open(inode, filp);
-       unlock_kernel();
        return res;
 }
 
@@ -153,8 +154,8 @@ typedef struct {
        struct nfs_entry *entry;
        decode_dirent_t decode;
        int             plus;
-       int             error;
        unsigned long   timestamp;
+       unsigned long   gencount;
        int             timestamp_valid;
 } nfs_readdir_descriptor_t;
 
@@ -176,45 +177,44 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page)
        struct file     *file = desc->file;
        struct inode    *inode = file->f_path.dentry->d_inode;
        struct rpc_cred *cred = nfs_file_cred(file);
-       unsigned long   timestamp;
+       unsigned long   timestamp, gencount;
        int             error;
 
        dfprintk(DIRCACHE, "NFS: %s: reading cookie %Lu into page %lu\n",
-                       __FUNCTION__, (long long)desc->entry->cookie,
+                       __func__, (long long)desc->entry->cookie,
                        page->index);
 
  again:
        timestamp = jiffies;
+       gencount = nfs_inc_attr_generation_counter();
        error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, desc->entry->cookie, page,
                                          NFS_SERVER(inode)->dtsize, desc->plus);
        if (error < 0) {
                /* We requested READDIRPLUS, but the server doesn't grok it */
                if (error == -ENOTSUPP && desc->plus) {
                        NFS_SERVER(inode)->caps &= ~NFS_CAP_READDIRPLUS;
-                       clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_FLAGS(inode));
+                       clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
                        desc->plus = 0;
                        goto again;
                }
                goto error;
        }
        desc->timestamp = timestamp;
+       desc->gencount = gencount;
        desc->timestamp_valid = 1;
        SetPageUptodate(page);
        /* Ensure consistent page alignment of the data.
         * Note: assumes we have exclusive access to this mapping either
         *       through inode->i_mutex or some other mechanism.
         */
-       if (page->index == 0 && invalidate_inode_pages2_range(inode->i_mapping, PAGE_CACHE_SIZE, -1) < 0) {
+       if (invalidate_inode_pages2_range(inode->i_mapping, page->index + 1, -1) < 0) {
                /* Should never happen */
                nfs_zap_mapping(inode, inode->i_mapping);
        }
        unlock_page(page);
        return 0;
  error:
-       SetPageError(page);
        unlock_page(page);
-       nfs_zap_caches(inode);
-       desc->error = error;
        return -EIO;
 }
 
@@ -226,9 +226,10 @@ int dir_decode(nfs_readdir_descriptor_t *desc)
        if (IS_ERR(p))
                return PTR_ERR(p);
        desc->ptr = p;
-       if (desc->timestamp_valid)
+       if (desc->timestamp_valid) {
                desc->entry->fattr->time_start = desc->timestamp;
-       else
+               desc->entry->fattr->gencount = desc->gencount;
+       } else
                desc->entry->fattr->valid &= ~NFS_ATTR_FATTR;
        return 0;
 }
@@ -259,7 +260,7 @@ int find_dirent(nfs_readdir_descriptor_t *desc)
 
        while((status = dir_decode(desc)) == 0) {
                dfprintk(DIRCACHE, "NFS: %s: examining cookie %Lu\n",
-                               __FUNCTION__, (unsigned long long)entry->cookie);
+                               __func__, (unsigned long long)entry->cookie);
                if (entry->prev_cookie == *desc->dir_cookie)
                        break;
                if (loop_count++ > 200) {
@@ -318,7 +319,7 @@ int find_dirent_page(nfs_readdir_descriptor_t *desc)
        int             status;
 
        dfprintk(DIRCACHE, "NFS: %s: searching page %ld for target %Lu\n",
-                       __FUNCTION__, desc->page_index,
+                       __func__, desc->page_index,
                        (long long) *desc->dir_cookie);
 
        /* If we find the page in the page_cache, we cannot be sure
@@ -342,7 +343,7 @@ int find_dirent_page(nfs_readdir_descriptor_t *desc)
        if (status < 0)
                dir_page_release(desc);
  out:
-       dfprintk(DIRCACHE, "NFS: %s: returns %d\n", __FUNCTION__, status);
+       dfprintk(DIRCACHE, "NFS: %s: returns %d\n", __func__, status);
        return status;
 }
 
@@ -383,7 +384,7 @@ int readdir_search_pagecache(nfs_readdir_descriptor_t *desc)
                }
        }
 
-       dfprintk(DIRCACHE, "NFS: %s: returns %d\n", __FUNCTION__, res);
+       dfprintk(DIRCACHE, "NFS: %s: returns %d\n", __func__, res);
        return res;
 }
 
@@ -429,7 +430,8 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
                }
 
                res = filldir(dirent, entry->name, entry->len, 
-                             file->f_pos, fileid, d_type);
+                             file->f_pos, nfs_compat_user_ino64(fileid),
+                             d_type);
                if (res < 0)
                        break;
                file->f_pos++;
@@ -472,7 +474,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
        struct rpc_cred *cred = nfs_file_cred(file);
        struct page     *page = NULL;
        int             status;
-       unsigned long   timestamp;
+       unsigned long   timestamp, gencount;
 
        dfprintk(DIRCACHE, "NFS: uncached_readdir() searching for cookie %Lu\n",
                        (unsigned long long)*desc->dir_cookie);
@@ -483,14 +485,16 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
                goto out;
        }
        timestamp = jiffies;
-       desc->error = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred, *desc->dir_cookie,
-                                               page,
+       gencount = nfs_inc_attr_generation_counter();
+       status = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred,
+                                               *desc->dir_cookie, page,
                                                NFS_SERVER(inode)->dtsize,
                                                desc->plus);
        desc->page = page;
        desc->ptr = kmap(page);         /* matching kunmap in nfs_do_filldir */
-       if (desc->error >= 0) {
+       if (status >= 0) {
                desc->timestamp = timestamp;
+               desc->gencount = gencount;
                desc->timestamp_valid = 1;
                if ((status = dir_decode(desc)) == 0)
                        desc->entry->prev_cookie = *desc->dir_cookie;
@@ -508,7 +512,7 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc, void *dirent,
        desc->entry->eof = 0;
  out:
        dfprintk(DIRCACHE, "NFS: %s: returns %d\n",
-                       __FUNCTION__, status);
+                       __func__, status);
        return status;
  out_release:
        dir_page_release(desc);
@@ -526,23 +530,13 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
        nfs_readdir_descriptor_t my_desc,
                        *desc = &my_desc;
        struct nfs_entry my_entry;
-       struct nfs_fh    fh;
-       struct nfs_fattr fattr;
-       long            res;
+       int res = -ENOMEM;
 
-       dfprintk(VFS, "NFS: readdir(%s/%s) starting at cookie %Lu\n",
+       dfprintk(FILE, "NFS: readdir(%s/%s) starting at cookie %llu\n",
                        dentry->d_parent->d_name.name, dentry->d_name.name,
                        (long long)filp->f_pos);
        nfs_inc_stats(inode, NFSIOS_VFSGETDENTS);
 
-       lock_kernel();
-
-       res = nfs_revalidate_mapping_nolock(inode, filp->f_mapping);
-       if (res < 0) {
-               unlock_kernel();
-               return res;
-       }
-
        /*
         * filp->f_pos points to the dirent entry number.
         * *desc->dir_cookie has the cookie for the next entry. We have
@@ -558,11 +552,18 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 
        my_entry.cookie = my_entry.prev_cookie = 0;
        my_entry.eof = 0;
-       my_entry.fh = &fh;
-       my_entry.fattr = &fattr;
-       nfs_fattr_init(&fattr);
+       my_entry.fh = nfs_alloc_fhandle();
+       my_entry.fattr = nfs_alloc_fattr();
+       if (my_entry.fh == NULL || my_entry.fattr == NULL)
+               goto out_alloc_failed;
+
        desc->entry = &my_entry;
 
+       nfs_block_sillyrename(dentry);
+       res = nfs_revalidate_mapping(inode, filp->f_mapping);
+       if (res < 0)
+               goto out;
+
        while(!desc->entry->eof) {
                res = readdir_search_pagecache(desc);
 
@@ -578,7 +579,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
                        break;
                }
                if (res == -ETOOSMALL && desc->plus) {
-                       clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_FLAGS(inode));
+                       clear_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(inode)->flags);
                        nfs_zap_caches(inode);
                        desc->plus = 0;
                        desc->entry->eof = 0;
@@ -593,10 +594,14 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
                        break;
                }
        }
-       unlock_kernel();
+out:
+       nfs_unblock_sillyrename(dentry);
        if (res > 0)
                res = 0;
-       dfprintk(VFS, "NFS: readdir(%s/%s) returns %ld\n",
+out_alloc_failed:
+       nfs_free_fattr(my_entry.fattr);
+       nfs_free_fhandle(my_entry.fh);
+       dfprintk(FILE, "NFS: readdir(%s/%s) returns %d\n",
                        dentry->d_parent->d_name.name, dentry->d_name.name,
                        res);
        return res;
@@ -604,7 +609,15 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 
 static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin)
 {
-       mutex_lock(&filp->f_path.dentry->d_inode->i_mutex);
+       struct dentry *dentry = filp->f_path.dentry;
+       struct inode *inode = dentry->d_inode;
+
+       dfprintk(FILE, "NFS: llseek dir(%s/%s, %lld, %d)\n",
+                       dentry->d_parent->d_name.name,
+                       dentry->d_name.name,
+                       offset, origin);
+
+       mutex_lock(&inode->i_mutex);
        switch (origin) {
                case 1:
                        offset += filp->f_pos;
@@ -620,7 +633,7 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin)
                nfs_file_open_context(filp)->dir_cookie = 0;
        }
 out:
-       mutex_unlock(&filp->f_path.dentry->d_inode->i_mutex);
+       mutex_unlock(&inode->i_mutex);
        return offset;
 }
 
@@ -628,15 +641,33 @@ out:
  * All directory operations under NFS are synchronous, so fsync()
  * is a dummy operation.
  */
-static int nfs_fsync_dir(struct file *filp, struct dentry *dentry, int datasync)
+static int nfs_fsync_dir(struct file *filp, int datasync)
 {
-       dfprintk(VFS, "NFS: fsync_dir(%s/%s) datasync %d\n",
+       struct dentry *dentry = filp->f_path.dentry;
+
+       dfprintk(FILE, "NFS: fsync dir(%s/%s) datasync %d\n",
                        dentry->d_parent->d_name.name, dentry->d_name.name,
                        datasync);
 
+       nfs_inc_stats(dentry->d_inode, NFSIOS_VFSFSYNC);
        return 0;
 }
 
+/**
+ * nfs_force_lookup_revalidate - Mark the directory as having changed
+ * @dir - pointer to directory inode
+ *
+ * This forces the revalidation code in nfs_lookup_revalidate() to do a
+ * full lookup on all child dentries of 'dir' whenever a change occurs
+ * on the server that might have invalidated our dcache.
+ *
+ * The caller should be holding dir->i_lock
+ */
+void nfs_force_lookup_revalidate(struct inode *dir)
+{
+       NFS_I(dir)->cache_change_attribute++;
+}
+
 /*
  * A check for whether or not the parent directory has changed.
  * In the case it has, we assume that the dentries are untrustworthy
@@ -646,14 +677,16 @@ static int nfs_check_verifier(struct inode *dir, struct dentry *dentry)
 {
        if (IS_ROOT(dentry))
                return 1;
-       if (nfs_verify_change_attribute(dir, dentry->d_time))
-               return 1;
-       return 0;
-}
-
-static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf)
-{
-       dentry->d_time = verf;
+       if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONE)
+               return 0;
+       if (!nfs_verify_change_attribute(dir, dentry->d_time))
+               return 0;
+       /* Revalidate nfsi->cache_change_attribute before we declare a match */
+       if (nfs_revalidate_inode(NFS_SERVER(dir), dir) < 0)
+               return 0;
+       if (!nfs_verify_change_attribute(dir, dentry->d_time))
+               return 0;
+       return 1;
 }
 
 /*
@@ -671,6 +704,17 @@ static inline unsigned int nfs_lookup_check_intent(struct nameidata *nd, unsigne
 }
 
 /*
+ * Use intent information to check whether or not we're going to do
+ * an O_EXCL create using this path component.
+ */
+static int nfs_is_exclusive_create(struct inode *dir, struct nameidata *nd)
+{
+       if (NFS_PROTO(dir)->version == 2)
+               return 0;
+       return nd && nfs_lookup_check_intent(nd, LOOKUP_EXCL);
+}
+
+/*
  * Inode and filehandle revalidation for lookups.
  *
  * We force revalidation in the cases where the VFS sets LOOKUP_REVAL,
@@ -683,6 +727,8 @@ int nfs_lookup_verify_inode(struct inode *inode, struct nameidata *nd)
 {
        struct nfs_server *server = NFS_SERVER(inode);
 
+       if (test_bit(NFS_INO_MOUNTPOINT, &NFS_I(inode)->flags))
+               return 0;
        if (nd != NULL) {
                /* VFS wants an on-the-wire revalidation */
                if (nd->flags & LOOKUP_REVAL)
@@ -693,6 +739,7 @@ int nfs_lookup_verify_inode(struct inode *inode, struct nameidata *nd)
                                (S_ISREG(inode->i_mode) ||
                                 S_ISDIR(inode->i_mode)))
                        goto out_force;
+               return 0;
        }
        return nfs_revalidate_inode(server, inode);
 out_force:
@@ -713,6 +760,8 @@ int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry,
        /* Don't revalidate a negative dentry if we're creating a new file */
        if (nd != NULL && nfs_lookup_check_intent(nd, LOOKUP_CREATE) != 0)
                return 0;
+       if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONEG)
+               return 1;
        return !nfs_check_verifier(dir, dentry);
 }
 
@@ -732,21 +781,15 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
        struct inode *dir;
        struct inode *inode;
        struct dentry *parent;
+       struct nfs_fh *fhandle = NULL;
+       struct nfs_fattr *fattr = NULL;
        int error;
-       struct nfs_fh fhandle;
-       struct nfs_fattr fattr;
-       unsigned long verifier;
 
        parent = dget_parent(dentry);
-       lock_kernel();
        dir = parent->d_inode;
        nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE);
        inode = dentry->d_inode;
 
-       /* Revalidate parent directory attribute cache */
-       if (nfs_revalidate_inode(NFS_SERVER(dir), dir) < 0)
-               goto out_zap_parent;
-
        if (!inode) {
                if (nfs_neg_need_reval(dir, dentry, nd))
                        goto out_bad;
@@ -755,13 +798,16 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
 
        if (is_bad_inode(inode)) {
                dfprintk(LOOKUPCACHE, "%s: %s/%s has dud inode\n",
-                               __FUNCTION__, dentry->d_parent->d_name.name,
+                               __func__, dentry->d_parent->d_name.name,
                                dentry->d_name.name);
                goto out_bad;
        }
 
+       if (nfs_have_delegation(inode, FMODE_READ))
+               goto out_set_verifier;
+
        /* Force a full look up iff the parent directory has changed */
-       if (nfs_check_verifier(dir, dentry)) {
+       if (!nfs_is_exclusive_create(dir, nd) && nfs_check_verifier(dir, dentry)) {
                if (nfs_lookup_verify_inode(inode, nd))
                        goto out_zap_parent;
                goto out_valid;
@@ -770,42 +816,60 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
        if (NFS_STALE(inode))
                goto out_bad;
 
-       verifier = nfs_save_change_attribute(dir);
-       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
+       error = -ENOMEM;
+       fhandle = nfs_alloc_fhandle();
+       fattr = nfs_alloc_fattr();
+       if (fhandle == NULL || fattr == NULL)
+               goto out_error;
+
+       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
        if (error)
                goto out_bad;
-       if (nfs_compare_fh(NFS_FH(inode), &fhandle))
+       if (nfs_compare_fh(NFS_FH(inode), fhandle))
                goto out_bad;
-       if ((error = nfs_refresh_inode(inode, &fattr)) != 0)
+       if ((error = nfs_refresh_inode(inode, fattr)) != 0)
                goto out_bad;
 
-       nfs_set_verifier(dentry, verifier);
+       nfs_free_fattr(fattr);
+       nfs_free_fhandle(fhandle);
+out_set_verifier:
+       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
  out_valid:
-       unlock_kernel();
        dput(parent);
        dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is valid\n",
-                       __FUNCTION__, dentry->d_parent->d_name.name,
+                       __func__, dentry->d_parent->d_name.name,
                        dentry->d_name.name);
        return 1;
 out_zap_parent:
        nfs_zap_caches(dir);
  out_bad:
-       NFS_CACHEINV(dir);
+       nfs_mark_for_revalidate(dir);
        if (inode && S_ISDIR(inode->i_mode)) {
                /* Purge readdir caches. */
                nfs_zap_caches(inode);
                /* If we have submounts, don't unhash ! */
                if (have_submounts(dentry))
                        goto out_valid;
+               if (dentry->d_flags & DCACHE_DISCONNECTED)
+                       goto out_valid;
                shrink_dcache_parent(dentry);
        }
        d_drop(dentry);
-       unlock_kernel();
+       nfs_free_fattr(fattr);
+       nfs_free_fhandle(fhandle);
        dput(parent);
        dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is invalid\n",
-                       __FUNCTION__, dentry->d_parent->d_name.name,
+                       __func__, dentry->d_parent->d_name.name,
                        dentry->d_name.name);
        return 0;
+out_error:
+       nfs_free_fattr(fattr);
+       nfs_free_fhandle(fhandle);
+       dput(parent);
+       dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) lookup returned error %d\n",
+                       __func__, dentry->d_parent->d_name.name,
+                       dentry->d_name.name, error);
+       return error;
 }
 
 /*
@@ -817,6 +881,10 @@ static int nfs_dentry_delete(struct dentry *dentry)
                dentry->d_parent->d_name.name, dentry->d_name.name,
                dentry->d_flags);
 
+       /* Unhash any dentry with a stale inode */
+       if (dentry->d_inode != NULL && NFS_STALE(dentry->d_inode))
+               return 1;
+
        if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
                /* Unhash it, so that ->d_iput() would be called */
                return 1;
@@ -830,63 +898,45 @@ static int nfs_dentry_delete(struct dentry *dentry)
 
 }
 
+static void nfs_drop_nlink(struct inode *inode)
+{
+       spin_lock(&inode->i_lock);
+       if (inode->i_nlink > 0)
+               drop_nlink(inode);
+       spin_unlock(&inode->i_lock);
+}
+
 /*
  * Called when the dentry loses inode.
  * We use it to clean up silly-renamed files.
  */
 static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
 {
-       nfs_inode_return_delegation(inode);
        if (S_ISDIR(inode->i_mode))
                /* drop any readdir cache as it could easily be old */
                NFS_I(inode)->cache_validity |= NFS_INO_INVALID_DATA;
 
        if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
-               lock_kernel();
                drop_nlink(inode);
                nfs_complete_unlink(dentry, inode);
-               unlock_kernel();
        }
        iput(inode);
 }
 
-struct dentry_operations nfs_dentry_operations = {
+const struct dentry_operations nfs_dentry_operations = {
        .d_revalidate   = nfs_lookup_revalidate,
        .d_delete       = nfs_dentry_delete,
        .d_iput         = nfs_dentry_iput,
 };
 
-/*
- * Use intent information to check whether or not we're going to do
- * an O_EXCL create using this path component.
- */
-static inline
-int nfs_is_exclusive_create(struct inode *dir, struct nameidata *nd)
-{
-       if (NFS_PROTO(dir)->version == 2)
-               return 0;
-       if (nd == NULL || nfs_lookup_check_intent(nd, LOOKUP_CREATE) == 0)
-               return 0;
-       return (nd->intent.open.flags & O_EXCL) != 0;
-}
-
-static inline int nfs_reval_fsid(struct inode *dir, const struct nfs_fattr *fattr)
-{
-       struct nfs_server *server = NFS_SERVER(dir);
-
-       if (!nfs_fsid_equal(&server->fsid, &fattr->fsid))
-               /* Revalidate fsid using the parent directory */
-               return __nfs_revalidate_inode(server, dir);
-       return 0;
-}
-
 static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd)
 {
        struct dentry *res;
+       struct dentry *parent;
        struct inode *inode = NULL;
+       struct nfs_fh *fhandle = NULL;
+       struct nfs_fattr *fattr = NULL;
        int error;
-       struct nfs_fh fhandle;
-       struct nfs_fattr fattr;
 
        dfprintk(VFS, "NFS: lookup(%s/%s)\n",
                dentry->d_parent->d_name.name, dentry->d_name.name);
@@ -896,11 +946,8 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
        if (dentry->d_name.len > NFS_SERVER(dir)->namelen)
                goto out;
 
-       res = ERR_PTR(-ENOMEM);
        dentry->d_op = NFS_PROTO(dir)->dentry_ops;
 
-       lock_kernel();
-
        /*
         * If we're doing an exclusive create, optimize away the lookup
         * but don't hash the dentry.
@@ -908,50 +955,50 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
        if (nfs_is_exclusive_create(dir, nd)) {
                d_instantiate(dentry, NULL);
                res = NULL;
-               goto out_unlock;
+               goto out;
        }
 
-       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
+       res = ERR_PTR(-ENOMEM);
+       fhandle = nfs_alloc_fhandle();
+       fattr = nfs_alloc_fattr();
+       if (fhandle == NULL || fattr == NULL)
+               goto out;
+
+       parent = dentry->d_parent;
+       /* Protect against concurrent sillydeletes */
+       nfs_block_sillyrename(parent);
+       error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
        if (error == -ENOENT)
                goto no_entry;
        if (error < 0) {
                res = ERR_PTR(error);
-               goto out_unlock;
-       }
-       error = nfs_reval_fsid(dir, &fattr);
-       if (error < 0) {
-               res = ERR_PTR(error);
-               goto out_unlock;
+               goto out_unblock_sillyrename;
        }
-       inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr);
+       inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
        res = (struct dentry *)inode;
        if (IS_ERR(res))
-               goto out_unlock;
+               goto out_unblock_sillyrename;
 
 no_entry:
        res = d_materialise_unique(dentry, inode);
        if (res != NULL) {
-               struct dentry *parent;
                if (IS_ERR(res))
-                       goto out_unlock;
-               /* Was a directory renamed! */
-               parent = dget_parent(res);
-               if (!IS_ROOT(parent))
-                       nfs_mark_for_revalidate(parent->d_inode);
-               dput(parent);
+                       goto out_unblock_sillyrename;
                dentry = res;
        }
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
-out_unlock:
-       unlock_kernel();
+out_unblock_sillyrename:
+       nfs_unblock_sillyrename(parent);
 out:
+       nfs_free_fattr(fattr);
+       nfs_free_fhandle(fhandle);
        return res;
 }
 
 #ifdef CONFIG_NFS_V4
 static int nfs_open_revalidate(struct dentry *, struct nameidata *);
 
-struct dentry_operations nfs4_dentry_operations = {
+const struct dentry_operations nfs4_dentry_operations = {
        .d_revalidate   = nfs_open_revalidate,
        .d_delete       = nfs_dentry_delete,
        .d_iput         = nfs_dentry_iput,
@@ -961,7 +1008,7 @@ struct dentry_operations nfs4_dentry_operations = {
  * Use intent information to determine whether we need to substitute
  * the NFSv4-style stateful OPEN for the LOOKUP call
  */
-static int is_atomic_open(struct inode *dir, struct nameidata *nd)
+static int is_atomic_open(struct nameidata *nd)
 {
        if (nd == NULL || nfs_lookup_check_intent(nd, LOOKUP_OPEN) == 0)
                return 0;
@@ -969,7 +1016,8 @@ static int is_atomic_open(struct inode *dir, struct nameidata *nd)
        if (nd->flags & LOOKUP_DIRECTORY)
                return 0;
        /* Are we trying to write to a read only partition? */
-       if (IS_RDONLY(dir) && (nd->intent.open.flags & (O_CREAT|O_TRUNC|FMODE_WRITE)))
+       if (__mnt_is_readonly(nd->path.mnt) &&
+           (nd->intent.open.flags & (O_CREAT|O_TRUNC|FMODE_WRITE)))
                return 0;
        return 1;
 }
@@ -983,7 +1031,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
                        dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
 
        /* Check that we are indeed trying to open this file */
-       if (!is_atomic_open(dir, nd))
+       if (!is_atomic_open(nd))
                goto no_open;
 
        if (dentry->d_name.len > NFS_SERVER(dir)->namelen) {
@@ -992,29 +1040,15 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
        }
        dentry->d_op = NFS_PROTO(dir)->dentry_ops;
 
-       /* Let vfs_create() deal with O_EXCL */
-       if (nd->intent.open.flags & O_EXCL) {
-               d_add(dentry, NULL);
+       /* Let vfs_create() deal with O_EXCL. Instantiate, but don't hash
+        * the dentry. */
+       if (nd->flags & LOOKUP_EXCL) {
+               d_instantiate(dentry, NULL);
                goto out;
        }
 
        /* Open the file on the server */
-       lock_kernel();
-       /* Revalidate parent directory attribute cache */
-       error = nfs_revalidate_inode(NFS_SERVER(dir), dir);
-       if (error < 0) {
-               res = ERR_PTR(error);
-               unlock_kernel();
-               goto out;
-       }
-
-       if (nd->intent.open.flags & O_CREAT) {
-               nfs_begin_data_update(dir);
-               res = nfs4_atomic_open(dir, dentry, nd);
-               nfs_end_data_update(dir);
-       } else
-               res = nfs4_atomic_open(dir, dentry, nd);
-       unlock_kernel();
+       res = nfs4_atomic_open(dir, dentry, nd);
        if (IS_ERR(res)) {
                error = PTR_ERR(res);
                switch (error) {
@@ -1035,7 +1069,6 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
                }
        } else if (res != NULL)
                dentry = res;
-       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
 out:
        return res;
 no_open:
@@ -1047,25 +1080,28 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
        struct dentry *parent = NULL;
        struct inode *inode = dentry->d_inode;
        struct inode *dir;
-       unsigned long verifier;
        int openflags, ret = 0;
 
+       if (!is_atomic_open(nd) || d_mountpoint(dentry))
+               goto no_open;
        parent = dget_parent(dentry);
        dir = parent->d_inode;
-       if (!is_atomic_open(dir, nd))
-               goto no_open;
        /* We can't create new files in nfs_open_revalidate(), so we
         * optimize away revalidation of negative dentries.
         */
-       if (inode == NULL)
+       if (inode == NULL) {
+               if (!nfs_neg_need_reval(dir, dentry, nd))
+                       ret = 1;
                goto out;
+       }
+
        /* NFS only supports OPEN on regular files */
        if (!S_ISREG(inode->i_mode))
-               goto no_open;
+               goto no_open_dput;
        openflags = nd->intent.open.flags;
        /* We cannot do exclusive creation on a positive dentry */
        if ((openflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
-               goto no_open;
+               goto no_open_dput;
        /* We can't create new files, or truncate existing ones here */
        openflags &= ~(O_CREAT|O_TRUNC);
 
@@ -1074,21 +1110,15 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
         * operations that change the directory. We therefore save the
         * change attribute *before* we do the RPC call.
         */
-       lock_kernel();
-       verifier = nfs_save_change_attribute(dir);
        ret = nfs4_open_revalidate(dir, dentry, openflags, nd);
-       if (!ret)
-               nfs_set_verifier(dentry, verifier);
-       unlock_kernel();
 out:
        dput(parent);
        if (!ret)
                d_drop(dentry);
        return ret;
-no_open:
+no_open_dput:
        dput(parent);
-       if (inode != NULL && nfs_have_delegation(inode, FMODE_READ))
-               return 1;
+no_open:
        return nfs_lookup_revalidate(dentry, nd);
 }
 #endif /* CONFIG_NFSV4 */
@@ -1174,32 +1204,40 @@ out_renew:
 int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
                                struct nfs_fattr *fattr)
 {
+       struct dentry *parent = dget_parent(dentry);
+       struct inode *dir = parent->d_inode;
        struct inode *inode;
        int error = -EACCES;
 
+       d_drop(dentry);
+
        /* We may have been initialized further down */
        if (dentry->d_inode)
-               return 0;
+               goto out;
        if (fhandle->size == 0) {
-               struct inode *dir = dentry->d_parent->d_inode;
                error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr);
                if (error)
-                       return error;
+                       goto out_error;
        }
+       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
        if (!(fattr->valid & NFS_ATTR_FATTR)) {
                struct nfs_server *server = NFS_SB(dentry->d_sb);
                error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr);
                if (error < 0)
-                       return error;
+                       goto out_error;
        }
        inode = nfs_fhget(dentry->d_sb, fhandle, fattr);
        error = PTR_ERR(inode);
        if (IS_ERR(inode))
-               return error;
-       d_instantiate(dentry, inode);
-       if (d_unhashed(dentry))
-               d_rehash(dentry);
+               goto out_error;
+       d_add(dentry, inode);
+out:
+       dput(parent);
        return 0;
+out_error:
+       nfs_mark_for_revalidate(dir);
+       dput(parent);
+       return error;
 }
 
 /*
@@ -1224,17 +1262,11 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,
        if ((nd->flags & LOOKUP_CREATE) != 0)
                open_flags = nd->intent.open.flags;
 
-       lock_kernel();
-       nfs_begin_data_update(dir);
        error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, nd);
-       nfs_end_data_update(dir);
        if (error != 0)
                goto out_err;
-       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
-       unlock_kernel();
        return 0;
 out_err:
-       unlock_kernel();
        d_drop(dentry);
        return error;
 }
@@ -1257,17 +1289,11 @@ nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
        attr.ia_mode = mode;
        attr.ia_valid = ATTR_MODE;
 
-       lock_kernel();
-       nfs_begin_data_update(dir);
        status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev);
-       nfs_end_data_update(dir);
        if (status != 0)
                goto out_err;
-       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
-       unlock_kernel();
        return 0;
 out_err:
-       unlock_kernel();
        d_drop(dentry);
        return status;
 }
@@ -1286,21 +1312,21 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
        attr.ia_valid = ATTR_MODE;
        attr.ia_mode = mode | S_IFDIR;
 
-       lock_kernel();
-       nfs_begin_data_update(dir);
        error = NFS_PROTO(dir)->mkdir(dir, dentry, &attr);
-       nfs_end_data_update(dir);
        if (error != 0)
                goto out_err;
-       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
-       unlock_kernel();
        return 0;
 out_err:
        d_drop(dentry);
-       unlock_kernel();
        return error;
 }
 
+static void nfs_dentry_handle_enoent(struct dentry *dentry)
+{
+       if (dentry->d_inode != NULL && !d_unhashed(dentry))
+               d_delete(dentry);
+}
+
 static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
 {
        int error;
@@ -1308,14 +1334,12 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
        dfprintk(VFS, "NFS: rmdir(%s/%ld), %s\n",
                        dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
 
-       lock_kernel();
-       nfs_begin_data_update(dir);
        error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name);
        /* Ensure the VFS deletes this inode */
        if (error == 0 && dentry->d_inode != NULL)
                clear_nlink(dentry->d_inode);
-       nfs_end_data_update(dir);
-       unlock_kernel();
+       else if (error == -ENOENT)
+               nfs_dentry_handle_enoent(dentry);
 
        return error;
 }
@@ -1372,17 +1396,13 @@ static int nfs_sillyrename(struct inode *dir, struct dentry *dentry)
 
        qsilly.name = silly;
        qsilly.len  = strlen(silly);
-       nfs_begin_data_update(dir);
        if (dentry->d_inode) {
-               nfs_begin_data_update(dentry->d_inode);
                error = NFS_PROTO(dir)->rename(dir, &dentry->d_name,
                                dir, &qsilly);
                nfs_mark_for_revalidate(dentry->d_inode);
-               nfs_end_data_update(dentry->d_inode);
        } else
                error = NFS_PROTO(dir)->rename(dir, &dentry->d_name,
                                dir, &qsilly);
-       nfs_end_data_update(dir);
        if (!error) {
                nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
                d_move(dentry, sdentry);
@@ -1416,19 +1436,17 @@ static int nfs_safe_remove(struct dentry *dentry)
                goto out;
        }
 
-       nfs_begin_data_update(dir);
        if (inode != NULL) {
                nfs_inode_return_delegation(inode);
-               nfs_begin_data_update(inode);
                error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
                /* The VFS may want to delete this inode */
                if (error == 0)
-                       drop_nlink(inode);
+                       nfs_drop_nlink(inode);
                nfs_mark_for_revalidate(inode);
-               nfs_end_data_update(inode);
        } else
                error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);
-       nfs_end_data_update(dir);
+       if (error == -ENOENT)
+               nfs_dentry_handle_enoent(dentry);
 out:
        return error;
 }
@@ -1446,7 +1464,6 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
        dfprintk(VFS, "NFS: unlink(%s/%ld, %s)\n", dir->i_sb->s_id,
                dir->i_ino, dentry->d_name.name);
 
-       lock_kernel();
        spin_lock(&dcache_lock);
        spin_lock(&dentry->d_lock);
        if (atomic_read(&dentry->d_count) > 1) {
@@ -1455,7 +1472,6 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
                /* Start asynchronous writeout of the inode */
                write_inode_now(dentry->d_inode, 0);
                error = nfs_sillyrename(dir, dentry);
-               unlock_kernel();
                return error;
        }
        if (!d_unhashed(dentry)) {
@@ -1465,11 +1481,10 @@ static int nfs_unlink(struct inode *dir, struct dentry *dentry)
        spin_unlock(&dentry->d_lock);
        spin_unlock(&dcache_lock);
        error = nfs_safe_remove(dentry);
-       if (!error) {
+       if (!error || error == -ENOENT) {
                nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
        } else if (need_rehash)
                d_rehash(dentry);
-       unlock_kernel();
        return error;
 }
 
@@ -1506,13 +1521,9 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
        attr.ia_mode = S_IFLNK | S_IRWXUGO;
        attr.ia_valid = ATTR_MODE;
 
-       lock_kernel();
-
        page = alloc_page(GFP_HIGHUSER);
-       if (!page) {
-               unlock_kernel();
+       if (!page)
                return -ENOMEM;
-       }
 
        kaddr = kmap_atomic(page, KM_USER0);
        memcpy(kaddr, symname, pathlen);
@@ -1520,16 +1531,13 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
                memset(kaddr + pathlen, 0, PAGE_SIZE - pathlen);
        kunmap_atomic(kaddr, KM_USER0);
 
-       nfs_begin_data_update(dir);
        error = NFS_PROTO(dir)->symlink(dir, dentry, page, pathlen, &attr);
-       nfs_end_data_update(dir);
        if (error != 0) {
                dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s) error %d\n",
                        dir->i_sb->s_id, dir->i_ino,
                        dentry->d_name.name, symname, error);
                d_drop(dentry);
                __free_page(page);
-               unlock_kernel();
                return error;
        }
 
@@ -1541,13 +1549,12 @@ static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *sym
        if (!add_to_page_cache(page, dentry->d_inode->i_mapping, 0,
                                                        GFP_KERNEL)) {
                pagevec_add(&lru_pvec, page);
-               pagevec_lru_add(&lru_pvec);
+               pagevec_lru_add_file(&lru_pvec);
                SetPageUptodate(page);
                unlock_page(page);
        } else
                __free_page(page);
 
-       unlock_kernel();
        return 0;
 }
 
@@ -1561,17 +1568,14 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
                old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
                dentry->d_parent->d_name.name, dentry->d_name.name);
 
-       lock_kernel();
-       nfs_begin_data_update(dir);
-       nfs_begin_data_update(inode);
+       nfs_inode_return_delegation(inode);
+
+       d_drop(dentry);
        error = NFS_PROTO(dir)->link(inode, dir, &dentry->d_name);
        if (error == 0) {
                atomic_inc(&inode->i_count);
-               d_instantiate(dentry, inode);
+               d_add(dentry, inode);
        }
-       nfs_end_data_update(inode);
-       nfs_end_data_update(dir);
-       unlock_kernel();
        return error;
 }
 
@@ -1607,57 +1611,47 @@ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
        struct dentry *dentry = NULL, *rehash = NULL;
        int error = -EBUSY;
 
-       /*
-        * To prevent any new references to the target during the rename,
-        * we unhash the dentry and free the inode in advance.
-        */
-       lock_kernel();
-       if (!d_unhashed(new_dentry)) {
-               d_drop(new_dentry);
-               rehash = new_dentry;
-       }
-
        dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n",
                 old_dentry->d_parent->d_name.name, old_dentry->d_name.name,
                 new_dentry->d_parent->d_name.name, new_dentry->d_name.name,
                 atomic_read(&new_dentry->d_count));
 
        /*
-        * First check whether the target is busy ... we can't
-        * safely do _any_ rename if the target is in use.
-        *
-        * For files, make a copy of the dentry and then do a 
-        * silly-rename. If the silly-rename succeeds, the
-        * copied dentry is hashed and becomes the new target.
+        * For non-directories, check whether the target is busy and if so,
+        * make a copy of the dentry and then do a silly-rename. If the
+        * silly-rename succeeds, the copied dentry is hashed and becomes
+        * the new target.
         */
-       if (!new_inode)
-               goto go_ahead;
-       if (S_ISDIR(new_inode->i_mode)) {
-               error = -EISDIR;
-               if (!S_ISDIR(old_inode->i_mode))
-                       goto out;
-       } else if (atomic_read(&new_dentry->d_count) > 2) {
-               int err;
-               /* copy the target dentry's name */
-               dentry = d_alloc(new_dentry->d_parent,
-                                &new_dentry->d_name);
-               if (!dentry)
-                       goto out;
+       if (new_inode && !S_ISDIR(new_inode->i_mode)) {
+               /*
+                * To prevent any new references to the target during the
+                * rename, we unhash the dentry in advance.
+                */
+               if (!d_unhashed(new_dentry)) {
+                       d_drop(new_dentry);
+                       rehash = new_dentry;
+               }
+
+               if (atomic_read(&new_dentry->d_count) > 2) {
+                       int err;
+
+                       /* copy the target dentry's name */
+                       dentry = d_alloc(new_dentry->d_parent,
+                                        &new_dentry->d_name);
+                       if (!dentry)
+                               goto out;
+
+                       /* silly-rename the existing target ... */
+                       err = nfs_sillyrename(new_dir, new_dentry);
+                       if (err)
+                               goto out;
 
-               /* silly-rename the existing target ... */
-               err = nfs_sillyrename(new_dir, new_dentry);
-               if (!err) {
-                       new_dentry = rehash = dentry;
+                       new_dentry = dentry;
+                       rehash = NULL;
                        new_inode = NULL;
-                       /* instantiate the replacement target */
-                       d_instantiate(new_dentry, NULL);
-               } else if (atomic_read(&new_dentry->d_count) > 1)
-                       /* dentry still busy? */
-                       goto out;
-       } else
-               drop_nlink(new_inode);
+               }
+       }
 
-go_ahead:
        /*
         * ... prune child dentries and writebacks if needed.
         */
@@ -1668,33 +1662,27 @@ go_ahead:
        }
        nfs_inode_return_delegation(old_inode);
 
-       if (new_inode != NULL) {
+       if (new_inode != NULL)
                nfs_inode_return_delegation(new_inode);
-               d_delete(new_dentry);
-       }
 
-       nfs_begin_data_update(old_dir);
-       nfs_begin_data_update(new_dir);
-       nfs_begin_data_update(old_inode);
        error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name,
                                           new_dir, &new_dentry->d_name);
        nfs_mark_for_revalidate(old_inode);
-       nfs_end_data_update(old_inode);
-       nfs_end_data_update(new_dir);
-       nfs_end_data_update(old_dir);
 out:
        if (rehash)
                d_rehash(rehash);
        if (!error) {
+               if (new_inode != NULL)
+                       nfs_drop_nlink(new_inode);
                d_move(old_dentry, new_dentry);
                nfs_set_verifier(new_dentry,
                                        nfs_save_change_attribute(new_dir));
-       }
+       } else if (error == -ENOENT)
+               nfs_dentry_handle_enoent(old_dentry);
 
        /* new dentry created? */
        if (dentry)
                dput(dentry);
-       unlock_kernel();
        return error;
 }
 
@@ -1711,22 +1699,33 @@ static void nfs_access_free_entry(struct nfs_access_entry *entry)
        smp_mb__after_atomic_dec();
 }
 
+static void nfs_access_free_list(struct list_head *head)
+{
+       struct nfs_access_entry *cache;
+
+       while (!list_empty(head)) {
+               cache = list_entry(head->next, struct nfs_access_entry, lru);
+               list_del(&cache->lru);
+               nfs_access_free_entry(cache);
+       }
+}
+
 int nfs_access_cache_shrinker(int nr_to_scan, gfp_t gfp_mask)
 {
        LIST_HEAD(head);
        struct nfs_inode *nfsi;
        struct nfs_access_entry *cache;
 
-restart:
+       if ((gfp_mask & GFP_KERNEL) != GFP_KERNEL)
+               return (nr_to_scan == 0) ? 0 : -1;
+
        spin_lock(&nfs_access_lru_lock);
        list_for_each_entry(nfsi, &nfs_access_lru_list, access_cache_inode_lru) {
                struct inode *inode;
 
                if (nr_to_scan-- == 0)
                        break;
-               inode = igrab(&nfsi->vfs_inode);
-               if (inode == NULL)
-                       continue;
+               inode = &nfsi->vfs_inode;
                spin_lock(&inode->i_lock);
                if (list_empty(&nfsi->access_cache_entry_lru))
                        goto remove_lru_entry;
@@ -1740,60 +1739,48 @@ restart:
                else {
 remove_lru_entry:
                        list_del_init(&nfsi->access_cache_inode_lru);
+                       smp_mb__before_clear_bit();
                        clear_bit(NFS_INO_ACL_LRU_SET, &nfsi->flags);
+                       smp_mb__after_clear_bit();
                }
                spin_unlock(&inode->i_lock);
-               spin_unlock(&nfs_access_lru_lock);
-               iput(inode);
-               goto restart;
        }
        spin_unlock(&nfs_access_lru_lock);
-       while (!list_empty(&head)) {
-               cache = list_entry(head.next, struct nfs_access_entry, lru);
-               list_del(&cache->lru);
-               nfs_access_free_entry(cache);
-       }
+       nfs_access_free_list(&head);
        return (atomic_long_read(&nfs_access_nr_entries) / 100) * sysctl_vfs_cache_pressure;
 }
 
-static void __nfs_access_zap_cache(struct inode *inode)
+static void __nfs_access_zap_cache(struct nfs_inode *nfsi, struct list_head *head)
 {
-       struct nfs_inode *nfsi = NFS_I(inode);
        struct rb_root *root_node = &nfsi->access_cache;
-       struct rb_node *n, *dispose = NULL;
+       struct rb_node *n;
        struct nfs_access_entry *entry;
 
        /* Unhook entries from the cache */
        while ((n = rb_first(root_node)) != NULL) {
                entry = rb_entry(n, struct nfs_access_entry, rb_node);
                rb_erase(n, root_node);
-               list_del(&entry->lru);
-               n->rb_left = dispose;
-               dispose = n;
+               list_move(&entry->lru, head);
        }
        nfsi->cache_validity &= ~NFS_INO_INVALID_ACCESS;
-       spin_unlock(&inode->i_lock);
-
-       /* Now kill them all! */
-       while (dispose != NULL) {
-               n = dispose;
-               dispose = n->rb_left;
-               nfs_access_free_entry(rb_entry(n, struct nfs_access_entry, rb_node));
-       }
 }
 
 void nfs_access_zap_cache(struct inode *inode)
 {
+       LIST_HEAD(head);
+
+       if (test_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags) == 0)
+               return;
        /* Remove from global LRU init */
-       if (test_and_clear_bit(NFS_INO_ACL_LRU_SET, &NFS_FLAGS(inode))) {
-               spin_lock(&nfs_access_lru_lock);
+       spin_lock(&nfs_access_lru_lock);
+       if (test_and_clear_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags))
                list_del_init(&NFS_I(inode)->access_cache_inode_lru);
-               spin_unlock(&nfs_access_lru_lock);
-       }
 
        spin_lock(&inode->i_lock);
-       /* This will release the spinlock */
-       __nfs_access_zap_cache(inode);
+       __nfs_access_zap_cache(NFS_I(inode), &head);
+       spin_unlock(&inode->i_lock);
+       spin_unlock(&nfs_access_lru_lock);
+       nfs_access_free_list(&head);
 }
 
 static struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, struct rpc_cred *cred)
@@ -1826,7 +1813,8 @@ static int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, str
        cache = nfs_access_search_rbtree(inode, cred);
        if (cache == NULL)
                goto out;
-       if (!time_in_range(jiffies, cache->jiffies, cache->jiffies + NFS_ATTRTIMEO(inode)))
+       if (!nfs_have_delegated_attributes(inode) &&
+           !time_in_range_open(jiffies, cache->jiffies, cache->jiffies + nfsi->attrtimeo))
                goto out_stale;
        res->jiffies = cache->jiffies;
        res->cred = cache->cred;
@@ -1843,8 +1831,8 @@ out_stale:
        nfs_access_free_entry(cache);
        return -ENOENT;
 out_zap:
-       /* This will release the spinlock */
-       __nfs_access_zap_cache(inode);
+       spin_unlock(&inode->i_lock);
+       nfs_access_zap_cache(inode);
        return -ENOENT;
 }
 
@@ -1899,9 +1887,11 @@ static void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *s
        smp_mb__after_atomic_inc();
 
        /* Add inode to global LRU list */
-       if (!test_and_set_bit(NFS_INO_ACL_LRU_SET, &NFS_FLAGS(inode))) {
+       if (!test_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags)) {
                spin_lock(&nfs_access_lru_lock);
-               list_add_tail(&NFS_I(inode)->access_cache_inode_lru, &nfs_access_lru_list);
+               if (!test_and_set_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags))
+                       list_add_tail(&NFS_I(inode)->access_cache_inode_lru,
+                                       &nfs_access_lru_list);
                spin_unlock(&nfs_access_lru_lock);
        }
 }
@@ -1920,11 +1910,17 @@ static int nfs_do_access(struct inode *inode, struct rpc_cred *cred, int mask)
        cache.cred = cred;
        cache.jiffies = jiffies;
        status = NFS_PROTO(inode)->access(inode, &cache);
-       if (status != 0)
+       if (status != 0) {
+               if (status == -ESTALE) {
+                       nfs_zap_caches(inode);
+                       if (!S_ISDIR(inode->i_mode))
+                               set_bit(NFS_INO_STALE, &NFS_I(inode)->flags);
+               }
                return status;
+       }
        nfs_access_add_cache(inode, &cache);
 out:
-       if ((cache.mask & mask) == mask)
+       if ((mask & ~cache.mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
                return 0;
        return -EACCES;
 }
@@ -1947,17 +1943,17 @@ int nfs_may_open(struct inode *inode, struct rpc_cred *cred, int openflags)
        return nfs_do_access(inode, cred, nfs_open_permission_mask(openflags));
 }
 
-int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
+int nfs_permission(struct inode *inode, int mask)
 {
        struct rpc_cred *cred;
        int res = 0;
 
        nfs_inc_stats(inode, NFSIOS_VFSACCESS);
 
-       if (mask == 0)
+       if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
                goto out;
        /* Is this sys_access() ? */
-       if (nd != NULL && (nd->flags & LOOKUP_ACCESS))
+       if (mask & MAY_ACCESS)
                goto force_lookup;
 
        switch (inode->i_mode & S_IFMT) {
@@ -1966,8 +1962,8 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
                case S_IFREG:
                        /* NFSv4 has atomic_open... */
                        if (nfs_server_capable(inode, NFS_CAP_ATOMIC_OPEN)
-                                       && nd != NULL
-                                       && (nd->flags & LOOKUP_OPEN))
+                                       && (mask & MAY_OPEN)
+                                       && !(mask & MAY_EXEC))
                                goto out;
                        break;
                case S_IFDIR:
@@ -1980,19 +1976,19 @@ int nfs_permission(struct inode *inode, int mask, struct nameidata *nd)
        }
 
 force_lookup:
-       lock_kernel();
-
        if (!NFS_PROTO(inode)->access)
                goto out_notsup;
 
-       cred = rpcauth_lookupcred(NFS_CLIENT(inode)->cl_auth, 0);
+       cred = rpc_lookup_cred();
        if (!IS_ERR(cred)) {
                res = nfs_do_access(inode, cred, mask);
                put_rpccred(cred);
        } else
                res = PTR_ERR(cred);
-       unlock_kernel();
 out:
+       if (!res && (mask & MAY_EXEC) && !execute_ok(inode))
+               res = -EACCES;
+
        dfprintk(VFS, "NFS: permission(%s/%ld), mask=0x%x, res=%d\n",
                inode->i_sb->s_id, inode->i_ino, mask, res);
        return res;
@@ -2000,7 +1996,6 @@ out_notsup:
        res = nfs_revalidate_inode(NFS_SERVER(inode), inode);
        if (res == 0)
                res = generic_permission(inode, mask, NULL);
-       unlock_kernel();
        goto out;
 }