From a32971251670acc7944dcd4b1eade1ab161e587f Mon Sep 17 00:00:00 2001 From: Jeffrey Altman Date: Sat, 28 Feb 2009 15:40:56 +0000 Subject: [PATCH] windows-afsd-btree-20090228 LICENSE MIT Change how BPlusDir enumerations behave with regards to bulk stat operations. If the number of entries in the enumeration is larger than the number of cm_scache objects, then using the previous model of cm_BPlusDirEnumBulkStat being called for the entire enumeration list results in the early objects being recycled and the status info discarded before the caller of cm_BPlusDirNextEnumEntry() receives the name. The revised model triggers bulk stat operations from within NextEnumEntry() as objects requiring status fetching are about to be returned to the caller. This reduces the thrashing of the stat cache. We should consider adding a flag field to cm_BPlusDirEnumerate() or cm_BPlusDirNextEnumEntry() to permit enumeration without status fetching. --- src/WINNT/afsd/cm_btree.c | 98 +++++++++++++++++++++++++++++++++++++++++------ src/WINNT/afsd/cm_btree.h | 12 ++++-- 2 files changed, 96 insertions(+), 14 deletions(-) diff --git a/src/WINNT/afsd/cm_btree.c b/src/WINNT/afsd/cm_btree.c index ec5c853..766997e 100644 --- a/src/WINNT/afsd/cm_btree.c +++ b/src/WINNT/afsd/cm_btree.c @@ -1613,7 +1613,7 @@ cm_BPlusDirLookupOriginalName(cm_dirOp_t * op, clientchar_t *centry, */ slot = getSlot(op->scp->dirBplus, leafNode); if (slot <= BTERROR) { - op->scp->dirDataVersion = 0; + op->scp->dirDataVersion = CM_SCACHE_VERSION_BAD; rc = (slot == BTERROR ? EINVAL : ENOENT); goto done; } @@ -2170,8 +2170,8 @@ cm_BPlusEnumAlloc(afs_uint32 entries) } long -cm_BPlusDirEnumerate(cm_scache_t *scp, afs_uint32 locked, - clientchar_t * maskp, cm_direnum_t **enumpp) +cm_BPlusDirEnumerate(cm_scache_t *scp, cm_user_t *userp, cm_req_t *reqp, + afs_uint32 locked, clientchar_t * maskp, cm_direnum_t **enumpp) { afs_uint32 count = 0, slot, numentries; Nptr leafNode = NONODE, nextLeafNode; @@ -2191,6 +2191,7 @@ cm_BPlusDirEnumerate(cm_scache_t *scp, afs_uint32 locked, * recycled while the enumeration is active. */ cm_HoldSCache(scp); + cm_HoldUser(userp); if (scp->dirBplus == NULL) { osi_Log0(afsd_logp, "cm_BPlusDirEnumerate No BPlus Tree"); @@ -2292,6 +2293,8 @@ cm_BPlusDirEnumerate(cm_scache_t *scp, afs_uint32 locked, } enump->dscp = scp; + enump->userp = userp; + enump->reqFlags = reqp->flags; done: if (!locked) @@ -2303,6 +2306,7 @@ cm_BPlusDirEnumerate(cm_scache_t *scp, afs_uint32 locked, /* release the directory because we failed to generate an enumeration object */ cm_ReleaseSCache(scp); + cm_ReleaseUser(userp); if (enump) { for ( count = 0; count < enump->count && enump->entry[count].name; count++ ) { free(enump->entry[count].name); @@ -2318,12 +2322,17 @@ cm_BPlusDirEnumerate(cm_scache_t *scp, afs_uint32 locked, } long -cm_BPlusDirEnumBulkStat(cm_direnum_t *enump, cm_user_t *userp, cm_req_t *reqp) +cm_BPlusDirEnumBulkStat(cm_direnum_t *enump) { cm_scache_t *dscp = enump->dscp; + cm_user_t *userp = enump->userp; cm_bulkStat_t *bsp; afs_uint32 count; - afs_uint32 code; + afs_uint32 code = 0; + cm_req_t req; + + cm_InitReq(&req); + req.flags = enump->reqFlags; if ( dscp->fid.cell == AFS_FAKE_ROOT_CELL_ID ) return 0; @@ -2345,6 +2354,7 @@ cm_BPlusDirEnumBulkStat(cm_direnum_t *enump, cm_user_t *userp, cm_req_t *reqp) */ lock_ReleaseWrite(&tscp->rw); cm_ReleaseSCache(tscp); + enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS; continue; } lock_ReleaseWrite(&tscp->rw); @@ -2356,21 +2366,82 @@ cm_BPlusDirEnumBulkStat(cm_direnum_t *enump, cm_user_t *userp, cm_req_t *reqp) bsp->fids[i].Volume = enump->entry[count].fid.volume; bsp->fids[i].Vnode = enump->entry[count].fid.vnode; bsp->fids[i].Unique = enump->entry[count].fid.unique; + enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS; if (bsp->counter == AFSCBMAX) { - code = cm_TryBulkStatRPC(dscp, bsp, userp, reqp); + code = cm_TryBulkStatRPC(dscp, bsp, userp, &req); memset(bsp, 0, sizeof(cm_bulkStat_t)); } } if (bsp->counter > 0) - code = cm_TryBulkStatRPC(dscp, bsp, userp, reqp); + code = cm_TryBulkStatRPC(dscp, bsp, userp, &req); free(bsp); - return 0; + return code; } -long +static long +cm_BPlusDirEnumBulkStatNext(cm_direnum_t *enump) +{ + cm_scache_t *dscp = enump->dscp; + cm_user_t *userp = enump->userp; + cm_bulkStat_t *bsp; + afs_uint32 count; + afs_uint32 code = 0; + cm_req_t req; + + cm_InitReq(&req); + req.flags = enump->reqFlags; + + if ( dscp->fid.cell == AFS_FAKE_ROOT_CELL_ID ) + return 0; + + bsp = malloc(sizeof(cm_bulkStat_t)); + memset(bsp, 0, sizeof(cm_bulkStat_t)); + + for ( count = enump->next; count < enump->count; count++ ) { + cm_scache_t *tscp = cm_FindSCache(&enump->entry[count].fid); + int i; + + if (tscp) { + if (lock_TryWrite(&tscp->rw)) { + /* we have an entry that we can look at */ + if (!(tscp->flags & CM_SCACHEFLAG_EACCESS) && cm_HaveCallback(tscp)) { + /* we have a callback on it. Don't bother + * fetching this stat entry, since we're happy + * with the info we have. + */ + lock_ReleaseWrite(&tscp->rw); + cm_ReleaseSCache(tscp); + enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS; + continue; + } + lock_ReleaseWrite(&tscp->rw); + } /* got lock */ + cm_ReleaseSCache(tscp); + } /* found entry */ + + i = bsp->counter++; + bsp->fids[i].Volume = enump->entry[count].fid.volume; + bsp->fids[i].Vnode = enump->entry[count].fid.vnode; + bsp->fids[i].Unique = enump->entry[count].fid.unique; + enump->entry[count].flags |= CM_DIRENUM_FLAG_GOT_STATUS; + + if (bsp->counter == AFSCBMAX) { + code = cm_TryBulkStatRPC(dscp, bsp, userp, &req); + break; + } + } + + if (bsp->counter > 0 && bsp->counter < AFSCBMAX) + code = cm_TryBulkStatRPC(dscp, bsp, userp, &req); + + free(bsp); + return code; +} + +long cm_BPlusDirNextEnumEntry(cm_direnum_t *enump, cm_direnum_entry_t **entrypp) { if (enump == NULL || entrypp == NULL || enump->next >= enump->count) { @@ -2380,6 +2451,9 @@ cm_BPlusDirNextEnumEntry(cm_direnum_t *enump, cm_direnum_entry_t **entrypp) return CM_ERROR_INVAL; } + if (!(enump->entry[enump->next].flags & CM_DIRENUM_FLAG_GOT_STATUS)) + cm_BPlusDirEnumBulkStatNext(enump); + *entrypp = &enump->entry[enump->next++]; if ( enump->next == enump->count ) { osi_Log0(afsd_logp, "cm_BPlusDirNextEnumEntry STOPNOW"); @@ -2401,6 +2475,7 @@ cm_BPlusDirFreeEnumeration(cm_direnum_t *enump) if (enump) { /* Release the directory object */ cm_ReleaseSCache(enump->dscp); + cm_ReleaseUser(enump->userp); for ( count = 0; count < enump->count && enump->entry[count].name; count++ ) { free(enump->entry[count].name); @@ -2411,15 +2486,16 @@ cm_BPlusDirFreeEnumeration(cm_direnum_t *enump) } long -cm_BPlusDirEnumTest(cm_scache_t * dscp, afs_uint32 locked) +cm_BPlusDirEnumTest(cm_scache_t * dscp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked) { cm_direnum_t * enump = NULL; cm_direnum_entry_t * entryp; long code; + osi_Log0(afsd_logp, "cm_BPlusDirEnumTest start"); - for (code = cm_BPlusDirEnumerate(dscp, locked, NULL, &enump); code == 0; ) { + for (code = cm_BPlusDirEnumerate(dscp, userp, reqp, locked, NULL, &enump); code == 0; ) { code = cm_BPlusDirNextEnumEntry(enump, &entryp); if (code == 0 || code == CM_ERROR_STOPNOW) { char buffer[1024]; diff --git a/src/WINNT/afsd/cm_btree.h b/src/WINNT/afsd/cm_btree.h index 5989dfd..f5c897d 100644 --- a/src/WINNT/afsd/cm_btree.h +++ b/src/WINNT/afsd/cm_btree.h @@ -156,20 +156,26 @@ typedef struct cm_direnum_entry { clientchar_t *name; cm_fid_t fid; normchar_t shortName[13]; + afs_uint32 flags; } cm_direnum_entry_t; +#define CM_DIRENUM_FLAG_GOT_STATUS 1 + typedef struct cm_direnum { cm_scache_t *dscp; + cm_user_t *userp; + afs_uint32 reqFlags; afs_uint32 count; afs_uint32 next; cm_direnum_entry_t entry[1]; } cm_direnum_t; -long cm_BPlusDirEnumerate(cm_scache_t *dscp, afs_uint32 locked, clientchar_t *maskp, cm_direnum_t **enumpp); +long cm_BPlusDirEnumerate(cm_scache_t *dscp, cm_user_t *userp, cm_req_t *reqp, + afs_uint32 locked, clientchar_t *maskp, cm_direnum_t **enumpp); long cm_BPlusDirNextEnumEntry(cm_direnum_t *enump, cm_direnum_entry_t **entrypp); long cm_BPlusDirFreeEnumeration(cm_direnum_t *enump); -long cm_BPlusDirEnumTest(cm_scache_t * dscp, afs_uint32 locked); -long cm_BPlusDirEnumBulkStat(cm_direnum_t *enump, cm_user_t *userp, cm_req_t *reqp); +long cm_BPlusDirEnumTest(cm_scache_t * dscp, cm_user_t *userp, cm_req_t *reqp, afs_uint32 locked); +long cm_BPlusDirEnumBulkStat(cm_direnum_t *enump); long cm_InitBPlusDir(void); -- 1.9.4