viced-vol-ansification-20030303
[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 void VInvalidateVnode_r(register struct Vnode *avnode)
155 {
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 int VInitVnodes(VnodeClass class, int nVnodes)
164 {
165     byte *va;
166     register struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
167
168     vcp->allocs = vcp->gets = vcp->reads = vcp->writes = 0;
169     vcp->cacheSize = nVnodes;
170     switch(class) {
171       case vSmall:
172         assert(CHECKSIZE_SMALLVNODE);
173         vcp->lruHead = NULL;
174         vcp->residentSize = SIZEOF_SMALLVNODE;
175         vcp->diskSize = SIZEOF_SMALLDISKVNODE;
176         vcp->magic = SMALLVNODEMAGIC;
177         break;
178       case vLarge:
179         vcp->lruHead = NULL;
180         vcp->residentSize = SIZEOF_LARGEVNODE;
181         vcp->diskSize = SIZEOF_LARGEDISKVNODE;
182         vcp->magic = LARGEVNODEMAGIC;
183         break;
184     }
185     {   int s = vcp->diskSize-1;
186         int n = 0;
187         while (s)
188             s >>= 1, n++;
189         vcp->logSize = n;
190     }
191
192     if (nVnodes == 0)
193             return 0;
194
195     va = (byte *) calloc(nVnodes,vcp->residentSize);
196     assert (va != NULL);
197     while (nVnodes--) {
198         Vnode *vnp = (Vnode *) va;
199         vnp->nUsers = 0;    /* no context switches */
200         Lock_Init(&vnp->lock);
201         vnp->changed_oldTime = 0;
202         vnp->changed_newTime = 0;
203         vnp->volumePtr = NULL;
204         vnp->cacheCheck = 0;
205         vnp->delete = vnp->vnodeNumber = 0;
206 #ifdef AFS_PTHREAD_ENV
207         vnp->writer = (pthread_t) 0;
208 #else /* AFS_PTHREAD_ENV */
209         vnp->writer = (PROCESS) 0;
210 #endif /* AFS_PTHREAD_ENV */
211         vnp->hashIndex = 0;
212         vnp->handle = NULL;
213         if (vcp->lruHead == NULL)
214             vcp->lruHead = vnp->lruNext = vnp->lruPrev = vnp;
215         else {
216             vnp->lruNext = vcp->lruHead;
217             vnp->lruPrev = vcp->lruHead->lruPrev;
218             vcp->lruHead->lruPrev = vnp;
219             vnp->lruPrev->lruNext = vnp;
220             vcp->lruHead = vnp;
221         }
222         va += vcp->residentSize;
223     }
224     return 0;
225 }
226
227
228 /* allocate an *unused* vnode from the LRU chain, going backwards of course.  It shouldn't
229     be necessary to specify that nUsers == 0 since if it is in the list, nUsers
230     should be 0.  Things shouldn't be in lruq unless no one is using them.  */
231 Vnode *VGetFreeVnode_r(vcp)
232 struct VnodeClassInfo *vcp; {
233     register Vnode *vnp;
234
235     vnp = vcp->lruHead->lruPrev;
236     if (vnp->nUsers != 0 || CheckLock(&vnp->lock))
237         Abort("locked vnode in lruq");
238     VNLog(1, 2, vnp->vnodeNumber, (afs_int32) vnp);
239     IH_RELEASE(vnp->handle);
240     return vnp;
241 }
242
243 static mlkReason=0;
244 static mlkLastAlloc = 0;
245 static mlkLastOver = 0;
246 static mlkLastDelete = 0;
247
248 Vnode *VAllocVnode(ec,vp,type)
249     Error *ec;
250     Volume *vp;
251     VnodeType type;
252 {
253     Vnode *retVal;
254     VOL_LOCK
255     retVal = VAllocVnode_r(ec, vp, type);
256     VOL_UNLOCK
257     return retVal;
258 }
259
260 Vnode *VAllocVnode_r(ec,vp,type)
261     Error *ec;
262     Volume *vp;
263     VnodeType type;
264 {
265     register Vnode *vnp;
266     VnodeId vnodeNumber;
267     int newHash, bitNumber;
268     register struct VnodeClassInfo *vcp;
269     VnodeClass class;
270     Unique unique;
271
272     *ec = 0;
273     if (programType == fileServer && !V_inUse(vp)) {
274         if (vp->specialStatus) {
275             *ec = vp->specialStatus;
276         } else {
277             *ec = VOFFLINE;
278         }
279         return NULL;
280     }
281     class = vnodeTypeToClass(type);
282     vcp = &VnodeClassInfo[class];
283
284     if (!VolumeWriteable(vp)) {
285         *ec = VREADONLY;
286         return NULL;
287     }
288
289     unique = vp->nextVnodeUnique++;
290     if (!unique)
291        unique = vp->nextVnodeUnique++;
292
293     if (vp->nextVnodeUnique > V_uniquifier(vp)) {
294         VUpdateVolume_r(ec,vp);
295         if (*ec)
296             return NULL;
297     }
298
299     if (programType == fileServer) {
300         VAddToVolumeUpdateList_r(ec, vp);
301         if (*ec)
302             return NULL;
303     }
304
305     /* Find a slot in the bit map */
306     bitNumber = VAllocBitmapEntry_r(ec,vp,&vp->vnodeIndex[class]);
307     if (*ec)
308         return NULL;
309     vnodeNumber = bitNumberToVnodeNumber(bitNumber,class);
310
311     VNLog(2, 1, vnodeNumber);
312     /* Prepare to move it to the new hash chain */
313     newHash = VNODE_HASH(vp, vnodeNumber);
314     for (vnp = VnodeHashTable[newHash];
315          vnp && (vnp->vnodeNumber!=vnodeNumber || vnp->volumePtr!=vp
316                  || vnp->volumePtr->cacheCheck!=vnp->cacheCheck);
317          vnp = vnp->hashNext
318         );
319     if (vnp) {
320         /* slot already exists.  May even not be in lruq (consider store file locking a file being deleted)
321             so we may have to wait for it below */
322         VNLog(3, 2, vnodeNumber, (afs_int32) vnp);
323
324         /* If first user, remove it from the LRU chain.  We can assume that
325            there is at least one item in the queue */
326         if (++vnp->nUsers == 1) {
327             if (vnp == vcp->lruHead)
328                 vcp->lruHead = vcp->lruHead->lruNext;
329             vnp->lruPrev->lruNext = vnp->lruNext;
330             vnp->lruNext->lruPrev = vnp->lruPrev;
331             if (vnp == vcp->lruHead || vcp->lruHead == NULL)
332                 Abort("VGetVnode: lru chain addled!\n");
333             /* This won't block */
334             ObtainWriteLock(&vnp->lock);
335         } else {
336             /* follow locking hierarchy */
337             VOL_UNLOCK
338             ObtainWriteLock(&vnp->lock);
339             VOL_LOCK
340         }
341 #ifdef AFS_PTHREAD_ENV
342         vnp->writer = pthread_self();
343 #else /* AFS_PTHREAD_ENV */
344         LWP_CurrentProcess(&vnp->writer);
345 #endif /* AFS_PTHREAD_ENV */
346     }
347     else {
348         vnp = VGetFreeVnode_r(vcp);
349         /* Remove vnode from LRU chain and grab a write lock */
350         if (vnp == vcp->lruHead)
351             vcp->lruHead = vcp->lruHead->lruNext;
352         vnp->lruPrev->lruNext = vnp->lruNext;
353         vnp->lruNext->lruPrev = vnp->lruPrev;
354         if (vnp == vcp->lruHead || vcp->lruHead == NULL)
355             Abort("VGetVnode: lru chain addled!\n");
356         /* Initialize the header fields so noone allocates another
357            vnode with the same number */
358         vnp->vnodeNumber = vnodeNumber;
359         vnp->volumePtr = vp;
360         vnp->cacheCheck = vp->cacheCheck;
361         vnp->nUsers = 1;
362         moveHash(vnp, newHash);
363         /* This will never block */
364         ObtainWriteLock(&vnp->lock);
365 #ifdef AFS_PTHREAD_ENV
366         vnp->writer = pthread_self();
367 #else /* AFS_PTHREAD_ENV */
368         LWP_CurrentProcess(&vnp->writer);
369 #endif /* AFS_PTHREAD_ENV */
370         /* Sanity check:  is this vnode really not in use? */
371         { 
372           int size;
373           IHandle_t *ihP = vp->vnodeIndex[class].handle;
374           FdHandle_t *fdP;
375           off_t off = vnodeIndexOffset(vcp, vnodeNumber);
376
377           VOL_UNLOCK
378           fdP = IH_OPEN(ihP);
379           if (fdP == NULL)
380               Abort("VAllocVnode: can't open index file!\n");
381           if ((size = FDH_SIZE(fdP)) < 0)
382               Abort("VAllocVnode: can't stat index file!\n");
383           if (FDH_SEEK(fdP, off, SEEK_SET) < 0)
384               Abort("VAllocVnode: can't seek on index file!\n");
385           if (off < size) {
386               if (FDH_READ(fdP, &vnp->disk, vcp->diskSize) == vcp->diskSize) {
387                   if (vnp->disk.type != vNull)
388                       Abort("VAllocVnode:  addled bitmap or index!\n");
389               }
390           } else {
391               /* growing file - grow in a reasonable increment */
392               char *buf = (char *)malloc(16*1024);
393               if (!buf) Abort("VAllocVnode: malloc failed\n");
394               memset(buf, 0, 16*1024);
395               FDH_WRITE(fdP, buf, 16*1024);
396               free(buf);
397           }
398           FDH_CLOSE(fdP);
399           VOL_LOCK
400       }
401         VNLog(4, 2, vnodeNumber, (afs_int32) vnp);
402     }
403
404     VNLog(5, 1, (afs_int32) vnp);
405 #ifdef AFS_PTHREAD_ENV
406     vnp->writer = pthread_self();
407 #else /* AFS_PTHREAD_ENV */
408     LWP_CurrentProcess(&vnp->writer);
409 #endif /* AFS_PTHREAD_ENV */
410     memset(&vnp->disk, 0, sizeof(vnp->disk));
411     vnp->changed_newTime = 0; /* set this bit when vnode is updated */
412     vnp->changed_oldTime = 0; /* set this on CopyOnWrite. */
413     vnp->delete = 0;
414     vnp->disk.vnodeMagic = vcp->magic;
415     vnp->disk.type = type;
416     vnp->disk.uniquifier = unique;
417     vnp->handle = NULL;
418     vcp->allocs++;
419     return vnp;
420 }
421     
422 Vnode *VGetVnode(ec,vp,vnodeNumber,locktype)
423     Error *ec;
424     Volume *vp;
425     VnodeId vnodeNumber;
426     int locktype;       /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
427 {
428     Vnode *retVal;
429     VOL_LOCK
430     retVal = VGetVnode_r(ec, vp, vnodeNumber, locktype);
431     VOL_UNLOCK
432     return retVal;
433 }
434
435 Vnode *VGetVnode_r(ec,vp,vnodeNumber,locktype)
436     Error *ec;
437     Volume *vp;
438     VnodeId vnodeNumber;
439     int locktype;       /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
440 {
441     register Vnode *vnp;
442     int newHash;
443     VnodeClass class;
444     struct VnodeClassInfo *vcp;
445
446     *ec = 0;
447     mlkReason = 0;  /* last call didn't fail */
448
449     if (vnodeNumber == 0) {
450         *ec = VNOVNODE;
451         mlkReason = 1;
452         return NULL;
453     }
454
455     VNLog(100, 1, vnodeNumber);
456     if (programType == fileServer && !V_inUse(vp)) {
457         *ec = (vp->specialStatus ? vp->specialStatus : VOFFLINE);
458
459         /* If the volume is VBUSY (being cloned or dumped) and this is
460          * a READ operation, then don't fail.
461          */
462         if ((*ec != VBUSY) || (locktype != READ_LOCK)) {
463            mlkReason = 2;
464            return NULL;
465         }
466         *ec = 0;
467     }
468     class = vnodeIdToClass(vnodeNumber);
469     vcp = &VnodeClassInfo[class];
470     if (locktype == WRITE_LOCK && !VolumeWriteable(vp)) {
471         *ec = VREADONLY;
472         mlkReason = 3;
473         return NULL;
474     }
475
476     if (locktype == WRITE_LOCK && programType == fileServer) {
477         VAddToVolumeUpdateList_r(ec, vp);
478         if (*ec) {
479             mlkReason = 1000 + *ec;
480             return NULL;
481         }
482     }
483
484     /* See whether the vnode is in the cache. */
485     newHash = VNODE_HASH(vp, vnodeNumber);
486     for (vnp = VnodeHashTable[newHash];
487          vnp && (vnp->vnodeNumber!=vnodeNumber || vnp->volumePtr!=vp
488                  || vnp->volumePtr->cacheCheck!=vnp->cacheCheck);
489          vnp = vnp->hashNext
490         );
491     vcp->gets++;
492     if (vnp == NULL) {
493         int     n;
494         IHandle_t *ihP = vp->vnodeIndex[class].handle;
495         FdHandle_t *fdP;
496         /* Not in cache; tentatively grab most distantly used one from the LRU
497            chain */
498         vcp->reads++;
499         vnp = VGetFreeVnode_r(vcp);
500         /* Remove it from the old hash chain */
501         moveHash(vnp, newHash);
502         /* Remove it from the LRU chain */
503         if (vnp == vcp->lruHead)
504             vcp->lruHead = vcp->lruHead->lruNext;
505         if (vnp == vcp->lruHead || vcp->lruHead == NULL)
506             Abort("VGetVnode: lru chain addled!\n");
507         vnp->lruPrev->lruNext = vnp->lruNext;
508         vnp->lruNext->lruPrev = vnp->lruPrev;
509         /* Initialize */
510         vnp->changed_newTime = vnp->changed_oldTime = 0;
511         vnp->delete = 0;
512         vnp->vnodeNumber = vnodeNumber;
513         vnp->volumePtr = vp;
514         vnp->cacheCheck = vp->cacheCheck;
515         vnp->nUsers = 1;
516
517         /* This will never block */
518         ObtainWriteLock(&vnp->lock);
519 #ifdef AFS_PTHREAD_ENV
520         vnp->writer = pthread_self();
521 #else /* AFS_PTHREAD_ENV */
522           LWP_CurrentProcess(&vnp->writer);
523 #endif /* AFS_PTHREAD_ENV */
524
525         /* Read vnode from volume index */
526         VOL_UNLOCK
527         fdP = IH_OPEN(ihP);
528         if (fdP == NULL) {
529             Log("VGetVnode: can't open index dev=%d, i=%s\n",
530                 vp->device, PrintInode(NULL,
531                                        vp->vnodeIndex[class].handle->ih_ino));
532             *ec = VIO;
533             mlkReason=9;
534         }
535         else if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, vnodeNumber),
536                          SEEK_SET) < 0) {
537             Log ("VGetVnode: can't seek on index file vn=%d\n",vnodeNumber);
538             *ec = VIO;
539             mlkReason=10;
540             FDH_REALLYCLOSE(fdP);
541         }
542         else if ((n = FDH_READ(fdP, (char*)&vnp->disk, vcp->diskSize))
543                  != vcp->diskSize) {
544             /* Don't take volume off line if the inumber is out of range
545                or the inode table is full. */
546             FDH_REALLYCLOSE(fdP);
547             VOL_LOCK
548             if(n == BAD_IGET) {
549                 Log("VGetVnode: bad inumber %s\n",
550                     PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
551                 *ec = VIO;
552                 mlkReason = 4;
553             }
554             /* Check for disk errors.  Anything else just means that the vnode
555                is not allocated */
556             if (n == -1 && errno == EIO) {
557                 Log("VGetVnode: Couldn't read vnode %d, volume %u (%s); volume needs salvage\n",  
558                     vnodeNumber, V_id(vp), V_name(vp));
559                 VForceOffline_r(vp);
560                 *ec = VSALVAGE;
561                 mlkReason = 4;
562             } else {
563                 mlkReason = 5;
564                 *ec = VIO;
565             }
566             VInvalidateVnode_r(vnp);
567             if (vnp->nUsers-- == 1)
568                 StickOnLruChain_r(vnp,vcp);
569             ReleaseWriteLock(&vnp->lock);
570             return NULL;
571         }
572         FDH_CLOSE(fdP);
573         VOL_LOCK
574         /* Quick check to see that the data is reasonable */
575         if (vnp->disk.vnodeMagic != vcp->magic || vnp->disk.type == vNull) {
576             if (vnp->disk.type == vNull) {
577                 *ec = VNOVNODE;
578                 mlkReason = 6;
579                 VInvalidateVnode_r(vnp);
580                 if (vnp->nUsers-- == 1)
581                     StickOnLruChain_r(vnp,vcp);
582                 ReleaseWriteLock(&vnp->lock);
583                 return NULL;    /* The vnode is not allocated */
584             }
585             else {
586                 struct vnodeIndex *index = &vp->vnodeIndex[class];
587                 int bitNumber = vnodeIdToBitNumber(vnodeNumber);
588                 int offset = bitNumber >> 3;
589
590                 /* Test to see if vnode number is valid. */
591                 if ((offset >= index->bitmapSize) ||
592                     ((*(index->bitmap+offset) & (1<<(bitNumber&0x7))) == 0)) {
593                     Log("VGetVnode: Request for unallocated vnode %u, volume %u (%s) denied.\n",
594                         vnodeNumber, V_id(vp), V_name(vp));
595                     mlkReason = 11;
596                     *ec = VNOVNODE;
597                 }
598                 else {
599                     Log("VGetVnode: Bad magic number, vnode %d, volume %u (%s); volume needs salvage\n",  
600                 vnodeNumber, V_id(vp), V_name(vp));
601                     vp->goingOffline = 1;       /* used to call VOffline, but that would mess
602                                                    up the volume ref count if called here */
603                     *ec = VSALVAGE;
604                     mlkReason = 7;
605                 }
606                 VInvalidateVnode_r(vnp);
607                 if (vnp->nUsers-- == 1)
608                     StickOnLruChain_r(vnp,vcp);
609                 ReleaseWriteLock(&vnp->lock);
610                 return NULL;
611             }
612         }
613         IH_INIT(vnp->handle, V_device(vp), V_parentId(vp), VN_GET_INO(vnp));
614         ReleaseWriteLock(&vnp->lock);
615     } else {
616         VNLog(101, 2, vnodeNumber, (afs_int32) vnp);
617         if (++vnp->nUsers == 1) {
618         /* First user.  Remove it from the LRU chain.  We can assume that
619            there is at least one item in the queue */
620             if (vnp == vcp->lruHead)
621                 vcp->lruHead = vcp->lruHead->lruNext;
622             if (vnp == vcp->lruHead || vcp->lruHead == NULL)
623                 Abort("VGetVnode: lru chain addled!\n");
624             vnp->lruPrev->lruNext = vnp->lruNext;
625             vnp->lruNext->lruPrev = vnp->lruPrev;
626         }
627     }
628     VOL_UNLOCK
629     if (locktype == READ_LOCK)
630         ObtainReadLock(&vnp->lock);
631     else {
632         ObtainWriteLock(&vnp->lock);
633 #ifdef AFS_PTHREAD_ENV
634         vnp->writer = pthread_self();
635 #else /* AFS_PTHREAD_ENV */
636         LWP_CurrentProcess(&vnp->writer);
637 #endif /* AFS_PTHREAD_ENV */
638     }
639     VOL_LOCK
640     /* Check that the vnode hasn't been removed while we were obtaining
641        the lock */
642     VNLog(102, 2, vnodeNumber, (afs_int32) vnp);
643     if ((vnp->disk.type == vNull) || (vnp->cacheCheck == 0)){
644         if (vnp->nUsers-- == 1)
645             StickOnLruChain_r(vnp,vcp);
646         if (locktype == READ_LOCK)
647             ReleaseReadLock(&vnp->lock);
648         else
649             ReleaseWriteLock(&vnp->lock);
650         *ec = VNOVNODE;
651         mlkReason = 8;
652         /* vnode is labelled correctly by now, so we don't have to invalidate it */
653         return NULL;
654     }
655     if (programType == fileServer)
656         VBumpVolumeUsage_r(vnp->volumePtr);/* Hack; don't know where it should be
657                                               called from.  Maybe VGetVolume */
658     return vnp;
659 }
660
661
662 int  TrustVnodeCacheEntry = 1;
663 /* This variable is bogus--when it's set to 0, the hash chains fill
664    up with multiple versions of the same vnode.  Should fix this!! */
665 void
666 VPutVnode(ec,vnp)
667     Error *ec;
668     register Vnode *vnp;
669 {
670     VOL_LOCK
671     VPutVnode_r(ec,vnp);
672     VOL_UNLOCK
673 }
674
675 void
676 VPutVnode_r(ec,vnp)
677     Error *ec;
678     register Vnode *vnp;
679 {
680     int writeLocked, offset;
681     VnodeClass class;
682     struct VnodeClassInfo *vcp;
683     int code;
684
685     *ec = 0;
686     assert (vnp->nUsers != 0);
687     class = vnodeIdToClass(vnp->vnodeNumber);
688     vcp = &VnodeClassInfo[class];
689     assert(vnp->disk.vnodeMagic == vcp->magic);
690     VNLog(200, 2, vnp->vnodeNumber, (afs_int32) vnp);
691
692     writeLocked = WriteLocked(&vnp->lock);
693     if (writeLocked) {
694 #ifdef AFS_PTHREAD_ENV
695         pthread_t thisProcess = pthread_self();
696 #else /* AFS_PTHREAD_ENV */
697         PROCESS thisProcess;
698         LWP_CurrentProcess(&thisProcess);
699 #endif /* AFS_PTHREAD_ENV */
700         VNLog(201, 2, (afs_int32) vnp,
701               ((vnp->changed_newTime) << 1) | ((vnp->changed_oldTime) << 1) | vnp->delete);
702         if (thisProcess != vnp->writer)
703             Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",vnp);
704         if (vnp->changed_oldTime || vnp->changed_newTime || vnp->delete) {
705             Volume *vp = vnp->volumePtr;
706             afs_int32 now = FT_ApproxTime();
707             assert(vnp->cacheCheck == vp->cacheCheck);
708
709             if (vnp->delete) {
710                 /* No longer any directory entries for this vnode. Free the Vnode */
711                 memset(&vnp->disk, 0, sizeof (vnp->disk));
712                 mlkLastDelete = vnp->vnodeNumber;
713                 /* delete flag turned off further down */
714                 VNLog(202, 2, vnp->vnodeNumber, (afs_int32) vnp);
715             } else if (vnp->changed_newTime) {
716                 vnp->disk.serverModifyTime = now;
717             }
718             if (vnp->changed_newTime)
719                 V_updateDate(vp) = vp->updateTime = now;
720
721             /* The vnode has been changed. Write it out to disk */
722             if (!V_inUse(vp)) {
723                 assert(V_needsSalvaged(vp));
724                 *ec = VSALVAGE;
725             } else {
726                 IHandle_t *ihP = vp->vnodeIndex[class].handle;
727                 FdHandle_t *fdP;
728                 VOL_UNLOCK
729                 fdP = IH_OPEN(ihP);
730                 if (fdP == NULL)
731                     Abort("VPutVnode: can't open index file!\n");
732                 offset = vnodeIndexOffset(vcp, vnp->vnodeNumber);
733                 if (FDH_SEEK(fdP, offset, SEEK_SET) < 0) {
734                    Abort("VPutVnode: can't seek on index file! fdp=0x%x offset=%d, errno=%d\n",
735                          fdP, offset, errno);
736                 }
737                 code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
738                 if (code != vcp->diskSize) {
739                     /* Don't force volume offline if the inumber is out of
740                      * range or the inode table is full.
741                      */
742                     VOL_LOCK
743                     if (code == BAD_IGET) {
744                         Log("VPutVnode: bad inumber %s\n",
745                             PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
746                         *ec = VIO;
747                     } else {
748                         Log("VPutVnode: Couldn't write vnode %d, volume %u (%s) (error %d)\n",
749                             vnp->vnodeNumber, V_id(vnp->volumePtr),
750                             V_name(vnp->volumePtr), code);
751                         VForceOffline_r(vp);
752                         *ec = VSALVAGE;
753                     }
754                     VOL_UNLOCK
755                     FDH_REALLYCLOSE(fdP);
756                 } else {
757                     FDH_CLOSE(fdP);
758                 }
759                 VOL_LOCK
760
761                 /* If the vnode is to be deleted, and we wrote the vnode out,
762                  * free its bitmap entry. Do after the vnode is written so we
763                  * don't allocate from bitmap before the vnode is written
764                  * (doing so could cause a "addled bitmap" message).
765                  */
766                 if (vnp->delete && !*ec) {
767                     VFreeBitMapEntry_r(ec, &vp->vnodeIndex[class],
768                                        vnodeIdToBitNumber(vnp->vnodeNumber));
769                 }
770             }
771             vcp->writes++;
772             vnp->changed_newTime = vnp->changed_oldTime = 0;
773         }
774     } else { /* Not write locked */
775         if (vnp->changed_newTime || vnp->changed_oldTime || vnp->delete)
776             Abort("VPutVnode: Change or delete flag for vnode 0x%x is set but vnode is not write locked!\n", vnp);
777     }
778
779     /* Do not look at disk portion of vnode after this point; it may
780        have been deleted above */
781     if (vnp->nUsers-- == 1)
782         StickOnLruChain_r(vnp,vcp);
783     vnp->delete = 0;
784
785     if (writeLocked)
786         ReleaseWriteLock(&vnp->lock);
787     else
788         ReleaseReadLock(&vnp->lock);
789 }
790
791 /*
792  * Make an attempt to convert a vnode lock from write to read.
793  * Do nothing if the vnode isn't write locked or the vnode has
794  * been deleted.
795  */
796 int VVnodeWriteToRead(ec,vnp)
797     Error *ec;
798     register Vnode *vnp;
799 {
800     int retVal;
801     VOL_LOCK
802     retVal = VVnodeWriteToRead_r(ec, vnp);
803     VOL_UNLOCK
804     return retVal;
805 }
806
807 int VVnodeWriteToRead_r(ec,vnp)
808     Error *ec;
809     register Vnode *vnp;
810 {
811     int writeLocked;
812     VnodeClass class;
813     struct VnodeClassInfo *vcp;
814     int code;
815 #ifdef AFS_PTHREAD_ENV
816     pthread_t thisProcess;
817 #else /* AFS_PTHREAD_ENV */
818     PROCESS thisProcess;
819 #endif /* AFS_PTHREAD_ENV */
820
821     *ec = 0;
822     assert (vnp->nUsers != 0);
823     class = vnodeIdToClass(vnp->vnodeNumber);
824     vcp = &VnodeClassInfo[class];
825     assert(vnp->disk.vnodeMagic == vcp->magic);
826     writeLocked = WriteLocked(&vnp->lock);
827     VNLog(300, 2, vnp->vnodeNumber, (afs_int32) vnp);
828
829     if (!writeLocked) {
830         return 0;
831     }
832
833 #ifdef AFS_PTHREAD_ENV
834     thisProcess = pthread_self();
835 #else /* AFS_PTHREAD_ENV */
836     LWP_CurrentProcess(&thisProcess);
837 #endif /* AFS_PTHREAD_ENV */
838
839     VNLog(301, 2, (afs_int32) vnp,
840           ((vnp->changed_newTime) << 1) | ((vnp->changed_oldTime) << 1) |
841           vnp->delete);
842     if (thisProcess != vnp->writer)
843         Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",vnp);
844     if (vnp->delete) {
845         return 0;
846     }
847     if (vnp->changed_oldTime || vnp->changed_newTime) {
848         Volume *vp = vnp->volumePtr;
849         afs_int32 now = FT_ApproxTime();
850         assert(vnp->cacheCheck == vp->cacheCheck);
851         if (vnp->changed_newTime)
852             vnp->disk.serverModifyTime = now;
853         if (vnp->changed_newTime)
854             V_updateDate(vp) = vp->updateTime = now;
855
856         /* The inode has been changed.  Write it out to disk */
857         if (!V_inUse(vp)) {
858             assert(V_needsSalvaged(vp));
859             *ec = VSALVAGE;
860         } else {
861             IHandle_t *ihP = vp->vnodeIndex[class].handle;
862             FdHandle_t *fdP;
863             off_t off = vnodeIndexOffset(vcp, vnp->vnodeNumber);
864             VOL_UNLOCK
865             fdP = IH_OPEN(ihP);
866             if (fdP == NULL)
867                 Abort("VPutVnode: can't open index file!\n");
868             code = FDH_SEEK(fdP, off, SEEK_SET);
869             if (code < 0)
870                 Abort("VPutVnode: can't seek on index file!\n");
871             code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
872             if (code != vcp->diskSize) {
873                 /*
874                  * Don't force volume offline if the inumber is out of
875                  * range or the inode table is full.
876                  */
877                 VOL_LOCK
878                 if(code == BAD_IGET)
879                 {
880                             Log("VPutVnode: bad inumber %d\n",
881                                     vp->vnodeIndex[class].handle->ih_ino);
882                             *ec = VIO;
883                 } else {
884                     Log("VPutVnode: Couldn't write vnode %d, volume %u (%s)\n",
885                         vnp->vnodeNumber, V_id(vnp->volumePtr),
886                         V_name(vnp->volumePtr));
887                         VForceOffline_r(vp);
888                     *ec = VSALVAGE;
889                 }
890                 VOL_UNLOCK
891             }
892             FDH_CLOSE(fdP);
893             VOL_LOCK
894         }
895         vcp->writes++;
896         vnp->changed_newTime = vnp->changed_oldTime = 0;
897     }
898
899     ConvertWriteToReadLock(&vnp->lock);
900     return 0;
901 }
902
903 /* Move the vnode, vnp, to the new hash table given by the
904    hash table index, newHash */
905 static int moveHash(vnp, newHash)
906     register Vnode *vnp;
907     bit32 newHash;
908 {
909     Vnode *tvnp;
910  /* Remove it from the old hash chain */
911     tvnp = VnodeHashTable[vnp->hashIndex];
912     if (tvnp == vnp)
913         VnodeHashTable[vnp->hashIndex] = vnp->hashNext;
914     else {
915         while (tvnp && tvnp->hashNext != vnp)
916             tvnp = tvnp->hashNext;
917         if (tvnp)
918             tvnp->hashNext = vnp->hashNext;
919     }
920  /* Add it to the new hash chain */
921     vnp->hashNext = VnodeHashTable[newHash];
922     VnodeHashTable[newHash] = vnp;
923     vnp->hashIndex = newHash;
924     return 0;
925 }
926
927 void
928 StickOnLruChain_r(vnp,vcp)
929     register Vnode *vnp;
930     register struct VnodeClassInfo *vcp;
931 {
932  /* Add it to the circular LRU list */
933     if (vcp->lruHead == NULL)
934         Abort("VPutVnode: vcp->lruHead==NULL");
935     else {
936         vnp->lruNext = vcp->lruHead;
937         vnp->lruPrev = vcp->lruHead->lruPrev;
938         vcp->lruHead->lruPrev = vnp;
939         vnp->lruPrev->lruNext = vnp;
940         vcp->lruHead = vnp;
941     }
942  /* If the vnode was just deleted, put it at the end of the chain so it
943     will be reused immediately */
944     if (vnp->delete)
945         vcp->lruHead = vnp->lruNext;
946  /* If caching is turned off, set volumeptr to NULL to invalidate the
947     entry */
948     if (!TrustVnodeCacheEntry)
949         vnp->volumePtr = NULL;
950 }
951
952 /* VCloseVnodeFiles - called when a volume is going off line. All open
953  * files for vnodes in that volume are closed. This might be excessive,
954  * since we may only be taking one volume of a volume group offline.
955  */
956 void VCloseVnodeFiles_r(Volume *vp)
957 {
958     int i;
959     Vnode *vnp;
960
961     for (i=0; i<VNODE_HASH_TABLE_SIZE; i++) {
962         for (vnp = VnodeHashTable[i]; vnp; vnp = vnp->hashNext) {
963             if (vnp->volumePtr == vp) {
964                 IH_REALLYCLOSE(vnp->handle);
965             }
966         }
967     }
968 }
969
970 /* VReleaseVnodeFiles - called when a volume is going detached. All open
971  * files for vnodes in that volume are closed and all inode handles
972  * for vnodes in that volume are released.
973  */
974 void VReleaseVnodeFiles_r(Volume *vp)
975 {
976     int i;
977     Vnode *vnp;
978
979     for (i=0; i<VNODE_HASH_TABLE_SIZE; i++) {
980         for (vnp = VnodeHashTable[i]; vnp; vnp = vnp->hashNext) {
981             if (vnp->volumePtr == vp) {
982                 IH_RELEASE(vnp->handle);
983             }
984         }
985     }
986 }