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 vp->shuttingDown = 0;
713 vp->goingOffline = 0;
718 (void) ReadHeader(ec, V_diskDataHandle(vp),
719 (char *)&V_disk(vp), sizeof(V_disk(vp)),
720 VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
723 Log("VAttachVolume: Error reading diskDataHandle vol header %s; error=%d\n",
727 struct IndexFileHeader iHead;
729 #if TRANSARC_VOL_STATS
731 * We just read in the diskstuff part of the header. If the detailed
732 * volume stats area has not yet been initialized, we should bzero the
733 * area and mark it as initialized.
735 if (! (V_stat_initialized(vp))) {
736 memset((char *)(V_stat_area(vp)), 0, VOL_STATS_BYTES);
737 V_stat_initialized(vp) = 1;
739 #endif /* TRANSARC_VOL_STATS */
741 (void) ReadHeader(ec, vp->vnodeIndex[vSmall].handle,
742 (char *)&iHead, sizeof(iHead),
743 SMALLINDEXMAGIC, SMALLINDEXVERSION);
746 Log("VAttachVolume: Error reading smallVnode vol header %s; error=%d\n",
751 struct IndexFileHeader iHead;
753 (void) ReadHeader(ec, vp->vnodeIndex[vLarge].handle,
754 (char *)&iHead, sizeof(iHead),
755 LARGEINDEXMAGIC, LARGEINDEXVERSION);
758 Log("VAttachVolume: Error reading largeVnode vol header %s; error=%d\n",
764 struct versionStamp stamp;
766 (void) ReadHeader(ec, V_linkHandle(vp),
767 (char *)&stamp, sizeof(stamp),
768 LINKTABLEMAGIC, LINKTABLEVERSION);
771 Log("VAttachVolume: Error reading namei vol header %s; error=%d\n",
777 Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%d\n",
782 if (V_needsSalvaged(vp)) {
783 if (vp->specialStatus) vp->specialStatus = 0;
784 Log("VAttachVolume: volume salvage flag is ON for %s; volume needs salvage\n", path);
788 if (programType == fileServer) {
790 if (V_inUse(vp) && VolumeWriteable(vp)) {
791 if (!V_needsSalvaged(vp)) {
792 V_needsSalvaged(vp) = 1;
793 VUpdateVolume_r(ec,vp);
796 Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
800 #endif /* FAST_RESTART */
801 if (V_destroyMe(vp) == DESTROY_ME) {
803 Log("VAttachVolume: volume %s is junk; it should be destroyed at next salvage\n", path);
809 AddVolumeToHashTable(vp, V_id(vp));
810 vp->nextVnodeUnique = V_uniquifier(vp);
811 vp->vnodeIndex[vSmall].bitmap = vp->vnodeIndex[vLarge].bitmap = NULL;
813 if (programType == fileServer && VolumeWriteable(vp)) {
815 for (i = 0; i<nVNODECLASSES; i++) {
821 Log("VAttachVolume: error getting bitmap for volume (%s)\n", path);
826 #endif /* BITMAP_LATER */
828 if (programType == fileServer) {
829 if (vp->specialStatus) vp->specialStatus = 0;
830 if (V_blessed(vp) && V_inService(vp) && !V_needsSalvaged(vp)) {
832 V_offlineMessage(vp)[0] = '\0';
839 /* Attach an existing volume.
840 The volume also normally goes online at this time.
841 An offline volume must be reattached to make it go online.
845 VAttachVolume(ec,volumeId, mode)
853 retVal = VAttachVolume_r(ec, volumeId, mode);
860 VAttachVolume_r(ec,volumeId, mode)
866 GetVolumePath(ec,volumeId, &part, &name);
870 vp = VGetVolume_r(&error, volumeId);
872 assert(V_inUse(vp) == 0);
873 VDetachVolume_r(ec, vp);
877 return VAttachVolumeByName_r(ec, part, name, mode);
880 /* Increment a reference count to a volume, sans context swaps. Requires
881 * possibly reading the volume header in from the disk, since there's
882 * an invariant in the volume package that nUsers>0 ==> vp->header is valid.
884 * N.B. This call can fail if we can't read in the header!! In this case
885 * we still guarantee we won't context swap, but the ref count won't be
886 * incremented (otherwise we'd violate the invariant).
888 static int VHold_r(register Volume *vp)
892 if (vp->nUsers == 0 && !GetVolumeHeader(vp)) {
893 VolumeReplacements++;
894 ReadHeader(&error, V_diskDataHandle(vp),
895 (char *)&V_disk(vp), sizeof(V_disk(vp)),
896 VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
897 if (error) return error;
903 static int VHold(register Volume *vp)
907 retVal = VHold_r(vp);
912 void VTakeOffline_r(register Volume *vp)
914 assert(vp->nUsers > 0);
915 assert(programType == fileServer);
916 vp->goingOffline = 1;
917 V_needsSalvaged(vp) = 1;
920 void VTakeOffline(register Volume *vp)
927 void VPutVolume_r(register Volume *vp)
929 assert(--vp->nUsers >= 0);
930 if (vp->nUsers == 0) {
931 ReleaseVolumeHeader(vp->header);
932 if (vp->goingOffline) {
934 assert(programType == fileServer);
935 vp->goingOffline = 0;
937 VUpdateVolume_r(&error, vp);
938 VCloseVolumeHandles_r(vp);
940 Log("VOffline: Volume %u (%s) is now offline",
941 V_id(vp), V_name(vp));
942 if (V_offlineMessage(vp)[0])
943 Log(" (%s)", V_offlineMessage(vp));
946 #ifdef AFS_PTHREAD_ENV
947 assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
948 #else /* AFS_PTHREAD_ENV */
949 LWP_NoYieldSignal(VPutVolume);
950 #endif /* AFS_PTHREAD_ENV */
952 if (vp->shuttingDown) {
953 VReleaseVolumeHandles_r(vp);
955 if (programType == fileServer)
956 #ifdef AFS_PTHREAD_ENV
957 assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
958 #else /* AFS_PTHREAD_ENV */
959 LWP_NoYieldSignal(VPutVolume);
960 #endif /* AFS_PTHREAD_ENV */
965 void VPutVolume(register Volume *vp)
972 /* Get a pointer to an attached volume. The pointer is returned regardless
973 of whether or not the volume is in service or on/off line. An error
974 code, however, is returned with an indication of the volume's status */
975 Volume *VGetVolume(ec,volumeId)
981 retVal = VGetVolume_r(ec,volumeId);
986 Volume *VGetVolume_r(ec,volumeId)
991 unsigned short V0=0, V1=0, V2=0, V3=0, V4=0, V5=0, V6=0, V7=0, V8=0, V9=0;
992 unsigned short V10=0, V11=0, V12=0, V13=0, V14=0, V15=0;
997 for (vp = VolumeHashTable[VOLUME_HASH(volumeId)];
998 vp && vp->hashid != volumeId; vp = vp->hashNext)
1005 /* Until we have reached an initialization level of 2
1006 we don't know whether this volume exists or not.
1007 We can't sleep and retry later because before a volume
1008 is attached, the caller tries to get it first. Just
1009 return VOFFLINE and the caller can choose whether to
1010 retry the command or not.*/
1021 if (vp->nUsers == 0 && !GetVolumeHeader(vp)) {
1023 VolumeReplacements++;
1024 ReadHeader(ec, V_diskDataHandle(vp),
1025 (char *)&V_disk(vp), sizeof(V_disk(vp)), VOLUMEINFOMAGIC,
1029 /* Only log the error if it was a totally unexpected error. Simply
1030 a missing inode is likely to be caused by the volume being deleted */
1031 if (errno != ENXIO || LogLevel)
1032 Log("Volume %u: couldn't reread volume header\n", vp->hashid);
1039 if (vp->shuttingDown) {
1045 if (programType == fileServer) {
1047 if (vp->goingOffline) {
1049 #ifdef AFS_PTHREAD_ENV
1050 pthread_cond_wait(&vol_put_volume_cond, &vol_glock_mutex);
1051 #else /* AFS_PTHREAD_ENV */
1052 LWP_WaitProcess(VPutVolume);
1053 #endif /* AFS_PTHREAD_ENV */
1056 if (vp->specialStatus) {
1058 *ec = vp->specialStatus;
1060 else if (V_inService(vp)==0 || V_blessed(vp)==0) {
1064 else if (V_inUse(vp)==0) {
1075 /* if no error, bump nUsers */
1076 if (vp) vp->nUsers++;
1083 /* For both VForceOffline and VOffline, we close all relevant handles.
1084 * For VOffline, if we re-attach the volume, the files may possible be
1085 * different than before.
1087 static void VReleaseVolumeHandles_r(Volume *vp)
1089 DFlushVolume(V_id(vp));
1090 VReleaseVnodeFiles_r(vp);
1092 /* Too time consuming and unnecessary for the volserver */
1093 if (programType != volumeUtility) {
1094 IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
1095 IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
1096 IH_CONDSYNC(vp->diskDataHandle);
1098 IH_CONDSYNC(vp->linkHandle);
1099 #endif /* AFS_NT40_ENV */
1102 IH_RELEASE(vp->vnodeIndex[vLarge].handle);
1103 IH_RELEASE(vp->vnodeIndex[vSmall].handle);
1104 IH_RELEASE(vp->diskDataHandle);
1105 IH_RELEASE(vp->linkHandle);
1108 /* Force the volume offline, set the salvage flag. No further references to
1109 * the volume through the volume package will be honored. */
1110 void VForceOffline_r(Volume *vp)
1115 strcpy(V_offlineMessage(vp), "Forced offline due to internal error: volume needs to be salvaged");
1116 Log("Volume %u forced offline: it needs salvaging!\n", V_id(vp));
1118 vp->goingOffline = 0;
1119 V_needsSalvaged(vp) = 1;
1120 VUpdateVolume_r(&error, vp);
1121 #ifdef AFS_PTHREAD_ENV
1122 assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
1123 #else /* AFS_PTHREAD_ENV */
1124 LWP_NoYieldSignal(VPutVolume);
1125 #endif /* AFS_PTHREAD_ENV */
1127 VReleaseVolumeHandles_r(vp);
1131 void VForceOffline(Volume *vp)
1134 VForceOffline_r(vp);
1138 /* The opposite of VAttachVolume. The volume header is written to disk, with
1139 the inUse bit turned off. A copy of the header is maintained in memory,
1140 however (which is why this is VOffline, not VDetach).
1142 void VOffline_r(Volume *vp, char *message)
1145 VolumeId vid = V_id(vp);
1146 assert(programType != volumeUtility);
1151 if (V_offlineMessage(vp)[0] == '\0')
1152 strncpy(V_offlineMessage(vp),message,
1153 sizeof(V_offlineMessage(vp)));
1154 V_offlineMessage(vp)[sizeof(V_offlineMessage(vp))-1] = '\0';
1155 vp->goingOffline = 1;
1157 vp = VGetVolume_r(&error, vid); /* Wait for it to go offline */
1158 if (vp) /* In case it was reattached... */
1162 void VOffline(Volume *vp, char *message)
1165 VOffline_r(vp, message);
1169 /* For VDetachVolume, we close all cached file descriptors, but keep
1170 * the Inode handles in case we need to read from a busy volume.
1172 static void VCloseVolumeHandles_r(Volume *vp)
1174 DFlushVolume(V_id(vp));
1175 VCloseVnodeFiles_r(vp);
1177 /* Too time consuming and unnecessary for the volserver */
1178 if (programType != volumeUtility) {
1179 IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
1180 IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
1181 IH_CONDSYNC(vp->diskDataHandle);
1183 IH_CONDSYNC(vp->linkHandle);
1184 #endif /* AFS_NT40_ENV */
1187 IH_REALLYCLOSE(vp->vnodeIndex[vLarge].handle);
1188 IH_REALLYCLOSE(vp->vnodeIndex[vSmall].handle);
1189 IH_REALLYCLOSE(vp->diskDataHandle);
1190 IH_REALLYCLOSE(vp->linkHandle);
1193 /* This gets used for the most part by utility routines that don't want
1194 * to keep all the volume headers around. Generally, the file server won't
1195 * call this routine, because then the offline message in the volume header
1196 * (or other information) will still be available to clients. For NAMEI, also
1197 * close the file handles.
1199 void VDetachVolume_r(Error *ec, Volume *vp)
1202 struct DiskPartition *tpartp;
1203 int notifyServer, useDone;
1205 *ec = 0; /* always "succeeds" */
1206 if (programType == volumeUtility) {
1207 notifyServer = vp->needsPutBack;
1208 useDone = (V_destroyMe(vp) == DESTROY_ME);
1210 tpartp = vp->partition;
1212 DeleteVolumeFromHashTable(vp);
1213 vp->shuttingDown = 1;
1215 /* Will be detached sometime in the future--this is OK since volume is offline */
1217 if (programType == volumeUtility && notifyServer) {
1218 /* Note: The server is not notified in the case of a bogus volume explicitly to
1219 make it possible to create a volume, do a partial restore, then abort the
1220 operation without ever putting the volume online. This is essential in the
1221 case of a volume move operation between two partitions on the same server. In
1222 that case, there would be two instances of the same volume, one of them bogus,
1223 which the file server would attempt to put on line */
1225 FSYNC_askfs(volume, tpartp->name, FSYNC_DONE, 0); /* don't put online */
1227 FSYNC_askfs(volume, tpartp->name, FSYNC_ON, 0); /* fs can use it again */
1228 /* Dettaching it so break all callbacks on it*/
1229 if (V_BreakVolumeCallbacks) {
1230 Log("volume %u detached; breaking all call backs\n", volume);
1231 (*V_BreakVolumeCallbacks)(volume);
1237 void VDetachVolume(Error *ec, Volume *vp)
1240 VDetachVolume_r(ec, vp);
1245 int VAllocBitmapEntry_r(ec,vp,index)
1248 register struct vnodeIndex *index;
1250 register byte *bp,*ep;
1252 /* This test is probably redundant */
1253 if (!VolumeWriteable(vp)) {
1258 if ((programType == fileServer) && !index->bitmap) {
1261 if (vp->specialStatus == VBUSY) {
1262 if (vp->goingOffline) { /* vos dump waiting for the volume to
1263 go offline. We probably come here
1264 from AddNewReadableResidency */
1268 while (vp->specialStatus == VBUSY)
1269 #ifdef AFS_PTHREAD_ENV
1271 #else /* AFS_PTHREAD_ENV */
1273 #endif /* AFS_PTHREAD_ENV */
1277 if (!index->bitmap) {
1278 vp->specialStatus = VBUSY; /* Stop anyone else from using it.*/
1279 for (i = 0; i<nVNODECLASSES; i++) {
1284 vp->specialStatus = 0;
1285 vp->shuttingDown = 1; /* Let who has it free it. */
1290 vp->specialStatus = 0; /* Allow others to have access. */
1293 #endif /* BITMAP_LATER */
1294 bp = index->bitmap + index->bitmapOffset;
1295 ep = index->bitmap + index->bitmapSize;
1297 if ((*(bit32 *)bp) != 0xffffffff) {
1299 index->bitmapOffset = bp - index->bitmap;
1302 o = ffs(~*bp)-1; /* ffs is documented in BSTRING(3) */
1304 return (bp - index->bitmap)*8 + o;
1306 bp += sizeof(bit32) /* i.e. 4 */;
1308 /* No bit map entry--must grow bitmap */
1310 realloc(index->bitmap, index->bitmapSize+VOLUME_BITMAP_GROWSIZE);
1313 bp += index->bitmapSize;
1314 memset(bp, 0, VOLUME_BITMAP_GROWSIZE);
1315 index->bitmapOffset = index->bitmapSize;
1316 index->bitmapSize += VOLUME_BITMAP_GROWSIZE;
1318 return index->bitmapOffset*8;
1321 int VAllocBitmapEntry(ec,vp,index)
1324 register struct vnodeIndex *index;
1328 retVal = VAllocBitmapEntry_r(ec,vp,index);
1333 void VFreeBitMapEntry_r(Error *ec, register struct vnodeIndex *index,
1336 unsigned int offset;
1339 if (!index->bitmap) return;
1340 #endif /* BITMAP_LATER */
1341 offset = bitNumber>>3;
1342 if (offset >= index->bitmapSize) {
1346 if (offset < index->bitmapOffset)
1347 index->bitmapOffset = offset&~3; /* Truncate to nearest bit32 */
1348 *(index->bitmap + offset) &= ~(1 << (bitNumber & 0x7));
1351 void VFreeBitMapEntry(Error *ec, register struct vnodeIndex *index,
1355 VFreeBitMapEntry_r(ec, index, bitNumber);
1359 void VUpdateVolume_r(Error *ec,Volume *vp)
1362 if (programType == fileServer)
1363 V_uniquifier(vp) = (V_inUse(vp)? V_nextVnodeUnique(vp) + 200: V_nextVnodeUnique(vp));
1364 /*printf("Writing volume header for '%s'\n", V_name(vp));*/
1365 WriteVolumeHeader_r(ec, vp);
1368 "VUpdateVolume: error updating volume header, volume %u (%s)\n",
1369 V_id(vp), V_name(vp));
1370 VForceOffline_r(vp);
1374 void VUpdateVolume(Error *ec, Volume *vp)
1377 VUpdateVolume_r(ec, vp);
1381 void VSyncVolume_r(Error *ec, Volume *vp)
1384 VUpdateVolume_r(ec, vp);
1387 fdP = IH_OPEN(V_diskDataHandle(vp));
1388 assert(fdP != NULL);
1389 code = FDH_SYNC(fdP);
1395 void VSyncVolume(Error *ec, Volume *vp)
1398 VSyncVolume_r(ec, vp);
1402 static void FreeVolume(vp)
1408 for (i = 0; i<nVNODECLASSES; i++)
1409 if (vp->vnodeIndex[i].bitmap)
1410 free(vp->vnodeIndex[i].bitmap);
1411 FreeVolumeHeader(vp);
1412 DeleteVolumeFromHashTable(vp);
1416 static void GetBitmap(Error *ec, Volume *vp, VnodeClass class)
1418 StreamHandle_t *file;
1422 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
1423 struct vnodeIndex *vip = &vp->vnodeIndex[class];
1424 struct VnodeDiskObject *vnode;
1425 unsigned int unique = 0;
1429 #endif /* BITMAP_LATER */
1433 fdP = IH_OPEN(vip->handle);
1434 assert (fdP != NULL);
1435 file = FDH_FDOPEN(fdP, "r");
1436 assert (file != NULL);
1437 vnode = (VnodeDiskObject *) malloc(vcp->diskSize);
1438 assert(vnode != NULL);
1439 size = OS_SIZE(fdP->fd_fd);
1441 nVnodes = (size <= vcp->diskSize? 0: size-vcp->diskSize)
1443 vip->bitmapSize = ((nVnodes/8)+10)/4*4; /* The 10 is a little extra so
1444 a few files can be created in this volume,
1445 the whole thing is rounded up to nearest 4
1446 bytes, because the bit map allocator likes
1449 BitMap = (byte *) calloc(1, vip->bitmapSize);
1450 assert(BitMap != NULL);
1451 #else /* BITMAP_LATER */
1452 vip->bitmap = (byte *) calloc(1, vip->bitmapSize);
1453 assert(vip->bitmap != NULL);
1454 vip->bitmapOffset = 0;
1455 #endif /* BITMAP_LATER */
1456 if (STREAM_SEEK(file,vcp->diskSize,0) != -1) {
1458 for (bitNumber = 0; bitNumber < nVnodes+100; bitNumber++) {
1459 if (STREAM_READ(vnode, vcp->diskSize, 1, file) != 1)
1461 if (vnode->type != vNull) {
1462 if (vnode->vnodeMagic != vcp->magic) {
1463 Log("GetBitmap: addled vnode index in volume %s; volume needs salvage\n",
1469 *(BitMap + (bitNumber>>3)) |= (1 << (bitNumber & 0x7));
1470 #else /* BITMAP_LATER */
1471 *(vip->bitmap + (bitNumber>>3)) |= (1 << (bitNumber & 0x7));
1472 #endif /* BITMAP_LATER */
1473 if (unique <= vnode->uniquifier)
1474 unique = vnode->uniquifier + 1;
1476 #ifndef AFS_PTHREAD_ENV
1477 if ((bitNumber & 0x00ff) == 0x0ff) { /* every 256 iterations */
1480 #endif /* !AFS_PTHREAD_ENV */
1483 if (vp->nextVnodeUnique < unique) {
1484 Log("GetBitmap: bad volume uniquifier for volume %s; volume needs salvage\n", V_name(vp));
1487 /* Paranoia, partly justified--I think fclose after fdopen
1488 * doesn't seem to close fd. In any event, the documentation
1489 * doesn't specify, so it's safer to close it twice.
1495 /* There may have been a racing condition with some other thread, both
1496 * creating the bitmaps for this volume. If the other thread was faster
1497 * the pointer to bitmap should already be filled and we can free ours.
1499 if (vip->bitmap == NULL) {
1500 vip->bitmap = BitMap;
1501 vip->bitmapOffset = 0;
1503 free((byte *)BitMap);
1504 #endif /* BITMAP_LATER */
1507 static void GetVolumePath(Error *ec, VolId volumeId, char **partitionp,
1510 static char partition[VMAXPATHLEN], name[VMAXPATHLEN];
1511 char path[VMAXPATHLEN];
1513 struct DiskPartition *dp;
1517 sprintf(&name[1],VFORMAT,volumeId);
1518 for (dp = DiskPartitionList; dp; dp = dp->next) {
1519 #ifdef AFS_LARGEFILE_ENV
1520 struct stat64 status;
1521 #else /* !AFS_LARGEFILE_ENV */
1524 strcpy(path, VPartitionPath(dp));
1526 #ifdef AFS_LARGEFILE_ENV
1527 if (stat64(path,&status) == 0)
1528 #else /* !AFS_LARGEFILE_ENV */
1529 if (stat(path,&status) == 0)
1530 #endif /* !AFS_LARGEFILE_ENV */
1532 strcpy(partition, dp->name);
1539 *partitionp = *namep = NULL;
1542 *partitionp = partition;
1552 return atoi(name+1);
1555 char *VolumeExternalName(volumeId)
1558 static char name[15];
1559 sprintf(name,VFORMAT,volumeId);
1563 #if TRANSARC_VOL_STATS
1564 #define OneDay (86400) /* 24 hours' worth of seconds */
1566 #define OneDay (24*60*60) /* 24 hours */
1567 #endif /* TRANSARC_VOL_STATS */
1569 #define Midnight(date) ((date-TimeZoneCorrection)/OneDay*OneDay+TimeZoneCorrection)
1571 /*------------------------------------------------------------------------
1572 * [export] VAdjustVolumeStatistics
1575 * If we've passed midnight, we need to update all the day use
1576 * statistics as well as zeroing the detailed volume statistics
1577 * (if we are implementing them).
1580 * vp : Pointer to the volume structure describing the lucky
1581 * volume being considered for update.
1587 * Nothing interesting.
1591 *------------------------------------------------------------------------*/
1593 VAdjustVolumeStatistics_r(vp)
1594 register Volume *vp;
1596 { /*VAdjustVolumeStatistics*/
1598 unsigned int now = FT_ApproxTime();
1600 if (now - V_dayUseDate(vp) > OneDay) {
1603 ndays = (now - V_dayUseDate(vp)) / OneDay;
1604 for (i = 6; i>ndays-1; i--)
1605 V_weekUse(vp)[i] = V_weekUse(vp)[i-ndays];
1606 for (i = 0; i<ndays-1 && i<7; i++)
1607 V_weekUse(vp)[i] = 0;
1609 V_weekUse(vp)[ndays-1] = V_dayUse(vp);
1611 V_dayUseDate(vp) = Midnight(now);
1613 #if TRANSARC_VOL_STATS
1615 * All we need to do is bzero the entire VOL_STATS_BYTES of
1616 * the detailed volume statistics area.
1618 memset((char *)(V_stat_area(vp)), 0, VOL_STATS_BYTES);
1619 #endif /* TRANSARC_VOL_STATS */
1620 } /*It's been more than a day of collection*/
1622 #if TRANSARC_VOL_STATS
1624 * Always return happily.
1627 #endif /* TRANSARC_VOL_STATS */
1629 } /*VAdjustVolumeStatistics*/
1631 VAdjustVolumeStatistics(vp)
1632 register Volume *vp;
1636 VAdjustVolumeStatistics_r(vp);
1641 void VBumpVolumeUsage_r(register Volume *vp)
1643 unsigned int now = FT_ApproxTime();
1644 if (now - V_dayUseDate(vp) > OneDay)
1645 VAdjustVolumeStatistics_r(vp);
1647 * Save the volume header image to disk after every 128 bumps to dayUse.
1649 if ((V_dayUse(vp)++ & 127) == 0) {
1651 VUpdateVolume_r(&error, vp);
1655 void VBumpVolumeUsage(register Volume *vp)
1658 VBumpVolumeUsage_r(vp);
1662 void VSetDiskUsage_r(void)
1664 static int FifteenMinuteCounter = 0;
1667 /* NOTE: Don't attempt to access the partitions list until the
1668 initialization level indicates that all volumes are attached,
1669 which implies that all partitions are initialized. */
1670 #ifdef AFS_PTHREAD_ENV
1672 #else /* AFS_PTHREAD_ENV */
1674 #endif /* AFS_PTHREAD_ENV */
1677 VResetDiskUsage_r();
1678 if (++FifteenMinuteCounter == 3) {
1679 FifteenMinuteCounter = 0;
1684 void VSetDiskUsage(void)
1691 /* The number of minutes that a volume hasn't been updated before the
1692 * "Dont salvage" flag in the volume header will be turned on */
1693 #define SALVAGE_INTERVAL (10*60)
1695 static VolumeId *UpdateList; /* Pointer to array of Volume ID's */
1696 static int nUpdatedVolumes; /* Updated with entry in UpdateList, salvage after crash flag on */
1697 static int updateSize; /* number of entries possible */
1698 #define UPDATE_LIST_SIZE 100 /* size increment */
1700 void VAddToVolumeUpdateList_r(Error *ec, Volume *vp)
1703 vp->updateTime = FT_ApproxTime();
1704 if (V_dontSalvage(vp) == 0)
1706 V_dontSalvage(vp) = 0;
1707 VSyncVolume_r(ec, vp);
1711 updateSize = UPDATE_LIST_SIZE;
1712 UpdateList = (VolumeId *) malloc(sizeof (VolumeId) * updateSize);
1714 if (nUpdatedVolumes == updateSize) {
1715 updateSize += UPDATE_LIST_SIZE;
1716 UpdateList = (VolumeId *) realloc(UpdateList, sizeof (VolumeId) * updateSize);
1719 UpdateList[nUpdatedVolumes++] = V_id(vp);
1722 static void VScanUpdateList() {
1723 register int i, gap;
1724 register Volume *vp;
1726 afs_int32 now = FT_ApproxTime();
1727 /* Be careful with this code, since it works with interleaved calls to AddToVolumeUpdateList */
1728 for (i = gap = 0; i<nUpdatedVolumes; i++) {
1729 vp = VGetVolume_r(&error, UpdateList[i-gap] = UpdateList[i]);
1732 } else if (vp->nUsers == 1 && now - vp->updateTime > SALVAGE_INTERVAL) {
1733 V_dontSalvage(vp) = DONT_SALVAGE;
1734 VUpdateVolume_r(&error, vp); /* No need to fsync--not critical */
1739 #ifndef AFS_PTHREAD_ENV
1741 #endif /* !AFS_PTHREAD_ENV */
1743 nUpdatedVolumes -= gap;
1746 /***************************************************/
1747 /* Add on routines to manage a volume header cache */
1748 /***************************************************/
1750 static struct volHeader *volumeLRU;
1752 /* Allocate a bunch of headers; string them together */
1753 static void InitLRU(howMany)
1756 register struct volHeader *hp;
1757 if (programType != fileServer)
1759 hp = (struct volHeader *)(calloc(howMany, sizeof(struct volHeader)));
1761 ReleaseVolumeHeader(hp++);
1764 /* Get a volume header from the LRU list; update the old one if necessary */
1765 /* Returns 1 if there was already a header, which is removed from the LRU list */
1766 static int GetVolumeHeader(vp)
1767 register Volume *vp;
1770 register struct volHeader *hd;
1772 static int everLogged = 0;
1774 old = (vp->header != 0); /* old == volume already has a header */
1775 if (programType != fileServer) {
1777 hd = (struct volHeader *) calloc(1, sizeof(*vp->header));
1786 if (volumeLRU == hd)
1787 volumeLRU = hd->next;
1788 assert(hd->back == vp);
1792 hd = volumeLRU->prev; /* not currently in use and least recently used */
1794 hd = (struct volHeader *) calloc(1, sizeof(*vp->header));
1795 hd->prev = hd->next = hd; /* make it look like single elt LRU */
1797 Log("****Allocated more volume headers, probably leak****\n");
1802 if (hd->diskstuff.inUse) {
1803 WriteVolumeHeader_r(&error, hd->back);
1804 /* Ignore errors; catch them later */
1806 hd->back->header = 0;
1811 if (hd->next) { /* hd->next != 0 --> in LRU chain (we zero it later) */
1812 hd->prev->next = hd->next; /* pull hd out of LRU list */
1813 hd->next->prev = hd->prev; /* if hd only element, this is noop */
1815 hd->next = hd->prev = 0;
1816 /* if not in LRU chain, next test won't be true */
1817 if (hd == volumeLRU) /* last header item, turn into empty list */
1823 /* Put it at the top of the LRU chain */
1824 static void ReleaseVolumeHeader(hd)
1825 register struct volHeader *hd;
1827 if (programType != fileServer)
1829 if (!hd || hd->next) /* no header, or header already released */
1832 hd->next = hd->prev = hd;
1834 hd->prev = volumeLRU->prev;
1835 hd->next = volumeLRU;
1836 hd->prev->next = hd->next->prev = hd;
1841 static void FreeVolumeHeader(vp)
1842 register Volume *vp;
1844 register struct volHeader *hd = vp->header;
1847 if (programType == fileServer) {
1848 ReleaseVolumeHeader(hd);
1858 /***************************************************/
1859 /* Routines to add volume to hash chain, delete it */
1860 /***************************************************/
1862 static void AddVolumeToHashTable(vp, hashid)
1863 register Volume *vp;
1865 int hash = VOLUME_HASH(hashid);
1866 vp->hashid = hashid;
1867 vp->hashNext = VolumeHashTable[hash];
1868 VolumeHashTable[hash] = vp;
1869 vp->vnodeHashOffset = VolumeHashOffset_r();
1872 static void DeleteVolumeFromHashTable(vp)
1873 register Volume *vp;
1875 int hash = VOLUME_HASH(vp->hashid);
1876 if (VolumeHashTable[hash] == vp)
1877 VolumeHashTable[hash] = vp->hashNext;
1879 Volume *tvp = VolumeHashTable[hash];
1882 while (tvp->hashNext && tvp->hashNext != vp)
1883 tvp = tvp->hashNext;
1884 if (tvp->hashNext == NULL)
1886 tvp->hashNext = vp->hashNext;
1891 void VPrintCacheStats_r(void)
1893 register struct VnodeClassInfo *vcp;
1894 vcp = &VnodeClassInfo[vLarge];
1895 Log("Large vnode cache, %d entries, %d allocs, %d gets (%d reads), %d writes\n",
1896 vcp->cacheSize, vcp->allocs, vcp->gets, vcp->reads, vcp->writes);
1897 vcp = &VnodeClassInfo[vSmall];
1898 Log("Small vnode cache,%d entries, %d allocs, %d gets (%d reads), %d writes\n",
1899 vcp->cacheSize, vcp->allocs, vcp->gets, vcp->reads, vcp->writes);
1900 Log("Volume header cache, %d entries, %d gets, %d replacements\n",
1901 VolumeCacheSize, VolumeGets, VolumeReplacements);
1904 void VPrintCacheStats(void)
1907 VPrintCacheStats_r();