<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">commit 9d1e9232223a7f065be7f956a7b749a4cbbbe16d
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Thu May 25 01:40:46 2006 -0400

    NFSv4: Some NFSv4 servers have broken behaviour for the change attribute
    
    The Linux NFSv4 server violates RFC3530 in that the change attribute is not
    guaranteed to be updated for every change to the inode. Our optimisation
    for checking whether or not the inode metadata has changed or not is broken
    too. Grr....
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

    [ modified to apply on 2.6.9-89 by Chuck Lever &lt;chuck.lever@oracle.com&gt; ]
    [ The change to use "now" instead of "jiffies" is already applied
      to 2.6.9-89. ]

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-23 13:15:43.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-23 13:20:00.000000000 -0400
@@ -1370,9 +1370,8 @@ static int nfs_check_inode_attributes(st
 	/* Do atomic weak cache consistency updates */
 	nfs_wcc_update_inode(inode, fattr);
 
-	if ((fattr-&gt;valid &amp; NFS_ATTR_FATTR_V4) != 0) {
-		if (nfsi-&gt;change_attr == fattr-&gt;change_attr)
-			goto out;
+	if ((fattr-&gt;valid &amp; NFS_ATTR_FATTR_V4) != 0 &amp;&amp;
+			nfsi-&gt;change_attr != fattr-&gt;change_attr) {
 		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR;
 		if (!data_unstable)
 			nfsi-&gt;cache_validity |= NFS_INO_REVAL_PAGECACHE;
@@ -1400,7 +1399,6 @@ static int nfs_check_inode_attributes(st
 	if (inode-&gt;i_nlink != fattr-&gt;nlink)
 		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR;
 
-out:
 	if (!timespec_equal(&amp;inode-&gt;i_atime, &amp;fattr-&gt;atime))
 		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATIME;
 
@@ -1573,15 +1571,13 @@ static int nfs_update_inode(struct inode
  		inode-&gt;i_blksize = fattr-&gt;du.nfs2.blocksize;
  	}
 
-	if ((fattr-&gt;valid &amp; NFS_ATTR_FATTR_V4)) {
-		if (nfsi-&gt;change_attr != fattr-&gt;change_attr) {
-			dprintk("NFS: change_attr change on server for file %s/%ld\n",
-					inode-&gt;i_sb-&gt;s_id, inode-&gt;i_ino);
-			nfsi-&gt;change_attr = fattr-&gt;change_attr;
-			invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
-			nfsi-&gt;cache_change_attribute = now;
-		} else
-			invalid &amp;= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA);
+	if ((fattr-&gt;valid &amp; NFS_ATTR_FATTR_V4) != 0 &amp;&amp;
+			nfsi-&gt;change_attr != fattr-&gt;change_attr) {
+		dprintk("NFS: change_attr change on server for file %s/%ld\n",
+				inode-&gt;i_sb-&gt;s_id, inode-&gt;i_ino);
+		nfsi-&gt;change_attr = fattr-&gt;change_attr;
+		invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
+		nfsi-&gt;cache_change_attribute = now;
 	}
 
 	/* Update attrtimeo value if we're out of the unstable period */
commit 73a3d07c1082145a3b78407bb5252df290470c4c
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Thu May 25 01:40:47 2006 -0400

    NFS: Clean up inode metadata updates
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

    [ modified to apply on 2.6.9-89 by Chuck Lever &lt;chuck.lever@oracle.com&gt; ]
    [ The change from nfs_refresh_inode() to nfs_post_op_update_inode() in
      _nfs4_proc_link() is already applied in 2.6.9-89 ]

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-22 21:19:51.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-22 21:24:43.000000000 -0400
@@ -1328,12 +1328,6 @@ static void nfs_wcc_update_inode(struct 
 {
 	struct nfs_inode *nfsi = NFS_I(inode);
 
-	if ((fattr-&gt;valid &amp; NFS_ATTR_PRE_CHANGE) != 0
-			&amp;&amp; nfsi-&gt;change_attr == fattr-&gt;pre_change_attr) {
-		nfsi-&gt;change_attr = fattr-&gt;change_attr;
-		nfsi-&gt;cache_change_attribute = jiffies;
-	}
-
 	/* If we have atomic WCC data, we may update some attributes */
 	if ((fattr-&gt;valid &amp; NFS_ATTR_WCC) != 0) {
 		if (timespec_equal(&amp;inode-&gt;i_ctime, &amp;fattr-&gt;pre_ctime)) {
@@ -1366,9 +1360,6 @@ static int nfs_check_inode_attributes(st
 	loff_t cur_size, new_isize;
 	int data_unstable;
 
-	if ((fattr-&gt;valid &amp; NFS_ATTR_FATTR) == 0)
-		return 0;
-
 	/* Has the inode gone and changed behind our back? */
 	if (nfsi-&gt;fileid != fattr-&gt;fileid
 			|| (inode-&gt;i_mode &amp; S_IFMT) != (fattr-&gt;mode &amp; S_IFMT)) {
@@ -1495,9 +1486,6 @@ static int nfs_update_inode(struct inode
 			__FUNCTION__, inode-&gt;i_sb-&gt;s_id, inode-&gt;i_ino,
 			atomic_read(&amp;inode-&gt;i_count), fattr-&gt;valid);
 
-	if ((fattr-&gt;valid &amp; NFS_ATTR_FATTR) == 0)
-		return 0;
-
 	if (nfsi-&gt;fileid != fattr-&gt;fileid) {
 		printk(KERN_ERR "%s: inode number mismatch\n"
 		       "expected (%s/0x%Lx), got (%s/0x%Lx)\n",
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_xdr.h cel-2.6.9/include/linux/nfs_xdr.h
--- linux-2.6.9/include/linux/nfs_xdr.h	2009-10-22 21:19:45.000000000 -0400
+++ cel-2.6.9/include/linux/nfs_xdr.h	2009-10-22 21:21:20.000000000 -0400
@@ -47,8 +47,7 @@ struct nfs_fattr {
 #define NFS_ATTR_WCC		0x0001		/* pre-op WCC data    */
 #define NFS_ATTR_FATTR		0x0002		/* post-op attributes */
 #define NFS_ATTR_FATTR_V3	0x0004		/* NFSv3 attributes */
-#define NFS_ATTR_FATTR_V4	0x0008
-#define NFS_ATTR_PRE_CHANGE	0x0010
+#define NFS_ATTR_FATTR_V4	0x0008		/* NFSv4 change attribute */
 
 /*
  * Info on the file system
commit f1bb0b92ba2cdfffe6e437f7a7da53138cf08d52
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Thu May 25 01:40:55 2006 -0400

    NFS: Fix page cache revalidation
    
    Fix up a bug in the handling of NFS_INO_REVAL_PAGECACHE: make sure that
    nfs_update_inode() clears it when we're sure we're not racing with other
    updates.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-23 14:46:33.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-23 14:47:23.000000000 -0400
@@ -1373,18 +1373,12 @@ static int nfs_check_inode_attributes(st
 	nfs_wcc_update_inode(inode, fattr);
 
 	if ((fattr-&gt;valid &amp; NFS_ATTR_FATTR_V4) != 0 &amp;&amp;
-			nfsi-&gt;change_attr != fattr-&gt;change_attr) {
-		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR;
-		if (!data_unstable)
-			nfsi-&gt;cache_validity |= NFS_INO_REVAL_PAGECACHE;
-	}
+			nfsi-&gt;change_attr != fattr-&gt;change_attr)
+		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
 
 	/* Verify a few of the more important attributes */
-	if (!timespec_equal(&amp;inode-&gt;i_mtime, &amp;fattr-&gt;mtime)) {
-		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR;
-		if (!data_unstable)
-			nfsi-&gt;cache_validity |= NFS_INO_REVAL_PAGECACHE;
-	}
+	if (!timespec_equal(&amp;inode-&gt;i_mtime, &amp;fattr-&gt;mtime))
+		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
 
 	cur_size = i_size_read(inode);
  	new_isize = nfs_size_to_loff_t(fattr-&gt;size);
@@ -1426,7 +1420,6 @@ int nfs_refresh_inode(struct inode *inod
 	if ((fattr-&gt;valid &amp; NFS_ATTR_FATTR) == 0)
 		return 0;
 	spin_lock(&amp;inode-&gt;i_lock);
-	nfsi-&gt;cache_validity &amp;= ~NFS_INO_REVAL_PAGECACHE;
 	if (time_after(fattr-&gt;time_start, nfsi-&gt;last_updated))
 		status = nfs_update_inode(inode, fattr);
 	else
@@ -1451,7 +1444,7 @@ int nfs_post_op_update_inode(struct inod
 
 	spin_lock(&amp;inode-&gt;i_lock);
 	if (unlikely((fattr-&gt;valid &amp; NFS_ATTR_FATTR) == 0)) {
-		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS;
+		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
 		goto out;
 	}
 	status = nfs_update_inode(inode, fattr);
@@ -1512,7 +1505,7 @@ static int nfs_update_inode(struct inode
 	/* Are we racing with known updates of the metadata on the server? */
 	data_stable = nfs_verify_change_attribute(inode, fattr-&gt;time_start);
 	if (data_stable)
-		nfsi-&gt;cache_validity &amp;= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME);
+		nfsi-&gt;cache_validity &amp;= ~(NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_ATIME);
 
 	/* Do atomic weak cache consistency updates */
 	nfs_wcc_update_inode(inode, fattr);
commit 38478b24e37587f1c4fedf8ac070ca54f052ed28
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Thu May 25 01:40:57 2006 -0400

    NFS: More page cache revalidation fixups
    
    Whenever the directory changes, we want to make sure that we always
    invalidate its page cache. Fix up update_changeattr() and
    nfs_mark_for_revalidate() so that they do so.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/nfs4proc.c cel-2.6.9/fs/nfs/nfs4proc.c
--- linux-2.6.9/fs/nfs/nfs4proc.c	2009-10-22 21:52:20.000000000 -0400
+++ cel-2.6.9/fs/nfs/nfs4proc.c	2009-10-22 21:53:10.000000000 -0400
@@ -181,15 +181,15 @@ renew_lease(struct nfs_server *server, u
 	spin_unlock(&amp;clp-&gt;cl_lock);
 }
 
-static void update_changeattr(struct inode *inode, struct nfs4_change_info *cinfo)
+static void update_changeattr(struct inode *dir, struct nfs4_change_info *cinfo)
 {
-	struct nfs_inode *nfsi = NFS_I(inode);
+	struct nfs_inode *nfsi = NFS_I(dir);
 
-	spin_lock(&amp;inode-&gt;i_lock);
-	nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR;
+	spin_lock(&amp;dir-&gt;i_lock);
+	nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA;
 	if (cinfo-&gt;before == nfsi-&gt;change_attr &amp;&amp; cinfo-&gt;atomic)
 		nfsi-&gt;change_attr = cinfo-&gt;after;
-	spin_unlock(&amp;inode-&gt;i_lock);
+	spin_unlock(&amp;dir-&gt;i_lock);
 }
 
 /*
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h
--- linux-2.6.9/include/linux/nfs_fs.h	2009-10-22 21:52:19.000000000 -0400
+++ cel-2.6.9/include/linux/nfs_fs.h	2009-10-22 21:53:10.000000000 -0400
@@ -267,8 +267,12 @@ static inline int nfs_caches_unstable(st
 
 static inline void nfs_mark_for_revalidate(struct inode *inode)
 {
+	struct nfs_inode *nfsi = NFS_I(inode);
+
 	spin_lock(&amp;inode-&gt;i_lock);
-	NFS_I(inode)-&gt;cache_validity |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS;
+	nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS;
+	if (S_ISDIR(inode-&gt;i_mode))
+		nfsi-&gt;cache_validity |= NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA;
 	spin_unlock(&amp;inode-&gt;i_lock);
 }
 
commit 97db8f41792839a6912fd21be8b61dd6c50db58f
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Thu Sep 14 14:03:14 2006 -0400

    NFS: Don't invalidate the symlink we just stuffed into the cache
    
    And slight optimisation of nfs_end_data_update(): directories never have
    delegations anyway.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-23 13:24:46.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-23 13:29:32.000000000 -0400
@@ -1312,13 +1312,11 @@ void nfs_end_data_update(struct inode *i
 {
 	struct nfs_inode *nfsi = NFS_I(inode);
 
-	if (!nfs_have_delegation(inode, FMODE_READ)) {
-		/* Directories and symlinks: invalidate page cache */
-		if (S_ISDIR(inode-&gt;i_mode) || S_ISLNK(inode-&gt;i_mode)) {
-			spin_lock(&amp;inode-&gt;i_lock);
-			nfsi-&gt;cache_validity |= NFS_INO_INVALID_DATA;
-			spin_unlock(&amp;inode-&gt;i_lock);
-		}
+	/* Directories: invalidate page cache */
+	if (S_ISDIR(inode-&gt;i_mode)) {
+		spin_lock(&amp;inode-&gt;i_lock);
+		nfsi-&gt;cache_validity |= NFS_INO_INVALID_DATA;
+		spin_unlock(&amp;inode-&gt;i_lock);
 	}
 	nfsi-&gt;cache_change_attribute = jiffies;
 	atomic_dec(&amp;nfsi-&gt;data_updates);
commit b0c4fddca2bc3967381b728732a8850de35e1b20
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Mon Feb 5 14:44:22 2007 -0800

    NFS: Cleanup - avoid rereading 'jiffies' more than once in the same routine
    
    Micro-optimisations for nfs_fhget() and nfs_wcc_update_inode().
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-22 20:29:06.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-22 20:55:28.000000000 -0400
@@ -845,6 +845,7 @@ nfs_fhget(struct super_block *sb, struct
 
 	if (inode-&gt;i_state &amp; I_NEW) {
 		struct nfs_inode *nfsi = NFS_I(inode);
+		unsigned long now = jiffies;
 
 		/* We set i_ino for the few things that still rely on it,
 		 * such as stat(2) */
@@ -884,7 +885,8 @@ nfs_fhget(struct super_block *sb, struct
 			init_special_inode(inode, inode-&gt;i_mode, fattr-&gt;rdev);
 
 		nfsi-&gt;read_cache_jiffies = fattr-&gt;time_start;
-		nfsi-&gt;last_updated = jiffies;
+		nfsi-&gt;last_updated = now;
+		nfsi-&gt;cache_change_attribute = now;
 		inode-&gt;i_atime = fattr-&gt;atime;
 		inode-&gt;i_mtime = fattr-&gt;mtime;
 		inode-&gt;i_ctime = fattr-&gt;ctime;
@@ -905,7 +907,7 @@ nfs_fhget(struct super_block *sb, struct
 			inode-&gt;i_blksize = fattr-&gt;du.nfs2.blocksize;
 		}
 		nfsi-&gt;attrtimeo = NFS_MINATTRTIMEO(inode);
-		nfsi-&gt;attrtimeo_timestamp = jiffies;
+		nfsi-&gt;attrtimeo_timestamp = now;
 		memset(nfsi-&gt;cookieverf, 0, sizeof(nfsi-&gt;cookieverf));
 		nfsi-&gt;access_cache = RB_ROOT;
 
@@ -1326,6 +1328,7 @@ void nfs_end_data_update(struct inode *i
 static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 {
 	struct nfs_inode *nfsi = NFS_I(inode);
+	unsigned long now = jiffies;
 
 	if ((fattr-&gt;valid &amp; NFS_ATTR_PRE_CHANGE) != 0
 			&amp;&amp; nfsi-&gt;change_attr == fattr-&gt;pre_change_attr) {
@@ -1337,15 +1340,15 @@ static void nfs_wcc_update_inode(struct 
 	if ((fattr-&gt;valid &amp; NFS_ATTR_WCC) != 0) {
 		if (timespec_equal(&amp;inode-&gt;i_ctime, &amp;fattr-&gt;pre_ctime)) {
 			memcpy(&amp;inode-&gt;i_ctime, &amp;fattr-&gt;ctime, sizeof(inode-&gt;i_ctime));
-			nfsi-&gt;cache_change_attribute = jiffies;
+			nfsi-&gt;cache_change_attribute = now;
 		}
 		if (timespec_equal(&amp;inode-&gt;i_mtime, &amp;fattr-&gt;pre_mtime)) {
 			memcpy(&amp;inode-&gt;i_mtime, &amp;fattr-&gt;mtime, sizeof(inode-&gt;i_mtime));
-			nfsi-&gt;cache_change_attribute = jiffies;
+			nfsi-&gt;cache_change_attribute = now;
 		}
 		if (inode-&gt;i_size == fattr-&gt;pre_size &amp;&amp; nfsi-&gt;npages == 0) {
 			inode-&gt;i_size = fattr-&gt;size;
-			nfsi-&gt;cache_change_attribute = jiffies;
+			nfsi-&gt;cache_change_attribute = now;
 		}
 	}
 }
@@ -2161,7 +2164,6 @@ static struct inode *nfs_alloc_inode(str
 		return NULL;
 	nfsi-&gt;flags = 0UL;
 	nfsi-&gt;cache_validity = 0UL;
-	nfsi-&gt;cache_change_attribute = jiffies;
 #ifdef CONFIG_NFS_V3_ACL
 	nfsi-&gt;acl_access = ERR_PTR(-EAGAIN);
 	nfsi-&gt;acl_default = ERR_PTR(-EAGAIN);
commit 1f4eab7e7c1d90dcd8ca4d7c064ee78dfbb345eb
Author: Neil Brown &lt;neilb@suse.de&gt;
Date:   Mon Apr 16 09:35:27 2007 +1000

    NFS: Set meaningful value for fattr-&gt;time_start in readdirplus results.
    
    Don't use uninitialsed value for fattr-&gt;time_start in readdirplus results.
    
    The 'fattr' structure filled in by nfs3_decode_direct does not get a
    value for -&gt;time_start set.
    Thus if an entry is for an inode that we already have in cache,
    when nfs_readdir_lookup calls nfs_fhget, it will call nfs_refresh_inode
    and may update the inode with out-of-date information.
    
    Directories are read a page at a time, so each page could have a
    different timestamp that "should" be used to set the time_start for
    the fattr for info in that page.  However storing the timestamp per
    page is awkward.  (We could stick in the first 4 bytes and only read 4092
    bytes, but that is a bigger code change than I am interested it).
    
    This patch ignores the readdir_plus attributes if a readdir finds the
    information already in cache, and otherwise sets -&gt;time_start to the time
    the readdir request was sent to the server.
    
    It might be nice to store - in the directory inode - the time stamp for
    the earliest readdir request that is still in the page cache, so that we
    don't ignore attribute data that we don't have to.  This patch doesn't do
    that.
    
    Signed-off-by: Neil Brown &lt;neilb@suse.de&gt;
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

    [ modified to apply on 2.6.9-89 by Chuck Lever &lt;chuck.lever@oracle.com&gt; ]
    [ commit 25606656 already applied to 2.6.9-89 ]

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c
--- linux-2.6.9/fs/nfs/dir.c	2009-10-27 10:36:09.000000000 -0400
+++ cel-2.6.9/fs/nfs/dir.c	2009-10-27 11:54:17.000000000 -0400
@@ -178,6 +178,8 @@ typedef struct {
 	struct nfs_entry *entry;
 	decode_dirent_t	decode;
 	int		plus;
+	unsigned long	timestamp;
+	int		timestamp_valid;
 } nfs_readdir_descriptor_t;
 
 /* Now we cache directories properly, by stuffing the dirent
@@ -217,6 +219,8 @@ int nfs_readdir_filler(nfs_readdir_descr
 		}
 		goto error;
 	}
+	desc-&gt;timestamp = timestamp;
+	desc-&gt;timestamp_valid = 1;
 	SetPageUptodate(page);
 	spin_lock(&amp;inode-&gt;i_lock);
 	NFS_I(inode)-&gt;cache_validity |= NFS_INO_INVALID_ATIME;
@@ -246,6 +250,10 @@ int dir_decode(nfs_readdir_descriptor_t 
 	if (IS_ERR(p))
 		return PTR_ERR(p);
 	desc-&gt;ptr = p;
+	if (desc-&gt;timestamp_valid)
+		desc-&gt;entry-&gt;fattr-&gt;time_start = desc-&gt;timestamp;
+	else
+		desc-&gt;entry-&gt;fattr-&gt;valid &amp;= ~NFS_ATTR_FATTR;
 	return 0;
 }
 
@@ -335,6 +343,10 @@ int find_dirent_page(nfs_readdir_descrip
 
 	dfprintk(VFS, "NFS: find_dirent_page() searching directory page %ld\n", desc-&gt;page_index);
 
+	/* If we find the page in the page_cache, we cannot be sure
+	 * how fresh the data is, so we will ignore readdir_plus attributes.
+	 */
+	desc-&gt;timestamp_valid = 0;
 	page = read_cache_page(inode-&gt;i_mapping, desc-&gt;page_index,
 			       (filler_t *)nfs_readdir_filler, desc);
 	if (IS_ERR(page)) {
@@ -535,6 +547,7 @@ int uncached_readdir(nfs_readdir_descrip
 	struct rpc_cred	*cred = nfs_file_cred(file);
 	struct page	*page = NULL;
 	int		status;
+	unsigned long	timestamp;
 
 	dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (unsigned long long)*desc-&gt;dir_cookie);
 
@@ -543,6 +556,7 @@ int uncached_readdir(nfs_readdir_descrip
 		status = -ENOMEM;
 		goto out;
 	}
+	timestamp = jiffies;
 	status = NFS_PROTO(inode)-&gt;readdir(file-&gt;f_dentry, cred,
 						*desc-&gt;dir_cookie, page,
 						NFS_SERVER(inode)-&gt;dtsize,
@@ -553,6 +567,8 @@ int uncached_readdir(nfs_readdir_descrip
 	desc-&gt;page = page;
 	desc-&gt;ptr = kmap(page);		/* matching kunmap in nfs_do_filldir */
 	if (status &gt;= 0) {
+		desc-&gt;timestamp = timestamp;
+		desc-&gt;timestamp_valid = 1;
 		if ((status = dir_decode(desc)) == 0)
 			desc-&gt;entry-&gt;prev_cookie = *desc-&gt;dir_cookie;
 	} else
commit e70c490810dc683fad39e57cf00e69d5f120c542
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Wed May 9 09:00:18 2007 -0400

    NFS: Remove redundant check in nfs_check_verifier()
    
    The check for nfs_attribute_timeout(dir) in nfs_check_verifier is
    redundant: nfs_lookup_revalidate() will already call nfs_revalidate_inode()
    on the parent dir when necessary.
    
    The only case where this is not done is the case of a negative dentry. Fix
    this case by moving up the revalidation code.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c
--- linux-2.6.9/fs/nfs/dir.c	2009-10-22 20:00:55.000000000 -0400
+++ cel-2.6.9/fs/nfs/dir.c	2009-10-22 20:01:43.000000000 -0400
@@ -712,9 +712,7 @@ static inline int nfs_check_verifier(str
 	if (IS_ROOT(dentry))
 		return 1;
 	verf = (unsigned long)dentry-&gt;d_fsdata;
-	if ((NFS_I(dir)-&gt;cache_validity &amp; NFS_INO_INVALID_ATTR) != 0
-			|| nfs_attribute_timeout(dir)
-			|| nfs_caches_unstable(dir)
+	if (nfs_caches_unstable(dir)
 			|| verf != NFS_I(dir)-&gt;cache_change_attribute)
 		return 0;
 	return 1;
@@ -805,6 +803,10 @@ static int nfs_lookup_revalidate(struct 
 	nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE);
 	inode = dentry-&gt;d_inode;
 
+	/* Revalidate parent directory attribute cache */
+	if (nfs_revalidate_inode(NFS_SERVER(dir), dir) &lt; 0)
+		goto out_zap_parent;
+
 	if (!inode) {
 		if (nfs_neg_need_reval(dir, dentry, nd))
 			goto out_bad;
@@ -817,10 +819,6 @@ static int nfs_lookup_revalidate(struct 
 		goto out_bad;
 	}
 
-	/* Revalidate parent directory attribute cache */
-	if (nfs_revalidate_inode(NFS_SERVER(dir), dir) &lt; 0)
-		goto out_zap_parent;
-
 	/* Force a full look up iff the parent directory has changed */
 	if (nfs_check_verifier(dir, dentry)) {
 		if (nfs_lookup_verify_inode(inode, nd))
commit 412c77cee6d6e73fbe1dc3d67f52163efed33fc4
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Tue Jul 3 16:10:55 2007 -0400

    NFSv4: Defer inode revalidation when setting up a delegation
    
    Currently we force a synchronous call to __nfs_revalidate_inode() in
    nfs_inode_set_delegation(). This not only ensures that we cannot call
    nfs_inode_set_delegation from an asynchronous context, but it also slows
    down any call to open().
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

    [ modified to apply on 2.6.9-89 by Chuck Lever &lt;chuck.lever@oracle.com&gt; ]
    [ white space and comments were not exactly the same ]

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/delegation.c cel-2.6.9/fs/nfs/delegation.c
--- linux-2.6.9/fs/nfs/delegation.c	2009-10-23 15:11:41.000000000 -0400
+++ cel-2.6.9/fs/nfs/delegation.c	2009-10-23 15:18:03.000000000 -0400
@@ -84,10 +84,6 @@ int nfs_inode_set_delegation(struct inod
 	struct nfs_delegation *delegation;
 	int status = 0;
 
-	/* Ensure we first revalidate the attributes and page cache! */
-	if ((nfsi-&gt;cache_validity &amp; (NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_ATTR)))
-		__nfs_revalidate_inode(NFS_SERVER(inode), inode);
-
 	delegation = nfs_alloc_delegation();
 	if (delegation == NULL)
 		return -ENOMEM;
@@ -113,6 +109,12 @@ int nfs_inode_set_delegation(struct inod
 			status = -EIO;
 		}
 	}
+
+	/* Ensure we revalidate the attributes and page cache! */
+	spin_lock(&amp;inode-&gt;i_lock);
+	nfsi-&gt;cache_validity |= NFS_INO_REVAL_FORCED;
+	spin_unlock(&amp;inode-&gt;i_lock);
+
 	spin_unlock(&amp;clp-&gt;cl_lock);
 	if (delegation != NULL)
 		kfree(delegation);
@@ -159,6 +161,12 @@ int __nfs_inode_return_delegation(struct
 		nfsi-&gt;delegation = NULL;
 		nfsi-&gt;delegation_state = 0;
 	}
+
+	/* Ensure we revalidate the attributes and page cache! */
+	spin_lock(&amp;inode-&gt;i_lock);
+	nfsi-&gt;cache_validity |= NFS_INO_REVAL_FORCED;
+	spin_unlock(&amp;inode-&gt;i_lock);
+
 	spin_unlock(&amp;clp-&gt;cl_lock);
 	nfs_delegation_claim_opens(inode);
 	up_write(&amp;nfsi-&gt;rwsem);
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-23 15:12:14.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-23 15:12:42.000000000 -0400
@@ -1596,8 +1596,10 @@ static int nfs_update_inode(struct inode
 		invalid &amp;= ~NFS_INO_INVALID_DATA;
 	if (data_stable)
 		invalid &amp;= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME|NFS_INO_REVAL_PAGECACHE);
-	if (!nfs_have_delegation(inode, FMODE_READ))
+	if (!nfs_have_delegation(inode, FMODE_READ) ||
+			(nfsi-&gt;cache_validity &amp; NFS_INO_REVAL_FORCED))
 		nfsi-&gt;cache_validity |= invalid;
+	nfsi-&gt;cache_validity &amp;= ~NFS_INO_REVAL_FORCED;
 
 	return 0;
  out_changed:
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h
--- linux-2.6.9/include/linux/nfs_fs.h	2009-10-23 15:12:14.000000000 -0400
+++ cel-2.6.9/include/linux/nfs_fs.h	2009-10-23 15:16:49.000000000 -0400
@@ -222,6 +222,7 @@ struct nfs_inode {
 #define NFS_INO_INVALID_ACCESS 0x0008      /* cached access cred invalid */
 #define NFS_INO_INVALID_ACL    0x0010      /* cached acls are invalid */
 #define NFS_INO_REVAL_PAGECACHE    0x0020      /* must revalidate pagecache */
+#define NFS_INO_REVAL_FORCED   0x0040      /* force revalidation ignoring a delegation */
 
 /*
  * Legal values of flags field
commit 3062c532ad410fe0e8320566fe2879a396be6701
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Sat Jul 14 17:36:45 2007 -0400

    NFS: Use dentry-&gt;d_time to store the parent directory verifier.
    
    This will free up the d_fsdata field for other use.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c
--- linux-2.6.9/fs/nfs/dir.c	2009-10-22 20:21:04.000000000 -0400
+++ cel-2.6.9/fs/nfs/dir.c	2009-10-22 20:22:10.000000000 -0400
@@ -711,7 +711,7 @@ static inline int nfs_check_verifier(str
 
 	if (IS_ROOT(dentry))
 		return 1;
-	verf = (unsigned long)dentry-&gt;d_fsdata;
+	verf = dentry-&gt;d_time;
 	if (nfs_caches_unstable(dir)
 			|| verf != NFS_I(dir)-&gt;cache_change_attribute)
 		return 0;
@@ -720,7 +720,7 @@ static inline int nfs_check_verifier(str
 
 static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf)
 {
-	dentry-&gt;d_fsdata = (void *)verf;
+	dentry-&gt;d_time = verf;
 }
 
 /*
commit f2115dc9877d480392e48e3c83bc8cbb4b418fee
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Wed Aug 15 12:49:17 2007 -0400

    NFS: Fix over-conservative attribute invalidation in nfs_update_inode()
    
    We should always be declaring the attribute cache as valid after having
    updated it.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-23 15:22:15.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-23 15:23:17.000000000 -0400
@@ -1505,8 +1505,8 @@ static int nfs_update_inode(struct inode
 
 	/* Are we racing with known updates of the metadata on the server? */
 	data_stable = nfs_verify_change_attribute(inode, fattr-&gt;time_start);
-	if (data_stable)
-		nfsi-&gt;cache_validity &amp;= ~(NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_ATIME);
+	nfsi-&gt;cache_validity &amp;= ~(NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ATIME
+			| NFS_INO_REVAL_PAGECACHE);
 
 	/* Do atomic weak cache consistency updates */
 	nfs_wcc_update_inode(inode, fattr);
@@ -1590,12 +1590,11 @@ static int nfs_update_inode(struct inode
 			nfsi-&gt;attrtimeo = NFS_MAXATTRTIMEO(inode);
 		nfsi-&gt;attrtimeo_timestamp = now;
 	}
+	invalid &amp;= ~NFS_INO_INVALID_ATTR;
 	/* Don't invalidate the data if we were to blame */
 	if (!(S_ISREG(inode-&gt;i_mode) || S_ISDIR(inode-&gt;i_mode)
 				|| S_ISLNK(inode-&gt;i_mode)))
 		invalid &amp;= ~NFS_INO_INVALID_DATA;
-	if (data_stable)
-		invalid &amp;= ~(NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME|NFS_INO_REVAL_PAGECACHE);
 	if (!nfs_have_delegation(inode, FMODE_READ) ||
 			(nfsi-&gt;cache_validity &amp; NFS_INO_REVAL_FORCED))
 		nfsi-&gt;cache_validity |= invalid;
commit 68e8a70d3cae23716f6b2b3872eba10eccea148c
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Wed Aug 15 12:59:12 2007 -0400

    NFS: nfs_post_op_update_inode() should call nfs_refresh_inode()
    
    Ensure that we don't clobber the results from a more recent getattr call...
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-23 15:26:24.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-23 15:27:10.000000000 -0400
@@ -1443,14 +1443,14 @@ int nfs_post_op_update_inode(struct inod
 	struct nfs_inode *nfsi = NFS_I(inode);
 	int status = 0;
 
-	spin_lock(&amp;inode-&gt;i_lock);
 	if (unlikely((fattr-&gt;valid &amp; NFS_ATTR_FATTR) == 0)) {
+		spin_lock(&amp;inode-&gt;i_lock);
 		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
+		spin_unlock(&amp;inode-&gt;i_lock);
 		goto out;
 	}
-	status = nfs_update_inode(inode, fattr);
+	status = nfs_refresh_inode(inode, fattr);
 out:
-	spin_unlock(&amp;inode-&gt;i_lock);
 	return status;
 }
 
commit 7957c1418f4b6c66e28d4ac3c4d7a8c19d526c48
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Fri Sep 28 14:20:12 2007 -0400

    NFS: fix nfs_verify_change_attribute
    
    We always want to check that the verifier and directory
    cache_change_attribute match. This also allows us to remove the 'wraparound
    hack' for the cache_change_attribute. If we're only checking for equality,
    then we don't care about wraparound issues.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-22 18:59:01.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-22 19:00:16.000000000 -0400
@@ -1559,10 +1559,6 @@ static int nfs_update_inode(struct inode
 	nfsi-&gt;read_cache_jiffies = fattr-&gt;time_start;
 	nfsi-&gt;last_updated = now;
 
-	/* Fix a wraparound issue with nfsi-&gt;cache_change_attribute */
-	if (time_before(now, nfsi-&gt;cache_change_attribute))
-		nfsi-&gt;cache_change_attribute = now - 600*HZ;
-
 	/* Are we racing with known updates of the metadata on the server? */
 	data_stable = nfs_verify_change_attribute(inode, fattr-&gt;time_start);
 	if (data_stable)
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h
--- linux-2.6.9/include/linux/nfs_fs.h	2009-10-22 18:58:59.000000000 -0400
+++ cel-2.6.9/include/linux/nfs_fs.h	2009-10-22 19:00:31.000000000 -0400
@@ -315,7 +315,7 @@ static inline long nfs_save_change_attri
 static inline int nfs_verify_change_attribute(struct inode *inode, unsigned long chattr)
 {
 	return !nfs_caches_unstable(inode)
-		&amp;&amp; time_after_eq(chattr, NFS_I(inode)-&gt;cache_change_attribute);
+		&amp;&amp; chattr == NFS_I(inode)-&gt;cache_change_attribute;
 }
 
 /*
commit 6ecc5e8fcad7ad64d68c098249359831331bd299
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Fri Sep 28 14:20:33 2007 -0400

    NFS: Fix dcache revalidation bugs
    
    We don't need to force a dentry lookup just because we're making changes to
    the directory.
    
    Don't update nfsi-&gt;cache_change_attribute in nfs_end_data_update: that
    overrides the NFSv3/v4 weak consistency checking that tells us our update
    was the only one, and that tells us the dcache is still valid.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c
--- linux-2.6.9/fs/nfs/dir.c	2009-10-23 21:14:29.000000000 -0400
+++ cel-2.6.9/fs/nfs/dir.c	2009-10-23 21:15:07.000000000 -0400
@@ -707,15 +707,11 @@ int nfs_fsync_dir(struct file *filp, str
  */
 static inline int nfs_check_verifier(struct inode *dir, struct dentry *dentry)
 {
-	unsigned long verf;
-
 	if (IS_ROOT(dentry))
 		return 1;
-	verf = dentry-&gt;d_time;
-	if (nfs_caches_unstable(dir)
-			|| verf != NFS_I(dir)-&gt;cache_change_attribute)
-		return 0;
-	return 1;
+	if (dentry-&gt;d_time == NFS_I(dir)-&gt;cache_change_attribute)
+		return 1;
+	return 0;
 }
 
 static inline void nfs_set_verifier(struct dentry * dentry, unsigned long verf)
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-23 21:14:29.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-23 21:15:07.000000000 -0400
@@ -1320,7 +1320,6 @@ void nfs_end_data_update(struct inode *i
 		nfsi-&gt;cache_validity |= NFS_INO_INVALID_DATA;
 		spin_unlock(&amp;inode-&gt;i_lock);
 	}
-	nfsi-&gt;cache_change_attribute = jiffies;
 	atomic_dec(&amp;nfsi-&gt;data_updates);
 }
 
commit e323ea46d95d7f8c789effd1194dfc120284dbbd
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Sun Sep 30 17:03:25 2007 -0400

    NFS: nfs_wcc_update_inode: directory caches are always invalidated
    
    We must ensure that the readdir data is always invalidated whether or not
    the weak cache consistency data update succeeds.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-22 20:59:02.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-22 21:01:02.000000000 -0400
@@ -1344,6 +1344,8 @@ static void nfs_wcc_update_inode(struct 
 		}
 		if (timespec_equal(&amp;inode-&gt;i_mtime, &amp;fattr-&gt;pre_mtime)) {
 			memcpy(&amp;inode-&gt;i_mtime, &amp;fattr-&gt;mtime, sizeof(inode-&gt;i_mtime));
+			if (S_ISDIR(inode-&gt;i_mode))
+				nfsi-&gt;cache_validity |= NFS_INO_INVALID_DATA;
 			nfsi-&gt;cache_change_attribute = now;
 		}
 		if (inode-&gt;i_size == fattr-&gt;pre_size &amp;&amp; nfsi-&gt;npages == 0) {
commit 17cadc95372e28024be0874e67329c1862912c5d
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Thu Sep 27 10:07:31 2007 -0400

    NFS: Don't force a dcache revalidation if nfs_wcc_update_inode succeeds
    
    The reason is that if the weak cache consistency update was successful,
    then we know that our client must be the only one that changed the
    directory, and we've already updated the dcache to reflect the change.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-23 15:31:00.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-23 15:31:35.000000000 -0400
@@ -1327,24 +1327,18 @@ void nfs_end_data_update(struct inode *i
 static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 {
 	struct nfs_inode *nfsi = NFS_I(inode);
-	unsigned long now = jiffies;
 
 	/* If we have atomic WCC data, we may update some attributes */
 	if ((fattr-&gt;valid &amp; NFS_ATTR_WCC) != 0) {
-		if (timespec_equal(&amp;inode-&gt;i_ctime, &amp;fattr-&gt;pre_ctime)) {
+		if (timespec_equal(&amp;inode-&gt;i_ctime, &amp;fattr-&gt;pre_ctime))
 			memcpy(&amp;inode-&gt;i_ctime, &amp;fattr-&gt;ctime, sizeof(inode-&gt;i_ctime));
-			nfsi-&gt;cache_change_attribute = now;
-		}
 		if (timespec_equal(&amp;inode-&gt;i_mtime, &amp;fattr-&gt;pre_mtime)) {
 			memcpy(&amp;inode-&gt;i_mtime, &amp;fattr-&gt;mtime, sizeof(inode-&gt;i_mtime));
 			if (S_ISDIR(inode-&gt;i_mode))
 				nfsi-&gt;cache_validity |= NFS_INO_INVALID_DATA;
-			nfsi-&gt;cache_change_attribute = now;
 		}
-		if (inode-&gt;i_size == fattr-&gt;pre_size &amp;&amp; nfsi-&gt;npages == 0) {
+		if (inode-&gt;i_size == fattr-&gt;pre_size &amp;&amp; nfsi-&gt;npages == 0)
 			inode-&gt;i_size = fattr-&gt;size;
-			nfsi-&gt;cache_change_attribute = now;
-		}
 	}
 }
 
@@ -1448,6 +1442,7 @@ int nfs_post_op_update_inode(struct inod
 	if (unlikely((fattr-&gt;valid &amp; NFS_ATTR_FATTR) == 0)) {
 		spin_lock(&amp;inode-&gt;i_lock);
 		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
+		nfsi-&gt;cache_change_attribute = jiffies;
 		spin_unlock(&amp;inode-&gt;i_lock);
 		goto out;
 	}
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/nfs4proc.c cel-2.6.9/fs/nfs/nfs4proc.c
--- linux-2.6.9/fs/nfs/nfs4proc.c	2009-10-23 15:31:00.000000000 -0400
+++ cel-2.6.9/fs/nfs/nfs4proc.c	2009-10-23 15:31:35.000000000 -0400
@@ -186,9 +186,12 @@ static void update_changeattr(struct ino
 	struct nfs_inode *nfsi = NFS_I(dir);
 
 	spin_lock(&amp;dir-&gt;i_lock);
-	nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA;
-	if (cinfo-&gt;before == nfsi-&gt;change_attr &amp;&amp; cinfo-&gt;atomic)
+	if (cinfo-&gt;after != nfsi-&gt;change_attr) {
+		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA;
+		if (!cinfo-&gt;atomic || cinfo-&gt;before != nfsi-&gt;change_attr)
+			nfsi-&gt;cache_change_attribute = jiffies;
 		nfsi-&gt;change_attr = cinfo-&gt;after;
+	}
 	spin_unlock(&amp;dir-&gt;i_lock);
 }
 
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h
--- linux-2.6.9/include/linux/nfs_fs.h	2009-10-23 15:31:00.000000000 -0400
+++ cel-2.6.9/include/linux/nfs_fs.h	2009-10-23 15:31:35.000000000 -0400
@@ -272,8 +272,10 @@ static inline void nfs_mark_for_revalida
 
 	spin_lock(&amp;inode-&gt;i_lock);
 	nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS;
-	if (S_ISDIR(inode-&gt;i_mode))
+	if (S_ISDIR(inode-&gt;i_mode)) {
 		nfsi-&gt;cache_validity |= NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA;
+		nfsi-&gt;cache_change_attribute = jiffies;
+	}
 	spin_unlock(&amp;inode-&gt;i_lock);
 }
 
commit 47aabaa7e45385fee4a535a6f6e523ff944e1684
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Thu Sep 27 15:57:24 2007 -0400

    NFSv4: Don't use ctime/mtime for determining when to invalidate the caches
    
    In NFSv4 we should only be looking at the change attribute.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-23 13:40:25.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-23 13:47:59.000000000 -0400
@@ -1510,6 +1510,27 @@ static int nfs_update_inode(struct inode
 	/* Do atomic weak cache consistency updates */
 	nfs_wcc_update_inode(inode, fattr);
 
+	/* More cache consistency checks */
+	if (!(fattr-&gt;valid &amp; NFS_ATTR_FATTR_V4)) {
+		/* NFSv2/v3: Check if the mtime agrees */
+		if (!timespec_equal(&amp;inode-&gt;i_mtime, &amp;fattr-&gt;mtime)) {
+			dprintk("NFS: mtime change on server for file %s/%ld\n",
+					inode-&gt;i_sb-&gt;s_id, inode-&gt;i_ino);
+			invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
+			nfsi-&gt;cache_change_attribute = now;
+		}
+		/* If ctime has changed we should definitely clear access+acl caches */
+		if (!timespec_equal(&amp;inode-&gt;i_ctime, &amp;fattr-&gt;ctime)) {
+			invalid |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
+			nfsi-&gt;cache_change_attribute = now;
+		}
+	} else if (nfsi-&gt;change_attr != fattr-&gt;change_attr) {
+		dprintk("NFS: change_attr change on server for file %s/%ld\n",
+				inode-&gt;i_sb-&gt;s_id, inode-&gt;i_ino);
+		invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
+		nfsi-&gt;cache_change_attribute = now;
+	}
+
 	/* Check if our cached file size is stale */
  	new_isize = nfs_size_to_loff_t(fattr-&gt;size);
 	cur_isize = i_size_read(inode);
@@ -1531,22 +1552,11 @@ static int nfs_update_inode(struct inode
 				inode-&gt;i_sb-&gt;s_id, inode-&gt;i_ino);
 	}
 
-	/* Check if the mtime agrees */
-	if (!timespec_equal(&amp;inode-&gt;i_mtime, &amp;fattr-&gt;mtime)) {
-		memcpy(&amp;inode-&gt;i_mtime, &amp;fattr-&gt;mtime, sizeof(inode-&gt;i_mtime));
-		dprintk("NFS: mtime change on server for file %s/%ld\n",
-				inode-&gt;i_sb-&gt;s_id, inode-&gt;i_ino);
-		invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
-		nfsi-&gt;cache_change_attribute = now;
-	}
 
-	/* If ctime has changed we should definitely clear access+acl caches */
-	if (!timespec_equal(&amp;inode-&gt;i_ctime, &amp;fattr-&gt;ctime)) {
-		invalid |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
-		memcpy(&amp;inode-&gt;i_ctime, &amp;fattr-&gt;ctime, sizeof(inode-&gt;i_ctime));
-		nfsi-&gt;cache_change_attribute = now;
-	}
+	memcpy(&amp;inode-&gt;i_mtime, &amp;fattr-&gt;mtime, sizeof(inode-&gt;i_mtime));
+	memcpy(&amp;inode-&gt;i_ctime, &amp;fattr-&gt;ctime, sizeof(inode-&gt;i_ctime));
 	memcpy(&amp;inode-&gt;i_atime, &amp;fattr-&gt;atime, sizeof(inode-&gt;i_atime));
+	nfsi-&gt;change_attr = fattr-&gt;change_attr;
 
 	if ((inode-&gt;i_mode &amp; S_IALLUGO) != (fattr-&gt;mode &amp; S_IALLUGO) ||
 	    inode-&gt;i_uid != fattr-&gt;uid ||
@@ -1569,15 +1579,6 @@ static int nfs_update_inode(struct inode
  		inode-&gt;i_blksize = fattr-&gt;du.nfs2.blocksize;
  	}
 
-	if ((fattr-&gt;valid &amp; NFS_ATTR_FATTR_V4) != 0 &amp;&amp;
-			nfsi-&gt;change_attr != fattr-&gt;change_attr) {
-		dprintk("NFS: change_attr change on server for file %s/%ld\n",
-				inode-&gt;i_sb-&gt;s_id, inode-&gt;i_ino);
-		nfsi-&gt;change_attr = fattr-&gt;change_attr;
-		invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
-		nfsi-&gt;cache_change_attribute = now;
-	}
-
 	/* Update attrtimeo value if we're out of the unstable period */
 	if (invalid &amp; NFS_INO_INVALID_ATTR) {
 		nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
commit b64e8a5ef758888cb42b7c105dcfaaf51aab1baf
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Sun Sep 30 15:13:17 2007 -0400

    NFS: Remove bogus check of cache_change_attribute in nfs_update_inode
    
    Remove the bogus 'data_stable' check in nfs_update_inode. The
    cache_change_attribute tells you if the directory changed on the server,
    and should have nothing to do with the file length.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-23 13:55:59.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-23 14:00:22.000000000 -0400
@@ -1475,7 +1475,6 @@ static int nfs_update_inode(struct inode
 	loff_t cur_isize, new_isize;
 	unsigned int	invalid = 0;
 	unsigned long now = jiffies;
-	int data_stable;
 
 	dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n",
 			__FUNCTION__, inode-&gt;i_sb-&gt;s_id, inode-&gt;i_ino,
@@ -1502,8 +1501,6 @@ static int nfs_update_inode(struct inode
 	nfsi-&gt;read_cache_jiffies = fattr-&gt;time_start;
 	nfsi-&gt;last_updated = now;
 
-	/* Are we racing with known updates of the metadata on the server? */
-	data_stable = nfs_verify_change_attribute(inode, fattr-&gt;time_start);
 	nfsi-&gt;cache_validity &amp;= ~(NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ATIME
 			| NFS_INO_REVAL_PAGECACHE);
 
@@ -1535,15 +1532,9 @@ static int nfs_update_inode(struct inode
  	new_isize = nfs_size_to_loff_t(fattr-&gt;size);
 	cur_isize = i_size_read(inode);
 	if (new_isize != cur_isize) {
-		/* Do we perhaps have any outstanding writes? */
-		if (nfsi-&gt;npages == 0) {
-			/* No, but did we race with nfs_end_data_update()? */
-			if (data_stable) {
-				inode-&gt;i_size = new_isize;
-				invalid |= NFS_INO_INVALID_DATA;
-			}
-			invalid |= NFS_INO_INVALID_ATTR;
-		} else if (new_isize &gt; cur_isize) {
+		/* Do we perhaps have any outstanding writes, or has
+		 * the file grown beyond our last write? */
+		if (nfsi-&gt;npages == 0 || new_isize &gt; cur_isize) {
 			inode-&gt;i_size = new_isize;
 			invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
 		}
commit 4b841736bc16b320bcdb1e8ece585b3ced9a8811
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Sat Sep 29 17:15:01 2007 -0400

    NFS: Fix nfs_verify_change_attribute()
    
    We don't care about whether or not some other process on our client is
    changing the directory while we're in nfs_lookup_revalidate(), because the
    dcache will take care of ensuring local atomicity.
    We can therefore remove the test for nfs_caches_unstable().
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c
--- linux-2.6.9/fs/nfs/dir.c	2009-10-23 21:22:17.000000000 -0400
+++ cel-2.6.9/fs/nfs/dir.c	2009-10-23 21:22:51.000000000 -0400
@@ -709,7 +709,7 @@ static inline int nfs_check_verifier(str
 {
 	if (IS_ROOT(dentry))
 		return 1;
-	if (dentry-&gt;d_time == NFS_I(dir)-&gt;cache_change_attribute)
+	if (nfs_verify_change_attribute(dir, dentry-&gt;d_time))
 		return 1;
 	return 0;
 }
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h
--- linux-2.6.9/include/linux/nfs_fs.h	2009-10-23 21:22:17.000000000 -0400
+++ cel-2.6.9/include/linux/nfs_fs.h	2009-10-23 21:23:22.000000000 -0400
@@ -313,16 +313,16 @@ static inline long nfs_save_change_attri
 }
 
 /**
- * nfs_verify_change_attribute - Detects NFS inode cache updates
- * @inode - pointer to inode
+ * nfs_verify_change_attribute - Detects NFS remote directory changes
+ * @dir - pointer to parent directory inode
  * @chattr - previously saved change attribute
- * Return "false" if metadata has been updated (or is in the process of
- * being updated) since the change attribute was saved.
+ * Return "false" if the verifiers doesn't match the change attribute.
+ * This would usually indicate that the directory contents have changed on
+ * the server, and that any dentries need revalidating.
  */
-static inline int nfs_verify_change_attribute(struct inode *inode, unsigned long chattr)
+static inline int nfs_verify_change_attribute(struct inode *dir, unsigned long chattr)
 {
-	return !nfs_caches_unstable(inode)
-		&amp;&amp; chattr == NFS_I(inode)-&gt;cache_change_attribute;
+ 	return chattr == NFS_I(dir)-&gt;cache_change_attribute;
 }
 
 /*
commit 2f78e4313afd34a4ded591ec5687843113fbaa01
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Sun Sep 30 15:31:19 2007 -0400

    NFS: Don't set cache_change_attribute in nfs_revalidate_mapping
    
    The attribute revalidation code will already have taken care of resetting
    nfsi-&gt;cache_change_attribute.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

    [ modified to apply on 2.6.9-89 by Chuck Lever &lt;chuck.lever@oracle.com&gt; ]
    [ 2.6.9-89 missing commit 717d44e8 and pre-requisites ]

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-11-17 16:32:06.000000000 -0500
+++ cel-2.6.9/fs/nfs/inode.c	2009-11-17 16:33:38.000000000 -0500
@@ -1280,11 +1280,8 @@ void nfs_revalidate_mapping(struct inode
 
 		spin_lock(&amp;inode-&gt;i_lock);
 		nfsi-&gt;cache_validity &amp;= ~NFS_INO_INVALID_DATA;
-		if (S_ISDIR(inode-&gt;i_mode)) {
+		if (S_ISDIR(inode-&gt;i_mode))
 			memset(nfsi-&gt;cookieverf, 0, sizeof(nfsi-&gt;cookieverf));
-			/* This ensures we revalidate child dentries */
-			nfsi-&gt;cache_change_attribute = jiffies;
-		}
 		spin_unlock(&amp;inode-&gt;i_lock);
 
 		dfprintk(PAGECACHE, "NFS: (%s/%Ld) data cache invalidated\n",
commit 12b373ebf05485d4937dd63a00c16f8efeaa79ba
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Mon Oct 1 09:56:59 2007 -0400

    NFS: Don't revalidate dentries on directory size or ctime changes
    
    We only need to look at the mtime changes...
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-23 14:02:50.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-23 14:11:08.000000000 -0400
@@ -1517,10 +1517,8 @@ static int nfs_update_inode(struct inode
 			nfsi-&gt;cache_change_attribute = now;
 		}
 		/* If ctime has changed we should definitely clear access+acl caches */
-		if (!timespec_equal(&amp;inode-&gt;i_ctime, &amp;fattr-&gt;ctime)) {
+		if (!timespec_equal(&amp;inode-&gt;i_ctime, &amp;fattr-&gt;ctime))
 			invalid |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
-			nfsi-&gt;cache_change_attribute = now;
-		}
 	} else if (nfsi-&gt;change_attr != fattr-&gt;change_attr) {
 		dprintk("NFS: change_attr change on server for file %s/%ld\n",
 				inode-&gt;i_sb-&gt;s_id, inode-&gt;i_ino);
@@ -1538,7 +1536,6 @@ static int nfs_update_inode(struct inode
 			inode-&gt;i_size = new_isize;
 			invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
 		}
-		nfsi-&gt;cache_change_attribute = now;
 		dprintk("NFS: isize change on server for file %s/%ld\n",
 				inode-&gt;i_sb-&gt;s_id, inode-&gt;i_ino);
 	}
commit 7668fdbe9aaeab705d1169ac86d0d18a12906d06
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Mon Oct 1 09:59:15 2007 -0400

    NFS: nfs_post_op_update_inode don't update cache_change_attribute
    
    If nfs_post_op_update_inode fails because the server didn't return any
    attributes, then we let the subsequent inode revalidation update
    cache_change_attribute.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-23 15:34:21.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-23 16:16:13.000000000 -0400
@@ -1436,18 +1436,14 @@ int nfs_refresh_inode(struct inode *inod
 int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 {
 	struct nfs_inode *nfsi = NFS_I(inode);
-	int status = 0;
 
-	if (unlikely((fattr-&gt;valid &amp; NFS_ATTR_FATTR) == 0)) {
-		spin_lock(&amp;inode-&gt;i_lock);
-		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
-		nfsi-&gt;cache_change_attribute = jiffies;
-		spin_unlock(&amp;inode-&gt;i_lock);
-		goto out;
-	}
-	status = nfs_refresh_inode(inode, fattr);
-out:
-	return status;
+	if (fattr-&gt;valid &amp; NFS_ATTR_FATTR)
+		return nfs_refresh_inode(inode, fattr);
+
+	spin_lock(&amp;inode-&gt;i_lock);
+	nfsi-&gt;cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
+	spin_unlock(&amp;inode-&gt;i_lock);
+	return 0;
 }
 
 /*
commit f38211100d4823be530577dc3452f838861222ec
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Mon Oct 1 10:00:23 2007 -0400

    NFS: nfs_mark_for_revalidate don't update cache_change_attribute
    
    Just let the subsequent inode revalidation do the update...
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h
--- linux-2.6.9/include/linux/nfs_fs.h	2009-11-17 16:40:45.000000000 -0500
+++ cel-2.6.9/include/linux/nfs_fs.h	2009-11-17 16:41:31.000000000 -0500
@@ -272,10 +272,8 @@ static inline void nfs_mark_for_revalida
 
 	spin_lock(&amp;inode-&gt;i_lock);
 	nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS;
-	if (S_ISDIR(inode-&gt;i_mode)) {
+	if (S_ISDIR(inode-&gt;i_mode))
 		nfsi-&gt;cache_validity |= NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA;
-		nfsi-&gt;cache_change_attribute = jiffies;
-	}
 	spin_unlock(&amp;inode-&gt;i_lock);
 }
 
commit a1643a92f6de92074116922a2d2906dd33499ff4
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Sat Sep 29 17:25:43 2007 -0400

    NFS: NFS_CACHEINV() should not test for nfs_caches_unstable()
    
    The fact that we're in the process of modifying the inode does not mean
    that we should not invalidate the attribute and data caches. The defensive
    thing is to always invalidate when we're confronted with inode
    mtime/ctime or change_attribute updates that we do not immediately
    recognise.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

    [ modified to apply on 2.6.9-89 by Chuck Lever &lt;chuck.lever@oracle.com&gt; ]
    [ commit 864472e9 does not apply to 2.6.9-89 ]

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c
--- linux-2.6.9/fs/nfs/dir.c	2009-10-27 15:03:46.000000000 -0400
+++ cel-2.6.9/fs/nfs/dir.c	2009-10-27 15:04:59.000000000 -0400
@@ -874,7 +874,7 @@ static int nfs_lookup_revalidate(struct 
 out_zap_parent:
 	nfs_zap_caches(dir);
  out_bad:
-	NFS_CACHEINV(dir);
+	nfs_mark_for_revalidate(dir);
 	if (inode &amp;&amp; S_ISDIR(inode-&gt;i_mode)) {
 		/* Purge readdir caches. */
 		nfs_zap_caches(inode);
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/nfs4proc.c cel-2.6.9/fs/nfs/nfs4proc.c
--- linux-2.6.9/fs/nfs/nfs4proc.c	2009-10-27 15:03:46.000000000 -0400
+++ cel-2.6.9/fs/nfs/nfs4proc.c	2009-10-27 15:05:12.000000000 -0400
@@ -246,8 +246,6 @@ static int _nfs4_open_reclaim(struct nfs
 		}
 	}
 	clear_bit(NFS_DELEGATED_STATE, &amp;state-&gt;flags);
-	/* Ensure we update the inode attributes */
-	NFS_CACHEINV(inode);
 	return status;
 }
 
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h
--- linux-2.6.9/include/linux/nfs_fs.h	2009-10-27 15:03:46.000000000 -0400
+++ cel-2.6.9/include/linux/nfs_fs.h	2009-10-27 15:04:59.000000000 -0400
@@ -279,12 +279,6 @@ static inline void nfs_mark_for_revalida
 	spin_unlock(&amp;inode-&gt;i_lock);
 }
 
-static inline void NFS_CACHEINV(struct inode *inode)
-{
-	if (!nfs_caches_unstable(inode))
-		nfs_mark_for_revalidate(inode);
-}
-
 static inline int nfs_server_capable(struct inode *inode, int cap)
 {
 	return NFS_SERVER(inode)-&gt;caps &amp; cap;
commit 80eb209def76d375677840800eb838abce1e6639
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Sat Sep 29 17:34:46 2007 -0400

    NFS: Remove NFS_I(inode)-&gt;data_updates
    
    We have no more users...
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

    [ modified to apply on 2.6.9-89 by Chuck Lever &lt;chuck.lever@oracle.com&gt; ]
    [ 2.6.9-89 is missing commits 2aefa104 and da6d503a;
      2.6.9-89 still caches NFS LOOKUP results ]

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c
--- linux-2.6.9/fs/nfs/dir.c	2009-10-27 15:42:19.000000000 -0400
+++ cel-2.6.9/fs/nfs/dir.c	2009-10-27 15:43:29.000000000 -0400
@@ -1256,9 +1256,7 @@ int nfs_cached_lookup(struct inode *dir,
 		return -ENOENT;
 	server = NFS_SERVER(dir);
 	/* Don't use readdirplus unless the cache is stable */
-	if ((server-&gt;flags &amp; NFS_MOUNT_NOAC) != 0
-			|| nfs_caches_unstable(dir)
-			|| nfs_attribute_timeout(dir))
+	if (nfs_attribute_timeout(dir))
 		return -ENOENT;
 	if ((NFS_I(dir)-&gt;cache_validity &amp; (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA)) != 0)
 		return -ENOENT;
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-27 15:42:19.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-27 15:42:50.000000000 -0400
@@ -195,7 +195,6 @@ nfs_clear_inode(struct inode *inode)
 
 	nfs_wb_all(inode);
 	BUG_ON (!list_empty(&amp;nfsi-&gt;open_files));
-	BUG_ON(atomic_read(&amp;nfsi-&gt;data_updates) != 0);
 	nfs_zap_acl_cache(inode);
 	nfs_access_zap_cache(inode);
 }
@@ -1294,16 +1293,6 @@ void nfs_revalidate_mapping(struct inode
 }
 
 /**
- * nfs_begin_data_update
- * @inode - pointer to inode
- * Declare that a set of operations will update file data on the server
- */
-void nfs_begin_data_update(struct inode *inode)
-{
-	atomic_inc(&amp;NFS_I(inode)-&gt;data_updates);
-}
-
-/**
  * nfs_end_data_update
  * @inode - pointer to inode
  * Declare end of the operations that will update file data
@@ -1312,15 +1301,12 @@ void nfs_begin_data_update(struct inode 
  */
 void nfs_end_data_update(struct inode *inode)
 {
-	struct nfs_inode *nfsi = NFS_I(inode);
-
 	/* Directories: invalidate page cache */
 	if (S_ISDIR(inode-&gt;i_mode)) {
 		spin_lock(&amp;inode-&gt;i_lock);
-		nfsi-&gt;cache_validity |= NFS_INO_INVALID_DATA;
+		NFS_I(inode)-&gt;cache_validity |= NFS_INO_INVALID_DATA;
 		spin_unlock(&amp;inode-&gt;i_lock);
 	}
-	atomic_dec(&amp;nfsi-&gt;data_updates);
 }
 
 static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
@@ -1354,7 +1340,6 @@ static int nfs_check_inode_attributes(st
 {
 	struct nfs_inode *nfsi = NFS_I(inode);
 	loff_t cur_size, new_isize;
-	int data_unstable;
 
 	/* Has the inode gone and changed behind our back? */
 	if (nfsi-&gt;fileid != fattr-&gt;fileid
@@ -1362,9 +1347,6 @@ static int nfs_check_inode_attributes(st
 		return -EIO;
 	}
 
-	/* Are we in the process of updating data on the server? */
-	data_unstable = nfs_caches_unstable(inode);
-
 	/* Do atomic weak cache consistency updates */
 	nfs_wcc_update_inode(inode, fattr);
 
@@ -2144,7 +2126,6 @@ static void init_once(void * foo, kmem_c
 		INIT_LIST_HEAD(&amp;nfsi-&gt;access_cache_entry_lru);
 		INIT_LIST_HEAD(&amp;nfsi-&gt;access_cache_inode_lru);
 		INIT_RADIX_TREE(&amp;nfsi-&gt;nfs_page_tree, GFP_ATOMIC);
-		atomic_set(&amp;nfsi-&gt;data_updates, 0);
 		nfsi-&gt;ndirty = 0;
 		nfsi-&gt;ncommit = 0;
 		nfsi-&gt;npages = 0;
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h
--- linux-2.6.9/include/linux/nfs_fs.h	2009-10-27 15:42:19.000000000 -0400
+++ cel-2.6.9/include/linux/nfs_fs.h	2009-10-27 15:42:50.000000000 -0400
@@ -165,11 +165,6 @@ struct nfs_inode {
 	 * server.
 	 */
 	unsigned long		cache_change_attribute;
-	/*
-	 * Counter indicating the number of outstanding requests that
-	 * will cause a file data update.
-	 */
-	atomic_t		data_updates;
 
 	struct rb_root		access_cache;
 	struct list_head	access_cache_entry_lru;
@@ -261,9 +256,13 @@ static inline struct nfs_inode *NFS_I(st
 
 #define NFS_FILEID(inode)		(NFS_I(inode)-&gt;fileid)
 
-static inline int nfs_caches_unstable(struct inode *inode)
+/**
+ * nfs_begin_data_update
+ * @inode - pointer to inode
+ * Declare that a set of operations will update file data on the server
+ */
+static inline void nfs_begin_data_update(struct inode *inode)
 {
-	return atomic_read(&amp;NFS_I(inode)-&gt;data_updates) != 0;
 }
 
 static inline void nfs_mark_for_revalidate(struct inode *inode)
@@ -343,7 +342,6 @@ extern int nfs_setattr(struct dentry *, 
 extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr);
 extern void nfs_begin_attr_update(struct inode *);
 extern void nfs_end_attr_update(struct inode *);
-extern void nfs_begin_data_update(struct inode *);
 extern void nfs_end_data_update(struct inode *);
 extern void nfs_end_data_update_defer(struct inode *);
 extern struct nfs_open_context *alloc_nfs_open_context(struct vfsmount *mnt, struct dentry *dentry, struct rpc_cred *cred);
commit 60ccd4ec4170c9487e3792322626acd160197bce
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Sat Sep 29 17:48:19 2007 -0400

    NFS: Remove nfs_begin_data_update/nfs_end_data_update
    
    The lower level routines in fs/nfs/proc.c, fs/nfs/nfs3proc.c and
    fs/nfs/nfs4proc.c should already be dealing with the revalidation issues.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

    [ modified to apply on 2.6.9-89 by Chuck Lever &lt;chuck.lever@oracle.com&gt; ]
    [ 2.6.9-89 missing commit 02a913a7 (361 lines);
      2.6.9-89 missing incompatible changes to symlink, direct write;
      2.6.9-89 missing incompatible changes to async unlink (pre-git) ]

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c
--- linux-2.6.9/fs/nfs/dir.c	2009-10-23 17:11:54.000000000 -0400
+++ cel-2.6.9/fs/nfs/dir.c	2009-10-23 17:24:48.000000000 -0400
@@ -1060,12 +1060,7 @@ static struct dentry *nfs_atomic_lookup(
 		goto out;
 	}
 
-	if (nd-&gt;intent.open.flags &amp; O_CREAT) {
-		nfs_begin_data_update(dir);
-		inode = nfs4_atomic_open(dir, dentry, nd);
-		nfs_end_data_update(dir);
-	} else
-		inode = nfs4_atomic_open(dir, dentry, nd);
+	inode = nfs4_atomic_open(dir, dentry, nd);
 	unlock_kernel();
 	if (IS_ERR(inode)) {
 		error = PTR_ERR(inode);
@@ -1344,9 +1339,7 @@ static int nfs_create(struct inode *dir,
 	 * does not pass the create flags.
 	 */
 	lock_kernel();
-	nfs_begin_data_update(dir);
 	error = NFS_PROTO(dir)-&gt;create(dir, dentry, &amp;attr, open_flags);
-	nfs_end_data_update(dir);
 	if (error != 0)
 		goto out_err;
 	nfs_renew_times(dentry);
@@ -1379,9 +1372,7 @@ nfs_mknod(struct inode *dir, struct dent
 	attr.ia_valid = ATTR_MODE;
 
 	lock_kernel();
-	nfs_begin_data_update(dir);
 	status = NFS_PROTO(dir)-&gt;mknod(dir, dentry, &amp;attr, rdev);
-	nfs_end_data_update(dir);
 	if (status != 0)
 		goto out_err;
 	nfs_renew_times(dentry);
@@ -1409,9 +1400,7 @@ static int nfs_mkdir(struct inode *dir, 
 	attr.ia_mode = mode | S_IFDIR;
 
 	lock_kernel();
-	nfs_begin_data_update(dir);
 	error = NFS_PROTO(dir)-&gt;mkdir(dir, dentry, &amp;attr);
-	nfs_end_data_update(dir);
 	if (error != 0)
 		goto out_err;
 	nfs_renew_times(dentry);
@@ -1433,12 +1422,10 @@ static int nfs_rmdir(struct inode *dir, 
 		dir-&gt;i_ino, dentry-&gt;d_name.name);
 
 	lock_kernel();
-	nfs_begin_data_update(dir);
 	error = NFS_PROTO(dir)-&gt;rmdir(dir, &amp;dentry-&gt;d_name);
 	/* Ensure the VFS deletes this inode */
 	if (error == 0 &amp;&amp; dentry-&gt;d_inode != NULL)
 		dentry-&gt;d_inode-&gt;i_nlink = 0;
-	nfs_end_data_update(dir);
 	unlock_kernel();
 
 	return error;
@@ -1501,16 +1488,12 @@ dentry-&gt;d_parent-&gt;d_name.name, dentry-&gt;d
 
 	qsilly.name = silly;
 	qsilly.len  = strlen(silly);
-	nfs_begin_data_update(dir);
-	if (dentry-&gt;d_inode) {
-		nfs_begin_data_update(dentry-&gt;d_inode);
+	if (dentry-&gt;d_inode)
 		error = NFS_PROTO(dir)-&gt;rename(dir, &amp;dentry-&gt;d_name,
 				dir, &amp;qsilly);
-		nfs_end_data_update(dentry-&gt;d_inode);
-	} else
+	else
 		error = NFS_PROTO(dir)-&gt;rename(dir, &amp;dentry-&gt;d_name,
 				dir, &amp;qsilly);
-	nfs_end_data_update(dir);
 	if (!error) {
 		nfs_renew_times(dentry);
 		nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
@@ -1545,18 +1528,14 @@ static int nfs_safe_remove(struct 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)-&gt;remove(dir, &amp;dentry-&gt;d_name);
 		/* The VFS may want to delete this inode */
 		if (error == 0)
 			inode-&gt;i_nlink--;
-		nfs_end_data_update(inode);
 	} else
 		error = NFS_PROTO(dir)-&gt;remove(dir, &amp;dentry-&gt;d_name);
-	nfs_end_data_update(dir);
 out:
 	return error;
 }
@@ -1628,10 +1607,8 @@ dentry-&gt;d_parent-&gt;d_name.name, dentry-&gt;d
 	qsymname.len  = strlen(symname);
 
 	lock_kernel();
-	nfs_begin_data_update(dir);
 	error = NFS_PROTO(dir)-&gt;symlink(dir, &amp;dentry-&gt;d_name, &amp;qsymname,
 					  &amp;attr, &amp;sym_fh, &amp;sym_attr);
-	nfs_end_data_update(dir);
 	if (!error) {
 		error = nfs_instantiate(dentry, &amp;sym_fh, &amp;sym_attr);
 	} else {
@@ -1663,11 +1640,7 @@ nfs_link(struct dentry *old_dentry, stru
 	lock_kernel();
 	d_drop(dentry);
 
-	nfs_begin_data_update(dir);
-	nfs_begin_data_update(inode);
 	error = NFS_PROTO(dir)-&gt;link(inode, dir, &amp;dentry-&gt;d_name);
-	nfs_end_data_update(inode);
-	nfs_end_data_update(dir);
 	unlock_kernel();
 	return error;
 }
@@ -1776,14 +1749,8 @@ 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)-&gt;rename(old_dir, &amp;old_dentry-&gt;d_name,
 					   new_dir, &amp;new_dentry-&gt;d_name);
-	nfs_end_data_update(old_inode);
-	nfs_end_data_update(new_dir);
-	nfs_end_data_update(old_dir);
 out:
 	if (rehash)
 		d_rehash(rehash);
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/direct.c cel-2.6.9/fs/nfs/direct.c
--- linux-2.6.9/fs/nfs/direct.c	2009-10-23 17:11:30.000000000 -0400
+++ cel-2.6.9/fs/nfs/direct.c	2009-10-23 17:17:16.000000000 -0400
@@ -446,8 +446,6 @@ static void nfs_direct_write_result(stru
 	}
 
 	spin_unlock(&amp;dreq-&gt;lock);
-
-	nfs_end_data_update(data-&gt;inode);
 	nfs_direct_complete(dreq);
 }
 
@@ -541,8 +539,6 @@ static ssize_t nfs_direct_write(struct k
 	if (!is_sync_kiocb(iocb))
 		dreq-&gt;iocb = iocb;
 
-	nfs_begin_data_update(inode);
-
 	rpc_clnt_sigmask(clnt, &amp;oldset);
 	nfs_direct_write_schedule(dreq, user_addr, count, pos);
 	result = nfs_direct_wait(dreq, clnt-&gt;cl_intr);
@@ -702,10 +698,6 @@ ssize_t nfs_file_direct_write(struct kio
 					pos, pages, page_count);
 
 	/*
-	 * XXX: nfs_end_data_update() already ensures this file's
-	 *      cached data is subsequently invalidated.  Do we really
-	 *      need to call invalidate_inode_pages2() again here?
-	 *
 	 *      For aio writes, this invalidation will almost certainly
 	 *      occur before the writes complete.  Kind of racey.
 	 */
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-23 17:11:54.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-23 17:18:06.000000000 -0400
@@ -948,7 +948,6 @@ nfs_setattr(struct dentry *dentry, struc
 		return 0;
 
 	lock_kernel();
-	nfs_begin_data_update(inode);
 	/* Write all dirty data */
 	if (S_ISREG(inode-&gt;i_mode)) {
 		filemap_write_and_wait(inode-&gt;i_mapping);
@@ -957,7 +956,6 @@ nfs_setattr(struct dentry *dentry, struc
 	error = NFS_PROTO(inode)-&gt;setattr(dentry, &amp;fattr, attr);
 	if (error == 0)
 		nfs_refresh_inode(inode, &amp;fattr);
-	nfs_end_data_update(inode);
 	unlock_kernel();
 	return error;
 }
@@ -1292,23 +1290,6 @@ void nfs_revalidate_mapping(struct inode
 	}
 }
 
-/**
- * nfs_end_data_update
- * @inode - pointer to inode
- * Declare end of the operations that will update file data
- * This will mark the inode as immediately needing revalidation
- * of its attribute cache.
- */
-void nfs_end_data_update(struct inode *inode)
-{
-	/* Directories: invalidate page cache */
-	if (S_ISDIR(inode-&gt;i_mode)) {
-		spin_lock(&amp;inode-&gt;i_lock);
-		NFS_I(inode)-&gt;cache_validity |= NFS_INO_INVALID_DATA;
-		spin_unlock(&amp;inode-&gt;i_lock);
-	}
-}
-
 static void nfs_wcc_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 {
 	struct nfs_inode *nfsi = NFS_I(inode);
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/nfs3acl.c cel-2.6.9/fs/nfs/nfs3acl.c
--- linux-2.6.9/fs/nfs/nfs3acl.c	2009-10-23 17:11:50.000000000 -0400
+++ cel-2.6.9/fs/nfs/nfs3acl.c	2009-10-23 17:19:03.000000000 -0400
@@ -307,13 +307,11 @@ static int nfs3_proc_setacls(struct inod
 
 	dprintk("NFS call setacl\n");
 	nfs_fattr_init(&amp;fattr);
-	nfs_begin_data_update(inode);
 	status = rpc_call(server-&gt;client_acl, ACLPROC3_SETACL,
 			  &amp;args, &amp;fattr, 0);
 	spin_lock(&amp;inode-&gt;i_lock);
 	NFS_I(inode)-&gt;cache_validity |= NFS_INO_INVALID_ACCESS;
 	spin_unlock(&amp;inode-&gt;i_lock);
-	nfs_end_data_update(inode);
 	dprintk("NFS reply setacl: %d\n", status);
 
 	/* pages may have been allocated at the xdr layer. */
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/unlink.c cel-2.6.9/fs/nfs/unlink.c
--- linux-2.6.9/fs/nfs/unlink.c	2009-10-23 17:11:30.000000000 -0400
+++ cel-2.6.9/fs/nfs/unlink.c	2009-10-23 17:20:05.000000000 -0400
@@ -104,11 +104,6 @@ nfs_async_unlink_init(struct rpc_task *t
 	status = NFS_PROTO(dir-&gt;d_inode)-&gt;unlink_setup(&amp;msg, dir, &amp;data-&gt;name);
 	if (status &lt; 0)
 		goto out_err;
-	/*
-	 * We should really have surrounded the RPC call with a call to
-	 * nfs_begin_data_update()/nfs_end_data_update(), but that may
-	 * cause problems at umount time.
-	 */
 	rpc_call_setup(task, &amp;msg, 0);
 	return;
  out_err:
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/write.c cel-2.6.9/fs/nfs/write.c
--- linux-2.6.9/fs/nfs/write.c	2009-10-23 17:11:35.000000000 -0400
+++ cel-2.6.9/fs/nfs/write.c	2009-10-23 17:21:03.000000000 -0400
@@ -175,7 +175,6 @@ static int nfs_writepage_sync(struct nfs
 		(long long)NFS_FILEID(inode),
 		count, (long long)(page_offset(page) + offset));
 
-	nfs_begin_data_update(inode);
 	do {
 		if (count &lt; wsize)
 			wdata-&gt;args.count = count;
@@ -207,8 +206,6 @@ static int nfs_writepage_sync(struct nfs
 		ClearPageError(page);
 
 io_error:
-	nfs_end_data_update(inode);
-
 	kfree(wdata);
 	return written ? written : result;
 }
@@ -381,7 +378,6 @@ static int nfs_inode_add_request(struct 
 		return error;
 	if (!nfsi-&gt;npages) {
 		igrab(inode);
-		nfs_begin_data_update(inode);
 		if (nfs_have_delegation(inode, FMODE_WRITE))
 			nfsi-&gt;change_attr++;
 	}
@@ -405,7 +401,6 @@ static void nfs_inode_remove_request(str
 	nfsi-&gt;npages--;
 	if (!nfsi-&gt;npages) {
 		spin_unlock(&amp;nfsi-&gt;req_lock);
-		nfs_end_data_update(inode);
 		iput(inode);
 	} else
 		spin_unlock(&amp;nfsi-&gt;req_lock);
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h
--- linux-2.6.9/include/linux/nfs_fs.h	2009-10-23 17:11:54.000000000 -0400
+++ cel-2.6.9/include/linux/nfs_fs.h	2009-10-23 17:29:43.000000000 -0400
@@ -256,15 +256,6 @@ static inline struct nfs_inode *NFS_I(st
 
 #define NFS_FILEID(inode)		(NFS_I(inode)-&gt;fileid)
 
-/**
- * nfs_begin_data_update
- * @inode - pointer to inode
- * Declare that a set of operations will update file data on the server
- */
-static inline void nfs_begin_data_update(struct inode *inode)
-{
-}
-
 static inline void nfs_mark_for_revalidate(struct inode *inode)
 {
 	struct nfs_inode *nfsi = NFS_I(inode);
@@ -346,10 +337,6 @@ extern int __nfs_revalidate_inode(struct
 extern void nfs_revalidate_mapping(struct inode *inode, struct address_space *mapping);
 extern int nfs_setattr(struct dentry *, struct iattr *);
 extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr);
-extern void nfs_begin_attr_update(struct inode *);
-extern void nfs_end_attr_update(struct inode *);
-extern void nfs_end_data_update(struct inode *);
-extern void nfs_end_data_update_defer(struct inode *);
 extern struct nfs_open_context *alloc_nfs_open_context(struct vfsmount *mnt, struct dentry *dentry, struct rpc_cred *cred);
 extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx);
 extern void put_nfs_open_context(struct nfs_open_context *ctx);
commit 6d2b2966869142660f46d1e06cf9d15c3debcf77
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Mon Oct 1 18:57:50 2007 -0400

    NFS: Reset nfsi-&gt;last_updated only if the attribute changed
    
    Otherwise set it to nfsi-&gt;read_cache_jiffies in order to prevent jiffy
    wraparound issues.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

    [ modified to apply on 2.6.9-89 by Chuck Lever &lt;chuck.lever@oracle.com&gt; ]
    [ commit 64672d55 (12/23/08) already applied to 2.6.9-89 ]

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-26 19:52:19.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-26 19:54:20.000000000 -0400
@@ -1470,7 +1470,6 @@ static int nfs_update_inode(struct inode
 	 * Update the read time so we don't revalidate too often.
 	 */
 	nfsi-&gt;read_cache_jiffies = fattr-&gt;time_start;
-	nfsi-&gt;last_updated = now;
 
 	nfsi-&gt;cache_validity &amp;= ~(NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ATIME
 			| NFS_INO_REVAL_PAGECACHE);
@@ -1543,11 +1542,18 @@ static int nfs_update_inode(struct inode
 		nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
 		nfsi-&gt;attrtimeo = NFS_MINATTRTIMEO(inode);
 		nfsi-&gt;attrtimeo_timestamp = now;
-	} else if (!time_in_range_open(now, nfsi-&gt;attrtimeo_timestamp,
-				nfsi-&gt;attrtimeo_timestamp + nfsi-&gt;attrtimeo)) {
-		if ((nfsi-&gt;attrtimeo &lt;&lt;= 1) &gt; NFS_MAXATTRTIMEO(inode))
-			nfsi-&gt;attrtimeo = NFS_MAXATTRTIMEO(inode);
-		nfsi-&gt;attrtimeo_timestamp = now;
+		nfsi-&gt;last_updated = now;
+	} else {
+		if (!time_in_range_open(now, nfsi-&gt;attrtimeo_timestamp, nfsi-&gt;attrtimeo_timestamp + nfsi-&gt;attrtimeo)) {
+			if ((nfsi-&gt;attrtimeo &lt;&lt;= 1) &gt; NFS_MAXATTRTIMEO(inode))
+				nfsi-&gt;attrtimeo = NFS_MAXATTRTIMEO(inode);
+			nfsi-&gt;attrtimeo_timestamp = now;
+		}
+		/*
+		 * Avoid jiffy wraparound issues with nfsi-&gt;last_updated
+		 */
+		if (!time_in_range(nfsi-&gt;last_updated, nfsi-&gt;read_cache_jiffies, now))
+			nfsi-&gt;last_updated = nfsi-&gt;read_cache_jiffies;
 	}
 	invalid &amp;= ~NFS_INO_INVALID_ATTR;
 	/* Don't invalidate the data if we were to blame */
commit 40d24704091c8a29a4c99d25670f1996749aea6f
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Mon Oct 8 09:24:22 2007 -0400

    NFS: Fix a connectathon regression in NFSv3 and NFSv4
    
    We're failing basic test6 against Linux servers because they lack a correct
    change attribute. The fix is to assume that we always want to invalidate
    the readdir caches when we call update_changeattr and/or
    nfs_post_op_update_inode on a directory.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-23 19:45:39.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-23 20:16:11.000000000 -0400
@@ -1400,11 +1400,19 @@ int nfs_post_op_update_inode(struct inod
 {
 	struct nfs_inode *nfsi = NFS_I(inode);
 
-	if (fattr-&gt;valid &amp; NFS_ATTR_FATTR)
+	if (fattr-&gt;valid &amp; NFS_ATTR_FATTR) {
+		if (S_ISDIR(inode-&gt;i_mode)) {
+			spin_lock(&amp;inode-&gt;i_lock);
+			nfsi-&gt;cache_validity |= NFS_INO_INVALID_DATA;
+			spin_unlock(&amp;inode-&gt;i_lock);
+		}
 		return nfs_refresh_inode(inode, fattr);
+	}
 
 	spin_lock(&amp;inode-&gt;i_lock);
 	nfsi-&gt;cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
+	if (S_ISDIR(inode-&gt;i_mode))
+		nfsi-&gt;cache_validity |= NFS_INO_INVALID_DATA;
 	spin_unlock(&amp;inode-&gt;i_lock);
 	return 0;
 }
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/nfs4proc.c cel-2.6.9/fs/nfs/nfs4proc.c
--- linux-2.6.9/fs/nfs/nfs4proc.c	2009-10-23 19:45:39.000000000 -0400
+++ cel-2.6.9/fs/nfs/nfs4proc.c	2009-10-23 20:16:11.000000000 -0400
@@ -186,12 +186,10 @@ static void update_changeattr(struct ino
 	struct nfs_inode *nfsi = NFS_I(dir);
 
 	spin_lock(&amp;dir-&gt;i_lock);
-	if (cinfo-&gt;after != nfsi-&gt;change_attr) {
-		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA;
-		if (!cinfo-&gt;atomic || cinfo-&gt;before != nfsi-&gt;change_attr)
-			nfsi-&gt;cache_change_attribute = jiffies;
-		nfsi-&gt;change_attr = cinfo-&gt;after;
-	}
+	nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA;
+	if (!cinfo-&gt;atomic || cinfo-&gt;before != nfsi-&gt;change_attr)
+		nfsi-&gt;cache_change_attribute = jiffies;
+	nfsi-&gt;change_attr = cinfo-&gt;after;
 	spin_unlock(&amp;dir-&gt;i_lock);
 }
 
commit 2a3f5fd45938bd86ce8faf4cb26be4f7e9ae2941
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Mon Oct 8 14:26:13 2007 -0400

    NFS: nfs_refresh_inode should clear cache_validity flags on success
    
    If the cached attributes match the ones supplied in the fattr, then assume
    we've revalidated the inode.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-26 19:37:13.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-26 19:38:29.000000000 -0400
@@ -1321,6 +1321,7 @@ static int nfs_check_inode_attributes(st
 {
 	struct nfs_inode *nfsi = NFS_I(inode);
 	loff_t cur_size, new_isize;
+	unsigned long invalid = 0;
 
 	/* Has the inode gone and changed behind our back? */
 	if (nfsi-&gt;fileid != fattr-&gt;fileid
@@ -1333,29 +1334,36 @@ static int nfs_check_inode_attributes(st
 
 	if ((fattr-&gt;valid &amp; NFS_ATTR_FATTR_V4) != 0 &amp;&amp;
 			nfsi-&gt;change_attr != fattr-&gt;change_attr)
-		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
+		invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
 
 	/* Verify a few of the more important attributes */
 	if (!timespec_equal(&amp;inode-&gt;i_mtime, &amp;fattr-&gt;mtime))
-		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
+		invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
 
 	cur_size = i_size_read(inode);
  	new_isize = nfs_size_to_loff_t(fattr-&gt;size);
 	if (cur_size != new_isize &amp;&amp; nfsi-&gt;npages == 0)
-		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
+		invalid |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
 
 	/* Have any file permissions changed? */
 	if ((inode-&gt;i_mode &amp; S_IALLUGO) != (fattr-&gt;mode &amp; S_IALLUGO)
 			|| inode-&gt;i_uid != fattr-&gt;uid
 			|| inode-&gt;i_gid != fattr-&gt;gid)
-		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL;
+		invalid |= NFS_INO_INVALID_ATTR | NFS_INO_INVALID_ACCESS | NFS_INO_INVALID_ACL;
 
 	/* Has the link count changed? */
 	if (inode-&gt;i_nlink != fattr-&gt;nlink)
-		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR;
+		invalid |= NFS_INO_INVALID_ATTR;
 
 	if (!timespec_equal(&amp;inode-&gt;i_atime, &amp;fattr-&gt;atime))
-		nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATIME;
+		invalid |= NFS_INO_INVALID_ATIME;
+
+	if (invalid != 0)
+		nfsi-&gt;cache_validity |= invalid;
+	else
+		nfsi-&gt;cache_validity &amp;= ~(NFS_INO_INVALID_ATTR
+				| NFS_INO_INVALID_ATIME
+				| NFS_INO_REVAL_PAGECACHE);
 
 	nfsi-&gt;read_cache_jiffies = fattr-&gt;time_start;
 	return 0;
@@ -1400,21 +1408,12 @@ int nfs_post_op_update_inode(struct inod
 {
 	struct nfs_inode *nfsi = NFS_I(inode);
 
-	if (fattr-&gt;valid &amp; NFS_ATTR_FATTR) {
-		if (S_ISDIR(inode-&gt;i_mode)) {
-			spin_lock(&amp;inode-&gt;i_lock);
-			nfsi-&gt;cache_validity |= NFS_INO_INVALID_DATA;
-			spin_unlock(&amp;inode-&gt;i_lock);
-		}
-		return nfs_refresh_inode(inode, fattr);
-	}
-
 	spin_lock(&amp;inode-&gt;i_lock);
-	nfsi-&gt;cache_validity |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
+	nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
 	if (S_ISDIR(inode-&gt;i_mode))
 		nfsi-&gt;cache_validity |= NFS_INO_INVALID_DATA;
 	spin_unlock(&amp;inode-&gt;i_lock);
-	return 0;
+	return nfs_refresh_inode(inode, fattr);
 }
 
 /*
@@ -1433,7 +1432,7 @@ static int nfs_update_inode(struct inode
 {
 	struct nfs_inode *nfsi = NFS_I(inode);
 	loff_t cur_isize, new_isize;
-	unsigned int	invalid = 0;
+	unsigned long invalid = 0;
 	unsigned long now = jiffies;
 
 	dfprintk(VFS, "NFS: %s(%s/%ld ct=%d info=0x%x)\n",
commit bfc69a456642a51c89dfd8e5184468857cb44f32
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Mon Oct 15 18:18:29 2007 -0400

    NFS: define a function to update nfsi-&gt;cache_change_attribute
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

    [ modified to apply on 2.6.9-89 by Chuck Lever &lt;chuck.lever@oracle.com&gt; ]
    [ different context in include/linux/nfs_fs.h ]

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c
--- linux-2.6.9/fs/nfs/dir.c	2009-10-23 20:19:10.000000000 -0400
+++ cel-2.6.9/fs/nfs/dir.c	2009-10-23 20:19:55.000000000 -0400
@@ -700,6 +700,21 @@ int nfs_fsync_dir(struct file *filp, str
 	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-&gt;i_lock
+ */
+void nfs_force_lookup_revalidate(struct inode *dir)
+{
+	NFS_I(dir)-&gt;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
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-23 20:19:10.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-23 20:19:55.000000000 -0400
@@ -1474,7 +1474,8 @@ static int nfs_update_inode(struct inode
 			dprintk("NFS: mtime change on server for file %s/%ld\n",
 					inode-&gt;i_sb-&gt;s_id, inode-&gt;i_ino);
 			invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA;
-			nfsi-&gt;cache_change_attribute = now;
+			if (S_ISDIR(inode-&gt;i_mode))
+				nfs_force_lookup_revalidate(inode);
 		}
 		/* If ctime has changed we should definitely clear access+acl caches */
 		if (!timespec_equal(&amp;inode-&gt;i_ctime, &amp;fattr-&gt;ctime))
@@ -1483,7 +1484,8 @@ static int nfs_update_inode(struct inode
 		dprintk("NFS: change_attr change on server for file %s/%ld\n",
 				inode-&gt;i_sb-&gt;s_id, inode-&gt;i_ino);
 		invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_DATA|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
-		nfsi-&gt;cache_change_attribute = now;
+		if (S_ISDIR(inode-&gt;i_mode))
+			nfs_force_lookup_revalidate(inode);
 	}
 
 	/* Check if our cached file size is stale */
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/nfs4proc.c cel-2.6.9/fs/nfs/nfs4proc.c
--- linux-2.6.9/fs/nfs/nfs4proc.c	2009-10-23 20:19:10.000000000 -0400
+++ cel-2.6.9/fs/nfs/nfs4proc.c	2009-10-23 20:19:55.000000000 -0400
@@ -188,7 +188,7 @@ static void update_changeattr(struct ino
 	spin_lock(&amp;dir-&gt;i_lock);
 	nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE|NFS_INO_INVALID_DATA;
 	if (!cinfo-&gt;atomic || cinfo-&gt;before != nfsi-&gt;change_attr)
-		nfsi-&gt;cache_change_attribute = jiffies;
+		nfs_force_lookup_revalidate(dir);
 	nfsi-&gt;change_attr = cinfo-&gt;after;
 	spin_unlock(&amp;dir-&gt;i_lock);
 }
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h
--- linux-2.6.9/include/linux/nfs_fs.h	2009-10-23 20:19:10.000000000 -0400
+++ cel-2.6.9/include/linux/nfs_fs.h	2009-10-23 20:19:55.000000000 -0400
@@ -414,6 +414,7 @@ extern struct file_operations_ext nfs3_d
 #endif /* CONFIG_NFS_V3 */
 extern struct dentry_operations nfs_dentry_operations;
 
+extern void nfs_force_lookup_revalidate(struct inode *dir);
 extern int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fh, struct nfs_fattr *fattr);
 extern int is_atomic_open(struct inode *dir, struct nameidata *nd);
 
commit 870a5be8b92151332da65021b7b21104e9c1de07
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Sun Oct 5 12:07:23 2008 -0400

    NFS: Clean up nfs_refresh_inode() and nfs_post_op_update_inode()
    
    Try to avoid taking and dropping the inode-&gt;i_lock more than once. Do so by
    moving the code in nfs_refresh_inode() that needs to be done under the
    spinlock into a function nfs_refresh_inode_locked(), and then having both
    nfs_refresh_inode() and nfs_post_op_update_inode() call it directly.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-26 19:41:09.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-26 19:41:53.000000000 -0400
@@ -1369,6 +1369,15 @@ static int nfs_check_inode_attributes(st
 	return 0;
 }
 
+static int nfs_refresh_inode_locked(struct inode *inode, struct nfs_fattr *fattr)
+{
+	struct nfs_inode *nfsi = NFS_I(inode);
+
+	if (time_after(fattr-&gt;time_start, nfsi-&gt;last_updated))
+		return nfs_update_inode(inode, fattr);
+	return nfs_check_inode_attributes(inode, fattr);
+}
+
 /**
  * nfs_refresh_inode - try to update the inode attribute cache
  * @inode - pointer to inode
@@ -1381,17 +1390,12 @@ static int nfs_check_inode_attributes(st
  */
 int nfs_refresh_inode(struct inode *inode, struct nfs_fattr *fattr)
 {
-	struct nfs_inode *nfsi = NFS_I(inode);
 	int status;
 
 	if ((fattr-&gt;valid &amp; NFS_ATTR_FATTR) == 0)
 		return 0;
 	spin_lock(&amp;inode-&gt;i_lock);
-	if (time_after(fattr-&gt;time_start, nfsi-&gt;last_updated))
-		status = nfs_update_inode(inode, fattr);
-	else
-		status = nfs_check_inode_attributes(inode, fattr);
-
+	status = nfs_refresh_inode_locked(inode, fattr);
 	spin_unlock(&amp;inode-&gt;i_lock);
 	return status;
 }
@@ -1407,13 +1411,16 @@ int nfs_refresh_inode(struct inode *inod
 int nfs_post_op_update_inode(struct inode *inode, struct nfs_fattr *fattr)
 {
 	struct nfs_inode *nfsi = NFS_I(inode);
+	int status = 0;
 
 	spin_lock(&amp;inode-&gt;i_lock);
 	nfsi-&gt;cache_validity |= NFS_INO_INVALID_ATTR|NFS_INO_REVAL_PAGECACHE;
 	if (S_ISDIR(inode-&gt;i_mode))
 		nfsi-&gt;cache_validity |= NFS_INO_INVALID_DATA;
+	if ((fattr-&gt;valid &amp; NFS_ATTR_FATTR) != 0)
+		status = nfs_refresh_inode_locked(inode, fattr);
 	spin_unlock(&amp;inode-&gt;i_lock);
-	return nfs_refresh_inode(inode, fattr);
+	return status;
 }
 
 /*
commit a10ad17630024bf7aae8e7f18352f816ee483091
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Tue Sep 23 17:28:41 2008 -0400

    NFS: Fix the NFS attribute update
    
    Currently nfs_refresh_inode() will only update the inode metadata if it
    sees that the RPC call that returned the nfs_fattr was started
    after the last update of the inode. This means that if we have parallel
    RPC calls to the same inode (when sending WRITE calls, for instance), we
    may often miss updates.
    
    This patch attempts to recover those missed updates by also accepting
    them if the ctime in the nfs_fattr is more recent than the inode's
    cached ctime.
    It also recovers the case where the file size has increased, but the
    ctime has not been updated due to limited ctime resolution.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

    [ modified to apply on 2.6.9-89 by Chuck Lever &lt;chuck.lever@oracle.com&gt; ]
    [ 2.6.9-89 does not have timespec_compare(), introduced by
      commit 643a6545, and updated by 77adbfbf. ]

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-27 16:39:46.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-27 16:42:25.000000000 -0400
@@ -1369,11 +1369,49 @@ static int nfs_check_inode_attributes(st
 	return 0;
 }
 
-static int nfs_refresh_inode_locked(struct inode *inode, struct nfs_fattr *fattr)
+static int nfs_ctime_need_update(const struct inode *inode, const struct nfs_fattr *fattr)
 {
-	struct nfs_inode *nfsi = NFS_I(inode);
+	return timespec_compare(&amp;fattr-&gt;ctime, &amp;inode-&gt;i_ctime) &gt; 0;
+}
+
+static int nfs_size_need_update(const struct inode *inode, const struct nfs_fattr *fattr)
+{
+	return nfs_size_to_loff_t(fattr-&gt;size) &gt; i_size_read(inode);
+}
 
-	if (time_after(fattr-&gt;time_start, nfsi-&gt;last_updated))
+/**
+ * nfs_inode_attrs_need_update - check if the inode attributes need updating
+ * @inode - pointer to inode
+ * @fattr - attributes
+ *
+ * Attempt to divine whether or not an RPC call reply carrying stale
+ * attributes got scheduled after another call carrying updated ones.
+ *
+ * To do so, the function first assumes that a more recent ctime means
+ * that the attributes in fattr are newer, however it also attempt to
+ * catch the case where ctime either didn't change, or went backwards
+ * (if someone reset the clock on the server) by looking at whether
+ * or not this RPC call was started after the inode was last updated.
+ * Note also the check for jiffy wraparound if the last_updated timestamp
+ * is later than 'jiffies'.
+ *
+ * The function returns 'true' if it thinks the attributes in 'fattr' are
+ * more recent than the ones cached in the inode.
+ *
+ */
+static int nfs_inode_attrs_need_update(const struct inode *inode, const struct nfs_fattr *fattr)
+{
+	const struct nfs_inode *nfsi = NFS_I(inode);
+
+	return nfs_ctime_need_update(inode, fattr) ||
+			nfs_size_need_update(inode, fattr) ||
+			time_after(fattr-&gt;time_start, nfsi-&gt;last_updated) ||
+			time_after(nfsi-&gt;last_updated, jiffies);
+}
+
+static int nfs_refresh_inode_locked(struct inode *inode, struct nfs_fattr *fattr)
+{
+	if (nfs_inode_attrs_need_update(inode, fattr))
 		return nfs_update_inode(inode, fattr);
 	return nfs_check_inode_attributes(inode, fattr);
 }
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/time.h cel-2.6.9/include/linux/time.h
--- linux-2.6.9/include/linux/time.h	2009-10-27 16:39:22.000000000 -0400
+++ cel-2.6.9/include/linux/time.h	2009-10-27 16:43:42.000000000 -0400
@@ -301,6 +301,20 @@ static __inline__ int timespec_equal(str
 	return (a-&gt;tv_sec == b-&gt;tv_sec) &amp;&amp; (a-&gt;tv_nsec == b-&gt;tv_nsec);
 } 
 
+/*
+ * lhs &lt; rhs:  return &lt;0
+ * lhs == rhs: return 0
+ * lhs &gt; rhs:  return &gt;0
+ */
+static inline int timespec_compare(const struct timespec *lhs, const struct timespec *rhs)
+{
+	if (lhs-&gt;tv_sec &lt; rhs-&gt;tv_sec)
+		return -1;
+	if (lhs-&gt;tv_sec &gt; rhs-&gt;tv_sec)
+		return 1;
+	return lhs-&gt;tv_nsec - rhs-&gt;tv_nsec;
+}
+
 /* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
  * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
  * =&gt; year=1980, mon=12, day=31, hour=23, min=59, sec=59.
commit 03254e65a60d3113164672dbbadc023c4a56ecd1
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Thu Oct 9 13:27:55 2008 -0400

    NFS: Fix attribute updates
    
    This fixes a regression seen when running the Connectathon testsuite
    against an ext3 filesystem. The reason was that the inode was constantly
    being marked as 'just updated' by the jiffy wraparound test.
    This again meant that newer GETATTR calls were failing to pass the
    nfs_inode_attrs_need_update() test unless the changes caused a ctime update
    on the server, since they were perceived as having been started before the
    latest inode update.
    
    Given that nfs_inode_attrs_need_update() already checks for wraparound
    of nfsi-&gt;last_updated, we can drop the buggy "protection" in
    nfs_update_inode().
    
    Also make a slight micro-optimisation of nfs_inode_attrs_need_update(): we
    are more often going to see time_after(fattr-&gt;time_start, nfsi-&gt;last_updated)
    be true, rather than seeing an update of ctime/size, so put that test
    first to ensure that we optimise away the ctime/size tests.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-26 19:57:05.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-26 19:57:49.000000000 -0400
@@ -1403,10 +1403,10 @@ static int nfs_inode_attrs_need_update(c
 {
 	const struct nfs_inode *nfsi = NFS_I(inode);
 
-	return nfs_ctime_need_update(inode, fattr) ||
-			nfs_size_need_update(inode, fattr) ||
-			time_after(fattr-&gt;time_start, nfsi-&gt;last_updated) ||
-			time_after(nfsi-&gt;last_updated, jiffies);
+	return time_after(fattr-&gt;time_start, nfsi-&gt;last_updated) ||
+		nfs_ctime_need_update(inode, fattr) ||
+		nfs_size_need_update(inode, fattr) ||
+		time_after(nfsi-&gt;last_updated, jiffies);
 }
 
 static int nfs_refresh_inode_locked(struct inode *inode, struct nfs_fattr *fattr)
@@ -1584,11 +1584,6 @@ static int nfs_update_inode(struct inode
 				nfsi-&gt;attrtimeo = NFS_MAXATTRTIMEO(inode);
 			nfsi-&gt;attrtimeo_timestamp = now;
 		}
-		/*
-		 * Avoid jiffy wraparound issues with nfsi-&gt;last_updated
-		 */
-		if (!time_in_range(nfsi-&gt;last_updated, nfsi-&gt;read_cache_jiffies, now))
-			nfsi-&gt;last_updated = nfsi-&gt;read_cache_jiffies;
 	}
 	invalid &amp;= ~NFS_INO_INVALID_ATTR;
 	/* Don't invalidate the data if we were to blame */
commit 4704f0e274829e3af00737d2d9adace2d71a9605
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Tue Oct 14 19:16:07 2008 -0400

    NFS: Fix the resolution problem with nfs_inode_attrs_need_update()
    
    It appears that 'jiffies' timestamps do not have high enough resolution for
    nfs_inode_attrs_need_update(). One problem is that a GETATTR can be
    launched within &lt; 1 jiffy of the last operation that updated the attribute.
    Another problem is that RPC calls can take &lt; 1 jiffy to execute.
    
    We can fix this by switching the variables to use a simple global counter
    that gets incremented every time we start another GETATTR call.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

    [ modified to apply on 2.6.9-89 by Chuck Lever &lt;chuck.lever@oracle.com&gt; ]
    [ commit 25606656 already applied to 2.6.9-89;
      context in include/linux/nfs_fs.h is different ]

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c
--- linux-2.6.9/fs/nfs/dir.c	2009-10-27 12:00:41.000000000 -0400
+++ cel-2.6.9/fs/nfs/dir.c	2009-10-27 12:02:51.000000000 -0400
@@ -179,6 +179,7 @@ typedef struct {
 	decode_dirent_t	decode;
 	int		plus;
 	unsigned long	timestamp;
+	unsigned long	gencount;
 	int		timestamp_valid;
 } nfs_readdir_descriptor_t;
 
@@ -200,13 +201,14 @@ int nfs_readdir_filler(nfs_readdir_descr
 	struct file	*file = desc-&gt;file;
 	struct inode	*inode = file-&gt;f_dentry-&gt;d_inode;
 	struct rpc_cred	*cred = nfs_file_cred(file);
-	unsigned long	timestamp;
+	unsigned long	timestamp, gencount;
 	int		error;
 
 	dfprintk(VFS, "NFS: nfs_readdir_filler() reading cookie %Lu into page %lu.\n", (long long)desc-&gt;entry-&gt;cookie, page-&gt;index);
 
  again:
 	timestamp = jiffies;
+	gencount = nfs_inc_attr_generation_counter();
 	error = NFS_PROTO(inode)-&gt;readdir(file-&gt;f_dentry, cred, desc-&gt;entry-&gt;cookie, page,
 					  NFS_SERVER(inode)-&gt;dtsize, desc-&gt;plus);
 	if (error &lt; 0) {
@@ -220,6 +222,7 @@ int nfs_readdir_filler(nfs_readdir_descr
 		goto error;
 	}
 	desc-&gt;timestamp = timestamp;
+	desc-&gt;gencount = gencount;
 	desc-&gt;timestamp_valid = 1;
 	SetPageUptodate(page);
 	spin_lock(&amp;inode-&gt;i_lock);
@@ -250,9 +253,10 @@ int dir_decode(nfs_readdir_descriptor_t 
 	if (IS_ERR(p))
 		return PTR_ERR(p);
 	desc-&gt;ptr = p;
-	if (desc-&gt;timestamp_valid)
+	if (desc-&gt;timestamp_valid) {
 		desc-&gt;entry-&gt;fattr-&gt;time_start = desc-&gt;timestamp;
-	else
+		desc-&gt;entry-&gt;fattr-&gt;gencount = desc-&gt;gencount;
+	} else
 		desc-&gt;entry-&gt;fattr-&gt;valid &amp;= ~NFS_ATTR_FATTR;
 	return 0;
 }
@@ -547,7 +551,7 @@ int uncached_readdir(nfs_readdir_descrip
 	struct rpc_cred	*cred = nfs_file_cred(file);
 	struct page	*page = NULL;
 	int		status;
-	unsigned long	timestamp;
+	unsigned long	timestamp, gencount;
 
 	dfprintk(VFS, "NFS: uncached_readdir() searching for cookie %Lu\n", (unsigned long long)*desc-&gt;dir_cookie);
 
@@ -557,6 +561,7 @@ int uncached_readdir(nfs_readdir_descrip
 		goto out;
 	}
 	timestamp = jiffies;
+	gencount = nfs_inc_attr_generation_counter();
 	status = NFS_PROTO(inode)-&gt;readdir(file-&gt;f_dentry, cred,
 						*desc-&gt;dir_cookie, page,
 						NFS_SERVER(inode)-&gt;dtsize,
@@ -568,6 +573,7 @@ int uncached_readdir(nfs_readdir_descrip
 	desc-&gt;ptr = kmap(page);		/* matching kunmap in nfs_do_filldir */
 	if (status &gt;= 0) {
 		desc-&gt;timestamp = timestamp;
+		desc-&gt;gencount = gencount;
 		desc-&gt;timestamp_valid = 1;
 		if ((status = dir_decode(desc)) == 0)
 			desc-&gt;entry-&gt;prev_cookie = *desc-&gt;dir_cookie;
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-27 12:00:41.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-27 12:01:30.000000000 -0400
@@ -884,7 +884,7 @@ nfs_fhget(struct super_block *sb, struct
 			init_special_inode(inode, inode-&gt;i_mode, fattr-&gt;rdev);
 
 		nfsi-&gt;read_cache_jiffies = fattr-&gt;time_start;
-		nfsi-&gt;last_updated = now;
+		nfsi-&gt;attr_gencount = fattr-&gt;gencount;
 		nfsi-&gt;cache_change_attribute = now;
 		inode-&gt;i_atime = fattr-&gt;atime;
 		inode-&gt;i_mtime = fattr-&gt;mtime;
@@ -1379,6 +1379,30 @@ static int nfs_size_need_update(const st
 	return nfs_size_to_loff_t(fattr-&gt;size) &gt; i_size_read(inode);
 }
 
+static unsigned long nfs_attr_generation_counter;
+
+static unsigned long nfs_read_attr_generation_counter(void)
+{
+	smp_rmb();
+	return nfs_attr_generation_counter;
+}
+
+unsigned long nfs_inc_attr_generation_counter(void)
+{
+	unsigned long ret;
+	smp_rmb();
+	ret = ++nfs_attr_generation_counter;
+	smp_wmb();
+	return ret;
+}
+
+void nfs_fattr_init(struct nfs_fattr *fattr)
+{
+	fattr-&gt;valid = 0;
+	fattr-&gt;time_start = jiffies;
+	fattr-&gt;gencount = nfs_inc_attr_generation_counter();
+}
+
 /**
  * nfs_inode_attrs_need_update - check if the inode attributes need updating
  * @inode - pointer to inode
@@ -1392,8 +1416,7 @@ static int nfs_size_need_update(const st
  * catch the case where ctime either didn't change, or went backwards
  * (if someone reset the clock on the server) by looking at whether
  * or not this RPC call was started after the inode was last updated.
- * Note also the check for jiffy wraparound if the last_updated timestamp
- * is later than 'jiffies'.
+ * Note also the check for wraparound of 'attr_gencount'
  *
  * The function returns 'true' if it thinks the attributes in 'fattr' are
  * more recent than the ones cached in the inode.
@@ -1403,10 +1426,10 @@ static int nfs_inode_attrs_need_update(c
 {
 	const struct nfs_inode *nfsi = NFS_I(inode);
 
-	return time_after(fattr-&gt;time_start, nfsi-&gt;last_updated) ||
+	return ((long)fattr-&gt;gencount - (long)nfsi-&gt;attr_gencount) &gt; 0 ||
 		nfs_ctime_need_update(inode, fattr) ||
 		nfs_size_need_update(inode, fattr) ||
-		time_after(nfsi-&gt;last_updated, jiffies);
+		((long)nfsi-&gt;attr_gencount - (long)nfs_read_attr_generation_counter() &gt; 0);
 }
 
 static int nfs_refresh_inode_locked(struct inode *inode, struct nfs_fattr *fattr)
@@ -1522,7 +1545,7 @@ static int nfs_update_inode(struct inode
 		}
 		/* If ctime has changed we should definitely clear access+acl caches */
 		if (!timespec_equal(&amp;inode-&gt;i_ctime, &amp;fattr-&gt;ctime))
-			invalid |= NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
+			invalid |= NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ACCESS|NFS_INO_INVALID_ACL;
 	} else if (nfsi-&gt;change_attr != fattr-&gt;change_attr) {
 		dprintk("NFS: change_attr change on server for file %s/%ld\n",
 				inode-&gt;i_sb-&gt;s_id, inode-&gt;i_ino);
@@ -1577,7 +1600,7 @@ static int nfs_update_inode(struct inode
 		nfs_inc_stats(inode, NFSIOS_ATTRINVALIDATE);
 		nfsi-&gt;attrtimeo = NFS_MINATTRTIMEO(inode);
 		nfsi-&gt;attrtimeo_timestamp = now;
-		nfsi-&gt;last_updated = now;
+		nfsi-&gt;attr_gencount = nfs_inc_attr_generation_counter();
 	} else {
 		if (!time_in_range_open(now, nfsi-&gt;attrtimeo_timestamp, nfsi-&gt;attrtimeo_timestamp + nfsi-&gt;attrtimeo)) {
 			if ((nfsi-&gt;attrtimeo &lt;&lt;= 1) &gt; NFS_MAXATTRTIMEO(inode))
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_fs.h cel-2.6.9/include/linux/nfs_fs.h
--- linux-2.6.9/include/linux/nfs_fs.h	2009-10-27 12:00:41.000000000 -0400
+++ cel-2.6.9/include/linux/nfs_fs.h	2009-10-27 12:06:27.000000000 -0400
@@ -159,7 +159,7 @@ struct nfs_inode {
 	unsigned long		attrtimeo_timestamp;
 	__u64			change_attr;		/* v4 only */
 
-	unsigned long		last_updated;
+	unsigned long		attr_gencount;
 	/* "Generation counter" for the attribute cache. This is
 	 * bumped whenever we update the metadata on the
 	 * server.
@@ -344,15 +344,11 @@ extern void nfs_file_set_open_context(st
 extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, int mode);
 extern void nfs_file_clear_open_context(struct file *filp);
 extern u64 nfs_compat_user_ino64(u64 fileid);
+extern void nfs_fattr_init(struct nfs_fattr *fattr);
 
 /* linux/net/ipv4/ipconfig.c: trims ip addr off front of name, too. */
 extern u32 root_nfs_parse_addr(char *name); /*__init*/
-
-static inline void nfs_fattr_init(struct nfs_fattr *fattr)
-{
-	fattr-&gt;valid = 0;
-	fattr-&gt;time_start = jiffies;
-}
+extern unsigned long nfs_inc_attr_generation_counter(void);
 
 /*
  * linux/fs/nfs/file.c
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/include/linux/nfs_xdr.h cel-2.6.9/include/linux/nfs_xdr.h
--- linux-2.6.9/include/linux/nfs_xdr.h	2009-10-27 12:00:41.000000000 -0400
+++ cel-2.6.9/include/linux/nfs_xdr.h	2009-10-27 12:01:30.000000000 -0400
@@ -42,6 +42,7 @@ struct nfs_fattr {
 	__u64			change_attr;	/* NFSv4 change attribute */
 	__u64			pre_change_attr;/* pre-op NFSv4 change attribute */
 	unsigned long		time_start;
+	unsigned long		gencount;
 };
 
 #define NFS_ATTR_WCC		0x0001		/* pre-op WCC data    */
commit 011935a0a710c20bb7ae63523b78856848db1926
Author: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;
Date:   Tue Oct 14 19:24:50 2008 -0400

    NFS: Fix a resolution problem with nfs_inode-&gt;cache_change_attribute
    
    The cache_change_attribute is used to decide whether or not a directory has
    changed, in which case we may need to look it up again. Again, the use of
    'jiffies' leads to an issue of resolution.
    
    Once again, the fix is to change nfs_inode-&gt;cache_change_attribute, and
    just make it a simple counter.
    
    Signed-off-by: Trond Myklebust &lt;Trond.Myklebust@netapp.com&gt;

diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/dir.c cel-2.6.9/fs/nfs/dir.c
--- linux-2.6.9/fs/nfs/dir.c	2009-10-23 20:22:24.000000000 -0400
+++ cel-2.6.9/fs/nfs/dir.c	2009-10-23 20:23:15.000000000 -0400
@@ -712,7 +712,7 @@ int nfs_fsync_dir(struct file *filp, str
  */
 void nfs_force_lookup_revalidate(struct inode *dir)
 {
-	NFS_I(dir)-&gt;cache_change_attribute = jiffies;
+	NFS_I(dir)-&gt;cache_change_attribute++;
 }
 
 /*
diff --new-file --text --unified=3 --recursive --show-c-function linux-2.6.9/fs/nfs/inode.c cel-2.6.9/fs/nfs/inode.c
--- linux-2.6.9/fs/nfs/inode.c	2009-10-23 20:22:25.000000000 -0400
+++ cel-2.6.9/fs/nfs/inode.c	2009-10-23 20:23:15.000000000 -0400
@@ -885,7 +885,6 @@ nfs_fhget(struct super_block *sb, struct
 
 		nfsi-&gt;read_cache_jiffies = fattr-&gt;time_start;
 		nfsi-&gt;attr_gencount = fattr-&gt;gencount;
-		nfsi-&gt;cache_change_attribute = now;
 		inode-&gt;i_atime = fattr-&gt;atime;
 		inode-&gt;i_mtime = fattr-&gt;mtime;
 		inode-&gt;i_ctime = fattr-&gt;ctime;
</pre></body></html>