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