afs: Never use GetNewDSlot after init
[openafs.git] / src / afs / afs_dcache.c
index 92c7f46..647f912 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright 2000, International Business Machines Corporation and others.
  *$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
@@ -13,8 +13,6 @@
 #include <afsconfig.h>
 #include "afs/param.h"
 
-RCSID
-    ("$Header$");
 
 #include "afs/sysincludes.h"   /*Standard vendor system headers */
 #include "afsincludes.h"       /*AFS-based standard headers */
@@ -38,48 +36,48 @@ static afs_int32 afs_DCWhichBucket(afs_int32, afs_int32);
  * --------------------- Exported definitions ---------------------
  */
 /* For split cache */
-afs_int32 afs_blocksUsed_0;    /*1K blocks in cache - in theory is zero */
-afs_int32 afs_blocksUsed_1;    /*1K blocks in cache */
-afs_int32 afs_blocksUsed_2;    /*1K blocks in cache */
+afs_int32 afs_blocksUsed_0;    /*!< 1K blocks in cache - in theory is zero */
+afs_int32 afs_blocksUsed_1;    /*!< 1K blocks in cache */
+afs_int32 afs_blocksUsed_2;    /*!< 1K blocks in cache */
 afs_int32 afs_pct1 = -1;
 afs_int32 afs_pct2 = -1;
 afs_uint32 afs_tpct1 = 0;
 afs_uint32 afs_tpct2 = 0;
 afs_uint32 splitdcache = 0;
 
-afs_lock_t afs_xdcache;                /*Lock: alloc new disk cache entries */
-afs_int32 afs_freeDCList;      /*Free list for disk cache entries */
-afs_int32 afs_freeDCCount;     /*Count of elts in freeDCList */
-afs_int32 afs_discardDCList;   /*Discarded disk cache entries */
-afs_int32 afs_discardDCCount;  /*Count of elts in discardDCList */
-struct dcache *afs_freeDSList; /*Free list for disk slots */
-struct dcache *afs_Initial_freeDSList; /*Initial list for above */
-ino_t cacheInode;              /*Inode for CacheItems file */
-struct osi_file *afs_cacheInodep = 0;  /* file for CacheItems inode */
-struct afs_q afs_DLRU;         /*dcache LRU */
+afs_lock_t afs_xdcache;                /*!< Lock: alloc new disk cache entries */
+afs_int32 afs_freeDCList;      /*!< Free list for disk cache entries */
+afs_int32 afs_freeDCCount;     /*!< Count of elts in freeDCList */
+afs_int32 afs_discardDCList;   /*!< Discarded disk cache entries */
+afs_int32 afs_discardDCCount;  /*!< Count of elts in discardDCList */
+struct dcache *afs_freeDSList; /*!< Free list for disk slots */
+struct dcache *afs_Initial_freeDSList; /*!< Initial list for above */
+afs_dcache_id_t cacheInode;               /*!< Inode for CacheItems file */
+struct osi_file *afs_cacheInodep = 0;  /*!< file for CacheItems inode */
+struct afs_q afs_DLRU;         /*!< dcache LRU */
 afs_int32 afs_dhashsize = 1024;
-afs_int32 *afs_dvhashTbl;      /*Data cache hash table */
-afs_int32 *afs_dchashTbl;      /*Data cache hash table */
-afs_int32 *afs_dvnextTbl;      /*Dcache hash table links */
-afs_int32 *afs_dcnextTbl;      /*Dcache hash table links */
-struct dcache **afs_indexTable;        /*Pointers to dcache entries */
-afs_hyper_t *afs_indexTimes;   /*Dcache entry Access times */
-afs_int32 *afs_indexUnique;    /*dcache entry Fid.Unique */
-unsigned char *afs_indexFlags; /*(only one) Is there data there? */
-afs_hyper_t afs_indexCounter;  /*Fake time for marking index
+afs_int32 *afs_dvhashTbl;      /*!< Data cache hash table: hashed by FID + chunk number. */
+afs_int32 *afs_dchashTbl;      /*!< Data cache hash table: hashed by FID. */
+afs_int32 *afs_dvnextTbl;      /*!< Dcache hash table links */
+afs_int32 *afs_dcnextTbl;      /*!< Dcache hash table links */
+struct dcache **afs_indexTable;        /*!< Pointers to dcache entries */
+afs_hyper_t *afs_indexTimes;   /*!< Dcache entry Access times */
+afs_int32 *afs_indexUnique;    /*!< dcache entry Fid.Unique */
+unsigned char *afs_indexFlags; /*!< (only one) Is there data there? */
+afs_hyper_t afs_indexCounter;  /*!< Fake time for marking index
                                 * entries */
-afs_int32 afs_cacheFiles = 0;  /*Size of afs_indexTable */
-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 = 1023;       /*Underlying Filesystem minimum unit 
+afs_int32 afs_cacheFiles = 0;  /*!< Size of afs_indexTable */
+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
                                         *of disk allocation usually 1K
                                         *this value is (truefrag -1 ) to
                                         *save a bunch of subtracts... */
 #ifdef AFS_64BIT_CLIENT
 #ifdef AFS_VM_RDWR_ENV
-afs_size_t afs_vmMappingEnd;   /* for large files (>= 2GB) the VM
+afs_size_t afs_vmMappingEnd;   /* !< For large files (>= 2GB) the VM
                                 * mapping an 32bit addressing machines
                                 * can only be used below the 2 GB
                                 * line. From this point upwards we
@@ -96,55 +94,83 @@ int afs_WaitForCacheDrain = 0;
 int afs_TruncateDaemonRunning = 0;
 int afs_CacheTooFull = 0;
 
-afs_int32 afs_dcentries;       /* In-memory dcache entries */
+afs_int32 afs_dcentries;       /*!< In-memory dcache entries */
 
 
 int dcacheDisabled = 0;
 
-static int afs_UFSCacheFetchProc(), afs_UFSCacheStoreProc();
 struct afs_cacheOps afs_UfsCacheOps = {
+#ifndef HAVE_STRUCT_LABEL_SUPPORT
     osi_UFSOpen,
     osi_UFSTruncate,
     afs_osi_Read,
     afs_osi_Write,
     osi_UFSClose,
-    afs_UFSRead,
-    afs_UFSWrite,
-    afs_UFSCacheFetchProc,
-    afs_UFSCacheStoreProc,
+    afs_UFSReadUIO,
+    afs_UFSWriteUIO,
     afs_UFSGetDSlot,
     afs_UFSGetVolSlot,
     afs_UFSHandleLink,
+#else
+    .open      = osi_UFSOpen,
+    .truncate  = osi_UFSTruncate,
+    .fread     = afs_osi_Read,
+    .fwrite    = afs_osi_Write,
+    .close     = osi_UFSClose,
+    .vreadUIO  = afs_UFSReadUIO,
+    .vwriteUIO = afs_UFSWriteUIO,
+    .GetDSlot  = afs_UFSGetDSlot,
+    .GetVolSlot = afs_UFSGetVolSlot,
+    .HandleLink        = afs_UFSHandleLink,
+#endif
 };
 
 struct afs_cacheOps afs_MemCacheOps = {
+#ifndef HAVE_STRUCT_LABEL_SUPPORT
     afs_MemCacheOpen,
     afs_MemCacheTruncate,
     afs_MemReadBlk,
     afs_MemWriteBlk,
     afs_MemCacheClose,
-    afs_MemRead,
-    afs_MemWrite,
-    afs_MemCacheFetchProc,
-    afs_MemCacheStoreProc,
+    afs_MemReadUIO,
+    afs_MemWriteUIO,
     afs_MemGetDSlot,
     afs_MemGetVolSlot,
     afs_MemHandleLink,
+#else
+    .open      = afs_MemCacheOpen,
+    .truncate  = afs_MemCacheTruncate,
+    .fread     = afs_MemReadBlk,
+    .fwrite    = afs_MemWriteBlk,
+    .close     = afs_MemCacheClose,
+    .vreadUIO  = afs_MemReadUIO,
+    .vwriteUIO = afs_MemWriteUIO,
+    .GetDSlot  = afs_MemGetDSlot,
+    .GetVolSlot        = afs_MemGetVolSlot,
+    .HandleLink        = afs_MemHandleLink,
+#endif
 };
 
 int cacheDiskType;             /*Type of backing disk for cache */
 struct afs_cacheOps *afs_cacheType;
 
+/*!
+ * Where is this vcache's entry associated dcache located/
+ * \param avc The vcache entry.
+ * \return Bucket index:
+ *     1 : main
+ *     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->states & CRO) {
+    if (avc->f.states & CRO) {
        return 2;
-    } else if (avc->states & CBackup) {
+    } else if (avc->f.states & CBackup) {
        return 1;
     } else {
        /* RW */
@@ -153,15 +179,23 @@ afs_DCGetBucket(struct vcache *avc)
     return 1;
 }
 
-static void 
+/*!
+ * Readjust a dcache's size.
+ *
+ * \param adc The dcache to be adjusted.
+ * \param oldSize Old size for the dcache.
+ * \param newSize The new size to be adjusted to.
+ *
+ */
+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;
@@ -180,13 +214,22 @@ afs_DCAdjustSize(struct dcache *adc, afs_int32 oldSize, afs_int32 newSize)
     return;
 }
 
-static void 
+/*!
+ * 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
 afs_DCMoveBucket(struct dcache *adc, afs_int32 size, afs_int32 newBucket)
 {
-    if (!splitdcache) 
+    if (!splitdcache)
        return;
 
-    switch (adc->bucket) 
+    /* Substract size from old bucket. */
+    switch (adc->bucket)
     {
     case 0:
        afs_blocksUsed_0 -= size;
@@ -199,9 +242,10 @@ afs_DCMoveBucket(struct dcache *adc, afs_int32 size, afs_int32 newBucket)
        break;
     }
 
+    /* Set new bucket and increase destination bucket size. */
     adc->bucket = newBucket;
 
-    switch (adc->bucket) 
+    switch (adc->bucket)
     {
     case 0:
        afs_blocksUsed_0 += size;
@@ -213,30 +257,38 @@ afs_DCMoveBucket(struct dcache *adc, afs_int32 size, afs_int32 newBucket)
        afs_blocksUsed_2 += size;
        break;
     }
-    
+
     return;
 }
 
-static void 
-afs_DCSizeInit(void) 
+/*!
+ * Init split caches size.
+ */
+static void
+afs_DCSizeInit(void)
 {
     afs_blocksUsed_0 = afs_blocksUsed_1 = afs_blocksUsed_2 = 0;
 }
 
+
+/*!
+ * \param phase
+ * \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;
@@ -244,26 +296,21 @@ afs_DCWhichBucket(afs_int32 phase, afs_int32 bucket)
 }
 
 
-/*
- * afs_StoreWarn
- *
- * Description:
- *     Warn about failing to store a file.
+/*!
+ * Warn about failing to store a file.
  *
- * Parameters:
- *     acode   : Associated error code.
- *     avolume : Volume involved.
- *     aflags  : How to handle the output:
- *                     aflags & 1: Print out on console
- *                     aflags & 2: Print out on controlling tty
+ * \param acode Associated error code.
+ * \param avolume Volume involved.
+ * \param aflags How to handle the output:
+ *     aflags & 1: Print out on console
+ *     aflags & 2: Print out on controlling tty
  *
- * Environment:
- *     Call this from close call when vnodeops is RCS unlocked.
+ * \note Environment: Call this from close call when vnodeops is RCS unlocked.
  */
 
 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";
@@ -316,6 +363,9 @@ afs_StoreWarn(register afs_int32 acode, afs_int32 avolume,
     }
 }                              /*afs_StoreWarn */
 
+/*!
+ * Try waking up truncation daemon, if it's worth it.
+ */
 void
 afs_MaybeWakeupTruncateDaemon(void)
 {
@@ -329,7 +379,10 @@ afs_MaybeWakeupTruncateDaemon(void)
     }
 }
 
-/* Keep statistics on run time for afs_CacheTruncateDaemon. This is a
+/*!
+ * /struct CTD_stats
+ *
+ * Keep statistics on run time for afs_CacheTruncateDaemon. This is a
  * struct so we need only export one symbol for AIX.
  */
 static struct CTD_stats {
@@ -341,6 +394,12 @@ static struct CTD_stats {
 } CTD_stats;
 
 u_int afs_min_cache = 0;
+
+/*!
+ * Keeps the cache clean and free by truncating uneeded files, when used.
+ * \param
+ * \return
+ */
 void
 afs_CacheTruncateDaemon(void)
 {
@@ -356,17 +415,23 @@ afs_CacheTruncateDaemon(void)
     afs_TruncateDaemonRunning = 1;
     while (1) {
        cb_lowat = PERCENT((CM_DCACHESPACEFREEPCT - CM_DCACHEEXTRAPCT), afs_cacheBlocks);
-       MObtainWriteLock(&afs_xdcache, 266);
+       ObtainWriteLock(&afs_xdcache, 266);
        if (afs_CacheTooFull) {
            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)) {
+                   afs_CacheTooFull = 0;
                    break;
                }
                if (afs_termState == AFSOP_STOP_TRUNCDAEMON)
@@ -374,8 +439,8 @@ afs_CacheTruncateDaemon(void)
            }
            if (!afs_CacheIsTooFull())
                afs_CacheTooFull = 0;
-       }
-       MReleaseWriteLock(&afs_xdcache);
+       }       /* end of cache cleanup */
+       ReleaseWriteLock(&afs_xdcache);
 
        /*
         * This is a defensive check to try to avoid starving threads
@@ -419,11 +484,7 @@ afs_CacheTruncateDaemon(void)
            afs_stats_AddTo(CTD_stats.CTD_sleepTime, CTD_tmpTime);
        }
        if (afs_termState == AFSOP_STOP_TRUNCDAEMON) {
-#ifdef AFS_AFSDB_ENV
            afs_termState = AFSOP_STOP_AFSDB;
-#else
-           afs_termState = AFSOP_STOP_RXEVENT;
-#endif
            afs_osi_Wakeup(&afs_termState);
            break;
        }
@@ -431,26 +492,23 @@ afs_CacheTruncateDaemon(void)
 }
 
 
-/*
- * afs_AdjustSize
- *
- * Description:
- *     Make adjustment for the new size in the disk cache entry
+/*!
+ * Make adjustment for the new size in the disk cache entry
  *
- * Major Assumptions Here:
+ * \note Major Assumptions Here:
  *      Assumes that frag size is an integral power of two, less one,
  *      and that this is a two's complement machine.  I don't
  *      know of any filesystems which violate this assumption...
  *
- * Parameters:
- *     adc      : Ptr to dcache entry.
- *     anewsize : New size desired.
+ * \param adc Ptr to dcache entry.
+ * \param anewsize New size desired.
+ *
  */
 
 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);
 
