xref: /csrg-svn/lib/libc/db/hash/hash_buf.c (revision 46365)
1*46365Sbostic /*-
2*46365Sbostic  * Copyright (c) 1990 The Regents of the University of California.
3*46365Sbostic  * All rights reserved.
4*46365Sbostic  *
5*46365Sbostic  * This code is derived from software contributed to Berkeley by
6*46365Sbostic  * Margo Seltzer.
7*46365Sbostic  *
8*46365Sbostic  * %sccs.include.redist.c%
9*46365Sbostic  */
10*46365Sbostic 
11*46365Sbostic #if defined(LIBC_SCCS) && !defined(lint)
12*46365Sbostic static char sccsid[] = "@(#)hash_buf.c	5.1 (Berkeley) 02/12/91";
13*46365Sbostic #endif /* LIBC_SCCS and not lint */
14*46365Sbostic 
15*46365Sbostic /******************************************************************************
16*46365Sbostic 
17*46365Sbostic PACKAGE: hash
18*46365Sbostic 
19*46365Sbostic DESCRIPTION:
20*46365Sbostic 	Contains buffer management
21*46365Sbostic 
22*46365Sbostic ROUTINES:
23*46365Sbostic     External
24*46365Sbostic 	__buf_init
25*46365Sbostic 	__get_buf
26*46365Sbostic 	__buf_free
27*46365Sbostic 	__reclaim_buf
28*46365Sbostic     Internal
29*46365Sbostic 	newbuf
30*46365Sbostic 
31*46365Sbostic ******************************************************************************/
32*46365Sbostic #include <sys/param.h>
33*46365Sbostic #include <sys/file.h>
34*46365Sbostic #include <assert.h>
35*46365Sbostic #include <errno.h>
36*46365Sbostic #include "hash.h"
37*46365Sbostic #include <stdio.h>
38*46365Sbostic 
39*46365Sbostic /* Externals */
40*46365Sbostic extern HTAB	*hashp;
41*46365Sbostic 
42*46365Sbostic /* My internals */
43*46365Sbostic static BUFHEAD *newbuf();
44*46365Sbostic 
45*46365Sbostic /* Unlink B from its place in the lru */
46*46365Sbostic #define BUF_REMOVE(B)			\
47*46365Sbostic {					\
48*46365Sbostic     B->prev->next = B->next;		\
49*46365Sbostic     B->next->prev = B->prev;		\
50*46365Sbostic }
51*46365Sbostic 
52*46365Sbostic /* Insert B after P */
53*46365Sbostic #define BUF_INSERT(B,P)			\
54*46365Sbostic {					\
55*46365Sbostic     B->next = P->next;			\
56*46365Sbostic     B->prev = P;			\
57*46365Sbostic     P->next = B;			\
58*46365Sbostic     B->next->prev = B;			\
59*46365Sbostic }
60*46365Sbostic 
61*46365Sbostic #define	MRU	hashp->bufhead.next
62*46365Sbostic #define	LRU	hashp->bufhead.prev
63*46365Sbostic 
64*46365Sbostic #define MRU_INSERT(B)	BUF_INSERT(B,(&hashp->bufhead))
65*46365Sbostic #define LRU_INSERT(B)	BUF_INSERT(B,LRU)
66*46365Sbostic 
67*46365Sbostic /*
68*46365Sbostic     We are looking for a buffer with address "addr".
69*46365Sbostic     If prev_bp is NULL, then address is a bucket index.
70*46365Sbostic     If prev_bp is not NULL, then it points to the page previous
71*46365Sbostic 	to an overflow page that we are trying to find.
72*46365Sbostic 
73*46365Sbostic     CAVEAT:  The buffer header accessed via prev_bp's ovfl field
74*46365Sbostic     may no longer be valid.  Therefore, you must always verify that
75*46365Sbostic     its address matches the address you are seeking.
76*46365Sbostic */
77*46365Sbostic extern BUFHEAD *
78*46365Sbostic __get_buf ( addr, prev_bp, newpage )
79*46365Sbostic int	addr;
80*46365Sbostic BUFHEAD	*prev_bp;
81*46365Sbostic int	newpage;		/* If prev_bp is set, indicates that this is
82*46365Sbostic 					a new overflow page */
83*46365Sbostic {
84*46365Sbostic     register int	segment_ndx;
85*46365Sbostic     register	BUFHEAD	*bp;
86*46365Sbostic     register	unsigned	is_disk = 0;
87*46365Sbostic     SEGMENT	segp;
88*46365Sbostic 
89*46365Sbostic     if ( prev_bp ) {
90*46365Sbostic 	bp = prev_bp->ovfl;
91*46365Sbostic 	if ( !bp || (bp->addr != addr) ) bp = NULL;
92*46365Sbostic 	if ( !newpage ) is_disk = BUF_DISK;
93*46365Sbostic     }
94*46365Sbostic     else {
95*46365Sbostic 	/* Grab buffer out of directory */
96*46365Sbostic 	segment_ndx = addr & ( hashp->SGSIZE - 1 );
97*46365Sbostic 
98*46365Sbostic 	/*
99*46365Sbostic 	 * valid segment ensured by __call_hash()
100*46365Sbostic 	 */
101*46365Sbostic 	segp = hashp->dir[addr >> hashp->SSHIFT];
102*46365Sbostic #ifdef DEBUG
103*46365Sbostic 	assert(segp != NULL);
104*46365Sbostic #endif
105*46365Sbostic 	bp = PTROF(segp[segment_ndx]);
106*46365Sbostic 	is_disk = ISDISK(segp[segment_ndx]);
107*46365Sbostic     }
108*46365Sbostic 
109*46365Sbostic     if ( !bp ) {
110*46365Sbostic 	bp = newbuf ( addr, prev_bp );
111*46365Sbostic 	if ( !bp || __get_page ( bp->page, addr, !prev_bp, (int)is_disk, 0 )) {
112*46365Sbostic 	    return(NULL);
113*46365Sbostic 	}
114*46365Sbostic 	if ( !prev_bp ) {
115*46365Sbostic 	    segp[segment_ndx] = (BUFHEAD *)((unsigned)bp | is_disk);
116*46365Sbostic 	}
117*46365Sbostic     } else {
118*46365Sbostic 	BUF_REMOVE ( bp );
119*46365Sbostic 	MRU_INSERT ( bp );
120*46365Sbostic     }
121*46365Sbostic     return(bp);
122*46365Sbostic }
123*46365Sbostic 
124*46365Sbostic /*
125*46365Sbostic     We need a buffer for this page. Either allocate one, or
126*46365Sbostic     evict a resident one (if we have as many buffers as we're
127*46365Sbostic     allowed) and put this one in.
128*46365Sbostic 
129*46365Sbostic     If newbuf finds an error (returning NULL), it also sets errno
130*46365Sbostic */
131*46365Sbostic static BUFHEAD *
132*46365Sbostic newbuf ( addr, prev_bp )
133*46365Sbostic int	addr;
134*46365Sbostic BUFHEAD	*prev_bp;
135*46365Sbostic {
136*46365Sbostic     register	BUFHEAD	*bp;	/* The buffer we're going to use */
137*46365Sbostic     register	BUFHEAD	*xbp;	/* Temp pointer */
138*46365Sbostic     register	BUFHEAD *next_xbp;
139*46365Sbostic     int	segment_ndx;
140*46365Sbostic     u_short	*shortp;
141*46365Sbostic     u_short	oaddr;
142*46365Sbostic     SEGMENT	segp;
143*46365Sbostic 
144*46365Sbostic     if ( hashp->nbufs ) {
145*46365Sbostic 	/* Allocate a new one */
146*46365Sbostic 	bp = (BUFHEAD *)malloc ( sizeof (struct _bufhead) );
147*46365Sbostic 	if ( !bp || !(bp->page = (char *)malloc ( hashp->BSIZE )) ) {
148*46365Sbostic 	    return (NULL);
149*46365Sbostic 	}
150*46365Sbostic 	hashp->nbufs--;
151*46365Sbostic     } else {
152*46365Sbostic 	/* Kick someone out */
153*46365Sbostic 	bp = LRU;
154*46365Sbostic 	BUF_REMOVE( bp );
155*46365Sbostic 	/*
156*46365Sbostic 	    Set oaddr before __put_page so that you get it
157*46365Sbostic 	    before bytes are swapped
158*46365Sbostic 	*/
159*46365Sbostic 	shortp = (u_short *)bp->page;
160*46365Sbostic 	oaddr = shortp[shortp[0]-1];
161*46365Sbostic 	if ( (bp->flags & BUF_MOD) &&
162*46365Sbostic 	     __put_page(bp->page, bp->addr, (int)IS_BUCKET(bp->flags), 0) ) {
163*46365Sbostic 	    return(NULL);
164*46365Sbostic 	}
165*46365Sbostic 	/*
166*46365Sbostic 	    Update the pointer to this page (i.e. invalidate it).
167*46365Sbostic 
168*46365Sbostic 	    If this is a new file (i.e. we created it at open time),
169*46365Sbostic 	    make sure that we mark pages which have been written to
170*46365Sbostic 	    disk so we retrieve them from disk later, rather than
171*46365Sbostic 	    allocating new pages.
172*46365Sbostic 	*/
173*46365Sbostic 
174*46365Sbostic 	if ( IS_BUCKET(bp->flags)) {
175*46365Sbostic 	    segment_ndx = bp->addr & ( hashp->SGSIZE - 1 );
176*46365Sbostic 
177*46365Sbostic 	    segp = hashp->dir[bp->addr >> hashp->SSHIFT];
178*46365Sbostic 
179*46365Sbostic 	    assert(segp != NULL);
180*46365Sbostic 
181*46365Sbostic 	    if ( hashp->new_file &&
182*46365Sbostic 		 ((bp->flags & BUF_MOD) || ISDISK(segp[segment_ndx])) ) {
183*46365Sbostic 		segp[segment_ndx] = (BUFHEAD *)BUF_DISK;
184*46365Sbostic 	    } else segp[segment_ndx] = NULL;
185*46365Sbostic 	}
186*46365Sbostic 
187*46365Sbostic 	/*
188*46365Sbostic 	    Since overflow pages can only be access by means of
189*46365Sbostic 	    their bucket, free overflow pages associated with this
190*46365Sbostic 	    bucket.
191*46365Sbostic 	*/
192*46365Sbostic 	for ( xbp = bp; xbp->ovfl; ) {
193*46365Sbostic 
194*46365Sbostic 	    next_xbp = xbp->ovfl;
195*46365Sbostic 	    xbp->ovfl = 0;
196*46365Sbostic 	    xbp = next_xbp;
197*46365Sbostic 
198*46365Sbostic 	    /* Check that ovfl pointer is up date */
199*46365Sbostic 	    if ( IS_BUCKET(xbp->flags) || (oaddr != xbp->addr) ) break;
200*46365Sbostic 
201*46365Sbostic 	    shortp = (u_short *)xbp->page;
202*46365Sbostic 	    oaddr = shortp[shortp[0]-1];	/* set before __put_page */
203*46365Sbostic 	    if ( (xbp->flags & BUF_MOD) &&
204*46365Sbostic 		__put_page ( xbp->page, xbp->addr, 0, 0 ) ) {
205*46365Sbostic 		return(NULL);
206*46365Sbostic 	    }
207*46365Sbostic 	    xbp->addr = 0;
208*46365Sbostic 	    xbp->flags = 0;
209*46365Sbostic 	    BUF_REMOVE ( xbp );
210*46365Sbostic 	    LRU_INSERT ( xbp );
211*46365Sbostic 	}
212*46365Sbostic     }
213*46365Sbostic 
214*46365Sbostic     /* Now assign this buffer */
215*46365Sbostic     bp->addr = addr;
216*46365Sbostic #ifdef DEBUG1
217*46365Sbostic     fprintf ( stderr, "NEWBUF1: %d->ovfl was %d is now %d\n", bp->addr,
218*46365Sbostic 		(bp->ovfl?bp->ovfl->addr:0),  0);
219*46365Sbostic #endif
220*46365Sbostic     bp->ovfl = NULL;
221*46365Sbostic     if ( prev_bp ) {
222*46365Sbostic 	/*
223*46365Sbostic 	    If prev_bp is set, this is an overflow page, hook it in to the
224*46365Sbostic 	    buffer overflow links
225*46365Sbostic 	*/
226*46365Sbostic #ifdef DEBUG1
227*46365Sbostic 	fprintf ( stderr, "NEWBUF2: %d->ovfl was %d is now %d\n", prev_bp->addr,
228*46365Sbostic 		    (prev_bp->ovfl?bp->ovfl->addr:0),
229*46365Sbostic 		    (bp?bp->addr: 0));
230*46365Sbostic #endif
231*46365Sbostic 	prev_bp->ovfl = bp;
232*46365Sbostic 	bp->flags = 0;
233*46365Sbostic     } else bp->flags = BUF_BUCKET;
234*46365Sbostic     MRU_INSERT ( bp );
235*46365Sbostic     return ( bp );
236*46365Sbostic }
237*46365Sbostic 
238*46365Sbostic extern void
239*46365Sbostic __buf_init ( nbytes )
240*46365Sbostic int	nbytes;
241*46365Sbostic {
242*46365Sbostic     int	npages;
243*46365Sbostic     BUFHEAD	*bfp = &(hashp->bufhead);
244*46365Sbostic 
245*46365Sbostic     npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT;
246*46365Sbostic     npages = MAX ( npages, MIN_BUFFERS );
247*46365Sbostic 
248*46365Sbostic     hashp->nbufs = npages;
249*46365Sbostic     bfp->next = bfp;
250*46365Sbostic     bfp->prev = bfp;
251*46365Sbostic     /*
252*46365Sbostic 	This space is calloc'd so these are already null
253*46365Sbostic 
254*46365Sbostic 	bfp->ovfl = NULL;
255*46365Sbostic 	bfp->flags = 0;
256*46365Sbostic 	bfp->page = NULL;
257*46365Sbostic 	bfp->addr = 0;
258*46365Sbostic     */
259*46365Sbostic }
260*46365Sbostic 
261*46365Sbostic extern int
262*46365Sbostic __buf_free ( do_free, to_disk )
263*46365Sbostic int	do_free;
264*46365Sbostic int	to_disk;
265*46365Sbostic {
266*46365Sbostic     BUFHEAD	*bp;
267*46365Sbostic 
268*46365Sbostic     /* Need to make sure that buffer manager has been initialized */
269*46365Sbostic     if ( !LRU ) {
270*46365Sbostic 	return(0);
271*46365Sbostic     }
272*46365Sbostic 
273*46365Sbostic     for ( bp = LRU; bp != &hashp->bufhead; ) {
274*46365Sbostic 	/* Check that the buffer is valid */
275*46365Sbostic 	if ( bp->addr || IS_BUCKET(bp->flags) ) {
276*46365Sbostic 	    if ( to_disk && (bp->flags & BUF_MOD) &&
277*46365Sbostic 		 __put_page (bp->page, bp->addr, IS_BUCKET(bp->flags), 0 )) {
278*46365Sbostic 		return (-1);
279*46365Sbostic 	    }
280*46365Sbostic 	}
281*46365Sbostic 
282*46365Sbostic 	/* Check if we are freeing stuff */
283*46365Sbostic 	if ( do_free ) {
284*46365Sbostic 	    if ( bp->page ) free ( bp->page );
285*46365Sbostic 	    BUF_REMOVE(bp);
286*46365Sbostic 	    (void)free ( bp );
287*46365Sbostic 	    bp = LRU;
288*46365Sbostic 	} else bp = bp->prev;
289*46365Sbostic     }
290*46365Sbostic 
291*46365Sbostic     return(0);
292*46365Sbostic }
293*46365Sbostic 
294*46365Sbostic extern void
295*46365Sbostic __reclaim_buf ( bp )
296*46365Sbostic BUFHEAD	*bp;
297*46365Sbostic {
298*46365Sbostic     bp->ovfl = 0;
299*46365Sbostic     bp->addr = 0;
300*46365Sbostic     bp->flags = 0;
301*46365Sbostic     BUF_REMOVE ( bp );
302*46365Sbostic     LRU_INSERT ( bp );
303*46365Sbostic }
304