]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/nfs/dir.c
[ARM] 4914/1: AT91: Update defconfigs (Part 1)
[linux-2.6-omap-h63xx.git] / fs / nfs / dir.c
index ea97408e423e916bf77ef5b303541a23551d2cb3..6cea7479c5b4d13136fb753eecc453f19dc43229 100644 (file)
@@ -38,6 +38,7 @@
 #include "nfs4_fs.h"
 #include "delegation.h"
 #include "iostat.h"
+#include "internal.h"
 
 /* #define NFS_DEBUG_VERBOSE 1 */
 
@@ -153,7 +154,6 @@ typedef struct {
        struct nfs_entry *entry;
        decode_dirent_t decode;
        int             plus;
-       int             error;
        unsigned long   timestamp;
        int             timestamp_valid;
 } nfs_readdir_descriptor_t;
@@ -191,7 +191,7 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page)
                /* 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;
                }
@@ -200,9 +200,6 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page)
        desc->timestamp = timestamp;
        desc->timestamp_valid = 1;
        SetPageUptodate(page);
-       spin_lock(&inode->i_lock);
-       NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATIME;
-       spin_unlock(&inode->i_lock);
        /* 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.
@@ -214,10 +211,7 @@ int nfs_readdir_filler(nfs_readdir_descriptor_t *desc, struct page *page)
        unlock_page(page);
        return 0;
  error:
-       SetPageError(page);
        unlock_page(page);
-       nfs_zap_caches(inode);
-       desc->error = error;
        return -EIO;
 }
 
@@ -407,7 +401,7 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
        struct file     *file = desc->file;
        struct nfs_entry *entry = desc->entry;
        struct dentry   *dentry = NULL;
-       unsigned long   fileid;
+       u64             fileid;
        int             loop_count = 0,
                        res;
 
@@ -418,7 +412,7 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
                unsigned d_type = DT_UNKNOWN;
                /* Note: entry->prev_cookie contains the cookie for
                 *       retrieving the current dirent on the server */
-               fileid = nfs_fileid_to_ino_t(entry->ino);
+               fileid = entry->ino;
 
                /* Get a dentry if we have one */
                if (dentry != NULL)
@@ -428,11 +422,12 @@ int nfs_do_filldir(nfs_readdir_descriptor_t *desc, void *dirent,
                /* Use readdirplus info */
                if (dentry != NULL && dentry->d_inode != NULL) {
                        d_type = dt_type(dentry->d_inode);
-                       fileid = dentry->d_inode->i_ino;
+                       fileid = NFS_FILEID(dentry->d_inode);
                }
 
                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++;
@@ -486,16 +481,13 @@ 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,
+       status = NFS_PROTO(inode)->readdir(file->f_path.dentry, cred,
+                                               *desc->dir_cookie, page,
                                                NFS_SERVER(inode)->dtsize,
                                                desc->plus);
-       spin_lock(&inode->i_lock);
-       NFS_I(inode)->cache_validity |= NFS_INO_INVALID_ATIME;
-       spin_unlock(&inode->i_lock);
        desc->page = page;
        desc->ptr = kmap(page);         /* matching kunmap in nfs_do_filldir */
-       if (desc->error >= 0) {
+       if (status >= 0) {
                desc->timestamp = timestamp;
                desc->timestamp_valid = 1;
                if ((status = dir_decode(desc)) == 0)
@@ -543,12 +535,6 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
 
        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,7 +544,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
        memset(desc, 0, sizeof(*desc));
 
        desc->file = filp;
-       desc->dir_cookie = &((struct nfs_open_context *)filp->private_data)->dir_cookie;
+       desc->dir_cookie = &nfs_file_open_context(filp)->dir_cookie;
        desc->decode = NFS_PROTO(inode)->decode_dirent;
        desc->plus = NFS_USE_READDIRPLUS(inode);
 
@@ -569,6 +555,11 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
        nfs_fattr_init(&fattr);
        desc->entry = &my_entry;
 
+       nfs_block_sillyrename(dentry);
+       res = nfs_revalidate_mapping_nolock(inode, filp->f_mapping);
+       if (res < 0)
+               goto out;
+
        while(!desc->entry->eof) {
                res = readdir_search_pagecache(desc);
 
@@ -584,7 +575,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;
@@ -599,6 +590,8 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
                        break;
                }
        }
+out:
+       nfs_unblock_sillyrename(dentry);
        unlock_kernel();
        if (res > 0)
                res = 0;
@@ -623,7 +616,7 @@ static loff_t nfs_llseek_dir(struct file *filp, loff_t offset, int origin)
        }
        if (offset != filp->f_pos) {
                filp->f_pos = offset;
-               ((struct nfs_open_context *)filp->private_data)->dir_cookie = 0;
+               nfs_file_open_context(filp)->dir_cookie = 0;
        }
 out:
        mutex_unlock(&filp->f_path.dentry->d_inode->i_mutex);
@@ -643,6 +636,21 @@ static int nfs_fsync_dir(struct file *filp, struct dentry *dentry, int datasync)
        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 = jiffies;
+}
+
 /*
  * A check for whether or not the parent directory has changed.
  * In the case it has, we assume that the dentries are untrustworthy
@@ -650,36 +658,18 @@ static int nfs_fsync_dir(struct file *filp, struct dentry *dentry, int datasync)
  */
 static int nfs_check_verifier(struct inode *dir, struct dentry *dentry)
 {
-       unsigned long verf;
-
        if (IS_ROOT(dentry))
                return 1;
-       verf = dentry->d_time;
-       if (nfs_caches_unstable(dir)
-                       || verf != NFS_I(dir)->cache_change_attribute)
+       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;
 }
 
