large-partition-support-20080305
[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 RCSID
25     ("$Header$");
26
27 #include <rx/xdr.h>
28 #include <afs/afsint.h>
29 #include <ctype.h>
30 #ifndef AFS_NT40_ENV
31 #include <sys/param.h>
32 #if !defined(AFS_SGI_ENV)
33 #ifdef  AFS_OSF_ENV
34 #include <ufs/fs.h>
35 #else /* AFS_OSF_ENV */
36 #ifdef AFS_VFSINCL_ENV
37 #define VFS
38 #ifdef  AFS_SUN5_ENV
39 #include <sys/fs/ufs_fs.h>
40 #else
41 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
42 #include <ufs/ufs/dinode.h>
43 #include <ufs/ffs/fs.h>
44 #else
45 #include <ufs/fs.h>
46 #endif
47 #endif
48 #else /* AFS_VFSINCL_ENV */
49 #if !defined(AFS_AIX_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
50 #include <sys/fs.h>
51 #endif
52 #endif /* AFS_VFSINCL_ENV */
53 #endif /* AFS_OSF_ENV */
54 #endif /* AFS_SGI_ENV */
55 #endif /* AFS_NT40_ENV */
56 #include <errno.h>
57 #include <sys/stat.h>
58 #include <stdio.h>
59 #ifdef AFS_NT40_ENV
60 #include <fcntl.h>
61 #else
62 #include <sys/file.h>
63 #endif
64 #include <dirent.h>
65 #ifdef  AFS_AIX_ENV
66 #include <sys/vfs.h>
67 #include <fcntl.h>
68 #else
69 #ifdef  AFS_HPUX_ENV
70 #include <fcntl.h>
71 #include <mntent.h>
72 #else
73 #if     defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
74 #ifdef  AFS_SUN5_ENV
75 #include <sys/mnttab.h>
76 #include <sys/mntent.h>
77 #else
78 #include <mntent.h>
79 #endif
80 #else
81 #ifndef AFS_NT40_ENV
82 #if defined(AFS_SGI_ENV)
83 #include <fcntl.h>
84 #include <mntent.h>
85
86 #else
87 #ifndef AFS_LINUX20_ENV
88 #include <fstab.h>              /* Need to find in libc 5, present in libc 6 */
89 #endif
90 #endif
91 #endif /* AFS_SGI_ENV */
92 #endif
93 #endif /* AFS_HPUX_ENV */
94 #endif
95 #ifndef AFS_NT40_ENV
96 #include <netdb.h>
97 #include <netinet/in.h>
98 #include <sys/wait.h>
99 #include <setjmp.h>
100 #ifndef ITIMER_REAL
101 #include <sys/time.h>
102 #endif /* ITIMER_REAL */
103 #endif /* AFS_NT40_ENV */
104 #if defined(AFS_SUN5_ENV) || defined(AFS_NT40_ENV) || defined(AFS_LINUX20_ENV)
105 #include <string.h>
106 #else
107 #include <strings.h>
108 #endif
109
110 #include "nfs.h"
111 #include <afs/errors.h>
112 #include "lock.h"
113 #include "lwp.h"
114 #include <afs/afssyscalls.h>
115 #include "ihandle.h"
116 #include <afs/afsutil.h>
117 #ifdef AFS_NT40_ENV
118 #include <io.h>
119 #endif
120 #include "daemon_com.h"
121 #include "fssync.h"
122 #include "salvsync.h"
123 #include "vnode.h"
124 #include "volume.h"
125 #include "partition.h"
126 #include "volume_inline.h"
127 #ifdef AFS_PTHREAD_ENV
128 #include <assert.h>
129 #else /* AFS_PTHREAD_ENV */
130 #include "afs/assert.h"
131 #endif /* AFS_PTHREAD_ENV */
132 #include "vutils.h"
133 #ifndef AFS_NT40_ENV
134 #include <dir/dir.h>
135 #include <unistd.h>
136 #endif
137
138 #if !defined(offsetof)
139 #include <stddef.h>
140 #endif
141
142 #ifdef O_LARGEFILE
143 #define afs_stat        stat64
144 #define afs_fstat       fstat64
145 #define afs_open        open64
146 #else /* !O_LARGEFILE */
147 #define afs_stat        stat
148 #define afs_fstat       fstat
149 #define afs_open        open
150 #endif /* !O_LARGEFILE */
151
152 #ifdef AFS_PTHREAD_ENV
153 pthread_mutex_t vol_glock_mutex;
154 pthread_mutex_t vol_trans_mutex;
155 pthread_cond_t vol_put_volume_cond;
156 pthread_cond_t vol_sleep_cond;
157 int vol_attach_threads = 1;
158 #endif /* AFS_PTHREAD_ENV */
159
160 #ifdef AFS_DEMAND_ATTACH_FS
161 pthread_mutex_t vol_salvsync_mutex;
162 #endif /* AFS_DEMAND_ATTACH_FS */
163
164 #ifdef  AFS_OSF_ENV
165 extern void *calloc(), *realloc();
166 #endif
167
168 /*@printflike@*/ extern void Log(const char *format, ...);
169
170 /* Forward declarations */
171 static Volume *attach2(Error * ec, VolId vid, char *path,
172                        register struct VolumeHeader *header,
173                        struct DiskPartition64 *partp, Volume * vp, 
174                        int isbusy, int mode);
175 static void ReallyFreeVolume(Volume * vp);
176 #ifdef AFS_DEMAND_ATTACH_FS
177 static void FreeVolume(Volume * vp);
178 #else /* !AFS_DEMAND_ATTACH_FS */
179 #define FreeVolume(vp) ReallyFreeVolume(vp)
180 static void VScanUpdateList(void);
181 #endif /* !AFS_DEMAND_ATTACH_FS */
182 static void VInitVolumeHeaderCache(afs_uint32 howMany);
183 static int GetVolumeHeader(register Volume * vp);
184 static void ReleaseVolumeHeader(register struct volHeader *hd);
185 static void FreeVolumeHeader(register Volume * vp);
186 static void AddVolumeToHashTable(register Volume * vp, int hashid);
187 static void DeleteVolumeFromHashTable(register Volume * vp);
188 static int VHold(Volume * vp);
189 static int VHold_r(Volume * vp);
190 static void VGetBitmap_r(Error * ec, Volume * vp, VnodeClass class);
191 static void GetVolumePath(Error * ec, VolId volumeId, char **partitionp,
192                           char **namep);
193 static void VReleaseVolumeHandles_r(Volume * vp);
194 static void VCloseVolumeHandles_r(Volume * vp);
195 static void LoadVolumeHeader(Error * ec, Volume * vp);
196 static int VCheckOffline(register Volume * vp);
197 static int VCheckDetach(register Volume * vp);
198 static Volume * GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flags);
199 static int VolumeExternalName_r(VolumeId volumeId, char * name, size_t len);
200
201 int LogLevel;                   /* Vice loglevel--not defined as extern so that it will be
202                                  * defined when not linked with vice, XXXX */
203 ProgramType programType;        /* The type of program using the package */
204
205 /* extended volume package statistics */
206 VolPkgStats VStats;
207
208 #ifdef VOL_LOCK_DEBUG
209 pthread_t vol_glock_holder = 0;
210 #endif
211
212
213 #define VOLUME_BITMAP_GROWSIZE  16      /* bytes, => 128vnodes */
214                                         /* Must be a multiple of 4 (1 word) !! */
215
216 /* this parameter needs to be tunable at runtime.
217  * 128 was really inadequate for largish servers -- at 16384 volumes this
218  * puts average chain length at 128, thus an average 65 deref's to find a volptr.
219  * talk about bad spatial locality...
220  *
221  * an AVL or splay tree might work a lot better, but we'll just increase
222  * the default hash table size for now
223  */
224 #define DEFAULT_VOLUME_HASH_SIZE 256   /* Must be a power of 2!! */
225 #define DEFAULT_VOLUME_HASH_MASK (DEFAULT_VOLUME_HASH_SIZE-1)
226 #define VOLUME_HASH(volumeId) (volumeId&(VolumeHashTable.Mask))
227
228 /*
229  * turn volume hash chains into partially ordered lists.
230  * when the threshold is exceeded between two adjacent elements,
231  * perform a chain rebalancing operation.
232  *
233  * keep the threshold high in order to keep cache line invalidates
234  * low "enough" on SMPs
235  */
236 #define VOLUME_HASH_REORDER_THRESHOLD 200
237
238 /*
239  * when possible, don't just reorder single elements, but reorder
240  * entire chains of elements at once.  a chain of elements that
241  * exceed the element previous to the pivot by at least CHAIN_THRESH 
242  * accesses are moved in front of the chain whose elements have at
243  * least CHAIN_THRESH less accesses than the pivot element
244  */
245 #define VOLUME_HASH_REORDER_CHAIN_THRESH (VOLUME_HASH_REORDER_THRESHOLD / 2)
246
247 #include "rx/rx_queue.h"
248
249
250 VolumeHashTable_t VolumeHashTable = {
251     DEFAULT_VOLUME_HASH_SIZE,
252     DEFAULT_VOLUME_HASH_MASK,
253     NULL
254 };
255
256
257 static void VInitVolumeHash(void);
258
259
260 #ifndef AFS_HAVE_FFS
261 /* This macro is used where an ffs() call does not exist. Was in util/ffs.c */
262 ffs(x)
263 {
264     afs_int32 ffs_i;
265     afs_int32 ffs_tmp = x;
266     if (ffs_tmp == 0)
267         return (-1);
268     else
269         for (ffs_i = 1;; ffs_i++) {
270             if (ffs_tmp & 1)
271                 return (ffs_i);
272             else
273                 ffs_tmp >>= 1;
274         }
275 }
276 #endif /* !AFS_HAVE_FFS */
277
278 #ifdef AFS_PTHREAD_ENV
279 typedef struct diskpartition_queue_t {
280     struct rx_queue queue;
281     struct DiskPartition64 * diskP;
282 } diskpartition_queue_t;
283 typedef struct vinitvolumepackage_thread_t {
284     struct rx_queue queue;
285     pthread_cond_t thread_done_cv;
286     int n_threads_complete;
287 } vinitvolumepackage_thread_t;
288 static void * VInitVolumePackageThread(void * args);
289 #endif /* AFS_PTHREAD_ENV */
290
291 static int VAttachVolumesByPartition(struct DiskPartition64 *diskP, 
292                                      int * nAttached, int * nUnattached);
293
294
295 #ifdef AFS_DEMAND_ATTACH_FS
296 /* demand attach fileserver extensions */
297
298 /* XXX
299  * in the future we will support serialization of VLRU state into the fs_state
300  * disk dumps
301  *
302  * these structures are the beginning of that effort
303  */
304 struct VLRU_DiskHeader {
305     struct versionStamp stamp;            /* magic and structure version number */
306     afs_uint32 mtime;                     /* time of dump to disk */
307     afs_uint32 num_records;               /* number of VLRU_DiskEntry records */
308 };
309
310 struct VLRU_DiskEntry {
311     afs_uint32 vid;                       /* volume ID */
312     afs_uint32 idx;                       /* generation */
313     afs_uint32 last_get;                  /* timestamp of last get */
314 };
315
316 struct VLRU_StartupQueue {
317     struct VLRU_DiskEntry * entry;
318     int num_entries;
319     int next_idx;
320 };
321
322 typedef struct vshutdown_thread_t {
323     struct rx_queue q;
324     pthread_mutex_t lock;
325     pthread_cond_t cv;
326     pthread_cond_t master_cv;
327     int n_threads;
328     int n_threads_complete;
329     int vol_remaining;
330     int schedule_version;
331     int pass;
332     byte n_parts;
333     byte n_parts_done_pass;
334     byte part_thread_target[VOLMAXPARTS+1];
335     byte part_done_pass[VOLMAXPARTS+1];
336     struct rx_queue * part_pass_head[VOLMAXPARTS+1];
337     int stats[4][VOLMAXPARTS+1];
338 } vshutdown_thread_t;
339 static void * VShutdownThread(void * args);
340
341
342 static Volume * VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode);
343 static int VCheckFree(Volume * vp);
344
345 /* VByP List */
346 static void AddVolumeToVByPList_r(Volume * vp);
347 static void DeleteVolumeFromVByPList_r(Volume * vp);
348 static void VVByPListBeginExclusive_r(struct DiskPartition64 * dp);
349 static void VVByPListEndExclusive_r(struct DiskPartition64 * dp);
350 static void VVByPListWait_r(struct DiskPartition64 * dp);
351
352 /* online salvager */
353 static int VCheckSalvage(register Volume * vp);
354 static int VUpdateSalvagePriority_r(Volume * vp);
355 static int VScheduleSalvage_r(Volume * vp);
356 static int VCancelSalvage_r(Volume * vp, int reason);
357
358 /* Volume hash table */
359 static void VReorderHash_r(VolumeHashChainHead * head, Volume * pp, Volume * vp);
360 static void VHashBeginExclusive_r(VolumeHashChainHead * head);
361 static void VHashEndExclusive_r(VolumeHashChainHead * head);
362 static void VHashWait_r(VolumeHashChainHead * head);
363
364 /* shutdown */
365 static int ShutdownVByPForPass_r(struct DiskPartition64 * dp, int pass);
366 static int ShutdownVolumeWalk_r(struct DiskPartition64 * dp, int pass,
367                                 struct rx_queue ** idx);
368 static void ShutdownController(vshutdown_thread_t * params);
369 static void ShutdownCreateSchedule(vshutdown_thread_t * params);
370
371 /* VLRU */
372 static void VLRU_ComputeConstants(void);
373 static void VInitVLRU(void);
374 static void VLRU_Init_Node_r(volatile Volume * vp);
375 static void VLRU_Add_r(volatile Volume * vp);
376 static void VLRU_Delete_r(volatile Volume * vp);
377 static void VLRU_UpdateAccess_r(volatile Volume * vp);
378 static void * VLRU_ScannerThread(void * args);
379 static void VLRU_Scan_r(int idx);
380 static void VLRU_Promote_r(int idx);
381 static void VLRU_Demote_r(int idx);
382 static void VLRU_SwitchQueues(volatile Volume * vp, int new_idx, int append);
383
384 /* soft detach */
385 static int VCheckSoftDetach(volatile Volume * vp, afs_uint32 thresh);
386 static int VCheckSoftDetachCandidate(volatile Volume * vp, afs_uint32 thresh);
387 static int VSoftDetachVolume_r(volatile Volume * vp, afs_uint32 thresh);
388 #endif /* AFS_DEMAND_ATTACH_FS */
389
390
391 struct Lock vol_listLock;       /* Lock obtained when listing volumes:  
392                                  * prevents a volume from being missed 
393                                  * if the volume is attached during a 
394                                  * list volumes */
395
396
397 static int TimeZoneCorrection;  /* Number of seconds west of GMT */
398
399 /* Common message used when the volume goes off line */
400 char *VSalvageMessage =
401     "Files in this volume are currently unavailable; call operations";
402
403 int VInit;                      /* 0 - uninitialized,
404                                  * 1 - initialized but not all volumes have been attached,
405                                  * 2 - initialized and all volumes have been attached,
406                                  * 3 - initialized, all volumes have been attached, and
407                                  * VConnectFS() has completed. */
408
409
410 bit32 VolumeCacheCheck;         /* Incremented everytime a volume goes on line--
411                                  * used to stamp volume headers and in-core
412                                  * vnodes.  When the volume goes on-line the
413                                  * vnode will be invalidated
414                                  * access only with VOL_LOCK held */
415
416
417
418
419 /***************************************************/
420 /* Startup routines                                */
421 /***************************************************/
422
423 int
424 VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVnodes,
425                    int connect, afs_uint32 volcache)
426 {
427     int errors = 0;             /* Number of errors while finding vice partitions. */
428     struct timeval tv;
429     struct timezone tz;
430
431     programType = pt;
432
433     memset(&VStats, 0, sizeof(VStats));
434     VStats.hdr_cache_size = 200;
435
436     VInitPartitionPackage();
437     VInitVolumeHash();
438 #ifdef AFS_DEMAND_ATTACH_FS
439     if (programType == fileServer) {
440         VInitVLRU();
441     } else {
442         VLRU_SetOptions(VLRU_SET_ENABLED, 0);
443     }
444 #endif
445
446 #ifdef AFS_PTHREAD_ENV
447     assert(pthread_mutex_init(&vol_glock_mutex, NULL) == 0);
448     assert(pthread_mutex_init(&vol_trans_mutex, NULL) == 0);
449     assert(pthread_cond_init(&vol_put_volume_cond, NULL) == 0);
450     assert(pthread_cond_init(&vol_sleep_cond, NULL) == 0);
451 #else /* AFS_PTHREAD_ENV */
452     IOMGR_Initialize();
453 #endif /* AFS_PTHREAD_ENV */
454     Lock_Init(&vol_listLock);
455
456     srandom(time(0));           /* For VGetVolumeInfo */
457     gettimeofday(&tv, &tz);
458     TimeZoneCorrection = tz.tz_minuteswest * 60;
459
460 #ifdef AFS_DEMAND_ATTACH_FS
461     assert(pthread_mutex_init(&vol_salvsync_mutex, NULL) == 0);
462 #endif /* AFS_DEMAND_ATTACH_FS */
463
464     /* Ok, we have done enough initialization that fileserver can 
465      * start accepting calls, even though the volumes may not be 
466      * available just yet.
467      */
468     VInit = 1;
469
470 #if defined(AFS_DEMAND_ATTACH_FS) && defined(SALVSYNC_BUILD_SERVER)
471     if (programType == salvageServer) {
472         SALVSYNC_salvInit();
473     }
474 #endif /* AFS_DEMAND_ATTACH_FS */
475 #ifdef FSSYNC_BUILD_SERVER
476     if (programType == fileServer) {
477         FSYNC_fsInit();
478     }
479 #endif
480 #if defined(AFS_DEMAND_ATTACH_FS) && defined(SALVSYNC_BUILD_CLIENT)
481     if (programType == fileServer) {
482         /* establish a connection to the salvager at this point */
483         assert(VConnectSALV() != 0);
484     }
485 #endif /* AFS_DEMAND_ATTACH_FS */
486
487     if (volcache > VStats.hdr_cache_size)
488         VStats.hdr_cache_size = volcache;
489     VInitVolumeHeaderCache(VStats.hdr_cache_size);
490
491     VInitVnodes(vLarge, nLargeVnodes);
492     VInitVnodes(vSmall, nSmallVnodes);
493
494
495     errors = VAttachPartitions();
496     if (errors)
497         return -1;
498
499     if (programType == fileServer) {
500         struct DiskPartition64 *diskP;
501 #ifdef AFS_PTHREAD_ENV
502         struct vinitvolumepackage_thread_t params;
503         struct diskpartition_queue_t * dpq;
504         int i, threads, parts;
505         pthread_t tid;
506         pthread_attr_t attrs;
507
508         assert(pthread_cond_init(&params.thread_done_cv,NULL) == 0);
509         queue_Init(&params);
510         params.n_threads_complete = 0;
511
512         /* create partition work queue */
513         for (parts=0, diskP = DiskPartitionList; diskP; diskP = diskP->next, parts++) {
514             dpq = (diskpartition_queue_t *) malloc(sizeof(struct diskpartition_queue_t));
515             assert(dpq != NULL);
516             dpq->diskP = diskP;
517             queue_Append(&params,dpq);
518         }
519
520         threads = MIN(parts, vol_attach_threads);
521
522         if (threads > 1) {
523             /* spawn off a bunch of initialization threads */
524             assert(pthread_attr_init(&attrs) == 0);
525             assert(pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED) == 0);
526
527             Log("VInitVolumePackage: beginning parallel fileserver startup\n");
528 #ifdef AFS_DEMAND_ATTACH_FS
529             Log("VInitVolumePackage: using %d threads to pre-attach volumes on %d partitions\n",
530                 threads, parts);
531 #else /* AFS_DEMAND_ATTACH_FS */
532             Log("VInitVolumePackage: using %d threads to attach volumes on %d partitions\n",
533                 threads, parts);
534 #endif /* AFS_DEMAND_ATTACH_FS */
535
536             VOL_LOCK;
537             for (i=0; i < threads; i++) {
538                 assert(pthread_create
539                        (&tid, &attrs, &VInitVolumePackageThread,
540                         &params) == 0);
541             }
542
543             while(params.n_threads_complete < threads) {
544                 VOL_CV_WAIT(&params.thread_done_cv);
545             }
546             VOL_UNLOCK;
547
548             assert(pthread_attr_destroy(&attrs) == 0);
549         } else {
550             /* if we're only going to run one init thread, don't bother creating
551              * another LWP */
552             Log("VInitVolumePackage: beginning single-threaded fileserver startup\n");
553 #ifdef AFS_DEMAND_ATTACH_FS
554             Log("VInitVolumePackage: using 1 thread to pre-attach volumes on %d partition(s)\n",
555                 parts);
556 #else /* AFS_DEMAND_ATTACH_FS */
557             Log("VInitVolumePackage: using 1 thread to attach volumes on %d partition(s)\n",
558                 parts);
559 #endif /* AFS_DEMAND_ATTACH_FS */
560
561             VInitVolumePackageThread(&params);
562         }
563
564         assert(pthread_cond_destroy(&params.thread_done_cv) == 0);
565
566 #else /* AFS_PTHREAD_ENV */
567         DIR *dirp;
568         struct dirent *dp;
569
570         /* Attach all the volumes in this partition */
571         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
572             int nAttached = 0, nUnattached = 0;
573             assert(VAttachVolumesByPartition(diskP, &nAttached, &nUnattached) == 0);
574         }
575 #endif /* AFS_PTHREAD_ENV */
576     }
577
578     VInit = 2;                  /* Initialized, and all volumes have been attached */
579 #ifdef FSSYNC_BUILD_CLIENT
580     if (programType == volumeUtility && connect) {
581         if (!VConnectFS()) {
582             Log("Unable to connect to file server; aborted\n");
583             exit(1);
584         }
585     }
586 #ifdef AFS_DEMAND_ATTACH_FS
587     else if (programType == salvageServer) {
588         if (!VConnectFS()) {
589             Log("Unable to connect to file server; aborted\n");
590             exit(1);
591         }
592     }
593 #endif /* AFS_DEMAND_ATTACH_FS */
594 #endif /* FSSYNC_BUILD_CLIENT */
595     return 0;
596 }
597
598 #ifdef AFS_PTHREAD_ENV
599 static void *
600 VInitVolumePackageThread(void * args) {
601     int errors = 0;             /* Number of errors while finding vice partitions. */
602
603     DIR *dirp;
604     struct dirent *dp;
605     struct DiskPartition64 *diskP;
606     struct vinitvolumepackage_thread_t * params;
607     struct diskpartition_queue_t * dpq;
608
609     params = (vinitvolumepackage_thread_t *) args;
610
611
612     VOL_LOCK;
613     /* Attach all the volumes in this partition */
614     while (queue_IsNotEmpty(params)) {
615         int nAttached = 0, nUnattached = 0;
616
617         dpq = queue_First(params,diskpartition_queue_t);
618         queue_Remove(dpq);
619         VOL_UNLOCK;
620         diskP = dpq->diskP;
621         free(dpq);
622
623         assert(VAttachVolumesByPartition(diskP, &nAttached, &nUnattached) == 0);
624
625         VOL_LOCK;
626     }
627
628     params->n_threads_complete++;
629     pthread_cond_signal(&params->thread_done_cv);
630     VOL_UNLOCK;
631     return NULL;
632 }
633 #endif /* AFS_PTHREAD_ENV */
634
635 /*
636  * attach all volumes on a given disk partition
637  */
638 static int
639 VAttachVolumesByPartition(struct DiskPartition64 *diskP, int * nAttached, int * nUnattached)
640 {
641   DIR * dirp;
642   struct dirent * dp;
643   int ret = 0;
644
645   Log("Partition %s: attaching volumes\n", diskP->name);
646   dirp = opendir(VPartitionPath(diskP));
647   if (!dirp) {
648     Log("opendir on Partition %s failed!\n", diskP->name);
649     return 1;
650   }
651
652   while ((dp = readdir(dirp))) {
653     char *p;
654     p = strrchr(dp->d_name, '.');
655     if (p != NULL && strcmp(p, VHDREXT) == 0) {
656       Error error;
657       Volume *vp;
658 #ifdef AFS_DEMAND_ATTACH_FS
659       vp = VPreAttachVolumeByName(&error, diskP->name, dp->d_name);
660 #else /* AFS_DEMAND_ATTACH_FS */
661       vp = VAttachVolumeByName(&error, diskP->name, dp->d_name,
662                                V_VOLUPD);
663 #endif /* AFS_DEMAND_ATTACH_FS */
664       (*(vp ? nAttached : nUnattached))++;
665       if (error == VOFFLINE)
666         Log("Volume %d stays offline (/vice/offline/%s exists)\n", VolumeNumber(dp->d_name), dp->d_name);
667       else if (LogLevel >= 5) {
668         Log("Partition %s: attached volume %d (%s)\n",
669             diskP->name, VolumeNumber(dp->d_name),
670             dp->d_name);
671       }
672 #if !defined(AFS_DEMAND_ATTACH_FS)
673       if (vp) {
674         VPutVolume(vp);
675       }
676 #endif /* AFS_DEMAND_ATTACH_FS */
677     }
678   }
679
680   Log("Partition %s: attached %d volumes; %d volumes not attached\n", diskP->name, *nAttached, *nUnattached);
681   closedir(dirp);
682   return ret;
683 }
684
685
686 /***************************************************/
687 /* Shutdown routines                               */
688 /***************************************************/
689
690 /*
691  * demand attach fs
692  * highly multithreaded volume package shutdown
693  *
694  * with the demand attach fileserver extensions,
695  * VShutdown has been modified to be multithreaded.
696  * In order to achieve optimal use of many threads,
697  * the shutdown code involves one control thread and
698  * n shutdown worker threads.  The control thread
699  * periodically examines the number of volumes available
700  * for shutdown on each partition, and produces a worker
701  * thread allocation schedule.  The idea is to eliminate
702  * redundant scheduling computation on the workers by
703  * having a single master scheduler.
704  *
705  * The scheduler's objectives are:
706  * (1) fairness
707  *   each partition with volumes remaining gets allocated
708  *   at least 1 thread (assuming sufficient threads)
709  * (2) performance
710  *   threads are allocated proportional to the number of
711  *   volumes remaining to be offlined.  This ensures that
712  *   the OS I/O scheduler has many requests to elevator
713  *   seek on partitions that will (presumably) take the
714  *   longest amount of time (from now) to finish shutdown
715  * (3) keep threads busy
716  *   when there are extra threads, they are assigned to
717  *   partitions using a simple round-robin algorithm
718  *
719  * In the future, we may wish to add the ability to adapt
720  * to the relative performance patterns of each disk
721  * partition.
722  *
723  *
724  * demand attach fs
725  * multi-step shutdown process
726  *
727  * demand attach shutdown is a four-step process. Each
728  * shutdown "pass" shuts down increasingly more difficult
729  * volumes.  The main purpose is to achieve better cache
730  * utilization during shutdown.
731  *
732  * pass 0
733  *   shutdown volumes in the unattached, pre-attached
734  *   and error states
735  * pass 1
736  *   shutdown attached volumes with cached volume headers
737  * pass 2
738  *   shutdown all volumes in non-exclusive states
739  * pass 3
740  *   shutdown all remaining volumes
741  */
742
743 void
744 VShutdown_r(void)
745 {
746     int i;
747     register Volume *vp, *np;
748     register afs_int32 code;
749 #ifdef AFS_DEMAND_ATTACH_FS
750     struct DiskPartition64 * diskP;
751     struct diskpartition_queue_t * dpq;
752     vshutdown_thread_t params;
753     pthread_t tid;
754     pthread_attr_t attrs;
755
756     memset(&params, 0, sizeof(vshutdown_thread_t));
757
758     for (params.n_parts=0, diskP = DiskPartitionList;
759          diskP; diskP = diskP->next, params.n_parts++);
760
761     Log("VShutdown:  shutting down on-line volumes on %d partition%s...\n", 
762         params.n_parts, params.n_parts > 1 ? "s" : "");
763
764     if (vol_attach_threads > 1) {
765         /* prepare for parallel shutdown */
766         params.n_threads = vol_attach_threads;
767         assert(pthread_mutex_init(&params.lock, NULL) == 0);
768         assert(pthread_cond_init(&params.cv, NULL) == 0);
769         assert(pthread_cond_init(&params.master_cv, NULL) == 0);
770         assert(pthread_attr_init(&attrs) == 0);
771         assert(pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED) == 0);
772         queue_Init(&params);
773
774         /* setup the basic partition information structures for
775          * parallel shutdown */
776         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
777             /* XXX debug */
778             struct rx_queue * qp, * nqp;
779             Volume * vp;
780             int count = 0;
781
782             VVByPListWait_r(diskP);
783             VVByPListBeginExclusive_r(diskP);
784
785             /* XXX debug */
786             for (queue_Scan(&diskP->vol_list, qp, nqp, rx_queue)) {
787                 vp = (Volume *)((char *)qp - offsetof(Volume, vol_list));
788                 if (vp->header)
789                     count++;
790             }
791             Log("VShutdown: partition %s has %d volumes with attached headers\n",
792                 VPartitionPath(diskP), count);
793                 
794
795             /* build up the pass 0 shutdown work queue */
796             dpq = (struct diskpartition_queue_t *) malloc(sizeof(struct diskpartition_queue_t));
797             assert(dpq != NULL);
798             dpq->diskP = diskP;
799             queue_Prepend(&params, dpq);
800
801             params.part_pass_head[diskP->device] = queue_First(&diskP->vol_list, rx_queue);
802         }
803
804         Log("VShutdown:  beginning parallel fileserver shutdown\n");
805         Log("VShutdown:  using %d threads to offline volumes on %d partition%s\n",
806             vol_attach_threads, params.n_parts, params.n_parts > 1 ? "s" : "" );
807
808         /* do pass 0 shutdown */
809         assert(pthread_mutex_lock(&params.lock) == 0);
810         for (i=0; i < params.n_threads; i++) {
811             assert(pthread_create
812                    (&tid, &attrs, &VShutdownThread,
813                     &params) == 0);
814         }
815         
816         /* wait for all the pass 0 shutdowns to complete */
817         while (params.n_threads_complete < params.n_threads) {
818             assert(pthread_cond_wait(&params.master_cv, &params.lock) == 0);
819         }
820         params.n_threads_complete = 0;
821         params.pass = 1;
822         assert(pthread_cond_broadcast(&params.cv) == 0);
823         assert(pthread_mutex_unlock(&params.lock) == 0);
824
825         Log("VShutdown:  pass 0 completed using the 1 thread per partition algorithm\n");
826         Log("VShutdown:  starting passes 1 through 3 using finely-granular mp-fast algorithm\n");
827
828         /* run the parallel shutdown scheduler. it will drop the glock internally */
829         ShutdownController(&params);
830         
831         /* wait for all the workers to finish pass 3 and terminate */
832         while (params.pass < 4) {
833             VOL_CV_WAIT(&params.cv);
834         }
835         
836         assert(pthread_attr_destroy(&attrs) == 0);
837         assert(pthread_cond_destroy(&params.cv) == 0);
838         assert(pthread_cond_destroy(&params.master_cv) == 0);
839         assert(pthread_mutex_destroy(&params.lock) == 0);
840
841         /* drop the VByPList exclusive reservations */
842         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
843             VVByPListEndExclusive_r(diskP);
844             Log("VShutdown:  %s stats : (pass[0]=%d, pass[1]=%d, pass[2]=%d, pass[3]=%d)\n",
845                 VPartitionPath(diskP),
846                 params.stats[0][diskP->device],
847                 params.stats[1][diskP->device],
848                 params.stats[2][diskP->device],
849                 params.stats[3][diskP->device]);
850         }
851
852         Log("VShutdown:  shutdown finished using %d threads\n", params.n_threads);
853     } else {
854         /* if we're only going to run one shutdown thread, don't bother creating
855          * another LWP */
856         Log("VShutdown:  beginning single-threaded fileserver shutdown\n");
857
858         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
859             VShutdownByPartition_r(diskP);
860         }
861     }
862
863     Log("VShutdown:  complete.\n");
864 #else /* AFS_DEMAND_ATTACH_FS */
865     Log("VShutdown:  shutting down on-line volumes...\n");
866     for (i = 0; i < VolumeHashTable.Size; i++) {
867         /* try to hold first volume in the hash table */
868         for (queue_Scan(&VolumeHashTable.Table[i],vp,np,Volume)) {
869             code = VHold_r(vp);
870             if (code == 0) {
871                 if (LogLevel >= 5)
872                     Log("VShutdown:  Attempting to take volume %u offline.\n",
873                         vp->hashid);
874                 
875                 /* next, take the volume offline (drops reference count) */
876                 VOffline_r(vp, "File server was shut down");
877             }
878         }
879     }
880     Log("VShutdown:  complete.\n");
881 #endif /* AFS_DEMAND_ATTACH_FS */
882 }
883
884 void
885 VShutdown(void)
886 {
887     VOL_LOCK;
888     VShutdown_r();
889     VOL_UNLOCK;
890 }
891
892 #ifdef AFS_DEMAND_ATTACH_FS
893 /*
894  * demand attach fs
895  * shutdown control thread
896  */
897 static void
898 ShutdownController(vshutdown_thread_t * params)
899 {
900     /* XXX debug */
901     struct DiskPartition64 * diskP;
902     Device id;
903     vshutdown_thread_t shadow;
904
905     ShutdownCreateSchedule(params);
906
907     while ((params->pass < 4) &&
908            (params->n_threads_complete < params->n_threads)) {
909         /* recompute schedule once per second */
910
911         memcpy(&shadow, params, sizeof(vshutdown_thread_t));
912
913         VOL_UNLOCK;
914         /* XXX debug */
915         Log("ShutdownController:  schedule version=%d, vol_remaining=%d, pass=%d\n",
916             shadow.schedule_version, shadow.vol_remaining, shadow.pass);
917         Log("ShutdownController:  n_threads_complete=%d, n_parts_done_pass=%d\n",
918             shadow.n_threads_complete, shadow.n_parts_done_pass);
919         for (diskP = DiskPartitionList; diskP; diskP=diskP->next) {
920             id = diskP->device;
921             Log("ShutdownController:  part[%d] : (len=%d, thread_target=%d, done_pass=%d, pass_head=%p)\n",
922                 id, 
923                 diskP->vol_list.len,
924                 shadow.part_thread_target[id], 
925                 shadow.part_done_pass[id], 
926                 shadow.part_pass_head[id]);
927         }
928
929         sleep(1);
930         VOL_LOCK;
931
932         ShutdownCreateSchedule(params);
933     }
934 }
935
936 /* create the shutdown thread work schedule.
937  * this scheduler tries to implement fairness
938  * by allocating at least 1 thread to each 
939  * partition with volumes to be shutdown,
940  * and then it attempts to allocate remaining
941  * threads based upon the amount of work left
942  */
943 static void
944 ShutdownCreateSchedule(vshutdown_thread_t * params)
945 {
946     struct DiskPartition64 * diskP;
947     int sum, thr_workload, thr_left;
948     int part_residue[VOLMAXPARTS+1];
949     Device id;
950
951     /* compute the total number of outstanding volumes */
952     sum = 0;
953     for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
954         sum += diskP->vol_list.len;
955     }
956     
957     params->schedule_version++;
958     params->vol_remaining = sum;
959
960     if (!sum)
961         return;
962
963     /* compute average per-thread workload */
964     thr_workload = sum / params->n_threads;
965     if (sum % params->n_threads)
966         thr_workload++;
967
968     thr_left = params->n_threads;
969     memset(&part_residue, 0, sizeof(part_residue));
970
971     /* for fairness, give every partition with volumes remaining
972      * at least one thread */
973     for (diskP = DiskPartitionList; diskP && thr_left; diskP = diskP->next) {
974         id = diskP->device;
975         if (diskP->vol_list.len) {
976             params->part_thread_target[id] = 1;
977             thr_left--;
978         } else {
979             params->part_thread_target[id] = 0;
980         }
981     }
982
983     if (thr_left && thr_workload) {
984         /* compute length-weighted workloads */
985         int delta;
986
987         for (diskP = DiskPartitionList; diskP && thr_left; diskP = diskP->next) {
988             id = diskP->device;
989             delta = (diskP->vol_list.len / thr_workload) -
990                 params->part_thread_target[id];
991             if (delta < 0) {
992                 continue;
993             }
994             if (delta < thr_left) {
995                 params->part_thread_target[id] += delta;
996                 thr_left -= delta;
997             } else {
998                 params->part_thread_target[id] += thr_left;
999                 thr_left = 0;
1000                 break;
1001             }
1002         }
1003     }
1004
1005     if (thr_left) {
1006         /* try to assign any leftover threads to partitions that
1007          * had volume lengths closer to needing thread_target+1 */
1008         int max_residue, max_id;
1009
1010         /* compute the residues */
1011         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1012             id = diskP->device;
1013             part_residue[id] = diskP->vol_list.len - 
1014                 (params->part_thread_target[id] * thr_workload);
1015         }
1016
1017         /* now try to allocate remaining threads to partitions with the
1018          * highest residues */
1019         while (thr_left) {
1020             max_residue = 0;
1021             for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1022                 id = diskP->device;
1023                 if (part_residue[id] > max_residue) {
1024                     max_residue = part_residue[id];
1025                     max_id = id;
1026                 }
1027             }
1028
1029             if (!max_residue) {
1030                 break;
1031             }
1032
1033             params->part_thread_target[max_id]++;
1034             thr_left--;
1035             part_residue[max_id] = 0;
1036         }
1037     }
1038
1039     if (thr_left) {
1040         /* punt and give any remaining threads equally to each partition */
1041         int alloc;
1042         if (thr_left >= params->n_parts) {
1043             alloc = thr_left / params->n_parts;
1044             for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1045                 id = diskP->device;
1046                 params->part_thread_target[id] += alloc;
1047                 thr_left -= alloc;
1048             }
1049         }
1050
1051         /* finish off the last of the threads */
1052         for (diskP = DiskPartitionList; thr_left && diskP; diskP = diskP->next) {
1053             id = diskP->device;
1054             params->part_thread_target[id]++;
1055             thr_left--;
1056         }
1057     }
1058 }
1059
1060 /* worker thread for parallel shutdown */
1061 static void *
1062 VShutdownThread(void * args)
1063 {
1064     struct rx_queue *qp;
1065     Volume * vp;
1066     vshutdown_thread_t * params;
1067     int part, code, found, pass, schedule_version_save, count;
1068     struct DiskPartition64 *diskP;
1069     struct diskpartition_queue_t * dpq;
1070     Device id;
1071
1072     params = (vshutdown_thread_t *) args;
1073
1074     /* acquire the shutdown pass 0 lock */
1075     assert(pthread_mutex_lock(&params->lock) == 0);
1076
1077     /* if there's still pass 0 work to be done,
1078      * get a work entry, and do a pass 0 shutdown */
1079     if (queue_IsNotEmpty(params)) {
1080         dpq = queue_First(params, diskpartition_queue_t);
1081         queue_Remove(dpq);
1082         assert(pthread_mutex_unlock(&params->lock) == 0);
1083         diskP = dpq->diskP;
1084         free(dpq);
1085         id = diskP->device;
1086
1087         count = 0;
1088         while (ShutdownVolumeWalk_r(diskP, 0, &params->part_pass_head[id]))
1089             count++;
1090         params->stats[0][diskP->device] = count;
1091         assert(pthread_mutex_lock(&params->lock) == 0);
1092     }
1093
1094     params->n_threads_complete++;
1095     if (params->n_threads_complete == params->n_threads) {
1096       /* notify control thread that all workers have completed pass 0 */
1097       assert(pthread_cond_signal(&params->master_cv) == 0);
1098     }
1099     while (params->pass == 0) {
1100       assert(pthread_cond_wait(&params->cv, &params->lock) == 0);
1101     }
1102
1103     /* switch locks */
1104     assert(pthread_mutex_unlock(&params->lock) == 0);
1105     VOL_LOCK;
1106
1107     pass = params->pass;
1108     assert(pass > 0);
1109
1110     /* now escalate through the more complicated shutdowns */
1111     while (pass <= 3) {
1112         schedule_version_save = params->schedule_version;
1113         found = 0;
1114         /* find a disk partition to work on */
1115         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1116             id = diskP->device;
1117             if (params->part_thread_target[id] && !params->part_done_pass[id]) {
1118                 params->part_thread_target[id]--;
1119                 found = 1;
1120                 break;
1121             }
1122         }
1123         
1124         if (!found) {
1125             /* hmm. for some reason the controller thread couldn't find anything for 
1126              * us to do. let's see if there's anything we can do */
1127             for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1128                 id = diskP->device;
1129                 if (diskP->vol_list.len && !params->part_done_pass[id]) {
1130                     found = 1;
1131                     break;
1132                 } else if (!params->part_done_pass[id]) {
1133                     params->part_done_pass[id] = 1;
1134                     params->n_parts_done_pass++;
1135                     if (pass == 3) {
1136                         Log("VShutdown:  done shutting down volumes on partition %s.\n",
1137                             VPartitionPath(diskP));
1138                     }
1139                 }
1140             }
1141         }
1142         
1143         /* do work on this partition until either the controller
1144          * creates a new schedule, or we run out of things to do
1145          * on this partition */
1146         if (found) {
1147             count = 0;
1148             while (!params->part_done_pass[id] &&
1149                    (schedule_version_save == params->schedule_version)) {
1150                 /* ShutdownVolumeWalk_r will drop the glock internally */
1151                 if (!ShutdownVolumeWalk_r(diskP, pass, &params->part_pass_head[id])) {
1152                     if (!params->part_done_pass[id]) {
1153                         params->part_done_pass[id] = 1;
1154                         params->n_parts_done_pass++;
1155                         if (pass == 3) {
1156                             Log("VShutdown:  done shutting down volumes on partition %s.\n",
1157                                 VPartitionPath(diskP));
1158                         }
1159                     }
1160                     break;
1161                 }
1162                 count++;
1163             }
1164
1165             params->stats[pass][id] += count;
1166         } else {
1167             /* ok, everyone is done this pass, proceed */
1168
1169             /* barrier lock */
1170             params->n_threads_complete++;
1171             while (params->pass == pass) {
1172                 if (params->n_threads_complete == params->n_threads) {
1173                     /* we are the last thread to complete, so we will
1174                      * reinitialize worker pool state for the next pass */
1175                     params->n_threads_complete = 0;
1176                     params->n_parts_done_pass = 0;
1177                     params->pass++;
1178                     for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1179                         id = diskP->device;
1180                         params->part_done_pass[id] = 0;
1181                         params->part_pass_head[id] = queue_First(&diskP->vol_list, rx_queue);
1182                     }
1183
1184                     /* compute a new thread schedule before releasing all the workers */
1185                     ShutdownCreateSchedule(params);
1186
1187                     /* wake up all the workers */
1188                     assert(pthread_cond_broadcast(&params->cv) == 0);
1189
1190                     VOL_UNLOCK;
1191                     Log("VShutdown:  pass %d completed using %d threads on %d partitions\n",
1192                         pass, params->n_threads, params->n_parts);
1193                     VOL_LOCK;
1194                 } else {
1195                     VOL_CV_WAIT(&params->cv);
1196                 }
1197             }
1198             pass = params->pass;
1199         }
1200         
1201         /* for fairness */
1202         VOL_UNLOCK;
1203         pthread_yield();
1204         VOL_LOCK;
1205     }
1206
1207     VOL_UNLOCK;
1208
1209     return NULL;
1210 }
1211
1212 /* shut down all volumes on a given disk partition 
1213  *
1214  * note that this function will not allow mp-fast
1215  * shutdown of a partition */
1216 int
1217 VShutdownByPartition_r(struct DiskPartition64 * dp)
1218 {
1219     int pass, retVal;
1220     int pass_stats[4];
1221     int total;
1222
1223     /* wait for other exclusive ops to finish */
1224     VVByPListWait_r(dp);
1225
1226     /* begin exclusive access */
1227     VVByPListBeginExclusive_r(dp);
1228
1229     /* pick the low-hanging fruit first,
1230      * then do the complicated ones last 
1231      * (has the advantage of keeping
1232      *  in-use volumes up until the bitter end) */
1233     for (pass = 0, total=0; pass < 4; pass++) {
1234         pass_stats[pass] = ShutdownVByPForPass_r(dp, pass);
1235         total += pass_stats[pass];
1236     }
1237
1238     /* end exclusive access */
1239     VVByPListEndExclusive_r(dp);
1240
1241     Log("VShutdownByPartition:  shut down %d volumes on %s (pass[0]=%d, pass[1]=%d, pass[2]=%d, pass[3]=%d)\n",
1242         total, VPartitionPath(dp), pass_stats[0], pass_stats[1], pass_stats[2], pass_stats[3]);
1243
1244     return retVal;
1245 }
1246
1247 /* internal shutdown functionality
1248  *
1249  * for multi-pass shutdown:
1250  * 0 to only "shutdown" {pre,un}attached and error state volumes
1251  * 1 to also shutdown attached volumes w/ volume header loaded
1252  * 2 to also shutdown attached volumes w/o volume header loaded
1253  * 3 to also shutdown exclusive state volumes 
1254  *
1255  * caller MUST hold exclusive access on the hash chain
1256  * because we drop vol_glock_mutex internally
1257  * 
1258  * this function is reentrant for passes 1--3 
1259  * (e.g. multiple threads can cooperate to 
1260  *  shutdown a partition mp-fast)
1261  *
1262  * pass 0 is not scaleable because the volume state data is
1263  * synchronized by vol_glock mutex, and the locking overhead
1264  * is too high to drop the lock long enough to do linked list
1265  * traversal
1266  */
1267 static int
1268 ShutdownVByPForPass_r(struct DiskPartition64 * dp, int pass)
1269 {
1270     struct rx_queue * q = queue_First(&dp->vol_list, rx_queue);
1271     register int i = 0;
1272
1273     while (ShutdownVolumeWalk_r(dp, pass, &q))
1274         i++;
1275
1276     return i;
1277 }
1278
1279 /* conditionally shutdown one volume on partition dp
1280  * returns 1 if a volume was shutdown in this pass,
1281  * 0 otherwise */
1282 static int
1283 ShutdownVolumeWalk_r(struct DiskPartition64 * dp, int pass,
1284                      struct rx_queue ** idx)
1285 {
1286     struct rx_queue *qp, *nqp;
1287     Volume * vp;
1288
1289     qp = *idx;
1290
1291     for (queue_ScanFrom(&dp->vol_list, qp, qp, nqp, rx_queue)) {
1292         vp = (Volume *) (((char *)qp) - offsetof(Volume, vol_list));
1293         
1294         switch (pass) {
1295         case 0:
1296             if ((V_attachState(vp) != VOL_STATE_UNATTACHED) &&
1297                 (V_attachState(vp) != VOL_STATE_ERROR) &&
1298                 (V_attachState(vp) != VOL_STATE_PREATTACHED)) {
1299                 break;
1300             }
1301         case 1:
1302             if ((V_attachState(vp) == VOL_STATE_ATTACHED) &&
1303                 (vp->header == NULL)) {
1304                 break;
1305             }
1306         case 2:
1307             if (VIsExclusiveState(V_attachState(vp))) {
1308                 break;
1309             }
1310         case 3:
1311             *idx = nqp;
1312             DeleteVolumeFromVByPList_r(vp);
1313             VShutdownVolume_r(vp);
1314             vp = NULL;
1315             return 1;
1316         }
1317     }
1318
1319     return 0;
1320 }
1321
1322 /*
1323  * shutdown a specific volume
1324  */
1325 /* caller MUST NOT hold a heavyweight ref on vp */
1326 int
1327 VShutdownVolume_r(Volume * vp)
1328 {
1329     int code;
1330
1331     VCreateReservation_r(vp);
1332
1333     if (LogLevel >= 5) {
1334         Log("VShutdownVolume_r:  vid=%u, device=%d, state=%hu\n",
1335             vp->hashid, vp->partition->device, V_attachState(vp));
1336     }
1337
1338     /* wait for other blocking ops to finish */
1339     VWaitExclusiveState_r(vp);
1340
1341     assert(VIsValidState(V_attachState(vp)));
1342     
1343     switch(V_attachState(vp)) {
1344     case VOL_STATE_SALVAGING:
1345         /* make sure salvager knows we don't want
1346          * the volume back */
1347         VCancelSalvage_r(vp, SALVSYNC_SHUTDOWN);
1348     case VOL_STATE_PREATTACHED:
1349     case VOL_STATE_ERROR:
1350         VChangeState_r(vp, VOL_STATE_UNATTACHED);
1351     case VOL_STATE_UNATTACHED:
1352         break;
1353     case VOL_STATE_GOING_OFFLINE:
1354     case VOL_STATE_SHUTTING_DOWN:
1355     case VOL_STATE_ATTACHED:
1356         code = VHold_r(vp);
1357         if (!code) {
1358             if (LogLevel >= 5)
1359                 Log("VShutdown:  Attempting to take volume %u offline.\n",
1360                     vp->hashid);
1361
1362             /* take the volume offline (drops reference count) */
1363             VOffline_r(vp, "File server was shut down");
1364         }
1365         break;
1366     }
1367     
1368     VCancelReservation_r(vp);
1369     vp = NULL;
1370     return 0;
1371 }
1372 #endif /* AFS_DEMAND_ATTACH_FS */
1373
1374
1375 /***************************************************/
1376 /* Header I/O routines                             */
1377 /***************************************************/
1378
1379 /* open a descriptor for the inode (h),
1380  * read in an on-disk structure into buffer (to) of size (size),
1381  * verify versionstamp in structure has magic (magic) and
1382  * optionally verify version (version) if (version) is nonzero
1383  */
1384 static void
1385 ReadHeader(Error * ec, IHandle_t * h, char *to, int size, bit32 magic,
1386            bit32 version)
1387 {
1388     struct versionStamp *vsn;
1389     FdHandle_t *fdP;
1390
1391     *ec = 0;
1392     if (h == NULL) {
1393         *ec = VSALVAGE;
1394         return;
1395     }
1396
1397     fdP = IH_OPEN(h);
1398     if (fdP == NULL) {
1399         *ec = VSALVAGE;
1400         return;
1401     }
1402
1403     if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1404         *ec = VSALVAGE;
1405         FDH_REALLYCLOSE(fdP);
1406         return;
1407     }
1408     vsn = (struct versionStamp *)to;
1409     if (FDH_READ(fdP, to, size) != size || vsn->magic != magic) {
1410         *ec = VSALVAGE;
1411         FDH_REALLYCLOSE(fdP);
1412         return;
1413     }
1414     FDH_CLOSE(fdP);
1415
1416     /* Check is conditional, in case caller wants to inspect version himself */
1417     if (version && vsn->version != version) {
1418         *ec = VSALVAGE;
1419     }
1420 }
1421
1422 void
1423 WriteVolumeHeader_r(Error * ec, Volume * vp)
1424 {
1425     IHandle_t *h = V_diskDataHandle(vp);
1426     FdHandle_t *fdP;
1427
1428     *ec = 0;
1429
1430     fdP = IH_OPEN(h);
1431     if (fdP == NULL) {
1432         *ec = VSALVAGE;
1433         return;
1434     }
1435     if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1436         *ec = VSALVAGE;
1437         FDH_REALLYCLOSE(fdP);
1438         return;
1439     }
1440     if (FDH_WRITE(fdP, (char *)&V_disk(vp), sizeof(V_disk(vp)))
1441         != sizeof(V_disk(vp))) {
1442         *ec = VSALVAGE;
1443         FDH_REALLYCLOSE(fdP);
1444         return;
1445     }
1446     FDH_CLOSE(fdP);
1447 }
1448
1449 /* VolumeHeaderToDisk
1450  * Allows for storing 64 bit inode numbers in on-disk volume header
1451  * file.
1452  */
1453 /* convert in-memory representation of a volume header to the
1454  * on-disk representation of a volume header */
1455 void
1456 VolumeHeaderToDisk(VolumeDiskHeader_t * dh, VolumeHeader_t * h)
1457 {
1458
1459     memset((char *)dh, 0, sizeof(VolumeDiskHeader_t));
1460     dh->stamp = h->stamp;
1461     dh->id = h->id;
1462     dh->parent = h->parent;
1463
1464 #ifdef AFS_64BIT_IOPS_ENV
1465     dh->volumeInfo_lo = (afs_int32) h->volumeInfo & 0xffffffff;
1466     dh->volumeInfo_hi = (afs_int32) (h->volumeInfo >> 32) & 0xffffffff;
1467     dh->smallVnodeIndex_lo = (afs_int32) h->smallVnodeIndex & 0xffffffff;
1468     dh->smallVnodeIndex_hi =
1469         (afs_int32) (h->smallVnodeIndex >> 32) & 0xffffffff;
1470     dh->largeVnodeIndex_lo = (afs_int32) h->largeVnodeIndex & 0xffffffff;
1471     dh->largeVnodeIndex_hi =
1472         (afs_int32) (h->largeVnodeIndex >> 32) & 0xffffffff;
1473     dh->linkTable_lo = (afs_int32) h->linkTable & 0xffffffff;
1474     dh->linkTable_hi = (afs_int32) (h->linkTable >> 32) & 0xffffffff;
1475 #else
1476     dh->volumeInfo_lo = h->volumeInfo;
1477     dh->smallVnodeIndex_lo = h->smallVnodeIndex;
1478     dh->largeVnodeIndex_lo = h->largeVnodeIndex;
1479     dh->linkTable_lo = h->linkTable;
1480 #endif
1481 }
1482
1483 /* DiskToVolumeHeader
1484  * Converts an on-disk representation of a volume header to
1485  * the in-memory representation of a volume header.
1486  *
1487  * Makes the assumption that AFS has *always* 
1488  * zero'd the volume header file so that high parts of inode
1489  * numbers are 0 in older (SGI EFS) volume header files.
1490  */
1491 void
1492 DiskToVolumeHeader(VolumeHeader_t * h, VolumeDiskHeader_t * dh)
1493 {
1494     memset((char *)h, 0, sizeof(VolumeHeader_t));
1495     h->stamp = dh->stamp;
1496     h->id = dh->id;
1497     h->parent = dh->parent;
1498
1499 #ifdef AFS_64BIT_IOPS_ENV
1500     h->volumeInfo =
1501         (Inode) dh->volumeInfo_lo | ((Inode) dh->volumeInfo_hi << 32);
1502
1503     h->smallVnodeIndex =
1504         (Inode) dh->smallVnodeIndex_lo | ((Inode) dh->
1505                                           smallVnodeIndex_hi << 32);
1506
1507     h->largeVnodeIndex =
1508         (Inode) dh->largeVnodeIndex_lo | ((Inode) dh->
1509                                           largeVnodeIndex_hi << 32);
1510     h->linkTable =
1511         (Inode) dh->linkTable_lo | ((Inode) dh->linkTable_hi << 32);
1512 #else
1513     h->volumeInfo = dh->volumeInfo_lo;
1514     h->smallVnodeIndex = dh->smallVnodeIndex_lo;
1515     h->largeVnodeIndex = dh->largeVnodeIndex_lo;
1516     h->linkTable = dh->linkTable_lo;
1517 #endif
1518 }
1519
1520
1521 /***************************************************/
1522 /* Volume Attachment routines                      */
1523 /***************************************************/
1524
1525 #ifdef AFS_DEMAND_ATTACH_FS
1526 /**
1527  * pre-attach a volume given its path.
1528  *
1529  * @param[out] ec         outbound error code
1530  * @param[in]  partition  partition path string
1531  * @param[in]  name       volume id string
1532  *
1533  * @return volume object pointer
1534  *
1535  * @note A pre-attached volume will only have its partition
1536  *       and hashid fields initialized.  At first call to 
1537  *       VGetVolume, the volume will be fully attached.
1538  *
1539  */
1540 Volume *
1541 VPreAttachVolumeByName(Error * ec, char *partition, char *name)
1542 {
1543     Volume * vp;
1544     VOL_LOCK;
1545     vp = VPreAttachVolumeByName_r(ec, partition, name);
1546     VOL_UNLOCK;
1547     return vp;
1548 }
1549
1550 /**
1551  * pre-attach a volume given its path.
1552  *
1553  * @param[out] ec         outbound error code
1554  * @param[in]  partition  path to vice partition
1555  * @param[in]  name       volume id string
1556  *
1557  * @return volume object pointer
1558  *
1559  * @pre VOL_LOCK held
1560  *
1561  * @internal volume package internal use only.
1562  */
1563 Volume *
1564 VPreAttachVolumeByName_r(Error * ec, char *partition, char *name)
1565 {
1566     return VPreAttachVolumeById_r(ec, 
1567                                   partition,
1568                                   VolumeNumber(name));
1569 }
1570
1571 /**
1572  * pre-attach a volume given its path and numeric volume id.
1573  *
1574  * @param[out] ec          error code return
1575  * @param[in]  partition   path to vice partition
1576  * @param[in]  volumeId    numeric volume id
1577  *
1578  * @return volume object pointer
1579  *
1580  * @pre VOL_LOCK held
1581  *
1582  * @internal volume package internal use only.
1583  */
1584 Volume *
1585 VPreAttachVolumeById_r(Error * ec, 
1586                        char * partition,
1587                        VolId volumeId)
1588 {
1589     Volume *vp;
1590     struct DiskPartition64 *partp;
1591
1592     *ec = 0;
1593
1594     assert(programType == fileServer);
1595
1596     if (!(partp = VGetPartition_r(partition, 0))) {
1597         *ec = VNOVOL;
1598         Log("VPreAttachVolumeById_r:  Error getting partition (%s)\n", partition);
1599         return NULL;
1600     }
1601
1602     vp = VLookupVolume_r(ec, volumeId, NULL);
1603     if (*ec) {
1604         return NULL;
1605     }
1606
1607     return VPreAttachVolumeByVp_r(ec, partp, vp, volumeId);
1608 }
1609
1610 /**
1611  * preattach a volume.
1612  *
1613  * @param[out] ec     outbound error code
1614  * @param[in]  partp  pointer to partition object
1615  * @param[in]  vp     pointer to volume object
1616  * @param[in]  vid    volume id
1617  *
1618  * @return volume object pointer
1619  *
1620  * @pre VOL_LOCK is held.
1621  *
1622  * @warning Returned volume object pointer does not have to
1623  *          equal the pointer passed in as argument vp.  There
1624  *          are potential race conditions which can result in
1625  *          the pointers having different values.  It is up to
1626  *          the caller to make sure that references are handled
1627  *          properly in this case.
1628  *
1629  * @note If there is already a volume object registered with
1630  *       the same volume id, its pointer MUST be passed as 
1631  *       argument vp.  Failure to do so will result in a silent
1632  *       failure to preattach.
1633  *
1634  * @internal volume package internal use only.
1635  */
1636 Volume * 
1637 VPreAttachVolumeByVp_r(Error * ec, 
1638                        struct DiskPartition64 * partp, 
1639                        Volume * vp,
1640                        VolId vid)
1641 {
1642     Volume *nvp = NULL;
1643
1644     *ec = 0;
1645
1646     /* check to see if pre-attach already happened */
1647     if (vp && 
1648         (V_attachState(vp) != VOL_STATE_UNATTACHED) && 
1649         (V_attachState(vp) != VOL_STATE_PREATTACHED) &&
1650         !VIsErrorState(V_attachState(vp))) {
1651         /*
1652          * pre-attach is a no-op in all but the following cases:
1653          *
1654          *   - volume is unattached
1655          *   - volume is in an error state
1656          *   - volume is pre-attached
1657          */
1658         Log("VPreattachVolumeByVp_r: volume %u not in quiescent state\n", vid);
1659         goto done;
1660     } else if (vp) {
1661         /* we're re-attaching a volume; clear out some old state */
1662         memset(&vp->salvage, 0, sizeof(struct VolumeOnlineSalvage));
1663
1664         if (V_partition(vp) != partp) {
1665             /* XXX potential race */
1666             DeleteVolumeFromVByPList_r(vp);
1667         }
1668     } else {
1669         /* if we need to allocate a new Volume struct,
1670          * go ahead and drop the vol glock, otherwise
1671          * do the basic setup synchronised, as it's
1672          * probably not worth dropping the lock */
1673         VOL_UNLOCK;
1674
1675         /* allocate the volume structure */
1676         vp = nvp = (Volume *) malloc(sizeof(Volume));
1677         assert(vp != NULL);
1678         memset(vp, 0, sizeof(Volume));
1679         queue_Init(&vp->vnode_list);
1680         assert(pthread_cond_init(&V_attachCV(vp), NULL) == 0);
1681     }
1682
1683     /* link the volume with its associated vice partition */
1684     vp->device = partp->device;
1685     vp->partition = partp;
1686     vp->hashid = vid;
1687
1688     /* if we dropped the lock, reacquire the lock,
1689      * check for pre-attach races, and then add
1690      * the volume to the hash table */
1691     if (nvp) {
1692         VOL_LOCK;
1693         nvp = VLookupVolume_r(ec, vid, NULL);
1694         if (*ec) {
1695             free(vp);
1696             vp = NULL;
1697             goto done;
1698         } else if (nvp) { /* race detected */
1699             free(vp);
1700             vp = nvp;
1701             goto done;
1702         } else {
1703           /* hack to make up for VChangeState_r() decrementing 
1704            * the old state counter */
1705           VStats.state_levels[0]++;
1706         }
1707     }
1708
1709     /* put pre-attached volume onto the hash table
1710      * and bring it up to the pre-attached state */
1711     AddVolumeToHashTable(vp, vp->hashid);
1712     AddVolumeToVByPList_r(vp);
1713     VLRU_Init_Node_r(vp);
1714     VChangeState_r(vp, VOL_STATE_PREATTACHED);
1715
1716     if (LogLevel >= 5)
1717         Log("VPreAttachVolumeByVp_r:  volume %u pre-attached\n", vp->hashid);
1718
1719   done:
1720     if (*ec)
1721         return NULL;
1722     else
1723         return vp;
1724 }
1725 #endif /* AFS_DEMAND_ATTACH_FS */
1726
1727 /* Attach an existing volume, given its pathname, and return a
1728    pointer to the volume header information.  The volume also
1729    normally goes online at this time.  An offline volume
1730    must be reattached to make it go online */
1731 Volume *
1732 VAttachVolumeByName(Error * ec, char *partition, char *name, int mode)
1733 {
1734     Volume *retVal;
1735     VOL_LOCK;
1736     retVal = VAttachVolumeByName_r(ec, partition, name, mode);
1737     VOL_UNLOCK;
1738     return retVal;
1739 }
1740
1741 Volume *
1742 VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
1743 {
1744     register Volume *vp = NULL, *svp = NULL;
1745     int fd, n;
1746     struct afs_stat status;
1747     struct VolumeDiskHeader diskHeader;
1748     struct VolumeHeader iheader;
1749     struct DiskPartition64 *partp;
1750     char path[64];
1751     int isbusy = 0;
1752     VolId volumeId;
1753 #ifdef AFS_DEMAND_ATTACH_FS
1754     VolumeStats stats_save;
1755 #endif /* AFS_DEMAND_ATTACH_FS */
1756
1757     *ec = 0;
1758    
1759     volumeId = VolumeNumber(name);
1760
1761     if (!(partp = VGetPartition_r(partition, 0))) {
1762         *ec = VNOVOL;
1763         Log("VAttachVolume: Error getting partition (%s)\n", partition);
1764         goto done;
1765     }
1766
1767     if (programType == volumeUtility) {
1768         assert(VInit == 3);
1769         VLockPartition_r(partition);
1770     } else if (programType == fileServer) {
1771 #ifdef AFS_DEMAND_ATTACH_FS
1772         /* lookup the volume in the hash table */
1773         vp = VLookupVolume_r(ec, volumeId, NULL);
1774         if (*ec) {
1775             return NULL;
1776         }
1777
1778         if (vp) {
1779             /* save any counters that are supposed to
1780              * be monotonically increasing over the
1781              * lifetime of the fileserver */
1782             memcpy(&stats_save, &vp->stats, sizeof(VolumeStats));
1783         } else {
1784             memset(&stats_save, 0, sizeof(VolumeStats));
1785         }
1786
1787         /* if there's something in the hash table, and it's not
1788          * in the pre-attach state, then we may need to detach
1789          * it before proceeding */
1790         if (vp && (V_attachState(vp) != VOL_STATE_PREATTACHED)) {
1791             VCreateReservation_r(vp);
1792             VWaitExclusiveState_r(vp);
1793
1794             /* at this point state must be one of:
1795              *   UNATTACHED,
1796              *   ATTACHED,
1797              *   SHUTTING_DOWN,
1798              *   GOING_OFFLINE,
1799              *   SALVAGING,
1800              *   ERROR
1801              */
1802
1803             if (vp->specialStatus == VBUSY)
1804                 isbusy = 1;
1805             
1806             /* if it's already attached, see if we can return it */
1807             if (V_attachState(vp) == VOL_STATE_ATTACHED) {
1808                 VGetVolumeByVp_r(ec, vp);
1809                 if (V_inUse(vp)) {
1810                     VCancelReservation_r(vp);
1811                     return vp;
1812                 }
1813
1814                 /* otherwise, we need to detach, and attempt to re-attach */
1815                 VDetachVolume_r(ec, vp);
1816                 if (*ec) {
1817                     Log("VAttachVolume: Error detaching old volume instance (%s)\n", name);
1818                 }
1819             } else {
1820                 /* if it isn't fully attached, delete from the hash tables,
1821                    and let the refcounter handle the rest */
1822                 DeleteVolumeFromHashTable(vp);
1823                 DeleteVolumeFromVByPList_r(vp);
1824             }
1825
1826             VCancelReservation_r(vp);
1827             vp = NULL;
1828         }
1829
1830         /* pre-attach volume if it hasn't been done yet */
1831         if (!vp || 
1832             (V_attachState(vp) == VOL_STATE_UNATTACHED) ||
1833             (V_attachState(vp) == VOL_STATE_ERROR)) {
1834             svp = vp;
1835             vp = VPreAttachVolumeByVp_r(ec, partp, vp, volumeId);
1836             if (*ec) {
1837                 return NULL;
1838             }
1839         }
1840
1841         assert(vp != NULL);
1842
1843         /* handle pre-attach races 
1844          *
1845          * multiple threads can race to pre-attach a volume,
1846          * but we can't let them race beyond that
1847          * 
1848          * our solution is to let the first thread to bring
1849          * the volume into an exclusive state win; the other
1850          * threads just wait until it finishes bringing the
1851          * volume online, and then they do a vgetvolumebyvp
1852          */
1853         if (svp && (svp != vp)) {
1854             /* wait for other exclusive ops to finish */
1855             VCreateReservation_r(vp);
1856             VWaitExclusiveState_r(vp);
1857
1858             /* get a heavyweight ref, kill the lightweight ref, and return */
1859             VGetVolumeByVp_r(ec, vp);
1860             VCancelReservation_r(vp);
1861             return vp;
1862         }
1863
1864         /* at this point, we are chosen as the thread to do
1865          * demand attachment for this volume. all other threads
1866          * doing a getvolume on vp->hashid will block until we finish */
1867
1868         /* make sure any old header cache entries are invalidated
1869          * before proceeding */
1870         FreeVolumeHeader(vp);
1871
1872         VChangeState_r(vp, VOL_STATE_ATTACHING);
1873
1874         /* restore any saved counters */
1875         memcpy(&vp->stats, &stats_save, sizeof(VolumeStats));
1876 #else /* AFS_DEMAND_ATTACH_FS */
1877         vp = VGetVolume_r(ec, volumeId);
1878         if (vp) {
1879             if (V_inUse(vp))
1880                 return vp;
1881             if (vp->specialStatus == VBUSY)
1882                 isbusy = 1;
1883             VDetachVolume_r(ec, vp);
1884             if (*ec) {
1885                 Log("VAttachVolume: Error detaching volume (%s)\n", name);
1886             }
1887             vp = NULL;
1888         }
1889 #endif /* AFS_DEMAND_ATTACH_FS */
1890     }
1891
1892     *ec = 0;
1893     strcpy(path, VPartitionPath(partp));
1894
1895     VOL_UNLOCK;
1896
1897     strcat(path, "/");
1898     strcat(path, name);
1899     if ((fd = afs_open(path, O_RDONLY)) == -1 || afs_fstat(fd, &status) == -1) {
1900         Log("VAttachVolume: Failed to open %s (errno %d)\n", path, errno);
1901         if (fd > -1)
1902             close(fd);
1903         *ec = VNOVOL;
1904         VOL_LOCK;
1905         goto done;
1906     }
1907     n = read(fd, &diskHeader, sizeof(diskHeader));
1908     close(fd);
1909     if (n != sizeof(diskHeader)
1910         || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1911         Log("VAttachVolume: Error reading volume header %s\n", path);
1912         *ec = VSALVAGE;
1913         VOL_LOCK;
1914         goto done;
1915     }
1916     if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
1917         Log("VAttachVolume: Volume %s, version number is incorrect; volume needs salvaged\n", path);
1918         *ec = VSALVAGE;
1919         VOL_LOCK;
1920         goto done;
1921     }
1922
1923     DiskToVolumeHeader(&iheader, &diskHeader);
1924 #ifdef FSSYNC_BUILD_CLIENT
1925     if (programType == volumeUtility && mode != V_SECRETLY && mode != V_PEEK) {
1926         VOL_LOCK;
1927         if (FSYNC_VolOp(iheader.id, partition, FSYNC_VOL_NEEDVOLUME, mode, NULL)
1928             != SYNC_OK) {
1929             Log("VAttachVolume: attach of volume %u apparently denied by file server\n", iheader.id);
1930             *ec = VNOVOL;       /* XXXX */
1931             goto done;
1932         }
1933         VOL_UNLOCK;
1934     }
1935 #endif
1936
1937     if (!vp) {
1938       vp = (Volume *) calloc(1, sizeof(Volume));
1939       assert(vp != NULL);
1940       vp->device = partp->device;
1941       vp->partition = partp;
1942       queue_Init(&vp->vnode_list);
1943 #ifdef AFS_DEMAND_ATTACH_FS
1944       assert(pthread_cond_init(&V_attachCV(vp), NULL) == 0);
1945 #endif /* AFS_DEMAND_ATTACH_FS */
1946     }
1947
1948     /* attach2 is entered without any locks, and returns
1949      * with vol_glock_mutex held */
1950     vp = attach2(ec, volumeId, path, &iheader, partp, vp, isbusy, mode);
1951
1952     if (programType == volumeUtility && vp) {
1953 #ifdef AFS_DEMAND_ATTACH_FS
1954         /* for dafs, we should tell the fileserver, except for V_PEEK
1955          * where we know it is not necessary */
1956         if (mode == V_PEEK) {
1957             vp->needsPutBack = 0;
1958         } else {
1959             vp->needsPutBack = 1;
1960         }
1961 #else /* !AFS_DEMAND_ATTACH_FS */
1962         /* duplicate computation in fssync.c about whether the server
1963          * takes the volume offline or not.  If the volume isn't
1964          * offline, we must not return it when we detach the volume,
1965          * or the server will abort */
1966         if (mode == V_READONLY || mode == V_PEEK
1967             || (!VolumeWriteable(vp) && (mode == V_CLONE || mode == V_DUMP)))
1968             vp->needsPutBack = 0;
1969         else
1970             vp->needsPutBack = 1;
1971 #endif /* !AFS_DEMAND_ATTACH_FS */
1972     }
1973     /* OK, there's a problem here, but one that I don't know how to
1974      * fix right now, and that I don't think should arise often.
1975      * Basically, we should only put back this volume to the server if
1976      * it was given to us by the server, but since we don't have a vp,
1977      * we can't run the VolumeWriteable function to find out as we do
1978      * above when computing vp->needsPutBack.  So we send it back, but
1979      * there's a path in VAttachVolume on the server which may abort
1980      * if this volume doesn't have a header.  Should be pretty rare
1981      * for all of that to happen, but if it does, probably the right
1982      * fix is for the server to allow the return of readonly volumes
1983      * that it doesn't think are really checked out. */
1984 #ifdef FSSYNC_BUILD_CLIENT
1985     if (programType == volumeUtility && vp == NULL &&
1986         mode != V_SECRETLY && mode != V_PEEK) {
1987         FSYNC_VolOp(iheader.id, partition, FSYNC_VOL_ON, 0, NULL);
1988     } else 
1989 #endif
1990     if (programType == fileServer && vp) {
1991 #ifdef AFS_DEMAND_ATTACH_FS
1992         /* 
1993          * we can get here in cases where we don't "own"
1994          * the volume (e.g. volume owned by a utility).
1995          * short circuit around potential disk header races.
1996          */
1997         if (V_attachState(vp) != VOL_STATE_ATTACHED) {
1998             goto done;
1999         }
2000 #endif
2001         V_needsCallback(vp) = 0;
2002 #ifdef  notdef
2003         if (VInit >= 2 && V_BreakVolumeCallbacks) {
2004             Log("VAttachVolume: Volume %u was changed externally; breaking callbacks\n", V_id(vp));
2005             (*V_BreakVolumeCallbacks) (V_id(vp));
2006         }
2007 #endif
2008         VUpdateVolume_r(ec, vp, 0);
2009         if (*ec) {
2010             Log("VAttachVolume: Error updating volume\n");
2011             if (vp)
2012                 VPutVolume_r(vp);
2013             goto done;
2014         }
2015         if (VolumeWriteable(vp) && V_dontSalvage(vp) == 0) {
2016 #ifndef AFS_DEMAND_ATTACH_FS
2017             /* This is a hack: by temporarily setting the incore
2018              * dontSalvage flag ON, the volume will be put back on the
2019              * Update list (with dontSalvage OFF again).  It will then
2020              * come back in N minutes with DONT_SALVAGE eventually
2021              * set.  This is the way that volumes that have never had
2022              * it set get it set; or that volumes that have been
2023              * offline without DONT SALVAGE having been set also
2024              * eventually get it set */
2025             V_dontSalvage(vp) = DONT_SALVAGE;
2026 #endif /* !AFS_DEMAND_ATTACH_FS */
2027             VAddToVolumeUpdateList_r(ec, vp);
2028             if (*ec) {
2029                 Log("VAttachVolume: Error adding volume to update list\n");
2030                 if (vp)
2031                     VPutVolume_r(vp);
2032                 goto done;
2033             }
2034         }
2035         if (LogLevel)
2036             Log("VOnline:  volume %u (%s) attached and online\n", V_id(vp),
2037                 V_name(vp));
2038     }
2039
2040   done:
2041     if (programType == volumeUtility) {
2042         VUnlockPartition_r(partition);
2043     }
2044     if (*ec) {
2045 #ifdef AFS_DEMAND_ATTACH_FS
2046         /* attach failed; make sure we're in error state */
2047         if (vp && !VIsErrorState(V_attachState(vp))) {
2048             VChangeState_r(vp, VOL_STATE_ERROR);
2049         }
2050 #endif /* AFS_DEMAND_ATTACH_FS */
2051         return NULL;
2052     } else {
2053         return vp;
2054     }
2055 }
2056
2057 #ifdef AFS_DEMAND_ATTACH_FS
2058 /* VAttachVolumeByVp_r
2059  *
2060  * finish attaching a volume that is
2061  * in a less than fully attached state
2062  */
2063 /* caller MUST hold a ref count on vp */
2064 static Volume *
2065 VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode)
2066 {
2067     char name[VMAXPATHLEN];
2068     int fd, n, reserve = 0;
2069     struct afs_stat status;
2070     struct VolumeDiskHeader diskHeader;
2071     struct VolumeHeader iheader;
2072     struct DiskPartition64 *partp;
2073     char path[64];
2074     int isbusy = 0;
2075     VolId volumeId;
2076     Volume * nvp;
2077     VolumeStats stats_save;
2078     *ec = 0;
2079
2080     /* volume utility should never call AttachByVp */
2081     assert(programType == fileServer);
2082    
2083     volumeId = vp->hashid;
2084     partp = vp->partition;
2085     VolumeExternalName_r(volumeId, name, sizeof(name));
2086
2087
2088     /* if another thread is performing a blocking op, wait */
2089     VWaitExclusiveState_r(vp);
2090
2091     memcpy(&stats_save, &vp->stats, sizeof(VolumeStats));
2092
2093     /* if it's already attached, see if we can return it */
2094     if (V_attachState(vp) == VOL_STATE_ATTACHED) {
2095         VGetVolumeByVp_r(ec, vp);
2096         if (V_inUse(vp)) {
2097             return vp;
2098         } else {
2099             if (vp->specialStatus == VBUSY)
2100                 isbusy = 1;
2101             VDetachVolume_r(ec, vp);
2102             if (*ec) {
2103                 Log("VAttachVolume: Error detaching volume (%s)\n", name);
2104             }
2105             vp = NULL;
2106         }
2107     }
2108
2109     /* pre-attach volume if it hasn't been done yet */
2110     if (!vp || 
2111         (V_attachState(vp) == VOL_STATE_UNATTACHED) ||
2112         (V_attachState(vp) == VOL_STATE_ERROR)) {
2113         nvp = VPreAttachVolumeByVp_r(ec, partp, vp, volumeId);
2114         if (*ec) {
2115             return NULL;
2116         }
2117         if (nvp != vp) {
2118             reserve = 1;
2119             VCreateReservation_r(nvp);
2120             vp = nvp;
2121         }
2122     }
2123     
2124     assert(vp != NULL);
2125     VChangeState_r(vp, VOL_STATE_ATTACHING);
2126
2127     /* restore monotonically increasing stats */
2128     memcpy(&vp->stats, &stats_save, sizeof(VolumeStats));
2129
2130     *ec = 0;
2131
2132
2133     /* compute path to disk header, 
2134      * read in header, 
2135      * and verify magic and version stamps */
2136     strcpy(path, VPartitionPath(partp));
2137
2138     VOL_UNLOCK;
2139
2140     strcat(path, "/");
2141     strcat(path, name);
2142     if ((fd = afs_open(path, O_RDONLY)) == -1 || afs_fstat(fd, &status) == -1) {
2143         Log("VAttachVolume: Failed to open %s (errno %d)\n", path, errno);
2144         if (fd > -1)
2145             close(fd);
2146         *ec = VNOVOL;
2147         VOL_LOCK;
2148         goto done;
2149     }
2150     n = read(fd, &diskHeader, sizeof(diskHeader));
2151     close(fd);
2152     if (n != sizeof(diskHeader)
2153         || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
2154         Log("VAttachVolume: Error reading volume header %s\n", path);
2155         *ec = VSALVAGE;
2156         VOL_LOCK;
2157         goto done;
2158     }
2159     if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
2160         Log("VAttachVolume: Volume %s, version number is incorrect; volume needs salvaged\n", path);
2161         *ec = VSALVAGE;
2162         VOL_LOCK;
2163         goto done;
2164     }
2165
2166     /* convert on-disk header format to in-memory header format */
2167     DiskToVolumeHeader(&iheader, &diskHeader);
2168
2169     /* do volume attach
2170      *
2171      * NOTE: attach2 is entered without any locks, and returns
2172      * with vol_glock_mutex held */
2173     vp = attach2(ec, volumeId, path, &iheader, partp, vp, isbusy, mode);
2174
2175     /*
2176      * the event that an error was encountered, or
2177      * the volume was not brought to an attached state
2178      * for any reason, skip to the end.  We cannot
2179      * safely call VUpdateVolume unless we "own" it.
2180      */
2181     if (*ec || 
2182         (vp == NULL) ||
2183         (V_attachState(vp) != VOL_STATE_ATTACHED)) {
2184         goto done;
2185     }
2186
2187     V_needsCallback(vp) = 0;
2188     VUpdateVolume_r(ec, vp, 0);
2189     if (*ec) {
2190         Log("VAttachVolume: Error updating volume %u\n", vp->hashid);
2191         VPutVolume_r(vp);
2192         goto done;
2193     }
2194     if (VolumeWriteable(vp) && V_dontSalvage(vp) == 0) {
2195 #ifndef AFS_DEMAND_ATTACH_FS
2196         /* This is a hack: by temporarily setting the incore
2197          * dontSalvage flag ON, the volume will be put back on the
2198          * Update list (with dontSalvage OFF again).  It will then
2199          * come back in N minutes with DONT_SALVAGE eventually
2200          * set.  This is the way that volumes that have never had
2201          * it set get it set; or that volumes that have been
2202          * offline without DONT SALVAGE having been set also
2203          * eventually get it set */
2204         V_dontSalvage(vp) = DONT_SALVAGE;
2205 #endif /* !AFS_DEMAND_ATTACH_FS */
2206         VAddToVolumeUpdateList_r(ec, vp);
2207         if (*ec) {
2208             Log("VAttachVolume: Error adding volume %u to update list\n", vp->hashid);
2209             if (vp)
2210                 VPutVolume_r(vp);
2211             goto done;
2212         }
2213     }
2214     if (LogLevel)
2215         Log("VOnline:  volume %u (%s) attached and online\n", V_id(vp),
2216             V_name(vp));
2217   done:
2218     if (reserve) {
2219         VCancelReservation_r(nvp);
2220         reserve = 0;
2221     }
2222     if (*ec && (*ec != VOFFLINE) && (*ec != VSALVAGE)) {
2223         if (vp && !VIsErrorState(V_attachState(vp))) {
2224             VChangeState_r(vp, VOL_STATE_ERROR);
2225         }
2226         return NULL;
2227     } else {
2228         return vp;
2229     }
2230 }
2231 #endif /* AFS_DEMAND_ATTACH_FS */
2232
2233 /*
2234  * called without any locks held
2235  * returns with vol_glock_mutex held
2236  */
2237 private Volume * 
2238 attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * header,
2239         struct DiskPartition64 * partp, register Volume * vp, int isbusy, int mode)
2240 {
2241     vp->specialStatus = (byte) (isbusy ? VBUSY : 0);
2242     IH_INIT(vp->vnodeIndex[vLarge].handle, partp->device, header->parent,
2243             header->largeVnodeIndex);
2244     IH_INIT(vp->vnodeIndex[vSmall].handle, partp->device, header->parent,
2245             header->smallVnodeIndex);
2246     IH_INIT(vp->diskDataHandle, partp->device, header->parent,
2247             header->volumeInfo);
2248     IH_INIT(vp->linkHandle, partp->device, header->parent, header->linkTable);
2249     vp->shuttingDown = 0;
2250     vp->goingOffline = 0;
2251     vp->nUsers = 1;
2252 #ifdef AFS_DEMAND_ATTACH_FS
2253     vp->stats.last_attach = FT_ApproxTime();
2254     vp->stats.attaches++;
2255 #endif
2256
2257     VOL_LOCK;
2258     IncUInt64(&VStats.attaches);
2259     vp->cacheCheck = ++VolumeCacheCheck;
2260     /* just in case this ever rolls over */
2261     if (!vp->cacheCheck)
2262         vp->cacheCheck = ++VolumeCacheCheck;
2263     GetVolumeHeader(vp);
2264     VOL_UNLOCK;
2265
2266 #if defined(AFS_DEMAND_ATTACH_FS) && defined(FSSYNC_BUILD_CLIENT)
2267     /* demand attach changes the V_PEEK mechanism
2268      *
2269      * we can now suck the current disk data structure over
2270      * the fssync interface without going to disk
2271      *
2272      * (technically, we don't need to restrict this feature
2273      *  to demand attach fileservers.  However, I'm trying
2274      *  to limit the number of common code changes)
2275      */
2276     if (programType != fileServer && mode == V_PEEK) {
2277         SYNC_response res;
2278         res.payload.len = sizeof(VolumeDiskData);
2279         res.payload.buf = &vp->header->diskstuff;
2280
2281         if (FSYNC_VolOp(volumeId,
2282                         VPartitionPath(partp),
2283                         FSYNC_VOL_QUERY_HDR,
2284                         FSYNC_WHATEVER,
2285                         &res) == SYNC_OK) {
2286             goto disk_header_loaded;
2287         }
2288     }
2289 #endif /* AFS_DEMAND_ATTACH_FS && FSSYNC_BUILD_CLIENT */
2290     (void)ReadHeader(ec, V_diskDataHandle(vp), (char *)&V_disk(vp),
2291                      sizeof(V_disk(vp)), VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
2292
2293 #ifdef AFS_DEMAND_ATTACH_FS
2294     /* update stats */
2295     VOL_LOCK;
2296     IncUInt64(&VStats.hdr_loads);
2297     IncUInt64(&vp->stats.hdr_loads);
2298     VOL_UNLOCK;
2299 #endif /* AFS_DEMAND_ATTACH_FS */
2300     
2301     if (*ec) {
2302         Log("VAttachVolume: Error reading diskDataHandle vol header %s; error=%u\n", path, *ec);
2303     }
2304
2305  disk_header_loaded:
2306
2307 #ifdef AFS_DEMAND_ATTACH_FS
2308     if (!*ec) {
2309
2310         /* check for pending volume operations */
2311         if (vp->pending_vol_op) {
2312             /* see if the pending volume op requires exclusive access */
2313             if (!VVolOpLeaveOnline_r(vp, vp->pending_vol_op)) {
2314                 /* mark the volume down */
2315                 *ec = VOFFLINE;
2316                 VChangeState_r(vp, VOL_STATE_UNATTACHED);
2317                 if (V_offlineMessage(vp)[0] == '\0')
2318                     strlcpy(V_offlineMessage(vp),
2319                             "A volume utility is running.", 
2320                             sizeof(V_offlineMessage(vp)));
2321                 V_offlineMessage(vp)[sizeof(V_offlineMessage(vp)) - 1] = '\0';
2322
2323                 /* check to see if we should set the specialStatus flag */
2324                 if (VVolOpSetVBusy_r(vp, vp->pending_vol_op)) {
2325                     vp->specialStatus = VBUSY;
2326                 }
2327             }
2328         }
2329
2330         V_attachFlags(vp) |= VOL_HDR_LOADED;
2331         vp->stats.last_hdr_load = vp->stats.last_attach;
2332     }
2333 #endif /* AFS_DEMAND_ATTACH_FS */
2334
2335     if (!*ec) {
2336         struct IndexFileHeader iHead;
2337
2338 #if OPENAFS_VOL_STATS
2339         /*
2340          * We just read in the diskstuff part of the header.  If the detailed
2341          * volume stats area has not yet been initialized, we should bzero the
2342          * area and mark it as initialized.
2343          */
2344         if (!(V_stat_initialized(vp))) {
2345             memset((char *)(V_stat_area(vp)), 0, VOL_STATS_BYTES);
2346             V_stat_initialized(vp) = 1;
2347         }
2348 #endif /* OPENAFS_VOL_STATS */
2349
2350         (void)ReadHeader(ec, vp->vnodeIndex[vSmall].handle,
2351                          (char *)&iHead, sizeof(iHead),
2352                          SMALLINDEXMAGIC, SMALLINDEXVERSION);
2353
2354         if (*ec) {
2355             Log("VAttachVolume: Error reading smallVnode vol header %s; error=%u\n", path, *ec);
2356         }
2357     }
2358
2359     if (!*ec) {
2360         struct IndexFileHeader iHead;
2361
2362         (void)ReadHeader(ec, vp->vnodeIndex[vLarge].handle,
2363                          (char *)&iHead, sizeof(iHead),
2364                          LARGEINDEXMAGIC, LARGEINDEXVERSION);
2365
2366         if (*ec) {
2367             Log("VAttachVolume: Error reading largeVnode vol header %s; error=%u\n", path, *ec);
2368         }
2369     }
2370
2371 #ifdef AFS_NAMEI_ENV
2372     if (!*ec) {
2373         struct versionStamp stamp;
2374
2375         (void)ReadHeader(ec, V_linkHandle(vp), (char *)&stamp,
2376                          sizeof(stamp), LINKTABLEMAGIC, LINKTABLEVERSION);
2377
2378         if (*ec) {
2379             Log("VAttachVolume: Error reading namei vol header %s; error=%u\n", path, *ec);
2380         }
2381     }
2382 #endif /* AFS_NAMEI_ENV */
2383
2384 #if defined(AFS_DEMAND_ATTACH_FS)
2385     if (*ec && ((*ec != VOFFLINE) || (V_attachState(vp) != VOL_STATE_UNATTACHED))) {
2386         VOL_LOCK;
2387         if (programType == fileServer) {
2388             VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2389             vp->nUsers = 0;
2390         } else {
2391             Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%u\n", path, *ec);
2392             FreeVolume(vp);
2393             *ec = VSALVAGE;
2394         }
2395         return NULL;
2396     } else if (*ec) {
2397         /* volume operation in progress */
2398         VOL_LOCK;
2399         return NULL;
2400     }
2401 #else /* AFS_DEMAND_ATTACH_FS */
2402     if (*ec) {
2403         Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%u\n", path, *ec);
2404         VOL_LOCK;
2405         FreeVolume(vp);
2406         return NULL;
2407     }
2408 #endif /* AFS_DEMAND_ATTACH_FS */
2409
2410     if (V_needsSalvaged(vp)) {
2411         if (vp->specialStatus)
2412             vp->specialStatus = 0;
2413         VOL_LOCK;
2414 #if defined(AFS_DEMAND_ATTACH_FS)
2415         if (programType == fileServer) {
2416             VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
2417             vp->nUsers = 0;
2418         } else {
2419             Log("VAttachVolume: volume salvage flag is ON for %s; volume needs salvage\n", path);
2420             FreeVolume(vp);
2421             *ec = VSALVAGE;
2422         }
2423 #else /* AFS_DEMAND_ATTACH_FS */
2424         FreeVolume(vp);
2425         *ec = VSALVAGE;
2426 #endif /* AFS_DEMAND_ATTACH_FS */
2427         return NULL;
2428     }
2429
2430     VOL_LOCK;
2431     if (programType == fileServer) {
2432 #ifndef FAST_RESTART
2433         if (V_inUse(vp) && VolumeWriteable(vp)) {
2434             if (!V_needsSalvaged(vp)) {
2435                 V_needsSalvaged(vp) = 1;
2436                 VUpdateVolume_r(ec, vp, 0);
2437             }
2438 #if defined(AFS_DEMAND_ATTACH_FS)
2439             VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
2440             vp->nUsers = 0;
2441 #else /* AFS_DEMAND_ATTACH_FS */
2442             Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
2443             FreeVolume(vp);
2444             *ec = VSALVAGE;
2445 #endif /* AFS_DEMAND_ATTACH_FS */
2446             return NULL;
2447         }
2448 #endif /* FAST_RESTART */
2449
2450         if (V_destroyMe(vp) == DESTROY_ME) {
2451 #if defined(AFS_DEMAND_ATTACH_FS)
2452             /* schedule a salvage so the volume goes away on disk */
2453             VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2454             VChangeState_r(vp, VOL_STATE_ERROR);
2455             vp->nUsers = 0;
2456 #endif /* AFS_DEMAND_ATTACH_FS */
2457             FreeVolume(vp);
2458             Log("VAttachVolume: volume %s is junk; it should be destroyed at next salvage\n", path);
2459             *ec = VNOVOL;
2460             return NULL;
2461         }
2462     }
2463
2464     vp->nextVnodeUnique = V_uniquifier(vp);
2465     vp->vnodeIndex[vSmall].bitmap = vp->vnodeIndex[vLarge].bitmap = NULL;
2466 #ifndef BITMAP_LATER
2467     if (programType == fileServer && VolumeWriteable(vp)) {
2468         int i;
2469         for (i = 0; i < nVNODECLASSES; i++) {
2470             VGetBitmap_r(ec, vp, i);
2471             if (*ec) {
2472 #ifdef AFS_DEMAND_ATTACH_FS
2473                 VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2474                 vp->nUsers = 0;
2475 #else /* AFS_DEMAND_ATTACH_FS */
2476                 FreeVolume(vp);
2477 #endif /* AFS_DEMAND_ATTACH_FS */
2478                 Log("VAttachVolume: error getting bitmap for volume (%s)\n",
2479                     path);
2480                 return NULL;
2481             }
2482         }
2483     }
2484 #endif /* BITMAP_LATER */
2485
2486     if (programType == fileServer) {
2487         if (vp->specialStatus)
2488             vp->specialStatus = 0;
2489         if (V_blessed(vp) && V_inService(vp) && !V_needsSalvaged(vp)) {
2490             V_inUse(vp) = 1;
2491             V_offlineMessage(vp)[0] = '\0';
2492         }
2493     }
2494
2495     AddVolumeToHashTable(vp, V_id(vp));
2496 #ifdef AFS_DEMAND_ATTACH_FS
2497     AddVolumeToVByPList_r(vp);
2498     VLRU_Add_r(vp);
2499     if ((programType != fileServer) ||
2500         V_inUse(vp)) {
2501         VChangeState_r(vp, VOL_STATE_ATTACHED);
2502     } else {
2503         VChangeState_r(vp, VOL_STATE_UNATTACHED);
2504     }
2505 #endif
2506     return vp;
2507 }
2508
2509 /* Attach an existing volume.
2510    The volume also normally goes online at this time.
2511    An offline volume must be reattached to make it go online.
2512  */
2513
2514 Volume *
2515 VAttachVolume(Error * ec, VolumeId volumeId, int mode)
2516 {
2517     Volume *retVal;
2518     VOL_LOCK;
2519     retVal = VAttachVolume_r(ec, volumeId, mode);
2520     VOL_UNLOCK;
2521     return retVal;
2522 }
2523
2524 Volume *
2525 VAttachVolume_r(Error * ec, VolumeId volumeId, int mode)
2526 {
2527     char *part, *name;
2528     GetVolumePath(ec, volumeId, &part, &name);
2529     if (*ec) {
2530         register Volume *vp;
2531         Error error;
2532         vp = VGetVolume_r(&error, volumeId);
2533         if (vp) {
2534             assert(V_inUse(vp) == 0);
2535             VDetachVolume_r(ec, vp);
2536         }
2537         return NULL;
2538     }
2539     return VAttachVolumeByName_r(ec, part, name, mode);
2540 }
2541
2542 /* Increment a reference count to a volume, sans context swaps.  Requires
2543  * possibly reading the volume header in from the disk, since there's
2544  * an invariant in the volume package that nUsers>0 ==> vp->header is valid.
2545  *
2546  * N.B. This call can fail if we can't read in the header!!  In this case
2547  * we still guarantee we won't context swap, but the ref count won't be
2548  * incremented (otherwise we'd violate the invariant).
2549  */
2550 /* NOTE: with the demand attach fileserver extensions, the global lock
2551  * is dropped within VHold */
2552 #ifdef AFS_DEMAND_ATTACH_FS
2553 static int
2554 VHold_r(register Volume * vp)
2555 {
2556     Error error;
2557
2558     VCreateReservation_r(vp);
2559     VWaitExclusiveState_r(vp);
2560
2561     LoadVolumeHeader(&error, vp);
2562     if (error) {
2563         VCancelReservation_r(vp);
2564         return error;
2565     }
2566     vp->nUsers++;
2567     VCancelReservation_r(vp);
2568     return 0;
2569 }
2570 #else /* AFS_DEMAND_ATTACH_FS */
2571 static int
2572 VHold_r(register Volume * vp)
2573 {
2574     Error error;
2575
2576     LoadVolumeHeader(&error, vp);
2577     if (error)
2578         return error;
2579     vp->nUsers++;
2580     return 0;
2581 }
2582 #endif /* AFS_DEMAND_ATTACH_FS */
2583
2584 static int
2585 VHold(register Volume * vp)
2586 {
2587     int retVal;
2588     VOL_LOCK;
2589     retVal = VHold_r(vp);
2590     VOL_UNLOCK;
2591     return retVal;
2592 }
2593
2594
2595 /***************************************************/
2596 /* get and put volume routines                     */
2597 /***************************************************/
2598
2599 /**
2600  * put back a heavyweight reference to a volume object.
2601  *
2602  * @param[in] vp  volume object pointer
2603  *
2604  * @pre VOL_LOCK held
2605  *
2606  * @post heavyweight volume reference put back.
2607  *       depending on state, volume may have been taken offline,
2608  *       detached, salvaged, freed, etc.
2609  *
2610  * @internal volume package internal use only
2611  */
2612 void
2613 VPutVolume_r(register Volume * vp)
2614 {
2615     assert(--vp->nUsers >= 0);
2616     if (vp->nUsers == 0) {
2617         VCheckOffline(vp);
2618         ReleaseVolumeHeader(vp->header);
2619 #ifdef AFS_DEMAND_ATTACH_FS
2620         if (!VCheckDetach(vp)) {
2621             VCheckSalvage(vp);
2622             VCheckFree(vp);
2623         }
2624 #else /* AFS_DEMAND_ATTACH_FS */
2625         VCheckDetach(vp);
2626 #endif /* AFS_DEMAND_ATTACH_FS */
2627     }
2628 }
2629
2630 void
2631 VPutVolume(register Volume * vp)
2632 {
2633     VOL_LOCK;
2634     VPutVolume_r(vp);
2635     VOL_UNLOCK;
2636 }
2637
2638
2639 /* Get a pointer to an attached volume.  The pointer is returned regardless
2640    of whether or not the volume is in service or on/off line.  An error
2641    code, however, is returned with an indication of the volume's status */
2642 Volume *
2643 VGetVolume(Error * ec, Error * client_ec, VolId volumeId)
2644 {
2645     Volume *retVal;
2646     VOL_LOCK;
2647     retVal = GetVolume(ec, client_ec, volumeId, NULL, 0);
2648     VOL_UNLOCK;
2649     return retVal;
2650 }
2651
2652 Volume *
2653 VGetVolume_r(Error * ec, VolId volumeId)
2654 {
2655     return GetVolume(ec, NULL, volumeId, NULL, 0);
2656 }
2657
2658 /* try to get a volume we've previously looked up */
2659 /* for demand attach fs, caller MUST NOT hold a ref count on vp */
2660 Volume * 
2661 VGetVolumeByVp_r(Error * ec, Volume * vp)
2662 {
2663     return GetVolume(ec, NULL, vp->hashid, vp, 0);
2664 }
2665
2666 /* private interface for getting a volume handle
2667  * volumeId must be provided.
2668  * hint is an optional parameter to speed up hash lookups
2669  * flags is not used at this time
2670  */
2671 /* for demand attach fs, caller MUST NOT hold a ref count on hint */
2672 static Volume *
2673 GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flags)
2674 {
2675     Volume *vp = hint;
2676     /* pull this profiling/debugging code out of regular builds */
2677 #ifdef notdef
2678 #define VGET_CTR_INC(x) x++
2679     unsigned short V0 = 0, V1 = 0, V2 = 0, V3 = 0, V5 = 0, V6 =
2680         0, V7 = 0, V8 = 0, V9 = 0;
2681     unsigned short V10 = 0, V11 = 0, V12 = 0, V13 = 0, V14 = 0, V15 = 0;
2682 #else
2683 #define VGET_CTR_INC(x)
2684 #endif
2685 #ifdef AFS_DEMAND_ATTACH_FS
2686     Volume *avp, * rvp = hint;
2687 #endif
2688
2689 #ifdef AFS_DEMAND_ATTACH_FS
2690     if (rvp) {
2691         VCreateReservation_r(rvp);
2692     }
2693 #endif /* AFS_DEMAND_ATTACH_FS */
2694
2695     for (;;) {
2696         *ec = 0;
2697         if (client_ec)
2698             *client_ec = 0;
2699         VGET_CTR_INC(V0);
2700
2701         vp = VLookupVolume_r(ec, volumeId, vp);
2702         if (*ec) {
2703             vp = NULL;
2704             break;
2705         }
2706
2707 #ifdef AFS_DEMAND_ATTACH_FS
2708         if (rvp && (rvp != vp)) {
2709             /* break reservation on old vp */
2710             VCancelReservation_r(rvp);
2711             rvp = NULL;
2712         }
2713 #endif /* AFS_DEMAND_ATTACH_FS */
2714
2715         if (!vp) {
2716             VGET_CTR_INC(V1);
2717             if (VInit < 2) {
2718                 VGET_CTR_INC(V2);
2719                 /* Until we have reached an initialization level of 2
2720                  * we don't know whether this volume exists or not.
2721                  * We can't sleep and retry later because before a volume
2722                  * is attached, the caller tries to get it first.  Just
2723                  * return VOFFLINE and the caller can choose whether to
2724                  * retry the command or not. */
2725                 *ec = VOFFLINE;
2726                 break;
2727             }
2728
2729             *ec = VNOVOL;
2730             break;
2731         }
2732
2733         VGET_CTR_INC(V3);
2734         IncUInt64(&VStats.hdr_gets);
2735         
2736 #ifdef AFS_DEMAND_ATTACH_FS
2737         /* block if someone else is performing an exclusive op on this volume */
2738         if (rvp != vp) {
2739             rvp = vp;
2740             VCreateReservation_r(rvp);
2741         }
2742         VWaitExclusiveState_r(vp);
2743
2744         /* short circuit with VNOVOL in the following circumstances:
2745          *
2746          *   VOL_STATE_ERROR
2747          *   VOL_STATE_SHUTTING_DOWN
2748          */
2749         if ((V_attachState(vp) == VOL_STATE_ERROR) ||
2750             (V_attachState(vp) == VOL_STATE_SHUTTING_DOWN)) {
2751             *ec = VNOVOL;
2752             vp = NULL;
2753             break;
2754         }
2755
2756         /*
2757          * short circuit with VOFFLINE in the following circumstances:
2758          *
2759          *   VOL_STATE_UNATTACHED
2760          */
2761        if (V_attachState(vp) == VOL_STATE_UNATTACHED) {
2762            *ec = VOFFLINE;
2763            vp = NULL;
2764            break;
2765        }
2766
2767         /* allowable states:
2768          *   UNATTACHED
2769          *   PREATTACHED
2770          *   ATTACHED
2771          *   GOING_OFFLINE
2772          *   SALVAGING
2773          */
2774
2775         if (vp->salvage.requested) {
2776             VUpdateSalvagePriority_r(vp);
2777         }
2778
2779         if (V_attachState(vp) == VOL_STATE_PREATTACHED) {
2780             avp = VAttachVolumeByVp_r(ec, vp, 0);
2781             if (avp) {
2782                 if (vp != avp) {
2783                     /* VAttachVolumeByVp_r can return a pointer
2784                      * != the vp passed to it under certain
2785                      * conditions; make sure we don't leak
2786                      * reservations if that happens */
2787                     vp = avp;
2788                     VCancelReservation_r(rvp);
2789                     rvp = avp;
2790                     VCreateReservation_r(rvp);
2791                 }
2792                 VPutVolume_r(avp);
2793             }
2794             if (*ec) {
2795                 int endloop = 0;
2796                 switch (*ec) {
2797                 case VSALVAGING:
2798                     break;
2799                 case VOFFLINE:
2800                     if (!vp->pending_vol_op) {
2801                         endloop = 1;
2802                     }
2803                     break;
2804                 default:
2805                     *ec = VNOVOL;
2806                     endloop = 1;
2807                 }
2808                 if (endloop) {
2809                     vp = NULL;
2810                     break;
2811                 }
2812             }
2813         }
2814
2815         if ((V_attachState(vp) == VOL_STATE_SALVAGING) ||
2816             (*ec == VSALVAGING)) {
2817             if (client_ec) {
2818                 /* see CheckVnode() in afsfileprocs.c for an explanation
2819                  * of this error code logic */
2820                 afs_uint32 now = FT_ApproxTime();
2821                 if ((vp->stats.last_salvage + (10 * 60)) >= now) {
2822                     *client_ec = VBUSY;
2823                 } else {
2824                     *client_ec = VRESTARTING;
2825                 }
2826             }
2827             *ec = VSALVAGING;
2828             vp = NULL;
2829             break;
2830         }
2831 #endif
2832
2833         LoadVolumeHeader(ec, vp);
2834         if (*ec) {
2835             VGET_CTR_INC(V6);
2836             /* Only log the error if it was a totally unexpected error.  Simply
2837              * a missing inode is likely to be caused by the volume being deleted */
2838             if (errno != ENXIO || LogLevel)
2839                 Log("Volume %u: couldn't reread volume header\n",
2840                     vp->hashid);
2841 #ifdef AFS_DEMAND_ATTACH_FS
2842             if (programType == fileServer) {
2843                 VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2844             } else {
2845                 FreeVolume(vp);
2846                 vp = NULL;
2847             }
2848 #else /* AFS_DEMAND_ATTACH_FS */
2849             FreeVolume(vp);
2850             vp = NULL;
2851 #endif /* AFS_DEMAND_ATTACH_FS */
2852             break;
2853         }
2854
2855 #ifdef AFS_DEMAND_ATTACH_FS
2856         /*
2857          * this test MUST happen after the volume header is loaded
2858          */
2859         if (vp->pending_vol_op && !VVolOpLeaveOnline_r(vp, vp->pending_vol_op)) {
2860             if (client_ec) {
2861                 /* see CheckVnode() in afsfileprocs.c for an explanation
2862                  * of this error code logic */
2863                 afs_uint32 now = FT_ApproxTime();
2864                 if ((vp->stats.last_vol_op + (10 * 60)) >= now) {
2865                     *client_ec = VBUSY;
2866                 } else {
2867                     *client_ec = VRESTARTING;
2868                 }
2869             }
2870             *ec = VOFFLINE;
2871             ReleaseVolumeHeader(vp->header);
2872             vp = NULL;
2873             break;
2874         }
2875 #endif /* AFS_DEMAND_ATTACH_FS */
2876         
2877         VGET_CTR_INC(V7);
2878         if (vp->shuttingDown) {
2879             VGET_CTR_INC(V8);
2880             *ec = VNOVOL;
2881             vp = NULL;
2882             break;
2883         }
2884
2885         if (programType == fileServer) {
2886             VGET_CTR_INC(V9);
2887             if (vp->goingOffline) {
2888                 VGET_CTR_INC(V10);
2889 #ifdef AFS_DEMAND_ATTACH_FS
2890                 /* wait for the volume to go offline */
2891                 if (V_attachState(vp) == VOL_STATE_GOING_OFFLINE) {
2892                     VWaitStateChange_r(vp);
2893                 }
2894 #elif defined(AFS_PTHREAD_ENV)
2895                 VOL_CV_WAIT(&vol_put_volume_cond);
2896 #else /* AFS_PTHREAD_ENV */
2897                 LWP_WaitProcess(VPutVolume);
2898 #endif /* AFS_PTHREAD_ENV */
2899                 continue;
2900             }
2901             if (vp->specialStatus) {
2902                 VGET_CTR_INC(V11);
2903                 *ec = vp->specialStatus;
2904             } else if (V_inService(vp) == 0 || V_blessed(vp) == 0) {
2905                 VGET_CTR_INC(V12);
2906                 *ec = VNOVOL;
2907             } else if (V_inUse(vp) == 0) {
2908                 VGET_CTR_INC(V13);
2909                 *ec = VOFFLINE;
2910             } else {
2911                 VGET_CTR_INC(V14);
2912             }
2913         }
2914         break;
2915     }
2916     VGET_CTR_INC(V15);
2917
2918 #ifdef AFS_DEMAND_ATTACH_FS
2919     /* if no error, bump nUsers */
2920     if (vp) {
2921         vp->nUsers++;
2922         VLRU_UpdateAccess_r(vp);
2923     }
2924     if (rvp) {
2925         VCancelReservation_r(rvp);
2926         rvp = NULL;
2927     }
2928     if (client_ec && !*client_ec) {
2929         *client_ec = *ec;
2930     }
2931 #else /* AFS_DEMAND_ATTACH_FS */
2932     /* if no error, bump nUsers */
2933     if (vp) {
2934         vp->nUsers++;
2935     }
2936     if (client_ec) {
2937         *client_ec = *ec;
2938     }
2939 #endif /* AFS_DEMAND_ATTACH_FS */
2940
2941     assert(vp || *ec);
2942     return vp;
2943 }
2944
2945
2946 /***************************************************/
2947 /* Volume offline/detach routines                  */
2948 /***************************************************/
2949
2950 /* caller MUST hold a heavyweight ref on vp */
2951 #ifdef AFS_DEMAND_ATTACH_FS
2952 void
2953 VTakeOffline_r(register Volume * vp)
2954 {
2955     Error error;
2956
2957     assert(vp->nUsers > 0);
2958     assert(programType == fileServer);
2959
2960     VCreateReservation_r(vp);
2961     VWaitExclusiveState_r(vp);
2962
2963     vp->goingOffline = 1;
2964     V_needsSalvaged(vp) = 1;
2965
2966     VRequestSalvage_r(&error, vp, SALVSYNC_ERROR, 0);
2967     VCancelReservation_r(vp);
2968 }
2969 #else /* AFS_DEMAND_ATTACH_FS */
2970 void
2971 VTakeOffline_r(register Volume * vp)
2972 {
2973     assert(vp->nUsers > 0);
2974     assert(programType == fileServer);
2975
2976     vp->goingOffline = 1;
2977     V_needsSalvaged(vp) = 1;
2978 }
2979 #endif /* AFS_DEMAND_ATTACH_FS */
2980
2981 void
2982 VTakeOffline(register Volume * vp)
2983 {
2984     VOL_LOCK;
2985     VTakeOffline_r(vp);
2986     VOL_UNLOCK;
2987 }
2988
2989 /**
2990  * force a volume offline.
2991  *
2992  * @param[in] vp     volume object pointer
2993  * @param[in] flags  flags (see note below)
2994  *
2995  * @note the flag VOL_FORCEOFF_NOUPDATE is a recursion control flag
2996  *       used when VUpdateVolume_r needs to call VForceOffline_r
2997  *       (which in turn would normally call VUpdateVolume_r)
2998  *
2999  * @see VUpdateVolume_r
3000  *
3001  * @pre VOL_LOCK must be held.
3002  *      for DAFS, caller must hold ref.
3003  *
3004  * @note for DAFS, it _is safe_ to call this function from an
3005  *       exclusive state
3006  *
3007  * @post needsSalvaged flag is set.
3008  *       for DAFS, salvage is requested.
3009  *       no further references to the volume through the volume 
3010  *       package will be honored.
3011  *       all file descriptor and vnode caches are invalidated.
3012  *
3013  * @warning this is a heavy-handed interface.  it results in
3014  *          a volume going offline regardless of the current 
3015  *          reference count state.
3016  *
3017  * @internal  volume package internal use only
3018  */
3019 void
3020 VForceOffline_r(Volume * vp, int flags)
3021 {
3022     Error error;
3023     if (!V_inUse(vp)) {
3024 #ifdef AFS_DEMAND_ATTACH_FS
3025         VChangeState_r(vp, VOL_STATE_ERROR);
3026 #endif
3027         return;
3028     }
3029
3030     strcpy(V_offlineMessage(vp),
3031            "Forced offline due to internal error: volume needs to be salvaged");
3032     Log("Volume %u forced offline:  it needs salvaging!\n", V_id(vp));
3033
3034     V_inUse(vp) = 0;
3035     vp->goingOffline = 0;
3036     V_needsSalvaged(vp) = 1;
3037     if (!(flags & VOL_FORCEOFF_NOUPDATE)) {
3038         VUpdateVolume_r(&error, vp, VOL_UPDATE_NOFORCEOFF);
3039     }
3040
3041 #ifdef AFS_DEMAND_ATTACH_FS
3042     VRequestSalvage_r(&error, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
3043 #endif /* AFS_DEMAND_ATTACH_FS */
3044
3045 #ifdef AFS_PTHREAD_ENV
3046     assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3047 #else /* AFS_PTHREAD_ENV */
3048     LWP_NoYieldSignal(VPutVolume);
3049 #endif /* AFS_PTHREAD_ENV */
3050
3051     VReleaseVolumeHandles_r(vp);
3052 }
3053
3054 /**
3055  * force a volume offline.
3056  *
3057  * @param[in] vp  volume object pointer
3058  *
3059  * @see VForceOffline_r
3060  */
3061 void
3062 VForceOffline(Volume * vp)
3063 {
3064     VOL_LOCK;
3065     VForceOffline_r(vp, 0);
3066     VOL_UNLOCK;
3067 }
3068
3069 /* The opposite of VAttachVolume.  The volume header is written to disk, with
3070    the inUse bit turned off.  A copy of the header is maintained in memory,
3071    however (which is why this is VOffline, not VDetach).
3072  */
3073 void
3074 VOffline_r(Volume * vp, char *message)
3075 {
3076     Error error;
3077     VolumeId vid = V_id(vp);
3078
3079     assert(programType != volumeUtility);
3080     if (!V_inUse(vp)) {
3081         VPutVolume_r(vp);
3082         return;
3083     }
3084     if (V_offlineMessage(vp)[0] == '\0')
3085         strncpy(V_offlineMessage(vp), message, sizeof(V_offlineMessage(vp)));
3086     V_offlineMessage(vp)[sizeof(V_offlineMessage(vp)) - 1] = '\0';
3087
3088     vp->goingOffline = 1;
3089 #ifdef AFS_DEMAND_ATTACH_FS
3090     VChangeState_r(vp, VOL_STATE_GOING_OFFLINE);
3091     VCreateReservation_r(vp);
3092     VPutVolume_r(vp);
3093
3094     /* wait for the volume to go offline */
3095     if (V_attachState(vp) == VOL_STATE_GOING_OFFLINE) {
3096         VWaitStateChange_r(vp);
3097     }
3098     VCancelReservation_r(vp);
3099 #else /* AFS_DEMAND_ATTACH_FS */
3100     VPutVolume_r(vp);
3101     vp = VGetVolume_r(&error, vid);     /* Wait for it to go offline */
3102     if (vp)                     /* In case it was reattached... */
3103         VPutVolume_r(vp);
3104 #endif /* AFS_DEMAND_ATTACH_FS */
3105 }
3106
3107 void
3108 VOffline(Volume * vp, char *message)
3109 {
3110     VOL_LOCK;
3111     VOffline_r(vp, message);
3112     VOL_UNLOCK;
3113 }
3114
3115 /* This gets used for the most part by utility routines that don't want
3116  * to keep all the volume headers around.  Generally, the file server won't
3117  * call this routine, because then the offline message in the volume header
3118  * (or other information) won't be available to clients. For NAMEI, also
3119  * close the file handles.  However, the fileserver does call this during
3120  * an attach following a volume operation.
3121  */
3122 void
3123 VDetachVolume_r(Error * ec, Volume * vp)
3124 {
3125     VolumeId volume;
3126     struct DiskPartition64 *tpartp;
3127     int notifyServer, useDone = FSYNC_VOL_ON;
3128
3129     *ec = 0;                    /* always "succeeds" */
3130     if (programType == volumeUtility) {
3131         notifyServer = vp->needsPutBack;
3132         if (V_destroyMe(vp) == DESTROY_ME)
3133             useDone = FSYNC_VOL_DONE;
3134 #ifdef AFS_DEMAND_ATTACH_FS
3135         else if (!V_blessed(vp) || !V_inService(vp))
3136             useDone = FSYNC_VOL_LEAVE_OFF;
3137 #endif
3138     }
3139     tpartp = vp->partition;
3140     volume = V_id(vp);
3141     DeleteVolumeFromHashTable(vp);
3142     vp->shuttingDown = 1;
3143 #ifdef AFS_DEMAND_ATTACH_FS
3144     DeleteVolumeFromVByPList_r(vp);
3145     VLRU_Delete_r(vp);
3146     VChangeState_r(vp, VOL_STATE_SHUTTING_DOWN);
3147 #endif /* AFS_DEMAND_ATTACH_FS */
3148     VPutVolume_r(vp);
3149     /* Will be detached sometime in the future--this is OK since volume is offline */
3150
3151     /* XXX the following code should really be moved to VCheckDetach() since the volume
3152      * is not technically detached until the refcounts reach zero
3153      */
3154 #ifdef FSSYNC_BUILD_CLIENT
3155     if (programType == volumeUtility && notifyServer) {
3156         /* 
3157          * Note:  The server is not notified in the case of a bogus volume 
3158          * explicitly to make it possible to create a volume, do a partial 
3159          * restore, then abort the operation without ever putting the volume 
3160          * online.  This is essential in the case of a volume move operation 
3161          * between two partitions on the same server.  In that case, there 
3162          * would be two instances of the same volume, one of them bogus, 
3163          * which the file server would attempt to put on line 
3164          */
3165         FSYNC_VolOp(volume, tpartp->name, useDone, 0, NULL);
3166         /* XXX this code path is only hit by volume utilities, thus
3167          * V_BreakVolumeCallbacks will always be NULL.  if we really
3168          * want to break callbacks in this path we need to use FSYNC_VolOp() */
3169 #ifdef notdef
3170         /* Dettaching it so break all callbacks on it */
3171         if (V_BreakVolumeCallbacks) {
3172             Log("volume %u detached; breaking all call backs\n", volume);
3173             (*V_BreakVolumeCallbacks) (volume);
3174         }
3175 #endif
3176     }
3177 #endif /* FSSYNC_BUILD_CLIENT */
3178 }
3179
3180 void
3181 VDetachVolume(Error * ec, Volume * vp)
3182 {
3183     VOL_LOCK;
3184     VDetachVolume_r(ec, vp);
3185     VOL_UNLOCK;
3186 }
3187
3188
3189 /***************************************************/
3190 /* Volume fd/inode handle closing routines         */
3191 /***************************************************/
3192
3193 /* For VDetachVolume, we close all cached file descriptors, but keep
3194  * the Inode handles in case we need to read from a busy volume.
3195  */
3196 /* for demand attach, caller MUST hold ref count on vp */
3197 static void
3198 VCloseVolumeHandles_r(Volume * vp)
3199 {
3200 #ifdef AFS_DEMAND_ATTACH_FS
3201     VolState state_save;
3202
3203     state_save = VChangeState_r(vp, VOL_STATE_OFFLINING);
3204 #endif
3205
3206     /* demand attach fs
3207      *
3208      * XXX need to investigate whether we can perform
3209      * DFlushVolume outside of vol_glock_mutex... 
3210      *
3211      * VCloseVnodeFiles_r drops the glock internally */
3212     DFlushVolume(V_id(vp));
3213     VCloseVnodeFiles_r(vp);
3214
3215 #ifdef AFS_DEMAND_ATTACH_FS
3216     VOL_UNLOCK;
3217 #endif
3218
3219     /* Too time consuming and unnecessary for the volserver */
3220     if (programType != volumeUtility) {
3221         IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
3222         IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
3223         IH_CONDSYNC(vp->diskDataHandle);
3224 #ifdef AFS_NT40_ENV
3225         IH_CONDSYNC(vp->linkHandle);
3226 #endif /* AFS_NT40_ENV */
3227     }
3228
3229     IH_REALLYCLOSE(vp->vnodeIndex[vLarge].handle);
3230     IH_REALLYCLOSE(vp->vnodeIndex[vSmall].handle);
3231     IH_REALLYCLOSE(vp->diskDataHandle);
3232     IH_REALLYCLOSE(vp->linkHandle);
3233
3234 #ifdef AFS_DEMAND_ATTACH_FS
3235     VOL_LOCK;
3236     VChangeState_r(vp, state_save);
3237 #endif
3238 }
3239
3240 /* For both VForceOffline and VOffline, we close all relevant handles.
3241  * For VOffline, if we re-attach the volume, the files may possible be
3242  * different than before. 
3243  */
3244 /* for demand attach, caller MUST hold a ref count on vp */
3245 static void
3246 VReleaseVolumeHandles_r(Volume * vp)
3247 {
3248 #ifdef AFS_DEMAND_ATTACH_FS
3249     VolState state_save;
3250
3251     state_save = VChangeState_r(vp, VOL_STATE_DETACHING);
3252 #endif
3253
3254     /* XXX need to investigate whether we can perform
3255      * DFlushVolume outside of vol_glock_mutex... */
3256     DFlushVolume(V_id(vp));
3257
3258     VReleaseVnodeFiles_r(vp); /* releases the glock internally */
3259
3260 #ifdef AFS_DEMAND_ATTACH_FS
3261     VOL_UNLOCK;
3262 #endif
3263
3264     /* Too time consuming and unnecessary for the volserver */
3265     if (programType != volumeUtility) {
3266         IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
3267         IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
3268         IH_CONDSYNC(vp->diskDataHandle);
3269 #ifdef AFS_NT40_ENV
3270         IH_CONDSYNC(vp->linkHandle);
3271 #endif /* AFS_NT40_ENV */
3272     }
3273
3274     IH_RELEASE(vp->vnodeIndex[vLarge].handle);
3275     IH_RELEASE(vp->vnodeIndex[vSmall].handle);
3276     IH_RELEASE(vp->diskDataHandle);
3277     IH_RELEASE(vp->linkHandle);
3278
3279 #ifdef AFS_DEMAND_ATTACH_FS
3280     VOL_LOCK;
3281     VChangeState_r(vp, state_save);
3282 #endif
3283 }
3284
3285
3286 /***************************************************/
3287 /* Volume write and fsync routines                 */
3288 /***************************************************/
3289
3290 void
3291 VUpdateVolume_r(Error * ec, Volume * vp, int flags)
3292 {
3293 #ifdef AFS_DEMAND_ATTACH_FS
3294     VolState state_save;
3295
3296     if (flags & VOL_UPDATE_WAIT) {
3297         VCreateReservation_r(vp);
3298         VWaitExclusiveState_r(vp);
3299     }
3300 #endif
3301
3302     *ec = 0;
3303     if (programType == fileServer)
3304         V_uniquifier(vp) =
3305             (V_inUse(vp) ? V_nextVnodeUnique(vp) +
3306              200 : V_nextVnodeUnique(vp));
3307
3308 #ifdef AFS_DEMAND_ATTACH_FS
3309     state_save = VChangeState_r(vp, VOL_STATE_UPDATING);
3310     VOL_UNLOCK;
3311 #endif
3312
3313     WriteVolumeHeader_r(ec, vp);
3314
3315 #ifdef AFS_DEMAND_ATTACH_FS
3316     VOL_LOCK;
3317     VChangeState_r(vp, state_save);
3318     if (flags & VOL_UPDATE_WAIT) {
3319         VCancelReservation_r(vp);
3320     }
3321 #endif
3322
3323     if (*ec) {
3324         Log("VUpdateVolume: error updating volume header, volume %u (%s)\n",
3325             V_id(vp), V_name(vp));
3326         /* try to update on-disk header, 
3327          * while preventing infinite recursion */
3328         if (!(flags & VOL_UPDATE_NOFORCEOFF)) {
3329             VForceOffline_r(vp, VOL_FORCEOFF_NOUPDATE);
3330         }
3331     }
3332 }
3333
3334 void
3335 VUpdateVolume(Error * ec, Volume * vp)
3336 {
3337     VOL_LOCK;
3338     VUpdateVolume_r(ec, vp, VOL_UPDATE_WAIT);
3339     VOL_UNLOCK;
3340 }
3341
3342 void
3343 VSyncVolume_r(Error * ec, Volume * vp, int flags)
3344 {
3345     FdHandle_t *fdP;
3346     int code;
3347 #ifdef AFS_DEMAND_ATTACH_FS
3348     VolState state_save;
3349 #endif
3350
3351     if (flags & VOL_SYNC_WAIT) {
3352         VUpdateVolume_r(ec, vp, VOL_UPDATE_WAIT);
3353     } else {
3354         VUpdateVolume_r(ec, vp, 0);
3355     }
3356     if (!*ec) {
3357 #ifdef AFS_DEMAND_ATTACH_FS
3358         state_save = VChangeState_r(vp, VOL_STATE_UPDATING);
3359         VOL_UNLOCK;
3360 #endif
3361         fdP = IH_OPEN(V_diskDataHandle(vp));
3362         assert(fdP != NULL);
3363         code = FDH_SYNC(fdP);
3364         assert(code == 0);
3365         FDH_CLOSE(fdP);
3366 #ifdef AFS_DEMAND_ATTACH_FS
3367         VOL_LOCK;
3368         VChangeState_r(vp, state_save);
3369 #endif
3370     }
3371 }
3372
3373 void
3374 VSyncVolume(Error * ec, Volume * vp)
3375 {
3376     VOL_LOCK;
3377     VSyncVolume_r(ec, vp, VOL_SYNC_WAIT);
3378     VOL_UNLOCK;
3379 }
3380
3381
3382 /***************************************************/
3383 /* Volume dealloaction routines                    */
3384 /***************************************************/
3385
3386 #ifdef AFS_DEMAND_ATTACH_FS
3387 static void
3388 FreeVolume(Volume * vp)
3389 {
3390     /* free the heap space, iff it's safe.
3391      * otherwise, pull it out of the hash table, so it
3392      * will get deallocated when all refs to it go away */
3393     if (!VCheckFree(vp)) {
3394         DeleteVolumeFromHashTable(vp);
3395         DeleteVolumeFromVByPList_r(vp);
3396
3397         /* make sure we invalidate the header cache entry */
3398         FreeVolumeHeader(vp);
3399     }
3400 }
3401 #endif /* AFS_DEMAND_ATTACH_FS */
3402
3403 static void
3404 ReallyFreeVolume(Volume * vp)
3405 {
3406     int i;
3407     if (!vp)
3408         return;
3409 #ifdef AFS_DEMAND_ATTACH_FS
3410     /* debug */
3411     VChangeState_r(vp, VOL_STATE_FREED);
3412     if (vp->pending_vol_op)
3413         free(vp->pending_vol_op);
3414 #endif /* AFS_DEMAND_ATTACH_FS */
3415     for (i = 0; i < nVNODECLASSES; i++)
3416         if (vp->vnodeIndex[i].bitmap)
3417             free(vp->vnodeIndex[i].bitmap);
3418     FreeVolumeHeader(vp);
3419 #ifndef AFS_DEMAND_ATTACH_FS
3420     DeleteVolumeFromHashTable(vp);
3421 #endif /* AFS_DEMAND_ATTACH_FS */
3422     free(vp);
3423 }
3424
3425 /* check to see if we should shutdown this volume
3426  * returns 1 if volume was freed, 0 otherwise */
3427 #ifdef AFS_DEMAND_ATTACH_FS
3428 static int
3429 VCheckDetach(register Volume * vp)
3430 {
3431     int ret = 0;
3432
3433     if (vp->nUsers || vp->nWaiters)
3434         return ret;
3435
3436     if (vp->shuttingDown) {
3437         ret = 1;
3438         VReleaseVolumeHandles_r(vp);
3439         VCheckSalvage(vp);
3440         ReallyFreeVolume(vp);
3441         if (programType == fileServer) {
3442             assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3443         }
3444     }
3445     return ret;
3446 }
3447 #else /* AFS_DEMAND_ATTACH_FS */
3448 static int
3449 VCheckDetach(register Volume * vp)
3450 {
3451     int ret = 0;
3452
3453     if (vp->nUsers)
3454         return ret;
3455
3456     if (vp->shuttingDown) {
3457         ret = 1;
3458         VReleaseVolumeHandles_r(vp);
3459         ReallyFreeVolume(vp);
3460         if (programType == fileServer) {
3461 #if defined(AFS_PTHREAD_ENV)
3462             assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3463 #else /* AFS_PTHREAD_ENV */
3464             LWP_NoYieldSignal(VPutVolume);
3465 #endif /* AFS_PTHREAD_ENV */
3466         }
3467     }
3468     return ret;
3469 }
3470 #endif /* AFS_DEMAND_ATTACH_FS */
3471
3472 /* check to see if we should offline this volume
3473  * return 1 if volume went offline, 0 otherwise */
3474 #ifdef AFS_DEMAND_ATTACH_FS
3475 static int
3476 VCheckOffline(register Volume * vp)
3477 {
3478     Volume * rvp = NULL;
3479     int ret = 0;
3480
3481     if (vp->goingOffline && !vp->nUsers) {
3482         Error error;
3483         assert(programType == fileServer);
3484         assert((V_attachState(vp) != VOL_STATE_ATTACHED) &&
3485                (V_attachState(vp) != VOL_STATE_FREED) &&
3486                (V_attachState(vp) != VOL_STATE_PREATTACHED) &&
3487                (V_attachState(vp) != VOL_STATE_UNATTACHED));
3488
3489         /* valid states:
3490          *
3491          * VOL_STATE_GOING_OFFLINE
3492          * VOL_STATE_SHUTTING_DOWN
3493          * VIsErrorState(V_attachState(vp))
3494          * VIsExclusiveState(V_attachState(vp))
3495          */
3496
3497         VCreateReservation_r(vp);
3498         VChangeState_r(vp, VOL_STATE_OFFLINING);
3499
3500         ret = 1;
3501         /* must clear the goingOffline flag before we drop the glock */
3502         vp->goingOffline = 0;
3503         V_inUse(vp) = 0;
3504
3505         VLRU_Delete_r(vp);
3506
3507         /* perform async operations */
3508         VUpdateVolume_r(&error, vp, 0);
3509         VCloseVolumeHandles_r(vp);
3510
3511         if (LogLevel) {
3512             Log("VOffline: Volume %u (%s) is now offline", V_id(vp),
3513                 V_name(vp));
3514             if (V_offlineMessage(vp)[0])
3515                 Log(" (%s)", V_offlineMessage(vp));
3516             Log("\n");
3517         }
3518
3519         /* invalidate the volume header cache entry */
3520         FreeVolumeHeader(vp);
3521
3522         /* if nothing changed state to error or salvaging,
3523          * drop state to unattached */
3524         if (!VIsErrorState(V_attachState(vp))) {
3525             VChangeState_r(vp, VOL_STATE_UNATTACHED);
3526         }
3527         VCancelReservation_r(vp);
3528         /* no usage of vp is safe beyond this point */
3529     }
3530     return ret;
3531 }
3532 #else /* AFS_DEMAND_ATTACH_FS */
3533 static int
3534 VCheckOffline(register Volume * vp)
3535 {
3536     Volume * rvp = NULL;
3537     int ret = 0;
3538
3539     if (vp->goingOffline && !vp->nUsers) {
3540         Error error;
3541         assert(programType == fileServer);
3542
3543         ret = 1;
3544         vp->goingOffline = 0;
3545         V_inUse(vp) = 0;
3546         VUpdateVolume_r(&error, vp, 0);
3547         VCloseVolumeHandles_r(vp);
3548         if (LogLevel) {
3549             Log("VOffline: Volume %u (%s) is now offline", V_id(vp),
3550                 V_name(vp));
3551             if (V_offlineMessage(vp)[0])
3552                 Log(" (%s)", V_offlineMessage(vp));
3553             Log("\n");
3554         }
3555         FreeVolumeHeader(vp);
3556 #ifdef AFS_PTHREAD_ENV
3557         assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3558 #else /* AFS_PTHREAD_ENV */
3559         LWP_NoYieldSignal(VPutVolume);
3560 #endif /* AFS_PTHREAD_ENV */
3561     }
3562     return ret;
3563 }
3564 #endif /* AFS_DEMAND_ATTACH_FS */
3565
3566 /***************************************************/
3567 /* demand attach fs ref counting routines          */
3568 /***************************************************/
3569
3570 #ifdef AFS_DEMAND_ATTACH_FS
3571 /* the following two functions handle reference counting for
3572  * asynchronous operations on volume structs.
3573  *
3574  * their purpose is to prevent a VDetachVolume or VShutdown
3575  * from free()ing the Volume struct during an async i/o op */
3576
3577 /* register with the async volume op ref counter */
3578 /* VCreateReservation_r moved into inline code header because it 
3579  * is now needed in vnode.c -- tkeiser 11/20/2007 
3580  */
3581
3582 /**
3583  * decrement volume-package internal refcount.
3584  *
3585  * @param vp  volume object pointer
3586  *
3587  * @internal volume package internal use only
3588  *
3589  * @pre 
3590  *    @arg VOL_LOCK is held
3591  *    @arg lightweight refcount held
3592  *
3593  * @post volume waiters refcount is decremented; volume may
3594  *       have been deallocated/shutdown/offlined/salvaged/
3595  *       whatever during the process
3596  *
3597  * @warning once you have tossed your last reference (you can acquire
3598  *          lightweight refs recursively) it is NOT SAFE to reference
3599  *          a volume object pointer ever again
3600  *
3601  * @see VCreateReservation_r
3602  *
3603  * @note DEMAND_ATTACH_FS only
3604  */
3605 void
3606 VCancelReservation_r(Volume * vp)
3607 {
3608     assert(--vp->nWaiters >= 0);
3609     if (vp->nWaiters == 0) {
3610         VCheckOffline(vp);
3611         if (!VCheckDetach(vp)) {
3612             VCheckSalvage(vp);
3613             VCheckFree(vp);
3614         }
3615     }
3616 }
3617
3618 /* check to see if we should free this volume now
3619  * return 1 if volume was freed, 0 otherwise */
3620 static int
3621 VCheckFree(Volume * vp)
3622 {
3623     int ret = 0;
3624     if ((vp->nUsers == 0) &&
3625         (vp->nWaiters == 0) &&
3626         !(V_attachFlags(vp) & (VOL_IN_HASH | 
3627                                VOL_ON_VBYP_LIST | 
3628                                VOL_IS_BUSY |
3629                                VOL_ON_VLRU))) {
3630         ReallyFreeVolume(vp);
3631         ret = 1;
3632     }
3633     return ret;
3634 }
3635 #endif /* AFS_DEMAND_ATTACH_FS */
3636
3637
3638 /***************************************************/
3639 /* online volume operations routines               */
3640 /***************************************************/
3641
3642 #ifdef AFS_DEMAND_ATTACH_FS
3643 /**
3644  * register a volume operation on a given volume.
3645  *
3646  * @param[in] vp       volume object
3647  * @param[in] vopinfo  volume operation info object
3648  *
3649  * @pre VOL_LOCK is held
3650  *
3651  * @post volume operation info object attached to volume object.
3652  *       volume operation statistics updated.
3653  *
3654  * @note by "attached" we mean a copy of the passed in object is made
3655  *
3656  * @internal volume package internal use only
3657  */
3658 int
3659 VRegisterVolOp_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
3660 {
3661     FSSYNC_VolOp_info * info;
3662
3663     /* attach a vol op info node to the volume struct */
3664     info = (FSSYNC_VolOp_info *) malloc(sizeof(FSSYNC_VolOp_info));
3665     assert(info != NULL);
3666     memcpy(info, vopinfo, sizeof(FSSYNC_VolOp_info));
3667     vp->pending_vol_op = info;
3668
3669     /* update stats */
3670     vp->stats.last_vol_op = FT_ApproxTime();
3671     vp->stats.vol_ops++;
3672     IncUInt64(&VStats.vol_ops);
3673
3674     return 0;
3675 }
3676
3677 /**
3678  * deregister the volume operation attached to this volume.
3679  *
3680  * @param[in] vp  volume object pointer
3681  *
3682  * @pre VOL_LOCK is held
3683  *
3684  * @post the volume operation info object is detached from the volume object
3685  *
3686  * @internal volume package internal use only
3687  */
3688 int
3689 VDeregisterVolOp_r(Volume * vp)
3690 {
3691     if (vp->pending_vol_op) {
3692         free(vp->pending_vol_op);
3693         vp->pending_vol_op = NULL;
3694     }
3695     return 0;
3696 }
3697 #endif /* AFS_DEMAND_ATTACH_FS */
3698
3699 /**
3700  * determine whether it is safe to leave a volume online during
3701  * the volume operation described by the vopinfo object.
3702  *
3703  * @param[in] vp        volume object
3704  * @param[in] vopinfo   volume operation info object
3705  *
3706  * @return whether it is safe to leave volume online
3707  *    @retval 0  it is NOT SAFE to leave the volume online
3708  *    @retval 1  it is safe to leave the volume online during the operation
3709  *
3710  * @pre
3711  *    @arg VOL_LOCK is held
3712  *    @arg disk header attached to vp (heavyweight ref on vp will guarantee
3713  *         this condition is met)
3714  *
3715  * @internal volume package internal use only
3716  */
3717 int
3718 VVolOpLeaveOnline_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
3719 {
3720     return (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
3721             (vopinfo->com.reason == V_READONLY ||
3722              (!VolumeWriteable(vp) &&
3723               (vopinfo->com.reason == V_CLONE ||
3724                vopinfo->com.reason == V_DUMP))));
3725 }
3726
3727 /**
3728  * determine whether VBUSY should be set during this volume operation.
3729  *
3730  * @param[in] vp        volume object
3731  * @param[in] vopinfo   volume operation info object
3732  *
3733  * @return whether VBUSY should be set
3734  *   @retval 0  VBUSY does NOT need to be set
3735  *   @retval 1  VBUSY SHOULD be set
3736  *
3737  * @pre VOL_LOCK is held
3738  *
3739  * @internal volume package internal use only
3740  */
3741 int
3742 VVolOpSetVBusy_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
3743 {
3744     return (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
3745             (vopinfo->com.reason == V_CLONE ||
3746              vopinfo->com.reason == V_DUMP));
3747 }
3748
3749
3750 /***************************************************/
3751 /* online salvager routines                        */
3752 /***************************************************/
3753 #if defined(AFS_DEMAND_ATTACH_FS)
3754 #define SALVAGE_PRIO_UPDATE_INTERVAL 3      /**< number of seconds between prio updates */
3755 #define SALVAGE_COUNT_MAX 16                /**< number of online salvages we
3756                                              *   allow before moving the volume
3757                                              *   into a permanent error state
3758                                              *
3759                                              *   once this threshold is reached,
3760                                              *   the operator will have to manually
3761                                              *   issue a 'bos salvage' to bring
3762                                              *   the volume back online
3763                                              */
3764
3765 /**
3766  * check whether a salvage needs to be performed on this volume.
3767  *
3768  * @param[in] vp   pointer to volume object
3769  *
3770  * @return status code
3771  *    @retval 0 no salvage scheduled
3772  *    @retval 1 a salvage has been scheduled with the salvageserver
3773  *
3774  * @pre VOL_LOCK is held
3775  *
3776  * @post if salvage request flag is set and nUsers and nWaiters are zero,
3777  *       then a salvage will be requested
3778  *
3779  * @note this is one of the event handlers called by VCancelReservation_r
3780  *
3781  * @see VCancelReservation_r
3782  *
3783  * @internal volume package internal use only.
3784  */
3785 static int
3786 VCheckSalvage(register Volume * vp)
3787 {
3788     int ret = 0;
3789 #ifdef SALVSYNC_BUILD_CLIENT
3790     if (vp->nUsers || vp->nWaiters)
3791         return ret;
3792     if (vp->salvage.requested) {
3793         VScheduleSalvage_r(vp);
3794         ret = 1;
3795     }
3796 #endif /* SALVSYNC_BUILD_CLIENT */
3797     return ret;
3798 }
3799
3800 /**
3801  * request volume salvage.
3802  *
3803  * @param[out] ec      computed client error code
3804  * @param[in]  vp      volume object pointer
3805  * @param[in]  reason  reason code (passed to salvageserver via SALVSYNC)
3806  * @param[in]  flags   see flags note below
3807  *
3808  * @note flags:
3809  *       VOL_SALVAGE_INVALIDATE_HEADER causes volume header cache entry 
3810  *                                     to be invalidated.
3811  *
3812  * @pre VOL_LOCK is held.
3813  *
3814  * @post volume state is changed.
3815  *       for fileserver, salvage will be requested once refcount reaches zero.
3816  *
3817  * @return operation status code
3818  *   @retval 0  volume salvage will occur
3819  *   @retval 1  volume salvage could not be scheduled
3820  *
3821  * @note DAFS fileserver only
3822  *
3823  * @note this call does not synchronously schedule a volume salvage.  rather,
3824  *       it sets volume state so that when volume refcounts reach zero, a
3825  *       volume salvage will occur.  by "refcounts", we mean both nUsers and 
3826  *       nWaiters must be zero.
3827  *
3828  * @internal volume package internal use only.
3829  */
3830 int
3831 VRequestSalvage_r(Error * ec, Volume * vp, int reason, int flags)
3832 {
3833     int code = 0;
3834     /*
3835      * for DAFS volume utilities, transition to error state
3836      * (at some point in the future, we should consider
3837      *  making volser talk to salsrv)
3838      */
3839     if (programType != fileServer) {
3840         VChangeState_r(vp, VOL_STATE_ERROR);
3841         *ec = VSALVAGE;
3842         return 1;
3843     }
3844
3845     if (!vp->salvage.requested) {
3846         vp->salvage.requested = 1;
3847         vp->salvage.reason = reason;
3848         vp->stats.last_salvage = FT_ApproxTime();
3849         if (flags & VOL_SALVAGE_INVALIDATE_HEADER) {
3850             /* XXX this should likely be changed to FreeVolumeHeader() */
3851             ReleaseVolumeHeader(vp->header);
3852         }
3853         if (vp->stats.salvages < SALVAGE_COUNT_MAX) {
3854             VChangeState_r(vp, VOL_STATE_SALVAGING);
3855             *ec = VSALVAGING;
3856         } else {
3857             Log("VRequestSalvage: volume %u online salvaged too many times; forced offline.\n", vp->hashid);
3858             VChangeState_r(vp, VOL_STATE_ERROR);
3859             *ec = VSALVAGE;
3860             code = 1;
3861         }
3862     }
3863     return code;
3864 }
3865
3866 /**
3867  * update salvageserver scheduling priority for a volume.
3868  *
3869  * @param[in] vp  pointer to volume object
3870  *
3871  * @return operation status
3872  *   @retval 0  success
3873  *   @retval 1  request denied, or SALVSYNC communications failure
3874  *
3875  * @pre VOL_LOCK is held.
3876  *
3877  * @post in-core salvage priority counter is incremented.  if at least
3878  *       SALVAGE_PRIO_UPDATE_INTERVAL seconds have elapsed since the
3879  *       last SALVSYNC_RAISEPRIO request, we contact the salvageserver
3880  *       to update its priority queue.  if no salvage is scheduled,
3881  *       this function is a no-op.
3882  *
3883  * @note DAFS fileserver only
3884  *
3885  * @note this should be called whenever a VGetVolume fails due to a 
3886  *       pending salvage request
3887  *
3888  * @todo should set exclusive state and drop glock around salvsync call
3889  *
3890  * @internal volume package internal use only.
3891  */
3892 static int
3893 VUpdateSalvagePriority_r(Volume * vp)
3894 {
3895     int code, ret=0;
3896     afs_uint32 now;
3897
3898 #ifdef SALVSYNC_BUILD_CLIENT
3899     vp->salvage.prio++;
3900     now = FT_ApproxTime();
3901
3902     /* update the salvageserver priority queue occasionally so that
3903      * frequently requested volumes get moved to the head of the queue 
3904      */
3905     if ((vp->salvage.scheduled) &&
3906         (vp->stats.last_salvage_req < (now-SALVAGE_PRIO_UPDATE_INTERVAL))) {
3907         code = SALVSYNC_SalvageVolume(vp->hashid,
3908                                       VPartitionPath(vp->partition),
3909                                       SALVSYNC_RAISEPRIO,
3910                                       vp->salvage.reason,
3911                                       vp->salvage.prio,
3912                                       NULL);
3913         vp->stats.last_salvage_req = now;
3914         if (code != SYNC_OK) {
3915             ret = 1;
3916         }
3917     }
3918 #endif /* SALVSYNC_BUILD_CLIENT */
3919     return ret;
3920 }
3921
3922
3923 /**
3924  * schedule a salvage with the salvage server.
3925  *
3926  * @param[in] vp  pointer to volume object
3927  *
3928  * @return operation status
3929  *    @retval 0 salvage scheduled successfully
3930  *    @retval 1 salvage not scheduled, or SALVSYNC com error
3931  *
3932  * @pre 
3933  *    @arg VOL_LOCK is held.
3934  *    @arg nUsers and nWaiters should be zero.
3935  *
3936  * @post salvageserver is sent a salvage request
3937  *
3938  * @note DAFS fileserver only
3939  *
3940  * @internal volume package internal use only.
3941  */
3942 static int
3943 VScheduleSalvage_r(Volume * vp)
3944 {
3945     int code, ret=0;
3946 #ifdef SALVSYNC_BUILD_CLIENT
3947     VolState state_save;
3948     char partName[16];
3949
3950     if (vp->nWaiters || vp->nUsers) {
3951         return 1;
3952     }
3953
3954     /* prevent endless salvage,attach,salvage,attach,... loops */
3955     if (vp->stats.salvages >= SALVAGE_COUNT_MAX)
3956         return 1;
3957
3958     if (!vp->salvage.scheduled) {
3959         /* if we haven't previously scheduled a salvage, do so now 
3960          *
3961          * set the volume to an exclusive state and drop the lock
3962          * around the SALVSYNC call
3963          *
3964          * note that we do NOT acquire a reservation here -- doing so
3965          * could result in unbounded recursion
3966          */
3967         strlcpy(partName, VPartitionPath(vp->partition), sizeof(partName));
3968         state_save = VChangeState_r(vp, VOL_STATE_SALVSYNC_REQ);
3969         V_attachFlags(vp) |= VOL_IS_BUSY;
3970         VOL_UNLOCK;
3971
3972         /* can't use V_id() since there's no guarantee
3973          * we have the disk data header at this point */
3974         code = SALVSYNC_SalvageVolume(vp->hashid,
3975                                       partName,
3976                                       SALVSYNC_SALVAGE,
3977                                       vp->salvage.reason,
3978                                       vp->salvage.prio,
3979                                       NULL);
3980         VOL_LOCK;
3981         VChangeState_r(vp, state_save);
3982         V_attachFlags(vp) &= ~(VOL_IS_BUSY);
3983
3984         if (code == SYNC_OK) {
3985             vp->salvage.scheduled = 1;
3986             vp->stats.salvages++;
3987             vp->stats.last_salvage_req = FT_ApproxTime();
3988             IncUInt64(&VStats.salvages);
3989         } else {
3990             ret = 1;
3991             switch(code) {
3992             case SYNC_BAD_COMMAND:
3993             case SYNC_COM_ERROR:
3994                 break;
3995             case SYNC_DENIED:
3996                 Log("VScheduleSalvage_r:  SALVSYNC request denied\n");
3997                 break;
3998             default:
3999                 Log("VScheduleSalvage_r:  SALVSYNC unknown protocol error\n");
4000                 break;
4001             }
4002         }
4003     }
4004 #endif /* SALVSYNC_BUILD_CLIENT */
4005     return ret;
4006 }
4007
4008 /**
4009  * ask salvageserver to cancel a scheduled salvage operation.
4010  *
4011  * @param[in] vp      pointer to volume object
4012  * @param[in] reason  SALVSYNC protocol reason code
4013  *
4014  * @return operation status
4015  *    @retval 0 success
4016  *    @retval 1 request failed
4017  *
4018  * @pre VOL_LOCK is held.
4019  *
4020  * @post salvageserver is sent a request to cancel the volume salvage
4021  *
4022  * @todo should set exclusive state and drop glock around salvsync call
4023  *
4024  * @internal volume package internal use only.
4025  */
4026 static int
4027 VCancelSalvage_r(Volume * vp, int reason)
4028 {
4029     int code, ret = 0;
4030
4031 #ifdef SALVSYNC_BUILD_CLIENT
4032     if (vp->salvage.scheduled) {
4033         code = SALVSYNC_SalvageVolume(vp->hashid,
4034                                       VPartitionPath(vp->partition),
4035                                       SALVSYNC_CANCEL,
4036                                       reason,
4037                                       0,
4038                                       NULL);
4039         if (code == SYNC_OK) {
4040             vp->salvage.scheduled = 0;
4041         } else {
4042             ret = 1;
4043         }
4044     }
4045 #endif /* SALVSYNC_BUILD_CLIENT */
4046     return ret;
4047 }
4048
4049
4050 #ifdef SALVSYNC_BUILD_CLIENT
4051 /**
4052  * connect to the salvageserver SYNC service.
4053  *
4054  * @return operation status
4055  *    @retval 0 failure
4056  *    @retval 1 success
4057  *
4058  * @post connection to salvageserver SYNC service established
4059  *
4060  * @see VConnectSALV_r
4061  * @see VDisconnectSALV
4062  * @see VReconnectSALV
4063  */
4064 int
4065 VConnectSALV(void)
4066 {
4067     int retVal;
4068     VOL_LOCK;
4069     retVal = VConnectSALV_r();
4070     VOL_UNLOCK;
4071     return retVal;
4072 }
4073
4074 /**
4075  * connect to the salvageserver SYNC service.
4076  *
4077  * @return operation status
4078  *    @retval 0 failure
4079  *    @retval 1 success
4080  *
4081  * @pre VOL_LOCK is held.
4082  *
4083  * @post connection to salvageserver SYNC service established
4084  *
4085  * @see VConnectSALV
4086  * @see VDisconnectSALV_r
4087  * @see VReconnectSALV_r
4088  * @see SALVSYNC_clientInit
4089  *
4090  * @internal volume package internal use only.
4091  */
4092 int
4093 VConnectSALV_r(void)
4094 {
4095     return SALVSYNC_clientInit();
4096 }
4097
4098 /**
4099  * disconnect from the salvageserver SYNC service.
4100  *
4101  * @return operation status
4102  *    @retval 0 success
4103  *
4104  * @pre client should have a live connection to the salvageserver
4105  *
4106  * @post connection to salvageserver SYNC service destroyed
4107  *
4108  * @see VDisconnectSALV_r
4109  * @see VConnectSALV
4110  * @see VReconnectSALV
4111  */
4112 int
4113 VDisconnectSALV(void)
4114 {
4115     int retVal;
4116     VOL_LOCK;
4117     VDisconnectSALV_r();
4118     VOL_UNLOCK;
4119     return retVal;
4120 }
4121
4122 /**
4123  * disconnect from the salvageserver SYNC service.
4124  *
4125  * @return operation status
4126  *    @retval 0 success
4127  *
4128  * @pre 
4129  *    @arg VOL_LOCK is held.
4130  *    @arg client should have a live connection to the salvageserver.
4131  *
4132  * @post connection to salvageserver SYNC service destroyed
4133  *
4134  * @see VDisconnectSALV
4135  * @see VConnectSALV_r
4136  * @see VReconnectSALV_r
4137  * @see SALVSYNC_clientFinis
4138  *
4139  * @internal volume package internal use only.
4140  */
4141 int
4142 VDisconnectSALV_r(void)
4143
4144     return SALVSYNC_clientFinis();
4145 }
4146
4147 /**
4148  * disconnect and then re-connect to the salvageserver SYNC service.
4149  *
4150  * @return operation status
4151  *    @retval 0 failure
4152  *    @retval 1 success
4153  *
4154  * @pre client should have a live connection to the salvageserver
4155  *
4156  * @post old connection is dropped, and a new one is established
4157  *
4158  * @see VConnectSALV
4159  * @see VDisconnectSALV
4160  * @see VReconnectSALV_r
4161  */
4162 int
4163 VReconnectSALV(void)
4164 {
4165     int retVal;
4166     VOL_LOCK;
4167     retVal = VReconnectSALV_r();
4168     VOL_UNLOCK;
4169     return retVal;
4170 }
4171
4172 /**
4173  * disconnect and then re-connect to the salvageserver SYNC service.
4174  *
4175  * @return operation status
4176  *    @retval 0 failure
4177  *    @retval 1 success
4178  *
4179  * @pre 
4180  *    @arg VOL_LOCK is held.
4181  *    @arg client should have a live connection to the salvageserver.
4182  *
4183  * @post old connection is dropped, and a new one is established
4184  *
4185  * @see VConnectSALV_r
4186  * @see VDisconnectSALV
4187  * @see VReconnectSALV
4188  * @see SALVSYNC_clientReconnect
4189  *
4190  * @internal volume package internal use only.
4191  */
4192 int
4193 VReconnectSALV_r(void)
4194 {
4195     return SALVSYNC_clientReconnect();
4196 }
4197 #endif /* SALVSYNC_BUILD_CLIENT */
4198 #endif /* AFS_DEMAND_ATTACH_FS */
4199
4200
4201 /***************************************************/
4202 /* FSSYNC routines                                 */
4203 /***************************************************/
4204
4205 /* This must be called by any volume utility which needs to run while the
4206    file server is also running.  This is separated from VInitVolumePackage so
4207    that a utility can fork--and each of the children can independently
4208    initialize communication with the file server */
4209 #ifdef FSSYNC_BUILD_CLIENT
4210 /**
4211  * connect to the fileserver SYNC service.
4212  *
4213  * @return operation status
4214  *    @retval 0 failure
4215  *    @retval 1 success
4216  *
4217  * @pre 
4218  *    @arg VInit must equal 2.
4219  *    @arg Program Type must not be fileserver or salvager.
4220  *
4221  * @post connection to fileserver SYNC service established
4222  *
4223  * @see VConnectFS_r
4224  * @see VDisconnectFS
4225  * @see VChildProcReconnectFS
4226  */
4227 int
4228 VConnectFS(void)
4229 {
4230     int retVal;
4231     VOL_LOCK;
4232     retVal = VConnectFS_r();
4233     VOL_UNLOCK;
4234     return retVal;
4235 }
4236
4237 /**
4238  * connect to the fileserver SYNC service.
4239  *
4240  * @return operation status
4241  *    @retval 0 failure
4242  *    @retval 1 success
4243  *
4244  * @pre 
4245  *    @arg VInit must equal 2.
4246  *    @arg Program Type must not be fileserver or salvager.
4247  *    @arg VOL_LOCK is held.
4248  *
4249  * @post connection to fileserver SYNC service established
4250  *
4251  * @see VConnectFS
4252  * @see VDisconnectFS_r
4253  * @see VChildProcReconnectFS_r
4254  *
4255  * @internal volume package internal use only.
4256  */
4257 int
4258 VConnectFS_r(void)
4259 {
4260     int rc;
4261     assert((VInit == 2) && 
4262            (programType != fileServer) &&
4263            (programType != salvager));
4264     rc = FSYNC_clientInit();
4265     if (rc)
4266         VInit = 3;
4267     return rc;
4268 }
4269
4270 /**
4271  * disconnect from the fileserver SYNC service.
4272  *
4273  * @pre 
4274  *    @arg client should have a live connection to the fileserver.
4275  *    @arg VOL_LOCK is held.
4276  *    @arg Program Type must not be fileserver or salvager.
4277  *
4278  * @post connection to fileserver SYNC service destroyed
4279  *
4280  * @see VDisconnectFS
4281  * @see VConnectFS_r
4282  * @see VChildProcReconnectFS_r
4283  *
4284  * @internal volume package internal use only.
4285  */
4286 void
4287 VDisconnectFS_r(void)
4288 {
4289     assert((programType != fileServer) &&
4290            (programType != salvager));
4291     FSYNC_clientFinis();
4292     VInit = 2;
4293 }
4294
4295 /**
4296  * disconnect from the fileserver SYNC service.
4297  *
4298  * @pre
4299  *    @arg client should have a live connection to the fileserver.
4300  *    @arg Program Type must not be fileserver or salvager.
4301  *
4302  * @post connection to fileserver SYNC service destroyed
4303  *
4304  * @see VDisconnectFS_r
4305  * @see VConnectFS
4306  * @see VChildProcReconnectFS
4307  */
4308 void
4309 VDisconnectFS(void)
4310 {
4311     VOL_LOCK;
4312     VDisconnectFS_r();
4313     VOL_UNLOCK;
4314 }
4315
4316 /**
4317  * connect to the fileserver SYNC service from a child process following a fork.
4318  *
4319  * @return operation status
4320  *    @retval 0 failure
4321  *    @retval 1 success
4322  *
4323  * @pre
4324  *    @arg VOL_LOCK is held.
4325  *    @arg current FSYNC handle is shared with a parent process
4326  *
4327  * @post current FSYNC handle is discarded and a new connection to the
4328  *       fileserver SYNC service is established
4329  *
4330  * @see VChildProcReconnectFS
4331  * @see VConnectFS_r
4332  * @see VDisconnectFS_r
4333  *
4334  * @internal volume package internal use only.
4335  */
4336 int
4337 VChildProcReconnectFS_r(void)
4338 {
4339     return FSYNC_clientChildProcReconnect();
4340 }
4341
4342 /**
4343  * connect to the fileserver SYNC service from a child process following a fork.
4344  *
4345  * @return operation status
4346  *    @retval 0 failure
4347  *    @retval 1 success
4348  *
4349  * @pre current FSYNC handle is shared with a parent process
4350  *
4351  * @post current FSYNC handle is discarded and a new connection to the
4352  *       fileserver SYNC service is established
4353  *
4354  * @see VChildProcReconnectFS_r
4355  * @see VConnectFS
4356  * @see VDisconnectFS
4357  */
4358 int
4359 VChildProcReconnectFS(void)
4360 {
4361     int ret;
4362     VOL_LOCK;
4363     ret = VChildProcReconnectFS_r();
4364     VOL_UNLOCK;
4365     return ret;
4366 }
4367 #endif /* FSSYNC_BUILD_CLIENT */
4368
4369
4370 /***************************************************/
4371 /* volume bitmap routines                          */
4372 /***************************************************/
4373
4374 /*
4375  * For demand attach fs, flags parameter controls
4376  * locking behavior.  If (flags & VOL_ALLOC_BITMAP_WAIT)
4377  * is set, then this function will create a reservation
4378  * and block on any other exclusive operations.  Otherwise,
4379  * this function assumes the caller already has exclusive
4380  * access to vp, and we just change the volume state.
4381  */
4382 VnodeId
4383 VAllocBitmapEntry_r(Error * ec, Volume * vp, 
4384                     struct vnodeIndex *index, int flags)
4385 {
4386     VnodeId ret;
4387     register byte *bp, *ep;
4388 #ifdef AFS_DEMAND_ATTACH_FS
4389     VolState state_save;
4390 #endif /* AFS_DEMAND_ATTACH_FS */
4391
4392     *ec = 0;
4393
4394     /* This test is probably redundant */
4395     if (!VolumeWriteable(vp)) {
4396         *ec = (bit32) VREADONLY;
4397         return 0;
4398     }
4399
4400 #ifdef AFS_DEMAND_ATTACH_FS
4401     if (flags & VOL_ALLOC_BITMAP_WAIT) {
4402         VCreateReservation_r(vp);
4403         VWaitExclusiveState_r(vp);
4404     }
4405     state_save = VChangeState_r(vp, VOL_STATE_GET_BITMAP);
4406 #endif /* AFS_DEMAND_ATTACH_FS */
4407
4408 #ifdef BITMAP_LATER
4409     if ((programType == fileServer) && !index->bitmap) {
4410         int i;
4411 #ifndef AFS_DEMAND_ATTACH_FS
4412         /* demand attach fs uses the volume state to avoid races.
4413          * specialStatus field is not used at all */
4414         int wasVBUSY = 0;
4415         if (vp->specialStatus == VBUSY) {
4416             if (vp->goingOffline) {     /* vos dump waiting for the volume to
4417                                          * go offline. We probably come here
4418                                          * from AddNewReadableResidency */
4419                 wasVBUSY = 1;
4420             } else {
4421                 while (vp->specialStatus == VBUSY) {
4422 #ifdef AFS_PTHREAD_ENV
4423                     VOL_UNLOCK;
4424                     sleep(2);
4425                     VOL_LOCK;
4426 #else /* !AFS_PTHREAD_ENV */
4427                     IOMGR_Sleep(2);
4428 #endif /* !AFS_PTHREAD_ENV */
4429                 }
4430             }
4431         }
4432 #endif /* !AFS_DEMAND_ATTACH_FS */
4433
4434         if (!index->bitmap) {
4435 #ifndef AFS_DEMAND_ATTACH_FS
4436             vp->specialStatus = VBUSY;  /* Stop anyone else from using it. */
4437 #endif /* AFS_DEMAND_ATTACH_FS */
4438             for (i = 0; i < nVNODECLASSES; i++) {
4439                 VGetBitmap_r(ec, vp, i);
4440                 if (*ec) {
4441 #ifdef AFS_DEMAND_ATTACH_FS
4442                     VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
4443 #else /* AFS_DEMAND_ATTACH_FS */
4444                     DeleteVolumeFromHashTable(vp);
4445                     vp->shuttingDown = 1;       /* Let who has it free it. */
4446                     vp->specialStatus = 0;
4447 #endif /* AFS_DEMAND_ATTACH_FS */
4448                     ret = NULL;
4449                     goto done;
4450                 }
4451             }
4452 #ifndef AFS_DEMAND_ATTACH_FS
4453             if (!wasVBUSY)
4454                 vp->specialStatus = 0;  /* Allow others to have access. */
4455 #endif /* AFS_DEMAND_ATTACH_FS */
4456         }
4457     }
4458 #endif /* BITMAP_LATER */
4459
4460 #ifdef AFS_DEMAND_ATTACH_FS
4461     VOL_UNLOCK;
4462 #endif /* AFS_DEMAND_ATTACH_FS */
4463     bp = index->bitmap + index->bitmapOffset;
4464     ep = index->bitmap + index->bitmapSize;
4465     while (bp < ep) {
4466         if ((*(bit32 *) bp) != (bit32) 0xffffffff) {
4467             int o;
4468             index->bitmapOffset = (afs_uint32) (bp - index->bitmap);
4469             while (*bp == 0xff)
4470                 bp++;
4471             o = ffs(~*bp) - 1;  /* ffs is documented in BSTRING(3) */
4472             *bp |= (1 << o);
4473             ret = (VnodeId) ((bp - index->bitmap) * 8 + o);
4474 #ifdef AFS_DEMAND_ATTACH_FS
4475             VOL_LOCK;
4476 #endif /* AFS_DEMAND_ATTACH_FS */
4477             goto done;
4478         }
4479         bp += sizeof(bit32) /* i.e. 4 */ ;
4480     }
4481     /* No bit map entry--must grow bitmap */
4482     bp = (byte *)
4483         realloc(index->bitmap, index->bitmapSize + VOLUME_BITMAP_GROWSIZE);
4484     assert(bp != NULL);
4485     index->bitmap = bp;
4486     bp += index->bitmapSize;
4487     memset(bp, 0, VOLUME_BITMAP_GROWSIZE);
4488     index->bitmapOffset = index->bitmapSize;
4489     index->bitmapSize += VOLUME_BITMAP_GROWSIZE;
4490     *bp = 1;
4491     ret = index->bitmapOffset * 8;
4492 #ifdef AFS_DEMAND_ATTACH_FS
4493     VOL_LOCK;
4494 #endif /* AFS_DEMAND_ATTACH_FS */
4495
4496  done:
4497 #ifdef AFS_DEMAND_ATTACH_FS
4498     VChangeState_r(vp, state_save);
4499     if (flags & VOL_ALLOC_BITMAP_WAIT) {
4500         VCancelReservation_r(vp);
4501     }
4502 #endif /* AFS_DEMAND_ATTACH_FS */
4503     return ret;
4504 }
4505
4506 VnodeId
4507 VAllocBitmapEntry(Error * ec, Volume * vp, register struct vnodeIndex * index)
4508 {
4509     VnodeId retVal;
4510     VOL_LOCK;
4511     retVal = VAllocBitmapEntry_r(ec, vp, index, VOL_ALLOC_BITMAP_WAIT);
4512     VOL_UNLOCK;
4513     return retVal;
4514 }
4515
4516 void
4517 VFreeBitMapEntry_r(Error * ec, register struct vnodeIndex *index,
4518                    unsigned bitNumber)
4519 {
4520     unsigned int offset;
4521
4522     *ec = 0;
4523 #ifdef BITMAP_LATER
4524     if (!index->bitmap)
4525         return;
4526 #endif /* BITMAP_LATER */
4527     offset = bitNumber >> 3;
4528     if (offset >= index->bitmapSize) {
4529         *ec = VNOVNODE;
4530         return;
4531     }
4532     if (offset < index->bitmapOffset)
4533         index->bitmapOffset = offset & ~3;      /* Truncate to nearest bit32 */
4534     *(index->bitmap + offset) &= ~(1 << (bitNumber & 0x7));
4535 }
4536
4537 void
4538 VFreeBitMapEntry(Error * ec, register struct vnodeIndex *index,
4539                  unsigned bitNumber)
4540 {
4541     VOL_LOCK;
4542     VFreeBitMapEntry_r(ec, index, bitNumber);
4543     VOL_UNLOCK;
4544 }
4545
4546 /* this function will drop the glock internally.
4547  * for old pthread fileservers, this is safe thanks to vbusy.
4548  *
4549  * for demand attach fs, caller must have already called
4550  * VCreateReservation_r and VWaitExclusiveState_r */
4551 static void
4552 VGetBitmap_r(Error * ec, Volume * vp, VnodeClass class)
4553 {
4554     StreamHandle_t *file;
4555     int nVnodes;
4556     int size;
4557     struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
4558     struct vnodeIndex *vip = &vp->vnodeIndex[class];
4559     struct VnodeDiskObject *vnode;
4560     unsigned int unique = 0;
4561     FdHandle_t *fdP;
4562 #ifdef BITMAP_LATER
4563     byte *BitMap = 0;
4564 #endif /* BITMAP_LATER */
4565 #ifdef AFS_DEMAND_ATTACH_FS
4566     VolState state_save;
4567 #endif /* AFS_DEMAND_ATTACH_FS */
4568
4569     *ec = 0;
4570
4571 #ifdef AFS_DEMAND_ATTACH_FS
4572     state_save = VChangeState_r(vp, VOL_STATE_GET_BITMAP);
4573 #endif /* AFS_DEMAND_ATTACH_FS */
4574     VOL_UNLOCK;
4575
4576     fdP = IH_OPEN(vip->handle);
4577     assert(fdP != NULL);
4578     file = FDH_FDOPEN(fdP, "r");
4579     assert(file != NULL);
4580     vnode = (VnodeDiskObject *) malloc(vcp->diskSize);
4581     assert(vnode != NULL);
4582     size = OS_SIZE(fdP->fd_fd);
4583     assert(size != -1);
4584     nVnodes = (size <= vcp->diskSize ? 0 : size - vcp->diskSize)
4585         >> vcp->logSize;
4586     vip->bitmapSize = ((nVnodes / 8) + 10) / 4 * 4;     /* The 10 is a little extra so
4587                                                          * a few files can be created in this volume,
4588                                                          * the whole thing is rounded up to nearest 4
4589                                                          * bytes, because the bit map allocator likes
4590                                                          * it that way */
4591 #ifdef BITMAP_LATER
4592     BitMap = (byte *) calloc(1, vip->bitmapSize);
4593     assert(BitMap != NULL);
4594 #else /* BITMAP_LATER */
4595     vip->bitmap = (byte *) calloc(1, vip->bitmapSize);
4596     assert(vip->bitmap != NULL);
4597     vip->bitmapOffset = 0;
4598 #endif /* BITMAP_LATER */
4599     if (STREAM_SEEK(file, vcp->diskSize, 0) != -1) {
4600         int bitNumber = 0;
4601         for (bitNumber = 0; bitNumber < nVnodes + 100; bitNumber++) {
4602             if (STREAM_READ(vnode, vcp->diskSize, 1, file) != 1)
4603                 break;
4604             if (vnode->type != vNull) {
4605                 if (vnode->vnodeMagic != vcp->magic) {
4606                     Log("GetBitmap: addled vnode index in volume %s; volume needs salvage\n", V_name(vp));
4607                     *ec = VSALVAGE;
4608                     break;
4609                 }
4610 #ifdef BITMAP_LATER
4611                 *(BitMap + (bitNumber >> 3)) |= (1 << (bitNumber & 0x7));
4612 #else /* BITMAP_LATER */
4613                 *(vip->bitmap + (bitNumber >> 3)) |= (1 << (bitNumber & 0x7));
4614 #endif /* BITMAP_LATER */
4615                 if (unique <= vnode->uniquifier)
4616                     unique = vnode->uniquifier + 1;
4617             }
4618 #ifndef AFS_PTHREAD_ENV
4619             if ((bitNumber & 0x00ff) == 0x0ff) {        /* every 256 iterations */
4620                 IOMGR_Poll();
4621             }
4622 #endif /* !AFS_PTHREAD_ENV */
4623         }
4624     }
4625     if (vp->nextVnodeUnique < unique) {
4626         Log("GetBitmap: bad volume uniquifier for volume %s; volume needs salvage\n", V_name(vp));
4627         *ec = VSALVAGE;
4628     }
4629     /* Paranoia, partly justified--I think fclose after fdopen
4630      * doesn't seem to close fd.  In any event, the documentation
4631      * doesn't specify, so it's safer to close it twice.
4632      */
4633     STREAM_CLOSE(file);
4634     FDH_CLOSE(fdP);
4635     free(vnode);
4636
4637     VOL_LOCK;
4638 #ifdef BITMAP_LATER
4639     /* There may have been a racing condition with some other thread, both
4640      * creating the bitmaps for this volume. If the other thread was faster
4641      * the pointer to bitmap should already be filled and we can free ours.
4642      */
4643     if (vip->bitmap == NULL) {
4644         vip->bitmap = BitMap;
4645         vip->bitmapOffset = 0;
4646     } else
4647         free((byte *) BitMap);
4648 #endif /* BITMAP_LATER */
4649 #ifdef AFS_DEMAND_ATTACH_FS
4650     VChangeState_r(vp, state_save);
4651 #endif /* AFS_DEMAND_ATTACH_FS */
4652 }
4653
4654
4655 /***************************************************/
4656 /* Volume Path and Volume Number utility routines  */
4657 /***************************************************/
4658
4659 /**
4660  * find the first occurrence of a volume header file and return the path.
4661  *
4662  * @param[out] ec          outbound error code
4663  * @param[in]  volumeId    volume id to find
4664  * @param[out] partitionp  pointer to disk partition path string
4665  * @param[out] namep       pointer to volume header file name string
4666  *
4667  * @post path to first occurrence of volume header is returned in partitionp
4668  *       and namep, or ec is set accordingly.
4669  *
4670  * @warning this function is NOT re-entrant -- partitionp and namep point to
4671  *          static data segments
4672  *
4673  * @note if a volume utility inadvertently leaves behind a stale volume header
4674  *       on a vice partition, it is possible for callers to get the wrong one,
4675  *       depending on the order of the disk partition linked list.
4676  *
4677  * @internal volume package internal use only.
4678  */
4679 static void
4680 GetVolumePath(Error * ec, VolId volumeId, char **partitionp, char **namep)
4681 {
4682     static char partition[VMAXPATHLEN], name[VMAXPATHLEN];
4683     char path[VMAXPATHLEN];
4684     int found = 0;
4685     struct DiskPartition64 *dp;
4686
4687     *ec = 0;
4688     name[0] = '/';
4689     (void)afs_snprintf(&name[1], (sizeof name) - 1, VFORMAT, volumeId);
4690     for (dp = DiskPartitionList; dp; dp = dp->next) {
4691         struct afs_stat status;
4692         strcpy(path, VPartitionPath(dp));
4693         strcat(path, name);
4694         if (afs_stat(path, &status) == 0) {
4695             strcpy(partition, dp->name);
4696             found = 1;
4697             break;
4698         }
4699     }
4700     if (!found) {
4701         *ec = VNOVOL;
4702         *partitionp = *namep = NULL;
4703     } else {
4704         *partitionp = partition;
4705         *namep = name;
4706     }
4707 }
4708
4709 /**
4710  * extract a volume number from a volume header filename string.
4711  *
4712  * @param[in] name  volume header filename string
4713  *
4714  * @return volume number
4715  *
4716  * @note the string must be of the form VFORMAT.  the only permissible
4717  *       deviation is a leading '/' character.
4718  *
4719  * @see VFORMAT
4720  */
4721 int
4722 VolumeNumber(char *name)
4723 {
4724     if (*name == '/')
4725         name++;
4726     return atoi(name + 1);
4727 }
4728
4729 /**
4730  * compute the volume header filename.
4731  *
4732  * @param[in] volumeId
4733  *
4734  * @return volume header filename
4735  *
4736  * @post volume header filename string is constructed
4737  *
4738  * @warning this function is NOT re-entrant -- the returned string is
4739  *          stored in a static char array.  see VolumeExternalName_r
4740  *          for a re-entrant equivalent.
4741  *
4742  * @see VolumeExternalName_r
4743  *
4744  * @deprecated due to the above re-entrancy warning, this interface should
4745  *             be considered deprecated.  Please use VolumeExternalName_r
4746  *             in its stead.
4747  */
4748 char *
4749 VolumeExternalName(VolumeId volumeId)
4750 {
4751     static char name[VMAXPATHLEN];
4752     (void)afs_snprintf(name, sizeof name, VFORMAT, volumeId);
4753     return name;
4754 }
4755
4756 /**
4757  * compute the volume header filename.
4758  *
4759  * @param[in]     volumeId
4760  * @param[inout]  name       array in which to store filename
4761  * @param[in]     len        length of name array
4762  *
4763  * @return result code from afs_snprintf
4764  *
4765  * @see VolumeExternalName
4766  * @see afs_snprintf
4767  *
4768  * @note re-entrant equivalent of VolumeExternalName
4769  *
4770  * @internal volume package internal use only.
4771  */
4772 static int
4773 VolumeExternalName_r(VolumeId volumeId, char * name, size_t len)
4774 {
4775     return afs_snprintf(name, len, VFORMAT, volumeId);
4776 }
4777
4778
4779 /***************************************************/
4780 /* Volume Usage Statistics routines                */
4781 /***************************************************/
4782
4783 #if OPENAFS_VOL_STATS
4784 #define OneDay  (86400)         /* 24 hours' worth of seconds */
4785 #else
4786 #define OneDay  (24*60*60)      /* 24 hours */
4787 #endif /* OPENAFS_VOL_STATS */
4788
4789 #define Midnight(date) ((date-TimeZoneCorrection)/OneDay*OneDay+TimeZoneCorrection)
4790
4791 /*------------------------------------------------------------------------
4792  * [export] VAdjustVolumeStatistics
4793  *
4794  * Description:
4795  *      If we've passed midnight, we need to update all the day use
4796  *      statistics as well as zeroing the detailed volume statistics
4797  *      (if we are implementing them).
4798  *
4799  * Arguments:
4800  *      vp : Pointer to the volume structure describing the lucky
4801  *              volume being considered for update.
4802  *
4803  * Returns:
4804  *      0 (always!)
4805  *
4806  * Environment:
4807  *      Nothing interesting.
4808  *
4809  * Side Effects:
4810  *      As described.
4811  *------------------------------------------------------------------------*/
4812
4813 int
4814 VAdjustVolumeStatistics_r(register Volume * vp)
4815 {
4816     unsigned int now = FT_ApproxTime();
4817
4818     if (now - V_dayUseDate(vp) > OneDay) {
4819         register int ndays, i;
4820
4821         ndays = (now - V_dayUseDate(vp)) / OneDay;
4822         for (i = 6; i > ndays - 1; i--)
4823             V_weekUse(vp)[i] = V_weekUse(vp)[i - ndays];
4824         for (i = 0; i < ndays - 1 && i < 7; i++)
4825             V_weekUse(vp)[i] = 0;
4826         if (ndays <= 7)
4827             V_weekUse(vp)[ndays - 1] = V_dayUse(vp);
4828         V_dayUse(vp) = 0;
4829         V_dayUseDate(vp) = Midnight(now);
4830
4831 #if OPENAFS_VOL_STATS
4832         /*
4833          * All we need to do is bzero the entire VOL_STATS_BYTES of
4834          * the detailed volume statistics area.
4835          */
4836         memset((char *)(V_stat_area(vp)), 0, VOL_STATS_BYTES);
4837 #endif /* OPENAFS_VOL_STATS */
4838     }
4839
4840     /*It's been more than a day of collection */
4841     /*
4842      * Always return happily.
4843      */
4844     return (0);
4845 }                               /*VAdjustVolumeStatistics */
4846
4847 int
4848 VAdjustVolumeStatistics(register Volume * vp)
4849 {
4850     int retVal;
4851     VOL_LOCK;
4852     retVal = VAdjustVolumeStatistics_r(vp);
4853     VOL_UNLOCK;
4854     return retVal;
4855 }
4856
4857 void
4858 VBumpVolumeUsage_r(register Volume * vp)
4859 {
4860     unsigned int now = FT_ApproxTime();
4861     if (now - V_dayUseDate(vp) > OneDay)
4862         VAdjustVolumeStatistics_r(vp);
4863     /*
4864      * Save the volume header image to disk after every 128 bumps to dayUse.
4865      */
4866     if ((V_dayUse(vp)++ & 127) == 0) {
4867         Error error;
4868         VUpdateVolume_r(&error, vp, VOL_UPDATE_WAIT);
4869     }
4870 }
4871
4872 void
4873 VBumpVolumeUsage(register Volume * vp)
4874 {
4875     VOL_LOCK;
4876     VBumpVolumeUsage_r(vp);
4877     VOL_UNLOCK;
4878 }
4879
4880 void
4881 VSetDiskUsage_r(void)
4882 {
4883 #ifndef AFS_DEMAND_ATTACH_FS
4884     static int FifteenMinuteCounter = 0;
4885 #endif
4886
4887     while (VInit < 2) {
4888         /* NOTE: Don't attempt to access the partitions list until the
4889          * initialization level indicates that all volumes are attached,
4890          * which implies that all partitions are initialized. */
4891 #ifdef AFS_PTHREAD_ENV
4892         sleep(10);
4893 #else /* AFS_PTHREAD_ENV */
4894         IOMGR_Sleep(10);
4895 #endif /* AFS_PTHREAD_ENV */
4896     }
4897
4898     VResetDiskUsage_r();
4899
4900 #ifndef AFS_DEMAND_ATTACH_FS
4901     if (++FifteenMinuteCounter == 3) {
4902         FifteenMinuteCounter = 0;
4903         VScanUpdateList();
4904     }
4905 #endif /* !AFS_DEMAND_ATTACH_FS */
4906 }
4907
4908 void
4909 VSetDiskUsage(void)
4910 {
4911     VOL_LOCK;
4912     VSetDiskUsage_r();
4913     VOL_UNLOCK;
4914 }
4915
4916
4917 /***************************************************/
4918 /* Volume Update List routines                     */
4919 /***************************************************/
4920
4921 /* The number of minutes that a volume hasn't been updated before the
4922  * "Dont salvage" flag in the volume header will be turned on */
4923 #define SALVAGE_INTERVAL        (10*60)
4924
4925 /*
4926  * demand attach fs
4927  *
4928  * volume update list functionality has been moved into the VLRU
4929  * the DONT_SALVAGE flag is now set during VLRU demotion
4930  */
4931
4932 #ifndef AFS_DEMAND_ATTACH_FS
4933 static VolumeId *UpdateList = NULL;     /* Pointer to array of Volume ID's */
4934 static int nUpdatedVolumes = 0;         /* Updated with entry in UpdateList, salvage after crash flag on */
4935 static int updateSize = 0;              /* number of entries possible */
4936 #define UPDATE_LIST_SIZE 128            /* initial size increment (must be a power of 2!) */
4937 #endif /* !AFS_DEMAND_ATTACH_FS */
4938
4939 void
4940 VAddToVolumeUpdateList_r(Error * ec, Volume * vp)
4941 {
4942     *ec = 0;
4943     vp->updateTime = FT_ApproxTime();
4944     if (V_dontSalvage(vp) == 0)
4945         return;
4946     V_dontSalvage(vp) = 0;
4947     VSyncVolume_r(ec, vp, 0);
4948 #ifdef AFS_DEMAND_ATTACH_FS
4949     V_attachFlags(vp) &= ~(VOL_HDR_DONTSALV);
4950 #else /* !AFS_DEMAND_ATTACH_FS */
4951     if (*ec)
4952         return;
4953     if (UpdateList == NULL) {
4954         updateSize = UPDATE_LIST_SIZE;
4955         UpdateList = (VolumeId *) malloc(sizeof(VolumeId) * updateSize);
4956     } else {
4957         if (nUpdatedVolumes == updateSize) {
4958             updateSize <<= 1;
4959             if (updateSize > 524288) {
4960                 Log("warning: there is likely a bug in the volume update scanner\n");
4961                 return;
4962             }
4963             UpdateList =
4964                 (VolumeId *) realloc(UpdateList,
4965                                      sizeof(VolumeId) * updateSize);
4966         }
4967     }
4968     assert(UpdateList != NULL);
4969     UpdateList[nUpdatedVolumes++] = V_id(vp);
4970 #endif /* !AFS_DEMAND_ATTACH_FS */
4971 }
4972
4973 #ifndef AFS_DEMAND_ATTACH_FS
4974 static void
4975 VScanUpdateList(void)
4976 {
4977     register int i, gap;
4978     register Volume *vp;
4979     Error error;
4980     afs_uint32 now = FT_ApproxTime();
4981     /* Be careful with this code, since it works with interleaved calls to AddToVolumeUpdateList */
4982     for (i = gap = 0; i < nUpdatedVolumes; i++) {
4983         if (gap)
4984             UpdateList[i - gap] = UpdateList[i];
4985
4986         /* XXX this routine needlessly messes up the Volume LRU by
4987          * breaking the LRU temporal-locality assumptions.....
4988          * we should use a special volume header allocator here */
4989         vp = VGetVolume_r(&error, UpdateList[i - gap] = UpdateList[i]);
4990         if (error) {
4991             gap++;
4992         } else if (vp->nUsers == 1 && now - vp->updateTime > SALVAGE_INTERVAL) {
4993             V_dontSalvage(vp) = DONT_SALVAGE;
4994             VUpdateVolume_r(&error, vp, 0);     /* No need to fsync--not critical */
4995             gap++;
4996         }
4997
4998         if (vp) {
4999             VPutVolume_r(vp);
5000         }
5001
5002 #ifndef AFS_PTHREAD_ENV
5003         IOMGR_Poll();
5004 #endif /* !AFS_PTHREAD_ENV */
5005     }
5006     nUpdatedVolumes -= gap;
5007 }
5008 #endif /* !AFS_DEMAND_ATTACH_FS */
5009
5010
5011 /***************************************************/
5012 /* Volume LRU routines                             */
5013 /***************************************************/
5014
5015 /* demand attach fs
5016  * volume LRU
5017  *
5018  * with demand attach fs, we attempt to soft detach(1)
5019  * volumes which have not been accessed in a long time
5020  * in order to speed up fileserver shutdown
5021  *
5022  * (1) by soft detach we mean a process very similar
5023  *     to VOffline, except the final state of the 
5024  *     Volume will be VOL_STATE_PREATTACHED, instead
5025  *     of the usual VOL_STATE_UNATTACHED
5026  */
5027 #ifdef AFS_DEMAND_ATTACH_FS
5028
5029 /* implementation is reminiscent of a generational GC
5030  *
5031  * queue 0 is newly attached volumes. this queue is
5032  * sorted by attach timestamp
5033  *
5034  * queue 1 is volumes that have been around a bit
5035  * longer than queue 0. this queue is sorted by
5036  * attach timestamp
5037  *
5038  * queue 2 is volumes tha have been around the longest.
5039  * this queue is unsorted
5040  *
5041  * queue 3 is volumes that have been marked as
5042  * candidates for soft detachment. this queue is
5043  * unsorted
5044  */
5045 #define VLRU_GENERATIONS  3   /**< number of generations in VLRU */
5046 #define VLRU_QUEUES       5   /**< total number of VLRU queues */
5047
5048 /**
5049  * definition of a VLRU queue.
5050  */
5051 struct VLRU_q {
5052     volatile struct rx_queue q;
5053     volatile int len;
5054     volatile int busy;
5055     pthread_cond_t cv;
5056 };
5057
5058 /**
5059  * main VLRU data structure.
5060  */
5061 struct VLRU {
5062     struct VLRU_q q[VLRU_QUEUES];   /**< VLRU queues */
5063
5064     /* VLRU config */
5065     /** time interval (in seconds) between promotion passes for
5066      *  each young generation queue. */
5067     afs_uint32 promotion_interval[VLRU_GENERATIONS-1];
5068
5069     /** time interval (in seconds) between soft detach candidate
5070      *  scans for each generation queue.
5071      *
5072      *  scan_interval[VLRU_QUEUE_CANDIDATE] defines how frequently
5073      *  we perform a soft detach pass. */
5074     afs_uint32 scan_interval[VLRU_GENERATIONS+1];
5075
5076     /* scheduler state */
5077     int next_idx;                                       /**< next queue to receive attention */
5078     afs_uint32 last_promotion[VLRU_GENERATIONS-1];      /**< timestamp of last promotion scan */
5079     afs_uint32 last_scan[VLRU_GENERATIONS+1];           /**< timestamp of last detach scan */
5080
5081     int scanner_state;                                  /**< state of scanner thread */
5082     pthread_cond_t cv;                                  /**< state transition CV */
5083 };
5084
5085 /** global VLRU state */
5086 static struct VLRU volume_LRU;
5087
5088 /**
5089  * defined states for VLRU scanner thread.
5090  */
5091 typedef enum {
5092     VLRU_SCANNER_STATE_OFFLINE        = 0,    /**< vlru scanner thread is offline */
5093     VLRU_SCANNER_STATE_ONLINE         = 1,    /**< vlru scanner thread is online */
5094     VLRU_SCANNER_STATE_SHUTTING_DOWN  = 2,    /**< vlru scanner thread is shutting down */
5095     VLRU_SCANNER_STATE_PAUSING        = 3,    /**< vlru scanner thread is getting ready to pause */
5096     VLRU_SCANNER_STATE_PAUSED         = 4     /**< vlru scanner thread is paused */
5097 } vlru_thread_state_t;
5098
5099 /* vlru disk data header stuff */
5100 #define VLRU_DISK_MAGIC      0x7a8b9cad        /**< vlru disk entry magic number */
5101 #define VLRU_DISK_VERSION    1                 /**< vlru disk entry version number */
5102
5103 /** vlru default expiration time (for eventual fs state serialization of vlru data) */
5104 #define VLRU_DUMP_EXPIRATION_TIME   (60*60*24*7)  /* expire vlru data after 1 week */
5105
5106
5107 /** minimum volume inactivity (in seconds) before a volume becomes eligible for
5108  *  soft detachment. */
5109 static afs_uint32 VLRU_offline_thresh = VLRU_DEFAULT_OFFLINE_THRESH;
5110
5111 /** time interval (in seconds) between VLRU scanner thread soft detach passes. */
5112 static afs_uint32 VLRU_offline_interval = VLRU_DEFAULT_OFFLINE_INTERVAL;
5113
5114 /** maximum number of volumes to soft detach in a VLRU soft detach pass. */
5115 static afs_uint32 VLRU_offline_max = VLRU_DEFAULT_OFFLINE_MAX;
5116
5117 /** VLRU control flag.  non-zero value implies VLRU subsystem is activated. */
5118 static afs_uint32 VLRU_enabled = 1;
5119
5120 /* queue synchronization routines */
5121 static void VLRU_BeginExclusive_r(struct VLRU_q * q);
5122 static void VLRU_EndExclusive_r(struct VLRU_q * q);
5123 static void VLRU_Wait_r(struct VLRU_q * q);
5124
5125 /**
5126  * set VLRU subsystem tunable parameters.
5127  *
5128  * @param[in] option  tunable option to modify
5129  * @param[in] val     new value for tunable parameter
5130  *
5131  * @pre @c VInitVolumePackage has not yet been called.
5132  *
5133  * @post tunable parameter is modified
5134  *
5135  * @note DAFS only
5136  *
5137  * @note valid option parameters are:
5138  *    @arg @c VLRU_SET_THRESH 
5139  *         set the period of inactivity after which
5140  *         volumes are eligible for soft detachment
5141  *    @arg @c VLRU_SET_INTERVAL 
5142  *         set the time interval between calls
5143  *         to the volume LRU "garbage collector"
5144  *    @arg @c VLRU_SET_MAX 
5145  *         set the max number of volumes to deallocate
5146  *         in one GC pass
5147  */
5148 void
5149 VLRU_SetOptions(int option, afs_uint32 val)
5150 {
5151     if (option == VLRU_SET_THRESH) {
5152         VLRU_offline_thresh = val;
5153     } else if (option == VLRU_SET_INTERVAL) {
5154         VLRU_offline_interval = val;
5155     } else if (option == VLRU_SET_MAX) {
5156         VLRU_offline_max = val;
5157     } else if (option == VLRU_SET_ENABLED) {
5158         VLRU_enabled = val;
5159     }
5160     VLRU_ComputeConstants();
5161 }
5162
5163 /**
5164  * compute VLRU internal timing parameters.
5165  *
5166  * @post VLRU scanner thread internal timing parameters are computed
5167  *
5168  * @note computes internal timing parameters based upon user-modifiable 
5169  *       tunable parameters.
5170  *
5171  * @note DAFS only
5172  *
5173  * @internal volume package internal use only.
5174  */
5175 static void
5176 VLRU_ComputeConstants(void)
5177 {
5178     afs_uint32 factor = VLRU_offline_thresh / VLRU_offline_interval;
5179
5180     /* compute the candidate scan interval */
5181     volume_LRU.scan_interval[VLRU_QUEUE_CANDIDATE] = VLRU_offline_interval;
5182
5183     /* compute the promotion intervals */
5184     volume_LRU.promotion_interval[VLRU_QUEUE_NEW] = VLRU_offline_thresh * 2;
5185     volume_LRU.promotion_interval[VLRU_QUEUE_MID] = VLRU_offline_thresh * 4;
5186
5187     if (factor > 16) {
5188         /* compute the gen 0 scan interval */
5189         volume_LRU.scan_interval[VLRU_QUEUE_NEW] = VLRU_offline_thresh / 8;
5190     } else {
5191         /* compute the gen 0 scan interval */
5192         volume_LRU.scan_interval[VLRU_QUEUE_NEW] = VLRU_offline_interval * 2;
5193     }
5194 }
5195
5196 /**
5197  * initialize VLRU subsystem.
5198  *
5199  * @pre this function has not yet been called
5200  *
5201  * @post VLRU subsystem is initialized and VLRU scanner thread is starting
5202  *
5203  * @note DAFS only
5204  *
5205  * @internal volume package internal use only.
5206  */
5207 static void
5208 VInitVLRU(void)
5209 {
5210     pthread_t tid;
5211     pthread_attr_t attrs;
5212     int i;
5213
5214     if (!VLRU_enabled) {
5215         Log("VLRU: disabled\n");
5216         return;
5217     }
5218
5219     /* initialize each of the VLRU queues */
5220     for (i = 0; i < VLRU_QUEUES; i++) {
5221         queue_Init(&volume_LRU.q[i]);
5222         volume_LRU.q[i].len = 0;
5223         volume_LRU.q[i].busy = 0;
5224         assert(pthread_cond_init(&volume_LRU.q[i].cv, NULL) == 0);
5225     }
5226
5227     /* setup the timing constants */
5228     VLRU_ComputeConstants();
5229
5230     /* XXX put inside LogLevel check? */
5231     Log("VLRU: starting scanner with the following configuration parameters:\n");
5232     Log("VLRU:  offlining volumes after minimum of %d seconds of inactivity\n", VLRU_offline_thresh);
5233     Log("VLRU:  running VLRU soft detach pass every %d seconds\n", VLRU_offline_interval);
5234     Log("VLRU:  taking up to %d volumes offline per pass\n", VLRU_offline_max);
5235     Log("VLRU:  scanning generation 0 for inactive volumes every %d seconds\n", volume_LRU.scan_interval[0]);
5236     Log("VLRU:  scanning for promotion/demotion between generations 0 and 1 every %d seconds\n", volume_LRU.promotion_interval[0]);
5237     Log("VLRU:  scanning for promotion/demotion between generations 1 and 2 every %d seconds\n", volume_LRU.promotion_interval[1]);
5238
5239     /* start up the VLRU scanner */
5240     volume_LRU.scanner_state = VLRU_SCANNER_STATE_OFFLINE;
5241     if (programType == fileServer) {
5242         assert(pthread_cond_init(&volume_LRU.cv, NULL) == 0);
5243         assert(pthread_attr_init(&attrs) == 0);
5244         assert(pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED) == 0);
5245         assert(pthread_create(&tid, &attrs, &VLRU_ScannerThread, NULL) == 0);
5246     }
5247 }
5248
5249 /**
5250  * initialize the VLRU-related fields of a newly allocated volume object.
5251  *
5252  * @param[in] vp  pointer to volume object
5253  *
5254  * @pre
5255  *    @arg @c VOL_LOCK is held.
5256  *    @arg volume object is not on a VLRU queue.
5257  *
5258  * @post VLRU fields are initialized to indicate that volume object is not
5259  *       currently registered with the VLRU subsystem
5260  *
5261  * @note DAFS only
5262  *
5263  * @internal volume package interal use only.
5264  */
5265 static void
5266 VLRU_Init_Node_r(volatile Volume * vp)
5267 {
5268     if (!VLRU_enabled)
5269         return;
5270
5271     assert(queue_IsNotOnQueue(&vp->vlru));
5272     vp->vlru.idx = VLRU_QUEUE_INVALID;
5273 }
5274
5275 /**
5276  * add a volume object to a VLRU queue.
5277  *
5278  * @param[in] vp  pointer to volume object
5279  *
5280  * @pre
5281  *    @arg @c VOL_LOCK is held.
5282  *    @arg caller MUST hold a lightweight ref on @p vp.
5283  *    @arg caller MUST NOT hold exclusive ownership of the VLRU queue.
5284  *
5285  * @post the volume object is added to the appropriate VLRU queue
5286  *
5287  * @note if @c vp->vlru.idx contains the index of a valid VLRU queue,
5288  *       then the volume is added to that queue.  Otherwise, the value
5289  *       @c VLRU_QUEUE_NEW is stored into @c vp->vlru.idx and the
5290  *       volume is added to the NEW generation queue.
5291  *
5292  * @note @c VOL_LOCK may be dropped internally
5293  *
5294  * @note Volume state is temporarily set to @c VOL_STATE_VLRU_ADD
5295  *       during the add operation, and is restored to the previous
5296  *       state prior to return.
5297  *
5298  * @note DAFS only
5299  *
5300  * @internal volume package internal use only.
5301  */
5302 static void
5303 VLRU_Add_r(volatile Volume * vp)
5304 {
5305     int idx;
5306     VolState state_save;
5307
5308     if (!VLRU_enabled)
5309         return;
5310
5311     if (queue_IsOnQueue(&vp->vlru))
5312         return;
5313
5314     state_save = VChangeState_r(vp, VOL_STATE_VLRU_ADD);
5315
5316     idx = vp->vlru.idx;
5317     if ((idx < 0) || (idx >= VLRU_QUEUE_INVALID)) {
5318         idx = VLRU_QUEUE_NEW;
5319     }
5320
5321     VLRU_Wait_r(&volume_LRU.q[idx]);
5322
5323     /* repeat check since VLRU_Wait_r may have dropped
5324      * the glock */
5325     if (queue_IsNotOnQueue(&vp->vlru)) {
5326         vp->vlru.idx = idx;
5327         queue_Prepend(&volume_LRU.q[idx], &vp->vlru);
5328         volume_LRU.q[idx].len++;
5329         V_attachFlags(vp) |= VOL_ON_VLRU;
5330         vp->stats.last_promote = FT_ApproxTime();
5331     }
5332
5333     VChangeState_r(vp, state_save);
5334 }
5335
5336 /**
5337  * delete a volume object from a VLRU queue.
5338  *
5339  * @param[in] vp  pointer to volume object
5340  *
5341  * @pre
5342  *    @arg @c VOL_LOCK is held.
5343  *    @arg caller MUST hold a lightweight ref on @p vp.
5344  *    @arg caller MUST NOT hold exclusive ownership of the VLRU queue.
5345  *
5346  * @post volume object is removed from the VLRU queue
5347  *
5348  * @note @c VOL_LOCK may be dropped internally
5349  *
5350  * @note DAFS only
5351  *
5352  * @todo We should probably set volume state to something exlcusive 
5353  *       (as @c VLRU_Add_r does) prior to dropping @c VOL_LOCK.
5354  *
5355  * @internal volume package internal use only.
5356  */
5357 static void
5358 VLRU_Delete_r(volatile Volume * vp)
5359 {
5360     int idx;
5361
5362     if (!VLRU_enabled)
5363         return;
5364
5365     if (queue_IsNotOnQueue(&vp->vlru))
5366         return;
5367
5368     /* handle races */
5369     do {
5370       idx = vp->vlru.idx;
5371       if (idx == VLRU_QUEUE_INVALID)
5372           return;
5373       VLRU_Wait_r(&volume_LRU.q[idx]);
5374     } while (idx != vp->vlru.idx);
5375
5376     /* now remove from the VLRU and update 
5377      * the appropriate counter */
5378     queue_Remove(&vp->vlru);
5379     volume_LRU.q[idx].len--;
5380     vp->vlru.idx = VLRU_QUEUE_INVALID;
5381     V_attachFlags(vp) &= ~(VOL_ON_VLRU);
5382 }
5383
5384 /**
5385  * tell the VLRU subsystem that a volume was just accessed.
5386  *
5387  * @param[in] vp  pointer to volume object
5388  *
5389  * @pre
5390  *    @arg @c VOL_LOCK is held
5391  *    @arg caller MUST hold a lightweight ref on @p vp
5392  *    @arg caller MUST NOT hold exclusive ownership of any VLRU queue
5393  *
5394  * @post volume VLRU access statistics are updated.  If the volume was on
5395  *       the VLRU soft detach candidate queue, it is moved to the NEW
5396  *       generation queue.
5397  *
5398  * @note @c VOL_LOCK may be dropped internally
5399  *
5400  * @note DAFS only
5401  *
5402  * @internal volume package internal use only.
5403  */
5404 static void
5405 VLRU_UpdateAccess_r(volatile Volume * vp)
5406 {
5407     afs_uint32 live_interval;
5408     Volume * rvp = NULL;
5409
5410     if (!VLRU_enabled)
5411         return;
5412
5413     if (queue_IsNotOnQueue(&vp->vlru))
5414         return;
5415
5416     assert(V_attachFlags(vp) & VOL_ON_VLRU);
5417
5418     /* update the access timestamp */
5419     vp->stats.last_get = FT_ApproxTime();
5420
5421     /*
5422      * if the volume is on the soft detach candidate
5423      * list, we need to safely move it back to a
5424      * regular generation.  this has to be done
5425      * carefully so we don't race against the scanner
5426      * thread.
5427      */
5428
5429     /* if this volume is on the soft detach candidate queue,
5430      * then grab exclusive access to the necessary queues */
5431     if (vp->vlru.idx == VLRU_QUEUE_CANDIDATE) {
5432         rvp = vp;
5433         VCreateReservation_r(rvp);
5434
5435         VLRU_Wait_r(&volume_LRU.q[VLRU_QUEUE_NEW]);
5436         VLRU_BeginExclusive_r(&volume_LRU.q[VLRU_QUEUE_NEW]);
5437         VLRU_Wait_r(&volume_LRU.q[VLRU_QUEUE_CANDIDATE]);
5438         VLRU_BeginExclusive_r(&volume_LRU.q[VLRU_QUEUE_CANDIDATE]);
5439     }
5440
5441     /* make sure multiple threads don't race to update */
5442     if (vp->vlru.idx == VLRU_QUEUE_CANDIDATE) {
5443         VLRU_SwitchQueues(vp, VLRU_QUEUE_NEW, 1);
5444     }
5445
5446     if (rvp) {
5447       VLRU_EndExclusive_r(&volume_LRU.q[VLRU_QUEUE_CANDIDATE]);
5448       VLRU_EndExclusive_r(&volume_LRU.q[VLRU_QUEUE_NEW]);
5449       VCancelReservation_r(rvp);
5450     }
5451 }
5452
5453 /**
5454  * switch a volume between two VLRU queues.
5455  *
5456  * @param[in] vp       pointer to volume object
5457  * @param[in] new_idx  index of VLRU queue onto which the volume will be moved
5458  * @param[in] append   controls whether the volume will be appended or 
5459  *                     prepended to the queue.  A nonzero value means it will
5460  *                     be appended; zero means it will be prepended.
5461  *
5462  * @pre The new (and old, if applicable) queue(s) must either be owned 
5463  *      exclusively by the calling thread for asynchronous manipulation,
5464  *      or the queue(s) must be quiescent and VOL_LOCK must be held.
5465  *      Please see VLRU_BeginExclusive_r, VLRU_EndExclusive_r and VLRU_Wait_r
5466  *      for further details of the queue asynchronous processing mechanism.
5467  *
5468  * @post If the volume object was already on a VLRU queue, it is
5469  *       removed from the queue.  Depending on the value of the append
5470  *       parameter, the volume object is either appended or prepended
5471  *       to the VLRU queue referenced by the new_idx parameter.
5472  *
5473  * @note DAFS only
5474  *
5475  * @see VLRU_BeginExclusive_r
5476  * @see VLRU_EndExclusive_r
5477  * @see VLRU_Wait_r
5478  *
5479  * @internal volume package internal use only.
5480  */
5481 static void
5482 VLRU_SwitchQueues(volatile Volume * vp, int new_idx, int append)
5483 {
5484     if (queue_IsNotOnQueue(&vp->vlru))
5485         return;
5486
5487     queue_Remove(&vp->vlru);
5488     volume_LRU.q[vp->vlru.idx].len--;
5489     
5490     /* put the volume back on the correct generational queue */
5491     if (append) {
5492         queue_Append(&volume_LRU.q[new_idx], &vp->vlru);
5493     } else {
5494         queue_Prepend(&volume_LRU.q[new_idx], &vp->vlru);
5495     }
5496
5497     volume_LRU.q[new_idx].len++;
5498     vp->vlru.idx = new_idx;
5499 }
5500
5501 /**
5502  * VLRU background thread.
5503  *
5504  * The VLRU Scanner Thread is responsible for periodically scanning through
5505  * each VLRU queue looking for volumes which should be moved to another
5506  * queue, or soft detached.
5507  *
5508  * @param[in] args  unused thread arguments parameter
5509  *
5510  * @return unused thread return value
5511  *    @retval NULL always
5512  *
5513  * @internal volume package internal use only.
5514  */
5515 static void *
5516 VLRU_ScannerThread(void * args)
5517 {
5518     afs_uint32 now, min_delay, delay;
5519     afs_uint32 next_scan[VLRU_GENERATIONS];
5520     afs_uint32 next_promotion[VLRU_GENERATIONS];
5521     int i, min_idx, min_op, overdue, state;
5522
5523     /* set t=0 for promotion cycle to be 
5524      * fileserver startup */
5525     now = FT_ApproxTime();
5526     for (i=0; i < VLRU_GENERATIONS-1; i++) {
5527         volume_LRU.last_promotion[i] = now;
5528     }
5529
5530     /* don't start the scanner until VLRU_offline_thresh
5531      * plus a small delay for VInitVolumePackage to finish
5532      * has gone by */
5533
5534     sleep(VLRU_offline_thresh + 60);
5535
5536     /* set t=0 for scan cycle to be now */
5537     now = FT_ApproxTime();
5538     for (i=0; i < VLRU_GENERATIONS+1; i++) {
5539         volume_LRU.last_scan[i] = now;
5540     }
5541
5542     VOL_LOCK;
5543     if (volume_LRU.scanner_state == VLRU_SCANNER_STATE_OFFLINE) {
5544         volume_LRU.scanner_state = VLRU_SCANNER_STATE_ONLINE;
5545     }
5546
5547     while ((state = volume_LRU.scanner_state) != VLRU_SCANNER_STATE_SHUTTING_DOWN) {
5548         /* check to see if we've been asked to pause */
5549         if (volume_LRU.scanner_state == VLRU_SCANNER_STATE_PAUSING) {
5550             volume_LRU.scanner_state = VLRU_SCANNER_STATE_PAUSED;
5551             assert(pthread_cond_broadcast(&volume_LRU.cv) == 0);
5552             do {
5553                 VOL_CV_WAIT(&volume_LRU.cv);
5554             } while (volume_LRU.scanner_state == VLRU_SCANNER_STATE_PAUSED);
5555         }
5556
5557         /* scheduling can happen outside the glock */
5558         VOL_UNLOCK;
5559
5560         /* figure out what is next on the schedule */
5561
5562         /* figure out a potential schedule for the new generation first */
5563         overdue = 0;
5564         min_delay = volume_LRU.scan_interval[0] + volume_LRU.last_scan[0] - now;
5565         min_idx = 0;
5566         min_op = 0;
5567         if (min_delay > volume_LRU.scan_interval[0]) {
5568             /* unsigned overflow -- we're overdue to run this scan */
5569             min_delay = 0;
5570             overdue = 1;
5571         }
5572
5573         /* if we're not overdue for gen 0, figure out schedule for candidate gen */
5574         if (!overdue) {
5575             i = VLRU_QUEUE_CANDIDATE;
5576             delay = volume_LRU.scan_interval[i] + volume_LRU.last_scan[i] - now;
5577             if (delay < min_delay) {
5578                 min_delay = delay;
5579                 min_idx = i;
5580             }
5581             if (delay > volume_LRU.scan_interval[i]) {
5582                 /* unsigned overflow -- we're overdue to run this scan */
5583                 min_delay = 0;
5584                 min_idx = i;
5585                 overdue = 1;
5586                 break;
5587             }
5588         }
5589
5590         /* if we're still not overdue for something, figure out schedules for promotions */
5591         for (i=0; !overdue && i < VLRU_GENERATIONS-1; i++) {
5592             delay = volume_LRU.promotion_interval[i] + volume_LRU.last_promotion[i] - now;
5593             if (delay < min_delay) {
5594                 min_delay = delay;
5595                 min_idx = i;
5596                 min_op = 1;
5597             }
5598             if (delay > volume_LRU.promotion_interval[i]) {
5599                 /* unsigned overflow -- we're overdue to run this promotion */
5600                 min_delay = 0;
5601                 min_idx = i;
5602                 min_op = 1;
5603                 overdue = 1;
5604                 break;
5605             }
5606         }
5607
5608         /* sleep as needed */
5609         if (min_delay) {
5610             sleep(min_delay);
5611         }
5612
5613         /* do whatever is next */
5614         VOL_LOCK;
5615         if (min_op) {
5616             VLRU_Promote_r(min_idx);
5617             VLRU_Demote_r(min_idx+1);
5618         } else {
5619             VLRU_Scan_r(min_idx);
5620         }
5621         now = FT_ApproxTime();
5622     }
5623
5624     Log("VLRU scanner asked to go offline (scanner_state=%d)\n", state);
5625
5626     /* signal that scanner is down */
5627     volume_LRU.scanner_state = VLRU_SCANNER_STATE_OFFLINE;
5628     assert(pthread_cond_broadcast(&volume_LRU.cv) == 0);
5629     VOL_UNLOCK;
5630     return NULL;
5631 }
5632
5633 /**
5634  * promote volumes from one VLRU generation to the next.
5635  *
5636  * This routine scans a VLRU generation looking for volumes which are
5637  * eligible to be promoted to the next generation.  All volumes which
5638  * meet the eligibility requirement are promoted.
5639  *
5640  * Promotion eligibility is based upon meeting both of the following
5641  * requirements:
5642  *
5643  *    @arg The volume has been accessed since the last promotion:
5644  *         @c (vp->stats.last_get >= vp->stats.last_promote)
5645  *    @arg The last promotion occurred at least 
5646  *         @c volume_LRU.promotion_interval[idx] seconds ago
5647  *
5648  * As a performance optimization, promotions are "globbed".  In other
5649  * words, we promote arbitrarily large contiguous sublists of elements
5650  * as one operation.  
5651  *
5652  * @param[in] idx  VLRU queue index to scan
5653  *
5654  * @note DAFS only
5655  *
5656  * @internal VLRU internal use only.
5657  */
5658 static void
5659 VLRU_Promote_r(int idx)
5660 {
5661     int len, chaining, promote;
5662     afs_uint32 now, thresh;
5663     struct rx_queue *qp, *nqp;
5664     Volume * vp, *start, *end;
5665
5666     /* get exclusive access to two chains, and drop the glock */
5667     VLRU_Wait_r(&volume_LRU.q[idx]);
5668     VLRU_BeginExclusive_r(&volume_LRU.q[idx]);
5669     VLRU_Wait_r(&volume_LRU.q[idx+1]);
5670     VLRU_BeginExclusive_r(&volume_LRU.q[idx+1]);
5671     VOL_UNLOCK;
5672
5673     thresh = volume_LRU.promotion_interval[idx];
5674     now = FT_ApproxTime();
5675
5676     len = chaining = 0;
5677     for (queue_ScanBackwards(&volume_LRU.q[idx], qp, nqp, rx_queue)) {
5678         vp = (Volume *)((char *)qp - offsetof(Volume, vlru));
5679         promote = (((vp->stats.last_promote + thresh) <= now) &&
5680                    (vp->stats.last_get >= vp->stats.last_promote));
5681
5682         if (chaining) {
5683             if (promote) {
5684                 vp->vlru.idx++;
5685                 len++;
5686                 start = vp;
5687             } else {
5688                 /* promote and prepend chain */
5689                 queue_MoveChainAfter(&volume_LRU.q[idx+1], &start->vlru, &end->vlru);
5690                 chaining = 0;
5691             }
5692         } else {
5693             if (promote) {
5694                 vp->vlru.idx++;
5695                 len++;
5696                 chaining = 1;
5697                 start = end = vp;
5698             }
5699         }
5700     }
5701
5702     if (chaining) {
5703         /* promote and prepend */
5704         queue_MoveChainAfter(&volume_LRU.q[idx+1], &start->vlru, &end->vlru);
5705     }
5706
5707     if (len) {
5708         volume_LRU.q[idx].len -= len;
5709         volume_LRU.q[idx+1].len += len;
5710     }
5711
5712     /* release exclusive access to the two chains */
5713     VOL_LOCK;
5714     volume_LRU.last_promotion[idx] = now;
5715     VLRU_EndExclusive_r(&volume_LRU.q[idx+1]);
5716     VLRU_EndExclusive_r(&volume_LRU.q[idx]);
5717 }
5718
5719 /* run the demotions */
5720 static void
5721 VLRU_Demote_r(int idx)
5722 {
5723     Error ec;
5724     int len, chaining, demote;
5725     afs_uint32 now, thresh;
5726     struct rx_queue *qp, *nqp;
5727     Volume * vp, *start, *end;
5728     Volume ** salv_flag_vec = NULL;
5729     int salv_vec_offset = 0;
5730
5731     assert(idx == VLRU_QUEUE_MID || idx == VLRU_QUEUE_OLD);
5732
5733     /* get exclusive access to two chains, and drop the glock */
5734     VLRU_Wait_r(&volume_LRU.q[idx-1]);
5735     VLRU_BeginExclusive_r(&volume_LRU.q[idx-1]);
5736     VLRU_Wait_r(&volume_LRU.q[idx]);
5737     VLRU_BeginExclusive_r(&volume_LRU.q[idx]);
5738     VOL_UNLOCK;
5739
5740     /* no big deal if this allocation fails */
5741     if (volume_LRU.q[idx].len) {
5742         salv_flag_vec = (Volume **) malloc(volume_LRU.q[idx].len * sizeof(Volume *));
5743     }
5744
5745     now = FT_ApproxTime();
5746     thresh = volume_LRU.promotion_interval[idx-1];
5747
5748     len = chaining = 0;
5749     for (queue_ScanBackwards(&volume_LRU.q[idx], qp, nqp, rx_queue)) {
5750         vp = (Volume *)((char *)qp - offsetof(Volume, vlru));
5751         demote = (((vp->stats.last_promote + thresh) <= now) &&
5752                   (vp->stats.last_get < (now - thresh)));
5753
5754         /* we now do volume update list DONT_SALVAGE flag setting during
5755          * demotion passes */
5756         if (salv_flag_vec &&
5757             !(V_attachFlags(vp) & VOL_HDR_DONTSALV) &&
5758             demote && 
5759             (vp->updateTime < (now - SALVAGE_INTERVAL)) &&
5760             (V_attachState(vp) == VOL_STATE_ATTACHED)) {
5761             salv_flag_vec[salv_vec_offset++] = vp;
5762             VCreateReservation_r(vp);
5763         }
5764
5765         if (chaining) {
5766             if (demote) {
5767                 vp->vlru.idx--;
5768                 len++;
5769                 start = vp;
5770             } else {
5771                 /* demote and append chain */
5772                 queue_MoveChainBefore(&volume_LRU.q[idx-1], &start->vlru, &end->vlru);
5773                 chaining = 0;
5774             }
5775         } else {
5776             if (demote) {
5777                 vp->vlru.idx--;
5778                 len++;
5779                 chaining = 1;
5780                 start = end = vp;
5781             }
5782         }
5783     }
5784
5785     if (chaining) {
5786         queue_MoveChainBefore(&volume_LRU.q[idx-1], &start->vlru, &end->vlru);
5787     }
5788
5789     if (len) {
5790         volume_LRU.q[idx].len -= len;
5791         volume_LRU.q[idx-1].len += len;
5792     }
5793
5794     /* release exclusive access to the two chains */
5795     VOL_LOCK;
5796     VLRU_EndExclusive_r(&volume_LRU.q[idx]);
5797     VLRU_EndExclusive_r(&volume_LRU.q[idx-1]);
5798
5799     /* now go back and set the DONT_SALVAGE flags as appropriate */
5800     if (salv_flag_vec) {
5801         int i;
5802         for (i = 0; i < salv_vec_offset; i++) {
5803             vp = salv_flag_vec[i];
5804             if (!(V_attachFlags(vp) & VOL_HDR_DONTSALV) &&
5805                 (vp->updateTime < (now - SALVAGE_INTERVAL)) &&
5806                 (V_attachState(vp) == VOL_STATE_ATTACHED)) {
5807                 ec = VHold_r(vp);
5808                 if (!ec) {
5809                     V_attachFlags(vp) |= VOL_HDR_DONTSALV;
5810                     V_dontSalvage(vp) = DONT_SALVAGE;
5811                     VUpdateVolume_r(&ec, vp, 0);
5812                     VPutVolume_r(vp);
5813                 }
5814             }
5815             VCancelReservation_r(vp);
5816         }
5817         free(salv_flag_vec);
5818     }
5819 }
5820
5821 /* run a pass of the VLRU GC scanner */
5822 static void
5823 VLRU_Scan_r(int idx)
5824 {
5825     afs_uint32 now, thresh;
5826     struct rx_queue *qp, *nqp;
5827     volatile Volume * vp;
5828     int i, locked = 1;
5829
5830     assert(idx == VLRU_QUEUE_NEW || idx == VLRU_QUEUE_CANDIDATE);
5831
5832     /* gain exclusive access to the idx VLRU */
5833     VLRU_Wait_r(&volume_LRU.q[idx]);
5834     VLRU_BeginExclusive_r(&volume_LRU.q[idx]);
5835
5836     if (idx != VLRU_QUEUE_CANDIDATE) {
5837         /* gain exclusive access to the candidate VLRU */
5838         VLRU_Wait_r(&volume_LRU.q[VLRU_QUEUE_CANDIDATE]);
5839         VLRU_BeginExclusive_r(&volume_LRU.q[VLRU_QUEUE_CANDIDATE]);
5840     }
5841
5842     now = FT_ApproxTime();
5843     thresh = now - VLRU_offline_thresh;
5844
5845     /* perform candidate selection and soft detaching */
5846     if (idx == VLRU_QUEUE_CANDIDATE) {
5847         /* soft detach some volumes from the candidate pool */
5848         VOL_UNLOCK;
5849         locked = 0;
5850
5851         for (i=0,queue_ScanBackwards(&volume_LRU.q[idx], qp, nqp, rx_queue)) {
5852             vp = (Volume *)((char *)qp - offsetof(Volume, vlru));
5853             if (i >= VLRU_offline_max) {
5854                 break;
5855             }
5856             /* check timestamp to see if it's a candidate for soft detaching */
5857             if (vp->stats.last_get <= thresh) {
5858                 VOL_LOCK;
5859                 if (VCheckSoftDetach(vp, thresh))
5860                     i++;
5861                 VOL_UNLOCK;
5862             }
5863         }
5864     } else {
5865         /* scan for volumes to become soft detach candidates */
5866         for (i=1,queue_ScanBackwards(&volume_LRU.q[idx], qp, nqp, rx_queue),i++) {
5867             vp = (Volume *)((char *)qp - offsetof(Volume, vlru));
5868
5869             /* check timestamp to see if it's a candidate for soft detaching */
5870             if (vp->stats.last_get <= thresh) {
5871                 VCheckSoftDetachCandidate(vp, thresh);
5872             }
5873
5874             if (!(i&0x7f)) {   /* lock coarsening optimization */
5875                 VOL_UNLOCK;
5876                 pthread_yield();
5877                 VOL_LOCK;
5878             }
5879         }
5880     }
5881
5882     /* relinquish exclusive access to the VLRU chains */
5883     if (!locked) {
5884         VOL_LOCK;
5885     }
5886     volume_LRU.last_scan[idx] = now;
5887     if (idx != VLRU_QUEUE_CANDIDATE) {
5888         VLRU_EndExclusive_r(&volume_LRU.q[VLRU_QUEUE_CANDIDATE]);
5889     }
5890     VLRU_EndExclusive_r(&volume_LRU.q[idx]);
5891 }
5892
5893 /* check whether volume is safe to soft detach
5894  * caller MUST NOT hold a ref count on vp */
5895 static int
5896 VCheckSoftDetach(volatile Volume * vp, afs_uint32 thresh)
5897 {
5898     int ret=0;
5899
5900     if (vp->nUsers || vp->nWaiters)
5901         return 0;
5902
5903     if (vp->stats.last_get <= thresh) {
5904         ret = VSoftDetachVolume_r(vp, thresh);
5905     }
5906
5907     return ret;
5908 }
5909
5910 /* check whether volume should be made a 
5911  * soft detach candidate */
5912 static int
5913 VCheckSoftDetachCandidate(volatile Volume * vp, afs_uint32 thresh)
5914 {
5915     int idx, ret = 0;
5916     if (vp->nUsers || vp->nWaiters)
5917         return 0;
5918
5919     idx = vp->vlru.idx;
5920
5921     assert(idx == VLRU_QUEUE_NEW);
5922
5923     if (vp->stats.last_get <= thresh) {
5924         /* move to candidate pool */
5925         queue_Remove(&vp->vlru);
5926         volume_LRU.q[VLRU_QUEUE_NEW].len--;
5927         queue_Prepend(&volume_LRU.q[VLRU_QUEUE_CANDIDATE], &vp->vlru);
5928         vp->vlru.idx = VLRU_QUEUE_CANDIDATE;
5929         volume_LRU.q[VLRU_QUEUE_CANDIDATE].len++;
5930         ret = 1;
5931     }
5932
5933     return ret;
5934 }
5935
5936
5937 /* begin exclusive access on VLRU */
5938 static void
5939 VLRU_BeginExclusive_r(struct VLRU_q * q)
5940 {
5941     assert(q->busy == 0);
5942     q->busy = 1;
5943 }
5944
5945 /* end exclusive access on VLRU */
5946 static void
5947 VLRU_EndExclusive_r(struct VLRU_q * q)
5948 {
5949     assert(q->busy);
5950     q->busy = 0;
5951     assert(pthread_cond_broadcast(&q->cv) == 0);
5952 }
5953
5954 /* wait for another thread to end exclusive access on VLRU */
5955 static void
5956 VLRU_Wait_r(struct VLRU_q * q)
5957 {
5958     while(q->busy) {
5959         VOL_CV_WAIT(&q->cv);
5960     }
5961 }
5962
5963 /* demand attach fs
5964  * volume soft detach
5965  *
5966  * caller MUST NOT hold a ref count on vp */
5967 static int
5968 VSoftDetachVolume_r(volatile Volume * vp, afs_uint32 thresh)
5969 {
5970     afs_uint32 ts_save;
5971     int ret = 0;
5972
5973     assert(vp->vlru.idx == VLRU_QUEUE_CANDIDATE);
5974
5975     ts_save = vp->stats.last_get;
5976     if (ts_save > thresh)
5977         return 0;
5978
5979     if (vp->nUsers || vp->nWaiters)
5980         return 0;
5981
5982     if (VIsExclusiveState(V_attachState(vp))) {
5983         return 0;
5984     }
5985
5986     switch (V_attachState(vp)) {
5987     case VOL_STATE_UNATTACHED:
5988     case VOL_STATE_PREATTACHED:
5989     case VOL_STATE_ERROR:
5990     case VOL_STATE_GOING_OFFLINE:
5991     case VOL_STATE_SHUTTING_DOWN:
5992     case VOL_STATE_SALVAGING:
5993         volume_LRU.q[vp->vlru.idx].len--;
5994
5995         /* create and cancel a reservation to
5996          * give the volume an opportunity to
5997          * be deallocated */
5998         VCreateReservation_r(vp);
5999         queue_Remove(&vp->vlru);
6000         vp->vlru.idx = VLRU_QUEUE_INVALID;
6001         V_attachFlags(vp) &= ~(VOL_ON_VLRU);
6002         VCancelReservation_r(vp);
6003         return 0;
6004     }
6005
6006     /* hold the volume and take it offline.
6007      * no need for reservations, as VHold_r
6008      * takes care of that internally. */
6009     if (VHold_r(vp) == 0) {
6010         /* vhold drops the glock, so now we should
6011          * check to make sure we aren't racing against
6012          * other threads.  if we are racing, offlining vp
6013          * would be wasteful, and block the scanner for a while 
6014          */
6015         if (vp->nWaiters || 
6016             (vp->nUsers > 1) ||
6017             (vp->shuttingDown) ||
6018             (vp->goingOffline) ||
6019             (vp->stats.last_get != ts_save)) {
6020             /* looks like we're racing someone else. bail */
6021             VPutVolume_r(vp);
6022             vp = NULL;
6023         } else {
6024             /* pull it off the VLRU */
6025             assert(vp->vlru.idx == VLRU_QUEUE_CANDIDATE);
6026             volume_LRU.q[VLRU_QUEUE_CANDIDATE].len--;
6027             queue_Remove(&vp->vlru);
6028             vp->vlru.idx = VLRU_QUEUE_INVALID;
6029             V_attachFlags(vp) &= ~(VOL_ON_VLRU);
6030
6031             /* take if offline */
6032             VOffline_r(vp, "volume has been soft detached");
6033
6034             /* invalidate the volume header cache */
6035             FreeVolumeHeader(vp);
6036
6037             /* update stats */
6038             IncUInt64(&VStats.soft_detaches);
6039             vp->stats.soft_detaches++;
6040
6041             /* put in pre-attached state so demand
6042              * attacher can work on it */
6043             VChangeState_r(vp, VOL_STATE_PREATTACHED);
6044             ret = 1;
6045         }
6046     }
6047     return ret;
6048 }
6049 #endif /* AFS_DEMAND_ATTACH_FS */
6050
6051
6052 /***************************************************/
6053 /* Volume Header Cache routines                    */
6054 /***************************************************/
6055
6056 /** 
6057  * volume header cache.
6058  */
6059 struct volume_hdr_LRU_t volume_hdr_LRU;
6060
6061 /**
6062  * initialize the volume header cache.
6063  *
6064  * @param[in] howMany  number of header cache entries to preallocate
6065  *
6066  * @pre VOL_LOCK held.  Function has never been called before.
6067  *
6068  * @post howMany cache entries are allocated, initialized, and added 
6069  *       to the LRU list.  Header cache statistics are initialized.
6070  *
6071  * @note only applicable to fileServer program type.  Should only be
6072  *       called once during volume package initialization.
6073  *
6074  * @internal volume package internal use only.
6075  */
6076 static void
6077 VInitVolumeHeaderCache(afs_uint32 howMany)
6078 {
6079     register struct volHeader *hp;
6080     if (programType != fileServer)
6081         return;
6082     queue_Init(&volume_hdr_LRU);
6083     volume_hdr_LRU.stats.free = 0;
6084     volume_hdr_LRU.stats.used = howMany;
6085     volume_hdr_LRU.stats.attached = 0;
6086     hp = (struct volHeader *)(calloc(howMany, sizeof(struct volHeader)));
6087     while (howMany--)
6088         ReleaseVolumeHeader(hp++);
6089 }
6090
6091 /**
6092  * get a volume header and attach it to the volume object.
6093  *
6094  * @param[in] vp  pointer to volume object
6095  *
6096  * @return cache entry status
6097  *    @retval 0  volume header was newly attached; cache data is invalid
6098  *    @retval 1  volume header was previously attached; cache data is valid
6099  *
6100  * @pre VOL_LOCK held.  For DAFS, lightweight ref must be held on volume object.
6101  *
6102  * @post volume header attached to volume object.  if necessary, header cache 
6103  *       entry on LRU is synchronized to disk.  Header is removed from LRU list.
6104  *
6105  * @note VOL_LOCK may be dropped
6106  *
6107  * @warning this interface does not load header data from disk.  it merely
6108  *          attaches a header object to the volume object, and may sync the old
6109  *          header cache data out to disk in the process.
6110  *
6111  * @internal volume package internal use only.
6112  */
6113 static int
6114 GetVolumeHeader(register Volume * vp)
6115 {
6116     Error error;
6117     register struct volHeader *hd;
6118     int old;
6119     static int everLogged = 0;
6120
6121 #ifdef AFS_DEMAND_ATTACH_FS
6122     VolState vp_save, back_save;
6123
6124     /* XXX debug 9/19/05 we've apparently got
6125      * a ref counting bug somewhere that's
6126      * breaking the nUsers == 0 => header on LRU
6127      * assumption */
6128     if (vp->header && queue_IsNotOnQueue(vp->header)) {
6129         Log("nUsers == 0, but header not on LRU\n");
6130         return 1;
6131     }
6132 #endif
6133
6134     old = (vp->header != NULL); /* old == volume already has a header */
6135
6136     if (programType != fileServer) {
6137         /* for volume utilities, we allocate volHeaders as needed */
6138         if (!vp->header) {
6139             hd = (struct volHeader *)calloc(1, sizeof(*vp->header));
6140             assert(hd != NULL);
6141             vp->header = hd;
6142             hd->back = vp;
6143 #ifdef AFS_DEMAND_ATTACH_FS
6144             V_attachFlags(vp) |= VOL_HDR_ATTACHED;
6145 #endif
6146         }
6147     } else {
6148         /* for the fileserver, we keep a volume header cache */
6149         if (old) {
6150             /* the header we previously dropped in the lru is
6151              * still available. pull it off the lru and return */
6152             hd = vp->header;
6153             queue_Remove(hd);
6154             assert(hd->back == vp);
6155         } else {
6156             /* we need to grab a new element off the LRU */
6157             if (queue_IsNotEmpty(&volume_hdr_LRU)) {
6158                 /* grab an element and pull off of LRU */
6159                 hd = queue_First(&volume_hdr_LRU, volHeader);
6160                 queue_Remove(hd);
6161             } else {
6162                 /* LRU is empty, so allocate a new volHeader 
6163                  * this is probably indicative of a leak, so let the user know */
6164                 hd = (struct volHeader *)calloc(1, sizeof(struct volHeader));
6165                 assert(hd != NULL);
6166                 if (!everLogged) {
6167                     Log("****Allocated more volume headers, probably leak****\n");
6168                     everLogged = 1;
6169                 }
6170                 volume_hdr_LRU.stats.free++;
6171             }
6172             if (hd->back) {
6173                 /* this header used to belong to someone else. 
6174                  * we'll need to check if the header needs to
6175                  * be sync'd out to disk */
6176
6177 #ifdef AFS_DEMAND_ATTACH_FS
6178                 /* if hd->back were in an exclusive state, then
6179                  * its volHeader would not be on the LRU... */
6180                 assert(!VIsExclusiveState(V_attachState(hd->back)));
6181 #endif
6182
6183                 if (hd->diskstuff.inUse) {
6184                     /* volume was in use, so we'll need to sync
6185                      * its header to disk */
6186
6187 #ifdef AFS_DEMAND_ATTACH_FS
6188                     back_save = VChangeState_r(hd->back, VOL_STATE_UPDATING);
6189                     vp_save = VChangeState_r(vp, VOL_STATE_HDR_ATTACHING);
6190                     VCreateReservation_r(hd->back);
6191                     VOL_UNLOCK;
6192 #endif
6193
6194                     WriteVolumeHeader_r(&error, hd->back);
6195                     /* Ignore errors; catch them later */
6196
6197 #ifdef AFS_DEMAND_ATTACH_FS
6198                     VOL_LOCK;
6199 #endif
6200                 }
6201
6202                 hd->back->header = NULL;
6203 #ifdef AFS_DEMAND_ATTACH_FS
6204                 V_attachFlags(hd->back) &= ~(VOL_HDR_ATTACHED | VOL_HDR_LOADED | VOL_HDR_IN_LRU);
6205
6206                 if (hd->diskstuff.inUse) {
6207                     VChangeState_r(hd->back, back_save);
6208                     VCancelReservation_r(hd->back);
6209                     VChangeState_r(vp, vp_save);
6210                 }
6211 #endif
6212             } else {
6213                 volume_hdr_LRU.stats.attached++;
6214             }
6215             hd->back = vp;
6216             vp->header = hd;
6217 #ifdef AFS_DEMAND_ATTACH_FS
6218             V_attachFlags(vp) |= VOL_HDR_ATTACHED;
6219 #endif
6220         }
6221         volume_hdr_LRU.stats.free--;
6222         volume_hdr_LRU.stats.used++;
6223     }
6224     IncUInt64(&VStats.hdr_gets);
6225 #ifdef AFS_DEMAND_ATTACH_FS
6226     IncUInt64(&vp->stats.hdr_gets);
6227     vp->stats.last_hdr_get = FT_ApproxTime();
6228 #endif
6229     return old;
6230 }
6231
6232
6233 /**
6234  * make sure volume header is attached and contains valid cache data.
6235  *
6236  * @param[out] ec  outbound error code
6237  * @param[in]  vp  pointer to volume object
6238  *
6239  * @pre VOL_LOCK held.  For DAFS, lightweight ref held on vp.
6240  *
6241  * @post header cache entry attached, and loaded with valid data, or
6242  *       *ec is nonzero, and the header is released back into the LRU.
6243  *
6244  * @internal volume package internal use only.
6245  */
6246 static void
6247 LoadVolumeHeader(Error * ec, Volume * vp)
6248 {
6249 #ifdef AFS_DEMAND_ATTACH_FS
6250     VolState state_save;
6251     afs_uint32 now;
6252     *ec = 0;
6253
6254     if (vp->nUsers == 0 && !GetVolumeHeader(vp)) {
6255         IncUInt64(&VStats.hdr_loads);
6256         state_save = VChangeState_r(vp, VOL_STATE_HDR_LOADING);
6257         VOL_UNLOCK;
6258
6259         ReadHeader(ec, V_diskDataHandle(vp), (char *)&V_disk(vp),
6260                    sizeof(V_disk(vp)), VOLUMEINFOMAGIC,
6261                    VOLUMEINFOVERSION);
6262         IncUInt64(&vp->stats.hdr_loads);
6263         now = FT_ApproxTime();
6264
6265         VOL_LOCK;
6266         if (!*ec) {
6267             V_attachFlags(vp) |= VOL_HDR_LOADED;
6268             vp->stats.last_hdr_load = now;
6269         }
6270         VChangeState_r(vp, state_save);
6271     }
6272 #else /* AFS_DEMAND_ATTACH_FS */
6273     *ec = 0;
6274     if (vp->nUsers == 0 && !GetVolumeHeader(vp)) {
6275         IncUInt64(&VStats.hdr_loads);
6276
6277         ReadHeader(ec, V_diskDataHandle(vp), (char *)&V_disk(vp),
6278                    sizeof(V_disk(vp)), VOLUMEINFOMAGIC,
6279                    VOLUMEINFOVERSION);
6280     }
6281 #endif /* AFS_DEMAND_ATTACH_FS */
6282     if (*ec) {
6283         /* maintain (nUsers==0) => header in LRU invariant */
6284         ReleaseVolumeHeader(vp->header);
6285     }
6286 }
6287
6288 /**
6289  * release a header cache entry back into the LRU list.
6290  *
6291  * @param[in] hd  pointer to volume header cache object
6292  *
6293  * @pre VOL_LOCK held.
6294  *
6295  * @post header cache object appended onto end of LRU list.
6296  *
6297  * @note only applicable to fileServer program type.
6298  *
6299  * @note used to place a header cache entry back into the
6300  *       LRU pool without invalidating it as a cache entry.
6301  *
6302  * @internal volume package internal use only.
6303  */
6304 static void
6305 ReleaseVolumeHeader(register struct volHeader *hd)
6306 {
6307     if (programType != fileServer)
6308         return;
6309     if (!hd || queue_IsOnQueue(hd))     /* no header, or header already released */
6310         return;
6311     queue_Append(&volume_hdr_LRU, hd);
6312 #ifdef AFS_DEMAND_ATTACH_FS
6313     if (hd->back) {
6314         V_attachFlags(hd->back) |= VOL_HDR_IN_LRU;
6315     }
6316 #endif
6317     volume_hdr_LRU.stats.free++;
6318     volume_hdr_LRU.stats.used--;
6319 }
6320
6321 /**
6322  * free/invalidate a volume header cache entry.
6323  *
6324  * @param[in] vp  pointer to volume object
6325  *
6326  * @pre VOL_LOCK is held.
6327  *
6328  * @post For fileserver, header cache entry is returned to LRU, and it is
6329  *       invalidated as a cache entry.  For volume utilities, the header
6330  *       cache entry is freed.
6331  *
6332  * @note For fileserver, this should be utilized instead of ReleaseVolumeHeader
6333  *       whenever it is necessary to invalidate the header cache entry.
6334  *
6335  * @see ReleaseVolumeHeader
6336  *
6337  * @internal volume package internal use only.
6338  */
6339 static void
6340 FreeVolumeHeader(register Volume * vp)
6341 {
6342     register struct volHeader *hd = vp->header;
6343     if (!hd)
6344         return;
6345     if (programType == fileServer) {
6346         ReleaseVolumeHeader(hd);
6347         hd->back = NULL;
6348     } else {
6349         free(hd);
6350     }
6351 #ifdef AFS_DEMAND_ATTACH_FS
6352     V_attachFlags(vp) &= ~(VOL_HDR_ATTACHED | VOL_HDR_IN_LRU | VOL_HDR_LOADED);
6353 #endif
6354     volume_hdr_LRU.stats.attached--;
6355     vp->header = NULL;
6356 }
6357
6358
6359 /***************************************************/
6360 /* Volume Hash Table routines                      */
6361 /***************************************************/
6362
6363 /**
6364  * set size of volume object hash table.
6365  *
6366  * @param[in] logsize   log(2) of desired hash table size
6367  *
6368  * @return operation status
6369  *    @retval 0 success
6370  *    @retval -1 failure
6371  *
6372  * @pre MUST be called prior to VInitVolumePackage
6373  *
6374  * @post Volume Hash Table will have 2^logsize buckets
6375  */
6376 int 
6377 VSetVolHashSize(int logsize)
6378 {
6379     /* 64 to 16384 hash buckets seems like a reasonable range */
6380     if ((logsize < 6 ) || (logsize > 14)) {
6381         return -1;
6382     }
6383     
6384     if (!VInit) {
6385         VolumeHashTable.Size = 1 << logsize;
6386         VolumeHashTable.Mask = VolumeHashTable.Size - 1;
6387     } else {
6388         /* we can't yet support runtime modification of this
6389          * parameter. we'll need a configuration rwlock to
6390          * make runtime modification feasible.... */
6391         return -1;
6392     }
6393     return 0;
6394 }
6395
6396 /**
6397  * initialize dynamic data structures for volume hash table.
6398  *
6399  * @post hash table is allocated, and fields are initialized.
6400  *
6401  * @internal volume package internal use only.
6402  */
6403 static void
6404 VInitVolumeHash(void)
6405 {
6406     register int i;
6407
6408     VolumeHashTable.Table = (VolumeHashChainHead *) calloc(VolumeHashTable.Size, 
6409                                                            sizeof(VolumeHashChainHead));
6410     assert(VolumeHashTable.Table != NULL);
6411     
6412     for (i=0; i < VolumeHashTable.Size; i++) {
6413         queue_Init(&VolumeHashTable.Table[i]);
6414 #ifdef AFS_DEMAND_ATTACH_FS
6415         assert(pthread_cond_init(&VolumeHashTable.Table[i].chain_busy_cv, NULL) == 0);
6416 #endif /* AFS_DEMAND_ATTACH_FS */
6417     }
6418 }
6419
6420 /**
6421  * add a volume object to the hash table.
6422  *
6423  * @param[in] vp      pointer to volume object
6424  * @param[in] hashid  hash of volume id
6425  *
6426  * @pre VOL_LOCK is held.  For DAFS, caller must hold a lightweight
6427  *      reference on vp.
6428  *
6429  * @post volume is added to hash chain.
6430  *
6431  * @internal volume package internal use only.
6432  *
6433  * @note For DAFS, VOL_LOCK may be dropped in order to wait for an
6434  *       asynchronous hash chain reordering to finish.
6435  */
6436 static void
6437 AddVolumeToHashTable(register Volume * vp, int hashid)
6438 {
6439     VolumeHashChainHead * head;
6440
6441     if (queue_IsOnQueue(vp))
6442         return;
6443
6444     head = &VolumeHashTable.Table[VOLUME_HASH(hashid)];
6445
6446 #ifdef AFS_DEMAND_ATTACH_FS
6447     /* wait for the hash chain to become available */
6448     VHashWait_r(head);
6449
6450     V_attachFlags(vp) |= VOL_IN_HASH;
6451     vp->chainCacheCheck = ++head->cacheCheck;
6452 #endif /* AFS_DEMAND_ATTACH_FS */
6453
6454     head->len++;
6455     vp->hashid = hashid;
6456     queue_Append(head, vp);
6457     vp->vnodeHashOffset = VolumeHashOffset_r();
6458 }
6459
6460 /**
6461  * delete a volume object from the hash table.
6462  *
6463  * @param[in] vp  pointer to volume object
6464  *
6465  * @pre VOL_LOCK is held.  For DAFS, caller must hold a lightweight
6466  *      reference on vp.
6467  *
6468  * @post volume is removed from hash chain.
6469  *
6470  * @internal volume package internal use only.
6471  *
6472  * @note For DAFS, VOL_LOCK may be dropped in order to wait for an
6473  *       asynchronous hash chain reordering to finish.
6474  */
6475 static void
6476 DeleteVolumeFromHashTable(register Volume * vp)
6477 {
6478     VolumeHashChainHead * head;
6479
6480     if (!queue_IsOnQueue(vp))
6481         return;
6482
6483     head = &VolumeHashTable.Table[VOLUME_HASH(vp->hashid)];
6484
6485 #ifdef AFS_DEMAND_ATTACH_FS
6486     /* wait for the hash chain to become available */
6487     VHashWait_r(head);
6488
6489     V_attachFlags(vp) &= ~(VOL_IN_HASH);
6490     head->cacheCheck++;
6491 #endif /* AFS_DEMAND_ATTACH_FS */
6492
6493     head->len--;
6494     queue_Remove(vp);
6495     /* do NOT reset hashid to zero, as the online
6496      * salvager package may need to know the volume id
6497      * after the volume is removed from the hash */
6498 }
6499
6500 /**
6501  * lookup a volume object in the hash table given a volume id.
6502  *
6503  * @param[out] ec        error code return
6504  * @param[in]  volumeId  volume id
6505  * @param[in]  hint      volume object which we believe could be the correct 
6506                          mapping
6507  *
6508  * @return volume object pointer
6509  *    @retval NULL  no such volume id is registered with the hash table.
6510  *
6511  * @pre VOL_LOCK is held.  For DAFS, caller must hold a lightweight 
6512         ref on hint.
6513  *
6514  * @post volume object with the given id is returned.  volume object and 
6515  *       hash chain access statistics are updated.  hash chain may have 
6516  *       been reordered.
6517  *
6518  * @note For DAFS, VOL_LOCK may be dropped in order to wait for an 
6519  *       asynchronous hash chain reordering operation to finish, or 
6520  *       in order for us to perform an asynchronous chain reordering.
6521  *
6522  * @note Hash chain reorderings occur when the access count for the 
6523  *       volume object being looked up exceeds the sum of the previous 
6524  *       node's (the node ahead of it in the hash chain linked list) 
6525  *       access count plus the constant VOLUME_HASH_REORDER_THRESHOLD.
6526  *
6527  * @note For DAFS, the hint parameter allows us to short-circuit if the 
6528  *       cacheCheck fields match between the hash chain head and the 
6529  *       hint volume object.
6530  */
6531 Volume *
6532 VLookupVolume_r(Error * ec, VolId volumeId, Volume * hint)
6533 {
6534     register int looks = 0;
6535     Volume * vp, *np, *pp;
6536     VolumeHashChainHead * head;
6537     *ec = 0;
6538
6539     head = &VolumeHashTable.Table[VOLUME_HASH(volumeId)];
6540
6541 #ifdef AFS_DEMAND_ATTACH_FS
6542     /* wait for the hash chain to become available */
6543     VHashWait_r(head);
6544
6545     /* check to see if we can short circuit without walking the hash chain */
6546     if (hint && (hint->chainCacheCheck == head->cacheCheck)) {
6547         IncUInt64(&hint->stats.hash_short_circuits);
6548         return hint;
6549     }
6550 #endif /* AFS_DEMAND_ATTACH_FS */
6551
6552     /* someday we need to either do per-chain locks, RWlocks,
6553      * or both for volhash access. 
6554      * (and move to a data structure with better cache locality) */
6555
6556     /* search the chain for this volume id */
6557     for(queue_Scan(head, vp, np, Volume)) {
6558         looks++;
6559         if ((vp->hashid == volumeId)) {
6560             break;
6561         }
6562     }
6563
6564     if (queue_IsEnd(head, vp)) {
6565         vp = NULL;
6566     }
6567
6568 #ifdef AFS_DEMAND_ATTACH_FS
6569     /* update hash chain statistics */
6570     {
6571         afs_uint64 lks;
6572         FillInt64(lks, 0, looks);
6573         AddUInt64(head->looks, lks, &head->looks);
6574         AddUInt64(VStats.hash_looks, lks, &VStats.hash_looks);
6575         IncUInt64(&head->gets);
6576     }
6577
6578     if (vp) {
6579         afs_uint64 thresh;
6580         IncUInt64(&vp->stats.hash_lookups);
6581
6582         /* for demand attach fileserver, we permit occasional hash chain reordering
6583          * so that frequently looked up volumes move towards the head of the chain */
6584         pp = queue_Prev(vp, Volume);
6585         if (!queue_IsEnd(head, pp)) {
6586             FillInt64(thresh, 0, VOLUME_HASH_REORDER_THRESHOLD);
6587             AddUInt64(thresh, pp->stats.hash_lookups, &thresh);
6588             if (GEInt64(vp->stats.hash_lookups, thresh)) {
6589                 VReorderHash_r(head, pp, vp);
6590             }
6591         }
6592
6593         /* update the short-circuit cache check */
6594         vp->chainCacheCheck = head->cacheCheck;
6595     }
6596 #endif /* AFS_DEMAND_ATTACH_FS */    
6597
6598     return vp;
6599 }
6600
6601 #ifdef AFS_DEMAND_ATTACH_FS
6602 /* perform volume hash chain reordering.
6603  *
6604  * advance a subchain beginning at vp ahead of
6605  * the adjacent subchain ending at pp */
6606 static void
6607 VReorderHash_r(VolumeHashChainHead * head, Volume * pp, Volume * vp)
6608 {
6609     Volume *tp, *np, *lp;
6610     afs_uint64 move_thresh;
6611
6612     /* this should never be called if the chain is already busy, so
6613      * no need to wait for other exclusive chain ops to finish */
6614
6615     /* this is a rather heavy set of operations,
6616      * so let's set the chain busy flag and drop
6617      * the vol_glock */
6618     VHashBeginExclusive_r(head);
6619     VOL_UNLOCK;
6620
6621     /* scan forward in the chain from vp looking for the last element
6622      * in the chain we want to advance */
6623     FillInt64(move_thresh, 0, VOLUME_HASH_REORDER_CHAIN_THRESH);
6624     AddUInt64(move_thresh, pp->stats.hash_lookups, &move_thresh);
6625     for(queue_ScanFrom(head, vp, tp, np, Volume)) {
6626         if (LTInt64(tp->stats.hash_lookups, move_thresh)) {
6627             break;
6628         }
6629     }
6630     lp = queue_Prev(tp, Volume);
6631
6632     /* scan backwards from pp to determine where to splice and
6633      * insert the subchain we're advancing */
6634     for(queue_ScanBackwardsFrom(head, pp, tp, np, Volume)) {
6635         if (GTInt64(tp->stats.hash_lookups, move_thresh)) {
6636             break;
6637         }
6638     }
6639     tp = queue_Next(tp, Volume);
6640
6641     /* rebalance chain(vp,...,lp) ahead of chain(tp,...,pp) */
6642     queue_MoveChainBefore(tp,vp,lp);
6643
6644     VOL_LOCK;
6645     IncUInt64(&VStats.hash_reorders);
6646     head->cacheCheck++;
6647     IncUInt64(&head->reorders);
6648
6649     /* wake up any threads waiting for the hash chain */
6650     VHashEndExclusive_r(head);
6651 }
6652
6653
6654 /* demand-attach fs volume hash
6655  * asynchronous exclusive operations */
6656
6657 /**
6658  * begin an asynchronous exclusive operation on a volume hash chain.
6659  *
6660  * @param[in] head   pointer to volume hash chain head object
6661  *
6662  * @pre VOL_LOCK held.  hash chain is quiescent.
6663  *
6664  * @post hash chain marked busy.
6665  *
6666  * @note this interface is used in conjunction with VHashEndExclusive_r and
6667  *       VHashWait_r to perform asynchronous (wrt VOL_LOCK) operations on a
6668  *       volume hash chain.  Its main use case is hash chain reordering, which
6669  *       has the potential to be a highly latent operation.
6670  *
6671  * @see VHashEndExclusive_r
6672  * @see VHashWait_r
6673  *
6674  * @note DAFS only
6675  *
6676  * @internal volume package internal use only.
6677  */
6678 static void
6679 VHashBeginExclusive_r(VolumeHashChainHead * head)
6680 {
6681     assert(head->busy == 0);
6682     head->busy = 1;
6683 }
6684
6685 /**
6686  * relinquish exclusive ownership of a volume hash chain.
6687  *
6688  * @param[in] head   pointer to volume hash chain head object
6689  *
6690  * @pre VOL_LOCK held.  thread owns the hash chain exclusively.
6691  *
6692  * @post hash chain is marked quiescent.  threads awaiting use of
6693  *       chain are awakened.
6694  *
6695  * @see VHashBeginExclusive_r
6696  * @see VHashWait_r
6697  *
6698  * @note DAFS only
6699  *
6700  * @internal volume package internal use only.
6701  */
6702 static void
6703 VHashEndExclusive_r(VolumeHashChainHead * head)
6704 {
6705     assert(head->busy);
6706     head->busy = 0;
6707     assert(pthread_cond_broadcast(&head->chain_busy_cv) == 0);
6708 }
6709
6710 /**
6711  * wait for all asynchronous operations on a hash chain to complete.
6712  *
6713  * @param[in] head   pointer to volume hash chain head object
6714  *
6715  * @pre VOL_LOCK held.
6716  *
6717  * @post hash chain object is quiescent.
6718  *
6719  * @see VHashBeginExclusive_r
6720  * @see VHashEndExclusive_r
6721  *
6722  * @note DAFS only
6723  *
6724  * @note This interface should be called before any attempt to
6725  *       traverse the hash chain.  It is permissible for a thread
6726  *       to gain exclusive access to the chain, and then perform
6727  *       latent operations on the chain asynchronously wrt the 
6728  *       VOL_LOCK.
6729  *
6730  * @warning if waiting is necessary, VOL_LOCK is dropped
6731  *
6732  * @internal volume package internal use only.
6733  */
6734 static void
6735 VHashWait_r(VolumeHashChainHead * head)
6736 {
6737     while (head->busy) {
6738         VOL_CV_WAIT(&head->chain_busy_cv);
6739     }
6740 }
6741 #endif /* AFS_DEMAND_ATTACH_FS */
6742
6743
6744 /***************************************************/
6745 /* Volume by Partition List routines               */
6746 /***************************************************/
6747
6748 /*
6749  * demand attach fileserver adds a
6750  * linked list of volumes to each
6751  * partition object, thus allowing
6752  * for quick enumeration of all
6753  * volumes on a partition
6754  */
6755
6756 #ifdef AFS_DEMAND_ATTACH_FS
6757 /**
6758  * add a volume to its disk partition VByPList.
6759  *
6760  * @param[in] vp  pointer to volume object
6761  *
6762  * @pre either the disk partition VByPList is owned exclusively
6763  *      by the calling thread, or the list is quiescent and
6764  *      VOL_LOCK is held.
6765  *
6766  * @post volume is added to disk partition VByPList
6767  *
6768  * @note DAFS only
6769  *
6770  * @warning it is the caller's responsibility to ensure list
6771  *          quiescence.
6772  *
6773  * @see VVByPListWait_r
6774  * @see VVByPListBeginExclusive_r
6775  * @see VVByPListEndExclusive_r
6776  *
6777  * @internal volume package internal use only.
6778  */
6779 static void
6780 AddVolumeToVByPList_r(Volume * vp)
6781 {
6782     if (queue_IsNotOnQueue(&vp->vol_list)) {
6783         queue_Append(&vp->partition->vol_list, &vp->vol_list);
6784         V_attachFlags(vp) |= VOL_ON_VBYP_LIST;
6785         vp->partition->vol_list.len++;
6786     }
6787 }
6788
6789 /**
6790  * delete a volume from its disk partition VByPList.
6791  *
6792  * @param[in] vp  pointer to volume object
6793  *
6794  * @pre either the disk partition VByPList is owned exclusively
6795  *      by the calling thread, or the list is quiescent and
6796  *      VOL_LOCK is held.
6797  *
6798  * @post volume is removed from the disk partition VByPList
6799  *
6800  * @note DAFS only
6801  *
6802  * @warning it is the caller's responsibility to ensure list
6803  *          quiescence.
6804  *
6805  * @see VVByPListWait_r
6806  * @see VVByPListBeginExclusive_r
6807  * @see VVByPListEndExclusive_r
6808  *
6809  * @internal volume package internal use only.
6810  */
6811 static void
6812 DeleteVolumeFromVByPList_r(Volume * vp)
6813 {
6814     if (queue_IsOnQueue(&vp->vol_list)) {
6815         queue_Remove(&vp->vol_list);
6816         V_attachFlags(vp) &= ~(VOL_ON_VBYP_LIST);
6817         vp->partition->vol_list.len--;
6818     }
6819 }
6820
6821 /**
6822  * begin an asynchronous exclusive operation on a VByPList.
6823  *
6824  * @param[in] dp   pointer to disk partition object
6825  *
6826  * @pre VOL_LOCK held.  VByPList is quiescent.
6827  *
6828  * @post VByPList marked busy.
6829  *
6830  * @note this interface is used in conjunction with VVByPListEndExclusive_r and
6831  *       VVByPListWait_r to perform asynchronous (wrt VOL_LOCK) operations on a
6832  *       VByPList.
6833  *
6834  * @see VVByPListEndExclusive_r
6835  * @see VVByPListWait_r
6836  *
6837  * @note DAFS only
6838  *
6839  * @internal volume package internal use only.
6840  */
6841 /* take exclusive control over the list */
6842 static void
6843 VVByPListBeginExclusive_r(struct DiskPartition64 * dp)
6844 {
6845     assert(dp->vol_list.busy == 0);
6846     dp->vol_list.busy = 1;
6847 }
6848
6849 /**
6850  * relinquish exclusive ownership of a VByPList.
6851  *
6852  * @param[in] dp   pointer to disk partition object
6853  *
6854  * @pre VOL_LOCK held.  thread owns the VByPList exclusively.
6855  *
6856  * @post VByPList is marked quiescent.  threads awaiting use of
6857  *       the list are awakened.
6858  *
6859  * @see VVByPListBeginExclusive_r
6860  * @see VVByPListWait_r
6861  *
6862  * @note DAFS only
6863  *
6864  * @internal volume package internal use only.
6865  */
6866 static void
6867 VVByPListEndExclusive_r(struct DiskPartition64 * dp)
6868 {
6869     assert(dp->vol_list.busy);
6870     dp->vol_list.busy = 0;
6871     assert(pthread_cond_broadcast(&dp->vol_list.cv) == 0);
6872 }
6873
6874 /**
6875  * wait for all asynchronous operations on a VByPList to complete.
6876  *
6877  * @param[in] dp  pointer to disk partition object
6878  *
6879  * @pre VOL_LOCK is held.
6880  *
6881  * @post disk partition's VByP list is quiescent
6882  *
6883  * @note DAFS only
6884  *
6885  * @note This interface should be called before any attempt to
6886  *       traverse the VByPList.  It is permissible for a thread
6887  *       to gain exclusive access to the list, and then perform
6888  *       latent operations on the list asynchronously wrt the 
6889  *       VOL_LOCK.
6890  *
6891  * @warning if waiting is necessary, VOL_LOCK is dropped
6892  *
6893  * @see VVByPListEndExclusive_r
6894  * @see VVByPListBeginExclusive_r
6895  *
6896  * @internal volume package internal use only.
6897  */
6898 static void
6899 VVByPListWait_r(struct DiskPartition64 * dp)
6900 {
6901     while (dp->vol_list.busy) {
6902         VOL_CV_WAIT(&dp->vol_list.cv);
6903     }
6904 }
6905 #endif /* AFS_DEMAND_ATTACH_FS */
6906
6907 /***************************************************/
6908 /* Volume Cache Statistics routines                */
6909 /***************************************************/
6910
6911 void
6912 VPrintCacheStats_r(void)
6913 {
6914     afs_uint32 get_hi, get_lo, load_hi, load_lo;
6915     register struct VnodeClassInfo *vcp;
6916     vcp = &VnodeClassInfo[vLarge];
6917     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);
6918     vcp = &VnodeClassInfo[vSmall];
6919     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);
6920     SplitInt64(VStats.hdr_gets, get_hi, get_lo);
6921     SplitInt64(VStats.hdr_loads, load_hi, load_lo);
6922     Log("Volume header cache, %d entries, %d gets, %d replacements\n",
6923         VStats.hdr_cache_size, get_lo, load_lo);
6924 }
6925
6926 void
6927 VPrintCacheStats(void)
6928 {
6929     VOL_LOCK;
6930     VPrintCacheStats_r();
6931     VOL_UNLOCK;
6932 }
6933
6934 #ifdef AFS_DEMAND_ATTACH_FS
6935 static double
6936 UInt64ToDouble(afs_uint64 * x)
6937 {
6938     static double c32 = 4.0 * 1.073741824 * 1000000000.0;
6939     afs_uint32 h, l;
6940     SplitInt64(*x, h, l);
6941     return (((double)h) * c32) + ((double) l);
6942 }
6943
6944 static char *
6945 DoubleToPrintable(double x, char * buf, int len)
6946 {
6947     static double billion = 1000000000.0;
6948     afs_uint32 y[3];
6949
6950     y[0] = (afs_uint32) (x / (billion * billion));
6951     y[1] = (afs_uint32) ((x - (((double)y[0]) * billion * billion)) / billion);
6952     y[2] = (afs_uint32) (x - ((((double)y[0]) * billion * billion) + (((double)y[1]) * billion)));
6953
6954     if (y[0]) {
6955         snprintf(buf, len, "%d%09d%09d", y[0], y[1], y[2]);
6956     } else if (y[1]) {
6957         snprintf(buf, len, "%d%09d", y[1], y[2]);
6958     } else {
6959         snprintf(buf, len, "%d", y[2]);
6960     }
6961     buf[len-1] = '\0';
6962     return buf;
6963 }
6964
6965 void
6966 VPrintExtendedCacheStats_r(int flags)
6967 {
6968     int i, j;
6969     struct stats {
6970         double min;
6971         double max;
6972         double sum;
6973         double avg;
6974     };
6975     struct stats looks, gets, reorders, len;
6976     struct stats ch_looks, ch_gets, ch_reorders;
6977     char pr_buf[4][32];
6978     VolumeHashChainHead *head;
6979     Volume *vp, *np;
6980
6981     /* zero out stats */
6982     memset(&looks, 0, sizeof(struct stats));
6983     memset(&gets, 0, sizeof(struct stats));
6984     memset(&reorders, 0, sizeof(struct stats));
6985     memset(&len, 0, sizeof(struct stats));
6986     memset(&ch_looks, 0, sizeof(struct stats));
6987     memset(&ch_gets, 0, sizeof(struct stats));
6988     memset(&ch_reorders, 0, sizeof(struct stats));
6989
6990     for (i = 0; i < VolumeHashTable.Size; i++) {
6991         head = &VolumeHashTable.Table[i];
6992
6993         VHashWait_r(head);
6994         VHashBeginExclusive_r(head);
6995         VOL_UNLOCK;
6996
6997         ch_looks.sum    = UInt64ToDouble(&head->looks);
6998         ch_gets.sum     = UInt64ToDouble(&head->gets);
6999         ch_reorders.sum = UInt64ToDouble(&head->reorders);
7000
7001         /* update global statistics */
7002         {
7003             looks.sum    += ch_looks.sum;
7004             gets.sum     += ch_gets.sum;
7005             reorders.sum += ch_reorders.sum;
7006             len.sum      += (double)head->len;
7007             
7008             if (i == 0) {
7009                 len.min      = (double) head->len;
7010                 len.max      = (double) head->len;
7011                 looks.min    = ch_looks.sum;
7012                 looks.max    = ch_looks.sum;
7013                 gets.min     = ch_gets.sum;
7014                 gets.max     = ch_gets.sum;
7015                 reorders.min = ch_reorders.sum;
7016                 reorders.max = ch_reorders.sum;
7017             } else {
7018                 if (((double)head->len) < len.min)
7019                     len.min = (double) head->len;
7020                 if (((double)head->len) > len.max)
7021                     len.max = (double) head->len;
7022                 if (ch_looks.sum < looks.min)
7023                     looks.min = ch_looks.sum;
7024                 else if (ch_looks.sum > looks.max)
7025                     looks.max = ch_looks.sum;
7026                 if (ch_gets.sum < gets.min)
7027                     gets.min = ch_gets.sum;
7028                 else if (ch_gets.sum > gets.max)
7029                     gets.max = ch_gets.sum;
7030                 if (ch_reorders.sum < reorders.min)
7031                     reorders.min = ch_reorders.sum;
7032                 else if (ch_reorders.sum > reorders.max)
7033                     reorders.max = ch_reorders.sum;
7034             }
7035         }
7036
7037         if ((flags & VOL_STATS_PER_CHAIN2) && queue_IsNotEmpty(head)) {
7038             /* compute detailed per-chain stats */
7039             struct stats hdr_loads, hdr_gets;
7040             double v_looks, v_loads, v_gets;
7041
7042             /* initialize stats with data from first element in chain */
7043             vp = queue_First(head, Volume);
7044             v_looks = UInt64ToDouble(&vp->stats.hash_lookups);
7045             v_loads = UInt64ToDouble(&vp->stats.hdr_loads);
7046             v_gets  = UInt64ToDouble(&vp->stats.hdr_gets);
7047             ch_gets.min = ch_gets.max = v_looks;
7048             hdr_loads.min = hdr_loads.max = v_loads;
7049             hdr_gets.min = hdr_gets.max = v_gets;
7050             hdr_loads.sum = hdr_gets.sum = 0;
7051
7052             vp = queue_Next(vp, Volume);
7053
7054             /* pull in stats from remaining elements in chain */
7055             for (queue_ScanFrom(head, vp, vp, np, Volume)) {
7056                 v_looks = UInt64ToDouble(&vp->stats.hash_lookups);
7057                 v_loads = UInt64ToDouble(&vp->stats.hdr_loads);
7058                 v_gets  = UInt64ToDouble(&vp->stats.hdr_gets);
7059
7060                 hdr_loads.sum += v_loads;
7061                 hdr_gets.sum += v_gets;
7062
7063                 if (v_looks < ch_gets.min)
7064                     ch_gets.min = v_looks;
7065                 else if (v_looks > ch_gets.max)
7066                     ch_gets.max = v_looks;
7067
7068                 if (v_loads < hdr_loads.min)
7069                     hdr_loads.min = v_loads;
7070                 else if (v_loads > hdr_loads.max)
7071                     hdr_loads.max = v_loads;
7072
7073                 if (v_gets < hdr_gets.min)
7074                     hdr_gets.min = v_gets;
7075                 else if (v_gets > hdr_gets.max)
7076                     hdr_gets.max = v_gets;
7077             }
7078
7079             /* compute per-chain averages */
7080             ch_gets.avg = ch_gets.sum / ((double)head->len);
7081             hdr_loads.avg = hdr_loads.sum / ((double)head->len);
7082             hdr_gets.avg = hdr_gets.sum / ((double)head->len);
7083
7084             /* dump per-chain stats */
7085             Log("Volume hash chain %d : len=%d, looks=%s, reorders=%s\n",
7086                 i, head->len, 
7087                 DoubleToPrintable(ch_looks.sum, pr_buf[0], sizeof(pr_buf[0])),
7088                 DoubleToPrintable(ch_reorders.sum, pr_buf[1], sizeof(pr_buf[1])));
7089             Log("\tVolume gets : min=%s, max=%s, avg=%s, total=%s\n",
7090                 DoubleToPrintable(ch_gets.min, pr_buf[0], sizeof(pr_buf[0])),
7091                 DoubleToPrintable(ch_gets.max, pr_buf[1], sizeof(pr_buf[1])),
7092                 DoubleToPrintable(ch_gets.avg, pr_buf[2], sizeof(pr_buf[2])),
7093                 DoubleToPrintable(ch_gets.sum, pr_buf[3], sizeof(pr_buf[3])));
7094             Log("\tHDR gets : min=%s, max=%s, avg=%s, total=%s\n",
7095                 DoubleToPrintable(hdr_gets.min, pr_buf[0], sizeof(pr_buf[0])),
7096                 DoubleToPrintable(hdr_gets.max, pr_buf[1], sizeof(pr_buf[1])),
7097                 DoubleToPrintable(hdr_gets.avg, pr_buf[2], sizeof(pr_buf[2])),
7098                 DoubleToPrintable(hdr_gets.sum, pr_buf[3], sizeof(pr_buf[3])));
7099             Log("\tHDR loads : min=%s, max=%s, avg=%s, total=%s\n",
7100                 DoubleToPrintable(hdr_loads.min, pr_buf[0], sizeof(pr_buf[0])),
7101                 DoubleToPrintable(hdr_loads.max, pr_buf[1], sizeof(pr_buf[1])),
7102                 DoubleToPrintable(hdr_loads.avg, pr_buf[2], sizeof(pr_buf[2])),
7103                 DoubleToPrintable(hdr_loads.sum, pr_buf[3], sizeof(pr_buf[3])));
7104         } else if (flags & VOL_STATS_PER_CHAIN) {
7105             /* dump simple per-chain stats */
7106             Log("Volume hash chain %d : len=%d, looks=%s, gets=%s, reorders=%s\n",
7107                 i, head->len, 
7108                 DoubleToPrintable(ch_looks.sum, pr_buf[0], sizeof(pr_buf[0])),
7109                 DoubleToPrintable(ch_gets.sum, pr_buf[1], sizeof(pr_buf[1])),
7110                 DoubleToPrintable(ch_reorders.sum, pr_buf[2], sizeof(pr_buf[2])));
7111         }
7112
7113         VOL_LOCK;
7114         VHashEndExclusive_r(head);
7115     }
7116
7117     VOL_UNLOCK;
7118
7119     /* compute global averages */
7120     len.avg      = len.sum      / ((double)VolumeHashTable.Size);
7121     looks.avg    = looks.sum    / ((double)VolumeHashTable.Size);
7122     gets.avg     = gets.sum     / ((double)VolumeHashTable.Size);
7123     reorders.avg = reorders.sum / ((double)VolumeHashTable.Size);
7124
7125     /* dump global stats */
7126     Log("Volume hash summary: %d buckets\n", VolumeHashTable.Size);
7127     Log(" chain length : min=%s, max=%s, avg=%s, total=%s\n",
7128         DoubleToPrintable(len.min, pr_buf[0], sizeof(pr_buf[0])),
7129         DoubleToPrintable(len.max, pr_buf[1], sizeof(pr_buf[1])),
7130         DoubleToPrintable(len.avg, pr_buf[2], sizeof(pr_buf[2])),
7131         DoubleToPrintable(len.sum, pr_buf[3], sizeof(pr_buf[3])));
7132     Log(" looks : min=%s, max=%s, avg=%s, total=%s\n",
7133         DoubleToPrintable(looks.min, pr_buf[0], sizeof(pr_buf[0])),
7134         DoubleToPrintable(looks.max, pr_buf[1], sizeof(pr_buf[1])),
7135         DoubleToPrintable(looks.avg, pr_buf[2], sizeof(pr_buf[2])),
7136         DoubleToPrintable(looks.sum, pr_buf[3], sizeof(pr_buf[3])));
7137     Log(" gets : min=%s, max=%s, avg=%s, total=%s\n",
7138         DoubleToPrintable(gets.min, pr_buf[0], sizeof(pr_buf[0])),
7139         DoubleToPrintable(gets.max, pr_buf[1], sizeof(pr_buf[1])),
7140         DoubleToPrintable(gets.avg, pr_buf[2], sizeof(pr_buf[2])),
7141         DoubleToPrintable(gets.sum, pr_buf[3], sizeof(pr_buf[3])));
7142     Log(" reorders : min=%s, max=%s, avg=%s, total=%s\n",
7143         DoubleToPrintable(reorders.min, pr_buf[0], sizeof(pr_buf[0])),
7144         DoubleToPrintable(reorders.max, pr_buf[1], sizeof(pr_buf[1])),
7145         DoubleToPrintable(reorders.avg, pr_buf[2], sizeof(pr_buf[2])),
7146         DoubleToPrintable(reorders.sum, pr_buf[3], sizeof(pr_buf[3])));
7147
7148     /* print extended disk related statistics */
7149     {
7150         struct DiskPartition64 * diskP;
7151         afs_uint32 vol_count[VOLMAXPARTS+1];
7152         byte part_exists[VOLMAXPARTS+1];
7153         Device id;
7154         int i;
7155
7156         memset(vol_count, 0, sizeof(vol_count));
7157         memset(part_exists, 0, sizeof(part_exists));
7158
7159         VOL_LOCK;
7160
7161         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
7162             id = diskP->index;
7163             vol_count[id] = diskP->vol_list.len;
7164             part_exists[id] = 1;
7165         }
7166
7167         VOL_UNLOCK;
7168         for (i = 0; i <= VOLMAXPARTS; i++) {
7169             if (part_exists[i]) {
7170                 diskP = VGetPartitionById_r(i, 0);
7171                 if (diskP) {
7172                     Log("Partition %s has %d online volumes\n", 
7173                         VPartitionPath(diskP), diskP->vol_list.len);
7174                 }
7175             }
7176         }
7177         VOL_LOCK;
7178     }
7179
7180 }
7181
7182 void
7183 VPrintExtendedCacheStats(int flags)
7184 {
7185     VOL_LOCK;
7186     VPrintExtendedCacheStats_r(flags);
7187     VOL_UNLOCK;
7188 }
7189 #endif /* AFS_DEMAND_ATTACH_FS */