Index: kern/vfs_cache.c =================================================================== --- kern/vfs_cache.c (revision 187408) +++ kern/vfs_cache.c (working copy) @@ -607,6 +607,24 @@ } /* + * Invalidate all negative entries for a particular directory vnode. + */ +void +cache_purge_negative(vp) + struct vnode *vp; +{ + struct namecache *cp, *ncp; + + CTR1(KTR_VFS, "cache_purge_negative(%p)", vp); + CACHE_LOCK(); + LIST_FOREACH_SAFE(cp, &vp->v_cache_src, nc_src, ncp) { + if (cp->nc_vp == NULL) + cache_zap(cp); + } + CACHE_UNLOCK(); +} + +/* * Flush all entries referencing a particular filesystem. */ void Index: nfsclient/nfsnode.h =================================================================== --- nfsclient/nfsnode.h (revision 187408) +++ nfsclient/nfsnode.h (working copy) @@ -109,6 +109,7 @@ time_t n_modestamp; /* mode cache timestamp */ struct timespec n_mtime; /* Prev modify time. */ time_t n_ctime; /* Prev create time. */ + time_t n_dmtime; /* Prev dir modify time. */ time_t n_expiry; /* Lease expiry time */ nfsfh_t *n_fhp; /* NFS File Handle */ struct vnode *n_vnode; /* associated vnode */ Index: nfsclient/nfs_vnops.c =================================================================== --- nfsclient/nfs_vnops.c (revision 187408) +++ nfsclient/nfs_vnops.c (working copy) @@ -594,13 +594,6 @@ error = nfs_vinvalbuf(vp, V_SAVE, ap->a_td, 1); mtx_lock(&np->n_mtx); } - /* - * Invalidate the attribute cache in all cases. - * An open is going to fetch fresh attrs any way, other procs - * on this node that have file open will be forced to do an - * otw attr fetch, but this is safe. - */ - np->n_attrstamp = 0; if (np->n_flag & NWRITEERR) { np->n_flag &= ~NWRITEERR; error = np->n_error; @@ -648,12 +641,6 @@ */ if (nfs_getattrcache(vp, ap->a_vap) == 0) goto nfsmout; - if (v3 && nfsaccess_cache_timeout > 0) { - nfsstats.accesscache_misses++; - nfs3_access_otw(vp, NFSV3ACCESS_ALL, ap->a_td, ap->a_cred); - if (nfs_getattrcache(vp, ap->a_vap) == 0) - goto nfsmout; - } nfsstats.rpccnt[NFSPROC_GETATTR]++; mreq = nfsm_reqhead(vp, NFSPROC_GETATTR, NFSX_FH(v3)); mb = mreq; @@ -844,6 +831,7 @@ struct componentname *cnp = ap->a_cnp; struct vnode *dvp = ap->a_dvp; struct vnode **vpp = ap->a_vpp; + struct vattr vattr; int flags = cnp->cn_flags; struct vnode *newvp; struct nfsmount *nmp; @@ -869,8 +857,12 @@ return (error); } if ((error = cache_lookup(dvp, vpp, cnp)) && error != ENOENT) { - struct vattr vattr; - + /* + * We only accept a positive hit in the cache if the + * change time of the file matches our cached copy. + * Otherwise, we discard the cache entry and fallback + * to doing a lookup RPC. + */ newvp = *vpp; if (!VOP_GETATTR(newvp, &vattr, cnp->cn_cred, td) && vattr.va_ctime.tv_sec == VTONFS(newvp)->n_ctime) { @@ -886,6 +878,24 @@ else vrele(newvp); *vpp = NULLVP; + } else if (error == ENOENT) { + /* + * We only accept a negative hit in the cache if the + * modification time of the parent directory matches + * our cached copy. Otherwise, we discard all of the + * negative cache entries for this directory. + */ + if (VOP_GETATTR(dvp, &vattr, cnp->cn_cred, td) == 0 && + vattr.va_mtime.tv_sec == np->n_dmtime) { + nfsstats.lookupcache_hits++; + if ((cnp->cn_nameiop == CREATE || + cnp->cn_nameiop == RENAME) && + (flags & ISLASTCN)) + cnp->cn_flags |= SAVENAME; + return (ENOENT); + } + cache_purge_negative(dvp); + np->n_dmtime = 0; } error = 0; newvp = NULLVP; @@ -971,16 +981,38 @@ vput(newvp); *vpp = NULLVP; } + + if (error != ENOENT) + goto done; + + /* The requested file was not found. */ if ((cnp->cn_nameiop == CREATE || cnp->cn_nameiop == RENAME) && - (flags & ISLASTCN) && error == ENOENT) { + (flags & ISLASTCN)) { + /* + * XXX: UFS does a full VOP_ACCESS(dvp, + * VWRITE) here instead of just checking + * MNT_RDONLY. + */ if (dvp->v_mount->mnt_flag & MNT_RDONLY) - error = EROFS; - else - error = EJUSTRETURN; + return (EROFS); + cnp->cn_flags |= SAVENAME; + return (EJUSTRETURN); } - if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) - cnp->cn_flags |= SAVENAME; + + if ((cnp->cn_flags & MAKEENTRY) && cnp->cn_nameiop != CREATE) { + /* + * Maintain n_dmtime as the modification time + * of the parent directory when the oldest -ve + * name cache entry for this directory was + * added. + */ + if (np->n_dmtime == 0) + np->n_dmtime = np->n_vattr.va_mtime.tv_sec; + cache_enter(dvp, NULL, cnp); + } + return (ENOENT); } +done: return (error); } Index: sys/vnode.h =================================================================== --- sys/vnode.h (revision 187408) +++ sys/vnode.h (working copy) @@ -560,6 +560,7 @@ int cache_lookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp); void cache_purge(struct vnode *vp); +void cache_purge_negative(struct vnode *vp); void cache_purgevfs(struct mount *mp); int change_dir(struct vnode *vp, struct thread *td); int change_root(struct vnode *vp, struct thread *td);