9eb120d17dd1c99f1eb14e539aaf7aed7aad04fe
[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         int rollover = 0;
655
656         unique = vp->nextVnodeUnique++;
657         if (unique == 0) {
658             rollover = 1;       /* nextVnodeUnique rolled over */
659             vp->nextVnodeUnique = 2;    /* 1 is reserved for the root vnode */
660             unique = vp->nextVnodeUnique++;
661         }
662
663         if (vp->nextVnodeUnique > V_uniquifier(vp) || rollover) {
664             VUpdateVolume_r(ec, vp, 0);
665             if (*ec)
666                 return NULL;
667         }
668
669         /* Find a slot in the bit map */
670         bitNumber = VAllocBitmapEntry_r(ec, vp, &vp->vnodeIndex[class],
671                 VOL_ALLOC_BITMAP_WAIT);
672
673         if (*ec)
674             return NULL;
675         vnodeNumber = bitNumberToVnodeNumber(bitNumber, class);
676     } else {
677         index = &vp->vnodeIndex[class];
678         if (!in_unique) {
679             *ec = VNOVNODE;
680             return NULL;
681         }
682         /* Catch us up to where the master is */
683         if (in_unique > vp->nextVnodeUnique)
684             vp->nextVnodeUnique = in_unique+1;
685
686         if (vp->nextVnodeUnique > V_uniquifier(vp)) {
687             VUpdateVolume_r(ec, vp, 0);
688             if (*ec)
689                 return NULL;
690         }
691
692         unique = in_unique;
693         bitNumber = vnodeIdToBitNumber(in_vnode);
694         offset = bitNumber >> 3;
695
696         /* Mark vnode in use. Grow bitmap if needed. */
697         if ((offset >= index->bitmapSize)
698                 || ((*(index->bitmap + offset) & (1 << (bitNumber & 0x7))) == 0))
699             VGrowBitmap(index);
700         /* Should not happen */
701         if (*(index->bitmap + offset) & (1 << (bitNumber & 0x7))) {
702             *ec = VNOVNODE;
703             return NULL;
704         }
705
706         *(index->bitmap + offset) |= (1 << (bitNumber & 0x7));
707         vnodeNumber = in_vnode;
708     }
709
710     /*
711      * DAFS:
712      * at this point we should be assured that V_attachState(vp) is non-exclusive
713      */
714
715  vnrehash:
716     VNLog(2, 1, vnodeNumber, 0, 0, 0);
717     /* Prepare to move it to the new hash chain */
718     vnp = VLookupVnode(vp, vnodeNumber);
719     if (vnp) {
720         /* slot already exists.  May even not be in lruq (consider store file locking a file being deleted)
721          * so we may have to wait for it below */
722         VNLog(3, 2, vnodeNumber, (intptr_t)vnp, 0, 0);
723
724         VnCreateReservation_r(vnp);
725         if (Vn_refcount(vnp) == 1) {
726             /* we're the only user */
727             /* This won't block */
728             VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, WILL_NOT_DEADLOCK);
729         } else {
730 #ifdef AFS_DEMAND_ATTACH_FS
731             /*
732              * DAFS:
733              * vnode was cached, wait for any existing exclusive ops to finish.
734              * once we have reacquired the lock, re-verify volume state.
735              *
736              * note: any vnode error state is related to the old vnode; disregard.
737              */
738             VnWaitQuiescent_r(vnp);
739             if (VIsErrorState(V_attachState(vp))) {
740                 VnUnlock(vnp, WRITE_LOCK);
741                 VnCancelReservation_r(vnp);
742                 *ec = DAFS_VSALVAGE;
743                 return NULL;
744             }
745 #endif
746
747             /* other users present; follow locking hierarchy */
748             VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, MIGHT_DEADLOCK);
749
750             /*
751              * verify state of the world hasn't changed
752              *
753              * (technically, this should never happen because cachecheck
754              *  is only updated during a volume attach, which should not
755              *  happen when refs are held)
756              */
757             if (Vn_volume(vnp)->cacheCheck != Vn_cacheCheck(vnp)) {
758                 VnUnlock(vnp, WRITE_LOCK);
759                 VnCancelReservation_r(vnp);
760                 goto vnrehash;
761             }
762         }
763
764         /* sanity check: vnode should be blank if it was deleted. If it's
765          * not blank, it is still in use somewhere; but the bitmap told us
766          * this vnode number was free, so something is wrong. */
767         if (vnp->disk.type != vNull) {
768             Error tmp;
769             Log("VAllocVnode:  addled bitmap or vnode object! (vol %" AFS_VOLID_FMT ", "
770                 "vnode %p, number %ld, type %ld)\n", afs_printable_VolumeId_lu(vp->hashid), vnp,
771                 (long)Vn_id(vnp), (long)vnp->disk.type);
772             *ec = EIO;
773             VFreeBitMapEntry_r(&tmp, vp, &vp->vnodeIndex[class], bitNumber,
774                                VOL_FREE_BITMAP_WAIT);
775             VInvalidateVnode_r(vnp);
776             VnUnlock(vnp, WRITE_LOCK);
777             VnCancelReservation_r(vnp);
778 #ifdef AFS_DEMAND_ATTACH_FS
779             VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0);
780 #else
781             VForceOffline_r(vp, 0);
782 #endif
783             return NULL;
784         }
785
786     } else {
787         /* no such vnode in the cache */
788
789         vnp = VGetFreeVnode_r(vcp, vp, vnodeNumber);
790
791         /* This will never block (guaranteed by check in VGetFreeVnode_r() */
792         VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, WILL_NOT_DEADLOCK);
793
794 #ifdef AFS_DEMAND_ATTACH_FS
795         VnChangeState_r(vnp, VN_STATE_ALLOC);
796 #endif
797
798         /* Sanity check:  is this vnode really not in use? */
799         {
800             afs_sfsize_t size;
801             IHandle_t *ihP = vp->vnodeIndex[class].handle;
802             FdHandle_t *fdP;
803             afs_foff_t off = vnodeIndexOffset(vcp, vnodeNumber);
804             Error tmp;
805
806             /* XXX we have a potential race here if two threads
807              * allocate new vnodes at the same time, and they
808              * both decide it's time to extend the index
809              * file size...
810              */
811 #ifdef AFS_DEMAND_ATTACH_FS
812             /*
813              * this race has been eliminated for the DAFS case
814              * using exclusive state VOL_STATE_VNODE_ALLOC
815              *
816              * if this becomes a bottleneck, there are ways to
817              * improve parallelism for this code path
818              *   -- tkeiser 11/28/2007
819              */
820             VCreateReservation_r(vp);
821             VWaitExclusiveState_r(vp);
822             vol_state_save = VChangeState_r(vp, VOL_STATE_VNODE_ALLOC);
823 #endif
824
825             VOL_UNLOCK;
826             fdP = IH_OPEN(ihP);
827             if (fdP == NULL) {
828                 Log("VAllocVnode: can't open index file!\n");
829                 *ec = ENOENT;
830                 goto error_encountered;
831             }
832             if ((size = FDH_SIZE(fdP)) < 0) {
833                 Log("VAllocVnode: can't stat index file!\n");
834                 *ec = EIO;
835                 goto error_encountered;
836             }
837             if (off + vcp->diskSize <= size) {
838               if (FDH_PREAD(fdP, &vnp->disk, vcp->diskSize, off) != vcp->diskSize) {
839                     Log("VAllocVnode: can't read index file!\n");
840                     *ec = EIO;
841                     goto error_encountered;
842                 }
843                 if (vnp->disk.type != vNull) {
844                     Log("VAllocVnode:  addled bitmap or index!\n");
845                     *ec = EIO;
846                     goto error_encountered;
847                 }
848             } else {
849                 /* growing file - grow in a reasonable increment */
850                 char *buf = malloc(16 * 1024);
851                 if (!buf) {
852                     Log("VAllocVnode: can't grow vnode index: out of memory\n");
853                     *ec = ENOMEM;
854                     goto error_encountered;
855                 }
856                 memset(buf, 0, 16 * 1024);
857                 if ((FDH_PWRITE(fdP, buf, 16 * 1024, off)) != 16 * 1024) {
858                     Log("VAllocVnode: can't grow vnode index: write failed\n");
859                     *ec = EIO;
860                     free(buf);
861                     goto error_encountered;
862                 }
863                 free(buf);
864             }
865             FDH_CLOSE(fdP);
866             VOL_LOCK;
867
868 #ifdef AFS_DEMAND_ATTACH_FS
869             VChangeState_r(vp, vol_state_save);
870             VCancelReservation_r(vp);
871 #endif
872             goto sane;
873
874
875         error_encountered:
876             /*
877              * close the file handle
878              * acquire VOL_LOCK
879              * invalidate the vnode
880              * free up the bitmap entry (although salvager should take care of it)
881              * salvage the volume
882              * drop vnode lock and refs
883              */
884             if (fdP)
885                 FDH_CLOSE(fdP);
886             VOL_LOCK;
887             VFreeBitMapEntry_r(&tmp, vp, &vp->vnodeIndex[class], bitNumber, 0 /*flags*/);
888             VInvalidateVnode_r(vnp);
889             VnUnlock(vnp, WRITE_LOCK);
890             VnCancelReservation_r(vnp);
891 #ifdef AFS_DEMAND_ATTACH_FS
892             VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0);
893             VCancelReservation_r(vp);
894 #else
895             VForceOffline_r(vp, 0);
896 #endif
897             return NULL;
898         }
899     sane:
900         VNLog(4, 2, vnodeNumber, (intptr_t)vnp, 0, 0);
901 #ifndef AFS_DEMAND_ATTACH_FS
902         AddToVnHash(vnp);
903 #endif
904     }
905
906     VNLog(5, 1, (intptr_t)vnp, 0, 0, 0);
907     memset(&vnp->disk, 0, sizeof(vnp->disk));
908     vnp->changed_newTime = 0;   /* set this bit when vnode is updated */
909     vnp->changed_oldTime = 0;   /* set this on CopyOnWrite. */
910     vnp->delete = 0;
911     vnp->disk.vnodeMagic = vcp->magic;
912     vnp->disk.type = type;
913     vnp->disk.uniquifier = unique;
914     vnp->handle = NULL;
915     vcp->allocs++;
916     V_filecount(vp)++;
917 #ifdef AFS_DEMAND_ATTACH_FS
918     VnChangeState_r(vnp, VN_STATE_EXCLUSIVE);
919 #endif
920     return vnp;
921 }
922
923 /**
924  * load a vnode from disk.
925  *
926  * @param[out] ec     client error code return
927  * @param[in]  vp     volume object pointer
928  * @param[in]  vnp    vnode object pointer
929  * @param[in]  vcp    vnode class info object pointer
930  * @param[in]  class  vnode class enumeration
931  *
932  * @pre vnode is registered in appropriate data structures;
933  *      caller holds a ref on vnode; VOL_LOCK is held
934  *
935  * @post vnode data is loaded from disk.
936  *       vnode state is set to VN_STATE_ONLINE.
937  *       on failure, vnode is invalidated.
938  *
939  * @internal vnode package internal use only
940  */
941 static void
942 VnLoad(Error * ec, Volume * vp, Vnode * vnp,
943        struct VnodeClassInfo * vcp, VnodeClass class)
944 {
945     /* vnode not cached */
946     Error error;
947     int dosalv = 1;
948     ssize_t nBytes;
949     IHandle_t *ihP = vp->vnodeIndex[class].handle;
950     FdHandle_t *fdP;
951     afs_ino_str_t stmp;
952
953     *ec = 0;
954     vcp->reads++;
955
956 #ifdef AFS_DEMAND_ATTACH_FS
957     VnChangeState_r(vnp, VN_STATE_LOAD);
958 #endif
959
960     /* This will never block */
961     VnLock(vnp, WRITE_LOCK, VOL_LOCK_HELD, WILL_NOT_DEADLOCK);
962
963     VOL_UNLOCK;
964     fdP = IH_OPEN(ihP);
965     if (fdP == NULL) {
966         Log("VnLoad: can't open index dev=%u, i=%s\n", vp->device,
967             PrintInode(stmp, vp->vnodeIndex[class].handle->ih_ino));
968         *ec = VIO;
969         goto error_encountered_nolock;
970     } else if ((nBytes = FDH_PREAD(fdP, (char *)&vnp->disk, vcp->diskSize, vnodeIndexOffset(vcp, Vn_id(vnp))))
971                != vcp->diskSize) {
972         /* Don't take volume off line if the inumber is out of range
973          * or the inode table is full. */
974         if (nBytes == BAD_IGET) {
975             Log("VnLoad: bad inumber %s\n",
976                 PrintInode(stmp, vp->vnodeIndex[class].handle->ih_ino));
977             *ec = VIO;
978             dosalv = 0;
979         } else if (nBytes == -1 && errno == EIO) {
980             /* disk error; salvage */
981             Log("VnLoad: Couldn't read vnode %u, volume %" AFS_VOLID_FMT " (%s); volume needs salvage\n", Vn_id(vnp), afs_printable_VolumeId_lu(V_id(vp)), V_name(vp));
982         } else {
983             /* vnode is not allocated */
984             if (LogLevel >= 5)
985                 Log("VnLoad: Couldn't read vnode %u, volume %" AFS_VOLID_FMT " (%s); read %d bytes, errno %d\n",
986                     Vn_id(vnp), afs_printable_VolumeId_lu(V_id(vp)), V_name(vp), (int)nBytes, errno);
987             *ec = VNOVNODE;
988             dosalv = 0;
989         }
990         goto error_encountered_nolock;
991     }
992     FDH_CLOSE(fdP);
993     VOL_LOCK;
994
995     /* Quick check to see that the data is reasonable */
996     if (vnp->disk.vnodeMagic != vcp->magic || vnp->disk.type == vNull) {
997         if (vnp->disk.type == vNull) {
998             *ec = VNOVNODE;
999             dosalv = 0;
1000         } else {
1001             struct vnodeIndex *index = &vp->vnodeIndex[class];
1002             unsigned int bitNumber = vnodeIdToBitNumber(Vn_id(vnp));
1003             unsigned int offset = bitNumber >> 3;
1004
1005 #ifdef AFS_DEMAND_ATTACH_FS
1006             /* Make sure the volume bitmap isn't getting updated while we are
1007              * checking it */
1008             VWaitExclusiveState_r(vp);
1009 #endif
1010
1011             /* Test to see if vnode number is valid. */
1012             if ((offset >= index->bitmapSize)
1013                 || ((*(index->bitmap + offset) & (1 << (bitNumber & 0x7)))
1014                     == 0)) {
1015                 Log("VnLoad: Request for unallocated vnode %u, volume %" AFS_VOLID_FMT " (%s) denied.\n", Vn_id(vnp), afs_printable_VolumeId_lu(V_id(vp)), V_name(vp));
1016                 *ec = VNOVNODE;
1017                 dosalv = 0;
1018             } else {
1019                 Log("VnLoad: Bad magic number, vnode %u, volume %" AFS_VOLID_FMT " (%s); volume needs salvage\n", Vn_id(vnp), afs_printable_VolumeId_lu(V_id(vp)), V_name(vp));
1020             }
1021         }
1022         goto error_encountered;
1023     }
1024
1025     IH_INIT(vnp->handle, V_device(vp), afs_printable_VolumeId_lu(V_parentId(vp)), VN_GET_INO(vnp));
1026     VnUnlock(vnp, WRITE_LOCK);
1027 #ifdef AFS_DEMAND_ATTACH_FS
1028     VnChangeState_r(vnp, VN_STATE_ONLINE);
1029 #endif
1030     return;
1031
1032
1033  error_encountered_nolock:
1034     if (fdP) {
1035         FDH_REALLYCLOSE(fdP);
1036     }
1037     VOL_LOCK;
1038
1039  error_encountered:
1040     if (dosalv) {
1041 #ifdef AFS_DEMAND_ATTACH_FS
1042         VRequestSalvage_r(&error, vp, SALVSYNC_ERROR, 0);
1043 #else
1044         VForceOffline_r(vp, 0);
1045         error = VSALVAGE;
1046 #endif
1047         if (!*ec)
1048             *ec = error;
1049     }
1050
1051     VInvalidateVnode_r(vnp);
1052     VnUnlock(vnp, WRITE_LOCK);
1053 }
1054
1055 /**
1056  * store a vnode to disk.
1057  *
1058  * @param[out] ec     error code output
1059  * @param[in]  vp     volume object pointer
1060  * @param[in]  vnp    vnode object pointer
1061  * @param[in]  vcp    vnode class info object pointer
1062  * @param[in]  class  vnode class enumeration
1063  *
1064  * @pre VOL_LOCK held.
1065  *      caller holds refs to volume and vnode.
1066  *      DAFS: caller is responsible for performing state sanity checks.
1067  *
1068  * @post vnode state is stored to disk.
1069  *
1070  * @internal vnode package internal use only
1071  */
1072 static void
1073 VnStore(Error * ec, Volume * vp, Vnode * vnp,
1074         struct VnodeClassInfo * vcp, VnodeClass class)
1075 {
1076     ssize_t nBytes;
1077     afs_foff_t offset;
1078     IHandle_t *ihP = vp->vnodeIndex[class].handle;
1079     FdHandle_t *fdP;
1080     afs_ino_str_t stmp;
1081 #ifdef AFS_DEMAND_ATTACH_FS
1082     VnState vn_state_save;
1083 #endif
1084
1085     *ec = 0;
1086
1087 #ifdef AFS_DEMAND_ATTACH_FS
1088     vn_state_save = VnChangeState_r(vnp, VN_STATE_STORE);
1089 #endif
1090
1091     offset = vnodeIndexOffset(vcp, Vn_id(vnp));
1092     VOL_UNLOCK;
1093     fdP = IH_OPEN(ihP);
1094     if (fdP == NULL) {
1095         Log("VnStore: can't open index file!\n");
1096         goto error_encountered;
1097     }
1098     nBytes = FDH_PWRITE(fdP, &vnp->disk, vcp->diskSize, offset);
1099     if (nBytes != vcp->diskSize) {
1100         /* Don't force volume offline if the inumber is out of
1101          * range or the inode table is full.
1102          */
1103         FDH_REALLYCLOSE(fdP);
1104         if (nBytes == BAD_IGET) {
1105             Log("VnStore: bad inumber %s\n",
1106                 PrintInode(stmp,
1107                            vp->vnodeIndex[class].handle->ih_ino));
1108             *ec = VIO;
1109             VOL_LOCK;
1110 #ifdef AFS_DEMAND_ATTACH_FS
1111             VnChangeState_r(vnp, VN_STATE_ERROR);
1112 #endif
1113         } else {
1114             Log("VnStore: Couldn't write vnode %u, volume %" AFS_VOLID_FMT " (%s) (error %d)\n", Vn_id(vnp), afs_printable_VolumeId_lu(V_id(Vn_volume(vnp))), V_name(Vn_volume(vnp)), (int)nBytes);
1115 #ifdef AFS_DEMAND_ATTACH_FS
1116             goto error_encountered;
1117 #else
1118             VOL_LOCK;
1119             VForceOffline_r(vp, 0);
1120             *ec = VSALVAGE;
1121 #endif
1122         }
1123         return;
1124     } else {
1125         FDH_CLOSE(fdP);
1126     }
1127
1128     VOL_LOCK;
1129 #ifdef AFS_DEMAND_ATTACH_FS
1130     VnChangeState_r(vnp, vn_state_save);
1131 #endif
1132     return;
1133
1134  error_encountered:
1135 #ifdef AFS_DEMAND_ATTACH_FS
1136     /* XXX instead of dumping core, let's try to request a salvage
1137      * and just fail the putvnode */
1138     if (fdP)
1139         FDH_CLOSE(fdP);
1140     VOL_LOCK;
1141     VnChangeState_r(vnp, VN_STATE_ERROR);
1142     VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0);
1143 #else
1144     opr_abort();
1145 #endif
1146 }
1147
1148 /**
1149  * get a handle to a vnode object.
1150  *
1151  * @param[out] ec           error code
1152  * @param[in]  vp           volume object
1153  * @param[in]  vnodeNumber  vnode id
1154  * @param[in]  locktype     type of lock to acquire
1155  *
1156  * @return vnode object pointer
1157  *
1158  * @see VGetVnode_r
1159  */
1160 Vnode *
1161 VGetVnode(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
1162 {                               /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
1163     Vnode *retVal;
1164     VOL_LOCK;
1165     retVal = VGetVnode_r(ec, vp, vnodeNumber, locktype);
1166     VOL_UNLOCK;
1167     return retVal;
1168 }
1169
1170 /**
1171  * get a handle to a vnode object.
1172  *
1173  * @param[out] ec           error code
1174  * @param[in]  vp           volume object
1175  * @param[in]  vnodeNumber  vnode id
1176  * @param[in]  locktype     type of lock to acquire
1177  *
1178  * @return vnode object pointer
1179  *
1180  * @internal vnode package internal use only
1181  *
1182  * @pre VOL_LOCK held.
1183  *      heavyweight ref held on volume object.
1184  */
1185 Vnode *
1186 VGetVnode_r(Error * ec, Volume * vp, VnodeId vnodeNumber, int locktype)
1187 {                               /* READ_LOCK or WRITE_LOCK, as defined in lock.h */
1188     Vnode *vnp;
1189     VnodeClass class;
1190     struct VnodeClassInfo *vcp;
1191
1192     *ec = 0;
1193
1194     if (vnodeNumber == 0) {
1195         *ec = VNOVNODE;
1196         return NULL;
1197     }
1198
1199     VNLog(100, 1, vnodeNumber, 0, 0, 0);
1200
1201 #ifdef AFS_DEMAND_ATTACH_FS
1202     /*
1203      * once a volume has entered an error state, don't permit
1204      * further operations to proceed
1205      *  -- tkeiser 11/21/2007
1206      */
1207     VWaitExclusiveState_r(vp);
1208     if (VIsErrorState(V_attachState(vp))) {
1209         /* XXX is VSALVAGING acceptable here? */
1210         *ec = VSALVAGING;
1211         return NULL;
1212     }
1213 #endif
1214
1215     if (programType == fileServer && !V_inUse(vp)) {
1216         *ec = (vp->specialStatus ? vp->specialStatus : VOFFLINE);
1217
1218         /* If the volume is VBUSY (being cloned or dumped) and this is
1219          * a READ operation, then don't fail.
1220          */
1221         if ((*ec != VBUSY) || (locktype != READ_LOCK)) {
1222             return NULL;
1223         }
1224         *ec = 0;
1225     }
1226     class = vnodeIdToClass(vnodeNumber);
1227     vcp = &VnodeClassInfo[class];
1228     if (locktype == WRITE_LOCK && !VolumeWriteable(vp)) {
1229         *ec = (bit32) VREADONLY;
1230         return NULL;
1231     }
1232
1233     if (locktype == WRITE_LOCK && programType == fileServer) {
1234         VAddToVolumeUpdateList_r(ec, vp);
1235         if (*ec) {
1236             return NULL;
1237         }
1238     }
1239
1240     vcp->gets++;
1241
1242     /* See whether the vnode is in the cache. */
1243     vnp = VLookupVnode(vp, vnodeNumber);
1244     if (vnp) {
1245         /* vnode is in cache */
1246
1247         VNLog(101, 2, vnodeNumber, (intptr_t)vnp, 0, 0);
1248         VnCreateReservation_r(vnp);
1249
1250 #ifdef AFS_DEMAND_ATTACH_FS
1251         /*
1252          * this is the one DAFS case where we may run into contention.
1253          * here's the basic control flow:
1254          *
1255          * if locktype is READ_LOCK:
1256          *   wait until vnode is not exclusive
1257          *   set to VN_STATE_READ
1258          *   increment read count
1259          *   done
1260          * else
1261          *   wait until vnode is quiescent
1262          *   set to VN_STATE_EXCLUSIVE
1263          *   done
1264          */
1265         if (locktype == READ_LOCK) {
1266             VnWaitExclusiveState_r(vnp);
1267         } else {
1268             VnWaitQuiescent_r(vnp);
1269         }
1270
1271         if (VnIsErrorState(Vn_state(vnp))) {
1272             VnCancelReservation_r(vnp);
1273             *ec = VSALVAGE;
1274             return NULL;
1275         }
1276 #endif /* AFS_DEMAND_ATTACH_FS */
1277     } else {
1278         /* vnode not cached */
1279
1280         /* Not in cache; tentatively grab most distantly used one from the LRU
1281          * chain */
1282         vcp->reads++;
1283         vnp = VGetFreeVnode_r(vcp, vp, vnodeNumber);
1284
1285         /* Initialize */
1286         vnp->changed_newTime = vnp->changed_oldTime = 0;
1287         vnp->delete = 0;
1288
1289         /*
1290          * XXX for non-DAFS, there is a serious
1291          * race condition here:
1292          *
1293          * two threads can race to load a vnode.  the net
1294          * result is two struct Vnodes can be allocated
1295          * and hashed, which point to the same underlying
1296          * disk data store.  conflicting vnode locks can
1297          * thus be held concurrently.
1298          *
1299          * for non-DAFS to be safe, VOL_LOCK really shouldn't
1300          * be dropped in VnLoad.  Of course, this would likely
1301          * lead to an unacceptable slow-down.
1302          */
1303
1304         VnLoad(ec, vp, vnp, vcp, class);
1305         if (*ec) {
1306             VnCancelReservation_r(vnp);
1307             return NULL;
1308         }
1309 #ifndef AFS_DEMAND_ATTACH_FS
1310         AddToVnHash(vnp);
1311 #endif
1312         /*
1313          * DAFS:
1314          * there is no possibility for contention. we "own" this vnode.
1315          */
1316     }
1317
1318     /*
1319      * DAFS:
1320      * it is imperative that nothing drop vol lock between here
1321      * and the VnBeginRead/VnChangeState stanza below
1322      */
1323
1324     VnLock(vnp, locktype, VOL_LOCK_HELD, MIGHT_DEADLOCK);
1325
1326     /* Check that the vnode hasn't been removed while we were obtaining
1327      * the lock */
1328     VNLog(102, 2, vnodeNumber, (intptr_t) vnp, 0, 0);
1329     if ((vnp->disk.type == vNull) || (Vn_cacheCheck(vnp) == 0)) {
1330         VnUnlock(vnp, locktype);
1331         VnCancelReservation_r(vnp);
1332         *ec = VNOVNODE;
1333         /* vnode is labelled correctly by now, so we don't have to invalidate it */
1334         return NULL;
1335     }
1336
1337 #ifdef AFS_DEMAND_ATTACH_FS
1338     if (locktype == READ_LOCK) {
1339         VnBeginRead_r(vnp);
1340     } else {
1341         VnChangeState_r(vnp, VN_STATE_EXCLUSIVE);
1342     }
1343 #endif
1344
1345     if (programType == fileServer)
1346         VBumpVolumeUsage_r(Vn_volume(vnp));     /* Hack; don't know where it should be
1347                                                  * called from.  Maybe VGetVolume */
1348     return vnp;
1349 }
1350
1351
1352 int TrustVnodeCacheEntry = 1;
1353 /* This variable is bogus--when it's set to 0, the hash chains fill
1354    up with multiple versions of the same vnode.  Should fix this!! */
1355 void
1356 VPutVnode(Error * ec, Vnode * vnp)
1357 {
1358     VOL_LOCK;
1359     VPutVnode_r(ec, vnp);
1360     VOL_UNLOCK;
1361 }
1362
1363 /**
1364  * put back a handle to a vnode object.
1365  *
1366  * @param[out] ec   client error code
1367  * @param[in]  vnp  vnode object pointer
1368  *
1369  * @pre VOL_LOCK held.
1370  *      ref held on vnode.
1371  *
1372  * @post ref dropped on vnode.
1373  *       if vnode was modified or deleted, it is written out to disk
1374  *       (assuming a write lock was held).
1375  *
1376  * @internal volume package internal use only
1377  */
1378 void
1379 VPutVnode_r(Error * ec, Vnode * vnp)
1380 {
1381     int writeLocked;
1382     VnodeClass class;
1383     struct VnodeClassInfo *vcp;
1384
1385     *ec = 0;
1386     opr_Assert(Vn_refcount(vnp) != 0);
1387     class = vnodeIdToClass(Vn_id(vnp));
1388     vcp = &VnodeClassInfo[class];
1389     opr_Assert(vnp->disk.vnodeMagic == vcp->magic);
1390     VNLog(200, 2, Vn_id(vnp), (intptr_t) vnp, 0, 0);
1391
1392 #ifdef AFS_DEMAND_ATTACH_FS
1393     writeLocked = (Vn_state(vnp) == VN_STATE_EXCLUSIVE);
1394 #else
1395     writeLocked = WriteLocked(&vnp->lock);
1396 #endif
1397
1398     if (writeLocked) {
1399         /* sanity checks */
1400 #ifdef AFS_PTHREAD_ENV
1401         pthread_t thisProcess = pthread_self();
1402 #else /* AFS_PTHREAD_ENV */
1403         PROCESS thisProcess;
1404         LWP_CurrentProcess(&thisProcess);
1405 #endif /* AFS_PTHREAD_ENV */
1406         VNLog(201, 2, (intptr_t) vnp,
1407               ((vnp->changed_newTime) << 1) | ((vnp->
1408                                                 changed_oldTime) << 1) | vnp->
1409               delete, 0, 0);
1410         if (thisProcess != vnp->writer)
1411             Abort("VPutVnode: Vnode at %"AFS_PTR_FMT" locked by another process!\n",
1412                   vnp);
1413
1414
1415         if (vnp->changed_oldTime || vnp->changed_newTime || vnp->delete) {
1416             Volume *vp = Vn_volume(vnp);
1417             afs_uint32 now = FT_ApproxTime();
1418             opr_Assert(Vn_cacheCheck(vnp) == vp->cacheCheck);
1419
1420             if (vnp->delete) {
1421                 /* No longer any directory entries for this vnode. Free the Vnode */
1422                 memset(&vnp->disk, 0, sizeof(vnp->disk));
1423                 /* delete flag turned off further down */
1424                 VNLog(202, 2, Vn_id(vnp), (intptr_t) vnp, 0, 0);
1425             } else if (vnp->changed_newTime) {
1426                 vnp->disk.serverModifyTime = now;
1427             }
1428             if (vnp->changed_newTime)
1429             {
1430                 V_updateDate(vp) = vp->updateTime = now;
1431                 if(V_volUpCounter(vp)< UINT_MAX)
1432                         V_volUpCounter(vp)++;
1433             }
1434
1435             /* The vnode has been changed. Write it out to disk */
1436             if (!V_inUse(vp)) {
1437 #ifdef AFS_DEMAND_ATTACH_FS
1438                 VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0);
1439 #else
1440                 opr_Assert(V_needsSalvaged(vp));
1441                 *ec = VSALVAGE;
1442 #endif
1443             } else {
1444                 VnStore(ec, vp, vnp, vcp, class);
1445
1446                 /* If the vnode is to be deleted, and we wrote the vnode out,
1447                  * free its bitmap entry. Do after the vnode is written so we
1448                  * don't allocate from bitmap before the vnode is written
1449                  * (doing so could cause a "addled bitmap" message).
1450                  */
1451                 if (vnp->delete && !*ec) {
1452                   if (V_filecount(Vn_volume(vnp))-- < 1)
1453                       V_filecount(Vn_volume(vnp)) = 0;
1454                     VFreeBitMapEntry_r(ec, vp, &vp->vnodeIndex[class],
1455                                        vnodeIdToBitNumber(Vn_id(vnp)),
1456                                        VOL_FREE_BITMAP_WAIT);
1457                 }
1458             }
1459             vcp->writes++;
1460             vnp->changed_newTime = vnp->changed_oldTime = 0;
1461         }
1462 #ifdef AFS_DEMAND_ATTACH_FS
1463         VnChangeState_r(vnp, VN_STATE_ONLINE);
1464 #endif
1465     } else {                    /* Not write locked */
1466         if (vnp->changed_newTime || vnp->changed_oldTime || vnp->delete)
1467             Abort
1468                 ("VPutVnode: Change or delete flag for vnode "
1469                  "%"AFS_PTR_FMT" is set but vnode is not write locked!\n",
1470                  vnp);
1471 #ifdef AFS_DEMAND_ATTACH_FS
1472         VnEndRead_r(vnp);
1473 #endif
1474     }
1475
1476     /* Do not look at disk portion of vnode after this point; it may
1477      * have been deleted above */
1478     vnp->delete = 0;
1479     VnUnlock(vnp, ((writeLocked) ? WRITE_LOCK : READ_LOCK));
1480     VnCancelReservation_r(vnp);
1481 }
1482
1483 /*
1484  * Make an attempt to convert a vnode lock from write to read.
1485  * Do nothing if the vnode isn't write locked or the vnode has
1486  * been deleted.
1487  */
1488 int
1489 VVnodeWriteToRead(Error * ec, Vnode * vnp)
1490 {
1491     int retVal;
1492     VOL_LOCK;
1493     retVal = VVnodeWriteToRead_r(ec, vnp);
1494     VOL_UNLOCK;
1495     return retVal;
1496 }
1497
1498 /**
1499  * convert vnode handle from mutually exclusive to shared access.
1500  *
1501  * @param[out] ec   client error code
1502  * @param[in]  vnp  vnode object pointer
1503  *
1504  * @return unspecified use (see out argument 'ec' for error code return)
1505  *
1506  * @pre VOL_LOCK held.
1507  *      ref held on vnode.
1508  *      write lock held on vnode.
1509  *
1510  * @post read lock held on vnode.
1511  *       if vnode was modified, it has been written to disk.
1512  *
1513  * @internal volume package internal use only
1514  */
1515 int
1516 VVnodeWriteToRead_r(Error * ec, Vnode * vnp)
1517 {
1518     int writeLocked;
1519     VnodeClass class;
1520     struct VnodeClassInfo *vcp;
1521 #ifdef AFS_PTHREAD_ENV
1522     pthread_t thisProcess;
1523 #else /* AFS_PTHREAD_ENV */
1524     PROCESS thisProcess;
1525 #endif /* AFS_PTHREAD_ENV */
1526
1527     *ec = 0;
1528     opr_Assert(Vn_refcount(vnp) != 0);
1529     class = vnodeIdToClass(Vn_id(vnp));
1530     vcp = &VnodeClassInfo[class];
1531     opr_Assert(vnp->disk.vnodeMagic == vcp->magic);
1532     VNLog(300, 2, Vn_id(vnp), (intptr_t) vnp, 0, 0);
1533
1534 #ifdef AFS_DEMAND_ATTACH_FS
1535     writeLocked = (Vn_state(vnp) == VN_STATE_EXCLUSIVE);
1536 #else
1537     writeLocked = WriteLocked(&vnp->lock);
1538 #endif
1539     if (!writeLocked) {
1540         return 0;
1541     }
1542
1543
1544     VNLog(301, 2, (intptr_t) vnp,
1545           ((vnp->changed_newTime) << 1) | ((vnp->
1546                                             changed_oldTime) << 1) | vnp->
1547           delete, 0, 0);
1548
1549     /* sanity checks */
1550 #ifdef AFS_PTHREAD_ENV
1551     thisProcess = pthread_self();
1552 #else /* AFS_PTHREAD_ENV */
1553     LWP_CurrentProcess(&thisProcess);
1554 #endif /* AFS_PTHREAD_ENV */
1555     if (thisProcess != vnp->writer)
1556         Abort("VPutVnode: Vnode at %"AFS_PTR_FMT
1557               " locked by another process!\n", vnp);
1558
1559     if (vnp->delete) {
1560         return 0;
1561     }
1562     if (vnp->changed_oldTime || vnp->changed_newTime) {
1563         Volume *vp = Vn_volume(vnp);
1564         afs_uint32 now = FT_ApproxTime();
1565         opr_Assert(Vn_cacheCheck(vnp) == vp->cacheCheck);
1566         if (vnp->changed_newTime)
1567             vnp->disk.serverModifyTime = now;
1568         if (vnp->changed_newTime)
1569             V_updateDate(vp) = vp->updateTime = now;
1570
1571         /* The inode has been changed.  Write it out to disk */
1572         if (!V_inUse(vp)) {
1573 #ifdef AFS_DEMAND_ATTACH_FS
1574             VRequestSalvage_r(ec, vp, SALVSYNC_ERROR, 0);
1575 #else
1576             opr_Assert(V_needsSalvaged(vp));
1577             *ec = VSALVAGE;
1578 #endif
1579         } else {
1580             VnStore(ec, vp, vnp, vcp, class);
1581         }
1582         vcp->writes++;
1583         vnp->changed_newTime = vnp->changed_oldTime = 0;
1584     }
1585
1586     vnp->writer = 0;
1587 #ifdef AFS_DEMAND_ATTACH_FS
1588     VnChangeState_r(vnp, VN_STATE_ONLINE);
1589     VnBeginRead_r(vnp);
1590 #else
1591     ConvertWriteToReadLock(&vnp->lock);
1592 #endif
1593     return 0;
1594 }
1595
1596 /**
1597  * initial size of ihandle pointer vector.
1598  *
1599  * @see VInvalidateVnodesByVolume_r
1600  */
1601 #define IH_VEC_BASE_SIZE 256
1602
1603 /**
1604  * increment amount for growing ihandle pointer vector.
1605  *
1606  * @see VInvalidateVnodesByVolume_r
1607  */
1608 #define IH_VEC_INCREMENT 256
1609
1610 /**
1611  * Compile list of ihandles to be released/reallyclosed at a later time.
1612  *
1613  * @param[in]   vp            volume object pointer
1614  * @param[out]  vec_out       vector of ihandle pointers to be released/reallyclosed
1615  * @param[out]  vec_len_out   number of valid elements in ihandle vector
1616  *
1617  * @pre - VOL_LOCK is held
1618  *      - volume is in appropriate exclusive state (e.g. VOL_STATE_VNODE_CLOSE,
1619  *        VOL_STATE_VNODE_RELEASE)
1620  *
1621  * @post - all vnodes on VVn list are invalidated
1622  *       - ih_vec is populated with all valid ihandles
1623  *
1624  * @return operation status
1625  *    @retval 0         success
1626  *    @retval ENOMEM    out of memory
1627  *
1628  * @todo we should handle out of memory conditions more gracefully.
1629  *
1630  * @internal vnode package internal use only
1631  */
1632 static int
1633 VInvalidateVnodesByVolume_r(Volume * vp,
1634                             IHandle_t *** vec_out,
1635                             size_t * vec_len_out)
1636 {
1637     int ret = 0;
1638     Vnode *vnp, *nvnp;
1639     size_t i = 0, vec_len;
1640     IHandle_t **ih_vec, **ih_vec_new;
1641
1642 #ifdef AFS_DEMAND_ATTACH_FS
1643     VOL_UNLOCK;
1644 #endif /* AFS_DEMAND_ATTACH_FS */
1645
1646     vec_len = IH_VEC_BASE_SIZE;
1647     ih_vec = malloc(sizeof(IHandle_t *) * vec_len);
1648 #ifdef AFS_DEMAND_ATTACH_FS
1649     VOL_LOCK;
1650 #endif
1651     if (ih_vec == NULL)
1652         return ENOMEM;
1653
1654     /*
1655      * Traverse the volume's vnode list.  Pull all the ihandles out into a
1656      * thread-private array for later asynchronous processing.
1657      */
1658 #ifdef AFS_DEMAND_ATTACH_FS
1659 restart_traversal:
1660 #endif
1661     for (queue_Scan(&vp->vnode_list, vnp, nvnp, Vnode)) {
1662         if (vnp->handle != NULL) {
1663             if (i == vec_len) {
1664 #ifdef AFS_DEMAND_ATTACH_FS
1665                 VOL_UNLOCK;
1666 #endif
1667                 vec_len += IH_VEC_INCREMENT;
1668                 ih_vec_new = realloc(ih_vec, sizeof(IHandle_t *) * vec_len);
1669 #ifdef AFS_DEMAND_ATTACH_FS
1670                 VOL_LOCK;
1671 #endif
1672                 if (ih_vec_new == NULL) {
1673                     ret = ENOMEM;
1674                     goto done;
1675                 }
1676                 ih_vec = ih_vec_new;
1677 #ifdef AFS_DEMAND_ATTACH_FS
1678                 /*
1679                  * Theoretically, the volume's VVn list should not change
1680                  * because the volume is in an exclusive state.  For the
1681                  * sake of safety, we will restart the traversal from the
1682                  * the beginning (which is not expensive because we're
1683                  * deleting the items from the list as we go).
1684                  */
1685                 goto restart_traversal;
1686 #endif
1687             }
1688             ih_vec[i++] = vnp->handle;
1689             vnp->handle = NULL;
1690         }
1691         DeleteFromVVnList(vnp);
1692         VInvalidateVnode_r(vnp);
1693     }
1694
1695  done:
1696     *vec_out = ih_vec;
1697     *vec_len_out = i;
1698
1699     return ret;
1700 }
1701
1702 /* VCloseVnodeFiles - called when a volume is going off line. All open
1703  * files for vnodes in that volume are closed. This might be excessive,
1704  * since we may only be taking one volume of a volume group offline.
1705  */
1706 void
1707 VCloseVnodeFiles_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_CLOSE);
1717 #endif /* AFS_DEMAND_ATTACH_FS */
1718
1719     /* XXX need better error handling here */
1720     opr_Verify(VInvalidateVnodesByVolume_r(vp, &ih_vec,
1721                                            &vec_len) == 0);
1722
1723     /*
1724      * DAFS:
1725      * now we drop VOL_LOCK while we perform some potentially very
1726      * expensive operations in the background
1727      */
1728 #ifdef AFS_DEMAND_ATTACH_FS
1729     VOL_UNLOCK;
1730 #endif
1731
1732     for (i = 0; i < vec_len; i++) {
1733         IH_REALLYCLOSE(ih_vec[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 }
1744
1745
1746 /**
1747  * shut down all vnode cache state for a given volume.
1748  *
1749  * @param[in] vp  volume object pointer
1750  *
1751  * @pre VOL_LOCK is held
1752  *
1753  * @post all file descriptors closed.
1754  *       all inode handles released.
1755  *       all vnode cache objects disassociated from volume.
1756  *
1757  * @note for DAFS, these operations are performed outside the vol glock under
1758  *       volume exclusive state VOL_STATE_VNODE_RELEASE.  Please further note
1759  *       that it would be a bug to acquire and release a volume reservation
1760  *       during this exclusive operation.  This is due to the fact that we are
1761  *       generally called during the refcount 1->0 transition.
1762  *
1763  * @todo we should handle failures in VInvalidateVnodesByVolume_r more
1764  *       gracefully.
1765  *
1766  * @see VInvalidateVnodesByVolume_r
1767  *
1768  * @internal this routine is internal to the volume package
1769  */
1770 void
1771 VReleaseVnodeFiles_r(Volume * vp)
1772 {
1773 #ifdef AFS_DEMAND_ATTACH_FS
1774     VolState vol_state_save;
1775 #endif
1776     IHandle_t ** ih_vec;
1777     size_t i, vec_len;
1778
1779 #ifdef AFS_DEMAND_ATTACH_FS
1780     vol_state_save = VChangeState_r(vp, VOL_STATE_VNODE_RELEASE);
1781 #endif /* AFS_DEMAND_ATTACH_FS */
1782
1783     /* XXX need better error handling here */
1784     opr_Verify(VInvalidateVnodesByVolume_r(vp, &ih_vec,
1785                                            &vec_len) == 0);
1786
1787     /*
1788      * DAFS:
1789      * now we drop VOL_LOCK while we perform some potentially very
1790      * expensive operations in the background
1791      */
1792 #ifdef AFS_DEMAND_ATTACH_FS
1793     VOL_UNLOCK;
1794 #endif
1795
1796     for (i = 0; i < vec_len; i++) {
1797         IH_RELEASE(ih_vec[i]);
1798     }
1799
1800     free(ih_vec);
1801
1802 #ifdef AFS_DEMAND_ATTACH_FS
1803     VOL_LOCK;
1804     VChangeState_r(vp, vol_state_save);
1805 #endif /* AFS_DEMAND_ATTACH_FS */
1806 }