Header And Logo

PostgreSQL
| The world's most advanced open source database.

Data Structures | Defines | Functions | Variables

localbuf.c File Reference

#include "postgres.h"
#include "catalog/catalog.h"
#include "common/relpath.h"
#include "executor/instrument.h"
#include "storage/buf_internals.h"
#include "storage/bufmgr.h"
#include "utils/guc.h"
#include "utils/memutils.h"
#include "utils/resowner_private.h"
Include dependency graph for localbuf.c:

Go to the source code of this file.

Data Structures

struct  LocalBufferLookupEnt

Defines

#define LocalBufHdrGetBlock(bufHdr)   LocalBufferBlockPointers[-((bufHdr)->buf_id + 2)]

Functions

static void InitLocalBuffers (void)
static Block GetLocalBufferStorage (void)
void LocalPrefetchBuffer (SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum)
BufferDescLocalBufferAlloc (SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, bool *foundPtr)
void MarkLocalBufferDirty (Buffer buffer)
void DropRelFileNodeLocalBuffers (RelFileNode rnode, ForkNumber forkNum, BlockNumber firstDelBlock)
void DropRelFileNodeAllLocalBuffers (RelFileNode rnode)
void AtEOXact_LocalBuffers (bool isCommit)
void AtProcExit_LocalBuffers (void)

Variables

int NLocBuffer = 0
BufferDescLocalBufferDescriptors = NULL
BlockLocalBufferBlockPointers = NULL
int32LocalRefCount = NULL
static int nextFreeLocalBuf = 0
static HTABLocalBufHash = NULL

Define Documentation

#define LocalBufHdrGetBlock (   bufHdr  )     LocalBufferBlockPointers[-((bufHdr)->buf_id + 2)]

Definition at line 38 of file localbuf.c.

Referenced by LocalBufferAlloc().


Function Documentation

void AtEOXact_LocalBuffers ( bool  isCommit  ) 

Definition at line 500 of file localbuf.c.

References Assert, assert_enabled, i, LocalRefCount, NLocBuffer, and PrintBufferLeakWarning().

Referenced by AtEOXact_Buffers().

{
#ifdef USE_ASSERT_CHECKING
    if (assert_enabled && LocalRefCount)
    {
        int         RefCountErrors = 0;
        int         i;

        for (i = 0; i < NLocBuffer; i++)
        {
            if (LocalRefCount[i] != 0)
            {
                Buffer  b = -i - 1;

                PrintBufferLeakWarning(b);
                RefCountErrors++;
            }
        }
        Assert(RefCountErrors == 0);
    }
#endif
}

void AtProcExit_LocalBuffers ( void   ) 

Definition at line 532 of file localbuf.c.

References Assert, assert_enabled, i, LocalRefCount, NLocBuffer, and PrintBufferLeakWarning().

Referenced by AtProcExit_Buffers().

{
#ifdef USE_ASSERT_CHECKING
    if (assert_enabled && LocalRefCount)
    {
        int         RefCountErrors = 0;
        int         i;

        for (i = 0; i < NLocBuffer; i++)
        {
            if (LocalRefCount[i] != 0)
            {
                Buffer  b = -i - 1;

                PrintBufferLeakWarning(b);
                RefCountErrors++;
            }
        }
        Assert(RefCountErrors == 0);
    }
#endif
}

void DropRelFileNodeAllLocalBuffers ( RelFileNode  rnode  ) 

Definition at line 345 of file localbuf.c.

References buftag::blockNum, BM_TAG_VALID, CLEAR_BUFFERTAG, elog, ERROR, sbufdesc::flags, buftag::forkNum, hash_search(), i, LocalRefCount, MyBackendId, NLocBuffer, RelFileNodeEquals, relpathbackend(), buftag::rnode, sbufdesc::tag, and sbufdesc::usage_count.

Referenced by DropRelFileNodesAllBuffers().

