prototypes-fixes-20020821
[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 "../afs/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/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 (ino_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 ino_t *fid, register int page)
147 {
148     /* Read a page from the disk. */
149     register struct buffer *tb, *tb2;
150     void *tfile;
151     register afs_int32 code, *sizep;
152
153     AFS_STATCNT(DRead);
154     MObtainWriteLock(&afs_bufferLock,256);
155
156 /* some new code added 1/1/92 */
157 #define bufmatch(tb) (tb->page == page && dirp_Eq(tb->fid, fid))
158 #define buf_Front(head,parent,p) {(parent)->hashNext = (p)->hashNext; (p)->hashNext= *(head);*(head)=(p);}
159
160     /* this apparently-complicated-looking code is simply an example of
161      * a little bit of loop unrolling, and is a standard linked-list 
162      * traversal trick. It saves a few assignments at the the expense
163      * of larger code size.  This could be simplified by better use of
164      * macros. 
165      */
166     if ((tb = phTable[pHash(fid,page)])) {
167         if (bufmatch(tb)) {
168             MObtainWriteLock(&tb->lock,257);
169             ReleaseWriteLock(&afs_bufferLock);
170             tb->lockers++;
171             tb->accesstime = timecounter++;
172             AFS_STATS(afs_stats_cmperf.bufHits++);
173             MReleaseWriteLock(&tb->lock);
174             return tb->data;
175         }
176         else {
177           register struct buffer **bufhead;
178           bufhead = &( phTable[pHash(fid,page)] );
179           while ((tb2 = tb->hashNext)) {
180             if (bufmatch(tb2)) {
181               buf_Front(bufhead,tb,tb2);
182               MObtainWriteLock(&tb2->lock,258);
183               ReleaseWriteLock(&afs_bufferLock);
184               tb2->lockers++;
185               tb2->accesstime = timecounter++;
186               AFS_STATS(afs_stats_cmperf.bufHits++);
187               MReleaseWriteLock(&tb2->lock);
188               return tb2->data;
189             }
190             if ((tb = tb2->hashNext)) {
191               if (bufmatch(tb)) {
192                 buf_Front(bufhead,tb2,tb);
193                 MObtainWriteLock(&tb->lock,259);
194                 ReleaseWriteLock(&afs_bufferLock);
195                 tb->lockers++;
196                 tb->accesstime = timecounter++;
197                 AFS_STATS(afs_stats_cmperf.bufHits++);
198                 MReleaseWriteLock(&tb->lock);
199                 return tb->data;
200               }
201             }
202             else break;
203           }
204         }
205       }  
206     else tb2 = NULL;
207
208     AFS_STATS(afs_stats_cmperf.bufMisses++);
209     /* can't find it */
210     /* The last thing we looked at was either tb or tb2 (or nothing). That
211      * is at least the oldest buffer on one particular hash chain, so it's 
212      * a pretty good place to start looking for the truly oldest buffer.
213      */
214     tb = afs_newslot(fid, page, (tb ? tb : tb2));
215     if (!tb) {
216       MReleaseWriteLock(&afs_bufferLock);
217       return 0;
218     }
219     MObtainWriteLock(&tb->lock,260);
220     MReleaseWriteLock(&afs_bufferLock);
221     tb->lockers++;
222     tfile = afs_CFileOpen(fid[0]);
223     sizep = (afs_int32 *)tfile;
224     if (page * AFS_BUFFER_PAGESIZE >= *sizep) {
225         dirp_Zap(tb->fid);
226         tb->lockers--;
227         MReleaseWriteLock(&tb->lock);
228         afs_CFileClose(tfile);
229         return 0;
230     }
231     code = afs_CFileRead(tfile, tb->page * AFS_BUFFER_PAGESIZE,
232                          tb->data, AFS_BUFFER_PAGESIZE);
233     afs_CFileClose(tfile);
234     if (code < AFS_BUFFER_PAGESIZE) {
235         dirp_Zap(tb->fid);
236         tb->lockers--;
237         MReleaseWriteLock(&tb->lock);
238         return 0;
239     }
240     /* Note that findslot sets the page field in the buffer equal to
241      * what it is searching for. */
242     MReleaseWriteLock(&tb->lock);
243     return tb->data;
244 }
245
246 static void FixupBucket(register struct buffer *ap)
247 {
248     register struct buffer **lp, *tp;
249     register int i;
250     /* first try to get it out of its current hash bucket, in which it
251      * might not be */
252     AFS_STATCNT(FixupBucket);
253     i = ap->hashIndex;
254     lp = &phTable[i];
255     for(tp = *lp; tp; tp=tp->hashNext) {
256         if (tp == ap) {
257             *lp = tp->hashNext;
258             break;
259         }
260         lp = &tp->hashNext;
261     }
262     /* now figure the new hash bucket */
263     i = pHash(ap->fid,ap->page);
264     ap->hashIndex = i;          /* remember where we are for deletion */
265     ap->hashNext = phTable[i];  /* add us to the list */
266     phTable[i] = ap;            /* at the front, since it's LRU */
267 }
268
269 /* lp is pointer to a fairly-old buffer */
270 static struct buffer *afs_newslot (ino_t *afid, afs_int32 apage,register struct buffer *lp)
271 {
272     /* Find a usable buffer slot */
273     register afs_int32 i;
274     afs_int32 lt;
275     register struct buffer *tp;
276     void *tfile;
277
278     AFS_STATCNT(afs_newslot);
279     /* we take a pointer here to a buffer which was at the end of an
280      * LRU hash chain.  Odds are, it's one of the older buffers, not
281      * one of the newer.  Having an older buffer to start with may
282      * permit us to avoid a few of the assignments in the "typical
283      * case" for loop below.
284      */
285     if (lp && (lp->lockers == 0)) {
286       lt = lp->accesstime;
287     }
288     else {
289       lp = 0;
290       lt = BUF_TIME_MAX;
291     }
292
293     /* timecounter might have wrapped, if machine is very very busy
294      * and stays up for a long time.  Timecounter mustn't wrap twice
295      * (positive->negative->positive) before calling newslot, but that
296      * would require 2 billion consecutive cache hits... Anyway, the
297      * penalty is only that the cache replacement policy will be
298      * almost MRU for the next ~2 billion DReads...  newslot doesn't
299      * get called nearly as often as DRead, so in order to avoid the
300      * performance penalty of using the hypers, it's worth doing the
301      * extra check here every time.  It's probably cheaper than doing
302      * hcmp, anyway.  There is a little performance hit resulting from
303      * resetting all the access times to 0, but it only happens once
304      * every month or so, and the access times will rapidly sort
305      * themselves back out after just a few more DReads.
306      */
307     if (timecounter < 0) {
308       timecounter = 1;
309       tp = Buffers;
310       for (i=0;i<nbuffers;i++,tp++) {
311         tp->accesstime = 0;
312         if (!lp && !tp->lockers)  /* one is as good as the rest, I guess */
313           lp = tp;
314       }
315     }
316     else {
317       /* this is the typical case */
318       tp = Buffers;
319       for (i=0;i<nbuffers;i++,tp++) {
320         if (tp->lockers == 0) {
321           if (tp->accesstime < lt) {
322             lp = tp;
323             lt = tp->accesstime;
324           }
325         }
326       }
327     }
328
329     if (lp == 0) {
330       /* There are no unlocked buffers -- this used to panic, but that
331        * seems extreme.  To the best of my knowledge, all the callers
332        * of DRead are prepared to handle a zero return.  Some of them
333        * just panic directly, but not all of them. */
334       afs_warn ("all buffers locked");
335       return 0;
336     }
337
338     if (lp->dirty) {
339         tfile = afs_CFileOpen(lp->fid[0]);
340         afs_CFileWrite(tfile, lp->page * AFS_BUFFER_PAGESIZE, 
341                        lp->data, AFS_BUFFER_PAGESIZE);  
342         lp->dirty = 0;
343         afs_CFileClose(tfile);
344         AFS_STATS(afs_stats_cmperf.bufFlushDirty++);
345       }
346
347     /* Now fill in the header. */
348     dirp_Cpy(lp->fid, afid);    /* set this */
349     lp->page = apage;
350     lp->accesstime = timecounter++;
351     FixupBucket(lp);            /* move to the right hash bucket */
352
353     return lp;
354 }
355
356 void DRelease (register struct buffer *bp, int flag)
357 {
358     /* Release a buffer, specifying whether or not the buffer has been
359      * modified by the locker. */
360     register int index;
361 #if AFS_USEBUFFERS
362     register struct buffer *tp;
363 #endif
364
365     AFS_STATCNT(DRelease);
366     if (!bp) return;
367 #if AFS_USEBUFFERS
368     /* look for buffer by scanning Unix buffers for appropriate address */
369     tp = Buffers;
370     for(index = 0; index < nbuffers; index += NPB, tp += NPB) {
371         if ((afs_int32)bp >= (afs_int32)tp->data 
372             && (afs_int32)bp < (afs_int32)tp->data + AFS_BUFFER_PAGESIZE*NPB) {
373             /* we found the right range */
374             index += ((afs_int32)bp - (afs_int32)tp->data) >> LOGPS;
375             break;
376         }
377     }
378 #else
379     index = (((char *)bp)-((char *)BufferData))>>LOGPS;
380 #endif
381     bp = &(Buffers[index]);
382     MObtainWriteLock(&bp->lock,261);
383     bp->lockers--;
384     if (flag) bp->dirty=1;
385     MReleaseWriteLock(&bp->lock);
386 }
387
388 int DVOffset (register void *ap)
389 {
390     /* Return the byte within a file represented by a buffer pointer. */
391     register struct buffer *bp;
392     register int index;
393 #if AFS_USEBUFFERS
394     register struct buffer *tp;
395 #endif
396     AFS_STATCNT(DVOffset);
397     bp=ap;
398 #if AFS_USEBUFFERS
399     /* look for buffer by scanning Unix buffers for appropriate address */
400     tp = Buffers;
401     for(index = 0; index < nbuffers; index += NPB, tp += NPB) {
402         if ((afs_int32)bp >= (afs_int32)tp->data && (afs_int32)bp < (afs_int32)tp->data + AFS_BUFFER_PAGESIZE*NPB) {
403             /* we found the right range */
404             index += ((afs_int32)bp - (afs_int32)tp->data) >> LOGPS;
405             break;
406         }
407     }
408 #else
409     index = (((char *)bp)-((char *)BufferData))>>LOGPS;
410 #endif
411     if (index<0 || index >= nbuffers) return -1;
412     bp = &(Buffers[index]);
413     return AFS_BUFFER_PAGESIZE*bp->page+(int)(((char *)ap)-bp->data);
414 }
415
416 /* 1/1/91 - I've modified the hash function to take the page as well
417  * as the *fid, so that lookup will be a bit faster.  That presents some
418  * difficulties for Zap, which now has to have some knowledge of the nature
419  * of the hash function.  Oh well.  This should use the list traversal 
420  * method of DRead...
421  */
422 void DZap (ino_t *fid)
423 {
424     register int i;
425     /* Destroy all buffers pertaining to a particular fid. */
426     register struct buffer *tb;
427     
428     AFS_STATCNT(DZap);
429     MObtainReadLock(&afs_bufferLock);
430
431     for (i=0;i<=PHPAGEMASK;i++)
432     for(tb=phTable[pHash(fid,i)]; tb; tb=tb->hashNext)
433         if (dirp_Eq(tb->fid,fid)) {
434             MObtainWriteLock(&tb->lock,262);
435             dirp_Zap(tb->fid);
436             tb->dirty = 0;
437             MReleaseWriteLock(&tb->lock);
438         }
439     MReleaseReadLock(&afs_bufferLock);
440 }
441
442 void DFlush (void)
443 {
444     /* Flush all the modified buffers. */
445     register int i;
446     register struct buffer *tb;
447     void *tfile;
448
449     AFS_STATCNT(DFlush);
450     tb = Buffers;
451     MObtainReadLock(&afs_bufferLock);
452     for(i=0;i<nbuffers;i++,tb++) {
453         if (tb->dirty) {
454             MObtainWriteLock(&tb->lock,263);
455             tb->lockers++;
456             MReleaseReadLock(&afs_bufferLock);
457             if (tb->dirty) {
458                 tfile = afs_CFileOpen(tb->fid[0]);
459                 afs_CFileWrite(tfile, tb->page * AFS_BUFFER_PAGESIZE,
460                                tb->data, AFS_BUFFER_PAGESIZE);
461                 tb->dirty = 0;  /* Clear the dirty flag */
462                 afs_CFileClose(tfile);
463             }
464             tb->lockers--;
465             MReleaseWriteLock(&tb->lock);
466             MObtainReadLock(&afs_bufferLock);
467         }
468     }
469     MReleaseReadLock(&afs_bufferLock);
470 }
471
472 char *DNew (register ino_t *fid, register int page)
473 {
474     /* Same as read, only do *not* even try to read the page, since it probably doesn't exist. */
475     register struct buffer *tb;
476     AFS_STATCNT(DNew);
477     MObtainWriteLock(&afs_bufferLock,264);
478     if ((tb = afs_newslot(fid,page,NULL)) == 0) {
479         MReleaseWriteLock(&afs_bufferLock);
480         return 0;
481     }
482     MObtainWriteLock(&tb->lock,265);
483     MReleaseWriteLock(&afs_bufferLock);
484     tb->lockers++;
485     MReleaseWriteLock(&tb->lock);
486     return tb->data;
487 }
488
489 void shutdown_bufferpackage(void)
490 {
491 #if AFS_USEBUFFERS
492     register struct buffer *tp;
493 #endif
494     int i;
495     extern int afs_cold_shutdown;
496
497     AFS_STATCNT(shutdown_bufferpackage);
498     /* Free all allocated Buffers and associated buffer pages */
499     DFlush();
500     if (afs_cold_shutdown) {
501       dinit_flag = 0;
502 #if !AFS_USEBUFFERS
503       afs_osi_Free(BufferData, nbuffers * AFS_BUFFER_PAGESIZE);
504 #else
505       tp = Buffers;
506       for (i=0; i < nbuffers; i+= NPB, tp += NPB) {
507         /* The following check shouldn't be necessary and it will be removed soon */
508         if (!tp->bufp) 
509             afs_warn("shutdown_bufferpackage: bufp == 0!! Shouldn't happen\n");
510         else {
511             brelse(tp->bufp);
512             tp->bufp = 0;
513         }
514       }
515 #endif
516       afs_osi_Free(Buffers, nbuffers * sizeof(struct buffer));
517       nbuffers = 0;
518       timecounter = 1;
519       for(i=0;i<PHSIZE;i++) phTable[i] = 0;
520       memset((char *)&afs_bufferLock, 0, sizeof(afs_lock_t));
521   }
522 }