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 <shadow@dementia.org>
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Jeffrey Altman <jaltman@openafs.org>
Tested-by: Jeffrey Altman <jaltman@openafs.org>
(cherry picked from commit a00da7d2c9a66f1e5984ec2b3bd3efe10793e1c9)
Change-Id: I3603e746a6df1834612647931cc09228428842db
Reviewed-on: http://gerrit.openafs.org/3132
Tested-by: Derrick Brashear <shadow@dementia.org>
This commit is contained in:
Jeffrey Altman 2010-09-16 14:23:41 +02:00 committed by Derrick Brashear
parent 22cae170a5
commit 47d79237b6
6 changed files with 46 additions and 50 deletions

View File

@ -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();

View File

@ -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;
}

View File

@ -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;

View File

@ -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 */

View File

@ -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

View File

@ -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);