DAFS: Avoid SALVSYNC communication during shutdown
[openafs.git] / src / vol / volume.c
index 0ab72f5..313cb42 100644 (file)
@@ -6,7 +6,7 @@
  * License.  For details, see the LICENSE file in the top-level source
  * directory or online at http://www.openafs.org/dl/license10.html
  *
- * Portions Copyright (c) 2006 Sine Nomine Associates
+ * Portions Copyright (c) 2005-2008 Sine Nomine Associates
  */
 
 /* 1/1/89: NB:  this stuff is all going to be replaced.  Don't take it too seriously */
@@ -21,8 +21,6 @@
 #include <afsconfig.h>
 #include <afs/param.h>
 
-RCSID
-    ("$Header$");
 
 #include <rx/xdr.h>
 #include <afs/afsint.h>
@@ -123,6 +121,7 @@ RCSID
 #include "vnode.h"
 #include "volume.h"
 #include "partition.h"
+#include "volume_inline.h"
 #ifdef AFS_PTHREAD_ENV
 #include <assert.h>
 #else /* AFS_PTHREAD_ENV */
@@ -158,6 +157,12 @@ int vol_attach_threads = 1;
 
 #ifdef AFS_DEMAND_ATTACH_FS
 pthread_mutex_t vol_salvsync_mutex;
+
+/*
+ * Set this to 1 to disallow SALVSYNC communication in all threads; used
+ * during shutdown, since the salvageserver may have gone away.
+ */
+static volatile sig_atomic_t vol_disallow_salvsync = 0;
 #endif /* AFS_DEMAND_ATTACH_FS */
 
 #ifdef AFS_OSF_ENV
@@ -169,7 +174,7 @@ extern void *calloc(), *realloc();
 /* Forward declarations */
 static Volume *attach2(Error * ec, VolId vid, char *path,
                       register struct VolumeHeader *header,
-                      struct DiskPartition *partp, Volume * vp, 
+                      struct DiskPartition64 *partp, Volume * vp, 
                       int isbusy, int mode);
 static void ReallyFreeVolume(Volume * vp);
 #ifdef AFS_DEMAND_ATTACH_FS
@@ -184,18 +189,20 @@ static void ReleaseVolumeHeader(register struct volHeader *hd);
 static void FreeVolumeHeader(register Volume * vp);
 static void AddVolumeToHashTable(register Volume * vp, int hashid);
 static void DeleteVolumeFromHashTable(register Volume * vp);
+#if 0
 static int VHold(Volume * vp);
+#endif
 static int VHold_r(Volume * vp);
 static void VGetBitmap_r(Error * ec, Volume * vp, VnodeClass class);
-static void GetVolumePath(Error * ec, VolId volumeId, char **partitionp,
-                         char **namep);
 static void VReleaseVolumeHandles_r(Volume * vp);
 static void VCloseVolumeHandles_r(Volume * vp);
 static void LoadVolumeHeader(Error * ec, Volume * vp);
 static int VCheckOffline(register Volume * vp);
 static int VCheckDetach(register Volume * vp);
 static Volume * GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flags);
+#ifdef AFS_DEMAND_ATTACH_FS
 static int VolumeExternalName_r(VolumeId volumeId, char * name, size_t len);
+#endif
 
 int LogLevel;                  /* Vice loglevel--not defined as extern so that it will be
                                 * defined when not linked with vice, XXXX */
@@ -204,6 +211,10 @@ ProgramType programType;   /* The type of program using the package */
 /* extended volume package statistics */
 VolPkgStats VStats;
 
+#ifdef VOL_LOCK_DEBUG
+pthread_t vol_glock_holder = 0;
+#endif
+
 
 #define VOLUME_BITMAP_GROWSIZE 16      /* bytes, => 128vnodes */
                                        /* Must be a multiple of 4 (1 word) !! */
