#include "postgres.h"
#include "storage/buf_internals.h"
#include "storage/bufmgr.h"
Go to the source code of this file.
Data Structures | |
struct | BufferStrategyControl |
struct | BufferAccessStrategyData |
Typedefs | |
typedef struct BufferAccessStrategyData | BufferAccessStrategyData |
Functions | |
static volatile BufferDesc * | GetBufferFromRing (BufferAccessStrategy strategy) |
static void | AddBufferToRing (BufferAccessStrategy strategy, volatile BufferDesc *buf) |
volatile BufferDesc * | StrategyGetBuffer (BufferAccessStrategy strategy, bool *lock_held) |
void | StrategyFreeBuffer (volatile BufferDesc *buf) |
int | StrategySyncStart (uint32 *complete_passes, uint32 *num_buf_alloc) |
void | StrategyNotifyBgWriter (Latch *bgwriterLatch) |
Size | StrategyShmemSize (void) |
void | StrategyInitialize (bool init) |
BufferAccessStrategy | GetAccessStrategy (BufferAccessStrategyType btype) |
void | FreeAccessStrategy (BufferAccessStrategy strategy) |
bool | StrategyRejectBuffer (BufferAccessStrategy strategy, volatile BufferDesc *buf) |
Variables | |
static BufferStrategyControl * | StrategyControl = NULL |
typedef struct BufferAccessStrategyData BufferAccessStrategyData |
static void AddBufferToRing | ( | BufferAccessStrategy | strategy, | |
volatile BufferDesc * | buf | |||
) | [static] |
Definition at line 530 of file freelist.c.
References BufferDescriptorGetBuffer, BufferAccessStrategyData::buffers, and BufferAccessStrategyData::current.
Referenced by StrategyGetBuffer().
{ strategy->buffers[strategy->current] = BufferDescriptorGetBuffer(buf); }
void FreeAccessStrategy | ( | BufferAccessStrategy | strategy | ) |
Definition at line 462 of file freelist.c.
Referenced by FreeBulkInsertState(), heap_endscan(), and initscan().
BufferAccessStrategy GetAccessStrategy | ( | BufferAccessStrategyType | btype | ) |
Definition at line 407 of file freelist.c.
References BAS_BULKREAD, BAS_BULKWRITE, BAS_NORMAL, BAS_VACUUM, BufferAccessStrategyData::btype, elog, ERROR, Min, NBuffers, offsetof, palloc0(), and BufferAccessStrategyData::ring_size.
Referenced by do_autovacuum(), GetBulkInsertState(), initscan(), pgstat_heap(), pgstat_index(), pgstatindex(), and vacuum().
{ BufferAccessStrategy strategy; int ring_size; /* * Select ring size to use. See buffer/README for rationales. * * Note: if you change the ring size for BAS_BULKREAD, see also * SYNC_SCAN_REPORT_INTERVAL in access/heap/syncscan.c. */ switch (btype) { case BAS_NORMAL: /* if someone asks for NORMAL, just give 'em a "default" object */ return NULL; case BAS_BULKREAD: ring_size = 256 * 1024 / BLCKSZ; break; case BAS_BULKWRITE: ring_size = 16 * 1024 * 1024 / BLCKSZ; break; case BAS_VACUUM: ring_size = 256 * 1024 / BLCKSZ; break; default: elog(ERROR, "unrecognized buffer access strategy: %d", (int) btype); return NULL; /* keep compiler quiet */ } /* Make sure ring isn't an undue fraction of shared buffers */ ring_size = Min(NBuffers / 8, ring_size); /* Allocate the object and initialize all elements to zeroes */ strategy = (BufferAccessStrategy) palloc0(offsetof(BufferAccessStrategyData, buffers) + ring_size * sizeof(Buffer)); /* Set fields that don't start out zero */ strategy->btype = btype; strategy->ring_size = ring_size; return strategy; }
static volatile BufferDesc * GetBufferFromRing | ( | BufferAccessStrategy | strategy | ) | [static] |
Definition at line 476 of file freelist.c.
References buf, BufferDescriptors, BufferAccessStrategyData::buffers, BufferAccessStrategyData::current, BufferAccessStrategyData::current_was_in_ring, InvalidBuffer, LockBufHdr, sbufdesc::refcount, BufferAccessStrategyData::ring_size, UnlockBufHdr, and sbufdesc::usage_count.
Referenced by StrategyGetBuffer().
{ volatile BufferDesc *buf; Buffer bufnum; /* Advance to next ring slot */ if (++strategy->current >= strategy->ring_size) strategy->current = 0; /* * If the slot hasn't been filled yet, tell the caller to allocate a new * buffer with the normal allocation strategy. He will then fill this * slot by calling AddBufferToRing with the new buffer. */ bufnum = strategy->buffers[strategy->current]; if (bufnum == InvalidBuffer) { strategy->current_was_in_ring = false; return NULL; } /* * If the buffer is pinned we cannot use it under any circumstances. * * If usage_count is 0 or 1 then the buffer is fair game (we expect 1, * since our own previous usage of the ring element would have left it * there, but it might've been decremented by clock sweep since then). A * higher usage_count indicates someone else has touched the buffer, so we * shouldn't re-use it. */ buf = &BufferDescriptors[bufnum - 1]; LockBufHdr(buf); if (buf->refcount == 0 && buf->usage_count <= 1) { strategy->current_was_in_ring = true; return buf; } UnlockBufHdr(buf); /* * Tell caller to allocate a new buffer with the normal allocation * strategy. He'll then replace this ring element via AddBufferToRing. */ strategy->current_was_in_ring = false; return NULL; }
void StrategyFreeBuffer | ( | volatile BufferDesc * | buf | ) |
Definition at line 242 of file freelist.c.
References sbufdesc::buf_id, BufFreelistLock, BufferStrategyControl::firstFreeBuffer, sbufdesc::freeNext, FREENEXT_NOT_IN_LIST, BufferStrategyControl::lastFreeBuffer, LW_EXCLUSIVE, LWLockAcquire(), and LWLockRelease().
Referenced by InvalidateBuffer().
{ LWLockAcquire(BufFreelistLock, LW_EXCLUSIVE); /* * It is possible that we are told to put something in the freelist that * is already in it; don't screw up the list if so. */ if (buf->freeNext == FREENEXT_NOT_IN_LIST) { buf->freeNext = StrategyControl->firstFreeBuffer; if (buf->freeNext < 0) StrategyControl->lastFreeBuffer = buf->buf_id; StrategyControl->firstFreeBuffer = buf->buf_id; } LWLockRelease(BufFreelistLock); }
volatile BufferDesc* StrategyGetBuffer | ( | BufferAccessStrategy | strategy, | |
bool * | lock_held | |||
) |
Definition at line 112 of file freelist.c.
References AddBufferToRing(), Assert, BufferStrategyControl::bgwriterLatch, buf, BufferDescriptors, BufFreelistLock, BufferStrategyControl::completePasses, elog, ERROR, BufferStrategyControl::firstFreeBuffer, sbufdesc::freeNext, FREENEXT_NOT_IN_LIST, GetBufferFromRing(), LockBufHdr, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), NBuffers, BufferStrategyControl::nextVictimBuffer, NULL, BufferStrategyControl::numBufferAllocs, sbufdesc::refcount, SetLatch(), UnlockBufHdr, and sbufdesc::usage_count.
Referenced by BufferAlloc().
{ volatile BufferDesc *buf; Latch *bgwriterLatch; int trycounter; /* * If given a strategy object, see whether it can select a buffer. We * assume strategy objects don't need the BufFreelistLock. */ if (strategy != NULL) { buf = GetBufferFromRing(strategy); if (buf != NULL) { *lock_held = false; return buf; } } /* Nope, so lock the freelist */ *lock_held = true; LWLockAcquire(BufFreelistLock, LW_EXCLUSIVE); /* * We count buffer allocation requests so that the bgwriter can estimate * the rate of buffer consumption. Note that buffers recycled by a * strategy object are intentionally not counted here. */ StrategyControl->numBufferAllocs++; /* * If bgwriterLatch is set, we need to waken the bgwriter, but we should * not do so while holding BufFreelistLock; so release and re-grab. This * is annoyingly tedious, but it happens at most once per bgwriter cycle, * so the performance hit is minimal. */ bgwriterLatch = StrategyControl->bgwriterLatch; if (bgwriterLatch) { StrategyControl->bgwriterLatch = NULL; LWLockRelease(BufFreelistLock); SetLatch(bgwriterLatch); LWLockAcquire(BufFreelistLock, LW_EXCLUSIVE); } /* * Try to get a buffer from the freelist. Note that the freeNext fields * are considered to be protected by the BufFreelistLock not the * individual buffer spinlocks, so it's OK to manipulate them without * holding the spinlock. */ while (StrategyControl->firstFreeBuffer >= 0) { buf = &BufferDescriptors[StrategyControl->firstFreeBuffer]; Assert(buf->freeNext != FREENEXT_NOT_IN_LIST); /* Unconditionally remove buffer from freelist */ StrategyControl->firstFreeBuffer = buf->freeNext; buf->freeNext = FREENEXT_NOT_IN_LIST; /* * If the buffer is pinned or has a nonzero usage_count, we cannot use * it; discard it and retry. (This can only happen if VACUUM put a * valid buffer in the freelist and then someone else used it before * we got to it. It's probably impossible altogether as of 8.3, but * we'd better check anyway.) */ LockBufHdr(buf); if (buf->refcount == 0 && buf->usage_count == 0) { if (strategy != NULL) AddBufferToRing(strategy, buf); return buf; } UnlockBufHdr(buf); } /* Nothing on the freelist, so run the "clock sweep" algorithm */ trycounter = NBuffers; for (;;) { buf = &BufferDescriptors[StrategyControl->nextVictimBuffer]; if (++StrategyControl->nextVictimBuffer >= NBuffers) { StrategyControl->nextVictimBuffer = 0; StrategyControl->completePasses++; } /* * If the buffer is pinned or has a nonzero usage_count, we cannot use * it; decrement the usage_count (unless pinned) and keep scanning. */ LockBufHdr(buf); if (buf->refcount == 0) { if (buf->usage_count > 0) { buf->usage_count--; trycounter = NBuffers; } else { /* Found a usable buffer */ if (strategy != NULL) AddBufferToRing(strategy, buf); return buf; } } else if (--trycounter == 0) { /* * We've scanned all the buffers without making any state changes, * so all the buffers are pinned (or were when we looked at them). * We could hope that someone will free one eventually, but it's * probably better to fail than to risk getting stuck in an * infinite loop. */ UnlockBufHdr(buf); elog(ERROR, "no unpinned buffers available"); } UnlockBufHdr(buf); } }
void StrategyInitialize | ( | bool | init | ) |
Definition at line 342 of file freelist.c.
References Assert, BufferStrategyControl::bgwriterLatch, BufferStrategyControl::completePasses, BufferStrategyControl::firstFreeBuffer, InitBufTable(), BufferStrategyControl::lastFreeBuffer, NBuffers, BufferStrategyControl::nextVictimBuffer, NUM_BUFFER_PARTITIONS, BufferStrategyControl::numBufferAllocs, and ShmemInitStruct().
Referenced by InitBufferPool().
{ bool found; /* * Initialize the shared buffer lookup hashtable. * * Since we can't tolerate running out of lookup table entries, we must be * sure to specify an adequate table size here. The maximum steady-state * usage is of course NBuffers entries, but BufferAlloc() tries to insert * a new entry before deleting the old. In principle this could be * happening in each partition concurrently, so we could need as many as * NBuffers + NUM_BUFFER_PARTITIONS entries. */ InitBufTable(NBuffers + NUM_BUFFER_PARTITIONS); /* * Get or create the shared strategy control block */ StrategyControl = (BufferStrategyControl *) ShmemInitStruct("Buffer Strategy Status", sizeof(BufferStrategyControl), &found); if (!found) { /* * Only done once, usually in postmaster */ Assert(init); /* * Grab the whole linked list of free buffers for our strategy. We * assume it was previously set up by InitBufferPool(). */ StrategyControl->firstFreeBuffer = 0; StrategyControl->lastFreeBuffer = NBuffers - 1; /* Initialize the clock sweep pointer */ StrategyControl->nextVictimBuffer = 0; /* Clear statistics */ StrategyControl->completePasses = 0; StrategyControl->numBufferAllocs = 0; /* No pending notification */ StrategyControl->bgwriterLatch = NULL; } else Assert(!init); }
void StrategyNotifyBgWriter | ( | Latch * | bgwriterLatch | ) |
Definition at line 299 of file freelist.c.
References BufferStrategyControl::bgwriterLatch, BufFreelistLock, LW_EXCLUSIVE, LWLockAcquire(), and LWLockRelease().
Referenced by BackgroundWriterMain().
{ /* * We acquire the BufFreelistLock just to ensure that the store appears * atomic to StrategyGetBuffer. The bgwriter should call this rather * infrequently, so there's no performance penalty from being safe. */ LWLockAcquire(BufFreelistLock, LW_EXCLUSIVE); StrategyControl->bgwriterLatch = bgwriterLatch; LWLockRelease(BufFreelistLock); }
bool StrategyRejectBuffer | ( | BufferAccessStrategy | strategy, | |
volatile BufferDesc * | buf | |||
) |
Definition at line 547 of file freelist.c.
References BAS_BULKREAD, BufferAccessStrategyData::btype, BufferDescriptorGetBuffer, BufferAccessStrategyData::buffers, BufferAccessStrategyData::current, and BufferAccessStrategyData::current_was_in_ring.
Referenced by BufferAlloc().
{ /* We only do this in bulkread mode */ if (strategy->btype != BAS_BULKREAD) return false; /* Don't muck with behavior of normal buffer-replacement strategy */ if (!strategy->current_was_in_ring || strategy->buffers[strategy->current] != BufferDescriptorGetBuffer(buf)) return false; /* * Remove the dirty buffer from the ring; necessary to prevent infinite * loop if all ring members are dirty. */ strategy->buffers[strategy->current] = InvalidBuffer; return true; }
Size StrategyShmemSize | ( | void | ) |
Definition at line 321 of file freelist.c.
References add_size(), BufTableShmemSize(), MAXALIGN, NBuffers, and NUM_BUFFER_PARTITIONS.
Referenced by BufferShmemSize().
{ Size size = 0; /* size of lookup hash table ... see comment in StrategyInitialize */ size = add_size(size, BufTableShmemSize(NBuffers + NUM_BUFFER_PARTITIONS)); /* size of the shared replacement strategy control block */ size = add_size(size, MAXALIGN(sizeof(BufferStrategyControl))); return size; }
Definition at line 273 of file freelist.c.
References BufFreelistLock, BufferStrategyControl::completePasses, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), BufferStrategyControl::nextVictimBuffer, and BufferStrategyControl::numBufferAllocs.
Referenced by BgBufferSync(), and BufferSync().
{ int result; LWLockAcquire(BufFreelistLock, LW_EXCLUSIVE); result = StrategyControl->nextVictimBuffer; if (complete_passes) *complete_passes = StrategyControl->completePasses; if (num_buf_alloc) { *num_buf_alloc = StrategyControl->numBufferAllocs; StrategyControl->numBufferAllocs = 0; } LWLockRelease(BufFreelistLock); return result; }
BufferStrategyControl* StrategyControl = NULL [static] |
Definition at line 52 of file freelist.c.