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>
19 struct Lock afs_bufferLock;
22 #define BUFFER_PAGE_SIZE 2048
25 /* page hash table size */
27 /* The hash table should be somewhat efficient even if there are only
28 * a few partitions (less than 32). So the hash for the fileserver is now
29 * based on the volume id. This means this macro is dependent upon the
30 * layout of DirHandle in viced/viced.h, vol/salvage.h and volser/salvage.h.
32 #define pHash(fid) ((fid)[0] & (PHSIZE-1))
33 #define vHash(vid) (vid & (PHSIZE-1))
35 /* admittedly system dependent, this is the maximum signed 32-bit value */
36 #define BUFFER_LONG_MAX 2147483647
41 #ifdef AFS_64BIT_IOPS_ENV
42 #define BUFFER_FID_SIZE (9 + 2*sizeof(char*)/sizeof(int))
44 #define BUFFER_FID_SIZE (6 + 2*sizeof(char*)/sizeof(int))
48 /* fid is used for Unique cache key + i/o addressing.
49 * fid size is based on 4 + size of inode and size of pointer
51 afs_int32 fid[BUFFER_FID_SIZE];
54 struct buffer *hashNext;
64 static struct buffer *phTable[PHSIZE]; /* page hash table */
65 static struct buffer *LastBuffer;
68 static int calls=0, ios=0;
70 struct buffer *newslot();
72 int DStat (abuffers, acalls, aios)
73 int *abuffers, *acalls, *aios;
74 {*abuffers = nbuffers;
81 {/* Initialize the venus buffer system. */
82 register int i, tsize;
83 register struct buffer *tb;
86 Lock_Init(&afs_bufferLock);
87 /* Align each element of Buffers on a doubleword boundary */
88 tsize = (sizeof(struct buffer) + 7) & ~7;
89 tp = (char *) malloc(abuffers * tsize);
90 Buffers = (struct buffer **) malloc(abuffers * sizeof(struct buffer *));
91 BufferData = (char *) malloc(abuffers * BUFFER_PAGE_SIZE);
93 LastBuffer = (struct buffer *)tp;
95 for(i=0;i<PHSIZE;i++) phTable[i] = 0;
96 for (i=0;i<abuffers;i++)
97 {/* Fill in each buffer with an empty indication. */
98 tb = (struct buffer *)tp;
102 tb->accesstime = tb->lockers = 0;
103 tb->data = &BufferData[BUFFER_PAGE_SIZE*i];
106 Lock_Init(&tb->lock);
111 char *DRead(fid,page)
112 register afs_int32 *fid;
114 { /* Read a page from the disk. */
115 register struct buffer *tb, *tb2, **bufhead;
117 ObtainWriteLock(&afs_bufferLock);
120 #define bufmatch(tb) (tb->page == page && FidEq(tb->fid, fid))
121 #define buf_Front(head,parent,p) {(parent)->hashNext = (p)->hashNext; (p)->hashNext= *(head);*(head)=(p);}
123 /* this apparently-complicated-looking code is simply an example of
124 * a little bit of loop unrolling, and is a standard linked-list
125 * traversal trick. It saves a few assignments at the the expense
126 * of larger code size. This could be simplified by better use of
127 * macros. With the use of these LRU queues, the old one-cache is
130 if ( tb = phTable[pHash(fid)] ) { /* ASSMT HERE */
132 ObtainWriteLock(&tb->lock);
133 ReleaseWriteLock(&afs_bufferLock);
135 tb->accesstime = ++timecounter;
136 ReleaseWriteLock(&tb->lock);
140 bufhead = &( phTable[pHash(fid)] );
141 while (tb2 = tb->hashNext) {
143 buf_Front(bufhead,tb,tb2);
144 ObtainWriteLock(&tb2->lock);
145 ReleaseWriteLock(&afs_bufferLock);
147 tb2->accesstime = ++timecounter;
148 ReleaseWriteLock(&tb2->lock);
151 if (tb = tb2->hashNext) { /* ASSIGNMENT HERE! */
153 buf_Front(bufhead,tb2,tb);
154 ObtainWriteLock(&tb->lock);
155 ReleaseWriteLock(&afs_bufferLock);
157 tb->accesstime = ++timecounter;
158 ReleaseWriteLock(&tb->lock);
169 /* The last thing we looked at was either tb or tb2 (or nothing). That
170 * is at least the oldest buffer on one particular hash chain, so it's
171 * a pretty good place to start looking for the truly oldest buffer.
173 tb = newslot(fid, page, (tb ? tb : tb2));
175 ObtainWriteLock(&tb->lock);
176 ReleaseWriteLock(&afs_bufferLock);
178 if (ReallyRead(tb->fid,tb->page,tb->data)) {
180 FidZap(tb->fid); /* disaster */
181 ReleaseWriteLock(&tb->lock);
184 /* Note that findslot sets the page field in the buffer equal to
185 * what it is searching for.
187 ReleaseWriteLock(&tb->lock);
191 static FixupBucket(ap)
192 register struct buffer *ap;
193 {register struct buffer **lp, *tp;
195 /* first try to get it out of its current hash bucket, in which it might not be */
198 for(tp = *lp; tp; tp=tp->hashNext)
205 /* now figure the new hash bucket */
207 ap->hashIndex = i; /* remember where we are for deletion */
208 ap->hashNext = phTable[i]; /* add us to the list */
209 phTable[i] = ap; /* at the front, since it's LRU */
212 struct buffer *newslot (afid, apage, lp)
213 afs_int32 *afid, apage;
214 register struct buffer *lp; /* pointer to a fairly-old buffer */
215 {/* Find a usable buffer slot */
216 register afs_int32 i;
218 register struct buffer **tbp;
220 if (lp && (lp->lockers == 0)) {
225 lt = BUFFER_LONG_MAX;
229 for (i=0;i<nbuffers;i++,tbp++) {
230 if ((*tbp)->lockers == 0) {
231 if ((*tbp)->accesstime < lt) {
233 lt = (*tbp)->accesstime;
238 /* There are no unlocked buffers */
241 Die("accesstime counter wrapped");
243 Die ("all buffers locked");
246 /* We do not need to lock the buffer here because it has no lockers
247 * and the afs_bufferLock prevents other threads from zapping this
248 * buffer while we are writing it out */
250 if (ReallyWrite(lp->fid,lp->page,lp->data)) Die("writing bogus buffer");
254 /* Now fill in the header. */
256 FidCpy(lp->fid, afid); /* set this */
258 lp->accesstime = ++timecounter;
260 FixupBucket(lp); /* move to the right hash bucket */
266 register struct buffer *bp;
268 {/* Release a buffer, specifying whether or not the buffer has been modified by the locker. */
272 index = (((char *)bp)-((char *)BufferData))>>LOGPS;
274 ObtainWriteLock(&bp->lock);
276 if (flag) bp->dirty=1;
277 ReleaseWriteLock(&bp->lock);
282 {/* Return the byte within a file represented by a buffer pointer. */
283 register struct buffer *bp;
286 index = (((char *)bp) - ((char *)BufferData)) >> LOGPS;
287 if (index<0 || index >= nbuffers) return -1;
289 return BUFFER_PAGE_SIZE*bp->page+((char *)ap)-bp->data;
293 register afs_int32 *fid;
294 {/* Destroy all buffers pertaining to a particular fid. */
295 register struct buffer *tb;
296 ObtainReadLock(&afs_bufferLock);
297 for(tb=phTable[pHash(fid)]; tb; tb=tb->hashNext)
298 if (FidEq(tb->fid,fid)) {
299 ObtainWriteLock(&tb->lock);
302 ReleaseWriteLock(&tb->lock);
304 ReleaseReadLock(&afs_bufferLock);
308 register afs_int32 vid;
309 {/* Flush all data and release all inode handles for a particular volume */
310 register struct buffer *tb;
311 register int code, rcode = 0;
312 ObtainReadLock(&afs_bufferLock);
313 for(tb=phTable[vHash(vid)]; tb; tb=tb->hashNext)
314 if (FidVolEq(tb->fid,vid)) {
315 ObtainWriteLock(&tb->lock);
317 code = ReallyWrite(tb->fid, tb->page, tb->data);
323 ReleaseWriteLock(&tb->lock);
325 ReleaseReadLock(&afs_bufferLock);
330 register afs_int32 *fid;
331 {/* Flush pages modified by one entry. */
332 register struct buffer *tb;
335 ObtainReadLock(&afs_bufferLock);
336 for(tb = phTable[pHash(fid)]; tb; tb=tb->hashNext)
337 if (FidEq(tb->fid, fid) && tb->dirty) {
338 ObtainWriteLock(&tb->lock);
340 code = ReallyWrite(tb->fid, tb->page, tb->data);
342 ReleaseWriteLock(&tb->lock);
343 ReleaseReadLock(&afs_bufferLock);
348 ReleaseWriteLock(&tb->lock);
350 ReleaseReadLock(&afs_bufferLock);
355 {/* Flush all the modified buffers. */
357 register struct buffer **tbp;
358 afs_int32 code, rcode;
362 ObtainReadLock(&afs_bufferLock);
363 for(i=0;i<nbuffers;i++,tbp++) {
365 ObtainWriteLock(&(*tbp)->lock);
367 ReleaseReadLock(&afs_bufferLock);
369 code = ReallyWrite((*tbp)->fid, (*tbp)->page, (*tbp)->data);
371 (*tbp)->dirty = 0; /* Clear the dirty flag */
372 if (code && !rcode) {
377 ReleaseWriteLock(&(*tbp)->lock);
378 ObtainReadLock(&afs_bufferLock);
381 ReleaseReadLock(&afs_bufferLock);
385 char *DNew (fid,page)
387 register afs_int32 *fid;
389 /* Same as read, only do *not* even try to read the page,
390 * since it probably doesn't exist.
392 register struct buffer *tb;
393 ObtainWriteLock(&afs_bufferLock);
394 if ((tb = newslot(fid,page,0)) == 0) {
395 ReleaseWriteLock(&afs_bufferLock);
398 ObtainWriteLock(&tb->lock);
399 ReleaseWriteLock(&afs_bufferLock);
401 ReleaseWriteLock(&tb->lock);