agora inbox for [email protected]help / color / mirror / Atom feed
[PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. 91+ messages / 3 participants [nested] [flat]
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v8 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 86ef607ff38..2383e4cac08 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -810,9 +810,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1165,9 +1162,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1668,8 +1667,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1680,6 +1677,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1760,7 +1759,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1936,9 +1936,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2412,9 +2409,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2496,8 +2490,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2507,7 +2499,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3559,9 +3553,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3575,7 +3566,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3632,9 +3625,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3671,7 +3661,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3711,9 +3703,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3727,7 +3716,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 4fbdc62d8c7..13eed587601 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1609,8 +1609,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1698,13 +1696,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.2 --------------846F221A8A016B7AA8E567B3 Content-Type: text/x-patch; charset=UTF-8; name="v8-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v8-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v10 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index e88e4e918b0..cde9184c7d6 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -810,9 +810,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1174,9 +1171,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1677,8 +1676,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1689,6 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1769,7 +1768,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1945,9 +1945,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2421,9 +2418,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2505,8 +2499,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2516,7 +2508,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3555,9 +3549,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3571,7 +3562,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3628,9 +3621,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3667,7 +3657,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3707,9 +3699,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3723,7 +3712,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 4fbdc62d8c7..13eed587601 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1609,8 +1609,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1698,13 +1696,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.2 --------------AF226508B1039DE40796DE51 Content-Type: text/x-patch; charset=UTF-8; name="v10-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v10-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------7349B9D66ED595EF3B1030B4 Content-Type: text/x-patch; charset=UTF-8; name="v4-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v4-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------1104342304EC6341F5D7C60C Content-Type: text/x-patch; charset=UTF-8; name="v5-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v5-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index fa2b49c676e..86fdfc454d2 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.29.2 --------------F30A5443A5D524A5CBC51AE7 Content-Type: text/x-patch; charset=UTF-8; name="v6-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v6-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. @ 2021-01-13 10:21 Heikki Linnakangas <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Heikki Linnakangas @ 2021-01-13 10:21 UTC (permalink / raw) These are functions where quite a lot of things happen between the ResourceOwnerEnlarge and ResourceOwnerRemember calls. It's important that there are no unrelated ResourceOwnerRemember() calls in the code inbetween, otherwise the entry reserved by the ResourceOwnerEnlarge() call might be used up by the intervening ResourceOwnerRemember() and not be available at the intended ResourceOwnerRemember() call anymore. The longer the code path between them is, the harder it is to verify that. In bufmgr.c, there is a function similar to ResourceOwnerEnlarge(), to ensure that the private refcount array has enough space. The ReservePrivateRefCountEntry() calls, analogous to ResourceOwnerEnlarge(), were made at different places than the ResourceOwnerEnlarge() calls. Move the ResourceOwnerEnlarge() calls together with the ReservePrivateRefCountEntry() calls for consistency. --- src/backend/storage/buffer/bufmgr.c | 39 +++++++++++---------------- src/backend/storage/buffer/localbuf.c | 3 +++ src/backend/utils/cache/catcache.c | 13 ++++++--- 3 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c index 561c212092f..cde869e7d64 100644 --- a/src/backend/storage/buffer/bufmgr.c +++ b/src/backend/storage/buffer/bufmgr.c @@ -738,9 +738,6 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, *hit = false; - /* Make sure we will have room to remember the buffer pin */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - isExtend = (blockNum == P_NEW); TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum, @@ -1093,9 +1090,11 @@ BufferAlloc(SMgrRelation smgr, char relpersistence, ForkNumber forkNum, { /* * Ensure, while the spinlock's not yet held, that there's a free - * refcount entry. + * refcount entry and that the resoure owner has room to remember the + * pin. */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Select a victim buffer. The buffer is returned with its header @@ -1595,8 +1594,6 @@ 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 must have been done already. - * * Returns true if buffer is BM_VALID, else false. This provision allows * some callers to avoid an extra spinlock cycle. */ @@ -1607,6 +1604,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) bool result; PrivateRefCountEntry *ref; + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + ref = GetPrivateRefCountEntry(b, true); if (ref == NULL) @@ -1687,7 +1686,8 @@ PinBuffer(BufferDesc *buf, BufferAccessStrategy strategy) * The spinlock is released before return. * * As this function is called with the spinlock held, the caller has to - * previously call ReservePrivateRefCountEntry(). + * previously call ReservePrivateRefCountEntry() and + * ResourceOwnerEnlargeBuffers(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. @@ -1857,9 +1857,6 @@ BufferSync(int flags) int mask = BM_DIRTY; WritebackContext wb_context; - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - /* * Unless this is a shutdown checkpoint or we have been explicitly told, * we write only permanent, dirty buffers. But at shutdown or end of @@ -2334,9 +2331,6 @@ BgBufferSync(WritebackContext *wb_context) * requirements, or hit the bgwriter_lru_maxpages limit. */ - /* Make sure we can handle the pin inside SyncOneBuffer */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - num_to_scan = bufs_to_lap; num_written = 0; reusable_buffers = reusable_buffers_est; @@ -2418,8 +2412,6 @@ BgBufferSync(WritebackContext *wb_context) * * (BUF_WRITTEN could be set in error if FlushBuffer finds the buffer clean * after locking it, but we don't care all that much.) - * - * Note: caller must have done ResourceOwnerEnlargeBuffers. */ static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) @@ -2429,7 +2421,9 @@ SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context) uint32 buf_state; BufferTag tag; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); /* * Check whether buffer needs writing. @@ -3487,9 +3481,6 @@ FlushRelationBuffers(Relation rel) return; } - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3503,7 +3494,9 @@ FlushRelationBuffers(Relation rel) if (!RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node)) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, rel->rd_node) && @@ -3560,9 +3553,6 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (use_bsearch) pg_qsort(srels, nrels, sizeof(SMgrSortArray), rnode_comparator); - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { SMgrSortArray *srelent = NULL; @@ -3599,7 +3589,9 @@ FlushRelationsAllBuffers(SMgrRelation *smgrs, int nrels) if (srelent == NULL) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (RelFileNodeEquals(bufHdr->tag.rnode, srelent->rnode) && @@ -3639,9 +3631,6 @@ FlushDatabaseBuffers(Oid dbid) int i; BufferDesc *bufHdr; - /* Make sure we can handle the pin inside the loop */ - ResourceOwnerEnlargeBuffers(CurrentResourceOwner); - for (i = 0; i < NBuffers; i++) { uint32 buf_state; @@ -3655,7 +3644,9 @@ FlushDatabaseBuffers(Oid dbid) if (bufHdr->tag.rnode.dbNode != dbid) continue; + /* Make sure we can handle the pin */ ReservePrivateRefCountEntry(); + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); buf_state = LockBufHdr(bufHdr); if (bufHdr->tag.rnode.dbNode == dbid && diff --git a/src/backend/storage/buffer/localbuf.c b/src/backend/storage/buffer/localbuf.c index 04b3558ea33..f7c15ea8a44 100644 --- a/src/backend/storage/buffer/localbuf.c +++ b/src/backend/storage/buffer/localbuf.c @@ -123,6 +123,9 @@ LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum, if (LocalBufHash == NULL) InitLocalBuffers(); + /* Make sure we will have room to remember the buffer pin */ + ResourceOwnerEnlargeBuffers(CurrentResourceOwner); + /* See if the desired buffer already exists */ hresult = (LocalBufferLookupEnt *) hash_search(LocalBufHash, (void *) &newTag, HASH_FIND, NULL); diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 55c94458981..2630c800b05 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1607,8 +1607,6 @@ SearchCatCacheList(CatCache *cache, * block to ensure we can undo those refcounts if we get an error before * we finish constructing the CatCList. */ - ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); - ctlist = NIL; PG_TRY(); @@ -1696,13 +1694,22 @@ SearchCatCacheList(CatCache *cache, table_close(relation, AccessShareLock); + /* Make sure the resource owner has room to remember this entry. */ + ResourceOwnerEnlargeCatCacheListRefs(CurrentResourceOwner); + /* Now we can build the CatCList entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); nmembers = list_length(ctlist); cl = (CatCList *) palloc(offsetof(CatCList, members) + nmembers * sizeof(CatCTup *)); - /* Extract key values */ + /* + * Extract key values. + * + * XXX: If we run out of memory while copying the key values, we will + * leak any allocations we had already made in the CacheMemoryContext. + * That is unlikely enough that we just accept the risk. + */ CatCacheCopyKeys(cache->cc_tupdesc, nkeys, cache->cc_keyno, arguments, cl->keys); MemoryContextSwitchTo(oldcxt); -- 2.30.1 --------------C6A03312D0F99F9183A0DF46 Content-Type: text/x-patch; charset=UTF-8; name="v7-0002-Make-resowners-more-easily-extensible.patch" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="v7-0002-Make-resowners-more-easily-extensible.patch" ^ permalink raw reply [nested|flat] 91+ messages in thread
* Re: Is creating logical replication slots in template databases useful at all? @ 2024-07-10 06:29 Michael Paquier <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Michael Paquier @ 2024-07-10 06:29 UTC (permalink / raw) To: Ashutosh Bapat <[email protected]>; +Cc: Bharath Rupireddy <[email protected]>; PostgreSQL Hackers <[email protected]> On Tue, Jun 18, 2024 at 03:19:41PM +0530, Ashutosh Bapat wrote: > On Mon, Jun 17, 2024 at 5:50 PM Bharath Rupireddy < > [email protected]> wrote: >> https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=29d0a77fa6606f9c01ba17311fc452dab... >> , >> I noticed that get_old_cluster_logical_slot_infos gets called for even >> template1 and template0 databases. Which means, pg_upgrade executes >> queries against the template databases to get replication slot >> information. I then realized that postgres allows one to connect to >> template1 database (or any other user-defined template databases for >> that matter), and create logical replication slots in it. If created, >> all the subsequent database creations will end up adding inactive >> logical replication slots in the postgres server. This might not be a >> problem in production servers as I assume the connections to template >> databases are typically restricted. Despite the connection >> restrictions, if at all one gets to connect to template databases in >> any way, it's pretty much possible to load the postgres server with >> inactive replication slots. > > The replication slot names are unique across databases [1] Hence > replication slots created by connecting to template1 database should not > get copied over when creating a new database. Is that broken? A logical > replication slot is associated with a database but a physical replication > slot is not. The danger you mention above applies only to logical > replication slots I assume. get_old_cluster_logical_slot_infos() on even template0 is still correct, IMO, even if this template database is not something that should be modified at all, or even have allow_connections enabled. It seems to me the correct answer here is that users should not create slots where they are not going to use them. >> This leads me to think why one would need logical replication slots in >> template databases at all. Can postgres restrict logical replication >> slots creation in template databases? If restricted, it may deviate >> from the fundamental principle of template databases in the sense that >> everything in the template database must be copied over to the new >> database created using it. Is it okay to do this? Am I missing >> something here? > > If applications are using template1, they would want to keep the template1 > on primary and replica in sync. Replication slot associated with template1 > would be useful there. Templates defined in CREATE DATABASE can be any active database as long as they are in pg_database, so doing logical replication on template1 to keep it in sync across nodes is fine. In short, I am not quite seeing the problem here. -- Michael Attachments: [application/pgp-signature] signature.asc (833B, ../../[email protected]/2-signature.asc) download ^ permalink raw reply [nested|flat] 91+ messages in thread
* pg_dump: eliminate tmpfile double-write in tar format output @ 2026-04-17 00:47 Josh Kupershmidt <[email protected]> 0 siblings, 0 replies; 91+ messages in thread From: Josh Kupershmidt @ 2026-04-17 00:47 UTC (permalink / raw) To: pgsql-hackers Hi, Please find attached a patch optimizing pg_dump's tar format (-Ft) when writing to a seekable file. The diff here is limited to src/bin/pg_dump/pg_backup_tar.c. Currently, every TOC entry in the tar-format dump goes through a temporary file: data is written to a tmpfile, then on close the tmpfile is seeked to determine its length, the tar header is written, and the entire tmpfile gets copied to the tar output. We end up writing the data twice: once to the tmpfile and once to the final tar file. The patch adds a "direct-write" mode for seekable outputs. Instead of using a tmpfile, we write a placeholder tar header (with length 0) directly to the tar output, stream the data after it, then seek back to rewrite the header with the actual length. This should cut the I/O in half for the data path. The tmpfile path is preserved as a fallback for three cases: 1. Output is not seekable (stdout/pipe) 2. Another member is already being written directly (guard against interleaving) 3. We're in the LO section, where the blob TOC file stays open while individual blob data files are written and closed inside it On a test 500K-row database (~255MB, 184MB dump file), pg_dump -Ft time goes down from about 1.42s (master) to 1.22s (patched). The percent improvement is a bit less for larger databases: dump time goes down from 10.24s (master) to 9.34s (patched) for a database about 10x as large. A benchmark script (bench_tar_direct_write.sh) is included for reproducing some of the performance testing I did. Thanks, Josh Attachments: [application/x-sh] bench_tar_direct_write.sh (2.7K, ../../CAK3UJRE_9-iQsQpYnaZFx6RPL9AUqA2wehAc7fNgiY2yhJPZig@mail.gmail.com/3-bench_tar_direct_write.sh) download [application/octet-stream] pg_backup_tar_mode_direct_write.diff (8.8K, ../../CAK3UJRE_9-iQsQpYnaZFx6RPL9AUqA2wehAc7fNgiY2yhJPZig@mail.gmail.com/4-pg_backup_tar_mode_direct_write.diff) download | inline diff: diff --git a/src/bin/pg_dump/pg_backup_tar.c b/src/bin/pg_dump/pg_backup_tar.c index a3879410c94..7acff95dbc1 100644 --- a/src/bin/pg_dump/pg_backup_tar.c +++ b/src/bin/pg_dump/pg_backup_tar.c @@ -73,6 +73,8 @@ typedef struct pgoff_t pos; pgoff_t fileLen; ArchiveHandle *AH; + pgoff_t headerPos; /* position of tar header in tarFH for + * direct-write mode (seekable output) */ } TAR_MEMBER; typedef struct @@ -86,6 +88,9 @@ typedef struct TAR_MEMBER *FH; int isSpecialScript; TAR_MEMBER *scriptTH; + TAR_MEMBER *directWriteMember; /* currently active direct-write member, + * or NULL */ + bool inLOs; /* true while writing large objects */ } lclContext; typedef struct @@ -331,61 +336,101 @@ tarOpen(ArchiveHandle *AH, const char *filename, char mode) } else { - int old_umask; - tm = pg_malloc0_object(TAR_MEMBER); /* - * POSIX does not require, but permits, tmpfile() to restrict file - * permissions. Given an OS crash after we write data, the filesystem - * might retain the data but forget tmpfile()'s unlink(). If so, the - * file mode protects confidentiality of the data written. + * When the tar output is seekable and no other member is currently + * being written directly, we can write data directly to the tar + * file instead of using a temporary file. We write a placeholder + * header first, stream the data, then seek back to fix the header + * with the actual file length. This avoids the overhead of writing + * all data twice (once to tmpfile, once to tar output). + * + * We cannot use this optimization when another direct-write member + * is already active (e.g., the blob TOC file is open while + * individual blob data files are being written). The inLOs check + * is needed beyond directWriteMember==NULL because the loToc member + * stays open and receives interleaved tarPrintf writes while blob + * data files are being finalized, which would corrupt the archive + * if loToc were in direct-write mode. + * + * Note: tarFHpos is not kept up-to-date during direct writes + * (tarWrite updates th->pos but not ctx->tarFHpos). We fix it + * in _tarAddFile using ftello() after the member is complete. */ - old_umask = umask(S_IRWXG | S_IRWXO); + if (ctx->hasSeek && ctx->directWriteMember == NULL && + !ctx->inLOs) + { + tm->tmpFH = NULL; + tm->nFH = ctx->tarFH; /* tarWrite() writes data here */ + tm->headerPos = ctx->tarFHpos; + + /* Write a placeholder header (fileLen=0, will be rewritten) */ + tm->targetFile = pg_strdup(filename); + tm->fileLen = 0; + tm->tarFH = ctx->tarFH; /* _tarWriteHeader() writes here */ + _tarWriteHeader(tm); + ctx->tarFHpos += TAR_BLOCK_SIZE; + ctx->directWriteMember = tm; + } + else + { + int old_umask; + + /* + * POSIX does not require, but permits, tmpfile() to restrict + * file permissions. Given an OS crash after we write data, + * the filesystem might retain the data but forget tmpfile()'s + * unlink(). If so, the file mode protects confidentiality of + * the data written. + */ + old_umask = umask(S_IRWXG | S_IRWXO); #ifndef WIN32 - tm->tmpFH = tmpfile(); + tm->tmpFH = tmpfile(); #else - /* - * On WIN32, tmpfile() generates a filename in the root directory, - * which requires administrative permissions on certain systems. Loop - * until we find a unique file name we can create. - */ - while (1) - { - char *name; - int fd; + /* + * On WIN32, tmpfile() generates a filename in the root + * directory, which requires administrative permissions on + * certain systems. Loop until we find a unique file name we + * can create. + */ + while (1) + { + char *name; + int fd; - name = _tempnam(NULL, "pg_temp_"); - if (name == NULL) - break; - fd = open(name, O_RDWR | O_CREAT | O_EXCL | O_BINARY | - O_TEMPORARY, S_IRUSR | S_IWUSR); - free(name); + name = _tempnam(NULL, "pg_temp_"); + if (name == NULL) + break; + fd = open(name, O_RDWR | O_CREAT | O_EXCL | O_BINARY | + O_TEMPORARY, S_IRUSR | S_IWUSR); + free(name); - if (fd != -1) /* created a file */ - { - tm->tmpFH = fdopen(fd, "w+b"); - break; + if (fd != -1) /* created a file */ + { + tm->tmpFH = fdopen(fd, "w+b"); + break; + } + else if (errno != EEXIST) + break; } - else if (errno != EEXIST) /* failure other than file exists */ - break; - } #endif - if (tm->tmpFH == NULL) - pg_fatal("could not generate temporary file name: %m"); + if (tm->tmpFH == NULL) + pg_fatal("could not generate temporary file name: %m"); - umask(old_umask); + umask(old_umask); - if (AH->compression_spec.algorithm == PG_COMPRESSION_NONE) tm->nFH = tm->tmpFH; - else + tm->targetFile = pg_strdup(filename); + } + + if (AH->compression_spec.algorithm != PG_COMPRESSION_NONE) pg_fatal("compression is not supported by tar archive format"); tm->AH = AH; - tm->targetFile = pg_strdup(filename); } tm->mode = mode; @@ -882,6 +927,7 @@ _StartLOs(ArchiveHandle *AH, TocEntry *te) char fname[K_STD_BUF_SIZE]; sprintf(fname, "blobs_%d.toc", te->dumpId); + ctx->inLOs = true; ctx->loToc = tarOpen(AH, fname, 'w'); } @@ -941,6 +987,7 @@ _EndLOs(ArchiveHandle *AH, TocEntry *te) /* WriteInt(AH, 0); */ tarClose(AH, ctx->loToc); + ctx->inLOs = false; } @@ -989,51 +1036,95 @@ static void _tarAddFile(ArchiveHandle *AH, TAR_MEMBER *th) { lclContext *ctx = (lclContext *) AH->formatData; - FILE *tmp = th->tmpFH; /* Grab it for convenience */ - char buf[32768]; - size_t cnt; - pgoff_t len = 0; - size_t res; size_t i, pad; - /* - * Find file len & go back to start. - */ - if (fseeko(tmp, 0, SEEK_END) != 0) - pg_fatal("error during file seek: %m"); - th->fileLen = ftello(tmp); - if (th->fileLen < 0) - pg_fatal("could not determine seek position in archive file: %m"); - if (fseeko(tmp, 0, SEEK_SET) != 0) - pg_fatal("error during file seek: %m"); - - _tarWriteHeader(th); - - while ((cnt = fread(buf, 1, sizeof(buf), tmp)) > 0) + if (th->tmpFH == NULL) { - if ((res = fwrite(buf, 1, cnt, th->tarFH)) != cnt) + /* + * Direct-write mode: data was written directly to tarFH after a + * placeholder header. Seek back to rewrite the header with the + * actual file length, then seek to the end and write padding. + */ + pgoff_t endPos; + + /* Flush any buffered data so ftello returns the right position */ + if (fflush(th->tarFH) != 0) WRITE_ERROR_EXIT; - len += res; - } - if (!feof(tmp)) - READ_ERROR_EXIT(tmp); - if (fclose(tmp) != 0) /* This *should* delete it... */ - pg_fatal("could not close temporary file: %m"); + endPos = ftello(th->tarFH); + if (endPos < 0) + pg_fatal("could not determine seek position in archive file: %m"); - if (len != th->fileLen) - pg_fatal("actual file length (%lld) does not match expected (%lld)", - (long long) len, (long long) th->fileLen); + th->fileLen = endPos - th->headerPos - TAR_BLOCK_SIZE; - pad = tarPaddingBytesRequired(len); - for (i = 0; i < pad; i++) - { - if (fputc('\0', th->tarFH) == EOF) - WRITE_ERROR_EXIT; + /* Seek back to rewrite the header with the correct length */ + if (fseeko(th->tarFH, th->headerPos, SEEK_SET) != 0) + pg_fatal("error during file seek: %m"); + + _tarWriteHeader(th); + + /* Seek back to end of data */ + if (fseeko(th->tarFH, endPos, SEEK_SET) != 0) + pg_fatal("error during file seek: %m"); + + /* Write tar padding */ + pad = tarPaddingBytesRequired(th->fileLen); + for (i = 0; i < pad; i++) + { + if (fputc('\0', th->tarFH) == EOF) + WRITE_ERROR_EXIT; + } + + ctx->tarFHpos = endPos + pad; + ctx->directWriteMember = NULL; } + else + { + /* + * Tmpfile mode: copy data from tmpfile to tarFH (original path). + */ + FILE *tmp = th->tmpFH; + char buf[32768]; + size_t cnt; + pgoff_t len = 0; + size_t res; + + if (fseeko(tmp, 0, SEEK_END) != 0) + pg_fatal("error during file seek: %m"); + th->fileLen = ftello(tmp); + if (th->fileLen < 0) + pg_fatal("could not determine seek position in archive file: %m"); + if (fseeko(tmp, 0, SEEK_SET) != 0) + pg_fatal("error during file seek: %m"); + + _tarWriteHeader(th); + + while ((cnt = fread(buf, 1, sizeof(buf), tmp)) > 0) + { + if ((res = fwrite(buf, 1, cnt, th->tarFH)) != cnt) + WRITE_ERROR_EXIT; + len += res; + } + if (!feof(tmp)) + READ_ERROR_EXIT(tmp); - ctx->tarFHpos += len + pad; + if (fclose(tmp) != 0) + pg_fatal("could not close temporary file: %m"); + + if (len != th->fileLen) + pg_fatal("actual file length (%lld) does not match expected (%lld)", + (long long) len, (long long) th->fileLen); + + pad = tarPaddingBytesRequired(len); + for (i = 0; i < pad; i++) + { + if (fputc('\0', th->tarFH) == EOF) + WRITE_ERROR_EXIT; + } + + ctx->tarFHpos += len + pad; + } } /* Locate the file in the archive, read header and position to data */ ^ permalink raw reply [nested|flat] 91+ messages in thread
end of thread, other threads:[~2026-04-17 00:47 UTC | newest] Thread overview: 91+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v8 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v10 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v6 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v7 1/3] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v4 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2021-01-13 10:21 [PATCH v5 1/2] Move a few ResourceOwnerEnlarge() calls for safety and clarity. Heikki Linnakangas <[email protected]> 2024-07-10 06:29 Re: Is creating logical replication slots in template databases useful at all? Michael Paquier <[email protected]> 2026-04-17 00:47 pg_dump: eliminate tmpfile double-write in tar format output Josh Kupershmidt <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox