#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"
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) |
BufferDesc * | LocalBufferAlloc (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 |
BufferDesc * | LocalBufferDescriptors = NULL |
Block * | LocalBufferBlockPointers = NULL |
int32 * | LocalRefCount = NULL |
static int | nextFreeLocalBuf = 0 |
static HTAB * | LocalBufHash = NULL |
#define LocalBufHdrGetBlock | ( | bufHdr | ) | LocalBufferBlockPointers[-((bufHdr)->buf_id + 2)] |
Definition at line 38 of file localbuf.c.
Referenced by LocalBufferAlloc().
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; }
Block* LocalBufferBlockPointers = NULL |
Definition at line 44 of file localbuf.c.
Referenced by InitLocalBuffers().
BufferDesc* LocalBufferDescriptors = NULL |
Definition at line 43 of file localbuf.c.
Referenced by BufferGetBlockNumber(), BufferGetTag(), FlushRelationBuffers(), PrintBufferLeakWarning(), and ReleaseAndReadBuffer().
HTAB* LocalBufHash = NULL [static] |
Definition at line 49 of file localbuf.c.
int32* LocalRefCount = NULL |
Definition at line 45 of file localbuf.c.
Referenced by AtEOXact_LocalBuffers(), AtProcExit_LocalBuffers(), ConditionalLockBufferForCleanup(), DropRelFileNodeAllLocalBuffers(), DropRelFileNodeLocalBuffers(), IncrBufferRefCount(), InitLocalBuffers(), LocalBufferAlloc(), LockBufferForCleanup(), MarkLocalBufferDirty(), PrintBufferLeakWarning(), ReleaseAndReadBuffer(), and ReleaseBuffer().
int nextFreeLocalBuf = 0 [static] |
Definition at line 47 of file localbuf.c.
Referenced by InitLocalBuffers(), and LocalBufferAlloc().
int NLocBuffer = 0 |
Definition at line 41 of file localbuf.c.
Referenced by AtEOXact_LocalBuffers(), AtProcExit_LocalBuffers(), check_temp_buffers(), DropRelFileNodeAllLocalBuffers(), DropRelFileNodeLocalBuffers(), FlushRelationBuffers(), GetLocalBufferStorage(), InitLocalBuffers(), and LocalBufferAlloc().