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