2 * Copyright (c) 1990 The Regents of the University of California.
5 * This code is derived from software contributed to Berkeley by
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 #if defined(LIBC_SCCS) && !defined(lint)
38 char sccsid
[] = "@(#)lrucache.c 5.3 (Berkeley) 2/22/91";
39 #endif /* LIBC_SCCS and not lint */
42 * lrucache.c -- LRU cache for disk-based btree pages.
44 * This file implements an LRU cache in user space for disk-based
47 #include <sys/param.h>
54 * LRUINIT -- Initialize a new LRU cache.
56 * There's a separate LRU cache for every open file descriptor on which
57 * the user wants caching. The desired cache size and logical page
58 * size are passed in. We try to avoid growing the cache beyond the
59 * limit specified by the user, but if we cannot satisfy a block request
60 * without growing the cache, we do so.
62 * Note that the page size passed in is the logical page size for
63 * use with this file descriptor, and doesn't necessarily have anything
64 * to do with the underlying hardware page size.
67 * fd -- file descriptor for this cache
68 * cachesz -- number of buffers in cache (suggested)
69 * pagesz -- logical page size inside this file
70 * inproc -- routine to call when a buffer is read
71 * outproc -- routine to call when a buffer is written
74 * Opaque pointer to the LRU cache on success, NULL on failure.
77 * Allocates memory for the hash table and LRU cache. Buffers
78 * are allocated on demand, later.
81 lruinit(fd
, cachesz
, pagesz
, opaque
, inproc
, outproc
)
92 /* allocate the LRU cache struct */
93 if ((l
= (LRUCACHE
*) malloc((unsigned) sizeof(LRUCACHE
)))
97 /* allocate the hash table */
98 nbytes
= cachesz
* sizeof(CACHE_ENT
*);
99 if ((l
->lru_cache
= (CACHE_ENT
**) malloc((unsigned) nbytes
))
100 == (CACHE_ENT
**) NULL
) {
101 (void) free((char *) l
);
106 bzero((char *) (l
->lru_cache
), (size_t) nbytes
);
108 l
->lru_psize
= pagesz
;
109 l
->lru_csize
= cachesz
;
111 l
->lru_opaque
= opaque
;
112 l
->lru_head
= l
->lru_tail
= (LRU_ENT
*) NULL
;
113 l
->lru_inproc
= inproc
;
114 l
->lru_outproc
= outproc
;
120 * LRUGET -- Get a buffer from an LRU cache.
122 * If the buffer is not in the cache at present, this routine will
123 * instantiate it from the file. This REQUIRES that the desired
124 * block actually be on disk; we don't do non-blocking reads, so
125 * if it's not actually out there, this routine won't return for
126 * a very long time. In order to instantiate a new buffer, use
130 * lru -- the LRU cache to use.
131 * pgno -- the logical block number to get.
132 * nread -- pointer to an int, in which the number of bytes
136 * (char *) pointer to the buffer for the desired block. The
137 * number of bytes actually read is returned in *nread.
140 * The requested buffer is locked down until the user does
141 * an explicit lrurelease() on it.
145 lruget(lru
, pgno
, nread
)
150 LRUCACHE
*l
= (LRUCACHE
*) lru
;
156 /* is it already in the cache? */
157 if ((ce
= lruhashget(l
, pgno
)) != (CACHE_ENT
*) NULL
) {
159 /* yes, move it to the head and return it */
160 lruent
= ce
->c_lruent
;
161 lruent
->l_flags
&= ~F_FREE
;
162 lruhead(l
, ce
->c_lruent
);
163 *nread
= l
->lru_psize
;
164 return (ce
->c_lruent
->l_buffer
);
167 /* not there, get a free page */
168 if ((buffer
= lrugetpg(l
, pgno
, nread
, lruget
)) == (char *) NULL
)
169 return ((char *) NULL
);
171 /* okay, got a buffer -- fill it */
172 pos
= (long) (l
->lru_psize
* pgno
);
173 if (lseek(l
->lru_fd
, pos
, L_SET
) != pos
)
174 return ((char *) NULL
);
176 *nread
= read(l
->lru_fd
, buffer
, l
->lru_psize
);
179 (*(l
->lru_inproc
))(buffer
, l
->lru_opaque
);
185 * LRUGETNEW -- Get a page for a new block in an LRU cache.
187 * This routine allows users to instantiate pages for a file if they
188 * don't exist on disk yet. It's used to make a file bigger.
191 * lru -- the LRU cache to use
192 * pgno -- the (new) logical page number to instantiate
193 * nread -- ptr to int to get number of bytes read (this is
194 * guaranteed to be zero, since the page isn't on disk)
197 * Pointer to a buffer for the associated page, or NULL on
201 * The associated buffer is locked down until the user
202 * explicitly does an lrurelease() on it.
206 lrugetnew(lru
, pgno
, nread
)
211 LRUCACHE
*l
= (LRUCACHE
*) lru
;
214 return (lrugetpg(l
, pgno
, nread
, lrugetnew
));
218 * LRUFLUSH -- flush an LRU page to disk.
220 * This routine forces a page to disk. Users should use lruwrite(),
221 * which simply marks a page dirty and does late writing.
225 * lruent -- the LRU cache entry whose page we should flush
228 * Zero on success, -1 on failure.
238 (*(l
->lru_outproc
))(lruent
->l_buffer
, l
->lru_opaque
);
240 pos
= (long) (l
->lru_psize
* lruent
->l_pgno
);
241 if (lseek(l
->lru_fd
, pos
, L_SET
) != pos
)
243 if (write(l
->lru_fd
, lruent
->l_buffer
, l
->lru_psize
) != l
->lru_psize
)
247 (*(l
->lru_inproc
))(lruent
->l_buffer
, l
->lru_opaque
);
249 lruent
->l_flags
&= ~F_DIRTY
;
254 * LRUWRITE -- Mark an LRU cache buffer dirty
256 * This routine is how users should move their changes to disk. The
257 * cache code marks the associated buffer dirty, and flushes it to
258 * disk if we need to reuse the buffer for some other page.
262 * pgno -- page number to flush
265 * Zero on success, -1 on failure.
273 LRUCACHE
*l
= (LRUCACHE
*) lru
;
276 if ((ce
= lruhashget(l
, pgno
)) == (CACHE_ENT
*) NULL
)
279 /* mark the entry dirty */
280 ce
->c_lruent
->l_flags
|= F_DIRTY
;
286 * LRUSYNC -- Flush all changes to disk
288 * This routine allows users to force all changes to buffers currently
289 * in memory to disk. This is expensive.
292 * lru -- LRU cache to flush
295 * Zero on success, -1 on failure
298 * After this call, all buffers are clean.
305 LRUCACHE
*l
= (LRUCACHE
*) lru
;
308 for (le
= l
->lru_head
; le
!= (LRU_ENT
*) NULL
; le
= le
->l_next
)
309 if (le
->l_flags
& F_DIRTY
)
310 if (lruflush(l
, le
) < 0)
316 * LRURELEASE -- Release a buffer in the LRU cache for reuse
318 * When the user does an lruget() or lrugetnew(), the buffer we return
319 * is locked down, to guarantee that it's not reused while the user
320 * still needs it. Once a buffer is no longer needed, it should be
321 * released (via this routine) so that we can use it for other pages
326 * pgno -- page number of buffer to release
329 * Zero on success, -1 on failure
333 lrurelease(lru
, pgno
)
337 LRUCACHE
*l
= (LRUCACHE
*) lru
;
340 if ((ce
= lruhashget(l
, pgno
)) == (CACHE_ENT
*) NULL
)
342 ce
->c_lruent
->l_flags
|= F_FREE
;
347 * LRUFREE -- Free an entire LRU cache
349 * This routine releases an LRU cache. The cache should not be
353 * lru -- LRU cache to free
359 * Frees a lot of memory.
366 LRUCACHE
*l
= (LRUCACHE
*) lru
;
370 for (le
= l
->lru_head
; le
!= (LRU_ENT
*) NULL
; ) {
371 free((char *) (le
->l_buffer
));