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