/*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Mike Olson. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)lrutils.c 5.2 (Berkeley) 2/22/91"; #endif /* LIBC_SCCS and not lint */ #include #include #include "lrucache.h" /* * LRUGETPG -- Get a free page from the LRU cache. * * This routine grows the cache if necessary, finds an unused page if * it can, and handles flushing dirty buffers to disk. * * One of the parameters to this routine (f) is the routine that called * us. If we have to grow the cache, we call this routine recursively * in order to fill the buffer. The reason for this is that we have * two interfaces that call lrugetpg(). Lruget() fills a page from disk, * and lrugetnew() just allocates a new (empty) page. * * Parameters: * l -- LRU cache to use. * pgno -- page number for which we want a buffer * nread -- pointer to an int to get number of bytes read * f -- who called us * * Returns: * (char *) pointer to buffer to use, or NULL on failure. * * Warnings: * The buffer returned is locked down until the user does an * explicit lrurelease() on it. */ char * lrugetpg(l, pgno, nread, f) LRUCACHE *l; int pgno; int *nread; char *(*f)(); { CACHE_ENT *ce; LRU_ENT *lruent; char *buffer; /* if we're allowed to grow the cache, do so */ if (l->lru_cursz < l->lru_csize) { /* get a buffer */ if ((buffer = (char *) malloc((unsigned) l->lru_psize)) == (char *) NULL) return ((char *) NULL); /* get and LRU list entry */ if ((lruent = (LRU_ENT *) malloc((unsigned) sizeof(LRU_ENT))) == (LRU_ENT *) NULL) return ((char *) NULL); lruent->l_buffer = buffer; lruent->l_pgno = pgno; lruent->l_flags = NULL; /* manage spaghetti */ lruent->l_prev = (LRU_ENT *) NULL; lruent->l_next = l->lru_head; if (l->lru_head != (LRU_ENT *) NULL) l->lru_head->l_prev = lruent; l->lru_head = lruent; if (l->lru_tail == (LRU_ENT *) NULL) l->lru_tail = lruent; /* add it to the hash table */ ce = lruhashput(l, pgno, lruent); l->lru_cursz++; } else { lruent = l->lru_tail; /* find the oldest unused buffer */ while (lruent != (LRU_ENT *) NULL && !(lruent->l_flags & F_FREE)) lruent = lruent->l_prev; /* if no free buffer was found, we have to grow the cache */ if (lruent == (LRU_ENT *) NULL) { if (lrugrow(l) < 0) return ((char *) NULL); return ((*f)((LRU) l, pgno, nread)); } /* okay, found a free buffer -- update hash table and list */ ce = lruhashget(l, lruent->l_pgno); if (lruhashdel(l, lruent->l_pgno) < 0) return ((char *) NULL); /* flush the old page to disk, if necessary */ if (lruent->l_flags & F_DIRTY) if (lruflush(l, lruent) < 0) return ((char *) NULL); /* update stats, hash table, and list */ lruent->l_pgno = pgno; lruhead(l, lruent); ce = lruhashput(l, pgno, lruent); buffer = lruent->l_buffer; } #ifdef lint ce = ce; #endif /* lint */ /* lock this page down */ lruent->l_flags &= ~F_FREE; return (buffer); } /* * LRUHEAD -- Move an LRU list entry to the head of the list. * * The head of the list is where the most recently used item goes. * * Parameters: * l -- LRU cache * lruent -- entry to move to the head of the list * * Returns: * None * * Side Effects: * Updates the cache's head and tail pointers as required. */ void lruhead(l, lruent) LRUCACHE *l; LRU_ENT *lruent; { LRU_ENT *next; LRU_ENT *prev; if (l->lru_head == lruent) return; next = lruent->l_next; prev = lruent->l_prev; lruent->l_prev = (LRU_ENT *) NULL; lruent->l_next = l->lru_head; l->lru_head->l_prev = lruent; l->lru_head = lruent; prev->l_next = next; if (next != (LRU_ENT *) NULL) next->l_prev = prev; if (l->lru_tail == lruent) l->lru_tail = prev; } /* * LRUGROW -- Grow the LRU cache * * This routine is called only if we can't satisfy a user's get() request * using an existing buffer. We need to rebuild the hash table so that * subsequent lookups work correctly, since the cache size is used to * compute hash keys. * * Parameters: * l -- LRU cache to grow * * Returns: * Zero on success, -1 on failure */ int lrugrow(l) LRUCACHE *l; { int oldsz, newsz; CACHE_ENT **new; CACHE_ENT *ce, *nce; int h; int i; oldsz = l->lru_csize; newsz = l->lru_csize + 1; /* get a new hash table */ if ((new = (CACHE_ENT **) malloc((unsigned)newsz * sizeof(CACHE_ENT *))) == (CACHE_ENT **) NULL) return (-1); /* build the new hash table */ bzero((char *) new, (size_t) (newsz * sizeof(CACHE_ENT *))); for (i = oldsz; --i >= 0; ) { for (ce = l->lru_cache[i]; ce != (CACHE_ENT *) NULL; ) { nce = ce->c_chain; h = ce->c_pgno % newsz; ce->c_chain = new[h]; new[h] = ce; ce = nce; } } /* get rid of the old hash table, and update the cache */ free ((char *) (l->lru_cache)); l->lru_cache = new; l->lru_csize = newsz; return (0); }