afs: Remove osi_GetuTime
[openafs.git] / src / afs / afs_dcache.c
index f27e08a..8d97143 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright 2000, International Business Machines Corporation and others.
- *$All Rights Reserved.
- * 
+ * All Rights Reserved.
+ *
  * This software has been released under the terms of the IBM Public
  * License.  For details, see the LICENSE file in the top-level source
  * directory or online at http://www.openafs.org/dl/license10.html
 #include "afs/afs_cbqueue.h"
 #include "afs/afs_osidnlc.h"
 
+#include <opr/ffs.h>
+
 /* 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 */
@@ -71,7 +73,7 @@ afs_int32 afs_cacheBlocks;    /*!< 1K blocks in cache */
 afs_int32 afs_cacheStats;      /*!< Stat entries in cache */
 afs_int32 afs_blocksUsed;      /*!< Number of blocks in use */
 afs_int32 afs_blocksDiscarded; /*!<Blocks freed but not truncated */
-afs_int32 afs_fsfragsize = AFS_MIN_FRAGSIZE;   /*!< Underlying Filesystem minimum unit 
+afs_int32 afs_fsfragsize = AFS_MIN_FRAGSIZE;   /*!< Underlying Filesystem minimum unit
                                         *of disk allocation usually 1K
                                         *this value is (truefrag -1 ) to
                                         *save a bunch of subtracts... */
