*** a/src/backend/postmaster/bgwriter.c --- b/src/backend/postmaster/bgwriter.c *************** *** 230,235 **** BackgroundWriterMain(void) --- 230,242 ---- prev_hibernate = false; /* + * Initialize the freelist latch. ToDo, this needs to be done under + * spinlock which will be used to protect freelist. + */ + + StrategyInitFreeListLatch(&MyProc->procLatch); + + /* * Loop forever */ for (;;) *************** *** 259,265 **** BackgroundWriterMain(void) /* * Do one cycle of dirty-buffer writing. */ ! can_hibernate = BgBufferSync(); /* * Send off activity statistics to the stats collector --- 266,272 ---- /* * Do one cycle of dirty-buffer writing. */ ! /*can_hibernate = BgBufferSync(); */ /* * Send off activity statistics to the stats collector *************** *** 276,281 **** BackgroundWriterMain(void) --- 283,296 ---- } /* + * Sleep untill signalled by backend. + */ + WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_POSTMASTER_DEATH, -1); + + BgBufferSyncAndMoveBuffersToFreelist(); + + + /* * Sleep until we are signaled or BgWriterDelay has elapsed. * * Note: the feedback control loop in BgBufferSync() expects that we *************** *** 285,293 **** BackgroundWriterMain(void) * down with latch events that are likely to happen frequently during * normal operation. */ ! rc = WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, ! BgWriterDelay /* ms */ ); /* * If no latch event and BgBufferSync says nothing's happening, extend --- 300,308 ---- * down with latch events that are likely to happen frequently during * normal operation. */ ! /*rc = WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, ! BgWriterDelay /* ms */ /*);*/ /* * If no latch event and BgBufferSync says nothing's happening, extend *************** *** 307,323 **** BackgroundWriterMain(void) * for two consecutive cycles. Also, we mitigate any possible * consequences of a missed wakeup by not hibernating forever. */ ! if (rc == WL_TIMEOUT && can_hibernate && prev_hibernate) { /* Ask for notification at next buffer allocation */ ! StrategyNotifyBgWriter(&MyProc->procLatch); /* Sleep ... */ ! rc = WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, BgWriterDelay * HIBERNATE_FACTOR); /* Reset the notification request in case we timed out */ ! StrategyNotifyBgWriter(NULL); ! } /* * Emergency bailout if postmaster has died. This is to avoid the --- 322,338 ---- * for two consecutive cycles. Also, we mitigate any possible * consequences of a missed wakeup by not hibernating forever. */ ! /*if (rc == WL_TIMEOUT && can_hibernate && prev_hibernate) { /* Ask for notification at next buffer allocation */ ! /*StrategyNotifyBgWriter(&MyProc->procLatch); /* Sleep ... */ ! /*rc = WaitLatch(&MyProc->procLatch, WL_LATCH_SET | WL_TIMEOUT | WL_POSTMASTER_DEATH, BgWriterDelay * HIBERNATE_FACTOR); /* Reset the notification request in case we timed out */ ! /*StrategyNotifyBgWriter(NULL); ! }*/ /* * Emergency bailout if postmaster has died. This is to avoid the *** a/src/backend/storage/buffer/bufmgr.c --- b/src/backend/storage/buffer/bufmgr.c *************** *** 1633,1638 **** BgBufferSync(void) --- 1633,1725 ---- return (bufs_to_lap == 0 && recent_alloc == 0); } + void + BgBufferSyncAndMoveBuffersToFreelist(void) + { + uint32 next_to_clean; + uint32 save_next_to_clean; + uint32 num_to_free; + int num_written; + bool scanned_buflist = false; + int i; + volatile BufferDesc *bufHdr; + /* + * used to track how many times buffer list is scanned without + * freeing required number of buffers + */ + static uint16 num_times_buflist_scanned = 0; + + StrategySyncStartAndEnd(&next_to_clean, &num_to_free); + + /* + * save the next victim buffer, so that it can be used later + * as a start point to reduce the usage count of buffers. + */ + save_next_to_clean = next_to_clean; + + /* Make sure we can handle the pin inside SyncOneBuffer */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + + num_written = 0; + + /* Execute the LRU scan */ + while (num_to_free > 0) + { + int buffer_state = SyncOneBuffer(next_to_clean, true); + + bufHdr = &BufferDescriptors[next_to_clean]; + if (++next_to_clean >= NBuffers) + { + next_to_clean = 0; + /* + * if buflist is scanned and we didn't find enough buffers + * exit the loop, it can try to free after some more activity. + */ + if (scanned_buflist) + break; + scanned_buflist = true; + } + if (buffer_state & BUF_WRITTEN) + ++num_written; + if (buffer_state & BUF_REUSABLE) + { + if (StrategyMoveBufferToFreeListEnd (bufHdr)) + num_to_free--; + } + } + + BgWriterStats.m_buf_written_clean += num_written; + + /* + * try to reduce the usage count if not able to free required + * number of buffers. It will traverse proportional to number of times + * it failed to free required number of buffers. + */ + + if(scanned_buflist && num_to_free != 0) + { + ++num_times_buflist_scanned; + for (i = 0; i < num_times_buflist_scanned * NBuffers; i++) + { + bufHdr = &BufferDescriptors[save_next_to_clean]; + + if (++save_next_to_clean >= NBuffers) + save_next_to_clean = 0; + + /* + * If the buffer is not-pinned and has a nonzero usage_count, decrement + * the usage_count and keep scanning. + */ + LockBufHdr(bufHdr); + if (bufHdr->refcount == 0 && bufHdr->usage_count > 0) + bufHdr->usage_count--; + UnlockBufHdr(bufHdr); + } + } + else + num_times_buflist_scanned = 0; + } + /* * SyncOneBuffer -- process a single buffer during syncing. * *** a/src/backend/storage/buffer/freelist.c --- b/src/backend/storage/buffer/freelist.c *************** *** 29,34 **** typedef struct --- 29,35 ---- int firstFreeBuffer; /* Head of list of unused buffers */ int lastFreeBuffer; /* Tail of list of unused buffers */ + int numFreeListBuffers; /* number of buffers on freelist */ /* * NOTE: lastFreeBuffer is undefined when firstFreeBuffer is -1 (that is, *************** *** 42,47 **** typedef struct --- 43,52 ---- uint32 completePasses; /* Complete cycles of the clock sweep */ uint32 numBufferAllocs; /* Buffers allocated since last reset */ + Latch *freelistLatch; /* Latch to wake bgwriter */ + /* protects freelist variables (firstFreeBuffer, lastFreeBuffer, numFreeListBuffers, BufferDesc->freeNext)*/ + slock_t freelist_lck; + /* * Notification latch, or NULL if none. See StrategyNotifyBgWriter. */ *************** *** 112,118 **** volatile BufferDesc * StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held) { volatile BufferDesc *buf; ! Latch *bgwriterLatch; int trycounter; /* --- 117,123 ---- StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held) { volatile BufferDesc *buf; ! /*Latch *bgwriterLatch;*/ int trycounter; /* *************** *** 130,144 **** StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held) } /* 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 --- 135,149 ---- } /* 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 *************** *** 146,159 **** StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held) * 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 --- 151,169 ---- * 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); ! }*/ ! ! SpinLockAcquire(&StrategyControl->freelist_lck); ! ! if (StrategyControl->numFreeListBuffers < NBuffers/6) ! SetLatch(StrategyControl->freelistLatch); /* * Try to get a buffer from the freelist. Note that the freeNext fields *************** *** 169,174 **** StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held) --- 179,185 ---- /* Unconditionally remove buffer from freelist */ StrategyControl->firstFreeBuffer = buf->freeNext; buf->freeNext = FREENEXT_NOT_IN_LIST; + --StrategyControl->numFreeListBuffers; /* * If the buffer is pinned or has a nonzero usage_count, we cannot use *************** *** 182,194 **** StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held) --- 193,213 ---- { if (strategy != NULL) AddBufferToRing(strategy, buf); + SpinLockRelease(&StrategyControl->freelist_lck); + *lock_held = false; return buf; } UnlockBufHdr(buf); } + SpinLockRelease(&StrategyControl->freelist_lck); + /* Nothing on the freelist, so run the "clock sweep" algorithm */ trycounter = NBuffers; + + *lock_held = true; + LWLockAcquire(BufFreelistLock, LW_EXCLUSIVE); + for (;;) { buf = &BufferDescriptors[StrategyControl->nextVictimBuffer]; *************** *** 196,202 **** StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held) if (++StrategyControl->nextVictimBuffer >= NBuffers) { StrategyControl->nextVictimBuffer = 0; ! StrategyControl->completePasses++; } /* --- 215,221 ---- if (++StrategyControl->nextVictimBuffer >= NBuffers) { StrategyControl->nextVictimBuffer = 0; ! /*StrategyControl->completePasses++;*/ } /* *************** *** 241,247 **** StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held) void StrategyFreeBuffer(volatile BufferDesc *buf) { ! LWLockAcquire(BufFreelistLock, LW_EXCLUSIVE); /* * It is possible that we are told to put something in the freelist that --- 260,266 ---- void StrategyFreeBuffer(volatile BufferDesc *buf) { ! SpinLockAcquire(&StrategyControl->freelist_lck); /* * It is possible that we are told to put something in the freelist that *************** *** 253,264 **** StrategyFreeBuffer(volatile BufferDesc *buf) if (buf->freeNext < 0) StrategyControl->lastFreeBuffer = buf->buf_id; StrategyControl->firstFreeBuffer = buf->buf_id; } ! LWLockRelease(BufFreelistLock); } /* * StrategySyncStart -- tell BufferSync where to start syncing * * The result is the buffer index of the best buffer to sync first. --- 272,321 ---- if (buf->freeNext < 0) StrategyControl->lastFreeBuffer = buf->buf_id; StrategyControl->firstFreeBuffer = buf->buf_id; + ++StrategyControl->numFreeListBuffers; } ! SpinLockRelease(&StrategyControl->freelist_lck); } /* + * StrategyMoveBufferToFreeListEnd: put a buffer on the end of freelist + */ + bool + StrategyMoveBufferToFreeListEnd(volatile BufferDesc *buf) + { + bool freed = false; + SpinLockAcquire(&StrategyControl->freelist_lck); + /* + * 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) + { + ++StrategyControl->numFreeListBuffers; + freed = true; + /* + * put the buffer on end of list and if list is empty then + * assign first and last freebuffer with this buffer id. + */ + buf->freeNext = FREENEXT_END_OF_LIST; + if (StrategyControl->firstFreeBuffer < 0) + { + StrategyControl->firstFreeBuffer = buf->buf_id; + StrategyControl->lastFreeBuffer = buf->buf_id; + SpinLockRelease(&StrategyControl->freelist_lck); + return freed; + } + BufferDescriptors[StrategyControl->lastFreeBuffer].freeNext = buf->buf_id; + StrategyControl->lastFreeBuffer = buf->buf_id; + } + SpinLockRelease(&StrategyControl->freelist_lck); + + return freed; + } + + + /* * StrategySyncStart -- tell BufferSync where to start syncing * * The result is the buffer index of the best buffer to sync first. *************** *** 287,292 **** StrategySyncStart(uint32 *complete_passes, uint32 *num_buf_alloc) --- 344,372 ---- return result; } + void + StrategySyncStartAndEnd(uint32 *start, uint32 *end) + { + int curfreebuffers; + int reqfreebuffers; + + /* + * ideally numFreeListBuffers should get called under + * freelist spin lock, however here we need this number for + * estimating approximate number of free buffers required + * on freelist, so it would be okay, even if numFreeListBuffers is not exact. + */ + + + LWLockAcquire(BufFreelistLock, LW_EXCLUSIVE); + *start = StrategyControl->nextVictimBuffer; + curfreebuffers = StrategyControl->numFreeListBuffers; + reqfreebuffers = NBuffers/3; + *end = reqfreebuffers -curfreebuffers; + LWLockRelease(BufFreelistLock); + return; + } + /* * StrategyNotifyBgWriter -- set or clear allocation notification latch * *************** *** 309,314 **** StrategyNotifyBgWriter(Latch *bgwriterLatch) --- 389,407 ---- } + void + StrategyInitFreeListLatch(Latch *bgwriterLatch) + { + /* + * 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. + */ + SpinLockAcquire(&StrategyControl->freelist_lck); + StrategyControl->freelistLatch= bgwriterLatch; + SpinLockRelease(&StrategyControl->freelist_lck); + } + /* * StrategyShmemSize * *************** *** 376,381 **** StrategyInitialize(bool init) --- 469,475 ---- */ StrategyControl->firstFreeBuffer = 0; StrategyControl->lastFreeBuffer = NBuffers - 1; + StrategyControl->numFreeListBuffers = NBuffers; /* Initialize the clock sweep pointer */ StrategyControl->nextVictimBuffer = 0; *************** *** 386,391 **** StrategyInitialize(bool init) --- 480,487 ---- /* No pending notification */ StrategyControl->bgwriterLatch = NULL; + StrategyControl->freelistLatch = NULL; + SpinLockInit(&StrategyControl->freelist_lck); } else Assert(!init); *** a/src/include/storage/buf_internals.h --- b/src/include/storage/buf_internals.h *************** *** 185,195 **** extern BufferDesc *LocalBufferDescriptors; --- 185,198 ---- extern volatile BufferDesc *StrategyGetBuffer(BufferAccessStrategy strategy, bool *lock_held); extern void StrategyFreeBuffer(volatile BufferDesc *buf); + extern bool StrategyMoveBufferToFreeListEnd(volatile BufferDesc *buf); extern bool StrategyRejectBuffer(BufferAccessStrategy strategy, volatile BufferDesc *buf); extern int StrategySyncStart(uint32 *complete_passes, uint32 *num_buf_alloc); + extern void StrategySyncStartAndEnd(uint32 *start, uint32 *end); extern void StrategyNotifyBgWriter(Latch *bgwriterLatch); + extern void StrategyInitFreeListLatch(Latch *bgwriterLatch); extern Size StrategyShmemSize(void); extern void StrategyInitialize(bool init); *** a/src/include/storage/bufmgr.h --- b/src/include/storage/bufmgr.h *************** *** 217,222 **** extern void AbortBufferIO(void); --- 217,223 ---- extern void BufmgrCommit(void); extern bool BgBufferSync(void); + extern void BgBufferSyncAndMoveBuffersToFreelist(void); extern void AtProcExit_LocalBuffers(void); =