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