]>
git.saurik.com Git - apple/libc.git/blob - db/mpool/mpool.c
55bdefe96aadbd1a45697aad0c81855fc8cbdd76
2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
24 * Copyright (c) 1990, 1993, 1994
25 * The Regents of the University of California. All rights reserved.
27 * Redistribution and use in source and binary forms, with or without
28 * modification, are permitted provided that the following conditions
30 * 1. Redistributions of source code must retain the above copyright
31 * notice, this list of conditions and the following disclaimer.
32 * 2. Redistributions in binary form must reproduce the above copyright
33 * notice, this list of conditions and the following disclaimer in the
34 * documentation and/or other materials provided with the distribution.
35 * 3. All advertising materials mentioning features or use of this software
36 * must display the following acknowledgement:
37 * This product includes software developed by the University of
38 * California, Berkeley and its contributors.
39 * 4. Neither the name of the University nor the names of its contributors
40 * may be used to endorse or promote products derived from this software
41 * without specific prior written permission.
43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 #if defined(LIBC_SCCS) && !defined(lint)
57 static char sccsid
[] = "@(#)mpool.c 8.5 (Berkeley) 7/26/94";
58 #endif /* LIBC_SCCS and not lint */
59 #include <sys/cdefs.h>
61 #include <sys/param.h>
62 #include <sys/queue.h>
73 #define __MPOOLINTERFACE_PRIVATE
76 static BKT
*mpool_bkt(MPOOL
*);
77 static BKT
*mpool_look(MPOOL
*, pgno_t
);
78 static int mpool_write(MPOOL
*, BKT
*);
82 * Initialize a memory pool.
85 mpool_open(key
, fd
, pagesize
, maxcache
)
88 pgno_t pagesize
, maxcache
;
95 * Get information about the file.
98 * We don't currently handle pipes, although we should.
102 if (!S_ISREG(sb
.st_mode
)) {
107 /* Allocate and initialize the MPOOL cookie. */
108 if ((mp
= (MPOOL
*)calloc(1, sizeof(MPOOL
))) == NULL
)
110 TAILQ_INIT(&mp
->lqh
);
111 for (entry
= 0; entry
< HASHSIZE
; ++entry
)
112 TAILQ_INIT(&mp
->hqh
[entry
]);
113 mp
->maxcache
= maxcache
;
114 mp
->npages
= sb
.st_size
/ pagesize
;
115 mp
->pagesize
= pagesize
;
122 * Initialize input/output filters.
125 mpool_filter(mp
, pgin
, pgout
, pgcookie
)
127 void (*pgin
)(void *, pgno_t
, void *);
128 void (*pgout
)(void *, pgno_t
, void *);
133 mp
->pgcookie
= pgcookie
;
138 * Get a new page of memory.
141 mpool_new(mp
, pgnoaddr
)
148 if (mp
->npages
== MAX_PAGE_NUMBER
) {
149 (void)fprintf(stderr
, "mpool_new: page allocation overflow.\n");
156 * Get a BKT from the cache. Assign a new page number, attach
157 * it to the head of the hash chain, the tail of the lru chain,
160 if ((bp
= mpool_bkt(mp
)) == NULL
)
162 *pgnoaddr
= bp
->pgno
= mp
->npages
++;
163 bp
->flags
= MPOOL_PINNED
;
165 head
= &mp
->hqh
[HASHKEY(bp
->pgno
)];
166 TAILQ_INSERT_HEAD(head
, bp
, hq
);
167 TAILQ_INSERT_TAIL(&mp
->lqh
, bp
, q
);
176 mpool_get(mp
, pgno
, flags
)
179 u_int flags
; /* XXX not used? */
186 /* Check for attempt to retrieve a non-existent page. */
187 if (pgno
>= mp
->npages
) {
196 /* Check for a page that is cached. */
197 if ((bp
= mpool_look(mp
, pgno
)) != NULL
) {
199 if (bp
->flags
& MPOOL_PINNED
) {
200 (void)fprintf(stderr
,
201 "mpool_get: page %d already pinned\n", bp
->pgno
);
206 * Move the page to the head of the hash chain and the tail
209 head
= &mp
->hqh
[HASHKEY(bp
->pgno
)];
210 TAILQ_REMOVE(head
, bp
, hq
);
211 TAILQ_INSERT_HEAD(head
, bp
, hq
);
212 TAILQ_REMOVE(&mp
->lqh
, bp
, q
);
213 TAILQ_INSERT_TAIL(&mp
->lqh
, bp
, q
);
215 /* Return a pinned page. */
216 bp
->flags
|= MPOOL_PINNED
;
220 /* Get a page from the cache. */
221 if ((bp
= mpool_bkt(mp
)) == NULL
)
224 /* Read in the contents. */
228 off
= mp
->pagesize
* pgno
;
229 if (lseek(mp
->fd
, off
, SEEK_SET
) != off
)
231 if ((nr
= read(mp
->fd
, bp
->page
, mp
->pagesize
)) != mp
->pagesize
) {
237 /* Set the page number, pin the page. */
239 bp
->flags
= MPOOL_PINNED
;
242 * Add the page to the head of the hash chain and the tail
245 head
= &mp
->hqh
[HASHKEY(bp
->pgno
)];
246 TAILQ_INSERT_HEAD(head
, bp
, hq
);
247 TAILQ_INSERT_TAIL(&mp
->lqh
, bp
, q
);
249 /* Run through the user's filter. */
250 if (mp
->pgin
!= NULL
)
251 (mp
->pgin
)(mp
->pgcookie
, bp
->pgno
, bp
->page
);
261 mpool_put(mp
, page
, flags
)
271 bp
= (BKT
*)((char *)page
- sizeof(BKT
));
273 if (!(bp
->flags
& MPOOL_PINNED
)) {
274 (void)fprintf(stderr
,
275 "mpool_put: page %d not pinned\n", bp
->pgno
);
279 bp
->flags
&= ~MPOOL_PINNED
;
280 bp
->flags
|= flags
& MPOOL_DIRTY
;
281 return (RET_SUCCESS
);
286 * Close the buffer pool.
294 /* Free up any space allocated to the lru pages. */
295 while (!TAILQ_EMPTY(&mp
->lqh
)) {
296 bp
= TAILQ_FIRST(&mp
->lqh
);
297 TAILQ_REMOVE(&mp
->lqh
, bp
, q
);
301 /* Free the MPOOL cookie. */
303 return (RET_SUCCESS
);
308 * Sync the pool to disk.
316 /* Walk the lru chain, flushing any dirty pages to disk. */
317 TAILQ_FOREACH(bp
, &mp
->lqh
, q
)
318 if (bp
->flags
& MPOOL_DIRTY
&&
319 mpool_write(mp
, bp
) == RET_ERROR
)
322 /* Sync the file descriptor. */
323 return (fsync(mp
->fd
) ? RET_ERROR
: RET_SUCCESS
);
328 * Get a page from the cache (or create one).
337 /* If under the max cached, always create a new page. */
338 if (mp
->curcache
< mp
->maxcache
)
342 * If the cache is max'd out, walk the lru list for a buffer we
343 * can flush. If we find one, write it (if necessary) and take it
344 * off any lists. If we don't find anything we grow the cache anyway.
345 * The cache never shrinks.
347 TAILQ_FOREACH(bp
, &mp
->lqh
, q
)
348 if (!(bp
->flags
& MPOOL_PINNED
)) {
349 /* Flush if dirty. */
350 if (bp
->flags
& MPOOL_DIRTY
&&
351 mpool_write(mp
, bp
) == RET_ERROR
)
356 /* Remove from the hash and lru queues. */
357 head
= &mp
->hqh
[HASHKEY(bp
->pgno
)];
358 TAILQ_REMOVE(head
, bp
, hq
);
359 TAILQ_REMOVE(&mp
->lqh
, bp
, q
);
363 memset(bp
, 0xff, sizeof(BKT
) + mp
->pagesize
);
370 new: if ((bp
= (BKT
*)malloc(sizeof(BKT
) + mp
->pagesize
)) == NULL
)
375 #if defined(DEBUG) || defined(PURIFY)
376 memset(bp
, 0xff, sizeof(BKT
) + mp
->pagesize
);
378 bp
->page
= (char *)bp
+ sizeof(BKT
);
385 * Write a page to disk.
398 /* Run through the user's filter. */
400 (mp
->pgout
)(mp
->pgcookie
, bp
->pgno
, bp
->page
);
402 off
= mp
->pagesize
* bp
->pgno
;
403 if (lseek(mp
->fd
, off
, SEEK_SET
) != off
)
405 if (write(mp
->fd
, bp
->page
, mp
->pagesize
) != mp
->pagesize
)
408 bp
->flags
&= ~MPOOL_DIRTY
;
409 return (RET_SUCCESS
);
414 * Lookup a page in the cache.
424 head
= &mp
->hqh
[HASHKEY(pgno
)];
425 TAILQ_FOREACH(bp
, head
, hq
)
426 if (bp
->pgno
== pgno
) {
441 * Print out cache statistics.
451 (void)fprintf(stderr
, "%lu pages in the file\n", mp
->npages
);
452 (void)fprintf(stderr
,
453 "page size %lu, cacheing %lu pages of %lu page max cache\n",
454 mp
->pagesize
, mp
->curcache
, mp
->maxcache
);
455 (void)fprintf(stderr
, "%lu page puts, %lu page gets, %lu page new\n",
456 mp
->pageput
, mp
->pageget
, mp
->pagenew
);
457 (void)fprintf(stderr
, "%lu page allocs, %lu page flushes\n",
458 mp
->pagealloc
, mp
->pageflush
);
459 if (mp
->cachehit
+ mp
->cachemiss
)
460 (void)fprintf(stderr
,
461 "%.0f%% cache hit rate (%lu hits, %lu misses)\n",
462 ((double)mp
->cachehit
/ (mp
->cachehit
+ mp
->cachemiss
))
463 * 100, mp
->cachehit
, mp
->cachemiss
);
464 (void)fprintf(stderr
, "%lu page reads, %lu page writes\n",
465 mp
->pageread
, mp
->pagewrite
);
469 TAILQ_FOREACH(bp
, &mp
->lqh
, q
) {
470 (void)fprintf(stderr
, "%s%d", sep
, bp
->pgno
);
471 if (bp
->flags
& MPOOL_DIRTY
)
472 (void)fprintf(stderr
, "d");
473 if (bp
->flags
& MPOOL_PINNED
)
474 (void)fprintf(stderr
, "P");
482 (void)fprintf(stderr
, "\n");