1*46365Sbostic /*- 2*46365Sbostic * Copyright (c) 1990 The Regents of the University of California. 3*46365Sbostic * All rights reserved. 4*46365Sbostic * 5*46365Sbostic * This code is derived from software contributed to Berkeley by 6*46365Sbostic * Margo Seltzer. 7*46365Sbostic * 8*46365Sbostic * %sccs.include.redist.c% 9*46365Sbostic */ 10*46365Sbostic 11*46365Sbostic #if defined(LIBC_SCCS) && !defined(lint) 12*46365Sbostic static char sccsid[] = "@(#)hash_buf.c 5.1 (Berkeley) 02/12/91"; 13*46365Sbostic #endif /* LIBC_SCCS and not lint */ 14*46365Sbostic 15*46365Sbostic /****************************************************************************** 16*46365Sbostic 17*46365Sbostic PACKAGE: hash 18*46365Sbostic 19*46365Sbostic DESCRIPTION: 20*46365Sbostic Contains buffer management 21*46365Sbostic 22*46365Sbostic ROUTINES: 23*46365Sbostic External 24*46365Sbostic __buf_init 25*46365Sbostic __get_buf 26*46365Sbostic __buf_free 27*46365Sbostic __reclaim_buf 28*46365Sbostic Internal 29*46365Sbostic newbuf 30*46365Sbostic 31*46365Sbostic ******************************************************************************/ 32*46365Sbostic #include <sys/param.h> 33*46365Sbostic #include <sys/file.h> 34*46365Sbostic #include <assert.h> 35*46365Sbostic #include <errno.h> 36*46365Sbostic #include "hash.h" 37*46365Sbostic #include <stdio.h> 38*46365Sbostic 39*46365Sbostic /* Externals */ 40*46365Sbostic extern HTAB *hashp; 41*46365Sbostic 42*46365Sbostic /* My internals */ 43*46365Sbostic static BUFHEAD *newbuf(); 44*46365Sbostic 45*46365Sbostic /* Unlink B from its place in the lru */ 46*46365Sbostic #define BUF_REMOVE(B) \ 47*46365Sbostic { \ 48*46365Sbostic B->prev->next = B->next; \ 49*46365Sbostic B->next->prev = B->prev; \ 50*46365Sbostic } 51*46365Sbostic 52*46365Sbostic /* Insert B after P */ 53*46365Sbostic #define BUF_INSERT(B,P) \ 54*46365Sbostic { \ 55*46365Sbostic B->next = P->next; \ 56*46365Sbostic B->prev = P; \ 57*46365Sbostic P->next = B; \ 58*46365Sbostic B->next->prev = B; \ 59*46365Sbostic } 60*46365Sbostic 61*46365Sbostic #define MRU hashp->bufhead.next 62*46365Sbostic #define LRU hashp->bufhead.prev 63*46365Sbostic 64*46365Sbostic #define MRU_INSERT(B) BUF_INSERT(B,(&hashp->bufhead)) 65*46365Sbostic #define LRU_INSERT(B) BUF_INSERT(B,LRU) 66*46365Sbostic 67*46365Sbostic /* 68*46365Sbostic We are looking for a buffer with address "addr". 69*46365Sbostic If prev_bp is NULL, then address is a bucket index. 70*46365Sbostic If prev_bp is not NULL, then it points to the page previous 71*46365Sbostic to an overflow page that we are trying to find. 72*46365Sbostic 73*46365Sbostic CAVEAT: The buffer header accessed via prev_bp's ovfl field 74*46365Sbostic may no longer be valid. Therefore, you must always verify that 75*46365Sbostic its address matches the address you are seeking. 76*46365Sbostic */ 77*46365Sbostic extern BUFHEAD * 78*46365Sbostic __get_buf ( addr, prev_bp, newpage ) 79*46365Sbostic int addr; 80*46365Sbostic BUFHEAD *prev_bp; 81*46365Sbostic int newpage; /* If prev_bp is set, indicates that this is 82*46365Sbostic a new overflow page */ 83*46365Sbostic { 84*46365Sbostic register int segment_ndx; 85*46365Sbostic register BUFHEAD *bp; 86*46365Sbostic register unsigned is_disk = 0; 87*46365Sbostic SEGMENT segp; 88*46365Sbostic 89*46365Sbostic if ( prev_bp ) { 90*46365Sbostic bp = prev_bp->ovfl; 91*46365Sbostic if ( !bp || (bp->addr != addr) ) bp = NULL; 92*46365Sbostic if ( !newpage ) is_disk = BUF_DISK; 93*46365Sbostic } 94*46365Sbostic else { 95*46365Sbostic /* Grab buffer out of directory */ 96*46365Sbostic segment_ndx = addr & ( hashp->SGSIZE - 1 ); 97*46365Sbostic 98*46365Sbostic /* 99*46365Sbostic * valid segment ensured by __call_hash() 100*46365Sbostic */ 101*46365Sbostic segp = hashp->dir[addr >> hashp->SSHIFT]; 102*46365Sbostic #ifdef DEBUG 103*46365Sbostic assert(segp != NULL); 104*46365Sbostic #endif 105*46365Sbostic bp = PTROF(segp[segment_ndx]); 106*46365Sbostic is_disk = ISDISK(segp[segment_ndx]); 107*46365Sbostic } 108*46365Sbostic 109*46365Sbostic if ( !bp ) { 110*46365Sbostic bp = newbuf ( addr, prev_bp ); 111*46365Sbostic if ( !bp || __get_page ( bp->page, addr, !prev_bp, (int)is_disk, 0 )) { 112*46365Sbostic return(NULL); 113*46365Sbostic } 114*46365Sbostic if ( !prev_bp ) { 115*46365Sbostic segp[segment_ndx] = (BUFHEAD *)((unsigned)bp | is_disk); 116*46365Sbostic } 117*46365Sbostic } else { 118*46365Sbostic BUF_REMOVE ( bp ); 119*46365Sbostic MRU_INSERT ( bp ); 120*46365Sbostic } 121*46365Sbostic return(bp); 122*46365Sbostic } 123*46365Sbostic 124*46365Sbostic /* 125*46365Sbostic We need a buffer for this page. Either allocate one, or 126*46365Sbostic evict a resident one (if we have as many buffers as we're 127*46365Sbostic allowed) and put this one in. 128*46365Sbostic 129*46365Sbostic If newbuf finds an error (returning NULL), it also sets errno 130*46365Sbostic */ 131*46365Sbostic static BUFHEAD * 132*46365Sbostic newbuf ( addr, prev_bp ) 133*46365Sbostic int addr; 134*46365Sbostic BUFHEAD *prev_bp; 135*46365Sbostic { 136*46365Sbostic register BUFHEAD *bp; /* The buffer we're going to use */ 137*46365Sbostic register BUFHEAD *xbp; /* Temp pointer */ 138*46365Sbostic register BUFHEAD *next_xbp; 139*46365Sbostic int segment_ndx; 140*46365Sbostic u_short *shortp; 141*46365Sbostic u_short oaddr; 142*46365Sbostic SEGMENT segp; 143*46365Sbostic 144*46365Sbostic if ( hashp->nbufs ) { 145*46365Sbostic /* Allocate a new one */ 146*46365Sbostic bp = (BUFHEAD *)malloc ( sizeof (struct _bufhead) ); 147*46365Sbostic if ( !bp || !(bp->page = (char *)malloc ( hashp->BSIZE )) ) { 148*46365Sbostic return (NULL); 149*46365Sbostic } 150*46365Sbostic hashp->nbufs--; 151*46365Sbostic } else { 152*46365Sbostic /* Kick someone out */ 153*46365Sbostic bp = LRU; 154*46365Sbostic BUF_REMOVE( bp ); 155*46365Sbostic /* 156*46365Sbostic Set oaddr before __put_page so that you get it 157*46365Sbostic before bytes are swapped 158*46365Sbostic */ 159*46365Sbostic shortp = (u_short *)bp->page; 160*46365Sbostic oaddr = shortp[shortp[0]-1]; 161*46365Sbostic if ( (bp->flags & BUF_MOD) && 162*46365Sbostic __put_page(bp->page, bp->addr, (int)IS_BUCKET(bp->flags), 0) ) { 163*46365Sbostic return(NULL); 164*46365Sbostic } 165*46365Sbostic /* 166*46365Sbostic Update the pointer to this page (i.e. invalidate it). 167*46365Sbostic 168*46365Sbostic If this is a new file (i.e. we created it at open time), 169*46365Sbostic make sure that we mark pages which have been written to 170*46365Sbostic disk so we retrieve them from disk later, rather than 171*46365Sbostic allocating new pages. 172*46365Sbostic */ 173*46365Sbostic 174*46365Sbostic if ( IS_BUCKET(bp->flags)) { 175*46365Sbostic segment_ndx = bp->addr & ( hashp->SGSIZE - 1 ); 176*46365Sbostic 177*46365Sbostic segp = hashp->dir[bp->addr >> hashp->SSHIFT]; 178*46365Sbostic 179*46365Sbostic assert(segp != NULL); 180*46365Sbostic 181*46365Sbostic if ( hashp->new_file && 182*46365Sbostic ((bp->flags & BUF_MOD) || ISDISK(segp[segment_ndx])) ) { 183*46365Sbostic segp[segment_ndx] = (BUFHEAD *)BUF_DISK; 184*46365Sbostic } else segp[segment_ndx] = NULL; 185*46365Sbostic } 186*46365Sbostic 187*46365Sbostic /* 188*46365Sbostic Since overflow pages can only be access by means of 189*46365Sbostic their bucket, free overflow pages associated with this 190*46365Sbostic bucket. 191*46365Sbostic */ 192*46365Sbostic for ( xbp = bp; xbp->ovfl; ) { 193*46365Sbostic 194*46365Sbostic next_xbp = xbp->ovfl; 195*46365Sbostic xbp->ovfl = 0; 196*46365Sbostic xbp = next_xbp; 197*46365Sbostic 198*46365Sbostic /* Check that ovfl pointer is up date */ 199*46365Sbostic if ( IS_BUCKET(xbp->flags) || (oaddr != xbp->addr) ) break; 200*46365Sbostic 201*46365Sbostic shortp = (u_short *)xbp->page; 202*46365Sbostic oaddr = shortp[shortp[0]-1]; /* set before __put_page */ 203*46365Sbostic if ( (xbp->flags & BUF_MOD) && 204*46365Sbostic __put_page ( xbp->page, xbp->addr, 0, 0 ) ) { 205*46365Sbostic return(NULL); 206*46365Sbostic } 207*46365Sbostic xbp->addr = 0; 208*46365Sbostic xbp->flags = 0; 209*46365Sbostic BUF_REMOVE ( xbp ); 210*46365Sbostic LRU_INSERT ( xbp ); 211*46365Sbostic } 212*46365Sbostic } 213*46365Sbostic 214*46365Sbostic /* Now assign this buffer */ 215*46365Sbostic bp->addr = addr; 216*46365Sbostic #ifdef DEBUG1 217*46365Sbostic fprintf ( stderr, "NEWBUF1: %d->ovfl was %d is now %d\n", bp->addr, 218*46365Sbostic (bp->ovfl?bp->ovfl->addr:0), 0); 219*46365Sbostic #endif 220*46365Sbostic bp->ovfl = NULL; 221*46365Sbostic if ( prev_bp ) { 222*46365Sbostic /* 223*46365Sbostic If prev_bp is set, this is an overflow page, hook it in to the 224*46365Sbostic buffer overflow links 225*46365Sbostic */ 226*46365Sbostic #ifdef DEBUG1 227*46365Sbostic fprintf ( stderr, "NEWBUF2: %d->ovfl was %d is now %d\n", prev_bp->addr, 228*46365Sbostic (prev_bp->ovfl?bp->ovfl->addr:0), 229*46365Sbostic (bp?bp->addr: 0)); 230*46365Sbostic #endif 231*46365Sbostic prev_bp->ovfl = bp; 232*46365Sbostic bp->flags = 0; 233*46365Sbostic } else bp->flags = BUF_BUCKET; 234*46365Sbostic MRU_INSERT ( bp ); 235*46365Sbostic return ( bp ); 236*46365Sbostic } 237*46365Sbostic 238*46365Sbostic extern void 239*46365Sbostic __buf_init ( nbytes ) 240*46365Sbostic int nbytes; 241*46365Sbostic { 242*46365Sbostic int npages; 243*46365Sbostic BUFHEAD *bfp = &(hashp->bufhead); 244*46365Sbostic 245*46365Sbostic npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT; 246*46365Sbostic npages = MAX ( npages, MIN_BUFFERS ); 247*46365Sbostic 248*46365Sbostic hashp->nbufs = npages; 249*46365Sbostic bfp->next = bfp; 250*46365Sbostic bfp->prev = bfp; 251*46365Sbostic /* 252*46365Sbostic This space is calloc'd so these are already null 253*46365Sbostic 254*46365Sbostic bfp->ovfl = NULL; 255*46365Sbostic bfp->flags = 0; 256*46365Sbostic bfp->page = NULL; 257*46365Sbostic bfp->addr = 0; 258*46365Sbostic */ 259*46365Sbostic } 260*46365Sbostic 261*46365Sbostic extern int 262*46365Sbostic __buf_free ( do_free, to_disk ) 263*46365Sbostic int do_free; 264*46365Sbostic int to_disk; 265*46365Sbostic { 266*46365Sbostic BUFHEAD *bp; 267*46365Sbostic 268*46365Sbostic /* Need to make sure that buffer manager has been initialized */ 269*46365Sbostic if ( !LRU ) { 270*46365Sbostic return(0); 271*46365Sbostic } 272*46365Sbostic 273*46365Sbostic for ( bp = LRU; bp != &hashp->bufhead; ) { 274*46365Sbostic /* Check that the buffer is valid */ 275*46365Sbostic if ( bp->addr || IS_BUCKET(bp->flags) ) { 276*46365Sbostic if ( to_disk && (bp->flags & BUF_MOD) && 277*46365Sbostic __put_page (bp->page, bp->addr, IS_BUCKET(bp->flags), 0 )) { 278*46365Sbostic return (-1); 279*46365Sbostic } 280*46365Sbostic } 281*46365Sbostic 282*46365Sbostic /* Check if we are freeing stuff */ 283*46365Sbostic if ( do_free ) { 284*46365Sbostic if ( bp->page ) free ( bp->page ); 285*46365Sbostic BUF_REMOVE(bp); 286*46365Sbostic (void)free ( bp ); 287*46365Sbostic bp = LRU; 288*46365Sbostic } else bp = bp->prev; 289*46365Sbostic } 290*46365Sbostic 291*46365Sbostic return(0); 292*46365Sbostic } 293*46365Sbostic 294*46365Sbostic extern void 295*46365Sbostic __reclaim_buf ( bp ) 296*46365Sbostic BUFHEAD *bp; 297*46365Sbostic { 298*46365Sbostic bp->ovfl = 0; 299*46365Sbostic bp->addr = 0; 300*46365Sbostic bp->flags = 0; 301*46365Sbostic BUF_REMOVE ( bp ); 302*46365Sbostic LRU_INSERT ( bp ); 303*46365Sbostic } 304