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>
31 #ifdef AFS_PTHREAD_ENV
33 #else /* AFS_PTHREAD_ENV */
34 #include <afs/assert.h>
35 #endif /* AFS_PTHREAD_ENV */
38 #include <afs/afsint.h>
40 #include <afs/errors.h>
43 #include <afs/afssyscalls.h>
47 #include "partition.h"
48 #if defined(AFS_SGI_ENV)
49 #include "sys/types.h"
61 #include <sys/fcntl.h>
64 #endif /* AFS_NT40_ENV */
67 /*@printflike@*/ extern void Log(const char *format, ...);
69 /*@printflike@*/ extern void Abort(const char *format, ...);
72 struct VnodeClassInfo VnodeClassInfo[nVNODECLASSES];
74 private int moveHash(register Vnode * vnp, bit32 newHash);
75 void StickOnLruChain_r(register Vnode * vnp,
76 register struct VnodeClassInfo *vcp);
78 #define BAD_IGET -1000
80 /* There are two separate vnode queue types defined here:
81 * Each hash conflict chain -- is singly linked, with a single head
82 * pointer. New entries are added at the beginning. Old
83 * entries are removed by linear search, which generally
84 * only occurs after a disk read).
85 * LRU chain -- is doubly linked, single head pointer.
86 * Entries are added at the head, reclaimed from the tail,
87 * or removed from anywhere in the queue.
91 /* Vnode hash table. Find hash chain by taking lower bits of
92 * (volume_hash_offset + vnode).
93 * This distributes the root inodes of the volumes over the
94 * hash table entries and also distributes the vnodes of
95 * volumes reasonably fairly. The volume_hash_offset field
96 * for each volume is established as the volume comes on line
97 * by using the VOLUME_HASH_OFFSET macro. This distributes the
98 * volumes fairly among the cache entries, both when servicing
99 * a small number of volumes and when servicing a large number.
102 /* logging stuff for finding bugs */
103 #define THELOGSIZE 5120
104 static afs_int32 theLog[THELOGSIZE];
105 static afs_int32 vnLogPtr = 0;
106 VNLog(aop, anparms, av1, av2, av3, av4)
107 afs_int32 aop, anparms;
108 afs_int32 av1, av2, av3, av4;
110 register afs_int32 temp;
113 /* copy data to array */
119 anparms = 4; /* do bounds checking */
121 temp = (aop << 16) | anparms;
122 theLog[vnLogPtr++] = temp;
123 if (vnLogPtr >= THELOGSIZE)
125 for (temp = 0; temp < anparms; temp++) {
126 theLog[vnLogPtr++] = data[temp];
127 if (vnLogPtr >= THELOGSIZE)
132 /* VolumeHashOffset -- returns a new value to be stored in the
133 * volumeHashOffset of a Volume structure. Called when a
134 * volume is initialized. Sets the volumeHashOffset so that
135 * vnode cache entries are distributed reasonably between
136 * volumes (the root vnodes of the volumes will hash to
137 * different values, and spacing is maintained between volumes
138 * when there are not many volumes represented), and spread
139 * equally amongst vnodes within a single volume.
142 VolumeHashOffset_r(void)
144 static int nextVolumeHashOffset = 0;
145 /* hashindex Must be power of two in size */
147 # define hashMask ((1<<hashShift)-1)
148 static byte hashindex[1 << hashShift] =
149 { 0, 128, 64, 192, 32, 160, 96, 224 };
151 offset = hashindex[nextVolumeHashOffset & hashMask]
152 + (nextVolumeHashOffset >> hashShift);
153 nextVolumeHashOffset++;
157 /* Change hashindex (above) if you change this constant */
158 #define VNODE_HASH_TABLE_SIZE 256
159 private Vnode *VnodeHashTable[VNODE_HASH_TABLE_SIZE];
160 #define VNODE_HASH(volumeptr,vnodenumber)\
161 ((volumeptr->vnodeHashOffset + vnodenumber)&(VNODE_HASH_TABLE_SIZE-1))
163 /* Code to invalidate a vnode entry. Called when we've damaged a vnode, and want
164 to prevent future VGetVnode's from applying to it. Leaves it in the same hash bucket
165 but that shouldn't be important. */
167 VInvalidateVnode_r(register struct Vnode *avnode)
169 avnode->changed_newTime = 0; /* don't let it get flushed out again */
170 avnode->changed_oldTime = 0;
171 avnode->delete = 0; /* it isn't deleted, erally */
172 avnode->cacheCheck = 0; /* invalid: prevents future vnode searches from working */
175 /* Not normally called by general client; called by volume.c */
177 VInitVnodes(VnodeClass class, int nVnodes)
180 register struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
182 vcp->allocs = vcp->gets = vcp->reads = vcp->writes = 0;
183 vcp->cacheSize = nVnodes;
186 assert(CHECKSIZE_SMALLVNODE);
188 vcp->residentSize = SIZEOF_SMALLVNODE;
189 vcp->diskSize = SIZEOF_SMALLDISKVNODE;
190 vcp->magic = SMALLVNODEMAGIC;
194 vcp->residentSize = SIZEOF_LARGEVNODE;
195 vcp->diskSize = SIZEOF_LARGEDISKVNODE;
196 vcp->magic = LARGEVNODEMAGIC;
200 int s = vcp->diskSize - 1;
210 va = (byte *) calloc(nVnodes, vcp->residentSize);
213 Vnode *vnp = (Vnode *) va;
214 vnp->nUsers = 0; /* no context switches */
215 Lock_Init(&vnp->lock);
216 vnp->changed_oldTime = 0;
217 vnp->changed_newTime = 0;
218 vnp->volumePtr = NULL;
220 vnp->delete = vnp->vnodeNumber = 0;
221 #ifdef AFS_PTHREAD_ENV
222 vnp->writer = (pthread_t) 0;
223 #else /* AFS_PTHREAD_ENV */
224 vnp->writer = (PROCESS) 0;
225 #endif /* AFS_PTHREAD_ENV */
228 if (vcp->lruHead == NULL)
229 vcp->lruHead = vnp->lruNext = vnp->lruPrev = vnp;
231 vnp->lruNext = vcp->lruHead;
232 vnp->lruPrev = vcp->lruHead->lruPrev;
233 vcp->lruHead->lruPrev = vnp;
234 vnp->lruPrev->lruNext = vnp;
237 va += vcp->residentSize;
243 /* allocate an *unused* vnode from the LRU chain, going backwards of course. It shouldn't
244 be necessary to specify that nUsers == 0 since if it is in the list, nUsers
245 should be 0. Things shouldn't be in lruq unless no one is using them. */
247 VGetFreeVnode_r(struct VnodeClassInfo * vcp)
251 vnp = vcp->lruHead->lruPrev;
252 if (vnp->nUsers != 0 || CheckLock(&vnp->lock))
253 Abort("locked vnode in lruq");
254 VNLog(1, 2, vnp->vnodeNumber, (afs_int32) vnp);
255 IH_RELEASE(vnp->handle);
259 static mlkReason = 0;
260 static mlkLastAlloc = 0;
261 static mlkLastOver = 0;
262 static mlkLastDelete = 0;
265 VAllocVnode(Error * ec, Volume * vp, VnodeType type)
268 VOL_LOCK retVal = VAllocVnode_r(ec, vp, type);
269 VOL_UNLOCK return retVal;
273 VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
277 int newHash, bitNumber;
278 register struct VnodeClassInfo *vcp;
283 if (programType == fileServer && !V_inUse(vp)) {
284 if (vp->specialStatus) {
285 *ec = vp->specialStatus;
291 class = vnodeTypeToClass(type);
292 vcp = &VnodeClassInfo[class];
294 if (!VolumeWriteable(vp)) {
295 *ec = (bit32) VREADONLY;
299 unique = vp->nextVnodeUnique++;
301 unique = vp->nextVnodeUnique++;
303 if (vp->nextVnodeUnique > V_uniquifier(vp)) {
304 VUpdateVolume_r(ec, vp);
309 if (programType == fileServer) {
310 VAddToVolumeUpdateList_r(ec, vp);
315 /* Find a slot in the bit map */
316 bitNumber = VAllocBitmapEntry_r(ec, vp, &vp->vnodeIndex[class]);
319 vnodeNumber = bitNumberToVnodeNumber(bitNumber, class);
321 VNLog(2, 1, vnodeNumber);
322 /* Prepare to move it to the new hash chain */
323 newHash = VNODE_HASH(vp, vnodeNumber);
324 for (vnp = VnodeHashTable[newHash];
325 vnp && (vnp->vnodeNumber != vnodeNumber || vnp->volumePtr != vp
326 || vnp->volumePtr->cacheCheck != vnp->cacheCheck);
327 vnp = vnp->hashNext);
329 /* slot already exists. May even not be in lruq (consider store file locking a file being deleted)
330 * so we may have to wait for it below */
331 VNLog(3, 2, vnodeNumber, (afs_int32) vnp);
333 /* If first user, remove it from the LRU chain. We can assume that
334 * there is at least one item in the queue */
335 if (++vnp->nUsers == 1) {
336 if (vnp == vcp->lruHead)
337 vcp->lruHead = vcp->lruHead->lruNext;
338 vnp->lruPrev->lruNext = vnp->lruNext;
339 vnp->lruNext->lruPrev = vnp->lruPrev;
340 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
341 Abort("VGetVnode: lru chain addled!\n");
342 /* This won't block */
343 ObtainWriteLock(&vnp->lock);
345 /* follow locking hierarchy */
346 VOL_UNLOCK ObtainWriteLock(&vnp->lock);
348 #ifdef AFS_PTHREAD_ENV
349 vnp->writer = pthread_self();
350 #else /* AFS_PTHREAD_ENV */
351 LWP_CurrentProcess(&vnp->writer);
352 #endif /* AFS_PTHREAD_ENV */
354 vnp = VGetFreeVnode_r(vcp);
355 /* Remove vnode from LRU chain and grab a write lock */
356 if (vnp == vcp->lruHead)
357 vcp->lruHead = vcp->lruHead->lruNext;
358 vnp->lruPrev->lruNext = vnp->lruNext;
359 vnp->lruNext->lruPrev = vnp->lruPrev;
360 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
361 Abort("VGetVnode: lru chain addled!\n");
362 /* Initialize the header fields so noone allocates another
363 * vnode with the same number */
364 vnp->vnodeNumber = vnodeNumber;
366 vnp->cacheCheck = vp->cacheCheck;
368 moveHash(vnp, newHash);
369 /* This will never block */
370 ObtainWriteLock(&vnp->lock);
371 #ifdef AFS_PTHREAD_ENV
372 vnp->writer = pthread_self();
373 #else /* AFS_PTHREAD_ENV */
374 LWP_CurrentProcess(&vnp->writer);
375 #endif /* AFS_PTHREAD_ENV */
376 /* Sanity check: is this vnode really not in use? */
379 IHandle_t *ihP = vp->vnodeIndex[class].handle;
381 off_t off = vnodeIndexOffset(vcp, vnodeNumber);
383 VOL_UNLOCK fdP = IH_OPEN(ihP);
385 Abort("VAllocVnode: can't open index file!\n");
386 if ((size = FDH_SIZE(fdP)) < 0)
387 Abort("VAllocVnode: can't stat index file!\n");
388 if (FDH_SEEK(fdP, off, SEEK_SET) < 0)
389 Abort("VAllocVnode: can't seek on index file!\n");
391 if (FDH_READ(fdP, &vnp->disk, vcp->diskSize) == vcp->diskSize) {
392 if (vnp->disk.type != vNull)
393 Abort("VAllocVnode: addled bitmap or index!\n");
396 /* growing file - grow in a reasonable increment */
397 char *buf = (char *)malloc(16 * 1024);
399 Abort("VAllocVnode: malloc failed\n");
400 memset(buf, 0, 16 * 1024);
401 (void)FDH_WRITE(fdP, buf, 16 * 1024);
406 VNLog(4, 2, vnodeNumber, (afs_int32) vnp);
409 VNLog(5, 1, (afs_int32) vnp);
410 #ifdef AFS_PTHREAD_ENV
411 vnp->writer = pthread_self();
412 #else /* AFS_PTHREAD_ENV */
413 LWP_CurrentProcess(&vnp->writer);
414 #endif /* AFS_PTHREAD_ENV */
415 memset(&vnp->disk, 0, sizeof(vnp->disk));
416 vnp->changed_newTime = 0; /* set this bit when vnode is updated */
417 vnp->changed_oldTime = 0; /* set this on CopyOnWrite. */
419 vnp->disk.vnodeMagic = vcp->magic;
420 vnp->disk.type = type;
421 vnp->disk.uniquifier = unique;
428 VGetVnode(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
429 { /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
431 VOL_LOCK retVal = VGetVnode_r(ec, vp, vnodeNumber, locktype);
432 VOL_UNLOCK return retVal;
436 VGetVnode_r(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
437 { /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
441 struct VnodeClassInfo *vcp;
444 mlkReason = 0; /* last call didn't fail */
446 if (vnodeNumber == 0) {
452 VNLog(100, 1, vnodeNumber);
453 if (programType == fileServer && !V_inUse(vp)) {
454 *ec = (vp->specialStatus ? vp->specialStatus : VOFFLINE);
456 /* If the volume is VBUSY (being cloned or dumped) and this is
457 * a READ operation, then don't fail.
459 if ((*ec != VBUSY) || (locktype != READ_LOCK)) {
465 class = vnodeIdToClass(vnodeNumber);
466 vcp = &VnodeClassInfo[class];
467 if (locktype == WRITE_LOCK && !VolumeWriteable(vp)) {
468 *ec = (bit32) VREADONLY;
473 if (locktype == WRITE_LOCK && programType == fileServer) {
474 VAddToVolumeUpdateList_r(ec, vp);
476 mlkReason = 1000 + *ec;
481 /* See whether the vnode is in the cache. */
482 newHash = VNODE_HASH(vp, vnodeNumber);
483 for (vnp = VnodeHashTable[newHash];
484 vnp && (vnp->vnodeNumber != vnodeNumber || vnp->volumePtr != vp
485 || vnp->volumePtr->cacheCheck != vnp->cacheCheck);
486 vnp = vnp->hashNext);
490 IHandle_t *ihP = vp->vnodeIndex[class].handle;
492 /* Not in cache; tentatively grab most distantly used one from the LRU
495 vnp = VGetFreeVnode_r(vcp);
496 /* Remove it from the old hash chain */
497 moveHash(vnp, newHash);
498 /* Remove it from the LRU chain */
499 if (vnp == vcp->lruHead)
500 vcp->lruHead = vcp->lruHead->lruNext;
501 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
502 Abort("VGetVnode: lru chain addled!\n");
503 vnp->lruPrev->lruNext = vnp->lruNext;
504 vnp->lruNext->lruPrev = vnp->lruPrev;
506 vnp->changed_newTime = vnp->changed_oldTime = 0;
508 vnp->vnodeNumber = vnodeNumber;
510 vnp->cacheCheck = vp->cacheCheck;
513 /* This will never block */
514 ObtainWriteLock(&vnp->lock);
515 #ifdef AFS_PTHREAD_ENV
516 vnp->writer = pthread_self();
517 #else /* AFS_PTHREAD_ENV */
518 LWP_CurrentProcess(&vnp->writer);
519 #endif /* AFS_PTHREAD_ENV */
521 /* Read vnode from volume index */
522 VOL_UNLOCK fdP = IH_OPEN(ihP);
524 Log("VGetVnode: can't open index dev=%u, i=%s\n", vp->device,
525 PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
528 } else if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, vnodeNumber), SEEK_SET)
530 Log("VGetVnode: can't seek on index file vn=%u\n", vnodeNumber);
533 FDH_REALLYCLOSE(fdP);
534 } else if ((n = FDH_READ(fdP, (char *)&vnp->disk, vcp->diskSize))
536 /* Don't take volume off line if the inumber is out of range
537 * or the inode table is full. */
538 FDH_REALLYCLOSE(fdP);
539 VOL_LOCK if (n == BAD_IGET) {
540 Log("VGetVnode: bad inumber %s\n",
541 PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
545 /* Check for disk errors. Anything else just means that the vnode
546 * is not allocated */
547 if (n == -1 && errno == EIO) {
548 Log("VGetVnode: Couldn't read vnode %u, volume %u (%s); volume needs salvage\n", vnodeNumber, V_id(vp), V_name(vp));
556 VInvalidateVnode_r(vnp);
557 if (vnp->nUsers-- == 1)
558 StickOnLruChain_r(vnp, vcp);
559 ReleaseWriteLock(&vnp->lock);
564 /* Quick check to see that the data is reasonable */
565 if (vnp->disk.vnodeMagic != vcp->magic || vnp->disk.type == vNull) {
566 if (vnp->disk.type == vNull) {
569 VInvalidateVnode_r(vnp);
570 if (vnp->nUsers-- == 1)
571 StickOnLruChain_r(vnp, vcp);
572 ReleaseWriteLock(&vnp->lock);
573 return NULL; /* The vnode is not allocated */
575 struct vnodeIndex *index = &vp->vnodeIndex[class];
576 unsigned int bitNumber = vnodeIdToBitNumber(vnodeNumber);
577 unsigned int offset = bitNumber >> 3;
579 /* Test to see if vnode number is valid. */
580 if ((offset >= index->bitmapSize)
581 || ((*(index->bitmap + offset) & (1 << (bitNumber & 0x7)))
583 Log("VGetVnode: Request for unallocated vnode %u, volume %u (%s) denied.\n", vnodeNumber, V_id(vp), V_name(vp));
587 Log("VGetVnode: Bad magic number, vnode %u, volume %u (%s); volume needs salvage\n", vnodeNumber, V_id(vp), V_name(vp));
588 vp->goingOffline = 1; /* used to call VOffline, but that would mess
589 * up the volume ref count if called here */
593 VInvalidateVnode_r(vnp);
594 if (vnp->nUsers-- == 1)
595 StickOnLruChain_r(vnp, vcp);
596 ReleaseWriteLock(&vnp->lock);
600 IH_INIT(vnp->handle, V_device(vp), V_parentId(vp), VN_GET_INO(vnp));
601 ReleaseWriteLock(&vnp->lock);
603 VNLog(101, 2, vnodeNumber, (afs_int32) vnp);
604 if (++vnp->nUsers == 1) {
605 /* First user. Remove it from the LRU chain. We can assume that
606 * there is at least one item in the queue */
607 if (vnp == vcp->lruHead)
608 vcp->lruHead = vcp->lruHead->lruNext;
609 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
610 Abort("VGetVnode: lru chain addled!\n");
611 vnp->lruPrev->lruNext = vnp->lruNext;
612 vnp->lruNext->lruPrev = vnp->lruPrev;
615 VOL_UNLOCK if (locktype == READ_LOCK)
616 ObtainReadLock(&vnp->lock);
618 ObtainWriteLock(&vnp->lock);
619 #ifdef AFS_PTHREAD_ENV
620 vnp->writer = pthread_self();
621 #else /* AFS_PTHREAD_ENV */
622 LWP_CurrentProcess(&vnp->writer);
623 #endif /* AFS_PTHREAD_ENV */
626 /* Check that the vnode hasn't been removed while we were obtaining
628 VNLog(102, 2, vnodeNumber, (afs_int32) vnp);
629 if ((vnp->disk.type == vNull) || (vnp->cacheCheck == 0)) {
630 if (vnp->nUsers-- == 1)
631 StickOnLruChain_r(vnp, vcp);
632 if (locktype == READ_LOCK)
633 ReleaseReadLock(&vnp->lock);
635 ReleaseWriteLock(&vnp->lock);
638 /* vnode is labelled correctly by now, so we don't have to invalidate it */
641 if (programType == fileServer)
642 VBumpVolumeUsage_r(vnp->volumePtr); /* Hack; don't know where it should be
643 * called from. Maybe VGetVolume */
648 int TrustVnodeCacheEntry = 1;
649 /* This variable is bogus--when it's set to 0, the hash chains fill
650 up with multiple versions of the same vnode. Should fix this!! */
652 VPutVnode(Error * ec, register Vnode * vnp)
654 VOL_LOCK VPutVnode_r(ec, vnp);
658 VPutVnode_r(Error * ec, register Vnode * vnp)
660 int writeLocked, offset;
662 struct VnodeClassInfo *vcp;
666 assert(vnp->nUsers != 0);
667 class = vnodeIdToClass(vnp->vnodeNumber);
668 vcp = &VnodeClassInfo[class];
669 assert(vnp->disk.vnodeMagic == vcp->magic);
670 VNLog(200, 2, vnp->vnodeNumber, (afs_int32) vnp);
672 writeLocked = WriteLocked(&vnp->lock);
674 #ifdef AFS_PTHREAD_ENV
675 pthread_t thisProcess = pthread_self();
676 #else /* AFS_PTHREAD_ENV */
678 LWP_CurrentProcess(&thisProcess);
679 #endif /* AFS_PTHREAD_ENV */
680 VNLog(201, 2, (afs_int32) vnp,
681 ((vnp->changed_newTime) << 1) | ((vnp->
682 changed_oldTime) << 1) | vnp->
684 if (thisProcess != vnp->writer)
685 Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",
687 if (vnp->changed_oldTime || vnp->changed_newTime || vnp->delete) {
688 Volume *vp = vnp->volumePtr;
689 afs_uint32 now = FT_ApproxTime();
690 assert(vnp->cacheCheck == vp->cacheCheck);
693 /* No longer any directory entries for this vnode. Free the Vnode */
694 memset(&vnp->disk, 0, sizeof(vnp->disk));
695 mlkLastDelete = vnp->vnodeNumber;
696 /* delete flag turned off further down */
697 VNLog(202, 2, vnp->vnodeNumber, (afs_int32) vnp);
698 } else if (vnp->changed_newTime) {
699 vnp->disk.serverModifyTime = now;
701 if (vnp->changed_newTime)
702 V_updateDate(vp) = vp->updateTime = now;
704 /* The vnode has been changed. Write it out to disk */
706 assert(V_needsSalvaged(vp));
709 IHandle_t *ihP = vp->vnodeIndex[class].handle;
711 VOL_UNLOCK fdP = IH_OPEN(ihP);
713 Abort("VPutVnode: can't open index file!\n");
714 offset = vnodeIndexOffset(vcp, vnp->vnodeNumber);
715 if (FDH_SEEK(fdP, offset, SEEK_SET) < 0) {
717 ("VPutVnode: can't seek on index file! fdp=0x%x offset=%d, errno=%d\n",
720 code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
721 if (code != vcp->diskSize) {
722 /* Don't force volume offline if the inumber is out of
723 * range or the inode table is full.
725 VOL_LOCK if (code == BAD_IGET) {
726 Log("VPutVnode: bad inumber %s\n",
728 vp->vnodeIndex[class].handle->ih_ino));
731 Log("VPutVnode: Couldn't write vnode %u, volume %u (%s) (error %d)\n", vnp->vnodeNumber, V_id(vnp->volumePtr), V_name(vnp->volumePtr), code);
735 VOL_UNLOCK FDH_REALLYCLOSE(fdP);
740 /* If the vnode is to be deleted, and we wrote the vnode out,
741 * free its bitmap entry. Do after the vnode is written so we
742 * don't allocate from bitmap before the vnode is written
743 * (doing so could cause a "addled bitmap" message).
745 if (vnp->delete && !*ec) {
746 VFreeBitMapEntry_r(ec, &vp->vnodeIndex[class],
747 vnodeIdToBitNumber(vnp->vnodeNumber));
751 vnp->changed_newTime = vnp->changed_oldTime = 0;
753 } else { /* Not write locked */
754 if (vnp->changed_newTime || vnp->changed_oldTime || vnp->delete)
756 ("VPutVnode: Change or delete flag for vnode 0x%x is set but vnode is not write locked!\n",
760 /* Do not look at disk portion of vnode after this point; it may
761 * have been deleted above */
762 if (vnp->nUsers-- == 1)
763 StickOnLruChain_r(vnp, vcp);
767 ReleaseWriteLock(&vnp->lock);
769 ReleaseReadLock(&vnp->lock);
773 * Make an attempt to convert a vnode lock from write to read.
774 * Do nothing if the vnode isn't write locked or the vnode has
778 VVnodeWriteToRead(Error * ec, register Vnode * vnp)
781 VOL_LOCK retVal = VVnodeWriteToRead_r(ec, vnp);
782 VOL_UNLOCK return retVal;
786 VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
790 struct VnodeClassInfo *vcp;
792 #ifdef AFS_PTHREAD_ENV
793 pthread_t thisProcess;
794 #else /* AFS_PTHREAD_ENV */
796 #endif /* AFS_PTHREAD_ENV */
799 assert(vnp->nUsers != 0);
800 class = vnodeIdToClass(vnp->vnodeNumber);
801 vcp = &VnodeClassInfo[class];
802 assert(vnp->disk.vnodeMagic == vcp->magic);
803 writeLocked = WriteLocked(&vnp->lock);
804 VNLog(300, 2, vnp->vnodeNumber, (afs_int32) vnp);
809 #ifdef AFS_PTHREAD_ENV
810 thisProcess = pthread_self();
811 #else /* AFS_PTHREAD_ENV */
812 LWP_CurrentProcess(&thisProcess);
813 #endif /* AFS_PTHREAD_ENV */
815 VNLog(301, 2, (afs_int32) vnp,
816 ((vnp->changed_newTime) << 1) | ((vnp->
817 changed_oldTime) << 1) | vnp->
819 if (thisProcess != vnp->writer)
820 Abort("VPutVnode: Vnode at 0x%x locked by another process!\n", (int)vnp);
824 if (vnp->changed_oldTime || vnp->changed_newTime) {
825 Volume *vp = vnp->volumePtr;
826 afs_uint32 now = FT_ApproxTime();
827 assert(vnp->cacheCheck == vp->cacheCheck);
828 if (vnp->changed_newTime)
829 vnp->disk.serverModifyTime = now;
830 if (vnp->changed_newTime)
831 V_updateDate(vp) = vp->updateTime = now;
833 /* The inode has been changed. Write it out to disk */
835 assert(V_needsSalvaged(vp));
838 IHandle_t *ihP = vp->vnodeIndex[class].handle;
840 off_t off = vnodeIndexOffset(vcp, vnp->vnodeNumber);
841 VOL_UNLOCK fdP = IH_OPEN(ihP);
843 Abort("VPutVnode: can't open index file!\n");
844 code = FDH_SEEK(fdP, off, SEEK_SET);
846 Abort("VPutVnode: can't seek on index file!\n");
847 code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
848 if (code != vcp->diskSize) {
850 * Don't force volume offline if the inumber is out of
851 * range or the inode table is full.
853 VOL_LOCK if (code == BAD_IGET) {
854 Log("VPutVnode: bad inumber %s\n",
855 PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
858 Log("VPutVnode: Couldn't write vnode %u, volume %u (%s)\n", vnp->vnodeNumber, V_id(vnp->volumePtr), V_name(vnp->volumePtr));
866 vnp->changed_newTime = vnp->changed_oldTime = 0;
869 ConvertWriteToReadLock(&vnp->lock);
873 /* Move the vnode, vnp, to the new hash table given by the
874 hash table index, newHash */
876 moveHash(register Vnode * vnp, bit32 newHash)
879 /* Remove it from the old hash chain */
880 tvnp = VnodeHashTable[vnp->hashIndex];
882 VnodeHashTable[vnp->hashIndex] = vnp->hashNext;
884 while (tvnp && tvnp->hashNext != vnp)
885 tvnp = tvnp->hashNext;
887 tvnp->hashNext = vnp->hashNext;
889 /* Add it to the new hash chain */
890 vnp->hashNext = VnodeHashTable[newHash];
891 VnodeHashTable[newHash] = vnp;
892 vnp->hashIndex = newHash;
897 StickOnLruChain_r(register Vnode * vnp, register struct VnodeClassInfo *vcp)
899 /* Add it to the circular LRU list */
900 if (vcp->lruHead == NULL)
901 Abort("VPutVnode: vcp->lruHead==NULL");
903 vnp->lruNext = vcp->lruHead;
904 vnp->lruPrev = vcp->lruHead->lruPrev;
905 vcp->lruHead->lruPrev = vnp;
906 vnp->lruPrev->lruNext = vnp;
909 /* If the vnode was just deleted, put it at the end of the chain so it
910 * will be reused immediately */
912 vcp->lruHead = vnp->lruNext;
913 /* If caching is turned off, set volumeptr to NULL to invalidate the
915 if (!TrustVnodeCacheEntry)
916 vnp->volumePtr = NULL;
919 /* VCloseVnodeFiles - called when a volume is going off line. All open
920 * files for vnodes in that volume are closed. This might be excessive,
921 * since we may only be taking one volume of a volume group offline.
924 VCloseVnodeFiles_r(Volume * vp)
929 for (i = 0; i < VNODE_HASH_TABLE_SIZE; i++) {
930 for (vnp = VnodeHashTable[i]; vnp; vnp = vnp->hashNext) {
931 if (vnp->volumePtr == vp) {
932 IH_REALLYCLOSE(vnp->handle);
938 /* VReleaseVnodeFiles - called when a volume is going detached. All open
939 * files for vnodes in that volume are closed and all inode handles
940 * for vnodes in that volume are released.
943 VReleaseVnodeFiles_r(Volume * vp)
948 for (i = 0; i < VNODE_HASH_TABLE_SIZE; i++) {
949 for (vnp = VnodeHashTable[i]; vnp; vnp = vnp->hashNext) {
950 if (vnp->volumePtr == vp) {
951 IH_RELEASE(vnp->handle);