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 */
66 /*@printflike@*/ extern void Log(const char *format, ...);
68 /*@printflike@*/ extern void Abort(const char *format, ...);
71 struct VnodeClassInfo VnodeClassInfo[nVNODECLASSES];
73 private int moveHash();
74 void StickOnLruChain_r();
77 #define BAD_IGET -1000
79 /* There are two separate vnode queue types defined here:
80 * Each hash conflict chain -- is singly linked, with a single head
81 * pointer. New entries are added at the beginning. Old
82 * entries are removed by linear search, which generally
83 * only occurs after a disk read).
84 * LRU chain -- is doubly linked, single head pointer.
85 * Entries are added at the head, reclaimed from the tail,
86 * or removed from anywhere in the queue.
90 /* Vnode hash table. Find hash chain by taking lower bits of
91 * (volume_hash_offset + vnode).
92 * This distributes the root inodes of the volumes over the
93 * hash table entries and also distributes the vnodes of
94 * volumes reasonably fairly. The volume_hash_offset field
95 * for each volume is established as the volume comes on line
96 * by using the VOLUME_HASH_OFFSET macro. This distributes the
97 * volumes fairly among the cache entries, both when servicing
98 * a small number of volumes and when servicing a large number.
101 /* logging stuff for finding bugs */
102 #define THELOGSIZE 5120
103 static afs_int32 theLog[THELOGSIZE];
104 static afs_int32 vnLogPtr=0;
105 VNLog(aop, anparms, av1, av2, av3, av4)
106 afs_int32 aop, anparms;
107 afs_int32 av1, av2, av3,av4; {
108 register afs_int32 temp;
111 /* copy data to array */
116 if (anparms>4) anparms = 4; /* do bounds checking */
118 temp = (aop<<16) | anparms;
119 theLog[vnLogPtr++] = temp;
120 if (vnLogPtr >= THELOGSIZE) vnLogPtr = 0;
121 for(temp=0;temp<anparms;temp++) {
122 theLog[vnLogPtr++] = data[temp];
123 if (vnLogPtr >= THELOGSIZE) vnLogPtr = 0;
127 /* VolumeHashOffset -- returns a new value to be stored in the
128 * volumeHashOffset of a Volume structure. Called when a
129 * volume is initialized. Sets the volumeHashOffset so that
130 * vnode cache entries are distributed reasonably between
131 * volumes (the root vnodes of the volumes will hash to
132 * different values, and spacing is maintained between volumes
133 * when there are not many volumes represented), and spread
134 * equally amongst vnodes within a single volume.
136 int VolumeHashOffset_r() {
137 static int nextVolumeHashOffset = 0;
138 /* hashindex Must be power of two in size */
140 # define hashMask ((1<<hashShift)-1)
141 static byte hashindex[1<<hashShift] = {0,128,64,192,32,160,96,224};
143 offset = hashindex[nextVolumeHashOffset&hashMask]
144 + (nextVolumeHashOffset>>hashShift);
145 nextVolumeHashOffset++;
149 /* Change hashindex (above) if you change this constant */
150 #define VNODE_HASH_TABLE_SIZE 256
151 private Vnode *VnodeHashTable[VNODE_HASH_TABLE_SIZE];
152 #define VNODE_HASH(volumeptr,vnodenumber)\
153 ((volumeptr->vnodeHashOffset + vnodenumber)&(VNODE_HASH_TABLE_SIZE-1))
155 /* Code to invalidate a vnode entry. Called when we've damaged a vnode, and want
156 to prevent future VGetVnode's from applying to it. Leaves it in the same hash bucket
157 but that shouldn't be important. */
158 void VInvalidateVnode_r(register struct Vnode *avnode)
160 avnode->changed_newTime = 0; /* don't let it get flushed out again */
161 avnode->changed_oldTime = 0;
162 avnode->delete = 0; /* it isn't deleted, erally */
163 avnode->cacheCheck = 0; /* invalid: prevents future vnode searches from working */
166 /* Not normally called by general client; called by volume.c */
167 int VInitVnodes(VnodeClass class, int nVnodes)
170 register struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
172 vcp->allocs = vcp->gets = vcp->reads = vcp->writes = 0;
173 vcp->cacheSize = nVnodes;
176 assert(CHECKSIZE_SMALLVNODE);
178 vcp->residentSize = SIZEOF_SMALLVNODE;
179 vcp->diskSize = SIZEOF_SMALLDISKVNODE;
180 vcp->magic = SMALLVNODEMAGIC;
184 vcp->residentSize = SIZEOF_LARGEVNODE;
185 vcp->diskSize = SIZEOF_LARGEDISKVNODE;
186 vcp->magic = LARGEVNODEMAGIC;
189 { int s = vcp->diskSize-1;
199 va = (byte *) calloc(nVnodes,vcp->residentSize);
202 Vnode *vnp = (Vnode *) va;
203 vnp->nUsers = 0; /* no context switches */
204 Lock_Init(&vnp->lock);
205 vnp->changed_oldTime = 0;
206 vnp->changed_newTime = 0;
207 vnp->volumePtr = NULL;
209 vnp->delete = vnp->vnodeNumber = 0;
210 #ifdef AFS_PTHREAD_ENV
211 vnp->writer = (pthread_t) 0;
212 #else /* AFS_PTHREAD_ENV */
213 vnp->writer = (PROCESS) 0;
214 #endif /* AFS_PTHREAD_ENV */
217 if (vcp->lruHead == NULL)
218 vcp->lruHead = vnp->lruNext = vnp->lruPrev = vnp;
220 vnp->lruNext = vcp->lruHead;
221 vnp->lruPrev = vcp->lruHead->lruPrev;
222 vcp->lruHead->lruPrev = vnp;
223 vnp->lruPrev->lruNext = vnp;
226 va += vcp->residentSize;
232 /* allocate an *unused* vnode from the LRU chain, going backwards of course. It shouldn't
233 be necessary to specify that nUsers == 0 since if it is in the list, nUsers
234 should be 0. Things shouldn't be in lruq unless no one is using them. */
235 Vnode *VGetFreeVnode_r(vcp)
236 struct VnodeClassInfo *vcp; {
239 vnp = vcp->lruHead->lruPrev;
240 if (vnp->nUsers != 0 || CheckLock(&vnp->lock))
241 Abort("locked vnode in lruq");
242 VNLog(1, 2, vnp->vnodeNumber, (afs_int32) vnp);
243 IH_RELEASE(vnp->handle);
248 static mlkLastAlloc = 0;
249 static mlkLastOver = 0;
250 static mlkLastDelete = 0;
252 Vnode *VAllocVnode(ec,vp,type)
259 retVal = VAllocVnode_r(ec, vp, type);
264 Vnode *VAllocVnode_r(ec,vp,type)
271 int newHash, bitNumber;
272 register struct VnodeClassInfo *vcp;
277 if (programType == fileServer && !V_inUse(vp)) {
278 if (vp->specialStatus) {
279 *ec = vp->specialStatus;
285 class = vnodeTypeToClass(type);
286 vcp = &VnodeClassInfo[class];
288 if (!VolumeWriteable(vp)) {
289 *ec = (bit32) VREADONLY;
293 unique = vp->nextVnodeUnique++;
295 unique = vp->nextVnodeUnique++;
297 if (vp->nextVnodeUnique > V_uniquifier(vp)) {
298 VUpdateVolume_r(ec,vp);
303 if (programType == fileServer) {
304 VAddToVolumeUpdateList_r(ec, vp);
309 /* Find a slot in the bit map */
310 bitNumber = VAllocBitmapEntry_r(ec,vp,&vp->vnodeIndex[class]);
313 vnodeNumber = bitNumberToVnodeNumber(bitNumber,class);
315 VNLog(2, 1, vnodeNumber);
316 /* Prepare to move it to the new hash chain */
317 newHash = VNODE_HASH(vp, vnodeNumber);
318 for (vnp = VnodeHashTable[newHash];
319 vnp && (vnp->vnodeNumber!=vnodeNumber || vnp->volumePtr!=vp
320 || vnp->volumePtr->cacheCheck!=vnp->cacheCheck);
324 /* slot already exists. May even not be in lruq (consider store file locking a file being deleted)
325 so we may have to wait for it below */
326 VNLog(3, 2, vnodeNumber, (afs_int32) vnp);
328 /* If first user, remove it from the LRU chain. We can assume that
329 there is at least one item in the queue */
330 if (++vnp->nUsers == 1) {
331 if (vnp == vcp->lruHead)
332 vcp->lruHead = vcp->lruHead->lruNext;
333 vnp->lruPrev->lruNext = vnp->lruNext;
334 vnp->lruNext->lruPrev = vnp->lruPrev;
335 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
336 Abort("VGetVnode: lru chain addled!\n");
337 /* This won't block */
338 ObtainWriteLock(&vnp->lock);
340 /* follow locking hierarchy */
342 ObtainWriteLock(&vnp->lock);
345 #ifdef AFS_PTHREAD_ENV
346 vnp->writer = pthread_self();
347 #else /* AFS_PTHREAD_ENV */
348 LWP_CurrentProcess(&vnp->writer);
349 #endif /* AFS_PTHREAD_ENV */
352 vnp = VGetFreeVnode_r(vcp);
353 /* Remove vnode from LRU chain and grab a write lock */
354 if (vnp == vcp->lruHead)
355 vcp->lruHead = vcp->lruHead->lruNext;
356 vnp->lruPrev->lruNext = vnp->lruNext;
357 vnp->lruNext->lruPrev = vnp->lruPrev;
358 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
359 Abort("VGetVnode: lru chain addled!\n");
360 /* Initialize the header fields so noone allocates another
361 vnode with the same number */
362 vnp->vnodeNumber = vnodeNumber;
364 vnp->cacheCheck = vp->cacheCheck;
366 moveHash(vnp, newHash);
367 /* This will never block */
368 ObtainWriteLock(&vnp->lock);
369 #ifdef AFS_PTHREAD_ENV
370 vnp->writer = pthread_self();
371 #else /* AFS_PTHREAD_ENV */
372 LWP_CurrentProcess(&vnp->writer);
373 #endif /* AFS_PTHREAD_ENV */
374 /* Sanity check: is this vnode really not in use? */
377 IHandle_t *ihP = vp->vnodeIndex[class].handle;
379 off_t off = vnodeIndexOffset(vcp, vnodeNumber);
384 Abort("VAllocVnode: can't open index file!\n");
385 if ((size = FDH_SIZE(fdP)) < 0)
386 Abort("VAllocVnode: can't stat index file!\n");
387 if (FDH_SEEK(fdP, off, SEEK_SET) < 0)
388 Abort("VAllocVnode: can't seek on index file!\n");
390 if (FDH_READ(fdP, &vnp->disk, vcp->diskSize) == vcp->diskSize) {
391 if (vnp->disk.type != vNull)
392 Abort("VAllocVnode: addled bitmap or index!\n");
395 /* growing file - grow in a reasonable increment */
396 char *buf = (char *)malloc(16*1024);
397 if (!buf) Abort("VAllocVnode: malloc failed\n");
398 memset(buf, 0, 16*1024);
399 (void) FDH_WRITE(fdP, buf, 16*1024);
405 VNLog(4, 2, vnodeNumber, (afs_int32) vnp);
408 VNLog(5, 1, (afs_int32) vnp);
409 #ifdef AFS_PTHREAD_ENV
410 vnp->writer = pthread_self();
411 #else /* AFS_PTHREAD_ENV */
412 LWP_CurrentProcess(&vnp->writer);
413 #endif /* AFS_PTHREAD_ENV */
414 memset(&vnp->disk, 0, sizeof(vnp->disk));
415 vnp->changed_newTime = 0; /* set this bit when vnode is updated */
416 vnp->changed_oldTime = 0; /* set this on CopyOnWrite. */
418 vnp->disk.vnodeMagic = vcp->magic;
419 vnp->disk.type = type;
420 vnp->disk.uniquifier = unique;
426 Vnode *VGetVnode(ec,vp,vnodeNumber,locktype)
430 int locktype; /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
434 retVal = VGetVnode_r(ec, vp, vnodeNumber, locktype);
439 Vnode *VGetVnode_r(ec,vp,vnodeNumber,locktype)
443 int locktype; /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
448 struct VnodeClassInfo *vcp;
451 mlkReason = 0; /* last call didn't fail */
453 if (vnodeNumber == 0) {
459 VNLog(100, 1, vnodeNumber);
460 if (programType == fileServer && !V_inUse(vp)) {
461 *ec = (vp->specialStatus ? vp->specialStatus : VOFFLINE);
463 /* If the volume is VBUSY (being cloned or dumped) and this is
464 * a READ operation, then don't fail.
466 if ((*ec != VBUSY) || (locktype != READ_LOCK)) {
472 class = vnodeIdToClass(vnodeNumber);
473 vcp = &VnodeClassInfo[class];
474 if (locktype == WRITE_LOCK && !VolumeWriteable(vp)) {
475 *ec = (bit32) VREADONLY;
480 if (locktype == WRITE_LOCK && programType == fileServer) {
481 VAddToVolumeUpdateList_r(ec, vp);
483 mlkReason = 1000 + *ec;
488 /* See whether the vnode is in the cache. */
489 newHash = VNODE_HASH(vp, vnodeNumber);
490 for (vnp = VnodeHashTable[newHash];
491 vnp && (vnp->vnodeNumber!=vnodeNumber || vnp->volumePtr!=vp
492 || vnp->volumePtr->cacheCheck!=vnp->cacheCheck);
498 IHandle_t *ihP = vp->vnodeIndex[class].handle;
500 /* Not in cache; tentatively grab most distantly used one from the LRU
503 vnp = VGetFreeVnode_r(vcp);
504 /* Remove it from the old hash chain */
505 moveHash(vnp, newHash);
506 /* Remove it from the LRU chain */
507 if (vnp == vcp->lruHead)
508 vcp->lruHead = vcp->lruHead->lruNext;
509 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
510 Abort("VGetVnode: lru chain addled!\n");
511 vnp->lruPrev->lruNext = vnp->lruNext;
512 vnp->lruNext->lruPrev = vnp->lruPrev;
514 vnp->changed_newTime = vnp->changed_oldTime = 0;
516 vnp->vnodeNumber = vnodeNumber;
518 vnp->cacheCheck = vp->cacheCheck;
521 /* This will never block */
522 ObtainWriteLock(&vnp->lock);
523 #ifdef AFS_PTHREAD_ENV
524 vnp->writer = pthread_self();
525 #else /* AFS_PTHREAD_ENV */
526 LWP_CurrentProcess(&vnp->writer);
527 #endif /* AFS_PTHREAD_ENV */
529 /* Read vnode from volume index */
533 Log("VGetVnode: can't open index dev=%d, i=%s\n",
534 vp->device, PrintInode(NULL,
535 vp->vnodeIndex[class].handle->ih_ino));
539 else if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, vnodeNumber),
541 Log ("VGetVnode: can't seek on index file vn=%u\n",vnodeNumber);
544 FDH_REALLYCLOSE(fdP);
546 else if ((n = FDH_READ(fdP, (char*)&vnp->disk, vcp->diskSize))
548 /* Don't take volume off line if the inumber is out of range
549 or the inode table is full. */
550 FDH_REALLYCLOSE(fdP);
553 Log("VGetVnode: bad inumber %s\n",
554 PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
558 /* Check for disk errors. Anything else just means that the vnode
560 if (n == -1 && errno == EIO) {
561 Log("VGetVnode: Couldn't read vnode %u, volume %u (%s); volume needs salvage\n",
562 vnodeNumber, V_id(vp), V_name(vp));
570 VInvalidateVnode_r(vnp);
571 if (vnp->nUsers-- == 1)
572 StickOnLruChain_r(vnp,vcp);
573 ReleaseWriteLock(&vnp->lock);
578 /* Quick check to see that the data is reasonable */
579 if (vnp->disk.vnodeMagic != vcp->magic || vnp->disk.type == vNull) {
580 if (vnp->disk.type == vNull) {
583 VInvalidateVnode_r(vnp);
584 if (vnp->nUsers-- == 1)
585 StickOnLruChain_r(vnp,vcp);
586 ReleaseWriteLock(&vnp->lock);
587 return NULL; /* The vnode is not allocated */
590 struct vnodeIndex *index = &vp->vnodeIndex[class];
591 unsigned int bitNumber = vnodeIdToBitNumber(vnodeNumber);
592 unsigned int offset = bitNumber >> 3;
594 /* Test to see if vnode number is valid. */
595 if ((offset >= index->bitmapSize) ||
596 ((*(index->bitmap+offset) & (1<<(bitNumber&0x7))) == 0)) {
597 Log("VGetVnode: Request for unallocated vnode %u, volume %u (%s) denied.\n",
598 vnodeNumber, V_id(vp), V_name(vp));
603 Log("VGetVnode: Bad magic number, vnode %u, volume %u (%s); volume needs salvage\n",
604 vnodeNumber, V_id(vp), V_name(vp));
605 vp->goingOffline = 1; /* used to call VOffline, but that would mess
606 up the volume ref count if called here */
610 VInvalidateVnode_r(vnp);
611 if (vnp->nUsers-- == 1)
612 StickOnLruChain_r(vnp,vcp);
613 ReleaseWriteLock(&vnp->lock);
617 IH_INIT(vnp->handle, V_device(vp), V_parentId(vp), VN_GET_INO(vnp));
618 ReleaseWriteLock(&vnp->lock);
620 VNLog(101, 2, vnodeNumber, (afs_int32) vnp);
621 if (++vnp->nUsers == 1) {
622 /* First user. Remove it from the LRU chain. We can assume that
623 there is at least one item in the queue */
624 if (vnp == vcp->lruHead)
625 vcp->lruHead = vcp->lruHead->lruNext;
626 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
627 Abort("VGetVnode: lru chain addled!\n");
628 vnp->lruPrev->lruNext = vnp->lruNext;
629 vnp->lruNext->lruPrev = vnp->lruPrev;
633 if (locktype == READ_LOCK)
634 ObtainReadLock(&vnp->lock);
636 ObtainWriteLock(&vnp->lock);
637 #ifdef AFS_PTHREAD_ENV
638 vnp->writer = pthread_self();
639 #else /* AFS_PTHREAD_ENV */
640 LWP_CurrentProcess(&vnp->writer);
641 #endif /* AFS_PTHREAD_ENV */
644 /* Check that the vnode hasn't been removed while we were obtaining
646 VNLog(102, 2, vnodeNumber, (afs_int32) vnp);
647 if ((vnp->disk.type == vNull) || (vnp->cacheCheck == 0)){
648 if (vnp->nUsers-- == 1)
649 StickOnLruChain_r(vnp,vcp);
650 if (locktype == READ_LOCK)
651 ReleaseReadLock(&vnp->lock);
653 ReleaseWriteLock(&vnp->lock);
656 /* vnode is labelled correctly by now, so we don't have to invalidate it */
659 if (programType == fileServer)
660 VBumpVolumeUsage_r(vnp->volumePtr);/* Hack; don't know where it should be
661 called from. Maybe VGetVolume */
666 int TrustVnodeCacheEntry = 1;
667 /* This variable is bogus--when it's set to 0, the hash chains fill
668 up with multiple versions of the same vnode. Should fix this!! */
684 int writeLocked, offset;
686 struct VnodeClassInfo *vcp;
690 assert (vnp->nUsers != 0);
691 class = vnodeIdToClass(vnp->vnodeNumber);
692 vcp = &VnodeClassInfo[class];
693 assert(vnp->disk.vnodeMagic == vcp->magic);
694 VNLog(200, 2, vnp->vnodeNumber, (afs_int32) vnp);
696 writeLocked = WriteLocked(&vnp->lock);
698 #ifdef AFS_PTHREAD_ENV
699 pthread_t thisProcess = pthread_self();
700 #else /* AFS_PTHREAD_ENV */
702 LWP_CurrentProcess(&thisProcess);
703 #endif /* AFS_PTHREAD_ENV */
704 VNLog(201, 2, (afs_int32) vnp,
705 ((vnp->changed_newTime) << 1) | ((vnp->changed_oldTime) << 1) | vnp->delete);
706 if (thisProcess != vnp->writer)
707 Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",vnp);
708 if (vnp->changed_oldTime || vnp->changed_newTime || vnp->delete) {
709 Volume *vp = vnp->volumePtr;
710 afs_uint32 now = FT_ApproxTime();
711 assert(vnp->cacheCheck == vp->cacheCheck);
714 /* No longer any directory entries for this vnode. Free the Vnode */
715 memset(&vnp->disk, 0, sizeof (vnp->disk));
716 mlkLastDelete = vnp->vnodeNumber;
717 /* delete flag turned off further down */
718 VNLog(202, 2, vnp->vnodeNumber, (afs_int32) vnp);
719 } else if (vnp->changed_newTime) {
720 vnp->disk.serverModifyTime = now;
722 if (vnp->changed_newTime)
723 V_updateDate(vp) = vp->updateTime = now;
725 /* The vnode has been changed. Write it out to disk */
727 assert(V_needsSalvaged(vp));
730 IHandle_t *ihP = vp->vnodeIndex[class].handle;
735 Abort("VPutVnode: can't open index file!\n");
736 offset = vnodeIndexOffset(vcp, vnp->vnodeNumber);
737 if (FDH_SEEK(fdP, offset, SEEK_SET) < 0) {
738 Abort("VPutVnode: can't seek on index file! fdp=0x%x offset=%d, errno=%d\n",
741 code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
742 if (code != vcp->diskSize) {
743 /* Don't force volume offline if the inumber is out of
744 * range or the inode table is full.
747 if (code == BAD_IGET) {
748 Log("VPutVnode: bad inumber %s\n",
749 PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
752 Log("VPutVnode: Couldn't write vnode %u, volume %u (%s) (error %d)\n",
753 vnp->vnodeNumber, V_id(vnp->volumePtr),
754 V_name(vnp->volumePtr), code);
759 FDH_REALLYCLOSE(fdP);
765 /* If the vnode is to be deleted, and we wrote the vnode out,
766 * free its bitmap entry. Do after the vnode is written so we
767 * don't allocate from bitmap before the vnode is written
768 * (doing so could cause a "addled bitmap" message).
770 if (vnp->delete && !*ec) {
771 VFreeBitMapEntry_r(ec, &vp->vnodeIndex[class],
772 vnodeIdToBitNumber(vnp->vnodeNumber));
776 vnp->changed_newTime = vnp->changed_oldTime = 0;
778 } else { /* Not write locked */
779 if (vnp->changed_newTime || vnp->changed_oldTime || vnp->delete)
780 Abort("VPutVnode: Change or delete flag for vnode 0x%x is set but vnode is not write locked!\n", vnp);
783 /* Do not look at disk portion of vnode after this point; it may
784 have been deleted above */
785 if (vnp->nUsers-- == 1)
786 StickOnLruChain_r(vnp,vcp);
790 ReleaseWriteLock(&vnp->lock);
792 ReleaseReadLock(&vnp->lock);
796 * Make an attempt to convert a vnode lock from write to read.
797 * Do nothing if the vnode isn't write locked or the vnode has
800 int VVnodeWriteToRead(ec,vnp)
806 retVal = VVnodeWriteToRead_r(ec, vnp);
811 int VVnodeWriteToRead_r(ec,vnp)
817 struct VnodeClassInfo *vcp;
819 #ifdef AFS_PTHREAD_ENV
820 pthread_t thisProcess;
821 #else /* AFS_PTHREAD_ENV */
823 #endif /* AFS_PTHREAD_ENV */
826 assert (vnp->nUsers != 0);
827 class = vnodeIdToClass(vnp->vnodeNumber);
828 vcp = &VnodeClassInfo[class];
829 assert(vnp->disk.vnodeMagic == vcp->magic);
830 writeLocked = WriteLocked(&vnp->lock);
831 VNLog(300, 2, vnp->vnodeNumber, (afs_int32) vnp);
837 #ifdef AFS_PTHREAD_ENV
838 thisProcess = pthread_self();
839 #else /* AFS_PTHREAD_ENV */
840 LWP_CurrentProcess(&thisProcess);
841 #endif /* AFS_PTHREAD_ENV */
843 VNLog(301, 2, (afs_int32) vnp,
844 ((vnp->changed_newTime) << 1) | ((vnp->changed_oldTime) << 1) |
846 if (thisProcess != vnp->writer)
847 Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",vnp);
851 if (vnp->changed_oldTime || vnp->changed_newTime) {
852 Volume *vp = vnp->volumePtr;
853 afs_uint32 now = FT_ApproxTime();
854 assert(vnp->cacheCheck == vp->cacheCheck);
855 if (vnp->changed_newTime)
856 vnp->disk.serverModifyTime = now;
857 if (vnp->changed_newTime)
858 V_updateDate(vp) = vp->updateTime = now;
860 /* The inode has been changed. Write it out to disk */
862 assert(V_needsSalvaged(vp));
865 IHandle_t *ihP = vp->vnodeIndex[class].handle;
867 off_t off = vnodeIndexOffset(vcp, vnp->vnodeNumber);
871 Abort("VPutVnode: can't open index file!\n");
872 code = FDH_SEEK(fdP, off, SEEK_SET);
874 Abort("VPutVnode: can't seek on index file!\n");
875 code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
876 if (code != vcp->diskSize) {
878 * Don't force volume offline if the inumber is out of
879 * range or the inode table is full.
884 Log("VPutVnode: bad inumber %llu\n",
885 (afs_uintmax_t) vp->vnodeIndex[class].handle->ih_ino);
888 Log("VPutVnode: Couldn't write vnode %u, volume %u (%s)\n",
889 vnp->vnodeNumber, V_id(vnp->volumePtr),
890 V_name(vnp->volumePtr));
900 vnp->changed_newTime = vnp->changed_oldTime = 0;
903 ConvertWriteToReadLock(&vnp->lock);
907 /* Move the vnode, vnp, to the new hash table given by the
908 hash table index, newHash */
909 static int moveHash(vnp, newHash)
914 /* Remove it from the old hash chain */
915 tvnp = VnodeHashTable[vnp->hashIndex];
917 VnodeHashTable[vnp->hashIndex] = vnp->hashNext;
919 while (tvnp && tvnp->hashNext != vnp)
920 tvnp = tvnp->hashNext;
922 tvnp->hashNext = vnp->hashNext;
924 /* Add it to the new hash chain */
925 vnp->hashNext = VnodeHashTable[newHash];
926 VnodeHashTable[newHash] = vnp;
927 vnp->hashIndex = newHash;
932 StickOnLruChain_r(vnp,vcp)
934 register struct VnodeClassInfo *vcp;
936 /* Add it to the circular LRU list */
937 if (vcp->lruHead == NULL)
938 Abort("VPutVnode: vcp->lruHead==NULL");
940 vnp->lruNext = vcp->lruHead;
941 vnp->lruPrev = vcp->lruHead->lruPrev;
942 vcp->lruHead->lruPrev = vnp;
943 vnp->lruPrev->lruNext = vnp;
946 /* If the vnode was just deleted, put it at the end of the chain so it
947 will be reused immediately */
949 vcp->lruHead = vnp->lruNext;
950 /* If caching is turned off, set volumeptr to NULL to invalidate the
952 if (!TrustVnodeCacheEntry)
953 vnp->volumePtr = NULL;
956 /* VCloseVnodeFiles - called when a volume is going off line. All open
957 * files for vnodes in that volume are closed. This might be excessive,
958 * since we may only be taking one volume of a volume group offline.
960 void VCloseVnodeFiles_r(Volume *vp)
965 for (i=0; i<VNODE_HASH_TABLE_SIZE; i++) {
966 for (vnp = VnodeHashTable[i]; vnp; vnp = vnp->hashNext) {
967 if (vnp->volumePtr == vp) {
968 IH_REALLYCLOSE(vnp->handle);
974 /* VReleaseVnodeFiles - called when a volume is going detached. All open
975 * files for vnodes in that volume are closed and all inode handles
976 * for vnodes in that volume are released.
978 void VReleaseVnodeFiles_r(Volume *vp)
983 for (i=0; i<VNODE_HASH_TABLE_SIZE; i++) {
984 for (vnp = VnodeHashTable[i]; vnp; vnp = vnp->hashNext) {
985 if (vnp->volumePtr == vp) {
986 IH_RELEASE(vnp->handle);