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