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