xref: /netbsd-src/lib/libc/db/mpool/mpool.c (revision aae80e6be7bf8e6ad43f8b63d296d2d3923c4ee5)
1*aae80e6bSchristos /*	$NetBSD: mpool.c,v 1.23 2016/09/24 21:31:25 christos Exp $	*/
22c84ad3aScgd 
39f0aa214Scgd /*-
417140cefScgd  * Copyright (c) 1990, 1993, 1994
59f0aa214Scgd  *	The Regents of the University of California.  All rights reserved.
69f0aa214Scgd  *
79f0aa214Scgd  * Redistribution and use in source and binary forms, with or without
89f0aa214Scgd  * modification, are permitted provided that the following conditions
99f0aa214Scgd  * are met:
109f0aa214Scgd  * 1. Redistributions of source code must retain the above copyright
119f0aa214Scgd  *    notice, this list of conditions and the following disclaimer.
129f0aa214Scgd  * 2. Redistributions in binary form must reproduce the above copyright
139f0aa214Scgd  *    notice, this list of conditions and the following disclaimer in the
149f0aa214Scgd  *    documentation and/or other materials provided with the distribution.
15eb7c1594Sagc  * 3. Neither the name of the University nor the names of its contributors
169f0aa214Scgd  *    may be used to endorse or promote products derived from this software
179f0aa214Scgd  *    without specific prior written permission.
189f0aa214Scgd  *
199f0aa214Scgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
209f0aa214Scgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
219f0aa214Scgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
229f0aa214Scgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
239f0aa214Scgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
249f0aa214Scgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
259f0aa214Scgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
269f0aa214Scgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
279f0aa214Scgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
289f0aa214Scgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
299f0aa214Scgd  * SUCH DAMAGE.
309f0aa214Scgd  */
319f0aa214Scgd 
32d3595ddfSjoerg #if HAVE_NBTOOL_CONFIG_H
33d3595ddfSjoerg #include "nbtool_config.h"
34d3595ddfSjoerg #endif
35d3595ddfSjoerg 
3600ae392dSchristos #include <sys/cdefs.h>
37*aae80e6bSchristos __RCSID("$NetBSD: mpool.c,v 1.23 2016/09/24 21:31:25 christos Exp $");
389f0aa214Scgd 
3943fa6fe3Sjtc #include "namespace.h"
4017140cefScgd #include <sys/queue.h>
419f0aa214Scgd #include <sys/stat.h>
429f0aa214Scgd 
439f0aa214Scgd #include <errno.h>
449f0aa214Scgd #include <stdio.h>
459f0aa214Scgd #include <stdlib.h>
469f0aa214Scgd #include <string.h>
479f0aa214Scgd #include <unistd.h>
489f0aa214Scgd 
499f0aa214Scgd #include <db.h>
5017140cefScgd 
519f0aa214Scgd #define	__MPOOLINTERFACE_PRIVATE
5217140cefScgd #include <mpool.h>
539f0aa214Scgd 
5443fa6fe3Sjtc #ifdef __weak_alias
5560549036Smycroft __weak_alias(mpool_close,_mpool_close)
5660549036Smycroft __weak_alias(mpool_filter,_mpool_filter)
5760549036Smycroft __weak_alias(mpool_get,_mpool_get)
5860549036Smycroft __weak_alias(mpool_new,_mpool_new)
5986147b1cSchristos __weak_alias(mpool_newf,_mpool_newf)
6060549036Smycroft __weak_alias(mpool_open,_mpool_open)
6160549036Smycroft __weak_alias(mpool_put,_mpool_put)
6260549036Smycroft __weak_alias(mpool_sync,_mpool_sync)
6343fa6fe3Sjtc #endif
6443fa6fe3Sjtc 
65cb9daf8fSchristos static BKT *mpool_bkt(MPOOL *);
66cb9daf8fSchristos static BKT *mpool_look(MPOOL *, pgno_t);
67cb9daf8fSchristos static int  mpool_write(MPOOL *, BKT *);
689f0aa214Scgd 
699f0aa214Scgd /*
7017140cefScgd  * mpool_open --
7117140cefScgd  *	Initialize a memory pool.
729f0aa214Scgd  */
7361238e71Schristos /*ARGSUSED*/
749f0aa214Scgd MPOOL *
mpool_open(void * key,int fd,pgno_t pagesize,pgno_t maxcache)75cb9daf8fSchristos mpool_open(void *key, int fd, pgno_t pagesize, pgno_t maxcache)
769f0aa214Scgd {
779f0aa214Scgd 	struct stat sb;
789f0aa214Scgd 	MPOOL *mp;
799f0aa214Scgd 	int entry;
809f0aa214Scgd 
8117140cefScgd 	/*
8217140cefScgd 	 * Get information about the file.
8317140cefScgd 	 *
8417140cefScgd 	 * XXX
8517140cefScgd 	 * We don't currently handle pipes, although we should.
8617140cefScgd 	 */
879f0aa214Scgd 	if (fstat(fd, &sb))
884008ee50Schristos 		return NULL;
899f0aa214Scgd 	if (!S_ISREG(sb.st_mode)) {
909f0aa214Scgd 		errno = ESPIPE;
914008ee50Schristos 		return NULL;
929f0aa214Scgd 	}
939f0aa214Scgd 
9417140cefScgd 	/* Allocate and initialize the MPOOL cookie. */
954008ee50Schristos 	if ((mp = calloc(1, sizeof(*mp))) == NULL)
969f0aa214Scgd 		return (NULL);
97c64e7224Schristos 	TAILQ_INIT(&mp->lqh);
989f0aa214Scgd 	for (entry = 0; entry < HASHSIZE; ++entry)
99c64e7224Schristos 		TAILQ_INIT(&mp->hqh[entry]);
1009f0aa214Scgd 	mp->maxcache = maxcache;
10161238e71Schristos 	mp->npages = (pgno_t)(sb.st_size / pagesize);
10217140cefScgd 	mp->pagesize = pagesize;
1039f0aa214Scgd 	mp->fd = fd;
1044008ee50Schristos 	return mp;
1059f0aa214Scgd }
1069f0aa214Scgd 
1079f0aa214Scgd /*
10817140cefScgd  * mpool_filter --
10917140cefScgd  *	Initialize input/output filters.
1109f0aa214Scgd  */
1119f0aa214Scgd void
mpool_filter(MPOOL * mp,void (* pgin)(void *,pgno_t,void *),void (* pgout)(void *,pgno_t,void *),void * pgcookie)112cb9daf8fSchristos mpool_filter(MPOOL *mp, void (*pgin)(void *, pgno_t, void *),
113cb9daf8fSchristos     void (*pgout)(void *, pgno_t, void *), void *pgcookie)
1149f0aa214Scgd {
1159f0aa214Scgd 	mp->pgin = pgin;
1169f0aa214Scgd 	mp->pgout = pgout;
1179f0aa214Scgd 	mp->pgcookie = pgcookie;
1189f0aa214Scgd }
1199f0aa214Scgd 
1209f0aa214Scgd /*
12117140cefScgd  * mpool_new --
12217140cefScgd  *	Get a new page of memory.
1239f0aa214Scgd  */
1249f0aa214Scgd void *
mpool_newf(MPOOL * mp,pgno_t * pgnoaddr,unsigned int flags)12586147b1cSchristos mpool_newf(MPOOL *mp, pgno_t *pgnoaddr, unsigned int flags)
1269f0aa214Scgd {
12717140cefScgd 	struct _hqh *head;
12817140cefScgd 	BKT *bp;
1299f0aa214Scgd 
13017140cefScgd 	if (mp->npages == MAX_PAGE_NUMBER) {
13117140cefScgd 		(void)fprintf(stderr, "mpool_new: page allocation overflow.\n");
13217140cefScgd 		abort();
13317140cefScgd 	}
1349f0aa214Scgd #ifdef STATISTICS
1359f0aa214Scgd 	++mp->pagenew;
1369f0aa214Scgd #endif
1379f0aa214Scgd 	/*
13817140cefScgd 	 * Get a BKT from the cache.  Assign a new page number, attach
13917140cefScgd 	 * it to the head of the hash chain, the tail of the lru chain,
14017140cefScgd 	 * and return.
1419f0aa214Scgd 	 */
14217140cefScgd 	if ((bp = mpool_bkt(mp)) == NULL)
1434008ee50Schristos 		return NULL;
14486147b1cSchristos 
14586147b1cSchristos 	if (flags == MPOOL_PAGE_REQUEST) {
14686147b1cSchristos 		mp->npages++;
14786147b1cSchristos 		bp->pgno = *pgnoaddr;
14886147b1cSchristos 	} else
14986147b1cSchristos 		bp->pgno = *pgnoaddr = mp->npages++;
15086147b1cSchristos 
15186147b1cSchristos 	bp->flags = MPOOL_PINNED | MPOOL_INUSE;
15217140cefScgd 
15317140cefScgd 	head = &mp->hqh[HASHKEY(bp->pgno)];
154c64e7224Schristos 	TAILQ_INSERT_HEAD(head, bp, hq);
155c64e7224Schristos 	TAILQ_INSERT_TAIL(&mp->lqh, bp, q);
1564008ee50Schristos 	return bp->page;
1579f0aa214Scgd }
1589f0aa214Scgd 
15986147b1cSchristos void *
mpool_new(MPOOL * mp,pgno_t * pgnoaddr)16086147b1cSchristos mpool_new(MPOOL *mp, pgno_t *pgnoaddr)
16186147b1cSchristos {
16286147b1cSchristos 	return mpool_newf(mp, pgnoaddr, 0);
16386147b1cSchristos }
16486147b1cSchristos 
16586147b1cSchristos int
mpool_delete(MPOOL * mp,void * page)16686147b1cSchristos mpool_delete(MPOOL *mp, void *page)
16786147b1cSchristos {
16886147b1cSchristos 	struct _hqh *head;
16986147b1cSchristos 	BKT *bp;
17086147b1cSchristos 
17186147b1cSchristos 	bp = (void *)((char *)page - sizeof(BKT));
17286147b1cSchristos 
17386147b1cSchristos #ifdef DEBUG
17486147b1cSchristos 	if (!(bp->flags & MPOOL_PINNED)) {
17586147b1cSchristos 	       (void)fprintf(stderr,
17686147b1cSchristos 		   "%s: page %d not pinned\n", __func__, bp->pgno);
17786147b1cSchristos 	       abort();
17886147b1cSchristos 	}
17986147b1cSchristos #endif
18086147b1cSchristos 
18186147b1cSchristos 	/* Remove from the hash and lru queues. */
18286147b1cSchristos 	head = &mp->hqh[HASHKEY(bp->pgno)];
18386147b1cSchristos 	TAILQ_REMOVE(head, bp, hq);
18486147b1cSchristos 	TAILQ_REMOVE(&mp->lqh, bp, q);
18586147b1cSchristos 
18686147b1cSchristos 	free(bp);
18786147b1cSchristos 	return RET_SUCCESS;
18886147b1cSchristos }
18986147b1cSchristos 
1909f0aa214Scgd /*
19117140cefScgd  * mpool_get
19217140cefScgd  *	Get a page.
1939f0aa214Scgd  */
19461238e71Schristos /*ARGSUSED*/
1959f0aa214Scgd void *
mpool_get(MPOOL * mp,pgno_t pgno,unsigned int flags)196*aae80e6bSchristos mpool_get(MPOOL *mp, pgno_t pgno, unsigned int flags)
1979f0aa214Scgd {
19817140cefScgd 	struct _hqh *head;
19917140cefScgd 	BKT *bp;
2009f0aa214Scgd 	off_t off;
201cb9daf8fSchristos 	ssize_t nr;
2029f0aa214Scgd 
20317140cefScgd 	/* Check for attempt to retrieve a non-existent page. */
2049f0aa214Scgd 	if (pgno >= mp->npages) {
2059f0aa214Scgd 		errno = EINVAL;
2064008ee50Schristos 		return NULL;
2079f0aa214Scgd 	}
2089f0aa214Scgd 
20917140cefScgd #ifdef STATISTICS
21017140cefScgd 	++mp->pageget;
21117140cefScgd #endif
2129f0aa214Scgd 
21317140cefScgd 	/* Check for a page that is cached. */
21417140cefScgd 	if ((bp = mpool_look(mp, pgno)) != NULL) {
21517140cefScgd #ifdef DEBUG
21686147b1cSchristos 		if (!(flags & MPOOL_IGNOREPIN) && bp->flags & MPOOL_PINNED) {
21717140cefScgd 			(void)fprintf(stderr,
21817140cefScgd 			    "mpool_get: page %d already pinned\n", bp->pgno);
21917140cefScgd 			abort();
22017140cefScgd 		}
22117140cefScgd #endif
22217140cefScgd 		/*
22317140cefScgd 		 * Move the page to the head of the hash chain and the tail
22417140cefScgd 		 * of the lru chain.
22517140cefScgd 		 */
22617140cefScgd 		head = &mp->hqh[HASHKEY(bp->pgno)];
227c64e7224Schristos 		TAILQ_REMOVE(head, bp, hq);
228c64e7224Schristos 		TAILQ_INSERT_HEAD(head, bp, hq);
229c64e7224Schristos 		TAILQ_REMOVE(&mp->lqh, bp, q);
230c64e7224Schristos 		TAILQ_INSERT_TAIL(&mp->lqh, bp, q);
23117140cefScgd 
23217140cefScgd 		/* Return a pinned page. */
23386147b1cSchristos 		if (!(flags & MPOOL_IGNOREPIN))
23417140cefScgd 			bp->flags |= MPOOL_PINNED;
2354008ee50Schristos 		return bp->page;
23617140cefScgd 	}
23717140cefScgd 
23817140cefScgd 	/* Get a page from the cache. */
23917140cefScgd 	if ((bp = mpool_bkt(mp)) == NULL)
2404008ee50Schristos 		return NULL;
24117140cefScgd 
24217140cefScgd 	/* Read in the contents. */
2439f0aa214Scgd #ifdef STATISTICS
2449f0aa214Scgd 	++mp->pageread;
2459f0aa214Scgd #endif
2469f0aa214Scgd 	off = mp->pagesize * pgno;
24786147b1cSchristos 	if (off / mp->pagesize != pgno) {
24886147b1cSchristos 		/* Run past the end of the file, or at least the part we
24986147b1cSchristos 		   can address without large-file support?  */
25086147b1cSchristos 		errno = E2BIG;
25186147b1cSchristos  		return NULL;
25286147b1cSchristos 	}
25386147b1cSchristos 
25461238e71Schristos 	if ((nr = pread(mp->fd, bp->page, (size_t)mp->pagesize, off)) != (int)mp->pagesize) {
25586147b1cSchristos 		if (nr > 0) {
2569f0aa214Scgd 			errno = EFTYPE;
2574008ee50Schristos 			return NULL;
25886147b1cSchristos 		} else if (nr == 0) {
25986147b1cSchristos 			/*
26086147b1cSchristos 			 * A zero-length reads, means you need to create a
26186147b1cSchristos 			 * new page.
26286147b1cSchristos 			 */
26386147b1cSchristos 			memset(bp->page, 0, mp->pagesize);
26486147b1cSchristos 		} else
26586147b1cSchristos 			return NULL;
2669f0aa214Scgd 	}
2679f0aa214Scgd 
26817140cefScgd 	/* Set the page number, pin the page. */
26917140cefScgd 	bp->pgno = pgno;
27086147b1cSchristos 	if (!(flags & MPOOL_IGNOREPIN))
27117140cefScgd 		bp->flags = MPOOL_PINNED;
27286147b1cSchristos bp->flags |= MPOOL_INUSE;
27317140cefScgd 
27417140cefScgd 	/*
27517140cefScgd 	 * Add the page to the head of the hash chain and the tail
27617140cefScgd 	 * of the lru chain.
27717140cefScgd 	 */
27817140cefScgd 	head = &mp->hqh[HASHKEY(bp->pgno)];
279c64e7224Schristos 	TAILQ_INSERT_HEAD(head, bp, hq);
280c64e7224Schristos 	TAILQ_INSERT_TAIL(&mp->lqh, bp, q);
28117140cefScgd 
28217140cefScgd 	/* Run through the user's filter. */
28317140cefScgd 	if (mp->pgin != NULL)
28417140cefScgd 		(mp->pgin)(mp->pgcookie, bp->pgno, bp->page);
28517140cefScgd 
2864008ee50Schristos 	return bp->page;
2879f0aa214Scgd }
2889f0aa214Scgd 
2899f0aa214Scgd /*
29017140cefScgd  * mpool_put
29117140cefScgd  *	Return a page.
2929f0aa214Scgd  */
29361238e71Schristos /*ARGSUSED*/
2949f0aa214Scgd int
mpool_put(MPOOL * mp,void * page,u_int flags)295cb9daf8fSchristos mpool_put(MPOOL *mp, void *page, u_int flags)
2969f0aa214Scgd {
29717140cefScgd 	BKT *bp;
2989f0aa214Scgd 
2999f0aa214Scgd #ifdef STATISTICS
3009f0aa214Scgd 	++mp->pageput;
3019f0aa214Scgd #endif
3024008ee50Schristos 	bp = (void *)((intptr_t)page - sizeof(BKT));
3039f0aa214Scgd #ifdef DEBUG
30417140cefScgd 	if (!(bp->flags & MPOOL_PINNED)) {
30517140cefScgd 		(void)fprintf(stderr,
30617140cefScgd 		    "mpool_put: page %d not pinned\n", bp->pgno);
30717140cefScgd 		abort();
3089f0aa214Scgd 	}
3099f0aa214Scgd #endif
31017140cefScgd 	bp->flags &= ~MPOOL_PINNED;
31186147b1cSchristos 	if (flags & MPOOL_DIRTY)
31217140cefScgd 		bp->flags |= flags & MPOOL_DIRTY;
3139f0aa214Scgd 	return (RET_SUCCESS);
3149f0aa214Scgd }
3159f0aa214Scgd 
3169f0aa214Scgd /*
31717140cefScgd  * mpool_close
31817140cefScgd  *	Close the buffer pool.
3199f0aa214Scgd  */
3209f0aa214Scgd int
mpool_close(MPOOL * mp)321cb9daf8fSchristos mpool_close(MPOOL *mp)
3229f0aa214Scgd {
32317140cefScgd 	BKT *bp;
3249f0aa214Scgd 
3259f0aa214Scgd 	/* Free up any space allocated to the lru pages. */
326c64e7224Schristos 	while (!TAILQ_EMPTY(&mp->lqh)) {
327c64e7224Schristos 		bp = TAILQ_FIRST(&mp->lqh);
328c64e7224Schristos 		TAILQ_REMOVE(&mp->lqh, bp, q);
32917140cefScgd 		free(bp);
3309f0aa214Scgd 	}
33117140cefScgd 
33217140cefScgd 	/* Free the MPOOL cookie. */
3339f0aa214Scgd 	free(mp);
3344008ee50Schristos 	return RET_SUCCESS;
3359f0aa214Scgd }
3369f0aa214Scgd 
3379f0aa214Scgd /*
33817140cefScgd  * mpool_sync
33917140cefScgd  *	Sync the pool to disk.
3409f0aa214Scgd  */
3419f0aa214Scgd int
mpool_sync(MPOOL * mp)342cb9daf8fSchristos mpool_sync(MPOOL *mp)
3439f0aa214Scgd {
34417140cefScgd 	BKT *bp;
3459f0aa214Scgd 
34617140cefScgd 	/* Walk the lru chain, flushing any dirty pages to disk. */
347c64e7224Schristos 	TAILQ_FOREACH(bp, &mp->lqh, q)
34817140cefScgd 		if (bp->flags & MPOOL_DIRTY &&
34917140cefScgd 		    mpool_write(mp, bp) == RET_ERROR)
3504008ee50Schristos 			return RET_ERROR;
35117140cefScgd 
35217140cefScgd 	/* Sync the file descriptor. */
3534008ee50Schristos 	return fsync(mp->fd) ? RET_ERROR : RET_SUCCESS;
3549f0aa214Scgd }
3559f0aa214Scgd 
3569f0aa214Scgd /*
35717140cefScgd  * mpool_bkt
35817140cefScgd  *	Get a page from the cache (or create one).
3599f0aa214Scgd  */
3609f0aa214Scgd static BKT *
mpool_bkt(MPOOL * mp)361cb9daf8fSchristos mpool_bkt(MPOOL *mp)
3629f0aa214Scgd {
36317140cefScgd 	struct _hqh *head;
36417140cefScgd 	BKT *bp;
3659f0aa214Scgd 
36617140cefScgd 	/* If under the max cached, always create a new page. */
3679f0aa214Scgd 	if (mp->curcache < mp->maxcache)
3689f0aa214Scgd 		goto new;
3699f0aa214Scgd 
3709f0aa214Scgd 	/*
37117140cefScgd 	 * If the cache is max'd out, walk the lru list for a buffer we
37217140cefScgd 	 * can flush.  If we find one, write it (if necessary) and take it
37317140cefScgd 	 * off any lists.  If we don't find anything we grow the cache anyway.
3749f0aa214Scgd 	 * The cache never shrinks.
3759f0aa214Scgd 	 */
376c64e7224Schristos 	TAILQ_FOREACH(bp, &mp->lqh, q)
37717140cefScgd 		if (!(bp->flags & MPOOL_PINNED)) {
37817140cefScgd 			/* Flush if dirty. */
37917140cefScgd 			if (bp->flags & MPOOL_DIRTY &&
38017140cefScgd 			    mpool_write(mp, bp) == RET_ERROR)
3814008ee50Schristos 				return NULL;
3829f0aa214Scgd #ifdef STATISTICS
3839f0aa214Scgd 			++mp->pageflush;
3849f0aa214Scgd #endif
38517140cefScgd 			/* Remove from the hash and lru queues. */
38617140cefScgd 			head = &mp->hqh[HASHKEY(bp->pgno)];
387c64e7224Schristos 			TAILQ_REMOVE(head, bp, hq);
388c64e7224Schristos 			TAILQ_REMOVE(&mp->lqh, bp, q);
3899f0aa214Scgd #ifdef DEBUG
390096020daSchristos 			{
391096020daSchristos 				void *spage = bp->page;
392096020daSchristos 				(void)memset(bp, 0xff,
393096020daSchristos 				    (size_t)(sizeof(BKT) + mp->pagesize));
39417140cefScgd 				bp->page = spage;
3959f0aa214Scgd 			}
3969f0aa214Scgd #endif
3974008ee50Schristos 			return bp;
3989f0aa214Scgd 		}
3999f0aa214Scgd 
4000f03f83aSchristos new:	if ((bp = calloc(1, (size_t)(sizeof(BKT) + mp->pagesize))) == NULL)
4014008ee50Schristos 		return NULL;
4029f0aa214Scgd #ifdef STATISTICS
4039f0aa214Scgd 	++mp->pagealloc;
4049f0aa214Scgd #endif
40517140cefScgd #if defined(DEBUG) || defined(PURIFY)
406096020daSchristos 	(void)memset(bp, 0xff, (size_t)(sizeof(BKT) + mp->pagesize));
4079f0aa214Scgd #endif
4084008ee50Schristos 	bp->page = (void *)((intptr_t)bp + sizeof(BKT));
4099f0aa214Scgd 	++mp->curcache;
4104008ee50Schristos 	return bp;
4119f0aa214Scgd }
4129f0aa214Scgd 
4139f0aa214Scgd /*
41417140cefScgd  * mpool_write
41517140cefScgd  *	Write a page to disk.
4169f0aa214Scgd  */
4179f0aa214Scgd static int
mpool_write(MPOOL * mp,BKT * bp)418cb9daf8fSchristos mpool_write(MPOOL *mp, BKT *bp)
4199f0aa214Scgd {
4209f0aa214Scgd 	off_t off;
4219f0aa214Scgd 
4229f0aa214Scgd #ifdef STATISTICS
4239f0aa214Scgd 	++mp->pagewrite;
4249f0aa214Scgd #endif
42517140cefScgd 
42617140cefScgd 	/* Run through the user's filter. */
42717140cefScgd 	if (mp->pgout)
42817140cefScgd 		(mp->pgout)(mp->pgcookie, bp->pgno, bp->page);
42917140cefScgd 
43017140cefScgd 	off = mp->pagesize * bp->pgno;
43186147b1cSchristos 	if (off / mp->pagesize != bp->pgno) {
43286147b1cSchristos 		/* Run past the end of the file, or at least the part we
43386147b1cSchristos 		   can address without large-file support?  */
43486147b1cSchristos 		errno = E2BIG;
43586147b1cSchristos 		return RET_ERROR;
43686147b1cSchristos 	}
43786147b1cSchristos 
4384008ee50Schristos 	if (pwrite(mp->fd, bp->page, (size_t)mp->pagesize, off) !=
4394008ee50Schristos 	    (ssize_t)mp->pagesize)
4404008ee50Schristos 		return RET_ERROR;
44117140cefScgd 
4422a8d4256Sscw 	/*
4432a8d4256Sscw 	 * Re-run through the input filter since this page may soon be
4442a8d4256Sscw 	 * accessed via the cache, and whatever the user's output filter
4452a8d4256Sscw 	 * did may screw things up if we don't let the input filter
4462a8d4256Sscw 	 * restore the in-core copy.
4472a8d4256Sscw 	 */
4482a8d4256Sscw 	if (mp->pgin)
4492a8d4256Sscw 		(mp->pgin)(mp->pgcookie, bp->pgno, bp->page);
4502a8d4256Sscw 
45117140cefScgd 	bp->flags &= ~MPOOL_DIRTY;
4524008ee50Schristos 	return RET_SUCCESS;
4539f0aa214Scgd }
4549f0aa214Scgd 
4559f0aa214Scgd /*
45617140cefScgd  * mpool_look
45717140cefScgd  *	Lookup a page in the cache.
4589f0aa214Scgd  */
4599f0aa214Scgd static BKT *
mpool_look(MPOOL * mp,pgno_t pgno)460cb9daf8fSchristos mpool_look(MPOOL *mp, pgno_t pgno)
4619f0aa214Scgd {
46217140cefScgd 	struct _hqh *head;
46317140cefScgd 	BKT *bp;
4649f0aa214Scgd 
46517140cefScgd 	head = &mp->hqh[HASHKEY(pgno)];
466c64e7224Schristos 	TAILQ_FOREACH(bp, head, hq)
46717140cefScgd 		if (bp->pgno == pgno) {
4689f0aa214Scgd #ifdef STATISTICS
4699f0aa214Scgd 			++mp->cachehit;
4709f0aa214Scgd #endif
4714008ee50Schristos 			return bp;
4729f0aa214Scgd 		}
4739f0aa214Scgd #ifdef STATISTICS
4749f0aa214Scgd 	++mp->cachemiss;
4759f0aa214Scgd #endif
4764008ee50Schristos 	return NULL;
4779f0aa214Scgd }
4789f0aa214Scgd 
4799f0aa214Scgd #ifdef STATISTICS
4809f0aa214Scgd /*
48117140cefScgd  * mpool_stat
48217140cefScgd  *	Print out cache statistics.
4839f0aa214Scgd  */
4849f0aa214Scgd void
mpool_stat(mp)4859f0aa214Scgd mpool_stat(mp)
4869f0aa214Scgd 	MPOOL *mp;
4879f0aa214Scgd {
48817140cefScgd 	BKT *bp;
4899f0aa214Scgd 	int cnt;
490cb9daf8fSchristos 	const char *sep;
4919f0aa214Scgd 
492cb9daf8fSchristos 	(void)fprintf(stderr, "%lu pages in the file\n", (u_long)mp->npages);
4939f0aa214Scgd 	(void)fprintf(stderr,
4949f0aa214Scgd 	    "page size %lu, cacheing %lu pages of %lu page max cache\n",
495cb9daf8fSchristos 	    (u_long)mp->pagesize, (u_long)mp->curcache, (u_long)mp->maxcache);
4969f0aa214Scgd 	(void)fprintf(stderr, "%lu page puts, %lu page gets, %lu page new\n",
4979f0aa214Scgd 	    mp->pageput, mp->pageget, mp->pagenew);
4989f0aa214Scgd 	(void)fprintf(stderr, "%lu page allocs, %lu page flushes\n",
4999f0aa214Scgd 	    mp->pagealloc, mp->pageflush);
5009f0aa214Scgd 	if (mp->cachehit + mp->cachemiss)
5019f0aa214Scgd 		(void)fprintf(stderr,
5029f0aa214Scgd 		    "%.0f%% cache hit rate (%lu hits, %lu misses)\n",
5039f0aa214Scgd 		    ((double)mp->cachehit / (mp->cachehit + mp->cachemiss))
5049f0aa214Scgd 		    * 100, mp->cachehit, mp->cachemiss);
5059f0aa214Scgd 	(void)fprintf(stderr, "%lu page reads, %lu page writes\n",
5069f0aa214Scgd 	    mp->pageread, mp->pagewrite);
5079f0aa214Scgd 
5089f0aa214Scgd 	sep = "";
5099f0aa214Scgd 	cnt = 0;
510c64e7224Schristos 	TAILQ_FOREACH(bp, &mp->lqh, q) {
51117140cefScgd 		(void)fprintf(stderr, "%s%d", sep, bp->pgno);
51217140cefScgd 		if (bp->flags & MPOOL_DIRTY)
5139f0aa214Scgd 			(void)fprintf(stderr, "d");
51417140cefScgd 		if (bp->flags & MPOOL_PINNED)
5159f0aa214Scgd 			(void)fprintf(stderr, "P");
5169f0aa214Scgd 		if (++cnt == 10) {
5179f0aa214Scgd 			sep = "\n";
5189f0aa214Scgd 			cnt = 0;
5199f0aa214Scgd 		} else
5209f0aa214Scgd 			sep = ", ";
5219f0aa214Scgd 
5229f0aa214Scgd 	}
5239f0aa214Scgd 	(void)fprintf(stderr, "\n");
5249f0aa214Scgd }
5259f0aa214Scgd #endif
526