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