afs: Cope with afs_GetValidDSlot errors
[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
14 #include "afs/sysincludes.h"
15 #include "afsincludes.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)
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.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/afs.h"
45
46 #ifndef BUF_TIME_MAX
47 #define BUF_TIME_MAX    0x7fffffff
48 #endif
49 #define NPB 8                   /* must be a pwer of 2 */
50 static int afs_max_buffers;     /* should be an integral multiple of NPB */
51
52 /* page size */
53 #define AFS_BUFFER_PAGESIZE 2048
54 /* log page size */
55 #define LOGPS 11
56 /* If you change any of this PH stuff, make sure you don't break DZap() */
57 /* use last two bits for page */
58 #define PHPAGEMASK 3
59 /* use next five bits for fid */
60 #define PHFIDMASK 124
61 /* page hash table size - this is pretty intertwined with pHash */
62 #define PHSIZE (PHPAGEMASK + PHFIDMASK + 1)
63 /* the pHash macro */
64 #define pHash(fid,page) ((((afs_int32)(fid)) & PHFIDMASK) \
65                          | (page & PHPAGEMASK))
66
67 #ifdef  dirty
68 #undef dirty                    /* XXX */
69 #endif
70
71 static struct buffer *Buffers = 0;
72 static char *BufferData;
73
74 #ifdef  AFS_AIX_ENV
75 extern struct buf *geteblk();
76 #endif
77 #ifdef AFS_FBSD_ENV
78 #define timecounter afs_timecounter
79 #endif
80
81 /* A note on locking in 'struct buffer'
82  *
83  * afs_bufferLock protects the hash chain, and the 'lockers' field where that
84  * has a zero value. It must be held whenever lockers is incremented from zero.
85  *
86  * The individual buffer lock protects the contents of the structure, including
87  * the lockers field.
88  *
89  * For safety: afs_bufferLock and the individual buffer lock must be held
90  * when obtaining a reference on a structure. Only the individual buffer lock
91  * need be held when releasing a reference.
92  *
93  * The locking hierarchy is afs_bufferLock-> buffer.lock
94  *
95  */
96
97 static afs_lock_t afs_bufferLock;
98 static struct buffer *phTable[PHSIZE];  /* page hash table */
99 static int nbuffers;
100 static afs_int32 timecounter;
101
102 /* Prototypes for static routines */
103 static struct buffer *afs_newslot(struct dcache *adc, afs_int32 apage,
104                                   struct buffer *lp);
105
106 static int dinit_flag = 0;
107 void
108 DInit(int abuffers)
109 {
110     /* Initialize the venus buffer system. */
111     int i;
112     struct buffer *tb;
113
114     AFS_STATCNT(DInit);
115     if (dinit_flag)
116         return;
117     dinit_flag = 1;
118     /* round up to next multiple of NPB, since we allocate multiple pages per chunk */
119     abuffers = ((abuffers - 1) | (NPB - 1)) + 1;
120     afs_max_buffers = abuffers << 2;            /* possibly grow up to 4 times as big */
121     LOCK_INIT(&afs_bufferLock, "afs_bufferLock");
122     Buffers = afs_osi_Alloc(afs_max_buffers * sizeof(struct buffer));
123     osi_Assert(Buffers != NULL);
124     timecounter = 1;
125     afs_stats_cmperf.bufAlloced = nbuffers = abuffers;
126     for (i = 0; i < PHSIZE; i++)
127         phTable[i] = 0;
128     for (i = 0; i < abuffers; i++) {
129         if ((i & (NPB - 1)) == 0) {
130             /* time to allocate a fresh buffer */
131             BufferData = afs_osi_Alloc(AFS_BUFFER_PAGESIZE * NPB);
132             osi_Assert(BufferData != NULL);
133         }
134         /* Fill in each buffer with an empty indication. */
135         tb = &Buffers[i];
136         tb->fid = NULLIDX;
137         afs_reset_inode(&tb->inode);
138         tb->accesstime = 0;
139         tb->lockers = 0;
140         tb->data = &BufferData[AFS_BUFFER_PAGESIZE * (i & (NPB - 1))];
141         tb->hashIndex = 0;
142         tb->dirty = 0;
143         AFS_RWLOCK_INIT(&tb->lock, "buffer lock");
144     }
145     return;
146 }
147
148 int
149 DRead(struct dcache *adc, int page, struct DirBuffer *entry)
150 {
151     /* Read a page from the disk. */
152     struct buffer *tb, *tb2;
153     struct osi_file *tfile;
154     int code;
155
156     AFS_STATCNT(DRead);
157
158     memset(entry, 0, sizeof(struct DirBuffer));
159
160     ObtainWriteLock(&afs_bufferLock, 256);
161
162 #define bufmatch(tb) (tb->page == page && tb->fid == adc->index)
163 #define buf_Front(head,parent,p) {(parent)->hashNext = (p)->hashNext; (p)->hashNext= *(head);*(head)=(p);}
164
165     /* this apparently-complicated-looking code is simply an example of
166      * a little bit of loop unrolling, and is a standard linked-list
167      * traversal trick. It saves a few assignments at the the expense
168      * of larger code size.  This could be simplified by better use of
169      * macros.
170      */
171     if ((tb = phTable[pHash(adc->index, page)])) {
172         if (bufmatch(tb)) {
173             ObtainWriteLock(&tb->lock, 257);
174             tb->lockers++;
175             ReleaseWriteLock(&afs_bufferLock);
176             tb->accesstime = timecounter++;
177             AFS_STATS(afs_stats_cmperf.bufHits++);
178             ReleaseWriteLock(&tb->lock);
179             entry->buffer = tb;
180             entry->data = tb->data;
181             return 0;
182         } else {
183             struct buffer **bufhead;
184             bufhead = &(phTable[pHash(adc->index, page)]);
185             while ((tb2 = tb->hashNext)) {
186                 if (bufmatch(tb2)) {
187                     buf_Front(bufhead, tb, tb2);
188                     ObtainWriteLock(&tb2->lock, 258);
189                     tb2->lockers++;
190                     ReleaseWriteLock(&afs_bufferLock);
191                     tb2->accesstime = timecounter++;
192                     AFS_STATS(afs_stats_cmperf.bufHits++);
193                     ReleaseWriteLock(&tb2->lock);
194                     entry->buffer = tb2;
195                     entry->data = tb2->data;
196                     return 0;
197                 }
198                 if ((tb = tb2->hashNext)) {
199                     if (bufmatch(tb)) {
200                         buf_Front(bufhead, tb2, tb);
201                         ObtainWriteLock(&tb->lock, 259);
202                         tb->lockers++;
203                         ReleaseWriteLock(&afs_bufferLock);
204                         tb->accesstime = timecounter++;
205                         AFS_STATS(afs_stats_cmperf.bufHits++);
206                         ReleaseWriteLock(&tb->lock);
207                         entry->buffer = tb;
208                         entry->data = tb->data;
209                         return 0;
210                     }
211                 } else
212                     break;
213             }
214         }
215     } else
216         tb2 = NULL;
217
218     AFS_STATS(afs_stats_cmperf.bufMisses++);
219     /* can't find it */
220     /* The last thing we looked at was either tb or tb2 (or nothing). That
221      * is at least the oldest buffer on one particular hash chain, so it's
222      * a pretty good place to start looking for the truly oldest buffer.
223      */
224     tb = afs_newslot(adc, page, (tb ? tb : tb2));
225     if (!tb) {
226         ReleaseWriteLock(&afs_bufferLock);
227         return EIO;
228     }
229     ObtainWriteLock(&tb->lock, 260);
230     tb->lockers++;
231     ReleaseWriteLock(&afs_bufferLock);
232     if (page * AFS_BUFFER_PAGESIZE >= adc->f.chunkBytes) {
233         tb->fid = NULLIDX;
234         afs_reset_inode(&tb->inode);
235         tb->lockers--;
236         ReleaseWriteLock(&tb->lock);
237         return EIO;
238     }
239     tfile = afs_CFileOpen(&adc->f.inode);
240     code =
241         afs_CFileRead(tfile, tb->page * AFS_BUFFER_PAGESIZE, tb->data,
242                       AFS_BUFFER_PAGESIZE);
243     afs_CFileClose(tfile);
244     if (code < AFS_BUFFER_PAGESIZE) {
245         tb->fid = NULLIDX;
246         afs_reset_inode(&tb->inode);
247         tb->lockers--;
248         ReleaseWriteLock(&tb->lock);
249         return EIO;
250     }
251     /* Note that findslot sets the page field in the buffer equal to
252      * what it is searching for. */
253     ReleaseWriteLock(&tb->lock);
254     entry->buffer = tb;
255     entry->data = tb->data;
256     return 0;
257 }
258
259 static void
260 FixupBucket(struct buffer *ap)
261 {
262     struct buffer **lp, *tp;
263     int i;
264     /* first try to get it out of its current hash bucket, in which it
265      * might not be */
266     AFS_STATCNT(FixupBucket);
267     i = ap->hashIndex;
268     lp = &phTable[i];
269     for (tp = *lp; tp; tp = tp->hashNext) {
270         if (tp == ap) {
271             *lp = tp->hashNext;
272             break;
273         }
274         lp = &tp->hashNext;
275     }
276     /* now figure the new hash bucket */
277     i = pHash(ap->fid, ap->page);
278     ap->hashIndex = i;          /* remember where we are for deletion */
279     ap->hashNext = phTable[i];  /* add us to the list */
280     phTable[i] = ap;            /* at the front, since it's LRU */
281 }
282
283 /* lp is pointer to a fairly-old buffer */
284 static struct buffer *
285 afs_newslot(struct dcache *adc, afs_int32 apage, struct buffer *lp)
286 {
287     /* Find a usable buffer slot */
288     afs_int32 i;
289     afs_int32 lt = 0;
290     struct buffer *tp;
291     struct osi_file *tfile;
292
293     AFS_STATCNT(afs_newslot);
294     /* we take a pointer here to a buffer which was at the end of an
295      * LRU hash chain.  Odds are, it's one of the older buffers, not
296      * one of the newer.  Having an older buffer to start with may
297      * permit us to avoid a few of the assignments in the "typical
298      * case" for loop below.
299      */
300     if (lp && (lp->lockers == 0)) {
301         lt = lp->accesstime;
302     } else {
303         lp = NULL;
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     } else {
329         /* this is the typical case */
330         tp = Buffers;
331         for (i = 0; i < nbuffers; i++, tp++) {
332             if (tp->lockers == 0) {
333                 if (!lp || tp->accesstime < lt) {
334                     lp = tp;
335                     lt = tp->accesstime;
336                 }
337             }
338         }
339     }
340
341     if (lp == 0) {
342         /* No unlocked buffers. If still possible, allocate a new increment */
343         if (nbuffers + NPB > afs_max_buffers) {
344             /* There are no unlocked buffers -- this used to panic, but that
345              * seems extreme.  To the best of my knowledge, all the callers
346              * of DRead are prepared to handle a zero return.  Some of them
347              * just panic directly, but not all of them. */
348             afs_warn("afs: all buffers locked\n");
349             return 0;
350         }
351
352         BufferData = afs_osi_Alloc(AFS_BUFFER_PAGESIZE * NPB);
353         osi_Assert(BufferData != NULL);
354         for (i = 0; i< NPB; i++) {
355             /* Fill in each buffer with an empty indication. */
356             tp = &Buffers[i + nbuffers];
357             tp->fid = NULLIDX;
358             afs_reset_inode(&tp->inode);
359             tp->accesstime = 0;
360             tp->lockers = 0;
361             tp->data = &BufferData[AFS_BUFFER_PAGESIZE * i];
362             tp->hashIndex = 0;
363             tp->dirty = 0;
364             AFS_RWLOCK_INIT(&tp->lock, "buffer lock");
365         }
366         lp = &Buffers[nbuffers];
367         nbuffers += NPB;
368     }
369
370     if (lp->dirty) {
371         /* see DFlush for rationale for not getting and locking the dcache */
372         tfile = afs_CFileOpen(&lp->inode);
373         afs_CFileWrite(tfile, lp->page * AFS_BUFFER_PAGESIZE, lp->data,
374                        AFS_BUFFER_PAGESIZE);
375         lp->dirty = 0;
376         afs_CFileClose(tfile);
377         AFS_STATS(afs_stats_cmperf.bufFlushDirty++);
378     }
379
380     /* Now fill in the header. */
381     lp->fid = adc->index;
382     afs_copy_inode(&lp->inode, &adc->f.inode);
383     lp->page = apage;
384     lp->accesstime = timecounter++;
385     FixupBucket(lp);            /* move to the right hash bucket */
386
387     return lp;
388 }
389
390 void
391 DRelease(struct DirBuffer *entry, int flag)
392 {
393     struct buffer *tp;
394
395     AFS_STATCNT(DRelease);
396
397     tp = entry->buffer;
398     if (tp == NULL)
399         return;
400
401     tp = entry->buffer;
402     ObtainWriteLock(&tp->lock, 261);
403     tp->lockers--;
404     if (flag)
405         tp->dirty = 1;
406     ReleaseWriteLock(&tp->lock);
407 }
408
409 int
410 DVOffset(struct DirBuffer *entry)
411 {
412     struct buffer *bp;
413
414     AFS_STATCNT(DVOffset);
415
416     bp = entry->buffer;
417     return AFS_BUFFER_PAGESIZE * bp->page 
418             + (char *)entry->data - (char *)bp->data;
419 }
420
421 /*!
422  * Zap one dcache entry: destroy one FID's buffers.
423  *
424  * 1/1/91 - I've modified the hash function to take the page as well
425  * as the *fid, so that lookup will be a bit faster.  That presents some
426  * difficulties for Zap, which now has to have some knowledge of the nature
427  * of the hash function.  Oh well.  This should use the list traversal
428  * method of DRead...
429  *
430  * \param adc The dcache entry to be zapped.
431  */
432 void
433 DZap(struct dcache *adc)
434 {
435     int i;
436     /* Destroy all buffers pertaining to a particular fid. */
437     struct buffer *tb;
438
439     AFS_STATCNT(DZap);
440     ObtainReadLock(&afs_bufferLock);
441
442     for (i = 0; i <= PHPAGEMASK; i++)
443         for (tb = phTable[pHash(adc->index, i)]; tb; tb = tb->hashNext)
444             if (tb->fid == adc->index) {
445                 ObtainWriteLock(&tb->lock, 262);
446                 tb->fid = NULLIDX;
447                 afs_reset_inode(&tb->inode);
448                 tb->dirty = 0;
449                 ReleaseWriteLock(&tb->lock);
450             }
451     ReleaseReadLock(&afs_bufferLock);
452 }
453
454 static void
455 DFlushBuffer(struct buffer *ab)
456 {
457     struct osi_file *tfile;
458
459     tfile = afs_CFileOpen(&ab->inode);
460     afs_CFileWrite(tfile, ab->page * AFS_BUFFER_PAGESIZE,
461                    ab->data, AFS_BUFFER_PAGESIZE);
462     ab->dirty = 0;      /* Clear the dirty flag */
463     afs_CFileClose(tfile);
464 }
465
466 void
467 DFlushDCache(struct dcache *adc)
468 {
469     int i;
470     struct buffer *tb;
471
472     ObtainReadLock(&afs_bufferLock);
473
474     for (i = 0; i <= PHPAGEMASK; i++)
475         for (tb = phTable[pHash(adc->index, i)]; tb; tb = tb->hashNext)
476             if (tb->fid == adc->index) {
477                 ObtainWriteLock(&tb->lock, 701);
478                 tb->lockers++;
479                 ReleaseReadLock(&afs_bufferLock);
480                 if (tb->dirty) {
481                     DFlushBuffer(tb);
482                 }
483                 tb->lockers--;
484                 ReleaseWriteLock(&tb->lock);
485                 ObtainReadLock(&afs_bufferLock);
486             }
487
488     ReleaseReadLock(&afs_bufferLock);
489 }
490
491 int
492 DFlush(void)
493 {
494     /* Flush all the modified buffers. */
495     int i;
496     struct buffer *tb;
497
498     AFS_STATCNT(DFlush);
499     tb = Buffers;
500     ObtainReadLock(&afs_bufferLock);
501     for (i = 0; i < nbuffers; i++, tb++) {
502         if (tb->dirty) {
503             ObtainWriteLock(&tb->lock, 263);
504             tb->lockers++;
505             ReleaseReadLock(&afs_bufferLock);
506             if (tb->dirty) {
507                 /* it seems safe to do this I/O without having the dcache
508                  * locked, since the only things that will update the data in
509                  * a directory are the buffer package, which holds the relevant
510                  * tb->lock while doing the write, or afs_GetDCache, which
511                  * DZap's the directory while holding the dcache lock.
512                  * It is not possible to lock the dcache or even call
513                  * afs_GetDSlot to map the index to the dcache since the dir
514                  * package's caller has some dcache object locked already (so
515                  * we cannot lock afs_xdcache). In addition, we cannot obtain
516                  * a dcache lock while holding the tb->lock of the same file
517                  * since that can deadlock with DRead/DNew */
518                 DFlushBuffer(tb);
519             }
520             tb->lockers--;
521             ReleaseWriteLock(&tb->lock);
522             ObtainReadLock(&afs_bufferLock);
523         }
524     }
525     ReleaseReadLock(&afs_bufferLock);
526
527     return 0;
528 }
529
530 int
531 DNew(struct dcache *adc, int page, struct DirBuffer *entry)
532 {
533     /* Same as read, only do *not* even try to read the page, since it
534      * probably doesn't exist. */
535     struct buffer *tb;
536     AFS_STATCNT(DNew);
537
538     ObtainWriteLock(&afs_bufferLock, 264);
539     if ((tb = afs_newslot(adc, page, NULL)) == 0) {
540         ReleaseWriteLock(&afs_bufferLock);
541         return EIO;
542     }
543     /* extend the chunk, if needed */
544     /* Do it now, not in DFlush or afs_newslot when the data is written out,
545      * since now our caller has adc->lock writelocked, and we can't acquire
546      * that lock (or even map from a fid to a dcache) in afs_newslot or
547      * DFlush due to lock hierarchy issues */
548     if ((page + 1) * AFS_BUFFER_PAGESIZE > adc->f.chunkBytes) {
549         afs_AdjustSize(adc, (page + 1) * AFS_BUFFER_PAGESIZE);
550         osi_Assert(afs_WriteDCache(adc, 1) == 0);
551     }
552     ObtainWriteLock(&tb->lock, 265);
553     tb->lockers++;
554     ReleaseWriteLock(&afs_bufferLock);
555     ReleaseWriteLock(&tb->lock);
556     entry->buffer = tb;
557     entry->data = tb->data;
558
559     return 0;
560 }
561
562 void
563 shutdown_bufferpackage(void)
564 {
565     struct buffer *tp;
566     int i;
567
568     AFS_STATCNT(shutdown_bufferpackage);
569     /* Free all allocated Buffers and associated buffer pages */
570     DFlush();
571     if (afs_cold_shutdown) {
572         dinit_flag = 0;
573         tp = Buffers;
574         for (i = 0; i < nbuffers; i += NPB, tp += NPB) {
575             afs_osi_Free(tp->data, NPB * AFS_BUFFER_PAGESIZE);
576         }
577         afs_osi_Free(Buffers, nbuffers * sizeof(struct buffer));
578         nbuffers = 0;
579         timecounter = 1;
580         for (i = 0; i < PHSIZE; i++)
581             phTable[i] = 0;
582         memset(&afs_bufferLock, 0, sizeof(afs_lock_t));
583     }
584 }