vol: return VNOVNODE if vnode is not allocated
[openafs.git] / src / vol / vnode.c
index 2936d97..6983e8d 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
  */
 #include <afsconfig.h>
 #include <afs/param.h>
-#define MAXINT     (~(1<<((sizeof(int)*8)-1)))
 
-RCSID
-    ("$Header$");
+#include <roken.h>
 
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#ifdef AFS_PTHREAD_ENV
-#include <assert.h>
-#else /* AFS_PTHREAD_ENV */
-#include <afs/assert.h>
-#endif /* AFS_PTHREAD_ENV */
+#include <limits.h>
+
+#ifdef HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
 
-#include <rx/xdr.h>
+#include <afs/opr.h>
+#ifdef AFS_PTHREAD_ENV
+#include <opr/lock.h>
+#endif
 #include "rx/rx_queue.h"
 #include <afs/afsint.h>
 #include "nfs.h"
@@ -46,34 +44,14 @@ RCSID
 #include "vnode_inline.h"
 #include "partition.h"
 #include "salvsync.h"
-#if defined(AFS_SGI_ENV)
-#include "sys/types.h"
-#include "fcntl.h"
-#undef min
-#undef max
-#include "stdlib.h"
-#endif
+#include "common.h"
 #ifdef AFS_NT40_ENV
-#include <fcntl.h>
 #include "ntops.h"
-#else
-#include <sys/file.h>
-#ifdef AFS_SUN5_ENV
-#include <sys/fcntl.h>
 #endif
-#include <unistd.h>
-#endif /* AFS_NT40_ENV */
-#include <sys/stat.h>
-
-/*@printflike@*/ extern void Log(const char *format, ...);
-
-/*@printflike@*/ extern void Abort(const char *format, ...);
-
 
 struct VnodeClassInfo VnodeClassInfo[nVNODECLASSES];
 
-private void StickOnLruChain_r(register Vnode * vnp,
-                              register struct VnodeClassInfo *vcp);
+void VNLog(afs_int32 aop, afs_int32 anparms, ... );
 
 extern int LogLevel;
 
@@ -109,18 +87,13 @@ extern int LogLevel;
 static afs_int32 theLog[THELOGSIZE];
 static afs_int32 vnLogPtr = 0;
 void
-VNLog(aop, anparms, av1, av2, av3, av4)
-     afs_int32 aop, anparms;
-     afs_int32 av1, av2, av3, av4;
+VNLog(afs_int32 aop, afs_int32 anparms, ... )
 {
-    register afs_int32 temp;
-    afs_int32 data[4];
-
-    /* copy data to array */
-    data[0] = av1;
-    data[1] = av2;
-    data[2] = av3;
-    data[3] = av4;
+    afs_int32 temp;
+    va_list ap;
+
+    va_start(ap, anparms);
+
     if (anparms > 4)
        anparms = 4;            /* do bounds checking */
 
@@ -129,10 +102,11 @@ VNLog(aop, anparms, av1, av2, av3, av4)
     if (vnLogPtr >= THELOGSIZE)
        vnLogPtr = 0;
     for (temp = 0; temp < anparms; temp++) {
-       theLog[vnLogPtr++] = data[temp];
+       theLog[vnLogPtr++] = va_arg(ap, afs_int32);
        if (vnLogPtr >= THELOGSIZE)
            vnLogPtr = 0;
     }
+    va_end(ap);
 }
 
 /* VolumeHashOffset -- returns a new value to be stored in the
@@ -202,7 +176,7 @@ AddToVVnList(Volume * vp, Vnode * vnp)
  * @internal volume package internal use only
  */
 void
