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