-static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf)
-{
-       dentry->d_time = verf;
-}
-
-static void nfs_refresh_verifier(struct dentry * dentry, unsigned long verf)
-{
-       nfs_set_verifier(dentry, verf);
-}
-
-/*
- * Whenever an NFS operation succeeds, we know that the dentry
- * is valid, so we update the revalidation timestamp.
- */
-static inline void nfs_renew_times(struct dentry * dentry)
-{
-       dentry->d_time = jiffies;
-}
-
 /*
  * Return the intent data that applies to this particular path component
  *
@@ -694,6 +684,19 @@ static inline unsigned int nfs_lookup_check_intent(struct nameidata *nd, unsigne
        return nd->flags & mask;
 }
 
+/*
+ * 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;
+       if (nd == NULL || nfs_lookup_check_intent(nd, LOOKUP_CREATE) == 0)
+               return 0;
+       return (nd->intent.open.flags & O_EXCL) != 0;
+}
+
 /*
  * Inode and filehandle revalidation for lookups.
  *
@@ -707,6 +710,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)
@@ -717,6 +722,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:
@@ -759,7 +765,6 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
        int error;
        struct nfs_fh fhandle;
        struct nfs_fattr fattr;
-       unsigned long verifier;
 
        parent = dget_parent(dentry);
        lock_kernel();
@@ -767,10 +772,6 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
        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;
@@ -785,7 +786,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
        }
 
        /* 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;
@@ -794,7 +795,6 @@ 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);
        if (error)
                goto out_bad;
@@ -803,8 +803,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
        if ((error = nfs_refresh_inode(inode, &fattr)) != 0)
                goto out_bad;
 
-       nfs_renew_times(dentry);
-       nfs_refresh_verifier(dentry, verifier);
+       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
  out_valid:
        unlock_kernel();
        dput(parent);
@@ -815,7 +814,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
 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);
@@ -842,6 +841,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;
@@ -861,7 +864,6 @@ static int nfs_dentry_delete(struct dentry *dentry)
  */
 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;
@@ -872,8 +874,6 @@ static void nfs_dentry_iput(struct dentry *dentry, struct inode *inode)
                nfs_complete_unlink(dentry, inode);
                unlock_kernel();
        }
-       /* When creating a negative dentry, we want to renew d_time */
-       nfs_renew_times(dentry);
        iput(inode);
 }
 
@@ -883,33 +883,10 @@ struct dentry_operations nfs_dentry_operations = {
        .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;
        int error;
        struct nfs_fh fhandle;
@@ -938,38 +915,31 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru
                goto out_unlock;
        }
 
+       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);
        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_renew_times(dentry);
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
+out_unblock_sillyrename:
+       nfs_unblock_sillyrename(parent);
 out_unlock:
        unlock_kernel();
 out:
@@ -1020,28 +990,16 @@ 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 */
+       /* Let vfs_create() deal with O_EXCL. Instantiate, but don't hash
+        * the dentry. */
        if (nd->intent.open.flags & O_EXCL) {
-               d_add(dentry, NULL);
+               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);
+       res = nfs4_atomic_open(dir, dentry, nd);
        unlock_kernel();
        if (IS_ERR(res)) {
                error = PTR_ERR(res);
@@ -1063,8 +1021,6 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
                }
        } else if (res != NULL)
                dentry = res;
