hash_buf.c revision 189291
1877Sache/*-
21019Sache * Copyright (c) 1990, 1993, 1994
31019Sache *	The Regents of the University of California.  All rights reserved.
4877Sache *
51019Sache * This code is derived from software contributed to Berkeley by
61019Sache * Margo Seltzer.
71019Sache *
81019Sache * Redistribution and use in source and binary forms, with or without
91019Sache * modification, are permitted provided that the following conditions
101019Sache * are met:
111019Sache * 1. Redistributions of source code must retain the above copyright
121019Sache *    notice, this list of conditions and the following disclaimer.
13877Sache * 2. Redistributions in binary form must reproduce the above copyright
141019Sache *    notice, this list of conditions and the following disclaimer in the
151019Sache *    documentation and/or other materials provided with the distribution.
161019Sache * 4. Neither the name of the University nor the names of its contributors
171019Sache *    may be used to endorse or promote products derived from this software
181019Sache *    without specific prior written permission.
191019Sache *
201019Sache * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211019Sache * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221019Sache * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231019Sache * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241019Sache * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251817Sdg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261817Sdg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27877Sache * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28877Sache * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29877Sache * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30877Sache * SUCH DAMAGE.
31877Sache */
32877Sache
33877Sache#if defined(LIBC_SCCS) && !defined(lint)
34877Sachestatic char sccsid[] = "@(#)hash_buf.c	8.5 (Berkeley) 7/15/94";
35877Sache#endif /* LIBC_SCCS and not lint */
36877Sache#include <sys/cdefs.h>
37877Sache__FBSDID("$FreeBSD: head/lib/libc/db/hash/hash_buf.c 189291 2009-03-02 23:47:18Z delphij $");
38877Sache
39877Sache/*
40877Sache * PACKAGE: hash
41877Sache *
42877Sache * DESCRIPTION:
43877Sache *	Contains buffer management
44877Sache *
45877Sache * ROUTINES:
46877Sache * External
47877Sache *	__buf_init
48877Sache *	__get_buf
49877Sache *	__buf_free
50877Sache *	__reclaim_buf
51877Sache * Internal
52877Sache *	newbuf
53877Sache */
54877Sache
55877Sache#include <sys/param.h>
56877Sache
57877Sache#include <stddef.h>
58877Sache#include <stdio.h>
59877Sache#include <stdlib.h>
60877Sache
61877Sache#ifdef DEBUG
62877Sache#include <assert.h>
63877Sache#endif
64877Sache
65877Sache#include <db.h>
66877Sache#include "hash.h"
67877Sache#include "page.h"
68877Sache#include "extern.h"
69877Sache
70877Sachestatic BUFHEAD *newbuf(HTAB *, u_int32_t, BUFHEAD *);
71877Sache
72877Sache/* Unlink B from its place in the lru */
73877Sache#define BUF_REMOVE(B) { \
74877Sache	(B)->prev->next = (B)->next; \
75877Sache	(B)->next->prev = (B)->prev; \
76877Sache}
77877Sache
78877Sache/* Insert B after P */
79877Sache#define BUF_INSERT(B, P) { \
80877Sache	(B)->next = (P)->next; \
81877Sache	(B)->prev = (P); \
82877Sache	(P)->next = (B); \
83877Sache	(B)->next->prev = (B); \
84877Sache}
85877Sache
86877Sache#define	MRU	hashp->bufhead.next
87877Sache#define	LRU	hashp->bufhead.prev
88877Sache
89877Sache#define MRU_INSERT(B)	BUF_INSERT((B), &hashp->bufhead)
90877Sache#define LRU_INSERT(B)	BUF_INSERT((B), LRU)
91877Sache
92877Sache/*
93891Sache * We are looking for a buffer with address "addr".  If prev_bp is NULL, then
94891Sache * address is a bucket index.  If prev_bp is not NULL, then it points to the
95877Sache * page previous to an overflow page that we are trying to find.
96877Sache *
97877Sache * CAVEAT:  The buffer header accessed via prev_bp's ovfl field may no longer
98877Sache * be valid.  Therefore, you must always verify that its address matches the
99877Sache * address you are seeking.
100877Sache */
101BUFHEAD *
102__get_buf(HTAB *hashp, u_int32_t addr,
103    BUFHEAD *prev_bp,	/* If prev_bp set, indicates a new overflow page. */
104    int newpage)
105{
106	BUFHEAD *bp;
107	u_int32_t is_disk_mask;
108	int is_disk, segment_ndx;
109	SEGMENT segp;
110
111	is_disk = 0;
112	is_disk_mask = 0;
113	if (prev_bp) {
114		bp = prev_bp->ovfl;
115		if (!bp || (bp->addr != addr))
116			bp = NULL;
117		if (!newpage)
118			is_disk = BUF_DISK;
119	} else {
120		/* Grab buffer out of directory */
121		segment_ndx = addr & (hashp->SGSIZE - 1);
122
123		/* valid segment ensured by __call_hash() */
124		segp = hashp->dir[addr >> hashp->SSHIFT];
125#ifdef DEBUG
126		assert(segp != NULL);
127#endif
128		bp = PTROF(segp[segment_ndx]);
129		is_disk_mask = ISDISK(segp[segment_ndx]);
130		is_disk = is_disk_mask || !hashp->new_file;
131	}
132
133	if (!bp) {
134		bp = newbuf(hashp, addr, prev_bp);
135		if (!bp ||
136		    __get_page(hashp, bp->page, addr, !prev_bp, is_disk, 0))
137			return (NULL);
138		if (!prev_bp)
139			segp[segment_ndx] =
140			    (BUFHEAD *)((ptrdiff_t)bp | is_disk_mask);
141	} else {
142		BUF_REMOVE(bp);
143		MRU_INSERT(bp);
144	}
145	return (bp);
146}
147
148/*
149 * We need a buffer for this page. Either allocate one, or evict a resident
150 * one (if we have as many buffers as we're allowed) and put this one in.
151 *
152 * If newbuf finds an error (returning NULL), it also sets errno.
153 */
154static BUFHEAD *
155newbuf(HTAB *hashp, u_int32_t addr, BUFHEAD *prev_bp)
156{
157	BUFHEAD *bp;		/* The buffer we're going to use */
158	BUFHEAD *xbp;		/* Temp pointer */
159	BUFHEAD *next_xbp;
160	SEGMENT segp;
161	int segment_ndx;
162	u_int16_t oaddr, *shortp;
163
164	oaddr = 0;
165	bp = LRU;
166	/*
167	 * If LRU buffer is pinned, the buffer pool is too small. We need to
168	 * allocate more buffers.
169	 */
170	if (hashp->nbufs || (bp->flags & BUF_PIN)) {
171		/* Allocate a new one */
172		if ((bp = (BUFHEAD *)malloc(sizeof(BUFHEAD))) == NULL)
173			return (NULL);
174#ifdef PURIFY
175		memset(bp, 0xff, sizeof(BUFHEAD));
176#endif
177		if ((bp->page = (char *)malloc(hashp->BSIZE)) == NULL) {
178			free(bp);
179			return (NULL);
180		}
181#ifdef PURIFY
182		memset(bp->page, 0xff, hashp->BSIZE);
183#endif
184		if (hashp->nbufs)
185			hashp->nbufs--;
186	} else {
187		/* Kick someone out */
188		BUF_REMOVE(bp);
189		/*
190		 * If this is an overflow page with addr 0, it's already been
191		 * flushed back in an overflow chain and initialized.
192		 */
193		if ((bp->addr != 0) || (bp->flags & BUF_BUCKET)) {
194			/*
195			 * Set oaddr before __put_page so that you get it
196			 * before bytes are swapped.
197			 */
198			shortp = (u_int16_t *)bp->page;
199			if (shortp[0])
200				oaddr = shortp[shortp[0] - 1];
201			if ((bp->flags & BUF_MOD) && __put_page(hashp, bp->page,
202			    bp->addr, (int)IS_BUCKET(bp->flags), 0))
203				return (NULL);
204			/*
205			 * Update the pointer to this page (i.e. invalidate it).
206			 *
207			 * If this is a new file (i.e. we created it at open
208			 * time), make sure that we mark pages which have been
209			 * written to disk so we retrieve them from disk later,
210			 * rather than allocating new pages.
211			 */
212			if (IS_BUCKET(bp->flags)) {
213				segment_ndx = bp->addr & (hashp->SGSIZE - 1);
214				segp = hashp->dir[bp->addr >> hashp->SSHIFT];
215#ifdef DEBUG
216				assert(segp != NULL);
217#endif
218
219				if (hashp->new_file &&
220				    ((bp->flags & BUF_MOD) ||
221				    ISDISK(segp[segment_ndx])))
222					segp[segment_ndx] = (BUFHEAD *)BUF_DISK;
223				else
224					segp[segment_ndx] = NULL;
225			}
226			/*
227			 * Since overflow pages can only be access by means of
228			 * their bucket, free overflow pages associated with
229			 * this bucket.
230			 */
231			for (xbp = bp; xbp->ovfl;) {
232				next_xbp = xbp->ovfl;
233				xbp->ovfl = 0;
234				xbp = next_xbp;
235
236				/* Check that ovfl pointer is up date. */
237				if (IS_BUCKET(xbp->flags) ||
238				    (oaddr != xbp->addr))
239					break;
240
241				shortp = (u_int16_t *)xbp->page;
242				if (shortp[0])
243					/* set before __put_page */
244					oaddr = shortp[shortp[0] - 1];
245				if ((xbp->flags & BUF_MOD) && __put_page(hashp,
246				    xbp->page, xbp->addr, 0, 0))
247					return (NULL);
248				xbp->addr = 0;
249				xbp->flags = 0;
250				BUF_REMOVE(xbp);
251				LRU_INSERT(xbp);
252			}
253		}
254	}
255
256	/* Now assign this buffer */
257	bp->addr = addr;
258#ifdef DEBUG1
259	(void)fprintf(stderr, "NEWBUF1: %d->ovfl was %d is now %d\n",
260	    bp->addr, (bp->ovfl ? bp->ovfl->addr : 0), 0);
261#endif
262	bp->ovfl = NULL;
263	if (prev_bp) {
264		/*
265		 * If prev_bp is set, this is an overflow page, hook it in to
266		 * the buffer overflow links.
267		 */
268#ifdef DEBUG1
269		(void)fprintf(stderr, "NEWBUF2: %d->ovfl was %d is now %d\n",
270		    prev_bp->addr, (prev_bp->ovfl ? bp->ovfl->addr : 0),
271		    (bp ? bp->addr : 0));
272#endif
273		prev_bp->ovfl = bp;
274		bp->flags = 0;
275	} else
276		bp->flags = BUF_BUCKET;
277	MRU_INSERT(bp);
278	return (bp);
279}
280
281void
282__buf_init(HTAB *hashp, int nbytes)
283{
284	BUFHEAD *bfp;
285	int npages;
286
287	bfp = &(hashp->bufhead);
288	npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT;
289	npages = MAX(npages, MIN_BUFFERS);
290
291	hashp->nbufs = npages;
292	bfp->next = bfp;
293	bfp->prev = bfp;
294	/*
295	 * This space is calloc'd so these are already null.
296	 *
297	 * bfp->ovfl = NULL;
298	 * bfp->flags = 0;
299	 * bfp->page = NULL;
300	 * bfp->addr = 0;
301	 */
302}
303
304int
305__buf_free(HTAB *hashp, int do_free, int to_disk)
306{
307	BUFHEAD *bp;
308
309	/* Need to make sure that buffer manager has been initialized */
310	if (!LRU)
311		return (0);
312	for (bp = LRU; bp != &hashp->bufhead;) {
313		/* Check that the buffer is valid */
314		if (bp->addr || IS_BUCKET(bp->flags)) {
315			if (to_disk && (bp->flags & BUF_MOD) &&
316			    __put_page(hashp, bp->page,
317			    bp->addr, IS_BUCKET(bp->flags), 0))
318				return (-1);
319		}
320		/* Check if we are freeing stuff */
321		if (do_free) {
322			if (bp->page)
323				free(bp->page);
324			BUF_REMOVE(bp);
325			free(bp);
326			bp = LRU;
327		} else
328			bp = bp->prev;
329	}
330	return (0);
331}
332
333void
334__reclaim_buf(HTAB *hashp, BUFHEAD *bp)
335{
336	bp->ovfl = 0;
337	bp->addr = 0;
338	bp->flags = 0;
339	BUF_REMOVE(bp);
340	LRU_INSERT(bp);
341}
342