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