afs: the assert seems to be inverted when using memcache
[openafs.git] / src / afs / afs_dcache.c
index da56b7c..c2a5b04 100644 (file)
@@ -422,10 +422,16 @@ afs_CacheTruncateDaemon(void)
            for (counter = 0; counter < 10; counter++) {
                space_needed =
                    afs_blocksUsed - afs_blocksDiscarded - cb_lowat;
+               if (space_needed < 0)
+                   space_needed = 0;
                slots_needed =
                    dc_hiwat - afs_freeDCCount - afs_discardDCCount;
-               afs_GetDownD(slots_needed, &space_needed, 0);
+               if (slots_needed < 0)
+                   slots_needed = 0;
+               if (slots_needed || space_needed)
+                   afs_GetDownD(slots_needed, &space_needed, 0);
                if ((space_needed <= 0) && (slots_needed <= 0)) {
+                   afs_CacheTooFull = 0;
                    break;
                }
                if (afs_termState == AFSOP_STOP_TRUNCDAEMON)
@@ -666,15 +672,17 @@ afs_GetDownD(int anumber, int *aneedSpace, afs_int32 buckethint)
         * during the truncate operation.
         */
        for (i = 0; i < victimPtr; i++) {
-           tdc = afs_GetDSlot(victims[i], 0);
+           tdc = afs_GetValidDSlot(victims[i]);
            /* We got tdc->tlock(R) here */
-           if (tdc->refCount == 1)
+           if (tdc && tdc->refCount == 1)
                victimDCs[i] = tdc;
            else
                victimDCs[i] = 0;
-           ReleaseReadLock(&tdc->tlock);
-           if (!victimDCs[i])
-               afs_PutDCache(tdc);
+           if (tdc) {
+               ReleaseReadLock(&tdc->tlock);
+               if (!victimDCs[i])
+                   afs_PutDCache(tdc);
+           }
        }
        for (i = 0; i < victimPtr; i++) {
            /* q is first elt in dcache entry */
@@ -683,7 +691,7 @@ afs_GetDownD(int anumber, int *aneedSpace, afs_int32 buckethint)
             * have to verify, before proceeding, that there are no other
             * references to this dcache entry, even now.  Note that we
             * compare with 1, since we bumped it above when we called
-            * afs_GetDSlot to preserve the entry's identity.
+            * afs_GetValidDSlot to preserve the entry's identity.
             */
            if (tdc && tdc->refCount == 1) {
                unsigned char chunkFlags;
@@ -1079,7 +1087,7 @@ afs_FreeDiscardedDCache(void)
     /*
      * Get an entry from the list of discarded cache elements
      */
-    tdc = afs_GetDSlot(afs_discardDCList, 0);
+    tdc = afs_GetNewDSlot(afs_discardDCList);
     osi_Assert(tdc->refCount == 1);
     ReleaseReadLock(&tdc->tlock);
 
@@ -1195,7 +1203,7 @@ afs_GetDownDSlot(int anumber)
                }
 #else
                tdc->dflags &= ~DFEntryMod;
-               afs_WriteDCache(tdc, 1);
+               osi_Assert(afs_WriteDCache(tdc, 1) == 0);
 #endif
            }
 
@@ -1305,7 +1313,8 @@ afs_TryToSmush(struct vcache *avc, afs_ucred_t *acred, int sync)
        i = afs_dvnextTbl[index];       /* next pointer this hash table */
        if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
            int releaseTlock = 1;
-           tdc = afs_GetDSlot(index, NULL);
+           tdc = afs_GetValidDSlot(index);
+           if (!tdc) osi_Panic("afs_TryToSmush tdc");
            if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
                if (sync) {
                    if ((afs_indexFlags[index] & IFDataMod) == 0
@@ -1394,12 +1403,14 @@ afs_DCacheMissingChunks(struct vcache *avc)
     for (index = afs_dvhashTbl[i]; index != NULLIDX; index = i) {
         i = afs_dvnextTbl[index];
         if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
-            tdc = afs_GetDSlot(index, NULL);
-            if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
-               totalChunks--;
-            }
-            ReleaseReadLock(&tdc->tlock);
-            afs_PutDCache(tdc);
+            tdc = afs_GetValidDSlot(index);
+           if (tdc) {
+               if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
+                   totalChunks--;
+               }
+               ReleaseReadLock(&tdc->tlock);
+               afs_PutDCache(tdc);
+           }
         }
     }
     ReleaseWriteLock(&afs_xdcache);
