Path: blob/main/crypto/krb5/src/plugins/kdb/db2/libdb2/mpool/mpool.c
34927 views
/*-1* Copyright (c) 1990, 1993, 19942* The Regents of the University of California. All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12* 3. All advertising materials mentioning features or use of this software13* must display the following acknowledgement:14* This product includes software developed by the University of15* California, Berkeley and its contributors.16* 4. Neither the name of the University nor the names of its contributors17* may be used to endorse or promote products derived from this software18* without specific prior written permission.19*20* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND21* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE22* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE23* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE24* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL25* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS26* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)27* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT28* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY29* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF30* SUCH DAMAGE.31*/3233#if defined(LIBC_SCCS) && !defined(lint)34static char sccsid[] = "@(#)mpool.c 8.7 (Berkeley) 11/2/95";35#endif /* LIBC_SCCS and not lint */3637#include <sys/param.h>38#include <sys/stat.h>3940#include <errno.h>41#include <stdio.h>42#include <stdlib.h>43#include <string.h>44#include <unistd.h>4546#include "db-int.h"47#include "mpool.h"4849static BKT *mpool_bkt __P((MPOOL *));50static BKT *mpool_look __P((MPOOL *, db_pgno_t));51static int mpool_write __P((MPOOL *, BKT *));5253/*54* mpool_open --55* Initialize a memory pool.56*/57MPOOL *58mpool_open(void *key, int fd, db_pgno_t pagesize, db_pgno_t maxcache)59{60struct stat sb;61MPOOL *mp;62int entry;6364/*65* Get information about the file.66*67* XXX68* We don't currently handle pipes, although we should.69*/70if (fstat(fd, &sb))71return (NULL);72if (!S_ISREG(sb.st_mode)) {73errno = ESPIPE;74return (NULL);75}7677/* Allocate and initialize the MPOOL cookie. */78if ((mp = (MPOOL *)calloc(1, sizeof(MPOOL))) == NULL)79return (NULL);80TAILQ_INIT(&mp->lqh);81for (entry = 0; entry < HASHSIZE; ++entry)82TAILQ_INIT(&mp->hqh[entry]);83mp->maxcache = maxcache;84mp->npages = sb.st_size / pagesize;85mp->pagesize = pagesize;86mp->fd = fd;87return (mp);88}8990/*91* mpool_filter --92* Initialize input/output filters.93*/94void95mpool_filter(MPOOL *mp, void (*pgin) __P((void *, db_pgno_t, void *)),96void (*pgout) __P((void *, db_pgno_t, void *)), void *pgcookie)97{98mp->pgin = pgin;99mp->pgout = pgout;100mp->pgcookie = pgcookie;101}102103/*104* mpool_new --105* Get a new page of memory.106*/107void *108mpool_new(MPOOL *mp, db_pgno_t *pgnoaddr, u_int flags)109{110struct _hqh *head;111BKT *bp;112113if (mp->npages == MAX_PAGE_NUMBER) {114(void)fprintf(stderr, "mpool_new: page allocation overflow.\n");115abort();116}117#ifdef STATISTICS118++mp->pagenew;119#endif120/*121* Get a BKT from the cache. Assign a new page number, attach122* it to the head of the hash chain, the tail of the lru chain,123* and return.124*/125if ((bp = mpool_bkt(mp)) == NULL)126return (NULL);127if (flags == MPOOL_PAGE_REQUEST) {128mp->npages++;129bp->pgno = *pgnoaddr;130} else131bp->pgno = *pgnoaddr = mp->npages++;132133bp->flags = MPOOL_PINNED | MPOOL_INUSE;134135head = &mp->hqh[HASHKEY(bp->pgno)];136TAILQ_INSERT_HEAD(head, bp, hq);137TAILQ_INSERT_TAIL(&mp->lqh, bp, q);138return (bp->page);139}140141int142mpool_delete(MPOOL *mp, void *page)143{144struct _hqh *head;145BKT *bp;146147bp = (void *)((char *)page - sizeof(BKT));148149#ifdef DEBUG150if (!(bp->flags & MPOOL_PINNED)) {151(void)fprintf(stderr,152"mpool_delete: page %d not pinned\n", bp->pgno);153abort();154}155#endif156157/* Remove from the hash and lru queues. */158head = &mp->hqh[HASHKEY(bp->pgno)];159TAILQ_REMOVE(head, bp, hq);160TAILQ_REMOVE(&mp->lqh, bp, q);161162free(bp);163return (RET_SUCCESS);164}165166/*167* mpool_get168* Get a page.169*/170void *171mpool_get(MPOOL *mp, db_pgno_t pgno, u_int flags)172{173struct _hqh *head;174BKT *bp;175off_t off;176int nr;177178#ifdef STATISTICS179++mp->pageget;180#endif181182/* Check for a page that is cached. */183if ((bp = mpool_look(mp, pgno)) != NULL) {184#ifdef DEBUG185if (!(flags & MPOOL_IGNOREPIN) && bp->flags & MPOOL_PINNED) {186(void)fprintf(stderr,187"mpool_get: page %d already pinned\n", bp->pgno);188abort();189}190#endif191/*192* Move the page to the head of the hash chain and the tail193* of the lru chain.194*/195head = &mp->hqh[HASHKEY(bp->pgno)];196TAILQ_REMOVE(head, bp, hq);197TAILQ_INSERT_HEAD(head, bp, hq);198TAILQ_REMOVE(&mp->lqh, bp, q);199TAILQ_INSERT_TAIL(&mp->lqh, bp, q);200201/* Return a pinned page. */202if (!(flags & MPOOL_IGNOREPIN))203bp->flags |= MPOOL_PINNED;204return (bp->page);205}206207/* Get a page from the cache. */208if ((bp = mpool_bkt(mp)) == NULL)209return (NULL);210211/* Read in the contents. */212#ifdef STATISTICS213++mp->pageread;214#endif215off = mp->pagesize * pgno;216if (off / mp->pagesize != pgno) {217/* Run past the end of the file, or at least the part we218can address without large-file support? */219errno = E2BIG;220return NULL;221}222if (lseek(mp->fd, off, SEEK_SET) != off)223return (NULL);224225if ((nr = read(mp->fd, bp->page, mp->pagesize)) !=226(ssize_t)mp->pagesize) {227if (nr > 0) {228/* A partial read is definitely bad. */229errno = EINVAL;230return (NULL);231} else {232/*233* A zero-length reads, means you need to create a234* new page.235*/236memset(bp->page, 0, mp->pagesize);237}238}239240/* Set the page number, pin the page. */241bp->pgno = pgno;242if (!(flags & MPOOL_IGNOREPIN))243bp->flags = MPOOL_PINNED;244bp->flags |= MPOOL_INUSE;245246/*247* Add the page to the head of the hash chain and the tail248* of the lru chain.249*/250head = &mp->hqh[HASHKEY(bp->pgno)];251TAILQ_INSERT_HEAD(head, bp, hq);252TAILQ_INSERT_TAIL(&mp->lqh, bp, q);253254/* Run through the user's filter. */255if (mp->pgin != NULL)256(mp->pgin)(mp->pgcookie, bp->pgno, bp->page);257258return (bp->page);259}260261/*262* mpool_put263* Return a page.264*/265int266mpool_put(MPOOL *mp, void *page, u_int flags)267{268BKT *bp;269270#ifdef STATISTICS271++mp->pageput;272#endif273bp = (void *)((char *)page - sizeof(BKT));274#ifdef DEBUG275if (!(bp->flags & MPOOL_PINNED)) {276(void)fprintf(stderr,277"mpool_put: page %d not pinned\n", bp->pgno);278abort();279}280#endif281bp->flags &= ~MPOOL_PINNED;282if (flags & MPOOL_DIRTY)283bp->flags |= flags & MPOOL_DIRTY;284return (RET_SUCCESS);285}286287/*288* mpool_close289* Close the buffer pool.290*/291int292mpool_close(MPOOL *mp)293{294BKT *bp;295296/* Free up any space allocated to the lru pages. */297while ((bp = mp->lqh.tqh_first) != NULL) {298TAILQ_REMOVE(&mp->lqh, mp->lqh.tqh_first, q);299free(bp);300}301302/* Free the MPOOL cookie. */303free(mp);304return (RET_SUCCESS);305}306307/*308* mpool_sync309* Sync the pool to disk.310*/311int312mpool_sync(MPOOL *mp)313{314BKT *bp;315316/* Walk the lru chain, flushing any dirty pages to disk. */317for (bp = mp->lqh.tqh_first; bp != NULL; bp = bp->q.tqe_next)318if (bp->flags & MPOOL_DIRTY &&319mpool_write(mp, bp) == RET_ERROR)320return (RET_ERROR);321322/* Sync the file descriptor. */323return (fsync(mp->fd) ? RET_ERROR : RET_SUCCESS);324}325326/*327* mpool_bkt328* Get a page from the cache (or create one).329*/330static BKT *331mpool_bkt(MPOOL *mp)332{333struct _hqh *head;334BKT *bp;335336/* If under the max cached, always create a new page. */337if (mp->curcache < mp->maxcache)338goto new;339340/*341* If the cache is max'd out, walk the lru list for a buffer we342* can flush. If we find one, write it (if necessary) and take it343* off any lists. If we don't find anything we grow the cache anyway.344* The cache never shrinks.345*/346for (bp = mp->lqh.tqh_first; bp != NULL; bp = bp->q.tqe_next)347if (!(bp->flags & MPOOL_PINNED)) {348/* Flush if dirty. */349if (bp->flags & MPOOL_DIRTY &&350mpool_write(mp, bp) == RET_ERROR)351return (NULL);352#ifdef STATISTICS353++mp->pageflush;354#endif355/* Remove from the hash and lru queues. */356head = &mp->hqh[HASHKEY(bp->pgno)];357TAILQ_REMOVE(head, bp, hq);358TAILQ_REMOVE(&mp->lqh, bp, q);359#if defined(DEBUG) && !defined(DEBUG_IDX0SPLIT)360{ void *spage;361spage = bp->page;362memset(bp, 0xff, sizeof(BKT) + mp->pagesize);363bp->page = spage;364}365#endif366bp->flags = 0;367return (bp);368}369370new: if ((bp = (BKT *)malloc(sizeof(BKT) + mp->pagesize)) == NULL)371return (NULL);372#ifdef STATISTICS373++mp->pagealloc;374#endif375#if defined(DEBUG) || defined(PURIFY) || 1376memset(bp, 0xff, sizeof(BKT) + mp->pagesize);377#endif378bp->page = (char *)bp + sizeof(BKT);379bp->flags = 0;380++mp->curcache;381return (bp);382}383384/*385* mpool_write386* Write a page to disk.387*/388static int389mpool_write(MPOOL *mp, BKT *bp)390{391off_t off;392393#ifdef STATISTICS394++mp->pagewrite;395#endif396397/* Run through the user's filter. */398if (mp->pgout)399(mp->pgout)(mp->pgcookie, bp->pgno, bp->page);400401off = mp->pagesize * bp->pgno;402if (off / mp->pagesize != bp->pgno) {403/* Run past the end of the file, or at least the part we404can address without large-file support? */405errno = E2BIG;406return RET_ERROR;407}408if (lseek(mp->fd, off, SEEK_SET) != off)409return (RET_ERROR);410if (write(mp->fd, bp->page, mp->pagesize) !=411(ssize_t)mp->pagesize)412return (RET_ERROR);413414/*415* Re-run through the input filter since this page may soon be416* accessed via the cache, and whatever the user's output filter417* did may screw things up if we don't let the input filter418* restore the in-core copy.419*/420if (mp->pgin)421(mp->pgin)(mp->pgcookie, bp->pgno, bp->page);422bp->flags &= ~MPOOL_DIRTY;423return (RET_SUCCESS);424}425426/*427* mpool_look428* Lookup a page in the cache.429*/430static BKT *431mpool_look(MPOOL *mp, db_pgno_t pgno)432{433struct _hqh *head;434BKT *bp;435436head = &mp->hqh[HASHKEY(pgno)];437for (bp = head->tqh_first; bp != NULL; bp = bp->hq.tqe_next)438if ((bp->pgno == pgno) && (bp->flags & MPOOL_INUSE)) {439#ifdef STATISTICS440++mp->cachehit;441#endif442return (bp);443}444#ifdef STATISTICS445++mp->cachemiss;446#endif447return (NULL);448}449450#ifdef STATISTICS451/*452* mpool_stat453* Print out cache statistics.454*/455void456mpool_stat(MPOOL *mp)457{458BKT *bp;459int cnt;460char *sep;461462(void)fprintf(stderr, "%lu pages in the file\n", mp->npages);463(void)fprintf(stderr,464"page size %lu, cacheing %lu pages of %lu page max cache\n",465mp->pagesize, mp->curcache, mp->maxcache);466(void)fprintf(stderr, "%lu page puts, %lu page gets, %lu page new\n",467mp->pageput, mp->pageget, mp->pagenew);468(void)fprintf(stderr, "%lu page allocs, %lu page flushes\n",469mp->pagealloc, mp->pageflush);470if (mp->cachehit + mp->cachemiss)471(void)fprintf(stderr,472"%.0f%% cache hit rate (%lu hits, %lu misses)\n",473((double)mp->cachehit / (mp->cachehit + mp->cachemiss))474* 100, mp->cachehit, mp->cachemiss);475(void)fprintf(stderr, "%lu page reads, %lu page writes\n",476mp->pageread, mp->pagewrite);477478sep = "";479cnt = 0;480for (bp = mp->lqh.tqh_first; bp != NULL; bp = bp->q.tqe_next) {481(void)fprintf(stderr, "%s%d", sep, bp->pgno);482if (bp->flags & MPOOL_DIRTY)483(void)fprintf(stderr, "d");484if (bp->flags & MPOOL_PINNED)485(void)fprintf(stderr, "P");486if (++cnt == 10) {487sep = "\n";488cnt = 0;489} else490sep = ", ";491492}493(void)fprintf(stderr, "\n");494}495#else496void497mpool_stat(MPOOL *mp)498{499}500#endif501502503