-       nfs_renew_times(dentry);
-       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
 out:
        return res;
 no_open:
@@ -1076,7 +1032,6 @@ 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;
 
        parent = dget_parent(dentry);
@@ -1086,8 +1041,12 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
        /* 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;
@@ -1104,10 +1063,7 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
         * 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_refresh_verifier(dentry, verifier);
        unlock_kernel();
 out:
        dput(parent);
@@ -1133,6 +1089,7 @@ static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc)
                .len = entry->len,
        };
        struct inode *inode;
+       unsigned long verf = nfs_save_change_attribute(dir);
 
        switch (name.len) {
                case 2:
@@ -1143,6 +1100,14 @@ static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc)
                        if (name.name[0] == '.')
                                return dget(parent);
        }
+
+       spin_lock(&dir->i_lock);
+       if (NFS_I(dir)->cache_validity & NFS_INO_INVALID_DATA) {
+               spin_unlock(&dir->i_lock);
+               return NULL;
+       }
+       spin_unlock(&dir->i_lock);
+
        name.hash = full_name_hash(name.name, name.len);
        dentry = d_lookup(parent, &name);
        if (dentry != NULL) {
@@ -1162,6 +1127,8 @@ static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc)
        }
        if (!desc->plus || !(entry->fattr->valid & NFS_ATTR_FATTR))
                return NULL;
+       if (name.len > NFS_SERVER(dir)->namelen)
+               return NULL;
        /* Note: caller is already holding the dir->i_mutex! */
        dentry = d_alloc(parent, &name);
        if (dentry == NULL)
@@ -1181,12 +1148,8 @@ static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc)
                dentry = alias;
        }
 
-       nfs_renew_times(dentry);
-       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
-       return dentry;
 out_renew:
-       nfs_renew_times(dentry);
-       nfs_refresh_verifier(dentry, nfs_save_change_attribute(dir));
+       nfs_set_verifier(dentry, verf);
        return dentry;
 }
 
@@ -1196,32 +1159,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;
 }
 
 /*
@@ -1247,13 +1218,9 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,
                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_renew_times(dentry);
-       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
        unlock_kernel();
        return 0;
 out_err:
@@ -1281,13 +1248,9 @@ nfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
        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_renew_times(dentry);
-       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
        unlock_kernel();
        return 0;
 out_err:
@@ -1311,13 +1274,9 @@ static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int 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_renew_times(dentry);
-       nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
        unlock_kernel();
        return 0;
 out_err:
@@ -1326,6 +1285,12 @@ out_err:
        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;
@@ -1334,12 +1299,12 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
                        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);
+       else if (error == -ENOENT)
+               nfs_dentry_handle_enoent(dentry);
        unlock_kernel();
 
        return error;
@@ -1348,9 +1313,9 @@ static int nfs_rmdir(struct inode *dir, struct dentry *dentry)
 static int nfs_sillyrename(struct inode *dir, struct dentry *dentry)
 {
        static unsigned int sillycounter;
-       const int      i_inosize  = sizeof(dir->i_ino)*2;
+       const int      fileidsize  = sizeof(NFS_FILEID(dentry->d_inode))*2;
        const int      countersize = sizeof(sillycounter)*2;
-       const int      slen       = sizeof(".nfs") + i_inosize + countersize - 1;
+       const int      slen        = sizeof(".nfs")+fileidsize+countersize-1;
        char           silly[slen+1];
        struct qstr    qsilly;
        struct dentry *sdentry;
@@ -1368,8 +1333,9 @@ static int nfs_sillyrename(struct inode *dir, struct dentry *dentry)
        if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
                goto out;
 
-       sprintf(silly, ".nfs%*.*lx",
-               i_inosize, i_inosize, dentry->d_inode->i_ino);
+       sprintf(silly, ".nfs%*.*Lx",
+               fileidsize, fileidsize,
+               (unsigned long long)NFS_FILEID(dentry->d_inode));
 
        /* Return delegation in anticipation of the rename */
        nfs_inode_return_delegation(dentry->d_inode);
@@ -1396,19 +1362,14 @@ 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_renew_times(dentry);
                nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
                d_move(dentry, sdentry);
                error = nfs_async_unlink(dir, dentry);
