DRead-check-file-size-20021113
[openafs.git] / src / afs / afs_buffer.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 #include <afsconfig.h>
11 #include "afs/param.h"
12
13 RCSID("$Header$");
14
15 #include "afs/sysincludes.h"
16 #include "afsincludes.h"
17 #if !defined(UKERNEL)
18 #include "h/param.h"
19 #include "h/types.h"
20 #include "h/time.h"
21 #if     defined(AFS_AIX31_ENV) || defined(AFS_DEC_ENV)
22 #include "h/limits.h"
23 #endif
24 #if     !defined(AFS_AIX_ENV) && !defined(AFS_SUN5_ENV) && !defined(AFS_SGI_ENV) && !defined(AFS_LINUX20_ENV)
25 #include "h/kernel.h"    /* Doesn't needed, so it should go */
26 #endif
27 #endif /* !defined(UKERNEL) */
28
29 #include "afs/afs_osi.h"
30 #include "afsint.h"
31 #include "afs/lock.h"
32
33 #if !defined(UKERNEL) && !defined(AFS_LINUX20_ENV)
34 #include "h/buf.h"
35 #endif /* !defined(UKERNEL) */
36
37 #include "afs/stds.h"
38 #include "afs/volerrors.h"
39 #include "afs/exporter.h"
40 #include "afs/prs_fs.h"
41 #include "afs/afs_chunkops.h"
42 #include "afs/dir.h"
43
44 #include "afs/afs_stats.h"
45 #include "afs/longc_procs.h"
46 #include "afs/afs.h"
47
48 #ifndef BUF_TIME_MAX
49 #define BUF_TIME_MAX    0x7fffffff
50 #endif
51 /* number of pages per Unix buffer, when we're using Unix buffer pool */
52 #define NPB 4
53 /* page size */
54 #define AFS_BUFFER_PAGESIZE 2048
55 /* log page size */
56 #define LOGPS 11
57 /* If you change any of this PH stuff, make sure you don't break DZap() */
58 /* use last two bits for page */
59 #define PHPAGEMASK 3
60 /* use next five bits for fid */
61 #define PHFIDMASK 124
62 /* page hash table size - this is pretty intertwined with pHash */
63 #define PHSIZE (PHPAGEMASK + PHFIDMASK + 1)
64 /* the pHash macro */
65 #define pHash(fid,page) ((((afs_int32)((fid)[0])) & PHFIDMASK) \
66                          | (page & PHPAGEMASK))
67
68 #ifdef  dirty
69 #undef dirty    /* XXX */
70 #endif
71
72 static struct buffer *Buffers = 0;
73 static char *BufferData;
74
75 #ifdef  AFS_AIX_ENV
76 extern struct buf *geteblk();
77 #endif
78 #ifdef AFS_FBSD_ENV
79 #define timecounter afs_timecounter
80 #endif
81 /* The locks for individual buffer entries are now sometimes obtained while holding the
82  * afs_bufferLock. Thus we now have a locking hierarchy: afs_bufferLock -> Buffers[].lock.
83  */
84 static afs_lock_t afs_bufferLock;
85 static struct buffer *phTable[PHSIZE];  /* page hash table */
86 static int nbuffers;
87 static afs_int32 timecounter;
88
89 /* Prototypes for static routines */
90 static struct buffer *afs_newslot (afs_inode_t *afid, afs_int32 apage,register struct buffer *lp);
91
92 static int dinit_flag = 0;
93 void DInit (int abuffers)
94 {
95     /* Initialize the venus buffer system. */
96     register int i;
97     register struct buffer *tb;
98 #if AFS_USEBUFFERS
99     struct buf *tub;        /* unix buffer for allocation */
100 #endif
101
102     AFS_STATCNT(DInit);
103     if (dinit_flag) return;
104     dinit_flag = 1;
105 #if AFS_USEBUFFERS
106     /* round up to next multiple of NPB, since we allocate multiple pages per chunk */
107     abuffers = ((abuffers-1) | (NPB-1)) + 1;
108 #endif
109     LOCK_INIT(&afs_bufferLock, "afs_bufferLock");
110     Buffers = (struct buffer *) afs_osi_Alloc(abuffers * sizeof(struct buffer));
111 #if !AFS_USEBUFFERS
112     BufferData = (char *) afs_osi_Alloc(abuffers * AFS_BUFFER_PAGESIZE);
113 #endif
114     timecounter = 1;
115     afs_stats_cmperf.bufAlloced = nbuffers = abuffers;
116     for(i=0;i<PHSIZE;i++) phTable[i] = 0;
117     for (i=0;i<abuffers;i++) {
118 #if AFS_USEBUFFERS
119         if ((i & (NPB-1)) == 0) {
120             /* time to allocate a fresh buffer */
121             tub = geteblk(AFS_BUFFER_PAGESIZE*NPB);
122             BufferData = (char *) tub->b_un.b_addr;
123         }
124 #endif
125         /* Fill in each buffer with an empty indication. */
126         tb = &Buffers[i];
127         dirp_Zap(tb->fid);
128         tb->accesstime = 0;
129         tb->lockers = 0;
130 #if AFS_USEBUFFERS
131         if ((i & (NPB-1)) == 0) 
132             tb->bufp = tub;
133         else
134             tb->bufp = 0;
135         tb->data = &BufferData[AFS_BUFFER_PAGESIZE * (i&(NPB-1))];
136 #else
137         tb->data = &BufferData[AFS_BUFFER_PAGESIZE*i];
138 #endif
139         tb->hashIndex = 0;
140         tb->dirty = 0;
141         RWLOCK_INIT(&tb->lock, "buffer lock");
142     }
143     return;
144 }
145
146 char *DRead(register afs_inode_t *fid, register int page)
147 {
148     /* Read a page from the disk. */
149     register struct buffer *tb, *tb2;
150     struct osi_file *tfile;
151     int code;
152
153     AFS_STATCNT(DRead);
154     MObtainWriteLock(&afs_bufferLock,256);
155
156 #define bufmatch(tb) (tb->page == page && dirp_Eq(tb->fid, fid))
157 #define buf_Front(head,parent,p) {(parent)->hashNext = (p)->hashNext; (p)->hashNext= *(head);*(head)=(p);}
158
159     /* this apparently-complicated-looking code is simply an example of
160      * a little bit of loop unrolling, and is a standard linked-list 
161      * traversal trick. It saves a few assignments at the the expense
162      * of larger code size.  This could be simplified by better use of
163      * macros. 
164      */
165     if ((tb = phTable[pHash(fid,page)])) {
166         if (bufmatch(tb)) {
167             MObtainWriteLock(&tb->lock,257);
168             ReleaseWriteLock(&afs_bufferLock);
169             tb->lockers++;
170             tb->accesstime = timecounter++;
171             AFS_STATS(afs_stats_cmperf.bufHits++);
172             MReleaseWriteLock(&tb->lock);
173             return tb->data;
174         }
175         else {
176           register struct buffer **bufhead;
177           bufhead = &( phTable[pHash(fid,page)] );
178           while ((tb2 = tb->hashNext)) {
179             if (bufmatch(tb2)) {
180               buf_Front(bufhead,tb,tb2);
181               MObtainWriteLock(&tb2->lock,258);
182               ReleaseWriteLock(&afs_bufferLock);
183               tb2->lockers++;
184               tb2->accesstime = timecounter++;
185               AFS_STATS(afs_stats_cmperf.bufHits++);
186               MReleaseWriteLock(&tb2->lock);
187               return tb2->data;
188             }
189             if ((tb = tb2->hashNext)) {
190               if (bufmatch(tb)) {
191                 buf_Front(bufhead,tb2,tb);
192                 MObtainWriteLock(&tb->lock,259);
193                 ReleaseWriteLock(&afs_bufferLock);
194                 tb->lockers++;
195                 tb->accesstime = timecounter++;
196                 AFS_STATS(afs_stats_cmperf.bufHits++);
197                 MReleaseWriteLock(&tb->lock);
198                 return tb->data;
199               }
200             }
201             else break;
202           }
203         }
204       }  
205     else tb2 = NULL;
206
207     AFS_STATS(afs_stats_cmperf.bufMisses++);
208     /* can't find it */
209     /* The last thing we looked at was either tb or tb2 (or nothing). That
210      * is at least the oldest buffer on one particular hash chain, so it's 
211      * a pretty good place to start looking for the truly oldest buffer.
212      */
213     tb = afs_newslot(fid, page, (tb ? tb : tb2));
214     if (!tb) {
215         MReleaseWriteLock(&afs_bufferLock);
216         return NULL;
217     }
218     MObtainWriteLock(&tb->lock,260);
219     MReleaseWriteLock(&afs_bufferLock);
220     tb->lockers++;
221     tfile = afs_CFileOpen(fid[0]);
222     if (page * AFS_BUFFER_PAGESIZE >= tfile->size) {
223         dirp_Zap(tb->fid);
224         tb->lockers--;
225         MReleaseWriteLock(&tb->lock);
226         afs_CFileClose(tfile);
227         return NULL;
228     }
229     code = afs_CFileRead(tfile, tb->page * AFS_BUFFER_PAGESIZE,
230                          tb->data, AFS_BUFFER_PAGESIZE);
231     afs_CFileClose(tfile);
232     if (code < AFS_BUFFER_PAGESIZE) {
233         dirp_Zap(tb->fid);
234         tb->lockers--;
235         MReleaseWriteLock(&tb->lock);
236         return NULL;
237     }
238     /* Note that findslot sets the page field in the buffer equal to
239      * what it is searching for. */
240     MReleaseWriteLock(&tb->lock);
241     return tb->data;
242 }
243
244 static void FixupBucket(register struct buffer *ap)
245 {
246     register struct buffer **lp, *tp;
247     register int i;
248     /* first try to get it out of its current hash bucket, in which it
249      * might not be */
250     AFS_STATCNT(FixupBucket);
251     i = ap->hashIndex;
252     lp = &phTable[i];
253     for(tp = *lp; tp; tp=tp->hashNext) {
254         if (tp == ap) {
255             *lp = tp->hashNext;
256             break;
257         }
258         lp = &tp->hashNext;
259     }
260     /* now figure the new hash bucket */
261     i = pHash(ap->fid,ap->page);
262     ap->hashIndex = i;          /* remember where we are for deletion */
263     ap->hashNext = phTable[i];  /* add us to the list */
264     phTable[i] = ap;            /* at the front, since it's LRU */
265 }
266
267 /* lp is pointer to a fairly-old buffer */
268 static struct buffer *afs_newslot (afs_inode_t *afid, afs_int32 apage,register struct buffer *lp)
269 {
270     /* Find a usable buffer slot */
271     register afs_int32 i;
272     afs_int32 lt;
273     register struct buffer *tp;
274     struct osi_file *tfile;
275
276     AFS_STATCNT(afs_newslot);
277     /* we take a pointer here to a buffer which was at the end of an
278      * LRU hash chain.  Odds are, it's one of the older buffers, not
279      * one of the newer.  Having an older buffer to start with may
280      * permit us to avoid a few of the assignments in the "typical
281      * case" for loop below.
282      */
283     if (lp && (lp->lockers == 0)) {
284       lt = lp->accesstime;
285     }
286     else {
287       lp = 0;
288       lt = BUF_TIME_MAX;
289     }
290
291     /* timecounter might have wrapped, if machine is very very busy
292      * and stays up for a long time.  Timecounter mustn't wrap twice
293      * (positive->negative->positive) before calling newslot, but that
294      * would require 2 billion consecutive cache hits... Anyway, the
295      * penalty is only that the cache replacement policy will be
296      * almost MRU for the next ~2 billion DReads...  newslot doesn't
297      * get called nearly as often as DRead, so in order to avoid the
298      * performance penalty of using the hypers, it's worth doing the
299      * extra check here every time.  It's probably cheaper than doing
300      * hcmp, anyway.  There is a little performance hit resulting from
301      * resetting all the access times to 0, but it only happens once
302      * every month or so, and the access times will rapidly sort
303      * themselves back out after just a few more DReads.
304      */
305     if (timecounter < 0) {
306       timecounter = 1;
307       tp = Buffers;
308       for (i=0;i<nbuffers;i++,tp++) {
309         tp->accesstime = 0;
310         if (!lp && !tp->lockers)  /* one is as good as the rest, I guess */
311           lp = tp;
312       }
313     }
314     else {
315       /* this is the typical case */
316       tp = Buffers;
317       for (i=0;i<nbuffers;i++,tp++) {
318         if (tp->lockers == 0) {
319           if (tp->accesstime < lt) {
320             lp = tp;
321             lt = tp->accesstime;
322           }
323         }
324       }
325     }
326
327     if (lp == 0) {
328       /* There are no unlocked buffers -- this used to panic, but that
329        * seems extreme.  To the best of my knowledge, all the callers
330        * of DRead are prepared to handle a zero return.  Some of them
331        * just panic directly, but not all of them. */
332       afs_warn ("all buffers locked");
333       return 0;
334     }
335
336     if (lp->dirty) {
337         tfile = afs_CFileOpen(lp->fid[0]);
338         afs_CFileWrite(tfile, lp->page * AFS_BUFFER_PAGESIZE, 
339                        lp->data, AFS_BUFFER_PAGESIZE);  
340         lp->dirty = 0;
341         afs_CFileClose(tfile);
342         AFS_STATS(afs_stats_cmperf.bufFlushDirty++);
343       }
344
345     /* Now fill in the header. */
346     dirp_Cpy(lp->fid, afid);    /* set this */
347     lp->page = apage;
348     lp->accesstime = timecounter++;
349     FixupBucket(lp);            /* move to the right hash bucket */
350
351     return lp;
352 }
353
354 void DRelease (register struct buffer *bp, int flag)
355 {
356     /* Release a buffer, specifying whether or not the buffer has been
357      * modified by the locker. */
358     register int index;
359 #if AFS_USEBUFFERS
360     register struct buffer *tp;
361 #endif
362
363     AFS_STATCNT(DRelease);
364     if (!bp) return;
365 #if AFS_USEBUFFERS
366     /* look for buffer by scanning Unix buffers for appropriate address */
367     tp = Buffers;
368     for(index = 0; index < nbuffers; index += NPB, tp += NPB) {
369         if ((afs_int32)bp >= (afs_int32)tp->data 
370             && (afs_int32)bp < (afs_int32)tp->data + AFS_BUFFER_PAGESIZE*NPB) {
371             /* we found the right range */
372             index += ((afs_int32)bp - (afs_int32)tp->data) >> LOGPS;
373             break;
374         }
375     }
376 #else
377     index = (((char *)bp)-((char *)BufferData))>>LOGPS;
378 #endif
379     bp = &(Buffers[index]);
380     MObtainWriteLock(&bp->lock,261);
381     bp->lockers--;
382     if (flag) bp->dirty=1;
383     MReleaseWriteLock(&bp->lock);
384 }
385
386 int DVOffset (register void *ap)
387 {
388     /* Return the byte within a file represented by a buffer pointer. */
389     register struct buffer *bp;
390     register int index;
391 #if AFS_USEBUFFERS
392     register struct buffer *tp;
393 #endif
394     AFS_STATCNT(DVOffset);
395     bp=ap;
396 #if AFS_USEBUFFERS
397     /* look for buffer by scanning Unix buffers for appropriate address */
398     tp = Buffers;
399     for(index = 0; index < nbuffers; index += NPB, tp += NPB) {
400         if ((afs_int32)bp >= (afs_int32)tp->data && (afs_int32)bp < (afs_int32)tp->data + AFS_BUFFER_PAGESIZE*NPB) {
401             /* we found the right range */
402             index += ((afs_int32)bp - (afs_int32)tp->data) >> LOGPS;
403             break;
404         }
405     }
406 #else
407     index = (((char *)bp)-((char *)BufferData))>>LOGPS;
408 #endif
409     if (index<0 || index >= nbuffers) return -1;
410     bp = &(Buffers[index]);
411     return AFS_BUFFER_PAGESIZE*bp->page+(int)(((char *)ap)-bp->data);
412 }
413
414 /* 1/1/91 - I've modified the hash function to take the page as well
415  * as the *fid, so that lookup will be a bit faster.  That presents some
416  * difficulties for Zap, which now has to have some knowledge of the nature
417  * of the hash function.  Oh well.  This should use the list traversal 
418  * method of DRead...
419  */
420 void DZap (afs_inode_t *fid)
421 {
422     register int i;
423     /* Destroy all buffers pertaining to a particular fid. */
424     register struct buffer *tb;
425     
426     AFS_STATCNT(DZap);
427     MObtainReadLock(&afs_bufferLock);
428
429     for (i=0;i<=PHPAGEMASK;i++)
430     for(tb=phTable[pHash(fid,i)]; tb; tb=tb->hashNext)
431         if (dirp_Eq(tb->fid,fid)) {
432             MObtainWriteLock(&tb->lock,262);
433             dirp_Zap(tb->fid);
434             tb->dirty = 0;
435             MReleaseWriteLock(&tb->lock);
436         }
437     MReleaseReadLock(&afs_bufferLock);
438 }
439
440 void DFlush (void)
441 {
442     /* Flush all the modified buffers. */
443     register int i;
444     register struct buffer *tb;
445     struct osi_file *tfile;
446
447     AFS_STATCNT(DFlush);
448     tb = Buffers;
449     MObtainReadLock(&afs_bufferLock);
450     for(i=0;i<nbuffers;i++,tb++) {
451         if (tb->dirty) {
452             MObtainWriteLock(&tb->lock,263);
453             tb->lockers++;
454             MReleaseReadLock(&afs_bufferLock);
455             if (tb->dirty) {
456                 tfile = afs_CFileOpen(tb->fid[0]);
457                 afs_CFileWrite(tfile, tb->page * AFS_BUFFER_PAGESIZE,
458                                tb->data, AFS_BUFFER_PAGESIZE);
459                 tb->dirty = 0;  /* Clear the dirty flag */
460                 afs_CFileClose(tfile);
461             }
462             tb->lockers--;
463             MReleaseWriteLock(&tb->lock);
464             MObtainReadLock(&afs_bufferLock);
465         }
466     }
467     MReleaseReadLock(&afs_bufferLock);
468 }
469
470 char *DNew (register afs_inode_t *fid, register int page)
471 {
472     /* Same as read, only do *not* even try to read the page, since it probably doesn't exist. */
473     register struct buffer *tb;
474     AFS_STATCNT(DNew);
475     MObtainWriteLock(&afs_bufferLock,264);
476     if ((tb = afs_newslot(fid,page,NULL)) == 0) {
477         MReleaseWriteLock(&afs_bufferLock);
478         return 0;
479     }
480     MObtainWriteLock(&tb->lock,265);
481     MReleaseWriteLock(&afs_bufferLock);
482     tb->lockers++;
483     MReleaseWriteLock(&tb->lock);
484     return tb->data;
485 }
486
487 void shutdown_bufferpackage(void)
488 {
489 #if AFS_USEBUFFERS
490     register struct buffer *tp;
491 #endif
492     int i;
493     extern int afs_cold_shutdown;
494
495     AFS_STATCNT(shutdown_bufferpackage);
496     /* Free all allocated Buffers and associated buffer pages */
497     DFlush();
498     if (afs_cold_shutdown) {
499       dinit_flag = 0;
500 #if !AFS_USEBUFFERS
501       afs_osi_Free(BufferData, nbuffers * AFS_BUFFER_PAGESIZE);
502 #else
503       tp = Buffers;
504       for (i=0; i < nbuffers; i+= NPB, tp += NPB) {
505         /* The following check shouldn't be necessary and it will be removed soon */
506         if (!tp->bufp) 
507             afs_warn("shutdown_bufferpackage: bufp == 0!! Shouldn't happen\n");
508         else {
509             brelse(tp->bufp);
510             tp->bufp = 0;
511         }
512       }
513 #endif
514       afs_osi_Free(Buffers, nbuffers * sizeof(struct buffer));
515       nbuffers = 0;
516       timecounter = 1;
517       for(i=0;i<PHSIZE;i++) phTable[i] = 0;
518       memset((char *)&afs_bufferLock, 0, sizeof(afs_lock_t));
519   }
520 }