aboutsummaryrefslogtreecommitdiff
path: root/src/backend/storage/buffer/bufmgr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/storage/buffer/bufmgr.c')
-rw-r--r--src/backend/storage/buffer/bufmgr.c164
1 files changed, 126 insertions, 38 deletions
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 506f71e6533..f7c67d504cd 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -47,6 +47,7 @@
#include "postmaster/bgwriter.h"
#include "storage/buf_internals.h"
#include "storage/bufmgr.h"
+#include "storage/fd.h"
#include "storage/ipc.h"
#include "storage/lmgr.h"
#include "storage/proc.h"
@@ -55,7 +56,7 @@
#include "utils/memdebug.h"
#include "utils/ps_status.h"
#include "utils/rel.h"
-#include "utils/resowner_private.h"
+#include "utils/resowner.h"
#include "utils/timestamp.h"
@@ -205,6 +206,30 @@ static PrivateRefCountEntry *GetPrivateRefCountEntry(Buffer buffer, bool do_move
static inline int32 GetPrivateRefCount(Buffer buffer);
static void ForgetPrivateRefCountEntry(PrivateRefCountEntry *ref);
+/* ResourceOwner callbacks to hold in-progress I/Os and buffer pins */
+static void ResOwnerReleaseBufferIO(Datum res);
+static char *ResOwnerPrintBufferIO(Datum res);
+static void ResOwnerReleaseBufferPin(Datum res);
+static char *ResOwnerPrintBufferPin(Datum res);
+
+const ResourceOwnerDesc buffer_io_resowner_desc =
+{
+ .name = "buffer io",
+ .release_phase = RESOURCE_RELEASE_BEFORE_LOCKS,
+ .release_priority = RELEASE_PRIO_BUFFER_IOS,
+ .ReleaseResource = ResOwnerReleaseBufferIO,
+ .DebugPrint = ResOwnerPrintBufferIO
+};
+
+const ResourceOwnerDesc buffer_pin_resowner_desc =
+{
+ .name = "buffer pin",
+ .release_phase = RESOURCE_RELEASE_BEFORE_LOCKS,
+ .release_priority = RELEASE_PRIO_BUFFER_PINS,
+ .ReleaseResource = ResOwnerReleaseBufferPin,
+ .DebugPrint = ResOwnerPrintBufferPin
+};
+
/*
* Ensure that the PrivateRefCountArray has sufficient space to store one more
* entry. This has to be called before using NewPrivateRefCountEntry() to fill
@@ -470,6 +495,7 @@ static BlockNumber ExtendBufferedRelShared(BufferManagerRelation bmr,
static bool PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy);
static void PinBuffer_Locked(BufferDesc *buf);
static void UnpinBuffer(BufferDesc *buf);
+static void UnpinBufferNoOwner(BufferDesc *buf);
static void BufferSync(int flags);
static uint32 WaitBufHdrUnlocked(BufferDesc *buf);
static int SyncOneBuffer(int buf_id, bool skip_recently_used,
@@ -477,7 +503,8 @@ static int SyncOneBuffer(int buf_id, bool skip_recently_used,
static void WaitIO(BufferDesc *buf);
static bool StartBufferIO(BufferDesc *buf, bool forInput);
static void TerminateBufferIO(BufferDesc *buf, bool clear_dirty,
- uint32 set_flag_bits);
+ uint32 set_flag_bits, bool forget_owner);
+static void AbortBufferIO(Buffer buffer);
static void shared_buffer_write_error_callback(void *arg);
static void local_buffer_write_error_callback(void *arg);
static BufferDesc *BufferAlloc(SMgrRelation smgr,
@@ -639,7 +666,7 @@ ReadRecentBuffer(RelFileLocator rlocator, ForkNumber forkNum, BlockNumber blockN
Assert(BufferIsValid(recent_buffer));
- ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
+ ResourceOwnerEnlarge(CurrentResourceOwner);
ReservePrivateRefCountEntry();
InitBufferTag(&tag, &rlocator, forkNum, blockNum);
@@ -1173,7 +1200,7 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
else
{
/* Set BM_VALID, terminate IO, and wake up any waiters */
- TerminateBufferIO(bufHdr, false, BM_VALID);
+ TerminateBufferIO(bufHdr, false, BM_VALID, true);
}
VacuumPageMiss++;
@@ -1228,7 +1255,7 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
uint32 victim_buf_state;
/* Make sure we will have room to remember the buffer pin */
- ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
+ ResourceOwnerEnlarge(CurrentResourceOwner);
ReservePrivateRefCountEntry();
/* create a tag so we can lookup the buffer */
@@ -1315,9 +1342,8 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
* use.
*
* We could do this after releasing the partition lock, but then we'd
- * have to call ResourceOwnerEnlargeBuffers() &
- * ReservePrivateRefCountEntry() before acquiring the lock, for the
- * rare case of such a collision.
+ * have to call ResourceOwnerEnlarge() & ReservePrivateRefCountEntry()
+ * before acquiring the lock, for the rare case of such a collision.
*/
UnpinBuffer(victim_buf_hdr);
@@ -1595,7 +1621,7 @@ GetVictimBuffer(BufferAccessStrategy strategy, IOContext io_context)
* entry, and a resource owner slot for the pin.
*/
ReservePrivateRefCountEntry();
- ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
+ ResourceOwnerEnlarge(CurrentResourceOwner);
/* we return here if a prospective victim buffer gets used concurrently */
again:
@@ -1946,7 +1972,7 @@ ExtendBufferedRelShared(BufferManagerRelation bmr,
int existing_id;
/* in case we need to pin an existing buffer below */
- ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
+ ResourceOwnerEnlarge(CurrentResourceOwner);
ReservePrivateRefCountEntry();
InitBufferTag(&tag, &bmr.smgr->smgr_rlocator.locator, fork, first_block + i);
@@ -2090,7 +2116,7 @@ ExtendBufferedRelShared(BufferManagerRelation bmr,
if (lock)
LWLockAcquire(BufferDescriptorGetContentLock(buf_hdr), LW_EXCLUSIVE);
- TerminateBufferIO(buf_hdr, false, BM_VALID);
+ TerminateBufferIO(buf_hdr, false, BM_VALID, true);
}
pgBufferUsage.shared_blks_written += extend_by;
@@ -2283,7 +2309,7 @@ ReleaseAndReadBuffer(Buffer buffer,
* taking the buffer header lock; instead update the state variable in loop of
* CAS operations. Hopefully it's just a single CAS.
*
- * Note that ResourceOwnerEnlargeBuffers and ReservePrivateRefCountEntry()
+ * Note that ResourceOwnerEnlarge() and ReservePrivateRefCountEntry()
* must have been done already.
*
* Returns true if buffer is BM_VALID, else false. This provision allows
@@ -2379,7 +2405,7 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy)
*
* As this function is called with the spinlock held, the caller has to
* previously call ReservePrivateRefCountEntry() and
- * ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
+ * ResourceOwnerEnlarge(CurrentResourceOwner);
*
* Currently, no callers of this function want to modify the buffer's
* usage_count at all, so there's no need for a strategy parameter.
@@ -2441,6 +2467,15 @@ PinBuffer_Locked(BufferDesc *buf)
static void
UnpinBuffer(BufferDesc *buf)
{
+ Buffer b = BufferDescriptorGetBuffer(buf);
+
+ ResourceOwnerForgetBuffer(CurrentResourceOwner, b);
+ UnpinBufferNoOwner(buf);
+}
+
+static void
+UnpinBufferNoOwner(BufferDesc *buf)
+{
PrivateRefCountEntry *ref;
Buffer b = BufferDescriptorGetBuffer(buf);
@@ -2449,9 +2484,6 @@ UnpinBuffer(BufferDesc *buf)
/* not moving as we're likely deleting it soon anyway */
ref = GetPrivateRefCountEntry(b, false);
Assert(ref != NULL);
-
- ResourceOwnerForgetBuffer(CurrentResourceOwner, b);
-
Assert(ref->refcount > 0);
ref->refcount--;
if (ref->refcount == 0)
@@ -3122,7 +3154,7 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context)
/* Make sure we can handle the pin */
ReservePrivateRefCountEntry();
- ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
+ ResourceOwnerEnlarge(CurrentResourceOwner);
/*
* Check whether buffer needs writing.
@@ -3252,6 +3284,7 @@ CheckForBufferLeaks(void)
int RefCountErrors = 0;
PrivateRefCountEntry *res;
int i;
+ char *s;
/* check the array */
for (i = 0; i < REFCOUNT_ARRAY_ENTRIES; i++)
@@ -3260,7 +3293,10 @@ CheckForBufferLeaks(void)
if (res->buffer != InvalidBuffer)
{
- PrintBufferLeakWarning(res->buffer);
+ s = DebugPrintBufferRefcount(res->buffer);
+ elog(WARNING, "buffer refcount leak: %s", s);
+ pfree(s);
+
RefCountErrors++;
}
}
@@ -3273,7 +3309,9 @@ CheckForBufferLeaks(void)
hash_seq_init(&hstat, PrivateRefCountHash);
while ((res = (PrivateRefCountEntry *) hash_seq_search(&hstat)) != NULL)
{
- PrintBufferLeakWarning(res->buffer);
+ s = DebugPrintBufferRefcount(res->buffer);
+ elog(WARNING, "buffer refcount leak: %s", s);
+ pfree(s);
RefCountErrors++;
}
}
@@ -3285,12 +3323,13 @@ CheckForBufferLeaks(void)
/*
* Helper routine to issue warnings when a buffer is unexpectedly pinned
*/
-void
-PrintBufferLeakWarning(Buffer buffer)
+char *
+DebugPrintBufferRefcount(Buffer buffer)
{
BufferDesc *buf;
int32 loccount;
char *path;
+ char *result;
BackendId backend;
uint32 buf_state;
@@ -3312,13 +3351,13 @@ PrintBufferLeakWarning(Buffer buffer)
path = relpathbackend(BufTagGetRelFileLocator(&buf->tag), backend,
BufTagGetForkNum(&buf->tag));
buf_state = pg_atomic_read_u32(&buf->state);
- elog(WARNING,
- "buffer refcount leak: [%03d] "
- "(rel=%s, blockNum=%u, flags=0x%x, refcount=%u %d)",
- buffer, path,
- buf->tag.blockNum, buf_state & BUF_FLAG_MASK,
- BUF_STATE_GET_REFCOUNT(buf_state), loccount);
+
+ result = psprintf("[%03d] (rel=%s, blockNum=%u, flags=0x%x, refcount=%u %d)",
+ buffer, path,
+ buf->tag.blockNum, buf_state & BUF_FLAG_MASK,
+ BUF_STATE_GET_REFCOUNT(buf_state), loccount);
pfree(path);
+ return result;
}
/*
@@ -3522,7 +3561,7 @@ FlushBuffer(BufferDesc *buf, SMgrRelation reln, IOObject io_object,
* Mark the buffer as clean (unless BM_JUST_DIRTIED has become set) and
* end the BM_IO_IN_PROGRESS state.
*/
- TerminateBufferIO(buf, true, 0);
+ TerminateBufferIO(buf, true, 0, true);
TRACE_POSTGRESQL_BUFFER_FLUSH_DONE(BufTagGetForkNum(&buf->tag),
buf->tag.blockNum,
@@ -4182,7 +4221,7 @@ FlushRelationBuffers(Relation rel)
/* Make sure we can handle the pin */
ReservePrivateRefCountEntry();
- ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
+ ResourceOwnerEnlarge(CurrentResourceOwner);
buf_state = LockBufHdr(bufHdr);
if (BufTagMatchesRelFileLocator(&bufHdr->tag, &rel->rd_locator) &&
@@ -4279,7 +4318,7 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels)
/* Make sure we can handle the pin */
ReservePrivateRefCountEntry();
- ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
+ ResourceOwnerEnlarge(CurrentResourceOwner);
buf_state = LockBufHdr(bufHdr);
if (BufTagMatchesRelFileLocator(&bufHdr->tag, &srelent->rlocator) &&
@@ -4489,7 +4528,7 @@ FlushDatabaseBuffers(Oid dbid)
/* Make sure we can handle the pin */
ReservePrivateRefCountEntry();
- ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
+ ResourceOwnerEnlarge(CurrentResourceOwner);
buf_state = LockBufHdr(bufHdr);
if (bufHdr->tag.dbOid == dbid &&
@@ -4566,7 +4605,7 @@ void
IncrBufferRefCount(Buffer buffer)
{
Assert(BufferIsPinned(buffer));
- ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
+ ResourceOwnerEnlarge(CurrentResourceOwner);
if (BufferIsLocal(buffer))
LocalRefCount[-buffer - 1]++;
else
@@ -5164,7 +5203,7 @@ StartBufferIO(BufferDesc *buf, bool forInput)
{
uint32 buf_state;
- ResourceOwnerEnlargeBufferIOs(CurrentResourceOwner);
+ ResourceOwnerEnlarge(CurrentResourceOwner);
for (;;)
{
@@ -5209,9 +5248,14 @@ StartBufferIO(BufferDesc *buf, bool forInput)
* set_flag_bits gets ORed into the buffer's flags. It must include
* BM_IO_ERROR in a failure case. For successful completion it could
* be 0, or BM_VALID if we just finished reading in the page.
+ *
+ * If forget_owner is true, we release the buffer I/O from the current
+ * resource owner. (forget_owner=false is used when the resource owner itself
+ * is being released)
*/
static void
-TerminateBufferIO(BufferDesc *buf, bool clear_dirty, uint32 set_flag_bits)
+TerminateBufferIO(BufferDesc *buf, bool clear_dirty, uint32 set_flag_bits,
+ bool forget_owner)
{
uint32 buf_state;
@@ -5226,8 +5270,9 @@ TerminateBufferIO(BufferDesc *buf, bool clear_dirty, uint32 set_flag_bits)
buf_state |= set_flag_bits;
UnlockBufHdr(buf, buf_state);
- ResourceOwnerForgetBufferIO(CurrentResourceOwner,
- BufferDescriptorGetBuffer(buf));
+ if (forget_owner)
+ ResourceOwnerForgetBufferIO(CurrentResourceOwner,
+ BufferDescriptorGetBuffer(buf));
ConditionVariableBroadcast(BufferDescriptorGetIOCV(buf));
}
@@ -5240,8 +5285,12 @@ TerminateBufferIO(BufferDesc *buf, bool clear_dirty, uint32 set_flag_bits)
*
* If I/O was in progress, we always set BM_IO_ERROR, even though it's
* possible the error condition wasn't related to the I/O.
+ *
+ * Note: this does not remove the buffer I/O from the resource owner.
+ * That's correct when we're releasing the whole resource owner, but
+ * beware if you use this in other contexts.
*/
-void
+static void
AbortBufferIO(Buffer buffer)
{
BufferDesc *buf_hdr = GetBufferDescriptor(buffer - 1);
@@ -5277,7 +5326,7 @@ AbortBufferIO(Buffer buffer)
}
}
- TerminateBufferIO(buf_hdr, false, BM_IO_ERROR);
+ TerminateBufferIO(buf_hdr, false, BM_IO_ERROR, false);
}
/*
@@ -5629,3 +5678,42 @@ IssuePendingWritebacks(WritebackContext *wb_context, IOContext io_context)
wb_context->nr_pending = 0;
}
+
+/* ResourceOwner callbacks */
+
+static void
+ResOwnerReleaseBufferIO(Datum res)
+{
+ Buffer buffer = DatumGetInt32(res);
+
+ AbortBufferIO(buffer);
+}
+
+static char *
+ResOwnerPrintBufferIO(Datum res)
+{
+ Buffer buffer = DatumGetInt32(res);
+
+ return psprintf("lost track of buffer IO on buffer %d", buffer);
+}
+
+static void
+ResOwnerReleaseBufferPin(Datum res)
+{
+ Buffer buffer = DatumGetInt32(res);
+
+ /* Like ReleaseBuffer, but don't call ResourceOwnerForgetBuffer */
+ if (!BufferIsValid(buffer))
+ elog(ERROR, "bad buffer ID: %d", buffer);
+
+ if (BufferIsLocal(buffer))
+ UnpinLocalBufferNoOwner(buffer);
+ else
+ UnpinBufferNoOwner(GetBufferDescriptor(buffer - 1));
+}
+
+static char *
+ResOwnerPrintBufferPin(Datum res)
+{
+ return DebugPrintBufferRefcount(DatumGetInt32(res));
+}