c61013b9696d2cfea67b1f6dfd1c338ccaa3e1d9
[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) 2006 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 #ifdef AFS_PTHREAD_ENV
127 #include <assert.h>
128 #else /* AFS_PTHREAD_ENV */
129 #include "afs/assert.h"
130 #endif /* AFS_PTHREAD_ENV */
131 #include "vutils.h"
132 #ifndef AFS_NT40_ENV
133 #include <dir/dir.h>
134 #include <unistd.h>
135 #endif
136
137 #if !defined(offsetof)
138 #include <stddef.h>
139 #endif
140
141 #ifdef O_LARGEFILE
142 #define afs_stat        stat64
143 #define afs_fstat       fstat64
144 #define afs_open        open64
145 #else /* !O_LARGEFILE */
146 #define afs_stat        stat
147 #define afs_fstat       fstat
148 #define afs_open        open
149 #endif /* !O_LARGEFILE */
150
151 #ifdef AFS_PTHREAD_ENV
152 pthread_mutex_t vol_glock_mutex;
153 pthread_mutex_t vol_trans_mutex;
154 pthread_cond_t vol_put_volume_cond;
155 pthread_cond_t vol_sleep_cond;
156 int vol_attach_threads = 1;
157 #endif /* AFS_PTHREAD_ENV */
158
159 #ifdef AFS_DEMAND_ATTACH_FS
160 pthread_mutex_t vol_salvsync_mutex;
161 #endif /* AFS_DEMAND_ATTACH_FS */
162
163 #ifdef  AFS_OSF_ENV
164 extern void *calloc(), *realloc();
165 #endif
166
167 /*@printflike@*/ extern void Log(const char *format, ...);
168
169 /* Forward declarations */
170 static Volume *attach2(Error * ec, VolId vid, char *path,
171                        register struct VolumeHeader *header,
172                        struct DiskPartition *partp, Volume * vp, 
173                        int isbusy, int mode);
174 static void ReallyFreeVolume(Volume * vp);
175 #ifdef AFS_DEMAND_ATTACH_FS
176 static void FreeVolume(Volume * vp);
177 #else /* !AFS_DEMAND_ATTACH_FS */
178 #define FreeVolume(vp) ReallyFreeVolume(vp)
179 static void VScanUpdateList(void);
180 #endif /* !AFS_DEMAND_ATTACH_FS */
181 static void VInitVolumeHeaderCache(afs_uint32 howMany);
182 static int GetVolumeHeader(register Volume * vp);
183 static void ReleaseVolumeHeader(register struct volHeader *hd);
184 static void FreeVolumeHeader(register Volume * vp);
185 static void AddVolumeToHashTable(register Volume * vp, int hashid);
186 static void DeleteVolumeFromHashTable(register Volume * vp);
187 static int VHold(Volume * vp);
188 static int VHold_r(Volume * vp);
189 static void VGetBitmap_r(Error * ec, Volume * vp, VnodeClass class);
190 static void GetVolumePath(Error * ec, VolId volumeId, char **partitionp,
191                           char **namep);
192 static void VReleaseVolumeHandles_r(Volume * vp);
193 static void VCloseVolumeHandles_r(Volume * vp);
194 static void LoadVolumeHeader(Error * ec, Volume * vp);
195 static int VCheckOffline(register Volume * vp);
196 static int VCheckDetach(register Volume * vp);
197 static Volume * GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flags);
198 static int VolumeExternalName_r(VolumeId volumeId, char * name, size_t len);
199
200 int LogLevel;                   /* Vice loglevel--not defined as extern so that it will be
201                                  * defined when not linked with vice, XXXX */
202 ProgramType programType;        /* The type of program using the package */
203
204
205 /* extended volume package statistics */
206 VolPkgStats VStats;
207
208
209 #define VOLUME_BITMAP_GROWSIZE  16      /* bytes, => 128vnodes */
210                                         /* Must be a multiple of 4 (1 word) !! */
211
212 /* this parameter needs to be tunable at runtime.
213  * 128 was really inadequate for largish servers -- at 16384 volumes this
214  * puts average chain length at 128, thus an average 65 deref's to find a volptr.
215  * talk about bad spatial locality...
216  *
217  * an AVL or splay tree might work a lot better, but we'll just increase
218  * the default hash table size for now
219  */
220 #define DEFAULT_VOLUME_HASH_SIZE 256   /* Must be a power of 2!! */
221 #define DEFAULT_VOLUME_HASH_MASK (DEFAULT_VOLUME_HASH_SIZE-1)
222 #define VOLUME_HASH(volumeId) (volumeId&(VolumeHashTable.Mask))
223
224 /*
225  * turn volume hash chains into partially ordered lists.
226  * when the threshold is exceeded between two adjacent elements,
227  * perform a chain rebalancing operation.
228  *
229  * keep the threshold high in order to keep cache line invalidates
230  * low "enough" on SMPs
231  */
232 #define VOLUME_HASH_REORDER_THRESHOLD 200
233
234 /*
235  * when possible, don't just reorder single elements, but reorder
236  * entire chains of elements at once.  a chain of elements that
237  * exceed the element previous to the pivot by at least CHAIN_THRESH 
238  * accesses are moved in front of the chain whose elements have at
239  * least CHAIN_THRESH less accesses than the pivot element
240  */
241 #define VOLUME_HASH_REORDER_CHAIN_THRESH (VOLUME_HASH_REORDER_THRESHOLD / 2)
242
243 #include "rx/rx_queue.h"
244
245
246 VolumeHashTable_t VolumeHashTable = {
247     DEFAULT_VOLUME_HASH_SIZE,
248     DEFAULT_VOLUME_HASH_MASK,
249     NULL
250 };
251
252
253 static void VInitVolumeHash(void);
254
255
256 #ifndef AFS_HAVE_FFS
257 /* This macro is used where an ffs() call does not exist. Was in util/ffs.c */
258 ffs(x)
259 {
260     afs_int32 ffs_i;
261     afs_int32 ffs_tmp = x;
262     if (ffs_tmp == 0)
263         return (-1);
264     else
265         for (ffs_i = 1;; ffs_i++) {
266             if (ffs_tmp & 1)
267                 return (ffs_i);
268             else
269                 ffs_tmp >>= 1;
270         }
271 }
272 #endif /* !AFS_HAVE_FFS */
273
274 #ifdef AFS_PTHREAD_ENV
275 typedef struct diskpartition_queue_t {
276     struct rx_queue queue;
277     struct DiskPartition * diskP;
278 } diskpartition_queue_t;
279 typedef struct vinitvolumepackage_thread_t {
280     struct rx_queue queue;
281     pthread_cond_t thread_done_cv;
282     int n_threads_complete;
283 } vinitvolumepackage_thread_t;
284 static void * VInitVolumePackageThread(void * args);
285 #endif /* AFS_PTHREAD_ENV */
286
287 static int VAttachVolumesByPartition(struct DiskPartition *diskP, 
288                                      int * nAttached, int * nUnattached);
289
290
291 #ifdef AFS_DEMAND_ATTACH_FS
292 /* demand attach fileserver extensions */
293
294 /* XXX
295  * in the future we will support serialization of VLRU state into the fs_state
296  * disk dumps
297  *
298  * these structures are the beginning of that effort
299  */
300 struct VLRU_DiskHeader {
301     struct versionStamp stamp;            /* magic and structure version number */
302     afs_uint32 mtime;                     /* time of dump to disk */
303     afs_uint32 num_records;               /* number of VLRU_DiskEntry records */
304 };
305
306 struct VLRU_DiskEntry {
307     afs_uint32 vid;                       /* volume ID */
308     afs_uint32 idx;                       /* generation */
309     afs_uint32 last_get;                  /* timestamp of last get */
310 };
311
312 struct VLRU_StartupQueue {
313     struct VLRU_DiskEntry * entry;
314     int num_entries;
315     int next_idx;
316 };
317
318 typedef struct vshutdown_thread_t {
319     struct rx_queue q;
320     pthread_mutex_t lock;
321     pthread_cond_t cv;
322     pthread_cond_t master_cv;
323     int n_threads;
324     int n_threads_complete;
325     int vol_remaining;
326     int schedule_version;
327     int pass;
328     byte n_parts;
329     byte n_parts_done_pass;
330     byte part_thread_target[VOLMAXPARTS+1];
331     byte part_done_pass[VOLMAXPARTS+1];
332     struct rx_queue * part_pass_head[VOLMAXPARTS+1];
333     int stats[4][VOLMAXPARTS+1];
334 } vshutdown_thread_t;
335 static void * VShutdownThread(void * args);
336
337
338 static Volume * VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode);
339 static int VCheckFree(Volume * vp);
340
341 /* VByP List */
342 static void AddVolumeToVByPList_r(Volume * vp);
343 static void DeleteVolumeFromVByPList_r(Volume * vp);
344 static void VVByPListBeginExclusive_r(struct DiskPartition * dp);
345 static void VVByPListEndExclusive_r(struct DiskPartition * dp);
346 static void VVByPListWait_r(struct DiskPartition * dp);
347
348 /* online salvager */
349 static int VCheckSalvage(register Volume * vp);
350 static int VUpdateSalvagePriority_r(Volume * vp);
351 static int VScheduleSalvage_r(Volume * vp);
352 static int VCancelSalvage_r(Volume * vp, int reason);
353
354 /* Volume hash table */
355 static void VReorderHash_r(VolumeHashChainHead * head, Volume * pp, Volume * vp);
356 static void VHashBeginExclusive_r(VolumeHashChainHead * head);
357 static void VHashEndExclusive_r(VolumeHashChainHead * head);
358 static void VHashWait_r(VolumeHashChainHead * head);
359
360 /* Volume state machine */
361 static void VCreateReservation_r(Volume * vp);
362 static void VCancelReservation_r(Volume * vp);
363 static void VWaitStateChange_r(Volume * vp);
364 static void VWaitExclusiveState_r(Volume * vp);
365 static int IsExclusiveState(VolState state);
366 static int IsErrorState(VolState state);
367 static int IsValidState(VolState state);
368
369 /* shutdown */
370 static int ShutdownVByPForPass_r(struct DiskPartition * dp, int pass);
371 static int ShutdownVolumeWalk_r(struct DiskPartition * dp, int pass,
372                                 struct rx_queue ** idx);
373 static void ShutdownController(vshutdown_thread_t * params);
374 static void ShutdownCreateSchedule(vshutdown_thread_t * params);
375
376 /* VLRU */
377 static void VLRU_ComputeConstants(void);
378 static void VInitVLRU(void);
379 static void VLRU_Init_Node_r(volatile Volume * vp);
380 static void VLRU_Add_r(volatile Volume * vp);
381 static void VLRU_Delete_r(volatile Volume * vp);
382 static void VLRU_UpdateAccess_r(volatile Volume * vp);
383 static void * VLRU_ScannerThread(void * args);
384 static void VLRU_Scan_r(int idx);
385 static void VLRU_Promote_r(int idx);
386 static void VLRU_Demote_r(int idx);
387 static void VLRU_SwitchQueues(volatile Volume * vp, int new_idx, int append);
388
389 /* soft detach */
390 static int VCheckSoftDetach(volatile Volume * vp, afs_uint32 thresh);
391 static int VCheckSoftDetachCandidate(volatile Volume * vp, afs_uint32 thresh);
392 static int VSoftDetachVolume_r(volatile Volume * vp, afs_uint32 thresh);
393 #endif /* AFS_DEMAND_ATTACH_FS */
394
395
396 struct Lock vol_listLock;       /* Lock obtained when listing volumes:  
397                                  * prevents a volume from being missed 
398                                  * if the volume is attached during a 
399                                  * list volumes */
400
401
402 static int TimeZoneCorrection;  /* Number of seconds west of GMT */
403
404 /* Common message used when the volume goes off line */
405 char *VSalvageMessage =
406     "Files in this volume are currently unavailable; call operations";
407
408 int VInit;                      /* 0 - uninitialized,
409                                  * 1 - initialized but not all volumes have been attached,
410                                  * 2 - initialized and all volumes have been attached,
411                                  * 3 - initialized, all volumes have been attached, and
412                                  * VConnectFS() has completed. */
413
414
415 bit32 VolumeCacheCheck;         /* Incremented everytime a volume goes on line--
416                                  * used to stamp volume headers and in-core
417                                  * vnodes.  When the volume goes on-line the
418                                  * vnode will be invalidated
419                                  * access only with VOL_LOCK held */
420
421
422
423
424 /***************************************************/
425 /* Startup routines                                */
426 /***************************************************/
427
428 int
429 VInitVolumePackage(ProgramType pt, afs_uint32 nLargeVnodes, afs_uint32 nSmallVnodes,
430                    int connect, afs_uint32 volcache)
431 {
432     int errors = 0;             /* Number of errors while finding vice partitions. */
433     struct timeval tv;
434     struct timezone tz;
435
436     programType = pt;
437
438 #ifdef AFS_DEMAND_ATTACH_FS
439     memset(&VStats, 0, sizeof(VStats));
440     VStats.hdr_cache_size = 200;
441 #endif
442
443     VInitPartitionPackage();
444     VInitVolumeHash();
445     VInitVnHashByVolume();
446 #ifdef AFS_DEMAND_ATTACH_FS
447     if (programType == fileServer) {
448         VInitVLRU();
449     } else {
450         VLRU_SetOptions(VLRU_SET_ENABLED, 0);
451     }
452 #endif
453
454 #ifdef AFS_PTHREAD_ENV
455     assert(pthread_mutex_init(&vol_glock_mutex, NULL) == 0);
456     assert(pthread_mutex_init(&vol_trans_mutex, NULL) == 0);
457     assert(pthread_cond_init(&vol_put_volume_cond, NULL) == 0);
458     assert(pthread_cond_init(&vol_sleep_cond, NULL) == 0);
459 #else /* AFS_PTHREAD_ENV */
460     IOMGR_Initialize();
461 #endif /* AFS_PTHREAD_ENV */
462     Lock_Init(&vol_listLock);
463
464     srandom(time(0));           /* For VGetVolumeInfo */
465     gettimeofday(&tv, &tz);
466     TimeZoneCorrection = tz.tz_minuteswest * 60;
467
468 #ifdef AFS_DEMAND_ATTACH_FS
469     assert(pthread_mutex_init(&vol_salvsync_mutex, NULL) == 0);
470 #endif /* AFS_DEMAND_ATTACH_FS */
471
472     /* Ok, we have done enough initialization that fileserver can 
473      * start accepting calls, even though the volumes may not be 
474      * available just yet.
475      */
476     VInit = 1;
477
478 #if defined(AFS_DEMAND_ATTACH_FS) && defined(SALVSYNC_BUILD_SERVER)
479     if (programType == salvageServer) {
480         SALVSYNC_salvInit();
481     }
482 #endif /* AFS_DEMAND_ATTACH_FS */
483 #ifdef FSSYNC_BUILD_SERVER
484     if (programType == fileServer) {
485         FSYNC_fsInit();
486     }
487 #endif
488 #if defined(AFS_DEMAND_ATTACH_FS) && defined(SALVSYNC_BUILD_CLIENT)
489     if (programType == fileServer) {
490         /* establish a connection to the salvager at this point */
491         assert(VConnectSALV() != 0);
492     }
493 #endif /* AFS_DEMAND_ATTACH_FS */
494
495     if (volcache > VStats.hdr_cache_size)
496         VStats.hdr_cache_size = volcache;
497     VInitVolumeHeaderCache(VStats.hdr_cache_size);
498
499     VInitVnodes(vLarge, nLargeVnodes);
500     VInitVnodes(vSmall, nSmallVnodes);
501
502
503     errors = VAttachPartitions();
504     if (errors)
505         return -1;
506
507     if (programType == fileServer) {
508         struct DiskPartition *diskP;
509 #ifdef AFS_PTHREAD_ENV
510         struct vinitvolumepackage_thread_t params;
511         struct diskpartition_queue_t * dpq;
512         int i, threads, parts;
513         pthread_t tid;
514         pthread_attr_t attrs;
515
516         assert(pthread_cond_init(&params.thread_done_cv,NULL) == 0);
517         queue_Init(&params);
518         params.n_threads_complete = 0;
519
520         /* create partition work queue */
521         for (parts=0, diskP = DiskPartitionList; diskP; diskP = diskP->next, parts++) {
522             dpq = (diskpartition_queue_t *) malloc(sizeof(struct diskpartition_queue_t));
523             assert(dpq != NULL);
524             dpq->diskP = diskP;
525             queue_Prepend(&params,dpq);
526         }
527
528         threads = MIN(parts, vol_attach_threads);
529
530         if (threads > 1) {
531             /* spawn off a bunch of initialization threads */
532             assert(pthread_attr_init(&attrs) == 0);
533             assert(pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED) == 0);
534
535             Log("VInitVolumePackage: beginning parallel fileserver startup\n");
536 #ifdef AFS_DEMAND_ATTACH_FS
537             Log("VInitVolumePackage: using %d threads to pre-attach volumes on %d partitions\n",
538                 threads, parts);
539 #else /* AFS_DEMAND_ATTACH_FS */
540             Log("VInitVolumePackage: using %d threads to attach volumes on %d partitions\n",
541                 threads, parts);
542 #endif /* AFS_DEMAND_ATTACH_FS */
543
544             VOL_LOCK;
545             for (i=0; i < threads; i++) {
546                 assert(pthread_create
547                        (&tid, &attrs, &VInitVolumePackageThread,
548                         &params) == 0);
549             }
550
551             while(params.n_threads_complete < threads) {
552                 pthread_cond_wait(&params.thread_done_cv,&vol_glock_mutex);
553             }
554             VOL_UNLOCK;
555
556             assert(pthread_attr_destroy(&attrs) == 0);
557         } else {
558             /* if we're only going to run one init thread, don't bother creating
559              * another LWP */
560             Log("VInitVolumePackage: beginning single-threaded fileserver startup\n");
561 #ifdef AFS_DEMAND_ATTACH_FS
562             Log("VInitVolumePackage: using 1 thread to pre-attach volumes on %d partition(s)\n",
563                 parts);
564 #else /* AFS_DEMAND_ATTACH_FS */
565             Log("VInitVolumePackage: using 1 thread to attach volumes on %d partition(s)\n",
566                 parts);
567 #endif /* AFS_DEMAND_ATTACH_FS */
568
569             VInitVolumePackageThread(&params);
570         }
571
572         assert(pthread_cond_destroy(&params.thread_done_cv) == 0);
573
574 #else /* AFS_PTHREAD_ENV */
575         DIR *dirp;
576         struct dirent *dp;
577
578         /* Attach all the volumes in this partition */
579         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
580             int nAttached = 0, nUnattached = 0;
581             assert(VAttachVolumesByPartition(diskP, &nAttached, &nUnattached) == 0);
582         }
583 #endif /* AFS_PTHREAD_ENV */
584     }
585
586     VInit = 2;                  /* Initialized, and all volumes have been attached */
587 #ifdef FSSYNC_BUILD_CLIENT
588     if (programType == volumeUtility && connect) {
589         if (!VConnectFS()) {
590             Log("Unable to connect to file server; aborted\n");
591             exit(1);
592         }
593     }
594 #ifdef AFS_DEMAND_ATTACH_FS
595     else if (programType == salvageServer) {
596         if (!VConnectFS()) {
597             Log("Unable to connect to file server; aborted\n");
598             exit(1);
599         }
600     }
601 #endif /* AFS_DEMAND_ATTACH_FS */
602 #endif /* FSSYNC_BUILD_CLIENT */
603     return 0;
604 }
605
606 #ifdef AFS_PTHREAD_ENV
607 static void *
608 VInitVolumePackageThread(void * args) {
609     int errors = 0;             /* Number of errors while finding vice partitions. */
610
611     DIR *dirp;
612     struct dirent *dp;
613     struct DiskPartition *diskP;
614     struct vinitvolumepackage_thread_t * params;
615     struct diskpartition_queue_t * dpq;
616
617     params = (vinitvolumepackage_thread_t *) args;
618
619
620     VOL_LOCK;
621     /* Attach all the volumes in this partition */
622     while (queue_IsNotEmpty(params)) {
623         int nAttached = 0, nUnattached = 0;
624
625         dpq = queue_First(params,diskpartition_queue_t);
626         queue_Remove(dpq);
627         VOL_UNLOCK;
628         diskP = dpq->diskP;
629         free(dpq);
630
631         assert(VAttachVolumesByPartition(diskP, &nAttached, &nUnattached) == 0);
632
633         VOL_LOCK;
634     }
635
636     params->n_threads_complete++;
637     pthread_cond_signal(&params->thread_done_cv);
638     VOL_UNLOCK;
639     return NULL;
640 }
641 #endif /* AFS_PTHREAD_ENV */
642
643 /*
644  * attach all volumes on a given disk partition
645  */
646 static int
647 VAttachVolumesByPartition(struct DiskPartition *diskP, int * nAttached, int * nUnattached)
648 {
649   DIR * dirp;
650   struct dirent * dp;
651   int ret = 0;
652
653   Log("Partition %s: attaching volumes\n", diskP->name);
654   dirp = opendir(VPartitionPath(diskP));
655   if (!dirp) {
656     Log("opendir on Partition %s failed!\n", diskP->name);
657     return 1;
658   }
659
660   while ((dp = readdir(dirp))) {
661     char *p;
662     p = strrchr(dp->d_name, '.');
663     if (p != NULL && strcmp(p, VHDREXT) == 0) {
664       Error error;
665       Volume *vp;
666 #ifdef AFS_DEMAND_ATTACH_FS
667       vp = VPreAttachVolumeByName(&error, diskP->name, dp->d_name,
668                                   V_VOLUPD);
669 #else /* AFS_DEMAND_ATTACH_FS */
670       vp = VAttachVolumeByName(&error, diskP->name, dp->d_name,
671                                V_VOLUPD);
672 #endif /* AFS_DEMAND_ATTACH_FS */
673       (*(vp ? nAttached : nUnattached))++;
674       if (error == VOFFLINE)
675         Log("Volume %d stays offline (/vice/offline/%s exists)\n", VolumeNumber(dp->d_name), dp->d_name);
676       else if (LogLevel >= 5) {
677         Log("Partition %s: attached volume %d (%s)\n",
678             diskP->name, VolumeNumber(dp->d_name),
679             dp->d_name);
680       }
681 #if !defined(AFS_DEMAND_ATTACH_FS)
682       if (vp) {
683         VPutVolume(vp);
684       }
685 #endif /* AFS_DEMAND_ATTACH_FS */
686     }
687   }
688
689   Log("Partition %s: attached %d volumes; %d volumes not attached\n", diskP->name, *nAttached, *nUnattached);
690   closedir(dirp);
691   return ret;
692 }
693
694
695 /***************************************************/
696 /* Shutdown routines                               */
697 /***************************************************/
698
699 /*
700  * demand attach fs
701  * highly multithreaded volume package shutdown
702  *
703  * with the demand attach fileserver extensions,
704  * VShutdown has been modified to be multithreaded.
705  * In order to achieve optimal use of many threads,
706  * the shutdown code involves one control thread and
707  * n shutdown worker threads.  The control thread
708  * periodically examines the number of volumes available
709  * for shutdown on each partition, and produces a worker
710  * thread allocation schedule.  The idea is to eliminate
711  * redundant scheduling computation on the workers by
712  * having a single master scheduler.
713  *
714  * The scheduler's objectives are:
715  * (1) fairness
716  *   each partition with volumes remaining gets allocated
717  *   at least 1 thread (assuming sufficient threads)
718  * (2) performance
719  *   threads are allocated proportional to the number of
720  *   volumes remaining to be offlined.  This ensures that
721  *   the OS I/O scheduler has many requests to elevator
722  *   seek on partitions that will (presumably) take the
723  *   longest amount of time (from now) to finish shutdown
724  * (3) keep threads busy
725  *   when there are extra threads, they are assigned to
726  *   partitions using a simple round-robin algorithm
727  *
728  * In the future, we may wish to add the ability to adapt
729  * to the relative performance patterns of each disk
730  * partition.
731  *
732  *
733  * demand attach fs
734  * multi-step shutdown process
735  *
736  * demand attach shutdown is a four-step process. Each
737  * shutdown "pass" shuts down increasingly more difficult
738  * volumes.  The main purpose is to achieve better cache
739  * utilization during shutdown.
740  *
741  * pass 0
742  *   shutdown volumes in the unattached, pre-attached
743  *   and error states
744  * pass 1
745  *   shutdown attached volumes with cached volume headers
746  * pass 2
747  *   shutdown all volumes in non-exclusive states
748  * pass 3
749  *   shutdown all remaining volumes
750  */
751
752 void
753 VShutdown_r(void)
754 {
755     int i;
756     register Volume *vp, *np;
757     register afs_int32 code;
758 #ifdef AFS_DEMAND_ATTACH_FS
759     struct DiskPartition * diskP;
760     struct diskpartition_queue_t * dpq;
761     vshutdown_thread_t params;
762     pthread_t tid;
763     pthread_attr_t attrs;
764
765     memset(&params, 0, sizeof(vshutdown_thread_t));
766
767     for (params.n_parts=0, diskP = DiskPartitionList;
768          diskP; diskP = diskP->next, params.n_parts++);
769
770     Log("VShutdown:  shutting down on-line volumes on %d partition%s...\n", 
771         params.n_parts, params.n_parts > 1 ? "s" : "");
772
773     if (vol_attach_threads > 1) {
774         /* prepare for parallel shutdown */
775         params.n_threads = vol_attach_threads;
776         assert(pthread_mutex_init(&params.lock, NULL) == 0);
777         assert(pthread_cond_init(&params.cv, NULL) == 0);
778         assert(pthread_cond_init(&params.master_cv, NULL) == 0);
779         assert(pthread_attr_init(&attrs) == 0);
780         assert(pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED) == 0);
781         queue_Init(&params);
782
783         /* setup the basic partition information structures for
784          * parallel shutdown */
785         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
786             /* XXX debug */
787             struct rx_queue * qp, * nqp;
788             Volume * vp;
789             int count = 0;
790
791             VVByPListWait_r(diskP);
792             VVByPListBeginExclusive_r(diskP);
793
794             /* XXX debug */
795             for (queue_Scan(&diskP->vol_list, qp, nqp, rx_queue)) {
796                 vp = (Volume *)((char *)qp - offsetof(Volume, vol_list));
797                 if (vp->header)
798                     count++;
799             }
800             Log("VShutdown: partition %s has %d volumes with attached headers\n",
801                 VPartitionPath(diskP), count);
802                 
803
804             /* build up the pass 0 shutdown work queue */
805             dpq = (struct diskpartition_queue_t *) malloc(sizeof(struct diskpartition_queue_t));
806             assert(dpq != NULL);
807             dpq->diskP = diskP;
808             queue_Prepend(&params, dpq);
809
810             params.part_pass_head[diskP->device] = queue_First(&diskP->vol_list, rx_queue);
811         }
812
813         Log("VShutdown:  beginning parallel fileserver shutdown\n");
814         Log("VShutdown:  using %d threads to offline volumes on %d partition%s\n",
815             vol_attach_threads, params.n_parts, params.n_parts > 1 ? "s" : "" );
816
817         /* do pass 0 shutdown */
818         assert(pthread_mutex_lock(&params.lock) == 0);
819         for (i=0; i < params.n_threads; i++) {
820             assert(pthread_create
821                    (&tid, &attrs, &VShutdownThread,
822                     &params) == 0);
823         }
824         
825         /* wait for all the pass 0 shutdowns to complete */
826         while (params.n_threads_complete < params.n_threads) {
827             assert(pthread_cond_wait(&params.master_cv, &params.lock) == 0);
828         }
829         params.n_threads_complete = 0;
830         params.pass = 1;
831         assert(pthread_cond_broadcast(&params.cv) == 0);
832         assert(pthread_mutex_unlock(&params.lock) == 0);
833
834         Log("VShutdown:  pass 0 completed using the 1 thread per partition algorithm\n");
835         Log("VShutdown:  starting passes 1 through 3 using finely-granular mp-fast algorithm\n");
836
837         /* run the parallel shutdown scheduler. it will drop the glock internally */
838         ShutdownController(&params);
839         
840         /* wait for all the workers to finish pass 3 and terminate */
841         while (params.pass < 4) {
842             assert(pthread_cond_wait(&params.cv, &vol_glock_mutex) == 0);
843         }
844         
845         assert(pthread_attr_destroy(&attrs) == 0);
846         assert(pthread_cond_destroy(&params.cv) == 0);
847         assert(pthread_cond_destroy(&params.master_cv) == 0);
848         assert(pthread_mutex_destroy(&params.lock) == 0);
849
850         /* drop the VByPList exclusive reservations */
851         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
852             VVByPListEndExclusive_r(diskP);
853             Log("VShutdown:  %s stats : (pass[0]=%d, pass[1]=%d, pass[2]=%d, pass[3]=%d)\n",
854                 VPartitionPath(diskP),
855                 params.stats[0][diskP->device],
856                 params.stats[1][diskP->device],
857                 params.stats[2][diskP->device],
858                 params.stats[3][diskP->device]);
859         }
860
861         Log("VShutdown:  shutdown finished using %d threads\n", params.n_threads);
862     } else {
863         /* if we're only going to run one shutdown thread, don't bother creating
864          * another LWP */
865         Log("VShutdown:  beginning single-threaded fileserver shutdown\n");
866
867         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
868             VShutdownByPartition_r(diskP);
869         }
870     }
871
872     Log("VShutdown:  complete.\n");
873 #else /* AFS_DEMAND_ATTACH_FS */
874     Log("VShutdown:  shutting down on-line volumes...\n");
875     for (i = 0; i < VolumeHashTable.Size; i++) {
876         /* try to hold first volume in the hash table */
877         for (queue_Scan(&VolumeHashTable.Table[i],vp,np,Volume)) {
878             code = VHold_r(vp);
879             if (code == 0) {
880                 if (LogLevel >= 5)
881                     Log("VShutdown:  Attempting to take volume %u offline.\n",
882                         vp->hashid);
883                 
884                 /* next, take the volume offline (drops reference count) */
885                 VOffline_r(vp, "File server was shut down");
886             }
887         }
888     }
889     Log("VShutdown:  complete.\n");
890 #endif /* AFS_DEMAND_ATTACH_FS */
891 }
892
893 void
894 VShutdown(void)
895 {
896     VOL_LOCK;
897     VShutdown_r();
898     VOL_UNLOCK;
899 }
900
901 #ifdef AFS_DEMAND_ATTACH_FS
902 /*
903  * demand attach fs
904  * shutdown control thread
905  */
906 static void
907 ShutdownController(vshutdown_thread_t * params)
908 {
909     /* XXX debug */
910     struct DiskPartition * diskP;
911     Device id;
912     vshutdown_thread_t shadow;
913
914     ShutdownCreateSchedule(params);
915
916     while ((params->pass < 4) &&
917            (params->n_threads_complete < params->n_threads)) {
918         /* recompute schedule once per second */
919
920         memcpy(&shadow, params, sizeof(vshutdown_thread_t));
921
922         VOL_UNLOCK;
923         /* XXX debug */
924         Log("ShutdownController:  schedule version=%d, vol_remaining=%d, pass=%d\n",
925             shadow.schedule_version, shadow.vol_remaining, shadow.pass);
926         Log("ShutdownController:  n_threads_complete=%d, n_parts_done_pass=%d\n",
927             shadow.n_threads_complete, shadow.n_parts_done_pass);
928         for (diskP = DiskPartitionList; diskP; diskP=diskP->next) {
929             id = diskP->device;
930             Log("ShutdownController:  part[%d] : (len=%d, thread_target=%d, done_pass=%d, pass_head=%p)\n",
931                 id, 
932                 diskP->vol_list.len,
933                 shadow.part_thread_target[id], 
934                 shadow.part_done_pass[id], 
935                 shadow.part_pass_head[id]);
936         }
937
938         sleep(1);
939         VOL_LOCK;
940
941         ShutdownCreateSchedule(params);
942     }
943 }
944
945 /* create the shutdown thread work schedule.
946  * this scheduler tries to implement fairness
947  * by allocating at least 1 thread to each 
948  * partition with volumes to be shutdown,
949  * and then it attempts to allocate remaining
950  * threads based upon the amount of work left
951  */
952 static void
953 ShutdownCreateSchedule(vshutdown_thread_t * params)
954 {
955     struct DiskPartition * diskP;
956     int sum, thr_workload, thr_left;
957     int part_residue[VOLMAXPARTS+1];
958     Device id;
959
960     /* compute the total number of outstanding volumes */
961     sum = 0;
962     for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
963         sum += diskP->vol_list.len;
964     }
965     
966     params->schedule_version++;
967     params->vol_remaining = sum;
968
969     if (!sum)
970         return;
971
972     /* compute average per-thread workload */
973     thr_workload = sum / params->n_threads;
974     if (sum % params->n_threads)
975         thr_workload++;
976
977     thr_left = params->n_threads;
978     memset(&part_residue, 0, sizeof(part_residue));
979
980     /* for fairness, give every partition with volumes remaining
981      * at least one thread */
982     for (diskP = DiskPartitionList; diskP && thr_left; diskP = diskP->next) {
983         id = diskP->device;
984         if (diskP->vol_list.len) {
985             params->part_thread_target[id] = 1;
986             thr_left--;
987         } else {
988             params->part_thread_target[id] = 0;
989         }
990     }
991
992     if (thr_left && thr_workload) {
993         /* compute length-weighted workloads */
994         int delta;
995
996         for (diskP = DiskPartitionList; diskP && thr_left; diskP = diskP->next) {
997             id = diskP->device;
998             delta = (diskP->vol_list.len / thr_workload) -
999                 params->part_thread_target[id];
1000             if (delta < 0) {
1001                 continue;
1002             }
1003             if (delta < thr_left) {
1004                 params->part_thread_target[id] += delta;
1005                 thr_left -= delta;
1006             } else {
1007                 params->part_thread_target[id] += thr_left;
1008                 thr_left = 0;
1009                 break;
1010             }
1011         }
1012     }
1013
1014     if (thr_left) {
1015         /* try to assign any leftover threads to partitions that
1016          * had volume lengths closer to needing thread_target+1 */
1017         int max_residue, max_id;
1018
1019         /* compute the residues */
1020         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1021             id = diskP->device;
1022             part_residue[id] = diskP->vol_list.len - 
1023                 (params->part_thread_target[id] * thr_workload);
1024         }
1025
1026         /* now try to allocate remaining threads to partitions with the
1027          * highest residues */
1028         while (thr_left) {
1029             max_residue = 0;
1030             for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1031                 id = diskP->device;
1032                 if (part_residue[id] > max_residue) {
1033                     max_residue = part_residue[id];
1034                     max_id = id;
1035                 }
1036             }
1037
1038             if (!max_residue) {
1039                 break;
1040             }
1041
1042             params->part_thread_target[max_id]++;
1043             thr_left--;
1044             part_residue[max_id] = 0;
1045         }
1046     }
1047
1048     if (thr_left) {
1049         /* punt and give any remaining threads equally to each partition */
1050         int alloc;
1051         if (thr_left >= params->n_parts) {
1052             alloc = thr_left / params->n_parts;
1053             for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1054                 id = diskP->device;
1055                 params->part_thread_target[id] += alloc;
1056                 thr_left -= alloc;
1057             }
1058         }
1059
1060         /* finish off the last of the threads */
1061         for (diskP = DiskPartitionList; thr_left && diskP; diskP = diskP->next) {
1062             id = diskP->device;
1063             params->part_thread_target[id]++;
1064             thr_left--;
1065         }
1066     }
1067 }
1068
1069 /* worker thread for parallel shutdown */
1070 static void *
1071 VShutdownThread(void * args)
1072 {
1073     struct rx_queue *qp;
1074     Volume * vp;
1075     vshutdown_thread_t * params;
1076     int part, code, found, pass, schedule_version_save, count;
1077     struct DiskPartition *diskP;
1078     struct diskpartition_queue_t * dpq;
1079     Device id;
1080
1081     params = (vshutdown_thread_t *) args;
1082
1083     /* acquire the shutdown pass 0 lock */
1084     assert(pthread_mutex_lock(&params->lock) == 0);
1085
1086     /* if there's still pass 0 work to be done,
1087      * get a work entry, and do a pass 0 shutdown */
1088     if (queue_IsNotEmpty(params)) {
1089         dpq = queue_First(params, diskpartition_queue_t);
1090         queue_Remove(dpq);
1091         assert(pthread_mutex_unlock(&params->lock) == 0);
1092         diskP = dpq->diskP;
1093         free(dpq);
1094         id = diskP->device;
1095
1096         count = 0;
1097         while (ShutdownVolumeWalk_r(diskP, 0, &params->part_pass_head[id]))
1098             count++;
1099         params->stats[0][diskP->device] = count;
1100         assert(pthread_mutex_lock(&params->lock) == 0);
1101     }
1102
1103     params->n_threads_complete++;
1104     if (params->n_threads_complete == params->n_threads) {
1105       /* notify control thread that all workers have completed pass 0 */
1106       assert(pthread_cond_signal(&params->master_cv) == 0);
1107     }
1108     while (params->pass == 0) {
1109       assert(pthread_cond_wait(&params->cv, &params->lock) == 0);
1110     }
1111
1112     /* switch locks */
1113     assert(pthread_mutex_unlock(&params->lock) == 0);
1114     VOL_LOCK;
1115
1116     pass = params->pass;
1117     assert(pass > 0);
1118
1119     /* now escalate through the more complicated shutdowns */
1120     while (pass <= 3) {
1121         schedule_version_save = params->schedule_version;
1122         found = 0;
1123         /* find a disk partition to work on */
1124         for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1125             id = diskP->device;
1126             if (params->part_thread_target[id] && !params->part_done_pass[id]) {
1127                 params->part_thread_target[id]--;
1128                 found = 1;
1129                 break;
1130             }
1131         }
1132         
1133         if (!found) {
1134             /* hmm. for some reason the controller thread couldn't find anything for 
1135              * us to do. let's see if there's anything we can do */
1136             for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1137                 id = diskP->device;
1138                 if (diskP->vol_list.len && !params->part_done_pass[id]) {
1139                     found = 1;
1140                     break;
1141                 } else if (!params->part_done_pass[id]) {
1142                     params->part_done_pass[id] = 1;
1143                     params->n_parts_done_pass++;
1144                     if (pass == 3) {
1145                         Log("VShutdown:  done shutting down volumes on partition %s.\n",
1146                             VPartitionPath(diskP));
1147                     }
1148                 }
1149             }
1150         }
1151         
1152         /* do work on this partition until either the controller
1153          * creates a new schedule, or we run out of things to do
1154          * on this partition */
1155         if (found) {
1156             count = 0;
1157             while (!params->part_done_pass[id] &&
1158                    (schedule_version_save == params->schedule_version)) {
1159                 /* ShutdownVolumeWalk_r will drop the glock internally */
1160                 if (!ShutdownVolumeWalk_r(diskP, pass, &params->part_pass_head[id])) {
1161                     if (!params->part_done_pass[id]) {
1162                         params->part_done_pass[id] = 1;
1163                         params->n_parts_done_pass++;
1164                         if (pass == 3) {
1165                             Log("VShutdown:  done shutting down volumes on partition %s.\n",
1166                                 VPartitionPath(diskP));
1167                         }
1168                     }
1169                     break;
1170                 }
1171                 count++;
1172             }
1173
1174             params->stats[pass][id] += count;
1175         } else {
1176             /* ok, everyone is done this pass, proceed */
1177
1178             /* barrier lock */
1179             params->n_threads_complete++;
1180             while (params->pass == pass) {
1181                 if (params->n_threads_complete == params->n_threads) {
1182                     /* we are the last thread to complete, so we will
1183                      * reinitialize worker pool state for the next pass */
1184                     params->n_threads_complete = 0;
1185                     params->n_parts_done_pass = 0;
1186                     params->pass++;
1187                     for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
1188                         id = diskP->device;
1189                         params->part_done_pass[id] = 0;
1190                         params->part_pass_head[id] = queue_First(&diskP->vol_list, rx_queue);
1191                     }
1192
1193                     /* compute a new thread schedule before releasing all the workers */
1194                     ShutdownCreateSchedule(params);
1195
1196                     /* wake up all the workers */
1197                     assert(pthread_cond_broadcast(&params->cv) == 0);
1198
1199                     VOL_UNLOCK;
1200                     Log("VShutdown:  pass %d completed using %d threads on %d partitions\n",
1201                         pass, params->n_threads, params->n_parts);
1202                     VOL_LOCK;
1203                 } else {
1204                     assert(pthread_cond_wait(&params->cv, &vol_glock_mutex) == 0);
1205                 }
1206             }
1207             pass = params->pass;
1208         }
1209         
1210         /* for fairness */
1211         VOL_UNLOCK;
1212         pthread_yield();
1213         VOL_LOCK;
1214     }
1215
1216     VOL_UNLOCK;
1217
1218     return NULL;
1219 }
1220
1221 /* shut down all volumes on a given disk partition 
1222  *
1223  * note that this function will not allow mp-fast
1224  * shutdown of a partition */
1225 int
1226 VShutdownByPartition_r(struct DiskPartition * dp)
1227 {
1228     int pass, retVal;
1229     int pass_stats[4];
1230     int total;
1231
1232     /* wait for other exclusive ops to finish */
1233     VVByPListWait_r(dp);
1234
1235     /* begin exclusive access */
1236     VVByPListBeginExclusive_r(dp);
1237
1238     /* pick the low-hanging fruit first,
1239      * then do the complicated ones last 
1240      * (has the advantage of keeping
1241      *  in-use volumes up until the bitter end) */
1242     for (pass = 0, total=0; pass < 4; pass++) {
1243         pass_stats[pass] = ShutdownVByPForPass_r(dp, pass);
1244         total += pass_stats[pass];
1245     }
1246
1247     /* end exclusive access */
1248     VVByPListEndExclusive_r(dp);
1249
1250     Log("VShutdownByPartition:  shut down %d volumes on %s (pass[0]=%d, pass[1]=%d, pass[2]=%d, pass[3]=%d)\n",
1251         total, VPartitionPath(dp), pass_stats[0], pass_stats[1], pass_stats[2], pass_stats[3]);
1252
1253     return retVal;
1254 }
1255
1256 /* internal shutdown functionality
1257  *
1258  * for multi-pass shutdown:
1259  * 0 to only "shutdown" {pre,un}attached and error state volumes
1260  * 1 to also shutdown attached volumes w/ volume header loaded
1261  * 2 to also shutdown attached volumes w/o volume header loaded
1262  * 3 to also shutdown exclusive state volumes 
1263  *
1264  * caller MUST hold exclusive access on the hash chain
1265  * because we drop vol_glock_mutex internally
1266  * 
1267  * this function is reentrant for passes 1--3 
1268  * (e.g. multiple threads can cooperate to 
1269  *  shutdown a partition mp-fast)
1270  *
1271  * pass 0 is not scaleable because the volume state data is
1272  * synchronized by vol_glock mutex, and the locking overhead
1273  * is too high to drop the lock long enough to do linked list
1274  * traversal
1275  */
1276 static int
1277 ShutdownVByPForPass_r(struct DiskPartition * dp, int pass)
1278 {
1279     struct rx_queue * q = queue_First(&dp->vol_list, rx_queue);
1280     register int i = 0;
1281
1282     while (ShutdownVolumeWalk_r(dp, pass, &q))
1283         i++;
1284
1285     return i;
1286 }
1287
1288 /* conditionally shutdown one volume on partition dp
1289  * returns 1 if a volume was shutdown in this pass,
1290  * 0 otherwise */
1291 static int
1292 ShutdownVolumeWalk_r(struct DiskPartition * dp, int pass,
1293                      struct rx_queue ** idx)
1294 {
1295     struct rx_queue *qp, *nqp;
1296     Volume * vp;
1297
1298     qp = *idx;
1299
1300     for (queue_ScanFrom(&dp->vol_list, qp, qp, nqp, rx_queue)) {
1301         vp = (Volume *) (((char *)qp) - offsetof(Volume, vol_list));
1302         
1303         switch (pass) {
1304         case 0:
1305             if ((V_attachState(vp) != VOL_STATE_UNATTACHED) &&
1306                 (V_attachState(vp) != VOL_STATE_ERROR) &&
1307                 (V_attachState(vp) != VOL_STATE_PREATTACHED)) {
1308                 break;
1309             }
1310         case 1:
1311             if ((V_attachState(vp) == VOL_STATE_ATTACHED) &&
1312                 (vp->header == NULL)) {
1313                 break;
1314             }
1315         case 2:
1316             if (IsExclusiveState(V_attachState(vp))) {
1317                 break;
1318             }
1319         case 3:
1320             *idx = nqp;
1321             DeleteVolumeFromVByPList_r(vp);
1322             VShutdownVolume_r(vp);
1323             vp = NULL;
1324             return 1;
1325         }
1326     }
1327
1328     return 0;
1329 }
1330
1331 /*
1332  * shutdown a specific volume
1333  */
1334 /* caller MUST NOT hold a heavyweight ref on vp */
1335 int
1336 VShutdownVolume_r(Volume * vp)
1337 {
1338     int code;
1339
1340     VCreateReservation_r(vp);
1341
1342     if (LogLevel >= 5) {
1343         Log("VShutdownVolume_r:  vid=%u, device=%d, state=%hu\n",
1344             vp->hashid, vp->partition->device, V_attachState(vp));
1345     }
1346
1347     /* wait for other blocking ops to finish */
1348     VWaitExclusiveState_r(vp);
1349
1350     assert(IsValidState(V_attachState(vp)));
1351     
1352     switch(V_attachState(vp)) {
1353     case VOL_STATE_SALVAGING:
1354         /* make sure salvager knows we don't want
1355          * the volume back */
1356         VCancelSalvage_r(vp, SALVSYNC_SHUTDOWN);
1357     case VOL_STATE_PREATTACHED:
1358     case VOL_STATE_ERROR:
1359         VChangeState_r(vp, VOL_STATE_UNATTACHED);
1360     case VOL_STATE_UNATTACHED:
1361         break;
1362     case VOL_STATE_GOING_OFFLINE:
1363     case VOL_STATE_SHUTTING_DOWN:
1364     case VOL_STATE_ATTACHED:
1365         code = VHold_r(vp);
1366         if (!code) {
1367             if (LogLevel >= 5)
1368                 Log("VShutdown:  Attempting to take volume %u offline.\n",
1369                     vp->hashid);
1370
1371             /* take the volume offline (drops reference count) */
1372             VOffline_r(vp, "File server was shut down");
1373         }
1374         break;
1375     }
1376     
1377     VCancelReservation_r(vp);
1378     vp = NULL;
1379     return 0;
1380 }
1381 #endif /* AFS_DEMAND_ATTACH_FS */
1382
1383
1384 /***************************************************/
1385 /* Header I/O routines                             */
1386 /***************************************************/
1387
1388 /* open a descriptor for the inode (h),
1389  * read in an on-disk structure into buffer (to) of size (size),
1390  * verify versionstamp in structure has magic (magic) and
1391  * optionally verify version (version) if (version) is nonzero
1392  */
1393 static void
1394 ReadHeader(Error * ec, IHandle_t * h, char *to, int size, bit32 magic,
1395            bit32 version)
1396 {
1397     struct versionStamp *vsn;
1398     FdHandle_t *fdP;
1399
1400     *ec = 0;
1401     if (h == NULL) {
1402         *ec = VSALVAGE;
1403         return;
1404     }
1405
1406     fdP = IH_OPEN(h);
1407     if (fdP == NULL) {
1408         *ec = VSALVAGE;
1409         return;
1410     }
1411
1412     if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1413         *ec = VSALVAGE;
1414         FDH_REALLYCLOSE(fdP);
1415         return;
1416     }
1417     vsn = (struct versionStamp *)to;
1418     if (FDH_READ(fdP, to, size) != size || vsn->magic != magic) {
1419         *ec = VSALVAGE;
1420         FDH_REALLYCLOSE(fdP);
1421         return;
1422     }
1423     FDH_CLOSE(fdP);
1424
1425     /* Check is conditional, in case caller wants to inspect version himself */
1426     if (version && vsn->version != version) {
1427         *ec = VSALVAGE;
1428     }
1429 }
1430
1431 void
1432 WriteVolumeHeader_r(Error * ec, Volume * vp)
1433 {
1434     IHandle_t *h = V_diskDataHandle(vp);
1435     FdHandle_t *fdP;
1436
1437     *ec = 0;
1438
1439     fdP = IH_OPEN(h);
1440     if (fdP == NULL) {
1441         *ec = VSALVAGE;
1442         return;
1443     }
1444     if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
1445         *ec = VSALVAGE;
1446         FDH_REALLYCLOSE(fdP);
1447         return;
1448     }
1449     if (FDH_WRITE(fdP, (char *)&V_disk(vp), sizeof(V_disk(vp)))
1450         != sizeof(V_disk(vp))) {
1451         *ec = VSALVAGE;
1452         FDH_REALLYCLOSE(fdP);
1453         return;
1454     }
1455     FDH_CLOSE(fdP);
1456 }
1457
1458 /* VolumeHeaderToDisk
1459  * Allows for storing 64 bit inode numbers in on-disk volume header
1460  * file.
1461  */
1462 /* convert in-memory representation of a volume header to the
1463  * on-disk representation of a volume header */
1464 void
1465 VolumeHeaderToDisk(VolumeDiskHeader_t * dh, VolumeHeader_t * h)
1466 {
1467
1468     memset((char *)dh, 0, sizeof(VolumeDiskHeader_t));
1469     dh->stamp = h->stamp;
1470     dh->id = h->id;
1471     dh->parent = h->parent;
1472
1473 #ifdef AFS_64BIT_IOPS_ENV
1474     dh->volumeInfo_lo = (afs_int32) h->volumeInfo & 0xffffffff;
1475     dh->volumeInfo_hi = (afs_int32) (h->volumeInfo >> 32) & 0xffffffff;
1476     dh->smallVnodeIndex_lo = (afs_int32) h->smallVnodeIndex & 0xffffffff;
1477     dh->smallVnodeIndex_hi =
1478         (afs_int32) (h->smallVnodeIndex >> 32) & 0xffffffff;
1479     dh->largeVnodeIndex_lo = (afs_int32) h->largeVnodeIndex & 0xffffffff;
1480     dh->largeVnodeIndex_hi =
1481         (afs_int32) (h->largeVnodeIndex >> 32) & 0xffffffff;
1482     dh->linkTable_lo = (afs_int32) h->linkTable & 0xffffffff;
1483     dh->linkTable_hi = (afs_int32) (h->linkTable >> 32) & 0xffffffff;
1484 #else
1485     dh->volumeInfo_lo = h->volumeInfo;
1486     dh->smallVnodeIndex_lo = h->smallVnodeIndex;
1487     dh->largeVnodeIndex_lo = h->largeVnodeIndex;
1488     dh->linkTable_lo = h->linkTable;
1489 #endif
1490 }
1491
1492 /* DiskToVolumeHeader
1493  * Converts an on-disk representation of a volume header to
1494  * the in-memory representation of a volume header.
1495  *
1496  * Makes the assumption that AFS has *always* 
1497  * zero'd the volume header file so that high parts of inode
1498  * numbers are 0 in older (SGI EFS) volume header files.
1499  */
1500 void
1501 DiskToVolumeHeader(VolumeHeader_t * h, VolumeDiskHeader_t * dh)
1502 {
1503     memset((char *)h, 0, sizeof(VolumeHeader_t));
1504     h->stamp = dh->stamp;
1505     h->id = dh->id;
1506     h->parent = dh->parent;
1507
1508 #ifdef AFS_64BIT_IOPS_ENV
1509     h->volumeInfo =
1510         (Inode) dh->volumeInfo_lo | ((Inode) dh->volumeInfo_hi << 32);
1511
1512     h->smallVnodeIndex =
1513         (Inode) dh->smallVnodeIndex_lo | ((Inode) dh->
1514                                           smallVnodeIndex_hi << 32);
1515
1516     h->largeVnodeIndex =
1517         (Inode) dh->largeVnodeIndex_lo | ((Inode) dh->
1518                                           largeVnodeIndex_hi << 32);
1519     h->linkTable =
1520         (Inode) dh->linkTable_lo | ((Inode) dh->linkTable_hi << 32);
1521 #else
1522     h->volumeInfo = dh->volumeInfo_lo;
1523     h->smallVnodeIndex = dh->smallVnodeIndex_lo;
1524     h->largeVnodeIndex = dh->largeVnodeIndex_lo;
1525     h->linkTable = dh->linkTable_lo;
1526 #endif
1527 }
1528
1529
1530 /***************************************************/
1531 /* Volume Attachment routines                      */
1532 /***************************************************/
1533
1534 #ifdef AFS_DEMAND_ATTACH_FS
1535 /* pre-attach a volume given its path 
1536  *
1537  * a pre-attached volume will only have its partition
1538  * and hashid fields initialized
1539  *
1540  * at first call to VGetVolume, the volume will be
1541  * fully attached
1542  */
1543 Volume *
1544 VPreAttachVolumeByName(Error * ec, char *partition, char *name, int mode)
1545 {
1546     Volume * vp;
1547     VOL_LOCK;
1548     vp = VPreAttachVolumeByName_r(ec, partition, name, mode);
1549     VOL_UNLOCK;
1550     return vp;
1551 }
1552
1553 Volume *
1554 VPreAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
1555 {
1556     register Volume *vp = NULL;
1557     int fd, n;
1558     struct afs_stat status;
1559     struct DiskPartition *partp;
1560     char path[64];
1561     int isbusy = 0;
1562     VolId volumeId;
1563     *ec = 0;
1564
1565     assert(programType == fileServer);
1566
1567     if (!(partp = VGetPartition_r(partition, 0))) {
1568         *ec = VNOVOL;
1569         Log("VPreAttachVolume:  Error getting partition (%s)\n", partition);
1570         return NULL;
1571     }
1572
1573     volumeId = VolumeNumber(name);
1574
1575     vp = VLookupVolume_r(ec, volumeId, NULL);
1576     if (*ec) {
1577         return NULL;
1578     }
1579
1580     return VPreAttachVolumeById_r(ec, partp, vp, volumeId);
1581 }
1582
1583 /* pre-attach a volume given its partition and volume id
1584  *
1585  * if vp == NULL, then a new vp is created
1586  * if vp != NULL, then we assumed it is already on the hash chain
1587  */
1588 Volume * 
1589 VPreAttachVolumeById_r(Error * ec, struct DiskPartition * partp, 
1590                        Volume * vp, int vid)
1591 {
1592     Volume *nvp = NULL;
1593
1594     *ec = 0;
1595
1596     /* check to see if pre-attach already happened */
1597     if (vp && 
1598         (V_attachState(vp) != VOL_STATE_UNATTACHED) && 
1599         !IsErrorState(V_attachState(vp))) {
1600         goto done;
1601     } else if (vp) {
1602         /* we're re-attaching a volume; clear out some old state */
1603         memset(&vp->salvage, 0, sizeof(struct VolumeOnlineSalvage));
1604     } else {
1605         /* if we need to allocate a new Volume struct,
1606          * go ahead and drop the vol glock, otherwise
1607          * do the basic setup synchronised, as it's
1608          * probably not worth dropping the lock */
1609         VOL_UNLOCK;
1610
1611         /* allocate the volume structure */
1612         vp = nvp = (Volume *) malloc(sizeof(Volume));
1613         assert(vp != NULL);
1614         memset(vp, 0, sizeof(Volume));
1615         assert(pthread_cond_init(&V_attachCV(vp), NULL) == 0);
1616     }
1617
1618     /* link the volume with its associated vice partition */
1619     vp->device = partp->device;
1620     vp->partition = partp;
1621     vp->hashid = vid;
1622
1623     /* if we dropped the lock, reacquire the lock,
1624      * check for pre-attach races, and then add
1625      * the volume to the hash table */
1626     if (nvp) {
1627         VOL_LOCK;
1628         nvp = VLookupVolume_r(ec, vid, NULL);
1629         if (*ec) {
1630             free(vp);
1631             vp = NULL;
1632             goto done;
1633         } else if (nvp) { /* race detected */
1634             free(vp);
1635             vp = nvp;
1636             goto done;
1637         } else {
1638           /* hack to make up for VChangeState_r() decrementing 
1639            * the old state counter */
1640           VStats.state_levels[0]++;
1641         }
1642     }
1643
1644     /* put pre-attached volume onto the hash table
1645      * and bring it up to the pre-attached state */
1646     AddVolumeToHashTable(vp, vp->hashid);
1647     AddVolumeToVByPList_r(vp);
1648     VLRU_Init_Node_r(vp);
1649     VChangeState_r(vp, VOL_STATE_PREATTACHED);
1650
1651     if (LogLevel >= 5)
1652         Log("VPreAttachVolumeById_r:  volume %u pre-attached\n", vp->hashid);
1653
1654   done:
1655     if (*ec)
1656         return NULL;
1657     else
1658         return vp;
1659 }
1660 #endif /* AFS_DEMAND_ATTACH_FS */
1661
1662 /* Attach an existing volume, given its pathname, and return a
1663    pointer to the volume header information.  The volume also
1664    normally goes online at this time.  An offline volume
1665    must be reattached to make it go online */
1666 Volume *
1667 VAttachVolumeByName(Error * ec, char *partition, char *name, int mode)
1668 {
1669     Volume *retVal;
1670     VOL_LOCK;
1671     retVal = VAttachVolumeByName_r(ec, partition, name, mode);
1672     VOL_UNLOCK;
1673     return retVal;
1674 }
1675
1676 Volume *
1677 VAttachVolumeByName_r(Error * ec, char *partition, char *name, int mode)
1678 {
1679     register Volume *vp = NULL, *svp = NULL;
1680     int fd, n;
1681     struct afs_stat status;
1682     struct VolumeDiskHeader diskHeader;
1683     struct VolumeHeader iheader;
1684     struct DiskPartition *partp;
1685     char path[64];
1686     int isbusy = 0;
1687     VolId volumeId;
1688 #ifdef AFS_DEMAND_ATTACH_FS
1689     VolumeStats stats_save;
1690 #endif /* AFS_DEMAND_ATTACH_FS */
1691
1692     *ec = 0;
1693    
1694     volumeId = VolumeNumber(name);
1695
1696     if (!(partp = VGetPartition_r(partition, 0))) {
1697         *ec = VNOVOL;
1698         Log("VAttachVolume: Error getting partition (%s)\n", partition);
1699         goto done;
1700     }
1701
1702     if (programType == volumeUtility) {
1703         assert(VInit == 3);
1704         VLockPartition_r(partition);
1705     } else if (programType == fileServer) {
1706 #ifdef AFS_DEMAND_ATTACH_FS
1707         /* lookup the volume in the hash table */
1708         vp = VLookupVolume_r(ec, volumeId, NULL);
1709         if (*ec) {
1710             return NULL;
1711         }
1712
1713         if (vp) {
1714             /* save any counters that are supposed to
1715              * be monotonically increasing over the
1716              * lifetime of the fileserver */
1717             memcpy(&stats_save, &vp->stats, sizeof(VolumeStats));
1718         } else {
1719             memset(&stats_save, 0, sizeof(VolumeStats));
1720         }
1721
1722         /* if there's something in the hash table, and it's not
1723          * in the pre-attach state, then we may need to detach
1724          * it before proceeding */
1725         if (vp && (V_attachState(vp) != VOL_STATE_PREATTACHED)) {
1726             VCreateReservation_r(vp);
1727             VWaitExclusiveState_r(vp);
1728
1729             /* at this point state must be one of:
1730              *   UNATTACHED,
1731              *   ATTACHED,
1732              *   SHUTTING_DOWN,
1733              *   GOING_OFFLINE,
1734              *   SALVAGING,
1735              *   ERROR
1736              */
1737
1738             if (vp->specialStatus == VBUSY)
1739                 isbusy = 1;
1740             
1741             /* if it's already attached, see if we can return it */
1742             if (V_attachState(vp) == VOL_STATE_ATTACHED) {
1743                 VGetVolumeByVp_r(ec, vp);
1744                 if (V_inUse(vp)) {
1745                     VCancelReservation_r(vp);
1746                     return vp;
1747                 }
1748
1749                 /* otherwise, we need to detach, and attempt to re-attach */
1750                 VDetachVolume_r(ec, vp);
1751                 if (*ec) {
1752                     Log("VAttachVolume: Error detaching old volume instance (%s)\n", name);
1753                 }
1754             } else {
1755                 /* if it isn't fully attached, delete from the hash tables,
1756                    and let the refcounter handle the rest */
1757                 DeleteVolumeFromHashTable(vp);
1758                 DeleteVolumeFromVByPList_r(vp);
1759             }
1760
1761             VCancelReservation_r(vp);
1762             vp = NULL;
1763         }
1764
1765         /* pre-attach volume if it hasn't been done yet */
1766         if (!vp || 
1767             (V_attachState(vp) == VOL_STATE_UNATTACHED) ||
1768             (V_attachState(vp) == VOL_STATE_ERROR)) {
1769             svp = vp;
1770             vp = VPreAttachVolumeById_r(ec, partp, vp, volumeId);
1771             if (*ec) {
1772                 return NULL;
1773             }
1774         }
1775
1776         assert(vp != NULL);
1777
1778         /* handle pre-attach races 
1779          *
1780          * multiple threads can race to pre-attach a volume,
1781          * but we can't let them race beyond that
1782          * 
1783          * our solution is to let the first thread to bring
1784          * the volume into an exclusive state win; the other
1785          * threads just wait until it finishes bringing the
1786          * volume online, and then they do a vgetvolumebyvp
1787          */
1788         if (svp && (svp != vp)) {
1789             /* wait for other exclusive ops to finish */
1790             VCreateReservation_r(vp);
1791             VWaitExclusiveState_r(vp);
1792
1793             /* get a heavyweight ref, kill the lightweight ref, and return */
1794             VGetVolumeByVp_r(ec, vp);
1795             VCancelReservation_r(vp);
1796             return vp;
1797         }
1798
1799         /* at this point, we are chosen as the thread to do
1800          * demand attachment for this volume. all other threads
1801          * doing a getvolume on vp->hashid will block until we finish */
1802
1803         /* make sure any old header cache entries are invalidated
1804          * before proceeding */
1805         FreeVolumeHeader(vp);
1806
1807         VChangeState_r(vp, VOL_STATE_ATTACHING);
1808
1809         /* restore any saved counters */
1810         memcpy(&vp->stats, &stats_save, sizeof(VolumeStats));
1811 #else /* AFS_DEMAND_ATTACH_FS */
1812         vp = VGetVolume_r(ec, volumeId);
1813         if (vp) {
1814             if (V_inUse(vp))
1815                 return vp;
1816             if (vp->specialStatus == VBUSY)
1817                 isbusy = 1;
1818             VDetachVolume_r(ec, vp);
1819             if (*ec) {
1820                 Log("VAttachVolume: Error detaching volume (%s)\n", name);
1821             }
1822             vp = NULL;
1823         }
1824 #endif /* AFS_DEMAND_ATTACH_FS */
1825     }
1826
1827     *ec = 0;
1828     strcpy(path, VPartitionPath(partp));
1829
1830     VOL_UNLOCK;
1831
1832     strcat(path, "/");
1833     strcat(path, name);
1834     if ((fd = afs_open(path, O_RDONLY)) == -1 || afs_fstat(fd, &status) == -1) {
1835         Log("VAttachVolume: Failed to open %s (errno %d)\n", path, errno);
1836         if (fd > -1)
1837             close(fd);
1838         *ec = VNOVOL;
1839         VOL_LOCK;
1840         goto done;
1841     }
1842     n = read(fd, &diskHeader, sizeof(diskHeader));
1843     close(fd);
1844     if (n != sizeof(diskHeader)
1845         || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
1846         Log("VAttachVolume: Error reading volume header %s\n", path);
1847         *ec = VSALVAGE;
1848         VOL_LOCK;
1849         goto done;
1850     }
1851     if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
1852         Log("VAttachVolume: Volume %s, version number is incorrect; volume needs salvaged\n", path);
1853         *ec = VSALVAGE;
1854         VOL_LOCK;
1855         goto done;
1856     }
1857
1858     DiskToVolumeHeader(&iheader, &diskHeader);
1859 #ifdef FSSYNC_BUILD_CLIENT
1860     if (programType == volumeUtility && mode != V_SECRETLY && mode != V_PEEK) {
1861         VOL_LOCK;
1862         if (FSYNC_VolOp(iheader.id, partition, FSYNC_VOL_NEEDVOLUME, mode, NULL)
1863             != SYNC_OK) {
1864             Log("VAttachVolume: attach of volume %u apparently denied by file server\n", iheader.id);
1865             *ec = VNOVOL;       /* XXXX */
1866             goto done;
1867         }
1868         VOL_UNLOCK;
1869     }
1870 #endif
1871
1872     if (!vp) {
1873       vp = (Volume *) calloc(1, sizeof(Volume));
1874       assert(vp != NULL);
1875       vp->device = partp->device;
1876       vp->partition = partp;
1877 #ifdef AFS_DEMAND_ATTACH_FS
1878       assert(pthread_cond_init(&V_attachCV(vp), NULL) == 0);
1879 #endif /* AFS_DEMAND_ATTACH_FS */
1880     }
1881
1882     /* attach2 is entered without any locks, and returns
1883      * with vol_glock_mutex held */
1884     vp = attach2(ec, volumeId, path, &iheader, partp, vp, isbusy, mode);
1885
1886     if (programType == volumeUtility && vp) {
1887 #ifdef AFS_DEMAND_ATTACH_FS
1888         /* for dafs, we should tell the fileserver, except for V_PEEK
1889          * where we know it is not necessary */
1890         if (mode == V_PEEK) {
1891             vp->needsPutBack = 0;
1892         } else {
1893             vp->needsPutBack = 1;
1894         }
1895 #else /* !AFS_DEMAND_ATTACH_FS */
1896         /* duplicate computation in fssync.c about whether the server
1897          * takes the volume offline or not.  If the volume isn't
1898          * offline, we must not return it when we detach the volume,
1899          * or the server will abort */
1900         if (mode == V_READONLY || mode == V_PEEK
1901             || (!VolumeWriteable(vp) && (mode == V_CLONE || mode == V_DUMP)))
1902             vp->needsPutBack = 0;
1903         else
1904             vp->needsPutBack = 1;
1905 #endif /* !AFS_DEMAND_ATTACH_FS */
1906     }
1907     /* OK, there's a problem here, but one that I don't know how to
1908      * fix right now, and that I don't think should arise often.
1909      * Basically, we should only put back this volume to the server if
1910      * it was given to us by the server, but since we don't have a vp,
1911      * we can't run the VolumeWriteable function to find out as we do
1912      * above when computing vp->needsPutBack.  So we send it back, but
1913      * there's a path in VAttachVolume on the server which may abort
1914      * if this volume doesn't have a header.  Should be pretty rare
1915      * for all of that to happen, but if it does, probably the right
1916      * fix is for the server to allow the return of readonly volumes
1917      * that it doesn't think are really checked out. */
1918 #ifdef FSSYNC_BUILD_CLIENT
1919     if (programType == volumeUtility && vp == NULL &&
1920         mode != V_SECRETLY && mode != V_PEEK) {
1921         FSYNC_VolOp(iheader.id, partition, FSYNC_VOL_ON, 0, NULL);
1922     } else 
1923 #endif
1924     if (programType == fileServer && vp) {
1925         V_needsCallback(vp) = 0;
1926 #ifdef  notdef
1927         if (VInit >= 2 && V_BreakVolumeCallbacks) {
1928             Log("VAttachVolume: Volume %u was changed externally; breaking callbacks\n", V_id(vp));
1929             (*V_BreakVolumeCallbacks) (V_id(vp));
1930         }
1931 #endif
1932         VUpdateVolume_r(ec, vp, 0);
1933         if (*ec) {
1934             Log("VAttachVolume: Error updating volume\n");
1935             if (vp)
1936                 VPutVolume_r(vp);
1937             goto done;
1938         }
1939         if (VolumeWriteable(vp) && V_dontSalvage(vp) == 0) {
1940 #ifndef AFS_DEMAND_ATTACH_FS
1941             /* This is a hack: by temporarily setting the incore
1942              * dontSalvage flag ON, the volume will be put back on the
1943              * Update list (with dontSalvage OFF again).  It will then
1944              * come back in N minutes with DONT_SALVAGE eventually
1945              * set.  This is the way that volumes that have never had
1946              * it set get it set; or that volumes that have been
1947              * offline without DONT SALVAGE having been set also
1948              * eventually get it set */
1949             V_dontSalvage(vp) = DONT_SALVAGE;
1950 #endif /* !AFS_DEMAND_ATTACH_FS */
1951             VAddToVolumeUpdateList_r(ec, vp);
1952             if (*ec) {
1953                 Log("VAttachVolume: Error adding volume to update list\n");
1954                 if (vp)
1955                     VPutVolume_r(vp);
1956                 goto done;
1957             }
1958         }
1959         if (LogLevel)
1960             Log("VOnline:  volume %u (%s) attached and online\n", V_id(vp),
1961                 V_name(vp));
1962     }
1963   done:
1964     if (programType == volumeUtility) {
1965         VUnlockPartition_r(partition);
1966     }
1967     if (*ec) {
1968 #ifdef AFS_DEMAND_ATTACH_FS
1969         if (vp) {
1970             V_attachState(vp) = VOL_STATE_ERROR;
1971             assert(pthread_cond_broadcast(&V_attachCV(vp)) == 0);
1972         }
1973 #endif /* AFS_DEMAND_ATTACH_FS */
1974         return NULL;
1975     } else {
1976         return vp;
1977     }
1978 }
1979
1980 #ifdef AFS_DEMAND_ATTACH_FS
1981 /* VAttachVolumeByVp_r
1982  *
1983  * finish attaching a volume that is
1984  * in a less than fully attached state
1985  */
1986 /* caller MUST hold a ref count on vp */
1987 static Volume *
1988 VAttachVolumeByVp_r(Error * ec, Volume * vp, int mode)
1989 {
1990     char name[VMAXPATHLEN];
1991     int fd, n, reserve = 0;
1992     struct afs_stat status;
1993     struct VolumeDiskHeader diskHeader;
1994     struct VolumeHeader iheader;
1995     struct DiskPartition *partp;
1996     char path[64];
1997     int isbusy = 0;
1998     VolId volumeId;
1999     Volume * nvp;
2000     VolumeStats stats_save;
2001     *ec = 0;
2002
2003     /* volume utility should never call AttachByVp */
2004     assert(programType == fileServer);
2005    
2006     volumeId = vp->hashid;
2007     partp = vp->partition;
2008     VolumeExternalName_r(volumeId, name, sizeof(name));
2009
2010
2011     /* if another thread is performing a blocking op, wait */
2012     VWaitExclusiveState_r(vp);
2013
2014     memcpy(&stats_save, &vp->stats, sizeof(VolumeStats));
2015
2016     /* if it's already attached, see if we can return it */
2017     if (V_attachState(vp) == VOL_STATE_ATTACHED) {
2018         VGetVolumeByVp_r(ec, vp);
2019         if (V_inUse(vp)) {
2020             return vp;
2021         } else {
2022             if (vp->specialStatus == VBUSY)
2023                 isbusy = 1;
2024             VDetachVolume_r(ec, vp);
2025             if (*ec) {
2026                 Log("VAttachVolume: Error detaching volume (%s)\n", name);
2027             }
2028             vp = NULL;
2029         }
2030     }
2031
2032     /* pre-attach volume if it hasn't been done yet */
2033     if (!vp || 
2034         (V_attachState(vp) == VOL_STATE_UNATTACHED) ||
2035         (V_attachState(vp) == VOL_STATE_ERROR)) {
2036         nvp = VPreAttachVolumeById_r(ec, partp, vp, volumeId);
2037         if (*ec) {
2038             return NULL;
2039         }
2040         if (nvp != vp) {
2041             reserve = 1;
2042             VCreateReservation_r(nvp);
2043             vp = nvp;
2044         }
2045     }
2046     
2047     assert(vp != NULL);
2048     VChangeState_r(vp, VOL_STATE_ATTACHING);
2049
2050     /* restore monotonically increasing stats */
2051     memcpy(&vp->stats, &stats_save, sizeof(VolumeStats));
2052
2053     *ec = 0;
2054
2055
2056     /* compute path to disk header, 
2057      * read in header, 
2058      * and verify magic and version stamps */
2059     strcpy(path, VPartitionPath(partp));
2060
2061     VOL_UNLOCK;
2062
2063     strcat(path, "/");
2064     strcat(path, name);
2065     if ((fd = afs_open(path, O_RDONLY)) == -1 || afs_fstat(fd, &status) == -1) {
2066         Log("VAttachVolume: Failed to open %s (errno %d)\n", path, errno);
2067         if (fd > -1)
2068             close(fd);
2069         *ec = VNOVOL;
2070         VOL_LOCK;
2071         goto done;
2072     }
2073     n = read(fd, &diskHeader, sizeof(diskHeader));
2074     close(fd);
2075     if (n != sizeof(diskHeader)
2076         || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
2077         Log("VAttachVolume: Error reading volume header %s\n", path);
2078         *ec = VSALVAGE;
2079         VOL_LOCK;
2080         goto done;
2081     }
2082     if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
2083         Log("VAttachVolume: Volume %s, version number is incorrect; volume needs salvaged\n", path);
2084         *ec = VSALVAGE;
2085         VOL_LOCK;
2086         goto done;
2087     }
2088
2089     /* convert on-disk header format to in-memory header format */
2090     DiskToVolumeHeader(&iheader, &diskHeader);
2091
2092     /* do volume attach
2093      *
2094      * NOTE: attach2 is entered without any locks, and returns
2095      * with vol_glock_mutex held */
2096     vp = attach2(ec, volumeId, path, &iheader, partp, vp, isbusy, mode);
2097
2098     if (*ec || vp == NULL) {
2099         goto done;
2100     }
2101
2102     V_needsCallback(vp) = 0;
2103     VUpdateVolume_r(ec, vp, 0);
2104     if (*ec) {
2105         Log("VAttachVolume: Error updating volume %u\n", vp->hashid);
2106         VPutVolume_r(vp);
2107         goto done;
2108     }
2109     if (VolumeWriteable(vp) && V_dontSalvage(vp) == 0) {
2110 #ifndef AFS_DEMAND_ATTACH_FS
2111         /* This is a hack: by temporarily setting the incore
2112          * dontSalvage flag ON, the volume will be put back on the
2113          * Update list (with dontSalvage OFF again).  It will then
2114          * come back in N minutes with DONT_SALVAGE eventually
2115          * set.  This is the way that volumes that have never had
2116          * it set get it set; or that volumes that have been
2117          * offline without DONT SALVAGE having been set also
2118          * eventually get it set */
2119         V_dontSalvage(vp) = DONT_SALVAGE;
2120 #endif /* !AFS_DEMAND_ATTACH_FS */
2121         VAddToVolumeUpdateList_r(ec, vp);
2122         if (*ec) {
2123             Log("VAttachVolume: Error adding volume %u to update list\n", vp->hashid);
2124             if (vp)
2125                 VPutVolume_r(vp);
2126             goto done;
2127         }
2128     }
2129     if (LogLevel)
2130         Log("VOnline:  volume %u (%s) attached and online\n", V_id(vp),
2131             V_name(vp));
2132   done:
2133     if (reserve) {
2134         VCancelReservation_r(nvp);
2135         reserve = 0;
2136     }
2137     if (*ec && (*ec != VOFFLINE) && (*ec != VSALVAGE)) {
2138         if (vp && !IsErrorState(V_attachState(vp))) {
2139             VChangeState_r(vp, VOL_STATE_ERROR);
2140         }
2141         return NULL;
2142     } else {
2143         return vp;
2144     }
2145 }
2146 #endif /* AFS_DEMAND_ATTACH_FS */
2147
2148 /*
2149  * called without any locks held
2150  * returns with vol_glock_mutex held
2151  */
2152 private Volume * 
2153 attach2(Error * ec, VolId volumeId, char *path, register struct VolumeHeader * header,
2154         struct DiskPartition * partp, register Volume * vp, int isbusy, int mode)
2155 {
2156     vp->specialStatus = (byte) (isbusy ? VBUSY : 0);
2157     IH_INIT(vp->vnodeIndex[vLarge].handle, partp->device, header->parent,
2158             header->largeVnodeIndex);
2159     IH_INIT(vp->vnodeIndex[vSmall].handle, partp->device, header->parent,
2160             header->smallVnodeIndex);
2161     IH_INIT(vp->diskDataHandle, partp->device, header->parent,
2162             header->volumeInfo);
2163     IH_INIT(vp->linkHandle, partp->device, header->parent, header->linkTable);
2164     vp->shuttingDown = 0;
2165     vp->goingOffline = 0;
2166     vp->nUsers = 1;
2167 #ifdef AFS_DEMAND_ATTACH_FS
2168     vp->stats.last_attach = FT_ApproxTime();
2169     vp->stats.attaches++;
2170 #endif
2171
2172     VOL_LOCK;
2173 #ifdef AFS_DEMAND_ATTACH_FS
2174     IncUInt64(&VStats.attaches);
2175 #endif
2176     vp->cacheCheck = ++VolumeCacheCheck;
2177     /* just in case this ever rolls over */
2178     if (!vp->cacheCheck)
2179         vp->cacheCheck = ++VolumeCacheCheck;
2180     GetVolumeHeader(vp);
2181     VOL_UNLOCK;
2182
2183 #if defined(AFS_DEMAND_ATTACH_FS) && defined(FSSYNC_BUILD_CLIENT)
2184     /* demand attach changes the V_PEEK mechanism
2185      *
2186      * we can now suck the current disk data structure over
2187      * the fssync interface without going to disk
2188      *
2189      * (technically, we don't need to restrict this feature
2190      *  to demand attach fileservers.  However, I'm trying
2191      *  to limit the number of common code changes)
2192      */
2193     if (programType != fileServer && mode == V_PEEK) {
2194         SYNC_response res;
2195         res.payload.len = sizeof(VolumeDiskData);
2196         res.payload.buf = &vp->header->diskstuff;
2197
2198         if (FSYNC_VolOp(volumeId,
2199                         VPartitionPath(partp),
2200                         FSYNC_VOL_QUERY_HDR,
2201                         FSYNC_WHATEVER,
2202                         &res) == SYNC_OK) {
2203             goto disk_header_loaded;
2204         }
2205     }
2206 #endif /* AFS_DEMAND_ATTACH_FS && FSSYNC_BUILD_CLIENT */
2207     (void)ReadHeader(ec, V_diskDataHandle(vp), (char *)&V_disk(vp),
2208                      sizeof(V_disk(vp)), VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
2209
2210 #ifdef AFS_DEMAND_ATTACH_FS
2211     /* update stats */
2212     VOL_LOCK;
2213     IncUInt64(&VStats.hdr_loads);
2214     IncUInt64(&vp->stats.hdr_loads);
2215     VOL_UNLOCK;
2216 #endif /* AFS_DEMAND_ATTACH_FS */
2217     
2218     if (*ec) {
2219         Log("VAttachVolume: Error reading diskDataHandle vol header %s; error=%u\n", path, *ec);
2220     }
2221
2222  disk_header_loaded:
2223
2224 #ifdef AFS_DEMAND_ATTACH_FS
2225     if (!*ec) {
2226
2227         /* check for pending volume operations */
2228         if (vp->pending_vol_op) {
2229             /* see if the pending volume op requires exclusive access */
2230             if (!VVolOpLeaveOnline_r(vp, vp->pending_vol_op)) {
2231                 /* mark the volume down */
2232                 *ec = VOFFLINE;
2233                 VChangeState_r(vp, VOL_STATE_UNATTACHED);
2234                 if (V_offlineMessage(vp)[0] == '\0')
2235                     strlcpy(V_offlineMessage(vp),
2236                             "A volume utility is running.", 
2237                             sizeof(V_offlineMessage(vp)));
2238                 V_offlineMessage(vp)[sizeof(V_offlineMessage(vp)) - 1] = '\0';
2239
2240                 /* check to see if we should set the specialStatus flag */
2241                 if (VVolOpSetVBusy_r(vp, vp->pending_vol_op)) {
2242                     vp->specialStatus = VBUSY;
2243                 }
2244             }
2245         }
2246
2247         V_attachFlags(vp) |= VOL_HDR_LOADED;
2248     }
2249 #endif /* AFS_DEMAND_ATTACH_FS */
2250
2251     if (!*ec) {
2252         struct IndexFileHeader iHead;
2253
2254 #if OPENAFS_VOL_STATS
2255         /*
2256          * We just read in the diskstuff part of the header.  If the detailed
2257          * volume stats area has not yet been initialized, we should bzero the
2258          * area and mark it as initialized.
2259          */
2260         if (!(V_stat_initialized(vp))) {
2261             memset((char *)(V_stat_area(vp)), 0, VOL_STATS_BYTES);
2262             V_stat_initialized(vp) = 1;
2263         }
2264 #endif /* OPENAFS_VOL_STATS */
2265
2266         (void)ReadHeader(ec, vp->vnodeIndex[vSmall].handle,
2267                          (char *)&iHead, sizeof(iHead),
2268                          SMALLINDEXMAGIC, SMALLINDEXVERSION);
2269
2270         if (*ec) {
2271             Log("VAttachVolume: Error reading smallVnode vol header %s; error=%u\n", path, *ec);
2272         }
2273     }
2274
2275     if (!*ec) {
2276         struct IndexFileHeader iHead;
2277
2278         (void)ReadHeader(ec, vp->vnodeIndex[vLarge].handle,
2279                          (char *)&iHead, sizeof(iHead),
2280                          LARGEINDEXMAGIC, LARGEINDEXVERSION);
2281
2282         if (*ec) {
2283             Log("VAttachVolume: Error reading largeVnode vol header %s; error=%u\n", path, *ec);
2284         }
2285     }
2286
2287 #ifdef AFS_NAMEI_ENV
2288     if (!*ec) {
2289         struct versionStamp stamp;
2290
2291         (void)ReadHeader(ec, V_linkHandle(vp), (char *)&stamp,
2292                          sizeof(stamp), LINKTABLEMAGIC, LINKTABLEVERSION);
2293
2294         if (*ec) {
2295             Log("VAttachVolume: Error reading namei vol header %s; error=%u\n", path, *ec);
2296         }
2297     }
2298 #endif /* AFS_NAMEI_ENV */
2299
2300 #if defined(AFS_DEMAND_ATTACH_FS)
2301     if (*ec && ((*ec != VOFFLINE) || (V_attachState(vp) != VOL_STATE_UNATTACHED))) {
2302         VOL_LOCK;
2303         if (programType == fileServer) {
2304             VRequestSalvage_r(vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2305             vp->nUsers = 0;
2306             *ec = VSALVAGING;
2307         } else {
2308             Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%u\n", path, *ec);
2309             FreeVolume(vp);
2310             *ec = VSALVAGE;
2311         }
2312         return NULL;
2313     } else if (*ec) {
2314         /* volume operation in progress */
2315         VOL_LOCK;
2316         return NULL;
2317     }
2318 #else /* AFS_DEMAND_ATTACH_FS */
2319     if (*ec) {
2320         Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%u\n", path, *ec);
2321         VOL_LOCK;
2322         FreeVolume(vp);
2323         return NULL;
2324     }
2325 #endif /* AFS_DEMAND_ATTACH_FS */
2326
2327     if (V_needsSalvaged(vp)) {
2328         if (vp->specialStatus)
2329             vp->specialStatus = 0;
2330         VOL_LOCK;
2331 #if defined(AFS_DEMAND_ATTACH_FS)
2332         if (programType == fileServer) {
2333             VRequestSalvage_r(vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
2334             vp->nUsers = 0;
2335             *ec = VSALVAGING;
2336         } else {
2337             Log("VAttachVolume: volume salvage flag is ON for %s; volume needs salvage\n", path);
2338             FreeVolume(vp);
2339             *ec = VSALVAGE;
2340         }
2341 #else /* AFS_DEMAND_ATTACH_FS */
2342         FreeVolume(vp);
2343         *ec = VSALVAGE;
2344 #endif /* AFS_DEMAND_ATTACH_FS */
2345         return NULL;
2346     }
2347
2348     VOL_LOCK;
2349     if (programType == fileServer) {
2350 #ifndef FAST_RESTART
2351         if (V_inUse(vp) && VolumeWriteable(vp)) {
2352             if (!V_needsSalvaged(vp)) {
2353                 V_needsSalvaged(vp) = 1;
2354                 VUpdateVolume_r(ec, vp, 0);
2355             }
2356 #if defined(AFS_DEMAND_ATTACH_FS)
2357             VRequestSalvage_r(vp, SALVSYNC_NEEDED, VOL_SALVAGE_INVALIDATE_HEADER);
2358             vp->nUsers = 0;
2359             *ec = VSALVAGING;
2360 #else /* AFS_DEMAND_ATTACH_FS */
2361             Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
2362             FreeVolume(vp);
2363             *ec = VSALVAGE;
2364 #endif /* AFS_DEMAND_ATTACH_FS */
2365             return NULL;
2366         }
2367 #endif /* FAST_RESTART */
2368
2369         if (V_destroyMe(vp) == DESTROY_ME) {
2370 #if defined(AFS_DEMAND_ATTACH_FS)
2371             /* schedule a salvage so the volume goes away on disk */
2372             VRequestSalvage_r(vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2373             VChangeState_r(vp, VOL_STATE_ERROR);
2374             vp->nUsers = 0;
2375 #endif /* AFS_DEMAND_ATTACH_FS */
2376             FreeVolume(vp);
2377             Log("VAttachVolume: volume %s is junk; it should be destroyed at next salvage\n", path);
2378             *ec = VNOVOL;
2379             return NULL;
2380         }
2381     }
2382
2383     vp->nextVnodeUnique = V_uniquifier(vp);
2384     vp->vnodeIndex[vSmall].bitmap = vp->vnodeIndex[vLarge].bitmap = NULL;
2385 #ifndef BITMAP_LATER
2386     if (programType == fileServer && VolumeWriteable(vp)) {
2387         int i;
2388         for (i = 0; i < nVNODECLASSES; i++) {
2389             VGetBitmap_r(ec, vp, i);
2390             if (*ec) {
2391 #ifdef AFS_DEMAND_ATTACH_FS
2392                 VRequestSalvage_r(vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2393                 vp->nUsers = 0;
2394                 *ec = VSALVAGING;
2395 #else /* AFS_DEMAND_ATTACH_FS */
2396                 FreeVolume(vp);
2397 #endif /* AFS_DEMAND_ATTACH_FS */
2398                 Log("VAttachVolume: error getting bitmap for volume (%s)\n",
2399                     path);
2400                 return NULL;
2401             }
2402         }
2403     }
2404 #endif /* BITMAP_LATER */
2405
2406     if (programType == fileServer) {
2407         if (vp->specialStatus)
2408             vp->specialStatus = 0;
2409         if (V_blessed(vp) && V_inService(vp) && !V_needsSalvaged(vp)) {
2410             V_inUse(vp) = 1;
2411             V_offlineMessage(vp)[0] = '\0';
2412         }
2413     }
2414
2415     AddVolumeToHashTable(vp, V_id(vp));
2416 #ifdef AFS_DEMAND_ATTACH_FS
2417     AddVolumeToVByPList_r(vp);
2418     VLRU_Add_r(vp);
2419     VChangeState_r(vp, VOL_STATE_ATTACHED);
2420 #endif
2421     return vp;
2422 }
2423
2424 /* Attach an existing volume.
2425    The volume also normally goes online at this time.
2426    An offline volume must be reattached to make it go online.
2427  */
2428
2429 Volume *
2430 VAttachVolume(Error * ec, VolumeId volumeId, int mode)
2431 {
2432     Volume *retVal;
2433     VOL_LOCK;
2434     retVal = VAttachVolume_r(ec, volumeId, mode);
2435     VOL_UNLOCK;
2436     return retVal;
2437 }
2438
2439 Volume *
2440 VAttachVolume_r(Error * ec, VolumeId volumeId, int mode)
2441 {
2442     char *part, *name;
2443     GetVolumePath(ec, volumeId, &part, &name);
2444     if (*ec) {
2445         register Volume *vp;
2446         Error error;
2447         vp = VGetVolume_r(&error, volumeId);
2448         if (vp) {
2449             assert(V_inUse(vp) == 0);
2450             VDetachVolume_r(ec, vp);
2451         }
2452         return NULL;
2453     }
2454     return VAttachVolumeByName_r(ec, part, name, mode);
2455 }
2456
2457 /* Increment a reference count to a volume, sans context swaps.  Requires
2458  * possibly reading the volume header in from the disk, since there's
2459  * an invariant in the volume package that nUsers>0 ==> vp->header is valid.
2460  *
2461  * N.B. This call can fail if we can't read in the header!!  In this case
2462  * we still guarantee we won't context swap, but the ref count won't be
2463  * incremented (otherwise we'd violate the invariant).
2464  */
2465 /* NOTE: with the demand attach fileserver extensions, the global lock
2466  * is dropped within VHold */
2467 #ifdef AFS_DEMAND_ATTACH_FS
2468 static int
2469 VHold_r(register Volume * vp)
2470 {
2471     Error error;
2472
2473     VCreateReservation_r(vp);
2474     VWaitExclusiveState_r(vp);
2475
2476     LoadVolumeHeader(&error, vp);
2477     if (error) {
2478         VCancelReservation_r(vp);
2479         return error;
2480     }
2481     vp->nUsers++;
2482     VCancelReservation_r(vp);
2483     return 0;
2484 }
2485 #else /* AFS_DEMAND_ATTACH_FS */
2486 static int
2487 VHold_r(register Volume * vp)
2488 {
2489     Error error;
2490
2491     LoadVolumeHeader(&error, vp);
2492     if (error)
2493         return error;
2494     vp->nUsers++;
2495     return 0;
2496 }
2497 #endif /* AFS_DEMAND_ATTACH_FS */
2498
2499 static int
2500 VHold(register Volume * vp)
2501 {
2502     int retVal;
2503     VOL_LOCK;
2504     retVal = VHold_r(vp);
2505     VOL_UNLOCK;
2506     return retVal;
2507 }
2508
2509
2510 /***************************************************/
2511 /* get and put volume routines                     */
2512 /***************************************************/
2513
2514 void
2515 VPutVolume_r(register Volume * vp)
2516 {
2517     assert(--vp->nUsers >= 0);
2518     if (vp->nUsers == 0) {
2519         VCheckOffline(vp);
2520         ReleaseVolumeHeader(vp->header);
2521 #ifdef AFS_DEMAND_ATTACH_FS
2522         if (!VCheckDetach(vp)) {
2523             VCheckSalvage(vp);
2524             VCheckFree(vp);
2525         }
2526 #else /* AFS_DEMAND_ATTACH_FS */
2527         VCheckDetach(vp);
2528 #endif /* AFS_DEMAND_ATTACH_FS */
2529     }
2530 }
2531
2532 void
2533 VPutVolume(register Volume * vp)
2534 {
2535     VOL_LOCK;
2536     VPutVolume_r(vp);
2537     VOL_UNLOCK;
2538 }
2539
2540
2541 /* Get a pointer to an attached volume.  The pointer is returned regardless
2542    of whether or not the volume is in service or on/off line.  An error
2543    code, however, is returned with an indication of the volume's status */
2544 Volume *
2545 VGetVolume(Error * ec, Error * client_ec, VolId volumeId)
2546 {
2547     Volume *retVal;
2548     VOL_LOCK;
2549     retVal = GetVolume(ec, client_ec, volumeId, NULL, 0);
2550     VOL_UNLOCK;
2551     return retVal;
2552 }
2553
2554 Volume *
2555 VGetVolume_r(Error * ec, VolId volumeId)
2556 {
2557     return GetVolume(ec, NULL, volumeId, NULL, 0);
2558 }
2559
2560 /* try to get a volume we've previously looked up */
2561 /* for demand attach fs, caller MUST NOT hold a ref count on vp */
2562 Volume * 
2563 VGetVolumeByVp_r(Error * ec, Volume * vp)
2564 {
2565     return GetVolume(ec, NULL, vp->hashid, vp, 0);
2566 }
2567
2568 /* private interface for getting a volume handle
2569  * volumeId must be provided.
2570  * hint is an optional parameter to speed up hash lookups
2571  * flags is not used at this time
2572  */
2573 /* for demand attach fs, caller MUST NOT hold a ref count on hint */
2574 static Volume *
2575 GetVolume(Error * ec, Error * client_ec, VolId volumeId, Volume * hint, int flags)
2576 {
2577     Volume *vp = hint;
2578     /* pull this profiling/debugging code out of regular builds */
2579 #ifdef notdef
2580 #define VGET_CTR_INC(x) x++
2581     unsigned short V0 = 0, V1 = 0, V2 = 0, V3 = 0, V5 = 0, V6 =
2582         0, V7 = 0, V8 = 0, V9 = 0;
2583     unsigned short V10 = 0, V11 = 0, V12 = 0, V13 = 0, V14 = 0, V15 = 0;
2584 #else
2585 #define VGET_CTR_INC(x)
2586 #endif
2587
2588 #ifdef AFS_DEMAND_ATTACH_FS
2589     Volume *avp, * rvp = hint;
2590
2591     if (rvp) {
2592         VCreateReservation_r(rvp);
2593     }
2594 #endif /* AFS_DEMAND_ATTACH_FS */
2595
2596     for (;;) {
2597         *ec = 0;
2598         if (client_ec)
2599             *client_ec = 0;
2600         VGET_CTR_INC(V0);
2601
2602         vp = VLookupVolume_r(ec, volumeId, vp);
2603         if (*ec) {
2604             vp = NULL;
2605             break;
2606         }
2607
2608 #ifdef AFS_DEMAND_ATTACH_FS
2609         if (rvp && (rvp != vp)) {
2610             /* break reservation on old vp */
2611             VCancelReservation_r(rvp);
2612             rvp = NULL;
2613         }
2614 #endif /* AFS_DEMAND_ATTACH_FS */
2615
2616         if (!vp) {
2617             VGET_CTR_INC(V1);
2618             if (VInit < 2) {
2619                 VGET_CTR_INC(V2);
2620                 /* Until we have reached an initialization level of 2
2621                  * we don't know whether this volume exists or not.
2622                  * We can't sleep and retry later because before a volume
2623                  * is attached, the caller tries to get it first.  Just
2624                  * return VOFFLINE and the caller can choose whether to
2625                  * retry the command or not. */
2626                 *ec = VOFFLINE;
2627                 break;
2628             }
2629
2630             *ec = VNOVOL;
2631             break;
2632         }
2633
2634         VGET_CTR_INC(V3);
2635         IncUInt64(&VStats.hdr_gets);
2636         
2637 #ifdef AFS_DEMAND_ATTACH_FS
2638         /* block if someone else is performing an exclusive op on this volume */
2639         if (rvp != vp) {
2640             rvp = vp;
2641             VCreateReservation_r(rvp);
2642         }
2643         VWaitExclusiveState_r(vp);
2644
2645         /* short circuit with VNOVOL in the following circumstances:
2646          *
2647          *   VOL_STATE_ERROR
2648          *   VOL_STATE_SHUTTING_DOWN
2649          */
2650         if ((V_attachState(vp) == VOL_STATE_ERROR) ||
2651             (V_attachState(vp) == VOL_STATE_SHUTTING_DOWN)) {
2652             *ec = VNOVOL;
2653             vp = NULL;
2654             break;
2655         }
2656
2657         /* allowable states:
2658          *   UNATTACHED
2659          *   PREATTACHED
2660          *   ATTACHED
2661          *   GOING_OFFLINE
2662          *   SALVAGING
2663          */
2664
2665         if (vp->salvage.requested) {
2666             VUpdateSalvagePriority_r(vp);
2667         }
2668
2669         if (V_attachState(vp) == VOL_STATE_PREATTACHED) {
2670             avp = VAttachVolumeByVp_r(ec, vp, 0);
2671             if (avp) {
2672                 if (vp != avp) {
2673                     /* VAttachVolumeByVp_r can return a pointer
2674                      * != the vp passed to it under certain
2675                      * conditions; make sure we don't leak
2676                      * reservations if that happens */
2677                     vp = avp;
2678                     VCancelReservation_r(rvp);
2679                     rvp = avp;
2680                     VCreateReservation_r(rvp);
2681                 }
2682                 VPutVolume_r(avp);
2683             }
2684             if (*ec) {
2685                 int endloop = 0;
2686                 switch (*ec) {
2687                 case VSALVAGING:
2688                     break;
2689                 case VOFFLINE:
2690                     if (!vp->pending_vol_op) {
2691                         endloop = 1;
2692                     }
2693                     break;
2694                 default:
2695                     *ec = VNOVOL;
2696                     endloop = 1;
2697                 }
2698                 if (endloop) {
2699                     vp = NULL;
2700                     break;
2701                 }
2702             }
2703         }
2704
2705         if ((V_attachState(vp) == VOL_STATE_SALVAGING) ||
2706             (*ec == VSALVAGING)) {
2707             if (client_ec) {
2708                 /* see CheckVnode() in afsfileprocs.c for an explanation
2709                  * of this error code logic */
2710                 afs_uint32 now = FT_ApproxTime();
2711                 if ((vp->stats.last_salvage + (10 * 60)) >= now) {
2712                     *client_ec = VBUSY;
2713                 } else {
2714                     *client_ec = VRESTARTING;
2715                 }
2716             }
2717             *ec = VSALVAGING;
2718             vp = NULL;
2719             break;
2720         }
2721
2722         if (vp->pending_vol_op && !VVolOpLeaveOnline_r(vp, vp->pending_vol_op)) {
2723             if (client_ec) {
2724                 /* see CheckVnode() in afsfileprocs.c for an explanation
2725                  * of this error code logic */
2726                 afs_uint32 now = FT_ApproxTime();
2727                 if ((vp->stats.last_vol_op + (10 * 60)) >= now) {
2728                     *client_ec = VBUSY;
2729                 } else {
2730                     *client_ec = VRESTARTING;
2731                 }
2732             }
2733             *ec = VOFFLINE;
2734             vp = NULL;
2735             break;
2736         }
2737
2738         if (V_attachState(vp) == VOL_STATE_UNATTACHED) {
2739             *ec = VOFFLINE;
2740             vp = NULL;
2741             break;
2742         }
2743 #endif /* AFS_DEMAND_ATTACH_FS */
2744         
2745         LoadVolumeHeader(ec, vp);
2746         if (*ec) {
2747             VGET_CTR_INC(V6);
2748             /* Only log the error if it was a totally unexpected error.  Simply
2749              * a missing inode is likely to be caused by the volume being deleted */
2750             if (errno != ENXIO || LogLevel)
2751                 Log("Volume %u: couldn't reread volume header\n",
2752                     vp->hashid);
2753 #ifdef AFS_DEMAND_ATTACH_FS
2754             if (programType == fileServer) {
2755                 VRequestSalvage_r(vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2756                 *ec = VSALVAGING;
2757             } else {
2758                 FreeVolume(vp);
2759                 vp = NULL;
2760             }
2761 #else /* AFS_DEMAND_ATTACH_FS */
2762             FreeVolume(vp);
2763             vp = NULL;
2764 #endif /* AFS_DEMAND_ATTACH_FS */
2765             break;
2766         }
2767
2768         VGET_CTR_INC(V7);
2769         if (vp->shuttingDown) {
2770             VGET_CTR_INC(V8);
2771             *ec = VNOVOL;
2772             vp = NULL;
2773             break;
2774         }
2775
2776         if (programType == fileServer) {
2777             VGET_CTR_INC(V9);
2778             if (vp->goingOffline) {
2779                 VGET_CTR_INC(V10);
2780 #ifdef AFS_DEMAND_ATTACH_FS
2781                 /* wait for the volume to go offline */
2782                 if (V_attachState(vp) == VOL_STATE_GOING_OFFLINE) {
2783                     VWaitStateChange_r(vp);
2784                 }
2785 #elif defined(AFS_PTHREAD_ENV)
2786                 assert(pthread_cond_wait(&vol_put_volume_cond, &vol_glock_mutex) == 0);
2787 #else /* AFS_PTHREAD_ENV */
2788                 LWP_WaitProcess(VPutVolume);
2789 #endif /* AFS_PTHREAD_ENV */
2790                 continue;
2791             }
2792             if (vp->specialStatus) {
2793                 VGET_CTR_INC(V11);
2794                 *ec = vp->specialStatus;
2795             } else if (V_inService(vp) == 0 || V_blessed(vp) == 0) {
2796                 VGET_CTR_INC(V12);
2797                 *ec = VNOVOL;
2798             } else if (V_inUse(vp) == 0) {
2799                 VGET_CTR_INC(V13);
2800                 *ec = VOFFLINE;
2801             } else {
2802                 VGET_CTR_INC(V14);
2803             }
2804         }
2805         break;
2806     }
2807     VGET_CTR_INC(V15);
2808
2809 #ifdef AFS_DEMAND_ATTACH_FS
2810     /* if no error, bump nUsers */
2811     if (vp) {
2812         vp->nUsers++;
2813         VLRU_UpdateAccess_r(vp);
2814     }
2815     if (rvp) {
2816         VCancelReservation_r(rvp);
2817         rvp = NULL;
2818     }
2819     if (client_ec && !*client_ec) {
2820         *client_ec = *ec;
2821     }
2822 #else /* AFS_DEMAND_ATTACH_FS */
2823     /* if no error, bump nUsers */
2824     if (vp) {
2825         vp->nUsers++;
2826     }
2827     if (client_ec) {
2828         *client_ec = *ec;
2829     }
2830 #endif /* AFS_DEMAND_ATTACH_FS */
2831
2832     assert(vp || *ec);
2833     return vp;
2834 }
2835
2836
2837 /***************************************************/
2838 /* Volume offline/detach routines                  */
2839 /***************************************************/
2840
2841 /* caller MUST hold a heavyweight ref on vp */
2842 #ifdef AFS_DEMAND_ATTACH_FS
2843 void
2844 VTakeOffline_r(register Volume * vp)
2845 {
2846     assert(vp->nUsers > 0);
2847     assert(programType == fileServer);
2848
2849     VCreateReservation_r(vp);
2850     VWaitExclusiveState_r(vp);
2851
2852     vp->goingOffline = 1;
2853     V_needsSalvaged(vp) = 1;
2854
2855     VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
2856     VCancelReservation_r(vp);
2857 }
2858 #else /* AFS_DEMAND_ATTACH_FS */
2859 void
2860 VTakeOffline_r(register Volume * vp)
2861 {
2862     assert(vp->nUsers > 0);
2863     assert(programType == fileServer);
2864
2865     vp->goingOffline = 1;
2866     V_needsSalvaged(vp) = 1;
2867 }
2868 #endif /* AFS_DEMAND_ATTACH_FS */
2869
2870 void
2871 VTakeOffline(register Volume * vp)
2872 {
2873     VOL_LOCK;
2874     VTakeOffline_r(vp);
2875     VOL_UNLOCK;
2876 }
2877
2878 /* Force the volume offline, set the salvage flag.  No further references to
2879  * the volume through the volume package will be honored. */
2880 /* for demand attach, caller MUST hold ref count on vp */
2881 void
2882 VForceOffline_r(Volume * vp, int flags)
2883 {
2884     Error error;
2885     if (!V_inUse(vp))
2886         return;
2887     strcpy(V_offlineMessage(vp),
2888            "Forced offline due to internal error: volume needs to be salvaged");
2889     Log("Volume %u forced offline:  it needs salvaging!\n", V_id(vp));
2890     V_inUse(vp) = 0;
2891     vp->goingOffline = 0;
2892     V_needsSalvaged(vp) = 1;
2893     if (!(flags & VOL_FORCEOFF_NOUPDATE)) {
2894         VUpdateVolume_r(&error, vp, VOL_UPDATE_WAIT | VOL_UPDATE_NOFORCEOFF);
2895     }
2896 #ifdef AFS_DEMAND_ATTACH_FS
2897 #ifdef SALVSYNC_BUILD_CLIENT
2898     if (programType == fileServer) {
2899         VRequestSalvage_r(vp, SALVSYNC_ERROR, VOL_SALVAGE_INVALIDATE_HEADER);
2900     }
2901 #endif
2902     VChangeState_r(vp, VOL_STATE_ERROR);
2903 #endif /* AFS_DEMAND_ATTACH_FS */
2904 #ifdef AFS_PTHREAD_ENV
2905     assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
2906 #else /* AFS_PTHREAD_ENV */
2907     LWP_NoYieldSignal(VPutVolume);
2908 #endif /* AFS_PTHREAD_ENV */
2909
2910     VReleaseVolumeHandles_r(vp);
2911 }
2912
2913 void
2914 VForceOffline(Volume * vp)
2915 {
2916     VOL_LOCK;
2917     VForceOffline_r(vp, 0);
2918     VOL_UNLOCK;
2919 }
2920
2921 /* The opposite of VAttachVolume.  The volume header is written to disk, with
2922    the inUse bit turned off.  A copy of the header is maintained in memory,
2923    however (which is why this is VOffline, not VDetach).
2924  */
2925 void
2926 VOffline_r(Volume * vp, char *message)
2927 {
2928     Error error;
2929     VolumeId vid = V_id(vp);
2930
2931     assert(programType != volumeUtility);
2932     if (!V_inUse(vp)) {
2933         VPutVolume_r(vp);
2934         return;
2935     }
2936     if (V_offlineMessage(vp)[0] == '\0')
2937         strncpy(V_offlineMessage(vp), message, sizeof(V_offlineMessage(vp)));
2938     V_offlineMessage(vp)[sizeof(V_offlineMessage(vp)) - 1] = '\0';
2939
2940     vp->goingOffline = 1;
2941 #ifdef AFS_DEMAND_ATTACH_FS
2942     VChangeState_r(vp, VOL_STATE_GOING_OFFLINE);
2943     VCreateReservation_r(vp);
2944     VPutVolume_r(vp);
2945
2946     /* wait for the volume to go offline */
2947     if (V_attachState(vp) == VOL_STATE_GOING_OFFLINE) {
2948         VWaitStateChange_r(vp);
2949     }
2950     VCancelReservation_r(vp);
2951 #else /* AFS_DEMAND_ATTACH_FS */
2952     VPutVolume_r(vp);
2953     vp = VGetVolume_r(&error, vid);     /* Wait for it to go offline */
2954     if (vp)                     /* In case it was reattached... */
2955         VPutVolume_r(vp);
2956 #endif /* AFS_DEMAND_ATTACH_FS */
2957 }
2958
2959 void
2960 VOffline(Volume * vp, char *message)
2961 {
2962     VOL_LOCK;
2963     VOffline_r(vp, message);
2964     VOL_UNLOCK;
2965 }
2966
2967 /* This gets used for the most part by utility routines that don't want
2968  * to keep all the volume headers around.  Generally, the file server won't
2969  * call this routine, because then the offline message in the volume header
2970  * (or other information) won't be available to clients. For NAMEI, also
2971  * close the file handles.  However, the fileserver does call this during
2972  * an attach following a volume operation.
2973  */
2974 void
2975 VDetachVolume_r(Error * ec, Volume * vp)
2976 {
2977     VolumeId volume;
2978     struct DiskPartition *tpartp;
2979     int notifyServer, useDone;
2980
2981     *ec = 0;                    /* always "succeeds" */
2982     if (programType == volumeUtility) {
2983         notifyServer = vp->needsPutBack;
2984         useDone = (V_destroyMe(vp) == DESTROY_ME);
2985     }
2986     tpartp = vp->partition;
2987     volume = V_id(vp);
2988     DeleteVolumeFromHashTable(vp);
2989     vp->shuttingDown = 1;
2990 #ifdef AFS_DEMAND_ATTACH_FS
2991     DeleteVolumeFromVByPList_r(vp);
2992     VLRU_Delete_r(vp);
2993     VChangeState_r(vp, VOL_STATE_SHUTTING_DOWN);
2994 #endif /* AFS_DEMAND_ATTACH_FS */
2995     VPutVolume_r(vp);
2996     /* Will be detached sometime in the future--this is OK since volume is offline */
2997
2998     /* XXX the following code should really be moved to VCheckDetach() since the volume
2999      * is not technically detached until the refcounts reach zero
3000      */
3001 #ifdef FSSYNC_BUILD_CLIENT
3002     if (programType == volumeUtility && notifyServer) {
3003         /* 
3004          * Note:  The server is not notified in the case of a bogus volume 
3005          * explicitly to make it possible to create a volume, do a partial 
3006          * restore, then abort the operation without ever putting the volume 
3007          * online.  This is essential in the case of a volume move operation 
3008          * between two partitions on the same server.  In that case, there 
3009          * would be two instances of the same volume, one of them bogus, 
3010          * which the file server would attempt to put on line 
3011          */
3012         if (useDone) {
3013             /* don't put online */
3014             FSYNC_VolOp(volume, tpartp->name, FSYNC_VOL_DONE, 0, NULL);
3015         } else {
3016             /* fs can use it again */
3017             FSYNC_VolOp(volume, tpartp->name, FSYNC_VOL_ON, 0, NULL);
3018
3019             /* XXX this code path is only hit by volume utilities, thus
3020              * V_BreakVolumeCallbacks will always be NULL.  if we really
3021              * want to break callbacks in this path we need to use FSYNC_VolOp() */
3022 #ifdef notdef
3023             /* Dettaching it so break all callbacks on it */
3024             if (V_BreakVolumeCallbacks) {
3025                 Log("volume %u detached; breaking all call backs\n", volume);
3026                 (*V_BreakVolumeCallbacks) (volume);
3027             }
3028 #endif
3029         }
3030     }
3031 #endif /* FSSYNC_BUILD_CLIENT */
3032 }
3033
3034 void
3035 VDetachVolume(Error * ec, Volume * vp)
3036 {
3037     VOL_LOCK;
3038     VDetachVolume_r(ec, vp);
3039     VOL_UNLOCK;
3040 }
3041
3042
3043 /***************************************************/
3044 /* Volume fd/inode handle closing routines         */
3045 /***************************************************/
3046
3047 /* For VDetachVolume, we close all cached file descriptors, but keep
3048  * the Inode handles in case we need to read from a busy volume.
3049  */
3050 /* for demand attach, caller MUST hold ref count on vp */
3051 static void
3052 VCloseVolumeHandles_r(Volume * vp)
3053 {
3054 #ifdef AFS_DEMAND_ATTACH_FS
3055     VolState state_save;
3056
3057     state_save = VChangeState_r(vp, VOL_STATE_OFFLINING);
3058 #endif
3059
3060     /* demand attach fs
3061      *
3062      * XXX need to investigate whether we can perform
3063      * DFlushVolume outside of vol_glock_mutex... 
3064      *
3065      * VCloseVnodeFiles_r drops the glock internally */
3066     DFlushVolume(V_id(vp));
3067     VCloseVnodeFiles_r(vp);
3068
3069 #ifdef AFS_DEMAND_ATTACH_FS
3070     VOL_UNLOCK;
3071 #endif
3072
3073     /* Too time consuming and unnecessary for the volserver */
3074     if (programType != volumeUtility) {
3075         IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
3076         IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
3077         IH_CONDSYNC(vp->diskDataHandle);
3078 #ifdef AFS_NT40_ENV
3079         IH_CONDSYNC(vp->linkHandle);
3080 #endif /* AFS_NT40_ENV */
3081     }
3082
3083     IH_REALLYCLOSE(vp->vnodeIndex[vLarge].handle);
3084     IH_REALLYCLOSE(vp->vnodeIndex[vSmall].handle);
3085     IH_REALLYCLOSE(vp->diskDataHandle);
3086     IH_REALLYCLOSE(vp->linkHandle);
3087
3088 #ifdef AFS_DEMAND_ATTACH_FS
3089     VOL_LOCK;
3090     VChangeState_r(vp, state_save);
3091 #endif
3092 }
3093
3094 /* For both VForceOffline and VOffline, we close all relevant handles.
3095  * For VOffline, if we re-attach the volume, the files may possible be
3096  * different than before. 
3097  */
3098 /* for demand attach, caller MUST hold a ref count on vp */
3099 static void
3100 VReleaseVolumeHandles_r(Volume * vp)
3101 {
3102 #ifdef AFS_DEMAND_ATTACH_FS
3103     VolState state_save;
3104
3105     state_save = VChangeState_r(vp, VOL_STATE_DETACHING);
3106 #endif
3107
3108     /* XXX need to investigate whether we can perform
3109      * DFlushVolume outside of vol_glock_mutex... */
3110     DFlushVolume(V_id(vp));
3111
3112     VReleaseVnodeFiles_r(vp); /* releases the glock internally */
3113
3114 #ifdef AFS_DEMAND_ATTACH_FS
3115     VOL_UNLOCK;
3116 #endif
3117
3118     /* Too time consuming and unnecessary for the volserver */
3119     if (programType != volumeUtility) {
3120         IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
3121         IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
3122         IH_CONDSYNC(vp->diskDataHandle);
3123 #ifdef AFS_NT40_ENV
3124         IH_CONDSYNC(vp->linkHandle);
3125 #endif /* AFS_NT40_ENV */
3126     }
3127
3128     IH_RELEASE(vp->vnodeIndex[vLarge].handle);
3129     IH_RELEASE(vp->vnodeIndex[vSmall].handle);
3130     IH_RELEASE(vp->diskDataHandle);
3131     IH_RELEASE(vp->linkHandle);
3132
3133 #ifdef AFS_DEMAND_ATTACH_FS
3134     VOL_LOCK;
3135     VChangeState_r(vp, state_save);
3136 #endif
3137 }
3138
3139
3140 /***************************************************/
3141 /* Volume write and fsync routines                 */
3142 /***************************************************/
3143
3144 void
3145 VUpdateVolume_r(Error * ec, Volume * vp, int flags)
3146 {
3147 #ifdef AFS_DEMAND_ATTACH_FS
3148     VolState state_save;
3149
3150     if (flags & VOL_UPDATE_WAIT) {
3151         VCreateReservation_r(vp);
3152         VWaitExclusiveState_r(vp);
3153     }
3154 #endif
3155
3156     *ec = 0;
3157     if (programType == fileServer)
3158         V_uniquifier(vp) =
3159             (V_inUse(vp) ? V_nextVnodeUnique(vp) +
3160              200 : V_nextVnodeUnique(vp));
3161
3162 #ifdef AFS_DEMAND_ATTACH_FS
3163     state_save = VChangeState_r(vp, VOL_STATE_UPDATING);
3164     VOL_UNLOCK;
3165 #endif
3166
3167     WriteVolumeHeader_r(ec, vp);
3168
3169 #ifdef AFS_DEMAND_ATTACH_FS
3170     VOL_LOCK;
3171     VChangeState_r(vp, state_save);
3172     if (flags & VOL_UPDATE_WAIT) {
3173         VCancelReservation_r(vp);
3174     }
3175 #endif
3176
3177     if (*ec) {
3178         Log("VUpdateVolume: error updating volume header, volume %u (%s)\n",
3179             V_id(vp), V_name(vp));
3180         /* try to update on-disk header, 
3181          * while preventing infinite recursion */
3182         if (!(flags & VOL_UPDATE_NOFORCEOFF)) {
3183             VForceOffline_r(vp, VOL_FORCEOFF_NOUPDATE);
3184         }
3185     }
3186 }
3187
3188 void
3189 VUpdateVolume(Error * ec, Volume * vp)
3190 {
3191     VOL_LOCK;
3192     VUpdateVolume_r(ec, vp, VOL_UPDATE_WAIT);
3193     VOL_UNLOCK;
3194 }
3195
3196 void
3197 VSyncVolume_r(Error * ec, Volume * vp, int flags)
3198 {
3199     FdHandle_t *fdP;
3200     int code;
3201 #ifdef AFS_DEMAND_ATTACH_FS
3202     VolState state_save;
3203 #endif
3204
3205     if (flags & VOL_SYNC_WAIT) {
3206         VUpdateVolume_r(ec, vp, VOL_UPDATE_WAIT);
3207     } else {
3208         VUpdateVolume_r(ec, vp, 0);
3209     }
3210     if (!*ec) {
3211 #ifdef AFS_DEMAND_ATTACH_FS
3212         state_save = VChangeState_r(vp, VOL_STATE_UPDATING);
3213         VOL_UNLOCK;
3214 #endif
3215         fdP = IH_OPEN(V_diskDataHandle(vp));
3216         assert(fdP != NULL);
3217         code = FDH_SYNC(fdP);
3218         assert(code == 0);
3219         FDH_CLOSE(fdP);
3220 #ifdef AFS_DEMAND_ATTACH_FS
3221         VOL_LOCK;
3222         VChangeState_r(vp, state_save);
3223 #endif
3224     }
3225 }
3226
3227 void
3228 VSyncVolume(Error * ec, Volume * vp)
3229 {
3230     VOL_LOCK;
3231     VSyncVolume_r(ec, vp, VOL_SYNC_WAIT);
3232     VOL_UNLOCK;
3233 }
3234
3235
3236 /***************************************************/
3237 /* Volume dealloaction routines                    */
3238 /***************************************************/
3239
3240 #ifdef AFS_DEMAND_ATTACH_FS
3241 static void
3242 FreeVolume(Volume * vp)
3243 {
3244     /* free the heap space, iff it's safe.
3245      * otherwise, pull it out of the hash table, so it
3246      * will get deallocated when all refs to it go away */
3247     if (!VCheckFree(vp)) {
3248         DeleteVolumeFromHashTable(vp);
3249         DeleteVolumeFromVByPList_r(vp);
3250
3251         /* make sure we invalidate the header cache entry */
3252         FreeVolumeHeader(vp);
3253     }
3254 }
3255 #endif /* AFS_DEMAND_ATTACH_FS */
3256
3257 static void
3258 ReallyFreeVolume(Volume * vp)
3259 {
3260     int i;
3261     if (!vp)
3262         return;
3263 #ifdef AFS_DEMAND_ATTACH_FS
3264     /* debug */
3265     VChangeState_r(vp, VOL_STATE_FREED);
3266     if (vp->pending_vol_op)
3267         free(vp->pending_vol_op);
3268 #endif /* AFS_DEMAND_ATTACH_FS */
3269     for (i = 0; i < nVNODECLASSES; i++)
3270         if (vp->vnodeIndex[i].bitmap)
3271             free(vp->vnodeIndex[i].bitmap);
3272     FreeVolumeHeader(vp);
3273 #ifndef AFS_DEMAND_ATTACH_FS
3274     DeleteVolumeFromHashTable(vp);
3275 #endif /* AFS_DEMAND_ATTACH_FS */
3276     free(vp);
3277 }
3278
3279 /* check to see if we should shutdown this volume
3280  * returns 1 if volume was freed, 0 otherwise */
3281 #ifdef AFS_DEMAND_ATTACH_FS
3282 static int
3283 VCheckDetach(register Volume * vp)
3284 {
3285     int ret = 0;
3286
3287     if (vp->nUsers || vp->nWaiters)
3288         return ret;
3289
3290     if (vp->shuttingDown) {
3291         ret = 1;
3292         VReleaseVolumeHandles_r(vp);
3293         VCheckSalvage(vp);
3294         ReallyFreeVolume(vp);
3295         if (programType == fileServer) {
3296             assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3297         }
3298     }
3299     return ret;
3300 }
3301 #else /* AFS_DEMAND_ATTACH_FS */
3302 static int
3303 VCheckDetach(register Volume * vp)
3304 {
3305     int ret = 0;
3306
3307     if (vp->nUsers)
3308         return ret;
3309
3310     if (vp->shuttingDown) {
3311         ret = 1;
3312         VReleaseVolumeHandles_r(vp);
3313         ReallyFreeVolume(vp);
3314         if (programType == fileServer) {
3315 #if defined(AFS_PTHREAD_ENV)
3316             assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3317 #else /* AFS_PTHREAD_ENV */
3318             LWP_NoYieldSignal(VPutVolume);
3319 #endif /* AFS_PTHREAD_ENV */
3320         }
3321     }
3322     return ret;
3323 }
3324 #endif /* AFS_DEMAND_ATTACH_FS */
3325
3326 /* check to see if we should offline this volume
3327  * return 1 if volume went offline, 0 otherwise */
3328 #ifdef AFS_DEMAND_ATTACH_FS
3329 static int
3330 VCheckOffline(register Volume * vp)
3331 {
3332     Volume * rvp = NULL;
3333     int ret = 0;
3334
3335     if (vp->goingOffline && !vp->nUsers) {
3336         Error error;
3337         assert(programType == fileServer);
3338         assert((V_attachState(vp) != VOL_STATE_ATTACHED) &&
3339                (V_attachState(vp) != VOL_STATE_FREED) &&
3340                (V_attachState(vp) != VOL_STATE_PREATTACHED) &&
3341                (V_attachState(vp) != VOL_STATE_UNATTACHED));
3342
3343         /* valid states:
3344          *
3345          * VOL_STATE_GOING_OFFLINE
3346          * VOL_STATE_SHUTTING_DOWN
3347          * IsErrorState(V_attachState(vp))
3348          * IsExclusiveState(V_attachState(vp))
3349          */
3350
3351         VCreateReservation_r(vp);
3352         VChangeState_r(vp, VOL_STATE_OFFLINING);
3353
3354         ret = 1;
3355         /* must clear the goingOffline flag before we drop the glock */
3356         vp->goingOffline = 0;
3357         V_inUse(vp) = 0;
3358
3359         VLRU_Delete_r(vp);
3360
3361         /* perform async operations */
3362         VUpdateVolume_r(&error, vp, 0);
3363         VCloseVolumeHandles_r(vp);
3364
3365         /* invalidate the volume header cache entry */
3366         FreeVolumeHeader(vp);
3367
3368         if (LogLevel) {
3369             Log("VOffline: Volume %u (%s) is now offline", V_id(vp),
3370                 V_name(vp));
3371             if (V_offlineMessage(vp)[0])
3372                 Log(" (%s)", V_offlineMessage(vp));
3373             Log("\n");
3374         }
3375
3376         /* if nothing changed state to error or salvaging,
3377          * drop state to unattached */
3378         if (!IsErrorState(V_attachState(vp))) {
3379             VChangeState_r(vp, VOL_STATE_UNATTACHED);
3380         }
3381         VCancelReservation_r(vp);
3382     }
3383     return ret;
3384 }
3385 #else /* AFS_DEMAND_ATTACH_FS */
3386 static int
3387 VCheckOffline(register Volume * vp)
3388 {
3389     Volume * rvp = NULL;
3390     int ret = 0;
3391
3392     if (vp->goingOffline && !vp->nUsers) {
3393         Error error;
3394         assert(programType == fileServer);
3395
3396         ret = 1;
3397         vp->goingOffline = 0;
3398         V_inUse(vp) = 0;
3399         VUpdateVolume_r(&error, vp, 0);
3400         VCloseVolumeHandles_r(vp);
3401         FreeVolumeHeader(vp);
3402         if (LogLevel) {
3403             Log("VOffline: Volume %u (%s) is now offline", V_id(vp),
3404                 V_name(vp));
3405             if (V_offlineMessage(vp)[0])
3406                 Log(" (%s)", V_offlineMessage(vp));
3407             Log("\n");
3408         }
3409 #ifdef AFS_PTHREAD_ENV
3410         assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
3411 #else /* AFS_PTHREAD_ENV */
3412         LWP_NoYieldSignal(VPutVolume);
3413 #endif /* AFS_PTHREAD_ENV */
3414     }
3415     return ret;
3416 }
3417 #endif /* AFS_DEMAND_ATTACH_FS */
3418
3419 /***************************************************/
3420 /* demand attach fs ref counting routines          */
3421 /***************************************************/
3422
3423 #ifdef AFS_DEMAND_ATTACH_FS
3424 /* the following two functions handle reference counting for
3425  * asynchronous operations on volume structs.
3426  *
3427  * their purpose is to prevent a VDetachVolume or VShutdown
3428  * from free()ing the Volume struct during an async i/o op */
3429
3430 /* register with the async volume op ref counter */
3431 static void
3432 VCreateReservation_r(Volume * vp)
3433 {
3434     vp->nWaiters++;
3435 }
3436
3437 /* unregister with the async volume op ref counter */
3438 static void
3439 VCancelReservation_r(Volume * vp)
3440 {
3441     assert(--vp->nWaiters >= 0);
3442     if (vp->nWaiters == 0) {
3443         VCheckOffline(vp);
3444         if (!VCheckDetach(vp)) {
3445             VCheckSalvage(vp);
3446             VCheckFree(vp);
3447         }
3448     }
3449 }
3450
3451 /* check to see if we should free this volume now
3452  * return 1 if volume was freed, 0 otherwise */
3453 static int
3454 VCheckFree(Volume * vp)
3455 {
3456     int ret = 0;
3457     if ((vp->nUsers == 0) &&
3458         (vp->nWaiters == 0) &&
3459         !(V_attachFlags(vp) & (VOL_IN_HASH | 
3460                                VOL_ON_VBYP_LIST | 
3461                                VOL_IS_BUSY |
3462                                VOL_ON_VLRU))) {
3463         ReallyFreeVolume(vp);
3464         ret = 1;
3465     }
3466     return ret;
3467 }
3468 #endif /* AFS_DEMAND_ATTACH_FS */
3469
3470
3471 /***************************************************/
3472 /* online volume operations routines               */
3473 /***************************************************/
3474
3475 #ifdef AFS_DEMAND_ATTACH_FS
3476 int
3477 VRegisterVolOp_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
3478 {
3479     FSSYNC_VolOp_info * info;
3480
3481     /* attach a vol op info node to the volume struct */
3482     info = (FSSYNC_VolOp_info *) malloc(sizeof(FSSYNC_VolOp_info));
3483     assert(info != NULL);
3484     memcpy(info, vopinfo, sizeof(FSSYNC_VolOp_info));
3485     vp->pending_vol_op = info;
3486
3487     /* update stats */
3488     vp->stats.last_vol_op = FT_ApproxTime();
3489     vp->stats.vol_ops++;
3490     IncUInt64(&VStats.vol_ops);
3491
3492     return 0;
3493 }
3494
3495 int
3496 VDeregisterVolOp_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
3497 {
3498     if (vp->pending_vol_op) {
3499         free(vp->pending_vol_op);
3500         vp->pending_vol_op = NULL;
3501     }
3502     return 0;
3503 }
3504 #endif /* AFS_DEMAND_ATTACH_FS */
3505
3506 int
3507 VVolOpLeaveOnline_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
3508 {
3509     return (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
3510             (vopinfo->com.reason == V_READONLY ||
3511              (!VolumeWriteable(vp) &&
3512               (vopinfo->com.reason == V_CLONE ||
3513                vopinfo->com.reason == V_DUMP))));
3514 }
3515
3516 int
3517 VVolOpSetVBusy_r(Volume * vp, FSSYNC_VolOp_info * vopinfo)
3518 {
3519     return (vopinfo->com.command == FSYNC_VOL_NEEDVOLUME &&
3520             (vopinfo->com.reason == V_CLONE ||
3521              vopinfo->com.reason == V_DUMP));
3522 }
3523
3524
3525 /***************************************************/
3526 /* online salvager routines                        */
3527 /***************************************************/
3528 #if defined(AFS_DEMAND_ATTACH_FS)
3529 #define SALVAGE_PRIO_UPDATE_INTERVAL 3      /* number of seconds between prio updates */
3530 #define SALVAGE_COUNT_MAX 16                /* number of online salvages we
3531                                              * allow before moving the volume
3532                                              * into a permanent error state
3533                                              *
3534                                              * once this threshold is reached,
3535                                              * the operator will have to manually
3536                                              * issue a 'bos salvage' to bring
3537                                              * the volume back online
3538                                              */
3539
3540 /* check to see if we should salvage this volume
3541  * returns 1 if salvage scheduled, 0 otherwise */
3542 static int
3543 VCheckSalvage(register Volume * vp)
3544 {
3545     int ret = 0;
3546 #ifdef SALVSYNC_BUILD_CLIENT
3547     if (vp->nUsers || vp->nWaiters)
3548         return ret;
3549     if (vp->salvage.requested) {
3550         VScheduleSalvage_r(vp);
3551         ret = 1;
3552     }
3553 #endif /* SALVSYNC_BUILD_CLIENT */
3554     return ret;
3555 }
3556
3557 /*
3558  * request that a salvage be performed once
3559  * ref counts reach zero
3560  */
3561 int
3562 VRequestSalvage_r(Volume * vp, int reason, int flags)
3563 {
3564 #ifdef SALVSYNC_BUILD_CLIENT
3565     if (programType != fileServer)
3566         return 1;
3567
3568     if (!vp->salvage.requested) {
3569         vp->salvage.requested = 1;
3570         vp->salvage.reason = reason;
3571         vp->stats.last_salvage = FT_ApproxTime();
3572         if (flags & VOL_SALVAGE_INVALIDATE_HEADER) {
3573             ReleaseVolumeHeader(vp->header);
3574         }
3575         if (vp->stats.salvages < SALVAGE_COUNT_MAX) {
3576             VChangeState_r(vp, VOL_STATE_SALVAGING);
3577         } else {
3578             Log("VRequestSalvage: volume %u online salvaged too many times; forced offline.\n", vp->hashid);
3579             VChangeState_r(vp, VOL_STATE_ERROR);
3580         }
3581     }
3582 #endif /* SALVSYNC_BUILD_CLIENT */
3583     return 0;
3584 }
3585
3586 /*
3587  * update salvage priority
3588  */
3589 static int
3590 VUpdateSalvagePriority_r(Volume * vp)
3591 {
3592     int code, ret=0;
3593     afs_uint32 now;
3594
3595 #ifdef SALVSYNC_BUILD_CLIENT
3596     vp->salvage.prio++;
3597     now = FT_ApproxTime();
3598
3599     /* update the salvageserver priority queue occasionally so that
3600      * frequently requested volumes get moved to the head of the queue 
3601      */
3602     if ((vp->salvage.scheduled) &&
3603         (vp->stats.last_salvage_req < (now-SALVAGE_PRIO_UPDATE_INTERVAL))) {
3604         code = SALVSYNC_SalvageVolume(vp->hashid,
3605                                       VPartitionPath(vp->partition),
3606                                       SALVSYNC_RAISEPRIO,
3607                                       vp->salvage.reason,
3608                                       vp->salvage.prio,
3609                                       NULL);
3610         vp->stats.last_salvage_req = now;
3611         if (code != SYNC_OK) {
3612             ret = 1;
3613         }
3614     }
3615 #endif /* SALVSYNC_BUILD_CLIENT */
3616     return ret;
3617 }
3618
3619
3620 /*
3621  * schedule a salvage with the salvage server
3622  */
3623 static int
3624 VScheduleSalvage_r(Volume * vp)
3625 {
3626     int code, ret=0;
3627 #ifdef SALVSYNC_BUILD_CLIENT
3628     VolState state_save;
3629     char partName[16];
3630
3631     if (vp->nWaiters || vp->nUsers) {
3632         return 1;
3633     }
3634
3635     /* prevent endless salvage,attach,salvage,attach,... loops */
3636     if (vp->stats.salvages >= SALVAGE_COUNT_MAX)
3637         return 1;
3638
3639     if (!vp->salvage.scheduled) {
3640         /* if we haven't previously scheduled a salvage, do so now 
3641          *
3642          * set the volume to an exclusive state and drop the lock
3643          * around the SALVSYNC call
3644          */
3645         strlcpy(partName, VPartitionPath(vp->partition), sizeof(partName));
3646         state_save = VChangeState_r(vp, VOL_STATE_SALVSYNC_REQ);
3647         V_attachFlags(vp) |= VOL_IS_BUSY;
3648         VOL_UNLOCK;
3649
3650         /* can't use V_id() since there's no guarantee
3651          * we have the disk data header at this point */
3652         code = SALVSYNC_SalvageVolume(vp->hashid,
3653                                       partName,
3654                                       SALVSYNC_SALVAGE,
3655                                       vp->salvage.reason,
3656                                       vp->salvage.prio,
3657                                       NULL);
3658         VOL_LOCK;
3659         VChangeState_r(vp, state_save);
3660         V_attachFlags(vp) &= ~(VOL_IS_BUSY);
3661
3662         if (code == SYNC_OK) {
3663             vp->salvage.scheduled = 1;
3664             vp->stats.salvages++;
3665             vp->stats.last_salvage_req = FT_ApproxTime();
3666             IncUInt64(&VStats.salvages);
3667         } else {
3668             ret = 1;
3669             switch(code) {
3670             case SYNC_BAD_COMMAND:
3671             case SYNC_COM_ERROR:
3672                 break;
3673             case SYNC_DENIED:
3674                 Log("VScheduleSalvage_r:  SALVSYNC request denied\n");
3675                 break;
3676             default:
3677                 Log("VScheduleSalvage_r:  SALVSYNC unknown protocol error\n");
3678                 break;
3679             }
3680         }
3681     }
3682 #endif /* SALVSYNC_BUILD_CLIENT */
3683     return ret;
3684 }
3685
3686 /*
3687  * cancel a scheduled salvage operation
3688  */
3689 static int
3690 VCancelSalvage_r(Volume * vp, int reason)
3691 {
3692     int code, ret = 0;
3693
3694 #ifdef SALVSYNC_BUILD_CLIENT
3695     if (vp->salvage.scheduled) {
3696         code = SALVSYNC_SalvageVolume(vp->hashid,
3697                                       VPartitionPath(vp->partition),
3698                                       SALVSYNC_CANCEL,
3699                                       reason,
3700                                       0,
3701                                       NULL);
3702         if (code == SYNC_OK) {
3703             vp->salvage.scheduled = 0;
3704         } else {
3705             ret = 1;
3706         }
3707     }
3708 #endif /* SALVSYNC_BUILD_CLIENT */
3709     return ret;
3710 }
3711
3712 /* This must be called by any volume utility which needs to run while the
3713    file server is also running.  This is separated from VInitVolumePackage so
3714    that a utility can fork--and each of the children can independently
3715    initialize communication with the file server */
3716 #ifdef SALVSYNC_BUILD_CLIENT
3717 int
3718 VConnectSALV(void)
3719 {
3720     int retVal;
3721     VOL_LOCK;
3722     retVal = VConnectSALV_r();
3723     VOL_UNLOCK;
3724     return retVal;
3725 }
3726
3727 int
3728 VConnectSALV_r(void)
3729 {
3730     assert((programType != salvageServer) &&
3731            (programType != volumeUtility));
3732     return SALVSYNC_clientInit();
3733 }
3734
3735 int
3736 VDisconnectSALV(void)
3737 {
3738     int retVal;
3739     VOL_LOCK;
3740     VDisconnectSALV_r();
3741     VOL_UNLOCK;
3742     return retVal;
3743 }
3744
3745 int
3746 VDisconnectSALV_r(void)
3747
3748     assert((programType != salvageServer) &&
3749            (programType != volumeUtility));
3750     return SALVSYNC_clientFinis();
3751 }
3752
3753 int
3754 VReconnectSALV(void)
3755 {
3756     int retVal;
3757     VOL_LOCK;
3758     retVal = VReconnectSALV_r();
3759     VOL_UNLOCK;
3760     return retVal;
3761 }
3762
3763 int
3764 VReconnectSALV_r(void)
3765 {
3766     assert((programType != salvageServer) &&
3767            (programType != volumeUtility));
3768     return SALVSYNC_clientReconnect();
3769 }
3770 #endif /* SALVSYNC_BUILD_CLIENT */
3771 #endif /* AFS_DEMAND_ATTACH_FS */
3772
3773
3774 /***************************************************/
3775 /* FSSYNC routines                                 */
3776 /***************************************************/
3777
3778 /* This must be called by any volume utility which needs to run while the
3779    file server is also running.  This is separated from VInitVolumePackage so
3780    that a utility can fork--and each of the children can independently
3781    initialize communication with the file server */
3782 #ifdef FSSYNC_BUILD_CLIENT
3783 int
3784 VConnectFS(void)
3785 {
3786     int retVal;
3787     VOL_LOCK;
3788     retVal = VConnectFS_r();
3789     VOL_UNLOCK;
3790     return retVal;
3791 }
3792
3793 int
3794 VConnectFS_r(void)
3795 {
3796     int rc;
3797     assert((VInit == 2) && 
3798            (programType != fileServer) &&
3799            (programType != salvager));
3800     rc = FSYNC_clientInit();
3801     if (rc)
3802         VInit = 3;
3803     return rc;
3804 }
3805
3806 void
3807 VDisconnectFS_r(void)
3808 {
3809     assert((programType != fileServer) &&
3810            (programType != salvager));
3811     FSYNC_clientFinis();
3812     VInit = 2;
3813 }
3814
3815 void
3816 VDisconnectFS(void)
3817 {
3818     VOL_LOCK;
3819     VDisconnectFS_r();
3820     VOL_UNLOCK;
3821 }
3822
3823 static int
3824 VChildProcReconnectFS_r(void)