2 * Copyright 2000, International Business Machines Corporation and others.
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
10 #include <afsconfig.h>
11 #include <afs/param.h>
20 #ifdef AFS_64BIT_IOPS_ENV
21 #define BUFFER_FID_SIZE (9*sizeof(int) + 2*sizeof(char*))
23 #define BUFFER_FID_SIZE (6*sizeof(int) + 2*sizeof(char*))
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
30 char fid
[BUFFER_FID_SIZE
];
33 struct buffer
*hashNext
;
41 static_inline dir_file_t
42 bufferDir(struct buffer
*b
)
44 return (dir_file_t
) &b
->fid
;
47 struct Lock afs_bufferLock
;
50 #define BUFFER_PAGE_SIZE 2048
53 /* page hash table size */
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.
60 #define pHash(fid) (((afs_int32 *)fid)[0] & (PHSIZE-1))
61 #define vHash(vid) (vid & (PHSIZE-1))
63 /* admittedly system dependent, this is the maximum signed 32-bit value */
64 #define BUFFER_LONG_MAX 2147483647
69 static struct buffer
**Buffers
;
73 static struct buffer
*phTable
[PHSIZE
]; /* page hash table */
74 static struct buffer
*LastBuffer
;
77 static int calls
= 0, ios
= 0;
79 struct buffer
*newslot(dir_file_t dir
, afs_int32 apage
,
82 /* XXX - This sucks. The correct prototypes for these functions are ...
84 * extern void FidZero(DirHandle *);
85 * extern int FidEq(DirHandle *a, DirHandle *b);
86 * extern int ReallyRead(DirHandle *a, int block, char *data);
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
);
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
);
98 DStat(int *abuffers
, int *acalls
, int *aios
)
100 *abuffers
= nbuffers
;
107 * initialize the directory package.
109 * @param[in] abuffers size of directory buffer cache
111 * @return operation status
117 /* Initialize the venus buffer system. */
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
);
129 LastBuffer
= (struct buffer
*)tp
;
131 for (i
= 0; i
< PHSIZE
; i
++)
133 for (i
= 0; i
< abuffers
; i
++) {
134 /* Fill in each buffer with an empty indication. */
135 tb
= (struct buffer
*)tp
;
138 FidZero(bufferDir(tb
));
139 tb
->accesstime
= tb
->lockers
= 0;
140 tb
->data
= &BufferData
[BUFFER_PAGE_SIZE
* i
];
143 Lock_Init(&tb
->lock
);
149 * read a page out of a directory object.
151 * @param[in] fid directory object fid
152 * @param[in] page page in hash table to be read
154 * @return pointer to requested page in directory cache
155 * @retval NULL read failed
158 DRead(dir_file_t fid
, int page
, struct DirBuffer
*entry
)
160 /* Read a page from the disk. */
161 struct buffer
*tb
, *tb2
, **bufhead
;
163 memset(entry
, 0, sizeof(struct DirBuffer
));
165 ObtainWriteLock(&afs_bufferLock
);
168 #define bufmatch(tb,fid) (tb->page == page && FidEq(bufferDir(tb), fid))
169 #define buf_Front(head,parent,p) {(parent)->hashNext = (p)->hashNext; (p)->hashNext= *(head);*(head)=(p);}
171 /* this apparently-complicated-looking code is simply an example of
172 * a little bit of loop unrolling, and is a standard linked-list
173 * traversal trick. It saves a few assignments at the the expense
174 * of larger code size. This could be simplified by better use of
175 * macros. With the use of these LRU queues, the old one-cache is
178 if ((tb
= phTable
[pHash(fid
)])) { /* ASSMT HERE */
179 if (bufmatch(tb
, fid
)) {
180 ObtainWriteLock(&tb
->lock
);
182 ReleaseWriteLock(&afs_bufferLock
);
183 tb
->accesstime
= ++timecounter
;
184 ReleaseWriteLock(&tb
->lock
);
186 entry
->data
= tb
->data
;
189 bufhead
= &(phTable
[pHash(fid
)]);
190 while ((tb2
= tb
->hashNext
)) {
191 if (bufmatch(tb2
, fid
)) {
192 buf_Front(bufhead
, tb
, tb2
);
193 ObtainWriteLock(&tb2
->lock
);
195 ReleaseWriteLock(&afs_bufferLock
);
196 tb2
->accesstime
= ++timecounter
;
197 ReleaseWriteLock(&tb2
->lock
);
199 entry
->data
= tb2
->data
;
202 if ((tb
= tb2
->hashNext
)) { /* ASSIGNMENT HERE! */
203 if (bufmatch(tb
, fid
)) {
204 buf_Front(bufhead
, tb2
, tb
);
205 ObtainWriteLock(&tb
->lock
);
207 ReleaseWriteLock(&afs_bufferLock
);
208 tb
->accesstime
= ++timecounter
;
209 ReleaseWriteLock(&tb
->lock
);
211 entry
->data
= tb
->data
;
222 /* The last thing we looked at was either tb or tb2 (or nothing). That
223 * is at least the oldest buffer on one particular hash chain, so it's
224 * a pretty good place to start looking for the truly oldest buffer.
226 tb
= newslot(fid
, page
, (tb
? tb
: tb2
));
228 ObtainWriteLock(&tb
->lock
);
230 ReleaseWriteLock(&afs_bufferLock
);
231 if (ReallyRead(bufferDir(tb
), tb
->page
, tb
->data
)) {
233 FidZap(bufferDir(tb
)); /* disaster */
234 ReleaseWriteLock(&tb
->lock
);
237 /* Note that findslot sets the page field in the buffer equal to
238 * what it is searching for.
240 ReleaseWriteLock(&tb
->lock
);
242 entry
->data
= tb
->data
;
248 FixupBucket(struct buffer
*ap
)
250 struct buffer
**lp
, *tp
;
253 /* first try to get it out of its current hash bucket, in which it might not be */
256 for (tp
= *lp
; tp
; tp
= tp
->hashNext
) {
263 /* now figure the new hash bucket */
265 ap
->hashIndex
= i
; /* remember where we are for deletion */
266 ap
->hashNext
= phTable
[i
]; /* add us to the list */
267 phTable
[i
] = ap
; /* at the front, since it's LRU */
272 newslot(dir_file_t dir
, afs_int32 apage
, struct buffer
*lp
)
274 /* Find a usable buffer slot */
279 if (lp
&& (lp
->lockers
== 0)) {
283 lt
= BUFFER_LONG_MAX
;
287 for (i
= 0; i
< nbuffers
; i
++, tbp
++) {
288 if ((*tbp
)->lockers
== 0) {
289 if ((*tbp
)->accesstime
< lt
) {
291 lt
= (*tbp
)->accesstime
;
296 /* There are no unlocked buffers */
299 Die("accesstime counter wrapped");
301 Die("all buffers locked");
304 /* We do not need to lock the buffer here because it has no lockers
305 * and the afs_bufferLock prevents other threads from zapping this
306 * buffer while we are writing it out */
308 if (ReallyWrite(bufferDir(lp
), lp
->page
, lp
->data
))
309 Die("writing bogus buffer");
313 /* Now fill in the header. */
314 FidZap(bufferDir(lp
));
315 FidCpy(bufferDir(lp
), dir
); /* set this */
316 memset(lp
->data
, 0, BUFFER_PAGE_SIZE
); /* Don't leak stale data. */
318 lp
->accesstime
= ++timecounter
;
320 FixupBucket(lp
); /* move to the right hash bucket */
325 /* Release a buffer, specifying whether or not the buffer has been modified
328 DRelease(struct DirBuffer
*entry
, int flag
)
332 bp
= (struct buffer
*) entry
->buffer
;
335 ObtainWriteLock(&bp
->lock
);
339 ReleaseWriteLock(&bp
->lock
);
342 /* Return the byte within a file represented by a buffer pointer. */
344 DVOffset(struct DirBuffer
*entry
)
349 return BUFFER_PAGE_SIZE
* bp
->page
+ (char *)entry
->data
- (char *)bp
->data
;
355 /* Destroy all buffers pertaining to a particular fid. */
357 ObtainReadLock(&afs_bufferLock
);
358 for (tb
= phTable
[pHash(dir
)]; tb
; tb
= tb
->hashNext
)
359 if (FidEq(bufferDir(tb
), dir
)) {
360 ObtainWriteLock(&tb
->lock
);
361 FidZap(bufferDir(tb
));
363 ReleaseWriteLock(&tb
->lock
);
365 ReleaseReadLock(&afs_bufferLock
);
369 DFlushVolume(afs_int32 vid
)
371 /* Flush all data and release all inode handles for a particular volume */
374 ObtainReadLock(&afs_bufferLock
);
375 for (tb
= phTable
[vHash(vid
)]; tb
; tb
= tb
->hashNext
)
376 if (FidVolEq(bufferDir(tb
), vid
)) {
377 ObtainWriteLock(&tb
->lock
);
379 code
= ReallyWrite(bufferDir(tb
), tb
->page
, tb
->data
);
384 FidZap(bufferDir(tb
));
385 ReleaseWriteLock(&tb
->lock
);
387 ReleaseReadLock(&afs_bufferLock
);
392 DFlushEntry(dir_file_t fid
)
394 /* Flush pages modified by one entry. */
398 ObtainReadLock(&afs_bufferLock
);
399 for (tb
= phTable
[pHash(fid
)]; tb
; tb
= tb
->hashNext
)
400 if (FidEq(bufferDir(tb
), fid
) && tb
->dirty
) {
401 ObtainWriteLock(&tb
->lock
);
403 code
= ReallyWrite(bufferDir(tb
), tb
->page
, tb
->data
);
405 ReleaseWriteLock(&tb
->lock
);
406 ReleaseReadLock(&afs_bufferLock
);
411 ReleaseWriteLock(&tb
->lock
);
413 ReleaseReadLock(&afs_bufferLock
);
420 /* Flush all the modified buffers. */
423 afs_int32 code
, rcode
;
427 ObtainReadLock(&afs_bufferLock
);
428 for (i
= 0; i
< nbuffers
; i
++, tbp
++) {
430 ObtainWriteLock(&(*tbp
)->lock
);
432 ReleaseReadLock(&afs_bufferLock
);
434 code
= ReallyWrite(bufferDir(*tbp
), (*tbp
)->page
, (*tbp
)->data
);
436 (*tbp
)->dirty
= 0; /* Clear the dirty flag */
437 if (code
&& !rcode
) {
442 ReleaseWriteLock(&(*tbp
)->lock
);
443 ObtainReadLock(&afs_bufferLock
);
446 ReleaseReadLock(&afs_bufferLock
);
450 /* Same as read, only do *not* even try to read the page,
451 * since it probably doesn't exist.
454 DNew(dir_file_t dir
, int page
, struct DirBuffer
*entry
)
458 memset(entry
,0, sizeof(struct DirBuffer
));
460 ObtainWriteLock(&afs_bufferLock
);
461 if ((tb
= newslot(dir
, page
, 0)) == 0) {
462 ReleaseWriteLock(&afs_bufferLock
);
465 ObtainWriteLock(&tb
->lock
);
467 ReleaseWriteLock(&afs_bufferLock
);
468 ReleaseWriteLock(&tb
->lock
);
471 entry
->data
= tb
->data
;