linux-warning-reduction-20090318
[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         AFS_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 #if defined(LINUX_USE_FH)
235     tfile = afs_CFileOpen(&adc->f.fh, adc->f.fh_type);
236 #else
237     tfile = afs_CFileOpen(adc->f.inode);
238 #endif
239     code =
240         afs_CFileRead(tfile, tb->page * AFS_BUFFER_PAGESIZE, tb->data,
241                       AFS_BUFFER_PAGESIZE);
242     afs_CFileClose(tfile);
243     if (code < AFS_BUFFER_PAGESIZE) {
244         tb->fid = NULLIDX;
245         tb->inode = 0;
246         tb->lockers--;
247         MReleaseWriteLock(&tb->lock);
248         return NULL;
249     }
250     /* Note that findslot sets the page field in the buffer equal to
251      * what it is searching for. */
252     MReleaseWriteLock(&tb->lock);
253     return tb->data;
254 }
255
256 static void
257 FixupBucket(register struct buffer *ap)
258 {
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 /* lp is pointer to a fairly-old buffer */
281 static struct buffer *
282 afs_newslot(struct dcache *adc, afs_int32 apage, register struct buffer *lp)
283 {
284     /* Find a usable buffer slot */
285     register afs_int32 i;
286     afs_int32 lt;
287     register struct buffer *tp;
288     struct osi_file *tfile;
289
290     AFS_STATCNT(afs_newslot);
291     /* we take a pointer here to a buffer which was at the end of an
292      * LRU hash chain.  Odds are, it's one of the older buffers, not
293      * one of the newer.  Having an older buffer to start with may
294      * permit us to avoid a few of the assignments in the "typical
295      * case" for loop below.
296      */
297     if (lp && (lp->lockers == 0)) {
298         lt = lp->accesstime;
299     } else {
300         lp = 0;
301         lt = BUF_TIME_MAX;
302     }
303
304     /* timecounter might have wrapped, if machine is very very busy
305      * and stays up for a long time.  Timecounter mustn't wrap twice
306      * (positive->negative->positive) before calling newslot, but that
307      * would require 2 billion consecutive cache hits... Anyway, the
308      * penalty is only that the cache replacement policy will be
309      * almost MRU for the next ~2 billion DReads...  newslot doesn't
310      * get called nearly as often as DRead, so in order to avoid the
311      * performance penalty of using the hypers, it's worth doing the
312      * extra check here every time.  It's probably cheaper than doing
313      * hcmp, anyway.  There is a little performance hit resulting from
314      * resetting all the access times to 0, but it only happens once
315      * every month or so, and the access times will rapidly sort
316      * themselves back out after just a few more DReads.
317      */
318     if (timecounter < 0) {
319         timecounter = 1;
320         tp = Buffers;
321         for (i = 0; i < nbuffers; i++, tp++) {
322             tp->accesstime = 0;
323             if (!lp && !tp->lockers)    /* one is as good as the rest, I guess */
324                 lp = tp;
325         }
326     } else {
327         /* this is the typical case */
328         tp = Buffers;
329         for (i = 0; i < nbuffers; i++, tp++) {
330             if (tp->lockers == 0) {
331                 if (tp->accesstime < lt) {
332                     lp = tp;
333                     lt = tp->accesstime;
334                 }
335             }
336         }
337     }
338
339     if (lp == 0) {
340         /* There are no unlocked buffers -- this used to panic, but that
341          * seems extreme.  To the best of my knowledge, all the callers
342          * of DRead are prepared to handle a zero return.  Some of them
343          * just panic directly, but not all of them. */
344         afs_warn("all buffers locked");
345         return 0;
346     }
347
348     if (lp->dirty) {
349         /* see DFlush for rationale for not getting and locking the dcache */
350 #if defined(LINUX_USE_FH)
351         tfile = afs_CFileOpen(&lp->fh, lp->fh_type);
352 #else
353         tfile = afs_CFileOpen(lp->inode);
354 #endif
355         afs_CFileWrite(tfile, lp->page * AFS_BUFFER_PAGESIZE, lp->data,
356                        AFS_BUFFER_PAGESIZE);
357         lp->dirty = 0;
358         afs_CFileClose(tfile);
359         AFS_STATS(afs_stats_cmperf.bufFlushDirty++);
360     }
361
362     /* Now fill in the header. */
363     lp->fid = adc->index;
364 #if defined(LINUX_USE_FH)
365     memcpy(&lp->fh, &adc->f.fh, sizeof(struct fid));
366     lp->fh_type = adc->f.fh_type;
367 #else
368     lp->inode = adc->f.inode;
369 #endif
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
378 DRelease(register struct buffer *bp, int flag)
379 {
380     /* Release a buffer, specifying whether or not the buffer has been
381      * modified by the locker. */
382     register int index;
383 #if defined(AFS_USEBUFFERS)
384     register struct buffer *tp;
385 #endif
386
387     AFS_STATCNT(DRelease);
388     if (!bp)
389         return;
390 #if defined(AFS_USEBUFFERS)
391     /* look for buffer by scanning Unix buffers for appropriate address */
392     tp = Buffers;
393     for (index = 0; index < nbuffers; index += NPB, tp += NPB) {
394         if ((afs_int32) bp >= (afs_int32) tp->data
395             && (afs_int32) bp <
396             (afs_int32) tp->data + AFS_BUFFER_PAGESIZE * NPB) {
397             /* we found the right range */
398             index += ((afs_int32) bp - (afs_int32) tp->data) >> LOGPS;
399             break;
400         }
401     }
402 #else
403     index = (((char *)bp) - ((char *)BufferData)) >> LOGPS;
404 #endif
405     bp = &(Buffers[index]);
406     MObtainWriteLock(&bp->lock, 261);
407     bp->lockers--;
408     if (flag)
409         bp->dirty = 1;
410     MReleaseWriteLock(&bp->lock);
411 }
412
413 int
414 DVOffset(register void *ap)
415 {
416     /* Return the byte within a file represented by a buffer pointer. */
417     register struct buffer *bp;
418     register int index;
419 #if defined(AFS_USEBUFFERS)
420     register struct buffer *tp;
421 #endif
422     AFS_STATCNT(DVOffset);
423     bp = ap;
424 #if defined(AFS_USEBUFFERS)
425     /* look for buffer by scanning Unix buffers for appropriate address */
426     tp = Buffers;
427     for (index = 0; index < nbuffers; index += NPB, tp += NPB) {
428         if ((afs_int32) bp >= (afs_int32) tp->data
429             && (afs_int32) bp <
430             (afs_int32) tp->data + AFS_BUFFER_PAGESIZE * NPB) {
431             /* we found the right range */
432             index += ((afs_int32) bp - (afs_int32) tp->data) >> LOGPS;
433             break;
434         }
435     }
436 #else
437     index = (((char *)bp) - ((char *)BufferData)) >> LOGPS;
438 #endif
439     if (index < 0 || index >= nbuffers)
440         return -1;
441     bp = &(Buffers[index]);
442     return AFS_BUFFER_PAGESIZE * bp->page + (int)(((char *)ap) - bp->data);
443 }
444
445 /*! 
446  * Zap one dcache entry: destroy one FID's buffers.
447  *
448  * 1/1/91 - I've modified the hash function to take the page as well
449  * as the *fid, so that lookup will be a bit faster.  That presents some
450  * difficulties for Zap, which now has to have some knowledge of the nature
451  * of the hash function.  Oh well.  This should use the list traversal 
452  * method of DRead...
453  *
454  * \param adc The dcache entry to be zapped.
455  */
456 void
457 DZap(struct dcache *adc)
458 {
459     register int i;
460     /* Destroy all buffers pertaining to a particular fid. */
461     register struct buffer *tb;
462
463     AFS_STATCNT(DZap);
464     MObtainReadLock(&afs_bufferLock);
465
466     for (i = 0; i <= PHPAGEMASK; i++)
467         for (tb = phTable[pHash(adc->index, i)]; tb; tb = tb->hashNext)
468             if (tb->fid == adc->index) {
469                 MObtainWriteLock(&tb->lock, 262);
470                 tb->fid = NULLIDX;
471                 tb->inode = 0;
472                 tb->dirty = 0;
473                 MReleaseWriteLock(&tb->lock);
474             }
475     MReleaseReadLock(&afs_bufferLock);
476 }
477
478 static void
479 DFlushBuffer(struct buffer *ab) {
480     struct osi_file *tfile;
481     
482 #if defined(LINUX_USE_FH)
483     tfile = afs_CFileOpen(&ab->fh, ab->fh_type);
484 #else
485     tfile = afs_CFileOpen(ab->inode);
486 #endif
487     afs_CFileWrite(tfile, ab->page * AFS_BUFFER_PAGESIZE,
488                    ab->data, AFS_BUFFER_PAGESIZE);
489     ab->dirty = 0;      /* Clear the dirty flag */
490     afs_CFileClose(tfile);
491 }
492
493 void
494 DFlushDCache(struct dcache *adc) 
495 {
496     int i;
497     struct buffer *tb;
498
499     ObtainReadLock(&afs_bufferLock);
500
501     for (i = 0; i <= PHPAGEMASK; i++)
502         for (tb = phTable[pHash(adc->index, i)]; tb; tb = tb->hashNext)
503             if (tb->fid == adc->index) {
504                 ObtainWriteLock(&tb->lock, 701);
505                 tb->lockers++;
506                 ReleaseReadLock(&afs_bufferLock);
507                 if (tb->dirty) {
508                     DFlushBuffer(tb);
509                 }
510                 tb->lockers--;
511                 ReleaseWriteLock(&tb->lock);
512                 ObtainReadLock(&afs_bufferLock);
513             }
514
515     ReleaseReadLock(&afs_bufferLock);
516 }
517
518 void
519 DFlush(void)
520 {
521     /* Flush all the modified buffers. */
522     register int i;
523     register struct buffer *tb;
524
525     AFS_STATCNT(DFlush);
526     tb = Buffers;
527     MObtainReadLock(&afs_bufferLock);
528     for (i = 0; i < nbuffers; i++, tb++) {
529         if (tb->dirty) {
530             MObtainWriteLock(&tb->lock, 263);
531             tb->lockers++;
532             MReleaseReadLock(&afs_bufferLock);
533             if (tb->dirty) {
534                 /* it seems safe to do this I/O without having the dcache
535                  * locked, since the only things that will update the data in
536                  * a directory are the buffer package, which holds the relevant
537                  * tb->lock while doing the write, or afs_GetDCache, which 
538                  * DZap's the directory while holding the dcache lock.
539                  * It is not possible to lock the dcache or even call
540                  * afs_GetDSlot to map the index to the dcache since the dir
541                  * package's caller has some dcache object locked already (so
542                  * we cannot lock afs_xdcache). In addition, we cannot obtain
543                  * a dcache lock while holding the tb->lock of the same file
544                  * since that can deadlock with DRead/DNew */
545                 DFlushBuffer(tb);
546             }
547             tb->lockers--;
548             MReleaseWriteLock(&tb->lock);
549             MObtainReadLock(&afs_bufferLock);
550         }
551     }
552     MReleaseReadLock(&afs_bufferLock);
553 }
554
555 void *
556 DNew(register struct dcache *adc, register int page)
557 {
558     /* Same as read, only do *not* even try to read the page, since it probably doesn't exist. */
559     register struct buffer *tb;
560     AFS_STATCNT(DNew);
561     MObtainWriteLock(&afs_bufferLock, 264);
562     if ((tb = afs_newslot(adc, page, NULL)) == 0) {
563         MReleaseWriteLock(&afs_bufferLock);
564         return 0;
565     }
566     /* extend the chunk, if needed */
567     /* Do it now, not in DFlush or afs_newslot when the data is written out,
568      * since now our caller has adc->lock writelocked, and we can't acquire
569      * that lock (or even map from a fid to a dcache) in afs_newslot or
570      * DFlush due to lock hierarchy issues */
571     if ((page + 1) * AFS_BUFFER_PAGESIZE > adc->f.chunkBytes) {
572         afs_AdjustSize(adc, (page + 1) * AFS_BUFFER_PAGESIZE);
573         afs_WriteDCache(adc, 1);
574     }
575     MObtainWriteLock(&tb->lock, 265);
576     MReleaseWriteLock(&afs_bufferLock);
577     tb->lockers++;
578     MReleaseWriteLock(&tb->lock);
579     return tb->data;
580 }
581
582 void
583 shutdown_bufferpackage(void)
584 {
585 #if defined(AFS_USEBUFFERS)
586     register struct buffer *tp;
587 #endif
588     int i;
589
590     AFS_STATCNT(shutdown_bufferpackage);
591     /* Free all allocated Buffers and associated buffer pages */
592     DFlush();
593     if (afs_cold_shutdown) {
594         dinit_flag = 0;
595 #if !defined(AFS_USEBUFFERS)
596         afs_osi_Free(BufferData, nbuffers * AFS_BUFFER_PAGESIZE);
597 #else
598         tp = Buffers;
599         for (i = 0; i < nbuffers; i += NPB, tp += NPB) {
600             /* The following check shouldn't be necessary and it will be removed soon */
601             if (!tp->bufp)
602                 afs_warn
603                     ("shutdown_bufferpackage: bufp == 0!! Shouldn't happen\n");
604             else {
605                 brelse(tp->bufp);
606                 tp->bufp = 0;
607             }
608         }
609 #endif
610         afs_osi_Free(Buffers, nbuffers * sizeof(struct buffer));
611         nbuffers = 0;
612         timecounter = 1;
613         for (i = 0; i < PHSIZE; i++)
614             phTable[i] = 0;
615         memset((char *)&afs_bufferLock, 0, sizeof(afs_lock_t));
616     }
617 }