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*46502Sbostic static char sccsid[] = "@(#)hash_buf.c 5.3 (Berkeley) 02/21/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; 87*46502Sbostic register unsigned is_disk_mask = 0; 8846365Sbostic SEGMENT segp; 8946365Sbostic 9046365Sbostic if ( prev_bp ) { 9146365Sbostic bp = prev_bp->ovfl; 9246365Sbostic if ( !bp || (bp->addr != addr) ) bp = NULL; 9346365Sbostic if ( !newpage ) is_disk = BUF_DISK; 9446365Sbostic } 9546365Sbostic else { 9646365Sbostic /* Grab buffer out of directory */ 9746365Sbostic segment_ndx = addr & ( hashp->SGSIZE - 1 ); 9846365Sbostic 9946365Sbostic /* 10046365Sbostic * valid segment ensured by __call_hash() 10146365Sbostic */ 10246365Sbostic segp = hashp->dir[addr >> hashp->SSHIFT]; 10346365Sbostic #ifdef DEBUG 10446365Sbostic assert(segp != NULL); 10546365Sbostic #endif 10646365Sbostic bp = PTROF(segp[segment_ndx]); 107*46502Sbostic is_disk_mask = ISDISK(segp[segment_ndx]); 108*46502Sbostic is_disk = is_disk_mask || !hashp->new_file; 10946365Sbostic } 11046365Sbostic 11146365Sbostic if ( !bp ) { 11246365Sbostic bp = newbuf ( addr, prev_bp ); 113*46502Sbostic if ( !bp || __get_page ( bp->page, addr, !prev_bp, is_disk, 0 )) { 11446365Sbostic return(NULL); 11546365Sbostic } 11646365Sbostic if ( !prev_bp ) { 117*46502Sbostic segp[segment_ndx] = (BUFHEAD *)((unsigned)bp | is_disk_mask ); 11846365Sbostic } 11946365Sbostic } else { 12046365Sbostic BUF_REMOVE ( bp ); 12146365Sbostic MRU_INSERT ( bp ); 12246365Sbostic } 12346365Sbostic return(bp); 12446365Sbostic } 12546365Sbostic 12646365Sbostic /* 12746365Sbostic We need a buffer for this page. Either allocate one, or 12846365Sbostic evict a resident one (if we have as many buffers as we're 12946365Sbostic allowed) and put this one in. 13046365Sbostic 13146365Sbostic If newbuf finds an error (returning NULL), it also sets errno 13246365Sbostic */ 13346365Sbostic static BUFHEAD * 13446365Sbostic newbuf ( addr, prev_bp ) 13546365Sbostic int addr; 13646365Sbostic BUFHEAD *prev_bp; 13746365Sbostic { 13846365Sbostic register BUFHEAD *bp; /* The buffer we're going to use */ 13946365Sbostic register BUFHEAD *xbp; /* Temp pointer */ 14046365Sbostic register BUFHEAD *next_xbp; 14146365Sbostic int segment_ndx; 14246365Sbostic u_short *shortp; 14346365Sbostic u_short oaddr; 14446365Sbostic SEGMENT segp; 14546365Sbostic 14646456Sbostic bp = LRU; 14746456Sbostic /* 14846456Sbostic If LRU buffer is pinned, the buffer pool is too small. 14946456Sbostic We need to allocate more buffers 15046456Sbostic */ 15146456Sbostic if ( hashp->nbufs || (bp->flags & BUF_PIN) ) { 15246365Sbostic /* Allocate a new one */ 15346365Sbostic bp = (BUFHEAD *)malloc ( sizeof (struct _bufhead) ); 15446365Sbostic if ( !bp || !(bp->page = (char *)malloc ( hashp->BSIZE )) ) { 15546365Sbostic return (NULL); 15646365Sbostic } 15746365Sbostic hashp->nbufs--; 15846365Sbostic } else { 15946365Sbostic /* Kick someone out */ 16046365Sbostic BUF_REMOVE( bp ); 16146365Sbostic /* 16246456Sbostic If this is an overflow page with addr 0, it's already 16346456Sbostic been flushed back in an overflow chain and initialized 16446365Sbostic */ 16546456Sbostic if ( (bp->addr != 0) || (bp->flags & BUF_BUCKET) ) { 16646456Sbostic /* 16746456Sbostic Set oaddr before __put_page so that you get it 16846456Sbostic before bytes are swapped 16946456Sbostic */ 17046456Sbostic shortp = (u_short *)bp->page; 17146456Sbostic oaddr = shortp[shortp[0]-1]; 17246456Sbostic if ( (bp->flags & BUF_MOD) && 17346456Sbostic __put_page(bp->page, bp->addr, (int)IS_BUCKET(bp->flags), 0) ) { 17446456Sbostic return(NULL); 17546456Sbostic } 17646456Sbostic /* 17746456Sbostic Update the pointer to this page (i.e. invalidate it). 17846365Sbostic 17946456Sbostic If this is a new file (i.e. we created it at open time), 18046456Sbostic make sure that we mark pages which have been written to 18146456Sbostic disk so we retrieve them from disk later, rather than 18246456Sbostic allocating new pages. 18346456Sbostic */ 18446365Sbostic 18546456Sbostic if ( IS_BUCKET(bp->flags)) { 18646456Sbostic segment_ndx = bp->addr & ( hashp->SGSIZE - 1 ); 18746365Sbostic 18846456Sbostic segp = hashp->dir[bp->addr >> hashp->SSHIFT]; 18946365Sbostic 19046456Sbostic assert(segp != NULL); 19146365Sbostic 19246456Sbostic if ( hashp->new_file && 19346456Sbostic ((bp->flags & BUF_MOD) || ISDISK(segp[segment_ndx])) ) { 19446456Sbostic segp[segment_ndx] = (BUFHEAD *)BUF_DISK; 19546456Sbostic } else segp[segment_ndx] = NULL; 19646456Sbostic } 19746365Sbostic 19846456Sbostic /* 19946456Sbostic Since overflow pages can only be access by means of 20046456Sbostic their bucket, free overflow pages associated with this 20146456Sbostic bucket. 20246456Sbostic */ 20346456Sbostic for ( xbp = bp; xbp->ovfl; ) { 20446365Sbostic 20546456Sbostic next_xbp = xbp->ovfl; 20646456Sbostic xbp->ovfl = 0; 20746456Sbostic xbp = next_xbp; 20846365Sbostic 20946456Sbostic /* Check that ovfl pointer is up date */ 21046456Sbostic if ( IS_BUCKET(xbp->flags) || (oaddr != xbp->addr) ) break; 21146365Sbostic 21246456Sbostic shortp = (u_short *)xbp->page; 21346456Sbostic oaddr = shortp[shortp[0]-1]; /* set before __put_page */ 21446456Sbostic if ( (xbp->flags & BUF_MOD) && 21546456Sbostic __put_page ( xbp->page, xbp->addr, 0, 0 ) ) { 21646456Sbostic return(NULL); 21746456Sbostic } 21846456Sbostic xbp->addr = 0; 21946456Sbostic xbp->flags = 0; 22046456Sbostic BUF_REMOVE ( xbp ); 22146456Sbostic LRU_INSERT ( xbp ); 22246365Sbostic } 22346365Sbostic } 22446365Sbostic } 22546365Sbostic 22646365Sbostic /* Now assign this buffer */ 22746365Sbostic bp->addr = addr; 22846365Sbostic #ifdef DEBUG1 22946365Sbostic fprintf ( stderr, "NEWBUF1: %d->ovfl was %d is now %d\n", bp->addr, 23046365Sbostic (bp->ovfl?bp->ovfl->addr:0), 0); 23146365Sbostic #endif 23246365Sbostic bp->ovfl = NULL; 23346365Sbostic if ( prev_bp ) { 23446365Sbostic /* 23546365Sbostic If prev_bp is set, this is an overflow page, hook it in to the 23646365Sbostic buffer overflow links 23746365Sbostic */ 23846365Sbostic #ifdef DEBUG1 23946365Sbostic fprintf ( stderr, "NEWBUF2: %d->ovfl was %d is now %d\n", prev_bp->addr, 24046365Sbostic (prev_bp->ovfl?bp->ovfl->addr:0), 24146365Sbostic (bp?bp->addr: 0)); 24246365Sbostic #endif 24346365Sbostic prev_bp->ovfl = bp; 24446365Sbostic bp->flags = 0; 24546365Sbostic } else bp->flags = BUF_BUCKET; 24646365Sbostic MRU_INSERT ( bp ); 24746365Sbostic return ( bp ); 24846365Sbostic } 24946365Sbostic 25046365Sbostic extern void 25146365Sbostic __buf_init ( nbytes ) 25246365Sbostic int nbytes; 25346365Sbostic { 25446365Sbostic int npages; 25546365Sbostic BUFHEAD *bfp = &(hashp->bufhead); 25646365Sbostic 25746365Sbostic npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT; 25846365Sbostic npages = MAX ( npages, MIN_BUFFERS ); 25946365Sbostic 26046365Sbostic hashp->nbufs = npages; 26146365Sbostic bfp->next = bfp; 26246365Sbostic bfp->prev = bfp; 26346365Sbostic /* 26446365Sbostic This space is calloc'd so these are already null 26546365Sbostic 26646365Sbostic bfp->ovfl = NULL; 26746365Sbostic bfp->flags = 0; 26846365Sbostic bfp->page = NULL; 26946365Sbostic bfp->addr = 0; 27046365Sbostic */ 27146365Sbostic } 27246365Sbostic 27346365Sbostic extern int 27446365Sbostic __buf_free ( do_free, to_disk ) 27546365Sbostic int do_free; 27646365Sbostic int to_disk; 27746365Sbostic { 27846365Sbostic BUFHEAD *bp; 27946365Sbostic 28046365Sbostic /* Need to make sure that buffer manager has been initialized */ 28146365Sbostic if ( !LRU ) { 28246365Sbostic return(0); 28346365Sbostic } 28446365Sbostic 28546365Sbostic for ( bp = LRU; bp != &hashp->bufhead; ) { 28646365Sbostic /* Check that the buffer is valid */ 28746365Sbostic if ( bp->addr || IS_BUCKET(bp->flags) ) { 28846365Sbostic if ( to_disk && (bp->flags & BUF_MOD) && 28946365Sbostic __put_page (bp->page, bp->addr, IS_BUCKET(bp->flags), 0 )) { 29046365Sbostic return (-1); 29146365Sbostic } 29246365Sbostic } 29346365Sbostic 29446365Sbostic /* Check if we are freeing stuff */ 29546365Sbostic if ( do_free ) { 29646365Sbostic if ( bp->page ) free ( bp->page ); 29746365Sbostic BUF_REMOVE(bp); 29846365Sbostic (void)free ( bp ); 29946365Sbostic bp = LRU; 30046365Sbostic } else bp = bp->prev; 30146365Sbostic } 30246365Sbostic 30346365Sbostic return(0); 30446365Sbostic } 30546365Sbostic 30646365Sbostic extern void 30746365Sbostic __reclaim_buf ( bp ) 30846365Sbostic BUFHEAD *bp; 30946365Sbostic { 31046365Sbostic bp->ovfl = 0; 31146365Sbostic bp->addr = 0; 31246365Sbostic bp->flags = 0; 31346365Sbostic BUF_REMOVE ( bp ); 31446365Sbostic LRU_INSERT ( bp ); 31546365Sbostic } 316