]>
git.saurik.com Git - apple/libc.git/blob - db.subproj/mpool.subproj/mpool.c
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 * Copyright (c) 1990, 1993
24 * The Regents of the University of California. All rights reserved.
26 * Redistribution and use in source and binary forms, with or without
27 * modification, are permitted provided that the following conditions
29 * 1. Redistributions of source code must retain the above copyright
30 * notice, this list of conditions and the following disclaimer.
31 * 2. Redistributions in binary form must reproduce the above copyright
32 * notice, this list of conditions and the following disclaimer in the
33 * documentation and/or other materials provided with the distribution.
34 * 3. All advertising materials mentioning features or use of this software
35 * must display the following acknowledgement:
36 * This product includes software developed by the University of
37 * California, Berkeley and its contributors.
38 * 4. Neither the name of the University nor the names of its contributors
39 * may be used to endorse or promote products derived from this software
40 * without specific prior written permission.
42 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
43 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
46 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
56 #include <sys/param.h>
66 #define __MPOOLINTERFACE_PRIVATE
69 static BKT
*mpool_bkt
__P((MPOOL
*));
70 static BKT
*mpool_look
__P((MPOOL
*, pgno_t
));
71 static int mpool_write
__P((MPOOL
*, BKT
*));
73 static void __mpoolerr
__P((const char *fmt
, ...));
77 * MPOOL_OPEN -- initialize a memory pool.
80 * key: Shared buffer key.
81 * fd: File descriptor.
82 * pagesize: File page size.
83 * maxcache: Max number of cached pages.
86 * MPOOL pointer, NULL on error.
89 mpool_open(key
, fd
, pagesize
, maxcache
)
92 pgno_t pagesize
, maxcache
;
101 * We should only set st_size to 0 for pipes -- 4.4BSD has the fix so
102 * that stat(2) returns true for ISSOCK on pipes. Until then, this is
105 if (!S_ISREG(sb
.st_mode
)) {
110 if ((mp
= (MPOOL
*)malloc(sizeof(MPOOL
))) == NULL
)
112 mp
->free
.cnext
= mp
->free
.cprev
= (BKT
*)&mp
->free
;
113 mp
->lru
.cnext
= mp
->lru
.cprev
= (BKT
*)&mp
->lru
;
114 for (entry
= 0; entry
< HASHSIZE
; ++entry
)
115 mp
->hashtable
[entry
].hnext
= mp
->hashtable
[entry
].hprev
=
116 mp
->hashtable
[entry
].cnext
= mp
->hashtable
[entry
].cprev
=
117 (BKT
*)&mp
->hashtable
[entry
];
119 mp
->maxcache
= maxcache
;
120 mp
->pagesize
= pagesize
;
121 mp
->npages
= sb
.st_size
/ pagesize
;
124 mp
->pgin
= mp
->pgout
= NULL
;
127 mp
->cachehit
= mp
->cachemiss
= mp
->pagealloc
= mp
->pageflush
=
128 mp
->pageget
= mp
->pagenew
= mp
->pageput
= mp
->pageread
=
135 * MPOOL_FILTER -- initialize input/output filters.
138 * pgin: Page in conversion routine.
139 * pgout: Page out conversion routine.
140 * pgcookie: Cookie for page in/out routines.
143 mpool_filter(mp
, pgin
, pgout
, pgcookie
)
145 void (*pgin
) __P((void *, pgno_t
, void *));
146 void (*pgout
) __P((void *, pgno_t
, void *));
151 mp
->pgcookie
= pgcookie
;
155 * MPOOL_NEW -- get a new page
159 * pgnoadddr: place to store new page number
161 * RET_ERROR, RET_SUCCESS
164 mpool_new(mp
, pgnoaddr
)
175 * Get a BKT from the cache. Assign a new page number, attach it to
176 * the hash and lru chains and return.
178 if ((b
= mpool_bkt(mp
)) == NULL
)
180 *pgnoaddr
= b
->pgno
= mp
->npages
++;
181 b
->flags
= MPOOL_PINNED
;
183 inschain(b
, &mp
->lru
);
188 * MPOOL_GET -- get a page from the pool
196 * RET_ERROR, RET_SUCCESS
199 mpool_get(mp
, pgno
, flags
)
202 u_int flags
; /* XXX not used? */
210 * If asking for a specific page that is already in the cache, find
213 if (b
= mpool_look(mp
, pgno
)) {
218 if (b
->flags
& MPOOL_PINNED
)
219 __mpoolerr("mpool_get: page %d already pinned",
223 inschain(b
, &mp
->lru
);
224 b
->flags
|= MPOOL_PINNED
;
228 /* Not allowed to retrieve a non-existent page. */
229 if (pgno
>= mp
->npages
) {
234 /* Get a page from the cache. */
235 if ((b
= mpool_bkt(mp
)) == NULL
)
238 b
->flags
= MPOOL_PINNED
;
243 /* Read in the contents. */
244 off
= mp
->pagesize
* pgno
;
245 if (lseek(mp
->fd
, off
, SEEK_SET
) != off
)
247 if ((nr
= read(mp
->fd
, b
->page
, mp
->pagesize
)) != mp
->pagesize
) {
253 (mp
->pgin
)(mp
->pgcookie
, b
->pgno
, b
->page
);
256 inschain(b
, &mp
->lru
);
264 * MPOOL_PUT -- return a page to the pool
272 * RET_ERROR, RET_SUCCESS
275 mpool_put(mp
, page
, flags
)
288 baddr
= (BKT
*)((char *)page
- sizeof(BKT
));
290 if (!(baddr
->flags
& MPOOL_PINNED
))
291 __mpoolerr("mpool_put: page %d not pinned", b
->pgno
);
292 for (b
= mp
->lru
.cnext
; b
!= (BKT
*)&mp
->lru
; b
= b
->cnext
) {
293 if (b
== (BKT
*)&mp
->lru
)
294 __mpoolerr("mpool_put: %0x: bad address", baddr
);
299 baddr
->flags
&= ~MPOOL_PINNED
;
300 baddr
->flags
|= flags
& MPOOL_DIRTY
;
301 return (RET_SUCCESS
);
305 * MPOOL_CLOSE -- close the buffer pool
311 * RET_ERROR, RET_SUCCESS
319 /* Free up any space allocated to the lru pages. */
320 for (b
= mp
->lru
.cprev
; b
!= (BKT
*)&mp
->lru
; b
= next
) {
325 return (RET_SUCCESS
);
329 * MPOOL_SYNC -- sync the file to disk.
335 * RET_ERROR, RET_SUCCESS
343 for (b
= mp
->lru
.cprev
; b
!= (BKT
*)&mp
->lru
; b
= b
->cprev
)
344 if (b
->flags
& MPOOL_DIRTY
&& mpool_write(mp
, b
) == RET_ERROR
)
346 return (fsync(mp
->fd
) ? RET_ERROR
: RET_SUCCESS
);
350 * MPOOL_BKT -- get/create a BKT from the cache
356 * NULL on failure and a pointer to the BKT on success
364 if (mp
->curcache
< mp
->maxcache
)
368 * If the cache is maxxed out, search the lru list for a buffer we
369 * can flush. If we find one, write it if necessary and take it off
370 * any lists. If we don't find anything we grow the cache anyway.
371 * The cache never shrinks.
373 for (b
= mp
->lru
.cprev
; b
!= (BKT
*)&mp
->lru
; b
= b
->cprev
)
374 if (!(b
->flags
& MPOOL_PINNED
)) {
375 if (b
->flags
& MPOOL_DIRTY
&&
376 mpool_write(mp
, b
) == RET_ERROR
)
387 memset(b
, 0xff, sizeof(BKT
) + mp
->pagesize
);
394 new: if ((b
= (BKT
*)malloc(sizeof(BKT
) + mp
->pagesize
)) == NULL
)
400 memset(b
, 0xff, sizeof(BKT
) + mp
->pagesize
);
402 b
->page
= (char *)b
+ sizeof(BKT
);
408 * MPOOL_WRITE -- sync a page to disk
414 * RET_ERROR, RET_SUCCESS
424 (mp
->pgout
)(mp
->pgcookie
, b
->pgno
, b
->page
);
429 off
= mp
->pagesize
* b
->pgno
;
430 if (lseek(mp
->fd
, off
, SEEK_SET
) != off
)
432 if (write(mp
->fd
, b
->page
, mp
->pagesize
) != mp
->pagesize
)
434 b
->flags
&= ~MPOOL_DIRTY
;
435 return (RET_SUCCESS
);
439 * MPOOL_LOOK -- lookup a page
446 * NULL on failure and a pointer to the BKT on success
457 * If find the buffer, put it first on the hash chain so can
458 * find it again quickly.
460 tb
= &mp
->hashtable
[HASHKEY(pgno
)];
461 for (b
= tb
->hnext
; b
!= (BKT
*)tb
; b
= b
->hnext
)
462 if (b
->pgno
== pgno
) {
476 * MPOOL_STAT -- cache statistics
489 (void)fprintf(stderr
, "%lu pages in the file\n", mp
->npages
);
490 (void)fprintf(stderr
,
491 "page size %lu, cacheing %lu pages of %lu page max cache\n",
492 mp
->pagesize
, mp
->curcache
, mp
->maxcache
);
493 (void)fprintf(stderr
, "%lu page puts, %lu page gets, %lu page new\n",
494 mp
->pageput
, mp
->pageget
, mp
->pagenew
);
495 (void)fprintf(stderr
, "%lu page allocs, %lu page flushes\n",
496 mp
->pagealloc
, mp
->pageflush
);
497 if (mp
->cachehit
+ mp
->cachemiss
)
498 (void)fprintf(stderr
,
499 "%.0f%% cache hit rate (%lu hits, %lu misses)\n",
500 ((double)mp
->cachehit
/ (mp
->cachehit
+ mp
->cachemiss
))
501 * 100, mp
->cachehit
, mp
->cachemiss
);
502 (void)fprintf(stderr
, "%lu page reads, %lu page writes\n",
503 mp
->pageread
, mp
->pagewrite
);
507 for (b
= mp
->lru
.cnext
; b
!= (BKT
*)&mp
->lru
; b
= b
->cnext
) {
508 (void)fprintf(stderr
, "%s%d", sep
, b
->pgno
);
509 if (b
->flags
& MPOOL_DIRTY
)
510 (void)fprintf(stderr
, "d");
511 if (b
->flags
& MPOOL_PINNED
)
512 (void)fprintf(stderr
, "P");
520 (void)fprintf(stderr
, "\n");
533 __mpoolerr(const char *fmt
, ...)
535 __mpoolerr(fmt
, va_alist
)
546 (void)vfprintf(stderr
, fmt
, ap
);
548 (void)fprintf(stderr
, "\n");