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
13 Institution: The Information Technology Center, Carnegie-Mellon University
16 #include <afsconfig.h>
17 #include <afs/param.h>
23 #ifdef AFS_PTHREAD_ENV
25 #else /* AFS_PTHREAD_ENV */
26 #include <afs/assert.h>
27 #endif /* AFS_PTHREAD_ENV */
30 #include <afs/afsint.h>
32 #include <afs/errors.h>
35 #include <afs/afssyscalls.h>
38 #include "partition.h"
40 #if defined(AFS_SGI_ENV)
41 #include "sys/types.h"
53 #include <sys/fcntl.h>
56 #endif /* AFS_NT40_ENV */
60 struct VnodeClassInfo VnodeClassInfo[nVNODECLASSES];
62 private int moveHash();
63 void StickOnLruChain_r();
66 #define BAD_IGET -1000
68 /* There are two separate vnode queue types defined here:
69 * Each hash conflict chain -- is singly linked, with a single head
70 * pointer. New entries are added at the beginning. Old
71 * entries are removed by linear search, which generally
72 * only occurs after a disk read).
73 * LRU chain -- is doubly linked, single head pointer.
74 * Entries are added at the head, reclaimed from the tail,
75 * or removed from anywhere in the queue.
79 /* Vnode hash table. Find hash chain by taking lower bits of
80 * (volume_hash_offset + vnode).
81 * This distributes the root inodes of the volumes over the
82 * hash table entries and also distributes the vnodes of
83 * volumes reasonably fairly. The volume_hash_offset field
84 * for each volume is established as the volume comes on line
85 * by using the VOLUME_HASH_OFFSET macro. This distributes the
86 * volumes fairly among the cache entries, both when servicing
87 * a small number of volumes and when servicing a large number.
90 /* logging stuff for finding bugs */
91 #define THELOGSIZE 5120
92 static afs_int32 theLog[THELOGSIZE];
93 static afs_int32 vnLogPtr=0;
94 VNLog(aop, anparms, av1, av2, av3, av4)
95 afs_int32 aop, anparms;
96 afs_int32 av1, av2, av3,av4; {
97 register afs_int32 temp;
100 /* copy data to array */
105 if (anparms>4) anparms = 4; /* do bounds checking */
107 temp = (aop<<16) | anparms;
108 theLog[vnLogPtr++] = temp;
109 if (vnLogPtr >= THELOGSIZE) vnLogPtr = 0;
110 for(temp=0;temp<anparms;temp++) {
111 theLog[vnLogPtr++] = data[temp];
112 if (vnLogPtr >= THELOGSIZE) vnLogPtr = 0;
116 /* VolumeHashOffset -- returns a new value to be stored in the
117 * volumeHashOffset of a Volume structure. Called when a
118 * volume is initialized. Sets the volumeHashOffset so that
119 * vnode cache entries are distributed reasonably between
120 * volumes (the root vnodes of the volumes will hash to
121 * different values, and spacing is maintained between volumes
122 * when there are not many volumes represented), and spread
123 * equally amongst vnodes within a single volume.
125 int VolumeHashOffset_r() {
126 static int nextVolumeHashOffset = 0;
127 /* hashindex Must be power of two in size */
129 # define hashMask ((1<<hashShift)-1)
130 static byte hashindex[1<<hashShift] = {0,128,64,192,32,160,96,224};
132 offset = hashindex[nextVolumeHashOffset&hashMask]
133 + (nextVolumeHashOffset>>hashShift);
134 nextVolumeHashOffset++;
138 /* Change hashindex (above) if you change this constant */
139 #define VNODE_HASH_TABLE_SIZE 256
140 private Vnode *VnodeHashTable[VNODE_HASH_TABLE_SIZE];
141 #define VNODE_HASH(volumeptr,vnodenumber)\
142 ((volumeptr->vnodeHashOffset + vnodenumber)&(VNODE_HASH_TABLE_SIZE-1))
144 /* Code to invalidate a vnode entry. Called when we've damaged a vnode, and want
145 to prevent future VGetVnode's from applying to it. Leaves it in the same hash bucket
146 but that shouldn't be important. */
147 VInvalidateVnode_r(avnode)
148 register struct Vnode *avnode; {
149 avnode->changed_newTime = 0; /* don't let it get flushed out again */
150 avnode->changed_oldTime = 0;
151 avnode->delete = 0; /* it isn't deleted, erally */
152 avnode->cacheCheck = 0; /* invalid: prevents future vnode searches from working */
155 /* Not normally called by general client; called by volume.c */
156 VInitVnodes(class,nVnodes)
161 register struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
163 vcp->allocs = vcp->gets = vcp->reads = vcp->writes = 0;
164 vcp->cacheSize = nVnodes;
167 assert(CHECKSIZE_SMALLVNODE);
169 vcp->residentSize = SIZEOF_SMALLVNODE;
170 vcp->diskSize = SIZEOF_SMALLDISKVNODE;
171 vcp->magic = SMALLVNODEMAGIC;
175 vcp->residentSize = SIZEOF_LARGEVNODE;
176 vcp->diskSize = SIZEOF_LARGEDISKVNODE;
177 vcp->magic = LARGEVNODEMAGIC;
180 { int s = vcp->diskSize-1;
190 va = (byte *) calloc(nVnodes,vcp->residentSize);
193 Vnode *vnp = (Vnode *) va;
194 vnp->nUsers = 0; /* no context switches */
195 Lock_Init(&vnp->lock);
196 vnp->changed_oldTime = 0;
197 vnp->changed_newTime = 0;
198 vnp->volumePtr = NULL;
200 vnp->delete = vnp->vnodeNumber = 0;
201 #ifdef AFS_PTHREAD_ENV
202 vnp->writer = (pthread_t) 0;
203 #else /* AFS_PTHREAD_ENV */
204 vnp->writer = (PROCESS) 0;
205 #endif /* AFS_PTHREAD_ENV */
208 if (vcp->lruHead == NULL)
209 vcp->lruHead = vnp->lruNext = vnp->lruPrev = vnp;
211 vnp->lruNext = vcp->lruHead;
212 vnp->lruPrev = vcp->lruHead->lruPrev;
213 vcp->lruHead->lruPrev = vnp;
214 vnp->lruPrev->lruNext = vnp;
217 va += vcp->residentSize;
223 /* allocate an *unused* vnode from the LRU chain, going backwards of course. It shouldn't
224 be necessary to specify that nUsers == 0 since if it is in the list, nUsers
225 should be 0. Things shouldn't be in lruq unless no one is using them. */
226 Vnode *VGetFreeVnode_r(vcp)
227 struct VnodeClassInfo *vcp; {
230 vnp = vcp->lruHead->lruPrev;
231 if (vnp->nUsers != 0 || CheckLock(&vnp->lock))
232 Abort("locked vnode in lruq");
233 VNLog(1, 2, vnp->vnodeNumber, (afs_int32) vnp);
234 IH_RELEASE(vnp->handle);
239 static mlkLastAlloc = 0;
240 static mlkLastOver = 0;
241 static mlkLastDelete = 0;
243 Vnode *VAllocVnode(ec,vp,type)
250 retVal = VAllocVnode_r(ec, vp, type);
255 Vnode *VAllocVnode_r(ec,vp,type)
262 int newHash, bitNumber;
263 register struct VnodeClassInfo *vcp;
268 if (programType == fileServer && !V_inUse(vp)) {
269 if (vp->specialStatus) {
270 *ec = vp->specialStatus;
276 class = vnodeTypeToClass(type);
277 vcp = &VnodeClassInfo[class];
279 if (!VolumeWriteable(vp)) {
284 unique = vp->nextVnodeUnique++;
286 unique = vp->nextVnodeUnique++;
288 if (vp->nextVnodeUnique > V_uniquifier(vp)) {
289 VUpdateVolume_r(ec,vp);
294 if (programType == fileServer) {
295 VAddToVolumeUpdateList_r(ec, vp);
300 /* Find a slot in the bit map */
301 bitNumber = VAllocBitmapEntry_r(ec,vp,&vp->vnodeIndex[class]);
304 vnodeNumber = bitNumberToVnodeNumber(bitNumber,class);
306 VNLog(2, 1, vnodeNumber);
307 /* Prepare to move it to the new hash chain */
308 newHash = VNODE_HASH(vp, vnodeNumber);
309 for (vnp = VnodeHashTable[newHash];
310 vnp && (vnp->vnodeNumber!=vnodeNumber || vnp->volumePtr!=vp
311 || vnp->volumePtr->cacheCheck!=vnp->cacheCheck);
315 /* slot already exists. May even not be in lruq (consider store file locking a file being deleted)
316 so we may have to wait for it below */
317 VNLog(3, 2, vnodeNumber, (afs_int32) vnp);
319 /* If first user, remove it from the LRU chain. We can assume that
320 there is at least one item in the queue */
321 if (++vnp->nUsers == 1) {
322 if (vnp == vcp->lruHead)
323 vcp->lruHead = vcp->lruHead->lruNext;
324 vnp->lruPrev->lruNext = vnp->lruNext;
325 vnp->lruNext->lruPrev = vnp->lruPrev;
326 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
327 Abort("VGetVnode: lru chain addled!\n");
328 /* This won't block */
329 ObtainWriteLock(&vnp->lock);
331 /* follow locking hierarchy */
333 ObtainWriteLock(&vnp->lock);
336 #ifdef AFS_PTHREAD_ENV
337 vnp->writer = pthread_self();
338 #else /* AFS_PTHREAD_ENV */
339 LWP_CurrentProcess(&vnp->writer);
340 #endif /* AFS_PTHREAD_ENV */
343 vnp = VGetFreeVnode_r(vcp);
344 /* Remove vnode from LRU chain and grab a write lock */
345 if (vnp == vcp->lruHead)
346 vcp->lruHead = vcp->lruHead->lruNext;
347 vnp->lruPrev->lruNext = vnp->lruNext;
348 vnp->lruNext->lruPrev = vnp->lruPrev;
349 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
350 Abort("VGetVnode: lru chain addled!\n");
351 /* Initialize the header fields so noone allocates another
352 vnode with the same number */
353 vnp->vnodeNumber = vnodeNumber;
355 vnp->cacheCheck = vp->cacheCheck;
357 moveHash(vnp, newHash);
358 /* This will never block */
359 ObtainWriteLock(&vnp->lock);
360 #ifdef AFS_PTHREAD_ENV
361 vnp->writer = pthread_self();
362 #else /* AFS_PTHREAD_ENV */
363 LWP_CurrentProcess(&vnp->writer);
364 #endif /* AFS_PTHREAD_ENV */
365 /* Sanity check: is this vnode really not in use? */
368 IHandle_t *ihP = vp->vnodeIndex[class].handle;
370 off_t off = vnodeIndexOffset(vcp, vnodeNumber);
375 Abort("VAllocVnode: can't open index file!\n");
376 if ((size = FDH_SIZE(fdP)) < 0)
377 Abort("VAllocVnode: can't stat index file!\n");
378 if (FDH_SEEK(fdP, off, SEEK_SET) < 0)
379 Abort("VAllocVnode: can't seek on index file!\n");
381 if (FDH_READ(fdP, &vnp->disk, vcp->diskSize) == vcp->diskSize) {
382 if (vnp->disk.type != vNull)
383 Abort("VAllocVnode: addled bitmap or index!\n");
386 /* growing file - grow in a reasonable increment */
387 char *buf = (char *)malloc(16*1024);
388 memset(buf, 0, 16*1024);
389 FDH_WRITE(fdP, buf, 16*1024);
395 VNLog(4, 2, vnodeNumber, (afs_int32) vnp);
398 VNLog(5, 1, (afs_int32) vnp);
399 #ifdef AFS_PTHREAD_ENV
400 vnp->writer = pthread_self();
401 #else /* AFS_PTHREAD_ENV */
402 LWP_CurrentProcess(&vnp->writer);
403 #endif /* AFS_PTHREAD_ENV */
404 memset(&vnp->disk, 0, sizeof(vnp->disk));
405 vnp->changed_newTime = 0; /* set this bit when vnode is updated */
406 vnp->changed_oldTime = 0; /* set this on CopyOnWrite. */
408 vnp->disk.vnodeMagic = vcp->magic;
409 vnp->disk.type = type;
410 vnp->disk.uniquifier = unique;
416 Vnode *VGetVnode(ec,vp,vnodeNumber,locktype)
420 int locktype; /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
424 retVal = VGetVnode_r(ec, vp, vnodeNumber, locktype);
429 Vnode *VGetVnode_r(ec,vp,vnodeNumber,locktype)
433 int locktype; /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
438 struct VnodeClassInfo *vcp;
441 mlkReason = 0; /* last call didn't fail */
443 if (vnodeNumber == 0) {
449 VNLog(100, 1, vnodeNumber);
450 if (programType == fileServer && !V_inUse(vp)) {
451 *ec = (vp->specialStatus ? vp->specialStatus : VOFFLINE);
453 /* If the volume is VBUSY (being cloned or dumped) and this is
454 * a READ operation, then don't fail.
456 if ((*ec != VBUSY) || (locktype != READ_LOCK)) {
462 class = vnodeIdToClass(vnodeNumber);
463 vcp = &VnodeClassInfo[class];
464 if (locktype == WRITE_LOCK && !VolumeWriteable(vp)) {
470 if (locktype == WRITE_LOCK && programType == fileServer) {
471 VAddToVolumeUpdateList_r(ec, vp);
473 mlkReason = 1000 + *ec;
478 /* See whether the vnode is in the cache. */
479 newHash = VNODE_HASH(vp, vnodeNumber);
480 for (vnp = VnodeHashTable[newHash];
481 vnp && (vnp->vnodeNumber!=vnodeNumber || vnp->volumePtr!=vp
482 || vnp->volumePtr->cacheCheck!=vnp->cacheCheck);
488 IHandle_t *ihP = vp->vnodeIndex[class].handle;
490 /* Not in cache; tentatively grab most distantly used one from the LRU
493 vnp = VGetFreeVnode_r(vcp);
494 /* Remove it from the old hash chain */
495 moveHash(vnp, newHash);
496 /* Remove it from the LRU chain */
497 if (vnp == vcp->lruHead)
498 vcp->lruHead = vcp->lruHead->lruNext;
499 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
500 Abort("VGetVnode: lru chain addled!\n");
501 vnp->lruPrev->lruNext = vnp->lruNext;
502 vnp->lruNext->lruPrev = vnp->lruPrev;
504 vnp->changed_newTime = vnp->changed_oldTime = 0;
506 vnp->vnodeNumber = vnodeNumber;
508 vnp->cacheCheck = vp->cacheCheck;
511 /* This will never block */
512 ObtainWriteLock(&vnp->lock);
513 #ifdef AFS_PTHREAD_ENV
514 vnp->writer = pthread_self();
515 #else /* AFS_PTHREAD_ENV */
516 LWP_CurrentProcess(&vnp->writer);
517 #endif /* AFS_PTHREAD_ENV */
519 /* Read vnode from volume index */
523 Log("VGetVnode: can't open index dev=%d, i=%s\n",
524 vp->device, PrintInode(NULL,
525 vp->vnodeIndex[class].handle->ih_ino));
529 else if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, vnodeNumber),
531 Log ("VGetVnode: can't seek on index file vn=%d\n",vnodeNumber);
534 FDH_REALLYCLOSE(fdP);
536 else if ((n = FDH_READ(fdP, (char*)&vnp->disk, vcp->diskSize))
538 /* Don't take volume off line if the inumber is out of range
539 or the inode table is full. */
540 FDH_REALLYCLOSE(fdP);
543 Log("VGetVnode: bad inumber %s\n",
544 PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
548 /* Check for disk errors. Anything else just means that the vnode
550 if (n == -1 && errno == EIO) {
551 Log("VGetVnode: Couldn't read vnode %d, volume %u (%s); volume needs salvage\n",
552 vnodeNumber, V_id(vp), V_name(vp));
560 VInvalidateVnode_r(vnp);
561 if (vnp->nUsers-- == 1)
562 StickOnLruChain_r(vnp,vcp);
563 ReleaseWriteLock(&vnp->lock);
568 /* Quick check to see that the data is reasonable */
569 if (vnp->disk.vnodeMagic != vcp->magic || vnp->disk.type == vNull) {
570 if (vnp->disk.type == vNull) {
573 VInvalidateVnode_r(vnp);
574 if (vnp->nUsers-- == 1)
575 StickOnLruChain_r(vnp,vcp);
576 ReleaseWriteLock(&vnp->lock);
577 return NULL; /* The vnode is not allocated */
580 struct vnodeIndex *index = &vp->vnodeIndex[class];
581 int bitNumber = vnodeIdToBitNumber(vnodeNumber);
582 int offset = bitNumber >> 3;
584 /* Test to see if vnode number is valid. */
585 if ((offset >= index->bitmapSize) ||
586 ((*(index->bitmap+offset) & (1<<(bitNumber&0x7))) == 0)) {
587 Log("VGetVnode: Request for unallocated vnode %u, volume %u (%s) denied.\n",
588 vnodeNumber, V_id(vp), V_name(vp));
593 Log("VGetVnode: Bad magic number, vnode %d, volume %u (%s); volume needs salvage\n",
594 vnodeNumber, V_id(vp), V_name(vp));
595 vp->goingOffline = 1; /* used to call VOffline, but that would mess
596 up the volume ref count if called here */
600 VInvalidateVnode_r(vnp);
601 if (vnp->nUsers-- == 1)
602 StickOnLruChain_r(vnp,vcp);
603 ReleaseWriteLock(&vnp->lock);
607 IH_INIT(vnp->handle, V_device(vp), V_parentId(vp), VN_GET_INO(vnp));
608 ReleaseWriteLock(&vnp->lock);
610 VNLog(101, 2, vnodeNumber, (afs_int32) vnp);
611 if (++vnp->nUsers == 1) {
612 /* First user. Remove it from the LRU chain. We can assume that
613 there is at least one item in the queue */
614 if (vnp == vcp->lruHead)
615 vcp->lruHead = vcp->lruHead->lruNext;
616 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
617 Abort("VGetVnode: lru chain addled!\n");
618 vnp->lruPrev->lruNext = vnp->lruNext;
619 vnp->lruNext->lruPrev = vnp->lruPrev;
623 if (locktype == READ_LOCK)
624 ObtainReadLock(&vnp->lock);
626 ObtainWriteLock(&vnp->lock);
627 #ifdef AFS_PTHREAD_ENV
628 vnp->writer = pthread_self();
629 #else /* AFS_PTHREAD_ENV */
630 LWP_CurrentProcess(&vnp->writer);
631 #endif /* AFS_PTHREAD_ENV */
634 /* Check that the vnode hasn't been removed while we were obtaining
636 VNLog(102, 2, vnodeNumber, (afs_int32) vnp);
637 if (vnp->disk.type == vNull) {
638 if (vnp->nUsers-- == 1)
639 StickOnLruChain_r(vnp,vcp);
640 if (locktype == READ_LOCK)
641 ReleaseReadLock(&vnp->lock);
643 ReleaseWriteLock(&vnp->lock);
646 /* vnode is labelled correctly by now, so we don't have to invalidate it */
649 if (programType == fileServer)
650 VBumpVolumeUsage_r(vnp->volumePtr);/* Hack; don't know where it should be
651 called from. Maybe VGetVolume */
656 int TrustVnodeCacheEntry = 1;
657 /* This variable is bogus--when it's set to 0, the hash chains fill
658 up with multiple versions of the same vnode. Should fix this!! */
674 int writeLocked, offset;
676 struct VnodeClassInfo *vcp;
680 assert (vnp->nUsers != 0);
681 class = vnodeIdToClass(vnp->vnodeNumber);
682 vcp = &VnodeClassInfo[class];
683 assert(vnp->disk.vnodeMagic == vcp->magic);
684 VNLog(200, 2, vnp->vnodeNumber, (afs_int32) vnp);
686 writeLocked = WriteLocked(&vnp->lock);
688 #ifdef AFS_PTHREAD_ENV
689 pthread_t thisProcess = pthread_self();
690 #else /* AFS_PTHREAD_ENV */
692 LWP_CurrentProcess(&thisProcess);
693 #endif /* AFS_PTHREAD_ENV */
694 VNLog(201, 2, (afs_int32) vnp,
695 ((vnp->changed_newTime) << 1) | ((vnp->changed_oldTime) << 1) | vnp->delete);
696 if (thisProcess != vnp->writer)
697 Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",vnp);
698 if (vnp->changed_oldTime || vnp->changed_newTime || vnp->delete) {
699 Volume *vp = vnp->volumePtr;
700 afs_int32 now = FT_ApproxTime();
701 assert(vnp->cacheCheck == vp->cacheCheck);
704 /* No longer any directory entries for this vnode. Free the Vnode */
705 memset(&vnp->disk, 0, sizeof (vnp->disk));
706 mlkLastDelete = vnp->vnodeNumber;
707 /* delete flag turned off further down */
708 VNLog(202, 2, vnp->vnodeNumber, (afs_int32) vnp);
709 } else if (vnp->changed_newTime) {
710 vnp->disk.serverModifyTime = now;
712 if (vnp->changed_newTime)
713 V_updateDate(vp) = vp->updateTime = now;
715 /* The vnode has been changed. Write it out to disk */
717 assert(V_needsSalvaged(vp));
720 IHandle_t *ihP = vp->vnodeIndex[class].handle;
725 Abort("VPutVnode: can't open index file!\n");
726 offset = vnodeIndexOffset(vcp, vnp->vnodeNumber);
727 if (FDH_SEEK(fdP, offset, SEEK_SET) < 0) {
728 Abort("VPutVnode: can't seek on index file! fdp=0x%x offset=%d, errno=%d\n",
731 code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
732 if (code != vcp->diskSize) {
733 /* Don't force volume offline if the inumber is out of
734 * range or the inode table is full.
737 if (code == BAD_IGET) {
738 Log("VPutVnode: bad inumber %s\n",
739 PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
742 Log("VPutVnode: Couldn't write vnode %d, volume %u (%s)\n",
743 vnp->vnodeNumber, V_id(vnp->volumePtr),
744 V_name(vnp->volumePtr));
749 FDH_REALLYCLOSE(fdP);
755 /* If the vnode is to be deleted, and we wrote the vnode out,
756 * free its bitmap entry. Do after the vnode is written so we
757 * don't allocate from bitmap before the vnode is written
758 * (doing so could cause a "addled bitmap" message).
760 if (vnp->delete && !*ec) {
761 VFreeBitMapEntry_r(ec, &vp->vnodeIndex[class],
762 vnodeIdToBitNumber(vnp->vnodeNumber));
766 vnp->changed_newTime = vnp->changed_oldTime = 0;
768 } else { /* Not write locked */
769 if (vnp->changed_newTime || vnp->changed_oldTime || vnp->delete)
770 Abort("VPutVnode: Change or delete flag for vnode 0x%x is set but vnode is not write locked!\n", vnp);
773 /* Do not look at disk portion of vnode after this point; it may
774 have been deleted above */
775 if (vnp->nUsers-- == 1)
776 StickOnLruChain_r(vnp,vcp);
780 ReleaseWriteLock(&vnp->lock);
782 ReleaseReadLock(&vnp->lock);
786 * Make an attempt to convert a vnode lock from write to read.
787 * Do nothing if the vnode isn't write locked or the vnode has
790 int VVnodeWriteToRead(ec,vnp)
796 retVal = VVnodeWriteToRead_r(ec, vnp);
801 int VVnodeWriteToRead_r(ec,vnp)
807 struct VnodeClassInfo *vcp;
809 #ifdef AFS_PTHREAD_ENV
810 pthread_t thisProcess;
811 #else /* AFS_PTHREAD_ENV */
813 #endif /* AFS_PTHREAD_ENV */
816 assert (vnp->nUsers != 0);
817 class = vnodeIdToClass(vnp->vnodeNumber);
818 vcp = &VnodeClassInfo[class];
819 assert(vnp->disk.vnodeMagic == vcp->magic);
820 writeLocked = WriteLocked(&vnp->lock);
821 VNLog(300, 2, vnp->vnodeNumber, (afs_int32) vnp);
827 #ifdef AFS_PTHREAD_ENV
828 thisProcess = pthread_self();
829 #else /* AFS_PTHREAD_ENV */
830 LWP_CurrentProcess(&thisProcess);
831 #endif /* AFS_PTHREAD_ENV */
833 VNLog(301, 2, (afs_int32) vnp,
834 ((vnp->changed_newTime) << 1) | ((vnp->changed_oldTime) << 1) |
836 if (thisProcess != vnp->writer)
837 Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",vnp);
841 if (vnp->changed_oldTime || vnp->changed_newTime) {
842 Volume *vp = vnp->volumePtr;
843 afs_int32 now = FT_ApproxTime();
844 assert(vnp->cacheCheck == vp->cacheCheck);
845 if (vnp->changed_newTime)
846 vnp->disk.serverModifyTime = now;
847 if (vnp->changed_newTime)
848 V_updateDate(vp) = vp->updateTime = now;
850 /* The inode has been changed. Write it out to disk */
852 assert(V_needsSalvaged(vp));
855 IHandle_t *ihP = vp->vnodeIndex[class].handle;
857 off_t off = vnodeIndexOffset(vcp, vnp->vnodeNumber);
861 Abort("VPutVnode: can't open index file!\n");
862 code = FDH_SEEK(fdP, off, SEEK_SET);
864 Abort("VPutVnode: can't seek on index file!\n");
865 code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
866 if (code != vcp->diskSize) {
868 * Don't force volume offline if the inumber is out of
869 * range or the inode table is full.
874 Log("VPutVnode: bad inumber %d\n",
875 vp->vnodeIndex[class].handle->ih_ino);
878 Log("VPutVnode: Couldn't write vnode %d, volume %u (%s)\n",
879 vnp->vnodeNumber, V_id(vnp->volumePtr),
880 V_name(vnp->volumePtr));
890 vnp->changed_newTime = vnp->changed_oldTime = 0;
893 ConvertWriteToReadLock(&vnp->lock);
897 /* Move the vnode, vnp, to the new hash table given by the
898 hash table index, newHash */
899 static int moveHash(vnp, newHash)
904 /* Remove it from the old hash chain */
905 tvnp = VnodeHashTable[vnp->hashIndex];
907 VnodeHashTable[vnp->hashIndex] = vnp->hashNext;
909 while (tvnp && tvnp->hashNext != vnp)
910 tvnp = tvnp->hashNext;
912 tvnp->hashNext = vnp->hashNext;
914 /* Add it to the new hash chain */
915 vnp->hashNext = VnodeHashTable[newHash];
916 VnodeHashTable[newHash] = vnp;
917 vnp->hashIndex = newHash;
922 StickOnLruChain_r(vnp,vcp)
924 register struct VnodeClassInfo *vcp;
926 /* Add it to the circular LRU list */
927 if (vcp->lruHead == NULL)
928 Abort("VPutVnode: vcp->lruHead==NULL");
930 vnp->lruNext = vcp->lruHead;
931 vnp->lruPrev = vcp->lruHead->lruPrev;
932 vcp->lruHead->lruPrev = vnp;
933 vnp->lruPrev->lruNext = vnp;
936 /* If the vnode was just deleted, put it at the end of the chain so it
937 will be reused immediately */
939 vcp->lruHead = vnp->lruNext;
940 /* If caching is turned off, set volumeptr to NULL to invalidate the
942 if (!TrustVnodeCacheEntry)
943 vnp->volumePtr = NULL;
946 /* VCloseVnodeFiles - called when a volume is going off line. All open
947 * files for vnodes in that volume are closed. This might be excessive,
948 * since we may only be taking one volume of a volume group offline.
950 void VCloseVnodeFiles_r(Volume *vp)
955 for (i=0; i<VNODE_HASH_TABLE_SIZE; i++) {
956 for (vnp = VnodeHashTable[i]; vnp; vnp = vnp->hashNext) {
957 if (vnp->volumePtr == vp) {
958 IH_REALLYCLOSE(vnp->handle);
964 /* VReleaseVnodeFiles - called when a volume is going detached. All open
965 * files for vnodes in that volume are closed and all inode handles
966 * for vnodes in that volume are released.
968 void VReleaseVnodeFiles_r(Volume *vp)
973 for (i=0; i<VNODE_HASH_TABLE_SIZE; i++) {
974 for (vnp = VnodeHashTable[i]; vnp; vnp = vnp->hashNext) {
975 if (vnp->volumePtr == vp) {
976 IH_RELEASE(vnp->handle);