xref: /netbsd-src/lib/libc/db/hash/hash_buf.c (revision 5f7096188587a2c7c95fa3c69b78e1ec9c7923d0)
1 /*-
2  * Copyright (c) 1990, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Margo Seltzer.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #if defined(LIBC_SCCS) && !defined(lint)
38 /*static char *sccsid = "from: @(#)hash_buf.c	8.1 (Berkeley) 6/4/93";*/
39 static char *rcsid = "$Id: hash_buf.c,v 1.3 1993/08/26 00:43:42 jtc Exp $";
40 #endif /* LIBC_SCCS and not lint */
41 
42 /*
43  * PACKAGE: hash
44  *
45  * DESCRIPTION:
46  *	Contains buffer management
47  *
48  * ROUTINES:
49  * External
50  *	__buf_init
51  *	__get_buf
52  *	__buf_free
53  *	__reclaim_buf
54  * Internal
55  *	newbuf
56  */
57 
58 #include <sys/param.h>
59 
60 #include <errno.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #ifdef DEBUG
64 #include <assert.h>
65 #endif
66 
67 #include <db.h>
68 #include "hash.h"
69 #include "page.h"
70 #include "extern.h"
71 
72 static BUFHEAD *newbuf __P((HTAB *, u_int, BUFHEAD *));
73 
74 /* Unlink B from its place in the lru */
75 #define BUF_REMOVE(B) { \
76 	(B)->prev->next = (B)->next; \
77 	(B)->next->prev = (B)->prev; \
78 }
79 
80 /* Insert B after P */
81 #define BUF_INSERT(B, P) { \
82 	(B)->next = (P)->next; \
83 	(B)->prev = (P); \
84 	(P)->next = (B); \
85 	(B)->next->prev = (B); \
86 }
87 
88 #define	MRU	hashp->bufhead.next
89 #define	LRU	hashp->bufhead.prev
90 
91 #define MRU_INSERT(B)	BUF_INSERT((B), &hashp->bufhead)
92 #define LRU_INSERT(B)	BUF_INSERT((B), LRU)
93 
94 /*
95  * We are looking for a buffer with address "addr".  If prev_bp is NULL, then
96  * address is a bucket index.  If prev_bp is not NULL, then it points to the
97  * page previous to an overflow page that we are trying to find.
98  *
99  * CAVEAT:  The buffer header accessed via prev_bp's ovfl field may no longer
100  * be valid.  Therefore, you must always verify that its address matches the
101  * address you are seeking.
102  */
103 extern BUFHEAD *
104 __get_buf(hashp, addr, prev_bp, newpage)
105 	HTAB *hashp;
106 	u_int addr;
107 	BUFHEAD *prev_bp;
108 	int newpage;	/* If prev_bp set, indicates a new overflow page. */
109 {
110 	register BUFHEAD *bp;
111 	register u_int is_disk_mask;
112 	register int is_disk, segment_ndx;
113 	SEGMENT segp;
114 
115 	is_disk = 0;
116 	is_disk_mask = 0;
117 	if (prev_bp) {
118 		bp = prev_bp->ovfl;
119 		if (!bp || (bp->addr != addr))
120 			bp = NULL;
121 		if (!newpage)
122 			is_disk = BUF_DISK;
123 	} else {
124 		/* Grab buffer out of directory */
125 		segment_ndx = addr & (hashp->SGSIZE - 1);
126 
127 		/* valid segment ensured by __call_hash() */
128 		segp = hashp->dir[addr >> hashp->SSHIFT];
129 #ifdef DEBUG
130 		assert(segp != NULL);
131 #endif
132 		bp = PTROF(segp[segment_ndx]);
133 		is_disk_mask = ISDISK(segp[segment_ndx]);
134 		is_disk = is_disk_mask || !hashp->new_file;
135 	}
136 
137 	if (!bp) {
138 		bp = newbuf(hashp, addr, prev_bp);
139 		if (!bp ||
140 		    __get_page(hashp, bp->page, addr, !prev_bp, is_disk, 0))
141 			return (NULL);
142 		if (!prev_bp)
143 			segp[segment_ndx] =
144 			    (BUFHEAD *)((u_int)bp | is_disk_mask);
145 	} else {
146 		BUF_REMOVE(bp);
147 		MRU_INSERT(bp);
148 	}
149 	return (bp);
150 }
151 
152 /*
153  * We need a buffer for this page. Either allocate one, or evict a resident
154  * one (if we have as many buffers as we're allowed) and put this one in.
155  *
156  * If newbuf finds an error (returning NULL), it also sets errno.
157  */
158 static BUFHEAD *
159 newbuf(hashp, addr, prev_bp)
160 	HTAB *hashp;
161 	u_int addr;
162 	BUFHEAD *prev_bp;
163 {
164 	register BUFHEAD *bp;		/* The buffer we're going to use */
165 	register BUFHEAD *xbp;		/* Temp pointer */
166 	register BUFHEAD *next_xbp;
167 	SEGMENT segp;
168 	int segment_ndx;
169 	u_short oaddr, *shortp;
170 
171 	oaddr = 0;
172 	bp = LRU;
173 	/*
174 	 * If LRU buffer is pinned, the buffer pool is too small. We need to
175 	 * allocate more buffers.
176 	 */
177 	if (hashp->nbufs || (bp->flags & BUF_PIN)) {
178 		/* Allocate a new one */
179 		bp = malloc(sizeof(struct _bufhead));
180 		if (!bp || !(bp->page = malloc(hashp->BSIZE)))
181 			return (NULL);
182 		if (hashp->nbufs)
183 			hashp->nbufs--;
184 	} else {
185 		/* Kick someone out */
186 		BUF_REMOVE(bp);
187 		/*
188 		 * If this is an overflow page with addr 0, it's already been
189 		 * flushed back in an overflow chain and initialized.
190 		 */
191 		if ((bp->addr != 0) || (bp->flags & BUF_BUCKET)) {
192 			/*
193 			 * Set oaddr before __put_page so that you get it
194 			 * before bytes are swapped.
195 			 */
196 			shortp = (u_short *)bp->page;
197 			if (shortp[0])
198 				oaddr = shortp[shortp[0] - 1];
199 			if ((bp->flags & BUF_MOD) && __put_page(hashp, bp->page,
200 			    bp->addr, (int)IS_BUCKET(bp->flags), 0))
201 				return (NULL);
202 			/*
203 			 * Update the pointer to this page (i.e. invalidate it).
204 			 *
205 			 * If this is a new file (i.e. we created it at open
206 			 * time), make sure that we mark pages which have been
207 			 * written to disk so we retrieve them from disk later,
208 			 * rather than allocating new pages.
209 			 */
210 			if (IS_BUCKET(bp->flags)) {
211 				segment_ndx = bp->addr & (hashp->SGSIZE - 1);
212 				segp = hashp->dir[bp->addr >> hashp->SSHIFT];
213 #ifdef DEBUG
214 				assert(segp != NULL);
215 #endif
216 
217 				if (hashp->new_file &&
218 				    ((bp->flags & BUF_MOD) ||
219 				    ISDISK(segp[segment_ndx])))
220 					segp[segment_ndx] = (BUFHEAD *)BUF_DISK;
221 				else
222 					segp[segment_ndx] = NULL;
223 			}
224 			/*
225 			 * Since overflow pages can only be access by means of
226 			 * their bucket, free overflow pages associated with
227 			 * this bucket.
228 			 */
229 			for (xbp = bp; xbp->ovfl;) {
230 				next_xbp = xbp->ovfl;
231 				xbp->ovfl = 0;
232 				xbp = next_xbp;
233 
234 				/* Check that ovfl pointer is up date. */
235 				if (IS_BUCKET(xbp->flags) ||
236 				    (oaddr != xbp->addr))
237 					break;
238 
239 				shortp = (u_short *)xbp->page;
240 				if (shortp[0])
241 					/* set before __put_page */
242 					oaddr = shortp[shortp[0] - 1];
243 				if ((xbp->flags & BUF_MOD) && __put_page(hashp,
244 				    xbp->page, xbp->addr, 0, 0))
245 					return (NULL);
246 				xbp->addr = 0;
247 				xbp->flags = 0;
248 				BUF_REMOVE(xbp);
249 				LRU_INSERT(xbp);
250 			}
251 		}
252 	}
253 
254 	/* Now assign this buffer */
255 	bp->addr = addr;
256 #ifdef DEBUG1
257 	(void)fprintf(stderr, "NEWBUF1: %d->ovfl was %d is now %d\n",
258 	    bp->addr, (bp->ovfl ? bp->ovfl->addr : 0), 0);
259 #endif
260 	bp->ovfl = NULL;
261 	if (prev_bp) {
262 		/*
263 		 * If prev_bp is set, this is an overflow page, hook it in to
264 		 * the buffer overflow links.
265 		 */
266 #ifdef DEBUG1
267 		(void)fprintf(stderr, "NEWBUF2: %d->ovfl was %d is now %d\n",
268 		    prev_bp->addr, (prev_bp->ovfl ? bp->ovfl->addr : 0),
269 		    (bp ? bp->addr : 0));
270 #endif
271 		prev_bp->ovfl = bp;
272 		bp->flags = 0;
273 	} else
274 		bp->flags = BUF_BUCKET;
275 	MRU_INSERT(bp);
276 	return (bp);
277 }
278 
279 extern void
280 __buf_init(hashp, nbytes)
281 	HTAB *hashp;
282 	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 
304 extern int
305 __buf_free(hashp, do_free, to_disk)
306 	HTAB *hashp;
307 	int do_free, to_disk;
308 {
309 	BUFHEAD *bp;
310 
311 	/* Need to make sure that buffer manager has been initialized */
312 	if (!LRU)
313 		return (0);
314 	for (bp = LRU; bp != &hashp->bufhead;) {
315 		/* Check that the buffer is valid */
316 		if (bp->addr || IS_BUCKET(bp->flags)) {
317 			if (to_disk && (bp->flags & BUF_MOD) &&
318 			    __put_page(hashp, bp->page,
319 			    bp->addr, IS_BUCKET(bp->flags), 0))
320 				return (-1);
321 		}
322 		/* Check if we are freeing stuff */
323 		if (do_free) {
324 			if (bp->page)
325 				free(bp->page);
326 			BUF_REMOVE(bp);
327 			free(bp);
328 			bp = LRU;
329 		} else
330 			bp = bp->prev;
331 	}
332 	return (0);
333 }
334 
335 extern void
336 __reclaim_buf(hashp, bp)
337 	HTAB *hashp;
338 	BUFHEAD *bp;
339 {
340 	bp->ovfl = 0;
341 	bp->addr = 0;
342 	bp->flags = 0;
343 	BUF_REMOVE(bp);
344 	LRU_INSERT(bp);
345 }
346