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