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