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