xref: /csrg-svn/lib/libc/db/hash/hash_buf.c (revision 46502)
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*46502Sbostic static char sccsid[] = "@(#)hash_buf.c	5.3 (Berkeley) 02/21/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;
87*46502Sbostic     register	unsigned	is_disk_mask = 0;
8846365Sbostic     SEGMENT	segp;
8946365Sbostic 
9046365Sbostic     if ( prev_bp ) {
9146365Sbostic 	bp = prev_bp->ovfl;
9246365Sbostic 	if ( !bp || (bp->addr != addr) ) bp = NULL;
9346365Sbostic 	if ( !newpage ) is_disk = BUF_DISK;
9446365Sbostic     }
9546365Sbostic     else {
9646365Sbostic 	/* Grab buffer out of directory */
9746365Sbostic 	segment_ndx = addr & ( hashp->SGSIZE - 1 );
9846365Sbostic 
9946365Sbostic 	/*
10046365Sbostic 	 * valid segment ensured by __call_hash()
10146365Sbostic 	 */
10246365Sbostic 	segp = hashp->dir[addr >> hashp->SSHIFT];
10346365Sbostic #ifdef DEBUG
10446365Sbostic 	assert(segp != NULL);
10546365Sbostic #endif
10646365Sbostic 	bp = PTROF(segp[segment_ndx]);
107*46502Sbostic 	is_disk_mask = ISDISK(segp[segment_ndx]);
108*46502Sbostic 	is_disk = is_disk_mask || !hashp->new_file;
10946365Sbostic     }
11046365Sbostic 
11146365Sbostic     if ( !bp ) {
11246365Sbostic 	bp = newbuf ( addr, prev_bp );
113*46502Sbostic 	if ( !bp || __get_page ( bp->page, addr, !prev_bp, is_disk, 0 )) {
11446365Sbostic 	    return(NULL);
11546365Sbostic 	}
11646365Sbostic 	if ( !prev_bp ) {
117*46502Sbostic 	    segp[segment_ndx] = (BUFHEAD *)((unsigned)bp | is_disk_mask );
11846365Sbostic 	}
11946365Sbostic     } else {
12046365Sbostic 	BUF_REMOVE ( bp );
12146365Sbostic 	MRU_INSERT ( bp );
12246365Sbostic     }
12346365Sbostic     return(bp);
12446365Sbostic }
12546365Sbostic 
12646365Sbostic /*
12746365Sbostic     We need a buffer for this page. Either allocate one, or
12846365Sbostic     evict a resident one (if we have as many buffers as we're
12946365Sbostic     allowed) and put this one in.
13046365Sbostic 
13146365Sbostic     If newbuf finds an error (returning NULL), it also sets errno
13246365Sbostic */
13346365Sbostic static BUFHEAD *
13446365Sbostic newbuf ( addr, prev_bp )
13546365Sbostic int	addr;
13646365Sbostic BUFHEAD	*prev_bp;
13746365Sbostic {
13846365Sbostic     register	BUFHEAD	*bp;	/* The buffer we're going to use */
13946365Sbostic     register	BUFHEAD	*xbp;	/* Temp pointer */
14046365Sbostic     register	BUFHEAD *next_xbp;
14146365Sbostic     int	segment_ndx;
14246365Sbostic     u_short	*shortp;
14346365Sbostic     u_short	oaddr;
14446365Sbostic     SEGMENT	segp;
14546365Sbostic 
14646456Sbostic     bp = LRU;
14746456Sbostic     /*
14846456Sbostic 	If LRU buffer is pinned, the buffer pool is too small.
14946456Sbostic 	We need to allocate more buffers
15046456Sbostic     */
15146456Sbostic     if ( hashp->nbufs || (bp->flags & BUF_PIN) ) {
15246365Sbostic 	/* Allocate a new one */
15346365Sbostic 	bp = (BUFHEAD *)malloc ( sizeof (struct _bufhead) );
15446365Sbostic 	if ( !bp || !(bp->page = (char *)malloc ( hashp->BSIZE )) ) {
15546365Sbostic 	    return (NULL);
15646365Sbostic 	}
15746365Sbostic 	hashp->nbufs--;
15846365Sbostic     } else {
15946365Sbostic 	/* Kick someone out */
16046365Sbostic 	BUF_REMOVE( bp );
16146365Sbostic 	/*
16246456Sbostic 	    If this is an overflow page with addr 0, it's already
16346456Sbostic 	    been flushed back in an overflow chain and initialized
16446365Sbostic 	*/
16546456Sbostic 	if ( (bp->addr != 0) || (bp->flags & BUF_BUCKET) ) {
16646456Sbostic 	    /*
16746456Sbostic 		Set oaddr before __put_page so that you get it
16846456Sbostic 		before bytes are swapped
16946456Sbostic 	    */
17046456Sbostic 	    shortp = (u_short *)bp->page;
17146456Sbostic 	    oaddr = shortp[shortp[0]-1];
17246456Sbostic 	    if ( (bp->flags & BUF_MOD) &&
17346456Sbostic 		 __put_page(bp->page, bp->addr, (int)IS_BUCKET(bp->flags), 0) ) {
17446456Sbostic 		return(NULL);
17546456Sbostic 	    }
17646456Sbostic 	    /*
17746456Sbostic 		Update the pointer to this page (i.e. invalidate it).
17846365Sbostic 
17946456Sbostic 		If this is a new file (i.e. we created it at open time),
18046456Sbostic 		make sure that we mark pages which have been written to
18146456Sbostic 		disk so we retrieve them from disk later, rather than
18246456Sbostic 		allocating new pages.
18346456Sbostic 	    */
18446365Sbostic 
18546456Sbostic 	    if ( IS_BUCKET(bp->flags)) {
18646456Sbostic 		segment_ndx = bp->addr & ( hashp->SGSIZE - 1 );
18746365Sbostic 
18846456Sbostic 		segp = hashp->dir[bp->addr >> hashp->SSHIFT];
18946365Sbostic 
19046456Sbostic 		assert(segp != NULL);
19146365Sbostic 
19246456Sbostic 		if ( hashp->new_file &&
19346456Sbostic 		     ((bp->flags & BUF_MOD) || ISDISK(segp[segment_ndx])) ) {
19446456Sbostic 		    segp[segment_ndx] = (BUFHEAD *)BUF_DISK;
19546456Sbostic 		} else segp[segment_ndx] = NULL;
19646456Sbostic 	    }
19746365Sbostic 
19846456Sbostic 	    /*
19946456Sbostic 		Since overflow pages can only be access by means of
20046456Sbostic 		their bucket, free overflow pages associated with this
20146456Sbostic 		bucket.
20246456Sbostic 	    */
20346456Sbostic 	    for ( xbp = bp; xbp->ovfl; ) {
20446365Sbostic 
20546456Sbostic 		next_xbp = xbp->ovfl;
20646456Sbostic 		xbp->ovfl = 0;
20746456Sbostic 		xbp = next_xbp;
20846365Sbostic 
20946456Sbostic 		/* Check that ovfl pointer is up date */
21046456Sbostic 		if ( IS_BUCKET(xbp->flags) || (oaddr != xbp->addr) ) break;
21146365Sbostic 
21246456Sbostic 		shortp = (u_short *)xbp->page;
21346456Sbostic 		oaddr = shortp[shortp[0]-1];	/* set before __put_page */
21446456Sbostic 		if ( (xbp->flags & BUF_MOD) &&
21546456Sbostic 		    __put_page ( xbp->page, xbp->addr, 0, 0 ) ) {
21646456Sbostic 		    return(NULL);
21746456Sbostic 		}
21846456Sbostic 		xbp->addr = 0;
21946456Sbostic 		xbp->flags = 0;
22046456Sbostic 		BUF_REMOVE ( xbp );
22146456Sbostic 		LRU_INSERT ( xbp );
22246365Sbostic 	    }
22346365Sbostic 	}
22446365Sbostic     }
22546365Sbostic 
22646365Sbostic     /* Now assign this buffer */
22746365Sbostic     bp->addr = addr;
22846365Sbostic #ifdef DEBUG1
22946365Sbostic     fprintf ( stderr, "NEWBUF1: %d->ovfl was %d is now %d\n", bp->addr,
23046365Sbostic 		(bp->ovfl?bp->ovfl->addr:0),  0);
23146365Sbostic #endif
23246365Sbostic     bp->ovfl = NULL;
23346365Sbostic     if ( prev_bp ) {
23446365Sbostic 	/*
23546365Sbostic 	    If prev_bp is set, this is an overflow page, hook it in to the
23646365Sbostic 	    buffer overflow links
23746365Sbostic 	*/
23846365Sbostic #ifdef DEBUG1
23946365Sbostic 	fprintf ( stderr, "NEWBUF2: %d->ovfl was %d is now %d\n", prev_bp->addr,
24046365Sbostic 		    (prev_bp->ovfl?bp->ovfl->addr:0),
24146365Sbostic 		    (bp?bp->addr: 0));
24246365Sbostic #endif
24346365Sbostic 	prev_bp->ovfl = bp;
24446365Sbostic 	bp->flags = 0;
24546365Sbostic     } else bp->flags = BUF_BUCKET;
24646365Sbostic     MRU_INSERT ( bp );
24746365Sbostic     return ( bp );
24846365Sbostic }
24946365Sbostic 
25046365Sbostic extern void
25146365Sbostic __buf_init ( nbytes )
25246365Sbostic int	nbytes;
25346365Sbostic {
25446365Sbostic     int	npages;
25546365Sbostic     BUFHEAD	*bfp = &(hashp->bufhead);
25646365Sbostic 
25746365Sbostic     npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT;
25846365Sbostic     npages = MAX ( npages, MIN_BUFFERS );
25946365Sbostic 
26046365Sbostic     hashp->nbufs = npages;
26146365Sbostic     bfp->next = bfp;
26246365Sbostic     bfp->prev = bfp;
26346365Sbostic     /*
26446365Sbostic 	This space is calloc'd so these are already null
26546365Sbostic 
26646365Sbostic 	bfp->ovfl = NULL;
26746365Sbostic 	bfp->flags = 0;
26846365Sbostic 	bfp->page = NULL;
26946365Sbostic 	bfp->addr = 0;
27046365Sbostic     */
27146365Sbostic }
27246365Sbostic 
27346365Sbostic extern int
27446365Sbostic __buf_free ( do_free, to_disk )
27546365Sbostic int	do_free;
27646365Sbostic int	to_disk;
27746365Sbostic {
27846365Sbostic     BUFHEAD	*bp;
27946365Sbostic 
28046365Sbostic     /* Need to make sure that buffer manager has been initialized */
28146365Sbostic     if ( !LRU ) {
28246365Sbostic 	return(0);
28346365Sbostic     }
28446365Sbostic 
28546365Sbostic     for ( bp = LRU; bp != &hashp->bufhead; ) {
28646365Sbostic 	/* Check that the buffer is valid */
28746365Sbostic 	if ( bp->addr || IS_BUCKET(bp->flags) ) {
28846365Sbostic 	    if ( to_disk && (bp->flags & BUF_MOD) &&
28946365Sbostic 		 __put_page (bp->page, bp->addr, IS_BUCKET(bp->flags), 0 )) {
29046365Sbostic 		return (-1);
29146365Sbostic 	    }
29246365Sbostic 	}
29346365Sbostic 
29446365Sbostic 	/* Check if we are freeing stuff */
29546365Sbostic 	if ( do_free ) {
29646365Sbostic 	    if ( bp->page ) free ( bp->page );
29746365Sbostic 	    BUF_REMOVE(bp);
29846365Sbostic 	    (void)free ( bp );
29946365Sbostic 	    bp = LRU;
30046365Sbostic 	} else bp = bp->prev;
30146365Sbostic     }
30246365Sbostic 
30346365Sbostic     return(0);
30446365Sbostic }
30546365Sbostic 
30646365Sbostic extern void
30746365Sbostic __reclaim_buf ( bp )
30846365Sbostic BUFHEAD	*bp;
30946365Sbostic {
31046365Sbostic     bp->ovfl = 0;
31146365Sbostic     bp->addr = 0;
31246365Sbostic     bp->flags = 0;
31346365Sbostic     BUF_REMOVE ( bp );
31446365Sbostic     LRU_INSERT ( bp );
31546365Sbostic }
316