xref: /csrg-svn/lib/libc/db/hash/hash_buf.c (revision 56351)
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*56351Smargo static char sccsid[] = "@(#)hash_buf.c	5.10 (Berkeley) 09/29/92";
1346365Sbostic #endif /* LIBC_SCCS and not lint */
1446365Sbostic 
1550997Sbostic /*
1650997Sbostic  * PACKAGE: hash
1750997Sbostic  *
1850997Sbostic  * DESCRIPTION:
1950997Sbostic  *	Contains buffer management
2050997Sbostic  *
2150997Sbostic  * ROUTINES:
2250997Sbostic  * External
2350997Sbostic  *	__buf_init
2450997Sbostic  *	__get_buf
2550997Sbostic  *	__buf_free
2650997Sbostic  *	__reclaim_buf
2750997Sbostic  * Internal
2850997Sbostic  *	newbuf
2950997Sbostic  */
3046365Sbostic 
3146644Sbostic #include <sys/param.h>
3250997Sbostic #include <db.h>
3346365Sbostic #include <errno.h>
3446562Sbostic #include <stdio.h>
3546562Sbostic #include <stdlib.h>
3650997Sbostic #ifdef DEBUG
3750997Sbostic #include <assert.h>
3850997Sbostic #endif
3946365Sbostic #include "hash.h"
4050997Sbostic #include "page.h"
4150997Sbostic #include "extern.h"
4246365Sbostic 
4350997Sbostic static BUFHEAD *newbuf __P((u_int, BUFHEAD *));
4446365Sbostic 
4546365Sbostic /* Unlink B from its place in the lru */
4650997Sbostic #define BUF_REMOVE(B) { \
4750997Sbostic 	(B)->prev->next = (B)->next; \
4850997Sbostic 	(B)->next->prev = (B)->prev; \
4946365Sbostic }
5046365Sbostic 
5146365Sbostic /* Insert B after P */
5250997Sbostic #define BUF_INSERT(B, P) { \
5350997Sbostic 	(B)->next = (P)->next; \
5450997Sbostic 	(B)->prev = (P); \
5550997Sbostic 	(P)->next = (B); \
5650997Sbostic 	(B)->next->prev = (B); \
5746365Sbostic }
5846365Sbostic 
5946365Sbostic #define	MRU	hashp->bufhead.next
6046365Sbostic #define	LRU	hashp->bufhead.prev
6146365Sbostic 
6250997Sbostic #define MRU_INSERT(B)	BUF_INSERT((B), &hashp->bufhead)
6350997Sbostic #define LRU_INSERT(B)	BUF_INSERT((B), LRU)
6446365Sbostic 
6546365Sbostic /*
6650997Sbostic  * We are looking for a buffer with address "addr".  If prev_bp is NULL, then
6750997Sbostic  * address is a bucket index.  If prev_bp is not NULL, then it points to the
6850997Sbostic  * page previous to an overflow page that we are trying to find.
6950997Sbostic  *
7050997Sbostic  * CAVEAT:  The buffer header accessed via prev_bp's ovfl field may no longer
7150997Sbostic  * be valid.  Therefore, you must always verify that its address matches the
7250997Sbostic  * address you are seeking.
7350997Sbostic  */
7446365Sbostic extern BUFHEAD *
7550997Sbostic __get_buf(addr, prev_bp, newpage)
7650997Sbostic 	u_int addr;
7750997Sbostic 	BUFHEAD *prev_bp;
7850997Sbostic 	int newpage;	/* If prev_bp set, indicates a new overflow page. */
7946365Sbostic {
8050997Sbostic 	register BUFHEAD *bp;
8151058Sbostic 	register u_int is_disk_mask;
8251058Sbostic 	register int is_disk, segment_ndx;
8350997Sbostic 	SEGMENT segp;
8446365Sbostic 
8550997Sbostic 	is_disk = 0;
8650997Sbostic 	is_disk_mask = 0;
8750997Sbostic 	if (prev_bp) {
8850997Sbostic 		bp = prev_bp->ovfl;
8950997Sbostic 		if (!bp || (bp->addr != addr))
9050997Sbostic 			bp = NULL;
9150997Sbostic 		if (!newpage)
9250997Sbostic 			is_disk = BUF_DISK;
9350997Sbostic 	} else {
9450997Sbostic 		/* Grab buffer out of directory */
9550997Sbostic 		segment_ndx = addr & (hashp->SGSIZE - 1);
9646365Sbostic 
9750997Sbostic 		/* valid segment ensured by __call_hash() */
9850997Sbostic 		segp = hashp->dir[addr >> hashp->SSHIFT];
9946365Sbostic #ifdef DEBUG
10050997Sbostic 		assert(segp != NULL);
10146365Sbostic #endif
10250997Sbostic 		bp = PTROF(segp[segment_ndx]);
10350997Sbostic 		is_disk_mask = ISDISK(segp[segment_ndx]);
10450997Sbostic 		is_disk = is_disk_mask || !hashp->new_file;
10550997Sbostic 	}
10646365Sbostic 
10750997Sbostic 	if (!bp) {
10850997Sbostic 		bp = newbuf(addr, prev_bp);
10950997Sbostic 		if (!bp || __get_page(bp->page, addr, !prev_bp, is_disk, 0))
11050997Sbostic 			return (NULL);
11150997Sbostic 		if (!prev_bp)
11250997Sbostic 			segp[segment_ndx] =
11350997Sbostic 			    (BUFHEAD *)((u_int)bp | is_disk_mask);
11450997Sbostic 	} else {
11550997Sbostic 		BUF_REMOVE(bp);
11650997Sbostic 		MRU_INSERT(bp);
11746365Sbostic 	}
11850997Sbostic 	return (bp);
11946365Sbostic }
12046365Sbostic 
12146365Sbostic /*
12250997Sbostic  * We need a buffer for this page. Either allocate one, or evict a resident
12350997Sbostic  * one (if we have as many buffers as we're allowed) and put this one in.
12450997Sbostic  *
12550997Sbostic  * If newbuf finds an error (returning NULL), it also sets errno.
12650997Sbostic  */
12746365Sbostic static BUFHEAD *
12850997Sbostic newbuf(addr, prev_bp)
12950997Sbostic 	u_int   addr;
13050997Sbostic 	BUFHEAD *prev_bp;
13146365Sbostic {
13250997Sbostic 	register BUFHEAD *bp;		/* The buffer we're going to use */
13350997Sbostic 	register BUFHEAD *xbp;		/* Temp pointer */
13450997Sbostic 	register BUFHEAD *next_xbp;
13550997Sbostic 	SEGMENT segp;
13650997Sbostic 	int segment_ndx;
13750997Sbostic 	u_short oaddr, *shortp;
13846365Sbostic 
13950997Sbostic 	oaddr = 0;
14050997Sbostic 	bp = LRU;
14150997Sbostic 	/*
14250997Sbostic 	 * If LRU buffer is pinned, the buffer pool is too small. We need to
14350997Sbostic 	 * allocate more buffers.
14450997Sbostic 	 */
14550997Sbostic 	if (hashp->nbufs || (bp->flags & BUF_PIN)) {
14650997Sbostic 		/* Allocate a new one */
14750997Sbostic 		bp = malloc(sizeof(struct _bufhead));
14850997Sbostic 		if (!bp || !(bp->page = malloc(hashp->BSIZE)))
14950997Sbostic 			return (NULL);
150*56351Smargo 		if (hashp->nbufs)
151*56351Smargo 			hashp->nbufs--;
15250997Sbostic 	} else {
15350997Sbostic 		/* Kick someone out */
15450997Sbostic 		BUF_REMOVE(bp);
15550997Sbostic 		/*
15650997Sbostic 		 * If this is an overflow page with addr 0, it's already been
15750997Sbostic 		 * flushed back in an overflow chain and initialized.
15850997Sbostic 		 */
15950997Sbostic 		if ((bp->addr != 0) || (bp->flags & BUF_BUCKET)) {
16050997Sbostic 			/*
16150997Sbostic 			 * Set oaddr before __put_page so that you get it
16250997Sbostic 			 * before bytes are swapped.
16350997Sbostic 			 */
16450997Sbostic 			shortp = (u_short *)bp->page;
16550997Sbostic 			if (shortp[0])
16650997Sbostic 				oaddr = shortp[shortp[0] - 1];
16750997Sbostic 			if ((bp->flags & BUF_MOD) && __put_page(bp->page,
16850997Sbostic 			    bp->addr, (int)IS_BUCKET(bp->flags), 0))
16950997Sbostic 				return (NULL);
17050997Sbostic 			/*
17150997Sbostic 			 * Update the pointer to this page (i.e. invalidate it).
17250997Sbostic 			 *
17350997Sbostic 			 * If this is a new file (i.e. we created it at open
17450997Sbostic 			 * time), make sure that we mark pages which have been
17550997Sbostic 			 * written to disk so we retrieve them from disk later,
17650997Sbostic 			 * rather than allocating new pages.
17750997Sbostic 			 */
17850997Sbostic 			if (IS_BUCKET(bp->flags)) {
17950997Sbostic 				segment_ndx = bp->addr & (hashp->SGSIZE - 1);
18050997Sbostic 				segp = hashp->dir[bp->addr >> hashp->SSHIFT];
18150997Sbostic #ifdef DEBUG
18250997Sbostic 				assert(segp != NULL);
18350997Sbostic #endif
18446365Sbostic 
18550997Sbostic 				if (hashp->new_file &&
18650997Sbostic 				    ((bp->flags & BUF_MOD) ||
18750997Sbostic 				    ISDISK(segp[segment_ndx])))
18850997Sbostic 					segp[segment_ndx] = (BUFHEAD *)BUF_DISK;
18950997Sbostic 				else
19050997Sbostic 					segp[segment_ndx] = NULL;
19150997Sbostic 			}
19250997Sbostic 			/*
19350997Sbostic 			 * Since overflow pages can only be access by means of
19450997Sbostic 			 * their bucket, free overflow pages associated with
19550997Sbostic 			 * this bucket.
19650997Sbostic 			 */
19750997Sbostic 			for (xbp = bp; xbp->ovfl;) {
19850997Sbostic 				next_xbp = xbp->ovfl;
19950997Sbostic 				xbp->ovfl = 0;
20050997Sbostic 				xbp = next_xbp;
20146365Sbostic 
20250997Sbostic 				/* Check that ovfl pointer is up date. */
20350997Sbostic 				if (IS_BUCKET(xbp->flags) ||
20450997Sbostic 				    (oaddr != xbp->addr))
20550997Sbostic 					break;
20646365Sbostic 
20750997Sbostic 				shortp = (u_short *)xbp->page;
20850997Sbostic 				if (shortp[0])
20950997Sbostic 					/* set before __put_page */
21050997Sbostic 					oaddr = shortp[shortp[0] - 1];
21150997Sbostic 				if ((xbp->flags & BUF_MOD) &&
21250997Sbostic 				    __put_page(xbp->page, xbp->addr, 0, 0))
21350997Sbostic 					return (NULL);
21450997Sbostic 				xbp->addr = 0;
21550997Sbostic 				xbp->flags = 0;
21650997Sbostic 				BUF_REMOVE(xbp);
21750997Sbostic 				LRU_INSERT(xbp);
21850997Sbostic 			}
21946506Sbostic 		}
22046365Sbostic 	}
22146365Sbostic 
22250997Sbostic 	/* Now assign this buffer */
22350997Sbostic 	bp->addr = addr;
22446365Sbostic #ifdef DEBUG1
22550997Sbostic 	(void)fprintf(stderr, "NEWBUF1: %d->ovfl was %d is now %d\n",
22650997Sbostic 	    bp->addr, (bp->ovfl ? bp->ovfl->addr : 0), 0);
22746365Sbostic #endif
22850997Sbostic 	bp->ovfl = NULL;
22950997Sbostic 	if (prev_bp) {
23050997Sbostic 		/*
23150997Sbostic 		 * If prev_bp is set, this is an overflow page, hook it in to
23250997Sbostic 		 * the buffer overflow links.
23350997Sbostic 		 */
23446365Sbostic #ifdef DEBUG1
23550997Sbostic 		(void)fprintf(stderr, "NEWBUF2: %d->ovfl was %d is now %d\n",
23650997Sbostic 		    prev_bp->addr, (prev_bp->ovfl ? bp->ovfl->addr : 0),
23750997Sbostic 		    (bp ? bp->addr : 0));
23846365Sbostic #endif
23950997Sbostic 		prev_bp->ovfl = bp;
24050997Sbostic 		bp->flags = 0;
24150997Sbostic 	} else
24250997Sbostic 		bp->flags = BUF_BUCKET;
24350997Sbostic 	MRU_INSERT(bp);
24450997Sbostic 	return (bp);
24546365Sbostic }
24646365Sbostic 
24746365Sbostic extern void
24850997Sbostic __buf_init(nbytes)
24950997Sbostic 	int nbytes;
25046365Sbostic {
25150997Sbostic 	BUFHEAD *bfp;
25250997Sbostic 	int npages;
25346365Sbostic 
25450997Sbostic 	bfp = &(hashp->bufhead);
25550997Sbostic 	npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT;
25650997Sbostic 	npages = MAX(npages, MIN_BUFFERS);
25746365Sbostic 
25850997Sbostic 	hashp->nbufs = npages;
25950997Sbostic 	bfp->next = bfp;
26050997Sbostic 	bfp->prev = bfp;
26150997Sbostic 	/*
26250997Sbostic 	 * This space is calloc'd so these are already null.
26350997Sbostic 	 *
26450997Sbostic 	 * bfp->ovfl = NULL;
26550997Sbostic 	 * bfp->flags = 0;
26650997Sbostic 	 * bfp->page = NULL;
26750997Sbostic 	 * bfp->addr = 0;
26850997Sbostic 	 */
26946365Sbostic }
27046365Sbostic 
27146365Sbostic extern int
27250997Sbostic __buf_free(do_free, to_disk)
27350997Sbostic 	int do_free, to_disk;
27446365Sbostic {
27550997Sbostic 	BUFHEAD *bp;
27646365Sbostic 
27750997Sbostic 	/* Need to make sure that buffer manager has been initialized */
27850997Sbostic 	if (!LRU)
27950997Sbostic 		return (0);
28050997Sbostic 	for (bp = LRU; bp != &hashp->bufhead;) {
28150997Sbostic 		/* Check that the buffer is valid */
28250997Sbostic 		if (bp->addr || IS_BUCKET(bp->flags)) {
28350997Sbostic 			if (to_disk && (bp->flags & BUF_MOD) &&
28450997Sbostic 			    __put_page(bp->page,
28550997Sbostic 			    bp->addr, IS_BUCKET(bp->flags), 0))
28650997Sbostic 				return (-1);
28750997Sbostic 		}
28850997Sbostic 		/* Check if we are freeing stuff */
28950997Sbostic 		if (do_free) {
29050997Sbostic 			if (bp->page)
29150997Sbostic 				free(bp->page);
29250997Sbostic 			BUF_REMOVE(bp);
29350997Sbostic 			free(bp);
29450997Sbostic 			bp = LRU;
29550997Sbostic 		} else
29650997Sbostic 			bp = bp->prev;
29746365Sbostic 	}
29850997Sbostic 	return (0);
29946365Sbostic }
30046365Sbostic 
30146365Sbostic extern void
30250997Sbostic __reclaim_buf(bp)
30350997Sbostic 	BUFHEAD *bp;
30446365Sbostic {
30550997Sbostic 	bp->ovfl = 0;
30650997Sbostic 	bp->addr = 0;
30750997Sbostic 	bp->flags = 0;
30850997Sbostic 	BUF_REMOVE(bp);
30950997Sbostic 	LRU_INSERT(bp);
31046365Sbostic }
311