1N/A/*-
1N/A * See the file LICENSE for redistribution information.
1N/A *
1N/A * Copyright (c) 1996, 1997, 1998
1N/A * Sleepycat Software. All rights reserved.
1N/A */
1N/A#include "config.h"
1N/A
1N/A#ifndef lint
1N/Astatic const char sccsid[] = "@(#)mp_fput.c 10.24 (Sleepycat) 9/27/98";
1N/A#endif /* not lint */
1N/A
1N/A#ifndef NO_SYSTEM_INCLUDES
1N/A#include <sys/types.h>
1N/A
1N/A#include <errno.h>
1N/A#endif
1N/A
1N/A#include "db_int.h"
1N/A#include "shqueue.h"
1N/A#include "db_shash.h"
1N/A#include "mp.h"
1N/A#include "common_ext.h"
1N/A
1N/A/*
1N/A * memp_fput --
1N/A * Mpool file put function.
1N/A */
1N/Aint
1N/Amemp_fput(dbmfp, pgaddr, flags)
1N/A DB_MPOOLFILE *dbmfp;
1N/A void *pgaddr;
1N/A u_int32_t flags;
1N/A{
1N/A BH *bhp;
1N/A DB_MPOOL *dbmp;
1N/A MPOOL *mp;
1N/A int wrote, ret;
1N/A
1N/A dbmp = dbmfp->dbmp;
1N/A mp = dbmp->mp;
1N/A
1N/A MP_PANIC_CHECK(dbmp);
1N/A
1N/A /* Validate arguments. */
1N/A if (flags) {
1N/A if ((ret = __db_fchk(dbmp->dbenv, "memp_fput", flags,
1N/A DB_MPOOL_CLEAN | DB_MPOOL_DIRTY | DB_MPOOL_DISCARD)) != 0)
1N/A return (ret);
1N/A if ((ret = __db_fcchk(dbmp->dbenv, "memp_fput",
1N/A flags, DB_MPOOL_CLEAN, DB_MPOOL_DIRTY)) != 0)
1N/A return (ret);
1N/A
1N/A if (LF_ISSET(DB_MPOOL_DIRTY) && F_ISSET(dbmfp, MP_READONLY)) {
1N/A __db_err(dbmp->dbenv,
1N/A "%s: dirty flag set for readonly file page",
1N/A __memp_fn(dbmfp));
1N/A return (EACCES);
1N/A }
1N/A }
1N/A
1N/A LOCKREGION(dbmp);
1N/A
1N/A /* Decrement the pinned reference count. */
1N/A if (dbmfp->pinref == 0)
1N/A __db_err(dbmp->dbenv,
1N/A "%s: put: more blocks returned than retrieved",
1N/A __memp_fn(dbmfp));
1N/A else
1N/A --dbmfp->pinref;
1N/A
1N/A /*
1N/A * If we're mapping the file, there's nothing to do. Because we can
1N/A * stop mapping the file at any time, we have to check on each buffer
1N/A * to see if the address we gave the application was part of the map
1N/A * region.
1N/A */
1N/A if (dbmfp->addr != NULL && pgaddr >= dbmfp->addr &&
1N/A (u_int8_t *)pgaddr <= (u_int8_t *)dbmfp->addr + dbmfp->len) {
1N/A UNLOCKREGION(dbmp);
1N/A return (0);
1N/A }
1N/A
1N/A /* Convert the page address to a buffer header. */
1N/A bhp = (BH *)((u_int8_t *)pgaddr - SSZA(BH, buf));
1N/A
1N/A /* Set/clear the page bits. */
1N/A if (LF_ISSET(DB_MPOOL_CLEAN) && F_ISSET(bhp, BH_DIRTY)) {
1N/A ++mp->stat.st_page_clean;
1N/A --mp->stat.st_page_dirty;
1N/A F_CLR(bhp, BH_DIRTY);
1N/A }
1N/A if (LF_ISSET(DB_MPOOL_DIRTY) && !F_ISSET(bhp, BH_DIRTY)) {
1N/A --mp->stat.st_page_clean;
1N/A ++mp->stat.st_page_dirty;
1N/A F_SET(bhp, BH_DIRTY);
1N/A }
1N/A if (LF_ISSET(DB_MPOOL_DISCARD))
1N/A F_SET(bhp, BH_DISCARD);
1N/A
1N/A /*
1N/A * Check for a reference count going to zero. This can happen if the
1N/A * application returns a page twice.
1N/A */
1N/A if (bhp->ref == 0) {
1N/A __db_err(dbmp->dbenv, "%s: page %lu: unpinned page returned",
1N/A __memp_fn(dbmfp), (u_long)bhp->pgno);
1N/A UNLOCKREGION(dbmp);
1N/A return (EINVAL);
1N/A }
1N/A
1N/A /*
1N/A * If more than one reference to the page, we're done. Ignore the
1N/A * discard flags (for now) and leave it at its position in the LRU
1N/A * chain. The rest gets done at last reference close.
1N/A */
1N/A if (--bhp->ref > 0) {
1N/A UNLOCKREGION(dbmp);
1N/A return (0);
1N/A }
1N/A
1N/A /* Move the buffer to the head/tail of the LRU chain. */
1N/A SH_TAILQ_REMOVE(&mp->bhq, bhp, q, __bh);
1N/A if (F_ISSET(bhp, BH_DISCARD))
1N/A SH_TAILQ_INSERT_HEAD(&mp->bhq, bhp, q, __bh);
1N/A else
1N/A SH_TAILQ_INSERT_TAIL(&mp->bhq, bhp, q);
1N/A
1N/A /*
1N/A * If this buffer is scheduled for writing because of a checkpoint, we
1N/A * need to write it (if we marked it dirty), or update the checkpoint
1N/A * counters (if we didn't mark it dirty). If we try to write it and
1N/A * can't, that's not necessarily an error, but set a flag so that the
1N/A * next time the memp_sync function runs we try writing it there, as
1N/A * the checkpoint application better be able to write all of the files.
1N/A */
1N/A if (F_ISSET(bhp, BH_WRITE))
1N/A if (F_ISSET(bhp, BH_DIRTY)) {
1N/A if (__memp_bhwrite(dbmp,
1N/A dbmfp->mfp, bhp, NULL, &wrote) != 0 || !wrote)
1N/A F_SET(mp, MP_LSN_RETRY);
1N/A } else {
1N/A F_CLR(bhp, BH_WRITE);
1N/A
1N/A --dbmfp->mfp->lsn_cnt;
1N/A --mp->lsn_cnt;
1N/A }
1N/A
1N/A UNLOCKREGION(dbmp);
1N/A return (0);
1N/A}