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*46456Sbostic static char sccsid[] = "@(#)hash_buf.c 5.2 (Berkeley) 02/19/91"; 1346365Sbostic #endif /* LIBC_SCCS and not lint */ 1446365Sbostic 1546365Sbostic /****************************************************************************** 1646365Sbostic 1746365Sbostic PACKAGE: hash 1846365Sbostic 1946365Sbostic DESCRIPTION: 2046365Sbostic Contains buffer management 2146365Sbostic 2246365Sbostic ROUTINES: 2346365Sbostic External 2446365Sbostic __buf_init 2546365Sbostic __get_buf 2646365Sbostic __buf_free 2746365Sbostic __reclaim_buf 2846365Sbostic Internal 2946365Sbostic newbuf 3046365Sbostic 3146365Sbostic ******************************************************************************/ 3246365Sbostic #include <sys/param.h> 3346365Sbostic #include <sys/file.h> 3446365Sbostic #include <assert.h> 3546365Sbostic #include <errno.h> 3646365Sbostic #include "hash.h" 3746365Sbostic #include <stdio.h> 3846365Sbostic 3946365Sbostic /* Externals */ 4046365Sbostic extern HTAB *hashp; 4146365Sbostic 4246365Sbostic /* My internals */ 4346365Sbostic static BUFHEAD *newbuf(); 4446365Sbostic 4546365Sbostic /* Unlink B from its place in the lru */ 4646365Sbostic #define BUF_REMOVE(B) \ 4746365Sbostic { \ 4846365Sbostic B->prev->next = B->next; \ 4946365Sbostic B->next->prev = B->prev; \ 5046365Sbostic } 5146365Sbostic 5246365Sbostic /* Insert B after P */ 5346365Sbostic #define BUF_INSERT(B,P) \ 5446365Sbostic { \ 5546365Sbostic B->next = P->next; \ 5646365Sbostic B->prev = P; \ 5746365Sbostic P->next = B; \ 5846365Sbostic B->next->prev = B; \ 5946365Sbostic } 6046365Sbostic 6146365Sbostic #define MRU hashp->bufhead.next 6246365Sbostic #define LRU hashp->bufhead.prev 6346365Sbostic 6446365Sbostic #define MRU_INSERT(B) BUF_INSERT(B,(&hashp->bufhead)) 6546365Sbostic #define LRU_INSERT(B) BUF_INSERT(B,LRU) 6646365Sbostic 6746365Sbostic /* 6846365Sbostic We are looking for a buffer with address "addr". 6946365Sbostic If prev_bp is NULL, then address is a bucket index. 7046365Sbostic If prev_bp is not NULL, then it points to the page previous 7146365Sbostic to an overflow page that we are trying to find. 7246365Sbostic 7346365Sbostic CAVEAT: The buffer header accessed via prev_bp's ovfl field 7446365Sbostic may no longer be valid. Therefore, you must always verify that 7546365Sbostic its address matches the address you are seeking. 7646365Sbostic */ 7746365Sbostic extern BUFHEAD * 7846365Sbostic __get_buf ( addr, prev_bp, newpage ) 7946365Sbostic int addr; 8046365Sbostic BUFHEAD *prev_bp; 8146365Sbostic int newpage; /* If prev_bp is set, indicates that this is 8246365Sbostic a new overflow page */ 8346365Sbostic { 8446365Sbostic register int segment_ndx; 8546365Sbostic register BUFHEAD *bp; 8646365Sbostic register unsigned is_disk = 0; 8746365Sbostic SEGMENT segp; 8846365Sbostic 8946365Sbostic if ( prev_bp ) { 9046365Sbostic bp = prev_bp->ovfl; 9146365Sbostic if ( !bp || (bp->addr != addr) ) bp = NULL; 9246365Sbostic if ( !newpage ) is_disk = BUF_DISK; 9346365Sbostic } 9446365Sbostic else { 9546365Sbostic /* Grab buffer out of directory */ 9646365Sbostic segment_ndx = addr & ( hashp->SGSIZE - 1 ); 9746365Sbostic 9846365Sbostic /* 9946365Sbostic * valid segment ensured by __call_hash() 10046365Sbostic */ 10146365Sbostic segp = hashp->dir[addr >> hashp->SSHIFT]; 10246365Sbostic #ifdef DEBUG 10346365Sbostic assert(segp != NULL); 10446365Sbostic #endif 10546365Sbostic bp = PTROF(segp[segment_ndx]); 10646365Sbostic is_disk = ISDISK(segp[segment_ndx]); 10746365Sbostic } 10846365Sbostic 10946365Sbostic if ( !bp ) { 11046365Sbostic bp = newbuf ( addr, prev_bp ); 11146365Sbostic if ( !bp || __get_page ( bp->page, addr, !prev_bp, (int)is_disk, 0 )) { 11246365Sbostic return(NULL); 11346365Sbostic } 11446365Sbostic if ( !prev_bp ) { 11546365Sbostic segp[segment_ndx] = (BUFHEAD *)((unsigned)bp | is_disk); 11646365Sbostic } 11746365Sbostic } else { 11846365Sbostic BUF_REMOVE ( bp ); 11946365Sbostic MRU_INSERT ( bp ); 12046365Sbostic } 12146365Sbostic return(bp); 12246365Sbostic } 12346365Sbostic 12446365Sbostic /* 12546365Sbostic We need a buffer for this page. Either allocate one, or 12646365Sbostic evict a resident one (if we have as many buffers as we're 12746365Sbostic allowed) and put this one in. 12846365Sbostic 12946365Sbostic If newbuf finds an error (returning NULL), it also sets errno 13046365Sbostic */ 13146365Sbostic static BUFHEAD * 13246365Sbostic newbuf ( addr, prev_bp ) 13346365Sbostic int addr; 13446365Sbostic BUFHEAD *prev_bp; 13546365Sbostic { 13646365Sbostic register BUFHEAD *bp; /* The buffer we're going to use */ 13746365Sbostic register BUFHEAD *xbp; /* Temp pointer */ 13846365Sbostic register BUFHEAD *next_xbp; 13946365Sbostic int segment_ndx; 14046365Sbostic u_short *shortp; 14146365Sbostic u_short oaddr; 14246365Sbostic SEGMENT segp; 14346365Sbostic 144*46456Sbostic bp = LRU; 145*46456Sbostic /* 146*46456Sbostic If LRU buffer is pinned, the buffer pool is too small. 147*46456Sbostic We need to allocate more buffers 148*46456Sbostic */ 149*46456Sbostic if ( hashp->nbufs || (bp->flags & BUF_PIN) ) { 15046365Sbostic /* Allocate a new one */ 15146365Sbostic bp = (BUFHEAD *)malloc ( sizeof (struct _bufhead) ); 15246365Sbostic if ( !bp || !(bp->page = (char *)malloc ( hashp->BSIZE )) ) { 15346365Sbostic return (NULL); 15446365Sbostic } 15546365Sbostic hashp->nbufs--; 15646365Sbostic } else { 15746365Sbostic /* Kick someone out */ 15846365Sbostic BUF_REMOVE( bp ); 15946365Sbostic /* 160*46456Sbostic If this is an overflow page with addr 0, it's already 161*46456Sbostic been flushed back in an overflow chain and initialized 16246365Sbostic */ 163*46456Sbostic if ( (bp->addr != 0) || (bp->flags & BUF_BUCKET) ) { 164*46456Sbostic /* 165*46456Sbostic Set oaddr before __put_page so that you get it 166*46456Sbostic before bytes are swapped 167*46456Sbostic */ 168*46456Sbostic shortp = (u_short *)bp->page; 169*46456Sbostic oaddr = shortp[shortp[0]-1]; 170*46456Sbostic if ( (bp->flags & BUF_MOD) && 171*46456Sbostic __put_page(bp->page, bp->addr, (int)IS_BUCKET(bp->flags), 0) ) { 172*46456Sbostic return(NULL); 173*46456Sbostic } 174*46456Sbostic /* 175*46456Sbostic Update the pointer to this page (i.e. invalidate it). 17646365Sbostic 177*46456Sbostic If this is a new file (i.e. we created it at open time), 178*46456Sbostic make sure that we mark pages which have been written to 179*46456Sbostic disk so we retrieve them from disk later, rather than 180*46456Sbostic allocating new pages. 181*46456Sbostic */ 18246365Sbostic 183*46456Sbostic if ( IS_BUCKET(bp->flags)) { 184*46456Sbostic segment_ndx = bp->addr & ( hashp->SGSIZE - 1 ); 18546365Sbostic 186*46456Sbostic segp = hashp->dir[bp->addr >> hashp->SSHIFT]; 18746365Sbostic 188*46456Sbostic assert(segp != NULL); 18946365Sbostic 190*46456Sbostic if ( hashp->new_file && 191*46456Sbostic ((bp->flags & BUF_MOD) || ISDISK(segp[segment_ndx])) ) { 192*46456Sbostic segp[segment_ndx] = (BUFHEAD *)BUF_DISK; 193*46456Sbostic } else segp[segment_ndx] = NULL; 194*46456Sbostic } 19546365Sbostic 196*46456Sbostic /* 197*46456Sbostic Since overflow pages can only be access by means of 198*46456Sbostic their bucket, free overflow pages associated with this 199*46456Sbostic bucket. 200*46456Sbostic */ 201*46456Sbostic for ( xbp = bp; xbp->ovfl; ) { 20246365Sbostic 203*46456Sbostic next_xbp = xbp->ovfl; 204*46456Sbostic xbp->ovfl = 0; 205*46456Sbostic xbp = next_xbp; 20646365Sbostic 207*46456Sbostic /* Check that ovfl pointer is up date */ 208*46456Sbostic if ( IS_BUCKET(xbp->flags) || (oaddr != xbp->addr) ) break; 20946365Sbostic 210*46456Sbostic shortp = (u_short *)xbp->page; 211*46456Sbostic oaddr = shortp[shortp[0]-1]; /* set before __put_page */ 212*46456Sbostic if ( (xbp->flags & BUF_MOD) && 213*46456Sbostic __put_page ( xbp->page, xbp->addr, 0, 0 ) ) { 214*46456Sbostic return(NULL); 215*46456Sbostic } 216*46456Sbostic xbp->addr = 0; 217*46456Sbostic xbp->flags = 0; 218*46456Sbostic BUF_REMOVE ( xbp ); 219*46456Sbostic LRU_INSERT ( xbp ); 22046365Sbostic } 22146365Sbostic } 22246365Sbostic } 22346365Sbostic 22446365Sbostic /* Now assign this buffer */ 22546365Sbostic bp->addr = addr; 22646365Sbostic #ifdef DEBUG1 22746365Sbostic fprintf ( stderr, "NEWBUF1: %d->ovfl was %d is now %d\n", bp->addr, 22846365Sbostic (bp->ovfl?bp->ovfl->addr:0), 0); 22946365Sbostic #endif 23046365Sbostic bp->ovfl = NULL; 23146365Sbostic if ( prev_bp ) { 23246365Sbostic /* 23346365Sbostic If prev_bp is set, this is an overflow page, hook it in to the 23446365Sbostic buffer overflow links 23546365Sbostic */ 23646365Sbostic #ifdef DEBUG1 23746365Sbostic fprintf ( stderr, "NEWBUF2: %d->ovfl was %d is now %d\n", prev_bp->addr, 23846365Sbostic (prev_bp->ovfl?bp->ovfl->addr:0), 23946365Sbostic (bp?bp->addr: 0)); 24046365Sbostic #endif 24146365Sbostic prev_bp->ovfl = bp; 24246365Sbostic bp->flags = 0; 24346365Sbostic } else bp->flags = BUF_BUCKET; 24446365Sbostic MRU_INSERT ( bp ); 24546365Sbostic return ( bp ); 24646365Sbostic } 24746365Sbostic 24846365Sbostic extern void 24946365Sbostic __buf_init ( nbytes ) 25046365Sbostic int nbytes; 25146365Sbostic { 25246365Sbostic int npages; 25346365Sbostic BUFHEAD *bfp = &(hashp->bufhead); 25446365Sbostic 25546365Sbostic npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT; 25646365Sbostic npages = MAX ( npages, MIN_BUFFERS ); 25746365Sbostic 25846365Sbostic hashp->nbufs = npages; 25946365Sbostic bfp->next = bfp; 26046365Sbostic bfp->prev = bfp; 26146365Sbostic /* 26246365Sbostic This space is calloc'd so these are already null 26346365Sbostic 26446365Sbostic bfp->ovfl = NULL; 26546365Sbostic bfp->flags = 0; 26646365Sbostic bfp->page = NULL; 26746365Sbostic bfp->addr = 0; 26846365Sbostic */ 26946365Sbostic } 27046365Sbostic 27146365Sbostic extern int 27246365Sbostic __buf_free ( do_free, to_disk ) 27346365Sbostic int do_free; 27446365Sbostic int to_disk; 27546365Sbostic { 27646365Sbostic BUFHEAD *bp; 27746365Sbostic 27846365Sbostic /* Need to make sure that buffer manager has been initialized */ 27946365Sbostic if ( !LRU ) { 28046365Sbostic return(0); 28146365Sbostic } 28246365Sbostic 28346365Sbostic for ( bp = LRU; bp != &hashp->bufhead; ) { 28446365Sbostic /* Check that the buffer is valid */ 28546365Sbostic if ( bp->addr || IS_BUCKET(bp->flags) ) { 28646365Sbostic if ( to_disk && (bp->flags & BUF_MOD) && 28746365Sbostic __put_page (bp->page, bp->addr, IS_BUCKET(bp->flags), 0 )) { 28846365Sbostic return (-1); 28946365Sbostic } 29046365Sbostic } 29146365Sbostic 29246365Sbostic /* Check if we are freeing stuff */ 29346365Sbostic if ( do_free ) { 29446365Sbostic if ( bp->page ) free ( bp->page ); 29546365Sbostic BUF_REMOVE(bp); 29646365Sbostic (void)free ( bp ); 29746365Sbostic bp = LRU; 29846365Sbostic } else bp = bp->prev; 29946365Sbostic } 30046365Sbostic 30146365Sbostic return(0); 30246365Sbostic } 30346365Sbostic 30446365Sbostic extern void 30546365Sbostic __reclaim_buf ( bp ) 30646365Sbostic BUFHEAD *bp; 30746365Sbostic { 30846365Sbostic bp->ovfl = 0; 30946365Sbostic bp->addr = 0; 31046365Sbostic bp->flags = 0; 31146365Sbostic BUF_REMOVE ( bp ); 31246365Sbostic LRU_INSERT ( bp ); 31346365Sbostic } 314