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