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