Skip to content

Commit 9660906

Browse files
committed
Add routines for marking buffers dirty efficiently
This commit introduces new internal bufmgr routines for marking shared buffers as dirty: * MarkDirtyUnpinnedBuffer() * MarkDirtyRelUnpinnedBuffers() * MarkDirtyAllUnpinnedBuffers() These functions provide an efficient mechanism to respectively mark one buffer, all the buffers of a relation, or the entire shared buffer pool as dirty, something that can be useful to force patterns for the checkpointer. MarkDirtyUnpinnedBufferInternal(), an extra routine, is used by these three, to mark as dirty an unpinned buffer. They are intended as developer tools to manipulate buffer dirtiness in bulk, and will be used in a follow-up commit. Author: Nazir Bilal Yavuz <byavuz81@gmail.com> Reviewed-by: Andres Freund <andres@anarazel.de> Reviewed-by: Aidar Imamov <a.imamov@postgrespro.ru> Reviewed-by: Amit Kapila <amit.kapila16@gmail.com> Reviewed-by: Joseph Koshakow <koshy44@gmail.com> Reviewed-by: Michael Paquier <michael@paquier.xyz> Reviewed-by: Yuhang Qiu <iamqyh@gmail.com> Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com> Discussion: https://postgr.es/m/CAN55FZ0h_YoSqqutxV6DES1RW8ig6wcA8CR9rJk358YRMxZFmw@mail.gmail.com
1 parent 5528e8d commit 9660906

File tree

2 files changed

+196
-0
lines changed

2 files changed

+196
-0
lines changed

src/backend/storage/buffer/bufmgr.c

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6776,6 +6776,194 @@ EvictRelUnpinnedBuffers(Relation rel, int32 *buffers_evicted,
67766776
}
67776777
}
67786778

