xref: /original-bsd/lib/libc/db/btree/lrucache.c (revision 08eb28af)
1 /*-
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Mike Olson.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #if defined(LIBC_SCCS) && !defined(lint)
12 char sccsid[] = "@(#)lrucache.c	5.3 (Berkeley) 02/22/91";
13 #endif /* LIBC_SCCS and not lint */
14 
15 /*
16  *  lrucache.c -- LRU cache for disk-based btree pages.
17  *
18  *	This file implements an LRU cache in user space for disk-based
19  *	btrees.
20  */
21 #include <sys/param.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include "lrucache.h"
26 
27 /*
28  *  LRUINIT -- Initialize a new LRU cache.
29  *
30  *	There's a separate LRU cache for every open file descriptor on which
31  *	the user wants caching.  The desired cache size and logical page
32  *	size are passed in.  We try to avoid growing the cache beyond the
33  *	limit specified by the user, but if we cannot satisfy a block request
34  *	without growing the cache, we do so.
35  *
36  *	Note that the page size passed in is the logical page size for
37  *	use with this file descriptor, and doesn't necessarily have anything
38  *	to do with the underlying hardware page size.
39  *
40  *	Parameters:
41  *		fd -- file descriptor for this cache
42  *		cachesz -- number of buffers in cache (suggested)
43  *		pagesz -- logical page size inside this file
44  *		inproc -- routine to call when a buffer is read
45  *		outproc -- routine to call when a buffer is written
46  *
47  *	Returns:
48  *		Opaque pointer to the LRU cache on success, NULL on failure.
49  *
50  *	Side Effects:
51  *		Allocates memory for the hash table and LRU cache.  Buffers
52  *		are allocated on demand, later.
53  */
54 LRU
55 lruinit(fd, cachesz, pagesz, opaque, inproc, outproc)
56 	int fd;
57 	int cachesz;
58 	int pagesz;
59 	char *opaque;
60 	int (*inproc)();
61 	int (*outproc)();
62 {
63 	register LRUCACHE *l;
64 	int nbytes;
65 
66 	/* allocate the LRU cache struct */
67 	if ((l = (LRUCACHE *) malloc((unsigned) sizeof(LRUCACHE)))
68 	    == (LRUCACHE *) NULL)
69 		return ((LRU) NULL);
70 
71 	/* allocate the hash table */
72 	nbytes = cachesz * sizeof(CACHE_ENT *);
73 	if ((l->lru_cache = (CACHE_ENT **) malloc((unsigned) nbytes))
74 	    == (CACHE_ENT **) NULL) {
75 		(void) free((char *) l);
76 		return ((LRU) NULL);
77 	}
78 
79 	/* init memory */
80 	bzero((char *) (l->lru_cache), (size_t) nbytes);
81 	l->lru_fd = fd;
82 	l->lru_psize = pagesz;
83 	l->lru_csize = cachesz;
84 	l->lru_cursz = 0;
85 	l->lru_opaque = opaque;
86 	l->lru_head = l->lru_tail = (LRU_ENT *) NULL;
87 	l->lru_inproc = inproc;
88 	l->lru_outproc = outproc;
89 
90 	return ((LRU) l);
91 }
92 
93 /*
94  *  LRUGET -- Get a buffer from an LRU cache.
95  *
96  *	If the buffer is not in the cache at present, this routine will
97  *	instantiate it from the file.  This REQUIRES that the desired
98  *	block actually be on disk; we don't do non-blocking reads, so
99  *	if it's not actually out there, this routine won't return for
100  *	a very long time.  In order to instantiate a new buffer, use
101  *	lrugetnew().
102  *
103  *	Parameters:
104  *		lru -- the LRU cache to use.
105  *		pgno -- the logical block number to get.
106  *		nread -- pointer to an int, in which the number of bytes
107  *			 read is returned.
108  *
109  *	Returns:
110  *		(char *) pointer to the buffer for the desired block.  The
111  *		number of bytes actually read is returned in *nread.
112  *
113  *	Warnings:
114  *		The requested buffer is locked down until the user does
115  *		an explicit lrurelease() on it.
116  */
117 
118 char *
119 lruget(lru, pgno, nread)
120 	LRU lru;
121 	int pgno;
122 	int *nread;
123 {
124 	LRUCACHE *l = (LRUCACHE *) lru;
125 	CACHE_ENT *ce;
126 	LRU_ENT *lruent;
127 	char *buffer;
128 	long pos;
129 
130 	/* is it already in the cache? */
131 	if ((ce = lruhashget(l, pgno)) != (CACHE_ENT *) NULL) {
132 
133 		/* yes, move it to the head and return it */
134 		lruent = ce->c_lruent;
135 		lruent->l_flags &= ~F_FREE;
136 		lruhead(l, ce->c_lruent);
137 		*nread = l->lru_psize;
138 		return (ce->c_lruent->l_buffer);
139 	}
140 
141 	/* not there, get a free page */
142 	if ((buffer = lrugetpg(l, pgno, nread, lruget)) == (char *) NULL)
143 		return ((char *) NULL);
144 
145 	/* okay, got a buffer -- fill it */
146 	pos = (long) (l->lru_psize * pgno);
147 	if (lseek(l->lru_fd, pos, L_SET) != pos)
148 		return ((char *) NULL);
149 
150 	*nread = read(l->lru_fd, buffer, l->lru_psize);
151 
152 	if (l->lru_inproc)
153 		(*(l->lru_inproc))(buffer, l->lru_opaque);
154 
155 	return (buffer);
156 }
157 
158 /*
159  *  LRUGETNEW -- Get a page for a new block in an LRU cache.
160  *
161  *	This routine allows users to instantiate pages for a file if they
162  *	don't exist on disk yet.  It's used to make a file bigger.
163  *
164  *	Parameters:
165  *		lru -- the LRU cache to use
166  *		pgno -- the (new) logical page number to instantiate
167  *		nread -- ptr to int to get number of bytes read (this is
168  *			 guaranteed to be zero, since the page isn't on disk)
169  *
170  *	Returns:
171  *		Pointer to a buffer for the associated page, or NULL on
172  *		failure.
173  *
174  *	Warnings:
175  *		The associated buffer is locked down until the user
176  *		explicitly does an lrurelease() on it.
177  */
178 
179 char *
180 lrugetnew(lru, pgno, nread)
181 	LRU lru;
182 	int pgno;
183 	int *nread;
184 {
185 	LRUCACHE *l = (LRUCACHE *) lru;
186 
187 	*nread = 0;
188 	return (lrugetpg(l, pgno, nread, lrugetnew));
189 }
190 
191 /*
192  *  LRUFLUSH -- flush an LRU page to disk.
193  *
194  *	This routine forces a page to disk.  Users should use lruwrite(),
195  *	which simply marks a page dirty and does late writing.
196  *
197  *	Parameters:
198  *		l -- LRU cache
199  *		lruent -- the LRU cache entry whose page we should flush
200  *
201  *	Returns:
202  *		Zero on success, -1 on failure.
203  */
204 
205 lruflush(l, lruent)
206 	LRUCACHE *l;
207 	LRU_ENT *lruent;
208 {
209 	long pos;
210 
211 	if (l->lru_outproc)
212 		(*(l->lru_outproc))(lruent->l_buffer, l->lru_opaque);
213 
214 	pos = (long) (l->lru_psize * lruent->l_pgno);
215 	if (lseek(l->lru_fd, pos, L_SET) != pos)
216 		return (-1);
217 	if (write(l->lru_fd, lruent->l_buffer, l->lru_psize) != l->lru_psize)
218 		return (-1);
219 
220 	if (l->lru_inproc)
221 		(*(l->lru_inproc))(lruent->l_buffer, l->lru_opaque);
222 
223 	lruent->l_flags &= ~F_DIRTY;
224 	return (0);
225 }
226 
227 /*
228  *  LRUWRITE -- Mark an LRU cache buffer dirty
229  *
230  *	This routine is how users should move their changes to disk.  The
231  *	cache code marks the associated buffer dirty, and flushes it to
232  *	disk if we need to reuse the buffer for some other page.
233  *
234  *	Parameters:
235  *		lru -- LRU cache
236  *		pgno -- page number to flush
237  *
238  *	Returns:
239  *		Zero on success, -1 on failure.
240  */
241 
242 int
243 lruwrite(lru, pgno)
244 	LRU lru;
245 	int pgno;
246 {
247 	LRUCACHE *l = (LRUCACHE *) lru;
248 	CACHE_ENT *ce;
249 
250 	if ((ce = lruhashget(l, pgno)) == (CACHE_ENT *) NULL)
251 		return (-1);
252 
253 	/* mark the entry dirty */
254 	ce->c_lruent->l_flags |= F_DIRTY;
255 
256 	return (0);
257 }
258 
259 /*
260  *  LRUSYNC -- Flush all changes to disk
261  *
262  *	This routine allows users to force all changes to buffers currently
263  *	in memory to disk.  This is expensive.
264  *
265  *	Parameters:
266  *		lru -- LRU cache to flush
267  *
268  *	Returns:
269  *		Zero on success, -1 on failure
270  *
271  *	Side Effects:
272  *		After this call, all buffers are clean.
273  */
274 
275 int
276 lrusync(lru)
277 	LRU lru;
278 {
279 	LRUCACHE *l = (LRUCACHE *) lru;
280 	LRU_ENT *le;
281 
282 	for (le = l->lru_head; le != (LRU_ENT *) NULL; le = le->l_next)
283 		if (le->l_flags & F_DIRTY)
284 			if (lruflush(l, le) < 0)
285 				return (-1);
286 	return (0);
287 }
288 
289 /*
290  *  LRURELEASE -- Release a buffer in the LRU cache for reuse
291  *
292  *	When the user does an lruget() or lrugetnew(), the buffer we return
293  *	is locked down, to guarantee that it's not reused while the user
294  *	still needs it.  Once a buffer is no longer needed, it should be
295  *	released (via this routine) so that we can use it for other pages
296  *	if necessary.
297  *
298  *	Parameters:
299  *		lru -- LRU cache
300  *		pgno -- page number of buffer to release
301  *
302  *	Returns:
303  *		Zero on success, -1 on failure
304  */
305 
306 int
307 lrurelease(lru, pgno)
308 	LRU lru;
309 	int pgno;
310 {
311 	LRUCACHE *l = (LRUCACHE *) lru;
312 	CACHE_ENT *ce;
313 
314 	if ((ce = lruhashget(l, pgno)) == (CACHE_ENT *) NULL)
315 		return (-1);
316 	ce->c_lruent->l_flags |= F_FREE;
317 	return (0);
318 }
319 
320 /*
321  *  LRUFREE -- Free an entire LRU cache
322  *
323  *	This routine releases an LRU cache.  The cache should not be
324  *	used again.
325  *
326  *	Parameters:
327  *		lru -- LRU cache to free
328  *
329  *	Returns:
330  *		None.
331  *
332  *	Side Effects:
333  *		Frees a lot of memory.
334  */
335 
336 void
337 lrufree(lru)
338 	LRU lru;
339 {
340 	LRUCACHE *l = (LRUCACHE *) lru;
341 	LRU_ENT *le;
342 	LRU_ENT *sle;
343 
344 	for (le = l->lru_head; le != (LRU_ENT *) NULL; ) {
345 		free((char *) (le->l_buffer));
346 		sle = le;
347 		le = le->l_next;
348 		free((char *) sle);
349 	}
350 	free ((char *) l);
351 }
352