@@ -471,18 +529,14 @@ afs_AdjustSize(register struct dcache *adc, register afs_int32 newSize)
 }
 
 
-/*
- * afs_GetDownD
- *
- * Description:
- *     This routine is responsible for moving at least one entry (but up
- *     to some number of them) from the LRU queue to the free queue.
+/*!
+ * This routine is responsible for moving at least one entry (but up
+ * to some number of them) from the LRU queue to the free queue.
  *
- * Parameters:
- *     anumber    : Number of entries that should ideally be moved.
- *     aneedSpace : How much space we need (1K blocks);
+ * \param anumber Number of entries that should ideally be moved.
+ * \param aneedSpace How much space we need (1K blocks);
  *
- * Environment:
+ * \note Environment:
  *     The anumber parameter is just a hint; at least one entry MUST be
  *     moved, or we'll panic.  We must be called with afs_xdcache
  *     write-locked.  We should try to satisfy both anumber and aneedspace,
@@ -490,8 +544,9 @@ 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)) 
- *  N.B. if we're called with aneedSpace <= 0 and anumber > 0, that
+ *          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
  *  something. We should be able to automatically correct that problem.
  */
@@ -506,7 +561,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 */
@@ -515,25 +570,17 @@ afs_GetDownD(int anumber, int *aneedSpace, afs_int32 buckethint)
     afs_uint32 maxVictimPtr;   /* where it is */
     int discard;
     int curbucket;
-    int vfslocked;
-
-#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 */
        }
     }
@@ -545,10 +592,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;
@@ -623,15 +672,17 @@ afs_GetDownD(int anumber, int *aneedSpace, afs_int32 buckethint)
         * during the truncate operation.
         */
        for (i = 0; i < victimPtr; i++) {
-           tdc = afs_GetDSlot(victims[i], 0);
+           tdc = afs_GetValidDSlot(victims[i]);
            /* We got tdc->tlock(R) here */
-           if (tdc->refCount == 1)
+           if (tdc && tdc->refCount == 1)
                victimDCs[i] = tdc;
            else
                victimDCs[i] = 0;
-           ReleaseReadLock(&tdc->tlock);
-           if (!victimDCs[i])
-               afs_PutDCache(tdc);
+           if (tdc) {
+               ReleaseReadLock(&tdc->tlock);
+               if (!victimDCs[i])
+                   afs_PutDCache(tdc);
+           }
        }
        for (i = 0; i < victimPtr; i++) {
            /* q is first elt in dcache entry */
@@ -640,18 +691,18 @@ 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;
                afs_size_t tchunkoffset = 0;
                afid = &tdc->f.fid;
                /* xdcache is lower than the xvcache lock */
-               MReleaseWriteLock(&afs_xdcache);
-               MObtainReadLock(&afs_xvcache);
+               ReleaseWriteLock(&afs_xdcache);
+               ObtainReadLock(&afs_xvcache);
                tvc = afs_FindVCache(afid, 0, 0 /* no stats, no vlru */ );
-               MReleaseReadLock(&afs_xvcache);
-               MObtainWriteLock(&afs_xdcache, 527);
+               ReleaseReadLock(&afs_xvcache);
+               ObtainWriteLock(&afs_xdcache, 527);
                skip = 0;
                if (tdc->refCount > 1)
                    skip = 1;
@@ -661,7 +712,7 @@ afs_GetDownD(int anumber, int *aneedSpace, afs_int32 buckethint)
                    if (((phase & 1) == 0) && osi_Active(tvc))
                         skip = 1;
                    if (((phase & 1) == 1) && osi_Active(tvc)
-                        && (tvc->states & CDCLock)
+                        && (tvc->f.states & CDCLock)
                         && (chunkFlags & IFAnyPages))
                         skip = 1;
                    if (chunkFlags & IFDataMod)
@@ -681,41 +732,43 @@ afs_GetDownD(int anumber, int *aneedSpace, afs_int32 buckethint)
                    if (!skip && (chunkFlags & IFAnyPages)) {
                        int code;
 
-                       MReleaseWriteLock(&afs_xdcache);
-                       MObtainWriteLock(&tvc->vlock, 543);
-                       if (tvc->multiPage) {
-                           skip = 1;
-                           goto endmultipage;
+                       ReleaseWriteLock(&afs_xdcache);
+                       ObtainWriteLock(&tvc->vlock, 543);
+                       if (!QEmpty(&tvc->multiPage)) {
+                           if (phase < 3 || osi_VM_MultiPageConflict(tvc, tdc)) {
+                               skip = 1;
+                               goto endmultipage;
+                           }
                        }
                        /* block locking pages */
                        tvc->vstates |= VPageCleaning;
                        /* block getting new pages */
                        tvc->activeV++;
-                       MReleaseWriteLock(&tvc->vlock);
+                       ReleaseWriteLock(&tvc->vlock);
                        /* One last recheck */
-                       MObtainWriteLock(&afs_xdcache, 333);
+                       ObtainWriteLock(&afs_xdcache, 333);
                        chunkFlags = afs_indexFlags[tdc->index];
                        if (tdc->refCount > 1 || (chunkFlags & IFDataMod)
-                           || (osi_Active(tvc) && (tvc->states & CDCLock)
+                           || (osi_Active(tvc) && (tvc->f.states & CDCLock)
                                && (chunkFlags & IFAnyPages))) {
                            skip = 1;
-                           MReleaseWriteLock(&afs_xdcache);
+                           ReleaseWriteLock(&afs_xdcache);
                            goto endputpage;
                        }
-                       MReleaseWriteLock(&afs_xdcache);
+                       ReleaseWriteLock(&afs_xdcache);
 
                        code = osi_VM_GetDownD(tvc, tdc);
 
-                       MObtainWriteLock(&afs_xdcache, 269);
+                       ObtainWriteLock(&afs_xdcache, 269);
                        /* we actually removed all pages, clean and dirty */
                        if (code == 0) {
                            afs_indexFlags[tdc->index] &=
                                ~(IFDirtyPages | IFAnyPages);
                        } else
                            skip = 1;
-                       MReleaseWriteLock(&afs_xdcache);
+                       ReleaseWriteLock(&afs_xdcache);
                      endputpage:
-                       MObtainWriteLock(&tvc->vlock, 544);
+                       ObtainWriteLock(&tvc->vlock, 544);
                        if (--tvc->activeV == 0
                            && (tvc->vstates & VRevokeWait)) {
                            tvc->vstates &= ~VRevokeWait;
@@ -727,15 +780,15 @@ afs_GetDownD(int anumber, int *aneedSpace, afs_int32 buckethint)
                            afs_osi_Wakeup((char *)&tvc->vstates);
                        }
                      endmultipage:
-                       MReleaseWriteLock(&tvc->vlock);
+                       ReleaseWriteLock(&tvc->vlock);
                    } else
 #endif /* AFS_SUN5_ENV */
                    {
-                       MReleaseWriteLock(&afs_xdcache);
+                       ReleaseWriteLock(&afs_xdcache);
                    }
 
                    afs_PutVCache(tvc); /*XXX was AFS_FAST_RELE?*/
-                   MObtainWriteLock(&afs_xdcache, 528);
+                   ObtainWriteLock(&afs_xdcache, 528);
                    if (afs_indexFlags[tdc->index] &
                        (IFDataMod | IFDirtyPages | IFAnyPages))
                        skip = 1;
@@ -792,8 +845,9 @@ 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) {
            /* Phase is 0 and no one was found, so try phase 1 (ignore
@@ -811,23 +865,19 @@ 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 */
 
 
-/*
- * Description: remove adc from any hash tables that would allow it to be located
+/*!
+ * Remove adc from any hash tables that would allow it to be located
  * again by afs_FindDCache or afs_GetDCache.
  *
- * Parameters: adc -- pointer to dcache entry to remove from hash tables.
- *            zap -- zap the given dcache ?
+ * \param adc Pointer to dcache entry to remove from hash tables.
+ *
+ * \note Locks: Must have the afs_xdcache lock write-locked to call this function.
  *
- * Locks: Must have the afs_xdcache lock write-locked to call this function.
  */
 int
 afs_HashOutDCache(struct dcache *adc, int zap)
@@ -895,23 +945,18 @@ afs_HashOutDCache(struct dcache *adc, int zap)
     return 0;
 }                              /*afs_HashOutDCache */
 
-/*
- * afs_FlushDCache
- *
- * Description:
- *     Flush the given dcache entry, pulling it from hash chains
- *     and truncating the associated cache file.
+/*!
+ * Flush the given dcache entry, pulling it from hash chains
+ * and truncating the associated cache file.
  *
- * Arguments:
- *     adc: Ptr to dcache entry to flush.
+ * \param adc Ptr to dcache entry to flush.
  *
- * Environment:
+ * \note Environment:
  *     This routine must be called with the afs_xdcache lock held
- *     (in write mode)
+ *     (in write mode).
  */
-
 void
-afs_FlushDCache(register struct dcache *adc)
+afs_FlushDCache(struct dcache *adc)
 {
     AFS_STATCNT(afs_FlushDCache);
     /*
@@ -944,17 +989,15 @@ afs_FlushDCache(register struct dcache *adc)
 }                              /*afs_FlushDCache */
 
 
-/*
- * afs_FreeDCache
- *
- * Description: put a dcache entry on the free dcache entry list.
+/*!
+ * Put a dcache entry on the free dcache entry list.
  *
- * Parameters: adc -- dcache entry to free
+ * \param adc dcache entry to free.
  *
- * Environment: called with afs_xdcache lock write-locked.
+ * \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
@@ -974,32 +1017,28 @@ afs_FreeDCache(register struct dcache *adc)
            afs_osi_Wakeup(&afs_WaitForCacheDrain);
        }
     }
-}
+}                              /* afs_FreeDCache */
 
-/*
- * afs_DiscardDCache
- *
- * Description:
- *     Discard the cache element by moving it to the discardDCList.
- *      This puts the cache element into a quasi-freed state, where
- *      the space may be reused, but the file has not been truncated.
+/*!
+ * Discard the cache element by moving it to the discardDCList.
+ * This puts the cache element into a quasi-freed state, where
+ * the space may be reused, but the file has not been truncated.
  *
- * Major Assumptions Here:
+ * \note Major Assumptions Here:
  *      Assumes that frag size is an integral power of two, less one,
  *      and that this is a two's complement machine.  I don't
  *      know of any filesystems which violate this assumption...
  *
- * Parameters:
- *     adc      : Ptr to dcache entry.
+ * \param adr Ptr to dcache entry.
  *
- * Environment:
+ * \note Environment:
  *     Must be called with afs_xdcache write-locked.
  */
 
 static void
-afs_DiscardDCache(register struct dcache *adc)
+afs_DiscardDCache(struct dcache *adc)
 {
-    register afs_int32 size;
+    afs_int32 size;
 
     AFS_STATCNT(afs_DiscardDCache);
 
@@ -1027,31 +1066,29 @@ afs_DiscardDCache(register struct dcache *adc)
 
 }                              /*afs_DiscardDCache */
 
-/*
- * afs_FreeDiscardedDCache
- *
- * Description:
- *     Free the next element on the list of discarded cache elements.
+/*!
+ * Free the next element on the list of discarded cache elements.
  */
 static void
 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);
 
-    MObtainWriteLock(&afs_xdcache, 510);
+    ObtainWriteLock(&afs_xdcache, 510);
     if (!afs_blocksDiscarded) {
-       MReleaseWriteLock(&afs_xdcache);
+       ReleaseWriteLock(&afs_xdcache);
        return;
     }
 
     /*
      * Get an entry from the list of discarded cache elements
      */
-    tdc = afs_GetDSlot(afs_discardDCList, 0);
+    tdc = afs_GetUnusedDSlot(afs_discardDCList);
+    osi_Assert(tdc);
     osi_Assert(tdc->refCount == 1);
     ReleaseReadLock(&tdc->tlock);
 
@@ -1063,12 +1100,12 @@ afs_FreeDiscardedDCache(void)
     afs_stats_cmperf.cacheBlocksDiscarded = afs_blocksDiscarded;
     /* We can lock because we just took it off the free list */
     ObtainWriteLock(&tdc->lock, 626);
-    MReleaseWriteLock(&afs_xdcache);
+    ReleaseWriteLock(&afs_xdcache);
 
     /*
      * Truncate the element to reclaim its space
      */
-    tfile = afs_CFileOpen(tdc->f.inode);
+    tfile = afs_CFileOpen(&tdc->f.inode);
     afs_CFileTruncate(tfile, 0);
     afs_CFileClose(tfile);
     afs_AdjustSize(tdc, 0);
@@ -1077,24 +1114,20 @@ afs_FreeDiscardedDCache(void)
     /*
      * Free the element we just truncated
      */
-    MObtainWriteLock(&afs_xdcache, 511);
+    ObtainWriteLock(&afs_xdcache, 511);
     afs_indexFlags[tdc->index] &= ~IFDiscarded;
     afs_FreeDCache(tdc);
     tdc->f.states &= ~(DRO|DBackup|DRW);
     ReleaseWriteLock(&tdc->lock);
     afs_PutDCache(tdc);
-    MReleaseWriteLock(&afs_xdcache);
+    ReleaseWriteLock(&afs_xdcache);
 }
 
-/*
- * afs_MaybeFreeDiscardedDCache
- *
- * Description:
- *      Free as many entries from the list of discarded cache elements
- *      as we need to get the free space down below CM_WAITFORDRAINPCT (98%).
+/*!
+ * Free as many entries from the list of discarded cache elements
+ * as we need to get the free space down below CM_WAITFORDRAINPCT (98%).
  *
- * Parameters:
- *      None
+ * \return 0
  */
 int
 afs_MaybeFreeDiscardedDCache(void)
@@ -1110,17 +1143,14 @@ afs_MaybeFreeDiscardedDCache(void)
     return 0;
 }
 
