hash_buf.c revision 1.2 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.2 1993/08/01 18:43:30 mycroft 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