{
    int         i;

    for (i = 0; i < NLocBuffer; i++)
    {
        BufferDesc *bufHdr = &LocalBufferDescriptors[i];
        LocalBufferLookupEnt *hresult;

        if ((bufHdr->flags & BM_TAG_VALID) &&
            RelFileNodeEquals(bufHdr->tag.rnode, rnode))
        {
            if (LocalRefCount[i] != 0)
                elog(ERROR, "block %u of %s is still referenced (local %u)",
                     bufHdr->tag.blockNum,
                     relpathbackend(bufHdr->tag.rnode, MyBackendId,
                                    bufHdr->tag.forkNum),
                     LocalRefCount[i]);
            /* Remove entry from hashtable */
            hresult = (LocalBufferLookupEnt *)
                hash_search(LocalBufHash, (void *) &bufHdr->tag,
                            HASH_REMOVE, NULL);
            if (!hresult)       /* shouldn't happen */
                elog(ERROR, "local buffer hash table corrupted");
            /* Mark buffer invalid */
            CLEAR_BUFFERTAG(bufHdr->tag);
            bufHdr->flags = 0;
            bufHdr->usage_count = 0;
        }
    }
}

void DropRelFileNodeLocalBuffers ( RelFileNode  rnode,
ForkNumber  forkNum,
BlockNumber  firstDelBlock 
)

Definition at line 302 of file localbuf.c.

References buftag::blockNum, BM_TAG_VALID, CLEAR_BUFFERTAG, elog, ERROR, sbufdesc::flags, buftag::forkNum, hash_search(), i, LocalRefCount, MyBackendId, NLocBuffer, RelFileNodeEquals, relpathbackend(), buftag::rnode, sbufdesc::tag, and sbufdesc::usage_count.

Referenced by DropRelFileNodeBuffers().

{
    int         i;

    for (i = 0; i < NLocBuffer; i++)
    {
        BufferDesc *bufHdr = &LocalBufferDescriptors[i];
        LocalBufferLookupEnt *hresult;

        if ((bufHdr->flags & BM_TAG_VALID) &&
            RelFileNodeEquals(bufHdr->tag.rnode, rnode) &&
            bufHdr->tag.forkNum == forkNum &&
            bufHdr->tag.blockNum >= firstDelBlock)
        {
            if (LocalRefCount[i] != 0)
                elog(ERROR, "block %u of %s is still referenced (local %u)",
                     bufHdr->tag.blockNum,
                     relpathbackend(bufHdr->tag.rnode, MyBackendId,
                                    bufHdr->tag.forkNum),
                     LocalRefCount[i]);
            /* Remove entry from hashtable */
            hresult = (LocalBufferLookupEnt *)
                hash_search(LocalBufHash, (void *) &bufHdr->tag,
                            HASH_REMOVE, NULL);
            if (!hresult)       /* shouldn't happen */
                elog(ERROR, "local buffer hash table corrupted");
            /* Mark buffer invalid */
            CLEAR_BUFFERTAG(bufHdr->tag);
            bufHdr->flags = 0;
            bufHdr->usage_count = 0;
        }
    }
}

static Block GetLocalBufferStorage ( void   )  [static]

Definition at line 443 of file localbuf.c.

References ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), Assert, Max, MaxAllocSize, MemoryContextAlloc(), Min, NLocBuffer, NULL, and TopMemoryContext.

Referenced by LocalBufferAlloc().

{
    static char *cur_block = NULL;
    static int  next_buf_in_block = 0;
    static int  num_bufs_in_block = 0;
    static int  total_bufs_allocated = 0;
    static MemoryContext LocalBufferContext = NULL;

    char       *this_buf;

    Assert(total_bufs_allocated < NLocBuffer);

    if (next_buf_in_block >= num_bufs_in_block)
    {
        /* Need to make a new request to memmgr */
        int         num_bufs;

        /*
         * We allocate local buffers in a context of their own, so that the
         * space eaten for them is easily recognizable in MemoryContextStats
         * output.  Create the context on first use.
         */
        if (LocalBufferContext == NULL)
            LocalBufferContext =
                AllocSetContextCreate(TopMemoryContext,
                                      "LocalBufferContext",
                                      ALLOCSET_DEFAULT_MINSIZE,
                                      ALLOCSET_DEFAULT_INITSIZE,
                                      ALLOCSET_DEFAULT_MAXSIZE);

        /* Start with a 16-buffer request; subsequent ones double each time */
        num_bufs = Max(num_bufs_in_block * 2, 16);
        /* But not more than what we need for all remaining local bufs */
        num_bufs = Min(num_bufs, NLocBuffer - total_bufs_allocated);
        /* And don't overflow MaxAllocSize, either */
        num_bufs = Min(num_bufs, MaxAllocSize / BLCKSZ);

        cur_block = (char *) MemoryContextAlloc(LocalBufferContext,
                                                num_bufs * BLCKSZ);
        next_buf_in_block = 0;
        num_bufs_in_block = num_bufs;
    }

    /* Allocate next buffer in current memory block */
    this_buf = cur_block + next_buf_in_block * BLCKSZ;
    next_buf_in_block++;
    total_bufs_allocated++;

    return (Block) this_buf;
}

