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
9 * Portions Copyright (c) 2006 Sine Nomine Associates
15 Institution: The Information Technology Center, Carnegie-Mellon University
18 #include <afsconfig.h>
19 #include <afs/param.h>
20 #define MAXINT (~(1<<((sizeof(int)*8)-1)))
28 #ifdef AFS_PTHREAD_ENV
30 #else /* AFS_PTHREAD_ENV */
31 #include <afs/assert.h>
32 #endif /* AFS_PTHREAD_ENV */
35 #include <afs/afsint.h>
37 #include <afs/errors.h>
40 #include <afs/afssyscalls.h>
44 #include "partition.h"
46 #if defined(AFS_SGI_ENV)
47 #include "sys/types.h"
59 #include <sys/fcntl.h>
62 #endif /* AFS_NT40_ENV */
65 /*@printflike@*/ extern void Log(const char *format, ...);
67 /*@printflike@*/ extern void Abort(const char *format, ...);
70 struct VnodeClassInfo VnodeClassInfo[nVNODECLASSES];
72 private int moveHash(register Vnode * vnp, bit32 newHash);
73 private void StickOnLruChain_r(register Vnode * vnp,
74 register struct VnodeClassInfo *vcp);
76 #define BAD_IGET -1000
78 /* There are two separate vnode queue types defined here:
79 * Each hash conflict chain -- is singly linked, with a single head
80 * pointer. New entries are added at the beginning. Old
81 * entries are removed by linear search, which generally
82 * only occurs after a disk read).
83 * LRU chain -- is doubly linked, single head pointer.
84 * Entries are added at the head, reclaimed from the tail,
85 * or removed from anywhere in the queue.
89 /* Vnode hash table. Find hash chain by taking lower bits of
90 * (volume_hash_offset + vnode).
91 * This distributes the root inodes of the volumes over the
92 * hash table entries and also distributes the vnodes of
93 * volumes reasonably fairly. The volume_hash_offset field
94 * for each volume is established as the volume comes on line
95 * by using the VOLUME_HASH_OFFSET macro. This distributes the
96 * volumes fairly among the cache entries, both when servicing
97 * a small number of volumes and when servicing a large number.
100 /* logging stuff for finding bugs */
101 #define THELOGSIZE 5120
102 static afs_int32 theLog[THELOGSIZE];
103 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;
109 register afs_int32 temp;
112 /* copy data to array */
118 anparms = 4; /* do bounds checking */
120 temp = (aop << 16) | anparms;
121 theLog[vnLogPtr++] = temp;
122 if (vnLogPtr >= THELOGSIZE)
124 for (temp = 0; temp < anparms; temp++) {
125 theLog[vnLogPtr++] = data[temp];
126 if (vnLogPtr >= THELOGSIZE)
131 /* VolumeHashOffset -- returns a new value to be stored in the
132 * volumeHashOffset of a Volume structure. Called when a
133 * volume is initialized. Sets the volumeHashOffset so that
134 * vnode cache entries are distributed reasonably between
135 * volumes (the root vnodes of the volumes will hash to
136 * different values, and spacing is maintained between volumes
137 * when there are not many volumes represented), and spread
138 * equally amongst vnodes within a single volume.
141 VolumeHashOffset_r(void)
143 static int nextVolumeHashOffset = 0;
144 /* hashindex Must be power of two in size */
146 # define hashMask ((1<<hashShift)-1)
147 static byte hashindex[1 << hashShift] =
148 { 0, 128, 64, 192, 32, 160, 96, 224 };
150 offset = hashindex[nextVolumeHashOffset & hashMask]
151 + (nextVolumeHashOffset >> hashShift);
152 nextVolumeHashOffset++;
156 /* Change hashindex (above) if you change this constant */
157 #define VNODE_HASH_TABLE_SIZE 256
158 private Vnode *VnodeHashTable[VNODE_HASH_TABLE_SIZE];
159 #define VNODE_HASH(volumeptr,vnodenumber)\
160 ((volumeptr->vnodeHashOffset + vnodenumber)&(VNODE_HASH_TABLE_SIZE-1))
163 * new support to secondarily hash vnodes by volume id
165 #define VNVOLUME_HASH(volumeId) (volumeId&(VolumeHashTable.Mask))
167 #include "rx/rx_queue.h"
168 typedef struct VnodeHashByVolumeChainHead {
169 struct rx_queue queue;
171 /* someday we could put a per-chain lock here... */
172 #ifdef AFS_DEMAND_ATTACH_FS
174 pthread_cond_t chain_busy_cv;
175 #endif /* AFS_DEMAND_ATTACH_FS */
176 } VnodeHashByVolumeChainHead;
177 private VnodeHashByVolumeChainHead *VnodeHashByVolumeTable = NULL;
180 VInitVnHashByVolume(void)
184 VnodeHashByVolumeTable = (VnodeHashByVolumeChainHead *) calloc(VolumeHashTable.Size,
185 sizeof(VnodeHashByVolumeChainHead));
186 assert(VnodeHashByVolumeTable != NULL);
188 for (i=0; i < VolumeHashTable.Size; i++) {
189 queue_Init(&VnodeHashByVolumeTable[i]);
190 #ifdef AFS_DEMAND_ATTACH_FS
191 assert(pthread_cond_init(&VnodeHashByVolumeTable[i].chain_busy_cv, NULL) == 0);
192 #endif /* AFS_DEMAND_ATTACH_FS */
197 AddToVnHashByVolumeTable(register Vnode * vnp)
199 VnodeHashByVolumeChainHead * head;
201 if (queue_IsOnQueue(vnp))
204 head = &VnodeHashByVolumeTable[VNVOLUME_HASH(vnp->volumePtr->hashid)];
206 #ifdef AFS_DEMAND_ATTACH_FS
208 /* if the hash table is busy, wait */
209 assert(pthread_cond_wait(&head->chain_busy_cv, &vol_glock_mutex) == 0);
211 #endif /* AFS_DEMAND_ATTACH_FS */
214 queue_Append(head, vnp);
217 /* for demand-attach, caller MUST hold a ref count on vp */
219 DeleteFromVnHashByVolumeTable(register Vnode * vnp)
221 VnodeHashByVolumeChainHead * head;
223 if (!queue_IsOnQueue(vnp))
226 head = &VnodeHashByVolumeTable[VNVOLUME_HASH(vnp->volumePtr->hashid)];
228 #ifdef AFS_DEMAND_ATTACH_FS
230 /* if the hash table is busy, wait */
231 assert(pthread_cond_wait(&head->chain_busy_cv, &vol_glock_mutex) == 0);
233 #endif /* AFS_DEMAND_ATTACH_FS */
239 /* Code to invalidate a vnode entry. Called when we've damaged a vnode, and want
240 to prevent future VGetVnode's from applying to it. Leaves it in the same hash bucket
241 but that shouldn't be important. */
243 VInvalidateVnode_r(register struct Vnode *avnode)
245 avnode->changed_newTime = 0; /* don't let it get flushed out again */
246 avnode->changed_oldTime = 0;
247 avnode->delete = 0; /* it isn't deleted, erally */
248 avnode->cacheCheck = 0; /* invalid: prevents future vnode searches from working */
251 /* Not normally called by general client; called by volume.c */
253 VInitVnodes(VnodeClass class, int nVnodes)
256 register struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
258 vcp->allocs = vcp->gets = vcp->reads = vcp->writes = 0;
259 vcp->cacheSize = nVnodes;
262 assert(CHECKSIZE_SMALLVNODE);
264 vcp->residentSize = SIZEOF_SMALLVNODE;
265 vcp->diskSize = SIZEOF_SMALLDISKVNODE;
266 vcp->magic = SMALLVNODEMAGIC;
270 vcp->residentSize = SIZEOF_LARGEVNODE;
271 vcp->diskSize = SIZEOF_LARGEDISKVNODE;
272 vcp->magic = LARGEVNODEMAGIC;
276 int s = vcp->diskSize - 1;
286 va = (byte *) calloc(nVnodes, vcp->residentSize);
289 Vnode *vnp = (Vnode *) va;
290 vnp->nUsers = 0; /* no context switches */
291 Lock_Init(&vnp->lock);
292 vnp->changed_oldTime = 0;
293 vnp->changed_newTime = 0;
294 vnp->volumePtr = NULL;
296 vnp->delete = vnp->vnodeNumber = 0;
297 #ifdef AFS_PTHREAD_ENV
298 vnp->writer = (pthread_t) 0;
299 #else /* AFS_PTHREAD_ENV */
300 vnp->writer = (PROCESS) 0;
301 #endif /* AFS_PTHREAD_ENV */
304 if (vcp->lruHead == NULL)
305 vcp->lruHead = vnp->lruNext = vnp->lruPrev = vnp;
307 vnp->lruNext = vcp->lruHead;
308 vnp->lruPrev = vcp->lruHead->lruPrev;
309 vcp->lruHead->lruPrev = vnp;
310 vnp->lruPrev->lruNext = vnp;
313 va += vcp->residentSize;
319 /* allocate an *unused* vnode from the LRU chain, going backwards of course. It shouldn't
320 be necessary to specify that nUsers == 0 since if it is in the list, nUsers
321 should be 0. Things shouldn't be in lruq unless no one is using them. */
323 VGetFreeVnode_r(struct VnodeClassInfo * vcp)
327 vnp = vcp->lruHead->lruPrev;
328 if (vnp->nUsers != 0 || CheckLock(&vnp->lock))
329 Abort("locked vnode in lruq");
330 VNLog(1, 2, vnp->vnodeNumber, (afs_int32) vnp);
331 IH_RELEASE(vnp->handle);
335 static mlkReason = 0;
336 static mlkLastAlloc = 0;
337 static mlkLastOver = 0;
338 static mlkLastDelete = 0;
341 VAllocVnode(Error * ec, Volume * vp, VnodeType type)
345 retVal = VAllocVnode_r(ec, vp, type);
351 VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
355 int newHash, bitNumber;
356 register struct VnodeClassInfo *vcp;
361 if (programType == fileServer && !V_inUse(vp)) {
362 if (vp->specialStatus) {
363 *ec = vp->specialStatus;
369 class = vnodeTypeToClass(type);
370 vcp = &VnodeClassInfo[class];
372 if (!VolumeWriteable(vp)) {
373 *ec = (bit32) VREADONLY;
377 unique = vp->nextVnodeUnique++;
379 unique = vp->nextVnodeUnique++;
381 if (vp->nextVnodeUnique > V_uniquifier(vp)) {
382 VUpdateVolume_r(ec, vp, VOL_UPDATE_WAIT);
387 if (programType == fileServer) {
388 VAddToVolumeUpdateList_r(ec, vp);
393 /* Find a slot in the bit map */
394 bitNumber = VAllocBitmapEntry_r(ec, vp, &vp->vnodeIndex[class],
395 VOL_ALLOC_BITMAP_WAIT);
398 vnodeNumber = bitNumberToVnodeNumber(bitNumber, class);
401 VNLog(2, 1, vnodeNumber);
402 /* Prepare to move it to the new hash chain */
403 newHash = VNODE_HASH(vp, vnodeNumber);
404 for (vnp = VnodeHashTable[newHash];
405 vnp && (vnp->vnodeNumber != vnodeNumber || vnp->volumePtr != vp
406 || vnp->volumePtr->cacheCheck != vnp->cacheCheck);
407 vnp = vnp->hashNext);
409 /* slot already exists. May even not be in lruq (consider store file locking a file being deleted)
410 * so we may have to wait for it below */
411 VNLog(3, 2, vnodeNumber, (afs_int32) vnp);
413 /* If first user, remove it from the LRU chain. We can assume that
414 * there is at least one item in the queue */
415 if (++vnp->nUsers == 1) {
416 if (vnp == vcp->lruHead)
417 vcp->lruHead = vcp->lruHead->lruNext;
418 vnp->lruPrev->lruNext = vnp->lruNext;
419 vnp->lruNext->lruPrev = vnp->lruPrev;
420 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
421 Abort("VGetVnode: lru chain addled!\n");
422 /* This won't block */
423 ObtainWriteLock(&vnp->lock);
425 /* follow locking hierarchy */
427 ObtainWriteLock(&vnp->lock);
429 if (vnp->volumePtr->cacheCheck != vnp->cacheCheck) {
430 ReleaseWriteLock(&vnp->lock);
434 #ifdef AFS_PTHREAD_ENV
435 vnp->writer = pthread_self();
436 #else /* AFS_PTHREAD_ENV */
437 LWP_CurrentProcess(&vnp->writer);
438 #endif /* AFS_PTHREAD_ENV */
440 vnp = VGetFreeVnode_r(vcp);
441 /* Remove vnode from LRU chain and grab a write lock */
442 if (vnp == vcp->lruHead)
443 vcp->lruHead = vcp->lruHead->lruNext;
444 vnp->lruPrev->lruNext = vnp->lruNext;
445 vnp->lruNext->lruPrev = vnp->lruPrev;
446 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
447 Abort("VGetVnode: lru chain addled!\n");
448 /* Initialize the header fields so noone allocates another
449 * vnode with the same number */
450 vnp->vnodeNumber = vnodeNumber;
452 vnp->cacheCheck = vp->cacheCheck;
454 /* This will never block */
455 ObtainWriteLock(&vnp->lock);
456 #ifdef AFS_PTHREAD_ENV
457 vnp->writer = pthread_self();
458 #else /* AFS_PTHREAD_ENV */
459 LWP_CurrentProcess(&vnp->writer);
460 #endif /* AFS_PTHREAD_ENV */
461 /* Sanity check: is this vnode really not in use? */
464 IHandle_t *ihP = vp->vnodeIndex[class].handle;
466 off_t off = vnodeIndexOffset(vcp, vnodeNumber);
468 /* XXX we have a potential race here if two threads
469 * allocate new vnodes at the same time, and they
470 * both decide it's time to extend the index
476 Log("VAllocVnode: can't open index file!\n");
477 goto error_encountered;
479 if ((size = FDH_SIZE(fdP)) < 0) {
480 Log("VAllocVnode: can't stat index file!\n");
481 goto error_encountered;
483 if (FDH_SEEK(fdP, off, SEEK_SET) < 0) {
484 Log("VAllocVnode: can't seek on index file!\n");
485 goto error_encountered;
487 if (off + vcp->diskSize <= size) {
488 if (FDH_READ(fdP, &vnp->disk, vcp->diskSize) != vcp->diskSize) {
489 Log("VAllocVnode: can't read index file!\n");
490 goto error_encountered;
492 if (vnp->disk.type != vNull) {
493 Log("VAllocVnode: addled bitmap or index!\n");
494 goto error_encountered;
497 /* growing file - grow in a reasonable increment */
498 char *buf = (char *)malloc(16 * 1024);
500 Abort("VAllocVnode: malloc failed\n");
501 memset(buf, 0, 16 * 1024);
502 (void)FDH_WRITE(fdP, buf, 16 * 1024);
511 #ifdef AFS_DEMAND_ATTACH_FS
513 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
516 VInvalidateVnode_r(vnp);
517 StickOnLruChain_r(vnp, vcp);
525 VNLog(4, 2, vnodeNumber, (afs_int32) vnp);
526 AddToVnHashByVolumeTable(vnp);
527 moveHash(vnp, newHash);
530 VNLog(5, 1, (afs_int32) vnp);
531 #ifdef AFS_PTHREAD_ENV
532 vnp->writer = pthread_self();
533 #else /* AFS_PTHREAD_ENV */
534 LWP_CurrentProcess(&vnp->writer);
535 #endif /* AFS_PTHREAD_ENV */
536 memset(&vnp->disk, 0, sizeof(vnp->disk));
537 vnp->changed_newTime = 0; /* set this bit when vnode is updated */
538 vnp->changed_oldTime = 0; /* set this on CopyOnWrite. */
540 vnp->disk.vnodeMagic = vcp->magic;
541 vnp->disk.type = type;
542 vnp->disk.uniquifier = unique;
545 vp->header->diskstuff.filecount++;
550 VGetVnode(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
551 { /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
554 retVal = VGetVnode_r(ec, vp, vnodeNumber, locktype);
560 VGetVnode_r(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
561 { /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
565 struct VnodeClassInfo *vcp;
568 mlkReason = 0; /* last call didn't fail */
570 if (vnodeNumber == 0) {
576 VNLog(100, 1, vnodeNumber);
577 if (programType == fileServer && !V_inUse(vp)) {
578 *ec = (vp->specialStatus ? vp->specialStatus : VOFFLINE);
580 /* If the volume is VBUSY (being cloned or dumped) and this is
581 * a READ operation, then don't fail.
583 if ((*ec != VBUSY) || (locktype != READ_LOCK)) {
589 class = vnodeIdToClass(vnodeNumber);
590 vcp = &VnodeClassInfo[class];
591 if (locktype == WRITE_LOCK && !VolumeWriteable(vp)) {
592 *ec = (bit32) VREADONLY;
597 if (locktype == WRITE_LOCK && programType == fileServer) {
598 VAddToVolumeUpdateList_r(ec, vp);
600 mlkReason = 1000 + *ec;
605 /* See whether the vnode is in the cache. */
606 newHash = VNODE_HASH(vp, vnodeNumber);
607 for (vnp = VnodeHashTable[newHash];
608 vnp && (vnp->vnodeNumber != vnodeNumber || vnp->volumePtr != vp
609 || vnp->volumePtr->cacheCheck != vnp->cacheCheck);
610 vnp = vnp->hashNext);
614 IHandle_t *ihP = vp->vnodeIndex[class].handle;
616 /* Not in cache; tentatively grab most distantly used one from the LRU
619 vnp = VGetFreeVnode_r(vcp);
620 /* Remove it from the old hash chain */
622 DeleteFromVnHashByVolumeTable(vnp);
623 moveHash(vnp, newHash);
624 /* Remove it from the LRU chain */
625 if (vnp == vcp->lruHead)
626 vcp->lruHead = vcp->lruHead->lruNext;
627 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
628 Abort("VGetVnode: lru chain addled!\n");
629 vnp->lruPrev->lruNext = vnp->lruNext;
630 vnp->lruNext->lruPrev = vnp->lruPrev;
632 vnp->changed_newTime = vnp->changed_oldTime = 0;
634 vnp->vnodeNumber = vnodeNumber;
636 vnp->cacheCheck = vp->cacheCheck;
638 AddToVnHashByVolumeTable(vnp);
640 /* This will never block */
641 ObtainWriteLock(&vnp->lock);
642 #ifdef AFS_PTHREAD_ENV
643 vnp->writer = pthread_self();
644 #else /* AFS_PTHREAD_ENV */
645 LWP_CurrentProcess(&vnp->writer);
646 #endif /* AFS_PTHREAD_ENV */
648 /* Read vnode from volume index */
652 Log("VGetVnode: can't open index dev=%u, i=%s\n", vp->device,
653 PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
654 #ifdef AFS_DEMAND_ATTACH_FS
656 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
661 } else if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, vnodeNumber), SEEK_SET)
663 Log("VGetVnode: can't seek on index file vn=%u\n", vnodeNumber);
664 #ifdef AFS_DEMAND_ATTACH_FS
666 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
671 FDH_REALLYCLOSE(fdP);
672 } else if ((n = FDH_READ(fdP, (char *)&vnp->disk, vcp->diskSize))
674 /* Don't take volume off line if the inumber is out of range
675 * or the inode table is full. */
676 FDH_REALLYCLOSE(fdP);
679 Log("VGetVnode: bad inumber %s\n",
680 PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
684 /* Check for disk errors. Anything else just means that the vnode
685 * is not allocated */
686 if (n == -1 && errno == EIO) {
687 Log("VGetVnode: Couldn't read vnode %u, volume %u (%s); volume needs salvage\n", vnodeNumber, V_id(vp), V_name(vp));
688 #ifdef AFS_DEMAND_ATTACH_FS
689 if (programType == fileServer) {
690 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
693 VForceOffline_r(vp, 0);
697 VForceOffline_r(vp, 0);
705 VInvalidateVnode_r(vnp);
706 if (vnp->nUsers-- == 1)
707 StickOnLruChain_r(vnp, vcp);
708 ReleaseWriteLock(&vnp->lock);
713 /* Quick check to see that the data is reasonable */
714 if (vnp->disk.vnodeMagic != vcp->magic || vnp->disk.type == vNull) {
715 if (vnp->disk.type == vNull) {
718 VInvalidateVnode_r(vnp);
719 if (vnp->nUsers-- == 1)
720 StickOnLruChain_r(vnp, vcp);
721 ReleaseWriteLock(&vnp->lock);
722 return NULL; /* The vnode is not allocated */
724 struct vnodeIndex *index = &vp->vnodeIndex[class];
725 unsigned int bitNumber = vnodeIdToBitNumber(vnodeNumber);
726 unsigned int offset = bitNumber >> 3;
728 /* Test to see if vnode number is valid. */
729 if ((offset >= index->bitmapSize)
730 || ((*(index->bitmap + offset) & (1 << (bitNumber & 0x7)))
732 Log("VGetVnode: Request for unallocated vnode %u, volume %u (%s) denied.\n", vnodeNumber, V_id(vp), V_name(vp));
736 Log("VGetVnode: Bad magic number, vnode %u, volume %u (%s); volume needs salvage\n", vnodeNumber, V_id(vp), V_name(vp));
737 #ifdef AFS_DEMAND_ATTACH_FS
738 if (programType == fileServer) {
739 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
742 vp->goingOffline = 1;
746 vp->goingOffline = 1; /* used to call VOffline, but that would mess
747 * up the volume ref count if called here */
752 VInvalidateVnode_r(vnp);
753 if (vnp->nUsers-- == 1)
754 StickOnLruChain_r(vnp, vcp);
755 ReleaseWriteLock(&vnp->lock);
759 IH_INIT(vnp->handle, V_device(vp), V_parentId(vp), VN_GET_INO(vnp));
760 ReleaseWriteLock(&vnp->lock);
762 VNLog(101, 2, vnodeNumber, (afs_int32) vnp);
763 if (++vnp->nUsers == 1) {
764 /* First user. Remove it from the LRU chain. We can assume that
765 * there is at least one item in the queue */
766 if (vnp == vcp->lruHead)
767 vcp->lruHead = vcp->lruHead->lruNext;
768 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
769 Abort("VGetVnode: lru chain addled!\n");
770 vnp->lruPrev->lruNext = vnp->lruNext;
771 vnp->lruNext->lruPrev = vnp->lruPrev;
775 if (locktype == READ_LOCK)
776 ObtainReadLock(&vnp->lock);
778 ObtainWriteLock(&vnp->lock);
779 #ifdef AFS_PTHREAD_ENV
780 vnp->writer = pthread_self();
781 #else /* AFS_PTHREAD_ENV */
782 LWP_CurrentProcess(&vnp->writer);
783 #endif /* AFS_PTHREAD_ENV */
786 /* Check that the vnode hasn't been removed while we were obtaining
788 VNLog(102, 2, vnodeNumber, (afs_int32) vnp);
789 if ((vnp->disk.type == vNull) || (vnp->cacheCheck == 0)) {
790 if (vnp->nUsers-- == 1)
791 StickOnLruChain_r(vnp, vcp);
792 if (locktype == READ_LOCK)
793 ReleaseReadLock(&vnp->lock);
795 ReleaseWriteLock(&vnp->lock);
798 /* vnode is labelled correctly by now, so we don't have to invalidate it */
801 if (programType == fileServer)
802 VBumpVolumeUsage_r(vnp->volumePtr); /* Hack; don't know where it should be
803 * called from. Maybe VGetVolume */
808 int TrustVnodeCacheEntry = 1;
809 /* This variable is bogus--when it's set to 0, the hash chains fill
810 up with multiple versions of the same vnode. Should fix this!! */
812 VPutVnode(Error * ec, register Vnode * vnp)
815 VPutVnode_r(ec, vnp);
820 VPutVnode_r(Error * ec, register Vnode * vnp)
822 int writeLocked, offset;
824 struct VnodeClassInfo *vcp;
828 assert(vnp->nUsers != 0);
829 class = vnodeIdToClass(vnp->vnodeNumber);
830 vcp = &VnodeClassInfo[class];
831 assert(vnp->disk.vnodeMagic == vcp->magic);
832 VNLog(200, 2, vnp->vnodeNumber, (afs_int32) vnp);
834 writeLocked = WriteLocked(&vnp->lock);
836 #ifdef AFS_PTHREAD_ENV
837 pthread_t thisProcess = pthread_self();
838 #else /* AFS_PTHREAD_ENV */
840 LWP_CurrentProcess(&thisProcess);
841 #endif /* AFS_PTHREAD_ENV */
842 VNLog(201, 2, (afs_int32) vnp,
843 ((vnp->changed_newTime) << 1) | ((vnp->
844 changed_oldTime) << 1) | vnp->
846 if (thisProcess != vnp->writer)
847 Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",
849 if (vnp->changed_oldTime || vnp->changed_newTime || vnp->delete) {
850 Volume *vp = vnp->volumePtr;
851 afs_uint32 now = FT_ApproxTime();
852 assert(vnp->cacheCheck == vp->cacheCheck);
855 /* No longer any directory entries for this vnode. Free the Vnode */
856 memset(&vnp->disk, 0, sizeof(vnp->disk));
857 mlkLastDelete = vnp->vnodeNumber;
858 /* delete flag turned off further down */
859 VNLog(202, 2, vnp->vnodeNumber, (afs_int32) vnp);
860 } else if (vnp->changed_newTime) {
861 vnp->disk.serverModifyTime = now;
863 if (vnp->changed_newTime)
865 V_updateDate(vp) = vp->updateTime = now;
866 if(V_volUpCounter(vp)<MAXINT)
867 V_volUpCounter(vp)++;
870 /* The vnode has been changed. Write it out to disk */
872 #ifdef AFS_DEMAND_ATTACH_FS
873 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
876 assert(V_needsSalvaged(vp));
880 IHandle_t *ihP = vp->vnodeIndex[class].handle;
885 Log("VPutVnode: can't open index file!\n");
886 goto error_encountered;
888 offset = vnodeIndexOffset(vcp, vnp->vnodeNumber);
889 if (FDH_SEEK(fdP, offset, SEEK_SET) < 0) {
890 Log("VPutVnode: can't seek on index file! fdp=0x%x offset=%d, errno=%d\n",
892 goto error_encountered;
894 code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
895 if (code != vcp->diskSize) {
896 /* Don't force volume offline if the inumber is out of
897 * range or the inode table is full.
900 if (code == BAD_IGET) {
901 Log("VPutVnode: bad inumber %s\n",
903 vp->vnodeIndex[class].handle->ih_ino));
906 Log("VPutVnode: Couldn't write vnode %u, volume %u (%s) (error %d)\n", vnp->vnodeNumber, V_id(vnp->volumePtr), V_name(vnp->volumePtr), code);
907 #ifdef AFS_DEMAND_ATTACH_FS
908 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
911 VForceOffline_r(vp, 0);
916 FDH_REALLYCLOSE(fdP);
924 #ifdef AFS_DEMAND_ATTACH_FS
925 /* XXX instead of dumping core, let's try to request a salvage
926 * and just fail the putvnode */
930 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
938 /* If the vnode is to be deleted, and we wrote the vnode out,
939 * free its bitmap entry. Do after the vnode is written so we
940 * don't allocate from bitmap before the vnode is written
941 * (doing so could cause a "addled bitmap" message).
943 if (vnp->delete && !*ec) {
944 if (vnp->volumePtr->header->diskstuff.filecount-- < 1)
945 vnp->volumePtr->header->diskstuff.filecount = 0;
946 VFreeBitMapEntry_r(ec, &vp->vnodeIndex[class],
947 vnodeIdToBitNumber(vnp->vnodeNumber));
951 vnp->changed_newTime = vnp->changed_oldTime = 0;
953 } else { /* Not write locked */
954 if (vnp->changed_newTime || vnp->changed_oldTime || vnp->delete)
956 ("VPutVnode: Change or delete flag for vnode 0x%x is set but vnode is not write locked!\n",
961 /* Do not look at disk portion of vnode after this point; it may
962 * have been deleted above */
963 if (vnp->nUsers-- == 1)
964 StickOnLruChain_r(vnp, vcp);
968 ReleaseWriteLock(&vnp->lock);
970 ReleaseReadLock(&vnp->lock);
974 * Make an attempt to convert a vnode lock from write to read.
975 * Do nothing if the vnode isn't write locked or the vnode has
979 VVnodeWriteToRead(Error * ec, register Vnode * vnp)
983 retVal = VVnodeWriteToRead_r(ec, vnp);
989 VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
993 struct VnodeClassInfo *vcp;
995 #ifdef AFS_PTHREAD_ENV
996 pthread_t thisProcess;
997 #else /* AFS_PTHREAD_ENV */
999 #endif /* AFS_PTHREAD_ENV */
1002 assert(vnp->nUsers != 0);
1003 class = vnodeIdToClass(vnp->vnodeNumber);
1004 vcp = &VnodeClassInfo[class];
1005 assert(vnp->disk.vnodeMagic == vcp->magic);
1006 writeLocked = WriteLocked(&vnp->lock);
1007 VNLog(300, 2, vnp->vnodeNumber, (afs_int32) vnp);
1012 #ifdef AFS_PTHREAD_ENV
1013 thisProcess = pthread_self();
1014 #else /* AFS_PTHREAD_ENV */
1015 LWP_CurrentProcess(&thisProcess);
1016 #endif /* AFS_PTHREAD_ENV */
1018 VNLog(301, 2, (afs_int32) vnp,
1019 ((vnp->changed_newTime) << 1) | ((vnp->
1020 changed_oldTime) << 1) | vnp->
1022 if (thisProcess != vnp->writer)
1023 Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",
1028 if (vnp->changed_oldTime || vnp->changed_newTime) {
1029 Volume *vp = vnp->volumePtr;
1030 afs_uint32 now = FT_ApproxTime();
1031 assert(vnp->cacheCheck == vp->cacheCheck);
1032 if (vnp->changed_newTime)
1033 vnp->disk.serverModifyTime = now;
1034 if (vnp->changed_newTime)
1035 V_updateDate(vp) = vp->updateTime = now;
1037 /* The inode has been changed. Write it out to disk */
1039 #ifdef AFS_DEMAND_ATTACH_FS
1040 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
1043 assert(V_needsSalvaged(vp));
1047 IHandle_t *ihP = vp->vnodeIndex[class].handle;
1049 off_t off = vnodeIndexOffset(vcp, vnp->vnodeNumber);
1053 Log("VPutVnode: can't open index file!\n");
1054 goto error_encountered;
1056 code = FDH_SEEK(fdP, off, SEEK_SET);
1058 Log("VPutVnode: can't seek on index file!\n");
1059 goto error_encountered;
1061 code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
1062 if (code != vcp->diskSize) {
1064 * Don't force volume offline if the inumber is out of
1065 * range or the inode table is full.
1068 if (code == BAD_IGET) {
1069 Log("VPutVnode: bad inumber %s\n",
1071 vp->vnodeIndex[class].handle->ih_ino));
1074 Log("VPutVnode: Couldn't write vnode %u, volume %u (%s)\n", vnp->vnodeNumber, V_id(vnp->volumePtr), V_name(vnp->volumePtr));
1075 #ifdef AFS_DEMAND_ATTACH_FS
1076 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
1079 VForceOffline_r(vp, 0);
1090 #ifdef AFS_DEMAND_ATTACH_FS
1094 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
1103 vnp->changed_newTime = vnp->changed_oldTime = 0;
1106 ConvertWriteToReadLock(&vnp->lock);
1110 /* Move the vnode, vnp, to the new hash table given by the
1111 hash table index, newHash */
1113 moveHash(register Vnode * vnp, bit32 newHash)
1116 /* Remove it from the old hash chain */
1117 tvnp = VnodeHashTable[vnp->hashIndex];
1119 VnodeHashTable[vnp->hashIndex] = vnp->hashNext;
1121 while (tvnp && tvnp->hashNext != vnp)
1122 tvnp = tvnp->hashNext;
1124 tvnp->hashNext = vnp->hashNext;
1126 /* Add it to the new hash chain */
1127 vnp->hashNext = VnodeHashTable[newHash];
1128 VnodeHashTable[newHash] = vnp;
1129 vnp->hashIndex = newHash;
1134 StickOnLruChain_r(register Vnode * vnp, register struct VnodeClassInfo *vcp)
1136 /* Add it to the circular LRU list */
1137 if (vcp->lruHead == NULL)
1138 Abort("VPutVnode: vcp->lruHead==NULL");
1140 vnp->lruNext = vcp->lruHead;
1141 vnp->lruPrev = vcp->lruHead->lruPrev;
1142 vcp->lruHead->lruPrev = vnp;
1143 vnp->lruPrev->lruNext = vnp;
1146 /* If the vnode was just deleted, put it at the end of the chain so it
1147 * will be reused immediately */
1149 vcp->lruHead = vnp->lruNext;
1150 /* If caching is turned off, set volumeptr to NULL to invalidate the
1152 if (!TrustVnodeCacheEntry) {
1153 DeleteFromVnHashByVolumeTable(vnp);
1154 vnp->volumePtr = NULL;
1158 /* VCloseVnodeFiles - called when a volume is going off line. All open
1159 * files for vnodes in that volume are closed. This might be excessive,
1160 * since we may only be taking one volume of a volume group offline.
1163 VCloseVnodeFiles_r(Volume * vp)
1167 VnodeHashByVolumeChainHead * head;
1169 head = &VnodeHashByVolumeTable[VNVOLUME_HASH(vp->hashid)];
1170 #ifdef AFS_DEMAND_ATTACH_FS
1171 while (head->busy) {
1172 assert(pthread_cond_wait(&head->chain_busy_cv, &vol_glock_mutex) == 0);
1177 #endif /* AFS_DEMAND_ATTACH_FS */
1179 for (queue_Scan(head, vnp, nvnp, Vnode)) {
1180 if (vnp->volumePtr == vp) {
1181 IH_REALLYCLOSE(vnp->handle);
1185 #ifdef AFS_DEMAND_ATTACH_FS
1188 assert(pthread_cond_broadcast(&head->chain_busy_cv) == 0);
1189 #endif /* AFS_DEMAND_ATTACH_FS */
1192 /* VReleaseVnodeFiles - called when a volume is going detached. All open
1193 * files for vnodes in that volume are closed and all inode handles
1194 * for vnodes in that volume are released.
1197 VReleaseVnodeFiles_r(Volume * vp)
1201 VnodeHashByVolumeChainHead * head;
1203 head = &VnodeHashByVolumeTable[VNVOLUME_HASH(vp->hashid)];
1205 #ifdef AFS_DEMAND_ATTACH_FS
1206 while (head->busy) {
1207 assert(pthread_cond_wait(&head->chain_busy_cv, &vol_glock_mutex) == 0);
1212 #endif /* AFS_DEMAND_ATTACH_FS */
1214 for (queue_Scan(head, vnp, nvnp, Vnode)) {
1215 if (vnp->volumePtr == vp) {
1216 IH_RELEASE(vnp->handle);
1220 #ifdef AFS_DEMAND_ATTACH_FS
1223 assert(pthread_cond_broadcast(&head->chain_busy_cv) == 0);
1224 #endif /* AFS_DEMAND_ATTACH_FS */