summaryrefslogtreecommitdiff
path: root/bdb/mp/mp_fput.c
blob: be03b721f369130c28146e2a1e12165ea58d11dd (plain)
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 1996, 1997, 1998, 1999, 2000
 *	Sleepycat Software.  All rights reserved.
 */
#include "db_config.h"

#ifndef lint
static const char revid[] = "$Id: mp_fput.c,v 11.16 2000/11/30 00:58:41 ubell Exp $";
#endif /* not lint */

#ifndef NO_SYSTEM_INCLUDES
#include <sys/types.h>

#endif

#ifdef  HAVE_RPC
#include "db_server.h"
#endif

#include "db_int.h"
#include "db_shash.h"
#include "mp.h"

#ifdef HAVE_RPC
#include "gen_client_ext.h"
#include "rpc_client_ext.h"
#endif

/*
 * memp_fput --
 *	Mpool file put function.
 */
int
memp_fput(dbmfp, pgaddr, flags)
	DB_MPOOLFILE *dbmfp;
	void *pgaddr;
	u_int32_t flags;
{
	BH *bhp;
	DB_ENV *dbenv;
	DB_MPOOL *dbmp;
	MPOOL *c_mp, *mp;
	int ret, wrote;

	dbmp = dbmfp->dbmp;
	dbenv = dbmp->dbenv;
	mp = dbmp->reginfo[0].primary;

#ifdef HAVE_RPC
	if (F_ISSET(dbenv, DB_ENV_RPCCLIENT))
		return (__dbcl_memp_fput(dbmfp, pgaddr, flags));
#endif

	PANIC_CHECK(dbenv);

	/* Validate arguments. */
	if (flags) {
		if ((ret = __db_fchk(dbenv, "memp_fput", flags,
		    DB_MPOOL_CLEAN | DB_MPOOL_DIRTY | DB_MPOOL_DISCARD)) != 0)
			return (ret);
		if ((ret = __db_fcchk(dbenv, "memp_fput",
		    flags, DB_MPOOL_CLEAN, DB_MPOOL_DIRTY)) != 0)
			return (ret);

		if (LF_ISSET(DB_MPOOL_DIRTY) && F_ISSET(dbmfp, MP_READONLY)) {
			__db_err(dbenv,
			    "%s: dirty flag set for readonly file page",
			    __memp_fn(dbmfp));
			return (EACCES);
		}
	}

	R_LOCK(dbenv, dbmp->reginfo);

	/* Decrement the pinned reference count. */
	if (dbmfp->pinref == 0) {
		__db_err(dbenv,
		    "%s: more pages returned than retrieved", __memp_fn(dbmfp));
		R_UNLOCK(dbenv, dbmp->reginfo);
		return (EINVAL);
	} 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. */
	c_mp = 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)) {
		++c_mp->stat.st_page_clean;
		--c_mp->stat.st_page_dirty;
		F_CLR(bhp, BH_DIRTY);
	}
	if (LF_ISSET(DB_MPOOL_DIRTY) && !F_ISSET(bhp, BH_DIRTY)) {
		--c_mp->stat.st_page_clean;
		++c_mp->stat.st_page_dirty;
		F_SET(bhp, BH_DIRTY);
	}
	if (LF_ISSET(DB_MPOOL_DISCARD))
		F_SET(bhp, BH_DISCARD);

	/*
	 * If the page is dirty and being scheduled to be written as part of
	 * a checkpoint, we no longer know that the log is up-to-date.
	 */
	if (F_ISSET(bhp, BH_DIRTY) && F_ISSET(bhp, BH_SYNC))
		F_SET(bhp, BH_SYNC_LOGFLSH);

	/*
	 * Check for a reference count going to zero.  This can happen if the
	 * application returns a page twice.
	 */
	if (bhp->ref == 0) {
		__db_err(dbenv, "%s: page %lu: unpinned page returned",
		    __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(&c_mp->bhq, bhp, q, __bh);
	if (F_ISSET(bhp, BH_DISCARD))
		SH_TAILQ_INSERT_HEAD(&c_mp->bhq, bhp, q, __bh);
	else
		SH_TAILQ_INSERT_TAIL(&c_mp->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 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_SYNC)) {
		if (F_ISSET(bhp, BH_DIRTY)) {
			if (__memp_bhwrite(dbmp,
			    dbmfp->mfp, bhp, NULL, &wrote) != 0 || !wrote)
				F_SET(mp, MP_LSN_RETRY);
		} else {
			F_CLR(bhp, BH_SYNC);

			--mp->lsn_cnt;
			--dbmfp->mfp->lsn_cnt;
		}
	}

	R_UNLOCK(dbenv, dbmp->reginfo);
	return (0);
}