Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * localbuf.c
4 : : * local buffer manager. Fast buffer manager for temporary tables,
5 : : * which never need to be WAL-logged or checkpointed, etc.
6 : : *
7 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
8 : : * Portions Copyright (c) 1994-5, Regents of the University of California
9 : : *
10 : : *
11 : : * IDENTIFICATION
12 : : * src/backend/storage/buffer/localbuf.c
13 : : *
14 : : *-------------------------------------------------------------------------
15 : : */
16 : : #include "postgres.h"
17 : :
18 : : #include "access/parallel.h"
19 : : #include "executor/instrument.h"
20 : : #include "pgstat.h"
21 : : #include "storage/aio.h"
22 : : #include "storage/buf_internals.h"
23 : : #include "storage/bufmgr.h"
24 : : #include "storage/fd.h"
25 : : #include "utils/guc_hooks.h"
26 : : #include "utils/memdebug.h"
27 : : #include "utils/memutils.h"
28 : : #include "utils/rel.h"
29 : : #include "utils/resowner.h"
30 : :
31 : :
32 : : /*#define LBDEBUG*/
33 : :
34 : : /* entry for buffer lookup hashtable */
35 : : typedef struct
36 : : {
37 : : BufferTag key; /* Tag of a disk page */
38 : : int id; /* Associated local buffer's index */
39 : : } LocalBufferLookupEnt;
40 : :
41 : : /* Note: this macro only works on local buffers, not shared ones! */
42 : : #define LocalBufHdrGetBlock(bufHdr) \
43 : : LocalBufferBlockPointers[-((bufHdr)->buf_id + 2)]
44 : :
45 : : int NLocBuffer = 0; /* until buffers are initialized */
46 : :
47 : : BufferDesc *LocalBufferDescriptors = NULL;
48 : : Block *LocalBufferBlockPointers = NULL;
49 : : int32 *LocalRefCount = NULL;
50 : :
51 : : static int nextFreeLocalBufId = 0;
52 : :
53 : : static HTAB *LocalBufHash = NULL;
54 : :
55 : : /* number of local buffers pinned at least once */
56 : : static int NLocalPinnedBuffers = 0;
57 : :
58 : :
59 : : static void InitLocalBuffers(void);
60 : : static Block GetLocalBufferStorage(void);
61 : : static Buffer GetLocalVictimBuffer(void);
62 : :
63 : :
64 : : /*
65 : : * PrefetchLocalBuffer -
66 : : * initiate asynchronous read of a block of a relation
67 : : *
68 : : * Do PrefetchBuffer's work for temporary relations.
69 : : * No-op if prefetching isn't compiled in.
70 : : */
71 : : PrefetchBufferResult
2218 tmunro@postgresql.or 72 :CBC 1535 : PrefetchLocalBuffer(SMgrRelation smgr, ForkNumber forkNum,
73 : : BlockNumber blockNum)
74 : : {
75 : 1535 : PrefetchBufferResult result = {InvalidBuffer, false};
76 : : BufferTag newTag; /* identity of requested block */
77 : : LocalBufferLookupEnt *hresult;
78 : :
1378 rhaas@postgresql.org 79 : 1535 : InitBufferTag(&newTag, &smgr->smgr_rlocator.locator, forkNum, blockNum);
80 : :
81 : : /* Initialize local buffers if first request in this session */
6322 tgl@sss.pgh.pa.us 82 [ - + ]: 1535 : if (LocalBufHash == NULL)
6322 tgl@sss.pgh.pa.us 83 :UBC 0 : InitLocalBuffers();
84 : :
85 : : /* See if the desired buffer already exists */
86 : : hresult = (LocalBufferLookupEnt *)
1184 peter@eisentraut.org 87 :CBC 1535 : hash_search(LocalBufHash, &newTag, HASH_FIND, NULL);
88 : :
6322 tgl@sss.pgh.pa.us 89 [ + + ]: 1535 : if (hresult)
90 : : {
91 : : /* Yes, so nothing to do */
2218 tmunro@postgresql.or 92 : 1211 : result.recent_buffer = -hresult->id - 1;
93 : : }
94 : : else
95 : : {
96 : : #ifdef USE_PREFETCH
97 : : /* Not in buffers, so initiate prefetch */
1123 tmunro@postgresql.or 98 [ + - + - ]:GBC 648 : if ((io_direct_flags & IO_DIRECT_DATA) == 0 &&
871 99 : 324 : smgrprefetch(smgr, forkNum, blockNum, 1))
100 : : {
1123 101 : 324 : result.initiated_io = true;
102 : : }
103 : : #endif /* USE_PREFETCH */
104 : : }
105 : :
2218 tmunro@postgresql.or 106 :CBC 1535 : return result;
107 : : }
108 : :
109 : :
110 : : /*
111 : : * LocalBufferAlloc -
112 : : * Find or create a local buffer for the given page of the given relation.
113 : : *
114 : : * API is similar to bufmgr.c's BufferAlloc, except that we do not need to do
115 : : * any locking since this is all local. We support only default access
116 : : * strategy (hence, usage_count is always advanced).
117 : : */
118 : : BufferDesc *
6476 heikki.linnakangas@i 119 : 1912003 : LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
120 : : bool *foundPtr)
121 : : {
122 : : BufferTag newTag; /* identity of requested block */
123 : : LocalBufferLookupEnt *hresult;
124 : : BufferDesc *bufHdr;
125 : : Buffer victim_buffer;
126 : : int bufid;
127 : : bool found;
128 : :
1378 rhaas@postgresql.org 129 : 1912003 : InitBufferTag(&newTag, &smgr->smgr_rlocator.locator, forkNum, blockNum);
130 : :
131 : : /* Initialize local buffers if first request in this session */
7717 tgl@sss.pgh.pa.us 132 [ + + ]: 1912003 : if (LocalBufHash == NULL)
133 : 17 : InitLocalBuffers();
134 : :
909 heikki.linnakangas@i 135 : 1912003 : ResourceOwnerEnlarge(CurrentResourceOwner);
136 : :
137 : : /* See if the desired buffer already exists */
138 : : hresult = (LocalBufferLookupEnt *)
1184 peter@eisentraut.org 139 : 1912003 : hash_search(LocalBufHash, &newTag, HASH_FIND, NULL);
140 : :
7717 tgl@sss.pgh.pa.us 141 [ + + ]: 1912003 : if (hresult)
142 : : {
1126 andres@anarazel.de 143 : 1900910 : bufid = hresult->id;
144 : 1900910 : bufHdr = GetLocalBufferDescriptor(bufid);
1378 rhaas@postgresql.org 145 [ - + ]: 1900910 : Assert(BufferTagsEqual(&bufHdr->tag, &newTag));
146 : :
1126 andres@anarazel.de 147 : 1900910 : *foundPtr = PinLocalBuffer(bufHdr, true);
148 : : }
149 : : else
150 : : {
151 : : uint64 buf_state;
152 : :
153 : 11093 : victim_buffer = GetLocalVictimBuffer();
154 : 11085 : bufid = -victim_buffer - 1;
155 : 11085 : bufHdr = GetLocalBufferDescriptor(bufid);
156 : :
157 : : hresult = (LocalBufferLookupEnt *)
158 : 11085 : hash_search(LocalBufHash, &newTag, HASH_ENTER, &found);
159 [ - + ]: 11085 : if (found) /* shouldn't happen */
1126 andres@anarazel.de 160 [ # # ]:UBC 0 : elog(ERROR, "local buffer hash table corrupted");
1126 andres@anarazel.de 161 :CBC 11085 : hresult->id = bufid;
162 : :
163 : : /*
164 : : * it's all ours now.
165 : : */
166 : 11085 : bufHdr->tag = newTag;
167 : :
110 andres@anarazel.de 168 :GNC 11085 : buf_state = pg_atomic_read_u64(&bufHdr->state);
1126 andres@anarazel.de 169 :CBC 11085 : buf_state &= ~(BUF_FLAG_MASK | BUF_USAGECOUNT_MASK);
170 : 11085 : buf_state |= BM_TAG_VALID | BUF_USAGECOUNT_ONE;
110 andres@anarazel.de 171 :GNC 11085 : pg_atomic_unlocked_write_u64(&bufHdr->state, buf_state);
172 : :
1126 andres@anarazel.de 173 :CBC 11085 : *foundPtr = false;
174 : : }
175 : :
176 : 1911995 : return bufHdr;
177 : : }
178 : :
179 : : /*
180 : : * Like FlushBuffer(), just for local buffers.
181 : : */
182 : : void
416 183 : 4863 : FlushLocalBuffer(BufferDesc *bufHdr, SMgrRelation reln)
184 : : {
185 : : instr_time io_start;
186 : 4863 : Page localpage = (char *) LocalBufHdrGetBlock(bufHdr);
187 : :
393 188 [ - + ]: 4863 : Assert(LocalRefCount[-BufferDescriptorGetBuffer(bufHdr) - 1] > 0);
189 : :
190 : : /*
191 : : * Try to start an I/O operation. There currently are no reasons for
192 : : * StartLocalBufferIO to return anything other than
193 : : * BUFFER_IO_READY_FOR_IO, so we raise an error in that case.
194 : : */
39 andres@anarazel.de 195 [ - + ]:GNC 4863 : if (StartLocalBufferIO(bufHdr, false, true, NULL) != BUFFER_IO_READY_FOR_IO)
416 andres@anarazel.de 196 [ # # ]:UBC 0 : elog(ERROR, "failed to start write IO on local buffer");
197 : :
198 : : /* Find smgr relation for buffer */
416 andres@anarazel.de 199 [ + + ]:CBC 4863 : if (reln == NULL)
200 : 4471 : reln = smgropen(BufTagGetRelFileLocator(&bufHdr->tag),
201 : : MyProcNumber);
202 : :
39 andres@anarazel.de 203 :GNC 4863 : PageSetChecksum(localpage, bufHdr->tag.blockNum);
204 : :
416 andres@anarazel.de 205 :CBC 4863 : io_start = pgstat_prepare_io_time(track_io_timing);
206 : :
207 : : /* And write... */
208 : 4863 : smgrwrite(reln,
209 : 4863 : BufTagGetForkNum(&bufHdr->tag),
210 : : bufHdr->tag.blockNum,
211 : : localpage,
212 : : false);
213 : :
214 : : /* Temporary table I/O does not use Buffer Access Strategies */
215 : 4863 : pgstat_count_io_op_time(IOOBJECT_TEMP_RELATION, IOCONTEXT_NORMAL,
216 : : IOOP_WRITE, io_start, 1, BLCKSZ);
217 : :
218 : : /* Mark not-dirty */
401 219 : 4863 : TerminateLocalBufferIO(bufHdr, true, 0, false);
220 : :
416 221 : 4863 : pgBufferUsage.local_blks_written++;
222 : 4863 : }
223 : :
224 : : static Buffer
1126 225 : 31945 : GetLocalVictimBuffer(void)
226 : : {
227 : : int victim_bufid;
228 : : int trycounter;
229 : : BufferDesc *bufHdr;
230 : :
909 heikki.linnakangas@i 231 : 31945 : ResourceOwnerEnlarge(CurrentResourceOwner);
232 : :
233 : : /*
234 : : * Need to get a new buffer. We use a clock-sweep algorithm (essentially
235 : : * the same as what freelist.c does now...)
236 : : */
7732 tgl@sss.pgh.pa.us 237 : 31945 : trycounter = NLocBuffer;
238 : : for (;;)
239 : : {
1126 andres@anarazel.de 240 : 171691 : victim_bufid = nextFreeLocalBufId;
241 : :
242 [ + + ]: 171691 : if (++nextFreeLocalBufId >= NLocBuffer)
243 : 1484 : nextFreeLocalBufId = 0;
244 : :
245 : 171691 : bufHdr = GetLocalBufferDescriptor(victim_bufid);
246 : :
247 [ + + ]: 171691 : if (LocalRefCount[victim_bufid] == 0)
248 : : {
110 andres@anarazel.de 249 :GNC 58723 : uint64 buf_state = pg_atomic_read_u64(&bufHdr->state);
250 : :
3677 andres@anarazel.de 251 [ + + ]:CBC 58723 : if (BUF_STATE_GET_USAGECOUNT(buf_state) > 0)
252 : : {
253 : 26786 : buf_state -= BUF_USAGECOUNT_ONE;
110 andres@anarazel.de 254 :GNC 26786 : pg_atomic_unlocked_write_u64(&bufHdr->state, buf_state);
6915 tgl@sss.pgh.pa.us 255 :CBC 26786 : trycounter = NLocBuffer;
256 : : }
402 andres@anarazel.de 257 [ + - ]: 31937 : else if (BUF_STATE_GET_REFCOUNT(buf_state) > 0)
258 : : {
259 : : /*
260 : : * This can be reached if the backend initiated AIO for this
261 : : * buffer and then errored out.
262 : : */
263 : : }
264 : : else
265 : : {
266 : : /* Found a usable buffer */
1126 267 : 31937 : PinLocalBuffer(bufHdr, false);
6915 tgl@sss.pgh.pa.us 268 : 31937 : break;
269 : : }
270 : : }
7732 271 [ + + ]: 112968 : else if (--trycounter == 0)
272 [ + - ]: 8 : ereport(ERROR,
273 : : (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
274 : : errmsg("no empty local buffer available")));
275 : : }
276 : :
277 : : /*
278 : : * lazy memory allocation: allocate space on first use of a buffer.
279 : : */
1126 andres@anarazel.de 280 [ + + ]: 31937 : if (LocalBufHdrGetBlock(bufHdr) == NULL)
281 : : {
282 : : /* Set pointer for use by BufferGetBlock() macro */
283 : 22069 : LocalBufHdrGetBlock(bufHdr) = GetLocalBufferStorage();
284 : : }
285 : :
286 : : /*
287 : : * this buffer is not referenced but it might still be dirty. if that's
288 : : * the case, write it out before reusing it!
289 : : */
110 andres@anarazel.de 290 [ + + ]:GNC 31937 : if (pg_atomic_read_u64(&bufHdr->state) & BM_DIRTY)
416 andres@anarazel.de 291 :CBC 4360 : FlushLocalBuffer(bufHdr, NULL);
292 : :
293 : : /*
294 : : * Remove the victim buffer from the hashtable and mark as invalid.
295 : : */
110 andres@anarazel.de 296 [ + + ]:GNC 31937 : if (pg_atomic_read_u64(&bufHdr->state) & BM_TAG_VALID)
297 : : {
416 andres@anarazel.de 298 :CBC 8500 : InvalidateLocalBuffer(bufHdr, false);
299 : :
476 michael@paquier.xyz 300 : 8500 : pgstat_count_io_op(IOOBJECT_TEMP_RELATION, IOCONTEXT_NORMAL, IOOP_EVICT, 1, 0);
301 : : }
302 : :
1126 andres@anarazel.de 303 : 31937 : return BufferDescriptorGetBuffer(bufHdr);
304 : : }
305 : :
306 : : /* see GetPinLimit() */
307 : : uint32
417 tmunro@postgresql.or 308 : 42138 : GetLocalPinLimit(void)
309 : : {
310 : : /*
311 : : * Every backend has its own temporary buffers, but we leave headroom for
312 : : * concurrent pin-holders -- like multiple scans in the same query.
313 : : */
14 melanieplageman@gmai 314 :GNC 42138 : return num_temp_buffers / 4;
315 : : }
316 : :
317 : : /* see GetAdditionalPinLimit() */
318 : : uint32
417 tmunro@postgresql.or 319 :CBC 32850 : GetAdditionalLocalPinLimit(void)
320 : : {
14 melanieplageman@gmai 321 :GNC 32850 : uint32 total = GetLocalPinLimit();
322 : :
417 tmunro@postgresql.or 323 [ - + ]:CBC 32850 : Assert(NLocalPinnedBuffers <= num_temp_buffers);
324 : :
14 melanieplageman@gmai 325 [ + + ]:GNC 32850 : if (NLocalPinnedBuffers >= total)
326 : 5216 : return 0;
327 : 27634 : return total - NLocalPinnedBuffers;
328 : : }
329 : :
330 : : /* see LimitAdditionalPins() */
331 : : void
1126 andres@anarazel.de 332 :CBC 16012 : LimitAdditionalLocalPins(uint32 *additional_pins)
333 : : {
334 : : uint32 max_pins;
335 : :
336 [ + + ]: 16012 : if (*additional_pins <= 1)
337 : 15557 : return;
338 : :
339 : : /*
340 : : * In contrast to LimitAdditionalPins() other backends don't play a role
341 : : * here. We can allow up to NLocBuffer pins in total, but it might not be
342 : : * initialized yet so read num_temp_buffers.
343 : : */
762 tmunro@postgresql.or 344 : 455 : max_pins = (num_temp_buffers - NLocalPinnedBuffers);
345 : :
1126 andres@anarazel.de 346 [ - + ]: 455 : if (*additional_pins >= max_pins)
1126 andres@anarazel.de 347 :UBC 0 : *additional_pins = max_pins;
348 : : }
349 : :
350 : : /*
351 : : * Implementation of ExtendBufferedRelBy() and ExtendBufferedRelTo() for
352 : : * temporary buffers.
353 : : */
354 : : BlockNumber
986 tmunro@postgresql.or 355 :CBC 16012 : ExtendBufferedRelLocal(BufferManagerRelation bmr,
356 : : ForkNumber fork,
357 : : uint32 flags,
358 : : uint32 extend_by,
359 : : BlockNumber extend_upto,
360 : : Buffer *buffers,
361 : : uint32 *extended_by)
362 : : {
363 : : BlockNumber first_block;
364 : : instr_time io_start;
365 : :
366 : : /* Initialize local buffers if first request in this session */
1126 andres@anarazel.de 367 [ + + ]: 16012 : if (LocalBufHash == NULL)
368 : 343 : InitLocalBuffers();
369 : :
370 : 16012 : LimitAdditionalLocalPins(&extend_by);
371 : :
372 [ + + ]: 36864 : for (uint32 i = 0; i < extend_by; i++)
373 : : {
374 : : BufferDesc *buf_hdr;
375 : : Block buf_block;
376 : :
377 : 20852 : buffers[i] = GetLocalVictimBuffer();
378 : 20852 : buf_hdr = GetLocalBufferDescriptor(-buffers[i] - 1);
379 : 20852 : buf_block = LocalBufHdrGetBlock(buf_hdr);
380 : :
381 : : /* new buffers are zero-filled */
447 peter@eisentraut.org 382 [ + - + - : 20852 : MemSet(buf_block, 0, BLCKSZ);
+ - - + -
- ]
383 : : }
384 : :
196 alvherre@kurilemu.de 385 [ + - ]:GNC 16012 : first_block = smgrnblocks(BMR_GET_SMGR(bmr), fork);
386 : :
1126 andres@anarazel.de 387 [ + + ]:CBC 16012 : if (extend_upto != InvalidBlockNumber)
388 : : {
389 : : /*
390 : : * In contrast to shared relations, nothing could change the relation
391 : : * size concurrently. Thus we shouldn't end up finding that we don't
392 : : * need to do anything.
393 : : */
394 [ - + ]: 384 : Assert(first_block <= extend_upto);
395 : :
396 [ - + ]: 384 : Assert((uint64) first_block + extend_by <= extend_upto);
397 : : }
398 : :
399 : : /* Fail if relation is already at maximum possible length */
400 [ - + ]: 16012 : if ((uint64) first_block + extend_by >= MaxBlockNumber)
1126 andres@anarazel.de 401 [ # # # # :UBC 0 : ereport(ERROR,
# # # # #
# ]
402 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
403 : : errmsg("cannot extend relation %s beyond %u blocks",
404 : : relpath(BMR_GET_SMGR(bmr)->smgr_rlocator, fork).str,
405 : : MaxBlockNumber)));
406 : :
959 peter@eisentraut.org 407 [ + + ]:CBC 36864 : for (uint32 i = 0; i < extend_by; i++)
408 : : {
409 : : int victim_buf_id;
410 : : BufferDesc *victim_buf_hdr;
411 : : BufferTag tag;
412 : : LocalBufferLookupEnt *hresult;
413 : : bool found;
414 : :
1126 andres@anarazel.de 415 : 20852 : victim_buf_id = -buffers[i] - 1;
416 : 20852 : victim_buf_hdr = GetLocalBufferDescriptor(victim_buf_id);
417 : :
418 : : /* in case we need to pin an existing buffer below */
823 heikki.linnakangas@i 419 : 20852 : ResourceOwnerEnlarge(CurrentResourceOwner);
420 : :
196 alvherre@kurilemu.de 421 [ + - ]:GNC 20852 : InitBufferTag(&tag, &BMR_GET_SMGR(bmr)->smgr_rlocator.locator, fork,
422 : : first_block + i);
423 : :
424 : : hresult = (LocalBufferLookupEnt *)
537 peter@eisentraut.org 425 :CBC 20852 : hash_search(LocalBufHash, &tag, HASH_ENTER, &found);
1126 andres@anarazel.de 426 [ - + ]: 20852 : if (found)
427 : : {
428 : : BufferDesc *existing_hdr;
429 : : uint64 buf_state;
430 : :
1126 andres@anarazel.de 431 :UBC 0 : UnpinLocalBuffer(BufferDescriptorGetBuffer(victim_buf_hdr));
432 : :
433 : 0 : existing_hdr = GetLocalBufferDescriptor(hresult->id);
434 : 0 : PinLocalBuffer(existing_hdr, false);
435 : 0 : buffers[i] = BufferDescriptorGetBuffer(existing_hdr);
436 : :
437 : : /*
438 : : * Clear the BM_VALID bit, do StartLocalBufferIO() and proceed.
439 : : */
110 andres@anarazel.de 440 :UNC 0 : buf_state = pg_atomic_read_u64(&existing_hdr->state);
1126 andres@anarazel.de 441 [ # # ]:UBC 0 : Assert(buf_state & BM_TAG_VALID);
442 [ # # ]: 0 : Assert(!(buf_state & BM_DIRTY));
851 michael@paquier.xyz 443 : 0 : buf_state &= ~BM_VALID;
110 andres@anarazel.de 444 :UNC 0 : pg_atomic_unlocked_write_u64(&existing_hdr->state, buf_state);
445 : :
446 : : /* no need to loop for local buffers */
39 447 : 0 : StartLocalBufferIO(existing_hdr, true, true, NULL);
448 : : }
449 : : else
450 : : {
110 andres@anarazel.de 451 :GNC 20852 : uint64 buf_state = pg_atomic_read_u64(&victim_buf_hdr->state);
452 : :
55 453 [ - + ]: 20852 : Assert(!(buf_state & (BM_VALID | BM_TAG_VALID | BM_DIRTY)));
454 : :
1126 andres@anarazel.de 455 :CBC 20852 : victim_buf_hdr->tag = tag;
456 : :
457 : 20852 : buf_state |= BM_TAG_VALID | BUF_USAGECOUNT_ONE;
458 : :
110 andres@anarazel.de 459 :GNC 20852 : pg_atomic_unlocked_write_u64(&victim_buf_hdr->state, buf_state);
460 : :
1126 andres@anarazel.de 461 :CBC 20852 : hresult->id = victim_buf_id;
462 : :
39 andres@anarazel.de 463 :GNC 20852 : StartLocalBufferIO(victim_buf_hdr, true, true, NULL);
464 : : }
465 : : }
466 : :
433 michael@paquier.xyz 467 :CBC 16012 : io_start = pgstat_prepare_io_time(track_io_timing);
468 : :
469 : : /* actually extend relation */
196 alvherre@kurilemu.de 470 [ + - ]:GNC 16012 : smgrzeroextend(BMR_GET_SMGR(bmr), fork, first_block, extend_by, false);
471 : :
1124 andres@anarazel.de 472 :CBC 16012 : pgstat_count_io_op_time(IOOBJECT_TEMP_RELATION, IOCONTEXT_NORMAL, IOOP_EXTEND,
476 michael@paquier.xyz 473 : 16012 : io_start, 1, extend_by * BLCKSZ);
474 : :
959 peter@eisentraut.org 475 [ + + ]: 36864 : for (uint32 i = 0; i < extend_by; i++)
476 : : {
1126 andres@anarazel.de 477 : 20852 : Buffer buf = buffers[i];
478 : : BufferDesc *buf_hdr;
479 : : uint64 buf_state;
480 : :
481 : 20852 : buf_hdr = GetLocalBufferDescriptor(-buf - 1);
482 : :
110 andres@anarazel.de 483 :GNC 20852 : buf_state = pg_atomic_read_u64(&buf_hdr->state);
1126 andres@anarazel.de 484 :CBC 20852 : buf_state |= BM_VALID;
110 andres@anarazel.de 485 :GNC 20852 : pg_atomic_unlocked_write_u64(&buf_hdr->state, buf_state);
486 : : }
487 : :
1126 andres@anarazel.de 488 :CBC 16012 : *extended_by = extend_by;
489 : :
965 490 : 16012 : pgBufferUsage.local_blks_written += extend_by;
491 : :
1126 492 : 16012 : return first_block;
493 : : }
494 : :
495 : : /*
496 : : * MarkLocalBufferDirty -
497 : : * mark a local buffer dirty
498 : : */
499 : : void
7340 tgl@sss.pgh.pa.us 500 : 2678984 : MarkLocalBufferDirty(Buffer buffer)
501 : : {
502 : : int bufid;
503 : : BufferDesc *bufHdr;
504 : : uint64 buf_state;
505 : :
10467 bruce@momjian.us 506 [ - + ]: 2678984 : Assert(BufferIsLocal(buffer));
507 : :
508 : : #ifdef LBDEBUG
509 : : fprintf(stderr, "LB DIRTY %d\n", buffer);
510 : : #endif
511 : :
1132 andres@anarazel.de 512 : 2678984 : bufid = -buffer - 1;
513 : :
7732 tgl@sss.pgh.pa.us 514 [ - + ]: 2678984 : Assert(LocalRefCount[bufid] > 0);
515 : :
4114 andres@anarazel.de 516 : 2678984 : bufHdr = GetLocalBufferDescriptor(bufid);
517 : :
110 andres@anarazel.de 518 :GNC 2678984 : buf_state = pg_atomic_read_u64(&bufHdr->state);
519 : :
3677 andres@anarazel.de 520 [ + + ]:CBC 2678984 : if (!(buf_state & BM_DIRTY))
521 : 20693 : pgBufferUsage.local_blks_dirtied++;
522 : :
3674 523 : 2678984 : buf_state |= BM_DIRTY;
524 : :
110 andres@anarazel.de 525 :GNC 2678984 : pg_atomic_unlocked_write_u64(&bufHdr->state, buf_state);
10892 scrappy@hub.org 526 :CBC 2678984 : }
527 : :
528 : : /*
529 : : * Like StartSharedBufferIO, but for local buffers
530 : : */
531 : : StartBufferIOResult
39 andres@anarazel.de 532 :GNC 36909 : StartLocalBufferIO(BufferDesc *bufHdr, bool forInput, bool wait, PgAioWaitRef *io_wref)
533 : : {
534 : : uint64 buf_state;
535 : :
536 : : /*
537 : : * With AIO the buffer could have IO in progress, e.g. when there are two
538 : : * scans of the same relation. Either wait for the other IO (if wait =
539 : : * true and io_wref == NULL) or return BUFFER_IO_IN_PROGRESS;
540 : : */
401 andres@anarazel.de 541 [ + + ]:CBC 36909 : if (pgaio_wref_valid(&bufHdr->io_wref))
542 : : {
39 andres@anarazel.de 543 :GNC 5 : PgAioWaitRef buf_wref = bufHdr->io_wref;
544 : :
545 [ + + ]: 5 : if (io_wref != NULL)
546 : : {
547 : : /* We've already asynchronously started this IO, so join it */
548 : 3 : *io_wref = buf_wref;
549 : 5 : return BUFFER_IO_IN_PROGRESS;
550 : : }
551 : :
552 : : /*
553 : : * For temp buffers we should never need to wait in
554 : : * StartLocalBufferIO() when called with io_wref == NULL while there
555 : : * are staged IOs, as it's not allowed to call code that is not aware
556 : : * of AIO while in batch mode.
557 : : */
558 [ - + ]: 2 : Assert(!pgaio_have_staged());
559 : :
560 [ + - ]: 2 : if (!wait)
561 : 2 : return BUFFER_IO_IN_PROGRESS;
562 : :
39 andres@anarazel.de 563 :UNC 0 : pgaio_wref_wait(&buf_wref);
564 : : }
565 : :
566 : : /* Once we get here, there is definitely no I/O active on this buffer */
567 : :
568 : : /* Check if someone else already did the I/O */
110 andres@anarazel.de 569 :GNC 36904 : buf_state = pg_atomic_read_u64(&bufHdr->state);
416 andres@anarazel.de 570 [ + + + + ]:CBC 36904 : if (forInput ? (buf_state & BM_VALID) : !(buf_state & BM_DIRTY))
571 : : {
39 andres@anarazel.de 572 :GNC 5 : return BUFFER_IO_ALREADY_DONE;
573 : : }
574 : :
575 : : /* BM_IO_IN_PROGRESS isn't currently used for local buffers */
576 : :
577 : : /* local buffers don't track IO using resowners */
578 : :
579 : 36899 : return BUFFER_IO_READY_FOR_IO;
580 : : }
581 : :
582 : : /*
583 : : * Like TerminateBufferIO, but for local buffers
584 : : */
585 : : void
110 586 : 16044 : TerminateLocalBufferIO(BufferDesc *bufHdr, bool clear_dirty, uint64 set_flag_bits,
587 : : bool release_aio)
588 : : {
589 : : /* Only need to adjust flags */
590 : 16044 : uint64 buf_state = pg_atomic_read_u64(&bufHdr->state);
591 : :
592 : : /* BM_IO_IN_PROGRESS isn't currently used for local buffers */
593 : :
594 : : /* Clear earlier errors, if this IO failed, it'll be marked again */
416 andres@anarazel.de 595 :CBC 16044 : buf_state &= ~BM_IO_ERROR;
596 : :
597 [ + + ]: 16044 : if (clear_dirty)
598 : 4863 : buf_state &= ~BM_DIRTY;
599 : :
401 600 [ + + ]: 16044 : if (release_aio)
601 : : {
602 : : /* release pin held by IO subsystem, see also buffer_stage_common() */
603 [ - + ]: 11130 : Assert(BUF_STATE_GET_REFCOUNT(buf_state) > 0);
604 : 11130 : buf_state -= BUF_REFCOUNT_ONE;
605 : 11130 : pgaio_wref_clear(&bufHdr->io_wref);
606 : : }
607 : :
416 608 : 16044 : buf_state |= set_flag_bits;
110 andres@anarazel.de 609 :GNC 16044 : pg_atomic_unlocked_write_u64(&bufHdr->state, buf_state);
610 : :
611 : : /* local buffers don't track IO using resowners */
612 : :
613 : : /* local buffers don't use the IO CV, as no other process can see buffer */
614 : :
615 : : /* local buffers don't use BM_PIN_COUNT_WAITER, so no need to wake */
416 andres@anarazel.de 616 :CBC 16044 : }
617 : :
618 : : /*
619 : : * InvalidateLocalBuffer -- mark a local buffer invalid.
620 : : *
621 : : * If check_unreferenced is true, error out if the buffer is still
622 : : * pinned. Passing false is appropriate when calling InvalidateLocalBuffer()
623 : : * as part of changing the identity of a buffer, instead of just dropping the
624 : : * buffer.
625 : : *
626 : : * See also InvalidateBuffer().
627 : : */
628 : : void
629 : 31937 : InvalidateLocalBuffer(BufferDesc *bufHdr, bool check_unreferenced)
630 : : {
631 : 31937 : Buffer buffer = BufferDescriptorGetBuffer(bufHdr);
632 : 31937 : int bufid = -buffer - 1;
633 : : uint64 buf_state;
634 : : LocalBufferLookupEnt *hresult;
635 : :
636 : : /*
637 : : * It's possible that we started IO on this buffer before e.g. aborting
638 : : * the transaction that created a table. We need to wait for that IO to
639 : : * complete before removing / reusing the buffer.
640 : : */
401 641 [ - + ]: 31937 : if (pgaio_wref_valid(&bufHdr->io_wref))
642 : : {
401 andres@anarazel.de 643 :UBC 0 : PgAioWaitRef iow = bufHdr->io_wref;
644 : :
645 : 0 : pgaio_wref_wait(&iow);
646 [ # # ]: 0 : Assert(!pgaio_wref_valid(&bufHdr->io_wref));
647 : : }
648 : :
110 andres@anarazel.de 649 :GNC 31937 : buf_state = pg_atomic_read_u64(&bufHdr->state);
650 : :
651 : : /*
652 : : * We need to test not just LocalRefCount[bufid] but also the BufferDesc
653 : : * itself, as the latter is used to represent a pin by the AIO subsystem.
654 : : * This can happen if AIO is initiated and then the query errors out.
655 : : */
402 andres@anarazel.de 656 [ + + ]:CBC 31937 : if (check_unreferenced &&
657 [ + - - + ]: 23437 : (LocalRefCount[bufid] != 0 || BUF_STATE_GET_REFCOUNT(buf_state) != 0))
336 peter@eisentraut.org 658 [ # # ]:UBC 0 : elog(ERROR, "block %u of %s is still referenced (local %d)",
659 : : bufHdr->tag.blockNum,
660 : : relpathbackend(BufTagGetRelFileLocator(&bufHdr->tag),
661 : : MyProcNumber,
662 : : BufTagGetForkNum(&bufHdr->tag)).str,
663 : : LocalRefCount[bufid]);
664 : :
665 : : /* Remove entry from hashtable */
666 : : hresult = (LocalBufferLookupEnt *)
416 andres@anarazel.de 667 :CBC 31937 : hash_search(LocalBufHash, &bufHdr->tag, HASH_REMOVE, NULL);
668 [ - + ]: 31937 : if (!hresult) /* shouldn't happen */
416 andres@anarazel.de 669 [ # # ]:UBC 0 : elog(ERROR, "local buffer hash table corrupted");
670 : : /* Mark buffer invalid */
416 andres@anarazel.de 671 :CBC 31937 : ClearBufferTag(&bufHdr->tag);
672 : 31937 : buf_state &= ~BUF_FLAG_MASK;
673 : 31937 : buf_state &= ~BUF_USAGECOUNT_MASK;
110 andres@anarazel.de 674 :GNC 31937 : pg_atomic_unlocked_write_u64(&bufHdr->state, buf_state);
416 andres@anarazel.de 675 :CBC 31937 : }
676 : :
677 : : /*
678 : : * DropRelationLocalBuffers
679 : : * This function removes from the buffer pool all the pages of the
680 : : * specified relation that have block numbers >= firstDelBlock.
681 : : * (In particular, with firstDelBlock = 0, all pages are removed.)
682 : : * Dirty pages are simply dropped, without bothering to write them
683 : : * out first. Therefore, this is NOT rollback-able, and so should be
684 : : * used only with extreme caution!
685 : : *
686 : : * See DropRelationBuffers in bufmgr.c for more notes.
687 : : */
688 : : void
305 fujii@postgresql.org 689 :GNC 498 : DropRelationLocalBuffers(RelFileLocator rlocator, ForkNumber *forkNum,
690 : : int nforks, BlockNumber *firstDelBlock)
691 : : {
692 : : int i;
693 : : int j;
694 : :
7474 tgl@sss.pgh.pa.us 695 [ + + ]:CBC 412146 : for (i = 0; i < NLocBuffer; i++)
696 : : {
4114 andres@anarazel.de 697 : 411648 : BufferDesc *bufHdr = GetLocalBufferDescriptor(i);
698 : : uint64 buf_state;
699 : :
110 andres@anarazel.de 700 :GNC 411648 : buf_state = pg_atomic_read_u64(&bufHdr->state);
701 : :
305 fujii@postgresql.org 702 [ + + ]: 411648 : if (!(buf_state & BM_TAG_VALID) ||
703 [ + + ]: 38156 : !BufTagMatchesRelFileLocator(&bufHdr->tag, &rlocator))
704 : 410530 : continue;
705 : :
706 [ + + ]: 1279 : for (j = 0; j < nforks; j++)
707 : : {
708 [ + + ]: 1229 : if (BufTagGetForkNum(&bufHdr->tag) == forkNum[j] &&
709 [ + + ]: 1110 : bufHdr->tag.blockNum >= firstDelBlock[j])
710 : : {
711 : 1068 : InvalidateLocalBuffer(bufHdr, true);
712 : 1068 : break;
713 : : }
714 : : }
715 : : }
7474 tgl@sss.pgh.pa.us 716 :CBC 498 : }
717 : :
718 : : /*
719 : : * DropRelationAllLocalBuffers
720 : : * This function removes from the buffer pool all pages of all forks
721 : : * of the specified relation.
722 : : *
723 : : * See DropRelationsAllBuffers in bufmgr.c for more notes.
724 : : */
725 : : void
1393 rhaas@postgresql.org 726 : 4407 : DropRelationAllLocalBuffers(RelFileLocator rlocator)
727 : : {
728 : : int i;
729 : :
5080 tgl@sss.pgh.pa.us 730 [ + + ]: 4131111 : for (i = 0; i < NLocBuffer; i++)
731 : : {
4114 andres@anarazel.de 732 : 4126704 : BufferDesc *bufHdr = GetLocalBufferDescriptor(i);
733 : : uint64 buf_state;
734 : :
110 andres@anarazel.de 735 :GNC 4126704 : buf_state = pg_atomic_read_u64(&bufHdr->state);
736 : :
3677 andres@anarazel.de 737 [ + + + + ]:CBC 4442679 : if ((buf_state & BM_TAG_VALID) &&
1350 rhaas@postgresql.org 738 : 315975 : BufTagMatchesRelFileLocator(&bufHdr->tag, &rlocator))
739 : : {
416 andres@anarazel.de 740 : 22132 : InvalidateLocalBuffer(bufHdr, true);
741 : : }
742 : : }
5080 tgl@sss.pgh.pa.us 743 : 4407 : }
744 : :
745 : : /*
746 : : * InitLocalBuffers -
747 : : * init the local buffer cache. Since most queries (esp. multi-user ones)
748 : : * don't involve local buffers, we delay allocating actual memory for the
749 : : * buffers until we need them; just make the buffer headers here.
750 : : */
751 : : static void
7717 752 : 360 : InitLocalBuffers(void)
753 : : {
754 : 360 : int nbufs = num_temp_buffers;
755 : : HASHCTL info;
756 : : int i;
757 : :
758 : : /*
759 : : * Parallel workers can't access data in temporary tables, because they
760 : : * have no visibility into the local buffers of their leader. This is a
761 : : * convenient, low-cost place to provide a backstop check for that. Note
762 : : * that we don't wish to prevent a parallel worker from accessing catalog
763 : : * metadata about a temp table, so checks at higher levels would be
764 : : * inappropriate.
765 : : */
3617 766 [ - + ]: 360 : if (IsParallelWorker())
3617 tgl@sss.pgh.pa.us 767 [ # # ]:UBC 0 : ereport(ERROR,
768 : : (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
769 : : errmsg("cannot access temporary tables during a parallel operation")));
770 : :
771 : : /* Allocate and zero buffer headers and auxiliary arrays */
7563 tgl@sss.pgh.pa.us 772 :CBC 360 : LocalBufferDescriptors = (BufferDesc *) calloc(nbufs, sizeof(BufferDesc));
773 : 360 : LocalBufferBlockPointers = (Block *) calloc(nbufs, sizeof(Block));
774 : 360 : LocalRefCount = (int32 *) calloc(nbufs, sizeof(int32));
775 [ + - + - : 360 : if (!LocalBufferDescriptors || !LocalBufferBlockPointers || !LocalRefCount)
- + ]
7563 tgl@sss.pgh.pa.us 776 [ # # ]:UBC 0 : ereport(FATAL,
777 : : (errcode(ERRCODE_OUT_OF_MEMORY),
778 : : errmsg("out of memory")));
779 : :
1126 andres@anarazel.de 780 :CBC 360 : nextFreeLocalBufId = 0;
781 : :
782 : : /* initialize fields that need to start off nonzero */
7717 tgl@sss.pgh.pa.us 783 [ + + ]: 342204 : for (i = 0; i < nbufs; i++)
784 : : {
4114 andres@anarazel.de 785 : 341844 : BufferDesc *buf = GetLocalBufferDescriptor(i);
786 : :
787 : : /*
788 : : * negative to indicate local buffer. This is tricky: shared buffers
789 : : * start with 0. We have to start with -2. (Note that the routine
790 : : * BufferDescriptorGetBuffer adds 1 to buf_id so our first buffer id
791 : : * is -1.)
792 : : */
10467 bruce@momjian.us 793 : 341844 : buf->buf_id = -i - 2;
794 : :
401 andres@anarazel.de 795 : 341844 : pgaio_wref_clear(&buf->io_wref);
796 : :
797 : : /*
798 : : * Intentionally do not initialize the buffer's atomic variable
799 : : * (besides zeroing the underlying memory above). That way we get
800 : : * errors on platforms without atomics, if somebody (re-)introduces
801 : : * atomic operations for local buffers.
802 : : */
803 : : }
804 : :
805 : : /* Create the lookup hash table */
7717 tgl@sss.pgh.pa.us 806 : 360 : info.keysize = sizeof(BufferTag);
807 : 360 : info.entrysize = sizeof(LocalBufferLookupEnt);
808 : :
809 : 360 : LocalBufHash = hash_create("Local Buffer Lookup Table",
810 : : nbufs,
811 : : &info,
812 : : HASH_ELEM | HASH_BLOBS);
813 : :
814 [ - + ]: 360 : if (!LocalBufHash)
7717 tgl@sss.pgh.pa.us 815 [ # # ]:UBC 0 : elog(ERROR, "could not initialize local buffer hash table");
816 : :
817 : : /* Initialization done, mark buffers allocated */
7717 tgl@sss.pgh.pa.us 818 :CBC 360 : NLocBuffer = nbufs;
10892 scrappy@hub.org 819 : 360 : }
820 : :
821 : : /*
822 : : * XXX: We could have a slightly more efficient version of PinLocalBuffer()
823 : : * that does not support adjusting the usagecount - but so far it does not
824 : : * seem worth the trouble.
825 : : *
826 : : * Note that ResourceOwnerEnlarge() must have been done already.
827 : : */
828 : : bool
1126 andres@anarazel.de 829 : 1933449 : PinLocalBuffer(BufferDesc *buf_hdr, bool adjust_usagecount)
830 : : {
831 : : uint64 buf_state;
832 : 1933449 : Buffer buffer = BufferDescriptorGetBuffer(buf_hdr);
833 : 1933449 : int bufid = -buffer - 1;
834 : :
110 andres@anarazel.de 835 :GNC 1933449 : buf_state = pg_atomic_read_u64(&buf_hdr->state);
836 : :
1126 andres@anarazel.de 837 [ + + ]:CBC 1933449 : if (LocalRefCount[bufid] == 0)
838 : : {
839 : 1823115 : NLocalPinnedBuffers++;
402 840 : 1823115 : buf_state += BUF_REFCOUNT_ONE;
1126 841 [ + + ]: 1823115 : if (adjust_usagecount &&
842 [ + + ]: 1790786 : BUF_STATE_GET_USAGECOUNT(buf_state) < BM_MAX_USAGE_COUNT)
843 : : {
844 : 89842 : buf_state += BUF_USAGECOUNT_ONE;
845 : : }
110 andres@anarazel.de 846 :GNC 1823115 : pg_atomic_unlocked_write_u64(&buf_hdr->state, buf_state);
847 : :
848 : : /*
849 : : * See comment in PinBuffer().
850 : : *
851 : : * If the buffer isn't allocated yet, it'll be marked as defined in
852 : : * GetLocalBufferStorage().
853 : : */
393 andres@anarazel.de 854 :CBC 1823115 : if (LocalBufHdrGetBlock(buf_hdr) != NULL)
855 : : VALGRIND_MAKE_MEM_DEFINED(LocalBufHdrGetBlock(buf_hdr), BLCKSZ);
856 : : }
1126 857 : 1933449 : LocalRefCount[bufid]++;
858 : 1933449 : ResourceOwnerRememberBuffer(CurrentResourceOwner,
859 : : BufferDescriptorGetBuffer(buf_hdr));
860 : :
861 : 1933449 : return buf_state & BM_VALID;
862 : : }
863 : :
864 : : void
865 : 2400650 : UnpinLocalBuffer(Buffer buffer)
866 : : {
909 heikki.linnakangas@i 867 : 2400650 : UnpinLocalBufferNoOwner(buffer);
868 : 2400650 : ResourceOwnerForgetBuffer(CurrentResourceOwner, buffer);
869 : 2400650 : }
870 : :
871 : : void
872 : 2404648 : UnpinLocalBufferNoOwner(Buffer buffer)
873 : : {
1126 andres@anarazel.de 874 : 2404648 : int buffid = -buffer - 1;
875 : :
876 [ - + ]: 2404648 : Assert(BufferIsLocal(buffer));
877 [ - + ]: 2404648 : Assert(LocalRefCount[buffid] > 0);
878 [ - + ]: 2404648 : Assert(NLocalPinnedBuffers > 0);
879 : :
880 [ + + ]: 2404648 : if (--LocalRefCount[buffid] == 0)
881 : : {
402 882 : 1823115 : BufferDesc *buf_hdr = GetLocalBufferDescriptor(buffid);
883 : : uint64 buf_state;
884 : :
1126 885 : 1823115 : NLocalPinnedBuffers--;
886 : :
110 andres@anarazel.de 887 :GNC 1823115 : buf_state = pg_atomic_read_u64(&buf_hdr->state);
402 andres@anarazel.de 888 [ - + ]:CBC 1823115 : Assert(BUF_STATE_GET_REFCOUNT(buf_state) > 0);
889 : 1823115 : buf_state -= BUF_REFCOUNT_ONE;
110 andres@anarazel.de 890 :GNC 1823115 : pg_atomic_unlocked_write_u64(&buf_hdr->state, buf_state);
891 : :
892 : : /* see comment in UnpinBufferNoOwner */
893 : : VALGRIND_MAKE_MEM_NOACCESS(LocalBufHdrGetBlock(buf_hdr), BLCKSZ);
894 : : }
1126 andres@anarazel.de 895 :CBC 2404648 : }
896 : :
897 : : /*
898 : : * GUC check_hook for temp_buffers
899 : : */
900 : : bool
1330 tgl@sss.pgh.pa.us 901 : 1305 : check_temp_buffers(int *newval, void **extra, GucSource source)
902 : : {
903 : : /*
904 : : * Once local buffers have been initialized, it's too late to change this.
905 : : * However, if this is only a test call, allow it.
906 : : */
907 [ + - - + : 1305 : if (source != PGC_S_TEST && NLocBuffer && NLocBuffer != *newval)
- - ]
908 : : {
718 peter@eisentraut.org 909 :UBC 0 : GUC_check_errdetail("\"temp_buffers\" cannot be changed after any temporary tables have been accessed in the session.");
1330 tgl@sss.pgh.pa.us 910 : 0 : return false;
911 : : }
1330 tgl@sss.pgh.pa.us 912 :CBC 1305 : return true;
913 : : }
914 : :
915 : : /*
916 : : * GetLocalBufferStorage - allocate memory for a local buffer
917 : : *
918 : : * The idea of this function is to aggregate our requests for storage
919 : : * so that the memory manager doesn't see a whole lot of relatively small
920 : : * requests. Since we'll never give back a local buffer once it's created
921 : : * within a particular process, no point in burdening memmgr with separately
922 : : * managed chunks.
923 : : */
924 : : static Block
7069 925 : 22069 : GetLocalBufferStorage(void)
926 : : {
927 : : static char *cur_block = NULL;
928 : : static int next_buf_in_block = 0;
929 : : static int num_bufs_in_block = 0;
930 : : static int total_bufs_allocated = 0;
931 : : static MemoryContext LocalBufferContext = NULL;
932 : :
933 : : char *this_buf;
934 : :
935 [ - + ]: 22069 : Assert(total_bufs_allocated < NLocBuffer);
936 : :
937 [ + + ]: 22069 : if (next_buf_in_block >= num_bufs_in_block)
938 : : {
939 : : /* Need to make a new request to memmgr */
940 : : int num_bufs;
941 : :
942 : : /*
943 : : * We allocate local buffers in a context of their own, so that the
944 : : * space eaten for them is easily recognizable in MemoryContextStats
945 : : * output. Create the context on first use.
946 : : */
5738 947 [ + + ]: 592 : if (LocalBufferContext == NULL)
948 : 360 : LocalBufferContext =
949 : 360 : AllocSetContextCreate(TopMemoryContext,
950 : : "LocalBufferContext",
951 : : ALLOCSET_DEFAULT_SIZES);
952 : :
953 : : /* Start with a 16-buffer request; subsequent ones double each time */
7069 954 : 592 : num_bufs = Max(num_bufs_in_block * 2, 16);
955 : : /* But not more than what we need for all remaining local bufs */
956 : 592 : num_bufs = Min(num_bufs, NLocBuffer - total_bufs_allocated);
957 : : /* And don't overflow MaxAllocSize, either */
958 [ + - ]: 592 : num_bufs = Min(num_bufs, MaxAllocSize / BLCKSZ);
959 : :
960 : : /* Buffers should be I/O aligned. */
276 tgl@sss.pgh.pa.us 961 :GNC 1184 : cur_block = MemoryContextAllocAligned(LocalBufferContext,
962 : 592 : num_bufs * BLCKSZ,
963 : : PG_IO_ALIGN_SIZE,
964 : : 0);
965 : :
7069 tgl@sss.pgh.pa.us 966 :CBC 592 : next_buf_in_block = 0;
967 : 592 : num_bufs_in_block = num_bufs;
968 : : }
969 : :
970 : : /* Allocate next buffer in current memory block */
971 : 22069 : this_buf = cur_block + next_buf_in_block * BLCKSZ;
972 : 22069 : next_buf_in_block++;
973 : 22069 : total_bufs_allocated++;
974 : :
975 : : /*
976 : : * Caller's PinLocalBuffer() was too early for Valgrind updates, so do it
977 : : * here. The block is actually undefined, but we want consistency with
978 : : * the regular case of not needing to allocate memory. This is
979 : : * specifically needed when method_io_uring.c fills the block, because
980 : : * Valgrind doesn't recognize io_uring reads causing undefined memory to
981 : : * become defined.
982 : : */
983 : : VALGRIND_MAKE_MEM_DEFINED(this_buf, BLCKSZ);
984 : :
985 : 22069 : return (Block) this_buf;
986 : : }
987 : :
988 : : /*
989 : : * CheckForLocalBufferLeaks - ensure this backend holds no local buffer pins
990 : : *
991 : : * This is just like CheckForBufferLeaks(), but for local buffers.
992 : : */
993 : : static void
4337 andres@anarazel.de 994 : 446260 : CheckForLocalBufferLeaks(void)
995 : : {
996 : : #ifdef USE_ASSERT_CHECKING
997 [ + + ]: 446260 : if (LocalRefCount)
998 : : {
4799 tgl@sss.pgh.pa.us 999 : 79602 : int RefCountErrors = 0;
1000 : : int i;
1001 : :
7575 1002 [ + + ]: 80784474 : for (i = 0; i < NLocBuffer; i++)
1003 : : {
4799 1004 [ - + ]: 80704872 : if (LocalRefCount[i] != 0)
1005 : : {
4724 bruce@momjian.us 1006 :UBC 0 : Buffer b = -i - 1;
1007 : : char *s;
1008 : :
909 heikki.linnakangas@i 1009 : 0 : s = DebugPrintBufferRefcount(b);
1010 [ # # ]: 0 : elog(WARNING, "local buffer refcount leak: %s", s);
1011 : 0 : pfree(s);
1012 : :
4799 tgl@sss.pgh.pa.us 1013 : 0 : RefCountErrors++;
1014 : : }
1015 : : }
4799 tgl@sss.pgh.pa.us 1016 [ - + ]:CBC 79602 : Assert(RefCountErrors == 0);
1017 : : }
1018 : : #endif
10892 scrappy@hub.org 1019 : 446260 : }
1020 : :
1021 : : /*
1022 : : * AtEOXact_LocalBuffers - clean up at end of transaction.
1023 : : *
1024 : : * This is just like AtEOXact_Buffers, but for local buffers.
1025 : : */
1026 : : void
4337 andres@anarazel.de 1027 : 423297 : AtEOXact_LocalBuffers(bool isCommit)
1028 : : {
1029 : 423297 : CheckForLocalBufferLeaks();
1030 : 423297 : }
1031 : :
1032 : : /*
1033 : : * AtProcExit_LocalBuffers - ensure we have dropped pins during backend exit.
1034 : : *
1035 : : * This is just like AtProcExit_Buffers, but for local buffers.
1036 : : */
1037 : : void
7718 tgl@sss.pgh.pa.us 1038 : 22963 : AtProcExit_LocalBuffers(void)
1039 : : {
1040 : : /*
1041 : : * We shouldn't be holding any remaining pins; if we are, and assertions
1042 : : * aren't enabled, we'll fail later in DropRelationBuffers while trying to
1043 : : * drop the temp rels.
1044 : : */
4337 andres@anarazel.de 1045 : 22963 : CheckForLocalBufferLeaks();
7718 tgl@sss.pgh.pa.us 1046 : 22963 : }
|