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