2 * Copyright (c) 1990, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * $FreeBSD: src/lib/libc/db/mpool/mpool.c,v 1.5.2.1 2001/03/05 23:05:01 obrien Exp $
34 * $DragonFly: src/lib/libc/db/mpool/mpool.c,v 1.3 2003/11/12 20:21:23 eirikn Exp $
36 * @(#)mpool.c 8.5 (Berkeley) 7/26/94
39 #include <sys/param.h>
40 #include <sys/queue.h>
51 #define __MPOOLINTERFACE_PRIVATE
54 static BKT *mpool_bkt (MPOOL *);
55 static BKT *mpool_look (MPOOL *, pgno_t);
56 static int mpool_write (MPOOL *, BKT *);
60 * Initialize a memory pool.
63 mpool_open(key, fd, pagesize, maxcache)
66 pgno_t pagesize, maxcache;
73 * Get information about the file.
76 * We don't currently handle pipes, although we should.
80 if (!S_ISREG(sb.st_mode)) {
85 /* Allocate and initialize the MPOOL cookie. */
86 if ((mp = (MPOOL *)calloc(1, sizeof(MPOOL))) == NULL)
89 for (entry = 0; entry < HASHSIZE; ++entry)
90 TAILQ_INIT(&mp->hqh[entry]);
91 mp->maxcache = maxcache;
92 mp->npages = sb.st_size / pagesize;
93 mp->pagesize = pagesize;
100 * Initialize input/output filters.
103 mpool_filter(mp, pgin, pgout, pgcookie)
105 void (*pgin) (void *, pgno_t, void *);
106 void (*pgout) (void *, pgno_t, void *);
111 mp->pgcookie = pgcookie;
116 * Get a new page of memory.
119 mpool_new(mp, pgnoaddr)
126 if (mp->npages == MAX_PAGE_NUMBER) {
127 (void)fprintf(stderr, "mpool_new: page allocation overflow.\n");
134 * Get a BKT from the cache. Assign a new page number, attach
135 * it to the head of the hash chain, the tail of the lru chain,
138 if ((bp = mpool_bkt(mp)) == NULL)
140 *pgnoaddr = bp->pgno = mp->npages++;
141 bp->flags = MPOOL_PINNED;
143 head = &mp->hqh[HASHKEY(bp->pgno)];
144 TAILQ_INSERT_HEAD(head, bp, hq);
145 TAILQ_INSERT_TAIL(&mp->lqh, bp, q);
154 mpool_get(mp, pgno, flags)
157 u_int flags; /* XXX not used? */
164 /* Check for attempt to retrieve a non-existent page. */
165 if (pgno >= mp->npages) {
174 /* Check for a page that is cached. */
175 if ((bp = mpool_look(mp, pgno)) != NULL) {
177 if (bp->flags & MPOOL_PINNED) {
178 (void)fprintf(stderr,
179 "mpool_get: page %d already pinned\n", bp->pgno);
184 * Move the page to the head of the hash chain and the tail
187 head = &mp->hqh[HASHKEY(bp->pgno)];
188 TAILQ_REMOVE(head, bp, hq);
189 TAILQ_INSERT_HEAD(head, bp, hq);
190 TAILQ_REMOVE(&mp->lqh, bp, q);
191 TAILQ_INSERT_TAIL(&mp->lqh, bp, q);
193 /* Return a pinned page. */
194 bp->flags |= MPOOL_PINNED;
198 /* Get a page from the cache. */
199 if ((bp = mpool_bkt(mp)) == NULL)
202 /* Read in the contents. */
206 off = mp->pagesize * pgno;
207 if (lseek(mp->fd, off, SEEK_SET) != off)
209 if ((nr = _read(mp->fd, bp->page, mp->pagesize)) != mp->pagesize) {
215 /* Set the page number, pin the page. */
217 bp->flags = MPOOL_PINNED;
220 * Add the page to the head of the hash chain and the tail
223 head = &mp->hqh[HASHKEY(bp->pgno)];
224 TAILQ_INSERT_HEAD(head, bp, hq);
225 TAILQ_INSERT_TAIL(&mp->lqh, bp, q);
227 /* Run through the user's filter. */
228 if (mp->pgin != NULL)
229 (mp->pgin)(mp->pgcookie, bp->pgno, bp->page);
239 mpool_put(mp, page, flags)
249 bp = (BKT *)((char *)page - sizeof(BKT));
251 if (!(bp->flags & MPOOL_PINNED)) {
252 (void)fprintf(stderr,
253 "mpool_put: page %d not pinned\n", bp->pgno);
257 bp->flags &= ~MPOOL_PINNED;
258 bp->flags |= flags & MPOOL_DIRTY;
259 return (RET_SUCCESS);
264 * Close the buffer pool.
272 /* Free up any space allocated to the lru pages. */
273 while (!TAILQ_EMPTY(&mp->lqh)) {
274 bp = TAILQ_FIRST(&mp->lqh);
275 TAILQ_REMOVE(&mp->lqh, bp, q);
279 /* Free the MPOOL cookie. */
281 return (RET_SUCCESS);
286 * Sync the pool to disk.
294 /* Walk the lru chain, flushing any dirty pages to disk. */
295 TAILQ_FOREACH(bp, &mp->lqh, q)
296 if (bp->flags & MPOOL_DIRTY &&
297 mpool_write(mp, bp) == RET_ERROR)
300 /* Sync the file descriptor. */
301 return (_fsync(mp->fd) ? RET_ERROR : RET_SUCCESS);
306 * Get a page from the cache (or create one).
315 /* If under the max cached, always create a new page. */
316 if (mp->curcache < mp->maxcache)
320 * If the cache is max'd out, walk the lru list for a buffer we
321 * can flush. If we find one, write it (if necessary) and take it
322 * off any lists. If we don't find anything we grow the cache anyway.
323 * The cache never shrinks.
325 TAILQ_FOREACH(bp, &mp->lqh, q)
326 if (!(bp->flags & MPOOL_PINNED)) {
327 /* Flush if dirty. */
328 if (bp->flags & MPOOL_DIRTY &&
329 mpool_write(mp, bp) == RET_ERROR)
334 /* Remove from the hash and lru queues. */
335 head = &mp->hqh[HASHKEY(bp->pgno)];
336 TAILQ_REMOVE(head, bp, hq);
337 TAILQ_REMOVE(&mp->lqh, bp, q);
341 memset(bp, 0xff, sizeof(BKT) + mp->pagesize);
348 new: if ((bp = (BKT *)malloc(sizeof(BKT) + mp->pagesize)) == NULL)
353 #if defined(DEBUG) || defined(PURIFY)
354 memset(bp, 0xff, sizeof(BKT) + mp->pagesize);
356 bp->page = (char *)bp + sizeof(BKT);
363 * Write a page to disk.
376 /* Run through the user's filter. */
378 (mp->pgout)(mp->pgcookie, bp->pgno, bp->page);
380 off = mp->pagesize * bp->pgno;
381 if (lseek(mp->fd, off, SEEK_SET) != off)
383 if (_write(mp->fd, bp->page, mp->pagesize) != mp->pagesize)
386 bp->flags &= ~MPOOL_DIRTY;
387 return (RET_SUCCESS);
392 * Lookup a page in the cache.
402 head = &mp->hqh[HASHKEY(pgno)];
403 TAILQ_FOREACH(bp, head, hq)
404 if (bp->pgno == pgno) {
419 * Print out cache statistics.
429 (void)fprintf(stderr, "%lu pages in the file\n", mp->npages);
430 (void)fprintf(stderr,
431 "page size %lu, cacheing %lu pages of %lu page max cache\n",
432 mp->pagesize, mp->curcache, mp->maxcache);
433 (void)fprintf(stderr, "%lu page puts, %lu page gets, %lu page new\n",
434 mp->pageput, mp->pageget, mp->pagenew);
435 (void)fprintf(stderr, "%lu page allocs, %lu page flushes\n",
436 mp->pagealloc, mp->pageflush);
437 if (mp->cachehit + mp->cachemiss)
438 (void)fprintf(stderr,
439 "%.0f%% cache hit rate (%lu hits, %lu misses)\n",
440 ((double)mp->cachehit / (mp->cachehit + mp->cachemiss))
441 * 100, mp->cachehit, mp->cachemiss);
442 (void)fprintf(stderr, "%lu page reads, %lu page writes\n",
443 mp->pageread, mp->pagewrite);
447 TAILQ_FOREACH(bp, &mp->lqh, q) {
448 (void)fprintf(stderr, "%s%d", sep, bp->pgno);
449 if (bp->flags & MPOOL_DIRTY)
450 (void)fprintf(stderr, "d");
451 if (bp->flags & MPOOL_PINNED)
452 (void)fprintf(stderr, "P");
460 (void)fprintf(stderr, "\n");