dir-hold-afs-bufferlock-across-increment-of-buffer-lockers-to-prevent-newslot-from...
[openafs.git] / src / dir / 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 <stdlib.h>
16 #include <lock.h>
17
18 #include "dir.h"
19
20 struct Lock afs_bufferLock;
21
22 /* page size */
23 #define BUFFER_PAGE_SIZE 2048
24 /* log page size */
25 #define LOGPS 11
26 /* page hash table size */
27 #define PHSIZE 32
28 /* The hash table should be somewhat efficient even if there are only
29  * a few partitions (less than 32). So the hash for the fileserver is now
30  * based on the volume id. This means this macro is dependent upon the
31  * layout of DirHandle in viced/viced.h, vol/salvage.h and volser/salvage.h.
32  */
33 #define pHash(fid) ((fid)[0] & (PHSIZE-1))
34 #define vHash(vid) (vid & (PHSIZE-1))
35
36 /* admittedly system dependent, this is the maximum signed 32-bit value */
37 #define BUFFER_LONG_MAX   2147483647
38 #ifndef NULL
39 #define NULL 0
40 #endif
41
42 #ifdef AFS_64BIT_IOPS_ENV
43 #define BUFFER_FID_SIZE (9 + 2*sizeof(char*)/sizeof(int))
44 #else
45 #define BUFFER_FID_SIZE (6 + 2*sizeof(char*)/sizeof(int))
46 #endif
47
48 struct buffer {
49     /* fid is used for Unique cache key + i/o addressing.
50      * fid size is based on 4 + size of inode and size of pointer
51      */
52     afs_int32 fid[BUFFER_FID_SIZE];
53     afs_int32 page;
54     afs_int32 accesstime;
55     struct buffer *hashNext;
56     char *data;
57     char lockers;
58     char dirty;
59     char hashIndex;
60     struct Lock lock;
61     } **Buffers;
62
63 char *BufferData;
64
65 static struct buffer *phTable[PHSIZE];  /* page hash table */
66 static struct buffer *LastBuffer;
67 int nbuffers;
68 int timecounter;
69 static int calls=0, ios=0;
70
71 struct buffer *newslot();
72
73 int DStat (abuffers, acalls, aios)
74     int *abuffers, *acalls, *aios;
75     {*abuffers = nbuffers;
76     *acalls = calls;
77     *aios = ios;
78     return 0;
79     }
80
81 int DInit (abuffers)
82     int abuffers;
83     {/* Initialize the venus buffer system. */
84     register int i, tsize;
85     register struct buffer *tb;
86     register char *tp;
87
88     Lock_Init(&afs_bufferLock);
89     /* Align each element of Buffers on a doubleword boundary */
90     tsize = (sizeof(struct buffer) + 7) & ~7;
91     tp = (char *) malloc(abuffers * tsize);
92     Buffers = (struct buffer **) malloc(abuffers * sizeof(struct buffer *));
93     BufferData = (char *) malloc(abuffers * BUFFER_PAGE_SIZE);
94     timecounter = 0;
95     LastBuffer = (struct buffer *)tp;
96     nbuffers = abuffers;
97     for(i=0;i<PHSIZE;i++) phTable[i] = 0;
98     for (i=0;i<abuffers;i++)
99         {/* Fill in each buffer with an empty indication. */
100         tb = (struct buffer *)tp;
101         Buffers[i] = tb;
102         tp += tsize;
103         FidZero(tb->fid);
104         tb->accesstime = tb->lockers = 0;
105         tb->data = &BufferData[BUFFER_PAGE_SIZE*i];
106         tb->hashIndex = 0;
107         tb->dirty = 0;
108         Lock_Init(&tb->lock);
109         }
110     return 0;
111     }
112
113 char *DRead(fid,page)
114   register afs_int32 *fid;
115   register int page;
116 { /* Read a page from the disk. */
117     register struct buffer *tb, *tb2, **bufhead;
118
119     ObtainWriteLock(&afs_bufferLock);
120     calls++;
121
122 #define bufmatch(tb) (tb->page == page && FidEq(tb->fid, fid))
123 #define buf_Front(head,parent,p) {(parent)->hashNext = (p)->hashNext; (p)->hashNext= *(head);*(head)=(p);}
124
125     /* this apparently-complicated-looking code is simply an example of
126      * a little bit of loop unrolling, and is a standard linked-list 
127      * traversal trick. It saves a few assignments at the the expense
128      * of larger code size.  This could be simplified by better use of
129      * macros.  With the use of these LRU queues, the old one-cache is
130      * probably obsolete. 
131      */
132     if ( tb = phTable[pHash(fid)] ) {  /* ASSMT HERE */
133         if (bufmatch(tb)) {
134             ObtainWriteLock(&tb->lock);
135             tb->lockers++;
136             ReleaseWriteLock(&afs_bufferLock);
137             tb->accesstime = ++timecounter;
138             ReleaseWriteLock(&tb->lock);
139             return tb->data;
140         }
141         else {
142           bufhead = &( phTable[pHash(fid)] );
143           while (tb2 = tb->hashNext) {
144             if (bufmatch(tb2)) {
145               buf_Front(bufhead,tb,tb2);
146               ObtainWriteLock(&tb2->lock);
147               tb2->lockers++;
148               ReleaseWriteLock(&afs_bufferLock);
149               tb2->accesstime = ++timecounter;
150               ReleaseWriteLock(&tb2->lock);
151               return tb2->data;
152             }
153             if (tb = tb2->hashNext) { /* ASSIGNMENT HERE! */ 
154               if (bufmatch(tb)) {
155                 buf_Front(bufhead,tb2,tb);
156                 ObtainWriteLock(&tb->lock);
157                 tb->lockers++;
158                 ReleaseWriteLock(&afs_bufferLock);
159                 tb->accesstime = ++timecounter;
160                 ReleaseWriteLock(&tb->lock);
161                 return tb->data;
162               }
163             }
164             else break;
165           }
166         }
167       }  
168     else tb2 = NULL;
169
170     /* can't find it */
171     /* The last thing we looked at was either tb or tb2 (or nothing). That
172      * is at least the oldest buffer on one particular hash chain, so it's 
173      * a pretty good place to start looking for the truly oldest buffer.
174      */
175     tb = newslot(fid, page, (tb ? tb : tb2));
176     ios++;
177     ObtainWriteLock(&tb->lock);
178     tb->lockers++;
179     ReleaseWriteLock(&afs_bufferLock);
180     if (ReallyRead(tb->fid,tb->page,tb->data)) {
181         tb->lockers--;
182         FidZap(tb->fid);        /* disaster */
183         ReleaseWriteLock(&tb->lock);
184         return 0;
185     }
186     /* Note that findslot sets the page field in the buffer equal to
187      * what it is searching for.
188      */
189     ReleaseWriteLock(&tb->lock);
190     return tb->data;
191 }
192
193 static FixupBucket(ap)
194     register struct buffer *ap;
195     {register struct buffer **lp, *tp;
196     register int i;
197     /* first try to get it out of its current hash bucket, in which it might not be */
198     i = ap->hashIndex;
199     lp = &phTable[i];
200     for(tp = *lp; tp; tp=tp->hashNext)
201         {if (tp == ap)
202             {*lp = tp->hashNext;
203             break;
204             }
205         lp = &tp->hashNext;
206         }
207     /* now figure the new hash bucket */
208     i = pHash(ap->fid);
209     ap->hashIndex = i;          /* remember where we are for deletion */
210     ap->hashNext = phTable[i];  /* add us to the list */
211     phTable[i] = ap;            /* at the front, since it's LRU */
212     }
213
214 struct buffer *newslot (afid, apage, lp)
215     afs_int32 *afid, apage;
216      register struct buffer *lp;   /* pointer to a fairly-old buffer */
217     {/* Find a usable buffer slot */
218     register afs_int32 i;
219     afs_int32 lt;
220     register struct buffer **tbp;
221
222     if (lp && (lp->lockers == 0)) {
223       lt = lp->accesstime;
224     }
225     else {
226       lp = 0;
227       lt = BUFFER_LONG_MAX;
228     }
229
230     tbp = Buffers;
231     for (i=0;i<nbuffers;i++,tbp++) {
232       if ((*tbp)->lockers == 0) {
233         if ((*tbp)->accesstime < lt) {
234           lp = (*tbp);
235           lt = (*tbp)->accesstime;
236         }
237       }
238     }
239
240     /* There are no unlocked buffers */
241     if (lp == 0) {
242       if (lt < 0)
243         Die("accesstime counter wrapped");
244       else 
245         Die ("all buffers locked");
246     }
247
248     /* We do not need to lock the buffer here because it has no lockers
249      * and the afs_bufferLock prevents other threads from zapping this
250      * buffer while we are writing it out */
251     if (lp->dirty) {
252         if (ReallyWrite(lp->fid,lp->page,lp->data)) Die("writing bogus buffer");
253         lp->dirty = 0;
254         }
255
256     /* Now fill in the header. */
257     FidZap(lp->fid);
258     FidCpy(lp->fid, afid);      /* set this */
259     lp->page = apage;
260     lp->accesstime = ++timecounter;
261
262     FixupBucket(lp);            /* move to the right hash bucket */
263
264     return lp;
265     }
266
267 void
268 DRelease (bp,flag)
269     register struct buffer *bp;
270     int flag;
271     {/* Release a buffer, specifying whether or not the buffer has been modified by the locker. */
272     register int index;
273
274     if (!bp) return;
275     index = (((char *)bp)-((char *)BufferData))>>LOGPS;
276     bp = Buffers[index];
277     ObtainWriteLock(&bp->lock);
278     bp->lockers--;
279     if (flag) bp->dirty=1;
280     ReleaseWriteLock(&bp->lock);
281     }
282
283 DVOffset (ap)
284     register void *ap;
285     {/* Return the byte within a file represented by a buffer pointer. */
286     register struct buffer *bp;
287     register int index;
288     bp=ap;
289     index = (((char *)bp) - ((char *)BufferData)) >> LOGPS;
290     if (index<0 || index >= nbuffers) return -1;
291     bp = Buffers[index];
292     return BUFFER_PAGE_SIZE*bp->page+((char *)ap)-bp->data;
293     }
294
295 DZap (fid)
296     register afs_int32 *fid;
297     {/* Destroy all buffers pertaining to a particular fid. */
298     register struct buffer *tb;
299     ObtainReadLock(&afs_bufferLock);
300     for(tb=phTable[pHash(fid)]; tb; tb=tb->hashNext)
301         if (FidEq(tb->fid,fid)) {
302             ObtainWriteLock(&tb->lock);
303             FidZap(tb->fid);
304             tb->dirty = 0;
305             ReleaseWriteLock(&tb->lock);
306         }
307     ReleaseReadLock(&afs_bufferLock);
308     }
309
310 DFlushVolume (vid)
311     register afs_int32 vid;
312     {/* Flush all data and release all inode handles for a particular volume */
313     register struct buffer *tb;
314     register int code, rcode = 0;
315     ObtainReadLock(&afs_bufferLock);
316     for(tb=phTable[vHash(vid)]; tb; tb=tb->hashNext)
317         if (FidVolEq(tb->fid,vid)) {
318             ObtainWriteLock(&tb->lock);
319             if (tb->dirty) {
320                 code = ReallyWrite(tb->fid, tb->page, tb->data);
321                 if (code && !rcode)
322                     rcode = code;
323                 tb->dirty = 0;
324             }
325             FidZap(tb->fid);
326             ReleaseWriteLock(&tb->lock);
327         }
328     ReleaseReadLock(&afs_bufferLock);
329     return rcode;
330     }
331
332 DFlushEntry (fid)
333 register afs_int32 *fid;
334 {/* Flush pages modified by one entry. */
335     register struct buffer *tb;
336     int code;
337
338     ObtainReadLock(&afs_bufferLock);
339     for(tb = phTable[pHash(fid)]; tb; tb=tb->hashNext)
340         if (FidEq(tb->fid, fid) && tb->dirty) {
341             ObtainWriteLock(&tb->lock);
342             if (tb->dirty) {
343                 code = ReallyWrite(tb->fid, tb->page, tb->data);
344                 if (code) {
345                     ReleaseWriteLock(&tb->lock);
346                     ReleaseReadLock(&afs_bufferLock);
347                     return code;
348                 }
349                 tb->dirty = 0;
350             }
351             ReleaseWriteLock(&tb->lock);
352         }
353     ReleaseReadLock(&afs_bufferLock);
354     return 0;
355 }
356
357 DFlush ()
358 {/* Flush all the modified buffers. */
359     register int i;
360     register struct buffer **tbp;
361     afs_int32 code, rcode;
362
363     rcode = 0;
364     tbp = Buffers;
365     ObtainReadLock(&afs_bufferLock);
366     for(i=0;i<nbuffers;i++,tbp++) {
367         if ((*tbp)->dirty) {
368             ObtainWriteLock(&(*tbp)->lock);
369             (*tbp)->lockers++;
370             ReleaseReadLock(&afs_bufferLock);
371             if ((*tbp)->dirty) {
372                 code = ReallyWrite((*tbp)->fid, (*tbp)->page, (*tbp)->data);
373                 if (!code)
374                     (*tbp)->dirty = 0;  /* Clear the dirty flag */
375                 if (code && !rcode) {
376                     rcode = code;
377                 }
378             }
379             (*tbp)->lockers--;
380             ReleaseWriteLock(&(*tbp)->lock);
381             ObtainReadLock(&afs_bufferLock);
382         }
383     }
384     ReleaseReadLock(&afs_bufferLock);
385     return rcode;
386 }
387
388 char *DNew (fid,page)
389   register int page;
390   register afs_int32 *fid;
391 {
392     /* Same as read, only do *not* even try to read the page,
393      * since it probably doesn't exist.
394      */
395     register struct buffer *tb;
396     ObtainWriteLock(&afs_bufferLock);
397     if ((tb = newslot(fid,page,0)) == 0) {
398         ReleaseWriteLock(&afs_bufferLock);
399         return 0;
400     }
401     ObtainWriteLock(&tb->lock);
402     tb->lockers++;
403     ReleaseWriteLock(&afs_bufferLock);
404     ReleaseWriteLock(&tb->lock);
405     return tb->data;
406 }