1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
|
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1996, 1997, 1998, 1999
* Sleepycat Software. All rights reserved.
*/
#include "db_config.h"
#ifndef lint
static const char sccsid[] = "@(#)mp_fput.c 11.3 (Sleepycat) 10/29/99";
#endif /* not lint */
#ifndef NO_SYSTEM_INCLUDES
#include <sys/types.h>
#include <errno.h>
#endif
#include "db_int.h"
#include "db_shash.h"
#include "mp.h"
/*
* CDB_memp_fput --
* Mpool file put function.
*/
int
CDB_memp_fput(dbmfp, pgaddr, flags)
DB_MPOOLFILE *dbmfp;
void *pgaddr;
u_int32_t flags;
{
BH *bhp;
DB_ENV *dbenv;
DB_MPOOL *dbmp;
MCACHE *mc;
MPOOL *mp;
int ret, wrote;
dbmp = dbmfp->dbmp;
dbenv = dbmp->dbenv;
mp = dbmp->reginfo.primary;
PANIC_CHECK(dbenv);
/* Validate arguments. */
if (flags) {
if ((ret = CDB___db_fchk(dbenv, "CDB_memp_fput", flags,
DB_MPOOL_CLEAN | DB_MPOOL_DIRTY | DB_MPOOL_DISCARD)) != 0)
return (ret);
if ((ret = CDB___db_fcchk(dbenv, "CDB_memp_fput",
flags, DB_MPOOL_CLEAN, DB_MPOOL_DIRTY)) != 0)
return (ret);
if (LF_ISSET(DB_MPOOL_DIRTY) && F_ISSET(dbmfp, MP_READONLY)) {
CDB___db_err(dbenv,
"%s: dirty flag set for readonly file page",
CDB___memp_fn(dbmfp));
return (EACCES);
}
}
R_LOCK(dbenv, &dbmp->reginfo);
/* Decrement the pinned reference count. */
if (dbmfp->pinref == 0)
CDB___db_err(dbenv, "%s: put: more blocks returned than retrieved",
CDB___memp_fn(dbmfp));
else
--dbmfp->pinref;
/*
* If we're mapping the file, there's nothing to do. Because we can
* stop mapping the file at any time, we have to check on each buffer
* to see if the address we gave the application was part of the map
* region.
*/
if (dbmfp->addr != NULL && pgaddr >= dbmfp->addr &&
(u_int8_t *)pgaddr <= (u_int8_t *)dbmfp->addr + dbmfp->len) {
R_UNLOCK(dbenv, &dbmp->reginfo);
return (0);
}
/* Convert the page address to a buffer header. */
bhp = (BH *)((u_int8_t *)pgaddr - SSZA(BH, buf));
/* Convert the buffer header to a cache. */
mc = BH_TO_CACHE(dbmp, bhp);
/* UNLOCK THE REGION, LOCK THE CACHE. */
/* Set/clear the page bits. */
if (LF_ISSET(DB_MPOOL_CLEAN) && F_ISSET(bhp, BH_DIRTY)) {
++mc->stat.st_page_clean;
--mc->stat.st_page_dirty;
F_CLR(bhp, BH_DIRTY);
}
if (LF_ISSET(DB_MPOOL_DIRTY) && !F_ISSET(bhp, BH_DIRTY)) {
--mc->stat.st_page_clean;
++mc->stat.st_page_dirty;
F_SET(bhp, BH_DIRTY);
}
if (LF_ISSET(DB_MPOOL_DISCARD))
F_SET(bhp, BH_DISCARD);
/*
* Check for a reference count going to zero. This can happen if the
* application returns a page twice.
*/
if (bhp->ref == 0) {
CDB___db_err(dbenv, "%s: page %lu: unpinned page returned",
CDB___memp_fn(dbmfp), (u_long)bhp->pgno);
R_UNLOCK(dbenv, &dbmp->reginfo);
return (EINVAL);
}
/*
* If more than one reference to the page, we're done. Ignore the
* discard flags (for now) and leave it at its position in the LRU
* chain. The rest gets done at last reference close.
*/
if (--bhp->ref > 0) {
R_UNLOCK(dbenv, &dbmp->reginfo);
return (0);
}
/*
* Move the buffer to the head/tail of the LRU chain. We do this
* before writing the buffer for checkpoint purposes, as the write
* can discard the region lock and allow another process to acquire
* buffer. We could keep that from happening, but there seems no
* reason to do so.
*/
SH_TAILQ_REMOVE(&mc->bhq, bhp, q, __bh);
if (F_ISSET(bhp, BH_DISCARD))
SH_TAILQ_INSERT_HEAD(&mc->bhq, bhp, q, __bh);
else
SH_TAILQ_INSERT_TAIL(&mc->bhq, bhp, q);
/*
* If this buffer is scheduled for writing because of a checkpoint, we
* need to write it (if it's dirty), or update the checkpoint counters
* (if it's not dirty). If we try to write it and can't, that's not
* necessarily an error as it's not completely unreasonable that the
* application have permission to write the underlying file, but set a
* flag so that the next time the CDB_memp_sync function is called we try
* writing it there, as the checkpoint thread of control better be able
* to write all of the files.
*/
if (F_ISSET(bhp, BH_WRITE)) {
if (F_ISSET(bhp, BH_DIRTY)) {
if (CDB___memp_bhwrite(dbmp,
dbmfp->mfp, bhp, NULL, &wrote) != 0 || !wrote)
F_SET(mp, MP_LSN_RETRY);
} else {
F_CLR(bhp, BH_WRITE);
--mp->lsn_cnt;
--dbmfp->mfp->lsn_cnt;
}
}
R_UNLOCK(dbenv, &dbmp->reginfo);
return (0);
}
|