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>
30 #ifdef AFS_PTHREAD_ENV
32 #else /* AFS_PTHREAD_ENV */
33 #include <afs/assert.h>
34 #endif /* AFS_PTHREAD_ENV */
37 #include <afs/afsint.h>
39 #include <afs/errors.h>
42 #include <afs/afssyscalls.h>
45 #include "partition.h"
47 #if defined(AFS_SGI_ENV)
48 #include "sys/types.h"
60 #include <sys/fcntl.h>
63 #endif /* AFS_NT40_ENV */
67 struct VnodeClassInfo VnodeClassInfo[nVNODECLASSES];
69 private int moveHash();
70 void StickOnLruChain_r();
73 #define BAD_IGET -1000
75 /* There are two separate vnode queue types defined here:
76 * Each hash conflict chain -- is singly linked, with a single head
77 * pointer. New entries are added at the beginning. Old
78 * entries are removed by linear search, which generally
79 * only occurs after a disk read).
80 * LRU chain -- is doubly linked, single head pointer.
81 * Entries are added at the head, reclaimed from the tail,
82 * or removed from anywhere in the queue.
86 /* Vnode hash table. Find hash chain by taking lower bits of
87 * (volume_hash_offset + vnode).
88 * This distributes the root inodes of the volumes over the
89 * hash table entries and also distributes the vnodes of
90 * volumes reasonably fairly. The volume_hash_offset field
91 * for each volume is established as the volume comes on line
92 * by using the VOLUME_HASH_OFFSET macro. This distributes the
93 * volumes fairly among the cache entries, both when servicing
94 * a small number of volumes and when servicing a large number.
97 /* logging stuff for finding bugs */
98 #define THELOGSIZE 5120
99 static afs_int32 theLog[THELOGSIZE];
100 static afs_int32 vnLogPtr=0;
101 VNLog(aop, anparms, av1, av2, av3, av4)
102 afs_int32 aop, anparms;
103 afs_int32 av1, av2, av3,av4; {
104 register afs_int32 temp;
107 /* copy data to array */
112 if (anparms>4) anparms = 4; /* do bounds checking */
114 temp = (aop<<16) | anparms;
115 theLog[vnLogPtr++] = temp;
116 if (vnLogPtr >= THELOGSIZE) vnLogPtr = 0;
117 for(temp=0;temp<anparms;temp++) {
118 theLog[vnLogPtr++] = data[temp];
119 if (vnLogPtr >= THELOGSIZE) vnLogPtr = 0;
123 /* VolumeHashOffset -- returns a new value to be stored in the
124 * volumeHashOffset of a Volume structure. Called when a
125 * volume is initialized. Sets the volumeHashOffset so that
126 * vnode cache entries are distributed reasonably between
127 * volumes (the root vnodes of the volumes will hash to
128 * different values, and spacing is maintained between volumes
129 * when there are not many volumes represented), and spread
130 * equally amongst vnodes within a single volume.
132 int VolumeHashOffset_r() {
133 static int nextVolumeHashOffset = 0;
134 /* hashindex Must be power of two in size */
136 # define hashMask ((1<<hashShift)-1)
137 static byte hashindex[1<<hashShift] = {0,128,64,192,32,160,96,224};
139 offset = hashindex[nextVolumeHashOffset&hashMask]
140 + (nextVolumeHashOffset>>hashShift);
141 nextVolumeHashOffset++;
145 /* Change hashindex (above) if you change this constant */
146 #define VNODE_HASH_TABLE_SIZE 256
147 private Vnode *VnodeHashTable[VNODE_HASH_TABLE_SIZE];
148 #define VNODE_HASH(volumeptr,vnodenumber)\
149 ((volumeptr->vnodeHashOffset + vnodenumber)&(VNODE_HASH_TABLE_SIZE-1))
151 /* Code to invalidate a vnode entry. Called when we've damaged a vnode, and want
152 to prevent future VGetVnode's from applying to it. Leaves it in the same hash bucket
153 but that shouldn't be important. */
154 VInvalidateVnode_r(avnode)
155 register struct Vnode *avnode; {
156 avnode->changed_newTime = 0; /* don't let it get flushed out again */
157 avnode->changed_oldTime = 0;
158 avnode->delete = 0; /* it isn't deleted, erally */
159 avnode->cacheCheck = 0; /* invalid: prevents future vnode searches from working */
162 /* Not normally called by general client; called by volume.c */
163 VInitVnodes(class,nVnodes)
168 register struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
170 vcp->allocs = vcp->gets = vcp->reads = vcp->writes = 0;
171 vcp->cacheSize = nVnodes;
174 assert(CHECKSIZE_SMALLVNODE);
176 vcp->residentSize = SIZEOF_SMALLVNODE;
177 vcp->diskSize = SIZEOF_SMALLDISKVNODE;
178 vcp->magic = SMALLVNODEMAGIC;
182 vcp->residentSize = SIZEOF_LARGEVNODE;
183 vcp->diskSize = SIZEOF_LARGEDISKVNODE;
184 vcp->magic = LARGEVNODEMAGIC;
187 { int s = vcp->diskSize-1;
197 va = (byte *) calloc(nVnodes,vcp->residentSize);
200 Vnode *vnp = (Vnode *) va;
201 vnp->nUsers = 0; /* no context switches */
202 Lock_Init(&vnp->lock);
203 vnp->changed_oldTime = 0;
204 vnp->changed_newTime = 0;
205 vnp->volumePtr = NULL;
207 vnp->delete = vnp->vnodeNumber = 0;
208 #ifdef AFS_PTHREAD_ENV
209 vnp->writer = (pthread_t) 0;
210 #else /* AFS_PTHREAD_ENV */
211 vnp->writer = (PROCESS) 0;
212 #endif /* AFS_PTHREAD_ENV */
215 if (vcp->lruHead == NULL)
216 vcp->lruHead = vnp->lruNext = vnp->lruPrev = vnp;
218 vnp->lruNext = vcp->lruHead;
219 vnp->lruPrev = vcp->lruHead->lruPrev;
220 vcp->lruHead->lruPrev = vnp;
221 vnp->lruPrev->lruNext = vnp;
224 va += vcp->residentSize;
230 /* allocate an *unused* vnode from the LRU chain, going backwards of course. It shouldn't
231 be necessary to specify that nUsers == 0 since if it is in the list, nUsers
232 should be 0. Things shouldn't be in lruq unless no one is using them. */
233 Vnode *VGetFreeVnode_r(vcp)
234 struct VnodeClassInfo *vcp; {
237 vnp = vcp->lruHead->lruPrev;
238 if (vnp->nUsers != 0 || CheckLock(&vnp->lock))
239 Abort("locked vnode in lruq");
240 VNLog(1, 2, vnp->vnodeNumber, (afs_int32) vnp);
241 IH_RELEASE(vnp->handle);
246 static mlkLastAlloc = 0;
247 static mlkLastOver = 0;
248 static mlkLastDelete = 0;
250 Vnode *VAllocVnode(ec,vp,type)
257 retVal = VAllocVnode_r(ec, vp, type);
262 Vnode *VAllocVnode_r(ec,vp,type)
269 int newHash, bitNumber;
270 register struct VnodeClassInfo *vcp;
275 if (programType == fileServer && !V_inUse(vp)) {
276 if (vp->specialStatus) {
277 *ec = vp->specialStatus;
283 class = vnodeTypeToClass(type);
284 vcp = &VnodeClassInfo[class];
286 if (!VolumeWriteable(vp)) {
291 unique = vp->nextVnodeUnique++;
293 unique = vp->nextVnodeUnique++;
295 if (vp->nextVnodeUnique > V_uniquifier(vp)) {
296 VUpdateVolume_r(ec,vp);
301 if (programType == fileServer) {
302 VAddToVolumeUpdateList_r(ec, vp);
307 /* Find a slot in the bit map */
308 bitNumber = VAllocBitmapEntry_r(ec,vp,&vp->vnodeIndex[class]);
311 vnodeNumber = bitNumberToVnodeNumber(bitNumber,class);
313 VNLog(2, 1, vnodeNumber);
314 /* Prepare to move it to the new hash chain */
315 newHash = VNODE_HASH(vp, vnodeNumber);
316 for (vnp = VnodeHashTable[newHash];
317 vnp && (vnp->vnodeNumber!=vnodeNumber || vnp->volumePtr!=vp
318 || vnp->volumePtr->cacheCheck!=vnp->cacheCheck);
322 /* slot already exists. May even not be in lruq (consider store file locking a file being deleted)
323 so we may have to wait for it below */
324 VNLog(3, 2, vnodeNumber, (afs_int32) vnp);
326 /* If first user, remove it from the LRU chain. We can assume that
327 there is at least one item in the queue */
328 if (++vnp->nUsers == 1) {
329 if (vnp == vcp->lruHead)
330 vcp->lruHead = vcp->lruHead->lruNext;
331 vnp->lruPrev->lruNext = vnp->lruNext;
332 vnp->lruNext->lruPrev = vnp->lruPrev;
333 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
334 Abort("VGetVnode: lru chain addled!\n");
335 /* This won't block */
336 ObtainWriteLock(&vnp->lock);
338 /* follow locking hierarchy */
340 ObtainWriteLock(&vnp->lock);
343 #ifdef AFS_PTHREAD_ENV
344 vnp->writer = pthread_self();
345 #else /* AFS_PTHREAD_ENV */
346 LWP_CurrentProcess(&vnp->writer);
347 #endif /* AFS_PTHREAD_ENV */
350 vnp = VGetFreeVnode_r(vcp);
351 /* Remove vnode from LRU chain and grab a write lock */
352 if (vnp == vcp->lruHead)
353 vcp->lruHead = vcp->lruHead->lruNext;
354 vnp->lruPrev->lruNext = vnp->lruNext;
355 vnp->lruNext->lruPrev = vnp->lruPrev;
356 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
357 Abort("VGetVnode: lru chain addled!\n");
358 /* Initialize the header fields so noone allocates another
359 vnode with the same number */
360 vnp->vnodeNumber = vnodeNumber;
362 vnp->cacheCheck = vp->cacheCheck;
364 moveHash(vnp, newHash);
365 /* This will never block */
366 ObtainWriteLock(&vnp->lock);
367 #ifdef AFS_PTHREAD_ENV
368 vnp->writer = pthread_self();
369 #else /* AFS_PTHREAD_ENV */
370 LWP_CurrentProcess(&vnp->writer);
371 #endif /* AFS_PTHREAD_ENV */
372 /* Sanity check: is this vnode really not in use? */
375 IHandle_t *ihP = vp->vnodeIndex[class].handle;
377 off_t off = vnodeIndexOffset(vcp, vnodeNumber);
382 Abort("VAllocVnode: can't open index file!\n");
383 if ((size = FDH_SIZE(fdP)) < 0)
384 Abort("VAllocVnode: can't stat index file!\n");
385 if (FDH_SEEK(fdP, off, SEEK_SET) < 0)
386 Abort("VAllocVnode: can't seek on index file!\n");
388 if (FDH_READ(fdP, &vnp->disk, vcp->diskSize) == vcp->diskSize) {
389 if (vnp->disk.type != vNull)
390 Abort("VAllocVnode: addled bitmap or index!\n");
393 /* growing file - grow in a reasonable increment */
394 char *buf = (char *)malloc(16*1024);
395 if (!buf) Abort("VAllocVnode: malloc failed\n");
396 memset(buf, 0, 16*1024);
397 FDH_WRITE(fdP, buf, 16*1024);
403 VNLog(4, 2, vnodeNumber, (afs_int32) vnp);
406 VNLog(5, 1, (afs_int32) vnp);
407 #ifdef AFS_PTHREAD_ENV
408 vnp->writer = pthread_self();
409 #else /* AFS_PTHREAD_ENV */
410 LWP_CurrentProcess(&vnp->writer);
411 #endif /* AFS_PTHREAD_ENV */
412 memset(&vnp->disk, 0, sizeof(vnp->disk));
413 vnp->changed_newTime = 0; /* set this bit when vnode is updated */
414 vnp->changed_oldTime = 0; /* set this on CopyOnWrite. */
416 vnp->disk.vnodeMagic = vcp->magic;
417 vnp->disk.type = type;
418 vnp->disk.uniquifier = unique;
424 Vnode *VGetVnode(ec,vp,vnodeNumber,locktype)
428 int locktype; /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
432 retVal = VGetVnode_r(ec, vp, vnodeNumber, locktype);
437 Vnode *VGetVnode_r(ec,vp,vnodeNumber,locktype)
441 int locktype; /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
446 struct VnodeClassInfo *vcp;
449 mlkReason = 0; /* last call didn't fail */
451 if (vnodeNumber == 0) {
457 VNLog(100, 1, vnodeNumber);
458 if (programType == fileServer && !V_inUse(vp)) {
459 *ec = (vp->specialStatus ? vp->specialStatus : VOFFLINE);
461 /* If the volume is VBUSY (being cloned or dumped) and this is
462 * a READ operation, then don't fail.
464 if ((*ec != VBUSY) || (locktype != READ_LOCK)) {
470 class = vnodeIdToClass(vnodeNumber);
471 vcp = &VnodeClassInfo[class];
472 if (locktype == WRITE_LOCK && !VolumeWriteable(vp)) {
478 if (locktype == WRITE_LOCK && programType == fileServer) {
479 VAddToVolumeUpdateList_r(ec, vp);
481 mlkReason = 1000 + *ec;
486 /* See whether the vnode is in the cache. */
487 newHash = VNODE_HASH(vp, vnodeNumber);
488 for (vnp = VnodeHashTable[newHash];
489 vnp && (vnp->vnodeNumber!=vnodeNumber || vnp->volumePtr!=vp
490 || vnp->volumePtr->cacheCheck!=vnp->cacheCheck);
496 IHandle_t *ihP = vp->vnodeIndex[class].handle;
498 /* Not in cache; tentatively grab most distantly used one from the LRU
501 vnp = VGetFreeVnode_r(vcp);
502 /* Remove it from the old hash chain */
503 moveHash(vnp, newHash);
504 /* Remove it from the LRU chain */
505 if (vnp == vcp->lruHead)
506 vcp->lruHead = vcp->lruHead->lruNext;
507 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
508 Abort("VGetVnode: lru chain addled!\n");
509 vnp->lruPrev->lruNext = vnp->lruNext;
510 vnp->lruNext->lruPrev = vnp->lruPrev;
512 vnp->changed_newTime = vnp->changed_oldTime = 0;
514 vnp->vnodeNumber = vnodeNumber;
516 vnp->cacheCheck = vp->cacheCheck;
519 /* This will never block */
520 ObtainWriteLock(&vnp->lock);
521 #ifdef AFS_PTHREAD_ENV
522 vnp->writer = pthread_self();
523 #else /* AFS_PTHREAD_ENV */
524 LWP_CurrentProcess(&vnp->writer);
525 #endif /* AFS_PTHREAD_ENV */
527 /* Read vnode from volume index */
531 Log("VGetVnode: can't open index dev=%d, i=%s\n",
532 vp->device, PrintInode(NULL,
533 vp->vnodeIndex[class].handle->ih_ino));
537 else if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, vnodeNumber),
539 Log ("VGetVnode: can't seek on index file vn=%d\n",vnodeNumber);
542 FDH_REALLYCLOSE(fdP);
544 else if ((n = FDH_READ(fdP, (char*)&vnp->disk, vcp->diskSize))
546 /* Don't take volume off line if the inumber is out of range
547 or the inode table is full. */
548 FDH_REALLYCLOSE(fdP);
551 Log("VGetVnode: bad inumber %s\n",
552 PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
556 /* Check for disk errors. Anything else just means that the vnode
558 if (n == -1 && errno == EIO) {
559 Log("VGetVnode: Couldn't read vnode %d, volume %u (%s); volume needs salvage\n",
560 vnodeNumber, V_id(vp), V_name(vp));
568 VInvalidateVnode_r(vnp);
569 if (vnp->nUsers-- == 1)
570 StickOnLruChain_r(vnp,vcp);
571 ReleaseWriteLock(&vnp->lock);
576 /* Quick check to see that the data is reasonable */
577 if (vnp->disk.vnodeMagic != vcp->magic || vnp->disk.type == vNull) {
578 if (vnp->disk.type == vNull) {
581 VInvalidateVnode_r(vnp);
582 if (vnp->nUsers-- == 1)
583 StickOnLruChain_r(vnp,vcp);
584 ReleaseWriteLock(&vnp->lock);
585 return NULL; /* The vnode is not allocated */
588 struct vnodeIndex *index = &vp->vnodeIndex[class];
589 int bitNumber = vnodeIdToBitNumber(vnodeNumber);
590 int offset = bitNumber >> 3;
592 /* Test to see if vnode number is valid. */
593 if ((offset >= index->bitmapSize) ||
594 ((*(index->bitmap+offset) & (1<<(bitNumber&0x7))) == 0)) {
595 Log("VGetVnode: Request for unallocated vnode %u, volume %u (%s) denied.\n",
596 vnodeNumber, V_id(vp), V_name(vp));
601 Log("VGetVnode: Bad magic number, vnode %d, volume %u (%s); volume needs salvage\n",
602 vnodeNumber, V_id(vp), V_name(vp));
603 vp->goingOffline = 1; /* used to call VOffline, but that would mess
604 up the volume ref count if called here */
608 VInvalidateVnode_r(vnp);
609 if (vnp->nUsers-- == 1)
610 StickOnLruChain_r(vnp,vcp);
611 ReleaseWriteLock(&vnp->lock);
615 IH_INIT(vnp->handle, V_device(vp), V_parentId(vp), VN_GET_INO(vnp));
616 ReleaseWriteLock(&vnp->lock);
618 VNLog(101, 2, vnodeNumber, (afs_int32) vnp);
619 if (++vnp->nUsers == 1) {
620 /* First user. Remove it from the LRU chain. We can assume that
621 there is at least one item in the queue */
622 if (vnp == vcp->lruHead)
623 vcp->lruHead = vcp->lruHead->lruNext;
624 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
625 Abort("VGetVnode: lru chain addled!\n");
626 vnp->lruPrev->lruNext = vnp->lruNext;
627 vnp->lruNext->lruPrev = vnp->lruPrev;
631 if (locktype == READ_LOCK)
632 ObtainReadLock(&vnp->lock);
634 ObtainWriteLock(&vnp->lock);
635 #ifdef AFS_PTHREAD_ENV
636 vnp->writer = pthread_self();
637 #else /* AFS_PTHREAD_ENV */
638 LWP_CurrentProcess(&vnp->writer);
639 #endif /* AFS_PTHREAD_ENV */
642 /* Check that the vnode hasn't been removed while we were obtaining
644 VNLog(102, 2, vnodeNumber, (afs_int32) vnp);
645 if ((vnp->disk.type == vNull) || (vnp->cacheCheck == 0)){
646 if (vnp->nUsers-- == 1)
647 StickOnLruChain_r(vnp,vcp);
648 if (locktype == READ_LOCK)
649 ReleaseReadLock(&vnp->lock);
651 ReleaseWriteLock(&vnp->lock);
654 /* vnode is labelled correctly by now, so we don't have to invalidate it */
657 if (programType == fileServer)
658 VBumpVolumeUsage_r(vnp->volumePtr);/* Hack; don't know where it should be
659 called from. Maybe VGetVolume */
664 int TrustVnodeCacheEntry = 1;
665 /* This variable is bogus--when it's set to 0, the hash chains fill
666 up with multiple versions of the same vnode. Should fix this!! */
682 int writeLocked, offset;
684 struct VnodeClassInfo *vcp;
688 assert (vnp->nUsers != 0);
689 class = vnodeIdToClass(vnp->vnodeNumber);
690 vcp = &VnodeClassInfo[class];
691 assert(vnp->disk.vnodeMagic == vcp->magic);
692 VNLog(200, 2, vnp->vnodeNumber, (afs_int32) vnp);
694 writeLocked = WriteLocked(&vnp->lock);
696 #ifdef AFS_PTHREAD_ENV
697 pthread_t thisProcess = pthread_self();
698 #else /* AFS_PTHREAD_ENV */
700 LWP_CurrentProcess(&thisProcess);
701 #endif /* AFS_PTHREAD_ENV */
702 VNLog(201, 2, (afs_int32) vnp,
703 ((vnp->changed_newTime) << 1) | ((vnp->changed_oldTime) << 1) | vnp->delete);
704 if (thisProcess != vnp->writer)
705 Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",vnp);
706 if (vnp->changed_oldTime || vnp->changed_newTime || vnp->delete) {
707 Volume *vp = vnp->volumePtr;
708 afs_int32 now = FT_ApproxTime();
709 assert(vnp->cacheCheck == vp->cacheCheck);
712 /* No longer any directory entries for this vnode. Free the Vnode */
713 memset(&vnp->disk, 0, sizeof (vnp->disk));
714 mlkLastDelete = vnp->vnodeNumber;
715 /* delete flag turned off further down */
716 VNLog(202, 2, vnp->vnodeNumber, (afs_int32) vnp);
717 } else if (vnp->changed_newTime) {
718 vnp->disk.serverModifyTime = now;
720 if (vnp->changed_newTime)
721 V_updateDate(vp) = vp->updateTime = now;
723 /* The vnode has been changed. Write it out to disk */
725 assert(V_needsSalvaged(vp));
728 IHandle_t *ihP = vp->vnodeIndex[class].handle;
733 Abort("VPutVnode: can't open index file!\n");
734 offset = vnodeIndexOffset(vcp, vnp->vnodeNumber);
735 if (FDH_SEEK(fdP, offset, SEEK_SET) < 0) {
736 Abort("VPutVnode: can't seek on index file! fdp=0x%x offset=%d, errno=%d\n",
739 code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
740 if (code != vcp->diskSize) {
741 /* Don't force volume offline if the inumber is out of
742 * range or the inode table is full.
745 if (code == BAD_IGET) {
746 Log("VPutVnode: bad inumber %s\n",
747 PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
750 Log("VPutVnode: Couldn't write vnode %d, volume %u (%s) (error %d)\n",
751 vnp->vnodeNumber, V_id(vnp->volumePtr),
752 V_name(vnp->volumePtr), code);
757 FDH_REALLYCLOSE(fdP);
763 /* If the vnode is to be deleted, and we wrote the vnode out,
764 * free its bitmap entry. Do after the vnode is written so we
765 * don't allocate from bitmap before the vnode is written
766 * (doing so could cause a "addled bitmap" message).
768 if (vnp->delete && !*ec) {
769 VFreeBitMapEntry_r(ec, &vp->vnodeIndex[class],
770 vnodeIdToBitNumber(vnp->vnodeNumber));
774 vnp->changed_newTime = vnp->changed_oldTime = 0;
776 } else { /* Not write locked */
777 if (vnp->changed_newTime || vnp->changed_oldTime || vnp->delete)
778 Abort("VPutVnode: Change or delete flag for vnode 0x%x is set but vnode is not write locked!\n", vnp);
781 /* Do not look at disk portion of vnode after this point; it may
782 have been deleted above */
783 if (vnp->nUsers-- == 1)
784 StickOnLruChain_r(vnp,vcp);
788 ReleaseWriteLock(&vnp->lock);
790 ReleaseReadLock(&vnp->lock);
794 * Make an attempt to convert a vnode lock from write to read.
795 * Do nothing if the vnode isn't write locked or the vnode has
798 int VVnodeWriteToRead(ec,vnp)
804 retVal = VVnodeWriteToRead_r(ec, vnp);
809 int VVnodeWriteToRead_r(ec,vnp)
815 struct VnodeClassInfo *vcp;
817 #ifdef AFS_PTHREAD_ENV
818 pthread_t thisProcess;
819 #else /* AFS_PTHREAD_ENV */
821 #endif /* AFS_PTHREAD_ENV */
824 assert (vnp->nUsers != 0);
825 class = vnodeIdToClass(vnp->vnodeNumber);
826 vcp = &VnodeClassInfo[class];
827 assert(vnp->disk.vnodeMagic == vcp->magic);
828 writeLocked = WriteLocked(&vnp->lock);
829 VNLog(300, 2, vnp->vnodeNumber, (afs_int32) vnp);
835 #ifdef AFS_PTHREAD_ENV
836 thisProcess = pthread_self();
837 #else /* AFS_PTHREAD_ENV */
838 LWP_CurrentProcess(&thisProcess);
839 #endif /* AFS_PTHREAD_ENV */
841 VNLog(301, 2, (afs_int32) vnp,
842 ((vnp->changed_newTime) << 1) | ((vnp->changed_oldTime) << 1) |
844 if (thisProcess != vnp->writer)
845 Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",vnp);
849 if (vnp->changed_oldTime || vnp->changed_newTime) {
850 Volume *vp = vnp->volumePtr;
851 afs_int32 now = FT_ApproxTime();
852 assert(vnp->cacheCheck == vp->cacheCheck);
853 if (vnp->changed_newTime)
854 vnp->disk.serverModifyTime = now;
855 if (vnp->changed_newTime)
856 V_updateDate(vp) = vp->updateTime = now;
858 /* The inode has been changed. Write it out to disk */
860 assert(V_needsSalvaged(vp));
863 IHandle_t *ihP = vp->vnodeIndex[class].handle;
865 off_t off = vnodeIndexOffset(vcp, vnp->vnodeNumber);
869 Abort("VPutVnode: can't open index file!\n");
870 code = FDH_SEEK(fdP, off, SEEK_SET);
872 Abort("VPutVnode: can't seek on index file!\n");
873 code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
874 if (code != vcp->diskSize) {
876 * Don't force volume offline if the inumber is out of
877 * range or the inode table is full.
882 Log("VPutVnode: bad inumber %d\n",
883 vp->vnodeIndex[class].handle->ih_ino);
886 Log("VPutVnode: Couldn't write vnode %d, volume %u (%s)\n",
887 vnp->vnodeNumber, V_id(vnp->volumePtr),
888 V_name(vnp->volumePtr));
898 vnp->changed_newTime = vnp->changed_oldTime = 0;
901 ConvertWriteToReadLock(&vnp->lock);
905 /* Move the vnode, vnp, to the new hash table given by the
906 hash table index, newHash */
907 static int moveHash(vnp, newHash)
912 /* Remove it from the old hash chain */
913 tvnp = VnodeHashTable[vnp->hashIndex];
915 VnodeHashTable[vnp->hashIndex] = vnp->hashNext;
917 while (tvnp && tvnp->hashNext != vnp)
918 tvnp = tvnp->hashNext;
920 tvnp->hashNext = vnp->hashNext;
922 /* Add it to the new hash chain */
923 vnp->hashNext = VnodeHashTable[newHash];
924 VnodeHashTable[newHash] = vnp;
925 vnp->hashIndex = newHash;
930 StickOnLruChain_r(vnp,vcp)
932 register struct VnodeClassInfo *vcp;
934 /* Add it to the circular LRU list */
935 if (vcp->lruHead == NULL)
936 Abort("VPutVnode: vcp->lruHead==NULL");
938 vnp->lruNext = vcp->lruHead;
939 vnp->lruPrev = vcp->lruHead->lruPrev;
940 vcp->lruHead->lruPrev = vnp;
941 vnp->lruPrev->lruNext = vnp;
944 /* If the vnode was just deleted, put it at the end of the chain so it
945 will be reused immediately */
947 vcp->lruHead = vnp->lruNext;
948 /* If caching is turned off, set volumeptr to NULL to invalidate the
950 if (!TrustVnodeCacheEntry)
951 vnp->volumePtr = NULL;
954 /* VCloseVnodeFiles - called when a volume is going off line. All open
955 * files for vnodes in that volume are closed. This might be excessive,
956 * since we may only be taking one volume of a volume group offline.
958 void VCloseVnodeFiles_r(Volume *vp)
963 for (i=0; i<VNODE_HASH_TABLE_SIZE; i++) {
964 for (vnp = VnodeHashTable[i]; vnp; vnp = vnp->hashNext) {
965 if (vnp->volumePtr == vp) {
966 IH_REALLYCLOSE(vnp->handle);
972 /* VReleaseVnodeFiles - called when a volume is going detached. All open
973 * files for vnodes in that volume are closed and all inode handles
974 * for vnodes in that volume are released.
976 void VReleaseVnodeFiles_r(Volume *vp)
981 for (i=0; i<VNODE_HASH_TABLE_SIZE; i++) {
982 for (vnp = VnodeHashTable[i]; vnp; vnp = vnp->hashNext) {
983 if (vnp->volumePtr == vp) {
984 IH_RELEASE(vnp->handle);