pull-prototypes-to-head-20020821
[openafs.git] / src / vol / vnode.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
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
8  */
9
10 /*
11         System:         VICE-TWO
12         Module:         vnode.c
13         Institution:    The Information Technology Center, Carnegie-Mellon University
14
15  */
16 #include <afsconfig.h>
17 #include <afs/param.h>
18
19 RCSID("$Header$");
20
21 #include <errno.h>
22 #include <stdio.h>
23 #ifdef HAVE_STRING_H
24 #include <string.h>
25 #else
26 #ifdef HAVE_STRINGS_H
27 #include <strings.h>
28 #endif
29 #endif
30 #ifdef AFS_PTHREAD_ENV
31 #include <assert.h>
32 #else /* AFS_PTHREAD_ENV */
33 #include <afs/assert.h>
34 #endif /* AFS_PTHREAD_ENV */
35
36 #include <rx/xdr.h>
37 #include <afs/afsint.h>
38 #include "nfs.h"
39 #include <afs/errors.h>
40 #include "lock.h"
41 #include "lwp.h"
42 #include <afs/afssyscalls.h>
43 #include "ihandle.h"
44 #include "vnode.h"
45 #include "partition.h"
46 #include "volume.h"
47 #if defined(AFS_SGI_ENV)
48 #include "sys/types.h"
49 #include "fcntl.h"
50 #undef min
51 #undef max
52 #include "stdlib.h"
53 #endif
54 #ifdef AFS_NT40_ENV
55 #include <fcntl.h>
56 #include "ntops.h"
57 #else
58 #include <sys/file.h>
59 #ifdef  AFS_SUN5_ENV
60 #include <sys/fcntl.h>
61 #endif
62 #include <unistd.h>
63 #endif /* AFS_NT40_ENV */
64 #include <sys/stat.h>
65
66
67 struct VnodeClassInfo VnodeClassInfo[nVNODECLASSES];
68
69 private int moveHash();
70 void StickOnLruChain_r();
71 void VPutVnode_r();
72
73 #define BAD_IGET        -1000
74
75 /* There are two separate vnode queue types defined here:
76  * Each hash conflict chain -- is singly linked, with a single head
77  * pointer. New entries are added at the beginning. Old
78  * entries are removed by linear search, which generally
79  * only occurs after a disk read).
80  * LRU chain -- is doubly linked, single head pointer.
81  * Entries are added at the head, reclaimed from the tail,
82  * or removed from anywhere in the queue.
83  */
84
85
86 /* Vnode hash table.  Find hash chain by taking lower bits of
87  * (volume_hash_offset + vnode).
88  * This distributes the root inodes of the volumes over the
89  * hash table entries and also distributes the vnodes of
90  * volumes reasonably fairly.  The volume_hash_offset field
91  * for each volume is established as the volume comes on line
92  * by using the VOLUME_HASH_OFFSET macro.  This distributes the
93  * volumes fairly among the cache entries, both when servicing
94  * a small number of volumes and when servicing a large number.
95  */
96
97 /* logging stuff for finding bugs */
98 #define THELOGSIZE      5120
99 static afs_int32 theLog[THELOGSIZE];
100 static afs_int32 vnLogPtr=0;
101 VNLog(aop, anparms, av1, av2, av3, av4)
102 afs_int32 aop, anparms;
103 afs_int32 av1, av2, av3,av4; {
104     register afs_int32 temp;
105     afs_int32 data[4];
106
107     /* copy data to array */
108     data[0] = av1;
109     data[1] = av2;
110     data[2] = av3;
111     data[3] = av4;
112     if (anparms>4) anparms = 4; /* do bounds checking */
113
114     temp = (aop<<16) | anparms;
115     theLog[vnLogPtr++] = temp;
116     if (vnLogPtr >= THELOGSIZE) vnLogPtr = 0;
117     for(temp=0;temp<anparms;temp++) {
118         theLog[vnLogPtr++] = data[temp];
119         if (vnLogPtr >= THELOGSIZE) vnLogPtr = 0;
120     }
121 }
122
123 /* VolumeHashOffset -- returns a new value to be stored in the
124  * volumeHashOffset of a Volume structure.  Called when a
125  * volume is initialized.  Sets the volumeHashOffset so that
126  * vnode cache entries are distributed reasonably between
127  * volumes (the root vnodes of the volumes will hash to
128  * different values, and spacing is maintained between volumes
129  * when there are not many volumes represented), and spread
130  * equally amongst vnodes within a single volume.
131  */
132 int VolumeHashOffset_r() {
133     static int nextVolumeHashOffset = 0;
134     /* hashindex Must be power of two in size */
135 #   define hashShift 3
136 #   define hashMask ((1<<hashShift)-1)
137     static byte hashindex[1<<hashShift] = {0,128,64,192,32,160,96,224};
138     int offset;
139     offset = hashindex[nextVolumeHashOffset&hashMask]
140            + (nextVolumeHashOffset>>hashShift);
141     nextVolumeHashOffset++;
142     return offset;
143 }
144
145 /* Change hashindex (above) if you change this constant */
146 #define VNODE_HASH_TABLE_SIZE 256
147 private Vnode *VnodeHashTable[VNODE_HASH_TABLE_SIZE];
148 #define VNODE_HASH(volumeptr,vnodenumber)\
149     ((volumeptr->vnodeHashOffset + vnodenumber)&(VNODE_HASH_TABLE_SIZE-1))
150
151 /* Code to invalidate a vnode entry.  Called when we've damaged a vnode, and want
152     to prevent future VGetVnode's from applying to it.  Leaves it in the same hash bucket
153     but that shouldn't be important.  */
154 VInvalidateVnode_r(avnode)
155 register struct Vnode *avnode; {
156     avnode->changed_newTime = 0;    /* don't let it get flushed out again */
157     avnode->changed_oldTime = 0;
158     avnode->delete = 0;     /* it isn't deleted, erally */
159     avnode->cacheCheck = 0; /* invalid: prevents future vnode searches from working */
160 }
161
162 /* Not normally called by general client; called by volume.c */
163 VInitVnodes(class,nVnodes)
164     VnodeClass class;
165     int nVnodes;
166 {
167     byte *va;
168     register struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
169
170     vcp->allocs = vcp->gets = vcp->reads = vcp->writes = 0;
171     vcp->cacheSize = nVnodes;
172     switch(class) {
173       case vSmall:
174         assert(CHECKSIZE_SMALLVNODE);
175         vcp->lruHead = NULL;
176         vcp->residentSize = SIZEOF_SMALLVNODE;
177         vcp->diskSize = SIZEOF_SMALLDISKVNODE;
178         vcp->magic = SMALLVNODEMAGIC;
179         break;
180       case vLarge:
181         vcp->lruHead = NULL;
182         vcp->residentSize = SIZEOF_LARGEVNODE;
183         vcp->diskSize = SIZEOF_LARGEDISKVNODE;
184         vcp->magic = LARGEVNODEMAGIC;
185         break;
186     }
187     {   int s = vcp->diskSize-1;
188         int n = 0;
189         while (s)
190             s >>= 1, n++;
191         vcp->logSize = n;
192     }
193
194     if (nVnodes == 0)
195             return 0;
196
197     va = (byte *) calloc(nVnodes,vcp->residentSize);
198     assert (va != NULL);
199     while (nVnodes--) {
200         Vnode *vnp = (Vnode *) va;
201         vnp->nUsers = 0;    /* no context switches */
202         Lock_Init(&vnp->lock);
203         vnp->changed_oldTime = 0;
204         vnp->changed_newTime = 0;
205         vnp->volumePtr = NULL;
206         vnp->cacheCheck = 0;
207         vnp->delete = vnp->vnodeNumber = 0;
208 #ifdef AFS_PTHREAD_ENV
209         vnp->writer = (pthread_t) 0;
210 #else /* AFS_PTHREAD_ENV */
211         vnp->writer = (PROCESS) 0;
212 #endif /* AFS_PTHREAD_ENV */
213         vnp->hashIndex = 0;
214         vnp->handle = NULL;
215         if (vcp->lruHead == NULL)
216             vcp->lruHead = vnp->lruNext = vnp->lruPrev = vnp;
217         else {
218             vnp->lruNext = vcp->lruHead;
219             vnp->lruPrev = vcp->lruHead->lruPrev;
220             vcp->lruHead->lruPrev = vnp;
221             vnp->lruPrev->lruNext = vnp;
222             vcp->lruHead = vnp;
223         }
224         va += vcp->residentSize;
225     }
226     return 0;
227 }
228
229
230 /* allocate an *unused* vnode from the LRU chain, going backwards of course.  It shouldn't
231     be necessary to specify that nUsers == 0 since if it is in the list, nUsers
232     should be 0.  Things shouldn't be in lruq unless no one is using them.  */
233 Vnode *VGetFreeVnode_r(vcp)
234 struct VnodeClassInfo *vcp; {
235     register Vnode *vnp;
236
237     vnp = vcp->lruHead->lruPrev;
238     if (vnp->nUsers != 0 || CheckLock(&vnp->lock))
239         Abort("locked vnode in lruq");
240     VNLog(1, 2, vnp->vnodeNumber, (afs_int32) vnp);
241     IH_RELEASE(vnp->handle);
242     return vnp;
243 }
244
245 static mlkReason=0;
246 static mlkLastAlloc = 0;
247 static mlkLastOver = 0;
248 static mlkLastDelete = 0;
249
250 Vnode *VAllocVnode(ec,vp,type)
251     Error *ec;
252     Volume *vp;
253     VnodeType type;
254 {
255     Vnode *retVal;
256     VOL_LOCK
257     retVal = VAllocVnode_r(ec, vp, type);
258     VOL_UNLOCK
259     return retVal;
260 }
261
262 Vnode *VAllocVnode_r(ec,vp,type)
263     Error *ec;
264     Volume *vp;
265     VnodeType type;
266 {
267     register Vnode *vnp;
268     VnodeId vnodeNumber;
269     int newHash, bitNumber;
270     register struct VnodeClassInfo *vcp;
271     VnodeClass class;
272     Unique unique;
273
274     *ec = 0;
275     if (programType == fileServer && !V_inUse(vp)) {
276         if (vp->specialStatus) {
277             *ec = vp->specialStatus;
278         } else {
279             *ec = VOFFLINE;
280         }
281         return NULL;
282     }
283     class = vnodeTypeToClass(type);
284     vcp = &VnodeClassInfo[class];
285
286     if (!VolumeWriteable(vp)) {
287         *ec = VREADONLY;
288         return NULL;
289     }
290
291     unique = vp->nextVnodeUnique++;
292     if (!unique)
293        unique = vp->nextVnodeUnique++;
294
295     if (vp->nextVnodeUnique > V_uniquifier(vp)) {
296         VUpdateVolume_r(ec,vp);
297         if (*ec)
298             return NULL;
299     }
300
301     if (programType == fileServer) {
302         VAddToVolumeUpdateList_r(ec, vp);
303         if (*ec)
304             return NULL;
305     }
306
307     /* Find a slot in the bit map */
308     bitNumber = VAllocBitmapEntry_r(ec,vp,&vp->vnodeIndex[class]);
309     if (*ec)
310         return NULL;
311     vnodeNumber = bitNumberToVnodeNumber(bitNumber,class);
312
313     VNLog(2, 1, vnodeNumber);
314     /* Prepare to move it to the new hash chain */
315     newHash = VNODE_HASH(vp, vnodeNumber);
316     for (vnp = VnodeHashTable[newHash];
317          vnp && (vnp->vnodeNumber!=vnodeNumber || vnp->volumePtr!=vp
318                  || vnp->volumePtr->cacheCheck!=vnp->cacheCheck);
319          vnp = vnp->hashNext
320         );
321     if (vnp) {
322         /* slot already exists.  May even not be in lruq (consider store file locking a file being deleted)
323             so we may have to wait for it below */
324         VNLog(3, 2, vnodeNumber, (afs_int32) vnp);
325
326         /* If first user, remove it from the LRU chain.  We can assume that
327            there is at least one item in the queue */
328         if (++vnp->nUsers == 1) {
329             if (vnp == vcp->lruHead)
330                 vcp->lruHead = vcp->lruHead->lruNext;
331             vnp->lruPrev->lruNext = vnp->lruNext;
332             vnp->lruNext->lruPrev = vnp->lruPrev;
333             if (vnp == vcp->lruHead || vcp->lruHead == NULL)
334                 Abort("VGetVnode: lru chain addled!\n");
335             /* This won't block */
336             ObtainWriteLock(&vnp->lock);
337         } else {
338             /* follow locking hierarchy */
339             VOL_UNLOCK
340             ObtainWriteLock(&vnp->lock);
341             VOL_LOCK
342         }
343 #ifdef AFS_PTHREAD_ENV
344         vnp->writer = pthread_self();
345 #else /* AFS_PTHREAD_ENV */
346         LWP_CurrentProcess(&vnp->writer);
347 #endif /* AFS_PTHREAD_ENV */
348     }
349     else {
350         vnp = VGetFreeVnode_r(vcp);
351         /* Remove vnode from LRU chain and grab a write lock */
352         if (vnp == vcp->lruHead)
353             vcp->lruHead = vcp->lruHead->lruNext;
354         vnp->lruPrev->lruNext = vnp->lruNext;
355         vnp->lruNext->lruPrev = vnp->lruPrev;
356         if (vnp == vcp->lruHead || vcp->lruHead == NULL)
357             Abort("VGetVnode: lru chain addled!\n");
358         /* Initialize the header fields so noone allocates another
359            vnode with the same number */
360         vnp->vnodeNumber = vnodeNumber;
361         vnp->volumePtr = vp;
362         vnp->cacheCheck = vp->cacheCheck;
363         vnp->nUsers = 1;
364         moveHash(vnp, newHash);
365         /* This will never block */
366         ObtainWriteLock(&vnp->lock);
367 #ifdef AFS_PTHREAD_ENV
368         vnp->writer = pthread_self();
369 #else /* AFS_PTHREAD_ENV */
370         LWP_CurrentProcess(&vnp->writer);
371 #endif /* AFS_PTHREAD_ENV */
372         /* Sanity check:  is this vnode really not in use? */
373         { 
374           int size;
375           IHandle_t *ihP = vp->vnodeIndex[class].handle;
376           FdHandle_t *fdP;
377           off_t off = vnodeIndexOffset(vcp, vnodeNumber);
378
379           VOL_UNLOCK
380           fdP = IH_OPEN(ihP);
381           if (fdP == NULL)
382               Abort("VAllocVnode: can't open index file!\n");
383           if ((size = FDH_SIZE(fdP)) < 0)
384               Abort("VAllocVnode: can't stat index file!\n");
385           if (FDH_SEEK(fdP, off, SEEK_SET) < 0)
386               Abort("VAllocVnode: can't seek on index file!\n");
387           if (off < size) {
388               if (FDH_READ(fdP, &vnp->disk, vcp->diskSize) == vcp->diskSize) {
389                   if (vnp->disk.type != vNull)
390                       Abort("VAllocVnode:  addled bitmap or index!\n");
391               }
392           } else {
393               /* growing file - grow in a reasonable increment */
394               char *buf = (char *)malloc(16*1024);
395               memset(buf, 0, 16*1024);
396               FDH_WRITE(fdP, buf, 16*1024);
397               free(buf);
398           }
399           FDH_CLOSE(fdP);
400           VOL_LOCK
401       }
402         VNLog(4, 2, vnodeNumber, (afs_int32) vnp);
403     }
404
405     VNLog(5, 1, (afs_int32) vnp);
406 #ifdef AFS_PTHREAD_ENV
407     vnp->writer = pthread_self();
408 #else /* AFS_PTHREAD_ENV */
409     LWP_CurrentProcess(&vnp->writer);
410 #endif /* AFS_PTHREAD_ENV */
411     memset(&vnp->disk, 0, sizeof(vnp->disk));
412     vnp->changed_newTime = 0; /* set this bit when vnode is updated */
413     vnp->changed_oldTime = 0; /* set this on CopyOnWrite. */
414     vnp->delete = 0;
415     vnp->disk.vnodeMagic = vcp->magic;
416     vnp->disk.type = type;
417     vnp->disk.uniquifier = unique;
418     vnp->handle = NULL;
419     vcp->allocs++;
420     return vnp;
421 }
422     
423 Vnode *VGetVnode(ec,vp,vnodeNumber,locktype)
424     Error *ec;
425     Volume *vp;
426     VnodeId vnodeNumber;
427     int locktype;       /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
428 {
429     Vnode *retVal;
430     VOL_LOCK
431     retVal = VGetVnode_r(ec, vp, vnodeNumber, locktype);
432     VOL_UNLOCK
433     return retVal;
434 }
435
436 Vnode *VGetVnode_r(ec,vp,vnodeNumber,locktype)
437     Error *ec;
438     Volume *vp;
439     VnodeId vnodeNumber;
440     int locktype;       /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
441 {
442     register Vnode *vnp;
443     int newHash;
444     VnodeClass class;
445     struct VnodeClassInfo *vcp;
446
447     *ec = 0;
448     mlkReason = 0;  /* last call didn't fail */
449
450     if (vnodeNumber == 0) {
451         *ec = VNOVNODE;
452         mlkReason = 1;
453         return NULL;
454     }
455
456     VNLog(100, 1, vnodeNumber);
457     if (programType == fileServer && !V_inUse(vp)) {
458         *ec = (vp->specialStatus ? vp->specialStatus : VOFFLINE);
459
460         /* If the volume is VBUSY (being cloned or dumped) and this is
461          * a READ operation, then don't fail.
462          */
463         if ((*ec != VBUSY) || (locktype != READ_LOCK)) {
464            mlkReason = 2;
465            return NULL;
466         }
467         *ec = 0;
468     }
469     class = vnodeIdToClass(vnodeNumber);
470     vcp = &VnodeClassInfo[class];
471     if (locktype == WRITE_LOCK && !VolumeWriteable(vp)) {
472         *ec = VREADONLY;
473         mlkReason = 3;
474         return NULL;
475     }
476
477     if (locktype == WRITE_LOCK && programType == fileServer) {
478         VAddToVolumeUpdateList_r(ec, vp);
479         if (*ec) {
480             mlkReason = 1000 + *ec;
481             return NULL;
482         }
483     }
484
485     /* See whether the vnode is in the cache. */
486     newHash = VNODE_HASH(vp, vnodeNumber);
487     for (vnp = VnodeHashTable[newHash];
488          vnp && (vnp->vnodeNumber!=vnodeNumber || vnp->volumePtr!=vp
489                  || vnp->volumePtr->cacheCheck!=vnp->cacheCheck);
490          vnp = vnp->hashNext
491         );
492     vcp->gets++;
493     if (vnp == NULL) {
494         int     n;
495         IHandle_t *ihP = vp->vnodeIndex[class].handle;
496         FdHandle_t *fdP;
497         /* Not in cache; tentatively grab most distantly used one from the LRU
498            chain */
499         vcp->reads++;
500         vnp = VGetFreeVnode_r(vcp);
501         /* Remove it from the old hash chain */
502         moveHash(vnp, newHash);
503         /* Remove it from the LRU chain */
504         if (vnp == vcp->lruHead)
505             vcp->lruHead = vcp->lruHead->lruNext;
506         if (vnp == vcp->lruHead || vcp->lruHead == NULL)
507             Abort("VGetVnode: lru chain addled!\n");
508         vnp->lruPrev->lruNext = vnp->lruNext;
509         vnp->lruNext->lruPrev = vnp->lruPrev;
510         /* Initialize */
511         vnp->changed_newTime = vnp->changed_oldTime = 0;
512         vnp->delete = 0;
513         vnp->vnodeNumber = vnodeNumber;
514         vnp->volumePtr = vp;
515         vnp->cacheCheck = vp->cacheCheck;
516         vnp->nUsers = 1;
517
518         /* This will never block */
519         ObtainWriteLock(&vnp->lock);
520 #ifdef AFS_PTHREAD_ENV
521         vnp->writer = pthread_self();
522 #else /* AFS_PTHREAD_ENV */
523           LWP_CurrentProcess(&vnp->writer);
524 #endif /* AFS_PTHREAD_ENV */
525
526         /* Read vnode from volume index */
527         VOL_UNLOCK
528         fdP = IH_OPEN(ihP);
529         if (fdP == NULL) {
530             Log("VGetVnode: can't open index dev=%d, i=%s\n",
531                 vp->device, PrintInode(NULL,
532                                        vp->vnodeIndex[class].handle->ih_ino));
533             *ec = VIO;
534             mlkReason=9;
535         }
536         else if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, vnodeNumber),
537                          SEEK_SET) < 0) {
538             Log ("VGetVnode: can't seek on index file vn=%d\n",vnodeNumber);
539             *ec = VIO;
540             mlkReason=10;
541             FDH_REALLYCLOSE(fdP);
542         }
543         else if ((n = FDH_READ(fdP, (char*)&vnp->disk, vcp->diskSize))
544                  != vcp->diskSize) {
545             /* Don't take volume off line if the inumber is out of range
546                or the inode table is full. */
547             FDH_REALLYCLOSE(fdP);
548             VOL_LOCK
549             if(n == BAD_IGET) {
550                 Log("VGetVnode: bad inumber %s\n",
551                     PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
552                 *ec = VIO;
553                 mlkReason = 4;
554             }
555             /* Check for disk errors.  Anything else just means that the vnode
556                is not allocated */
557             if (n == -1 && errno == EIO) {
558                 Log("VGetVnode: Couldn't read vnode %d, volume %u (%s); volume needs salvage\n",  
559                     vnodeNumber, V_id(vp), V_name(vp));
560                 VForceOffline_r(vp);
561                 *ec = VSALVAGE;
562                 mlkReason = 4;
563             } else {
564                 mlkReason = 5;
565                 *ec = VIO;
566             }
567             VInvalidateVnode_r(vnp);
568             if (vnp->nUsers-- == 1)
569                 StickOnLruChain_r(vnp,vcp);
570             ReleaseWriteLock(&vnp->lock);
571             return NULL;
572         }
573         FDH_CLOSE(fdP);
574         VOL_LOCK
575         /* Quick check to see that the data is reasonable */
576         if (vnp->disk.vnodeMagic != vcp->magic || vnp->disk.type == vNull) {
577             if (vnp->disk.type == vNull) {
578                 *ec = VNOVNODE;
579                 mlkReason = 6;
580                 VInvalidateVnode_r(vnp);
581                 if (vnp->nUsers-- == 1)
582                     StickOnLruChain_r(vnp,vcp);
583                 ReleaseWriteLock(&vnp->lock);
584                 return NULL;    /* The vnode is not allocated */
585             }
586             else {
587                 struct vnodeIndex *index = &vp->vnodeIndex[class];
588                 int bitNumber = vnodeIdToBitNumber(vnodeNumber);
589                 int offset = bitNumber >> 3;
590
591                 /* Test to see if vnode number is valid. */
592                 if ((offset >= index->bitmapSize) ||
593                     ((*(index->bitmap+offset) & (1<<(bitNumber&0x7))) == 0)) {
594                     Log("VGetVnode: Request for unallocated vnode %u, volume %u (%s) denied.\n",
595                         vnodeNumber, V_id(vp), V_name(vp));
596                     mlkReason = 11;
597                     *ec = VNOVNODE;
598                 }
599                 else {
600                     Log("VGetVnode: Bad magic number, vnode %d, volume %u (%s); volume needs salvage\n",  
601                 vnodeNumber, V_id(vp), V_name(vp));
602                     vp->goingOffline = 1;       /* used to call VOffline, but that would mess
603                                                    up the volume ref count if called here */
604                     *ec = VSALVAGE;
605                     mlkReason = 7;
606                 }
607                 VInvalidateVnode_r(vnp);
608                 if (vnp->nUsers-- == 1)
609                     StickOnLruChain_r(vnp,vcp);
610                 ReleaseWriteLock(&vnp->lock);
611                 return NULL;
612             }
613         }
614         IH_INIT(vnp->handle, V_device(vp), V_parentId(vp), VN_GET_INO(vnp));
615         ReleaseWriteLock(&vnp->lock);
616     } else {
617         VNLog(101, 2, vnodeNumber, (afs_int32) vnp);
618         if (++vnp->nUsers == 1) {
619         /* First user.  Remove it from the LRU chain.  We can assume that
620            there is at least one item in the queue */
621             if (vnp == vcp->lruHead)
622                 vcp->lruHead = vcp->lruHead->lruNext;
623             if (vnp == vcp->lruHead || vcp->lruHead == NULL)
624                 Abort("VGetVnode: lru chain addled!\n");
625             vnp->lruPrev->lruNext = vnp->lruNext;
626             vnp->lruNext->lruPrev = vnp->lruPrev;
627         }
628     }
629     VOL_UNLOCK
630     if (locktype == READ_LOCK)
631         ObtainReadLock(&vnp->lock);
632     else {
633         ObtainWriteLock(&vnp->lock);
634 #ifdef AFS_PTHREAD_ENV
635         vnp->writer = pthread_self();
636 #else /* AFS_PTHREAD_ENV */
637         LWP_CurrentProcess(&vnp->writer);
638 #endif /* AFS_PTHREAD_ENV */
639     }
640     VOL_LOCK
641     /* Check that the vnode hasn't been removed while we were obtaining
642        the lock */
643     VNLog(102, 2, vnodeNumber, (afs_int32) vnp);
644     if (vnp->disk.type == vNull) {
645         if (vnp->nUsers-- == 1)
646             StickOnLruChain_r(vnp,vcp);
647         if (locktype == READ_LOCK)
648             ReleaseReadLock(&vnp->lock);
649         else
650             ReleaseWriteLock(&vnp->lock);
651         *ec = VNOVNODE;
652         mlkReason = 8;
653         /* vnode is labelled correctly by now, so we don't have to invalidate it */
654         return NULL;
655     }
656     if (programType == fileServer)
657         VBumpVolumeUsage_r(vnp->volumePtr);/* Hack; don't know where it should be
658                                               called from.  Maybe VGetVolume */
659     return vnp;
660 }
661
662
663 int  TrustVnodeCacheEntry = 1;
664 /* This variable is bogus--when it's set to 0, the hash chains fill
665    up with multiple versions of the same vnode.  Should fix this!! */
666 void
667 VPutVnode(ec,vnp)
668     Error *ec;
669     register Vnode *vnp;
670 {
671     VOL_LOCK
672     VPutVnode_r(ec,vnp);
673     VOL_UNLOCK
674 }
675
676 void
677 VPutVnode_r(ec,vnp)
678     Error *ec;
679     register Vnode *vnp;
680 {
681     int writeLocked, offset;
682     VnodeClass class;
683     struct VnodeClassInfo *vcp;
684     int code;
685
686     *ec = 0;
687     assert (vnp->nUsers != 0);
688     class = vnodeIdToClass(vnp->vnodeNumber);
689     vcp = &VnodeClassInfo[class];
690     assert(vnp->disk.vnodeMagic == vcp->magic);
691     VNLog(200, 2, vnp->vnodeNumber, (afs_int32) vnp);
692
693     writeLocked = WriteLocked(&vnp->lock);
694     if (writeLocked) {
695 #ifdef AFS_PTHREAD_ENV
696         pthread_t thisProcess = pthread_self();
697 #else /* AFS_PTHREAD_ENV */
698         PROCESS thisProcess;
699         LWP_CurrentProcess(&thisProcess);
700 #endif /* AFS_PTHREAD_ENV */
701         VNLog(201, 2, (afs_int32) vnp,
702               ((vnp->changed_newTime) << 1) | ((vnp->changed_oldTime) << 1) | vnp->delete);
703         if (thisProcess != vnp->writer)
704             Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",vnp);
705         if (vnp->changed_oldTime || vnp->changed_newTime || vnp->delete) {
706             Volume *vp = vnp->volumePtr;
707             afs_int32 now = FT_ApproxTime();
708             assert(vnp->cacheCheck == vp->cacheCheck);
709
710             if (vnp->delete) {
711                 /* No longer any directory entries for this vnode. Free the Vnode */
712                 memset(&vnp->disk, 0, sizeof (vnp->disk));
713                 mlkLastDelete = vnp->vnodeNumber;
714                 /* delete flag turned off further down */
715                 VNLog(202, 2, vnp->vnodeNumber, (afs_int32) vnp);
716             } else if (vnp->changed_newTime) {
717                 vnp->disk.serverModifyTime = now;
718             }
719             if (vnp->changed_newTime)
720                 V_updateDate(vp) = vp->updateTime = now;
721
722             /* The vnode has been changed. Write it out to disk */
723             if (!V_inUse(vp)) {
724                 assert(V_needsSalvaged(vp));
725                 *ec = VSALVAGE;
726             } else {
727                 IHandle_t *ihP = vp->vnodeIndex[class].handle;
728                 FdHandle_t *fdP;
729                 VOL_UNLOCK
730                 fdP = IH_OPEN(ihP);
731                 if (fdP == NULL)
732                     Abort("VPutVnode: can't open index file!\n");
733                 offset = vnodeIndexOffset(vcp, vnp->vnodeNumber);
734                 if (FDH_SEEK(fdP, offset, SEEK_SET) < 0) {
735                    Abort("VPutVnode: can't seek on index file! fdp=0x%x offset=%d, errno=%d\n",
736                          fdP, offset, errno);
737                 }
738                 code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
739                 if (code != vcp->diskSize) {
740                     /* Don't force volume offline if the inumber is out of
741                      * range or the inode table is full.
742                      */
743                     VOL_LOCK
744                     if (code == BAD_IGET) {
745                         Log("VPutVnode: bad inumber %s\n",
746                             PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
747                         *ec = VIO;
748                     } else {
749                         Log("VPutVnode: Couldn't write vnode %d, volume %u (%s)\n",
750                             vnp->vnodeNumber, V_id(vnp->volumePtr),
751                             V_name(vnp->volumePtr));
752                         VForceOffline_r(vp);
753                         *ec = VSALVAGE;
754                     }
755                     VOL_UNLOCK
756                     FDH_REALLYCLOSE(fdP);
757                 } else {
758                     FDH_CLOSE(fdP);
759                 }
760                 VOL_LOCK
761
762                 /* If the vnode is to be deleted, and we wrote the vnode out,
763                  * free its bitmap entry. Do after the vnode is written so we
764                  * don't allocate from bitmap before the vnode is written
765                  * (doing so could cause a "addled bitmap" message).
766                  */
767                 if (vnp->delete && !*ec) {
768                     VFreeBitMapEntry_r(ec, &vp->vnodeIndex[class],
769                                        vnodeIdToBitNumber(vnp->vnodeNumber));
770                 }
771             }
772             vcp->writes++;
773             vnp->changed_newTime = vnp->changed_oldTime = 0;
774         }
775     } else { /* Not write locked */
776         if (vnp->changed_newTime || vnp->changed_oldTime || vnp->delete)
777             Abort("VPutVnode: Change or delete flag for vnode 0x%x is set but vnode is not write locked!\n", vnp);
778     }
779
780     /* Do not look at disk portion of vnode after this point; it may
781        have been deleted above */
782     if (vnp->nUsers-- == 1)
783         StickOnLruChain_r(vnp,vcp);
784     vnp->delete = 0;
785
786     if (writeLocked)
787         ReleaseWriteLock(&vnp->lock);
788     else
789         ReleaseReadLock(&vnp->lock);
790 }
791
792 /*
793  * Make an attempt to convert a vnode lock from write to read.
794  * Do nothing if the vnode isn't write locked or the vnode has
795  * been deleted.
796  */
797 int VVnodeWriteToRead(ec,vnp)
798     Error *ec;
799     register Vnode *vnp;
800 {
801     int retVal;
802     VOL_LOCK
803     retVal = VVnodeWriteToRead_r(ec, vnp);
804     VOL_UNLOCK
805     return retVal;
806 }
807
808 int VVnodeWriteToRead_r(ec,vnp)
809     Error *ec;
810     register Vnode *vnp;
811 {
812     int writeLocked;
813     VnodeClass class;
814     struct VnodeClassInfo *vcp;
815     int code;
816 #ifdef AFS_PTHREAD_ENV
817     pthread_t thisProcess;
818 #else /* AFS_PTHREAD_ENV */
819     PROCESS thisProcess;
820 #endif /* AFS_PTHREAD_ENV */
821
822     *ec = 0;
823     assert (vnp->nUsers != 0);
824     class = vnodeIdToClass(vnp->vnodeNumber);
825     vcp = &VnodeClassInfo[class];
826     assert(vnp->disk.vnodeMagic == vcp->magic);
827     writeLocked = WriteLocked(&vnp->lock);
828     VNLog(300, 2, vnp->vnodeNumber, (afs_int32) vnp);
829
830     if (!writeLocked) {
831         return 0;
832     }
833
834 #ifdef AFS_PTHREAD_ENV
835     thisProcess = pthread_self();
836 #else /* AFS_PTHREAD_ENV */
837     LWP_CurrentProcess(&thisProcess);
838 #endif /* AFS_PTHREAD_ENV */
839
840     VNLog(301, 2, (afs_int32) vnp,
841           ((vnp->changed_newTime) << 1) | ((vnp->changed_oldTime) << 1) |
842           vnp->delete);
843     if (thisProcess != vnp->writer)
844         Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",vnp);
845     if (vnp->delete) {
846         return 0;
847     }
848     if (vnp->changed_oldTime || vnp->changed_newTime) {
849         Volume *vp = vnp->volumePtr;
850         afs_int32 now = FT_ApproxTime();
851         assert(vnp->cacheCheck == vp->cacheCheck);
852         if (vnp->changed_newTime)
853             vnp->disk.serverModifyTime = now;
854         if (vnp->changed_newTime)
855             V_updateDate(vp) = vp->updateTime = now;
856
857         /* The inode has been changed.  Write it out to disk */
858         if (!V_inUse(vp)) {
859             assert(V_needsSalvaged(vp));
860             *ec = VSALVAGE;
861         } else {
862             IHandle_t *ihP = vp->vnodeIndex[class].handle;
863             FdHandle_t *fdP;
864             off_t off = vnodeIndexOffset(vcp, vnp->vnodeNumber);
865             VOL_UNLOCK
866             fdP = IH_OPEN(ihP);
867             if (fdP == NULL)
868                 Abort("VPutVnode: can't open index file!\n");
869             code = FDH_SEEK(fdP, off, SEEK_SET);
870             if (code < 0)
871                 Abort("VPutVnode: can't seek on index file!\n");
872             code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
873             if (code != vcp->diskSize) {
874                 /*
875                  * Don't force volume offline if the inumber is out of
876                  * range or the inode table is full.
877                  */
878                 VOL_LOCK
879                 if(code == BAD_IGET)
880                 {
881                             Log("VPutVnode: bad inumber %d\n",
882                                     vp->vnodeIndex[class].handle->ih_ino);
883                             *ec = VIO;
884                 } else {
885                     Log("VPutVnode: Couldn't write vnode %d, volume %u (%s)\n",
886                         vnp->vnodeNumber, V_id(vnp->volumePtr),
887                         V_name(vnp->volumePtr));
888                         VForceOffline_r(vp);
889                     *ec = VSALVAGE;
890                 }
891                 VOL_UNLOCK
892             }
893             FDH_CLOSE(fdP);
894             VOL_LOCK
895         }
896         vcp->writes++;
897         vnp->changed_newTime = vnp->changed_oldTime = 0;
898     }
899
900     ConvertWriteToReadLock(&vnp->lock);
901     return 0;
902 }
903
904 /* Move the vnode, vnp, to the new hash table given by the
905    hash table index, newHash */
906 static int moveHash(vnp, newHash)
907     register Vnode *vnp;
908     bit32 newHash;
909 {
910     Vnode *tvnp;
911  /* Remove it from the old hash chain */
912     tvnp = VnodeHashTable[vnp->hashIndex];
913     if (tvnp == vnp)
914         VnodeHashTable[vnp->hashIndex] = vnp->hashNext;
915     else {
916         while (tvnp && tvnp->hashNext != vnp)
917             tvnp = tvnp->hashNext;
918         if (tvnp)
919             tvnp->hashNext = vnp->hashNext;
920     }
921  /* Add it to the new hash chain */
922     vnp->hashNext = VnodeHashTable[newHash];
923     VnodeHashTable[newHash] = vnp;
924     vnp->hashIndex = newHash;
925     return 0;
926 }
927
928 void
929 StickOnLruChain_r(vnp,vcp)
930     register Vnode *vnp;
931     register struct VnodeClassInfo *vcp;
932 {
933  /* Add it to the circular LRU list */
934     if (vcp->lruHead == NULL)
935         Abort("VPutVnode: vcp->lruHead==NULL");
936     else {
937         vnp->lruNext = vcp->lruHead;
938         vnp->lruPrev = vcp->lruHead->lruPrev;
939         vcp->lruHead->lruPrev = vnp;
940         vnp->lruPrev->lruNext = vnp;
941         vcp->lruHead = vnp;
942     }
943  /* If the vnode was just deleted, put it at the end of the chain so it
944     will be reused immediately */
945     if (vnp->delete)
946         vcp->lruHead = vnp->lruNext;
947  /* If caching is turned off, set volumeptr to NULL to invalidate the
948     entry */
949     if (!TrustVnodeCacheEntry)
950         vnp->volumePtr = NULL;
951 }
952
953 /* VCloseVnodeFiles - called when a volume is going off line. All open
954  * files for vnodes in that volume are closed. This might be excessive,
955  * since we may only be taking one volume of a volume group offline.
956  */
957 void VCloseVnodeFiles_r(Volume *vp)
958 {
959     int i;
960     Vnode *vnp;
961
962     for (i=0; i<VNODE_HASH_TABLE_SIZE; i++) {
963         for (vnp = VnodeHashTable[i]; vnp; vnp = vnp->hashNext) {
964             if (vnp->volumePtr == vp) {
965                 IH_REALLYCLOSE(vnp->handle);
966             }
967         }
968     }
969 }
970
971 /* VReleaseVnodeFiles - called when a volume is going detached. All open
972  * files for vnodes in that volume are closed and all inode handles
973  * for vnodes in that volume are released.
974  */
975 void VReleaseVnodeFiles_r(Volume *vp)
976 {
977     int i;
978     Vnode *vnp;
979
980     for (i=0; i<VNODE_HASH_TABLE_SIZE; i++) {
981         for (vnp = VnodeHashTable[i]; vnp; vnp = vnp->hashNext) {
982             if (vnp->volumePtr == vp) {
983                 IH_RELEASE(vnp->handle);
984             }
985         }
986     }
987 }