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