1*46138Smao /*- 2*46138Smao * Copyright (c) 1990 The Regents of the University of California. 3*46138Smao * All rights reserved. 4*46138Smao * 5*46138Smao * This code is derived from software contributed to Berkeley by 6*46138Smao * Mike Olson. 7*46138Smao * 8*46138Smao * %sccs.include.redist.c% 9*46138Smao */ 10*46138Smao 11*46138Smao #if defined(LIBC_SCCS) && !defined(lint) 12*46138Smao static char sccsid[] = "@(#)lrutils.c 5.1 (Berkeley) 01/23/91"; 13*46138Smao #endif /* LIBC_SCCS and not lint */ 14*46138Smao 15*46138Smao #include <sys/types.h> 16*46138Smao #include "lrucache.h" 17*46138Smao 18*46138Smao /* 19*46138Smao * LRUGETPG -- Get a free page from the LRU cache. 20*46138Smao * 21*46138Smao * This routine grows the cache if necessary, finds an unused page if 22*46138Smao * it can, and handles flushing dirty buffers to disk. 23*46138Smao * 24*46138Smao * One of the parameters to this routine (f) is the routine that called 25*46138Smao * us. If we have to grow the cache, we call this routine recursively 26*46138Smao * in order to fill the buffer. The reason for this is that we have 27*46138Smao * two interfaces that call lrugetpg(). Lruget() fills a page from disk, 28*46138Smao * and lrugetnew() just allocates a new (empty) page. 29*46138Smao * 30*46138Smao * Parameters: 31*46138Smao * l -- LRU cache to use. 32*46138Smao * pgno -- page number for which we want a buffer 33*46138Smao * nread -- pointer to an int to get number of bytes read 34*46138Smao * f -- who called us 35*46138Smao * 36*46138Smao * Returns: 37*46138Smao * (char *) pointer to buffer to use, or NULL on failure. 38*46138Smao * 39*46138Smao * Warnings: 40*46138Smao * The buffer returned is locked down until the user does an 41*46138Smao * explicit lrurelease() on it. 42*46138Smao */ 43*46138Smao 44*46138Smao char * 45*46138Smao lrugetpg(l, pgno, nread, f) 46*46138Smao LRUCACHE *l; 47*46138Smao int pgno; 48*46138Smao int *nread; 49*46138Smao char *(*f)(); 50*46138Smao { 51*46138Smao CACHE_ENT *ce; 52*46138Smao LRU_ENT *lruent; 53*46138Smao char *buffer; 54*46138Smao 55*46138Smao /* if we're allowed to grow the cache, do so */ 56*46138Smao if (l->lru_cursz < l->lru_csize) { 57*46138Smao 58*46138Smao /* get a buffer */ 59*46138Smao if ((buffer = (char *) malloc((unsigned) l->lru_psize)) 60*46138Smao == (char *) NULL) 61*46138Smao return ((char *) NULL); 62*46138Smao 63*46138Smao /* get and LRU list entry */ 64*46138Smao if ((lruent = (LRU_ENT *) malloc((unsigned) sizeof(LRU_ENT))) 65*46138Smao == (LRU_ENT *) NULL) 66*46138Smao return ((char *) NULL); 67*46138Smao lruent->l_buffer = buffer; 68*46138Smao lruent->l_pgno = pgno; 69*46138Smao lruent->l_flags = NULL; 70*46138Smao 71*46138Smao /* manage spaghetti */ 72*46138Smao lruent->l_prev = (LRU_ENT *) NULL; 73*46138Smao lruent->l_next = l->lru_head; 74*46138Smao if (l->lru_head != (LRU_ENT *) NULL) 75*46138Smao l->lru_head->l_prev = lruent; 76*46138Smao l->lru_head = lruent; 77*46138Smao if (l->lru_tail == (LRU_ENT *) NULL) 78*46138Smao l->lru_tail = lruent; 79*46138Smao 80*46138Smao /* add it to the hash table */ 81*46138Smao ce = lruhashput(l, pgno, lruent); 82*46138Smao l->lru_cursz++; 83*46138Smao } else { 84*46138Smao lruent = l->lru_tail; 85*46138Smao 86*46138Smao /* find the oldest unused buffer */ 87*46138Smao while (lruent != (LRU_ENT *) NULL 88*46138Smao && !(lruent->l_flags & F_FREE)) 89*46138Smao lruent = lruent->l_prev; 90*46138Smao 91*46138Smao /* if no free buffer was found, we have to grow the cache */ 92*46138Smao if (lruent == (LRU_ENT *) NULL) { 93*46138Smao if (lrugrow(l) < 0) 94*46138Smao return ((char *) NULL); 95*46138Smao return ((*f)((LRU) l, pgno, nread)); 96*46138Smao } 97*46138Smao 98*46138Smao /* okay, found a free buffer -- update hash table and list */ 99*46138Smao ce = lruhashget(l, lruent->l_pgno); 100*46138Smao if (lruhashdel(l, lruent->l_pgno) < 0) 101*46138Smao return ((char *) NULL); 102*46138Smao 103*46138Smao /* flush the old page to disk, if necessary */ 104*46138Smao if (lruent->l_flags & F_DIRTY) 105*46138Smao if (lruflush(l, lruent) < 0) 106*46138Smao return ((char *) NULL); 107*46138Smao 108*46138Smao /* update stats, hash table, and list */ 109*46138Smao lruent->l_pgno = pgno; 110*46138Smao lruhead(l, lruent); 111*46138Smao ce = lruhashput(l, pgno, lruent); 112*46138Smao buffer = lruent->l_buffer; 113*46138Smao } 114*46138Smao #ifdef lint 115*46138Smao ce = ce; 116*46138Smao #endif /* lint */ 117*46138Smao 118*46138Smao /* lock this page down */ 119*46138Smao lruent->l_flags &= ~F_FREE; 120*46138Smao 121*46138Smao return (buffer); 122*46138Smao } 123*46138Smao 124*46138Smao /* 125*46138Smao * LRUHEAD -- Move an LRU list entry to the head of the list. 126*46138Smao * 127*46138Smao * The head of the list is where the most recently used item goes. 128*46138Smao * 129*46138Smao * Parameters: 130*46138Smao * l -- LRU cache 131*46138Smao * lruent -- entry to move to the head of the list 132*46138Smao * 133*46138Smao * Returns: 134*46138Smao * None 135*46138Smao * 136*46138Smao * Side Effects: 137*46138Smao * Updates the cache's head and tail pointers as required. 138*46138Smao */ 139*46138Smao 140*46138Smao void 141*46138Smao lruhead(l, lruent) 142*46138Smao LRUCACHE *l; 143*46138Smao LRU_ENT *lruent; 144*46138Smao { 145*46138Smao LRU_ENT *next; 146*46138Smao LRU_ENT *prev; 147*46138Smao 148*46138Smao if (l->lru_head == lruent) 149*46138Smao return; 150*46138Smao 151*46138Smao next = lruent->l_next; 152*46138Smao prev = lruent->l_prev; 153*46138Smao lruent->l_prev = (LRU_ENT *) NULL; 154*46138Smao lruent->l_next = l->lru_head; 155*46138Smao l->lru_head->l_prev = lruent; 156*46138Smao l->lru_head = lruent; 157*46138Smao 158*46138Smao prev->l_next = next; 159*46138Smao if (next != (LRU_ENT *) NULL) 160*46138Smao next->l_prev = prev; 161*46138Smao 162*46138Smao if (l->lru_tail == lruent) 163*46138Smao l->lru_tail = prev; 164*46138Smao } 165*46138Smao 166*46138Smao /* 167*46138Smao * LRUGROW -- Grow the LRU cache 168*46138Smao * 169*46138Smao * This routine is called only if we can't satisfy a user's get() request 170*46138Smao * using an existing buffer. We need to rebuild the hash table so that 171*46138Smao * subsequent lookups work correctly, since the cache size is used to 172*46138Smao * compute hash keys. 173*46138Smao * 174*46138Smao * Parameters: 175*46138Smao * l -- LRU cache to grow 176*46138Smao * 177*46138Smao * Returns: 178*46138Smao * Zero on success, -1 on failure 179*46138Smao */ 180*46138Smao 181*46138Smao int 182*46138Smao lrugrow(l) 183*46138Smao LRUCACHE *l; 184*46138Smao { 185*46138Smao int oldsz, newsz; 186*46138Smao CACHE_ENT **new; 187*46138Smao CACHE_ENT *ce, *nce; 188*46138Smao int h; 189*46138Smao int i; 190*46138Smao 191*46138Smao oldsz = l->lru_csize; 192*46138Smao newsz = l->lru_csize + 1; 193*46138Smao 194*46138Smao /* get a new hash table */ 195*46138Smao if ((new = (CACHE_ENT **) malloc((unsigned)newsz * sizeof(CACHE_ENT *))) 196*46138Smao == (CACHE_ENT **) NULL) 197*46138Smao return (-1); 198*46138Smao 199*46138Smao /* build the new hash table */ 200*46138Smao bzero((char *) new, (size_t) (newsz * sizeof(CACHE_ENT *))); 201*46138Smao for (i = oldsz; --i >= 0; ) { 202*46138Smao for (ce = l->lru_cache[i]; ce != (CACHE_ENT *) NULL; ) { 203*46138Smao nce = ce->c_chain; 204*46138Smao h = ce->c_pgno % newsz; 205*46138Smao ce->c_chain = new[h]; 206*46138Smao new[h] = ce; 207*46138Smao ce = nce; 208*46138Smao } 209*46138Smao } 210*46138Smao 211*46138Smao /* get rid of the old hash table, and update the cache */ 212*46138Smao free ((char *) (l->lru_cache)); 213*46138Smao l->lru_cache = new; 214*46138Smao l->lru_csize = newsz; 215*46138Smao 216*46138Smao return (0); 217*46138Smao } 218