1 /* $NetBSD: bufcache.c,v 1.12 2008/04/28 20:23:08 martin Exp $ */
3 * Copyright (c) 2003 The NetBSD Foundation, Inc.
6 * This code is derived from software contributed to The NetBSD Foundation
7 * by Konrad E. Schroder <perseant@hhhh.org>.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
31 #include <sys/types.h>
32 #include <sys/param.h>
35 #include <sys/queue.h>
36 #include <sys/mount.h>
50 * Definitions for the buffer free lists.
52 #define BQUEUES 3 /* number of free buffer queues */
54 #define BQ_LOCKED 0 /* super-blocks &c */
55 #define BQ_LRU 1 /* lru, useful buffers */
56 #define BQ_AGE 2 /* rubbish */
58 TAILQ_HEAD(bqueues
, ubuf
) bufqueues
[BQUEUES
];
60 struct bufhash_struct
*bufhash
;
63 int hashmax
= HASH_MAX
;
64 int hashmask
= (HASH_MAX
- 1);
66 int maxbufs
= BUF_CACHE_SIZE
;
71 off_t locked_queue_bytes
= 0;
72 int locked_queue_count
= 0;
74 /* Simple buffer hash function */
76 vl_hash(struct uvnode
* vp
, daddr_t lbn
)
78 return (int)((unsigned long) vp
+ lbn
) & hashmask
;
81 /* Initialize buffer cache */
88 for (hashmax
= 1; hashmax
< max
; hashmax
<<= 1)
90 hashmask
= hashmax
- 1;
93 for (i
= 0; i
< BQUEUES
; i
++) {
94 TAILQ_INIT(&bufqueues
[i
]);
96 bufhash
= emalloc(hashmax
* sizeof(*bufhash
));
97 for (i
= 0; i
< hashmax
; i
++)
98 LIST_INIT(&bufhash
[i
]);
101 /* Widen the hash table. */
102 void bufrehash(int max
)
104 int i
, newhashmax
, newhashmask
;
105 struct ubuf
*bp
, *nbp
;
106 struct bufhash_struct
*np
;
108 if (max
< 0 || max
< hashmax
)
111 /* Round up to a power of two */
112 for (newhashmax
= 1; newhashmax
< max
; newhashmax
<<= 1)
114 newhashmask
= newhashmax
- 1;
116 /* Allocate new empty hash table, if we can */
117 np
= emalloc(newhashmax
* sizeof(*bufhash
));
118 for (i
= 0; i
< newhashmax
; i
++)
121 /* Now reassign all existing buffers to their new hash chains. */
122 for (i
= 0; i
< hashmax
; i
++) {
123 bp
= LIST_FIRST(&bufhash
[i
]);
125 nbp
= LIST_NEXT(bp
, b_hash
);
126 LIST_REMOVE(bp
, b_hash
);
127 bp
->b_hashval
= vl_hash(bp
->b_vp
, bp
->b_lblkno
);
128 LIST_INSERT_HEAD(&np
[bp
->b_hashval
], bp
, b_hash
);
133 /* Switch over and clean up */
136 hashmax
= newhashmax
;
137 hashmask
= newhashmask
;
140 /* Print statistics of buffer cache usage */
144 printf("buffer cache: %d hits %d misses (%2.2f%%); hash width %d, depth %d\n",
145 cachehits
, cachemisses
,
146 (cachehits
* 100.0) / (cachehits
+ cachemisses
),
151 * Remove a buffer from the cache.
152 * Caller must remove the buffer from its free list.
155 buf_destroy(struct ubuf
* bp
)
157 bp
->b_flags
|= B_NEEDCOMMIT
;
158 LIST_REMOVE(bp
, b_vnbufs
);
159 LIST_REMOVE(bp
, b_hash
);
160 if (!(bp
->b_flags
& B_DONTFREE
))
166 /* Remove a buffer from its free list. */
168 bremfree(struct ubuf
* bp
)
170 struct bqueues
*dp
= NULL
;
173 * We only calculate the head of the freelist when removing
174 * the last element of the list as that is the only time that
175 * it is needed (e.g. to reset the tail pointer).
177 * NB: This makes an assumption about how tailq's are implemented.
179 if (bp
->b_flags
& B_LOCKED
) {
180 locked_queue_bytes
-= bp
->b_bcount
;
181 --locked_queue_count
;
183 if (TAILQ_NEXT(bp
, b_freelist
) == NULL
) {
184 for (dp
= bufqueues
; dp
< &bufqueues
[BQUEUES
]; dp
++)
185 if (dp
->tqh_last
== &bp
->b_freelist
.tqe_next
)
187 if (dp
== &bufqueues
[BQUEUES
])
188 errx(1, "bremfree: lost tail");
190 ++bp
->b_vp
->v_usecount
;
191 TAILQ_REMOVE(dp
, bp
, b_freelist
);
194 /* Return a buffer if it is in the cache, otherwise return NULL. */
196 incore(struct uvnode
* vp
, int lbn
)
201 hash
= vl_hash(vp
, lbn
);
202 /* XXX use a real hash instead. */
204 LIST_FOREACH(bp
, &bufhash
[hash
], b_hash
) {
205 if (++depth
> max_depth
)
207 assert(depth
<= nbufs
);
208 if (bp
->b_vp
== vp
&& bp
->b_lblkno
== lbn
) {
216 * Return a buffer of the given size, lbn and uvnode.
217 * If none is in core, make a new one.
220 getblk(struct uvnode
* vp
, daddr_t lbn
, int size
)
228 * First check the buffer cache lists.
229 * We might sometimes need to resize a buffer. If we are growing
230 * the buffer, its contents are invalid; but shrinking is okay.
232 if ((bp
= incore(vp
, lbn
)) != NULL
) {
233 assert(!(bp
->b_flags
& B_NEEDCOMMIT
));
234 assert(!(bp
->b_flags
& B_BUSY
));
235 bp
->b_flags
|= B_BUSY
;
237 if (bp
->b_bcount
== size
)
239 else if (bp
->b_bcount
> size
) {
240 assert(!(bp
->b_flags
& B_DELWRI
));
242 bp
->b_data
= erealloc(bp
->b_data
, size
);
252 * Get a new block of the appropriate size and use that.
253 * If not enough space, free blocks from the AGE and LRU lists
256 while (nbufs
>= maxbufs
+ locked_queue_count
) {
257 bp
= TAILQ_FIRST(&bufqueues
[BQ_AGE
]);
259 TAILQ_REMOVE(&bufqueues
[BQ_AGE
], bp
, b_freelist
);
261 bp
= TAILQ_FIRST(&bufqueues
[BQ_LRU
]);
263 TAILQ_REMOVE(&bufqueues
[BQ_LRU
], bp
,
267 if (bp
->b_flags
& B_DELWRI
)
274 warnx("allocating more than %d buffers", maxbufs
);
281 bp
= ecalloc(1, sizeof(*bp
));
282 bp
->b_data
= ecalloc(1, size
);
284 bp
->b_blkno
= bp
->b_lblkno
= lbn
;
286 bp
->b_hashval
= vl_hash(vp
, lbn
);
287 LIST_INSERT_HEAD(&bufhash
[bp
->b_hashval
], bp
, b_hash
);
288 LIST_INSERT_HEAD(&vp
->v_cleanblkhd
, bp
, b_vnbufs
);
289 bp
->b_flags
= B_BUSY
;
294 /* Write a buffer to disk according to its strategy routine. */
296 bwrite(struct ubuf
* bp
)
298 bp
->b_flags
&= ~(B_READ
| B_DONE
| B_DELWRI
| B_LOCKED
);
300 bp
->b_flags
|= B_DONE
;
301 reassignbuf(bp
, bp
->b_vp
);
305 /* Put a buffer back on its free list, clear B_BUSY. */
307 brelse(struct ubuf
* bp
, int set
)
311 assert(!(bp
->b_flags
& B_NEEDCOMMIT
));
312 assert(bp
->b_flags
& B_BUSY
);
316 age
= bp
->b_flags
& B_AGE
;
317 bp
->b_flags
&= ~(B_BUSY
| B_AGE
);
318 if (bp
->b_flags
& B_INVAL
) {
322 if (bp
->b_flags
& B_LOCKED
) {
323 locked_queue_bytes
+= bp
->b_bcount
;
324 ++locked_queue_count
;
325 TAILQ_INSERT_TAIL(&bufqueues
[BQ_LOCKED
], bp
, b_freelist
);
327 TAILQ_INSERT_TAIL(&bufqueues
[BQ_AGE
], bp
, b_freelist
);
329 TAILQ_INSERT_TAIL(&bufqueues
[BQ_LRU
], bp
, b_freelist
);
331 --bp
->b_vp
->v_usecount
;
333 /* Move to the front of the hash chain */
334 if (LIST_FIRST(&bufhash
[bp
->b_hashval
]) != bp
) {
335 LIST_REMOVE(bp
, b_hash
);
336 LIST_INSERT_HEAD(&bufhash
[bp
->b_hashval
], bp
, b_hash
);
340 /* Read the given block from disk, return it B_BUSY. */
342 bread(struct uvnode
* vp
, daddr_t lbn
, int size
, void * unused
,
343 int flags
, struct ubuf
** bpp
)
349 bp
= getblk(vp
, lbn
, size
);
351 if (bp
->b_flags
& (B_DELWRI
| B_DONE
)) {
358 * Not found. Need to find that block's location on disk,
362 error
= VOP_BMAP(vp
, lbn
, &daddr
);
365 bp
->b_flags
|= B_READ
;
366 return VOP_STRATEGY(bp
);
368 memset(bp
->b_data
, 0, bp
->b_bcount
);
372 /* Move a buffer between dirty and clean block lists. */
374 reassignbuf(struct ubuf
* bp
, struct uvnode
* vp
)
376 LIST_REMOVE(bp
, b_vnbufs
);
377 if (bp
->b_flags
& B_DELWRI
) {
378 LIST_INSERT_HEAD(&vp
->v_dirtyblkhd
, bp
, b_vnbufs
);
380 LIST_INSERT_HEAD(&vp
->v_cleanblkhd
, bp
, b_vnbufs
);
386 dump_free_lists(void)
391 for (i
= 0; i
<= BQ_LOCKED
; i
++) {
392 printf("==> free list %d:\n", i
);
393 TAILQ_FOREACH(bp
, &bufqueues
[i
], b_freelist
) {
394 printf("vp %p lbn %" PRId64
" flags %lx\n",
395 bp
->b_vp
, bp
->b_lblkno
, bp
->b_flags
);