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*46562Sbostic static char sccsid[] = "@(#)hash_buf.c 5.5 (Berkeley) 02/22/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 ******************************************************************************/ 32*46562Sbostic #include <sys/types.h> 3346365Sbostic #include <assert.h> 3446365Sbostic #include <errno.h> 35*46562Sbostic #include <stdio.h> 36*46562Sbostic #include <stdlib.h> 3746365Sbostic #include "hash.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; 8746502Sbostic 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]); 10746502Sbostic is_disk_mask = ISDISK(segp[segment_ndx]); 10846502Sbostic is_disk = is_disk_mask || !hashp->new_file; 10946365Sbostic } 11046365Sbostic 11146365Sbostic if ( !bp ) { 11246365Sbostic bp = newbuf ( addr, prev_bp ); 11346502Sbostic if ( !bp || __get_page ( bp->page, addr, !prev_bp, is_disk, 0 )) { 11446365Sbostic return(NULL); 11546365Sbostic } 11646365Sbostic if ( !prev_bp ) { 11746502Sbostic 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 14646506Sbostic oaddr = 0; 14746456Sbostic bp = LRU; 14846456Sbostic /* 14946456Sbostic If LRU buffer is pinned, the buffer pool is too small. 15046456Sbostic We need to allocate more buffers 15146456Sbostic */ 15246456Sbostic if ( hashp->nbufs || (bp->flags & BUF_PIN) ) { 15346365Sbostic /* Allocate a new one */ 15446365Sbostic bp = (BUFHEAD *)malloc ( sizeof (struct _bufhead) ); 15546365Sbostic if ( !bp || !(bp->page = (char *)malloc ( hashp->BSIZE )) ) { 15646365Sbostic return (NULL); 15746365Sbostic } 15846365Sbostic hashp->nbufs--; 15946365Sbostic } else { 16046365Sbostic /* Kick someone out */ 16146365Sbostic BUF_REMOVE( bp ); 16246365Sbostic /* 16346456Sbostic If this is an overflow page with addr 0, it's already 16446456Sbostic been flushed back in an overflow chain and initialized 16546365Sbostic */ 16646456Sbostic if ( (bp->addr != 0) || (bp->flags & BUF_BUCKET) ) { 16746456Sbostic /* 16846456Sbostic Set oaddr before __put_page so that you get it 16946456Sbostic before bytes are swapped 17046456Sbostic */ 17146456Sbostic shortp = (u_short *)bp->page; 17246506Sbostic if ( shortp[0] ) { 17346506Sbostic oaddr = shortp[shortp[0]-1]; 17446506Sbostic } 17546456Sbostic if ( (bp->flags & BUF_MOD) && 17646456Sbostic __put_page(bp->page, bp->addr, (int)IS_BUCKET(bp->flags), 0) ) { 17746456Sbostic return(NULL); 17846456Sbostic } 17946456Sbostic /* 18046456Sbostic Update the pointer to this page (i.e. invalidate it). 18146365Sbostic 18246456Sbostic If this is a new file (i.e. we created it at open time), 18346456Sbostic make sure that we mark pages which have been written to 18446456Sbostic disk so we retrieve them from disk later, rather than 18546456Sbostic allocating new pages. 18646456Sbostic */ 18746365Sbostic 18846456Sbostic if ( IS_BUCKET(bp->flags)) { 18946456Sbostic segment_ndx = bp->addr & ( hashp->SGSIZE - 1 ); 19046365Sbostic 19146456Sbostic segp = hashp->dir[bp->addr >> hashp->SSHIFT]; 19246365Sbostic 19346456Sbostic assert(segp != NULL); 19446365Sbostic 19546456Sbostic if ( hashp->new_file && 19646456Sbostic ((bp->flags & BUF_MOD) || ISDISK(segp[segment_ndx])) ) { 19746456Sbostic segp[segment_ndx] = (BUFHEAD *)BUF_DISK; 19846456Sbostic } else segp[segment_ndx] = NULL; 19946456Sbostic } 20046365Sbostic 20146456Sbostic /* 20246456Sbostic Since overflow pages can only be access by means of 20346456Sbostic their bucket, free overflow pages associated with this 20446456Sbostic bucket. 20546456Sbostic */ 20646456Sbostic for ( xbp = bp; xbp->ovfl; ) { 20746365Sbostic 20846456Sbostic next_xbp = xbp->ovfl; 20946456Sbostic xbp->ovfl = 0; 21046456Sbostic xbp = next_xbp; 21146365Sbostic 21246456Sbostic /* Check that ovfl pointer is up date */ 21346456Sbostic if ( IS_BUCKET(xbp->flags) || (oaddr != xbp->addr) ) break; 21446365Sbostic 21546456Sbostic shortp = (u_short *)xbp->page; 21646506Sbostic if ( shortp[0] ) { 21746506Sbostic oaddr = shortp[shortp[0]-1]; /* set before __put_page */ 21846506Sbostic } 21946456Sbostic if ( (xbp->flags & BUF_MOD) && 22046456Sbostic __put_page ( xbp->page, xbp->addr, 0, 0 ) ) { 22146456Sbostic return(NULL); 22246456Sbostic } 22346456Sbostic xbp->addr = 0; 22446456Sbostic xbp->flags = 0; 22546456Sbostic BUF_REMOVE ( xbp ); 22646456Sbostic LRU_INSERT ( xbp ); 22746365Sbostic } 22846365Sbostic } 22946365Sbostic } 23046365Sbostic 23146365Sbostic /* Now assign this buffer */ 23246365Sbostic bp->addr = addr; 23346365Sbostic #ifdef DEBUG1 23446365Sbostic fprintf ( stderr, "NEWBUF1: %d->ovfl was %d is now %d\n", bp->addr, 23546365Sbostic (bp->ovfl?bp->ovfl->addr:0), 0); 23646365Sbostic #endif 23746365Sbostic bp->ovfl = NULL; 23846365Sbostic if ( prev_bp ) { 23946365Sbostic /* 24046365Sbostic If prev_bp is set, this is an overflow page, hook it in to the 24146365Sbostic buffer overflow links 24246365Sbostic */ 24346365Sbostic #ifdef DEBUG1 24446365Sbostic fprintf ( stderr, "NEWBUF2: %d->ovfl was %d is now %d\n", prev_bp->addr, 24546365Sbostic (prev_bp->ovfl?bp->ovfl->addr:0), 24646365Sbostic (bp?bp->addr: 0)); 24746365Sbostic #endif 24846365Sbostic prev_bp->ovfl = bp; 24946365Sbostic bp->flags = 0; 25046365Sbostic } else bp->flags = BUF_BUCKET; 25146365Sbostic MRU_INSERT ( bp ); 25246365Sbostic return ( bp ); 25346365Sbostic } 25446365Sbostic 25546365Sbostic extern void 25646365Sbostic __buf_init ( nbytes ) 25746365Sbostic int nbytes; 25846365Sbostic { 25946365Sbostic int npages; 26046365Sbostic BUFHEAD *bfp = &(hashp->bufhead); 26146365Sbostic 26246365Sbostic npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT; 26346365Sbostic npages = MAX ( npages, MIN_BUFFERS ); 26446365Sbostic 26546365Sbostic hashp->nbufs = npages; 26646365Sbostic bfp->next = bfp; 26746365Sbostic bfp->prev = bfp; 26846365Sbostic /* 26946365Sbostic This space is calloc'd so these are already null 27046365Sbostic 27146365Sbostic bfp->ovfl = NULL; 27246365Sbostic bfp->flags = 0; 27346365Sbostic bfp->page = NULL; 27446365Sbostic bfp->addr = 0; 27546365Sbostic */ 27646365Sbostic } 27746365Sbostic 27846365Sbostic extern int 27946365Sbostic __buf_free ( do_free, to_disk ) 28046365Sbostic int do_free; 28146365Sbostic int to_disk; 28246365Sbostic { 28346365Sbostic BUFHEAD *bp; 28446365Sbostic 28546365Sbostic /* Need to make sure that buffer manager has been initialized */ 28646365Sbostic if ( !LRU ) { 28746365Sbostic return(0); 28846365Sbostic } 28946365Sbostic 29046365Sbostic for ( bp = LRU; bp != &hashp->bufhead; ) { 29146365Sbostic /* Check that the buffer is valid */ 29246365Sbostic if ( bp->addr || IS_BUCKET(bp->flags) ) { 29346365Sbostic if ( to_disk && (bp->flags & BUF_MOD) && 29446365Sbostic __put_page (bp->page, bp->addr, IS_BUCKET(bp->flags), 0 )) { 29546365Sbostic return (-1); 29646365Sbostic } 29746365Sbostic } 29846365Sbostic 29946365Sbostic /* Check if we are freeing stuff */ 30046365Sbostic if ( do_free ) { 30146365Sbostic if ( bp->page ) free ( bp->page ); 30246365Sbostic BUF_REMOVE(bp); 30346365Sbostic (void)free ( bp ); 30446365Sbostic bp = LRU; 30546365Sbostic } else bp = bp->prev; 30646365Sbostic } 30746365Sbostic 30846365Sbostic return(0); 30946365Sbostic } 31046365Sbostic 31146365Sbostic extern void 31246365Sbostic __reclaim_buf ( bp ) 31346365Sbostic BUFHEAD *bp; 31446365Sbostic { 31546365Sbostic bp->ovfl = 0; 31646365Sbostic bp->addr = 0; 31746365Sbostic bp->flags = 0; 31846365Sbostic BUF_REMOVE ( bp ); 31946365Sbostic LRU_INSERT ( bp ); 32046365Sbostic } 321