File: mp_fput.c

package info (click to toggle)
htdig 1%3A3.2.0b6-16
  • links: PTS
  • area: main
  • in suites: stretch
  • size: 15,624 kB
  • ctags: 9,630
  • sloc: ansic: 49,630; cpp: 46,470; sh: 17,400; xml: 4,180; perl: 2,543; makefile: 886; php: 79; asm: 14
file content (165 lines) | stat: -rw-r--r-- 4,403 bytes parent folder | download | duplicates (9)
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);
}