Content-Length: 340988 | pFad | http://github.com/postgres/postgres/commit/#start-of-content

6897CAAB Fix rare bug in read_stream.c's split IO handling. · postgres/postgres@b421223 · GitHub
Skip to content

Commit b421223

Browse files
committed
Fix rare bug in read_stream.c's split IO handling.
The internal queue of buffers could become corrupted in a rare edge case that failed to invalidate an entry, causing a stale buffer to be "forwarded" to StartReadBuffers(). This is a simple fix for the immediate problem. A small API change might be able to remove this and related fragility entirely, but that will have to wait a bit. Defect in commit ed0b87c. Bug: 19006 Backpatch-through: 18 Reported-by: Alexander Lakhin <exclusion@gmail.com> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Reviewed-by: Michael Paquier <michael@paquier.xyz> Reviewed-by: Xuneng Zhou <xunengzhou@gmail.com> Discussion: https://postgr.es/m/19006-80fcaaf69000377e%40postgresql.org
1 parent 665c3db commit b421223

File tree

1 file changed

+34
-0
lines changed

1 file changed

+34
-0
lines changed

src/backend/storage/aio/read_stream.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,12 +247,33 @@ read_stream_start_pending_read(ReadStream *stream)
247247
Assert(stream->pinned_buffers + stream->pending_read_nblocks <=
248248
stream->max_pinned_buffers);
249249

250+
#ifdef USE_ASSERT_CHECKING
250251
/* We had better not be overwriting an existing pinned buffer. */
251252
if (stream->pinned_buffers > 0)
252253
Assert(stream->next_buffer_index != stream->oldest_buffer_index);
253254
else
254255
Assert(stream->next_buffer_index == stream->oldest_buffer_index);
255256

257+
/*
258+
* Pinned buffers forwarded by a preceding StartReadBuffers() call that
259+
* had to split the operation should match the leading blocks of this
260+
* following StartReadBuffers() call.
261+
*/
262+
Assert(stream->forwarded_buffers <= stream->pending_read_nblocks);
263+
for (int i = 0; i < stream->forwarded_buffers; ++i)
264+
Assert(BufferGetBlockNumber(stream->buffers[stream->next_buffer_index + i]) ==
265+
stream->pending_read_blocknum + i);
266+
267+
/*
268+
* Check that we've cleared the queue/overflow entries corresponding to
269+
* the rest of the blocks covered by this read, unless it's the first go
270+
* around and we haven't even initialized them yet.
271+
*/
272+
for (int i = stream->forwarded_buffers; i < stream->pending_read_nblocks; ++i)
273+
Assert(stream->next_buffer_index + i >= stream->initialized_buffers ||
274+
stream->buffers[stream->next_buffer_index + i] == InvalidBuffer);
275+
#endif
276+
256277
/* Do we need to issue read-ahead advice? */
257278
flags = stream->read_buffers_flags;
258279
if (stream->advice_enabled)
@@ -979,6 +1000,19 @@ read_stream_next_buffer(ReadStream *stream, void **per_buffer_data)
9791000
stream->pending_read_nblocks == 0 &&
9801001
stream->per_buffer_data_size == 0)
9811002
{
1003+
/*
1004+
* The fast path spins on one buffer entry repeatedly instead of
1005+
* rotating through the whole queue and clearing the entries behind
1006+
* it. If the buffer it starts with happened to be forwarded between
1007+
* StartReadBuffers() calls and also wrapped around the circular queue
1008+
* partway through, then a copy also exists in the overflow zone, and
1009+
* it won't clear it out as the regular path would. Do that now, so
1010+
* it doesn't need code for that.
1011+
*/
1012+
if (stream->oldest_buffer_index < stream->io_combine_limit - 1)
1013+
stream->buffers[stream->queue_size + stream->oldest_buffer_index] =
1014+
InvalidBuffer;
1015+
9821016
stream->fast_path = true;
9831017
}
9841018
#endif

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/postgres/postgres/commit/#start-of-content

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy