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