diff -druN Linux-2.4.4/fs/exec.c linux/fs/exec.c
--- Linux-2.4.4/fs/exec.c	Tue May 22 16:42:02 2001
+++ linux/fs/exec.c	Tue May 22 16:17:37 2001
@@ -105,6 +105,7 @@
 	struct nameidata nd;
 	int error;
 
+retry_lookup:
 	error = user_path_walk(library, &nd);
 	if (error)
 		goto out;
@@ -119,8 +120,11 @@
 
 	file = dentry_open(nd.dentry, nd.mnt, O_RDONLY);
 	error = PTR_ERR(file);
-	if (IS_ERR(file))
+	if (IS_ERR(file)) {
+		if (error == -ESTALE)
+			goto retry_lookup;
 		goto out;
+	}
 
 	error = -ENOEXEC;
 	if(file->f_op && file->f_op->read) {
@@ -341,6 +345,7 @@
 	struct file *file;
 	int err = 0;
 
+retry_lookup:
 	if (path_init(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd))
 		err = path_walk(name, &nd);
 	file = ERR_PTR(err);
@@ -358,7 +363,8 @@
 						fput(file);
 						file = ERR_PTR(err);
 					}
-				}
+				} else if (PTR_ERR(file) == -ESTALE)
+					goto retry_lookup;
 out:
 				return file;
 			}
diff -druN Linux-2.4.4/fs/nfs/dir.c linux/fs/nfs/dir.c
--- Linux-2.4.4/fs/nfs/dir.c	Tue May 22 16:41:55 2001
+++ linux/fs/nfs/dir.c	Wed May 23 12:13:32 2001
@@ -370,7 +370,7 @@
 	struct nfs_entry my_entry;
 	long		res;
 
-	res = nfs_revalidate(dentry);
+	res = nfs_revalidate_inode(dentry);
 	if (res < 0)
 		return res;
 
@@ -428,46 +428,34 @@
 	dentry->d_time = jiffies;
 }
 
-static inline int nfs_dentry_force_reval(struct dentry *dentry, int flags)
+/*
+ * We trust lookup results for the same amount of time as attributes.
+ * Extra revalidation when a parent directory has recently changed
+ * is not necessary, since we usually check the lookup results with
+ * an on-the-wire GETATTR if we are opening the file.
+ */
+static inline int nfs_dentry_force_reval(struct dentry *dentry)
 {
-	struct inode *inode = dentry->d_inode;
-	unsigned long timeout = NFS_ATTRTIMEO(inode);
-
-	/*
-	 * If it's the last lookup in a series, we use a stricter
-	 * cache consistency check by looking at the parent mtime.
-	 *
-	 * If it's been modified in the last hour, be really strict.
-	 * (This still means that we can avoid doing unnecessary
-	 * work on directories like /usr/share/bin etc which basically
-	 * never change).
-	 */
-	if (!(flags & LOOKUP_CONTINUE)) {
-		long diff = CURRENT_TIME - dentry->d_parent->d_inode->i_mtime;
-
-		if (diff < 15*60)
-			timeout = 0;
-	}
-	
-	return time_after(jiffies,dentry->d_time + timeout);
+	return time_after(jiffies,
+			dentry->d_time + NFS_ATTRTIMEO(dentry->d_inode));
 }
 
 /*
- * We judge how long we want to trust negative
- * dentries by looking at the parent inode mtime.
- *
- * If mtime is close to present time, we revalidate
- * more often.
+ * We judge how long we want to trust negative dentries by looking
+ * at the parent inode mtime.  If mtime is close to present time,
+ * we revalidate more often.  This is necessary because some NFS
+ * servers export file systems that don't generate unique timestamps
+ * when a directory changes more than once in a second.
  */
