]> git.saurik.com Git - apple/libc.git/blame - db/mpool/mpool.c
Libc-320.tar.gz
[apple/libc.git] / db / mpool / mpool.c
CommitLineData
e9ce8d39 1/*
9385eb3d 2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
e9ce8d39
A
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
734aad71 6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
e9ce8d39 7 *
734aad71
A
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
e9ce8d39
A
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
734aad71
A
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
e9ce8d39
A
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
9385eb3d
A
25/*-
26 * Copyright (c) 1990, 1993, 1994
e9ce8d39
A
27 * The Regents of the University of California. All rights reserved.
28 *
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions
31 * are met:
32 * 1. Redistributions of source code must retain the above copyright
33 * notice, this list of conditions and the following disclaimer.
34 * 2. Redistributions in binary form must reproduce the above copyright
35 * notice, this list of conditions and the following disclaimer in the
36 * documentation and/or other materials provided with the distribution.
37 * 3. All advertising materials mentioning features or use of this software
38 * must display the following acknowledgement:
39 * This product includes software developed by the University of
40 * California, Berkeley and its contributors.
41 * 4. Neither the name of the University nor the names of its contributors
42 * may be used to endorse or promote products derived from this software
43 * without specific prior written permission.
44 *
45 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
48 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * SUCH DAMAGE.
56 */
57
9385eb3d
A
58#if defined(LIBC_SCCS) && !defined(lint)
59static char sccsid[] = "@(#)mpool.c 8.5 (Berkeley) 7/26/94";
60#endif /* LIBC_SCCS and not lint */
61#include <sys/cdefs.h>
e9ce8d39
A
62
63#include <sys/param.h>
9385eb3d 64#include <sys/queue.h>
e9ce8d39
A
65#include <sys/stat.h>
66
67#include <errno.h>
68#include <stdio.h>
69#include <stdlib.h>
70#include <string.h>
71#include <unistd.h>
72
73#include <db.h>
9385eb3d 74
e9ce8d39 75#define __MPOOLINTERFACE_PRIVATE
9385eb3d 76#include <mpool.h>
e9ce8d39 77
9385eb3d
A
78static BKT *mpool_bkt(MPOOL *);
79static BKT *mpool_look(MPOOL *, pgno_t);
80static int mpool_write(MPOOL *, BKT *);
e9ce8d39
A
81
82/*
9385eb3d
A
83 * mpool_open --
84 * Initialize a memory pool.
e9ce8d39
A
85 */
86MPOOL *
87mpool_open(key, fd, pagesize, maxcache)
9385eb3d 88 void *key;
e9ce8d39
A
89 int fd;
90 pgno_t pagesize, maxcache;
91{
92 struct stat sb;
93 MPOOL *mp;
94 int entry;
95
9385eb3d
A
96 /*
97 * Get information about the file.
98 *
99 * XXX
100 * We don't currently handle pipes, although we should.
101 */
e9ce8d39
A
102 if (fstat(fd, &sb))
103 return (NULL);
e9ce8d39
A
104 if (!S_ISREG(sb.st_mode)) {
105 errno = ESPIPE;
106 return (NULL);
107 }
108
9385eb3d
A
109 /* Allocate and initialize the MPOOL cookie. */
110 if ((mp = (MPOOL *)calloc(1, sizeof(MPOOL))) == NULL)
e9ce8d39 111 return (NULL);
9385eb3d 112 TAILQ_INIT(&mp->lqh);
e9ce8d39 113 for (entry = 0; entry < HASHSIZE; ++entry)
9385eb3d 114 TAILQ_INIT(&mp->hqh[entry]);
e9ce8d39 115 mp->maxcache = maxcache;
e9ce8d39 116 mp->npages = sb.st_size / pagesize;
9385eb3d 117 mp->pagesize = pagesize;
e9ce8d39 118 mp->fd = fd;
e9ce8d39
A
119 return (mp);
120}
121
122/*
9385eb3d
A
123 * mpool_filter --
124 * Initialize input/output filters.
e9ce8d39
A
125 */
126void
127mpool_filter(mp, pgin, pgout, pgcookie)
128 MPOOL *mp;
9385eb3d
A
129 void (*pgin)(void *, pgno_t, void *);
130 void (*pgout)(void *, pgno_t, void *);
e9ce8d39
A
131 void *pgcookie;
132{
133 mp->pgin = pgin;
134 mp->pgout = pgout;
135 mp->pgcookie = pgcookie;
136}
137
138/*
9385eb3d
A
139 * mpool_new --
140 * Get a new page of memory.
e9ce8d39
A
141 */
142void *
143mpool_new(mp, pgnoaddr)
144 MPOOL *mp;
145 pgno_t *pgnoaddr;
146{
9385eb3d
A
147 struct _hqh *head;
148 BKT *bp;
e9ce8d39 149
9385eb3d
A
150 if (mp->npages == MAX_PAGE_NUMBER) {
151 (void)fprintf(stderr, "mpool_new: page allocation overflow.\n");
152 abort();
153 }
e9ce8d39
A
154#ifdef STATISTICS
155 ++mp->pagenew;
156#endif
157 /*
9385eb3d
A
158 * Get a BKT from the cache. Assign a new page number, attach
159 * it to the head of the hash chain, the tail of the lru chain,
160 * and return.
e9ce8d39 161 */
9385eb3d 162 if ((bp = mpool_bkt(mp)) == NULL)
e9ce8d39 163 return (NULL);
9385eb3d
A
164 *pgnoaddr = bp->pgno = mp->npages++;
165 bp->flags = MPOOL_PINNED;
166
167 head = &mp->hqh[HASHKEY(bp->pgno)];
168 TAILQ_INSERT_HEAD(head, bp, hq);
169 TAILQ_INSERT_TAIL(&mp->lqh, bp, q);
170 return (bp->page);
e9ce8d39
A
171}
172
173/*
9385eb3d
A
174 * mpool_get
175 * Get a page.
e9ce8d39
A
176 */
177void *
178mpool_get(mp, pgno, flags)
179 MPOOL *mp;
180 pgno_t pgno;
9385eb3d 181 u_int flags; /* XXX not used? */
e9ce8d39 182{
9385eb3d
A
183 struct _hqh *head;
184 BKT *bp;
e9ce8d39
A
185 off_t off;
186 int nr;
187
9385eb3d
A
188 /* Check for attempt to retrieve a non-existent page. */
189 if (pgno >= mp->npages) {
190 errno = EINVAL;
191 return (NULL);
192 }
193
e9ce8d39 194#ifdef STATISTICS
9385eb3d 195 ++mp->pageget;
e9ce8d39 196#endif
9385eb3d
A
197
198 /* Check for a page that is cached. */
199 if ((bp = mpool_look(mp, pgno)) != NULL) {
e9ce8d39 200#ifdef DEBUG
9385eb3d
A
201 if (bp->flags & MPOOL_PINNED) {
202 (void)fprintf(stderr,
203 "mpool_get: page %d already pinned\n", bp->pgno);
204 abort();
205 }
e9ce8d39 206#endif
9385eb3d
A
207 /*
208 * Move the page to the head of the hash chain and the tail
209 * of the lru chain.
210 */
211 head = &mp->hqh[HASHKEY(bp->pgno)];
212 TAILQ_REMOVE(head, bp, hq);
213 TAILQ_INSERT_HEAD(head, bp, hq);
214 TAILQ_REMOVE(&mp->lqh, bp, q);
215 TAILQ_INSERT_TAIL(&mp->lqh, bp, q);
216
217 /* Return a pinned page. */
218 bp->flags |= MPOOL_PINNED;
219 return (bp->page);
e9ce8d39
A
220 }
221
222 /* Get a page from the cache. */
9385eb3d 223 if ((bp = mpool_bkt(mp)) == NULL)
e9ce8d39 224 return (NULL);
e9ce8d39 225
9385eb3d 226 /* Read in the contents. */
e9ce8d39
A
227#ifdef STATISTICS
228 ++mp->pageread;
229#endif
e9ce8d39
A
230 off = mp->pagesize * pgno;
231 if (lseek(mp->fd, off, SEEK_SET) != off)
232 return (NULL);
9385eb3d 233 if ((nr = read(mp->fd, bp->page, mp->pagesize)) != mp->pagesize) {
e9ce8d39
A
234 if (nr >= 0)
235 errno = EFTYPE;
236 return (NULL);
237 }
e9ce8d39 238
9385eb3d
A
239 /* Set the page number, pin the page. */
240 bp->pgno = pgno;
241 bp->flags = MPOOL_PINNED;
242
243 /*
244 * Add the page to the head of the hash chain and the tail
245 * of the lru chain.
246 */
247 head = &mp->hqh[HASHKEY(bp->pgno)];
248 TAILQ_INSERT_HEAD(head, bp, hq);
249 TAILQ_INSERT_TAIL(&mp->lqh, bp, q);
250
251 /* Run through the user's filter. */
252 if (mp->pgin != NULL)
253 (mp->pgin)(mp->pgcookie, bp->pgno, bp->page);
254
255 return (bp->page);
e9ce8d39
A
256}
257
258/*
9385eb3d
A
259 * mpool_put
260 * Return a page.
e9ce8d39
A
261 */
262int
263mpool_put(mp, page, flags)
264 MPOOL *mp;
265 void *page;
266 u_int flags;
267{
9385eb3d 268 BKT *bp;
e9ce8d39
A
269
270#ifdef STATISTICS
271 ++mp->pageput;
272#endif
9385eb3d 273 bp = (BKT *)((char *)page - sizeof(BKT));
e9ce8d39 274#ifdef DEBUG
9385eb3d
A
275 if (!(bp->flags & MPOOL_PINNED)) {
276 (void)fprintf(stderr,
277 "mpool_put: page %d not pinned\n", bp->pgno);
278 abort();
e9ce8d39
A
279 }
280#endif
9385eb3d
A
281 bp->flags &= ~MPOOL_PINNED;
282 bp->flags |= flags & MPOOL_DIRTY;
e9ce8d39
A
283 return (RET_SUCCESS);
284}
285
286/*
9385eb3d
A
287 * mpool_close
288 * Close the buffer pool.
e9ce8d39
A
289 */
290int
291mpool_close(mp)
292 MPOOL *mp;
293{
9385eb3d 294 BKT *bp;
e9ce8d39
A
295
296 /* Free up any space allocated to the lru pages. */
9385eb3d
A
297 while (!TAILQ_EMPTY(&mp->lqh)) {
298 bp = TAILQ_FIRST(&mp->lqh);
299 TAILQ_REMOVE(&mp->lqh, bp, q);
300 free(bp);
e9ce8d39 301 }
9385eb3d
A
302
303 /* Free the MPOOL cookie. */
e9ce8d39
A
304 free(mp);
305 return (RET_SUCCESS);
306}
307
308/*
9385eb3d
A
309 * mpool_sync
310 * Sync the pool to disk.
e9ce8d39
A
311 */
312int
313mpool_sync(mp)
314 MPOOL *mp;
315{
9385eb3d 316 BKT *bp;
e9ce8d39 317
9385eb3d
A
318 /* Walk the lru chain, flushing any dirty pages to disk. */
319 TAILQ_FOREACH(bp, &mp->lqh, q)
320 if (bp->flags & MPOOL_DIRTY &&
321 mpool_write(mp, bp) == RET_ERROR)
e9ce8d39 322 return (RET_ERROR);
9385eb3d
A
323
324 /* Sync the file descriptor. */
e9ce8d39
A
325 return (fsync(mp->fd) ? RET_ERROR : RET_SUCCESS);
326}
327
328/*
9385eb3d
A
329 * mpool_bkt
330 * Get a page from the cache (or create one).
e9ce8d39
A
331 */
332static BKT *
333mpool_bkt(mp)
334 MPOOL *mp;
335{
9385eb3d
A
336 struct _hqh *head;
337 BKT *bp;
e9ce8d39 338
9385eb3d 339 /* If under the max cached, always create a new page. */
e9ce8d39
A
340 if (mp->curcache < mp->maxcache)
341 goto new;
342
343 /*
9385eb3d
A
344 * If the cache is max'd out, walk the lru list for a buffer we
345 * can flush. If we find one, write it (if necessary) and take it
346 * off any lists. If we don't find anything we grow the cache anyway.
e9ce8d39
A
347 * The cache never shrinks.
348 */
9385eb3d
A
349 TAILQ_FOREACH(bp, &mp->lqh, q)
350 if (!(bp->flags & MPOOL_PINNED)) {
351 /* Flush if dirty. */
352 if (bp->flags & MPOOL_DIRTY &&
353 mpool_write(mp, bp) == RET_ERROR)
e9ce8d39 354 return (NULL);
e9ce8d39
A
355#ifdef STATISTICS
356 ++mp->pageflush;
357#endif
9385eb3d
A
358 /* Remove from the hash and lru queues. */
359 head = &mp->hqh[HASHKEY(bp->pgno)];
360 TAILQ_REMOVE(head, bp, hq);
361 TAILQ_REMOVE(&mp->lqh, bp, q);
e9ce8d39 362#ifdef DEBUG
9385eb3d
A
363 { void *spage;
364 spage = bp->page;
365 memset(bp, 0xff, sizeof(BKT) + mp->pagesize);
366 bp->page = spage;
e9ce8d39
A
367 }
368#endif
9385eb3d 369 return (bp);
e9ce8d39
A
370 }
371
9385eb3d 372new: if ((bp = (BKT *)malloc(sizeof(BKT) + mp->pagesize)) == NULL)
e9ce8d39
A
373 return (NULL);
374#ifdef STATISTICS
375 ++mp->pagealloc;
376#endif
9385eb3d
A
377#if defined(DEBUG) || defined(PURIFY)
378 memset(bp, 0xff, sizeof(BKT) + mp->pagesize);
e9ce8d39 379#endif
9385eb3d 380 bp->page = (char *)bp + sizeof(BKT);
e9ce8d39 381 ++mp->curcache;
9385eb3d 382 return (bp);
e9ce8d39
A
383}
384
385/*
9385eb3d
A
386 * mpool_write
387 * Write a page to disk.
e9ce8d39
A
388 */
389static int
9385eb3d 390mpool_write(mp, bp)
e9ce8d39 391 MPOOL *mp;
9385eb3d 392 BKT *bp;
e9ce8d39
A
393{
394 off_t off;
395
e9ce8d39
A
396#ifdef STATISTICS
397 ++mp->pagewrite;
398#endif
9385eb3d
A
399
400 /* Run through the user's filter. */
401 if (mp->pgout)
402 (mp->pgout)(mp->pgcookie, bp->pgno, bp->page);
403
404 off = mp->pagesize * bp->pgno;
e9ce8d39
A
405 if (lseek(mp->fd, off, SEEK_SET) != off)
406 return (RET_ERROR);
9385eb3d 407 if (write(mp->fd, bp->page, mp->pagesize) != mp->pagesize)
e9ce8d39 408 return (RET_ERROR);
9385eb3d
A
409
410 bp->flags &= ~MPOOL_DIRTY;
e9ce8d39
A
411 return (RET_SUCCESS);
412}
413
414/*
9385eb3d
A
415 * mpool_look
416 * Lookup a page in the cache.
e9ce8d39
A
417 */
418static BKT *
419mpool_look(mp, pgno)
420 MPOOL *mp;
421 pgno_t pgno;
422{
9385eb3d
A
423 struct _hqh *head;
424 BKT *bp;
e9ce8d39 425
9385eb3d
A
426 head = &mp->hqh[HASHKEY(pgno)];
427 TAILQ_FOREACH(bp, head, hq)
428 if (bp->pgno == pgno) {
e9ce8d39
A
429#ifdef STATISTICS
430 ++mp->cachehit;
431#endif
9385eb3d 432 return (bp);
e9ce8d39
A
433 }
434#ifdef STATISTICS
435 ++mp->cachemiss;
436#endif
437 return (NULL);
438}
439
440#ifdef STATISTICS
441/*
9385eb3d
A
442 * mpool_stat
443 * Print out cache statistics.
e9ce8d39
A
444 */
445void
446mpool_stat(mp)
447 MPOOL *mp;
448{
9385eb3d 449 BKT *bp;
e9ce8d39
A
450 int cnt;
451 char *sep;
452
453 (void)fprintf(stderr, "%lu pages in the file\n", mp->npages);
454 (void)fprintf(stderr,
455 "page size %lu, cacheing %lu pages of %lu page max cache\n",
456 mp->pagesize, mp->curcache, mp->maxcache);
457 (void)fprintf(stderr, "%lu page puts, %lu page gets, %lu page new\n",
458 mp->pageput, mp->pageget, mp->pagenew);
459 (void)fprintf(stderr, "%lu page allocs, %lu page flushes\n",
460 mp->pagealloc, mp->pageflush);
461 if (mp->cachehit + mp->cachemiss)
462 (void)fprintf(stderr,
463 "%.0f%% cache hit rate (%lu hits, %lu misses)\n",
464 ((double)mp->cachehit / (mp->cachehit + mp->cachemiss))
465 * 100, mp->cachehit, mp->cachemiss);
466 (void)fprintf(stderr, "%lu page reads, %lu page writes\n",
467 mp->pageread, mp->pagewrite);
468
469 sep = "";
470 cnt = 0;
9385eb3d
A
471 TAILQ_FOREACH(bp, &mp->lqh, q) {
472 (void)fprintf(stderr, "%s%d", sep, bp->pgno);
473 if (bp->flags & MPOOL_DIRTY)
e9ce8d39 474 (void)fprintf(stderr, "d");
9385eb3d 475 if (bp->flags & MPOOL_PINNED)
e9ce8d39
A
476 (void)fprintf(stderr, "P");
477 if (++cnt == 10) {
478 sep = "\n";
479 cnt = 0;
480 } else
481 sep = ", ";
482
483 }
484 (void)fprintf(stderr, "\n");
485}
486#endif