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