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