Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * test_aio.c
4 : : * Helpers to write tests for AIO
5 : : *
6 : : * This module provides interface functions for C functionality to SQL, to
7 : : * make it possible to test AIO related behavior in a targeted way from SQL.
8 : : * It'd not generally be safe to export these functions to SQL, but for a test
9 : : * that's fine.
10 : : *
11 : : * Copyright (c) 2020-2026, PostgreSQL Global Development Group
12 : : *
13 : : * IDENTIFICATION
14 : : * src/test/modules/test_aio/test_aio.c
15 : : *
16 : : *-------------------------------------------------------------------------
17 : : */
18 : :
19 : : #include "postgres.h"
20 : :
21 : : #include "access/relation.h"
22 : : #include "catalog/pg_type.h"
23 : : #include "fmgr.h"
24 : : #include "funcapi.h"
25 : : #include "storage/aio.h"
26 : : #include "storage/aio_internal.h"
27 : : #include "storage/buf_internals.h"
28 : : #include "storage/bufmgr.h"
29 : : #include "storage/checksum.h"
30 : : #include "storage/condition_variable.h"
31 : : #include "storage/lwlock.h"
32 : : #include "storage/proc.h"
33 : : #include "storage/procnumber.h"
34 : : #include "storage/read_stream.h"
35 : : #include "utils/array.h"
36 : : #include "utils/builtins.h"
37 : : #include "utils/injection_point.h"
38 : : #include "utils/rel.h"
39 : : #include "utils/tuplestore.h"
40 : : #include "utils/wait_event.h"
41 : :
42 : :
399 andres@anarazel.de 43 :CBC 10 : PG_MODULE_MAGIC;
44 : :
45 : :
46 : : /* In shared memory */
47 : : typedef struct InjIoErrorState
48 : : {
49 : : ConditionVariable cv;
50 : :
51 : : bool enabled_short_read;
52 : : bool enabled_reopen;
53 : :
54 : : bool enabled_completion_wait;
55 : : Oid completion_wait_relfilenode;
56 : : BlockNumber completion_wait_blockno;
57 : : pid_t completion_wait_pid;
58 : : uint32 completion_wait_event;
59 : :
60 : : bool short_read_result_set;
61 : : Oid short_read_relfilenode;
62 : : pid_t short_read_pid;
63 : : int short_read_result;
64 : : } InjIoErrorState;
65 : :
66 : : typedef struct BlocksReadStreamData
67 : : {
68 : : int nblocks;
69 : : int curblock;
70 : : uint32 *blocks;
71 : : } BlocksReadStreamData;
72 : :
73 : :
74 : : static InjIoErrorState *inj_io_error_state;
75 : :
76 : : /* Shared memory init callbacks */
77 : : static void test_aio_shmem_request(void *arg);
78 : : static void test_aio_shmem_init(void *arg);
79 : : static void test_aio_shmem_attach(void *arg);
80 : :
81 : : static const ShmemCallbacks inj_io_shmem_callbacks = {
82 : : .request_fn = test_aio_shmem_request,
83 : : .init_fn = test_aio_shmem_init,
84 : : .attach_fn = test_aio_shmem_attach,
85 : : };
86 : :
87 : :
88 : : static PgAioHandle *last_handle;
89 : :
90 : :
91 : :
92 : : static void
29 heikki.linnakangas@i 93 :GNC 10 : test_aio_shmem_request(void *arg)
94 : : {
95 : 10 : ShmemRequestStruct(.name = "test_aio injection points",
96 : : .size = sizeof(InjIoErrorState),
97 : : .ptr = (void **) &inj_io_error_state,
98 : : );
399 andres@anarazel.de 99 :CBC 10 : }
100 : :
101 : : static void
29 heikki.linnakangas@i 102 :GNC 10 : test_aio_shmem_init(void *arg)
103 : : {
104 : : /* First time through, initialize */
105 : 10 : inj_io_error_state->enabled_short_read = false;
106 : 10 : inj_io_error_state->enabled_reopen = false;
107 : 10 : inj_io_error_state->enabled_completion_wait = false;
108 : :
109 : 10 : ConditionVariableInit(&inj_io_error_state->cv);
110 : 10 : inj_io_error_state->completion_wait_event = WaitEventInjectionPointNew("completion_wait");
111 : :
112 : : #ifdef USE_INJECTION_POINTS
113 : 10 : InjectionPointAttach("aio-process-completion-before-shared",
114 : : "test_aio",
115 : : "inj_io_completion_hook",
116 : : NULL,
117 : : 0);
118 : 10 : InjectionPointLoad("aio-process-completion-before-shared");
119 : :
120 : 10 : InjectionPointAttach("aio-worker-after-reopen",
121 : : "test_aio",
122 : : "inj_io_reopen",
123 : : NULL,
124 : : 0);
125 : 10 : InjectionPointLoad("aio-worker-after-reopen");
126 : :
127 : : #endif
128 : 10 : }
129 : :
130 : : static void
29 heikki.linnakangas@i 131 :UNC 0 : test_aio_shmem_attach(void *arg)
132 : : {
133 : : /*
134 : : * Pre-load the injection points now, so we can call them in a critical
135 : : * section.
136 : : */
137 : : #ifdef USE_INJECTION_POINTS
138 : 0 : InjectionPointLoad("aio-process-completion-before-shared");
139 : 0 : InjectionPointLoad("aio-worker-after-reopen");
140 [ # # ]: 0 : elog(LOG, "injection point loaded");
141 : : #endif
399 andres@anarazel.de 142 :LBC (3) : }
143 : :
144 : : void
399 andres@anarazel.de 145 :CBC 10 : _PG_init(void)
146 : : {
147 [ - + ]: 10 : if (!process_shared_preload_libraries_in_progress)
399 andres@anarazel.de 148 :UBC 0 : return;
149 : :
29 heikki.linnakangas@i 150 :GNC 10 : RegisterShmemCallbacks(&inj_io_shmem_callbacks);
151 : : }
152 : :
153 : :
399 andres@anarazel.de 154 :CBC 13 : PG_FUNCTION_INFO_V1(errno_from_string);
155 : : Datum
156 : 9 : errno_from_string(PG_FUNCTION_ARGS)
157 : : {
158 : 9 : const char *sym = text_to_cstring(PG_GETARG_TEXT_PP(0));
159 : :
160 [ + + ]: 9 : if (strcmp(sym, "EIO") == 0)
161 : 6 : PG_RETURN_INT32(EIO);
162 [ - + ]: 3 : else if (strcmp(sym, "EAGAIN") == 0)
399 andres@anarazel.de 163 :UBC 0 : PG_RETURN_INT32(EAGAIN);
399 andres@anarazel.de 164 [ - + ]:CBC 3 : else if (strcmp(sym, "EINTR") == 0)
399 andres@anarazel.de 165 :UBC 0 : PG_RETURN_INT32(EINTR);
399 andres@anarazel.de 166 [ - + ]:CBC 3 : else if (strcmp(sym, "ENOSPC") == 0)
399 andres@anarazel.de 167 :UBC 0 : PG_RETURN_INT32(ENOSPC);
399 andres@anarazel.de 168 [ + - ]:CBC 3 : else if (strcmp(sym, "EROFS") == 0)
169 : 3 : PG_RETURN_INT32(EROFS);
170 : :
399 andres@anarazel.de 171 [ # # ]:UBC 0 : ereport(ERROR,
172 : : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
173 : : errmsg_internal("%s is not a supported errno value", sym));
174 : : PG_RETURN_INT32(0);
175 : : }
176 : :
399 andres@anarazel.de 177 :CBC 13 : PG_FUNCTION_INFO_V1(grow_rel);
178 : : Datum
179 : 9 : grow_rel(PG_FUNCTION_ARGS)
180 : : {
181 : 9 : Oid relid = PG_GETARG_OID(0);
182 : 9 : uint32 nblocks = PG_GETARG_UINT32(1);
183 : : Relation rel;
184 : : #define MAX_BUFFERS_TO_EXTEND_BY 64
185 : : Buffer victim_buffers[MAX_BUFFERS_TO_EXTEND_BY];
186 : :
187 : 9 : rel = relation_open(relid, AccessExclusiveLock);
188 : :
189 [ + + ]: 18 : while (nblocks > 0)
190 : : {
191 : : uint32 extend_by_pages;
192 : :
193 : 9 : extend_by_pages = Min(nblocks, MAX_BUFFERS_TO_EXTEND_BY);
194 : :
195 : 9 : ExtendBufferedRelBy(BMR_REL(rel),
196 : : MAIN_FORKNUM,
197 : : NULL,
198 : : 0,
199 : : extend_by_pages,
200 : : victim_buffers,
201 : : &extend_by_pages);
202 : :
203 : 9 : nblocks -= extend_by_pages;
204 : :
205 [ + + ]: 117 : for (uint32 i = 0; i < extend_by_pages; i++)
206 : : {
207 : 108 : ReleaseBuffer(victim_buffers[i]);
208 : : }
209 : : }
210 : :
211 : 9 : relation_close(rel, NoLock);
212 : :
213 : 9 : PG_RETURN_VOID();
214 : : }
215 : :
216 : 28 : PG_FUNCTION_INFO_V1(modify_rel_block);
217 : : Datum
218 : 105 : modify_rel_block(PG_FUNCTION_ARGS)
219 : : {
220 : 105 : Oid relid = PG_GETARG_OID(0);
221 : 105 : BlockNumber blkno = PG_GETARG_UINT32(1);
222 : 105 : bool zero = PG_GETARG_BOOL(2);
223 : 105 : bool corrupt_header = PG_GETARG_BOOL(3);
224 : 105 : bool corrupt_checksum = PG_GETARG_BOOL(4);
225 : 105 : Page page = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
226 : : bool flushed;
227 : : Relation rel;
228 : : Buffer buf;
229 : : PageHeader ph;
230 : :
231 : 105 : rel = relation_open(relid, AccessExclusiveLock);
232 : :
233 : 105 : buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno,
234 : : RBM_ZERO_ON_ERROR, NULL);
235 : :
236 : 105 : LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
237 : :
238 : : /*
239 : : * copy the page to local memory, seems nicer than to directly modify in
240 : : * the buffer pool.
241 : : */
242 : 105 : memcpy(page, BufferGetPage(buf), BLCKSZ);
243 : :
39 andres@anarazel.de 244 :GNC 105 : UnlockReleaseBuffer(buf);
245 : :
246 : : /*
247 : : * Don't want to have a buffer in-memory that's marked valid where the
248 : : * on-disk contents are invalid. Particularly not if the in-memory buffer
249 : : * could be dirty...
250 : : *
251 : : * While we hold an AEL on the relation nobody else should be able to read
252 : : * the buffer in.
253 : : *
254 : : * NB: This is probably racy, better don't copy this to non-test code.
255 : : */
399 andres@anarazel.de 256 [ + + ]:CBC 105 : if (BufferIsLocal(buf))
257 : 27 : InvalidateLocalBuffer(GetLocalBufferDescriptor(-buf - 1), true);
258 : : else
392 259 : 78 : EvictUnpinnedBuffer(buf, &flushed);
260 : :
261 : : /*
262 : : * Now modify the page as asked for by the caller.
263 : : */
399 264 [ + + ]: 105 : if (zero)
265 : 24 : memset(page, 0, BufferGetPageSize(buf));
266 : :
267 [ + + + + : 105 : if (PageIsEmpty(page) && (corrupt_header || corrupt_checksum))
+ + ]
268 : 24 : PageInit(page, BufferGetPageSize(buf), 0);
269 : :
270 : 105 : ph = (PageHeader) page;
271 : :
272 [ + + ]: 105 : if (corrupt_header)
273 : 48 : ph->pd_special = BLCKSZ + 1;
274 : :
275 [ + + ]: 105 : if (corrupt_checksum)
276 : : {
277 : 39 : bool successfully_corrupted = 0;
278 : :
279 : : /*
280 : : * Any single modification of the checksum could just end up being
281 : : * valid again, due to e.g. corrupt_header changing the data in a way
282 : : * that'd result in the "corrupted" checksum, or the checksum already
283 : : * being invalid. Retry in that, unlikely, case.
284 : : */
285 [ + - ]: 39 : for (int i = 0; i < 100; i++)
286 : : {
287 : : uint16 verify_checksum;
288 : : uint16 old_checksum;
289 : :
290 : 39 : old_checksum = ph->pd_checksum;
291 : 39 : ph->pd_checksum = old_checksum + 1;
292 : :
293 [ + - ]: 39 : elog(LOG, "corrupting checksum of blk %u from %u to %u",
294 : : blkno, old_checksum, ph->pd_checksum);
295 : :
296 : 39 : verify_checksum = pg_checksum_page(page, blkno);
297 [ + - ]: 39 : if (verify_checksum != ph->pd_checksum)
298 : : {
299 : 39 : successfully_corrupted = true;
300 : 39 : break;
301 : : }
302 : : }
303 : :
304 [ - + ]: 39 : if (!successfully_corrupted)
399 andres@anarazel.de 305 [ # # ]:UBC 0 : elog(ERROR, "could not corrupt checksum, what's going on?");
306 : : }
307 : : else
308 : : {
39 andres@anarazel.de 309 :GNC 66 : PageSetChecksum(page, blkno);
310 : : }
311 : :
399 andres@anarazel.de 312 :CBC 105 : smgrwrite(RelationGetSmgr(rel),
313 : : MAIN_FORKNUM, blkno, page, true);
314 : :
315 : 105 : relation_close(rel, NoLock);
316 : :
317 : 105 : PG_RETURN_VOID();
318 : : }
319 : :
320 : : /*
321 : : * Ensures a buffer for rel & blkno is in shared buffers, without actually
322 : : * caring about the buffer contents. Used to set up test scenarios.
323 : : */
324 : : static Buffer
325 : 241 : create_toy_buffer(Relation rel, BlockNumber blkno)
326 : : {
327 : : Buffer buf;
328 : : BufferDesc *buf_hdr;
329 : : uint64 buf_state;
330 : 241 : bool was_pinned = false;
110 andres@anarazel.de 331 :GNC 241 : uint64 unset_bits = 0;
332 : :
333 : : /* place buffer in shared buffers without erroring out */
399 andres@anarazel.de 334 :CBC 241 : buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_ZERO_AND_LOCK, NULL);
335 : 241 : LockBuffer(buf, BUFFER_LOCK_UNLOCK);
336 : :
337 [ + + ]: 241 : if (RelationUsesLocalBuffers(rel))
338 : : {
339 : 75 : buf_hdr = GetLocalBufferDescriptor(-buf - 1);
110 andres@anarazel.de 340 :GNC 75 : buf_state = pg_atomic_read_u64(&buf_hdr->state);
341 : : }
342 : : else
343 : : {
399 andres@anarazel.de 344 :CBC 166 : buf_hdr = GetBufferDescriptor(buf - 1);
345 : 166 : buf_state = LockBufHdr(buf_hdr);
346 : : }
347 : :
348 : : /*
349 : : * We should be the only backend accessing this buffer. This is just a
350 : : * small bit of belt-and-suspenders defense, none of this code should ever
351 : : * run in a cluster with real data.
352 : : */
353 [ - + ]: 241 : if (BUF_STATE_GET_REFCOUNT(buf_state) > 1)
399 andres@anarazel.de 354 :UBC 0 : was_pinned = true;
355 : : else
180 andres@anarazel.de 356 :GNC 241 : unset_bits |= BM_VALID | BM_DIRTY;
357 : :
399 andres@anarazel.de 358 [ + + ]:CBC 241 : if (RelationUsesLocalBuffers(rel))
359 : : {
180 andres@anarazel.de 360 :GNC 75 : buf_state &= ~unset_bits;
110 361 : 75 : pg_atomic_unlocked_write_u64(&buf_hdr->state, buf_state);
362 : : }
363 : : else
364 : : {
180 365 : 166 : UnlockBufHdrExt(buf_hdr, buf_state, 0, unset_bits, 0);
366 : : }
367 : :
399 andres@anarazel.de 368 [ - + ]:CBC 241 : if (was_pinned)
399 andres@anarazel.de 369 [ # # ]:UBC 0 : elog(ERROR, "toy buffer %d was already pinned",
370 : : buf);
371 : :
399 andres@anarazel.de 372 :CBC 241 : return buf;
373 : : }
374 : :
375 : : /*
376 : : * A "low level" read. This does similar things to what
377 : : * StartReadBuffers()/WaitReadBuffers() do, but provides more control (and
378 : : * less sanity).
379 : : */
380 : 40 : PG_FUNCTION_INFO_V1(read_rel_block_ll);
381 : : Datum
382 : 141 : read_rel_block_ll(PG_FUNCTION_ARGS)
383 : : {
384 : 141 : Oid relid = PG_GETARG_OID(0);
385 : 141 : BlockNumber blkno = PG_GETARG_UINT32(1);
386 : 141 : int nblocks = PG_GETARG_INT32(2);
387 : 141 : bool wait_complete = PG_GETARG_BOOL(3);
388 : 141 : bool batchmode_enter = PG_GETARG_BOOL(4);
389 : 141 : bool call_smgrreleaseall = PG_GETARG_BOOL(5);
390 : 141 : bool batchmode_exit = PG_GETARG_BOOL(6);
391 : 141 : bool zero_on_error = PG_GETARG_BOOL(7);
392 : : Relation rel;
393 : : Buffer bufs[PG_IOV_MAX];
394 : : BufferDesc *buf_hdrs[PG_IOV_MAX];
395 : : Page pages[PG_IOV_MAX];
396 : 141 : uint8 srb_flags = 0;
397 : : PgAioReturn ior;
398 : : PgAioHandle *ioh;
399 : : PgAioWaitRef iow;
400 : : SMgrRelation smgr;
401 : :
402 [ + - - + ]: 141 : if (nblocks <= 0 || nblocks > PG_IOV_MAX)
399 andres@anarazel.de 403 [ # # ]:UBC 0 : elog(ERROR, "nblocks is out of range");
404 : :
39 andres@anarazel.de 405 :GNC 141 : rel = relation_open(relid, AccessShareLock);
406 : :
399 andres@anarazel.de 407 [ + + ]:CBC 369 : for (int i = 0; i < nblocks; i++)
408 : : {
409 : 228 : bufs[i] = create_toy_buffer(rel, blkno + i);
410 : 228 : pages[i] = BufferGetBlock(bufs[i]);
411 : 228 : buf_hdrs[i] = BufferIsLocal(bufs[i]) ?
412 [ + + ]: 228 : GetLocalBufferDescriptor(-bufs[i] - 1) :
413 : 156 : GetBufferDescriptor(bufs[i] - 1);
414 : : }
415 : :
416 : 141 : smgr = RelationGetSmgr(rel);
417 : :
418 : 141 : pgstat_prepare_report_checksum_failure(smgr->smgr_rlocator.locator.dbOid);
419 : :
420 : 141 : ioh = pgaio_io_acquire(CurrentResourceOwner, &ior);
421 : 141 : pgaio_io_get_wref(ioh, &iow);
422 : :
423 [ + + ]: 141 : if (RelationUsesLocalBuffers(rel))
424 : : {
425 [ + + ]: 114 : for (int i = 0; i < nblocks; i++)
39 andres@anarazel.de 426 :GNC 72 : StartLocalBufferIO(buf_hdrs[i], true, true, NULL);
399 andres@anarazel.de 427 :CBC 42 : pgaio_io_set_flag(ioh, PGAIO_HF_REFERENCES_LOCAL);
428 : : }
429 : : else
430 : : {
431 [ + + ]: 255 : for (int i = 0; i < nblocks; i++)
39 andres@anarazel.de 432 :GNC 156 : StartSharedBufferIO(buf_hdrs[i], true, true, NULL);
433 : : }
434 : :
399 andres@anarazel.de 435 :CBC 141 : pgaio_io_set_handle_data_32(ioh, (uint32 *) bufs, nblocks);
436 : :
437 [ + + ]: 141 : if (zero_on_error | zero_damaged_pages)
438 : 33 : srb_flags |= READ_BUFFERS_ZERO_ON_ERROR;
439 [ + + ]: 141 : if (ignore_checksum_failure)
440 : 15 : srb_flags |= READ_BUFFERS_IGNORE_CHECKSUM_FAILURES;
441 : :
442 : 141 : pgaio_io_register_callbacks(ioh,
443 [ + + ]: 141 : RelationUsesLocalBuffers(rel) ?
444 : : PGAIO_HCB_LOCAL_BUFFER_READV :
445 : : PGAIO_HCB_SHARED_BUFFER_READV,
446 : : srb_flags);
447 : :
448 [ + + ]: 141 : if (batchmode_enter)
449 : 6 : pgaio_enter_batchmode();
450 : :
451 : 141 : smgrstartreadv(ioh, smgr, MAIN_FORKNUM, blkno,
452 : : (void *) pages, nblocks);
453 : :
454 [ + + ]: 141 : if (call_smgrreleaseall)
455 : 6 : smgrreleaseall();
456 : :
457 [ + + ]: 141 : if (batchmode_exit)
458 : 6 : pgaio_exit_batchmode();
459 : :
460 [ + + ]: 369 : for (int i = 0; i < nblocks; i++)
461 : 228 : ReleaseBuffer(bufs[i]);
462 : :
463 [ + + ]: 141 : if (wait_complete)
464 : : {
465 : 90 : pgaio_wref_wait(&iow);
466 : :
467 [ + + ]: 90 : if (ior.result.status != PGAIO_RS_OK)
468 : 69 : pgaio_result_report(ior.result,
469 : : &ior.target_data,
470 [ + + ]: 69 : ior.result.status == PGAIO_RS_ERROR ?
471 : : ERROR : WARNING);
472 : : }
473 : :
474 : 105 : relation_close(rel, NoLock);
475 : :
476 : 105 : PG_RETURN_VOID();
477 : : }
478 : :
479 : : /* helper for invalidate_rel_block() and evict_rel() */
480 : : static void
39 andres@anarazel.de 481 :GNC 703 : invalidate_one_block(Relation rel, ForkNumber forknum, BlockNumber blkno)
482 : : {
483 : : PrefetchBufferResult pr;
484 : : Buffer buf;
485 : :
486 : : /*
487 : : * This is a gross hack, but there's no other API exposed that allows to
488 : : * get a buffer ID without actually reading the block in.
489 : : */
490 : 703 : pr = PrefetchBuffer(rel, forknum, blkno);
399 andres@anarazel.de 491 :CBC 703 : buf = pr.recent_buffer;
492 : :
493 [ + + ]: 703 : if (BufferIsValid(buf))
494 : : {
495 : : /* if the buffer contents aren't valid, this'll return false */
39 andres@anarazel.de 496 [ + + ]:GNC 345 : if (ReadRecentBuffer(rel->rd_locator, forknum, blkno, buf))
497 : : {
399 andres@anarazel.de 498 :CBC 336 : BufferDesc *buf_hdr = BufferIsLocal(buf) ?
499 : 210 : GetLocalBufferDescriptor(-buf - 1)
500 [ + + ]: 336 : : GetBufferDescriptor(buf - 1);
501 : : bool flushed;
502 : :
503 : 336 : LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
504 : :
110 andres@anarazel.de 505 [ + + ]:GNC 336 : if (pg_atomic_read_u64(&buf_hdr->state) & BM_DIRTY)
506 : : {
399 andres@anarazel.de 507 [ + + ]:CBC 186 : if (BufferIsLocal(buf))
508 : 111 : FlushLocalBuffer(buf_hdr, NULL);
509 : : else
510 : 75 : FlushOneBuffer(buf);
511 : : }
39 andres@anarazel.de 512 :GNC 336 : UnlockReleaseBuffer(buf);
513 : :
399 andres@anarazel.de 514 [ + + ]:CBC 336 : if (BufferIsLocal(buf))
515 : 210 : InvalidateLocalBuffer(GetLocalBufferDescriptor(-buf - 1), true);
392 516 [ - + ]: 126 : else if (!EvictUnpinnedBuffer(buf, &flushed))
399 andres@anarazel.de 517 [ # # ]:UBC 0 : elog(ERROR, "couldn't evict");
518 : : }
519 : : }
520 : :
39 andres@anarazel.de 521 :GNC 703 : }
522 : :
523 : 23 : PG_FUNCTION_INFO_V1(invalidate_rel_block);
524 : : Datum
525 : 235 : invalidate_rel_block(PG_FUNCTION_ARGS)
526 : : {
527 : 235 : Oid relid = PG_GETARG_OID(0);
528 : 235 : BlockNumber blkno = PG_GETARG_UINT32(1);
529 : : Relation rel;
530 : :
531 : 235 : rel = relation_open(relid, AccessExclusiveLock);
532 : :
533 : 235 : invalidate_one_block(rel, MAIN_FORKNUM, blkno);
534 : :
399 andres@anarazel.de 535 :CBC 235 : relation_close(rel, AccessExclusiveLock);
536 : :
537 : 235 : PG_RETURN_VOID();
538 : : }
539 : :
39 andres@anarazel.de 540 :GNC 19 : PG_FUNCTION_INFO_V1(evict_rel);
541 : : Datum
542 : 61 : evict_rel(PG_FUNCTION_ARGS)
543 : : {
544 : 61 : Oid relid = PG_GETARG_OID(0);
545 : : Relation rel;
546 : :
547 : 61 : rel = relation_open(relid, AccessExclusiveLock);
548 : :
549 : : /*
550 : : * EvictRelUnpinnedBuffers() doesn't support temp tables, so for temp
551 : : * tables we have to do it the expensive way and evict every possible
552 : : * buffer.
553 : : */
554 [ + + ]: 61 : if (RelationUsesLocalBuffers(rel))
555 : : {
556 : 18 : SMgrRelation smgr = RelationGetSmgr(rel);
557 : :
558 [ + + ]: 90 : for (int forknum = MAIN_FORKNUM; forknum <= MAX_FORKNUM; forknum++)
559 : : {
560 : : BlockNumber nblocks;
561 : :
562 [ + + ]: 72 : if (!smgrexists(smgr, forknum))
563 : 36 : continue;
564 : :
565 : 36 : nblocks = smgrnblocks(smgr, forknum);
566 : :
567 [ + + ]: 504 : for (int blkno = 0; blkno < nblocks; blkno++)
568 : : {
569 : 468 : invalidate_one_block(rel, forknum, blkno);
570 : : }
571 : : }
572 : : }
573 : : else
574 : : {
575 : : int32 buffers_evicted,
576 : : buffers_flushed,
577 : : buffers_skipped;
578 : :
579 : 43 : EvictRelUnpinnedBuffers(rel, &buffers_evicted, &buffers_flushed,
580 : : &buffers_skipped);
581 : : }
582 : :
583 : 61 : relation_close(rel, AccessExclusiveLock);
584 : :
585 : :
586 : 61 : PG_RETURN_VOID();
587 : : }
588 : :
399 andres@anarazel.de 589 :CBC 12 : PG_FUNCTION_INFO_V1(buffer_create_toy);
590 : : Datum
591 : 13 : buffer_create_toy(PG_FUNCTION_ARGS)
592 : : {
593 : 13 : Oid relid = PG_GETARG_OID(0);
594 : 13 : BlockNumber blkno = PG_GETARG_UINT32(1);
595 : : Relation rel;
596 : : Buffer buf;
597 : :
598 : 13 : rel = relation_open(relid, AccessExclusiveLock);
599 : :
600 : 13 : buf = create_toy_buffer(rel, blkno);
601 : 13 : ReleaseBuffer(buf);
602 : :
603 : 13 : relation_close(rel, NoLock);
604 : :
605 : 13 : PG_RETURN_INT32(buf);
606 : : }
607 : :
608 : 15 : PG_FUNCTION_INFO_V1(buffer_call_start_io);
609 : : Datum
610 : 34 : buffer_call_start_io(PG_FUNCTION_ARGS)
611 : : {
612 : 34 : Buffer buf = PG_GETARG_INT32(0);
613 : 34 : bool for_input = PG_GETARG_BOOL(1);
39 andres@anarazel.de 614 :GNC 34 : bool wait = PG_GETARG_BOOL(2);
615 : : StartBufferIOResult result;
616 : : bool can_start;
617 : :
399 andres@anarazel.de 618 [ + + ]:CBC 34 : if (BufferIsLocal(buf))
39 andres@anarazel.de 619 :GNC 12 : result = StartLocalBufferIO(GetLocalBufferDescriptor(-buf - 1),
620 : : for_input, wait, NULL);
621 : : else
622 : 22 : result = StartSharedBufferIO(GetBufferDescriptor(buf - 1),
623 : : for_input, wait, NULL);
624 : :
625 : 34 : can_start = result == BUFFER_IO_READY_FOR_IO;
626 : :
627 : : /*
628 : : * For tests we don't want the resowner release preventing us from
629 : : * orchestrating odd scenarios.
630 : : */
399 andres@anarazel.de 631 [ + + + + ]:CBC 34 : if (can_start && !BufferIsLocal(buf))
632 : 13 : ResourceOwnerForgetBufferIO(CurrentResourceOwner,
633 : : buf);
634 : :
635 [ + - ]: 34 : ereport(LOG,
636 : : errmsg("buffer %d after StartBufferIO: %s",
637 : : buf, DebugPrintBufferRefcount(buf)),
638 : : errhidestmt(true), errhidecontext(true));
639 : :
640 : 34 : PG_RETURN_BOOL(can_start);
641 : : }
642 : :
643 : 15 : PG_FUNCTION_INFO_V1(buffer_call_terminate_io);
644 : : Datum
645 : 19 : buffer_call_terminate_io(PG_FUNCTION_ARGS)
646 : : {
647 : 19 : Buffer buf = PG_GETARG_INT32(0);
648 : 19 : bool for_input = PG_GETARG_BOOL(1);
649 : 19 : bool succeed = PG_GETARG_BOOL(2);
650 : 19 : bool io_error = PG_GETARG_BOOL(3);
651 : 19 : bool release_aio = PG_GETARG_BOOL(4);
652 : 19 : bool clear_dirty = false;
110 andres@anarazel.de 653 :GNC 19 : uint64 set_flag_bits = 0;
654 : :
399 andres@anarazel.de 655 [ - + ]:CBC 19 : if (io_error)
399 andres@anarazel.de 656 :UBC 0 : set_flag_bits |= BM_IO_ERROR;
657 : :
399 andres@anarazel.de 658 [ + - ]:CBC 19 : if (for_input)
659 : : {
660 : 19 : clear_dirty = false;
661 : :
662 [ + + ]: 19 : if (succeed)
663 : 8 : set_flag_bits |= BM_VALID;
664 : : }
665 : : else
666 : : {
399 andres@anarazel.de 667 [ # # ]:UBC 0 : if (succeed)
668 : 0 : clear_dirty = true;
669 : : }
670 : :
399 andres@anarazel.de 671 [ + - ]:CBC 19 : ereport(LOG,
672 : : errmsg("buffer %d before Terminate[Local]BufferIO: %s",
673 : : buf, DebugPrintBufferRefcount(buf)),
674 : : errhidestmt(true), errhidecontext(true));
675 : :
676 [ + + ]: 19 : if (BufferIsLocal(buf))
677 : 6 : TerminateLocalBufferIO(GetLocalBufferDescriptor(-buf - 1),
678 : : clear_dirty, set_flag_bits, release_aio);
679 : : else
680 : 13 : TerminateBufferIO(GetBufferDescriptor(buf - 1),
681 : : clear_dirty, set_flag_bits, false, release_aio);
682 : :
683 [ + - ]: 19 : ereport(LOG,
684 : : errmsg("buffer %d after Terminate[Local]BufferIO: %s",
685 : : buf, DebugPrintBufferRefcount(buf)),
686 : : errhidestmt(true), errhidecontext(true));
687 : :
688 : 19 : PG_RETURN_VOID();
689 : : }
690 : :
39 andres@anarazel.de 691 :GNC 13 : PG_FUNCTION_INFO_V1(read_buffers);
692 : : /*
693 : : * Infrastructure to test StartReadBuffers()
694 : : */
695 : : Datum
696 : 112 : read_buffers(PG_FUNCTION_ARGS)
697 : : {
698 : 112 : Oid relid = PG_GETARG_OID(0);
699 : 112 : BlockNumber startblock = PG_GETARG_UINT32(1);
700 : 112 : int32 nblocks = PG_GETARG_INT32(2);
701 : 112 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
702 : : Relation rel;
703 : : SMgrRelation smgr;
704 : 112 : int nblocks_done = 0;
705 : 112 : int nblocks_disp = 0;
706 : 112 : int nios = 0;
707 : : ReadBuffersOperation *operations;
708 : : Buffer *buffers;
709 : : Datum *buffers_datum;
710 : : bool *io_reqds;
711 : : int *nblocks_per_io;
712 : :
713 [ - + ]: 112 : Assert(nblocks > 0);
714 : :
715 : 112 : InitMaterializedSRF(fcinfo, 0);
716 : :
717 : : /* at worst each block gets its own IO */
718 : 112 : operations = palloc0(sizeof(ReadBuffersOperation) * nblocks);
719 : 112 : buffers = palloc0(sizeof(Buffer) * nblocks);
720 : 112 : buffers_datum = palloc0(sizeof(Datum) * nblocks);
721 : 112 : io_reqds = palloc0(sizeof(bool) * nblocks);
35 melanieplageman@gmai 722 : 112 : nblocks_per_io = palloc0(sizeof(int) * nblocks);
723 : :
39 andres@anarazel.de 724 : 112 : rel = relation_open(relid, AccessShareLock);
725 : 112 : smgr = RelationGetSmgr(rel);
726 : :
727 : : /*
728 : : * Do StartReadBuffers() until IO for all the required blocks has been
729 : : * started (if required).
730 : : */
731 [ + + ]: 326 : while (nblocks_done < nblocks)
732 : : {
733 : 214 : ReadBuffersOperation *operation = &operations[nios];
734 : 214 : int nblocks_this_io =
735 : 214 : Min(nblocks - nblocks_done, io_combine_limit);
736 : :
737 : 214 : operation->rel = rel;
738 : 214 : operation->smgr = smgr;
739 : 214 : operation->persistence = rel->rd_rel->relpersistence;
740 : 214 : operation->strategy = NULL;
741 : 214 : operation->forknum = MAIN_FORKNUM;
742 : :
743 : 428 : io_reqds[nios] = StartReadBuffers(operation,
744 : 214 : &buffers[nblocks_done],
745 : : startblock + nblocks_done,
746 : : &nblocks_this_io,
747 : : 0);
35 melanieplageman@gmai 748 : 214 : nblocks_per_io[nios] = nblocks_this_io;
39 andres@anarazel.de 749 : 214 : nios++;
750 : 214 : nblocks_done += nblocks_this_io;
751 : : }
752 : :
753 : : /*
754 : : * Now wait for all operations that required IO. This is done at the end,
755 : : * as otherwise waiting for IO in progress in other backends could
756 : : * influence the result for subsequent buffers / blocks.
757 : : */
758 [ + + ]: 326 : for (int nio = 0; nio < nios; nio++)
759 : : {
760 : 214 : ReadBuffersOperation *operation = &operations[nio];
761 : :
762 [ + + ]: 214 : if (io_reqds[nio])
763 : 122 : WaitReadBuffers(operation);
764 : : }
765 : :
766 : : /*
767 : : * Convert what has been done into SQL SRF return value.
768 : : */
769 [ + + ]: 326 : for (int nio = 0; nio < nios; nio++)
770 : : {
771 : 214 : ReadBuffersOperation *operation = &operations[nio];
35 melanieplageman@gmai 772 : 214 : int nblocks_this_io = nblocks_per_io[nio];
39 andres@anarazel.de 773 : 214 : Datum values[6] = {0};
774 : 214 : bool nulls[6] = {0};
775 : : ArrayType *buffers_arr;
776 : :
777 : : /* convert buffer array to datum array */
778 [ + + ]: 504 : for (int i = 0; i < nblocks_this_io; i++)
779 : : {
35 melanieplageman@gmai 780 : 290 : Buffer buf = buffers[nblocks_disp + i];
781 : :
39 andres@anarazel.de 782 [ - + ]: 290 : Assert(BufferGetBlockNumber(buf) == startblock + nblocks_disp + i);
783 : :
784 : 290 : buffers_datum[nblocks_disp + i] = Int32GetDatum(buf);
785 : : }
786 : :
787 : 214 : buffers_arr = construct_array_builtin(&buffers_datum[nblocks_disp],
788 : : nblocks_this_io,
789 : : INT4OID);
790 : :
791 : : /* blockoff */
792 : 214 : values[0] = Int32GetDatum(nblocks_disp);
793 : 214 : nulls[0] = false;
794 : :
795 : : /* blocknum */
796 : 214 : values[1] = UInt32GetDatum(startblock + nblocks_disp);
797 : 214 : nulls[1] = false;
798 : :
799 : : /* io_reqd */
800 : 214 : values[2] = BoolGetDatum(io_reqds[nio]);
801 : 214 : nulls[2] = false;
802 : :
803 : : /* foreign IO - only valid when IO was required */
35 melanieplageman@gmai 804 [ + + + + ]: 214 : values[3] = BoolGetDatum(io_reqds[nio] ? operation->foreign_io : false);
39 andres@anarazel.de 805 : 214 : nulls[3] = false;
806 : :
807 : : /* nblocks */
808 : 214 : values[4] = Int32GetDatum(nblocks_this_io);
809 : 214 : nulls[4] = false;
810 : :
811 : : /* array of buffers */
812 : 214 : values[5] = PointerGetDatum(buffers_arr);
813 : 214 : nulls[5] = false;
814 : :
815 : 214 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
816 : :
817 : 214 : nblocks_disp += nblocks_this_io;
818 : : }
819 : :
820 : : /* release pins on all the buffers */
35 melanieplageman@gmai 821 [ + + ]: 402 : for (int i = 0; i < nblocks_done; i++)
822 : 290 : ReleaseBuffer(buffers[i]);
823 : :
824 : : /*
825 : : * Free explicitly, to have a chance to detect potential issues with too
826 : : * long lived references to the operation.
827 : : */
39 andres@anarazel.de 828 : 112 : pfree(operations);
829 : 112 : pfree(buffers);
830 : 112 : pfree(buffers_datum);
831 : 112 : pfree(io_reqds);
35 melanieplageman@gmai 832 : 112 : pfree(nblocks_per_io);
833 : :
39 andres@anarazel.de 834 : 112 : relation_close(rel, NoLock);
835 : :
836 : 112 : return (Datum) 0;
837 : : }
838 : :
839 : :
840 : : static BlockNumber
841 : 156 : read_stream_for_blocks_cb(ReadStream *stream,
842 : : void *callback_private_data,
843 : : void *per_buffer_data)
844 : : {
845 : 156 : BlocksReadStreamData *stream_data = callback_private_data;
846 : :
847 [ + + ]: 156 : if (stream_data->curblock >= stream_data->nblocks)
848 : 24 : return InvalidBlockNumber;
849 : 132 : return stream_data->blocks[stream_data->curblock++];
850 : : }
851 : :
852 : 13 : PG_FUNCTION_INFO_V1(read_stream_for_blocks);
853 : : Datum
854 : 24 : read_stream_for_blocks(PG_FUNCTION_ARGS)
855 : : {
856 : 24 : Oid relid = PG_GETARG_OID(0);
857 : 24 : ArrayType *blocksarray = PG_GETARG_ARRAYTYPE_P(1);
858 : 24 : ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
859 : : Relation rel;
860 : : BlocksReadStreamData stream_data;
861 : : ReadStream *stream;
862 : :
863 : 24 : InitMaterializedSRF(fcinfo, 0);
864 : :
865 : : /*
866 : : * We expect the input to be an N-element int4 array; verify that. We
867 : : * don't need to use deconstruct_array() since the array data is just
868 : : * going to look like a C array of N int4 values.
869 : : */
870 [ + - ]: 24 : if (ARR_NDIM(blocksarray) != 1 ||
871 [ + - ]: 24 : ARR_HASNULL(blocksarray) ||
872 [ - + ]: 24 : ARR_ELEMTYPE(blocksarray) != INT4OID)
39 andres@anarazel.de 873 [ # # ]:UNC 0 : elog(ERROR, "expected 1 dimensional int4 array");
874 : :
39 andres@anarazel.de 875 :GNC 24 : stream_data.curblock = 0;
876 : 24 : stream_data.nblocks = ARR_DIMS(blocksarray)[0];
877 [ - + ]: 24 : stream_data.blocks = (uint32 *) ARR_DATA_PTR(blocksarray);
878 : :
879 : 24 : rel = relation_open(relid, AccessShareLock);
880 : :
881 : 24 : stream = read_stream_begin_relation(READ_STREAM_FULL,
882 : : NULL,
883 : : rel,
884 : : MAIN_FORKNUM,
885 : : read_stream_for_blocks_cb,
886 : : &stream_data,
887 : : 0);
888 : :
889 [ + + ]: 156 : for (int i = 0; i < stream_data.nblocks; i++)
890 : : {
891 : 132 : Buffer buf = read_stream_next_buffer(stream, NULL);
892 : 132 : Datum values[3] = {0};
893 : 132 : bool nulls[3] = {0};
894 : :
895 [ - + ]: 132 : if (!BufferIsValid(buf))
39 andres@anarazel.de 896 [ # # ]:UNC 0 : elog(ERROR, "read_stream_next_buffer() call %d is unexpectedly invalid", i);
897 : :
39 andres@anarazel.de 898 :GNC 132 : values[0] = Int32GetDatum(i);
899 : 132 : values[1] = UInt32GetDatum(stream_data.blocks[i]);
900 : 132 : values[2] = UInt32GetDatum(buf);
901 : :
902 : 132 : tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
903 : :
904 : 132 : ReleaseBuffer(buf);
905 : : }
906 : :
907 [ - + ]: 24 : if (read_stream_next_buffer(stream, NULL) != InvalidBuffer)
39 andres@anarazel.de 908 [ # # ]:UNC 0 : elog(ERROR, "read_stream_next_buffer() call %d is unexpectedly valid",
909 : : stream_data.nblocks);
910 : :
39 andres@anarazel.de 911 :GNC 24 : read_stream_end(stream);
912 : :
913 : 24 : relation_close(rel, NoLock);
914 : :
915 : 24 : return (Datum) 0;
916 : : }
917 : :
918 : :
399 andres@anarazel.de 919 :CBC 10 : PG_FUNCTION_INFO_V1(handle_get);
920 : : Datum
921 : 18 : handle_get(PG_FUNCTION_ARGS)
922 : : {
923 : 18 : last_handle = pgaio_io_acquire(CurrentResourceOwner, NULL);
924 : :
925 : 18 : PG_RETURN_VOID();
926 : : }
927 : :
928 : 10 : PG_FUNCTION_INFO_V1(handle_release_last);
929 : : Datum
930 : 6 : handle_release_last(PG_FUNCTION_ARGS)
931 : : {
932 [ - + ]: 6 : if (!last_handle)
399 andres@anarazel.de 933 [ # # ]:UBC 0 : elog(ERROR, "no handle");
934 : :
399 andres@anarazel.de 935 :CBC 6 : pgaio_io_release(last_handle);
936 : :
937 : 3 : PG_RETURN_VOID();
938 : : }
939 : :
940 : 10 : PG_FUNCTION_INFO_V1(handle_get_and_error);
941 : : Datum
942 : 9 : handle_get_and_error(PG_FUNCTION_ARGS)
943 : : {
944 : 9 : pgaio_io_acquire(CurrentResourceOwner, NULL);
945 : :
946 [ + - ]: 9 : elog(ERROR, "as you command");
947 : : PG_RETURN_VOID();
948 : : }
949 : :
950 : 10 : PG_FUNCTION_INFO_V1(handle_get_twice);
951 : : Datum
952 : 3 : handle_get_twice(PG_FUNCTION_ARGS)
953 : : {
954 : 3 : pgaio_io_acquire(CurrentResourceOwner, NULL);
955 : 3 : pgaio_io_acquire(CurrentResourceOwner, NULL);
956 : :
399 andres@anarazel.de 957 :UBC 0 : PG_RETURN_VOID();
958 : : }
959 : :
399 andres@anarazel.de 960 :CBC 10 : PG_FUNCTION_INFO_V1(handle_get_release);
961 : : Datum
962 : 9 : handle_get_release(PG_FUNCTION_ARGS)
963 : : {
964 : : PgAioHandle *handle;
965 : :
966 : 9 : handle = pgaio_io_acquire(CurrentResourceOwner, NULL);
967 : 9 : pgaio_io_release(handle);
968 : :
969 : 9 : PG_RETURN_VOID();
970 : : }
971 : :
972 : 10 : PG_FUNCTION_INFO_V1(batch_start);
973 : : Datum
974 : 9 : batch_start(PG_FUNCTION_ARGS)
975 : : {
976 : 9 : pgaio_enter_batchmode();
977 : 9 : PG_RETURN_VOID();
978 : : }
979 : :
980 : 10 : PG_FUNCTION_INFO_V1(batch_end);
981 : : Datum
982 : 3 : batch_end(PG_FUNCTION_ARGS)
983 : : {
984 : 3 : pgaio_exit_batchmode();
985 : 3 : PG_RETURN_VOID();
986 : : }
987 : :
988 : : #ifdef USE_INJECTION_POINTS
989 : : extern PGDLLEXPORT void inj_io_completion_hook(const char *name,
990 : : const void *private_data,
991 : : void *arg);
992 : : extern PGDLLEXPORT void inj_io_reopen(const char *name,
993 : : const void *private_data,
994 : : void *arg);
995 : :
996 : : static bool
39 andres@anarazel.de 997 :GNC 3123 : inj_io_short_read_matches(PgAioHandle *ioh)
998 : : {
999 : : PGPROC *io_proc;
1000 : : int32 io_pid;
1001 : : int32 inj_pid;
1002 : : PgAioTargetData *td;
1003 : :
1004 [ + + ]: 3123 : if (!inj_io_error_state->enabled_short_read)
1005 : 2879 : return false;
1006 : :
1007 [ - + ]: 244 : if (!inj_io_error_state->short_read_result_set)
39 andres@anarazel.de 1008 :UNC 0 : return false;
1009 : :
39 andres@anarazel.de 1010 :GNC 244 : io_proc = GetPGProcByNumber(pgaio_io_get_owner(ioh));
1011 : 244 : io_pid = io_proc->pid;
1012 : 244 : inj_pid = inj_io_error_state->short_read_pid;
1013 : :
1014 [ + + + + ]: 244 : if (inj_pid != InvalidPid && inj_pid != io_pid)
1015 : 12 : return false;
1016 : :
1017 : 232 : td = pgaio_io_get_target_data(ioh);
1018 : :
1019 [ + + ]: 232 : if (inj_io_error_state->short_read_relfilenode != InvalidOid &&
1020 [ - + ]: 3 : td->smgr.rlocator.relNumber != inj_io_error_state->short_read_relfilenode)
39 andres@anarazel.de 1021 :UNC 0 : return false;
1022 : :
1023 : : /*
1024 : : * Only shorten reads that are actually longer than the target size,
1025 : : * otherwise we can trigger over-reads.
1026 : : */
39 andres@anarazel.de 1027 [ + + ]:GNC 232 : if (inj_io_error_state->short_read_result >= ioh->result)
1028 : 51 : return false;
1029 : :
1030 : 181 : return true;
1031 : : }
1032 : :
1033 : : static bool
1034 : 3153 : inj_io_completion_wait_matches(PgAioHandle *ioh)
1035 : : {
1036 : : PGPROC *io_proc;
1037 : : int32 io_pid;
1038 : : PgAioTargetData *td;
1039 : : int32 inj_pid;
1040 : : BlockNumber io_blockno;
1041 : : BlockNumber inj_blockno;
1042 : : Oid inj_relfilenode;
1043 : :
1044 [ + + ]: 3153 : if (!inj_io_error_state->enabled_completion_wait)
1045 : 2945 : return false;
1046 : :
1047 : 208 : io_proc = GetPGProcByNumber(pgaio_io_get_owner(ioh));
1048 : 208 : io_pid = io_proc->pid;
1049 : 208 : inj_pid = inj_io_error_state->completion_wait_pid;
1050 : :
1051 [ + - + + ]: 208 : if (inj_pid != InvalidPid && inj_pid != io_pid)
1052 : 166 : return false;
1053 : :
1054 : 42 : td = pgaio_io_get_target_data(ioh);
1055 : :
1056 : 42 : inj_relfilenode = inj_io_error_state->completion_wait_relfilenode;
1057 [ + - ]: 42 : if (inj_relfilenode != InvalidOid &&
1058 [ + + ]: 42 : td->smgr.rlocator.relNumber != inj_relfilenode)
1059 : 12 : return false;
1060 : :
1061 : 30 : inj_blockno = inj_io_error_state->completion_wait_blockno;
1062 : 30 : io_blockno = td->smgr.blockNum;
1063 [ + + + - ]: 30 : if (inj_blockno != InvalidBlockNumber &&
1064 [ - + ]: 12 : !(inj_blockno >= io_blockno && inj_blockno < (io_blockno + td->smgr.nblocks)))
39 andres@anarazel.de 1065 :UNC 0 : return false;
1066 : :
39 andres@anarazel.de 1067 :GNC 30 : return true;
1068 : : }
1069 : :
1070 : : static void
1071 : 3123 : inj_io_completion_wait_hook(const char *name, const void *private_data, void *arg)
1072 : : {
1073 : 3123 : PgAioHandle *ioh = (PgAioHandle *) arg;
1074 : :
1075 [ + + ]: 3123 : if (!inj_io_completion_wait_matches(ioh))
1076 : 3108 : return;
1077 : :
1078 : 15 : ConditionVariablePrepareToSleep(&inj_io_error_state->cv);
1079 : :
1080 : : while (true)
1081 : : {
1082 [ + + ]: 30 : if (!inj_io_completion_wait_matches(ioh))
1083 : 15 : break;
1084 : :
1085 : 15 : ConditionVariableSleep(&inj_io_error_state->cv,
1086 : 15 : inj_io_error_state->completion_wait_event);
1087 : : }
1088 : :
1089 : 15 : ConditionVariableCancelSleep();
1090 : : }
1091 : :
1092 : : static void
1093 : 3123 : inj_io_short_read_hook(const char *name, const void *private_data, void *arg)
1094 : : {
360 michael@paquier.xyz 1095 :CBC 3123 : PgAioHandle *ioh = (PgAioHandle *) arg;
1096 : :
399 andres@anarazel.de 1097 [ + - ]: 3123 : ereport(LOG,
1098 : : errmsg("short read injection point called, is enabled: %d",
1099 : : inj_io_error_state->enabled_short_read),
1100 : : errhidestmt(true), errhidecontext(true));
1101 : :
39 andres@anarazel.de 1102 [ + + ]:GNC 3123 : if (inj_io_short_read_matches(ioh))
1103 : : {
1104 : 181 : struct iovec *iov = &pgaio_ctl->iovecs[ioh->iovec_off];
1105 : 181 : int32 old_result = ioh->result;
1106 : 181 : int32 new_result = inj_io_error_state->short_read_result;
1107 : 181 : int32 processed = 0;
1108 : :
1109 [ + - ]: 181 : ereport(LOG,
1110 : : errmsg("short read inject point, changing result from %d to %d",
1111 : : old_result, new_result),
1112 : : errhidestmt(true), errhidecontext(true));
1113 : :
1114 : : /*
1115 : : * The underlying IO actually completed OK, and thus the "invalid"
1116 : : * portion of the IOV actually contains valid data. That can hide a
1117 : : * lot of problems, e.g. if we were to wrongly mark a buffer, that
1118 : : * wasn't read according to the shortened-read, IO as valid, the
1119 : : * contents would look valid and we might miss a bug.
1120 : : *
1121 : : * To avoid that, iterate through the IOV and zero out the "failed"
1122 : : * portion of the IO.
1123 : : */
1124 [ + + ]: 368 : for (int i = 0; i < ioh->op_data.read.iov_length; i++)
1125 : : {
1126 [ + + ]: 187 : if (processed + iov[i].iov_len <= new_result)
1127 : 18 : processed += iov[i].iov_len;
1128 [ + - ]: 169 : else if (processed <= new_result)
1129 : : {
1130 : 169 : uint32 ok_part = new_result - processed;
1131 : :
1132 : 169 : memset((char *) iov[i].iov_base + ok_part, 0, iov[i].iov_len - ok_part);
1133 : 169 : processed += iov[i].iov_len;
1134 : : }
1135 : : else
1136 : : {
39 andres@anarazel.de 1137 :UNC 0 : memset((char *) iov[i].iov_base, 0, iov[i].iov_len);
1138 : : }
1139 : : }
1140 : :
39 andres@anarazel.de 1141 :GNC 181 : ioh->result = new_result;
1142 : : }
399 andres@anarazel.de 1143 :CBC 3123 : }
1144 : :
1145 : : void
39 andres@anarazel.de 1146 :GNC 3123 : inj_io_completion_hook(const char *name, const void *private_data, void *arg)
1147 : : {
1148 : 3123 : inj_io_completion_wait_hook(name, private_data, arg);
1149 : 3123 : inj_io_short_read_hook(name, private_data, arg);
1150 : 3123 : }
1151 : :
1152 : : void
360 michael@paquier.xyz 1153 :CBC 505 : inj_io_reopen(const char *name, const void *private_data, void *arg)
1154 : : {
399 andres@anarazel.de 1155 [ + - ]: 505 : ereport(LOG,
1156 : : errmsg("reopen injection point called, is enabled: %d",
1157 : : inj_io_error_state->enabled_reopen),
1158 : : errhidestmt(true), errhidecontext(true));
1159 : :
1160 [ + + ]: 505 : if (inj_io_error_state->enabled_reopen)
1161 [ + - ]: 1 : elog(ERROR, "injection point triggering failure to reopen ");
1162 : 504 : }
1163 : : #endif
1164 : :
39 andres@anarazel.de 1165 :GNC 13 : PG_FUNCTION_INFO_V1(inj_io_completion_wait);
1166 : : Datum
1167 : 15 : inj_io_completion_wait(PG_FUNCTION_ARGS)
1168 : : {
1169 : : #ifdef USE_INJECTION_POINTS
1170 : 15 : inj_io_error_state->enabled_completion_wait = true;
1171 : 15 : inj_io_error_state->completion_wait_pid =
1172 [ + - ]: 15 : PG_ARGISNULL(0) ? InvalidPid : PG_GETARG_INT32(0);
1173 : 15 : inj_io_error_state->completion_wait_relfilenode =
1174 [ + - ]: 15 : PG_ARGISNULL(1) ? InvalidOid : PG_GETARG_OID(1);
1175 : 15 : inj_io_error_state->completion_wait_blockno =
1176 [ + + ]: 15 : PG_ARGISNULL(2) ? InvalidBlockNumber : PG_GETARG_UINT32(2);
1177 : : #else
1178 : : elog(ERROR, "injection points not supported");
1179 : : #endif
1180 : :
1181 : 15 : PG_RETURN_VOID();
1182 : : }
1183 : :
1184 : 19 : PG_FUNCTION_INFO_V1(inj_io_completion_continue);
1185 : : Datum
1186 : 15 : inj_io_completion_continue(PG_FUNCTION_ARGS)
1187 : : {
1188 : : #ifdef USE_INJECTION_POINTS
1189 : 15 : inj_io_error_state->enabled_completion_wait = false;
1190 : 15 : inj_io_error_state->completion_wait_pid = InvalidPid;
1191 : 15 : inj_io_error_state->completion_wait_relfilenode = InvalidOid;
1192 : 15 : inj_io_error_state->completion_wait_blockno = InvalidBlockNumber;
1193 : 15 : ConditionVariableBroadcast(&inj_io_error_state->cv);
1194 : : #else
1195 : : elog(ERROR, "injection points not supported");
1196 : : #endif
1197 : :
1198 : 15 : PG_RETURN_VOID();
1199 : : }
1200 : :
399 andres@anarazel.de 1201 :CBC 13 : PG_FUNCTION_INFO_V1(inj_io_short_read_attach);
1202 : : Datum
1203 : 24 : inj_io_short_read_attach(PG_FUNCTION_ARGS)
1204 : : {
1205 : : #ifdef USE_INJECTION_POINTS
1206 : 24 : inj_io_error_state->enabled_short_read = true;
1207 : 24 : inj_io_error_state->short_read_result_set = !PG_ARGISNULL(0);
1208 [ + - ]: 24 : if (inj_io_error_state->short_read_result_set)
1209 : 24 : inj_io_error_state->short_read_result = PG_GETARG_INT32(0);
39 andres@anarazel.de 1210 :GNC 24 : inj_io_error_state->short_read_pid =
1211 [ + + ]: 24 : PG_ARGISNULL(1) ? InvalidPid : PG_GETARG_INT32(1);
1212 : 24 : inj_io_error_state->short_read_relfilenode =
1213 [ + + ]: 24 : PG_ARGISNULL(2) ? InvalidOid : PG_GETARG_OID(2);
1214 : : #else
1215 : : elog(ERROR, "injection points not supported");
1216 : : #endif
1217 : :
399 andres@anarazel.de 1218 :CBC 24 : PG_RETURN_VOID();
1219 : : }
1220 : :
1221 : 13 : PG_FUNCTION_INFO_V1(inj_io_short_read_detach);
1222 : : Datum
1223 : 9 : inj_io_short_read_detach(PG_FUNCTION_ARGS)
1224 : : {
1225 : : #ifdef USE_INJECTION_POINTS
1226 : 9 : inj_io_error_state->enabled_short_read = false;
1227 : : #else
1228 : : elog(ERROR, "injection points not supported");
1229 : : #endif
1230 : 9 : PG_RETURN_VOID();
1231 : : }
1232 : :
1233 : 8 : PG_FUNCTION_INFO_V1(inj_io_reopen_attach);
1234 : : Datum
1235 : 1 : inj_io_reopen_attach(PG_FUNCTION_ARGS)
1236 : : {
1237 : : #ifdef USE_INJECTION_POINTS
1238 : 1 : inj_io_error_state->enabled_reopen = true;
1239 : : #else
1240 : : elog(ERROR, "injection points not supported");
1241 : : #endif
1242 : :
1243 : 1 : PG_RETURN_VOID();
1244 : : }
1245 : :
1246 : 8 : PG_FUNCTION_INFO_V1(inj_io_reopen_detach);
1247 : : Datum
1248 : 1 : inj_io_reopen_detach(PG_FUNCTION_ARGS)
1249 : : {
1250 : : #ifdef USE_INJECTION_POINTS
1251 : 1 : inj_io_error_state->enabled_reopen = false;
1252 : : #else
1253 : : elog(ERROR, "injection points not supported");
1254 : : #endif
1255 : 1 : PG_RETURN_VOID();
1256 : : }
|