2 * Copyright (c) 1990 The Regents of the University of California.
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
34 #if defined(LIBC_SCCS) && !defined(lint)
35 static char sccsid
[] = "@(#)mpool.c 5.5 (Berkeley) 2/19/93";
36 #endif /* LIBC_SCCS and not lint */
38 #include <sys/param.h>
48 #define __MPOOLINTERFACE_PRIVATE
51 static BKT
*mpool_bkt
__P((MPOOL
*));
52 static BKT
*mpool_look
__P((MPOOL
*, pgno_t
));
53 static int mpool_write
__P((MPOOL
*, BKT
*));
55 static void err
__P((const char *fmt
, ...));
59 * MPOOL_OPEN -- initialize a memory pool.
62 * key: Shared buffer key.
63 * fd: File descriptor.
64 * pagesize: File page size.
65 * maxcache: Max number of cached pages.
68 * MPOOL pointer, NULL on error.
71 mpool_open(key
, fd
, pagesize
, maxcache
)
74 pgno_t pagesize
, maxcache
;
83 * We should only set st_size to 0 for pipes -- 4.4BSD has the fix so
84 * that stat(2) returns true for ISSOCK on pipes. Until then, this is
87 if (!S_ISREG(sb
.st_mode
)) {
92 if ((mp
= malloc(sizeof(MPOOL
))) == NULL
)
94 mp
->free
.cnext
= mp
->free
.cprev
= (BKT
*)&mp
->free
;
95 mp
->lru
.cnext
= mp
->lru
.cprev
= (BKT
*)&mp
->lru
;
96 for (entry
= 0; entry
< HASHSIZE
; ++entry
)
97 mp
->hashtable
[entry
].hnext
= mp
->hashtable
[entry
].hprev
=
98 mp
->hashtable
[entry
].cnext
= mp
->hashtable
[entry
].cprev
=
99 (BKT
*)&mp
->hashtable
[entry
];
101 mp
->maxcache
= maxcache
;
102 mp
->pagesize
= pagesize
;
103 mp
->npages
= sb
.st_size
/ pagesize
;
106 mp
->pgin
= mp
->pgout
= NULL
;
109 mp
->cachehit
= mp
->cachemiss
= mp
->pagealloc
= mp
->pageflush
=
110 mp
->pageget
= mp
->pagenew
= mp
->pageput
= mp
->pageread
=
117 * MPOOL_FILTER -- initialize input/output filters.
120 * pgin: Page in conversion routine.
121 * pgout: Page out conversion routine.
122 * pgcookie: Cookie for page in/out routines.
125 mpool_filter(mp
, pgin
, pgout
, pgcookie
)
127 void (*pgin
) __P((void *, pgno_t
, void *));
128 void (*pgout
) __P((void *, pgno_t
, void *));
133 mp
->pgcookie
= pgcookie
;
137 * MPOOL_NEW -- get a new page
141 * pgnoadddr: place to store new page number
143 * RET_ERROR, RET_SUCCESS
146 mpool_new(mp
, pgnoaddr
)
157 * Get a BKT from the cache. Assign a new page number, attach it to
158 * the hash and lru chains and return.
160 if ((b
= mpool_bkt(mp
)) == NULL
)
162 *pgnoaddr
= b
->pgno
= mp
->npages
++;
163 b
->flags
= MPOOL_PINNED
;
165 inschain(b
, &mp
->lru
);
170 * MPOOL_GET -- get a page from the pool
178 * RET_ERROR, RET_SUCCESS
181 mpool_get(mp
, pgno
, flags
)
184 u_int flags
; /* XXX not used? */
192 * If asking for a specific page that is already in the cache, find
195 if (b
= mpool_look(mp
, pgno
)) {
200 if (b
->flags
& MPOOL_PINNED
)
201 err("mpool_get: page %d already pinned", b
->pgno
);
204 inschain(b
, &mp
->lru
);
205 b
->flags
|= MPOOL_PINNED
;
209 /* Not allowed to retrieve a non-existent page. */
210 if (pgno
>= mp
->npages
) {
215 /* Get a page from the cache. */
216 if ((b
= mpool_bkt(mp
)) == NULL
)
219 b
->flags
= MPOOL_PINNED
;
224 /* Read in the contents. */
225 off
= mp
->pagesize
* pgno
;
226 if (lseek(mp
->fd
, off
, SEEK_SET
) != off
)
228 if ((nr
= read(mp
->fd
, b
->page
, mp
->pagesize
)) != mp
->pagesize
) {
234 (mp
->pgin
)(mp
->pgcookie
, b
->pgno
, b
->page
);
237 inschain(b
, &mp
->lru
);
245 * MPOOL_PUT -- return a page to the pool
253 * RET_ERROR, RET_SUCCESS
256 mpool_put(mp
, page
, flags
)
269 baddr
= (BKT
*)((char *)page
- sizeof(BKT
));
271 if (!(baddr
->flags
& MPOOL_PINNED
))
272 err("mpool_put: page %d not pinned", b
->pgno
);
273 for (b
= mp
->lru
.cnext
; b
!= (BKT
*)&mp
->lru
; b
= b
->cnext
) {
274 if (b
== (BKT
*)&mp
->lru
)
275 err("mpool_put: %0x: bad address", baddr
);
280 baddr
->flags
&= ~MPOOL_PINNED
;
281 baddr
->flags
|= flags
& MPOOL_DIRTY
;
282 return (RET_SUCCESS
);
286 * MPOOL_CLOSE -- close the buffer pool
292 * RET_ERROR, RET_SUCCESS
300 /* Free up any space allocated to the lru pages. */
301 for (b
= mp
->lru
.cprev
; b
!= (BKT
*)&mp
->lru
; b
= next
) {
306 return (RET_SUCCESS
);
310 * MPOOL_SYNC -- sync the file to disk.
316 * RET_ERROR, RET_SUCCESS
324 for (b
= mp
->lru
.cprev
; b
!= (BKT
*)&mp
->lru
; b
= b
->cprev
)
325 if (b
->flags
& MPOOL_DIRTY
&& mpool_write(mp
, b
) == RET_ERROR
)
327 return (fsync(mp
->fd
) ? RET_ERROR
: RET_SUCCESS
);
331 * MPOOL_BKT -- get/create a BKT from the cache
337 * NULL on failure and a pointer to the BKT on success
345 if (mp
->curcache
< mp
->maxcache
)
349 * If the cache is maxxed out, search the lru list for a buffer we
350 * can flush. If we find one, write it if necessary and take it off
351 * any lists. If we don't find anything we grow the cache anyway.
352 * The cache never shrinks.
354 for (b
= mp
->lru
.cprev
; b
!= (BKT
*)&mp
->lru
; b
= b
->cprev
)
355 if (!(b
->flags
& MPOOL_PINNED
)) {
356 if (b
->flags
& MPOOL_DIRTY
&&
357 mpool_write(mp
, b
) == RET_ERROR
)
368 memset(b
, 0xff, sizeof(BKT
) + mp
->pagesize
);
375 new: if ((b
= malloc(sizeof(BKT
) + mp
->pagesize
)) == NULL
)
381 memset(b
, 0xff, sizeof(BKT
) + mp
->pagesize
);
383 b
->page
= (char *)b
+ sizeof(BKT
);
389 * MPOOL_WRITE -- sync a page to disk
395 * RET_ERROR, RET_SUCCESS
405 (mp
->pgout
)(mp
->pgcookie
, b
->pgno
, b
->page
);
410 off
= mp
->pagesize
* b
->pgno
;
411 if (lseek(mp
->fd
, off
, SEEK_SET
) != off
)
413 if (write(mp
->fd
, b
->page
, mp
->pagesize
) != mp
->pagesize
)
415 b
->flags
&= ~MPOOL_DIRTY
;
416 return (RET_SUCCESS
);
420 * MPOOL_LOOK -- lookup a page
427 * NULL on failure and a pointer to the BKT on success
438 * If find the buffer, put it first on the hash chain so can
439 * find it again quickly.
441 tb
= &mp
->hashtable
[HASHKEY(pgno
)];
442 for (b
= tb
->hnext
; b
!= (BKT
*)tb
; b
= b
->hnext
)
443 if (b
->pgno
== pgno
) {
457 * MPOOL_STAT -- cache statistics
470 (void)fprintf(stderr
, "%lu pages in the file\n", mp
->npages
);
471 (void)fprintf(stderr
,
472 "page size %lu, cacheing %lu pages of %lu page max cache\n",
473 mp
->pagesize
, mp
->curcache
, mp
->maxcache
);
474 (void)fprintf(stderr
, "%lu page puts, %lu page gets, %lu page new\n",
475 mp
->pageput
, mp
->pageget
, mp
->pagenew
);
476 (void)fprintf(stderr
, "%lu page allocs, %lu page flushes\n",
477 mp
->pagealloc
, mp
->pageflush
);
478 if (mp
->cachehit
+ mp
->cachemiss
)
479 (void)fprintf(stderr
,
480 "%.0f%% cache hit rate (%lu hits, %lu misses)\n",
481 ((double)mp
->cachehit
/ (mp
->cachehit
+ mp
->cachemiss
))
482 * 100, mp
->cachehit
, mp
->cachemiss
);
483 (void)fprintf(stderr
, "%lu page reads, %lu page writes\n",
484 mp
->pageread
, mp
->pagewrite
);
488 for (b
= mp
->lru
.cnext
; b
!= (BKT
*)&mp
->lru
; b
= b
->cnext
) {
489 (void)fprintf(stderr
, "%s%d", sep
, b
->pgno
);
490 if (b
->flags
& MPOOL_DIRTY
)
491 (void)fprintf(stderr
, "d");
492 if (b
->flags
& MPOOL_PINNED
)
493 (void)fprintf(stderr
, "P");
501 (void)fprintf(stderr
, "\n");
514 err(const char *fmt
, ...)
527 (void)vfprintf(stderr
, fmt
, ap
);
529 (void)fprintf(stderr
, "\n");