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*50997Sbostic static char sccsid[] = "@(#)hash_buf.c 5.8 (Berkeley) 09/04/91"; 1346365Sbostic #endif /* LIBC_SCCS and not lint */ 1446365Sbostic 15*50997Sbostic /* 16*50997Sbostic * PACKAGE: hash 17*50997Sbostic * 18*50997Sbostic * DESCRIPTION: 19*50997Sbostic * Contains buffer management 20*50997Sbostic * 21*50997Sbostic * ROUTINES: 22*50997Sbostic * External 23*50997Sbostic * __buf_init 24*50997Sbostic * __get_buf 25*50997Sbostic * __buf_free 26*50997Sbostic * __reclaim_buf 27*50997Sbostic * Internal 28*50997Sbostic * newbuf 29*50997Sbostic */ 3046365Sbostic 3146644Sbostic #include <sys/param.h> 32*50997Sbostic #include <db.h> 3346365Sbostic #include <errno.h> 3446562Sbostic #include <stdio.h> 3546562Sbostic #include <stdlib.h> 36*50997Sbostic #ifdef DEBUG 37*50997Sbostic #include <assert.h> 38*50997Sbostic #endif 3946365Sbostic #include "hash.h" 40*50997Sbostic #include "page.h" 41*50997Sbostic #include "extern.h" 4246365Sbostic 43*50997Sbostic static BUFHEAD *newbuf __P((u_int, BUFHEAD *)); 4446365Sbostic 4546365Sbostic /* Unlink B from its place in the lru */ 46*50997Sbostic #define BUF_REMOVE(B) { \ 47*50997Sbostic (B)->prev->next = (B)->next; \ 48*50997Sbostic (B)->next->prev = (B)->prev; \ 4946365Sbostic } 5046365Sbostic 5146365Sbostic /* Insert B after P */ 52*50997Sbostic #define BUF_INSERT(B, P) { \ 53*50997Sbostic (B)->next = (P)->next; \ 54*50997Sbostic (B)->prev = (P); \ 55*50997Sbostic (P)->next = (B); \ 56*50997Sbostic (B)->next->prev = (B); \ 5746365Sbostic } 5846365Sbostic 5946365Sbostic #define MRU hashp->bufhead.next 6046365Sbostic #define LRU hashp->bufhead.prev 6146365Sbostic 62*50997Sbostic #define MRU_INSERT(B) BUF_INSERT((B), &hashp->bufhead) 63*50997Sbostic #define LRU_INSERT(B) BUF_INSERT((B), LRU) 6446365Sbostic 6546365Sbostic /* 66*50997Sbostic * We are looking for a buffer with address "addr". If prev_bp is NULL, then 67*50997Sbostic * address is a bucket index. If prev_bp is not NULL, then it points to the 68*50997Sbostic * page previous to an overflow page that we are trying to find. 69*50997Sbostic * 70*50997Sbostic * CAVEAT: The buffer header accessed via prev_bp's ovfl field may no longer 71*50997Sbostic * be valid. Therefore, you must always verify that its address matches the 72*50997Sbostic * address you are seeking. 73*50997Sbostic */ 7446365Sbostic extern BUFHEAD * 75*50997Sbostic __get_buf(addr, prev_bp, newpage) 76*50997Sbostic u_int addr; 77*50997Sbostic BUFHEAD *prev_bp; 78*50997Sbostic int newpage; /* If prev_bp set, indicates a new overflow page. */ 7946365Sbostic { 80*50997Sbostic register BUFHEAD *bp; 81*50997Sbostic register u_int is_disk, is_disk_mask; 82*50997Sbostic register int segment_ndx; 83*50997Sbostic SEGMENT segp; 8446365Sbostic 85*50997Sbostic is_disk = 0; 86*50997Sbostic is_disk_mask = 0; 87*50997Sbostic if (prev_bp) { 88*50997Sbostic bp = prev_bp->ovfl; 89*50997Sbostic if (!bp || (bp->addr != addr)) 90*50997Sbostic bp = NULL; 91*50997Sbostic if (!newpage) 92*50997Sbostic is_disk = BUF_DISK; 93*50997Sbostic } else { 94*50997Sbostic /* Grab buffer out of directory */ 95*50997Sbostic segment_ndx = addr & (hashp->SGSIZE - 1); 9646365Sbostic 97*50997Sbostic /* valid segment ensured by __call_hash() */ 98*50997Sbostic segp = hashp->dir[addr >> hashp->SSHIFT]; 9946365Sbostic #ifdef DEBUG 100*50997Sbostic assert(segp != NULL); 10146365Sbostic #endif 102*50997Sbostic bp = PTROF(segp[segment_ndx]); 103*50997Sbostic is_disk_mask = ISDISK(segp[segment_ndx]); 104*50997Sbostic is_disk = is_disk_mask || !hashp->new_file; 105*50997Sbostic } 10646365Sbostic 107*50997Sbostic if (!bp) { 108*50997Sbostic bp = newbuf(addr, prev_bp); 109*50997Sbostic if (!bp || __get_page(bp->page, addr, !prev_bp, is_disk, 0)) 110*50997Sbostic return (NULL); 111*50997Sbostic if (!prev_bp) 112*50997Sbostic segp[segment_ndx] = 113*50997Sbostic (BUFHEAD *)((u_int)bp | is_disk_mask); 114*50997Sbostic } else { 115*50997Sbostic BUF_REMOVE(bp); 116*50997Sbostic MRU_INSERT(bp); 11746365Sbostic } 118*50997Sbostic return (bp); 11946365Sbostic } 12046365Sbostic 12146365Sbostic /* 122*50997Sbostic * We need a buffer for this page. Either allocate one, or evict a resident 123*50997Sbostic * one (if we have as many buffers as we're allowed) and put this one in. 124*50997Sbostic * 125*50997Sbostic * If newbuf finds an error (returning NULL), it also sets errno. 126*50997Sbostic */ 12746365Sbostic static BUFHEAD * 128*50997Sbostic newbuf(addr, prev_bp) 129*50997Sbostic u_int addr; 130*50997Sbostic BUFHEAD *prev_bp; 13146365Sbostic { 132*50997Sbostic register BUFHEAD *bp; /* The buffer we're going to use */ 133*50997Sbostic register BUFHEAD *xbp; /* Temp pointer */ 134*50997Sbostic register BUFHEAD *next_xbp; 135*50997Sbostic SEGMENT segp; 136*50997Sbostic int segment_ndx; 137*50997Sbostic u_short oaddr, *shortp; 13846365Sbostic 139*50997Sbostic oaddr = 0; 140*50997Sbostic bp = LRU; 141*50997Sbostic /* 142*50997Sbostic * If LRU buffer is pinned, the buffer pool is too small. We need to 143*50997Sbostic * allocate more buffers. 144*50997Sbostic */ 145*50997Sbostic if (hashp->nbufs || (bp->flags & BUF_PIN)) { 146*50997Sbostic /* Allocate a new one */ 147*50997Sbostic bp = malloc(sizeof(struct _bufhead)); 148*50997Sbostic if (!bp || !(bp->page = malloc(hashp->BSIZE))) 149*50997Sbostic return (NULL); 150*50997Sbostic hashp->nbufs--; 151*50997Sbostic } else { 152*50997Sbostic /* Kick someone out */ 153*50997Sbostic BUF_REMOVE(bp); 154*50997Sbostic /* 155*50997Sbostic * If this is an overflow page with addr 0, it's already been 156*50997Sbostic * flushed back in an overflow chain and initialized. 157*50997Sbostic */ 158*50997Sbostic if ((bp->addr != 0) || (bp->flags & BUF_BUCKET)) { 159*50997Sbostic /* 160*50997Sbostic * Set oaddr before __put_page so that you get it 161*50997Sbostic * before bytes are swapped. 162*50997Sbostic */ 163*50997Sbostic shortp = (u_short *)bp->page; 164*50997Sbostic if (shortp[0]) 165*50997Sbostic oaddr = shortp[shortp[0] - 1]; 166*50997Sbostic if ((bp->flags & BUF_MOD) && __put_page(bp->page, 167*50997Sbostic bp->addr, (int)IS_BUCKET(bp->flags), 0)) 168*50997Sbostic return (NULL); 169*50997Sbostic /* 170*50997Sbostic * Update the pointer to this page (i.e. invalidate it). 171*50997Sbostic * 172*50997Sbostic * If this is a new file (i.e. we created it at open 173*50997Sbostic * time), make sure that we mark pages which have been 174*50997Sbostic * written to disk so we retrieve them from disk later, 175*50997Sbostic * rather than allocating new pages. 176*50997Sbostic */ 177*50997Sbostic if (IS_BUCKET(bp->flags)) { 178*50997Sbostic segment_ndx = bp->addr & (hashp->SGSIZE - 1); 179*50997Sbostic segp = hashp->dir[bp->addr >> hashp->SSHIFT]; 180*50997Sbostic #ifdef DEBUG 181*50997Sbostic assert(segp != NULL); 182*50997Sbostic #endif 18346365Sbostic 184*50997Sbostic if (hashp->new_file && 185*50997Sbostic ((bp->flags & BUF_MOD) || 186*50997Sbostic ISDISK(segp[segment_ndx]))) 187*50997Sbostic segp[segment_ndx] = (BUFHEAD *)BUF_DISK; 188*50997Sbostic else 189*50997Sbostic segp[segment_ndx] = NULL; 190*50997Sbostic } 191*50997Sbostic /* 192*50997Sbostic * Since overflow pages can only be access by means of 193*50997Sbostic * their bucket, free overflow pages associated with 194*50997Sbostic * this bucket. 195*50997Sbostic */ 196*50997Sbostic for (xbp = bp; xbp->ovfl;) { 197*50997Sbostic next_xbp = xbp->ovfl; 198*50997Sbostic xbp->ovfl = 0; 199*50997Sbostic xbp = next_xbp; 20046365Sbostic 201*50997Sbostic /* Check that ovfl pointer is up date. */ 202*50997Sbostic if (IS_BUCKET(xbp->flags) || 203*50997Sbostic (oaddr != xbp->addr)) 204*50997Sbostic break; 20546365Sbostic 206*50997Sbostic shortp = (u_short *)xbp->page; 207*50997Sbostic if (shortp[0]) 208*50997Sbostic /* set before __put_page */ 209*50997Sbostic oaddr = shortp[shortp[0] - 1]; 210*50997Sbostic if ((xbp->flags & BUF_MOD) && 211*50997Sbostic __put_page(xbp->page, xbp->addr, 0, 0)) 212*50997Sbostic return (NULL); 213*50997Sbostic xbp->addr = 0; 214*50997Sbostic xbp->flags = 0; 215*50997Sbostic BUF_REMOVE(xbp); 216*50997Sbostic LRU_INSERT(xbp); 217*50997Sbostic } 21846506Sbostic } 21946365Sbostic } 22046365Sbostic 221*50997Sbostic /* Now assign this buffer */ 222*50997Sbostic bp->addr = addr; 22346365Sbostic #ifdef DEBUG1 224*50997Sbostic (void)fprintf(stderr, "NEWBUF1: %d->ovfl was %d is now %d\n", 225*50997Sbostic bp->addr, (bp->ovfl ? bp->ovfl->addr : 0), 0); 22646365Sbostic #endif 227*50997Sbostic bp->ovfl = NULL; 228*50997Sbostic if (prev_bp) { 229*50997Sbostic /* 230*50997Sbostic * If prev_bp is set, this is an overflow page, hook it in to 231*50997Sbostic * the buffer overflow links. 232*50997Sbostic */ 23346365Sbostic #ifdef DEBUG1 234*50997Sbostic (void)fprintf(stderr, "NEWBUF2: %d->ovfl was %d is now %d\n", 235*50997Sbostic prev_bp->addr, (prev_bp->ovfl ? bp->ovfl->addr : 0), 236*50997Sbostic (bp ? bp->addr : 0)); 23746365Sbostic #endif 238*50997Sbostic prev_bp->ovfl = bp; 239*50997Sbostic bp->flags = 0; 240*50997Sbostic } else 241*50997Sbostic bp->flags = BUF_BUCKET; 242*50997Sbostic MRU_INSERT(bp); 243*50997Sbostic return (bp); 24446365Sbostic } 24546365Sbostic 24646365Sbostic extern void 247*50997Sbostic __buf_init(nbytes) 248*50997Sbostic int nbytes; 24946365Sbostic { 250*50997Sbostic BUFHEAD *bfp; 251*50997Sbostic int npages; 25246365Sbostic 253*50997Sbostic bfp = &(hashp->bufhead); 254*50997Sbostic npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT; 255*50997Sbostic npages = MAX(npages, MIN_BUFFERS); 25646365Sbostic 257*50997Sbostic hashp->nbufs = npages; 258*50997Sbostic bfp->next = bfp; 259*50997Sbostic bfp->prev = bfp; 260*50997Sbostic /* 261*50997Sbostic * This space is calloc'd so these are already null. 262*50997Sbostic * 263*50997Sbostic * bfp->ovfl = NULL; 264*50997Sbostic * bfp->flags = 0; 265*50997Sbostic * bfp->page = NULL; 266*50997Sbostic * bfp->addr = 0; 267*50997Sbostic */ 26846365Sbostic } 26946365Sbostic 27046365Sbostic extern int 271*50997Sbostic __buf_free(do_free, to_disk) 272*50997Sbostic int do_free, to_disk; 27346365Sbostic { 274*50997Sbostic BUFHEAD *bp; 27546365Sbostic 276*50997Sbostic /* Need to make sure that buffer manager has been initialized */ 277*50997Sbostic if (!LRU) 278*50997Sbostic return (0); 279*50997Sbostic for (bp = LRU; bp != &hashp->bufhead;) { 280*50997Sbostic /* Check that the buffer is valid */ 281*50997Sbostic if (bp->addr || IS_BUCKET(bp->flags)) { 282*50997Sbostic if (to_disk && (bp->flags & BUF_MOD) && 283*50997Sbostic __put_page(bp->page, 284*50997Sbostic bp->addr, IS_BUCKET(bp->flags), 0)) 285*50997Sbostic return (-1); 286*50997Sbostic } 287*50997Sbostic /* Check if we are freeing stuff */ 288*50997Sbostic if (do_free) { 289*50997Sbostic if (bp->page) 290*50997Sbostic free(bp->page); 291*50997Sbostic BUF_REMOVE(bp); 292*50997Sbostic free(bp); 293*50997Sbostic bp = LRU; 294*50997Sbostic } else 295*50997Sbostic bp = bp->prev; 29646365Sbostic } 297*50997Sbostic return (0); 29846365Sbostic } 29946365Sbostic 30046365Sbostic extern void 301*50997Sbostic __reclaim_buf(bp) 302*50997Sbostic BUFHEAD *bp; 30346365Sbostic { 304*50997Sbostic bp->ovfl = 0; 305*50997Sbostic bp->addr = 0; 306*50997Sbostic bp->flags = 0; 307*50997Sbostic BUF_REMOVE(bp); 308*50997Sbostic LRU_INSERT(bp); 30946365Sbostic } 310