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