From 47d79237b6378a95907df110d96c262896779e61 Mon Sep 17 00:00:00 2001 From: Jeffrey Altman Date: Thu, 16 Sep 2010 14:23:41 +0200 Subject: [PATCH] Windows: Negative Caching for Volume Lookups If a volume lookup returns VL_NOENT or VL_BADNAME, cache the negative response for five minutes. This prevents volume lookup storms caused by the same volume lookup being performed repeated during a short time period. This can happen if mount points to volumes that do not exist are present in a directory that is being evaluated by Windows Explorer or Common Control File Dialogs. This functionality is implemented by storing the most recent update time for the volume group as part of the cm_volume_t. A non-existing volume group is identified with a new CM_VOLUMEFLAG_NOEXIST flag. The presence of the lastUpdateTime value also permits volume location information to expire at lastUpdateTime + lifetime instead of expiring all volume information simultaneously each lifetime period. LICENSE MIT Reviewed-on: http://gerrit.openafs.org/2771 Reviewed-by: Derrick Brashear Tested-by: BuildBot Reviewed-by: Jeffrey Altman Tested-by: Jeffrey Altman (cherry picked from commit a00da7d2c9a66f1e5984ec2b3bd3efe10793e1c9) Change-Id: I3603e746a6df1834612647931cc09228428842db Reviewed-on: http://gerrit.openafs.org/3132 Tested-by: Derrick Brashear --- src/WINNT/afsd/cm_daemon.c | 8 +++- src/WINNT/afsd/cm_ioctl.c | 2 +- src/WINNT/afsd/cm_memmap.c | 2 +- src/WINNT/afsd/cm_scache.h | 2 +- src/WINNT/afsd/cm_volume.c | 78 +++++++++++++++++--------------------- src/WINNT/afsd/cm_volume.h | 4 +- 6 files changed, 46 insertions(+), 50 deletions(-) diff --git a/src/WINNT/afsd/cm_daemon.c b/src/WINNT/afsd/cm_daemon.c index fac1be3682..72ec250962 100644 --- a/src/WINNT/afsd/cm_daemon.c +++ b/src/WINNT/afsd/cm_daemon.c @@ -507,11 +507,15 @@ void cm_Daemon(long parm) cm_VolStatus_Network_Addr_Change(); } - if (now > lastVolCheck + cm_daemonCheckVolInterval && + /* + * Once every five minutes inspect the volume list and enforce + * the volume location expiration time. + */ + if (now > lastVolCheck + 300 && daemon_ShutdownFlag == 0 && powerStateSuspended == 0) { lastVolCheck = now; - cm_RefreshVolumes(); + cm_RefreshVolumes(cm_daemonCheckVolInterval); if (daemon_ShutdownFlag == 1) break; now = osi_Time(); diff --git a/src/WINNT/afsd/cm_ioctl.c b/src/WINNT/afsd/cm_ioctl.c index b25cc0cfa9..a9bcf15bd5 100644 --- a/src/WINNT/afsd/cm_ioctl.c +++ b/src/WINNT/afsd/cm_ioctl.c @@ -1285,7 +1285,7 @@ cm_IoctlCheckServers(struct cm_ioctl *ioctlp, struct cm_user *userp) afs_int32 cm_IoctlCheckVolumes(cm_ioctl_t *ioctlp, cm_user_t *userp) { - cm_RefreshVolumes(); + cm_RefreshVolumes(0); return 0; } diff --git a/src/WINNT/afsd/cm_memmap.c b/src/WINNT/afsd/cm_memmap.c index 3b94612916..6fb91ed220 100644 --- a/src/WINNT/afsd/cm_memmap.c +++ b/src/WINNT/afsd/cm_memmap.c @@ -874,7 +874,7 @@ cm_InitMappedMemory(DWORD virtualCache, char * cachePath, DWORD stats, DWORD max cm_data.buf_blockSize = blockSize; cm_data.buf_hashSize = osi_PrimeLessThan((afs_uint32)(cacheBlocks/7 + 1)); - cm_data.mountRootGen = time(NULL); + cm_data.mountRootGen = 0; baseAddress += ComputeSizeOfConfigData(); cm_data.volumeBaseAddress = (cm_volume_t *) baseAddress; diff --git a/src/WINNT/afsd/cm_scache.h b/src/WINNT/afsd/cm_scache.h index 5bfc6fa1b9..a04f314027 100644 --- a/src/WINNT/afsd/cm_scache.h +++ b/src/WINNT/afsd/cm_scache.h @@ -145,7 +145,7 @@ typedef struct cm_scache { * the link contents here. */ cm_fid_t mountRootFid; /* mounted on root */ - time_t mountRootGen; /* time to update mountRootFidp? */ + time_t mountRootGen; /* time to update mountRootFid? */ cm_fid_t dotdotFid; /* parent of volume root */ /* callback info */ diff --git a/src/WINNT/afsd/cm_volume.c b/src/WINNT/afsd/cm_volume.c index 9976fe53b3..ffdfa43fa7 100644 --- a/src/WINNT/afsd/cm_volume.c +++ b/src/WINNT/afsd/cm_volume.c @@ -190,6 +190,17 @@ long cm_UpdateVolumeLocation(struct cm_cell *cellp, cm_user_t *userp, cm_req_t * lock_AssertWrite(&volp->rw); + /* + * If the last volume update was in the last five + * minutes and it did not exist, then avoid the RPC + * and return No Such Volume immediately. + */ + if ((volp->flags & CM_VOLUMEFLAG_NOEXIST) && + volp->lastUpdateTime + 600 < time(0)) + { + return CM_ERROR_NOSUCHVOLUME; + } + #ifdef AFS_FREELANCE_CLIENT if ( cellp->cellID == AFS_FAKE_ROOT_CELL_ID && volp->vol[RWVOL].ID == AFS_FAKE_ROOT_VOL_ID ) { @@ -614,28 +625,13 @@ long cm_UpdateVolumeLocation(struct cm_cell *cellp, cm_user_t *userp, cm_req_t * cm_RandomizeServer(&volp->vol[ROVOL].serversp); } - rwNewstate = rwServers_alldown ? vl_alldown : vl_online; roNewstate = roServers_alldown ? vl_alldown : vl_online; bkNewstate = bkServers_alldown ? vl_alldown : vl_online; + + volp->flags &= ~CM_VOLUMEFLAG_NOEXIST; } else if (code == CM_ERROR_NOSUCHVOLUME || code == VL_NOENT || code == VL_BADNAME) { - /* this volume does not exist - we should discard it */ - if (volp->flags & CM_VOLUMEFLAG_IN_HASH) - cm_RemoveVolumeFromNameHashTable(volp); - for ( volType = RWVOL; volType < NUM_VOL_TYPES; volType++) { - if (volp->vol[volType].flags & CM_VOLUMEFLAG_IN_HASH) - cm_RemoveVolumeFromIDHashTable(volp, volType); - if (volp->vol[volType].ID) { - cm_VolumeStatusNotification(volp, volp->vol[volType].ID, volp->vol[volType].state, vl_alldown); - volp->vol[volType].ID = 0; - } - cm_SetFid(&volp->vol[volType].dotdotFid, 0, 0, 0, 0); - } - - /* Move to the end so it will be recycled first */ - cm_MoveVolumeToLRULast(volp); - - volp->namep[0] ='\0'; + volp->flags |= CM_VOLUMEFLAG_NOEXIST; } else { rwNewstate = roNewstate = bkNewstate = vl_alldown; } @@ -656,6 +652,8 @@ long cm_UpdateVolumeLocation(struct cm_cell *cellp, cm_user_t *userp, cm_req_t * volp->vol[BACKVOL].state = bkNewstate; } + volp->lastUpdateTime = time(0); + if (code == 0) volp->flags &= ~CM_VOLUMEFLAG_RESET; @@ -1144,48 +1142,40 @@ long cm_GetROVolumeID(cm_volume_t *volp) return id; } -void cm_RefreshVolumes(void) +void cm_RefreshVolumes(int lifetime) { cm_volume_t *volp; cm_scache_t *scp; afs_int32 refCount; + time_t now; - cm_data.mountRootGen = time(NULL); + now = time(NULL); - /* force a re-loading of volume data from the vldb */ + /* force mount point target updates */ + if (cm_data.mountRootGen + lifetime <= now) + cm_data.mountRootGen = now; + + /* + * force a re-loading of volume data from the vldb + * if the lifetime for the cached data has expired + */ lock_ObtainRead(&cm_volumeLock); for (volp = cm_data.allVolumesp; volp; volp=volp->allNextp) { InterlockedIncrement(&volp->refCount); lock_ReleaseRead(&cm_volumeLock); - lock_ObtainWrite(&volp->rw); - volp->flags |= CM_VOLUMEFLAG_RESET; - lock_ReleaseWrite(&volp->rw); - + if (!(volp->flags & CM_VOLUMEFLAG_RESET)) { + lock_ObtainWrite(&volp->rw); + if (volp->lastUpdateTime + lifetime <= now) + volp->flags |= CM_VOLUMEFLAG_RESET; + lock_ReleaseWrite(&volp->rw); + } + lock_ObtainRead(&cm_volumeLock); refCount = InterlockedDecrement(&volp->refCount); osi_assertx(refCount >= 0, "cm_volume_t refCount underflow"); } lock_ReleaseRead(&cm_volumeLock); - - /* force mount points to be re-evaluated so that - * if the volume location has changed we will pick - * that up - */ - for ( scp = cm_data.scacheLRUFirstp; - scp; - scp = (cm_scache_t *) osi_QNext(&scp->q)) { - if ( scp->fileType == CM_SCACHETYPE_MOUNTPOINT -#ifdef AFS_FREELANCE_CLIENT - && !(scp->fid.cell == AFS_FAKE_ROOT_CELL_ID && scp->fid.volume == AFS_FAKE_ROOT_VOL_ID) -#endif - ) { - lock_ObtainWrite(&scp->rw); - scp->mountPointStringp[0] = '\0'; - lock_ReleaseWrite(&scp->rw); - } - } - } void diff --git a/src/WINNT/afsd/cm_volume.h b/src/WINNT/afsd/cm_volume.h index 7ec1a8c7a7..44f92c6eec 100644 --- a/src/WINNT/afsd/cm_volume.h +++ b/src/WINNT/afsd/cm_volume.h @@ -45,6 +45,7 @@ typedef struct cm_volume { struct cm_server *cbServerpRO; /* server granting RO callback; by cm_scacheLock */ time_t cbExpiresRO; /* latest RO expiration time; by cm_scacheLock */ time_t creationDateRO; /* latest volume creation date; 0 if unknown; by cm_scacheLock */ + time_t lastUpdateTime; /* most recent volume location update cm_volumeLock */ } cm_volume_t; #define CM_VOLUMEFLAG_RESET 1 /* reload this info on next use */ @@ -52,6 +53,7 @@ typedef struct cm_volume { #define CM_VOLUMEFLAG_IN_LRU_QUEUE 4 #define CM_VOLUMEFLAG_UPDATING_VL 8 #define CM_VOLUMEFLAG_DFS_VOLUME 16 +#define CM_VOLUMEFLAG_NOEXIST 32 typedef struct cm_volumeRef { struct cm_volumeRef * next; @@ -100,7 +102,7 @@ extern cm_serverRef_t **cm_GetVolServers(cm_volume_t *volp, afs_uint32 volume, extern void cm_ChangeRankVolume(cm_server_t *tsp); -extern void cm_RefreshVolumes(void); +extern void cm_RefreshVolumes(int lifetime); extern long cm_ValidateVolume(void);