xref: /csrg-svn/lib/libc/db/hash/hash_buf.c (revision 46562)
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*46562Sbostic static char sccsid[] = "@(#)hash_buf.c	5.5 (Berkeley) 02/22/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 ******************************************************************************/
32*46562Sbostic #include <sys/types.h>
3346365Sbostic #include <assert.h>
3446365Sbostic #include <errno.h>
35*46562Sbostic #include <stdio.h>
36*46562Sbostic #include <stdlib.h>
3746365Sbostic #include "hash.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;
8746502Sbostic     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]);
10746502Sbostic 	is_disk_mask = ISDISK(segp[segment_ndx]);
10846502Sbostic 	is_disk = is_disk_mask || !hashp->new_file;
10946365Sbostic     }
11046365Sbostic 
11146365Sbostic     if ( !bp ) {
11246365Sbostic 	bp = newbuf ( addr, prev_bp );
11346502Sbostic 	if ( !bp || __get_page ( bp->page, addr, !prev_bp, is_disk, 0 )) {
11446365Sbostic 	    return(NULL);
11546365Sbostic 	}
11646365Sbostic 	if ( !prev_bp ) {
11746502Sbostic 	    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 
14646506Sbostic     oaddr = 0;
14746456Sbostic     bp = LRU;
14846456Sbostic     /*
14946456Sbostic 	If LRU buffer is pinned, the buffer pool is too small.
15046456Sbostic 	We need to allocate more buffers
15146456Sbostic     */
15246456Sbostic     if ( hashp->nbufs || (bp->flags & BUF_PIN) ) {
15346365Sbostic 	/* Allocate a new one */
15446365Sbostic 	bp = (BUFHEAD *)malloc ( sizeof (struct _bufhead) );
15546365Sbostic 	if ( !bp || !(bp->page = (char *)malloc ( hashp->BSIZE )) ) {
15646365Sbostic 	    return (NULL);
15746365Sbostic 	}
15846365Sbostic 	hashp->nbufs--;
15946365Sbostic     } else {
16046365Sbostic 	/* Kick someone out */
16146365Sbostic 	BUF_REMOVE( bp );
16246365Sbostic 	/*
16346456Sbostic 	    If this is an overflow page with addr 0, it's already
16446456Sbostic 	    been flushed back in an overflow chain and initialized
16546365Sbostic 	*/
16646456Sbostic 	if ( (bp->addr != 0) || (bp->flags & BUF_BUCKET) ) {
16746456Sbostic 	    /*
16846456Sbostic 		Set oaddr before __put_page so that you get it
16946456Sbostic 		before bytes are swapped
17046456Sbostic 	    */
17146456Sbostic 	    shortp = (u_short *)bp->page;
17246506Sbostic 	    if ( shortp[0] ) {
17346506Sbostic 		oaddr = shortp[shortp[0]-1];
17446506Sbostic 	    }
17546456Sbostic 	    if ( (bp->flags & BUF_MOD) &&
17646456Sbostic 		 __put_page(bp->page, bp->addr, (int)IS_BUCKET(bp->flags), 0) ) {
17746456Sbostic 		return(NULL);
17846456Sbostic 	    }
17946456Sbostic 	    /*
18046456Sbostic 		Update the pointer to this page (i.e. invalidate it).
18146365Sbostic 
18246456Sbostic 		If this is a new file (i.e. we created it at open time),
18346456Sbostic 		make sure that we mark pages which have been written to
18446456Sbostic 		disk so we retrieve them from disk later, rather than
18546456Sbostic 		allocating new pages.
18646456Sbostic 	    */
18746365Sbostic 
18846456Sbostic 	    if ( IS_BUCKET(bp->flags)) {
18946456Sbostic 		segment_ndx = bp->addr & ( hashp->SGSIZE - 1 );
19046365Sbostic 
19146456Sbostic 		segp = hashp->dir[bp->addr >> hashp->SSHIFT];
19246365Sbostic 
19346456Sbostic 		assert(segp != NULL);
19446365Sbostic 
19546456Sbostic 		if ( hashp->new_file &&
19646456Sbostic 		     ((bp->flags & BUF_MOD) || ISDISK(segp[segment_ndx])) ) {
19746456Sbostic 		    segp[segment_ndx] = (BUFHEAD *)BUF_DISK;
19846456Sbostic 		} else segp[segment_ndx] = NULL;
19946456Sbostic 	    }
20046365Sbostic 
20146456Sbostic 	    /*
20246456Sbostic 		Since overflow pages can only be access by means of
20346456Sbostic 		their bucket, free overflow pages associated with this
20446456Sbostic 		bucket.
20546456Sbostic 	    */
20646456Sbostic 	    for ( xbp = bp; xbp->ovfl; ) {
20746365Sbostic 
20846456Sbostic 		next_xbp = xbp->ovfl;
20946456Sbostic 		xbp->ovfl = 0;
21046456Sbostic 		xbp = next_xbp;
21146365Sbostic 
21246456Sbostic 		/* Check that ovfl pointer is up date */
21346456Sbostic 		if ( IS_BUCKET(xbp->flags) || (oaddr != xbp->addr) ) break;
21446365Sbostic 
21546456Sbostic 		shortp = (u_short *)xbp->page;
21646506Sbostic 		if ( shortp[0] ) {
21746506Sbostic 		    oaddr = shortp[shortp[0]-1];  /* set before __put_page */
21846506Sbostic 		}
21946456Sbostic 		if ( (xbp->flags & BUF_MOD) &&
22046456Sbostic 		    __put_page ( xbp->page, xbp->addr, 0, 0 ) ) {
22146456Sbostic 		    return(NULL);
22246456Sbostic 		}
22346456Sbostic 		xbp->addr = 0;
22446456Sbostic 		xbp->flags = 0;
22546456Sbostic 		BUF_REMOVE ( xbp );
22646456Sbostic 		LRU_INSERT ( xbp );
22746365Sbostic 	    }
22846365Sbostic 	}
22946365Sbostic     }
23046365Sbostic 
23146365Sbostic     /* Now assign this buffer */
23246365Sbostic     bp->addr = addr;
23346365Sbostic #ifdef DEBUG1
23446365Sbostic     fprintf ( stderr, "NEWBUF1: %d->ovfl was %d is now %d\n", bp->addr,
23546365Sbostic 		(bp->ovfl?bp->ovfl->addr:0),  0);
23646365Sbostic #endif
23746365Sbostic     bp->ovfl = NULL;
23846365Sbostic     if ( prev_bp ) {
23946365Sbostic 	/*
24046365Sbostic 	    If prev_bp is set, this is an overflow page, hook it in to the
24146365Sbostic 	    buffer overflow links
24246365Sbostic 	*/
24346365Sbostic #ifdef DEBUG1
24446365Sbostic 	fprintf ( stderr, "NEWBUF2: %d->ovfl was %d is now %d\n", prev_bp->addr,
24546365Sbostic 		    (prev_bp->ovfl?bp->ovfl->addr:0),
24646365Sbostic 		    (bp?bp->addr: 0));
24746365Sbostic #endif
24846365Sbostic 	prev_bp->ovfl = bp;
24946365Sbostic 	bp->flags = 0;
25046365Sbostic     } else bp->flags = BUF_BUCKET;
25146365Sbostic     MRU_INSERT ( bp );
25246365Sbostic     return ( bp );
25346365Sbostic }
25446365Sbostic 
25546365Sbostic extern void
25646365Sbostic __buf_init ( nbytes )
25746365Sbostic int	nbytes;
25846365Sbostic {
25946365Sbostic     int	npages;
26046365Sbostic     BUFHEAD	*bfp = &(hashp->bufhead);
26146365Sbostic 
26246365Sbostic     npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT;
26346365Sbostic     npages = MAX ( npages, MIN_BUFFERS );
26446365Sbostic 
26546365Sbostic     hashp->nbufs = npages;
26646365Sbostic     bfp->next = bfp;
26746365Sbostic     bfp->prev = bfp;
26846365Sbostic     /*
26946365Sbostic 	This space is calloc'd so these are already null
27046365Sbostic 
27146365Sbostic 	bfp->ovfl = NULL;
27246365Sbostic 	bfp->flags = 0;
27346365Sbostic 	bfp->page = NULL;
27446365Sbostic 	bfp->addr = 0;
27546365Sbostic     */
27646365Sbostic }
27746365Sbostic 
27846365Sbostic extern int
27946365Sbostic __buf_free ( do_free, to_disk )
28046365Sbostic int	do_free;
28146365Sbostic int	to_disk;
28246365Sbostic {
28346365Sbostic     BUFHEAD	*bp;
28446365Sbostic 
28546365Sbostic     /* Need to make sure that buffer manager has been initialized */
28646365Sbostic     if ( !LRU ) {
28746365Sbostic 	return(0);
28846365Sbostic     }
28946365Sbostic 
29046365Sbostic     for ( bp = LRU; bp != &hashp->bufhead; ) {
29146365Sbostic 	/* Check that the buffer is valid */
29246365Sbostic 	if ( bp->addr || IS_BUCKET(bp->flags) ) {
29346365Sbostic 	    if ( to_disk && (bp->flags & BUF_MOD) &&
29446365Sbostic 		 __put_page (bp->page, bp->addr, IS_BUCKET(bp->flags), 0 )) {
29546365Sbostic 		return (-1);
29646365Sbostic 	    }
29746365Sbostic 	}
29846365Sbostic 
29946365Sbostic 	/* Check if we are freeing stuff */
30046365Sbostic 	if ( do_free ) {
30146365Sbostic 	    if ( bp->page ) free ( bp->page );
30246365Sbostic 	    BUF_REMOVE(bp);
30346365Sbostic 	    (void)free ( bp );
30446365Sbostic 	    bp = LRU;
30546365Sbostic 	} else bp = bp->prev;
30646365Sbostic     }
30746365Sbostic 
30846365Sbostic     return(0);
30946365Sbostic }
31046365Sbostic 
31146365Sbostic extern void
31246365Sbostic __reclaim_buf ( bp )
31346365Sbostic BUFHEAD	*bp;
31446365Sbostic {
31546365Sbostic     bp->ovfl = 0;
31646365Sbostic     bp->addr = 0;
31746365Sbostic     bp->flags = 0;
31846365Sbostic     BUF_REMOVE ( bp );
31946365Sbostic     LRU_INSERT ( bp );
32046365Sbostic }
321