afs_FreeDiscardedDCache: Avoid assert on error
[openafs.git] / src / afs / afs_dcache.c
index a932553..e03c765 100644 (file)
@@ -22,7 +22,7 @@
 
 /* Forward declarations. */
 static void afs_GetDownD(int anumber, int *aneedSpace, afs_int32 buckethint);
-static void afs_FreeDiscardedDCache(void);
+static int afs_FreeDiscardedDCache(void);
 static void afs_DiscardDCache(struct dcache *);
 static void afs_FreeDCache(struct dcache *);
 /* For split cache */
@@ -459,7 +459,16 @@ afs_CacheTruncateDaemon(void)
         */
        while (afs_blocksDiscarded && !afs_WaitForCacheDrain
               && (afs_termState != AFSOP_STOP_TRUNCDAEMON)) {
-           afs_FreeDiscardedDCache();
+           int code = afs_FreeDiscardedDCache();
+           if (code) {
+               /* If we can't free any discarded dcache entries, that's okay.
+                * We're just doing this in the background; if someone needs
+                * discarded entries freed, they will try it themselves and/or
+                * signal us that the cache is too full. In any case, we'll
+                * try doing this again the next time we run through the loop.
+                */
+               break;
+           }
        }
 
        /* See if we need to continue to run. Someone may have
@@ -1096,8 +1105,11 @@ afs_GetDSlotFromList(afs_int32 *indexp)
 
 /*!
  * Free the next element on the list of discarded cache elements.
+ *
+ * Returns -1 if we encountered an error preventing us from freeing a
+ * discarded dcache, or 0 on success.
  */
-static void
+static int
 afs_FreeDiscardedDCache(void)
 {
     struct dcache *tdc;
@@ -1109,14 +1121,17 @@ afs_FreeDiscardedDCache(void)
     ObtainWriteLock(&afs_xdcache, 510);
     if (!afs_blocksDiscarded) {
        ReleaseWriteLock(&afs_xdcache);
-       return;
+       return 0;
     }
 
     /*
      * Get an entry from the list of discarded cache elements
      */
     tdc = afs_GetDSlotFromList(&afs_discardDCList);
-    osi_Assert(tdc);
+    if (!tdc) {
+       ReleaseWriteLock(&afs_xdcache);
+       return -1;
+    }
 
     afs_discardDCCount--;
     size = ((tdc->f.chunkBytes + afs_fsfragsize) ^ afs_fsfragsize) >> 10;      /* round up */
@@ -1145,6 +1160,8 @@ afs_FreeDiscardedDCache(void)
     ReleaseWriteLock(&tdc->lock);
     afs_PutDCache(tdc);
     ReleaseWriteLock(&afs_xdcache);
+
+    return 0;
 }
 
 /*!
@@ -1162,7 +1179,14 @@ afs_MaybeFreeDiscardedDCache(void)
     while (afs_blocksDiscarded
           && (afs_blocksUsed >
               PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks))) {
-       afs_FreeDiscardedDCache();
+       int code = afs_FreeDiscardedDCache();
+       if (code) {
+           /* Callers depend on us to get the afs_blocksDiscarded count down.
+            * If we cannot do that, the callers can spin by calling us over
+            * and over. Panic for now until we can figure out something
+            * better. */
+           osi_Panic("Error freeing discarded dcache");
+       }
     }
     return 0;
 }
@@ -1512,6 +1536,53 @@ afs_FindDCache(struct vcache *avc, afs_size_t abyte)
     return NULL;
 }                              /*afs_FindDCache */
 
