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*57932Sbostic static char sccsid[] = "@(#)hash_buf.c 5.12 (Berkeley) 02/11/93"; 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 40*57932Sbostic #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 * 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 */ 15250997Sbostic bp = malloc(sizeof(struct _bufhead)); 15350997Sbostic if (!bp || !(bp->page = malloc(hashp->BSIZE))) 15450997Sbostic return (NULL); 15556351Smargo if (hashp->nbufs) 15656351Smargo hashp->nbufs--; 15750997Sbostic } else { 15850997Sbostic /* Kick someone out */ 15950997Sbostic BUF_REMOVE(bp); 16050997Sbostic /* 16150997Sbostic * If this is an overflow page with addr 0, it's already been 16250997Sbostic * flushed back in an overflow chain and initialized. 16350997Sbostic */ 16450997Sbostic if ((bp->addr != 0) || (bp->flags & BUF_BUCKET)) { 16550997Sbostic /* 16650997Sbostic * Set oaddr before __put_page so that you get it 16750997Sbostic * before bytes are swapped. 16850997Sbostic */ 16950997Sbostic shortp = (u_short *)bp->page; 17050997Sbostic if (shortp[0]) 17150997Sbostic oaddr = shortp[shortp[0] - 1]; 17257586Sbostic if ((bp->flags & BUF_MOD) && __put_page(hashp, bp->page, 17350997Sbostic bp->addr, (int)IS_BUCKET(bp->flags), 0)) 17450997Sbostic return (NULL); 17550997Sbostic /* 17650997Sbostic * Update the pointer to this page (i.e. invalidate it). 17750997Sbostic * 17850997Sbostic * If this is a new file (i.e. we created it at open 17950997Sbostic * time), make sure that we mark pages which have been 18050997Sbostic * written to disk so we retrieve them from disk later, 18150997Sbostic * rather than allocating new pages. 18250997Sbostic */ 18350997Sbostic if (IS_BUCKET(bp->flags)) { 18450997Sbostic segment_ndx = bp->addr & (hashp->SGSIZE - 1); 18550997Sbostic segp = hashp->dir[bp->addr >> hashp->SSHIFT]; 18650997Sbostic #ifdef DEBUG 18750997Sbostic assert(segp != NULL); 18850997Sbostic #endif 18946365Sbostic 19050997Sbostic if (hashp->new_file && 19150997Sbostic ((bp->flags & BUF_MOD) || 19250997Sbostic ISDISK(segp[segment_ndx]))) 19350997Sbostic segp[segment_ndx] = (BUFHEAD *)BUF_DISK; 19450997Sbostic else 19550997Sbostic segp[segment_ndx] = NULL; 19650997Sbostic } 19750997Sbostic /* 19850997Sbostic * Since overflow pages can only be access by means of 19950997Sbostic * their bucket, free overflow pages associated with 20050997Sbostic * this bucket. 20150997Sbostic */ 20250997Sbostic for (xbp = bp; xbp->ovfl;) { 20350997Sbostic next_xbp = xbp->ovfl; 20450997Sbostic xbp->ovfl = 0; 20550997Sbostic xbp = next_xbp; 20646365Sbostic 20750997Sbostic /* Check that ovfl pointer is up date. */ 20850997Sbostic if (IS_BUCKET(xbp->flags) || 20950997Sbostic (oaddr != xbp->addr)) 21050997Sbostic break; 21146365Sbostic 21250997Sbostic shortp = (u_short *)xbp->page; 21350997Sbostic if (shortp[0]) 21450997Sbostic /* set before __put_page */ 21550997Sbostic oaddr = shortp[shortp[0] - 1]; 21657586Sbostic if ((xbp->flags & BUF_MOD) && __put_page(hashp, 21757586Sbostic xbp->page, xbp->addr, 0, 0)) 21850997Sbostic return (NULL); 21950997Sbostic xbp->addr = 0; 22050997Sbostic xbp->flags = 0; 22150997Sbostic BUF_REMOVE(xbp); 22250997Sbostic LRU_INSERT(xbp); 22350997Sbostic } 22446506Sbostic } 22546365Sbostic } 22646365Sbostic 22750997Sbostic /* Now assign this buffer */ 22850997Sbostic bp->addr = addr; 22946365Sbostic #ifdef DEBUG1 23050997Sbostic (void)fprintf(stderr, "NEWBUF1: %d->ovfl was %d is now %d\n", 23150997Sbostic bp->addr, (bp->ovfl ? bp->ovfl->addr : 0), 0); 23246365Sbostic #endif 23350997Sbostic bp->ovfl = NULL; 23450997Sbostic if (prev_bp) { 23550997Sbostic /* 23650997Sbostic * If prev_bp is set, this is an overflow page, hook it in to 23750997Sbostic * the buffer overflow links. 23850997Sbostic */ 23946365Sbostic #ifdef DEBUG1 24050997Sbostic (void)fprintf(stderr, "NEWBUF2: %d->ovfl was %d is now %d\n", 24150997Sbostic prev_bp->addr, (prev_bp->ovfl ? bp->ovfl->addr : 0), 24250997Sbostic (bp ? bp->addr : 0)); 24346365Sbostic #endif 24450997Sbostic prev_bp->ovfl = bp; 24550997Sbostic bp->flags = 0; 24650997Sbostic } else 24750997Sbostic bp->flags = BUF_BUCKET; 24850997Sbostic MRU_INSERT(bp); 24950997Sbostic return (bp); 25046365Sbostic } 25146365Sbostic 25246365Sbostic extern void 25357586Sbostic __buf_init(hashp, nbytes) 25457586Sbostic HTAB *hashp; 25550997Sbostic int nbytes; 25646365Sbostic { 25750997Sbostic BUFHEAD *bfp; 25850997Sbostic int npages; 25946365Sbostic 26050997Sbostic bfp = &(hashp->bufhead); 26150997Sbostic npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT; 26250997Sbostic npages = MAX(npages, MIN_BUFFERS); 26346365Sbostic 26450997Sbostic hashp->nbufs = npages; 26550997Sbostic bfp->next = bfp; 26650997Sbostic bfp->prev = bfp; 26750997Sbostic /* 26850997Sbostic * This space is calloc'd so these are already null. 26950997Sbostic * 27050997Sbostic * bfp->ovfl = NULL; 27150997Sbostic * bfp->flags = 0; 27250997Sbostic * bfp->page = NULL; 27350997Sbostic * bfp->addr = 0; 27450997Sbostic */ 27546365Sbostic } 27646365Sbostic 27746365Sbostic extern int 27857586Sbostic __buf_free(hashp, do_free, to_disk) 27957586Sbostic HTAB *hashp; 28050997Sbostic int do_free, to_disk; 28146365Sbostic { 28250997Sbostic BUFHEAD *bp; 28346365Sbostic 28450997Sbostic /* Need to make sure that buffer manager has been initialized */ 28550997Sbostic if (!LRU) 28650997Sbostic return (0); 28750997Sbostic for (bp = LRU; bp != &hashp->bufhead;) { 28850997Sbostic /* Check that the buffer is valid */ 28950997Sbostic if (bp->addr || IS_BUCKET(bp->flags)) { 29050997Sbostic if (to_disk && (bp->flags & BUF_MOD) && 29157586Sbostic __put_page(hashp, bp->page, 29250997Sbostic bp->addr, IS_BUCKET(bp->flags), 0)) 29350997Sbostic return (-1); 29450997Sbostic } 29550997Sbostic /* Check if we are freeing stuff */ 29650997Sbostic if (do_free) { 29750997Sbostic if (bp->page) 29850997Sbostic free(bp->page); 29950997Sbostic BUF_REMOVE(bp); 30050997Sbostic free(bp); 30150997Sbostic bp = LRU; 30250997Sbostic } else 30350997Sbostic bp = bp->prev; 30446365Sbostic } 30550997Sbostic return (0); 30646365Sbostic } 30746365Sbostic 30846365Sbostic extern void 30957586Sbostic __reclaim_buf(hashp, bp) 31057586Sbostic HTAB *hashp; 31150997Sbostic BUFHEAD *bp; 31246365Sbostic { 31350997Sbostic bp->ovfl = 0; 31450997Sbostic bp->addr = 0; 31550997Sbostic bp->flags = 0; 31650997Sbostic BUF_REMOVE(bp); 31750997Sbostic LRU_INSERT(bp); 31846365Sbostic } 319