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-2025, 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 "fmgr.h"
23 : : #include "storage/aio.h"
24 : : #include "storage/aio_internal.h"
25 : : #include "storage/buf_internals.h"
26 : : #include "storage/bufmgr.h"
27 : : #include "storage/checksum.h"
28 : : #include "storage/ipc.h"
29 : : #include "storage/lwlock.h"
30 : : #include "utils/builtins.h"
31 : : #include "utils/injection_point.h"
32 : : #include "utils/rel.h"
33 : :
34 : :
260 andres@anarazel.de 35 :CBC 3 : PG_MODULE_MAGIC;
36 : :
37 : :
38 : : typedef struct InjIoErrorState
39 : : {
40 : : bool enabled_short_read;
41 : : bool enabled_reopen;
42 : :
43 : : bool short_read_result_set;
44 : : int short_read_result;
45 : : } InjIoErrorState;
46 : :
47 : : static InjIoErrorState *inj_io_error_state;
48 : :
49 : : /* Shared memory init callbacks */
50 : : static shmem_request_hook_type prev_shmem_request_hook = NULL;
51 : : static shmem_startup_hook_type prev_shmem_startup_hook = NULL;
52 : :
53 : :
54 : : static PgAioHandle *last_handle;
55 : :
56 : :
57 : :
58 : : static void
59 : 3 : test_aio_shmem_request(void)
60 : : {
61 [ - + ]: 3 : if (prev_shmem_request_hook)
260 andres@anarazel.de 62 :UBC 0 : prev_shmem_request_hook();
63 : :
260 andres@anarazel.de 64 :CBC 3 : RequestAddinShmemSpace(sizeof(InjIoErrorState));
65 : 3 : }
66 : :
67 : : static void
68 : 3 : test_aio_shmem_startup(void)
69 : : {
70 : : bool found;
71 : :
72 [ - + ]: 3 : if (prev_shmem_startup_hook)
260 andres@anarazel.de 73 :UBC 0 : prev_shmem_startup_hook();
74 : :
75 : : /* Create or attach to the shared memory state */
260 andres@anarazel.de 76 :CBC 3 : LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE);
77 : :
78 : 3 : inj_io_error_state = ShmemInitStruct("injection_points",
79 : : sizeof(InjIoErrorState),
80 : : &found);
81 : :
82 [ + - ]: 3 : if (!found)
83 : : {
84 : : /* First time through, initialize */
85 : 3 : inj_io_error_state->enabled_short_read = false;
86 : 3 : inj_io_error_state->enabled_reopen = false;
87 : :
88 : : #ifdef USE_INJECTION_POINTS
89 : : InjectionPointAttach("aio-process-completion-before-shared",
90 : : "test_aio",
91 : : "inj_io_short_read",
92 : : NULL,
93 : : 0);
94 : : InjectionPointLoad("aio-process-completion-before-shared");
95 : :
96 : : InjectionPointAttach("aio-worker-after-reopen",
97 : : "test_aio",
98 : : "inj_io_reopen",
99 : : NULL,
100 : : 0);
101 : : InjectionPointLoad("aio-worker-after-reopen");
102 : :
103 : : #endif
104 : : }
105 : : else
106 : : {
107 : : /*
108 : : * Pre-load the injection points now, so we can call them in a
109 : : * critical section.
110 : : */
111 : : #ifdef USE_INJECTION_POINTS
112 : : InjectionPointLoad("aio-process-completion-before-shared");
113 : : InjectionPointLoad("aio-worker-after-reopen");
114 : : elog(LOG, "injection point loaded");
115 : : #endif
116 : : }
117 : :
118 : 3 : LWLockRelease(AddinShmemInitLock);
119 : 3 : }
120 : :
121 : : void
122 : 3 : _PG_init(void)
123 : : {
124 [ - + ]: 3 : if (!process_shared_preload_libraries_in_progress)
260 andres@anarazel.de 125 :UBC 0 : return;
126 : :
260 andres@anarazel.de 127 :CBC 3 : prev_shmem_request_hook = shmem_request_hook;
128 : 3 : shmem_request_hook = test_aio_shmem_request;
129 : 3 : prev_shmem_startup_hook = shmem_startup_hook;
130 : 3 : shmem_startup_hook = test_aio_shmem_startup;
131 : : }
132 : :
133 : :
134 : 6 : PG_FUNCTION_INFO_V1(errno_from_string);
135 : : Datum
260 andres@anarazel.de 136 :UBC 0 : errno_from_string(PG_FUNCTION_ARGS)
137 : : {
138 : 0 : const char *sym = text_to_cstring(PG_GETARG_TEXT_PP(0));
139 : :
140 [ # # ]: 0 : if (strcmp(sym, "EIO") == 0)
141 : 0 : PG_RETURN_INT32(EIO);
142 [ # # ]: 0 : else if (strcmp(sym, "EAGAIN") == 0)
143 : 0 : PG_RETURN_INT32(EAGAIN);
144 [ # # ]: 0 : else if (strcmp(sym, "EINTR") == 0)
145 : 0 : PG_RETURN_INT32(EINTR);
146 [ # # ]: 0 : else if (strcmp(sym, "ENOSPC") == 0)
147 : 0 : PG_RETURN_INT32(ENOSPC);
148 [ # # ]: 0 : else if (strcmp(sym, "EROFS") == 0)
149 : 0 : PG_RETURN_INT32(EROFS);
150 : :
151 [ # # ]: 0 : ereport(ERROR,
152 : : errcode(ERRCODE_INVALID_PARAMETER_VALUE),
153 : : errmsg_internal("%s is not a supported errno value", sym));
154 : : PG_RETURN_INT32(0);
155 : : }
156 : :
260 andres@anarazel.de 157 :CBC 12 : PG_FUNCTION_INFO_V1(grow_rel);
158 : : Datum
159 : 9 : grow_rel(PG_FUNCTION_ARGS)
160 : : {
161 : 9 : Oid relid = PG_GETARG_OID(0);
162 : 9 : uint32 nblocks = PG_GETARG_UINT32(1);
163 : : Relation rel;
164 : : #define MAX_BUFFERS_TO_EXTEND_BY 64
165 : : Buffer victim_buffers[MAX_BUFFERS_TO_EXTEND_BY];
166 : :
167 : 9 : rel = relation_open(relid, AccessExclusiveLock);
168 : :
169 [ + + ]: 18 : while (nblocks > 0)
170 : : {
171 : : uint32 extend_by_pages;
172 : :
173 : 9 : extend_by_pages = Min(nblocks, MAX_BUFFERS_TO_EXTEND_BY);
174 : :
175 : 9 : ExtendBufferedRelBy(BMR_REL(rel),
176 : : MAIN_FORKNUM,
177 : : NULL,
178 : : 0,
179 : : extend_by_pages,
180 : : victim_buffers,
181 : : &extend_by_pages);
182 : :
183 : 9 : nblocks -= extend_by_pages;
184 : :
185 [ + + ]: 117 : for (uint32 i = 0; i < extend_by_pages; i++)
186 : : {
187 : 108 : ReleaseBuffer(victim_buffers[i]);
188 : : }
189 : : }
190 : :
191 : 9 : relation_close(rel, NoLock);
192 : :
193 : 9 : PG_RETURN_VOID();
194 : : }
195 : :
196 : 27 : PG_FUNCTION_INFO_V1(modify_rel_block);
197 : : Datum
198 : 105 : modify_rel_block(PG_FUNCTION_ARGS)
199 : : {
200 : 105 : Oid relid = PG_GETARG_OID(0);
201 : 105 : BlockNumber blkno = PG_GETARG_UINT32(1);
202 : 105 : bool zero = PG_GETARG_BOOL(2);
203 : 105 : bool corrupt_header = PG_GETARG_BOOL(3);
204 : 105 : bool corrupt_checksum = PG_GETARG_BOOL(4);
205 : 105 : Page page = palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
206 : : bool flushed;
207 : : Relation rel;
208 : : Buffer buf;
209 : : PageHeader ph;
210 : :
211 : 105 : rel = relation_open(relid, AccessExclusiveLock);
212 : :
213 : 105 : buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno,
214 : : RBM_ZERO_ON_ERROR, NULL);
215 : :
216 : 105 : LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
217 : :
218 : : /*
219 : : * copy the page to local memory, seems nicer than to directly modify in
220 : : * the buffer pool.
221 : : */
222 : 105 : memcpy(page, BufferGetPage(buf), BLCKSZ);
223 : :
224 : 105 : LockBuffer(buf, BUFFER_LOCK_UNLOCK);
225 : :
226 : 105 : ReleaseBuffer(buf);
227 : :
228 : : /*
229 : : * Don't want to have a buffer in-memory that's marked valid where the
230 : : * on-disk contents are invalid. Particularly not if the in-memory buffer
231 : : * could be dirty...
232 : : *
233 : : * While we hold an AEL on the relation nobody else should be able to read
234 : : * the buffer in.
235 : : *
236 : : * NB: This is probably racy, better don't copy this to non-test code.
237 : : */
238 [ + + ]: 105 : if (BufferIsLocal(buf))
239 : 27 : InvalidateLocalBuffer(GetLocalBufferDescriptor(-buf - 1), true);
240 : : else
253 241 : 78 : EvictUnpinnedBuffer(buf, &flushed);
242 : :
243 : : /*
244 : : * Now modify the page as asked for by the caller.
245 : : */
260 246 [ + + ]: 105 : if (zero)
247 : 24 : memset(page, 0, BufferGetPageSize(buf));
248 : :
249 [ + + + + : 105 : if (PageIsEmpty(page) && (corrupt_header || corrupt_checksum))
+ + ]
250 : 24 : PageInit(page, BufferGetPageSize(buf), 0);
251 : :
252 : 105 : ph = (PageHeader) page;
253 : :
254 [ + + ]: 105 : if (corrupt_header)
255 : 48 : ph->pd_special = BLCKSZ + 1;
256 : :
257 [ + + ]: 105 : if (corrupt_checksum)
258 : : {
259 : 39 : bool successfully_corrupted = 0;
260 : :
261 : : /*
262 : : * Any single modification of the checksum could just end up being
263 : : * valid again, due to e.g. corrupt_header changing the data in a way
264 : : * that'd result in the "corrupted" checksum, or the checksum already
265 : : * being invalid. Retry in that, unlikely, case.
266 : : */
267 [ + - ]: 39 : for (int i = 0; i < 100; i++)
268 : : {
269 : : uint16 verify_checksum;
270 : : uint16 old_checksum;
271 : :
272 : 39 : old_checksum = ph->pd_checksum;
273 : 39 : ph->pd_checksum = old_checksum + 1;
274 : :
275 [ + - ]: 39 : elog(LOG, "corrupting checksum of blk %u from %u to %u",
276 : : blkno, old_checksum, ph->pd_checksum);
277 : :
278 : 39 : verify_checksum = pg_checksum_page(page, blkno);
279 [ + - ]: 39 : if (verify_checksum != ph->pd_checksum)
280 : : {
281 : 39 : successfully_corrupted = true;
282 : 39 : break;
283 : : }
284 : : }
285 : :
286 [ - + ]: 39 : if (!successfully_corrupted)
260 andres@anarazel.de 287 [ # # ]:UBC 0 : elog(ERROR, "could not corrupt checksum, what's going on?");
288 : : }
289 : : else
290 : : {
260 andres@anarazel.de 291 :CBC 66 : PageSetChecksumInplace(page, blkno);
292 : : }
293 : :
294 : 105 : smgrwrite(RelationGetSmgr(rel),
295 : : MAIN_FORKNUM, blkno, page, true);
296 : :
297 : 105 : relation_close(rel, NoLock);
298 : :
299 : 105 : PG_RETURN_VOID();
300 : : }
301 : :
302 : : /*
303 : : * Ensures a buffer for rel & blkno is in shared buffers, without actually
304 : : * caring about the buffer contents. Used to set up test scenarios.
305 : : */
306 : : static Buffer
307 : 195 : create_toy_buffer(Relation rel, BlockNumber blkno)
308 : : {
309 : : Buffer buf;
310 : : BufferDesc *buf_hdr;
311 : : uint32 buf_state;
312 : 195 : bool was_pinned = false;
41 andres@anarazel.de 313 :GNC 195 : uint32 unset_bits = 0;
314 : :
315 : : /* place buffer in shared buffers without erroring out */
260 andres@anarazel.de 316 :CBC 195 : buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_ZERO_AND_LOCK, NULL);
317 : 195 : LockBuffer(buf, BUFFER_LOCK_UNLOCK);
318 : :
319 [ + + ]: 195 : if (RelationUsesLocalBuffers(rel))
320 : : {
321 : 66 : buf_hdr = GetLocalBufferDescriptor(-buf - 1);
322 : 66 : buf_state = pg_atomic_read_u32(&buf_hdr->state);
323 : : }
324 : : else
325 : : {
326 : 129 : buf_hdr = GetBufferDescriptor(buf - 1);
327 : 129 : buf_state = LockBufHdr(buf_hdr);
328 : : }
329 : :
330 : : /*
331 : : * We should be the only backend accessing this buffer. This is just a
332 : : * small bit of belt-and-suspenders defense, none of this code should ever
333 : : * run in a cluster with real data.
334 : : */
335 [ - + ]: 195 : if (BUF_STATE_GET_REFCOUNT(buf_state) > 1)
260 andres@anarazel.de 336 :UBC 0 : was_pinned = true;
337 : : else
41 andres@anarazel.de 338 :GNC 195 : unset_bits |= BM_VALID | BM_DIRTY;
339 : :
260 andres@anarazel.de 340 [ + + ]:CBC 195 : if (RelationUsesLocalBuffers(rel))
341 : : {
41 andres@anarazel.de 342 :GNC 66 : buf_state &= ~unset_bits;
260 andres@anarazel.de 343 :CBC 66 : pg_atomic_unlocked_write_u32(&buf_hdr->state, buf_state);
344 : : }
345 : : else
346 : : {
41 andres@anarazel.de 347 :GNC 129 : UnlockBufHdrExt(buf_hdr, buf_state, 0, unset_bits, 0);
348 : : }
349 : :
260 andres@anarazel.de 350 [ - + ]:CBC 195 : if (was_pinned)
260 andres@anarazel.de 351 [ # # ]:UBC 0 : elog(ERROR, "toy buffer %d was already pinned",
352 : : buf);
353 : :
260 andres@anarazel.de 354 :CBC 195 : return buf;
355 : : }
356 : :
357 : : /*
358 : : * A "low level" read. This does similar things to what
359 : : * StartReadBuffers()/WaitReadBuffers() do, but provides more control (and
360 : : * less sanity).
361 : : */
362 : 30 : PG_FUNCTION_INFO_V1(read_rel_block_ll);
363 : : Datum
364 : 108 : read_rel_block_ll(PG_FUNCTION_ARGS)
365 : : {
366 : 108 : Oid relid = PG_GETARG_OID(0);
367 : 108 : BlockNumber blkno = PG_GETARG_UINT32(1);
368 : 108 : int nblocks = PG_GETARG_INT32(2);
369 : 108 : bool wait_complete = PG_GETARG_BOOL(3);
370 : 108 : bool batchmode_enter = PG_GETARG_BOOL(4);
371 : 108 : bool call_smgrreleaseall = PG_GETARG_BOOL(5);
372 : 108 : bool batchmode_exit = PG_GETARG_BOOL(6);
373 : 108 : bool zero_on_error = PG_GETARG_BOOL(7);
374 : : Relation rel;
375 : : Buffer bufs[PG_IOV_MAX];
376 : : BufferDesc *buf_hdrs[PG_IOV_MAX];
377 : : Page pages[PG_IOV_MAX];
378 : 108 : uint8 srb_flags = 0;
379 : : PgAioReturn ior;
380 : : PgAioHandle *ioh;
381 : : PgAioWaitRef iow;
382 : : SMgrRelation smgr;
383 : :
384 [ + - - + ]: 108 : if (nblocks <= 0 || nblocks > PG_IOV_MAX)
260 andres@anarazel.de 385 [ # # ]:UBC 0 : elog(ERROR, "nblocks is out of range");
386 : :
260 andres@anarazel.de 387 :CBC 108 : rel = relation_open(relid, AccessExclusiveLock);
388 : :
389 [ + + ]: 294 : for (int i = 0; i < nblocks; i++)
390 : : {
391 : 186 : bufs[i] = create_toy_buffer(rel, blkno + i);
392 : 186 : pages[i] = BufferGetBlock(bufs[i]);
393 : 186 : buf_hdrs[i] = BufferIsLocal(bufs[i]) ?
394 [ + + ]: 186 : GetLocalBufferDescriptor(-bufs[i] - 1) :
395 : 123 : GetBufferDescriptor(bufs[i] - 1);
396 : : }
397 : :
398 : 108 : smgr = RelationGetSmgr(rel);
399 : :
400 : 108 : pgstat_prepare_report_checksum_failure(smgr->smgr_rlocator.locator.dbOid);
401 : :
402 : 108 : ioh = pgaio_io_acquire(CurrentResourceOwner, &ior);
403 : 108 : pgaio_io_get_wref(ioh, &iow);
404 : :
405 [ + + ]: 108 : if (RelationUsesLocalBuffers(rel))
406 : : {
407 [ + + ]: 96 : for (int i = 0; i < nblocks; i++)
408 : 63 : StartLocalBufferIO(buf_hdrs[i], true, false);
409 : 33 : pgaio_io_set_flag(ioh, PGAIO_HF_REFERENCES_LOCAL);
410 : : }
411 : : else
412 : : {
413 [ + + ]: 198 : for (int i = 0; i < nblocks; i++)
414 : 123 : StartBufferIO(buf_hdrs[i], true, false);
415 : : }
416 : :
417 : 108 : pgaio_io_set_handle_data_32(ioh, (uint32 *) bufs, nblocks);
418 : :
419 [ + + ]: 108 : if (zero_on_error | zero_damaged_pages)
420 : 33 : srb_flags |= READ_BUFFERS_ZERO_ON_ERROR;
421 [ + + ]: 108 : if (ignore_checksum_failure)
422 : 15 : srb_flags |= READ_BUFFERS_IGNORE_CHECKSUM_FAILURES;
423 : :
424 : 108 : pgaio_io_register_callbacks(ioh,
425 [ + + ]: 108 : RelationUsesLocalBuffers(rel) ?
426 : : PGAIO_HCB_LOCAL_BUFFER_READV :
427 : : PGAIO_HCB_SHARED_BUFFER_READV,
428 : : srb_flags);
429 : :
430 [ + + ]: 108 : if (batchmode_enter)
431 : 6 : pgaio_enter_batchmode();
432 : :
433 : 108 : smgrstartreadv(ioh, smgr, MAIN_FORKNUM, blkno,
434 : : (void *) pages, nblocks);
435 : :
436 [ + + ]: 108 : if (call_smgrreleaseall)
437 : 6 : smgrreleaseall();
438 : :
439 [ + + ]: 108 : if (batchmode_exit)
440 : 6 : pgaio_exit_batchmode();
441 : :
442 [ + + ]: 294 : for (int i = 0; i < nblocks; i++)
443 : 186 : ReleaseBuffer(bufs[i]);
444 : :
445 [ + + ]: 108 : if (wait_complete)
446 : : {
447 : 75 : pgaio_wref_wait(&iow);
448 : :
449 [ + + ]: 75 : if (ior.result.status != PGAIO_RS_OK)
450 : 66 : pgaio_result_report(ior.result,
451 : : &ior.target_data,
452 [ + + ]: 66 : ior.result.status == PGAIO_RS_ERROR ?
453 : : ERROR : WARNING);
454 : : }
455 : :
456 : 75 : relation_close(rel, NoLock);
457 : :
458 : 75 : PG_RETURN_VOID();
459 : : }
460 : :
461 : 12 : PG_FUNCTION_INFO_V1(invalidate_rel_block);
462 : : Datum
463 : 138 : invalidate_rel_block(PG_FUNCTION_ARGS)
464 : : {
465 : 138 : Oid relid = PG_GETARG_OID(0);
466 : 138 : BlockNumber blkno = PG_GETARG_UINT32(1);
467 : : Relation rel;
468 : : PrefetchBufferResult pr;
469 : : Buffer buf;
470 : :
471 : 138 : rel = relation_open(relid, AccessExclusiveLock);
472 : :
473 : : /*
474 : : * This is a gross hack, but there's no other API exposed that allows to
475 : : * get a buffer ID without actually reading the block in.
476 : : */
477 : 138 : pr = PrefetchBuffer(rel, MAIN_FORKNUM, blkno);
478 : 138 : buf = pr.recent_buffer;
479 : :
480 [ + + ]: 138 : if (BufferIsValid(buf))
481 : : {
482 : : /* if the buffer contents aren't valid, this'll return false */
483 [ + + ]: 117 : if (ReadRecentBuffer(rel->rd_locator, MAIN_FORKNUM, blkno, buf))
484 : : {
485 : 114 : BufferDesc *buf_hdr = BufferIsLocal(buf) ?
486 : 48 : GetLocalBufferDescriptor(-buf - 1)
487 [ + + ]: 114 : : GetBufferDescriptor(buf - 1);
488 : : bool flushed;
489 : :
490 : 114 : LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
491 : :
492 [ + + ]: 114 : if (pg_atomic_read_u32(&buf_hdr->state) & BM_DIRTY)
493 : : {
494 [ + + ]: 78 : if (BufferIsLocal(buf))
495 : 33 : FlushLocalBuffer(buf_hdr, NULL);
496 : : else
497 : 45 : FlushOneBuffer(buf);
498 : : }
499 : 114 : LockBuffer(buf, BUFFER_LOCK_UNLOCK);
500 : 114 : ReleaseBuffer(buf);
501 : :
502 [ + + ]: 114 : if (BufferIsLocal(buf))
503 : 48 : InvalidateLocalBuffer(GetLocalBufferDescriptor(-buf - 1), true);
253 504 [ - + ]: 66 : else if (!EvictUnpinnedBuffer(buf, &flushed))
260 andres@anarazel.de 505 [ # # ]:UBC 0 : elog(ERROR, "couldn't evict");
506 : : }
507 : : }
508 : :
260 andres@anarazel.de 509 :CBC 138 : relation_close(rel, AccessExclusiveLock);
510 : :
511 : 138 : PG_RETURN_VOID();
512 : : }
513 : :
514 : 9 : PG_FUNCTION_INFO_V1(buffer_create_toy);
515 : : Datum
516 : 9 : buffer_create_toy(PG_FUNCTION_ARGS)
517 : : {
518 : 9 : Oid relid = PG_GETARG_OID(0);
519 : 9 : BlockNumber blkno = PG_GETARG_UINT32(1);
520 : : Relation rel;
521 : : Buffer buf;
522 : :
523 : 9 : rel = relation_open(relid, AccessExclusiveLock);
524 : :
525 : 9 : buf = create_toy_buffer(rel, blkno);
526 : 9 : ReleaseBuffer(buf);
527 : :
528 : 9 : relation_close(rel, NoLock);
529 : :
530 : 9 : PG_RETURN_INT32(buf);
531 : : }
532 : :
533 : 12 : PG_FUNCTION_INFO_V1(buffer_call_start_io);
534 : : Datum
535 : 30 : buffer_call_start_io(PG_FUNCTION_ARGS)
536 : : {
537 : 30 : Buffer buf = PG_GETARG_INT32(0);
538 : 30 : bool for_input = PG_GETARG_BOOL(1);
539 : 30 : bool nowait = PG_GETARG_BOOL(2);
540 : : bool can_start;
541 : :
542 [ + + ]: 30 : if (BufferIsLocal(buf))
543 : 12 : can_start = StartLocalBufferIO(GetLocalBufferDescriptor(-buf - 1),
544 : : for_input, nowait);
545 : : else
546 : 18 : can_start = StartBufferIO(GetBufferDescriptor(buf - 1),
547 : : for_input, nowait);
548 : :
549 : : /*
550 : : * For tests we don't want the resowner release preventing us from
551 : : * orchestrating odd scenarios.
552 : : */
553 [ + + + + ]: 30 : if (can_start && !BufferIsLocal(buf))
554 : 9 : ResourceOwnerForgetBufferIO(CurrentResourceOwner,
555 : : buf);
556 : :
557 [ + - ]: 30 : ereport(LOG,
558 : : errmsg("buffer %d after StartBufferIO: %s",
559 : : buf, DebugPrintBufferRefcount(buf)),
560 : : errhidestmt(true), errhidecontext(true));
561 : :
562 : 30 : PG_RETURN_BOOL(can_start);
563 : : }
564 : :
565 : 12 : PG_FUNCTION_INFO_V1(buffer_call_terminate_io);
566 : : Datum
567 : 15 : buffer_call_terminate_io(PG_FUNCTION_ARGS)
568 : : {
569 : 15 : Buffer buf = PG_GETARG_INT32(0);
570 : 15 : bool for_input = PG_GETARG_BOOL(1);
571 : 15 : bool succeed = PG_GETARG_BOOL(2);
572 : 15 : bool io_error = PG_GETARG_BOOL(3);
573 : 15 : bool release_aio = PG_GETARG_BOOL(4);
574 : 15 : bool clear_dirty = false;
575 : 15 : uint32 set_flag_bits = 0;
576 : :
577 [ - + ]: 15 : if (io_error)
260 andres@anarazel.de 578 :UBC 0 : set_flag_bits |= BM_IO_ERROR;
579 : :
260 andres@anarazel.de 580 [ + - ]:CBC 15 : if (for_input)
581 : : {
582 : 15 : clear_dirty = false;
583 : :
584 [ + + ]: 15 : if (succeed)
585 : 6 : set_flag_bits |= BM_VALID;
586 : : }
587 : : else
588 : : {
260 andres@anarazel.de 589 [ # # ]:UBC 0 : if (succeed)
590 : 0 : clear_dirty = true;
591 : : }
592 : :
260 andres@anarazel.de 593 [ + - ]:CBC 15 : ereport(LOG,
594 : : errmsg("buffer %d before Terminate[Local]BufferIO: %s",
595 : : buf, DebugPrintBufferRefcount(buf)),
596 : : errhidestmt(true), errhidecontext(true));
597 : :
598 [ + + ]: 15 : if (BufferIsLocal(buf))
599 : 6 : TerminateLocalBufferIO(GetLocalBufferDescriptor(-buf - 1),
600 : : clear_dirty, set_flag_bits, release_aio);
601 : : else
602 : 9 : TerminateBufferIO(GetBufferDescriptor(buf - 1),
603 : : clear_dirty, set_flag_bits, false, release_aio);
604 : :
605 [ + - ]: 15 : ereport(LOG,
606 : : errmsg("buffer %d after Terminate[Local]BufferIO: %s",
607 : : buf, DebugPrintBufferRefcount(buf)),
608 : : errhidestmt(true), errhidecontext(true));
609 : :
610 : 15 : PG_RETURN_VOID();
611 : : }
612 : :
613 : 9 : PG_FUNCTION_INFO_V1(handle_get);
614 : : Datum
615 : 18 : handle_get(PG_FUNCTION_ARGS)
616 : : {
617 : 18 : last_handle = pgaio_io_acquire(CurrentResourceOwner, NULL);
618 : :
619 : 18 : PG_RETURN_VOID();
620 : : }
621 : :
622 : 9 : PG_FUNCTION_INFO_V1(handle_release_last);
623 : : Datum
624 : 6 : handle_release_last(PG_FUNCTION_ARGS)
625 : : {
626 [ - + ]: 6 : if (!last_handle)
260 andres@anarazel.de 627 [ # # ]:UBC 0 : elog(ERROR, "no handle");
628 : :
260 andres@anarazel.de 629 :CBC 6 : pgaio_io_release(last_handle);
630 : :
631 : 3 : PG_RETURN_VOID();
632 : : }
633 : :
634 : 9 : PG_FUNCTION_INFO_V1(handle_get_and_error);
635 : : Datum
636 : 9 : handle_get_and_error(PG_FUNCTION_ARGS)
637 : : {
638 : 9 : pgaio_io_acquire(CurrentResourceOwner, NULL);
639 : :
640 [ + - ]: 9 : elog(ERROR, "as you command");
641 : : PG_RETURN_VOID();
642 : : }
643 : :
644 : 9 : PG_FUNCTION_INFO_V1(handle_get_twice);
645 : : Datum
646 : 3 : handle_get_twice(PG_FUNCTION_ARGS)
647 : : {
648 : 3 : pgaio_io_acquire(CurrentResourceOwner, NULL);
649 : 3 : pgaio_io_acquire(CurrentResourceOwner, NULL);
650 : :
260 andres@anarazel.de 651 :UBC 0 : PG_RETURN_VOID();
652 : : }
653 : :
260 andres@anarazel.de 654 :CBC 9 : PG_FUNCTION_INFO_V1(handle_get_release);
655 : : Datum
656 : 9 : handle_get_release(PG_FUNCTION_ARGS)
657 : : {
658 : : PgAioHandle *handle;
659 : :
660 : 9 : handle = pgaio_io_acquire(CurrentResourceOwner, NULL);
661 : 9 : pgaio_io_release(handle);
662 : :
663 : 9 : PG_RETURN_VOID();
664 : : }
665 : :
666 : 9 : PG_FUNCTION_INFO_V1(batch_start);
667 : : Datum
668 : 9 : batch_start(PG_FUNCTION_ARGS)
669 : : {
670 : 9 : pgaio_enter_batchmode();
671 : 9 : PG_RETURN_VOID();
672 : : }
673 : :
674 : 9 : PG_FUNCTION_INFO_V1(batch_end);
675 : : Datum
676 : 3 : batch_end(PG_FUNCTION_ARGS)
677 : : {
678 : 3 : pgaio_exit_batchmode();
679 : 3 : PG_RETURN_VOID();
680 : : }
681 : :
682 : : #ifdef USE_INJECTION_POINTS
683 : : extern PGDLLEXPORT void inj_io_short_read(const char *name,
684 : : const void *private_data,
685 : : void *arg);
686 : : extern PGDLLEXPORT void inj_io_reopen(const char *name,
687 : : const void *private_data,
688 : : void *arg);
689 : :
690 : : void
691 : : inj_io_short_read(const char *name, const void *private_data, void *arg)
692 : : {
693 : : PgAioHandle *ioh = (PgAioHandle *) arg;
694 : :
695 : : ereport(LOG,
696 : : errmsg("short read injection point called, is enabled: %d",
697 : : inj_io_error_state->enabled_reopen),
698 : : errhidestmt(true), errhidecontext(true));
699 : :
700 : : if (inj_io_error_state->enabled_short_read)
701 : : {
702 : : /*
703 : : * Only shorten reads that are actually longer than the target size,
704 : : * otherwise we can trigger over-reads.
705 : : */
706 : : if (inj_io_error_state->short_read_result_set
707 : : && ioh->op == PGAIO_OP_READV
708 : : && inj_io_error_state->short_read_result <= ioh->result)
709 : : {
710 : : struct iovec *iov = &pgaio_ctl->iovecs[ioh->iovec_off];
711 : : int32 old_result = ioh->result;
712 : : int32 new_result = inj_io_error_state->short_read_result;
713 : : int32 processed = 0;
714 : :
715 : : ereport(LOG,
716 : : errmsg("short read inject point, changing result from %d to %d",
717 : : old_result, new_result),
718 : : errhidestmt(true), errhidecontext(true));
719 : :
720 : : /*
721 : : * The underlying IO actually completed OK, and thus the "invalid"
722 : : * portion of the IOV actually contains valid data. That can hide
723 : : * a lot of problems, e.g. if we were to wrongly mark a buffer,
724 : : * that wasn't read according to the shortened-read, IO as valid,
725 : : * the contents would look valid and we might miss a bug.
726 : : *
727 : : * To avoid that, iterate through the IOV and zero out the
728 : : * "failed" portion of the IO.
729 : : */
730 : : for (int i = 0; i < ioh->op_data.read.iov_length; i++)
731 : : {
732 : : if (processed + iov[i].iov_len <= new_result)
733 : : processed += iov[i].iov_len;
734 : : else if (processed <= new_result)
735 : : {
736 : : uint32 ok_part = new_result - processed;
737 : :
738 : : memset((char *) iov[i].iov_base + ok_part, 0, iov[i].iov_len - ok_part);
739 : : processed += iov[i].iov_len;
740 : : }
741 : : else
742 : : {
743 : : memset((char *) iov[i].iov_base, 0, iov[i].iov_len);
744 : : }
745 : : }
746 : :
747 : : ioh->result = new_result;
748 : : }
749 : : }
750 : : }
751 : :
752 : : void
753 : : inj_io_reopen(const char *name, const void *private_data, void *arg)
754 : : {
755 : : ereport(LOG,
756 : : errmsg("reopen injection point called, is enabled: %d",
757 : : inj_io_error_state->enabled_reopen),
758 : : errhidestmt(true), errhidecontext(true));
759 : :
760 : : if (inj_io_error_state->enabled_reopen)
761 : : elog(ERROR, "injection point triggering failure to reopen ");
762 : : }
763 : : #endif
764 : :
765 : 6 : PG_FUNCTION_INFO_V1(inj_io_short_read_attach);
766 : : Datum
260 andres@anarazel.de 767 :UBC 0 : inj_io_short_read_attach(PG_FUNCTION_ARGS)
768 : : {
769 : : #ifdef USE_INJECTION_POINTS
770 : : inj_io_error_state->enabled_short_read = true;
771 : : inj_io_error_state->short_read_result_set = !PG_ARGISNULL(0);
772 : : if (inj_io_error_state->short_read_result_set)
773 : : inj_io_error_state->short_read_result = PG_GETARG_INT32(0);
774 : : #else
775 [ # # ]: 0 : elog(ERROR, "injection points not supported");
776 : : #endif
777 : :
778 : : PG_RETURN_VOID();
779 : : }
780 : :
260 andres@anarazel.de 781 :CBC 6 : PG_FUNCTION_INFO_V1(inj_io_short_read_detach);
782 : : Datum
260 andres@anarazel.de 783 :UBC 0 : inj_io_short_read_detach(PG_FUNCTION_ARGS)
784 : : {
785 : : #ifdef USE_INJECTION_POINTS
786 : : inj_io_error_state->enabled_short_read = false;
787 : : #else
788 [ # # ]: 0 : elog(ERROR, "injection points not supported");
789 : : #endif
790 : : PG_RETURN_VOID();
791 : : }
792 : :
260 andres@anarazel.de 793 :CBC 6 : PG_FUNCTION_INFO_V1(inj_io_reopen_attach);
794 : : Datum
260 andres@anarazel.de 795 :UBC 0 : inj_io_reopen_attach(PG_FUNCTION_ARGS)
796 : : {
797 : : #ifdef USE_INJECTION_POINTS
798 : : inj_io_error_state->enabled_reopen = true;
799 : : #else
800 [ # # ]: 0 : elog(ERROR, "injection points not supported");
801 : : #endif
802 : :
803 : : PG_RETURN_VOID();
804 : : }
805 : :
260 andres@anarazel.de 806 :CBC 6 : PG_FUNCTION_INFO_V1(inj_io_reopen_detach);
807 : : Datum
260 andres@anarazel.de 808 :UBC 0 : inj_io_reopen_detach(PG_FUNCTION_ARGS)
809 : : {
810 : : #ifdef USE_INJECTION_POINTS
811 : : inj_io_error_state->enabled_reopen = false;
812 : : #else
813 [ # # ]: 0 : elog(ERROR, "injection points not supported");
814 : : #endif
815 : : PG_RETURN_VOID();
816 : : }
|