-/*
- * afs_GetDownDSlot
- *
- * Description:
- *     Try to free up a certain number of disk slots.
+/*!
+ * Try to free up a certain number of disk slots.
  *
- * Parameters:
- *     anumber : Targeted number of disk slots to free up.
+ * \param anumber Targeted number of disk slots to free up.
  *
- * Environment:
+ * \note Environment:
  *     Must be called with afs_xdcache write-locked.
+ *
  */
 static void
 afs_GetDownDSlot(int anumber)
@@ -1174,7 +1204,7 @@ afs_GetDownDSlot(int anumber)
                }
 #else
                tdc->dflags &= ~DFEntryMod;
-               afs_WriteDCache(tdc, 1);
+               osi_Assert(afs_WriteDCache(tdc, 1) == 0);
 #endif
            }
 
@@ -1230,7 +1260,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);
@@ -1256,14 +1286,14 @@ afs_PutDCache(register struct dcache *adc)
  *     Both pvnLock and lock are write held.
  */
 void
-afs_TryToSmush(register struct vcache *avc, struct AFS_UCRED *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->m.Length));
+              ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->f.m.Length));
     sync = 1;                  /* XX Temp testing XX */
 
 #if     defined(AFS_SUN5_ENV)
@@ -1278,14 +1308,15 @@ afs_TryToSmush(register struct vcache *avc, struct AFS_UCRED *acred, int sync)
     /*
      * Get the hash chain containing all dce's for this fid
      */
