afs: Traverse discard/free dslot list if errors
[openafs.git] / src / afs / afs_dcache.c
index 5d40929..a932553 100644 (file)
@@ -1066,6 +1066,34 @@ afs_DiscardDCache(struct dcache *adc)
 
 }                              /*afs_DiscardDCache */
 
+/**
+ * Get a dcache entry from the discard or free list
+ *
+ * @param[in] indexp  A pointer to the head of the dcache free list or discard
+ *                    list (afs_freeDCList, or afs_discardDCList)
+ *
+ * @return A dcache from that list, or NULL if none could be retrieved.
+ *
+ * @pre afs_xdcache is write-locked
+ */
+static struct dcache *
+afs_GetDSlotFromList(afs_int32 *indexp)
+{
+    struct dcache *tdc;
+
+    for ( ; *indexp != NULLIDX; indexp = &afs_dvnextTbl[*indexp]) {
+       tdc = afs_GetUnusedDSlot(*indexp);
+       if (tdc) {
+           osi_Assert(tdc->refCount == 1);
+           ReleaseReadLock(&tdc->tlock);
+           *indexp = afs_dvnextTbl[tdc->index];
+           afs_dvnextTbl[tdc->index] = NULLIDX;
+           return tdc;
+       }
+    }
+    return NULL;
+}
+
 /*!
  * Free the next element on the list of discarded cache elements.
  */
@@ -1087,12 +1115,9 @@ afs_FreeDiscardedDCache(void)
     /*
      * Get an entry from the list of discarded cache elements
      */
-    tdc = afs_GetNewDSlot(afs_discardDCList);
-    osi_Assert(tdc->refCount == 1);
-    ReleaseReadLock(&tdc->tlock);
+    tdc = afs_GetDSlotFromList(&afs_discardDCList);
+    osi_Assert(tdc);
 
-    afs_discardDCList = afs_dvnextTbl[tdc->index];
-    afs_dvnextTbl[tdc->index] = NULLIDX;
     afs_discardDCCount--;
     size = ((tdc->f.chunkBytes + afs_fsfragsize) ^ afs_fsfragsize) >> 10;      /* round up */
     afs_blocksDiscarded -= size;
@@ -1314,7 +1339,12 @@ afs_TryToSmush(struct vcache *avc, afs_ucred_t *acred, int sync)
        if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
            int releaseTlock = 1;
            tdc = afs_GetValidDSlot(index);
