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