-    i = DVHash(&avc->fid);
-    MObtainWriteLock(&afs_xdcache, 277);
+    i = DVHash(&avc->f.fid);
+    ObtainWriteLock(&afs_xdcache, 277);
     for (index = afs_dvhashTbl[i]; index != NULLIDX; index = i) {
        i = afs_dvnextTbl[index];       /* next pointer this hash table */
-       if (afs_indexUnique[index] == avc->fid.Fid.Unique) {
+       if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
            int releaseTlock = 1;
-           tdc = afs_GetDSlot(index, NULL);
-           if (!FidCmp(&tdc->f.fid, &avc->fid)) {
+           tdc = afs_GetValidDSlot(index);
+           if (!tdc) osi_Panic("afs_TryToSmush tdc");
+           if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
                if (sync) {
                    if ((afs_indexFlags[index] & IFDataMod) == 0
                        && tdc->refCount == 1) {
@@ -1309,9 +1340,9 @@ afs_TryToSmush(register struct vcache *avc, struct AFS_UCRED *acred, int sync)
     }
     ReleaseWriteLock(&avc->vlock);
 #endif
-    MReleaseWriteLock(&afs_xdcache);
+    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
      */
@@ -1324,13 +1355,13 @@ afs_TryToSmush(register struct vcache *avc, struct AFS_UCRED *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.
  */
@@ -1343,9 +1374,9 @@ afs_DCacheMissingChunks(struct vcache *avc)
     afs_uint32 totalChunks = 0;
     struct dcache *tdc;
 
-    totalLength = avc->m.Length;
-    if (avc->truncPos < totalLength)
-        totalLength = avc->truncPos;
+    totalLength = avc->f.m.Length;
+    if (avc->f.truncPos < totalLength)
+        totalLength = avc->f.truncPos;
 
     /* Length is 0, no chunk missing. */
     if (totalLength == 0)
@@ -1358,24 +1389,32 @@ afs_DCacheMissingChunks(struct vcache *avc)
     totalLength--;
     totalChunks = (AFS_CHUNK(totalLength) + 1);
 
+    /* If we're a directory, we only ever have one chunk, regardless of
+     * the size of the dir.
+     */
+    if (avc->f.fid.Fid.Vnode & 1 || vType(avc) == VDIR)
+       totalChunks = 1;
+
     /*
      printf("Should have %d chunks for %u bytes\n",
                totalChunks, (totalLength + 1));
     */
-    i = DVHash(&avc->fid);
-    MObtainWriteLock(&afs_xdcache, 1001);
+    i = DVHash(&avc->f.fid);
+    ObtainWriteLock(&afs_xdcache, 1001);
     for (index = afs_dvhashTbl[i]; index != NULLIDX; index = i) {
         i = afs_dvnextTbl[index];
-        if (afs_indexUnique[index] == avc->fid.Fid.Unique) {
-            tdc = afs_GetDSlot(index, NULL);
-            if (!FidCmp(&tdc->f.fid, &avc->fid)) {
-               totalChunks--;
-            }
-            ReleaseReadLock(&tdc->tlock);
-            afs_PutDCache(tdc);
+        if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
+            tdc = afs_GetValidDSlot(index);
+           if (tdc) {
+               if (!FidCmp(&tdc->f.fid, &avc->f.fid)) {
+                   totalChunks--;
+               }
+               ReleaseReadLock(&tdc->tlock);
+               afs_PutDCache(tdc);
+           }
         }
     }
-    MReleaseWriteLock(&afs_xdcache);
+    ReleaseWriteLock(&afs_xdcache);
 
     /*printf("Missing %d chunks\n", totalChunks);*/
 
@@ -1403,11 +1442,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);
@@ -1416,13 +1455,14 @@ afs_FindDCache(register struct vcache *avc, afs_size_t abyte)
      * Hash on the [fid, chunk] and get the corresponding dcache index
      * after write-locking the dcache.
      */
-    i = DCHash(&avc->fid, chunk);
-    MObtainWriteLock(&afs_xdcache, 278);
+    i = DCHash(&avc->f.fid, chunk);
+    ObtainWriteLock(&afs_xdcache, 278);
     for (index = afs_dchashTbl[i]; index != NULLIDX;) {
-       if (afs_indexUnique[index] == avc->fid.Fid.Unique) {
-           tdc = afs_GetDSlot(index, NULL);
+       if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
+           tdc = afs_GetValidDSlot(index);
+           if (!tdc) osi_Panic("afs_FindDCache tdc");
            ReleaseReadLock(&tdc->tlock);
-           if (!FidCmp(&tdc->f.fid, &avc->fid) && chunk == tdc->f.chunk) {
+           if (!FidCmp(&tdc->f.fid, &avc->f.fid) && chunk == tdc->f.chunk) {
                break;          /* leaving refCount high for caller */
            }
            afs_PutDCache(tdc);
@@ -1432,225 +1472,14 @@ afs_FindDCache(register struct vcache *avc, afs_size_t abyte)
     if (index != NULLIDX) {
        hset(afs_indexTimes[tdc->index], afs_indexCounter);
        hadd32(afs_indexCounter, 1);
-       MReleaseWriteLock(&afs_xdcache);
+       ReleaseWriteLock(&afs_xdcache);
        return tdc;
-    } 
-    MReleaseWriteLock(&afs_xdcache);
+    }
+    ReleaseWriteLock(&afs_xdcache);
     return NULL;
 }                              /*afs_FindDCache */
 
 
-/*
- * afs_UFSCacheStoreProc
- *
- * Description:
- *     Called upon store.
- *
- * Parameters:
- *     acall : Ptr to the Rx call structure involved.
- *     afile : Ptr to the related file descriptor.
- *     alen  : Size of the file in bytes.
- *     avc   : Ptr to the vcache entry.
- *      shouldWake : is it "safe" to return early from close() ?
- *     abytesToXferP  : Set to the number of bytes to xfer.
- *                      NOTE: This parameter is only used if AFS_NOSTATS
- *                             is not defined.
- *     abytesXferredP : Set to the number of bytes actually xferred.
- *                      NOTE: This parameter is only used if AFS_NOSTATS
- *                             is not defined.
- *
- * Environment:
- *     Nothing interesting.
- */
-static int
-afs_UFSCacheStoreProc(register struct rx_call *acall, struct osi_file *afile,
-                     register afs_int32 alen, struct vcache *avc,
-                     int *shouldWake, afs_size_t * abytesToXferP,
-                     afs_size_t * abytesXferredP)
-{
-    afs_int32 code, got;
-    register char *tbuffer;
-    register int tlen;
-
-    AFS_STATCNT(UFS_CacheStoreProc);
-
-#ifndef AFS_NOSTATS
-    /*
-     * In this case, alen is *always* the amount of data we'll be trying
-     * to ship here.
-     */
-    (*abytesToXferP) = alen;
-    (*abytesXferredP) = 0;
-#endif /* AFS_NOSTATS */
-
-    afs_Trace4(afs_iclSetp, CM_TRACE_STOREPROC, ICL_TYPE_POINTER, avc,
-              ICL_TYPE_FID, &(avc->fid), ICL_TYPE_OFFSET,
-              ICL_HANDLE_OFFSET(avc->m.Length), ICL_TYPE_INT32, alen);
-    tbuffer = osi_AllocLargeSpace(AFS_LRALLOCSIZ);
-    while (alen > 0) {
-       tlen = (alen > AFS_LRALLOCSIZ ? AFS_LRALLOCSIZ : alen);
-       got = afs_osi_Read(afile, -1, tbuffer, tlen);
-       if ((got < 0)
-#if defined(KERNEL_HAVE_UERROR)
-           || (got != tlen && getuerror())
-#endif
-           ) {
-           osi_FreeLargeSpace(tbuffer);
-           return EIO;
-       }
-       afs_Trace2(afs_iclSetp, CM_TRACE_STOREPROC2, ICL_TYPE_OFFSET,
-                  ICL_HANDLE_OFFSET(*tbuffer), ICL_TYPE_INT32, got);
-       RX_AFS_GUNLOCK();
-       code = rx_Write(acall, tbuffer, got);   /* writing 0 bytes will
-                                                * push a short packet.  Is that really what we want, just because the
-                                                * data didn't come back from the disk yet?  Let's try it and see. */
-       RX_AFS_GLOCK();
-#ifndef AFS_NOSTATS
-       (*abytesXferredP) += code;
-#endif /* AFS_NOSTATS */
-       if (code != got) {
-           code = rx_Error(acall);
-           osi_FreeLargeSpace(tbuffer);
-           return code ? code : -33;
-       }
-       alen -= got;
-       /*
-        * If file has been locked on server, we can allow the store
-        * to continue.
-        */
-       if (shouldWake && *shouldWake && (rx_GetRemoteStatus(acall) & 1)) {
-           *shouldWake = 0;    /* only do this once */
-           afs_wakeup(avc);
-       }
-    }
-    afs_Trace4(afs_iclSetp, CM_TRACE_STOREPROC, ICL_TYPE_POINTER, avc,
-              ICL_TYPE_FID, &(avc->fid), ICL_TYPE_OFFSET,
-              ICL_HANDLE_OFFSET(avc->m.Length), ICL_TYPE_INT32, alen);
-    osi_FreeLargeSpace(tbuffer);
-    return 0;
-
-}                              /* afs_UFSCacheStoreProc */
-
-
-/*
- * afs_UFSCacheFetchProc
- *
- * Description:
- *     Routine called on fetch; also tells people waiting for data
- *     that more has arrived.
- *
- * Parameters:
- *     acall : Ptr to the Rx call structure.
- *     afile : File descriptor for the cache file.
- *     abase : Base offset to fetch.
- *     adc   : Ptr to the dcache entry for the file, write-locked.
- *      avc   : Ptr to the vcache entry for the file.
- *     abytesToXferP  : Set to the number of bytes to xfer.
- *                      NOTE: This parameter is only used if AFS_NOSTATS
- *                             is not defined.
- *     abytesXferredP : Set to the number of bytes actually xferred.
- *                      NOTE: This parameter is only used if AFS_NOSTATS
- *                             is not defined.
- *
- * Environment:
- *     Nothing interesting.
- */
-
-static int
-afs_UFSCacheFetchProc(register struct rx_call *acall, struct osi_file *afile,
-                     afs_size_t abase, struct dcache *adc,
-                     struct vcache *avc, afs_size_t * abytesToXferP,
-                     afs_size_t * abytesXferredP, afs_int32 lengthFound)
-{
-    afs_int32 length;
-    register afs_int32 code;
-    register char *tbuffer;
-    register int tlen;
-    int moredata = 0;
-
-    AFS_STATCNT(UFS_CacheFetchProc);
-    osi_Assert(WriteLocked(&adc->lock));
-    afile->offset = 0;         /* Each time start from the beginning */
-    length = lengthFound;
-#ifndef AFS_NOSTATS
-    (*abytesToXferP) = 0;
-    (*abytesXferredP) = 0;
-#endif /* AFS_NOSTATS */
-    tbuffer = osi_AllocLargeSpace(AFS_LRALLOCSIZ);
-    adc->validPos = abase;
-    do {
-       if (moredata) {
-           RX_AFS_GUNLOCK();
-           code = rx_Read(acall, (char *)&length, sizeof(afs_int32));
-           RX_AFS_GLOCK();
-           length = ntohl(length);
-           if (code != sizeof(afs_int32)) {
-               osi_FreeLargeSpace(tbuffer);
-               code = rx_Error(acall);
-               return (code ? code : -1);      /* try to return code, not -1 */
-           }
-       }
-       /*
-        * The fetch protocol is extended for the AFS/DFS translator
-        * to allow multiple blocks of data, each with its own length,
-        * to be returned. As long as the top bit is set, there are more
-        * blocks expected.
-        *
-        * We do not do this for AFS file servers because they sometimes
-        * return large negative numbers as the transfer size.
-        */
-       if (avc->states & CForeign) {
-           moredata = length & 0x80000000;
-           length &= ~0x80000000;
-       } else {
-           moredata = 0;
-       }
-#ifndef AFS_NOSTATS
-       (*abytesToXferP) += length;
-#endif /* AFS_NOSTATS */
-       while (length > 0) {
-           tlen = (length > AFS_LRALLOCSIZ ? AFS_LRALLOCSIZ : length);
-#ifdef RX_KERNEL_TRACE
-           afs_Trace1(afs_iclSetp, CM_TRACE_TIMESTAMP, ICL_TYPE_STRING,
-                      "before rx_Read");
-#endif
-           RX_AFS_GUNLOCK();
-           code = rx_Read(acall, tbuffer, tlen);
-           RX_AFS_GLOCK();
-#ifdef RX_KERNEL_TRACE
-           afs_Trace1(afs_iclSetp, CM_TRACE_TIMESTAMP, ICL_TYPE_STRING,
-                      "after rx_Read");
-#endif
-#ifndef AFS_NOSTATS
-           (*abytesXferredP) += code;
-#endif /* AFS_NOSTATS */
-           if (code != tlen) {
-               osi_FreeLargeSpace(tbuffer);
-               afs_Trace3(afs_iclSetp, CM_TRACE_FETCH64READ,
-                          ICL_TYPE_POINTER, avc, ICL_TYPE_INT32, code,
-                          ICL_TYPE_INT32, length);
-               return -34;
-           }
-           code = afs_osi_Write(afile, -1, tbuffer, tlen);
-           if (code != tlen) {
-               osi_FreeLargeSpace(tbuffer);
-               return EIO;
-           }
-           abase += tlen;
-           length -= tlen;
-           adc->validPos = abase;
-           if (afs_osi_Wakeup(&adc->validPos) == 0)
-               afs_Trace4(afs_iclSetp, CM_TRACE_DCACHEWAKE, ICL_TYPE_STRING,
-                          __FILE__, ICL_TYPE_INT32, __LINE__,
-                          ICL_TYPE_POINTER, adc, ICL_TYPE_INT32,
-                          adc->dflags);
-       }
-    } while (moredata);
-    osi_FreeLargeSpace(tbuffer);
-    return 0;
-
-}                              /* afs_UFSCacheFetchProc */
-
 /*!
  * Get a fresh dcache from the free or discarded list.
  *
@@ -1667,10 +1496,9 @@ afs_UFSCacheFetchProc(register struct rx_call *acall, struct osi_file *afile,
  *
  * \return The new dcache.
  */
-struct dcache *afs_AllocDCache(struct vcache *avc,
-                               afs_int32 chunk,
-                               afs_int32 lock,
-                               struct VenusFid *ashFid)
+struct dcache *
+afs_AllocDCache(struct vcache *avc, afs_int32 chunk, afs_int32 lock,
+               struct VenusFid *ashFid)
 {
     struct dcache *tdc = NULL;
     afs_uint32 size = 0;
@@ -1680,7 +1508,8 @@ struct dcache *afs_AllocDCache(struct vcache *avc,
        || ((lock & 2) && afs_freeDCList != NULLIDX)) {
 
        afs_indexFlags[afs_freeDCList] &= ~IFFree;
-       tdc = afs_GetDSlot(afs_freeDCList, 0);
+       tdc = afs_GetUnusedDSlot(afs_freeDCList);
+       osi_Assert(tdc);
        osi_Assert(tdc->refCount == 1);
        ReleaseReadLock(&tdc->tlock);
        ObtainWriteLock(&tdc->lock, 604);
@@ -1688,7 +1517,8 @@ struct dcache *afs_AllocDCache(struct vcache *avc,
        afs_freeDCCount--;
     } else {
        afs_indexFlags[afs_discardDCList] &= ~IFDiscarded;
-       tdc = afs_GetDSlot(afs_discardDCList, 0);
+       tdc = afs_GetUnusedDSlot(afs_discardDCList);
+       osi_Assert(tdc);
        osi_Assert(tdc->refCount == 1);
        ReleaseReadLock(&tdc->tlock);
        ObtainWriteLock(&tdc->lock, 605);
@@ -1703,7 +1533,7 @@ struct dcache *afs_AllocDCache(struct vcache *avc,
        afs_stats_cmperf.cacheBlocksDiscarded = afs_blocksDiscarded;
        if (lock & 2) {
            /* Truncate the chunk so zeroes get filled properly */
-           file = afs_CFileOpen(tdc->f.inode);
+           file = afs_CFileOpen(&tdc->f.inode);
            afs_CFileTruncate(file, 0);
            afs_CFileClose(file);
            afs_AdjustSize(tdc, 0);
@@ -1727,10 +1557,10 @@ struct dcache *afs_AllocDCache(struct vcache *avc,
        tdc->f.fid = *ashFid;
     else
        /* Use normal vcache's fid otherwise. */
-       tdc->f.fid = avc->fid;
-    if (avc->states & CRO)
+       tdc->f.fid = avc->f.fid;
+    if (avc->f.states & CRO)
        tdc->f.states = DRO;
-    else if (avc->states & CBackup)
+    else if (avc->f.states & CBackup)
        tdc->f.states = DBackup;
     else
        tdc->f.states = DRW;
@@ -1777,12 +1607,6 @@ struct dcache *afs_AllocDCache(struct vcache *avc,
  *     The vcache entry pointed to by avc is unlocked upon entry.
  */
 
-struct tlocal1 {
-    struct AFSVolSync tsync;
-    struct AFSFetchStatus OutStatus;
-    struct AFSCallBack CallBack;
-};
-
 /*
  * Update the vnode-to-dcache hint if we can get the vnode lock
  * right away.  Assumes dcache entry is at least read-locked.
@@ -1791,7 +1615,7 @@ void
 updateV2DC(int lockVc, struct vcache *v, struct dcache *d, int src)
 {
     if (!lockVc || 0 == NBObtainWriteLock(&v->lock, src)) {
-       if (hsame(v->m.DataVersion, d->f.versionNo) && v->callback)
+       if (hsame(v->f.m.DataVersion, d->f.versionNo) && v->callback)
            v->dchint = d;
        if (lockVc)
            ReleaseWriteLock(&v->lock);
@@ -1800,30 +1624,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, code1 = 0, 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 */
-    struct rx_call *tcall;
     afs_size_t Position = 0;
-#ifdef AFS_64BIT_CLIENT
-    afs_size_t tsize;
-    afs_size_t lengthFound;    /* as returned from server */
-#endif /* AFS_64BIT_CLIENT */
     afs_int32 size, tlen;      /* size of segment to transfer */
-    struct tlocal1 *tsmall = 0;
-    register struct dcache *tdc;
-    register struct osi_file *file;
-    register struct conn *tc;
+    struct afs_FetchOutput *tsmall = 0;
+    struct dcache *tdc;
+    struct osi_file *file;
+    struct afs_conn *tc;
     int downDCount = 0;
     struct server *newCallback = NULL;
     char setNewCallback;
@@ -1833,21 +1651,15 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
     int doAdjustSize = 0;
     int doReallyAdjustSize = 0;
     int overWriteWholeChunk = 0;
+    struct rx_connection *rxconn;
 
-    XSTATS_DECLS;
 #ifndef AFS_NOSTATS
-    struct afs_stats_xferData *xferP;  /* Ptr to this op's xfer struct */
-    osi_timeval_t xferStartTime,       /*FS xfer start time */
-      xferStopTime;            /*FS xfer stop time */
-    afs_size_t bytesToXfer;    /* # bytes to xfer */
-    afs_size_t bytesXferred;   /* # bytes actually xferred */
     struct afs_stats_AccessInfo *accP; /*Ptr to access record in stats */
     int fromReplica;           /*Are we reading from a replica? */
     int numFetchLoops;         /*# times around the fetch/analyze loop */
 #endif /* AFS_NOSTATS */
 
     AFS_STATCNT(afs_GetDCache);
-
     if (dcacheDisabled)
        return NULL;
 
@@ -1857,7 +1669,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
      * Determine the chunk number and offset within the chunk corresponding
      * to the desired byte.
      */
-    if (avc->fid.Fid.Vnode & 1) {      /* if (vType(avc) == VDIR) */
+    if (avc->f.fid.Fid.Vnode & 1) {    /* if (vType(avc) == VDIR) */
        chunk = 0;
     } else {
        chunk = AFS_CHUNK(abyte);
@@ -1894,13 +1706,13 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
         * entries from the free list, and thereby assuming them to be not
         * referenced and not locked.
         */
-       MObtainReadLock(&afs_xdcache);
+       ObtainReadLock(&afs_xdcache);
        dcLocked = (0 == NBObtainSharedLock(&tdc->lock, 601));
 
        if (dcLocked && (tdc->index != NULLIDX)
-           && !FidCmp(&tdc->f.fid, &avc->fid) && chunk == tdc->f.chunk
+           && !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:
@@ -1910,17 +1722,17 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
            tdc->refCount++;
            ReleaseWriteLock(&tdc->tlock);
 
-           MReleaseReadLock(&afs_xdcache);
+           ReleaseReadLock(&afs_xdcache);
            shortcut = 1;
 
-           if (hsame(tdc->f.versionNo, avc->m.DataVersion)
+           if (hsame(tdc->f.versionNo, avc->f.m.DataVersion)
                && !(tdc->dflags & DFFetching)) {
 
                afs_stats_cmperf.dcacheHits++;
-               MObtainWriteLock(&afs_xdcache, 559);
+               ObtainWriteLock(&afs_xdcache, 559);
                QRemove(&tdc->lruq);
                QAdd(&afs_DLRU, &tdc->lruq);
-               MReleaseWriteLock(&afs_xdcache);
+               ReleaseWriteLock(&afs_xdcache);
 
                /* Locks held:
                 * avc->lock(R) if setLocks && !slowPass
@@ -1932,7 +1744,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
        } else {
            if (dcLocked)
                ReleaseSharedLock(&tdc->lock);
-           MReleaseReadLock(&afs_xdcache);
+           ReleaseReadLock(&afs_xdcache);
        }
 
        if (!shortcut)
@@ -1957,15 +1769,19 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
         * avc->lock(W) if !setLocks || slowPass
         */
 
-       i = DCHash(&avc->fid, chunk);
+       i = DCHash(&avc->f.fid, chunk);
        /* check to make sure our space is fine */
        afs_MaybeWakeupTruncateDaemon();
 
-       MObtainWriteLock(&afs_xdcache, 280);
+       ObtainWriteLock(&afs_xdcache, 280);
        us = NULLIDX;
        for (index = afs_dchashTbl[i]; index != NULLIDX;) {
-           if (afs_indexUnique[index] == avc->fid.Fid.Unique) {
-               tdc = afs_GetDSlot(index, NULL);
+           if (afs_indexUnique[index] == avc->f.fid.Fid.Unique) {
+               tdc = afs_GetValidDSlot(index);
+               if (!tdc) {
+                   ReleaseWriteLock(&afs_xdcache);
+                   goto done;
+               }
                ReleaseReadLock(&tdc->tlock);
                /*
                 * Locks held:
@@ -1973,14 +1789,14 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
                 * avc->lock(W) if !setLocks || slowPass
                 * afs_xdcache(W)
                 */
-               if (!FidCmp(&tdc->f.fid, &avc->fid) && chunk == tdc->f.chunk) {
+               if (!FidCmp(&tdc->f.fid, &avc->f.fid) && chunk == tdc->f.chunk) {
                    /* Move it up in the beginning of the list */
                    if (afs_dchashTbl[i] != index) {
                        afs_dcnextTbl[us] = afs_dcnextTbl[index];
                        afs_dcnextTbl[index] = afs_dchashTbl[i];
                        afs_dchashTbl[i] = index;
                    }
-                   MReleaseWriteLock(&afs_xdcache);
+                   ReleaseWriteLock(&afs_xdcache);
                    ObtainSharedLock(&tdc->lock, 606);
                    break;      /* leaving refCount high for caller */
                }
@@ -2008,18 +1824,19 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
            if (afs_discardDCList == NULLIDX && afs_freeDCList == NULLIDX) {
                while (1) {
                    if (!setLocks)
-                       avc->states |= CDCLock;
+                       avc->f.states |= CDCLock;
                    /* just need slots */
                    afs_GetDownD(5, (int *)0, afs_DCGetBucket(avc));
                    if (!setLocks)
-                       avc->states &= ~CDCLock;
+                       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)
+                   if (++downDCount > 300) {
                        osi_Panic("getdcache");
-                   MReleaseWriteLock(&afs_xdcache);
+                    }
+                   ReleaseWriteLock(&afs_xdcache);
                    /*
                     * Locks held:
                     * avc->lock(R) if setLocks
@@ -2038,13 +1855,13 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
             */
            afs_dcnextTbl[tdc->index] = afs_dchashTbl[i];
            afs_dchashTbl[i] = tdc->index;
-           i = DVHash(&avc->fid);
+           i = DVHash(&avc->f.fid);
            afs_dvnextTbl[tdc->index] = afs_dvhashTbl[i];
            afs_dvhashTbl[i] = tdc->index;
            tdc->dflags = DFEntryMod;
            tdc->mflags = 0;
            afs_MaybeWakeupTruncateDaemon();
-           MReleaseWriteLock(&afs_xdcache);
+           ReleaseWriteLock(&afs_xdcache);
            ConvertWToSLock(&tdc->lock);
        }
     }
@@ -2060,7 +1877,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
     afs_Trace4(afs_iclSetp, CM_TRACE_GETDCACHE2, ICL_TYPE_POINTER, avc,
               ICL_TYPE_POINTER, tdc, ICL_TYPE_INT32,
               hgetlo(tdc->f.versionNo), ICL_TYPE_INT32,
-              hgetlo(avc->m.DataVersion));
+              hgetlo(avc->f.m.DataVersion));
     /*
      * Here we have the entry in tdc, with its refCount incremented.
      * Note: we don't use the S-lock on avc; it costs concurrency when
@@ -2084,9 +1901,9 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
               ICL_TYPE_INT32, aflags, ICL_TYPE_OFFSET,
               ICL_HANDLE_OFFSET(abyte), ICL_TYPE_OFFSET,
               ICL_HANDLE_OFFSET(Position));
-    if ((aflags & 4) && (hiszero(avc->m.DataVersion)))
+    if ((aflags & 4) && (hiszero(avc->f.m.DataVersion)))
        doAdjustSize = 1;
-    if ((AFS_CHUNKTOBASE(chunk) >= avc->m.Length) ||
+    if ((AFS_CHUNKTOBASE(chunk) >= avc->f.m.Length) ||
         ((aflags & 4) && (abyte == Position) && (tlen >= size)))
        overWriteWholeChunk = 1;
     if (doAdjustSize || overWriteWholeChunk) {
@@ -2103,26 +1920,25 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
        if (doAdjustSize)
            adjustsize = 4096;
 #endif /* AFS_SGI_ENV */
-       if (AFS_CHUNKTOBASE(chunk) + adjustsize >= avc->m.Length &&
+       if (AFS_CHUNKTOBASE(chunk) + adjustsize >= avc->f.m.Length &&
 #else /* defined(AFS_AIX32_ENV) || defined(AFS_SGI_ENV) */
-#if    defined(AFS_SUN5_ENV)  || defined(AFS_OSF_ENV)
-       if ((doAdjustSize || (AFS_CHUNKTOBASE(chunk) >= avc->m.Length)) &&
+#if    defined(AFS_SUN5_ENV)
+       if ((doAdjustSize || (AFS_CHUNKTOBASE(chunk) >= avc->f.m.Length)) &&
 #else
-       if (AFS_CHUNKTOBASE(chunk) >= avc->m.Length &&
+       if (AFS_CHUNKTOBASE(chunk) >= avc->f.m.Length &&
 #endif
 #endif /* defined(AFS_AIX32_ENV) || defined(AFS_SGI_ENV) */
-           !hsame(avc->m.DataVersion, tdc->f.versionNo))
+           !hsame(avc->f.m.DataVersion, tdc->f.versionNo))
            doReallyAdjustSize = 1;
 
        if (doReallyAdjustSize || overWriteWholeChunk) {
            /* no data in file to read at this position */
            UpgradeSToWLock(&tdc->lock, 607);
-
-           file = afs_CFileOpen(tdc->f.inode);
+           file = afs_CFileOpen(&tdc->f.inode);
            afs_CFileTruncate(file, 0);
            afs_CFileClose(file);
            afs_AdjustSize(tdc, 0);
-           hset(tdc->f.versionNo, avc->m.DataVersion);
+           hset(tdc->f.versionNo, avc->f.m.DataVersion);
            tdc->dflags |= DFEntryMod;
 
            ConvertWToSLock(&tdc->lock);
@@ -2175,7 +1991,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
      * avc->lock(W) if !setLocks || slowPass
      * tdc->lock(S)
      */
-    if (!hsame(avc->m.DataVersion, tdc->f.versionNo) && !overWriteWholeChunk) {
+    if (!hsame(avc->f.m.DataVersion, tdc->f.versionNo) && !overWriteWholeChunk) {
        /*
         * Version number mismatch.
         */
@@ -2212,7 +2028,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
         * flush.  Clearly, at least, we don't have to flush the file more
         * often than it changes
         */
-       if (hcmp(avc->flushDV, avc->m.DataVersion) < 0) {
+       if (hcmp(avc->flushDV, avc->f.m.DataVersion) < 0) {
            /*
             * By here, the cache entry is always write-locked.  We can
             * deadlock if we call osi_Flush with the cache entry locked...
@@ -2245,7 +2061,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
         */
 
        /* Watch for standard race condition around osi_FlushText */
-       if (hsame(avc->m.DataVersion, tdc->f.versionNo)) {
+       if (hsame(avc->f.m.DataVersion, tdc->f.versionNo)) {
            updateV2DC(setLocks, avc, tdc, 569);        /* set hint */
            afs_stats_cmperf.dcacheHits++;
            ConvertWToSLock(&tdc->lock);
@@ -2273,29 +2089,58 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
            goto RetryGetDCache;
        }
 
-       /* Do not fetch data beyond truncPos. */
-       maxGoodLength = avc->m.Length;
-       if (avc->truncPos < maxGoodLength)
-           maxGoodLength = avc->truncPos;
        Position = AFS_CHUNKBASE(abyte);
        if (vType(avc) == VDIR) {
-           size = avc->m.Length;
+           size = avc->f.m.Length;
            if (size > tdc->f.chunkBytes) {
                /* pre-reserve space for file */
                afs_AdjustSize(tdc, size);
            }
            size = 999999999;   /* max size for transfer */
        } else {
+           afs_size_t maxGoodLength;
+
+           /* estimate how much data we're expecting back from the server,
+            * and reserve space in the dcache entry for it */
+
+           maxGoodLength = avc->f.m.Length;
+           if (avc->f.truncPos < maxGoodLength)
+               maxGoodLength = avc->f.truncPos;
+
            size = AFS_CHUNKSIZE(abyte);        /* expected max size */
-           /* don't read past end of good data on server */
            if (Position + size > maxGoodLength)
                size = maxGoodLength - Position;
            if (size < 0)
                size = 0;       /* Handle random races */
            if (size > tdc->f.chunkBytes) {
-               /* pre-reserve space for file */
+               /* pre-reserve estimated space for file */
                afs_AdjustSize(tdc, size);      /* changes chunkBytes */
-               /* max size for transfer still in size */
+           }
+
+           if (size) {
+               /* For the actual fetch, do not limit the request to the
+                * length of the file. If this results in a read past EOF on
+                * the server, the server will just reply with less data than
+                * requested. If we limit ourselves to only requesting data up
+                * to the avc file length, we open ourselves up to races if the
+                * file is extended on the server at about the same time.
+                *
+                * However, we must restrict ourselves to the avc->f.truncPos
+                * length, since this represents an outstanding local
+                * truncation of the file that will be committed to the
+                * fileserver when we actually write the fileserver contents.
+                * If we do not restrict the fetch length based on
+                * avc->f.truncPos, a different truncate operation extending
+                * the file length could cause the old data after
+                * avc->f.truncPos to reappear, instead of extending the file
+                * with NUL bytes. */
+               size = AFS_CHUNKSIZE(abyte);
+               if (Position + size > avc->f.truncPos) {
+                   size = avc->f.truncPos - Position;
+               }
+               if (size < 0) {
+                   size = 0;
+               }
            }
        }
        if (afs_mariner && !tdc->f.chunk)
@@ -2305,8 +2150,8 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
         * fetch the whole file.
         */
        DZap(tdc);      /* pages in cache may be old */
-       file = afs_CFileOpen(tdc->f.inode);
-       afs_RemoveVCB(&avc->fid);
+       file = afs_CFileOpen(&tdc->f.inode);
+       afs_RemoveVCB(&avc->f.fid);
        tdc->f.states |= DWriting;
        tdc->dflags |= DFFetching;
        tdc->validPos = Position;       /*  which is AFS_CHUNKBASE(abyte) */
@@ -2318,15 +2163,14 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
                           ICL_TYPE_POINTER, tdc, ICL_TYPE_INT32,
                           tdc->dflags);
        }
-       tsmall =
-           (struct tlocal1 *)osi_AllocLargeSpace(sizeof(struct tlocal1));
+       tsmall = osi_AllocLargeSpace(sizeof(struct afs_FetchOutput));
        setVcacheStatus = 0;
 #ifndef AFS_NOSTATS
        /*
         * Remember if we are doing the reading from a replicated volume,
         * and how many times we've zipped around the fetch/analyze loop.
         */
-       fromReplica = (avc->states & CRO) ? 1 : 0;
+       fromReplica = (avc->f.states & CRO) ? 1 : 0;
        numFetchLoops = 0;
        accP = &(afs_stats_cmfullperf.accessinf);
        if (fromReplica)
@@ -2336,7 +2180,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
 #endif /* AFS_NOSTATS */
        /* this is a cache miss */
        afs_Trace4(afs_iclSetp, CM_TRACE_FETCHPROC, ICL_TYPE_POINTER, avc,
-                  ICL_TYPE_FID, &(avc->fid), ICL_TYPE_OFFSET,
+                  ICL_TYPE_FID, &(avc->f.fid), ICL_TYPE_OFFSET,
                   ICL_HANDLE_OFFSET(Position), ICL_TYPE_INT32, size);
 
        if (size)
@@ -2401,9 +2245,8 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
                 * tdc->lock(W)
                 */
 
-               tc = afs_Conn(&avc->fid, areq, SHARED_LOCK);
+               tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK, &rxconn);
                if (tc) {
-                   afs_int32 length_hi, length, bytes;
 #ifndef AFS_NOSTATS
                    numFetchLoops++;
                    if (fromReplica)
@@ -2411,211 +2254,24 @@ 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();
-                   RX_AFS_GUNLOCK();
-                   tcall = rx_NewCall(tc->id);
-                   RX_AFS_GLOCK();
-
-                   XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHDATA);
-#ifdef AFS_64BIT_CLIENT
-                   length_hi = code = 0;
-                   if (!afs_serverHasNo64Bit(tc)) {
-                       tsize = size;
-                       RX_AFS_GUNLOCK();
-                       code =
-                           StartRXAFS_FetchData64(tcall,
-                                                  (struct AFSFid *)&avc->fid.
-                                                  Fid, Position, tsize);
-                       if (code != 0) {
-                           RX_AFS_GLOCK();
-                           afs_Trace2(afs_iclSetp, CM_TRACE_FETCH64CODE,
-                                      ICL_TYPE_POINTER, avc, ICL_TYPE_INT32,
-                                      code);
-                       } else {
-                           bytes =
-                               rx_Read(tcall, (char *)&length_hi,
-                                       sizeof(afs_int32));
-                           RX_AFS_GLOCK();
-                           if (bytes == sizeof(afs_int32)) {
-                               length_hi = ntohl(length_hi);
-                           } else {
-                               length_hi = 0;
-                               code = rx_Error(tcall);
-                               RX_AFS_GUNLOCK();
-                               code1 = rx_EndCall(tcall, code);
-                               RX_AFS_GLOCK();
-                               tcall = (struct rx_call *)0;
-                           }
-                       }
-                   }
-                   if (code == RXGEN_OPCODE || afs_serverHasNo64Bit(tc)) {
-                       if (Position > 0x7FFFFFFF) {
-                           code = EFBIG;
-                       } else {
-                           afs_int32 pos;
-                           pos = Position;
-                           RX_AFS_GUNLOCK();
-                           if (!tcall)
-                               tcall = rx_NewCall(tc->id);
-                           code =
-                               StartRXAFS_FetchData(tcall, (struct AFSFid *)
-                                                    &avc->fid.Fid, pos,
-                                                    size);
-                           RX_AFS_GLOCK();
-                       }
-                       afs_serverSetNo64Bit(tc);
-                   }
-                   if (code == 0) {
-                       RX_AFS_GUNLOCK();
-                       bytes =
-                           rx_Read(tcall, (char *)&length,
-                                   sizeof(afs_int32));
-                       RX_AFS_GLOCK();
-                       if (bytes == sizeof(afs_int32)) {
-                           length = ntohl(length);
-                       } else {
-                           code = rx_Error(tcall);
-                       }
-                   }
-                   FillInt64(lengthFound, length_hi, length);
-                   afs_Trace3(afs_iclSetp, CM_TRACE_FETCH64LENG,
-                              ICL_TYPE_POINTER, avc, ICL_TYPE_INT32, code,
-                              ICL_TYPE_OFFSET,
-                              ICL_HANDLE_OFFSET(lengthFound));
-#else /* AFS_64BIT_CLIENT */
-                   RX_AFS_GUNLOCK();
-                   code =
-                       StartRXAFS_FetchData(tcall,
-                                            (struct AFSFid *)&avc->fid.Fid,
-                                            Position, size);
-                   RX_AFS_GLOCK();
-                   if (code == 0) {
-                       RX_AFS_GUNLOCK();
-                       bytes =
-                           rx_Read(tcall, (char *)&length,
-                                   sizeof(afs_int32));
-                       RX_AFS_GLOCK();
-                       if (bytes == sizeof(afs_int32)) {
-                           length = ntohl(length);
-                       } else {
-                           code = rx_Error(tcall);
-                       }
-                   }
-#endif /* AFS_64BIT_CLIENT */
-                   if (code == 0) {
-
-#ifndef AFS_NOSTATS
-                       xferP =
-                           &(afs_stats_cmfullperf.rpc.
-                             fsXferTimes[AFS_STATS_FS_XFERIDX_FETCHDATA]);
-                       osi_GetuTime(&xferStartTime);
-
-                       code =
-                           afs_CacheFetchProc(tcall, file,
-                                              (afs_size_t) Position, tdc,
-                                              avc, &bytesToXfer,
-                                              &bytesXferred, length);
-
-                       osi_GetuTime(&xferStopTime);
-                       (xferP->numXfers)++;
-                       if (!code) {
-                           (xferP->numSuccesses)++;
-                           afs_stats_XferSumBytes
-                               [AFS_STATS_FS_XFERIDX_FETCHDATA] +=
-                               bytesXferred;
-                           (xferP->sumBytes) +=
-                               (afs_stats_XferSumBytes
-                                [AFS_STATS_FS_XFERIDX_FETCHDATA] >> 10);
-                           afs_stats_XferSumBytes
-                               [AFS_STATS_FS_XFERIDX_FETCHDATA] &= 0x3FF;
-                           if (bytesXferred < xferP->minBytes)
-                               xferP->minBytes = bytesXferred;
-                           if (bytesXferred > xferP->maxBytes)
-                               xferP->maxBytes = bytesXferred;
-
-                           /*
-                            * Tally the size of the object.  Note: we tally the actual size,
-                            * NOT the number of bytes that made it out over the wire.
-                            */
-                           if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET0)
-                               (xferP->count[0])++;
-                           else if (bytesToXfer <=
-                                    AFS_STATS_MAXBYTES_BUCKET1)
-                               (xferP->count[1])++;
-                           else if (bytesToXfer <=
-                                    AFS_STATS_MAXBYTES_BUCKET2)
-                               (xferP->count[2])++;
-                           else if (bytesToXfer <=
-                                    AFS_STATS_MAXBYTES_BUCKET3)
-                               (xferP->count[3])++;
-                           else if (bytesToXfer <=
-                                    AFS_STATS_MAXBYTES_BUCKET4)
-                               (xferP->count[4])++;
-                           else if (bytesToXfer <=
-                                    AFS_STATS_MAXBYTES_BUCKET5)
-                               (xferP->count[5])++;
-                           else if (bytesToXfer <=
-                                    AFS_STATS_MAXBYTES_BUCKET6)
-                               (xferP->count[6])++;
-                           else if (bytesToXfer <=
-                                    AFS_STATS_MAXBYTES_BUCKET7)
-                               (xferP->count[7])++;
-                           else
-                               (xferP->count[8])++;
-
-                           afs_stats_GetDiff(elapsedTime, xferStartTime,
-                                             xferStopTime);
-                           afs_stats_AddTo((xferP->sumTime), elapsedTime);
-                           afs_stats_SquareAddTo((xferP->sqrTime),
-                                                 elapsedTime);
-                           if (afs_stats_TimeLessThan
-                               (elapsedTime, (xferP->minTime))) {
-                               afs_stats_TimeAssign((xferP->minTime),
-                                                    elapsedTime);
-                           }
-                           if (afs_stats_TimeGreaterThan
-                               (elapsedTime, (xferP->maxTime))) {
-                               afs_stats_TimeAssign((xferP->maxTime),
-                                                    elapsedTime);
-                           }
-                       }
-#else
-                       code =
-                           afs_CacheFetchProc(tcall, file, Position, tdc,
-                                              avc, 0, 0, length);
-#endif /* AFS_NOSTATS */
-                   }
-                   if (code == 0) {
-                       RX_AFS_GUNLOCK();
-                       code =
-                           EndRXAFS_FetchData(tcall, &tsmall->OutStatus,
-                                              &tsmall->CallBack,
-                                              &tsmall->tsync);
-                       RX_AFS_GLOCK();
-                   }
-                   XSTATS_END_TIME;
-                   RX_AFS_GUNLOCK();
-                   if (tcall)
-                       code1 = rx_EndCall(tcall, code);
-                   RX_AFS_GLOCK();
-               } else {
-                   code = -1;
-               }
-               if (!code && code1)
-                   code = code1;
+                   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
                     */
                    size = tdc->validPos - Position;    /* actual segment size */
                    if (size < 0)
@@ -2625,10 +2281,10 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
                    if (!setLocks || slowPass) {
                        ObtainWriteLock(&afs_xcbhash, 453);
                        afs_DequeueCallback(avc);
-                       avc->states &= ~(CStatd | CUnique);
+                       avc->f.states &= ~(CStatd | CUnique);
                        avc->callback = NULL;
                        ReleaseWriteLock(&afs_xcbhash);
-                       if (avc->fid.Fid.Vnode & 1 || (vType(avc) == VDIR))
+                       if (avc->f.fid.Fid.Vnode & 1 || (vType(avc) == VDIR))
                            osi_dnlc_purgedp(avc);
                    } else {
                        /* Something lost.  Forget about performance, and go
@@ -2649,7 +2305,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
                }
 
            } while (afs_Analyze
-                    (tc, code, &avc->fid, areq,
+                    (tc, rxconn, code, &avc->f.fid, areq,
                      AFS_STATS_FS_RPCIDX_FETCHDATA, SHARED_LOCK, NULL));
 
        /*
@@ -2699,9 +2355,9 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
            if (!afs_IsDynroot(avc)) {
                ObtainWriteLock(&afs_xcbhash, 454);
                afs_DequeueCallback(avc);
-               avc->states &= ~(CStatd | CUnique);
+               avc->f.states &= ~(CStatd | CUnique);
                ReleaseWriteLock(&afs_xcbhash);
-               if (avc->fid.Fid.Vnode & 1 || (vType(avc) == VDIR))
+               if (avc->f.fid.Fid.Vnode & 1 || (vType(avc) == VDIR))
                    osi_dnlc_purgedp(avc);
                /*
                 * Locks held:
@@ -2709,8 +2365,6 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
                 */
                osi_Assert(!setLocks || slowPass);
            }
-           tdc->f.states &= ~(DRO|DBackup|DRW);
-           afs_DCMoveBucket(tdc, 0, 0);
            tdc = NULL;
            goto done;
        }
@@ -2752,7 +2406,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
     /*
      * See if this was a reference to a file in the local cell.
      */
-    if (afs_IsPrimaryCellNum(avc->fid.Cell))
+    if (afs_IsPrimaryCellNum(avc->f.fid.Cell))
        afs_stats_cmperf.dlocalAccesses++;
     else
        afs_stats_cmperf.dremoteAccesses++;
@@ -2760,10 +2414,10 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
     /* Fix up LRU info */
 
     if (tdc) {
-       MObtainWriteLock(&afs_xdcache, 602);
+       ObtainWriteLock(&afs_xdcache, 602);
        hset(afs_indexTimes[tdc->index], afs_indexCounter);
        hadd32(afs_indexCounter, 1);
-       MReleaseWriteLock(&afs_xdcache);
+       ReleaseWriteLock(&afs_xdcache);
 
        /* return the data */
        if (vType(avc) == VDIR)
@@ -2796,7 +2450,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
         */
        afs_hyper_t currentDV, statusDV;
 
-       hset(currentDV, avc->m.DataVersion);
+       hset(currentDV, avc->f.m.DataVersion);
 
        if (setNewCallback && avc->callback != newCallback)
            doVcacheUpdate = 1;
@@ -2805,7 +2459,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
            hset64(statusDV, tsmall->OutStatus.dataVersionHigh,
                   tsmall->OutStatus.DataVersion);
 
-           if (setVcacheStatus && avc->m.Length != tsmall->OutStatus.Length)
+           if (setVcacheStatus && avc->f.m.Length != tsmall->OutStatus.Length)
                doVcacheUpdate = 1;
            if (setVcacheStatus && !hsame(currentDV, statusDV))
                doVcacheUpdate = 1;
@@ -2815,7 +2469,7 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
 
        if (doVcacheUpdate) {
            ObtainWriteLock(&avc->lock, 615);
-           if (!hsame(avc->m.DataVersion, currentDV)) {
+           if (!hsame(avc->f.m.DataVersion, currentDV)) {
                /* We lose.  Someone will beat us to it. */
                doVcacheUpdate = 0;
                ReleaseWriteLock(&avc->lock);
@@ -2861,8 +2515,8 @@ afs_GetDCache(register struct vcache *avc, afs_size_t abyte,
 void
 afs_WriteThroughDSlots(void)
 {
-    register struct dcache *tdc;
-    register afs_int32 i, touchedit = 0;
+    struct dcache *tdc;
+    afs_int32 i, touchedit = 0;
 
     struct afs_q DirtyQ, *tq;
 
@@ -2873,7 +2527,7 @@ afs_WriteThroughDSlots(void)
      * holding afs_xdcache.  So we enter xdcache, get a reference
      * for every dcache entry, and exit xdcache.
      */
-    MObtainWriteLock(&afs_xdcache, 283);
+    ObtainWriteLock(&afs_xdcache, 283);
     QInit(&DirtyQ);
     for (i = 0; i < afs_cacheFiles; i++) {
        tdc = afs_indexTable[i];
@@ -2887,7 +2541,7 @@ afs_WriteThroughDSlots(void)
            QAdd(&DirtyQ, &tdc->dirty);
        }
     }
-    MReleaseWriteLock(&afs_xdcache);
+    ReleaseWriteLock(&afs_xdcache);
 
     /*
      * Now, for each dcache entry we found, check if it's dirty.
@@ -2908,9 +2562,9 @@ afs_WriteThroughDSlots(void)
            /* Now that we have the write lock, double-check */
            if (wrLock && (tdc->dflags & DFEntryMod)) {
                tdc->dflags &= ~DFEntryMod;
-               MObtainWriteLock(&afs_xdcache, 620);
-               afs_WriteDCache(tdc, 1);
-               MReleaseWriteLock(&afs_xdcache);
+               ObtainWriteLock(&afs_xdcache, 620);
+               osi_Assert(afs_WriteDCache(tdc, 1) == 0);
+               ReleaseWriteLock(&afs_xdcache);
                touchedit = 1;
            }
            if (wrLock)
@@ -2920,7 +2574,7 @@ afs_WriteThroughDSlots(void)
        afs_PutDCache(tdc);
     }
 
-    MObtainWriteLock(&afs_xdcache, 617);
+    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
         * up-to-date to avoid losing cached files on cold starts because
@@ -2932,9 +2586,10 @@ afs_WriteThroughDSlots(void)
        theader.firstCSize = AFS_FIRSTCSIZE;
        theader.otherCSize = AFS_OTHERCSIZE;
        theader.version = AFS_CI_VERSION;
+       theader.dataSize = sizeof(struct fcache);
        afs_osi_Write(afs_cacheInodep, 0, &theader, sizeof(theader));
     }
-    MReleaseWriteLock(&afs_xdcache);
+    ReleaseWriteLock(&afs_xdcache);
 }
 
 /*
@@ -2946,16 +2601,16 @@ afs_WriteThroughDSlots(void)
  *
  * Parameters:
  *     aslot : Dcache slot to look at.
- *     tmpdc : Ptr to dcache entry.
+ *      needvalid : Whether the specified slot should already exist
  *
  * 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, int indexvalid, int datavalid)
 {
-    register struct dcache *tdc;
+    struct dcache *tdc;
     int existing = 0;
 
     AFS_STATCNT(afs_MemGetDSlot);
@@ -2973,37 +2628,39 @@ 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 'indexvalid' is true, the slot must already exist and be populated
+     * somewhere. for memcache, the only place that dcache entries exist is
+     * in memory, so if we did not find it above, something is very wrong. */
+    osi_Assert(!indexvalid);
+
+    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;
     tdc->f.fid.Fid.Volume = 0;
     tdc->f.chunk = -1;
     hones(tdc->f.versionNo);
-    tdc->f.inode = aslot;
+    tdc->f.inode.mem = aslot;
     tdc->dflags |= DFEntryMod;
     tdc->refCount = 1;
     tdc->index = aslot;
@@ -3015,13 +2672,12 @@ afs_MemGetDSlot(register afs_int32 aslot, register struct dcache *tmpdc)
        osi_Assert(0 == NBObtainWriteLock(&tdc->tlock, 676));
     }
 
-    RWLOCK_INIT(&tdc->lock, "dcache lock");
-    RWLOCK_INIT(&tdc->tlock, "dcache tlock");
-    RWLOCK_INIT(&tdc->mflock, "dcache flock");
+    AFS_RWLOCK_INIT(&tdc->lock, "dcache lock");
+    AFS_RWLOCK_INIT(&tdc->tlock, "dcache tlock");
+    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 */
@@ -3037,18 +2693,26 @@ unsigned int last_error = 0, lasterrtime = 0;
  *
  * Parameters:
  *     aslot : Dcache slot to look at.
- *     tmpdc : Ptr to dcache entry.
+ *      indexvalid : 1 if we know the slot we're giving is valid, and thus
+ *                   reading the dcache from the disk index should succeed. 0
+ *                   if we are initializing a new dcache, and so reading from
+ *                   the disk index may fail.
+ *      datavalid : 0 if we are loading a dcache entry from the free or
+ *                  discard list, so we know the data in the given dcache is
+ *                  not valid. 1 if we are loading a known used dcache, so the
+ *                  data in the dcache must be valid.
  *
  * Environment:
  *     afs_xdcache lock write-locked.
  */
 struct dcache *
-afs_UFSGetDSlot(register afs_int32 aslot, register struct dcache *tmpdc)
+afs_UFSGetDSlot(afs_int32 aslot, int indexvalid, int datavalid)
 {
-    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)
@@ -3065,60 +2729,84 @@ 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 (indexvalid) {
+           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 (datavalid) {
+           osi_Panic("afs: needed valid dcache but index %d off %d has "
+                     "invalid cell num %d\n",
+                     (int)aslot, off, (int)tdc->f.fid.Cell);
+       }
+    }
+
+    if (datavalid && tdc->f.fid.Fid.Volume == 0) {
+       osi_Panic("afs: invalid zero-volume dcache entry at slot %d off %d",
+                 (int)aslot, off);
+    }
 
-    if (!entryok) {
+    if (!entryok || !datavalid) {
        tdc->f.fid.Cell = 0;
        tdc->f.fid.Fid.Volume = 0;
        tdc->f.chunk = -1;
        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);
@@ -3129,9 +2817,9 @@ afs_UFSGetDSlot(register afs_int32 aslot, register struct dcache *tmpdc)
            } else if (tdc->f.states & DBackup) {
                afs_DCMoveBucket(tdc, 0, 1);
            } else {
-               afs_DCMoveBucket(tdc, 0, 1); 
+               afs_DCMoveBucket(tdc, 0, 1);
            }
-       } 
+       }
     }
     tdc->refCount = 1;
     tdc->index = aslot;
@@ -3146,44 +2834,39 @@ afs_UFSGetDSlot(register afs_int32 aslot, register struct dcache *tmpdc)
        osi_Assert(0 == NBObtainWriteLock(&tdc->tlock, 676));
     }
 
-    RWLOCK_INIT(&tdc->lock, "dcache lock");
-    RWLOCK_INIT(&tdc->tlock, "dcache tlock");
-    RWLOCK_INIT(&tdc->mflock, "dcache flock");
+    AFS_RWLOCK_INIT(&tdc->lock, "dcache lock");
+    AFS_RWLOCK_INIT(&tdc->tlock, "dcache tlock");
+    AFS_RWLOCK_INIT(&tdc->mflock, "dcache flock");
     ObtainReadLock(&tdc->tlock);
 
     /*
      * 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 */
 
 
 
-/*
- * afs_WriteDCache
- *
- * Description:
- *     write a particular dcache entry back to its home in the
- *     CacheInfo file.
+/*!
+ * Write a particular dcache entry back to its home in the
+ * CacheInfo file.
  *
- * Parameters:
- *     adc   : Pointer to the dcache entry to write.
- *     atime : If true, set the modtime on the file to the current time.
+ * \param adc Pointer to the dcache entry to write.
+ * \param atime If true, set the modtime on the file to the current time.
  *
- * Environment:
+ * \note Environment:
  *     Must be called with the afs_xdcache lock at least read-locked,
  *     and dcache entry at least read-locked.
  *     The reference count is not changed.
  */
 
 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;
@@ -3191,6 +2874,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.
      */
@@ -3200,32 +2896,31 @@ 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;
 }
 
 
 
-/*
- * afs_wakeup
- *
- * Description:
- *     Wake up users of a particular file waiting for stores to take
- *     place.
+/*!
+ * Wake up users of a particular file waiting for stores to take
+ * place.
  *
- * Parameters:
- *     avc : Ptr to related vcache entry.
+ * \param avc Ptr to related vcache entry.
  *
- * Environment:
+ * \note Environment:
  *     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++) {
@@ -3241,7 +2936,7 @@ afs_wakeup(register struct vcache *avc)
             * I think this is redundant now because this sort of thing
             * is already being handled by the higher-level code.
             */
-           if ((avc->states & CSafeStore) == 0) {
+           if ((avc->f.states & CSafeStore) == 0) {
                tb->code = 0;
                tb->flags |= BUVALID;
                if (tb->flags & BUWAIT) {
@@ -3255,106 +2950,94 @@ afs_wakeup(register struct vcache *avc)
     return 0;
 }
 
-
-/*
- * afs_InitCacheFile
- *
- * Description:
- *     Given a file name and inode, set up that file to be an
- *     active member in the AFS cache.  This also involves checking
- *     the usability of its data.
+/*!
+ * Given a file name and inode, set up that file to be an
+ * active member in the AFS cache.  This also involves checking
+ * the usability of its data.
  *
- * Parameters:
- *     afile  : Name of the cache file to initialize.
- *     ainode : Inode of the file.
+ * \param afile Name of the cache file to initialize.
+ * \param ainode Inode of the file.
  *
- * Environment:
+ * \note Environment:
  *     This function is called only during initialization.
  */
-
 int
 afs_InitCacheFile(char *afile, ino_t ainode)
 {
-    register afs_int32 code;
-#if defined(AFS_LINUX22_ENV)
-    struct dentry *filevp;
-#else
-    struct vnode *filevp;
-#endif
+    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;
     if (index >= afs_cacheFiles)
        return EINVAL;
 
-    MObtainWriteLock(&afs_xdcache, 282);
-    tdc = afs_GetDSlot(index, NULL);
+    ObtainWriteLock(&afs_xdcache, 282);
+    tdc = afs_GetNewDSlot(index);
     ReleaseReadLock(&tdc->tlock);
-    MReleaseWriteLock(&afs_xdcache);
+    ReleaseWriteLock(&afs_xdcache);
 
     ObtainWriteLock(&tdc->lock, 621);
-    MObtainWriteLock(&afs_xdcache, 622);
-    if (afile) {
-       code = gop_lookupname(afile, AFS_UIOSYS, 0, &filevp);
-       if (code) {
-           ReleaseWriteLock(&afs_xdcache);
-           ReleaseWriteLock(&tdc->lock);
-           afs_PutDCache(tdc);
-           return code;
+    ObtainWriteLock(&afs_xdcache, 622);
+    if (!afile && !ainode) {
+       tfile = NULL;
+       fileIsBad = 1;
+    } else {
+       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;
+#else
+           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");
+
        /*
-        * We have a VN_HOLD on filevp.  Get the useful info out and
-        * return.  We make use of the fact that the cache is in the
-        * UFS file system, and just record the inode number.
+        * If file size doesn't match the cache info file, it's probably bad.
         */
-#ifdef AFS_LINUX22_ENV
-       tdc->f.inode = VTOI(filevp->d_inode)->i_number;
-       dput(filevp);
-#else
-       tdc->f.inode = afs_vnodeToInumber(filevp);
-       AFS_RELE(filevp);
-#endif /* AFS_LINUX22_ENV */
-    } else {
-       tdc->f.inode = ainode;
+       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;
     }
-    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");
-
-    /*
-     * If file size doesn't match the cache info file, it's probably bad.
-     */
-    if (tdc->f.chunkBytes != tstat.size)
-       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);
@@ -3391,11 +3074,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);
@@ -3411,16 +3095,20 @@ afs_InitCacheFile(char *afile, ino_t ainode)
  */
 #define DDSIZE 200
 
-/* 
- * afs_dcacheInit
+/*!
+ * Initialize dcache related variables.
+ *
+ * \param afiles
+ * \param ablocks
+ * \param aDentries
+ * \param achunk
+ * \param aflags
  *
- * Description:
- *     Initialize dcache related variables.
  */
 void
 afs_dcacheInit(int afiles, int ablocks, int aDentries, int achunk, int aflags)
 {
-    register struct dcache *tdp;
+    struct dcache *tdp;
     int i;
     int code;
 
@@ -3444,64 +3132,45 @@ 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) {
-           printf("afsd: memory cache too large for available memory.\n");
-           printf("afsd: AFS files cannot be accessed.\n\n");
-           dcacheDisabled = 1;
-           afiles = ablocks = 0;
-       } else
-           printf("Memory cache: Allocating %d dcache entries...",
-                  aDentries);
-    } else {
-       cacheDiskType = AFS_FCACHE_TYPE_UFS;
-       afs_cacheType = &afs_UfsCacheOps;
-    }
-
     if (aDentries > 512)
        afs_dhashsize = 2048;
     /* 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);
-    memset((char *)afs_indexTable, 0, sizeof(struct dcache *) * afiles);
-    afs_indexTimes =
-       (afs_hyper_t *) afs_osi_Alloc(afiles * sizeof(afs_hyper_t));
-    memset((char *)afs_indexTimes, 0, afiles * sizeof(afs_hyper_t));
-    afs_indexUnique =
-       (afs_int32 *) afs_osi_Alloc(afiles * sizeof(afs_uint32));
-    memset((char *)afs_indexUnique, 0, afiles * sizeof(afs_uint32));
-    afs_indexFlags = (u_char *) afs_osi_Alloc(afiles * sizeof(u_char));
-    memset((char *)afs_indexFlags, 0, afiles * sizeof(char));
+    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_osi_Alloc(afiles * sizeof(afs_hyper_t));
+    osi_Assert(afs_indexTimes != NULL);
+    memset(afs_indexTimes, 0, afiles * sizeof(afs_hyper_t));
+    afs_indexUnique = afs_osi_Alloc(afiles * sizeof(afs_uint32));
+    osi_Assert(afs_indexUnique != NULL);
+    memset(afs_indexUnique, 0, afiles * sizeof(afs_uint32));
+    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));
-    memset((char *)tdp, 0, 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 */
     pin((char *)afs_indexTimes, sizeof(afs_hyper_t) * afiles); /* XXX */
@@ -3517,14 +3186,14 @@ afs_dcacheInit(int afiles, int ablocks, int aDentries, int achunk, int aflags)
     afs_freeDSList = &tdp[0];
     for (i = 0; i < aDentries - 1; i++) {
        tdp[i].lruq.next = (struct afs_q *)(&tdp[i + 1]);
-        RWLOCK_INIT(&tdp[i].lock, "dcache lock");
-        RWLOCK_INIT(&tdp[i].tlock, "dcache tlock");
-        RWLOCK_INIT(&tdp[i].mflock, "dcache flock");
+        AFS_RWLOCK_INIT(&tdp[i].lock, "dcache lock");
+        AFS_RWLOCK_INIT(&tdp[i].tlock, "dcache tlock");
+        AFS_RWLOCK_INIT(&tdp[i].mflock, "dcache flock");
     }
     tdp[aDentries - 1].lruq.next = (struct afs_q *)0;
-    RWLOCK_INIT(&tdp[aDentries - 1].lock, "dcache lock");
-    RWLOCK_INIT(&tdp[aDentries - 1].tlock, "dcache tlock");
-    RWLOCK_INIT(&tdp[aDentries - 1].mflock, "dcache flock");
+    AFS_RWLOCK_INIT(&tdp[aDentries - 1].lock, "dcache lock");
+    AFS_RWLOCK_INIT(&tdp[aDentries - 1].tlock, "dcache tlock");
+    AFS_RWLOCK_INIT(&tdp[aDentries - 1].mflock, "dcache flock");
 
     afs_stats_cmperf.cacheBlocksOrig = afs_stats_cmperf.cacheBlocksTotal =
        afs_cacheBlocks = ablocks;
@@ -3532,15 +3201,38 @@ 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;
+           afiles = ablocks = 0;
+       } else
+           afs_warn("Memory cache: Allocating %d dcache entries...",
+                  aDentries);
+    } else {
+       cacheDiskType = AFS_FCACHE_TYPE_UFS;
+       afs_cacheType = &afs_UfsCacheOps;
+    }
 }
 
-/*
- * shutdown_dcache
+/*!
+ * Shuts down the cache.
  *
  */
 void
@@ -3548,6 +3240,18 @@ shutdown_dcache(void)
 {
     int i;
 
+#ifdef AFS_CACHE_VNODE_PATH
+    if (cacheDiskType != AFS_FCACHE_TYPE_MEM) {
+       struct dcache *tdc;
+       for (i = 0; i < afs_cacheFiles; i++) {
+           tdc = afs_indexTable[i];
+           if (tdc) {
+               afs_osi_FreeStr(tdc->f.inode.ufs);
+           }
+       }
+    }
+#endif
+
     afs_osi_Free(afs_dvnextTbl, afs_cacheFiles * sizeof(afs_int32));
     afs_osi_Free(afs_dcnextTbl, afs_cacheFiles * sizeof(afs_int32));
     afs_osi_Free(afs_indexTable, afs_cacheFiles * sizeof(struct dcache *));
@@ -3576,8 +3280,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);
 
@@ -3591,183 +3295,215 @@ shutdown_dcache(void)
 
 }
 
-#if defined(AFS_DISCON_ENV)
+/*!
+ * 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
+ * overwritten!).
+ *
+ * \param avc          The vcache to fetch a dcache for
+ * \param filePos      The start of the section to be written
+ * \param len          The length of the section to be written
+ * \param areq
+ * \param noLock
+ *
+ * \return If successful, a reference counted dcache with tdc->lock held. Lock
+ *         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
+ *       avc->lock and GLOCK.
+ */
+
+struct dcache *
+afs_ObtainDCacheForWriting(struct vcache *avc, afs_size_t filePos,
+                          afs_size_t len, struct vrequest *areq,
+                          int noLock)
+{
+    struct dcache *tdc = NULL;
+    afs_size_t offset;
+
+    /* read the cached info */
+    if (noLock) {
+       tdc = afs_FindDCache(avc, filePos);
+       if (tdc)
+           ObtainWriteLock(&tdc->lock, 657);
+    } else if (afs_blocksUsed >
+              PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) {
+       tdc = afs_FindDCache(avc, filePos);
+       if (tdc) {
+           ObtainWriteLock(&tdc->lock, 658);
+           if (!hsame(tdc->f.versionNo, avc->f.m.DataVersion)
+               || (tdc->dflags & DFFetching)) {
+               ReleaseWriteLock(&tdc->lock);
+               afs_PutDCache(tdc);
+               tdc = NULL;
+           }
+       }
+       if (!tdc) {
+           afs_MaybeWakeupTruncateDaemon();
+           while (afs_blocksUsed >
+                  PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) {
+               ReleaseWriteLock(&avc->lock);
+               if (afs_blocksUsed - afs_blocksDiscarded >
+                   PERCENT(CM_WAITFORDRAINPCT, afs_cacheBlocks)) {
+                   afs_WaitForCacheDrain = 1;
+                   afs_osi_Sleep(&afs_WaitForCacheDrain);
+               }
+               afs_MaybeFreeDiscardedDCache();
+               afs_MaybeWakeupTruncateDaemon();
+               ObtainWriteLock(&avc->lock, 509);
+           }
+           avc->f.states |= CDirty;
+           tdc = afs_GetDCache(avc, filePos, areq, &offset, &len, 4);
+           if (tdc)
+               ObtainWriteLock(&tdc->lock, 659);
+       }
+    } else {
+       tdc = afs_GetDCache(avc, filePos, areq, &offset, &len, 4);
+       if (tdc)
+           ObtainWriteLock(&tdc->lock, 660);
+    }
+    if (tdc) {
+       if (!(afs_indexFlags[tdc->index] & IFDataMod)) {
+           afs_stats_cmperf.cacheCurrDirtyChunks++;
+           afs_indexFlags[tdc->index] |= IFDataMod;    /* so it doesn't disappear */
+       }
+       if (!(tdc->f.states & DWriting)) {
+           /* don't mark entry as mod if we don't have to */
+           tdc->f.states |= DWriting;
+           tdc->dflags |= DFEntryMod;
+       }
+    }
+    return tdc;
+}
 
 /*!
- * Make a shadow copy of a dir's dcaches. It's used for disconnected
+ * 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.
  * On reconnection, we can diff the original data with the server and get the
  * server changes and with the local data to get the local changes.
  *
  * \param avc The dir vnode.
+ * \param adc The dir dcache.
  *
  * \return 0 for success.
  *
- * \note The only lock allowed to be set is the dir's vcache entry, and it
- * must be set in write mode.
  * \note The vcache entry must be write locked.
+ * \note The dcache entry must be read locked.
  */
-int afs_MakeShadowDir(struct vcache *avc)
+int
+afs_MakeShadowDir(struct vcache *avc, struct dcache *adc)
 {
-    int j, i, index, code, ret_code = 0, offset, trans_size, block;
-    struct dcache *tdc, *new_dc = NULL;
+    int i, code, ret_code = 0, written, trans_size;
+    struct dcache *new_dc = NULL;
     struct osi_file *tfile_src, *tfile_dst;
     struct VenusFid shadow_fid;
     char *data;
-    int lock_held = 0;
 
     /* Is this a dir? */
     if (vType(avc) != VDIR)
        return ENOTDIR;
 
+    if (avc->f.shadow.vnode || avc->f.shadow.unique)
+       return EEXIST;
+
     /* Generate a fid for the shadow dir. */
-    shadow_fid.Cell = avc->fid.Cell;
-    shadow_fid.Fid.Volume = avc->fid.Fid.Volume;
+    shadow_fid.Cell = avc->f.fid.Cell;
+    shadow_fid.Fid.Volume = avc->f.fid.Fid.Volume;
     afs_GenShadowFid(&shadow_fid);
 
-    /* For each dcache, do copy it into a new fresh one. */
-    i = DVHash(&avc->fid);
-    for (index = afs_dvhashTbl[i]; index != NULLIDX; index = i) {
-       /* Making sure that this isn't going to get locked twice. */
-       if (!lock_held) {
-           /* XXX: Moved it from outside of the loop.
-            * Maybe it's not quite okay because of the use of
-            * dvhashTbl (once) in the for statement.
-            */
-           ObtainWriteLock(&afs_xdcache, 716);
-           lock_held = 1;
-       }
-
-        i = afs_dvnextTbl[index];
-        if (afs_indexUnique[index] == avc->fid.Fid.Unique) {
-            tdc = afs_GetDSlot(index, NULL);
-
-           ReleaseReadLock(&tdc->tlock);
-
-           if (!FidCmp(&tdc->f.fid, &avc->fid)) {
-
-               /* Got a dir's dcache. */
-               lock_held = 0;
-
-               /* Get a fresh dcache. */
-               new_dc = afs_AllocDCache(avc, 0, 0, &shadow_fid);
-
-               /* Unlock hash for now. Don't need it during operations on the
-                * dcache. Oh, and we can't use it because of the locking
-                * hierarchy...
-                */
-               /* XXX: So much for lock ierarchy, the afs_AllocDCache doesn't
-                * respect it.
-                */
-               //ReleaseWriteLock(&afs_xdcache);
-
-               ObtainReadLock(&tdc->lock);
+    ObtainWriteLock(&afs_xdcache, 716);
 
-               /* Set up the new fid. */
-               /* Copy interesting data from original dir dcache. */
-               new_dc->mflags = tdc->mflags;
-               new_dc->dflags = tdc->dflags;
-               new_dc->f.modTime = tdc->f.modTime;
-               new_dc->f.versionNo = tdc->f.versionNo;
-               new_dc->f.states = tdc->f.states;
-               new_dc->f.chunk= tdc->f.chunk;
-               new_dc->f.chunkBytes = tdc->f.chunkBytes;
+    /* Get a fresh dcache. */
+    new_dc = afs_AllocDCache(avc, 0, 0, &shadow_fid);
 
-               /*
-                * Now add to the two hash chains - note that i is still set
-                * from the above DCHash call.
-                */
-               //ObtainWriteLock(&afs_xdcache, 713);
+    ObtainReadLock(&adc->mflock);
 
-               j = DCHash(&shadow_fid, 0);
-               afs_dcnextTbl[new_dc->index] = afs_dchashTbl[j];
-               afs_dchashTbl[j] = new_dc->index;
+    /* Set up the new fid. */
+    /* Copy interesting data from original dir dcache. */
+    new_dc->mflags = adc->mflags;
+    new_dc->dflags = adc->dflags;
+    new_dc->f.modTime = adc->f.modTime;
+    new_dc->f.versionNo = adc->f.versionNo;
+    new_dc->f.states = adc->f.states;
+    new_dc->f.chunk= adc->f.chunk;
+    new_dc->f.chunkBytes = adc->f.chunkBytes;
 
-               j = DVHash(&shadow_fid);
-               afs_dvnextTbl[new_dc->index] = afs_dvhashTbl[j];
-               afs_dvhashTbl[j] = new_dc->index;
-               afs_MaybeWakeupTruncateDaemon();
+    ReleaseReadLock(&adc->mflock);
 
-               ReleaseWriteLock(&afs_xdcache);
+    /* Now add to the two hash chains */
+    i = DCHash(&shadow_fid, 0);
+    afs_dcnextTbl[new_dc->index] = afs_dchashTbl[i];
+    afs_dchashTbl[i] = new_dc->index;
 
-               /* Alloc a 4k block. */
-               data = (char *) afs_osi_Alloc(4096);
-               if (!data) {
-                   printf("afs_MakeShadowDir: could not alloc data\n");
-                   ret_code = ENOMEM;
-                   goto done;
-               }
+    i = DVHash(&shadow_fid);
+    afs_dvnextTbl[new_dc->index] = afs_dvhashTbl[i];
+    afs_dvhashTbl[i] = new_dc->index;
 
-               /* Open the files. */
-               tfile_src = afs_CFileOpen(tdc->f.inode);
-               tfile_dst = afs_CFileOpen(new_dc->f.inode);
+    ReleaseWriteLock(&afs_xdcache);
 
-               /* Init no of blocks to be read and offset. */
-               block = (tdc->f.chunkBytes / 4096);
-               offset = 0;
+    /* Alloc a 4k block. */
+    data = afs_osi_Alloc(4096);
+    if (!data) {
+       afs_warn("afs_MakeShadowDir: could not alloc data\n");
+       ret_code = ENOMEM;
+       goto done;
+    }
 
-               /* And now copy dir dcache data into this dcache,
-                * 4k at a time.
-                */
-               while (block >= 0) {
-
-                   /* Last chunk might have less bytes to transfer. */
-                   if (!block) {
-                       /* Last block. */
-                       trans_size = (tdc->f.chunkBytes % 4096);
-                       if (!trans_size)
-                           /* An exact no of 4k blocks. */
-                           break;
-                   } else
-                       trans_size = 4096;
-
-                   /* Read a chunk from the dcache. */
-                   code = afs_CFileRead(tfile_src, offset, data, trans_size);
-                   if (code < trans_size) {
-                       /* Can't access file, stop doing stuff and return error. */
-                       ret_code = EIO;
-                       break;
-                   }
+    /* Open the files. */
+    tfile_src = afs_CFileOpen(&adc->f.inode);
+    tfile_dst = afs_CFileOpen(&new_dc->f.inode);
 
-                   /* Write it to the new dcache. */
-                   code = afs_CFileWrite(tfile_dst, offset, data, trans_size);
-                   if (code < trans_size) {
-                       ret_code = EIO;
-                       break;
-                   }
+    /* And now copy dir dcache data into this dcache,
+     * 4k at a time.
+     */
+    written = 0;
+    while (written < adc->f.chunkBytes) {
+       trans_size = adc->f.chunkBytes - written;
+       if (trans_size > 4096)
+           trans_size = 4096;
+
+       /* Read a chunk from the dcache. */
+       code = afs_CFileRead(tfile_src, written, data, trans_size);
+       if (code < trans_size) {
+           ret_code = EIO;
+           break;
+       }
 
-                   block--;
-                   offset += 4096;
-               }               /* while (block) */
+       /* Write it to the new dcache. */
+       code = afs_CFileWrite(tfile_dst, written, data, trans_size);
+       if (code < trans_size) {
+           ret_code = EIO;
+           break;
+       }
 
-               afs_CFileClose(tfile_dst);
-               afs_CFileClose(tfile_src);
+       written+=trans_size;
+    }
 
-               afs_osi_Free(data, 4096);
+    afs_CFileClose(tfile_dst);
+    afs_CFileClose(tfile_src);
 
-               ReleaseWriteLock(&new_dc->lock);
-               ReleaseReadLock(&tdc->lock);
+    afs_osi_Free(data, 4096);
 
-               afs_PutDCache(new_dc);
-           }                   /* if dcache fid match */
-            afs_PutDCache(tdc);
-        }                      /* if unuiquifier match */
-    }
-done:
-    if (lock_held)
-       ReleaseWriteLock(&afs_xdcache);
+    ReleaseWriteLock(&new_dc->lock);
+    afs_PutDCache(new_dc);
 
     if (!ret_code) {
-       if (!avc->ddirty_flags) {
-           ObtainWriteLock(&afs_DDirtyVCListLock, 763);
-           AFS_DISCON_ADD_DIRTY(avc);
-           ReleaseWriteLock(&afs_DDirtyVCListLock);
-       }
-       avc->shVnode = shadow_fid.Fid.Vnode;
-       avc->shUnique = shadow_fid.Fid.Unique;
-       avc->ddirty_flags |= VDisconShadowed;
+       ObtainWriteLock(&afs_xvcache, 763);
+       ObtainWriteLock(&afs_disconDirtyLock, 765);
+       QAdd(&afs_disconShadow, &avc->shadowq);
+       osi_Assert((afs_RefVCache(avc) == 0));
+       ReleaseWriteLock(&afs_disconDirtyLock);
+       ReleaseWriteLock(&afs_xvcache);
+
+       avc->f.shadow.vnode = shadow_fid.Fid.Vnode;
+       avc->f.shadow.unique = shadow_fid.Fid.Unique;
     }
 
+done:
     return ret_code;
 }
 
@@ -3778,15 +3514,16 @@ done:
  *
  * \note avc must be write locked.
  */
-void afs_DeleteShadowDir(struct vcache *avc)
+void
+afs_DeleteShadowDir(struct vcache *avc)
 {
     struct dcache *tdc;
     struct VenusFid shadow_fid;
 
-    shadow_fid.Cell = avc->fid.Cell;
-    shadow_fid.Fid.Volume = avc->fid.Fid.Volume;
-    shadow_fid.Fid.Vnode = avc->shVnode;
-    shadow_fid.Fid.Unique = avc->shUnique;
+    shadow_fid.Cell = avc->f.fid.Cell;
+    shadow_fid.Fid.Volume = avc->f.fid.Fid.Volume;
+    shadow_fid.Fid.Vnode = avc->f.shadow.vnode;
+    shadow_fid.Fid.Unique = avc->f.shadow.unique;
 
     tdc = afs_FindDCacheByFid(&shadow_fid);
     if (tdc) {
@@ -3794,7 +3531,52 @@ void afs_DeleteShadowDir(struct vcache *avc)
        afs_DiscardDCache(tdc);
        afs_PutDCache(tdc);
     }
-    /* Remove shadowed dir flag. */
-    avc->ddirty_flags &= ~VDisconShadowed;
+    avc->f.shadow.vnode = avc->f.shadow.unique = 0;
+    ObtainWriteLock(&afs_disconDirtyLock, 708);
+    QRemove(&avc->shadowq);
+    ReleaseWriteLock(&afs_disconDirtyLock);
+    afs_PutVCache(avc); /* Because we held it when we added to the queue */
+}
+
+/*!
+ * Populate a dcache with empty chunks up to a given file size,
+ * used before extending a file in order to avoid 'holes' which
+ * we can't access in disconnected mode.
+ *
+ * \param avc   The vcache which is being extended (locked)
+ * \param alen  The new length of the file
+ *
+ */
+void
+afs_PopulateDCache(struct vcache *avc, afs_size_t apos, struct vrequest *areq)
+{
+    struct dcache *tdc;
+    afs_size_t len, offset;
+    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
+     * length we GetDCache for that chunk.
+     */
+
+    if (AFS_CHUNK(apos) == 0 || apos <= avc->f.m.Length)
+       return;
+
+    if (avc->f.m.Length == 0)
+       start = 0;
+    else
+       start = AFS_CHUNK(avc->f.m.Length)+1;
+
+    end = AFS_CHUNK(apos);
+
+    while (start<end) {
+       len = AFS_CHUNKTOSIZE(start);
+       tdc = afs_GetDCache(avc, AFS_CHUNKTOBASE(start), areq, &offset, &len, 4);
+       if (tdc)
+           afs_PutDCache(tdc);
+       start++;
+    }
 }
-#endif