@@ -100,14 +102,14 @@ afs_int32 afs_dcentries;  /*!< In-memory dcache entries */
 int dcacheDisabled = 0;
 
 struct afs_cacheOps afs_UfsCacheOps = {
-#if defined(AFS_SGI_ENV) && !defined(__c99)
+#ifndef HAVE_STRUCT_LABEL_SUPPORT
     osi_UFSOpen,
     osi_UFSTruncate,
     afs_osi_Read,
     afs_osi_Write,
     osi_UFSClose,
-    afs_UFSRead,
-    afs_UFSWrite,
+    afs_UFSReadUIO,
+    afs_UFSWriteUIO,
     afs_UFSGetDSlot,
     afs_UFSGetVolSlot,
     afs_UFSHandleLink,
@@ -117,8 +119,8 @@ struct afs_cacheOps afs_UfsCacheOps = {
     .fread     = afs_osi_Read,
     .fwrite    = afs_osi_Write,
     .close     = osi_UFSClose,
-    .vread     = afs_UFSRead,
-    .vwrite    = afs_UFSWrite,
+    .vreadUIO  = afs_UFSReadUIO,
+    .vwriteUIO = afs_UFSWriteUIO,
     .GetDSlot  = afs_UFSGetDSlot,
     .GetVolSlot = afs_UFSGetVolSlot,
     .HandleLink        = afs_UFSHandleLink,
@@ -126,14 +128,14 @@ struct afs_cacheOps afs_UfsCacheOps = {
 };
 
 struct afs_cacheOps afs_MemCacheOps = {
-#if (defined(AFS_SGI_ENV) && !defined(__c99))
+#ifndef HAVE_STRUCT_LABEL_SUPPORT
     afs_MemCacheOpen,
     afs_MemCacheTruncate,
     afs_MemReadBlk,
     afs_MemWriteBlk,
     afs_MemCacheClose,
-    afs_MemRead,
-    afs_MemWrite,
+    afs_MemReadUIO,
+    afs_MemWriteUIO,
     afs_MemGetDSlot,
     afs_MemGetVolSlot,
     afs_MemHandleLink,
@@ -143,8 +145,8 @@ struct afs_cacheOps afs_MemCacheOps = {
     .fread     = afs_MemReadBlk,
     .fwrite    = afs_MemWriteBlk,
     .close     = afs_MemCacheClose,
-    .vread     = afs_MemRead,
-    .vwrite    = afs_MemWrite,
+    .vreadUIO  = afs_MemReadUIO,
+    .vwriteUIO = afs_MemWriteUIO,
     .GetDSlot  = afs_MemGetDSlot,
     .GetVolSlot        = afs_MemGetVolSlot,
     .HandleLink        = afs_MemHandleLink,
@@ -154,6 +156,29 @@ struct afs_cacheOps afs_MemCacheOps = {
 int cacheDiskType;             /*Type of backing disk for cache */
 struct afs_cacheOps *afs_cacheType;
 
+
+/*
+ * The PFlush algorithm makes use of the fact that Fid.Unique is not used in
+ * below hash algorithms.  Change it if need be so that flushing algorithm
+ * doesn't move things from one hash chain to another.
+ */
+/*Vnode, Chunk -> Hash table index */
+int DCHash(struct VenusFid *fid, afs_int32 chunk)
+{
+    afs_uint32 buf[3];
+
+    buf[0] = fid->Fid.Volume;
+    buf[1] = fid->Fid.Vnode;
+    buf[2] = chunk;
+    return opr_jhash(buf, 3, 0) & (afs_dhashsize - 1);
+}
+/*Vnode -> Other hash table index */
+int DVHash(struct VenusFid *fid)
+{
+    return opr_jhash_int2(fid->Fid.Volume, fid->Fid.Vnode, 0) &
+       (afs_dhashsize - 1);
+}
+
 /*!
  * Where is this vcache's entry associated dcache located/
  * \param avc The vcache entry.
@@ -162,11 +187,11 @@ struct afs_cacheOps *afs_cacheType;
  *     2 : RO
  */
 static afs_int32
-afs_DCGetBucket(struct vcache *avc) 
+afs_DCGetBucket(struct vcache *avc)
 {
-    if (!splitdcache) 
+    if (!splitdcache)
        return 1;
-    
+
     /* This should be replaced with some sort of user configurable function */
     if (avc->f.states & CRO) {
        return 2;
@@ -187,15 +212,15 @@ afs_DCGetBucket(struct vcache *avc)
  * \param newSize The new size to be adjusted to.
  *
  */
-static void 
+static void
 afs_DCAdjustSize(struct dcache *adc, afs_int32 oldSize, afs_int32 newSize)
 {
     afs_int32 adjustSize = newSize - oldSize;
 
-    if (!splitdcache) 
+    if (!splitdcache)
        return;
 
-    switch (adc->bucket) 
+    switch (adc->bucket)
     {
     case 0:
        afs_blocksUsed_0 += adjustSize;
@@ -216,20 +241,20 @@ afs_DCAdjustSize(struct dcache *adc, afs_int32 oldSize, afs_int32 newSize)
 
 /*!
  * Move a dcache from one bucket to another.
- * 
+ *
  * \param adc Operate on this dcache.
  * \param size Size in bucket (?).
  * \param newBucket Destination bucket.
  *
  */
-static void 
+static void
 afs_DCMoveBucket(struct dcache *adc, afs_int32 size, afs_int32 newBucket)
 {
-    if (!splitdcache) 
+    if (!splitdcache)
        return;
 
-    /* Substract size from old bucket. */      
-    switch (adc->bucket) 
+    /* Substract size from old bucket. */
+    switch (adc->bucket)
     {
     case 0:
        afs_blocksUsed_0 -= size;
@@ -245,7 +270,7 @@ afs_DCMoveBucket(struct dcache *adc, afs_int32 size, afs_int32 newBucket)
     /* Set new bucket and increase destination bucket size. */
     adc->bucket = newBucket;
 
-    switch (adc->bucket) 
+    switch (adc->bucket)
     {
     case 0:
        afs_blocksUsed_0 += size;
@@ -257,15 +282,15 @@ afs_DCMoveBucket(struct dcache *adc, afs_int32 size, afs_int32 newBucket)
        afs_blocksUsed_2 += size;
        break;
     }
-    
+
     return;
 }
 
 /*!
  * Init split caches size.
  */
-static void 
-afs_DCSizeInit(void) 
+static void
+afs_DCSizeInit(void)
 {
     afs_blocksUsed_0 = afs_blocksUsed_1 = afs_blocksUsed_2 = 0;
 }
@@ -276,19 +301,19 @@ afs_DCSizeInit(void)
  * \param bucket
  */
 static afs_int32
-afs_DCWhichBucket(afs_int32 phase, afs_int32 bucket) 
+afs_DCWhichBucket(afs_int32 phase, afs_int32 bucket)
 {
-    if (!splitdcache) 
+    if (!splitdcache)
        return 0;
 
     afs_pct1 = afs_blocksUsed_1 / (afs_cacheBlocks / 100);
     afs_pct2 = afs_blocksUsed_2 / (afs_cacheBlocks / 100);
 
     /* Short cut: if we don't know about it, try to kill it */
-    if (phase < 2 && afs_blocksUsed_0) 
+    if (phase < 2 && afs_blocksUsed_0)
        return 0;
-    
-    if (afs_pct1 > afs_tpct1) 
+
+    if (afs_pct1 > afs_tpct1)
        return 1;
     if (afs_pct2 > afs_tpct2)
        return 2;
@@ -309,8 +334,8 @@ afs_DCWhichBucket(afs_int32 phase, afs_int32 bucket)
  */
 
 void
-afs_StoreWarn(register afs_int32 acode, afs_int32 avolume,
-             register afs_int32 aflags)
+afs_StoreWarn(afs_int32 acode, afs_int32 avolume,
+             afs_int32 aflags)
 {
     static char problem_fmt[] =
        "afs: failed to store file in volume %d (%s)\n";
@@ -386,24 +411,45 @@ afs_MaybeWakeupTruncateDaemon(void)
  * struct so we need only export one symbol for AIX.
  */
 static struct CTD_stats {
-    osi_timeval_t CTD_beforeSleep;
-    osi_timeval_t CTD_afterSleep;
-    osi_timeval_t CTD_sleepTime;
-    osi_timeval_t CTD_runTime;
+    osi_timeval32_t CTD_beforeSleep;
+    osi_timeval32_t CTD_afterSleep;
+    osi_timeval32_t CTD_sleepTime;
+    osi_timeval32_t CTD_runTime;
     int CTD_nSleeps;
 } CTD_stats;
 
 u_int afs_min_cache = 0;
 
 /*!
+ * If there are waiters for the cache to drain, wake them if
+ * the number of free or discarded cache blocks reaches the
+ * CM_CACHESIZEDDRAINEDPCT limit.
+ *
+ * \note Environment:
+ *     This routine must be called with the afs_xdcache lock held
+ *     (in write mode).
+ */
+static void
+afs_WakeCacheWaitersIfDrained(void)
+{
+    if (afs_WaitForCacheDrain) {
+       if ((afs_blocksUsed - afs_blocksDiscarded) <=
+           PERCENT(CM_CACHESIZEDRAINEDPCT, afs_cacheBlocks)) {
+           afs_WaitForCacheDrain = 0;
+           afs_osi_Wakeup(&afs_WaitForCacheDrain);
+       }
+    }
+}
+
+/*!
  * Keeps the cache clean and free by truncating uneeded files, when used.
- * \param  
- * \return 
+ * \param
+ * \return
  */
 void
 afs_CacheTruncateDaemon(void)
 {
-    osi_timeval_t CTD_tmpTime;
+    osi_timeval32_t CTD_tmpTime;
     u_int counter;
     u_int cb_lowat;
     u_int dc_hiwat =
@@ -411,28 +457,35 @@ afs_CacheTruncateDaemon(void)
     afs_min_cache =
        (((10 * AFS_CHUNKSIZE(0)) + afs_fsfragsize) & ~afs_fsfragsize) >> 10;
 
-    osi_GetuTime(&CTD_stats.CTD_afterSleep);
+    osi_GetTime(&CTD_stats.CTD_afterSleep);
     afs_TruncateDaemonRunning = 1;
     while (1) {
        cb_lowat = PERCENT((CM_DCACHESPACEFREEPCT - CM_DCACHEEXTRAPCT), afs_cacheBlocks);
        ObtainWriteLock(&afs_xdcache, 266);
-       if (afs_CacheTooFull) {
+       if (afs_CacheTooFull || afs_WaitForCacheDrain) {
            int space_needed, slots_needed;
            /* if we get woken up, we should try to clean something out */
            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)) {
                    break;
                }
                if (afs_termState == AFSOP_STOP_TRUNCDAEMON)
                    break;
            }
-           if (!afs_CacheIsTooFull())
+           if (!afs_CacheIsTooFull()) {
                afs_CacheTooFull = 0;
+               afs_WakeCacheWaitersIfDrained();
+           }
        }       /* end of cache cleanup */
        ReleaseWriteLock(&afs_xdcache);
 
@@ -453,7 +506,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
@@ -463,7 +525,7 @@ afs_CacheTruncateDaemon(void)
            && (afs_termState != AFSOP_STOP_TRUNCDAEMON)) {
            /* Collect statistics on truncate daemon. */
            CTD_stats.CTD_nSleeps++;
-           osi_GetuTime(&CTD_stats.CTD_beforeSleep);
+           osi_GetTime(&CTD_stats.CTD_beforeSleep);
            afs_stats_GetDiff(CTD_tmpTime, CTD_stats.CTD_afterSleep,
                              CTD_stats.CTD_beforeSleep);
            afs_stats_AddTo(CTD_stats.CTD_runTime, CTD_tmpTime);
@@ -472,7 +534,7 @@ afs_CacheTruncateDaemon(void)
            afs_osi_Sleep((int *)afs_CacheTruncateDaemon);
            afs_TruncateDaemonRunning = 1;
 
-           osi_GetuTime(&CTD_stats.CTD_afterSleep);
+           osi_GetTime(&CTD_stats.CTD_afterSleep);
            afs_stats_GetDiff(CTD_tmpTime, CTD_stats.CTD_beforeSleep,
                              CTD_stats.CTD_afterSleep);
            afs_stats_AddTo(CTD_stats.CTD_sleepTime, CTD_tmpTime);
@@ -500,12 +562,29 @@ afs_CacheTruncateDaemon(void)
  */
 
 void
-afs_AdjustSize(register struct dcache *adc, register afs_int32 newSize)
+afs_AdjustSize(struct dcache *adc, afs_int32 newSize)
 {
-    register afs_int32 oldSize;
+    afs_int32 oldSize;
 
     AFS_STATCNT(afs_AdjustSize);
 
+    if (newSize > afs_OtherCSize && !(adc->f.fid.Fid.Vnode & 1)) {
+        /* No non-dir cache files should be larger than the chunk size.
+         * (Directory blobs are fetched in a single chunk file, so directories
+         * can be larger.) If someone is requesting that a chunk is larger than
+         * the chunk size, something strange is happening. Log a message about
+         * it, to give a hint to subsequent strange behavior, if any occurs. */
+        static int warned;
+        if (!warned) {
+            warned = 1;
+            afs_warn("afs: Warning: dcache %d is very large (%d > %d). This "
+                     "should not happen, but trying to continue regardless. If "
+                     "AFS starts hanging or behaving strangely, this might be "
+                     "why.\n",
+                     adc->index, newSize, afs_OtherCSize);
+        }
+    }
+
     adc->dflags |= DFEntryMod;
     oldSize = ((adc->f.chunkBytes + afs_fsfragsize) ^ afs_fsfragsize) >> 10;   /* round up */
     adc->f.chunkBytes = newSize;
@@ -538,7 +617,7 @@ afs_AdjustSize(register struct dcache *adc, register afs_int32 newSize)
  *      1.  only grab up to anumber victims if aneedSpace <= 0, not
  *          the whole set of MAXATONCE.
  *      2.  dynamically choose MAXATONCE to reflect severity of
- *          demand: something like (*aneedSpace >> (logChunk - 9)) 
+ *          demand: something like (*aneedSpace >> (logChunk - 9))
  *
  *  \note N.B. if we're called with aneedSpace <= 0 and anumber > 0, that
  *  indicates that the cache is not properly configured/tuned or
@@ -555,7 +634,7 @@ afs_GetDownD(int anumber, int *aneedSpace, afs_int32 buckethint)
     afs_int32 i, j;
     afs_hyper_t vtime;
     int skip, phase;
-    register struct vcache *tvc;
+    struct vcache *tvc;
     afs_uint32 victims[MAXATONCE];
     struct dcache *victimDCs[MAXATONCE];
     afs_hyper_t victimTimes[MAXATONCE];        /* youngest (largest LRU time) first */
@@ -564,27 +643,17 @@ afs_GetDownD(int anumber, int *aneedSpace, afs_int32 buckethint)
     afs_uint32 maxVictimPtr;   /* where it is */
     int discard;
     int curbucket;
-#if defined(AFS_FBSD80_ENV) && !defined(UKERNEL)
-    int vfslocked;
-#endif
-
-#if defined(AFS_FBSD80_ENV) && !defined(UKERNEL)
-    vfslocked = VFS_LOCK_GIANT(afs_globalVFS);
-#endif
 
     AFS_STATCNT(afs_GetDownD);
 
     if (CheckLock(&afs_xdcache) != -1)
        osi_Panic("getdownd nolock");
     /* decrement anumber first for all dudes in free list */
-    /* SHOULD always decrement anumber first, even if aneedSpace >0, 
+    /* SHOULD always decrement anumber first, even if aneedSpace >0,
      * because we should try to free space even if anumber <=0 */
     if (!aneedSpace || *aneedSpace <= 0) {
        anumber -= afs_freeDCCount;
        if (anumber <= 0) {
-#if defined(AFS_FBSD80_ENV) && !defined(UKERNEL)
-         VFS_UNLOCK_GIANT(vfslocked);
-#endif
            return;             /* enough already free */
        }
     }
@@ -596,10 +665,12 @@ afs_GetDownD(int anumber, int *aneedSpace, afs_int32 buckethint)
     /* rewrite so phases include a better eligiblity for gc test*/
     /*
      * The phase variable manages reclaims.  Set to 0, the first pass,
-     * we don't reclaim active entries, or other than target bucket.  
+     * we don't reclaim active entries, or other than target bucket.
      * Set to 1, we reclaim even active ones in target bucket.
      * Set to 2, we reclaim any inactive one.
-     * Set to 3, we reclaim even active ones.
+     * Set to 3, we reclaim even active ones. On Solaris, we also reclaim
+     * entries whose corresponding vcache has a nonempty multiPage list, when
+     * possible.
      */
     if (splitdcache) {
        phase = 0;
@@ -674,15 +745,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 */
@@ -691,7 +764,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;
@@ -734,9 +807,11 @@ afs_GetDownD(int anumber, int *aneedSpace, afs_int32 buckethint)
 
                        ReleaseWriteLock(&afs_xdcache);
                        ObtainWriteLock(&tvc->vlock, 543);
-                       if (tvc->multiPage) {
-                           skip = 1;
-                           goto endmultipage;
+                       if (!QEmpty(&tvc->multiPage)) {
+                           if (phase < 3 || osi_VM_MultiPageConflict(tvc, tdc)) {
+                               skip = 1;
+                               goto endmultipage;
+                           }
                        }
                        /* block locking pages */
                        tvc->vstates |= VPageCleaning;
@@ -843,7 +918,8 @@ afs_GetDownD(int anumber, int *aneedSpace, afs_int32 buckethint)
                    j = 1;      /* we reclaimed at least one victim */
                }
            }
-           afs_PutDCache(tdc);
+           if (tdc)
+               afs_PutDCache(tdc);
        }                       /* end of for victims loop */
 
        if (phase < 5) {
@@ -862,10 +938,6 @@ afs_GetDownD(int anumber, int *aneedSpace, afs_int32 buckethint)
        }
     }                          /* big while loop */
 
-#if defined(AFS_FBSD80_ENV) && !defined(UKERNEL)
-    VFS_UNLOCK_GIANT(vfslocked);
-#endif
-
     return;
 
 }                              /*afs_GetDownD */
@@ -957,7 +1029,7 @@ afs_HashOutDCache(struct dcache *adc, int zap)
  *     (in write mode).
  */
 void
-afs_FlushDCache(register struct dcache *adc)
+afs_FlushDCache(struct dcache *adc)
 {
     AFS_STATCNT(afs_FlushDCache);
     /*
@@ -979,14 +1051,6 @@ afs_FlushDCache(register struct dcache *adc)
     } else {
        afs_FreeDCache(adc);
     }
-
-    if (afs_WaitForCacheDrain) {
-       if (afs_blocksUsed <=
-           PERCENT(CM_CACHESIZEDRAINEDPCT, afs_cacheBlocks)) {
-           afs_WaitForCacheDrain = 0;
-           afs_osi_Wakeup(&afs_WaitForCacheDrain);
-       }
-    }
 }                              /*afs_FlushDCache */
 
 
@@ -998,7 +1062,7 @@ afs_FlushDCache(register struct dcache *adc)
  * \note Environment: called with afs_xdcache lock write-locked.
  */
 static void
-afs_FreeDCache(register struct dcache *adc)
+afs_FreeDCache(struct dcache *adc)
 {
     /* Thread on free list, update free list count and mark entry as
      * freed in its indexFlags element.  Also, ensure DCache entry gets
@@ -1011,13 +1075,7 @@ afs_FreeDCache(register struct dcache *adc)
     afs_indexFlags[adc->index] |= IFFree;
     adc->dflags |= DFEntryMod;
 
-    if (afs_WaitForCacheDrain) {
-       if ((afs_blocksUsed - afs_blocksDiscarded) <=
-           PERCENT(CM_CACHESIZEDRAINEDPCT, afs_cacheBlocks)) {
-           afs_WaitForCacheDrain = 0;
-           afs_osi_Wakeup(&afs_WaitForCacheDrain);
-       }
-    }
+    afs_WakeCacheWaitersIfDrained();
 }                              /* afs_FreeDCache */
 
 /*!
@@ -1037,9 +1095,9 @@ afs_FreeDCache(register struct dcache *adc)
  */
 
 static void
-afs_DiscardDCache(register struct dcache *adc)
+afs_DiscardDCache(struct dcache *adc)
 {
-    register afs_int32 size;
+    afs_int32 size;
 
     AFS_STATCNT(afs_DiscardDCache);
 
@@ -1057,43 +1115,77 @@ afs_DiscardDCache(register struct dcache *adc)
     adc->dflags |= DFEntryMod;
     afs_indexFlags[adc->index] |= IFDiscarded;
 
-    if (afs_WaitForCacheDrain) {
-       if ((afs_blocksUsed - afs_blocksDiscarded) <=
-           PERCENT(CM_CACHESIZEDRAINEDPCT, afs_cacheBlocks)) {
-           afs_WaitForCacheDrain = 0;
-           afs_osi_Wakeup(&afs_WaitForCacheDrain);
-       }
+    afs_WakeCacheWaitersIfDrained();
+}                              /*afs_DiscardDCache */
+
+/**
+ * Get a dcache entry from the discard or free list
+ *
+ * @param[out] adc    On success, a dcache from the given list. Otherwise, NULL.
+ * @param[in] indexp  A pointer to the head of the dcache free list or discard
+ *                    list (afs_freeDCList, or afs_discardDCList)
+ *
+ * @return 0 on success. If there are no dcache slots available, return ENOSPC.
+ *         If we encountered an error in disk i/o while trying to find a
+ *         dcache, return EIO.
+ *
+ * @pre afs_xdcache is write-locked
+ */
+static int
+afs_GetDSlotFromList(struct dcache **adc, afs_int32 *indexp)
+{
+    struct dcache *tdc;
+
+    *adc = NULL;
+
+    if (*indexp == NULLIDX) {
+        return ENOSPC;
     }
 
-}                              /*afs_DiscardDCache */
+    tdc = afs_GetUnusedDSlot(*indexp);
+    if (tdc == NULL) {
+        return EIO;
+    }
+
+    osi_Assert(tdc->refCount == 1);
+    ReleaseReadLock(&tdc->tlock);
+    *indexp = afs_dvnextTbl[tdc->index];
+    afs_dvnextTbl[tdc->index] = NULLIDX;
+
+    *adc = tdc;
+    return 0;
+}
 
 /*!
  * 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)
 {
-    register struct dcache *tdc;
-    register struct osi_file *tfile;
-    register afs_int32 size;
+    struct dcache *tdc;
+    struct osi_file *tfile;
+    afs_int32 size;
 
     AFS_STATCNT(afs_FreeDiscardedDCache);
 
     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_GetDSlot(afs_discardDCList, 0);
-    osi_Assert(tdc->refCount == 1);
-    ReleaseReadLock(&tdc->tlock);
+    (void)afs_GetDSlotFromList(&tdc, &afs_discardDCList);
+    if (!tdc) {
+       ReleaseWriteLock(&afs_xdcache);
+       return -1;
+    }
 
-    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;
@@ -1106,6 +1198,7 @@ afs_FreeDiscardedDCache(void)
      * Truncate the element to reclaim its space
      */
     tfile = afs_CFileOpen(&tdc->f.inode);
+    osi_Assert(tfile);
     afs_CFileTruncate(tfile, 0);
     afs_CFileClose(tfile);
     afs_AdjustSize(tdc, 0);
@@ -1121,6 +1214,8 @@ afs_FreeDiscardedDCache(void)
     ReleaseWriteLock(&tdc->lock);
     afs_PutDCache(tdc);
     ReleaseWriteLock(&afs_xdcache);
+
+    return 0;
 }
 
 /*!
@@ -1138,7 +1233,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;
 }
@@ -1180,8 +1282,6 @@ afs_GetDownDSlot(int anumber)
        if (tdc->refCount == 0) {
            if ((ix = tdc->index) == NULLIDX)
                osi_Panic("getdowndslot");
-           /* pull the entry out of the lruq and put it on the free list */
-           QRemove(&tdc->lruq);
 
            /* write-through if modified */
            if (tdc->dflags & DFEntryMod) {
@@ -1203,12 +1303,23 @@ afs_GetDownDSlot(int anumber)
                    AFS_GLOCK();
                }
 #else
+               int code;
+
+               code = afs_WriteDCache(tdc, 1);
+               if (code) {
+                   /*
+                    * We couldn't flush it at this time; return early because
+                    * if afs_WriteDCache() failed once it is likely to
+                    * continue failing for subsequent dcaches.
+                    */
+                   return;
+               }
                tdc->dflags &= ~DFEntryMod;
-               afs_WriteDCache(tdc, 1);
 #endif
            }
 
-           /* finally put the entry in the free list */
+           /* pull the entry out of the lruq and put it on the free list */
+           QRemove(&tdc->lruq);
            afs_indexTable[ix] = NULL;
            afs_indexFlags[ix] &= ~IFEverUsed;
            tdc->index = NULLIDX;
@@ -1260,7 +1371,7 @@ afs_RefDCache(struct dcache *adc)
  *     Nothing interesting.
  */
 int
-afs_PutDCache(register struct dcache *adc)
+afs_PutDCache(struct dcache *adc)
 {
     AFS_STATCNT(afs_PutDCache);
     ObtainWriteLock(&adc->tlock, 276);
@@ -1286,11 +1397,11 @@ afs_PutDCache(register struct dcache *adc)
  *     Both pvnLock and lock are write held.
  */
 void
-afs_TryToSmush(register struct vcache *avc, afs_ucred_t *acred, int sync)
+afs_TryToSmush(struct vcache *avc, afs_ucred_t *acred, int sync)
 {
-    register struct dcache *tdc;
-    register int index;
-    register int i;
+    struct dcache *tdc;
+    int index;
+    int i;
     AFS_STATCNT(afs_TryToSmush);
     afs_Trace2(afs_iclSetp, CM_TRACE_TRYTOSMUSH, ICL_TYPE_POINTER, avc,
               ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->f.m.Length));
@@ -1314,7 +1425,13 @@ afs_TryToSmush(register 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) {
+               /* afs_TryToSmush is best-effort; we may not actually discard
+                * everything, so failure to discard dcaches due to an i/o
+                * error is okay. */
+               break;
+           }
            if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
                if (sync) {
                    if ((afs_indexFlags[index] & IFDataMod) == 0
@@ -1341,7 +1458,7 @@ afs_TryToSmush(register struct vcache *avc, afs_ucred_t *acred, int sync)
 #endif
     ReleaseWriteLock(&afs_xdcache);
     /*
-     * It's treated like a callback so that when we do lookups we'll 
+     * It's treated like a callback so that when we do lookups we'll
      * invalidate the unique bit if any
      * trytoSmush occured during the lookup call
      */
@@ -1354,13 +1471,13 @@ afs_TryToSmush(register struct vcache *avc, afs_ucred_t *acred, int sync)
  * Description
  *     Given the cached info for a file, return the number of chunks that
  *     are not available from the dcache.
- * 
+ *
  * Parameters:
  *     avc:    Pointer to the (held) vcache entry to look in.
- * 
+ *
  * Returns:
  *     The number of chunks which are not currently cached.
- * 
+ *
  * Environment:
  *     The vcache entry is held upon entry.
  */
@@ -1393,7 +1510,7 @@ afs_DCacheMissingChunks(struct vcache *avc)
      */
     if (avc->f.fid.Fid.Vnode & 1 || vType(avc) == VDIR)
        totalChunks = 1;
-    
+
     /*
      printf("Should have %d chunks for %u bytes\n",
                totalChunks, (totalLength + 1));
@@ -1403,9 +1520,12 @@ 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);
+            tdc = afs_GetValidDSlot(index);
+            if (!tdc) {
+                break;
+            }
             if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
-               totalChunks--;
+                totalChunks--;
             }
             ReleaseReadLock(&tdc->tlock);
             afs_PutDCache(tdc);
@@ -1439,11 +1559,11 @@ afs_DCacheMissingChunks(struct vcache *avc)
  */
 
 struct dcache *
-afs_FindDCache(register struct vcache *avc, afs_size_t abyte)
+afs_FindDCache(struct vcache *avc, afs_size_t abyte)
 {
     afs_int32 chunk;
-    register afs_int32 i, index;
-    register struct dcache *tdc = NULL;
+    afs_int32 i, index;
+    struct dcache *tdc = NULL;
 
     AFS_STATCNT(afs_FindDCache);
     chunk = AFS_CHUNK(abyte);
@@ -1454,31 +1574,90 @@ afs_FindDCache(register 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_GetDSlot(index, NULL);
+           tdc = afs_GetValidDSlot(index);
+           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. */
+                index = NULLIDX;
+               break;
+           }
            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);
        hadd32(afs_indexCounter, 1);
        ReleaseWriteLock(&afs_xdcache);
        return tdc;
-    } 
+    }
     ReleaseWriteLock(&afs_xdcache);
     return NULL;
 }                              /*afs_FindDCache */
 
+/* only call these from afs_AllocDCache() */
+static int
+afs_AllocFreeDSlot(struct dcache **adc)
+{
+    int code;
+    struct dcache *tdc;
+
+    code = afs_GetDSlotFromList(&tdc, &afs_freeDCList);
+    if (code) {
+       return code;
+    }
+    afs_indexFlags[tdc->index] &= ~IFFree;
+    ObtainWriteLock(&tdc->lock, 604);
+    afs_freeDCCount--;
+
+    *adc = tdc;
+    return 0;
+}
+static int
+afs_AllocDiscardDSlot(struct dcache **adc, afs_int32 lock)
+{
+    int code;
+    struct dcache *tdc;
+    afs_uint32 size = 0;
+    struct osi_file *file;
+
+    code = afs_GetDSlotFromList(&tdc, &afs_discardDCList);
+    if (code) {
+       return code;
+    }
+    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);
+        osi_Assert(file);
+       afs_CFileTruncate(file, 0);
+       afs_CFileClose(file);
+       afs_AdjustSize(tdc, 0);
+    }
+
+    *adc = tdc;
+    return 0;
+}
 
 /*!
  * Get a fresh dcache from the free or discarded list.
  *
+ * \param adc Set to the new dcache on success, and NULL on error.
  * \param avc Who's dcache is this going to be?
  * \param chunk The position where it will be placed in.
  * \param lock How are locks held.
@@ -1490,49 +1669,35 @@ afs_FindDCache(register struct vcache *avc, afs_size_t abyte)
  *     - avc (R if (lock & 1) set and W otherwise)
  * \note It write locks the new dcache. The caller must unlock it.
  *
- * \return The new dcache.
+ * \return If we're out of dslots, ENOSPC. If we encountered disk errors, EIO.
+ *         On success, return 0.
  */
-struct dcache *
-afs_AllocDCache(struct vcache *avc, afs_int32 chunk, afs_int32 lock,
-               struct VenusFid *ashFid)
+static int
+afs_AllocDCache(struct dcache **adc, struct vcache *avc, afs_int32 chunk,
+                afs_int32 lock, struct VenusFid *ashFid)
 {
+    int code;
     struct dcache *tdc = NULL;
-    afs_uint32 size = 0;
-    struct osi_file *file;
 
-    if (afs_discardDCList == NULLIDX
-       || ((lock & 2) && afs_freeDCList != NULLIDX)) {
+    *adc = NULL;
 
-       afs_indexFlags[afs_freeDCList] &= ~IFFree;
-       tdc = afs_GetDSlot(afs_freeDCList, 0);
-       osi_Assert(tdc->refCount == 1);
-       ReleaseReadLock(&tdc->tlock);
-       ObtainWriteLock(&tdc->lock, 604);
-       afs_freeDCList = afs_dvnextTbl[tdc->index];
-       afs_freeDCCount--;
+    /* if (lock & 2), prefer 'free' dcaches; otherwise, prefer 'discard'
+     * dcaches. In either case, try both if our first choice doesn't work due
+     * to ENOSPC. */
+    if ((lock & 2)) {
+       code = afs_AllocFreeDSlot(&tdc);
+       if (code == ENOSPC) {
+           code = afs_AllocDiscardDSlot(&tdc, lock);
+       }
     } else {
-       afs_indexFlags[afs_discardDCList] &= ~IFDiscarded;
-       tdc = afs_GetDSlot(afs_discardDCList, 0);
-       osi_Assert(tdc->refCount == 1);
-       ReleaseReadLock(&tdc->tlock);
-       ObtainWriteLock(&tdc->lock, 605);
-       afs_discardDCList = afs_dvnextTbl[tdc->index];
-       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);
+       code = afs_AllocDiscardDSlot(&tdc, lock);
+       if (code == ENOSPC) {
+           code = afs_AllocFreeDSlot(&tdc);
        }
     }
+    if (code) {
+       return code;
+    }
 
     /*
      * Locks held:
@@ -1568,7 +1733,167 @@ afs_AllocDCache(struct vcache *avc, afs_int32 chunk, afs_int32 lock,
     if (tdc->lruq.prev == &tdc->lruq)
        osi_Panic("lruq 1");
 
-    return tdc;
+    *adc = tdc;
+    return 0;
+}
+
+static int
+IsDCacheSizeOK(struct dcache *adc, struct vcache *avc, afs_int32 chunk_bytes,
+              afs_size_t file_length, afs_uint32 versionNo, int from_net)
+{
+    afs_size_t expected_bytes;
+    afs_size_t chunk_start = AFS_CHUNKTOBASE(adc->f.chunk);
+
+    if (vType(avc) == VDIR) {
+       /*
+        * Directory blobs may be constructed locally (see afs_LocalHero), and
+        * the size of the blob may differ slightly compared to what's on the
+        * fileserver. So, skip size checks for directories.
+        */
+       return 1;
+    }
+
+    if ((avc->f.states & CDirty)) {
+       /*
+        * Our vcache may have writes that are local to our cache, but not yet
+        * written to the fileserver. In such a situation, we may have dcaches
+        * for that file that are "short". For example:
+        *
+        * Say we have a file that is 0 bytes long. A process opens that file,
+        * and writes some data to offset 5M (keeping the file open). Another
+        * process comes along and reads data from offset 1M. We'll try to
+        * fetch data at offset 1M, and the fileserver will respond with 0
+        * bytes, since our locally-written data hasn't been written to the
+        * fileserver yet (on the fileserver, the file is still 0-bytes long).
+        * So our dcache at offset 1M will have 0 bytes.
+        *
+        * So if CDirty is set, don't do any size/length checks at all, since
+        * we have no idea if the avc length is valid.
+        */
+       return 1;
+    }
+
+    if (!from_net && (adc->f.states & DRW)) {
+       /*
+        * The dcache data we're looking at is from our local cache (not from a
+        * fileserver), and it's for data in an RW volume. For cached RW data,
+        * there are some edge cases that can cause the below length checks to
+        * trigger false positives.
+        *
+        * For example: if the local client writes 4 bytes to a new file at
+        * offset 0, and then 4 bytes at offset 0x400000, the file will be
+        * 0x400004 bytes long, but the first dcache chunk will only contain 4
+        * bytes. If such a file is fetched from a fileserver, the first chunk
+        * will have a full chunk of data (most of it zeroes), but on the
+        * client that did the write, the sparse data will not appear in the
+        * dcache.
+        *
+        * Such false positives should only be possible with RW data, since
+        * non-RW data is never generated locally. So to avoid the false
+        * positives, assume the dcache length is OK for RW data if the dcache
+        * came from our local cache (and not directly from a fileserver).
+        */
+       return 1;
+    }
+
+    if (file_length < chunk_start) {
+       expected_bytes = 0;
+
+    } else {
+       expected_bytes = file_length - chunk_start;
+
+       if (vType(avc) != VDIR && expected_bytes > AFS_CHUNKTOSIZE(adc->f.chunk)) {
+           /* A non-dir chunk cannot have more bytes than the chunksize. */
+           expected_bytes = AFS_CHUNKTOSIZE(adc->f.chunk);
+       }
+    }
+
+    if (chunk_bytes != expected_bytes) {
+       static const afs_uint32 one_hour = 60 * 60;
+       static afs_uint32 last_warn;
+       afs_uint32 now = osi_Time();
+
+       if (now < last_warn) {
+           /* clock went backwards */
+           last_warn = now;
+       }
+
+       if (now - last_warn > one_hour) {
+           unsigned int mtime = adc->f.modTime;
+
+           last_warn = now;
+
+           if (from_net) {
+               /*
+                * The dcache we're looking at didn't come from the cache, but is
+                * being populated from the net. Don't print out its mtime in that
+                * case; that would be misleading since that's the mtime from the
+                * last time this dcache slot was written to.
+                */
+               mtime = 0;
+           }
+
+           afs_warn("afs: Detected corrupt dcache for file %d.%u.%u.%u: chunk %d "
+                    "(offset %lu) has %d bytes, but it should have %lu bytes\n",
+                    adc->f.fid.Cell,
+                    adc->f.fid.Fid.Volume,
+                    adc->f.fid.Fid.Vnode,
+                    adc->f.fid.Fid.Unique,
+                    adc->f.chunk,
+                    (unsigned long)chunk_start,
+                    chunk_bytes,
+                    (unsigned long)expected_bytes);
+           afs_warn("afs: (dcache %p, file length %lu, DV %u, dcache mtime %u, "
+                    "index %d, dflags 0x%x, mflags 0x%x, states 0x%x, vcache "
+                    "states 0x%x)\n",
+                    adc,
+                    (unsigned long)file_length,
+                    versionNo,
+                    mtime,
+                    adc->index,
+                    (unsigned)adc->dflags,
+                    (unsigned)adc->mflags,
+                    (unsigned)adc->f.states,
+                    avc->f.states);
+           afs_warn("afs: Ignoring the dcache for now, but this may indicate "
+                    "corruption in the AFS cache, or a bug.\n");
+       }
+       return 0;
+    }
+    return 1;
+}
+
+/*!
+ * Check if a dcache is "fresh". That is, if the dcache's DV matches the DV of
+ * the vcache for that file, and the dcache looks "sane" (its length makes
+ * sense, when considering the length of the given avc).
+ *
+ * \param adc The dcache to check
+ * \param avc The vcache for adc
+ *
+ * \return 1 if the dcache is "fresh". 0 otherwise.
+ */
+int
+afs_IsDCacheFresh(struct dcache *adc, struct vcache *avc)
+{
+    if (!hsame(adc->f.versionNo, avc->f.m.DataVersion)) {
+       return 0;
+    }
+
+    /*
+     * If we've reached here, the DV in adc matches the DV of our avc. Check if
+     * the number of bytes in adc agrees with the avc file length, as a sanity
+     * check. If they don't match, we'll pretend the DVs don't match, so the
+     * bad dcache data will not be used, and we'll probably re-fetch the chunk
+     * data, replacing the bad chunk.
+     */
+
+    if (!IsDCacheSizeOK(adc, avc, adc->f.chunkBytes, avc->f.m.Length,
+                       hgetlo(adc->f.versionNo), 0)) {
+       return 0;
+    }
+
+    return 1;
 }
 
 /*
@@ -1609,7 +1934,7 @@ void
 updateV2DC(int lockVc, struct vcache *v, struct dcache *d, int src)
 {
     if (!lockVc || 0 == NBObtainWriteLock(&v->lock, src)) {
-       if (hsame(v->f.m.DataVersion, d->f.versionNo) && v->callback)
+       if (afs_IsDCacheFresh(d, v) && v->callback)
            v->dchint = d;
        if (lockVc)
            ReleaseWriteLock(&v->lock);
@@ -1618,25 +1943,24 @@ updateV2DC(int lockVc, struct vcache *v, struct dcache *d, int src)
 
 /* avc - Write-locked unless aflags & 1 */
 struct dcache *
-afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
-             register struct vrequest *areq, afs_size_t * aoffset,
+afs_GetDCache(struct vcache *avc, afs_size_t abyte,
+             struct vrequest *areq, afs_size_t * aoffset,
              afs_size_t * alen, int aflags)
 {
-    register afs_int32 i, code, shortcut;
+    afs_int32 i, code, shortcut;
 #if    defined(AFS_AIX32_ENV) || defined(AFS_SGI_ENV)
-    register afs_int32 adjustsize = 0;
+    afs_int32 adjustsize = 0;
 #endif
     int setLocks;
     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;
-    register struct dcache *tdc;
-    register struct osi_file *file;
-    register struct afs_conn *tc;
+    struct dcache *tdc;
+    struct osi_file *file;
+    struct afs_conn *tc;
     int downDCount = 0;
     struct server *newCallback = NULL;
     char setNewCallback;
@@ -1646,6 +1970,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
     int doAdjustSize = 0;
     int doReallyAdjustSize = 0;
     int overWriteWholeChunk = 0;
+    struct rx_connection *rxconn;
 
 #ifndef AFS_NOSTATS
     struct afs_stats_AccessInfo *accP; /*Ptr to access record in stats */
@@ -1706,7 +2031,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
        if (dcLocked && (tdc->index != NULLIDX)
            && !FidCmp(&tdc->f.fid, &avc->f.fid) && chunk == tdc->f.chunk
            && !(afs_indexFlags[tdc->index] & (IFFree | IFDiscarded))) {
-           /* got the right one.  It might not be the right version, and it 
+           /* got the right one.  It might not be the right version, and it
             * might be fetching, but it's the right dcache entry.
             */
            /* All this code should be integrated better with what follows:
@@ -1719,7 +2044,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
            ReleaseReadLock(&afs_xdcache);
            shortcut = 1;
 
-           if (hsame(tdc->f.versionNo, avc->f.m.DataVersion)
+           if (afs_IsDCacheFresh(tdc, avc)
                && !(tdc->dflags & DFFetching)) {
 
                afs_stats_cmperf.dcacheHits++;
@@ -1752,6 +2077,7 @@ afs_GetDCache(register 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.
@@ -1769,9 +2095,18 @@ afs_GetDCache(register 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_GetDSlot(index, NULL);
+               tdc = afs_GetValidDSlot(index);
+               if (!tdc) {
+                    /* we got an i/o error when trying to get the given dslot.
+                     * it's possible the dslot we're looking for is elsewhere,
+                     * but most likely the disk cache is currently unusable, so
+                     * all afs_GetValidDSlot calls will fail, so just bail out. */
+                   dslot_error = 1;
+                    index = NULLIDX;
+                   break;
+               }
                ReleaseReadLock(&tdc->tlock);
                /*
                 * Locks held:
@@ -1793,8 +2128,6 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
                afs_PutDCache(tdc);
                tdc = 0;
            }
-           us = index;
-           index = afs_dcnextTbl[index];
        }
 
        /*
@@ -1810,34 +2143,64 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
            afs_Trace2(afs_iclSetp, CM_TRACE_GETDCACHE1, ICL_TYPE_POINTER,
                       avc, ICL_TYPE_INT32, chunk);
 
-           /* Make sure there is a free dcache entry for us to use */
+           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;
+           }
+
            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");
+               if (!setLocks)
+                   avc->f.states |= CDCLock;
+               /* just need slots */
+               afs_GetDownD(5, (int *)0, afs_DCGetBucket(avc));
+               if (!setLocks)
+                   avc->f.states &= ~CDCLock;
+           }
+           code = afs_AllocDCache(&tdc, avc, chunk, aflags, NULL);
+           if (code) {
+               ReleaseWriteLock(&afs_xdcache);
+                if (code == ENOSPC) {
+                    /* It looks like afs_AllocDCache failed because we don't
+                     * have any free dslots to use. Maybe if we wait a little
+                     * while, we'll be able to free up some slots, so try for 5
+                     * minutes, then bail out. */
+                    if (++downDCount > 300) {
+                        afs_warn("afs: Unable to get free cache space for file "
+                                 "%u:%u.%u.%u for 5 minutes; failing with an i/o error\n",
+                                 avc->f.fid.Cell,
+                                 avc->f.fid.Fid.Volume,
+                                 avc->f.fid.Fid.Vnode,
+                                 avc->f.fid.Fid.Unique);
+                        goto done;
                     }
-                   ReleaseWriteLock(&afs_xdcache);
-                   /*
-                    * Locks held:
-                    * avc->lock(R) if setLocks
-                    * avc->lock(W) if !setLocks
-                    */
-                   afs_osi_Wait(1000, 0, 0);
-                   goto RetryLookup;
-               }
+                    afs_osi_Wait(1000, 0, 0);
+                    goto RetryLookup;
+                }
+
+                /* afs_AllocDCache failed, but not because we're out of free
+                 * dslots. Something must be screwy with the cache, so bail out
+                 * immediately without waiting. */
+                afs_warn("afs: Error while alloc'ing cache slot for file "
+                         "%u:%u.%u.%u; failing with an i/o error\n",
+                         avc->f.fid.Cell,
+                         avc->f.fid.Fid.Volume,
+                         avc->f.fid.Fid.Vnode,
+                         avc->f.fid.Fid.Unique);
+                goto done;
            }
 
-           tdc = afs_AllocDCache(avc, chunk, aflags, NULL);
+           /*
+            * 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
@@ -1918,13 +2281,14 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
        if (AFS_CHUNKTOBASE(chunk) >= avc->f.m.Length &&
 #endif
 #endif /* defined(AFS_AIX32_ENV) || defined(AFS_SGI_ENV) */
-           !hsame(avc->f.m.DataVersion, tdc->f.versionNo))
+           !afs_IsDCacheFresh(tdc, avc))
            doReallyAdjustSize = 1;
 
        if (doReallyAdjustSize || overWriteWholeChunk) {
            /* no data in file to read at this position */
            UpgradeSToWLock(&tdc->lock, 607);
            file = afs_CFileOpen(&tdc->f.inode);
+            osi_Assert(file);
            afs_CFileTruncate(file, 0);
            afs_CFileClose(file);
            afs_AdjustSize(tdc, 0);
@@ -1981,7 +2345,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
      * avc->lock(W) if !setLocks || slowPass
      * tdc->lock(S)
      */
-    if (!hsame(avc->f.m.DataVersion, tdc->f.versionNo) && !overWriteWholeChunk) {
+    if (!afs_IsDCacheFresh(tdc, avc) && !overWriteWholeChunk) {
        /*
         * Version number mismatch.
         */
@@ -2051,7 +2415,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
         */
 
        /* Watch for standard race condition around osi_FlushText */
-       if (hsame(avc->f.m.DataVersion, tdc->f.versionNo)) {
+       if (afs_IsDCacheFresh(tdc, avc)) {
            updateV2DC(setLocks, avc, tdc, 569);        /* set hint */
            afs_stats_cmperf.dcacheHits++;
            ConvertWToSLock(&tdc->lock);
@@ -2079,10 +2443,6 @@ afs_GetDCache(register 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;
@@ -2092,16 +2452,52 @@ afs_GetDCache(register 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)
+            if (Position > maxGoodLength) { /* If we're beyond EOF */
+                size = 0;
+           } else if (Position + size > maxGoodLength) {
                size = maxGoodLength - Position;
-           if (size < 0)
-               size = 0;       /* Handle random races */
+            }
+            osi_Assert(size >= 0);
+
            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 > avc->f.truncPos) {
+                    size = 0;
+               } else if (Position + size > avc->f.truncPos) {
+                   size = avc->f.truncPos - Position;
+               }
+                osi_Assert(size >= 0);
            }
        }
        if (afs_mariner && !tdc->f.chunk)
@@ -2112,6 +2508,14 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
         */
        DZap(tdc);      /* pages in cache may be old */
        file = afs_CFileOpen(&tdc->f.inode);
+        if (!file) {
+            /* We can't access the file in the disk cache backing this dcache;
+             * bail out. */
+            ReleaseWriteLock(&tdc->lock);
+            afs_PutDCache(tdc);
+            tdc = NULL;
+            goto done;
+        }
        afs_RemoveVCB(&avc->f.fid);
        tdc->f.states |= DWriting;
        tdc->dflags |= DFFetching;
@@ -2124,8 +2528,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
                           ICL_TYPE_POINTER, tdc, ICL_TYPE_INT32,
                           tdc->dflags);
        }
-       tsmall =
-           (struct afs_FetchOutput *)osi_AllocLargeSpace(sizeof(struct afs_FetchOutput));
+       tsmall = osi_AllocLargeSpace(sizeof(struct afs_FetchOutput));
        setVcacheStatus = 0;
 #ifndef AFS_NOSTATS
        /*
@@ -2207,7 +2610,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
                 * tdc->lock(W)
                 */
 
-               tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK);
+               tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK, &rxconn);
                if (tc) {
 #ifndef AFS_NOSTATS
                    numFetchLoops++;
@@ -2216,38 +2619,44 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
 
 #endif /* AFS_NOSTATS */
                    if (!setLocks || slowPass) {
-                       avc->callback = tc->srvr->server;
+                       avc->callback = tc->parent->srvr->server;
                    } else {
-                       newCallback = tc->srvr->server;
+                       newCallback = tc->parent->srvr->server;
                        setNewCallback = 1;
                    }
                    i = osi_Time();
-                   code = afs_CacheFetchProc(tc, file, Position, tdc,
+                   code = afs_CacheFetchProc(tc, rxconn, file, Position, tdc,
                                               avc, size, tsmall);
                } else
                   code = -1;
 
                if (code == 0) {
-                   /* callback could have been broken (or expired) in a race here, 
+                   /* callback could have been broken (or expired) in a race here,
                     * but we return the data anyway.  It's as good as we knew about
                     * when we started. */
-                   /* 
-                    * validPos is updated by CacheFetchProc, and can only be 
-                    * modifed under a dcache write lock, which we've blocked out 
+                   /*
+                    * validPos is updated by CacheFetchProc, and can only be
+                    * modifed under a dcache write lock, which we've blocked out
                     */
+                   afs_size_t length;
+
                    size = tdc->validPos - Position;    /* actual segment size */
                    if (size < 0)
                        size = 0;
                    afs_CFileTruncate(file, size);      /* prune it */
-               } else {
+
+                   /* Check that the amount of data that we fetched for the
+                    * dcache makes sense. */
+                   FillInt64(length, tsmall->OutStatus.Length_hi, tsmall->OutStatus.Length);
+                   if (!IsDCacheSizeOK(tdc, avc, size,
+                                       length,
+                                       tsmall->OutStatus.DataVersion, 1)) {
+                       code = EIO;
+                   }
+               }
+               if (code) {
                    if (!setLocks || slowPass) {
-                       ObtainWriteLock(&afs_xcbhash, 453);
-                       afs_DequeueCallback(avc);
-                       avc->f.states &= ~(CStatd | CUnique);
-                       avc->callback = NULL;
-                       ReleaseWriteLock(&afs_xcbhash);
-                       if (avc->f.fid.Fid.Vnode & 1 || (vType(avc) == VDIR))
-                           osi_dnlc_purgedp(avc);
+                       afs_StaleVCacheFlags(avc, AFS_STALEVC_CLEARCB, CUnique);
                    } else {
                        /* Something lost.  Forget about performance, and go
                         * back with a vcache write lock.
@@ -2260,14 +2669,26 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
                        ReleaseWriteLock(&tdc->lock);
                        afs_PutDCache(tdc);
                        tdc = 0;
+
+                       /*
+                        * Call afs_Analyze to manage the connection references
+                        * and handle the error code (possibly mark servers
+                        * down, etc). We are going to retry getting the
+                        * dcache regardless, so we just ignore the retry hint
+                        * returned by afs_Analyze on this call.
+                        */
+                       (void)afs_Analyze(tc, rxconn, code, &avc->f.fid, areq,
+                                         AFS_STATS_FS_RPCIDX_FETCHDATA, SHARED_LOCK, NULL);
+
                        ReleaseReadLock(&avc->lock);
+
                        slowPass = 1;
                        goto RetryGetDCache;
                    }
                }
 
            } while (afs_Analyze
-                    (tc, code, &avc->f.fid, areq,
+                    (tc, rxconn, code, &avc->f.fid, areq,
                      AFS_STATS_FS_RPCIDX_FETCHDATA, SHARED_LOCK, NULL));
 
        /*
@@ -2315,12 +2736,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
            ReleaseWriteLock(&tdc->lock);
            afs_PutDCache(tdc);
            if (!afs_IsDynroot(avc)) {
-               ObtainWriteLock(&afs_xcbhash, 454);
-               afs_DequeueCallback(avc);
-               avc->f.states &= ~(CStatd | CUnique);
-               ReleaseWriteLock(&afs_xcbhash);
-               if (avc->f.fid.Fid.Vnode & 1 || (vType(avc) == VDIR))
-                   osi_dnlc_purgedp(avc);
+               afs_StaleVCacheFlags(avc, 0, CUnique);
                /*
                 * Locks held:
                 * avc->lock(W); assert(!setLocks || slowPass)
@@ -2474,11 +2890,12 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
  * Environment:
  *     The afs_xdcache is write-locked through this whole affair.
  */
-void
+int
 afs_WriteThroughDSlots(void)
 {
-    register struct dcache *tdc;
-    register afs_int32 i, touchedit = 0;
+    struct dcache *tdc;
+    afs_int32 i, touchedit = 0;
+    int code = 0;
 
     struct afs_q DirtyQ, *tq;
 
@@ -2514,7 +2931,7 @@ afs_WriteThroughDSlots(void)
 
 #define DQTODC(q)      ((struct dcache *)(((char *) (q)) - sizeof(struct afs_q)))
 
-    for (tq = DirtyQ.prev; tq != &DirtyQ; tq = QPrev(tq)) {
+    for (tq = DirtyQ.prev; tq != &DirtyQ && code == 0; tq = QPrev(tq)) {
        tdc = DQTODC(tq);
        if (tdc->dflags & DFEntryMod) {
            int wrLock;
@@ -2525,9 +2942,15 @@ afs_WriteThroughDSlots(void)
            if (wrLock && (tdc->dflags & DFEntryMod)) {
                tdc->dflags &= ~DFEntryMod;
                ObtainWriteLock(&afs_xdcache, 620);
-               afs_WriteDCache(tdc, 1);
+               code = afs_WriteDCache(tdc, 1);
                ReleaseWriteLock(&afs_xdcache);
-               touchedit = 1;
+                if (code) {
+                    /* We didn't successfully write out the dslot; make sure we
+                     * try again later */
+                    tdc->dflags |= DFEntryMod;
+                } else {
+                    touchedit = 1;
+                }
            }
            if (wrLock)
                ReleaseWriteLock(&tdc->lock);
@@ -2536,6 +2959,10 @@ afs_WriteThroughDSlots(void)
        afs_PutDCache(tdc);
     }
 
+    if (code) {
+        return code;
+    }
+
     ObtainWriteLock(&afs_xdcache, 617);
     if (!touchedit && (cacheDiskType != AFS_FCACHE_TYPE_MEM)) {
        /* Touch the file to make sure that the mtime on the file is kept
@@ -2544,14 +2971,11 @@ afs_WriteThroughDSlots(void)
         */
        struct afs_fheader theader;
 
-       theader.magic = AFS_FHMAGIC;
-       theader.firstCSize = AFS_FIRSTCSIZE;
-       theader.otherCSize = AFS_OTHERCSIZE;
-       theader.version = AFS_CI_VERSION;
-       theader.dataSize = sizeof(struct fcache);
+       afs_InitFHeader(&theader);
        afs_osi_Write(afs_cacheInodep, 0, &theader, sizeof(theader));
     }
     ReleaseWriteLock(&afs_xdcache);
+    return 0;
 }
 
 /*
@@ -2563,16 +2987,16 @@ afs_WriteThroughDSlots(void)
  *
  * Parameters:
  *     aslot : Dcache slot to look at.
- *     tmpdc : Ptr to dcache entry.
+ *      type : What 'type' of dslot to get; see the dslot_state enum
  *
  * Environment:
  *     Must be called with afs_xdcache write-locked.
  */
 
 struct dcache *
-afs_MemGetDSlot(register afs_int32 aslot, register struct dcache *tmpdc)
+afs_MemGetDSlot(afs_int32 aslot, dslot_state type)
 {
-    register struct dcache *tdc;
+    struct dcache *tdc;
     int existing = 0;
 
     AFS_STATCNT(afs_MemGetDSlot);
@@ -2590,30 +3014,36 @@ afs_MemGetDSlot(register afs_int32 aslot, register 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 = (struct dcache *)afs_osi_Alloc(sizeof(struct dcache));
+
+    /* if we got here, the given slot is not in memory in our list of known
+     * slots. for memcache, the only place a dslot can exist is in memory, so
+     * if the caller is expecting to get back a known dslot, and we've reached
+     * here, something is very wrong. DSLOT_NEW is the only type of dslot that
+     * may not exist; for all others, the caller assumes the given dslot
+     * already exists. so, 'type' had better be DSLOT_NEW here, or something is
+     * very wrong. */
+    osi_Assert(type == DSLOT_NEW);
+
+    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;
@@ -2637,8 +3067,7 @@ afs_MemGetDSlot(register afs_int32 aslot, register 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 */
@@ -2654,18 +3083,19 @@ unsigned int last_error = 0, lasterrtime = 0;
  *
  * Parameters:
  *     aslot : Dcache slot to look at.
- *     tmpdc : Ptr to dcache entry.
+ *      type : What 'type' of dslot to get; see the dslot_state enum
  *
  * Environment:
  *     afs_xdcache lock write-locked.
  */
 struct dcache *
-afs_UFSGetDSlot(register afs_int32 aslot, register struct dcache *tmpdc)
+afs_UFSGetDSlot(afs_int32 aslot, dslot_state type)
 {
-    register afs_int32 code;
-    register struct dcache *tdc;
+    afs_int32 code;
+    struct dcache *tdc;
     int existing = 0;
     int entryok;
+    int off;
 
     AFS_STATCNT(afs_UFSGetDSlot);
     if (CheckLock(&afs_xdcache) != -1)
@@ -2682,49 +3112,88 @@ afs_UFSGetDSlot(register afs_int32 aslot, register 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 = (struct dcache *)afs_osi_Alloc(sizeof(struct dcache));
+    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();
+#else
+       last_error = code;
+#endif
+       lasterrtime = osi_Time();
+       if (type != DSLOT_NEW) {
+           /* If we are requesting a non-DSLOT_NEW slot, this is an error.
+            * non-DSLOT_NEW slots are supposed to already exist, so if we
+            * failed to read in the slot, something is wrong. */
+           struct osi_stat tstat;
+           if (afs_osi_Stat(afs_cacheInodep, &tstat)) {
+               tstat.size = -1;
+           }
+           afs_warn("afs: disk cache read error in CacheItems slot %d "
+                    "off %d/%d code %d/%d\n",
+                    (int)aslot,
+                    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 (type == DSLOT_VALID) {
+           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 (type == DSLOT_VALID && tdc->f.fid.Fid.Volume == 0) {
+       osi_Panic("afs: invalid zero-volume dcache entry at slot %d off %d",
+                 (int)aslot, off);
+    }
+
+    if (type == DSLOT_UNUSED) {
+       /* the requested dslot is known to exist, but contain invalid data
+        * (this happens when we're using a dslot from the free or discard
+        * list). be sure not to re-use the data in it, so force invalidation.
+        */
+       entryok = 0;
+    }
 
     if (!entryok) {
        tdc->f.fid.Cell = 0;
@@ -2732,23 +3201,17 @@ afs_UFSGetDSlot(register afs_int32 aslot, register 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);
     } else {
-       if (&tdc->f != 0) {
-           if (tdc->f.states & DRO) {
-               afs_DCMoveBucket(tdc, 0, 2);
-           } else if (tdc->f.states & DBackup) {
-               afs_DCMoveBucket(tdc, 0, 1);
-           } else {
-               afs_DCMoveBucket(tdc, 0, 1); 
-           }
-       } 
+       if (tdc->f.states & DRO) {
+           afs_DCMoveBucket(tdc, 0, 2);
+       } else if (tdc->f.states & DBackup) {
+           afs_DCMoveBucket(tdc, 0, 1);
+       } else {
+           afs_DCMoveBucket(tdc, 0, 1);
+       }
     }
     tdc->refCount = 1;
     tdc->index = aslot;
@@ -2772,8 +3235,7 @@ afs_UFSGetDSlot(register afs_int32 aslot, register 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 */
@@ -2794,9 +3256,9 @@ afs_UFSGetDSlot(register afs_int32 aslot, register struct dcache *tmpdc)
  */
 
 int
-afs_WriteDCache(register struct dcache *adc, int atime)
+afs_WriteDCache(struct dcache *adc, int atime)
 {
-    register afs_int32 code;
+    afs_int32 code;
 
     if (cacheDiskType == AFS_FCACHE_TYPE_MEM)
        return 0;
@@ -2804,6 +3266,19 @@ afs_WriteDCache(register 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.
      */
@@ -2813,8 +3288,12 @@ afs_WriteDCache(register 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;
 }
 
@@ -2830,10 +3309,10 @@ afs_WriteDCache(register struct dcache *adc, int atime)
  *     Nothing interesting.
  */
 int
-afs_wakeup(register struct vcache *avc)
+afs_wakeup(struct vcache *avc)
 {
-    register int i;
-    register struct brequest *tb;
+    int i;
+    struct brequest *tb;
     tb = afs_brs;
     AFS_STATCNT(afs_wakeup);
     for (i = 0; i < NBRS; i++, tb++) {
@@ -2850,7 +3329,7 @@ afs_wakeup(register struct vcache *avc)
             * is already being handled by the higher-level code.
             */
            if ((avc->f.states & CSafeStore) == 0) {
-               tb->code = 0;
+               tb->code_raw = tb->code_checkcode = 0;
                tb->flags |= BUVALID;
                if (tb->flags & BUWAIT) {
                    tb->flags &= ~BUWAIT;
@@ -2863,7 +3342,6 @@ afs_wakeup(register struct vcache *avc)
     return 0;
 }
 
-
 /*!
  * Given a file name and inode, set up that file to be an
  * active member in the AFS cache.  This also involves checking
@@ -2878,12 +3356,12 @@ afs_wakeup(register struct vcache *avc)
 int
 afs_InitCacheFile(char *afile, ino_t ainode)
 {
-    register afs_int32 code;
+    afs_int32 code;
     afs_int32 index;
     int fileIsBad;
     struct osi_file *tfile;
     struct osi_stat tstat;
-    register struct dcache *tdc;
+    struct dcache *tdc;
 
     AFS_STATCNT(afs_InitCacheFile);
     index = afs_stats_cmperf.cacheNumEntries;
@@ -2891,62 +3369,74 @@ 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);
 
     ObtainWriteLock(&tdc->lock, 621);
     ObtainWriteLock(&afs_xdcache, 622);
-    if (afile) {
-       code = afs_LookupInodeByPath(afile, &tdc->f.inode.ufs, NULL);
-       if (code) {
-           ReleaseWriteLock(&afs_xdcache);
-           ReleaseWriteLock(&tdc->lock);
-           afs_PutDCache(tdc);
-           return code;
-       }
+    if (!afile && !ainode) {
+       tfile = NULL;
+       fileIsBad = 1;
     } else {
-       /* Add any other 'complex' inode types here ... */
+       if (afile) {
+           code = afs_LookupInodeByPath(afile, &tdc->f.inode.ufs, NULL);
+           if (code) {
+               ReleaseWriteLock(&afs_xdcache);
+               ReleaseWriteLock(&tdc->lock);
+               afs_PutDCache(tdc);
+               return code;
+           }
+       } else {
+           /* Add any other 'complex' inode types here ... */
 #if !defined(AFS_LINUX26_ENV) && !defined(AFS_CACHE_VNODE_PATH)
-       tdc->f.inode.ufs = ainode;
+           tdc->f.inode.ufs = ainode;
 #else
-       osi_Panic("Can't init cache with inode numbers when complex inodes are "
-                 "in use\n");
+           osi_Panic("Can't init cache with inode numbers when complex inodes are "
+                     "in use\n");
 #endif
-    }
-    fileIsBad = 0;
-    if ((tdc->f.states & DWriting) || tdc->f.fid.Fid.Volume == 0)
-       fileIsBad = 1;
-    tfile = osi_UFSOpen(&tdc->f.inode);
-    code = afs_osi_Stat(tfile, &tstat);
-    if (code)
-       osi_Panic("initcachefile stat");
+       }
+       fileIsBad = 0;
+       if ((tdc->f.states & DWriting) || tdc->f.fid.Fid.Volume == 0)
+           fileIsBad = 1;
+       tfile = osi_UFSOpen(&tdc->f.inode);
+       if (!tfile) {
+           ReleaseWriteLock(&afs_xdcache);
+           ReleaseWriteLock(&tdc->lock);
+           afs_PutDCache(tdc);
+           return ENOENT;
+       }
 
-    /*
-     * If file size doesn't match the cache info file, it's probably bad.
-     */
-    if (tdc->f.chunkBytes != tstat.size)
-       fileIsBad = 1;
+       code = afs_osi_Stat(tfile, &tstat);
+       if (code)
+           osi_Panic("initcachefile stat");
+
+       /*
+        * If file size doesn't match the cache info file, it's probably bad.
+        */
+       if (tdc->f.chunkBytes != tstat.size)
+           fileIsBad = 1;
+       /*
+        * If file changed within T (120?) seconds of cache info file, it's
+        * probably bad.  In addition, if slot changed within last T seconds,
+        * the cache info file may be incorrectly identified, and so slot
+        * may be bad.
+        */
+       if (cacheInfoModTime < tstat.mtime + 120)
+           fileIsBad = 1;
+       if (cacheInfoModTime < tdc->f.modTime + 120)
+           fileIsBad = 1;
+       /* In case write through is behind, make sure cache items entry is
+        * at least as new as the chunk.
+        */
+       if (tdc->f.modTime < tstat.mtime)
+           fileIsBad = 1;
+    }
     tdc->f.chunkBytes = 0;
 
-    /*
-     * If file changed within T (120?) seconds of cache info file, it's
-     * probably bad.  In addition, if slot changed within last T seconds,
-     * the cache info file may be incorrectly identified, and so slot
-     * may be bad.
-     */
-    if (cacheInfoModTime < tstat.mtime + 120)
-       fileIsBad = 1;
-    if (cacheInfoModTime < tdc->f.modTime + 120)
-       fileIsBad = 1;
-    /* In case write through is behind, make sure cache items entry is
-     * at least as new as the chunk.
-     */
-    if (tdc->f.modTime < tstat.mtime)
-       fileIsBad = 1;
     if (fileIsBad) {
        tdc->f.fid.Fid.Volume = 0;      /* not in the hash table */
-       if (tstat.size != 0)
+       if (tfile && tstat.size != 0)
            osi_UFSTruncate(tfile, 0);
        tdc->f.states &= ~(DRO|DBackup|DRW);
        afs_DCMoveBucket(tdc, 0, 0);
@@ -2983,11 +3473,12 @@ afs_InitCacheFile(char *afile, ino_t ainode)
        afs_indexUnique[index] = tdc->f.fid.Fid.Unique;
     }                          /*File is not bad */
 
-    osi_UFSClose(tfile);
+    if (tfile)
+       osi_UFSClose(tfile);
     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);
@@ -3007,18 +3498,19 @@ afs_InitCacheFile(char *afile, ino_t ainode)
  * Initialize dcache related variables.
  *
  * \param afiles
- * \param ablocks 
+ * \param ablocks
  * \param aDentries
  * \param achunk
  * \param aflags
  *
  */
-void
+int
 afs_dcacheInit(int afiles, int ablocks, int aDentries, int achunk, int aflags)
 {
-    register struct dcache *tdp;
+    struct dcache *tdp;
     int i;
     int code;
+    int afs_dhashbits;
 
     afs_freeDCList = NULLIDX;
     afs_discardDCList = NULLIDX;
@@ -3040,63 +3532,54 @@ afs_dcacheInit(int afiles, int ablocks, int aDentries, int achunk, int aflags)
     if (!aDentries)
        aDentries = DDSIZE;
 
-    if (aflags & AFSCALL_INIT_MEMCACHE) {
-       /*
-        * Use a memory cache instead of a disk cache
-        */
-       cacheDiskType = AFS_FCACHE_TYPE_MEM;
-       afs_cacheType = &afs_MemCacheOps;
-       afiles = (afiles < aDentries) ? afiles : aDentries;     /* min */
-       ablocks = afiles * (AFS_FIRSTCSIZE / 1024);
-       /* ablocks is reported in 1K blocks */
-       code = afs_InitMemCache(afiles, AFS_FIRSTCSIZE, aflags);
-       if (code != 0) {
-           afs_warn("afsd: memory cache too large for available memory.\n");
-           afs_warn("afsd: AFS files cannot be accessed.\n\n");
-           dcacheDisabled = 1;
-           afiles = ablocks = 0;
-       } else
-           afs_warn("Memory cache: Allocating %d dcache entries...",
-                  aDentries);
-    } else {
-       cacheDiskType = AFS_FCACHE_TYPE_UFS;
-       afs_cacheType = &afs_UfsCacheOps;
-    }
-
+    /* afs_dhashsize defaults to 1024 */
     if (aDentries > 512)
        afs_dhashsize = 2048;
+    /* Try to keep the average chain length around two unless the table
+     * would be ridiculously big. */
+    if (aDentries > 4096) {
+       afs_dhashbits = opr_fls(aDentries) - 3;
+       /* Cap the hash tables to 32k entries. */
+       if (afs_dhashbits > 15)
+           afs_dhashbits = 15;
+       afs_dhashsize = opr_jhash_size(afs_dhashbits);
+    }
     /* initialize hash tables */
-    afs_dvhashTbl =
-       (afs_int32 *) afs_osi_Alloc(afs_dhashsize * sizeof(afs_int32));
-    afs_dchashTbl =
-       (afs_int32 *) afs_osi_Alloc(afs_dhashsize * sizeof(afs_int32));
+    afs_dvhashTbl = afs_osi_Alloc(afs_dhashsize * sizeof(afs_int32));
+    osi_Assert(afs_dvhashTbl != NULL);
+    afs_dchashTbl = afs_osi_Alloc(afs_dhashsize * sizeof(afs_int32));
+    osi_Assert(afs_dchashTbl != NULL);
     for (i = 0; i < afs_dhashsize; i++) {
        afs_dvhashTbl[i] = NULLIDX;
        afs_dchashTbl[i] = NULLIDX;
     }
-    afs_dvnextTbl = (afs_int32 *) afs_osi_Alloc(afiles * sizeof(afs_int32));
-    afs_dcnextTbl = (afs_int32 *) afs_osi_Alloc(afiles * sizeof(afs_int32));
+    afs_dvnextTbl = afs_osi_Alloc(afiles * sizeof(afs_int32));
+    osi_Assert(afs_dvnextTbl != NULL);
+    afs_dcnextTbl = afs_osi_Alloc(afiles * sizeof(afs_int32));
+    osi_Assert(afs_dcnextTbl != NULL);
     for (i = 0; i < afiles; i++) {
        afs_dvnextTbl[i] = NULLIDX;
        afs_dcnextTbl[i] = NULLIDX;
     }
 
     /* Allocate and zero the pointer array to the dcache entries */
-    afs_indexTable = (struct dcache **)
-       afs_osi_Alloc(sizeof(struct dcache *) * afiles);
+    afs_indexTable = afs_osi_Alloc(sizeof(struct dcache *) * afiles);
+    osi_Assert(afs_indexTable != NULL);
     memset(afs_indexTable, 0, sizeof(struct dcache *) * afiles);
-    afs_indexTimes =
-       (afs_hyper_t *) afs_osi_Alloc(afiles * sizeof(afs_hyper_t));
+    afs_indexTimes = afs_osi_Alloc(afiles * sizeof(afs_hyper_t));
+    osi_Assert(afs_indexTimes != NULL);
     memset(afs_indexTimes, 0, afiles * sizeof(afs_hyper_t));
-    afs_indexUnique =
-       (afs_int32 *) afs_osi_Alloc(afiles * sizeof(afs_uint32));
+    afs_indexUnique = afs_osi_Alloc(afiles * sizeof(afs_uint32));
+    osi_Assert(afs_indexUnique != NULL);
     memset(afs_indexUnique, 0, afiles * sizeof(afs_uint32));
-    afs_indexFlags = (u_char *) afs_osi_Alloc(afiles * sizeof(u_char));
+    afs_indexFlags = afs_osi_Alloc(afiles * sizeof(u_char));
+    osi_Assert(afs_indexFlags != NULL);
     memset(afs_indexFlags, 0, afiles * sizeof(char));
 
     /* Allocate and thread the struct dcache entries themselves */
     tdp = afs_Initial_freeDSList =
-       (struct dcache *)afs_osi_Alloc(aDentries * sizeof(struct dcache));
+       afs_osi_Alloc(aDentries * sizeof(struct dcache));
+    osi_Assert(tdp != NULL);
     memset(tdp, 0, aDentries * sizeof(struct dcache));
 #ifdef KERNEL_HAVE_PIN
     pin((char *)afs_indexTable, sizeof(struct dcache *) * afiles);     /* XXX */
@@ -3128,11 +3611,35 @@ afs_dcacheInit(int afiles, int ablocks, int aDentries, int achunk, int aflags)
 
     afs_dcentries = aDentries;
     afs_blocksUsed = 0;
-    afs_stats_cmperf.cacheBucket0_Discarded = 
-       afs_stats_cmperf.cacheBucket1_Discarded = 
+    afs_stats_cmperf.cacheBucket0_Discarded =
+       afs_stats_cmperf.cacheBucket1_Discarded =
        afs_stats_cmperf.cacheBucket2_Discarded = 0;
     afs_DCSizeInit();
     QInit(&afs_DLRU);
+
+    if (aflags & AFSCALL_INIT_MEMCACHE) {
+       /*
+        * Use a memory cache instead of a disk cache
+        */
+       cacheDiskType = AFS_FCACHE_TYPE_MEM;
+       afs_cacheType = &afs_MemCacheOps;
+       afiles = (afiles < aDentries) ? afiles : aDentries;     /* min */
+       ablocks = afiles * (AFS_FIRSTCSIZE / 1024);
+       /* ablocks is reported in 1K blocks */
+       code = afs_InitMemCache(afiles, AFS_FIRSTCSIZE, aflags);
+       if (code != 0) {
+           afs_warn("afsd: memory cache too large for available memory.\n");
+           afs_warn("afsd: AFS files cannot be accessed.\n\n");
+           dcacheDisabled = 1;
+            return code;
+       } else
+           afs_warn("Memory cache: Allocating %d dcache entries...",
+                  aDentries);
+    } else {
+       cacheDiskType = AFS_FCACHE_TYPE_UFS;
+       afs_cacheType = &afs_UfsCacheOps;
+    }
+    return 0;
 }
 
 /*!
@@ -3184,8 +3691,8 @@ shutdown_dcache(void)
     afs_osi_Free(afs_dchashTbl, afs_dhashsize * sizeof(afs_int32));
 
     afs_blocksUsed = afs_dcentries = 0;
-    afs_stats_cmperf.cacheBucket0_Discarded = 
-       afs_stats_cmperf.cacheBucket1_Discarded = 
+    afs_stats_cmperf.cacheBucket0_Discarded =
+       afs_stats_cmperf.cacheBucket1_Discarded =
        afs_stats_cmperf.cacheBucket2_Discarded = 0;
     hzero(afs_indexCounter);
 
@@ -3202,9 +3709,9 @@ shutdown_dcache(void)
 /*!
  * Get a dcache ready for writing, respecting the current cache size limits
  *
- * len is required because afs_GetDCache with flag == 4 expects the length 
- * field to be filled. It decides from this whether it's necessary to fetch 
- * data into the chunk before writing or not (when the whole chunk is 
+ * len is required because afs_GetDCache with flag == 4 expects the length
+ * field to be filled. It decides from this whether it's necessary to fetch
+ * data into the chunk before writing or not (when the whole chunk is
  * overwritten!).
  *
  * \param avc          The vcache to fetch a dcache for
@@ -3214,15 +3721,15 @@ shutdown_dcache(void)
  * \param noLock
  *
  * \return If successful, a reference counted dcache with tdc->lock held. Lock
- *         must be released and afs_PutDCache() called to free dcache. 
+ *         must be released and afs_PutDCache() called to free dcache.
  *         NULL on  failure
  *
- * \note avc->lock must be held on entry. Function may release and reobtain 
+ * \note avc->lock must be held on entry. Function may release and reobtain
  *       avc->lock and GLOCK.
  */
 
 struct dcache *
-afs_ObtainDCacheForWriting(struct vcache *avc, afs_size_t filePos, 
+afs_ObtainDCacheForWriting(struct vcache *avc, afs_size_t filePos,
                           afs_size_t len, struct vrequest *areq,
                           int noLock)
 {
@@ -3239,7 +3746,7 @@ afs_ObtainDCacheForWriting(struct vcache *avc, afs_size_t filePos,
        tdc = afs_FindDCache(avc, filePos);
        if (tdc) {
            ObtainWriteLock(&tdc->lock, 658);
-           if (!hsame(tdc->f.versionNo, avc->f.m.DataVersion)
+           if (!afs_IsDCacheFresh(tdc, avc)
                || (tdc->dflags & DFFetching)) {
                ReleaseWriteLock(&tdc->lock);
                afs_PutDCache(tdc);
@@ -3284,8 +3791,6 @@ afs_ObtainDCacheForWriting(struct vcache *avc, afs_size_t filePos,
     return tdc;
 }
 
-#if defined(AFS_DISCON_ENV)
-
 /*!
  * Make a shadow copy of a dir's dcache. It's used for disconnected
  * operations like remove/create/rename to keep the original directory data.
@@ -3324,7 +3829,8 @@ afs_MakeShadowDir(struct vcache *avc, struct dcache *adc)
     ObtainWriteLock(&afs_xdcache, 716);
 
     /* Get a fresh dcache. */
-    new_dc = afs_AllocDCache(avc, 0, 0, &shadow_fid);
+    (void)afs_AllocDCache(&new_dc, avc, 0, 0, &shadow_fid);
+    osi_Assert(new_dc);
 
     ObtainReadLock(&adc->mflock);
 
@@ -3339,7 +3845,7 @@ afs_MakeShadowDir(struct vcache *avc, struct dcache *adc)
     new_dc->f.chunkBytes = adc->f.chunkBytes;
 
     ReleaseReadLock(&adc->mflock);
-    
+
     /* Now add to the two hash chains */
     i = DCHash(&shadow_fid, 0);
     afs_dcnextTbl[new_dc->index] = afs_dchashTbl[i];
@@ -3352,7 +3858,7 @@ afs_MakeShadowDir(struct vcache *avc, struct dcache *adc)
     ReleaseWriteLock(&afs_xdcache);
 
     /* Alloc a 4k block. */
-    data = (char *) afs_osi_Alloc(4096);
+    data = afs_osi_Alloc(4096);
     if (!data) {
        afs_warn("afs_MakeShadowDir: could not alloc data\n");
        ret_code = ENOMEM;
@@ -3362,6 +3868,8 @@ afs_MakeShadowDir(struct vcache *avc, struct dcache *adc)
     /* Open the files. */
     tfile_src = afs_CFileOpen(&adc->f.inode);
     tfile_dst = afs_CFileOpen(&new_dc->f.inode);
+    osi_Assert(tfile_src);
+    osi_Assert(tfile_dst);
 
     /* And now copy dir dcache data into this dcache,
      * 4k at a time.
@@ -3401,7 +3909,7 @@ afs_MakeShadowDir(struct vcache *avc, struct dcache *adc)
        ObtainWriteLock(&afs_xvcache, 763);
        ObtainWriteLock(&afs_disconDirtyLock, 765);
        QAdd(&afs_disconShadow, &avc->shadowq);
-       osi_vnhold(avc, 0);
+       osi_Assert((afs_RefVCache(avc) == 0));
        ReleaseWriteLock(&afs_disconDirtyLock);
        ReleaseWriteLock(&afs_xvcache);
 
@@ -3453,7 +3961,7 @@ afs_DeleteShadowDir(struct vcache *avc)
  * \param alen  The new length of the file
  *
  */
-void 
+void
 afs_PopulateDCache(struct vcache *avc, afs_size_t apos, struct vrequest *areq)
 {
     struct dcache *tdc;
@@ -3461,19 +3969,19 @@ afs_PopulateDCache(struct vcache *avc, afs_size_t apos, struct vrequest *areq)
     afs_int32 start, end;
 
     /* We're doing this to deal with the situation where we extend
-     * by writing after lseek()ing past the end of the file . If that 
-     * extension skips chunks, then those chunks won't be created, and 
-     * GetDCache will assume that they have to be fetched from the server. 
-     * So, for each chunk between the current file position, and the new 
+     * by writing after lseek()ing past the end of the file . If that
+     * extension skips chunks, then those chunks won't be created, and
+     * GetDCache will assume that they have to be fetched from the server.
+     * So, for each chunk between the current file position, and the new
      * length we GetDCache for that chunk.
      */
 
-    if (AFS_CHUNK(apos) == 0 || apos <= avc->f.m.Length) 
+    if (AFS_CHUNK(apos) == 0 || apos <= avc->f.m.Length)
        return;
 
     if (avc->f.m.Length == 0)
        start = 0;
-    else 
+    else
        start = AFS_CHUNK(avc->f.m.Length)+1;
 
     end = AFS_CHUNK(apos);
@@ -3486,5 +3994,3 @@ afs_PopulateDCache(struct vcache *avc, afs_size_t apos, struct vrequest *areq)
        start++;
     }
 }
-
-#endif