+/* only call these from afs_AllocDCache() */
+static struct dcache *
+afs_AllocFreeDSlot(void)
+{
+    struct dcache *tdc;
+
+    tdc = afs_GetDSlotFromList(&afs_freeDCList);
+    if (!tdc) {
+       return NULL;
+    }
+    afs_indexFlags[tdc->index] &= ~IFFree;
+    ObtainWriteLock(&tdc->lock, 604);
+    afs_freeDCCount--;
+
+    return tdc;
+}
+static struct dcache *
+afs_AllocDiscardDSlot(afs_int32 lock)
+{
+    struct dcache *tdc;
+    afs_uint32 size = 0;
+    struct osi_file *file;
+
+    tdc = afs_GetDSlotFromList(&afs_discardDCList);
+    if (!tdc) {
+       return NULL;
+    }
+    afs_indexFlags[tdc->index] &= ~IFDiscarded;
+    ObtainWriteLock(&tdc->lock, 605);
+    afs_discardDCCount--;
+    size =
+       ((tdc->f.chunkBytes +
+         afs_fsfragsize) ^ afs_fsfragsize) >> 10;
+    tdc->f.states &= ~(DRO|DBackup|DRW);
+    afs_DCMoveBucket(tdc, size, 0);
+    afs_blocksDiscarded -= size;
+    afs_stats_cmperf.cacheBlocksDiscarded = afs_blocksDiscarded;
+    if ((lock & 2)) {
+       /* Truncate the chunk so zeroes get filled properly */
+       file = afs_CFileOpen(&tdc->f.inode);
+       afs_CFileTruncate(file, 0);
+       afs_CFileClose(file);
+       afs_AdjustSize(tdc, 0);
+    }
+
+    return tdc;
+}
 
 /*!
  * Get a fresh dcache from the free or discarded list.
@@ -1534,38 +1605,23 @@ afs_AllocDCache(struct vcache *avc, afs_int32 chunk, afs_int32 lock,
                struct VenusFid *ashFid)
 {
     struct dcache *tdc = NULL;
-    afs_uint32 size = 0;
-    struct osi_file *file;
 
-    if (afs_discardDCList == NULLIDX
-       || ((lock & 2) && afs_freeDCList != NULLIDX)) {
-
-       tdc = afs_GetDSlotFromList(&afs_freeDCList);
-       osi_Assert(tdc);
-       afs_indexFlags[tdc->index] &= ~IFFree;
-       ObtainWriteLock(&tdc->lock, 604);
-       afs_freeDCCount--;
+    /* if (lock & 2), prefer 'free' dcaches; otherwise, prefer 'discard'
+     * dcaches. In either case, try both if our first choice doesn't work. */
+    if ((lock & 2)) {
+       tdc = afs_AllocFreeDSlot();
+       if (!tdc) {
+           tdc = afs_AllocDiscardDSlot(lock);
+       }
     } else {
-       tdc = afs_GetDSlotFromList(&afs_discardDCList);
-       osi_Assert(tdc);
-       afs_indexFlags[tdc->index] &= ~IFDiscarded;
-       ObtainWriteLock(&tdc->lock, 605);
-       afs_discardDCCount--;
-       size =
-           ((tdc->f.chunkBytes +
-             afs_fsfragsize) ^ afs_fsfragsize) >> 10;
-       tdc->f.states &= ~(DRO|DBackup|DRW);
-       afs_DCMoveBucket(tdc, size, 0);
-       afs_blocksDiscarded -= size;
-       afs_stats_cmperf.cacheBlocksDiscarded = afs_blocksDiscarded;
-       if (lock & 2) {
-           /* Truncate the chunk so zeroes get filled properly */
-           file = afs_CFileOpen(&tdc->f.inode);
-           afs_CFileTruncate(file, 0);
-           afs_CFileClose(file);
-           afs_AdjustSize(tdc, 0);
+       tdc = afs_AllocDiscardDSlot(lock);
+       if (!tdc) {
+           tdc = afs_AllocFreeDSlot();
        }
     }
+    if (!tdc) {
+       return NULL;
+    }
 
     /*
      * Locks held:
@@ -1860,34 +1916,36 @@ afs_GetDCache(struct vcache *avc, afs_size_t abyte,
                goto done;
            }
 
-           /* Make sure there is a free dcache entry for us to use */
            if (afs_discardDCList == NULLIDX && afs_freeDCList == NULLIDX) {
-               while (1) {
-                   if (!setLocks)
-                       avc->f.states |= CDCLock;
-                   /* just need slots */
-                   afs_GetDownD(5, (int *)0, afs_DCGetBucket(avc));
-                   if (!setLocks)
-                       avc->f.states &= ~CDCLock;
-                   if (afs_discardDCList != NULLIDX
-                       || afs_freeDCList != NULLIDX)
-                       break;
-                   /* If we can't get space for 5 mins we give up and panic */
-                   if (++downDCount > 300) {
-                       osi_Panic("getdcache");
-                    }
-                   ReleaseWriteLock(&afs_xdcache);
-                   /*
-                    * Locks held:
-                    * avc->lock(R) if setLocks
-                    * avc->lock(W) if !setLocks
-                    */
-                   afs_osi_Wait(1000, 0, 0);
-                   goto RetryLookup;
-               }
+               if (!setLocks)
+                   avc->f.states |= CDCLock;
+               /* just need slots */
+               afs_GetDownD(5, (int *)0, afs_DCGetBucket(avc));
+               if (!setLocks)
+                   avc->f.states &= ~CDCLock;
            }
-
            tdc = afs_AllocDCache(avc, chunk, aflags, NULL);
+           if (!tdc) {
+               /* If we can't get space for 5 mins we give up and panic */
+               if (++downDCount > 300)
+                   osi_Panic("getdcache");
+               ReleaseWriteLock(&afs_xdcache);
+               /*
+                * Locks held:
+                * avc->lock(R) if setLocks
+                * avc->lock(W) if !setLocks
+                */
+               afs_osi_Wait(1000, 0, 0);
+               goto RetryLookup;
+           }
+
+           /*
+            * Locks held:
+            * avc->lock(R) if setLocks
+            * avc->lock(W) if !setLocks
+            * tdc->lock(W)
+            * afs_xdcache(W)
+            */
 
            /*
             * Now add to the two hash chains - note that i is still set
@@ -3459,6 +3517,7 @@ afs_MakeShadowDir(struct vcache *avc, struct dcache *adc)
 
     /* Get a fresh dcache. */
     new_dc = afs_AllocDCache(avc, 0, 0, &shadow_fid);
+    osi_Assert(new_dc);
 
     ObtainReadLock(&adc->mflock);