update from main archive 961114
[glibc.git] / db / mpool / mpool.c
blob9956aca87becd1871bd02eec5874bc3ea9ff922a
1 /*-
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
7 * are met:
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
31 * SUCH DAMAGE.
34 #if defined(LIBC_SCCS) && !defined(lint)
35 static char sccsid[] = "@(#)mpool.c 8.5 (Berkeley) 7/26/94";
36 #endif /* LIBC_SCCS and not lint */
38 #include <sys/param.h>
39 #include <sys/queue.h>
40 #include <sys/stat.h>
42 #include <errno.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
48 #include <db.h>
50 #define __MPOOLINTERFACE_PRIVATE
51 #include <mpool.h>
53 static BKT *mpool_bkt __P((MPOOL *));
54 static BKT *mpool_look __P((MPOOL *, pgno_t));
55 static int mpool_write __P((MPOOL *, BKT *));
58 * mpool_open --
59 * Initialize a memory pool.
61 MPOOL *
62 mpool_open(key, fd, pagesize, maxcache)
63 void *key;
64 int fd;
65 pgno_t pagesize, maxcache;
67 struct stat sb;
68 MPOOL *mp;
69 int entry;
72 * Get information about the file.
74 * XXX
75 * We don't currently handle pipes, although we should.
77 if (fstat(fd, &sb))
78 return (NULL);
79 if (!S_ISREG(sb.st_mode)) {
80 errno = ESPIPE;
81 return (NULL);
84 /* Allocate and initialize the MPOOL cookie. */
85 if ((mp = (MPOOL *)calloc(1, sizeof(MPOOL))) == NULL)
86 return (NULL);
87 CIRCLEQ_INIT(&mp->lqh);
88 for (entry = 0; entry < HASHSIZE; ++entry)
89 CIRCLEQ_INIT(&mp->hqh[entry]);
90 mp->maxcache = maxcache;
91 mp->npages = sb.st_size / pagesize;
92 mp->pagesize = pagesize;
93 mp->fd = fd;
94 return (mp);
98 * mpool_filter --
99 * Initialize input/output filters.
101 void
102 mpool_filter(mp, pgin, pgout, pgcookie)
103 MPOOL *mp;
104 void (*pgin) __P((void *, pgno_t, void *));
105 void (*pgout) __P((void *, pgno_t, void *));
106 void *pgcookie;
108 mp->pgin = pgin;
109 mp->pgout = pgout;
110 mp->pgcookie = pgcookie;
114 * mpool_new --
115 * Get a new page of memory.
117 void *
118 mpool_new(mp, pgnoaddr)
119 MPOOL *mp;
120 pgno_t *pgnoaddr;
122 struct _hqh *head;
123 BKT *bp;
125 if (mp->npages == MAX_PAGE_NUMBER) {
126 (void)fprintf(stderr, "mpool_new: page allocation overflow.\n");
127 abort();
129 #ifdef STATISTICS
130 ++mp->pagenew;
131 #endif
133 * Get a BKT from the cache. Assign a new page number, attach
134 * it to the head of the hash chain, the tail of the lru chain,
135 * and return.
137 if ((bp = mpool_bkt(mp)) == NULL)
138 return (NULL);
139 *pgnoaddr = bp->pgno = mp->npages++;
140 bp->flags = MPOOL_PINNED;
142 head = &mp->hqh[HASHKEY(bp->pgno)];
143 CIRCLEQ_INSERT_HEAD(head, bp, hq);
144 CIRCLEQ_INSERT_TAIL(&mp->lqh, bp, q);
145 return (bp->page);
149 * mpool_get
150 * Get a page.
152 void *
153 mpool_get(mp, pgno, flags)
154 MPOOL *mp;
155 pgno_t pgno;
156 u_int flags; /* XXX not used? */
158 struct _hqh *head;
159 BKT *bp;
160 off_t off;
161 int nr;
163 /* Check for attempt to retrieve a non-existent page. */
164 if (pgno >= mp->npages) {
165 errno = EINVAL;
166 return (NULL);
169 #ifdef STATISTICS
170 ++mp->pageget;
171 #endif
173 /* Check for a page that is cached. */
174 if ((bp = mpool_look(mp, pgno)) != NULL) {
175 #ifdef DEBUG
176 if (bp->flags & MPOOL_PINNED) {
177 (void)fprintf(stderr,
178 "mpool_get: page %d already pinned\n", bp->pgno);
179 abort();
181 #endif
183 * Move the page to the head of the hash chain and the tail
184 * of the lru chain.
186 head = &mp->hqh[HASHKEY(bp->pgno)];
187 CIRCLEQ_REMOVE(head, bp, hq);
188 CIRCLEQ_INSERT_HEAD(head, bp, hq);
189 CIRCLEQ_REMOVE(&mp->lqh, bp, q);
190 CIRCLEQ_INSERT_TAIL(&mp->lqh, bp, q);
192 /* Return a pinned page. */
193 bp->flags |= MPOOL_PINNED;
194 return (bp->page);
197 /* Get a page from the cache. */
198 if ((bp = mpool_bkt(mp)) == NULL)
199 return (NULL);
201 /* Read in the contents. */
202 #ifdef STATISTICS
203 ++mp->pageread;
204 #endif
205 off = mp->pagesize * pgno;
206 if (lseek(mp->fd, off, SEEK_SET) != off)
207 return (NULL);
208 if ((u_long) (nr = read(mp->fd, bp->page, mp->pagesize))
209 != mp->pagesize) {
210 if (nr >= 0)
211 errno = EFTYPE;
212 return (NULL);
215 /* Set the page number, pin the page. */
216 bp->pgno = pgno;
217 bp->flags = MPOOL_PINNED;
220 * Add the page to the head of the hash chain and the tail
221 * of the lru chain.
223 head = &mp->hqh[HASHKEY(bp->pgno)];
224 CIRCLEQ_INSERT_HEAD(head, bp, hq);
225 CIRCLEQ_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);
231 return (bp->page);
235 * mpool_put
236 * Return a page.
239 mpool_put(mp, page, flags)
240 MPOOL *mp;
241 void *page;
242 u_int flags;
244 BKT *bp;
246 #ifdef STATISTICS
247 ++mp->pageput;
248 #endif
249 bp = (BKT *)((char *)page - sizeof(BKT));
250 #ifdef DEBUG
251 if (!(bp->flags & MPOOL_PINNED)) {
252 (void)fprintf(stderr,
253 "mpool_put: page %d not pinned\n", bp->pgno);
254 abort();
256 #endif
257 bp->flags &= ~MPOOL_PINNED;
258 bp->flags |= flags & MPOOL_DIRTY;
259 return (RET_SUCCESS);
263 * mpool_close
264 * Close the buffer pool.
267 mpool_close(mp)
268 MPOOL *mp;
270 BKT *bp;
272 /* Free up any space allocated to the lru pages. */
273 while ((bp = mp->lqh.cqh_first) != (void *)&mp->lqh) {
274 CIRCLEQ_REMOVE(&mp->lqh, mp->lqh.cqh_first, q);
275 free(bp);
278 /* Free the MPOOL cookie. */
279 free(mp);
280 return (RET_SUCCESS);
284 * mpool_sync
285 * Sync the pool to disk.
288 mpool_sync(mp)
289 MPOOL *mp;
291 BKT *bp;
293 /* Walk the lru chain, flushing any dirty pages to disk. */
294 for (bp = mp->lqh.cqh_first;
295 bp != (void *)&mp->lqh; bp = bp->q.cqe_next)
296 if (bp->flags & MPOOL_DIRTY &&
297 mpool_write(mp, bp) == RET_ERROR)
298 return (RET_ERROR);
300 /* Sync the file descriptor. */
301 return (fsync(mp->fd) ? RET_ERROR : RET_SUCCESS);
305 * mpool_bkt
306 * Get a page from the cache (or create one).
308 static BKT *
309 mpool_bkt(mp)
310 MPOOL *mp;
312 struct _hqh *head;
313 BKT *bp;
315 /* If under the max cached, always create a new page. */
316 if (mp->curcache < mp->maxcache)
317 goto new;
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 for (bp = mp->lqh.cqh_first;
326 bp != (void *)&mp->lqh; bp = bp->q.cqe_next)
327 if (!(bp->flags & MPOOL_PINNED)) {
328 /* Flush if dirty. */
329 if (bp->flags & MPOOL_DIRTY &&
330 mpool_write(mp, bp) == RET_ERROR)
331 return (NULL);
332 #ifdef STATISTICS
333 ++mp->pageflush;
334 #endif
335 /* Remove from the hash and lru queues. */
336 head = &mp->hqh[HASHKEY(bp->pgno)];
337 CIRCLEQ_REMOVE(head, bp, hq);
338 CIRCLEQ_REMOVE(&mp->lqh, bp, q);
339 #ifdef DEBUG
340 { void *spage;
341 spage = bp->page;
342 memset(bp, 0xff, sizeof(BKT) + mp->pagesize);
343 bp->page = spage;
345 #endif
346 return (bp);
349 new: if ((bp = (BKT *)malloc(sizeof(BKT) + mp->pagesize)) == NULL)
350 return (NULL);
351 #ifdef STATISTICS
352 ++mp->pagealloc;
353 #endif
354 #if defined(DEBUG) || defined(PURIFY)
355 memset(bp, 0xff, sizeof(BKT) + mp->pagesize);
356 #endif
357 bp->page = (char *)bp + sizeof(BKT);
358 ++mp->curcache;
359 return (bp);
363 * mpool_write
364 * Write a page to disk.
366 static int
367 mpool_write(mp, bp)
368 MPOOL *mp;
369 BKT *bp;
371 off_t off;
373 #ifdef STATISTICS
374 ++mp->pagewrite;
375 #endif
377 /* Run through the user's filter. */
378 if (mp->pgout)
379 (mp->pgout)(mp->pgcookie, bp->pgno, bp->page);
381 off = mp->pagesize * bp->pgno;
382 if (lseek(mp->fd, off, SEEK_SET) != off)
383 return (RET_ERROR);
384 if ((u_long) write(mp->fd, bp->page, mp->pagesize) != mp->pagesize)
385 return (RET_ERROR);
387 bp->flags &= ~MPOOL_DIRTY;
388 return (RET_SUCCESS);
392 * mpool_look
393 * Lookup a page in the cache.
395 static BKT *
396 mpool_look(mp, pgno)
397 MPOOL *mp;
398 pgno_t pgno;
400 struct _hqh *head;
401 BKT *bp;
403 head = &mp->hqh[HASHKEY(pgno)];
404 for (bp = head->cqh_first; bp != (void *)head; bp = bp->hq.cqe_next)
405 if (bp->pgno == pgno) {
406 #ifdef STATISTICS
407 ++mp->cachehit;
408 #endif
409 return (bp);
411 #ifdef STATISTICS
412 ++mp->cachemiss;
413 #endif
414 return (NULL);
417 #ifdef STATISTICS
419 * mpool_stat
420 * Print out cache statistics.
422 void
423 mpool_stat(mp)
424 MPOOL *mp;
426 BKT *bp;
427 int cnt;
428 char *sep;
430 (void)fprintf(stderr, "%lu pages in the file\n", mp->npages);
431 (void)fprintf(stderr,
432 "page size %lu, cacheing %lu pages of %lu page max cache\n",
433 mp->pagesize, mp->curcache, mp->maxcache);
434 (void)fprintf(stderr, "%lu page puts, %lu page gets, %lu page new\n",
435 mp->pageput, mp->pageget, mp->pagenew);
436 (void)fprintf(stderr, "%lu page allocs, %lu page flushes\n",
437 mp->pagealloc, mp->pageflush);
438 if (mp->cachehit + mp->cachemiss)
439 (void)fprintf(stderr,
440 "%.0f%% cache hit rate (%lu hits, %lu misses)\n",
441 ((double)mp->cachehit / (mp->cachehit + mp->cachemiss))
442 * 100, mp->cachehit, mp->cachemiss);
443 (void)fprintf(stderr, "%lu page reads, %lu page writes\n",
444 mp->pageread, mp->pagewrite);
446 sep = "";
447 cnt = 0;
448 for (bp = mp->lqh.cqh_first;
449 bp != (void *)&mp->lqh; bp = bp->q.cqe_next) {
450 (void)fprintf(stderr, "%s%d", sep, bp->pgno);
451 if (bp->flags & MPOOL_DIRTY)
452 (void)fprintf(stderr, "d");
453 if (bp->flags & MPOOL_PINNED)
454 (void)fprintf(stderr, "P");
455 if (++cnt == 10) {
456 sep = "\n";
457 cnt = 0;
458 } else
459 sep = ", ";
462 (void)fprintf(stderr, "\n");
464 #endif