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