perl: fix -Wformat-security warning in ENDMESSAGE()
[nvi.git] / db.1.85 / mpool / mpool.c
bloba61041e091601cb7d2c53dbaf2c31ccfe2c3de2b
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 ((nr = read(mp->fd, bp->page, mp->pagesize)) != mp->pagesize) {
209 if (nr >= 0)
210 errno = EFTYPE;
211 return (NULL);
214 /* Set the page number, pin the page. */
215 bp->pgno = pgno;
216 bp->flags = MPOOL_PINNED;
219 * Add the page to the head of the hash chain and the tail
220 * of the lru chain.
222 head = &mp->hqh[HASHKEY(bp->pgno)];
223 CIRCLEQ_INSERT_HEAD(head, bp, hq);
224 CIRCLEQ_INSERT_TAIL(&mp->lqh, bp, q);
226 /* Run through the user's filter. */
227 if (mp->pgin != NULL)
228 (mp->pgin)(mp->pgcookie, bp->pgno, bp->page);
230 return (bp->page);
234 * mpool_put
235 * Return a page.
238 mpool_put(mp, page, flags)
239 MPOOL *mp;
240 void *page;
241 u_int flags;
243 BKT *bp;
245 #ifdef STATISTICS
246 ++mp->pageput;
247 #endif
248 bp = (BKT *)((char *)page - sizeof(BKT));
249 #ifdef DEBUG
250 if (!(bp->flags & MPOOL_PINNED)) {
251 (void)fprintf(stderr,
252 "mpool_put: page %d not pinned\n", bp->pgno);
253 abort();
255 #endif
256 bp->flags &= ~MPOOL_PINNED;
257 bp->flags |= flags & MPOOL_DIRTY;
258 return (RET_SUCCESS);
262 * mpool_close
263 * Close the buffer pool.
266 mpool_close(mp)
267 MPOOL *mp;
269 BKT *bp;
271 /* Free up any space allocated to the lru pages. */
272 while ((bp = mp->lqh.cqh_first) != (void *)&mp->lqh) {
273 CIRCLEQ_REMOVE(&mp->lqh, mp->lqh.cqh_first, q);
274 free(bp);
277 /* Free the MPOOL cookie. */
278 free(mp);
279 return (RET_SUCCESS);
283 * mpool_sync
284 * Sync the pool to disk.
287 mpool_sync(mp)
288 MPOOL *mp;
290 BKT *bp;
292 /* Walk the lru chain, flushing any dirty pages to disk. */
293 for (bp = mp->lqh.cqh_first;
294 bp != (void *)&mp->lqh; bp = bp->q.cqe_next)
295 if (bp->flags & MPOOL_DIRTY &&
296 mpool_write(mp, bp) == RET_ERROR)
297 return (RET_ERROR);
299 /* Sync the file descriptor. */
300 return (fsync(mp->fd) ? RET_ERROR : RET_SUCCESS);
304 * mpool_bkt
305 * Get a page from the cache (or create one).
307 static BKT *
308 mpool_bkt(mp)
309 MPOOL *mp;
311 struct _hqh *head;
312 BKT *bp;
314 /* If under the max cached, always create a new page. */
315 if (mp->curcache < mp->maxcache)
316 goto new;
319 * If the cache is max'd out, walk the lru list for a buffer we
320 * can flush. If we find one, write it (if necessary) and take it
321 * off any lists. If we don't find anything we grow the cache anyway.
322 * The cache never shrinks.
324 for (bp = mp->lqh.cqh_first;
325 bp != (void *)&mp->lqh; bp = bp->q.cqe_next)
326 if (!(bp->flags & MPOOL_PINNED)) {
327 /* Flush if dirty. */
328 if (bp->flags & MPOOL_DIRTY &&
329 mpool_write(mp, bp) == RET_ERROR)
330 return (NULL);
331 #ifdef STATISTICS
332 ++mp->pageflush;
333 #endif
334 /* Remove from the hash and lru queues. */
335 head = &mp->hqh[HASHKEY(bp->pgno)];
336 CIRCLEQ_REMOVE(head, bp, hq);
337 CIRCLEQ_REMOVE(&mp->lqh, bp, q);
338 #ifdef DEBUG
339 { void *spage;
340 spage = bp->page;
341 memset(bp, 0xff, sizeof(BKT) + mp->pagesize);
342 bp->page = spage;
344 #endif
345 return (bp);
348 new: if ((bp = (BKT *)malloc(sizeof(BKT) + mp->pagesize)) == NULL)
349 return (NULL);
350 #ifdef STATISTICS
351 ++mp->pagealloc;
352 #endif
353 #if defined(DEBUG) || defined(PURIFY)
354 memset(bp, 0xff, sizeof(BKT) + mp->pagesize);
355 #endif
356 bp->page = (char *)bp + sizeof(BKT);
357 ++mp->curcache;
358 return (bp);
362 * mpool_write
363 * Write a page to disk.
365 static int
366 mpool_write(mp, bp)
367 MPOOL *mp;
368 BKT *bp;
370 off_t off;
372 #ifdef STATISTICS
373 ++mp->pagewrite;
374 #endif
376 /* Run through the user's filter. */
377 if (mp->pgout)
378 (mp->pgout)(mp->pgcookie, bp->pgno, bp->page);
380 off = mp->pagesize * bp->pgno;
381 if (lseek(mp->fd, off, SEEK_SET) != off)
382 return (RET_ERROR);
383 if (write(mp->fd, bp->page, mp->pagesize) != mp->pagesize)
384 return (RET_ERROR);
386 bp->flags &= ~MPOOL_DIRTY;
387 return (RET_SUCCESS);
391 * mpool_look
392 * Lookup a page in the cache.
394 static BKT *
395 mpool_look(mp, pgno)
396 MPOOL *mp;
397 pgno_t pgno;
399 struct _hqh *head;
400 BKT *bp;
402 head = &mp->hqh[HASHKEY(pgno)];
403 for (bp = head->cqh_first; bp != (void *)head; bp = bp->hq.cqe_next)
404 if (bp->pgno == pgno) {
405 #ifdef STATISTICS
406 ++mp->cachehit;
407 #endif
408 return (bp);
410 #ifdef STATISTICS
411 ++mp->cachemiss;
412 #endif
413 return (NULL);
416 #ifdef STATISTICS
418 * mpool_stat
419 * Print out cache statistics.
421 void
422 mpool_stat(mp)
423 MPOOL *mp;
425 BKT *bp;
426 int cnt;
427 char *sep;
429 (void)fprintf(stderr, "%lu pages in the file\n", mp->npages);
430 (void)fprintf(stderr,
431 "page size %lu, cacheing %lu pages of %lu page max cache\n",
432 mp->pagesize, mp->curcache, mp->maxcache);
433 (void)fprintf(stderr, "%lu page puts, %lu page gets, %lu page new\n",
434 mp->pageput, mp->pageget, mp->pagenew);
435 (void)fprintf(stderr, "%lu page allocs, %lu page flushes\n",
436 mp->pagealloc, mp->pageflush);
437 if (mp->cachehit + mp->cachemiss)
438 (void)fprintf(stderr,
439 "%.0f%% cache hit rate (%lu hits, %lu misses)\n",
440 ((double)mp->cachehit / (mp->cachehit + mp->cachemiss))
441 * 100, mp->cachehit, mp->cachemiss);
442 (void)fprintf(stderr, "%lu page reads, %lu page writes\n",
443 mp->pageread, mp->pagewrite);
445 sep = "";
446 cnt = 0;
447 for (bp = mp->lqh.cqh_first;
448 bp != (void *)&mp->lqh; bp = bp->q.cqe_next) {
449 (void)fprintf(stderr, "%s%d", sep, bp->pgno);
450 if (bp->flags & MPOOL_DIRTY)
451 (void)fprintf(stderr, "d");
452 if (bp->flags & MPOOL_PINNED)
453 (void)fprintf(stderr, "P");
454 if (++cnt == 10) {
455 sep = "\n";
456 cnt = 0;
457 } else
458 sep = ", ";
461 (void)fprintf(stderr, "\n");
463 #endif