@@ -1441,19 +1402,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_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;
 }
@@ -1490,8 +1449,7 @@ 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) {
-               nfs_renew_times(dentry);
+       if (!error || error == -ENOENT) {
                nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
        } else if (need_rehash)
                d_rehash(dentry);
@@ -1546,9 +1504,7 @@ 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,
@@ -1588,15 +1544,12 @@ nfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry)
                dentry->d_parent->d_name.name, dentry->d_name.name);
 
        lock_kernel();
-       nfs_begin_data_update(dir);
-       nfs_begin_data_update(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;
 }
@@ -1699,23 +1652,18 @@ go_ahead:
                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) {
                d_move(old_dentry, new_dentry);
-               nfs_renew_times(new_dentry);
-               nfs_refresh_verifier(new_dentry, nfs_save_change_attribute(new_dir));
-       }
+               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)
@@ -1746,13 +1694,19 @@ int nfs_access_cache_shrinker(int nr_to_scan, gfp_t gfp_mask)
 restart:
        spin_lock(&nfs_access_lru_lock);
        list_for_each_entry(nfsi, &nfs_access_lru_list, access_cache_inode_lru) {
+               struct rw_semaphore *s_umount;
                struct inode *inode;
 
                if (nr_to_scan-- == 0)
                        break;
+               s_umount = &nfsi->vfs_inode.i_sb->s_umount;
+               if (!down_read_trylock(s_umount))
+                       continue;
                inode = igrab(&nfsi->vfs_inode);
-               if (inode == NULL)
+               if (inode == NULL) {
+                       up_read(s_umount);
                        continue;
+               }
                spin_lock(&inode->i_lock);
                if (list_empty(&nfsi->access_cache_entry_lru))
                        goto remove_lru_entry;
@@ -1771,6 +1725,7 @@ remove_lru_entry:
                spin_unlock(&inode->i_lock);
                spin_unlock(&nfs_access_lru_lock);
                iput(inode);
+               up_read(s_umount);
                goto restart;
        }
        spin_unlock(&nfs_access_lru_lock);
@@ -1811,7 +1766,7 @@ static void __nfs_access_zap_cache(struct inode *inode)
 void nfs_access_zap_cache(struct inode *inode)
 {
        /* Remove from global LRU init */
-       if (test_and_clear_bit(NFS_INO_ACL_LRU_SET, &NFS_FLAGS(inode))) {
+       if (test_and_clear_bit(NFS_INO_ACL_LRU_SET, &NFS_I(inode)->flags)) {
                spin_lock(&nfs_access_lru_lock);
                list_del_init(&NFS_I(inode)->access_cache_inode_lru);
                spin_unlock(&nfs_access_lru_lock);
@@ -1840,7 +1795,7 @@ static struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, st
        return NULL;
 }
 
-int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res)
+static int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
        struct nfs_access_entry *cache;
@@ -1852,7 +1807,7 @@ int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs
        cache = nfs_access_search_rbtree(inode, cred);
        if (cache == NULL)
                goto out;
-       if (time_after(jiffies, cache->jiffies + NFS_ATTRTIMEO(inode)))
+       if (!time_in_range(jiffies, cache->jiffies, cache->jiffies + nfsi->attrtimeo))
                goto out_stale;
        res->jiffies = cache->jiffies;
        res->cred = cache->cred;
@@ -1907,7 +1862,7 @@ found:
        nfs_access_free_entry(entry);
 }
 
-void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
+static void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
 {
        struct nfs_access_entry *cache = kmalloc(sizeof(*cache), GFP_KERNEL);
        if (cache == NULL)
@@ -1925,7 +1880,7 @@ void nfs_access_add_cache(struct inode *inode, struct nfs_access_entry *set)
        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_and_set_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);
                spin_unlock(&nfs_access_lru_lock);
@@ -1955,6 +1910,24 @@ out:
        return -EACCES;
 }
 
+static int nfs_open_permission_mask(int openflags)
+{
+       int mask = 0;
+
+       if (openflags & FMODE_READ)
+               mask |= MAY_READ;
+       if (openflags & FMODE_WRITE)
+               mask |= MAY_WRITE;
+       if (openflags & FMODE_EXEC)
+               mask |= MAY_EXEC;
+       return mask;
+}
+
+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)
 {
        struct rpc_cred *cred;