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;
397 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
399 FDH_REALLYCLOSE(fdP);
402 vsn = (struct versionStamp *) to;
403 if (FDH_READ(fdP, to, size) != size || vsn->magic != magic) {
405 FDH_REALLYCLOSE(fdP);
410 /* Check is conditional, in case caller wants to inspect version himself */
411 if (version && vsn->version != version) {
416 /* VolumeHeaderToDisk
417 * Allows for storing 64 bit inode numbers in on-disk volume header
420 void VolumeHeaderToDisk(VolumeDiskHeader_t *dh, VolumeHeader_t *h)
423 memset((char*)dh, 0, sizeof(VolumeDiskHeader_t));
424 dh->stamp = h->stamp;
426 dh->parent = h->parent;
428 #ifdef AFS_64BIT_IOPS_ENV
429 dh->volumeInfo_lo = (afs_int32) h->volumeInfo & 0xffffffff;
430 dh->volumeInfo_hi = (afs_int32) (h->volumeInfo >> 32) & 0xffffffff;
431 dh->smallVnodeIndex_lo = (afs_int32) h->smallVnodeIndex & 0xffffffff;
432 dh->smallVnodeIndex_hi = (afs_int32) (h->smallVnodeIndex >> 32) & 0xffffffff;
433 dh->largeVnodeIndex_lo = (afs_int32) h->largeVnodeIndex & 0xffffffff;
434 dh->largeVnodeIndex_hi = (afs_int32) (h->largeVnodeIndex >> 32) & 0xffffffff;
435 dh->linkTable_lo = (afs_int32) h->linkTable & 0xffffffff;
436 dh->linkTable_hi = (afs_int32) (h->linkTable >> 32) & 0xffffffff;
438 dh->volumeInfo_lo = h->volumeInfo;
439 dh->smallVnodeIndex_lo = h->smallVnodeIndex;
440 dh->largeVnodeIndex_lo = h->largeVnodeIndex;
441 dh->linkTable_lo = h->linkTable;
445 /* DiskToVolumeHeader
446 * Reads volume header file from disk, convering 64 bit inodes
447 * if required. Makes the assumption that AFS has *always*
448 * zero'd the volume header file so that high parts of inode
449 * numbers are 0 in older (SGI EFS) volume header files.
451 void DiskToVolumeHeader(VolumeHeader_t *h, VolumeDiskHeader_t *dh)
453 memset((char*)h, 0, sizeof(VolumeHeader_t));
454 h->stamp = dh->stamp;
456 h->parent = dh->parent;
458 #ifdef AFS_64BIT_IOPS_ENV
459 h->volumeInfo = dh->volumeInfo_lo | ((Inode)dh->volumeInfo_hi << 32);
461 h->smallVnodeIndex = dh->smallVnodeIndex_lo |
462 ((Inode)dh->smallVnodeIndex_hi << 32);
464 h->largeVnodeIndex = dh->largeVnodeIndex_lo |
465 ((Inode)dh->largeVnodeIndex_hi << 32);
466 h->linkTable = dh->linkTable_lo |
467 ((Inode)dh->linkTable_hi << 32);
469 h->volumeInfo = dh->volumeInfo_lo;
470 h->smallVnodeIndex = dh->smallVnodeIndex_lo;
471 h->largeVnodeIndex = dh->largeVnodeIndex_lo;
472 h->linkTable = dh->linkTable_lo;
477 void WriteVolumeHeader_r(ec, vp)
481 IHandle_t *h = V_diskDataHandle(vp);
491 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
493 FDH_REALLYCLOSE(fdP);
496 if (FDH_WRITE(fdP, (char*)&V_disk(vp), sizeof(V_disk(vp)))
497 != sizeof(V_disk(vp))) {
499 FDH_REALLYCLOSE(fdP);
505 /* Attach an existing volume, given its pathname, and return a
506 pointer to the volume header information. The volume also
507 normally goes online at this time. An offline volume
508 must be reattached to make it go online */
510 VAttachVolumeByName(ec, partition, name, mode)
519 retVal = VAttachVolumeByName_r(ec, partition, name, mode);
526 VAttachVolumeByName_r(ec, partition, name, mode)
535 struct VolumeDiskHeader diskHeader;
536 struct VolumeHeader iheader;
537 struct DiskPartition *partp;
541 if (programType == volumeUtility) {
543 VLockPartition_r(partition);
545 if (programType == fileServer) {
546 vp = VGetVolume_r(ec, VolumeNumber(name));
550 if (vp->specialStatus == VBUSY)
552 VDetachVolume_r(ec, vp);
554 Log("VAttachVolume: Error detaching volume (%s)\n", name);
559 if (!(partp = VGetPartition_r(partition, 0))) {
561 Log("VAttachVolume: Error getting partition (%s)\n", partition);
566 strcpy(path, VPartitionPath(partp));
570 if ((fd = open(path, O_RDONLY)) == -1 || fstat(fd,&status) == -1) {
576 n = read(fd, &diskHeader, sizeof (diskHeader));
579 if (n != sizeof (diskHeader) || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
580 Log("VAttachVolume: Error reading volume header %s\n", path);
584 if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
585 Log("VAttachVolume: Volume %s, version number is incorrect; volume needs salvaged\n",path);
590 DiskToVolumeHeader(&iheader, &diskHeader);
591 if (programType == volumeUtility && mode != V_SECRETLY) {
592 if (FSYNC_askfs(iheader.id, partition, FSYNC_NEEDVOLUME, mode)
594 Log("VAttachVolume: attach of volume %u apparently denied by file server\n",
596 *ec = VNOVOL; /* XXXX */
601 vp = attach2(ec, path, &iheader, partp, isbusy);
602 if (programType == volumeUtility && vp) {
603 /* duplicate computation in fssync.c about whether the server
604 * takes the volume offline or not. If the volume isn't
605 * offline, we must not return it when we detach the volume,
606 * or the server will abort */
607 if (mode == V_READONLY || (!VolumeWriteable(vp) && (mode==V_CLONE || mode==V_DUMP)))
608 vp->needsPutBack = 0;
610 vp->needsPutBack = 1;
612 /* OK, there's a problem here, but one that I don't know how to
613 * fix right now, and that I don't think should arise often.
614 * Basically, we should only put back this volume to the server if
615 * it was given to us by the server, but since we don't have a vp,
616 * we can't run the VolumeWriteable function to find out as we do
617 * above when computing vp->needsPutBack. So we send it back, but
618 * there's a path in VAttachVolume on the server which may abort
619 * if this volume doesn't have a header. Should be pretty rare
620 * for all of that to happen, but if it does, probably the right
621 * fix is for the server to allow the return of readonly volumes
622 * that it doesn't think are really checked out. */
623 if (programType == volumeUtility && vp == NULL && mode != V_SECRETLY) {
624 FSYNC_askfs(iheader.id, partition, FSYNC_ON, 0);
626 else if (programType == fileServer && vp) {
627 V_needsCallback(vp) = 0;
629 if (VInit >= 2 && V_BreakVolumeCallbacks) {
630 Log("VAttachVolume: Volume %u was changed externally; breaking callbacks\n", V_id(vp));
631 (*V_BreakVolumeCallbacks)(V_id(vp));
634 VUpdateVolume_r(ec,vp);
636 Log("VAttachVolume: Error updating volume\n");
641 if (VolumeWriteable(vp) && V_dontSalvage(vp) == 0) {
642 /* This is a hack: by temporarily settint the incore
643 * dontSalvage flag ON, the volume will be put back on the
644 * Update list (with dontSalvage OFF again). It will then
645 * come back in N minutes with DONT_SALVAGE eventually
646 * set. This is the way that volumes that have never had
647 * it set get it set; or that volumes that have been
648 * offline without DONT SALVAGE having been set also
649 * eventually get it set */
650 V_dontSalvage(vp) = DONT_SALVAGE;
651 VAddToVolumeUpdateList_r(ec,vp);
653 Log("VAttachVolume: Error adding volume to update list\n");
660 Log("VOnline: volume %u (%s) attached and online\n",
661 V_id(vp), V_name(vp));
664 if (programType == volumeUtility) {
665 VUnlockPartition_r(partition);
673 private Volume *attach2(ec, path, header, partp, isbusy)
676 register struct VolumeHeader *header;
677 struct DiskPartition *partp;
683 vp = (Volume *) calloc(1, sizeof(Volume));
685 vp->specialStatus = (isbusy ? VBUSY : 0);
686 vp->device = partp->device;
687 vp->partition = partp;
688 IH_INIT(vp->vnodeIndex[vLarge].handle, partp->device, header->parent,
689 header->largeVnodeIndex);
690 IH_INIT(vp->vnodeIndex[vSmall].handle, partp->device, header->parent,
691 header->smallVnodeIndex);
692 IH_INIT(vp->diskDataHandle, partp->device, header->parent,
694 IH_INIT(vp->linkHandle, partp->device, header->parent,
696 vp->cacheCheck = ++VolumeCacheCheck;
697 vp->shuttingDown = 0;
698 vp->goingOffline = 0;
703 (void) ReadHeader(ec, V_diskDataHandle(vp),
704 (char *)&V_disk(vp), sizeof(V_disk(vp)),
705 VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
708 Log("VAttachVolume: Error reading diskDataHandle vol header %s; error=%d\n",
712 struct IndexFileHeader iHead;
714 #if TRANSARC_VOL_STATS
716 * We just read in the diskstuff part of the header. If the detailed
717 * volume stats area has not yet been initialized, we should bzero the
718 * area and mark it as initialized.
720 if (! (V_stat_initialized(vp))) {
721 memset((char *)(V_stat_area(vp)), 0, VOL_STATS_BYTES);
722 V_stat_initialized(vp) = 1;
724 #endif /* TRANSARC_VOL_STATS */
726 (void) ReadHeader(ec, vp->vnodeIndex[vSmall].handle,
727 (char *)&iHead, sizeof(iHead),
728 SMALLINDEXMAGIC, SMALLINDEXVERSION);
731 Log("VAttachVolume: Error reading smallVnode vol header %s; error=%d\n",
736 struct IndexFileHeader iHead;
738 (void) ReadHeader(ec, vp->vnodeIndex[vLarge].handle,
739 (char *)&iHead, sizeof(iHead),
740 LARGEINDEXMAGIC, LARGEINDEXVERSION);
743 Log("VAttachVolume: Error reading largeVnode vol header %s; error=%d\n",
749 struct versionStamp stamp;
751 (void) ReadHeader(ec, V_linkHandle(vp),
752 (char *)&stamp, sizeof(stamp),
753 LINKTABLEMAGIC, LINKTABLEVERSION);
756 Log("VAttachVolume: Error reading namei vol header %s; error=%d\n",
762 Log("VAttachVolume: Error attaching volume %s; volume needs salvage; error=%d\n",
767 if (V_needsSalvaged(vp)) {
768 if (vp->specialStatus) vp->specialStatus = 0;
769 Log("VAttachVolume: volume salvage flag is ON for %s; volume needs salvage\n", path);
773 if (programType == fileServer) {
775 if (V_inUse(vp) && VolumeWriteable(vp)) {
776 if (!V_needsSalvaged(vp)) {
777 V_needsSalvaged(vp) = 1;
778 VUpdateVolume_r(ec,vp);
781 Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
785 #endif /* FAST_RESTART */
786 if (V_destroyMe(vp) == DESTROY_ME) {
788 Log("VAttachVolume: volume %s is junk; it should be destroyed at next salvage\n", path);
794 AddVolumeToHashTable(vp, V_id(vp));
795 vp->nextVnodeUnique = V_uniquifier(vp);
796 vp->vnodeIndex[vSmall].bitmap = vp->vnodeIndex[vLarge].bitmap = NULL;
798 if (programType == fileServer && VolumeWriteable(vp)) {
800 for (i = 0; i<nVNODECLASSES; i++) {
806 Log("VAttachVolume: error getting bitmap for volume (%s)\n", path);
811 #endif /* BITMAP_LATER */
813 if (programType == fileServer) {
814 if (vp->specialStatus) vp->specialStatus = 0;
815 if (V_blessed(vp) && V_inService(vp) && !V_needsSalvaged(vp)) {
817 V_offlineMessage(vp)[0] = '\0';
824 /* Attach an existing volume.
825 The volume also normally goes online at this time.
826 An offline volume must be reattached to make it go online.
830 VAttachVolume(ec,volumeId, mode)
838 retVal = VAttachVolume_r(ec, volumeId, mode);
845 VAttachVolume_r(ec,volumeId, mode)
851 GetVolumePath(ec,volumeId, &part, &name);
855 vp = VGetVolume_r(&error, volumeId);
857 assert(V_inUse(vp) == 0);
858 VDetachVolume_r(ec, vp);
862 return VAttachVolumeByName_r(ec, part, name, mode);
865 /* Increment a reference count to a volume, sans context swaps. Requires
866 * possibly reading the volume header in from the disk, since there's
867 * an invariant in the volume package that nUsers>0 ==> vp->header is valid.
869 * N.B. This call can fail if we can't read in the header!! In this case
870 * we still guarantee we won't context swap, but the ref count won't be
871 * incremented (otherwise we'd violate the invariant).
873 static int VHold_r(register Volume *vp)
877 if (vp->nUsers == 0 && !GetVolumeHeader(vp)) {
878 VolumeReplacements++;
879 ReadHeader(&error, V_diskDataHandle(vp),
880 (char *)&V_disk(vp), sizeof(V_disk(vp)),
881 VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
882 if (error) return error;
888 static int VHold(register Volume *vp)
892 retVal = VHold_r(vp);
897 void VTakeOffline_r(register Volume *vp)
899 assert(vp->nUsers > 0);
900 assert(programType == fileServer);
901 vp->goingOffline = 1;
902 V_needsSalvaged(vp) = 1;
905 void VTakeOffline(register Volume *vp)
912 void VPutVolume_r(register Volume *vp)
914 assert(--vp->nUsers >= 0);
915 if (vp->nUsers == 0) {
916 ReleaseVolumeHeader(vp->header);
917 if (vp->goingOffline) {
919 assert(programType == fileServer);
920 vp->goingOffline = 0;
922 VUpdateVolume_r(&error, vp);
923 VCloseVolumeHandles_r(vp);
925 Log("VOffline: Volume %u (%s) is now offline",
926 V_id(vp), V_name(vp));
927 if (V_offlineMessage(vp)[0])
928 Log(" (%s)", V_offlineMessage(vp));
931 #ifdef AFS_PTHREAD_ENV
932 assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
933 #else /* AFS_PTHREAD_ENV */
934 LWP_NoYieldSignal(VPutVolume);
935 #endif /* AFS_PTHREAD_ENV */
937 if (vp->shuttingDown) {
938 VReleaseVolumeHandles_r(vp);
940 if (programType == fileServer)
941 #ifdef AFS_PTHREAD_ENV
942 assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
943 #else /* AFS_PTHREAD_ENV */
944 LWP_NoYieldSignal(VPutVolume);
945 #endif /* AFS_PTHREAD_ENV */
950 void VPutVolume(register Volume *vp)
957 /* Get a pointer to an attached volume. The pointer is returned regardless
958 of whether or not the volume is in service or on/off line. An error
959 code, however, is returned with an indication of the volume's status */
960 Volume *VGetVolume(ec,volumeId)
966 retVal = VGetVolume_r(ec,volumeId);
971 Volume *VGetVolume_r(ec,volumeId)
976 unsigned short V0=0, V1=0, V2=0, V3=0, V4=0, V5=0, V6=0, V7=0, V8=0, V9=0;
977 unsigned short V10=0, V11=0, V12=0, V13=0, V14=0, V15=0;
982 for (vp = VolumeHashTable[VOLUME_HASH(volumeId)];
983 vp && vp->hashid != volumeId; vp = vp->hashNext)
990 /* Until we have reached an initialization level of 2
991 we don't know whether this volume exists or not.
992 We can't sleep and retry later because before a volume
993 is attached, the caller tries to get it first. Just
994 return VOFFLINE and the caller can choose whether to
995 retry the command or not.*/
1006 if (vp->nUsers == 0 && !GetVolumeHeader(vp)) {
1008 VolumeReplacements++;
1009 ReadHeader(ec, V_diskDataHandle(vp),
1010 (char *)&V_disk(vp), sizeof(V_disk(vp)), VOLUMEINFOMAGIC,
1014 /* Only log the error if it was a totally unexpected error. Simply
1015 a missing inode is likely to be caused by the volume being deleted */
1016 if (errno != ENXIO || LogLevel)
1017 Log("Volume %u: couldn't reread volume header\n", vp->hashid);
1024 if (vp->shuttingDown) {
1030 if (programType == fileServer) {
1032 if (vp->goingOffline) {
1034 #ifdef AFS_PTHREAD_ENV
1035 pthread_cond_wait(&vol_put_volume_cond, &vol_glock_mutex);
1036 #else /* AFS_PTHREAD_ENV */
1037 LWP_WaitProcess(VPutVolume);
1038 #endif /* AFS_PTHREAD_ENV */
1041 if (vp->specialStatus) {
1043 *ec = vp->specialStatus;
1045 else if (V_inService(vp)==0 || V_blessed(vp)==0) {
1049 else if (V_inUse(vp)==0) {
1060 /* if no error, bump nUsers */
1061 if (vp) vp->nUsers++;
1068 /* For both VForceOffline and VOffline, we close all relevant handles.
1069 * For VOffline, if we re-attach the volume, the files may possible be
1070 * different than before.
1072 static void VReleaseVolumeHandles_r(Volume *vp)
1074 DFlushVolume(V_id(vp));
1075 VReleaseVnodeFiles_r(vp);
1077 /* Too time consuming and unnecessary for the volserver */
1078 if (programType != volumeUtility) {
1079 IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
1080 IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
1081 IH_CONDSYNC(vp->diskDataHandle);
1083 IH_CONDSYNC(vp->linkHandle);
1084 #endif /* AFS_NT40_ENV */
1087 IH_RELEASE(vp->vnodeIndex[vLarge].handle);
1088 IH_RELEASE(vp->vnodeIndex[vSmall].handle);
1089 IH_RELEASE(vp->diskDataHandle);
1090 IH_RELEASE(vp->linkHandle);
1093 /* Force the volume offline, set the salvage flag. No further references to
1094 * the volume through the volume package will be honored. */
1095 void VForceOffline_r(Volume *vp)
1100 strcpy(V_offlineMessage(vp), "Forced offline due to internal error: volume needs to be salvaged");
1101 Log("Volume %u forced offline: it needs salvaging!\n", V_id(vp));
1103 vp->goingOffline = 0;
1104 V_needsSalvaged(vp) = 1;
1105 VUpdateVolume_r(&error, vp);
1106 #ifdef AFS_PTHREAD_ENV
1107 assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
1108 #else /* AFS_PTHREAD_ENV */
1109 LWP_NoYieldSignal(VPutVolume);
1110 #endif /* AFS_PTHREAD_ENV */
1112 VReleaseVolumeHandles_r(vp);
1116 void VForceOffline(Volume *vp)
1119 VForceOffline_r(vp);
1123 /* The opposite of VAttachVolume. The volume header is written to disk, with
1124 the inUse bit turned off. A copy of the header is maintained in memory,
1125 however (which is why this is VOffline, not VDetach).
1127 void VOffline_r(Volume *vp, char *message)
1130 VolumeId vid = V_id(vp);
1131 assert(programType != volumeUtility);
1136 if (V_offlineMessage(vp)[0] == '\0')
1137 strncpy(V_offlineMessage(vp),message,
1138 sizeof(V_offlineMessage(vp)));
1139 V_offlineMessage(vp)[sizeof(V_offlineMessage(vp))-1] = '\0';
1140 vp->goingOffline = 1;
1142 vp = VGetVolume_r(&error, vid); /* Wait for it to go offline */
1143 if (vp) /* In case it was reattached... */
1147 void VOffline(Volume *vp, char *message)
1150 VOffline_r(vp, message);
1154 /* For VDetachVolume, we close all cached file descriptors, but keep
1155 * the Inode handles in case we need to read from a busy volume.
1157 static void VCloseVolumeHandles_r(Volume *vp)
1159 DFlushVolume(V_id(vp));
1160 VCloseVnodeFiles_r(vp);
1162 /* Too time consuming and unnecessary for the volserver */
1163 if (programType != volumeUtility) {
1164 IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
1165 IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
1166 IH_CONDSYNC(vp->diskDataHandle);
1168 IH_CONDSYNC(vp->linkHandle);
1169 #endif /* AFS_NT40_ENV */
1172 IH_REALLYCLOSE(vp->vnodeIndex[vLarge].handle);
1173 IH_REALLYCLOSE(vp->vnodeIndex[vSmall].handle);
1174 IH_REALLYCLOSE(vp->diskDataHandle);
1175 IH_REALLYCLOSE(vp->linkHandle);
1178 /* This gets used for the most part by utility routines that don't want
1179 * to keep all the volume headers around. Generally, the file server won't
1180 * call this routine, because then the offline message in the volume header
1181 * (or other information) will still be available to clients. For NAMEI, also
1182 * close the file handles.
1184 void VDetachVolume_r(Error *ec, Volume *vp)
1187 struct DiskPartition *tpartp;
1188 int notifyServer, useDone;
1190 *ec = 0; /* always "succeeds" */
1191 if (programType == volumeUtility) {
1192 notifyServer = vp->needsPutBack;
1193 useDone = (V_destroyMe(vp) == DESTROY_ME);
1195 tpartp = vp->partition;
1197 DeleteVolumeFromHashTable(vp);
1198 vp->shuttingDown = 1;
1200 /* Will be detached sometime in the future--this is OK since volume is offline */
1202 if (programType == volumeUtility && notifyServer) {
1203 /* Note: The server is not notified in the case of a bogus volume explicitly to
1204 make it possible to create a volume, do a partial restore, then abort the
1205 operation without ever putting the volume online. This is essential in the
1206 case of a volume move operation between two partitions on the same server. In
1207 that case, there would be two instances of the same volume, one of them bogus,
1208 which the file server would attempt to put on line */
1210 FSYNC_askfs(volume, tpartp->name, FSYNC_DONE, 0); /* don't put online */
1212 FSYNC_askfs(volume, tpartp->name, FSYNC_ON, 0); /* fs can use it again */
1213 /* Dettaching it so break all callbacks on it*/
1214 if (V_BreakVolumeCallbacks) {
1215 Log("volume %u detached; breaking all call backs\n", volume);
1216 (*V_BreakVolumeCallbacks)(volume);
1222 void VDetachVolume(Error *ec, Volume *vp)
1225 VDetachVolume_r(ec, vp);
1230 int VAllocBitmapEntry_r(ec,vp,index)
1233 register struct vnodeIndex *index;
1235 register byte *bp,*ep;
1237 /* This test is probably redundant */
1238 if (!VolumeWriteable(vp)) {
1243 if ((programType == fileServer) && !index->bitmap) {
1246 if (vp->specialStatus == VBUSY) {
1247 if (vp->goingOffline) { /* vos dump waiting for the volume to
1248 go offline. We probably come here
1249 from AddNewReadableResidency */
1253 while (vp->specialStatus == VBUSY)
1254 #ifdef AFS_PTHREAD_ENV
1256 #else /* AFS_PTHREAD_ENV */
1258 #endif /* AFS_PTHREAD_ENV */
1262 if (!index->bitmap) {
1263 vp->specialStatus = VBUSY; /* Stop anyone else from using it.*/
1264 for (i = 0; i<nVNODECLASSES; i++) {
1269 vp->specialStatus = 0;
1270 vp->shuttingDown = 1; /* Let who has it free it. */
1275 vp->specialStatus = 0; /* Allow others to have access. */
1278 #endif /* BITMAP_LATER */
1279 bp = index->bitmap + index->bitmapOffset;
1280 ep = index->bitmap + index->bitmapSize;
1282 if ((*(bit32 *)bp) != 0xffffffff) {
1284 index->bitmapOffset = bp - index->bitmap;
1287 o = ffs(~*bp)-1; /* ffs is documented in BSTRING(3) */
1289 return (bp - index->bitmap)*8 + o;
1291 bp += sizeof(bit32) /* i.e. 4 */;
1293 /* No bit map entry--must grow bitmap */
1295 realloc(index->bitmap, index->bitmapSize+VOLUME_BITMAP_GROWSIZE);
1298 bp += index->bitmapSize;
1299 memset(bp, 0, VOLUME_BITMAP_GROWSIZE);
1300 index->bitmapOffset = index->bitmapSize;
1301 index->bitmapSize += VOLUME_BITMAP_GROWSIZE;
1303 return index->bitmapOffset*8;
1306 int VAllocBitmapEntry(ec,vp,index)
1309 register struct vnodeIndex *index;
1313 retVal = VAllocBitmapEntry_r(ec,vp,index);
1318 void VFreeBitMapEntry_r(Error *ec, register struct vnodeIndex *index,
1321 unsigned int offset;
1324 if (!index->bitmap) return;
1325 #endif /* BITMAP_LATER */
1326 offset = bitNumber>>3;
1327 if (offset >= index->bitmapSize) {
1331 if (offset < index->bitmapOffset)
1332 index->bitmapOffset = offset&~3; /* Truncate to nearest bit32 */
1333 *(index->bitmap + offset) &= ~(1 << (bitNumber & 0x7));
1336 void VFreeBitMapEntry(Error *ec, register struct vnodeIndex *index,
1340 VFreeBitMapEntry_r(ec, index, bitNumber);
1344 void VUpdateVolume_r(Error *ec,Volume *vp)
1347 if (programType == fileServer)
1348 V_uniquifier(vp) = (V_inUse(vp)? V_nextVnodeUnique(vp) + 200: V_nextVnodeUnique(vp));
1349 /*printf("Writing volume header for '%s'\n", V_name(vp));*/
1350 WriteVolumeHeader_r(ec, vp);
1353 "VUpdateVolume: error updating volume header, volume %u (%s)\n",
1354 V_id(vp), V_name(vp));
1355 VForceOffline_r(vp);
1359 void VUpdateVolume(Error *ec, Volume *vp)
1362 VUpdateVolume_r(ec, vp);
1366 void VSyncVolume_r(Error *ec, Volume *vp)
1369 VUpdateVolume_r(ec, vp);
1372 fdP = IH_OPEN(V_diskDataHandle(vp));
1373 assert(fdP != NULL);
1374 code = FDH_SYNC(fdP);
1380 void VSyncVolume(Error *ec, Volume *vp)
1383 VSyncVolume_r(ec, vp);
1387 static void FreeVolume(vp)
1393 for (i = 0; i<nVNODECLASSES; i++)
1394 if (vp->vnodeIndex[i].bitmap)
1395 free(vp->vnodeIndex[i].bitmap);
1396 FreeVolumeHeader(vp);
1397 DeleteVolumeFromHashTable(vp);
1401 static void GetBitmap(Error *ec, Volume *vp, VnodeClass class)
1403 StreamHandle_t *file;
1407 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
1408 struct vnodeIndex *vip = &vp->vnodeIndex[class];
1409 struct VnodeDiskObject *vnode;
1410 unsigned int unique = 0;
1414 #endif /* BITMAP_LATER */
1418 fdP = IH_OPEN(vip->handle);
1419 assert (fdP != NULL);
1420 file = FDH_FDOPEN(fdP, "r");
1421 assert (file != NULL);
1422 vnode = (VnodeDiskObject *) malloc(vcp->diskSize);
1423 assert(vnode != NULL);
1424 size = OS_SIZE(fdP->fd_fd);
1426 nVnodes = (size <= vcp->diskSize? 0: size-vcp->diskSize)
1428 vip->bitmapSize = ((nVnodes/8)+10)/4*4; /* The 10 is a little extra so
1429 a few files can be created in this volume,
1430 the whole thing is rounded up to nearest 4
1431 bytes, because the bit map allocator likes
1434 BitMap = (byte *) calloc(1, vip->bitmapSize);
1435 assert(BitMap != NULL);
1436 #else /* BITMAP_LATER */
1437 vip->bitmap = (byte *) calloc(1, vip->bitmapSize);
1438 assert(vip->bitmap != NULL);
1439 vip->bitmapOffset = 0;
1440 #endif /* BITMAP_LATER */
1441 if (STREAM_SEEK(file,vcp->diskSize,0) != -1) {
1443 for (bitNumber = 0; bitNumber < nVnodes+100; bitNumber++) {
1444 if (STREAM_READ(vnode, vcp->diskSize, 1, file) != 1)
1446 if (vnode->type != vNull) {
1447 if (vnode->vnodeMagic != vcp->magic) {
1448 Log("GetBitmap: addled vnode index in volume %s; volume needs salvage\n",
1454 *(BitMap + (bitNumber>>3)) |= (1 << (bitNumber & 0x7));
1455 #else /* BITMAP_LATER */
1456 *(vip->bitmap + (bitNumber>>3)) |= (1 << (bitNumber & 0x7));
1457 #endif /* BITMAP_LATER */
1458 if (unique <= vnode->uniquifier)
1459 unique = vnode->uniquifier + 1;
1461 #ifndef AFS_PTHREAD_ENV
1462 if ((bitNumber & 0x00ff) == 0x0ff) { /* every 256 iterations */
1465 #endif /* !AFS_PTHREAD_ENV */
1468 if (vp->nextVnodeUnique < unique) {
1469 Log("GetBitmap: bad volume uniquifier for volume %s; volume needs salvage\n", V_name(vp));
1472 /* Paranoia, partly justified--I think fclose after fdopen
1473 * doesn't seem to close fd. In any event, the documentation
1474 * doesn't specify, so it's safer to close it twice.
1480 /* There may have been a racing condition with some other thread, both
1481 * creating the bitmaps for this volume. If the other thread was faster
1482 * the pointer to bitmap should already be filled and we can free ours.
1484 if (vip->bitmap == NULL) {
1485 vip->bitmap = BitMap;
1486 vip->bitmapOffset = 0;
1488 free((byte *)BitMap);
1489 #endif /* BITMAP_LATER */
1492 static void GetVolumePath(Error *ec, VolId volumeId, char **partitionp,
1495 static char partition[VMAXPATHLEN], name[VMAXPATHLEN];
1496 char path[VMAXPATHLEN];
1498 struct DiskPartition *dp;
1502 sprintf(&name[1],VFORMAT,volumeId);
1503 for (dp = DiskPartitionList; dp; dp = dp->next) {
1505 strcpy(path, VPartitionPath(dp));
1507 if (stat(path,&status) == 0) {
1508 strcpy(partition, dp->name);
1515 *partitionp = *namep = NULL;
1518 *partitionp = partition;
1528 return atoi(name+1);
1531 char *VolumeExternalName(volumeId)
1534 static char name[15];
1535 sprintf(name,VFORMAT,volumeId);
1539 #if TRANSARC_VOL_STATS
1540 #define OneDay (86400) /* 24 hours' worth of seconds */
1542 #define OneDay (24*60*60) /* 24 hours */
1543 #endif /* TRANSARC_VOL_STATS */
1545 #define Midnight(date) ((date-TimeZoneCorrection)/OneDay*OneDay+TimeZoneCorrection)
1547 /*------------------------------------------------------------------------
1548 * [export] VAdjustVolumeStatistics
1551 * If we've passed midnight, we need to update all the day use
1552 * statistics as well as zeroing the detailed volume statistics
1553 * (if we are implementing them).
1556 * vp : Pointer to the volume structure describing the lucky
1557 * volume being considered for update.
1563 * Nothing interesting.
1567 *------------------------------------------------------------------------*/
1569 VAdjustVolumeStatistics_r(vp)
1570 register Volume *vp;
1572 { /*VAdjustVolumeStatistics*/
1574 unsigned int now = FT_ApproxTime();
1576 if (now - V_dayUseDate(vp) > OneDay) {
1579 ndays = (now - V_dayUseDate(vp)) / OneDay;
1580 for (i = 6; i>ndays-1; i--)
1581 V_weekUse(vp)[i] = V_weekUse(vp)[i-ndays];
1582 for (i = 0; i<ndays-1 && i<7; i++)
1583 V_weekUse(vp)[i] = 0;
1585 V_weekUse(vp)[ndays-1] = V_dayUse(vp);
1587 V_dayUseDate(vp) = Midnight(now);
1589 #if TRANSARC_VOL_STATS
1591 * All we need to do is bzero the entire VOL_STATS_BYTES of
1592 * the detailed volume statistics area.
1594 memset((char *)(V_stat_area(vp)), 0, VOL_STATS_BYTES);
1595 #endif /* TRANSARC_VOL_STATS */
1596 } /*It's been more than a day of collection*/
1598 #if TRANSARC_VOL_STATS
1600 * Always return happily.
1603 #endif /* TRANSARC_VOL_STATS */
1605 } /*VAdjustVolumeStatistics*/
1607 VAdjustVolumeStatistics(vp)
1608 register Volume *vp;
1612 VAdjustVolumeStatistics_r(vp);
1617 void VBumpVolumeUsage_r(register Volume *vp)
1619 unsigned int now = FT_ApproxTime();
1620 if (now - V_dayUseDate(vp) > OneDay)
1621 VAdjustVolumeStatistics_r(vp);
1623 * Save the volume header image to disk after every 128 bumps to dayUse.
1625 if ((V_dayUse(vp)++ & 127) == 0) {
1627 VUpdateVolume_r(&error, vp);
1631 void VBumpVolumeUsage(register Volume *vp)
1634 VBumpVolumeUsage_r(vp);
1638 void VSetDiskUsage_r(void)
1640 static int FifteenMinuteCounter = 0;
1643 /* NOTE: Don't attempt to access the partitions list until the
1644 initialization level indicates that all volumes are attached,
1645 which implies that all partitions are initialized. */
1646 #ifdef AFS_PTHREAD_ENV
1648 #else /* AFS_PTHREAD_ENV */
1650 #endif /* AFS_PTHREAD_ENV */
1653 VResetDiskUsage_r();
1654 if (++FifteenMinuteCounter == 3) {
1655 FifteenMinuteCounter = 0;
1660 void VSetDiskUsage(void)
1667 /* The number of minutes that a volume hasn't been updated before the
1668 * "Dont salvage" flag in the volume header will be turned on */
1669 #define SALVAGE_INTERVAL (10*60)
1671 static VolumeId *UpdateList; /* Pointer to array of Volume ID's */
1672 static int nUpdatedVolumes; /* Updated with entry in UpdateList, salvage after crash flag on */
1673 static int updateSize; /* number of entries possible */
1674 #define UPDATE_LIST_SIZE 100 /* size increment */
1676 void VAddToVolumeUpdateList_r(Error *ec, Volume *vp)
1679 vp->updateTime = FT_ApproxTime();
1680 if (V_dontSalvage(vp) == 0)
1682 V_dontSalvage(vp) = 0;
1683 VSyncVolume_r(ec, vp);
1687 updateSize = UPDATE_LIST_SIZE;
1688 UpdateList = (VolumeId *) malloc(sizeof (VolumeId) * updateSize);
1690 if (nUpdatedVolumes == updateSize) {
1691 updateSize += UPDATE_LIST_SIZE;
1692 UpdateList = (VolumeId *) realloc(UpdateList, sizeof (VolumeId) * updateSize);
1695 UpdateList[nUpdatedVolumes++] = V_id(vp);
1698 static void VScanUpdateList() {
1699 register int i, gap;
1700 register Volume *vp;
1702 afs_int32 now = FT_ApproxTime();
1703 /* Be careful with this code, since it works with interleaved calls to AddToVolumeUpdateList */
1704 for (i = gap = 0; i<nUpdatedVolumes; i++) {
1705 vp = VGetVolume_r(&error, UpdateList[i-gap] = UpdateList[i]);
1708 } else if (vp->nUsers == 1 && now - vp->updateTime > SALVAGE_INTERVAL) {
1709 V_dontSalvage(vp) = DONT_SALVAGE;
1710 VUpdateVolume_r(&error, vp); /* No need to fsync--not critical */
1715 #ifndef AFS_PTHREAD_ENV
1717 #endif /* !AFS_PTHREAD_ENV */
1719 nUpdatedVolumes -= gap;
1722 /***************************************************/
1723 /* Add on routines to manage a volume header cache */
1724 /***************************************************/
1726 static struct volHeader *volumeLRU;
1728 /* Allocate a bunch of headers; string them together */
1729 static void InitLRU(howMany)
1732 register struct volHeader *hp;
1733 if (programType != fileServer)
1735 hp = (struct volHeader *)(calloc(howMany, sizeof(struct volHeader)));
1737 ReleaseVolumeHeader(hp++);
1740 /* Get a volume header from the LRU list; update the old one if necessary */
1741 /* Returns 1 if there was already a header, which is removed from the LRU list */
1742 static int GetVolumeHeader(vp)
1743 register Volume *vp;
1746 register struct volHeader *hd;
1748 static int everLogged = 0;
1750 old = (vp->header != 0); /* old == volume already has a header */
1751 if (programType != fileServer) {
1753 hd = (struct volHeader *) calloc(1, sizeof(*vp->header));
1762 if (volumeLRU == hd)
1763 volumeLRU = hd->next;
1764 assert(hd->back == vp);
1768 hd = volumeLRU->prev; /* not currently in use and least recently used */
1770 hd = (struct volHeader *) calloc(1, sizeof(*vp->header));
1771 hd->prev = hd->next = hd; /* make it look like single elt LRU */
1773 Log("****Allocated more volume headers, probably leak****\n");
1778 if (hd->diskstuff.inUse) {
1779 WriteVolumeHeader_r(&error, hd->back);
1780 /* Ignore errors; catch them later */
1782 hd->back->header = 0;
1787 if (hd->next) { /* hd->next != 0 --> in LRU chain (we zero it later) */
1788 hd->prev->next = hd->next; /* pull hd out of LRU list */
1789 hd->next->prev = hd->prev; /* if hd only element, this is noop */
1791 hd->next = hd->prev = 0;
1792 /* if not in LRU chain, next test won't be true */
1793 if (hd == volumeLRU) /* last header item, turn into empty list */
1799 /* Put it at the top of the LRU chain */
1800 static void ReleaseVolumeHeader(hd)
1801 register struct volHeader *hd;
1803 if (programType != fileServer)
1805 if (!hd || hd->next) /* no header, or header already released */
1808 hd->next = hd->prev = hd;
1810 hd->prev = volumeLRU->prev;
1811 hd->next = volumeLRU;
1812 hd->prev->next = hd->next->prev = hd;
1817 static void FreeVolumeHeader(vp)
1818 register Volume *vp;
1820 register struct volHeader *hd = vp->header;
1823 if (programType == fileServer) {
1824 ReleaseVolumeHeader(hd);
1834 /***************************************************/
1835 /* Routines to add volume to hash chain, delete it */
1836 /***************************************************/
1838 static void AddVolumeToHashTable(vp, hashid)
1839 register Volume *vp;
1841 int hash = VOLUME_HASH(hashid);
1842 vp->hashid = hashid;
1843 vp->hashNext = VolumeHashTable[hash];
1844 VolumeHashTable[hash] = vp;
1845 vp->vnodeHashOffset = VolumeHashOffset_r();
1848 static void DeleteVolumeFromHashTable(vp)
1849 register Volume *vp;
1851 int hash = VOLUME_HASH(vp->hashid);
1852 if (VolumeHashTable[hash] == vp)
1853 VolumeHashTable[hash] = vp->hashNext;
1855 Volume *tvp = VolumeHashTable[hash];
1858 while (tvp->hashNext && tvp->hashNext != vp)
1859 tvp = tvp->hashNext;
1860 if (tvp->hashNext == NULL)
1862 tvp->hashNext = vp->hashNext;
1867 void VPrintCacheStats_r(void)
1869 register struct VnodeClassInfo *vcp;
1870 vcp = &VnodeClassInfo[vLarge];
1871 Log("Large vnode cache, %d entries, %d allocs, %d gets (%d reads), %d writes\n",
1872 vcp->cacheSize, vcp->allocs, vcp->gets, vcp->reads, vcp->writes);
1873 vcp = &VnodeClassInfo[vSmall];
1874 Log("Small vnode cache,%d entries, %d allocs, %d gets (%d reads), %d writes\n",
1875 vcp->cacheSize, vcp->allocs, vcp->gets, vcp->reads, vcp->writes);
1876 Log("Volume header cache, %d entries, %d gets, %d replacements\n",
1877 VolumeCacheSize, VolumeGets, VolumeReplacements);
1880 void VPrintCacheStats(void)
1883 VPrintCacheStats_r();