-#define NFS_REVALIDATE_NEGATIVE (1 * HZ)
 static inline int nfs_neg_need_reval(struct dentry *dentry)
 {
 	struct inode *dir = dentry->d_parent->d_inode;
 	unsigned long timeout = NFS_ATTRTIMEO(dir);
 	long diff = CURRENT_TIME - dir->i_mtime;
 
-	if (diff < 5*60 && timeout > NFS_REVALIDATE_NEGATIVE)
-		timeout = NFS_REVALIDATE_NEGATIVE;
+	/* This accounts for client<->server clock skew */
+	if (diff < (5 * 60))
+		timeout = 1 * HZ;
 
 	return time_after(jiffies, dentry->d_time + timeout);
 }
@@ -506,17 +494,14 @@
 		goto out_valid;
 	}
 
-	if (is_bad_inode(inode)) {
-		dfprintk(VFS, "nfs_lookup_validate: %s/%s has dud inode\n",
-			dentry->d_parent->d_name.name, dentry->d_name.name);
+	if (NFS_STALE(inode))
 		goto out_bad;
-	}
 
-	if (!nfs_dentry_force_reval(dentry, flags))
+	if (!nfs_dentry_force_reval(dentry))
 		goto out_valid;
 
 	if (IS_ROOT(dentry)) {
-		__nfs_revalidate_inode(NFS_SERVER(inode), inode);
+		__nfs_revalidate_inode(inode);
 		goto out_valid_renew;
 	}
 
