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);
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;
107 VNLog(aop, anparms, av1, av2, av3, av4)
108 afs_int32 aop, anparms;
109 afs_int32 av1, av2, av3, av4;
111 register afs_int32 temp;
114 /* copy data to array */
120 anparms = 4; /* do bounds checking */
122 temp = (aop << 16) | anparms;
123 theLog[vnLogPtr++] = temp;
124 if (vnLogPtr >= THELOGSIZE)
126 for (temp = 0; temp < anparms; temp++) {
127 theLog[vnLogPtr++] = data[temp];
128 if (vnLogPtr >= THELOGSIZE)
133 /* VolumeHashOffset -- returns a new value to be stored in the
134 * volumeHashOffset of a Volume structure. Called when a
135 * volume is initialized. Sets the volumeHashOffset so that
136 * vnode cache entries are distributed reasonably between
137 * volumes (the root vnodes of the volumes will hash to
138 * different values, and spacing is maintained between volumes
139 * when there are not many volumes represented), and spread
140 * equally amongst vnodes within a single volume.
143 VolumeHashOffset_r(void)
145 static int nextVolumeHashOffset = 0;
146 /* hashindex Must be power of two in size */
148 # define hashMask ((1<<hashShift)-1)
149 static byte hashindex[1 << hashShift] =
150 { 0, 128, 64, 192, 32, 160, 96, 224 };
152 offset = hashindex[nextVolumeHashOffset & hashMask]
153 + (nextVolumeHashOffset >> hashShift);
154 nextVolumeHashOffset++;
158 /* Change hashindex (above) if you change this constant */
159 #define VNODE_HASH_TABLE_SIZE 256
160 private Vnode *VnodeHashTable[VNODE_HASH_TABLE_SIZE];
161 #define VNODE_HASH(volumeptr,vnodenumber)\
162 ((volumeptr->vnodeHashOffset + vnodenumber)&(VNODE_HASH_TABLE_SIZE-1))
165 * new support to secondarily hash vnodes by volume id
167 #define VNVOLUME_HASH(volumeId) (volumeId&(VolumeHashTable.Mask))
169 #include "rx/rx_queue.h"
170 typedef struct VnodeHashByVolumeChainHead {
171 struct rx_queue queue;
173 /* someday we could put a per-chain lock here... */
174 #ifdef AFS_DEMAND_ATTACH_FS
176 pthread_cond_t chain_busy_cv;
177 #endif /* AFS_DEMAND_ATTACH_FS */
178 } VnodeHashByVolumeChainHead;
179 private VnodeHashByVolumeChainHead *VnodeHashByVolumeTable = NULL;
182 VInitVnHashByVolume(void)
186 VnodeHashByVolumeTable = (VnodeHashByVolumeChainHead *) calloc(VolumeHashTable.Size,
187 sizeof(VnodeHashByVolumeChainHead));
188 assert(VnodeHashByVolumeTable != NULL);
190 for (i=0; i < VolumeHashTable.Size; i++) {
191 queue_Init(&VnodeHashByVolumeTable[i]);
192 #ifdef AFS_DEMAND_ATTACH_FS
193 assert(pthread_cond_init(&VnodeHashByVolumeTable[i].chain_busy_cv, NULL) == 0);
194 #endif /* AFS_DEMAND_ATTACH_FS */
199 AddToVnHashByVolumeTable(register Vnode * vnp)
201 VnodeHashByVolumeChainHead * head;
203 if (queue_IsOnQueue(vnp))
206 head = &VnodeHashByVolumeTable[VNVOLUME_HASH(vnp->volumePtr->hashid)];
208 #ifdef AFS_DEMAND_ATTACH_FS
210 /* if the hash table is busy, wait */
211 assert(pthread_cond_wait(&head->chain_busy_cv, &vol_glock_mutex) == 0);
213 #endif /* AFS_DEMAND_ATTACH_FS */
216 queue_Append(head, vnp);
219 /* for demand-attach, caller MUST hold a ref count on vp */
221 DeleteFromVnHashByVolumeTable(register Vnode * vnp)
223 VnodeHashByVolumeChainHead * head;
225 if (!queue_IsOnQueue(vnp))
228 head = &VnodeHashByVolumeTable[VNVOLUME_HASH(vnp->volumePtr->hashid)];
230 #ifdef AFS_DEMAND_ATTACH_FS
232 /* if the hash table is busy, wait */
233 assert(pthread_cond_wait(&head->chain_busy_cv, &vol_glock_mutex) == 0);
235 #endif /* AFS_DEMAND_ATTACH_FS */
241 /* Code to invalidate a vnode entry. Called when we've damaged a vnode, and want
242 to prevent future VGetVnode's from applying to it. Leaves it in the same hash bucket
243 but that shouldn't be important. */
245 VInvalidateVnode_r(register struct Vnode *avnode)
247 avnode->changed_newTime = 0; /* don't let it get flushed out again */
248 avnode->changed_oldTime = 0;
249 avnode->delete = 0; /* it isn't deleted, erally */
250 avnode->cacheCheck = 0; /* invalid: prevents future vnode searches from working */
253 /* Not normally called by general client; called by volume.c */
255 VInitVnodes(VnodeClass class, int nVnodes)
258 register struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
260 vcp->allocs = vcp->gets = vcp->reads = vcp->writes = 0;
261 vcp->cacheSize = nVnodes;
264 assert(CHECKSIZE_SMALLVNODE);
266 vcp->residentSize = SIZEOF_SMALLVNODE;
267 vcp->diskSize = SIZEOF_SMALLDISKVNODE;
268 vcp->magic = SMALLVNODEMAGIC;
272 vcp->residentSize = SIZEOF_LARGEVNODE;
273 vcp->diskSize = SIZEOF_LARGEDISKVNODE;
274 vcp->magic = LARGEVNODEMAGIC;
278 int s = vcp->diskSize - 1;
288 va = (byte *) calloc(nVnodes, vcp->residentSize);
291 Vnode *vnp = (Vnode *) va;
292 vnp->nUsers = 0; /* no context switches */
293 Lock_Init(&vnp->lock);
294 vnp->changed_oldTime = 0;
295 vnp->changed_newTime = 0;
296 vnp->volumePtr = NULL;
298 vnp->delete = vnp->vnodeNumber = 0;
299 #ifdef AFS_PTHREAD_ENV
300 vnp->writer = (pthread_t) 0;
301 #else /* AFS_PTHREAD_ENV */
302 vnp->writer = (PROCESS) 0;
303 #endif /* AFS_PTHREAD_ENV */
306 if (vcp->lruHead == NULL)
307 vcp->lruHead = vnp->lruNext = vnp->lruPrev = vnp;
309 vnp->lruNext = vcp->lruHead;
310 vnp->lruPrev = vcp->lruHead->lruPrev;
311 vcp->lruHead->lruPrev = vnp;
312 vnp->lruPrev->lruNext = vnp;
315 va += vcp->residentSize;
321 /* allocate an *unused* vnode from the LRU chain, going backwards of course. It shouldn't
322 be necessary to specify that nUsers == 0 since if it is in the list, nUsers
323 should be 0. Things shouldn't be in lruq unless no one is using them. */
325 VGetFreeVnode_r(struct VnodeClassInfo * vcp)
329 vnp = vcp->lruHead->lruPrev;
330 if (vnp->nUsers != 0 || CheckLock(&vnp->lock))
331 Abort("locked vnode in lruq");
332 VNLog(1, 2, vnp->vnodeNumber, (afs_int32) vnp);
333 IH_RELEASE(vnp->handle);
337 static mlkReason = 0;
338 static mlkLastAlloc = 0;
339 static mlkLastOver = 0;
340 static mlkLastDelete = 0;
343 VAllocVnode(Error * ec, Volume * vp, VnodeType type)
347 retVal = VAllocVnode_r(ec, vp, type);
353 VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
357 int newHash, bitNumber;
358 register struct VnodeClassInfo *vcp;
363 if (programType == fileServer && !V_inUse(vp)) {
364 if (vp->specialStatus) {
365 *ec = vp->specialStatus;
371 class = vnodeTypeToClass(type);
372 vcp = &VnodeClassInfo[class];
374 if (!VolumeWriteable(vp)) {
375 *ec = (bit32) VREADONLY;
379 unique = vp->nextVnodeUnique++;
381 unique = vp->nextVnodeUnique++;
383 if (vp->nextVnodeUnique > V_uniquifier(vp)) {
384 VUpdateVolume_r(ec, vp, VOL_UPDATE_WAIT);
389 if (programType == fileServer) {
390 VAddToVolumeUpdateList_r(ec, vp);
395 /* Find a slot in the bit map */
396 bitNumber = VAllocBitmapEntry_r(ec, vp, &vp->vnodeIndex[class],
397 VOL_ALLOC_BITMAP_WAIT);
400 vnodeNumber = bitNumberToVnodeNumber(bitNumber, class);
403 VNLog(2, 1, vnodeNumber);
404 /* Prepare to move it to the new hash chain */
405 newHash = VNODE_HASH(vp, vnodeNumber);
406 for (vnp = VnodeHashTable[newHash];
407 vnp && (vnp->vnodeNumber != vnodeNumber || vnp->volumePtr != vp
408 || vnp->volumePtr->cacheCheck != vnp->cacheCheck);
409 vnp = vnp->hashNext);
411 /* slot already exists. May even not be in lruq (consider store file locking a file being deleted)
412 * so we may have to wait for it below */
413 VNLog(3, 2, vnodeNumber, (afs_int32) vnp);
415 /* If first user, remove it from the LRU chain. We can assume that
416 * there is at least one item in the queue */
417 if (++vnp->nUsers == 1) {
418 if (vnp == vcp->lruHead)
419 vcp->lruHead = vcp->lruHead->lruNext;
420 vnp->lruPrev->lruNext = vnp->lruNext;
421 vnp->lruNext->lruPrev = vnp->lruPrev;
422 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
423 Abort("VGetVnode: lru chain addled!\n");
424 /* This won't block */
425 ObtainWriteLock(&vnp->lock);
427 /* follow locking hierarchy */
429 ObtainWriteLock(&vnp->lock);
431 if (vnp->volumePtr->cacheCheck != vnp->cacheCheck) {
432 ReleaseWriteLock(&vnp->lock);
436 #ifdef AFS_PTHREAD_ENV
437 vnp->writer = pthread_self();
438 #else /* AFS_PTHREAD_ENV */
439 LWP_CurrentProcess(&vnp->writer);
440 #endif /* AFS_PTHREAD_ENV */
442 vnp = VGetFreeVnode_r(vcp);
443 /* Remove vnode from LRU chain and grab a write lock */
444 if (vnp == vcp->lruHead)
445 vcp->lruHead = vcp->lruHead->lruNext;
446 vnp->lruPrev->lruNext = vnp->lruNext;
447 vnp->lruNext->lruPrev = vnp->lruPrev;
448 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
449 Abort("VGetVnode: lru chain addled!\n");
450 /* Initialize the header fields so noone allocates another
451 * vnode with the same number */
452 vnp->vnodeNumber = vnodeNumber;
454 vnp->cacheCheck = vp->cacheCheck;
456 /* This will never block */
457 ObtainWriteLock(&vnp->lock);
458 #ifdef AFS_PTHREAD_ENV
459 vnp->writer = pthread_self();
460 #else /* AFS_PTHREAD_ENV */
461 LWP_CurrentProcess(&vnp->writer);
462 #endif /* AFS_PTHREAD_ENV */
463 /* Sanity check: is this vnode really not in use? */
466 IHandle_t *ihP = vp->vnodeIndex[class].handle;
468 off_t off = vnodeIndexOffset(vcp, vnodeNumber);
470 /* XXX we have a potential race here if two threads
471 * allocate new vnodes at the same time, and they
472 * both decide it's time to extend the index
478 Log("VAllocVnode: can't open index file!\n");
479 goto error_encountered;
481 if ((size = FDH_SIZE(fdP)) < 0) {
482 Log("VAllocVnode: can't stat index file!\n");
483 goto error_encountered;
485 if (FDH_SEEK(fdP, off, SEEK_SET) < 0) {
486 Log("VAllocVnode: can't seek on index file!\n");
487 goto error_encountered;
489 if (off + vcp->diskSize <= size) {
490 if (FDH_READ(fdP, &vnp->disk, vcp->diskSize) != vcp->diskSize) {
491 Log("VAllocVnode: can't read index file!\n");
492 goto error_encountered;
494 if (vnp->disk.type != vNull) {
495 Log("VAllocVnode: addled bitmap or index!\n");
496 goto error_encountered;
499 /* growing file - grow in a reasonable increment */
500 char *buf = (char *)malloc(16 * 1024);
502 Abort("VAllocVnode: malloc failed\n");
503 memset(buf, 0, 16 * 1024);
504 (void)FDH_WRITE(fdP, buf, 16 * 1024);
513 #ifdef AFS_DEMAND_ATTACH_FS
515 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
518 VInvalidateVnode_r(vnp);
519 StickOnLruChain_r(vnp, vcp);
527 VNLog(4, 2, vnodeNumber, (afs_int32) vnp);
528 AddToVnHashByVolumeTable(vnp);
529 moveHash(vnp, newHash);
532 VNLog(5, 1, (afs_int32) vnp);
533 #ifdef AFS_PTHREAD_ENV
534 vnp->writer = pthread_self();
535 #else /* AFS_PTHREAD_ENV */
536 LWP_CurrentProcess(&vnp->writer);
537 #endif /* AFS_PTHREAD_ENV */
538 memset(&vnp->disk, 0, sizeof(vnp->disk));
539 vnp->changed_newTime = 0; /* set this bit when vnode is updated */
540 vnp->changed_oldTime = 0; /* set this on CopyOnWrite. */
542 vnp->disk.vnodeMagic = vcp->magic;
543 vnp->disk.type = type;
544 vnp->disk.uniquifier = unique;
547 vp->header->diskstuff.filecount++;
552 VGetVnode(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
553 { /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
556 retVal = VGetVnode_r(ec, vp, vnodeNumber, locktype);
562 VGetVnode_r(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
563 { /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
567 struct VnodeClassInfo *vcp;
570 mlkReason = 0; /* last call didn't fail */
572 if (vnodeNumber == 0) {
578 VNLog(100, 1, vnodeNumber);
579 if (programType == fileServer && !V_inUse(vp)) {
580 *ec = (vp->specialStatus ? vp->specialStatus : VOFFLINE);
582 /* If the volume is VBUSY (being cloned or dumped) and this is
583 * a READ operation, then don't fail.
585 if ((*ec != VBUSY) || (locktype != READ_LOCK)) {
591 class = vnodeIdToClass(vnodeNumber);
592 vcp = &VnodeClassInfo[class];
593 if (locktype == WRITE_LOCK && !VolumeWriteable(vp)) {
594 *ec = (bit32) VREADONLY;
599 if (locktype == WRITE_LOCK && programType == fileServer) {
600 VAddToVolumeUpdateList_r(ec, vp);
602 mlkReason = 1000 + *ec;
607 /* See whether the vnode is in the cache. */
608 newHash = VNODE_HASH(vp, vnodeNumber);
609 for (vnp = VnodeHashTable[newHash];
610 vnp && (vnp->vnodeNumber != vnodeNumber || vnp->volumePtr != vp
611 || vnp->volumePtr->cacheCheck != vnp->cacheCheck);
612 vnp = vnp->hashNext);
616 IHandle_t *ihP = vp->vnodeIndex[class].handle;
618 /* Not in cache; tentatively grab most distantly used one from the LRU
621 vnp = VGetFreeVnode_r(vcp);
622 /* Remove it from the old hash chain */
624 DeleteFromVnHashByVolumeTable(vnp);
625 moveHash(vnp, newHash);
626 /* Remove it from the LRU chain */
627 if (vnp == vcp->lruHead)
628 vcp->lruHead = vcp->lruHead->lruNext;
629 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
630 Abort("VGetVnode: lru chain addled!\n");
631 vnp->lruPrev->lruNext = vnp->lruNext;
632 vnp->lruNext->lruPrev = vnp->lruPrev;
634 vnp->changed_newTime = vnp->changed_oldTime = 0;
636 vnp->vnodeNumber = vnodeNumber;
638 vnp->cacheCheck = vp->cacheCheck;
640 AddToVnHashByVolumeTable(vnp);
642 /* This will never block */
643 ObtainWriteLock(&vnp->lock);
644 #ifdef AFS_PTHREAD_ENV
645 vnp->writer = pthread_self();
646 #else /* AFS_PTHREAD_ENV */
647 LWP_CurrentProcess(&vnp->writer);
648 #endif /* AFS_PTHREAD_ENV */
650 /* Read vnode from volume index */
654 Log("VGetVnode: can't open index dev=%u, i=%s\n", vp->device,
655 PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
656 #ifdef AFS_DEMAND_ATTACH_FS
658 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
663 } else if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, vnodeNumber), SEEK_SET)
665 Log("VGetVnode: can't seek on index file vn=%u\n", vnodeNumber);
666 #ifdef AFS_DEMAND_ATTACH_FS
668 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
673 FDH_REALLYCLOSE(fdP);
674 } else if ((n = FDH_READ(fdP, (char *)&vnp->disk, vcp->diskSize))
676 /* Don't take volume off line if the inumber is out of range
677 * or the inode table is full. */
678 FDH_REALLYCLOSE(fdP);
681 Log("VGetVnode: bad inumber %s\n",
682 PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
686 /* Check for disk errors. Anything else just means that the vnode
687 * is not allocated */
688 if (n == -1 && errno == EIO) {
689 Log("VGetVnode: Couldn't read vnode %u, volume %u (%s); volume needs salvage\n", vnodeNumber, V_id(vp), V_name(vp));
690 #ifdef AFS_DEMAND_ATTACH_FS
691 if (programType == fileServer) {
692 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
695 VForceOffline_r(vp, 0);
699 VForceOffline_r(vp, 0);
704 /* Probably legit; Don't knock the volume offline */
706 Log("VGetVnode: Couldn't read vnode %u, volume %u (%s); errno %d\n", vnodeNumber, V_id(vp), V_name(vp), errno);
710 VInvalidateVnode_r(vnp);
711 if (vnp->nUsers-- == 1)
712 StickOnLruChain_r(vnp, vcp);
713 ReleaseWriteLock(&vnp->lock);
718 /* Quick check to see that the data is reasonable */
719 if (vnp->disk.vnodeMagic != vcp->magic || vnp->disk.type == vNull) {
720 if (vnp->disk.type == vNull) {
723 VInvalidateVnode_r(vnp);
724 if (vnp->nUsers-- == 1)
725 StickOnLruChain_r(vnp, vcp);
726 ReleaseWriteLock(&vnp->lock);
727 return NULL; /* The vnode is not allocated */
729 struct vnodeIndex *index = &vp->vnodeIndex[class];
730 unsigned int bitNumber = vnodeIdToBitNumber(vnodeNumber);
731 unsigned int offset = bitNumber >> 3;
733 /* Test to see if vnode number is valid. */
734 if ((offset >= index->bitmapSize)
735 || ((*(index->bitmap + offset) & (1 << (bitNumber & 0x7)))
737 Log("VGetVnode: Request for unallocated vnode %u, volume %u (%s) denied.\n", vnodeNumber, V_id(vp), V_name(vp));
741 Log("VGetVnode: Bad magic number, vnode %u, volume %u (%s); volume needs salvage\n", vnodeNumber, V_id(vp), V_name(vp));
742 #ifdef AFS_DEMAND_ATTACH_FS
743 if (programType == fileServer) {
744 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
747 vp->goingOffline = 1;
751 vp->goingOffline = 1; /* used to call VOffline, but that would mess
752 * up the volume ref count if called here */
757 VInvalidateVnode_r(vnp);
758 if (vnp->nUsers-- == 1)
759 StickOnLruChain_r(vnp, vcp);
760 ReleaseWriteLock(&vnp->lock);
764 IH_INIT(vnp->handle, V_device(vp), V_parentId(vp), VN_GET_INO(vnp));
765 ReleaseWriteLock(&vnp->lock);
767 VNLog(101, 2, vnodeNumber, (afs_int32) vnp);
768 if (++vnp->nUsers == 1) {
769 /* First user. Remove it from the LRU chain. We can assume that
770 * there is at least one item in the queue */
771 if (vnp == vcp->lruHead)
772 vcp->lruHead = vcp->lruHead->lruNext;
773 if (vnp == vcp->lruHead || vcp->lruHead == NULL)
774 Abort("VGetVnode: lru chain addled!\n");
775 vnp->lruPrev->lruNext = vnp->lruNext;
776 vnp->lruNext->lruPrev = vnp->lruPrev;
780 if (locktype == READ_LOCK)
781 ObtainReadLock(&vnp->lock);
783 ObtainWriteLock(&vnp->lock);
784 #ifdef AFS_PTHREAD_ENV
785 vnp->writer = pthread_self();
786 #else /* AFS_PTHREAD_ENV */
787 LWP_CurrentProcess(&vnp->writer);
788 #endif /* AFS_PTHREAD_ENV */
791 /* Check that the vnode hasn't been removed while we were obtaining
793 VNLog(102, 2, vnodeNumber, (afs_int32) vnp);
794 if ((vnp->disk.type == vNull) || (vnp->cacheCheck == 0)) {
795 if (vnp->nUsers-- == 1)
796 StickOnLruChain_r(vnp, vcp);
797 if (locktype == READ_LOCK)
798 ReleaseReadLock(&vnp->lock);
800 ReleaseWriteLock(&vnp->lock);
803 /* vnode is labelled correctly by now, so we don't have to invalidate it */
806 if (programType == fileServer)
807 VBumpVolumeUsage_r(vnp->volumePtr); /* Hack; don't know where it should be
808 * called from. Maybe VGetVolume */
813 int TrustVnodeCacheEntry = 1;
814 /* This variable is bogus--when it's set to 0, the hash chains fill
815 up with multiple versions of the same vnode. Should fix this!! */
817 VPutVnode(Error * ec, register Vnode * vnp)
820 VPutVnode_r(ec, vnp);
825 VPutVnode_r(Error * ec, register Vnode * vnp)
827 int writeLocked, offset;
829 struct VnodeClassInfo *vcp;
833 assert(vnp->nUsers != 0);
834 class = vnodeIdToClass(vnp->vnodeNumber);
835 vcp = &VnodeClassInfo[class];
836 assert(vnp->disk.vnodeMagic == vcp->magic);
837 VNLog(200, 2, vnp->vnodeNumber, (afs_int32) vnp);
839 writeLocked = WriteLocked(&vnp->lock);
841 #ifdef AFS_PTHREAD_ENV
842 pthread_t thisProcess = pthread_self();
843 #else /* AFS_PTHREAD_ENV */
845 LWP_CurrentProcess(&thisProcess);
846 #endif /* AFS_PTHREAD_ENV */
847 VNLog(201, 2, (afs_int32) vnp,
848 ((vnp->changed_newTime) << 1) | ((vnp->
849 changed_oldTime) << 1) | vnp->
851 if (thisProcess != vnp->writer)
852 Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",
854 if (vnp->changed_oldTime || vnp->changed_newTime || vnp->delete) {
855 Volume *vp = vnp->volumePtr;
856 afs_uint32 now = FT_ApproxTime();
857 assert(vnp->cacheCheck == vp->cacheCheck);
860 /* No longer any directory entries for this vnode. Free the Vnode */
861 memset(&vnp->disk, 0, sizeof(vnp->disk));
862 mlkLastDelete = vnp->vnodeNumber;
863 /* delete flag turned off further down */
864 VNLog(202, 2, vnp->vnodeNumber, (afs_int32) vnp);
865 } else if (vnp->changed_newTime) {
866 vnp->disk.serverModifyTime = now;
868 if (vnp->changed_newTime)
870 V_updateDate(vp) = vp->updateTime = now;
871 if(V_volUpCounter(vp)<MAXINT)
872 V_volUpCounter(vp)++;
875 /* The vnode has been changed. Write it out to disk */
877 #ifdef AFS_DEMAND_ATTACH_FS
878 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
881 assert(V_needsSalvaged(vp));
885 IHandle_t *ihP = vp->vnodeIndex[class].handle;
890 Log("VPutVnode: can't open index file!\n");
891 goto error_encountered;
893 offset = vnodeIndexOffset(vcp, vnp->vnodeNumber);
894 if (FDH_SEEK(fdP, offset, SEEK_SET) < 0) {
895 Log("VPutVnode: can't seek on index file! fdp=0x%x offset=%d, errno=%d\n",
897 goto error_encountered;
899 code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
900 if (code != vcp->diskSize) {
901 /* Don't force volume offline if the inumber is out of
902 * range or the inode table is full.
905 if (code == BAD_IGET) {
906 Log("VPutVnode: bad inumber %s\n",
908 vp->vnodeIndex[class].handle->ih_ino));
911 Log("VPutVnode: Couldn't write vnode %u, volume %u (%s) (error %d)\n", vnp->vnodeNumber, V_id(vnp->volumePtr), V_name(vnp->volumePtr), code);
912 #ifdef AFS_DEMAND_ATTACH_FS
913 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
916 VForceOffline_r(vp, 0);
921 FDH_REALLYCLOSE(fdP);
929 #ifdef AFS_DEMAND_ATTACH_FS
930 /* XXX instead of dumping core, let's try to request a salvage
931 * and just fail the putvnode */
935 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
943 /* If the vnode is to be deleted, and we wrote the vnode out,
944 * free its bitmap entry. Do after the vnode is written so we
945 * don't allocate from bitmap before the vnode is written
946 * (doing so could cause a "addled bitmap" message).
948 if (vnp->delete && !*ec) {
949 if (vnp->volumePtr->header->diskstuff.filecount-- < 1)
950 vnp->volumePtr->header->diskstuff.filecount = 0;
951 VFreeBitMapEntry_r(ec, &vp->vnodeIndex[class],
952 vnodeIdToBitNumber(vnp->vnodeNumber));
956 vnp->changed_newTime = vnp->changed_oldTime = 0;
958 } else { /* Not write locked */
959 if (vnp->changed_newTime || vnp->changed_oldTime || vnp->delete)
961 ("VPutVnode: Change or delete flag for vnode 0x%x is set but vnode is not write locked!\n",
966 /* Do not look at disk portion of vnode after this point; it may
967 * have been deleted above */
968 if (vnp->nUsers-- == 1)
969 StickOnLruChain_r(vnp, vcp);
973 ReleaseWriteLock(&vnp->lock);
975 ReleaseReadLock(&vnp->lock);
979 * Make an attempt to convert a vnode lock from write to read.
980 * Do nothing if the vnode isn't write locked or the vnode has
984 VVnodeWriteToRead(Error * ec, register Vnode * vnp)
988 retVal = VVnodeWriteToRead_r(ec, vnp);
994 VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
998 struct VnodeClassInfo *vcp;
1000 #ifdef AFS_PTHREAD_ENV
1001 pthread_t thisProcess;
1002 #else /* AFS_PTHREAD_ENV */
1003 PROCESS thisProcess;
1004 #endif /* AFS_PTHREAD_ENV */
1007 assert(vnp->nUsers != 0);
1008 class = vnodeIdToClass(vnp->vnodeNumber);
1009 vcp = &VnodeClassInfo[class];
1010 assert(vnp->disk.vnodeMagic == vcp->magic);
1011 writeLocked = WriteLocked(&vnp->lock);
1012 VNLog(300, 2, vnp->vnodeNumber, (afs_int32) vnp);
1017 #ifdef AFS_PTHREAD_ENV
1018 thisProcess = pthread_self();
1019 #else /* AFS_PTHREAD_ENV */
1020 LWP_CurrentProcess(&thisProcess);
1021 #endif /* AFS_PTHREAD_ENV */
1023 VNLog(301, 2, (afs_int32) vnp,
1024 ((vnp->changed_newTime) << 1) | ((vnp->
1025 changed_oldTime) << 1) | vnp->
1027 if (thisProcess != vnp->writer)
1028 Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",
1033 if (vnp->changed_oldTime || vnp->changed_newTime) {
1034 Volume *vp = vnp->volumePtr;
1035 afs_uint32 now = FT_ApproxTime();
1036 assert(vnp->cacheCheck == vp->cacheCheck);
1037 if (vnp->changed_newTime)
1038 vnp->disk.serverModifyTime = now;
1039 if (vnp->changed_newTime)
1040 V_updateDate(vp) = vp->updateTime = now;
1042 /* The inode has been changed. Write it out to disk */
1044 #ifdef AFS_DEMAND_ATTACH_FS
1045 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
1048 assert(V_needsSalvaged(vp));
1052 IHandle_t *ihP = vp->vnodeIndex[class].handle;
1054 off_t off = vnodeIndexOffset(vcp, vnp->vnodeNumber);
1058 Log("VPutVnode: can't open index file!\n");
1059 goto error_encountered;
1061 code = FDH_SEEK(fdP, off, SEEK_SET);
1063 Log("VPutVnode: can't seek on index file!\n");
1064 goto error_encountered;
1066 code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
1067 if (code != vcp->diskSize) {
1069 * Don't force volume offline if the inumber is out of
1070 * range or the inode table is full.
1073 if (code == BAD_IGET) {
1074 Log("VPutVnode: bad inumber %s\n",
1076 vp->vnodeIndex[class].handle->ih_ino));
1079 Log("VPutVnode: Couldn't write vnode %u, volume %u (%s)\n", vnp->vnodeNumber, V_id(vnp->volumePtr), V_name(vnp->volumePtr));
1080 #ifdef AFS_DEMAND_ATTACH_FS
1081 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
1084 VForceOffline_r(vp, 0);
1095 #ifdef AFS_DEMAND_ATTACH_FS
1099 VRequestSalvage_r(vp, SALVSYNC_ERROR, 0);
1108 vnp->changed_newTime = vnp->changed_oldTime = 0;
1111 ConvertWriteToReadLock(&vnp->lock);
1115 /* Move the vnode, vnp, to the new hash table given by the
1116 hash table index, newHash */
1118 moveHash(register Vnode * vnp, bit32 newHash)
1121 /* Remove it from the old hash chain */
1122 tvnp = VnodeHashTable[vnp->hashIndex];
1124 VnodeHashTable[vnp->hashIndex] = vnp->hashNext;
1126 while (tvnp && tvnp->hashNext != vnp)
1127 tvnp = tvnp->hashNext;
1129 tvnp->hashNext = vnp->hashNext;
1131 /* Add it to the new hash chain */
1132 vnp->hashNext = VnodeHashTable[newHash];
1133 VnodeHashTable[newHash] = vnp;
1134 vnp->hashIndex = newHash;
1139 StickOnLruChain_r(register Vnode * vnp, register struct VnodeClassInfo *vcp)
1141 /* Add it to the circular LRU list */
1142 if (vcp->lruHead == NULL)
1143 Abort("VPutVnode: vcp->lruHead==NULL");
1145 vnp->lruNext = vcp->lruHead;
1146 vnp->lruPrev = vcp->lruHead->lruPrev;
1147 vcp->lruHead->lruPrev = vnp;
1148 vnp->lruPrev->lruNext = vnp;
1151 /* If the vnode was just deleted, put it at the end of the chain so it
1152 * will be reused immediately */
1154 vcp->lruHead = vnp->lruNext;
1155 /* If caching is turned off, set volumeptr to NULL to invalidate the
1157 if (!TrustVnodeCacheEntry) {
1158 DeleteFromVnHashByVolumeTable(vnp);
1159 vnp->volumePtr = NULL;
1163 /* VCloseVnodeFiles - called when a volume is going off line. All open
1164 * files for vnodes in that volume are closed. This might be excessive,
1165 * since we may only be taking one volume of a volume group offline.
1168 VCloseVnodeFiles_r(Volume * vp)
1172 VnodeHashByVolumeChainHead * head;
1174 head = &VnodeHashByVolumeTable[VNVOLUME_HASH(vp->hashid)];
1175 #ifdef AFS_DEMAND_ATTACH_FS
1176 while (head->busy) {
1177 assert(pthread_cond_wait(&head->chain_busy_cv, &vol_glock_mutex) == 0);
1182 #endif /* AFS_DEMAND_ATTACH_FS */
1184 for (queue_Scan(head, vnp, nvnp, Vnode)) {
1185 if (vnp->volumePtr == vp) {
1186 IH_REALLYCLOSE(vnp->handle);
1190 #ifdef AFS_DEMAND_ATTACH_FS
1193 assert(pthread_cond_broadcast(&head->chain_busy_cv) == 0);
1194 #endif /* AFS_DEMAND_ATTACH_FS */
1197 /* VReleaseVnodeFiles - called when a volume is going detached. All open
1198 * files for vnodes in that volume are closed and all inode handles
1199 * for vnodes in that volume are released.
1202 VReleaseVnodeFiles_r(Volume * vp)
1206 VnodeHashByVolumeChainHead * head;
1208 head = &VnodeHashByVolumeTable[VNVOLUME_HASH(vp->hashid)];
1210 #ifdef AFS_DEMAND_ATTACH_FS
1211 while (head->busy) {
1212 assert(pthread_cond_wait(&head->chain_busy_cv, &vol_glock_mutex) == 0);
1217 #endif /* AFS_DEMAND_ATTACH_FS */
1219 for (queue_Scan(head, vnp, nvnp, Vnode)) {
1220 if (vnp->volumePtr == vp) {
1221 IH_RELEASE(vnp->handle);
1225 #ifdef AFS_DEMAND_ATTACH_FS
1228 assert(pthread_cond_broadcast(&head->chain_busy_cv) == 0);
1229 #endif /* AFS_DEMAND_ATTACH_FS */