-           if (!tdc) osi_Panic("afs_TryToSmush tdc");
+           if (!tdc) {
+               /* afs_TryToSmush is best-effort; we may not actually discard
+                * everything, so failure to discard a dcache due to an i/o
+                * error is okay. */
+               continue;
+           }
            if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
                if (sync) {
                    if ((afs_indexFlags[index] & IFDataMod) == 0
@@ -1456,17 +1486,21 @@ afs_FindDCache(struct vcache *avc, afs_size_t abyte)
      */
     i = DCHash(&avc->f.fid, chunk);
     ObtainWriteLock(&afs_xdcache, 278);
-    for (index = afs_dchashTbl[i]; index != NULLIDX;) {
+    for (index = afs_dchashTbl[i]; index != NULLIDX; index = afs_dcnextTbl[index]) {
        if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
            tdc = afs_GetValidDSlot(index);
-           if (!tdc) osi_Panic("afs_FindDCache tdc");
+           if (!tdc) {
+               /* afs_FindDCache is best-effort; we may not find the given
+                * file/offset, so if we cannot find the given dcache due to
+                * i/o errors, that is okay. */
+               continue;
+           }
            ReleaseReadLock(&tdc->tlock);
            if (!FidCmp(&tdc->f.fid, &avc->f.fid) && chunk == tdc->f.chunk) {
                break;          /* leaving refCount high for caller */
            }
            afs_PutDCache(tdc);
        }
-       index = afs_dcnextTbl[index];
     }
     if (index != NULLIDX) {
        hset(afs_indexTimes[tdc->index], afs_indexCounter);
@@ -1506,20 +1540,16 @@ afs_AllocDCache(struct vcache *avc, afs_int32 chunk, afs_int32 lock,
     if (afs_discardDCList == NULLIDX
        || ((lock & 2) && afs_freeDCList != NULLIDX)) {
 
-       afs_indexFlags[afs_freeDCList] &= ~IFFree;
-       tdc = afs_GetNewDSlot(afs_freeDCList);
-       osi_Assert(tdc->refCount == 1);
-       ReleaseReadLock(&tdc->tlock);
+       tdc = afs_GetDSlotFromList(&afs_freeDCList);
+       osi_Assert(tdc);
+       afs_indexFlags[tdc->index] &= ~IFFree;
        ObtainWriteLock(&tdc->lock, 604);
-       afs_freeDCList = afs_dvnextTbl[tdc->index];
        afs_freeDCCount--;
     } else {
-       afs_indexFlags[afs_discardDCList] &= ~IFDiscarded;
-       tdc = afs_GetNewDSlot(afs_discardDCList);
-       osi_Assert(tdc->refCount == 1);
-       ReleaseReadLock(&tdc->tlock);
+       tdc = afs_GetDSlotFromList(&afs_discardDCList);
+       osi_Assert(tdc);
+       afs_indexFlags[tdc->index] &= ~IFDiscarded;
        ObtainWriteLock(&tdc->lock, 605);
-       afs_discardDCList = afs_dvnextTbl[tdc->index];
        afs_discardDCCount--;
        size =
            ((tdc->f.chunkBytes +
@@ -1755,6 +1785,7 @@ afs_GetDCache(struct vcache *avc, afs_size_t abyte,
      */
 
     if (!tdc) {                        /* If the hint wasn't the right dcache entry */
+       int dslot_error = 0;
        /*
         * Hash on the [fid, chunk] and get the corresponding dcache index
         * after write-locking the dcache.
@@ -1772,12 +1803,16 @@ afs_GetDCache(struct vcache *avc, afs_size_t abyte,
 
        ObtainWriteLock(&afs_xdcache, 280);
        us = NULLIDX;
-       for (index = afs_dchashTbl[i]; index != NULLIDX;) {
+       for (index = afs_dchashTbl[i]; index != NULLIDX; us = index, index = afs_dcnextTbl[index]) {
            if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
                tdc = afs_GetValidDSlot(index);
                if (!tdc) {
-                   ReleaseWriteLock(&afs_xdcache);
-                   goto done;
+                   /* we got an i/o error when trying to get the given dslot,
+                    * but do not bail out just yet; it is possible the dcache
+                    * we're looking for is elsewhere, so it doesn't matter if
+                    * we can't load this one. */
+                   dslot_error = 1;
+                   continue;
                }
                ReleaseReadLock(&tdc->tlock);
                /*
@@ -1800,8 +1835,6 @@ afs_GetDCache(struct vcache *avc, afs_size_t abyte,
                afs_PutDCache(tdc);
                tdc = 0;
            }
-           us = index;
-           index = afs_dcnextTbl[index];
        }
 
        /*
@@ -1817,6 +1850,16 @@ afs_GetDCache(struct vcache *avc, afs_size_t abyte,
            afs_Trace2(afs_iclSetp, CM_TRACE_GETDCACHE1, ICL_TYPE_POINTER,
                       avc, ICL_TYPE_INT32, chunk);
 
+           if (dslot_error) {
+               /* We couldn't find the dcache we want, but we hit some i/o
+                * errors when trying to find it, so we're not sure if the
+                * dcache we want is in the cache or not. Error out, so we
+                * don't try to possibly create 2 separate dcaches for the
+                * same exact data. */
+               ReleaseWriteLock(&afs_xdcache);
+               goto done;
+           }
+
            /* Make sure there is a free dcache entry for us to use */
            if (afs_discardDCList == NULLIDX && afs_freeDCList == NULLIDX) {
                while (1) {
@@ -2605,7 +2648,7 @@ afs_WriteThroughDSlots(void)
  */
 
 struct dcache *
-afs_MemGetDSlot(afs_int32 aslot, int needvalid)
+afs_MemGetDSlot(afs_int32 aslot, int indexvalid, int datavalid)
 {
     struct dcache *tdc;
     int existing = 0;
@@ -2626,10 +2669,10 @@ afs_MemGetDSlot(afs_int32 aslot, int needvalid)
        return tdc;
     }
 
-    /* if 'needvalid' is true, the slot must already exist and be populated
+    /* if 'indexvalid' is true, the slot must already exist and be populated
      * somewhere. for memcache, the only place that dcache entries exist is
      * in memory, so if we did not find it above, something is very wrong. */
-    osi_Assert(!needvalid);
+    osi_Assert(!indexvalid);
 
     if (!afs_freeDSList)
        afs_GetDownDSlot(4);
@@ -2690,13 +2733,20 @@ unsigned int last_error = 0, lasterrtime = 0;
  *
  * Parameters:
  *     aslot : Dcache slot to look at.
- *      needvalid : Whether the specified slot should already exist
+ *      indexvalid : 1 if we know the slot we're giving is valid, and thus
+ *                   reading the dcache from the disk index should succeed. 0
+ *                   if we are initializing a new dcache, and so reading from
+ *                   the disk index may fail.
+ *      datavalid : 0 if we are loading a dcache entry from the free or
+ *                  discard list, so we know the data in the given dcache is
+ *                  not valid. 1 if we are loading a known used dcache, so the
+ *                  data in the dcache must be valid.
  *
  * Environment:
  *     afs_xdcache lock write-locked.
  */
 struct dcache *
-afs_UFSGetDSlot(afs_int32 aslot, int needvalid)
+afs_UFSGetDSlot(afs_int32 aslot, int indexvalid, int datavalid)
 {
     afs_int32 code;
     struct dcache *tdc;
@@ -2755,9 +2805,11 @@ afs_UFSGetDSlot(afs_int32 aslot, int needvalid)
        entryok = 0;
 #if defined(KERNEL_HAVE_UERROR)
        last_error = getuerror();
+#else
+       last_error = code;
 #endif
        lasterrtime = osi_Time();
-       if (needvalid) {
+       if (indexvalid) {
            struct osi_stat tstat;
            if (afs_osi_Stat(afs_cacheInodep, &tstat)) {
                tstat.size = -1;
@@ -2777,19 +2829,19 @@ afs_UFSGetDSlot(afs_int32 aslot, int needvalid)
     }
     if (!afs_CellNumValid(tdc->f.fid.Cell)) {
        entryok = 0;
-       if (needvalid) {
+       if (datavalid) {
            osi_Panic("afs: needed valid dcache but index %d off %d has "
                      "invalid cell num %d\n",
                      (int)aslot, off, (int)tdc->f.fid.Cell);
        }
     }
 
-    if (needvalid && tdc->f.fid.Fid.Volume == 0) {
+    if (datavalid && tdc->f.fid.Fid.Volume == 0) {
        osi_Panic("afs: invalid zero-volume dcache entry at slot %d off %d",
                  (int)aslot, off);
     }
 
-    if (!entryok) {
+    if (!entryok || !datavalid) {
        tdc->f.fid.Cell = 0;
        tdc->f.fid.Fid.Volume = 0;
        tdc->f.chunk = -1;