xref: /csrg-svn/lib/libc/db/hash/hash_buf.c (revision 46456)
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*46456Sbostic static char sccsid[] = "@(#)hash_buf.c	5.2 (Berkeley) 02/19/91";
1346365Sbostic #endif /* LIBC_SCCS and not lint */
1446365Sbostic 
1546365Sbostic /******************************************************************************
1646365Sbostic 
1746365Sbostic PACKAGE: hash
1846365Sbostic 
1946365Sbostic DESCRIPTION:
2046365Sbostic 	Contains buffer management
2146365Sbostic 
2246365Sbostic ROUTINES:
2346365Sbostic     External
2446365Sbostic 	__buf_init
2546365Sbostic 	__get_buf
2646365Sbostic 	__buf_free
2746365Sbostic 	__reclaim_buf
2846365Sbostic     Internal
2946365Sbostic 	newbuf
3046365Sbostic 
3146365Sbostic ******************************************************************************/
3246365Sbostic #include <sys/param.h>
3346365Sbostic #include <sys/file.h>
3446365Sbostic #include <assert.h>
3546365Sbostic #include <errno.h>
3646365Sbostic #include "hash.h"
3746365Sbostic #include <stdio.h>
3846365Sbostic 
3946365Sbostic /* Externals */
4046365Sbostic extern HTAB	*hashp;
4146365Sbostic 
4246365Sbostic /* My internals */
4346365Sbostic static BUFHEAD *newbuf();
4446365Sbostic 
4546365Sbostic /* Unlink B from its place in the lru */
4646365Sbostic #define BUF_REMOVE(B)			\
4746365Sbostic {					\
4846365Sbostic     B->prev->next = B->next;		\
4946365Sbostic     B->next->prev = B->prev;		\
5046365Sbostic }
5146365Sbostic 
5246365Sbostic /* Insert B after P */
5346365Sbostic #define BUF_INSERT(B,P)			\
5446365Sbostic {					\
5546365Sbostic     B->next = P->next;			\
5646365Sbostic     B->prev = P;			\
5746365Sbostic     P->next = B;			\
5846365Sbostic     B->next->prev = B;			\
5946365Sbostic }
6046365Sbostic 
6146365Sbostic #define	MRU	hashp->bufhead.next
6246365Sbostic #define	LRU	hashp->bufhead.prev
6346365Sbostic 
6446365Sbostic #define MRU_INSERT(B)	BUF_INSERT(B,(&hashp->bufhead))
6546365Sbostic #define LRU_INSERT(B)	BUF_INSERT(B,LRU)
6646365Sbostic 
6746365Sbostic /*
6846365Sbostic     We are looking for a buffer with address "addr".
6946365Sbostic     If prev_bp is NULL, then address is a bucket index.
7046365Sbostic     If prev_bp is not NULL, then it points to the page previous
7146365Sbostic 	to an overflow page that we are trying to find.
7246365Sbostic 
7346365Sbostic     CAVEAT:  The buffer header accessed via prev_bp's ovfl field
7446365Sbostic     may no longer be valid.  Therefore, you must always verify that
7546365Sbostic     its address matches the address you are seeking.
7646365Sbostic */
7746365Sbostic extern BUFHEAD *
7846365Sbostic __get_buf ( addr, prev_bp, newpage )
7946365Sbostic int	addr;
8046365Sbostic BUFHEAD	*prev_bp;
8146365Sbostic int	newpage;		/* If prev_bp is set, indicates that this is
8246365Sbostic 					a new overflow page */
8346365Sbostic {
8446365Sbostic     register int	segment_ndx;
8546365Sbostic     register	BUFHEAD	*bp;
8646365Sbostic     register	unsigned	is_disk = 0;
8746365Sbostic     SEGMENT	segp;
8846365Sbostic 
8946365Sbostic     if ( prev_bp ) {
9046365Sbostic 	bp = prev_bp->ovfl;
9146365Sbostic 	if ( !bp || (bp->addr != addr) ) bp = NULL;
9246365Sbostic 	if ( !newpage ) is_disk = BUF_DISK;
9346365Sbostic     }
9446365Sbostic     else {
9546365Sbostic 	/* Grab buffer out of directory */
9646365Sbostic 	segment_ndx = addr & ( hashp->SGSIZE - 1 );
9746365Sbostic 
9846365Sbostic 	/*
9946365Sbostic 	 * valid segment ensured by __call_hash()
10046365Sbostic 	 */
10146365Sbostic 	segp = hashp->dir[addr >> hashp->SSHIFT];
10246365Sbostic #ifdef DEBUG
10346365Sbostic 	assert(segp != NULL);
10446365Sbostic #endif
10546365Sbostic 	bp = PTROF(segp[segment_ndx]);
10646365Sbostic 	is_disk = ISDISK(segp[segment_ndx]);
10746365Sbostic     }
10846365Sbostic 
10946365Sbostic     if ( !bp ) {
11046365Sbostic 	bp = newbuf ( addr, prev_bp );
11146365Sbostic 	if ( !bp || __get_page ( bp->page, addr, !prev_bp, (int)is_disk, 0 )) {
11246365Sbostic 	    return(NULL);
11346365Sbostic 	}
11446365Sbostic 	if ( !prev_bp ) {
11546365Sbostic 	    segp[segment_ndx] = (BUFHEAD *)((unsigned)bp | is_disk);
11646365Sbostic 	}
11746365Sbostic     } else {
11846365Sbostic 	BUF_REMOVE ( bp );
11946365Sbostic 	MRU_INSERT ( bp );
12046365Sbostic     }
12146365Sbostic     return(bp);
12246365Sbostic }
12346365Sbostic 
12446365Sbostic /*
12546365Sbostic     We need a buffer for this page. Either allocate one, or
12646365Sbostic     evict a resident one (if we have as many buffers as we're
12746365Sbostic     allowed) and put this one in.
12846365Sbostic 
12946365Sbostic     If newbuf finds an error (returning NULL), it also sets errno
13046365Sbostic */
13146365Sbostic static BUFHEAD *
13246365Sbostic newbuf ( addr, prev_bp )
13346365Sbostic int	addr;
13446365Sbostic BUFHEAD	*prev_bp;
13546365Sbostic {
13646365Sbostic     register	BUFHEAD	*bp;	/* The buffer we're going to use */
13746365Sbostic     register	BUFHEAD	*xbp;	/* Temp pointer */
13846365Sbostic     register	BUFHEAD *next_xbp;
13946365Sbostic     int	segment_ndx;
14046365Sbostic     u_short	*shortp;
14146365Sbostic     u_short	oaddr;
14246365Sbostic     SEGMENT	segp;
14346365Sbostic 
144*46456Sbostic     bp = LRU;
145*46456Sbostic     /*
146*46456Sbostic 	If LRU buffer is pinned, the buffer pool is too small.
147*46456Sbostic 	We need to allocate more buffers
148*46456Sbostic     */
149*46456Sbostic     if ( hashp->nbufs || (bp->flags & BUF_PIN) ) {
15046365Sbostic 	/* Allocate a new one */
15146365Sbostic 	bp = (BUFHEAD *)malloc ( sizeof (struct _bufhead) );
15246365Sbostic 	if ( !bp || !(bp->page = (char *)malloc ( hashp->BSIZE )) ) {
15346365Sbostic 	    return (NULL);
15446365Sbostic 	}
15546365Sbostic 	hashp->nbufs--;
15646365Sbostic     } else {
15746365Sbostic 	/* Kick someone out */
15846365Sbostic 	BUF_REMOVE( bp );
15946365Sbostic 	/*
160*46456Sbostic 	    If this is an overflow page with addr 0, it's already
161*46456Sbostic 	    been flushed back in an overflow chain and initialized
16246365Sbostic 	*/
163*46456Sbostic 	if ( (bp->addr != 0) || (bp->flags & BUF_BUCKET) ) {
164*46456Sbostic 	    /*
165*46456Sbostic 		Set oaddr before __put_page so that you get it
166*46456Sbostic 		before bytes are swapped
167*46456Sbostic 	    */
168*46456Sbostic 	    shortp = (u_short *)bp->page;
169*46456Sbostic 	    oaddr = shortp[shortp[0]-1];
170*46456Sbostic 	    if ( (bp->flags & BUF_MOD) &&
171*46456Sbostic 		 __put_page(bp->page, bp->addr, (int)IS_BUCKET(bp->flags), 0) ) {
172*46456Sbostic 		return(NULL);
173*46456Sbostic 	    }
174*46456Sbostic 	    /*
175*46456Sbostic 		Update the pointer to this page (i.e. invalidate it).
17646365Sbostic 
177*46456Sbostic 		If this is a new file (i.e. we created it at open time),
178*46456Sbostic 		make sure that we mark pages which have been written to
179*46456Sbostic 		disk so we retrieve them from disk later, rather than
180*46456Sbostic 		allocating new pages.
181*46456Sbostic 	    */
18246365Sbostic 
183*46456Sbostic 	    if ( IS_BUCKET(bp->flags)) {
184*46456Sbostic 		segment_ndx = bp->addr & ( hashp->SGSIZE - 1 );
18546365Sbostic 
186*46456Sbostic 		segp = hashp->dir[bp->addr >> hashp->SSHIFT];
18746365Sbostic 
188*46456Sbostic 		assert(segp != NULL);
18946365Sbostic 
190*46456Sbostic 		if ( hashp->new_file &&
191*46456Sbostic 		     ((bp->flags & BUF_MOD) || ISDISK(segp[segment_ndx])) ) {
192*46456Sbostic 		    segp[segment_ndx] = (BUFHEAD *)BUF_DISK;
193*46456Sbostic 		} else segp[segment_ndx] = NULL;
194*46456Sbostic 	    }
19546365Sbostic 
196*46456Sbostic 	    /*
197*46456Sbostic 		Since overflow pages can only be access by means of
198*46456Sbostic 		their bucket, free overflow pages associated with this
199*46456Sbostic 		bucket.
200*46456Sbostic 	    */
201*46456Sbostic 	    for ( xbp = bp; xbp->ovfl; ) {
20246365Sbostic 
203*46456Sbostic 		next_xbp = xbp->ovfl;
204*46456Sbostic 		xbp->ovfl = 0;
205*46456Sbostic 		xbp = next_xbp;
20646365Sbostic 
207*46456Sbostic 		/* Check that ovfl pointer is up date */
208*46456Sbostic 		if ( IS_BUCKET(xbp->flags) || (oaddr != xbp->addr) ) break;
20946365Sbostic 
210*46456Sbostic 		shortp = (u_short *)xbp->page;
211*46456Sbostic 		oaddr = shortp[shortp[0]-1];	/* set before __put_page */
212*46456Sbostic 		if ( (xbp->flags & BUF_MOD) &&
213*46456Sbostic 		    __put_page ( xbp->page, xbp->addr, 0, 0 ) ) {
214*46456Sbostic 		    return(NULL);
215*46456Sbostic 		}
216*46456Sbostic 		xbp->addr = 0;
217*46456Sbostic 		xbp->flags = 0;
218*46456Sbostic 		BUF_REMOVE ( xbp );
219*46456Sbostic 		LRU_INSERT ( xbp );
22046365Sbostic 	    }
22146365Sbostic 	}
22246365Sbostic     }
22346365Sbostic 
22446365Sbostic     /* Now assign this buffer */
22546365Sbostic     bp->addr = addr;
22646365Sbostic #ifdef DEBUG1
22746365Sbostic     fprintf ( stderr, "NEWBUF1: %d->ovfl was %d is now %d\n", bp->addr,
22846365Sbostic 		(bp->ovfl?bp->ovfl->addr:0),  0);
22946365Sbostic #endif
23046365Sbostic     bp->ovfl = NULL;
23146365Sbostic     if ( prev_bp ) {
23246365Sbostic 	/*
23346365Sbostic 	    If prev_bp is set, this is an overflow page, hook it in to the
23446365Sbostic 	    buffer overflow links
23546365Sbostic 	*/
23646365Sbostic #ifdef DEBUG1
23746365Sbostic 	fprintf ( stderr, "NEWBUF2: %d->ovfl was %d is now %d\n", prev_bp->addr,
23846365Sbostic 		    (prev_bp->ovfl?bp->ovfl->addr:0),
23946365Sbostic 		    (bp?bp->addr: 0));
24046365Sbostic #endif
24146365Sbostic 	prev_bp->ovfl = bp;
24246365Sbostic 	bp->flags = 0;
24346365Sbostic     } else bp->flags = BUF_BUCKET;
24446365Sbostic     MRU_INSERT ( bp );
24546365Sbostic     return ( bp );
24646365Sbostic }
24746365Sbostic 
24846365Sbostic extern void
24946365Sbostic __buf_init ( nbytes )
25046365Sbostic int	nbytes;
25146365Sbostic {
25246365Sbostic     int	npages;
25346365Sbostic     BUFHEAD	*bfp = &(hashp->bufhead);
25446365Sbostic 
25546365Sbostic     npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT;
25646365Sbostic     npages = MAX ( npages, MIN_BUFFERS );
25746365Sbostic 
25846365Sbostic     hashp->nbufs = npages;
25946365Sbostic     bfp->next = bfp;
26046365Sbostic     bfp->prev = bfp;
26146365Sbostic     /*
26246365Sbostic 	This space is calloc'd so these are already null
26346365Sbostic 
26446365Sbostic 	bfp->ovfl = NULL;
26546365Sbostic 	bfp->flags = 0;
26646365Sbostic 	bfp->page = NULL;
26746365Sbostic 	bfp->addr = 0;
26846365Sbostic     */
26946365Sbostic }
27046365Sbostic 
27146365Sbostic extern int
27246365Sbostic __buf_free ( do_free, to_disk )
27346365Sbostic int	do_free;
27446365Sbostic int	to_disk;
27546365Sbostic {
27646365Sbostic     BUFHEAD	*bp;
27746365Sbostic 
27846365Sbostic     /* Need to make sure that buffer manager has been initialized */
27946365Sbostic     if ( !LRU ) {
28046365Sbostic 	return(0);
28146365Sbostic     }
28246365Sbostic 
28346365Sbostic     for ( bp = LRU; bp != &hashp->bufhead; ) {
28446365Sbostic 	/* Check that the buffer is valid */
28546365Sbostic 	if ( bp->addr || IS_BUCKET(bp->flags) ) {
28646365Sbostic 	    if ( to_disk && (bp->flags & BUF_MOD) &&
28746365Sbostic 		 __put_page (bp->page, bp->addr, IS_BUCKET(bp->flags), 0 )) {
28846365Sbostic 		return (-1);
28946365Sbostic 	    }
29046365Sbostic 	}
29146365Sbostic 
29246365Sbostic 	/* Check if we are freeing stuff */
29346365Sbostic 	if ( do_free ) {
29446365Sbostic 	    if ( bp->page ) free ( bp->page );
29546365Sbostic 	    BUF_REMOVE(bp);
29646365Sbostic 	    (void)free ( bp );
29746365Sbostic 	    bp = LRU;
29846365Sbostic 	} else bp = bp->prev;
29946365Sbostic     }
30046365Sbostic 
30146365Sbostic     return(0);
30246365Sbostic }
30346365Sbostic 
30446365Sbostic extern void
30546365Sbostic __reclaim_buf ( bp )
30646365Sbostic BUFHEAD	*bp;
30746365Sbostic {
30846365Sbostic     bp->ovfl = 0;
30946365Sbostic     bp->addr = 0;
31046365Sbostic     bp->flags = 0;
31146365Sbostic     BUF_REMOVE ( bp );
31246365Sbostic     LRU_INSERT ( bp );
31346365Sbostic }
314