6779+
/*
6780+
* Helper function to mark unpinned buffer dirty whose buffer header lock is
6781+
* already acquired.
6782+
*/
6783+
static bool
6784+
MarkDirtyUnpinnedBufferInternal(Buffer buf, BufferDesc *desc,
6785+
bool *buffer_already_dirty)
6786+
{
6787+
uint32 buf_state;
6788+
bool result = false;
6789+
6790+
*buffer_already_dirty = false;
6791+
6792+
buf_state = pg_atomic_read_u32(&(desc->state));
6793+
Assert(buf_state & BM_LOCKED);
6794+
6795+
if ((buf_state & BM_VALID) == 0)
6796+
{
6797+
UnlockBufHdr(desc);
6798+
return false;
6799+
}
6800+
6801+
/* Check that it's not pinned already. */
6802+
if (BUF_STATE_GET_REFCOUNT(buf_state) > 0)
6803+
{
6804+
UnlockBufHdr(desc);
6805+
return false;
6806+
}
6807+
6808+
/* Pin the buffer and then release the buffer spinlock */
6809+
PinBuffer_Locked(desc);
6810+
6811+
/* If it was not already dirty, mark it as dirty. */
6812+
if (!(buf_state & BM_DIRTY))
6813+
{
6814+
LWLockAcquire(BufferDescriptorGetContentLock(desc), LW_EXCLUSIVE);
6815+
MarkBufferDirty(buf);
6816+
result = true;
6817+
LWLockRelease(BufferDescriptorGetContentLock(desc));
6818+
}
6819+
else
6820+
*buffer_already_dirty = true;
6821+
6822+
UnpinBuffer(desc);
6823+
6824+
return result;
6825+
}
6826+
6827+
/*
6828+
* Try to mark the provided shared buffer as dirty.
6829+
*
6830+
* This function is intended for testing/development use only!
6831+
*
6832+
* Same as EvictUnpinnedBuffer() but with MarkBufferDirty() call inside.
6833+
*
6834+
* The buffer_already_dirty parameter is mandatory and indicate if the buffer
6835+
* could not be dirtied because it is already dirty.
6836+
*
6837+
* Returns true if the buffer has successfully been marked as dirty.
6838+
*/
6839+
bool
6840+
MarkDirtyUnpinnedBuffer(Buffer buf, bool *buffer_already_dirty)
6841+
{
6842+
BufferDesc *desc;
6843+
bool buffer_dirtied = false;
6844+
6845+
Assert(!BufferIsLocal(buf));
6846+
6847+
/* Make sure we can pin the buffer. */
6848+
ResourceOwnerEnlarge(CurrentResourceOwner);
6849+
ReservePrivateRefCountEntry();
6850+
6851+
desc = GetBufferDescriptor(buf - 1);
6852+
LockBufHdr(desc);
6853+
6854+
buffer_dirtied = MarkDirtyUnpinnedBufferInternal(buf, desc, buffer_already_dirty);
6855+
/* Both can not be true at the same time */
6856+
Assert(!(buffer_dirtied && *buffer_already_dirty));
6857+
6858+
return buffer_dirtied;
6859+
}
6860+
6861+
/*
6862+
* Try to mark all the shared buffers containing provided relation's pages as
6863+
* dirty.
6864+
*
6865+
* This function is intended for testing/development use only! See
6866+
* MarkDirtyUnpinnedBuffer().
6867+
*
6868+
* The buffers_* parameters are mandatory and indicate the total count of
6869+
* buffers that:
6870+
* - buffers_dirtied - were dirtied
6871+
* - buffers_already_dirty - were already dirty
6872+
* - buffers_skipped - could not be dirtied because of a reason different
6873+
* than a buffer being already dirty.
6874+
*/
6875+
void
6876+
MarkDirtyRelUnpinnedBuffers(Relation rel,
6877+
int32 *buffers_dirtied,
6878+
int32 *buffers_already_dirty,
6879+
int32 *buffers_skipped)
6880+
{
6881+
Assert(!RelationUsesLocalBuffers(rel));
6882+
6883+
*buffers_dirtied = 0;
6884+
*buffers_already_dirty = 0;
6885+
*buffers_skipped = 0;
6886+
6887+
for (int buf = 1; buf <= NBuffers; buf++)
6888+
{
6889+
BufferDesc *desc = GetBufferDescriptor(buf - 1);
6890+
uint32 buf_state = pg_atomic_read_u32(&(desc->state));
6891+
bool buffer_already_dirty;
6892+
6893+
CHECK_FOR_INTERRUPTS();
6894+
6895+
/* An unlocked precheck should be safe and saves some cycles. */
6896+
if ((buf_state & BM_VALID) == 0 ||
6897+
!BufTagMatchesRelFileLocator(&desc->tag, &rel->rd_locator))
6898+
continue;
6899+
6900+
/* Make sure we can pin the buffer. */
6901+
ResourceOwnerEnlarge(CurrentResourceOwner);
6902+
ReservePrivateRefCountEntry();
6903+
6904+
buf_state = LockBufHdr(desc);
6905+
6906+
/* recheck, could have changed without the lock */
6907+
if ((buf_state & BM_VALID) == 0 ||
6908+
!BufTagMatchesRelFileLocator(&desc->tag, &rel->rd_locator))
6909+
{
6910+
UnlockBufHdr(desc);
6911+
continue;
6912+
}
6913+
6914+
if (MarkDirtyUnpinnedBufferInternal(buf, desc, &buffer_already_dirty))
6915+
(*buffers_dirtied)++;
6916+
else if (buffer_already_dirty)
6917+
(*buffers_already_dirty)++;
6918+
else
6919+
(*buffers_skipped)++;
6920+
}
6921+
}
6922+
6923+
/*
6924+
* Try to mark all the shared buffers as dirty.
6925+
*
6926+
* This function is intended for testing/development use only! See
6927+
* MarkDirtyUnpinnedBuffer().
6928+
*
6929+
* See MarkDirtyRelUnpinnedBuffers() above for details about the buffers_*
6930+
* parameters.
6931+
*/
6932+
void
6933+
MarkDirtyAllUnpinnedBuffers(int32 *buffers_dirtied,
6934+
int32 *buffers_already_dirty,
6935+
int32 *buffers_skipped)
6936+
{
6937+
*buffers_dirtied = 0;
6938+
*buffers_already_dirty = 0;
6939+
*buffers_skipped = 0;
6940+
6941+
for (int buf = 1; buf <= NBuffers; buf++)
6942+
{
6943+
BufferDesc *desc = GetBufferDescriptor(buf - 1);
6944+
uint32 buf_state;
6945+
bool buffer_already_dirty;
6946+
6947+
CHECK_FOR_INTERRUPTS();
6948+
6949+
buf_state = pg_atomic_read_u32(&desc->state);
6950+
if (!(buf_state & BM_VALID))
6951+
continue;
6952+
6953+
ResourceOwnerEnlarge(CurrentResourceOwner);
6954+
ReservePrivateRefCountEntry();
6955+
6956+
LockBufHdr(desc);
6957+
6958+
if (MarkDirtyUnpinnedBufferInternal(buf, desc, &buffer_already_dirty))
6959+
(*buffers_dirtied)++;
6960+
else if (buffer_already_dirty)
6961+
(*buffers_already_dirty)++;
6962+
else
6963+
(*buffers_skipped)++;
6964+
}
6965+
}
6966+
67796967
/*
67806968
* Generic implementation of the AIO handle staging callback for readv/writev
67816969
* on local/shared buffers.

src/include/storage/bufmgr.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,14 @@ extern void EvictRelUnpinnedBuffers(Relation rel,
323323
int32 *buffers_evicted,
324324
int32 *buffers_flushed,
325325
int32 *buffers_skipped);
326+
extern bool MarkDirtyUnpinnedBuffer(Buffer buf, bool *buffer_already_dirty);
327+
extern void MarkDirtyRelUnpinnedBuffers(Relation rel,
328+
int32 *buffers_dirtied,
329+
int32 *buffers_already_dirty,
330+
int32 *buffers_skipped);
331+
extern void MarkDirtyAllUnpinnedBuffers(int32 *buffers_dirtied,
332+
int32 *buffers_already_dirty,
333+
int32 *buffers_skipped);
326334

327335
/* in buf_init.c */
328336
extern void BufferManagerShmemInit(void);

0 commit comments

Comments
 (0)