diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c index 86a0601f8aa7..2562c6157045 100644 --- a/sys/fs/nfsclient/nfs_clrpcops.c +++ b/sys/fs/nfsclient/nfs_clrpcops.c @@ -2951,10 +2951,12 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep, nfsattrbit_t attrbits, dattrbits; size_t tresid; u_int32_t *tl2 = NULL, fakefileno = 0xffffffff, rderr; + struct timespec dctime; KASSERT(uiop->uio_iovcnt == 1 && (uio_uio_resid(uiop) & (DIRBLKSIZ - 1)) == 0, ("nfs readdirplusrpc bad uio")); + timespecclear(&dctime); *attrflagp = 0; if (eofp != NULL) *eofp = 0; @@ -2997,6 +2999,7 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep, #endif if (error) return (error); + dctime = nfsva.na_ctime; dotfileid = nfsva.na_fileid; NFSCL_REQSTART(nd, NFSPROC_LOOKUPP, vp); NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED); @@ -3134,6 +3137,8 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep, error = nd->nd_repstat; goto nfsmout; } + if ((nd->nd_flag & ND_NFSV3) != 0 && *attrflagp != 0) + dctime = nap->na_ctime; NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED); NFSLOCKNODE(dnp); dnp->n_cookieverf.nfsuquad[0] = *tl++; @@ -3316,10 +3321,14 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep, vtonfs_dtype(np->n_vattr.na_type); ndp->ni_vp = newvp; NFSCNHASH(cnp, HASHINIT); - if (cnp->cn_namelen <= NCHNAMLEN) { + if (cnp->cn_namelen <= NCHNAMLEN && + (newvp->v_type != VDIR || + dctime.tv_sec != 0)) { cache_enter_time(ndp->ni_dvp, ndp->ni_vp, cnp, - &nfsva.na_ctime); + &nfsva.na_ctime, + newvp->v_type != VDIR ? NULL : + &dctime); } if (unlocknewvp) vput(newvp); diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c index f92d19330e10..57c687c93003 100644 --- a/sys/fs/nfsclient/nfs_clvnops.c +++ b/sys/fs/nfsclient/nfs_clvnops.c @@ -1172,7 +1172,7 @@ nfs_lookup(struct vop_lookup_args *ap) &dnfsva.na_mtime, ==)) { mtx_unlock(&np->n_mtx); cache_enter_time(dvp, NULL, cnp, - &dnfsva.na_mtime); + &dnfsva.na_mtime, NULL); } else mtx_unlock(&np->n_mtx); } @@ -1271,9 +1271,10 @@ nfs_lookup(struct vop_lookup_args *ap) if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; if ((cnp->cn_flags & MAKEENTRY) && - (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN)) && attrflag) { - cache_enter_time(dvp, newvp, cnp, &nfsva.na_ctime); - } + (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN)) && + attrflag != 0 && (newvp->v_type != VDIR || dattrflag != 0)) + cache_enter_time(dvp, newvp, cnp, &nfsva.na_ctime, + newvp->v_type != VDIR ? NULL : &dnfsva.na_ctime); *vpp = newvp; return (0); } @@ -1591,7 +1592,8 @@ nfs_create(struct vop_create_args *ap) } if (!error) { if ((cnp->cn_flags & MAKEENTRY) && attrflag) - cache_enter_time(dvp, newvp, cnp, &nfsva.na_ctime); + cache_enter_time(dvp, newvp, cnp, &nfsva.na_ctime, + NULL); *ap->a_vpp = newvp; } else if (NFS_ISV4(dvp)) { error = nfscl_maperr(cnp->cn_thread, error, vap->va_uid, @@ -1966,8 +1968,8 @@ nfs_link(struct vop_link_args *ap) * must care about lookup caching hit rate, so... */ if (VFSTONFS(vp->v_mount)->nm_negnametimeo != 0 && - (cnp->cn_flags & MAKEENTRY) && dattrflag) { - cache_enter_time(tdvp, vp, cnp, &dnfsva.na_mtime); + (cnp->cn_flags & MAKEENTRY) && attrflag != 0 && error == 0) { + cache_enter_time(tdvp, vp, cnp, &nfsva.na_ctime, NULL); } if (error && NFS_ISV4(vp)) error = nfscl_maperr(cnp->cn_thread, error, (uid_t)0, @@ -2033,21 +2035,21 @@ nfs_symlink(struct vop_symlink_args *ap) if (dattrflag != 0) { mtx_unlock(&dnp->n_mtx); (void) nfscl_loadattrcache(&dvp, &dnfsva, NULL, NULL, 0, 1); - /* - * If negative lookup caching is enabled, I might as well - * add an entry for this node. Not necessary for correctness, - * but if negative caching is enabled, then the system - * must care about lookup caching hit rate, so... - */ - if (VFSTONFS(dvp->v_mount)->nm_negnametimeo != 0 && - (cnp->cn_flags & MAKEENTRY)) { - cache_enter_time(dvp, newvp, cnp, &dnfsva.na_mtime); - } } else { dnp->n_attrstamp = 0; mtx_unlock(&dnp->n_mtx); KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(dvp); } + /* + * If negative lookup caching is enabled, I might as well + * add an entry for this node. Not necessary for correctness, + * but if negative caching is enabled, then the system + * must care about lookup caching hit rate, so... + */ + if (VFSTONFS(dvp->v_mount)->nm_negnametimeo != 0 && + (cnp->cn_flags & MAKEENTRY) && attrflag != 0 && error == 0) { + cache_enter_time(dvp, newvp, cnp, &nfsva.na_ctime, NULL); + } return (error); } @@ -2118,9 +2120,10 @@ nfs_mkdir(struct vop_mkdir_args *ap) * must care about lookup caching hit rate, so... */ if (VFSTONFS(dvp->v_mount)->nm_negnametimeo != 0 && - (cnp->cn_flags & MAKEENTRY) && dattrflag) { - cache_enter_time(dvp, newvp, cnp, &dnfsva.na_mtime); - } + (cnp->cn_flags & MAKEENTRY) && + attrflag != 0 && dattrflag != 0) + cache_enter_time(dvp, newvp, cnp, &nfsva.na_ctime, + &dnfsva.na_ctime); *ap->a_vpp = newvp; } return (error); diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index 1f081baa3ecf..ea481f4c73d5 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -105,6 +105,10 @@ struct namecache { /* * struct namecache_ts repeats struct namecache layout up to the * nc_nlen member. + * struct namecache_ts is used in place of struct namecache when time(s) need + * to be stored. The nc_dotdottime field is used when a cache entry is mapping + * both a non-dotdot directory name plus dotdot for the directory's + * parent. */ struct namecache_ts { LIST_ENTRY(namecache) nc_hash; /* hash chain */ @@ -115,6 +119,7 @@ struct namecache_ts { u_char nc_flag; /* flag bits */ u_char nc_nlen; /* length of name */ struct timespec nc_time; /* timespec provided by fs */ + struct timespec nc_dotdottime; /* dotdot timespec provided by fs */ int nc_ticks; /* ticks value when entry was added */ char nc_name[0]; /* segment name + nul */ }; @@ -125,6 +130,7 @@ struct namecache_ts { #define NCF_WHITE 0x01 #define NCF_ISDOTDOT 0x02 #define NCF_TS 0x04 +#define NCF_DTS 0x08 /* * Name caching works as follows: @@ -190,6 +196,7 @@ RW_SYSINIT(vfscache, &cache_lock, "Name Cache"); static uma_zone_t cache_zone_small; static uma_zone_t cache_zone_small_ts; static uma_zone_t cache_zone_large; +static uma_zone_t cache_zone_large_ts; #define CACHE_PATH_CUTOFF 35 @@ -197,8 +204,12 @@ static struct namecache * cache_alloc(int len, int ts) { - if (len > CACHE_PATH_CUTOFF) - return (uma_zalloc(cache_zone_large, M_WAITOK)); + if (len > CACHE_PATH_CUTOFF) { + if (ts) + return (uma_zalloc(cache_zone_large_ts, M_WAITOK)); + else + return (uma_zalloc(cache_zone_large, M_WAITOK)); + } if (ts) return (uma_zalloc(cache_zone_small_ts, M_WAITOK)); else @@ -218,7 +229,9 @@ cache_free(struct namecache *ncp) uma_zfree(cache_zone_small_ts, ncp); else uma_zfree(cache_zone_small, ncp); - } else + } else if (ts) + uma_zfree(cache_zone_large_ts, ncp); + else uma_zfree(cache_zone_large, ncp); } @@ -521,6 +534,10 @@ cache_lookup(dvp, vpp, cnp, tsp, ticksp) SDT_PROBE(vfs, namecache, lookup, hit, dvp, "..", *vpp, 0, 0); cache_out_ts(ncp, tsp, ticksp); + if ((ncp->nc_flag & (NCF_ISDOTDOT | NCF_DTS)) == + NCF_DTS && tsp != NULL) + *tsp = ((struct namecache_ts *)ncp)-> + nc_dotdottime; goto success; } } @@ -686,11 +703,12 @@ cache_lookup(dvp, vpp, cnp, tsp, ticksp) * Add an entry to the cache. */ void -cache_enter_time(dvp, vp, cnp, tsp) +cache_enter_time(dvp, vp, cnp, tsp, dtsp) struct vnode *dvp; struct vnode *vp; struct componentname *cnp; struct timespec *tsp; + struct timespec *dtsp; { struct namecache *ncp, *n2; struct namecache_ts *n3; @@ -769,6 +787,10 @@ cache_enter_time(dvp, vp, cnp, tsp) n3->nc_time = *tsp; n3->nc_ticks = ticks; n3->nc_flag |= NCF_TS; + if (dtsp != NULL) { + n3->nc_dotdottime = *dtsp; + n3->nc_flag |= NCF_DTS; + } } len = ncp->nc_nlen = cnp->cn_namelen; hash = fnv_32_buf(cnp->cn_nameptr, len, FNV1_32_INIT); @@ -794,6 +816,12 @@ cache_enter_time(dvp, vp, cnp, tsp) ((struct namecache_ts *)ncp)->nc_time; n3->nc_ticks = ((struct namecache_ts *)ncp)->nc_ticks; + if (dtsp != NULL) { + n3->nc_dotdottime = + ((struct namecache_ts *)ncp)-> + nc_dotdottime; + n3->nc_flag |= NCF_DTS; + } } CACHE_WUNLOCK(); cache_free(ncp); @@ -823,6 +851,11 @@ cache_enter_time(dvp, vp, cnp, tsp) ncp->nc_flag |= NCF_WHITE; } else if (vp->v_type == VDIR) { if (flag != NCF_ISDOTDOT) { + /* + * For this case, the cache entry maps both the + * directory name in it and the name ".." for the + * directory's parent. + */ if ((n2 = vp->v_cache_dd) != NULL && (n2->nc_flag & NCF_ISDOTDOT) != 0) cache_zap(n2); @@ -886,6 +919,9 @@ nchinit(void *dummy __unused) sizeof(struct namecache_ts) + CACHE_PATH_CUTOFF + 1, NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_ZINIT); cache_zone_large = uma_zcreate("L VFS Cache", + sizeof(struct namecache) + NAME_MAX + 1, + NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_ZINIT); + cache_zone_large_ts = uma_zcreate("LTS VFS Cache", sizeof(struct namecache_ts) + NAME_MAX + 1, NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_ZINIT); @@ -1382,7 +1418,7 @@ void cache_enter(struct vnode *dvp, struct vnode *vp, struct componentname *cnp) { - cache_enter_time(dvp, vp, cnp, NULL); + cache_enter_time(dvp, vp, cnp, NULL, NULL); } /* diff --git a/sys/nfsclient/nfs_vnops.c b/sys/nfsclient/nfs_vnops.c index 241af51410a8..191ed400ac33 100644 --- a/sys/nfsclient/nfs_vnops.c +++ b/sys/nfsclient/nfs_vnops.c @@ -912,7 +912,7 @@ nfs_lookup(struct vop_lookup_args *ap) struct vnode *dvp = ap->a_dvp; struct vnode **vpp = ap->a_vpp; struct mount *mp = dvp->v_mount; - struct vattr vattr; + struct vattr dvattr, vattr; struct timespec nctime; int flags = cnp->cn_flags; struct vnode *newvp; @@ -1129,7 +1129,7 @@ nfs_lookup(struct vop_lookup_args *ap) } if (v3) { nfsm_postop_attr_va(newvp, attrflag, &vattr); - nfsm_postop_attr(dvp, dattrflag); + nfsm_postop_attr_va(dvp, dattrflag, &dvattr); } else { nfsm_loadattr(newvp, &vattr); attrflag = 1; @@ -1137,9 +1137,10 @@ nfs_lookup(struct vop_lookup_args *ap) if (cnp->cn_nameiop != LOOKUP && (flags & ISLASTCN)) cnp->cn_flags |= SAVENAME; if ((cnp->cn_flags & MAKEENTRY) && - (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN)) && attrflag) { - cache_enter_time(dvp, newvp, cnp, &vattr.va_ctime); - } + (cnp->cn_nameiop != DELETE || !(flags & ISLASTCN)) && + attrflag != 0 && (newvp->v_type != VDIR || dattrflag != 0)) + cache_enter_time(dvp, newvp, cnp, &vattr.va_ctime, + newvp->v_type != VDIR ? NULL : &dvattr.va_ctime); *vpp = newvp; m_freem(mrep); nfsmout: @@ -1181,7 +1182,7 @@ nfs_lookup(struct vop_lookup_args *ap) &vattr.va_mtime, ==)) { mtx_unlock(&np->n_mtx); cache_enter_time(dvp, NULL, cnp, - &vattr.va_mtime); + &vattr.va_mtime, NULL); } else mtx_unlock(&np->n_mtx); } @@ -2463,11 +2464,11 @@ nfs_readdirplusrpc(struct vnode *vp, struct uio *uiop, struct ucred *cred) nfsuint64 cookie; struct nfsmount *nmp = VFSTONFS(vp->v_mount); struct nfsnode *dnp = VTONFS(vp), *np; - struct vattr vattr; + struct vattr vattr, dvattr; nfsfh_t *fhp; u_quad_t fileno; int error = 0, tlen, more_dirs = 1, blksiz = 0, doit, bigenough = 1, i; - int attrflag, fhsize; + int attrflag, dattrflag, fhsize; #ifndef nolint dp = NULL; @@ -2513,7 +2514,7 @@ nfs_readdirplusrpc(struct vnode *vp, struct uio *uiop, struct ucred *cred) *tl++ = txdr_unsigned(nmp->nm_readdirsize); *tl = txdr_unsigned(nmp->nm_rsize); nfsm_request(vp, NFSPROC_READDIRPLUS, uiop->uio_td, cred); - nfsm_postop_attr(vp, attrflag); + nfsm_postop_attr_va(vp, dattrflag, &dvattr); if (error) { m_freem(mrep); goto nfsmout; @@ -2649,8 +2650,11 @@ nfs_readdirplusrpc(struct vnode *vp, struct uio *uiop, struct ucred *cred) md = mdsav2; dp->d_type = IFTODT(VTTOIF(vattr.va_type)); ndp->ni_vp = newvp; - cache_enter_time(ndp->ni_dvp, ndp->ni_vp, cnp, - &vattr.va_ctime); + if (newvp->v_type != VDIR || dattrflag != 0) + cache_enter_time(ndp->ni_dvp, ndp->ni_vp, + cnp, &vattr.va_ctime, + newvp->v_type != VDIR ? NULL : + &dvattr.va_ctime); } } else { /* Just skip over the file handle */ diff --git a/sys/sys/vnode.h b/sys/sys/vnode.h index 1b62de018231..ec1d6734a29c 100644 --- a/sys/sys/vnode.h +++ b/sys/sys/vnode.h @@ -583,9 +583,10 @@ struct vnode; /* cache_* may belong in namei.h. */ #define cache_enter(dvp, vp, cnp) \ - cache_enter_time(dvp, vp, cnp, NULL) + cache_enter_time(dvp, vp, cnp, NULL, NULL) void cache_enter_time(struct vnode *dvp, struct vnode *vp, - struct componentname *cnp, struct timespec *tsp); + struct componentname *cnp, struct timespec *tsp, + struct timespec *dtsp); int cache_lookup(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp, struct timespec *tsp, int *ticksp); void cache_purge(struct vnode *vp);