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