xref: /csrg-svn/lib/libc/db/btree/lrutils.c (revision 46561)
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