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