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