venus: Remove dedebug
[openafs.git] / src / afs / afs_vcache.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 /*
11  * Implements:
12  * afs_FlushVCache
13  * afs_AllocCBR
14  * afs_FreeCBR
15  * afs_FlushVCBs
16  * afs_QueueVCB
17  * afs_RemoveVCB
18  * afs_NewVCache
19  * afs_FlushActiveVcaches
20  * afs_VerifyVCache2
21  * afs_WriteVCache
22  * afs_WriteVCacheDiscon
23  * afs_SimpleVStat
24  * afs_ProcessFS
25  * afs_RemoteLookup
26  * afs_GetVCache
27  * afs_LookupVCache
28  * afs_GetRootVCache
29  * afs_UpdateStatus
30  * afs_FetchStatus
31  * afs_StuffVcache
32  * afs_PutVCache
33  * afs_FindVCache
34  * afs_NFSFindVCache
35  * afs_vcacheInit
36  * shutdown_vcache
37  *
38  */
39 #include <afsconfig.h>
40 #include "afs/param.h"
41
42 #include "afs/sysincludes.h"   /*Standard vendor system headers */
43 #include "afsincludes.h"       /*AFS-based standard headers */
44 #include "afs/afs_stats.h"
45 #include "afs/afs_cbqueue.h"
46 #include "afs/afs_osidnlc.h"
47
48 afs_int32 afs_maxvcount = 0;    /* max number of vcache entries */
49 afs_int32 afs_vcount = 0;       /* number of vcache in use now */
50
51 #ifdef AFS_SGI_ENV
52 int afsvnumbers = 0;
53 #endif
54
55 #ifdef AFS_SGI64_ENV
56 char *makesname();
57 #endif /* AFS_SGI64_ENV */
58
59 /* Exported variables */
60 afs_rwlock_t afs_xvcdirty;      /*Lock: discon vcache dirty list mgmt */
61 afs_rwlock_t afs_xvcache;       /*Lock: alloc new stat cache entries */
62 afs_rwlock_t afs_xvreclaim;     /*Lock: entries reclaimed, not on free list */
63 afs_lock_t afs_xvcb;            /*Lock: fids on which there are callbacks */
64 #if !defined(AFS_LINUX22_ENV)
65 static struct vcache *freeVCList;       /*Free list for stat cache entries */
66 struct vcache *ReclaimedVCList; /*Reclaimed list for stat entries */
67 static struct vcache *Initial_freeVCList;       /*Initial list for above */
68 #endif
69 struct afs_q VLRU;              /*vcache LRU */
70 afs_int32 vcachegen = 0;
71 unsigned int afs_paniconwarn = 0;
72 struct vcache *afs_vhashT[VCSIZE];
73 struct afs_q afs_vhashTV[VCSIZE];
74 static struct afs_cbr *afs_cbrHashT[CBRSIZE];
75 afs_int32 afs_bulkStatsLost;
76 int afs_norefpanic = 0;
77
78
79 /* Disk backed vcache definitions
80  * Both protected by xvcache */
81 static int afs_nextVcacheSlot = 0;
82 static struct afs_slotlist *afs_freeSlotList = NULL;
83
84 /* Forward declarations */
85 static afs_int32 afs_QueueVCB(struct vcache *avc, int *slept);
86
87
88 /*
89  * The PFlush algorithm makes use of the fact that Fid.Unique is not used in
90  * below hash algorithms.  Change it if need be so that flushing algorithm
91  * doesn't move things from one hash chain to another.
92  */
93 /* Don't hash on the cell; our callback-breaking code sometimes fails to compute
94  * the cell correctly, and only scans one hash bucket. */
95 int VCHash(struct VenusFid *fid)
96 {
97     return opr_jhash_int2(fid->Fid.Volume, fid->Fid.Vnode, 0) &
98         opr_jhash_mask(VCSIZEBITS);
99 }
100 /* Hash only on volume to speed up volume callbacks. */
101 int VCHashV(struct VenusFid *fid)
102 {
103     return opr_jhash_int(fid->Fid.Volume, 0) & opr_jhash_mask(VCSIZEBITS);
104 }
105
106 /*!
107  * Generate an index into the hash table for a given Fid.
108  * \param fid
109  * \return The hash value.
110  */
111 static int
112 afs_HashCBRFid(struct AFSFid *fid)
113 {
114     return (fid->Volume + fid->Vnode + fid->Unique) % CBRSIZE;
115 }
116
117 /*!
118  * Insert a CBR entry into the hash table.
119  * Must be called with afs_xvcb held.
120  * \param cbr
121  * \return
122  */
123 static void
124 afs_InsertHashCBR(struct afs_cbr *cbr)
125 {
126     int slot = afs_HashCBRFid(&cbr->fid);
127
128     cbr->hash_next = afs_cbrHashT[slot];
129     if (afs_cbrHashT[slot])
130         afs_cbrHashT[slot]->hash_pprev = &cbr->hash_next;
131
132     cbr->hash_pprev = &afs_cbrHashT[slot];
133     afs_cbrHashT[slot] = cbr;
134 }
135
136 /*!
137  *
138  * Flush the given vcache entry.
139  *
140  * Environment:
141  *      afs_xvcache lock must be held for writing upon entry to
142  *      prevent people from changing the vrefCount field, and to
143  *      protect the lruq and hnext fields.
144  * LOCK: afs_FlushVCache afs_xvcache W
145  * REFCNT: vcache ref count must be zero on entry except for osf1
146  * RACE: lock is dropped and reobtained, permitting race in caller
147  *
148  * \param avc Pointer to vcache entry to flush.
149  * \param slept Pointer to int to set 1 if we sleep/drop locks, 0 if we don't.
150  *
151  */
152 int
153 afs_FlushVCache(struct vcache *avc, int *slept)
154 {                               /*afs_FlushVCache */
155
156     afs_int32 i, code;
157     struct vcache **uvc, *wvc;
158
159     /* NOTE: We must have nothing drop afs_xvcache until we have removed all
160      * possible references to this vcache. This means all hash tables, queues,
161      * DNLC, etc. */
162
163     *slept = 0;
164     AFS_STATCNT(afs_FlushVCache);
165     afs_Trace2(afs_iclSetp, CM_TRACE_FLUSHV, ICL_TYPE_POINTER, avc,
166                ICL_TYPE_INT32, avc->f.states);
167
168     code = osi_VM_FlushVCache(avc);
169     if (code)
170         goto bad;
171
172     if (avc->f.states & CVFlushed) {
173         code = EBUSY;
174         goto bad;
175     }
176 #if !defined(AFS_LINUX22_ENV)
177     if (avc->nextfree || !avc->vlruq.prev || !avc->vlruq.next) {        /* qv afs.h */
178         refpanic("LRU vs. Free inconsistency");
179     }
180 #endif
181     avc->f.states |= CVFlushed;
182     /* pull the entry out of the lruq and put it on the free list */
183     QRemove(&avc->vlruq);
184
185     /* keep track of # of files that we bulk stat'd, but never used
186      * before they got recycled.
187      */
188     if (avc->f.states & CBulkStat)
189         afs_bulkStatsLost++;
190     vcachegen++;
191     /* remove entry from the hash chain */
192     i = VCHash(&avc->f.fid);
193     uvc = &afs_vhashT[i];
194     for (wvc = *uvc; wvc; uvc = &wvc->hnext, wvc = *uvc) {
195         if (avc == wvc) {
196             *uvc = avc->hnext;
197             avc->hnext = NULL;
198             break;
199         }
200     }
201
202     /* remove entry from the volume hash table */
203     QRemove(&avc->vhashq);
204
205 #if defined(AFS_LINUX26_ENV)
206     {
207         struct pagewriter *pw, *store;
208         struct list_head tofree;
209
210         INIT_LIST_HEAD(&tofree);
211         spin_lock(&avc->pagewriter_lock);
212         list_for_each_entry_safe(pw, store, &avc->pagewriters, link) {
213             list_del(&pw->link);
214             /* afs_osi_Free may sleep so we need to defer it */
215             list_add_tail(&pw->link, &tofree);
216         }
217         spin_unlock(&avc->pagewriter_lock);
218         list_for_each_entry_safe(pw, store, &tofree, link) {
219             list_del(&pw->link);
220             afs_osi_Free(pw, sizeof(struct pagewriter));
221         }
222     }
223 #endif
224
225     if (avc->mvid.target_root)
226         osi_FreeSmallSpace(avc->mvid.target_root);
227     avc->mvid.target_root = NULL;
228     if (avc->linkData) {
229         afs_osi_Free(avc->linkData, strlen(avc->linkData) + 1);
230         avc->linkData = NULL;
231     }
232 #if defined(AFS_XBSD_ENV) || defined(AFS_DARWIN_ENV)
233     /* OK, there are no internal vrefCounts, so there shouldn't
234      * be any more refs here. */
235     if (avc->v) {
236 # ifdef AFS_DARWIN80_ENV
237         vnode_clearfsnode(AFSTOV(avc));
238         vnode_removefsref(AFSTOV(avc));
239 # else
240         avc->v->v_data = NULL;  /* remove from vnode */
241 # endif
242         AFSTOV(avc) = NULL;             /* also drop the ptr to vnode */
243     }
244 #endif
245
246 #ifdef AFS_SUN511_ENV
247     if (avc->v) {
248         vn_free(avc->v);
249         avc->v = NULL;
250     }
251 #elif defined(AFS_SUN510_ENV)
252     /* As we use private vnodes, cleanup is up to us */
253     vn_reinit(AFSTOV(avc));
254 #endif
255     afs_FreeAllAxs(&(avc->Access));
256     afs_StaleVCacheFlags(avc, AFS_STALEVC_FILENAME, CUnique);
257
258     /* By this point, the vcache has been removed from all global structures
259      * via which someone could try to use the vcache. It is okay to drop
260      * afs_xvcache at this point (if *slept is set). */
261
262     if (afs_shuttingdown == AFS_RUNNING)
263         afs_QueueVCB(avc, slept);
264
265     /*
266      * Next, keep track of which vnodes we've deleted for create's
267      * optimistic synchronization algorithm
268      */
269     afs_allZaps++;
270     if (avc->f.fid.Fid.Vnode & 1)
271         afs_oddZaps++;
272     else
273         afs_evenZaps++;
274
275     afs_vcount--;
276 #if !defined(AFS_LINUX22_ENV)
277     /* put the entry in the free list */
278     avc->nextfree = freeVCList;
279     freeVCList = avc;
280     if (avc->vlruq.prev || avc->vlruq.next) {
281         refpanic("LRU vs. Free inconsistency");
282     }
283     avc->f.states |= CVFlushed;
284 #else
285     /* This should put it back on the vnode free list since usecount is 1 */
286     vSetType(avc, VREG);
287     if (VREFCOUNT_GT(avc,0)) {
288         AFS_RELE(AFSTOV(avc));
289         afs_stats_cmperf.vcacheXAllocs--;
290     } else {
291         if (afs_norefpanic) {
292             afs_warn("flush vc refcnt < 1");
293             afs_norefpanic++;
294         } else
295             osi_Panic("flush vc refcnt < 1");
296     }
297 #endif /* AFS_LINUX22_ENV */
298     return 0;
299
300   bad:
301     return code;
302 }                               /*afs_FlushVCache */
303
304 #ifndef AFS_SGI_ENV
305 /*!
306  *  The core of the inactive vnode op for all but IRIX.
307  *
308  * \param avc
309  * \param acred
310  */
311 void
312 afs_InactiveVCache(struct vcache *avc, afs_ucred_t *acred)
313 {
314     AFS_STATCNT(afs_inactive);
315     if (avc->f.states & CDirty) {
316         /* we can't keep trying to push back dirty data forever.  Give up. */
317         afs_InvalidateAllSegments(avc); /* turns off dirty bit */
318     }
319     avc->f.states &= ~CMAPPED;
320     avc->f.states &= ~CDirty;   /* Turn it off */
321     if (avc->f.states & CUnlinked) {
322         if (CheckLock(&afs_xvcache) || CheckLock(&afs_xdcache)) {
323             avc->f.states |= CUnlinkedDel;
324             return;
325         }
326         afs_remunlink(avc, 1);  /* ignore any return code */
327     }
328
329 }
330 #endif
331
332 /*!
333  *   Allocate a callback return structure from the
334  * free list and return it.
335  *
336  * Environment: The alloc and free routines are both called with the afs_xvcb lock
337  * held, so we don't have to worry about blocking in osi_Alloc.
338  *
339  * \return The allocated afs_cbr.
340  */
341 static struct afs_cbr *afs_cbrSpace = 0;
342 /* if alloc limit below changes, fix me! */
343 static struct afs_cbr *afs_cbrHeads[16];
344 struct afs_cbr *
345 afs_AllocCBR(void)
346 {
347     struct afs_cbr *tsp;
348     int i;
349
350     while (!afs_cbrSpace) {
351         if (afs_stats_cmperf.CallBackAlloced >= sizeof(afs_cbrHeads)/sizeof(afs_cbrHeads[0])) {
352             /* don't allocate more than 16 * AFS_NCBRS for now */
353             afs_FlushVCBs(0);
354             afs_stats_cmperf.CallBackFlushes++;
355         } else {
356             /* try allocating */
357             tsp = afs_osi_Alloc(AFS_NCBRS * sizeof(struct afs_cbr));
358             osi_Assert(tsp != NULL);
359             for (i = 0; i < AFS_NCBRS - 1; i++) {
360                 tsp[i].next = &tsp[i + 1];
361             }
362             tsp[AFS_NCBRS - 1].next = 0;
363             afs_cbrSpace = tsp;
364             afs_cbrHeads[afs_stats_cmperf.CallBackAlloced] = tsp;
365             afs_stats_cmperf.CallBackAlloced++;
366         }
367     }
368     tsp = afs_cbrSpace;
369     afs_cbrSpace = tsp->next;
370     return tsp;
371 }
372
373 /*!
374  * Free a callback return structure, removing it from all lists.
375  *
376  * Environment: the xvcb lock is held over these calls.
377  *
378  * \param asp The address of the structure to free.
379  *
380  * \rerurn 0
381  */
382 int
383 afs_FreeCBR(struct afs_cbr *asp)
384 {
385     *(asp->pprev) = asp->next;
386     if (asp->next)
387         asp->next->pprev = asp->pprev;
388
389     *(asp->hash_pprev) = asp->hash_next;
390     if (asp->hash_next)
391         asp->hash_next->hash_pprev = asp->hash_pprev;
392
393     asp->next = afs_cbrSpace;
394     afs_cbrSpace = asp;
395     return 0;
396 }
397
398 static void
399 FlushAllVCBs(int nconns, struct rx_connection **rxconns,
400              struct afs_conn **conns)
401 {
402     afs_int32 *results;
403     afs_int32 i;
404
405     results = afs_osi_Alloc(nconns * sizeof (afs_int32));
406     osi_Assert(results != NULL);
407
408     AFS_GUNLOCK();
409     multi_Rx(rxconns,nconns)
410     {
411         multi_RXAFS_GiveUpAllCallBacks();
412         results[multi_i] = multi_error;
413     } multi_End;
414     AFS_GLOCK();
415
416     /*
417      * Freeing the CBR will unlink it from the server's CBR list
418      * do it here, not in the loop, because a dynamic CBR will call
419      * into the memory management routines.
420      */
421     for ( i = 0 ; i < nconns ; i++ ) {
422         if (results[i] == 0) {
423             /* Unchain all of them */
424             while (conns[i]->parent->srvr->server->cbrs)
425                 afs_FreeCBR(conns[i]->parent->srvr->server->cbrs);
426         }
427     }
428     afs_osi_Free(results, nconns * sizeof(afs_int32));
429 }
430
431 /*!
432  *   Flush all queued callbacks to all servers.
433  *
434  * Environment: holds xvcb lock over RPC to guard against race conditions
435  *      when a new callback is granted for the same file later on.
436  *
437  * \return 0 for success.
438  */
439 afs_int32
440 afs_FlushVCBs(afs_int32 lockit)
441 {
442     struct AFSFid *tfids;
443     struct AFSCallBack callBacks[1];
444     struct AFSCBFids fidArray;
445     struct AFSCBs cbArray;
446     afs_int32 code;
447     struct afs_cbr *tcbrp;
448     int tcount;
449     struct server *tsp;
450     int i;
451     struct vrequest *treq = NULL;
452     struct afs_conn *tc;
453     int safety1, safety2, safety3;
454     XSTATS_DECLS;
455
456     if (AFS_IS_DISCONNECTED)
457         return ENETDOWN;
458
459     if ((code = afs_CreateReq(&treq, afs_osi_credp)))
460         return code;
461     treq->flags |= O_NONBLOCK;
462     tfids = afs_osi_Alloc(sizeof(struct AFSFid) * AFS_MAXCBRSCALL);
463     osi_Assert(tfids != NULL);
464
465     if (lockit)
466         ObtainWriteLock(&afs_xvcb, 273);
467     /*
468      * Shutting down.
469      * First, attempt a multi across everything, all addresses
470      * for all servers we know of.
471      */
472
473     if (lockit == 2)
474         afs_LoopServers(AFS_LS_ALL, NULL, 0, FlushAllVCBs, NULL);
475
476     ObtainReadLock(&afs_xserver);
477     for (i = 0; i < NSERVERS; i++) {
478         for (safety1 = 0, tsp = afs_servers[i];
479              tsp && safety1 < afs_totalServers + 10;
480              tsp = tsp->next, safety1++) {
481             /* don't have any */
482             if (tsp->cbrs == (struct afs_cbr *)0)
483                 continue;
484
485             /* otherwise, grab a block of AFS_MAXCBRSCALL from the list
486              * and make an RPC, over and over again.
487              */
488             tcount = 0;         /* number found so far */
489             for (safety2 = 0; safety2 < afs_cacheStats; safety2++) {
490                 if (tcount >= AFS_MAXCBRSCALL || !tsp->cbrs) {
491                     struct rx_connection *rxconn;
492                     /* if buffer is full, or we've queued all we're going
493                      * to from this server, we should flush out the
494                      * callbacks.
495                      */
496                     fidArray.AFSCBFids_len = tcount;
497                     fidArray.AFSCBFids_val = (struct AFSFid *)tfids;
498                     cbArray.AFSCBs_len = 1;
499                     cbArray.AFSCBs_val = callBacks;
500                     memset(&callBacks[0], 0, sizeof(callBacks[0]));
501                     callBacks[0].CallBackType = CB_EXCLUSIVE;
502                     for (safety3 = 0; safety3 < AFS_MAXHOSTS * 2; safety3++) {
503                         tc = afs_ConnByHost(tsp, tsp->cell->fsport,
504                                             tsp->cell->cellNum, treq, 0,
505                                             SHARED_LOCK, 0, &rxconn);
506                         if (tc) {
507                             XSTATS_START_TIME
508                                 (AFS_STATS_FS_RPCIDX_GIVEUPCALLBACKS);
509                             RX_AFS_GUNLOCK();
510                             code =
511                                 RXAFS_GiveUpCallBacks(rxconn, &fidArray,
512                                                       &cbArray);
513                             RX_AFS_GLOCK();
514                             XSTATS_END_TIME;
515                         } else
516                             code = -1;
517                         if (!afs_Analyze
518                             (tc, rxconn, code, 0, treq,
519                              AFS_STATS_FS_RPCIDX_GIVEUPCALLBACKS, SHARED_LOCK,
520                              tsp->cell)) {
521                             break;
522                         }
523                     }
524                     /* ignore return code, since callbacks may have
525                      * been returned anyway, we shouldn't leave them
526                      * around to be returned again.
527                      *
528                      * Next, see if we are done with this server, and if so,
529                      * break to deal with the next one.
530                      */
531                     if (!tsp->cbrs)
532                         break;
533                     tcount = 0;
534                 }
535                 /* if to flush full buffer */
536                 /* if we make it here, we have an entry at the head of cbrs,
537                  * which we should copy to the file ID array and then free.
538                  */
539                 tcbrp = tsp->cbrs;
540                 tfids[tcount++] = tcbrp->fid;
541
542                 /* Freeing the CBR will unlink it from the server's CBR list */
543                 afs_FreeCBR(tcbrp);
544             }                   /* while loop for this one server */
545             if (safety2 > afs_cacheStats) {
546                 afs_warn("possible internal error afs_flushVCBs (%d)\n",
547                          safety2);
548             }
549         }                       /* for loop for this hash chain */
550     }                           /* loop through all hash chains */
551     if (safety1 > afs_totalServers + 2) {
552         afs_warn
553             ("AFS internal error (afs_flushVCBs) (%d > %d), continuing...\n",
554              safety1, afs_totalServers + 2);
555         if (afs_paniconwarn)
556             osi_Panic("afs_flushVCBS safety1");
557     }
558
559     ReleaseReadLock(&afs_xserver);
560     if (lockit)
561         ReleaseWriteLock(&afs_xvcb);
562     afs_osi_Free(tfids, sizeof(struct AFSFid) * AFS_MAXCBRSCALL);
563     afs_DestroyReq(treq);
564     return 0;
565 }
566
567 /*!
568  *  Queue a callback on the given fid.
569  *
570  * Environment:
571  *      Locks the xvcb lock.
572  *      Called when the xvcache lock is already held.
573  * RACE: afs_xvcache may be dropped and reacquired
574  *
575  * \param avc vcache entry
576  * \param slep Set to 1 if we dropped afs_xvcache
577  * \return 1 if queued, 0 otherwise
578  */
579
580 static afs_int32
581 afs_QueueVCB(struct vcache *avc, int *slept)
582 {
583     int queued = 0;
584     struct server *tsp;
585     struct afs_cbr *tcbp;
586     int reacquire = 0;
587
588     AFS_STATCNT(afs_QueueVCB);
589
590     ObtainWriteLock(&afs_xvcb, 274);
591
592     /* we can't really give back callbacks on RO files, since the
593      * server only tracks them on a per-volume basis, and we don't
594      * know whether we still have some other files from the same
595      * volume. */
596     if (!((avc->f.states & CRO) == 0 && avc->callback)) {
597         goto done;
598     }
599
600     /* The callback is really just a struct server ptr. */
601     tsp = (struct server *)(avc->callback);
602
603     if (!afs_cbrSpace) {
604         /* If we don't have CBR space, AllocCBR may block or hit the net for
605          * clearing up CBRs. Hitting the net may involve a fileserver
606          * needing to contact us, so we must drop xvcache so we don't block
607          * those requests from going through. */
608         reacquire = *slept = 1;
609         ReleaseWriteLock(&afs_xvcache);
610     }
611
612     /* we now have a pointer to the server, so we just allocate
613      * a queue entry and queue it.
614      */
615     tcbp = afs_AllocCBR();
616     tcbp->fid = avc->f.fid.Fid;
617
618     tcbp->next = tsp->cbrs;
619     if (tsp->cbrs)
620         tsp->cbrs->pprev = &tcbp->next;
621
622     tsp->cbrs = tcbp;
623     tcbp->pprev = &tsp->cbrs;
624
625     afs_InsertHashCBR(tcbp);
626     queued = 1;
627
628  done:
629     /* now release locks and return */
630     ReleaseWriteLock(&afs_xvcb);
631
632     if (reacquire) {
633         /* make sure this is after dropping xvcb, for locking order */
634         ObtainWriteLock(&afs_xvcache, 279);
635     }
636     return queued;
637 }
638
639
640 /*!
641  *   Remove a queued callback for a given Fid.
642  *
643  * Environment:
644  *      Locks xvcb and xserver locks.
645  *      Typically called with xdcache, xvcache and/or individual vcache
646  *      entries locked.
647  *
648  * \param afid The fid we want cleansed of queued callbacks.
649  *
650  */
651
652 void
653 afs_RemoveVCB(struct VenusFid *afid)
654 {
655     int slot;
656     struct afs_cbr *cbr, *ncbr;
657
658     AFS_STATCNT(afs_RemoveVCB);
659     ObtainWriteLock(&afs_xvcb, 275);
660
661     slot = afs_HashCBRFid(&afid->Fid);
662     ncbr = afs_cbrHashT[slot];
663
664     while (ncbr) {
665         cbr = ncbr;
666         ncbr = cbr->hash_next;
667
668         if (afid->Fid.Volume == cbr->fid.Volume &&
669             afid->Fid.Vnode == cbr->fid.Vnode &&
670             afid->Fid.Unique == cbr->fid.Unique) {
671             afs_FreeCBR(cbr);
672         }
673     }
674
675     ReleaseWriteLock(&afs_xvcb);
676 }
677
678 void
679 afs_FlushReclaimedVcaches(void)
680 {
681 #if !defined(AFS_LINUX22_ENV)
682     struct vcache *tvc;
683     int code, fv_slept;
684     struct vcache *tmpReclaimedVCList = NULL;
685
686     ObtainWriteLock(&afs_xvreclaim, 76);
687     while (ReclaimedVCList) {
688         tvc = ReclaimedVCList;  /* take from free list */
689         ReclaimedVCList = tvc->nextfree;
690         tvc->nextfree = NULL;
691         code = afs_FlushVCache(tvc, &fv_slept);
692         if (code) {
693             /* Ok, so, if we got code != 0, uh, wtf do we do? */
694             /* Probably, build a temporary list and then put all back when we
695                get to the end of the list */
696             /* This is actually really crappy, but we need to not leak these.
697                We probably need a way to be smarter about this. */
698             tvc->nextfree = tmpReclaimedVCList;
699             tmpReclaimedVCList = tvc;
700             /* printf("Reclaim list flush %lx failed: %d\n", (unsigned long) tvc, code); */
701         }
702         if (tvc->f.states & (CVInit
703 # ifdef AFS_DARWIN80_ENV
704                           | CDeadVnode
705 # endif
706            )) {
707            tvc->f.states &= ~(CVInit
708 # ifdef AFS_DARWIN80_ENV
709                             | CDeadVnode
710 # endif
711            );
712            afs_osi_Wakeup(&tvc->f.states);
713         }
714     }
715     if (tmpReclaimedVCList)
716         ReclaimedVCList = tmpReclaimedVCList;
717
718     ReleaseWriteLock(&afs_xvreclaim);
719 #endif
720 }
721
722 void
723 afs_PostPopulateVCache(struct vcache *avc, struct VenusFid *afid, int seq)
724 {
725     /*
726      * The proper value for mvstat (for root fids) is setup by the caller.
727      */
728     avc->mvstat = AFS_MVSTAT_FILE;
729     if (afid->Fid.Vnode == 1 && afid->Fid.Unique == 1)
730         avc->mvstat = AFS_MVSTAT_ROOT;
731
732     if (afs_globalVFS == 0)
733         osi_Panic("afs globalvfs");
734
735     osi_PostPopulateVCache(avc);
736
737     avc->dchint = NULL;
738     osi_dnlc_purgedp(avc);      /* this may be overkill */
739     memset(&(avc->callsort), 0, sizeof(struct afs_q));
740     avc->slocks = NULL;
741     avc->f.states &=~ CVInit;
742     if (seq) {
743         avc->f.states |= CBulkFetching;
744         avc->f.m.Length = seq;
745     }
746     afs_osi_Wakeup(&avc->f.states);
747 }
748
749 /*
750  * afs_VCacheStressed() is intended to determine if the stat cache looks
751  * stressed / full-ish. Due to the different strategies of allocating vcaches
752  * on different platforms, the definition of "stressed" varies, and is somewhat
753  * arbitrary. We just try to make a reasonable guess here.
754  *
755  * Returns 1 if the stat cache looks stressed, and 0 otherwise.
756  */
757 #ifdef AFS_LINUX26_ENV
758 int
759 afs_VCacheStressed(void)
760 {
761     if (afsd_dynamic_vcaches) {
762         /*
763          * For dynamic vcaches, the number of vcaches in use can vary wildly.
764          * Consider us stressed if we're significantly above the configured
765          * threshold. VCACHE_DYNAMIC_STRESSED is the arbitrary point at which
766          * we're considered "significantly" over the threshold.
767          */
768         if (afs_vcount > afs_cacheStats + VCACHE_DYNAMIC_STRESSED) {
769             return 1;
770         }
771         return 0;
772
773     } else {
774         /*
775          * For non-dynamic vcaches, we should never go above the configured
776          * limit, and ShakeLooseVCaches should try to get us to VCACHE_FREE
777          * under the limit. So if we're closer then VCACHE_FREE/2, then we're
778          * very close to the limit, so consider us stressed.
779          */
780         if (afs_vcount > afs_cacheStats || afs_cacheStats - afs_vcount < VCACHE_FREE/2) {
781             return 1;
782         }
783         return 0;
784     }
785 }
786 #else /* AFS_LINUX26_ENV */
787 int
788 afs_VCacheStressed(void)
789 {
790     /* If we don't have any vcaches in the free list, then consider the stat
791      * cache stressed. */
792     if (freeVCList != NULL) {
793         return 0;
794     }
795     return 1;
796 }
797 #endif /* AFS_LINUX26_ENV */
798
799 int
800 afs_ShakeLooseVCaches(afs_int32 anumber)
801 {
802     /* Try not to run for more than about 3 seconds */
803     static const int DEADLINE = 3;
804
805     afs_int32 i, loop;
806     int evicted;
807     struct vcache *tvc;
808     struct afs_q *tq, *uq;
809     int fv_slept, defersleep = 0;
810     int limit;
811     afs_uint32 start = osi_Time();
812
813     loop = 0;
814
815  retry:
816     i = 0;
817     limit = afs_vcount;
818     for (tq = VLRU.prev; tq != &VLRU && anumber > 0; tq = uq) {
819         tvc = QTOV(tq);
820         uq = QPrev(tq);
821         if (tvc->f.states & CVFlushed) {
822             refpanic("CVFlushed on VLRU");
823         } else if (i++ > limit) {
824             afs_warn("afs_ShakeLooseVCaches: i %d limit %d afs_vcount %d afs_maxvcount %d\n",
825                      (int)i, limit, (int)afs_vcount, (int)afs_maxvcount);
826             refpanic("Found too many AFS vnodes on VLRU (VLRU cycle?)");
827         } else if (QNext(uq) != tq) {
828             refpanic("VLRU inconsistent");
829         } else if (tvc->f.states & CVInit) {
830             continue;
831         }
832
833         fv_slept = 0;
834         evicted = osi_TryEvictVCache(tvc, &fv_slept, defersleep);
835         if (evicted) {
836             anumber--;
837         }
838
839         if (fv_slept) {
840             if (loop++ > 100) {
841                 afs_uint32 now = osi_Time();
842                 loop = 0;
843                 if (now < start) {
844                     start = now;
845                 }
846                 if (now - start >= DEADLINE) {
847                     static afs_uint32 last_warned;
848                     /* Warn about this at most every VCACHE_STRESS_LOGINTERVAL secs */
849                     if (now < last_warned ||
850                         now - last_warned > VCACHE_STRESS_LOGINTERVAL) {
851                         last_warned = now;
852                         afs_warn("afs: Warning: it took us a long time (around "
853                                  "%d seconds) to try to trim our stat cache "
854                                  "down to a reasonable size. This may indicate "
855                                  "someone is accessing an excessive number of "
856                                  "files, or something is wrong with the AFS "
857                                  "cache.\n",
858                                  now - start);
859                         afs_warn("afs: Consider raising the afsd -stat parameter "
860                                  "(current setting: %d, current vcount: %d), or "
861                                  "figure out what is accessing so many files.\n",
862                                  afs_cacheStats, afs_vcount);
863                     }
864                     break;
865                 }
866             }
867             if (!evicted) {
868                 /*
869                  * This vcache was busy and we slept while trying to evict it.
870                  * Move this busy vcache to the head of the VLRU so vcaches
871                  * following this busy vcache can be evicted during the retry.
872                  */
873                 QRemove(&tvc->vlruq);
874                 QAdd(&VLRU, &tvc->vlruq);
875             }
876             goto retry; /* start over - may have raced. */
877         }
878         if (uq == &VLRU) {
879             if (anumber && !defersleep) {
880                 defersleep = 1;
881                 goto retry;
882             }
883             break;
884         }
885     }
886
887     if (afs_VCacheStressed()) {
888         /*
889          * If it looks like we have too many vcaches, right after
890          * ShakeLooseVCaches has tried to trim down the number of vcaches, then
891          * maybe -stat should be increased. Log a warning, so if this is
892          * causing problems the user has a chance at noticing.
893          */
894         static afs_uint32 last_warned;
895         afs_uint32 now = osi_Time();
896
897         /* Warn about this at most once every VCACHE_STRESS_LOGINTERVAL secs */
898         if (now - last_warned > VCACHE_STRESS_LOGINTERVAL) {
899             last_warned = now;
900             afs_warn("afs: Warning: We are having trouble keeping the AFS stat "
901                      "cache trimmed down under the configured limit (current "
902                      "-stat setting: %d, current vcache usage: %d).\n",
903                      afs_cacheStats, afs_vcount);
904             afs_warn("afs: If AFS access seems slow, consider raising the "
905                      "-stat setting for afsd.\n");
906         }
907     }
908
909     return 0;
910 }
911
912 /* Alloc new vnode. */
913
914 static struct vcache *
915 afs_AllocVCache(void)
916 {
917     struct vcache *tvc;
918
919     tvc = osi_NewVnode();
920     if (tvc == NULL) {
921         return NULL;
922     }
923
924     afs_vcount++;
925
926     /* track the peak */
927     if (afsd_dynamic_vcaches && afs_maxvcount < afs_vcount) {
928         afs_maxvcount = afs_vcount;
929         /*printf("peak vnodes: %d\n", afs_maxvcount);*/
930     }
931
932     afs_stats_cmperf.vcacheXAllocs++;   /* count in case we have a leak */
933
934     /* If we create a new inode, we either give it a new slot number,
935      * or if one's available, use a slot number from the slot free list
936      */
937     if (afs_freeSlotList != NULL) {
938        struct afs_slotlist *tmp;
939
940        tvc->diskSlot = afs_freeSlotList->slot;
941        tmp = afs_freeSlotList;
942        afs_freeSlotList = tmp->next;
943        afs_osi_Free(tmp, sizeof(struct afs_slotlist));
944     }  else {
945        tvc->diskSlot = afs_nextVcacheSlot++;
946     }
947
948     return tvc;
949 }
950
951 /* Pre populate a newly allocated vcache. On platforms where the actual
952  * vnode is attached to the vcache, this function is called before attachment,
953  * therefore it cannot perform any actions on the vnode itself */
954
955 static void
956 afs_PrePopulateVCache(struct vcache *avc, struct VenusFid *afid,
957                       struct server *serverp) {
958
959     afs_uint32 slot;
960     afs_hyper_t zero;
961     slot = avc->diskSlot;
962
963     osi_PrePopulateVCache(avc);
964
965     avc->diskSlot = slot;
966     QZero(&avc->metadirty);
967
968     AFS_RWLOCK_INIT(&avc->lock, "vcache lock");
969
970     memset(&avc->mvid, 0, sizeof(avc->mvid));
971     avc->linkData = NULL;
972     avc->cbExpires = 0;
973     avc->opens = 0;
974     avc->execsOrWriters = 0;
975     avc->flockCount = 0;
976     avc->f.states = CVInit;
977     avc->last_looker = 0;
978     avc->f.fid = *afid;
979     avc->asynchrony = -1;
980     avc->vc_error = 0;
981
982     hzero(avc->mapDV);
983     avc->f.truncPos = AFS_NOTRUNC;   /* don't truncate until we need to */
984     hzero(zero);
985     afs_SetDataVersion(avc, &zero);  /* in case we copy it into flushDV */
986     avc->Access = NULL;
987     avc->callback = serverp;         /* to minimize chance that clear
988                                       * request is lost */
989
990 #if defined(AFS_CACHE_BYPASS)
991     avc->cachingStates = 0;
992     avc->cachingTransitions = 0;
993 #endif
994 }
995
996 void
997 afs_FlushAllVCaches(void)
998 {
999     int i;
1000     struct vcache *tvc, *nvc;
1001
1002     ObtainWriteLock(&afs_xvcache, 867);
1003
1004  retry:
1005     for (i = 0; i < VCSIZE; i++) {
1006         for (tvc = afs_vhashT[i]; tvc; tvc = nvc) {
1007             int slept;
1008
1009             nvc = tvc->hnext;
1010             if (afs_FlushVCache(tvc, &slept)) {
1011                 afs_warn("Failed to flush vcache 0x%lx\n", (unsigned long)(uintptrsz)tvc);
1012             }
1013             if (slept) {
1014                 goto retry;
1015             }
1016         }
1017     }
1018
1019     ReleaseWriteLock(&afs_xvcache);
1020 }
1021
1022 /*!
1023  *   This routine is responsible for allocating a new cache entry
1024  * from the free list.  It formats the cache entry and inserts it
1025  * into the appropriate hash tables.  It must be called with
1026  * afs_xvcache write-locked so as to prevent several processes from
1027  * trying to create a new cache entry simultaneously.
1028  *
1029  * LOCK: afs_NewVCache  afs_xvcache W
1030  *
1031  * \param afid The file id of the file whose cache entry is being created.
1032  *
1033  * \return The new vcache struct.
1034  */
1035
1036 static_inline struct vcache *
1037 afs_NewVCache_int(struct VenusFid *afid, struct server *serverp, int seq)
1038 {
1039     struct vcache *tvc;
1040     afs_int32 i, j;
1041     afs_int32 anumber = VCACHE_FREE;
1042
1043     AFS_STATCNT(afs_NewVCache);
1044
1045     afs_FlushReclaimedVcaches();
1046
1047 #if defined(AFS_LINUX22_ENV)
1048     if(!afsd_dynamic_vcaches && afs_vcount >= afs_maxvcount) {
1049         afs_ShakeLooseVCaches(anumber);
1050         if (afs_vcount >= afs_maxvcount) {
1051             afs_warn("afs_NewVCache - none freed\n");
1052             return NULL;
1053         }
1054     }
1055     tvc = afs_AllocVCache();
1056     if (tvc == NULL) {
1057         return NULL;
1058     }
1059 #else /* AFS_LINUX22_ENV */
1060     /* pull out a free cache entry */
1061     if (!freeVCList) {
1062         afs_ShakeLooseVCaches(anumber);
1063     }
1064
1065     if (!freeVCList) {
1066         tvc = afs_AllocVCache();
1067         if (tvc == NULL) {
1068             return NULL;
1069         }
1070     } else {
1071         tvc = freeVCList;       /* take from free list */
1072         freeVCList = tvc->nextfree;
1073         tvc->nextfree = NULL;
1074         afs_vcount++; /* balanced by FlushVCache */
1075     } /* end of if (!freeVCList) */
1076
1077 #endif /* AFS_LINUX22_ENV */
1078
1079 #if defined(AFS_XBSD_ENV) || defined(AFS_DARWIN_ENV)
1080     if (tvc->v)
1081         panic("afs_NewVCache(): free vcache with vnode attached");
1082 #endif
1083
1084     /* Populate the vcache with as much as we can. */
1085     afs_PrePopulateVCache(tvc, afid, serverp);
1086
1087     /* Thread the vcache onto the VLRU */
1088
1089     i = VCHash(afid);
1090     j = VCHashV(afid);
1091
1092     tvc->hnext = afs_vhashT[i];
1093     afs_vhashT[i] = tvc;
1094     QAdd(&afs_vhashTV[j], &tvc->vhashq);
1095
1096     if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
1097         refpanic("NewVCache VLRU inconsistent");
1098     }
1099     QAdd(&VLRU, &tvc->vlruq);   /* put in lruq */
1100     if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
1101         refpanic("NewVCache VLRU inconsistent2");
1102     }
1103     if (tvc->vlruq.next->prev != &(tvc->vlruq)) {
1104         refpanic("NewVCache VLRU inconsistent3");
1105     }
1106     if (tvc->vlruq.prev->next != &(tvc->vlruq)) {
1107         refpanic("NewVCache VLRU inconsistent4");
1108     }
1109     vcachegen++;
1110
1111     /* it should now be safe to drop the xvcache lock - so attach an inode
1112      * to this vcache, where necessary */
1113     osi_AttachVnode(tvc, seq);
1114
1115     /* Get a reference count to hold this vcache for the VLRUQ. Note that
1116      * we have to do this after attaching the vnode, because the reference
1117      * count may be held in the vnode itself */
1118
1119 #if defined(AFS_LINUX22_ENV)
1120     /* Hold it for the LRU (should make count 2) */
1121     osi_Assert(osi_vnhold(tvc) == 0);
1122 #elif !(defined (AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV))
1123     VREFCOUNT_SET(tvc, 1);      /* us */
1124 #endif
1125
1126 #if defined (AFS_FBSD_ENV)
1127     if (tvc->f.states & CVInit)
1128 #endif
1129     afs_PostPopulateVCache(tvc, afid, seq);
1130
1131     return tvc;
1132 }                               /*afs_NewVCache */
1133
1134
1135 struct vcache *
1136 afs_NewVCache(struct VenusFid *afid, struct server *serverp)
1137 {
1138     return afs_NewVCache_int(afid, serverp, 0);
1139 }
1140
1141 struct vcache *
1142 afs_NewBulkVCache(struct VenusFid *afid, struct server *serverp, int seq)
1143 {
1144     return afs_NewVCache_int(afid, serverp, seq);
1145 }
1146
1147 /*!
1148  * ???
1149  *
1150  * LOCK: afs_FlushActiveVcaches afs_xvcache N
1151  *
1152  * \param doflocks : Do we handle flocks?
1153  */
1154 void
1155 afs_FlushActiveVcaches(afs_int32 doflocks)
1156 {
1157     struct vcache *tvc;
1158     int i;
1159     struct afs_conn *tc;
1160     afs_int32 code;
1161     afs_ucred_t *cred = NULL;
1162     struct vrequest *treq = NULL;
1163     struct AFSVolSync tsync;
1164     int didCore;
1165     XSTATS_DECLS;
1166     AFS_STATCNT(afs_FlushActiveVcaches);
1167
1168     code = afs_CreateReq(&treq, afs_osi_credp);
1169     if (code) {
1170         afs_warn("unable to alloc treq\n");
1171         return;
1172     }
1173
1174     ObtainReadLock(&afs_xvcache);
1175     for (i = 0; i < VCSIZE; i++) {
1176         for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
1177             if (tvc->f.states & CVInit) continue;
1178 #ifdef AFS_DARWIN80_ENV
1179             if (tvc->f.states & CDeadVnode &&
1180                 (tvc->f.states & (CCore|CUnlinkedDel) ||
1181                  tvc->flockCount)) panic("Dead vnode has core/unlinkedel/flock");
1182 #endif
1183             if (doflocks && tvc->flockCount != 0) {
1184                 struct rx_connection *rxconn;
1185                 if (osi_vnhold(tvc) != 0) {
1186                     continue;
1187                 }
1188                 /* if this entry has an flock, send a keep-alive call out */
1189                 ReleaseReadLock(&afs_xvcache);
1190                 ObtainWriteLock(&tvc->lock, 51);
1191                 do {
1192                     code = afs_InitReq(treq, afs_osi_credp);
1193                     if (code) {
1194                         code = -1;
1195                         break; /* shutting down: do not try to extend the lock */
1196                     }
1197                     treq->flags |= O_NONBLOCK;
1198
1199                     tc = afs_Conn(&tvc->f.fid, treq, SHARED_LOCK, &rxconn);
1200                     if (tc) {
1201                         XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_EXTENDLOCK);
1202                         RX_AFS_GUNLOCK();
1203                         code =
1204                             RXAFS_ExtendLock(rxconn,
1205                                              (struct AFSFid *)&tvc->f.fid.Fid,
1206                                              &tsync);
1207                         RX_AFS_GLOCK();
1208                         XSTATS_END_TIME;
1209                     } else
1210                         code = -1;
1211                 } while (afs_Analyze
1212                          (tc, rxconn, code, &tvc->f.fid, treq,
1213                           AFS_STATS_FS_RPCIDX_EXTENDLOCK, SHARED_LOCK, NULL));
1214
1215                 ReleaseWriteLock(&tvc->lock);
1216 #ifdef AFS_DARWIN80_ENV
1217                 AFS_FAST_RELE(tvc);
1218                 ObtainReadLock(&afs_xvcache);
1219 #else
1220                 ObtainReadLock(&afs_xvcache);
1221                 AFS_FAST_RELE(tvc);
1222 #endif
1223             }
1224             didCore = 0;
1225             if ((tvc->f.states & CCore) || (tvc->f.states & CUnlinkedDel)) {
1226                 /*
1227                  * Don't let it evaporate in case someone else is in
1228                  * this code.  Also, drop the afs_xvcache lock while
1229                  * getting vcache locks.
1230                  */
1231                 if (osi_vnhold(tvc) != 0) {
1232                     continue;
1233                 }
1234                 ReleaseReadLock(&afs_xvcache);
1235 #if defined(AFS_SGI_ENV)
1236                 /*
1237                  * That's because if we come in via the CUnlinkedDel bit state path we'll be have 0 refcnt
1238                  */
1239                 osi_Assert(VREFCOUNT_GT(tvc,0));
1240                 AFS_RWLOCK((vnode_t *) tvc, VRWLOCK_WRITE);
1241 #endif
1242                 ObtainWriteLock(&tvc->lock, 52);
1243                 if (tvc->f.states & CCore) {
1244                     tvc->f.states &= ~CCore;
1245                     /* XXXX Find better place-holder for cred XXXX */
1246                     cred = (afs_ucred_t *)tvc->linkData;
1247                     tvc->linkData = NULL;       /* XXX */
1248                     code = afs_InitReq(treq, cred);
1249                     afs_Trace2(afs_iclSetp, CM_TRACE_ACTCCORE,
1250                                ICL_TYPE_POINTER, tvc, ICL_TYPE_INT32,
1251                                tvc->execsOrWriters);
1252                     if (!code) {  /* avoid store when shutting down */
1253                         code = afs_StoreOnLastReference(tvc, treq);
1254                     }
1255                     ReleaseWriteLock(&tvc->lock);
1256                     hzero(tvc->flushDV);
1257                     osi_FlushText(tvc);
1258                     didCore = 1;
1259                     if (code && code != VNOVNODE) {
1260                         afs_StoreWarn(code, tvc->f.fid.Fid.Volume,
1261                                       /* /dev/console */ 1);
1262                     }
1263                 } else if (tvc->f.states & CUnlinkedDel) {
1264                     /*
1265                      * Ignore errors
1266                      */
1267                     ReleaseWriteLock(&tvc->lock);
1268 #if defined(AFS_SGI_ENV)
1269                     AFS_RWUNLOCK((vnode_t *) tvc, VRWLOCK_WRITE);
1270 #endif
1271                     afs_remunlink(tvc, 0);
1272 #if defined(AFS_SGI_ENV)
1273                     AFS_RWLOCK((vnode_t *) tvc, VRWLOCK_WRITE);
1274 #endif
1275                 } else {
1276                     /* lost (or won, perhaps) the race condition */
1277                     ReleaseWriteLock(&tvc->lock);
1278                 }
1279 #if defined(AFS_SGI_ENV)
1280                 AFS_RWUNLOCK((vnode_t *) tvc, VRWLOCK_WRITE);
1281 #endif
1282 #ifdef AFS_DARWIN80_ENV
1283                 AFS_FAST_RELE(tvc);
1284                 if (didCore) {
1285                     AFS_RELE(AFSTOV(tvc));
1286                     /* Matches write code setting CCore flag */
1287                     crfree(cred);
1288                 }
1289                 ObtainReadLock(&afs_xvcache);
1290 #else
1291                 ObtainReadLock(&afs_xvcache);
1292                 AFS_FAST_RELE(tvc);
1293                 if (didCore) {
1294                     AFS_RELE(AFSTOV(tvc));
1295                     /* Matches write code setting CCore flag */
1296                     crfree(cred);
1297                 }
1298 #endif
1299             }
1300         }
1301     }
1302     ReleaseReadLock(&afs_xvcache);
1303     afs_DestroyReq(treq);
1304 }
1305
1306
1307
1308 /*!
1309  *   Make sure a cache entry is up-to-date status-wise.
1310  *
1311  * NOTE: everywhere that calls this can potentially be sped up
1312  *       by checking CStatd first, and avoiding doing the InitReq
1313  *       if this is up-to-date.
1314  *
1315  *  Anymore, the only places that call this KNOW already that the
1316  *  vcache is not up-to-date, so we don't screw around.
1317  *
1318  * \param avc  : Ptr to vcache entry to verify.
1319  * \param areq : ???
1320  */
1321
1322 /*!
1323  *
1324  *   Make sure a cache entry is up-to-date status-wise.
1325  *
1326  *   NOTE: everywhere that calls this can potentially be sped up
1327  *       by checking CStatd first, and avoiding doing the InitReq
1328  *       if this is up-to-date.
1329  *
1330  *   Anymore, the only places that call this KNOW already that the
1331  * vcache is not up-to-date, so we don't screw around.
1332  *
1333  * \param avc Pointer to vcache entry to verify.
1334  * \param areq
1335  *
1336  * \return 0 for success or other error codes.
1337  */
1338 int
1339 afs_VerifyVCache2(struct vcache *avc, struct vrequest *areq)
1340 {
1341     struct vcache *tvc;
1342
1343     AFS_STATCNT(afs_VerifyVCache);
1344
1345     /* otherwise we must fetch the status info */
1346
1347     ObtainWriteLock(&avc->lock, 53);
1348     if (avc->f.states & CStatd) {
1349         ReleaseWriteLock(&avc->lock);
1350         return 0;
1351     }
1352     afs_StaleVCacheFlags(avc, AFS_STALEVC_FILENAME | AFS_STALEVC_CLEARCB,
1353                          CUnique);
1354     ReleaseWriteLock(&avc->lock);
1355
1356     /* fetch the status info */
1357     tvc = afs_GetVCache(&avc->f.fid, areq);
1358     if (!tvc)
1359         return EIO;
1360     /* Put it back; caller has already incremented vrefCount */
1361     afs_PutVCache(tvc);
1362     return 0;
1363
1364 }                               /*afs_VerifyVCache */
1365
1366
1367 /*!
1368  * Simple copy of stat info into cache.
1369  *
1370  * Callers:as of 1992-04-29, only called by WriteVCache
1371  *
1372  * \param avc   Ptr to vcache entry involved.
1373  * \param astat Ptr to stat info to copy.
1374  *
1375  */
1376 static void
1377 afs_SimpleVStat(struct vcache *avc,
1378                 struct AFSFetchStatus *astat, struct vrequest *areq)
1379 {
1380     afs_size_t length;
1381     AFS_STATCNT(afs_SimpleVStat);
1382
1383 #ifdef AFS_64BIT_CLIENT
1384         FillInt64(length, astat->Length_hi, astat->Length);
1385 #else /* AFS_64BIT_CLIENT */
1386         length = astat->Length;
1387 #endif /* AFS_64BIT_CLIENT */
1388
1389 #if defined(AFS_SGI_ENV)
1390     if ((avc->execsOrWriters <= 0) && !afs_DirtyPages(avc)
1391         && !AFS_VN_MAPPED((vnode_t *) avc)) {
1392         osi_Assert((valusema(&avc->vc_rwlock) <= 0)
1393                    && (OSI_GET_LOCKID() == avc->vc_rwlockid));
1394         if (length < avc->f.m.Length) {
1395             vnode_t *vp = (vnode_t *) avc;
1396
1397             osi_Assert(WriteLocked(&avc->lock));
1398             ReleaseWriteLock(&avc->lock);
1399             AFS_GUNLOCK();
1400             PTOSSVP(vp, (off_t) length, (off_t) MAXLONG);
1401             AFS_GLOCK();
1402             ObtainWriteLock(&avc->lock, 67);
1403         }
1404     }
1405 #endif
1406
1407     if (!afs_DirtyPages(avc)) {
1408         /* if actively writing the file, don't fetch over this value */
1409         afs_Trace3(afs_iclSetp, CM_TRACE_SIMPLEVSTAT, ICL_TYPE_POINTER, avc,
1410                    ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->f.m.Length),
1411                    ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(length));
1412         avc->f.m.Length = length;
1413         avc->f.m.Date = astat->ClientModTime;
1414     }
1415     avc->f.m.Owner = astat->Owner;
1416     avc->f.m.Group = astat->Group;
1417     avc->f.m.Mode = astat->UnixModeBits;
1418     if (vType(avc) == VREG) {
1419         avc->f.m.Mode |= S_IFREG;
1420     } else if (vType(avc) == VDIR) {
1421         avc->f.m.Mode |= S_IFDIR;
1422     } else if (vType(avc) == VLNK) {
1423         avc->f.m.Mode |= S_IFLNK;
1424         if ((avc->f.m.Mode & 0111) == 0)
1425             avc->mvstat = AFS_MVSTAT_MTPT;
1426     }
1427     if (avc->f.states & CForeign) {
1428         struct axscache *ac;
1429         avc->f.anyAccess = astat->AnonymousAccess;
1430 #ifdef badidea
1431         if ((astat->CallerAccess & ~astat->AnonymousAccess))
1432             /*   USED TO SAY :
1433              * Caller has at least one bit not covered by anonymous, and
1434              * thus may have interesting rights.
1435              *
1436              * HOWEVER, this is a really bad idea, because any access query
1437              * for bits which aren't covered by anonymous, on behalf of a user
1438              * who doesn't have any special rights, will result in an answer of
1439              * the form "I don't know, lets make a FetchStatus RPC and find out!"
1440              * It's an especially bad idea under Ultrix, since (due to the lack of
1441              * a proper access() call) it must perform several afs_access() calls
1442              * in order to create magic mode bits that vary according to who makes
1443              * the call.  In other words, _every_ stat() generates a test for
1444              * writeability...
1445              */
1446 #endif /* badidea */
1447             if (avc->Access && (ac = afs_FindAxs(avc->Access, areq->uid)))
1448                 ac->axess = astat->CallerAccess;
1449             else                /* not found, add a new one if possible */
1450                 afs_AddAxs(avc->Access, areq->uid, astat->CallerAccess);
1451     }
1452
1453 }                               /*afs_SimpleVStat */
1454
1455
1456 /*!
1457  * Store the status info *only* back to the server for a
1458  * fid/vrequest.
1459  *
1460  * Environment: Must be called with a shared lock held on the vnode.
1461  *
1462  * \param avc Ptr to the vcache entry.
1463  * \param astatus Ptr to the status info to store.
1464  * \param areq Ptr to the associated vrequest.
1465  *
1466  * \return Operation status.
1467  */
1468
1469 int
1470 afs_WriteVCache(struct vcache *avc,
1471                 struct AFSStoreStatus *astatus,
1472                 struct vrequest *areq)
1473 {
1474     afs_int32 code;
1475     struct afs_conn *tc;
1476     struct AFSFetchStatus OutStatus;
1477     struct AFSVolSync tsync;
1478     struct rx_connection *rxconn;
1479     XSTATS_DECLS;
1480     AFS_STATCNT(afs_WriteVCache);
1481     afs_Trace2(afs_iclSetp, CM_TRACE_WVCACHE, ICL_TYPE_POINTER, avc,
1482                ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->f.m.Length));
1483     do {
1484         tc = afs_Conn(&avc->f.fid, areq, SHARED_LOCK, &rxconn);
1485         if (tc) {
1486             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_STORESTATUS);
1487             RX_AFS_GUNLOCK();
1488             code =
1489                 RXAFS_StoreStatus(rxconn, (struct AFSFid *)&avc->f.fid.Fid,
1490                                   astatus, &OutStatus, &tsync);
1491             RX_AFS_GLOCK();
1492             XSTATS_END_TIME;
1493         } else
1494             code = -1;
1495     } while (afs_Analyze
1496              (tc, rxconn, code, &avc->f.fid, areq, AFS_STATS_FS_RPCIDX_STORESTATUS,
1497               SHARED_LOCK, NULL));
1498
1499     UpgradeSToWLock(&avc->lock, 20);
1500     if (code == 0) {
1501         /* success, do the changes locally */
1502         afs_SimpleVStat(avc, &OutStatus, areq);
1503         /*
1504          * Update the date, too.  SimpleVStat didn't do this, since
1505          * it thought we were doing this after fetching new status
1506          * over a file being written.
1507          */
1508         avc->f.m.Date = OutStatus.ClientModTime;
1509     } else {
1510         /* failure, set up to check with server next time */
1511         afs_StaleVCacheFlags(avc, 0, CUnique);
1512     }
1513     ConvertWToSLock(&avc->lock);
1514     return code;
1515
1516 }                               /*afs_WriteVCache */
1517
1518 /*!
1519  * Store status info only locally, set the proper disconnection flags
1520  * and add to dirty list.
1521  *
1522  * \param avc The vcache to be written locally.
1523  * \param astatus Get attr fields from local store.
1524  * \param attrs This one is only of the vs_size.
1525  *
1526  * \note Must be called with a shared lock on the vnode
1527  */
1528 int
1529 afs_WriteVCacheDiscon(struct vcache *avc,
1530                       struct AFSStoreStatus *astatus,
1531                       struct vattr *attrs)
1532 {
1533     afs_int32 code = 0;
1534     afs_int32 flags = 0;
1535
1536     UpgradeSToWLock(&avc->lock, 700);
1537
1538     if (!astatus->Mask) {
1539
1540         return code;
1541
1542     } else {
1543
1544         /* Set attributes. */
1545         if (astatus->Mask & AFS_SETMODTIME) {
1546                 avc->f.m.Date = astatus->ClientModTime;
1547                 flags |= VDisconSetTime;
1548         }
1549
1550         if (astatus->Mask & AFS_SETOWNER) {
1551             /* printf("Not allowed yet. \n"); */
1552             /*avc->f.m.Owner = astatus->Owner;*/
1553         }
1554
1555         if (astatus->Mask & AFS_SETGROUP) {
1556             /* printf("Not allowed yet. \n"); */
1557             /*avc->f.m.Group =  astatus->Group;*/
1558         }
1559
1560         if (astatus->Mask & AFS_SETMODE) {
1561                 avc->f.m.Mode = astatus->UnixModeBits;
1562
1563                 flags |= VDisconSetMode;
1564          }              /* if(astatus.Mask & AFS_SETMODE) */
1565
1566      }                  /* if (!astatus->Mask) */
1567
1568      if (attrs->va_size > 0) {
1569         /* XXX: Do I need more checks? */
1570         /* Truncation operation. */
1571         flags |= VDisconTrunc;
1572      }
1573
1574     if (flags)
1575         afs_DisconAddDirty(avc, flags, 1);
1576
1577     /* XXX: How about the rest of the fields? */
1578
1579     ConvertWToSLock(&avc->lock);
1580
1581     return code;
1582 }
1583
1584 /*!
1585  * Copy astat block into vcache info
1586  *
1587  * \note This code may get dataversion and length out of sync if the file has
1588  * been modified.  This is less than ideal.  I haven't thought about it sufficiently
1589  * to be certain that it is adequate.
1590  *
1591  * \note Environment: Must be called under a write lock
1592  *
1593  * \param avc  Ptr to vcache entry.
1594  * \param astat Ptr to stat block to copy in.
1595  * \param areq Ptr to associated request.
1596  */
1597 void
1598 afs_ProcessFS(struct vcache *avc,
1599               struct AFSFetchStatus *astat, struct vrequest *areq)
1600 {
1601     afs_size_t length;
1602     afs_hyper_t newDV;
1603     AFS_STATCNT(afs_ProcessFS);
1604
1605 #ifdef AFS_64BIT_CLIENT
1606     FillInt64(length, astat->Length_hi, astat->Length);
1607 #else /* AFS_64BIT_CLIENT */
1608     length = astat->Length;
1609 #endif /* AFS_64BIT_CLIENT */
1610     /* WARNING: afs_DoBulkStat uses the Length field to store a sequence
1611      * number for each bulk status request. Under no circumstances
1612      * should afs_DoBulkStat store a sequence number if the new
1613      * length will be ignored when afs_ProcessFS is called with
1614      * new stats. If you change the following conditional then you
1615      * also need to change the conditional in afs_DoBulkStat.  */
1616 #ifdef AFS_SGI_ENV
1617     if ((avc->execsOrWriters <= 0) && !afs_DirtyPages(avc)
1618         && !AFS_VN_MAPPED((vnode_t *) avc)) {
1619 #else
1620     if ((avc->execsOrWriters <= 0) && !afs_DirtyPages(avc)) {
1621 #endif
1622         /* if we're writing or mapping this file, don't fetch over these
1623          *  values.
1624          */
1625         afs_Trace3(afs_iclSetp, CM_TRACE_PROCESSFS, ICL_TYPE_POINTER, avc,
1626                    ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(avc->f.m.Length),
1627                    ICL_TYPE_OFFSET, ICL_HANDLE_OFFSET(length));
1628         avc->f.m.Length = length;
1629         avc->f.m.Date = astat->ClientModTime;
1630     }
1631     hset64(newDV, astat->dataVersionHigh, astat->DataVersion);
1632     afs_SetDataVersion(avc, &newDV);
1633     avc->f.m.Owner = astat->Owner;
1634     avc->f.m.Mode = astat->UnixModeBits;
1635     avc->f.m.Group = astat->Group;
1636     avc->f.m.LinkCount = astat->LinkCount;
1637     if (astat->FileType == File) {
1638         vSetType(avc, VREG);
1639         avc->f.m.Mode |= S_IFREG;
1640     } else if (astat->FileType == Directory) {
1641         vSetType(avc, VDIR);
1642         avc->f.m.Mode |= S_IFDIR;
1643     } else if (astat->FileType == SymbolicLink) {
1644         if (afs_fakestat_enable && (avc->f.m.Mode & 0111) == 0) {
1645             vSetType(avc, VDIR);
1646             avc->f.m.Mode |= S_IFDIR;
1647         } else {
1648             vSetType(avc, VLNK);
1649             avc->f.m.Mode |= S_IFLNK;
1650         }
1651         if ((avc->f.m.Mode & 0111) == 0) {
1652             avc->mvstat = AFS_MVSTAT_MTPT;
1653         }
1654     }
1655     avc->f.anyAccess = astat->AnonymousAccess;
1656 #ifdef badidea
1657     if ((astat->CallerAccess & ~astat->AnonymousAccess))
1658         /*   USED TO SAY :
1659          * Caller has at least one bit not covered by anonymous, and
1660          * thus may have interesting rights.
1661          *
1662          * HOWEVER, this is a really bad idea, because any access query
1663          * for bits which aren't covered by anonymous, on behalf of a user
1664          * who doesn't have any special rights, will result in an answer of
1665          * the form "I don't know, lets make a FetchStatus RPC and find out!"
1666          * It's an especially bad idea under Ultrix, since (due to the lack of
1667          * a proper access() call) it must perform several afs_access() calls
1668          * in order to create magic mode bits that vary according to who makes
1669          * the call.  In other words, _every_ stat() generates a test for
1670          * writeability...
1671          */
1672 #endif /* badidea */
1673     {
1674         struct axscache *ac;
1675         if (avc->Access && (ac = afs_FindAxs(avc->Access, areq->uid)))
1676             ac->axess = astat->CallerAccess;
1677         else                    /* not found, add a new one if possible */
1678             afs_AddAxs(avc->Access, areq->uid, astat->CallerAccess);
1679     }
1680 }                               /*afs_ProcessFS */
1681
1682
1683 /*!
1684  * Get fid from server.
1685  *
1686  * \param afid
1687  * \param areq Request to be passed on.
1688  * \param name Name of ?? to lookup.
1689  * \param OutStatus Fetch status.
1690  * \param CallBackp
1691  * \param serverp
1692  * \param tsyncp
1693  *
1694  * \return Success status of operation.
1695  */
1696 int
1697 afs_RemoteLookup(struct VenusFid *afid, struct vrequest *areq,
1698                  char *name, struct VenusFid *nfid,
1699                  struct AFSFetchStatus *OutStatusp,
1700                  struct AFSCallBack *CallBackp, struct server **serverp,
1701                  struct AFSVolSync *tsyncp)
1702 {
1703     afs_int32 code;
1704     struct afs_conn *tc;
1705     struct rx_connection *rxconn;
1706     struct AFSFetchStatus OutDirStatus;
1707     XSTATS_DECLS;
1708     if (!name)
1709         name = "";              /* XXX */
1710     do {
1711         tc = afs_Conn(afid, areq, SHARED_LOCK, &rxconn);
1712         if (tc) {
1713             if (serverp)
1714                 *serverp = tc->parent->srvr->server;
1715             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_XLOOKUP);
1716             RX_AFS_GUNLOCK();
1717             code =
1718                 RXAFS_Lookup(rxconn, (struct AFSFid *)&afid->Fid, name,
1719                              (struct AFSFid *)&nfid->Fid, OutStatusp,
1720                              &OutDirStatus, CallBackp, tsyncp);
1721             RX_AFS_GLOCK();
1722             XSTATS_END_TIME;
1723         } else
1724             code = -1;
1725     } while (afs_Analyze
1726              (tc, rxconn, code, afid, areq, AFS_STATS_FS_RPCIDX_XLOOKUP, SHARED_LOCK,
1727               NULL));
1728
1729     return code;
1730 }
1731
1732
1733 /*!
1734  * afs_GetVCache
1735  *
1736  * Given a file id and a vrequest structure, fetch the status
1737  * information associated with the file.
1738  *
1739  * \param afid File ID.
1740  * \param areq Ptr to associated vrequest structure, specifying the
1741  *  user whose authentication tokens will be used.
1742  *
1743  * \note Environment:
1744  *      The cache entry is returned with an increased vrefCount field.
1745  *      The entry must be discarded by calling afs_PutVCache when you
1746  *      are through using the pointer to the cache entry.
1747  *
1748  *      You should not hold any locks when calling this function, except
1749  *      locks on other vcache entries.  If you lock more than one vcache
1750  *      entry simultaneously, you should lock them in this order:
1751  *
1752  *          1. Lock all files first, then directories.
1753  *          2.  Within a particular type, lock entries in Fid.Vnode order.
1754  *
1755  *      This locking hierarchy is convenient because it allows locking
1756  *      of a parent dir cache entry, given a file (to check its access
1757  *      control list).  It also allows renames to be handled easily by
1758  *      locking directories in a constant order.
1759  *
1760  * \note NB.  NewVCache -> FlushVCache presently (4/10/95) drops the xvcache lock.
1761  */
1762 struct vcache *
1763 afs_GetVCache(struct VenusFid *afid, struct vrequest *areq)
1764 {
1765
1766     afs_int32 code, newvcache = 0;
1767     struct vcache *tvc;
1768     struct volume *tvp;
1769     afs_int32 retry;
1770
1771     AFS_STATCNT(afs_GetVCache);
1772
1773 #if     defined(AFS_SGI_ENV) && !defined(AFS_SGI53_ENV)
1774   loop:
1775 #endif
1776
1777     ObtainSharedLock(&afs_xvcache, 5);
1778
1779     tvc = afs_FindVCache(afid, &retry, DO_STATS | DO_VLRU | IS_SLOCK);
1780     if (tvc && retry) {
1781 #if     defined(AFS_SGI_ENV) && !defined(AFS_SGI53_ENV)
1782         ReleaseSharedLock(&afs_xvcache);
1783         spunlock_psema(tvc->v.v_lock, retry, &tvc->v.v_sync, PINOD);
1784         goto loop;
1785 #endif
1786     }
1787     if (tvc) {
1788         osi_Assert((tvc->f.states & CVInit) == 0);
1789         /* If we are in readdir, return the vnode even if not statd */
1790         if ((tvc->f.states & CStatd) || afs_InReadDir(tvc)) {
1791             ReleaseSharedLock(&afs_xvcache);
1792             return tvc;
1793         }
1794     } else {
1795         UpgradeSToWLock(&afs_xvcache, 21);
1796
1797         /* no cache entry, better grab one */
1798         tvc = afs_NewVCache(afid, NULL);
1799         newvcache = 1;
1800
1801         ConvertWToSLock(&afs_xvcache);
1802         if (tvc == NULL)
1803         {
1804                 ReleaseSharedLock(&afs_xvcache);
1805                 return NULL;
1806         }
1807
1808         afs_stats_cmperf.vcacheMisses++;
1809     }
1810
1811     ReleaseSharedLock(&afs_xvcache);
1812
1813     ObtainWriteLock(&tvc->lock, 54);
1814
1815     if (tvc->f.states & CStatd) {
1816         ReleaseWriteLock(&tvc->lock);
1817         return tvc;
1818     }
1819 #ifdef AFS_DARWIN80_ENV
1820 /* Darwin 8.0 only has bufs in nfs, so we shouldn't have to worry about them.
1821    What about ubc? */
1822 #else
1823 # if defined(AFS_DARWIN_ENV) || defined(AFS_FBSD_ENV)
1824     /*
1825      * XXX - I really don't like this.  Should try to understand better.
1826      * It seems that sometimes, when we get called, we already hold the
1827      * lock on the vnode (e.g., from afs_getattr via afs_VerifyVCache).
1828      * We can't drop the vnode lock, because that could result in a race.
1829      * Sometimes, though, we get here and don't hold the vnode lock.
1830      * I hate code paths that sometimes hold locks and sometimes don't.
1831      * In any event, the dodge we use here is to check whether the vnode
1832      * is locked, and if it isn't, then we gain and drop it around the call
1833      * to vinvalbuf; otherwise, we leave it alone.
1834      */
1835     {
1836         struct vnode *vp = AFSTOV(tvc);
1837         int iheldthelock;
1838
1839 #  if defined(AFS_DARWIN_ENV)
1840         iheldthelock = VOP_ISLOCKED(vp);
1841         if (!iheldthelock)
1842             vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, current_proc());
1843         /* this is messy. we can call fsync which will try to reobtain this */
1844         if (VTOAFS(vp) == tvc)
1845           ReleaseWriteLock(&tvc->lock);
1846         if (UBCINFOEXISTS(vp)) {
1847           vinvalbuf(vp, V_SAVE, &afs_osi_cred, current_proc(), PINOD, 0);
1848         }
1849         if (VTOAFS(vp) == tvc)
1850           ObtainWriteLock(&tvc->lock, 954);
1851         if (!iheldthelock)
1852             VOP_UNLOCK(vp, LK_EXCLUSIVE, current_proc());
1853 #  elif defined(AFS_FBSD_ENV)
1854         AFS_GUNLOCK();
1855         iheldthelock = VOP_ISLOCKED(vp);
1856         if (!iheldthelock) {
1857             vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
1858         }
1859         vinvalbuf(vp, V_SAVE, PINOD, 0); /* changed late in 8.0-CURRENT */
1860         if (!iheldthelock)
1861             VOP_UNLOCK(vp, 0);
1862         AFS_GLOCK();
1863 #  elif defined(AFS_OBSD_ENV)
1864         iheldthelock = VOP_ISLOCKED(vp, curproc);
1865         if (!iheldthelock)
1866             VOP_LOCK(vp, LK_EXCLUSIVE | LK_RETRY, curproc);
1867         uvm_vnp_uncache(vp);
1868         if (!iheldthelock)
1869             VOP_UNLOCK(vp, 0, curproc);
1870 #  elif defined(AFS_NBSD40_ENV)
1871         iheldthelock = VOP_ISLOCKED(vp);
1872         if (!iheldthelock) {
1873             VOP_LOCK(vp, LK_EXCLUSIVE | LK_RETRY);
1874         }
1875         uvm_vnp_uncache(vp);
1876         if (!iheldthelock)
1877             VOP_UNLOCK(vp, 0);
1878 #  endif
1879     }
1880 # endif
1881 #endif
1882
1883     afs_StaleVCacheFlags(tvc, AFS_STALEVC_NODNLC | AFS_STALEVC_CLEARCB,
1884                          CUnique);
1885
1886     /* It is always appropriate to throw away all the access rights? */
1887     afs_FreeAllAxs(&(tvc->Access));
1888     tvp = afs_GetVolume(afid, areq, READ_LOCK); /* copy useful per-volume info */
1889     if (tvp) {
1890         if ((tvp->states & VForeign)) {
1891             if (newvcache)
1892                 tvc->f.states |= CForeign;
1893             if (newvcache && (tvp->rootVnode == afid->Fid.Vnode)
1894                 && (tvp->rootUnique == afid->Fid.Unique)) {
1895                 tvc->mvstat = AFS_MVSTAT_ROOT;
1896             }
1897         }
1898         if (tvp->states & VRO)
1899             tvc->f.states |= CRO;
1900         if (tvp->states & VBackup)
1901             tvc->f.states |= CBackup;
1902         /* now copy ".." entry back out of volume structure, if necessary */
1903         if (tvc->mvstat == AFS_MVSTAT_ROOT && tvp->dotdot.Fid.Volume != 0) {
1904             if (!tvc->mvid.parent)
1905                 tvc->mvid.parent = (struct VenusFid *)
1906                     osi_AllocSmallSpace(sizeof(struct VenusFid));
1907             *tvc->mvid.parent = tvp->dotdot;
1908         }
1909         afs_PutVolume(tvp, READ_LOCK);
1910     }
1911
1912     /* stat the file */
1913     afs_RemoveVCB(afid);
1914     {
1915         struct AFSFetchStatus OutStatus;
1916
1917         if (afs_DynrootNewVnode(tvc, &OutStatus)) {
1918             afs_ProcessFS(tvc, &OutStatus, areq);
1919             tvc->f.states |= CStatd | CUnique;
1920             tvc->f.parent.vnode  = OutStatus.ParentVnode;
1921             tvc->f.parent.unique = OutStatus.ParentUnique;
1922             code = 0;
1923         } else {
1924
1925             if (AFS_IS_DISCONNECTED) {
1926                 /* Nothing to do otherwise...*/
1927                 code = ENETDOWN;
1928                 /* printf("Network is down in afs_GetCache"); */
1929             } else
1930                 code = afs_FetchStatus(tvc, afid, areq, &OutStatus);
1931
1932             /* For the NFS translator's benefit, make sure
1933              * non-directory vnodes always have their parent FID set
1934              * correctly, even when created as a result of decoding an
1935              * NFS filehandle.  It would be nice to also do this for
1936              * directories, but we can't because the fileserver fills
1937              * in the FID of the directory itself instead of that of
1938              * its parent.
1939              */
1940             if (!code && OutStatus.FileType != Directory &&
1941                 !tvc->f.parent.vnode) {
1942                 tvc->f.parent.vnode  = OutStatus.ParentVnode;
1943                 tvc->f.parent.unique = OutStatus.ParentUnique;
1944                 /* XXX - SXW - It's conceivable we should mark ourselves
1945                  *             as dirty again here, incase we've been raced
1946                  *             out of the FetchStatus call.
1947                  */
1948             }
1949         }
1950     }
1951
1952     if (code) {
1953         ReleaseWriteLock(&tvc->lock);
1954
1955         afs_PutVCache(tvc);
1956         return NULL;
1957     }
1958
1959     ReleaseWriteLock(&tvc->lock);
1960     return tvc;
1961
1962 }                               /*afs_GetVCache */
1963
1964
1965
1966 /*!
1967  * Lookup a vcache by fid. Look inside the cache first, if not
1968  * there, lookup the file on the server, and then get it's fresh
1969  * cache entry.
1970  *
1971  * \param afid
1972  * \param areq
1973  * \param adp
1974  * \param aname
1975  *
1976  * \return The found element or NULL.
1977  */
1978 struct vcache *
1979 afs_LookupVCache(struct VenusFid *afid, struct vrequest *areq,
1980                  struct vcache *adp, char *aname)
1981 {
1982     afs_int32 code, now, newvcache = 0;
1983     struct VenusFid nfid;
1984     struct vcache *tvc;
1985     struct volume *tvp;
1986     struct AFSFetchStatus OutStatus;
1987     struct AFSCallBack CallBack;
1988     struct AFSVolSync tsync;
1989     struct server *serverp = 0;
1990     afs_int32 origCBs;
1991     afs_int32 retry;
1992
1993     AFS_STATCNT(afs_GetVCache);
1994
1995 #if     defined(AFS_SGI_ENV) && !defined(AFS_SGI53_ENV)
1996   loop1:
1997 #endif
1998
1999     ObtainReadLock(&afs_xvcache);
2000     tvc = afs_FindVCache(afid, &retry, DO_STATS /* no vlru */ );
2001
2002     if (tvc) {
2003         ReleaseReadLock(&afs_xvcache);
2004         if (retry) {
2005 #if     defined(AFS_SGI_ENV) && !defined(AFS_SGI53_ENV)
2006             spunlock_psema(tvc->v.v_lock, retry, &tvc->v.v_sync, PINOD);
2007             goto loop1;
2008 #endif
2009         }
2010         ObtainReadLock(&tvc->lock);
2011
2012         if (tvc->f.states & CStatd) {
2013             ReleaseReadLock(&tvc->lock);
2014             return tvc;
2015         }
2016         tvc->f.states &= ~CUnique;
2017
2018         ReleaseReadLock(&tvc->lock);
2019         afs_PutVCache(tvc);
2020         ObtainReadLock(&afs_xvcache);
2021     }
2022     /* if (tvc) */
2023     ReleaseReadLock(&afs_xvcache);
2024
2025     /* lookup the file */
2026     nfid = *afid;
2027     now = osi_Time();
2028     origCBs = afs_allCBs;       /* if anything changes, we don't have a cb */
2029
2030     if (AFS_IS_DISCONNECTED) {
2031         /* printf("Network is down in afs_LookupVcache\n"); */
2032         code = ENETDOWN;
2033     } else
2034         code =
2035             afs_RemoteLookup(&adp->f.fid, areq, aname, &nfid, &OutStatus,
2036                              &CallBack, &serverp, &tsync);
2037
2038 #if     defined(AFS_SGI_ENV) && !defined(AFS_SGI53_ENV)
2039   loop2:
2040 #endif
2041
2042     ObtainSharedLock(&afs_xvcache, 6);
2043     tvc = afs_FindVCache(&nfid, &retry, DO_VLRU | IS_SLOCK/* no xstats now */ );
2044     if (tvc && retry) {
2045 #if     defined(AFS_SGI_ENV) && !defined(AFS_SGI53_ENV)
2046         ReleaseSharedLock(&afs_xvcache);
2047         spunlock_psema(tvc->v.v_lock, retry, &tvc->v.v_sync, PINOD);
2048         goto loop2;
2049 #endif
2050     }
2051
2052     if (!tvc) {
2053         /* no cache entry, better grab one */
2054         UpgradeSToWLock(&afs_xvcache, 22);
2055         tvc = afs_NewVCache(&nfid, serverp);
2056         newvcache = 1;
2057         ConvertWToSLock(&afs_xvcache);
2058         if (!tvc)
2059         {
2060                 ReleaseSharedLock(&afs_xvcache);
2061                 return NULL;
2062         }
2063     }
2064
2065     ReleaseSharedLock(&afs_xvcache);
2066     ObtainWriteLock(&tvc->lock, 55);
2067
2068     /* It is always appropriate to throw away all the access rights? */
2069     afs_FreeAllAxs(&(tvc->Access));
2070     tvp = afs_GetVolume(afid, areq, READ_LOCK); /* copy useful per-vol info */
2071     if (tvp) {
2072         if ((tvp->states & VForeign)) {
2073             if (newvcache)
2074                 tvc->f.states |= CForeign;
2075             if (newvcache && (tvp->rootVnode == afid->Fid.Vnode)
2076                 && (tvp->rootUnique == afid->Fid.Unique))
2077                 tvc->mvstat = AFS_MVSTAT_ROOT;
2078         }
2079         if (tvp->states & VRO)
2080             tvc->f.states |= CRO;
2081         if (tvp->states & VBackup)
2082             tvc->f.states |= CBackup;
2083         /* now copy ".." entry back out of volume structure, if necessary */
2084         if (tvc->mvstat == AFS_MVSTAT_ROOT && tvp->dotdot.Fid.Volume != 0) {
2085             if (!tvc->mvid.parent)
2086                 tvc->mvid.parent = (struct VenusFid *)
2087                     osi_AllocSmallSpace(sizeof(struct VenusFid));
2088             *tvc->mvid.parent = tvp->dotdot;
2089         }
2090     }
2091
2092     if (code) {
2093         afs_StaleVCacheFlags(tvc, 0, CUnique);
2094         if (tvp)
2095             afs_PutVolume(tvp, READ_LOCK);
2096         ReleaseWriteLock(&tvc->lock);
2097         afs_PutVCache(tvc);
2098         return NULL;
2099     }
2100
2101     ObtainWriteLock(&afs_xcbhash, 466);
2102     if (origCBs == afs_allCBs) {
2103         if (CallBack.ExpirationTime) {
2104             tvc->callback = serverp;
2105             tvc->cbExpires = CallBack.ExpirationTime + now;
2106             tvc->f.states |= CStatd | CUnique;
2107             tvc->f.states &= ~CBulkFetching;
2108             afs_QueueCallback(tvc, CBHash(CallBack.ExpirationTime), tvp);
2109         } else if (tvc->f.states & CRO) {
2110             /* adapt gives us an hour. */
2111             tvc->cbExpires = 3600 + osi_Time();
2112              /*XXX*/ tvc->f.states |= CStatd | CUnique;
2113             tvc->f.states &= ~CBulkFetching;
2114             afs_QueueCallback(tvc, CBHash(3600), tvp);
2115         } else {
2116             afs_StaleVCacheFlags(tvc,
2117                                  AFS_STALEVC_CBLOCKED | AFS_STALEVC_CLEARCB,
2118                                  CUnique);
2119         }
2120     } else {
2121         afs_StaleVCacheFlags(tvc,
2122                              AFS_STALEVC_CBLOCKED | AFS_STALEVC_CLEARCB,
2123                              CUnique);
2124     }
2125     ReleaseWriteLock(&afs_xcbhash);
2126     if (tvp)
2127         afs_PutVolume(tvp, READ_LOCK);
2128     afs_ProcessFS(tvc, &OutStatus, areq);
2129
2130     ReleaseWriteLock(&tvc->lock);
2131     return tvc;
2132
2133 }
2134
2135 struct vcache *
2136 afs_GetRootVCache(struct VenusFid *afid, struct vrequest *areq,
2137                   struct volume *tvolp)
2138 {
2139     afs_int32 code = 0, i, newvcache = 0, haveStatus = 0;
2140     afs_int32 getNewFid = 0;
2141     afs_uint32 start;
2142     struct VenusFid nfid;
2143     struct vcache *tvc;
2144     struct server *serverp = 0;
2145     struct AFSFetchStatus OutStatus;
2146     struct AFSCallBack CallBack;
2147     struct AFSVolSync tsync;
2148     int origCBs = 0;
2149 #ifdef AFS_DARWIN80_ENV
2150     vnode_t tvp;
2151 #endif
2152
2153     start = osi_Time();
2154
2155   newmtpt:
2156     if (!tvolp->rootVnode || getNewFid) {
2157         struct VenusFid tfid;
2158
2159         tfid = *afid;
2160         tfid.Fid.Vnode = 0;     /* Means get rootfid of volume */
2161         origCBs = afs_allCBs;   /* ignore InitCallBackState */
2162         code =
2163             afs_RemoteLookup(&tfid, areq, NULL, &nfid, &OutStatus, &CallBack,
2164                              &serverp, &tsync);
2165         if (code) {
2166             return NULL;
2167         }
2168 /*      ReleaseReadLock(&tvolp->lock);           */
2169         ObtainWriteLock(&tvolp->lock, 56);
2170         tvolp->rootVnode = afid->Fid.Vnode = nfid.Fid.Vnode;
2171         tvolp->rootUnique = afid->Fid.Unique = nfid.Fid.Unique;
2172         ReleaseWriteLock(&tvolp->lock);
2173 /*      ObtainReadLock(&tvolp->lock);*/
2174         haveStatus = 1;
2175     } else {
2176         afid->Fid.Vnode = tvolp->rootVnode;
2177         afid->Fid.Unique = tvolp->rootUnique;
2178     }
2179
2180  rootvc_loop:
2181     ObtainSharedLock(&afs_xvcache, 7);
2182     i = VCHash(afid);
2183     for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
2184         if (!FidCmp(&(tvc->f.fid), afid)) {
2185             if (tvc->f.states & CVInit) {
2186                 ReleaseSharedLock(&afs_xvcache);
2187                 afs_osi_Sleep(&tvc->f.states);
2188                 goto rootvc_loop;
2189             }
2190 #ifdef AFS_DARWIN80_ENV
2191             if (tvc->f.states & CDeadVnode) {
2192                 ReleaseSharedLock(&afs_xvcache);
2193                 afs_osi_Sleep(&tvc->f.states);
2194                 goto rootvc_loop;
2195             }
2196             tvp = AFSTOV(tvc);
2197             if (vnode_get(tvp))       /* this bumps ref count */
2198                 continue;
2199             if (vnode_ref(tvp)) {
2200                 AFS_GUNLOCK();
2201                 /* AFSTOV(tvc) may be NULL */
2202                 vnode_put(tvp);
2203                 AFS_GLOCK();
2204                 continue;
2205             }
2206 #else
2207             if (osi_vnhold(tvc) != 0) {
2208                 continue;
2209             }
2210 #endif
2211             break;
2212         }
2213     }
2214
2215     if (!haveStatus && (!tvc || !(tvc->f.states & CStatd))) {
2216         /* Mount point no longer stat'd or unknown. FID may have changed. */
2217         getNewFid = 1;
2218 #ifdef AFS_DARWIN80_ENV
2219         ReleaseSharedLock(&afs_xvcache);
2220         if (tvc) {
2221             AFS_GUNLOCK();
2222             vnode_put(AFSTOV(tvc));
2223             vnode_rele(AFSTOV(tvc));
2224             AFS_GLOCK();
2225         }
2226 #else
2227         if (tvc) {
2228             AFS_FAST_RELE(tvc);
2229         }
2230         ReleaseSharedLock(&afs_xvcache);
2231 #endif
2232         tvc = NULL;
2233         goto newmtpt;
2234     }
2235
2236     if (!tvc) {
2237         UpgradeSToWLock(&afs_xvcache, 23);
2238         /* no cache entry, better grab one */
2239         tvc = afs_NewVCache(afid, NULL);
2240         if (!tvc)
2241         {
2242                 ReleaseWriteLock(&afs_xvcache);
2243                 return NULL;
2244         }
2245         newvcache = 1;
2246         afs_stats_cmperf.vcacheMisses++;
2247     } else {
2248         afs_stats_cmperf.vcacheHits++;
2249         UpgradeSToWLock(&afs_xvcache, 24);
2250         if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
2251             refpanic("GRVC VLRU inconsistent0");
2252         }
2253         if (tvc->vlruq.next->prev != &(tvc->vlruq)) {
2254             refpanic("GRVC VLRU inconsistent1");
2255         }
2256         if (tvc->vlruq.prev->next != &(tvc->vlruq)) {
2257             refpanic("GRVC VLRU inconsistent2");
2258         }
2259         QRemove(&tvc->vlruq);   /* move to lruq head */
2260         QAdd(&VLRU, &tvc->vlruq);
2261         if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
2262             refpanic("GRVC VLRU inconsistent3");
2263         }
2264         if (tvc->vlruq.next->prev != &(tvc->vlruq)) {
2265             refpanic("GRVC VLRU inconsistent4");
2266         }
2267         if (tvc->vlruq.prev->next != &(tvc->vlruq)) {
2268             refpanic("GRVC VLRU inconsistent5");
2269         }
2270         vcachegen++;
2271     }
2272
2273     ReleaseWriteLock(&afs_xvcache);
2274
2275     if (tvc->f.states & CStatd) {
2276         return tvc;
2277     } else {
2278
2279         ObtainReadLock(&tvc->lock);
2280         tvc->f.states &= ~CUnique;
2281         tvc->callback = NULL;   /* redundant, perhaps */
2282         ReleaseReadLock(&tvc->lock);
2283     }
2284
2285     ObtainWriteLock(&tvc->lock, 57);
2286
2287     /* It is always appropriate to throw away all the access rights? */
2288     afs_FreeAllAxs(&(tvc->Access));
2289
2290     if (newvcache)
2291         tvc->f.states |= CForeign;
2292     if (tvolp->states & VRO)
2293         tvc->f.states |= CRO;
2294     if (tvolp->states & VBackup)
2295         tvc->f.states |= CBackup;
2296     /* now copy ".." entry back out of volume structure, if necessary */
2297     if (newvcache && (tvolp->rootVnode == afid->Fid.Vnode)
2298         && (tvolp->rootUnique == afid->Fid.Unique)) {
2299         tvc->mvstat = AFS_MVSTAT_ROOT;
2300     }
2301     if (tvc->mvstat == AFS_MVSTAT_ROOT && tvolp->dotdot.Fid.Volume != 0) {
2302         if (!tvc->mvid.parent)
2303             tvc->mvid.parent = (struct VenusFid *)
2304                 osi_AllocSmallSpace(sizeof(struct VenusFid));
2305         *tvc->mvid.parent = tvolp->dotdot;
2306     }
2307
2308     /* stat the file */
2309     afs_RemoveVCB(afid);
2310
2311     if (!haveStatus) {
2312         struct VenusFid tfid;
2313
2314         tfid = *afid;
2315         tfid.Fid.Vnode = 0;     /* Means get rootfid of volume */
2316         origCBs = afs_allCBs;   /* ignore InitCallBackState */
2317         code =
2318             afs_RemoteLookup(&tfid, areq, NULL, &nfid, &OutStatus, &CallBack,
2319                              &serverp, &tsync);
2320     }
2321
2322     if (code) {
2323         afs_StaleVCacheFlags(tvc, AFS_STALEVC_CLEARCB, CUnique);
2324         ReleaseWriteLock(&tvc->lock);
2325         afs_PutVCache(tvc);
2326         return NULL;
2327     }
2328
2329     ObtainWriteLock(&afs_xcbhash, 468);
2330     if (origCBs == afs_allCBs) {
2331         tvc->f.states |= CTruth;
2332         tvc->callback = serverp;
2333         if (CallBack.ExpirationTime != 0) {
2334             tvc->cbExpires = CallBack.ExpirationTime + start;
2335             tvc->f.states |= CStatd;
2336             tvc->f.states &= ~CBulkFetching;
2337             afs_QueueCallback(tvc, CBHash(CallBack.ExpirationTime), tvolp);
2338         } else if (tvc->f.states & CRO) {
2339             /* adapt gives us an hour. */
2340             tvc->cbExpires = 3600 + osi_Time();
2341              /*XXX*/ tvc->f.states |= CStatd;
2342             tvc->f.states &= ~CBulkFetching;
2343             afs_QueueCallback(tvc, CBHash(3600), tvolp);
2344         }
2345     } else {
2346         afs_StaleVCacheFlags(tvc, AFS_STALEVC_CBLOCKED | AFS_STALEVC_CLEARCB,
2347                              CUnique);
2348     }
2349     ReleaseWriteLock(&afs_xcbhash);
2350     afs_ProcessFS(tvc, &OutStatus, areq);
2351
2352     ReleaseWriteLock(&tvc->lock);
2353     return tvc;
2354 }
2355
2356
2357 /*!
2358  * Update callback status and (sometimes) attributes of a vnode.
2359  * Called after doing a fetch status RPC. Whilst disconnected, attributes
2360  * shouldn't be written to the vcache here.
2361  *
2362  * \param avc
2363  * \param afid
2364  * \param areq
2365  * \param Outsp Server status after rpc call.
2366  * \param acb Callback for this vnode.
2367  *
2368  * \note The vcache must be write locked.
2369  */
2370 void
2371 afs_UpdateStatus(struct vcache *avc, struct VenusFid *afid,
2372                  struct vrequest *areq, struct AFSFetchStatus *Outsp,
2373                  struct AFSCallBack *acb, afs_uint32 start)
2374 {
2375     struct volume *volp;
2376
2377     if (!AFS_IN_SYNC)
2378         /* Dont write status in vcache if resyncing after a disconnection. */
2379         afs_ProcessFS(avc, Outsp, areq);
2380
2381     volp = afs_GetVolume(afid, areq, READ_LOCK);
2382     ObtainWriteLock(&afs_xcbhash, 469);
2383     avc->f.states |= CTruth;
2384     if (avc->callback /* check for race */ ) {
2385         if (acb->ExpirationTime != 0) {
2386             avc->cbExpires = acb->ExpirationTime + start;
2387             avc->f.states |= CStatd;
2388             avc->f.states &= ~CBulkFetching;
2389             afs_QueueCallback(avc, CBHash(acb->ExpirationTime), volp);
2390         } else if (avc->f.states & CRO) {
2391             /* ordinary callback on a read-only volume -- AFS 3.2 style */
2392             avc->cbExpires = 3600 + start;
2393             avc->f.states |= CStatd;
2394             avc->f.states &= ~CBulkFetching;
2395             afs_QueueCallback(avc, CBHash(3600), volp);
2396         } else {
2397             afs_StaleVCacheFlags(avc,
2398                                  AFS_STALEVC_CBLOCKED | AFS_STALEVC_CLEARCB,
2399                                  CUnique);
2400         }
2401     } else {
2402         afs_StaleVCacheFlags(avc, AFS_STALEVC_CBLOCKED | AFS_STALEVC_CLEARCB,
2403                              CUnique);
2404     }
2405     ReleaseWriteLock(&afs_xcbhash);
2406     if (volp)
2407         afs_PutVolume(volp, READ_LOCK);
2408 }
2409
2410 void
2411 afs_BadFetchStatus(struct afs_conn *tc)
2412 {
2413     int addr = ntohl(tc->parent->srvr->sa_ip);
2414     afs_warn("afs: Invalid AFSFetchStatus from server %u.%u.%u.%u\n",
2415              (addr >> 24) & 0xff, (addr >> 16) & 0xff, (addr >> 8) & 0xff,
2416              (addr) & 0xff);
2417     afs_warn("afs: This suggests the server may be sending bad data that "
2418              "can lead to availability issues or data corruption. The "
2419              "issue has been avoided for now, but it may not always be "
2420              "detectable. Please upgrade the server if possible.\n");
2421 }
2422
2423 /**
2424  * Check if a given AFSFetchStatus structure is sane.
2425  *
2426  * @param[in] tc The server from which we received the status
2427  * @param[in] status The status we received
2428  *
2429  * @return whether the given structure is valid or not
2430  *  @retval 0 the structure is fine
2431  *  @retval nonzero the structure looks like garbage; act as if we received
2432  *                  the returned error code from the server
2433  */
2434 int
2435 afs_CheckFetchStatus(struct afs_conn *tc, struct AFSFetchStatus *status)
2436 {
2437     if (status->errorCode ||
2438         status->InterfaceVersion != 1 ||
2439         !(status->FileType > Invalid && status->FileType <= SymbolicLink) ||
2440         status->ParentVnode == 0 || status->ParentUnique == 0) {
2441
2442         afs_warn("afs: FetchStatus ec %u iv %u ft %u pv %u pu %u\n",
2443                  (unsigned)status->errorCode, (unsigned)status->InterfaceVersion,
2444                  (unsigned)status->FileType, (unsigned)status->ParentVnode,
2445                  (unsigned)status->ParentUnique);
2446         afs_BadFetchStatus(tc);
2447
2448         return VBUSY;
2449     }
2450     return 0;
2451 }
2452
2453 /*!
2454  * Must be called with avc write-locked
2455  * don't absolutely have to invalidate the hint unless the dv has
2456  * changed, but be sure to get it right else there will be consistency bugs.
2457  */
2458 afs_int32
2459 afs_FetchStatus(struct vcache * avc, struct VenusFid * afid,
2460                 struct vrequest * areq, struct AFSFetchStatus * Outsp)
2461 {
2462     int code;
2463     afs_uint32 start = 0;
2464     struct afs_conn *tc;
2465     struct AFSCallBack CallBack;
2466     struct AFSVolSync tsync;
2467     struct rx_connection *rxconn;
2468     XSTATS_DECLS;
2469     do {
2470         tc = afs_Conn(afid, areq, SHARED_LOCK, &rxconn);
2471         avc->dchint = NULL;     /* invalidate hints */
2472         if (tc) {
2473             avc->callback = tc->parent->srvr->server;
2474             start = osi_Time();
2475             XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHSTATUS);
2476             RX_AFS_GUNLOCK();
2477             code =
2478                 RXAFS_FetchStatus(rxconn, (struct AFSFid *)&afid->Fid, Outsp,
2479                                   &CallBack, &tsync);
2480             RX_AFS_GLOCK();
2481
2482             XSTATS_END_TIME;
2483
2484             if (code == 0) {
2485                 code = afs_CheckFetchStatus(tc, Outsp);
2486             }
2487
2488         } else
2489             code = -1;
2490     } while (afs_Analyze
2491              (tc, rxconn, code, afid, areq, AFS_STATS_FS_RPCIDX_FETCHSTATUS,
2492               SHARED_LOCK, NULL));
2493
2494     if (!code) {
2495         afs_UpdateStatus(avc, afid, areq, Outsp, &CallBack, start);
2496     } else {
2497         /* used to undo the local callback, but that's too extreme.
2498          * There are plenty of good reasons that fetchstatus might return
2499          * an error, such as EPERM.  If we have the vnode cached, statd,
2500          * with callback, might as well keep track of the fact that we
2501          * don't have access...
2502          */
2503         if (code == EPERM || code == EACCES) {
2504             struct axscache *ac;
2505             if (avc->Access && (ac = afs_FindAxs(avc->Access, areq->uid)))
2506                 ac->axess = 0;
2507             else                /* not found, add a new one if possible */
2508                 afs_AddAxs(avc->Access, areq->uid, 0);
2509         }
2510     }
2511     return code;
2512 }
2513
2514 /*!
2515  * Decrements the reference count on a cache entry.
2516  *
2517  * \param avc Pointer to the cache entry to decrement.
2518  *
2519  * \note Environment: Nothing interesting.
2520  */
2521 void
2522 afs_PutVCache(struct vcache *avc)
2523 {
2524     AFS_STATCNT(afs_PutVCache);
2525 #ifdef AFS_DARWIN80_ENV
2526     vnode_put(AFSTOV(avc));
2527     AFS_FAST_RELE(avc);
2528 #else
2529     /*
2530      * Can we use a read lock here?
2531      */
2532     ObtainReadLock(&afs_xvcache);
2533     AFS_FAST_RELE(avc);
2534     ReleaseReadLock(&afs_xvcache);
2535 #endif
2536 }                               /*afs_PutVCache */
2537
2538
2539 /*!
2540  * Reset a vcache entry, so local contents are ignored, and the
2541  * server will be reconsulted next time the vcache is used
2542  *
2543  * \param avc Pointer to the cache entry to reset
2544  * \param acred
2545  * \param skipdnlc  skip the dnlc purge for this vnode
2546  *
2547  * \note avc must be write locked on entry
2548  *
2549  * \note The caller should purge the dnlc when skipdnlc is set.
2550  */
2551 void
2552 afs_ResetVCache(struct vcache *avc, afs_ucred_t *acred, afs_int32 skipdnlc)
2553 {
2554     afs_stalevc_flags_t flags = 0;
2555     if (skipdnlc) {
2556         flags |= AFS_STALEVC_NODNLC;
2557     }
2558
2559     afs_StaleVCacheFlags(avc, flags, CDirty); /* next reference will re-stat */
2560     /* now find the disk cache entries */
2561     afs_TryToSmush(avc, acred, 1);
2562     if (avc->linkData && !(avc->f.states & CCore)) {
2563         afs_osi_Free(avc->linkData, strlen(avc->linkData) + 1);
2564         avc->linkData = NULL;
2565     }
2566 }
2567
2568 /*!
2569  * Sleepa when searching for a vcache. Releases all the pending locks,
2570  * sleeps then obtains the previously released locks.
2571  *
2572  * \param vcache Enter sleep state.
2573  * \param flag Determines what locks to use.
2574  *
2575  * \return
2576  */
2577 static void
2578 findvc_sleep(struct vcache *avc, int flag)
2579 {
2580     if (flag & IS_SLOCK) {
2581             ReleaseSharedLock(&afs_xvcache);
2582     } else {
2583         if (flag & IS_WLOCK) {
2584             ReleaseWriteLock(&afs_xvcache);
2585         } else {
2586             ReleaseReadLock(&afs_xvcache);
2587         }
2588     }
2589     afs_osi_Sleep(&avc->f.states);
2590     if (flag & IS_SLOCK) {
2591             ObtainSharedLock(&afs_xvcache, 341);
2592     } else {
2593         if (flag & IS_WLOCK) {
2594             ObtainWriteLock(&afs_xvcache, 343);
2595         } else {
2596             ObtainReadLock(&afs_xvcache);
2597         }
2598     }
2599 }
2600
2601 /*!
2602  * Add a reference on an existing vcache entry.
2603  *
2604  * \param tvc Pointer to the vcache.
2605  *
2606  * \note Environment: Must be called with at least one reference from
2607  * elsewhere on the vcache, even if that reference will be dropped.
2608  * The global lock is required.
2609  *
2610  * \return 0 on success, -1 on failure.
2611  */
2612
2613 int
2614 afs_RefVCache(struct vcache *tvc)
2615 {
2616 #ifdef AFS_DARWIN80_ENV
2617     vnode_t tvp;
2618 #endif
2619
2620     /* AFS_STATCNT(afs_RefVCache); */
2621
2622 #ifdef  AFS_DARWIN80_ENV
2623     tvp = AFSTOV(tvc);
2624     if (vnode_get(tvp))
2625         return -1;
2626     if (vnode_ref(tvp)) {
2627         AFS_GUNLOCK();
2628         /* AFSTOV(tvc) may be NULL */
2629         vnode_put(tvp);
2630         AFS_GLOCK();
2631         return -1;
2632     }
2633 #else
2634     if (osi_vnhold(tvc) != 0) {
2635         return -1;
2636     }
2637 #endif
2638     return 0;
2639 }                               /*afs_RefVCache */
2640
2641 /*!
2642  * Find a vcache entry given a fid.
2643  *
2644  * \param afid Pointer to the fid whose cache entry we desire.
2645  * \param retry (SGI-specific) tell the caller to drop the lock on xvcache,
2646  *  unlock the vnode, and try again.
2647  * \param flag Bit 1 to specify whether to compute hit statistics.  Not
2648  *  set if FindVCache is called as part of internal bookkeeping.
2649  *
2650  * \note Environment: Must be called with the afs_xvcache lock at least held at
2651  * the read level.  In order to do the VLRU adjustment, the xvcache lock
2652  * must be shared-- we upgrade it here.
2653  */
2654
2655 struct vcache *
2656 afs_FindVCache(struct VenusFid *afid, afs_int32 * retry, afs_int32 flag)
2657 {
2658
2659     struct vcache *tvc;
2660     afs_int32 i;
2661 #ifdef AFS_DARWIN80_ENV
2662     struct vcache *deadvc = NULL, *livevc = NULL;
2663     vnode_t tvp;
2664 #endif
2665
2666     AFS_STATCNT(afs_FindVCache);
2667
2668  findloop:
2669     i = VCHash(afid);
2670     for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
2671         if (FidMatches(afid, tvc)) {
2672             if (tvc->f.states & CVInit) {
2673                 findvc_sleep(tvc, flag);
2674                 goto findloop;
2675             }
2676 #ifdef  AFS_DARWIN80_ENV
2677             if (tvc->f.states & CDeadVnode) {
2678                 findvc_sleep(tvc, flag);
2679                 goto findloop;
2680             }
2681 #endif
2682             break;
2683         }
2684     }
2685
2686     /* should I have a read lock on the vnode here? */
2687     if (tvc) {
2688 #if defined(AFS_DARWIN80_ENV)
2689         tvp = AFSTOV(tvc);
2690         if (vnode_get(tvp))
2691             tvp = NULL;
2692         if (tvp && vnode_ref(tvp)) {
2693             AFS_GUNLOCK();
2694             /* AFSTOV(tvc) may be NULL */
2695             vnode_put(tvp);
2696             AFS_GLOCK();
2697             tvp = NULL;
2698         }
2699         if (!tvp) {
2700             tvc = NULL;
2701             return tvc;
2702         }
2703 #elif defined(AFS_DARWIN_ENV)
2704         tvc->f.states |= CUBCinit;
2705         AFS_GUNLOCK();
2706         if (UBCINFOMISSING(AFSTOV(tvc)) ||
2707             UBCINFORECLAIMED(AFSTOV(tvc))) {
2708           ubc_info_init(AFSTOV(tvc));
2709         }
2710         AFS_GLOCK();
2711         tvc->f.states &= ~CUBCinit;
2712 #else
2713         if (osi_vnhold(tvc) != 0) {
2714             tvc = NULL;
2715         }
2716 #endif
2717     }
2718     if (tvc) {
2719         /*
2720          * only move to front of vlru if we have proper vcache locking)
2721          */
2722         if (flag & DO_VLRU) {
2723             if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
2724                 refpanic("FindVC VLRU inconsistent1");
2725             }
2726             if (tvc->vlruq.next->prev != &(tvc->vlruq)) {
2727                 refpanic("FindVC VLRU inconsistent1");
2728             }
2729             if (tvc->vlruq.prev->next != &(tvc->vlruq)) {
2730                 refpanic("FindVC VLRU inconsistent2");
2731             }
2732             UpgradeSToWLock(&afs_xvcache, 26);
2733             QRemove(&tvc->vlruq);
2734             QAdd(&VLRU, &tvc->vlruq);
2735             ConvertWToSLock(&afs_xvcache);
2736             if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
2737                 refpanic("FindVC VLRU inconsistent1");
2738             }
2739             if (tvc->vlruq.next->prev != &(tvc->vlruq)) {
2740                 refpanic("FindVC VLRU inconsistent2");
2741             }
2742             if (tvc->vlruq.prev->next != &(tvc->vlruq)) {
2743                 refpanic("FindVC VLRU inconsistent3");
2744             }
2745         }
2746         vcachegen++;
2747     }
2748
2749     if (flag & DO_STATS) {
2750         if (tvc)
2751             afs_stats_cmperf.vcacheHits++;
2752         else
2753             afs_stats_cmperf.vcacheMisses++;
2754         if (afs_IsPrimaryCellNum(afid->Cell))
2755             afs_stats_cmperf.vlocalAccesses++;
2756         else
2757             afs_stats_cmperf.vremoteAccesses++;
2758     }
2759     return tvc;
2760 }                               /*afs_FindVCache */
2761
2762 /*!
2763  * Find a vcache entry given a fid. Does a wildcard match on what we
2764  * have for the fid. If more than one entry, don't return anything.
2765  *
2766  * \param avcp Fill in pointer if we found one and only one.
2767  * \param afid Pointer to the fid whose cache entry we desire.
2768  * \param retry (SGI-specific) tell the caller to drop the lock on xvcache,
2769  *             unlock the vnode, and try again.
2770  * \param flags bit 1 to specify whether to compute hit statistics.  Not
2771  *             set if FindVCache is called as part of internal bookkeeping.
2772  *
2773  * \note Environment: Must be called with the afs_xvcache lock at least held at
2774  *  the read level.  In order to do the VLRU adjustment, the xvcache lock
2775  *  must be shared-- we upgrade it here.
2776  *
2777  * \return Number of matches found.
2778  */
2779
2780 int afs_duplicate_nfs_fids = 0;
2781
2782 afs_int32
2783 afs_NFSFindVCache(struct vcache **avcp, struct VenusFid *afid)
2784 {
2785     struct vcache *tvc;
2786     afs_int32 i;
2787     afs_int32 count = 0;
2788     struct vcache *found_tvc = NULL;
2789 #ifdef AFS_DARWIN80_ENV
2790     vnode_t tvp;
2791 #endif
2792
2793     AFS_STATCNT(afs_FindVCache);
2794
2795   loop:
2796
2797     ObtainSharedLock(&afs_xvcache, 331);
2798
2799     i = VCHash(afid);
2800     for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
2801         /* Match only on what we have.... */
2802         if (((tvc->f.fid.Fid.Vnode & 0xffff) == afid->Fid.Vnode)
2803             && (tvc->f.fid.Fid.Volume == afid->Fid.Volume)
2804             && ((tvc->f.fid.Fid.Unique & 0xffffff) == afid->Fid.Unique)
2805             && (tvc->f.fid.Cell == afid->Cell)) {
2806             if (tvc->f.states & CVInit) {
2807                 ReleaseSharedLock(&afs_xvcache);
2808                 afs_osi_Sleep(&tvc->f.states);
2809                 goto loop;
2810             }
2811 #ifdef  AFS_DARWIN80_ENV
2812             if (tvc->f.states & CDeadVnode) {
2813                 ReleaseSharedLock(&afs_xvcache);
2814                 afs_osi_Sleep(&tvc->f.states);
2815                 goto loop;
2816             }
2817             tvp = AFSTOV(tvc);
2818             if (vnode_get(tvp)) {
2819                 /* This vnode no longer exists. */
2820                 continue;
2821             }
2822             if (vnode_ref(tvp)) {
2823                 /* This vnode no longer exists. */
2824                 AFS_GUNLOCK();
2825                 /* AFSTOV(tvc) may be NULL */
2826                 vnode_put(tvp);
2827                 AFS_GLOCK();
2828                 continue;
2829             }
2830 #else
2831             if (osi_vnhold(tvc) != 0) {
2832                 continue;
2833             }
2834 #endif /* AFS_DARWIN80_ENV */
2835             count++;
2836             if (found_tvc) {
2837                 /* Duplicates */
2838                 afs_duplicate_nfs_fids++;
2839 #ifndef AFS_DARWIN80_ENV
2840                 AFS_FAST_RELE(tvc);
2841                 AFS_FAST_RELE(found_tvc);
2842 #endif
2843                 ReleaseSharedLock(&afs_xvcache);
2844 #ifdef AFS_DARWIN80_ENV
2845                 /* Drop our reference counts. */
2846                 vnode_put(AFSTOV(tvc));
2847                 vnode_put(AFSTOV(found_tvc));
2848 #endif
2849                 return count;
2850             }
2851             found_tvc = tvc;
2852         }
2853     }
2854
2855     tvc = found_tvc;
2856     /* should I have a read lock on the vnode here? */
2857     if (tvc) {
2858         /*
2859          * We obtained the xvcache lock above.
2860          */
2861         if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
2862             refpanic("FindVC VLRU inconsistent1");
2863         }
2864         if (tvc->vlruq.next->prev != &(tvc->vlruq)) {
2865             refpanic("FindVC VLRU inconsistent1");
2866         }
2867         if (tvc->vlruq.prev->next != &(tvc->vlruq)) {
2868             refpanic("FindVC VLRU inconsistent2");
2869         }
2870         UpgradeSToWLock(&afs_xvcache, 568);
2871         QRemove(&tvc->vlruq);
2872         QAdd(&VLRU, &tvc->vlruq);
2873         ConvertWToSLock(&afs_xvcache);
2874         if ((VLRU.next->prev != &VLRU) || (VLRU.prev->next != &VLRU)) {
2875             refpanic("FindVC VLRU inconsistent1");
2876         }
2877         if (tvc->vlruq.next->prev != &(tvc->vlruq)) {
2878             refpanic("FindVC VLRU inconsistent2");
2879         }
2880         if (tvc->vlruq.prev->next != &(tvc->vlruq)) {
2881             refpanic("FindVC VLRU inconsistent3");
2882         }
2883     }
2884     vcachegen++;
2885
2886     if (tvc)
2887         afs_stats_cmperf.vcacheHits++;
2888     else
2889         afs_stats_cmperf.vcacheMisses++;
2890     if (afs_IsPrimaryCellNum(afid->Cell))
2891         afs_stats_cmperf.vlocalAccesses++;
2892     else
2893         afs_stats_cmperf.vremoteAccesses++;
2894
2895     *avcp = tvc;                /* May be null */
2896
2897     ReleaseSharedLock(&afs_xvcache);
2898     return (tvc ? 1 : 0);
2899
2900 }                               /*afs_NFSFindVCache */
2901
2902
2903
2904
2905 /*!
2906  * Initialize vcache related variables
2907  *
2908  * \param astatSize
2909  */
2910 void
2911 afs_vcacheInit(int astatSize)
2912 {
2913 #if !defined(AFS_LINUX22_ENV)
2914     struct vcache *tvp;
2915 #endif
2916     int i;
2917     if (!afs_maxvcount) {
2918         afs_maxvcount = astatSize;      /* no particular limit on linux? */
2919     }
2920 #if !defined(AFS_LINUX22_ENV)
2921     freeVCList = NULL;
2922 #endif
2923
2924     AFS_RWLOCK_INIT(&afs_xvcache, "afs_xvcache");
2925     LOCK_INIT(&afs_xvcb, "afs_xvcb");
2926
2927 #if !defined(AFS_LINUX22_ENV)
2928     /* Allocate and thread the struct vcache entries */
2929     tvp = afs_osi_Alloc(astatSize * sizeof(struct vcache));
2930     osi_Assert(tvp != NULL);
2931     memset(tvp, 0, sizeof(struct vcache) * astatSize);
2932
2933     Initial_freeVCList = tvp;
2934     freeVCList = &(tvp[0]);
2935     for (i = 0; i < astatSize - 1; i++) {
2936         tvp[i].nextfree = &(tvp[i + 1]);
2937     }
2938     tvp[astatSize - 1].nextfree = NULL;
2939 # ifdef  KERNEL_HAVE_PIN
2940     pin((char *)tvp, astatSize * sizeof(struct vcache));        /* XXX */
2941 # endif
2942 #endif
2943
2944 #if defined(AFS_SGI_ENV)
2945     for (i = 0; i < astatSize; i++) {
2946         char name[METER_NAMSZ];
2947         struct vcache *tvc = &tvp[i];
2948
2949         tvc->v.v_number = ++afsvnumbers;
2950         tvc->vc_rwlockid = OSI_NO_LOCKID;
2951         initnsema(&tvc->vc_rwlock, 1,
2952                   makesname(name, "vrw", tvc->v.v_number));
2953 # ifndef        AFS_SGI53_ENV
2954         initnsema(&tvc->v.v_sync, 0, makesname(name, "vsy", tvc->v.v_number));
2955 # endif
2956 # ifndef AFS_SGI62_ENV
2957         initnlock(&tvc->v.v_lock, makesname(name, "vlk", tvc->v.v_number));
2958 # endif /* AFS_SGI62_ENV */
2959     }
2960 #endif
2961     QInit(&VLRU);
2962     for(i = 0; i < VCSIZE; ++i)
2963         QInit(&afs_vhashTV[i]);
2964 }
2965
2966 /*!
2967  * Shutdown vcache.
2968  */
2969 void
2970 shutdown_vcache(void)
2971 {
2972     int i;
2973     struct afs_cbr *tsp;
2974     /*
2975      * XXX We may potentially miss some of the vcaches because if when
2976      * there are no free vcache entries and all the vcache entries are active
2977      * ones then we allocate an additional one - admittedly we almost never
2978      * had that occur.
2979      */
2980
2981     {
2982         struct afs_q *tq, *uq = NULL;
2983         struct vcache *tvc;
2984         for (tq = VLRU.prev; tq != &VLRU; tq = uq) {
2985             tvc = QTOV(tq);
2986             uq = QPrev(tq);
2987             if (tvc->mvid.target_root) {
2988                 osi_FreeSmallSpace(tvc->mvid.target_root);
2989                 tvc->mvid.target_root = NULL;
2990             }
2991 #ifdef  AFS_AIX_ENV
2992             aix_gnode_rele(AFSTOV(tvc));
2993 #endif
2994             if (tvc->linkData) {
2995                 afs_osi_Free(tvc->linkData, strlen(tvc->linkData) + 1);
2996                 tvc->linkData = 0;
2997             }
2998         }
2999         /*
3000          * Also free the remaining ones in the Cache
3001          */
3002         for (i = 0; i < VCSIZE; i++) {
3003             for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
3004                 if (tvc->mvid.target_root) {
3005                     osi_FreeSmallSpace(tvc->mvid.target_root);
3006                     tvc->mvid.target_root = NULL;
3007                 }
3008 #ifdef  AFS_AIX_ENV
3009                 if (tvc->v.v_gnode)
3010                     afs_osi_Free(tvc->v.v_gnode, sizeof(struct gnode));
3011 # ifdef AFS_AIX32_ENV
3012                 if (tvc->segid) {
3013                     AFS_GUNLOCK();
3014                     vms_delete(tvc->segid);
3015                     AFS_GLOCK();
3016                     tvc->segid = tvc->vmh = NULL;
3017                     if (VREFCOUNT_GT(tvc,0))
3018                         osi_Panic("flushVcache: vm race");
3019                 }
3020                 if (tvc->credp) {
3021                     crfree(tvc->credp);
3022                     tvc->credp = NULL;
3023                 }
3024 # endif
3025 #endif
3026 #if     defined(AFS_SUN5_ENV)
3027                 if (tvc->credp) {
3028                     crfree(tvc->credp);
3029                     tvc->credp = NULL;
3030                 }
3031 #endif
3032                 if (tvc->linkData) {
3033                     afs_osi_Free(tvc->linkData, strlen(tvc->linkData) + 1);
3034                     tvc->linkData = 0;
3035                 }
3036
3037                 if (tvc->Access)
3038                     afs_FreeAllAxs(&(tvc->Access));
3039             }
3040             afs_vhashT[i] = 0;
3041         }
3042     }
3043     /*
3044      * Free any leftover callback queue
3045      */
3046     for (i = 0; i < afs_stats_cmperf.CallBackAlloced; i++) {
3047         tsp = afs_cbrHeads[i];
3048         afs_cbrHeads[i] = 0;
3049         afs_osi_Free((char *)tsp, AFS_NCBRS * sizeof(struct afs_cbr));
3050     }
3051     afs_cbrSpace = 0;
3052
3053 #if !defined(AFS_LINUX22_ENV)
3054     afs_osi_Free(Initial_freeVCList, afs_cacheStats * sizeof(struct vcache));
3055
3056 # ifdef  KERNEL_HAVE_PIN
3057     unpin(Initial_freeVCList, afs_cacheStats * sizeof(struct vcache));
3058 # endif
3059
3060     freeVCList = Initial_freeVCList = 0;
3061 #endif
3062
3063     AFS_RWLOCK_INIT(&afs_xvcache, "afs_xvcache");
3064     LOCK_INIT(&afs_xvcb, "afs_xvcb");
3065     QInit(&VLRU);
3066     for(i = 0; i < VCSIZE; ++i)
3067         QInit(&afs_vhashTV[i]);
3068 }
3069
3070 void
3071 afs_DisconGiveUpCallbacks(void)
3072 {
3073     int i;
3074     struct vcache *tvc;
3075     int nq=0;
3076
3077     ObtainWriteLock(&afs_xvcache, 1002); /* XXX - should be a unique number */
3078
3079  retry:
3080     /* Somehow, walk the set of vcaches, with each one coming out as tvc */
3081     for (i = 0; i < VCSIZE; i++) {
3082         for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
3083             int slept = 0;
3084             if (afs_QueueVCB(tvc, &slept)) {
3085                 tvc->callback = NULL;
3086                 nq++;
3087             }
3088             if (slept) {
3089                 goto retry;
3090             }
3091         }
3092     }
3093
3094     ReleaseWriteLock(&afs_xvcache);
3095
3096     afs_FlushVCBs(2);
3097 }
3098
3099 /*!
3100  *
3101  * Clear the Statd flag from all vcaches
3102  *
3103  * This function removes the Statd flag from all vcaches. It's used by
3104  * disconnected mode to tidy up during reconnection
3105  *
3106  */
3107 void
3108 afs_ClearAllStatdFlag(void)
3109 {
3110     int i;
3111     struct vcache *tvc;
3112
3113     ObtainWriteLock(&afs_xvcache, 715);
3114
3115     for (i = 0; i < VCSIZE; i++) {
3116         for (tvc = afs_vhashT[i]; tvc; tvc = tvc->hnext) {
3117             afs_StaleVCacheFlags(tvc, AFS_STALEVC_NODNLC | AFS_STALEVC_NOCB,
3118                                  CUnique);
3119         }
3120     }
3121     ReleaseWriteLock(&afs_xvcache);
3122 }
3123
3124 /**
3125  * Mark a vcache as stale; our metadata for the relevant file may be out of
3126  * date.
3127  *
3128  * @post Any subsequent access to this vcache will cause us to fetch the
3129  *       metadata for this vcache again.
3130  */
3131 void
3132 afs_StaleVCacheFlags(struct vcache *avc, afs_stalevc_flags_t flags,
3133                      afs_uint32 cflags)
3134 {
3135     int do_dnlc = 1;
3136     int do_filename = 0;
3137     int do_dequeue = 1;
3138     int lock_cbhash = 1;
3139
3140     if ((flags & AFS_STALEVC_NODNLC)) {
3141         do_dnlc = 0;
3142     }
3143     if ((flags & AFS_STALEVC_FILENAME)) {
3144         do_filename = 1;
3145     }
3146     if ((flags & AFS_STALEVC_CBLOCKED)) {
3147         lock_cbhash = 0;
3148     }
3149     if ((flags & AFS_STALEVC_NOCB)) {
3150         do_dequeue = 0;
3151         lock_cbhash = 0;
3152     }
3153
3154     if (lock_cbhash) {
3155         ObtainWriteLock(&afs_xcbhash, 486);
3156     }
3157     if (do_dequeue) {
3158         afs_DequeueCallback(avc);
3159     }
3160
3161     cflags |= CStatd;
3162     avc->f.states &= ~cflags;
3163
3164     if (lock_cbhash) {
3165         ReleaseWriteLock(&afs_xcbhash);
3166     }
3167
3168     if ((flags & AFS_STALEVC_SKIP_DNLC_FOR_INIT_FLUSHED) &&
3169         (avc->f.states & (CVInit | CVFlushed))) {
3170         do_dnlc = 0;
3171     }
3172
3173     if (flags & AFS_STALEVC_CLEARCB) {
3174         avc->callback = NULL;
3175     }
3176
3177     if (do_dnlc) {
3178         if ((avc->f.fid.Fid.Vnode & 1) ||
3179             AFSTOV(avc) == NULL || vType(avc) == VDIR ||
3180             (avc->f.states & CForeign)) {
3181             /* This vcache is (or could be) a directory. */
3182             osi_dnlc_purgedp(avc);
3183
3184         } else if (do_filename) {
3185             osi_dnlc_purgevp(avc);
3186         }
3187     }
3188 }
3189
3190 void
3191 afs_SetDataVersion(struct vcache *avc, afs_hyper_t *avers)
3192 {
3193     hset(avc->f.m.DataVersion, *avers);
3194 }