vclosevnodefiles-ihandle-leak-20090216
[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) 2005-2008 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 "rx/rx_queue.h"
36 #include <afs/afsint.h>
37 #include "nfs.h"
38 #include <afs/errors.h>
39 #include "lock.h"
40 #include "lwp.h"
41 #include <afs/afssyscalls.h>
42 #include "ihandle.h"
43 #include "vnode.h"
44 #include "volume.h"
45 #include "volume_inline.h"
46 #include "vnode_inline.h"
47 #include "partition.h"
48 #include "salvsync.h"
49 #if defined(AFS_SGI_ENV)
50 #include "sys/types.h"
51 #include "fcntl.h"
52 #undef min
53 #undef max
54 #include "stdlib.h"
55 #endif
56 #ifdef AFS_NT40_ENV
57 #include <fcntl.h>
58 #include "ntops.h"
59 #else
60 #include <sys/file.h>
61 #ifdef  AFS_SUN5_ENV
62 #include <sys/fcntl.h>
63 #endif
64 #include <unistd.h>
65 #endif /* AFS_NT40_ENV */
66 #include <sys/stat.h>
67
68 /*@printflike@*/ extern void Log(const char *format, ...);
69
70 /*@printflike@*/ extern void Abort(const char *format, ...);
71
72
73 struct VnodeClassInfo VnodeClassInfo[nVNODECLASSES];
74
75 private void StickOnLruChain_r(register Vnode * vnp,
76                                register struct VnodeClassInfo *vcp);
77
78 extern int LogLevel;
79
80
81
82
83 #define BAD_IGET        -1000
84
85 /* There are two separate vnode queue types defined here:
86  * Each hash conflict chain -- is singly linked, with a single head
87  * pointer. New entries are added at the beginning. Old
88  * entries are removed by linear search, which generally
89  * only occurs after a disk read).
90  * LRU chain -- is doubly linked, single head pointer.
91  * Entries are added at the head, reclaimed from the tail,
92  * or removed from anywhere in the queue.
93  */
94
95
96 /* Vnode hash table.  Find hash chain by taking lower bits of
97  * (volume_hash_offset + vnode).
98  * This distributes the root inodes of the volumes over the
99  * hash table entries and also distributes the vnodes of
100  * volumes reasonably fairly.  The volume_hash_offset field
101  * for each volume is established as the volume comes on line
102  * by using the VOLUME_HASH_OFFSET macro.  This distributes the
103  * volumes fairly among the cache entries, both when servicing
104  * a small number of volumes and when servicing a large number.
105  */
106
107 /* logging stuff for finding bugs */
108 #define THELOGSIZE      5120
109 static afs_int32 theLog[THELOGSIZE];
110 static afs_int32 vnLogPtr = 0;
111 void
112 VNLog(afs_int32 aop, afs_int32 anparms, afs_int32 av1, afs_int32 av2, 
113       afs_int32 av3, afs_int32 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 /**
170  * add a vnode to the volume's vnode list.
171  *
172  * @param[in] vp   volume object pointer
173  * @param[in] vnp  vnode object pointer
174  *
175  * @note for DAFS, it may seem like we should be acquiring a lightweight ref
176  *       on vp, but this would actually break things.  Right now, this is ok
177  *       because we destroy all vnode cache contents during during volume
178  *       detach.
179  *
180  * @pre VOL_LOCK held
181  *
182  * @internal volume package internal use only
183  */
184 void
185 AddToVVnList(Volume * vp, Vnode * vnp)
186 {
187     if (queue_IsOnQueue(vnp))
188         return;
189
190     Vn_volume(vnp) = vp;
191     Vn_cacheCheck(vnp) = vp->cacheCheck;
192     queue_Append(&vp->vnode_list, vnp);
193     Vn_stateFlags(vnp) |= VN_ON_VVN;
194 }
195
196 /**
197  * delete a vnode from the volume's vnode list.
198  *
199  * @pre VOL_LOCK held
200  *
201  * @internal volume package internal use only
202  */
203 void
204 DeleteFromVVnList(register Vnode * vnp)
205 {
206     Vn_volume(vnp) = NULL;
207
208     if (!queue_IsOnQueue(vnp))
209         return;
210
211     queue_Remove(vnp);
212     Vn_stateFlags(vnp) &= ~(VN_ON_VVN);
213 }
214
215 /**
216  * add a vnode to the end of the lru.
217  *
218  * @param[in] vcp  vnode class info object pointer
219  * @param[in] vnp  vnode object pointer
220  *
221  * @internal vnode package internal use only
222  */
223 void
224 AddToVnLRU(struct VnodeClassInfo * vcp, Vnode * vnp)
225 {
226     if (Vn_stateFlags(vnp) & VN_ON_LRU) {
227         return;
228     }
229
230     /* Add it to the circular LRU list */
231     if (vcp->lruHead == NULL)
232         Abort("VPutVnode: vcp->lruHead==NULL");
233     else {
234         vnp->lruNext = vcp->lruHead;
235         vnp->lruPrev = vcp->lruHead->lruPrev;
236         vcp->lruHead->lruPrev = vnp;
237         vnp->lruPrev->lruNext = vnp;
238         vcp->lruHead = vnp;
239     }
240
241     /* If the vnode was just deleted, put it at the end of the chain so it
242      * will be reused immediately */
243     if (vnp->delete)
244         vcp->lruHead = vnp->lruNext;
245
246     Vn_stateFlags(vnp) |= VN_ON_LRU;
247 }
248
249 /**
250  * delete a vnode from the lru.
251  *
252  * @param[in] vcp  vnode class info object pointer
253  * @param[in] vnp  vnode object pointer
254  *
255  * @internal vnode package internal use only
256  */
257 void
258 DeleteFromVnLRU(struct VnodeClassInfo * vcp, Vnode * vnp)
259 {
260     if (!(Vn_stateFlags(vnp) & VN_ON_LRU)) {
261         return;
262     }
263
264     if (vnp == vcp->lruHead)
265         vcp->lruHead = vcp->lruHead->lruNext;
266
267     if ((vnp == vcp->lruHead) || 
268         (vcp->lruHead == NULL))
269         Abort("DeleteFromVnLRU: lru chain addled!\n");
270
271     vnp->lruPrev->lruNext = vnp->lruNext;
272     vnp->lruNext->lruPrev = vnp->lruPrev;
273
274     Vn_stateFlags(vnp) &= ~(VN_ON_LRU);
275 }
276
277 /**
278  * add a vnode to the vnode hash table.
279  *
280  * @param[in] vnp  vnode object pointer
281  *
282  * @pre VOL_LOCK held
283  *
284  * @post vnode on hash
285  *
286  * @internal vnode package internal use only
287  */
288 void
289 AddToVnHash(Vnode * vnp)
290 {
291     unsigned int newHash;
292
293     if (!(Vn_stateFlags(vnp) & VN_ON_HASH)) {
294         newHash = VNODE_HASH(Vn_volume(vnp), Vn_id(vnp));
295         vnp->hashNext = VnodeHashTable[newHash];
296         VnodeHashTable[newHash] = vnp;
297         vnp->hashIndex = newHash;
298
299         Vn_stateFlags(vnp) |= VN_ON_HASH;
300     }
301 }
302
303 /**
304  * delete a vnode from the vnode hash table.
305  *
306  * @param[in] vnp
307  * @param[in] hash
308  *
309  * @pre VOL_LOCK held
310  *
311  * @post vnode removed from hash
312  *
313  * @internal vnode package internal use only
314  */
315 void
316 DeleteFromVnHash(Vnode * vnp)
317 {
318     Vnode * tvnp;
319
320     if (Vn_stateFlags(vnp) & VN_ON_HASH) {
321         tvnp = VnodeHashTable[vnp->hashIndex];
322         if (tvnp == vnp)
323             VnodeHashTable[vnp->hashIndex] = vnp->hashNext;
324         else {
325             while (tvnp && tvnp->hashNext != vnp)
326                 tvnp = tvnp->hashNext;
327             if (tvnp)
328                 tvnp->hashNext = vnp->hashNext;
329         }
330
331         vnp->hashNext = NULL;
332         vnp->hashIndex = 0;
333         Vn_stateFlags(vnp) &= ~(VN_ON_HASH);
334     }
335 }
336
337
338 /**
339  * invalidate a vnode cache entry.
340  *
341  * @param[in] avnode   vnode object pointer
342  *
343  * @pre VOL_LOCK held
344  *
345  * @post vnode metadata invalidated.
346  *       vnode removed from hash table.
347  *       DAFS: vnode state set to VN_STATE_INVALID.
348  *
349  * @internal vnode package internal use only
350  */
351 void
352 VInvalidateVnode_r(register struct Vnode *avnode)
353 {
354     avnode->changed_newTime = 0;        /* don't let it get flushed out again */
355     avnode->changed_oldTime = 0;
356     avnode->delete = 0;         /* it isn't deleted, really */
357     avnode->cacheCheck = 0;     /* invalid: prevents future vnode searches from working */
358     DeleteFromVnHash(avnode);
359 #ifdef AFS_DEMAND_ATTACH_FS
360     VnChangeState_r(avnode, VN_STATE_INVALID);
361 #endif
362 }
363
364
365 /**
366  * initialize vnode cache for a given vnode class.
367  *
368  * @param[in] class    vnode class
369  * @param[in] nVnodes  size of cache
370  *
371  * @post vnode cache allocated and initialized
372  *
373  * @internal volume package internal use only
374  *
375  * @note generally called by VInitVolumePackage_r
376  *
377  * @see VInitVolumePackage_r
378  */
379 int
380 VInitVnodes(VnodeClass class, int nVnodes)
381 {
382     byte *va;
383     register struct VnodeClassInfo *vcp = &VnodeClassInfo[class];
384
385     vcp->allocs = vcp->gets = vcp->reads = vcp->writes = 0;
386     vcp->cacheSize = nVnodes;
387     switch (class) {
388     case vSmall:
389         assert(CHECKSIZE_SMALLVNODE);
390         vcp->lruHead = NULL;
391         vcp->residentSize = SIZEOF_SMALLVNODE;
392         vcp->diskSize = SIZEOF_SMALLDISKVNODE;
393         vcp->magic = SMALLVNODEMAGIC;
394         break;
395     case vLarge:
396         vcp->lruHead = NULL;
397         vcp->residentSize = SIZEOF_LARGEVNODE;
398         vcp->diskSize = SIZEOF_LARGEDISKVNODE;
399         vcp->magic = LARGEVNODEMAGIC;
400         break;
401     }
402     {
403         int s = vcp->diskSize - 1;
404         int n = 0;
405         while (s)
406             s >>= 1, n++;
407         vcp->logSize = n;
408     }
409
410     if (nVnodes == 0)
411         return 0;
412
413     va = (byte *) calloc(nVnodes, vcp->residentSize);
414     assert(va != NULL);
415     while (nVnodes--) {
416         Vnode *vnp = (Vnode *) va;
417         Vn_refcount(vnp) = 0;   /* no context switches */
418         Vn_stateFlags(vnp) |= VN_ON_LRU;
419 #ifdef AFS_DEMAND_ATTACH_FS
420         assert(pthread_cond_init(&Vn_stateCV(vnp), NULL) == 0);
421         Vn_state(vnp) = VN_STATE_INVALID;
422         Vn_readers(vnp) = 0;
423 #else /* !AFS_DEMAND_ATTACH_FS */
424         Lock_Init(&vnp->lock);
425 #endif /* !AFS_DEMAND_ATTACH_FS */
426         vnp->changed_oldTime = 0;
427         vnp->changed_newTime = 0;
428         Vn_volume(vnp) = NULL;
429         Vn_cacheCheck(vnp) = 0;
430         vnp->delete = Vn_id(vnp) = 0;
431 #ifdef AFS_PTHREAD_ENV
432         vnp->writer = (pthread_t) 0;
433 #else /* AFS_PTHREAD_ENV */
434         vnp->writer = (PROCESS) 0;
435 #endif /* AFS_PTHREAD_ENV */
436         vnp->hashIndex = 0;
437         vnp->handle = NULL;
438         Vn_class(vnp) = vcp;
439         if (vcp->lruHead == NULL)
440             vcp->lruHead = vnp->lruNext = vnp->lruPrev = vnp;
441         else {
442             vnp->lruNext = vcp->lruHead;
443             vnp->lruPrev = vcp->lruHead->lruPrev;
444             vcp->lruHead->lruPrev = vnp;
445             vnp->lruPrev->lruNext = vnp;
446             vcp->lruHead = vnp;
447         }
448         va += vcp->residentSize;
449     }
450     return 0;
451 }
452
453
454 /**
455  * allocate an unused vnode from the lru chain.
456  *
457  * @param[in] vcp  vnode class info object pointer
458  *
459  * @pre VOL_LOCK is held
460  *
461  * @post vnode object is removed from lru, and vnode hash table.
462  *       vnode is disassociated from volume object.
463  *       state is set to VN_STATE_INVALID.
464  *       inode handle is released.
465  *
466  * @note we traverse backwards along the lru circlist.  It shouldn't 
467  *       be necessary to specify that nUsers == 0 since if it is in the list, 
468  *       nUsers should be 0.  Things shouldn't be in lruq unless no one is 
469  *       using them.
470  *
471  * @warning DAFS: VOL_LOCK is dropped while doing inode handle release
472  *
473  * @return vnode object pointer
474  */
475 Vnode *
476 VGetFreeVnode_r(struct VnodeClassInfo * vcp)
477 {
478     register Vnode *vnp;
479
480     vnp = vcp->lruHead->lruPrev;
481 #ifdef AFS_DEMAND_ATTACH_FS
482     if (Vn_refcount(vnp) != 0 || VnIsExclusiveState(Vn_state(vnp)) ||
483         Vn_readers(vnp) != 0)
484         Abort("VGetFreeVnode_r: in-use vnode in lruq");
485 #else
486     if (Vn_refcount(vnp) != 0 || CheckLock(&vnp->lock))
487         Abort("VGetFreeVnode_r: locked vnode in lruq");
488 #endif
489     VNLog(1, 2, Vn_id(vnp), (afs_int32) vnp, 0, 0);
490
491     /* 
492      * it's going to be overwritten soon enough.
493      * remove from LRU, delete hash entry, and 
494      * disassociate from old parent volume before
495      * we have a chance to drop the vol glock
496      */
497     DeleteFromVnLRU(vcp, vnp);
498     DeleteFromVnHash(vnp);
499     if (Vn_volume(vnp)) {
500         DeleteFromVVnList(vnp);
501     }
502
503     /* drop the file descriptor */
504     if (vnp->handle) {
505 #ifdef AFS_DEMAND_ATTACH_FS
506         VnChangeState_r(vnp, VN_STATE_RELEASING);
507         VOL_UNLOCK;
508 #endif
509         /* release is, potentially, a highly latent operation due to a couple
510          * factors:
511          *   - ihandle package lock contention
512          *   - closing file descriptor(s) associated with ih
513          *
514          * Hance, we perform outside of the volume package lock in order to 
515          * reduce the probability of contention.
516          */
517         IH_RELEASE(vnp->handle);
518 #ifdef AFS_DEMAND_ATTACH_FS
519         VOL_LOCK;
520 #endif
521     }
522
523 #ifdef AFS_DEMAND_ATTACH_FS
524     VnChangeState_r(vnp, VN_STATE_INVALID);
525 #endif
526
527     return vnp;
528 }
529
530
531 /**
532  * lookup a vnode in the vnode cache hash table.
533  *
534  * @param[in] vp       pointer to volume object
535  * @param[in] vnodeId  vnode id
536  *
537  * @pre VOL_LOCK held
538  *
539  * @post matching vnode object or NULL is returned
540  *
541  * @return vnode object pointer
542  *   @retval NULL   no matching vnode object was found in the cache
543  *
544  * @internal vnode package internal use only
545  *
546  * @note this symbol is exported strictly for fssync debug protocol use
547  */
548 Vnode *
549 VLookupVnode(Volume * vp, VnodeId vnodeId)
550 {
551     Vnode * vnp;
552     unsigned int newHash;
553
554     newHash = VNODE_HASH(vp, vnodeId);
555     for (vnp = VnodeHashTable[newHash];
556          (vnp && 
557           ((Vn_id(vnp) != vnodeId) || 
558            (Vn_volume(vnp) != vp) ||
559            (vp->cacheCheck != Vn_cacheCheck(vnp))));
560          vnp = vnp->hashNext);
561
562     return vnp;
563 }
564
565
566 Vnode *
567 VAllocVnode(Error * ec, Volume * vp, VnodeType type)
568 {
569     Vnode *retVal;
570     VOL_LOCK;
571     retVal = VAllocVnode_r(ec, vp, type);
572     VOL_UNLOCK;
573     return retVal;
574 }
575
576 /**
577  * allocate a new vnode.
578  *
579  * @param[out] ec    error code return
580  * @param[in]  vp    volume object pointer
581  * @param[in]  type  desired vnode type
582  *
583  * @return vnode object pointer
584  *
585  * @pre VOL_LOCK held;
586  *      heavyweight ref held on vp
587  *
588  * @post vnode allocated and returned
589  */
590 Vnode *
591 VAllocVnode_r(Error * ec, Volume * vp, VnodeType type)
592 {
593     register Vnode *vnp;
594     VnodeId vnodeNumber;
595     int bitNumber, code;
596     register struct VnodeClassInfo *vcp;
597     VnodeClass class;
598     Unique unique;
599 #ifdef AFS_DEMAND_ATTACH_FS
600     VolState vol_state_save;
601 #endif
602
603     *ec = 0;
604
605 #ifdef AFS_DEMAND_ATTACH_FS
606     /*
607      * once a volume has entered an error state, don't permit
608      * further operations to proceed
609      *  -- tkeiser 11/21/2007
610      */
611     VWaitExclusiveState_r(vp);
612     if (VIsErrorState(V_attachState(vp))) {
613         /* XXX is VSALVAGING acceptable here? */
614         *ec = DAFS_VSALVAGE;
615         return NULL;
616     }
617 #endif
618
619     if (programType == fileServer && !V_inUse(vp)) {
620         if (vp->specialStatus) {
621             *ec = vp->specialStatus;
622         } else {
623             *ec = VOFFLINE;
624         }
625         return NULL;
626     }
627     class = vnodeTypeToClass(type);
628     vcp = &VnodeClassInfo[class];
629
630     if (!VolumeWriteable(vp)) {
631         *ec = (bit32) VREADONLY;
632         return NULL;
633     }
634
635     unique = vp->nextVnodeUnique++;
636     if (!unique)
637         unique = vp->nextVnodeUnique++;
638
639     if (vp->nextVnodeUnique > V_uniquifier(vp)) {
640         VUpdateVolume_r(ec, vp, 0);
641         if (*ec)
642             return NULL;
643     }
644
645     if (programType == fileServer) {
646         VAddToVolumeUpdateList_r(ec, vp);
647         if (*ec)
648             return NULL;
649     }
650
651     /* Find a slot in the bit map */
652     bitNumber = VAllocBitmapEntry_r(ec, vp, &vp->vnodeIndex[class],
653                                     VOL_ALLOC_BITMAP_WAIT);
654     if (*ec)
655         return NULL;
656     vnodeNumber = bitNumberToVnodeNumber(bitNumber, class);
657
658     /*
659      * DAFS:
660      * at this point we should be assured that V_attachState(vp) is non-exclusive
661      */
662
663  vnrehash:
664     VNLog(2, 1, vnodeNumber, 0, 0, 0);
665     /* Prepare to move it to the new hash chain */
666     vnp = VLookupVnode(vp, vnodeNumber);
667     if (vnp) {
668         /* slot already exists.  May even not be in lruq (consider store file locking a file being deleted)
669          * so we may have to wait for it below */
670         VNLog(3, 2, vnodeNumber, (afs_int32) vnp, 0, 0);
671
672         VnCreateReservation_r(vnp);
673         if (Vn_refcount(vnp) == 1) {
674             /* we're the only user */
675             /* This won't block */
676             VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, WILL_NOT_DEADLOCK);
677         } else {
678             /* other users present; follow locking hierarchy */
679             VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, MIGHT_DEADLOCK);
680
681 #ifdef AFS_DEMAND_ATTACH_FS
682             /*
683              * DAFS:
684              * vnode was cached, wait for any existing exclusive ops to finish.
685              * once we have reacquired the lock, re-verify volume state.
686              *
687              * note: any vnode error state is related to the old vnode; disregard.
688              */
689             VnWaitQuiescent_r(vnp);
690             if (VIsErrorState(V_attachState(vp))) {
691                 VnUnlock(vnp, WRITE_LOCK);
692                 VnCancelReservation_r(vnp);
693                 *ec = DAFS_VSALVAGE;
694                 return NULL;
695             }
696 #endif
697
698             /*
699              * verify state of the world hasn't changed
700              *
701              * (technically, this should never happen because cachecheck
702              *  is only updated during a volume attach, which should not
703              *  happen when refs are held)
704              */
705             if (Vn_volume(vnp)->cacheCheck != Vn_cacheCheck(vnp)) {
706                 VnUnlock(vnp, WRITE_LOCK);
707                 VnCancelReservation_r(vnp);
708                 goto vnrehash;
709             }
710         }
711
712     } else {
713         /* no such vnode in the cache */
714
715         vnp = VGetFreeVnode_r(vcp);
716
717         /* Initialize the header fields so noone allocates another
718          * vnode with the same number */
719         Vn_id(vnp) = vnodeNumber;
720         VnCreateReservation_r(vnp);
721         AddToVVnList(vp, vnp);
722 #ifdef AFS_DEMAND_ATTACH_FS
723         AddToVnHash(vnp);
724 #endif
725
726         /* This will never block (guaranteed by check in VGetFreeVnode_r() */
727         VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, WILL_NOT_DEADLOCK);
728
729 #ifdef AFS_DEMAND_ATTACH_FS
730         VnChangeState_r(vnp, VN_STATE_ALLOC);
731 #endif
732
733         /* Sanity check:  is this vnode really not in use? */
734         {
735             int size;
736             IHandle_t *ihP = vp->vnodeIndex[class].handle;
737             FdHandle_t *fdP;
738             off_t off = vnodeIndexOffset(vcp, vnodeNumber);
739
740             /* XXX we have a potential race here if two threads
741              * allocate new vnodes at the same time, and they
742              * both decide it's time to extend the index
743              * file size...
744              */
745 #ifdef AFS_DEMAND_ATTACH_FS
746             /*
747              * this race has been eliminated for the DAFS case
748              * using exclusive state VOL_STATE_VNODE_ALLOC
749              *
750              * if this becomes a bottleneck, there are ways to
751              * improve parallelism for this code path
752              *   -- tkeiser 11/28/2007 
753              */
754             VCreateReservation_r(vp);
755             VWaitExclusiveState_r(vp);
756             vol_state_save = VChangeState_r(vp, VOL_STATE_VNODE_ALLOC);
757 #endif
758
759             VOL_UNLOCK;
760             fdP = IH_OPEN(ihP);
761             if (fdP == NULL) {
762                 Log("VAllocVnode: can't open index file!\n");
763                 goto error_encountered;
764             }
765             if ((size = FDH_SIZE(fdP)) < 0) {
766                 Log("VAllocVnode: can't stat index file!\n");
767                 goto error_encountered;
768             }
769             if (FDH_SEEK(fdP, off, SEEK_SET) < 0) {
770                 Log("VAllocVnode: can't seek on index file!\n");
771                 goto error_encountered;
772             }
773             if (off + vcp->diskSize <= size) {
774                 if (FDH_READ(fdP, &vnp->disk, vcp->diskSize) != vcp->diskSize) {
775                     Log("VAllocVnode: can't read index file!\n");
776                     goto error_encountered;
777                 }
778                 if (vnp->disk.type != vNull) {
779                     Log("VAllocVnode:  addled bitmap or index!\n");
780                     goto error_encountered;
781                 }
782             } else {
783                 /* growing file - grow in a reasonable increment */
784                 char *buf = (char *)malloc(16 * 1024);
785                 if (!buf)
786                     Abort("VAllocVnode: malloc failed\n");
787                 memset(buf, 0, 16 * 1024);
788                 (void)FDH_WRITE(fdP, buf, 16 * 1024);
789                 free(buf);
790             }
791             FDH_CLOSE(fdP);
792             VOL_LOCK;
793
794 #ifdef AFS_DEMAND_ATTACH_FS
795             VChangeState_r(vp, vol_state_save);
796             VCancelReservation_r(vp);
797 #endif
798             goto sane;
799
800
801         error_encountered:
802 #ifdef AFS_DEMAND_ATTACH_FS
803             /* 
804              * close the file handle
805              * acquire VOL_LOCK
806              * invalidate the vnode
807              * free up the bitmap entry (although salvager should take care of it)
808              * salvage the volume
809              * drop vnode lock and refs
810              */
811             if (fdP)
812                 FDH_CLOSE(fdP);
813             VOL_LOCK;
814             VFreeBitMapEntry_r(ec, &vp->vnodeIndex[class], bitNumber);
815             VInvalidateVnode_r(vnp);
816             VnUnlock(vnp, WRITE_LOCK);
817             VnCancelReservation_r(vnp);
818             VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0);
819             VCancelReservation_r(vp);
820             return NULL;
821 #else
822             assert(1 == 2);
823 #endif
824
825         }
826     sane:
827         VNLog(4, 2, vnodeNumber, (afs_int32) vnp, 0, 0);
828 #ifndef AFS_DEMAND_ATTACH_FS
829         AddToVnHash(vnp);
830 #endif
831     }
832
833     VNLog(5, 1, (afs_int32) vnp, 0, 0, 0);
834     memset(&vnp->disk, 0, sizeof(vnp->disk));
835     vnp->changed_newTime = 0;   /* set this bit when vnode is updated */
836     vnp->changed_oldTime = 0;   /* set this on CopyOnWrite. */
837     vnp->delete = 0;
838     vnp->disk.vnodeMagic = vcp->magic;
839     vnp->disk.type = type;
840     vnp->disk.uniquifier = unique;
841     vnp->handle = NULL;
842     vcp->allocs++;
843     vp->header->diskstuff.filecount++;
844 #ifdef AFS_DEMAND_ATTACH_FS
845     VnChangeState_r(vnp, VN_STATE_EXCLUSIVE);
846 #endif
847     return vnp;
848 }
849
850 /**
851  * load a vnode from disk.
852  *
853  * @param[out] ec     client error code return
854  * @param[in]  vp     volume object pointer
855  * @param[in]  vnp    vnode object pointer
856  * @param[in]  vcp    vnode class info object pointer
857  * @param[in]  class  vnode class enumeration
858  *
859  * @pre vnode is registered in appropriate data structures;
860  *      caller holds a ref on vnode; VOL_LOCK is held
861  *
862  * @post vnode data is loaded from disk.
863  *       vnode state is set to VN_STATE_ONLINE.
864  *       on failure, vnode is invalidated.
865  *
866  * @internal vnode package internal use only
867  */
868 static void
869 VnLoad(Error * ec, Volume * vp, Vnode * vnp, 
870        struct VnodeClassInfo * vcp, VnodeClass class)
871 {
872     /* vnode not cached */
873     Error error;
874     int n, dosalv = 1;
875     IHandle_t *ihP = vp->vnodeIndex[class].handle;
876     FdHandle_t *fdP;
877
878     *ec = 0;
879     vcp->reads++;
880
881 #ifdef AFS_DEMAND_ATTACH_FS
882     VnChangeState_r(vnp, VN_STATE_LOAD);
883 #endif
884
885     /* This will never block */
886     VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, WILL_NOT_DEADLOCK);
887
888     VOL_UNLOCK;
889     fdP = IH_OPEN(ihP);
890     if (fdP == NULL) {
891         Log("VnLoad: can't open index dev=%u, i=%s\n", vp->device,
892             PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
893         *ec = VIO;
894         goto error_encountered_nolock;
895     } else if (FDH_SEEK(fdP, vnodeIndexOffset(vcp, Vn_id(vnp)), SEEK_SET)
896                < 0) {
897         Log("VnLoad: can't seek on index file vn=%u\n", Vn_id(vnp));
898         *ec = VIO;
899         goto error_encountered_nolock;
900     } else if ((n = FDH_READ(fdP, (char *)&vnp->disk, vcp->diskSize))
901                != vcp->diskSize) {
902         /* Don't take volume off line if the inumber is out of range
903          * or the inode table is full. */
904         if (n == BAD_IGET) {
905             Log("VnLoad: bad inumber %s\n",
906                 PrintInode(NULL, vp->vnodeIndex[class].handle->ih_ino));
907             *ec = VIO;
908             dosalv = 0;
909         } else if (n == -1 && errno == EIO) {
910             /* disk error; salvage */
911             Log("VnLoad: Couldn't read vnode %u, volume %u (%s); volume needs salvage\n", Vn_id(vnp), V_id(vp), V_name(vp));
912         } else {
913             /* vnode is not allocated */
914             if (LogLevel >= 5) 
915                 Log("VnLoad: Couldn't read vnode %u, volume %u (%s); read %d bytes, errno %d\n", 
916                     Vn_id(vnp), V_id(vp), V_name(vp), n, errno);
917             *ec = VIO;
918             dosalv = 0;
919         }
920         goto error_encountered_nolock;
921     }
922     FDH_CLOSE(fdP);
923     VOL_LOCK;
924
925     /* Quick check to see that the data is reasonable */
926     if (vnp->disk.vnodeMagic != vcp->magic || vnp->disk.type == vNull) {
927         if (vnp->disk.type == vNull) {
928             *ec = VNOVNODE;
929             dosalv = 0;
930         } else {
931             struct vnodeIndex *index = &vp->vnodeIndex[class];
932             unsigned int bitNumber = vnodeIdToBitNumber(Vn_id(vnp));
933             unsigned int offset = bitNumber >> 3;
934
935             /* Test to see if vnode number is valid. */
936             if ((offset >= index->bitmapSize)
937                 || ((*(index->bitmap + offset) & (1 << (bitNumber & 0x7)))
938                     == 0)) {
939                 Log("VnLoad: Request for unallocated vnode %u, volume %u (%s) denied.\n", Vn_id(vnp), V_id(vp), V_name(vp));
940                 *ec = VNOVNODE;
941                 dosalv = 0;
942             } else {
943                 Log("VnLoad: Bad magic number, vnode %u, volume %u (%s); volume needs salvage\n", Vn_id(vnp), V_id(vp), V_name(vp));
944             }
945         }
946         goto error_encountered;
947     }
948
949     IH_INIT(vnp->handle, V_device(vp), V_parentId(vp), VN_GET_INO(vnp));
950     VnUnlock(vnp, WRITE_LOCK);
951 #ifdef AFS_DEMAND_ATTACH_FS
952     VnChangeState_r(vnp, VN_STATE_ONLINE);
953 #endif
954     return;
955
956
957  error_encountered_nolock:
958     if (fdP) {
959         FDH_REALLYCLOSE(fdP);
960     }
961     VOL_LOCK;
962
963  error_encountered:
964     if (dosalv) {
965 #ifdef AFS_DEMAND_ATTACH_FS
966         VRequestSalvage_r(&error, vp, SALVSYNC_ERROR, 0);
967 #else
968         VForceOffline_r(vp, 0);
969         error = VSALVAGE;
970 #endif
971         if (!*ec)
972             *ec = error;
973     }
974
975     VInvalidateVnode_r(vnp);
976     VnUnlock(vnp, WRITE_LOCK);
977 }
978
979 /**
980  * store a vnode to disk.
981  *
982  * @param[out] ec     error code output
983  * @param[in]  vp     volume object pointer
984  * @param[in]  vnp    vnode object pointer
985  * @param[in]  vcp    vnode class info object pointer
986  * @param[in]  class  vnode class enumeration
987  *
988  * @pre VOL_LOCK held.
989  *      caller holds refs to volume and vnode.
990  *      DAFS: caller is responsible for performing state sanity checks.
991  *
992  * @post vnode state is stored to disk.
993  *
994  * @internal vnode package internal use only
995  */
996 static void
997 VnStore(Error * ec, Volume * vp, Vnode * vnp, 
998         struct VnodeClassInfo * vcp, VnodeClass class)
999 {
1000     int offset, code;
1001     IHandle_t *ihP = vp->vnodeIndex[class].handle;
1002     FdHandle_t *fdP;
1003 #ifdef AFS_DEMAND_ATTACH_FS
1004     VnState vn_state_save;
1005 #endif
1006
1007     *ec = 0;
1008
1009 #ifdef AFS_DEMAND_ATTACH_FS
1010     vn_state_save = VnChangeState_r(vnp, VN_STATE_STORE);
1011 #endif
1012
1013     offset = vnodeIndexOffset(vcp, Vn_id(vnp));
1014     VOL_UNLOCK;
1015     fdP = IH_OPEN(ihP);
1016     if (fdP == NULL) {
1017         Log("VnStore: can't open index file!\n");
1018         goto error_encountered;
1019     }
1020     if (FDH_SEEK(fdP, offset, SEEK_SET) < 0) {
1021         Log("VnStore: can't seek on index file! fdp=0x%x offset=%d, errno=%d\n",
1022             fdP, offset, errno);
1023         goto error_encountered;
1024     }
1025
1026     code = FDH_WRITE(fdP, &vnp->disk, vcp->diskSize);
1027     if (code != vcp->diskSize) {
1028         /* Don't force volume offline if the inumber is out of
1029          * range or the inode table is full.
1030          */
1031         FDH_REALLYCLOSE(fdP);
1032         if (code == BAD_IGET) {
1033             Log("VnStore: bad inumber %s\n",
1034                 PrintInode(NULL,
1035                            vp->vnodeIndex[class].handle->ih_ino));
1036             *ec = VIO;
1037             VOL_LOCK;
1038 #ifdef AFS_DEMAND_ATTACH_FS
1039             VnChangeState_r(vnp, VN_STATE_ERROR);
1040 #endif
1041         } else {
1042             Log("VnStore: Couldn't write vnode %u, volume %u (%s) (error %d)\n", Vn_id(vnp), V_id(Vn_volume(vnp)), V_name(Vn_volume(vnp)), code);
1043 #ifdef AFS_DEMAND_ATTACH_FS
1044             goto error_encountered;
1045 #else
1046             VOL_LOCK;
1047             VForceOffline_r(vp, 0);
1048             *ec = VSALVAGE;
1049 #endif
1050         }
1051         return;
1052     } else {
1053         FDH_CLOSE(fdP);
1054     }
1055
1056     VOL_LOCK;
1057 #ifdef AFS_DEMAND_ATTACH_FS
1058     VnChangeState_r(vnp, vn_state_save);
1059 #endif
1060     return;
1061     
1062  error_encountered:
1063 #ifdef AFS_DEMAND_ATTACH_FS
1064     /* XXX instead of dumping core, let's try to request a salvage
1065      * and just fail the putvnode */
1066     if (fdP)
1067         FDH_CLOSE(fdP);
1068     VOL_LOCK;
1069     VnChangeState_r(vnp, VN_STATE_ERROR);
1070     VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0);
1071 #else
1072     assert(1 == 2);
1073 #endif
1074 }
1075
1076 /**
1077  * get a handle to a vnode object.
1078  *
1079  * @param[out] ec           error code
1080  * @param[in]  vp           volume object
1081  * @param[in]  vnodeNumber  vnode id
1082  * @param[in]  locktype     type of lock to acquire
1083  *
1084  * @return vnode object pointer
1085  *
1086  * @see VGetVnode_r
1087  */
1088 Vnode *
1089 VGetVnode(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
1090 {                               /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
1091     Vnode *retVal;
1092     VOL_LOCK;
1093     retVal = VGetVnode_r(ec, vp, vnodeNumber, locktype);
1094     VOL_UNLOCK;
1095     return retVal;
1096 }
1097
1098 /**
1099  * get a handle to a vnode object.
1100  *
1101  * @param[out] ec           error code
1102  * @param[in]  vp           volume object
1103  * @param[in]  vnodeNumber  vnode id
1104  * @param[in]  locktype     type of lock to acquire
1105  *
1106  * @return vnode object pointer
1107  *
1108  * @internal vnode package internal use only
1109  *
1110  * @pre VOL_LOCK held.
1111  *      heavyweight ref held on volume object.
1112  */
1113 Vnode *
1114 VGetVnode_r(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
1115 {                               /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
1116     register Vnode *vnp;
1117     int code;
1118     VnodeClass class;
1119     struct VnodeClassInfo *vcp;
1120     Volume * oldvp = NULL;
1121
1122     *ec = 0;
1123
1124     if (vnodeNumber == 0) {
1125         *ec = VNOVNODE;
1126         return NULL;
1127     }
1128
1129     VNLog(100, 1, vnodeNumber, 0, 0, 0);
1130
1131 #ifdef AFS_DEMAND_ATTACH_FS
1132     /*
1133      * once a volume has entered an error state, don't permit
1134      * further operations to proceed
1135      *  -- tkeiser 11/21/2007
1136      */
1137     VWaitExclusiveState_r(vp);
1138     if (VIsErrorState(V_attachState(vp))) {
1139         /* XXX is VSALVAGING acceptable here? */
1140         *ec = VSALVAGING;
1141         return NULL;
1142     }
1143 #endif
1144
1145     if (programType == fileServer && !V_inUse(vp)) {
1146         *ec = (vp->specialStatus ? vp->specialStatus : VOFFLINE);
1147
1148         /* If the volume is VBUSY (being cloned or dumped) and this is
1149          * a READ operation, then don't fail.
1150          */
1151         if ((*ec != VBUSY) || (locktype != READ_LOCK)) {
1152             return NULL;
1153         }
1154         *ec = 0;
1155     }
1156     class = vnodeIdToClass(vnodeNumber);
1157     vcp = &VnodeClassInfo[class];
1158     if (locktype == WRITE_LOCK && !VolumeWriteable(vp)) {
1159         *ec = (bit32) VREADONLY;
1160         return NULL;
1161     }
1162
1163     if (locktype == WRITE_LOCK && programType == fileServer) {
1164         VAddToVolumeUpdateList_r(ec, vp);
1165         if (*ec) {
1166             return NULL;
1167         }
1168     }
1169
1170     vcp->gets++;
1171
1172     /* See whether the vnode is in the cache. */
1173     vnp = VLookupVnode(vp, vnodeNumber);
1174     if (vnp) {
1175         /* vnode is in cache */
1176
1177         VNLog(101, 2, vnodeNumber, (afs_int32) vnp, 0, 0);
1178         VnCreateReservation_r(vnp);
1179
1180 #ifdef AFS_DEMAND_ATTACH_FS
1181         /*
1182          * this is the one DAFS case where we may run into contention.
1183          * here's the basic control flow:
1184          *
1185          * if locktype is READ_LOCK:
1186          *   wait until vnode is not exclusive
1187          *   set to VN_STATE_READ
1188          *   increment read count
1189          *   done
1190          * else
1191          *   wait until vnode is quiescent
1192          *   set to VN_STATE_EXCLUSIVE
1193          *   done
1194          */
1195         if (locktype == READ_LOCK) {
1196             VnWaitExclusiveState_r(vnp);
1197         } else {
1198             VnWaitQuiescent_r(vnp);
1199         }
1200
1201         if (VnIsErrorState(Vn_state(vnp))) {
1202             VnCancelReservation_r(vnp);
1203             *ec = VSALVAGE;
1204             return NULL;
1205         }
1206 #endif /* AFS_DEMAND_ATTACH_FS */
1207     } else {
1208         /* vnode not cached */
1209
1210         /* Not in cache; tentatively grab most distantly used one from the LRU
1211          * chain */
1212         vcp->reads++;
1213         vnp = VGetFreeVnode_r(vcp);
1214
1215         /* Initialize */
1216         vnp->changed_newTime = vnp->changed_oldTime = 0;
1217         vnp->delete = 0;
1218         Vn_id(vnp) = vnodeNumber;
1219         VnCreateReservation_r(vnp);
1220         AddToVVnList(vp, vnp);
1221 #ifdef AFS_DEMAND_ATTACH_FS
1222         AddToVnHash(vnp);
1223 #endif
1224
1225         /*
1226          * XXX for non-DAFS, there is a serious
1227          * race condition here:
1228          *
1229          * two threads can race to load a vnode.  the net
1230          * result is two struct Vnodes can be allocated
1231          * and hashed, which point to the same underlying
1232          * disk data store.  conflicting vnode locks can
1233          * thus be held concurrently.
1234          *
1235          * for non-DAFS to be safe, VOL_LOCK really shouldn't
1236          * be dropped in VnLoad.  Of course, this would likely
1237          * lead to an unacceptable slow-down.
1238          */
1239
1240         VnLoad(ec, vp, vnp, vcp, class);
1241         if (*ec) {
1242             VnCancelReservation_r(vnp);
1243             return NULL;
1244         }
1245 #ifndef AFS_DEMAND_ATTACH_FS
1246         AddToVnHash(vnp);
1247 #endif
1248         /*
1249          * DAFS:
1250          * there is no possibility for contention. we "own" this vnode.
1251          */
1252     }
1253
1254     /*
1255      * DAFS:
1256      * it is imperative that nothing drop vol lock between here
1257      * and the VnBeginRead/VnChangeState stanza below
1258      */
1259
1260     VnLock(vnp, locktype, VOL_LOCK_HELD, MIGHT_DEADLOCK);
1261
1262     /* Check that the vnode hasn't been removed while we were obtaining
1263      * the lock */
1264     VNLog(102, 2, vnodeNumber, (afs_int32) vnp, 0, 0);
1265     if ((vnp->disk.type == vNull) || (Vn_cacheCheck(vnp) == 0)) {
1266         VnUnlock(vnp, locktype);
1267         VnCancelReservation_r(vnp);
1268         *ec = VNOVNODE;
1269         /* vnode is labelled correctly by now, so we don't have to invalidate it */
1270         return NULL;
1271     }
1272
1273 #ifdef AFS_DEMAND_ATTACH_FS
1274     if (locktype == READ_LOCK) {
1275         VnBeginRead_r(vnp);
1276     } else {
1277         VnChangeState_r(vnp, VN_STATE_EXCLUSIVE);
1278     }
1279 #endif
1280
1281     if (programType == fileServer)
1282         VBumpVolumeUsage_r(Vn_volume(vnp));     /* Hack; don't know where it should be
1283                                                  * called from.  Maybe VGetVolume */
1284     return vnp;
1285 }
1286
1287
1288 int TrustVnodeCacheEntry = 1;
1289 /* This variable is bogus--when it's set to 0, the hash chains fill
1290    up with multiple versions of the same vnode.  Should fix this!! */
1291 void
1292 VPutVnode(Error * ec, register Vnode * vnp)
1293 {
1294     VOL_LOCK;
1295     VPutVnode_r(ec, vnp);
1296     VOL_UNLOCK;
1297 }
1298
1299 /**
1300  * put back a handle to a vnode object.
1301  *
1302  * @param[out] ec   client error code
1303  * @param[in]  vnp  vnode object pointer
1304  *
1305  * @pre VOL_LOCK held.
1306  *      ref held on vnode.
1307  *
1308  * @post ref dropped on vnode.
1309  *       if vnode was modified or deleted, it is written out to disk
1310  *       (assuming a write lock was held).
1311  *
1312  * @internal volume package internal use only
1313  */
1314 void
1315 VPutVnode_r(Error * ec, register Vnode * vnp)
1316 {
1317     int writeLocked;
1318     VnodeClass class;
1319     struct VnodeClassInfo *vcp;
1320     int code;
1321
1322     *ec = 0;
1323     assert(Vn_refcount(vnp) != 0);
1324     class = vnodeIdToClass(Vn_id(vnp));
1325     vcp = &VnodeClassInfo[class];
1326     assert(vnp->disk.vnodeMagic == vcp->magic);
1327     VNLog(200, 2, Vn_id(vnp), (afs_int32) vnp, 0, 0);
1328
1329 #ifdef AFS_DEMAND_ATTACH_FS
1330     writeLocked = (Vn_state(vnp) == VN_STATE_EXCLUSIVE);
1331 #else
1332     writeLocked = WriteLocked(&vnp->lock);
1333 #endif
1334
1335     if (writeLocked) {
1336         /* sanity checks */
1337 #ifdef AFS_PTHREAD_ENV
1338         pthread_t thisProcess = pthread_self();
1339 #else /* AFS_PTHREAD_ENV */
1340         PROCESS thisProcess;
1341         LWP_CurrentProcess(&thisProcess);
1342 #endif /* AFS_PTHREAD_ENV */
1343         VNLog(201, 2, (afs_int32) vnp,
1344               ((vnp->changed_newTime) << 1) | ((vnp->
1345                                                 changed_oldTime) << 1) | vnp->
1346               delete, 0, 0);
1347         if (thisProcess != vnp->writer)
1348             Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",
1349                   vnp);
1350
1351
1352         if (vnp->changed_oldTime || vnp->changed_newTime || vnp->delete) {
1353             Volume *vp = Vn_volume(vnp);
1354             afs_uint32 now = FT_ApproxTime();
1355             assert(Vn_cacheCheck(vnp) == vp->cacheCheck);
1356
1357             if (vnp->delete) {
1358                 /* No longer any directory entries for this vnode. Free the Vnode */
1359                 memset(&vnp->disk, 0, sizeof(vnp->disk));
1360                 /* delete flag turned off further down */
1361                 VNLog(202, 2, Vn_id(vnp), (afs_int32) vnp, 0, 0);
1362             } else if (vnp->changed_newTime) {
1363                 vnp->disk.serverModifyTime = now;
1364             }
1365             if (vnp->changed_newTime)
1366             {
1367                 V_updateDate(vp) = vp->updateTime = now;
1368                 if(V_volUpCounter(vp)<MAXINT)
1369                         V_volUpCounter(vp)++;
1370             }
1371
1372             /* The vnode has been changed. Write it out to disk */
1373             if (!V_inUse(vp)) {
1374 #ifdef AFS_DEMAND_ATTACH_FS
1375                 VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0);
1376 #else
1377                 assert(V_needsSalvaged(vp));
1378                 *ec = VSALVAGE;
1379 #endif
1380             } else {
1381                 VnStore(ec, vp, vnp, vcp, class);
1382
1383                 /* If the vnode is to be deleted, and we wrote the vnode out,
1384                  * free its bitmap entry. Do after the vnode is written so we
1385                  * don't allocate from bitmap before the vnode is written
1386                  * (doing so could cause a "addled bitmap" message).
1387                  */
1388                 if (vnp->delete && !*ec) {
1389                     if (Vn_volume(vnp)->header->diskstuff.filecount-- < 1)
1390                         Vn_volume(vnp)->header->diskstuff.filecount = 0;
1391                     VFreeBitMapEntry_r(ec, &vp->vnodeIndex[class],
1392                                        vnodeIdToBitNumber(Vn_id(vnp)));
1393                 }
1394             }
1395             vcp->writes++;
1396             vnp->changed_newTime = vnp->changed_oldTime = 0;
1397         }
1398 #ifdef AFS_DEMAND_ATTACH_FS
1399         VnChangeState_r(vnp, VN_STATE_ONLINE);
1400 #endif
1401     } else {                    /* Not write locked */
1402         if (vnp->changed_newTime || vnp->changed_oldTime || vnp->delete)
1403             Abort
1404                 ("VPutVnode: Change or delete flag for vnode 0x%x is set but vnode is not write locked!\n",
1405                  vnp);
1406 #ifdef AFS_DEMAND_ATTACH_FS
1407         VnEndRead_r(vnp);
1408 #endif
1409     }
1410
1411     /* Do not look at disk portion of vnode after this point; it may
1412      * have been deleted above */
1413     vnp->delete = 0;
1414     VnUnlock(vnp, ((writeLocked) ? WRITE_LOCK : READ_LOCK));
1415     VnCancelReservation_r(vnp);
1416 }
1417
1418 /*
1419  * Make an attempt to convert a vnode lock from write to read.
1420  * Do nothing if the vnode isn't write locked or the vnode has
1421  * been deleted.
1422  */
1423 int
1424 VVnodeWriteToRead(Error * ec, register Vnode * vnp)
1425 {
1426     int retVal;
1427     VOL_LOCK;
1428     retVal = VVnodeWriteToRead_r(ec, vnp);
1429     VOL_UNLOCK;
1430     return retVal;
1431 }
1432
1433 /**
1434  * convert vnode handle from mutually exclusive to shared access.
1435  *
1436  * @param[out] ec   client error code
1437  * @param[in]  vnp  vnode object pointer
1438  *
1439  * @return unspecified use (see out argument 'ec' for error code return)
1440  *
1441  * @pre VOL_LOCK held.
1442  *      ref held on vnode.
1443  *      write lock held on vnode.
1444  *
1445  * @post read lock held on vnode.
1446  *       if vnode was modified, it has been written to disk.
1447  *
1448  * @internal volume package internal use only
1449  */
1450 int
1451 VVnodeWriteToRead_r(Error * ec, register Vnode * vnp)
1452 {
1453     int writeLocked;
1454     VnodeClass class;
1455     struct VnodeClassInfo *vcp;
1456     int code;
1457 #ifdef AFS_PTHREAD_ENV
1458     pthread_t thisProcess;
1459 #else /* AFS_PTHREAD_ENV */
1460     PROCESS thisProcess;
1461 #endif /* AFS_PTHREAD_ENV */
1462
1463     *ec = 0;
1464     assert(Vn_refcount(vnp) != 0);
1465     class = vnodeIdToClass(Vn_id(vnp));
1466     vcp = &VnodeClassInfo[class];
1467     assert(vnp->disk.vnodeMagic == vcp->magic);
1468     VNLog(300, 2, Vn_id(vnp), (afs_int32) vnp, 0, 0);
1469
1470 #ifdef AFS_DEMAND_ATTACH_FS
1471     writeLocked = (Vn_state(vnp) == VN_STATE_EXCLUSIVE);
1472 #else
1473     writeLocked = WriteLocked(&vnp->lock);
1474 #endif
1475     if (!writeLocked) {
1476         return 0;
1477     }
1478
1479
1480     VNLog(301, 2, (afs_int32) vnp,
1481           ((vnp->changed_newTime) << 1) | ((vnp->
1482                                             changed_oldTime) << 1) | vnp->
1483           delete, 0, 0);
1484
1485     /* sanity checks */
1486 #ifdef AFS_PTHREAD_ENV
1487     thisProcess = pthread_self();
1488 #else /* AFS_PTHREAD_ENV */
1489     LWP_CurrentProcess(&thisProcess);
1490 #endif /* AFS_PTHREAD_ENV */
1491     if (thisProcess != vnp->writer)
1492         Abort("VPutVnode: Vnode at 0x%x locked by another process!\n",
1493               (int)vnp);
1494
1495     if (vnp->delete) {
1496         return 0;
1497     }
1498     if (vnp->changed_oldTime || vnp->changed_newTime) {
1499         Volume *vp = Vn_volume(vnp);
1500         afs_uint32 now = FT_ApproxTime();
1501         assert(Vn_cacheCheck(vnp) == vp->cacheCheck);
1502         if (vnp->changed_newTime)
1503             vnp->disk.serverModifyTime = now;
1504         if (vnp->changed_newTime)
1505             V_updateDate(vp) = vp->updateTime = now;
1506
1507         /* The inode has been changed.  Write it out to disk */
1508         if (!V_inUse(vp)) {
1509 #ifdef AFS_DEMAND_ATTACH_FS
1510             VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0);
1511 #else
1512             assert(V_needsSalvaged(vp));
1513             *ec = VSALVAGE;
1514 #endif
1515         } else {
1516             VnStore(ec, vp, vnp, vcp, class);
1517         }
1518     sane:
1519         vcp->writes++;
1520         vnp->changed_newTime = vnp->changed_oldTime = 0;
1521     }
1522
1523     vnp->writer = 0;
1524 #ifdef AFS_DEMAND_ATTACH_FS
1525     VnChangeState_r(vnp, VN_STATE_ONLINE);
1526     VnBeginRead_r(vnp);
1527 #else
1528     ConvertWriteToReadLock(&vnp->lock);
1529 #endif
1530     return 0;
1531 }
1532
1533 /** 
1534  * initial size of ihandle pointer vector.
1535  *
1536  * @see VInvalidateVnodesByVolume_r
1537  */
1538 #define IH_VEC_BASE_SIZE 256
1539
1540 /**
1541  * increment amount for growing ihandle pointer vector.
1542  *
1543  * @see VInvalidateVnodesByVolume_r
1544  */
1545 #define IH_VEC_INCREMENT 256
1546
1547 /**
1548  * Compile list of ihandles to be released/reallyclosed at a later time.
1549  *
1550  * @param[in]   vp            volume object pointer
1551  * @param[out]  vec_out       vector of ihandle pointers to be released/reallyclosed
1552  * @param[out]  vec_len_out   number of valid elements in ihandle vector
1553  *
1554  * @pre - VOL_LOCK is held
1555  *      - volume is in appropriate exclusive state (e.g. VOL_STATE_VNODE_CLOSE,
1556  *        VOL_STATE_VNODE_RELEASE)
1557  *
1558  * @post - all vnodes on VVn list are invalidated
1559  *       - ih_vec is populated with all valid ihandles
1560  *
1561  * @return operation status
1562  *    @retval 0         success
1563  *    @retval ENOMEM    out of memory
1564  *
1565  * @todo we should handle out of memory conditions more gracefully.
1566  *
1567  * @internal vnode package internal use only
1568  */
1569 static int
1570 VInvalidateVnodesByVolume_r(Volume * vp,
1571                             IHandle_t *** vec_out,
1572                             size_t * vec_len_out)
1573 {
1574     int ret = 0;
1575     Vnode *vnp, *nvnp;
1576     size_t i = 0, vec_len;
1577     IHandle_t **ih_vec, **ih_vec_new;
1578
1579 #ifdef AFS_DEMAND_ATTACH_FS
1580     VOL_UNLOCK;
1581 #endif /* AFS_DEMAND_ATTACH_FS */
1582
1583     vec_len = IH_VEC_BASE_SIZE;
1584     ih_vec = malloc(sizeof(IHandle_t *) * vec_len);
1585 #ifdef AFS_DEMAND_ATTACH_FS
1586     VOL_LOCK;
1587 #endif
1588     if (ih_vec == NULL)
1589         return ENOMEM;
1590
1591     /* 
1592      * Traverse the volume's vnode list.  Pull all the ihandles out into a 
1593      * thread-private array for later asynchronous processing.
1594      */
1595  restart_traversal:
1596     for (queue_Scan(&vp->vnode_list, vnp, nvnp, Vnode)) {
1597         if (vnp->handle != NULL) {
1598             if (i == vec_len) {
1599 #ifdef AFS_DEMAND_ATTACH_FS
1600                 VOL_UNLOCK;
1601 #endif
1602                 vec_len += IH_VEC_INCREMENT;
1603                 ih_vec_new = realloc(ih_vec, sizeof(IHandle_t *) * vec_len);
1604 #ifdef AFS_DEMAND_ATTACH_FS
1605                 VOL_LOCK;
1606 #endif
1607                 if (ih_vec_new == NULL) {
1608                     ret = ENOMEM;
1609                     goto done;
1610                 }
1611                 ih_vec = ih_vec_new;
1612 #ifdef AFS_DEMAND_ATTACH_FS
1613                 /*
1614                  * Theoretically, the volume's VVn list should not change 
1615                  * because the volume is in an exclusive state.  For the
1616                  * sake of safety, we will restart the traversal from the
1617                  * the beginning (which is not expensive because we're
1618                  * deleting the items from the list as we go).
1619                  */
1620                 goto restart_traversal;
1621 #endif
1622             }
1623             ih_vec[i++] = vnp->handle;
1624             vnp->handle = NULL;
1625         }
1626         DeleteFromVVnList(vnp);
1627         VInvalidateVnode_r(vnp);
1628     }
1629
1630  done:
1631     *vec_out = ih_vec;
1632     *vec_len_out = i;
1633
1634     return ret;
1635 }
1636
1637 /* VCloseVnodeFiles - called when a volume is going off line. All open
1638  * files for vnodes in that volume are closed. This might be excessive,
1639  * since we may only be taking one volume of a volume group offline.
1640  */
1641 void
1642 VCloseVnodeFiles_r(Volume * vp)
1643 {
1644 #ifdef AFS_DEMAND_ATTACH_FS
1645     VolState vol_state_save;
1646 #endif
1647     IHandle_t ** ih_vec;
1648     size_t i, vec_len;
1649
1650 #ifdef AFS_DEMAND_ATTACH_FS
1651     vol_state_save = VChangeState_r(vp, VOL_STATE_VNODE_CLOSE);
1652 #endif /* AFS_DEMAND_ATTACH_FS */
1653
1654     /* XXX need better error handling here */
1655     assert(VInvalidateVnodesByVolume_r(vp,
1656                                        &ih_vec,
1657                                        &vec_len) == 0);
1658
1659     /*
1660      * DAFS:
1661      * now we drop VOL_LOCK while we perform some potentially very
1662      * expensive operations in the background
1663      */
1664 #ifdef AFS_DEMAND_ATTACH_FS
1665     VOL_UNLOCK;
1666 #endif
1667
1668     for (i = 0; i < vec_len; i++) {
1669         IH_REALLYCLOSE(ih_vec[i]);
1670         IH_RELEASE(ih_vec[i]);
1671     }
1672
1673     free(ih_vec);
1674
1675 #ifdef AFS_DEMAND_ATTACH_FS
1676     VOL_LOCK;
1677     VChangeState_r(vp, vol_state_save);
1678 #endif /* AFS_DEMAND_ATTACH_FS */
1679 }
1680
1681
1682 /**
1683  * shut down all vnode cache state for a given volume.
1684  *
1685  * @param[in] vp  volume object pointer
1686  *
1687  * @pre VOL_LOCK is held
1688  *
1689  * @post all file descriptors closed.
1690  *       all inode handles released.
1691  *       all vnode cache objects disassociated from volume.
1692  *
1693  * @note for DAFS, these operations are performed outside the vol glock under
1694  *       volume exclusive state VOL_STATE_VNODE_RELEASE.  Please further note
1695  *       that it would be a bug to acquire and release a volume reservation
1696  *       during this exclusive operation.  This is due to the fact that we are
1697  *       generally called during the refcount 1->0 transition.
1698  *
1699  * @todo we should handle failures in VInvalidateVnodesByVolume_r more 
1700  *       gracefully.
1701  *
1702  * @see VInvalidateVnodesByVolume_r
1703  *
1704  * @internal this routine is internal to the volume package
1705  */
1706 void
1707 VReleaseVnodeFiles_r(Volume * vp)
1708 {
1709 #ifdef AFS_DEMAND_ATTACH_FS
1710     VolState vol_state_save;
1711 #endif
1712     IHandle_t ** ih_vec;
1713     size_t i, vec_len;
1714
1715 #ifdef AFS_DEMAND_ATTACH_FS
1716     vol_state_save = VChangeState_r(vp, VOL_STATE_VNODE_RELEASE);
1717 #endif /* AFS_DEMAND_ATTACH_FS */
1718
1719     /* XXX need better error handling here */
1720     assert(VInvalidateVnodesByVolume_r(vp,
1721                                        &ih_vec,
1722                                        &vec_len) == 0);
1723
1724     /*
1725      * DAFS:
1726      * now we drop VOL_LOCK while we perform some potentially very
1727      * expensive operations in the background
1728      */
1729 #ifdef AFS_DEMAND_ATTACH_FS
1730     VOL_UNLOCK;
1731 #endif
1732
1733     for (i = 0; i < vec_len; i++) {
1734         IH_RELEASE(ih_vec[i]);
1735     }
1736
1737     free(ih_vec);
1738
1739 #ifdef AFS_DEMAND_ATTACH_FS
1740     VOL_LOCK;
1741     VChangeState_r(vp, vol_state_save);
1742 #endif /* AFS_DEMAND_ATTACH_FS */
1743 }