dafs-kill-xcpu-dump-vlru-stats-20080318
[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) == fileServer) {
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) == fileServer)
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         if ((mode == V_VOLUPD) || (VolumeWriteable(vp) && (mode == V_CLONE))) {
1954             /* mark volume header as in use so that volser crashes lead to a
1955              * salvage attempt */
1956             VUpdateVolume_r(ec, vp, 0);
1957         }
1958 #ifdef AFS_DEMAND_ATTACH_FS
1959         /* for dafs, we should tell the fileserver, except for V_PEEK
1960          * where we know it is not necessary */
1961         if (mode == V_PEEK) {
1962             vp->needsPutBack = 0;
1963         } else {
1964             vp->needsPutBack = 1;
1965         }
1966 #else /* !AFS_DEMAND_ATTACH_FS */
1967         /* duplicate computation in fssync.c about whether the server
1968          * takes the volume offline or not.  If the volume isn't
1969          * offline, we must not return it when we detach the volume,
1970          * or the server will abort */
1971         if (mode == V_READONLY || mode == V_PEEK
1972             || (!VolumeWriteable(vp) && (mode == V_CLONE || mode == V_DUMP)))
1973             vp->needsPutBack = 0;
1974         else
1975             vp->needsPutBack = 1;
1976 #endif /* !AFS_DEMAND_ATTACH_FS */
1977     }
1978     /* OK, there's a problem here, but one that I don't know how to
1979      * fix right now, and that I don't think should arise often.
1980      * Basically, we should only put back this volume to the server if
1981      * it was given to us by the server, but since we don't have a vp,
1982      * we can't run the VolumeWriteable function to find out as we do
1983      * above when computing vp->needsPutBack.  So we send it back, but
1984      * there's a path in VAttachVolume on the server which may abort
1985      * if this volume doesn't have a header.  Should be pretty rare
1986      * for all of that to happen, but if it does, probably the right
1987      * fix is for the server to allow the return of readonly volumes
1988      * that it doesn't think are really checked out. */
1989 #ifdef FSSYNC_BUILD_CLIENT
1990     if (programType == volumeUtility && vp == NULL &&
1991         mode != V_SECRETLY && mode != V_PEEK) {
1992         FSYNC_VolOp(iheader.id, partition, FSYNC_VOL_ON, 0, NULL);
1993     } else 
1994 #endif
1995     if (programType == fileServer && vp) {
1996 #ifdef AFS_DEMAND_ATTACH_FS
1997         /* 
1998          * we can get here in cases where we don't "own"
1999          * the volume (e.g. volume owned by a utility).
2000          * short circuit around potential disk header races.
2001          */
2002         if (V_attachState(vp) != VOL_STATE_ATTACHED) {
2003             goto done;
2004         }
2005 #endif
2006         V_needsCallback(vp) = 0;
2007 #ifdef  notdef
2008         if (VInit >= 2 && V_BreakVolumeCallbacks) {
2009             Log("VAttachVolume: Volume %u was changed externally; breaking callbacks\n", V_id(vp));
2010             (*V_BreakVolumeCallbacks) (V_id(vp));
2011         }
2012 #endif
2013         VUpdateVolume_r(ec, vp, 0);
2014         if (*ec) {
2015             Log("VAttachVolume: Error updating volume\n");
2016             if (vp)
2017                 VPutVolume_r(vp);
2018             goto done;
2019         }
2020         if (VolumeWriteable(vp) && V_dontSalvage(vp) == 0) {
2021 #ifndef AFS_DEMAND_ATTACH_FS
2022             /* This is a hack: by temporarily setting the incore
2023              * dontSalvage flag ON, the volume will be put back on the
2024              * Update list (with dontSalvage OFF again).  It will then
2025              * come back in N minutes with DONT_SALVAGE eventually
2026              * set.  This is the way that volumes that have never had
2027              * it set get it set; or that volumes that have been
2028              * offline without DONT SALVAGE having been set also
2029              * eventually get it set */
2030             V_dontSalvage(vp) = DONT_SALVAGE;
2031 #endif /* !AFS_DEMAND_ATTACH_FS */
2032             VAddToVolumeUpdateList_r(ec, vp);
2033             if (*ec) {
2034                 Log("VAttachVolume: Error adding volume to update list\n");
2035                 if (vp)
2036                     VPutVolume_r(vp);
2037                 goto done;
2038             }
2039         }
2040         if (LogLevel)
2041             Log("VOnline:  volume %u (%s) attached and online\n", V_id(vp),
2042                 V_name(vp));
2043     }
2044
2045   done:
2046     if (programType == volumeUtility) {
2047         VUnlockPartition_r(partition);
2048     }
2049     if (*ec) {
2050 #ifdef AFS_DEMAND_ATTACH_FS
2051         /* attach failed; make sure we're in error state */
2052         if (vp && !VIsErrorState(V_attachState(vp))) {
2053             VChangeState_r(vp, VOL_STATE_ERROR);
2054         }
2055 #endif /* AFS_DEMAND_ATTACH_FS */
2056         return NULL;
2057     } else {
2058         return vp;
2059     }
2060 }
2061
2062 #ifdef AFS_DEMAND_ATTACH_FS
2063 /* VAttachVolumeByVp_r
2064  *
2065  * finish attaching a volume that is
2066  * in a less than fully attached state
2067  */
2068 /* caller MUST hold a ref count on vp */
2069 static Volume *
2070 VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode)
2071 {
2072     char name[VMAXPATHLEN];
2073     int fd, n, reserve = 0;
2074     struct afs_stat status;
2075     struct VolumeDiskHeader diskHeader;
2076     struct VolumeHeader iheader;
2077     struct DiskPartition64 *partp;
2078     char path[64];
2079     int isbusy = 0;
2080     VolId volumeId;
2081     Volume * nvp;
2082     VolumeStats stats_save;
2083     *ec = 0;
2084
2085     /* volume utility should never call AttachByVp */
2086     assert(programType == fileServer);
2087    
2088     volumeId = vp->hashid;
2089     partp = vp->partition;
2090     VolumeExternalName_r(volumeId, name, sizeof(name));
2091
2092
2093     /* if another thread is performing a blocking op, wait */
2094     VWaitExclusiveState_r(vp);
2095
2096     memcpy(&stats_save, &vp->stats, sizeof(VolumeStats));
2097
2098     /* if it's already attached, see if we can return it */
2099     if (V_attachState(vp) == VOL_STATE_ATTACHED) {
2100         VGetVolumeByVp_r(ec, vp);
2101         if (V_inUse(vp) == fileServer) {
2102             return vp;
2103         } else {
2104             if (vp->specialStatus == VBUSY)
2105                 isbusy = 1;
2106             VDetachVolume_r(ec, vp);
2107             if (*ec) {
2108                 Log("VAttachVolume: Error detaching volume (%s)\n", name);
2109             }
2110             vp = NULL;
2111         }
2112     }
2113
2114     /* pre-attach volume if it hasn't been done yet */
2115     if (!vp || 
2116         (V_attachState(vp) == VOL_STATE_UNATTACHED) ||
2117         (V_attachState(vp) == VOL_STATE_ERROR)) {
2118         nvp = VPreAttachVolumeByVp_r(ec, partp, vp, volumeId);
2119         if (*ec) {
2120             return NULL;
2121         }
2122         if (nvp != vp) {
2123             reserve = 1;
2124             VCreateReservation_r(nvp);
2125             vp = nvp;
2126         }
2127     }
2128     
2129     assert(vp != NULL);
2130     VChangeState_r(vp, VOL_STATE_ATTACHING);
2131
2132     /* restore monotonically increasing stats */
2133     memcpy(&vp->stats, &stats_save, sizeof(VolumeStats));
2134
2135     *ec = 0;
2136
2137
2138     /* compute path to disk header, 
2139      * read in header, 
2140      * and verify magic and version stamps */
2141     strcpy(path, VPartitionPath(partp));
2142
2143     VOL_UNLOCK;
2144
2145     strcat(path, "/");
2146     strcat(path, name);
2147     if ((fd = afs_open(path, O_RDONLY)) == -1 || afs_fstat(fd, &status) == -1) {
2148         Log("VAttachVolume: Failed to open %s (errno %d)\n", path, errno);
2149         if (fd > -1)
2150             close(fd);
2151         *ec = VNOVOL;
2152         VOL_LOCK;
2153         goto done;
2154     }
2155     n = read(fd, &diskHeader, sizeof(diskHeader));
2156     close(fd);
2157     if (n != sizeof(diskHeader)
2158         || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
2159         Log("VAttachVolume: Error reading volume header %s\n", path);
2160         *ec = VSALVAGE;
2161         VOL_LOCK;
2162         goto done;
2163     }
2164     if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
2165         Log("VAttachVolume: Volume %s, version number is incorrect; volume needs salvaged\n", path);
2166         *ec = VSALVAGE;
2167         VOL_LOCK;
2168         goto done;
2169     }
2170
2171     /* convert on-disk header format to in-memory header format */
2172     DiskToVolumeHeader(&iheader, &diskHeader);
2173
2174     /* do volume attach
2175      *
2176      * NOTE: attach2 is entered without any locks, and returns
2177      * with vol_glock_mutex held */
2178     vp = attach2(ec, volumeId, path, &iheader, partp, vp, isbusy, mode);
2179
2180     /*
2181      * the event that an error was encountered, or
2182      * the volume was not brought to an attached state
2183      * for any reason, skip to the end.  We cannot
2184      * safely call VUpdateVolume unless we "own" it.
2185      */
2186     if (*ec || 
2187         (vp == NULL) ||
2188         (V_attachState(vp) != VOL_STATE_ATTACHED)) {
2189         goto done;
2190     }
2191
2192     V_needsCallback(vp) = 0;
2193     VUpdateVolume_r(ec, vp, 0);
2194     if (*ec) {
2195         Log("VAttachVolume: Error updating volume %u\n", vp->hashid);
2196         VPutVolume_r(vp);
2197         goto done;
2198     }
2199     if (VolumeWriteable(vp) && V_dontSalvage(vp) == 0) {
2200 #ifndef AFS_DEMAND_ATTACH_FS
2201         /* This is a hack: by temporarily setting the incore
2202          * dontSalvage flag ON, the volume will be put back on the
2203          * Update list (with dontSalvage OFF again).  It will then
2204          * come back in N minutes with DONT_SALVAGE eventually
2205          * set.  This is the way that volumes that have never had
2206          * it set get it set; or that volumes that have been
2207          * offline without DONT SALVAGE having been set also
2208          * eventually get it set */
2209         V_dontSalvage(vp) = DONT_SALVAGE;
2210 #endif /* !AFS_DEMAND_ATTACH_FS */
2211         VAddToVolumeUpdateList_r(ec, vp);
2212         if (*ec) {
2213             Log("VAttachVolume: Error adding volume %u to update list\n", vp->hashid);
2214             if (vp)
2215                 VPutVolume_r(vp);
2216             goto done;
2217         }
2218     }
2219     if (LogLevel)
2220         Log("VOnline:  volume %u (%s) attached and online\n", V_id(vp),
2221             V_name(vp));
2222   done:
2223     if (reserve) {
2224         VCancelReservation_r(nvp);
2225         reserve = 0;
2226     }
2227     if (*ec && (*ec != VOFFLINE) && (*ec != VSALVAGE)) {
2228         if (vp && !VIsErrorState(V_attachState(vp))) {
2229             VChangeState_r(vp, VOL_STATE_ERROR);
2230         }
2231         return NULL;
2232     } else {
2233         return vp;
2234     }
2235 }
2236 #endif /* AFS_DEMAND_ATTACH_FS */
2237
2238 /*
2239  * called without any locks held
2240  * returns with vol_glock_mutex held
2241  */
2242 private Volume * 
2243 attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * header,
2244         struct DiskPartition64 * partp, register Volume * vp, int isbusy, int mode)
2245 {
2246     vp->specialStatus = (byte) (isbusy ? VBUSY : 0);
2247     IH_INIT(vp->vnodeIndex[vLarge].handle, partp->device, header->parent,
2248             header->largeVnodeIndex);
2249     IH_INIT(vp->vnodeIndex[vSmall].handle, partp->device, header->parent,
2250             header->smallVnodeIndex);
2251     IH_INIT(vp->diskDataHandle, partp->device, header->parent,
2252             header->volumeInfo);
2253     IH_INIT(vp->linkHandle, partp->device, header->parent, header->linkTable);
2254     vp->shuttingDown = 0;
2255     vp->goingOffline = 0;
2256     vp->nUsers = 1;
2257 #ifdef AFS_DEMAND_ATTACH_FS
2258     vp->stats.last_attach = FT_ApproxTime();
2259     vp->stats.attaches++;
2260 #endif
2261
2262     VOL_LOCK;
2263     IncUInt64(&VStats.attaches);
2264     vp->cacheCheck = ++VolumeCacheCheck;
2265     /* just in case this ever rolls over */
2266     if (!vp->cacheCheck)
2267         vp->cacheCheck = ++VolumeCacheCheck;
2268     GetVolumeHeader(vp);
2269     VOL_UNLOCK;
2270
2271 #if defined(AFS_DEMAND_ATTACH_FS) && defined(FSSYNC_BUILD_CLIENT)
2272     /* demand attach changes the V_PEEK mechanism
2273      *
2274      * we can now suck the current disk data structure over
2275      * the fssync interface without going to disk
2276      *
2277      * (technically, we don't need to restrict this feature
2278      *  to demand attach fileservers.  However, I'm trying
2279      *  to limit the number of common code changes)
2280      */
2281     if (programType != fileServer && mode == V_PEEK) {
2282         SYNC_response res;
2283         res.payload.len = sizeof(VolumeDiskData);
2284         res.payload.buf = &vp->header->diskstuff;
2285
2286         if (FSYNC_VolOp(volumeId,
2287                         VPartitionPath(partp),
2288                         FSYNC_VOL_QUERY_HDR,
2289                         FSYNC_WHATEVER,
2290                         &res) == SYNC_OK) {
2291             goto disk_header_loaded;
2292         }
2293     }
2294 #endif /* AFS_DEMAND_ATTACH_FS && FSSYNC_BUILD_CLIENT */
2295     (void)ReadHeader(ec, V_diskDataHandle(vp), (char *)&V_disk(vp),
2296                      sizeof(V_disk(vp)), VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
2297
2298 #ifdef AFS_DEMAND_ATTACH_FS
2299     /* update stats */
2300     VOL_LOCK;
2301     IncUInt64(&VStats.hdr_loads);
2302     IncUInt64(&vp->stats.hdr_loads);
2303     VOL_UNLOCK;
2304 #endif /* AFS_DEMAND_ATTACH_FS */
2305     
2306     if (*ec) {
2307         Log("VAttachVolume: Error reading diskDataHandle vol header %s; error=%u\n", path, *ec);
2308     }
2309
2310  disk_header_loaded:
2311
2312 #ifdef AFS_DEMAND_ATTACH_FS
2313     if (!*ec) {
2314
2315         /* check for pending volume operations */
2316         if (vp->pending_vol_op) {
2317             /* see if the pending volume op requires exclusive access */
2318             if (!VVolOpLeaveOnline_r(vp, vp->pending_vol_op)) {
2319                 /* mark the volume down */
2320                 *ec = VOFFLINE;
2321                 VChangeState_r(vp, VOL_STATE_UNATTACHED);
2322                 if (V_offlineMessage(vp)[0] == '\0')
2323                     strlcpy(V_offlineMessage(vp),
2324                             "A volume utility is running.", 
2325                             sizeof(V_offlineMessage(vp)));
2326                 V_offlineMessage(vp)[sizeof(V_offlineMessage(vp)) - 1] = '\0';
2327
2328                 /* check to see if we should set the specialStatus flag */
2329                 if (VVolOpSetVBusy_r(vp, vp->pending_vol_op)) {
2330                     vp->specialStatus = VBUSY;
2331                 }
2332             }
2333         }
2334
2335         V_attachFlags(vp) |= VOL_HDR_LOADED;
2336         vp->stats.last_hdr_load = vp->stats.last_attach;
2337     }
2338 #endif /* AFS_DEMAND_ATTACH_FS */
2339
2340     if (!*ec) {
2341         struct IndexFileHeader iHead;
2342
2343 #if OPENAFS_VOL_STATS
2344         /*
2345          * We just read in the diskstuff part of the header.  If the detailed
2346          * volume stats area has not yet been initialized, we should bzero the
2347          * area and mark it as initialized.
2348          */
2349         if (!(V_stat_initialized(vp))) {
2350             memset((char *)(V_stat_area(vp)), 0, VOL_STATS_BYTES);
2351             V_stat_initialized(vp) = 1;
2352         }
2353 #endif /* OPENAFS_VOL_STATS */
2354
2355         (void)ReadHeader(ec, vp->vnodeIndex[vSmall].handle,
2356                          (char *)&iHead, sizeof(iHead),
2357                          SMALLINDEXMAGIC, SMALLINDEXVERSION);
2358
2359         if (*ec) {
2360             Log("VAttachVolume: Error reading smallVnode vol header %s; error=%u\n", path, *ec);
2361         }
2362     }
2363
2364     if (!*ec) {
2365         struct IndexFileHeader iHead;
2366
2367         (void)ReadHeader(ec, vp->vnodeIndex[vLarge].handle,
2368                          (char *)&iHead, sizeof(iHead),
2369                          LARGEINDEXMAGIC, LARGEINDEXVERSION);
2370
2371         if (*ec) {
2372             Log("VAttachVolume: Error reading largeVnode vol header %s; error=%u\n", path, *ec);
2373         }
2374     }
2375
2376 #ifdef AFS_NAMEI_ENV
2377     if (!*ec) {
2378         struct versionStamp stamp;
2379
2380         (void)ReadHeader(ec, V_linkHandle(vp), (char *)&stamp,
2381                          sizeof(stamp), LINKTABLEMAGIC, LINKTABLEVERSION);
2382
2383         if (*ec) {
2384             Log("VAttachVolume: Error reading namei vol header %s; error=%u\n", path, *ec);
2385         }
2386     }
2387 #endif /* AFS_NAMEI_ENV */
2388
2389 #if defined(AFS_DEMAND_ATTACH_FS)
2390     if (*ec && ((*ec != VOFFLINE) || (V_attachState(vp) != VOL_STATE_UNATTACHED))) {
2391         VOL_LOCK;
2392         if (programType == fileServer) {
2393             VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2394             vp->nUsers = 0;
2395         } else {
2396             Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%u\n", path, *ec);
2397             FreeVolume(vp);
2398             *ec = VSALVAGE;
2399         }
2400         return NULL;
2401     } else if (*ec) {
2402         /* volume operation in progress */
2403         VOL_LOCK;
2404         return NULL;
2405     }
2406 #else /* AFS_DEMAND_ATTACH_FS */
2407     if (*ec) {
2408         Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%u\n", path, *ec);
2409         VOL_LOCK;
2410         FreeVolume(vp);
2411         return NULL;
2412     }
2413 #endif /* AFS_DEMAND_ATTACH_FS */
2414
2415     if (V_needsSalvaged(vp)) {
2416         if (vp->specialStatus)
2417             vp->specialStatus = 0;
2418         VOL_LOCK;
2419 #if defined(AFS_DEMAND_ATTACH_FS)
2420         if (programType == fileServer) {
2421             VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
2422             vp->nUsers = 0;
2423         } else {
2424             Log("VAttachVolume: volume salvage flag is ON for %s; volume needs salvage\n", path);
2425             FreeVolume(vp);
2426             *ec = VSALVAGE;
2427         }
2428 #else /* AFS_DEMAND_ATTACH_FS */
2429         FreeVolume(vp);
2430         *ec = VSALVAGE;
2431 #endif /* AFS_DEMAND_ATTACH_FS */
2432         return NULL;
2433     }
2434
2435     VOL_LOCK;
2436     if (programType == fileServer) {
2437 #ifndef FAST_RESTART
2438         if (V_inUse(vp) && VolumeWriteable(vp)) {
2439             if (!V_needsSalvaged(vp)) {
2440                 V_needsSalvaged(vp) = 1;
2441                 VUpdateVolume_r(ec, vp, 0);
2442             }
2443 #if defined(AFS_DEMAND_ATTACH_FS)
2444             VRequestSalvage_r(ec, vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
2445             vp->nUsers = 0;
2446 #else /* AFS_DEMAND_ATTACH_FS */
2447             Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
2448             FreeVolume(vp);
2449             *ec = VSALVAGE;
2450 #endif /* AFS_DEMAND_ATTACH_FS */
2451             return NULL;
2452         }
2453 #endif /* FAST_RESTART */
2454
2455         if (V_destroyMe(vp) == DESTROY_ME) {
2456 #if defined(AFS_DEMAND_ATTACH_FS)
2457             /* schedule a salvage so the volume goes away on disk */
2458             VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2459             VChangeState_r(vp, VOL_STATE_ERROR);
2460             vp->nUsers = 0;
2461 #endif /* AFS_DEMAND_ATTACH_FS */
2462             FreeVolume(vp);
2463             Log("VAttachVolume: volume %s is junk; it should be destroyed at next salvage\n", path);
2464             *ec = VNOVOL;
2465             return NULL;
2466         }
2467     }
2468
2469     vp->nextVnodeUnique = V_uniquifier(vp);
2470     vp->vnodeIndex[vSmall].bitmap = vp->vnodeIndex[vLarge].bitmap = NULL;
2471 #ifndef BITMAP_LATER
2472     if (programType == fileServer && VolumeWriteable(vp)) {
2473         int i;
2474         for (i = 0; i < nVNODECLASSES; i++) {
2475             VGetBitmap_r(ec, vp, i);
2476             if (*ec) {
2477 #ifdef AFS_DEMAND_ATTACH_FS
2478                 VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2479                 vp->nUsers = 0;
2480 #else /* AFS_DEMAND_ATTACH_FS */
2481                 FreeVolume(vp);
2482 #endif /* AFS_DEMAND_ATTACH_FS */
2483                 Log("VAttachVolume: error getting bitmap for volume (%s)\n",
2484                     path);
2485                 return NULL;
2486             }
2487         }
2488     }
2489 #endif /* BITMAP_LATER */
2490
2491     if (programType == fileServer) {
2492         if (vp->specialStatus)
2493             vp->specialStatus = 0;
2494         if (V_blessed(vp) && V_inService(vp) && !V_needsSalvaged(vp)) {
2495             V_inUse(vp) = fileServer;
2496             V_offlineMessage(vp)[0] = '\0';
2497         }
2498     } else {
2499         V_inUse(vp) = programType;
2500         V_checkoutMode(vp) = mode;
2501     }
2502
2503     AddVolumeToHashTable(vp, V_id(vp));
2504 #ifdef AFS_DEMAND_ATTACH_FS
2505     AddVolumeToVByPList_r(vp);
2506     VLRU_Add_r(vp);
2507     if ((programType != fileServer) ||
2508         (V_inUse(vp) == fileServer)) {
2509         VChangeState_r(vp, VOL_STATE_ATTACHED);
2510     } else {
2511         VChangeState_r(vp, VOL_STATE_UNATTACHED);
2512     }
2513 #endif
2514     return vp;
2515 }
2516
2517 /* Attach an existing volume.
2518    The volume also normally goes online at this time.
2519    An offline volume must be reattached to make it go online.
2520  */
2521
2522 Volume *
2523 VAttachVolume(Error * ec, VolumeId volumeId, int mode)
2524 {
2525     Volume *retVal;
2526     VOL_LOCK;
2527     retVal = VAttachVolume_r(ec, volumeId, mode);
2528     VOL_UNLOCK;
2529     return retVal;
2530 }
2531
2532 Volume *
2533 VAttachVolume_r(Error * ec, VolumeId volumeId, int mode)
2534 {
2535     char *part, *name;
2536     GetVolumePath(ec, volumeId, &part, &name);
2537     if (*ec) {
2538         register Volume *vp;
2539         Error error;
2540         vp = VGetVolume_r(&error, volumeId);
2541         if (vp) {
2542             assert(V_inUse(vp) == 0);
2543             VDetachVolume_r(ec, vp);
2544         }
2545         return NULL;
2546     }
2547     return VAttachVolumeByName_r(ec, part, name, mode);
2548 }
2549
2550 /* Increment a reference count to a volume, sans context swaps.  Requires
2551  * possibly reading the volume header in from the disk, since there's
2552  * an invariant in the volume package that nUsers>0 ==> vp->header is valid.
2553  *
2554  * N.B. This call can fail if we can't read in the header!!  In this case
2555  * we still guarantee we won't context swap, but the ref count won't be
2556  * incremented (otherwise we'd violate the invariant).
2557  */
2558 /* NOTE: with the demand attach fileserver extensions, the global lock
2559  * is dropped within VHold */
2560 #ifdef AFS_DEMAND_ATTACH_FS
2561 static int
2562 VHold_r(register Volume * vp)
2563 {
2564     Error error;
2565
2566     VCreateReservation_r(vp);
2567     VWaitExclusiveState_r(vp);
2568
2569     LoadVolumeHeader(&error, vp);
2570     if (error) {
2571         VCancelReservation_r(vp);
2572         return error;
2573     }
2574     vp->nUsers++;
2575     VCancelReservation_r(vp);
2576     return 0;
2577 }
2578 #else /* AFS_DEMAND_ATTACH_FS */
2579 static int
2580 VHold_r(register Volume * vp)
2581 {
2582     Error error;
2583
2584     LoadVolumeHeader(&error, vp);
2585     if (error)
2586         return error;
2587     vp->nUsers++;
2588     return 0;
2589 }
2590 #endif /* AFS_DEMAND_ATTACH_FS */
2591
2592 static int
2593 VHold(register Volume * vp)
2594 {
2595     int retVal;
2596     VOL_LOCK;
2597     retVal = VHold_r(vp);
2598     VOL_UNLOCK;
2599     return retVal;
2600 }
2601
2602
2603 /***************************************************/
2604 /* get and put volume routines                     */
2605 /***************************************************/
2606
2607 /**
2608  * put back a heavyweight reference to a volume object.
2609  *
2610  * @param[in] vp  volume object pointer
2611  *
2612  * @pre VOL_LOCK held
2613  *
2614  * @post heavyweight volume reference put back.
2615  *       depending on state, volume may have been taken offline,
2616  *       detached, salvaged, freed, etc.
2617  *
2618  * @internal volume package internal use only
2619  */
2620 void
2621 VPutVolume_r(register Volume * vp)
2622 {
2623     assert(--vp->nUsers >= 0);
2624     if (vp->nUsers == 0) {
2625         VCheckOffline(vp);
2626         ReleaseVolumeHeader(vp->header);
2627 #ifdef AFS_DEMAND_ATTACH_FS
2628         if (!VCheckDetach(vp)) {
2629             VCheckSalvage(vp);
2630             VCheckFree(vp);
2631         }
2632 #else /* AFS_DEMAND_ATTACH_FS */
2633         VCheckDetach(vp);
2634 #endif /* AFS_DEMAND_ATTACH_FS */
2635     }
2636 }
2637
2638 void
2639 VPutVolume(register Volume * vp)
2640 {
2641     VOL_LOCK;
2642     VPutVolume_r(vp);
2643     VOL_UNLOCK;
2644 }
2645
2646
2647 /* Get a pointer to an attached volume.  The pointer is returned regardless
2648    of whether or not the volume is in service or on/off line.  An error
2649    code, however, is returned with an indication of the volume's status */
2650 Volume *
2651 VGetVolume(Error * ec, Error * client_ec, VolId volumeId)
2652 {
2653     Volume *retVal;
2654     VOL_LOCK;
2655     retVal = GetVolume(ec, client_ec, volumeId, NULL, 0);
2656     VOL_UNLOCK;
2657     return retVal;
2658 }
2659
2660 Volume *
2661 VGetVolume_r(Error * ec, VolId volumeId)
2662 {
2663     return GetVolume(ec, NULL, volumeId, NULL, 0);
2664 }
2665
2666 /* try to get a volume we've previously looked up */
2667 /* for demand attach fs, caller MUST NOT hold a ref count on vp */
2668 Volume * 
2669 VGetVolumeByVp_r(Error * ec, Volume * vp)
2670 {
2671     return GetVolume(ec, NULL, vp->hashid, vp, 0);
2672 }
2673
2674 /* private interface for getting a volume handle
2675  * volumeId must be provided.
2676  * hint is an optional parameter to speed up hash lookups
2677  * flags is not used at this time
2678  */
2679 /* for demand attach fs, caller MUST NOT hold a ref count on hint */
2680 static Volume *
2681 GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flags)
2682 {
2683     Volume *vp = hint;
2684     /* pull this profiling/debugging code out of regular builds */
2685 #ifdef notdef
2686 #define VGET_CTR_INC(x) x++
2687     unsigned short V0 = 0, V1 = 0, V2 = 0, V3 = 0, V5 = 0, V6 =
2688         0, V7 = 0, V8 = 0, V9 = 0;
2689     unsigned short V10 = 0, V11 = 0, V12 = 0, V13 = 0, V14 = 0, V15 = 0;
2690 #else
2691 #define VGET_CTR_INC(x)
2692 #endif
2693 #ifdef AFS_DEMAND_ATTACH_FS
2694     Volume *avp, * rvp = hint;
2695 #endif
2696
2697 #ifdef AFS_DEMAND_ATTACH_FS
2698     if (rvp) {
2699         VCreateReservation_r(rvp);
2700     }
2701 #endif /* AFS_DEMAND_ATTACH_FS */
2702
2703     for (;;) {
2704         *ec = 0;
2705         if (client_ec)
2706             *client_ec = 0;
2707         VGET_CTR_INC(V0);
2708
2709         vp = VLookupVolume_r(ec, volumeId, vp);
2710         if (*ec) {
2711             vp = NULL;
2712             break;
2713         }
2714
2715 #ifdef AFS_DEMAND_ATTACH_FS
2716         if (rvp && (rvp != vp)) {
2717             /* break reservation on old vp */
2718             VCancelReservation_r(rvp);
2719             rvp = NULL;
2720         }
2721 #endif /* AFS_DEMAND_ATTACH_FS */
2722
2723         if (!vp) {
2724             VGET_CTR_INC(V1);
2725             if (VInit < 2) {
2726                 VGET_CTR_INC(V2);
2727                 /* Until we have reached an initialization level of 2
2728                  * we don't know whether this volume exists or not.
2729                  * We can't sleep and retry later because before a volume
2730                  * is attached, the caller tries to get it first.  Just
2731                  * return VOFFLINE and the caller can choose whether to
2732                  * retry the command or not. */
2733                 *ec = VOFFLINE;
2734                 break;
2735             }
2736
2737             *ec = VNOVOL;
2738             break;
2739         }
2740
2741         VGET_CTR_INC(V3);
2742         IncUInt64(&VStats.hdr_gets);
2743         
2744 #ifdef AFS_DEMAND_ATTACH_FS
2745         /* block if someone else is performing an exclusive op on this volume */
2746         if (rvp != vp) {
2747             rvp = vp;
2748             VCreateReservation_r(rvp);
2749         }
2750         VWaitExclusiveState_r(vp);
2751
2752         /* short circuit with VNOVOL in the following circumstances:
2753          *
2754          *   VOL_STATE_ERROR
2755          *   VOL_STATE_SHUTTING_DOWN
2756          */
2757         if ((V_attachState(vp) == VOL_STATE_ERROR) ||
2758             (V_attachState(vp) == VOL_STATE_SHUTTING_DOWN)) {
2759             *ec = VNOVOL;
2760             vp = NULL;
2761             break;
2762         }
2763
2764         /*
2765          * short circuit with VOFFLINE in the following circumstances:
2766          *
2767          *   VOL_STATE_UNATTACHED
2768          */
2769        if (V_attachState(vp) == VOL_STATE_UNATTACHED) {
2770            *ec = VOFFLINE;
2771            vp = NULL;
2772            break;
2773        }
2774
2775         /* allowable states:
2776          *   UNATTACHED
2777          *   PREATTACHED
2778          *   ATTACHED
2779          *   GOING_OFFLINE
2780          *   SALVAGING
2781          */
2782
2783         if (vp->salvage.requested) {
2784             VUpdateSalvagePriority_r(vp);
2785         }
2786
2787         if (V_attachState(vp) == VOL_STATE_PREATTACHED) {
2788             avp = VAttachVolumeByVp_r(ec, vp, 0);
2789             if (avp) {
2790                 if (vp != avp) {
2791                     /* VAttachVolumeByVp_r can return a pointer
2792                      * != the vp passed to it under certain
2793                      * conditions; make sure we don't leak
2794                      * reservations if that happens */
2795                     vp = avp;
2796                     VCancelReservation_r(rvp);
2797                     rvp = avp;
2798                     VCreateReservation_r(rvp);
2799                 }
2800                 VPutVolume_r(avp);
2801             }
2802             if (*ec) {
2803                 int endloop = 0;
2804                 switch (*ec) {
2805                 case VSALVAGING:
2806                     break;
2807                 case VOFFLINE:
2808                     if (!vp->pending_vol_op) {
2809                         endloop = 1;
2810                     }
2811                     break;
2812                 default:
2813                     *ec = VNOVOL;
2814                     endloop = 1;
2815                 }
2816                 if (endloop) {
2817                     vp = NULL;
2818                     break;
2819                 }
2820             }
2821         }
2822
2823         if ((V_attachState(vp) == VOL_STATE_SALVAGING) ||
2824             (*ec == VSALVAGING)) {
2825             if (client_ec) {
2826                 /* see CheckVnode() in afsfileprocs.c for an explanation
2827                  * of this error code logic */
2828                 afs_uint32 now = FT_ApproxTime();
2829                 if ((vp->stats.last_salvage + (10 * 60)) >= now) {
2830                     *client_ec = VBUSY;
2831                 } else {
2832                     *client_ec = VRESTARTING;
2833                 }
2834             }
2835             *ec = VSALVAGING;
2836             vp = NULL;
2837             break;
2838         }
2839 #endif
2840
2841         LoadVolumeHeader(ec, vp);
2842         if (*ec) {
2843             VGET_CTR_INC(V6);
2844             /* Only log the error if it was a totally unexpected error.  Simply
2845              * a missing inode is likely to be caused by the volume being deleted */
2846             if (errno != ENXIO || LogLevel)
2847                 Log("Volume %u: couldn't reread volume header\n",
2848                     vp->hashid);
2849 #ifdef AFS_DEMAND_ATTACH_FS
2850             if (programType == fileServer) {
2851                 VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2852             } else {
2853                 FreeVolume(vp);
2854                 vp = NULL;
2855             }
2856 #else /* AFS_DEMAND_ATTACH_FS */
2857             FreeVolume(vp);
2858             vp = NULL;
2859 #endif /* AFS_DEMAND_ATTACH_FS */
2860             break;
2861         }
2862
2863 #ifdef AFS_DEMAND_ATTACH_FS
2864         /*
2865          * this test MUST happen after the volume header is loaded
2866          */
2867         if (vp->pending_vol_op && !VVolOpLeaveOnline_r(vp, vp->pending_vol_op)) {
2868             if (client_ec) {
2869                 /* see CheckVnode() in afsfileprocs.c for an explanation
2870                  * of this error code logic */
2871                 afs_uint32 now = FT_ApproxTime();
2872                 if ((vp->stats.last_vol_op + (10 * 60)) >= now) {
2873                     *client_ec = VBUSY;
2874                 } else {
2875                     *client_ec = VRESTARTING;
2876                 }
2877             }
2878             *ec = VOFFLINE;
2879             ReleaseVolumeHeader(vp->header);
2880             vp = NULL;
2881             break;
2882         }
2883 #endif /* AFS_DEMAND_ATTACH_FS */
2884         
2885         VGET_CTR_INC(V7);
2886         if (vp->shuttingDown) {
2887             VGET_CTR_INC(V8);
2888             *ec = VNOVOL;
2889             vp = NULL;
2890             break;
2891         }
2892
2893         if (programType == fileServer) {
2894             VGET_CTR_INC(V9);
2895             if (vp->goingOffline) {
2896                 VGET_CTR_INC(V10);
2897 #ifdef AFS_DEMAND_ATTACH_FS
2898                 /* wait for the volume to go offline */
2899                 if (V_attachState(vp) == VOL_STATE_GOING_OFFLINE) {
2900                     VWaitStateChange_r(vp);
2901                 }
2902 #elif defined(AFS_PTHREAD_ENV)
2903                 VOL_CV_WAIT(&vol_put_volume_cond);
2904 #else /* AFS_PTHREAD_ENV */
2905                 LWP_WaitProcess(VPutVolume);
2906 #endif /* AFS_PTHREAD_ENV */
2907                 continue;
2908             }
2909             if (vp->specialStatus) {
2910                 VGET_CTR_INC(V11);
2911                 *ec = vp->specialStatus;
2912             } else if (V_inService(vp) == 0 || V_blessed(vp) == 0) {
2913                 VGET_CTR_INC(V12);
2914                 *ec = VNOVOL;
2915             } else if (V_inUse(vp) == 0) {
2916                 VGET_CTR_INC(V13);
2917                 *ec = VOFFLINE;
2918             } else {
2919                 VGET_CTR_INC(V14);
2920             }
2921         }
2922         break;
2923     }
2924     VGET_CTR_INC(V15);
2925
2926 #ifdef AFS_DEMAND_ATTACH_FS
2927     /* if no error, bump nUsers */
2928     if (vp) {
2929         vp->nUsers++;
2930         VLRU_UpdateAccess_r(vp);
2931     }
2932     if (rvp) {
2933         VCancelReservation_r(rvp);
2934         rvp = NULL;
2935     }
2936     if (client_ec && !*client_ec) {
2937         *client_ec = *ec;
2938     }
2939 #else /* AFS_DEMAND_ATTACH_FS */
2940     /* if no error, bump nUsers */
2941     if (vp) {
2942         vp->nUsers++;
2943     }
2944     if (client_ec) {
2945         *client_ec = *ec;
2946     }
2947 #endif /* AFS_DEMAND_ATTACH_FS */
2948
2949     assert(vp || *ec);
2950     return vp;
2951 }
2952
2953
2954 /***************************************************/
2955 /* Volume offline/detach routines                  */
2956 /***************************************************/
2957
2958 /* caller MUST hold a heavyweight ref on vp */
2959 #ifdef AFS_DEMAND_ATTACH_FS
2960 void
2961 VTakeOffline_r(register Volume * vp)
2962 {
2963     Error error;
2964
2965     assert(vp->nUsers > 0);
2966     assert(programType == fileServer);
2967
2968     VCreateReservation_r(vp);
2969     VWaitExclusiveState_r(vp);
2970
2971     vp->goingOffline = 1;
2972     V_needsSalvaged(vp) = 1;
2973
2974     VRequestSalvage_r(&error, vp, SALVSYNC_ERROR, 0);
2975     VCancelReservation_r(vp);
2976 }
2977 #else /* AFS_DEMAND_ATTACH_FS */
2978 void
2979 VTakeOffline_r(register Volume * vp)
2980 {
2981     assert(vp->nUsers > 0);
2982     assert(programType == fileServer);
2983
2984     vp->goingOffline = 1;
2985     V_needsSalvaged(vp) = 1;
2986 }
2987 #endif /* AFS_DEMAND_ATTACH_FS */
2988
2989 void
2990 VTakeOffline(register Volume * vp)
2991 {
2992     VOL_LOCK;
2993     VTakeOffline_r(vp);
2994     VOL_UNLOCK;
2995 }
2996
2997 /**
2998  * force a volume offline.
2999  *
3000  * @param[in] vp     volume object pointer
3001  * @param[in] flags  flags (see note below)
3002  *
3003  * @note the flag VOL_FORCEOFF_NOUPDATE is a recursion control flag
3004  *       used when VUpdateVolume_r needs to call VForceOffline_r
3005  *       (which in turn would normally call VUpdateVolume_r)
3006  *
3007  * @see VUpdateVolume_r
3008  *
3009  * @pre VOL_LOCK must be held.
3010  *      for DAFS, caller must hold ref.
3011  *
3012  * @note for DAFS, it _is safe_ to call this function from an
3013  *       exclusive state
3014  *
3015  * @post needsSalvaged flag is set.
3016  *       for DAFS, salvage is requested.
3017  *       no further references to the volume through the volume 
3018  *       package will be honored.
3019  *       all file descriptor and vnode caches are invalidated.
3020  *
3021  * @warning this is a heavy-handed interface.  it results in
3022  *          a volume going offline regardless of the current 
3023  *          reference count state.
3024  *
3025  * @internal  volume package internal use only
3026  */
3027 void
3028 VForceOffline_r(Volume * vp, int flags)
3029 {
3030     Error error;
3031     if (!V_inUse(vp)) {
3032 #ifdef AFS_DEMAND_ATTACH_FS
3033         VChangeState_r(vp, VOL_STATE_ERROR);
3034 #endif
3035         return;
3036     }
3037
3038     strcpy(V_offlineMessage(vp),
3039            "Forced offline due to internal error: volume needs to be salvaged");
3040     Log("Volume %u forced offline:  it needs salvaging!\n", V_id(vp));
3041
3042     V_inUse(vp) = 0;
3043     vp->goingOffline = 0;
3044     V_needsSalvaged(vp) = 1;
3045     if (!(flags & VOL_FORCEOFF_NOUPDATE)) {
3046         VUpdateVolume_r(&error, vp, VOL_UPDATE_NOFORCEOFF);
3047     }
3048
3049 #ifdef AFS_DEMAND_ATTACH_FS
3050     VRequestSalvage_r(&error, vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
3051 #endif /* AFS_DEMAND_ATTACH_FS */
3052
3053 #ifdef AFS_PTHREAD_ENV
3054     assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3055 #else /* AFS_PTHREAD_ENV */
3056     LWP_NoYieldSignal(VPutVolume);
3057 #endif /* AFS_PTHREAD_ENV */
3058
3059     VReleaseVolumeHandles_r(vp);
3060 }
3061
3062 /**
3063  * force a volume offline.
3064  *
3065  * @param[in] vp  volume object pointer
3066  *
3067  * @see VForceOffline_r
3068  */
3069 void
3070 VForceOffline(Volume * vp)
3071 {
3072     VOL_LOCK;
3073     VForceOffline_r(vp, 0);
3074     VOL_UNLOCK;
3075 }
3076
3077 /* The opposite of VAttachVolume.  The volume header is written to disk, with
3078    the inUse bit turned off.  A copy of the header is maintained in memory,
3079    however (which is why this is VOffline, not VDetach).
3080  */
3081 void
3082 VOffline_r(Volume * vp, char *message)
3083 {
3084     Error error;
3085     VolumeId vid = V_id(vp);
3086
3087     assert(programType != volumeUtility);
3088     if (!V_inUse(vp)) {
3089         VPutVolume_r(vp);
3090         return;
3091     }
3092     if (V_offlineMessage(vp)[0] == '\0')
3093         strncpy(V_offlineMessage(vp), message, sizeof(V_offlineMessage(vp)));
3094     V_offlineMessage(vp)[sizeof(V_offlineMessage(vp)) - 1] = '\0';
3095
3096     vp->goingOffline = 1;
3097 #ifdef AFS_DEMAND_ATTACH_FS
3098     VChangeState_r(vp, VOL_STATE_GOING_OFFLINE);
3099     VCreateReservation_r(vp);
3100     VPutVolume_r(vp);
3101
3102     /* wait for the volume to go offline */
3103     if (V_attachState(vp) == VOL_STATE_GOING_OFFLINE) {
3104         VWaitStateChange_r(vp);
3105     }
3106     VCancelReservation_r(vp);
3107 #else /* AFS_DEMAND_ATTACH_FS */
3108     VPutVolume_r(vp);
3109     vp = VGetVolume_r(&error, vid);     /* Wait for it to go offline */
3110     if (vp)                     /* In case it was reattached... */
3111         VPutVolume_r(vp);
3112 #endif /* AFS_DEMAND_ATTACH_FS */
3113 }
3114
3115 void
3116 VOffline(Volume * vp, char *message)
3117 {
3118     VOL_LOCK;
3119     VOffline_r(vp, message);
3120     VOL_UNLOCK;
3121 }
3122
3123 /* This gets used for the most part by utility routines that don't want
3124  * to keep all the volume headers around.  Generally, the file server won't
3125  * call this routine, because then the offline message in the volume header
3126  * (or other information) won't be available to clients. For NAMEI, also
3127  * close the file handles.  However, the fileserver does call this during
3128  * an attach following a volume operation.
3129  */
3130 void
3131 VDetachVolume_r(Error * ec, Volume * vp)
3132 {
3133     VolumeId volume;
3134     struct DiskPartition64 *tpartp;
3135     int notifyServer, useDone = FSYNC_VOL_ON;
3136
3137     *ec = 0;                    /* always "succeeds" */
3138     if (programType == volumeUtility) {
3139         notifyServer = vp->needsPutBack;
3140         if (V_destroyMe(vp) == DESTROY_ME)
3141             useDone = FSYNC_VOL_DONE;
3142 #ifdef AFS_DEMAND_ATTACH_FS
3143         else if (!V_blessed(vp) || !V_inService(vp))
3144             useDone = FSYNC_VOL_LEAVE_OFF;
3145 #endif
3146     }
3147     tpartp = vp->partition;
3148     volume = V_id(vp);
3149     DeleteVolumeFromHashTable(vp);
3150     vp->shuttingDown = 1;
3151 #ifdef AFS_DEMAND_ATTACH_FS
3152     DeleteVolumeFromVByPList_r(vp);
3153     VLRU_Delete_r(vp);
3154     VChangeState_r(vp, VOL_STATE_SHUTTING_DOWN);
3155 #endif /* AFS_DEMAND_ATTACH_FS */
3156     VPutVolume_r(vp);
3157     /* Will be detached sometime in the future--this is OK since volume is offline */
3158
3159     /* XXX the following code should really be moved to VCheckDetach() since the volume
3160      * is not technically detached until the refcounts reach zero
3161      */
3162 #ifdef FSSYNC_BUILD_CLIENT
3163     if (programType == volumeUtility && notifyServer) {
3164         /* 
3165          * Note:  The server is not notified in the case of a bogus volume 
3166          * explicitly to make it possible to create a volume, do a partial 
3167          * restore, then abort the operation without ever putting the volume 
3168          * online.  This is essential in the case of a volume move operation 
3169          * between two partitions on the same server.  In that case, there 
3170          * would be two instances of the same volume, one of them bogus, 
3171          * which the file server would attempt to put on line 
3172          */
3173         FSYNC_VolOp(volume, tpartp->name, useDone, 0, NULL);
3174         /* XXX this code path is only hit by volume utilities, thus
3175          * V_BreakVolumeCallbacks will always be NULL.  if we really
3176          * want to break callbacks in this path we need to use FSYNC_VolOp() */
3177 #ifdef notdef
3178         /* Dettaching it so break all callbacks on it */
3179         if (V_BreakVolumeCallbacks) {
3180             Log("volume %u detached; breaking all call backs\n", volume);
3181             (*V_BreakVolumeCallbacks) (volume);
3182         }
3183 #endif
3184     }
3185 #endif /* FSSYNC_BUILD_CLIENT */
3186 }
3187
3188 void
3189 VDetachVolume(Error * ec, Volume * vp)
3190 {
3191     VOL_LOCK;
3192     VDetachVolume_r(ec, vp);
3193     VOL_UNLOCK;
3194 }
3195
3196
3197 /***************************************************/
3198 /* Volume fd/inode handle closing routines         */
3199 /***************************************************/
3200
3201 /* For VDetachVolume, we close all cached file descriptors, but keep
3202  * the Inode handles in case we need to read from a busy volume.
3203  */
3204 /* for demand attach, caller MUST hold ref count on vp */
3205 static void
3206 VCloseVolumeHandles_r(Volume * vp)
3207 {
3208 #ifdef AFS_DEMAND_ATTACH_FS
3209     VolState state_save;
3210
3211     state_save = VChangeState_r(vp, VOL_STATE_OFFLINING);
3212 #endif
3213
3214     /* demand attach fs
3215      *
3216      * XXX need to investigate whether we can perform
3217      * DFlushVolume outside of vol_glock_mutex... 
3218      *
3219      * VCloseVnodeFiles_r drops the glock internally */
3220     DFlushVolume(V_id(vp));
3221     VCloseVnodeFiles_r(vp);
3222
3223 #ifdef AFS_DEMAND_ATTACH_FS
3224     VOL_UNLOCK;
3225 #endif
3226
3227     /* Too time consuming and unnecessary for the volserver */
3228     if (programType != volumeUtility) {
3229         IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
3230         IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
3231         IH_CONDSYNC(vp->diskDataHandle);
3232 #ifdef AFS_NT40_ENV
3233         IH_CONDSYNC(vp->linkHandle);
3234 #endif /* AFS_NT40_ENV */
3235     }
3236
3237     IH_REALLYCLOSE(vp->vnodeIndex[vLarge].handle);
3238     IH_REALLYCLOSE(vp->vnodeIndex[vSmall].handle);
3239     IH_REALLYCLOSE(vp->diskDataHandle);
3240     IH_REALLYCLOSE(vp->linkHandle);
3241
3242 #ifdef AFS_DEMAND_ATTACH_FS
3243     VOL_LOCK;
3244     VChangeState_r(vp, state_save);
3245 #endif
3246 }
3247
3248 /* For both VForceOffline and VOffline, we close all relevant handles.
3249  * For VOffline, if we re-attach the volume, the files may possible be
3250  * different than before. 
3251  */
3252 /* for demand attach, caller MUST hold a ref count on vp */
3253 static void
3254 VReleaseVolumeHandles_r(Volume * vp)
3255 {
3256 #ifdef AFS_DEMAND_ATTACH_FS
3257     VolState state_save;
3258
3259     state_save = VChangeState_r(vp, VOL_STATE_DETACHING);
3260 #endif
3261
3262     /* XXX need to investigate whether we can perform
3263      * DFlushVolume outside of vol_glock_mutex... */
3264     DFlushVolume(V_id(vp));
3265
3266     VReleaseVnodeFiles_r(vp); /* releases the glock internally */
3267
3268 #ifdef AFS_DEMAND_ATTACH_FS
3269     VOL_UNLOCK;
3270 #endif
3271
3272     /* Too time consuming and unnecessary for the volserver */
3273     if (programType != volumeUtility) {
3274         IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
3275         IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
3276         IH_CONDSYNC(vp->diskDataHandle);
3277 #ifdef AFS_NT40_ENV
3278         IH_CONDSYNC(vp->linkHandle);
3279 #endif /* AFS_NT40_ENV */
3280     }
3281
3282     IH_RELEASE(vp->vnodeIndex[vLarge].handle);
3283     IH_RELEASE(vp->vnodeIndex[vSmall].handle);
3284     IH_RELEASE(vp->diskDataHandle);
3285     IH_RELEASE(vp->linkHandle);
3286
3287 #ifdef AFS_DEMAND_ATTACH_FS
3288     VOL_LOCK;
3289     VChangeState_r(vp, state_save);
3290 #endif
3291 }
3292
3293
3294 /***************************************************/
3295 /* Volume write and fsync routines                 */
3296 /***************************************************/
3297
3298 void
3299 VUpdateVolume_r(Error * ec, Volume * vp, int flags)
3300 {
3301 #ifdef AFS_DEMAND_ATTACH_FS
3302     VolState state_save;
3303
3304     if (flags & VOL_UPDATE_WAIT) {
3305         VCreateReservation_r(vp);
3306         VWaitExclusiveState_r(vp);
3307     }
3308 #endif
3309
3310     *ec = 0;
3311     if (programType == fileServer)
3312         V_uniquifier(vp) =
3313             (V_inUse(vp) ? V_nextVnodeUnique(vp) +
3314              200 : V_nextVnodeUnique(vp));
3315
3316 #ifdef AFS_DEMAND_ATTACH_FS
3317     state_save = VChangeState_r(vp, VOL_STATE_UPDATING);
3318     VOL_UNLOCK;
3319 #endif
3320
3321     WriteVolumeHeader_r(ec, vp);
3322
3323 #ifdef AFS_DEMAND_ATTACH_FS
3324     VOL_LOCK;
3325     VChangeState_r(vp, state_save);
3326     if (flags & VOL_UPDATE_WAIT) {
3327         VCancelReservation_r(vp);
3328     }
3329 #endif
3330
3331     if (*ec) {
3332         Log("VUpdateVolume: error updating volume header, volume %u (%s)\n",
3333             V_id(vp), V_name(vp));
3334         /* try to update on-disk header, 
3335          * while preventing infinite recursion */
3336         if (!(flags & VOL_UPDATE_NOFORCEOFF)) {
3337             VForceOffline_r(vp, VOL_FORCEOFF_NOUPDATE);
3338         }
3339     }
3340 }
3341
3342 void
3343 VUpdateVolume(Error * ec, Volume * vp)
3344 {
3345     VOL_LOCK;
3346     VUpdateVolume_r(ec, vp, VOL_UPDATE_WAIT);
3347     VOL_UNLOCK;
3348 }
3349
3350 void
3351 VSyncVolume_r(Error * ec, Volume * vp, int flags)
3352 {
3353     FdHandle_t *fdP;
3354     int code;
3355 #ifdef AFS_DEMAND_ATTACH_FS
3356     VolState state_save;
3357 #endif
3358
3359     if (flags & VOL_SYNC_WAIT) {
3360         VUpdateVolume_r(ec, vp, VOL_UPDATE_WAIT);
3361     } else {
3362         VUpdateVolume_r(ec, vp, 0);
3363     }
3364     if (!*ec) {
3365 #ifdef AFS_DEMAND_ATTACH_FS
3366         state_save = VChangeState_r(vp, VOL_STATE_UPDATING);
3367         VOL_UNLOCK;
3368 #endif
3369         fdP = IH_OPEN(V_diskDataHandle(vp));
3370         assert(fdP != NULL);
3371         code = FDH_SYNC(fdP);
3372         assert(code == 0);
3373         FDH_CLOSE(fdP);
3374 #ifdef AFS_DEMAND_ATTACH_FS
3375         VOL_LOCK;
3376         VChangeState_r(vp, state_save);
3377 #endif
3378     }
3379 }
3380
3381 void
3382 VSyncVolume(Error * ec, Volume * vp)
3383 {
3384     VOL_LOCK;
3385     VSyncVolume_r(ec, vp, VOL_SYNC_WAIT);
3386     VOL_UNLOCK;
3387 }
3388
3389
3390 /***************************************************/
3391 /* Volume dealloaction routines                    */
3392 /***************************************************/
3393
3394 #ifdef AFS_DEMAND_ATTACH_FS
3395 static void
3396 FreeVolume(Volume * vp)
3397 {
3398     /* free the heap space, iff it's safe.
3399      * otherwise, pull it out of the hash table, so it
3400      * will get deallocated when all refs to it go away */
3401     if (!VCheckFree(vp)) {
3402         DeleteVolumeFromHashTable(vp);
3403         DeleteVolumeFromVByPList_r(vp);
3404
3405         /* make sure we invalidate the header cache entry */
3406         FreeVolumeHeader(vp);
3407     }
3408 }
3409 #endif /* AFS_DEMAND_ATTACH_FS */
3410
3411 static void
3412 ReallyFreeVolume(Volume * vp)
3413 {
3414     int i;
3415     if (!vp)
3416         return;
3417 #ifdef AFS_DEMAND_ATTACH_FS
3418     /* debug */
3419     VChangeState_r(vp, VOL_STATE_FREED);
3420     if (vp->pending_vol_op)
3421         free(vp->pending_vol_op);
3422 #endif /* AFS_DEMAND_ATTACH_FS */
3423     for (i = 0; i < nVNODECLASSES; i++)
3424         if (vp->vnodeIndex[i].bitmap)
3425             free(vp->vnodeIndex[i].bitmap);
3426     FreeVolumeHeader(vp);
3427 #ifndef AFS_DEMAND_ATTACH_FS
3428     DeleteVolumeFromHashTable(vp);
3429 #endif /* AFS_DEMAND_ATTACH_FS */
3430     free(vp);
3431 }
3432
3433 /* check to see if we should shutdown this volume
3434  * returns 1 if volume was freed, 0 otherwise */
3435 #ifdef AFS_DEMAND_ATTACH_FS
3436 static int
3437 VCheckDetach(register Volume * vp)
3438 {
3439     int ret = 0;
3440     Error ec = 0;
3441
3442     if (vp->nUsers || vp->nWaiters)
3443         return ret;
3444
3445     if (vp->shuttingDown) {
3446         ret = 1;
3447         if ((programType != fileServer) &&
3448             (V_inUse(vp) == programType) &&
3449             ((V_checkoutMode(vp) == V_VOLUPD) ||
3450              ((V_checkoutMode(vp) == V_CLONE) &&
3451               (VolumeWriteable(vp))))) {
3452             V_inUse(vp) = 0;
3453             VUpdateVolume_r(&ec, vp, VOL_UPDATE_NOFORCEOFF);
3454             if (ec) {
3455                 Log("VCheckDetach: failed to clear inUse failed during detachment of volid %u\n",
3456                     vp->hashid);
3457             }
3458         }
3459         VReleaseVolumeHandles_r(vp);
3460         VCheckSalvage(vp);
3461         ReallyFreeVolume(vp);
3462         if (programType == fileServer) {
3463             assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3464         }
3465     }
3466     return ret;
3467 }
3468 #else /* AFS_DEMAND_ATTACH_FS */
3469 static int
3470 VCheckDetach(register Volume * vp)
3471 {
3472     int ret = 0;
3473     Error ec = 0;
3474
3475     if (vp->nUsers)
3476         return ret;
3477
3478     if (vp->shuttingDown) {
3479         ret = 1;
3480         if ((programType != fileServer) &&
3481             (V_inUse(vp) == programType) &&
3482             ((V_checkoutMode(vp) == V_VOLUPD) ||
3483              ((V_checkoutMode(vp) == V_CLONE) &&
3484               (VolumeWriteable(vp))))) {
3485             V_inUse(vp) = 0;
3486             VUpdateVolume_r(&ec, vp, VOL_UPDATE_NOFORCEOFF);
3487             if (ec) {
3488                 Log("VCheckDetach: failed to clear inUse failed during detachment of volid %u\n",
3489                     vp->hashid);
3490             }
3491         }
3492         VReleaseVolumeHandles_r(vp);
3493         ReallyFreeVolume(vp);
3494         if (programType == fileServer) {
3495 #if defined(AFS_PTHREAD_ENV)
3496             assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3497 #else /* AFS_PTHREAD_ENV */
3498             LWP_NoYieldSignal(VPutVolume);
3499 #endif /* AFS_PTHREAD_ENV */
3500         }
3501     }
3502     return ret;
3503 }
3504 #endif /* AFS_DEMAND_ATTACH_FS */
3505
3506 /* check to see if we should offline this volume
3507  * return 1 if volume went offline, 0 otherwise */
3508 #ifdef AFS_DEMAND_ATTACH_FS
3509 static int
3510 VCheckOffline(register Volume * vp)
3511 {
3512     Volume * rvp = NULL;
3513     int ret = 0;
3514
3515     if (vp->goingOffline && !vp->nUsers) {
3516         Error error;
3517         assert(programType == fileServer);
3518         assert((V_attachState(vp) != VOL_STATE_ATTACHED) &&
3519                (V_attachState(vp) != VOL_STATE_FREED) &&
3520                (V_attachState(vp) != VOL_STATE_PREATTACHED) &&
3521                (V_attachState(vp) != VOL_STATE_UNATTACHED));
3522
3523         /* valid states:
3524          *
3525          * VOL_STATE_GOING_OFFLINE
3526          * VOL_STATE_SHUTTING_DOWN
3527          * VIsErrorState(V_attachState(vp))
3528          * VIsExclusiveState(V_attachState(vp))
3529          */
3530
3531         VCreateReservation_r(vp);
3532         VChangeState_r(vp, VOL_STATE_OFFLINING);
3533
3534         ret = 1;
3535         /* must clear the goingOffline flag before we drop the glock */
3536         vp->goingOffline = 0;
3537         V_inUse(vp) = 0;
3538
3539         VLRU_Delete_r(vp);
3540
3541         /* perform async operations */
3542         VUpdateVolume_r(&error, vp, 0);
3543         VCloseVolumeHandles_r(vp);
3544
3545         if (LogLevel) {
3546             Log("VOffline: Volume %u (%s) is now offline", V_id(vp),
3547                 V_name(vp));
3548             if (V_offlineMessage(vp)[0])
3549                 Log(" (%s)", V_offlineMessage(vp));
3550             Log("\n");
3551         }
3552
3553         /* invalidate the volume header cache entry */
3554         FreeVolumeHeader(vp);
3555
3556         /* if nothing changed state to error or salvaging,
3557          * drop state to unattached */
3558         if (!VIsErrorState(V_attachState(vp))) {
3559             VChangeState_r(vp, VOL_STATE_UNATTACHED);
3560         }
3561         VCancelReservation_r(vp);
3562         /* no usage of vp is safe beyond this point */
3563     }
3564     return ret;
3565 }
3566 #else /* AFS_DEMAND_ATTACH_FS */
3567 static int
3568 VCheckOffline(register Volume * vp)
3569 {
3570     Volume * rvp = NULL;
3571     int ret = 0;
3572
3573     if (vp->goingOffline && !vp->nUsers) {
3574         Error error;
3575         assert(programType == fileServer);
3576
3577         ret = 1;
3578         vp->goingOffline = 0;
3579         V_inUse(vp) = 0;
3580         VUpdateVolume_r(&error, vp, 0);
3581         VCloseVolumeHandles_r(vp);
3582         if (LogLevel) {
3583             Log("VOffline: Volume %u (%s) is now offline", V_id(vp),
3584                 V_name(vp));
3585             if (V_offlineMessage(vp)[0])
3586                 Log(" (%s)", V_offlineMessage(vp));
3587             Log("\n");
3588         }
3589         FreeVolumeHeader(vp);
3590 #ifdef AFS_PTHREAD_ENV
3591         assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3592 #else /* AFS_PTHREAD_ENV */
3593         LWP_NoYieldSignal(VPutVolume);
3594 #endif /* AFS_PTHREAD_ENV */
3595     }
3596     return ret;
3597 }
3598 #endif /* AFS_DEMAND_ATTACH_FS */
3599
3600 /***************************************************/
3601 /* demand attach fs ref counting routines          */
3602 /***************************************************/
3603
3604 #ifdef AFS_DEMAND_ATTACH_FS
3605 /* the following two functions handle reference counting for
3606  * asynchronous operations on volume structs.
3607  *
3608  * their purpose is to prevent a VDetachVolume or VShutdown
3609  * from free()ing the Volume struct during an async i/o op */
3610
3611 /* register with the async volume op ref counter */
3612 /* VCreateReservation_r moved into inline code header because it 
3613  * is now needed in vnode.c -- tkeiser 11/20/2007 
3614  */
3615
3616 /**
3617  * decrement volume-package internal refcount.
3618  *
3619  * @param vp  volume object pointer
3620  *
3621  * @internal volume package internal use only
3622  *
3623  * @pre 
3624  *    @arg VOL_LOCK is held
3625  *    @arg lightweight refcount held
3626  *
3627  * @post volume waiters refcount is decremented; volume may
3628  *       have been deallocated/shutdown/offlined/salvaged/
3629  *       whatever during the process
3630  *
3631  * @warning once you have tossed your last reference (you can acquire
3632  *          lightweight refs recursively) it is NOT SAFE to reference
3633  *          a volume object pointer ever again
3634  *
3635  * @see VCreateReservation_r
3636  *
3637  * @note DEMAND_ATTACH_FS only
3638  */
3639 void
3640 VCancelReservation_r(Volume * vp)
3641 {
3642     assert(--vp->nWaiters >= 0);
3643     if (vp->nWaiters == 0) {
3644         VCheckOffline(vp);
3645         if (!VCheckDetach(vp)) {
3646             VCheckSalvage(vp);
3647             VCheckFree(vp);
3648         }
3649     }
3650 }
3651
3652 /* check to see if we should free this volume now
3653  * return 1 if volume was freed, 0 otherwise */
3654 static int
3655 VCheckFree(Volume * vp)
3656 {
3657     int ret = 0;
3658     if ((vp->nUsers == 0) &&
3659         (vp->nWaiters == 0) &&
3660         !(V_attachFlags(vp) & (VOL_IN_HASH | 
3661                                VOL_ON_VBYP_LIST | 
3662                                VOL_IS_BUSY |
3663                                VOL_ON_VLRU))) {
3664         ReallyFreeVolume(vp);
3665         ret = 1;
3666     }
3667     return ret;
3668 }
3669 #endif /* AFS_DEMAND_ATTACH_FS */
3670
3671
3672 /***************************************************/
3673 /* online volume operations routines               */
3674 /***************************************************/
3675
3676 #ifdef AFS_DEMAND_ATTACH_FS
3677 /**
3678  * register a volume operation on a given volume.
3679  *
3680  * @param[in] vp       volume object
3681  * @param[in] vopinfo  volume operation info object
3682  *
3683  * @pre VOL_LOCK is held
3684  *
3685  * @post volume operation info object attached to volume object.
3686  *       volume operation statistics updated.
3687  *
3688  * @note by "attached" we mean a copy of the passed in object is made
3689  *
3690  * @internal volume package internal use only
3691  */
3692 int
3693 VRegisterVolOp_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
3694 {
3695     FSSYNC_VolOp_info * info;
3696
3697     /* attach a vol op info node to the volume struct */
3698     info = (FSSYNC_VolOp_info *) malloc(sizeof(FSSYNC_VolOp_info));
3699     assert(info != NULL);
3700     memcpy(info, vopinfo, sizeof(FSSYNC_VolOp_info));
3701     vp->pending_vol_op = info;
3702
3703     /* update stats */
3704     vp->stats.last_vol_op = FT_ApproxTime();
3705     vp->stats.vol_ops++;
3706     IncUInt64(&VStats.vol_ops);
3707
3708     return 0;
3709 }
3710
3711 /**
3712  * deregister the volume operation attached to this volume.
3713  *
3714  * @param[in] vp  volume object pointer
3715  *
3716  * @pre VOL_LOCK is held
3717  *
3718  * @post the volume operation info object is detached from the volume object
3719  *
3720  * @internal volume package internal use only
3721  */
3722 int
3723 VDeregisterVolOp_r(Volume * vp)
3724 {
3725     if (vp->pending_vol_op) {
3726         free(vp->pending_vol_op);
3727         vp->pending_vol_op = NULL;
3728     }
3729     return 0;
3730 }
3731 #endif /* AFS_DEMAND_ATTACH_FS */
3732
3733 /**
3734  * determine whether it is safe to leave a volume online during
3735  * the volume operation described by the vopinfo object.
3736  *
3737  * @param[in] vp        volume object
3738  * @param[in] vopinfo   volume operation info object
3739  *
3740  * @return whether it is safe to leave volume online
3741  *    @retval 0  it is NOT SAFE to leave the volume online
3742  *    @retval 1  it is safe to leave the volume online during the operation
3743  *
3744  * @pre
3745  *    @arg VOL_LOCK is held
3746  *    @arg disk header attached to vp (heavyweight ref on vp will guarantee
3747  *         this condition is met)
3748  *
3749  * @internal volume package internal use only
3750  */
3751 int
3752 VVolOpLeaveOnline_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
3753 {
3754     return (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
3755             (vopinfo->com.reason == V_READONLY ||
3756              (!VolumeWriteable(vp) &&
3757               (vopinfo->com.reason == V_CLONE ||
3758                vopinfo->com.reason == V_DUMP))));
3759 }
3760
3761 /**
3762  * determine whether VBUSY should be set during this volume operation.
3763  *
3764  * @param[in] vp        volume object
3765  * @param[in] vopinfo   volume operation info object
3766  *
3767  * @return whether VBUSY should be set
3768  *   @retval 0  VBUSY does NOT need to be set
3769  *   @retval 1  VBUSY SHOULD be set
3770  *
3771  * @pre VOL_LOCK is held
3772  *
3773  * @internal volume package internal use only
3774  */
3775 int
3776 VVolOpSetVBusy_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
3777 {
3778     return (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
3779             (vopinfo->com.reason == V_CLONE ||
3780              vopinfo->com.reason == V_DUMP));
3781 }
3782
3783
3784 /***************************************************/
3785 /* online salvager routines                        */
3786 /***************************************************/
3787 #if defined(AFS_DEMAND_ATTACH_FS)
3788 #define SALVAGE_PRIO_UPDATE_INTERVAL 3      /**< number of seconds between prio updates */
3789 #define SALVAGE_COUNT_MAX 16                /**< number of online salvages we
3790                                              *   allow before moving the volume
3791                                              *   into a permanent error state
3792                                              *
3793                                              *   once this threshold is reached,
3794                                              *   the operator will have to manually
3795                                              *   issue a 'bos salvage' to bring
3796                                              *   the volume back online
3797                                              */
3798
3799 /**
3800  * check whether a salvage needs to be performed on this volume.
3801  *
3802  * @param[in] vp   pointer to volume object
3803  *
3804  * @return status code
3805  *    @retval 0 no salvage scheduled
3806  *    @retval 1 a salvage has been scheduled with the salvageserver
3807  *
3808  * @pre VOL_LOCK is held
3809  *
3810  * @post if salvage request flag is set and nUsers and nWaiters are zero,
3811  *       then a salvage will be requested
3812  *
3813  * @note this is one of the event handlers called&n