Windows: Negative Caching for Volume Lookups
authorJeffrey Altman <jaltman@your-file-system.com>
Thu, 16 Sep 2010 12:23:41 +0000 (14:23 +0200)
committerJeffrey Altman <jaltman@openafs.org>
Tue, 21 Sep 2010 04:06:50 +0000 (21:06 -0700)
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

Change-Id: I01c5122a153fadc3824ff916655b0199d5c1de3c
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>

src/WINNT/afsd/cm_daemon.c
src/WINNT/afsd/cm_ioctl.c
src/WINNT/afsd/cm_memmap.c
src/WINNT/afsd/cm_scache.h
src/WINNT/afsd/cm_volume.c
src/WINNT/afsd/cm_volume.h

index fac1be3..72ec250 100644 (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();
index b25cc0c..a9bcf15 100644 (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;
 }       
 
index 3b94612..6fb91ed 100644 (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;
index 5bfc6fa..a04f314 100644 (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 */
index 9976fe5..ffdfa43 100644 (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;
-    } 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 if (code == CM_ERROR_NOSUCHVOLUME || code == VL_NOENT || code == VL_BADNAME) {
+        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 mount point target updates */
+    if (cm_data.mountRootGen + lifetime <= now)
+        cm_data.mountRootGen = now;
 
-    /* force a re-loading of volume data from the vldb */
+    /*
+     * 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
index 7ec1a8c..44f92c6 100644 (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);