static void InitLocalBuffers ( void   )  [static]

Definition at line 384 of file localbuf.c.

References buf, sbufdesc::buf_id, calloc, elog, HASHCTL::entrysize, ereport, errcode(), errmsg(), ERROR, FATAL, HASHCTL::hash, hash_create(), HASH_ELEM, HASH_FUNCTION, i, HASHCTL::keysize, LocalBufferBlockPointers, LocalRefCount, MemSet, nextFreeLocalBuf, NLocBuffer, and num_temp_buffers.

Referenced by LocalBufferAlloc(), and LocalPrefetchBuffer().

{
    int         nbufs = num_temp_buffers;
    HASHCTL     info;
    int         i;

    /* Allocate and zero buffer headers and auxiliary arrays */
    LocalBufferDescriptors = (BufferDesc *) calloc(nbufs, sizeof(BufferDesc));
    LocalBufferBlockPointers = (Block *) calloc(nbufs, sizeof(Block));
    LocalRefCount = (int32 *) calloc(nbufs, sizeof(int32));
    if (!LocalBufferDescriptors || !LocalBufferBlockPointers || !LocalRefCount)
        ereport(FATAL,
                (errcode(ERRCODE_OUT_OF_MEMORY),
                 errmsg("out of memory")));

    nextFreeLocalBuf = 0;

    /* initialize fields that need to start off nonzero */
    for (i = 0; i < nbufs; i++)
    {
        BufferDesc *buf = &LocalBufferDescriptors[i];

        /*
         * negative to indicate local buffer. This is tricky: shared buffers
         * start with 0. We have to start with -2. (Note that the routine
         * BufferDescriptorGetBuffer adds 1 to buf_id so our first buffer id
         * is -1.)
         */
        buf->buf_id = -i - 2;
    }

    /* Create the lookup hash table */
    MemSet(&info, 0, sizeof(info));
    info.keysize = sizeof(BufferTag);
    info.entrysize = sizeof(LocalBufferLookupEnt);
    info.hash = tag_hash;

    LocalBufHash = hash_create("Local Buffer Lookup Table",
                               nbufs,
                               &info,
                               HASH_ELEM | HASH_FUNCTION);

    if (!LocalBufHash)
        elog(ERROR, "could not initialize local buffer hash table");

    /* Initialization done, mark buffers allocated */
    NLocBuffer = nbufs;
}

BufferDesc* LocalBufferAlloc ( SMgrRelation  smgr,
ForkNumber  forkNum,
BlockNumber  blockNum,
bool foundPtr 
)

Definition at line 103 of file localbuf.c.

References Assert, buftag::blockNum, BM_DIRTY, BM_JUST_DIRTIED, BM_MAX_USAGE_COUNT, BM_TAG_VALID, BM_VALID, BufferDescriptorGetBuffer, BUFFERTAGS_EQUAL, CLEAR_BUFFERTAG, CurrentResourceOwner, elog, ereport, errcode(), errmsg(), ERROR, sbufdesc::flags, buftag::forkNum, GetLocalBufferStorage(), hash_search(), LocalBufferLookupEnt::id, INIT_BUFFERTAG, InitLocalBuffers(), BufferUsage::local_blks_written, LocalBufHdrGetBlock, LocalRefCount, MyBackendId, nextFreeLocalBuf, NLocBuffer, RelFileNodeBackend::node, NULL, PageSetChecksumInplace(), pgBufferUsage, RelFileNode::relNode, ResourceOwnerRememberBuffer(), buftag::rnode, SMgrRelationData::smgr_rnode, smgropen(), smgrwrite(), sbufdesc::tag, and sbufdesc::usage_count.

