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