xref: /csrg-svn/lib/libc/db/hash/hash_buf.c (revision 50997)
146365Sbostic /*-
246365Sbostic  * Copyright (c) 1990 The Regents of the University of California.
346365Sbostic  * All rights reserved.
446365Sbostic  *
546365Sbostic  * This code is derived from software contributed to Berkeley by
646365Sbostic  * Margo Seltzer.
746365Sbostic  *
846365Sbostic  * %sccs.include.redist.c%
946365Sbostic  */
1046365Sbostic 
1146365Sbostic #if defined(LIBC_SCCS) && !defined(lint)
12*50997Sbostic static char sccsid[] = "@(#)hash_buf.c	5.8 (Berkeley) 09/04/91";
1346365Sbostic #endif /* LIBC_SCCS and not lint */
1446365Sbostic 
15*50997Sbostic /*
16*50997Sbostic  * PACKAGE: hash
17*50997Sbostic  *
18*50997Sbostic  * DESCRIPTION:
19*50997Sbostic  *	Contains buffer management
20*50997Sbostic  *
21*50997Sbostic  * ROUTINES:
22*50997Sbostic  * External
23*50997Sbostic  *	__buf_init
24*50997Sbostic  *	__get_buf
25*50997Sbostic  *	__buf_free
26*50997Sbostic  *	__reclaim_buf
27*50997Sbostic  * Internal
28*50997Sbostic  *	newbuf
29*50997Sbostic  */
3046365Sbostic 
3146644Sbostic #include <sys/param.h>
32*50997Sbostic #include <db.h>
3346365Sbostic #include <errno.h>
3446562Sbostic #include <stdio.h>
3546562Sbostic #include <stdlib.h>
36*50997Sbostic #ifdef DEBUG
37*50997Sbostic #include <assert.h>
38*50997Sbostic #endif
3946365Sbostic #include "hash.h"
40*50997Sbostic #include "page.h"
41*50997Sbostic #include "extern.h"
4246365Sbostic 
43*50997Sbostic static BUFHEAD *newbuf __P((u_int, BUFHEAD *));
4446365Sbostic 
4546365Sbostic /* Unlink B from its place in the lru */
46*50997Sbostic #define BUF_REMOVE(B) { \
47*50997Sbostic 	(B)->prev->next = (B)->next; \
48*50997Sbostic 	(B)->next->prev = (B)->prev; \
4946365Sbostic }
5046365Sbostic 
5146365Sbostic /* Insert B after P */
52*50997Sbostic #define BUF_INSERT(B, P) { \
53*50997Sbostic 	(B)->next = (P)->next; \
54*50997Sbostic 	(B)->prev = (P); \
55*50997Sbostic 	(P)->next = (B); \
56*50997Sbostic 	(B)->next->prev = (B); \
5746365Sbostic }
5846365Sbostic 
5946365Sbostic #define	MRU	hashp->bufhead.next
6046365Sbostic #define	LRU	hashp->bufhead.prev
6146365Sbostic 
62*50997Sbostic #define MRU_INSERT(B)	BUF_INSERT((B), &hashp->bufhead)
63*50997Sbostic #define LRU_INSERT(B)	BUF_INSERT((B), LRU)
6446365Sbostic 
6546365Sbostic /*
66*50997Sbostic  * We are looking for a buffer with address "addr".  If prev_bp is NULL, then
67*50997Sbostic  * address is a bucket index.  If prev_bp is not NULL, then it points to the
68*50997Sbostic  * page previous to an overflow page that we are trying to find.
69*50997Sbostic  *
70*50997Sbostic  * CAVEAT:  The buffer header accessed via prev_bp's ovfl field may no longer
71*50997Sbostic  * be valid.  Therefore, you must always verify that its address matches the
72*50997Sbostic  * address you are seeking.
73*50997Sbostic  */
7446365Sbostic extern BUFHEAD *
75*50997Sbostic __get_buf(addr, prev_bp, newpage)
76*50997Sbostic 	u_int addr;
77*50997Sbostic 	BUFHEAD *prev_bp;
78*50997Sbostic 	int newpage;	/* If prev_bp set, indicates a new overflow page. */
7946365Sbostic {
80*50997Sbostic 	register BUFHEAD *bp;
81*50997Sbostic 	register u_int is_disk, is_disk_mask;
82*50997Sbostic 	register int segment_ndx;
83*50997Sbostic 	SEGMENT segp;
8446365Sbostic 
85*50997Sbostic 	is_disk = 0;
86*50997Sbostic 	is_disk_mask = 0;
87*50997Sbostic 	if (prev_bp) {
88*50997Sbostic 		bp = prev_bp->ovfl;
89*50997Sbostic 		if (!bp || (bp->addr != addr))
90*50997Sbostic 			bp = NULL;
91*50997Sbostic 		if (!newpage)
92*50997Sbostic 			is_disk = BUF_DISK;
93*50997Sbostic 	} else {
94*50997Sbostic 		/* Grab buffer out of directory */
95*50997Sbostic 		segment_ndx = addr & (hashp->SGSIZE - 1);
9646365Sbostic 
97*50997Sbostic 		/* valid segment ensured by __call_hash() */
98*50997Sbostic 		segp = hashp->dir[addr >> hashp->SSHIFT];
9946365Sbostic #ifdef DEBUG
100*50997Sbostic 		assert(segp != NULL);
10146365Sbostic #endif
102*50997Sbostic 		bp = PTROF(segp[segment_ndx]);
103*50997Sbostic 		is_disk_mask = ISDISK(segp[segment_ndx]);
104*50997Sbostic 		is_disk = is_disk_mask || !hashp->new_file;
105*50997Sbostic 	}
10646365Sbostic 
107*50997Sbostic 	if (!bp) {
108*50997Sbostic 		bp = newbuf(addr, prev_bp);
109*50997Sbostic 		if (!bp || __get_page(bp->page, addr, !prev_bp, is_disk, 0))
110*50997Sbostic 			return (NULL);
111*50997Sbostic 		if (!prev_bp)
112*50997Sbostic 			segp[segment_ndx] =
113*50997Sbostic 			    (BUFHEAD *)((u_int)bp | is_disk_mask);
114*50997Sbostic 	} else {
115*50997Sbostic 		BUF_REMOVE(bp);
116*50997Sbostic 		MRU_INSERT(bp);
11746365Sbostic 	}
118*50997Sbostic 	return (bp);
11946365Sbostic }
12046365Sbostic 
12146365Sbostic /*
122*50997Sbostic  * We need a buffer for this page. Either allocate one, or evict a resident
123*50997Sbostic  * one (if we have as many buffers as we're allowed) and put this one in.
124*50997Sbostic  *
125*50997Sbostic  * If newbuf finds an error (returning NULL), it also sets errno.
126*50997Sbostic  */
12746365Sbostic static BUFHEAD *
128*50997Sbostic newbuf(addr, prev_bp)
129*50997Sbostic 	u_int   addr;
130*50997Sbostic 	BUFHEAD *prev_bp;
13146365Sbostic {
132*50997Sbostic 	register BUFHEAD *bp;		/* The buffer we're going to use */
133*50997Sbostic 	register BUFHEAD *xbp;		/* Temp pointer */
134*50997Sbostic 	register BUFHEAD *next_xbp;
135*50997Sbostic 	SEGMENT segp;
136*50997Sbostic 	int segment_ndx;
137*50997Sbostic 	u_short oaddr, *shortp;
13846365Sbostic 
139*50997Sbostic 	oaddr = 0;
140*50997Sbostic 	bp = LRU;
141*50997Sbostic 	/*
142*50997Sbostic 	 * If LRU buffer is pinned, the buffer pool is too small. We need to
143*50997Sbostic 	 * allocate more buffers.
144*50997Sbostic 	 */
145*50997Sbostic 	if (hashp->nbufs || (bp->flags & BUF_PIN)) {
146*50997Sbostic 		/* Allocate a new one */
147*50997Sbostic 		bp = malloc(sizeof(struct _bufhead));
148*50997Sbostic 		if (!bp || !(bp->page = malloc(hashp->BSIZE)))
149*50997Sbostic 			return (NULL);
150*50997Sbostic 		hashp->nbufs--;
151*50997Sbostic 	} else {
152*50997Sbostic 		/* Kick someone out */
153*50997Sbostic 		BUF_REMOVE(bp);
154*50997Sbostic 		/*
155*50997Sbostic 		 * If this is an overflow page with addr 0, it's already been
156*50997Sbostic 		 * flushed back in an overflow chain and initialized.
157*50997Sbostic 		 */
158*50997Sbostic 		if ((bp->addr != 0) || (bp->flags & BUF_BUCKET)) {
159*50997Sbostic 			/*
160*50997Sbostic 			 * Set oaddr before __put_page so that you get it
161*50997Sbostic 			 * before bytes are swapped.
162*50997Sbostic 			 */
163*50997Sbostic 			shortp = (u_short *)bp->page;
164*50997Sbostic 			if (shortp[0])
165*50997Sbostic 				oaddr = shortp[shortp[0] - 1];
166*50997Sbostic 			if ((bp->flags & BUF_MOD) && __put_page(bp->page,
167*50997Sbostic 			    bp->addr, (int)IS_BUCKET(bp->flags), 0))
168*50997Sbostic 				return (NULL);
169*50997Sbostic 			/*
170*50997Sbostic 			 * Update the pointer to this page (i.e. invalidate it).
171*50997Sbostic 			 *
172*50997Sbostic 			 * If this is a new file (i.e. we created it at open
173*50997Sbostic 			 * time), make sure that we mark pages which have been
174*50997Sbostic 			 * written to disk so we retrieve them from disk later,
175*50997Sbostic 			 * rather than allocating new pages.
176*50997Sbostic 			 */
177*50997Sbostic 			if (IS_BUCKET(bp->flags)) {
178*50997Sbostic 				segment_ndx = bp->addr & (hashp->SGSIZE - 1);
179*50997Sbostic 				segp = hashp->dir[bp->addr >> hashp->SSHIFT];
180*50997Sbostic #ifdef DEBUG
181*50997Sbostic 				assert(segp != NULL);
182*50997Sbostic #endif
18346365Sbostic 
184*50997Sbostic 				if (hashp->new_file &&
185*50997Sbostic 				    ((bp->flags & BUF_MOD) ||
186*50997Sbostic 				    ISDISK(segp[segment_ndx])))
187*50997Sbostic 					segp[segment_ndx] = (BUFHEAD *)BUF_DISK;
188*50997Sbostic 				else
189*50997Sbostic 					segp[segment_ndx] = NULL;
190*50997Sbostic 			}
191*50997Sbostic 			/*
192*50997Sbostic 			 * Since overflow pages can only be access by means of
193*50997Sbostic 			 * their bucket, free overflow pages associated with
194*50997Sbostic 			 * this bucket.
195*50997Sbostic 			 */
196*50997Sbostic 			for (xbp = bp; xbp->ovfl;) {
197*50997Sbostic 				next_xbp = xbp->ovfl;
198*50997Sbostic 				xbp->ovfl = 0;
199*50997Sbostic 				xbp = next_xbp;
20046365Sbostic 
201*50997Sbostic 				/* Check that ovfl pointer is up date. */
202*50997Sbostic 				if (IS_BUCKET(xbp->flags) ||
203*50997Sbostic 				    (oaddr != xbp->addr))
204*50997Sbostic 					break;
20546365Sbostic 
206*50997Sbostic 				shortp = (u_short *)xbp->page;
207*50997Sbostic 				if (shortp[0])
208*50997Sbostic 					/* set before __put_page */
209*50997Sbostic 					oaddr = shortp[shortp[0] - 1];
210*50997Sbostic 				if ((xbp->flags & BUF_MOD) &&
211*50997Sbostic 				    __put_page(xbp->page, xbp->addr, 0, 0))
212*50997Sbostic 					return (NULL);
213*50997Sbostic 				xbp->addr = 0;
214*50997Sbostic 				xbp->flags = 0;
215*50997Sbostic 				BUF_REMOVE(xbp);
216*50997Sbostic 				LRU_INSERT(xbp);
217*50997Sbostic 			}
21846506Sbostic 		}
21946365Sbostic 	}
22046365Sbostic 
221*50997Sbostic 	/* Now assign this buffer */
222*50997Sbostic 	bp->addr = addr;
22346365Sbostic #ifdef DEBUG1
224*50997Sbostic 	(void)fprintf(stderr, "NEWBUF1: %d->ovfl was %d is now %d\n",
225*50997Sbostic 	    bp->addr, (bp->ovfl ? bp->ovfl->addr : 0), 0);
22646365Sbostic #endif
227*50997Sbostic 	bp->ovfl = NULL;
228*50997Sbostic 	if (prev_bp) {
229*50997Sbostic 		/*
230*50997Sbostic 		 * If prev_bp is set, this is an overflow page, hook it in to
231*50997Sbostic 		 * the buffer overflow links.
232*50997Sbostic 		 */
23346365Sbostic #ifdef DEBUG1
234*50997Sbostic 		(void)fprintf(stderr, "NEWBUF2: %d->ovfl was %d is now %d\n",
235*50997Sbostic 		    prev_bp->addr, (prev_bp->ovfl ? bp->ovfl->addr : 0),
236*50997Sbostic 		    (bp ? bp->addr : 0));
23746365Sbostic #endif
238*50997Sbostic 		prev_bp->ovfl = bp;
239*50997Sbostic 		bp->flags = 0;
240*50997Sbostic 	} else
241*50997Sbostic 		bp->flags = BUF_BUCKET;
242*50997Sbostic 	MRU_INSERT(bp);
243*50997Sbostic 	return (bp);
24446365Sbostic }
24546365Sbostic 
24646365Sbostic extern void
247*50997Sbostic __buf_init(nbytes)
248*50997Sbostic 	int nbytes;
24946365Sbostic {
250*50997Sbostic 	BUFHEAD *bfp;
251*50997Sbostic 	int npages;
25246365Sbostic 
253*50997Sbostic 	bfp = &(hashp->bufhead);
254*50997Sbostic 	npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT;
255*50997Sbostic 	npages = MAX(npages, MIN_BUFFERS);
25646365Sbostic 
257*50997Sbostic 	hashp->nbufs = npages;
258*50997Sbostic 	bfp->next = bfp;
259*50997Sbostic 	bfp->prev = bfp;
260*50997Sbostic 	/*
261*50997Sbostic 	 * This space is calloc'd so these are already null.
262*50997Sbostic 	 *
263*50997Sbostic 	 * bfp->ovfl = NULL;
264*50997Sbostic 	 * bfp->flags = 0;
265*50997Sbostic 	 * bfp->page = NULL;
266*50997Sbostic 	 * bfp->addr = 0;
267*50997Sbostic 	 */
26846365Sbostic }
26946365Sbostic 
27046365Sbostic extern int
271*50997Sbostic __buf_free(do_free, to_disk)
272*50997Sbostic 	int do_free, to_disk;
27346365Sbostic {
274*50997Sbostic 	BUFHEAD *bp;
27546365Sbostic 
276*50997Sbostic 	/* Need to make sure that buffer manager has been initialized */
277*50997Sbostic 	if (!LRU)
278*50997Sbostic 		return (0);
279*50997Sbostic 	for (bp = LRU; bp != &hashp->bufhead;) {
280*50997Sbostic 		/* Check that the buffer is valid */
281*50997Sbostic 		if (bp->addr || IS_BUCKET(bp->flags)) {
282*50997Sbostic 			if (to_disk && (bp->flags & BUF_MOD) &&
283*50997Sbostic 			    __put_page(bp->page,
284*50997Sbostic 			    bp->addr, IS_BUCKET(bp->flags), 0))
285*50997Sbostic 				return (-1);
286*50997Sbostic 		}
287*50997Sbostic 		/* Check if we are freeing stuff */
288*50997Sbostic 		if (do_free) {
289*50997Sbostic 			if (bp->page)
290*50997Sbostic 				free(bp->page);
291*50997Sbostic 			BUF_REMOVE(bp);
292*50997Sbostic 			free(bp);
293*50997Sbostic 			bp = LRU;
294*50997Sbostic 		} else
295*50997Sbostic 			bp = bp->prev;
29646365Sbostic 	}
297*50997Sbostic 	return (0);
29846365Sbostic }
29946365Sbostic 
30046365Sbostic extern void
301*50997Sbostic __reclaim_buf(bp)
302*50997Sbostic 	BUFHEAD *bp;
30346365Sbostic {
304*50997Sbostic 	bp->ovfl = 0;
305*50997Sbostic 	bp->addr = 0;
306*50997Sbostic 	bp->flags = 0;
307*50997Sbostic 	BUF_REMOVE(bp);
308*50997Sbostic 	LRU_INSERT(bp);
30946365Sbostic }
310