diff -u --recursive --new-file linux-2.4.6-seekdir/fs/namei.c linux-2.4.6-cto/fs/namei.c --- linux-2.4.6-seekdir/fs/namei.c Wed Jul 4 17:30:28 2001 +++ linux-2.4.6-cto/fs/namei.c Wed Jul 4 18:41:14 2001 @@ -418,7 +418,7 @@ while (*name=='/') name++; if (!*name) - goto return_base; + goto return_reval; inode = nd->dentry->d_inode; if (current->link_count) @@ -537,7 +537,7 @@ inode = nd->dentry->d_inode; /* fallthrough */ case 1: - goto return_base; + goto return_reval; } if (nd->dentry->d_op && nd->dentry->d_op->d_hash) { err = nd->dentry->d_op->d_hash(nd->dentry, &this); @@ -588,6 +588,10 @@ nd->last_type = LAST_DOT; else if (this.len == 2 && this.name[1] == '.') nd->last_type = LAST_DOTDOT; +return_reval: + dentry = nd->dentry; + if (dentry && dentry->d_op && dentry->d_op->d_revalidate) + dentry->d_op->d_revalidate(dentry, nd->flags); return_base: return 0; out_dput: diff -u --recursive --new-file linux-2.4.6-seekdir/fs/nfs/dir.c linux-2.4.6-cto/fs/nfs/dir.c --- linux-2.4.6-seekdir/fs/nfs/dir.c Wed Jul 4 18:40:55 2001 +++ linux-2.4.6-cto/fs/nfs/dir.c Wed Jul 4 18:41:14 2001 @@ -422,55 +422,74 @@ return 0; } +static inline +void nfs_renew_verifier(struct inode *dir, struct dentry *dentry) +{ + dentry->d_verifier = NFS_CACHE_MTIME(dir); +} + +/* + * A check for whether or not the parent directory has changed. + * In the case it has, we assume that the dentries are untrustworthy + * and may need to be looked up again. + */ +static inline +int nfs_check_verifier(struct inode *dir, struct dentry *dentry) +{ + if (IS_ROOT(dentry)) + return 1; + nfs_revalidate_inode(NFS_SERVER(dir),dir); + return dentry->d_verifier == NFS_CACHE_MTIME(dir); +} + /* * Whenever an NFS operation succeeds, we know that the dentry * is valid, so we update the revalidation timestamp. */ -static inline void nfs_renew_times(struct dentry * dentry) +static inline void __nfs_renew_times(struct dentry * dentry) { dentry->d_time = jiffies; } +static inline void nfs_renew_times(struct dentry * dentry) +{ + __nfs_renew_times(dentry); + nfs_renew_verifier(dentry->d_parent->d_inode, dentry); +} + static inline int nfs_dentry_force_reval(struct dentry *dentry, int flags) { 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 we're interested in close-to-open cache consistency, + * then we revalidate the inode upon lookup. */ - if (!(flags & LOOKUP_CONTINUE)) { - long diff = CURRENT_TIME - dentry->d_parent->d_inode->i_mtime; + if (!(NFS_SERVER(inode)->flags & NFS_MOUNT_NOCTO) + && !(flags & LOOKUP_CONTINUE)) + 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. + * If parent mtime has changed, we revalidate, else we wait for a + * period corresponding to the parent's attribute cache timeout value. */ -#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; + if (!nfs_check_verifier(dir, dentry)) + return 1; return time_after(jiffies, dentry->d_time + timeout); } @@ -483,9 +502,8 @@ * 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. + * If the parent directory is seen to have changed, we throw out the + * cached dentry and do a new lookup. */ static int nfs_lookup_revalidate(struct dentry * dentry, int flags) { @@ -498,11 +516,7 @@ 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; @@ -515,48 +529,50 @@ goto out_bad; } - if (!nfs_dentry_force_reval(dentry, flags)) - goto out_valid; - - if (IS_ROOT(dentry)) { - __nfs_revalidate_inode(NFS_SERVER(inode), inode); - goto out_valid_renew; - } + /* Force a full look up iff the parent directory has changed */ + if (nfs_check_verifier(dir, dentry)) + goto fast_getattr; - /* - * Do a new lookup and check the dentry attributes. - */ error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr); if (error) goto out_bad; - - /* Inode number matches? */ - if (!(fattr.valid & NFS_ATTR_FATTR) || - NFS_FSID(inode) != fattr.fsid || - NFS_FILEID(inode) != fattr.fileid) + if (memcmp(NFS_FH(inode), &fhandle, sizeof(struct nfs_fh))!= 0) + goto out_bad; + if ((error = nfs_refresh_inode(inode, &fattr)) != 0) goto out_bad; - /* Ok, remember that we successfully checked it.. */ - nfs_refresh_inode(inode, &fattr); + nfs_renew_times(dentry); + goto out_valid; + + fast_getattr: + if (!nfs_dentry_force_reval(dentry, flags)) + goto out_valid; - if (nfs_inode_is_stale(inode, &fhandle, &fattr)) + /* + * Revalidate the cached attributes. + */ + error = NFS_PROTO(inode)->getattr(inode, &fattr); + if (error) + goto out_bad; + if ((error = nfs_refresh_inode(inode, &fattr)) != 0) goto out_bad; - out_valid_renew: - nfs_renew_times(dentry); -out_valid: + __nfs_renew_times(dentry); + out_valid: unlock_kernel(); return 1; -out_bad: - shrink_dcache_parent(dentry); - /* If we have submounts, don't unhash ! */ - if (have_submounts(dentry)) - goto out_valid; - d_drop(dentry); - /* Purge readdir caches. */ - nfs_zap_caches(dir); - if (inode && S_ISDIR(inode->i_mode)) + out_bad: + if (inode && S_ISDIR(inode->i_mode)) { + /* Purge readdir caches. */ nfs_zap_caches(inode); + /* If we are root, or have submounts, don't unhash ! */ + if (IS_ROOT(dentry) || have_submounts(dentry)) { + nfs_renew_verifier(dir, dentry); + goto out_valid; + } + shrink_dcache_parent(dentry); + } + d_drop(dentry); unlock_kernel(); return 0; } @@ -625,9 +641,9 @@ if (inode) { no_entry: d_add(dentry, inode); - nfs_renew_times(dentry); error = 0; } + nfs_renew_times(dentry); } out: return ERR_PTR(error); diff -u --recursive --new-file linux-2.4.6-seekdir/fs/nfs/inode.c linux-2.4.6-cto/fs/nfs/inode.c --- linux-2.4.6-seekdir/fs/nfs/inode.c Wed Jul 4 21:06:57 2001 +++ linux-2.4.6-cto/fs/nfs/inode.c Wed Jul 4 21:11:14 2001 @@ -652,27 +652,6 @@ return 1; } -int -nfs_inode_is_stale(struct inode *inode, struct nfs_fh *fh, struct nfs_fattr *fattr) -{ - /* Empty inodes are not stale */ - if (!inode->i_mode) - return 0; - - if ((fattr->mode & S_IFMT) != (inode->i_mode & S_IFMT)) - return 1; - - if (is_bad_inode(inode) || 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) - return 1; - - return 0; -} - /* * This is our own version of iget that looks up inodes by file handle * instead of inode number. We use this technique instead of using @@ -738,7 +717,7 @@ /* * Make sure the inode is up-to-date. */ - error = nfs_revalidate(dentry); + error = nfs_revalidate_inode(NFS_SERVER(inode),inode); if (error) { #ifdef NFS_PARANOIA printk("nfs_notify_change: revalidate failed, error=%d\n", error); diff -u --recursive --new-file linux-2.4.6-seekdir/fs/nfs/nfs3proc.c linux-2.4.6-cto/fs/nfs/nfs3proc.c --- linux-2.4.6-seekdir/fs/nfs/nfs3proc.c Mon Dec 4 03:01:01 2000 +++ linux-2.4.6-cto/fs/nfs/nfs3proc.c Wed Jul 4 18:41:17 2001 @@ -80,7 +80,8 @@ status = rpc_call(NFS_CLIENT(dir), NFS3PROC_GETATTR, fhandle, fattr, 0); dprintk("NFS reply lookup: %d\n", status); - nfs_refresh_inode(dir, &dir_attr); + if (status >= 0) + status = nfs_refresh_inode(dir, &dir_attr); return status; } diff -u --recursive --new-file linux-2.4.6-seekdir/include/linux/dcache.h linux-2.4.6-cto/include/linux/dcache.h --- linux-2.4.6-seekdir/include/linux/dcache.h Wed Jul 4 17:30:33 2001 +++ linux-2.4.6-cto/include/linux/dcache.h Wed Jul 4 18:49:38 2001 @@ -80,6 +80,7 @@ struct super_block * d_sb; /* The root of the dentry tree */ unsigned long d_vfs_flags; void * d_fsdata; /* fs-specific data */ + unsigned long long d_verifier; /* used by nfs d_revalidate */ unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */ };