--- //depot/projects/smpng/sys/kern/vfs_cache.c 2008/09/24 21:56:18 +++ //depot/user/jhb/lock/kern/vfs_cache.c 2008/09/24 22:29:09 @@ -42,9 +42,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -109,11 +109,14 @@ #endif struct nchstats nchstats; /* cache effectiveness statistics */ -static struct mtx cache_lock; -MTX_SYSINIT(vfscache, &cache_lock, "Name Cache", MTX_DEF); +static struct rwlock cache_lock; +RW_SYSINIT(vfscache, &cache_lock, "Name Cache"); -#define CACHE_LOCK() mtx_lock(&cache_lock) -#define CACHE_UNLOCK() mtx_unlock(&cache_lock) +#define CACHE_UPGRADE_LOCK() rw_try_upgrade(&cache_lock) +#define CACHE_RLOCK() rw_rlock(&cache_lock) +#define CACHE_RUNLOCK() rw_runlock(&cache_lock) +#define CACHE_WLOCK() rw_wlock(&cache_lock) +#define CACHE_WUNLOCK() rw_wunlock(&cache_lock) /* * UMA zones for the VFS cache. @@ -162,6 +165,7 @@ static u_long numposhits; STATNODE(CTLFLAG_RD, numposhits, &numposhits); static u_long numnegzaps; STATNODE(CTLFLAG_RD, numnegzaps, &numnegzaps); static u_long numneghits; STATNODE(CTLFLAG_RD, numneghits, &numneghits); +static u_long numupgrades; STATNODE(CTLFLAG_RD, numupgrades, &numupgrades); SYSCTL_OPAQUE(_vfs_cache, OID_AUTO, nchstats, CTLFLAG_RD, &nchstats, sizeof(nchstats), "LU", "VFS cache effectiveness statistics"); @@ -199,12 +203,12 @@ /* Scan hash tables for applicable entries */ for (ncpp = nchashtbl; n_nchash > 0; n_nchash--, ncpp++) { - CACHE_LOCK(); + CACHE_RLOCK(); count = 0; LIST_FOREACH(ncp, ncpp, nc_hash) { count++; } - CACHE_UNLOCK(); + CACHE_RUNLOCK(); error = SYSCTL_OUT(req, &count, sizeof(count)); if (error) return (error); @@ -233,11 +237,11 @@ /* Scan hash tables for applicable entries */ for (ncpp = nchashtbl; n_nchash > 0; n_nchash--, ncpp++) { count = 0; - CACHE_LOCK(); + CACHE_RLOCK(); LIST_FOREACH(ncp, ncpp, nc_hash) { count++; } - CACHE_UNLOCK(); + CACHE_RUNLOCK(); if (count) used++; if (maxlength < count) @@ -274,7 +278,7 @@ { struct vnode *vp; - mtx_assert(&cache_lock, MA_OWNED); + rw_assert(&cache_lock, RA_WLOCKED); CTR2(KTR_VFS, "cache_zap(%p) vp %p", ncp, ncp->nc_vp); vp = NULL; LIST_REMOVE(ncp, nc_hash); @@ -321,16 +325,19 @@ { struct namecache *ncp; u_int32_t hash; - int error, ltype; + int error, ltype, wlocked; if (!doingcache) { cnp->cn_flags &= ~MAKEENTRY; return (0); } retry: - CACHE_LOCK(); + CACHE_RLOCK(); + wlocked = 0; numcalls++; + error = 0; +retry_wlocked: if (cnp->cn_nameptr[0] == '.') { if (cnp->cn_namelen == 1) { *vpp = dvp; @@ -343,8 +350,7 @@ dotdothits++; if (dvp->v_dd == NULL || (cnp->cn_flags & MAKEENTRY) == 0) { - CACHE_UNLOCK(); - return (0); + goto unlock; } *vpp = dvp->v_dd; CTR3(KTR_VFS, "cache_lookup(%p, %s) found %p via ..", @@ -363,23 +369,24 @@ } /* We failed to find an entry */ - if (ncp == 0) { + if (ncp == NULL) { if ((cnp->cn_flags & MAKEENTRY) == 0) { nummisszap++; } else { nummiss++; } nchstats.ncs_miss++; - CACHE_UNLOCK(); - return (0); + goto unlock; } /* We don't want to have an entry, so dump it */ if ((cnp->cn_flags & MAKEENTRY) == 0) { numposzaps++; nchstats.ncs_badhits++; + if (!wlocked && !CACHE_UPGRADE_LOCK()) + goto wlock; cache_zap(ncp); - CACHE_UNLOCK(); + CACHE_WUNLOCK(); return (0); } @@ -397,11 +404,15 @@ if (cnp->cn_nameiop == CREATE) { numnegzaps++; nchstats.ncs_badhits++; + if (!wlocked && !CACHE_UPGRADE_LOCK()) + goto wlock; cache_zap(ncp); - CACHE_UNLOCK(); + CACHE_WUNLOCK(); return (0); } + if (!wlocked && !CACHE_UPGRADE_LOCK()) + goto wlock; numneghits++; /* * We found a "negative" match, so we shift it to the end of @@ -414,9 +425,20 @@ nchstats.ncs_neghits++; if (ncp->nc_flag & NCF_WHITE) cnp->cn_flags |= ISWHITEOUT; - CACHE_UNLOCK(); + CACHE_WUNLOCK(); return (ENOENT); +wlock: + /* + * We need to update the cache after our lookup, so upgrade to + * a write lock and retry the operation. + */ + CACHE_RUNLOCK(); + CACHE_WLOCK(); + numupgrades++; + wlocked = 1; + goto retry_wlocked; + success: /* * On success we return a locked and ref'd vnode as per the lookup @@ -424,7 +446,10 @@ */ if (dvp == *vpp) { /* lookup on "." */ VREF(*vpp); - CACHE_UNLOCK(); + if (wlocked) + CACHE_WUNLOCK(); + else + CACHE_RUNLOCK(); /* * When we lookup "." we still can be asked to lock it * differently... @@ -450,7 +475,10 @@ VOP_UNLOCK(dvp, 0); } VI_LOCK(*vpp); - CACHE_UNLOCK(); + if (wlocked) + CACHE_WUNLOCK(); + else + CACHE_RUNLOCK(); error = vget(*vpp, cnp->cn_lkflags | LK_INTERLOCK, cnp->cn_thread); if (cnp->cn_flags & ISDOTDOT) vn_lock(dvp, ltype | LK_RETRY); @@ -463,6 +491,13 @@ ASSERT_VOP_ELOCKED(*vpp, "cache_lookup"); } return (-1); + +unlock: + if (wlocked) + CACHE_WUNLOCK(); + else + CACHE_RUNLOCK(); + return (0); } /* @@ -500,10 +535,10 @@ * cache_purge() time. */ if (cnp->cn_namelen == 2 && cnp->cn_nameptr[1] == '.') { - CACHE_LOCK(); + CACHE_WLOCK(); if (!TAILQ_EMPTY(&dvp->v_cache_dst)) dvp->v_dd = vp; - CACHE_UNLOCK(); + CACHE_WUNLOCK(); return; } } @@ -522,7 +557,7 @@ hash = fnv_32_buf(cnp->cn_nameptr, len, FNV1_32_INIT); bcopy(cnp->cn_nameptr, ncp->nc_name, len); hash = fnv_32_buf(&dvp, sizeof(dvp), hash); - CACHE_LOCK(); + CACHE_WLOCK(); /* * See if this vnode is already in the cache with this name. @@ -536,7 +571,7 @@ if (n2->nc_dvp == dvp && n2->nc_nlen == cnp->cn_namelen && !bcmp(n2->nc_name, cnp->cn_nameptr, n2->nc_nlen)) { - CACHE_UNLOCK(); + CACHE_WUNLOCK(); cache_free(ncp); return; } @@ -582,7 +617,7 @@ vhold(dvp); if (zap) cache_zap(ncp); - CACHE_UNLOCK(); + CACHE_WUNLOCK(); } /* @@ -613,13 +651,13 @@ { CTR1(KTR_VFS, "cache_purge(%p)", vp); - CACHE_LOCK(); + CACHE_WLOCK(); while (!LIST_EMPTY(&vp->v_cache_src)) cache_zap(LIST_FIRST(&vp->v_cache_src)); while (!TAILQ_EMPTY(&vp->v_cache_dst)) cache_zap(TAILQ_FIRST(&vp->v_cache_dst)); vp->v_dd = NULL; - CACHE_UNLOCK(); + CACHE_WUNLOCK(); } /* @@ -633,14 +671,14 @@ struct namecache *ncp, *nnp; /* Scan hash tables for applicable entries */ - CACHE_LOCK(); + CACHE_WLOCK(); for (ncpp = &nchashtbl[nchash]; ncpp >= nchashtbl; ncpp--) { LIST_FOREACH_SAFE(ncp, ncpp, nc_hash, nnp) { if (ncp->nc_dvp->v_mount == mp) cache_zap(ncp); } } - CACHE_UNLOCK(); + CACHE_WUNLOCK(); } /* @@ -833,20 +871,20 @@ error = 0; slash_prefixed = 0; - CACHE_LOCK(); + CACHE_RLOCK(); numfullpathcalls++; if (vp->v_type != VDIR) { ncp = TAILQ_FIRST(&vp->v_cache_dst); if (!ncp) { numfullpathfail2++; - CACHE_UNLOCK(); + CACHE_RUNLOCK(); return (ENOENT); } for (i = ncp->nc_nlen - 1; i >= 0 && bp > buf; i--) *--bp = ncp->nc_name[i]; if (bp == buf) { numfullpathfail4++; - CACHE_UNLOCK(); + CACHE_RUNLOCK(); return (ENOMEM); } *--bp = '/'; @@ -886,20 +924,20 @@ vp = ncp->nc_dvp; } if (error) { - CACHE_UNLOCK(); + CACHE_RUNLOCK(); return (error); } if (!slash_prefixed) { if (bp == buf) { numfullpathfail4++; - CACHE_UNLOCK(); + CACHE_RUNLOCK(); return (ENOMEM); } else { *--bp = '/'; } } numfullpathfound++; - CACHE_UNLOCK(); + CACHE_RUNLOCK(); *retbuf = bp; return (0); @@ -911,15 +949,15 @@ struct namecache *ncp; int l; - CACHE_LOCK(); + CACHE_RLOCK(); ncp = TAILQ_FIRST(&vp->v_cache_dst); if (!ncp) { - CACHE_UNLOCK(); + CACHE_RUNLOCK(); return (ENOENT); } l = min(ncp->nc_nlen, buflen - 1); memcpy(buf, ncp->nc_name, l); - CACHE_UNLOCK(); + CACHE_RUNLOCK(); buf[l] = '\0'; return (0); }