146365Sbostic /*-
261202Sbostic * Copyright (c) 1990, 1993
361202Sbostic * The Regents of the University of California. All rights reserved.
446365Sbostic *
546365Sbostic * This code is derived from software contributed to Berkeley by
646365Sbostic * Margo Seltzer.
746365Sbostic *
846365Sbostic * %sccs.include.redist.c%
946365Sbostic */
1046365Sbostic
1146365Sbostic #if defined(LIBC_SCCS) && !defined(lint)
12*66215Sbostic static char sccsid[] = "@(#)hash_buf.c 8.2 (Berkeley) 02/21/94";
1346365Sbostic #endif /* LIBC_SCCS and not lint */
1446365Sbostic
1550997Sbostic /*
1650997Sbostic * PACKAGE: hash
1750997Sbostic *
1850997Sbostic * DESCRIPTION:
1950997Sbostic * Contains buffer management
2050997Sbostic *
2150997Sbostic * ROUTINES:
2250997Sbostic * External
2350997Sbostic * __buf_init
2450997Sbostic * __get_buf
2550997Sbostic * __buf_free
2650997Sbostic * __reclaim_buf
2750997Sbostic * Internal
2850997Sbostic * newbuf
2950997Sbostic */
3046365Sbostic
3146644Sbostic #include <sys/param.h>
3257586Sbostic
3346365Sbostic #include <errno.h>
3446562Sbostic #include <stdio.h>
3546562Sbostic #include <stdlib.h>
3650997Sbostic #ifdef DEBUG
3750997Sbostic #include <assert.h>
3850997Sbostic #endif
3957586Sbostic
4057932Sbostic #include <db.h>
4146365Sbostic #include "hash.h"
4250997Sbostic #include "page.h"
4350997Sbostic #include "extern.h"
4446365Sbostic
4557586Sbostic static BUFHEAD *newbuf __P((HTAB *, u_int, BUFHEAD *));
4646365Sbostic
4746365Sbostic /* Unlink B from its place in the lru */
4850997Sbostic #define BUF_REMOVE(B) { \
4950997Sbostic (B)->prev->next = (B)->next; \
5050997Sbostic (B)->next->prev = (B)->prev; \
5146365Sbostic }
5246365Sbostic
5346365Sbostic /* Insert B after P */
5450997Sbostic #define BUF_INSERT(B, P) { \
5550997Sbostic (B)->next = (P)->next; \
5650997Sbostic (B)->prev = (P); \
5750997Sbostic (P)->next = (B); \
5850997Sbostic (B)->next->prev = (B); \
5946365Sbostic }
6046365Sbostic
6146365Sbostic #define MRU hashp->bufhead.next
6246365Sbostic #define LRU hashp->bufhead.prev
6346365Sbostic
6450997Sbostic #define MRU_INSERT(B) BUF_INSERT((B), &hashp->bufhead)
6550997Sbostic #define LRU_INSERT(B) BUF_INSERT((B), LRU)
6646365Sbostic
6746365Sbostic /*
6850997Sbostic * We are looking for a buffer with address "addr". If prev_bp is NULL, then
6950997Sbostic * address is a bucket index. If prev_bp is not NULL, then it points to the
7050997Sbostic * page previous to an overflow page that we are trying to find.
7150997Sbostic *
7250997Sbostic * CAVEAT: The buffer header accessed via prev_bp's ovfl field may no longer
7350997Sbostic * be valid. Therefore, you must always verify that its address matches the
7450997Sbostic * address you are seeking.
7550997Sbostic */
7646365Sbostic extern BUFHEAD *
7757586Sbostic __get_buf(hashp, addr, prev_bp, newpage)
7857586Sbostic HTAB *hashp;
7950997Sbostic u_int addr;
8050997Sbostic BUFHEAD *prev_bp;
8150997Sbostic int newpage; /* If prev_bp set, indicates a new overflow page. */
8246365Sbostic {
8350997Sbostic register BUFHEAD *bp;
8451058Sbostic register u_int is_disk_mask;
8551058Sbostic register int is_disk, segment_ndx;
8650997Sbostic SEGMENT segp;
8746365Sbostic
8850997Sbostic is_disk = 0;
8950997Sbostic is_disk_mask = 0;
9050997Sbostic if (prev_bp) {
9150997Sbostic bp = prev_bp->ovfl;
9250997Sbostic if (!bp || (bp->addr != addr))
9350997Sbostic bp = NULL;
9450997Sbostic if (!newpage)
9550997Sbostic is_disk = BUF_DISK;
9650997Sbostic } else {
9750997Sbostic /* Grab buffer out of directory */
9850997Sbostic segment_ndx = addr & (hashp->SGSIZE - 1);
9946365Sbostic
10050997Sbostic /* valid segment ensured by __call_hash() */
10150997Sbostic segp = hashp->dir[addr >> hashp->SSHIFT];
10246365Sbostic #ifdef DEBUG
10350997Sbostic assert(segp != NULL);
10446365Sbostic #endif
10550997Sbostic bp = PTROF(segp[segment_ndx]);
10650997Sbostic is_disk_mask = ISDISK(segp[segment_ndx]);
10750997Sbostic is_disk = is_disk_mask || !hashp->new_file;
10850997Sbostic }
10946365Sbostic
11050997Sbostic if (!bp) {
11157586Sbostic bp = newbuf(hashp, addr, prev_bp);
11257586Sbostic if (!bp ||
11357586Sbostic __get_page(hashp, bp->page, addr, !prev_bp, is_disk, 0))
11450997Sbostic return (NULL);
11550997Sbostic if (!prev_bp)
11650997Sbostic segp[segment_ndx] =
11750997Sbostic (BUFHEAD *)((u_int)bp | is_disk_mask);
11850997Sbostic } else {
11950997Sbostic BUF_REMOVE(bp);
12050997Sbostic MRU_INSERT(bp);
12146365Sbostic }
12250997Sbostic return (bp);
12346365Sbostic }
12446365Sbostic
12546365Sbostic /*
12650997Sbostic * We need a buffer for this page. Either allocate one, or evict a resident
12750997Sbostic * one (if we have as many buffers as we're allowed) and put this one in.
12850997Sbostic *
12950997Sbostic * If newbuf finds an error (returning NULL), it also sets errno.
13050997Sbostic */
13146365Sbostic static BUFHEAD *
newbuf(hashp,addr,prev_bp)13257586Sbostic newbuf(hashp, addr, prev_bp)
13357586Sbostic HTAB *hashp;
13457586Sbostic u_int addr;
13550997Sbostic BUFHEAD *prev_bp;
13646365Sbostic {
13750997Sbostic register BUFHEAD *bp; /* The buffer we're going to use */
13850997Sbostic register BUFHEAD *xbp; /* Temp pointer */
13950997Sbostic register BUFHEAD *next_xbp;
14050997Sbostic SEGMENT segp;
14150997Sbostic int segment_ndx;
14250997Sbostic u_short oaddr, *shortp;
14346365Sbostic
14450997Sbostic oaddr = 0;
14550997Sbostic bp = LRU;
14650997Sbostic /*
14750997Sbostic * If LRU buffer is pinned, the buffer pool is too small. We need to
14850997Sbostic * allocate more buffers.
14950997Sbostic */
15050997Sbostic if (hashp->nbufs || (bp->flags & BUF_PIN)) {
15150997Sbostic /* Allocate a new one */
152*66215Sbostic if ((bp = (BUFHEAD *)malloc(sizeof(BUFHEAD))) == NULL)
15350997Sbostic return (NULL);
154*66215Sbostic if ((bp->page = (char *)malloc(hashp->BSIZE)) == NULL) {
155*66215Sbostic free(bp);
156*66215Sbostic return (NULL);
157*66215Sbostic }
15856351Smargo if (hashp->nbufs)
15956351Smargo hashp->nbufs--;
16050997Sbostic } else {
16150997Sbostic /* Kick someone out */
16250997Sbostic BUF_REMOVE(bp);
16350997Sbostic /*
16450997Sbostic * If this is an overflow page with addr 0, it's already been
16550997Sbostic * flushed back in an overflow chain and initialized.
16650997Sbostic */
16750997Sbostic if ((bp->addr != 0) || (bp->flags & BUF_BUCKET)) {
16850997Sbostic /*
16950997Sbostic * Set oaddr before __put_page so that you get it
17050997Sbostic * before bytes are swapped.
17150997Sbostic */
17250997Sbostic shortp = (u_short *)bp->page;
17350997Sbostic if (shortp[0])
17450997Sbostic oaddr = shortp[shortp[0] - 1];
17557586Sbostic if ((bp->flags & BUF_MOD) && __put_page(hashp, bp->page,
17650997Sbostic bp->addr, (int)IS_BUCKET(bp->flags), 0))
17750997Sbostic return (NULL);
17850997Sbostic /*
17950997Sbostic * Update the pointer to this page (i.e. invalidate it).
18050997Sbostic *
18150997Sbostic * If this is a new file (i.e. we created it at open
18250997Sbostic * time), make sure that we mark pages which have been
18350997Sbostic * written to disk so we retrieve them from disk later,
18450997Sbostic * rather than allocating new pages.
18550997Sbostic */
18650997Sbostic if (IS_BUCKET(bp->flags)) {
18750997Sbostic segment_ndx = bp->addr & (hashp->SGSIZE - 1);
18850997Sbostic segp = hashp->dir[bp->addr >> hashp->SSHIFT];
18950997Sbostic #ifdef DEBUG
19050997Sbostic assert(segp != NULL);
19150997Sbostic #endif
19246365Sbostic
19350997Sbostic if (hashp->new_file &&
19450997Sbostic ((bp->flags & BUF_MOD) ||
19550997Sbostic ISDISK(segp[segment_ndx])))
19650997Sbostic segp[segment_ndx] = (BUFHEAD *)BUF_DISK;
19750997Sbostic else
19850997Sbostic segp[segment_ndx] = NULL;
19950997Sbostic }
20050997Sbostic /*
20150997Sbostic * Since overflow pages can only be access by means of
20250997Sbostic * their bucket, free overflow pages associated with
20350997Sbostic * this bucket.
20450997Sbostic */
20550997Sbostic for (xbp = bp; xbp->ovfl;) {
20650997Sbostic next_xbp = xbp->ovfl;
20750997Sbostic xbp->ovfl = 0;
20850997Sbostic xbp = next_xbp;
20946365Sbostic
21050997Sbostic /* Check that ovfl pointer is up date. */
21150997Sbostic if (IS_BUCKET(xbp->flags) ||
21250997Sbostic (oaddr != xbp->addr))
21350997Sbostic break;
21446365Sbostic
21550997Sbostic shortp = (u_short *)xbp->page;
21650997Sbostic if (shortp[0])
21750997Sbostic /* set before __put_page */
21850997Sbostic oaddr = shortp[shortp[0] - 1];
21957586Sbostic if ((xbp->flags & BUF_MOD) && __put_page(hashp,
22057586Sbostic xbp->page, xbp->addr, 0, 0))
22150997Sbostic return (NULL);
22250997Sbostic xbp->addr = 0;
22350997Sbostic xbp->flags = 0;
22450997Sbostic BUF_REMOVE(xbp);
22550997Sbostic LRU_INSERT(xbp);
22650997Sbostic }
22746506Sbostic }
22846365Sbostic }
22946365Sbostic
23050997Sbostic /* Now assign this buffer */
23150997Sbostic bp->addr = addr;
23246365Sbostic #ifdef DEBUG1
23350997Sbostic (void)fprintf(stderr, "NEWBUF1: %d->ovfl was %d is now %d\n",
23450997Sbostic bp->addr, (bp->ovfl ? bp->ovfl->addr : 0), 0);
23546365Sbostic #endif
23650997Sbostic bp->ovfl = NULL;
23750997Sbostic if (prev_bp) {
23850997Sbostic /*
23950997Sbostic * If prev_bp is set, this is an overflow page, hook it in to
24050997Sbostic * the buffer overflow links.
24150997Sbostic */
24246365Sbostic #ifdef DEBUG1
24350997Sbostic (void)fprintf(stderr, "NEWBUF2: %d->ovfl was %d is now %d\n",
24450997Sbostic prev_bp->addr, (prev_bp->ovfl ? bp->ovfl->addr : 0),
24550997Sbostic (bp ? bp->addr : 0));
24646365Sbostic #endif
24750997Sbostic prev_bp->ovfl = bp;
24850997Sbostic bp->flags = 0;
24950997Sbostic } else
25050997Sbostic bp->flags = BUF_BUCKET;
25150997Sbostic MRU_INSERT(bp);
25250997Sbostic return (bp);
25346365Sbostic }
25446365Sbostic
25546365Sbostic extern void
25657586Sbostic __buf_init(hashp, nbytes)
25757586Sbostic HTAB *hashp;
25850997Sbostic int nbytes;
25946365Sbostic {
26050997Sbostic BUFHEAD *bfp;
26150997Sbostic int npages;
26246365Sbostic
26350997Sbostic bfp = &(hashp->bufhead);
26450997Sbostic npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT;
26550997Sbostic npages = MAX(npages, MIN_BUFFERS);
26646365Sbostic
26750997Sbostic hashp->nbufs = npages;
26850997Sbostic bfp->next = bfp;
26950997Sbostic bfp->prev = bfp;
27050997Sbostic /*
27150997Sbostic * This space is calloc'd so these are already null.
27250997Sbostic *
27350997Sbostic * bfp->ovfl = NULL;
27450997Sbostic * bfp->flags = 0;
27550997Sbostic * bfp->page = NULL;
27650997Sbostic * bfp->addr = 0;
27750997Sbostic */
27846365Sbostic }
27946365Sbostic
28046365Sbostic extern int
28157586Sbostic __buf_free(hashp, do_free, to_disk)
28257586Sbostic HTAB *hashp;
28350997Sbostic int do_free, to_disk;
28446365Sbostic {
28550997Sbostic BUFHEAD *bp;
28646365Sbostic
28750997Sbostic /* Need to make sure that buffer manager has been initialized */
28850997Sbostic if (!LRU)
28950997Sbostic return (0);
29050997Sbostic for (bp = LRU; bp != &hashp->bufhead;) {
29150997Sbostic /* Check that the buffer is valid */
29250997Sbostic if (bp->addr || IS_BUCKET(bp->flags)) {
29350997Sbostic if (to_disk && (bp->flags & BUF_MOD) &&
29457586Sbostic __put_page(hashp, bp->page,
29550997Sbostic bp->addr, IS_BUCKET(bp->flags), 0))
29650997Sbostic return (-1);
29750997Sbostic }
29850997Sbostic /* Check if we are freeing stuff */
29950997Sbostic if (do_free) {
30050997Sbostic if (bp->page)
30150997Sbostic free(bp->page);
30250997Sbostic BUF_REMOVE(bp);
30350997Sbostic free(bp);
30450997Sbostic bp = LRU;
30550997Sbostic } else
30650997Sbostic bp = bp->prev;
30746365Sbostic }
30850997Sbostic return (0);
30946365Sbostic }
31046365Sbostic
31146365Sbostic extern void
31257586Sbostic __reclaim_buf(hashp, bp)
31357586Sbostic HTAB *hashp;
31450997Sbostic BUFHEAD *bp;
31546365Sbostic {
31650997Sbostic bp->ovfl = 0;
31750997Sbostic bp->addr = 0;
31850997Sbostic bp->flags = 0;
31950997Sbostic BUF_REMOVE(bp);
32050997Sbostic LRU_INSERT(bp);
32146365Sbostic }
322