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