f1839c31e10c09845e1ef586545f6a7db372cfdd
[openafs.git] / src / afs / afs_dcache.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  */
13 #include <afsconfig.h>
14 #include "../afs/param.h"
15
16 RCSID("$Header$");
17
18 #include "../afs/sysincludes.h" /*Standard vendor system headers*/
19 #include "../afs/afsincludes.h" /*AFS-based standard headers*/
20 #include "../afs/afs_stats.h"  /* statistics */
21 #include "../afs/afs_cbqueue.h"
22 #include "../afs/afs_osidnlc.h"
23
24 /* Forward declarations. */
25 static void afs_GetDownD(int anumber, int *aneedSpace);
26 static void afs_FreeDiscardedDCache(void);
27 static void afs_DiscardDCache(struct dcache *);
28
29 /* Imported variables */
30 extern afs_rwlock_t afs_xvcache;
31 extern afs_rwlock_t afs_xcbhash;
32 extern afs_int32 afs_mariner;
33 extern afs_int32 cacheInfoModTime;                      /*Last time cache info modified*/
34
35
36 /*
37  * --------------------- Exported definitions ---------------------
38  */
39 afs_lock_t afs_xdcache;                 /*Lock: alloc new disk cache entries*/
40 afs_int32 afs_freeDCList;                       /*Free list for disk cache entries*/
41 afs_int32 afs_freeDCCount;                      /*Count of elts in freeDCList*/
42 afs_int32 afs_discardDCList;            /*Discarded disk cache entries*/
43 afs_int32 afs_discardDCCount;           /*Count of elts in discardDCList*/
44 struct dcache *afs_freeDSList;          /*Free list for disk slots */
45 struct dcache *afs_Initial_freeDSList;  /*Initial list for above*/
46 ino_t cacheInode;                       /*Inode for CacheItems file*/
47 struct osi_file *afs_cacheInodep = 0;   /* file for CacheItems inode */
48 struct afs_q afs_DLRU;                  /*dcache LRU*/
49 afs_int32 afs_dhashsize = 1024;
50 afs_int32 *afs_dvhashTbl;                       /*Data cache hash table*/
51 afs_int32 *afs_dchashTbl;                       /*Data cache hash table*/
52 afs_int32 *afs_dvnextTbl;                    /*Dcache hash table links */
53 afs_int32 *afs_dcnextTbl;                    /*Dcache hash table links */
54 struct dcache **afs_indexTable;         /*Pointers to dcache entries*/
55 afs_hyper_t *afs_indexTimes;                    /*Dcache entry Access times*/
56 afs_int32 *afs_indexUnique;                  /*dcache entry Fid.Unique */
57 unsigned char *afs_indexFlags;          /*(only one) Is there data there?*/
58 afs_hyper_t afs_indexCounter;                   /*Fake time for marking index
59                                           entries*/
60 afs_int32 afs_cacheFiles =0;            /*Size of afs_indexTable*/
61 afs_int32 afs_cacheBlocks;                      /*1K blocks in cache*/
62 afs_int32 afs_cacheStats;                       /*Stat entries in cache*/
63 afs_int32 afs_blocksUsed;                       /*Number of blocks in use*/
64 afs_int32 afs_blocksDiscarded;          /*Blocks freed but not truncated */
65 afs_int32 afs_fsfragsize = 1023;             /*Underlying Filesystem minimum unit 
66                                          *of disk allocation usually 1K
67                                          *this value is (truefrag -1 ) to
68                                          *save a bunch of subtracts... */
69
70 /* The following is used to ensure that new dcache's aren't obtained when
71  * the cache is nearly full.
72  */
73 int afs_WaitForCacheDrain = 0;
74 int afs_TruncateDaemonRunning = 0;
75 int afs_CacheTooFull = 0;
76
77 afs_int32  afs_dcentries;                       /* In-memory dcache entries */
78
79
80 int dcacheDisabled = 0;
81
82 extern struct dcache *afs_UFSGetDSlot();
83 extern struct volume *afs_UFSGetVolSlot();
84 extern int osi_UFSTruncate(), afs_osi_Read(), afs_osi_Write(), osi_UFSClose();
85 extern int afs_UFSRead(), afs_UFSWrite();
86 static int afs_UFSCacheFetchProc(), afs_UFSCacheStoreProc();
87 extern int afs_UFSHandleLink();
88 struct afs_cacheOps afs_UfsCacheOps = {
89     osi_UFSOpen,
90     osi_UFSTruncate,
91     afs_osi_Read,
92     afs_osi_Write,
93     osi_UFSClose,
94     afs_UFSRead,
95     afs_UFSWrite,
96     afs_UFSCacheFetchProc,
97     afs_UFSCacheStoreProc,
98     afs_UFSGetDSlot,
99     afs_UFSGetVolSlot,
100     afs_UFSHandleLink,
101 };
102
103 extern void *afs_MemCacheOpen();
104 extern struct dcache *afs_MemGetDSlot();
105 extern struct volume *afs_MemGetVolSlot();
106 extern int afs_MemCacheTruncate(), afs_MemReadBlk(), afs_MemWriteBlk(), afs_MemCacheClose();
107 extern int afs_MemRead(), afs_MemWrite(), afs_MemCacheFetchProc(), afs_MemCacheStoreProc();
108 extern int afs_MemHandleLink();
109 struct afs_cacheOps afs_MemCacheOps = {
110     afs_MemCacheOpen,
111     afs_MemCacheTruncate,
112     afs_MemReadBlk,
113     afs_MemWriteBlk,
114     afs_MemCacheClose,
115     afs_MemRead,
116     afs_MemWrite,
117     afs_MemCacheFetchProc,
118     afs_MemCacheStoreProc,
119     afs_MemGetDSlot,
120     afs_MemGetVolSlot,
121     afs_MemHandleLink,
122 };
123
124 int cacheDiskType;                      /*Type of backing disk for cache*/
125 struct afs_cacheOps *afs_cacheType;
126
127
128
129
130 /*
131  * afs_StoreWarn
132  *
133  * Description:
134  *      Warn about failing to store a file.
135  *
136  * Parameters:
137  *      acode   : Associated error code.
138  *      avolume : Volume involved.
139  *      aflags  : How to handle the output:
140  *                      aflags & 1: Print out on console
141  *                      aflags & 2: Print out on controlling tty
142  *
143  * Environment:
144  *      Call this from close call when vnodeops is RCS unlocked.
145  */
146
147 void
148 afs_StoreWarn(acode, avolume, aflags)
149     register afs_int32 acode;
150     afs_int32 avolume;
151     register afs_int32 aflags;
152
153 { /*afs_StoreWarn*/
154
155     static char problem_fmt[] =
156         "afs: failed to store file in volume %d (%s)\n";
157     static char problem_fmt_w_error[] =
158         "afs: failed to store file in volume %d (error %d)\n";
159     static char netproblems[] = "network problems";
160     static char partfull[]    = "partition full";
161     static char overquota[]   = "over quota";
162     static char unknownerr[]  = "unknown error";
163
164     AFS_STATCNT(afs_StoreWarn);
165     if (acode < 0) {
166         /*
167          * Network problems
168          */
169         if (aflags & 1)
170             afs_warn(problem_fmt, avolume, netproblems);
171         if (aflags & 2)
172             afs_warnuser(problem_fmt, avolume, netproblems);
173     }
174     else
175         if (acode == ENOSPC) {
176             /*
177              * Partition full
178              */
179             if (aflags & 1)
180                 afs_warn(problem_fmt, avolume, partfull);
181             if (aflags & 2)
182                 afs_warnuser(problem_fmt, avolume, partfull);
183         }
184         else
185 #ifndef AFS_SUN5_ENV
186             /* EDQUOT doesn't exist on solaris and won't be sent by the server.
187              * Instead ENOSPC will be sent...
188              */
189             if (acode == EDQUOT) {
190                 /*
191                  * Quota exceeded
192                  */
193                 if (aflags & 1)
194                     afs_warn(problem_fmt, avolume, overquota);
195                 if (aflags & 2)
196                     afs_warnuser(problem_fmt, avolume, overquota);
197             } else
198 #endif
199             {
200                 /*
201                  * Unknown error
202                  */
203                 if (aflags & 1)
204                     afs_warn(problem_fmt_w_error, avolume, acode);
205                 if (aflags & 2)
206                     afs_warnuser(problem_fmt_w_error, avolume, acode);
207             }
208 } /*afs_StoreWarn*/
209
210 /* Keep statistics on run time for afs_CacheTruncateDaemon. This is a
211  * struct so we need only export one symbol for AIX.
212  */
213 struct CTD_stats {
214     osi_timeval_t CTD_beforeSleep;
215     osi_timeval_t CTD_afterSleep;
216     osi_timeval_t CTD_sleepTime;
217     osi_timeval_t CTD_runTime;
218     int CTD_nSleeps;
219 } CTD_stats;
220
221 void afs_MaybeWakeupTruncateDaemon() {
222     if (!afs_CacheTooFull && afs_CacheIsTooFull()) {
223         afs_CacheTooFull = 1;
224         if (!afs_TruncateDaemonRunning)
225             afs_osi_Wakeup((char *)afs_CacheTruncateDaemon);
226     } else if (!afs_TruncateDaemonRunning &&
227                afs_blocksDiscarded > CM_MAXDISCARDEDCHUNKS) {
228         afs_osi_Wakeup((char *)afs_CacheTruncateDaemon);
229     }
230 }
231
232 u_int afs_min_cache = 0;
233 void afs_CacheTruncateDaemon() {
234     osi_timeval_t CTD_tmpTime;
235     u_int counter;
236     u_int cb_lowat;
237     u_int dc_hiwat = (100-CM_DCACHECOUNTFREEPCT+CM_DCACHEEXTRAPCT)*afs_cacheFiles/100;
238     afs_min_cache = (((10 * AFS_CHUNKSIZE(0)) + afs_fsfragsize) & ~afs_fsfragsize)>>10;
239
240     osi_GetuTime(&CTD_stats.CTD_afterSleep);
241     afs_TruncateDaemonRunning = 1;
242     while (1) {
243         cb_lowat =  ((CM_DCACHESPACEFREEPCT-CM_DCACHEEXTRAPCT)
244                      * afs_cacheBlocks) / 100;
245         MObtainWriteLock(&afs_xdcache,266);
246         if (afs_CacheTooFull) {
247           int space_needed, slots_needed;
248             /* if we get woken up, we should try to clean something out */
249             for (counter = 0; counter < 10; counter++) {
250                 space_needed = afs_blocksUsed - afs_blocksDiscarded - cb_lowat;
251                 slots_needed = dc_hiwat - afs_freeDCCount - afs_discardDCCount;
252                 afs_GetDownD(slots_needed, &space_needed);      
253                 if ((space_needed <= 0) && (slots_needed <= 0)) {
254                     break;
255                 }
256                 if (afs_termState == AFSOP_STOP_TRUNCDAEMON)
257                     break;
258             }
259             if (!afs_CacheIsTooFull())
260                 afs_CacheTooFull = 0;
261         }
262         MReleaseWriteLock(&afs_xdcache);
263
264         /*
265          * This is a defensive check to try to avoid starving threads
266          * that may need the global lock so thay can help free some
267          * cache space. If this thread won't be sleeping or truncating
268          * any cache files then give up the global lock so other
269          * threads get a chance to run.
270          */
271         if ((afs_termState!=AFSOP_STOP_TRUNCDAEMON) && afs_CacheTooFull &&
272             (!afs_blocksDiscarded || afs_WaitForCacheDrain)) {
273             afs_osi_Wait(100, 0, 0); /* 100 milliseconds */
274         }
275
276         /*
277          * This is where we free the discarded cache elements.
278          */
279         while(afs_blocksDiscarded && !afs_WaitForCacheDrain && 
280               (afs_termState!=AFSOP_STOP_TRUNCDAEMON))
281         {
282             afs_FreeDiscardedDCache();
283         }
284
285         /* See if we need to continue to run. Someone may have
286          * signalled us while we were executing.
287          */
288         if (!afs_WaitForCacheDrain && !afs_CacheTooFull &&
289                         (afs_termState!=AFSOP_STOP_TRUNCDAEMON))
290         {
291             /* Collect statistics on truncate daemon. */
292             CTD_stats.CTD_nSleeps++;
293             osi_GetuTime(&CTD_stats.CTD_beforeSleep);
294             afs_stats_GetDiff(CTD_tmpTime, CTD_stats.CTD_afterSleep,
295                               CTD_stats.CTD_beforeSleep);
296             afs_stats_AddTo(CTD_stats.CTD_runTime, CTD_tmpTime);
297
298             afs_TruncateDaemonRunning = 0;
299             afs_osi_Sleep((char *)afs_CacheTruncateDaemon);  
300             afs_TruncateDaemonRunning = 1;
301
302             osi_GetuTime(&CTD_stats.CTD_afterSleep);
303             afs_stats_GetDiff(CTD_tmpTime, CTD_stats.CTD_beforeSleep,
304                               CTD_stats.CTD_afterSleep);
305             afs_stats_AddTo(CTD_stats.CTD_sleepTime, CTD_tmpTime);
306         }
307         if (afs_termState == AFSOP_STOP_TRUNCDAEMON) {
308 #ifdef AFS_AFSDB_ENV
309             afs_termState = AFSOP_STOP_AFSDB;
310 #else
311             afs_termState = AFSOP_STOP_RXEVENT;
312 #endif
313             afs_osi_Wakeup(&afs_termState);
314             break;
315         }
316     }
317 }
318
319
320 /*
321  * afs_AdjustSize
322  *
323  * Description:
324  *      Make adjustment for the new size in the disk cache entry
325  *
326  * Major Assumptions Here:
327  *      Assumes that frag size is an integral power of two, less one,
328  *      and that this is a two's complement machine.  I don't
329  *      know of any filesystems which violate this assumption...
330  *
331  * Parameters:
332  *      adc      : Ptr to dcache entry.
333  *      anewsize : New size desired.
334  */
335
336 void
337 afs_AdjustSize(adc, anewSize)
338     register struct dcache *adc;
339     register afs_int32 anewSize;
340
341 { /*afs_AdjustSize*/
342
343     register afs_int32 oldSize;
344
345     AFS_STATCNT(afs_AdjustSize);
346     adc->flags |= DFEntryMod;
347     oldSize = ((adc->f.chunkBytes + afs_fsfragsize)^afs_fsfragsize)>>10;/* round up */
348     adc->f.chunkBytes = anewSize;
349     anewSize = ((anewSize + afs_fsfragsize)^afs_fsfragsize)>>10;/* round up */
350     if (anewSize > oldSize) {
351         /* We're growing the file, wakeup the daemon */
352         afs_MaybeWakeupTruncateDaemon();
353     }
354     afs_blocksUsed += (anewSize - oldSize);
355     afs_stats_cmperf.cacheBlocksInUse = afs_blocksUsed; /* XXX */
356
357 } /*afs_AdjustSize*/
358
359
360
361
362
363 /*
364  * afs_GetDownD
365  *
366  * Description:
367  *      This routine is responsible for moving at least one entry (but up
368  *      to some number of them) from the LRU queue to the free queue.
369  *
370  * Parameters:
371  *      anumber    : Number of entries that should ideally be moved.
372  *      aneedSpace : How much space we need (1K blocks);
373  *
374  * Environment:
375  *      The anumber parameter is just a hint; at least one entry MUST be
376  *      moved, of we'll panic.  We must be called with afs_xdcache
377  *      write-locked.  We should try to satisfy both anumber and aneedspace,
378  *      whichever is more demanding - need to do several things:
379  *      1.  only grab up to anumber victims if aneedSpace <= 0, not
380  *          the whole set of MAXATONCE.
381  *      2.  dynamically choose MAXATONCE to reflect severity of
382  *          demand: something like (*aneedSpace >> (logChunk - 9)) 
383  *  N.B. if we're called with aneedSpace <= 0 and anumber > 0, that
384  *  indicates that the cache is not properly configured/tuned or
385  *  something. We should be able to automatically correct that problem.
386  */
387
388 #define MAXATONCE   16          /* max we can obtain at once */
389 static void afs_GetDownD(int anumber, int *aneedSpace)
390 {
391
392     struct dcache *tdc;
393     struct VenusFid *afid;
394     afs_int32 i, j, k;
395     afs_hyper_t vtime;
396     int skip, phase;
397     register struct vcache *tvc;
398     afs_uint32 victims[MAXATONCE];
399     struct dcache *victimDCs[MAXATONCE];
400     afs_hyper_t victimTimes[MAXATONCE];/* youngest (largest LRU time) first */
401     afs_uint32 victimPtr;           /* next free item in victim arrays */
402     afs_hyper_t maxVictimTime;      /* youngest (largest LRU time) victim */
403     afs_uint32 maxVictimPtr;                /* where it is */
404     int discard;
405
406     AFS_STATCNT(afs_GetDownD);
407     if (CheckLock(&afs_xdcache) != -1)
408         osi_Panic("getdownd nolock");
409     /* decrement anumber first for all dudes in free list */
410     /* SHOULD always decrement anumber first, even if aneedSpace >0, 
411      * because we should try to free space even if anumber <=0 */
412     if (!aneedSpace || *aneedSpace <= 0) {
413         anumber -= afs_freeDCCount;
414         if (anumber <= 0) return;       /* enough already free */
415     }
416     /* bounds check parameter */
417     if (anumber > MAXATONCE)
418         anumber = MAXATONCE;   /* all we can do */
419
420     /*
421      * The phase variable manages reclaims.  Set to 0, the first pass,
422      * we don't reclaim active entries.  Set to 1, we reclaim even active
423      * ones.
424      */
425     phase = 0;
426     for (i = 0; i < afs_cacheFiles; i++)
427         /* turn off all flags */
428         afs_indexFlags[i] &= ~IFFlag;
429
430     while (anumber > 0 || (aneedSpace && *aneedSpace >0)) {
431         /* find oldest entries for reclamation */
432         maxVictimPtr = victimPtr = 0;
433         hzero(maxVictimTime);
434         /* select victims from access time array */
435         for (i = 0; i < afs_cacheFiles; i++) {
436             if (afs_indexFlags[i] & (IFDataMod | IFFree | IFDiscarded)) {
437               /* skip if dirty or already free */
438               continue;
439             }
440             tdc = afs_indexTable[i];
441             if (tdc && (tdc->refCount != 0)) {
442               /* Referenced; can't use it! */
443               continue;
444             }
445             hset(vtime, afs_indexTimes[i]);
446
447             /* if we've already looked at this one, skip it */
448             if (afs_indexFlags[i] & IFFlag) continue;
449
450             if (victimPtr < MAXATONCE) {
451                 /* if there's at least one free victim slot left */
452                 victims[victimPtr] = i;
453                 hset(victimTimes[victimPtr], vtime);
454                 if (hcmp(vtime, maxVictimTime) > 0) {
455                     hset(maxVictimTime, vtime);
456                     maxVictimPtr = victimPtr;
457                 }
458                 victimPtr++;
459             }
460             else if (hcmp(vtime, maxVictimTime) < 0) {
461                 /*
462                  * We're older than youngest victim, so we replace at
463                  * least one victim
464                  */
465                 /* find youngest (largest LRU) victim */
466                 j = maxVictimPtr;
467                 if (j == victimPtr) osi_Panic("getdownd local");
468                 victims[j] = i;
469                 hset(victimTimes[j], vtime);
470                 /* recompute maxVictimTime */
471                 hset(maxVictimTime, vtime);
472                 for(j = 0; j < victimPtr; j++)
473                     if (hcmp(maxVictimTime, victimTimes[j]) < 0) {
474                         hset(maxVictimTime, victimTimes[j]);
475                         maxVictimPtr = j;
476                     }
477             }
478         } /* big for loop */
479
480         /* now really reclaim the victims */
481         j = 0;  /* flag to track if we actually got any of the victims */
482         /* first, hold all the victims, since we're going to release the lock
483          * during the truncate operation.
484          */
485         for(i=0; i < victimPtr; i++)
486             victimDCs[i] = afs_GetDSlot(victims[i], 0);
487         for(i = 0; i < victimPtr; i++) {
488             /* q is first elt in dcache entry */
489             tdc = victimDCs[i];
490             /* now, since we're dropping the afs_xdcache lock below, we
491              * have to verify, before proceeding, that there are no other
492              * references to this dcache entry, even now.  Note that we
493              * compare with 1, since we bumped it above when we called
494              * afs_GetDSlot to preserve the entry's identity.
495              */
496             if (tdc->refCount == 1) {
497                 unsigned char chunkFlags;
498                 afid = &tdc->f.fid;
499                 /* xdcache is lower than the xvcache lock */
500                 MReleaseWriteLock(&afs_xdcache);
501                 MObtainReadLock(&afs_xvcache);
502                 tvc = afs_FindVCache(afid, 0,0, 0, 0 /* no stats, no vlru */ );
503                 MReleaseReadLock(&afs_xvcache);
504                 MObtainWriteLock(&afs_xdcache, 527);
505                 skip = 0;
506                 if (tdc->refCount > 1) skip = 1;
507                 if (tvc) {
508                     chunkFlags = afs_indexFlags[tdc->index];
509                     if (phase == 0 && osi_Active(tvc)) skip = 1;
510                     if (phase > 0 && osi_Active(tvc) && (tvc->states & CDCLock)
511                                 && (chunkFlags & IFAnyPages)) skip = 1;
512                     if (chunkFlags & IFDataMod) skip = 1;
513                     afs_Trace4(afs_iclSetp, CM_TRACE_GETDOWND,
514                                ICL_TYPE_POINTER, tvc, ICL_TYPE_INT32, skip,
515                                ICL_TYPE_INT32,
516                                (afs_int32)(chunkFlags & IFDirtyPages),
517                                ICL_TYPE_INT32, AFS_CHUNKTOBASE(tdc->f.chunk));
518
519 #if     defined(AFS_SUN5_ENV)
520                     /*
521                      * Now we try to invalidate pages.  We do this only for
522                      * Solaris.  For other platforms, it's OK to recycle a
523                      * dcache entry out from under a page, because the strategy
524                      * function can call afs_GetDCache().
525                      */
526                     if (!skip && (chunkFlags & IFAnyPages)) {
527                         int code;
528
529                         MReleaseWriteLock(&afs_xdcache);
530                         MObtainWriteLock(&tvc->vlock, 543);
531                         if (tvc->multiPage) {
532                             skip = 1;
533                             goto endmultipage;
534                         }
535                         /* block locking pages */
536                         tvc->vstates |= VPageCleaning;
537                         /* block getting new pages */
538                         tvc->activeV++;
539                         MReleaseWriteLock(&tvc->vlock);
540                         /* One last recheck */
541                         MObtainWriteLock(&afs_xdcache, 333);
542                         chunkFlags = afs_indexFlags[tdc->index];
543                         if (tdc->refCount > 1
544                             || (chunkFlags & IFDataMod)
545                             || (osi_Active(tvc) && (tvc->states & CDCLock)
546                                 && (chunkFlags & IFAnyPages))) {
547                             skip = 1;
548                             MReleaseWriteLock(&afs_xdcache);
549                             goto endputpage;
550                         }
551                         MReleaseWriteLock(&afs_xdcache);
552
553                         code = osi_VM_GetDownD(tvc, tdc);
554
555                         MObtainWriteLock(&afs_xdcache,269);
556                         /* we actually removed all pages, clean and dirty */
557                         if (code == 0) {
558                             afs_indexFlags[tdc->index] &= ~(IFDirtyPages| IFAnyPages);
559                         } else
560                             skip = 1;
561                         MReleaseWriteLock(&afs_xdcache);
562 endputpage:
563                         MObtainWriteLock(&tvc->vlock, 544);
564                         if (--tvc->activeV == 0 && (tvc->vstates & VRevokeWait)) {
565                             tvc->vstates &= ~VRevokeWait;
566                             afs_osi_Wakeup((char *)&tvc->vstates);
567
568                         }
569                         if (tvc->vstates & VPageCleaning) {
570                             tvc->vstates &= ~VPageCleaning;
571                             afs_osi_Wakeup((char *)&tvc->vstates);
572                         }
573 endmultipage:
574                         MReleaseWriteLock(&tvc->vlock);
575                     } else
576 #endif  /* AFS_SUN5_ENV */
577                     {
578                         MReleaseWriteLock(&afs_xdcache);
579                     }
580
581                     AFS_FAST_RELE(tvc);
582                     MObtainWriteLock(&afs_xdcache, 528);
583                     if (afs_indexFlags[tdc->index] &
584                          (IFDataMod | IFDirtyPages | IFAnyPages)) skip = 1;
585                     if (tdc->refCount > 1) skip = 1;
586                 }
587 #if     defined(AFS_SUN5_ENV)
588                 else {
589                     /* no vnode, so IFDirtyPages is spurious (we don't
590                      * sweep dcaches on vnode recycling, so we can have
591                      * DIRTYPAGES set even when all pages are gone).  Just
592                      * clear the flag.
593                      * Hold vcache lock to prevent vnode from being
594                      * created while we're clearing IFDirtyPages.
595                      */
596                     afs_indexFlags[tdc->index] &= ~(IFDirtyPages | IFAnyPages);
597                 }
598 #endif
599                 if (skip) {
600                     /* skip this guy and mark him as recently used */
601                     afs_indexFlags[tdc->index] |= IFFlag;
602                 }
603                 else {
604                     /* flush this dude from the data cache and reclaim;
605                      * first, make sure no one will care that we damage
606                      * it, by removing it from all hash tables.  Then,
607                      * melt it down for parts.  Note that any concurrent
608                      * (new possibility!) calls to GetDownD won't touch
609                      * this guy because his reference count is > 0. */
610 #ifndef AFS_DEC_ENV
611                     AFS_STATCNT(afs_gget);
612 #endif
613                     afs_HashOutDCache(tdc);
614                     if (tdc->f.chunkBytes != 0) {
615                         discard = 1;
616                         if (aneedSpace)
617                           *aneedSpace -= (tdc->f.chunkBytes + afs_fsfragsize) >> 10;
618                     } else {
619                         discard = 0;
620                     }
621                     if (discard) {
622                         afs_DiscardDCache(tdc);
623                     } else {
624                         afs_FreeDCache(tdc);
625                     }
626                     anumber--;
627                     j = 1;      /* we reclaimed at least one victim */
628                 }
629             }
630 #ifdef  AFS_SUN5_ENVX
631             afs_PutDCache(tdc);
632 #else
633             tdc->refCount--;            /* put it back */
634 #endif
635         }
636         
637         if (phase == 0) {
638             /* Phase is 0 and no one was found, so try phase 1 (ignore
639              * osi_Active flag) */
640             if (j == 0) {
641                 phase = 1;
642                 for (i = 0; i < afs_cacheFiles; i++)
643                     /* turn off all flags */
644                     afs_indexFlags[i] &= ~IFFlag;
645             }
646         }
647         else {
648             /* found no one in phase 1, we're hosed */
649             if (victimPtr == 0) break;
650         }
651     } /* big while loop */
652     return;
653
654 } /*afs_GetDownD*/
655
656
657 /*
658  * Description: remove adc from any hash tables that would allow it to be located
659  * again by afs_FindDCache or afs_GetDCache.
660  *
661  * Parameters: adc -- pointer to dcache entry to remove from hash tables.
662  *
663  * Locks: Must have the afs_xdcache lock write-locked to call this function.
664  */
665 afs_HashOutDCache(adc)
666     struct dcache *adc;
667
668 { /*afs_HashOutDCache*/
669
670     int i, us;
671
672 #ifndef AFS_DEC_ENV
673     AFS_STATCNT(afs_glink);
674 #endif
675     /* we know this guy's in the LRUQ.  We'll move dude into DCQ below */
676     DZap(&adc->f.inode);
677     /* if this guy is in the hash table, pull him out */
678     if (adc->f.fid.Fid.Volume != 0) {
679         /* remove entry from first hash chains */
680         i = DCHash(&adc->f.fid, adc->f.chunk);
681         us = afs_dchashTbl[i];
682         if (us == adc->index) {
683             /* first dude in the list */
684             afs_dchashTbl[i] = afs_dcnextTbl[adc->index];
685         }
686         else {
687             /* somewhere on the chain */
688             while (us != NULLIDX) {
689                 if (afs_dcnextTbl[us] == adc->index) {
690                     /* found item pointing at the one to delete */
691                     afs_dcnextTbl[us] = afs_dcnextTbl[adc->index];
692                     break;
693                 }
694                 us = afs_dcnextTbl[us];
695             }
696             if (us == NULLIDX) osi_Panic("dcache hc");
697         }
698         /* remove entry from *other* hash chain */
699         i = DVHash(&adc->f.fid);
700         us = afs_dvhashTbl[i];
701         if (us == adc->index) {
702             /* first dude in the list */
703             afs_dvhashTbl[i] = afs_dvnextTbl[adc->index];
704         }
705         else {
706             /* somewhere on the chain */
707             while (us != NULLIDX) {
708                 if (afs_dvnextTbl[us] == adc->index) {
709                     /* found item pointing at the one to delete */
710                     afs_dvnextTbl[us] = afs_dvnextTbl[adc->index];
711                     break;
712                 }
713                 us = afs_dvnextTbl[us];
714             }
715             if (us == NULLIDX) osi_Panic("dcache hv");
716         }
717     }
718
719     /* prevent entry from being found on a reboot (it is already out of
720      * the hash table, but after a crash, we just look at fid fields of
721      * stable (old) entries).
722      */
723     adc->f.fid.Fid.Volume = 0;  /* invalid */
724
725     /* mark entry as modified */
726     adc->flags |= DFEntryMod;
727
728     /* all done */
729     return 0;
730 } /*afs_HashOutDCache */
731
732
733 /*
734  * afs_FlushDCache
735  *
736  * Description:
737  *      Flush the given dcache entry, pulling it from hash chains
738  *      and truncating the associated cache file.
739  *
740  * Arguments:
741  *      adc: Ptr to dcache entry to flush.
742  *
743  * Environment:
744  *      This routine must be called with the afs_xdcache lock held
745  *      (in write mode)
746  */
747
748 void
749 afs_FlushDCache(adc)
750 register struct dcache *adc;
751 { /*afs_FlushDCache*/
752
753     AFS_STATCNT(afs_FlushDCache);
754     /*
755      * Bump the number of cache files flushed.
756      */
757     afs_stats_cmperf.cacheFlushes++;
758
759     /* remove from all hash tables */
760     afs_HashOutDCache(adc);
761
762     /* Free its space; special case null operation, since truncate operation
763      * in UFS is slow even in this case, and this allows us to pre-truncate
764      * these files at more convenient times with fewer locks set
765      * (see afs_GetDownD).
766      */
767     if (adc->f.chunkBytes != 0) {
768         afs_DiscardDCache(adc);
769         afs_MaybeWakeupTruncateDaemon();
770     } else {
771         afs_FreeDCache(adc);
772     }
773
774     if (afs_WaitForCacheDrain) {
775         if (afs_blocksUsed <=
776             (CM_CACHESIZEDRAINEDPCT*afs_cacheBlocks)/100) {
777             afs_WaitForCacheDrain = 0;
778             afs_osi_Wakeup(&afs_WaitForCacheDrain);
779         }
780     }
781 } /*afs_FlushDCache*/
782
783
784 /*
785  * afs_FreeDCache
786  *
787  * Description: put a dcache entry on the free dcache entry list.
788  *
789  * Parameters: adc -- dcache entry to free
790  *
791  * Environment: called with afs_xdcache lock write-locked.
792  */
793 afs_FreeDCache(adc)
794 register struct dcache *adc; {
795     /* Thread on free list, update free list count and mark entry as
796      * freed in its indexFlags element.  Also, ensure DCache entry gets
797      * written out (set DFEntryMod).
798      */
799
800     afs_dvnextTbl[adc->index] = afs_freeDCList;
801     afs_freeDCList = adc->index;
802     afs_freeDCCount++;
803     afs_indexFlags[adc->index] |= IFFree;
804     adc->flags |= DFEntryMod;
805
806     if (afs_WaitForCacheDrain) {
807         if ((afs_blocksUsed - afs_blocksDiscarded) <=
808             (CM_CACHESIZEDRAINEDPCT*afs_cacheBlocks)/100) {
809             afs_WaitForCacheDrain = 0;
810             afs_osi_Wakeup(&afs_WaitForCacheDrain);
811         }
812     }
813     return 0;
814 }
815
816 /*
817  * afs_DiscardDCache
818  *
819  * Description:
820  *      Discard the cache element by moving it to the discardDCList.
821  *      This puts the cache element into a quasi-freed state, where
822  *      the space may be reused, but the file has not been truncated.
823  *
824  * Major Assumptions Here:
825  *      Assumes that frag size is an integral power of two, less one,
826  *      and that this is a two's complement machine.  I don't
827  *      know of any filesystems which violate this assumption...
828  *
829  * Parameters:
830  *      adc      : Ptr to dcache entry.
831  */
832
833 static void
834 afs_DiscardDCache(adc)
835     register struct dcache *adc;
836
837 { /*afs_DiscardDCache*/
838
839     register afs_int32 size;
840
841     AFS_STATCNT(afs_DiscardDCache);
842     size = ((adc->f.chunkBytes + afs_fsfragsize)^afs_fsfragsize)>>10;/* round up */
843     afs_blocksDiscarded += size;
844     afs_stats_cmperf.cacheBlocksDiscarded = afs_blocksDiscarded;
845
846     afs_dvnextTbl[adc->index] = afs_discardDCList;
847     afs_discardDCList = adc->index;
848     afs_discardDCCount++;
849
850     adc->f.fid.Fid.Volume = 0;
851     adc->flags |= DFEntryMod;
852     afs_indexFlags[adc->index] |= IFDiscarded;
853
854     if (afs_WaitForCacheDrain) {
855         if ((afs_blocksUsed - afs_blocksDiscarded) <=
856             (CM_CACHESIZEDRAINEDPCT*afs_cacheBlocks)/100) {
857             afs_WaitForCacheDrain = 0;
858             afs_osi_Wakeup(&afs_WaitForCacheDrain);
859         }
860     }
861
862 } /*afs_DiscardDCache*/
863
864 /*
865  * afs_FreeDiscardedDCache
866  *
867  * Description:
868  *     Free the next element on the list of discarded cache elements.
869  */
870 static void
871 afs_FreeDiscardedDCache()
872 {
873     register struct dcache *tdc;
874     register struct osi_file *tfile; 
875     register afs_int32 size;
876
877     AFS_STATCNT(afs_FreeDiscardedDCache);
878
879     MObtainWriteLock(&afs_xdcache,510);
880     if (!afs_blocksDiscarded) {
881         MReleaseWriteLock(&afs_xdcache);
882         return;
883     }
884
885     /*
886      * Get an entry from the list of discarded cache elements
887      */
888     tdc = afs_GetDSlot(afs_discardDCList, 0);
889     afs_discardDCList = afs_dvnextTbl[tdc->index];
890     afs_dvnextTbl[tdc->index] = NULLIDX;
891     afs_discardDCCount--;
892     size = ((tdc->f.chunkBytes + afs_fsfragsize)^afs_fsfragsize)>>10;/* round up */
893     afs_blocksDiscarded -= size;
894     afs_stats_cmperf.cacheBlocksDiscarded = afs_blocksDiscarded;
895     MReleaseWriteLock(&afs_xdcache);
896
897     /*
898      * Truncate the element to reclaim its space
899      */
900     tfile = afs_CFileOpen(tdc->f.inode);
901     afs_CFileTruncate(tfile, 0);
902     afs_CFileClose(tfile);
903     afs_AdjustSize(tdc, 0);
904
905     /*
906      * Free the element we just truncated
907      */
908     MObtainWriteLock(&afs_xdcache,511);
909     afs_indexFlags[tdc->index] &= ~IFDiscarded;
910     afs_FreeDCache(tdc);
911     tdc->refCount--;
912     MReleaseWriteLock(&afs_xdcache);
913 }
914
915 /*
916  * afs_MaybeFreeDiscardedDCache
917  *
918  * Description:
919  *      Free as many entries from the list of discarded cache elements
920  *      as we need to get the free space down below CM_WAITFORDRAINPCT (98%).
921  *
922  * Parameters:
923  *      None
924  */
925 afs_MaybeFreeDiscardedDCache()
926 {
927
928     AFS_STATCNT(afs_MaybeFreeDiscardedDCache);
929
930     while (afs_blocksDiscarded &&
931            (afs_blocksUsed > (CM_WAITFORDRAINPCT*afs_cacheBlocks)/100)) {
932         afs_FreeDiscardedDCache();
933     }
934     return 0;
935 }
936
937 /*
938  * afs_GetDownDSlot
939  *
940  * Description:
941  *      Try to free up a certain number of disk slots.
942  *
943  * Parameters:
944  *      anumber : Targeted number of disk slots to free up.
945  */
946 #if defined(AFS_SGI_ENV) && defined(AFS_SGI_SHORTSTACK)
947 extern SV_TYPE afs_sgibksync;
948 extern SV_TYPE afs_sgibkwait;
949 extern lock_t afs_sgibklock;
950 extern struct dcache *afs_sgibklist;
951 #endif
952
953 static void
954 afs_GetDownDSlot(anumber)
955     int anumber;
956
957 { /*afs_GetDownDSlot*/
958
959     struct afs_q *tq, *nq;
960     struct dcache *tdc;
961     int ix;
962     unsigned int i=0;
963     unsigned int cnt;
964
965     AFS_STATCNT(afs_GetDownDSlot);
966     if (cacheDiskType == AFS_FCACHE_TYPE_MEM)
967         osi_Panic("diskless getdowndslot");
968
969     if (CheckLock(&afs_xdcache) != -1)
970         osi_Panic("getdowndslot nolock");
971
972     /* decrement anumber first for all dudes in free list */
973     for(tdc = afs_freeDSList; tdc; tdc = (struct dcache *)tdc->lruq.next)
974         anumber--;
975     if (anumber <= 0)
976         return;                         /* enough already free */
977
978     for(cnt=0, tq = afs_DLRU.prev; tq != &afs_DLRU && anumber > 0;
979         tq = nq, cnt++) {
980         tdc = (struct dcache *) tq;     /* q is first elt in dcache entry */
981         nq = QPrev(tq); /* in case we remove it */
982         if (tdc->refCount == 0) {
983             if ((ix=tdc->index) == NULLIDX) osi_Panic("getdowndslot");
984             /* pull the entry out of the lruq and put it on the free list */
985             QRemove(&tdc->lruq);
986
987             /* write-through if modified */
988             if (tdc->flags & DFEntryMod) {
989 #if defined(AFS_SGI_ENV) && defined(AFS_SGI_SHORTSTACK)
990                 /*
991                  * ask proxy to do this for us - we don't have the stack space
992                  */
993                 while (tdc->flags & DFEntryMod) {
994                     int s;
995                     AFS_GUNLOCK();
996                     s = SPLOCK(afs_sgibklock);
997                     if (afs_sgibklist == NULL) {
998                         /* if slot is free, grab it. */
999                         afs_sgibklist = tdc;
1000                         SV_SIGNAL(&afs_sgibksync);
1001                     }
1002                     /* wait for daemon to (start, then) finish. */
1003                     SP_WAIT(afs_sgibklock, s, &afs_sgibkwait, PINOD);
1004                     AFS_GLOCK();
1005                 }
1006 #else
1007                 tdc->flags &= ~DFEntryMod;
1008                 afs_WriteDCache(tdc, 1);
1009 #endif
1010             }
1011
1012             tdc->stamp = 0;
1013 #ifdef IHINT
1014              if (tdc->ihint) {
1015                  struct osi_file * f = (struct osi_file *)tdc->ihint;
1016                  tdc->ihint = 0;
1017                  afs_UFSClose(f);
1018                  nihints--;
1019              }
1020 #endif /* IHINT */
1021
1022
1023             /* finally put the entry in the free list */
1024             afs_indexTable[ix] = (struct dcache *) 0;
1025             afs_indexFlags[ix] &= ~IFEverUsed;
1026             tdc->index = NULLIDX;
1027             tdc->lruq.next = (struct afs_q *) afs_freeDSList;
1028             afs_freeDSList = tdc;
1029             anumber--;
1030         }
1031     }
1032 } /*afs_GetDownDSlot*/
1033
1034
1035
1036 /*
1037  * afs_PutDCache
1038  *
1039  * Description:
1040  *      Decrement the reference count on a disk cache entry.
1041  *
1042  * Parameters:
1043  *      ad : Ptr to the dcache entry to decrement.
1044  *
1045  * Environment:
1046  *      Nothing interesting.
1047  */
1048 afs_PutDCache(ad)
1049     register struct dcache *ad; 
1050
1051 { /*afs_PutDCache*/
1052     AFS_STATCNT(afs_PutDCache);
1053 #ifndef AFS_SUN5_ENVX
1054     MObtainWriteLock(&afs_xdcache,276);
1055 #endif
1056     if (ad->refCount <= 0)
1057         osi_Panic("putdcache");
1058     --ad->refCount;
1059 #ifdef  AFS_SUN5_ENVX
1060     MReleaseWriteLock(&ad->lock);
1061 #else
1062     MReleaseWriteLock(&afs_xdcache);
1063 #endif
1064     return 0;
1065
1066 } /*afs_PutDCache*/
1067
1068
1069 /*
1070  * afs_TryToSmush
1071  *
1072  * Description:
1073  *      Try to discard all data associated with this file from the
1074  *      cache.
1075  *
1076  * Parameters:
1077  *      avc : Pointer to the cache info for the file.
1078  *
1079  * Environment:
1080  *      Both pvnLock and lock are write held.
1081  */
1082 void
1083 afs_TryToSmush(avc, acred, sync)
1084     register struct vcache *avc;
1085     struct AFS_UCRED *acred;
1086     int sync;
1087 { /*afs_TryToSmush*/
1088
1089     register struct dcache *tdc;
1090     register int index;
1091     register int i;
1092     AFS_STATCNT(afs_TryToSmush);
1093     afs_Trace2(afs_iclSetp, CM_TRACE_TRYTOSMUSH, ICL_TYPE_POINTER, avc,
1094                ICL_TYPE_INT32, avc->m.Length);
1095     sync = 1;                           /* XX Temp testing XX*/
1096
1097 #if     defined(AFS_SUN5_ENV)
1098     ObtainWriteLock(&avc->vlock, 573);
1099     avc->activeV++;     /* block new getpages */
1100     ReleaseWriteLock(&avc->vlock);
1101 #endif
1102
1103     /* Flush VM pages */
1104     osi_VM_TryToSmush(avc, acred, sync);
1105
1106     /*
1107      * Get the hash chain containing all dce's for this fid
1108      */
1109     i = DVHash(&avc->fid);
1110     MObtainWriteLock(&afs_xdcache,277);
1111     for(index = afs_dvhashTbl[i]; index != NULLIDX; index=i) {
1112       i = afs_dvnextTbl[index]; /* next pointer this hash table */
1113       if (afs_indexUnique[index] == avc->fid.Fid.Unique) {
1114         tdc = afs_GetDSlot(index, (struct dcache *)0);
1115         if (!FidCmp(&tdc->f.fid, &avc->fid)) {
1116             if (sync) {
1117                 if ((afs_indexFlags[index] & IFDataMod) == 0 &&
1118                     tdc->refCount == 1) {
1119                     afs_FlushDCache(tdc);
1120                 }
1121             } else
1122                 afs_indexTable[index] = 0;
1123         }
1124         lockedPutDCache(tdc);
1125       }
1126     }
1127 #if     defined(AFS_SUN5_ENV)
1128     ObtainWriteLock(&avc->vlock, 545);
1129     if (--avc->activeV == 0 && (avc->vstates & VRevokeWait)) {
1130         avc->vstates &= ~VRevokeWait;
1131         afs_osi_Wakeup((char *)&avc->vstates);
1132     }
1133     ReleaseWriteLock(&avc->vlock);
1134 #endif
1135     MReleaseWriteLock(&afs_xdcache);
1136     /*
1137      * It's treated like a callback so that when we do lookups we'll invalidate the unique bit if any
1138      * trytoSmush occured during the lookup call
1139      */
1140     afs_allCBs++;
1141 } /*afs_TryToSmush*/
1142
1143 /*
1144  * afs_FindDCache
1145  *
1146  * Description:
1147  *      Given the cached info for a file and a byte offset into the
1148  *      file, make sure the dcache entry for that file and containing
1149  *      the given byte is available, returning it to our caller.
1150  *
1151  * Parameters:
1152  *      avc   : Pointer to the (held) vcache entry to look in.
1153  *      abyte : Which byte we want to get to.
1154  *
1155  * Returns:
1156  *      Pointer to the dcache entry covering the file & desired byte,
1157  *      or NULL if not found.
1158  *
1159  * Environment:
1160  *      The vcache entry is held upon entry.
1161  */
1162
1163 struct dcache *afs_FindDCache(avc, abyte)
1164     register struct vcache *avc;
1165     afs_int32 abyte;
1166
1167 { /*afs_FindDCache*/
1168
1169     afs_int32 chunk;
1170     register afs_int32 i, index;
1171     register struct dcache *tdc;
1172
1173     AFS_STATCNT(afs_FindDCache);
1174     chunk = AFS_CHUNK(abyte);
1175
1176     /*
1177      * Hash on the [fid, chunk] and get the corresponding dcache index
1178      * after write-locking the dcache.
1179      */
1180     i = DCHash(&avc->fid, chunk);
1181     MObtainWriteLock(&afs_xdcache,278);
1182     for(index = afs_dchashTbl[i]; index != NULLIDX;) {
1183       if (afs_indexUnique[index] == avc->fid.Fid.Unique) {
1184         tdc = afs_GetDSlot(index, (struct dcache *)0);
1185         if (!FidCmp(&tdc->f.fid, &avc->fid) && chunk == tdc->f.chunk) {
1186             break;  /* leaving refCount high for caller */
1187         }
1188         lockedPutDCache(tdc);
1189       }
1190       index = afs_dcnextTbl[index];
1191     }
1192     MReleaseWriteLock(&afs_xdcache);
1193     if (index != NULLIDX) {
1194         hset(afs_indexTimes[tdc->index], afs_indexCounter);
1195         hadd32(afs_indexCounter, 1);
1196         return tdc;
1197     }
1198     else
1199         return(struct dcache *) 0;
1200
1201 } /*afs_FindDCache*/
1202
1203
1204 /*
1205  * afs_UFSCacheStoreProc
1206  *
1207  * Description:
1208  *      Called upon store.
1209  *
1210  * Parameters:
1211  *      acall : Ptr to the Rx call structure involved.
1212  *      afile : Ptr to the related file descriptor.
1213  *      alen  : Size of the file in bytes.
1214  *      avc   : Ptr to the vcache entry.
1215  *      shouldWake : is it "safe" to return early from close() ?
1216  *      abytesToXferP  : Set to the number of bytes to xfer.
1217  *                       NOTE: This parameter is only used if AFS_NOSTATS
1218  *                              is not defined.
1219  *      abytesXferredP : Set to the number of bytes actually xferred.
1220  *                       NOTE: This parameter is only used if AFS_NOSTATS
1221  *                              is not defined.
1222  *
1223  * Environment:
1224  *      Nothing interesting.
1225  */
1226 static int afs_UFSCacheStoreProc(acall, afile, alen, avc, shouldWake,
1227                               abytesToXferP, abytesXferredP)
1228      register struct rx_call *acall;
1229      struct osi_file *afile;
1230      register afs_int32 alen;
1231      struct vcache *avc;
1232      int *shouldWake;
1233      afs_int32 *abytesToXferP;
1234      afs_int32 *abytesXferredP;
1235 { /* afs_UFSCacheStoreProc*/
1236
1237     afs_int32 code, got;
1238     register char *tbuffer;
1239     register int tlen;
1240
1241     AFS_STATCNT(UFS_CacheStoreProc);
1242
1243 #ifndef AFS_NOSTATS
1244     /*
1245      * In this case, alen is *always* the amount of data we'll be trying
1246      * to ship here.
1247      */
1248     (*abytesToXferP)  = alen;
1249     (*abytesXferredP) = 0;
1250 #endif /* AFS_NOSTATS */
1251
1252     afs_Trace3(afs_iclSetp, CM_TRACE_STOREPROC, ICL_TYPE_POINTER, avc,
1253                ICL_TYPE_INT32, avc->m.Length, ICL_TYPE_INT32, alen);
1254     tbuffer = osi_AllocLargeSpace(AFS_LRALLOCSIZ);
1255     while (alen > 0) {
1256         tlen = (alen > AFS_LRALLOCSIZ ? AFS_LRALLOCSIZ : alen);
1257         got = afs_osi_Read(afile, -1, tbuffer, tlen);
1258         if ((got < 0) 
1259 #if     !defined(AFS_SUN5_ENV) && !defined(AFS_OSF_ENV) && !defined(AFS_SGI64_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_FBSD_ENV)
1260             || (got != tlen && getuerror())
1261 #endif
1262             ) {
1263             osi_FreeLargeSpace(tbuffer);
1264             return EIO;
1265         }
1266 #ifdef RX_ENABLE_LOCKS
1267         AFS_GUNLOCK();
1268 #endif /* RX_ENABLE_LOCKS */
1269         code = rx_Write(acall, tbuffer, got);  /* writing 0 bytes will
1270         * push a short packet.  Is that really what we want, just because the
1271         * data didn't come back from the disk yet?  Let's try it and see. */
1272 #ifdef RX_ENABLE_LOCKS
1273         AFS_GLOCK();
1274 #endif /* RX_ENABLE_LOCKS */
1275 #ifndef AFS_NOSTATS
1276         (*abytesXferredP) += code;
1277 #endif /* AFS_NOSTATS */
1278         if (code != got) {
1279             osi_FreeLargeSpace(tbuffer);
1280             return -33;
1281         }
1282         alen -= got;
1283         /*
1284          * If file has been locked on server, we can allow the store
1285          * to continue.
1286          */
1287         if (shouldWake && *shouldWake && (rx_GetRemoteStatus(acall) & 1)) {
1288             *shouldWake = 0;  /* only do this once */
1289             afs_wakeup(avc);
1290         }
1291     }
1292     osi_FreeLargeSpace(tbuffer);
1293     return 0;
1294
1295 } /* afs_UFSCacheStoreProc*/
1296
1297
1298 /*
1299  * afs_UFSCacheFetchProc
1300  *
1301  * Description:
1302  *      Routine called on fetch; also tells people waiting for data
1303  *      that more has arrived.
1304  *
1305  * Parameters:
1306  *      acall : Ptr to the Rx call structure.
1307  *      afile : File descriptor for the cache file.
1308  *      abase : Base offset to fetch.
1309  *      adc   : Ptr to the dcache entry for the file.
1310  *      avc   : Ptr to the vcache entry for the file.
1311  *      abytesToXferP  : Set to the number of bytes to xfer.
1312  *                       NOTE: This parameter is only used if AFS_NOSTATS
1313  *                              is not defined.
1314  *      abytesXferredP : Set to the number of bytes actually xferred.
1315  *                       NOTE: This parameter is only used if AFS_NOSTATS
1316  *                              is not defined.
1317  *
1318  * Environment:
1319  *      Nothing interesting.
1320  */
1321
1322 static int afs_UFSCacheFetchProc(acall, afile, abase, adc, avc,
1323                               abytesToXferP, abytesXferredP)
1324     register struct rx_call *acall;
1325     afs_int32 abase;
1326     struct dcache *adc;
1327     struct vcache *avc;
1328     struct osi_file *afile;
1329     afs_int32 *abytesToXferP;
1330     afs_int32 *abytesXferredP;
1331
1332 { /*UFS_CacheFetchProc*/
1333
1334     afs_int32 length;
1335     register afs_int32 code;
1336     register char *tbuffer;
1337     register int tlen;
1338     int moredata;
1339
1340     AFS_STATCNT(UFS_CacheFetchProc);
1341     afile->offset = 0;  /* Each time start from the beginning */
1342 #ifndef AFS_NOSTATS
1343     (*abytesToXferP)  = 0;
1344     (*abytesXferredP) = 0;
1345 #endif /* AFS_NOSTATS */
1346     tbuffer = osi_AllocLargeSpace(AFS_LRALLOCSIZ);
1347     do {
1348 #ifdef RX_ENABLE_LOCKS
1349         AFS_GUNLOCK();
1350 #endif /* RX_ENABLE_LOCKS */
1351         code = rx_Read(acall, (char *)&length, sizeof(afs_int32));
1352 #ifdef RX_ENABLE_LOCKS
1353         AFS_GLOCK();
1354 #endif /* RX_ENABLE_LOCKS */
1355         if (code != sizeof(afs_int32)) {
1356             osi_FreeLargeSpace(tbuffer);
1357             code = rx_Error(acall);
1358             return (code?code:-1);      /* try to return code, not -1 */
1359         }
1360         length = ntohl(length);
1361         /*
1362          * The fetch protocol is extended for the AFS/DFS translator
1363          * to allow multiple blocks of data, each with its own length,
1364          * to be returned. As long as the top bit is set, there are more
1365          * blocks expected.
1366          *
1367          * We do not do this for AFS file servers because they sometimes
1368          * return large negative numbers as the transfer size.
1369          */
1370         if (avc->states & CForeign) {
1371             moredata = length & 0x80000000;
1372             length &= ~0x80000000;
1373         } else {
1374             moredata = 0;
1375         }
1376 #ifndef AFS_NOSTATS
1377         (*abytesToXferP) += length;
1378 #endif                          /* AFS_NOSTATS */
1379         while (length > 0) {
1380             tlen = (length > AFS_LRALLOCSIZ ? AFS_LRALLOCSIZ : length);
1381 #ifdef RX_ENABLE_LOCKS
1382             AFS_GUNLOCK();
1383 #endif /* RX_ENABLE_LOCKS */
1384             code = rx_Read(acall, tbuffer, tlen);
1385 #ifdef RX_ENABLE_LOCKS
1386             AFS_GLOCK();
1387 #endif /* RX_ENABLE_LOCKS */
1388 #ifndef AFS_NOSTATS
1389             (*abytesXferredP) += code;
1390 #endif                          /* AFS_NOSTATS */
1391             if (code != tlen) {
1392                 osi_FreeLargeSpace(tbuffer);
1393                 return -34;
1394             }
1395             code = afs_osi_Write(afile, -1, tbuffer, tlen);
1396             if (code != tlen) {
1397                 osi_FreeLargeSpace(tbuffer);
1398                 return EIO;
1399             }
1400             abase += tlen;
1401             length -= tlen;
1402             adc->validPos = abase;
1403             if (adc->flags & DFWaiting) {
1404                 adc->flags &= ~DFWaiting;
1405                 afs_osi_Wakeup(&adc->validPos);
1406             }
1407         }
1408     } while (moredata);
1409     osi_FreeLargeSpace(tbuffer);
1410     return 0;
1411
1412 } /* afs_UFSCacheFetchProc*/
1413
1414 /*
1415  * afs_GetDCache
1416  *
1417  * Description:
1418  *      This function is called to obtain a reference to data stored in
1419  *      the disk cache, locating a chunk of data containing the desired
1420  *      byte and returning a reference to the disk cache entry, with its
1421  *      reference count incremented.
1422  *
1423  * Parameters:
1424  * IN:
1425  *      avc     : Ptr to a vcache entry (unlocked)
1426  *      abyte   : Byte position in the file desired
1427  *      areq    : Request structure identifying the requesting user.
1428  *      aflags  : Settings as follows:
1429  *                      1 : Set locks
1430  *                      2 : Return after creating entry.
1431  * OUT:
1432  *      aoffset : Set to the offset within the chunk where the resident
1433  *                byte is located.
1434  *      alen    : Set to the number of bytes of data after the desired
1435  *                byte (including the byte itself) which can be read
1436  *                from this chunk.
1437  *
1438  * Environment:
1439  *      The vcache entry pointed to by avc is unlocked upon entry.
1440  */
1441
1442 struct tlocal1 {
1443     struct AFSVolSync tsync;
1444     struct AFSFetchStatus OutStatus;
1445     struct AFSCallBack CallBack;
1446 };
1447
1448 /* these fields are protected by the lock on the vcache and luck 
1449  * on the dcache */
1450 void updateV2DC(int l, struct vcache *v, struct dcache *d, int src) {
1451     if (!l || 0 == NBObtainWriteLock(&(v->lock),src)) {
1452         if (hsame(v->m.DataVersion, d->f.versionNo) && v->callback) {
1453             v->quick.dc = d;
1454             v->quick.stamp = d->stamp = MakeStamp();
1455             v->quick.minLoc = AFS_CHUNKTOBASE(d->f.chunk);
1456             /* Don't think I need these next two lines forever */
1457             v->quick.len = d->f.chunkBytes;
1458             v->h1.dchint = d;
1459         }
1460         if(l) ReleaseWriteLock(&((v)->lock));
1461     }
1462 }
1463
1464 struct dcache *afs_GetDCache(avc, abyte, areq, aoffset, alen, aflags)
1465     register struct vcache *avc;    /*Held*/
1466     afs_int32 abyte;
1467     int aflags;
1468     afs_int32 *aoffset, *alen;
1469     register struct vrequest *areq;
1470
1471 { /*afs_GetDCache*/
1472
1473     register afs_int32 i, code, code1, shortcut , adjustsize=0;
1474     int setLocks;
1475     afs_int32 index;
1476     afs_int32 us;
1477     afs_int32 chunk;
1478     afs_int32 maxGoodLength;            /* amount of good data at server */
1479     struct rx_call *tcall;
1480     afs_int32 Position = 0;
1481     afs_int32 size;                             /* size of segment to transfer */
1482     struct tlocal1 *tsmall;
1483     register struct dcache *tdc;
1484     register struct osi_file *file;
1485     register struct conn *tc;
1486     int downDCount = 0;
1487     XSTATS_DECLS
1488 #ifndef AFS_NOSTATS
1489     struct afs_stats_xferData *xferP;   /* Ptr to this op's xfer struct */
1490     osi_timeval_t  xferStartTime,       /*FS xfer start time*/
1491                    xferStopTime;        /*FS xfer stop time*/
1492     afs_int32 bytesToXfer;                      /* # bytes to xfer*/
1493     afs_int32 bytesXferred;                     /* # bytes actually xferred*/
1494     struct afs_stats_AccessInfo *accP;  /*Ptr to access record in stats*/
1495     int fromReplica;                    /*Are we reading from a replica?*/
1496     int numFetchLoops;                  /*# times around the fetch/analyze loop*/
1497 #endif /* AFS_NOSTATS */
1498
1499     AFS_STATCNT(afs_GetDCache);
1500
1501     if (dcacheDisabled)
1502         return NULL;
1503
1504     /*
1505      * Determine the chunk number and offset within the chunk corresponding
1506      * to the desired byte.
1507      */
1508     if (vType(avc) == VDIR) {
1509         chunk = 0;
1510     }
1511     else {
1512         chunk = AFS_CHUNK(abyte);
1513     }
1514
1515     setLocks = aflags & 1;
1516
1517     /* come back to here if we waited for the cache to drain. */
1518  RetryGetDCache:
1519     shortcut = 0;
1520
1521     /* check hints first! (might could use bcmp or some such...) */
1522
1523     if (tdc = avc->h1.dchint) {
1524         MObtainReadLock(&afs_xdcache);
1525         if ( (tdc->index != NULLIDX) && !FidCmp(&tdc->f.fid, &avc->fid) &&
1526             chunk == tdc->f.chunk &&
1527             !(afs_indexFlags[tdc->index] & (IFFree|IFDiscarded))) {
1528             /* got the right one.  It might not be the right version, and it 
1529              * might be fetching, but it's the right dcache entry.
1530              */
1531             /* All this code should be integrated better with what follows:
1532              * I can save a good bit more time under a write lock if I do..
1533              */
1534             /* does avc need to be locked? */
1535             /* Note that the race labeled LOCKXXX is inconsequential: the xdcache
1536              * lock protects both the dcache slots AND the DLRU list.  While 
1537              * the slots and hash table and DLRU list all may change in the race,
1538              * THIS particular dcache structure cannot be recycled and its LRU 
1539              * pointers must still be valid once we get the lock again.  Still
1540              * we should either create another lock or invent a new method of
1541              * managing dcache structs -- CLOCK or something. */
1542             shortcut = 1;           
1543 #ifdef  AFS_SUN5_ENVX
1544             MObtainWriteLock(&tdc->lock,279);
1545 #endif
1546             tdc->refCount++;
1547             if (hsame(tdc->f.versionNo, avc->m.DataVersion) 
1548                 && !(tdc->flags & DFFetching)) {
1549                 afs_stats_cmperf.dcacheHits++;
1550                 MReleaseReadLock(&afs_xdcache);
1551
1552                 MObtainWriteLock(&afs_xdcache, 559); /* LOCKXXX */
1553                 QRemove(&tdc->lruq);
1554                 QAdd(&afs_DLRU, &tdc->lruq);
1555                 MReleaseWriteLock(&afs_xdcache);
1556                 goto done;
1557             }
1558 #ifdef  AFS_SUN5_ENVX
1559            MReleaseWriteLock(&tdc->lock);
1560 #endif
1561         }
1562         MReleaseReadLock(&afs_xdcache);
1563     }
1564     if (!shortcut)
1565       {
1566
1567     /*
1568      * Hash on the [fid, chunk] and get the corresponding dcache index
1569      * after write-locking the dcache.
1570      */
1571  RetryLookup:
1572     i = DCHash(&avc->fid, chunk);
1573     afs_MaybeWakeupTruncateDaemon();    /* check to make sure our space is fine */
1574     MObtainWriteLock(&afs_xdcache,280);
1575     us = NULLIDX;
1576     for(index = afs_dchashTbl[i]; index != NULLIDX;) {
1577       if (afs_indexUnique[index] == avc->fid.Fid.Unique) {
1578         tdc = afs_GetDSlot(index, (struct dcache *)0);
1579         if (!FidCmp(&tdc->f.fid, &avc->fid) && chunk == tdc->f.chunk) {
1580             /* Move it up in the beginning of the list */
1581             if (afs_dchashTbl[i] != index)  {
1582                 afs_dcnextTbl[us] = afs_dcnextTbl[index];
1583                 afs_dcnextTbl[index] = afs_dchashTbl[i];
1584                 afs_dchashTbl[i] = index;
1585             }
1586             MReleaseWriteLock(&afs_xdcache);
1587             break;  /* leaving refCount high for caller */
1588         }
1589         lockedPutDCache(tdc);
1590       }
1591       us = index;
1592       index = afs_dcnextTbl[index];
1593     }
1594     /*
1595      * If we didn't find the entry, we'll create one.
1596      */
1597     if (index == NULLIDX) {
1598         afs_Trace2(afs_iclSetp, CM_TRACE_GETDCACHE1, ICL_TYPE_POINTER, avc,
1599                ICL_TYPE_INT32, chunk);
1600
1601         if (afs_discardDCList == NULLIDX && afs_freeDCList == NULLIDX) {
1602             while (1) {
1603                 if (!setLocks) avc->states |= CDCLock;
1604                 afs_GetDownD(5, (int*)0);       /* just need slots */
1605                 if (!setLocks) avc->states &= (~CDCLock);
1606                 if (afs_discardDCList != NULLIDX || afs_freeDCList != NULLIDX)
1607                     break;
1608                 /* If we can't get space for 5 mins we give up and panic */
1609                 if (++downDCount > 300)
1610                     osi_Panic("getdcache"); 
1611                 MReleaseWriteLock(&afs_xdcache);                
1612                 afs_osi_Wait(1000, 0, 0);
1613                 goto RetryLookup;
1614             }
1615         }
1616         if (afs_discardDCList == NULLIDX ||
1617             ((aflags & 2) && afs_freeDCList != NULLIDX)) {
1618             afs_indexFlags[afs_freeDCList] &= ~IFFree;
1619             tdc = afs_GetDSlot(afs_freeDCList, 0);
1620             afs_freeDCList = afs_dvnextTbl[tdc->index];
1621             afs_freeDCCount--;
1622         } else {
1623             afs_indexFlags[afs_discardDCList] &= ~IFDiscarded;
1624             tdc = afs_GetDSlot(afs_discardDCList, 0);
1625             afs_discardDCList = afs_dvnextTbl[tdc->index];
1626             afs_discardDCCount--;
1627             size = ((tdc->f.chunkBytes + afs_fsfragsize)^afs_fsfragsize)>>10;
1628             afs_blocksDiscarded -= size;
1629             afs_stats_cmperf.cacheBlocksDiscarded = afs_blocksDiscarded;
1630             if (aflags & 2) {
1631                 /* Truncate the chunk so zeroes get filled properly */
1632                 file = afs_CFileOpen(tdc->f.inode);
1633                 afs_CFileTruncate(file, 0);
1634                 afs_CFileClose(file);
1635                 afs_AdjustSize(tdc, 0);
1636             }
1637         }
1638
1639         /*
1640          * Fill in the newly-allocated dcache record.
1641          */
1642         afs_indexFlags[tdc->index] &= ~(IFDirtyPages | IFAnyPages);
1643         tdc->f.fid = avc->fid;
1644         afs_indexUnique[tdc->index] = tdc->f.fid.Fid.Unique;
1645         hones(tdc->f.versionNo);            /* invalid value */
1646         tdc->f.chunk = chunk;
1647         /* XXX */
1648         if (tdc->lruq.prev == &tdc->lruq) osi_Panic("lruq 1");
1649         /*
1650          * Now add to the two hash chains - note that i is still set
1651          * from the above DCHash call.
1652          */
1653         afs_dcnextTbl[tdc->index] = afs_dchashTbl[i];
1654         afs_dchashTbl[i] = tdc->index;
1655         i = DVHash(&avc->fid);
1656         afs_dvnextTbl[tdc->index] = afs_dvhashTbl[i];
1657         afs_dvhashTbl[i] = tdc->index;
1658         tdc->flags = DFEntryMod;
1659         tdc->f.states = 0;
1660         afs_MaybeWakeupTruncateDaemon();
1661         MReleaseWriteLock(&afs_xdcache);
1662     }
1663   }  /* else hint failed... */
1664
1665     afs_Trace4(afs_iclSetp, CM_TRACE_GETDCACHE2, ICL_TYPE_POINTER, avc,
1666                ICL_TYPE_POINTER, tdc,
1667                ICL_TYPE_INT32, hgetlo(tdc->f.versionNo),
1668                ICL_TYPE_INT32, hgetlo(avc->m.DataVersion));
1669     /*
1670      * Here we have the unlocked entry in tdc, with its refCount
1671      * incremented.  Note: we don't use the S-lock; it costs concurrency
1672      * when storing a file back to the server.
1673      */
1674     if (setLocks) ObtainReadLock(&avc->lock);
1675
1676     /*
1677      * Not a newly created file so we need to check the file's length and
1678      * compare data versions since someone could have changed the data or we're
1679      * reading a file written elsewhere. We only want to bypass doing no-op
1680      * read rpcs on newly created files (dv of 0) since only then we guarantee
1681      * that this chunk's data hasn't been filled by another client.
1682      */
1683     if (!hiszero(avc->m.DataVersion))
1684         aflags &= ~4;
1685 #if     defined(AFS_AIX32_ENV) || defined(AFS_SGI_ENV)
1686 #ifdef  AFS_SGI_ENV
1687 #ifdef AFS_SGI64_ENV
1688     if (aflags & 4) adjustsize = NBPP;
1689 #else
1690     if (aflags & 4) adjustsize = 8192;
1691 #endif
1692 #else
1693     if (aflags & 4) adjustsize = 4096;
1694 #endif
1695     if (AFS_CHUNKTOBASE(chunk)+adjustsize >= avc->m.Length &&
1696 #else
1697 #if     defined(AFS_SUN_ENV)  || defined(AFS_OSF_ENV)
1698     if (((aflags & 4) || (AFS_CHUNKTOBASE(chunk) >= avc->m.Length)) &&
1699 #else
1700     if (AFS_CHUNKTOBASE(chunk) >= avc->m.Length &&
1701 #endif
1702 #endif
1703         !hsame(avc->m.DataVersion, tdc->f.versionNo)) {
1704         /* no data in file to read at this position */
1705         if (setLocks) {
1706             ReleaseReadLock(&avc->lock);
1707             ObtainWriteLock(&avc->lock,64);
1708         }
1709         /* check again, now that we have a write lock */
1710 #if     defined(AFS_AIX32_ENV) || defined(AFS_SGI_ENV)
1711         if (AFS_CHUNKTOBASE(chunk)+adjustsize >= avc->m.Length &&
1712 #else
1713 #if     defined(AFS_SUN_ENV)  || defined(AFS_OSF_ENV)
1714         if (((aflags & 4) || (AFS_CHUNKTOBASE(chunk) >= avc->m.Length)) &&
1715 #else
1716         if (AFS_CHUNKTOBASE(chunk) >= avc->m.Length &&
1717 #endif
1718 #endif
1719             !hsame(avc->m.DataVersion, tdc->f.versionNo)) {
1720             file = afs_CFileOpen(tdc->f.inode);
1721             afs_CFileTruncate(file, 0);
1722             afs_CFileClose(file);
1723             afs_AdjustSize(tdc, 0);
1724             hset(tdc->f.versionNo, avc->m.DataVersion);
1725             tdc->flags |= DFEntryMod;
1726         }
1727         if (setLocks) {
1728             ReleaseWriteLock(&avc->lock);
1729             ObtainReadLock(&avc->lock);
1730         }
1731     }
1732     if (setLocks) ReleaseReadLock(&avc->lock);
1733
1734     /*
1735      * We must read in the whole chunk iff the version number doesn't
1736      * match.
1737      */
1738     if (aflags & 2) {
1739         /* don't need data, just a unique dcache entry */
1740         hset(afs_indexTimes[tdc->index], afs_indexCounter);
1741         hadd32(afs_indexCounter, 1);
1742         updateV2DC(setLocks,avc,tdc,567);
1743         return tdc;     /* check if we're done */
1744     }
1745     osi_Assert(setLocks || WriteLocked(&avc->lock));
1746
1747     if (setLocks) ObtainReadLock(&avc->lock);
1748     if (!hsame(avc->m.DataVersion, tdc->f.versionNo)) {
1749         /*
1750          * Version number mismatch.
1751          */
1752         if (setLocks) {
1753             ReleaseReadLock(&avc->lock);
1754             ObtainWriteLock(&avc->lock,65);
1755         }
1756
1757         /*
1758          * If data ever existed for this vnode, and this is a text object,
1759          * do some clearing.  Now, you'd think you need only do the flush
1760          * when VTEXT is on, but VTEXT is turned off when the text object
1761          * is freed, while pages are left lying around in memory marked
1762          * with this vnode.  If we would reactivate (create a new text
1763          * object from) this vnode, we could easily stumble upon some of
1764          * these old pages in pagein.  So, we always flush these guys.
1765          * Sun has a wonderful lack of useful invariants in this system.
1766          *
1767          * avc->flushDV is the data version # of the file at the last text
1768          * flush.  Clearly, at least, we don't have to flush the file more
1769          * often than it changes
1770          */
1771         if (hcmp(avc->flushDV, avc->m.DataVersion) < 0) {
1772             /*
1773              * By here, the cache entry is always write-locked.  We can
1774              * deadlock if we call osi_Flush with the cache entry locked...
1775              */
1776             ReleaseWriteLock(&avc->lock);
1777             osi_FlushText(avc);
1778             /*
1779              * Call osi_FlushPages in open, read/write, and map, since it
1780              * is too hard here to figure out if we should lock the
1781              * pvnLock.
1782              */
1783             ObtainWriteLock(&avc->lock,66);
1784         }
1785
1786         /* Watch for standard race condition */
1787         if (hsame(avc->m.DataVersion, tdc->f.versionNo)) {
1788             updateV2DC(0,avc,tdc,569);          /* set hint */
1789             if (setLocks) ReleaseWriteLock(&avc->lock);
1790             afs_stats_cmperf.dcacheHits++;
1791             goto done;
1792         }
1793
1794         /* Sleep here when cache needs to be drained. */
1795         if (setLocks &&
1796             (afs_blocksUsed > (CM_WAITFORDRAINPCT*afs_cacheBlocks)/100)) {
1797             /* Make sure truncate daemon is running */
1798             afs_MaybeWakeupTruncateDaemon();
1799             tdc->refCount--; /* we'll re-obtain the dcache when we re-try. */
1800             ReleaseWriteLock(&avc->lock);
1801             while ((afs_blocksUsed-afs_blocksDiscarded) >
1802                    (CM_WAITFORDRAINPCT*afs_cacheBlocks)/100) {
1803                 afs_WaitForCacheDrain = 1;
1804                 afs_osi_Sleep(&afs_WaitForCacheDrain);
1805             }
1806             afs_MaybeFreeDiscardedDCache();
1807             /* need to check if someone else got the chunk first. */
1808             goto RetryGetDCache;
1809         }
1810
1811         /* Do not fetch data beyond truncPos. */
1812         maxGoodLength = avc->m.Length;
1813         if (avc->truncPos < maxGoodLength) maxGoodLength = avc->truncPos;
1814         Position = AFS_CHUNKBASE(abyte);
1815         if (vType(avc) == VDIR) {
1816             size = avc->m.Length;
1817             if (size > tdc->f.chunkBytes) {
1818                 /* pre-reserve space for file */
1819                 afs_AdjustSize(tdc, size);
1820             }
1821             size = 999999999;       /* max size for transfer */
1822         }
1823         else {
1824             size = AFS_CHUNKSIZE(abyte);        /* expected max size */
1825             /* don't read past end of good data on server */
1826             if (Position + size > maxGoodLength)
1827                 size = maxGoodLength - Position;
1828             if (size < 0) size = 0; /* Handle random races */
1829             if (size > tdc->f.chunkBytes) {
1830                 /* pre-reserve space for file */
1831                 afs_AdjustSize(tdc, size);      /* changes chunkBytes */
1832                 /* max size for transfer still in size */
1833             }
1834         }
1835         if (afs_mariner && !tdc->f.chunk) 
1836             afs_MarinerLog("fetch$Fetching", avc); /* , Position, size, afs_indexCounter );*/
1837         /*
1838          * Right now, we only have one tool, and it's a hammer.  So, we
1839          * fetch the whole file.
1840          */
1841         DZap(&tdc->f.inode);    /* pages in cache may be old */
1842 #ifdef  IHINT
1843         if (file = tdc->ihint) {
1844           if (tdc->f.inode == file->inum ) 
1845             usedihint++;
1846           else {
1847             tdc->ihint = 0;
1848             afs_UFSClose(file);
1849             file = 0;
1850             nihints--;
1851             file = osi_UFSOpen(tdc->f.inode);
1852           }
1853         }
1854         else
1855 #endif /* IHINT */
1856         file = afs_CFileOpen(tdc->f.inode);
1857         afs_RemoveVCB(&avc->fid);
1858         tdc->f.states |= DWriting;
1859         tdc->flags |= DFFetching;
1860         tdc->validPos = Position; /*Last valid position in this chunk*/
1861         if (tdc->flags & DFFetchReq) {
1862             tdc->flags &= ~DFFetchReq;
1863             afs_osi_Wakeup(&tdc->validPos);
1864         }
1865         tsmall = (struct tlocal1 *) osi_AllocLargeSpace(sizeof(struct tlocal1));
1866 #ifndef AFS_NOSTATS
1867         /*
1868          * Remember if we are doing the reading from a replicated volume,
1869          * and how many times we've zipped around the fetch/analyze loop.
1870          */
1871         fromReplica = (avc->states & CRO) ? 1 : 0;
1872         numFetchLoops = 0;
1873         accP = &(afs_stats_cmfullperf.accessinf);
1874         if (fromReplica)
1875             (accP->replicatedRefs)++;
1876         else
1877             (accP->unreplicatedRefs)++;
1878 #endif /* AFS_NOSTATS */
1879         /* this is a cache miss */
1880         afs_stats_cmperf.dcacheMisses++; 
1881         afs_Trace3(afs_iclSetp, CM_TRACE_FETCHPROC, ICL_TYPE_POINTER, avc,
1882                    ICL_TYPE_INT32, Position, ICL_TYPE_INT32, size);
1883
1884         /*
1885          * Dynamic root support:  fetch data from local memory.
1886          */
1887         if (afs_IsDynroot(avc)) {
1888             char *dynrootDir;
1889             int dynrootLen;
1890
1891             afs_GetDynroot(&dynrootDir, &dynrootLen, &tsmall->OutStatus);
1892
1893             dynrootDir += Position;
1894             dynrootLen -= Position;
1895             if (size > dynrootLen)
1896                 size = dynrootLen;
1897             if (size < 0) size = 0;
1898             code = afs_osi_Write(file, -1, dynrootDir, size);
1899             afs_PutDynroot();
1900
1901             if (code == size)
1902                 code = 0;
1903             else
1904                 code = -1;
1905
1906             tdc->validPos = Position + size;
1907             afs_CFileTruncate(file, size); /* prune it */
1908         } else
1909         /*
1910          * Not a dynamic vnode:  do the real fetch.
1911          */
1912         do {
1913             tc = afs_Conn(&avc->fid, areq, SHARED_LOCK);
1914             if (tc) {
1915 #ifndef AFS_NOSTATS
1916                 numFetchLoops++;
1917                 if (fromReplica)
1918                     (accP->numReplicasAccessed)++;
1919                 
1920 #endif /* AFS_NOSTATS */
1921                 avc->callback = tc->srvr->server;
1922                 ConvertWToSLock(&avc->lock);
1923                 i = osi_Time();
1924 #ifdef RX_ENABLE_LOCKS
1925                 AFS_GUNLOCK();
1926 #endif /* RX_ENABLE_LOCKS */
1927                 tcall = rx_NewCall(tc->id);
1928 #ifdef RX_ENABLE_LOCKS
1929                 AFS_GLOCK();
1930 #endif /* RX_ENABLE_LOCKS */
1931
1932
1933                 XSTATS_START_TIME(AFS_STATS_FS_RPCIDX_FETCHDATA);
1934 #ifdef RX_ENABLE_LOCKS
1935                 AFS_GUNLOCK();
1936 #endif /* RX_ENABLE_LOCKS */
1937                 code = StartRXAFS_FetchData(tcall,
1938                                             (struct AFSFid *) &avc->fid.Fid,
1939                                             Position, size);
1940 #ifdef RX_ENABLE_LOCKS
1941                 AFS_GLOCK();
1942 #endif /* RX_ENABLE_LOCKS */
1943                 if (code == 0) {
1944
1945 #ifndef AFS_NOSTATS
1946                     xferP = &(afs_stats_cmfullperf.rpc.fsXferTimes[AFS_STATS_FS_XFERIDX_FETCHDATA]);
1947                     osi_GetuTime(&xferStartTime);
1948
1949                     code = afs_CacheFetchProc(tcall, file, Position, tdc, avc,
1950                                           &bytesToXfer, &bytesXferred);
1951
1952                     osi_GetuTime(&xferStopTime);
1953                     (xferP->numXfers)++;
1954                     if (!code) {
1955                         (xferP->numSuccesses)++;
1956                         afs_stats_XferSumBytes[AFS_STATS_FS_XFERIDX_FETCHDATA] += bytesXferred;
1957                         (xferP->sumBytes) += (afs_stats_XferSumBytes[AFS_STATS_FS_XFERIDX_FETCHDATA] >> 10);
1958                         afs_stats_XferSumBytes[AFS_STATS_FS_XFERIDX_FETCHDATA] &= 0x3FF;
1959                         if (bytesXferred < xferP->minBytes)
1960                            xferP->minBytes = bytesXferred;
1961                         if (bytesXferred > xferP->maxBytes)
1962                            xferP->maxBytes = bytesXferred;
1963                     
1964                       /*
1965                        * Tally the size of the object.  Note: we tally the actual size,
1966                        * NOT the number of bytes that made it out over the wire.
1967                        */
1968                         if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET0)
1969                             (xferP->count[0])++;
1970                         else
1971                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET1)
1972                                 (xferP->count[1])++;
1973                         else
1974                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET2)
1975                                 (xferP->count[2])++;
1976                         else
1977                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET3)
1978                                 (xferP->count[3])++;
1979                         else
1980                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET4)
1981                                 (xferP->count[4])++;
1982                         else
1983                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET5)
1984                                 (xferP->count[5])++;
1985                         else
1986                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET6)
1987                                 (xferP->count[6])++;
1988                         else
1989                             if (bytesToXfer <= AFS_STATS_MAXBYTES_BUCKET7)
1990                                 (xferP->count[7])++;
1991                         else
1992                             (xferP->count[8])++;
1993
1994                         afs_stats_GetDiff(elapsedTime, xferStartTime, xferStopTime);
1995                         afs_stats_AddTo((xferP->sumTime), elapsedTime);
1996                         afs_stats_SquareAddTo((xferP->sqrTime), elapsedTime);
1997                         if (afs_stats_TimeLessThan(elapsedTime, (xferP->minTime))) {
1998                            afs_stats_TimeAssign((xferP->minTime), elapsedTime);
1999                         }
2000                         if (afs_stats_TimeGreaterThan(elapsedTime, (xferP->maxTime))) {
2001                            afs_stats_TimeAssign((xferP->maxTime), elapsedTime);
2002                         }
2003                       }
2004 #else
2005                     code = afs_CacheFetchProc(tcall, file, Position, tdc, avc, 0, 0);
2006 #endif /* AFS_NOSTATS */
2007                 }
2008                 if (code == 0) {
2009 #ifdef RX_ENABLE_LOCKS
2010                     AFS_GUNLOCK();
2011 #endif /* RX_ENABLE_LOCKS */
2012                     code = EndRXAFS_FetchData(tcall,
2013                                               &tsmall->OutStatus,
2014                                               &tsmall->CallBack,
2015                                               &tsmall->tsync);
2016 #ifdef RX_ENABLE_LOCKS
2017                     AFS_GLOCK();
2018 #endif /* RX_ENABLE_LOCKS */
2019                 }
2020                 XSTATS_END_TIME;
2021                 code1 = rx_EndCall(tcall, code);
2022                 UpgradeSToWLock(&avc->lock,27);
2023             }
2024             else {
2025                 code = -1;
2026             }
2027             if ( !code && code1 )
2028                 code = code1;
2029
2030             if (code == 0) {
2031                 /* callback could have been broken (or expired) in a race here, 
2032                  * but we return the data anyway.  It's as good as we knew about
2033                  * when we started. */
2034                 /* 
2035                  * validPos is updated by CacheFetchProc, and can only be 
2036                  * modifed under an S or W lock, which we've blocked out 
2037                  */
2038                 size = tdc->validPos - Position;  /* actual segment size */
2039                 if (size < 0) size = 0;
2040                 afs_CFileTruncate(file, size); /* prune it */
2041             }
2042             else { 
2043                ObtainWriteLock(&afs_xcbhash, 453);
2044                afs_DequeueCallback(avc);
2045                avc->states &= ~(CStatd | CUnique);   
2046                avc->callback = (struct server *)0;
2047                ReleaseWriteLock(&afs_xcbhash);
2048                if (avc->fid.Fid.Vnode & 1 || (vType(avc) == VDIR))
2049                    osi_dnlc_purgedp(avc);
2050            }
2051
2052         } while
2053             (afs_Analyze(tc, code, &avc->fid, areq,
2054                          AFS_STATS_FS_RPCIDX_FETCHDATA,
2055                          SHARED_LOCK, (struct cell *)0));
2056
2057 #ifndef AFS_NOSTATS
2058         /*
2059          * In the case of replicated access, jot down info on the number of
2060          * attempts it took before we got through or gave up.
2061          */
2062         if (fromReplica) {
2063             if (numFetchLoops <= 1)
2064                 (accP->refFirstReplicaOK)++;
2065             if (numFetchLoops > accP->maxReplicasPerRef)
2066                 accP->maxReplicasPerRef = numFetchLoops;
2067         }
2068 #endif /* AFS_NOSTATS */
2069
2070         tdc->flags &= ~DFFetching;
2071         if (tdc->flags & DFWaiting) {
2072             tdc->flags &= ~DFWaiting;
2073             afs_osi_Wakeup(&tdc->validPos);
2074         }
2075         if (avc->execsOrWriters == 0) tdc->f.states &= ~DWriting;
2076
2077         /* now, if code != 0, we have an error and should punt */
2078         if (code) {
2079             afs_CFileTruncate(file, 0);
2080             afs_AdjustSize(tdc, 0);
2081             afs_CFileClose(file);
2082             ZapDCE(tdc);        /* sets DFEntryMod */
2083             if (vType(avc) == VDIR) {
2084                 DZap(&tdc->f.inode);
2085             }
2086 #ifdef  AFS_SUN5_ENVX
2087             afs_PutDCache(tdc);
2088 #else
2089             tdc->refCount--;
2090 #endif
2091             ObtainWriteLock(&afs_xcbhash, 454);
2092             afs_DequeueCallback(avc);
2093             avc->states &= ~( CStatd | CUnique );
2094             ReleaseWriteLock(&afs_xcbhash);
2095             if (avc->fid.Fid.Vnode & 1 || (vType(avc) == VDIR))
2096                 osi_dnlc_purgedp(avc);
2097             if (setLocks) ReleaseWriteLock(&avc->lock);
2098             osi_FreeLargeSpace(tsmall);
2099             tdc = (struct dcache *) 0;
2100             goto done;
2101         }
2102
2103         /* otherwise we copy in the just-fetched info */
2104         afs_CFileClose(file);
2105         afs_AdjustSize(tdc, size);  /* new size */
2106         /*
2107          * Copy appropriate fields into vcache
2108          */
2109         afs_ProcessFS(avc, &tsmall->OutStatus, areq);
2110         hset64(tdc->f.versionNo, tsmall->OutStatus.dataVersionHigh, tsmall->OutStatus.DataVersion);
2111         tdc->flags |= DFEntryMod;
2112         afs_indexFlags[tdc->index] |= IFEverUsed;
2113         if (setLocks) ReleaseWriteLock(&avc->lock);
2114         osi_FreeLargeSpace(tsmall);
2115     } /*Data version numbers don't match*/
2116     else {
2117         /*
2118          * Data version numbers match.  Release locks if we locked
2119          * them, and remember we've had a cache hit.
2120          */
2121         if (setLocks)
2122             ReleaseReadLock(&avc->lock);
2123         afs_stats_cmperf.dcacheHits++;
2124     }  /*Data version numbers match*/
2125
2126     updateV2DC(setLocks,avc,tdc,332);    /* set hint */
2127 done:
2128     /*
2129      * See if this was a reference to a file in the local cell.
2130      */
2131     if (avc->fid.Cell == LOCALCELL)
2132         afs_stats_cmperf.dlocalAccesses++;
2133     else
2134         afs_stats_cmperf.dremoteAccesses++;
2135
2136     /* Fix up LRU info */
2137
2138     if (tdc) {
2139       hset(afs_indexTimes[tdc->index], afs_indexCounter);
2140       hadd32(afs_indexCounter, 1);
2141
2142         /* return the data */
2143         if (vType(avc) == VDIR)
2144             *aoffset = abyte;
2145         else
2146             *aoffset = AFS_CHUNKOFFSET(abyte);
2147         *alen = (tdc->f.chunkBytes - *aoffset);
2148     }
2149
2150     return tdc;
2151
2152 } /*afs_GetDCache*/
2153
2154
2155 /*
2156  * afs_WriteThroughDSlots
2157  *
2158  * Description:
2159  *      Sweep through the dcache slots and write out any modified
2160  *      in-memory data back on to our caching store.
2161  *
2162  * Parameters:
2163  *      None.
2164  *
2165  * Environment:
2166  *      The afs_xdcache is write-locked through this whole affair.
2167  */
2168 void
2169 afs_WriteThroughDSlots()
2170
2171 { /*afs_WriteThroughDSlots*/
2172
2173     register struct dcache *tdc;
2174     register afs_int32 i, touchedit=0;
2175
2176     AFS_STATCNT(afs_WriteThroughDSlots);
2177     MObtainWriteLock(&afs_xdcache,283);
2178     for(i = 0; i < afs_cacheFiles; i++) {
2179         tdc = afs_indexTable[i];
2180         if (tdc && (tdc->flags & DFEntryMod)) {
2181             tdc->flags &= ~DFEntryMod;
2182             afs_WriteDCache(tdc, 1);
2183             touchedit = 1;
2184         }
2185     }
2186     if (!touchedit && (cacheDiskType != AFS_FCACHE_TYPE_MEM)) {
2187         /* Touch the file to make sure that the mtime on the file is kept up-to-date
2188          * to avoid losing cached files on cold starts because their mtime seems old...
2189          */
2190         struct afs_fheader theader;
2191
2192         theader.magic = AFS_FHMAGIC;
2193         theader.firstCSize = AFS_FIRSTCSIZE;
2194         theader.otherCSize = AFS_OTHERCSIZE;
2195         theader.version = AFS_CI_VERSION;
2196         afs_osi_Write(afs_cacheInodep, 0, &theader, sizeof(theader));
2197     }
2198     MReleaseWriteLock(&afs_xdcache);
2199
2200 } /*afs_WriteThroughDSlots*/
2201
2202 /*
2203  * afs_MemGetDSlot
2204  *
2205  * Description:
2206  *      Return a pointer to an freshly initialized dcache entry using
2207  *      a memory-based cache.
2208  *
2209  * Parameters:
2210  *      aslot : Dcache slot to look at.
2211  *      tmpdc : Ptr to dcache entry.
2212  *
2213  * Environment:
2214  *      Nothing interesting.
2215  */
2216
2217 struct dcache *afs_MemGetDSlot(aslot, tmpdc)
2218      register afs_int32 aslot;
2219      register struct dcache *tmpdc;
2220
2221 { /*afs_MemGetDSlot*/
2222
2223     register afs_int32 code;
2224     register struct dcache *tdc;
2225     register char *tfile;
2226
2227     AFS_STATCNT(afs_MemGetDSlot);
2228     if (CheckLock(&afs_xdcache) != -1) osi_Panic("getdslot nolock");
2229     if (aslot < 0 || aslot >= afs_cacheFiles) osi_Panic("getdslot slot");
2230     tdc = afs_indexTable[aslot];
2231     if (tdc) {
2232         QRemove(&tdc->lruq);        /* move to queue head */
2233         QAdd(&afs_DLRU, &tdc->lruq);
2234         tdc->refCount++;
2235         return tdc;
2236     }
2237     if (tmpdc == (struct dcache *)0) {
2238         if (!afs_freeDSList) afs_GetDownDSlot(4); 
2239         if (!afs_freeDSList) {
2240             /* none free, making one is better than a panic */
2241             afs_stats_cmperf.dcacheXAllocs++;   /* count in case we have a leak */
2242             tdc = (struct dcache *) afs_osi_Alloc(sizeof (struct dcache));
2243 #ifdef  AFS_AIX32_ENV
2244             pin((char *)tdc, sizeof(struct dcache));    /* XXX */
2245 #endif
2246         } else {
2247             tdc = afs_freeDSList;
2248             afs_freeDSList = (struct dcache *) tdc->lruq.next;
2249         }
2250         tdc->flags = 0; /* up-to-date, not in free q */
2251         QAdd(&afs_DLRU, &tdc->lruq);
2252         if (tdc->lruq.prev == &tdc->lruq) osi_Panic("lruq 3");
2253     }
2254     else {
2255         tdc = tmpdc;
2256         tdc->f.states = 0;
2257     }
2258     
2259     /* initialize entry */
2260     tdc->f.fid.Cell = 0;
2261     tdc->f.fid.Fid.Volume = 0;
2262     tdc->f.chunk = -1;
2263     hones(tdc->f.versionNo);
2264     tdc->f.inode = aslot;
2265     tdc->flags |= DFEntryMod;
2266     tdc->refCount = 1;
2267     tdc->index = aslot;
2268     afs_indexUnique[aslot] = tdc->f.fid.Fid.Unique;
2269     
2270     if (tmpdc == (struct dcache *)0)
2271         afs_indexTable[aslot] = tdc;
2272     return tdc;
2273
2274 } /*afs_MemGetDSlot*/
2275
2276 unsigned int last_error = 0, lasterrtime = 0;
2277
2278 /*
2279  * afs_UFSGetDSlot
2280  *
2281  * Description:
2282  *      Return a pointer to an freshly initialized dcache entry using
2283  *      a UFS-based disk cache.
2284  *
2285  * Parameters:
2286  *      aslot : Dcache slot to look at.
2287  *      tmpdc : Ptr to dcache entry.
2288  *
2289  * Environment:
2290  *      afs_xdcache lock write-locked.
2291  */
2292 struct dcache *afs_UFSGetDSlot(aslot, tmpdc)
2293     register afs_int32 aslot;
2294     register struct dcache *tmpdc;
2295
2296 { /*afs_UFSGetDSlot*/
2297
2298     register afs_int32 code;
2299     register struct dcache *tdc;
2300
2301     AFS_STATCNT(afs_UFSGetDSlot);
2302     if (CheckLock(&afs_xdcache) != -1) osi_Panic("getdslot nolock");
2303     if (aslot < 0 || aslot >= afs_cacheFiles) osi_Panic("getdslot slot");
2304     tdc = afs_indexTable[aslot];
2305     if (tdc) {
2306 #ifdef  AFS_SUN5_ENVX
2307         mutex_enter(&tdc->lock);
2308 #endif
2309         QRemove(&tdc->lruq);        /* move to queue head */
2310         QAdd(&afs_DLRU, &tdc->lruq);
2311         tdc->refCount++;
2312         return tdc;
2313     }
2314     /* otherwise we should read it in from the cache file */
2315     /*
2316      * If we weren't passed an in-memory region to place the file info,
2317      * we have to allocate one.
2318      */
2319     if (tmpdc == (struct dcache *)0) {
2320         if (!afs_freeDSList) afs_GetDownDSlot(4);
2321         if (!afs_freeDSList) {
2322             /* none free, making one is better than a panic */
2323             afs_stats_cmperf.dcacheXAllocs++;   /* count in case we have a leak */
2324             tdc = (struct dcache *) afs_osi_Alloc(sizeof (struct dcache));
2325 #ifdef  AFS_AIX32_ENV
2326             pin((char *)tdc, sizeof(struct dcache));    /* XXX */
2327 #endif
2328         } else {
2329             tdc = afs_freeDSList;
2330             afs_freeDSList = (struct dcache *) tdc->lruq.next;
2331         }
2332         tdc->flags = 0; /* up-to-date, not in free q */
2333         QAdd(&afs_DLRU, &tdc->lruq);
2334         if (tdc->lruq.prev == &tdc->lruq) osi_Panic("lruq 3");
2335     }
2336     else {
2337         tdc = tmpdc;
2338         tdc->f.states = 0;
2339         tdc->ihint = 0;
2340     }
2341
2342 #ifdef  AFS_SUN5_ENVX
2343     mutex_enter(&tdc->lock);
2344 #endif
2345     /*
2346       * Seek to the aslot'th entry and read it in.
2347       */
2348     code = afs_osi_Read(afs_cacheInodep, sizeof(struct fcache) * aslot + sizeof(struct afs_fheader),
2349                     (char *)(&tdc->f), sizeof(struct fcache));
2350     if (code != sizeof(struct fcache)) {
2351         tdc->f.fid.Cell = 0;
2352         tdc->f.fid.Fid.Volume = 0;
2353         tdc->f.chunk = -1;
2354         hones(tdc->f.versionNo);
2355         tdc->flags |= DFEntryMod;
2356 #if !defined(AFS_SUN5_ENV) && !defined(AFS_OSF_ENV) && !defined(AFS_SGI64_ENV) && !defined(AFS_LINUX20_ENV) && !defined(AFS_DARWIN_ENV) && !defined(AFS_FBSD_ENV)
2357         last_error = getuerror();
2358 #endif
2359         lasterrtime = osi_Time();
2360         afs_indexUnique[aslot] = tdc->f.fid.Fid.Unique;
2361     }
2362     tdc->refCount = 1;
2363     tdc->index = aslot;
2364
2365     /*
2366      * If we didn't read into a temporary dcache region, update the
2367      * slot pointer table.
2368      */
2369     if (tmpdc == (struct dcache *)0)
2370         afs_indexTable[aslot] = tdc;
2371     return tdc;
2372
2373 }  /*afs_UFSGetDSlot*/
2374
2375
2376
2377 /*
2378  * afs_WriteDCache
2379  *
2380  * Description:
2381  *      write a particular dcache entry back to its home in the
2382  *      CacheInfo file.
2383  *
2384  * Parameters:
2385  *      adc   : Pointer to the dcache entry to write.
2386  *      atime : If true, set the modtime on the file to the current time.
2387  *
2388  * Environment:
2389  *      Must be called with the afs_xdcache lock at least read-locked.
2390  *      The reference count is not changed.
2391  */
2392
2393 afs_WriteDCache(adc, atime)
2394     int atime;
2395     register struct dcache *adc;
2396
2397 { /*afs_WriteDCache*/
2398
2399     register struct osi_file *tfile;
2400     register afs_int32 code;
2401
2402     if (cacheDiskType == AFS_FCACHE_TYPE_MEM) return 0;
2403     AFS_STATCNT(afs_WriteDCache);
2404     if (atime)
2405         adc->f.modTime = osi_Time();
2406     /*
2407      * Seek to the right dcache slot and write the in-memory image out to disk.
2408      */
2409     code = afs_osi_Write(afs_cacheInodep, sizeof(struct fcache) * adc->index + sizeof(struct afs_fheader), 
2410                      (char *)(&adc->f), sizeof(struct fcache));
2411     if (code != sizeof(struct fcache)) return EIO;
2412     return 0;
2413
2414 } /*afs_WriteDCache*/
2415
2416
2417
2418 /*
2419  * afs_wakeup
2420  *
2421  * Description:
2422  *      Wake up users of a particular file waiting for stores to take
2423  *      place.
2424  *
2425  * Parameters:
2426  *      avc : Ptr to related vcache entry.
2427  *
2428  * Environment:
2429  *      Nothing interesting.
2430  */
2431
2432 afs_wakeup(avc)
2433     register struct vcache *avc;
2434
2435 { /*afs_wakeup*/
2436
2437     register int i;
2438     register struct brequest *tb;
2439     tb = afs_brs;
2440     AFS_STATCNT(afs_wakeup);
2441     for (i = 0; i < NBRS; i++, tb++) {
2442         /* if request is valid and for this file, we've found it */
2443         if (tb->refCount > 0 && avc == tb->vnode) {
2444
2445             /*
2446              * If CSafeStore is on, then we don't awaken the guy
2447              * waiting for the store until the whole store has finished.
2448              * Otherwise, we do it now.  Note that if CSafeStore is on,
2449              * the BStore routine actually wakes up the user, instead
2450              * of us.
2451              * I think this is redundant now because this sort of thing
2452              * is already being handled by the higher-level code.
2453              */
2454             if ((avc->states & CSafeStore) == 0) {
2455                 tb->code = 0;
2456                 tb->flags |= BUVALID;
2457                 if (tb->flags & BUWAIT) {
2458                     tb->flags &= ~BUWAIT;
2459                     afs_osi_Wakeup(tb);
2460                 }
2461             }
2462             break;
2463         }
2464     }
2465     return 0;
2466
2467 } /*afs_wakeup*/
2468
2469
2470 /*
2471  * afs_InitCacheFile
2472  *
2473  * Description:
2474  *      Given a file name and inode, set up that file to be an
2475  *      active member in the AFS cache.  This also involves checking
2476  *      the usability of its data.
2477  *
2478  * Parameters:
2479  *      afile  : Name of the cache file to initialize.
2480  *      ainode : Inode of the file.
2481  *
2482  * Environment:
2483  *      This function is called only during initialization.
2484  */
2485
2486 int afs_InitCacheFile(afile, ainode)
2487     ino_t ainode;
2488     char *afile;
2489
2490 { /*afs_InitCacheFile*/
2491
2492     register afs_int32 code;
2493 #ifdef AFS_LINUX22_ENV
2494     struct dentry *filevp;
2495 #else
2496     struct vnode *filevp;
2497 #endif
2498     afs_int32 index;
2499     int fileIsBad;
2500     struct osi_file *tfile;
2501     struct osi_stat tstat;
2502     register struct dcache *tdc;
2503
2504     AFS_STATCNT(afs_InitCacheFile);
2505     index = afs_stats_cmperf.cacheNumEntries;
2506     if (index >= afs_cacheFiles) return EINVAL;
2507
2508     MObtainWriteLock(&afs_xdcache,282);
2509     tdc = afs_GetDSlot(index, (struct dcache *)0);
2510     MReleaseWriteLock(&afs_xdcache);
2511     if (afile) {
2512         code = gop_lookupname(afile,
2513                               AFS_UIOSYS,
2514                               0,
2515                               (struct vnode **) 0,
2516                               &filevp);
2517         if (code) {
2518             afs_PutDCache(tdc);
2519             return code;
2520         }
2521         /*
2522          * We have a VN_HOLD on filevp.  Get the useful info out and
2523          * return.  We make use of the fact that the cache is in the
2524          * UFS file system, and just record the inode number.
2525          */
2526 #ifdef AFS_LINUX22_ENV
2527         tdc->f.inode = VTOI(filevp->d_inode)->i_number;
2528         dput(filevp);
2529 #else
2530         tdc->f.inode = afs_vnodeToInumber(filevp);
2531 #ifdef AFS_DEC_ENV
2532         grele(filevp);
2533 #else
2534         AFS_RELE((struct vnode *)filevp);
2535 #endif
2536 #endif /* AFS_LINUX22_ENV */
2537     }
2538     else {
2539         tdc->f.inode = ainode;
2540     }
2541     fileIsBad = 0;
2542     if ((tdc->f.states & DWriting) ||
2543         tdc->f.fid.Fid.Volume == 0) fileIsBad = 1;
2544     tfile = osi_UFSOpen(tdc->f.inode);
2545     code = afs_osi_Stat(tfile, &tstat);
2546     if (code) osi_Panic("initcachefile stat");
2547
2548     /*
2549      * If file size doesn't match the cache info file, it's probably bad.
2550      */
2551     if (tdc->f.chunkBytes != tstat.size) fileIsBad = 1;
2552     tdc->f.chunkBytes = 0;
2553
2554     /*
2555      * If file changed within T (120?) seconds of cache info file, it's
2556      * probably bad.  In addition, if slot changed within last T seconds,
2557      * the cache info file may be incorrectly identified, and so slot
2558      * may be bad.
2559      */
2560     if (cacheInfoModTime < tstat.mtime + 120) fileIsBad = 1;
2561     if (cacheInfoModTime < tdc->f.modTime + 120) fileIsBad = 1;
2562     /* In case write through is behind, make sure cache items entry is
2563      * at least as new as the chunk.
2564      */
2565     if (tdc->f.modTime < tstat.mtime) fileIsBad = 1;
2566     if (fileIsBad) {
2567         tdc->f.fid.Fid.Volume = 0;  /* not in the hash table */
2568         if (tstat.size != 0)
2569             osi_UFSTruncate(tfile, 0);
2570         /* put entry in free cache slot list */
2571         afs_dvnextTbl[tdc->index] = afs_freeDCList;
2572         afs_freeDCList = index;
2573         afs_freeDCCount++;
2574         afs_indexFlags[index] |= IFFree;
2575         afs_indexUnique[index] = 0;
2576     }
2577     else {
2578         /*
2579          * We must put this entry in the appropriate hash tables.
2580          * Note that i is still set from the above DCHash call
2581          */
2582         code = DCHash(&tdc->f.fid, tdc->f.chunk);
2583         afs_dcnextTbl[tdc->index] = afs_dchashTbl[code];        
2584         afs_dchashTbl[code] = tdc->index;
2585         code = DVHash(&tdc->f.fid);
2586         afs_dvnextTbl[tdc->index] = afs_dvhashTbl[code];
2587         afs_dvhashTbl[code] = tdc->index;
2588         afs_AdjustSize(tdc, tstat.size);    /* adjust to new size */
2589         if (tstat.size > 0)
2590             /* has nontrivial amt of data */
2591             afs_indexFlags[index] |= IFEverUsed;
2592         afs_stats_cmperf.cacheFilesReused++;
2593         /*
2594          * Initialize index times to file's mod times; init indexCounter
2595          * to max thereof
2596          */
2597         hset32(afs_indexTimes[index], tstat.atime);
2598         if (hgetlo(afs_indexCounter) < tstat.atime) {
2599             hset32(afs_indexCounter, tstat.atime);
2600         }
2601         afs_indexUnique[index] = tdc->f.fid.Fid.Unique;
2602     } /*File is not bad*/
2603
2604     osi_UFSClose(tfile);
2605     tdc->f.states &= ~DWriting;
2606     tdc->flags &= ~DFEntryMod;
2607     /* don't set f.modTime; we're just cleaning up */
2608     afs_WriteDCache(tdc, 0);
2609     afs_PutDCache(tdc);
2610     afs_stats_cmperf.cacheNumEntries++;
2611     return 0;
2612
2613 } /*afs_InitCacheFile*/
2614
2615
2616 /*Max # of struct dcache's resident at any time*/
2617 /*
2618  * If 'dchint' is enabled then in-memory dcache min is increased because of
2619  * crashes...
2620  */
2621 #define DDSIZE 200
2622
2623 /* 
2624  * afs_dcacheInit
2625  *
2626  * Description:
2627  *      Initialize dcache related variables.
2628  */
2629 void afs_dcacheInit(int afiles, int ablocks, int aDentries, int achunk,
2630                     int aflags)
2631 {
2632     register struct dcache *tdp;
2633     int i;
2634     int code;
2635
2636     afs_freeDCList = NULLIDX;
2637     afs_discardDCList = NULLIDX;
2638     afs_freeDCCount = 0;
2639     afs_freeDSList = (struct dcache *)0;
2640     hzero(afs_indexCounter);
2641
2642     LOCK_INIT(&afs_xdcache, "afs_xdcache");
2643    
2644     /*
2645      * Set chunk size
2646      */
2647     if (achunk) {
2648         if (achunk < 0 || achunk > 30)
2649             achunk = 13;                /* Use default */
2650         AFS_SETCHUNKSIZE(achunk);
2651     }
2652     
2653     if(!aDentries)
2654         aDentries = DDSIZE;
2655     
2656     if(aflags & AFSCALL_INIT_MEMCACHE) {
2657         /*
2658          * Use a memory cache instead of a disk cache
2659          */
2660         cacheDiskType = AFS_FCACHE_TYPE_MEM;
2661         afs_cacheType = &afs_MemCacheOps;
2662         afiles = (afiles < aDentries) ? afiles : aDentries; /* min */
2663         ablocks = afiles * (AFS_FIRSTCSIZE/1024);
2664         /* ablocks is reported in 1K blocks */
2665         code = afs_InitMemCache(afiles * AFS_FIRSTCSIZE, AFS_FIRSTCSIZE, aflags);
2666         if (code != 0) {
2667             printf("afsd: memory cache too large for available memory.\n");
2668             printf("afsd: AFS files cannot be accessed.\n\n");
2669             dcacheDisabled = 1;
2670             afiles = ablocks = 0;
2671         }
2672         else
2673             printf("Memory cache: Allocating %d dcache entries...", aDentries);
2674     } else {
2675         cacheDiskType = AFS_FCACHE_TYPE_UFS;
2676         afs_cacheType = &afs_UfsCacheOps;
2677     }
2678
2679     if (aDentries > 512) 
2680         afs_dhashsize = 2048;
2681     /* initialize hash tables */
2682     afs_dvhashTbl = (afs_int32 *) afs_osi_Alloc(afs_dhashsize * sizeof(afs_int32));
2683     afs_dchashTbl = (afs_int32 *) afs_osi_Alloc(afs_dhashsize * sizeof(afs_int32));
2684     for(i=0;i< afs_dhashsize;i++) {
2685         afs_dvhashTbl[i] = NULLIDX;
2686         afs_dchashTbl[i] = NULLIDX;
2687     }
2688     afs_dvnextTbl = (afs_int32 *) afs_osi_Alloc(afiles * sizeof(afs_int32));
2689     afs_dcnextTbl = (afs_int32 *) afs_osi_Alloc(afiles * sizeof(afs_int32));
2690     for(i=0;i< afiles;i++) {
2691         afs_dvnextTbl[i] = NULLIDX;
2692         afs_dcnextTbl[i] = NULLIDX;
2693     }
2694     
2695     /* Allocate and zero the pointer array to the dcache entries */
2696     afs_indexTable = (struct dcache **)
2697         afs_osi_Alloc(sizeof(struct dcache *) * afiles);
2698     memset((char *)afs_indexTable, 0, sizeof(struct dcache *) * afiles);
2699     afs_indexTimes = (afs_hyper_t *) afs_osi_Alloc(afiles * sizeof(afs_hyper_t));
2700     memset((char *)afs_indexTimes, 0, afiles * sizeof(afs_hyper_t));
2701     afs_indexUnique = (afs_int32 *) afs_osi_Alloc(afiles * sizeof(afs_uint32));
2702     memset((char *)afs_indexUnique, 0, afiles * sizeof(afs_uint32));
2703     afs_indexFlags = (u_char *) afs_osi_Alloc(afiles * sizeof(u_char));
2704     memset((char *)afs_indexFlags, 0, afiles * sizeof(char));
2705     
2706     /* Allocate and thread the struct dcache entries themselves */
2707     tdp = afs_Initial_freeDSList =
2708         (struct dcache *) afs_osi_Alloc(aDentries * sizeof(struct dcache));
2709     memset((char *)tdp, 0, aDentries * sizeof(struct dcache));
2710 #ifdef  AFS_AIX32_ENV
2711     pin((char *)afs_indexTable, sizeof(struct dcache *) * afiles);/* XXX */    
2712     pin((char *)afs_indexTimes, sizeof(afs_hyper_t) * afiles);  /* XXX */    
2713     pin((char *)afs_indexFlags, sizeof(char) * afiles);         /* XXX */    
2714     pin((char *)afs_indexUnique, sizeof(afs_int32) * afiles);   /* XXX */    
2715     pin((char *)tdp, aDentries * sizeof(struct dcache));        /* XXX */    
2716     pin((char *)afs_dvhashTbl, sizeof(afs_int32) * afs_dhashsize);      /* XXX */    
2717     pin((char *)afs_dchashTbl, sizeof(afs_int32) * afs_dhashsize);      /* XXX */    
2718     pin((char *)afs_dcnextTbl, sizeof(afs_int32) * afiles);             /* XXX */    
2719     pin((char *)afs_dvnextTbl, sizeof(afs_int32) * afiles);             /* XXX */    
2720 #endif
2721
2722     afs_freeDSList = &tdp[0];
2723     for(i=0; i < aDentries-1; i++) {
2724         tdp[i].lruq.next = (struct afs_q *) (&tdp[i+1]);
2725     }
2726     tdp[aDentries-1].lruq.next = (struct afs_q *) 0;
2727
2728     afs_stats_cmperf.cacheBlocksOrig = afs_stats_cmperf.cacheBlocksTotal = afs_cacheBlocks = ablocks;
2729     afs_ComputeCacheParms();    /* compute parms based on cache size */
2730
2731     afs_dcentries = aDentries;
2732     afs_blocksUsed = 0;
2733     QInit(&afs_DLRU);
2734
2735
2736 }
2737
2738 /*
2739  * shutdown_dcache
2740  *
2741  */
2742 void shutdown_dcache(void)
2743 {
2744     int i;
2745
2746     afs_osi_Free(afs_dvnextTbl, afs_cacheFiles * sizeof(afs_int32));
2747     afs_osi_Free(afs_dcnextTbl, afs_cacheFiles * sizeof(afs_int32));
2748     afs_osi_Free(afs_indexTable, afs_cacheFiles * sizeof(struct dcache *));
2749     afs_osi_Free(afs_indexTimes, afs_cacheFiles * sizeof(afs_hyper_t));
2750     afs_osi_Free(afs_indexUnique, afs_cacheFiles * sizeof(afs_uint32));
2751     afs_osi_Free(afs_indexFlags, afs_cacheFiles * sizeof(u_char));
2752     afs_osi_Free(afs_Initial_freeDSList, afs_dcentries * sizeof(struct dcache));
2753 #ifdef  AFS_AIX32_ENV
2754     unpin((char *)afs_dcnextTbl, afs_cacheFiles * sizeof(afs_int32));
2755     unpin((char *)afs_dvnextTbl, afs_cacheFiles * sizeof(afs_int32));
2756     unpin((char *)afs_indexTable, afs_cacheFiles * sizeof(struct dcache *));
2757     unpin((char *)afs_indexTimes, afs_cacheFiles * sizeof(afs_hyper_t));
2758     unpin((char *)afs_indexUnique, afs_cacheFiles * sizeof(afs_uint32));
2759     unpin((u_char *)afs_indexFlags, afs_cacheFiles * sizeof(u_char));
2760     unpin(afs_Initial_freeDSList, afs_dcentries * sizeof(struct dcache));
2761 #endif
2762
2763
2764     for(i=0;i< afs_dhashsize;i++) {
2765         afs_dvhashTbl[i] = NULLIDX;
2766         afs_dchashTbl[i] = NULLIDX;
2767     }
2768
2769
2770     afs_blocksUsed = afs_dcentries = 0;
2771     hzero(afs_indexCounter);
2772
2773     afs_freeDCCount =  0;
2774     afs_freeDCList = NULLIDX;
2775     afs_discardDCList = NULLIDX;
2776     afs_freeDSList = afs_Initial_freeDSList = 0;
2777
2778     LOCK_INIT(&afs_xdcache, "afs_xdcache");
2779     QInit(&afs_DLRU);
2780
2781 }