145875Sbostic /*- 245875Sbostic * Copyright (c) 1990 The Regents of the University of California. 345875Sbostic * All rights reserved. 445875Sbostic * 545875Sbostic * This code is derived from software contributed to Berkeley by 645875Sbostic * Mike Olson. 745875Sbostic * 845875Sbostic * %sccs.include.redist.c% 945875Sbostic */ 1045875Sbostic 1145875Sbostic #if defined(LIBC_SCCS) && !defined(lint) 12*46127Smao char sccsid[] = "@(#)lrucache.c 5.2 (Berkeley) 01/23/91"; 1345875Sbostic #endif /* LIBC_SCCS and not lint */ 1445875Sbostic 1545875Sbostic /* 1645875Sbostic * lrucache.c -- LRU cache for disk-based btree pages. 1745875Sbostic * 1845875Sbostic * This file implements an LRU cache in user space for disk-based 1945875Sbostic * btrees. 2045875Sbostic */ 2145875Sbostic #include <sys/param.h> 2245875Sbostic #include <sys/file.h> 23*46127Smao #include "lrucache.h" 2445875Sbostic 2545875Sbostic /* 2645875Sbostic * LRUINIT -- Initialize a new LRU cache. 2745875Sbostic * 2845875Sbostic * There's a separate LRU cache for every open file descriptor on which 2945875Sbostic * the user wants caching. The desired cache size and logical page 3045875Sbostic * size are passed in. We try to avoid growing the cache beyond the 3145875Sbostic * limit specified by the user, but if we cannot satisfy a block request 3245875Sbostic * without growing the cache, we do so. 3345875Sbostic * 3445875Sbostic * Note that the page size passed in is the logical page size for 3545875Sbostic * use with this file descriptor, and doesn't necessarily have anything 3645875Sbostic * to do with the underlying hardware page size. 3745875Sbostic * 3845875Sbostic * Parameters: 3945875Sbostic * fd -- file descriptor for this cache 4045875Sbostic * cachesz -- number of buffers in cache (suggested) 4145875Sbostic * pagesz -- logical page size inside this file 4245875Sbostic * inproc -- routine to call when a buffer is read 4345875Sbostic * outproc -- routine to call when a buffer is written 4445875Sbostic * 4545875Sbostic * Returns: 4645875Sbostic * Opaque pointer to the LRU cache on success, NULL on failure. 4745875Sbostic * 4845875Sbostic * Side Effects: 4945875Sbostic * Allocates memory for the hash table and LRU cache. Buffers 5045875Sbostic * are allocated on demand, later. 5145875Sbostic */ 5245875Sbostic LRU 5345875Sbostic lruinit(fd, cachesz, pagesz, opaque, inproc, outproc) 5445875Sbostic int fd; 5545875Sbostic int cachesz; 5645875Sbostic int pagesz; 5745875Sbostic char *opaque; 5845875Sbostic int (*inproc)(); 5945875Sbostic int (*outproc)(); 6045875Sbostic { 6145875Sbostic register LRUCACHE *l; 6245875Sbostic int nbytes; 6345875Sbostic 6445875Sbostic /* allocate the LRU cache struct */ 6545875Sbostic if ((l = (LRUCACHE *) malloc((unsigned) sizeof(LRUCACHE))) 6645875Sbostic == (LRUCACHE *) NULL) 6745875Sbostic return ((LRU) NULL); 6845875Sbostic 6945875Sbostic /* allocate the hash table */ 7045875Sbostic nbytes = cachesz * sizeof(CACHE_ENT *); 7145875Sbostic if ((l->lru_cache = (CACHE_ENT **) malloc((unsigned) nbytes)) 7245875Sbostic == (CACHE_ENT **) NULL) { 73*46127Smao (void) free((char *) l); 7445875Sbostic return ((LRU) NULL); 7545875Sbostic } 7645875Sbostic 7745875Sbostic /* init memory */ 78*46127Smao bzero((char *) (l->lru_cache), (size_t) nbytes); 7945875Sbostic l->lru_fd = fd; 8045875Sbostic l->lru_psize = pagesz; 8145875Sbostic l->lru_csize = cachesz; 8245875Sbostic l->lru_cursz = 0; 8345875Sbostic l->lru_opaque = opaque; 8445875Sbostic l->lru_head = l->lru_tail = (LRU_ENT *) NULL; 8545875Sbostic l->lru_inproc = inproc; 8645875Sbostic l->lru_outproc = outproc; 8745875Sbostic 8845875Sbostic return ((LRU) l); 8945875Sbostic } 9045875Sbostic 9145875Sbostic /* 9245875Sbostic * LRUGET -- Get a buffer from an LRU cache. 9345875Sbostic * 9445875Sbostic * If the buffer is not in the cache at present, this routine will 9545875Sbostic * instantiate it from the file. This REQUIRES that the desired 9645875Sbostic * block actually be on disk; we don't do non-blocking reads, so 9745875Sbostic * if it's not actually out there, this routine won't return for 9845875Sbostic * a very long time. In order to instantiate a new buffer, use 9945875Sbostic * lrugetnew(). 10045875Sbostic * 10145875Sbostic * Parameters: 10245875Sbostic * lru -- the LRU cache to use. 10345875Sbostic * pgno -- the logical block number to get. 10445875Sbostic * nread -- pointer to an int, in which the number of bytes 10545875Sbostic * read is returned. 10645875Sbostic * 10745875Sbostic * Returns: 10845875Sbostic * (char *) pointer to the buffer for the desired block. The 10945875Sbostic * number of bytes actually read is returned in *nread. 11045875Sbostic * 11145875Sbostic * Warnings: 11245875Sbostic * The requested buffer is locked down until the user does 11345875Sbostic * an explicit lrurelease() on it. 11445875Sbostic */ 11545875Sbostic 11645875Sbostic char * 11745875Sbostic lruget(lru, pgno, nread) 11845875Sbostic LRU lru; 11945875Sbostic int pgno; 12045875Sbostic int *nread; 12145875Sbostic { 12245875Sbostic LRUCACHE *l = (LRUCACHE *) lru; 12345875Sbostic CACHE_ENT *ce; 12445875Sbostic LRU_ENT *lruent; 12545875Sbostic char *buffer; 12645875Sbostic long pos; 12745875Sbostic 12845875Sbostic /* is it already in the cache? */ 12945875Sbostic if ((ce = lruhashget(l, pgno)) != (CACHE_ENT *) NULL) { 13045875Sbostic 13145875Sbostic /* yes, move it to the head and return it */ 13245875Sbostic lruent = ce->c_lruent; 13345875Sbostic lruent->l_flags &= ~F_FREE; 13445875Sbostic lruhead(l, ce->c_lruent); 13545875Sbostic *nread = l->lru_psize; 13645875Sbostic return (ce->c_lruent->l_buffer); 13745875Sbostic } 13845875Sbostic 13945875Sbostic /* not there, get a free page */ 14045875Sbostic if ((buffer = lrugetpg(l, pgno, nread, lruget)) == (char *) NULL) 14145875Sbostic return ((char *) NULL); 14245875Sbostic 14345875Sbostic /* okay, got a buffer -- fill it */ 14445875Sbostic pos = (long) (l->lru_psize * pgno); 14545875Sbostic if (lseek(l->lru_fd, pos, L_SET) != pos) 14645875Sbostic return ((char *) NULL); 14745875Sbostic 14845875Sbostic *nread = read(l->lru_fd, buffer, l->lru_psize); 14945875Sbostic 15045875Sbostic if (l->lru_inproc) 15145875Sbostic (*(l->lru_inproc))(buffer, l->lru_opaque); 15245875Sbostic 15345875Sbostic return (buffer); 15445875Sbostic } 15545875Sbostic 15645875Sbostic /* 15745875Sbostic * LRUGETNEW -- Get a page for a new block in an LRU cache. 15845875Sbostic * 15945875Sbostic * This routine allows users to instantiate pages for a file if they 16045875Sbostic * don't exist on disk yet. It's used to make a file bigger. 16145875Sbostic * 16245875Sbostic * Parameters: 16345875Sbostic * lru -- the LRU cache to use 16445875Sbostic * pgno -- the (new) logical page number to instantiate 16545875Sbostic * nread -- ptr to int to get number of bytes read (this is 16645875Sbostic * guaranteed to be zero, since the page isn't on disk) 16745875Sbostic * 16845875Sbostic * Returns: 16945875Sbostic * Pointer to a buffer for the associated page, or NULL on 17045875Sbostic * failure. 17145875Sbostic * 17245875Sbostic * Warnings: 17345875Sbostic * The associated buffer is locked down until the user 17445875Sbostic * explicitly does an lrurelease() on it. 17545875Sbostic */ 17645875Sbostic 17745875Sbostic char * 17845875Sbostic lrugetnew(lru, pgno, nread) 17945875Sbostic LRU lru; 18045875Sbostic int pgno; 18145875Sbostic int *nread; 18245875Sbostic { 18345875Sbostic LRUCACHE *l = (LRUCACHE *) lru; 18445875Sbostic 18545875Sbostic *nread = 0; 18645875Sbostic return (lrugetpg(l, pgno, nread, lrugetnew)); 18745875Sbostic } 18845875Sbostic 18945875Sbostic /* 19045875Sbostic * LRUFLUSH -- flush an LRU page to disk. 19145875Sbostic * 19245875Sbostic * This routine forces a page to disk. Users should use lruwrite(), 19345875Sbostic * which simply marks a page dirty and does late writing. 19445875Sbostic * 19545875Sbostic * Parameters: 19645875Sbostic * l -- LRU cache 19745875Sbostic * lruent -- the LRU cache entry whose page we should flush 19845875Sbostic * 19945875Sbostic * Returns: 20045875Sbostic * Zero on success, -1 on failure. 20145875Sbostic */ 20245875Sbostic 20345875Sbostic lruflush(l, lruent) 20445875Sbostic LRUCACHE *l; 20545875Sbostic LRU_ENT *lruent; 20645875Sbostic { 20745875Sbostic long pos; 20845875Sbostic 20945875Sbostic if (l->lru_outproc) 21045875Sbostic (*(l->lru_outproc))(lruent->l_buffer, l->lru_opaque); 21145875Sbostic 21245875Sbostic pos = (long) (l->lru_psize * lruent->l_pgno); 21345875Sbostic if (lseek(l->lru_fd, pos, L_SET) != pos) 21445875Sbostic return (-1); 21545875Sbostic if (write(l->lru_fd, lruent->l_buffer, l->lru_psize) != l->lru_psize) 21645875Sbostic return (-1); 21745875Sbostic 21845875Sbostic if (l->lru_inproc) 21945875Sbostic (*(l->lru_inproc))(lruent->l_buffer, l->lru_opaque); 22045875Sbostic 22145875Sbostic lruent->l_flags &= ~F_DIRTY; 22245875Sbostic return (0); 22345875Sbostic } 22445875Sbostic 22545875Sbostic /* 22645875Sbostic * LRUWRITE -- Mark an LRU cache buffer dirty 22745875Sbostic * 22845875Sbostic * This routine is how users should move their changes to disk. The 22945875Sbostic * cache code marks the associated buffer dirty, and flushes it to 23045875Sbostic * disk if we need to reuse the buffer for some other page. 23145875Sbostic * 23245875Sbostic * Parameters: 23345875Sbostic * lru -- LRU cache 23445875Sbostic * pgno -- page number to flush 23545875Sbostic * 23645875Sbostic * Returns: 23745875Sbostic * Zero on success, -1 on failure. 23845875Sbostic */ 23945875Sbostic 24045875Sbostic int 24145875Sbostic lruwrite(lru, pgno) 24245875Sbostic LRU lru; 24345875Sbostic int pgno; 24445875Sbostic { 24545875Sbostic LRUCACHE *l = (LRUCACHE *) lru; 24645875Sbostic CACHE_ENT *ce; 24745875Sbostic 24845875Sbostic if ((ce = lruhashget(l, pgno)) == (CACHE_ENT *) NULL) 24945875Sbostic return (-1); 25045875Sbostic 25145875Sbostic /* mark the entry dirty */ 25245875Sbostic ce->c_lruent->l_flags |= F_DIRTY; 25345875Sbostic 25445875Sbostic return (0); 25545875Sbostic } 25645875Sbostic 25745875Sbostic /* 25845875Sbostic * LRUSYNC -- Flush all changes to disk 25945875Sbostic * 26045875Sbostic * This routine allows users to force all changes to buffers currently 26145875Sbostic * in memory to disk. This is expensive. 26245875Sbostic * 26345875Sbostic * Parameters: 26445875Sbostic * lru -- LRU cache to flush 26545875Sbostic * 26645875Sbostic * Returns: 26745875Sbostic * Zero on success, -1 on failure 26845875Sbostic * 26945875Sbostic * Side Effects: 27045875Sbostic * After this call, all buffers are clean. 27145875Sbostic */ 27245875Sbostic 27345875Sbostic int 27445875Sbostic lrusync(lru) 27545875Sbostic LRU lru; 27645875Sbostic { 27745875Sbostic LRUCACHE *l = (LRUCACHE *) lru; 27845875Sbostic LRU_ENT *le; 27945875Sbostic 28045875Sbostic for (le = l->lru_head; le != (LRU_ENT *) NULL; le = le->l_next) 28145875Sbostic if (le->l_flags & F_DIRTY) 28245875Sbostic if (lruflush(l, le) < 0) 28345875Sbostic return (-1); 28445875Sbostic return (0); 28545875Sbostic } 28645875Sbostic 28745875Sbostic /* 28845875Sbostic * LRURELEASE -- Release a buffer in the LRU cache for reuse 28945875Sbostic * 29045875Sbostic * When the user does an lruget() or lrugetnew(), the buffer we return 29145875Sbostic * is locked down, to guarantee that it's not reused while the user 29245875Sbostic * still needs it. Once a buffer is no longer needed, it should be 29345875Sbostic * released (via this routine) so that we can use it for other pages 29445875Sbostic * if necessary. 29545875Sbostic * 29645875Sbostic * Parameters: 29745875Sbostic * lru -- LRU cache 29845875Sbostic * pgno -- page number of buffer to release 29945875Sbostic * 30045875Sbostic * Returns: 30145875Sbostic * Zero on success, -1 on failure 30245875Sbostic */ 30345875Sbostic 30445875Sbostic int 30545875Sbostic lrurelease(lru, pgno) 30645875Sbostic LRU lru; 30745875Sbostic int pgno; 30845875Sbostic { 30945875Sbostic LRUCACHE *l = (LRUCACHE *) lru; 31045875Sbostic CACHE_ENT *ce; 31145875Sbostic 31245875Sbostic if ((ce = lruhashget(l, pgno)) == (CACHE_ENT *) NULL) 31345875Sbostic return (-1); 31445875Sbostic ce->c_lruent->l_flags |= F_FREE; 31545875Sbostic return (0); 31645875Sbostic } 31745875Sbostic 31845875Sbostic /* 31945875Sbostic * LRUFREE -- Free an entire LRU cache 32045875Sbostic * 32145875Sbostic * This routine releases an LRU cache. The cache should not be 32245875Sbostic * used again. 32345875Sbostic * 32445875Sbostic * Parameters: 32545875Sbostic * lru -- LRU cache to free 32645875Sbostic * 32745875Sbostic * Returns: 32845875Sbostic * None. 32945875Sbostic * 33045875Sbostic * Side Effects: 33145875Sbostic * Frees a lot of memory. 33245875Sbostic */ 33345875Sbostic 33445875Sbostic void 33545875Sbostic lrufree(lru) 33645875Sbostic LRU lru; 33745875Sbostic { 33845875Sbostic LRUCACHE *l = (LRUCACHE *) lru; 33945875Sbostic LRU_ENT *le; 34045875Sbostic LRU_ENT *sle; 34145875Sbostic 34245875Sbostic for (le = l->lru_head; le != (LRU_ENT *) NULL; ) { 343*46127Smao free((char *) (le->l_buffer)); 34445875Sbostic sle = le; 34545875Sbostic le = le->l_next; 346*46127Smao free((char *) sle); 34745875Sbostic } 348*46127Smao free ((char *) l); 34945875Sbostic } 350