Referenced by ReadBuffer_common().

{
    BufferTag   newTag;         /* identity of requested block */
    LocalBufferLookupEnt *hresult;
    BufferDesc *bufHdr;
    int         b;
    int         trycounter;
    bool        found;

    INIT_BUFFERTAG(newTag, smgr->smgr_rnode.node, forkNum, blockNum);

    /* Initialize local buffers if first request in this session */
    if (LocalBufHash == NULL)
        InitLocalBuffers();

    /* See if the desired buffer already exists */
    hresult = (LocalBufferLookupEnt *)
        hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL);

    if (hresult)
    {
        b = hresult->id;
        bufHdr = &LocalBufferDescriptors[b];
        Assert(BUFFERTAGS_EQUAL(bufHdr->tag, newTag));
#ifdef LBDEBUG
        fprintf(stderr, "LB ALLOC (%u,%d,%d) %d\n",
                smgr->smgr_rnode.node.relNode, forkNum, blockNum, -b - 1);
#endif
        /* this part is equivalent to PinBuffer for a shared buffer */
        if (LocalRefCount[b] == 0)
        {
            if (bufHdr->usage_count < BM_MAX_USAGE_COUNT)
                bufHdr->usage_count++;
        }
        LocalRefCount[b]++;
        ResourceOwnerRememberBuffer(CurrentResourceOwner,
                                    BufferDescriptorGetBuffer(bufHdr));
        if (bufHdr->flags & BM_VALID)
            *foundPtr = TRUE;
        else
        {
            /* Previous read attempt must have failed; try again */
            *foundPtr = FALSE;
        }
        return bufHdr;
    }

#ifdef LBDEBUG
    fprintf(stderr, "LB ALLOC (%u,%d,%d) %d\n",
            smgr->smgr_rnode.node.relNode, forkNum, blockNum,
            -nextFreeLocalBuf - 1);
#endif

    /*
     * Need to get a new buffer.  We use a clock sweep algorithm (essentially
     * the same as what freelist.c does now...)
     */
    trycounter = NLocBuffer;
    for (;;)
    {
        b = nextFreeLocalBuf;

        if (++nextFreeLocalBuf >= NLocBuffer)
            nextFreeLocalBuf = 0;

        bufHdr = &LocalBufferDescriptors[b];

        if (LocalRefCount[b] == 0)
        {
            if (bufHdr->usage_count > 0)
            {
                bufHdr->usage_count--;
                trycounter = NLocBuffer;
            }
            else
            {
                /* Found a usable buffer */
                LocalRefCount[b]++;
                ResourceOwnerRememberBuffer(CurrentResourceOwner,
                                          BufferDescriptorGetBuffer(bufHdr));
                break;
            }
        }
        else if (--trycounter == 0)
            ereport(ERROR,
                    (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
                     errmsg("no empty local buffer available")));
    }

    /*
     * this buffer is not referenced but it might still be dirty. if that's
     * the case, write it out before reusing it!
     */
    if (bufHdr->flags & BM_DIRTY)
    {
        SMgrRelation    oreln;
        Page            localpage = (char *) LocalBufHdrGetBlock(bufHdr);

        /* Find smgr relation for buffer */
        oreln = smgropen(bufHdr->tag.rnode, MyBackendId);

        PageSetChecksumInplace(localpage, bufHdr->tag.blockNum);

        /* And write... */
        smgrwrite(oreln,
                  bufHdr->tag.forkNum,
                  bufHdr->tag.blockNum,
                  localpage,
                  false);

        /* Mark not-dirty now in case we error out below */
        bufHdr->flags &= ~BM_DIRTY;

        pgBufferUsage.local_blks_written++;
    }

    /*
     * lazy memory allocation: allocate space on first use of a buffer.
     */
    if (LocalBufHdrGetBlock(bufHdr) == NULL)
    {
        /* Set pointer for use by BufferGetBlock() macro */
        LocalBufHdrGetBlock(bufHdr) = GetLocalBufferStorage();
    }

    /*
     * Update the hash table: remove old entry, if any, and make new one.
     */
    if (bufHdr->flags & BM_TAG_VALID)
    {
        hresult = (LocalBufferLookupEnt *)
            hash_search(LocalBufHash, (void *) &bufHdr->tag,
                        HASH_REMOVE, NULL);
        if (!hresult)           /* shouldn't happen */
            elog(ERROR, "local buffer hash table corrupted");
        /* mark buffer invalid just in case hash insert fails */
        CLEAR_BUFFERTAG(bufHdr->tag);
        bufHdr->flags &= ~(BM_VALID | BM_TAG_VALID);
    }

    hresult = (LocalBufferLookupEnt *)
        hash_search(LocalBufHash, (void *) &newTag, HASH_ENTER, &found);
    if (found)                  /* shouldn't happen */
        elog(ERROR, "local buffer hash table corrupted");
    hresult->id = b;

    /*
     * it's all ours now.
     */
    bufHdr->tag = newTag;
    bufHdr->flags &= ~(BM_VALID | BM_DIRTY | BM_JUST_DIRTIED | BM_IO_ERROR);
    bufHdr->flags |= BM_TAG_VALID;
    bufHdr->usage_count = 1;

    *foundPtr = FALSE;
    return bufHdr;
}

