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