diff -dpruN Linux-2.4.2/fs/exec.c linux/fs/exec.c
--- Linux-2.4.2/fs/exec.c	Sun Feb 25 22:04:49 2001
+++ linux/fs/exec.c	Thu Mar 15 17:31:04 2001
@@ -338,7 +338,7 @@ struct file *open_exec(const char *name)
 	struct file *file;
 	int err = 0;
 
-	if (path_init(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE, &nd))
+	if (path_init(name, LOOKUP_FOLLOW|LOOKUP_POSITIVE|LOOKUP_REVAL, &nd))
 		err = path_walk(name, &nd);
 	file = ERR_PTR(err);
 	if (!err) {
diff -dpruN Linux-2.4.2/fs/namei.c linux/fs/namei.c
--- Linux-2.4.2/fs/namei.c	Fri Dec 29 17:07:23 2000
+++ linux/fs/namei.c	Sat Mar 24 15:53:03 2001
@@ -429,7 +429,7 @@ int path_walk(const char * name, struct 
 	while (*name=='/')
 		name++;
 	if (!*name)
-		goto return_base;
+		goto reval_base;
 
 	inode = nd->dentry->d_inode;
 	if (current->link_count)
@@ -548,14 +548,14 @@ last_component:
 				inode = nd->dentry->d_inode;
 				/* fallthrough */
 			case 1:
-				goto return_base;
+				goto reval_base;
 		}
 		if (nd->dentry->d_op && nd->dentry->d_op->d_hash) {
 			err = nd->dentry->d_op->d_hash(nd->dentry, &this);
 			if (err < 0)
 				break;
 		}
-		dentry = cached_lookup(nd->dentry, &this, 0);
+		dentry = cached_lookup(nd->dentry, &this, lookup_flags);
 		if (!dentry) {
 			dentry = real_lookup(nd->dentry, &this, 0);
 			err = PTR_ERR(dentry);
@@ -599,6 +599,19 @@ lookup_parent:
 			nd->last_type = LAST_DOT;
 		else if (this.len == 2 && this.name[1] == '.')
 			nd->last_type = LAST_DOTDOT;
+reval_base:
+		dentry = nd->dentry;
+		err = -ENOENT;
+		if (dentry->d_op && dentry->d_op->d_revalidate) {
+			if (!dentry->d_op->d_revalidate(dentry, lookup_flags))
+				/* if it's busy, we can still use it */
+				if (d_invalidate(dentry))
+					goto return_base;
+		}
+		/* has our cwd been removed? */
+		if (!IS_ROOT(dentry) && (dentry == current->fs->pwd))
+			if (d_unhashed(dentry))
+				break;
 return_base:
 		return 0;
 out_dput:
@@ -881,7 +894,7 @@ static inline int may_create(struct inod
  */
 static inline int lookup_flags(unsigned int f)
 {
-	unsigned long retval = LOOKUP_FOLLOW;
+	unsigned long retval = LOOKUP_FOLLOW|LOOKUP_REVAL;
 
 	if (f & O_NOFOLLOW)
 		retval &= ~LOOKUP_FOLLOW;
diff -dpruN Linux-2.4.2/fs/nfs/dir.c linux/fs/nfs/dir.c
--- Linux-2.4.2/fs/nfs/dir.c	Sun Feb 25 22:04:50 2001
+++ linux/fs/nfs/dir.c	Sat Mar 24 21:15:48 2001
@@ -433,84 +433,72 @@ static inline int nfs_dentry_force_reval
 	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 ((flags & LOOKUP_REVAL) &&
+	   !(NFS_SERVER(inode)->flags & NFS_MOUNT_NOCTO))
+		return 1;
 
-		if (diff < 15*60)
-			timeout = 0;
-	}
-	
-	return time_after(jiffies,dentry->d_time + timeout);
+	if (time_after(jiffies, NFS_READTIME(inode) + timeout))
+		return 1;
+
+	return time_after(jiffies, dentry->d_time + timeout);
 }
 
 /*
  * 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.
  */
-#define NFS_REVALIDATE_NEGATIVE (1 * HZ)
-static inline int nfs_neg_need_reval(struct dentry *dentry)
+static inline int nfs_neg_need_reval(struct dentry *dentry, int flags)
 {
 	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;
+	if ((flags & LOOKUP_REVAL) &&
+	   !(NFS_SERVER(dir)->flags & NFS_MOUNT_NOCTO))
+		return 1;
+
+	if (flags & LOOKUP_POSITIVE)
+		return 1;
 
 	return time_after(jiffies, dentry->d_time + timeout);
 }
 
 /*
+ * The VFS layer calls this to ask: "Can I use this dentry?"
+ *
  * This is called every time the dcache has a lookup hit,
  * and we should check whether we can really trust that
  * lookup.
  *
- * NOTE! The hit can be a negative hit too, don't assume
- * we have an inode!
- *
  * If the dentry is older than the revalidation interval, 
  * we do a new lookup and verify that the dentry is still
- * correct.
+ * correct.  This is the only place it's safe to signal
+ * to the VFS layer to remove a dentry for a stale object.
+ *
+ * NB: As a side effect, this revalidates the object's inode
+ *     as well.
  */
 static int nfs_lookup_revalidate(struct dentry * dentry, int flags)
 {
 	struct inode *dir;
 	struct inode *inode;
-	int error;
 	struct nfs_fh fhandle;
 	struct nfs_fattr fattr;
 
 	lock_kernel();
 	dir = dentry->d_parent->d_inode;
 	inode = dentry->d_inode;
-	/*
-	 * If we don't have an inode, let's look at the parent
-	 * directory mtime to get a hint about how often we
-	 * should validate things..
-	 */
+
 	if (!inode) {
-		if (nfs_neg_need_reval(dentry))
-			goto out_bad;
+		if (nfs_neg_need_reval(dentry, flags)) {
+			nfs_zap_caches(dir);
+			unlock_kernel();
+			return 0;
+		}
 		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 (is_bad_inode(inode) || NFS_STALE(inode))
 		goto out_bad;
-	}
 
 	if (!nfs_dentry_force_reval(dentry, flags))
 		goto out_valid;
@@ -520,30 +508,26 @@ static int nfs_lookup_revalidate(struct 
 		goto out_valid_renew;
 	}
 
-	/*
-	 * Do a new lookup and check the dentry attributes.
-	 */
-	error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr);
-	if (error)
+	if (NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr))
 		goto out_bad;
 
-	/* Inode number matches? */
-	if (!(fattr.valid & NFS_ATTR_FATTR) ||
-	    NFS_FSID(inode) != fattr.fsid ||
-	    NFS_FILEID(inode) != fattr.fileid)
+        /*
+	 * Has the filehandle changed? This can result from a server reboot,
+	 * or because the object was replaced.  We revalidate in either case.
+	 */
+        if (memcmp(&inode->u.nfs_i.fh, &fhandle, sizeof(inode->u.nfs_i.fh)))
 		goto out_bad;
 
-	/* Ok, remember that we successfully checked it.. */
-	nfs_refresh_inode(inode, &fattr);
-
-	if (nfs_inode_is_stale(inode, &fhandle, &fattr))
+	/* XXX: this is really expensive */
+	if (nfs_refresh_inode(inode, &fattr))
 		goto out_bad;
 
- out_valid_renew:
+out_valid_renew:
 	nfs_renew_times(dentry);
 out_valid:
 	unlock_kernel();
 	return 1;
+
 out_bad:
 	shrink_dcache_parent(dentry);
 	/* If we have submounts, don't unhash ! */
@@ -552,7 +536,7 @@ out_bad:
 	d_drop(dentry);
 	/* Purge readdir caches. */
 	nfs_zap_caches(dir);
-	if (inode && S_ISDIR(inode->i_mode))
+	if (S_ISDIR(inode->i_mode))
 		nfs_zap_caches(inode);
 	unlock_kernel();
 	return 0;
diff -dpruN Linux-2.4.2/fs/nfs/inode.c linux/fs/nfs/inode.c
--- Linux-2.4.2/fs/nfs/inode.c	Sun Feb 25 22:04:51 2001
+++ linux/fs/nfs/inode.c	Sat Mar 24 14:18:43 2001
@@ -964,6 +964,8 @@ nfs_refresh_inode(struct inode *inode, s
 	 * If size and mtime match the pre-operation values, we can
 	 * assume that any attribute changes were caused by our NFS
          * operation, so there's no need to invalidate the caches.
+	 * XXX: isn't mtime set by the server? what if client and
+	 *      server clocks are skewed?
          */
         if ((fattr->valid & NFS_ATTR_WCC)
 	    && NFS_CACHE_ISIZE(inode) == fattr->pre_size
diff -dpruN Linux-2.4.2/fs/nfs/nfs3proc.c linux/fs/nfs/nfs3proc.c
--- Linux-2.4.2/fs/nfs/nfs3proc.c	Sun Dec  3 21:01:01 2000
+++ linux/fs/nfs/nfs3proc.c	Sat Mar 24 20:16:51 2001
@@ -76,11 +76,17 @@ nfs3_proc_lookup(struct inode *dir, stru
 	dir_attr.valid = 0;
 	fattr->valid = 0;
 	status = rpc_call(NFS_CLIENT(dir), NFS3PROC_LOOKUP, &arg, &res, 0);
+#if 0
 	if (status >= 0 && !(fattr->valid & NFS_ATTR_FATTR))
 		status = rpc_call(NFS_CLIENT(dir), NFS3PROC_GETATTR,
 			 fhandle, fattr, 0);
+#endif
 	dprintk("NFS reply lookup: %d\n", status);
+#if 0
+	/* nfs_refresh_inode is too expensive to do repeatedly.
+	 * we'll wait until NFS really needs these attributes. */
 	nfs_refresh_inode(dir, &dir_attr);
+#endif
 	return status;
 }
 
diff -dpruN Linux-2.4.2/fs/stat.c linux/fs/stat.c
--- Linux-2.4.2/fs/stat.c	Tue Dec  5 15:29:39 2000
+++ linux/fs/stat.c	Sat Mar 24 20:09:09 2001
@@ -24,7 +24,6 @@ do_revalidate(struct dentry *dentry)
 	return 0;
 }
 
-
 #if !defined(__alpha__) && !defined(__sparc__) && !defined(__ia64__) && !defined(__s390__) && !defined(__hppa__)
 
 /*
@@ -138,8 +137,10 @@ asmlinkage long sys_stat(char * filename
 
 	error = user_path_walk(filename, &nd);
 	if (!error) {
+#if 0
 		error = do_revalidate(nd.dentry);
 		if (!error)
+#endif
 			error = cp_old_stat(nd.dentry->d_inode, statbuf);
 		path_release(&nd);
 	}
@@ -154,8 +155,10 @@ asmlinkage long sys_newstat(char * filen
 
 	error = user_path_walk(filename, &nd);
 	if (!error) {
+#if 0
 		error = do_revalidate(nd.dentry);
 		if (!error)
+#endif
 			error = cp_new_stat(nd.dentry->d_inode, statbuf);
 		path_release(&nd);
 	}
@@ -175,8 +178,10 @@ asmlinkage long sys_lstat(char * filenam
 
 	error = user_path_walk_link(filename, &nd);
 	if (!error) {
+#if 0
 		error = do_revalidate(nd.dentry);
 		if (!error)
+#endif
 			error = cp_old_stat(nd.dentry->d_inode, statbuf);
 		path_release(&nd);
 	}
@@ -192,8 +197,10 @@ asmlinkage long sys_newlstat(char * file
 
 	error = user_path_walk_link(filename, &nd);
 	if (!error) {
+#if 0
 		error = do_revalidate(nd.dentry);
 		if (!error)
+#endif
 			error = cp_new_stat(nd.dentry->d_inode, statbuf);
 		path_release(&nd);
 	}
@@ -255,8 +262,12 @@ asmlinkage long sys_readlink(const char 
 		struct inode * inode = nd.dentry->d_inode;
 
 		error = -EINVAL;
+#if 0
 		if (inode->i_op && inode->i_op->readlink &&
 		    !(error = do_revalidate(nd.dentry))) {
+#else
+		if (inode->i_op && inode->i_op->readlink) {
+#endif
 			UPDATE_ATIME(inode);
 			error = inode->i_op->readlink(nd.dentry, buf, bufsiz);
 		}
@@ -335,8 +346,10 @@ asmlinkage long sys_stat64(char * filena
 
 	error = user_path_walk(filename, &nd);
 	if (!error) {
+#if 0
 		error = do_revalidate(nd.dentry);
 		if (!error)
+#endif
 			error = cp_new_stat64(nd.dentry->d_inode, statbuf);
 		path_release(&nd);
 	}
@@ -350,8 +363,10 @@ asmlinkage long sys_lstat64(char * filen
 
 	error = user_path_walk_link(filename, &nd);
 	if (!error) {
+#if 0
 		error = do_revalidate(nd.dentry);
 		if (!error)
+#endif
 			error = cp_new_stat64(nd.dentry->d_inode, statbuf);
 		path_release(&nd);
 	}
diff -dpruN Linux-2.4.2/include/linux/fs.h linux/include/linux/fs.h
--- Linux-2.4.2/include/linux/fs.h	Sun Feb 25 22:04:57 2001
+++ linux/include/linux/fs.h	Sat Mar 24 21:04:01 2001
@@ -994,7 +994,7 @@ extern void init_special_inode(struct in
 
 /* Invalid inode operations -- fs/bad_inode.c */
 extern void make_bad_inode(struct inode *);
-extern int is_bad_inode(struct inode *);
+extern int FASTCALL(is_bad_inode(struct inode * inode));
 
 extern struct file_operations read_fifo_fops;
 extern struct file_operations write_fifo_fops;
@@ -1154,6 +1154,7 @@ static inline long IS_ERR(const void *pt
  *  - require a directory
  *  - ending slashes ok even for nonexistent files
  *  - internal "there are more path compnents" flag
+ *  - force an immediate revalidation of the dcache entry
  */
 #define LOOKUP_FOLLOW		(1)
 #define LOOKUP_DIRECTORY	(2)
@@ -1161,6 +1162,7 @@ static inline long IS_ERR(const void *pt
 #define LOOKUP_POSITIVE		(8)
 #define LOOKUP_PARENT		(16)
 #define LOOKUP_NOALT		(32)
+#define LOOKUP_REVAL		(64)
 /*
  * Type of the last component on LOOKUP_PARENT
  */
