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