xref: /csrg-svn/lib/libc/db/hash/hash_buf.c (revision 57932)
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*57932Sbostic static char sccsid[] = "@(#)hash_buf.c	5.12 (Berkeley) 02/11/93";
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>
3257586Sbostic 
3346365Sbostic #include <errno.h>
3446562Sbostic #include <stdio.h>
3546562Sbostic #include <stdlib.h>
3650997Sbostic #ifdef DEBUG
3750997Sbostic #include <assert.h>
3850997Sbostic #endif
3957586Sbostic 
40*57932Sbostic #include <db.h>
4146365Sbostic #include "hash.h"
4250997Sbostic #include "page.h"
4350997Sbostic #include "extern.h"
4446365Sbostic 
4557586Sbostic static BUFHEAD *newbuf __P((HTAB *, u_int, BUFHEAD *));
4646365Sbostic 
4746365Sbostic /* Unlink B from its place in the lru */
4850997Sbostic #define BUF_REMOVE(B) { \
4950997Sbostic 	(B)->prev->next = (B)->next; \
5050997Sbostic 	(B)->next->prev = (B)->prev; \
5146365Sbostic }
5246365Sbostic 
5346365Sbostic /* Insert B after P */
5450997Sbostic #define BUF_INSERT(B, P) { \
5550997Sbostic 	(B)->next = (P)->next; \
5650997Sbostic 	(B)->prev = (P); \
5750997Sbostic 	(P)->next = (B); \
5850997Sbostic 	(B)->next->prev = (B); \
5946365Sbostic }
6046365Sbostic 
6146365Sbostic #define	MRU	hashp->bufhead.next
6246365Sbostic #define	LRU	hashp->bufhead.prev
6346365Sbostic 
6450997Sbostic #define MRU_INSERT(B)	BUF_INSERT((B), &hashp->bufhead)
6550997Sbostic #define LRU_INSERT(B)	BUF_INSERT((B), LRU)
6646365Sbostic 
6746365Sbostic /*
6850997Sbostic  * We are looking for a buffer with address "addr".  If prev_bp is NULL, then
6950997Sbostic  * address is a bucket index.  If prev_bp is not NULL, then it points to the
7050997Sbostic  * page previous to an overflow page that we are trying to find.
7150997Sbostic  *
7250997Sbostic  * CAVEAT:  The buffer header accessed via prev_bp's ovfl field may no longer
7350997Sbostic  * be valid.  Therefore, you must always verify that its address matches the
7450997Sbostic  * address you are seeking.
7550997Sbostic  */
7646365Sbostic extern BUFHEAD *
7757586Sbostic __get_buf(hashp, addr, prev_bp, newpage)
7857586Sbostic 	HTAB *hashp;
7950997Sbostic 	u_int addr;
8050997Sbostic 	BUFHEAD *prev_bp;
8150997Sbostic 	int newpage;	/* If prev_bp set, indicates a new overflow page. */
8246365Sbostic {
8350997Sbostic 	register BUFHEAD *bp;
8451058Sbostic 	register u_int is_disk_mask;
8551058Sbostic 	register int is_disk, segment_ndx;
8650997Sbostic 	SEGMENT segp;
8746365Sbostic 
8850997Sbostic 	is_disk = 0;
8950997Sbostic 	is_disk_mask = 0;
9050997Sbostic 	if (prev_bp) {
9150997Sbostic 		bp = prev_bp->ovfl;
9250997Sbostic 		if (!bp || (bp->addr != addr))
9350997Sbostic 			bp = NULL;
9450997Sbostic 		if (!newpage)
9550997Sbostic 			is_disk = BUF_DISK;
9650997Sbostic 	} else {
9750997Sbostic 		/* Grab buffer out of directory */
9850997Sbostic 		segment_ndx = addr & (hashp->SGSIZE - 1);
9946365Sbostic 
10050997Sbostic 		/* valid segment ensured by __call_hash() */
10150997Sbostic 		segp = hashp->dir[addr >> hashp->SSHIFT];
10246365Sbostic #ifdef DEBUG
10350997Sbostic 		assert(segp != NULL);
10446365Sbostic #endif
10550997Sbostic 		bp = PTROF(segp[segment_ndx]);
10650997Sbostic 		is_disk_mask = ISDISK(segp[segment_ndx]);
10750997Sbostic 		is_disk = is_disk_mask || !hashp->new_file;
10850997Sbostic 	}
10946365Sbostic 
11050997Sbostic 	if (!bp) {
11157586Sbostic 		bp = newbuf(hashp, addr, prev_bp);
11257586Sbostic 		if (!bp ||
11357586Sbostic 		    __get_page(hashp, bp->page, addr, !prev_bp, is_disk, 0))
11450997Sbostic 			return (NULL);
11550997Sbostic 		if (!prev_bp)
11650997Sbostic 			segp[segment_ndx] =
11750997Sbostic 			    (BUFHEAD *)((u_int)bp | is_disk_mask);
11850997Sbostic 	} else {
11950997Sbostic 		BUF_REMOVE(bp);
12050997Sbostic 		MRU_INSERT(bp);
12146365Sbostic 	}
12250997Sbostic 	return (bp);
12346365Sbostic }
12446365Sbostic 
12546365Sbostic /*
12650997Sbostic  * We need a buffer for this page. Either allocate one, or evict a resident
12750997Sbostic  * one (if we have as many buffers as we're allowed) and put this one in.
12850997Sbostic  *
12950997Sbostic  * If newbuf finds an error (returning NULL), it also sets errno.
13050997Sbostic  */
13146365Sbostic static BUFHEAD *
13257586Sbostic newbuf(hashp, addr, prev_bp)
13357586Sbostic 	HTAB *hashp;
13457586Sbostic 	u_int addr;
13550997Sbostic 	BUFHEAD *prev_bp;
13646365Sbostic {
13750997Sbostic 	register BUFHEAD *bp;		/* The buffer we're going to use */
13850997Sbostic 	register BUFHEAD *xbp;		/* Temp pointer */
13950997Sbostic 	register BUFHEAD *next_xbp;
14050997Sbostic 	SEGMENT segp;
14150997Sbostic 	int segment_ndx;
14250997Sbostic 	u_short oaddr, *shortp;
14346365Sbostic 
14450997Sbostic 	oaddr = 0;
14550997Sbostic 	bp = LRU;
14650997Sbostic 	/*
14750997Sbostic 	 * If LRU buffer is pinned, the buffer pool is too small. We need to
14850997Sbostic 	 * allocate more buffers.
14950997Sbostic 	 */
15050997Sbostic 	if (hashp->nbufs || (bp->flags & BUF_PIN)) {
15150997Sbostic 		/* Allocate a new one */
15250997Sbostic 		bp = malloc(sizeof(struct _bufhead));
15350997Sbostic 		if (!bp || !(bp->page = malloc(hashp->BSIZE)))
15450997Sbostic 			return (NULL);
15556351Smargo 		if (hashp->nbufs)
15656351Smargo 			hashp->nbufs--;
15750997Sbostic 	} else {
15850997Sbostic 		/* Kick someone out */
15950997Sbostic 		BUF_REMOVE(bp);
16050997Sbostic 		/*
16150997Sbostic 		 * If this is an overflow page with addr 0, it's already been
16250997Sbostic 		 * flushed back in an overflow chain and initialized.
16350997Sbostic 		 */
16450997Sbostic 		if ((bp->addr != 0) || (bp->flags & BUF_BUCKET)) {
16550997Sbostic 			/*
16650997Sbostic 			 * Set oaddr before __put_page so that you get it
16750997Sbostic 			 * before bytes are swapped.
16850997Sbostic 			 */
16950997Sbostic 			shortp = (u_short *)bp->page;
17050997Sbostic 			if (shortp[0])
17150997Sbostic 				oaddr = shortp[shortp[0] - 1];
17257586Sbostic 			if ((bp->flags & BUF_MOD) && __put_page(hashp, bp->page,
17350997Sbostic 			    bp->addr, (int)IS_BUCKET(bp->flags), 0))
17450997Sbostic 				return (NULL);
17550997Sbostic 			/*
17650997Sbostic 			 * Update the pointer to this page (i.e. invalidate it).
17750997Sbostic 			 *
17850997Sbostic 			 * If this is a new file (i.e. we created it at open
17950997Sbostic 			 * time), make sure that we mark pages which have been
18050997Sbostic 			 * written to disk so we retrieve them from disk later,
18150997Sbostic 			 * rather than allocating new pages.
18250997Sbostic 			 */
18350997Sbostic 			if (IS_BUCKET(bp->flags)) {
18450997Sbostic 				segment_ndx = bp->addr & (hashp->SGSIZE - 1);
18550997Sbostic 				segp = hashp->dir[bp->addr >> hashp->SSHIFT];
18650997Sbostic #ifdef DEBUG
18750997Sbostic 				assert(segp != NULL);
18850997Sbostic #endif
18946365Sbostic 
19050997Sbostic 				if (hashp->new_file &&
19150997Sbostic 				    ((bp->flags & BUF_MOD) ||
19250997Sbostic 				    ISDISK(segp[segment_ndx])))
19350997Sbostic 					segp[segment_ndx] = (BUFHEAD *)BUF_DISK;
19450997Sbostic 				else
19550997Sbostic 					segp[segment_ndx] = NULL;
19650997Sbostic 			}
19750997Sbostic 			/*
19850997Sbostic 			 * Since overflow pages can only be access by means of
19950997Sbostic 			 * their bucket, free overflow pages associated with
20050997Sbostic 			 * this bucket.
20150997Sbostic 			 */
20250997Sbostic 			for (xbp = bp; xbp->ovfl;) {
20350997Sbostic 				next_xbp = xbp->ovfl;
20450997Sbostic 				xbp->ovfl = 0;
20550997Sbostic 				xbp = next_xbp;
20646365Sbostic 
20750997Sbostic 				/* Check that ovfl pointer is up date. */
20850997Sbostic 				if (IS_BUCKET(xbp->flags) ||
20950997Sbostic 				    (oaddr != xbp->addr))
21050997Sbostic 					break;
21146365Sbostic 
21250997Sbostic 				shortp = (u_short *)xbp->page;
21350997Sbostic 				if (shortp[0])
21450997Sbostic 					/* set before __put_page */
21550997Sbostic 					oaddr = shortp[shortp[0] - 1];
21657586Sbostic 				if ((xbp->flags & BUF_MOD) && __put_page(hashp,
21757586Sbostic 				    xbp->page, xbp->addr, 0, 0))
21850997Sbostic 					return (NULL);
21950997Sbostic 				xbp->addr = 0;
22050997Sbostic 				xbp->flags = 0;
22150997Sbostic 				BUF_REMOVE(xbp);
22250997Sbostic 				LRU_INSERT(xbp);
22350997Sbostic 			}
22446506Sbostic 		}
22546365Sbostic 	}
22646365Sbostic 
22750997Sbostic 	/* Now assign this buffer */
22850997Sbostic 	bp->addr = addr;
22946365Sbostic #ifdef DEBUG1
23050997Sbostic 	(void)fprintf(stderr, "NEWBUF1: %d->ovfl was %d is now %d\n",
23150997Sbostic 	    bp->addr, (bp->ovfl ? bp->ovfl->addr : 0), 0);
23246365Sbostic #endif
23350997Sbostic 	bp->ovfl = NULL;
23450997Sbostic 	if (prev_bp) {
23550997Sbostic 		/*
23650997Sbostic 		 * If prev_bp is set, this is an overflow page, hook it in to
23750997Sbostic 		 * the buffer overflow links.
23850997Sbostic 		 */
23946365Sbostic #ifdef DEBUG1
24050997Sbostic 		(void)fprintf(stderr, "NEWBUF2: %d->ovfl was %d is now %d\n",
24150997Sbostic 		    prev_bp->addr, (prev_bp->ovfl ? bp->ovfl->addr : 0),
24250997Sbostic 		    (bp ? bp->addr : 0));
24346365Sbostic #endif
24450997Sbostic 		prev_bp->ovfl = bp;
24550997Sbostic 		bp->flags = 0;
24650997Sbostic 	} else
24750997Sbostic 		bp->flags = BUF_BUCKET;
24850997Sbostic 	MRU_INSERT(bp);
24950997Sbostic 	return (bp);
25046365Sbostic }
25146365Sbostic 
25246365Sbostic extern void
25357586Sbostic __buf_init(hashp, nbytes)
25457586Sbostic 	HTAB *hashp;
25550997Sbostic 	int nbytes;
25646365Sbostic {
25750997Sbostic 	BUFHEAD *bfp;
25850997Sbostic 	int npages;
25946365Sbostic 
26050997Sbostic 	bfp = &(hashp->bufhead);
26150997Sbostic 	npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT;
26250997Sbostic 	npages = MAX(npages, MIN_BUFFERS);
26346365Sbostic 
26450997Sbostic 	hashp->nbufs = npages;
26550997Sbostic 	bfp->next = bfp;
26650997Sbostic 	bfp->prev = bfp;
26750997Sbostic 	/*
26850997Sbostic 	 * This space is calloc'd so these are already null.
26950997Sbostic 	 *
27050997Sbostic 	 * bfp->ovfl = NULL;
27150997Sbostic 	 * bfp->flags = 0;
27250997Sbostic 	 * bfp->page = NULL;
27350997Sbostic 	 * bfp->addr = 0;
27450997Sbostic 	 */
27546365Sbostic }
27646365Sbostic 
27746365Sbostic extern int
27857586Sbostic __buf_free(hashp, do_free, to_disk)
27957586Sbostic 	HTAB *hashp;
28050997Sbostic 	int do_free, to_disk;
28146365Sbostic {
28250997Sbostic 	BUFHEAD *bp;
28346365Sbostic 
28450997Sbostic 	/* Need to make sure that buffer manager has been initialized */
28550997Sbostic 	if (!LRU)
28650997Sbostic 		return (0);
28750997Sbostic 	for (bp = LRU; bp != &hashp->bufhead;) {
28850997Sbostic 		/* Check that the buffer is valid */
28950997Sbostic 		if (bp->addr || IS_BUCKET(bp->flags)) {
29050997Sbostic 			if (to_disk && (bp->flags & BUF_MOD) &&
29157586Sbostic 			    __put_page(hashp, bp->page,
29250997Sbostic 			    bp->addr, IS_BUCKET(bp->flags), 0))
29350997Sbostic 				return (-1);
29450997Sbostic 		}
29550997Sbostic 		/* Check if we are freeing stuff */
29650997Sbostic 		if (do_free) {
29750997Sbostic 			if (bp->page)
29850997Sbostic 				free(bp->page);
29950997Sbostic 			BUF_REMOVE(bp);
30050997Sbostic 			free(bp);
30150997Sbostic 			bp = LRU;
30250997Sbostic 		} else
30350997Sbostic 			bp = bp->prev;
30446365Sbostic 	}
30550997Sbostic 	return (0);
30646365Sbostic }
30746365Sbostic 
30846365Sbostic extern void
30957586Sbostic __reclaim_buf(hashp, bp)
31057586Sbostic 	HTAB *hashp;
31150997Sbostic 	BUFHEAD *bp;
31246365Sbostic {
31350997Sbostic 	bp->ovfl = 0;
31450997Sbostic 	bp->addr = 0;
31550997Sbostic 	bp->flags = 0;
31650997Sbostic 	BUF_REMOVE(bp);
31750997Sbostic 	LRU_INSERT(bp);
31846365Sbostic }
319