/*-1* SPDX-License-Identifier: BSD-3-Clause2*3* Copyright (c) 1990, 1993, 19944* The Regents of the University of California. All rights reserved.5*6* This code is derived from software contributed to Berkeley by7* Margo Seltzer.8*9* Redistribution and use in source and binary forms, with or without10* modification, are permitted provided that the following conditions11* are met:12* 1. Redistributions of source code must retain the above copyright13* notice, this list of conditions and the following disclaimer.14* 2. Redistributions in binary form must reproduce the above copyright15* notice, this list of conditions and the following disclaimer in the16* documentation and/or other materials provided with the distribution.17* 3. Neither the name of the University nor the names of its contributors18* may be used to endorse or promote products derived from this software19* without specific prior written permission.20*21* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND22* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE23* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE24* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE25* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL26* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS27* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)28* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT29* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY30* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF31* SUCH DAMAGE.32*/3334/*35* PACKAGE: hash36*37* DESCRIPTION:38* Contains buffer management39*40* ROUTINES:41* External42* __buf_init43* __get_buf44* __buf_free45* __reclaim_buf46* Internal47* newbuf48*/4950#include <sys/param.h>5152#include <stddef.h>53#include <stdio.h>54#include <stdlib.h>55#include <string.h>5657#ifdef DEBUG58#include <assert.h>59#endif6061#include <db.h>62#include "hash.h"63#include "page.h"64#include "extern.h"6566static BUFHEAD *newbuf(HTAB *, u_int32_t, BUFHEAD *);6768/* Unlink B from its place in the lru */69#define BUF_REMOVE(B) { \70(B)->prev->next = (B)->next; \71(B)->next->prev = (B)->prev; \72}7374/* Insert B after P */75#define BUF_INSERT(B, P) { \76(B)->next = (P)->next; \77(B)->prev = (P); \78(P)->next = (B); \79(B)->next->prev = (B); \80}8182#define MRU hashp->bufhead.next83#define LRU hashp->bufhead.prev8485#define MRU_INSERT(B) BUF_INSERT((B), &hashp->bufhead)86#define LRU_INSERT(B) BUF_INSERT((B), LRU)8788/*89* We are looking for a buffer with address "addr". If prev_bp is NULL, then90* address is a bucket index. If prev_bp is not NULL, then it points to the91* page previous to an overflow page that we are trying to find.92*93* CAVEAT: The buffer header accessed via prev_bp's ovfl field may no longer94* be valid. Therefore, you must always verify that its address matches the95* address you are seeking.96*/97BUFHEAD *98__get_buf(HTAB *hashp, u_int32_t addr,99BUFHEAD *prev_bp, /* If prev_bp set, indicates a new overflow page. */100int newpage)101{102BUFHEAD *bp;103u_int32_t is_disk_mask;104int is_disk, segment_ndx;105SEGMENT segp;106107is_disk = 0;108is_disk_mask = 0;109if (prev_bp) {110bp = prev_bp->ovfl;111if (!bp || (bp->addr != addr))112bp = NULL;113if (!newpage)114is_disk = BUF_DISK;115} else {116/* Grab buffer out of directory */117segment_ndx = addr & (hashp->SGSIZE - 1);118119/* valid segment ensured by __call_hash() */120segp = hashp->dir[addr >> hashp->SSHIFT];121#ifdef DEBUG122assert(segp != NULL);123#endif124bp = PTROF(segp[segment_ndx]);125is_disk_mask = ISDISK(segp[segment_ndx]);126is_disk = is_disk_mask || !hashp->new_file;127}128129if (!bp) {130bp = newbuf(hashp, addr, prev_bp);131if (!bp ||132__get_page(hashp, bp->page, addr, !prev_bp, is_disk, 0))133return (NULL);134if (!prev_bp)135segp[segment_ndx] =136(BUFHEAD *)((intptr_t)bp | is_disk_mask);137} else {138BUF_REMOVE(bp);139MRU_INSERT(bp);140}141return (bp);142}143144/*145* We need a buffer for this page. Either allocate one, or evict a resident146* one (if we have as many buffers as we're allowed) and put this one in.147*148* If newbuf finds an error (returning NULL), it also sets errno.149*/150static BUFHEAD *151newbuf(HTAB *hashp, u_int32_t addr, BUFHEAD *prev_bp)152{153BUFHEAD *bp; /* The buffer we're going to use */154BUFHEAD *xbp; /* Temp pointer */155BUFHEAD *next_xbp;156SEGMENT segp;157int segment_ndx;158u_int16_t oaddr, *shortp;159160oaddr = 0;161bp = LRU;162163/* It is bad to overwrite the page under the cursor. */164if (bp == hashp->cpage) {165BUF_REMOVE(bp);166MRU_INSERT(bp);167bp = LRU;168}169170/* If prev_bp is part of bp overflow, create a new buffer. */171if (hashp->nbufs == 0 && prev_bp && bp->ovfl) {172BUFHEAD *ovfl;173174for (ovfl = bp->ovfl; ovfl ; ovfl = ovfl->ovfl) {175if (ovfl == prev_bp) {176hashp->nbufs++;177break;178}179}180}181182/*183* If LRU buffer is pinned, the buffer pool is too small. We need to184* allocate more buffers.185*/186if (hashp->nbufs || (bp->flags & BUF_PIN) || bp == hashp->cpage) {187/* Allocate a new one */188if ((bp = (BUFHEAD *)calloc(1, sizeof(BUFHEAD))) == NULL)189return (NULL);190if ((bp->page = (char *)calloc(1, hashp->BSIZE)) == NULL) {191free(bp);192return (NULL);193}194if (hashp->nbufs)195hashp->nbufs--;196} else {197/* Kick someone out */198BUF_REMOVE(bp);199/*200* If this is an overflow page with addr 0, it's already been201* flushed back in an overflow chain and initialized.202*/203if ((bp->addr != 0) || (bp->flags & BUF_BUCKET)) {204/*205* Set oaddr before __put_page so that you get it206* before bytes are swapped.207*/208shortp = (u_int16_t *)bp->page;209if (shortp[0])210oaddr = shortp[shortp[0] - 1];211if ((bp->flags & BUF_MOD) && __put_page(hashp, bp->page,212bp->addr, (int)IS_BUCKET(bp->flags), 0))213return (NULL);214/*215* Update the pointer to this page (i.e. invalidate it).216*217* If this is a new file (i.e. we created it at open218* time), make sure that we mark pages which have been219* written to disk so we retrieve them from disk later,220* rather than allocating new pages.221*/222if (IS_BUCKET(bp->flags)) {223segment_ndx = bp->addr & (hashp->SGSIZE - 1);224segp = hashp->dir[bp->addr >> hashp->SSHIFT];225#ifdef DEBUG226assert(segp != NULL);227#endif228229if (hashp->new_file &&230((bp->flags & BUF_MOD) ||231ISDISK(segp[segment_ndx])))232segp[segment_ndx] = (BUFHEAD *)BUF_DISK;233else234segp[segment_ndx] = NULL;235}236/*237* Since overflow pages can only be access by means of238* their bucket, free overflow pages associated with239* this bucket.240*/241for (xbp = bp; xbp->ovfl;) {242next_xbp = xbp->ovfl;243xbp->ovfl = NULL;244xbp = next_xbp;245246/* Check that ovfl pointer is up date. */247if (IS_BUCKET(xbp->flags) ||248(oaddr != xbp->addr))249break;250251shortp = (u_int16_t *)xbp->page;252if (shortp[0])253/* set before __put_page */254oaddr = shortp[shortp[0] - 1];255if ((xbp->flags & BUF_MOD) && __put_page(hashp,256xbp->page, xbp->addr, 0, 0))257return (NULL);258xbp->addr = 0;259xbp->flags = 0;260BUF_REMOVE(xbp);261LRU_INSERT(xbp);262}263}264}265266/* Now assign this buffer */267bp->addr = addr;268#ifdef DEBUG1269(void)fprintf(stderr, "NEWBUF1: %d->ovfl was %d is now %d\n",270bp->addr, (bp->ovfl ? bp->ovfl->addr : 0), 0);271#endif272bp->ovfl = NULL;273if (prev_bp) {274/*275* If prev_bp is set, this is an overflow page, hook it in to276* the buffer overflow links.277*/278#ifdef DEBUG1279(void)fprintf(stderr, "NEWBUF2: %d->ovfl was %d is now %d\n",280prev_bp->addr, (prev_bp->ovfl ? prev_bp->ovfl->addr : 0),281(bp ? bp->addr : 0));282#endif283prev_bp->ovfl = bp;284bp->flags = 0;285} else286bp->flags = BUF_BUCKET;287MRU_INSERT(bp);288return (bp);289}290291void292__buf_init(HTAB *hashp, int nbytes)293{294BUFHEAD *bfp;295int npages;296297bfp = &(hashp->bufhead);298npages = (nbytes + hashp->BSIZE - 1) >> hashp->BSHIFT;299npages = MAX(npages, MIN_BUFFERS);300301hashp->nbufs = npages;302bfp->next = bfp;303bfp->prev = bfp;304/*305* This space is calloc'd so these are already null.306*307* bfp->ovfl = NULL;308* bfp->flags = 0;309* bfp->page = NULL;310* bfp->addr = 0;311*/312}313314int315__buf_free(HTAB *hashp, int do_free, int to_disk)316{317BUFHEAD *bp;318319/* Need to make sure that buffer manager has been initialized */320if (!LRU)321return (0);322for (bp = LRU; bp != &hashp->bufhead;) {323/* Check that the buffer is valid */324if (bp->addr || IS_BUCKET(bp->flags)) {325if (to_disk && (bp->flags & BUF_MOD) &&326__put_page(hashp, bp->page,327bp->addr, IS_BUCKET(bp->flags), 0))328return (-1);329}330/* Check if we are freeing stuff */331if (do_free) {332if (bp->page) {333(void)memset(bp->page, 0, hashp->BSIZE);334free(bp->page);335}336BUF_REMOVE(bp);337free(bp);338bp = LRU;339} else340bp = bp->prev;341}342return (0);343}344345void346__reclaim_buf(HTAB *hashp, BUFHEAD *bp)347{348bp->ovfl = NULL;349bp->addr = 0;350bp->flags = 0;351BUF_REMOVE(bp);352LRU_INSERT(bp);353}354355356