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