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