2 * Copyright 2000, International Business Machines Corporation and others.
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
10 /* 1/1/89: NB: this stuff is all going to be replaced. Don't take it too seriously */
15 Institution: The Information Technology Center, Carnegie-Mellon University
19 #include <afsconfig.h>
20 #include <afs/param.h>
25 #include <afs/afsint.h>
28 #include <sys/param.h>
29 #if !defined(AFS_SGI_ENV)
32 #else /* AFS_OSF_ENV */
33 #ifdef AFS_VFSINCL_ENV
36 #include <sys/fs/ufs_fs.h>
38 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
39 #include <ufs/ufs/dinode.h>
40 #include <ufs/ffs/fs.h>
45 #else /* AFS_VFSINCL_ENV */
46 #if !defined(AFS_AIX_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
49 #endif /* AFS_VFSINCL_ENV */
50 #endif /* AFS_OSF_ENV */
51 #endif /* AFS_SGI_ENV */
52 #endif /* AFS_NT40_ENV */
70 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
72 #include <sys/mnttab.h>
73 #include <sys/mntent.h>
79 #if defined(AFS_SGI_ENV)
82 #ifdef AFS_SGI_EFS_IOPS_ENV
83 #define ROOTINO EFS_ROOTINO
84 #include <sys/fs/efs.h>
85 #include "sgiefs/efs.h" /* until 5.1 release */
90 #ifndef AFS_LINUX20_ENV
91 #include <fstab.h> /* Need to find in libc 5, present in libc 6 */
94 #endif /* AFS_SGI_ENV */
96 #endif /* AFS_HPUX_ENV */
100 #include <netinet/in.h>
101 #include <sys/wait.h>
104 #include <sys/time.h>
105 #endif /* ITIMER_REAL */
106 #endif /* AFS_NT40_ENV */
107 #if defined(AFS_SUN5_ENV) || defined(AFS_NT40_ENV) || defined(AFS_LINUX20_ENV)
114 #include <afs/errors.h>
117 #include <afs/afssyscalls.h>
120 #include <afs/afsutil.h>
125 #include "partition.h"
126 #ifdef AFS_PTHREAD_ENV
128 #else /* AFS_PTHREAD_ENV */
129 #include "afs/assert.h"
130 #endif /* AFS_PTHREAD_ENV */
137 #ifdef AFS_PTHREAD_ENV
138 pthread_mutex_t vol_glock_mutex;
139 pthread_mutex_t vol_attach_mutex;
140 pthread_cond_t vol_put_volume_cond;
141 pthread_cond_t vol_sleep_cond;
142 #endif /* AFS_PTHREAD_ENV */
145 extern void *calloc(), *realloc();
148 /* Forward declarations */
149 static Volume *attach2();
150 static void FreeVolume();
151 static void VScanUpdateList();
152 static void InitLRU();
153 static int GetVolumeHeader();
154 static void ReleaseVolumeHeader();
155 static void FreeVolumeHeader();
156 static void AddVolumeToHashTable();
157 static void DeleteVolumeFromHashTable();
158 static int VHold(Volume *vp);
159 static int VHold_r(Volume *vp);
160 static void GetBitmap(Error *ec, Volume *vp, VnodeClass class);
161 static void GetVolumePath(Error *ec, VolId volumeId, char **partitionp,
163 static void VReleaseVolumeHandles_r(Volume *vp);
164 static void VCloseVolumeHandles_r(Volume *vp);
166 int LogLevel; /* Vice loglevel--not defined as extern so that it will be
167 defined when not linked with vice, XXXX */
168 ProgramType programType; /* The type of program using the package */
171 #define VOLUME_BITMAP_GROWSIZE 16 /* bytes, => 128vnodes */
172 /* Must be a multiple of 4 (1 word) !!*/
173 #define VOLUME_HASH_TABLE_SIZE 128 /* Must be a power of 2!! */
174 #define VOLUME_HASH(volumeId) (volumeId&(VOLUME_HASH_TABLE_SIZE-1))
175 private Volume *VolumeHashTable[VOLUME_HASH_TABLE_SIZE];
178 /* This macro is used where an ffs() call does not exist. Was in util/ffs.c */
181 afs_int32 ffs_tmp = x; \
182 if (ffs_tmp == 0) return(-1); \
184 for (ffs_i = 1;; ffs_i++) { \
185 if (ffs_tmp & 1) return(ffs_i); \
186 else ffs_tmp >>= 1; \
189 #endif /* !AFS_HAVE_FFS */
191 struct Lock vol_listLock; /* Lock obtained when listing volumes: prevents a volume from being missed if the volume is attached during a list volumes */
193 extern struct Lock FSYNC_handler_lock;
195 Volume *VAttachVolumeByName();
196 Volume *VAttachVolumeByName_r();
198 static int TimeZoneCorrection; /* Number of seconds west of GMT */
200 /* Common message used when the volume goes off line */
201 char *VSalvageMessage =
202 "Files in this volume are currently unavailable; call operations";
204 int VInit; /* 0 - uninitialized,
205 1 - initialized but not all volumes have been attached,
206 2 - initialized and all volumes have been attached,
207 3 - initialized, all volumes have been attached, and
208 VConnectFS() has completed. */
211 int VolumeCacheCheck; /* Incremented everytime a volume goes on line--
212 * used to stamp volume headers and in-core
213 * vnodes. When the volume goes on-line the
214 * vnode will be invalidated */
216 int VolumeCacheSize = 200, VolumeGets=0, VolumeReplacements=0, Vlooks = 0;
219 int VInitVolumePackage(ProgramType pt, int nLargeVnodes, int nSmallVnodes,
220 int connect, int volcache)
222 int errors = 0; /* Number of errors while finding vice partitions. */
228 #ifdef AFS_PTHREAD_ENV
229 assert(pthread_mutex_init(&vol_glock_mutex, NULL) == 0);
230 assert(pthread_mutex_init(&vol_attach_mutex, NULL) == 0);
231 assert(pthread_cond_init(&vol_put_volume_cond, NULL) == 0);
232 assert(pthread_cond_init(&vol_sleep_cond, NULL) == 0);
233 #else /* AFS_PTHREAD_ENV */
235 #endif /* AFS_PTHREAD_ENV */
236 Lock_Init(&vol_listLock);
237 Lock_Init(&FSYNC_handler_lock);
238 srandom(time(0)); /* For VGetVolumeInfo */
239 gettimeofday(&tv, &tz);
240 TimeZoneCorrection = tz.tz_minuteswest*60;
242 /* Ok, we have done enough initialization that fileserver can
243 * start accepting calls, even though the volumes may not be
244 * available just yet.
248 if (programType == fileServer) {
249 /* File server or "stand" */
253 if (volcache > VolumeCacheSize)
254 VolumeCacheSize = volcache;
255 InitLRU(VolumeCacheSize);
257 VInitVnodes(vLarge, nLargeVnodes);
258 VInitVnodes(vSmall, nSmallVnodes);
261 errors = VAttachPartitions();
265 if (programType == fileServer) {
268 struct DiskPartition *diskP;
271 /* Attach all the volumes in this partition */
272 for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
273 int nAttached = 0, nUnattached = 0;
274 dirp = opendir(VPartitionPath(diskP));
276 while (dp = readdir(dirp)) {
278 p = strrchr(dp->d_name, '.');
279 if (p != NULL && strcmp(p, VHDREXT) == 0) {
282 vp = VAttachVolumeByName(&error, diskP->name, dp->d_name,
284 (*(vp?&nAttached:&nUnattached))++;
285 if (error == VOFFLINE)
286 Log("Volume %u stays offline (/vice/offline/%s exists)\n",
287 VolumeNumber(dp->d_name), dp->d_name);
293 Log("Partition %s: attached %d volumes; %d volumes not attached\n",
294 diskP->name, nAttached, nUnattached);
299 VInit = 2; /* Initialized, and all volumes have been attached */
300 if (programType == volumeUtility && connect) {
302 Log("Unable to connect to file server; aborted\n");
309 /* This must be called by any volume utility which needs to run while the
310 file server is also running. This is separated from VInitVolumePackage so
311 that a utility can fork--and each of the children can independently
312 initialize communication with the file server */
317 retVal = VConnectFS_r();
322 int VConnectFS_r(void)
325 assert(VInit == 2 && programType == volumeUtility);
326 rc = FSYNC_clientInit();
332 void VDisconnectFS_r(void) {
333 assert(programType == volumeUtility);
338 void VDisconnectFS(void) {
344 void VShutdown_r(void)
347 register Volume *vp, *np;
348 register afs_int32 code;
350 Log("VShutdown: shutting down on-line volumes...\n");
351 for (i=0; i<VOLUME_HASH_TABLE_SIZE; i++) {
352 /* try to hold first volume in the hash table */
353 for(vp = VolumeHashTable[i]; vp; vp=vp->hashNext) {
355 if (code == 0) break; /* got it */
356 /* otherwise we go around again, trying another volume */
359 /* first compute np before releasing vp, in case vp disappears
360 * after releasing. Hold it, so it doesn't disapear. If we
361 * can't hold it, try the next one in the chain. Invariant
362 * at the top of this loop is that vp is held (has extra ref count).
364 for(np=vp->hashNext; np; np=np->hashNext) {
366 if (code == 0) break; /* got it */
368 /* next, take the volume offline (drops reference count) */
369 VOffline_r(vp, "File server was shut down");
370 vp = np; /* next guy to try */
373 Log("VShutdown: complete.\n");
384 static void ReadHeader(Error *ec, IHandle_t *h, char *to, int size,
385 int magic, int version)
387 struct versionStamp *vsn;
402 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
404 FDH_REALLYCLOSE(fdP);
407 vsn = (struct versionStamp *) to;
408 if (FDH_READ(fdP, to, size) != size || vsn->magic != magic) {
410 FDH_REALLYCLOSE(fdP);
415 /* Check is conditional, in case caller wants to inspect version himself */
416 if (version && vsn->version != version) {
421 /* VolumeHeaderToDisk
422 * Allows for storing 64 bit inode numbers in on-disk volume header
425 void VolumeHeaderToDisk(VolumeDiskHeader_t *dh, VolumeHeader_t *h)
428 memset((char*)dh, 0, sizeof(VolumeDiskHeader_t));
429 dh->stamp = h->stamp;
431 dh->parent = h->parent;
433 #ifdef AFS_64BIT_IOPS_ENV
434 dh->volumeInfo_lo = (afs_int32) h->volumeInfo & 0xffffffff;
435 dh->volumeInfo_hi = (afs_int32) (h->volumeInfo >> 32) & 0xffffffff;
436 dh->smallVnodeIndex_lo = (afs_int32) h->smallVnodeIndex & 0xffffffff;
437 dh->smallVnodeIndex_hi = (afs_int32) (h->smallVnodeIndex >> 32) & 0xffffffff;
438 dh->largeVnodeIndex_lo = (afs_int32) h->largeVnodeIndex & 0xffffffff;
439 dh->largeVnodeIndex_hi = (afs_int32) (h->largeVnodeIndex >> 32) & 0xffffffff;
440 dh->linkTable_lo = (afs_int32) h->linkTable & 0xffffffff;
441 dh->linkTable_hi = (afs_int32) (h->linkTable >> 32) & 0xffffffff;
443 dh->volumeInfo_lo = h->volumeInfo;
444 dh->smallVnodeIndex_lo = h->smallVnodeIndex;
445 dh->largeVnodeIndex_lo = h->largeVnodeIndex;
446 dh->linkTable_lo = h->linkTable;
450 /* DiskToVolumeHeader
451 * Reads volume header file from disk, convering 64 bit inodes
452 * if required. Makes the assumption that AFS has *always*
453 * zero'd the volume header file so that high parts of inode
454 * numbers are 0 in older (SGI EFS) volume header files.
456 void DiskToVolumeHeader(VolumeHeader_t *h, VolumeDiskHeader_t *dh)
458 memset((char*)h, 0, sizeof(VolumeHeader_t));
459 h->stamp = dh->stamp;
461 h->parent = dh->parent;
463 #ifdef AFS_64BIT_IOPS_ENV
464 h->volumeInfo = dh->volumeInfo_lo | ((Inode)dh->volumeInfo_hi << 32);
466 h->smallVnodeIndex = dh->smallVnodeIndex_lo |
467 ((Inode)dh->smallVnodeIndex_hi << 32);
469 h->largeVnodeIndex = dh->largeVnodeIndex_lo |
470 ((Inode)dh->largeVnodeIndex_hi << 32);
471 h->linkTable = dh->linkTable_lo |
472 ((Inode)dh->linkTable_hi << 32);
474 h->volumeInfo = dh->volumeInfo_lo;
475 h->smallVnodeIndex = dh->smallVnodeIndex_lo;
476 h->largeVnodeIndex = dh->largeVnodeIndex_lo;
477 h->linkTable = dh->linkTable_lo;
482 void WriteVolumeHeader_r(ec, vp)
486 IHandle_t *h = V_diskDataHandle(vp);
496 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
498 FDH_REALLYCLOSE(fdP);
501 if (FDH_WRITE(fdP, (char*)&V_disk(vp), sizeof(V_disk(vp)))
502 != sizeof(V_disk(vp))) {
504 FDH_REALLYCLOSE(fdP);
510 /* Attach an existing volume, given its pathname, and return a
511 pointer to the volume header information. The volume also
512 normally goes online at this time. An offline volume
513 must be reattached to make it go online */
515 VAttachVolumeByName(ec, partition, name, mode)
524 retVal = VAttachVolumeByName_r(ec, partition, name, mode);
531 VAttachVolumeByName_r(ec, partition, name, mode)
539 #ifdef AFS_LARGEFILE_ENV
540 struct stat64 status;
541 #else /* !AFS_LARGEFILE_ENV */
543 #endif /* !AFS_LARGEFILE_ENV */
544 struct VolumeDiskHeader diskHeader;
545 struct VolumeHeader iheader;
546 struct DiskPartition *partp;
550 if (programType == volumeUtility) {
552 VLockPartition_r(partition);
554 if (programType == fileServer) {
555 vp = VGetVolume_r(ec, VolumeNumber(name));
559 if (vp->specialStatus == VBUSY)
561 VDetachVolume_r(ec, vp);
563 Log("VAttachVolume: Error detaching volume (%s)\n", name);
568 if (!(partp = VGetPartition_r(partition, 0))) {
570 Log("VAttachVolume: Error getting partition (%s)\n", partition);
575 strcpy(path, VPartitionPath(partp));
579 if ((fd = open(path, O_RDONLY)) == -1
580 #ifdef AFS_LARGEFILE_ENV
581 || fstat64(fd,&status) == -1
582 #else /* !AFS_LARGEFILE_ENV */
583 || fstat(fd,&status) == -1
584 #endif /* !AFS_LARGEFILE_ENV */
591 n = read(fd, &diskHeader, sizeof (diskHeader));
594 if (n != sizeof (diskHeader) || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
595 Log("VAttachVolume: Error reading volume header %s\n", path);
599 if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
600 Log("VAttachVolume: Volume %s, version number is incorrect; volume needs salvaged\n",path);
605 DiskToVolumeHeader(&iheader, &diskHeader);
606 if (programType == volumeUtility && mode != V_SECRETLY) {
607 if (FSYNC_askfs(iheader.id, partition, FSYNC_NEEDVOLUME, mode)
609 Log("VAttachVolume: attach of volume %u apparently denied by file server\n",
611 *ec = VNOVOL; /* XXXX */
616 vp = attach2(ec, path, &iheader, partp, isbusy);
617 if (programType == volumeUtility && vp) {
618 /* duplicate computation in fssync.c about whether the server
619 * takes the volume offline or not. If the volume isn't
620 * offline, we must not return it when we detach the volume,
621 * or the server will abort */
622 if (mode == V_READONLY || (!VolumeWriteable(vp) && (mode==V_CLONE || mode==V_DUMP)))
623 vp->needsPutBack = 0;
625 vp->needsPutBack = 1;
627 /* OK, there's a problem here, but one that I don't know how to
628 * fix right now, and that I don't think should arise often.
629 * Basically, we should only put back this volume to the server if
630 * it was given to us by the server, but since we don't have a vp,
631 * we can't run the VolumeWriteable function to find out as we do
632 * above when computing vp->needsPutBack. So we send it back, but
633 * there's a path in VAttachVolume on the server which may abort
634 * if this volume doesn't have a header. Should be pretty rare
635 * for all of that to happen, but if it does, probably the right
636 * fix is for the server to allow the return of readonly volumes
637 * that it doesn't think are really checked out. */
638 if (programType == volumeUtility && vp == NULL && mode != V_SECRETLY) {
639 FSYNC_askfs(iheader.id, partition, FSYNC_ON, 0);
641 else if (programType == fileServer && vp) {
642 V_needsCallback(vp) = 0;
644 if (VInit >= 2 && V_BreakVolumeCallbacks) {
645 Log("VAttachVolume: Volume %u was changed externally; breaking callbacks\n", V_id(vp));
646 (*V_BreakVolumeCallbacks)(V_id(vp));
649 VUpdateVolume_r(ec,vp);
651 Log("VAttachVolume: Error updating volume\n");
656 if (VolumeWriteable(vp) && V_dontSalvage(vp) == 0) {
657 /* This is a hack: by temporarily settint the incore
658 * dontSalvage flag ON, the volume will be put back on the
659 * Update list (with dontSalvage OFF again). It will then
660 * come back in N minutes with DONT_SALVAGE eventually
661 * set. This is the way that volumes that have never had
662 * it set get it set; or that volumes that have been
663 * offline without DONT SALVAGE having been set also
664 * eventually get it set */
665 V_dontSalvage(vp) = DONT_SALVAGE;
666 VAddToVolumeUpdateList_r(ec,vp);
668 Log("VAttachVolume: Error adding volume to update list\n");
675 Log("VOnline: volume %u (%s) attached and online\n",
676 V_id(vp), V_name(vp));
679 if (programType == volumeUtility) {
680 VUnlockPartition_r(partition);
688 private Volume *attach2(ec, path, header, partp, isbusy)
691 register struct VolumeHeader *header;
692 struct DiskPartition *partp;
698 vp = (Volume *) calloc(1, sizeof(Volume));
700 vp->specialStatus = (isbusy ? VBUSY : 0);
701 vp->device = partp->device;
702 vp->partition = partp;
703 IH_INIT(vp->vnodeIndex[vLarge].handle, partp->device, header->parent,
704 header->largeVnodeIndex);
705 IH_INIT(vp->vnodeIndex[vSmall].handle, partp->device, header->parent,
706 header->smallVnodeIndex);
707 IH_INIT(vp->diskDataHandle, partp->device, header->parent,
709 IH_INIT(vp->linkHandle, partp->device, header->parent,
711 vp->cacheCheck = ++VolumeCacheCheck;
712 /* just in case this ever rolls over */
714 vp->cacheCheck = ++VolumeCacheCheck;
715 vp->shuttingDown = 0;
716 vp->goingOffline = 0;
721 (void) ReadHeader(ec, V_diskDataHandle(vp),
722 (char *)&V_disk(vp), sizeof(V_disk(vp)),
723 VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
726 Log("VAttachVolume: Error reading diskDataHandle vol header %s; error=%d\n",
730 struct IndexFileHeader iHead;
732 #if TRANSARC_VOL_STATS
734 * We just read in the diskstuff part of the header. If the detailed
735 * volume stats area has not yet been initialized, we should bzero the
736 * area and mark it as initialized.
738 if (! (V_stat_initialized(vp))) {
739 memset((char *)(V_stat_area(vp)), 0, VOL_STATS_BYTES);
740 V_stat_initialized(vp) = 1;
742 #endif /* TRANSARC_VOL_STATS */
744 (void) ReadHeader(ec, vp->vnodeIndex[vSmall].handle,
745 (char *)&iHead, sizeof(iHead),
746 SMALLINDEXMAGIC, SMALLINDEXVERSION);
749 Log("VAttachVolume: Error reading smallVnode vol header %s; error=%d\n",
754 struct IndexFileHeader iHead;
756 (void) ReadHeader(ec, vp->vnodeIndex[vLarge].handle,
757 (char *)&iHead, sizeof(iHead),
758 LARGEINDEXMAGIC, LARGEINDEXVERSION);
761 Log("VAttachVolume: Error reading largeVnode vol header %s; error=%d\n",
767 struct versionStamp stamp;
769 (void) ReadHeader(ec, V_linkHandle(vp),
770 (char *)&stamp, sizeof(stamp),
771 LINKTABLEMAGIC, LINKTABLEVERSION);
774 Log("VAttachVolume: Error reading namei vol header %s; error=%d\n",
780 Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%d\n",
785 if (V_needsSalvaged(vp)) {
786 if (vp->specialStatus) vp->specialStatus = 0;
787 Log("VAttachVolume: volume salvage flag is ON for %s; volume needs salvage\n", path);
791 if (programType == fileServer) {
793 if (V_inUse(vp) && VolumeWriteable(vp)) {
794 if (!V_needsSalvaged(vp)) {
795 V_needsSalvaged(vp) = 1;
796 VUpdateVolume_r(ec,vp);
799 Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
803 #endif /* FAST_RESTART */
804 if (V_destroyMe(vp) == DESTROY_ME) {
806 Log("VAttachVolume: volume %s is junk; it should be destroyed at next salvage\n", path);
812 AddVolumeToHashTable(vp, V_id(vp));
813 vp->nextVnodeUnique = V_uniquifier(vp);
814 vp->vnodeIndex[vSmall].bitmap = vp->vnodeIndex[vLarge].bitmap = NULL;
816 if (programType == fileServer && VolumeWriteable(vp)) {
818 for (i = 0; i<nVNODECLASSES; i++) {
824 Log("VAttachVolume: error getting bitmap for volume (%s)\n", path);
829 #endif /* BITMAP_LATER */
831 if (programType == fileServer) {
832 if (vp->specialStatus) vp->specialStatus = 0;
833 if (V_blessed(vp) && V_inService(vp) && !V_needsSalvaged(vp)) {
835 V_offlineMessage(vp)[0] = '\0';
842 /* Attach an existing volume.
843 The volume also normally goes online at this time.
844 An offline volume must be reattached to make it go online.
848 VAttachVolume(ec,volumeId, mode)
856 retVal = VAttachVolume_r(ec, volumeId, mode);
863 VAttachVolume_r(ec,volumeId, mode)
869 GetVolumePath(ec,volumeId, &part, &name);
873 vp = VGetVolume_r(&error, volumeId);
875 assert(V_inUse(vp) == 0);
876 VDetachVolume_r(ec, vp);
880 return VAttachVolumeByName_r(ec, part, name, mode);
883 /* Increment a reference count to a volume, sans context swaps. Requires
884 * possibly reading the volume header in from the disk, since there's
885 * an invariant in the volume package that nUsers>0 ==> vp->header is valid.
887 * N.B. This call can fail if we can't read in the header!! In this case
888 * we still guarantee we won't context swap, but the ref count won't be
889 * incremented (otherwise we'd violate the invariant).
891 static int VHold_r(register Volume *vp)
895 if (vp->nUsers == 0 && !GetVolumeHeader(vp)) {
896 VolumeReplacements++;
897 ReadHeader(&error, V_diskDataHandle(vp),
898 (char *)&V_disk(vp), sizeof(V_disk(vp)),
899 VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
900 if (error) return error;
906 static int VHold(register Volume *vp)
910 retVal = VHold_r(vp);
915 void VTakeOffline_r(register Volume *vp)
917 assert(vp->nUsers > 0);
918 assert(programType == fileServer);
919 vp->goingOffline = 1;
920 V_needsSalvaged(vp) = 1;
923 void VTakeOffline(register Volume *vp)
930 void VPutVolume_r(register Volume *vp)
932 assert(--vp->nUsers >= 0);
933 if (vp->nUsers == 0) {
934 ReleaseVolumeHeader(vp->header);
935 if (vp->goingOffline) {
937 assert(programType == fileServer);
938 vp->goingOffline = 0;
940 VUpdateVolume_r(&error, vp);
941 VCloseVolumeHandles_r(vp);
943 Log("VOffline: Volume %u (%s) is now offline",
944 V_id(vp), V_name(vp));
945 if (V_offlineMessage(vp)[0])
946 Log(" (%s)", V_offlineMessage(vp));
949 #ifdef AFS_PTHREAD_ENV
950 assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
951 #else /* AFS_PTHREAD_ENV */
952 LWP_NoYieldSignal(VPutVolume);
953 #endif /* AFS_PTHREAD_ENV */
955 if (vp->shuttingDown) {
956 VReleaseVolumeHandles_r(vp);
958 if (programType == fileServer)
959 #ifdef AFS_PTHREAD_ENV
960 assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
961 #else /* AFS_PTHREAD_ENV */
962 LWP_NoYieldSignal(VPutVolume);
963 #endif /* AFS_PTHREAD_ENV */
968 void VPutVolume(register Volume *vp)
975 /* Get a pointer to an attached volume. The pointer is returned regardless
976 of whether or not the volume is in service or on/off line. An error
977 code, however, is returned with an indication of the volume's status */
978 Volume *VGetVolume(ec,volumeId)
984 retVal = VGetVolume_r(ec,volumeId);
989 Volume *VGetVolume_r(ec,volumeId)
994 unsigned short V0=0, V1=0, V2=0, V3=0, V4=0, V5=0, V6=0, V7=0, V8=0, V9=0;
995 unsigned short V10=0, V11=0, V12=0, V13=0, V14=0, V15=0;
1000 for (vp = VolumeHashTable[VOLUME_HASH(volumeId)];
1001 vp && vp->hashid != volumeId; vp = vp->hashNext)
1008 /* Until we have reached an initialization level of 2
1009 we don't know whether this volume exists or not.
1010 We can't sleep and retry later because before a volume
1011 is attached, the caller tries to get it first. Just
1012 return VOFFLINE and the caller can choose whether to
1013 retry the command or not.*/
1024 if (vp->nUsers == 0 && !GetVolumeHeader(vp)) {
1026 VolumeReplacements++;
1027 ReadHeader(ec, V_diskDataHandle(vp),
1028 (char *)&V_disk(vp), sizeof(V_disk(vp)), VOLUMEINFOMAGIC,
1032 /* Only log the error if it was a totally unexpected error. Simply
1033 a missing inode is likely to be caused by the volume being deleted */
1034 if (errno != ENXIO || LogLevel)
1035 Log("Volume %u: couldn't reread volume header\n", vp->hashid);
1042 if (vp->shuttingDown) {
1048 if (programType == fileServer) {
1050 if (vp->goingOffline) {
1052 #ifdef AFS_PTHREAD_ENV
1053 pthread_cond_wait(&vol_put_volume_cond, &vol_glock_mutex);
1054 #else /* AFS_PTHREAD_ENV */
1055 LWP_WaitProcess(VPutVolume);
1056 #endif /* AFS_PTHREAD_ENV */
1059 if (vp->specialStatus) {
1061 *ec = vp->specialStatus;
1063 else if (V_inService(vp)==0 || V_blessed(vp)==0) {
1067 else if (V_inUse(vp)==0) {
1078 /* if no error, bump nUsers */
1079 if (vp) vp->nUsers++;
1086 /* For both VForceOffline and VOffline, we close all relevant handles.
1087 * For VOffline, if we re-attach the volume, the files may possible be
1088 * different than before.
1090 static void VReleaseVolumeHandles_r(Volume *vp)
1092 DFlushVolume(V_id(vp));
1093 VReleaseVnodeFiles_r(vp);
1095 /* Too time consuming and unnecessary for the volserver */
1096 if (programType != volumeUtility) {
1097 IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
1098 IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
1099 IH_CONDSYNC(vp->diskDataHandle);
1101 IH_CONDSYNC(vp->linkHandle);
1102 #endif /* AFS_NT40_ENV */
1105 IH_RELEASE(vp->vnodeIndex[vLarge].handle);
1106 IH_RELEASE(vp->vnodeIndex[vSmall].handle);
1107 IH_RELEASE(vp->diskDataHandle);
1108 IH_RELEASE(vp->linkHandle);
1111 /* Force the volume offline, set the salvage flag. No further references to
1112 * the volume through the volume package will be honored. */
1113 void VForceOffline_r(Volume *vp)
1118 strcpy(V_offlineMessage(vp), "Forced offline due to internal error: volume needs to be salvaged");
1119 Log("Volume %u forced offline: it needs salvaging!\n", V_id(vp));
1121 vp->goingOffline = 0;
1122 V_needsSalvaged(vp) = 1;
1123 VUpdateVolume_r(&error, vp);
1124 #ifdef AFS_PTHREAD_ENV
1125 assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
1126 #else /* AFS_PTHREAD_ENV */
1127 LWP_NoYieldSignal(VPutVolume);
1128 #endif /* AFS_PTHREAD_ENV */
1130 VReleaseVolumeHandles_r(vp);
1134 void VForceOffline(Volume *vp)
1137 VForceOffline_r(vp);
1141 /* The opposite of VAttachVolume. The volume header is written to disk, with
1142 the inUse bit turned off. A copy of the header is maintained in memory,
1143 however (which is why this is VOffline, not VDetach).
1145 void VOffline_r(Volume *vp, char *message)
1148 VolumeId vid = V_id(vp);
1149 assert(programType != volumeUtility);
1154 if (V_offlineMessage(vp)[0] == '\0')
1155 strncpy(V_offlineMessage(vp),message,
1156 sizeof(V_offlineMessage(vp)));
1157 V_offlineMessage(vp)[sizeof(V_offlineMessage(vp))-1] = '\0';
1158 vp->goingOffline = 1;
1160 vp = VGetVolume_r(&error, vid); /* Wait for it to go offline */
1161 if (vp) /* In case it was reattached... */
1165 void VOffline(Volume *vp, char *message)
1168 VOffline_r(vp, message);
1172 /* For VDetachVolume, we close all cached file descriptors, but keep
1173 * the Inode handles in case we need to read from a busy volume.
1175 static void VCloseVolumeHandles_r(Volume *vp)
1177 DFlushVolume(V_id(vp));
1178 VCloseVnodeFiles_r(vp);
1180 /* Too time consuming and unnecessary for the volserver */
1181 if (programType != volumeUtility) {
1182 IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
1183 IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
1184 IH_CONDSYNC(vp->diskDataHandle);
1186 IH_CONDSYNC(vp->linkHandle);
1187 #endif /* AFS_NT40_ENV */
1190 IH_REALLYCLOSE(vp->vnodeIndex[vLarge].handle);
1191 IH_REALLYCLOSE(vp->vnodeIndex[vSmall].handle);
1192 IH_REALLYCLOSE(vp->diskDataHandle);
1193 IH_REALLYCLOSE(vp->linkHandle);
1196 /* This gets used for the most part by utility routines that don't want
1197 * to keep all the volume headers around. Generally, the file server won't
1198 * call this routine, because then the offline message in the volume header
1199 * (or other information) will still be available to clients. For NAMEI, also
1200 * close the file handles.
1202 void VDetachVolume_r(Error *ec, Volume *vp)
1205 struct DiskPartition *tpartp;
1206 int notifyServer, useDone;
1208 *ec = 0; /* always "succeeds" */
1209 if (programType == volumeUtility) {
1210 notifyServer = vp->needsPutBack;
1211 useDone = (V_destroyMe(vp) == DESTROY_ME);
1213 tpartp = vp->partition;
1215 DeleteVolumeFromHashTable(vp);
1216 vp->shuttingDown = 1;
1218 /* Will be detached sometime in the future--this is OK since volume is offline */
1220 if (programType == volumeUtility && notifyServer) {
1221 /* Note: The server is not notified in the case of a bogus volume explicitly to
1222 make it possible to create a volume, do a partial restore, then abort the
1223 operation without ever putting the volume online. This is essential in the
1224 case of a volume move operation between two partitions on the same server. In
1225 that case, there would be two instances of the same volume, one of them bogus,
1226 which the file server would attempt to put on line */
1228 FSYNC_askfs(volume, tpartp->name, FSYNC_DONE, 0); /* don't put online */
1230 FSYNC_askfs(volume, tpartp->name, FSYNC_ON, 0); /* fs can use it again */
1231 /* Dettaching it so break all callbacks on it*/
1232 if (V_BreakVolumeCallbacks) {
1233 Log("volume %u detached; breaking all call backs\n", volume);
1234 (*V_BreakVolumeCallbacks)(volume);
1240 void VDetachVolume(Error *ec, Volume *vp)
1243 VDetachVolume_r(ec, vp);
1248 int VAllocBitmapEntry_r(ec,vp,index)
1251 register struct vnodeIndex *index;
1253 register byte *bp,*ep;
1255 /* This test is probably redundant */
1256 if (!VolumeWriteable(vp)) {
1261 if ((programType == fileServer) && !index->bitmap) {
1264 if (vp->specialStatus == VBUSY) {
1265 if (vp->goingOffline) { /* vos dump waiting for the volume to
1266 go offline. We probably come here
1267 from AddNewReadableResidency */
1271 while (vp->specialStatus == VBUSY)
1272 #ifdef AFS_PTHREAD_ENV
1274 #else /* AFS_PTHREAD_ENV */
1276 #endif /* AFS_PTHREAD_ENV */
1280 if (!index->bitmap) {
1281 vp->specialStatus = VBUSY; /* Stop anyone else from using it.*/
1282 for (i = 0; i<nVNODECLASSES; i++) {
1287 vp->specialStatus = 0;
1288 vp->shuttingDown = 1; /* Let who has it free it. */
1293 vp->specialStatus = 0; /* Allow others to have access. */
1296 #endif /* BITMAP_LATER */
1297 bp = index->bitmap + index->bitmapOffset;
1298 ep = index->bitmap + index->bitmapSize;
1300 if ((*(bit32 *)bp) != 0xffffffff) {
1302 index->bitmapOffset = bp - index->bitmap;
1305 o = ffs(~*bp)-1; /* ffs is documented in BSTRING(3) */
1307 return (bp - index->bitmap)*8 + o;
1309 bp += sizeof(bit32) /* i.e. 4 */;
1311 /* No bit map entry--must grow bitmap */
1313 realloc(index->bitmap, index->bitmapSize+VOLUME_BITMAP_GROWSIZE);
1316 bp += index->bitmapSize;
1317 memset(bp, 0, VOLUME_BITMAP_GROWSIZE);
1318 index->bitmapOffset = index->bitmapSize;
1319 index->bitmapSize += VOLUME_BITMAP_GROWSIZE;
1321 return index->bitmapOffset*8;
1324 int VAllocBitmapEntry(ec,vp,index)
1327 register struct vnodeIndex *index;
1331 retVal = VAllocBitmapEntry_r(ec,vp,index);
1336 void VFreeBitMapEntry_r(Error *ec, register struct vnodeIndex *index,
1339 unsigned int offset;
1342 if (!index->bitmap) return;
1343 #endif /* BITMAP_LATER */
1344 offset = bitNumber>>3;
1345 if (offset >= index->bitmapSize) {
1349 if (offset < index->bitmapOffset)
1350 index->bitmapOffset = offset&~3; /* Truncate to nearest bit32 */
1351 *(index->bitmap + offset) &= ~(1 << (bitNumber & 0x7));
1354 void VFreeBitMapEntry(Error *ec, register struct vnodeIndex *index,
1358 VFreeBitMapEntry_r(ec, index, bitNumber);
1362 void VUpdateVolume_r(Error *ec,Volume *vp)
1365 if (programType == fileServer)
1366 V_uniquifier(vp) = (V_inUse(vp)? V_nextVnodeUnique(vp) + 200: V_nextVnodeUnique(vp));
1367 /*printf("Writing volume header for '%s'\n", V_name(vp));*/
1368 WriteVolumeHeader_r(ec, vp);
1371 "VUpdateVolume: error updating volume header, volume %u (%s)\n",
1372 V_id(vp), V_name(vp));
1373 VForceOffline_r(vp);
1377 void VUpdateVolume(Error *ec, Volume *vp)
1380 VUpdateVolume_r(ec, vp);
1384 void VSyncVolume_r(Error *ec, Volume *vp)
1387 VUpdateVolume_r(ec, vp);
1390 fdP = IH_OPEN(V_diskDataHandle(vp));
1391 assert(fdP != NULL);
1392 code = FDH_SYNC(fdP);
1398 void VSyncVolume(Error *ec, Volume *vp)
1401 VSyncVolume_r(ec, vp);
1405 static void FreeVolume(vp)
1411 for (i = 0; i<nVNODECLASSES; i++)
1412 if (vp->vnodeIndex[i].bitmap)
1413 free(vp->vnodeIndex[i].bitmap);
1414 FreeVolumeHeader(vp);
1415 DeleteVolumeFromHashTable(vp);
1419 static void GetBitmap(Error *ec, Volume *vp, VnodeClass class)
1421 StreamHandle_t *file;
1425 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
1426 struct vnodeIndex *vip = &vp->vnodeIndex[class];
1427 struct VnodeDiskObject *vnode;
1428 unsigned int unique = 0;
1432 #endif /* BITMAP_LATER */
1436 fdP = IH_OPEN(vip->handle);
1437 assert (fdP != NULL);
1438 file = FDH_FDOPEN(fdP, "r");
1439 assert (file != NULL);
1440 vnode = (VnodeDiskObject *) malloc(vcp->diskSize);
1441 assert(vnode != NULL);
1442 size = OS_SIZE(fdP->fd_fd);
1444 nVnodes = (size <= vcp->diskSize? 0: size-vcp->diskSize)
1446 vip->bitmapSize = ((nVnodes/8)+10)/4*4; /* The 10 is a little extra so
1447 a few files can be created in this volume,
1448 the whole thing is rounded up to nearest 4
1449 bytes, because the bit map allocator likes
1452 BitMap = (byte *) calloc(1, vip->bitmapSize);
1453 assert(BitMap != NULL);
1454 #else /* BITMAP_LATER */
1455 vip->bitmap = (byte *) calloc(1, vip->bitmapSize);
1456 assert(vip->bitmap != NULL);
1457 vip->bitmapOffset = 0;
1458 #endif /* BITMAP_LATER */
1459 if (STREAM_SEEK(file,vcp->diskSize,0) != -1) {
1461 for (bitNumber = 0; bitNumber < nVnodes+100; bitNumber++) {
1462 if (STREAM_READ(vnode, vcp->diskSize, 1, file) != 1)
1464 if (vnode->type != vNull) {
1465 if (vnode->vnodeMagic != vcp->magic) {
1466 Log("GetBitmap: addled vnode index in volume %s; volume needs salvage\n",
1472 *(BitMap + (bitNumber>>3)) |= (1 << (bitNumber & 0x7));
1473 #else /* BITMAP_LATER */
1474 *(vip->bitmap + (bitNumber>>3)) |= (1 << (bitNumber & 0x7));
1475 #endif /* BITMAP_LATER */
1476 if (unique <= vnode->uniquifier)
1477 unique = vnode->uniquifier + 1;
1479 #ifndef AFS_PTHREAD_ENV
1480 if ((bitNumber & 0x00ff) == 0x0ff) { /* every 256 iterations */
1483 #endif /* !AFS_PTHREAD_ENV */
1486 if (vp->nextVnodeUnique < unique) {
1487 Log("GetBitmap: bad volume uniquifier for volume %s; volume needs salvage\n", V_name(vp));
1490 /* Paranoia, partly justified--I think fclose after fdopen
1491 * doesn't seem to close fd. In any event, the documentation
1492 * doesn't specify, so it's safer to close it twice.
1498 /* There may have been a racing condition with some other thread, both
1499 * creating the bitmaps for this volume. If the other thread was faster
1500 * the pointer to bitmap should already be filled and we can free ours.
1502 if (vip->bitmap == NULL) {
1503 vip->bitmap = BitMap;
1504 vip->bitmapOffset = 0;
1506 free((byte *)BitMap);
1507 #endif /* BITMAP_LATER */
1510 static void GetVolumePath(Error *ec, VolId volumeId, char **partitionp,
1513 static char partition[VMAXPATHLEN], name[VMAXPATHLEN];
1514 char path[VMAXPATHLEN];
1516 struct DiskPartition *dp;
1520 sprintf(&name[1],VFORMAT,volumeId);
1521 for (dp = DiskPartitionList; dp; dp = dp->next) {
1522 #ifdef AFS_LARGEFILE_ENV
1523 struct stat64 status;
1524 #else /* !AFS_LARGEFILE_ENV */
1527 strcpy(path, VPartitionPath(dp));
1529 #ifdef AFS_LARGEFILE_ENV
1530 if (stat64(path,&status) == 0)
1531 #else /* !AFS_LARGEFILE_ENV */
1532 if (stat(path,&status) == 0)
1533 #endif /* !AFS_LARGEFILE_ENV */
1535 strcpy(partition, dp->name);
1542 *partitionp = *namep = NULL;
1545 *partitionp = partition;
1555 return atoi(name+1);
1558 char *VolumeExternalName(volumeId)
1561 static char name[15];
1562 sprintf(name,VFORMAT,volumeId);
1566 #if TRANSARC_VOL_STATS
1567 #define OneDay (86400) /* 24 hours' worth of seconds */
1569 #define OneDay (24*60*60) /* 24 hours */
1570 #endif /* TRANSARC_VOL_STATS */
1572 #define Midnight(date) ((date-TimeZoneCorrection)/OneDay*OneDay+TimeZoneCorrection)
1574 /*------------------------------------------------------------------------
1575 * [export] VAdjustVolumeStatistics
1578 * If we've passed midnight, we need to update all the day use
1579 * statistics as well as zeroing the detailed volume statistics
1580 * (if we are implementing them).
1583 * vp : Pointer to the volume structure describing the lucky
1584 * volume being considered for update.
1590 * Nothing interesting.
1594 *------------------------------------------------------------------------*/
1596 VAdjustVolumeStatistics_r(vp)
1597 register Volume *vp;
1599 { /*VAdjustVolumeStatistics*/
1601 unsigned int now = FT_ApproxTime();
1603 if (now - V_dayUseDate(vp) > OneDay) {
1606 ndays = (now - V_dayUseDate(vp)) / OneDay;
1607 for (i = 6; i>ndays-1; i--)
1608 V_weekUse(vp)[i] = V_weekUse(vp)[i-ndays];
1609 for (i = 0; i<ndays-1 && i<7; i++)
1610 V_weekUse(vp)[i] = 0;
1612 V_weekUse(vp)[ndays-1] = V_dayUse(vp);
1614 V_dayUseDate(vp) = Midnight(now);
1616 #if TRANSARC_VOL_STATS
1618 * All we need to do is bzero the entire VOL_STATS_BYTES of
1619 * the detailed volume statistics area.
1621 memset((char *)(V_stat_area(vp)), 0, VOL_STATS_BYTES);
1622 #endif /* TRANSARC_VOL_STATS */
1623 } /*It's been more than a day of collection*/
1625 #if TRANSARC_VOL_STATS
1627 * Always return happily.
1630 #endif /* TRANSARC_VOL_STATS */
1632 } /*VAdjustVolumeStatistics*/
1634 VAdjustVolumeStatistics(vp)
1635 register Volume *vp;
1639 VAdjustVolumeStatistics_r(vp);
1644 void VBumpVolumeUsage_r(register Volume *vp)
1646 unsigned int now = FT_ApproxTime();
1647 if (now - V_dayUseDate(vp) > OneDay)
1648 VAdjustVolumeStatistics_r(vp);
1650 * Save the volume header image to disk after every 128 bumps to dayUse.
1652 if ((V_dayUse(vp)++ & 127) == 0) {
1654 VUpdateVolume_r(&error, vp);
1658 void VBumpVolumeUsage(register Volume *vp)
1661 VBumpVolumeUsage_r(vp);
1665 void VSetDiskUsage_r(void)
1667 static int FifteenMinuteCounter = 0;
1670 /* NOTE: Don't attempt to access the partitions list until the
1671 initialization level indicates that all volumes are attached,
1672 which implies that all partitions are initialized. */
1673 #ifdef AFS_PTHREAD_ENV
1675 #else /* AFS_PTHREAD_ENV */
1677 #endif /* AFS_PTHREAD_ENV */
1680 VResetDiskUsage_r();
1681 if (++FifteenMinuteCounter == 3) {
1682 FifteenMinuteCounter = 0;
1687 void VSetDiskUsage(void)
1694 /* The number of minutes that a volume hasn't been updated before the
1695 * "Dont salvage" flag in the volume header will be turned on */
1696 #define SALVAGE_INTERVAL (10*60)
1698 static VolumeId *UpdateList; /* Pointer to array of Volume ID's */
1699 static int nUpdatedVolumes; /* Updated with entry in UpdateList, salvage after crash flag on */
1700 static int updateSize; /* number of entries possible */
1701 #define UPDATE_LIST_SIZE 100 /* size increment */
1703 void VAddToVolumeUpdateList_r(Error *ec, Volume *vp)
1706 vp->updateTime = FT_ApproxTime();
1707 if (V_dontSalvage(vp) == 0)
1709 V_dontSalvage(vp) = 0;
1710 VSyncVolume_r(ec, vp);
1714 updateSize = UPDATE_LIST_SIZE;
1715 UpdateList = (VolumeId *) malloc(sizeof (VolumeId) * updateSize);
1717 if (nUpdatedVolumes == updateSize) {
1718 updateSize += UPDATE_LIST_SIZE;
1719 UpdateList = (VolumeId *) realloc(UpdateList, sizeof (VolumeId) * updateSize);
1722 assert(UpdateList != NULL);
1723 UpdateList[nUpdatedVolumes++] = V_id(vp);
1726 static void VScanUpdateList() {
1727 register int i, gap;
1728 register Volume *vp;
1730 afs_int32 now = FT_ApproxTime();
1731 /* Be careful with this code, since it works with interleaved calls to AddToVolumeUpdateList */
1732 for (i = gap = 0; i<nUpdatedVolumes; i++) {
1733 vp = VGetVolume_r(&error, UpdateList[i-gap] = UpdateList[i]);
1736 } else if (vp->nUsers == 1 && now - vp->updateTime > SALVAGE_INTERVAL) {
1737 V_dontSalvage(vp) = DONT_SALVAGE;
1738 VUpdateVolume_r(&error, vp); /* No need to fsync--not critical */
1743 #ifndef AFS_PTHREAD_ENV
1745 #endif /* !AFS_PTHREAD_ENV */
1747 nUpdatedVolumes -= gap;
1750 /***************************************************/
1751 /* Add on routines to manage a volume header cache */
1752 /***************************************************/
1754 static struct volHeader *volumeLRU;
1756 /* Allocate a bunch of headers; string them together */
1757 static void InitLRU(howMany)
1760 register struct volHeader *hp;
1761 if (programType != fileServer)
1763 hp = (struct volHeader *)(calloc(howMany, sizeof(struct volHeader)));
1765 ReleaseVolumeHeader(hp++);
1768 /* Get a volume header from the LRU list; update the old one if necessary */
1769 /* Returns 1 if there was already a header, which is removed from the LRU list */
1770 static int GetVolumeHeader(vp)
1771 register Volume *vp;
1774 register struct volHeader *hd;
1776 static int everLogged = 0;
1778 old = (vp->header != 0); /* old == volume already has a header */
1779 if (programType != fileServer) {
1781 hd = (struct volHeader *) calloc(1, sizeof(*vp->header));
1790 if (volumeLRU == hd)
1791 volumeLRU = hd->next;
1792 assert(hd->back == vp);
1796 hd = volumeLRU->prev; /* not currently in use and least recently used */
1798 hd = (struct volHeader *) calloc(1, sizeof(*vp->header));
1799 hd->prev = hd->next = hd; /* make it look like single elt LRU */
1801 Log("****Allocated more volume headers, probably leak****\n");
1806 if (hd->diskstuff.inUse) {
1807 WriteVolumeHeader_r(&error, hd->back);
1808 /* Ignore errors; catch them later */
1810 hd->back->header = 0;
1815 if (hd->next) { /* hd->next != 0 --> in LRU chain (we zero it later) */
1816 hd->prev->next = hd->next; /* pull hd out of LRU list */
1817 hd->next->prev = hd->prev; /* if hd only element, this is noop */
1819 hd->next = hd->prev = 0;
1820 /* if not in LRU chain, next test won't be true */
1821 if (hd == volumeLRU) /* last header item, turn into empty list */
1827 /* Put it at the top of the LRU chain */
1828 static void ReleaseVolumeHeader(hd)
1829 register struct volHeader *hd;
1831 if (programType != fileServer)
1833 if (!hd || hd->next) /* no header, or header already released */
1836 hd->next = hd->prev = hd;
1838 hd->prev = volumeLRU->prev;
1839 hd->next = volumeLRU;
1840 hd->prev->next = hd->next->prev = hd;
1845 static void FreeVolumeHeader(vp)
1846 register Volume *vp;
1848 register struct volHeader *hd = vp->header;
1851 if (programType == fileServer) {
1852 ReleaseVolumeHeader(hd);
1862 /***************************************************/
1863 /* Routines to add volume to hash chain, delete it */
1864 /***************************************************/
1866 static void AddVolumeToHashTable(vp, hashid)
1867 register Volume *vp;
1869 int hash = VOLUME_HASH(hashid);
1870 vp->hashid = hashid;
1871 vp->hashNext = VolumeHashTable[hash];
1872 VolumeHashTable[hash] = vp;
1873 vp->vnodeHashOffset = VolumeHashOffset_r();
1876 static void DeleteVolumeFromHashTable(vp)
1877 register Volume *vp;
1879 int hash = VOLUME_HASH(vp->hashid);
1880 if (VolumeHashTable[hash] == vp)
1881 VolumeHashTable[hash] = vp->hashNext;
1883 Volume *tvp = VolumeHashTable[hash];
1886 while (tvp->hashNext && tvp->hashNext != vp)
1887 tvp = tvp->hashNext;
1888 if (tvp->hashNext == NULL)
1890 tvp->hashNext = vp->hashNext;
1895 void VPrintCacheStats_r(void)
1897 register struct VnodeClassInfo *vcp;
1898 vcp = &VnodeClassInfo[vLarge];
1899 Log("Large vnode cache, %d entries, %d allocs, %d gets (%d reads), %d writes\n",
1900 vcp->cacheSize, vcp->allocs, vcp->gets, vcp->reads, vcp->writes);
1901 vcp = &VnodeClassInfo[vSmall];
1902 Log("Small vnode cache,%d entries, %d allocs, %d gets (%d reads), %d writes\n",
1903 vcp->cacheSize, vcp->allocs, vcp->gets, vcp->reads, vcp->writes);
1904 Log("Volume header cache, %d entries, %d gets, %d replacements\n",
1905 VolumeCacheSize, VolumeGets, VolumeReplacements);
1908 void VPrintCacheStats(void)
1911 VPrintCacheStats_r();