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