-DeleteFromVVnList(register Vnode * vnp)
+DeleteFromVVnList(Vnode * vnp)
 {
     Vn_volume(vnp) = NULL;
 
@@ -265,7 +239,7 @@ DeleteFromVnLRU(struct VnodeClassInfo * vcp, Vnode * vnp)
     if (vnp == vcp->lruHead)
        vcp->lruHead = vcp->lruHead->lruNext;
 
-    if ((vnp == vcp->lruHead) || 
+    if ((vnp == vcp->lruHead) ||
        (vcp->lruHead == NULL))
        Abort("DeleteFromVnLRU: lru chain addled!\n");
 
@@ -350,7 +324,7 @@ DeleteFromVnHash(Vnode * vnp)
  * @internal vnode package internal use only
  */
 void
-VInvalidateVnode_r(register struct Vnode *avnode)
+VInvalidateVnode_r(struct Vnode *avnode)
 {
     avnode->changed_newTime = 0;       /* don't let it get flushed out again */
     avnode->changed_oldTime = 0;
@@ -381,13 +355,13 @@ int
 VInitVnodes(VnodeClass class, int nVnodes)
 {
     byte *va;
-    register struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
+    struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
 
     vcp->allocs = vcp->gets = vcp->reads = vcp->writes = 0;
     vcp->cacheSize = nVnodes;
     switch (class) {
     case vSmall:
-       assert(CHECKSIZE_SMALLVNODE);
+       opr_Assert(CHECKSIZE_SMALLVNODE);
        vcp->lruHead = NULL;
        vcp->residentSize = SIZEOF_SMALLVNODE;
        vcp->diskSize = SIZEOF_SMALLDISKVNODE;
@@ -412,13 +386,13 @@ VInitVnodes(VnodeClass class, int nVnodes)
        return 0;
 
     va = (byte *) calloc(nVnodes, vcp->residentSize);
-    assert(va != NULL);
+    opr_Assert(va != NULL);
     while (nVnodes--) {
        Vnode *vnp = (Vnode *) va;
        Vn_refcount(vnp) = 0;   /* no context switches */
        Vn_stateFlags(vnp) |= VN_ON_LRU;
 #ifdef AFS_DEMAND_ATTACH_FS
-       assert(pthread_cond_init(&Vn_stateCV(vnp), NULL) == 0);
+       CV_INIT(&Vn_stateCV(vnp), "vnode state", CV_DEFAULT, 0);
        Vn_state(vnp) = VN_STATE_INVALID;
        Vn_readers(vnp) = 0;
 #else /* !AFS_DEMAND_ATTACH_FS */
@@ -456,27 +430,37 @@ VInitVnodes(VnodeClass class, int nVnodes)
  * allocate an unused vnode from the lru chain.
  *
  * @param[in] vcp  vnode class info object pointer
+ * @param[in] vp   volume pointer
+ * @param[in] vnodeNumber new vnode number that the vnode will be used for
  *
  * @pre VOL_LOCK is held
  *
- * @post vnode object is removed from lru, and vnode hash table.
- *       vnode is disassociated from volume object.
+ * @post vnode object is removed from lru
+ *       vnode is disassociated with its old volume, and associated with its
+ *         new volume
+ *       vnode is removed from its old vnode hash table, and for DAFS, it is
+ *         added to its new hash table
  *       state is set to VN_STATE_INVALID.
  *       inode handle is released.
+ *       a reservation is held on the vnode object
  *
- * @note we traverse backwards along the lru circlist.  It shouldn't 
- *       be necessary to specify that nUsers == 0 since if it is in the list, 
- *       nUsers should be 0.  Things shouldn't be in lruq unless no one is 
+ * @note we traverse backwards along the lru circlist.  It shouldn't
+ *       be necessary to specify that nUsers == 0 since if it is in the list,
+ *       nUsers should be 0.  Things shouldn't be in lruq unless no one is
  *       using them.
  *
  * @warning DAFS: VOL_LOCK is dropped while doing inode handle release
  *
+ * @warning for non-DAFS, the vnode is _not_ hashed on the vnode hash table;
+ *          non-DAFS must hash the vnode itself after loading data
+ *
  * @return vnode object pointer
  */
 Vnode *
-VGetFreeVnode_r(struct VnodeClassInfo * vcp)
+VGetFreeVnode_r(struct VnodeClassInfo * vcp, struct Volume *vp,
+                VnodeId vnodeNumber)
 {
-    register Vnode *vnp;
+    Vnode *vnp;
 
     vnp = vcp->lruHead->lruPrev;
 #ifdef AFS_DEMAND_ATTACH_FS
@@ -487,11 +471,11 @@ VGetFreeVnode_r(struct VnodeClassInfo * vcp)
     if (Vn_refcount(vnp) != 0 || CheckLock(&vnp->lock))
        Abort("VGetFreeVnode_r: locked vnode in lruq");
 #endif
-    VNLog(1, 2, Vn_id(vnp), (afs_int32) vnp);
+    VNLog(1, 2, Vn_id(vnp), (intptr_t)vnp, 0, 0);
 
-    /* 
+    /*
      * it's going to be overwritten soon enough.
-     * remove from LRU, delete hash entry, and 
+     * remove from LRU, delete hash entry, and
      * disassociate from old parent volume before
      * we have a chance to drop the vol glock
      */
@@ -501,12 +485,30 @@ VGetFreeVnode_r(struct VnodeClassInfo * vcp)
        DeleteFromVVnList(vnp);
     }
 
+    /* we must re-hash the vnp _before_ we drop the glock again; otherwise,
+     * someone else might try to grab the same vnode id, and we'll both alloc
+     * a vnode object for the same vn id, bypassing vnode locking */
+    Vn_id(vnp) = vnodeNumber;
+    VnCreateReservation_r(vnp);
+    AddToVVnList(vp, vnp);
+#ifdef AFS_DEMAND_ATTACH_FS
+    AddToVnHash(vnp);
+#endif
+
     /* drop the file descriptor */
     if (vnp->handle) {
 #ifdef AFS_DEMAND_ATTACH_FS
        VnChangeState_r(vnp, VN_STATE_RELEASING);
        VOL_UNLOCK;
 #endif
+       /* release is, potentially, a highly latent operation due to a couple
+        * factors:
+        *   - ihandle package lock contention
+        *   - closing file descriptor(s) associated with ih
+        *
+        * Hance, we perform outside of the volume package lock in order to
+        * reduce the probability of contention.
+        */
        IH_RELEASE(vnp->handle);
 #ifdef AFS_DEMAND_ATTACH_FS
        VOL_LOCK;
@@ -546,8 +548,8 @@ VLookupVnode(Volume * vp, VnodeId vnodeId)
 
     newHash = VNODE_HASH(vp, vnodeId);
     for (vnp = VnodeHashTable[newHash];
-        (vnp && 
-         ((Vn_id(vnp) != vnodeId) || 
+        (vnp &&
+         ((Vn_id(vnp) != vnodeId) ||
           (Vn_volume(vnp) != vp) ||
           (vp->cacheCheck != Vn_cacheCheck(vnp))));
         vnp = vnp->hashNext);
@@ -557,11 +559,11 @@ VLookupVnode(Volume * vp, VnodeId vnodeId)
 
 
 Vnode *
-VAllocVnode(Error * ec, Volume * vp, VnodeType type)
+VAllocVnode(Error * ec, Volume * vp, VnodeType type, VnodeId in_vnode, Unique in_unique)
 {
     Vnode *retVal;
     VOL_LOCK;
-    retVal = VAllocVnode_r(ec, vp, type);
+    retVal = VAllocVnode_r(ec, vp, type, in_vnode, in_unique);
     VOL_UNLOCK;
     return retVal;
 }
@@ -572,6 +574,8 @@ VAllocVnode(Error * ec, Volume * vp, VnodeType type)
  * @param[out] ec    error code return
  * @param[in]  vp    volume object pointer
  * @param[in]  type  desired vnode type
+ * @param[in]  type  desired vnode ID (optional)
+ * @param[in]  type  desired vnode Unique (optional)
  *
  * @return vnode object pointer
  *
@@ -581,14 +585,17 @@ VAllocVnode(Error * ec, Volume * vp, VnodeType type)
  * @post vnode allocated and returned
  */
 Vnode *
-VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
+VAllocVnode_r(Error * ec, Volume * vp, VnodeType type, VnodeId in_vnode, Unique in_unique)
 {
-    register Vnode *vnp;
+    Vnode *vnp;
     VnodeId vnodeNumber;
-    int bitNumber, code;
-    register struct VnodeClassInfo *vcp;
+    int bitNumber;
+    struct VnodeClassInfo *vcp;
     VnodeClass class;
     Unique unique;
+    struct vnodeIndex *index;
+    unsigned int offset;
+
 #ifdef AFS_DEMAND_ATTACH_FS
     VolState vol_state_save;
 #endif
@@ -625,10 +632,6 @@ VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
        return NULL;
     }
 
-    unique = vp->nextVnodeUnique++;
-    if (!unique)
-       unique = vp->nextVnodeUnique++;
-
     if (vp->nextVnodeUnique > V_uniquifier(vp)) {
        VUpdateVolume_r(ec, vp, 0);
        if (*ec)
@@ -641,12 +644,63 @@ VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
            return NULL;
     }
 
-    /* Find a slot in the bit map */
-    bitNumber = VAllocBitmapEntry_r(ec, vp, &vp->vnodeIndex[class],
-                                   VOL_ALLOC_BITMAP_WAIT);
-    if (*ec)
-       return NULL;
-    vnodeNumber = bitNumberToVnodeNumber(bitNumber, class);
+    /*
+     * If in_vnode and in_unique are specified, we are asked to
+     * allocate a specifc vnode slot.  Used by RW replication to
+     * keep vnode IDs consistent with the master.
+     */
+
+    if (!in_vnode) {
+       unique = vp->nextVnodeUnique++;
+       if (!unique)
+           unique = vp->nextVnodeUnique++;
+
+       if (vp->nextVnodeUnique > V_uniquifier(vp)) {
+           VUpdateVolume_r(ec, vp, 0);
+           if (*ec)
+               return NULL;
+       }
+
+       /* Find a slot in the bit map */
+       bitNumber = VAllocBitmapEntry_r(ec, vp, &vp->vnodeIndex[class],
+               VOL_ALLOC_BITMAP_WAIT);
+
+       if (*ec)
+           return NULL;
+       vnodeNumber = bitNumberToVnodeNumber(bitNumber, class);
+    } else {
+       index = &vp->vnodeIndex[class];
+       if (!in_unique) {
+           *ec = VNOVNODE;
+           return NULL;
+       }
+       /* Catch us up to where the master is */
+       if (in_unique > vp->nextVnodeUnique)
+           vp->nextVnodeUnique = in_unique+1;
+
+       if (vp->nextVnodeUnique > V_uniquifier(vp)) {
+           VUpdateVolume_r(ec, vp, 0);
+           if (*ec)
+               return NULL;
+       }
+
+       unique = in_unique;
+       bitNumber = vnodeIdToBitNumber(in_vnode);
+       offset = bitNumber >> 3;
+
+       /* Mark vnode in use. Grow bitmap if needed. */
+       if ((offset >= index->bitmapSize)
+               || ((*(index->bitmap + offset) & (1 << (bitNumber & 0x7))) == 0))
+           VGrowBitmap(index);
+       /* Should not happen */
+       if (*(index->bitmap + offset) & (1 << (bitNumber & 0x7))) {
+           *ec = VNOVNODE;
+           return NULL;
+       }
+
+       *(index->bitmap + offset) |= (1 << (bitNumber & 0x7));
+       vnodeNumber = in_vnode;
+    }
 
     /*
      * DAFS:
@@ -654,13 +708,13 @@ VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
      */
 
  vnrehash:
-    VNLog(2, 1, vnodeNumber);
+    VNLog(2, 1, vnodeNumber, 0, 0, 0);
     /* Prepare to move it to the new hash chain */
     vnp = VLookupVnode(vp, vnodeNumber);
     if (vnp) {
        /* slot already exists.  May even not be in lruq (consider store file locking a file being deleted)
         * so we may have to wait for it below */
-       VNLog(3, 2, vnodeNumber, (afs_int32) vnp);
+       VNLog(3, 2, vnodeNumber, (intptr_t)vnp, 0, 0);
 
        VnCreateReservation_r(vnp);
        if (Vn_refcount(vnp) == 1) {
@@ -668,9 +722,6 @@ VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
            /* This won't block */
            VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, WILL_NOT_DEADLOCK);
        } else {
-           /* other users present; follow locking hierarchy */
-           VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, MIGHT_DEADLOCK);
-
 #ifdef AFS_DEMAND_ATTACH_FS
            /*
             * DAFS:
@@ -688,6 +739,9 @@ VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
            }
 #endif
 
+           /* other users present; follow locking hierarchy */
+           VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, MIGHT_DEADLOCK);
+
            /*
             * verify state of the world hasn't changed
             *
@@ -702,19 +756,32 @@ VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
            }
        }
 
+       /* sanity check: vnode should be blank if it was deleted. If it's
+        * not blank, it is still in use somewhere; but the bitmap told us
+        * this vnode number was free, so something is wrong. */
+       if (vnp->disk.type != vNull) {
+           Error tmp;
+           Log("VAllocVnode:  addled bitmap or vnode object! (vol %" AFS_VOLID_FMT ", "
+               "vnode %p, number %ld, type %ld)\n", afs_printable_VolumeId_lu(vp->hashid), vnp,
+               (long)Vn_id(vnp), (long)vnp->disk.type);
+           *ec = EIO;
+           VFreeBitMapEntry_r(&tmp, vp, &vp->vnodeIndex[class], bitNumber,
+                              VOL_FREE_BITMAP_WAIT);
+           VInvalidateVnode_r(vnp);
+           VnUnlock(vnp, WRITE_LOCK);
+           VnCancelReservation_r(vnp);
+#ifdef AFS_DEMAND_ATTACH_FS
+           VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0);
+#else
+           VForceOffline_r(vp, 0);
+#endif
+           return NULL;
+       }
+
     } else {
        /* no such vnode in the cache */
 
-       vnp = VGetFreeVnode_r(vcp);
-
-       /* Initialize the header fields so noone allocates another
-        * vnode with the same number */
-       Vn_id(vnp) = vnodeNumber;
-       VnCreateReservation_r(vnp);
-       AddToVVnList(vp, vnp);
-#ifdef AFS_DEMAND_ATTACH_FS
-       AddToVnHash(vnp);
-#endif
+       vnp = VGetFreeVnode_r(vcp, vp, vnodeNumber);
 
        /* This will never block (guaranteed by check in VGetFreeVnode_r() */
        VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, WILL_NOT_DEADLOCK);
@@ -725,10 +792,11 @@ VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
 
        /* Sanity check:  is this vnode really not in use? */
        {
-           int size;
+           afs_sfsize_t size;
            IHandle_t *ihP = vp->vnodeIndex[class].handle;
            FdHandle_t *fdP;
-           off_t off = vnodeIndexOffset(vcp, vnodeNumber);
+           afs_foff_t off = vnodeIndexOffset(vcp, vnodeNumber);
+           Error tmp;
 
            /* XXX we have a potential race here if two threads
             * allocate new vnodes at the same time, and they
@@ -742,7 +810,7 @@ VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
             *
             * if this becomes a bottleneck, there are ways to
             * improve parallelism for this code path
-            *   -- tkeiser 11/28/2007 
+            *   -- tkeiser 11/28/2007
             */
            VCreateReservation_r(vp);
            VWaitExclusiveState_r(vp);
@@ -753,32 +821,40 @@ VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
            fdP = IH_OPEN(ihP);
            if (fdP == NULL) {
                Log("VAllocVnode: can't open index file!\n");
+               *ec = ENOENT;
                goto error_encountered;
            }
            if ((size = FDH_SIZE(fdP)) < 0) {
                Log("VAllocVnode: can't stat index file!\n");
-               goto error_encountered;
-           }
-           if (FDH_SEEK(fdP, off, SEEK_SET) < 0) {
-               Log("VAllocVnode: can't seek on index file!\n");
+               *ec = EIO;
                goto error_encountered;
            }
            if (off + vcp->diskSize <= size) {
-               if (FDH_READ(fdP, &vnp->disk, vcp->diskSize) != vcp->diskSize) {
+             if (FDH_PREAD(fdP, &vnp->disk, vcp->diskSize, off) != vcp->diskSize) {
                    Log("VAllocVnode: can't read index file!\n");
+                   *ec = EIO;
                    goto error_encountered;
                }
                if (vnp->disk.type != vNull) {
                    Log("VAllocVnode:  addled bitmap or index!\n");
+                   *ec = EIO;
                    goto error_encountered;
                }
            } else {
                /* growing file - grow in a reasonable increment */
-               char *buf = (char *)malloc(16 * 1024);
-               if (!buf)
-                   Abort("VAllocVnode: malloc failed\n");
+               char *buf = malloc(16 * 1024);
+               if (!buf) {
+                   Log("VAllocVnode: can't grow vnode index: out of memory\n");
+                   *ec = ENOMEM;
+                   goto error_encountered;
+               }
                memset(buf, 0, 16 * 1024);
-               (void)FDH_WRITE(fdP, buf, 16 * 1024);
+               if ((FDH_PWRITE(fdP, buf, 16 * 1024, off)) != 16 * 1024) {
+                   Log("VAllocVnode: can't grow vnode index: write failed\n");
+                   *ec = EIO;
+                   free(buf);
+                   goto error_encountered;
+               }
                free(buf);
            }
            FDH_CLOSE(fdP);
@@ -792,8 +868,7 @@ VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
 
 
        error_encountered:
-#ifdef AFS_DEMAND_ATTACH_FS
-           /* 
+           /*
             * close the file handle
             * acquire VOL_LOCK
             * invalidate the vnode
@@ -804,26 +879,26 @@ VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
            if (fdP)
                FDH_CLOSE(fdP);
            VOL_LOCK;
-           VFreeBitMapEntry_r(ec, &vp->vnodeIndex[class], bitNumber);
+           VFreeBitMapEntry_r(&tmp, vp, &vp->vnodeIndex[class], bitNumber, 0 /*flags*/);
            VInvalidateVnode_r(vnp);
            VnUnlock(vnp, WRITE_LOCK);
            VnCancelReservation_r(vnp);
+#ifdef AFS_DEMAND_ATTACH_FS
            VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0);
            VCancelReservation_r(vp);
-           return NULL;
 #else
-           assert(1 == 2);
+           VForceOffline_r(vp, 0);
 #endif
-
+           return NULL;
        }
     sane:
-       VNLog(4, 2, vnodeNumber, (afs_int32) vnp);
+       VNLog(4, 2, vnodeNumber, (intptr_t)vnp, 0, 0);
 #ifndef AFS_DEMAND_ATTACH_FS
        AddToVnHash(vnp);
 #endif
     }
 
-    VNLog(5, 1, (afs_int32) vnp);
+    VNLog(5, 1, (intptr_t)vnp, 0, 0, 0);
     memset(&vnp->disk, 0, sizeof(vnp->disk));
     vnp->changed_newTime = 0;  /* set this bit when vnode is updated */
     vnp->changed_oldTime = 0;  /* set this on CopyOnWrite. */
@@ -833,7 +908,7 @@ VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
     vnp->disk.uniquifier = unique;
     vnp->handle = NULL;
     vcp->allocs++;
-    vp->header->diskstuff.filecount++;
+    V_filecount(vp)++;
 #ifdef AFS_DEMAND_ATTACH_FS
     VnChangeState_r(vnp, VN_STATE_EXCLUSIVE);
 #endif
@@ -859,14 +934,16 @@ VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
  * @internal vnode package internal use only
  */
 static void
-VnLoad(Error * ec, Volume * vp, Vnode * vnp, 
+VnLoad(Error * ec, Volume * vp, Vnode * vnp,
        struct VnodeClassInfo * vcp, VnodeClass class)
 {
     /* vnode not cached */
     Error error;
-    int n, dosalv = 1;
+    int dosalv = 1;
+    ssize_t nBytes;
     IHandle_t *ihP = vp->vnodeIndex[class].handle;
     FdHandle_t *fdP;
+    afs_ino_str_t stmp;
 
     *ec = 0;
     vcp->reads++;
@@ -882,32 +959,27 @@ VnLoad(Error * ec, Volume * vp, Vnode * vnp,
     fdP = IH_OPEN(ihP);
     if (fdP == NULL) {
        Log("VnLoad: can't open index dev=%u, i=%s\n", vp->device,
-           PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
+           PrintInode(stmp, vp->vnodeIndex[class].handle->ih_ino));
        *ec = VIO;
        goto error_encountered_nolock;
-    } else if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, Vn_id(vnp)), SEEK_SET)
-              < 0) {
-       Log("VnLoad: can't seek on index file vn=%u\n", Vn_id(vnp));
-       *ec = VIO;
-       goto error_encountered_nolock;
-    } else if ((n = FDH_READ(fdP, (char *)&vnp->disk, vcp->diskSize))
+    } else if ((nBytes = FDH_PREAD(fdP, (char *)&vnp->disk, vcp->diskSize, vnodeIndexOffset(vcp, Vn_id(vnp))))
               != vcp->diskSize) {
        /* Don't take volume off line if the inumber is out of range
         * or the inode table is full. */
-       if (n == BAD_IGET) {
+       if (nBytes == BAD_IGET) {
            Log("VnLoad: bad inumber %s\n",
-               PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
+               PrintInode(stmp, vp->vnodeIndex[class].handle->ih_ino));
            *ec = VIO;
            dosalv = 0;
-       } else if (n == -1 && errno == EIO) {
+       } else if (nBytes == -1 && errno == EIO) {
            /* disk error; salvage */
-           Log("VnLoad: Couldn't read vnode %u, volume %u (%s); volume needs salvage\n", Vn_id(vnp), V_id(vp), V_name(vp));
+           Log("VnLoad: Couldn't read vnode %u, volume %" AFS_VOLID_FMT " (%s); volume needs salvage\n", Vn_id(vnp), afs_printable_VolumeId_lu(V_id(vp)), V_name(vp));
        } else {
            /* vnode is not allocated */
-           if (LogLevel >= 5) 
-               Log("VnLoad: Couldn't read vnode %u, volume %u (%s); read %d bytes, errno %d\n", 
-                   Vn_id(vnp), V_id(vp), V_name(vp), n, errno);
-           *ec = VIO;
+           if (LogLevel >= 5)
+               Log("VnLoad: Couldn't read vnode %u, volume %" AFS_VOLID_FMT " (%s); read %d bytes, errno %d\n",
+                   Vn_id(vnp), afs_printable_VolumeId_lu(V_id(vp)), V_name(vp), (int)nBytes, errno);
+           *ec = VNOVNODE;
            dosalv = 0;
        }
        goto error_encountered_nolock;
@@ -925,21 +997,27 @@ VnLoad(Error * ec, Volume * vp, Vnode * vnp,
            unsigned int bitNumber = vnodeIdToBitNumber(Vn_id(vnp));
            unsigned int offset = bitNumber >> 3;
 
+#ifdef AFS_DEMAND_ATTACH_FS
+           /* Make sure the volume bitmap isn't getting updated while we are
+            * checking it */
+           VWaitExclusiveState_r(vp);
+#endif
+
            /* Test to see if vnode number is valid. */
            if ((offset >= index->bitmapSize)
                || ((*(index->bitmap + offset) & (1 << (bitNumber & 0x7)))
                    == 0)) {
-               Log("VnLoad: Request for unallocated vnode %u, volume %u (%s) denied.\n", Vn_id(vnp), V_id(vp), V_name(vp));
+               Log("VnLoad: Request for unallocated vnode %u, volume %" AFS_VOLID_FMT " (%s) denied.\n", Vn_id(vnp), afs_printable_VolumeId_lu(V_id(vp)), V_name(vp));
                *ec = VNOVNODE;
                dosalv = 0;
            } else {
-               Log("VnLoad: Bad magic number, vnode %u, volume %u (%s); volume needs salvage\n", Vn_id(vnp), V_id(vp), V_name(vp));
+               Log("VnLoad: Bad magic number, vnode %u, volume %" AFS_VOLID_FMT " (%s); volume needs salvage\n", Vn_id(vnp), afs_printable_VolumeId_lu(V_id(vp)), V_name(vp));
            }
        }
        goto error_encountered;
     }
 
-    IH_INIT(vnp->handle, V_device(vp), V_parentId(vp), VN_GET_INO(vnp));
+    IH_INIT(vnp->handle, V_device(vp), afs_printable_VolumeId_lu(V_parentId(vp)), VN_GET_INO(vnp));
     VnUnlock(vnp, WRITE_LOCK);
 #ifdef AFS_DEMAND_ATTACH_FS
     VnChangeState_r(vnp, VN_STATE_ONLINE);
@@ -987,12 +1065,14 @@ VnLoad(Error * ec, Volume * vp, Vnode * vnp,
  * @internal vnode package internal use only
  */
 static void
-VnStore(Error * ec, Volume * vp, Vnode * vnp, 
+VnStore(Error * ec, Volume * vp, Vnode * vnp,
        struct VnodeClassInfo * vcp, VnodeClass class)
 {
-    int offset, code;
+    ssize_t nBytes;
+    afs_foff_t offset;
     IHandle_t *ihP = vp->vnodeIndex[class].handle;
     FdHandle_t *fdP;
+    afs_ino_str_t stmp;
 #ifdef AFS_DEMAND_ATTACH_FS
     VnState vn_state_save;
 #endif
@@ -1010,21 +1090,15 @@ VnStore(Error * ec, Volume * vp, Vnode * vnp,
        Log("VnStore: can't open index file!\n");
        goto error_encountered;
     }
-    if (FDH_SEEK(fdP, offset, SEEK_SET) < 0) {
-       Log("VnStore: can't seek on index file! fdp=0x%x offset=%d, errno=%d\n",
-           fdP, offset, errno);
-       goto error_encountered;
-    }
-
-    code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
-    if (code != vcp->diskSize) {
+    nBytes = FDH_PWRITE(fdP, &vnp->disk, vcp->diskSize, offset);
+    if (nBytes != vcp->diskSize) {
        /* Don't force volume offline if the inumber is out of
         * range or the inode table is full.
         */
        FDH_REALLYCLOSE(fdP);
-       if (code == BAD_IGET) {
+       if (nBytes == BAD_IGET) {
            Log("VnStore: bad inumber %s\n",
-               PrintInode(NULL,
+               PrintInode(stmp,
                           vp->vnodeIndex[class].handle->ih_ino));
            *ec = VIO;
            VOL_LOCK;
@@ -1032,7 +1106,7 @@ VnStore(Error * ec, Volume * vp, Vnode * vnp,
            VnChangeState_r(vnp, VN_STATE_ERROR);
 #endif
        } else {
-           Log("VnStore: Couldn't write vnode %u, volume %u (%s) (error %d)\n", Vn_id(vnp), V_id(Vn_volume(vnp)), V_name(Vn_volume(vnp)), code);
+           Log("VnStore: Couldn't write vnode %u, volume %" AFS_VOLID_FMT " (%s) (error %d)\n", Vn_id(vnp), afs_printable_VolumeId_lu(V_id(Vn_volume(vnp))), V_name(Vn_volume(vnp)), (int)nBytes);
 #ifdef AFS_DEMAND_ATTACH_FS
            goto error_encountered;
 #else
@@ -1051,7 +1125,7 @@ VnStore(Error * ec, Volume * vp, Vnode * vnp,
     VnChangeState_r(vnp, vn_state_save);
 #endif
     return;
-    
+
  error_encountered:
 #ifdef AFS_DEMAND_ATTACH_FS
     /* XXX instead of dumping core, let's try to request a salvage
@@ -1062,7 +1136,7 @@ VnStore(Error * ec, Volume * vp, Vnode * vnp,
     VnChangeState_r(vnp, VN_STATE_ERROR);
     VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0);
 #else
-    assert(1 == 2);
+    opr_abort();
 #endif
 }
 
@@ -1106,11 +1180,9 @@ VGetVnode(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
 Vnode *
 VGetVnode_r(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
 {                              /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
-    register Vnode *vnp;
-    int code;
+    Vnode *vnp;
     VnodeClass class;
     struct VnodeClassInfo *vcp;
-    Volume * oldvp = NULL;
 
     *ec = 0;
 
@@ -1119,7 +1191,7 @@ VGetVnode_r(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
        return NULL;
     }
 
-    VNLog(100, 1, vnodeNumber);
+    VNLog(100, 1, vnodeNumber, 0, 0, 0);
 
 #ifdef AFS_DEMAND_ATTACH_FS
     /*
@@ -1167,7 +1239,7 @@ VGetVnode_r(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
     if (vnp) {
        /* vnode is in cache */
 
-       VNLog(101, 2, vnodeNumber, (afs_int32) vnp);
+       VNLog(101, 2, vnodeNumber, (intptr_t)vnp, 0, 0);
        VnCreateReservation_r(vnp);
 
 #ifdef AFS_DEMAND_ATTACH_FS
@@ -1203,17 +1275,11 @@ VGetVnode_r(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
        /* Not in cache; tentatively grab most distantly used one from the LRU
         * chain */
        vcp->reads++;
-       vnp = VGetFreeVnode_r(vcp);
+       vnp = VGetFreeVnode_r(vcp, vp, vnodeNumber);
 
        /* Initialize */
        vnp->changed_newTime = vnp->changed_oldTime = 0;
        vnp->delete = 0;
-       Vn_id(vnp) = vnodeNumber;
-       VnCreateReservation_r(vnp);
-       AddToVVnList(vp, vnp);
-#ifdef AFS_DEMAND_ATTACH_FS
-       AddToVnHash(vnp);
-#endif
 
        /*
         * XXX for non-DAFS, there is a serious
@@ -1254,7 +1320,7 @@ VGetVnode_r(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
 
     /* Check that the vnode hasn't been removed while we were obtaining
      * the lock */
-    VNLog(102, 2, vnodeNumber, (afs_int32) vnp);
+    VNLog(102, 2, vnodeNumber, (intptr_t) vnp, 0, 0);
     if ((vnp->disk.type == vNull) || (Vn_cacheCheck(vnp) == 0)) {
        VnUnlock(vnp, locktype);
        VnCancelReservation_r(vnp);
@@ -1282,7 +1348,7 @@ int TrustVnodeCacheEntry = 1;
 /* This variable is bogus--when it's set to 0, the hash chains fill
    up with multiple versions of the same vnode.  Should fix this!! */
 void
-VPutVnode(Error * ec, register Vnode * vnp)
+VPutVnode(Error * ec, Vnode * vnp)
 {
     VOL_LOCK;
     VPutVnode_r(ec, vnp);
@@ -1305,19 +1371,18 @@ VPutVnode(Error * ec, register Vnode * vnp)
  * @internal volume package internal use only
  */
 void
-VPutVnode_r(Error * ec, register Vnode * vnp)
+VPutVnode_r(Error * ec, Vnode * vnp)
 {
     int writeLocked;
     VnodeClass class;
     struct VnodeClassInfo *vcp;
-    int code;
 
     *ec = 0;
-    assert(Vn_refcount(vnp) != 0);
+    opr_Assert(Vn_refcount(vnp) != 0);
     class = vnodeIdToClass(Vn_id(vnp));
     vcp = &VnodeClassInfo[class];
-    assert(vnp->disk.vnodeMagic == vcp->magic);
-    VNLog(200, 2, Vn_id(vnp), (afs_int32) vnp);
+    opr_Assert(vnp->disk.vnodeMagic == vcp->magic);
+    VNLog(200, 2, Vn_id(vnp), (intptr_t) vnp, 0, 0);
 
 #ifdef AFS_DEMAND_ATTACH_FS
     writeLocked = (Vn_state(vnp) == VN_STATE_EXCLUSIVE);
@@ -1333,32 +1398,32 @@ VPutVnode_r(Error * ec, register Vnode * vnp)
        PROCESS thisProcess;
        LWP_CurrentProcess(&thisProcess);
 #endif /* AFS_PTHREAD_ENV */
-       VNLog(201, 2, (afs_int32) vnp,
+       VNLog(201, 2, (intptr_t) vnp,
              ((vnp->changed_newTime) << 1) | ((vnp->
                                                changed_oldTime) << 1) | vnp->
-             delete);
+             delete, 0, 0);
        if (thisProcess != vnp->writer)
-           Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",
+           Abort("VPutVnode: Vnode at %"AFS_PTR_FMT" locked by another process!\n",
                  vnp);
 
 
        if (vnp->changed_oldTime || vnp->changed_newTime || vnp->delete) {
            Volume *vp = Vn_volume(vnp);
            afs_uint32 now = FT_ApproxTime();
-           assert(Vn_cacheCheck(vnp) == vp->cacheCheck);
+           opr_Assert(Vn_cacheCheck(vnp) == vp->cacheCheck);
 
            if (vnp->delete) {
                /* No longer any directory entries for this vnode. Free the Vnode */
                memset(&vnp->disk, 0, sizeof(vnp->disk));
                /* delete flag turned off further down */
-               VNLog(202, 2, Vn_id(vnp), (afs_int32) vnp);
+               VNLog(202, 2, Vn_id(vnp), (intptr_t) vnp, 0, 0);
            } else if (vnp->changed_newTime) {
                vnp->disk.serverModifyTime = now;
            }
            if (vnp->changed_newTime)
            {
                V_updateDate(vp) = vp->updateTime = now;
-               if(V_volUpCounter(vp)<MAXINT)
+               if(V_volUpCounter(vp)< UINT_MAX)
                        V_volUpCounter(vp)++;
            }
 
@@ -1367,7 +1432,7 @@ VPutVnode_r(Error * ec, register Vnode * vnp)
 #ifdef AFS_DEMAND_ATTACH_FS
                VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0);
 #else
-               assert(V_needsSalvaged(vp));
+               opr_Assert(V_needsSalvaged(vp));
                *ec = VSALVAGE;
 #endif
            } else {
@@ -1379,10 +1444,11 @@ VPutVnode_r(Error * ec, register Vnode * vnp)
                 * (doing so could cause a "addled bitmap" message).
                 */
                if (vnp->delete && !*ec) {
-                   if (Vn_volume(vnp)->header->diskstuff.filecount-- < 1)
-                       Vn_volume(vnp)->header->diskstuff.filecount = 0;
-                   VFreeBitMapEntry_r(ec, &vp->vnodeIndex[class],
-                                      vnodeIdToBitNumber(Vn_id(vnp)));
+                 if (V_filecount(Vn_volume(vnp))-- < 1)
+                     V_filecount(Vn_volume(vnp)) = 0;
+                   VFreeBitMapEntry_r(ec, vp, &vp->vnodeIndex[class],
+                                      vnodeIdToBitNumber(Vn_id(vnp)),
+                                      VOL_FREE_BITMAP_WAIT);
                }
            }
            vcp->writes++;
@@ -1394,7 +1460,8 @@ VPutVnode_r(Error * ec, register Vnode * vnp)
     } else {                   /* Not write locked */
        if (vnp->changed_newTime || vnp->changed_oldTime || vnp->delete)
            Abort
-               ("VPutVnode: Change or delete flag for vnode 0x%x is set but vnode is not write locked!\n",
+               ("VPutVnode: Change or delete flag for vnode "
+                "%"AFS_PTR_FMT" is set but vnode is not write locked!\n",
                 vnp);
 #ifdef AFS_DEMAND_ATTACH_FS
        VnEndRead_r(vnp);
@@ -1414,7 +1481,7 @@ VPutVnode_r(Error * ec, register Vnode * vnp)
  * been deleted.
  */
 int
-VVnodeWriteToRead(Error * ec, register Vnode * vnp)
+VVnodeWriteToRead(Error * ec, Vnode * vnp)
 {
     int retVal;
     VOL_LOCK;
@@ -1441,12 +1508,11 @@ VVnodeWriteToRead(Error * ec, register Vnode * vnp)
  * @internal volume package internal use only
  */
 int
-VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
+VVnodeWriteToRead_r(Error * ec, Vnode * vnp)
 {
     int writeLocked;
     VnodeClass class;
     struct VnodeClassInfo *vcp;
-    int code;
 #ifdef AFS_PTHREAD_ENV
     pthread_t thisProcess;
 #else /* AFS_PTHREAD_ENV */
@@ -1454,11 +1520,11 @@ VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
 #endif /* AFS_PTHREAD_ENV */
 
     *ec = 0;
-    assert(Vn_refcount(vnp) != 0);
+    opr_Assert(Vn_refcount(vnp) != 0);
     class = vnodeIdToClass(Vn_id(vnp));
     vcp = &VnodeClassInfo[class];
-    assert(vnp->disk.vnodeMagic == vcp->magic);
-    VNLog(300, 2, Vn_id(vnp), (afs_int32) vnp);
+    opr_Assert(vnp->disk.vnodeMagic == vcp->magic);
+    VNLog(300, 2, Vn_id(vnp), (intptr_t) vnp, 0, 0);
 
 #ifdef AFS_DEMAND_ATTACH_FS
     writeLocked = (Vn_state(vnp) == VN_STATE_EXCLUSIVE);
@@ -1470,10 +1536,10 @@ VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
     }
 
 
-    VNLog(301, 2, (afs_int32) vnp,
+    VNLog(301, 2, (intptr_t) vnp,
          ((vnp->changed_newTime) << 1) | ((vnp->
                                            changed_oldTime) << 1) | vnp->
-         delete);
+         delete, 0, 0);
 
     /* sanity checks */
 #ifdef AFS_PTHREAD_ENV
@@ -1482,8 +1548,8 @@ VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
     LWP_CurrentProcess(&thisProcess);
 #endif /* AFS_PTHREAD_ENV */
     if (thisProcess != vnp->writer)
-       Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",
-             (int)vnp);
+       Abort("VPutVnode: Vnode at %"AFS_PTR_FMT
+             " locked by another process!\n", vnp);
 
     if (vnp->delete) {
        return 0;
@@ -1491,7 +1557,7 @@ VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
     if (vnp->changed_oldTime || vnp->changed_newTime) {
        Volume *vp = Vn_volume(vnp);
        afs_uint32 now = FT_ApproxTime();
-       assert(Vn_cacheCheck(vnp) == vp->cacheCheck);
+       opr_Assert(Vn_cacheCheck(vnp) == vp->cacheCheck);
        if (vnp->changed_newTime)
            vnp->disk.serverModifyTime = now;
        if (vnp->changed_newTime)
@@ -1502,13 +1568,12 @@ VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
 #ifdef AFS_DEMAND_ATTACH_FS
            VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0);
 #else
-           assert(V_needsSalvaged(vp));
+           opr_Assert(V_needsSalvaged(vp));
            *ec = VSALVAGE;
 #endif
        } else {
            VnStore(ec, vp, vnp, vcp, class);
        }
-    sane:
        vcp->writes++;
        vnp->changed_newTime = vnp->changed_oldTime = 0;
     }
@@ -1523,6 +1588,112 @@ VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
     return 0;
 }
 
+/**
+ * initial size of ihandle pointer vector.
+ *
+ * @see VInvalidateVnodesByVolume_r
+ */
+#define IH_VEC_BASE_SIZE 256
+
+/**
+ * increment amount for growing ihandle pointer vector.
+ *
+ * @see VInvalidateVnodesByVolume_r
+ */
+#define IH_VEC_INCREMENT 256
+
+/**
+ * Compile list of ihandles to be released/reallyclosed at a later time.
+ *
+ * @param[in]   vp            volume object pointer
+ * @param[out]  vec_out       vector of ihandle pointers to be released/reallyclosed
+ * @param[out]  vec_len_out   number of valid elements in ihandle vector
+ *
+ * @pre - VOL_LOCK is held
+ *      - volume is in appropriate exclusive state (e.g. VOL_STATE_VNODE_CLOSE,
+ *        VOL_STATE_VNODE_RELEASE)
+ *
+ * @post - all vnodes on VVn list are invalidated
+ *       - ih_vec is populated with all valid ihandles
+ *
+ * @return operation status
+ *    @retval 0         success
+ *    @retval ENOMEM    out of memory
+ *
+ * @todo we should handle out of memory conditions more gracefully.
+ *
+ * @internal vnode package internal use only
+ */
+static int
+VInvalidateVnodesByVolume_r(Volume * vp,
+                           IHandle_t *** vec_out,
+                           size_t * vec_len_out)
+{
+    int ret = 0;
+    Vnode *vnp, *nvnp;
+    size_t i = 0, vec_len;
+    IHandle_t **ih_vec, **ih_vec_new;
+
+#ifdef AFS_DEMAND_ATTACH_FS
+    VOL_UNLOCK;
+#endif /* AFS_DEMAND_ATTACH_FS */
+
+    vec_len = IH_VEC_BASE_SIZE;
+    ih_vec = malloc(sizeof(IHandle_t *) * vec_len);
+#ifdef AFS_DEMAND_ATTACH_FS
+    VOL_LOCK;
+#endif
+    if (ih_vec == NULL)
+       return ENOMEM;
+
+    /*
+     * Traverse the volume's vnode list.  Pull all the ihandles out into a
+     * thread-private array for later asynchronous processing.
+     */
+#ifdef AFS_DEMAND_ATTACH_FS
+restart_traversal:
+#endif
+    for (queue_Scan(&vp->vnode_list, vnp, nvnp, Vnode)) {
+       if (vnp->handle != NULL) {
+           if (i == vec_len) {
+#ifdef AFS_DEMAND_ATTACH_FS
+               VOL_UNLOCK;
+#endif
+               vec_len += IH_VEC_INCREMENT;
+               ih_vec_new = realloc(ih_vec, sizeof(IHandle_t *) * vec_len);
+#ifdef AFS_DEMAND_ATTACH_FS
+               VOL_LOCK;
+#endif
+               if (ih_vec_new == NULL) {
+                   ret = ENOMEM;
+                   goto done;
+               }
+               ih_vec = ih_vec_new;
+#ifdef AFS_DEMAND_ATTACH_FS
+               /*
+                * Theoretically, the volume's VVn list should not change
+                * because the volume is in an exclusive state.  For the
+                * sake of safety, we will restart the traversal from the
+                * the beginning (which is not expensive because we're
+                * deleting the items from the list as we go).
+                */
+               goto restart_traversal;
+#endif
+           }
+           ih_vec[i++] = vnp->handle;
+           vnp->handle = NULL;
+       }
+       DeleteFromVVnList(vnp);
+       VInvalidateVnode_r(vnp);
+    }
+
+ done:
+    *vec_out = ih_vec;
+    *vec_len_out = i;
+
+    return ret;
+}
+
 /* VCloseVnodeFiles - called when a volume is going off line. All open
  * files for vnodes in that volume are closed. This might be excessive,
  * since we may only be taking one volume of a volume group offline.
@@ -1530,26 +1701,43 @@ VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
 void
 VCloseVnodeFiles_r(Volume * vp)
 {
-    int i;
-    Vnode *vnp, *nvnp;
 #ifdef AFS_DEMAND_ATTACH_FS
     VolState vol_state_save;
+#endif
+    IHandle_t ** ih_vec;
+    size_t i, vec_len;
 
+#ifdef AFS_DEMAND_ATTACH_FS
     vol_state_save = VChangeState_r(vp, VOL_STATE_VNODE_CLOSE);
-    VOL_UNLOCK;
 #endif /* AFS_DEMAND_ATTACH_FS */
 
-    for (queue_Scan(&vp->vnode_list, vnp, nvnp, Vnode)) {
-       IH_REALLYCLOSE(vnp->handle);
-       DeleteFromVVnList(vnp);
+    /* XXX need better error handling here */
+    opr_Verify(VInvalidateVnodesByVolume_r(vp, &ih_vec,
+                                          &vec_len) == 0);
+
+    /*
+     * DAFS:
+     * now we drop VOL_LOCK while we perform some potentially very
+     * expensive operations in the background
+     */
+#ifdef AFS_DEMAND_ATTACH_FS
+    VOL_UNLOCK;
+#endif
+
+    for (i = 0; i < vec_len; i++) {
+       IH_REALLYCLOSE(ih_vec[i]);
+        IH_RELEASE(ih_vec[i]);
     }
 
+    free(ih_vec);
+
 #ifdef AFS_DEMAND_ATTACH_FS
     VOL_LOCK;
     VChangeState_r(vp, vol_state_save);
 #endif /* AFS_DEMAND_ATTACH_FS */
 }
 
+
 /**
  * shut down all vnode cache state for a given volume.
  *
@@ -1567,25 +1755,45 @@ VCloseVnodeFiles_r(Volume * vp)
  *       during this exclusive operation.  This is due to the fact that we are
  *       generally called during the refcount 1->0 transition.
  *
+ * @todo we should handle failures in VInvalidateVnodesByVolume_r more
+ *       gracefully.
+ *
+ * @see VInvalidateVnodesByVolume_r
+ *
  * @internal this routine is internal to the volume package
  */
 void
 VReleaseVnodeFiles_r(Volume * vp)
 {
-    int i;
-    Vnode *vnp, *nvnp;
 #ifdef AFS_DEMAND_ATTACH_FS
     VolState vol_state_save;
+#endif
+    IHandle_t ** ih_vec;
+    size_t i, vec_len;
 
+#ifdef AFS_DEMAND_ATTACH_FS
     vol_state_save = VChangeState_r(vp, VOL_STATE_VNODE_RELEASE);
-    VOL_UNLOCK;
 #endif /* AFS_DEMAND_ATTACH_FS */
 
-    for (queue_Scan(&vp->vnode_list, vnp, nvnp, Vnode)) {
-       IH_RELEASE(vnp->handle);
-       DeleteFromVVnList(vnp);
+    /* XXX need better error handling here */
+    opr_Verify(VInvalidateVnodesByVolume_r(vp, &ih_vec,
+                                          &vec_len) == 0);
+
+    /*
+     * DAFS:
+     * now we drop VOL_LOCK while we perform some potentially very
+     * expensive operations in the background
+     */
+#ifdef AFS_DEMAND_ATTACH_FS
+    VOL_UNLOCK;
+#endif
+
+    for (i = 0; i < vec_len; i++) {
+       IH_RELEASE(ih_vec[i]);
     }
 
+    free(ih_vec);
+
 #ifdef AFS_DEMAND_ATTACH_FS
     VOL_LOCK;
     VChangeState_r(vp, vol_state_save);