@@ -1447,7 +1458,8 @@ afs_FindDCache(struct vcache *avc, afs_size_t abyte)
     ObtainWriteLock(&afs_xdcache, 278);
     for (index = afs_dchashTbl[i]; index != NULLIDX;) {
        if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
-           tdc = afs_GetDSlot(index, NULL);
+           tdc = afs_GetValidDSlot(index);
+           if (!tdc) osi_Panic("afs_FindDCache tdc");
            ReleaseReadLock(&tdc->tlock);
            if (!FidCmp(&tdc->f.fid, &avc->f.fid) && chunk == tdc->f.chunk) {
                break;          /* leaving refCount high for caller */
@@ -1495,7 +1507,7 @@ afs_AllocDCache(struct vcache *avc, afs_int32 chunk, afs_int32 lock,
        || ((lock & 2) && afs_freeDCList != NULLIDX)) {
 
        afs_indexFlags[afs_freeDCList] &= ~IFFree;
-       tdc = afs_GetDSlot(afs_freeDCList, 0);
+       tdc = afs_GetNewDSlot(afs_freeDCList);
        osi_Assert(tdc->refCount == 1);
        ReleaseReadLock(&tdc->tlock);
        ObtainWriteLock(&tdc->lock, 604);
@@ -1503,7 +1515,7 @@ afs_AllocDCache(struct vcache *avc, afs_int32 chunk, afs_int32 lock,
        afs_freeDCCount--;
     } else {
        afs_indexFlags[afs_discardDCList] &= ~IFDiscarded;
-       tdc = afs_GetDSlot(afs_discardDCList, 0);
+       tdc = afs_GetNewDSlot(afs_discardDCList);
        osi_Assert(tdc->refCount == 1);
        ReleaseReadLock(&tdc->tlock);
        ObtainWriteLock(&tdc->lock, 605);
@@ -1621,7 +1633,6 @@ afs_GetDCache(struct vcache *avc, afs_size_t abyte,
     afs_int32 index;
     afs_int32 us;
     afs_int32 chunk;
-    afs_size_t maxGoodLength;  /* amount of good data at server */
     afs_size_t Position = 0;
     afs_int32 size, tlen;      /* size of segment to transfer */
     struct afs_FetchOutput *tsmall = 0;
@@ -1763,7 +1774,11 @@ afs_GetDCache(struct vcache *avc, afs_size_t abyte,
        us = NULLIDX;
        for (index = afs_dchashTbl[i]; index != NULLIDX;) {
            if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
-               tdc = afs_GetDSlot(index, NULL);
+               tdc = afs_GetValidDSlot(index);
+               if (!tdc) {
+                   ReleaseWriteLock(&afs_xdcache);
+                   goto done;
+               }
                ReleaseReadLock(&tdc->tlock);
                /*
                 * Locks held:
@@ -2071,10 +2086,6 @@ afs_GetDCache(struct vcache *avc, afs_size_t abyte,
            goto RetryGetDCache;
        }
 
-       /* Do not fetch data beyond truncPos. */
-       maxGoodLength = avc->f.m.Length;
-       if (avc->f.truncPos < maxGoodLength)
-           maxGoodLength = avc->f.truncPos;
        Position = AFS_CHUNKBASE(abyte);
        if (vType(avc) == VDIR) {
            size = avc->f.m.Length;
@@ -2084,16 +2095,49 @@ afs_GetDCache(struct vcache *avc, afs_size_t abyte,
            }
            size = 999999999;   /* max size for transfer */
        } else {
+           afs_size_t maxGoodLength;
+
+           /* estimate how much data we're expecting back from the server,
+            * and reserve space in the dcache entry for it */
+
+           maxGoodLength = avc->f.m.Length;
+           if (avc->f.truncPos < maxGoodLength)
+               maxGoodLength = avc->f.truncPos;
+
            size = AFS_CHUNKSIZE(abyte);        /* expected max size */
-           /* don't read past end of good data on server */
            if (Position + size > maxGoodLength)
                size = maxGoodLength - Position;
            if (size < 0)
                size = 0;       /* Handle random races */
            if (size > tdc->f.chunkBytes) {
-               /* pre-reserve space for file */
+               /* pre-reserve estimated space for file */
                afs_AdjustSize(tdc, size);      /* changes chunkBytes */
-               /* max size for transfer still in size */
+           }
+
+           if (size) {
+               /* For the actual fetch, do not limit the request to the
+                * length of the file. If this results in a read past EOF on
+                * the server, the server will just reply with less data than
+                * requested. If we limit ourselves to only requesting data up
+                * to the avc file length, we open ourselves up to races if the
+                * file is extended on the server at about the same time.
+                *
+                * However, we must restrict ourselves to the avc->f.truncPos
+                * length, since this represents an outstanding local
+                * truncation of the file that will be committed to the
+                * fileserver when we actually write the fileserver contents.
+                * If we do not restrict the fetch length based on
+                * avc->f.truncPos, a different truncate operation extending
+                * the file length could cause the old data after
+                * avc->f.truncPos to reappear, instead of extending the file
+                * with NUL bytes. */
+               size = AFS_CHUNKSIZE(abyte);
+               if (Position + size > avc->f.truncPos) {
+                   size = avc->f.truncPos - Position;
+               }
+               if (size < 0) {
+                   size = 0;
+               }
            }
        }
        if (afs_mariner && !tdc->f.chunk)
@@ -2517,7 +2561,7 @@ afs_WriteThroughDSlots(void)
            if (wrLock && (tdc->dflags & DFEntryMod)) {
                tdc->dflags &= ~DFEntryMod;
                ObtainWriteLock(&afs_xdcache, 620);
-               afs_WriteDCache(tdc, 1);
+               osi_Assert(afs_WriteDCache(tdc, 1) == 0);
                ReleaseWriteLock(&afs_xdcache);
                touchedit = 1;
            }
@@ -2555,14 +2599,13 @@ afs_WriteThroughDSlots(void)
  *
  * Parameters:
  *     aslot : Dcache slot to look at.
- *     tmpdc : Ptr to dcache entry.
  *
  * Environment:
  *     Must be called with afs_xdcache write-locked.
  */
 
 struct dcache *
-afs_MemGetDSlot(afs_int32 aslot, struct dcache *tmpdc)
+afs_MemGetDSlot(afs_int32 aslot, int needvalid)
 {
     struct dcache *tdc;
     int existing = 0;
@@ -2582,31 +2625,29 @@ afs_MemGetDSlot(afs_int32 aslot, struct dcache *tmpdc)
        ConvertWToRLock(&tdc->tlock);
        return tdc;
     }
-    if (tmpdc == NULL) {
-       if (!afs_freeDSList)
-           afs_GetDownDSlot(4);
-       if (!afs_freeDSList) {
-           /* none free, making one is better than a panic */
-           afs_stats_cmperf.dcacheXAllocs++;   /* count in case we have a leak */
-           tdc = afs_osi_Alloc(sizeof(struct dcache));
-           osi_Assert(tdc != NULL);
+
+    osi_Assert(needvalid);
+
+    if (!afs_freeDSList)
+       afs_GetDownDSlot(4);
+    if (!afs_freeDSList) {
+       /* none free, making one is better than a panic */
+       afs_stats_cmperf.dcacheXAllocs++;       /* count in case we have a leak */
+       tdc = afs_osi_Alloc(sizeof(struct dcache));
+       osi_Assert(tdc != NULL);
 #ifdef KERNEL_HAVE_PIN
-           pin((char *)tdc, sizeof(struct dcache));    /* XXX */
+       pin((char *)tdc, sizeof(struct dcache));        /* XXX */
 #endif
-       } else {
-           tdc = afs_freeDSList;
-           afs_freeDSList = (struct dcache *)tdc->lruq.next;
-           existing = 1;
-       }
-       tdc->dflags = 0;        /* up-to-date, not in free q */
-       tdc->mflags = 0;
-       QAdd(&afs_DLRU, &tdc->lruq);
-       if (tdc->lruq.prev == &tdc->lruq)
-           osi_Panic("lruq 3");
     } else {
-       tdc = tmpdc;
-       tdc->f.states = 0;
+       tdc = afs_freeDSList;
+       afs_freeDSList = (struct dcache *)tdc->lruq.next;
+       existing = 1;
     }
+    tdc->dflags = 0;   /* up-to-date, not in free q */
+    tdc->mflags = 0;
+    QAdd(&afs_DLRU, &tdc->lruq);
+    if (tdc->lruq.prev == &tdc->lruq)
+       osi_Panic("lruq 3");
 
     /* initialize entry */
     tdc->f.fid.Cell = 0;
@@ -2630,8 +2671,7 @@ afs_MemGetDSlot(afs_int32 aslot, struct dcache *tmpdc)
     AFS_RWLOCK_INIT(&tdc->mflock, "dcache flock");
     ObtainReadLock(&tdc->tlock);
 
-    if (tmpdc == NULL)
-       afs_indexTable[aslot] = tdc;
+    afs_indexTable[aslot] = tdc;
     return tdc;
 
 }                              /*afs_MemGetDSlot */
@@ -2647,18 +2687,18 @@ unsigned int last_error = 0, lasterrtime = 0;
  *
  * Parameters:
  *     aslot : Dcache slot to look at.
- *     tmpdc : Ptr to dcache entry.
  *
  * Environment:
  *     afs_xdcache lock write-locked.
  */
 struct dcache *
-afs_UFSGetDSlot(afs_int32 aslot, struct dcache *tmpdc)
+afs_UFSGetDSlot(afs_int32 aslot, int needvalid)
 {
     afs_int32 code;
     struct dcache *tdc;
     int existing = 0;
     int entryok;
+    int off;
 
     AFS_STATCNT(afs_UFSGetDSlot);
     if (CheckLock(&afs_xdcache) != -1)
@@ -2675,50 +2715,74 @@ afs_UFSGetDSlot(afs_int32 aslot, struct dcache *tmpdc)
        ConvertWToRLock(&tdc->tlock);
        return tdc;
     }
+
     /* otherwise we should read it in from the cache file */
-    /*
-     * If we weren't passed an in-memory region to place the file info,
-     * we have to allocate one.
-     */
-    if (tmpdc == NULL) {
-       if (!afs_freeDSList)
-           afs_GetDownDSlot(4);
-       if (!afs_freeDSList) {
-           /* none free, making one is better than a panic */
-           afs_stats_cmperf.dcacheXAllocs++;   /* count in case we have a leak */
-           tdc = afs_osi_Alloc(sizeof(struct dcache));
-           osi_Assert(tdc != NULL);
+    if (!afs_freeDSList)
+       afs_GetDownDSlot(4);
+    if (!afs_freeDSList) {
+       /* none free, making one is better than a panic */
+       afs_stats_cmperf.dcacheXAllocs++;       /* count in case we have a leak */
+       tdc = afs_osi_Alloc(sizeof(struct dcache));
+       osi_Assert(tdc != NULL);
 #ifdef KERNEL_HAVE_PIN
-           pin((char *)tdc, sizeof(struct dcache));    /* XXX */
+       pin((char *)tdc, sizeof(struct dcache));        /* XXX */
 #endif
-       } else {
-           tdc = afs_freeDSList;
-           afs_freeDSList = (struct dcache *)tdc->lruq.next;
-           existing = 1;
-       }
-       tdc->dflags = 0;        /* up-to-date, not in free q */
-       tdc->mflags = 0;
-       QAdd(&afs_DLRU, &tdc->lruq);
-       if (tdc->lruq.prev == &tdc->lruq)
-           osi_Panic("lruq 3");
     } else {
-       tdc = tmpdc;
-       tdc->f.states = 0;
+       tdc = afs_freeDSList;
+       afs_freeDSList = (struct dcache *)tdc->lruq.next;
+       existing = 1;
     }
+    tdc->dflags = 0;   /* up-to-date, not in free q */
+    tdc->mflags = 0;
+    QAdd(&afs_DLRU, &tdc->lruq);
+    if (tdc->lruq.prev == &tdc->lruq)
+       osi_Panic("lruq 3");
 
     /*
      * Seek to the aslot'th entry and read it in.
      */
+    off = sizeof(struct fcache)*aslot + sizeof(struct afs_fheader);
     code =
        afs_osi_Read(afs_cacheInodep,
-                    sizeof(struct fcache) * aslot +
-                    sizeof(struct afs_fheader), (char *)(&tdc->f),
+                    off, (char *)(&tdc->f),
                     sizeof(struct fcache));
     entryok = 1;
-    if (code != sizeof(struct fcache))
+    if (code != sizeof(struct fcache)) {
        entryok = 0;
-    if (!afs_CellNumValid(tdc->f.fid.Cell))
+#if defined(KERNEL_HAVE_UERROR)
+       last_error = getuerror();
+#endif
+       lasterrtime = osi_Time();
+       if (needvalid) {
+           struct osi_stat tstat;
+           if (afs_osi_Stat(afs_cacheInodep, &tstat)) {
+               tstat.size = -1;
+           }
+           afs_warn("afs: disk cache read error in CacheItems off %d/%d "
+                    "code %d/%d\n",
+                    off, (int)tstat.size,
+                    (int)code, (int)sizeof(struct fcache));
+           /* put tdc back on the free dslot list */
+           QRemove(&tdc->lruq);
+           tdc->index = NULLIDX;
+           tdc->lruq.next = (struct afs_q *)afs_freeDSList;
+           afs_freeDSList = tdc;
+           return NULL;
+       }
+    }
+    if (!afs_CellNumValid(tdc->f.fid.Cell)) {
        entryok = 0;
+       if (needvalid) {
+           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) {
+       osi_Panic("afs: invalid zero-volume dcache entry at slot %d off %d",
+                 (int)aslot, off);
+    }
 
     if (!entryok) {
        tdc->f.fid.Cell = 0;
@@ -2726,10 +2790,6 @@ afs_UFSGetDSlot(afs_int32 aslot, struct dcache *tmpdc)
        tdc->f.chunk = -1;
        hones(tdc->f.versionNo);
        tdc->dflags |= DFEntryMod;
-#if defined(KERNEL_HAVE_UERROR)
-       last_error = getuerror();
-#endif
-       lasterrtime = osi_Time();
        afs_indexUnique[aslot] = tdc->f.fid.Fid.Unique;
        tdc->f.states &= ~(DRO|DBackup|DRW);
        afs_DCMoveBucket(tdc, 0, 0);
@@ -2766,8 +2826,7 @@ afs_UFSGetDSlot(afs_int32 aslot, struct dcache *tmpdc)
      * If we didn't read into a temporary dcache region, update the
      * slot pointer table.
      */
-    if (tmpdc == NULL)
-       afs_indexTable[aslot] = tdc;
+    afs_indexTable[aslot] = tdc;
     return tdc;
 
 }                              /*afs_UFSGetDSlot */
@@ -2798,6 +2857,19 @@ afs_WriteDCache(struct dcache *adc, int atime)
     osi_Assert(WriteLocked(&afs_xdcache));
     if (atime)
        adc->f.modTime = osi_Time();
+
+    if ((afs_indexFlags[adc->index] & (IFFree | IFDiscarded)) == 0 &&
+        adc->f.fid.Fid.Volume == 0) {
+       /* If a dcache slot is not on the free or discard list, it must be
+        * in the hash table. Thus, the volume must be non-zero, since that
+        * is how we determine whether or not to unhash the entry when kicking
+        * it out of the cache. Do this check now, since otherwise this can
+        * cause hash table corruption and a panic later on after we read the
+        * entry back in. */
+       osi_Panic("afs_WriteDCache zero volume index %d flags 0x%x\n",
+                 adc->index, (unsigned)afs_indexFlags[adc->index]);
+    }
+
     /*
      * Seek to the right dcache slot and write the in-memory image out to disk.
      */
@@ -2807,8 +2879,12 @@ afs_WriteDCache(struct dcache *adc, int atime)
                      sizeof(struct fcache) * adc->index +
                      sizeof(struct afs_fheader), (char *)(&adc->f),
                      sizeof(struct fcache));
-    if (code != sizeof(struct fcache))
+    if (code != sizeof(struct fcache)) {
+       afs_warn("afs: failed to write to CacheItems off %ld code %d/%d\n",
+                (long)(sizeof(struct fcache) * adc->index + sizeof(struct afs_fheader)),
+                (int)code, (int)sizeof(struct fcache));
        return EIO;
+    }
     return 0;
 }
 
@@ -2885,7 +2961,7 @@ afs_InitCacheFile(char *afile, ino_t ainode)
        return EINVAL;
 
     ObtainWriteLock(&afs_xdcache, 282);
-    tdc = afs_GetDSlot(index, NULL);
+    tdc = afs_GetNewDSlot(index);
     ReleaseReadLock(&tdc->tlock);
     ReleaseWriteLock(&afs_xdcache);
 
@@ -2981,7 +3057,7 @@ afs_InitCacheFile(char *afile, ino_t ainode)
     tdc->f.states &= ~DWriting;
     tdc->dflags &= ~DFEntryMod;
     /* don't set f.modTime; we're just cleaning up */
-    afs_WriteDCache(tdc, 0);
+    osi_Assert(afs_WriteDCache(tdc, 0) == 0);
     ReleaseWriteLock(&afs_xdcache);
     ReleaseWriteLock(&tdc->lock);
     afs_PutDCache(tdc);