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
12 /* 1/1/89: NB: this stuff is all going to be replaced. Don't take it too seriously */
17 Institution: The Information Technology Center, Carnegie-Mellon University
21 #include <afs/param.h>
23 #include <afs/afsint.h>
26 #include <sys/param.h>
27 #if !defined(AFS_SGI_ENV)
30 #else /* AFS_OSF_ENV */
31 #ifdef AFS_VFSINCL_ENV
34 #include <sys/fs/ufs_fs.h>
37 #include <ufs/ufs/dinode.h>
38 #include <ufs/ffs/fs.h>
43 #else /* AFS_VFSINCL_ENV */
44 #if !defined(AFS_AIX_ENV) && !defined(AFS_LINUX20_ENV)
47 #endif /* AFS_VFSINCL_ENV */
48 #endif /* AFS_OSF_ENV */
49 #endif /* AFS_SGI_ENV */
50 #endif /* AFS_NT40_ENV */
68 #if defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
70 #include <sys/mnttab.h>
71 #include <sys/mntent.h>
77 #if defined(AFS_SGI_ENV)
80 #ifdef AFS_SGI_EFS_IOPS_ENV
81 #define ROOTINO EFS_ROOTINO
82 #include <sys/fs/efs.h>
83 #include "../sgiefs/efs.h" /* until 5.1 release */
88 #ifndef AFS_LINUX20_ENV
89 #include <fstab.h> /* Need to find in libc 5, present in libc 6 */
92 #endif /* AFS_SGI_ENV */
94 #endif /* AFS_HPUX_ENV */
98 #include <netinet/in.h>
102 #include <sys/time.h>
103 #endif /* ITIMER_REAL */
104 #endif /* AFS_NT40_ENV */
105 #if defined(AFS_SUN5_ENV) || defined(AFS_NT40_ENV) || defined(AFS_LINUX20_ENV)
112 #include <afs/errors.h>
115 #include <afs/afssyscalls.h>
118 #include <afs/afsutil.h>
123 #include "partition.h"
124 #ifdef AFS_PTHREAD_ENV
126 #else /* AFS_PTHREAD_ENV */
127 #include "afs/assert.h"
128 #endif /* AFS_PTHREAD_ENV */
131 #if !defined(AFS_NT40_ENV) && !defined(AFS_NAMEI_ENV)
132 #include <afs/osi_inode.h>
138 #ifdef AFS_PTHREAD_ENV
139 pthread_mutex_t vol_glock_mutex;
140 pthread_mutex_t vol_attach_mutex;
141 pthread_cond_t vol_put_volume_cond;
142 pthread_cond_t vol_sleep_cond;
143 #endif /* AFS_PTHREAD_ENV */
146 extern void *calloc(), *realloc();
149 /* Forward declarations */
150 static Volume *attach2();
151 static void FreeVolume();
152 static void VScanUpdateList();
153 static void InitLRU();
154 static int GetVolumeHeader();
155 static void ReleaseVolumeHeader();
156 static void FreeVolumeHeader();
157 static void AddVolumeToHashTable();
158 static void DeleteVolumeFromHashTable();
159 static int VHold(Volume *vp);
160 static int VHold_r(Volume *vp);
161 static void GetBitmap(Error *ec, Volume *vp, VnodeClass class);
162 static void GetVolumePath(Error *ec, VolId volumeId, char **partitionp,
164 static void VReleaseVolumeHandles_r(Volume *vp);
165 static void VCloseVolumeHandles_r(Volume *vp);
167 int LogLevel; /* Vice loglevel--not defined as extern so that it will be
168 defined when not linked with vice, XXXX */
169 ProgramType programType; /* The type of program using the package */
172 #define VOLUME_BITMAP_GROWSIZE 16 /* bytes, => 128vnodes */
173 /* Must be a multiple of 4 (1 word) !!*/
174 #define VOLUME_HASH_TABLE_SIZE 128 /* Must be a power of 2!! */
175 #define VOLUME_HASH(volumeId) (volumeId&(VOLUME_HASH_TABLE_SIZE-1))
176 private Volume *VolumeHashTable[VOLUME_HASH_TABLE_SIZE];
179 /* This macro is used where an ffs() call does not exist. Was in util/ffs.c */
182 afs_int32 ffs_tmp = x; \
183 if (ffs_tmp == 0) return(-1); \
185 for (ffs_i = 1;; ffs_i++) { \
186 if (ffs_tmp & 1) return(ffs_i); \
187 else ffs_tmp >>= 1; \
190 #endif /* !AFS_HAVE_FFS */
192 struct Lock vol_listLock; /* Lock obtained when listing volumes: prevents a volume from being missed if the volume is attached during a list volumes */
194 extern struct Lock FSYNC_handler_lock;
196 Volume *VAttachVolumeByName();
197 Volume *VAttachVolumeByName_r();
199 static int TimeZoneCorrection; /* Number of seconds west of GMT */
201 /* Common message used when the volume goes off line */
202 char *VSalvageMessage =
203 "Files in this volume are currently unavailable; call operations";
205 int VInit; /* 0 - uninitialized,
206 1 - initialized but not all volumes have been attached,
207 2 - initialized and all volumes have been attached,
208 3 - initialized, all volumes have been attached, and
209 VConnectFS() has completed. */
212 int VolumeCacheCheck; /* Incremented everytime a volume goes on line--
213 * used to stamp volume headers and in-core
214 * vnodes. When the volume goes on-line the
215 * vnode will be invalidated */
217 int VolumeCacheSize = 200, VolumeGets=0, VolumeReplacements=0, Vlooks = 0;
220 int VInitVolumePackage(ProgramType pt, int nLargeVnodes, int nSmallVnodes,
221 int connect, int volcache)
223 int errors = 0; /* Number of errors while finding vice partitions. */
229 #ifdef AFS_PTHREAD_ENV
230 assert(pthread_mutex_init(&vol_glock_mutex, NULL) == 0);
231 assert(pthread_mutex_init(&vol_attach_mutex, NULL) == 0);
232 assert(pthread_cond_init(&vol_put_volume_cond, NULL) == 0);
233 assert(pthread_cond_init(&vol_sleep_cond, NULL) == 0);
234 #else /* AFS_PTHREAD_ENV */
236 #endif /* AFS_PTHREAD_ENV */
237 Lock_Init(&vol_listLock);
238 Lock_Init(&FSYNC_handler_lock);
239 srandom(time(0)); /* For VGetVolumeInfo */
240 gettimeofday(&tv, &tz);
241 TimeZoneCorrection = tz.tz_minuteswest*60;
243 /* Ok, we have done enough initialization that fileserver can
244 * start accepting calls, even though the volumes may not be
245 * available just yet.
249 if (programType == fileServer) {
250 /* File server or "stand" */
254 if (volcache > VolumeCacheSize)
255 VolumeCacheSize = volcache;
256 InitLRU(VolumeCacheSize);
258 VInitVnodes(vLarge, nLargeVnodes);
259 VInitVnodes(vSmall, nSmallVnodes);
262 errors = VAttachPartitions();
266 if (programType == fileServer) {
269 struct DiskPartition *diskP;
272 /* Attach all the volumes in this partition */
273 for (diskP = DiskPartitionList; diskP; diskP = diskP->next) {
274 int nAttached = 0, nUnattached = 0;
275 dirp = opendir(VPartitionPath(diskP));
277 while (dp = readdir(dirp)) {
279 p = strrchr(dp->d_name, '.');
280 if (p != NULL && strcmp(p, VHDREXT) == 0) {
283 vp = VAttachVolumeByName(&error, diskP->name, dp->d_name,
285 (*(vp?&nAttached:&nUnattached))++;
286 if (error == VOFFLINE)
287 Log("Volume %u stays offline (/vice/offline/%s exists)\n",
288 VolumeNumber(dp->d_name), dp->d_name);
294 Log("Partition %s: attached %d volumes; %d volumes not attached\n",
295 diskP->name, nAttached, nUnattached);
300 VInit = 2; /* Initialized, and all volumes have been attached */
301 if (programType == volumeUtility && connect) {
303 Log("Unable to connect to file server; aborted\n");
310 /* This must be called by any volume utility which needs to run while the
311 file server is also running. This is separated from VInitVolumePackage so
312 that a utility can fork--and each of the children can independently
313 initialize communication with the file server */
318 retVal = VConnectFS_r();
323 int VConnectFS_r(void)
326 assert(VInit == 2 && programType == volumeUtility);
327 rc = FSYNC_clientInit();
333 void VDisconnectFS_r(void) {
334 assert(programType == volumeUtility);
339 void VDisconnectFS(void) {
345 void VShutdown_r(void)
348 register Volume *vp, *np;
349 register afs_int32 code;
351 Log("VShutdown: shutting down on-line volumes...\n");
352 for (i=0; i<VOLUME_HASH_TABLE_SIZE; i++) {
353 /* try to hold first volume in the hash table */
354 for(vp = VolumeHashTable[i]; vp; vp=vp->hashNext) {
356 if (code == 0) break; /* got it */
357 /* otherwise we go around again, trying another volume */
360 /* first compute np before releasing vp, in case vp disappears
361 * after releasing. Hold it, so it doesn't disapear. If we
362 * can't hold it, try the next one in the chain. Invariant
363 * at the top of this loop is that vp is held (has extra ref count).
365 for(np=vp->hashNext; np; np=np->hashNext) {
367 if (code == 0) break; /* got it */
369 /* next, take the volume offline (drops reference count) */
370 VOffline_r(vp, "File server was shut down");
371 vp = np; /* next guy to try */
374 Log("VShutdown: complete.\n");
385 static void ReadHeader(Error *ec, IHandle_t *h, char *to, int size,
386 int magic, int version)
388 struct versionStamp *vsn;
398 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
400 FDH_REALLYCLOSE(fdP);
403 vsn = (struct versionStamp *) to;
404 if (FDH_READ(fdP, to, size) != size || vsn->magic != magic) {
406 FDH_REALLYCLOSE(fdP);
411 /* Check is conditional, in case caller wants to inspect version himself */
412 if (version && vsn->version != version) {
417 /* VolumeHeaderToDisk
418 * Allows for storing 64 bit inode numbers in on-disk volume header
421 void VolumeHeaderToDisk(VolumeDiskHeader_t *dh, VolumeHeader_t *h)
424 bzero((char*)dh, sizeof(VolumeDiskHeader_t));
425 dh->stamp = h->stamp;
427 dh->parent = h->parent;
429 #ifdef AFS_64BIT_IOPS_ENV
430 dh->volumeInfo_lo = (afs_int32) h->volumeInfo & 0xffffffff;
431 dh->volumeInfo_hi = (afs_int32) (h->volumeInfo >> 32) & 0xffffffff;
432 dh->smallVnodeIndex_lo = (afs_int32) h->smallVnodeIndex & 0xffffffff;
433 dh->smallVnodeIndex_hi = (afs_int32) (h->smallVnodeIndex >> 32) & 0xffffffff;
434 dh->largeVnodeIndex_lo = (afs_int32) h->largeVnodeIndex & 0xffffffff;
435 dh->largeVnodeIndex_hi = (afs_int32) (h->largeVnodeIndex >> 32) & 0xffffffff;
436 dh->linkTable_lo = (afs_int32) h->linkTable & 0xffffffff;
437 dh->linkTable_hi = (afs_int32) (h->linkTable >> 32) & 0xffffffff;
439 dh->volumeInfo_lo = h->volumeInfo;
440 dh->smallVnodeIndex_lo = h->smallVnodeIndex;
441 dh->largeVnodeIndex_lo = h->largeVnodeIndex;
442 dh->linkTable_lo = h->linkTable;
446 /* DiskToVolumeHeader
447 * Reads volume header file from disk, convering 64 bit inodes
448 * if required. Makes the assumption that AFS has *always*
449 * zero'd the volume header file so that high parts of inode
450 * numbers are 0 in older (SGI EFS) volume header files.
452 void DiskToVolumeHeader(VolumeHeader_t *h, VolumeDiskHeader_t *dh)
454 bzero((char*)h, sizeof(VolumeHeader_t));
455 h->stamp = dh->stamp;
457 h->parent = dh->parent;
459 #ifdef AFS_64BIT_IOPS_ENV
460 h->volumeInfo = dh->volumeInfo_lo | ((Inode)dh->volumeInfo_hi << 32);
462 h->smallVnodeIndex = dh->smallVnodeIndex_lo |
463 ((Inode)dh->smallVnodeIndex_hi << 32);
465 h->largeVnodeIndex = dh->largeVnodeIndex_lo |
466 ((Inode)dh->largeVnodeIndex_hi << 32);
467 h->linkTable = dh->linkTable_lo |
468 ((Inode)dh->linkTable_hi << 32);
470 h->volumeInfo = dh->volumeInfo_lo;
471 h->smallVnodeIndex = dh->smallVnodeIndex_lo;
472 h->largeVnodeIndex = dh->largeVnodeIndex_lo;
473 h->linkTable = dh->linkTable_lo;
478 void WriteVolumeHeader_r(ec, vp)
482 IHandle_t *h = V_diskDataHandle(vp);
492 if (FDH_SEEK(fdP, 0, SEEK_SET) < 0) {
494 FDH_REALLYCLOSE(fdP);
497 if (FDH_WRITE(fdP, (char*)&V_disk(vp), sizeof(V_disk(vp)))
498 != sizeof(V_disk(vp))) {
500 FDH_REALLYCLOSE(fdP);
506 /* Attach an existing volume, given its pathname, and return a
507 pointer to the volume header information. The volume also
508 normally goes online at this time. An offline volume
509 must be reattached to make it go online */
511 VAttachVolumeByName(ec, partition, name, mode)
520 retVal = VAttachVolumeByName_r(ec, partition, name, mode);
527 VAttachVolumeByName_r(ec, partition, name, mode)
536 struct VolumeDiskHeader diskHeader;
537 struct VolumeHeader iheader;
538 struct DiskPartition *partp;
542 if (programType == volumeUtility) {
544 VLockPartition_r(partition);
546 if (programType == fileServer) {
547 vp = VGetVolume_r(ec, VolumeNumber(name));
551 if (vp->specialStatus == VBUSY)
553 VDetachVolume_r(ec, vp);
557 if (!(partp = VGetPartition_r(partition, 0))) {
563 strcpy(path, VPartitionPath(partp));
567 if ((fd = open(path, O_RDONLY)) == -1 || fstat(fd,&status) == -1) {
573 n = read(fd, &diskHeader, sizeof (diskHeader));
576 if (n != sizeof (diskHeader) || diskHeader.stamp.magic != VOLUMEHEADERMAGIC) {
577 Log("VAttachVolume: Error reading volume header %s\n", path);
581 if (diskHeader.stamp.version != VOLUMEHEADERVERSION) {
582 Log("VAttachVolume: Volume %s, version number is incorrect; volume needs salvaged\n",path);
587 DiskToVolumeHeader(&iheader, &diskHeader);
588 if (programType == volumeUtility && mode != V_SECRETLY) {
589 if (FSYNC_askfs(iheader.id, partition, FSYNC_NEEDVOLUME, mode)
591 Log("VAttachVolume: attach of volume %u apparently denied by file server\n",
593 *ec = VNOVOL; /* XXXX */
598 vp = attach2(ec, path, &iheader, partp, isbusy);
599 if (programType == volumeUtility && vp) {
600 /* duplicate computation in fssync.c about whether the server
601 * takes the volume offline or not. If the volume isn't
602 * offline, we must not return it when we detach the volume,
603 * or the server will abort */
604 if (mode == V_READONLY || (!VolumeWriteable(vp) && (mode==V_CLONE || mode==V_DUMP)))
605 vp->needsPutBack = 0;
607 vp->needsPutBack = 1;
609 /* OK, there's a problem here, but one that I don't know how to
610 * fix right now, and that I don't think should arise often.
611 * Basically, we should only put back this volume to the server if
612 * it was given to us by the server, but since we don't have a vp,
613 * we can't run the VolumeWriteable function to find out as we do
614 * above when computing vp->needsPutBack. So we send it back, but
615 * there's a path in VAttachVolume on the server which may abort
616 * if this volume doesn't have a header. Should be pretty rare
617 * for all of that to happen, but if it does, probably the right
618 * fix is for the server to allow the return of readonly volumes
619 * that it doesn't think are really checked out. */
620 if (programType == volumeUtility && vp == NULL && mode != V_SECRETLY) {
621 FSYNC_askfs(iheader.id, partition, FSYNC_ON, 0);
623 else if (programType == fileServer && vp) {
624 V_needsCallback(vp) = 0;
626 if (VInit >= 2 && V_BreakVolumeCallbacks) {
627 Log("VAttachVolume: Volume %u was changed externally; breaking callbacks\n", V_id(vp));
628 (*V_BreakVolumeCallbacks)(V_id(vp));
631 VUpdateVolume_r(ec,vp);
637 if (VolumeWriteable(vp) && V_dontSalvage(vp) == 0) {
638 /* This is a hack: by temporarily settint the incore
639 * dontSalvage flag ON, the volume will be put back on the
640 * Update list (with dontSalvage OFF again). It will then
641 * come back in N minutes with DONT_SALVAGE eventually
642 * set. This is the way that volumes that have never had
643 * it set get it set; or that volumes that have been
644 * offline without DONT SALVAGE having been set also
645 * eventually get it set */
646 V_dontSalvage(vp) = DONT_SALVAGE;
647 VAddToVolumeUpdateList_r(ec,vp);
655 Log("VOnline: volume %u (%s) attached and online\n",
656 V_id(vp), V_name(vp));
659 if (programType == volumeUtility) {
660 VUnlockPartition_r(partition);
668 private Volume *attach2(ec, path, header, partp, isbusy)
671 register struct VolumeHeader *header;
672 struct DiskPartition *partp;
678 vp = (Volume *) calloc(1, sizeof(Volume));
680 vp->specialStatus = (isbusy ? VBUSY : 0);
681 vp->device = partp->device;
682 vp->partition = partp;
683 IH_INIT(vp->vnodeIndex[vLarge].handle, partp->device, header->parent,
684 header->largeVnodeIndex);
685 IH_INIT(vp->vnodeIndex[vSmall].handle, partp->device, header->parent,
686 header->smallVnodeIndex);
687 IH_INIT(vp->diskDataHandle, partp->device, header->parent,
689 IH_INIT(vp->linkHandle, partp->device, header->parent,
691 vp->cacheCheck = ++VolumeCacheCheck;
692 vp->shuttingDown = 0;
693 vp->goingOffline = 0;
698 (void) ReadHeader(ec, V_diskDataHandle(vp),
699 (char *)&V_disk(vp), sizeof(V_disk(vp)),
700 VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
703 struct IndexFileHeader iHead;
705 #if TRANSARC_VOL_STATS
707 * We just read in the diskstuff part of the header. If the detailed
708 * volume stats area has not yet been initialized, we should bzero the
709 * area and mark it as initialized.
711 if (! (V_stat_initialized(vp))) {
712 bzero((char *)(V_stat_area(vp)), VOL_STATS_BYTES);
713 V_stat_initialized(vp) = 1;
715 #endif /* TRANSARC_VOL_STATS */
717 (void) ReadHeader(ec, vp->vnodeIndex[vSmall].handle,
718 (char *)&iHead, sizeof(iHead),
719 SMALLINDEXMAGIC, SMALLINDEXVERSION);
723 struct IndexFileHeader iHead;
725 (void) ReadHeader(ec, vp->vnodeIndex[vLarge].handle,
726 (char *)&iHead, sizeof(iHead),
727 LARGEINDEXMAGIC, LARGEINDEXVERSION);
732 struct versionStamp stamp;
734 (void) ReadHeader(ec, V_linkHandle(vp),
735 (char *)&stamp, sizeof(stamp),
736 LINKTABLEMAGIC, LINKTABLEVERSION);
741 Log("VAttachVolume: Error attaching volume %s; volume needs salvage\n",
746 if (V_needsSalvaged(vp)) {
747 if (vp->specialStatus) vp->specialStatus = 0;
748 Log("VAttachVolume: volume salvage flag is ON for %s; volume needs salvage\n", path);
752 if (programType == fileServer) {
754 if (V_inUse(vp) && VolumeWriteable(vp)) {
755 if (!V_needsSalvaged(vp)) {
756 V_needsSalvaged(vp) = 1;
757 VUpdateVolume_r(ec,vp);
760 Log("VAttachVolume: volume %s needs to be salvaged; not attached.\n", path);
764 #endif /* FAST_RESTART */
765 if (V_destroyMe(vp) == DESTROY_ME) {
767 Log("VAttachVolume: volume %s is junk; it should be destroyed at next salvage\n", path);
773 AddVolumeToHashTable(vp, V_id(vp));
774 vp->nextVnodeUnique = V_uniquifier(vp);
775 vp->vnodeIndex[vSmall].bitmap = vp->vnodeIndex[vLarge].bitmap = NULL;
777 if (programType == fileServer && VolumeWriteable(vp)) {
779 for (i = 0; i<nVNODECLASSES; i++) {
789 #endif /* BITMAP_LATER */
791 if (programType == fileServer) {
792 if (vp->specialStatus) vp->specialStatus = 0;
793 if (V_blessed(vp) && V_inService(vp) && !V_needsSalvaged(vp)) {
795 V_offlineMessage(vp)[0] = '\0';
802 /* Attach an existing volume.
803 The volume also normally goes online at this time.
804 An offline volume must be reattached to make it go online.
808 VAttachVolume(ec,volumeId, mode)
816 retVal = VAttachVolume_r(ec, volumeId, mode);
823 VAttachVolume_r(ec,volumeId, mode)
829 GetVolumePath(ec,volumeId, &part, &name);
833 vp = VGetVolume_r(&error, volumeId);
835 assert(V_inUse(vp) == 0);
836 VDetachVolume_r(ec, vp);
840 return VAttachVolumeByName_r(ec, part, name, mode);
843 /* Increment a reference count to a volume, sans context swaps. Requires
844 * possibly reading the volume header in from the disk, since there's
845 * an invariant in the volume package that nUsers>0 ==> vp->header is valid.
847 * N.B. This call can fail if we can't read in the header!! In this case
848 * we still guarantee we won't context swap, but the ref count won't be
849 * incremented (otherwise we'd violate the invariant).
851 static int VHold_r(register Volume *vp)
855 if (vp->nUsers == 0 && !GetVolumeHeader(vp)) {
856 VolumeReplacements++;
857 ReadHeader(&error, V_diskDataHandle(vp),
858 (char *)&V_disk(vp), sizeof(V_disk(vp)),
859 VOLUMEINFOMAGIC, VOLUMEINFOVERSION);
860 if (error) return error;
866 static int VHold(register Volume *vp)
870 retVal = VHold_r(vp);
875 void VTakeOffline_r(register Volume *vp)
877 assert(vp->nUsers > 0);
878 assert(programType == fileServer);
879 vp->goingOffline = 1;
880 V_needsSalvaged(vp) = 1;
883 void VTakeOffline(register Volume *vp)
890 void VPutVolume_r(register Volume *vp)
892 assert(--vp->nUsers >= 0);
893 if (vp->nUsers == 0) {
894 ReleaseVolumeHeader(vp->header);
895 if (vp->goingOffline) {
897 assert(programType == fileServer);
898 vp->goingOffline = 0;
900 VUpdateVolume_r(&error, vp);
901 VCloseVolumeHandles_r(vp);
903 Log("VOffline: Volume %u (%s) is now offline",
904 V_id(vp), V_name(vp));
905 if (V_offlineMessage(vp)[0])
906 Log(" (%s)", V_offlineMessage(vp));
909 #ifdef AFS_PTHREAD_ENV
910 assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
911 #else /* AFS_PTHREAD_ENV */
912 LWP_NoYieldSignal(VPutVolume);
913 #endif /* AFS_PTHREAD_ENV */
915 if (vp->shuttingDown) {
916 VReleaseVolumeHandles_r(vp);
918 if (programType == fileServer)
919 #ifdef AFS_PTHREAD_ENV
920 assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
921 #else /* AFS_PTHREAD_ENV */
922 LWP_NoYieldSignal(VPutVolume);
923 #endif /* AFS_PTHREAD_ENV */
928 void VPutVolume(register Volume *vp)
935 /* Get a pointer to an attached volume. The pointer is returned regardless
936 of whether or not the volume is in service or on/off line. An error
937 code, however, is returned with an indication of the volume's status */
938 Volume *VGetVolume(ec,volumeId)
944 retVal = VGetVolume_r(ec,volumeId);
949 Volume *VGetVolume_r(ec,volumeId)
954 unsigned short V0=0, V1=0, V2=0, V3=0, V4=0, V5=0, V6=0, V7=0, V8=0, V9=0;
955 unsigned short V10=0, V11=0, V12=0, V13=0, V14=0, V15=0;
960 for (vp = VolumeHashTable[VOLUME_HASH(volumeId)];
961 vp && vp->hashid != volumeId; vp = vp->hashNext)
968 /* Until we have reached an initialization level of 2
969 we don't know whether this volume exists or not.
970 We can't sleep and retry later because before a volume
971 is attached, the caller tries to get it first. Just
972 return VOFFLINE and the caller can choose whether to
973 retry the command or not.*/
984 if (vp->nUsers == 0 && !GetVolumeHeader(vp)) {
986 VolumeReplacements++;
987 ReadHeader(ec, V_diskDataHandle(vp),
988 (char *)&V_disk(vp), sizeof(V_disk(vp)), VOLUMEINFOMAGIC,
992 /* Only log the error if it was a totally unexpected error. Simply
993 a missing inode is likely to be caused by the volume being deleted */
994 if (errno != ENXIO || LogLevel)
995 Log("Volume %u: couldn't reread volume header\n", vp->hashid);
1002 if (vp->shuttingDown) {
1008 if (programType == fileServer) {
1010 if (vp->goingOffline) {
1012 #ifdef AFS_PTHREAD_ENV
1013 pthread_cond_wait(&vol_put_volume_cond, &vol_glock_mutex);
1014 #else /* AFS_PTHREAD_ENV */
1015 LWP_WaitProcess(VPutVolume);
1016 #endif /* AFS_PTHREAD_ENV */
1019 if (vp->specialStatus) {
1021 *ec = vp->specialStatus;
1023 else if (V_inService(vp)==0 || V_blessed(vp)==0) {
1027 else if (V_inUse(vp)==0) {
1038 /* if no error, bump nUsers */
1039 if (vp) vp->nUsers++;
1046 /* For both VForceOffline and VOffline, we close all relevant handles.
1047 * For VOffline, if we re-attach the volume, the files may possible be
1048 * different than before.
1050 static void VReleaseVolumeHandles_r(Volume *vp)
1052 DFlushVolume(V_id(vp));
1053 VReleaseVnodeFiles_r(vp);
1055 /* Too time consuming and unnecessary for the volserver */
1056 if (programType != volumeUtility) {
1057 IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
1058 IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
1059 IH_CONDSYNC(vp->diskDataHandle);
1061 IH_CONDSYNC(vp->linkHandle);
1062 #endif /* AFS_NT40_ENV */
1065 IH_RELEASE(vp->vnodeIndex[vLarge].handle);
1066 IH_RELEASE(vp->vnodeIndex[vSmall].handle);
1067 IH_RELEASE(vp->diskDataHandle);
1068 IH_RELEASE(vp->linkHandle);
1071 /* Force the volume offline, set the salvage flag. No further references to
1072 * the volume through the volume package will be honored. */
1073 void VForceOffline_r(Volume *vp)
1078 strcpy(V_offlineMessage(vp), "Forced offline due to internal error: volume needs to be salvaged");
1079 Log("Volume %u forced offline: it needs salvaging!\n", V_id(vp));
1081 vp->goingOffline = 0;
1082 V_needsSalvaged(vp) = 1;
1083 VUpdateVolume_r(&error, vp);
1084 #ifdef AFS_PTHREAD_ENV
1085 assert(pthread_cond_broadcast(&vol_put_volume_cond) == 0);
1086 #else /* AFS_PTHREAD_ENV */
1087 LWP_NoYieldSignal(VPutVolume);
1088 #endif /* AFS_PTHREAD_ENV */
1090 VReleaseVolumeHandles_r(vp);
1094 void VForceOffline(Volume *vp)
1097 VForceOffline_r(vp);
1101 /* The opposite of VAttachVolume. The volume header is written to disk, with
1102 the inUse bit turned off. A copy of the header is maintained in memory,
1103 however (which is why this is VOffline, not VDetach).
1105 void VOffline_r(Volume *vp, char *message)
1108 VolumeId vid = V_id(vp);
1109 assert(programType != volumeUtility);
1114 if (V_offlineMessage(vp)[0] == '\0')
1115 strncpy(V_offlineMessage(vp),message,
1116 sizeof(V_offlineMessage(vp)));
1117 V_offlineMessage(vp)[sizeof(V_offlineMessage(vp))-1] = '\0';
1118 vp->goingOffline = 1;
1120 vp = VGetVolume_r(&error, vid); /* Wait for it to go offline */
1121 if (vp) /* In case it was reattached... */
1125 void VOffline(Volume *vp, char *message)
1128 VOffline_r(vp, message);
1132 /* For VDetachVolume, we close all cached file descriptors, but keep
1133 * the Inode handles in case we need to read from a busy volume.
1135 static void VCloseVolumeHandles_r(Volume *vp)
1137 DFlushVolume(V_id(vp));
1138 VCloseVnodeFiles_r(vp);
1140 /* Too time consuming and unnecessary for the volserver */
1141 if (programType != volumeUtility) {
1142 IH_CONDSYNC(vp->vnodeIndex[vLarge].handle);
1143 IH_CONDSYNC(vp->vnodeIndex[vSmall].handle);
1144 IH_CONDSYNC(vp->diskDataHandle);
1146 IH_CONDSYNC(vp->linkHandle);
1147 #endif /* AFS_NT40_ENV */
1150 IH_REALLYCLOSE(vp->vnodeIndex[vLarge].handle);
1151 IH_REALLYCLOSE(vp->vnodeIndex[vSmall].handle);
1152 IH_REALLYCLOSE(vp->diskDataHandle);
1153 IH_REALLYCLOSE(vp->linkHandle);
1156 /* This gets used for the most part by utility routines that don't want
1157 * to keep all the volume headers around. Generally, the file server won't
1158 * call this routine, because then the offline message in the volume header
1159 * (or other information) will still be available to clients. For NAMEI, also
1160 * close the file handles.
1162 void VDetachVolume_r(Error *ec, Volume *vp)
1165 struct DiskPartition *tpartp;
1166 int notifyServer, useDone;
1168 *ec = 0; /* always "succeeds" */
1169 if (programType == volumeUtility) {
1170 notifyServer = vp->needsPutBack;
1171 useDone = (V_destroyMe(vp) == DESTROY_ME);
1173 tpartp = vp->partition;
1175 DeleteVolumeFromHashTable(vp);
1176 vp->shuttingDown = 1;
1178 /* Will be detached sometime in the future--this is OK since volume is offline */
1180 if (programType == volumeUtility && notifyServer) {
1181 /* Note: The server is not notified in the case of a bogus volume explicitly to
1182 make it possible to create a volume, do a partial restore, then abort the
1183 operation without ever putting the volume online. This is essential in the
1184 case of a volume move operation between two partitions on the same server. In
1185 that case, there would be two instances of the same volume, one of them bogus,
1186 which the file server would attempt to put on line */
1188 FSYNC_askfs(volume, tpartp->name, FSYNC_DONE, 0); /* don't put online */
1190 FSYNC_askfs(volume, tpartp->name, FSYNC_ON, 0); /* fs can use it again */
1191 /* Dettaching it so break all callbacks on it*/
1192 if (V_BreakVolumeCallbacks) {
1193 Log("volume %u detached; breaking all call backs\n", volume);
1194 (*V_BreakVolumeCallbacks)(volume);
1200 void VDetachVolume(Error *ec, Volume *vp)
1203 VDetachVolume_r(ec, vp);
1208 int VAllocBitmapEntry_r(ec,vp,index)
1211 register struct vnodeIndex *index;
1213 register byte *bp,*ep;
1215 /* This test is probably redundant */
1216 if (!VolumeWriteable(vp)) {
1221 if ((programType == fileServer) && !index->bitmap) {
1224 if (vp->specialStatus == VBUSY) {
1225 if (vp->goingOffline) { /* vos dump waiting for the volume to
1226 go offline. We probably come here
1227 from AddNewReadableResidency */
1231 while (vp->specialStatus == VBUSY)
1232 #ifdef AFS_PTHREAD_ENV
1234 #else /* AFS_PTHREAD_ENV */
1236 #endif /* AFS_PTHREAD_ENV */
1240 if (!index->bitmap) {
1241 vp->specialStatus = VBUSY; /* Stop anyone else from using it.*/
1242 for (i = 0; i<nVNODECLASSES; i++) {
1247 vp->specialStatus = 0;
1248 vp->shuttingDown = 1; /* Let who has it free it. */
1253 vp->specialStatus = 0; /* Allow others to have access. */
1256 #endif /* BITMAP_LATER */
1257 bp = index->bitmap + index->bitmapOffset;
1258 ep = index->bitmap + index->bitmapSize;
1260 if ((*(bit32 *)bp) != 0xffffffff) {
1262 index->bitmapOffset = bp - index->bitmap;
1265 o = ffs(~*bp)-1; /* ffs is documented in BSTRING(3) */
1267 return (bp - index->bitmap)*8 + o;
1269 bp += sizeof(bit32) /* i.e. 4 */;
1271 /* No bit map entry--must grow bitmap */
1273 realloc(index->bitmap, index->bitmapSize+VOLUME_BITMAP_GROWSIZE);
1276 bp += index->bitmapSize;
1277 bzero(bp, VOLUME_BITMAP_GROWSIZE);
1278 index->bitmapOffset = index->bitmapSize;
1279 index->bitmapSize += VOLUME_BITMAP_GROWSIZE;
1281 return index->bitmapOffset*8;
1284 int VAllocBitmapEntry(ec,vp,index)
1287 register struct vnodeIndex *index;
1291 retVal = VAllocBitmapEntry_r(ec,vp,index);
1296 void VFreeBitMapEntry_r(Error *ec, register struct vnodeIndex *index,
1299 unsigned int offset;
1302 if (!index->bitmap) return;
1303 #endif /* BITMAP_LATER */
1304 offset = bitNumber>>3;
1305 if (offset >= index->bitmapSize) {
1309 if (offset < index->bitmapOffset)
1310 index->bitmapOffset = offset&~3; /* Truncate to nearest bit32 */
1311 *(index->bitmap + offset) &= ~(1 << (bitNumber & 0x7));
1314 void VFreeBitMapEntry(Error *ec, register struct vnodeIndex *index,
1318 VFreeBitMapEntry_r(ec, index, bitNumber);
1322 void VUpdateVolume_r(Error *ec,Volume *vp)
1325 if (programType == fileServer)
1326 V_uniquifier(vp) = (V_inUse(vp)? V_nextVnodeUnique(vp) + 200: V_nextVnodeUnique(vp));
1327 /*printf("Writing volume header for '%s'\n", V_name(vp));*/
1328 WriteVolumeHeader_r(ec, vp);
1331 "VUpdateVolume: error updating volume header, volume %u (%s)\n",
1332 V_id(vp), V_name(vp));
1333 VForceOffline_r(vp);
1337 void VUpdateVolume(Error *ec, Volume *vp)
1340 VUpdateVolume_r(ec, vp);
1344 void VSyncVolume_r(Error *ec, Volume *vp)
1347 VUpdateVolume_r(ec, vp);
1350 fdP = IH_OPEN(V_diskDataHandle(vp));
1351 assert(fdP != NULL);
1352 code = FDH_SYNC(fdP);
1358 void VSyncVolume(Error *ec, Volume *vp)
1361 VSyncVolume_r(ec, vp);
1365 static void FreeVolume(vp)
1371 for (i = 0; i<nVNODECLASSES; i++)
1372 if (vp->vnodeIndex[i].bitmap)
1373 free(vp->vnodeIndex[i].bitmap);
1374 FreeVolumeHeader(vp);
1375 DeleteVolumeFromHashTable(vp);
1379 static void GetBitmap(Error *ec, Volume *vp, VnodeClass class)
1381 StreamHandle_t *file;
1385 struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
1386 struct vnodeIndex *vip = &vp->vnodeIndex[class];
1387 struct VnodeDiskObject *vnode;
1388 unsigned int unique = 0;
1392 #endif /* BITMAP_LATER */
1396 fdP = IH_OPEN(vip->handle);
1397 assert (fdP != NULL);
1398 file = FDH_FDOPEN(fdP, "r");
1399 assert (file != NULL);
1400 vnode = (VnodeDiskObject *) malloc(vcp->diskSize);
1401 assert(vnode != NULL);
1402 size = OS_SIZE(fdP->fd_fd);
1404 nVnodes = (size <= vcp->diskSize? 0: size-vcp->diskSize)
1406 vip->bitmapSize = ((nVnodes/8)+10)/4*4; /* The 10 is a little extra so
1407 a few files can be created in this volume,
1408 the whole thing is rounded up to nearest 4
1409 bytes, because the bit map allocator likes
1412 BitMap = (byte *) calloc(1, vip->bitmapSize);
1413 assert(BitMap != NULL);
1414 #else /* BITMAP_LATER */
1415 vip->bitmap = (byte *) calloc(1, vip->bitmapSize);
1416 assert(vip->bitmap != NULL);
1417 vip->bitmapOffset = 0;
1418 #endif /* BITMAP_LATER */
1419 if (STREAM_SEEK(file,vcp->diskSize,0) != -1) {
1421 for (bitNumber = 0; bitNumber < nVnodes+100; bitNumber++) {
1422 if (STREAM_READ(vnode, vcp->diskSize, 1, file) != 1)
1424 if (vnode->type != vNull) {
1425 if (vnode->vnodeMagic != vcp->magic) {
1426 Log("GetBitmap: addled vnode index in volume %s; volume needs salvage\n",
1432 *(BitMap + (bitNumber>>3)) |= (1 << (bitNumber & 0x7));
1433 #else /* BITMAP_LATER */
1434 *(vip->bitmap + (bitNumber>>3)) |= (1 << (bitNumber & 0x7));
1435 #endif /* BITMAP_LATER */
1436 if (unique <= vnode->uniquifier)
1437 unique = vnode->uniquifier + 1;
1439 #ifndef AFS_PTHREAD_ENV
1440 if ((bitNumber & 0x00ff) == 0x0ff) { /* every 256 iterations */
1443 #endif /* !AFS_PTHREAD_ENV */
1446 if (vp->nextVnodeUnique < unique) {
1447 Log("GetBitmap: bad volume uniquifier for volume %s; volume needs salvage\n", V_name(vp));
1450 /* Paranoia, partly justified--I think fclose after fdopen
1451 * doesn't seem to close fd. In any event, the documentation
1452 * doesn't specify, so it's safer to close it twice.
1458 /* There may have been a racing condition with some other thread, both
1459 * creating the bitmaps for this volume. If the other thread was faster
1460 * the pointer to bitmap should already be filled and we can free ours.
1462 if (vip->bitmap == NULL) {
1463 vip->bitmap = BitMap;
1464 vip->bitmapOffset = 0;
1466 free((byte *)BitMap);
1467 #endif /* BITMAP_LATER */
1470 static void GetVolumePath(Error *ec, VolId volumeId, char **partitionp,
1473 static char partition[VMAXPATHLEN], name[VMAXPATHLEN];
1474 char path[VMAXPATHLEN];
1476 struct DiskPartition *dp;
1480 sprintf(&name[1],VFORMAT,volumeId);
1481 for (dp = DiskPartitionList; dp; dp = dp->next) {
1483 strcpy(path, VPartitionPath(dp));
1485 if (stat(path,&status) == 0) {
1486 strcpy(partition, dp->name);
1493 *partitionp = *namep = NULL;
1496 *partitionp = partition;
1506 return atoi(name+1);
1509 char *VolumeExternalName(volumeId)
1512 static char name[15];
1513 sprintf(name,VFORMAT,volumeId);
1517 #if TRANSARC_VOL_STATS
1518 #define OneDay (86400) /* 24 hours' worth of seconds */
1520 #define OneDay (24*60*60) /* 24 hours */
1521 #endif /* TRANSARC_VOL_STATS */
1523 #define Midnight(date) ((date-TimeZoneCorrection)/OneDay*OneDay+TimeZoneCorrection)
1525 /*------------------------------------------------------------------------
1526 * [export] VAdjustVolumeStatistics
1529 * If we've passed midnight, we need to update all the day use
1530 * statistics as well as zeroing the detailed volume statistics
1531 * (if we are implementing them).
1534 * vp : Pointer to the volume structure describing the lucky
1535 * volume being considered for update.
1541 * Nothing interesting.
1545 *------------------------------------------------------------------------*/
1547 VAdjustVolumeStatistics_r(vp)
1548 register Volume *vp;
1550 { /*VAdjustVolumeStatistics*/
1552 unsigned int now = FT_ApproxTime();
1554 if (now - V_dayUseDate(vp) > OneDay) {
1557 ndays = (now - V_dayUseDate(vp)) / OneDay;
1558 for (i = 6; i>ndays-1; i--)
1559 V_weekUse(vp)[i] = V_weekUse(vp)[i-ndays];
1560 for (i = 0; i<ndays-1 && i<7; i++)
1561 V_weekUse(vp)[i] = 0;
1563 V_weekUse(vp)[ndays-1] = V_dayUse(vp);
1565 V_dayUseDate(vp) = Midnight(now);
1567 #if TRANSARC_VOL_STATS
1569 * All we need to do is bzero the entire VOL_STATS_BYTES of
1570 * the detailed volume statistics area.
1572 bzero((char *)(V_stat_area(vp)), VOL_STATS_BYTES);
1573 #endif /* TRANSARC_VOL_STATS */
1574 } /*It's been more than a day of collection*/
1576 #if TRANSARC_VOL_STATS
1578 * Always return happily.
1581 #endif /* TRANSARC_VOL_STATS */
1583 } /*VAdjustVolumeStatistics*/
1585 VAdjustVolumeStatistics(vp)
1586 register Volume *vp;
1590 VAdjustVolumeStatistics_r(vp);
1595 void VBumpVolumeUsage_r(register Volume *vp)
1597 unsigned int now = FT_ApproxTime();
1598 if (now - V_dayUseDate(vp) > OneDay)
1599 VAdjustVolumeStatistics_r(vp);
1601 * Save the volume header image to disk after every 128 bumps to dayUse.
1603 if ((V_dayUse(vp)++ & 127) == 0) {
1605 VUpdateVolume_r(&error, vp);
1609 void VBumpVolumeUsage(register Volume *vp)
1612 VBumpVolumeUsage_r(vp);
1616 void VSetDiskUsage_r(void)
1618 static int FifteenMinuteCounter = 0;
1621 /* NOTE: Don't attempt to access the partitions list until the
1622 initialization level indicates that all volumes are attached,
1623 which implies that all partitions are initialized. */
1624 #ifdef AFS_PTHREAD_ENV
1626 #else /* AFS_PTHREAD_ENV */
1628 #endif /* AFS_PTHREAD_ENV */
1631 VResetDiskUsage_r();
1632 if (++FifteenMinuteCounter == 3) {
1633 FifteenMinuteCounter = 0;
1638 void VSetDiskUsage(void)
1645 /* The number of minutes that a volume hasn't been updated before the
1646 * "Dont salvage" flag in the volume header will be turned on */
1647 #define SALVAGE_INTERVAL (10*60)
1649 static VolumeId *UpdateList; /* Pointer to array of Volume ID's */
1650 static int nUpdatedVolumes; /* Updated with entry in UpdateList, salvage after crash flag on */
1651 static int updateSize; /* number of entries possible */
1652 #define UPDATE_LIST_SIZE 100 /* size increment */
1654 void VAddToVolumeUpdateList_r(Error *ec, Volume *vp)
1657 vp->updateTime = FT_ApproxTime();
1658 if (V_dontSalvage(vp) == 0)
1660 V_dontSalvage(vp) = 0;
1661 VSyncVolume_r(ec, vp);
1665 updateSize = UPDATE_LIST_SIZE;
1666 UpdateList = (VolumeId *) malloc(sizeof (VolumeId) * updateSize);
1668 if (nUpdatedVolumes == updateSize) {
1669 updateSize += UPDATE_LIST_SIZE;
1670 UpdateList = (VolumeId *) realloc(UpdateList, sizeof (VolumeId) * updateSize);
1673 UpdateList[nUpdatedVolumes++] = V_id(vp);
1676 static void VScanUpdateList() {
1677 register int i, gap;
1678 register Volume *vp;
1680 afs_int32 now = FT_ApproxTime();
1681 /* Be careful with this code, since it works with interleaved calls to AddToVolumeUpdateList */
1682 for (i = gap = 0; i<nUpdatedVolumes; i++) {
1683 vp = VGetVolume_r(&error, UpdateList[i-gap] = UpdateList[i]);
1686 } else if (vp->nUsers == 1 && now - vp->updateTime > SALVAGE_INTERVAL) {
1687 V_dontSalvage(vp) = DONT_SALVAGE;
1688 VUpdateVolume_r(&error, vp); /* No need to fsync--not critical */
1693 #ifndef AFS_PTHREAD_ENV
1695 #endif /* !AFS_PTHREAD_ENV */
1697 nUpdatedVolumes -= gap;
1700 /***************************************************/
1701 /* Add on routines to manage a volume header cache */
1702 /***************************************************/
1704 static struct volHeader *volumeLRU;
1706 /* Allocate a bunch of headers; string them together */
1707 static void InitLRU(howMany)
1710 register struct volHeader *hp;
1711 if (programType != fileServer)
1713 hp = (struct volHeader *)(calloc(howMany, sizeof(struct volHeader)));
1715 ReleaseVolumeHeader(hp++);
1718 /* Get a volume header from the LRU list; update the old one if necessary */
1719 /* Returns 1 if there was already a header, which is removed from the LRU list */
1720 static int GetVolumeHeader(vp)
1721 register Volume *vp;
1724 register struct volHeader *hd;
1726 static int everLogged = 0;
1728 old = (vp->header != 0); /* old == volume already has a header */
1729 if (programType != fileServer) {
1731 hd = (struct volHeader *) calloc(1, sizeof(*vp->header));
1740 if (volumeLRU == hd)
1741 volumeLRU = hd->next;
1742 assert(hd->back == vp);
1746 hd = volumeLRU->prev; /* not currently in use and least recently used */
1748 hd = (struct volHeader *) calloc(1, sizeof(*vp->header));
1749 hd->prev = hd->next = hd; /* make it look like single elt LRU */
1751 Log("****Allocated more volume headers, probably leak****\n");
1756 if (hd->diskstuff.inUse) {
1757 WriteVolumeHeader_r(&error, hd->back);
1758 /* Ignore errors; catch them later */
1760 hd->back->header = 0;
1765 if (hd->next) { /* hd->next != 0 --> in LRU chain (we zero it later) */
1766 hd->prev->next = hd->next; /* pull hd out of LRU list */
1767 hd->next->prev = hd->prev; /* if hd only element, this is noop */
1769 hd->next = hd->prev = 0;
1770 /* if not in LRU chain, next test won't be true */
1771 if (hd == volumeLRU) /* last header item, turn into empty list */
1772 volumeLRU = (struct volHeader *) 0;
1777 /* Put it at the top of the LRU chain */
1778 static void ReleaseVolumeHeader(hd)
1779 register struct volHeader *hd;
1781 if (programType != fileServer)
1783 if (!hd || hd->next) /* no header, or header already released */
1786 hd->next = hd->prev = hd;
1788 hd->prev = volumeLRU->prev;
1789 hd->next = volumeLRU;
1790 hd->prev->next = hd->next->prev = hd;
1795 static void FreeVolumeHeader(vp)
1796 register Volume *vp;
1798 register struct volHeader *hd = vp->header;
1801 if (programType == fileServer) {
1802 ReleaseVolumeHeader(hd);
1812 /***************************************************/
1813 /* Routines to add volume to hash chain, delete it */
1814 /***************************************************/
1816 static void AddVolumeToHashTable(vp, hashid)
1817 register Volume *vp;
1819 int hash = VOLUME_HASH(hashid);
1820 vp->hashid = hashid;
1821 vp->hashNext = VolumeHashTable[hash];
1822 VolumeHashTable[hash] = vp;
1823 vp->vnodeHashOffset = VolumeHashOffset_r();
1826 static void DeleteVolumeFromHashTable(vp)
1827 register Volume *vp;
1829 int hash = VOLUME_HASH(vp->hashid);
1830 if (VolumeHashTable[hash] == vp)
1831 VolumeHashTable[hash] = vp->hashNext;
1833 Volume *tvp = VolumeHashTable[hash];
1836 while (tvp->hashNext && tvp->hashNext != vp)
1837 tvp = tvp->hashNext;
1838 if (tvp->hashNext == NULL)
1840 tvp->hashNext = vp->hashNext;
1845 void VPrintCacheStats_r(void)
1847 register struct VnodeClassInfo *vcp;
1848 vcp = &VnodeClassInfo[vLarge];
1849 Log("Large vnode cache, %d entries, %d allocs, %d gets (%d reads), %d writes\n",
1850 vcp->cacheSize, vcp->allocs, vcp->gets, vcp->reads, vcp->writes);
1851 vcp = &VnodeClassInfo[vSmall];
1852 Log("Small vnode cache,%d entries, %d allocs, %d gets (%d reads), %d writes\n",
1853 vcp->cacheSize, vcp->allocs, vcp->gets, vcp->reads, vcp->writes);
1854 Log("Volume header cache, %d entries, %d gets, %d replacements\n",
1855 VolumeCacheSize, VolumeGets, VolumeReplacements);
1858 void VPrintCacheStats(void)
1861 VPrintCacheStats_r();