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 : :
158 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)
158 andres@anarazel.de 62 :UBC 0 : prev_shmem_request_hook();
63 : :
158 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)
158 andres@anarazel.de 73 :UBC 0 : prev_shmem_startup_hook();
74 : :
75 : : /* Create or attach to the shared memory state */
158 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)
158 andres@anarazel.de 125 :UBC 0 : return;
126 : :
158 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
158 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 : :
158 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
151 241 : 78 : EvictUnpinnedBuffer(buf, &flushed);
242 : :
243 : : /*
244 : : * Now modify the page as asked for by the caller.
245 : : */
158 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)
158 andres@anarazel.de 287 [ # # ]:UBC 0 : elog(ERROR, "could not corrupt checksum, what's going on?");
288 : : }
289 : : else
290 : : {
158 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;
313 : :
314 : : /* place buffer in shared buffers without erroring out */
315 : 195 : buf = ReadBufferExtended(rel, MAIN_FORKNUM, blkno, RBM_ZERO_AND_LOCK, NULL);
316 : 195 : LockBuffer(buf, BUFFER_LOCK_UNLOCK);
317 : :
318 [ + + ]: 195 : if (RelationUsesLocalBuffers(rel))
319 : : {
320 : 66 : buf_hdr = GetLocalBufferDescriptor(-buf - 1);
321 : 66 : buf_state = pg_atomic_read_u32(&buf_hdr->state);
322 : : }
323 : : else
324 : : {
325 : 129 : buf_hdr = GetBufferDescriptor(buf - 1);
326 : 129 : buf_state = LockBufHdr(buf_hdr);
327 : : }
328 : :
329 : : /*
330 : : * We should be the only backend accessing this buffer. This is just a
331 : : * small bit of belt-and-suspenders defense, none of this code should ever
332 : : * run in a cluster with real data.
333 : : */
334 [ - + ]: 195 : if (BUF_STATE_GET_REFCOUNT(buf_state) > 1)
158 andres@anarazel.de 335 :UBC 0 : was_pinned = true;
336 : : else
158 andres@anarazel.de 337 :CBC 195 : buf_state &= ~(BM_VALID | BM_DIRTY);
338 : :
339 [ + + ]: 195 : if (RelationUsesLocalBuffers(rel))
340 : 66 : pg_atomic_unlocked_write_u32(&buf_hdr->state, buf_state);
341 : : else
342 : 129 : UnlockBufHdr(buf_hdr, buf_state);
343 : :
344 [ - + ]: 195 : if (was_pinned)
158 andres@anarazel.de 345 [ # # ]:UBC 0 : elog(ERROR, "toy buffer %d was already pinned",
346 : : buf);
347 : :
158 andres@anarazel.de 348 :CBC 195 : return buf;
349 : : }
350 : :
351 : : /*
352 : : * A "low level" read. This does similar things to what
353 : : * StartReadBuffers()/WaitReadBuffers() do, but provides more control (and
354 : : * less sanity).
355 : : */
356 : 30 : PG_FUNCTION_INFO_V1(read_rel_block_ll);
357 : : Datum
358 : 108 : read_rel_block_ll(PG_FUNCTION_ARGS)
359 : : {
360 : 108 : Oid relid = PG_GETARG_OID(0);
361 : 108 : BlockNumber blkno = PG_GETARG_UINT32(1);
362 : 108 : int nblocks = PG_GETARG_INT32(2);
363 : 108 : bool wait_complete = PG_GETARG_BOOL(3);
364 : 108 : bool batchmode_enter = PG_GETARG_BOOL(4);
365 : 108 : bool call_smgrreleaseall = PG_GETARG_BOOL(5);
366 : 108 : bool batchmode_exit = PG_GETARG_BOOL(6);
367 : 108 : bool zero_on_error = PG_GETARG_BOOL(7);
368 : : Relation rel;
369 : : Buffer bufs[PG_IOV_MAX];
370 : : BufferDesc *buf_hdrs[PG_IOV_MAX];
371 : : Page pages[PG_IOV_MAX];
372 : 108 : uint8 srb_flags = 0;
373 : : PgAioReturn ior;
374 : : PgAioHandle *ioh;
375 : : PgAioWaitRef iow;
376 : : SMgrRelation smgr;
377 : :
378 [ + - - + ]: 108 : if (nblocks <= 0 || nblocks > PG_IOV_MAX)
158 andres@anarazel.de 379 [ # # ]:UBC 0 : elog(ERROR, "nblocks is out of range");
380 : :
158 andres@anarazel.de 381 :CBC 108 : rel = relation_open(relid, AccessExclusiveLock);
382 : :
383 [ + + ]: 294 : for (int i = 0; i < nblocks; i++)
384 : : {
385 : 186 : bufs[i] = create_toy_buffer(rel, blkno + i);
386 : 186 : pages[i] = BufferGetBlock(bufs[i]);
387 : 186 : buf_hdrs[i] = BufferIsLocal(bufs[i]) ?
388 [ + + ]: 186 : GetLocalBufferDescriptor(-bufs[i] - 1) :
389 : 123 : GetBufferDescriptor(bufs[i] - 1);
390 : : }
391 : :
392 : 108 : smgr = RelationGetSmgr(rel);
393 : :
394 : 108 : pgstat_prepare_report_checksum_failure(smgr->smgr_rlocator.locator.dbOid);
395 : :
396 : 108 : ioh = pgaio_io_acquire(CurrentResourceOwner, &ior);
397 : 108 : pgaio_io_get_wref(ioh, &iow);
398 : :
399 [ + + ]: 108 : if (RelationUsesLocalBuffers(rel))
400 : : {
401 [ + + ]: 96 : for (int i = 0; i < nblocks; i++)
402 : 63 : StartLocalBufferIO(buf_hdrs[i], true, false);
403 : 33 : pgaio_io_set_flag(ioh, PGAIO_HF_REFERENCES_LOCAL);
404 : : }
405 : : else
406 : : {
407 [ + + ]: 198 : for (int i = 0; i < nblocks; i++)
408 : 123 : StartBufferIO(buf_hdrs[i], true, false);
409 : : }
410 : :
411 : 108 : pgaio_io_set_handle_data_32(ioh, (uint32 *) bufs, nblocks);
412 : :
413 [ + + ]: 108 : if (zero_on_error | zero_damaged_pages)
414 : 33 : srb_flags |= READ_BUFFERS_ZERO_ON_ERROR;
415 [ + + ]: 108 : if (ignore_checksum_failure)
416 : 15 : srb_flags |= READ_BUFFERS_IGNORE_CHECKSUM_FAILURES;
417 : :
418 : 108 : pgaio_io_register_callbacks(ioh,
419 [ + + ]: 108 : RelationUsesLocalBuffers(rel) ?
420 : : PGAIO_HCB_LOCAL_BUFFER_READV :
421 : : PGAIO_HCB_SHARED_BUFFER_READV,
422 : : srb_flags);
423 : :
424 [ + + ]: 108 : if (batchmode_enter)
425 : 6 : pgaio_enter_batchmode();
426 : :
427 : 108 : smgrstartreadv(ioh, smgr, MAIN_FORKNUM, blkno,
428 : : (void *) pages, nblocks);
429 : :
430 [ + + ]: 108 : if (call_smgrreleaseall)
431 : 6 : smgrreleaseall();
432 : :
433 [ + + ]: 108 : if (batchmode_exit)
434 : 6 : pgaio_exit_batchmode();
435 : :
436 [ + + ]: 294 : for (int i = 0; i < nblocks; i++)
437 : 186 : ReleaseBuffer(bufs[i]);
438 : :
439 [ + + ]: 108 : if (wait_complete)
440 : : {
441 : 75 : pgaio_wref_wait(&iow);
442 : :
443 [ + + ]: 75 : if (ior.result.status != PGAIO_RS_OK)
444 : 66 : pgaio_result_report(ior.result,
445 : : &ior.target_data,
446 [ + + ]: 66 : ior.result.status == PGAIO_RS_ERROR ?
447 : : ERROR : WARNING);
448 : : }
449 : :
450 : 75 : relation_close(rel, NoLock);
451 : :
452 : 75 : PG_RETURN_VOID();
453 : : }
454 : :
455 : 12 : PG_FUNCTION_INFO_V1(invalidate_rel_block);
456 : : Datum
457 : 138 : invalidate_rel_block(PG_FUNCTION_ARGS)
458 : : {
459 : 138 : Oid relid = PG_GETARG_OID(0);
460 : 138 : BlockNumber blkno = PG_GETARG_UINT32(1);
461 : : Relation rel;
462 : : PrefetchBufferResult pr;
463 : : Buffer buf;
464 : :
465 : 138 : rel = relation_open(relid, AccessExclusiveLock);
466 : :
467 : : /*
468 : : * This is a gross hack, but there's no other API exposed that allows to
469 : : * get a buffer ID without actually reading the block in.
470 : : */
471 : 138 : pr = PrefetchBuffer(rel, MAIN_FORKNUM, blkno);
472 : 138 : buf = pr.recent_buffer;
473 : :
474 [ + + ]: 138 : if (BufferIsValid(buf))
475 : : {
476 : : /* if the buffer contents aren't valid, this'll return false */
477 [ + + ]: 117 : if (ReadRecentBuffer(rel->rd_locator, MAIN_FORKNUM, blkno, buf))
478 : : {
479 : 114 : BufferDesc *buf_hdr = BufferIsLocal(buf) ?
480 : 48 : GetLocalBufferDescriptor(-buf - 1)
481 [ + + ]: 114 : : GetBufferDescriptor(buf - 1);
482 : : bool flushed;
483 : :
484 : 114 : LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
485 : :
486 [ + + ]: 114 : if (pg_atomic_read_u32(&buf_hdr->state) & BM_DIRTY)
487 : : {
488 [ + + ]: 78 : if (BufferIsLocal(buf))
489 : 33 : FlushLocalBuffer(buf_hdr, NULL);
490 : : else
491 : 45 : FlushOneBuffer(buf);
492 : : }
493 : 114 : LockBuffer(buf, BUFFER_LOCK_UNLOCK);
494 : 114 : ReleaseBuffer(buf);
495 : :
496 [ + + ]: 114 : if (BufferIsLocal(buf))
497 : 48 : InvalidateLocalBuffer(GetLocalBufferDescriptor(-buf - 1), true);
151 498 [ - + ]: 66 : else if (!EvictUnpinnedBuffer(buf, &flushed))
158 andres@anarazel.de 499 [ # # ]:UBC 0 : elog(ERROR, "couldn't evict");
500 : : }
501 : : }
502 : :
158 andres@anarazel.de 503 :CBC 138 : relation_close(rel, AccessExclusiveLock);
504 : :
505 : 138 : PG_RETURN_VOID();
506 : : }
507 : :
508 : 9 : PG_FUNCTION_INFO_V1(buffer_create_toy);
509 : : Datum
510 : 9 : buffer_create_toy(PG_FUNCTION_ARGS)
511 : : {
512 : 9 : Oid relid = PG_GETARG_OID(0);
513 : 9 : BlockNumber blkno = PG_GETARG_UINT32(1);
514 : : Relation rel;
515 : : Buffer buf;
516 : :
517 : 9 : rel = relation_open(relid, AccessExclusiveLock);
518 : :
519 : 9 : buf = create_toy_buffer(rel, blkno);
520 : 9 : ReleaseBuffer(buf);
521 : :
522 : 9 : relation_close(rel, NoLock);
523 : :
524 : 9 : PG_RETURN_INT32(buf);
525 : : }
526 : :
527 : 12 : PG_FUNCTION_INFO_V1(buffer_call_start_io);
528 : : Datum
529 : 30 : buffer_call_start_io(PG_FUNCTION_ARGS)
530 : : {
531 : 30 : Buffer buf = PG_GETARG_INT32(0);
532 : 30 : bool for_input = PG_GETARG_BOOL(1);
533 : 30 : bool nowait = PG_GETARG_BOOL(2);
534 : : bool can_start;
535 : :
536 [ + + ]: 30 : if (BufferIsLocal(buf))
537 : 12 : can_start = StartLocalBufferIO(GetLocalBufferDescriptor(-buf - 1),
538 : : for_input, nowait);
539 : : else
540 : 18 : can_start = StartBufferIO(GetBufferDescriptor(buf - 1),
541 : : for_input, nowait);
542 : :
543 : : /*
544 : : * For tests we don't want the resowner release preventing us from
545 : : * orchestrating odd scenarios.
546 : : */
547 [ + + + + ]: 30 : if (can_start && !BufferIsLocal(buf))
548 : 9 : ResourceOwnerForgetBufferIO(CurrentResourceOwner,
549 : : buf);
550 : :
551 [ + - ]: 30 : ereport(LOG,
552 : : errmsg("buffer %d after StartBufferIO: %s",
553 : : buf, DebugPrintBufferRefcount(buf)),
554 : : errhidestmt(true), errhidecontext(true));
555 : :
556 : 30 : PG_RETURN_BOOL(can_start);
557 : : }
558 : :
559 : 12 : PG_FUNCTION_INFO_V1(buffer_call_terminate_io);
560 : : Datum
561 : 15 : buffer_call_terminate_io(PG_FUNCTION_ARGS)
562 : : {
563 : 15 : Buffer buf = PG_GETARG_INT32(0);
564 : 15 : bool for_input = PG_GETARG_BOOL(1);
565 : 15 : bool succeed = PG_GETARG_BOOL(2);
566 : 15 : bool io_error = PG_GETARG_BOOL(3);
567 : 15 : bool release_aio = PG_GETARG_BOOL(4);
568 : 15 : bool clear_dirty = false;
569 : 15 : uint32 set_flag_bits = 0;
570 : :
571 [ - + ]: 15 : if (io_error)
158 andres@anarazel.de 572 :UBC 0 : set_flag_bits |= BM_IO_ERROR;
573 : :
158 andres@anarazel.de 574 [ + - ]:CBC 15 : if (for_input)
575 : : {
576 : 15 : clear_dirty = false;
577 : :
578 [ + + ]: 15 : if (succeed)
579 : 6 : set_flag_bits |= BM_VALID;
580 : : }
581 : : else
582 : : {
158 andres@anarazel.de 583 [ # # ]:UBC 0 : if (succeed)
584 : 0 : clear_dirty = true;
585 : : }
586 : :
158 andres@anarazel.de 587 [ + - ]:CBC 15 : ereport(LOG,
588 : : errmsg("buffer %d before Terminate[Local]BufferIO: %s",
589 : : buf, DebugPrintBufferRefcount(buf)),
590 : : errhidestmt(true), errhidecontext(true));
591 : :
592 [ + + ]: 15 : if (BufferIsLocal(buf))
593 : 6 : TerminateLocalBufferIO(GetLocalBufferDescriptor(-buf - 1),
594 : : clear_dirty, set_flag_bits, release_aio);
595 : : else
596 : 9 : TerminateBufferIO(GetBufferDescriptor(buf - 1),
597 : : clear_dirty, set_flag_bits, false, release_aio);
598 : :
599 [ + - ]: 15 : ereport(LOG,
600 : : errmsg("buffer %d after Terminate[Local]BufferIO: %s",
601 : : buf, DebugPrintBufferRefcount(buf)),
602 : : errhidestmt(true), errhidecontext(true));
603 : :
604 : 15 : PG_RETURN_VOID();
605 : : }
606 : :
607 : 9 : PG_FUNCTION_INFO_V1(handle_get);
608 : : Datum
609 : 18 : handle_get(PG_FUNCTION_ARGS)
610 : : {
611 : 18 : last_handle = pgaio_io_acquire(CurrentResourceOwner, NULL);
612 : :
613 : 18 : PG_RETURN_VOID();
614 : : }
615 : :
616 : 9 : PG_FUNCTION_INFO_V1(handle_release_last);
617 : : Datum
618 : 6 : handle_release_last(PG_FUNCTION_ARGS)
619 : : {
620 [ - + ]: 6 : if (!last_handle)
158 andres@anarazel.de 621 [ # # ]:UBC 0 : elog(ERROR, "no handle");
622 : :
158 andres@anarazel.de 623 :CBC 6 : pgaio_io_release(last_handle);
624 : :
625 : 3 : PG_RETURN_VOID();
626 : : }
627 : :
628 : 9 : PG_FUNCTION_INFO_V1(handle_get_and_error);
629 : : Datum
630 : 9 : handle_get_and_error(PG_FUNCTION_ARGS)
631 : : {
632 : 9 : pgaio_io_acquire(CurrentResourceOwner, NULL);
633 : :
634 [ + - ]: 9 : elog(ERROR, "as you command");
635 : : PG_RETURN_VOID();
636 : : }
637 : :
638 : 9 : PG_FUNCTION_INFO_V1(handle_get_twice);
639 : : Datum
640 : 3 : handle_get_twice(PG_FUNCTION_ARGS)
641 : : {
642 : 3 : pgaio_io_acquire(CurrentResourceOwner, NULL);
643 : 3 : pgaio_io_acquire(CurrentResourceOwner, NULL);
644 : :
158 andres@anarazel.de 645 :UBC 0 : PG_RETURN_VOID();
646 : : }
647 : :
158 andres@anarazel.de 648 :CBC 9 : PG_FUNCTION_INFO_V1(handle_get_release);
649 : : Datum
650 : 9 : handle_get_release(PG_FUNCTION_ARGS)
651 : : {
652 : : PgAioHandle *handle;
653 : :
654 : 9 : handle = pgaio_io_acquire(CurrentResourceOwner, NULL);
655 : 9 : pgaio_io_release(handle);
656 : :
657 : 9 : PG_RETURN_VOID();
658 : : }
659 : :
660 : 9 : PG_FUNCTION_INFO_V1(batch_start);
661 : : Datum
662 : 9 : batch_start(PG_FUNCTION_ARGS)
663 : : {
664 : 9 : pgaio_enter_batchmode();
665 : 9 : PG_RETURN_VOID();
666 : : }
667 : :
668 : 9 : PG_FUNCTION_INFO_V1(batch_end);
669 : : Datum
670 : 3 : batch_end(PG_FUNCTION_ARGS)
671 : : {
672 : 3 : pgaio_exit_batchmode();
673 : 3 : PG_RETURN_VOID();
674 : : }
675 : :
676 : : #ifdef USE_INJECTION_POINTS
677 : : extern PGDLLEXPORT void inj_io_short_read(const char *name,
678 : : const void *private_data,
679 : : void *arg);
680 : : extern PGDLLEXPORT void inj_io_reopen(const char *name,
681 : : const void *private_data,
682 : : void *arg);
683 : :
684 : : void
685 : : inj_io_short_read(const char *name, const void *private_data, void *arg)
686 : : {
687 : : PgAioHandle *ioh = (PgAioHandle *) arg;
688 : :
689 : : ereport(LOG,
690 : : errmsg("short read injection point called, is enabled: %d",
691 : : inj_io_error_state->enabled_reopen),
692 : : errhidestmt(true), errhidecontext(true));
693 : :
694 : : if (inj_io_error_state->enabled_short_read)
695 : : {
696 : : /*
697 : : * Only shorten reads that are actually longer than the target size,
698 : : * otherwise we can trigger over-reads.
699 : : */
700 : : if (inj_io_error_state->short_read_result_set
701 : : && ioh->op == PGAIO_OP_READV
702 : : && inj_io_error_state->short_read_result <= ioh->result)
703 : : {
704 : : struct iovec *iov = &pgaio_ctl->iovecs[ioh->iovec_off];
705 : : int32 old_result = ioh->result;
706 : : int32 new_result = inj_io_error_state->short_read_result;
707 : : int32 processed = 0;
708 : :
709 : : ereport(LOG,
710 : : errmsg("short read inject point, changing result from %d to %d",
711 : : old_result, new_result),
712 : : errhidestmt(true), errhidecontext(true));
713 : :
714 : : /*
715 : : * The underlying IO actually completed OK, and thus the "invalid"
716 : : * portion of the IOV actually contains valid data. That can hide
717 : : * a lot of problems, e.g. if we were to wrongly mark a buffer,
718 : : * that wasn't read according to the shortened-read, IO as valid,
719 : : * the contents would look valid and we might miss a bug.
720 : : *
721 : : * To avoid that, iterate through the IOV and zero out the
722 : : * "failed" portion of the IO.
723 : : */
724 : : for (int i = 0; i < ioh->op_data.read.iov_length; i++)
725 : : {
726 : : if (processed + iov[i].iov_len <= new_result)
727 : : processed += iov[i].iov_len;
728 : : else if (processed <= new_result)
729 : : {
730 : : uint32 ok_part = new_result - processed;
731 : :
732 : : memset((char *) iov[i].iov_base + ok_part, 0, iov[i].iov_len - ok_part);
733 : : processed += iov[i].iov_len;
734 : : }
735 : : else
736 : : {
737 : : memset((char *) iov[i].iov_base, 0, iov[i].iov_len);
738 : : }
739 : : }
740 : :
741 : : ioh->result = new_result;
742 : : }
743 : : }
744 : : }
745 : :
746 : : void
747 : : inj_io_reopen(const char *name, const void *private_data, void *arg)
748 : : {
749 : : ereport(LOG,
750 : : errmsg("reopen injection point called, is enabled: %d",
751 : : inj_io_error_state->enabled_reopen),
752 : : errhidestmt(true), errhidecontext(true));
753 : :
754 : : if (inj_io_error_state->enabled_reopen)
755 : : elog(ERROR, "injection point triggering failure to reopen ");
756 : : }
757 : : #endif
758 : :
759 : 6 : PG_FUNCTION_INFO_V1(inj_io_short_read_attach);
760 : : Datum
158 andres@anarazel.de 761 :UBC 0 : inj_io_short_read_attach(PG_FUNCTION_ARGS)
762 : : {
763 : : #ifdef USE_INJECTION_POINTS
764 : : inj_io_error_state->enabled_short_read = true;
765 : : inj_io_error_state->short_read_result_set = !PG_ARGISNULL(0);
766 : : if (inj_io_error_state->short_read_result_set)
767 : : inj_io_error_state->short_read_result = PG_GETARG_INT32(0);
768 : : #else
769 [ # # ]: 0 : elog(ERROR, "injection points not supported");
770 : : #endif
771 : :
772 : : PG_RETURN_VOID();
773 : : }
774 : :
158 andres@anarazel.de 775 :CBC 6 : PG_FUNCTION_INFO_V1(inj_io_short_read_detach);
776 : : Datum
158 andres@anarazel.de 777 :UBC 0 : inj_io_short_read_detach(PG_FUNCTION_ARGS)
778 : : {
779 : : #ifdef USE_INJECTION_POINTS
780 : : inj_io_error_state->enabled_short_read = false;
781 : : #else
782 [ # # ]: 0 : elog(ERROR, "injection points not supported");
783 : : #endif
784 : : PG_RETURN_VOID();
785 : : }
786 : :
158 andres@anarazel.de 787 :CBC 6 : PG_FUNCTION_INFO_V1(inj_io_reopen_attach);
788 : : Datum
158 andres@anarazel.de 789 :UBC 0 : inj_io_reopen_attach(PG_FUNCTION_ARGS)
790 : : {
791 : : #ifdef USE_INJECTION_POINTS
792 : : inj_io_error_state->enabled_reopen = true;
793 : : #else
794 [ # # ]: 0 : elog(ERROR, "injection points not supported");
795 : : #endif
796 : :
797 : : PG_RETURN_VOID();
798 : : }
799 : :
158 andres@anarazel.de 800 :CBC 6 : PG_FUNCTION_INFO_V1(inj_io_reopen_detach);
801 : : Datum
158 andres@anarazel.de 802 :UBC 0 : inj_io_reopen_detach(PG_FUNCTION_ARGS)
803 : : {
804 : : #ifdef USE_INJECTION_POINTS
805 : : inj_io_error_state->enabled_reopen = false;
806 : : #else
807 [ # # ]: 0 : elog(ERROR, "injection points not supported");
808 : : #endif
809 : : PG_RETURN_VOID();
810 : : }
|