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)))
34 #ifdef AFS_PTHREAD_ENV
36 #else /* AFS_PTHREAD_ENV */
37 #include <afs/assert.h>
38 #endif /* AFS_PTHREAD_ENV */
41 #include <afs/afsint.h>
43 #include <afs/errors.h>
46 #include <afs/afssyscalls.h>
50 #include "partition.h"
52 #if defined(AFS_SGI_ENV)
53 #include "sys/types.h"
65 #include <sys/fcntl.h>
68 #endif /* AFS_NT40_ENV */
71 /*@printflike@*/ extern void Log(const char *format, ...);
73 /*@printflike@*/ extern void Abort(const char *format, ...);
76 struct VnodeClassInfo VnodeClassInfo[nVNODECLASSES];
78 private int moveHash(register Vnode * vnp, bit32 newHash);
79 private void StickOnLruChain_r(register Vnode * vnp,
80 register struct VnodeClassInfo *vcp);
82 #define BAD_IGET -1000
84 /* There are two separate vnode queue types defined here:
85 * Each hash conflict chain -- is singly linked, with a single head
86 * pointer. New entries are added at the beginning. Old
87 * entries are removed by linear search, which generally
88 * only occurs after a disk read).
89 * LRU chain -- is doubly linked, single head pointer.
90 * Entries are added at the head, reclaimed from the tail,
91 * or removed from anywhere in the queue.
95 /* Vnode hash table. Find hash chain by taking lower bits of
96 * (volume_hash_offset + vnode).
97 * This distributes the root inodes of the volumes over the
98 * hash table entries and also distributes the vnodes of
99 * volumes reasonably fairly. The volume_hash_offset field
100 * for each volume is established as the volume comes on line
101 * by using the VOLUME_HASH_OFFSET macro. This distributes the
102 * volumes fairly among the cache entries, both when servicing
103 * a small number of volumes and when servicing a large number.
106 /* logging stuff for finding bugs */
107 #define THELOGSIZE 5120
108 static afs_int32 theLog[THELOGSIZE];
109 static afs_int32 vnLogPtr = 0;
111 VNLog(aop, anparms, av1, av2, av3, av4)
112 afs_int32 aop, anparms;
113 afs_int32 av1, av2, av3, av4;
115 register afs_int32 temp;
118 /* copy data to array */
124 anparms = 4; /* do bounds checking */
126 temp = (aop << 16) | anparms;
127 theLog[vnLogPtr++] = temp;
128 if (vnLogPtr >= THELOGSIZE)
130 for (temp = 0; temp < anparms; temp++) {
131 theLog[vnLogPtr++] = data[temp];
132 if (vnLogPtr >= THELOGSIZE)
137 /* VolumeHashOffset -- returns a new value to be stored in the
138 * volumeHashOffset of a Volume structure. Called when a
139 * volume is initialized. Sets the volumeHashOffset so that
140 * vnode cache entries are distributed reasonably between
141 * volumes (the root vnodes of the volumes will hash to
142 * different values, and spacing is maintained between volumes
143 * when there are not many volumes represented), and spread
144 * equally amongst vnodes within a single volume.
147 VolumeHashOffset_r(void)
149 static int nextVolumeHashOffset = 0;
150 /* hashindex Must be power of two in size */
152 # define hashMask ((1<<hashShift)-1)
153 static byte hashindex[1 << hashShift] =
154 { 0, 128, 64, 192, 32, 160, 96, 224 };
156 offset = hashindex[nextVolumeHashOffset & hashMask]
157 + (nextVolumeHashOffset >> hashShift);
158 nextVolumeHashOffset++;
162 /* Change hashindex (above) if you change this constant */
163 #define VNODE_HASH_TABLE_SIZE 256
164 private Vnode *VnodeHashTable[VNODE_HASH_TABLE_SIZE];
165 #define VNODE_HASH(volumeptr,vnodenumber)\
166 ((volumeptr->vnodeHashOffset + vnodenumber)&(VNODE_HASH_TABLE_SIZE-1))
169 * new support to secondarily hash vnodes by volume id
171 #define VNVOLUME_HASH(volumeId) (volumeId&(VolumeHashTable.Mask))
173 #include "rx/rx_queue.h"
174 typedef struct VnodeHashByVolumeChainHead {
175 struct rx_queue queue;
177 /* someday we could put a per-chain lock here... */
178 #ifdef AFS_DEMAND_ATTACH_FS
180 pthread_cond_t chain_busy_cv;
181 #endif /* AFS_DEMAND_ATTACH_FS */
182 } VnodeHashByVolumeChainHead;
183 private VnodeHashByVolumeChainHead *VnodeHashByVolumeTable = NULL;
186 VInitVnHashByVolume(void)
190 VnodeHashByVolumeTable = (VnodeHashByVolumeChainHead *) calloc(VolumeHashTable.Size,
191 sizeof(VnodeHashByVolumeChainHead));
192 assert(VnodeHashByVolumeTable != NULL);
194 for (i=0; i < VolumeHashTable.Size; i++) {
195 queue_Init(&VnodeHashByVolumeTable[i]);
196 #ifdef AFS_DEMAND_ATTACH_FS
197 assert(pthread_cond_init(&VnodeHashByVolumeTable[i].chain_busy_cv, NULL) == 0);
198 #endif /* AFS_DEMAND_ATTACH_FS */
203 AddToVnHashByVolumeTable(register Vnode * vnp)
205 VnodeHashByVolumeChainHead * head;
207 if (queue_IsOnQueue(vnp))
210 head = &VnodeHashByVolumeTable[VNVOLUME_HASH(vnp->volumePtr->hashid)];
212 #ifdef AFS_DEMAND_ATTACH_FS
214 /* if the hash table is busy, wait */
215 assert(pthread_cond_wait(&head->chain_busy_cv, &vol_glock_mutex) == 0);
217 #endif /* AFS_DEMAND_ATTACH_FS */
220 queue_Append(head, vnp);
223 /* for demand-attach, caller MUST hold a ref count on vp */
225 DeleteFromVnHashByVolumeTable(register Vnode * vnp)
227 VnodeHashByVolumeChainHead * head;
229 if (!queue_IsOnQueue(vnp))
232 head = &VnodeHashByVolumeTable[VNVOLUME_HASH(vnp->volumePtr->hashid)];
234 #ifdef AFS_DEMAND_ATTACH_FS
236 /* if the hash table is busy, wait */
237 assert(pthread_cond_wait(&head->chain_busy_cv, &vol_glock_mutex) == 0);
239 #endif /* AFS_DEMAND_ATTACH_FS */
245 /* Code to invalidate a vnode entry. Called when we've damaged a vnode, and want
246 to prevent future VGetVnode's from applying to it. Leaves it in the same hash bucket
247 but that shouldn't be important. */
249 VInvalidateVnode_r(register struct Vnode *avnode)
251 avnode->changed_newTime = 0; /* don't let it get flushed out again */
252 avnode->changed_oldTime = 0;
253 avnode->delete = 0; /* it isn't deleted, erally */
254 avnode->cacheCheck = 0; /* invalid: prevents future vnode searches from working */
257 /* Not normally called by general client; called by volume.c */
259 VInitVnodes(VnodeClass class, int nVnodes)
262 register struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
264 vcp->allocs = vcp->gets = vcp->reads = vcp->writes = 0;
265 vcp->cacheSize = nVnodes;
268 assert(CHECKSIZE_SMALLVNODE);
270 vcp->residentSize = SIZEOF_SMALLVNODE;
271 vcp->diskSize = SIZEOF_SMALLDISKVNODE;
272 vcp->magic = SMALLVNODEMAGIC;
276 vcp->residentSize = SIZEOF_LARGEVNODE;
277 vcp->diskSize = SIZEOF_LARGEDISKVNODE;
278 vcp->magic = LARGEVNODEMAGIC;
282 int s = vcp->diskSize - 1;
292 va = (byte *) calloc(nVnodes, vcp->residentSize);
295 Vnode *vnp = (Vnode *) va;
296 vnp->nUsers = 0; /* no context switches */
297 Lock_Init(&vnp->lock);
298 vnp->changed_oldTime = 0;
299 vnp->changed_newTime = 0;
300 vnp->volumePtr = NULL;
302 vnp->delete = vnp->vnodeNumber = 0;
303 #ifdef AFS_PTHREAD_ENV
304 vnp->writer = (pthread_t) 0;
305 #else /* AFS_PTHREAD_ENV */
306 vnp->writer = (PROCESS) 0;
307 #endif /* AFS_PTHREAD_ENV */
310 if (vcp->lruHead == NULL)
311 vcp->lruHead = vnp->lruNext = vnp->lruPrev = vnp;
313 vnp->lruNext = vcp->lruHead;
314 vnp->lruPrev = vcp->lruHead->lruPrev;
315 vcp->lruHead->lruPrev = vnp;
316 vnp->lruPrev->lruNext = vnp;
319 va += vcp->residentSize;
325 /* allocate an *unused* vnode from the LRU chain, going backwards of course. It shouldn't
326 be necessary to specify that nUsers == 0 since if it is in the list, nUsers
327 should be 0. Things shouldn't be in lruq unless no one is using them. */
329 VGetFreeVnode_r(struct VnodeClassInfo * vcp)
333 vnp = vcp->lruHead->lruPrev;
334 if (vnp->nUsers != 0 || CheckLock(&vnp->lock))
335 Abort("locked vnode in lruq");
336 VNLog(1, 2, vnp->vnodeNumber, (afs_int32) vnp);
337 IH_RELEASE(vnp->handle);
341 static mlkReason = 0;
342 static mlkLastAlloc = 0;
343 static mlkLastOver = 0;
344 static mlkLastDelete = 0;
347 VAllocVnode(Error * ec, Volume * vp, VnodeType type)
351 retVal = VAllocVnode_r(ec, vp, type);
357 VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
361 int newHash, bitNumber;
362 register struct VnodeClassInfo *vcp;
367 if (programType == fileServer && !V_inUse(vp)) {
368 if (vp->specialStatus) {
369 *ec = vp->specialStatus;
375 class = vnodeTypeToClass(type);
376 vcp = &VnodeClassInfo[class];
378 if (!VolumeWriteable(vp)) {
379 *ec = (bit32) VREADONLY;
383 unique = vp->nextVnodeUnique++;
385 unique = vp->nextVnodeUnique++;
387 if (vp->nextVnodeUnique > V_uniquifier(vp)) {
388 VUpdateVolume_r(ec, vp, VOL_UPDATE_WAIT);
393 if (programType == fileServer) {
394 VAddToVolumeUpdateList_r(ec, vp);
399 /* Find a slot in the bit map */
400 bitNumber = VAllocBitmapEntry_r(ec, vp, &vp->vnodeIndex[class],
401 VOL_ALLOC_BITMAP_WAIT);
404 vnodeNumber = bitNumberToVnodeNumber(bitNumber, class);
407 VNLog(2, 1, vnodeNumber);
408 /* Prepare to move it to the new hash chain */
409 newHash = VNODE_HASH(vp, vnodeNumber);
410 for (vnp = VnodeHashTable[newHash];
411 vnp && (vnp->vnodeNumber != vnodeNumber || vnp->volumePtr != vp
412 || vnp->volumePtr->cacheCheck != vnp->cacheCheck);
413 vnp = vnp->hashNext);
415 /* slot already exists. May even not be in lruq (consider store file locking a file being deleted)
416 * so we may have to wait for it below */
417 VNLog(3, 2, vnodeNumber, (afs_int32) vnp);
419 /* If first user, remove it from the LRU chain. We can assume that
420 * there is at least one item in the queue */
421 if (++vnp->nUsers == 1) {
422 if (vnp == vcp->lruHead)
423 vcp->lruHead = vcp->lruHead->lruNext;
424 vnp->lruPrev->lruNext = vnp->lruNext;
425 vnp->lruNext->lruPrev = vnp->lruPrev;
426 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
427 Abort("VGetVnode: lru chain addled!\n");
428 /* This won't block */
429 ObtainWriteLock(&vnp->lock);
431 /* follow locking hierarchy */
433 ObtainWriteLock(&vnp->lock);
435 if (vnp->volumePtr->cacheCheck != vnp->cacheCheck) {
436 ReleaseWriteLock(&vnp->lock);
440 #ifdef AFS_PTHREAD_ENV
441 vnp->writer = pthread_self();
442 #else /* AFS_PTHREAD_ENV */
443 LWP_CurrentProcess(&vnp->writer);
444 #endif /* AFS_PTHREAD_ENV */
446 vnp = VGetFreeVnode_r(vcp);
447 /* Remove vnode from LRU chain and grab a write lock */
448 if (vnp == vcp->lruHead)
449 vcp->lruHead = vcp->lruHead->lruNext;
450 vnp->lruPrev->lruNext = vnp->lruNext;
451 vnp->lruNext->lruPrev = vnp->lruPrev;
452 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
453 Abort("VGetVnode: lru chain addled!\n");
454 /* Initialize the header fields so noone allocates another
455 * vnode with the same number */
456 vnp->vnodeNumber = vnodeNumber;
458 vnp->cacheCheck = vp->cacheCheck;
460 /* This will never block */
461 ObtainWriteLock(&vnp->lock);
462 #ifdef AFS_PTHREAD_ENV
463 vnp->writer = pthread_self();
464 #else /* AFS_PTHREAD_ENV */
465 LWP_CurrentProcess(&vnp->writer);
466 #endif /* AFS_PTHREAD_ENV */
467 /* Sanity check: is this vnode really not in use? */
470 IHandle_t *ihP = vp->vnodeIndex[class].handle;
472 off_t off = vnodeIndexOffset(vcp, vnodeNumber);
474 /* XXX we have a potential race here if two threads
475 * allocate new vnodes at the same time, and they
476 * both decide it's time to extend the index
482 Log("VAllocVnode: can't open index file!\n");
483 goto error_encountered;
485 if ((size = FDH_SIZE(fdP)) < 0) {
486 Log("VAllocVnode: can't stat index file!\n");
487 goto error_encountered;
489 if (FDH_SEEK(fdP, off, SEEK_SET) < 0) {
490 Log("VAllocVnode: can't seek on index file!\n");
491 goto error_encountered;
493 if (off + vcp->diskSize <= size) {
494 if (FDH_READ(fdP, &vnp->disk, vcp->diskSize) != vcp->diskSize) {
495 Log("VAllocVnode: can't read index file!\n");
496 goto error_encountered;
498 if (vnp->disk.type != vNull) {
499 Log("VAllocVnode: addled bitmap or index!\n");
500 goto error_encountered;
503 /* growing file - grow in a reasonable increment */
504 char *buf = (char *)malloc(16 * 1024);
506 Abort("VAllocVnode: malloc failed\n");
507 memset(buf, 0, 16 * 1024);
508 (void)FDH_WRITE(fdP, buf, 16 * 1024);
517 #ifdef AFS_DEMAND_ATTACH_FS
519 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
522 VInvalidateVnode_r(vnp);
523 StickOnLruChain_r(vnp, vcp);
531 VNLog(4, 2, vnodeNumber, (afs_int32) vnp);
532 AddToVnHashByVolumeTable(vnp);
533 moveHash(vnp, newHash);
536 VNLog(5, 1, (afs_int32) vnp);
537 #ifdef AFS_PTHREAD_ENV
538 vnp->writer = pthread_self();
539 #else /* AFS_PTHREAD_ENV */
540 LWP_CurrentProcess(&vnp->writer);
541 #endif /* AFS_PTHREAD_ENV */
542 memset(&vnp->disk, 0, sizeof(vnp->disk));
543 vnp->changed_newTime = 0; /* set this bit when vnode is updated */
544 vnp->changed_oldTime = 0; /* set this on CopyOnWrite. */
546 vnp->disk.vnodeMagic = vcp->magic;
547 vnp->disk.type = type;
548 vnp->disk.uniquifier = unique;
551 vp->header->diskstuff.filecount++;
556 VGetVnode(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
557 { /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
560 retVal = VGetVnode_r(ec, vp, vnodeNumber, locktype);
566 VGetVnode_r(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
567 { /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
571 struct VnodeClassInfo *vcp;
574 mlkReason = 0; /* last call didn't fail */
576 if (vnodeNumber == 0) {
582 VNLog(100, 1, vnodeNumber);
583 if (programType == fileServer && !V_inUse(vp)) {
584 *ec = (vp->specialStatus ? vp->specialStatus : VOFFLINE);
586 /* If the volume is VBUSY (being cloned or dumped) and this is
587 * a READ operation, then don't fail.
589 if ((*ec != VBUSY) || (locktype != READ_LOCK)) {
595 class = vnodeIdToClass(vnodeNumber);
596 vcp = &VnodeClassInfo[class];
597 if (locktype == WRITE_LOCK && !VolumeWriteable(vp)) {
598 *ec = (bit32) VREADONLY;
603 if (locktype == WRITE_LOCK && programType == fileServer) {
604 VAddToVolumeUpdateList_r(ec, vp);
606 mlkReason = 1000 + *ec;
611 /* See whether the vnode is in the cache. */
612 newHash = VNODE_HASH(vp, vnodeNumber);
613 for (vnp = VnodeHashTable[newHash];
614 vnp && (vnp->vnodeNumber != vnodeNumber || vnp->volumePtr != vp
615 || vnp->volumePtr->cacheCheck != vnp->cacheCheck);
616 vnp = vnp->hashNext);
620 IHandle_t *ihP = vp->vnodeIndex[class].handle;
622 /* Not in cache; tentatively grab most distantly used one from the LRU
625 vnp = VGetFreeVnode_r(vcp);
626 /* Remove it from the old hash chain */
628 DeleteFromVnHashByVolumeTable(vnp);
629 moveHash(vnp, newHash);
630 /* Remove it from the LRU chain */
631 if (vnp == vcp->lruHead)
632 vcp->lruHead = vcp->lruHead->lruNext;
633 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
634 Abort("VGetVnode: lru chain addled!\n");
635 vnp->lruPrev->lruNext = vnp->lruNext;
636 vnp->lruNext->lruPrev = vnp->lruPrev;
638 vnp->changed_newTime = vnp->changed_oldTime = 0;
640 vnp->vnodeNumber = vnodeNumber;
642 vnp->cacheCheck = vp->cacheCheck;
644 AddToVnHashByVolumeTable(vnp);
646 /* This will never block */
647 ObtainWriteLock(&vnp->lock);
648 #ifdef AFS_PTHREAD_ENV
649 vnp->writer = pthread_self();
650 #else /* AFS_PTHREAD_ENV */
651 LWP_CurrentProcess(&vnp->writer);
652 #endif /* AFS_PTHREAD_ENV */
654 /* Read vnode from volume index */
658 Log("VGetVnode: can't open index dev=%u, i=%s\n", vp->device,
659 PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
660 #ifdef AFS_DEMAND_ATTACH_FS
662 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
667 } else if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, vnodeNumber), SEEK_SET)
669 Log("VGetVnode: can't seek on index file vn=%u\n", vnodeNumber);
670 #ifdef AFS_DEMAND_ATTACH_FS
672 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
677 FDH_REALLYCLOSE(fdP);
678 } else if ((n = FDH_READ(fdP, (char *)&vnp->disk, vcp->diskSize))
680 /* Don't take volume off line if the inumber is out of range
681 * or the inode table is full. */
682 FDH_REALLYCLOSE(fdP);
685 Log("VGetVnode: bad inumber %s\n",
686 PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
690 /* Check for disk errors. Anything else just means that the vnode
691 * is not allocated */
692 if (n == -1 && errno == EIO) {
693 Log("VGetVnode: Couldn't read vnode %u, volume %u (%s); volume needs salvage\n", vnodeNumber, V_id(vp), V_name(vp));
694 #ifdef AFS_DEMAND_ATTACH_FS
695 if (programType == fileServer) {
696 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
699 VForceOffline_r(vp, 0);
703 VForceOffline_r(vp, 0);
711 VInvalidateVnode_r(vnp);
712 if (vnp->nUsers-- == 1)
713 StickOnLruChain_r(vnp, vcp);
714 ReleaseWriteLock(&vnp->lock);
719 /* Quick check to see that the data is reasonable */
720 if (vnp->disk.vnodeMagic != vcp->magic || vnp->disk.type == vNull) {
721 if (vnp->disk.type == vNull) {
724 VInvalidateVnode_r(vnp);
725 if (vnp->nUsers-- == 1)
726 StickOnLruChain_r(vnp, vcp);
727 ReleaseWriteLock(&vnp->lock);
728 return NULL; /* The vnode is not allocated */
730 struct vnodeIndex *index = &vp->vnodeIndex[class];
731 unsigned int bitNumber = vnodeIdToBitNumber(vnodeNumber);
732 unsigned int offset = bitNumber >> 3;
734 /* Test to see if vnode number is valid. */
735 if ((offset >= index->bitmapSize)
736 || ((*(index->bitmap + offset) & (1 << (bitNumber & 0x7)))
738 Log("VGetVnode: Request for unallocated vnode %u, volume %u (%s) denied.\n", vnodeNumber, V_id(vp), V_name(vp));
742 Log("VGetVnode: Bad magic number, vnode %u, volume %u (%s); volume needs salvage\n", vnodeNumber, V_id(vp), V_name(vp));
743 #ifdef AFS_DEMAND_ATTACH_FS
744 if (programType == fileServer) {
745 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
748 vp->goingOffline = 1;
752 vp->goingOffline = 1; /* used to call VOffline, but that would mess
753 * up the volume ref count if called here */
758 VInvalidateVnode_r(vnp);
759 if (vnp->nUsers-- == 1)
760 StickOnLruChain_r(vnp, vcp);
761 ReleaseWriteLock(&vnp->lock);
765 IH_INIT(vnp->handle, V_device(vp), V_parentId(vp), VN_GET_INO(vnp));
766 ReleaseWriteLock(&vnp->lock);
768 VNLog(101, 2, vnodeNumber, (afs_int32) vnp);
769 if (++vnp->nUsers == 1) {
770 /* First user. Remove it from the LRU chain. We can assume that
771 * there is at least one item in the queue */
772 if (vnp == vcp->lruHead)
773 vcp->lruHead = vcp->lruHead->lruNext;
774 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
775 Abort("VGetVnode: lru chain addled!\n");
776 vnp->lruPrev->lruNext = vnp->lruNext;
777 vnp->lruNext->lruPrev = vnp->lruPrev;
781 if (locktype == READ_LOCK)
782 ObtainReadLock(&vnp->lock);
784 ObtainWriteLock(&vnp->lock);
785 #ifdef AFS_PTHREAD_ENV
786 vnp->writer = pthread_self();
787 #else /* AFS_PTHREAD_ENV */
788 LWP_CurrentProcess(&vnp->writer);
789 #endif /* AFS_PTHREAD_ENV */
792 /* Check that the vnode hasn't been removed while we were obtaining
794 VNLog(102, 2, vnodeNumber, (afs_int32) vnp);
795 if ((vnp->disk.type == vNull) || (vnp->cacheCheck == 0)) {
796 if (vnp->nUsers-- == 1)
797 StickOnLruChain_r(vnp, vcp);
798 if (locktype == READ_LOCK)
799 ReleaseReadLock(&vnp->lock);
801 ReleaseWriteLock(&vnp->lock);
804 /* vnode is labelled correctly by now, so we don't have to invalidate it */
807 if (programType == fileServer)
808 VBumpVolumeUsage_r(vnp->volumePtr); /* Hack; don't know where it should be
809 * called from. Maybe VGetVolume */
814 int TrustVnodeCacheEntry = 1;
815 /* This variable is bogus--when it's set to 0, the hash chains fill
816 up with multiple versions of the same vnode. Should fix this!! */
818 VPutVnode(Error * ec, register Vnode * vnp)
821 VPutVnode_r(ec, vnp);
826 VPutVnode_r(Error * ec, register Vnode * vnp)
828 int writeLocked, offset;
830 struct VnodeClassInfo *vcp;
834 assert(vnp->nUsers != 0);
835 class = vnodeIdToClass(vnp->vnodeNumber);
836 vcp = &VnodeClassInfo[class];
837 assert(vnp->disk.vnodeMagic == vcp->magic);
838 VNLog(200, 2, vnp->vnodeNumber, (afs_int32) vnp);
840 writeLocked = WriteLocked(&vnp->lock);
842 #ifdef AFS_PTHREAD_ENV
843 pthread_t thisProcess = pthread_self();
844 #else /* AFS_PTHREAD_ENV */
846 LWP_CurrentProcess(&thisProcess);
847 #endif /* AFS_PTHREAD_ENV */
848 VNLog(201, 2, (afs_int32) vnp,
849 ((vnp->changed_newTime) << 1) | ((vnp->
850 changed_oldTime) << 1) | vnp->
852 if (thisProcess != vnp->writer)
853 Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",
855 if (vnp->changed_oldTime || vnp->changed_newTime || vnp->delete) {
856 Volume *vp = vnp->volumePtr;
857 afs_uint32 now = FT_ApproxTime();
858 assert(vnp->cacheCheck == vp->cacheCheck);
861 /* No longer any directory entries for this vnode. Free the Vnode */
862 memset(&vnp->disk, 0, sizeof(vnp->disk));
863 mlkLastDelete = vnp->vnodeNumber;
864 /* delete flag turned off further down */
865 VNLog(202, 2, vnp->vnodeNumber, (afs_int32) vnp);
866 } else if (vnp->changed_newTime) {
867 vnp->disk.serverModifyTime = now;
869 if (vnp->changed_newTime)
871 V_updateDate(vp) = vp->updateTime = now;
872 if(V_volUpCounter(vp)<MAXINT)
873 V_volUpCounter(vp)++;
876 /* The vnode has been changed. Write it out to disk */
878 #ifdef AFS_DEMAND_ATTACH_FS
879 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
882 assert(V_needsSalvaged(vp));
886 IHandle_t *ihP = vp->vnodeIndex[class].handle;
891 Log("VPutVnode: can't open index file!\n");
892 goto error_encountered;
894 offset = vnodeIndexOffset(vcp, vnp->vnodeNumber);
895 if (FDH_SEEK(fdP, offset, SEEK_SET) < 0) {
896 Log("VPutVnode: can't seek on index file! fdp=0x%x offset=%d, errno=%d\n",
898 goto error_encountered;
900 code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
901 if (code != vcp->diskSize) {
902 /* Don't force volume offline if the inumber is out of
903 * range or the inode table is full.
906 if (code == BAD_IGET) {
907 Log("VPutVnode: bad inumber %s\n",
909 vp->vnodeIndex[class].handle->ih_ino));
912 Log("VPutVnode: Couldn't write vnode %u, volume %u (%s) (error %d)\n", vnp->vnodeNumber, V_id(vnp->volumePtr), V_name(vnp->volumePtr), code);
913 #ifdef AFS_DEMAND_ATTACH_FS
914 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
917 VForceOffline_r(vp, 0);
922 FDH_REALLYCLOSE(fdP);
930 #ifdef AFS_DEMAND_ATTACH_FS
931 /* XXX instead of dumping core, let's try to request a salvage
932 * and just fail the putvnode */
936 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
944 /* If the vnode is to be deleted, and we wrote the vnode out,
945 * free its bitmap entry. Do after the vnode is written so we
946 * don't allocate from bitmap before the vnode is written
947 * (doing so could cause a "addled bitmap" message).
949 if (vnp->delete && !*ec) {
950 if (vnp->volumePtr->header->diskstuff.filecount-- < 1)
951 vnp->volumePtr->header->diskstuff.filecount = 0;
952 VFreeBitMapEntry_r(ec, &vp->vnodeIndex[class],
953 vnodeIdToBitNumber(vnp->vnodeNumber));
957 vnp->changed_newTime = vnp->changed_oldTime = 0;
959 } else { /* Not write locked */
960 if (vnp->changed_newTime || vnp->changed_oldTime || vnp->delete)
962 ("VPutVnode: Change or delete flag for vnode 0x%x is set but vnode is not write locked!\n",
967 /* Do not look at disk portion of vnode after this point; it may
968 * have been deleted above */
969 if (vnp->nUsers-- == 1)
970 StickOnLruChain_r(vnp, vcp);
974 ReleaseWriteLock(&vnp->lock);
976 ReleaseReadLock(&vnp->lock);
980 * Make an attempt to convert a vnode lock from write to read.
981 * Do nothing if the vnode isn't write locked or the vnode has
985 VVnodeWriteToRead(Error * ec, register Vnode * vnp)
989 retVal = VVnodeWriteToRead_r(ec, vnp);
995 VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
999 struct VnodeClassInfo *vcp;
1001 #ifdef AFS_PTHREAD_ENV
1002 pthread_t thisProcess;
1003 #else /* AFS_PTHREAD_ENV */
1004 PROCESS thisProcess;
1005 #endif /* AFS_PTHREAD_ENV */
1008 assert(vnp->nUsers != 0);
1009 class = vnodeIdToClass(vnp->vnodeNumber);
1010 vcp = &VnodeClassInfo[class];
1011 assert(vnp->disk.vnodeMagic == vcp->magic);
1012 writeLocked = WriteLocked(&vnp->lock);
1013 VNLog(300, 2, vnp->vnodeNumber, (afs_int32) vnp);
1018 #ifdef AFS_PTHREAD_ENV
1019 thisProcess = pthread_self();
1020 #else /* AFS_PTHREAD_ENV */
1021 LWP_CurrentProcess(&thisProcess);
1022 #endif /* AFS_PTHREAD_ENV */
1024 VNLog(301, 2, (afs_int32) vnp,
1025 ((vnp->changed_newTime) << 1) | ((vnp->
1026 changed_oldTime) << 1) | vnp->
1028 if (thisProcess != vnp->writer)
1029 Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",
1034 if (vnp->changed_oldTime || vnp->changed_newTime) {
1035 Volume *vp = vnp->volumePtr;
1036 afs_uint32 now = FT_ApproxTime();
1037 assert(vnp->cacheCheck == vp->cacheCheck);
1038 if (vnp->changed_newTime)
1039 vnp->disk.serverModifyTime = now;
1040 if (vnp->changed_newTime)
1041 V_updateDate(vp) = vp->updateTime = now;
1043 /* The inode has been changed. Write it out to disk */
1045 #ifdef AFS_DEMAND_ATTACH_FS
1046 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
1049 assert(V_needsSalvaged(vp));
1053 IHandle_t *ihP = vp->vnodeIndex[class].handle;
1055 off_t off = vnodeIndexOffset(vcp, vnp->vnodeNumber);
1059 Log("VPutVnode: can't open index file!\n");
1060 goto error_encountered;
1062 code = FDH_SEEK(fdP, off, SEEK_SET);
1064 Log("VPutVnode: can't seek on index file!\n");
1065 goto error_encountered;
1067 code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
1068 if (code != vcp->diskSize) {
1070 * Don't force volume offline if the inumber is out of
1071 * range or the inode table is full.
1074 if (code == BAD_IGET) {
1075 Log("VPutVnode: bad inumber %s\n",
1077 vp->vnodeIndex[class].handle->ih_ino));
1080 Log("VPutVnode: Couldn't write vnode %u, volume %u (%s)\n", vnp->vnodeNumber, V_id(vnp->volumePtr), V_name(vnp->volumePtr));
1081 #ifdef AFS_DEMAND_ATTACH_FS
1082 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
1085 VForceOffline_r(vp, 0);
1096 #ifdef AFS_DEMAND_ATTACH_FS
1100 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
1109 vnp->changed_newTime = vnp->changed_oldTime = 0;
1112 ConvertWriteToReadLock(&vnp->lock);
1116 /* Move the vnode, vnp, to the new hash table given by the
1117 hash table index, newHash */
1119 moveHash(register Vnode * vnp, bit32 newHash)
1122 /* Remove it from the old hash chain */
1123 tvnp = VnodeHashTable[vnp->hashIndex];
1125 VnodeHashTable[vnp->hashIndex] = vnp->hashNext;
1127 while (tvnp && tvnp->hashNext != vnp)
1128 tvnp = tvnp->hashNext;
1130 tvnp->hashNext = vnp->hashNext;
1132 /* Add it to the new hash chain */
1133 vnp->hashNext = VnodeHashTable[newHash];
1134 VnodeHashTable[newHash] = vnp;
1135 vnp->hashIndex = newHash;
1140 StickOnLruChain_r(register Vnode * vnp, register struct VnodeClassInfo *vcp)
1142 /* Add it to the circular LRU list */
1143 if (vcp->lruHead == NULL)
1144 Abort("VPutVnode: vcp->lruHead==NULL");
1146 vnp->lruNext = vcp->lruHead;
1147 vnp->lruPrev = vcp->lruHead->lruPrev;
1148 vcp->lruHead->lruPrev = vnp;
1149 vnp->lruPrev->lruNext = vnp;
1152 /* If the vnode was just deleted, put it at the end of the chain so it
1153 * will be reused immediately */
1155 vcp->lruHead = vnp->lruNext;
1156 /* If caching is turned off, set volumeptr to NULL to invalidate the
1158 if (!TrustVnodeCacheEntry) {
1159 DeleteFromVnHashByVolumeTable(vnp);
1160 vnp->volumePtr = NULL;
1164 /* VCloseVnodeFiles - called when a volume is going off line. All open
1165 * files for vnodes in that volume are closed. This might be excessive,
1166 * since we may only be taking one volume of a volume group offline.
1169 VCloseVnodeFiles_r(Volume * vp)
1173 VnodeHashByVolumeChainHead * head;
1175 head = &VnodeHashByVolumeTable[VNVOLUME_HASH(vp->hashid)];
1176 #ifdef AFS_DEMAND_ATTACH_FS
1177 while (head->busy) {
1178 assert(pthread_cond_wait(&head->chain_busy_cv, &vol_glock_mutex) == 0);
1183 #endif /* AFS_DEMAND_ATTACH_FS */
1185 for (queue_Scan(head, vnp, nvnp, Vnode)) {
1186 if (vnp->volumePtr == vp) {
1187 IH_REALLYCLOSE(vnp->handle);
1191 #ifdef AFS_DEMAND_ATTACH_FS
1194 assert(pthread_cond_broadcast(&head->chain_busy_cv) == 0);
1195 #endif /* AFS_DEMAND_ATTACH_FS */
1198 /* VReleaseVnodeFiles - called when a volume is going detached. All open
1199 * files for vnodes in that volume are closed and all inode handles
1200 * for vnodes in that volume are released.
1203 VReleaseVnodeFiles_r(Volume * vp)
1207 VnodeHashByVolumeChainHead * head;
1209 head = &VnodeHashByVolumeTable[VNVOLUME_HASH(vp->hashid)];
1211 #ifdef AFS_DEMAND_ATTACH_FS
1212 while (head->busy) {
1213 assert(pthread_cond_wait(&head->chain_busy_cv, &vol_glock_mutex) == 0);
1218 #endif /* AFS_DEMAND_ATTACH_FS */
1220 for (queue_Scan(head, vnp, nvnp, Vnode)) {
1221 if (vnp->volumePtr == vp) {
1222 IH_RELEASE(vnp->handle);
1226 #ifdef AFS_DEMAND_ATTACH_FS
1229 assert(pthread_cond_broadcast(&head->chain_busy_cv) == 0);
1230 #endif /* AFS_DEMAND_ATTACH_FS */