xref: /csrg-svn/lib/libc/db/btree/lrutils.c (revision 46138)
1*46138Smao /*-
2*46138Smao  * Copyright (c) 1990 The Regents of the University of California.
3*46138Smao  * All rights reserved.
4*46138Smao  *
5*46138Smao  * This code is derived from software contributed to Berkeley by
6*46138Smao  * Mike Olson.
7*46138Smao  *
8*46138Smao  * %sccs.include.redist.c%
9*46138Smao  */
10*46138Smao 
11*46138Smao #if defined(LIBC_SCCS) && !defined(lint)
12*46138Smao static char sccsid[] = "@(#)lrutils.c	5.1 (Berkeley) 01/23/91";
13*46138Smao #endif /* LIBC_SCCS and not lint */
14*46138Smao 
15*46138Smao #include <sys/types.h>
16*46138Smao #include "lrucache.h"
17*46138Smao 
18*46138Smao /*
19*46138Smao  *  LRUGETPG -- Get a free page from the LRU cache.
20*46138Smao  *
21*46138Smao  *	This routine grows the cache if necessary, finds an unused page if
22*46138Smao  *	it can, and handles flushing dirty buffers to disk.
23*46138Smao  *
24*46138Smao  *	One of the parameters to this routine (f) is the routine that called
25*46138Smao  *	us.  If we have to grow the cache, we call this routine recursively
26*46138Smao  *	in order to fill the buffer.  The reason for this is that we have
27*46138Smao  *	two interfaces that call lrugetpg().  Lruget() fills a page from disk,
28*46138Smao  *	and lrugetnew() just allocates a new (empty) page.
29*46138Smao  *
30*46138Smao  *	Parameters:
31*46138Smao  *		l -- LRU cache to use.
32*46138Smao  *		pgno -- page number for which we want a buffer
33*46138Smao  *		nread -- pointer to an int to get number of bytes read
34*46138Smao  *		f -- who called us
35*46138Smao  *
36*46138Smao  *	Returns:
37*46138Smao  *		(char *) pointer to buffer to use, or NULL on failure.
38*46138Smao  *
39*46138Smao  *	Warnings:
40*46138Smao  *		The buffer returned is locked down until the user does an
41*46138Smao  *		explicit lrurelease() on it.
42*46138Smao  */
43*46138Smao 
44*46138Smao char *
45*46138Smao lrugetpg(l, pgno, nread, f)
46*46138Smao 	LRUCACHE *l;
47*46138Smao 	int pgno;
48*46138Smao 	int *nread;
49*46138Smao 	char *(*f)();
50*46138Smao {
51*46138Smao 	CACHE_ENT *ce;
52*46138Smao 	LRU_ENT *lruent;
53*46138Smao 	char *buffer;
54*46138Smao 
55*46138Smao 	/* if we're allowed to grow the cache, do so */
56*46138Smao 	if (l->lru_cursz < l->lru_csize) {
57*46138Smao 
58*46138Smao 		/* get a buffer */
59*46138Smao 		if ((buffer = (char *) malloc((unsigned) l->lru_psize))
60*46138Smao 		    == (char *) NULL)
61*46138Smao 			return ((char *) NULL);
62*46138Smao 
63*46138Smao 		/* get and LRU list entry */
64*46138Smao 		if ((lruent = (LRU_ENT *) malloc((unsigned) sizeof(LRU_ENT)))
65*46138Smao 		    == (LRU_ENT *) NULL)
66*46138Smao 			return ((char *) NULL);
67*46138Smao 		lruent->l_buffer = buffer;
68*46138Smao 		lruent->l_pgno = pgno;
69*46138Smao 		lruent->l_flags = NULL;
70*46138Smao 
71*46138Smao 		/* manage spaghetti */
72*46138Smao 		lruent->l_prev = (LRU_ENT *) NULL;
73*46138Smao 		lruent->l_next = l->lru_head;
74*46138Smao 		if (l->lru_head != (LRU_ENT *) NULL)
75*46138Smao 			l->lru_head->l_prev = lruent;
76*46138Smao 		l->lru_head = lruent;
77*46138Smao 		if (l->lru_tail == (LRU_ENT *) NULL)
78*46138Smao 			l->lru_tail = lruent;
79*46138Smao 
80*46138Smao 		/* add it to the hash table */
81*46138Smao 		ce = lruhashput(l, pgno, lruent);
82*46138Smao 		l->lru_cursz++;
83*46138Smao 	} else {
84*46138Smao 		lruent = l->lru_tail;
85*46138Smao 
86*46138Smao 		/* find the oldest unused buffer */
87*46138Smao 		while (lruent != (LRU_ENT *) NULL
88*46138Smao 		       && !(lruent->l_flags & F_FREE))
89*46138Smao 			lruent = lruent->l_prev;
90*46138Smao 
91*46138Smao 		/* if no free buffer was found, we have to grow the cache */
92*46138Smao 		if (lruent == (LRU_ENT *) NULL) {
93*46138Smao 			if (lrugrow(l) < 0)
94*46138Smao 				return ((char *) NULL);
95*46138Smao 			return ((*f)((LRU) l, pgno, nread));
96*46138Smao 		}
97*46138Smao 
98*46138Smao 		/* okay, found a free buffer -- update hash table and list */
99*46138Smao 		ce = lruhashget(l, lruent->l_pgno);
100*46138Smao 		if (lruhashdel(l, lruent->l_pgno) < 0)
101*46138Smao 			return ((char *) NULL);
102*46138Smao 
103*46138Smao 		/* flush the old page to disk, if necessary */
104*46138Smao 		if (lruent->l_flags & F_DIRTY)
105*46138Smao 			if (lruflush(l, lruent) < 0)
106*46138Smao 				return ((char *) NULL);
107*46138Smao 
108*46138Smao 		/* update stats, hash table, and list */
109*46138Smao 		lruent->l_pgno = pgno;
110*46138Smao 		lruhead(l, lruent);
111*46138Smao 		ce = lruhashput(l, pgno, lruent);
112*46138Smao 		buffer = lruent->l_buffer;
113*46138Smao 	}
114*46138Smao #ifdef lint
115*46138Smao 	ce = ce;
116*46138Smao #endif /* lint */
117*46138Smao 
118*46138Smao 	/* lock this page down */
119*46138Smao 	lruent->l_flags &= ~F_FREE;
120*46138Smao 
121*46138Smao 	return (buffer);
122*46138Smao }
123*46138Smao 
124*46138Smao /*
125*46138Smao  *  LRUHEAD -- Move an LRU list entry to the head of the list.
126*46138Smao  *
127*46138Smao  *	The head of the list is where the most recently used item goes.
128*46138Smao  *
129*46138Smao  *	Parameters:
130*46138Smao  *		l -- LRU cache
131*46138Smao  *		lruent -- entry to move to the head of the list
132*46138Smao  *
133*46138Smao  *	Returns:
134*46138Smao  *		None
135*46138Smao  *
136*46138Smao  *	Side Effects:
137*46138Smao  *		Updates the cache's head and tail pointers as required.
138*46138Smao  */
139*46138Smao 
140*46138Smao void
141*46138Smao lruhead(l, lruent)
142*46138Smao 	LRUCACHE *l;
143*46138Smao 	LRU_ENT *lruent;
144*46138Smao {
145*46138Smao 	LRU_ENT *next;
146*46138Smao 	LRU_ENT *prev;
147*46138Smao 
148*46138Smao 	if (l->lru_head == lruent)
149*46138Smao 		return;
150*46138Smao 
151*46138Smao 	next = lruent->l_next;
152*46138Smao 	prev = lruent->l_prev;
153*46138Smao 	lruent->l_prev = (LRU_ENT *) NULL;
154*46138Smao 	lruent->l_next = l->lru_head;
155*46138Smao 	l->lru_head->l_prev = lruent;
156*46138Smao 	l->lru_head = lruent;
157*46138Smao 
158*46138Smao 	prev->l_next = next;
159*46138Smao 	if (next != (LRU_ENT *) NULL)
160*46138Smao 		next->l_prev = prev;
161*46138Smao 
162*46138Smao 	if (l->lru_tail == lruent)
163*46138Smao 		l->lru_tail = prev;
164*46138Smao }
165*46138Smao 
166*46138Smao /*
167*46138Smao  *  LRUGROW -- Grow the LRU cache
168*46138Smao  *
169*46138Smao  *	This routine is called only if we can't satisfy a user's get() request
170*46138Smao  *	using an existing buffer.  We need to rebuild the hash table so that
171*46138Smao  *	subsequent lookups work correctly, since the cache size is used to
172*46138Smao  *	compute hash keys.
173*46138Smao  *
174*46138Smao  *	Parameters:
175*46138Smao  *		l -- LRU cache to grow
176*46138Smao  *
177*46138Smao  *	Returns:
178*46138Smao  *		Zero on success, -1 on failure
179*46138Smao  */
180*46138Smao 
181*46138Smao int
182*46138Smao lrugrow(l)
183*46138Smao 	LRUCACHE *l;
184*46138Smao {
185*46138Smao 	int oldsz, newsz;
186*46138Smao 	CACHE_ENT **new;
187*46138Smao 	CACHE_ENT *ce, *nce;
188*46138Smao 	int h;
189*46138Smao 	int i;
190*46138Smao 
191*46138Smao 	oldsz = l->lru_csize;
192*46138Smao 	newsz = l->lru_csize + 1;
193*46138Smao 
194*46138Smao 	/* get a new hash table */
195*46138Smao 	if ((new = (CACHE_ENT **) malloc((unsigned)newsz * sizeof(CACHE_ENT *)))
196*46138Smao 	    == (CACHE_ENT **) NULL)
197*46138Smao 		return (-1);
198*46138Smao 
199*46138Smao 	/* build the new hash table */
200*46138Smao 	bzero((char *) new, (size_t) (newsz * sizeof(CACHE_ENT *)));
201*46138Smao 	for (i = oldsz; --i >= 0; ) {
202*46138Smao 		for (ce = l->lru_cache[i]; ce != (CACHE_ENT *) NULL; ) {
203*46138Smao 			nce = ce->c_chain;
204*46138Smao 			h = ce->c_pgno % newsz;
205*46138Smao 			ce->c_chain = new[h];
206*46138Smao 			new[h] = ce;
207*46138Smao 			ce = nce;
208*46138Smao 		}
209*46138Smao 	}
210*46138Smao 
211*46138Smao 	/* get rid of the old hash table, and update the cache */
212*46138Smao 	free ((char *) (l->lru_cache));
213*46138Smao 	l->lru_cache = new;
214*46138Smao 	l->lru_csize = newsz;
215*46138Smao 
216*46138Smao 	return (0);
217*46138Smao }
218