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