@@ -273,7 +284,7 @@ ffs(x)
 #ifdef AFS_PTHREAD_ENV
 typedef struct diskpartition_queue_t {
     struct rx_queue queue;
-    struct DiskPartition * diskP;
+    struct DiskPartition64 * diskP;
 } diskpartition_queue_t;
 typedef struct vinitvolumepackage_thread_t {
     struct rx_queue queue;
@@ -283,7 +294,7 @@ typedef struct vinitvolumepackage_thread_t {
 static void * VInitVolumePackageThread(void * args);
 #endif /* AFS_PTHREAD_ENV */
 
-static int VAttachVolumesByPartition(struct DiskPartition *diskP, 
+static int VAttachVolumesByPartition(struct DiskPartition64 *diskP, 
                                     int * nAttached, int * nUnattached);
 
 
@@ -340,15 +351,16 @@ static int VCheckFree(Volume * vp);
 /* VByP List */
 static void AddVolumeToVByPList_r(Volume * vp);
 static void DeleteVolumeFromVByPList_r(Volume * vp);
-static void VVByPListBeginExclusive_r(struct DiskPartition * dp);
-static void VVByPListEndExclusive_r(struct DiskPartition * dp);
-static void VVByPListWait_r(struct DiskPartition * dp);
+static void VVByPListBeginExclusive_r(struct DiskPartition64 * dp);
+static void VVByPListEndExclusive_r(struct DiskPartition64 * dp);
+static void VVByPListWait_r(struct DiskPartition64 * dp);
 
 /* online salvager */
 static int VCheckSalvage(register Volume * vp);
 static int VUpdateSalvagePriority_r(Volume * vp);
+#ifdef SALVSYNC_BUILD_CLIENT
 static int VScheduleSalvage_r(Volume * vp);
-static int VCancelSalvage_r(Volume * vp, int reason);
+#endif
 
 /* Volume hash table */
 static void VReorderHash_r(VolumeHashChainHead * head, Volume * pp, Volume * vp);
@@ -356,18 +368,9 @@ static void VHashBeginExclusive_r(VolumeHashChainHead * head);
 static void VHashEndExclusive_r(VolumeHashChainHead * head);
 static void VHashWait_r(VolumeHashChainHead * head);
 
-/* Volume state machine */
-static void VCreateReservation_r(Volume * vp);
-static void VCancelReservation_r(Volume * vp);
-static void VWaitStateChange_r(Volume * vp);
-static void VWaitExclusiveState_r(Volume * vp);
-static int IsExclusiveState(VolState state);
-static int IsErrorState(VolState state);
-static int IsValidState(VolState state);
-
 /* shutdown */
-static int ShutdownVByPForPass_r(struct DiskPartition * dp, int pass);
-static int ShutdownVolumeWalk_r(struct DiskPartition * dp, int pass,
+static int ShutdownVByPForPass_r(struct DiskPartition64 * dp, int pass);
+static int ShutdownVolumeWalk_r(struct DiskPartition64 * dp, int pass,
                                struct rx_queue ** idx);
 static void ShutdownController(vshutdown_thread_t * params);
 static void ShutdownCreateSchedule(vshutdown_thread_t * params);
@@ -375,20 +378,26 @@ static void ShutdownCreateSchedule(vshutdown_thread_t * params);
 /* VLRU */
 static void VLRU_ComputeConstants(void);
 static void VInitVLRU(void);
-static void VLRU_Init_Node_r(volatile Volume * vp);
-static void VLRU_Add_r(volatile Volume * vp);
-static void VLRU_Delete_r(volatile Volume * vp);
-static void VLRU_UpdateAccess_r(volatile Volume * vp);
+static void VLRU_Init_Node_r(Volume * vp);
+static void VLRU_Add_r(Volume * vp);
+static void VLRU_Delete_r(Volume * vp);
+static void VLRU_UpdateAccess_r(Volume * vp);
 static void * VLRU_ScannerThread(void * args);
 static void VLRU_Scan_r(int idx);
 static void VLRU_Promote_r(int idx);
 static void VLRU_Demote_r(int idx);
-static void VLRU_SwitchQueues(volatile Volume * vp, int new_idx, int append);
+static void VLRU_SwitchQueues(Volume * vp, int new_idx, int append);
 
 /* soft detach */
-static int VCheckSoftDetach(volatile Volume * vp, afs_uint32 thresh);
-static int VCheckSoftDetachCandidate(volatile Volume * vp, afs_uint32 thresh);
-static int VSoftDetachVolume_r(volatile Volume * vp, afs_uint32 thresh);
+static int VCheckSoftDetach(Volume * vp, afs_uint32 thresh);
+static int VCheckSoftDetachCandidate(Volume * vp, afs_uint32 thresh);
+static int VSoftDetachVolume_r(Volume * vp, afs_uint32 thresh);
+
+
+pthread_key_t VThread_key;
+VThreadOptions_t VThread_defaults = {
+    0                           /**< allow salvsync */
+};
 #endif /* AFS_DEMAND_ATTACH_FS */
 
 
@@ -434,20 +443,18 @@ VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVno
 
     programType = pt;
 
-#ifdef AFS_DEMAND_ATTACH_FS
     memset(&VStats, 0, sizeof(VStats));
     VStats.hdr_cache_size = 200;
-#endif
 
     VInitPartitionPackage();
     VInitVolumeHash();
-    VInitVnHashByVolume();
 #ifdef AFS_DEMAND_ATTACH_FS
     if (programType == fileServer) {
        VInitVLRU();
     } else {
        VLRU_SetOptions(VLRU_SET_ENABLED, 0);
     }
+    assert(pthread_key_create(&VThread_key, NULL) == 0);
 #endif
 
 #ifdef AFS_PTHREAD_ENV
@@ -504,7 +511,7 @@ VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVno
        return -1;
 
     if (programType == fileServer) {
-       struct DiskPartition *diskP;
+       struct DiskPartition64 *diskP;
 #ifdef AFS_PTHREAD_ENV
        struct vinitvolumepackage_thread_t params;
        struct diskpartition_queue_t * dpq;
@@ -548,7 +555,7 @@ VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVno
            }
 
            while(params.n_threads_complete < threads) {
-               pthread_cond_wait(&params.thread_done_cv,&vol_glock_mutex);
+               VOL_CV_WAIT(&params.thread_done_cv);
            }
            VOL_UNLOCK;
 
@@ -571,8 +578,6 @@ VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVno
        assert(pthread_cond_destroy(&params.thread_done_cv) == 0);
 
 #else /* AFS_PTHREAD_ENV */
-       DIR *dirp;
-       struct dirent *dp;
 
        /* Attach all the volumes in this partition */
        for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
@@ -586,8 +591,8 @@ VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVno
 #ifdef FSSYNC_BUILD_CLIENT
     if (programType == volumeUtility && connect) {
        if (!VConnectFS()) {
-           Log("Unable to connect to file server; aborted\n");
-           exit(1);
+           Log("Unable to connect to file server; will retry at need\n");
+           /*exit(1);*/
        }
     }
 #ifdef AFS_DEMAND_ATTACH_FS
@@ -605,11 +610,8 @@ VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVno
 #ifdef AFS_PTHREAD_ENV
 static void *
 VInitVolumePackageThread(void * args) {
-    int errors = 0;            /* Number of errors while finding vice partitions. */
 
-    DIR *dirp;
-    struct dirent *dp;
-    struct DiskPartition *diskP;
+    struct DiskPartition64 *diskP;
     struct vinitvolumepackage_thread_t * params;
     struct diskpartition_queue_t * dpq;
 
@@ -643,7 +645,7 @@ VInitVolumePackageThread(void * args) {
  * attach all volumes on a given disk partition
  */
 static int
-VAttachVolumesByPartition(struct DiskPartition *diskP, int * nAttached, int * nUnattached)
+VAttachVolumesByPartition(struct DiskPartition64 *diskP, int * nAttached, int * nUnattached)
 {
   DIR * dirp;
   struct dirent * dp;
@@ -663,8 +665,7 @@ VAttachVolumesByPartition(struct DiskPartition *diskP, int * nAttached, int * nU
       Error error;
       Volume *vp;
 #ifdef AFS_DEMAND_ATTACH_FS
-      vp = VPreAttachVolumeByName(&error, diskP->name, dp->d_name,
-                                  V_VOLUPD);
+      vp = VPreAttachVolumeByName(&error, diskP->name, dp->d_name);
 #else /* AFS_DEMAND_ATTACH_FS */
       vp = VAttachVolumeByName(&error, diskP->name, dp->d_name,
                               V_VOLUPD);
@@ -748,14 +749,13 @@ VAttachVolumesByPartition(struct DiskPartition *diskP, int * nAttached, int * nU
  *   shutdown all remaining volumes
  */
 
+#ifdef AFS_DEMAND_ATTACH_FS
+
 void
 VShutdown_r(void)
 {
     int i;
-    register Volume *vp, *np;
-    register afs_int32 code;
-#ifdef AFS_DEMAND_ATTACH_FS
-    struct DiskPartition * diskP;
+    struct DiskPartition64 * diskP;
     struct diskpartition_queue_t * dpq;
     vshutdown_thread_t params;
     pthread_t tid;
@@ -806,7 +806,7 @@ VShutdown_r(void)
            dpq->diskP = diskP;
            queue_Prepend(&params, dpq);
 
-           params.part_pass_head[diskP->device] = queue_First(&diskP->vol_list, rx_queue);
+           params.part_pass_head[diskP->index] = queue_First(&diskP->vol_list, rx_queue);
        }
 
        Log("VShutdown:  beginning parallel fileserver shutdown\n");
@@ -838,7 +838,7 @@ VShutdown_r(void)
        
        /* wait for all the workers to finish pass 3 and terminate */
        while (params.pass < 4) {
-           assert(pthread_cond_wait(&params.cv, &vol_glock_mutex) == 0);
+           VOL_CV_WAIT(&params.cv);
        }
        
        assert(pthread_attr_destroy(&attrs) == 0);
@@ -851,10 +851,10 @@ VShutdown_r(void)
            VVByPListEndExclusive_r(diskP);
            Log("VShutdown:  %s stats : (pass[0]=%d, pass[1]=%d, pass[2]=%d, pass[3]=%d)\n",
                VPartitionPath(diskP),
-               params.stats[0][diskP->device],
-               params.stats[1][diskP->device],
-               params.stats[2][diskP->device],
-               params.stats[3][diskP->device]);
+               params.stats[0][diskP->index],
+               params.stats[1][diskP->index],
+               params.stats[2][diskP->index],
+               params.stats[3][diskP->index]);
        }
 
        Log("VShutdown:  shutdown finished using %d threads\n", params.n_threads);
@@ -869,7 +869,16 @@ VShutdown_r(void)
     }
 
     Log("VShutdown:  complete.\n");
+}
+
 #else /* AFS_DEMAND_ATTACH_FS */
+
+void
+VShutdown_r(void)
+{
+    int i;
+    register Volume *vp, *np;
+    register afs_int32 code;
     Log("VShutdown:  shutting down on-line volumes...\n");
     for (i = 0; i < VolumeHashTable.Size; i++) {
        /* try to hold first volume in the hash table */
@@ -886,8 +895,9 @@ VShutdown_r(void)
        }
     }
     Log("VShutdown:  complete.\n");
-#endif /* AFS_DEMAND_ATTACH_FS */
 }
+#endif /* AFS_DEMAND_ATTACH_FS */
+
 
 void
 VShutdown(void)
@@ -897,6 +907,26 @@ VShutdown(void)
     VOL_UNLOCK;
 }
 
+/**
+ * stop new activity (e.g. SALVSYNC) from occurring
+ *
+ * Use this to make the volume package less busy; for example, during
+ * shutdown. This doesn't actually shutdown/detach anything in the
+ * volume package, but prevents certain processes from ocurring. For
+ * example, preventing new SALVSYNC communication in DAFS. In theory, we
+ * could also use this to prevent new volume attachment, or prevent
+ * other programs from checking out volumes, etc.
+ */
+void
+VSetTranquil(void)
+{
+#ifdef AFS_DEMAND_ATTACH_FS
+    /* make sure we don't try to contact the salvageserver, since it may
+     * not be around anymore */
+    vol_disallow_salvsync = 1;
+#endif
+}
+
 #ifdef AFS_DEMAND_ATTACH_FS
 /*
  * demand attach fs
@@ -906,7 +936,7 @@ static void
 ShutdownController(vshutdown_thread_t * params)
 {
     /* XXX debug */
-    struct DiskPartition * diskP;
+    struct DiskPartition64 * diskP;
     Device id;
     vshutdown_thread_t shadow;
 
@@ -925,7 +955,7 @@ ShutdownController(vshutdown_thread_t * params)
        Log("ShutdownController:  n_threads_complete=%d, n_parts_done_pass=%d\n",
            shadow.n_threads_complete, shadow.n_parts_done_pass);
        for (diskP = DiskPartitionList; diskP; diskP=diskP->next) {
-           id = diskP->device;
+           id = diskP->index;
            Log("ShutdownController:  part[%d] : (len=%d, thread_target=%d, done_pass=%d, pass_head=%p)\n",
                id, 
                diskP->vol_list.len,
@@ -951,7 +981,7 @@ ShutdownController(vshutdown_thread_t * params)
 static void
 ShutdownCreateSchedule(vshutdown_thread_t * params)
 {
-    struct DiskPartition * diskP;
+    struct DiskPartition64 * diskP;
     int sum, thr_workload, thr_left;
     int part_residue[VOLMAXPARTS+1];
     Device id;
@@ -979,7 +1009,7 @@ ShutdownCreateSchedule(vshutdown_thread_t * params)
     /* for fairness, give every partition with volumes remaining
      * at least one thread */
     for (diskP = DiskPartitionList; diskP && thr_left; diskP = diskP->next) {
-       id = diskP->device;
+       id = diskP->index;
        if (diskP->vol_list.len) {
            params->part_thread_target[id] = 1;
            thr_left--;
@@ -993,7 +1023,7 @@ ShutdownCreateSchedule(vshutdown_thread_t * params)
        int delta;
 
        for (diskP = DiskPartitionList; diskP && thr_left; diskP = diskP->next) {
-           id = diskP->device;
+           id = diskP->index;
            delta = (diskP->vol_list.len / thr_workload) -
                params->part_thread_target[id];
            if (delta < 0) {
@@ -1013,11 +1043,11 @@ ShutdownCreateSchedule(vshutdown_thread_t * params)
     if (thr_left) {
        /* try to assign any leftover threads to partitions that
         * had volume lengths closer to needing thread_target+1 */
-       int max_residue, max_id;
+       int max_residue, max_id = 0;
 
        /* compute the residues */
        for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
-           id = diskP->device;
+           id = diskP->index;
            part_residue[id] = diskP->vol_list.len - 
                (params->part_thread_target[id] * thr_workload);
        }
@@ -1027,7 +1057,7 @@ ShutdownCreateSchedule(vshutdown_thread_t * params)
        while (thr_left) {
            max_residue = 0;
            for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
-               id = diskP->device;
+               id = diskP->index;
                if (part_residue[id] > max_residue) {
                    max_residue = part_residue[id];
                    max_id = id;
@@ -1050,7 +1080,7 @@ ShutdownCreateSchedule(vshutdown_thread_t * params)
        if (thr_left >= params->n_parts) {
            alloc = thr_left / params->n_parts;
            for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
-               id = diskP->device;
+               id = diskP->index;
                params->part_thread_target[id] += alloc;
                thr_left -= alloc;
            }
@@ -1058,7 +1088,7 @@ ShutdownCreateSchedule(vshutdown_thread_t * params)
 
        /* finish off the last of the threads */
        for (diskP = DiskPartitionList; thr_left && diskP; diskP = diskP->next) {
-           id = diskP->device;
+           id = diskP->index;
            params->part_thread_target[id]++;
            thr_left--;
        }
@@ -1069,11 +1099,9 @@ ShutdownCreateSchedule(vshutdown_thread_t * params)
 static void *
 VShutdownThread(void * args)
 {
-    struct rx_queue *qp;
-    Volume * vp;
     vshutdown_thread_t * params;
-    int part, code, found, pass, schedule_version_save, count;
-    struct DiskPartition *diskP;
+    int found, pass, schedule_version_save, count;
+    struct DiskPartition64 *diskP;
     struct diskpartition_queue_t * dpq;
     Device id;
 
@@ -1090,12 +1118,12 @@ VShutdownThread(void * args)
        assert(pthread_mutex_unlock(&params->lock) == 0);
        diskP = dpq->diskP;
        free(dpq);
-       id = diskP->device;
+       id = diskP->index;
 
        count = 0;
        while (ShutdownVolumeWalk_r(diskP, 0, &params->part_pass_head[id]))
            count++;
-       params->stats[0][diskP->device] = count;
+       params->stats[0][diskP->index] = count;
        assert(pthread_mutex_lock(&params->lock) == 0);
     }
 
@@ -1121,7 +1149,7 @@ VShutdownThread(void * args)
        found = 0;
        /* find a disk partition to work on */
        for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
-           id = diskP->device;
+           id = diskP->index;
            if (params->part_thread_target[id] && !params->part_done_pass[id]) {
                params->part_thread_target[id]--;
                found = 1;
@@ -1133,7 +1161,7 @@ VShutdownThread(void * args)
            /* hmm. for some reason the controller thread couldn't find anything for 
             * us to do. let's see if there's anything we can do */
            for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
-               id = diskP->device;
+               id = diskP->index;
                if (diskP->vol_list.len && !params->part_done_pass[id]) {
                    found = 1;
                    break;
@@ -1184,7 +1212,7 @@ VShutdownThread(void * args)
                    params->n_parts_done_pass = 0;
                    params->pass++;
                    for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
-                       id = diskP->device;
+                       id = diskP->index;
                        params->part_done_pass[id] = 0;
                        params->part_pass_head[id] = queue_First(&diskP->vol_list, rx_queue);
                    }
@@ -1200,7 +1228,7 @@ VShutdownThread(void * args)
                        pass, params->n_threads, params->n_parts);
                    VOL_LOCK;
                } else {
-                   assert(pthread_cond_wait(&params->cv, &vol_glock_mutex) == 0);
+                   VOL_CV_WAIT(&params->cv);
                }
            }
            pass = params->pass;
@@ -1222,9 +1250,9 @@ VShutdownThread(void * args)
  * note that this function will not allow mp-fast
  * shutdown of a partition */
 int
-VShutdownByPartition_r(struct DiskPartition * dp)
+VShutdownByPartition_r(struct DiskPartition64 * dp)
 {
-    int pass, retVal;
+    int pass;
     int pass_stats[4];
     int total;
 
@@ -1249,7 +1277,7 @@ VShutdownByPartition_r(struct DiskPartition * dp)
     Log("VShutdownByPartition:  shut down %d volumes on %s (pass[0]=%d, pass[1]=%d, pass[2]=%d, pass[3]=%d)\n",
        total, VPartitionPath(dp), pass_stats[0], pass_stats[1], pass_stats[2], pass_stats[3]);
 
-    return retVal;
+    return 0;
 }
 
 /* internal shutdown functionality
@@ -1273,7 +1301,7 @@ VShutdownByPartition_r(struct DiskPartition * dp)
  * traversal
  */
 static int
-ShutdownVByPForPass_r(struct DiskPartition * dp, int pass)
+ShutdownVByPForPass_r(struct DiskPartition64 * dp, int pass)
 {
     struct rx_queue * q = queue_First(&dp->vol_list, rx_queue);
     register int i = 0;
@@ -1288,7 +1316,7 @@ ShutdownVByPForPass_r(struct DiskPartition * dp, int pass)
  * returns 1 if a volume was shutdown in this pass,
  * 0 otherwise */
 static int
-ShutdownVolumeWalk_r(struct DiskPartition * dp, int pass,
+ShutdownVolumeWalk_r(struct DiskPartition64 * dp, int pass,
                     struct rx_queue ** idx)
 {
     struct rx_queue *qp, *nqp;
@@ -1312,7 +1340,7 @@ ShutdownVolumeWalk_r(struct DiskPartition * dp, int pass,
                break;
            }
        case 2:
-           if (IsExclusiveState(V_attachState(vp))) {
+           if (VIsExclusiveState(V_attachState(vp))) {
                break;
            }
        case 3:
@@ -1346,13 +1374,14 @@ VShutdownVolume_r(Volume * vp)
     /* wait for other blocking ops to finish */
     VWaitExclusiveState_r(vp);
 
-    assert(IsValidState(V_attachState(vp)));
+    assert(VIsValidState(V_attachState(vp)));
     
     switch(V_attachState(vp)) {
     case VOL_STATE_SALVAGING:
-       /* make sure salvager knows we don't want
-        * the volume back */
-       VCancelSalvage_r(vp, SALVSYNC_SHUTDOWN);
+       /* Leave salvaging volumes alone. Any in-progress salvages will
+        * continue working after viced shuts down. This is intentional.
+        */
+
     case VOL_STATE_PREATTACHED:
     case VOL_STATE_ERROR:
        VChangeState_r(vp, VOL_STATE_UNATTACHED);
@@ -1371,6 +1400,8 @@ VShutdownVolume_r(Volume * vp)
            VOffline_r(vp, "File server was shut down");
        }
        break;
+    default:
+       break;
     }
     
     VCancelReservation_r(vp);
@@ -1464,7 +1495,7 @@ void
 VolumeHeaderToDisk(VolumeDiskHeader_t * dh, VolumeHeader_t * h)
 {
 
-    memset((char *)dh, 0, sizeof(VolumeDiskHeader_t));
+    memset(dh, 0, sizeof(VolumeDiskHeader_t));
     dh->stamp = h->stamp;
     dh->id = h->id;
     dh->parent = h->parent;
@@ -1499,7 +1530,7 @@ VolumeHeaderToDisk(VolumeDiskHeader_t * dh, VolumeHeader_t * h)
 void
 DiskToVolumeHeader(VolumeHeader_t * h, VolumeDiskHeader_t * dh)
 {
-    memset((char *)h, 0, sizeof(VolumeHeader_t));
+    memset(h, 0, sizeof(VolumeHeader_t));
     h->stamp = dh->stamp;
     h->id = dh->id;
     h->parent = dh->parent;
@@ -1531,62 +1562,121 @@ DiskToVolumeHeader(VolumeHeader_t * h, VolumeDiskHeader_t * dh)
 /***************************************************/
 
 #ifdef AFS_DEMAND_ATTACH_FS
-/* pre-attach a volume given its path 
+/**
+ * pre-attach a volume given its path.
+ *
+ * @param[out] ec         outbound error code
+ * @param[in]  partition  partition path string
+ * @param[in]  name       volume id string
  *
- * a pre-attached volume will only have its partition
- * and hashid fields initialized
+ * @return volume object pointer
+ *
+ * @note A pre-attached volume will only have its partition
+ *       and hashid fields initialized.  At first call to 
+ *       VGetVolume, the volume will be fully attached.
  *
- * at first call to VGetVolume, the volume will be
- * fully attached
  */
 Volume *
-VPreAttachVolumeByName(Error * ec, char *partition, char *name, int mode)
+VPreAttachVolumeByName(Error * ec, char *partition, char *name)
 {
     Volume * vp;
     VOL_LOCK;
-    vp = VPreAttachVolumeByName_r(ec, partition, name, mode);
+    vp = VPreAttachVolumeByName_r(ec, partition, name);
     VOL_UNLOCK;
     return vp;
 }
 
+/**
+ * pre-attach a volume given its path.
+ *
+ * @param[out] ec         outbound error code
+ * @param[in]  partition  path to vice partition
+ * @param[in]  name       volume id string
+ *
+ * @return volume object pointer
+ *
+ * @pre VOL_LOCK held
+ *
+ * @internal volume package internal use only.
+ */
 Volume *
-VPreAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
+VPreAttachVolumeByName_r(Error * ec, char *partition, char *name)
 {
-    register Volume *vp = NULL;
-    int fd, n;
-    struct afs_stat status;
-    struct DiskPartition *partp;
-    char path[64];
-    int isbusy = 0;
-    VolId volumeId;
+    return VPreAttachVolumeById_r(ec, 
+                                 partition,
+                                 VolumeNumber(name));
+}
+
+/**
+ * pre-attach a volume given its path and numeric volume id.
+ *
+ * @param[out] ec          error code return
+ * @param[in]  partition   path to vice partition
+ * @param[in]  volumeId    numeric volume id
+ *
+ * @return volume object pointer
+ *
+ * @pre VOL_LOCK held
+ *
+ * @internal volume package internal use only.
+ */
+Volume *
+VPreAttachVolumeById_r(Error * ec, 
+                      char * partition,
+                      VolId volumeId)
+{
+    Volume *vp;
+    struct DiskPartition64 *partp;
+
     *ec = 0;
 
     assert(programType == fileServer);
 
     if (!(partp = VGetPartition_r(partition, 0))) {
        *ec = VNOVOL;
-       Log("VPreAttachVolume:  Error getting partition (%s)\n", partition);
+       Log("VPreAttachVolumeById_r:  Error getting partition (%s)\n", partition);
        return NULL;
     }
 
-    volumeId = VolumeNumber(name);
-
     vp = VLookupVolume_r(ec, volumeId, NULL);
     if (*ec) {
        return NULL;
     }
 
-    return VPreAttachVolumeById_r(ec, partp, vp, volumeId);
+    return VPreAttachVolumeByVp_r(ec, partp, vp, volumeId);
 }
 
-/* pre-attach a volume given its partition and volume id
+/**
+ * preattach a volume.
+ *
+ * @param[out] ec     outbound error code
+ * @param[in]  partp  pointer to partition object
+ * @param[in]  vp     pointer to volume object
+ * @param[in]  vid    volume id
+ *
+ * @return volume object pointer
+ *
+ * @pre VOL_LOCK is held.
+ *
+ * @warning Returned volume object pointer does not have to
+ *          equal the pointer passed in as argument vp.  There
+ *          are potential race conditions which can result in
+ *          the pointers having different values.  It is up to
+ *          the caller to make sure that references are handled
+ *          properly in this case.
  *
- * if vp == NULL, then a new vp is created
- * if vp != NULL, then we assumed it is already on the hash chain
+ * @note If there is already a volume object registered with
+ *       the same volume id, its pointer MUST be passed as 
+ *       argument vp.  Failure to do so will result in a silent
+ *       failure to preattach.
+ *
+ * @internal volume package internal use only.
  */
 Volume * 
-VPreAttachVolumeById_r(Error * ec, struct DiskPartition * partp, 
-                      Volume * vp, int vid)
+VPreAttachVolumeByVp_r(Error * ec, 
+                      struct DiskPartition64 * partp, 
+                      Volume * vp,
+                      VolId vid)
 {
     Volume *nvp = NULL;
 
@@ -1595,11 +1685,25 @@ VPreAttachVolumeById_r(Error * ec, struct DiskPartition * partp,
     /* check to see if pre-attach already happened */
     if (vp && 
        (V_attachState(vp) != VOL_STATE_UNATTACHED) && 
-       !IsErrorState(V_attachState(vp))) {
+       (V_attachState(vp) != VOL_STATE_PREATTACHED) &&
+       !VIsErrorState(V_attachState(vp))) {
+       /*
+        * pre-attach is a no-op in all but the following cases:
+        *
+        *   - volume is unattached
+        *   - volume is in an error state
+        *   - volume is pre-attached
+        */
+       Log("VPreattachVolumeByVp_r: volume %u not in quiescent state\n", vid);
        goto done;
     } else if (vp) {
        /* we're re-attaching a volume; clear out some old state */
        memset(&vp->salvage, 0, sizeof(struct VolumeOnlineSalvage));
+
+       if (V_partition(vp) != partp) {
+           /* XXX potential race */
+           DeleteVolumeFromVByPList_r(vp);
+       }
     } else {
        /* if we need to allocate a new Volume struct,
         * go ahead and drop the vol glock, otherwise
@@ -1611,13 +1715,16 @@ VPreAttachVolumeById_r(Error * ec, struct DiskPartition * partp,
        vp = nvp = (Volume *) malloc(sizeof(Volume));
        assert(vp != NULL);
        memset(vp, 0, sizeof(Volume));
+       queue_Init(&vp->vnode_list);
        assert(pthread_cond_init(&V_attachCV(vp), NULL) == 0);
     }
 
     /* link the volume with its associated vice partition */
     vp->device = partp->device;
     vp->partition = partp;
+
     vp->hashid = vid;
+    vp->specialStatus = 0;
 
     /* if we dropped the lock, reacquire the lock,
      * check for pre-attach races, and then add
@@ -1648,7 +1755,7 @@ VPreAttachVolumeById_r(Error * ec, struct DiskPartition * partp,
     VChangeState_r(vp, VOL_STATE_PREATTACHED);
 
     if (LogLevel >= 5)
-       Log("VPreAttachVolumeById_r:  volume %u pre-attached\n", vp->hashid);
+       Log("VPreAttachVolumeByVp_r:  volume %u pre-attached\n", vp->hashid);
 
   done:
     if (*ec)
@@ -1675,17 +1782,18 @@ VAttachVolumeByName(Error * ec, char *partition, char *name, int mode)
 Volume *
 VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
 {
-    register Volume *vp = NULL, *svp = NULL;
+    register Volume *vp = NULL;
     int fd, n;
     struct afs_stat status;
     struct VolumeDiskHeader diskHeader;
     struct VolumeHeader iheader;
-    struct DiskPartition *partp;
+    struct DiskPartition64 *partp;
     char path[64];
     int isbusy = 0;
     VolId volumeId;
 #ifdef AFS_DEMAND_ATTACH_FS
     VolumeStats stats_save;
+    Volume *svp = NULL;
 #endif /* AFS_DEMAND_ATTACH_FS */
 
     *ec = 0;
@@ -1726,12 +1834,12 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
            VWaitExclusiveState_r(vp);
 
            /* at this point state must be one of:
-            *   UNATTACHED,
-            *   ATTACHED,
-            *   SHUTTING_DOWN,
-            *   GOING_OFFLINE,
-            *   SALVAGING,
-            *   ERROR
+            *   - UNATTACHED
+            *   - ATTACHED
+            *   - SHUTTING_DOWN
+            *   - GOING_OFFLINE
+            *   - SALVAGING
+            *   - ERROR
             */
 
            if (vp->specialStatus == VBUSY)
@@ -1740,7 +1848,7 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
            /* if it's already attached, see if we can return it */
            if (V_attachState(vp) == VOL_STATE_ATTACHED) {
                VGetVolumeByVp_r(ec, vp);
-               if (V_inUse(vp)) {
+               if (V_inUse(vp) == fileServer) {
                    VCancelReservation_r(vp);
                    return vp;
                }
@@ -1766,7 +1874,7 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
            (V_attachState(vp) == VOL_STATE_UNATTACHED) ||
            (V_attachState(vp) == VOL_STATE_ERROR)) {
            svp = vp;
-           vp = VPreAttachVolumeById_r(ec, partp, vp, volumeId);
+           vp = VPreAttachVolumeByVp_r(ec, partp, vp, volumeId);
            if (*ec) {
                return NULL;
            }
@@ -1810,7 +1918,7 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
 #else /* AFS_DEMAND_ATTACH_FS */
        vp = VGetVolume_r(ec, volumeId);
        if (vp) {
-           if (V_inUse(vp))
+           if (V_inUse(vp) == fileServer)
                return vp;
            if (vp->specialStatus == VBUSY)
                isbusy = 1;
@@ -1873,6 +1981,7 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
       assert(vp != NULL);
       vp->device = partp->device;
       vp->partition = partp;
+      queue_Init(&vp->vnode_list);
 #ifdef AFS_DEMAND_ATTACH_FS
       assert(pthread_cond_init(&V_attachCV(vp), NULL) == 0);
 #endif /* AFS_DEMAND_ATTACH_FS */
@@ -1883,6 +1992,11 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
     vp = attach2(ec, volumeId, path, &iheader, partp, vp, isbusy, mode);
 
     if (programType == volumeUtility && vp) {
+       if ((mode == V_VOLUPD) || (VolumeWriteable(vp) && (mode == V_CLONE))) {
+           /* mark volume header as in use so that volser crashes lead to a
+            * salvage attempt */
+           VUpdateVolume_r(ec, vp, 0);
+       }
 #ifdef AFS_DEMAND_ATTACH_FS
        /* for dafs, we should tell the fileserver, except for V_PEEK
          * where we know it is not necessary */
@@ -1921,6 +2035,16 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
     } else 
 #endif
     if (programType == fileServer && vp) {
+#ifdef AFS_DEMAND_ATTACH_FS
+       /* 
+        * we can get here in cases where we don't "own"
+        * the volume (e.g. volume owned by a utility).
+        * short circuit around potential disk header races.
+        */
+       if (V_attachState(vp) != VOL_STATE_ATTACHED) {
+           goto done;
+       }
+#endif
        V_needsCallback(vp) = 0;
 #ifdef notdef
        if (VInit >= 2 && V_BreakVolumeCallbacks) {
@@ -1959,15 +2083,16 @@ VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
            Log("VOnline:  volume %u (%s) attached and online\n", V_id(vp),
                V_name(vp));
     }
+
   done:
     if (programType == volumeUtility) {
        VUnlockPartition_r(partition);
     }
     if (*ec) {
 #ifdef AFS_DEMAND_ATTACH_FS
-       if (vp) {
-           V_attachState(vp) = VOL_STATE_ERROR;
-           assert(pthread_cond_broadcast(&V_attachCV(vp)) == 0);
+       /* attach failed; make sure we're in error state */
+       if (vp && !VIsErrorState(V_attachState(vp))) {
+           VChangeState_r(vp, VOL_STATE_ERROR);
        }
 #endif /* AFS_DEMAND_ATTACH_FS */
        return NULL;
@@ -1991,11 +2116,11 @@ VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode)
     struct afs_stat status;
     struct VolumeDiskHeader diskHeader;
     struct VolumeHeader iheader;
-    struct DiskPartition *partp;
+    struct DiskPartition64 *partp;
     char path[64];
     int isbusy = 0;
     VolId volumeId;
-    Volume * nvp;
+    Volume * nvp = NULL;
     VolumeStats stats_save;
     *ec = 0;
 
@@ -2015,7 +2140,7 @@ VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode)
     /* if it's already attached, see if we can return it */
     if (V_attachState(vp) == VOL_STATE_ATTACHED) {
        VGetVolumeByVp_r(ec, vp);
-       if (V_inUse(vp)) {
+       if (V_inUse(vp) == fileServer) {
            return vp;
        } else {
            if (vp->specialStatus == VBUSY)
@@ -2032,7 +2157,7 @@ VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode)
     if (!vp || 
        (V_attachState(vp) == VOL_STATE_UNATTACHED) ||
        (V_attachState(vp) == VOL_STATE_ERROR)) {
-       nvp = VPreAttachVolumeById_r(ec, partp, vp, volumeId);
+       nvp = VPreAttachVolumeByVp_r(ec, partp, vp, volumeId);
        if (*ec) {
            return NULL;
        }
@@ -2094,7 +2219,15 @@ VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode)
      * with vol_glock_mutex held */
     vp = attach2(ec, volumeId, path, &iheader, partp, vp, isbusy, mode);
 
-    if (*ec || vp == NULL) {
+    /*
+     * the event that an error was encountered, or
+     * the volume was not brought to an attached state
+     * for any reason, skip to the end.  We cannot
+     * safely call VUpdateVolume unless we "own" it.
+     */
+    if (*ec || 
+       (vp == NULL) ||
+       (V_attachState(vp) != VOL_STATE_ATTACHED)) {
        goto done;
     }
 
@@ -2134,7 +2267,7 @@ VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode)
        reserve = 0;
     }
     if (*ec && (*ec != VOFFLINE) && (*ec != VSALVAGE)) {
-       if (vp && !IsErrorState(V_attachState(vp))) {
+       if (vp && !VIsErrorState(V_attachState(vp))) {
            VChangeState_r(vp, VOL_STATE_ERROR);
        }
        return NULL;
@@ -2150,7 +2283,7 @@ VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode)
  */
 private Volume * 
 attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * header,
-       struct DiskPartition * partp, register Volume * vp, int isbusy, int mode)
+       struct DiskPartition64 * partp, register Volume * vp, int isbusy, int mode)
 {
     vp->specialStatus = (byte) (isbusy ? VBUSY : 0);
     IH_INIT(vp->vnodeIndex[vLarge].handle, partp->device, header->parent,
@@ -2169,9 +2302,7 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
 #endif
 
     VOL_LOCK;
-#ifdef AFS_DEMAND_ATTACH_FS
     IncUInt64(&VStats.attaches);
-#endif
     vp->cacheCheck = ++VolumeCacheCheck;
     /* just in case this ever rolls over */
     if (!vp->cacheCheck)
@@ -2195,7 +2326,7 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
        res.payload.buf = &vp->header->diskstuff;
 
        if (FSYNC_VolOp(volumeId,
-                       VPartitionPath(partp),
+                       partp->name,
                        FSYNC_VOL_QUERY_HDR,
                        FSYNC_WHATEVER,
                        &res) == SYNC_OK) {
@@ -2218,15 +2349,31 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
        Log("VAttachVolume: Error reading diskDataHandle vol header %s; error=%u\n", path, *ec);
     }
 
- disk_header_loaded:
-
 #ifdef AFS_DEMAND_ATTACH_FS
+# ifdef FSSYNC_BUILD_CLIENT
+ disk_header_loaded:
+#endif
     if (!*ec) {
 
        /* check for pending volume operations */
        if (vp->pending_vol_op) {
            /* see if the pending volume op requires exclusive access */
-           if (!VVolOpLeaveOnline_r(vp, vp->pending_vol_op)) {
+           switch (vp->pending_vol_op->vol_op_state) {
+           case FSSYNC_VolOpPending:
+               /* this should never happen */
+               assert(vp->pending_vol_op->vol_op_state != FSSYNC_VolOpPending);
+               break;
+
+           case FSSYNC_VolOpRunningUnknown:
+               if (VVolOpLeaveOnline_r(vp, vp->pending_vol_op)) {
+                   vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningOnline;
+                   break;
+               } else {
+                   vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningOffline;
+                   /* fall through to take volume offline */
+               }
+
+           case FSSYNC_VolOpRunningOffline:
                /* mark the volume down */
                *ec = VOFFLINE;
                VChangeState_r(vp, VOL_STATE_UNATTACHED);
@@ -2240,10 +2387,13 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
                if (VVolOpSetVBusy_r(vp, vp->pending_vol_op)) {
                    vp->specialStatus = VBUSY;
                }
+           default:
+               break;
            }
        }
 
        V_attachFlags(vp) |= VOL_HDR_LOADED;
+       vp->stats.last_hdr_load = vp->stats.last_attach;
     }
 #endif /* AFS_DEMAND_ATTACH_FS */
 
@@ -2257,7 +2407,7 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
         * area and mark it as initialized.
         */
        if (!(V_stat_initialized(vp))) {
-           memset((char *)(V_stat_area(vp)), 0, VOL_STATS_BYTES);
+           memset((V_stat_area(vp)), 0, VOL_STATS_BYTES);
            V_stat_initialized(vp) = 1;
        }
 #endif /* OPENAFS_VOL_STATS */
@@ -2300,9 +2450,8 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
     if (*ec && ((*ec != VOFFLINE) || (V_attachState(vp) != VOL_STATE_UNATTACHED))) {
         VOL_LOCK;
        if (programType == fileServer) {
-           VRequestSalvage_r(vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
+           VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
            vp->nUsers = 0;
-           *ec = VSALVAGING;
        } else {
            Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%u\n", path, *ec);
            FreeVolume(vp);
@@ -2329,9 +2478,8 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
         VOL_LOCK;
 #if defined(AFS_DEMAND_ATTACH_FS)
        if (programType == fileServer) {
-           VRequestSalvage_r(vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
+           VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
            vp->nUsers = 0;
-           *ec = VSALVAGING;
        } else {
            Log("VAttachVolume: volume salvage flag is ON for %s; volume needs salvage\n", path);
            FreeVolume(vp);
@@ -2353,9 +2501,8 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
                VUpdateVolume_r(ec, vp, 0);
            }
 #if defined(AFS_DEMAND_ATTACH_FS)
-           VRequestSalvage_r(vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
+           VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
            vp->nUsers = 0;
-           *ec = VSALVAGING;
 #else /* AFS_DEMAND_ATTACH_FS */
            Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
            FreeVolume(vp);
@@ -2368,7 +2515,7 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
        if (V_destroyMe(vp) == DESTROY_ME) {
 #if defined(AFS_DEMAND_ATTACH_FS)
            /* schedule a salvage so the volume goes away on disk */
-           VRequestSalvage_r(vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
+           VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
            VChangeState_r(vp, VOL_STATE_ERROR);
            vp->nUsers = 0;
 #endif /* AFS_DEMAND_ATTACH_FS */
@@ -2388,9 +2535,8 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
            VGetBitmap_r(ec, vp, i);
            if (*ec) {
 #ifdef AFS_DEMAND_ATTACH_FS
-               VRequestSalvage_r(vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
+               VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
                vp->nUsers = 0;
-               *ec = VSALVAGING;
 #else /* AFS_DEMAND_ATTACH_FS */
                FreeVolume(vp);
 #endif /* AFS_DEMAND_ATTACH_FS */
@@ -2406,16 +2552,25 @@ attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * h
        if (vp->specialStatus)
            vp->specialStatus = 0;
        if (V_blessed(vp) && V_inService(vp) && !V_needsSalvaged(vp)) {
-           V_inUse(vp) = 1;
+           V_inUse(vp) = fileServer;
            V_offlineMessage(vp)[0] = '\0';
        }
+    } else {
+       if ((mode != V_PEEK) && (mode != V_SECRETLY))
+           V_inUse(vp) = programType;
+       V_checkoutMode(vp) = mode;
     }
 
     AddVolumeToHashTable(vp, V_id(vp));
 #ifdef AFS_DEMAND_ATTACH_FS
-    AddVolumeToVByPList_r(vp);
-    VLRU_Add_r(vp);
-    VChangeState_r(vp, VOL_STATE_ATTACHED);
+    if ((programType != fileServer) ||
+       (V_inUse(vp) == fileServer)) {
+       AddVolumeToVByPList_r(vp);
+       VLRU_Add_r(vp);
+       VChangeState_r(vp, VOL_STATE_ATTACHED);
+    } else {
+       VChangeState_r(vp, VOL_STATE_UNATTACHED);
+    }
 #endif
     return vp;
 }
@@ -2439,7 +2594,7 @@ Volume *
 VAttachVolume_r(Error * ec, VolumeId volumeId, int mode)
 {
     char *part, *name;
-    GetVolumePath(ec, volumeId, &part, &name);
+    VGetVolumePath(ec, volumeId, &part, &name);
     if (*ec) {
        register Volume *vp;
        Error error;
@@ -2495,6 +2650,7 @@ VHold_r(register Volume * vp)
 }
 #endif /* AFS_DEMAND_ATTACH_FS */
 
+#if 0
 static int
 VHold(register Volume * vp)
 {
@@ -2504,12 +2660,26 @@ VHold(register Volume * vp)
     VOL_UNLOCK;
     return retVal;
 }
+#endif
 
 
 /***************************************************/
 /* get and put volume routines                     */
 /***************************************************/
 
+/**
+ * put back a heavyweight reference to a volume object.
+ *
+ * @param[in] vp  volume object pointer
+ *
+ * @pre VOL_LOCK held
+ *
+ * @post heavyweight volume reference put back.
+ *       depending on state, volume may have been taken offline,
+ *       detached, salvaged, freed, etc.
+ *
+ * @internal volume package internal use only
+ */
 void
 VPutVolume_r(register Volume * vp)
 {
@@ -2583,10 +2753,25 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flag
 #else
 #define VGET_CTR_INC(x)
 #endif
-
 #ifdef AFS_DEMAND_ATTACH_FS
     Volume *avp, * rvp = hint;
+#endif
+
+    /* 
+     * if VInit is zero, the volume package dynamic
+     * data structures have not been initialized yet,
+     * and we must immediately return an error
+     */
+    if (VInit == 0) {
+       vp = NULL;
+       *ec = VOFFLINE;
+       if (client_ec) {
+           *client_ec = VOFFLINE;
+       }
+       goto not_inited;
+    }
 
+#ifdef AFS_DEMAND_ATTACH_FS
     if (rvp) {
        VCreateReservation_r(rvp);
     }
@@ -2643,22 +2828,36 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flag
 
        /* short circuit with VNOVOL in the following circumstances:
         *
-        *   VOL_STATE_ERROR
-        *   VOL_STATE_SHUTTING_DOWN
+        *   - VOL_STATE_ERROR
+        *   - VOL_STATE_SHUTTING_DOWN
         */
        if ((V_attachState(vp) == VOL_STATE_ERROR) ||
-           (V_attachState(vp) == VOL_STATE_SHUTTING_DOWN)) {
+           (V_attachState(vp) == VOL_STATE_SHUTTING_DOWN) ||
+           (V_attachState(vp) == VOL_STATE_GOING_OFFLINE)) {
            *ec = VNOVOL;
            vp = NULL;
            break;
        }
 
+       /*
+        * short circuit with VOFFLINE in the following circumstances:
+        *
+        *   - VOL_STATE_UNATTACHED
+        */
+       if (V_attachState(vp) == VOL_STATE_UNATTACHED) {
+          if (vp->specialStatus) {
+              *ec = vp->specialStatus;
+          } else {
+              *ec = VOFFLINE;
+          }
+           vp = NULL;
+           break;
+       }
+
        /* allowable states:
-        *   UNATTACHED
-        *   PREATTACHED
-        *   ATTACHED
-        *   GOING_OFFLINE
-        *   SALVAGING
+        *   - PREATTACHED
+        *   - ATTACHED
+        *   - SALVAGING
         */
 
        if (vp->salvage.requested) {
@@ -2717,30 +2916,8 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flag
            vp = NULL;
            break;
        }
+#endif
 
-       if (vp->pending_vol_op && !VVolOpLeaveOnline_r(vp, vp->pending_vol_op)) {
-           if (client_ec) {
-               /* see CheckVnode() in afsfileprocs.c for an explanation
-                * of this error code logic */
-               afs_uint32 now = FT_ApproxTime();
-               if ((vp->stats.last_vol_op + (10 * 60)) >= now) {
-                   *client_ec = VBUSY;
-               } else {
-                   *client_ec = VRESTARTING;
-               }
-           }
-           *ec = VOFFLINE;
-           vp = NULL;
-           break;
-       }
-
-       if (V_attachState(vp) == VOL_STATE_UNATTACHED) {
-           *ec = VOFFLINE;
-           vp = NULL;
-           break;
-       }
-#endif /* AFS_DEMAND_ATTACH_FS */
-       
        LoadVolumeHeader(ec, vp);
        if (*ec) {
            VGET_CTR_INC(V6);
@@ -2751,8 +2928,7 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flag
                    vp->hashid);
 #ifdef AFS_DEMAND_ATTACH_FS
            if (programType == fileServer) {
-               VRequestSalvage_r(vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
-               *ec = VSALVAGING;
+               VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
            } else {
                FreeVolume(vp);
                vp = NULL;
@@ -2764,6 +2940,48 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flag
            break;
        }
 
+#ifdef AFS_DEMAND_ATTACH_FS
+       /*
+        * this test MUST happen after the volume header is loaded
+        */
+        
+         /* only valid before/during demand attachment */
+         assert(!vp->pending_vol_op || vp->pending_vol_op->vol_op_state != FSSYNC_VolOpRunningUnknown);
+        
+         /* deny getvolume due to running mutually exclusive vol op */
+         if (vp->pending_vol_op && vp->pending_vol_op->vol_op_state==FSSYNC_VolOpRunningOffline) {
+          /* 
+           * volume cannot remain online during this volume operation.
+           * notify client. 
+           */
+          if (vp->specialStatus) {
+              /*
+               * special status codes outrank normal VOFFLINE code
+               */
+              *ec = vp->specialStatus;
+              if (client_ec) {
+                  *client_ec = vp->specialStatus;
+              }
+          } else {
+              if (client_ec) {
+                  /* see CheckVnode() in afsfileprocs.c for an explanation
+                   * of this error code logic */
+                  afs_uint32 now = FT_ApproxTime();
+                  if ((vp->stats.last_vol_op + (10 * 60)) >= now) {
+                      *client_ec = VBUSY;
+                  } else {
+                      *client_ec = VRESTARTING;
+                  }
+              }
+              *ec = VOFFLINE;
+          }
+          VChangeState_r(vp, VOL_STATE_UNATTACHED);
+          FreeVolumeHeader(vp);
+          vp = NULL;
+          break;
+       }
+#endif /* AFS_DEMAND_ATTACH_FS */
+       
        VGET_CTR_INC(V7);
        if (vp->shuttingDown) {
            VGET_CTR_INC(V8);
@@ -2782,7 +3000,7 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flag
                    VWaitStateChange_r(vp);
                }
 #elif defined(AFS_PTHREAD_ENV)
-               assert(pthread_cond_wait(&vol_put_volume_cond, &vol_glock_mutex) == 0);
+               VOL_CV_WAIT(&vol_put_volume_cond);
 #else /* AFS_PTHREAD_ENV */
                LWP_WaitProcess(VPutVolume);
 #endif /* AFS_PTHREAD_ENV */
@@ -2828,6 +3046,7 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flag
     }
 #endif /* AFS_DEMAND_ATTACH_FS */
 
+ not_inited:
     assert(vp || *ec);
     return vp;
 }
@@ -2842,6 +3061,8 @@ GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flag
 void
 VTakeOffline_r(register Volume * vp)
 {
+    Error error;
+
     assert(vp->nUsers > 0);
     assert(programType == fileServer);
 
@@ -2851,7 +3072,7 @@ VTakeOffline_r(register Volume * vp)
     vp->goingOffline = 1;
     V_needsSalvaged(vp) = 1;
 
-    VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
+    VRequestSalvage_r(&error, vp, SALVSYNC_ERROR, 0);
     VCancelReservation_r(vp);
 }
 #else /* AFS_DEMAND_ATTACH_FS */
@@ -2874,32 +3095,62 @@ VTakeOffline(register Volume * vp)
     VOL_UNLOCK;
 }
 
-/* Force the volume offline, set the salvage flag.  No further references to
- * the volume through the volume package will be honored. */
-/* for demand attach, caller MUST hold ref count on vp */
+/**
+ * force a volume offline.
+ *
+ * @param[in] vp     volume object pointer
+ * @param[in] flags  flags (see note below)
+ *
+ * @note the flag VOL_FORCEOFF_NOUPDATE is a recursion control flag
+ *       used when VUpdateVolume_r needs to call VForceOffline_r
+ *       (which in turn would normally call VUpdateVolume_r)
+ *
+ * @see VUpdateVolume_r
+ *
+ * @pre VOL_LOCK must be held.
+ *      for DAFS, caller must hold ref.
+ *
+ * @note for DAFS, it _is safe_ to call this function from an
+ *       exclusive state
+ *
+ * @post needsSalvaged flag is set.
+ *       for DAFS, salvage is requested.
+ *       no further references to the volume through the volume 
+ *       package will be honored.
+ *       all file descriptor and vnode caches are invalidated.
+ *
+ * @warning this is a heavy-handed interface.  it results in
+ *          a volume going offline regardless of the current 
+ *          reference count state.
+ *
+ * @internal  volume package internal use only
+ */
 void
 VForceOffline_r(Volume * vp, int flags)
 {
     Error error;
-    if (!V_inUse(vp))
+    if (!V_inUse(vp)) {
+#ifdef AFS_DEMAND_ATTACH_FS
+       VChangeState_r(vp, VOL_STATE_ERROR);
+#endif
        return;
+    }
+
     strcpy(V_offlineMessage(vp),
           "Forced offline due to internal error: volume needs to be salvaged");
     Log("Volume %u forced offline:  it needs salvaging!\n", V_id(vp));
+
     V_inUse(vp) = 0;
     vp->goingOffline = 0;
     V_needsSalvaged(vp) = 1;
     if (!(flags & VOL_FORCEOFF_NOUPDATE)) {
-       VUpdateVolume_r(&error, vp, VOL_UPDATE_WAIT | VOL_UPDATE_NOFORCEOFF);
+       VUpdateVolume_r(&error, vp, VOL_UPDATE_NOFORCEOFF);
     }
+
 #ifdef AFS_DEMAND_ATTACH_FS
-#ifdef SALVSYNC_BUILD_CLIENT
-    if (programType == fileServer) {
-       VRequestSalvage_r(vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
-    }
-#endif
-    VChangeState_r(vp, VOL_STATE_ERROR);
+    VRequestSalvage_r(&error, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
 #endif /* AFS_DEMAND_ATTACH_FS */
+
 #ifdef AFS_PTHREAD_ENV
     assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
 #else /* AFS_PTHREAD_ENV */
@@ -2909,6 +3160,13 @@ VForceOffline_r(Volume * vp, int flags)
     VReleaseVolumeHandles_r(vp);
 }
 
+/**
+ * force a volume offline.
+ *
+ * @param[in] vp  volume object pointer
+ *
+ * @see VForceOffline_r
+ */
 void
 VForceOffline(Volume * vp)
 {
@@ -2924,8 +3182,10 @@ VForceOffline(Volume * vp)
 void
 VOffline_r(Volume * vp, char *message)
 {
+#ifndef AFS_DEMAND_ATTACH_FS
     Error error;
     VolumeId vid = V_id(vp);
+#endif
 
     assert(programType != volumeUtility);
     if (!V_inUse(vp)) {
@@ -2955,6 +3215,67 @@ VOffline_r(Volume * vp, char *message)
 #endif /* AFS_DEMAND_ATTACH_FS */
 }
 
+#ifdef AFS_DEMAND_ATTACH_FS
+/**
+ * Take a volume offline in order to perform a volume operation.
+ *
+ * @param[inout] ec       address in which to store error code
+ * @param[in]    vp       volume object pointer
+ * @param[in]    message  volume offline status message
+ *
+ * @pre
+ *    - VOL_LOCK is held
+ *    - caller MUST hold a heavyweight ref on vp
+ *
+ * @post
+ *    - volume is taken offline
+ *    - if possible, volume operation is promoted to running state
+ *    - on failure, *ec is set to nonzero
+ *
+ * @note Although this function does not return any value, it may
+ *       still fail to promote our pending volume operation to
+ *       a running state.  Any caller MUST check the value of *ec,
+ *       and MUST NOT blindly assume success.
+ *
+ * @warning if the caller does not hold a lightweight ref on vp,
+ *          then it MUST NOT reference vp after this function
+ *          returns to the caller.
+ *
+ * @internal volume package internal use only
+ */
+void
+VOfflineForVolOp_r(Error *ec, Volume *vp, char *message)
+{
+    assert(vp->pending_vol_op);
+    if (!V_inUse(vp)) {
+       VPutVolume_r(vp);
+        *ec = 1;
+       return;
+    }
+    if (V_offlineMessage(vp)[0] == '\0')
+       strncpy(V_offlineMessage(vp), message, sizeof(V_offlineMessage(vp)));
+    V_offlineMessage(vp)[sizeof(V_offlineMessage(vp)) - 1] = '\0';
+
+    vp->goingOffline = 1;
+    VChangeState_r(vp, VOL_STATE_GOING_OFFLINE);
+    VCreateReservation_r(vp);
+    VPutVolume_r(vp);
+
+    /* Wait for the volume to go offline */
+    while (!VIsOfflineState(V_attachState(vp))) {
+        /* do not give corrupted volumes to the volserver */
+        if (vp->salvage.requested && vp->pending_vol_op->com.programType != salvageServer) {
+           *ec = 1; 
+          goto error;
+        }
+       VWaitStateChange_r(vp);
+    }
+    *ec = 0; 
+ error:
+    VCancelReservation_r(vp);
+}
+#endif /* AFS_DEMAND_ATTACH_FS */
+
 void
 VOffline(Volume * vp, char *message)
 {
@@ -2974,13 +3295,19 @@ void
 VDetachVolume_r(Error * ec, Volume * vp)
 {
     VolumeId volume;
-    struct DiskPartition *tpartp;
-    int notifyServer, useDone;
+    struct DiskPartition64 *tpartp;
+    int notifyServer = 0;
+    int  useDone = FSYNC_VOL_ON;
 
     *ec = 0;                   /* always "succeeds" */
     if (programType == volumeUtility) {
        notifyServer = vp->needsPutBack;
-       useDone = (V_destroyMe(vp) == DESTROY_ME);
+       if (V_destroyMe(vp) == DESTROY_ME)
+           useDone = FSYNC_VOL_DONE;
+#ifdef AFS_DEMAND_ATTACH_FS
+       else if (!V_blessed(vp) || !V_inService(vp))
+           useDone = FSYNC_VOL_LEAVE_OFF;
+#endif
     }
     tpartp = vp->partition;
     volume = V_id(vp);
@@ -2990,6 +3317,9 @@ VDetachVolume_r(Error * ec, Volume * vp)
     DeleteVolumeFromVByPList_r(vp);
     VLRU_Delete_r(vp);
     VChangeState_r(vp, VOL_STATE_SHUTTING_DOWN);
+#else
+    if (programType != fileServer) 
+       V_inUse(vp) = 0;
 #endif /* AFS_DEMAND_ATTACH_FS */
     VPutVolume_r(vp);
     /* Will be detached sometime in the future--this is OK since volume is offline */
@@ -3008,24 +3338,17 @@ VDetachVolume_r(Error * ec, Volume * vp)
         * would be two instances of the same volume, one of them bogus, 
         * which the file server would attempt to put on line 
         */
-       if (useDone) {
-           /* don't put online */
-           FSYNC_VolOp(volume, tpartp->name, FSYNC_VOL_DONE, 0, NULL);
-       } else {
-           /* fs can use it again */
-           FSYNC_VolOp(volume, tpartp->name, FSYNC_VOL_ON, 0, NULL);
-
-           /* XXX this code path is only hit by volume utilities, thus
-            * V_BreakVolumeCallbacks will always be NULL.  if we really
-            * want to break callbacks in this path we need to use FSYNC_VolOp() */
+       FSYNC_VolOp(volume, tpartp->name, useDone, 0, NULL);
+       /* XXX this code path is only hit by volume utilities, thus
+        * V_BreakVolumeCallbacks will always be NULL.  if we really
+        * want to break callbacks in this path we need to use FSYNC_VolOp() */
 #ifdef notdef
-           /* Dettaching it so break all callbacks on it */
-           if (V_BreakVolumeCallbacks) {
-               Log("volume %u detached; breaking all call backs\n", volume);
-               (*V_BreakVolumeCallbacks) (volume);
-           }
-#endif
+       /* Dettaching it so break all callbacks on it */
+       if (V_BreakVolumeCallbacks) {
+           Log("volume %u detached; breaking all call backs\n", volume);
+           (*V_BreakVolumeCallbacks) (volume);
        }
+#endif
     }
 #endif /* FSSYNC_BUILD_CLIENT */
 }
@@ -3282,12 +3605,26 @@ static int
 VCheckDetach(register Volume * vp)
 {
     int ret = 0;
+    Error ec = 0;
 
     if (vp->nUsers || vp->nWaiters)
        return ret;
 
     if (vp->shuttingDown) {
        ret = 1;
+       if ((programType != fileServer) &&
+           (V_inUse(vp) == programType) &&
+           ((V_checkoutMode(vp) == V_VOLUPD) ||
+            (V_checkoutMode(vp) == V_SECRETLY) ||
+            ((V_checkoutMode(vp) == V_CLONE) &&
+             (VolumeWriteable(vp))))) {
+           V_inUse(vp) = 0;
+           VUpdateVolume_r(&ec, vp, VOL_UPDATE_NOFORCEOFF);
+           if (ec) {
+               Log("VCheckDetach: volume header update for volume %u "
+                   "failed with errno %d\n", vp->hashid, errno);
+           }
+       }
        VReleaseVolumeHandles_r(vp);
        VCheckSalvage(vp);
        ReallyFreeVolume(vp);
@@ -3302,12 +3639,26 @@ static int
 VCheckDetach(register Volume * vp)
 {
     int ret = 0;
+    Error ec = 0;
 
     if (vp->nUsers)
        return ret;
 
     if (vp->shuttingDown) {
        ret = 1;
+       if ((programType != fileServer) &&
+           (V_inUse(vp) == programType) &&
+           ((V_checkoutMode(vp) == V_VOLUPD) ||
+            (V_checkoutMode(vp) == V_SECRETLY) ||
+            ((V_checkoutMode(vp) == V_CLONE) &&
+             (VolumeWriteable(vp))))) {
+           V_inUse(vp) = 0;
+           VUpdateVolume_r(&ec, vp, VOL_UPDATE_NOFORCEOFF);
+           if (ec) {
+               Log("VCheckDetach: volume header update for volume %u failed with errno %d\n",
+                   vp->hashid, errno);
+           }
+       }
        VReleaseVolumeHandles_r(vp);
        ReallyFreeVolume(vp);
        if (programType == fileServer) {
@@ -3328,7 +3679,6 @@ VCheckDetach(register Volume * vp)
 static int
 VCheckOffline(register Volume * vp)
 {
-    Volume * rvp = NULL;
     int ret = 0;
 
     if (vp->goingOffline && !vp->nUsers) {
@@ -3343,8 +3693,8 @@ VCheckOffline(register Volume * vp)
         *
         * VOL_STATE_GOING_OFFLINE
         * VOL_STATE_SHUTTING_DOWN
-        * IsErrorState(V_attachState(vp))
-        * IsExclusiveState(V_attachState(vp))
+        * VIsErrorState(V_attachState(vp))
+        * VIsExclusiveState(V_attachState(vp))
         */
 
        VCreateReservation_r(vp);
@@ -3374,7 +3724,7 @@ VCheckOffline(register Volume * vp)
 
        /* if nothing changed state to error or salvaging,
         * drop state to unattached */
-       if (!IsErrorState(V_attachState(vp))) {
+       if (!VIsErrorState(V_attachState(vp))) {
            VChangeState_r(vp, VOL_STATE_UNATTACHED);
        }
        VCancelReservation_r(vp);
@@ -3386,7 +3736,6 @@ VCheckOffline(register Volume * vp)
 static int
 VCheckOffline(register Volume * vp)
 {
-    Volume * rvp = NULL;
     int ret = 0;
 
     if (vp->goingOffline && !vp->nUsers) {
@@ -3428,14 +3777,34 @@ VCheckOffline(register Volume * vp)
  * from free()ing the Volume struct during an async i/o op */
 
 /* register with the async volume op ref counter */
-static void
-VCreateReservation_r(Volume * vp)
-{
-    vp->nWaiters++;
-}
+/* VCreateReservation_r moved into inline code header because it 
+ * is now needed in vnode.c -- tkeiser 11/20/2007 
+ */
 
-/* unregister with the async volume op ref counter */
-static void
+/**
+ * decrement volume-package internal refcount.
+ *
+ * @param vp  volume object pointer
+ *
+ * @internal volume package internal use only
+ *
+ * @pre 
+ *    @arg VOL_LOCK is held
+ *    @arg lightweight refcount held
+ *
+ * @post volume waiters refcount is decremented; volume may
+ *       have been deallocated/shutdown/offlined/salvaged/
+ *       whatever during the process
+ *
+ * @warning once you have tossed your last reference (you can acquire
+ *          lightweight refs recursively) it is NOT SAFE to reference
+ *          a volume object pointer ever again
+ *
+ * @see VCreateReservation_r
+ *
+ * @note DEMAND_ATTACH_FS only
+ */
+void
 VCancelReservation_r(Volume * vp)
 {
     assert(--vp->nWaiters >= 0);
@@ -3473,9 +3842,24 @@ VCheckFree(Volume * vp)
 /***************************************************/
 
 #ifdef AFS_DEMAND_ATTACH_FS
-int
-VRegisterVolOp_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
-{
+/**
+ * register a volume operation on a given volume.
+ *
+ * @param[in] vp       volume object
+ * @param[in] vopinfo  volume operation info object
+ *
+ * @pre VOL_LOCK is held
+ *
+ * @post volume operation info object attached to volume object.
+ *       volume operation statistics updated.
+ *
+ * @note by "attached" we mean a copy of the passed in object is made
+ *
+ * @internal volume package internal use only
+ */
+int
+VRegisterVolOp_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
+{
     FSSYNC_VolOp_info * info;
 
     /* attach a vol op info node to the volume struct */
@@ -3492,8 +3876,19 @@ VRegisterVolOp_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
     return 0;
 }
 
+/**
+ * deregister the volume operation attached to this volume.
+ *
+ * @param[in] vp  volume object pointer
+ *
+ * @pre VOL_LOCK is held
+ *
+ * @post the volume operation info object is detached from the volume object
+ *
+ * @internal volume package internal use only
+ */
 int
-VDeregisterVolOp_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
+VDeregisterVolOp_r(Volume * vp)
 {
     if (vp->pending_vol_op) {
        free(vp->pending_vol_op);
@@ -3503,22 +3898,57 @@ VDeregisterVolOp_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
 }
 #endif /* AFS_DEMAND_ATTACH_FS */
 
+/**
+ * determine whether it is safe to leave a volume online during
+ * the volume operation described by the vopinfo object.
+ *
+ * @param[in] vp        volume object
+ * @param[in] vopinfo   volume operation info object
+ *
+ * @return whether it is safe to leave volume online
+ *    @retval 0  it is NOT SAFE to leave the volume online
+ *    @retval 1  it is safe to leave the volume online during the operation
+ *
+ * @pre
+ *    @arg VOL_LOCK is held
+ *    @arg disk header attached to vp (heavyweight ref on vp will guarantee
+ *         this condition is met)
+ *
+ * @internal volume package internal use only
+ */
 int
 VVolOpLeaveOnline_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
 {
-    return (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
+    return (vopinfo->vol_op_state == FSSYNC_VolOpRunningOnline ||
+           (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
            (vopinfo->com.reason == V_READONLY ||
             (!VolumeWriteable(vp) &&
              (vopinfo->com.reason == V_CLONE ||
-              vopinfo->com.reason == V_DUMP))));
+              vopinfo->com.reason == V_DUMP)))));
 }
 
+/**
+ * determine whether VBUSY should be set during this volume operation.
+ *
+ * @param[in] vp        volume object
+ * @param[in] vopinfo   volume operation info object
+ *
+ * @return whether VBUSY should be set
+ *   @retval 0  VBUSY does NOT need to be set
+ *   @retval 1  VBUSY SHOULD be set
+ *
+ * @pre VOL_LOCK is held
+ *
+ * @internal volume package internal use only
+ */
 int
 VVolOpSetVBusy_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
 {
-    return (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
+    return ((vopinfo->com.command == FSYNC_VOL_OFF &&
+           vopinfo->com.reason == FSYNC_SALVAGE) ||
+           (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
            (vopinfo->com.reason == V_CLONE ||
-            vopinfo->com.reason == V_DUMP));
+            vopinfo->com.reason == V_DUMP)));
 }
 
 
@@ -3526,19 +3956,37 @@ VVolOpSetVBusy_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
 /* online salvager routines                        */
 /***************************************************/
 #if defined(AFS_DEMAND_ATTACH_FS)
-#define SALVAGE_PRIO_UPDATE_INTERVAL 3      /* number of seconds between prio updates */
-#define SALVAGE_COUNT_MAX 16                /* number of online salvages we
-                                            * allow before moving the volume
-                                            * into a permanent error state
+#define SALVAGE_PRIO_UPDATE_INTERVAL 3      /**< number of seconds between prio updates */
+#define SALVAGE_COUNT_MAX 16                /**< number of online salvages we
+                                            *   allow before moving the volume
+                                            *   into a permanent error state
                                             *
-                                            * once this threshold is reached,
-                                            * the operator will have to manually
-                                            * issue a 'bos salvage' to bring
-                                            * the volume back online
+                                            *   once this threshold is reached,
+                                            *   the operator will have to manually
+                                            *   issue a 'bos salvage' to bring
+                                            *   the volume back online
                                             */
 
-/* check to see if we should salvage this volume
- * returns 1 if salvage scheduled, 0 otherwise */
+/**
+ * check whether a salvage needs to be performed on this volume.
+ *
+ * @param[in] vp   pointer to volume object
+ *
+ * @return status code
+ *    @retval 0 no salvage scheduled
+ *    @retval 1 a salvage has been scheduled with the salvageserver
+ *
+ * @pre VOL_LOCK is held
+ *
+ * @post if salvage request flag is set and nUsers and nWaiters are zero,
+ *       then a salvage will be requested
+ *
+ * @note this is one of the event handlers called by VCancelReservation_r
+ *
+ * @see VCancelReservation_r
+ *
+ * @internal volume package internal use only.
+ */
 static int
 VCheckSalvage(register Volume * vp)
 {
@@ -3554,45 +4002,132 @@ VCheckSalvage(register Volume * vp)
     return ret;
 }
 
-/*
- * request that a salvage be performed once
- * ref counts reach zero
+/**
+ * request volume salvage.
+ *
+ * @param[out] ec      computed client error code
+ * @param[in]  vp      volume object pointer
+ * @param[in]  reason  reason code (passed to salvageserver via SALVSYNC)
+ * @param[in]  flags   see flags note below
+ *
+ * @note flags:
+ *       VOL_SALVAGE_INVALIDATE_HEADER causes volume header cache entry 
+ *                                     to be invalidated.
+ *
+ * @pre VOL_LOCK is held.
+ *
+ * @post volume state is changed.
+ *       for fileserver, salvage will be requested once refcount reaches zero.
+ *
+ * @return operation status code
+ *   @retval 0  volume salvage will occur
+ *   @retval 1  volume salvage could not be scheduled
+ *
+ * @note DAFS fileserver only
+ *
+ * @note this call does not synchronously schedule a volume salvage.  rather,
+ *       it sets volume state so that when volume refcounts reach zero, a
+ *       volume salvage will occur.  by "refcounts", we mean both nUsers and 
+ *       nWaiters must be zero.
+ *
+ * @internal volume package internal use only.
  */
 int
-VRequestSalvage_r(Volume * vp, int reason, int flags)
+VRequestSalvage_r(Error * ec, Volume * vp, int reason, int flags)
 {
-#ifdef SALVSYNC_BUILD_CLIENT
-    if (programType != fileServer)
+    int code = 0;
+    /*
+     * for DAFS volume utilities, transition to error state
+     * (at some point in the future, we should consider
+     *  making volser talk to salsrv)
+     */
+    if (programType != fileServer) {
+       VChangeState_r(vp, VOL_STATE_ERROR);
+       *ec = VSALVAGE;
        return 1;
+    }
 
     if (!vp->salvage.requested) {
        vp->salvage.requested = 1;
        vp->salvage.reason = reason;
        vp->stats.last_salvage = FT_ApproxTime();
-       if (flags & VOL_SALVAGE_INVALIDATE_HEADER) {
-           ReleaseVolumeHeader(vp->header);
-       }
-       if (vp->stats.salvages < SALVAGE_COUNT_MAX) {
+       if (VIsSalvager(V_inUse(vp))) {
+           Log("VRequestSalvage: volume %u appears to be salvaging, but we\n", vp->hashid);
+           Log("  didn't request a salvage. Forcing it offline waiting for the\n");
+           Log("  salvage to finish; if you are sure no salvage is running,\n");
+           Log("  run a salvage manually.\n");
+
+           /* make sure neither VScheduleSalvage_r nor
+            * VUpdateSalvagePriority_r try to schedule another salvage */
+           vp->salvage.requested = vp->salvage.scheduled = 0;
+
+           /* these stats aren't correct, but doing this makes them
+            * slightly closer to being correct */
+           vp->stats.salvages++;
+           vp->stats.last_salvage_req = FT_ApproxTime();
+           IncUInt64(&VStats.salvages);
+
+           VChangeState_r(vp, VOL_STATE_ERROR);
+           *ec = VSALVAGE;
+           code = 1;
+
+       } else if (vp->stats.salvages < SALVAGE_COUNT_MAX) {
            VChangeState_r(vp, VOL_STATE_SALVAGING);
+           *ec = VSALVAGING;
        } else {
            Log("VRequestSalvage: volume %u online salvaged too many times; forced offline.\n", vp->hashid);
            VChangeState_r(vp, VOL_STATE_ERROR);
+           *ec = VSALVAGE;
+           code = 1;
+       }
+       if (flags & VOL_SALVAGE_INVALIDATE_HEADER) {
+           /* Instead of ReleaseVolumeHeader, we do FreeVolumeHeader() 
+               so that the the next VAttachVolumeByVp_r() invocation 
+               of attach2() will pull in a cached header 
+               entry and fail, then load a fresh one from disk and attach 
+               it to the volume.             
+           */
+           FreeVolumeHeader(vp);
        }
     }
-#endif /* SALVSYNC_BUILD_CLIENT */
-    return 0;
+    return code;
 }
 
-/*
- * update salvage priority
+/**
+ * update salvageserver scheduling priority for a volume.
+ *
+ * @param[in] vp  pointer to volume object
+ *
+ * @return operation status
+ *   @retval 0  success
+ *   @retval 1  request denied, or SALVSYNC communications failure
+ *
+ * @pre VOL_LOCK is held.
+ *
+ * @post in-core salvage priority counter is incremented.  if at least
+ *       SALVAGE_PRIO_UPDATE_INTERVAL seconds have elapsed since the
+ *       last SALVSYNC_RAISEPRIO request, we contact the salvageserver
+ *       to update its priority queue.  if no salvage is scheduled,
+ *       this function is a no-op.
+ *
+ * @note DAFS fileserver only
+ *
+ * @note this should be called whenever a VGetVolume fails due to a 
+ *       pending salvage request
+ *
+ * @todo should set exclusive state and drop glock around salvsync call
+ *
+ * @internal volume package internal use only.
  */
 static int
 VUpdateSalvagePriority_r(Volume * vp)
 {
-    int code, ret=0;
-    afs_uint32 now;
+    int ret=0;
 
 #ifdef SALVSYNC_BUILD_CLIENT
+    afs_uint32 now;
+    int code;
+
     vp->salvage.prio++;
     now = FT_ApproxTime();
 
@@ -3617,15 +4152,33 @@ VUpdateSalvagePriority_r(Volume * vp)
 }
 
 
-/*
- * schedule a salvage with the salvage server
+#ifdef SALVSYNC_BUILD_CLIENT
+/**
+ * schedule a salvage with the salvage server.
+ *
+ * @param[in] vp  pointer to volume object
+ *
+ * @return operation status
+ *    @retval 0 salvage scheduled successfully
+ *    @retval 1 salvage not scheduled, or SALVSYNC com error
+ *
+ * @pre 
+ *    @arg VOL_LOCK is held.
+ *    @arg nUsers and nWaiters should be zero.
+ *
+ * @post salvageserver is sent a salvage request
+ *
+ * @note DAFS fileserver only
+ *
+ * @internal volume package internal use only.
  */
 static int
 VScheduleSalvage_r(Volume * vp)
 {
-    int code, ret=0;
-#ifdef SALVSYNC_BUILD_CLIENT
+    int ret=0;
+    int code;
     VolState state_save;
+    VThreadOptions_t * thread_opts;
     char partName[16];
 
     if (vp->nWaiters || vp->nUsers) {
@@ -3636,15 +4189,32 @@ VScheduleSalvage_r(Volume * vp)
     if (vp->stats.salvages >= SALVAGE_COUNT_MAX)
        return 1;
 
+    /*
+     * don't perform salvsync ops on certain threads
+     */
+    thread_opts = pthread_getspecific(VThread_key);
+    if (thread_opts == NULL) {
+       thread_opts = &VThread_defaults;
+    }
+    if (thread_opts->disallow_salvsync || vol_disallow_salvsync) {
+       return 1;
+    }
+
+    /*
+     * XXX the scheduling process should really be done asynchronously
+     *     to avoid fssync deadlocks
+     */
     if (!vp->salvage.scheduled) {
        /* if we haven't previously scheduled a salvage, do so now 
         *
         * set the volume to an exclusive state and drop the lock
         * around the SALVSYNC call
+        *
+        * note that we do NOT acquire a reservation here -- doing so
+        * could result in unbounded recursion
         */
        strlcpy(partName, VPartitionPath(vp->partition), sizeof(partName));
        state_save = VChangeState_r(vp, VOL_STATE_SALVSYNC_REQ);
-       V_attachFlags(vp) |= VOL_IS_BUSY;
        VOL_UNLOCK;
 
        /* can't use V_id() since there's no guarantee
@@ -3657,7 +4227,6 @@ VScheduleSalvage_r(Volume * vp)
                                      NULL);
        VOL_LOCK;
        VChangeState_r(vp, state_save);
-       V_attachFlags(vp) &= ~(VOL_IS_BUSY);
 
        if (code == SYNC_OK) {
            vp->salvage.scheduled = 1;
@@ -3679,41 +4248,22 @@ VScheduleSalvage_r(Volume * vp)
            }
        }
     }
-#endif /* SALVSYNC_BUILD_CLIENT */
     return ret;
 }
 
-/*
- * cancel a scheduled salvage operation
+/**
+ * connect to the salvageserver SYNC service.
+ *
+ * @return operation status
+ *    @retval 0 failure
+ *    @retval 1 success
+ *
+ * @post connection to salvageserver SYNC service established
+ *
+ * @see VConnectSALV_r
+ * @see VDisconnectSALV
+ * @see VReconnectSALV
  */
-static int
-VCancelSalvage_r(Volume * vp, int reason)
-{
-    int code, ret = 0;
-
-#ifdef SALVSYNC_BUILD_CLIENT
-    if (vp->salvage.scheduled) {
-       code = SALVSYNC_SalvageVolume(vp->hashid,
-                                     VPartitionPath(vp->partition),
-                                     SALVSYNC_CANCEL,
-                                     reason,
-                                     0,
-                                     NULL);
-       if (code == SYNC_OK) {
-           vp->salvage.scheduled = 0;
-       } else {
-           ret = 1;
-       }
-    }
-#endif /* SALVSYNC_BUILD_CLIENT */
-    return ret;
-}
-
-/* This must be called by any volume utility which needs to run while the
-   file server is also running.  This is separated from VInitVolumePackage so
-   that a utility can fork--and each of the children can independently
-   initialize communication with the file server */
-#ifdef SALVSYNC_BUILD_CLIENT
 int
 VConnectSALV(void)
 {
@@ -3724,32 +4274,93 @@ VConnectSALV(void)
     return retVal;
 }
 
+/**
+ * connect to the salvageserver SYNC service.
+ *
+ * @return operation status
+ *    @retval 0 failure
+ *    @retval 1 success
+ *
+ * @pre VOL_LOCK is held.
+ *
+ * @post connection to salvageserver SYNC service established
+ *
+ * @see VConnectSALV
+ * @see VDisconnectSALV_r
+ * @see VReconnectSALV_r
+ * @see SALVSYNC_clientInit
+ *
+ * @internal volume package internal use only.
+ */
 int
 VConnectSALV_r(void)
 {
-    assert((programType != salvageServer) &&
-          (programType != volumeUtility));
     return SALVSYNC_clientInit();
 }
 
+/**
+ * disconnect from the salvageserver SYNC service.
+ *
+ * @return operation status
+ *    @retval 0 success
+ *
+ * @pre client should have a live connection to the salvageserver
+ *
+ * @post connection to salvageserver SYNC service destroyed
+ *
+ * @see VDisconnectSALV_r
+ * @see VConnectSALV
+ * @see VReconnectSALV
+ */
 int
 VDisconnectSALV(void)
 {
-    int retVal;
     VOL_LOCK;
     VDisconnectSALV_r();
     VOL_UNLOCK;
-    return retVal;
+    return 0;
 }
 
+/**
+ * disconnect from the salvageserver SYNC service.
+ *
+ * @return operation status
+ *    @retval 0 success
+ *
+ * @pre 
+ *    @arg VOL_LOCK is held.
+ *    @arg client should have a live connection to the salvageserver.
+ *
+ * @post connection to salvageserver SYNC service destroyed
+ *
+ * @see VDisconnectSALV
+ * @see VConnectSALV_r
+ * @see VReconnectSALV_r
+ * @see SALVSYNC_clientFinis
+ *
+ * @internal volume package internal use only.
+ */
 int
 VDisconnectSALV_r(void)
 { 
-    assert((programType != salvageServer) &&
-          (programType != volumeUtility));
     return SALVSYNC_clientFinis();
 }
 
+/**
+ * disconnect and then re-connect to the salvageserver SYNC service.
+ *
+ * @return operation status
+ *    @retval 0 failure
+ *    @retval 1 success
+ *
+ * @pre client should have a live connection to the salvageserver
+ *
+ * @post old connection is dropped, and a new one is established
+ *
+ * @see VConnectSALV
+ * @see VDisconnectSALV
+ * @see VReconnectSALV_r
+ */
 int
 VReconnectSALV(void)
 {
@@ -3760,11 +4371,29 @@ VReconnectSALV(void)
     return retVal;
 }
 
+/**
+ * disconnect and then re-connect to the salvageserver SYNC service.
+ *
+ * @return operation status
+ *    @retval 0 failure
+ *    @retval 1 success
+ *
+ * @pre 
+ *    @arg VOL_LOCK is held.
+ *    @arg client should have a live connection to the salvageserver.
+ *
+ * @post old connection is dropped, and a new one is established
+ *
+ * @see VConnectSALV_r
+ * @see VDisconnectSALV
+ * @see VReconnectSALV
+ * @see SALVSYNC_clientReconnect
+ *
+ * @internal volume package internal use only.
+ */
 int
 VReconnectSALV_r(void)
 {
-    assert((programType != salvageServer) &&
-          (programType != volumeUtility));
     return SALVSYNC_clientReconnect();
 }
 #endif /* SALVSYNC_BUILD_CLIENT */
@@ -3780,6 +4409,23 @@ VReconnectSALV_r(void)
    that a utility can fork--and each of the children can independently
    initialize communication with the file server */
 #ifdef FSSYNC_BUILD_CLIENT
+/**
+ * connect to the fileserver SYNC service.
+ *
+ * @return operation status
+ *    @retval 0 failure
+ *    @retval 1 success
+ *
+ * @pre 
+ *    @arg VInit must equal 2.
+ *    @arg Program Type must not be fileserver or salvager.
+ *
+ * @post connection to fileserver SYNC service established
+ *
+ * @see VConnectFS_r
+ * @see VDisconnectFS
+ * @see VChildProcReconnectFS
+ */
 int
 VConnectFS(void)
 {
@@ -3790,6 +4436,26 @@ VConnectFS(void)
     return retVal;
 }
 
+/**
+ * connect to the fileserver SYNC service.
+ *
+ * @return operation status
+ *    @retval 0 failure
+ *    @retval 1 success
+ *
+ * @pre 
+ *    @arg VInit must equal 2.
+ *    @arg Program Type must not be fileserver or salvager.
+ *    @arg VOL_LOCK is held.
+ *
+ * @post connection to fileserver SYNC service established
+ *
+ * @see VConnectFS
+ * @see VDisconnectFS_r
+ * @see VChildProcReconnectFS_r
+ *
+ * @internal volume package internal use only.
+ */
 int
 VConnectFS_r(void)
 {
@@ -3803,6 +4469,22 @@ VConnectFS_r(void)
     return rc;
 }
 
+/**
+ * disconnect from the fileserver SYNC service.
+ *
+ * @pre 
+ *    @arg client should have a live connection to the fileserver.
+ *    @arg VOL_LOCK is held.
+ *    @arg Program Type must not be fileserver or salvager.
+ *
+ * @post connection to fileserver SYNC service destroyed
+ *
+ * @see VDisconnectFS
+ * @see VConnectFS_r
+ * @see VChildProcReconnectFS_r
+ *
+ * @internal volume package internal use only.
+ */
 void
 VDisconnectFS_r(void)
 {
@@ -3812,6 +4494,19 @@ VDisconnectFS_r(void)
     VInit = 2;
 }
 
+/**
+ * disconnect from the fileserver SYNC service.
+ *
+ * @pre
+ *    @arg client should have a live connection to the fileserver.
+ *    @arg Program Type must not be fileserver or salvager.
+ *
+ * @post connection to fileserver SYNC service destroyed
+ *
+ * @see VDisconnectFS_r
+ * @see VConnectFS
+ * @see VChildProcReconnectFS
+ */
 void
 VDisconnectFS(void)
 {
@@ -3820,12 +4515,48 @@ VDisconnectFS(void)
     VOL_UNLOCK;
 }
 
-static int
+/**
+ * connect to the fileserver SYNC service from a child process following a fork.
+ *
+ * @return operation status
+ *    @retval 0 failure
+ *    @retval 1 success
+ *
+ * @pre
+ *    @arg VOL_LOCK is held.
+ *    @arg current FSYNC handle is shared with a parent process
+ *
+ * @post current FSYNC handle is discarded and a new connection to the
+ *       fileserver SYNC service is established
+ *
+ * @see VChildProcReconnectFS
+ * @see VConnectFS_r
+ * @see VDisconnectFS_r
+ *
+ * @internal volume package internal use only.
+ */
+int
 VChildProcReconnectFS_r(void)
 {
     return FSYNC_clientChildProcReconnect();
 }
 
+/**
+ * connect to the fileserver SYNC service from a child process following a fork.
+ *
+ * @return operation status
+ *    @retval 0 failure
+ *    @retval 1 success
+ *
+ * @pre current FSYNC handle is shared with a parent process
+ *
+ * @post current FSYNC handle is discarded and a new connection to the
+ *       fileserver SYNC service is established
+ *
+ * @see VChildProcReconnectFS_r
+ * @see VConnectFS
+ * @see VDisconnectFS
+ */
 int
 VChildProcReconnectFS(void)
 {
@@ -3894,9 +4625,9 @@ VAllocBitmapEntry_r(Error * ec, Volume * vp,
                    VOL_UNLOCK;
                    sleep(2);
                    VOL_LOCK;
-#else /* AFS_PTHREAD_ENV */
+#else /* !AFS_PTHREAD_ENV */
                    IOMGR_Sleep(2);
-#endif /* AFS_DEMAND_ATTACH_FS */
+#endif /* !AFS_PTHREAD_ENV */
                }
            }
        }
@@ -3910,8 +4641,7 @@ VAllocBitmapEntry_r(Error * ec, Volume * vp,
                VGetBitmap_r(ec, vp, i);
                if (*ec) {
 #ifdef AFS_DEMAND_ATTACH_FS
-                   VRequestSalvage_r(vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
-                   *ec = VSALVAGING;
+                   VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
 #else /* AFS_DEMAND_ATTACH_FS */
                    DeleteVolumeFromHashTable(vp);
                    vp->shuttingDown = 1;       /* Let who has it free it. */
@@ -4125,110 +4855,39 @@ VGetBitmap_r(Error * ec, Volume * vp, VnodeClass class)
 
 
 /***************************************************/
-/* demand attach fs state machine routines         */
-/***************************************************/
-
-#ifdef AFS_DEMAND_ATTACH_FS
-/* wait for the volume to change states */
-static void
-VWaitStateChange_r(Volume * vp)
-{
-    VolState state_save = V_attachState(vp);
-
-    assert(vp->nWaiters || vp->nUsers);
-    do {
-       assert(pthread_cond_wait(&V_attachCV(vp), &vol_glock_mutex) == 0);
-    } while (V_attachState(vp) == state_save);
-    assert(V_attachState(vp) != VOL_STATE_FREED);
-}
-
-/* wait for blocking ops to end */
-static void
-VWaitExclusiveState_r(Volume * vp)
-{
-    assert(vp->nWaiters || vp->nUsers);
-    while (IsExclusiveState(V_attachState(vp))) {
-       assert(pthread_cond_wait(&V_attachCV(vp), &vol_glock_mutex) == 0);
-    }
-    assert(V_attachState(vp) != VOL_STATE_FREED);
-}
-
-/* change state, and notify other threads,
- * return previous state to caller */
-VolState
-VChangeState_r(Volume * vp, VolState new_state)
-{
-    VolState old_state = V_attachState(vp);
-
-    /* XXX profiling need to make sure these counters
-     * don't kill performance... */
-    VStats.state_levels[old_state]--;
-    VStats.state_levels[new_state]++;
-
-    V_attachState(vp) = new_state;
-    assert(pthread_cond_broadcast(&V_attachCV(vp)) == 0);
-    return old_state;
-}
-
-/* tells caller whether or not the current state requires
- * exclusive access without holding glock */
-static int
-IsExclusiveState(VolState state)
-{
-    switch (state) {
-    case VOL_STATE_UPDATING:
-    case VOL_STATE_ATTACHING:
-    case VOL_STATE_GET_BITMAP:
-    case VOL_STATE_HDR_LOADING:
-    case VOL_STATE_HDR_ATTACHING:
-    case VOL_STATE_OFFLINING:
-    case VOL_STATE_DETACHING:
-       return 1;
-    }
-    return 0;
-}
-
-/* tell caller whether V_attachState is an error condition */
-static int
-IsErrorState(VolState state)
-{
-    switch (state) {
-    case VOL_STATE_ERROR:
-    case VOL_STATE_SALVAGING:
-       return 1;
-    }
-    return 0;
-}
-
-/* tell caller whether V_attachState is valid */
-static int
-IsValidState(VolState state)
-{
-    if ((state >= 0) && 
-       (state < VOL_STATE_COUNT) &&
-       (state != VOL_STATE_FREED)) {
-       return 1;
-    }
-    return 0;
-}
-#endif /* AFS_DEMAND_ATTACH_FS */
-
-
-/***************************************************/
 /* Volume Path and Volume Number utility routines  */
 /***************************************************/
 
-static void
-GetVolumePath(Error * ec, VolId volumeId, char **partitionp, char **namep)
+/**
+ * find the first occurrence of a volume header file and return the path.
+ *
+ * @param[out] ec          outbound error code
+ * @param[in]  volumeId    volume id to find
+ * @param[out] partitionp  pointer to disk partition path string
+ * @param[out] namep       pointer to volume header file name string
+ *
+ * @post path to first occurrence of volume header is returned in partitionp
+ *       and namep, or ec is set accordingly.
+ *
+ * @warning this function is NOT re-entrant -- partitionp and namep point to
+ *          static data segments
+ *
+ * @note if a volume utility inadvertently leaves behind a stale volume header
+ *       on a vice partition, it is possible for callers to get the wrong one,
+ *       depending on the order of the disk partition linked list.
+ *
+ */
+void
+VGetVolumePath(Error * ec, VolId volumeId, char **partitionp, char **namep)
 {
     static char partition[VMAXPATHLEN], name[VMAXPATHLEN];
     char path[VMAXPATHLEN];
     int found = 0;
-    struct DiskPartition *dp;
+    struct DiskPartition64 *dp;
 
     *ec = 0;
     name[0] = '/';
-    (void)afs_snprintf(&name[1], (sizeof name) - 1, VFORMAT, volumeId);
+    (void)afs_snprintf(&name[1], (sizeof name) - 1, VFORMAT, afs_printable_uint32_lu(volumeId));
     for (dp = DiskPartitionList; dp; dp = dp->next) {
        struct afs_stat status;
        strcpy(path, VPartitionPath(dp));
@@ -4248,6 +4907,18 @@ GetVolumePath(Error * ec, VolId volumeId, char **partitionp, char **namep)
     }
 }
 
+/**
+ * extract a volume number from a volume header filename string.
+ *
+ * @param[in] name  volume header filename string
+ *
+ * @return volume number
+ *
+ * @note the string must be of the form VFORMAT.  the only permissible
+ *       deviation is a leading '/' character.
+ *
+ * @see VFORMAT
+ */
 int
 VolumeNumber(char *name)
 {
@@ -4256,19 +4927,56 @@ VolumeNumber(char *name)
     return atoi(name + 1);
 }
 
+/**
+ * compute the volume header filename.
+ *
+ * @param[in] volumeId
+ *
+ * @return volume header filename
+ *
+ * @post volume header filename string is constructed
+ *
+ * @warning this function is NOT re-entrant -- the returned string is
+ *          stored in a static char array.  see VolumeExternalName_r
+ *          for a re-entrant equivalent.
+ *
+ * @see VolumeExternalName_r
+ *
+ * @deprecated due to the above re-entrancy warning, this interface should
+ *             be considered deprecated.  Please use VolumeExternalName_r
+ *             in its stead.
+ */
 char *
 VolumeExternalName(VolumeId volumeId)
 {
     static char name[VMAXPATHLEN];
-    (void)afs_snprintf(name, sizeof name, VFORMAT, volumeId);
+    (void)afs_snprintf(name, sizeof name, VFORMAT, afs_printable_uint32_lu(volumeId));
     return name;
 }
 
+/**
+ * compute the volume header filename.
+ *
+ * @param[in]     volumeId
+ * @param[inout]  name       array in which to store filename
+ * @param[in]     len        length of name array
+ *
+ * @return result code from afs_snprintf
+ *
+ * @see VolumeExternalName
+ * @see afs_snprintf
+ *
+ * @note re-entrant equivalent of VolumeExternalName
+ *
+ * @internal volume package internal use only.
+ */
+#ifdef AFS_DEMAND_ATTACH_FS
 static int
 VolumeExternalName_r(VolumeId volumeId, char * name, size_t len)
 {
-    return afs_snprintf(name, len, VFORMAT, volumeId);
+    return afs_snprintf(name, len, VFORMAT, afs_printable_uint32_lu(volumeId));
 }
+#endif
 
 
 /***************************************************/
@@ -4328,7 +5036,7 @@ VAdjustVolumeStatistics_r(register Volume * vp)
         * All we need to do is bzero the entire VOL_STATS_BYTES of
         * the detailed volume statistics area.
         */
-       memset((char *)(V_stat_area(vp)), 0, VOL_STATS_BYTES);
+       memset((V_stat_area(vp)), 0, VOL_STATS_BYTES);
 #endif /* OPENAFS_VOL_STATS */
     }
 
@@ -4353,6 +5061,7 @@ void
 VBumpVolumeUsage_r(register Volume * vp)
 {
     unsigned int now = FT_ApproxTime();
+    V_accessDate(vp) = now;
     if (now - V_dayUseDate(vp) > OneDay)
        VAdjustVolumeStatistics_r(vp);
     /*
@@ -4537,50 +5246,79 @@ VScanUpdateList(void)
  * candidates for soft detachment. this queue is
  * unsorted
  */
-#define VLRU_GENERATIONS  3   /* number of generations in VLRU */
-#define VLRU_QUEUES       5   /* total number of VLRU queues */
+#define VLRU_GENERATIONS  3   /**< number of generations in VLRU */
+#define VLRU_QUEUES       5   /**< total number of VLRU queues */
+
+/**
+ * definition of a VLRU queue.
+ */
 struct VLRU_q {
     volatile struct rx_queue q;
     volatile int len;
     volatile int busy;
     pthread_cond_t cv;
 };
+
+/**
+ * main VLRU data structure.
+ */
 struct VLRU {
-    struct VLRU_q q[VLRU_QUEUES];
+    struct VLRU_q q[VLRU_QUEUES];   /**< VLRU queues */
 
     /* VLRU config */
-    afs_uint32 promotion_interval[VLRU_GENERATIONS-1];  /* interval between promotions */
-    afs_uint32 scan_interval[VLRU_GENERATIONS+1];       /* interval between scans for candidates */
+    /** time interval (in seconds) between promotion passes for
+     *  each young generation queue. */
+    afs_uint32 promotion_interval[VLRU_GENERATIONS-1];
 
-    /* state */
-    int next_idx;
-    afs_uint32 last_promotion[VLRU_GENERATIONS-1];      /* timestamp of last promotion scan */
-    afs_uint32 last_scan[VLRU_GENERATIONS+1];           /* timestamp of last detach scan */
+    /** time interval (in seconds) between soft detach candidate
+     *  scans for each generation queue.
+     *
+     *  scan_interval[VLRU_QUEUE_CANDIDATE] defines how frequently
+     *  we perform a soft detach pass. */
+    afs_uint32 scan_interval[VLRU_GENERATIONS+1];
 
-    int scanner_state;                                  /* state of scanner thread */
-    pthread_cond_t cv;                                  /* state transition CV */
+    /* scheduler state */
+    int next_idx;                                       /**< next queue to receive attention */
+    afs_uint32 last_promotion[VLRU_GENERATIONS-1];      /**< timestamp of last promotion scan */
+    afs_uint32 last_scan[VLRU_GENERATIONS+1];           /**< timestamp of last detach scan */
+
+    int scanner_state;                                  /**< state of scanner thread */
+    pthread_cond_t cv;                                  /**< state transition CV */
 };
 
+/** global VLRU state */
 static struct VLRU volume_LRU;
 
-/* valid scanner states */
-#define VLRU_SCANNER_STATE_OFFLINE        0
-#define VLRU_SCANNER_STATE_ONLINE         1
-#define VLRU_SCANNER_STATE_SHUTTING_DOWN  2
-#define VLRU_SCANNER_STATE_PAUSING        3
-#define VLRU_SCANNER_STATE_PAUSED         4
+/**
+ * defined states for VLRU scanner thread.
+ */
+typedef enum {
+    VLRU_SCANNER_STATE_OFFLINE        = 0,    /**< vlru scanner thread is offline */
+    VLRU_SCANNER_STATE_ONLINE         = 1,    /**< vlru scanner thread is online */
+    VLRU_SCANNER_STATE_SHUTTING_DOWN  = 2,    /**< vlru scanner thread is shutting down */
+    VLRU_SCANNER_STATE_PAUSING        = 3,    /**< vlru scanner thread is getting ready to pause */
+    VLRU_SCANNER_STATE_PAUSED         = 4     /**< vlru scanner thread is paused */
+} vlru_thread_state_t;
 
 /* vlru disk data header stuff */
-#define VLRU_DISK_MAGIC      0x7a8b9cad
-#define VLRU_DISK_VERSION    1
+#define VLRU_DISK_MAGIC      0x7a8b9cad        /**< vlru disk entry magic number */
+#define VLRU_DISK_VERSION    1                 /**< vlru disk entry version number */
 
-/* vlru default expiration time (for eventual fs state serialization of vlru data) */
+/** vlru default expiration time (for eventual fs state serialization of vlru data) */
 #define VLRU_DUMP_EXPIRATION_TIME   (60*60*24*7)  /* expire vlru data after 1 week */
 
 
+/** minimum volume inactivity (in seconds) before a volume becomes eligible for
+ *  soft detachment. */
 static afs_uint32 VLRU_offline_thresh = VLRU_DEFAULT_OFFLINE_THRESH;
+
+/** time interval (in seconds) between VLRU scanner thread soft detach passes. */
 static afs_uint32 VLRU_offline_interval = VLRU_DEFAULT_OFFLINE_INTERVAL;
+
+/** maximum number of volumes to soft detach in a VLRU soft detach pass. */
 static afs_uint32 VLRU_offline_max = VLRU_DEFAULT_OFFLINE_MAX;
+
+/** VLRU control flag.  non-zero value implies VLRU subsystem is activated. */
 static afs_uint32 VLRU_enabled = 1;
 
 /* queue synchronization routines */
@@ -4588,15 +5326,28 @@ static void VLRU_BeginExclusive_r(struct VLRU_q * q);
 static void VLRU_EndExclusive_r(struct VLRU_q * q);
 static void VLRU_Wait_r(struct VLRU_q * q);
 
-/* set the VLRU parameters 
+/**
+ * set VLRU subsystem tunable parameters.
  *
- * valid options are:
- *  VLRU_SET_THRESH -- set the period of inactivity after
- *    which volumes are eligible for being detached
- *  VLRU_SET_INTERVAL -- the time interval between calls
- *    to the volume LRU "garbage collector"
- *  VLRU_SET_MAX -- the max number of volumes to deallocate
- *    in one GC pass
+ * @param[in] option  tunable option to modify
+ * @param[in] val     new value for tunable parameter
+ *
+ * @pre @c VInitVolumePackage has not yet been called.
+ *
+ * @post tunable parameter is modified
+ *
+ * @note DAFS only
+ *
+ * @note valid option parameters are:
+ *    @arg @c VLRU_SET_THRESH 
+ *         set the period of inactivity after which
+ *         volumes are eligible for soft detachment
+ *    @arg @c VLRU_SET_INTERVAL 
+ *         set the time interval between calls
+ *         to the volume LRU "garbage collector"
+ *    @arg @c VLRU_SET_MAX 
+ *         set the max number of volumes to deallocate
+ *         in one GC pass
  */
 void
 VLRU_SetOptions(int option, afs_uint32 val)
@@ -4613,7 +5364,18 @@ VLRU_SetOptions(int option, afs_uint32 val)
     VLRU_ComputeConstants();
 }
 
-/* compute the VLRU internal timing parameters based upon the user's inputs */
+/**
+ * compute VLRU internal timing parameters.
+ *
+ * @post VLRU scanner thread internal timing parameters are computed
+ *
+ * @note computes internal timing parameters based upon user-modifiable 
+ *       tunable parameters.
+ *
+ * @note DAFS only
+ *
+ * @internal volume package internal use only.
+ */
 static void
 VLRU_ComputeConstants(void)
 {
@@ -4635,7 +5397,17 @@ VLRU_ComputeConstants(void)
     }
 }
 
-/* initialize VLRU */
+/**
+ * initialize VLRU subsystem.
+ *
+ * @pre this function has not yet been called
+ *
+ * @post VLRU subsystem is initialized and VLRU scanner thread is starting
+ *
+ * @note DAFS only
+ *
+ * @internal volume package internal use only.
+ */
 static void
 VInitVLRU(void)
 {
@@ -4678,9 +5450,24 @@ VInitVLRU(void)
     }
 }
 
-/* initialize LRU support for a volume */
+/**
+ * initialize the VLRU-related fields of a newly allocated volume object.
+ *
+ * @param[in] vp  pointer to volume object
+ *
+ * @pre
+ *    @arg @c VOL_LOCK is held.
+ *    @arg volume object is not on a VLRU queue.
+ *
+ * @post VLRU fields are initialized to indicate that volume object is not
+ *       currently registered with the VLRU subsystem
+ *
+ * @note DAFS only
+ *
+ * @internal volume package interal use only.
+ */
 static void
-VLRU_Init_Node_r(volatile Volume * vp)
+VLRU_Init_Node_r(Volume * vp)
 {
     if (!VLRU_enabled)
        return;
@@ -4689,14 +5476,38 @@ VLRU_Init_Node_r(volatile Volume * vp)
     vp->vlru.idx = VLRU_QUEUE_INVALID;
 }
 
-/* add volume to VLRU 
- * now supports adding to queues other
- * than new for vlru state restore
- * caller MUST hold a ref count on vp */
+/**
+ * add a volume object to a VLRU queue.
+ *
+ * @param[in] vp  pointer to volume object
+ *
+ * @pre
+ *    @arg @c VOL_LOCK is held.
+ *    @arg caller MUST hold a lightweight ref on @p vp.
+ *    @arg caller MUST NOT hold exclusive ownership of the VLRU queue.
+ *
+ * @post the volume object is added to the appropriate VLRU queue
+ *
+ * @note if @c vp->vlru.idx contains the index of a valid VLRU queue,
+ *       then the volume is added to that queue.  Otherwise, the value
+ *       @c VLRU_QUEUE_NEW is stored into @c vp->vlru.idx and the
+ *       volume is added to the NEW generation queue.
+ *
+ * @note @c VOL_LOCK may be dropped internally
+ *
+ * @note Volume state is temporarily set to @c VOL_STATE_VLRU_ADD
+ *       during the add operation, and is restored to the previous
+ *       state prior to return.
+ *
+ * @note DAFS only
+ *
+ * @internal volume package internal use only.
+ */
 static void
-VLRU_Add_r(volatile Volume * vp)
+VLRU_Add_r(Volume * vp)
 {
     int idx;
+    VolState state_save;
 
     if (!VLRU_enabled)
        return;
@@ -4704,26 +5515,51 @@ VLRU_Add_r(volatile Volume * vp)
     if (queue_IsOnQueue(&vp->vlru))
        return;
 
-    VLRU_Wait_r(&volume_LRU.q[VLRU_QUEUE_NEW]);
+    state_save = VChangeState_r(vp, VOL_STATE_VLRU_ADD);
+
+    idx = vp->vlru.idx;
+    if ((idx < 0) || (idx >= VLRU_QUEUE_INVALID)) {
+       idx = VLRU_QUEUE_NEW;
+    }
+
+    VLRU_Wait_r(&volume_LRU.q[idx]);
 
     /* repeat check since VLRU_Wait_r may have dropped
      * the glock */
     if (queue_IsNotOnQueue(&vp->vlru)) {
-       idx = vp->vlru.idx;
-       if ((idx < 0) || (idx >= VLRU_QUEUE_INVALID)) {
-           idx = vp->vlru.idx = VLRU_QUEUE_NEW;
-       }
+       vp->vlru.idx = idx;
        queue_Prepend(&volume_LRU.q[idx], &vp->vlru);
        volume_LRU.q[idx].len++;
        V_attachFlags(vp) |= VOL_ON_VLRU;
        vp->stats.last_promote = FT_ApproxTime();
     }
+
+    VChangeState_r(vp, state_save);
 }
 
-/* delete volume from VLRU 
- * caller MUST hold a ref count on vp */
+/**
+ * delete a volume object from a VLRU queue.
+ *
+ * @param[in] vp  pointer to volume object
+ *
+ * @pre
+ *    @arg @c VOL_LOCK is held.
+ *    @arg caller MUST hold a lightweight ref on @p vp.
+ *    @arg caller MUST NOT hold exclusive ownership of the VLRU queue.
+ *
+ * @post volume object is removed from the VLRU queue
+ *
+ * @note @c VOL_LOCK may be dropped internally
+ *
+ * @note DAFS only
+ *
+ * @todo We should probably set volume state to something exlcusive 
+ *       (as @c VLRU_Add_r does) prior to dropping @c VOL_LOCK.
+ *
+ * @internal volume package internal use only.
+ */
 static void
-VLRU_Delete_r(volatile Volume * vp)
+VLRU_Delete_r(Volume * vp)
 {
     int idx;
 
@@ -4749,12 +5585,29 @@ VLRU_Delete_r(volatile Volume * vp)
     V_attachFlags(vp) &= ~(VOL_ON_VLRU);
 }
 
-/* signal that volume was just accessed.
- * caller MUST hold a ref count on vp */
+/**
+ * tell the VLRU subsystem that a volume was just accessed.
+ *
+ * @param[in] vp  pointer to volume object
+ *
+ * @pre
+ *    @arg @c VOL_LOCK is held
+ *    @arg caller MUST hold a lightweight ref on @p vp
+ *    @arg caller MUST NOT hold exclusive ownership of any VLRU queue
+ *
+ * @post volume VLRU access statistics are updated.  If the volume was on
+ *       the VLRU soft detach candidate queue, it is moved to the NEW
+ *       generation queue.
+ *
+ * @note @c VOL_LOCK may be dropped internally
+ *
+ * @note DAFS only
+ *
+ * @internal volume package internal use only.
+ */
 static void
-VLRU_UpdateAccess_r(volatile Volume * vp)
+VLRU_UpdateAccess_r(Volume * vp)
 {
-    afs_uint32 live_interval;
     Volume * rvp = NULL;
 
     if (!VLRU_enabled)
@@ -4800,9 +5653,36 @@ VLRU_UpdateAccess_r(volatile Volume * vp)
     }
 }
 
-/* switch a volume between two VLRU queues */
+/**
+ * switch a volume between two VLRU queues.
+ *
+ * @param[in] vp       pointer to volume object
+ * @param[in] new_idx  index of VLRU queue onto which the volume will be moved
+ * @param[in] append   controls whether the volume will be appended or 
+ *                     prepended to the queue.  A nonzero value means it will
+ *                     be appended; zero means it will be prepended.
+ *
+ * @pre The new (and old, if applicable) queue(s) must either be owned 
+ *      exclusively by the calling thread for asynchronous manipulation,
+ *      or the queue(s) must be quiescent and VOL_LOCK must be held.
+ *      Please see VLRU_BeginExclusive_r, VLRU_EndExclusive_r and VLRU_Wait_r
+ *      for further details of the queue asynchronous processing mechanism.
+ *
+ * @post If the volume object was already on a VLRU queue, it is
+ *       removed from the queue.  Depending on the value of the append
+ *       parameter, the volume object is either appended or prepended
+ *       to the VLRU queue referenced by the new_idx parameter.
+ *
+ * @note DAFS only
+ *
+ * @see VLRU_BeginExclusive_r
+ * @see VLRU_EndExclusive_r
+ * @see VLRU_Wait_r
+ *
+ * @internal volume package internal use only.
+ */
 static void
-VLRU_SwitchQueues(volatile Volume * vp, int new_idx, int append)
+VLRU_SwitchQueues(Volume * vp, int new_idx, int append)
 {
     if (queue_IsNotOnQueue(&vp->vlru))
        return;
@@ -4821,13 +5701,24 @@ VLRU_SwitchQueues(volatile Volume * vp, int new_idx, int append)
     vp->vlru.idx = new_idx;
 }
 
-/* VLRU GC thread */
+/**
+ * VLRU background thread.
+ *
+ * The VLRU Scanner Thread is responsible for periodically scanning through
+ * each VLRU queue looking for volumes which should be moved to another
+ * queue, or soft detached.
+ *
+ * @param[in] args  unused thread arguments parameter
+ *
+ * @return unused thread return value
+ *    @retval NULL always
+ *
+ * @internal volume package internal use only.
+ */
 static void *
 VLRU_ScannerThread(void * args)
 {
     afs_uint32 now, min_delay, delay;
-    afs_uint32 next_scan[VLRU_GENERATIONS];
-    afs_uint32 next_promotion[VLRU_GENERATIONS];
     int i, min_idx, min_op, overdue, state;
 
     /* set t=0 for promotion cycle to be 
@@ -4860,7 +5751,7 @@ VLRU_ScannerThread(void * args)
            volume_LRU.scanner_state = VLRU_SCANNER_STATE_PAUSED;
            assert(pthread_cond_broadcast(&volume_LRU.cv) == 0);
            do {
-               assert(pthread_cond_wait(&volume_LRU.cv, &vol_glock_mutex) == 0);
+               VOL_CV_WAIT(&volume_LRU.cv);
            } while (volume_LRU.scanner_state == VLRU_SCANNER_STATE_PAUSED);
        }
 
@@ -4893,7 +5784,6 @@ VLRU_ScannerThread(void * args)
                min_delay = 0;
                min_idx = i;
                overdue = 1;
-               break;
            }
        }
 
@@ -4940,14 +5830,38 @@ VLRU_ScannerThread(void * args)
     return NULL;
 }
 
-/* run the promotions */
+/**
+ * promote volumes from one VLRU generation to the next.
+ *
+ * This routine scans a VLRU generation looking for volumes which are
+ * eligible to be promoted to the next generation.  All volumes which
+ * meet the eligibility requirement are promoted.
+ *
+ * Promotion eligibility is based upon meeting both of the following
+ * requirements:
+ *
+ *    @arg The volume has been accessed since the last promotion:
+ *         @c (vp->stats.last_get >= vp->stats.last_promote)
+ *    @arg The last promotion occurred at least 
+ *         @c volume_LRU.promotion_interval[idx] seconds ago
+ *
+ * As a performance optimization, promotions are "globbed".  In other
+ * words, we promote arbitrarily large contiguous sublists of elements
+ * as one operation.  
+ *
+ * @param[in] idx  VLRU queue index to scan
+ *
+ * @note DAFS only
+ *
+ * @internal VLRU internal use only.
+ */
 static void
 VLRU_Promote_r(int idx)
 {
     int len, chaining, promote;
     afs_uint32 now, thresh;
     struct rx_queue *qp, *nqp;
-    Volume * vp, *start, *end;
+    Volume * vp, *start = NULL, *end = NULL;
 
     /* get exclusive access to two chains, and drop the glock */
     VLRU_Wait_r(&volume_LRU.q[idx]);
@@ -5010,7 +5924,7 @@ VLRU_Demote_r(int idx)
     int len, chaining, demote;
     afs_uint32 now, thresh;
     struct rx_queue *qp, *nqp;
-    Volume * vp, *start, *end;
+    Volume * vp, *start = NULL, *end = NULL;
     Volume ** salv_flag_vec = NULL;
     int salv_vec_offset = 0;
 
@@ -5110,7 +6024,7 @@ VLRU_Scan_r(int idx)
 {
     afs_uint32 now, thresh;
     struct rx_queue *qp, *nqp;
-    volatile Volume * vp;
+    Volume * vp;
     int i, locked = 1;
 
     assert(idx == VLRU_QUEUE_NEW || idx == VLRU_QUEUE_CANDIDATE);
@@ -5179,7 +6093,7 @@ VLRU_Scan_r(int idx)
 /* check whether volume is safe to soft detach
  * caller MUST NOT hold a ref count on vp */
 static int
-VCheckSoftDetach(volatile Volume * vp, afs_uint32 thresh)
+VCheckSoftDetach(Volume * vp, afs_uint32 thresh)
 {
     int ret=0;
 
@@ -5196,7 +6110,7 @@ VCheckSoftDetach(volatile Volume * vp, afs_uint32 thresh)
 /* check whether volume should be made a 
  * soft detach candidate */
 static int
-VCheckSoftDetachCandidate(volatile Volume * vp, afs_uint32 thresh)
+VCheckSoftDetachCandidate(Volume * vp, afs_uint32 thresh)
 {
     int idx, ret = 0;
     if (vp->nUsers || vp->nWaiters)
@@ -5242,7 +6156,7 @@ static void
 VLRU_Wait_r(struct VLRU_q * q)
 {
     while(q->busy) {
-       assert(pthread_cond_wait(&q->cv, &vol_glock_mutex) == 0);
+       VOL_CV_WAIT(&q->cv);
     }
 }
 
@@ -5251,7 +6165,7 @@ VLRU_Wait_r(struct VLRU_q * q)
  *
  * caller MUST NOT hold a ref count on vp */
 static int
-VSoftDetachVolume_r(volatile Volume * vp, afs_uint32 thresh)
+VSoftDetachVolume_r(Volume * vp, afs_uint32 thresh)
 {
     afs_uint32 ts_save;
     int ret = 0;
@@ -5265,7 +6179,7 @@ VSoftDetachVolume_r(volatile Volume * vp, afs_uint32 thresh)
     if (vp->nUsers || vp->nWaiters)
        return 0;
 
-    if (IsExclusiveState(V_attachState(vp))) {
+    if (VIsExclusiveState(V_attachState(vp))) {
        return 0;
     }
 
@@ -5287,6 +6201,8 @@ VSoftDetachVolume_r(volatile Volume * vp, afs_uint32 thresh)
        V_attachFlags(vp) &= ~(VOL_ON_VLRU);
        VCancelReservation_r(vp);
        return 0;
+    default:
+       break;
     }
 
     /* hold the volume and take it offline.
@@ -5339,9 +6255,26 @@ VSoftDetachVolume_r(volatile Volume * vp, afs_uint32 thresh)
 /* Volume Header Cache routines                    */
 /***************************************************/
 
+/** 
+ * volume header cache.
+ */
 struct volume_hdr_LRU_t volume_hdr_LRU;
 
-/* Allocate a bunch of headers; string them together */
+/**
+ * initialize the volume header cache.
+ *
+ * @param[in] howMany  number of header cache entries to preallocate
+ *
+ * @pre VOL_LOCK held.  Function has never been called before.
+ *
+ * @post howMany cache entries are allocated, initialized, and added 
+ *       to the LRU list.  Header cache statistics are initialized.
+ *
+ * @note only applicable to fileServer program type.  Should only be
+ *       called once during volume package initialization.
+ *
+ * @internal volume package internal use only.
+ */
 static void
 VInitVolumeHeaderCache(afs_uint32 howMany)
 {
@@ -5349,122 +6282,41 @@ VInitVolumeHeaderCache(afs_uint32 howMany)
     if (programType != fileServer)
        return;
     queue_Init(&volume_hdr_LRU);
-#ifdef AFS_DEMAND_ATTACH_FS
     volume_hdr_LRU.stats.free = 0;
     volume_hdr_LRU.stats.used = howMany;
     volume_hdr_LRU.stats.attached = 0;
-#endif
     hp = (struct volHeader *)(calloc(howMany, sizeof(struct volHeader)));
-    while (howMany--)
-       ReleaseVolumeHeader(hp++);
-}
-
-#ifdef AFS_DEMAND_ATTACH_FS
-/* Get a volume header from the LRU list; update the old one if necessary */
-/* Returns 1 if there was already a header, which is removed from the LRU list */
-/* caller MUST has a ref count on vp */
-static int
-GetVolumeHeader(register Volume * vp)
-{
-    Error error;
-    register struct volHeader *hd;
-    int old;
-    static int everLogged = 0;
-
-    /* XXX debug 9/19/05 we've apparently got
-     * a ref counting bug somewhere that's
-     * breaking the nUsers == 0 => header on LRU
-     * assumption */
-    if (vp->header && queue_IsNotOnQueue(vp->header)) {
-       Log("nUsers == 0, but header not on LRU\n");
-       return 1;
-    }
-
-    old = (vp->header != NULL);        /* old == volume already has a header */
-
-    if (programType != fileServer) {
-       /* for volume utilities, we allocate volHeaders as needed */
-       if (!vp->header) {
-           hd = (struct volHeader *)calloc(1, sizeof(*vp->header));
-           assert(hd != NULL);
-           vp->header = hd;
-           hd->back = vp;
-           V_attachFlags(vp) |= VOL_HDR_ATTACHED;
-       }
-    } else {
-       if (old) {
-           /* the header we previously dropped in the lru is
-            * still available. pull it off the lru and return */
-           hd = vp->header;
-           queue_Remove(hd);
-           assert(hd->back == vp);
-       } else {
-           /* we need to grab a new element off the LRU */
-           if (queue_IsNotEmpty(&volume_hdr_LRU)) {
-               /* grab an element and pull off of LRU */
-               hd = queue_First(&volume_hdr_LRU, volHeader);
-               queue_Remove(hd);
-           } else {
-               /* LRU is empty, so allocate a new volHeader 
-                * this is probably indicative of a leak, so let the user know */
-               hd = (struct volHeader *)calloc(1, sizeof(struct volHeader));
-               assert(hd != NULL);
-               if (!everLogged) {
-                   Log("****Allocated more volume headers, probably leak****\n");
-                   everLogged = 1;
-               }
-               volume_hdr_LRU.stats.free++;
-           }
-           if (hd->back) {
-               VolState vp_save, back_save;
-               /* this header used to belong to someone else. 
-                * we'll need to check if the header needs to
-                * be sync'd out to disk */
-
-               /* if hd->back were in an exclusive state, then
-                * its volHeader would not be on the LRU... */
-               assert(!IsExclusiveState(V_attachState(hd->back)));
-
-               if (hd->diskstuff.inUse) {
-                   /* volume was in use, so we'll need to sync
-                    * its header to disk */
-                   back_save = VChangeState_r(hd->back, VOL_STATE_UPDATING);
-                   vp_save = VChangeState_r(vp, VOL_STATE_HDR_ATTACHING);
-                   VCreateReservation_r(hd->back);
-                   VOL_UNLOCK;
+    assert(hp != NULL);
 
-                   WriteVolumeHeader_r(&error, hd->back);
-                   /* Ignore errors; catch them later */
-
-                   VOL_LOCK;
-               }
-
-               V_attachFlags(hd->back) &= ~(VOL_HDR_ATTACHED | VOL_HDR_LOADED | VOL_HDR_IN_LRU);
-               hd->back->header = NULL;
-
-               if (hd->diskstuff.inUse) {
-                   VChangeState_r(hd->back, back_save);
-                   VCancelReservation_r(hd->back);
-                   VChangeState_r(vp, vp_save);
-               }
-           } else {
-               volume_hdr_LRU.stats.attached++;
-           }
-           hd->back = vp;
-           vp->header = hd;
-           V_attachFlags(vp) |= VOL_HDR_ATTACHED;
-       }
-       volume_hdr_LRU.stats.free--;
-       volume_hdr_LRU.stats.used++;
-    }
-    IncUInt64(&VStats.hdr_gets);
-    IncUInt64(&vp->stats.hdr_gets);
-    vp->stats.last_hdr_get = FT_ApproxTime();
-    return old;
+    while (howMany--)
+       /* We are using ReleaseVolumeHeader to initialize the values on the header list
+        * to ensure they have the right values
+        */
+       ReleaseVolumeHeader(hp++);
 }
-#else /* AFS_DEMAND_ATTACH_FS */
-/* Get a volume header from the LRU list; update the old one if necessary */
-/* Returns 1 if there was already a header, which is removed from the LRU list */
+
+/**
+ * get a volume header and attach it to the volume object.
+ *
+ * @param[in] vp  pointer to volume object
+ *
+ * @return cache entry status
+ *    @retval 0  volume header was newly attached; cache data is invalid
+ *    @retval 1  volume header was previously attached; cache data is valid
+ *
+ * @pre VOL_LOCK held.  For DAFS, lightweight ref must be held on volume object.
+ *
+ * @post volume header attached to volume object.  if necessary, header cache 
+ *       entry on LRU is synchronized to disk.  Header is removed from LRU list.
+ *
+ * @note VOL_LOCK may be dropped
+ *
+ * @warning this interface does not load header data from disk.  it merely
+ *          attaches a header object to the volume object, and may sync the old
+ *          header cache data out to disk in the process.
+ *
+ * @internal volume package internal use only.
+ */
 static int
 GetVolumeHeader(register Volume * vp)
 {
@@ -5473,6 +6325,19 @@ GetVolumeHeader(register Volume * vp)
     int old;
     static int everLogged = 0;
 
+#ifdef AFS_DEMAND_ATTACH_FS
+    VolState vp_save = 0, back_save = 0;
+
+    /* XXX debug 9/19/05 we've apparently got
+     * a ref counting bug somewhere that's
+     * breaking the nUsers == 0 => header on LRU
+     * assumption */
+    if (vp->header && queue_IsNotOnQueue(vp->header)) {
+       Log("nUsers == 0, but header not on LRU\n");
+       return 1;
+    }
+#endif
+
     old = (vp->header != NULL);        /* old == volume already has a header */
 
     if (programType != fileServer) {
@@ -5482,6 +6347,9 @@ GetVolumeHeader(register Volume * vp)
            assert(hd != NULL);
            vp->header = hd;
            hd->back = vp;
+#ifdef AFS_DEMAND_ATTACH_FS
+           V_attachFlags(vp) |= VOL_HDR_ATTACHED;
+#endif
        }
     } else {
        /* for the fileserver, we keep a volume header cache */
@@ -5494,7 +6362,7 @@ GetVolumeHeader(register Volume * vp)
        } else {
            /* we need to grab a new element off the LRU */
            if (queue_IsNotEmpty(&volume_hdr_LRU)) {
-               /* grab an element */
+               /* grab an element and pull off of LRU */
                hd = queue_First(&volume_hdr_LRU, volHeader);
                queue_Remove(hd);
            } else {
@@ -5506,36 +6374,88 @@ GetVolumeHeader(register Volume * vp)
                    Log("****Allocated more volume headers, probably leak****\n");
                    everLogged = 1;
                }
+               volume_hdr_LRU.stats.free++;
            }
            if (hd->back) {
                /* this header used to belong to someone else. 
                 * we'll need to check if the header needs to
                 * be sync'd out to disk */
 
+#ifdef AFS_DEMAND_ATTACH_FS
+               /* if hd->back were in an exclusive state, then
+                * its volHeader would not be on the LRU... */
+               assert(!VIsExclusiveState(V_attachState(hd->back)));
+#endif
+
                if (hd->diskstuff.inUse) {
+                   /* volume was in use, so we'll need to sync
+                    * its header to disk */
+
+#ifdef AFS_DEMAND_ATTACH_FS
+                   back_save = VChangeState_r(hd->back, VOL_STATE_UPDATING);
+                   vp_save = VChangeState_r(vp, VOL_STATE_HDR_ATTACHING);
+                   VCreateReservation_r(hd->back);
+                   VOL_UNLOCK;
+#endif
+
                    WriteVolumeHeader_r(&error, hd->back);
                    /* Ignore errors; catch them later */
+
+#ifdef AFS_DEMAND_ATTACH_FS
+                   VOL_LOCK;
+#endif
                }
+
                hd->back->header = NULL;
+#ifdef AFS_DEMAND_ATTACH_FS
+               V_attachFlags(hd->back) &= ~(VOL_HDR_ATTACHED | VOL_HDR_LOADED | VOL_HDR_IN_LRU);
+
+               if (hd->diskstuff.inUse) {
+                   VChangeState_r(hd->back, back_save);
+                   VCancelReservation_r(hd->back);
+                   VChangeState_r(vp, vp_save);
+               }
+#endif
+           } else {
+               volume_hdr_LRU.stats.attached++;
            }
            hd->back = vp;
            vp->header = hd;
+#ifdef AFS_DEMAND_ATTACH_FS
+           V_attachFlags(vp) |= VOL_HDR_ATTACHED;
+#endif
        }
+       volume_hdr_LRU.stats.free--;
+       volume_hdr_LRU.stats.used++;
     }
+    IncUInt64(&VStats.hdr_gets);
+#ifdef AFS_DEMAND_ATTACH_FS
+    IncUInt64(&vp->stats.hdr_gets);
+    vp->stats.last_hdr_get = FT_ApproxTime();
+#endif
     return old;
 }
-#endif /* AFS_DEMAND_ATTACH_FS */
 
 
-/* make sure a volume header is attached to
- * vp, and has the correct data loaded from
- * disk. */
-#ifdef AFS_DEMAND_ATTACH_FS
-/* caller MUST hold a ref count on vp */
+/**
+ * make sure volume header is attached and contains valid cache data.
+ *
+ * @param[out] ec  outbound error code
+ * @param[in]  vp  pointer to volume object
+ *
+ * @pre VOL_LOCK held.  For DAFS, lightweight ref held on vp.
+ *
+ * @post header cache entry attached, and loaded with valid data, or
+ *       *ec is nonzero, and the header is released back into the LRU.
+ *
+ * @internal volume package internal use only.
+ */
 static void
 LoadVolumeHeader(Error * ec, Volume * vp)
 {
+#ifdef AFS_DEMAND_ATTACH_FS
     VolState state_save;
+    afs_uint32 now;
     *ec = 0;
 
     if (vp->nUsers == 0 && !GetVolumeHeader(vp)) {
@@ -5547,21 +6467,16 @@ LoadVolumeHeader(Error * ec, Volume * vp)
                   sizeof(V_disk(vp)), VOLUMEINFOMAGIC,
                   VOLUMEINFOVERSION);
        IncUInt64(&vp->stats.hdr_loads);
+       now = FT_ApproxTime();
 
        VOL_LOCK;
-       if (!*ec)
+       if (!*ec) {
            V_attachFlags(vp) |= VOL_HDR_LOADED;
+           vp->stats.last_hdr_load = now;
+       }
        VChangeState_r(vp, state_save);
     }
-    if (*ec) {
-       /* maintain (nUsers==0) => header in LRU invariant */
-       ReleaseVolumeHeader(vp->header);
-    }
-}
 #else /* AFS_DEMAND_ATTACH_FS */
-static void
-LoadVolumeHeader(Error * ec, Volume * vp)
-{
     *ec = 0;
     if (vp->nUsers == 0 && !GetVolumeHeader(vp)) {
        IncUInt64(&VStats.hdr_loads);
@@ -5570,14 +6485,29 @@ LoadVolumeHeader(Error * ec, Volume * vp)
                   sizeof(V_disk(vp)), VOLUMEINFOMAGIC,
                   VOLUMEINFOVERSION);
     }
+#endif /* AFS_DEMAND_ATTACH_FS */
     if (*ec) {
        /* maintain (nUsers==0) => header in LRU invariant */
-       ReleaseVolumeHeader(vp->header);
+       FreeVolumeHeader(vp);
     }
 }
-#endif /* AFS_DEMAND_ATTACH_FS */
 
-/* Put it at the top of the LRU chain */
+/**
+ * release a header cache entry back into the LRU list.
+ *
+ * @param[in] hd  pointer to volume header cache object
+ *
+ * @pre VOL_LOCK held.
+ *
+ * @post header cache object appended onto end of LRU list.
+ *
+ * @note only applicable to fileServer program type.
+ *
+ * @note used to place a header cache entry back into the
+ *       LRU pool without invalidating it as a cache entry.
+ *
+ * @internal volume package internal use only.
+ */
 static void
 ReleaseVolumeHeader(register struct volHeader *hd)
 {
@@ -5590,15 +6520,29 @@ ReleaseVolumeHeader(register struct volHeader *hd)
     if (hd->back) {
        V_attachFlags(hd->back) |= VOL_HDR_IN_LRU;
     }
+#endif
     volume_hdr_LRU.stats.free++;
     volume_hdr_LRU.stats.used--;
-#endif
 }
 
-/* for fileserver, return header to LRU, and
- * invalidate it as a cache entry.
+/**
+ * free/invalidate a volume header cache entry.
+ *
+ * @param[in] vp  pointer to volume object
  *
- * for volume utilities, free the heap space */
+ * @pre VOL_LOCK is held.
+ *
+ * @post For fileserver, header cache entry is returned to LRU, and it is
+ *       invalidated as a cache entry.  For volume utilities, the header
+ *       cache entry is freed.
+ *
+ * @note For fileserver, this should be utilized instead of ReleaseVolumeHeader
+ *       whenever it is necessary to invalidate the header cache entry.
+ *
+ * @see ReleaseVolumeHeader
+ *
+ * @internal volume package internal use only.
+ */
 static void
 FreeVolumeHeader(register Volume * vp)
 {
@@ -5613,8 +6557,8 @@ FreeVolumeHeader(register Volume * vp)
     }
 #ifdef AFS_DEMAND_ATTACH_FS
     V_attachFlags(vp) &= ~(VOL_HDR_ATTACHED | VOL_HDR_IN_LRU | VOL_HDR_LOADED);
-    volume_hdr_LRU.stats.attached--;
 #endif
+    volume_hdr_LRU.stats.attached--;
     vp->header = NULL;
 }
 
@@ -5623,6 +6567,19 @@ FreeVolumeHeader(register Volume * vp)
 /* Volume Hash Table routines                      */
 /***************************************************/
 
+/**
+ * set size of volume object hash table.
+ *
+ * @param[in] logsize   log(2) of desired hash table size
+ *
+ * @return operation status
+ *    @retval 0 success
+ *    @retval -1 failure
+ *
+ * @pre MUST be called prior to VInitVolumePackage
+ *
+ * @post Volume Hash Table will have 2^logsize buckets
+ */
 int 
 VSetVolHashSize(int logsize)
 {
@@ -5643,6 +6600,13 @@ VSetVolHashSize(int logsize)
     return 0;
 }
 
+/**
+ * initialize dynamic data structures for volume hash table.
+ *
+ * @post hash table is allocated, and fields are initialized.
+ *
+ * @internal volume package internal use only.
+ */
 static void
 VInitVolumeHash(void)
 {
@@ -5660,7 +6624,22 @@ VInitVolumeHash(void)
     }
 }
 
-/* for demand-attach, caller MUST hold a ref count on vp */
+/**
+ * add a volume object to the hash table.
+ *
+ * @param[in] vp      pointer to volume object
+ * @param[in] hashid  hash of volume id
+ *
+ * @pre VOL_LOCK is held.  For DAFS, caller must hold a lightweight
+ *      reference on vp.
+ *
+ * @post volume is added to hash chain.
+ *
+ * @internal volume package internal use only.
+ *
+ * @note For DAFS, VOL_LOCK may be dropped in order to wait for an
+ *       asynchronous hash chain reordering to finish.
+ */
 static void
 AddVolumeToHashTable(register Volume * vp, int hashid)
 {
@@ -5685,7 +6664,21 @@ AddVolumeToHashTable(register Volume * vp, int hashid)
     vp->vnodeHashOffset = VolumeHashOffset_r();
 }
 
-/* for demand-attach, caller MUST hold a ref count on vp */
+/**
+ * delete a volume object from the hash table.
+ *
+ * @param[in] vp  pointer to volume object
+ *
+ * @pre VOL_LOCK is held.  For DAFS, caller must hold a lightweight
+ *      reference on vp.
+ *
+ * @post volume is removed from hash chain.
+ *
+ * @internal volume package internal use only.
+ *
+ * @note For DAFS, VOL_LOCK may be dropped in order to wait for an
+ *       asynchronous hash chain reordering to finish.
+ */
 static void
 DeleteVolumeFromHashTable(register Volume * vp)
 {
@@ -5711,19 +6704,45 @@ DeleteVolumeFromHashTable(register Volume * vp)
      * after the volume is removed from the hash */
 }
 
-/* - look up a volume id in the hash table
- * - occasionally rebalance hash chains
- * - update lookup statistics accordingly
+/**
+ * lookup a volume object in the hash table given a volume id.
+ *
+ * @param[out] ec        error code return
+ * @param[in]  volumeId  volume id
+ * @param[in]  hint      volume object which we believe could be the correct 
+                         mapping
+ *
+ * @return volume object pointer
+ *    @retval NULL  no such volume id is registered with the hash table.
+ *
+ * @pre VOL_LOCK is held.  For DAFS, caller must hold a lightweight 
+        ref on hint.
+ *
+ * @post volume object with the given id is returned.  volume object and 
+ *       hash chain access statistics are updated.  hash chain may have 
+ *       been reordered.
+ *
+ * @note For DAFS, VOL_LOCK may be dropped in order to wait for an 
+ *       asynchronous hash chain reordering operation to finish, or 
+ *       in order for us to perform an asynchronous chain reordering.
+ *
+ * @note Hash chain reorderings occur when the access count for the 
+ *       volume object being looked up exceeds the sum of the previous 
+ *       node's (the node ahead of it in the hash chain linked list) 
+ *       access count plus the constant VOLUME_HASH_REORDER_THRESHOLD.
+ *
+ * @note For DAFS, the hint parameter allows us to short-circuit if the 
+ *       cacheCheck fields match between the hash chain head and the 
+ *       hint volume object.
  */
-/* the hint parameter allows us to short-circuit on
- * DEMAND_ATTACH_FS if the cacheChecks match between
- * the hash chain head and hint
- * caller MUST hold a refcount on hint */
 Volume *
 VLookupVolume_r(Error * ec, VolId volumeId, Volume * hint)
 {
     register int looks = 0;
-    Volume * vp, *np, *pp;
+    Volume * vp, *np;
+#ifdef AFS_DEMAND_ATTACH_FS
+    Volume *pp;
+#endif
     VolumeHashChainHead * head;
     *ec = 0;
 
@@ -5845,7 +6864,27 @@ VReorderHash_r(VolumeHashChainHead * head, Volume * pp, Volume * vp)
 /* demand-attach fs volume hash
  * asynchronous exclusive operations */
 
-/* take exclusive control over the hash chain */
+/**
+ * begin an asynchronous exclusive operation on a volume hash chain.
+ *
+ * @param[in] head   pointer to volume hash chain head object
+ *
+ * @pre VOL_LOCK held.  hash chain is quiescent.
+ *
+ * @post hash chain marked busy.
+ *
+ * @note this interface is used in conjunction with VHashEndExclusive_r and
+ *       VHashWait_r to perform asynchronous (wrt VOL_LOCK) operations on a
+ *       volume hash chain.  Its main use case is hash chain reordering, which
+ *       has the potential to be a highly latent operation.
+ *
+ * @see VHashEndExclusive_r
+ * @see VHashWait_r
+ *
+ * @note DAFS only
+ *
+ * @internal volume package internal use only.
+ */
 static void
 VHashBeginExclusive_r(VolumeHashChainHead * head)
 {
@@ -5853,7 +6892,23 @@ VHashBeginExclusive_r(VolumeHashChainHead * head)
     head->busy = 1;
 }
 
-/* relinquish exclusive control over the hash chain */
+/**
+ * relinquish exclusive ownership of a volume hash chain.
+ *
+ * @param[in] head   pointer to volume hash chain head object
+ *
+ * @pre VOL_LOCK held.  thread owns the hash chain exclusively.
+ *
+ * @post hash chain is marked quiescent.  threads awaiting use of
+ *       chain are awakened.
+ *
+ * @see VHashBeginExclusive_r
+ * @see VHashWait_r
+ *
+ * @note DAFS only
+ *
+ * @internal volume package internal use only.
+ */
 static void
 VHashEndExclusive_r(VolumeHashChainHead * head)
 {
@@ -5862,12 +6917,35 @@ VHashEndExclusive_r(VolumeHashChainHead * head)
     assert(pthread_cond_broadcast(&head->chain_busy_cv) == 0);
 }
 
-/* wait for another thread to finish its exclusive ops */
+/**
+ * wait for all asynchronous operations on a hash chain to complete.
+ *
+ * @param[in] head   pointer to volume hash chain head object
+ *
+ * @pre VOL_LOCK held.
+ *
+ * @post hash chain object is quiescent.
+ *
+ * @see VHashBeginExclusive_r
+ * @see VHashEndExclusive_r
+ *
+ * @note DAFS only
+ *
+ * @note This interface should be called before any attempt to
+ *       traverse the hash chain.  It is permissible for a thread
+ *       to gain exclusive access to the chain, and then perform
+ *       latent operations on the chain asynchronously wrt the 
+ *       VOL_LOCK.
+ *
+ * @warning if waiting is necessary, VOL_LOCK is dropped
+ *
+ * @internal volume package internal use only.
+ */
 static void
 VHashWait_r(VolumeHashChainHead * head)
 {
     while (head->busy) {
-       assert(pthread_cond_wait(&head->chain_busy_cv, &vol_glock_mutex) == 0);
+       VOL_CV_WAIT(&head->chain_busy_cv);
     }
 }
 #endif /* AFS_DEMAND_ATTACH_FS */
@@ -5886,6 +6964,28 @@ VHashWait_r(VolumeHashChainHead * head)
  */
 
 #ifdef AFS_DEMAND_ATTACH_FS
+/**
+ * add a volume to its disk partition VByPList.
+ *
+ * @param[in] vp  pointer to volume object
+ *
+ * @pre either the disk partition VByPList is owned exclusively
+ *      by the calling thread, or the list is quiescent and
+ *      VOL_LOCK is held.
+ *
+ * @post volume is added to disk partition VByPList
+ *
+ * @note DAFS only
+ *
+ * @warning it is the caller's responsibility to ensure list
+ *          quiescence.
+ *
+ * @see VVByPListWait_r
+ * @see VVByPListBeginExclusive_r
+ * @see VVByPListEndExclusive_r
+ *
+ * @internal volume package internal use only.
+ */
 static void
 AddVolumeToVByPList_r(Volume * vp)
 {
@@ -5896,6 +6996,28 @@ AddVolumeToVByPList_r(Volume * vp)
     }
 }
 
+/**
+ * delete a volume from its disk partition VByPList.
+ *
+ * @param[in] vp  pointer to volume object
+ *
+ * @pre either the disk partition VByPList is owned exclusively
+ *      by the calling thread, or the list is quiescent and
+ *      VOL_LOCK is held.
+ *
+ * @post volume is removed from the disk partition VByPList
+ *
+ * @note DAFS only
+ *
+ * @warning it is the caller's responsibility to ensure list
+ *          quiescence.
+ *
+ * @see VVByPListWait_r
+ * @see VVByPListBeginExclusive_r
+ * @see VVByPListEndExclusive_r
+ *
+ * @internal volume package internal use only.
+ */
 static void
 DeleteVolumeFromVByPList_r(Volume * vp)
 {
@@ -5906,29 +7028,88 @@ DeleteVolumeFromVByPList_r(Volume * vp)
     }
 }
 
+/**
+ * begin an asynchronous exclusive operation on a VByPList.
+ *
+ * @param[in] dp   pointer to disk partition object
+ *
+ * @pre VOL_LOCK held.  VByPList is quiescent.
+ *
+ * @post VByPList marked busy.
+ *
+ * @note this interface is used in conjunction with VVByPListEndExclusive_r and
+ *       VVByPListWait_r to perform asynchronous (wrt VOL_LOCK) operations on a
+ *       VByPList.
+ *
+ * @see VVByPListEndExclusive_r
+ * @see VVByPListWait_r
+ *
+ * @note DAFS only
+ *
+ * @internal volume package internal use only.
+ */
 /* take exclusive control over the list */
 static void
-VVByPListBeginExclusive_r(struct DiskPartition * dp)
+VVByPListBeginExclusive_r(struct DiskPartition64 * dp)
 {
     assert(dp->vol_list.busy == 0);
     dp->vol_list.busy = 1;
 }
 
-/* relinquish exclusive control over the list */
+/**
+ * relinquish exclusive ownership of a VByPList.
+ *
+ * @param[in] dp   pointer to disk partition object
+ *
+ * @pre VOL_LOCK held.  thread owns the VByPList exclusively.
+ *
+ * @post VByPList is marked quiescent.  threads awaiting use of
+ *       the list are awakened.
+ *
+ * @see VVByPListBeginExclusive_r
+ * @see VVByPListWait_r
+ *
+ * @note DAFS only
+ *
+ * @internal volume package internal use only.
+ */
 static void
-VVByPListEndExclusive_r(struct DiskPartition * dp)
+VVByPListEndExclusive_r(struct DiskPartition64 * dp)
 {
     assert(dp->vol_list.busy);
     dp->vol_list.busy = 0;
     assert(pthread_cond_broadcast(&dp->vol_list.cv) == 0);
 }
 
-/* wait for another thread to finish its exclusive ops */
+/**
+ * wait for all asynchronous operations on a VByPList to complete.
+ *
+ * @param[in] dp  pointer to disk partition object
+ *
+ * @pre VOL_LOCK is held.
+ *
+ * @post disk partition's VByP list is quiescent
+ *
+ * @note DAFS only
+ *
+ * @note This interface should be called before any attempt to
+ *       traverse the VByPList.  It is permissible for a thread
+ *       to gain exclusive access to the list, and then perform
+ *       latent operations on the list asynchronously wrt the 
+ *       VOL_LOCK.
+ *
+ * @warning if waiting is necessary, VOL_LOCK is dropped
+ *
+ * @see VVByPListEndExclusive_r
+ * @see VVByPListBeginExclusive_r
+ *
+ * @internal volume package internal use only.
+ */
 static void
-VVByPListWait_r(struct DiskPartition * dp)
+VVByPListWait_r(struct DiskPartition64 * dp)
 {
     while (dp->vol_list.busy) {
-       assert(pthread_cond_wait(&dp->vol_list.cv, &vol_glock_mutex) == 0);
+       VOL_CV_WAIT(&dp->vol_list.cv);
     }
 }
 #endif /* AFS_DEMAND_ATTACH_FS */
@@ -5991,10 +7172,110 @@ DoubleToPrintable(double x, char * buf, int len)
     return buf;
 }
 
+struct VLRUExtStatsEntry {
+    VolumeId volid;
+};
+
+struct VLRUExtStats {
+    afs_uint32 len;
+    afs_uint32 used;
+    struct {
+       afs_uint32 start;
+       afs_uint32 len;
+    } queue_info[VLRU_QUEUE_INVALID];
+    struct VLRUExtStatsEntry * vec;
+};
+
+/** 
+ * add a 256-entry fudge factor onto the vector in case state changes
+ * out from under us.
+ */
+#define VLRU_EXT_STATS_VEC_LEN_FUDGE   256
+
+/**
+ * collect extended statistics for the VLRU subsystem.
+ *
+ * @param[out] stats  pointer to stats structure to be populated
+ * @param[in] nvols   number of volumes currently known to exist
+ *
+ * @pre VOL_LOCK held
+ *
+ * @post stats->vec allocated and populated
+ *
+ * @return operation status
+ *    @retval 0 success
+ *    @retval 1 failure
+ */
+static int
+VVLRUExtStats_r(struct VLRUExtStats * stats, afs_uint32 nvols)
+{
+    afs_uint32 cur, idx, len;
+    struct rx_queue * qp, * nqp;
+    Volume * vp;
+    struct VLRUExtStatsEntry * vec;
+
+    len = nvols + VLRU_EXT_STATS_VEC_LEN_FUDGE;
+    vec = stats->vec = calloc(len,
+                             sizeof(struct VLRUExtStatsEntry));
+    if (vec == NULL) {
+       return 1;
+    }
+
+    cur = 0;
+    for (idx = VLRU_QUEUE_NEW; idx < VLRU_QUEUE_INVALID; idx++) {
+       VLRU_Wait_r(&volume_LRU.q[idx]);
+       VLRU_BeginExclusive_r(&volume_LRU.q[idx]);
+       VOL_UNLOCK;
+
+       stats->queue_info[idx].start = cur;
+
+       for (queue_Scan(&volume_LRU.q[idx], qp, nqp, rx_queue)) {
+           if (cur == len) {
+               /* out of space in vec */
+               break;
+           }
+           vp = (Volume *)((char *)qp - offsetof(Volume, vlru));
+           vec[cur].volid = vp->hashid;
+           cur++;
+       }
+
+       stats->queue_info[idx].len = cur - stats->queue_info[idx].start;
+
+       VOL_LOCK;
+       VLRU_EndExclusive_r(&volume_LRU.q[idx]);
+    }
+
+    stats->len = len;
+    stats->used = cur;
+    return 0;
+}
+
+#define ENUMTOSTRING(en)  #en
+#define ENUMCASE(en) \
+    case en: \
+        return ENUMTOSTRING(en); \
+        break
+
+static char *
+vlru_idx_to_string(int idx)
+{
+    switch (idx) {
+       ENUMCASE(VLRU_QUEUE_NEW);
+       ENUMCASE(VLRU_QUEUE_MID);
+       ENUMCASE(VLRU_QUEUE_OLD);
+       ENUMCASE(VLRU_QUEUE_CANDIDATE);
+       ENUMCASE(VLRU_QUEUE_HELD);
+       ENUMCASE(VLRU_QUEUE_INVALID);
+    default:
+       return "**UNKNOWN**";
+    }
+}
+
 void
 VPrintExtendedCacheStats_r(int flags)
 {
-    int i, j;
+    int i;
+    afs_uint32 vol_sum = 0;
     struct stats {
        double min;
        double max;
@@ -6006,6 +7287,7 @@ VPrintExtendedCacheStats_r(int flags)
     char pr_buf[4][32];
     VolumeHashChainHead *head;
     Volume *vp, *np;
+    struct VLRUExtStats vlru_stats;
 
     /* zero out stats */
     memset(&looks, 0, sizeof(struct stats));
@@ -6033,6 +7315,7 @@ VPrintExtendedCacheStats_r(int flags)
            gets.sum     += ch_gets.sum;
            reorders.sum += ch_reorders.sum;
            len.sum      += (double)head->len;
+           vol_sum      += head->len;
            
            if (i == 0) {
                len.min      = (double) head->len;
@@ -6176,7 +7459,7 @@ VPrintExtendedCacheStats_r(int flags)
 
     /* print extended disk related statistics */
     {
-       struct DiskPartition * diskP;
+       struct DiskPartition64 * diskP;
        afs_uint32 vol_count[VOLMAXPARTS+1];
        byte part_exists[VOLMAXPARTS+1];
        Device id;
@@ -6188,7 +7471,7 @@ VPrintExtendedCacheStats_r(int flags)
        VOL_LOCK;
 
        for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
-           id = diskP->device;
+           id = diskP->index;
            vol_count[id] = diskP->vol_list.len;
            part_exists[id] = 1;
        }
@@ -6196,6 +7479,8 @@ VPrintExtendedCacheStats_r(int flags)
        VOL_UNLOCK;
        for (i = 0; i <= VOLMAXPARTS; i++) {
            if (part_exists[i]) {
+               /* XXX while this is currently safe, it is a violation
+                *     of the VGetPartitionById_r interface contract. */
                diskP = VGetPartitionById_r(i, 0);
                if (diskP) {
                    Log("Partition %s has %d online volumes\n", 
@@ -6206,6 +7491,43 @@ VPrintExtendedCacheStats_r(int flags)
        VOL_LOCK;
     }
 
+    /* print extended VLRU statistics */
+    if (VVLRUExtStats_r(&vlru_stats, vol_sum) == 0) {
+       afs_uint32 idx, cur, lpos;
+       VOL_UNLOCK;
+       VolumeId line[5];
+
+       Log("VLRU State Dump:\n\n");
+
+       for (idx = VLRU_QUEUE_NEW; idx < VLRU_QUEUE_INVALID; idx++) {
+           Log("\t%s:\n", vlru_idx_to_string(idx));
+
+           lpos = 0;
+           for (cur = vlru_stats.queue_info[idx].start;
+                cur < vlru_stats.queue_info[idx].len;
+                cur++) {
+               line[lpos++] = vlru_stats.vec[cur].volid;
+               if (lpos==5) {
+                   Log("\t\t%u, %u, %u, %u, %u,\n",
+                       line[0], line[1], line[2], line[3], line[4]);
+                   lpos = 0;
+               }
+           }
+
+           if (lpos) {
+               while (lpos < 5) {
+                   line[lpos++] = 0;
+               }
+               Log("\t\t%u, %u, %u, %u, %u\n",
+                   line[0], line[1], line[2], line[3], line[4]);
+           }
+           Log("\n");
+       }
+
+       free(vlru_stats.vec);
+
+       VOL_LOCK;
+    }
 }
 
 void