void LocalPrefetchBuffer ( SMgrRelation  smgr,
ForkNumber  forkNum,
BlockNumber  blockNum 
)

Definition at line 64 of file localbuf.c.

References hash_search(), INIT_BUFFERTAG, InitLocalBuffers(), RelFileNodeBackend::node, NULL, SMgrRelationData::smgr_rnode, and smgrprefetch().

Referenced by PrefetchBuffer().

{
#ifdef USE_PREFETCH
    BufferTag   newTag;         /* identity of requested block */
    LocalBufferLookupEnt *hresult;

    INIT_BUFFERTAG(newTag, smgr->smgr_rnode.node, forkNum, blockNum);

    /* Initialize local buffers if first request in this session */
    if (LocalBufHash == NULL)
        InitLocalBuffers();

    /* See if the desired buffer already exists */
    hresult = (LocalBufferLookupEnt *)
        hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL);

    if (hresult)
    {
        /* Yes, so nothing to do */
        return;
    }

    /* Not in buffers, so initiate prefetch */
    smgrprefetch(smgr, forkNum, blockNum);
#endif   /* USE_PREFETCH */
}

void MarkLocalBufferDirty ( Buffer  buffer  ) 

Definition at line 267 of file localbuf.c.

References Assert, BM_DIRTY, BufferIsLocal, sbufdesc::flags, BufferUsage::local_blks_dirtied, LocalRefCount, and pgBufferUsage.

Referenced by MarkBufferDirty(), and MarkBufferDirtyHint().

{
    int         bufid;
    BufferDesc *bufHdr;

    Assert(BufferIsLocal(buffer));

#ifdef LBDEBUG
    fprintf(stderr, "LB DIRTY %d\n", buffer);
#endif

    bufid = -(buffer + 1);

    Assert(LocalRefCount[bufid] > 0);

    bufHdr = &LocalBufferDescriptors[bufid];

    if (!(bufHdr->flags & BM_DIRTY))
        pgBufferUsage.local_blks_dirtied++;

    bufHdr->flags |= BM_DIRTY;
}


Variable Documentation

Definition at line 44 of file localbuf.c.

Referenced by InitLocalBuffers().

HTAB* LocalBufHash = NULL [static]

Definition at line 49 of file localbuf.c.

int nextFreeLocalBuf = 0 [static]

Definition at line 47 of file localbuf.c.

Referenced by InitLocalBuffers(), and LocalBufferAlloc().

int NLocBuffer = 0