Index: nfsclient/nfsnode.h =================================================================== --- nfsclient/nfsnode.h (revision 225458) +++ nfsclient/nfsnode.h (working copy) @@ -110,7 +110,7 @@ struct timespec n_mtime; /* Prev modify time. */ time_t n_unused0; time_t n_unused1; - int n_dmtime_ticks; /* Tick of -ve cache entry */ + int n_unused3; time_t n_unused2; nfsfh_t *n_fhp; /* NFS File Handle */ struct vnode *n_vnode; /* associated vnode */ @@ -137,8 +137,8 @@ int n_directio_opens; int n_directio_asyncwr; struct nfs_attrcache_timestamp n_unused; - struct timespec n_ctime; /* Prev create time. */ - struct timespec n_dmtime; /* Prev dir modify time. */ + struct timespec n_unused4; + struct timespec n_unused5; }; #define n_atim n_un1.nf_atim Index: nfsclient/nfsm_subs.h =================================================================== --- nfsclient/nfsm_subs.h (revision 225458) +++ nfsclient/nfsm_subs.h (working copy) @@ -152,8 +152,8 @@ caddr_t *dpos); int nfsm_loadattr_xx(struct vnode **v, struct vattr *va, struct mbuf **md, caddr_t *dpos); -int nfsm_postop_attr_xx(struct vnode **v, int *f, struct mbuf **md, - caddr_t *dpos); +int nfsm_postop_attr_xx(struct vnode **v, int *f, struct vattr *va, + struct mbuf **md, caddr_t *dpos); int nfsm_wcc_data_xx(struct vnode **v, int *f, struct mbuf **md, caddr_t *dpos); @@ -181,10 +181,17 @@ #define nfsm_postop_attr(v, f) \ do { \ int32_t t1; \ - t1 = nfsm_postop_attr_xx(&v, &f, &md, &dpos); \ + t1 = nfsm_postop_attr_xx(&v, &f, NULL, &md, &dpos); \ nfsm_dcheck(t1, mrep); \ } while (0) +#define nfsm_postop_attr_va(v, f, va) \ +do { \ + int32_t t1; \ + t1 = nfsm_postop_attr_xx(&v, &f, va, &md, &dpos); \ + nfsm_dcheck(t1, mrep); \ +} while (0) + /* Used as (f) for nfsm_wcc_data() */ #define NFSV3_WCCRATTR 0 #define NFSV3_WCCCHK 1 Index: nfsclient/nfs_vnops.c =================================================================== --- nfsclient/nfs_vnops.c (revision 225458) +++ nfsclient/nfs_vnops.c (working copy) @@ -914,7 +914,7 @@ struct vnode **vpp = ap->a_vpp; struct mount *mp = dvp->v_mount; struct vattr vattr; - struct timespec dmtime; + struct timespec nctime; int flags = cnp->cn_flags; struct vnode *newvp; struct nfsmount *nmp; @@ -923,7 +923,7 @@ long len; nfsfh_t *fhp; struct nfsnode *np, *newnp; - int error = 0, attrflag, fhsize, ltype; + int error = 0, attrflag, dattrflag, fhsize, ltype, ncticks; int v3 = NFS_ISV3(dvp); struct thread *td = cnp->cn_thread; @@ -939,11 +939,24 @@ *vpp = NULLVP; return (error); } - error = cache_lookup(dvp, vpp, cnp); + error = cache_lookup_times(dvp, vpp, cnp, &nctime, &ncticks); if (error > 0 && error != ENOENT) return (error); if (error == -1) { /* + * Lookups of "." are special and always return the + * current directory. cache_lookup() already handles + * associated locking bookkeeping, etc. + */ + if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { + /* XXX: Is this really correct? */ + if (cnp->cn_nameiop != LOOKUP && + (flags & ISLASTCN)) + cnp->cn_flags |= SAVENAME; + return (0); + } + + /* * 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 @@ -969,7 +982,7 @@ mtx_unlock(&newnp->n_mtx); } if (VOP_GETATTR(newvp, &vattr, cnp->cn_cred) == 0 && - timespeccmp(&vattr.va_ctime, &newnp->n_ctime, ==)) { + timespeccmp(&vattr.va_ctime, &nctime, ==)) { nfsstats.lookupcache_hits++; if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) @@ -988,36 +1001,22 @@ /* * 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. We also - * only trust -ve cache entries for less than - * nm_negative_namecache_timeout seconds. + * the cached copy in the name cache entry. + * Otherwise, we discard all of the negative cache + * entries for this directory. We also only trust + * negative cache entries for up to nm_negnametimeo + * seconds. */ - if ((u_int)(ticks - np->n_dmtime_ticks) < - (nmp->nm_negnametimeo * hz) && + if ((u_int)(ticks - ncticks) < (nmp->nm_negnametimeo * hz) && VOP_GETATTR(dvp, &vattr, cnp->cn_cred) == 0 && - timespeccmp(&vattr.va_mtime, &np->n_dmtime, ==)) { + timespeccmp(&vattr.va_mtime, &nctime, ==)) { nfsstats.lookupcache_hits++; return (ENOENT); } cache_purge_negative(dvp); - mtx_lock(&np->n_mtx); - timespecclear(&np->n_dmtime); - mtx_unlock(&np->n_mtx); } - /* - * Cache the modification time of the parent directory in case - * the lookup fails and results in adding the first negative - * name cache entry for the directory. Since this is reading - * a single time_t, don't bother with locking. The - * modification time may be a bit stale, but it must be read - * before performing the lookup RPC to prevent a race where - * another lookup updates the timestamp on the directory after - * the lookup RPC has been performed on the server but before - * n_dmtime is set at the end of this function. - */ - dmtime = np->n_vattr.va_mtime; + attrflag = dattrflag = 0; error = 0; newvp = NULLVP; nfsstats.lookupcache_misses++; @@ -1032,7 +1031,7 @@ nfsm_request(dvp, NFSPROC_LOOKUP, cnp->cn_thread, cnp->cn_cred); if (error) { if (v3) { - nfsm_postop_attr(dvp, attrflag); + nfsm_postop_attr_va(dvp, dattrflag, &vattr); m_freem(mrep); } goto nfsmout; @@ -1128,16 +1127,17 @@ } } if (v3) { - nfsm_postop_attr(newvp, attrflag); - nfsm_postop_attr(dvp, attrflag); - } else - nfsm_loadattr(newvp, NULL); + nfsm_postop_attr_va(newvp, attrflag, &vattr); + nfsm_postop_attr(dvp, dattrflag); + } else { + nfsm_loadattr(newvp, &vattr); + attrflag = 1; + } if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; if ((cnp->cn_flags & MAKEENTRY) && - (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))) { - np->n_ctime = np->n_vattr.va_ctime; - cache_enter(dvp, newvp, cnp); + (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN)) && attrflag) { + cache_enter_time(dvp, newvp, cnp, &vattr.va_ctime); } *vpp = newvp; m_freem(mrep); @@ -1165,30 +1165,22 @@ return (EJUSTRETURN); } - if ((cnp->cn_flags & MAKEENTRY) && cnp->cn_nameiop != CREATE) { + if ((cnp->cn_flags & MAKEENTRY) && cnp->cn_nameiop != CREATE && + dattrflag) { /* - * Maintain n_dmtime as the modification time - * of the parent directory when the oldest -ve - * name cache entry for this directory was - * added. If a -ve cache entry has already - * been added with a newer modification time - * by a concurrent lookup, then don't bother - * adding a cache entry. The modification - * time of the directory might have changed - * due to the file this lookup failed to find - * being created. In that case a subsequent - * lookup would incorrectly use the entry - * added here instead of doing an extra - * lookup. + * Cache the modification time of the parent + * directory from the post-op attributes in + * the name cache entry. The negative cache + * entry will be ignored once the directory + * has changed. Don't bother adding the entry + * if the directory has already changed. */ mtx_lock(&np->n_mtx); - if (timespeccmp(&np->n_dmtime, &dmtime, <=)) { - if (!timespecisset(&np->n_dmtime)) { - np->n_dmtime = dmtime; - np->n_dmtime_ticks = ticks; - } + if (timespeccmp(&np->n_vattr.va_mtime, + &vattr.va_mtime, ==)) { mtx_unlock(&np->n_mtx); - cache_enter(dvp, NULL, cnp); + cache_enter_time(dvp, NULL, cnp, + &vattr.va_mtime); } else mtx_unlock(&np->n_mtx); } @@ -2474,6 +2466,7 @@ nfsuint64 cookie; struct nfsmount *nmp = VFSTONFS(vp->v_mount); struct nfsnode *dnp = VTONFS(vp), *np; + struct vattr vattr; nfsfh_t *fhp; u_quad_t fileno; int error = 0, tlen, more_dirs = 1, blksiz = 0, doit, bigenough = 1, i; @@ -2654,18 +2647,13 @@ dpos = dpossav1; mdsav2 = md; md = mdsav1; - nfsm_loadattr(newvp, NULL); + nfsm_loadattr(newvp, &vattr); dpos = dpossav2; md = mdsav2; - dp->d_type = - IFTODT(VTTOIF(np->n_vattr.va_type)); + dp->d_type = IFTODT(VTTOIF(vattr.va_type)); ndp->ni_vp = newvp; - /* - * Update n_ctime so subsequent lookup - * doesn't purge entry. - */ - np->n_ctime = np->n_vattr.va_ctime; - cache_enter(ndp->ni_dvp, ndp->ni_vp, cnp); + cache_enter_time(ndp->ni_dvp, ndp->ni_vp, cnp, + &vattr.va_ctime); } } else { /* Just skip over the file handle */ Index: nfsclient/nfs_subs.c =================================================================== --- nfsclient/nfs_subs.c (revision 225458) +++ nfsclient/nfs_subs.c (working copy) @@ -978,8 +978,8 @@ } int -nfsm_postop_attr_xx(struct vnode **v, int *f, struct mbuf **md, - caddr_t *dpos) +nfsm_postop_attr_xx(struct vnode **v, int *f, struct vattr *va, + struct mbuf **md, caddr_t *dpos) { u_int32_t *tl; int t1; @@ -990,7 +990,7 @@ return EBADRPC; *f = fxdr_unsigned(int, *tl); if (*f != 0) { - t1 = nfs_loadattrcache(&ttvp, md, dpos, NULL, 1); + t1 = nfs_loadattrcache(&ttvp, md, dpos, va, 1); if (t1 != 0) { *f = 0; return t1; @@ -1020,7 +1020,7 @@ VTONFS(*v)->n_mtime.tv_nsec == fxdr_unsigned(u_int32_t, *(tl + 3))); mtx_unlock(&(VTONFS(*v))->n_mtx); } - t1 = nfsm_postop_attr_xx(v, &ttattrf, md, dpos); + t1 = nfsm_postop_attr_xx(v, &ttattrf, NULL, md, dpos); if (t1) return t1; if (*f) Index: kern/vfs_cache.c =================================================================== --- kern/vfs_cache.c (revision 225458) +++ kern/vfs_cache.c (working copy) @@ -96,6 +96,8 @@ TAILQ_ENTRY(namecache) nc_dst; /* destination vnode list */ struct vnode *nc_dvp; /* vnode of parent of name */ struct vnode *nc_vp; /* vnode the name refers to */ + struct timespec nc_time; /* timespec provided by fs */ + int nc_ticks; /* ticks value when entry was added */ u_char nc_flag; /* flag bits */ u_char nc_nlen; /* length of name */ char nc_name[0]; /* segment name + nul */ @@ -394,10 +396,12 @@ */ int -cache_lookup(dvp, vpp, cnp) +cache_lookup_times(dvp, vpp, cnp, tsp, ticksp) struct vnode *dvp; struct vnode **vpp; struct componentname *cnp; + struct timespec *tsp; + int *ticksp; { struct namecache *ncp; u_int32_t hash; @@ -422,6 +426,10 @@ dothits++; SDT_PROBE(vfs, namecache, lookup, hit, dvp, ".", *vpp, 0, 0); + if (tsp != NULL) + timespecclear(tsp); + if (ticksp != NULL) + *ticksp = ticks; goto success; } if (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.') { @@ -440,19 +448,22 @@ CACHE_WUNLOCK(); return (0); } - if (dvp->v_cache_dd->nc_flag & NCF_ISDOTDOT) - *vpp = dvp->v_cache_dd->nc_vp; + ncp = dvp->v_cache_dd; + if (ncp->nc_flag & NCF_ISDOTDOT) + *vpp = ncp->nc_vp; else - *vpp = dvp->v_cache_dd->nc_dvp; + *vpp = ncp->nc_dvp; /* Return failure if negative entry was found. */ - if (*vpp == NULL) { - ncp = dvp->v_cache_dd; + if (*vpp == NULL) goto negative_success; - } CTR3(KTR_VFS, "cache_lookup(%p, %s) found %p via ..", dvp, cnp->cn_nameptr, *vpp); SDT_PROBE(vfs, namecache, lookup, hit, dvp, "..", *vpp, 0, 0); + if (tsp != NULL) + *tsp = ncp->nc_time; + if (ticksp != NULL) + *ticksp = ncp->nc_ticks; goto success; } } @@ -499,6 +510,10 @@ dvp, cnp->cn_nameptr, *vpp, ncp); SDT_PROBE(vfs, namecache, lookup, hit, dvp, ncp->nc_name, *vpp, 0, 0); + if (tsp != NULL) + *tsp = ncp->nc_time; + if (ticksp != NULL) + *ticksp = ncp->nc_ticks; goto success; } @@ -530,6 +545,10 @@ cnp->cn_flags |= ISWHITEOUT; SDT_PROBE(vfs, namecache, lookup, hit_negative, dvp, ncp->nc_name, 0, 0, 0); + if (tsp != NULL) + *tsp = ncp->nc_time; + if (ticksp != NULL) + *ticksp = ncp->nc_ticks; CACHE_WUNLOCK(); return (ENOENT); @@ -616,10 +635,11 @@ * Add an entry to the cache. */ void -cache_enter(dvp, vp, cnp) +cache_enter_time(dvp, vp, cnp, tsp) struct vnode *dvp; struct vnode *vp; struct componentname *cnp; + struct timespec *tsp; { struct namecache *ncp, *n2; struct nchashhead *ncpp; @@ -692,6 +712,11 @@ ncp->nc_vp = vp; ncp->nc_dvp = dvp; ncp->nc_flag = flag; + if (tsp != NULL) + ncp->nc_time = *tsp; + else + timespecclear(&ncp->nc_time); + ncp->nc_ticks = ticks; len = ncp->nc_nlen = cnp->cn_namelen; hash = fnv_32_buf(cnp->cn_nameptr, len, FNV1_32_INIT); strlcpy(ncp->nc_name, cnp->cn_nameptr, len + 1); @@ -708,6 +733,8 @@ if (n2->nc_dvp == dvp && n2->nc_nlen == cnp->cn_namelen && !bcmp(n2->nc_name, cnp->cn_nameptr, n2->nc_nlen)) { + n2->nc_time = ncp->nc_time; + n2->nc_ticks = ncp->nc_ticks; CACHE_WUNLOCK(); cache_free(ncp); return; @@ -1248,3 +1275,26 @@ buf[l] = '\0'; return (0); } + +/* ABI compat shims for old kernel modules. */ +#undef cache_enter +#undef cache_lookup + +void cache_enter(struct vnode *dvp, struct vnode *vp, + struct componentname *cnp); +int cache_lookup(struct vnode *dvp, struct vnode **vpp, + struct componentname *cnp); + +void +cache_enter(struct vnode *dvp, struct vnode *vp, struct componentname *cnp) +{ + + cache_enter_time(dvp, vp, cnp, NULL); +} + +int +cache_lookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp) +{ + + return (cache_lookup_times(dvp, vpp, cnp, NULL, NULL)); +} Index: fs/nfsclient/nfsnode.h =================================================================== --- fs/nfsclient/nfsnode.h (revision 225458) +++ fs/nfsclient/nfsnode.h (working copy) @@ -104,7 +104,7 @@ struct timespec n_mtime; /* Prev modify time. */ time_t n_unused0; time_t n_unused1; - int n_dmtime_ticks; /* Tick of -ve cache entry */ + int n_unused3; time_t n_unused2; struct nfsfh *n_fhp; /* NFS File Handle */ struct vnode *n_vnode; /* associated vnode */ @@ -131,8 +131,8 @@ struct nfs_attrcache_timestamp n_unused; u_int64_t n_change; /* old Change attribute */ struct nfsv4node *n_v4; /* extra V4 stuff */ - struct timespec n_ctime; /* Prev create time. */ - struct timespec n_dmtime; /* Prev dir modify time. */ + struct timespec n_unused4; + struct timespec n_unused5; }; #define n_atim n_un1.nf_atim Index: fs/nfsclient/nfs_clvnops.c =================================================================== --- fs/nfsclient/nfs_clvnops.c (revision 225458) +++ fs/nfsclient/nfs_clvnops.c (working copy) @@ -979,12 +979,12 @@ struct vnode *newvp; struct nfsmount *nmp; struct nfsnode *np, *newnp; - int error = 0, attrflag, dattrflag, ltype; + int error = 0, attrflag, dattrflag, ltype, ncticks; struct thread *td = cnp->cn_thread; struct nfsfh *nfhp; struct nfsvattr dnfsva, nfsva; struct vattr vattr; - struct timespec dmtime; + struct timespec nctime; *vpp = NULLVP; if ((flags & ISLASTCN) && (mp->mnt_flag & MNT_RDONLY) && @@ -1005,11 +1005,24 @@ if ((error = VOP_ACCESS(dvp, VEXEC, cnp->cn_cred, td)) != 0) return (error); - error = cache_lookup(dvp, vpp, cnp); + error = cache_lookup_times(dvp, vpp, cnp, &nctime, &ncticks); if (error > 0 && error != ENOENT) return (error); if (error == -1) { /* + * Lookups of "." are special and always return the + * current directory. cache_lookup() already handles + * associated locking bookkeeping, etc. + */ + if (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') { + /* XXX: Is this really correct? */ + if (cnp->cn_nameiop != LOOKUP && + (flags & ISLASTCN)) + cnp->cn_flags |= SAVENAME; + return (0); + } + + /* * 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 @@ -1035,7 +1048,7 @@ } if (nfscl_nodeleg(newvp, 0) == 0 || (VOP_GETATTR(newvp, &vattr, cnp->cn_cred) == 0 && - timespeccmp(&vattr.va_ctime, &newnp->n_ctime, ==))) { + timespeccmp(&vattr.va_ctime, &nctime, ==))) { NFSINCRGLOBAL(newnfsstats.lookupcache_hits); if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) @@ -1054,36 +1067,21 @@ /* * 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. We also - * only trust -ve cache entries for less than - * nm_negative_namecache_timeout seconds. + * the cached copy in the name cache entry. + * Otherwise, we discard all of the negative cache + * entries for this directory. We also only trust + * negative cache entries for up to nm_negnametimeo + * seconds. */ - if ((u_int)(ticks - np->n_dmtime_ticks) < - (nmp->nm_negnametimeo * hz) && + if ((u_int)(ticks - ncticks) < (nmp->nm_negnametimeo * hz) && VOP_GETATTR(dvp, &vattr, cnp->cn_cred) == 0 && - timespeccmp(&vattr.va_mtime, &np->n_dmtime, ==)) { + timespeccmp(&vattr.va_mtime, &nctime, ==)) { NFSINCRGLOBAL(newnfsstats.lookupcache_hits); return (ENOENT); } cache_purge_negative(dvp); - mtx_lock(&np->n_mtx); - timespecclear(&np->n_dmtime); - mtx_unlock(&np->n_mtx); } - /* - * Cache the modification time of the parent directory in case - * the lookup fails and results in adding the first negative - * name cache entry for the directory. Since this is reading - * a single time_t, don't bother with locking. The - * modification time may be a bit stale, but it must be read - * before performing the lookup RPC to prevent a race where - * another lookup updates the timestamp on the directory after - * the lookup RPC has been performed on the server but before - * n_dmtime is set at the end of this function. - */ - dmtime = np->n_vattr.na_mtime; error = 0; newvp = NULLVP; NFSINCRGLOBAL(newnfsstats.lookupcache_misses); @@ -1119,30 +1117,22 @@ return (EJUSTRETURN); } - if ((cnp->cn_flags & MAKEENTRY) && cnp->cn_nameiop != CREATE) { + if ((cnp->cn_flags & MAKEENTRY) && cnp->cn_nameiop != CREATE && + dattrflag) { /* - * Maintain n_dmtime as the modification time - * of the parent directory when the oldest -ve - * name cache entry for this directory was - * added. If a -ve cache entry has already - * been added with a newer modification time - * by a concurrent lookup, then don't bother - * adding a cache entry. The modification - * time of the directory might have changed - * due to the file this lookup failed to find - * being created. In that case a subsequent - * lookup would incorrectly use the entry - * added here instead of doing an extra - * lookup. + * Cache the modification time of the parent + * directory from the post-op attributes in + * the name cache entry. The negative cache + * entry will be ignored once the directory + * has changed. Don't bother adding the entry + * if the directory has already changed. */ mtx_lock(&np->n_mtx); - if (timespeccmp(&np->n_dmtime, &dmtime, <=)) { - if (!timespecisset(&np->n_dmtime)) { - np->n_dmtime = dmtime; - np->n_dmtime_ticks = ticks; - } + if (timespeccmp(&np->n_vattr.na_mtime, + &dnfsva.na_mtime, ==)) { mtx_unlock(&np->n_mtx); - cache_enter(dvp, NULL, cnp); + cache_enter_time(dvp, NULL, cnp, + &dnfsva.na_mtime); } else mtx_unlock(&np->n_mtx); } @@ -1240,9 +1230,8 @@ if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; if ((cnp->cn_flags & MAKEENTRY) && - (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN))) { - np->n_ctime = np->n_vattr.na_vattr.va_ctime; - cache_enter(dvp, newvp, cnp); + (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN)) && attrflag) { + cache_enter_time(dvp, newvp, cnp, &nfsva.na_ctime); } *vpp = newvp; return (0); Index: fs/nfsclient/nfs_clrpcops.c =================================================================== --- fs/nfsclient/nfs_clrpcops.c (revision 225458) +++ fs/nfsclient/nfs_clrpcops.c (working copy) @@ -3320,8 +3320,9 @@ ndp->ni_vp = newvp; NFSCNHASH(cnp, HASHINIT); if (cnp->cn_namelen <= NCHNAMLEN) { - np->n_ctime = np->n_vattr.na_ctime; - cache_enter(ndp->ni_dvp,ndp->ni_vp,cnp); + cache_enter_time(ndp->ni_dvp, + ndp->ni_vp, cnp, + &nfsva.na_ctime); } if (unlocknewvp) vput(newvp); Index: sys/vnode.h =================================================================== --- sys/vnode.h (revision 225458) +++ sys/vnode.h (working copy) @@ -582,10 +582,14 @@ struct vnode; /* cache_* may belong in namei.h. */ -void cache_enter(struct vnode *dvp, struct vnode *vp, - struct componentname *cnp); -int cache_lookup(struct vnode *dvp, struct vnode **vpp, - struct componentname *cnp); +#define cache_enter(dvp, vp, cnp) \ + cache_enter_time(dvp, vp, cnp, NULL) +void cache_enter_time(struct vnode *dvp, struct vnode *vp, + struct componentname *cnp, struct timespec *tsp); +#define cache_lookup(dvp, vpp, cnp) \ + cache_lookup_times(dvp, vpp, cnp, NULL, NULL) +int cache_lookup_times(struct vnode *dvp, struct vnode **vpp, + struct componentname *cnp, struct timespec *tsp, int *ticksp); void cache_purge(struct vnode *vp); void cache_purge_negative(struct vnode *vp); void cache_purgevfs(struct mount *mp);