146365Sbostic /*- 246365Sbostic * Copyright (c) 1990 The Regents of the University of California. 346365Sbostic * 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*56351Smargo static char sccsid[] = "@(#)hash_buf.c 5.10 (Berkeley) 09/29/92"; 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> 3250997Sbostic #include <db.h> 3346365Sbostic #include <errno.h> 3446562Sbostic #include <stdio.h> 3546562Sbostic #include <stdlib.h> 3650997Sbostic #ifdef DEBUG 3750997Sbostic #include <assert.h> 3850997Sbostic #endif 3946365Sbostic #include "hash.h" 4050997Sbostic #include "page.h" 4150997Sbostic #include "extern.h" 4246365Sbostic 4350997Sbostic static BUFHEAD *newbuf __P((u_int, BUFHEAD *)); 4446365Sbostic 4546365Sbostic /* Unlink B from its place in the lru */ 4650997Sbostic #define BUF_REMOVE(B) { \ 4750997Sbostic (B)->prev->next = (B)->next; \ 4850997Sbostic (B)->next->prev = (B)->prev; \ 4946365Sbostic } 5046365Sbostic 5146365Sbostic /* Insert B after P */ 5250997Sbostic #define BUF_INSERT(B, P) { \ 5350997Sbostic (B)->next = (P)->next; \ 5450997Sbostic (B)->prev = (P); \ 5550997Sbostic (P)->next = (B); \ 5650997Sbostic (B)->next->prev = (B); \ 5746365Sbostic } 5846365Sbostic 5946365Sbostic #define MRU hashp->bufhead.next 6046365Sbostic #define LRU hashp->bufhead.prev 6146365Sbostic 6250997Sbostic #define MRU_INSERT(B) BUF_INSERT((B), &hashp->bufhead) 6350997Sbostic #define LRU_INSERT(B) BUF_INSERT((B), LRU) 6446365Sbostic 6546365Sbostic /* 6650997Sbostic * We are looking for a buffer with address "addr". If prev_bp is NULL, then 6750997Sbostic * address is a bucket index. If prev_bp is not NULL, then it points to the 6850997Sbostic * page previous to an overflow page that we are trying to find. 6950997Sbostic * 7050997Sbostic * CAVEAT: The buffer header accessed via prev_bp's ovfl field may no longer 7150997Sbostic * be valid. Therefore, you must always verify that its address matches the 7250997Sbostic * address you are seeking. 7350997Sbostic */ 7446365Sbostic extern BUFHEAD * 7550997Sbostic __get_buf(addr, prev_bp, newpage) 7650997Sbostic u_int addr; 7750997Sbostic BUFHEAD *prev_bp; 7850997Sbostic int newpage; /* If prev_bp set, indicates a new overflow page. */ 7946365Sbostic { 8050997Sbostic register BUFHEAD *bp; 8151058Sbostic register u_int is_disk_mask; 8251058Sbostic register int is_disk, segment_ndx; 8350997Sbostic SEGMENT segp; 8446365Sbostic 8550997Sbostic is_disk = 0; 8650997Sbostic is_disk_mask = 0; 8750997Sbostic if (prev_bp) { 8850997Sbostic bp = prev_bp->ovfl; 8950997Sbostic if (!bp || (bp->addr != addr)) 9050997Sbostic bp = NULL; 9150997Sbostic if (!newpage) 9250997Sbostic is_disk = BUF_DISK; 9350997Sbostic } else { 9450997Sbostic /* Grab buffer out of directory */ 9550997Sbostic segment_ndx = addr & (hashp->SGSIZE - 1); 9646365Sbostic 9750997Sbostic /* valid segment ensured by __call_hash() */ 9850997Sbostic segp = hashp->dir[addr >> hashp->SSHIFT]; 9946365Sbostic #ifdef DEBUG 10050997Sbostic assert(segp != NULL); 10146365Sbostic #endif 10250997Sbostic bp = PTROF(segp[segment_ndx]); 10350997Sbostic is_disk_mask = ISDISK(segp[segment_ndx]); 10450997Sbostic is_disk = is_disk_mask || !hashp->new_file; 10550997Sbostic } 10646365Sbostic 10750997Sbostic if (!bp) { 10850997Sbostic bp = newbuf(addr, prev_bp); 10950997Sbostic if (!bp || __get_page(bp->page, addr, !prev_bp, is_disk, 0)) 11050997Sbostic return (NULL); 11150997Sbostic if (!prev_bp) 11250997Sbostic segp[segment_ndx] = 11350997Sbostic (BUFHEAD *)((u_int)bp | is_disk_mask); 11450997Sbostic } else { 11550997Sbostic BUF_REMOVE(bp); 11650997Sbostic MRU_INSERT(bp); 11746365Sbostic } 11850997Sbostic return (bp); 11946365Sbostic } 12046365Sbostic 12146365Sbostic /* 12250997Sbostic * We need a buffer for this page. Either allocate one, or evict a resident 12350997Sbostic * one (if we have as many buffers as we're allowed) and put this one in. 12450997Sbostic * 12550997Sbostic * If newbuf finds an error (returning NULL), it also sets errno. 12650997Sbostic */ 12746365Sbostic static BUFHEAD * 12850997Sbostic newbuf(addr, prev_bp) 12950997Sbostic u_int addr; 13050997Sbostic BUFHEAD *prev_bp; 13146365Sbostic { 13250997Sbostic register BUFHEAD *bp; /* The buffer we're going to use */ 13350997Sbostic register BUFHEAD *xbp; /* Temp pointer */ 13450997Sbostic register BUFHEAD *next_xbp; 13550997Sbostic SEGMENT segp; 13650997Sbostic int segment_ndx; 13750997Sbostic u_short oaddr, *shortp; 13846365Sbostic 13950997Sbostic oaddr = 0; 14050997Sbostic bp = LRU; 14150997Sbostic /* 14250997Sbostic * If LRU buffer is pinned, the buffer pool is too small. We need to 14350997Sbostic * allocate more buffers. 14450997Sbostic */ 14550997Sbostic if (hashp->nbufs || (bp->flags & BUF_PIN)) { 14650997Sbostic /* Allocate a new one */ 14750997Sbostic bp = malloc(sizeof(struct _bufhead)); 14850997Sbostic if (!bp || !(bp->page = malloc(hashp->BSIZE))) 14950997Sbostic return (NULL); 150*56351Smargo if (hashp->nbufs) 151*56351Smargo hashp->nbufs--; 15250997Sbostic } else { 15350997Sbostic /* Kick someone out */ 15450997Sbostic BUF_REMOVE(bp); 15550997Sbostic /* 15650997Sbostic * If this is an overflow page with addr 0, it's already been 15750997Sbostic * flushed back in an overflow chain and initialized. 15850997Sbostic */ 15950997Sbostic if ((bp->addr != 0) || (bp->flags & BUF_BUCKET)) { 16050997Sbostic /* 16150997Sbostic * Set oaddr before __put_page so that you get it 16250997Sbostic * before bytes are swapped. 16350997Sbostic */ 16450997Sbostic shortp = (u_short *)bp->page; 16550997Sbostic if (shortp[0]) 16650997Sbostic oaddr = shortp[shortp[0] - 1]; 16750997Sbostic if ((bp->flags & BUF_MOD) && __put_page(bp->page, 16850997Sbostic bp->addr, (int)IS_BUCKET(bp->flags), 0)) 16950997Sbostic return (NULL); 17050997Sbostic /* 17150997Sbostic * Update the pointer to this page (i.e. invalidate it). 17250997Sbostic * 17350997Sbostic * If this is a new file (i.e. we created it at open 17450997Sbostic * time), make sure that we mark pages which have been 17550997Sbostic * written to disk so we retrieve them from disk later, 17650997Sbostic * rather than allocating new pages. 17750997Sbostic */ 17850997Sbostic if (IS_BUCKET(bp->flags)) { 17950997Sbostic segment_ndx = bp->addr & (hashp->SGSIZE - 1); 18050997Sbostic segp = hashp->dir[bp->addr >> hashp->SSHIFT]; 18150997Sbostic #ifdef DEBUG 18250997Sbostic assert(segp != NULL); 18350997Sbostic #endif 18446365Sbostic 18550997Sbostic if (hashp->new_file && 18650997Sbostic ((bp->flags & BUF_MOD) || 18750997Sbostic ISDISK(segp[segment_ndx]))) 18850997Sbostic segp[segment_ndx] = (BUFHEAD *)BUF_DISK; 18950997Sbostic else 19050997Sbostic segp[segment_ndx] = NULL; 19150997Sbostic } 19250997Sbostic /* 19350997Sbostic * Since overflow pages can only be access by means of 19450997Sbostic * their bucket, free overflow pages associated with 19550997Sbostic * this bucket. 19650997Sbostic */ 19750997Sbostic for (xbp = bp; xbp->ovfl;) { 19850997Sbostic next_xbp = xbp->ovfl; 19950997Sbostic xbp->ovfl = 0; 20050997Sbostic xbp = next_xbp; 20146365Sbostic 20250997Sbostic /* Check that ovfl pointer is up date. */ 20350997Sbostic if (IS_BUCKET(xbp->flags) || 20450997Sbostic (oaddr != xbp->addr)) 20550997Sbostic break; 20646365Sbostic 20750997Sbostic shortp = (u_short *)xbp->page; 20850997Sbostic if (shortp[0]) 20950997Sbostic /* set before __put_page */ 21050997Sbostic oaddr = shortp[shortp[0] - 1]; 21150997Sbostic if ((xbp->flags & BUF_MOD) && 21250997Sbostic __put_page(xbp->page, xbp->addr, 0, 0)) 21350997Sbostic return (NULL); 21450997Sbostic xbp->addr = 0; 21550997Sbostic xbp->flags = 0; 21650997Sbostic BUF_REMOVE(xbp); 21750997Sbostic LRU_INSERT(xbp); 21850997Sbostic } 21946506Sbostic } 22046365Sbostic } 22146365Sbostic 22250997Sbostic /* Now assign this buffer */ 22350997Sbostic bp->addr = addr; 22446365Sbostic #ifdef DEBUG1 22550997Sbostic (void)fprintf(stderr, "NEWBUF1: %d->ovfl was %d is now %d\n", 22650997Sbostic bp->addr, (bp->ovfl ? bp->ovfl->addr : 0), 0); 22746365Sbostic #endif 22850997Sbostic bp->ovfl = NULL; 22950997Sbostic if (prev_bp) { 23050997Sbostic /* 23150997Sbostic * If prev_bp is set, this is an overflow page, hook it in to 23250997Sbostic * the buffer overflow links. 23350997Sbostic */ 23446365Sbostic #ifdef DEBUG1 23550997Sbostic (void)fprintf(stderr, "NEWBUF2: %d->ovfl was %d is now %d\n", 23650997Sbostic prev_bp->addr, (prev_bp->ovfl ? bp->ovfl->addr : 0), 23750997Sbostic (bp ? bp->addr : 0)); 23846365Sbostic #endif 23950997Sbostic prev_bp->ovfl = bp; 24050997Sbostic bp->flags = 0; 24150997Sbostic } else 24250997Sbostic bp->flags = BUF_BUCKET; 24350997Sbostic MRU_INSERT(bp); 24450997Sbostic return (bp); 24546365Sbostic } 24646365Sbostic 24746365Sbostic extern void 24850997Sbostic __buf_init(nbytes) 24950997Sbostic int nbytes; 25046365Sbostic { 25150997Sbostic BUFHEAD *bfp; 25250997Sbostic int npages; 25346365Sbostic 25450997Sbostic bfp = &(hashp->bufhead); 25550997Sbostic npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT; 25650997Sbostic npages = MAX(npages, MIN_BUFFERS); 25746365Sbostic 25850997Sbostic hashp->nbufs = npages; 25950997Sbostic bfp->next = bfp; 26050997Sbostic bfp->prev = bfp; 26150997Sbostic /* 26250997Sbostic * This space is calloc'd so these are already null. 26350997Sbostic * 26450997Sbostic * bfp->ovfl = NULL; 26550997Sbostic * bfp->flags = 0; 26650997Sbostic * bfp->page = NULL; 26750997Sbostic * bfp->addr = 0; 26850997Sbostic */ 26946365Sbostic } 27046365Sbostic 27146365Sbostic extern int 27250997Sbostic __buf_free(do_free, to_disk) 27350997Sbostic int do_free, to_disk; 27446365Sbostic { 27550997Sbostic BUFHEAD *bp; 27646365Sbostic 27750997Sbostic /* Need to make sure that buffer manager has been initialized */ 27850997Sbostic if (!LRU) 27950997Sbostic return (0); 28050997Sbostic for (bp = LRU; bp != &hashp->bufhead;) { 28150997Sbostic /* Check that the buffer is valid */ 28250997Sbostic if (bp->addr || IS_BUCKET(bp->flags)) { 28350997Sbostic if (to_disk && (bp->flags & BUF_MOD) && 28450997Sbostic __put_page(bp->page, 28550997Sbostic bp->addr, IS_BUCKET(bp->flags), 0)) 28650997Sbostic return (-1); 28750997Sbostic } 28850997Sbostic /* Check if we are freeing stuff */ 28950997Sbostic if (do_free) { 29050997Sbostic if (bp->page) 29150997Sbostic free(bp->page); 29250997Sbostic BUF_REMOVE(bp); 29350997Sbostic free(bp); 29450997Sbostic bp = LRU; 29550997Sbostic } else 29650997Sbostic bp = bp->prev; 29746365Sbostic } 29850997Sbostic return (0); 29946365Sbostic } 30046365Sbostic 30146365Sbostic extern void 30250997Sbostic __reclaim_buf(bp) 30350997Sbostic BUFHEAD *bp; 30446365Sbostic { 30550997Sbostic bp->ovfl = 0; 30650997Sbostic bp->addr = 0; 30750997Sbostic bp->flags = 0; 30850997Sbostic BUF_REMOVE(bp); 30950997Sbostic LRU_INSERT(bp); 31046365Sbostic } 311