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