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