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