* Copyright (c) 1990 The Regents of the University of California.
* %sccs.include.redist.c%
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid
[] = "@(#)mpool.c 5.6 (Berkeley) %G%";
#endif /* LIBC_SCCS and not lint */
#define __MPOOLINTERFACE_PRIVATE
static BKT
*mpool_bkt
__P((MPOOL
*));
static BKT
*mpool_look
__P((MPOOL
*, pgno_t
));
static int mpool_write
__P((MPOOL
*, BKT
*));
static void __mpoolerr
__P((const char *fmt
, ...));
* MPOOL_OPEN -- initialize a memory pool.
* key: Shared buffer key.
* pagesize: File page size.
* maxcache: Max number of cached pages.
* MPOOL pointer, NULL on error.
mpool_open(key
, fd
, pagesize
, maxcache
)
pgno_t pagesize
, maxcache
;
* We should only set st_size to 0 for pipes -- 4.4BSD has the fix so
* that stat(2) returns true for ISSOCK on pipes. Until then, this is
if (!S_ISREG(sb
.st_mode
)) {
if ((mp
= malloc(sizeof(MPOOL
))) == NULL
)
mp
->free
.cnext
= mp
->free
.cprev
= (BKT
*)&mp
->free
;
mp
->lru
.cnext
= mp
->lru
.cprev
= (BKT
*)&mp
->lru
;
for (entry
= 0; entry
< HASHSIZE
; ++entry
)
mp
->hashtable
[entry
].hnext
= mp
->hashtable
[entry
].hprev
=
mp
->hashtable
[entry
].cnext
= mp
->hashtable
[entry
].cprev
=
(BKT
*)&mp
->hashtable
[entry
];
mp
->npages
= sb
.st_size
/ pagesize
;
mp
->pgin
= mp
->pgout
= NULL
;
mp
->cachehit
= mp
->cachemiss
= mp
->pagealloc
= mp
->pageflush
=
mp
->pageget
= mp
->pagenew
= mp
->pageput
= mp
->pageread
=
* MPOOL_FILTER -- initialize input/output filters.
* pgin: Page in conversion routine.
* pgout: Page out conversion routine.
* pgcookie: Cookie for page in/out routines.
mpool_filter(mp
, pgin
, pgout
, pgcookie
)
void (*pgin
) __P((void *, pgno_t
, void *));
void (*pgout
) __P((void *, pgno_t
, void *));
* MPOOL_NEW -- get a new page
* pgnoadddr: place to store new page number
* Get a BKT from the cache. Assign a new page number, attach it to
* the hash and lru chains and return.
if ((b
= mpool_bkt(mp
)) == NULL
)
*pgnoaddr
= b
->pgno
= mp
->npages
++;
* MPOOL_GET -- get a page from the pool
mpool_get(mp
, pgno
, flags
)
u_int flags
; /* XXX not used? */
* If asking for a specific page that is already in the cache, find
if (b
= mpool_look(mp
, pgno
)) {
if (b
->flags
& MPOOL_PINNED
)
__mpoolerr("mpool_get: page %d already pinned",
b
->flags
|= MPOOL_PINNED
;
/* Not allowed to retrieve a non-existent page. */
if (pgno
>= mp
->npages
) {
/* Get a page from the cache. */
if ((b
= mpool_bkt(mp
)) == NULL
)
/* Read in the contents. */
off
= mp
->pagesize
* pgno
;
if (lseek(mp
->fd
, off
, SEEK_SET
) != off
)
if ((nr
= read(mp
->fd
, b
->page
, mp
->pagesize
)) != mp
->pagesize
) {
(mp
->pgin
)(mp
->pgcookie
, b
->pgno
, b
->page
);
* MPOOL_PUT -- return a page to the pool
mpool_put(mp
, page
, flags
)
baddr
= (BKT
*)((char *)page
- sizeof(BKT
));
if (!(baddr
->flags
& MPOOL_PINNED
))
__mpoolerr("mpool_put: page %d not pinned", b
->pgno
);
for (b
= mp
->lru
.cnext
; b
!= (BKT
*)&mp
->lru
; b
= b
->cnext
) {
if (b
== (BKT
*)&mp
->lru
)
__mpoolerr("mpool_put: %0x: bad address", baddr
);
baddr
->flags
&= ~MPOOL_PINNED
;
baddr
->flags
|= flags
& MPOOL_DIRTY
;
* MPOOL_CLOSE -- close the buffer pool
/* Free up any space allocated to the lru pages. */
for (b
= mp
->lru
.cprev
; b
!= (BKT
*)&mp
->lru
; b
= next
) {
* MPOOL_SYNC -- sync the file to disk.
for (b
= mp
->lru
.cprev
; b
!= (BKT
*)&mp
->lru
; b
= b
->cprev
)
if (b
->flags
& MPOOL_DIRTY
&& mpool_write(mp
, b
) == RET_ERROR
)
return (fsync(mp
->fd
) ? RET_ERROR
: RET_SUCCESS
);
* MPOOL_BKT -- get/create a BKT from the cache
* NULL on failure and a pointer to the BKT on success
if (mp
->curcache
< mp
->maxcache
)
* If the cache is maxxed out, search the lru list for a buffer we
* can flush. If we find one, write it if necessary and take it off
* any lists. If we don't find anything we grow the cache anyway.
* The cache never shrinks.
for (b
= mp
->lru
.cprev
; b
!= (BKT
*)&mp
->lru
; b
= b
->cprev
)
if (!(b
->flags
& MPOOL_PINNED
)) {
if (b
->flags
& MPOOL_DIRTY
&&
mpool_write(mp
, b
) == RET_ERROR
)
memset(b
, 0xff, sizeof(BKT
) + mp
->pagesize
);
new: if ((b
= malloc(sizeof(BKT
) + mp
->pagesize
)) == NULL
)
memset(b
, 0xff, sizeof(BKT
) + mp
->pagesize
);
b
->page
= (char *)b
+ sizeof(BKT
);
* MPOOL_WRITE -- sync a page to disk
(mp
->pgout
)(mp
->pgcookie
, b
->pgno
, b
->page
);
off
= mp
->pagesize
* b
->pgno
;
if (lseek(mp
->fd
, off
, SEEK_SET
) != off
)
if (write(mp
->fd
, b
->page
, mp
->pagesize
) != mp
->pagesize
)
b
->flags
&= ~MPOOL_DIRTY
;
* MPOOL_LOOK -- lookup a page
* NULL on failure and a pointer to the BKT on success
* If find the buffer, put it first on the hash chain so can
tb
= &mp
->hashtable
[HASHKEY(pgno
)];
for (b
= tb
->hnext
; b
!= (BKT
*)tb
; b
= b
->hnext
)
* MPOOL_STAT -- cache statistics
(void)fprintf(stderr
, "%lu pages in the file\n", mp
->npages
);
"page size %lu, cacheing %lu pages of %lu page max cache\n",
mp
->pagesize
, mp
->curcache
, mp
->maxcache
);
(void)fprintf(stderr
, "%lu page puts, %lu page gets, %lu page new\n",
mp
->pageput
, mp
->pageget
, mp
->pagenew
);
(void)fprintf(stderr
, "%lu page allocs, %lu page flushes\n",
mp
->pagealloc
, mp
->pageflush
);
if (mp
->cachehit
+ mp
->cachemiss
)
"%.0f%% cache hit rate (%lu hits, %lu misses)\n",
((double)mp
->cachehit
/ (mp
->cachehit
+ mp
->cachemiss
))
* 100, mp
->cachehit
, mp
->cachemiss
);
(void)fprintf(stderr
, "%lu page reads, %lu page writes\n",
mp
->pageread
, mp
->pagewrite
);
for (b
= mp
->lru
.cnext
; b
!= (BKT
*)&mp
->lru
; b
= b
->cnext
) {
(void)fprintf(stderr
, "%s%d", sep
, b
->pgno
);
if (b
->flags
& MPOOL_DIRTY
)
(void)fprintf(stderr
, "d");
if (b
->flags
& MPOOL_PINNED
)
(void)fprintf(stderr
, "P");
(void)fprintf(stderr
, "\n");
__mpoolerr(const char *fmt
, ...)
__mpoolerr(fmt
, va_alist
)
(void)vfprintf(stderr
, fmt
, ap
);
(void)fprintf(stderr
, "\n");