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