146138Smao /*-
246138Smao * Copyright (c) 1990 The Regents of the University of California.
346138Smao * All rights reserved.
446138Smao *
546138Smao * This code is derived from software contributed to Berkeley by
646138Smao * Mike Olson.
746138Smao *
846138Smao * %sccs.include.redist.c%
946138Smao */
1046138Smao
1146138Smao #if defined(LIBC_SCCS) && !defined(lint)
12*46561Sbostic static char sccsid[] = "@(#)lrutils.c 5.2 (Berkeley) 02/22/91";
1346138Smao #endif /* LIBC_SCCS and not lint */
1446138Smao
15*46561Sbostic #include <stdlib.h>
16*46561Sbostic #include <string.h>
1746138Smao #include "lrucache.h"
1846138Smao
1946138Smao /*
2046138Smao * LRUGETPG -- Get a free page from the LRU cache.
2146138Smao *
2246138Smao * This routine grows the cache if necessary, finds an unused page if
2346138Smao * it can, and handles flushing dirty buffers to disk.
2446138Smao *
2546138Smao * One of the parameters to this routine (f) is the routine that called
2646138Smao * us. If we have to grow the cache, we call this routine recursively
2746138Smao * in order to fill the buffer. The reason for this is that we have
2846138Smao * two interfaces that call lrugetpg(). Lruget() fills a page from disk,
2946138Smao * and lrugetnew() just allocates a new (empty) page.
3046138Smao *
3146138Smao * Parameters:
3246138Smao * l -- LRU cache to use.
3346138Smao * pgno -- page number for which we want a buffer
3446138Smao * nread -- pointer to an int to get number of bytes read
3546138Smao * f -- who called us
3646138Smao *
3746138Smao * Returns:
3846138Smao * (char *) pointer to buffer to use, or NULL on failure.
3946138Smao *
4046138Smao * Warnings:
4146138Smao * The buffer returned is locked down until the user does an
4246138Smao * explicit lrurelease() on it.
4346138Smao */
4446138Smao
4546138Smao char *
lrugetpg(l,pgno,nread,f)4646138Smao lrugetpg(l, pgno, nread, f)
4746138Smao LRUCACHE *l;
4846138Smao int pgno;
4946138Smao int *nread;
5046138Smao char *(*f)();
5146138Smao {
5246138Smao CACHE_ENT *ce;
5346138Smao LRU_ENT *lruent;
5446138Smao char *buffer;
5546138Smao
5646138Smao /* if we're allowed to grow the cache, do so */
5746138Smao if (l->lru_cursz < l->lru_csize) {
5846138Smao
5946138Smao /* get a buffer */
6046138Smao if ((buffer = (char *) malloc((unsigned) l->lru_psize))
6146138Smao == (char *) NULL)
6246138Smao return ((char *) NULL);
6346138Smao
6446138Smao /* get and LRU list entry */
6546138Smao if ((lruent = (LRU_ENT *) malloc((unsigned) sizeof(LRU_ENT)))
6646138Smao == (LRU_ENT *) NULL)
6746138Smao return ((char *) NULL);
6846138Smao lruent->l_buffer = buffer;
6946138Smao lruent->l_pgno = pgno;
7046138Smao lruent->l_flags = NULL;
7146138Smao
7246138Smao /* manage spaghetti */
7346138Smao lruent->l_prev = (LRU_ENT *) NULL;
7446138Smao lruent->l_next = l->lru_head;
7546138Smao if (l->lru_head != (LRU_ENT *) NULL)
7646138Smao l->lru_head->l_prev = lruent;
7746138Smao l->lru_head = lruent;
7846138Smao if (l->lru_tail == (LRU_ENT *) NULL)
7946138Smao l->lru_tail = lruent;
8046138Smao
8146138Smao /* add it to the hash table */
8246138Smao ce = lruhashput(l, pgno, lruent);
8346138Smao l->lru_cursz++;
8446138Smao } else {
8546138Smao lruent = l->lru_tail;
8646138Smao
8746138Smao /* find the oldest unused buffer */
8846138Smao while (lruent != (LRU_ENT *) NULL
8946138Smao && !(lruent->l_flags & F_FREE))
9046138Smao lruent = lruent->l_prev;
9146138Smao
9246138Smao /* if no free buffer was found, we have to grow the cache */
9346138Smao if (lruent == (LRU_ENT *) NULL) {
9446138Smao if (lrugrow(l) < 0)
9546138Smao return ((char *) NULL);
9646138Smao return ((*f)((LRU) l, pgno, nread));
9746138Smao }
9846138Smao
9946138Smao /* okay, found a free buffer -- update hash table and list */
10046138Smao ce = lruhashget(l, lruent->l_pgno);
10146138Smao if (lruhashdel(l, lruent->l_pgno) < 0)
10246138Smao return ((char *) NULL);
10346138Smao
10446138Smao /* flush the old page to disk, if necessary */
10546138Smao if (lruent->l_flags & F_DIRTY)
10646138Smao if (lruflush(l, lruent) < 0)
10746138Smao return ((char *) NULL);
10846138Smao
10946138Smao /* update stats, hash table, and list */
11046138Smao lruent->l_pgno = pgno;
11146138Smao lruhead(l, lruent);
11246138Smao ce = lruhashput(l, pgno, lruent);
11346138Smao buffer = lruent->l_buffer;
11446138Smao }
11546138Smao #ifdef lint
11646138Smao ce = ce;
11746138Smao #endif /* lint */
11846138Smao
11946138Smao /* lock this page down */
12046138Smao lruent->l_flags &= ~F_FREE;
12146138Smao
12246138Smao return (buffer);
12346138Smao }
12446138Smao
12546138Smao /*
12646138Smao * LRUHEAD -- Move an LRU list entry to the head of the list.
12746138Smao *
12846138Smao * The head of the list is where the most recently used item goes.
12946138Smao *
13046138Smao * Parameters:
13146138Smao * l -- LRU cache
13246138Smao * lruent -- entry to move to the head of the list
13346138Smao *
13446138Smao * Returns:
13546138Smao * None
13646138Smao *
13746138Smao * Side Effects:
13846138Smao * Updates the cache's head and tail pointers as required.
13946138Smao */
14046138Smao
14146138Smao void
lruhead(l,lruent)14246138Smao lruhead(l, lruent)
14346138Smao LRUCACHE *l;
14446138Smao LRU_ENT *lruent;
14546138Smao {
14646138Smao LRU_ENT *next;
14746138Smao LRU_ENT *prev;
14846138Smao
14946138Smao if (l->lru_head == lruent)
15046138Smao return;
15146138Smao
15246138Smao next = lruent->l_next;
15346138Smao prev = lruent->l_prev;
15446138Smao lruent->l_prev = (LRU_ENT *) NULL;
15546138Smao lruent->l_next = l->lru_head;
15646138Smao l->lru_head->l_prev = lruent;
15746138Smao l->lru_head = lruent;
15846138Smao
15946138Smao prev->l_next = next;
16046138Smao if (next != (LRU_ENT *) NULL)
16146138Smao next->l_prev = prev;
16246138Smao
16346138Smao if (l->lru_tail == lruent)
16446138Smao l->lru_tail = prev;
16546138Smao }
16646138Smao
16746138Smao /*
16846138Smao * LRUGROW -- Grow the LRU cache
16946138Smao *
17046138Smao * This routine is called only if we can't satisfy a user's get() request
17146138Smao * using an existing buffer. We need to rebuild the hash table so that
17246138Smao * subsequent lookups work correctly, since the cache size is used to
17346138Smao * compute hash keys.
17446138Smao *
17546138Smao * Parameters:
17646138Smao * l -- LRU cache to grow
17746138Smao *
17846138Smao * Returns:
17946138Smao * Zero on success, -1 on failure
18046138Smao */
18146138Smao
18246138Smao int
lrugrow(l)18346138Smao lrugrow(l)
18446138Smao LRUCACHE *l;
18546138Smao {
18646138Smao int oldsz, newsz;
18746138Smao CACHE_ENT **new;
18846138Smao CACHE_ENT *ce, *nce;
18946138Smao int h;
19046138Smao int i;
19146138Smao
19246138Smao oldsz = l->lru_csize;
19346138Smao newsz = l->lru_csize + 1;
19446138Smao
19546138Smao /* get a new hash table */
19646138Smao if ((new = (CACHE_ENT **) malloc((unsigned)newsz * sizeof(CACHE_ENT *)))
19746138Smao == (CACHE_ENT **) NULL)
19846138Smao return (-1);
19946138Smao
20046138Smao /* build the new hash table */
20146138Smao bzero((char *) new, (size_t) (newsz * sizeof(CACHE_ENT *)));
20246138Smao for (i = oldsz; --i >= 0; ) {
20346138Smao for (ce = l->lru_cache[i]; ce != (CACHE_ENT *) NULL; ) {
20446138Smao nce = ce->c_chain;
20546138Smao h = ce->c_pgno % newsz;
20646138Smao ce->c_chain = new[h];
20746138Smao new[h] = ce;
20846138Smao ce = nce;
20946138Smao }
21046138Smao }
21146138Smao
21246138Smao /* get rid of the old hash table, and update the cache */
21346138Smao free ((char *) (l->lru_cache));
21446138Smao l->lru_cache = new;
21546138Smao l->lru_csize = newsz;
21646138Smao
21746138Smao return (0);
21846138Smao }
219