88e355f551693e6e248360fea29711d09a248af5
[openafs.git] / src / vol / volume.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  *
9  * Portions Copyright (c) 2005-2008 Sine Nomine Associates
10  */
11
12 /* 1/1/89: NB:  this stuff is all going to be replaced.  Don't take it too seriously */
13 /*
14
15         System:         VICE-TWO
16         Module:         volume.c
17         Institution:    The Information Technology Center, Carnegie-Mellon University
18
19  */
20
21 #include <afsconfig.h>
22 #include <afs/param.h>
23
24 #include <roken.h>
25 #include <afs/opr.h>
26
27 #include <ctype.h>
28 #include <stddef.h>
29
30 #ifdef HAVE_SYS_FILE_H
31 #include <sys/file.h>
32 #endif
33
34 #ifdef AFS_PTHREAD_ENV
35 # include <opr/lock.h>
36 #else
37 # include <opr/lockstub.h>
38 #endif
39 #include <opr/ffs.h>
40 #include <opr/jhash.h>
41
42 #include <afs/afsint.h>
43
44 #include <rx/rx_queue.h>
45
46 #ifndef AFS_NT40_ENV
47 #if !defined(AFS_SGI_ENV)
48 #ifdef  AFS_OSF_ENV
49 #include <ufs/fs.h>
50 #else /* AFS_OSF_ENV */
51 #ifdef AFS_VFSINCL_ENV
52 #define VFS
53 #ifdef  AFS_SUN5_ENV
54 #include <sys/fs/ufs_fs.h>
55 #else
56 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
57 #include <ufs/ufs/dinode.h>
58 #include <ufs/ffs/fs.h>
59 #else
60 #include <ufs/fs.h>
61 #endif
62 #endif
63 #else /* AFS_VFSINCL_ENV */
64 #if !defined(AFS_AIX_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV) && !defined(AFS_DARWIN_ENV)
65 #include <sys/fs.h>
66 #endif
67 #endif /* AFS_VFSINCL_ENV */
68 #endif /* AFS_OSF_ENV */
69 #endif /* AFS_SGI_ENV */
70 #endif /* !AFS_NT40_ENV */
71
72 #ifdef  AFS_AIX_ENV
73 #include <sys/vfs.h>
74 #else
75 #ifdef  AFS_HPUX_ENV
76 #include <mntent.h>
77 #else
78 #if     defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
79 #ifdef  AFS_SUN5_ENV
80 #include <sys/mnttab.h>
81 #include <sys/mntent.h>
82 #else
83 #include <mntent.h>
84 #endif
85 #else
86 #ifndef AFS_NT40_ENV
87 #if defined(AFS_SGI_ENV)
88 #include <mntent.h>
89 #else
90 #ifndef AFS_LINUX20_ENV
91 #include <fstab.h>              /* Need to find in libc 5, present in libc 6 */
92 #endif
93 #endif
94 #endif /* AFS_SGI_ENV */
95 #endif
96 #endif /* AFS_HPUX_ENV */
97 #endif
98
99 #include "nfs.h"
100 #include <afs/errors.h>
101 #include "lock.h"
102 #include "lwp.h"
103 #include <afs/afssyscalls.h>
104 #include "ihandle.h"
105 #include <afs/afsutil.h>
106 #include "daemon_com.h"
107 #include "fssync.h"
108 #include "salvsync.h"
109 #include "vnode.h"
110 #include "volume.h"
111 #include "partition.h"
112 #include "volume_inline.h"
113 #include "common.h"
114 #include "vutils.h"
115 #include <afs/dir.h>
116
117 #ifdef AFS_PTHREAD_ENV
118 pthread_mutex_t vol_glock_mutex;
119 pthread_mutex_t vol_trans_mutex;
120 pthread_cond_t vol_put_volume_cond;
121 pthread_cond_t vol_sleep_cond;
122 pthread_cond_t vol_init_attach_cond;
123 pthread_cond_t vol_vinit_cond;
124 int vol_attach_threads = 1;
125 #endif /* AFS_PTHREAD_ENV */
126
127 /* start-time configurable I/O parameters */
128 ih_init_params vol_io_params;
129
130 #ifdef AFS_DEMAND_ATTACH_FS
131 pthread_mutex_t vol_salvsync_mutex;
132
133 /*
134  * Set this to 1 to disallow SALVSYNC communication in all threads; used
135  * during shutdown, since the salvageserver may have gone away.
136  */
137 static volatile sig_atomic_t vol_disallow_salvsync = 0;
138 #endif /* AFS_DEMAND_ATTACH_FS */
139
140 /**
141  * has VShutdown_r been called / is VShutdown_r running?
142  */
143 static int vol_shutting_down = 0;
144
145 #ifdef  AFS_OSF_ENV
146 extern void *calloc(), *realloc();
147 #endif
148
149 /* Forward declarations */
150 static Volume *attach2(Error * ec, VolumeId volumeId, char *path,
151                        struct DiskPartition64 *partp, Volume * vp,
152                        int isbusy, int mode, int *acheckedOut);
153 static void ReallyFreeVolume(Volume * vp);
154 #ifdef AFS_DEMAND_ATTACH_FS
155 static void FreeVolume(Volume * vp);
156 #else /* !AFS_DEMAND_ATTACH_FS */
157 #define FreeVolume(vp) ReallyFreeVolume(vp)
158 static void VScanUpdateList(void);
159 #endif /* !AFS_DEMAND_ATTACH_FS */
160 static void VInitVolumeHeaderCache(afs_uint32 howMany);
161 static int GetVolumeHeader(Volume * vp);
162 static void ReleaseVolumeHeader(struct volHeader *hd);
163 static void FreeVolumeHeader(Volume * vp);
164 static void AddVolumeToHashTable(Volume * vp, VolumeId hashid);
165 static void DeleteVolumeFromHashTable(Volume * vp);
166 #if 0
167 static int VHold(Volume * vp);
168 #endif
169 static int VHold_r(Volume * vp);
170 static void VGetBitmap_r(Error * ec, Volume * vp, VnodeClass class);
171 static void VReleaseVolumeHandles_r(Volume * vp);
172 static void VCloseVolumeHandles_r(Volume * vp);
173 static void LoadVolumeHeader(Error * ec, Volume * vp);
174 static int VCheckOffline(Volume * vp);
175 static int VCheckDetach(Volume * vp);
176 static Volume * GetVolume(Error * ec, Error * client_ec, VolumeId volumeId,
177                           Volume * hint, const struct timespec *ts);
178
179 int LogLevel;                   /* Vice loglevel--not defined as extern so that it will be
180                                  * defined when not linked with vice, XXXX */
181 ProgramType programType;        /* The type of program using the package */
182 static VolumePackageOptions vol_opts;
183
184 /* extended volume package statistics */
185 VolPkgStats VStats;
186
187 #ifdef VOL_LOCK_DEBUG
188 pthread_t vol_glock_holder = 0;
189 #endif
190
191
192 /* this parameter needs to be tunable at runtime.
193  * 128 was really inadequate for largish servers -- at 16384 volumes this
194  * puts average chain length at 128, thus an average 65 deref's to find a volptr.
195  * talk about bad spatial locality...
196  *
197  * an AVL or splay tree might work a lot better, but we'll just increase
198  * the default hash table size for now
199  */
200 #define DEFAULT_VOLUME_HASH_BITS 10
201 #define DEFAULT_VOLUME_HASH_SIZE opr_jhash_size(DEFAULT_VOLUME_HASH_BITS)
202 #define DEFAULT_VOLUME_HASH_MASK opr_jhash_mask(DEFAULT_VOLUME_HASH_BITS)
203 #define VOLUME_HASH(volumeId) \
204     (opr_jhash_int(volumeId, 0) & VolumeHashTable.Mask)
205
206 /*
207  * turn volume hash chains into partially ordered lists.
208  * when the threshold is exceeded between two adjacent elements,
209  * perform a chain rebalancing operation.
210  *
211  * keep the threshold high in order to keep cache line invalidates
212  * low "enough" on SMPs
213  */
214 #define VOLUME_HASH_REORDER_THRESHOLD 200
215
216 /*
217  * when possible, don't just reorder single elements, but reorder
218  * entire chains of elements at once.  a chain of elements that
219  * exceed the element previous to the pivot by at least CHAIN_THRESH
220  * accesses are moved in front of the chain whose elements have at
221  * least CHAIN_THRESH less accesses than the pivot element
222  */
223 #define VOLUME_HASH_REORDER_CHAIN_THRESH (VOLUME_HASH_REORDER_THRESHOLD / 2)
224
225 /*
226  * The per volume uniquifier is bumped by 200 and and written to disk
227  * every 200 file creates.
228  */
229 #define VOLUME_UPDATE_UNIQUIFIER_BUMP 200
230
231 #include "rx/rx_queue.h"
232
233
234 VolumeHashTable_t VolumeHashTable = {
235     DEFAULT_VOLUME_HASH_SIZE,
236     DEFAULT_VOLUME_HASH_MASK,
237     NULL
238 };
239
240
241 static void VInitVolumeHash(void);
242
243
244 #ifdef AFS_PTHREAD_ENV
245 /**
246  * disk partition queue element
247  */
248 typedef struct diskpartition_queue_t {
249     struct rx_queue queue;             /**< queue header */
250     struct DiskPartition64 *diskP;     /**< disk partition table entry */
251 } diskpartition_queue_t;
252
253 #ifndef AFS_DEMAND_ATTACH_FS
254
255 typedef struct vinitvolumepackage_thread_t {
256     struct rx_queue queue;
257     pthread_cond_t thread_done_cv;
258     int n_threads_complete;
259 } vinitvolumepackage_thread_t;
260 static void * VInitVolumePackageThread(void * args);
261
262 #else  /* !AFS_DEMAND_ATTTACH_FS */
263 #define VINIT_BATCH_MAX_SIZE 512
264
265 /**
266  * disk partition work queue
267  */
268 struct partition_queue {
269     struct rx_queue head;              /**< diskpartition_queue_t queue */
270     pthread_mutex_t mutex;
271     pthread_cond_t cv;
272 };
273
274 /**
275  * volumes parameters for preattach
276  */
277 struct volume_init_batch {
278     struct rx_queue queue;               /**< queue header */
279     int thread;                          /**< posting worker thread */
280     int last;                            /**< indicates thread is done */
281     int size;                            /**< number of volume ids in batch */
282     Volume *batch[VINIT_BATCH_MAX_SIZE]; /**< volumes ids to preattach */
283 };
284
285 /**
286  * volume parameters work queue
287  */
288 struct volume_init_queue {
289     struct rx_queue head;                /**< volume_init_batch queue */
290     pthread_mutex_t mutex;
291     pthread_cond_t cv;
292 };
293
294 /**
295  * volume init worker thread parameters
296  */
297 struct vinitvolumepackage_thread_param {
298     int nthreads;                        /**< total number of worker threads */
299     int thread;                          /**< thread number for this worker thread */
300     struct partition_queue *pq;          /**< queue partitions to scan */
301     struct volume_init_queue *vq;        /**< queue of volume to preattach */
302 };
303
304 static void *VInitVolumePackageThread(void *args);
305 static struct DiskPartition64 *VInitNextPartition(struct partition_queue *pq);
306 static VolumeId VInitNextVolumeId(DIR *dirp);
307 static int VInitPreAttachVolumes(int nthreads, struct volume_init_queue *vq);
308
309 #endif /* !AFS_DEMAND_ATTACH_FS */
310 #endif /* AFS_PTHREAD_ENV */
311
312 #ifndef AFS_DEMAND_ATTACH_FS
313 static int VAttachVolumesByPartition(struct DiskPartition64 *diskP,
314                                      int * nAttached, int * nUnattached);
315 #endif /* AFS_DEMAND_ATTACH_FS */
316
317
318 #ifdef AFS_DEMAND_ATTACH_FS
319 /* demand attach fileserver extensions */
320
321 /* XXX
322  * in the future we will support serialization of VLRU state into the fs_state
323  * disk dumps
324  *
325  * these structures are the beginning of that effort
326  */
327 struct VLRU_DiskHeader {
328     struct versionStamp stamp;            /* magic and structure version number */
329     afs_uint32 mtime;                     /* time of dump to disk */
330     afs_uint32 num_records;               /* number of VLRU_DiskEntry records */
331 };
332
333 struct VLRU_DiskEntry {
334     VolumeId vid;                       /* volume ID */
335     afs_uint32 idx;                       /* generation */
336     afs_uint32 last_get;                  /* timestamp of last get */
337 };
338
339 struct VLRU_StartupQueue {
340     struct VLRU_DiskEntry * entry;
341     int num_entries;
342     int next_idx;
343 };
344
345 typedef struct vshutdown_thread_t {
346     struct rx_queue q;
347     pthread_mutex_t lock;
348     pthread_cond_t cv;
349     pthread_cond_t master_cv;
350     int n_threads;
351     int n_threads_complete;
352     int vol_remaining;
353     int schedule_version;
354     int pass;
355     byte n_parts;
356     byte n_parts_done_pass;
357     byte part_thread_target[VOLMAXPARTS+1];
358     byte part_done_pass[VOLMAXPARTS+1];
359     struct rx_queue * part_pass_head[VOLMAXPARTS+1];
360     int stats[4][VOLMAXPARTS+1];
361 } vshutdown_thread_t;
362 static void * VShutdownThread(void * args);
363
364
365 static Volume * VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode);
366 static int VCheckFree(Volume * vp);
367
368 /* VByP List */
369 static void AddVolumeToVByPList_r(Volume * vp);
370 static void DeleteVolumeFromVByPList_r(Volume * vp);
371 static void VVByPListBeginExclusive_r(struct DiskPartition64 * dp);
372 static void VVByPListEndExclusive_r(struct DiskPartition64 * dp);
373 static void VVByPListWait_r(struct DiskPartition64 * dp);
374
375 /* online salvager */
376 typedef enum {
377     VCHECK_SALVAGE_OK = 0,         /**< no pending salvage */
378     VCHECK_SALVAGE_SCHEDULED = 1,  /**< salvage has been scheduled */
379     VCHECK_SALVAGE_ASYNC = 2,      /**< salvage being scheduled */
380     VCHECK_SALVAGE_DENIED = 3,     /**< salvage not scheduled; denied */
381     VCHECK_SALVAGE_FAIL = 4        /**< salvage not scheduled; failed */
382 } vsalvage_check;
383 static int VCheckSalvage(Volume * vp);
384 #if defined(SALVSYNC_BUILD_CLIENT) || defined(FSSYNC_BUILD_CLIENT)
385 static int VScheduleSalvage_r(Volume * vp);
386 #endif
387
388 /* Volume hash table */
389 static void VReorderHash_r(VolumeHashChainHead * head, Volume * pp, Volume * vp);
390 static void VHashBeginExclusive_r(VolumeHashChainHead * head);
391 static void VHashEndExclusive_r(VolumeHashChainHead * head);
392 static void VHashWait_r(VolumeHashChainHead * head);
393
394 /* shutdown */
395 static int ShutdownVByPForPass_r(struct DiskPartition64 * dp, int pass);
396 static int ShutdownVolumeWalk_r(struct DiskPartition64 * dp, int pass,
397                                 struct rx_queue ** idx);
398 static void ShutdownController(vshutdown_thread_t * params);
399 static void ShutdownCreateSchedule(vshutdown_thread_t * params);
400
401 /* VLRU */
402 static void VLRU_ComputeConstants(void);
403 static void VInitVLRU(void);
404 static void VLRU_Init_Node_r(Volume * vp);
405 static void VLRU_Add_r(Volume * vp);
406 static void VLRU_Delete_r(Volume * vp);
407 static void VLRU_UpdateAccess_r(Volume * vp);
408 static void * VLRU_ScannerThread(void * args);
409 static void VLRU_Scan_r(int idx);
410 static void VLRU_Promote_r(int idx);
411 static void VLRU_Demote_r(int idx);
412 static void VLRU_SwitchQueues(Volume * vp, int new_idx, int append);
413
414 /* soft detach */
415 static int VCheckSoftDetach(Volume * vp, afs_uint32 thresh);
416 static int VCheckSoftDetachCandidate(Volume * vp, afs_uint32 thresh);
417 static int VSoftDetachVolume_r(Volume * vp, afs_uint32 thresh);
418
419
420 pthread_key_t VThread_key;
421 VThreadOptions_t VThread_defaults = {
422     0                           /**< allow salvsync */
423 };
424 #endif /* AFS_DEMAND_ATTACH_FS */
425
426
427 struct Lock vol_listLock;       /* Lock obtained when listing volumes:
428                                  * prevents a volume from being missed
429                                  * if the volume is attached during a
430                                  * list volumes */
431
432
433 /* Common message used when the volume goes off line */
434 char *VSalvageMessage =
435     "Files in this volume are currently unavailable; call operations";
436
437 int VInit;                      /* 0 - uninitialized,
438                                  * 1 - initialized but not all volumes have been attached,
439                                  * 2 - initialized and all volumes have been attached,
440                                  * 3 - initialized, all volumes have been attached, and
441                                  * VConnectFS() has completed. */
442
443 static int vinit_attach_abort = 0;
444
445 bit32 VolumeCacheCheck;         /* Incremented everytime a volume goes on line--
446                                  * used to stamp volume headers and in-core
447                                  * vnodes.  When the volume goes on-line the
448                                  * vnode will be invalidated
449                                  * access only with VOL_LOCK held */
450
451
452
453
454 /***************************************************/
455 /* Startup routines                                */
456 /***************************************************/
457
458 #if defined(FAST_RESTART) && defined(AFS_DEMAND_ATTACH_FS)
459 # error FAST_RESTART and DAFS are incompatible. For the DAFS equivalent \
460         of FAST_RESTART, use the -unsafe-nosalvage fileserver argument
461 #endif
462
463 /**
464  * assign default values to a VolumePackageOptions struct.
465  *
466  * Always call this on a VolumePackageOptions struct first, then set any
467  * specific options you want, then call VInitVolumePackage2.
468  *
469  * @param[in]  pt   caller's program type
470  * @param[out] opts volume package options
471  */
472 void
473 VOptDefaults(ProgramType pt, VolumePackageOptions *opts)
474 {
475     opts->nLargeVnodes = opts->nSmallVnodes = 5;
476     opts->volcache = 0;
477
478     opts->canScheduleSalvage = 0;
479     opts->canUseFSSYNC = 0;
480     opts->canUseSALVSYNC = 0;
481
482     opts->interrupt_rxcall = NULL;
483     opts->offline_timeout = -1;
484     opts->offline_shutdown_timeout = -1;
485     opts->usage_threshold = 128;
486     opts->usage_rate_limit = 5;
487
488 #ifdef FAST_RESTART
489     opts->unsafe_attach = 1;
490 #else /* !FAST_RESTART */
491     opts->unsafe_attach = 0;
492 #endif /* !FAST_RESTART */
493
494     switch (pt) {
495     case fileServer:
496         opts->canScheduleSalvage = 1;
497         opts->canUseSALVSYNC = 1;
498         break;
499
500     case salvageServer:
501         opts->canUseFSSYNC = 1;
502         break;
503
504     case volumeServer:
505         opts->nLargeVnodes = 0;
506         opts->nSmallVnodes = 0;
507
508         opts->canScheduleSalvage = 1;
509         opts->canUseFSSYNC = 1;
510         break;
511
512     default:
513         /* noop */
514         break;
515     }
516 }
517
518 /**
519  * Set VInit to a certain value, and signal waiters.
520  *
521  * @param[in] value  the value to set VInit to
522  *
523  * @pre VOL_LOCK held
524  */
525 static void
526 VSetVInit_r(int value)
527 {
528     VInit = value;
529     opr_cv_broadcast(&vol_vinit_cond);
530 }
531
532 static_inline void
533 VLogOfflineTimeout(const char *type, afs_int32 timeout)
534 {
535     if (timeout < 0) {
536         return;
537     }
538     if (timeout == 0) {
539         Log("VInitVolumePackage: Interrupting clients accessing %s "
540             "immediately\n", type);
541     } else {
542         Log("VInitVolumePackage: Interrupting clients accessing %s "
543             "after %ld second%s\n", type, (long)timeout, timeout==1?"":"s");
544     }
545 }
546
547 int
548 VInitVolumePackage2(ProgramType pt, VolumePackageOptions * opts)
549 {
550     int errors = 0;             /* Number of errors while finding vice partitions. */
551
552     programType = pt;
553     vol_opts = *opts;
554
555 #ifndef AFS_PTHREAD_ENV
556     if (opts->offline_timeout != -1 || opts->offline_shutdown_timeout != -1) {
557         Log("VInitVolumePackage: offline_timeout and/or "
558             "offline_shutdown_timeout was specified, but the volume package "
559             "does not support these for LWP builds\n");
560         return -1;
561     }
562 #endif
563     VLogOfflineTimeout("volumes going offline", opts->offline_timeout);
564     VLogOfflineTimeout("volumes going offline during shutdown",
565                        opts->offline_shutdown_timeout);
566
567     memset(&VStats, 0, sizeof(VStats));
568     VStats.hdr_cache_size = 200;
569
570     VInitPartitionPackage();
571     VInitVolumeHash();
572 #ifdef AFS_DEMAND_ATTACH_FS
573     if (programType == fileServer) {
574         VInitVLRU();
575     } else {
576         VLRU_SetOptions(VLRU_SET_ENABLED, 0);
577     }
578     opr_Verify(pthread_key_create(&VThread_key, NULL) == 0);
579 #endif
580
581     opr_mutex_init(&vol_glock_mutex);
582     opr_mutex_init(&vol_trans_mutex);
583     opr_cv_init(&vol_put_volume_cond);
584     opr_cv_init(&vol_sleep_cond);
585     opr_cv_init(&vol_init_attach_cond);
586     opr_cv_init(&vol_vinit_cond);
587 #ifndef AFS_PTHREAD_ENV
588     IOMGR_Initialize();
589 #endif /* AFS_PTHREAD_ENV */
590     Lock_Init(&vol_listLock);
591
592     srandom(time(0));           /* For VGetVolumeInfo */
593
594 #ifdef AFS_DEMAND_ATTACH_FS
595     opr_mutex_init(&vol_salvsync_mutex);
596 #endif /* AFS_DEMAND_ATTACH_FS */
597
598     /* Ok, we have done enough initialization that fileserver can
599      * start accepting calls, even though the volumes may not be
600      * available just yet.
601      */
602     VInit = 1;
603
604 #if defined(AFS_DEMAND_ATTACH_FS) && defined(SALVSYNC_BUILD_SERVER)
605     if (programType == salvageServer) {
606         SALVSYNC_salvInit();
607     }
608 #endif /* AFS_DEMAND_ATTACH_FS */
609 #ifdef FSSYNC_BUILD_SERVER
610     if (programType == fileServer) {
611         FSYNC_fsInit();
612     }
613 #endif
614 #if defined(AFS_DEMAND_ATTACH_FS) && defined(SALVSYNC_BUILD_CLIENT)
615     if (VCanUseSALVSYNC()) {
616         /* establish a connection to the salvager at this point */
617         opr_Verify(VConnectSALV() != 0);
618     }
619 #endif /* AFS_DEMAND_ATTACH_FS */
620
621     if (opts->volcache > VStats.hdr_cache_size)
622         VStats.hdr_cache_size = opts->volcache;
623     VInitVolumeHeaderCache(VStats.hdr_cache_size);
624
625     VInitVnodes(vLarge, opts->nLargeVnodes);
626     VInitVnodes(vSmall, opts->nSmallVnodes);
627
628
629     errors = VAttachPartitions();
630     if (errors)
631         return -1;
632
633     if (programType != fileServer) {
634         errors = VInitAttachVolumes(programType);
635         if (errors) {
636             return -1;
637         }
638     }
639
640 #ifdef FSSYNC_BUILD_CLIENT
641     if (VCanUseFSSYNC()) {
642         if (!VConnectFS()) {
643 #ifdef AFS_DEMAND_ATTACH_FS
644             if (programType == salvageServer) {
645                 Log("Unable to connect to file server; aborted\n");
646                 exit(1);
647             }
648 #endif /* AFS_DEMAND_ATTACH_FS */
649             Log("Unable to connect to file server; will retry at need\n");
650         }
651     }
652 #endif /* FSSYNC_BUILD_CLIENT */
653     return 0;
654 }
655
656
657 #if !defined(AFS_PTHREAD_ENV)
658 /**
659  * Attach volumes in vice partitions
660  *
661  * @param[in]  pt         calling program type
662  *
663  * @return 0
664  * @note This is the original, non-threaded version of attach parititions.
665  *
666  * @post VInit state is 2
667  */
668 int
669 VInitAttachVolumes(ProgramType pt)
670 {
671     opr_Assert(VInit==1);
672     if (pt == fileServer) {
673         struct DiskPartition64 *diskP;
674         /* Attach all the volumes in this partition */
675         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
676             int nAttached = 0, nUnattached = 0;
677             opr_Verify(VAttachVolumesByPartition(diskP,
678                                                  &nAttached, &nUnattached)
679                             == 0);
680         }
681     }
682     VOL_LOCK;
683     VSetVInit_r(2);                     /* Initialized, and all volumes have been attached */
684     LWP_NoYieldSignal(VInitAttachVolumes);
685     VOL_UNLOCK;
686     return 0;
687 }
688 #endif /* !AFS_PTHREAD_ENV */
689
690 #if defined(AFS_PTHREAD_ENV) && !defined(AFS_DEMAND_ATTACH_FS)
691 /**
692  * Attach volumes in vice partitions
693  *
694  * @param[in]  pt         calling program type
695  *
696  * @return 0
697  * @note Threaded version of attach parititions.
698  *
699  * @post VInit state is 2
700  */
701 int
702 VInitAttachVolumes(ProgramType pt)
703 {
704     opr_Assert(VInit==1);
705     if (pt == fileServer) {
706         struct DiskPartition64 *diskP;
707         struct vinitvolumepackage_thread_t params;
708         struct diskpartition_queue_t * dpq;
709         int i, threads, parts;
710         pthread_t tid;
711         pthread_attr_t attrs;
712
713         opr_cv_init(&params.thread_done_cv);
714         queue_Init(&params);
715         params.n_threads_complete = 0;
716
717         /* create partition work queue */
718         for (parts=0, diskP = DiskPartitionList; diskP; diskP = diskP->next, parts++) {
719             dpq = malloc(sizeof(struct diskpartition_queue_t));
720             opr_Assert(dpq != NULL);
721             dpq->diskP = diskP;
722             queue_Append(&params,dpq);
723         }
724
725         threads = min(parts, vol_attach_threads);
726
727         if (threads > 1) {
728             /* spawn off a bunch of initialization threads */
729             opr_Verify(pthread_attr_init(&attrs) == 0);
730             opr_Verify(pthread_attr_setdetachstate(&attrs,
731                                                    PTHREAD_CREATE_DETACHED)
732                             == 0);
733
734             Log("VInitVolumePackage: beginning parallel fileserver startup\n");
735             Log("VInitVolumePackage: using %d threads to attach volumes on %d partitions\n",
736                 threads, parts);
737
738             VOL_LOCK;
739             for (i=0; i < threads; i++) {
740                 AFS_SIGSET_DECL;
741                 AFS_SIGSET_CLEAR();
742                 opr_Verify(pthread_create(&tid, &attrs,
743                                           &VInitVolumePackageThread,
744                                           &params) == 0);
745                 AFS_SIGSET_RESTORE();
746             }
747
748             while(params.n_threads_complete < threads) {
749                 VOL_CV_WAIT(&params.thread_done_cv);
750             }
751             VOL_UNLOCK;
752
753             opr_Verify(pthread_attr_destroy(&attrs) == 0);
754         } else {
755             /* if we're only going to run one init thread, don't bother creating
756              * another LWP */
757             Log("VInitVolumePackage: beginning single-threaded fileserver startup\n");
758             Log("VInitVolumePackage: using 1 thread to attach volumes on %d partition(s)\n",
759                 parts);
760
761             VInitVolumePackageThread(&params);
762         }
763
764         opr_cv_destroy(&params.thread_done_cv);
765     }
766     VOL_LOCK;
767     VSetVInit_r(2);                     /* Initialized, and all volumes have been attached */
768     opr_cv_broadcast(&vol_init_attach_cond);
769     VOL_UNLOCK;
770     return 0;
771 }
772
773 static void *
774 VInitVolumePackageThread(void * args) {
775
776     struct DiskPartition64 *diskP;
777     struct vinitvolumepackage_thread_t * params;
778     struct diskpartition_queue_t * dpq;
779
780     params = (vinitvolumepackage_thread_t *) args;
781
782
783     VOL_LOCK;
784     /* Attach all the volumes in this partition */
785     while (queue_IsNotEmpty(params)) {
786         int nAttached = 0, nUnattached = 0;
787
788         if (vinit_attach_abort) {
789             Log("Aborting initialization\n");
790             goto done;
791         }
792
793         dpq = queue_First(params,diskpartition_queue_t);
794         queue_Remove(dpq);
795         VOL_UNLOCK;
796         diskP = dpq->diskP;
797         free(dpq);
798
799         opr_Verify(VAttachVolumesByPartition(diskP, &nAttached,
800                                              &nUnattached) == 0);
801
802         VOL_LOCK;
803     }
804
805 done:
806     params->n_threads_complete++;
807     opr_cv_signal(&params->thread_done_cv);
808     VOL_UNLOCK;
809     return NULL;
810 }
811 #endif /* AFS_PTHREAD_ENV && !AFS_DEMAND_ATTACH_FS */
812
813 #if defined(AFS_DEMAND_ATTACH_FS)
814 /**
815  * Attach volumes in vice partitions
816  *
817  * @param[in]  pt         calling program type
818  *
819  * @return 0
820  * @note Threaded version of attach partitions.
821  *
822  * @post VInit state is 2
823  */
824 int
825 VInitAttachVolumes(ProgramType pt)
826 {
827     opr_Assert(VInit==1);
828     if (pt == fileServer) {
829
830         struct DiskPartition64 *diskP;
831         struct partition_queue pq;
832         struct volume_init_queue vq;
833
834         int i, threads, parts;
835         pthread_t tid;
836         pthread_attr_t attrs;
837
838         /* create partition work queue */
839         queue_Init(&pq);
840         opr_cv_init(&pq.cv);
841         opr_mutex_init(&pq.mutex);
842         for (parts = 0, diskP = DiskPartitionList; diskP; diskP = diskP->next, parts++) {
843             struct diskpartition_queue_t *dp;
844             dp = malloc(sizeof(struct diskpartition_queue_t));
845             opr_Assert(dp != NULL);
846             dp->diskP = diskP;
847             queue_Append(&pq, dp);
848         }
849
850         /* number of worker threads; at least one, not to exceed the number of partitions */
851         threads = min(parts, vol_attach_threads);
852
853         /* create volume work queue */
854         queue_Init(&vq);
855         opr_cv_init(&vq.cv);
856         opr_mutex_init(&vq.mutex);
857
858         opr_Verify(pthread_attr_init(&attrs) == 0);
859         opr_Verify(pthread_attr_setdetachstate(&attrs,
860                                                PTHREAD_CREATE_DETACHED) == 0);
861
862         Log("VInitVolumePackage: beginning parallel fileserver startup\n");
863         Log("VInitVolumePackage: using %d threads to pre-attach volumes on %d partitions\n",
864                 threads, parts);
865
866         /* create threads to scan disk partitions. */
867         for (i=0; i < threads; i++) {
868             struct vinitvolumepackage_thread_param *params;
869             AFS_SIGSET_DECL;
870
871             params = malloc(sizeof(struct vinitvolumepackage_thread_param));
872             opr_Assert(params);
873             params->pq = &pq;
874             params->vq = &vq;
875             params->nthreads = threads;
876             params->thread = i+1;
877
878             AFS_SIGSET_CLEAR();
879             opr_Verify(pthread_create(&tid, &attrs,
880                                       &VInitVolumePackageThread,
881                                       (void*)params) == 0);
882             AFS_SIGSET_RESTORE();
883         }
884
885         VInitPreAttachVolumes(threads, &vq);
886
887         opr_Verify(pthread_attr_destroy(&attrs) == 0);
888         opr_cv_destroy(&pq.cv);
889         opr_mutex_destroy(&pq.mutex);
890         opr_cv_destroy(&vq.cv);
891         opr_mutex_destroy(&vq.mutex);
892     }
893
894     VOL_LOCK;
895     VSetVInit_r(2);                     /* Initialized, and all volumes have been attached */
896     opr_cv_broadcast(&vol_init_attach_cond);
897     VOL_UNLOCK;
898
899     return 0;
900 }
901
902 /**
903  * Volume package initialization worker thread. Scan partitions for volume
904  * header files. Gather batches of volume ids and dispatch them to
905  * the main thread to be preattached.  The volume preattachement is done
906  * in the main thread to avoid global volume lock contention.
907  */
908 static void *
909 VInitVolumePackageThread(void *args)
910 {
911     struct vinitvolumepackage_thread_param *params;
912     struct DiskPartition64 *partition;
913     struct partition_queue *pq;
914     struct volume_init_queue *vq;
915     struct volume_init_batch *vb;
916
917     opr_Assert(args);
918     params = (struct vinitvolumepackage_thread_param *)args;
919     pq = params->pq;
920     vq = params->vq;
921     opr_Assert(pq);
922     opr_Assert(vq);
923
924     vb = malloc(sizeof(struct volume_init_batch));
925     opr_Assert(vb);
926     vb->thread = params->thread;
927     vb->last = 0;
928     vb->size = 0;
929
930     Log("Scanning partitions on thread %d of %d\n", params->thread, params->nthreads);
931     while((partition = VInitNextPartition(pq))) {
932         DIR *dirp;
933         VolumeId vid;
934
935         Log("Partition %s: pre-attaching volumes\n", partition->name);
936         dirp = opendir(VPartitionPath(partition));
937         if (!dirp) {
938             Log("opendir on Partition %s failed, errno=%d!\n", partition->name, errno);
939             continue;
940         }
941         while ((vid = VInitNextVolumeId(dirp))) {
942             Volume *vp = calloc(1, sizeof(Volume));
943             opr_Assert(vp);
944             vp->device = partition->device;
945             vp->partition = partition;
946             vp->hashid = vid;
947             queue_Init(&vp->vnode_list);
948             queue_Init(&vp->rx_call_list);
949             opr_cv_init(&V_attachCV(vp));
950
951             vb->batch[vb->size++] = vp;
952             if (vb->size == VINIT_BATCH_MAX_SIZE) {
953                 opr_mutex_enter(&vq->mutex);
954                 queue_Append(vq, vb);
955                 opr_cv_broadcast(&vq->cv);
956                 opr_mutex_exit(&vq->mutex);
957
958                 vb = malloc(sizeof(struct volume_init_batch));
959                 opr_Assert(vb);
960                 vb->thread = params->thread;
961                 vb->size = 0;
962                 vb->last = 0;
963             }
964         }
965         closedir(dirp);
966     }
967
968     vb->last = 1;
969     opr_mutex_enter(&vq->mutex);
970     queue_Append(vq, vb);
971     opr_cv_broadcast(&vq->cv);
972     opr_mutex_exit(&vq->mutex);
973
974     Log("Partition scan thread %d of %d ended\n", params->thread, params->nthreads);
975     free(params);
976     return NULL;
977 }
978
979 /**
980  * Read next element from the pre-populated partition list.
981  */
982 static struct DiskPartition64*
983 VInitNextPartition(struct partition_queue *pq)
984 {
985     struct DiskPartition64 *partition;
986     struct diskpartition_queue_t *dp; /* queue element */
987
988     if (vinit_attach_abort) {
989         Log("Aborting volume preattach thread.\n");
990         return NULL;
991     }
992
993     /* get next partition to scan */
994     opr_mutex_enter(&pq->mutex);
995     if (queue_IsEmpty(pq)) {
996         opr_mutex_exit(&pq->mutex);
997         return NULL;
998     }
999     dp = queue_First(pq, diskpartition_queue_t);
1000     queue_Remove(dp);
1001     opr_mutex_exit(&pq->mutex);
1002
1003     opr_Assert(dp);
1004     opr_Assert(dp->diskP);
1005
1006     partition = dp->diskP;
1007     free(dp);
1008     return partition;
1009 }
1010
1011 /**
1012  * Find next volume id on the partition.
1013  */
1014 static VolumeId
1015 VInitNextVolumeId(DIR *dirp)
1016 {
1017     struct dirent *d;
1018     VolumeId vid = 0;
1019     char *ext;
1020
1021     while((d = readdir(dirp))) {
1022         if (vinit_attach_abort) {
1023             Log("Aborting volume preattach thread.\n");
1024             break;
1025         }
1026         ext = strrchr(d->d_name, '.');
1027         if (d->d_name[0] == 'V' && ext && strcmp(ext, VHDREXT) == 0) {
1028             vid = VolumeNumber(d->d_name);
1029             if (vid) {
1030                break;
1031             }
1032             Log("Warning: bogus volume header file: %s\n", d->d_name);
1033         }
1034     }
1035     return vid;
1036 }
1037
1038 /**
1039  * Preattach volumes in batches to avoid lock contention.
1040  */
1041 static int
1042 VInitPreAttachVolumes(int nthreads, struct volume_init_queue *vq)
1043 {
1044     struct volume_init_batch *vb;
1045     int i;
1046
1047     while (nthreads) {
1048         /* dequeue next volume */
1049         opr_mutex_enter(&vq->mutex);
1050         if (queue_IsEmpty(vq)) {
1051             opr_cv_wait(&vq->cv, &vq->mutex);
1052         }
1053         vb = queue_First(vq, volume_init_batch);
1054         queue_Remove(vb);
1055         opr_mutex_exit(&vq->mutex);
1056
1057         if (vb->size) {
1058             VOL_LOCK;
1059             for (i = 0; i<vb->size; i++) {
1060                 Volume *vp;
1061                 Volume *dup;
1062                 Error ec = 0;
1063
1064                 vp = vb->batch[i];
1065                 dup = VLookupVolume_r(&ec, vp->hashid, NULL);
1066                 if (ec) {
1067                     Log("Error looking up volume, code=%d\n", ec);
1068                 }
1069                 else if (dup) {
1070                     Log("Warning: Duplicate volume id %" AFS_VOLID_FMT " detected.\n", afs_printable_VolumeId_lu(vp->hashid));
1071                 }
1072                 else {
1073                     /* put pre-attached volume onto the hash table
1074                      * and bring it up to the pre-attached state */
1075                     AddVolumeToHashTable(vp, vp->hashid);
1076                     AddVolumeToVByPList_r(vp);
1077                     VLRU_Init_Node_r(vp);
1078                     VChangeState_r(vp, VOL_STATE_PREATTACHED);
1079                 }
1080             }
1081             VOL_UNLOCK;
1082         }
1083
1084         if (vb->last) {
1085             nthreads--;
1086         }
1087         free(vb);
1088     }
1089     return 0;
1090 }
1091 #endif /* AFS_DEMAND_ATTACH_FS */
1092
1093 #if !defined(AFS_DEMAND_ATTACH_FS)
1094 /*
1095  * attach all volumes on a given disk partition
1096  */
1097 static int
1098 VAttachVolumesByPartition(struct DiskPartition64 *diskP, int * nAttached, int * nUnattached)
1099 {
1100   DIR * dirp;
1101   struct dirent * dp;
1102   int ret = 0;
1103
1104   Log("Partition %s: attaching volumes\n", diskP->name);
1105   dirp = opendir(VPartitionPath(diskP));
1106   if (!dirp) {
1107     Log("opendir on Partition %s failed!\n", diskP->name);
1108     return 1;
1109   }
1110
1111   while ((dp = readdir(dirp))) {
1112     char *p;
1113     p = strrchr(dp->d_name, '.');
1114
1115     if (vinit_attach_abort) {
1116       Log("Partition %s: abort attach volumes\n", diskP->name);
1117       goto done;
1118     }
1119
1120     if (p != NULL && strcmp(p, VHDREXT) == 0) {
1121       Error error;
1122       Volume *vp;
1123       vp = VAttachVolumeByName(&error, diskP->name, dp->d_name,
1124                                V_VOLUPD);
1125       (*(vp ? nAttached : nUnattached))++;
1126       if (error == VOFFLINE)
1127         Log("Volume %d stays offline (/vice/offline/%s exists)\n", VolumeNumber(dp->d_name), dp->d_name);
1128       else if (LogLevel >= 5) {
1129         Log("Partition %s: attached volume %d (%s)\n",
1130             diskP->name, VolumeNumber(dp->d_name),
1131             dp->d_name);
1132       }
1133       if (vp) {
1134         VPutVolume(vp);
1135       }
1136     }
1137   }
1138
1139   Log("Partition %s: attached %d volumes; %d volumes not attached\n", diskP->name, *nAttached, *nUnattached);
1140 done:
1141   closedir(dirp);
1142   return ret;
1143 }
1144 #endif /* !AFS_DEMAND_ATTACH_FS */
1145
1146 /***************************************************/
1147 /* Shutdown routines                               */
1148 /***************************************************/
1149
1150 /*
1151  * demand attach fs
1152  * highly multithreaded volume package shutdown
1153  *
1154  * with the demand attach fileserver extensions,
1155  * VShutdown has been modified to be multithreaded.
1156  * In order to achieve optimal use of many threads,
1157  * the shutdown code involves one control thread and
1158  * n shutdown worker threads.  The control thread
1159  * periodically examines the number of volumes available
1160  * for shutdown on each partition, and produces a worker
1161  * thread allocation schedule.  The idea is to eliminate
1162  * redundant scheduling computation on the workers by
1163  * having a single master scheduler.
1164  *
1165  * The scheduler's objectives are:
1166  * (1) fairness
1167  *   each partition with volumes remaining gets allocated
1168  *   at least 1 thread (assuming sufficient threads)
1169  * (2) performance
1170  *   threads are allocated proportional to the number of
1171  *   volumes remaining to be offlined.  This ensures that
1172  *   the OS I/O scheduler has many requests to elevator
1173  *   seek on partitions that will (presumably) take the
1174  *   longest amount of time (from now) to finish shutdown
1175  * (3) keep threads busy
1176  *   when there are extra threads, they are assigned to
1177  *   partitions using a simple round-robin algorithm
1178  *
1179  * In the future, we may wish to add the ability to adapt
1180  * to the relative performance patterns of each disk
1181  * partition.
1182  *
1183  *
1184  * demand attach fs
1185  * multi-step shutdown process
1186  *
1187  * demand attach shutdown is a four-step process. Each
1188  * shutdown "pass" shuts down increasingly more difficult
1189  * volumes.  The main purpose is to achieve better cache
1190  * utilization during shutdown.
1191  *
1192  * pass 0
1193  *   shutdown volumes in the unattached, pre-attached
1194  *   and error states
1195  * pass 1
1196  *   shutdown attached volumes with cached volume headers
1197  * pass 2
1198  *   shutdown all volumes in non-exclusive states
1199  * pass 3
1200  *   shutdown all remaining volumes
1201  */
1202
1203 #ifdef AFS_DEMAND_ATTACH_FS
1204
1205 void
1206 VShutdown_r(void)
1207 {
1208     int i;
1209     struct DiskPartition64 * diskP;
1210     struct diskpartition_queue_t * dpq;
1211     vshutdown_thread_t params;
1212     pthread_t tid;
1213     pthread_attr_t attrs;
1214
1215     memset(&params, 0, sizeof(vshutdown_thread_t));
1216
1217     if (VInit < 2) {
1218         Log("VShutdown:  aborting attach volumes\n");
1219         vinit_attach_abort = 1;
1220         VOL_CV_WAIT(&vol_init_attach_cond);
1221     }
1222
1223     for (params.n_parts=0, diskP = DiskPartitionList;
1224          diskP; diskP = diskP->next, params.n_parts++);
1225
1226     Log("VShutdown:  shutting down on-line volumes on %d partition%s...\n",
1227         params.n_parts, params.n_parts > 1 ? "s" : "");
1228
1229     vol_shutting_down = 1;
1230
1231     if (vol_attach_threads > 1) {
1232         /* prepare for parallel shutdown */
1233         params.n_threads = vol_attach_threads;
1234         opr_mutex_init(&params.lock);
1235         opr_cv_init(&params.cv);
1236         opr_cv_init(&params.master_cv);
1237         opr_Verify(pthread_attr_init(&attrs) == 0);
1238         opr_Verify(pthread_attr_setdetachstate(&attrs,
1239                                                PTHREAD_CREATE_DETACHED) == 0);
1240         queue_Init(&params);
1241
1242         /* setup the basic partition information structures for
1243          * parallel shutdown */
1244         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1245             /* XXX debug */
1246             struct rx_queue * qp, * nqp;
1247             Volume * vp;
1248             int count = 0;
1249
1250             VVByPListWait_r(diskP);
1251             VVByPListBeginExclusive_r(diskP);
1252
1253             /* XXX debug */
1254             for (queue_Scan(&diskP->vol_list, qp, nqp, rx_queue)) {
1255                 vp = (Volume *)((char *)qp - offsetof(Volume, vol_list));
1256                 if (vp->header)
1257                     count++;
1258             }
1259             Log("VShutdown: partition %s has %d volumes with attached headers\n",
1260                 VPartitionPath(diskP), count);
1261
1262
1263             /* build up the pass 0 shutdown work queue */
1264             dpq = malloc(sizeof(struct diskpartition_queue_t));
1265             opr_Assert(dpq != NULL);
1266             dpq->diskP = diskP;
1267             queue_Prepend(&params, dpq);
1268
1269             params.part_pass_head[diskP->index] = queue_First(&diskP->vol_list, rx_queue);
1270         }
1271
1272         Log("VShutdown:  beginning parallel fileserver shutdown\n");
1273         Log("VShutdown:  using %d threads to offline volumes on %d partition%s\n",
1274             vol_attach_threads, params.n_parts, params.n_parts > 1 ? "s" : "" );
1275
1276         /* do pass 0 shutdown */
1277         opr_mutex_enter(&params.lock);
1278         for (i=0; i < params.n_threads; i++) {
1279             opr_Verify(pthread_create(&tid, &attrs, &VShutdownThread,
1280                                       &params) == 0);
1281         }
1282
1283         /* wait for all the pass 0 shutdowns to complete */
1284         while (params.n_threads_complete < params.n_threads) {
1285             CV_WAIT(&params.master_cv, &params.lock);
1286         }
1287         params.n_threads_complete = 0;
1288         params.pass = 1;
1289         opr_cv_broadcast(&params.cv);
1290         opr_mutex_exit(&params.lock);
1291
1292         Log("VShutdown:  pass 0 completed using the 1 thread per partition algorithm\n");
1293         Log("VShutdown:  starting passes 1 through 3 using finely-granular mp-fast algorithm\n");
1294
1295         /* run the parallel shutdown scheduler. it will drop the glock internally */
1296         ShutdownController(&params);
1297
1298         /* wait for all the workers to finish pass 3 and terminate */
1299         while (params.pass < 4) {
1300             VOL_CV_WAIT(&params.cv);
1301         }
1302
1303         opr_Verify(pthread_attr_destroy(&attrs) == 0);
1304         opr_cv_destroy(&params.cv);
1305         opr_cv_destroy(&params.master_cv);
1306         opr_mutex_destroy(&params.lock);
1307
1308         /* drop the VByPList exclusive reservations */
1309         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1310             VVByPListEndExclusive_r(diskP);
1311             Log("VShutdown:  %s stats : (pass[0]=%d, pass[1]=%d, pass[2]=%d, pass[3]=%d)\n",
1312                 VPartitionPath(diskP),
1313                 params.stats[0][diskP->index],
1314                 params.stats[1][diskP->index],
1315                 params.stats[2][diskP->index],
1316                 params.stats[3][diskP->index]);
1317         }
1318
1319         Log("VShutdown:  shutdown finished using %d threads\n", params.n_threads);
1320     } else {
1321         /* if we're only going to run one shutdown thread, don't bother creating
1322          * another LWP */
1323         Log("VShutdown:  beginning single-threaded fileserver shutdown\n");
1324
1325         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1326             VShutdownByPartition_r(diskP);
1327         }
1328     }
1329
1330     Log("VShutdown:  complete.\n");
1331 }
1332
1333 #else /* AFS_DEMAND_ATTACH_FS */
1334
1335 void
1336 VShutdown_r(void)
1337 {
1338     int i;
1339     Volume *vp, *np;
1340     afs_int32 code;
1341
1342     if (VInit < 2) {
1343         Log("VShutdown:  aborting attach volumes\n");
1344         vinit_attach_abort = 1;
1345 #ifdef AFS_PTHREAD_ENV
1346         VOL_CV_WAIT(&vol_init_attach_cond);
1347 #else
1348         LWP_WaitProcess(VInitAttachVolumes);
1349 #endif /* AFS_PTHREAD_ENV */
1350     }
1351
1352     Log("VShutdown:  shutting down on-line volumes...\n");
1353     vol_shutting_down = 1;
1354     for (i = 0; i < VolumeHashTable.Size; i++) {
1355         /* try to hold first volume in the hash table */
1356         for (queue_Scan(&VolumeHashTable.Table[i],vp,np,Volume)) {
1357             code = VHold_r(vp);
1358             if (code == 0) {
1359                 if (LogLevel >= 5)
1360                     Log("VShutdown:  Attempting to take volume %" AFS_VOLID_FMT " offline.\n",
1361                         afs_printable_VolumeId_lu(vp->hashid));
1362
1363                 /* next, take the volume offline (drops reference count) */
1364                 VOffline_r(vp, "File server was shut down");
1365             }
1366         }
1367     }
1368     Log("VShutdown:  complete.\n");
1369 }
1370 #endif /* AFS_DEMAND_ATTACH_FS */
1371
1372
1373 void
1374 VShutdown(void)
1375 {
1376     opr_Assert(VInit>0);
1377     VOL_LOCK;
1378     VShutdown_r();
1379     VOL_UNLOCK;
1380 }
1381
1382 /**
1383  * stop new activity (e.g. SALVSYNC) from occurring
1384  *
1385  * Use this to make the volume package less busy; for example, during
1386  * shutdown. This doesn't actually shutdown/detach anything in the
1387  * volume package, but prevents certain processes from ocurring. For
1388  * example, preventing new SALVSYNC communication in DAFS. In theory, we
1389  * could also use this to prevent new volume attachment, or prevent
1390  * other programs from checking out volumes, etc.
1391  */
1392 void
1393 VSetTranquil(void)
1394 {
1395 #ifdef AFS_DEMAND_ATTACH_FS
1396     /* make sure we don't try to contact the salvageserver, since it may
1397      * not be around anymore */
1398     vol_disallow_salvsync = 1;
1399 #endif
1400 }
1401
1402 #ifdef AFS_DEMAND_ATTACH_FS
1403 /*
1404  * demand attach fs
1405  * shutdown control thread
1406  */
1407 static void
1408 ShutdownController(vshutdown_thread_t * params)
1409 {
1410     /* XXX debug */
1411     struct DiskPartition64 * diskP;
1412     Device id;
1413     vshutdown_thread_t shadow;
1414
1415     ShutdownCreateSchedule(params);
1416
1417     while ((params->pass < 4) &&
1418            (params->n_threads_complete < params->n_threads)) {
1419         /* recompute schedule once per second */
1420
1421         memcpy(&shadow, params, sizeof(vshutdown_thread_t));
1422
1423         VOL_UNLOCK;
1424         /* XXX debug */
1425         Log("ShutdownController:  schedule version=%d, vol_remaining=%d, pass=%d\n",
1426             shadow.schedule_version, shadow.vol_remaining, shadow.pass);
1427         Log("ShutdownController:  n_threads_complete=%d, n_parts_done_pass=%d\n",
1428             shadow.n_threads_complete, shadow.n_parts_done_pass);
1429         for (diskP = DiskPartitionList; diskP; diskP=diskP->next) {
1430             id = diskP->index;
1431             Log("ShutdownController:  part[%d] : (len=%d, thread_target=%d, done_pass=%d, pass_head=%p)\n",
1432                 id,
1433                 diskP->vol_list.len,
1434                 shadow.part_thread_target[id],
1435                 shadow.part_done_pass[id],
1436                 shadow.part_pass_head[id]);
1437         }
1438
1439         sleep(1);
1440         VOL_LOCK;
1441
1442         ShutdownCreateSchedule(params);
1443     }
1444 }
1445
1446 /* create the shutdown thread work schedule.
1447  * this scheduler tries to implement fairness
1448  * by allocating at least 1 thread to each
1449  * partition with volumes to be shutdown,
1450  * and then it attempts to allocate remaining
1451  * threads based upon the amount of work left
1452  */
1453 static void
1454 ShutdownCreateSchedule(vshutdown_thread_t * params)
1455 {
1456     struct DiskPartition64 * diskP;
1457     int sum, thr_workload, thr_left;
1458     int part_residue[VOLMAXPARTS+1];
1459     Device id;
1460
1461     /* compute the total number of outstanding volumes */
1462     sum = 0;
1463     for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1464         sum += diskP->vol_list.len;
1465     }
1466
1467     params->schedule_version++;
1468     params->vol_remaining = sum;
1469
1470     if (!sum)
1471         return;
1472
1473     /* compute average per-thread workload */
1474     thr_workload = sum / params->n_threads;
1475     if (sum % params->n_threads)
1476         thr_workload++;
1477
1478     thr_left = params->n_threads;
1479     memset(&part_residue, 0, sizeof(part_residue));
1480
1481     /* for fairness, give every partition with volumes remaining
1482      * at least one thread */
1483     for (diskP = DiskPartitionList; diskP && thr_left; diskP = diskP->next) {
1484         id = diskP->index;
1485         if (diskP->vol_list.len) {
1486             params->part_thread_target[id] = 1;
1487             thr_left--;
1488         } else {
1489             params->part_thread_target[id] = 0;
1490         }
1491     }
1492
1493     if (thr_left && thr_workload) {
1494         /* compute length-weighted workloads */
1495         int delta;
1496
1497         for (diskP = DiskPartitionList; diskP && thr_left; diskP = diskP->next) {
1498             id = diskP->index;
1499             delta = (diskP->vol_list.len / thr_workload) -
1500                 params->part_thread_target[id];
1501             if (delta < 0) {
1502                 continue;
1503             }
1504             if (delta < thr_left) {
1505                 params->part_thread_target[id] += delta;
1506                 thr_left -= delta;
1507             } else {
1508                 params->part_thread_target[id] += thr_left;
1509                 thr_left = 0;
1510                 break;
1511             }
1512         }
1513     }
1514
1515     if (thr_left) {
1516         /* try to assign any leftover threads to partitions that
1517          * had volume lengths closer to needing thread_target+1 */
1518         int max_residue, max_id = 0;
1519
1520         /* compute the residues */
1521         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1522             id = diskP->index;
1523             part_residue[id] = diskP->vol_list.len -
1524                 (params->part_thread_target[id] * thr_workload);
1525         }
1526
1527         /* now try to allocate remaining threads to partitions with the
1528          * highest residues */
1529         while (thr_left) {
1530             max_residue = 0;
1531             for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1532                 id = diskP->index;
1533                 if (part_residue[id] > max_residue) {
1534                     max_residue = part_residue[id];
1535                     max_id = id;
1536                 }
1537             }
1538
1539             if (!max_residue) {
1540                 break;
1541             }
1542
1543             params->part_thread_target[max_id]++;
1544             thr_left--;
1545             part_residue[max_id] = 0;
1546         }
1547     }
1548
1549     if (thr_left) {
1550         /* punt and give any remaining threads equally to each partition */
1551         int alloc;
1552         if (thr_left >= params->n_parts) {
1553             alloc = thr_left / params->n_parts;
1554             for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1555                 id = diskP->index;
1556                 params->part_thread_target[id] += alloc;
1557                 thr_left -= alloc;
1558             }
1559         }
1560
1561         /* finish off the last of the threads */
1562         for (diskP = DiskPartitionList; thr_left && diskP; diskP = diskP->next) {
1563             id = diskP->index;
1564             params->part_thread_target[id]++;
1565             thr_left--;
1566         }
1567     }
1568 }
1569
1570 /* worker thread for parallel shutdown */
1571 static void *
1572 VShutdownThread(void * args)
1573 {
1574     vshutdown_thread_t * params;
1575     int found, pass, schedule_version_save, count;
1576     struct DiskPartition64 *diskP;
1577     struct diskpartition_queue_t * dpq;
1578     Device id;
1579
1580     params = (vshutdown_thread_t *) args;
1581
1582     /* acquire the shutdown pass 0 lock */
1583     opr_mutex_enter(&params->lock);
1584
1585     /* if there's still pass 0 work to be done,
1586      * get a work entry, and do a pass 0 shutdown */
1587     if (queue_IsNotEmpty(params)) {
1588         dpq = queue_First(params, diskpartition_queue_t);
1589         queue_Remove(dpq);
1590         opr_mutex_exit(&params->lock);
1591         diskP = dpq->diskP;
1592         free(dpq);
1593         id = diskP->index;
1594
1595         count = 0;
1596         while (ShutdownVolumeWalk_r(diskP, 0, &params->part_pass_head[id]))
1597             count++;
1598         params->stats[0][diskP->index] = count;
1599         opr_mutex_enter(&params->lock);
1600     }
1601
1602     params->n_threads_complete++;
1603     if (params->n_threads_complete == params->n_threads) {
1604         /* notify control thread that all workers have completed pass 0 */
1605         opr_cv_signal(&params->master_cv);
1606     }
1607     while (params->pass == 0) {
1608         opr_cv_wait(&params->cv, &params->lock);
1609     }
1610
1611     /* switch locks */
1612     opr_mutex_exit(&params->lock);
1613     VOL_LOCK;
1614
1615     pass = params->pass;
1616     opr_Assert(pass > 0);
1617
1618     /* now escalate through the more complicated shutdowns */
1619     while (pass <= 3) {
1620         schedule_version_save = params->schedule_version;
1621         found = 0;
1622         /* find a disk partition to work on */
1623         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1624             id = diskP->index;
1625             if (params->part_thread_target[id] && !params->part_done_pass[id]) {
1626                 params->part_thread_target[id]--;
1627                 found = 1;
1628                 break;
1629             }
1630         }
1631
1632         if (!found) {
1633             /* hmm. for some reason the controller thread couldn't find anything for
1634              * us to do. let's see if there's anything we can do */
1635             for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1636                 id = diskP->index;
1637                 if (diskP->vol_list.len && !params->part_done_pass[id]) {
1638                     found = 1;
1639                     break;
1640                 } else if (!params->part_done_pass[id]) {
1641                     params->part_done_pass[id] = 1;
1642                     params->n_parts_done_pass++;
1643                     if (pass == 3) {
1644                         Log("VShutdown:  done shutting down volumes on partition %s.\n",
1645                             VPartitionPath(diskP));
1646                     }
1647                 }
1648             }
1649         }
1650
1651         /* do work on this partition until either the controller
1652          * creates a new schedule, or we run out of things to do
1653          * on this partition */
1654         if (found) {
1655             count = 0;
1656             while (!params->part_done_pass[id] &&
1657                    (schedule_version_save == params->schedule_version)) {
1658                 /* ShutdownVolumeWalk_r will drop the glock internally */
1659                 if (!ShutdownVolumeWalk_r(diskP, pass, &params->part_pass_head[id])) {
1660                     if (!params->part_done_pass[id]) {
1661                         params->part_done_pass[id] = 1;
1662                         params->n_parts_done_pass++;
1663                         if (pass == 3) {
1664                             Log("VShutdown:  done shutting down volumes on partition %s.\n",
1665                                 VPartitionPath(diskP));
1666                         }
1667                     }
1668                     break;
1669                 }
1670                 count++;
1671             }
1672
1673             params->stats[pass][id] += count;
1674         } else {
1675             /* ok, everyone is done this pass, proceed */
1676
1677             /* barrier lock */
1678             params->n_threads_complete++;
1679             while (params->pass == pass) {
1680                 if (params->n_threads_complete == params->n_threads) {
1681                     /* we are the last thread to complete, so we will
1682                      * reinitialize worker pool state for the next pass */
1683                     params->n_threads_complete = 0;
1684                     params->n_parts_done_pass = 0;
1685                     params->pass++;
1686                     for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1687                         id = diskP->index;
1688                         params->part_done_pass[id] = 0;
1689                         params->part_pass_head[id] = queue_First(&diskP->vol_list, rx_queue);
1690                     }
1691
1692                     /* compute a new thread schedule before releasing all the workers */
1693                     ShutdownCreateSchedule(params);
1694
1695                     /* wake up all the workers */
1696                     opr_cv_broadcast(&params->cv);
1697
1698                     VOL_UNLOCK;
1699                     Log("VShutdown:  pass %d completed using %d threads on %d partitions\n",
1700                         pass, params->n_threads, params->n_parts);
1701                     VOL_LOCK;
1702                 } else {
1703                     VOL_CV_WAIT(&params->cv);
1704                 }
1705             }
1706             pass = params->pass;
1707         }
1708
1709         /* for fairness */
1710         VOL_UNLOCK;
1711         pthread_yield();
1712         VOL_LOCK;
1713     }
1714
1715     VOL_UNLOCK;
1716
1717     return NULL;
1718 }
1719
1720 /* shut down all volumes on a given disk partition
1721  *
1722  * note that this function will not allow mp-fast
1723  * shutdown of a partition */
1724 int
1725 VShutdownByPartition_r(struct DiskPartition64 * dp)
1726 {
1727     int pass;
1728     int pass_stats[4];
1729     int total;
1730
1731     /* wait for other exclusive ops to finish */
1732     VVByPListWait_r(dp);
1733
1734     /* begin exclusive access */
1735     VVByPListBeginExclusive_r(dp);
1736
1737     /* pick the low-hanging fruit first,
1738      * then do the complicated ones last
1739      * (has the advantage of keeping
1740      *  in-use volumes up until the bitter end) */
1741     for (pass = 0, total=0; pass < 4; pass++) {
1742         pass_stats[pass] = ShutdownVByPForPass_r(dp, pass);
1743         total += pass_stats[pass];
1744     }
1745
1746     /* end exclusive access */
1747     VVByPListEndExclusive_r(dp);
1748
1749     Log("VShutdownByPartition:  shut down %d volumes on %s (pass[0]=%d, pass[1]=%d, pass[2]=%d, pass[3]=%d)\n",
1750         total, VPartitionPath(dp), pass_stats[0], pass_stats[1], pass_stats[2], pass_stats[3]);
1751
1752     return 0;
1753 }
1754
1755 /* internal shutdown functionality
1756  *
1757  * for multi-pass shutdown:
1758  * 0 to only "shutdown" {pre,un}attached and error state volumes
1759  * 1 to also shutdown attached volumes w/ volume header loaded
1760  * 2 to also shutdown attached volumes w/o volume header loaded
1761  * 3 to also shutdown exclusive state volumes
1762  *
1763  * caller MUST hold exclusive access on the hash chain
1764  * because we drop vol_glock_mutex internally
1765  *
1766  * this function is reentrant for passes 1--3
1767  * (e.g. multiple threads can cooperate to
1768  *  shutdown a partition mp-fast)
1769  *
1770  * pass 0 is not scaleable because the volume state data is
1771  * synchronized by vol_glock mutex, and the locking overhead
1772  * is too high to drop the lock long enough to do linked list
1773  * traversal
1774  */
1775 static int
1776 ShutdownVByPForPass_r(struct DiskPartition64 * dp, int pass)
1777 {
1778     struct rx_queue * q = queue_First(&dp->vol_list, rx_queue);
1779     int i = 0;
1780     const char *pass_strs[4] = {"{un/pre}attached vols", "vols w/ vol header loaded", "vols w/o vol header loaded", "vols with exclusive state"};
1781
1782     while (ShutdownVolumeWalk_r(dp, pass, &q)) {
1783         i++;
1784         if (0 == i%100) {
1785             Log("VShutdownByPartition:  ... shut down %d volumes on %s in pass %d (%s)\n", i, VPartitionPath(dp), pass, pass_strs[pass]);
1786         }
1787     }
1788
1789     return i;
1790 }
1791
1792 /* conditionally shutdown one volume on partition dp
1793  * returns 1 if a volume was shutdown in this pass,
1794  * 0 otherwise */
1795 static int
1796 ShutdownVolumeWalk_r(struct DiskPartition64 * dp, int pass,
1797                      struct rx_queue ** idx)
1798 {
1799     struct rx_queue *qp, *nqp;
1800     Volume * vp;
1801
1802     qp = *idx;
1803
1804     for (queue_ScanFrom(&dp->vol_list, qp, qp, nqp, rx_queue)) {
1805         vp = (Volume *) (((char *)qp) - offsetof(Volume, vol_list));
1806
1807         switch (pass) {
1808         case 0:
1809             if ((V_attachState(vp) != VOL_STATE_UNATTACHED) &&
1810                 (V_attachState(vp) != VOL_STATE_ERROR) &&
1811                 (V_attachState(vp) != VOL_STATE_DELETED) &&
1812                 (V_attachState(vp) != VOL_STATE_PREATTACHED)) {
1813                 break;
1814             }
1815         case 1:
1816             if ((V_attachState(vp) == VOL_STATE_ATTACHED) &&
1817                 (vp->header == NULL)) {
1818                 break;
1819             }
1820         case 2:
1821             if (VIsExclusiveState(V_attachState(vp))) {
1822                 break;
1823             }
1824         case 3:
1825             *idx = nqp;
1826             DeleteVolumeFromVByPList_r(vp);
1827             VShutdownVolume_r(vp);
1828             vp = NULL;
1829             return 1;
1830         }
1831     }
1832
1833     return 0;
1834 }
1835
1836 /*
1837  * shutdown a specific volume
1838  */
1839 /* caller MUST NOT hold a heavyweight ref on vp */
1840 int
1841 VShutdownVolume_r(Volume * vp)
1842 {
1843     int code;
1844
1845     VCreateReservation_r(vp);
1846
1847     if (LogLevel >= 5) {
1848         Log("VShutdownVolume_r:  vid=%" AFS_VOLID_FMT ", device=%d, state=%u\n",
1849             afs_printable_VolumeId_lu(vp->hashid), vp->partition->device,
1850             (unsigned int) V_attachState(vp));
1851     }
1852
1853     /* wait for other blocking ops to finish */
1854     VWaitExclusiveState_r(vp);
1855
1856     opr_Assert(VIsValidState(V_attachState(vp)));
1857
1858     switch(V_attachState(vp)) {
1859     case VOL_STATE_SALVAGING:
1860         /* Leave salvaging volumes alone. Any in-progress salvages will
1861          * continue working after viced shuts down. This is intentional.
1862          */
1863
1864     case VOL_STATE_PREATTACHED:
1865     case VOL_STATE_ERROR:
1866         VChangeState_r(vp, VOL_STATE_UNATTACHED);
1867     case VOL_STATE_UNATTACHED:
1868     case VOL_STATE_DELETED:
1869         break;
1870     case VOL_STATE_GOING_OFFLINE:
1871     case VOL_STATE_SHUTTING_DOWN:
1872     case VOL_STATE_ATTACHED:
1873         code = VHold_r(vp);
1874         if (!code) {
1875             if (LogLevel >= 5)
1876                 Log("VShutdown:  Attempting to take volume %" AFS_VOLID_FMT " offline.\n",
1877                     afs_printable_VolumeId_lu(vp->hashid));
1878
1879             /* take the volume offline (drops reference count) */
1880             VOffline_r(vp, "File server was shut down");
1881         }
1882         break;
1883     default:
1884         break;
1885     }
1886
1887     VCancelReservation_r(vp);
1888     vp = NULL;
1889     return 0;
1890 }
1891 #endif /* AFS_DEMAND_ATTACH_FS */
1892
1893
1894 /***************************************************/
1895 /* Header I/O routines                             */
1896 /***************************************************/
1897
1898 static const char *
1899 HeaderName(bit32 magic)
1900 {
1901     switch (magic) {
1902     case VOLUMEINFOMAGIC:
1903         return "volume info";
1904     case SMALLINDEXMAGIC:
1905         return "small index";
1906     case LARGEINDEXMAGIC:
1907         return "large index";
1908     case LINKTABLEMAGIC:
1909         return "link table";
1910     }
1911     return "unknown";
1912 }
1913
1914 /* open a descriptor for the inode (h),
1915  * read in an on-disk structure into buffer (to) of size (size),
1916  * verify versionstamp in structure has magic (magic) and
1917  * optionally verify version (version) if (version) is nonzero
1918  */
1919 static void
1920 ReadHeader(Error * ec, IHandle_t * h, char *to, int size, bit32 magic,
1921            bit32 version)
1922 {
1923     struct versionStamp *vsn;
1924     FdHandle_t *fdP;
1925     afs_sfsize_t nbytes;
1926     afs_ino_str_t stmp;
1927
1928     *ec = 0;
1929     if (h == NULL) {
1930         Log("ReadHeader: Null inode handle argument for %s header file.\n",
1931             HeaderName(magic));
1932         *ec = VSALVAGE;
1933         return;
1934     }
1935
1936     fdP = IH_OPEN(h);
1937     if (fdP == NULL) {
1938         Log("ReadHeader: Failed to open %s header file "
1939             "(volume=%" AFS_VOLID_FMT ", inode=%s); errno=%d\n", HeaderName(magic), afs_printable_VolumeId_lu(h->ih_vid),
1940             PrintInode(stmp, h->ih_ino), errno);
1941         *ec = VSALVAGE;
1942         return;
1943     }
1944
1945     vsn = (struct versionStamp *)to;
1946     nbytes = FDH_PREAD(fdP, to, size, 0);
1947     if (nbytes < 0) {
1948         Log("ReadHeader: Failed to read %s header file "
1949             "(volume=%" AFS_VOLID_FMT ", inode=%s); errno=%d\n", HeaderName(magic), afs_printable_VolumeId_lu(h->ih_vid),
1950             PrintInode(stmp, h->ih_ino), errno);
1951         *ec = VSALVAGE;
1952         FDH_REALLYCLOSE(fdP);
1953         return;
1954     }
1955     if (nbytes != size) {
1956         Log("ReadHeader: Incorrect number of bytes read from %s header file "
1957             "(volume=%" AFS_VOLID_FMT ", inode=%s); expected=%d, read=%d\n",
1958             HeaderName(magic), afs_printable_VolumeId_lu(h->ih_vid), 
1959             PrintInode(stmp, h->ih_ino), size, (int)nbytes);
1960         *ec = VSALVAGE;
1961         FDH_REALLYCLOSE(fdP);
1962         return;
1963     }
1964     if (vsn->magic != magic) {
1965         Log("ReadHeader: Incorrect magic for %s header file "
1966             "(volume=%" AFS_VOLID_FMT ", inode=%s); expected=0x%x, read=0x%x\n",
1967             HeaderName(magic), afs_printable_VolumeId_lu(h->ih_vid),
1968             PrintInode(stmp, h->ih_ino), magic, vsn->magic);
1969         *ec = VSALVAGE;
1970         FDH_REALLYCLOSE(fdP);
1971         return;
1972     }
1973
1974     FDH_CLOSE(fdP);
1975
1976     /* Check is conditional, in case caller wants to inspect version himself */
1977     if (version && vsn->version != version) {
1978         Log("ReadHeader: Incorrect version for %s header file "
1979             "(volume=%" AFS_VOLID_FMT ", inode=%s); expected=%x, read=%x\n",
1980             HeaderName(magic), afs_printable_VolumeId_lu(h->ih_vid), PrintInode(stmp, h->ih_ino),
1981             version, vsn->version);
1982         *ec = VSALVAGE;
1983     }
1984 }
1985
1986 void
1987 WriteVolumeHeader_r(Error * ec, Volume * vp)
1988 {
1989     IHandle_t *h = V_diskDataHandle(vp);
1990     FdHandle_t *fdP;
1991
1992     *ec = 0;
1993
1994     fdP = IH_OPEN(h);
1995     if (fdP == NULL) {
1996         *ec = VSALVAGE;
1997         return;
1998     }
1999     if (FDH_PWRITE(fdP, (char *)&V_disk(vp), sizeof(V_disk(vp)), 0)
2000         != sizeof(V_disk(vp))) {
2001         *ec = VSALVAGE;
2002         FDH_REALLYCLOSE(fdP);
2003         return;
2004     }
2005     FDH_CLOSE(fdP);
2006 }
2007
2008 /* VolumeHeaderToDisk
2009  * Allows for storing 64 bit inode numbers in on-disk volume header
2010  * file.
2011  */
2012 /* convert in-memory representation of a volume header to the
2013  * on-disk representation of a volume header */
2014 void
2015 VolumeHeaderToDisk(VolumeDiskHeader_t * dh, VolumeHeader_t * h)
2016 {
2017
2018     memset(dh, 0, sizeof(VolumeDiskHeader_t));
2019     dh->stamp = h->stamp;
2020     dh->id = h->id;
2021     dh->parent = h->parent;
2022
2023 #ifdef AFS_64BIT_IOPS_ENV
2024     dh->volumeInfo_lo = (afs_int32) h->volumeInfo & 0xffffffff;
2025     dh->volumeInfo_hi = (afs_int32) (h->volumeInfo >> 32) & 0xffffffff;
2026     dh->smallVnodeIndex_lo = (afs_int32) h->smallVnodeIndex & 0xffffffff;
2027     dh->smallVnodeIndex_hi =
2028         (afs_int32) (h->smallVnodeIndex >> 32) & 0xffffffff;
2029     dh->largeVnodeIndex_lo = (afs_int32) h->largeVnodeIndex & 0xffffffff;
2030     dh->largeVnodeIndex_hi =
2031         (afs_int32) (h->largeVnodeIndex >> 32) & 0xffffffff;
2032     dh->linkTable_lo = (afs_int32) h->linkTable & 0xffffffff;
2033     dh->linkTable_hi = (afs_int32) (h->linkTable >> 32) & 0xffffffff;
2034 #else
2035     dh->volumeInfo_lo = h->volumeInfo;
2036     dh->smallVnodeIndex_lo = h->smallVnodeIndex;
2037     dh->largeVnodeIndex_lo = h->largeVnodeIndex;
2038     dh->linkTable_lo = h->linkTable;
2039 #endif
2040 }
2041
2042 /* DiskToVolumeHeader
2043  * Converts an on-disk representation of a volume header to
2044  * the in-memory representation of a volume header.
2045  *
2046  * Makes the assumption that AFS has *always*
2047  * zero'd the volume header file so that high parts of inode
2048  * numbers are 0 in older (SGI EFS) volume header files.
2049  */
2050 void
2051 DiskToVolumeHeader(VolumeHeader_t * h, VolumeDiskHeader_t * dh)
2052 {
2053     memset(h, 0, sizeof(VolumeHeader_t));
2054     h->stamp = dh->stamp;
2055     h->id = dh->id;
2056     h->parent = dh->parent;
2057
2058 #ifdef AFS_64BIT_IOPS_ENV
2059     h->volumeInfo =
2060         (Inode) dh->volumeInfo_lo | ((Inode) dh->volumeInfo_hi << 32);
2061
2062     h->smallVnodeIndex =
2063         (Inode) dh->smallVnodeIndex_lo | ((Inode) dh->
2064                                           smallVnodeIndex_hi << 32);
2065
2066     h->largeVnodeIndex =
2067         (Inode) dh->largeVnodeIndex_lo | ((Inode) dh->
2068                                           largeVnodeIndex_hi << 32);
2069     h->linkTable =
2070         (Inode) dh->linkTable_lo | ((Inode) dh->linkTable_hi << 32);
2071 #else
2072     h->volumeInfo = dh->volumeInfo_lo;
2073     h->smallVnodeIndex = dh->smallVnodeIndex_lo;
2074     h->largeVnodeIndex = dh->largeVnodeIndex_lo;
2075     h->linkTable = dh->linkTable_lo;
2076 #endif
2077 }
2078
2079
2080 /***************************************************/
2081 /* Volume Attachment routines                      */
2082 /***************************************************/
2083
2084 #ifdef AFS_DEMAND_ATTACH_FS
2085 /**
2086  * pre-attach a volume given its path.
2087  *
2088  * @param[out] ec         outbound error code
2089  * @param[in]  partition  partition path string
2090  * @param[in]  name       volume id string
2091  *
2092  * @return volume object pointer
2093  *
2094  * @note A pre-attached volume will only have its partition
2095  *       and hashid fields initialized.  At first call to
2096  *       VGetVolume, the volume will be fully attached.
2097  *
2098  */
2099 Volume *
2100 VPreAttachVolumeByName(Error * ec, char *partition, char *name)
2101 {
2102     Volume * vp;
2103     VOL_LOCK;
2104     vp = VPreAttachVolumeByName_r(ec, partition, name);
2105     VOL_UNLOCK;
2106     return vp;
2107 }
2108
2109 /**
2110  * pre-attach a volume given its path.
2111  *
2112  * @param[out] ec         outbound error code
2113  * @param[in]  partition  path to vice partition
2114  * @param[in]  name       volume id string
2115  *
2116  * @return volume object pointer
2117  *
2118  * @pre VOL_LOCK held
2119  *
2120  * @internal volume package internal use only.
2121  */
2122 Volume *
2123 VPreAttachVolumeByName_r(Error * ec, char *partition, char *name)
2124 {
2125     return VPreAttachVolumeById_r(ec,
2126                                   partition,
2127                                   VolumeNumber(name));
2128 }
2129
2130 /**
2131  * pre-attach a volume given its path and numeric volume id.
2132  *
2133  * @param[out] ec          error code return
2134  * @param[in]  partition   path to vice partition
2135  * @param[in]  volumeId    numeric volume id
2136  *
2137  * @return volume object pointer
2138  *
2139  * @pre VOL_LOCK held
2140  *
2141  * @internal volume package internal use only.
2142  */
2143 Volume *
2144 VPreAttachVolumeById_r(Error * ec,
2145                        char * partition,
2146                        VolumeId volumeId)
2147 {
2148     Volume *vp;
2149     struct DiskPartition64 *partp;
2150
2151     *ec = 0;
2152
2153     opr_Assert(programType == fileServer);
2154
2155     if (!(partp = VGetPartition_r(partition, 0))) {
2156         *ec = VNOVOL;
2157         Log("VPreAttachVolumeById_r:  Error getting partition (%s)\n", partition);
2158         return NULL;
2159     }
2160
2161     /* ensure that any vp we pass to VPreAttachVolumeByVp_r
2162      * is NOT in exclusive state.
2163      */
2164  retry:
2165     vp = VLookupVolume_r(ec, volumeId, NULL);
2166
2167     if (*ec) {
2168         return NULL;
2169     }
2170
2171     if (vp && VIsExclusiveState(V_attachState(vp))) {
2172         VCreateReservation_r(vp);
2173         VWaitExclusiveState_r(vp);
2174         VCancelReservation_r(vp);
2175         vp = NULL;
2176         goto retry;    /* look up volume again */
2177     }
2178
2179     /* vp == NULL or vp not exclusive both OK */
2180
2181     return VPreAttachVolumeByVp_r(ec, partp, vp, volumeId);
2182 }
2183
2184 /**
2185  * preattach a volume.
2186  *
2187  * @param[out] ec     outbound error code
2188  * @param[in]  partp  pointer to partition object
2189  * @param[in]  vp     pointer to volume object
2190  * @param[in]  vid    volume id
2191  *
2192  * @return volume object pointer
2193  *
2194  * @pre VOL_LOCK is held.
2195  *
2196  * @pre vp (if specified) must not be in exclusive state.
2197  *
2198  * @warning Returned volume object pointer does not have to
2199  *          equal the pointer passed in as argument vp.  There
2200  *          are potential race conditions which can result in
2201  *          the pointers having different values.  It is up to
2202  *          the caller to make sure that references are handled
2203  *          properly in this case.
2204  *
2205  * @note If there is already a volume object registered with
2206  *       the same volume id, its pointer MUST be passed as
2207  *       argument vp.  Failure to do so will result in a silent
2208  *       failure to preattach.
2209  *
2210  * @internal volume package internal use only.
2211  */
2212 Volume *
2213 VPreAttachVolumeByVp_r(Error * ec,
2214                        struct DiskPartition64 * partp,
2215                        Volume * vp,
2216                        VolumeId vid)
2217 {
2218     Volume *nvp = NULL;
2219
2220     *ec = 0;
2221
2222     /* don't proceed unless it's safe */
2223     if (vp) {
2224         opr_Assert(!VIsExclusiveState(V_attachState(vp)));
2225     }
2226
2227     /* check to see if pre-attach already happened */
2228     if (vp &&
2229         (V_attachState(vp) != VOL_STATE_UNATTACHED) &&
2230         (V_attachState(vp) != VOL_STATE_DELETED) &&
2231         (V_attachState(vp) != VOL_STATE_PREATTACHED) &&
2232         !VIsErrorState(V_attachState(vp))) {
2233         /*
2234          * pre-attach is a no-op in all but the following cases:
2235          *
2236          *   - volume is unattached
2237          *   - volume is in an error state
2238          *   - volume is pre-attached
2239          */
2240         Log("VPreattachVolumeByVp_r: volume %" AFS_VOLID_FMT " not in quiescent state (state %u flags 0x%x)\n",
2241             afs_printable_VolumeId_lu(vid), V_attachState(vp),
2242             V_attachFlags(vp));
2243         goto done;
2244     } else if (vp) {
2245         /* we're re-attaching a volume; clear out some old state */
2246         memset(&vp->salvage, 0, sizeof(struct VolumeOnlineSalvage));
2247
2248         if (V_partition(vp) != partp) {
2249             /* XXX potential race */
2250             DeleteVolumeFromVByPList_r(vp);
2251         }
2252     } else {
2253         /* if we need to allocate a new Volume struct,
2254          * go ahead and drop the vol glock, otherwise
2255          * do the basic setup synchronised, as it's
2256          * probably not worth dropping the lock */
2257         VOL_UNLOCK;
2258
2259         /* allocate the volume structure */
2260         vp = nvp = calloc(1, sizeof(Volume));
2261         opr_Assert(vp != NULL);
2262         queue_Init(&vp->vnode_list);
2263         queue_Init(&vp->rx_call_list);
2264         opr_cv_init(&V_attachCV(vp));
2265     }
2266
2267     /* link the volume with its associated vice partition */
2268     vp->device = partp->device;
2269     vp->partition = partp;
2270
2271     vp->hashid = vid;
2272     vp->specialStatus = 0;
2273
2274     /* if we dropped the lock, reacquire the lock,
2275      * check for pre-attach races, and then add
2276      * the volume to the hash table */
2277     if (nvp) {
2278         VOL_LOCK;
2279         nvp = VLookupVolume_r(ec, vid, NULL);
2280         if (*ec) {
2281             free(vp);
2282             vp = NULL;
2283             goto done;
2284         } else if (nvp) { /* race detected */
2285             free(vp);
2286             vp = nvp;
2287             goto done;
2288         } else {
2289           /* hack to make up for VChangeState_r() decrementing
2290            * the old state counter */
2291           VStats.state_levels[0]++;
2292         }
2293     }
2294
2295     /* put pre-attached volume onto the hash table
2296      * and bring it up to the pre-attached state */
2297     AddVolumeToHashTable(vp, vp->hashid);
2298     AddVolumeToVByPList_r(vp);
2299     VLRU_Init_Node_r(vp);
2300     VChangeState_r(vp, VOL_STATE_PREATTACHED);
2301
2302     if (LogLevel >= 5)
2303         Log("VPreAttachVolumeByVp_r:  volume %" AFS_VOLID_FMT " pre-attached\n", afs_printable_VolumeId_lu(vp->hashid));
2304
2305   done:
2306     if (*ec)
2307         return NULL;
2308     else
2309         return vp;
2310 }
2311 #endif /* AFS_DEMAND_ATTACH_FS */
2312
2313 /* Attach an existing volume, given its pathname, and return a
2314    pointer to the volume header information.  The volume also
2315    normally goes online at this time.  An offline volume
2316    must be reattached to make it go online */
2317 Volume *
2318 VAttachVolumeByName(Error * ec, char *partition, char *name, int mode)
2319 {
2320     Volume *retVal;
2321     VOL_LOCK;
2322     retVal = VAttachVolumeByName_r(ec, partition, name, mode);
2323     VOL_UNLOCK;
2324     return retVal;
2325 }
2326
2327 Volume *
2328 VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
2329 {
2330     Volume *vp = NULL;
2331     struct DiskPartition64 *partp;
2332     char path[64];
2333     int isbusy = 0;
2334     VolumeId volumeId;
2335     int checkedOut;
2336 #ifdef AFS_DEMAND_ATTACH_FS
2337     VolumeStats stats_save;
2338     Volume *svp = NULL;
2339 #endif /* AFS_DEMAND_ATTACH_FS */
2340
2341     *ec = 0;
2342
2343     volumeId = VolumeNumber(name);
2344
2345     if (!(partp = VGetPartition_r(partition, 0))) {
2346         *ec = VNOVOL;
2347         Log("VAttachVolume: Error getting partition (%s)\n", partition);
2348         goto done;
2349     }
2350
2351     if (VRequiresPartLock()) {
2352         opr_Assert(VInit == 3);
2353         VLockPartition_r(partition);
2354     } else if (programType == fileServer) {
2355 #ifdef AFS_DEMAND_ATTACH_FS
2356         /* lookup the volume in the hash table */
2357         vp = VLookupVolume_r(ec, volumeId, NULL);
2358         if (*ec) {
2359             return NULL;
2360         }
2361
2362         if (vp) {
2363             /* save any counters that are supposed to
2364              * be monotonically increasing over the
2365              * lifetime of the fileserver */
2366             memcpy(&stats_save, &vp->stats, sizeof(VolumeStats));
2367         } else {
2368             memset(&stats_save, 0, sizeof(VolumeStats));
2369         }
2370
2371         /* if there's something in the hash table, and it's not
2372          * in the pre-attach state, then we may need to detach
2373          * it before proceeding */
2374         if (vp && (V_attachState(vp) != VOL_STATE_PREATTACHED)) {
2375             VCreateReservation_r(vp);
2376             VWaitExclusiveState_r(vp);
2377
2378             /* at this point state must be one of:
2379              *   - UNATTACHED
2380              *   - ATTACHED
2381              *   - SHUTTING_DOWN
2382              *   - GOING_OFFLINE
2383              *   - SALVAGING
2384              *   - ERROR
2385              *   - DELETED
2386              */
2387
2388             if (vp->specialStatus == VBUSY)
2389                 isbusy = 1;
2390
2391             /* if it's already attached, see if we can return it */
2392             if (V_attachState(vp) == VOL_STATE_ATTACHED) {
2393                 VGetVolumeByVp_r(ec, vp);
2394                 if (V_inUse(vp) == fileServer) {
2395                     VCancelReservation_r(vp);
2396                     return vp;
2397                 }
2398
2399                 /* otherwise, we need to detach, and attempt to re-attach */
2400                 VDetachVolume_r(ec, vp);
2401                 if (*ec) {
2402                     Log("VAttachVolume: Error detaching old volume instance (%s)\n", name);
2403                 }
2404             } else {
2405                 /* if it isn't fully attached, delete from the hash tables,
2406                    and let the refcounter handle the rest */
2407                 DeleteVolumeFromHashTable(vp);
2408                 DeleteVolumeFromVByPList_r(vp);
2409             }
2410
2411             VCancelReservation_r(vp);
2412             vp = NULL;
2413         }
2414
2415         /* pre-attach volume if it hasn't been done yet */
2416         if (!vp ||
2417             (V_attachState(vp) == VOL_STATE_UNATTACHED) ||
2418             (V_attachState(vp) == VOL_STATE_DELETED) ||
2419             (V_attachState(vp) == VOL_STATE_ERROR)) {
2420             svp = vp;
2421             vp = VPreAttachVolumeByVp_r(ec, partp, vp, volumeId);
2422             if (*ec) {
2423                 return NULL;
2424             }
2425         }
2426
2427         opr_Assert(vp != NULL);
2428
2429         /* handle pre-attach races
2430          *
2431          * multiple threads can race to pre-attach a volume,
2432          * but we can't let them race beyond that
2433          *
2434          * our solution is to let the first thread to bring
2435          * the volume into an exclusive state win; the other
2436          * threads just wait until it finishes bringing the
2437          * volume online, and then they do a vgetvolumebyvp
2438          */
2439         if (svp && (svp != vp)) {
2440             /* wait for other exclusive ops to finish */
2441             VCreateReservation_r(vp);
2442             VWaitExclusiveState_r(vp);
2443
2444             /* get a heavyweight ref, kill the lightweight ref, and return */
2445             VGetVolumeByVp_r(ec, vp);
2446             VCancelReservation_r(vp);
2447             return vp;
2448         }
2449
2450         /* at this point, we are chosen as the thread to do
2451          * demand attachment for this volume. all other threads
2452          * doing a getvolume on vp->hashid will block until we finish */
2453
2454         /* make sure any old header cache entries are invalidated
2455          * before proceeding */
2456         FreeVolumeHeader(vp);
2457
2458         VChangeState_r(vp, VOL_STATE_ATTACHING);
2459
2460         /* restore any saved counters */
2461         memcpy(&vp->stats, &stats_save, sizeof(VolumeStats));
2462 #else /* AFS_DEMAND_ATTACH_FS */
2463         vp = VGetVolume_r(ec, volumeId);
2464         if (vp) {
2465             if (V_inUse(vp) == fileServer)
2466                 return vp;
2467             if (vp->specialStatus == VBUSY)
2468                 isbusy = 1;
2469             VDetachVolume_r(ec, vp);
2470             if (*ec) {
2471                 Log("VAttachVolume: Error detaching volume (%s)\n", name);
2472             }
2473             vp = NULL;
2474         }
2475 #endif /* AFS_DEMAND_ATTACH_FS */
2476     }
2477
2478     *ec = 0;
2479     strcpy(path, VPartitionPath(partp));
2480
2481     VOL_UNLOCK;
2482
2483     strcat(path, OS_DIRSEP);
2484     strcat(path, name);
2485
2486     if (!vp) {
2487       vp = (Volume *) calloc(1, sizeof(Volume));
2488       opr_Assert(vp != NULL);
2489       vp->hashid = volumeId;
2490       vp->device = partp->device;
2491       vp->partition = partp;
2492       queue_Init(&vp->vnode_list);
2493       queue_Init(&vp->rx_call_list);
2494 #ifdef AFS_DEMAND_ATTACH_FS
2495       opr_cv_init(&V_attachCV(vp));
2496 #endif /* AFS_DEMAND_ATTACH_FS */
2497     }
2498
2499     /* attach2 is entered without any locks, and returns
2500      * with vol_glock_mutex held */
2501     vp = attach2(ec, volumeId, path, partp, vp, isbusy, mode, &checkedOut);
2502
2503     if (VCanUseFSSYNC() && vp) {
2504 #ifdef AFS_DEMAND_ATTACH_FS
2505         if ((mode == V_VOLUPD) || (VolumeWriteable(vp) && (mode == V_CLONE))) {
2506             /* mark volume header as in use so that volser crashes lead to a
2507              * salvage attempt */
2508             VUpdateVolume_r(ec, vp, 0);
2509         }
2510         /* for dafs, we should tell the fileserver, except for V_PEEK
2511          * where we know it is not necessary */
2512         if (mode == V_PEEK) {
2513             vp->needsPutBack = 0;
2514         } else {
2515             vp->needsPutBack = VOL_PUTBACK;
2516         }
2517 #else /* !AFS_DEMAND_ATTACH_FS */
2518         /* duplicate computation in fssync.c about whether the server
2519          * takes the volume offline or not.  If the volume isn't
2520          * offline, we must not return it when we detach the volume,
2521          * or the server will abort */
2522         if (mode == V_READONLY || mode == V_PEEK
2523             || (!VolumeWriteable(vp) && (mode == V_CLONE || mode == V_DUMP)))
2524             vp->needsPutBack = 0;
2525         else
2526             vp->needsPutBack = VOL_PUTBACK;
2527 #endif /* !AFS_DEMAND_ATTACH_FS */
2528     }
2529 #ifdef FSSYNC_BUILD_CLIENT
2530     /* Only give back the vol to the fileserver if we checked it out; attach2
2531      * will set checkedOut only if we successfully checked it out from the
2532      * fileserver. */
2533     if (VCanUseFSSYNC() && vp == NULL && checkedOut) {
2534
2535 #ifdef AFS_DEMAND_ATTACH_FS
2536         /* If we couldn't attach but we scheduled a salvage, we already
2537          * notified the fileserver; don't online it now */
2538         if (*ec != VSALVAGING)
2539 #endif /* AFS_DEMAND_ATTACH_FS */
2540         FSYNC_VolOp(volumeId, partition, FSYNC_VOL_ON, 0, NULL);
2541     } else
2542 #endif
2543     if (programType == fileServer && vp) {
2544 #ifdef AFS_DEMAND_ATTACH_FS
2545         /*
2546          * we can get here in cases where we don't "own"
2547          * the volume (e.g. volume owned by a utility).
2548          * short circuit around potential disk header races.
2549          */
2550         if (V_attachState(vp) != VOL_STATE_ATTACHED) {
2551             goto done;
2552         }
2553 #endif
2554         VUpdateVolume_r(ec, vp, 0);
2555         if (*ec) {
2556             Log("VAttachVolume: Error updating volume\n");
2557             if (vp)
2558                 VPutVolume_r(vp);
2559             goto done;
2560         }
2561         if (VolumeWriteable(vp) && V_dontSalvage(vp) == 0) {
2562 #ifndef AFS_DEMAND_ATTACH_FS
2563             /* This is a hack: by temporarily setting the incore
2564              * dontSalvage flag ON, the volume will be put back on the
2565              * Update list (with dontSalvage OFF again).  It will then
2566              * come back in N minutes with DONT_SALVAGE eventually
2567              * set.  This is the way that volumes that have never had
2568              * it set get it set; or that volumes that have been
2569              * offline without DONT SALVAGE having been set also
2570              * eventually get it set */
2571             V_dontSalvage(vp) = DONT_SALVAGE;
2572 #endif /* !AFS_DEMAND_ATTACH_FS */
2573             VAddToVolumeUpdateList_r(ec, vp);
2574             if (*ec) {
2575                 Log("VAttachVolume: Error adding volume to update list\n");
2576                 if (vp)
2577                     VPutVolume_r(vp);
2578                 goto done;
2579             }
2580         }
2581         if (LogLevel)
2582           Log("VOnline:  volume %" AFS_VOLID_FMT " (%s) attached and online\n", afs_printable_VolumeId_lu(V_id(vp)),
2583                 V_name(vp));
2584     }
2585
2586   done:
2587     if (VRequiresPartLock()) {
2588         VUnlockPartition_r(partition);
2589     }
2590     if (*ec) {
2591 #ifdef AFS_DEMAND_ATTACH_FS
2592         /* attach failed; make sure we're in error state */
2593         if (vp && !VIsErrorState(V_attachState(vp))) {
2594             VChangeState_r(vp, VOL_STATE_ERROR);
2595         }
2596 #endif /* AFS_DEMAND_ATTACH_FS */
2597         return NULL;
2598     } else {
2599         return vp;
2600     }
2601 }
2602
2603 #ifdef AFS_DEMAND_ATTACH_FS
2604 /* VAttachVolumeByVp_r
2605  *
2606  * finish attaching a volume that is
2607  * in a less than fully attached state
2608  */
2609 /* caller MUST hold a ref count on vp */
2610 static Volume *
2611 VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode)
2612 {
2613     char name[VMAXPATHLEN];
2614     int reserve = 0;
2615     struct DiskPartition64 *partp;
2616     char path[64];
2617     int isbusy = 0;
2618     VolumeId volumeId;
2619     Volume * nvp = NULL;
2620     VolumeStats stats_save;
2621     int checkedOut;
2622     *ec = 0;
2623
2624     /* volume utility should never call AttachByVp */
2625     opr_Assert(programType == fileServer);
2626
2627     volumeId = vp->hashid;
2628     partp = vp->partition;
2629     VolumeExternalName_r(volumeId, name, sizeof(name));
2630
2631
2632     /* if another thread is performing a blocking op, wait */
2633     VWaitExclusiveState_r(vp);
2634
2635     memcpy(&stats_save, &vp->stats, sizeof(VolumeStats));
2636
2637     /* if it's already attached, see if we can return it */
2638     if (V_attachState(vp) == VOL_STATE_ATTACHED) {
2639         VGetVolumeByVp_r(ec, vp);
2640         if (V_inUse(vp) == fileServer) {
2641             return vp;
2642         } else {
2643             if (vp->specialStatus == VBUSY)
2644                 isbusy = 1;
2645             VDetachVolume_r(ec, vp);
2646             if (*ec) {
2647                 Log("VAttachVolume: Error detaching volume (%s)\n", name);
2648             }
2649             vp = NULL;
2650         }
2651     }
2652
2653     /* pre-attach volume if it hasn't been done yet */
2654     if (!vp ||
2655         (V_attachState(vp) == VOL_STATE_UNATTACHED) ||
2656         (V_attachState(vp) == VOL_STATE_DELETED) ||
2657         (V_attachState(vp) == VOL_STATE_ERROR)) {
2658         nvp = VPreAttachVolumeByVp_r(ec, partp, vp, volumeId);
2659         if (*ec) {
2660             return NULL;
2661         }
2662         if (nvp != vp) {
2663             reserve = 1;
2664             VCreateReservation_r(nvp);
2665             vp = nvp;
2666         }
2667     }
2668
2669     opr_Assert(vp != NULL);
2670     VChangeState_r(vp, VOL_STATE_ATTACHING);
2671
2672     /* restore monotonically increasing stats */
2673     memcpy(&vp->stats, &stats_save, sizeof(VolumeStats));
2674
2675     *ec = 0;
2676
2677     /* compute path to disk header */
2678     strcpy(path, VPartitionPath(partp));
2679
2680     VOL_UNLOCK;
2681
2682     strcat(path, OS_DIRSEP);
2683     strcat(path, name);
2684
2685     /* do volume attach
2686      *
2687      * NOTE: attach2 is entered without any locks, and returns
2688      * with vol_glock_mutex held */
2689     vp = attach2(ec, volumeId, path, partp, vp, isbusy, mode, &checkedOut);
2690
2691     /*
2692      * the event that an error was encountered, or
2693      * the volume was not brought to an attached state
2694      * for any reason, skip to the end.  We cannot
2695      * safely call VUpdateVolume unless we "own" it.
2696      */
2697     if (*ec ||
2698         (vp == NULL) ||
2699         (V_attachState(vp) != VOL_STATE_ATTACHED)) {
2700         goto done;
2701     }
2702
2703     VUpdateVolume_r(ec, vp, 0);
2704     if (*ec) {
2705         Log("VAttachVolume: Error updating volume %" AFS_VOLID_FMT "\n",
2706             afs_printable_VolumeId_lu(vp->hashid));
2707         VPutVolume_r(vp);
2708         goto done;
2709     }
2710     if (VolumeWriteable(vp) && V_dontSalvage(vp) == 0) {
2711 #ifndef AFS_DEMAND_ATTACH_FS
2712         /* This is a hack: by temporarily setting the incore
2713          * dontSalvage flag ON, the volume will be put back on the
2714          * Update list (with dontSalvage OFF again).  It will then
2715          * come back in N minutes with DONT_SALVAGE eventually
2716          * set.  This is the way that volumes that have never had
2717          * it set get it set; or that volumes that have been
2718          * offline without DONT SALVAGE having been set also
2719          * eventually get it set */
2720         V_dontSalvage(vp) = DONT_SALVAGE;
2721 #endif /* !AFS_DEMAND_ATTACH_FS */
2722         VAddToVolumeUpdateList_r(ec, vp);
2723         if (*ec) {
2724             Log("VAttachVolume: Error adding volume %" AFS_VOLID_FMT " to update list\n",
2725                 afs_printable_VolumeId_lu(vp->hashid));
2726             if (vp)
2727                 VPutVolume_r(vp);
2728             goto done;
2729         }
2730     }
2731     if (LogLevel)
2732         Log("VOnline:  volume %" AFS_VOLID_FMT " (%s) attached and online\n",
2733             afs_printable_VolumeId_lu(V_id(vp)), V_name(vp));
2734   done:
2735     if (reserve) {
2736         VCancelReservation_r(nvp);
2737         reserve = 0;
2738     }
2739     if (*ec && (*ec != VOFFLINE) && (*ec != VSALVAGE)) {
2740         if (vp && !VIsErrorState(V_attachState(vp))) {
2741             VChangeState_r(vp, VOL_STATE_ERROR);
2742         }
2743         return NULL;
2744     } else {
2745         return vp;
2746     }
2747 }
2748
2749 /**
2750  * lock a volume on disk (non-blocking).
2751  *
2752  * @param[in] vp  The volume to lock
2753  * @param[in] locktype READ_LOCK or WRITE_LOCK
2754  *
2755  * @return operation status
2756  *  @retval 0 success, lock was obtained
2757  *  @retval EBUSY a conflicting lock was held by another process
2758  *  @retval EIO   error acquiring lock
2759  *
2760  * @pre If we're in the fileserver, vp is in an exclusive state
2761  *
2762  * @pre vp is not already locked
2763  */
2764 static int
2765 VLockVolumeNB(Volume *vp, int locktype)
2766 {
2767     int code;
2768
2769     opr_Assert(programType != fileServer
2770                || VIsExclusiveState(V_attachState(vp)));
2771     opr_Assert(!(V_attachFlags(vp) & VOL_LOCKED));
2772
2773     code = VLockVolumeByIdNB(vp->hashid, vp->partition, locktype);
2774     if (code == 0) {
2775         V_attachFlags(vp) |= VOL_LOCKED;
2776     }
2777
2778     return code;
2779 }
2780
2781 /**
2782  * unlock a volume on disk that was locked with VLockVolumeNB.
2783  *
2784  * @param[in] vp  volume to unlock
2785  *
2786  * @pre If we're in the fileserver, vp is in an exclusive state
2787  *
2788  * @pre vp has already been locked
2789  */
2790 static void
2791 VUnlockVolume(Volume *vp)
2792 {
2793     opr_Assert(programType != fileServer
2794                || VIsExclusiveState(V_attachState(vp)));
2795     opr_Assert((V_attachFlags(vp) & VOL_LOCKED));
2796
2797     VUnlockVolumeById(vp->hashid, vp->partition);
2798
2799     V_attachFlags(vp) &= ~VOL_LOCKED;
2800 }
2801 #endif /* AFS_DEMAND_ATTACH_FS */
2802
2803 /**
2804  * read in a vol header, possibly lock the vol header, and possibly check out
2805  * the vol header from the fileserver, as part of volume attachment.
2806  *
2807  * @param[out] ec     error code
2808  * @param[in] vp      volume pointer object
2809  * @param[in] partp   disk partition object of the attaching partition
2810  * @param[in] mode    attachment mode such as V_VOLUPD, V_DUMP, etc (see
2811  *                    volume.h)
2812  * @param[in] peek    1 to just try to read in the volume header and make sure
2813  *                    we don't try to lock the vol, or check it out from
2814  *                    FSSYNC or anything like that; 0 otherwise, for 'normal'
2815  *                    operation
2816  * @param[out] acheckedOut   If we successfully checked-out the volume from
2817  *                           the fileserver (if we needed to), this is set
2818  *                           to 1, otherwise it is untouched.
2819  *
2820  * @note As part of DAFS volume attachment, the volume header may be either
2821  *       read- or write-locked to ensure mutual exclusion of certain volume
2822  *       operations. In some cases in order to determine whether we need to
2823  *       read- or write-lock the header, we need to read in the header to see
2824  *       if the volume is RW or not. So, if we read in the header under a
2825  *       read-lock and determine that we actually need a write-lock on the
2826  *       volume header, this function will drop the read lock, acquire a write
2827  *       lock, and read the header in again.
2828  */
2829 static void
2830 attach_volume_header(Error *ec, Volume *vp, struct DiskPartition64 *partp,
2831                      int mode, int peek, int *acheckedOut)
2832 {
2833     struct VolumeDiskHeader diskHeader;
2834     struct VolumeHeader header;
2835     int code;
2836     int first_try = 1;
2837     int lock_tries = 0, checkout_tries = 0;
2838     int retry;
2839     VolumeId volid = vp->hashid;
2840 #ifdef FSSYNC_BUILD_CLIENT
2841     int checkout, done_checkout = 0;
2842 #endif /* FSSYNC_BUILD_CLIENT */
2843 #ifdef AFS_DEMAND_ATTACH_FS
2844     int locktype = 0, use_locktype = -1;
2845 #endif /* AFS_DEMAND_ATTACH_FS */
2846
2847  retry:
2848     retry = 0;
2849     *ec = 0;
2850
2851     if (lock_tries > VOL_MAX_CHECKOUT_RETRIES) {
2852         Log("VAttachVolume: retried too many times trying to lock header for "
2853             "vol %lu part %s; giving up\n", afs_printable_uint32_lu(volid),
2854             VPartitionPath(partp));
2855         *ec = VNOVOL;
2856         goto done;
2857     }
2858     if (checkout_tries > VOL_MAX_CHECKOUT_RETRIES) {
2859         Log("VAttachVolume: retried too many times trying to checkout "
2860             "vol %lu part %s; giving up\n", afs_printable_uint32_lu(volid),
2861             VPartitionPath(partp));
2862         *ec = VNOVOL;
2863         goto done;
2864     }
2865
2866     if (VReadVolumeDiskHeader(volid, partp, NULL)) {
2867         /* short-circuit the 'volume does not exist' case */
2868         *ec = VNOVOL;
2869         goto done;
2870     }
2871
2872 #ifdef FSSYNC_BUILD_CLIENT
2873     checkout = !done_checkout;
2874     done_checkout = 1;
2875     if (!peek && checkout && VMustCheckoutVolume(mode)) {
2876         SYNC_response res;
2877         memset(&res, 0, sizeof(res));
2878
2879         if (FSYNC_VolOp(volid, partp->name, FSYNC_VOL_NEEDVOLUME, mode, &res)
2880             != SYNC_OK) {
2881
2882             if (res.hdr.reason == FSYNC_SALVAGE) {
2883                 Log("VAttachVolume: file server says volume %lu is salvaging\n",
2884                      afs_printable_uint32_lu(volid));
2885                 *ec = VSALVAGING;
2886             } else {
2887                 Log("VAttachVolume: attach of volume %lu apparently denied by file server\n",
2888                      afs_printable_uint32_lu(volid));
2889                 *ec = VNOVOL;   /* XXXX */
2890             }
2891             goto done;
2892         }
2893         *acheckedOut = 1;
2894     }
2895 #endif
2896
2897 #ifdef AFS_DEMAND_ATTACH_FS
2898     if (use_locktype < 0) {
2899         /* don't know whether vol is RO or RW; assume it's RO and we can retry
2900          * if it turns out to be RW */
2901         locktype = VVolLockType(mode, 0);
2902
2903     } else {
2904         /* a previous try says we should use use_locktype to lock the volume,
2905          * so use that */
2906         locktype = use_locktype;
2907     }
2908
2909     if (!peek && locktype) {
2910         code = VLockVolumeNB(vp, locktype);
2911         if (code) {
2912             if (code == EBUSY) {
2913                 Log("VAttachVolume: another program has vol %lu locked\n",
2914                     afs_printable_uint32_lu(volid));
2915             } else {
2916                 Log("VAttachVolume: error %d trying to lock vol %lu\n",
2917                     code, afs_printable_uint32_lu(volid));
2918             }
2919
2920             *ec = VNOVOL;
2921             goto done;
2922         }
2923     }
2924 #endif /* AFS_DEMAND_ATTACH_FS */
2925
2926     code = VReadVolumeDiskHeader(volid, partp, &diskHeader);
2927     if (code) {
2928         if (code == EIO) {
2929             *ec = VSALVAGE;
2930         } else {
2931             *ec = VNOVOL;
2932         }
2933         goto done;
2934     }
2935
2936     DiskToVolumeHeader(&header, &diskHeader);
2937
2938     IH_INIT(vp->vnodeIndex[vLarge].handle, partp->device, header.parent,
2939             header.largeVnodeIndex);
2940     IH_INIT(vp->vnodeIndex[vSmall].handle, partp->device, header.parent,
2941             header.smallVnodeIndex);
2942     IH_INIT(vp->diskDataHandle, partp->device, header.parent,
2943             header.volumeInfo);
2944     IH_INIT(vp->linkHandle, partp->device, header.parent, header.linkTable);
2945
2946     if (first_try) {
2947         /* only need to do this once */
2948         VOL_LOCK;
2949         GetVolumeHeader(vp);
2950         VOL_UNLOCK;
2951     }
2952
2953 #if defined(AFS_DEMAND_ATTACH_FS) && defined(FSSYNC_BUILD_CLIENT)
2954     /* demand attach changes the V_PEEK mechanism
2955      *
2956      * we can now suck the current disk data structure over
2957      * the fssync interface without going to disk
2958      *
2959      * (technically, we don't need to restrict this feature
2960      *  to demand attach fileservers.  However, I'm trying
2961      *  to limit the number of common code changes)
2962      */
2963     if (VCanUseFSSYNC() && (mode == V_PEEK || peek)) {
2964         SYNC_response res;
2965         res.payload.len = sizeof(VolumeDiskData);
2966         res.payload.buf = &(V_disk(vp));
2967
2968         if (FSYNC_VolOp(vp->hashid,
2969                         partp->name,
2970                         FSYNC_VOL_QUERY_HDR,
2971                         FSYNC_WHATEVER,
2972                         &res) == SYNC_OK) {
2973             goto disk_header_loaded;
2974         }
2975     }
2976 #endif /* AFS_DEMAND_ATTACH_FS && FSSYNC_BUILD_CLIENT */
2977     (void)ReadHeader(ec, V_diskDataHandle(vp), (char *)&V_disk(vp),
2978                      sizeof(V_disk(vp)), VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
2979
2980 #ifdef AFS_DEMAND_ATTACH_FS
2981     /* update stats */
2982     VOL_LOCK;
2983     IncUInt64(&VStats.hdr_loads);
2984     IncUInt64(&vp->stats.hdr_loads);
2985     VOL_UNLOCK;
2986 #endif /* AFS_DEMAND_ATTACH_FS */
2987
2988     if (*ec) {
2989         Log("VAttachVolume: Error reading diskDataHandle header for vol %lu; "
2990             "error=%u\n", afs_printable_uint32_lu(volid), *ec);
2991         goto done;
2992     }
2993
2994 #ifdef AFS_DEMAND_ATTACH_FS
2995 # ifdef FSSYNC_BUILD_CLIENT
2996  disk_header_loaded:
2997 # endif /* FSSYNC_BUILD_CLIENT */
2998
2999     /* if the lock type we actually used to lock the volume is different than
3000      * the lock type we should have used, retry with the lock type we should
3001      * use */
3002     use_locktype = VVolLockType(mode, VolumeWriteable(vp));
3003     if (locktype != use_locktype) {
3004         retry = 1;
3005         lock_tries++;
3006     }
3007 #endif /* AFS_DEMAND_ATTACH_FS */
3008
3009     *ec = 0;
3010
3011  done:
3012 #if defined(AFS_DEMAND_ATTACH_FS) && defined(FSSYNC_BUILD_CLIENT)
3013     if (!peek && *ec == 0 && retry == 0 && VMustCheckoutVolume(mode)) {
3014
3015         code = FSYNC_VerifyCheckout(volid, partp->name, FSYNC_VOL_NEEDVOLUME, mode);
3016
3017         if (code == SYNC_DENIED) {
3018             /* must retry checkout; fileserver no longer thinks we have
3019              * the volume */
3020             retry = 1;
3021             checkout_tries++;
3022             done_checkout = 0;
3023
3024         } else if (code != SYNC_OK) {
3025             *ec = VNOVOL;
3026         }
3027     }
3028 #endif /* AFS_DEMAND_ATTACH_FS && FSSYNC_BUILD_CLIENT */
3029
3030     if (*ec || retry) {
3031         /* either we are going to be called again for a second pass, or we
3032          * encountered an error; clean up in either case */
3033
3034 #ifdef AFS_DEMAND_ATTACH_FS
3035         if ((V_attachFlags(vp) & VOL_LOCKED)) {
3036             VUnlockVolume(vp);
3037         }
3038 #endif /* AFS_DEMAND_ATTACH_FS */
3039         if (vp->linkHandle) {
3040             IH_RELEASE(vp->vnodeIndex[vLarge].handle);
3041             IH_RELEASE(vp->vnodeIndex[vSmall].handle);
3042             IH_RELEASE(vp->diskDataHandle);
3043             IH_RELEASE(vp->linkHandle);
3044         }
3045     }
3046
3047     if (*ec) {
3048         VOL_LOCK;
3049         FreeVolumeHeader(vp);
3050         VOL_UNLOCK;
3051         return;
3052     }
3053     if (retry) {
3054         first_try = 0;
3055         goto retry;
3056     }
3057 }
3058
3059 #ifdef AFS_DEMAND_ATTACH_FS
3060 static void
3061 attach_check_vop(Error *ec, VolumeId volid, struct DiskPartition64 *partp,
3062                  Volume *vp, int *acheckedOut)
3063 {
3064     *ec = 0;
3065
3066     if (vp->pending_vol_op) {
3067
3068         VOL_LOCK;
3069
3070         if (vp->pending_vol_op->vol_op_state == FSSYNC_VolOpRunningUnknown) {
3071             int code;
3072             code = VVolOpLeaveOnlineNoHeader_r(vp, vp->pending_vol_op);
3073             if (code == 1) {
3074                 vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningOnline;
3075             } else if (code == 0) {
3076                 vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningOffline;
3077
3078             } else {
3079                 /* we need the vol header to determine if the volume can be
3080                  * left online for the vop, so... get the header */
3081
3082                 VOL_UNLOCK;
3083
3084                 /* attach header with peek=1 to avoid checking out the volume
3085                  * or locking it; we just want the header info, we're not
3086                  * messing with the volume itself at all */
3087                 attach_volume_header(ec, vp, partp, V_PEEK, 1, acheckedOut);
3088                 if (*ec) {
3089                     return;
3090                 }
3091
3092                 VOL_LOCK;
3093
3094                 if (VVolOpLeaveOnline_r(vp, vp->pending_vol_op)) {
3095                     vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningOnline;
3096                 } else {
3097                     vp->pending_vol_op->vol_op_state = FSSYNC_VolOpRunningOffline;
3098                 }
3099
3100                 /* make sure we grab a new vol header and re-open stuff on
3101                  * actual attachment; we can't keep the data we grabbed, since
3102                  * it was not done under a lock and thus not safe */
3103                 FreeVolumeHeader(vp);
3104                 VReleaseVolumeHandles_r(vp);
3105             }
3106         }
3107         /* see if the pending volume op requires exclusive access */
3108         switch (vp->pending_vol_op->vol_op_state) {
3109         case FSSYNC_VolOpPending:
3110             /* this should never happen */
3111             opr_Assert(vp->pending_vol_op->vol_op_state
3112                             != FSSYNC_VolOpPending);
3113             break;
3114
3115         case FSSYNC_VolOpRunningUnknown:
3116             /* this should never happen; we resolved 'unknown' above */
3117             opr_Assert(vp->pending_vol_op->vol_op_state
3118                             != FSSYNC_VolOpRunningUnknown);
3119             break;
3120
3121         case FSSYNC_VolOpRunningOffline:
3122             /* mark the volume down */
3123             *ec = VOFFLINE;
3124             VChangeState_r(vp, VOL_STATE_UNATTACHED);
3125
3126             /* do not set V_offlineMessage here; we don't have ownership of
3127              * the volume (and probably do not have the header loaded), so we
3128              * can't alter the disk header */
3129
3130             /* check to see if we should set the specialStatus flag */
3131             if (VVolOpSetVBusy_r(vp, vp->pending_vol_op)) {
3132                 /* don't overwrite specialStatus if it was already set to
3133                  * something else (e.g. VMOVED) */
3134                 if (!vp->specialStatus) {
3135                     vp->specialStatus = VBUSY;
3136                 }
3137             }
3138             break;
3139
3140         default:
3141             break;
3142         }
3143
3144         VOL_UNLOCK;
3145     }
3146 }
3147 #endif /* AFS_DEMAND_ATTACH_FS */
3148
3149 /**
3150  * volume attachment helper function.
3151  *
3152  * @param[out] ec      error code
3153  * @param[in] volumeId volume ID of the attaching volume
3154  * @param[in] path     full path to the volume header .vol file
3155  * @param[in] partp    disk partition object for the attaching partition
3156  * @param[in] vp       volume object; vp->hashid, vp->device, vp->partition,
3157  *                     vp->vnode_list, vp->rx_call_list, and V_attachCV (for
3158  *                     DAFS) should already be initialized
3159  * @param[in] isbusy   1 if vp->specialStatus should be set to VBUSY; that is,
3160  *                     if there is a volume operation running for this volume
3161  *                     that should set the volume to VBUSY during its run. 0
3162  *                     otherwise. (see VVolOpSetVBusy_r)
3163  * @param[in] mode     attachment mode such as V_VOLUPD, V_DUMP, etc (see
3164  *                     volume.h)
3165  * @param[out] acheckedOut   If we successfully checked-out the volume from
3166  *                           the fileserver (if we needed to), this is set
3167  *                           to 1, otherwise it is 0.
3168  *
3169  * @return pointer to the semi-attached volume pointer
3170  *  @retval NULL an error occurred (check value of *ec)
3171  *  @retval vp volume successfully attaching
3172  *
3173  * @pre no locks held
3174  *
3175  * @post VOL_LOCK held
3176  */
3177 static Volume *
3178 attach2(Error * ec, VolumeId volumeId, char *path, struct DiskPartition64 *partp,
3179         Volume * vp, int isbusy, int mode, int *acheckedOut)
3180 {
3181     /* have we read in the header successfully? */
3182     int read_header = 0;
3183
3184 #ifdef AFS_DEMAND_ATTACH_FS
3185     /* should we FreeVolume(vp) instead of VCheckFree(vp) in the error
3186      * cleanup? */
3187     int forcefree = 0;
3188
3189     /* in the case of an error, to what state should the volume be
3190      * transitioned? */
3191     VolState error_state = VOL_STATE_ERROR;
3192 #endif /* AFS_DEMAND_ATTACH_FS */
3193
3194     *ec = 0;
3195
3196     vp->vnodeIndex[vLarge].handle = NULL;
3197     vp->vnodeIndex[vSmall].handle = NULL;
3198     vp->diskDataHandle = NULL;
3199     vp->linkHandle = NULL;
3200
3201     *acheckedOut = 0;
3202
3203 #ifdef AFS_DEMAND_ATTACH_FS
3204     attach_check_vop(ec, volumeId, partp, vp, acheckedOut);
3205     if (!*ec) {
3206         attach_volume_header(ec, vp, partp, mode, 0, acheckedOut);
3207     }
3208 #else
3209     attach_volume_header(ec, vp, partp, mode, 0, acheckedOut);
3210 #endif /* !AFS_DEMAND_ATTACH_FS */
3211
3212     if (*ec == VNOVOL) {
3213         /* if the volume doesn't exist, skip straight to 'error' so we don't
3214          * request a salvage */
3215         goto unlocked_error;
3216     }
3217
3218     if (!*ec) {
3219         read_header = 1;
3220
3221         /* ensure that we don't override specialStatus if it was set to
3222          * something else (e.g. VMOVED) */
3223         if (isbusy && !vp->specialStatus) {
3224             vp->specialStatus = VBUSY;
3225         }
3226         vp->shuttingDown = 0;
3227         vp->goingOffline = 0;
3228         vp->nUsers = 1;
3229 #ifdef AFS_DEMAND_ATTACH_FS
3230         vp->stats.last_attach = FT_ApproxTime();
3231         vp->stats.attaches++;
3232 #endif
3233
3234         VOL_LOCK;
3235         IncUInt64(&VStats.attaches);
3236         vp->cacheCheck = ++VolumeCacheCheck;
3237         /* just in case this ever rolls over */
3238         if (!vp->cacheCheck)
3239             vp->cacheCheck = ++VolumeCacheCheck;
3240         VOL_UNLOCK;
3241
3242 #ifdef AFS_DEMAND_ATTACH_FS
3243         V_attachFlags(vp) |= VOL_HDR_LOADED;
3244         vp->stats.last_hdr_load = vp->stats.last_attach;
3245 #endif /* AFS_DEMAND_ATTACH_FS */
3246     }
3247
3248     if (!*ec) {
3249         struct IndexFileHeader iHead;
3250
3251         /*
3252          * We just read in the diskstuff part of the header.  If the detailed
3253          * volume stats area has not yet been initialized, we should bzero the
3254          * area and mark it as initialized.
3255          */
3256         if (!(V_stat_initialized(vp))) {
3257             memset((V_stat_area(vp)), 0, VOL_STATS_BYTES);
3258             V_stat_initialized(vp) = 1;
3259         }
3260
3261         (void)ReadHeader(ec, vp->vnodeIndex[vSmall].handle,
3262                          (char *)&iHead, sizeof(iHead),
3263                          SMALLINDEXMAGIC, SMALLINDEXVERSION);
3264
3265         if (*ec) {
3266             Log("VAttachVolume: Error reading smallVnode vol header %s; error=%u\n", path, *ec);
3267         }
3268     }
3269
3270     if (!*ec) {
3271         struct IndexFileHeader iHead;
3272
3273         (void)ReadHeader(ec, vp->vnodeIndex[vLarge].handle,
3274                          (char *)&iHead, sizeof(iHead),
3275                          LARGEINDEXMAGIC, LARGEINDEXVERSION);
3276
3277         if (*ec) {
3278             Log("VAttachVolume: Error reading largeVnode vol header %s; error=%u\n", path, *ec);
3279         }
3280     }
3281
3282 #ifdef AFS_NAMEI_ENV
3283     if (!*ec) {
3284         struct versionStamp stamp;
3285
3286         (void)ReadHeader(ec, V_linkHandle(vp), (char *)&stamp,
3287                          sizeof(stamp), LINKTABLEMAGIC, LINKTABLEVERSION);
3288
3289         if (*ec) {
3290             Log("VAttachVolume: Error reading namei vol header %s; error=%u\n", path, *ec);
3291         }
3292     }
3293 #endif /* AFS_NAMEI_ENV */
3294
3295 #if defined(AFS_DEMAND_ATTACH_FS)
3296     if (*ec && ((*ec != VOFFLINE) || (V_attachState(vp) != VOL_STATE_UNATTACHED))) {
3297         VOL_LOCK;
3298         if (!VCanScheduleSalvage()) {
3299             Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%u\n", path, *ec);
3300         }
3301         VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_NO_OFFLINE);
3302         vp->nUsers = 0;
3303
3304         goto locked_error;
3305     } else if (*ec) {
3306         /* volume operation in progress */
3307         VOL_LOCK;
3308         /* we have already transitioned the vp away from ATTACHING state, so we
3309          * can go right to the end of attach2, and we do not need to transition
3310          * to ERROR. */
3311         goto error_notbroken;
3312     }
3313 #else /* AFS_DEMAND_ATTACH_FS */
3314     if (*ec) {
3315         Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%u\n", path, *ec);
3316         goto unlocked_error;
3317     }
3318 #endif /* AFS_DEMAND_ATTACH_FS */
3319
3320     if (V_needsSalvaged(vp)) {
3321         if (vp->specialStatus)
3322             vp->specialStatus = 0;
3323         VOL_LOCK;
3324 #if defined(AFS_DEMAND_ATTACH_FS)
3325         if (!VCanScheduleSalvage()) {
3326             Log("VAttachVolume: volume salvage flag is ON for %s; volume needs salvage\n", path);
3327         }
3328         VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_NO_OFFLINE);
3329         vp->nUsers = 0;
3330
3331 #else /* AFS_DEMAND_ATTACH_FS */
3332         *ec = VSALVAGE;
3333 #endif /* AFS_DEMAND_ATTACH_FS */
3334
3335         goto locked_error;
3336     }
3337
3338     VOL_LOCK;
3339     vp->nextVnodeUnique = V_uniquifier(vp);
3340
3341     if (VShouldCheckInUse(mode) && V_inUse(vp) && VolumeWriteable(vp)) {
3342         if (!V_needsSalvaged(vp)) {
3343             V_needsSalvaged(vp) = 1;
3344             VUpdateVolume_r(ec, vp, 0);
3345         }
3346 #if defined(AFS_DEMAND_ATTACH_FS)
3347         if (!VCanScheduleSalvage()) {
3348             Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
3349         }
3350         VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_NO_OFFLINE);
3351         vp->nUsers = 0;
3352
3353 #else /* AFS_DEMAND_ATTACH_FS */
3354         Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
3355         *ec = VSALVAGE;
3356 #endif /* AFS_DEMAND_ATTACH_FS */
3357
3358         goto locked_error;
3359     }
3360
3361     if (programType == fileServer && V_destroyMe(vp) == DESTROY_ME) {
3362         /* Only check destroyMe if we are the fileserver, since the
3363          * volserver et al sometimes need to work with volumes with
3364          * destroyMe set. Examples are 'temporary' volumes the
3365          * volserver creates, and when we create a volume (destroyMe
3366          * is set on creation; sometimes a separate volserver
3367          * transaction is created to clear destroyMe).
3368          */
3369
3370 #if defined(AFS_DEMAND_ATTACH_FS)
3371         /* schedule a salvage so the volume goes away on disk */
3372         VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_NO_OFFLINE);
3373         VChangeState_r(vp, VOL_STATE_ERROR);
3374         vp->nUsers = 0;
3375         forcefree = 1;
3376 #endif /* AFS_DEMAND_ATTACH_FS */
3377         Log("VAttachVolume: volume %s is junk; it should be destroyed at next salvage\n", path);
3378         *ec = VNOVOL;
3379         goto locked_error;
3380     }
3381
3382     vp->vnodeIndex[vSmall].bitmap = vp->vnodeIndex[vLarge].bitmap = NULL;
3383 #ifndef BITMAP_LATER
3384     if (programType == fileServer && VolumeWriteable(vp)) {
3385         int i;
3386         for (i = 0; i < nVNODECLASSES; i++) {
3387             VGetBitmap_r(ec, vp, i);
3388             if (*ec) {
3389 #ifdef AFS_DEMAND_ATTACH_FS
3390                 VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_NO_OFFLINE);
3391                 vp->nUsers = 0;
3392 #endif /* AFS_DEMAND_ATTACH_FS */
3393                 Log("VAttachVolume: error getting bitmap for volume (%s)\n",
3394                     path);
3395                 goto locked_error;
3396             }
3397         }
3398     }
3399 #endif /* BITMAP_LATER */
3400
3401     if (VInit >= 2 && V_needsCallback(vp)) {
3402         if (V_BreakVolumeCallbacks) {
3403             Log("VAttachVolume: Volume %lu was changed externally; breaking callbacks\n",
3404                 afs_printable_uint32_lu(V_id(vp)));
3405             V_needsCallback(vp) = 0;
3406             VOL_UNLOCK;
3407             (*V_BreakVolumeCallbacks) (V_id(vp));
3408             VOL_LOCK;
3409
3410             VUpdateVolume_r(ec, vp, 0);
3411         }
3412 #ifdef FSSYNC_BUILD_CLIENT
3413         else if (VCanUseFSSYNC()) {
3414             afs_int32 fsync_code;
3415
3416             V_needsCallback(vp) = 0;
3417             VOL_UNLOCK;
3418             fsync_code = FSYNC_VolOp(V_id(vp), NULL, FSYNC_VOL_BREAKCBKS, FSYNC_WHATEVER, NULL);
3419             VOL_LOCK;
3420
3421             if (fsync_code) {
3422                 V_needsCallback(vp) = 1;
3423                 Log("Error trying to tell the fileserver to break callbacks for "
3424                     "changed volume %lu; error code %ld\n",
3425                     afs_printable_uint32_lu(V_id(vp)),
3426                     afs_printable_int32_ld(fsync_code));
3427             } else {
3428                 VUpdateVolume_r(ec, vp, 0);
3429             }
3430         }
3431 #endif /* FSSYNC_BUILD_CLIENT */
3432
3433         if (*ec) {
3434             Log("VAttachVolume: error %d clearing needsCallback on volume "
3435                 "%lu; needs salvage\n", (int)*ec,
3436                 afs_printable_uint32_lu(V_id(vp)));
3437 #ifdef AFS_DEMAND_ATTACH_FS
3438             VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_NO_OFFLINE);
3439             vp->nUsers = 0;
3440 #else /* !AFS_DEMAND_ATTACH_FS */
3441             *ec = VSALVAGE;
3442 #endif /* !AFS_DEMAND_ATTACh_FS */
3443             goto locked_error;
3444         }
3445     }
3446
3447     if (programType == fileServer) {
3448         if (vp->specialStatus)
3449             vp->specialStatus = 0;
3450         if (V_blessed(vp) && V_inService(vp) && !V_needsSalvaged(vp)) {
3451             V_inUse(vp) = fileServer;
3452             V_offlineMessage(vp)[0] = '\0';
3453         }
3454 #ifdef AFS_DEMAND_ATTACH_FS
3455         /* check if the volume is actually usable. only do this for DAFS; for
3456          * non-DAFS, volumes that are not inService/blessed can still be
3457          * attached, even if clients cannot access them. this is relevant
3458          * because for non-DAFS, we try to attach the volume when e.g.
3459          * volserver gives us back then vol when its done with it, but
3460          * volserver may give us back a volume that is not inService/blessed. */
3461
3462         if (!V_inUse(vp)) {
3463             *ec = VNOVOL;
3464             /* Put the vol into PREATTACHED state, so if someone tries to
3465              * access it again, we try to attach, see that we're not blessed,
3466              * and give a VNOVOL error again. Putting it into UNATTACHED state
3467              * would result in a VOFFLINE error instead. */
3468             error_state = VOL_STATE_PREATTACHED;
3469
3470             /* mimic e.g. GetVolume errors */
3471             if (!V_blessed(vp)) {
3472                 Log("Volume %lu offline: not blessed\n", afs_printable_uint32_lu(V_id(vp)));
3473                 FreeVolumeHeader(vp);
3474             } else if (!V_inService(vp)) {
3475                 Log("Volume %lu offline: not in service\n", afs_printable_uint32_lu(V_id(vp)));
3476                 FreeVolumeHeader(vp);
3477             } else {
3478                 Log("Volume %lu offline: needs salvage\n", afs_printable_uint32_lu(V_id(vp)));
3479                 *ec = VSALVAGE;
3480                 error_state = VOL_STATE_ERROR;
3481                 /* see if we can recover */
3482                 VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_NO_OFFLINE);
3483             }
3484             vp->nUsers = 0;
3485             goto locked_error;
3486         }
3487 #endif /* AFS_DEMAND_ATTACH_FS */
3488     } else {
3489 #ifdef AFS_DEMAND_ATTACH_FS
3490         if ((mode != V_PEEK) && (mode != V_SECRETLY) && (mode != V_READONLY))
3491             V_inUse(vp) = programType;
3492 #endif /* AFS_DEMAND_ATTACH_FS */
3493         V_checkoutMode(vp) = mode;
3494     }
3495
3496     AddVolumeToHashTable(vp, vp->hashid);
3497 #ifdef AFS_DEMAND_ATTACH_FS
3498     if (VCanUnlockAttached() && (V_attachFlags(vp) & VOL_LOCKED)) {
3499         VUnlockVolume(vp);
3500     }
3501     if ((programType != fileServer) ||
3502         (V_inUse(vp) == fileServer)) {
3503         AddVolumeToVByPList_r(vp);
3504         VLRU_Add_r(vp);
3505         VChangeState_r(vp, VOL_STATE_ATTACHED);
3506     } else {
3507         VChangeState_r(vp, VOL_STATE_UNATTACHED);
3508     }
3509 #endif
3510
3511     return vp;
3512
3513 unlocked_error:
3514     VOL_LOCK;
3515 locked_error:
3516 #ifdef AFS_DEMAND_ATTACH_FS
3517     if (!VIsErrorState(V_attachState(vp))) {
3518         if (programType != fileServer && *ec == VNOVOL) {
3519             /* do not log anything in this case; it is common for
3520              * non-fileserver programs to fail here with VNOVOL, since that
3521              * is what happens when they simply try to use a volume, but that
3522              * volume doesn't exist. */
3523
3524         } else if (VIsErrorState(error_state)) {
3525             Log("attach2: forcing vol %" AFS_VOLID_FMT " to error state (state %u flags 0x%x ec %d)\n",
3526                 afs_printable_VolumeId_lu(vp->hashid), V_attachState(vp),
3527                 V_attachFlags(vp), *ec);
3528         }
3529         VChangeState_r(vp, error_state);
3530     }
3531 #endif /* AFS_DEMAND_ATTACH_FS */
3532
3533     if (read_header) {
3534         VReleaseVolumeHandles_r(vp);
3535     }
3536
3537 #ifdef AFS_DEMAND_ATTACH_FS
3538  error_notbroken:
3539     if (VCheckSalvage(vp) == VCHECK_SALVAGE_FAIL) {
3540         /* The salvage could not be scheduled with the salvage server
3541          * due to a hard error. Reset the error code to prevent retry loops by
3542          * callers. */
3543         if (*ec == VSALVAGING) {
3544             *ec = VSALVAGE;
3545         }
3546     }
3547     if (forcefree) {
3548         FreeVolume(vp);
3549     } else {
3550         VCheckFree(vp);
3551     }
3552 #else /* !AFS_DEMAND_ATTACH_FS */
3553     FreeVolume(vp);
3554 #endif /* !AFS_DEMAND_ATTACH_FS */
3555     return NULL;
3556 }
3557
3558 /* Attach an existing volume.
3559    The volume also normally goes online at this time.
3560    An offline volume must be reattached to make it go online.
3561  */
3562
3563 Volume *
3564 VAttachVolume(Error * ec, VolumeId volumeId, int mode)
3565 {
3566     Volume *retVal;
3567     VOL_LOCK;
3568     retVal = VAttachVolume_r(ec, volumeId, mode);
3569     VOL_UNLOCK;
3570     return retVal;
3571 }
3572
3573 Volume *
3574 VAttachVolume_r(Error * ec, VolumeId volumeId, int mode)
3575 {
3576     char *part, *name;
3577     VGetVolumePath(ec, volumeId, &part, &name);
3578     if (*ec) {
3579         Volume *vp;
3580         Error error;
3581         vp = VGetVolume_r(&error, volumeId);
3582         if (vp) {
3583             opr_Assert(V_inUse(vp) == 0);
3584             VDetachVolume_r(ec, vp);
3585         }
3586         return NULL;
3587     }
3588     return VAttachVolumeByName_r(ec, part, name, mode);
3589 }
3590
3591 /* Increment a reference count to a volume, sans context swaps.  Requires
3592  * possibly reading the volume header in from the disk, since there's
3593  * an invariant in the volume package that nUsers>0 ==> vp->header is valid.
3594  *
3595  * N.B. This call can fail if we can't read in the header!!  In this case
3596  * we still guarantee we won't context swap, but the ref count won't be
3597  * incremented (otherwise we'd violate the invariant).
3598  */
3599 /* NOTE: with the demand attach fileserver extensions, the global lock
3600  * is dropped within VHold */
3601 #ifdef AFS_DEMAND_ATTACH_FS
3602 static int
3603 VHold_r(Volume * vp)
3604 {
3605     Error error;
3606
3607     VCreateReservation_r(vp);
3608     VWaitExclusiveState_r(vp);
3609
3610     LoadVolumeHeader(&error, vp);
3611     if (error) {
3612         VCancelReservation_r(vp);
3613         return error;
3614     }
3615     vp->nUsers++;
3616     VCancelReservation_r(vp);
3617     return 0;
3618 }
3619 #else /* AFS_DEMAND_ATTACH_FS */
3620 static int
3621 VHold_r(Volume * vp)
3622 {
3623     Error error;
3624
3625     LoadVolumeHeader(&error, vp);
3626     if (error)
3627         return error;
3628     vp->nUsers++;
3629     return 0;
3630 }
3631 #endif /* AFS_DEMAND_ATTACH_FS */
3632
3633 /**** volume timeout-related stuff ****/
3634
3635 #ifdef AFS_PTHREAD_ENV
3636
3637 static struct timespec *shutdown_timeout;
3638 static pthread_once_t shutdown_timeout_once = PTHREAD_ONCE_INIT;
3639
3640 static_inline int
3641 VTimedOut(const struct timespec *ts)
3642 {
3643     struct timeval tv;
3644     int code;
3645
3646     if (ts->tv_sec == 0) {
3647         /* short-circuit; this will have always timed out */
3648         return 1;
3649     }
3650
3651     code = gettimeofday(&tv, NULL);
3652     if (code) {
3653         Log("Error %d from gettimeofday, assuming we have not timed out\n", errno);
3654         /* assume no timeout; failure mode is we just wait longer than normal
3655          * instead of returning errors when we shouldn't */
3656         return 0;
3657     }
3658
3659     if (tv.tv_sec < ts->tv_sec ||
3660         (tv.tv_sec == ts->tv_sec && tv.tv_usec*1000 < ts->tv_nsec)) {
3661
3662         return 0;
3663     }
3664
3665     return 1;
3666 }
3667
3668 /**
3669  * Calculate an absolute timeout.
3670  *
3671  * @param[out] ts  A timeout that is "timeout" seconds from now, if we return
3672  *                 NULL, the memory is not touched
3673  * @param[in]  timeout  How long the timeout should be from now
3674  *
3675  * @return timeout to use
3676  *  @retval NULL      no timeout; wait forever
3677  *  @retval non-NULL  the given value for "ts"
3678  *
3679  * @internal
3680  */
3681 static struct timespec *
3682 VCalcTimeout(struct timespec *ts, afs_int32 timeout)
3683 {
3684     struct timeval now;
3685     int code;
3686
3687     if (timeout < 0) {
3688         return NULL;
3689     }
3690
3691     if (timeout == 0) {
3692         ts->tv_sec = ts->tv_nsec = 0;
3693         return ts;
3694     }
3695
3696     code = gettimeofday(&now, NULL);
3697     if (code) {
3698         Log("Error %d from gettimeofday, falling back to 'forever' timeout\n", errno);
3699         return NULL;
3700     }
3701
3702     ts->tv_sec = now.tv_sec + timeout;
3703     ts->tv_nsec = now.tv_usec * 1000;
3704
3705     return ts;
3706 }
3707
3708 /**
3709  * Initialize the shutdown_timeout global.
3710  */
3711 static void
3712 VShutdownTimeoutInit(void)
3713 {
3714     struct timespec *ts;
3715
3716     ts = malloc(sizeof(*ts));
3717
3718     shutdown_timeout = VCalcTimeout(ts, vol_opts.offline_shutdown_timeout);
3719
3720     if (!shutdown_timeout) {
3721         free(ts);
3722     }
3723 }
3724
3725 /**
3726  * Figure out the timeout that should be used for waiting for offline volumes.
3727  *
3728  * @param[out] ats  Storage space for a local timeout value if needed
3729  *
3730  * @return The timeout value that should be used
3731  *   @retval NULL      No timeout; wait forever for offlining volumes
3732  *   @retval non-NULL  A pointer to the absolute time that should be used as
3733  *                     the deadline for waiting for offlining volumes.
3734  *
3735  * @note If we return non-NULL, the pointer we return may or may not be the
3736  *       same as "ats"
3737  */
3738 static const struct timespec *
3739 VOfflineTimeout(struct timespec *ats)
3740 {
3741     if (vol_shutting_down) {
3742         opr_Verify(pthread_once(&shutdown_timeout_once,
3743                                 VShutdownTimeoutInit) == 0);
3744         return shutdown_timeout;
3745     } else {
3746         return VCalcTimeout(ats, vol_opts.offline_timeout);
3747     }
3748 }
3749
3750 #else /* AFS_PTHREAD_ENV */
3751
3752 /* Waiting a certain amount of time for offlining volumes is not supported
3753  * for LWP due to a lack of primitives. So, we never time out */
3754 # define VTimedOut(x) (0)
3755 # define VOfflineTimeout(x) (NULL)
3756
3757 #endif /* !AFS_PTHREAD_ENV */
3758
3759 #if 0
3760 static int
3761 VHold(Volume * vp)
3762 {
3763     int retVal;
3764     VOL_LOCK;
3765     retVal = VHold_r(vp);
3766     VOL_UNLOCK;
3767     return retVal;
3768 }
3769 #endif
3770
3771 static afs_int32
3772 VIsGoingOffline_r(struct Volume *vp)
3773 {
3774     afs_int32 code = 0;
3775
3776     if (vp->goingOffline) {
3777         if (vp->specialStatus) {
3778             code = vp->specialStatus;
3779         } else if (V_inService(vp) == 0 || V_blessed(vp) == 0) {
3780             code = VNOVOL;
3781         } else {
3782             code = VOFFLINE;
3783         }
3784     }
3785
3786     return code;
3787 }
3788
3789 /**
3790  * Tell the caller if a volume is waiting to go offline.
3791  *
3792  * @param[in] vp  The volume we want to know about
3793  *
3794  * @return volume status
3795  *   @retval 0 volume is not waiting to go offline, go ahead and use it
3796  *   @retval nonzero volume is waiting to offline, and give the returned code
3797  *           as an error to anyone accessing the volume
3798  *
3799  * @pre VOL_LOCK is NOT held
3800  * @pre caller holds a heavyweight reference on vp
3801  */
3802 afs_int32
3803 VIsGoingOffline(struct Volume *vp)
3804 {
3805     afs_int32 code;
3806
3807     VOL_LOCK;
3808     code = VIsGoingOffline_r(vp);
3809     VOL_UNLOCK;
3810
3811     return code;
3812 }
3813
3814 /**
3815  * Register an RX call with a volume.
3816  *
3817  * @param[inout] ec        Error code; if unset when passed in, may be set if
3818  *                         the volume starts going offline
3819  * @param[out]   client_ec @see GetVolume
3820  * @param[in] vp   Volume struct
3821  * @param[in] cbv  VCallByVol struct containing the RX call to register
3822  *
3823  * @pre VOL_LOCK held
3824  * @pre caller holds heavy ref on vp
3825  *
3826  * @internal
3827  */
3828 static void
3829 VRegisterCall_r(Error *ec, Error *client_ec, Volume *vp, struct VCallByVol *cbv)
3830 {
3831     if (vp && cbv) {
3832 #ifdef AFS_DEMAND_ATTACH_FS
3833         if (!*ec) {
3834             /* just in case the volume started going offline after we got the
3835              * reference to it... otherwise, if the volume started going
3836              * offline right at the end of GetVolume(), we might race with the
3837              * RX call scanner, and return success and add our cbv to the
3838              * rx_call_list _after_ the scanner has scanned the list. */
3839             *ec = VIsGoingOffline_r(vp);
3840             if (client_ec) {
3841                 *client_ec = *ec;
3842             }
3843         }
3844
3845         while (V_attachState(vp) == VOL_STATE_SCANNING_RXCALLS) {
3846             VWaitStateChange_r(vp);
3847         }
3848 #endif /* AFS_DEMAND_ATTACH_FS */
3849
3850         queue_Prepend(&vp->rx_call_list, cbv);
3851     }
3852 }
3853
3854 /**
3855  * Deregister an RX call with a volume.
3856  *
3857  * @param[in] vp   Volume struct
3858  * @param[in] cbv  VCallByVol struct containing the RX call to deregister
3859  *
3860  * @pre VOL_LOCK held
3861  * @pre caller holds heavy ref on vp
3862  *
3863  * @internal
3864  */
3865 static void
3866 VDeregisterCall_r(Volume *vp, struct VCallByVol *cbv)
3867 {
3868     if (cbv && queue_IsOnQueue(cbv)) {
3869 #ifdef AFS_DEMAND_ATTACH_FS
3870         while (V_attachState(vp) == VOL_STATE_SCANNING_RXCALLS) {
3871             VWaitStateChange_r(vp);
3872         }
3873 #endif /* AFS_DEMAND_ATTACH_FS */
3874
3875         queue_Remove(cbv);
3876     }
3877 }
3878
3879 /***************************************************/
3880 /* get and put volume routines                     */
3881 /***************************************************/
3882
3883 /**
3884  * put back a heavyweight reference to a volume object.
3885  *
3886  * @param[in] vp  volume object pointer
3887  *
3888  * @pre VOL_LOCK held
3889  *
3890  * @post heavyweight volume reference put back.
3891  *       depending on state, volume may have been taken offline,
3892  *       detached, salvaged, freed, etc.
3893  *
3894  * @internal volume package internal use only
3895  */
3896 void
3897 VPutVolume_r(Volume * vp)
3898 {
3899     opr_Verify(--vp->nUsers >= 0);
3900     if (vp->nUsers == 0) {
3901         VCheckOffline(vp);
3902         ReleaseVolumeHeader(vp->header);
3903 #ifdef AFS_DEMAND_ATTACH_FS
3904         if (!VCheckDetach(vp)) {
3905             VCheckSalvage(vp);
3906             VCheckFree(vp);
3907         }
3908 #else /* AFS_DEMAND_ATTACH_FS */
3909         VCheckDetach(vp);
3910 #endif /* AFS_DEMAND_ATTACH_FS */
3911     }
3912 }
3913
3914 void
3915 VPutVolume(Volume * vp)
3916 {
3917     VOL_LOCK;
3918     VPutVolume_r(vp);
3919     VOL_UNLOCK;
3920 }
3921
3922 /**
3923  * Puts a volume reference obtained with VGetVolumeWithCall.
3924  *
3925  * @param[in] vp  Volume struct
3926  * @param[in] cbv VCallByVol struct given to VGetVolumeWithCall, or NULL if none
3927  *
3928  * @pre VOL_LOCK is NOT held
3929  */
3930 void
3931 VPutVolumeWithCall(Volume *vp, struct VCallByVol *cbv)
3932 {
3933     VOL_LOCK;
3934     VDeregisterCall_r(vp, cbv);
3935     VPutVolume_r(vp);
3936     VOL_UNLOCK;
3937 }
3938
3939 /* Get a pointer to an attached volume.  The pointer is returned regardless
3940    of whether or not the volume is in service or on/off line.  An error
3941    code, however, is returned with an indication of the volume's status */
3942 Volume *
3943 VGetVolume(Error * ec, Error * client_ec, VolumeId volumeId)
3944 {
3945     Volume *retVal;
3946     VOL_LOCK;
3947     retVal = GetVolume(ec, client_ec, volumeId, NULL, 0);
3948     VOL_UNLOCK;
3949     return retVal;
3950 }
3951
3952 /**
3953  * Get a volume reference associated with an RX call.
3954  *
3955  * @param[out] ec @see GetVolume
3956  * @param[out] client_ec @see GetVolume
3957  * @param[in] volumeId @see GetVolume
3958  * @param[in] ts  How long to wait for going-offline volumes (absolute time).
3959  *                If NULL, wait forever. If ts->tv_sec == 0, return immediately
3960  *                with an error if the volume is going offline.
3961  * @param[in] cbv Contains an RX call to be associated with this volume
3962  *                reference. This call may be interrupted if the volume is
3963  *                requested to go offline while we hold a ref on it. Give NULL
3964  *                to not associate an RX call with this reference.
3965  *
3966  * @return @see GetVolume
3967  *
3968  * @note for LWP builds, ts must be NULL
3969  *
3970  * @note A reference obtained with this function MUST be put back with
3971  *       VPutVolumeWithCall
3972  */
3973 Volume *
3974 VGetVolumeWithCall(Error * ec, Error * client_ec, VolumeId volumeId,
3975                    const struct timespec *ts, struct VCallByVol *cbv)
3976 {
3977     Volume *retVal;
3978     VOL_LOCK;
3979     retVal = GetVolume(ec, client_ec, volumeId, NULL, ts);
3980     VRegisterCall_r(ec, client_ec, retVal, cbv);
3981     VOL_UNLOCK;
3982     return retVal;
3983 }
3984
3985 Volume *
3986 VGetVolume_r(Error * ec, VolumeId volumeId)
3987 {
3988     return GetVolume(ec, NULL, volumeId, NULL, NULL);
3989 }
3990
3991 /* try to get a volume we've previously looked up */
3992 /* for demand attach fs, caller MUST NOT hold a ref count on vp */
3993 Volume *
3994 VGetVolumeByVp_r(Error * ec, Volume * vp)
3995 {
3996     return GetVolume(ec, NULL, vp->hashid, vp, NULL);
3997 }
3998
3999 /**
4000  * private interface for getting a volume handle
4001  *
4002  * @param[out] ec         error code (0 if no error)
4003  * @param[out] client_ec  wire error code to be given to clients
4004  * @param[in]  volumeId   ID of the volume we want
4005  * @param[in]  hint       optional hint for hash lookups, or NULL
4006  * @param[in]  timeout    absolute deadline for waiting for the volume to go
4007  *                        offline, if it is going offline. NULL to wait forever.
4008  *
4009  * @return a volume handle for the specified volume
4010  *  @retval NULL an error occurred, or the volume is in such a state that
4011  *               we cannot load a header or return any volume struct
4012  *
4013  * @note for DAFS, caller must NOT hold a ref count on 'hint'
4014  *
4015  * @note 'timeout' is only checked if the volume is actually going offline; so
4016  *       if you pass timeout->tv_sec = 0, this will exhibit typical
4017  *       nonblocking behavior.
4018  *
4019  * @note for LWP builds, 'timeout' must be NULL
4020  */
4021 static Volume *
4022 GetVolume(Error * ec, Error * client_ec, VolumeId volumeId, Volume * hint,
4023           const struct timespec *timeout)
4024 {
4025     Volume *vp = hint;
4026     /* pull this profiling/debugging code out of regular builds */
4027 #ifdef notdef
4028 #define VGET_CTR_INC(x) x++
4029     unsigned short V0 = 0, V1 = 0, V2 = 0, V3 = 0, V5 = 0, V6 =
4030         0, V7 = 0, V8 = 0, V9 = 0;
4031     unsigned short V10 = 0, V11 = 0, V12 = 0, V13 = 0, V14 = 0, V15 = 0;
4032 #else
4033 #define VGET_CTR_INC(x)
4034 #endif
4035 #ifdef AFS_DEMAND_ATTACH_FS
4036     Volume *avp, * rvp = hint;
4037 #endif
4038
4039     /*
4040      * if VInit is zero, the volume package dynamic
4041      * data structures have not been initialized yet,
4042      * and we must immediately return an error
4043      */
4044     if (VInit == 0) {
4045         vp = NULL;
4046         *ec = VOFFLINE;
4047         if (client_ec) {
4048             *client_ec = VOFFLINE;
4049         }
4050         goto not_inited;
4051     }
4052
4053 #ifdef AFS_DEMAND_ATTACH_FS
4054     if (rvp) {
4055         VCreateReservation_r(rvp);
4056     }
4057 #endif /* AFS_DEMAND_ATTACH_FS */
4058
4059     for (;;) {
4060         *ec = 0;
4061         if (client_ec)
4062             *client_ec = 0;
4063         VGET_CTR_INC(V0);
4064
4065         vp = VLookupVolume_r(ec, volumeId, vp);
4066         if (*ec) {
4067             vp = NULL;
4068             break;
4069         }
4070
4071 #ifdef AFS_DEMAND_ATTACH_FS
4072         if (rvp && (rvp != vp)) {
4073             /* break reservation on old vp */
4074             VCancelReservation_r(rvp);
4075             rvp = NULL;
4076         }
4077 #endif /* AFS_DEMAND_ATTACH_FS */
4078
4079         if (!vp) {
4080             VGET_CTR_INC(V1);
4081             if (VInit < 2) {
4082                 VGET_CTR_INC(V2);
4083                 /* Until we have reached an initialization level of 2
4084                  * we don't know whether this volume exists or not.
4085                  * We can't sleep and retry later because before a volume
4086                  * is attached, the caller tries to get it first.  Just
4087                  * return VOFFLINE and the caller can choose whether to
4088                  * retry the command or not. */
4089                 *ec = VOFFLINE;
4090                 break;
4091             }
4092
4093             *ec = VNOVOL;
4094             break;
4095         }
4096
4097         VGET_CTR_INC(V3);
4098         IncUInt64(&VStats.hdr_gets);
4099
4100 #ifdef AFS_DEMAND_ATTACH_FS
4101         /* block if someone else is performing an exclusive op on this volume */
4102         if (rvp != vp) {
4103             rvp = vp;
4104             VCreateReservation_r(rvp);
4105         }
4106         VWaitExclusiveState_r(vp);
4107
4108         /* short circuit with VNOVOL in the following circumstances:
4109          *
4110          *   - VOL_STATE_ERROR
4111          *   - VOL_STATE_SHUTTING_DOWN
4112          */
4113         if ((V_attachState(vp) == VOL_STATE_ERROR) ||
4114             (V_attachState(vp) == VOL_STATE_SHUTTING_DOWN)) {
4115             *ec = VNOVOL;
4116             vp = NULL;
4117             break;
4118         }
4119
4120         /*
4121          * short circuit with VOFFLINE for VOL_STATE_UNATTACHED/GOING_OFFLINE and
4122          *                    VNOVOL   for VOL_STATE_DELETED
4123          */
4124        if ((V_attachState(vp) == VOL_STATE_UNATTACHED) ||
4125            (V_attachState(vp) == VOL_STATE_GOING_OFFLINE) ||
4126            (V_attachState(vp) == VOL_STATE_DELETED)) {
4127            if (vp->specialStatus) {
4128                *ec = vp->specialStatus;
4129            } else if (V_attachState(vp) == VOL_STATE_DELETED) {
4130                *ec = VNOVOL;
4131            } else {
4132                *ec = VOFFLINE;
4133            }
4134            vp = NULL;
4135            break;
4136        }
4137
4138         /* allowable states:
4139          *   - PREATTACHED
4140          *   - ATTACHED
4141          *   - SALVAGING
4142          *   - SALVAGE_REQ
4143          */
4144
4145         if (vp->salvage.requested) {
4146             VUpdateSalvagePriority_r(vp);
4147         }
4148
4149         if (V_attachState(vp) == VOL_STATE_PREATTACHED) {
4150             if (vp->specialStatus) {
4151                 *ec = vp->specialStatus;
4152                 vp = NULL;
4153                 break;
4154             }
4155             avp = VAttachVolumeByVp_r(ec, vp, 0);
4156             if (avp) {
4157                 if (vp != avp) {
4158                     /* VAttachVolumeByVp_r can return a pointer
4159                      * != the vp passed to it under certain
4160                      * conditions; make sure we don't leak
4161                      * reservations if that happens */
4162                     vp = avp;
4163                     VCancelReservation_r(rvp);
4164                     rvp = avp;
4165                     VCreateReservation_r(rvp);
4166                 }
4167                 VPutVolume_r(avp);
4168             }
4169             if (*ec) {
4170                 int endloop = 0;
4171                 switch (*ec) {
4172                 case VSALVAGING:
4173                     break;
4174                 case VOFFLINE:
4175                     endloop = 1;
4176                     if (vp->specialStatus) {
4177                         *ec = vp->specialStatus;
4178                     }
4179                     break;
4180
4181                 default:
4182                     if (vp->specialStatus) {
4183                         *ec = vp->specialStatus;
4184                     } else {
4185                         *ec = VNOVOL;
4186                     }
4187                     endloop = 1;
4188                 }
4189                 if (endloop) {
4190                     vp = NULL;
4191                     break;
4192                 }
4193             }
4194         }
4195
4196         if (VIsSalvaging(vp) || (*ec == VSALVAGING)) {
4197             if (client_ec) {
4198                 /* see CheckVnode() in afsfileprocs.c for an explanation
4199                  * of this error code logic */
4200                 afs_uint32 now = FT_ApproxTime();
4201                 if ((vp->stats.last_salvage + (10 * 60)) >= now) {
4202                     *client_ec = VBUSY;
4203                 } else {
4204                     *client_ec = VRESTARTING;
4205                 }
4206             }
4207             *ec = VSALVAGING;
4208             vp = NULL;
4209             break;
4210         }
4211
4212         if (VIsErrorState(V_attachState(vp))) {
4213             /* make sure we don't take a vp in VOL_STATE_ERROR state and use
4214              * it, or transition it out of that state */
4215             if (!*ec) {
4216                 *ec = VNOVOL;
4217             }
4218             vp = NULL;
4219             break;
4220         }
4221
4222         /*
4223          * this test MUST happen after VAttachVolymeByVp, so we have no
4224          * conflicting vol op. (attach2 would have errored out if we had one;
4225          * specifically attach_check_vop must have detected a conflicting vop)
4226          */
4227          opr_Assert(!vp->pending_vol_op || vp->pending_vol_op->vol_op_state == FSSYNC_VolOpRunningOnline);
4228
4229 #endif /* AFS_DEMAND_ATTACH_FS */
4230
4231         LoadVolumeHeader(ec, vp);
4232         if (*ec) {
4233             VGET_CTR_INC(V6);
4234             /* Only log the error if it was a totally unexpected error.  Simply
4235              * a missing inode is likely to be caused by the volume being deleted */
4236             if (errno != ENXIO || LogLevel)
4237                 Log("Volume %" AFS_VOLID_FMT ": couldn't reread volume header\n",
4238                     afs_printable_VolumeId_lu(vp->hashid));
4239 #ifdef AFS_DEMAND_ATTACH_FS
4240             if (VCanScheduleSalvage()) {
4241                 VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0 /*flags*/);
4242             } else {
4243                 FreeVolume(vp);
4244                 vp = NULL;
4245             }
4246 #else /* AFS_DEMAND_ATTACH_FS */
4247             FreeVolume(vp);
4248             vp = NULL;
4249 #endif /* AFS_DEMAND_ATTACH_FS */
4250             break;
4251         }
4252
4253         VGET_CTR_INC(V7);
4254         if (vp->shuttingDown) {
4255             VGET_CTR_INC(V8);
4256             *ec = VNOVOL;
4257             vp = NULL;
4258             break;
4259         }
4260
4261         if (programType == fileServer) {
4262             VGET_CTR_INC(V9);
4263             if (vp->goingOffline) {
4264                 if (timeout && VTimedOut(timeout)) {
4265                     /* we've timed out; don't wait for the vol */
4266                 } else {
4267                     VGET_CTR_INC(V10);
4268 #ifdef AFS_DEMAND_ATTACH_FS
4269                     /* wait for the volume to go offline */
4270                     if (V_attachState(vp) == VOL_STATE_GOING_OFFLINE) {
4271                         VTimedWaitStateChange_r(vp, timeout, NULL);
4272                     }
4273 #elif defined(AFS_PTHREAD_ENV)
4274                     VOL_CV_TIMEDWAIT(&vol_put_volume_cond, timeout, NULL);
4275 #else /* AFS_PTHREAD_ENV */
4276                     /* LWP has no timed wait, so the caller better not be
4277                      * expecting one */
4278                     opr_Assert(!timeout);
4279                     LWP_WaitProcess(VPutVolume);
4280 #endif /* AFS_PTHREAD_ENV */
4281                     continue;
4282                 }
4283             }
4284             if (vp->specialStatus) {
4285                 VGET_CTR_INC(V11);
4286                 *ec = vp->specialStatus;
4287             } else if (V_inService(vp) == 0 || V_blessed(vp) == 0) {
4288                 VGET_CTR_INC(V12);
4289                 *ec = VNOVOL;
4290             } else if (V_inUse(vp) == 0 || vp->goingOffline) {
4291                 VGET_CTR_INC(V13);
4292                 *ec = VOFFLINE;
4293             } else {
4294                 VGET_CTR_INC(V14);
4295             }
4296         }
4297         break;
4298     }
4299     VGET_CTR_INC(V15);
4300
4301 #ifdef AFS_DEMAND_ATTACH_FS
4302     /* if no error, bump nUsers */
4303     if (vp) {
4304         vp->nUsers++;
4305         VLRU_UpdateAccess_r(vp);
4306     }
4307     if (rvp) {
4308         VCancelReservation_r(rvp);
4309         rvp = NULL;
4310     }
4311     if (client_ec && !*client_ec) {
4312         *client_ec = *ec;
4313     }
4314 #else /* AFS_DEMAND_ATTACH_FS */
4315     /* if no error, bump nUsers */
4316     if (vp) {
4317         vp->nUsers++;
4318     }
4319     if (client_ec) {
4320         *client_ec = *ec;
4321     }
4322 #endif /* AFS_DEMAND_ATTACH_FS */
4323
4324  not_inited:
4325     opr_Assert(vp || *ec);
4326     return vp;
4327 }
4328
4329
4330 /***************************************************/
4331 /* Volume offline/detach routines                  */
4332 /***************************************************/
4333
4334 /* caller MUST hold a heavyweight ref on vp */
4335 #ifdef AFS_DEMAND_ATTACH_FS
4336 void
4337 VTakeOffline_r(Volume * vp)
4338 {
4339     Error error;
4340
4341     opr_Assert(vp->nUsers > 0);
4342     opr_Assert(programType == fileServer);
4343
4344     VCreateReservation_r(vp);
4345     VWaitExclusiveState_r(vp);
4346
4347     vp->goingOffline = 1;
4348     V_needsSalvaged(vp) = 1;
4349
4350     VRequestSalvage_r(&error, vp, SALVSYNC_ERROR, 0);
4351     VCancelReservation_r(vp);
4352 }
4353 #else /* AFS_DEMAND_ATTACH_FS */
4354 void
4355 VTakeOffline_r(Volume * vp)
4356 {
4357     opr_Assert(vp->nUsers > 0);
4358     opr_Assert(programType == fileServer);
4359
4360     vp->goingOffline = 1;
4361     V_needsSalvaged(vp) = 1;
4362 }
4363 #endif /* AFS_DEMAND_ATTACH_FS */
4364
4365 void
4366 VTakeOffline(Volume * vp)
4367 {
4368     VOL_LOCK;
4369     VTakeOffline_r(vp);
4370     VOL_UNLOCK;
4371 }
4372
4373 /**
4374  * force a volume offline.
4375  *
4376  * @param[in] vp     volume object pointer
4377  * @param[in] flags  flags (see note below)
4378  *
4379  * @note the flag VOL_FORCEOFF_NOUPDATE is a recursion control flag
4380  *       used when VUpdateVolume_r needs to call VForceOffline_r
4381  *       (which in turn would normally call VUpdateVolume_r)
4382  *
4383  * @see VUpdateVolume_r
4384  *
4385  * @pre VOL_LOCK must be held.
4386  *      for DAFS, caller must hold ref.
4387  *
4388  * @note for DAFS, it _is safe_ to call this function from an
4389  *       exclusive state
4390  *
4391  * @post needsSalvaged flag is set.
4392  *       for DAFS, salvage is requested.
4393  *       no further references to the volume through the volume
4394  *       package will be honored.
4395  *       all file descriptor and vnode caches are invalidated.
4396  *
4397  * @warning this is a heavy-handed interface.  it results in
4398  *          a volume going offline regardless of the current
4399  *          reference count state.
4400  *
4401  * @internal  volume package internal use only
4402  */
4403 void
4404 VForceOffline_r(Volume * vp, int flags)
4405 {
4406     Error error;
4407     if (!V_inUse(vp)) {
4408 #ifdef AFS_DEMAND_ATTACH_FS
4409         VChangeState_r(vp, VOL_STATE_ERROR);
4410 #endif
4411         return;
4412     }
4413
4414     strcpy(V_offlineMessage(vp),
4415            "Forced offline due to internal error: volume needs to be salvaged");
4416     Log("Volume %" AFS_VOLID_FMT " forced offline:  it needs salvaging!\n", afs_printable_VolumeId_lu(V_id(vp)));
4417
4418     V_inUse(vp) = 0;
4419     vp->goingOffline = 0;
4420     V_needsSalvaged(vp) = 1;
4421     if (!(flags & VOL_FORCEOFF_NOUPDATE)) {
4422         VUpdateVolume_r(&error, vp, VOL_UPDATE_NOFORCEOFF);
4423     }
4424
4425 #ifdef AFS_DEMAND_ATTACH_FS
4426     VRequestSalvage_r(&error, vp, SALVSYNC_ERROR, 0 /*flags*/);
4427 #endif /* AFS_DEMAND_ATTACH_FS */
4428
4429 #ifdef AFS_PTHREAD_ENV
4430     opr_cv_broadcast(&vol_put_volume_cond);
4431 #else /* AFS_PTHREAD_ENV */
4432     LWP_NoYieldSignal(VPutVolume);
4433 #endif /* AFS_PTHREAD_ENV */
4434
4435     VReleaseVolumeHandles_r(vp);
4436 }
4437
4438 /**
4439  * force a volume offline.
4440  *
4441  * @param[in] vp  volume object pointer
4442  *
4443  * @see VForceOffline_r
4444  */
4445 void
4446 VForceOffline(Volume * vp)
4447 {
4448     VOL_LOCK;
4449     VForceOffline_r(vp, 0);
4450     VOL_UNLOCK;
4451 }
4452
4453 /**
4454  * Iterate over the RX calls associated with a volume, and interrupt them.
4455  *
4456  * @param[in] vp The volume whose RX calls we want to scan
4457  *
4458  * @pre VOL_LOCK held
4459  */
4460 static void
4461 VScanCalls_r(struct Volume *vp)
4462 {
4463     struct VCallByVol *cbv, *ncbv;
4464     afs_int32 err;
4465 #ifdef AFS_DEMAND_ATTACH_FS
4466     VolState state_save;
4467 #endif
4468
4469     if (queue_IsEmpty(&vp->rx_call_list))
4470         return; /* no calls to interrupt */
4471     if (!vol_opts.interrupt_rxcall)
4472         return; /* we have no function with which to interrupt calls */
4473     err = VIsGoingOffline_r(vp);
4474     if (!err)
4475         return; /* we're not going offline anymore */
4476
4477 #ifdef AFS_DEMAND_ATTACH_FS
4478     VWaitExclusiveState_r(vp);
4479     state_save = VChangeState_r(vp, VOL_STATE_SCANNING_RXCALLS);
4480     VOL_UNLOCK;
4481 #endif /* AFS_DEMAND_ATTACH_FS */
4482
4483     for(queue_Scan(&vp->rx_call_list, cbv, ncbv, VCallByVol)) {
4484         if (LogLevel > 0) {
4485             struct rx_peer *peer;
4486             char hoststr[16];
4487             peer = rx_PeerOf(rx_ConnectionOf(cbv->call));
4488
4489             Log("Offlining volume %" AFS_VOLID_FMT " while client %s:%u is trying to read "
4490                 "from it; kicking client off with error %ld\n",
4491                 afs_printable_VolumeId_lu(vp->hashid),
4492                 afs_inet_ntoa_r(rx_HostOf(peer), hoststr),
4493                 (unsigned) ntohs(rx_PortOf(peer)),
4494                 (long) err);
4495         }
4496         (*vol_opts.interrupt_rxcall) (cbv->call, err);
4497     }
4498
4499 #ifdef AFS_DEMAND_ATTACH_FS
4500     VOL_LOCK;
4501     VChangeState_r(vp, state_save);
4502 #endif /* AFS_DEMAND_ATTACH_FS */
4503 }
4504
4505 #ifdef AFS_DEMAND_ATTACH_FS
4506 /**
4507  * Wait for a vp to go offline.
4508  *
4509  * @param[out] ec 1 if a salvage on the volume has been requested and
4510  *                salvok == 0, 0 otherwise
4511  * @param[in] vp  The volume to wait for
4512  * @param[in] salvok  If 0, we return immediately with *ec = 1 if the volume
4513  *                    has been requested to salvage. Otherwise we keep waiting
4514  *                    until the volume has gone offline.
4515  *
4516  * @pre VOL_LOCK held
4517  * @pre caller holds a lightweight ref on vp
4518  *
4519  * @note DAFS only
4520  */
4521 static void
4522 VWaitForOfflineByVp_r(Error *ec, struct Volume *vp, int salvok)
4523 {
4524     struct timespec timeout_ts;
4525     const struct timespec *ts;
4526     int timedout = 0;
4527
4528     ts = VOfflineTimeout(&timeout_ts);
4529
4530     *ec = 0;
4531
4532     while (!VIsOfflineState(V_attachState(vp)) && !timedout) {
4533         if (!salvok && vp->salvage.requested) {
4534             *ec = 1;
4535             return;
4536         }
4537         VTimedWaitStateChange_r(vp, ts, &timedout);
4538     }
4539     if (!timedout) {
4540         /* we didn't time out, so the volume must be offline, so we're done */
4541         return;
4542     }
4543
4544     /* If we got here, we timed out waiting for the volume to go offline.
4545      * Kick off the accessing RX calls and wait again */
4546
4547     VScanCalls_r(vp);
4548
4549     while (!VIsOfflineState(V_attachState(vp))) {
4550         if (!salvok && vp->salvage.requested) {
4551             *ec = 1;
4552             return;
4553         }
4554
4555         VWaitStateChange_r(vp);
4556     }
4557 }
4558
4559 #else /* AFS_DEMAND_ATTACH_FS */
4560
4561 /**
4562  * Wait for a volume to go offline.
4563  *
4564  * @pre VOL_LOCK held
4565  *
4566  * @note non-DAFS only (for DAFS, use @see WaitForOfflineByVp_r)
4567  */
4568 static void
4569 VWaitForOffline_r(Error *ec, VolumeId volid)
4570 {
4571     struct Volume *vp;
4572     const struct timespec *ts;
4573 #ifdef AFS_PTHREAD_ENV
4574     struct timespec timeout_ts;
4575 #endif
4576
4577     ts = VOfflineTimeout(&timeout_ts);
4578
4579     vp = GetVolume(ec, NULL, volid, NULL, ts);
4580     if (!vp) {
4581         /* error occurred so bad that we can't even get a vp; we have no
4582          * information on the vol so we don't know whether to wait, so just
4583          * return */
4584         return;
4585     }
4586     if (!VIsGoingOffline_r(vp)) {
4587         /* volume is no longer going offline, so we're done */
4588         VPutVolume_r(vp);
4589         return;
4590     }
4591
4592     /* If we got here, we timed out waiting for the volume to go offline.
4593      * Kick off the accessing RX calls and wait again */
4594
4595     VScanCalls_r(vp);
4596     VPutVolume_r(vp);
4597     vp = NULL;
4598
4599     vp = VGetVolume_r(ec, volid);
4600     if (vp) {
4601         /* In case it was reattached... */
4602         VPutVolume_r(vp);
4603     }
4604 }
4605 #endif /* !AFS_DEMAND_ATTACH_FS */
4606
4607 /* The opposite of VAttachVolume.  The volume header is written to disk, with
4608    the inUse bit turned off.  A copy of the header is maintained in memory,
4609    however (which is why this is VOffline, not VDetach).
4610  */
4611 void
4612 VOffline_r(Volume * vp, char *message)
4613 {
4614     Error error;
4615 #ifndef AFS_DEMAND_ATTACH_FS
4616     VolumeId vid = V_id(vp);
4617 #endif
4618
4619     opr_Assert(programType != volumeUtility && programType != volumeServer);
4620     if (!V_inUse(vp)) {
4621         VPutVolume_r(vp);
4622         return;
4623     }
4624     if (V_offlineMessage(vp)[0] == '\0')
4625         strncpy(V_offlineMessage(vp), message, sizeof(V_offlineMessage(vp)));
4626     V_offlineMessage(vp)[sizeof(V_offlineMessage(vp)) - 1] = '\0';
4627
4628     vp->goingOffline = 1;
4629 #ifdef AFS_DEMAND_ATTACH_FS
4630     VChangeState_r(vp, VOL_STATE_GOING_OFFLINE);
4631     VCreateReservation_r(vp);
4632     VPutVolume_r(vp);
4633     VWaitForOfflineByVp_r(&error, vp, 1);
4634     VCancelReservation_r(vp);
4635 #else /* AFS_DEMAND_ATTACH_FS */
4636     VPutVolume_r(vp);
4637     VWaitForOffline_r(&error, vid);
4638 #endif /* AFS_DEMAND_ATTACH_FS */
4639 }
4640
4641 #ifdef AFS_DEMAND_ATTACH_FS
4642 /**
4643  * Take a volume offline in order to perform a volume operation.
4644  *
4645  * @param[inout] ec       address in which to store error code
4646  * @param[in]    vp       volume object pointer
4647  * @param[in]    message  volume offline status message
4648  *
4649  * @pre
4650  *    - VOL_LOCK is held
4651  *    - caller MUST hold a heavyweight ref on vp
4652  *
4653  * @post
4654  *    - volume is taken offline
4655  *    - if possible, volume operation is promoted to running state
4656  *    - on failure, *ec is set to nonzero
4657  *
4658  * @note Although this function does not return any value, it may
4659  *       still fail to promote our pending volume operation to
4660  *       a running state.  Any caller MUST check the value of *ec,
4661  *       and MUST NOT blindly assume success.
4662  *
4663  * @warning if the caller does not hold a lightweight ref on vp,
4664  *          then it MUST NOT reference vp after this function
4665  *          returns to the caller.
4666  *
4667  * @internal volume package internal use only
4668  */
4669 void
4670 VOfflineForVolOp_r(Error *ec, Volume *vp, char *message)
4671 {
4672     int salvok = 1;
4673     opr_Assert(vp->pending_vol_op);
4674     if (!V_inUse(vp)) {
4675         VPutVolume_r(vp);
4676         *ec = 1;
4677         return;
4678     }
4679     if (V_offlineMessage(vp)[0] == '\0')
4680         strncpy(V_offlineMessage(vp), message, sizeof(V_offlineMessage(vp)));
4681     V_offlineMessage(vp)[sizeof(V_offlineMessage(vp)) - 1] = '\0';
4682
4683     vp->goingOffline = 1;
4684     VChangeState_r(vp, VOL_STATE_GOING_OFFLINE);
4685     VCreateReservation_r(vp);
4686     VPutVolume_r(vp);
4687
4688     if (vp->pending_vol_op->com.programType != salvageServer) {
4689         /* do not give corrupted volumes to the volserver */
4690         salvok = 0;
4691     }
4692
4693     *ec = 0;
4694     VWaitForOfflineByVp_r(ec, vp, salvok);
4695
4696     VCancelReservation_r(vp);
4697 }
4698 #endif /* AFS_DEMAND_ATTACH_FS */
4699
4700 void
4701 VOffline(Volume * vp, char *message)
4702 {
4703     VOL_LOCK;
4704     VOffline_r(vp, message);
4705     VOL_UNLOCK;
4706 }
4707
4708 /* This gets used for the most part by utility routines that don't want
4709  * to keep all the volume headers around.  Generally, the file server won't
4710  * call this routine, because then the offline message in the volume header
4711  * (or other information) won't be available to clients. For NAMEI, also
4712  * close the file handles.  However, the fileserver does call this during
4713  * an attach following a volume operation.
4714  */
4715 void
4716 VDetachVolume_r(Error * ec, Volume * vp)
4717 {
4718 #ifdef FSSYNC_BUILD_CLIENT
4719     VolumeId volume;
4720     struct DiskPartition64 *tpartp;
4721     int notifyServer = 0;
4722     int  useDone = FSYNC_VOL_ON;
4723
4724     if (VCanUseFSSYNC()) {
4725         notifyServer = vp->needsPutBack;
4726         if (V_destroyMe(vp) == DESTROY_ME)
4727             useDone = FSYNC_VOL_LEAVE_OFF;
4728 # ifdef AFS_DEMAND_ATTACH_FS
4729         else if (!V_blessed(vp) || !V_inService(vp))
4730             useDone = FSYNC_VOL_LEAVE_OFF;
4731 # endif
4732     }
4733 # ifdef AFS_DEMAND_ATTACH_FS
4734     if (V_needsSalvaged(vp)) {
4735         notifyServer = 0;
4736         VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, 0);
4737     }
4738 # endif
4739     tpartp = vp->partition;
4740     volume = V_id(vp);
4741 #endif /* FSSYNC_BUILD_CLIENT */
4742
4743     *ec = 0;                    /* always "succeeds" */
4744     DeleteVolumeFromHashTable(vp);
4745     vp->shuttingDown = 1;
4746 #ifdef AFS_DEMAND_ATTACH_FS
4747     DeleteVolumeFromVByPList_r(vp);
4748     VLRU_Delete_r(vp);
4749     VChangeState_r(vp, VOL_STATE_SHUTTING_DOWN);
4750 #else
4751     if (programType != fileServer)
4752         V_inUse(vp) = 0;
4753 #endif /* AFS_DEMAND_ATTACH_FS */
4754     VPutVolume_r(vp);
4755     /* Will be detached sometime in the future--this is OK since volume is offline */
4756
4757     /* XXX the following code should really be moved to VCheckDetach() since the volume
4758      * is not technically detached until the refcounts reach zero
4759      */
4760 #ifdef FSSYNC_BUILD_CLIENT
4761     if (VCanUseFSSYNC() && notifyServer) {
4762         if (notifyServer == VOL_PUTBACK_DELETE) {
4763             /* Only send FSYNC_VOL_DONE if the volume was actually deleted.
4764              * volserver code will set needsPutBack to VOL_PUTBACK_DELETE
4765              * to signify a deleted volume. */
4766             useDone = FSYNC_VOL_DONE;
4767         }
4768         /*
4769          * Note:  The server is not notified in the case of a bogus volume
4770          * explicitly to make it possible to create a volume, do a partial
4771          * restore, then abort the operation without ever putting the volume
4772          * online.  This is essential in the case of a volume move operation
4773          * between two partitions on the same server.  In that case, there
4774          * would be two instances of the same volume, one of them bogus,
4775          * which the file server would attempt to put on line
4776          */
4777         FSYNC_VolOp(volume, tpartp->name, useDone, 0, NULL);
4778         /* XXX this code path is only hit by volume utilities, thus
4779          * V_BreakVolumeCallbacks will always be NULL.  if we really
4780          * want to break callbacks in this path we need to use FSYNC_VolOp() */
4781 #ifdef notdef
4782         /* Dettaching it so break all callbacks on it */
4783         if (V_BreakVolumeCallbacks) {
4784             Log("volume %u detached; breaking all call backs\n", volume);
4785             (*V_BreakVolumeCallbacks) (volume);
4786         }
4787 #endif
4788     }
4789 #endif /* FSSYNC_BUILD_CLIENT */
4790 }
4791
4792 void
4793 VDetachVolume(Error * ec, Volume * vp)
4794 {
4795     VOL_LOCK;
4796     VDetachVolume_r(ec, vp);
4797     VOL_UNLOCK;
4798 }
4799
4800
4801 /***************************************************/
4802 /* Volume fd/inode handle closing routines         */
4803 /***************************************************/
4804
4805 /* For VDetachVolume, we close all cached file descriptors, but keep
4806  * the Inode handles in case we need to read from a busy volume.
4807  */
4808 /* for demand attach, caller MUST hold ref count on vp */
4809 static void
4810 VCloseVolumeHandles_r(Volume * vp)
4811 {
4812 #ifdef AFS_DEMAND_ATTACH_FS
4813     VolState state_save;
4814
4815     state_save = VChangeState_r(vp, VOL_STATE_OFFLINING);
4816
4817     VOL_UNLOCK;
4818 #endif
4819
4820     DFlushVolume(vp->hashid);
4821
4822 #ifdef AFS_DEMAND_ATTACH_FS
4823     VOL_LOCK;
4824 #endif
4825
4826     /* DAFS: VCloseVnodeFiles_r drops the glock internally */
4827     VCloseVnodeFiles_r(vp);
4828
4829 #ifdef AFS_DEMAND_ATTACH_FS
4830     VOL_UNLOCK;
4831 #endif
4832
4833     /* Too time consuming and unnecessary for the volserver */
4834     if (programType == fileServer) {
4835         IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
4836         IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
4837         IH_CONDSYNC(vp->diskDataHandle);
4838 #ifdef AFS_NAMEI_ENV
4839         IH_CONDSYNC(vp->linkHandle);
4840 #endif /* AFS_NAMEI_ENV */
4841     }
4842
4843     IH_REALLYCLOSE(vp->vnodeIndex[vLarge].handle);
4844     IH_REALLYCLOSE(vp->vnodeIndex[vSmall].handle);
4845     IH_REALLYCLOSE(vp->diskDataHandle);
4846     IH_REALLYCLOSE(vp->linkHandle);
4847
4848 #ifdef AFS_DEMAND_ATTACH_FS
4849     if ((V_attachFlags(vp) & VOL_LOCKED)) {
4850         VUnlockVolume(vp);
4851     }
4852
4853     VOL_LOCK;
4854     VChangeState_r(vp, state_save);
4855 #endif
4856 }
4857
4858 /* For both VForceOffline and VOffline, we close all relevant handles.
4859  * For VOffline, if we re-attach the volume, the files may possible be
4860  * different than before.
4861  */
4862 /* for demand attach, caller MUST hold a ref count on vp */
4863 static void
4864 VReleaseVolumeHandles_r(Volume * vp)
4865 {
4866 #ifdef AFS_DEMAND_ATTACH_FS
4867     VolState state_save;
4868
4869     state_save = VChangeState_r(vp, VOL_STATE_DETACHING);
4870
4871     VOL_UNLOCK;
4872 #endif
4873
4874     DFlushVolume(vp->hashid);
4875
4876 #ifdef AFS_DEMAND_ATTACH_FS
4877     VOL_LOCK;
4878 #endif
4879
4880     VReleaseVnodeFiles_r(vp); /* DAFS: releases the glock internally */
4881
4882 #ifdef AFS_DEMAND_ATTACH_FS
4883     VOL_UNLOCK;
4884 #endif
4885
4886     /* Too time consuming and unnecessary for the volserver */
4887     if (programType == fileServer) {
4888         IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
4889         IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
4890         IH_CONDSYNC(vp->diskDataHandle);
4891 #ifdef AFS_NAMEI_ENV
4892         IH_CONDSYNC(vp->linkHandle);
4893 #endif /* AFS_NAMEI_ENV */
4894     }
4895
4896     IH_RELEASE(vp->vnodeIndex[vLarge].handle);
4897     IH_RELEASE(vp->vnodeIndex[vSmall].handle);
4898     IH_RELEASE(vp->diskDataHandle);
4899     IH_RELEASE(vp->linkHandle);
4900
4901 #ifdef AFS_DEMAND_ATTACH_FS
4902     if ((V_attachFlags(vp) & VOL_LOCKED)) {
4903         VUnlockVolume(vp);
4904     }
4905
4906     VOL_LOCK;
4907     VChangeState_r(vp, state_save);
4908 #endif
4909 }
4910
4911
4912 /***************************************************/
4913 /* Volume write and fsync routines                 */
4914 /***************************************************/
4915
4916 void
4917 VUpdateVolume_r(Error * ec, Volume * vp, int flags)
4918 {
4919 #ifdef AFS_DEMAND_ATTACH_FS
4920     VolState state_save;
4921
4922     if (flags & VOL_UPDATE_WAIT) {
4923         VCreateReservation_r(vp);
4924         VWaitExclusiveState_r(vp);
4925     }
4926 #endif
4927
4928     *ec = 0;
4929     if (programType == fileServer) {
4930         if (!V_inUse(vp)) {
4931             V_uniquifier(vp) = V_nextVnodeUnique(vp);
4932         } else {
4933             V_uniquifier(vp) =
4934                 V_nextVnodeUnique(vp) + VOLUME_UPDATE_UNIQUIFIER_BUMP;
4935             if (V_uniquifier(vp) < V_nextVnodeUnique(vp)) {
4936                 /* uniquifier rolled over; reset the counters */
4937                 V_nextVnodeUnique(vp) = 2;      /* 1 is reserved for the root vnode */
4938                 V_uniquifier(vp) =
4939                     V_nextVnodeUnique(vp) + VOLUME_UPDATE_UNIQUIFIER_BUMP;
4940             }
4941         }
4942     }
4943
4944 #ifdef AFS_DEMAND_ATTACH_FS
4945     state_save = VChangeState_r(vp, VOL_STATE_UPDATING);
4946     VOL_UNLOCK;
4947 #endif
4948
4949     WriteVolumeHeader_r(ec, vp);
4950
4951 #ifdef AFS_DEMAND_ATTACH_FS
4952     VOL_LOCK;
4953     VChangeState_r(vp, state_save);
4954     if (flags & VOL_UPDATE_WAIT) {
4955         VCancelReservation_r(vp);
4956     }
4957 #endif
4958
4959     if (*ec) {
4960         Log("VUpdateVolume: error updating volume header, volume %" AFS_VOLID_FMT " (%s)\n",
4961             afs_printable_VolumeId_lu(V_id(vp)), V_name(vp));
4962         /* try to update on-disk header,
4963          * while preventing infinite recursion */
4964         if (!(flags & VOL_UPDATE_NOFORCEOFF)) {
4965             VForceOffline_r(vp, VOL_FORCEOFF_NOUPDATE);
4966         }
4967     }
4968 }
4969
4970 void
4971 VUpdateVolume(Error * ec, Volume * vp)
4972 {
4973     VOL_LOCK;
4974     VUpdateVolume_r(ec, vp, VOL_UPDATE_WAIT);
4975     VOL_UNLOCK;
4976 }
4977
4978 void
4979 VSyncVolume_r(Error * ec, Volume * vp, int flags)
4980 {
4981     FdHandle_t *fdP;
4982     int code;
4983 #ifdef AFS_DEMAND_ATTACH_FS
4984     VolState state_save;
4985 #endif
4986
4987     if (flags & VOL_SYNC_WAIT) {
4988         VUpdateVolume_r(ec, vp, VOL_UPDATE_WAIT);
4989     } else {
4990         VUpdateVolume_r(ec, vp, 0);
4991     }
4992     if (!*ec) {
4993 #ifdef AFS_DEMAND_ATTACH_FS
4994         state_save = VChangeState_r(vp, VOL_STATE_UPDATING);
4995         VOL_UNLOCK;
4996 #endif
4997         fdP = IH_OPEN(V_diskDataHandle(vp));
4998         opr_Assert(fdP != NULL);
4999         code = FDH_SYNC(fdP);
5000         opr_Assert(code == 0);
5001         FDH_CLOSE(fdP);
5002 #ifdef AFS_DEMAND_ATTACH_FS
5003         VOL_LOCK;
5004         VChangeState_r(vp, state_save);
5005 #endif
5006     }
5007 }
5008
5009 void
5010 VSyncVolume(Error * ec, Volume * vp)
5011 {
5012     VOL_LOCK;
5013     VSyncVolume_r(ec, vp, VOL_SYNC_WAIT);
5014     VOL_UNLOCK;
5015 }
5016
5017
5018 /***************************************************/
5019 /* Volume dealloaction routines                    */
5020 /***************************************************/
5021
5022 #ifdef AFS_DEMAND_ATTACH_FS
5023 static void
5024 FreeVolume(Volume * vp)
5025 {
5026     /* free the heap space, iff it's safe.
5027      * otherwise, pull it out of the hash table, so it
5028      * will get deallocated when all refs to it go away */
5029     if (!VCheckFree(vp)) {
5030         DeleteVolumeFromHashTable(vp);
5031         DeleteVolumeFromVByPList_r(vp);
5032
5033         /* make sure we invalidate the header cache entry */
5034         FreeVolumeHeader(vp);
5035     }
5036 }
5037 #endif /* AFS_DEMAND_ATTACH_FS */
5038
5039 static void
5040 ReallyFreeVolume(Volume * vp)
5041 {
5042     int i;
5043     if (!vp)
5044         return;
5045 #ifdef AFS_DEMAND_ATTACH_FS
5046     /* debug */
5047     VChangeState_r(vp, VOL_STATE_FREED);
5048     if (vp->pending_vol_op)
5049         free(vp->pending_vol_op);
5050 #endif /* AFS_DEMAND_ATTACH_FS */
5051     for (i = 0; i < nVNODECLASSES; i++)
5052         if (vp->vnodeIndex[i].bitmap)
5053             free(vp->vnodeIndex[i].bitmap);
5054     FreeVolumeHeader(vp);
5055 #ifndef AFS_DEMAND_ATTACH_FS
5056     DeleteVolumeFromHashTable(vp);
5057 #endif /* AFS_DEMAND_ATTACH_FS */
5058     free(vp);
5059 }
5060
5061 /* check to see if we should shutdown this volume
5062  * returns 1 if volume was freed, 0 otherwise */
5063 #ifdef AFS_DEMAND_ATTACH_FS
5064 static int
5065 VCheckDetach(Volume * vp)
5066 {
5067     int ret = 0;
5068     Error ec = 0;
5069
5070     if (vp->nUsers || vp->nWaiters)
5071         return ret;
5072
5073     if (vp->shuttingDown) {
5074         ret = 1;
5075         if ((programType != fileServer) &&
5076             (V_inUse(vp) == programType) &&
5077             ((V_checkoutMode(vp) == V_VOLUPD) ||
5078              (V_checkoutMode(vp) == V_SECRETLY) ||
5079              ((V_checkoutMode(vp) == V_CLONE) &&
5080               (VolumeWriteable(vp))))) {
5081             V_inUse(vp) = 0;
5082             VUpdateVolume_r(&ec, vp, VOL_UPDATE_NOFORCEOFF);
5083             if (ec) {
5084                 Log("VCheckDetach: volume header update for volume %" AFS_VOLID_FMT " "
5085                     "failed with errno %d\n", afs_printable_VolumeId_lu(vp->hashid), errno);
5086             }
5087         }
5088         VReleaseVolumeHandles_r(vp);
5089         VCheckSalvage(vp);
5090         ReallyFreeVolume(vp);
5091         if (programType == fileServer) {
5092             opr_cv_broadcast(&vol_put_volume_cond);
5093         }
5094     }
5095     return ret;
5096 }
5097 #else /* AFS_DEMAND_ATTACH_FS */
5098 static int
5099 VCheckDetach(Volume * vp)
5100 {
5101     int ret = 0;
5102     Error ec = 0;
5103
5104     if (vp->nUsers)
5105         return ret;
5106
5107     if (vp->shuttingDown) {
5108         ret = 1;
5109         if ((programType != fileServer) &&
5110             (V_inUse(vp) == programType) &&
5111             ((V_checkoutMode(vp) == V_VOLUPD) ||
5112              (V_checkoutMode(vp) == V_SECRETLY) ||
5113              ((V_checkoutMode(vp) == V_CLONE) &&
5114               (VolumeWriteable(vp))))) {
5115             V_inUse(vp) = 0;
5116             VUpdateVolume_r(&ec, vp, VOL_UPDATE_NOFORCEOFF);
5117             if (ec) {
5118                 Log("VCheckDetach: volume header update for volume %" AFS_VOLID_FMT " failed with errno %d\n",
5119                     afs_printable_VolumeId_lu(vp->hashid), errno);
5120             }
5121         }
5122         VReleaseVolumeHandles_r(vp);
5123         ReallyFreeVolume(vp);
5124         if (programType == fileServer) {
5125 #if defined(AFS_PTHREAD_ENV)
5126             opr_cv_broadcast(&vol_put_volume_cond);
5127 #else /* AFS_PTHREAD_ENV */
5128             LWP_NoYieldSignal(VPutVolume);
5129 #endif /* AFS_PTHREAD_ENV */
5130         }
5131     }
5132     return ret;
5133 }
5134 #endif /* AFS_DEMAND_ATTACH_FS */
5135
5136 /* check to see if we should offline this volume
5137  * return 1 if volume went offline, 0 otherwise */
5138 #ifdef AFS_DEMAND_ATTACH_FS
5139 static int
5140 VCheckOffline(Volume * vp)
5141 {
5142     int ret = 0;
5143
5144     if (vp->goingOffline && !vp->nUsers) {
5145         Error error;
5146         opr_Assert(programType == fileServer);
5147         opr_Assert((V_attachState(vp) != VOL_STATE_ATTACHED) &&
5148                (V_attachState(vp) != VOL_STATE_FREED) &&
5149                (V_attachState(vp) != VOL_STATE_PREATTACHED) &&
5150                (V_attachState(vp) != VOL_STATE_UNATTACHED) &&
5151                (V_attachState(vp) != VOL_STATE_DELETED));
5152
5153         /* valid states:
5154          *
5155          * VOL_STATE_GOING_OFFLINE
5156          * VOL_STATE_SHUTTING_DOWN
5157          * VIsErrorState(V_attachState(vp))
5158          * VIsExclusiveState(V_attachState(vp))
5159          */
5160
5161         VCreateReservation_r(vp);
5162         VChangeState_r(vp, VOL_STATE_OFFLINING);
5163
5164         ret = 1;
5165         /* must clear the goingOffline flag before we drop the glock */
5166         vp->goingOffline = 0;
5167         V_inUse(vp) = 0;
5168
5169         VLRU_Delete_r(vp);
5170
5171         /* perform async operations */
5172         VUpdateVolume_r(&error, vp, 0);
5173         VCloseVolumeHandles_r(vp);
5174
5175         if (LogLevel) {
5176             if (V_offlineMessage(vp)[0]) {
5177                 Log("VOffline: Volume %lu (%s) is now offline (%s)\n",
5178                     afs_printable_uint32_lu(V_id(vp)), V_name(vp),
5179                     V_offlineMessage(vp));
5180             } else {
5181                 Log("VOffline: Volume %lu (%s) is now offline\n",
5182                     afs_printable_uint32_lu(V_id(vp)), V_name(vp));
5183             }
5184         }
5185
5186         /* invalidate the volume header cache entry */
5187         FreeVolumeHeader(vp);
5188
5189         /* if nothing changed state to error or salvaging,
5190          * drop state to unattached */
5191         if (!VIsErrorState(V_attachState(vp))) {
5192             VChangeState_r(vp, VOL_STATE_UNATTACHED);
5193         }
5194         VCancelReservation_r(vp);
5195         /* no usage of vp is safe beyond this point */
5196     }
5197     return ret;
5198 }
5199 #else /* AFS_DEMAND_ATTACH_FS */
5200 static int
5201 VCheckOffline(Volume * vp)
5202 {
5203     int ret = 0;
5204
5205     if (vp->goingOffline && !vp->nUsers) {
5206         Error error;
5207         opr_Assert(programType == fileServer);
5208
5209         ret = 1;
5210         vp->goingOffline = 0;
5211         V_inUse(vp) = 0;
5212         VUpdateVolume_r(&error, vp, 0);
5213         VCloseVolumeHandles_r(vp);
5214         if (LogLevel) {
5215             if (V_offlineMessage(vp)[0]) {
5216                 Log("VOffline: Volume %lu (%s) is now offline (%s)\n",
5217                     afs_printable_uint32_lu(V_id(vp)), V_name(vp),
5218                     V_offlineMessage(vp));
5219             } else {
5220                 Log("VOffline: Volume %lu (%s) is now offline\n",
5221                     afs_printable_uint32_lu(V_id(vp)), V_name(vp));
5222             }
5223         }
5224         FreeVolumeHeader(vp);
5225 #ifdef AFS_PTHREAD_ENV
5226         opr_cv_broadcast(&vol_put_volume_cond);
5227 #else /* AFS_PTHREAD_ENV */
5228         LWP_NoYieldSignal(VPutVolume);
5229 #endif /* AFS_PTHREAD_ENV */
5230     }
5231     return ret;
5232 }
5233 #endif /* AFS_DEMAND_ATTACH_FS */
5234
5235 /***************************************************/
5236 /* demand attach fs ref counting routines          */
5237 /***************************************************/
5238
5239 #ifdef AFS_DEMAND_ATTACH_FS
5240 /* the following two functions handle reference counting for
5241  * asynchronous operations on volume structs.
5242  *
5243  * their purpose is to prevent a VDetachVolume or VShutdown
5244  * from free()ing the Volume struct during an async i/o op */
5245
5246 /* register with the async volume op ref counter */
5247 /* VCreateReservation_r moved into inline code header because it
5248  * is now needed in vnode.c -- tkeiser 11/20/2007
5249  */
5250
5251 /**
5252  * decrement volume-package internal refcount.
5253  *
5254  * @param vp  volume object pointer
5255  *
5256  * @internal volume package internal use only
5257  *
5258  * @pre
5259  *    @arg VOL_LOCK is held
5260  *    @arg lightweight refcount held
5261  *
5262  * @post volume waiters refcount is decremented; volume may
5263  *       have been deallocated/shutdown/offlined/salvaged/
5264  *       whatever during the process
5265  *
5266  * @warning once you have tossed your last reference (you can acquire
5267  *          lightweight refs recursively) it is NOT SAFE to reference
5268  *          a volume object pointer ever again
5269  *
5270  * @see VCreateReservation_r
5271  *
5272  * @note DEMAND_ATTACH_FS only
5273  */
5274 void
5275 VCancelReservation_r(Volume * vp)
5276 {
5277     opr_Verify(--vp->nWaiters >= 0);
5278     if (vp->nWaiters == 0) {
5279         VCheckOffline(vp);
5280         if (!VCheckDetach(vp)) {
5281             VCheckSalvage(vp);
5282             VCheckFree(vp);
5283         }
5284     }
5285 }
5286
5287 /* check to see if we should free this volume now
5288  * return 1 if volume was freed, 0 otherwise */
5289 static int
5290 VCheckFree(Volume * vp)
5291 {
5292     int ret = 0;
5293     if ((vp->nUsers == 0) &&
5294         (vp->nWaiters == 0) &&
5295         !(V_attachFlags(vp) & (VOL_IN_HASH |
5296                                VOL_ON_VBYP_LIST |
5297                                VOL_IS_BUSY |
5298                                VOL_ON_VLRU))) {
5299         ReallyFreeVolume(vp);
5300         ret = 1;
5301     }
5302     return ret;
5303 }
5304 #endif /* AFS_DEMAND_ATTACH_FS */
5305
5306
5307 /***************************************************/
5308 /* online volume operations routines               */
5309 /***************************************************/
5310
5311 #ifdef AFS_DEMAND_ATTACH_FS
5312 /**
5313  * register a volume operation on a given volume.
5314  *
5315  * @param[in] vp       volume object
5316  * @param[in] vopinfo  volume operation info object
5317  *
5318  * @pre VOL_LOCK is held
5319  *
5320  * @post volume operation info object attached to volume object.
5321  *       volume operation statistics updated.
5322  *
5323  * @note by "attached" we mean a copy of the passed in object is made
5324  *
5325  * @internal volume package internal use only
5326  */
5327 int
5328 VRegisterVolOp_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
5329 {
5330     FSSYNC_VolOp_info * info;
5331
5332     /* attach a vol op info node to the volume struct */
5333     info = malloc(sizeof(FSSYNC_VolOp_info));
5334     opr_Assert(info != NULL);
5335     memcpy(info, vopinfo, sizeof(FSSYNC_VolOp_info));
5336     vp->pending_vol_op = info;
5337
5338     /* update stats */
5339     vp->stats.last_vol_op = FT_ApproxTime();
5340     vp->stats.vol_ops++;
5341     IncUInt64(&VStats.vol_ops);
5342
5343     return 0;
5344 }
5345
5346 /**
5347  * deregister the volume operation attached to this volume.
5348  *
5349  * @param[in] vp  volume object pointer
5350  *
5351  * @pre VOL_LOCK is held
5352  *
5353  * @post the volume operation info object is detached from the volume object
5354  *
5355  * @internal volume package internal use only
5356  */
5357 int
5358 VDeregisterVolOp_r(Volume * vp)
5359 {
5360     if (vp->pending_vol_op) {
5361         free(vp->pending_vol_op);
5362         vp->pending_vol_op = NULL;
5363     }
5364     return 0;
5365 }
5366 #endif /* AFS_DEMAND_ATTACH_FS */
5367
5368 /**
5369  * determine whether it is safe to leave a volume online during
5370  * the volume operation described by the vopinfo object.
5371  *
5372  * @param[in] vp        volume object
5373  * @param[in] vopinfo   volume operation info object
5374  *
5375  * @return whether it is safe to leave volume online
5376  *    @retval 0  it is NOT SAFE to leave the volume online
5377  *    @retval 1  it is safe to leave the volume online during the operation
5378  *
5379  * @pre
5380  *    @arg VOL_LOCK is held
5381  *    @arg disk header attached to vp (heavyweight ref on vp will guarantee
5382  *         this condition is met)
5383  *
5384  * @internal volume package internal use only
5385  */
5386 int
5387 VVolOpLeaveOnline_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
5388 {
5389     return (vopinfo->vol_op_state == FSSYNC_VolOpRunningOnline ||
5390             (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
5391             (vopinfo->com.reason == V_READONLY ||
5392              (!VolumeWriteable(vp) &&
5393               (vopinfo->com.reason == V_CLONE ||
5394                vopinfo->com.reason == V_DUMP)))));
5395 }
5396
5397 /**
5398  * same as VVolOpLeaveOnline_r, but does not require a volume with an attached
5399  * header.
5400  *
5401  * @param[in] vp        volume object
5402  * @param[in] vopinfo   volume operation info object
5403  *
5404  * @return whether it is safe to leave volume online
5405  *    @retval 0  it is NOT SAFE to leave the volume online
5406  *    @retval 1  it is safe to leave the volume online during the operation
5407  *    @retval -1 unsure; volume header is required in order to know whether or
5408  *               not is is safe to leave the volume online
5409  *
5410  * @pre VOL_LOCK is held
5411  *
5412  * @internal volume package internal use only
5413  */
5414 int
5415 VVolOpLeaveOnlineNoHeader_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
5416 {
5417     /* follow the logic in VVolOpLeaveOnline_r; this is the same, except
5418      * assume that we don't know VolumeWriteable; return -1 if the answer
5419      * depends on VolumeWriteable */
5420
5421     if (vopinfo->vol_op_state == FSSYNC_VolOpRunningOnline) {
5422         return 1;
5423     }
5424     if (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
5425         vopinfo->com.reason == V_READONLY) {
5426
5427         return 1;
5428     }
5429     if (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
5430         (vopinfo->com.reason == V_CLONE ||
5431          vopinfo->com.reason == V_DUMP)) {
5432
5433         /* must know VolumeWriteable */
5434         return -1;
5435     }
5436     return 0;
5437 }
5438
5439 /**
5440  * determine whether VBUSY should be set during this volume operation.
5441  *
5442  * @param[in] vp        volume object
5443  * @param[in] vopinfo   volume operation info object
5444  *
5445  * @return whether VBUSY should be set
5446  *   @retval 0  VBUSY does NOT need to be set
5447  *   @retval 1  VBUSY SHOULD be set
5448  *
5449  * @pre VOL_LOCK is held
5450  *
5451  * @internal volume package internal use only
5452  */
5453 int
5454 VVolOpSetVBusy_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
5455 {
5456     return ((vopinfo->com.command == FSYNC_VOL_OFF &&
5457             vopinfo->com.reason == FSYNC_SALVAGE) ||
5458             (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
5459             (vopinfo->com.reason == V_CLONE ||
5460              vopinfo->com.reason == V_DUMP)));
5461 }
5462
5463
5464 /***************************************************/
5465 /* online salvager routines                        */
5466 /***************************************************/
5467 #if defined(AFS_DEMAND_ATTACH_FS)
5468
5469 /**
5470  * offline a volume to let it be salvaged.
5471  *
5472  * @param[in] vp  Volume to offline
5473  *
5474  * @return whether we offlined the volume successfully
5475  *  @retval 0 volume was not offlined
5476  *  @retval 1 volume is now offline
5477  *
5478  * @note This is similar to VCheckOffline, but slightly different. We do not
5479  *       deal with vp->goingOffline, and we try to avoid touching the volume
5480  *       header except just to set needsSalvaged
5481  *
5482  * @pre VOL_LOCK held
5483  * @pre vp->nUsers == 0
5484  * @pre V_attachState(vp) == VOL_STATE_SALVAGE_REQ
5485  */
5486 static int
5487 VOfflineForSalvage_r(struct Volume *vp)
5488 {
5489     Error error;
5490
5491     VCreateReservation_r(vp);
5492     VWaitExclusiveState_r(vp);
5493
5494     if (vp->nUsers || V_attachState(vp) == VOL_STATE_SALVAGING) {
5495         /* Someone's using the volume, or someone got to scheduling the salvage
5496          * before us. I don't think either of these should be possible, as we
5497          * should gain no new heavyweight references while we're trying to
5498          * salvage, but just to be sure... */
5499         VCancelReservation_r(vp);
5500         return 0;
5501     }
5502
5503     VChangeState_r(vp, VOL_STATE_OFFLINING);
5504
5505     VLRU_Delete_r(vp);
5506     if (vp->header) {
5507         V_needsSalvaged(vp) = 1;
5508         /* ignore error; updating needsSalvaged is just best effort */
5509         VUpdateVolume_r(&error, vp, VOL_UPDATE_NOFORCEOFF);
5510     }
5511     VCloseVolumeHandles_r(vp);
5512
5513     FreeVolumeHeader(vp);
5514
5515     /* volume has been effectively offlined; we can mark it in the SALVAGING
5516      * state now, which lets FSSYNC give it away */
5517     VChangeState_r(vp, VOL_STATE_SALVAGING);
5518
5519     VCancelReservation_r(vp);
5520
5521     return 1;
5522 }
5523
5524 /**
5525  * check whether a salvage needs to be performed on this volume.
5526  *
5527  * @param[in] vp   pointer to volume object
5528  *
5529  * @return status code
5530  *    @retval VCHECK_SALVAGE_OK (0)         no pending salvage
5531  *    @retval VCHECK_SALVAGE_SCHEDULED (1)  salvage has been scheduled
5532  *    @retval VCHECK_SALVAGE_ASYNC (2)      salvage being scheduled
5533  *    @retval VCHECK_SALVAGE_DENIED (3)     salvage not scheduled; denied
5534  *    @retval VCHECK_SALVAGE_FAIL (4)       salvage not scheduled; failed
5535  *
5536  * @pre VOL_LOCK is held
5537  *
5538  * @post if salvage request flag is set and nUsers and nWaiters are zero,
5539  *       then a salvage will be requested
5540  *
5541  * @note this is one of the event handlers called by VCancelReservation_r
5542  *
5543  * @note the caller must check if the volume needs to be freed after calling
5544  *       this; the volume may not have any references or be on any lists after
5545  *       we return, and we do not free it
5546  *
5547  * @see VCancelReservation_r
5548  *
5549  * @internal volume package internal use only.
5550  */
5551 static int
5552 VCheckSalvage(Volume * vp)
5553 {
5554     int ret = VCHECK_SALVAGE_OK;
5555
5556 #if defined(SALVSYNC_BUILD_CLIENT) || defined(FSSYNC_BUILD_CLIENT)
5557     if (!vp->salvage.requested) {
5558         return VCHECK_SALVAGE_OK;
5559     }
5560     if (vp->nUsers) {
5561         return VCHECK_SALVAGE_ASYNC;
5562     }
5563
5564     /* prevent recursion; some of the code below creates and removes
5565      * lightweight refs, which can call VCheckSalvage */
5566     if (vp->salvage.scheduling) {
5567         return VCHECK_SALVAGE_ASYNC;
5568     }
5569     vp->salvage.scheduling = 1;
5570
5571     if (V_attachState(vp) == VOL_STATE_SALVAGE_REQ) {
5572         if (!VOfflineForSalvage_r(vp)) {
5573             vp->salvage.scheduling = 0;
5574             return VCHECK_SALVAGE_FAIL;
5575         }
5576     }
5577
5578     if (vp->salvage.requested) {
5579         ret = VScheduleSalvage_r(vp);
5580     }
5581     vp->salvage.scheduling = 0;
5582 #endif /* SALVSYNC_BUILD_CLIENT || FSSYNC_BUILD_CLIENT */
5583     return ret;
5584 }
5585
5586 /**
5587  * request volume salvage.
5588  *
5589  * @param[out] ec      computed client error code
5590  * @param[in]  vp      volume object pointer
5591  * @param[in]  reason  reason code (passed to salvageserver via SALVSYNC)
5592  * @param[in]  flags   see flags note below
5593  *
5594  * @note flags:
5595  *       VOL_SALVAGE_NO_OFFLINE do not need to wait to offline the volume; it has
5596  *                              not been fully attached
5597  *
5598  * @pre VOL_LOCK is held.
5599  *
5600  * @post volume state is changed.
5601  *       for fileserver, salvage will be requested once refcount reaches zero.
5602  *
5603  * @return operation status code
5604  *   @retval 0  volume salvage will occur
5605  *   @retval 1  volume salvage could not be scheduled
5606  *
5607  * @note DAFS only
5608  *
5609  * @note in the fileserver, this call does not synchronously schedule a volume
5610  *       salvage. rather, it sets volume state so that when volume refcounts
5611  *       reach zero, a volume salvage will occur. by "refcounts", we mean both
5612  *       nUsers and nWaiters must be zero.
5613  *
5614  * @internal volume package internal use only.
5615  */
5616 int
5617 VRequestSalvage_r(Error * ec, Volume * vp, int reason, int flags)
5618 {
5619     int code = 0;
5620     /*
5621      * for DAFS volume utilities that are not supposed to schedule salvages,
5622      * just transition to error state instead
5623      */
5624     if (!VCanScheduleSalvage()) {
5625         VChangeState_r(vp, VOL_STATE_ERROR);
5626         *ec = VSALVAGE;
5627         return 1;
5628     }
5629
5630     if (programType != fileServer && !VCanUseFSSYNC()) {
5631         VChangeState_r(vp, VOL_STATE_ERROR);
5632         *ec = VSALVAGE;
5633         return 1;
5634     }
5635
5636     if (!vp->salvage.requested) {
5637         vp->salvage.requested = 1;
5638         vp->salvage.reason = reason;
5639         vp->stats.last_salvage = FT_ApproxTime();
5640
5641         /* Note that it is not possible for us to reach this point if a
5642          * salvage is already running on this volume (even if the fileserver
5643          * was restarted during the salvage). If a salvage were running, the
5644          * salvager would have write-locked the volume header file, so when
5645          * we tried to lock the volume header, the lock would have failed,
5646          * and we would have failed during attachment prior to calling
5647          * VRequestSalvage. So we know that we can schedule salvages without
5648          * fear of a salvage already running for this volume. */
5649
5650         if (vp->stats.salvages < SALVAGE_COUNT_MAX) {
5651
5652             /* if we don't need to offline the volume, we can go directly
5653              * to SALVAGING. SALVAGING says the volume is offline and is
5654              * either salvaging or ready to be handed to the salvager.
5655              * SALVAGE_REQ says that we want to salvage the volume, but we
5656              * are waiting for it to go offline first. */
5657             if (flags & VOL_SALVAGE_NO_OFFLINE) {
5658                 VChangeState_r(vp, VOL_STATE_SALVAGING);
5659             } else {
5660                 VChangeState_r(vp, VOL_STATE_SALVAGE_REQ);
5661                 if (vp->nUsers == 0) {
5662                     /* normally VOfflineForSalvage_r would be called from
5663                      * PutVolume et al when nUsers reaches 0, but if
5664                      * it's already 0, just do it ourselves, since PutVolume
5665                      * isn't going to get called */
5666                     VOfflineForSalvage_r(vp);
5667                 }
5668             }
5669             /* If we are non-fileserver, we're telling the fileserver to
5670              * salvage the vol, so we don't need to give it back separately. */
5671             vp->needsPutBack = 0;
5672
5673             *ec = VSALVAGING;
5674         } else {
5675             Log("VRequestSalvage: volume %" AFS_VOLID_FMT " online salvaged too many times; forced offline.\n", afs_printable_VolumeId_lu(vp->hashid));
5676
5677             /* make sure neither VScheduleSalvage_r nor
5678              * VUpdateSalvagePriority_r try to schedule another salvage */
5679             vp->salvage.requested = vp->salvage.scheduled = 0;
5680
5681             VChangeState_r(vp, VOL_STATE_ERROR);
5682             *ec = VSALVAGE;
5683             code = 1;
5684         }
5685         if ((flags & VOL_SALVAGE_NO_OFFLINE)) {
5686             /* Here, we free the header for the volume, but make sure to only
5687              * do this if VOL_SALVAGE_NO_OFFLINE is specified. The reason for
5688              * this requires a bit of explanation.
5689              *
5690              * Normally, the volume header will be freed when the volume goes
5691              * goes offline. However, if VOL_SALVAGE_NO_OFFLINE has been
5692              * specified, the volume was in the process of being attached when
5693              * we discovered that it needed salvaging. Thus, the volume will
5694              * never go offline, since it never went fully online in the first
5695              * place. Specifically, we do not call VOfflineForSalvage_r above,
5696              * and we never get rid of the volume via VPutVolume_r; the volume
5697              * has not been initialized enough for those to work.
5698              *
5699              * So instead, explicitly free the volume header here. If we do not
5700              * do this, we are wasting a header that some other volume could be
5701              * using, since the header remains attached to the volume. Also if
5702              * we do not free the header here, we end up with a volume where
5703              * nUsers == 0, but the volume has a header that is not on the
5704              * header LRU. Some code expects that all nUsers == 0 volumes have
5705              * their header on the header LRU (or have no header).
5706              *
5707              * Also note that we must not free the volume header here if
5708              * VOL_SALVAGE_NO_OFFLINE is not set. Since, if
5709              * VOL_SALVAGE_NO_OFFLINE is not set, someone else may have a
5710              * reference to this volume, and they assume they can use the
5711              * volume's header. If we free the volume out from under them, they
5712              * can easily segfault.
5713              */
5714             FreeVolumeHeader(vp);
5715         }
5716     }
5717     return code;
5718 }
5719
5720 /**
5721  * update salvageserver scheduling priority for a volume.
5722  *
5723  * @param[in] vp  pointer to volume object
5724  *
5725  * @return operation status
5726  *   @retval 0  success
5727  *   @retval 1  request denied, or SALVSYNC communications failure
5728  *
5729  * @pre VOL_LOCK is held.
5730  *
5731  * @post in-core salvage priority counter is incremented.  if at least
5732  *       SALVAGE_PRIO_UPDATE_INTERVAL seconds have elapsed since the
5733  *       last SALVSYNC_RAISEPRIO request, we contact the salvageserver
5734  *       to update its priority queue.  if no salvage is scheduled,
5735  *       this function is a no-op.
5736  *
5737  * @note DAFS fileserver only
5738  *
5739  * @note this should be called whenever a VGetVolume fails due to a
5740  *       pending salvage request
5741  *
5742  * @todo should set exclusive state and drop glock around salvsync call
5743  *
5744  * @internal volume package internal use only.
5745  */
5746 int
5747 VUpdateSalvagePriority_r(Volume * vp)
5748 {
5749     int ret=0;
5750
5751 #ifdef SALVSYNC_BUILD_CLIENT
5752     afs_uint32 now;
5753     int code;
5754
5755     vp->salvage.prio++;
5756     now = FT_ApproxTime();
5757
5758     /* update the salvageserver priority queue occasionally so that
5759      * frequently requested volumes get moved to the head of the queue
5760      */
5761     if ((vp->salvage.scheduled) &&
5762         (vp->stats.last_salvage_req < (now-SALVAGE_PRIO_UPDATE_INTERVAL))) {
5763         code = SALVSYNC_SalvageVolume(vp->hashid,
5764                                       VPartitionPath(vp->partition),
5765                                       SALVSYNC_RAISEPRIO,
5766                                       vp->salvage.reason,
5767                                       vp->salvage.prio,
5768                                       NULL);
5769         vp->stats.last_salvage_req = now;
5770         if (code != SYNC_OK) {
5771             ret = 1;
5772         }
5773     }
5774 #endif /* SALVSYNC_BUILD_CLIENT */
5775     return ret;
5776 }
5777
5778
5779 #if defined(SALVSYNC_BUILD_CLIENT) || defined(FSSYNC_BUILD_CLIENT)
5780
5781 /* A couple of little helper functions. These return true if we tried to
5782  * use this mechanism to schedule a salvage, false if we haven't tried.
5783  * If we did try a salvage then the results are contained in code.
5784  */
5785
5786 static_inline int
5787 try_SALVSYNC(Volume *vp, char *partName, int *code) {
5788 #ifdef SALVSYNC_BUILD_CLIENT
5789     if (VCanUseSALVSYNC()) {
5790         Log("Scheduling salvage for volume %" AFS_VOLID_FMT " on part %s over SALVSYNC\n",
5791             afs_printable_VolumeId_lu(vp->hashid), partName);
5792
5793         /* can't use V_id() since there's no guarantee
5794          * we have the disk data header at this point */
5795         *code = SALVSYNC_SalvageVolume(vp->hashid,
5796                                        partName,
5797                                        SALVSYNC_SALVAGE,
5798                                        vp->salvage.reason,
5799                                        vp->salvage.prio,
5800                                        NULL);
5801         return 1;
5802     }
5803 #endif
5804     return 0;
5805 }
5806
5807 static_inline int
5808 try_FSSYNC(Volume *vp, char *partName, int *code) {
5809 #ifdef FSSYNC_BUILD_CLIENT
5810     if (VCanUseFSSYNC()) {
5811         Log("Scheduling salvage for volume %" AFS_VOLID_FMT " on part %s over FSSYNC\n",
5812             afs_printable_VolumeId_lu(vp->hashid), partName);
5813
5814         /*
5815          * If we aren't the fileserver, tell the fileserver the volume
5816          * needs to be salvaged. We could directly tell the
5817          * salvageserver, but the fileserver keeps track of some stats
5818          * related to salvages, and handles some other salvage-related
5819          * complications for us.
5820          */
5821         *code = FSYNC_VolOp(vp->hashid, partName,
5822                             FSYNC_VOL_FORCE_ERROR, FSYNC_SALVAGE, NULL);
5823         return 1;
5824     }
5825 #endif /* FSSYNC_BUILD_CLIENT */
5826     return 0;
5827 }
5828
5829 /**
5830  * schedule a salvage with the salvage server or fileserver.
5831  *
5832  * @param[in] vp  pointer to volume object
5833  *
5834  * @return operation status
5835  *    @retval VCHECK_SALVAGE_OK (0)         no pending salvage
5836  *    @retval VCHECK_SALVAGE_SCHEDULED (1)  salvage has been scheduled
5837  *    @retval VCHECK_SALVAGE_ASYNC (2)      salvage being scheduled
5838  *    @retval VCHECK_SALVAGE_DENIED (3)     salvage not scheduled; denied
5839  *    @retval VCHECK_SALVAGE_FAIL (4)       salvage not scheduled; failed
5840  *
5841  * @pre
5842  *    @arg VOL_LOCK is held.
5843  *    @arg nUsers and nWaiters should be zero.
5844  *
5845  * @post salvageserver or fileserver is sent a salvage request
5846  *
5847  * @note If we are the fileserver, the request will be sent to the salvage
5848  * server over SALVSYNC. If we are not the fileserver, the request will be
5849  * sent to the fileserver over FSSYNC (FSYNC_VOL_FORCE_ERROR/FSYNC_SALVAGE).
5850  *
5851  * @note the caller must check if the volume needs to be freed after calling
5852  *       this; the volume may not have any references or be on any lists after
5853  *       we return, and we do not free it
5854  *
5855  * @note DAFS only
5856  *
5857  * @internal volume package internal use only.
5858  */
5859 static int
5860 VScheduleSalvage_r(Volume * vp)
5861 {
5862     int ret = VCHECK_SALVAGE_SCHEDULED;
5863     int code = 0;
5864     VolState state_save;
5865     VThreadOptions_t * thread_opts;
5866     char partName[16];
5867
5868     opr_Verify(VCanUseSALVSYNC() || VCanUseFSSYNC());
5869
5870     if (vp->nWaiters || vp->nUsers) {
5871         return VCHECK_SALVAGE_ASYNC;
5872     }
5873
5874     /* prevent endless salvage,attach,salvage,attach,... loops */
5875     if (vp->stats.salvages >= SALVAGE_COUNT_MAX) {
5876         return VCHECK_SALVAGE_FAIL;
5877     }
5878
5879     /*
5880      * don't perform salvsync ops on certain threads
5881      */
5882     thread_opts = pthread_getspecific(VThread_key);
5883     if (thread_opts == NULL) {
5884         thread_opts = &VThread_defaults;
5885     }
5886     if (thread_opts->disallow_salvsync || vol_disallow_salvsync) {
5887         return VCHECK_SALVAGE_ASYNC;
5888     }
5889
5890     if (vp->salvage.scheduled) {
5891         return VCHECK_SALVAGE_SCHEDULED;
5892     }
5893
5894     VCreateReservation_r(vp);
5895     VWaitExclusiveState_r(vp);
5896
5897     /*
5898      * XXX the scheduling process should really be done asynchronously
5899      *     to avoid fssync deadlocks
5900      */
5901     if (vp->salvage.scheduled) {
5902         ret = VCHECK_SALVAGE_SCHEDULED;
5903     } else {
5904         /* if we haven't previously scheduled a salvage, do so now
5905          *
5906          * set the volume to an exclusive state and drop the lock
5907          * around the SALVSYNC call
5908          */
5909         strlcpy(partName, vp->partition->name, sizeof(partName));
5910         state_save = VChangeState_r(vp, VOL_STATE_SALVSYNC_REQ);
5911         VOL_UNLOCK;
5912
5913         opr_Verify(try_SALVSYNC(vp, partName, &code)
5914                    || try_FSSYNC(vp, partName, &code));
5915
5916         VOL_LOCK;
5917         VChangeState_r(vp, state_save);
5918
5919         if (code == SYNC_OK) {
5920             ret = VCHECK_SALVAGE_SCHEDULED;
5921             vp->salvage.scheduled = 1;
5922             vp->stats.last_salvage_req = FT_ApproxTime();
5923             if (VCanUseSALVSYNC()) {
5924                 /* don't record these stats for non-fileservers; let the
5925                  * fileserver take care of these */
5926                 vp->stats.salvages++;
5927                 IncUInt64(&VStats.salvages);
5928             }
5929         } else {
5930             switch(code) {
5931             case SYNC_BAD_COMMAND:
5932             case SYNC_COM_ERROR:
5933                 ret = VCHECK_SALVAGE_FAIL;
5934                 break;
5935             case SYNC_DENIED:
5936                 ret = VCHECK_SALVAGE_DENIED;
5937                 Log("VScheduleSalvage_r: Salvage request for volume %" AFS_VOLID_FMT " "
5938                     "denied\n", afs_printable_VolumeId_lu(vp->hashid));
5939                 break;
5940             case SYNC_FAILED:
5941                 ret = VCHECK_SALVAGE_FAIL;
5942                 Log("VScheduleSalvage_r: Salvage request for volume %" AFS_VOLID_FMT " "
5943                     "failed\n", afs_printable_VolumeId_lu(vp->hashid));
5944                 break;
5945             default:
5946                 ret = VCHECK_SALVAGE_FAIL;
5947                 Log("VScheduleSalvage_r: Salvage request for volume %" AFS_VOLID_FMT " "
5948                     "received unknown protocol error %d\n",
5949                     afs_printable_VolumeId_lu(vp->hashid), code);
5950                 break;
5951             }
5952
5953             if (VCanUseFSSYNC()) {
5954                 VChangeState_r(vp, VOL_STATE_ERROR);
5955             }
5956         }
5957     }
5958
5959     /* NB: this is cancelling the reservation we obtained above, but we do
5960      * not call VCancelReservation_r, since that may trigger the vp dtor,
5961      * possibly free'ing the vp. We need to keep the vp around after
5962      * this, as the caller may reference vp without any refs. Instead, it
5963      * is the duty of the caller to inspect 'vp' after we return to see if
5964      * needs to be freed. */
5965     opr_Verify(--vp->nWaiters >= 0);
5966     return ret;
5967 }
5968 #endif /* SALVSYNC_BUILD_CLIENT || FSSYNC_BUILD_CLIENT */
5969
5970 #ifdef SALVSYNC_BUILD_CLIENT
5971
5972 /**
5973  * connect to the salvageserver SYNC service.
5974  *
5975  * @return operation status
5976  *    @retval 0 failure
5977  *    @retval 1 success
5978  *
5979  * @post connection to salvageserver SYNC service established
5980  *
5981  * @see VConnectSALV_r
5982  * @see VDisconnectSALV
5983  * @see VReconnectSALV
5984  */
5985 int
5986 VConnectSALV(void)
5987 {
5988     int retVal;
5989     VOL_LOCK;
5990     retVal = VConnectSALV_r();
5991     VOL_UNLOCK;
5992     return retVal;
5993 }
5994
5995 /**
5996  * connect to the salvageserver SYNC service.
5997  *
5998  * @return operation status
5999  *    @retval 0 failure
6000  *    @retval 1 success
6001  *
6002  * @pre VOL_LOCK is held.
6003  *
6004  * @post connection to salvageserver SYNC service established
6005  *
6006  * @see VConnectSALV
6007  * @see VDisconnectSALV_r
6008  * @see VReconnectSALV_r
6009  * @see SALVSYNC_clientInit
6010  *
6011  * @internal volume package internal use only.
6012  */
6013 int
6014 VConnectSALV_r(void)
6015 {
6016     return SALVSYNC_clientInit();
6017 }
6018
6019 /**
6020  * disconnect from the salvageserver SYNC service.
6021  *
6022  * @return operation status
6023  *    @retval 0 success
6024  *
6025  * @pre client should have a live connection to the salvageserver
6026  *
6027  * @post connection to salvageserver SYNC service destroyed
6028  *
6029  * @see VDisconnectSALV_r
6030  * @see VConnectSALV
6031  * @see VReconnectSALV
6032  */
6033 int
6034 VDisconnectSALV(void)
6035 {
6036     VOL_LOCK;
6037     VDisconnectSALV_r();
6038     VOL_UNLOCK;
6039     return 0;
6040 }
6041
6042 /**
6043  * disconnect from the salvageserver SYNC service.
6044  *
6045  * @return operation status
6046  *    @retval 0 success
6047  *
6048  * @pre
6049  *    @arg VOL_LOCK is held.
6050  *    @arg client should have a live connection to the salvageserver.
6051  *
6052  * @post connection to salvageserver SYNC service destroyed
6053  *
6054  * @see VDisconnectSALV
6055  * @see VConnectSALV_r
6056  * @see VReconnectSALV_r
6057  * @see SALVSYNC_clientFinis
6058  *
6059  * @internal volume package internal use only.
6060  */
6061 int
6062 VDisconnectSALV_r(void)
6063 {
6064     return SALVSYNC_clientFinis();
6065 }
6066
6067 /**
6068  * disconnect and then re-connect to the salvageserver SYNC service.
6069  *
6070  * @return operation status
6071  *    @retval 0 failure
6072  *    @retval 1 success
6073  *
6074  * @pre client should have a live connection to the salvageserver
6075  *
6076  * @post old connection is dropped, and a new one is established
6077  *
6078  * @see VConnectSALV
6079  * @see VDisconnectSALV
6080  * @see VReconnectSALV_r
6081  */
6082 int
6083 VReconnectSALV(void)
6084 {
6085     int retVal;
6086     VOL_LOCK;
6087     retVal = VReconnectSALV_r();
6088     VOL_UNLOCK;
6089     return retVal;
6090 }
6091
6092 /**
6093  * disconnect and then re-connect to the salvageserver SYNC service.
6094  *
6095  * @return operation status
6096  *    @retval 0 failure
6097  *    @retval 1 success
6098  *
6099  * @pre
6100  *    @arg VOL_LOCK is held.
6101  *    @arg client should have a live connection to the salvageserver.
6102  *
6103  * @post old connection is dropped, and a new one is established
6104  *
6105  * @see VConnectSALV_r
6106  * @see VDisconnectSALV
6107  * @see VReconnectSALV
6108  * @see SALVSYNC_clientReconnect
6109  *
6110  * @internal volume package internal use only.
6111  */
6112 int
6113 VReconnectSALV_r(void)
6114 {
6115     return SALVSYNC_clientReconnect();
6116 }
6117 #endif /* SALVSYNC_BUILD_CLIENT */
6118 #endif /* AFS_DEMAND_ATTACH_FS */
6119
6120
6121 /***************************************************/
6122 /* FSSYNC routines                                 */
6123 /***************************************************/
6124
6125 /* This must be called by any volume utility which needs to run while the
6126    file server is also running.  This is separated from VInitVolumePackage2 so
6127    that a utility can fork--and each of the children can independently
6128    initialize communication with the file server */
6129 #ifdef FSSYNC_BUILD_CLIENT
6130 /**
6131  * connect to the fileserver SYNC service.
6132  *
6133  * @return operation status
6134  *    @retval 0 failure
6135  *    @retval 1 success
6136  *
6137  * @pre
6138  *    @arg VInit must equal 2.
6139  *    @arg Program Type must not be fileserver or salvager.
6140  *
6141  * @post connection to fileserver SYNC service established
6142  *
6143  * @see VConnectFS_r
6144  * @see VDisconnectFS
6145  * @see VChildProcReconnectFS
6146  */
6147 int
6148 VConnectFS(void)
6149 {
6150     int retVal;
6151     VOL_LOCK;
6152     retVal = VConnectFS_r();
6153     VOL_UNLOCK;
6154     return retVal;
6155 }
6156
6157 /**
6158  * connect to the fileserver SYNC service.
6159  *
6160  * @return operation status
6161  *    @retval 0 failure
6162  *    @retval 1 success
6163  *
6164  * @pre
6165  *    @arg VInit must equal 2.
6166  *    @arg Program Type must not be fileserver or salvager.
6167  *    @arg VOL_LOCK is held.
6168  *
6169  * @post connection to fileserver SYNC service established
6170  *
6171  * @see VConnectFS
6172  * @see VDisconnectFS_r
6173  * @see VChildProcReconnectFS_r
6174  *
6175  * @internal volume package internal use only.
6176  */
6177 int
6178 VConnectFS_r(void)
6179 {
6180     int rc;
6181     opr_Assert((VInit == 2) &&
6182            (programType != fileServer) &&
6183            (programType != salvager));
6184     rc = FSYNC_clientInit();
6185     if (rc) {
6186         VSetVInit_r(3);
6187     }
6188     return rc;
6189 }
6190
6191 /**
6192  * disconnect from the fileserver SYNC service.
6193  *
6194  * @pre
6195  *    @arg client should have a live connection to the fileserver.
6196  *    @arg VOL_LOCK is held.
6197  *    @arg Program Type must not be fileserver or salvager.
6198  *
6199  * @post connection to fileserver SYNC service destroyed
6200  *
6201  * @see VDisconnectFS
6202  * @see VConnectFS_r
6203  * @see VChildProcReconnectFS_r
6204  *
6205  * @internal volume package internal use only.
6206  */
6207 void
6208 VDisconnectFS_r(void)
6209 {
6210     opr_Assert((programType != fileServer) &&
6211            (programType != salvager));
6212     FSYNC_clientFinis();
6213     VSetVInit_r(2);
6214 }
6215
6216 /**
6217  * disconnect from the fileserver SYNC service.
6218  *
6219  * @pre
6220  *    @arg client should have a live connection to the fileserver.
6221  *    @arg Program Type must not be fileserver or salvager.
6222  *
6223  * @post connection to fileserver SYNC service destroyed
6224  *
6225  * @see VDisconnectFS_r
6226  * @see VConnectFS
6227  * @see VChildProcReconnectFS
6228  */
6229 void
6230 VDisconnectFS(void)
6231 {
6232     VOL_LOCK;
6233     VDisconnectFS_r();
6234     VOL_UNLOCK;
6235 }
6236
6237 /**
6238  * connect to the fileserver SYNC service from a child process following a fork.
6239  *
6240  * @return operation status
6241  *    @retval 0 failure
6242  *    @retval 1 success
6243  *
6244  * @pre
6245  *    @arg VOL_LOCK is held.
6246  *    @arg current FSYNC handle is shared with a parent process
6247  *
6248  * @post current FSYNC handle is discarded and a new connection to the
6249  *       fileserver SYNC service is established
6250  *
6251  * @see VChildProcReconnectFS
6252  * @see VConnectFS_r
6253  * @see VDisconnectFS_r
6254  *
6255  * @internal volume package internal use only.
6256  */
6257 int
6258 VChildProcReconnectFS_r(void)
6259 {
6260     return FSYNC_clientChildProcReconnect();
6261 }
6262
6263 /**
6264  * connect to the fileserver SYNC service from a child process following a fork.
6265  *
6266  * @return operation status
6267  *    @retval 0 failure
6268  *    @retval 1 success
6269  *
6270  * @pre current FSYNC handle is shared with a parent process
6271  *
6272  * @post current FSYNC handle is discarded and a new connection to the
6273  *       fileserver SYNC service is established
6274  *
6275  * @see VChildProcReconnectFS_r
6276  * @see VConnectFS
6277  * @see VDisconnectFS
6278  */
6279 int
6280 VChildProcReconnectFS(void)
6281 {
6282     int ret;
6283     VOL_LOCK;
6284     ret = VChildProcReconnectFS_r();
6285     VOL_UNLOCK;
6286     return ret;
6287 }
6288 #endif /* FSSYNC_BUILD_CLIENT */
6289
6290
6291 /***************************************************/
6292 /* volume bitmap routines                          */
6293 /***************************************************/
6294
6295 /*
6296  * Grow the bitmap by the defined increment
6297  */
6298 void
6299 VGrowBitmap(struct vnodeIndex *index)
6300 {
6301     byte *bp;
6302
6303     bp = realloc(index->bitmap, index->bitmapSize + VOLUME_BITMAP_GROWSIZE);
6304     osi_Assert(bp != NULL);
6305     index->bitmap = bp;
6306     bp += index->bitmapSize;
6307     memset(bp, 0, VOLUME_BITMAP_GROWSIZE);
6308     index->bitmapOffset = index->bitmapSize;
6309     index->bitmapSize += VOLUME_BITMAP_GROWSIZE;
6310
6311     return;
6312 }
6313
6314 /**
6315  * allocate a vnode bitmap number for the vnode
6316  *
6317  * @param[out] ec  error code
6318  * @param[in] vp   volume object pointer
6319  * @param[in] index vnode index number for the vnode
6320  * @param[in] flags flag values described in note
6321  *
6322  * @note for DAFS, flags parameter controls locking behavior.
6323  * If (flags & VOL_ALLOC_BITMAP_WAIT) is set, then this function
6324  * will create a reservation and block on any other exclusive
6325  * operations.  Otherwise, this function assumes the caller
6326  * already has exclusive access to vp, and we just change the
6327  * volume state.
6328  *
6329  * @pre VOL_LOCK held
6330  *
6331  * @return bit number allocated
6332  */
6333 /*
6334
6335  */
6336 int
6337 VAllocBitmapEntry_r(Error * ec, Volume * vp,
6338                     struct vnodeIndex *index, int flags)
6339 {
6340     int ret = 0;
6341     byte *bp, *ep;
6342 #ifdef AFS_DEMAND_ATTACH_FS
6343     VolState state_save;
6344 #endif /* AFS_DEMAND_ATTACH_FS */
6345
6346     *ec = 0;
6347
6348     /* This test is probably redundant */
6349     if (!VolumeWriteable(vp)) {
6350         *ec = (bit32) VREADONLY;
6351         return ret;
6352     }
6353
6354 #ifdef AFS_DEMAND_ATTACH_FS
6355     if (flags & VOL_ALLOC_BITMAP_WAIT) {
6356         VCreateReservation_r(vp);
6357         VWaitExclusiveState_r(vp);
6358     }
6359     state_save = VChangeState_r(vp, VOL_STATE_GET_BITMAP);
6360 #endif /* AFS_DEMAND_ATTACH_FS */
6361
6362 #ifdef BITMAP_LATER
6363     if ((programType == fileServer) && !index->bitmap) {
6364         int i;
6365 #ifndef AFS_DEMAND_ATTACH_FS
6366         /* demand attach fs uses the volume state to avoid races.
6367          * specialStatus field is not used at all */
6368         int wasVBUSY = 0;
6369         if (vp->specialStatus == VBUSY) {
6370             if (vp->goingOffline) {     /* vos dump waiting for the volume to
6371                                          * go offline. We probably come here
6372                                          * from AddNewReadableResidency */
6373                 wasVBUSY = 1;
6374             } else {
6375                 while (vp->specialStatus == VBUSY) {
6376 #ifdef AFS_PTHREAD_ENV
6377                     VOL_UNLOCK;
6378                     sleep(2);
6379                     VOL_LOCK;
6380 #else /* !AFS_PTHREAD_ENV */
6381                     IOMGR_Sleep(2);
6382 #endif /* !AFS_PTHREAD_ENV */
6383                 }
6384             }
6385         }
6386 #endif /* !AFS_DEMAND_ATTACH_FS */
6387
6388         if (!index->bitmap) {
6389 #ifndef AFS_DEMAND_ATTACH_FS
6390             vp->specialStatus = VBUSY;  /* Stop anyone else from using it. */
6391 #endif /* AFS_DEMAND_ATTACH_FS */
6392             for (i = 0; i < nVNODECLASSES; i++) {
6393                 VGetBitmap_r(ec, vp, i);
6394                 if (*ec) {
6395 #ifdef AFS_DEMAND_ATTACH_FS
6396                     VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0 /*flags*/);
6397 #else /* AFS_DEMAND_ATTACH_FS */
6398                     DeleteVolumeFromHashTable(vp);
6399                     vp->shuttingDown = 1;       /* Let who has it free it. */
6400                     vp->specialStatus = 0;
6401 #endif /* AFS_DEMAND_ATTACH_FS */
6402                     goto done;
6403                 }
6404             }
6405 #ifndef AFS_DEMAND_ATTACH_FS
6406             if (!wasVBUSY)
6407                 vp->specialStatus = 0;  /* Allow others to have access. */
6408 #endif /* AFS_DEMAND_ATTACH_FS */
6409         }
6410     }
6411 #endif /* BITMAP_LATER */
6412
6413 #ifdef AFS_DEMAND_ATTACH_FS
6414     VOL_UNLOCK;
6415 #endif /* AFS_DEMAND_ATTACH_FS */
6416     bp = index->bitmap + index->bitmapOffset;
6417     ep = index->bitmap + index->bitmapSize;
6418     while (bp < ep) {
6419         if ((*(bit32 *) bp) != (bit32) 0xffffffff) {
6420             int o;
6421             index->bitmapOffset = (afs_uint32) (bp - index->bitmap);
6422             while (*bp == 0xff)
6423                 bp++;
6424             o = opr_ffs(~*bp) - 1;
6425             *bp |= (1 << o);
6426             ret = ((bp - index->bitmap) * 8 + o);
6427 #ifdef AFS_DEMAND_ATTACH_FS
6428             VOL_LOCK;
6429 #endif /* AFS_DEMAND_ATTACH_FS */
6430             goto done;
6431         }
6432         bp += sizeof(bit32) /* i.e. 4 */ ;
6433     }
6434     /* No bit map entry--must grow bitmap */
6435     VGrowBitmap(index);
6436     bp = index->bitmap + index->bitmapOffset;
6437     *bp = 1;
6438     ret = index->bitmapOffset * 8;
6439 #ifdef AFS_DEMAND_ATTACH_FS
6440     VOL_LOCK;
6441 #endif /* AFS_DEMAND_ATTACH_FS */
6442
6443  done:
6444 #ifdef AFS_DEMAND_ATTACH_FS
6445     VChangeState_r(vp, state_save);
6446     if (flags & VOL_ALLOC_BITMAP_WAIT) {
6447         VCancelReservation_r(vp);
6448     }
6449 #endif /* AFS_DEMAND_ATTACH_FS */
6450     return ret;
6451 }
6452
6453 int
6454 VAllocBitmapEntry(Error * ec, Volume * vp, struct vnodeIndex * index)
6455 {
6456     int retVal;
6457     VOL_LOCK;
6458     retVal = VAllocBitmapEntry_r(ec, vp, index, VOL_ALLOC_BITMAP_WAIT);
6459     VOL_UNLOCK;
6460     return retVal;
6461 }
6462
6463 void
6464 VFreeBitMapEntry_r(Error * ec, Volume *vp, struct vnodeIndex *index,
6465                    unsigned bitNumber, int flags)
6466 {
6467     unsigned int offset;
6468
6469     *ec = 0;
6470
6471 #ifdef AFS_DEMAND_ATTACH_FS
6472     if (flags & VOL_FREE_BITMAP_WAIT) {
6473         /* VAllocBitmapEntry_r allocs bitmap entries under an exclusive volume
6474          * state, so ensure we're not in an exclusive volume state when we update
6475          * the bitmap */
6476         VCreateReservation_r(vp);
6477         VWaitExclusiveState_r(vp);
6478     }
6479 #endif
6480
6481 #ifdef BITMAP_LATER
6482     if (!index->bitmap)
6483         goto done;
6484 #endif /* BITMAP_LATER */
6485
6486     offset = bitNumber >> 3;
6487     if (offset >= index->bitmapSize) {
6488         *ec = VNOVNODE;
6489         goto done;
6490     }
6491     if (offset < index->bitmapOffset)
6492         index->bitmapOffset = offset & ~3;      /* Truncate to nearest bit32 */
6493     *(index->bitmap + offset) &= ~(1 << (bitNumber & 0x7));
6494
6495  done:
6496 #ifdef AFS_DEMAND_ATTACH_FS
6497     if (flags & VOL_FREE_BITMAP_WAIT) {
6498         VCancelReservation_r(vp);
6499     }
6500 #endif
6501     return; /* make the compiler happy for non-DAFS */
6502 }
6503
6504 void
6505 VFreeBitMapEntry(Error * ec, Volume *vp, struct vnodeIndex *index,
6506                  unsigned bitNumber)
6507 {
6508     VOL_LOCK;
6509     VFreeBitMapEntry_r(ec, vp, index, bitNumber, VOL_FREE_BITMAP_WAIT);
6510     VOL_UNLOCK;
6511 }
6512
6513 /* this function will drop the glock internally.
6514  * for old pthread fileservers, this is safe thanks to vbusy.
6515  *
6516  * for demand attach fs, caller must have already called
6517  * VCreateReservation_r and VWaitExclusiveState_r */
6518 static void
6519 VGetBitmap_r(Error * ec, Volume * vp, VnodeClass class)
6520 {
6521     StreamHandle_t *file;
6522     afs_sfsize_t nVnodes, size;
6523     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
6524     struct vnodeIndex *vip = &vp->vnodeIndex[class];
6525     struct VnodeDiskObject *vnode;
6526     unsigned int unique = 0;
6527     FdHandle_t *fdP;
6528 #ifdef BITMAP_LATER
6529     byte *BitMap = 0;
6530 #endif /* BITMAP_LATER */
6531 #ifdef AFS_DEMAND_ATTACH_FS
6532     VolState state_save;
6533 #endif /* AFS_DEMAND_ATTACH_FS */
6534
6535     *ec = 0;
6536
6537 #ifdef AFS_DEMAND_ATTACH_FS
6538     state_save = VChangeState_r(vp, VOL_STATE_GET_BITMAP);
6539 #endif /* AFS_DEMAND_ATTACH_FS */
6540     VOL_UNLOCK;
6541
6542     fdP = IH_OPEN(vip->handle);
6543     opr_Assert(fdP != NULL);
6544     file = FDH_FDOPEN(fdP, "r");
6545     opr_Assert(file != NULL);
6546     vnode = malloc(vcp->diskSize);
6547     opr_Assert(vnode != NULL);
6548     size = OS_SIZE(fdP->fd_fd);
6549     opr_Assert(size != -1);
6550     nVnodes = (size <= vcp->diskSize ? 0 : size - vcp->diskSize)
6551         >> vcp->logSize;
6552     vip->bitmapSize = ((nVnodes / 8) + 10) / 4 * 4;     /* The 10 is a little extra so
6553                                                          * a few files can be created in this volume,
6554                                                          * the whole thing is rounded up to nearest 4
6555                                                          * bytes, because the bit map allocator likes
6556                                                          * it that way */
6557 #ifdef BITMAP_LATER
6558     BitMap = (byte *) calloc(1, vip->bitmapSize);
6559     opr_Assert(BitMap != NULL);
6560 #else /* BITMAP_LATER */
6561     vip->bitmap = (byte *) calloc(1, vip->bitmapSize);
6562     opr_Assert(vip->bitmap != NULL);
6563     vip->bitmapOffset = 0;
6564 #endif /* BITMAP_LATER */
6565     if (STREAM_ASEEK(file, vcp->diskSize) != -1) {
6566         int bitNumber = 0;
6567         for (bitNumber = 0; bitNumber < nVnodes + 100; bitNumber++) {
6568             if (STREAM_READ(vnode, vcp->diskSize, 1, file) != 1)
6569                 break;
6570             if (vnode->type != vNull) {
6571                 if (vnode->vnodeMagic != vcp->magic) {
6572                     Log("GetBitmap: addled vnode index in volume %s; volume needs salvage\n", V_name(vp));
6573                     *ec = VSALVAGE;
6574                     break;
6575                 }
6576 #ifdef BITMAP_LATER
6577                 *(BitMap + (bitNumber >> 3)) |= (1 << (bitNumber & 0x7));
6578 #else /* BITMAP_LATER */
6579                 *(vip->bitmap + (bitNumber >> 3)) |= (1 << (bitNumber & 0x7));
6580 #endif /* BITMAP_LATER */
6581                 if (unique <= vnode->uniquifier)
6582                     unique = vnode->uniquifier + 1;
6583             }
6584 #ifndef AFS_PTHREAD_ENV
6585             if ((bitNumber & 0x00ff) == 0x0ff) {        /* every 256 iterations */
6586                 IOMGR_Poll();
6587             }
6588 #endif /* !AFS_PTHREAD_ENV */
6589         }
6590     }
6591     if (vp->nextVnodeUnique < unique) {
6592         Log("GetBitmap: bad volume uniquifier for volume %s; volume needs salvage\n", V_name(vp));
6593         *ec = VSALVAGE;
6594     }
6595     /* Paranoia, partly justified--I think fclose after fdopen
6596      * doesn't seem to close fd.  In any event, the documentation
6597      * doesn't specify, so it's safer to close it twice.
6598      */
6599     STREAM_CLOSE(file);
6600     FDH_CLOSE(fdP);
6601     free(vnode);
6602
6603     VOL_LOCK;
6604 #ifdef BITMAP_LATER
6605     /* There may have been a racing condition with some other thread, both
6606      * creating the bitmaps for this volume. If the other thread was faster
6607      * the pointer to bitmap should already be filled and we can free ours.
6608      */
6609     if (vip->bitmap == NULL) {
6610         vip->bitmap = BitMap;
6611         vip->bitmapOffset = 0;
6612     } else
6613         free(BitMap);
6614 #endif /* BITMAP_LATER */
6615 #ifdef AFS_DEMAND_ATTACH_FS
6616     VChangeState_r(vp, state_save);
6617 #endif /* AFS_DEMAND_ATTACH_FS */
6618 }
6619
6620
6621 /***************************************************/
6622 /* Volume Path and Volume Number utility routines  */
6623 /***************************************************/
6624
6625 /**
6626  * find the first occurrence of a volume header file and return the path.
6627  *
6628  * @param[out] ec          outbound error code
6629  * @param[in]  volumeId    volume id to find
6630  * @param[out] partitionp  pointer to disk partition path string
6631  * @param[out] namep       pointer to volume header file name string
6632  *
6633  * @post path to first occurrence of volume header is returned in partitionp
6634  *       and namep, or ec is set accordingly.
6635  *
6636  * @warning this function is NOT re-entrant -- partitionp and namep point to
6637  *          static data segments
6638  *
6639  * @note if a volume utility inadvertently leaves behind a stale volume header
6640  *       on a vice partition, it is possible for callers to get the wrong one,
6641  *       depending on the order of the disk partition linked list.
6642  *
6643  */
6644 void
6645 VGetVolumePath(Error * ec, VolumeId volumeId, char **partitionp, char **namep)
6646 {
6647     static char partition[VMAXPATHLEN], name[VMAXPATHLEN];
6648     char path[VMAXPATHLEN];
6649     int found = 0;
6650     struct DiskPartition64 *dp;
6651
6652     *ec = 0;
6653     name[0] = OS_DIRSEPC;
6654     snprintf(&name[1], (sizeof name) - 1, VFORMAT,
6655              afs_printable_VolumeId_lu(volumeId));
6656     for (dp = DiskPartitionList; dp; dp = dp->next) {
6657         struct afs_stat_st status;
6658         strcpy(path, VPartitionPath(dp));
6659         strcat(path, name);
6660         if (afs_stat(path, &status) == 0) {
6661             strcpy(partition, dp->name);
6662             found = 1;
6663             break;
6664         }
6665     }
6666     if (!found) {
6667         *ec = VNOVOL;
6668         *partitionp = *namep = NULL;
6669     } else {
6670         *partitionp = partition;
6671         *namep = name;
6672     }
6673 }
6674
6675 /**
6676  * extract a volume number from a volume header filename string.
6677  *
6678  * @param[in] name  volume header filename string
6679  *
6680  * @return volume number
6681  *
6682  * @note the string must be of the form VFORMAT.  the only permissible
6683  *       deviation is a leading OS_DIRSEPC character.
6684  *
6685  * @see VFORMAT
6686  */
6687 int
6688 VolumeNumber(char *name)
6689 {
6690     if (*name == OS_DIRSEPC)
6691         name++;
6692     return strtoul(name + 1, NULL, 10);
6693 }
6694
6695 /**
6696  * compute the volume header filename.
6697  *
6698  * @param[in] volumeId
6699  *
6700  * @return volume header filename
6701  *
6702  * @post volume header filename string is constructed
6703  *
6704  * @warning this function is NOT re-entrant -- the returned string is
6705  *          stored in a static char array.  see VolumeExternalName_r
6706  *          for a re-entrant equivalent.
6707  *
6708  * @see VolumeExternalName_r
6709  *
6710  * @deprecated due to the above re-entrancy warning, this interface should
6711  *             be considered deprecated.  Please use VolumeExternalName_r
6712  *             in its stead.
6713  */
6714 char *
6715 VolumeExternalName(VolumeId volumeId)
6716 {
6717     static char name[VMAXPATHLEN];
6718     snprintf(name, sizeof name, VFORMAT, afs_printable_VolumeId_lu(volumeId));
6719     return name;
6720 }
6721
6722 /**
6723  * compute the volume header filename.
6724  *
6725  * @param[in]     volumeId
6726  * @param[inout]  name       array in which to store filename
6727  * @param[in]     len        length of name array
6728  *
6729  * @return result code from afs_snprintf
6730  *
6731  * @see VolumeExternalName
6732  * @see afs_snprintf
6733  *
6734  * @note re-entrant equivalent of VolumeExternalName
6735  */
6736 int
6737 VolumeExternalName_r(VolumeId volumeId, char * name, size_t len)
6738 {
6739     return snprintf(name, len, VFORMAT, afs_printable_VolumeId_lu(volumeId));
6740 }
6741
6742
6743 /***************************************************/
6744 /* Volume Usage Statistics routines                */
6745 /***************************************************/
6746
6747 #define OneDay  (86400)         /* 24 hours' worth of seconds */
6748
6749 static time_t
6750 Midnight(time_t t) {
6751     struct tm local, *l;
6752     time_t midnight;
6753
6754 #if defined(AFS_PTHREAD_ENV) && !defined(AFS_NT40_ENV)
6755     l = localtime_r(&t, &local);
6756 #else
6757     l = localtime(&t);
6758 #endif
6759
6760     if (l != NULL) {
6761         /* the following is strictly speaking problematic on the
6762            switching day to daylight saving time, after the switch,
6763            as tm_isdst does not match.  Similarly, on the looong day when
6764            switching back the OneDay check will not do what naively expected!
6765            The effects are minor, though, and more a matter of interpreting
6766            the numbers. */
6767 #ifndef AFS_PTHREAD_ENV
6768         local = *l;
6769 #endif
6770         local.tm_hour = local.tm_min=local.tm_sec = 0;
6771         midnight = mktime(&local);
6772         if (midnight != (time_t) -1) return(midnight);
6773     }
6774     return( (t/OneDay)*OneDay );
6775
6776 }
6777
6778 /*------------------------------------------------------------------------
6779  * [export] VAdjustVolumeStatistics
6780  *
6781  * Description:
6782  *      If we've passed midnight, we need to update all the day use
6783  *      statistics as well as zeroing the detailed volume statistics
6784  *      (if we are implementing them).
6785  *
6786  * Arguments:
6787  *      vp : Pointer to the volume structure describing the lucky
6788  *              volume being considered for update.
6789  *
6790  * Returns:
6791  *      0 (always!)
6792  *
6793  * Environment:
6794  *      Nothing interesting.
6795  *
6796  * Side Effects:
6797  *      As described.
6798  *------------------------------------------------------------------------*/
6799
6800 int
6801 VAdjustVolumeStatistics_r(Volume * vp)
6802 {
6803     unsigned int now = FT_ApproxTime();
6804
6805     if (now - V_dayUseDate(vp) > OneDay) {
6806         int ndays, i;
6807
6808         ndays = (now - V_dayUseDate(vp)) / OneDay;
6809         for (i = 6; i > ndays - 1; i--)
6810             V_weekUse(vp)[i] = V_weekUse(vp)[i - ndays];
6811         for (i = 0; i < ndays - 1 && i < 7; i++)
6812             V_weekUse(vp)[i] = 0;
6813         if (ndays <= 7)
6814             V_weekUse(vp)[ndays - 1] = V_dayUse(vp);
6815         V_dayUse(vp) = 0;
6816         V_dayUseDate(vp) = Midnight(now);
6817
6818         /*
6819          * All we need to do is bzero the entire VOL_STATS_BYTES of
6820          * the detailed volume statistics area.
6821          */
6822         memset((V_stat_area(vp)), 0, VOL_STATS_BYTES);
6823         }
6824
6825     /*It's been more than a day of collection */
6826     /*
6827      * Always return happily.
6828      */
6829     return (0);
6830 }                               /*VAdjustVolumeStatistics */
6831
6832 int
6833 VAdjustVolumeStatistics(Volume * vp)
6834 {
6835     int retVal;
6836     VOL_LOCK;
6837     retVal = VAdjustVolumeStatistics_r(vp);
6838     VOL_UNLOCK;
6839     return retVal;
6840 }
6841
6842 void
6843 VBumpVolumeUsage_r(Volume * vp)
6844 {
6845     unsigned int now = FT_ApproxTime();
6846     V_accessDate(vp) = now;
6847     if (now - V_dayUseDate(vp) > OneDay)
6848         VAdjustVolumeStatistics_r(vp);
6849     /*
6850      * Save the volume header image to disk after a threshold of bumps to dayUse,
6851      * at most every usage_rate_limit seconds.
6852      */
6853     V_dayUse(vp)++;
6854     vp->usage_bumps_outstanding++;
6855     if (vp->usage_bumps_outstanding >= vol_opts.usage_threshold
6856         && vp->usage_bumps_next_write <= now) {
6857         Error error;
6858         vp->usage_bumps_outstanding = 0;
6859         vp->usage_bumps_next_write = now + vol_opts.usage_rate_limit;
6860         VUpdateVolume_r(&error, vp, VOL_UPDATE_WAIT);
6861     }
6862 }
6863
6864 void
6865 VBumpVolumeUsage(Volume * vp)
6866 {
6867     VOL_LOCK;
6868     VBumpVolumeUsage_r(vp);
6869     VOL_UNLOCK;
6870 }
6871
6872 void
6873 VSetDiskUsage_r(void)
6874 {
6875 #ifndef AFS_DEMAND_ATTACH_FS
6876     static int FifteenMinuteCounter = 0;
6877 #endif
6878
6879     while (VInit < 2) {
6880         /* NOTE: Don't attempt to access the partitions list until the
6881          * initialization level indicates that all volumes are attached,
6882          * which implies that all partitions are initialized. */
6883 #ifdef AFS_PTHREAD_ENV
6884         VOL_CV_WAIT(&vol_vinit_cond);
6885 #else /* AFS_PTHREAD_ENV */
6886         IOMGR_Sleep(10);
6887 #endif /* AFS_PTHREAD_ENV */
6888     }
6889
6890     VResetDiskUsage_r();
6891
6892 #ifndef AFS_DEMAND_ATTACH_FS
6893     if (++FifteenMinuteCounter == 3) {
6894         FifteenMinuteCounter = 0;
6895         VScanUpdateList();
6896     }
6897 #endif /* !AFS_DEMAND_ATTACH_FS */
6898 }
6899
6900 void
6901 VSetDiskUsage(void)
6902 {
6903     VOL_LOCK;
6904     VSetDiskUsage_r();
6905     VOL_UNLOCK;
6906 }
6907
6908
6909 /***************************************************/
6910 /* Volume Update List routines                     */
6911 /***************************************************/
6912
6913 /* The number of minutes that a volume hasn't been updated before the
6914  * "Dont salvage" flag in the volume header will be turned on */
6915 #define SALVAGE_INTERVAL        (10*60)
6916
6917 /*
6918  * demand attach fs
6919  *
6920  * volume update list functionality has been moved into the VLRU
6921  * the DONT_SALVAGE flag is now set during VLRU demotion
6922  */
6923
6924 #ifndef AFS_DEMAND_ATTACH_FS
6925 static VolumeId *UpdateList = NULL;     /* Pointer to array of Volume ID's */
6926 static int nUpdatedVolumes = 0;         /* Updated with entry in UpdateList, salvage after crash flag on */
6927 static int updateSize = 0;              /* number of entries possible */
6928 #define UPDATE_LIST_SIZE 128            /* initial size increment (must be a power of 2!) */
6929 #endif /* !AFS_DEMAND_ATTACH_FS */
6930
6931 void
6932 VAddToVolumeUpdateList_r(Error * ec, Volume * vp)
6933 {
6934     *ec = 0;
6935     vp->updateTime = FT_ApproxTime();
6936     if (V_dontSalvage(vp) == 0)
6937         return;
6938     V_dontSalvage(vp) = 0;
6939     VSyncVolume_r(ec, vp, 0);
6940 #ifdef AFS_DEMAND_ATTACH_FS
6941     V_attachFlags(vp) &= ~(VOL_HDR_DONTSALV);
6942 #else /* !AFS_DEMAND_ATTACH_FS */
6943     if (*ec)
6944         return;
6945     if (UpdateList == NULL) {
6946         updateSize = UPDATE_LIST_SIZE;
6947         UpdateList = malloc(sizeof(VolumeId) * updateSize);
6948     } else {
6949         if (nUpdatedVolumes == updateSize) {
6950             updateSize <<= 1;
6951             if (updateSize > 524288) {
6952                 Log("warning: there is likely a bug in the volume update scanner\n");
6953                 return;
6954             }
6955             UpdateList = realloc(UpdateList,
6956                                  sizeof(VolumeId) * updateSize);
6957         }
6958     }
6959     opr_Assert(UpdateList != NULL);
6960     UpdateList[nUpdatedVolumes++] = V_id(vp);
6961 #endif /* !AFS_DEMAND_ATTACH_FS */
6962 }
6963
6964 #ifndef AFS_DEMAND_ATTACH_FS
6965 static void
6966 VScanUpdateList(void)
6967 {
6968     int i, gap;
6969     Volume *vp;
6970     Error error;
6971     afs_uint32 now = FT_ApproxTime();
6972     /* Be careful with this code, since it works with interleaved calls to AddToVolumeUpdateList */
6973     for (i = gap = 0; i < nUpdatedVolumes; i++) {
6974         if (gap)
6975             UpdateList[i - gap] = UpdateList[i];
6976
6977         /* XXX this routine needlessly messes up the Volume LRU by
6978          * breaking the LRU temporal-locality assumptions.....
6979          * we should use a special volume header allocator here */
6980         vp = VGetVolume_r(&error, UpdateList[i - gap] = UpdateList[i]);
6981         if (error) {
6982             gap++;
6983         } else if (vp->nUsers == 1 && now - vp->updateTime > SALVAGE_INTERVAL) {
6984             V_dontSalvage(vp) = DONT_SALVAGE;
6985             VUpdateVolume_r(&error, vp, 0);     /* No need to fsync--not critical */
6986             gap++;
6987         }
6988
6989         if (vp) {
6990             VPutVolume_r(vp);
6991         }
6992
6993 #ifndef AFS_PTHREAD_ENV
6994         IOMGR_Poll();
6995 #endif /* !AFS_PTHREAD_ENV */
6996     }
6997     nUpdatedVolumes -= gap;
6998 }
6999 #endif /* !AFS_DEMAND_ATTACH_FS */
7000
7001
7002 /***************************************************/
7003 /* Volume LRU routines                             */
7004 /***************************************************/
7005
7006 /* demand attach fs
7007  * volume LRU
7008  *
7009  * with demand attach fs, we attempt to soft detach(1)
7010  * volumes which have not been accessed in a long time
7011  * in order to speed up fileserver shutdown
7012  *
7013  * (1) by soft detach we mean a process very similar
7014  *     to VOffline, except the final state of the
7015  *     Volume will be VOL_STATE_PREATTACHED, instead
7016  *     of the usual VOL_STATE_UNATTACHED
7017  */
7018 #ifdef AFS_DEMAND_ATTACH_FS
7019
7020 /* implementation is reminiscent of a generational GC
7021  *
7022  * queue 0 is newly attached volumes. this queue is
7023  * sorted by attach timestamp
7024  *
7025  * queue 1 is volumes that have been around a bit
7026  * longer than queue 0. this queue is sorted by
7027  * attach timestamp
7028  *
7029  * queue 2 is volumes tha have been around the longest.
7030  * this queue is unsorted
7031  *
7032  * queue 3 is volumes that have been marked as
7033  * candidates for soft detachment. this queue is
7034  * unsorted
7035  */
7036 #define VLRU_GENERATIONS  3   /**< number of generations in VLRU */
7037 #define VLRU_QUEUES       5   /**< total number of VLRU queues */
7038
7039 /**
7040  * definition of a VLRU queue.
7041  */
7042 struct VLRU_q {
7043     volatile struct rx_queue q;
7044     volatile int len;
7045     volatile int busy;
7046     pthread_cond_t cv;
7047 };
7048
7049 /**
7050  * main VLRU data structure.
7051  */
7052 struct VLRU {
7053     struct VLRU_q q[VLRU_QUEUES];   /**< VLRU queues */
7054
7055     /* VLRU config */
7056     /** time interval (in seconds) between promotion passes for
7057      *  each young generation queue. */
7058     afs_uint32 promotion_interval[VLRU_GENERATIONS-1];
7059
7060     /** time interval (in seconds) between soft detach candidate
7061      *  scans for each generation queue.
7062      *
7063      *  scan_interval[VLRU_QUEUE_CANDIDATE] defines how frequently
7064      *  we perform a soft detach pass. */
7065     afs_uint32 scan_interval[VLRU_GENERATIONS+1];
7066
7067     /* scheduler state */
7068     int next_idx;                                       /**< next queue to receive attention */
7069     afs_uint32 last_promotion[VLRU_GENERATIONS-1];      /**< timestamp of last promotion scan */
7070     afs_uint32 last_scan[VLRU_GENERATIONS+1];           /**< timestamp of last detach scan */
7071
7072     int scanner_state;                                  /**< state of scanner thread */
7073     pthread_cond_t cv;                                  /**< state transition CV */
7074 };
7075
7076 /** global VLRU state */
7077 static struct VLRU volume_LRU;
7078
7079 /**
7080  * defined states for VLRU scanner thread.
7081  */
7082 typedef enum {
7083     VLRU_SCANNER_STATE_OFFLINE        = 0,    /**< vlru scanner thread is offline */
7084     VLRU_SCANNER_STATE_ONLINE         = 1,    /**< vlru scanner thread is online */
7085     VLRU_SCANNER_STATE_SHUTTING_DOWN  = 2,    /**< vlru scanner thread is shutting down */
7086     VLRU_SCANNER_STATE_PAUSING        = 3,    /**< vlru scanner thread is getting ready to pause */
7087     VLRU_SCANNER_STATE_PAUSED         = 4     /**< vlru scanner thread is paused */
7088 } vlru_thread_state_t;
7089
7090 /* vlru disk data header stuff */
7091 #define VLRU_DISK_MAGIC      0x7a8b9cad        /**< vlru disk entry magic number */
7092 #define VLRU_DISK_VERSION    1                 /**< vlru disk entry version number */
7093
7094 /** vlru default expiration time (for eventual fs state serialization of vlru data) */
7095 #define VLRU_DUMP_EXPIRATION_TIME   (60*60*24*7)  /* expire vlru data after 1 week */
7096
7097
7098 /** minimum volume inactivity (in seconds) before a volume becomes eligible for
7099  *  soft detachment. */
7100 static afs_uint32 VLRU_offline_thresh = VLRU_DEFAULT_OFFLINE_THRESH;
7101
7102 /** time interval (in seconds) between VLRU scanner thread soft detach passes. */
7103 static afs_uint32 VLRU_offline_interval = VLRU_DEFAULT_OFFLINE_INTERVAL;
7104
7105 /** maximum number of volumes to soft detach in a VLRU soft detach pass. */
7106 static afs_uint32 VLRU_offline_max = VLRU_DEFAULT_OFFLINE_MAX;
7107
7108 /** VLRU control flag.  non-zero value implies VLRU subsystem is activated. */
7109 static afs_uint32 VLRU_enabled = 1;
7110
7111 /* queue synchronization routines */
7112 static void VLRU_BeginExclusive_r(struct VLRU_q * q);
7113 static void VLRU_EndExclusive_r(struct VLRU_q * q);
7114 static void VLRU_Wait_r(struct VLRU_q * q);
7115
7116 /**
7117  * set VLRU subsystem tunable parameters.
7118  *
7119  * @param[in] option  tunable option to modify
7120  * @param[in] val     new value for tunable parameter
7121  *
7122  * @pre @c VInitVolumePackage2 has not yet been called.
7123  *
7124  * @post tunable parameter is modified
7125  *
7126  * @note DAFS only
7127  *
7128  * @note valid option parameters are:
7129  *    @arg @c VLRU_SET_THRESH
7130  *         set the period of inactivity after which
7131  *         volumes are eligible for soft detachment
7132  *    @arg @c VLRU_SET_INTERVAL
7133  *         set the time interval between calls
7134  *         to the volume LRU "garbage collector"
7135  *    @arg @c VLRU_SET_MAX
7136  *         set the max number of volumes to deallocate
7137  *         in one GC pass
7138  */
7139 void
7140 VLRU_SetOptions(int option, afs_uint32 val)
7141 {
7142     if (option == VLRU_SET_THRESH) {
7143         VLRU_offline_thresh = val;
7144     } else if (option == VLRU_SET_INTERVAL) {
7145         VLRU_offline_interval = val;
7146     } else if (option == VLRU_SET_MAX) {
7147         VLRU_offline_max = val;
7148     } else if (option == VLRU_SET_ENABLED) {
7149         VLRU_enabled = val;
7150     }
7151     VLRU_ComputeConstants();
7152 }
7153
7154 /**
7155  * compute VLRU internal timing parameters.
7156  *
7157  * @post VLRU scanner thread internal timing parameters are computed
7158  *
7159  * @note computes internal timing parameters based upon user-modifiable
7160  *       tunable parameters.
7161  *
7162  * @note DAFS only
7163  *
7164  * @internal volume package internal use only.
7165  */
7166 static void
7167 VLRU_ComputeConstants(void)
7168 {
7169     afs_uint32 factor = VLRU_offline_thresh / VLRU_offline_interval;
7170
7171     /* compute the candidate scan interval */
7172     volume_LRU.scan_interval[VLRU_QUEUE_CANDIDATE] = VLRU_offline_interval;
7173
7174     /* compute the promotion intervals */
7175     volume_LRU.promotion_interval[VLRU_QUEUE_NEW] = VLRU_offline_thresh * 2;
7176     volume_LRU.promotion_interval[VLRU_QUEUE_MID] = VLRU_offline_thresh * 4;
7177
7178     if (factor > 16) {
7179         /* compute the gen 0 scan interval */
7180         volume_LRU.scan_interval[VLRU_QUEUE_NEW] = VLRU_offline_thresh / 8;
7181     } else {
7182         /* compute the gen 0 scan interval */
7183         volume_LRU.scan_interval[VLRU_QUEUE_NEW] = VLRU_offline_interval * 2;
7184     }
7185 }
7186
7187 /**
7188  * initialize VLRU subsystem.
7189  *
7190  * @pre this function has not yet been called
7191  *
7192  * @post VLRU subsystem is initialized and VLRU scanner thread is starting
7193  *
7194  * @note DAFS only
7195  *
7196  * @internal volume package internal use only.
7197  */
7198 static void
7199 VInitVLRU(void)
7200 {
7201     pthread_t tid;
7202     pthread_attr_t attrs;
7203     int i;
7204
7205     if (!VLRU_enabled) {
7206         Log("VLRU: disabled\n");
7207         return;
7208     }
7209
7210     /* initialize each of the VLRU queues */
7211     for (i = 0; i < VLRU_QUEUES; i++) {
7212         queue_Init(&volume_LRU.q[i]);
7213         volume_LRU.q[i].len = 0;
7214         volume_LRU.q[i].busy = 0;
7215         opr_cv_init(&volume_LRU.q[i].cv);
7216     }
7217
7218     /* setup the timing constants */
7219     VLRU_ComputeConstants();
7220
7221     /* XXX put inside LogLevel check? */
7222     Log("VLRU: starting scanner with the following configuration parameters:\n");
7223     Log("VLRU:  offlining volumes after minimum of %d seconds of inactivity\n", VLRU_offline_thresh);
7224     Log("VLRU:  running VLRU soft detach pass every %d seconds\n", VLRU_offline_interval);
7225     Log("VLRU:  taking up to %d volumes offline per pass\n", VLRU_offline_max);
7226     Log("VLRU:  scanning generation 0 for inactive volumes every %d seconds\n", volume_LRU.scan_interval[0]);
7227     Log("VLRU:  scanning for promotion/demotion between generations 0 and 1 every %d seconds\n", volume_LRU.promotion_interval[0]);
7228     Log("VLRU:  scanning for promotion/demotion between generations 1 and 2 every %d seconds\n", volume_LRU.promotion_interval[1]);
7229
7230     /* start up the VLRU scanner */
7231     volume_LRU.scanner_state = VLRU_SCANNER_STATE_OFFLINE;
7232     if (programType == fileServer) {
7233         opr_cv_init(&volume_LRU.cv);
7234         opr_Verify(pthread_attr_init(&attrs) == 0);
7235         opr_Verify(pthread_attr_setdetachstate(&attrs,
7236                                                PTHREAD_CREATE_DETACHED) == 0);
7237         opr_Verify(pthread_create(&tid, &attrs,
7238                                   &VLRU_ScannerThread, NULL) == 0);
7239     }
7240 }
7241
7242 /**
7243  * initialize the VLRU-related fields of a newly allocated volume object.
7244  *
7245  * @param[in] vp  pointer to volume object
7246  *
7247  * @pre
7248  *    @arg @c VOL_LOCK is held.
7249  *    @arg volume object is not on a VLRU queue.
7250  *
7251  * @post VLRU fields are initialized to indicate that volume object is not
7252  *       currently registered with the VLRU subsystem
7253  *
7254  * @note DAFS only
7255  *
7256  * @internal volume package interal use only.
7257  */
7258 static void
7259 VLRU_Init_Node_r(Volume * vp)
7260 {
7261     if (!VLRU_enabled)
7262         return;
7263
7264     opr_Assert(queue_IsNotOnQueue(&vp->vlru));
7265     vp->vlru.idx = VLRU_QUEUE_INVALID;
7266 }
7267
7268 /**
7269  * add a volume object to a VLRU queue.
7270  *
7271  * @param[in] vp  pointer to volume object
7272  *
7273  * @pre
7274  *    @arg @c VOL_LOCK is held.
7275  *    @arg caller MUST hold a lightweight ref on @p vp.
7276  *    @arg caller MUST NOT hold exclusive ownership of the VLRU queue.
7277  *
7278  * @post the volume object is added to the appropriate VLRU queue
7279  *
7280  * @note if @c vp->vlru.idx contains the index of a valid VLRU queue,
7281  *       then the volume is added to that queue.  Otherwise, the value
7282  *       @c VLRU_QUEUE_NEW is stored into @c vp->vlru.idx and the
7283  *       volume is added to the NEW generation queue.
7284  *
7285  * @note @c VOL_LOCK may be dropped internally
7286  *
7287  * @note Volume state is temporarily set to @c VOL_STATE_VLRU_ADD
7288  *       during the add operation, and is restored to the previous
7289  *       state prior to return.
7290  *
7291  * @note DAFS only
7292  *
7293  * @internal volume package internal use only.
7294  */
7295 static void
7296 VLRU_Add_r(Volume * vp)
7297 {
7298     int idx;
7299     VolState state_save;
7300
7301     if (!VLRU_enabled)
7302         return;
7303
7304     if (queue_IsOnQueue(&vp->vlru))
7305         return;
7306
7307     state_save = VChangeState_r(vp, VOL_STATE_VLRU_ADD);
7308
7309     idx = vp->vlru.idx;
7310     if ((idx < 0) || (idx >= VLRU_QUEUE_INVALID)) {
7311         idx = VLRU_QUEUE_NEW;
7312     }
7313
7314     VLRU_Wait_r(&volume_LRU.q[idx]);
7315
7316     /* repeat check since VLRU_Wait_r may have dropped
7317      * the glock */
7318     if (queue_IsNotOnQueue(&vp->vlru)) {
7319         vp->vlru.idx = idx;
7320         queue_Prepend(&volume_LRU.q[idx], &vp->vlru);
7321         volume_LRU.q[idx].len++;
7322         V_attachFlags(vp) |= VOL_ON_VLRU;
7323         vp->stats.last_promote = FT_ApproxTime();
7324     }
7325
7326     VChangeState_r(vp, state_save);
7327 }
7328
7329 /**
7330  * delete a volume object from a VLRU queue.
7331  *
7332  * @param[in] vp  pointer to volume object
7333  *
7334  * @pre
7335  *    @arg @c VOL_LOCK is held.
7336  *    @arg caller MUST hold a lightweight ref on @p vp.
7337  *    @arg caller MUST NOT hold exclusive ownership of the VLRU queue.
7338  *
7339  * @post volume object is removed from the VLRU queue
7340  *
7341  * @note @c VOL_LOCK may be dropped internally
7342  *
7343  * @note DAFS only
7344  *
7345  * @todo We should probably set volume state to something exlcusive
7346  *       (as @c VLRU_Add_r does) prior to dropping @c VOL_LOCK.
7347  *
7348  * @internal volume package internal use only.
7349  */
7350 static void
7351 VLRU_Delete_r(Volume * vp)
7352 {
7353     int idx;
7354
7355     if (!VLRU_enabled)
7356         return;
7357
7358     if (queue_IsNotOnQueue(&vp->vlru))
7359         return;
7360
7361     /* handle races */
7362     do {
7363       idx = vp->vlru.idx;
7364       if (idx == VLRU_QUEUE_INVALID)
7365           return;
7366       VLRU_Wait_r(&volume_LRU.q[idx]);
7367     } while (idx != vp->vlru.idx);
7368
7369     /* now remove from the VLRU and update
7370      * the appropriate counter */
7371     queue_Remove(&vp->vlru);
7372     volume_LRU.q[idx].len--;
7373     vp->vlru.idx = VLRU_QUEUE_INVALID;
7374     V_attachFlags(vp) &= ~(VOL_ON_VLRU);
7375 }
7376
7377 /**
7378  * tell the VLRU subsystem that a volume was just accessed.
7379  *
7380  * @param[in] vp  pointer to volume object
7381  *
7382  * @pre
7383  *    @arg @c VOL_LOCK is held
7384  *    @arg caller MUST hold a lightweight ref on @p vp
7385  *    @arg caller MUST NOT hold exclusive ownership of any VLRU queue
7386  *
7387  * @post volume VLRU access statistics are updated.  If the volume was on
7388  *       the VLRU soft detach candidate queue, it is moved to the NEW
7389  *       generation queue.
7390  *
7391  * @note @c VOL_LOCK may be dropped internally
7392  *
7393  * @note DAFS only
7394  *
7395  * @internal volume package internal use only.
7396  */
7397 static void
7398 VLRU_UpdateAccess_r(Volume * vp)
7399 {
7400     Volume * rvp = NULL;
7401
7402     if (!VLRU_enabled)
7403         return;
7404
7405     if (queue_IsNotOnQueue(&vp->vlru))
7406         return;
7407
7408     opr_Assert(V_attachFlags(vp) & VOL_ON_VLRU);
7409
7410     /* update the access timestamp */
7411     vp->stats.last_get = FT_ApproxTime();
7412
7413     /*
7414      * if the volume is on the soft detach candidate
7415      * list, we need to safely move it back to a
7416      * regular generation.  this has to be done
7417      * carefully so we don't race against the scanner
7418      * thread.
7419      */
7420
7421     /* if this volume is on the soft detach candidate queue,
7422      * then grab exclusive access to the necessary queues */
7423     if (vp->vlru.idx == VLRU_QUEUE_CANDIDATE) {
7424         rvp = vp;
7425         VCreateReservation_r(rvp);
7426
7427         VLRU_Wait_r(&volume_LRU.q[VLRU_QUEUE_NEW]);
7428         VLRU_BeginExclusive_r(&volume_LRU.q[VLRU_QUEUE_NEW]);
7429         VLRU_Wait_r(&volume_LRU.q[VLRU_QUEUE_CANDIDATE]);
7430         VLRU_BeginExclusive_r(&volume_LRU.q[VLRU_QUEUE_CANDIDATE]);
7431     }
7432
7433     /* make sure multiple threads don't race to update */
7434     if (vp->vlru.idx == VLRU_QUEUE_CANDIDATE) {
7435         VLRU_SwitchQueues(vp, VLRU_QUEUE_NEW, 1);
7436     }
7437
7438     if (rvp) {
7439       VLRU_EndExclusive_r(&volume_LRU.q[VLRU_QUEUE_CANDIDATE]);
7440       VLRU_EndExclusive_r(&volume_LRU.q[VLRU_QUEUE_NEW]);
7441       VCancelReservation_r(rvp);
7442     }
7443 }
7444
7445 /**
7446  * switch a volume between two VLRU queues.
7447  *
7448  * @param[in] vp       pointer to volume object
7449  * @param[in] new_idx  index of VLRU queue onto which the volume will be moved
7450  * @param[in] append   controls whether the volume will be appended or
7451  *                     prepended to the queue.  A nonzero value means it will
7452  *                     be appended; zero means it will be prepended.
7453  *
7454  * @pre The new (and old, if applicable) queue(s) must either be owned
7455  *      exclusively by the calling thread for asynchronous manipulation,
7456  *      or the queue(s) must be quiescent and VOL_LOCK must be held.
7457  *      Please see VLRU_BeginExclusive_r, VLRU_EndExclusive_r and VLRU_Wait_r
7458  *      for further details of the queue asynchronous processing mechanism.
7459  *
7460  * @post If the volume object was already on a VLRU queue, it is
7461  *       removed from the queue.  Depending on the value of the append
7462  *       parameter, the volume object is either appended or prepended
7463  *       to the VLRU queue referenced by the new_idx parameter.
7464  *
7465  * @note DAFS only
7466  *
7467  * @see VLRU_BeginExclusive_r
7468  * @see VLRU_EndExclusive_r
7469  * @see VLRU_Wait_r
7470  *
7471  * @internal volume package internal use only.
7472  */
7473 static void
7474 VLRU_SwitchQueues(Volume * vp, int new_idx, int append)
7475 {
7476     if (queue_IsNotOnQueue(&vp->vlru))
7477         return;
7478
7479     queue_Remove(&vp->vlru);
7480     volume_LRU.q[vp->vlru.idx].len--;
7481
7482     /* put the volume back on the correct generational queue */
7483     if (append) {
7484         queue_Append(&volume_LRU.q[new_idx], &vp->vlru);
7485     } else {
7486         queue_Prepend(&volume_LRU.q[new_idx], &vp->vlru);
7487     }
7488
7489     volume_LRU.q[new_idx].len++;
7490     vp->vlru.idx = new_idx;
7491 }
7492
7493 /**
7494  * VLRU background thread.
7495  *
7496  * The VLRU Scanner Thread is responsible for periodically scanning through
7497  * each VLRU queue looking for volumes which should be moved to another
7498  * queue, or soft detached.
7499  *
7500  * @param[in] args  unused thread arguments parameter
7501  *
7502  * @return unused thread return value
7503  *    @retval NULL always
7504  *
7505  * @internal volume package internal use only.
7506  */
7507 static void *
7508 VLRU_ScannerThread(void * args)
7509 {
7510     afs_uint32 now, min_delay, delay;
7511     int i, min_idx, min_op, overdue, state;
7512
7513     /* set t=0 for promotion cycle to be
7514      * fileserver startup */
7515     now = FT_ApproxTime();
7516     for (i=0; i < VLRU_GENERATIONS-1; i++) {
7517         volume_LRU.last_promotion[i] = now;
7518     }
7519
7520     /* don't start the scanner until VLRU_offline_thresh
7521      * plus a small delay for VInitVolumePackage2 to finish
7522      * has gone by */
7523
7524     sleep(VLRU_offline_thresh + 60);
7525
7526     /* set t=0 for scan cycle to be now */
7527     now = FT_ApproxTime();
7528     for (i=0; i < VLRU_GENERATIONS+1; i++) {
7529         volume_LRU.last_scan[i] = now;
7530     }
7531
7532     VOL_LOCK;
7533     if (volume_LRU.scanner_state == VLRU_SCANNER_STATE_OFFLINE) {
7534         volume_LRU.scanner_state = VLRU_SCANNER_STATE_ONLINE;
7535     }
7536
7537     while ((state = volume_LRU.scanner_state) != VLRU_SCANNER_STATE_SHUTTING_DOWN) {
7538         /* check to see if we've been asked to pause */
7539         if (volume_LRU.scanner_state == VLRU_SCANNER_STATE_PAUSING) {
7540             volume_LRU.scanner_state = VLRU_SCANNER_STATE_PAUSED;
7541             opr_cv_broadcast(&volume_LRU.cv);
7542             do {
7543                 VOL_CV_WAIT(&volume_LRU.cv);
7544             } while (volume_LRU.scanner_state == VLRU_SCANNER_STATE_PAUSED);
7545         }
7546
7547         /* scheduling can happen outside the glock */
7548         VOL_UNLOCK;
7549
7550         /* figure out what is next on the schedule */
7551
7552         /* figure out a potential schedule for the new generation first */
7553         overdue = 0;
7554         min_delay = volume_LRU.scan_interval[0] + volume_LRU.last_scan[0] - now;
7555         min_idx = 0;
7556         min_op = 0;
7557         if (min_delay > volume_LRU.scan_interval[0]) {
7558             /* unsigned overflow -- we're overdue to run this scan */
7559             min_delay = 0;
7560             overdue = 1;
7561         }
7562
7563         /* if we're not overdue for gen 0, figure out schedule for candidate gen */
7564         if (!overdue) {
7565             i = VLRU_QUEUE_CANDIDATE;
7566             delay = volume_LRU.scan_interval[i] + volume_LRU.last_scan[i] - now;
7567             if (delay < min_delay) {
7568                 min_delay = delay;
7569                 min_idx = i;
7570             }
7571             if (delay > volume_LRU.scan_interval[i]) {
7572                 /* unsigned overflow -- we're overdue to run this scan */
7573                 min_delay = 0;
7574                 min_idx = i;
7575                 overdue = 1;
7576             }
7577         }
7578
7579         /* if we're still not overdue for something, figure out schedules for promotions */
7580         for (i=0; !overdue && i < VLRU_GENERATIONS-1; i++) {
7581             delay = volume_LRU.promotion_interval[i] + volume_LRU.last_promotion[i] - now;
7582             if (delay < min_delay) {
7583                 min_delay = delay;
7584                 min_idx = i;
7585                 min_op = 1;
7586             }
7587             if (delay > volume_LRU.promotion_interval[i]) {
7588                 /* unsigned overflow -- we're overdue to run this promotion */
7589                 min_delay = 0;
7590                 min_idx = i;
7591                 min_op = 1;
7592                 overdue = 1;
7593                 break;
7594             }
7595         }
7596
7597         /* sleep as needed */
7598         if (min_delay) {
7599             sleep(min_delay);
7600         }
7601
7602         /* do whatever is next */
7603         VOL_LOCK;
7604         if (min_op) {
7605             VLRU_Promote_r(min_idx);
7606             VLRU_Demote_r(min_idx+1);
7607         } else {
7608             VLRU_Scan_r(min_idx);
7609         }
7610         now = FT_ApproxTime();
7611     }
7612
7613     Log("VLRU scanner asked to go offline (scanner_state=%d)\n", state);
7614
7615     /* signal that scanner is down */
7616     volume_LRU.scanner_state = VLRU_SCANNER_STATE_OFFLINE;
7617     opr_cv_broadcast(&volume_LRU.cv);
7618     VOL_UNLOCK;
7619     return NULL;
7620 }
7621
7622 /**
7623  * promote volumes from one VLRU generation to the next.
7624  *
7625  * This routine scans a VLRU generation looking for volumes which are
7626  * eligible to be promoted to the next generation.  All volumes which
7627  * meet the eligibility requirement are promoted.
7628  *
7629  * Promotion eligibility is based upon meeting both of the following
7630  * requirements:
7631  *
7632  *    @arg The volume has been accessed since the last promotion:
7633  *         @c (vp->stats.last_get >= vp->stats.last_promote)
7634  *    @arg The last promotion occurred at least
7635  *         @c volume_LRU.promotion_interval[idx] seconds ago
7636  *
7637  * As a performance optimization, promotions are "globbed".  In other
7638  * words, we promote arbitrarily large contiguous sublists of elements
7639  * as one operation.
7640  *
7641  * @param[in] idx  VLRU queue index to scan
7642  *
7643  * @note DAFS only
7644  *
7645  * @internal VLRU internal use only.
7646  */
7647 static void
7648 VLRU_Promote_r(int idx)
7649 {
7650     int len, chaining, promote;
7651     afs_uint32 now, thresh;
7652     struct rx_queue *qp, *nqp;
7653     Volume * vp, *start = NULL, *end = NULL;
7654
7655     /* get exclusive access to two chains, and drop the glock */
7656     VLRU_Wait_r(&volume_LRU.q[idx]);
7657     VLRU_BeginExclusive_r(&volume_LRU.q[idx]);
7658     VLRU_Wait_r(&volume_LRU.q[idx+1]);
7659     VLRU_BeginExclusive_r(&volume_LRU.q[idx+1]);
7660     VOL_UNLOCK;
7661
7662     thresh = volume_LRU.promotion_interval[idx];
7663     now = FT_ApproxTime();
7664
7665     len = chaining = 0;
7666     for (queue_ScanBackwards(&volume_LRU.q[idx], qp, nqp, rx_queue)) {
7667         vp = (Volume *)((char *)qp - offsetof(Volume, vlru));
7668         promote = (((vp->stats.last_promote + thresh) <= now) &&
7669                    (vp->stats.last_get >= vp->stats.last_promote));
7670
7671         if (chaining) {
7672             if (promote) {
7673                 vp->vlru.idx++;
7674                 len++;
7675                 start = vp;
7676             } else {
7677                 /* promote and prepend chain */
7678                 queue_MoveChainAfter(&volume_LRU.q[idx+1], &start->vlru, &end->vlru);
7679                 chaining = 0;
7680             }
7681         } else {
7682             if (promote) {
7683                 vp->vlru.idx++;
7684                 len++;
7685                 chaining = 1;
7686                 start = end = vp;
7687             }
7688         }
7689     }
7690
7691     if (chaining) {
7692         /* promote and prepend */
7693         queue_MoveChainAfter(&volume_LRU.q[idx+1], &start->vlru, &end->vlru);
7694     }
7695
7696     if (len) {
7697         volume_LRU.q[idx].len -= len;
7698         volume_LRU.q[idx+1].len += len;
7699     }
7700
7701     /* release exclusive access to the two chains */
7702     VOL_LOCK;
7703     volume_LRU.last_promotion[idx] = now;
7704     VLRU_EndExclusive_r(&volume_LRU.q[idx+1]);
7705     VLRU_EndExclusive_r(&volume_LRU.q[idx]);
7706 }
7707
7708 /* run the demotions */
7709 static void
7710 VLRU_Demote_r(int idx)
7711 {
7712     Error ec;
7713     int len, chaining, demote;
7714     afs_uint32 now, thresh;
7715     struct rx_queue *qp, *nqp;
7716     Volume * vp, *start = NULL, *end = NULL;
7717     Volume ** salv_flag_vec = NULL;
7718     int salv_vec_offset = 0;
7719
7720     opr_Assert(idx == VLRU_QUEUE_MID || idx == VLRU_QUEUE_OLD);
7721
7722     /* get exclusive access to two chains, and drop the glock */
7723     VLRU_Wait_r(&volume_LRU.q[idx-1]);
7724     VLRU_BeginExclusive_r(&volume_LRU.q[idx-1]);
7725     VLRU_Wait_r(&volume_LRU.q[idx]);
7726     VLRU_BeginExclusive_r(&volume_LRU.q[idx]);
7727     VOL_UNLOCK;
7728
7729     /* no big deal if this allocation fails */
7730     if (volume_LRU.q[idx].len) {
7731         salv_flag_vec = malloc(volume_LRU.q[idx].len * sizeof(Volume *));
7732     }
7733
7734     now = FT_ApproxTime();
7735     thresh = volume_LRU.promotion_interval[idx-1];
7736
7737     len = chaining = 0;
7738     for (queue_ScanBackwards(&volume_LRU.q[idx], qp, nqp, rx_queue)) {
7739         vp = (Volume *)((char *)qp - offsetof(Volume, vlru));
7740         demote = (((vp->stats.last_promote + thresh) <= now) &&
7741                   (vp->stats.last_get < (now - thresh)));
7742
7743         /* we now do volume update list DONT_SALVAGE flag setting during
7744          * demotion passes */
7745         if (salv_flag_vec &&
7746             !(V_attachFlags(vp) & VOL_HDR_DONTSALV) &&
7747             demote &&
7748             (vp->updateTime < (now - SALVAGE_INTERVAL)) &&
7749             (V_attachState(vp) == VOL_STATE_ATTACHED)) {
7750             salv_flag_vec[salv_vec_offset++] = vp;
7751             VCreateReservation_r(vp);
7752         }
7753
7754         if (chaining) {
7755             if (demote) {
7756                 vp->vlru.idx--;
7757                 len++;
7758                 start = vp;
7759             } else {
7760                 /* demote and append chain */
7761                 queue_MoveChainBefore(&volume_LRU.q[idx-1], &start->vlru, &end->vlru);
7762                 chaining = 0;
7763             }
7764         } else {
7765             if (demote) {
7766                 vp->vlru.idx--;
7767                 len++;
7768                 chaining = 1;
7769                 start = end = vp;
7770             }
7771         }
7772     }
7773
7774     if (chaining) {
7775         queue_MoveChainBefore(&volume_LRU.q[idx-1], &start->vlru, &end->vlru);
7776     }
7777
7778     if (len) {
7779         volume_LRU.q[idx].len -= len;
7780         volume_LRU.q[idx-1].len += len;
7781     }
7782
7783     /* release exclusive access to the two chains */
7784     VOL_LOCK;
7785     VLRU_EndExclusive_r(&volume_LRU.q[idx]);
7786     VLRU_EndExclusive_r(&volume_LRU.q[idx-1]);
7787
7788     /* now go back and set the DONT_SALVAGE flags as appropriate */
7789     if (salv_flag_vec) {
7790         int i;
7791         for (i = 0; i < salv_vec_offset; i++) {
7792             vp = salv_flag_vec[i];
7793             if (!(V_attachFlags(vp) & VOL_HDR_DONTSALV) &&
7794                 (vp->updateTime < (now - SALVAGE_INTERVAL)) &&
7795                 (V_attachState(vp) == VOL_STATE_ATTACHED)) {
7796                 ec = VHold_r(vp);
7797                 if (!ec) {
7798                     V_attachFlags(vp) |= VOL_HDR_DONTSALV;
7799                     V_dontSalvage(vp) = DONT_SALVAGE;
7800                     VUpdateVolume_r(&ec, vp, 0);
7801                     VPutVolume_r(vp);
7802                 }
7803             }
7804             VCancelReservation_r(vp);
7805         }
7806         free(salv_flag_vec);
7807     }
7808 }
7809
7810 /* run a pass of the VLRU GC scanner */
7811 static void
7812 VLRU_Scan_r(int idx)
7813 {
7814     afs_uint32 now, thresh;
7815     struct rx_queue *qp, *nqp;
7816     Volume * vp;
7817     int i, locked = 1;
7818
7819     opr_Assert(idx == VLRU_QUEUE_NEW || idx == VLRU_QUEUE_CANDIDATE);
7820
7821     /* gain exclusive access to the idx VLRU */
7822     VLRU_Wait_r(&volume_LRU.q[idx]);
7823     VLRU_BeginExclusive_r(&volume_LRU.q[idx]);
7824
7825     if (idx != VLRU_QUEUE_CANDIDATE) {
7826         /* gain exclusive access to the candidate VLRU */
7827         VLRU_Wait_r(&volume_LRU.q[VLRU_QUEUE_CANDIDATE]);
7828         VLRU_BeginExclusive_r(&volume_LRU.q[VLRU_QUEUE_CANDIDATE]);
7829     }
7830
7831     now = FT_ApproxTime();
7832     thresh = now - VLRU_offline_thresh;
7833
7834     /* perform candidate selection and soft detaching */
7835     if (idx == VLRU_QUEUE_CANDIDATE) {
7836         /* soft detach some volumes from the candidate pool */
7837         VOL_UNLOCK;
7838         locked = 0;
7839
7840         for (i=0,queue_ScanBackwards(&volume_LRU.q[idx], qp, nqp, rx_queue)) {
7841             vp = (Volume *)((char *)qp - offsetof(Volume, vlru));
7842             if (i >= VLRU_offline_max) {
7843                 break;
7844             }
7845             /* check timestamp to see if it's a candidate for soft detaching */
7846             if (vp->stats.last_get <= thresh) {
7847                 VOL_LOCK;
7848                 if (VCheckSoftDetach(vp, thresh))
7849                     i++;
7850                 VOL_UNLOCK;
7851             }
7852         }
7853     } else {
7854         /* scan for volumes to become soft detach candidates */
7855         for (i=1,queue_ScanBackwards(&volume_LRU.q[idx], qp, nqp, rx_queue),i++) {
7856             vp = (Volume *)((char *)qp - offsetof(Volume, vlru));
7857
7858             /* check timestamp to see if it's a candidate for soft detaching */
7859             if (vp->stats.last_get <= thresh) {
7860                 VCheckSoftDetachCandidate(vp, thresh);
7861             }
7862
7863             if (!(i&0x7f)) {   /* lock coarsening optimization */
7864                 VOL_UNLOCK;
7865                 pthread_yield();
7866                 VOL_LOCK;
7867             }
7868         }
7869     }
7870
7871     /* relinquish exclusive access to the VLRU chains */
7872     if (!locked) {
7873         VOL_LOCK;
7874     }
7875     volume_LRU.last_scan[idx] = now;
7876     if (idx != VLRU_QUEUE_CANDIDATE) {
7877         VLRU_EndExclusive_r(&volume_LRU.q[VLRU_QUEUE_CANDIDATE]);
7878     }
7879     VLRU_EndExclusive_r(&volume_LRU.q[idx]);
7880 }
7881
7882 /* check whether volume is safe to soft detach
7883  * caller MUST NOT hold a ref count on vp */
7884 static int
7885 VCheckSoftDetach(Volume * vp, afs_uint32 thresh)
7886 {
7887     int ret=0;
7888
7889     if (vp->nUsers || vp->nWaiters)
7890         return 0;
7891
7892     if (vp->stats.last_get <= thresh) {
7893         ret = VSoftDetachVolume_r(vp, thresh);
7894     }
7895
7896     return ret;
7897 }
7898
7899 /* check whether volume should be made a
7900  * soft detach candidate */
7901 static int
7902 VCheckSoftDetachCandidate(Volume * vp, afs_uint32 thresh)
7903 {
7904     int idx, ret = 0;
7905     if (vp->nUsers || vp->nWaiters)
7906         return 0;
7907
7908     idx = vp->vlru.idx;
7909
7910     opr_Assert(idx == VLRU_QUEUE_NEW);
7911
7912     if (vp->stats.last_get <= thresh) {
7913         /* move to candidate pool */
7914         queue_Remove(&vp->vlru);
7915         volume_LRU.q[VLRU_QUEUE_NEW].len--;
7916         queue_Prepend(&volume_LRU.q[VLRU_QUEUE_CANDIDATE], &vp->vlru);
7917         vp->vlru.idx = VLRU_QUEUE_CANDIDATE;
7918         volume_LRU.q[VLRU_QUEUE_CANDIDATE].len++;
7919         ret = 1;
7920     }
7921
7922     return ret;
7923 }
7924
7925
7926 /* begin exclusive access on VLRU */
7927 static void
7928 VLRU_BeginExclusive_r(struct VLRU_q * q)
7929 {
7930     opr_Assert(q->busy == 0);
7931     q->busy = 1;
7932 }
7933
7934 /* end exclusive access on VLRU */
7935 static void
7936 VLRU_EndExclusive_r(struct VLRU_q * q)
7937 {
7938     opr_Assert(q->busy);
7939     q->busy = 0;
7940     opr_cv_broadcast(&q->cv);
7941 }
7942
7943 /* wait for another thread to end exclusive access on VLRU */
7944 static void
7945 VLRU_Wait_r(struct VLRU_q * q)
7946 {
7947     while(q->busy) {
7948         VOL_CV_WAIT(&q->cv);
7949     }
7950 }
7951
7952 /* demand attach fs
7953  * volume soft detach
7954  *
7955  * caller MUST NOT hold a ref count on vp */
7956 static int
7957 VSoftDetachVolume_r(Volume * vp, afs_uint32 thresh)
7958 {
7959     afs_uint32 ts_save;
7960     int ret = 0;
7961
7962     opr_Assert(vp->vlru.idx == VLRU_QUEUE_CANDIDATE);
7963
7964     ts_save = vp->stats.last_get;
7965     if (ts_save > thresh)
7966         return 0;
7967
7968     if (vp->nUsers || vp->nWaiters)
7969         return 0;
7970
7971     if (VIsExclusiveState(V_attachState(vp))) {
7972         return 0;
7973     }
7974
7975     switch (V_attachState(vp)) {
7976     case VOL_STATE_UNATTACHED:
7977     case VOL_STATE_PREATTACHED:
7978     case VOL_STATE_ERROR:
7979     case VOL_STATE_GOING_OFFLINE:
7980     case VOL_STATE_SHUTTING_DOWN:
7981     case VOL_STATE_SALVAGING:
7982     case VOL_STATE_DELETED:
7983         volume_LRU.q[vp->vlru.idx].len--;
7984
7985         /* create and cancel a reservation to
7986          * give the volume an opportunity to
7987          * be deallocated */
7988         VCreateReservation_r(vp);
7989         queue_Remove(&vp->vlru);
7990         vp->vlru.idx = VLRU_QUEUE_INVALID;
7991         V_attachFlags(vp) &= ~(VOL_ON_VLRU);
7992         VCancelReservation_r(vp);
7993         return 0;
7994     default:
7995         break;
7996     }
7997
7998     /* hold the volume and take it offline.
7999      * no need for reservations, as VHold_r
8000      * takes care of that internally. */
8001     if (VHold_r(vp) == 0) {
8002         /* vhold drops the glock, so now we should
8003          * check to make sure we aren't racing against
8004          * other threads.  if we are racing, offlining vp
8005          * would be wasteful, and block the scanner for a while
8006          */
8007         if (vp->nWaiters ||
8008             (vp->nUsers > 1) ||
8009             (vp->shuttingDown) ||
8010             (vp->goingOffline) ||
8011             (vp->stats.last_get != ts_save)) {
8012             /* looks like we're racing someone else. bail */
8013             VPutVolume_r(vp);
8014             vp = NULL;
8015         } else {
8016             /* pull it off the VLRU */
8017             opr_Assert(vp->vlru.idx == VLRU_QUEUE_CANDIDATE);
8018             volume_LRU.q[VLRU_QUEUE_CANDIDATE].len--;
8019             queue_Remove(&vp->vlru);
8020             vp->vlru.idx = VLRU_QUEUE_INVALID;
8021             V_attachFlags(vp) &= ~(VOL_ON_VLRU);
8022
8023             /* take if offline */
8024             VOffline_r(vp, "volume has been soft detached");
8025
8026             /* invalidate the volume header cache */
8027             FreeVolumeHeader(vp);
8028
8029             /* update stats */
8030             IncUInt64(&VStats.soft_detaches);
8031             vp->stats.soft_detaches++;
8032
8033             /* put in pre-attached state so demand
8034              * attacher can work on it */
8035             VChangeState_r(vp, VOL_STATE_PREATTACHED);
8036             ret = 1;
8037         }
8038     }
8039     return ret;
8040 }
8041 #endif /* AFS_DEMAND_ATTACH_FS */
8042
8043
8044 /***************************************************/
8045 /* Volume Header Cache routines                    */
8046 /***************************************************/
8047
8048 /**
8049  * volume header cache.
8050  */
8051 struct volume_hdr_LRU_t volume_hdr_LRU;
8052
8053 /**
8054  * initialize the volume header cache.
8055  *
8056  * @param[in] howMany  number of header cache entries to preallocate
8057  *
8058  * @pre VOL_LOCK held.  Function has never been called before.
8059  *
8060  * @post howMany cache entries are allocated, initialized, and added
8061  *       to the LRU list.  Header cache statistics are initialized.
8062  *
8063  * @note only applicable to fileServer program type.  Should only be
8064  *       called once during volume package initialization.
8065  *
8066  * @internal volume package internal use only.
8067  */
8068 static void
8069 VInitVolumeHeaderCache(afs_uint32 howMany)
8070 {
8071     struct volHeader *hp;
8072     if (programType != fileServer)
8073         return;
8074     queue_Init(&volume_hdr_LRU);
8075     volume_hdr_LRU.stats.free = 0;
8076     volume_hdr_LRU.stats.used = howMany;
8077     volume_hdr_LRU.stats.attached = 0;
8078     hp = (struct volHeader *)(calloc(howMany, sizeof(struct volHeader)));
8079     opr_Assert(hp != NULL);
8080
8081     while (howMany--)
8082         /* We are using ReleaseVolumeHeader to initialize the values on the header list
8083          * to ensure they have the right values
8084          */
8085         ReleaseVolumeHeader(hp++);
8086 }
8087
8088 /* get a volume header off of the volume header LRU.
8089  *
8090  * @return volume header
8091  *  @retval NULL no usable volume header is available on the LRU
8092  *
8093  * @pre VOL_LOCK held
8094  *
8095  * @post for DAFS, if the returned header is associated with a volume, that
8096  *       volume is NOT in an exclusive state
8097  *
8098  * @internal volume package internal use only.
8099  */
8100 #ifdef AFS_DEMAND_ATTACH_FS
8101 static struct volHeader*
8102 GetVolHeaderFromLRU(void)
8103 {
8104     struct volHeader *hd = NULL, *qh, *nqh;
8105     /* Usually, a volume in an exclusive state will not have its header on
8106      * the LRU. However, it is possible for this to occur when a salvage
8107      * request is received over FSSYNC, and possibly in other corner cases.
8108      * So just skip over headers whose volumes are in an exclusive state. We
8109      * could VWaitExclusiveState_r instead, but not waiting is faster and
8110      * easier to do */
8111     for (queue_Scan(&volume_hdr_LRU, qh, nqh, volHeader)) {
8112         if (!qh->back || !VIsExclusiveState(V_attachState(qh->back))) {
8113             queue_Remove(qh);
8114             hd = qh;
8115             break;
8116         }
8117     }
8118     return hd;
8119 }
8120 #else /* AFS_DEMAND_ATTACH_FS */
8121 static struct volHeader*
8122 GetVolHeaderFromLRU(void)
8123 {
8124     struct volHeader *hd = NULL;
8125     if (queue_IsNotEmpty(&volume_hdr_LRU)) {
8126         hd = queue_First(&volume_hdr_LRU, volHeader);
8127         queue_Remove(hd);
8128     }
8129     return hd;
8130 }
8131 #endif /* !AFS_DEMAND_ATTACH_FS */
8132
8133 /**
8134  * get a volume header and attach it to the volume object.
8135  *
8136  * @param[in] vp  pointer to volume object
8137  *
8138  * @return cache entry status
8139  *    @retval 0  volume header was newly attached; cache data is invalid
8140  *    @retval 1  volume header was previously attached; cache data is valid
8141  *
8142  * @pre VOL_LOCK held.  For DAFS, lightweight ref must be held on volume object.
8143  *
8144  * @post volume header attached to volume object.  if necessary, header cache
8145  *       entry on LRU is synchronized to disk.  Header is removed from LRU list.
8146  *
8147  * @note VOL_LOCK may be dropped
8148  *
8149  * @warning this interface does not load header data from disk.  it merely
8150  *          attaches a header object to the volume object, and may sync the old
8151  *          header cache data out to disk in the process.
8152  *
8153  * @internal volume package internal use only.
8154  */
8155 static int
8156 GetVolumeHeader(Volume * vp)
8157 {
8158     Error error;
8159     struct volHeader *hd;
8160     int old;
8161     static int everLogged = 0;
8162
8163 #ifdef AFS_DEMAND_ATTACH_FS
8164     VolState vp_save = 0, back_save = 0;
8165
8166     /* XXX debug 9/19/05 we've apparently got
8167      * a ref counting bug somewhere that's
8168      * breaking the nUsers == 0 => header on LRU
8169      * assumption */
8170     if (vp->header && queue_IsNotOnQueue(vp->header)) {
8171         Log("nUsers == 0, but header not on LRU\n");
8172         return 1;
8173     }
8174 #endif
8175
8176     old = (vp->header != NULL); /* old == volume already has a header */
8177
8178     if (programType != fileServer) {
8179         /* for volume utilities, we allocate volHeaders as needed */
8180         if (!vp->header) {
8181             hd = calloc(1, sizeof(*vp->header));
8182             opr_Assert(hd != NULL);
8183             vp->header = hd;
8184             hd->back = vp;
8185 #ifdef AFS_DEMAND_ATTACH_FS
8186             V_attachFlags(vp) |= VOL_HDR_ATTACHED;
8187 #endif
8188         }
8189     } else {
8190         /* for the fileserver, we keep a volume header cache */
8191         if (old) {
8192             /* the header we previously dropped in the lru is
8193              * still available. pull it off the lru and return */
8194             hd = vp->header;
8195             queue_Remove(hd);
8196             opr_Assert(hd->back == vp);
8197 #ifdef AFS_DEMAND_ATTACH_FS
8198             V_attachFlags(vp) &= ~(VOL_HDR_IN_LRU);
8199 #endif
8200         } else {
8201             hd = GetVolHeaderFromLRU();
8202             if (!hd) {
8203                 /* LRU is empty, so allocate a new volHeader
8204                  * this is probably indicative of a leak, so let the user know */
8205                 hd = calloc(1, sizeof(struct volHeader));
8206                 opr_Assert(hd != NULL);
8207                 if (!everLogged) {
8208                     Log("****Allocated more volume headers, probably leak****\n");
8209                     everLogged = 1;
8210                 }
8211                 volume_hdr_LRU.stats.free++;
8212             }
8213             if (hd->back) {
8214                 /* this header used to belong to someone else.
8215                  * we'll need to check if the header needs to
8216                  * be sync'd out to disk */
8217
8218 #ifdef AFS_DEMAND_ATTACH_FS
8219                 /* GetVolHeaderFromLRU had better not give us back a header
8220                  * with a volume in exclusive state... */
8221                 opr_Assert(!VIsExclusiveState(V_attachState(hd->back)));
8222 #endif
8223
8224                 if (hd->diskstuff.inUse) {
8225                     /* volume was in use, so we'll need to sync
8226                      * its header to disk */
8227
8228 #ifdef AFS_DEMAND_ATTACH_FS
8229                     back_save = VChangeState_r(hd->back, VOL_STATE_UPDATING);
8230                     vp_save = VChangeState_r(vp, VOL_STATE_HDR_ATTACHING);
8231                     VCreateReservation_r(hd->back);
8232                     VOL_UNLOCK;
8233 #endif
8234
8235                     WriteVolumeHeader_r(&error, hd->back);
8236                     /* Ignore errors; catch them later */
8237
8238 #ifdef AFS_DEMAND_ATTACH_FS
8239                     VOL_LOCK;
8240 #endif
8241                 }
8242
8243                 hd->back->header = NULL;
8244 #ifdef AFS_DEMAND_ATTACH_FS
8245                 V_attachFlags(hd->back) &= ~(VOL_HDR_ATTACHED | VOL_HDR_LOADED | VOL_HDR_IN_LRU);
8246
8247                 if (hd->diskstuff.inUse) {
8248                     VChangeState_r(hd->back, back_save);
8249                     VCancelReservation_r(hd->back);
8250                     VChangeState_r(vp, vp_save);
8251                 }
8252 #endif
8253             } else {
8254                 volume_hdr_LRU.stats.attached++;
8255             }
8256             hd->back = vp;
8257             vp->header = hd;
8258 #ifdef AFS_DEMAND_ATTACH_FS
8259             V_attachFlags(vp) |= VOL_HDR_ATTACHED;
8260 #endif
8261         }
8262         volume_hdr_LRU.stats.free--;
8263         volume_hdr_LRU.stats.used++;
8264     }
8265     IncUInt64(&VStats.hdr_gets);
8266 #ifdef AFS_DEMAND_ATTACH_FS
8267     IncUInt64(&vp->stats.hdr_gets);
8268     vp->stats.last_hdr_get = FT_ApproxTime();
8269 #endif
8270     return old;
8271 }
8272
8273
8274 /**
8275  * make sure volume header is attached and contains valid cache data.
8276  *
8277  * @param[out] ec  outbound error code
8278  * @param[in]  vp  pointer to volume object
8279  *
8280  * @pre VOL_LOCK held.  For DAFS, lightweight ref held on vp.
8281  *
8282  * @post header cache entry attached, and loaded with valid data, or
8283  *       *ec is nonzero, and the header is released back into the LRU.
8284  *
8285  * @internal volume package internal use only.
8286  */
8287 static void
8288 LoadVolumeHeader(Error * ec, Volume * vp)
8289 {
8290 #ifdef AFS_DEMAND_ATTACH_FS
8291     VolState state_save;
8292     afs_uint32 now;
8293     *ec = 0;
8294
8295     if (vp->nUsers == 0 && !GetVolumeHeader(vp)) {
8296         IncUInt64(&VStats.hdr_loads);
8297         state_save = VChangeState_r(vp, VOL_STATE_HDR_LOADING);
8298         VOL_UNLOCK;
8299
8300         ReadHeader(ec, V_diskDataHandle(vp), (char *)&V_disk(vp),
8301                    sizeof(V_disk(vp)), VOLUMEINFOMAGIC,
8302                    VOLUMEINFOVERSION);
8303         IncUInt64(&vp->stats.hdr_loads);
8304         now = FT_ApproxTime();
8305
8306         VOL_LOCK;
8307         if (!*ec) {
8308             V_attachFlags(vp) |= VOL_HDR_LOADED;
8309             vp->stats.last_hdr_load = now;
8310         }
8311         VChangeState_r(vp, state_save);
8312     }
8313 #else /* AFS_DEMAND_ATTACH_FS */
8314     *ec = 0;
8315     if (vp->nUsers == 0 && !GetVolumeHeader(vp)) {
8316         IncUInt64(&VStats.hdr_loads);
8317
8318         ReadHeader(ec, V_diskDataHandle(vp), (char *)&V_disk(vp),
8319                    sizeof(V_disk(vp)), VOLUMEINFOMAGIC,
8320                    VOLUMEINFOVERSION);
8321     }
8322 #endif /* AFS_DEMAND_ATTACH_FS */
8323     if (*ec) {
8324         /* maintain (nUsers==0) => header in LRU invariant */
8325         FreeVolumeHeader(vp);
8326     }
8327 }
8328
8329 /**
8330  * release a header cache entry back into the LRU list.
8331  *
8332  * @param[in] hd  pointer to volume header cache object
8333  *
8334  * @pre VOL_LOCK held.
8335  *
8336  * @post header cache object appended onto end of LRU list.
8337  *
8338  * @note only applicable to fileServer program type.
8339  *
8340  * @note used to place a header cache entry back into the
8341  *       LRU pool without invalidating it as a cache entry.
8342  *
8343  * @internal volume package internal use only.
8344  */
8345 static void
8346 ReleaseVolumeHeader(struct volHeader *hd)
8347 {
8348     if (programType != fileServer)
8349         return;
8350     if (!hd || queue_IsOnQueue(hd))     /* no header, or header already released */
8351         return;
8352     queue_Append(&volume_hdr_LRU, hd);
8353 #ifdef AFS_DEMAND_ATTACH_FS
8354     if (hd->back) {
8355         V_attachFlags(hd->back) |= VOL_HDR_IN_LRU;
8356     }
8357 #endif
8358     volume_hdr_LRU.stats.free++;
8359     volume_hdr_LRU.stats.used--;
8360 }
8361
8362 /**
8363  * free/invalidate a volume header cache entry.
8364  *
8365  * @param[in] vp  pointer to volume object
8366  *
8367  * @pre VOL_LOCK is held.
8368  *
8369  * @post For fileserver, header cache entry is returned to LRU, and it is
8370  *       invalidated as a cache entry.  For volume utilities, the header
8371  *       cache entry is freed.
8372  *
8373  * @note For fileserver, this should be utilized instead of ReleaseVolumeHeader
8374  *       whenever it is necessary to invalidate the header cache entry.
8375  *
8376  * @see ReleaseVolumeHeader
8377  *
8378  * @internal volume package internal use only.
8379  */
8380 static void
8381 FreeVolumeHeader(Volume * vp)
8382 {
8383     struct volHeader *hd = vp->header;
8384     if (!hd)
8385         return;
8386     if (programType == fileServer) {
8387         ReleaseVolumeHeader(hd);
8388         hd->back = NULL;
8389     } else {
8390         free(hd);
8391     }
8392 #ifdef AFS_DEMAND_ATTACH_FS
8393     V_attachFlags(vp) &= ~(VOL_HDR_ATTACHED | VOL_HDR_IN_LRU | VOL_HDR_LOADED);
8394 #endif
8395     volume_hdr_LRU.stats.attached--;
8396     vp->header = NULL;
8397 }
8398
8399
8400 /***************************************************/
8401 /* Volume Hash Table routines                      */
8402 /***************************************************/
8403
8404 /**
8405  * set size of volume object hash table.
8406  *
8407  * @param[in] logsize   log(2) of desired hash table size
8408  *
8409  * @return operation status
8410  *    @retval 0 success
8411  *    @retval -1 failure
8412  *
8413  * @pre MUST be called prior to VInitVolumePackage2
8414  *
8415  * @post Volume Hash Table will have 2^logsize buckets
8416  */
8417 int
8418 VSetVolHashSize(int logsize)
8419 {
8420     /* 64 to 268435456 hash buckets seems like a reasonable range */
8421     if ((logsize < 6 ) || (logsize > 28)) {
8422         return -1;
8423     }
8424
8425     if (!VInit) {
8426         VolumeHashTable.Size = opr_jhash_size(logsize);
8427         VolumeHashTable.Mask = opr_jhash_mask(logsize);
8428     } else {
8429         /* we can't yet support runtime modification of this
8430          * parameter. we'll need a configuration rwlock to
8431          * make runtime modification feasible.... */
8432         return -1;
8433     }
8434     return 0;
8435 }
8436
8437 /**
8438  * initialize dynamic data structures for volume hash table.
8439  *
8440  * @post hash table is allocated, and fields are initialized.
8441  *
8442  * @internal volume package internal use only.
8443  */
8444 static void
8445 VInitVolumeHash(void)
8446 {
8447     int i;
8448
8449     VolumeHashTable.Table = (VolumeHashChainHead *) calloc(VolumeHashTable.Size,
8450                                                            sizeof(VolumeHashChainHead));
8451     opr_Assert(VolumeHashTable.Table != NULL);
8452
8453     for (i=0; i < VolumeHashTable.Size; i++) {
8454         queue_Init(&VolumeHashTable.Table[i]);
8455 #ifdef AFS_DEMAND_ATTACH_FS
8456         opr_cv_init(&VolumeHashTable.Table[i].chain_busy_cv);
8457 #endif /* AFS_DEMAND_ATTACH_FS */
8458     }
8459 }
8460
8461 /**
8462  * add a volume object to the hash table.
8463  *
8464  * @param[in] vp      pointer to volume object
8465  * @param[in] hashid  hash of volume id
8466  *
8467  * @pre VOL_LOCK is held.  For DAFS, caller must hold a lightweight
8468  *      reference on vp.
8469  *
8470  * @post volume is added to hash chain.
8471  *
8472  * @internal volume package internal use only.
8473  *
8474  * @note For DAFS, VOL_LOCK may be dropped in order to wait for an
8475  *       asynchronous hash chain reordering to finish.
8476  */
8477 static void
8478 AddVolumeToHashTable(Volume * vp, VolumeId hashid)
8479 {
8480     VolumeHashChainHead * head;
8481
8482     if (queue_IsOnQueue(vp))
8483         return;
8484
8485     head = &VolumeHashTable.Table[VOLUME_HASH(hashid)];
8486
8487 #ifdef AFS_DEMAND_ATTACH_FS
8488     /* wait for the hash chain to become available */
8489     VHashWait_r(head);
8490
8491     V_attachFlags(vp) |= VOL_IN_HASH;
8492     vp->chainCacheCheck = ++head->cacheCheck;
8493 #endif /* AFS_DEMAND_ATTACH_FS */
8494
8495     head->len++;
8496     vp->hashid = hashid;
8497     queue_Append(head, vp);
8498 }
8499
8500 /**
8501  * delete a volume object from the hash table.
8502  *
8503  * @param[in] vp  pointer to volume object
8504  *
8505  * @pre VOL_LOCK is held.  For DAFS, caller must hold a lightweight
8506  *      reference on vp.
8507  *
8508  * @post volume is removed from hash chain.
8509  *
8510  * @internal volume package internal use only.
8511  *
8512  * @note For DAFS, VOL_LOCK may be dropped in order to wait for an
8513  *       asynchronous hash chain reordering to finish.
8514  */
8515 static void
8516 DeleteVolumeFromHashTable(Volume * vp)
8517 {
8518     VolumeHashChainHead * head;
8519
8520     if (!queue_IsOnQueue(vp))
8521         return;
8522
8523     head = &VolumeHashTable.Table[VOLUME_HASH(vp->hashid)];
8524
8525 #ifdef AFS_DEMAND_ATTACH_FS
8526     /* wait for the hash chain to become available */
8527     VHashWait_r(head);
8528
8529     V_attachFlags(vp) &= ~(VOL_IN_HASH);
8530     head->cacheCheck++;
8531 #endif /* AFS_DEMAND_ATTACH_FS */
8532
8533     head->len--;
8534     queue_Remove(vp);
8535     /* do NOT reset hashid to zero, as the online
8536      * salvager package may need to know the volume id
8537      * after the volume is removed from the hash */
8538 }
8539
8540 /**
8541  * lookup a volume object in the hash table given a volume id.
8542  *
8543  * @param[out] ec        error code return
8544  * @param[in]  volumeId  volume id
8545  * @param[in]  hint      volume object which we believe could be the correct
8546                          mapping
8547  *
8548  * @return volume object pointer
8549  *    @retval NULL  no such volume id is registered with the hash table.
8550  *
8551  * @pre VOL_LOCK is held.  For DAFS, caller must hold a lightweight
8552         ref on hint.
8553  *
8554  * @post volume object with the given id is returned.  volume object and
8555  *       hash chain access statistics are updated.  hash chain may have
8556  *       been reordered.
8557  *
8558  * @note For DAFS, VOL_LOCK may be dropped in order to wait for an
8559  *       asynchronous hash chain reordering operation to finish, or
8560  *       in order for us to perform an asynchronous chain reordering.
8561  *
8562  * @note Hash chain reorderings occur when the access count for the
8563  *       volume object being looked up exceeds the sum of the previous
8564  *       node's (the node ahead of it in the hash chain linked list)
8565  *       access count plus the constant VOLUME_HASH_REORDER_THRESHOLD.
8566  *
8567  * @note For DAFS, the hint parameter allows us to short-circuit if the
8568  *       cacheCheck fields match between the hash chain head and the
8569  *       hint volume object.
8570  */
8571 Volume *
8572 VLookupVolume_r(Error * ec, VolumeId volumeId, Volume * hint)
8573 {
8574     int looks = 0;
8575     Volume * vp, *np;
8576 #ifdef AFS_DEMAND_ATTACH_FS
8577     Volume *pp;
8578 #endif
8579     VolumeHashChainHead * head;
8580     *ec = 0;
8581
8582     head = &VolumeHashTable.Table[VOLUME_HASH(volumeId)];
8583
8584 #ifdef AFS_DEMAND_ATTACH_FS
8585     /* wait for the hash chain to become available */
8586     VHashWait_r(head);
8587
8588     /* check to see if we can short circuit without walking the hash chain */
8589     if (hint && (hint->chainCacheCheck == head->cacheCheck)) {
8590         IncUInt64(&hint->stats.hash_short_circuits);
8591         return hint;
8592     }
8593 #endif /* AFS_DEMAND_ATTACH_FS */
8594
8595     /* someday we need to either do per-chain locks, RWlocks,
8596      * or both for volhash access.
8597      * (and move to a data structure with better cache locality) */
8598
8599     /* search the chain for this volume id */
8600     for(queue_Scan(head, vp, np, Volume)) {
8601         looks++;
8602         if (vp->hashid == volumeId) {
8603             break;
8604         }
8605     }
8606
8607     if (queue_IsEnd(head, vp)) {
8608         vp = NULL;
8609     }
8610
8611 #ifdef AFS_DEMAND_ATTACH_FS
8612     /* update hash chain statistics */
8613     {
8614         afs_uint64 lks;
8615         FillInt64(lks, 0, looks);
8616         AddUInt64(head->looks, lks, &head->looks);
8617         AddUInt64(VStats.hash_looks, lks, &VStats.hash_looks);
8618         IncUInt64(&head->gets);
8619     }
8620
8621     if (vp) {
8622         afs_uint64 thresh;
8623         IncUInt64(&vp->stats.hash_lookups);
8624
8625         /* for demand attach fileserver, we permit occasional hash chain reordering
8626          * so that frequently looked up volumes move towards the head of the chain */
8627         pp = queue_Prev(vp, Volume);
8628         if (!queue_IsEnd(head, pp)) {
8629             FillInt64(thresh, 0, VOLUME_HASH_REORDER_THRESHOLD);
8630             AddUInt64(thresh, pp->stats.hash_lookups, &thresh);
8631             if (GEInt64(vp->stats.hash_lookups, thresh)) {
8632                 VReorderHash_r(head, pp, vp);
8633             }
8634         }
8635
8636         /* update the short-circuit cache check */
8637         vp->chainCacheCheck = head->cacheCheck;
8638     }
8639 #endif /* AFS_DEMAND_ATTACH_FS */
8640
8641     return vp;
8642 }
8643
8644 #ifdef AFS_DEMAND_ATTACH_FS
8645 /* perform volume hash chain reordering.
8646  *
8647  * advance a subchain beginning at vp ahead of
8648  * the adjacent subchain ending at pp */
8649 static void
8650 VReorderHash_r(VolumeHashChainHead * head, Volume * pp, Volume * vp)
8651 {
8652     Volume *tp, *np, *lp;
8653     afs_uint64 move_thresh;
8654
8655     /* this should never be called if the chain is already busy, so
8656      * no need to wait for other exclusive chain ops to finish */
8657
8658     /* this is a rather heavy set of operations,
8659      * so let's set the chain busy flag and drop
8660      * the vol_glock */
8661     VHashBeginExclusive_r(head);
8662     VOL_UNLOCK;
8663
8664     /* scan forward in the chain from vp looking for the last element
8665      * in the chain we want to advance */
8666     FillInt64(move_thresh, 0, VOLUME_HASH_REORDER_CHAIN_THRESH);
8667     AddUInt64(move_thresh, pp->stats.hash_lookups, &move_thresh);
8668     for(queue_ScanFrom(head, vp, tp, np, Volume)) {
8669         if (LTInt64(tp->stats.hash_lookups, move_thresh)) {
8670             break;
8671         }
8672     }
8673     lp = queue_Prev(tp, Volume);
8674
8675     /* scan backwards from pp to determine where to splice and
8676      * insert the subchain we're advancing */
8677     for(queue_ScanBackwardsFrom(head, pp, tp, np, Volume)) {
8678         if (GTInt64(tp->stats.hash_lookups, move_thresh)) {
8679             break;
8680         }
8681     }
8682     tp = queue_Next(tp, Volume);
8683
8684     /* rebalance chain(vp,...,lp) ahead of chain(tp,...,pp) */
8685     queue_MoveChainBefore(tp,vp,lp);
8686
8687     VOL_LOCK;
8688     IncUInt64(&VStats.hash_reorders);
8689     head->cacheCheck++;
8690     IncUInt64(&head->reorders);
8691
8692     /* wake up any threads waiting for the hash chain */
8693     VHashEndExclusive_r(head);
8694 }
8695
8696
8697 /* demand-attach fs volume hash
8698  * asynchronous exclusive operations */
8699
8700 /**
8701  * begin an asynchronous exclusive operation on a volume hash chain.
8702  *
8703  * @param[in] head   pointer to volume hash chain head object
8704  *
8705  * @pre VOL_LOCK held.  hash chain is quiescent.
8706  *
8707  * @post hash chain marked busy.
8708  *
8709  * @note this interface is used in conjunction with VHashEndExclusive_r and
8710  *       VHashWait_r to perform asynchronous (wrt VOL_LOCK) operations on a
8711  *       volume hash chain.  Its main use case is hash chain reordering, which
8712  *       has the potential to be a highly latent operation.
8713  *
8714  * @see VHashEndExclusive_r
8715  * @see VHashWait_r
8716  *
8717  * @note DAFS only
8718  *
8719  * @internal volume package internal use only.
8720  */
8721 static void
8722 VHashBeginExclusive_r(VolumeHashChainHead * head)
8723 {
8724     opr_Assert(head->busy == 0);
8725     head->busy = 1;
8726 }
8727
8728 /**
8729  * relinquish exclusive ownership of a volume hash chain.
8730  *
8731  * @param[in] head   pointer to volume hash chain head object
8732  *
8733  * @pre VOL_LOCK held.  thread owns the hash chain exclusively.
8734  *
8735  * @post hash chain is marked quiescent.  threads awaiting use of
8736  *       chain are awakened.
8737  *
8738  * @see VHashBeginExclusive_r
8739  * @see VHashWait_r
8740  *
8741  * @note DAFS only
8742  *
8743  * @internal volume package internal use only.
8744  */
8745 static void
8746 VHashEndExclusive_r(VolumeHashChainHead * head)
8747 {
8748     opr_Assert(head->busy);
8749     head->busy = 0;
8750     opr_cv_broadcast(&head->chain_busy_cv);
8751 }
8752
8753 /**
8754  * wait for all asynchronous operations on a hash chain to complete.
8755  *
8756  * @param[in] head   pointer to volume hash chain head object
8757  *
8758  * @pre VOL_LOCK held.
8759  *
8760  * @post hash chain object is quiescent.
8761  *
8762  * @see VHashBeginExclusive_r
8763  * @see VHashEndExclusive_r
8764  *
8765  * @note DAFS only
8766  *
8767  * @note This interface should be called before any attempt to
8768  *       traverse the hash chain.  It is permissible for a thread
8769  *       to gain exclusive access to the chain, and then perform
8770  *       latent operations on the chain asynchronously wrt the
8771  *       VOL_LOCK.
8772  *
8773  * @warning if waiting is necessary, VOL_LOCK is dropped
8774  *
8775  * @internal volume package internal use only.
8776  */
8777 static void
8778 VHashWait_r(VolumeHashChainHead * head)
8779 {
8780     while (head->busy) {
8781         VOL_CV_WAIT(&head->chain_busy_cv);
8782     }
8783 }
8784 #endif /* AFS_DEMAND_ATTACH_FS */
8785
8786
8787 /***************************************************/
8788 /* Volume by Partition List routines               */
8789 /***************************************************/
8790
8791 /*
8792  * demand attach fileserver adds a
8793  * linked list of volumes to each
8794  * partition object, thus allowing
8795  * for quick enumeration of all
8796  * volumes on a partition
8797  */
8798
8799 #ifdef AFS_DEMAND_ATTACH_FS
8800 /**
8801  * add a volume to its disk partition VByPList.
8802  *
8803  * @param[in] vp  pointer to volume object
8804  *
8805  * @pre either the disk partition VByPList is owned exclusively
8806  *      by the calling thread, or the list is quiescent and
8807  *      VOL_LOCK is held.
8808  *
8809  * @post volume is added to disk partition VByPList
8810  *
8811  * @note DAFS only
8812  *
8813  * @warning it is the caller's responsibility to ensure list
8814  *          quiescence.
8815  *
8816  * @see VVByPListWait_r
8817  * @see VVByPListBeginExclusive_r
8818  * @see VVByPListEndExclusive_r
8819  *
8820  * @internal volume package internal use only.
8821  */
8822 static void
8823 AddVolumeToVByPList_r(Volume * vp)
8824 {
8825     if (queue_IsNotOnQueue(&vp->vol_list)) {
8826         queue_Append(&vp->partition->vol_list, &vp->vol_list);
8827         V_attachFlags(vp) |= VOL_ON_VBYP_LIST;
8828         vp->partition->vol_list.len++;
8829     }
8830 }
8831
8832 /**
8833  * delete a volume from its disk partition VByPList.
8834  *
8835  * @param[in] vp  pointer to volume object
8836  *
8837  * @pre either the disk partition VByPList is owned exclusively
8838  *      by the calling thread, or the list is quiescent and
8839  *      VOL_LOCK is held.
8840  *
8841  * @post volume is removed from the disk partition VByPList
8842  *
8843  * @note DAFS only
8844  *
8845  * @warning it is the caller's responsibility to ensure list
8846  *          quiescence.
8847  *
8848  * @see VVByPListWait_r
8849  * @see VVByPListBeginExclusive_r
8850  * @see VVByPListEndExclusive_r
8851  *
8852  * @internal volume package internal use only.
8853  */
8854 static void
8855 DeleteVolumeFromVByPList_r(Volume * vp)
8856 {
8857     if (queue_IsOnQueue(&vp->vol_list)) {
8858         queue_Remove(&vp->vol_list);
8859         V_attachFlags(vp) &= ~(VOL_ON_VBYP_LIST);
8860         vp->partition->vol_list.len--;
8861     }
8862 }
8863
8864 /**
8865  * begin an asynchronous exclusive operation on a VByPList.
8866  *
8867  * @param[in] dp   pointer to disk partition object
8868  *
8869  * @pre VOL_LOCK held.  VByPList is quiescent.
8870  *
8871  * @post VByPList marked busy.
8872  *
8873  * @note this interface is used in conjunction with VVByPListEndExclusive_r and
8874  *       VVByPListWait_r to perform asynchronous (wrt VOL_LOCK) operations on a
8875  *       VByPList.
8876  *
8877  * @see VVByPListEndExclusive_r
8878  * @see VVByPListWait_r
8879  *
8880  * @note DAFS only
8881  *
8882  * @internal volume package internal use only.
8883  */
8884 /* take exclusive control over the list */
8885 static void
8886 VVByPListBeginExclusive_r(struct DiskPartition64 * dp)
8887 {
8888     opr_Assert(dp->vol_list.busy == 0);
8889     dp->vol_list.busy = 1;
8890 }
8891
8892 /**
8893  * relinquish exclusive ownership of a VByPList.
8894  *
8895  * @param[in] dp   pointer to disk partition object
8896  *
8897  * @pre VOL_LOCK held.  thread owns the VByPList exclusively.
8898  *
8899  * @post VByPList is marked quiescent.  threads awaiting use of
8900  *       the list are awakened.
8901  *
8902  * @see VVByPListBeginExclusive_r
8903  * @see VVByPListWait_r
8904  *
8905  * @note DAFS only
8906  *
8907  * @internal volume package internal use only.
8908  */
8909 static void
8910 VVByPListEndExclusive_r(struct DiskPartition64 * dp)
8911 {
8912     opr_Assert(dp->vol_list.busy);
8913     dp->vol_list.busy = 0;
8914     opr_cv_broadcast(&dp->vol_list.cv);
8915 }
8916
8917 /**
8918  * wait for all asynchronous operations on a VByPList to complete.
8919  *
8920  * @param[in] dp  pointer to disk partition object
8921  *
8922  * @pre VOL_LOCK is held.
8923  *
8924  * @post disk partition's VByP list is quiescent
8925  *
8926  * @note DAFS only
8927  *
8928  * @note This interface should be called before any attempt to
8929  *       traverse the VByPList.  It is permissible for a thread
8930  *       to gain exclusive access to the list, and then perform
8931  *       latent operations on the list asynchronously wrt the
8932  *       VOL_LOCK.
8933  *
8934  * @warning if waiting is necessary, VOL_LOCK is dropped
8935  *
8936  * @see VVByPListEndExclusive_r
8937  * @see VVByPListBeginExclusive_r
8938  *
8939  * @internal volume package internal use only.
8940  */
8941 static void
8942 VVByPListWait_r(struct DiskPartition64 * dp)
8943 {
8944     while (dp->vol_list.busy) {
8945         VOL_CV_WAIT(&dp->vol_list.cv);
8946     }
8947 }
8948 #endif /* AFS_DEMAND_ATTACH_FS */
8949
8950 /***************************************************/
8951 /* Volume Cache Statistics routines                */
8952 /***************************************************/
8953
8954 void
8955 VPrintCacheStats_r(void)
8956 {
8957     struct VnodeClassInfo *vcp;
8958     vcp = &VnodeClassInfo[vLarge];
8959     Log("Large vnode cache, %d entries, %d allocs, %d gets (%d reads), %d writes\n", vcp->cacheSize, vcp->allocs, vcp->gets, vcp->reads, vcp->writes);
8960     vcp = &VnodeClassInfo[vSmall];
8961     Log("Small vnode cache,%d entries, %d allocs, %d gets (%d reads), %d writes\n", vcp->cacheSize, vcp->allocs, vcp->gets, vcp->reads, vcp->writes);
8962     Log("Volume header cache, %d entries, %"AFS_INT64_FMT" gets, "
8963         "%"AFS_INT64_FMT" replacements\n",
8964         VStats.hdr_cache_size, VStats.hdr_gets, VStats.hdr_loads);
8965 }
8966
8967 void
8968 VPrintCacheStats(void)
8969 {
8970     VOL_LOCK;
8971     VPrintCacheStats_r();
8972     VOL_UNLOCK;
8973 }
8974
8975 #ifdef AFS_DEMAND_ATTACH_FS
8976 static double
8977 UInt64ToDouble(afs_uint64 * x)
8978 {
8979     static double c32 = 4.0 * 1.073741824 * 1000000000.0;
8980     afs_uint32 h, l;
8981     SplitInt64(*x, h, l);
8982     return (((double)h) * c32) + ((double) l);
8983 }
8984
8985 static char *
8986 DoubleToPrintable(double x, char * buf, int len)
8987 {
8988     static double billion = 1000000000.0;
8989     afs_uint32 y[3];
8990
8991     y[0] = (afs_uint32) (x / (billion * billion));
8992     y[1] = (afs_uint32) ((x - (((double)y[0]) * billion * billion)) / billion);
8993     y[2] = (afs_uint32) (x - ((((double)y[0]) * billion * billion) + (((double)y[1]) * billion)));
8994
8995     if (y[0]) {
8996         snprintf(buf, len, "%d%09d%09d", y[0], y[1], y[2]);
8997     } else if (y[1]) {
8998         snprintf(buf, len, "%d%09d", y[1], y[2]);
8999     } else {
9000         snprintf(buf, len, "%d", y[2]);
9001     }
9002     buf[len-1] = '\0';
9003     return buf;
9004 }
9005
9006 struct VLRUExtStatsEntry {
9007     VolumeId volid;
9008 };
9009
9010 struct VLRUExtStats {
9011     afs_uint32 len;
9012     afs_uint32 used;
9013     struct {
9014         afs_uint32 start;
9015         afs_uint32 len;
9016     } queue_info[VLRU_QUEUE_INVALID];
9017     struct VLRUExtStatsEntry * vec;
9018 };
9019
9020 /**
9021  * add a 256-entry fudge factor onto the vector in case state changes
9022  * out from under us.
9023  */
9024 #define VLRU_EXT_STATS_VEC_LEN_FUDGE   256
9025
9026 /**
9027  * collect extended statistics for the VLRU subsystem.
9028  *
9029  * @param[out] stats  pointer to stats structure to be populated
9030  * @param[in] nvols   number of volumes currently known to exist
9031  *
9032  * @pre VOL_LOCK held
9033  *
9034  * @post stats->vec allocated and populated
9035  *
9036  * @return operation status
9037  *    @retval 0 success
9038  *    @retval 1 failure
9039  */
9040 static int
9041 VVLRUExtStats_r(struct VLRUExtStats * stats, afs_uint32 nvols)
9042 {
9043     afs_uint32 cur, idx, len;
9044     struct rx_queue * qp, * nqp;
9045     Volume * vp;
9046     struct VLRUExtStatsEntry * vec;
9047
9048     len = nvols + VLRU_EXT_STATS_VEC_LEN_FUDGE;
9049     vec = stats->vec = calloc(len,
9050                               sizeof(struct VLRUExtStatsEntry));
9051     if (vec == NULL) {
9052         return 1;
9053     }
9054
9055     cur = 0;
9056     for (idx = VLRU_QUEUE_NEW; idx < VLRU_QUEUE_INVALID; idx++) {
9057         VLRU_Wait_r(&volume_LRU.q[idx]);
9058         VLRU_BeginExclusive_r(&volume_LRU.q[idx]);
9059         VOL_UNLOCK;
9060
9061         stats->queue_info[idx].start = cur;
9062
9063         for (queue_Scan(&volume_LRU.q[idx], qp, nqp, rx_queue)) {
9064             if (cur == len) {
9065                 /* out of space in vec */
9066                 break;
9067             }
9068             vp = (Volume *)((char *)qp - offsetof(Volume, vlru));
9069             vec[cur].volid = vp->hashid;
9070             cur++;
9071         }
9072
9073         stats->queue_info[idx].len = cur - stats->queue_info[idx].start;
9074
9075         VOL_LOCK;
9076         VLRU_EndExclusive_r(&volume_LRU.q[idx]);
9077     }
9078
9079     stats->len = len;
9080     stats->used = cur;
9081     return 0;
9082 }
9083
9084 #define ENUMTOSTRING(en)  #en
9085 #define ENUMCASE(en) \
9086     case en: return ENUMTOSTRING(en)
9087
9088 static char *
9089 vlru_idx_to_string(int idx)
9090 {
9091     switch (idx) {
9092         ENUMCASE(VLRU_QUEUE_NEW);
9093         ENUMCASE(VLRU_QUEUE_MID);
9094         ENUMCASE(VLRU_QUEUE_OLD);
9095         ENUMCASE(VLRU_QUEUE_CANDIDATE);
9096         ENUMCASE(VLRU_QUEUE_HELD);
9097         ENUMCASE(VLRU_QUEUE_INVALID);
9098     default:
9099         return "**UNKNOWN**";
9100     }
9101 }
9102
9103 void
9104 VPrintExtendedCacheStats_r(int flags)
9105 {
9106     int i;
9107     afs_uint32 vol_sum = 0;
9108     struct stats {
9109         double min;
9110         double max;
9111         double sum;
9112         double avg;
9113     };
9114     struct stats looks, gets, reorders, len;
9115     struct stats ch_looks, ch_gets, ch_reorders;
9116     char pr_buf[4][32];
9117     VolumeHashChainHead *head;
9118     Volume *vp, *np;
9119     struct VLRUExtStats vlru_stats;
9120
9121     /* zero out stats */
9122     memset(&looks, 0, sizeof(struct stats));
9123     memset(&gets, 0, sizeof(struct stats));
9124     memset(&reorders, 0, sizeof(struct stats));
9125     memset(&len, 0, sizeof(struct stats));
9126     memset(&ch_looks, 0, sizeof(struct stats));
9127     memset(&ch_gets, 0, sizeof(struct stats));
9128     memset(&ch_reorders, 0, sizeof(struct stats));
9129
9130     for (i = 0; i < VolumeHashTable.Size; i++) {
9131         head = &VolumeHashTable.Table[i];
9132
9133         VHashWait_r(head);
9134         VHashBeginExclusive_r(head);
9135         VOL_UNLOCK;
9136
9137         ch_looks.sum    = UInt64ToDouble(&head->looks);
9138         ch_gets.sum     = UInt64ToDouble(&head->gets);
9139         ch_reorders.sum = UInt64ToDouble(&head->reorders);
9140
9141         /* update global statistics */
9142         {
9143             looks.sum    += ch_looks.sum;
9144             gets.sum     += ch_gets.sum;
9145             reorders.sum += ch_reorders.sum;
9146             len.sum      += (double)head->len;
9147             vol_sum      += head->len;
9148
9149             if (i == 0) {
9150                 len.min      = (double) head->len;
9151                 len.max      = (double) head->len;
9152                 looks.min    = ch_looks.sum;
9153                 looks.max    = ch_looks.sum;
9154                 gets.min     = ch_gets.sum;
9155                 gets.max     = ch_gets.sum;
9156                 reorders.min = ch_reorders.sum;
9157                 reorders.max = ch_reorders.sum;
9158             } else {
9159                 if (((double)head->len) < len.min)
9160                     len.min = (double) head->len;
9161                 if (((double)head->len) > len.max)
9162                     len.max = (double) head->len;
9163                 if (ch_looks.sum < looks.min)
9164                     looks.min = ch_looks.sum;
9165                 else if (ch_looks.sum > looks.max)
9166                     looks.max = ch_looks.sum;
9167                 if (ch_gets.sum < gets.min)
9168                     gets.min = ch_gets.sum;
9169                 else if (ch_gets.sum > gets.max)
9170                     gets.max = ch_gets.sum;
9171                 if (ch_reorders.sum < reorders.min)
9172                     reorders.min = ch_reorders.sum;
9173                 else if (ch_reorders.sum > reorders.max)
9174                     reorders.max = ch_reorders.sum;
9175             }
9176         }
9177
9178         if ((flags & VOL_STATS_PER_CHAIN2) && queue_IsNotEmpty(head)) {
9179             /* compute detailed per-chain stats */
9180             struct stats hdr_loads, hdr_gets;
9181             double v_looks, v_loads, v_gets;
9182
9183             /* initialize stats with data from first element in chain */
9184             vp = queue_First(head, Volume);
9185             v_looks = UInt64ToDouble(&vp->stats.hash_lookups);
9186             v_loads = UInt64ToDouble(&vp->stats.hdr_loads);
9187             v_gets  = UInt64ToDouble(&vp->stats.hdr_gets);
9188             ch_gets.min = ch_gets.max = v_looks;
9189             hdr_loads.min = hdr_loads.max = v_loads;
9190             hdr_gets.min = hdr_gets.max = v_gets;
9191             hdr_loads.sum = hdr_gets.sum = 0;
9192
9193             vp = queue_Next(vp, Volume);
9194
9195             /* pull in stats from remaining elements in chain */
9196             for (queue_ScanFrom(head, vp, vp, np, Volume)) {
9197                 v_looks = UInt64ToDouble(&vp->stats.hash_lookups);
9198                 v_loads = UInt64ToDouble(&vp->stats.hdr_loads);
9199                 v_gets  = UInt64ToDouble(&vp->stats.hdr_gets);
9200
9201                 hdr_loads.sum += v_loads;
9202                 hdr_gets.sum += v_gets;
9203
9204                 if (v_looks < ch_gets.min)
9205                     ch_gets.min = v_looks;
9206                 else if (v_looks > ch_gets.max)
9207                     ch_gets.max = v_looks;
9208
9209                 if (v_loads < hdr_loads.min)
9210                     hdr_loads.min = v_loads;
9211                 else if (v_loads > hdr_loads.max)
9212                     hdr_loads.max = v_loads;
9213
9214                 if (v_gets < hdr_gets.min)
9215                     hdr_gets.min = v_gets;
9216                 else if (v_gets > hdr_gets.max)
9217                     hdr_gets.max = v_gets;
9218             }
9219
9220             /* compute per-chain averages */
9221             ch_gets.avg = ch_gets.sum / ((double)head->len);
9222             hdr_loads.avg = hdr_loads.sum / ((double)head->len);
9223             hdr_gets.avg = hdr_gets.sum / ((double)head->len);
9224
9225             /* dump per-chain stats */
9226             Log("Volume hash chain %d : len=%d, looks=%s, reorders=%s\n",
9227                 i, head->len,
9228                 DoubleToPrintable(ch_looks.sum, pr_buf[0], sizeof(pr_buf[0])),
9229                 DoubleToPrintable(ch_reorders.sum, pr_buf[1], sizeof(pr_buf[1])));
9230             Log("\tVolume gets : min=%s, max=%s, avg=%s, total=%s\n",
9231                 DoubleToPrintable(ch_gets.min, pr_buf[0], sizeof(pr_buf[0])),
9232                 DoubleToPrintable(ch_gets.max, pr_buf[1], sizeof(pr_buf[1])),
9233                 DoubleToPrintable(ch_gets.avg, pr_buf[2], sizeof(pr_buf[2])),
9234                 DoubleToPrintable(ch_gets.sum, pr_buf[3], sizeof(pr_buf[3])));
9235             Log("\tHDR gets : min=%s, max=%s, avg=%s, total=%s\n",
9236                 DoubleToPrintable(hdr_gets.min, pr_buf[0], sizeof(pr_buf[0])),
9237                 DoubleToPrintable(hdr_gets.max, pr_buf[1], sizeof(pr_buf[1])),
9238                 DoubleToPrintable(hdr_gets.avg, pr_buf[2], sizeof(pr_buf[2])),
9239                 DoubleToPrintable(hdr_gets.sum, pr_buf[3], sizeof(pr_buf[3])));
9240             Log("\tHDR loads : min=%s, max=%s, avg=%s, total=%s\n",
9241                 DoubleToPrintable(hdr_loads.min, pr_buf[0], sizeof(pr_buf[0])),
9242                 DoubleToPrintable(hdr_loads.max, pr_buf[1], sizeof(pr_buf[1])),
9243                 DoubleToPrintable(hdr_loads.avg, pr_buf[2], sizeof(pr_buf[2])),
9244                 DoubleToPrintable(hdr_loads.sum, pr_buf[3], sizeof(pr_buf[3])));
9245         } else if (flags & VOL_STATS_PER_CHAIN) {
9246             /* dump simple per-chain stats */
9247             Log("Volume hash chain %d : len=%d, looks=%s, gets=%s, reorders=%s\n",
9248                 i, head->len,
9249                 DoubleToPrintable(ch_looks.sum, pr_buf[0], sizeof(pr_buf[0])),
9250                 DoubleToPrintable(ch_gets.sum, pr_buf[1], sizeof(pr_buf[1])),
9251                 DoubleToPrintable(ch_reorders.sum, pr_buf[2], sizeof(pr_buf[2])));
9252         }
9253
9254         VOL_LOCK;
9255         VHashEndExclusive_r(head);
9256     }
9257
9258     VOL_UNLOCK;
9259
9260     /* compute global averages */
9261     len.avg      = len.sum      / ((double)VolumeHashTable.Size);
9262     looks.avg    = looks.sum    / ((double)VolumeHashTable.Size);
9263     gets.avg     = gets.sum     / ((double)VolumeHashTable.Size);
9264     reorders.avg = reorders.sum / ((double)VolumeHashTable.Size);
9265
9266     /* dump global stats */
9267     Log("Volume hash summary: %d buckets\n", VolumeHashTable.Size);
9268     Log(" chain length : min=%s, max=%s, avg=%s, total=%s\n",
9269         DoubleToPrintable(len.min, pr_buf[0], sizeof(pr_buf[0])),
9270         DoubleToPrintable(len.max, pr_buf[1], sizeof(pr_buf[1])),
9271         DoubleToPrintable(len.avg, pr_buf[2], sizeof(pr_buf[2])),
9272         DoubleToPrintable(len.sum, pr_buf[3], sizeof(pr_buf[3])));
9273     Log(" looks : min=%s, max=%s, avg=%s, total=%s\n",
9274         DoubleToPrintable(looks.min, pr_buf[0], sizeof(pr_buf[0])),
9275         DoubleToPrintable(looks.max, pr_buf[1], sizeof(pr_buf[1])),
9276         DoubleToPrintable(looks.avg, pr_buf[2], sizeof(pr_buf[2])),
9277         DoubleToPrintable(looks.sum, pr_buf[3], sizeof(pr_buf[3])));
9278     Log(" gets : min=%s, max=%s, avg=%s, total=%s\n",
9279         DoubleToPrintable(gets.min, pr_buf[0], sizeof(pr_buf[0])),
9280         DoubleToPrintable(gets.max, pr_buf[1], sizeof(pr_buf[1])),
9281         DoubleToPrintable(gets.avg, pr_buf[2], sizeof(pr_buf[2])),
9282         DoubleToPrintable(gets.sum, pr_buf[3], sizeof(pr_buf[3])));
9283     Log(" reorders : min=%s, max=%s, avg=%s, total=%s\n",
9284         DoubleToPrintable(reorders.min, pr_buf[0], sizeof(pr_buf[0])),
9285         DoubleToPrintable(reorders.max, pr_buf[1], sizeof(pr_buf[1])),
9286         DoubleToPrintable(reorders.avg, pr_buf[2], sizeof(pr_buf[2])),
9287         DoubleToPrintable(reorders.sum, pr_buf[3], sizeof(pr_buf[3])));
9288
9289     /* print extended disk related statistics */
9290     {
9291         struct DiskPartition64 * diskP;
9292         afs_uint32 vol_count[VOLMAXPARTS+1];
9293         byte part_exists[VOLMAXPARTS+1];
9294         Device id;
9295         int i;
9296
9297         memset(vol_count, 0, sizeof(vol_count));
9298         memset(part_exists, 0, sizeof(part_exists));
9299
9300         VOL_LOCK;
9301
9302         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
9303             id = diskP->index;
9304             vol_count[id] = diskP->vol_list.len;
9305             part_exists[id] = 1;
9306         }
9307
9308         VOL_UNLOCK;
9309         for (i = 0; i <= VOLMAXPARTS; i++) {
9310             if (part_exists[i]) {
9311                 /* XXX while this is currently safe, it is a violation
9312                  *     of the VGetPartitionById_r interface contract. */
9313                 diskP = VGetPartitionById_r(i, 0);
9314                 if (diskP) {
9315                     Log("Partition %s has %d online volumes\n",
9316                         VPartitionPath(diskP), diskP->vol_list.len);
9317                 }
9318             }
9319         }
9320         VOL_LOCK;
9321     }
9322
9323     /* print extended VLRU statistics */
9324     if (VVLRUExtStats_r(&vlru_stats, vol_sum) == 0) {
9325         afs_uint32 idx, cur, lpos;
9326         VolumeId line[5];
9327
9328         VOL_UNLOCK;
9329
9330         Log("VLRU State Dump:\n\n");
9331
9332         for (idx = VLRU_QUEUE_NEW; idx < VLRU_QUEUE_INVALID; idx++) {
9333             Log("\t%s:\n", vlru_idx_to_string(idx));
9334
9335             lpos = 0;
9336             for (cur = vlru_stats.queue_info[idx].start;
9337                  cur < vlru_stats.queue_info[idx].len;
9338                  cur++) {
9339                 line[lpos++] = vlru_stats.vec[cur].volid;
9340                 if (lpos==5) {
9341                     Log("\t\t%u, %u, %u, %u, %u,\n",
9342                         line[0], line[1], line[2], line[3], line[4]);
9343                     lpos = 0;
9344                 }
9345             }
9346
9347             if (lpos) {
9348                 while (lpos < 5) {
9349                     line[lpos++] = 0;
9350                 }
9351                 Log("\t\t%u, %u, %u, %u, %u\n",
9352                     line[0], line[1], line[2], line[3], line[4]);
9353             }
9354             Log("\n");
9355         }
9356
9357         free(vlru_stats.vec);
9358
9359         VOL_LOCK;
9360     }
9361 }
9362
9363 void
9364 VPrintExtendedCacheStats(int flags)
9365 {
9366     VOL_LOCK;
9367     VPrintExtendedCacheStats_r(flags);
9368     VOL_UNLOCK;
9369 }
9370 #endif /* AFS_DEMAND_ATTACH_FS */
9371
9372 afs_int32
9373 VCanScheduleSalvage(void)
9374 {
9375     return vol_opts.canScheduleSalvage;
9376 }
9377
9378 afs_int32
9379 VCanUseFSSYNC(void)
9380 {
9381     return vol_opts.canUseFSSYNC;
9382 }
9383
9384 afs_int32
9385 VCanUseSALVSYNC(void)
9386 {
9387     return vol_opts.canUseSALVSYNC;
9388 }
9389
9390 afs_int32
9391 VCanUnsafeAttach(void)
9392 {
9393     return vol_opts.unsafe_attach;
9394 }