diff -druN Linux-2.4.4/fs/nfs/file.c linux/fs/nfs/file.c
--- Linux-2.4.4/fs/nfs/file.c	Fri Feb  9 14:29:44 2001
+++ linux/fs/nfs/file.c	Mon May 21 12:07:24 2001
@@ -91,14 +91,13 @@
 nfs_file_read(struct file * file, char * buf, size_t count, loff_t *ppos)
 {
 	struct dentry * dentry = file->f_dentry;
-	struct inode * inode = dentry->d_inode;
 	ssize_t result;
 
 	dfprintk(VFS, "nfs: read(%s/%s, %lu@%lu)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name,
 		(unsigned long) count, (unsigned long) *ppos);
 
-	result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+	result = nfs_revalidate_inode(dentry);
 	if (!result)
 		result = generic_file_read(file, buf, count, ppos);
 	return result;
@@ -108,13 +107,12 @@
 nfs_file_mmap(struct file * file, struct vm_area_struct * vma)
 {
 	struct dentry *dentry = file->f_dentry;
-	struct inode *inode = dentry->d_inode;
 	int	status;
 
 	dfprintk(VFS, "nfs: mmap(%s/%s)\n",
 		dentry->d_parent->d_name.name, dentry->d_name.name);
 
-	status = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+	status = nfs_revalidate_inode(dentry);
 	if (!status)
 		status = generic_file_mmap(file, vma);
 	return status;
@@ -224,7 +222,7 @@
 	result = -EBUSY;
 	if (IS_SWAPFILE(inode))
 		goto out_swapfile;
-	result = nfs_revalidate_inode(NFS_SERVER(inode), inode);
+	result = nfs_revalidate_inode(dentry);
 	if (result)
 		goto out;
 
diff -druN Linux-2.4.4/fs/nfs/inode.c linux/fs/nfs/inode.c
--- Linux-2.4.4/fs/nfs/inode.c	Tue May 22 16:42:03 2001
+++ linux/fs/nfs/inode.c	Tue May 22 17:21:16 2001
@@ -635,12 +635,12 @@
 	if ((fattr->mode & S_IFMT) != (inode->i_mode & S_IFMT))
 		return 1;
 
-	if (is_bad_inode(inode))
+	if (NFS_STALE(inode))
 		return 1;
 
 	/* Has the filehandle changed? If so is the old one stale? */
 	if (memcmp(&inode->u.nfs_i.fh, fh, sizeof(inode->u.nfs_i.fh)) != 0 &&
-	    __nfs_revalidate_inode(NFS_SERVER(inode),inode) == -ESTALE)
+	    __nfs_revalidate_inode(inode) == -ESTALE)
 		return 1;
 
 	return 0;
@@ -711,7 +711,7 @@
 	/*
 	 * Make sure the inode is up-to-date.
 	 */
-	error = nfs_revalidate(dentry);
+	error = nfs_revalidate_inode(dentry);
 	if (error) {
 #ifdef NFS_PARANOIA
 printk("nfs_notify_change: revalidate failed, error=%d\n", error);
@@ -773,27 +773,82 @@
 }
 
 /*
- * Externally visible revalidation function
+ * Soft revalidation: if the attributes were checked recently,
+ * pretend they are up to date with the server.  Everyone but
+ * nfs_open uses this.
  */
 int
 nfs_revalidate(struct dentry *dentry)
 {
+	int error = 0;
 	struct inode *inode = dentry->d_inode;
-	return nfs_revalidate_inode(NFS_SERVER(inode), inode);
+
+	if (NFS_STALE(inode))
+		return -ESTALE;
+
+	if (time_before(jiffies, NFS_READTIME(inode) + NFS_ATTRTIMEO(inode)))
+		return 0;
+
+	lock_kernel();
+	error = __nfs_revalidate_inode(inode);
+	unlock_kernel();
+	return error;
 }
 
 /*
- * These are probably going to contain hooks for
- * allocating and releasing RPC credentials for
- * the file. I'll have to think about Tronds patch
- * a bit more..
+ * Close-to-open cache consistency requires that open()
+ * retrieve the server version of this object's attributes.
+ * See Callaghan's "NFS Illustrated" ss. 7.3.2 and 8.14.2.
  */
 int nfs_open(struct inode *inode, struct file *filp)
 {
+	int error;
 	struct rpc_auth *auth;
 	struct rpc_cred *cred;
 
+	/*
+	 * Don't allow anyone to open an inode that is already
+	 * known to be stale.  This catches open("."), which
+	 * isn't resolved via a dcache lookup.
+	 */
+	if (NFS_STALE(inode))
+		return -ENOENT;
+
 	lock_kernel();
+
+	/*
+	 * If this file system is mounted with the "nocto" option,
+	 * check with the server only if the inode's attribute
+	 * cache timeout has expired.
+	 */
+	if ((NFS_SERVER(inode)->flags & NFS_MOUNT_NOCTO) &&
+	   time_before(jiffies, (NFS_READTIME(inode) + NFS_ATTRTIMEO(inode))))
+		goto out_ok;
+
+	error = __nfs_revalidate_inode(inode);
+	if (!error)
+		goto out_ok;
+
+	/*
+	 * If nfs_revalidate_inode discovers this inode is stale,
+	 * we remove this dentry from the dcache and return ESTALE
+	 * to force the VFS layer to do a fresh on-the-wire lookup.
+	 */
+	if (error == -ESTALE) {
+		struct dentry * dentry = filp->f_dentry;
+
+        	shrink_dcache_parent(dentry);
+        	if (!have_submounts(dentry)) {
+        		d_drop(dentry);
+			nfs_zap_caches(dentry->d_parent->d_inode);
+			nfs_zap_caches(inode);
+		}
+	}
+
+	unlock_kernel();
+	return error;
+
+out_ok:
 	auth = NFS_CLIENT(inode)->cl_auth;
 	cred = rpcauth_lookupcred(auth, 0);
 	filp->private_data = cred;
@@ -820,7 +875,7 @@
  * the cached attributes have to be refreshed.
  */
 int
-__nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
+__nfs_revalidate_inode(struct inode *inode)
 {
 	int		 status = 0;
 	struct nfs_fattr fattr;
@@ -828,18 +883,10 @@
 	dfprintk(PAGECACHE, "NFS: revalidating (%x/%Ld)\n",
 		inode->i_dev, (long long)NFS_FILEID(inode));
 
-	lock_kernel();
-	if (!inode || is_bad_inode(inode) || NFS_STALE(inode)) {
-		unlock_kernel();
-		return -ESTALE;
-	}
-
 	while (NFS_REVALIDATING(inode)) {
 		status = nfs_wait_on_inode(inode, NFS_INO_REVALIDATING);
-		if (status < 0) {
-			unlock_kernel();
+		if (status < 0)
 			return status;
-		}
 		if (time_before(jiffies,NFS_READTIME(inode)+NFS_ATTRTIMEO(inode))) {
 			status = NFS_STALE(inode) ? -ESTALE : 0;
 			goto out_nowait;
@@ -870,7 +917,6 @@
 	NFS_FLAGS(inode) &= ~NFS_INO_REVALIDATING;
 	wake_up(&inode->i_wait);
  out_nowait:
-	unlock_kernel();
 	return status;
 }
 
diff -druN Linux-2.4.4/fs/open.c linux/fs/open.c
--- Linux-2.4.4/fs/open.c	Fri Feb  9 14:29:44 2001
+++ linux/fs/open.c	Wed May 23 10:01:02 2001
@@ -616,6 +616,7 @@
 {
 	int namei_flags, error;
 	struct nameidata nd;
+	struct file * file;
 
 	namei_flags = flags;
 	if ((namei_flags+1) & O_ACCMODE)
@@ -623,11 +624,14 @@
 	if (namei_flags & O_TRUNC)
 		namei_flags |= 2;
 
-	error = open_namei(filename, namei_flags, mode, &nd);
-	if (!error)
-		return dentry_open(nd.dentry, nd.mnt, flags);
+	do {
+		error = open_namei(filename, namei_flags, mode, &nd);
+		if (error)
+			return ERR_PTR(error);
+		file = dentry_open(nd.dentry, nd.mnt, flags);
+	} while (PTR_ERR(file) == -ESTALE);
 
-	return ERR_PTR(error);
+	return file;
 }
 
 struct file *dentry_open(struct dentry *dentry, struct vfsmount *mnt, int flags)
diff -druN Linux-2.4.4/include/linux/nfs_fs.h linux/include/linux/nfs_fs.h
--- Linux-2.4.4/include/linux/nfs_fs.h	Tue May 22 16:42:04 2001
+++ linux/include/linux/nfs_fs.h	Wed May 23 10:22:03 2001
@@ -92,7 +92,7 @@
 
 #define NFS_FLAGS(inode)		((inode)->u.nfs_i.flags)
 #define NFS_REVALIDATING(inode)		(NFS_FLAGS(inode) & NFS_INO_REVALIDATING)
-#define NFS_STALE(inode)		(NFS_FLAGS(inode) & NFS_INO_STALE)
+#define NFS_STALE(inode)		(is_bad_inode(inode) || (NFS_FLAGS(inode) & NFS_INO_STALE))
 
 #define NFS_FILEID(inode)		((inode)->u.nfs_i.fileid)
 #define NFS_FSID(inode)			((inode)->u.nfs_i.fsid)
@@ -147,7 +147,7 @@
 extern int nfs_permission(struct inode *, int);
 extern int nfs_open(struct inode *, struct file *);
 extern int nfs_release(struct inode *, struct file *);
-extern int __nfs_revalidate_inode(struct nfs_server *, struct inode *);
+extern int __nfs_revalidate_inode(struct inode *);
 extern int nfs_notify_change(struct dentry *, struct iattr *);
 
 /*
@@ -265,12 +265,24 @@
 /*
  * inline functions
  */
+
+/*
+ * NFSv2 needs to explicitly revalidate inodes periodically.
+ * NFSv3 will get attribute info back on almost every OTW operation,
+ * so explicitly asking the server for attribute info isn't needed.
+ */
 static inline int
-nfs_revalidate_inode(struct nfs_server *server, struct inode *inode)
+nfs_revalidate_inode(struct dentry *dentry)
 {
-	if (time_before(jiffies, NFS_READTIME(inode)+NFS_ATTRTIMEO(inode)))
-		return NFS_STALE(inode) ? -ESTALE : 0;
-	return __nfs_revalidate_inode(server, inode);
+#ifdef CONFIG_NFS_V3
+	struct inode * inode = dentry->d_inode;
+
+	if (NFS_PROTO(inode)->version == 2)
+		return nfs_revalidate(dentry);
+	return NFS_STALE(inode) ? -ESTALE : 0;
+#else
+	return nfs_revalidate(dentry);
+#endif
 }
 
 static inline loff_t
