xref: /illumos-gate/usr/src/cmd/sendmail/db/btree/bt_page.c (revision a386cc11a86ecb60f5a48078d22c1500e2ad003e)
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 1997, 1998
5  *	Sleepycat Software.  All rights reserved.
6  */
7 /*
8  * Copyright (c) 1990, 1993, 1994, 1995, 1996
9  *	Keith Bostic.  All rights reserved.
10  */
11 /*
12  * Copyright (c) 1990, 1993, 1994, 1995
13  *	The Regents of the University of California.  All rights reserved.
14  *
15  * This code is derived from software contributed to Berkeley by
16  * Mike Olson.
17  *
18  * Redistribution and use in source and binary forms, with or without
19  * modification, are permitted provided that the following conditions
20  * are met:
21  * 1. Redistributions of source code must retain the above copyright
22  *    notice, this list of conditions and the following disclaimer.
23  * 2. Redistributions in binary form must reproduce the above copyright
24  *    notice, this list of conditions and the following disclaimer in the
25  *    documentation and/or other materials provided with the distribution.
26  * 3. All advertising materials mentioning features or use of this software
27  *    must display the following acknowledgement:
28  *	This product includes software developed by the University of
29  *	California, Berkeley and its contributors.
30  * 4. Neither the name of the University nor the names of its contributors
31  *    may be used to endorse or promote products derived from this software
32  *    without specific prior written permission.
33  *
34  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
35  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
36  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
37  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
38  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
39  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
40  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
41  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
42  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
43  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44  * SUCH DAMAGE.
45  */
46 
47 #include "config.h"
48 
49 #ifndef lint
50 static const char sccsid[] = "@(#)bt_page.c	10.17 (Sleepycat) 1/3/99";
51 #endif /* not lint */
52 
53 #ifndef NO_SYSTEM_INCLUDES
54 #include <sys/types.h>
55 
56 #include <errno.h>
57 #include <string.h>
58 #endif
59 
60 #include "db_int.h"
61 #include "db_page.h"
62 #include "btree.h"
63 
64 /*
65  * __bam_new --
66  *	Get a new page, preferably from the freelist.
67  *
68  * PUBLIC: int __bam_new __P((DBC *, u_int32_t, PAGE **));
69  */
70 int
71 __bam_new(dbc, type, pagepp)
72 	DBC *dbc;
73 	u_int32_t type;
74 	PAGE **pagepp;
75 {
76 	BTMETA *meta;
77 	DB *dbp;
78 	DB_LOCK metalock;
79 	PAGE *h;
80 	db_pgno_t pgno;
81 	int ret;
82 
83 	dbp = dbc->dbp;
84 	meta = NULL;
85 	h = NULL;
86 	metalock = LOCK_INVALID;
87 
88 	pgno = PGNO_METADATA;
89 	if ((ret = __bam_lget(dbc, 0, pgno, DB_LOCK_WRITE, &metalock)) != 0)
90 		goto err;
91 	if ((ret = memp_fget(dbp->mpf, &pgno, 0, (PAGE **)&meta)) != 0)
92 		goto err;
93 
94 	if (meta->free == PGNO_INVALID) {
95 		if ((ret = memp_fget(dbp->mpf, &pgno, DB_MPOOL_NEW, &h)) != 0)
96 			goto err;
97 		ZERO_LSN(h->lsn);
98 		h->pgno = pgno;
99 	} else {
100 		pgno = meta->free;
101 		if ((ret = memp_fget(dbp->mpf, &pgno, 0, &h)) != 0)
102 			goto err;
103 		meta->free = h->next_pgno;
104 	}
105 
106 	/* Log the change. */
107 	if (DB_LOGGING(dbc)) {
108 		if ((ret = __bam_pg_alloc_log(dbp->dbenv->lg_info, dbc->txn,
109 		    &meta->lsn, 0, dbp->log_fileid, &meta->lsn, &h->lsn,
110 		    h->pgno, (u_int32_t)type, meta->free)) != 0)
111 			goto err;
112 		LSN(h) = LSN(meta);
113 	}
114 
115 	(void)memp_fput(dbp->mpf, (PAGE *)meta, DB_MPOOL_DIRTY);
116 	(void)__BT_TLPUT(dbc, metalock);
117 
118 	P_INIT(h, dbp->pgsize, h->pgno, PGNO_INVALID, PGNO_INVALID, 0, type);
119 	*pagepp = h;
120 	return (0);
121 
122 err:	if (h != NULL)
123 		(void)memp_fput(dbp->mpf, h, 0);
124 	if (meta != NULL)
125 		(void)memp_fput(dbp->mpf, meta, 0);
126 	if (metalock != LOCK_INVALID)
127 		(void)__BT_TLPUT(dbc, metalock);
128 	return (ret);
129 }
130 
131 /*
132  * __bam_lput --
133  *	The standard lock put call.
134  *
135  * PUBLIC: int __bam_lput __P((DBC *, DB_LOCK));
136  */
137 int
138 __bam_lput(dbc, lock)
139 	DBC *dbc;
140 	DB_LOCK lock;
141 {
142 	return (__BT_LPUT(dbc, lock));
143 }
144 
145 /*
146  * __bam_free --
147  *	Add a page to the head of the freelist.
148  *
149  * PUBLIC: int __bam_free __P((DBC *, PAGE *));
150  */
151 int
152 __bam_free(dbc, h)
153 	DBC *dbc;
154 	PAGE *h;
155 {
156 	BTMETA *meta;
157 	DB *dbp;
158 	DBT ldbt;
159 	DB_LOCK metalock;
160 	db_pgno_t pgno;
161 	u_int32_t dirty_flag;
162 	int ret, t_ret;
163 
164 	dbp = dbc->dbp;
165 
166 	/*
167 	 * Retrieve the metadata page and insert the page at the head of
168 	 * the free list.  If either the lock get or page get routines
169 	 * fail, then we need to put the page with which we were called
170 	 * back because our caller assumes we take care of it.
171 	 */
172 	dirty_flag = 0;
173 	pgno = PGNO_METADATA;
174 	if ((ret = __bam_lget(dbc, 0, pgno, DB_LOCK_WRITE, &metalock)) != 0)
175 		goto err;
176 	if ((ret = memp_fget(dbp->mpf, &pgno, 0, (PAGE **)&meta)) != 0) {
177 		(void)__BT_TLPUT(dbc, metalock);
178 		goto err;
179 	}
180 
181 	/* Log the change. */
182 	if (DB_LOGGING(dbc)) {
183 		memset(&ldbt, 0, sizeof(ldbt));
184 		ldbt.data = h;
185 		ldbt.size = P_OVERHEAD;
186 		if ((ret = __bam_pg_free_log(dbp->dbenv->lg_info,
187 		    dbc->txn, &meta->lsn, 0, dbp->log_fileid, h->pgno,
188 		    &meta->lsn, &ldbt, meta->free)) != 0) {
189 			(void)memp_fput(dbp->mpf, (PAGE *)meta, 0);
190 			(void)__BT_TLPUT(dbc, metalock);
191 			return (ret);
192 		}
193 		LSN(h) = LSN(meta);
194 	}
195 
196 	/*
197 	 * The page should have nothing interesting on it, re-initialize it,
198 	 * leaving only the page number and the LSN.
199 	 */
200 #ifdef DIAGNOSTIC
201 	{ db_pgno_t __pgno; DB_LSN __lsn;
202 		__pgno = h->pgno;
203 		__lsn = h->lsn;
204 		memset(h, 0xdb, dbp->pgsize);
205 		h->pgno = __pgno;
206 		h->lsn = __lsn;
207 	}
208 #endif
209 	P_INIT(h, dbp->pgsize, h->pgno, PGNO_INVALID, meta->free, 0, P_INVALID);
210 
211 	/* Link the page on the metadata free list. */
212 	meta->free = h->pgno;
213 
214 	/* Discard the metadata page. */
215 	ret = memp_fput(dbp->mpf, (PAGE *)meta, DB_MPOOL_DIRTY);
216 	if ((t_ret = __BT_TLPUT(dbc, metalock)) != 0)
217 		ret = t_ret;
218 
219 	/* Discard the caller's page reference. */
220 	dirty_flag = DB_MPOOL_DIRTY;
221 err:	if ((t_ret = memp_fput(dbp->mpf, h, dirty_flag)) != 0 && ret == 0)
222 		ret = t_ret;
223 
224 	/*
225 	 * XXX
226 	 * We have to unlock the caller's page in the caller!
227 	 */
228 	return (ret);
229 }
230 
231 #ifdef DEBUG
232 /*
233  * __bam_lt --
234  *	Print out the list of locks currently held by a cursor.
235  *
236  * PUBLIC: int __bam_lt __P((DBC *));
237  */
238 int
239 __bam_lt(dbc)
240 	DBC *dbc;
241 {
242 	DB *dbp;
243 	DB_LOCKREQ req;
244 
245 	dbp = dbc->dbp;
246 	if (F_ISSET(dbp, DB_AM_LOCKING)) {
247 		req.op = DB_LOCK_DUMP;
248 		lock_vec(dbp->dbenv->lk_info, dbc->locker, 0, &req, 1, NULL);
249 	}
250 	return (0);
251 }
252 #endif
253 
254 /*
255  * __bam_lget --
256  *	The standard lock get call.
257  *
258  * PUBLIC: int __bam_lget
259  * PUBLIC:    __P((DBC *, int, db_pgno_t, db_lockmode_t, DB_LOCK *));
260  */
261 int
262 __bam_lget(dbc, do_couple, pgno, mode, lockp)
263 	DBC *dbc;
264 	int do_couple;
265 	db_pgno_t pgno;
266 	db_lockmode_t mode;
267 	DB_LOCK *lockp;
268 {
269 	DB *dbp;
270 	DB_LOCKREQ couple[2];
271 	int ret;
272 
273 	dbp = dbc->dbp;
274 
275 	if (!F_ISSET(dbp, DB_AM_LOCKING)) {
276 		*lockp = LOCK_INVALID;
277 		return (0);
278 	}
279 
280 	dbc->lock.pgno = pgno;
281 
282 	/*
283 	 * If the object not currently locked, acquire the lock and return,
284 	 * otherwise, lock couple.  If we fail and it's not a system error,
285 	 * convert to EAGAIN.
286 	 */
287 	if (do_couple) {
288 		couple[0].op = DB_LOCK_GET;
289 		couple[0].obj = &dbc->lock_dbt;
290 		couple[0].mode = mode;
291 		couple[1].op = DB_LOCK_PUT;
292 		couple[1].lock = *lockp;
293 
294 		if (dbc->txn == NULL)
295 			ret = lock_vec(dbp->dbenv->lk_info,
296 			    dbc->locker, 0, couple, 2, NULL);
297 		else
298 			ret = lock_tvec(dbp->dbenv->lk_info,
299 			    dbc->txn, 0, couple, 2, NULL);
300 		if (ret != 0) {
301 			/* If we fail, discard the lock we held. */
302 			__BT_LPUT(dbc, *lockp);
303 
304 			return (ret < 0 ? EAGAIN : ret);
305 		}
306 		*lockp = couple[0].lock;
307 	} else {
308 		if (dbc->txn == NULL)
309 			ret = lock_get(dbp->dbenv->lk_info,
310 			    dbc->locker, 0, &dbc->lock_dbt, mode, lockp);
311 		else
312 			ret = lock_tget(dbp->dbenv->lk_info,
313 			    dbc->txn, 0, &dbc->lock_dbt, mode, lockp);
314 		 return (ret < 0 ? EAGAIN : ret);
315 	}
316 	return (0);
317 }
318