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-2024, 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/buf_internals.h"
22 : : #include "storage/bufmgr.h"
23 : : #include "storage/fd.h"
24 : : #include "utils/guc_hooks.h"
25 : : #include "utils/memutils.h"
26 : : #include "utils/resowner.h"
27 : :
28 : :
29 : : /*#define LBDEBUG*/
30 : :
31 : : /* entry for buffer lookup hashtable */
32 : : typedef struct
33 : : {
34 : : BufferTag key; /* Tag of a disk page */
35 : : int id; /* Associated local buffer's index */
36 : : } LocalBufferLookupEnt;
37 : :
38 : : /* Note: this macro only works on local buffers, not shared ones! */
39 : : #define LocalBufHdrGetBlock(bufHdr) \
40 : : LocalBufferBlockPointers[-((bufHdr)->buf_id + 2)]
41 : :
42 : : int NLocBuffer = 0; /* until buffers are initialized */
43 : :
44 : : BufferDesc *LocalBufferDescriptors = NULL;
45 : : Block *LocalBufferBlockPointers = NULL;
46 : : int32 *LocalRefCount = NULL;
47 : :
48 : : static int nextFreeLocalBufId = 0;
49 : :
50 : : static HTAB *LocalBufHash = NULL;
51 : :
52 : : /* number of local buffers pinned at least once */
53 : : static int NLocalPinnedBuffers = 0;
54 : :
55 : :
56 : : static void InitLocalBuffers(void);
57 : : static Block GetLocalBufferStorage(void);
58 : : static Buffer GetLocalVictimBuffer(void);
59 : :
60 : :
61 : : /*
62 : : * PrefetchLocalBuffer -
63 : : * initiate asynchronous read of a block of a relation
64 : : *
65 : : * Do PrefetchBuffer's work for temporary relations.
66 : : * No-op if prefetching isn't compiled in.
67 : : */
68 : : PrefetchBufferResult
1467 tmunro@postgresql.or 69 :CBC 3100 : PrefetchLocalBuffer(SMgrRelation smgr, ForkNumber forkNum,
70 : : BlockNumber blockNum)
71 : : {
72 : 3100 : PrefetchBufferResult result = {InvalidBuffer, false};
73 : : BufferTag newTag; /* identity of requested block */
74 : : LocalBufferLookupEnt *hresult;
75 : :
627 rhaas@postgresql.org 76 : 3100 : InitBufferTag(&newTag, &smgr->smgr_rlocator.locator, forkNum, blockNum);
77 : :
78 : : /* Initialize local buffers if first request in this session */
5571 tgl@sss.pgh.pa.us 79 [ - + ]: 3100 : if (LocalBufHash == NULL)
5571 tgl@sss.pgh.pa.us 80 :UBC 0 : InitLocalBuffers();
81 : :
82 : : /* See if the desired buffer already exists */
83 : : hresult = (LocalBufferLookupEnt *)
433 peter@eisentraut.org 84 :CBC 3100 : hash_search(LocalBufHash, &newTag, HASH_FIND, NULL);
85 : :
5571 tgl@sss.pgh.pa.us 86 [ + - ]: 3100 : if (hresult)
87 : : {
88 : : /* Yes, so nothing to do */
1467 tmunro@postgresql.or 89 : 3100 : result.recent_buffer = -hresult->id - 1;
90 : : }
91 : : else
92 : : {
93 : : #ifdef USE_PREFETCH
94 : : /* Not in buffers, so initiate prefetch */
372 tmunro@postgresql.or 95 [ # # # # ]:UBC 0 : if ((io_direct_flags & IO_DIRECT_DATA) == 0 &&
120 tmunro@postgresql.or 96 :UNC 0 : smgrprefetch(smgr, forkNum, blockNum, 1))
97 : : {
372 tmunro@postgresql.or 98 :UBC 0 : result.initiated_io = true;
99 : : }
100 : : #endif /* USE_PREFETCH */
101 : : }
102 : :
1467 tmunro@postgresql.or 103 :CBC 3100 : return result;
104 : : }
105 : :
106 : :
107 : : /*
108 : : * LocalBufferAlloc -
109 : : * Find or create a local buffer for the given page of the given relation.
110 : : *
111 : : * API is similar to bufmgr.c's BufferAlloc, except that we do not need to do
112 : : * any locking since this is all local. We support only default access
113 : : * strategy (hence, usage_count is always advanced).
114 : : */
115 : : BufferDesc *
5725 heikki.linnakangas@i 116 : 1254204 : LocalBufferAlloc(SMgrRelation smgr, ForkNumber forkNum, BlockNumber blockNum,
117 : : bool *foundPtr)
118 : : {
119 : : BufferTag newTag; /* identity of requested block */
120 : : LocalBufferLookupEnt *hresult;
121 : : BufferDesc *bufHdr;
122 : : Buffer victim_buffer;
123 : : int bufid;
124 : : bool found;
125 : :
627 rhaas@postgresql.org 126 : 1254204 : InitBufferTag(&newTag, &smgr->smgr_rlocator.locator, forkNum, blockNum);
127 : :
128 : : /* Initialize local buffers if first request in this session */
6966 tgl@sss.pgh.pa.us 129 [ + + ]: 1254204 : if (LocalBufHash == NULL)
130 : 13 : InitLocalBuffers();
131 : :
158 heikki.linnakangas@i 132 :GNC 1254204 : ResourceOwnerEnlarge(CurrentResourceOwner);
133 : :
134 : : /* See if the desired buffer already exists */
135 : : hresult = (LocalBufferLookupEnt *)
433 peter@eisentraut.org 136 :CBC 1254204 : hash_search(LocalBufHash, &newTag, HASH_FIND, NULL);
137 : :
6966 tgl@sss.pgh.pa.us 138 [ + + ]: 1254204 : if (hresult)
139 : : {
375 andres@anarazel.de 140 : 1250405 : bufid = hresult->id;
141 : 1250405 : bufHdr = GetLocalBufferDescriptor(bufid);
627 rhaas@postgresql.org 142 [ - + ]: 1250405 : Assert(BufferTagsEqual(&bufHdr->tag, &newTag));
143 : :
375 andres@anarazel.de 144 : 1250405 : *foundPtr = PinLocalBuffer(bufHdr, true);
145 : : }
146 : : else
147 : : {
148 : : uint32 buf_state;
149 : :
150 : 3799 : victim_buffer = GetLocalVictimBuffer();
151 : 3799 : bufid = -victim_buffer - 1;
152 : 3799 : bufHdr = GetLocalBufferDescriptor(bufid);
153 : :
154 : : hresult = (LocalBufferLookupEnt *)
155 : 3799 : hash_search(LocalBufHash, &newTag, HASH_ENTER, &found);
156 [ - + ]: 3799 : if (found) /* shouldn't happen */
375 andres@anarazel.de 157 [ # # ]:UBC 0 : elog(ERROR, "local buffer hash table corrupted");
375 andres@anarazel.de 158 :CBC 3799 : hresult->id = bufid;
159 : :
160 : : /*
161 : : * it's all ours now.
162 : : */
163 : 3799 : bufHdr->tag = newTag;
164 : :
165 : 3799 : buf_state = pg_atomic_read_u32(&bufHdr->state);
166 : 3799 : buf_state &= ~(BUF_FLAG_MASK | BUF_USAGECOUNT_MASK);
167 : 3799 : buf_state |= BM_TAG_VALID | BUF_USAGECOUNT_ONE;
168 : 3799 : pg_atomic_unlocked_write_u32(&bufHdr->state, buf_state);
169 : :
170 : 3799 : *foundPtr = false;
171 : : }
172 : :
173 : 1254204 : return bufHdr;
174 : : }
175 : :
176 : : static Buffer
177 : 16532 : GetLocalVictimBuffer(void)
178 : : {
179 : : int victim_bufid;
180 : : int trycounter;
181 : : uint32 buf_state;
182 : : BufferDesc *bufHdr;
183 : :
158 heikki.linnakangas@i 184 :GNC 16532 : ResourceOwnerEnlarge(CurrentResourceOwner);
185 : :
186 : : /*
187 : : * Need to get a new buffer. We use a clock sweep algorithm (essentially
188 : : * the same as what freelist.c does now...)
189 : : */
6981 tgl@sss.pgh.pa.us 190 :CBC 16532 : trycounter = NLocBuffer;
191 : : for (;;)
192 : : {
375 andres@anarazel.de 193 : 19739 : victim_bufid = nextFreeLocalBufId;
194 : :
195 [ + + ]: 19739 : if (++nextFreeLocalBufId >= NLocBuffer)
196 : 33 : nextFreeLocalBufId = 0;
197 : :
198 : 19739 : bufHdr = GetLocalBufferDescriptor(victim_bufid);
199 : :
200 [ + + ]: 19739 : if (LocalRefCount[victim_bufid] == 0)
201 : : {
2926 202 : 19682 : buf_state = pg_atomic_read_u32(&bufHdr->state);
203 : :
204 [ + + ]: 19682 : if (BUF_STATE_GET_USAGECOUNT(buf_state) > 0)
205 : : {
206 : 3150 : buf_state -= BUF_USAGECOUNT_ONE;
2746 207 : 3150 : pg_atomic_unlocked_write_u32(&bufHdr->state, buf_state);
6164 tgl@sss.pgh.pa.us 208 : 3150 : trycounter = NLocBuffer;
209 : : }
210 : : else
211 : : {
212 : : /* Found a usable buffer */
375 andres@anarazel.de 213 : 16532 : PinLocalBuffer(bufHdr, false);
6164 tgl@sss.pgh.pa.us 214 : 16532 : break;
215 : : }
216 : : }
6981 217 [ - + ]: 57 : else if (--trycounter == 0)
6981 tgl@sss.pgh.pa.us 218 [ # # ]:UBC 0 : ereport(ERROR,
219 : : (errcode(ERRCODE_INSUFFICIENT_RESOURCES),
220 : : errmsg("no empty local buffer available")));
221 : : }
222 : :
223 : : /*
224 : : * lazy memory allocation: allocate space on first use of a buffer.
225 : : */
375 andres@anarazel.de 226 [ + + ]:CBC 16532 : if (LocalBufHdrGetBlock(bufHdr) == NULL)
227 : : {
228 : : /* Set pointer for use by BufferGetBlock() macro */
229 : 15287 : LocalBufHdrGetBlock(bufHdr) = GetLocalBufferStorage();
230 : : }
231 : :
232 : : /*
233 : : * this buffer is not referenced but it might still be dirty. if that's
234 : : * the case, write it out before reusing it!
235 : : */
2926 236 [ + + ]: 16532 : if (buf_state & BM_DIRTY)
237 : : {
238 : : instr_time io_start;
239 : : SMgrRelation oreln;
3973 bruce@momjian.us 240 : 447 : Page localpage = (char *) LocalBufHdrGetBlock(bufHdr);
241 : :
242 : : /* Find smgr relation for buffer */
42 heikki.linnakangas@i 243 :GNC 447 : oreln = smgropen(BufTagGetRelFileLocator(&bufHdr->tag), MyProcNumber);
244 : :
4041 simon@2ndQuadrant.co 245 :CBC 447 : PageSetChecksumInplace(localpage, bufHdr->tag.blockNum);
246 : :
120 michael@paquier.xyz 247 :GNC 447 : io_start = pgstat_prepare_io_time(track_io_timing);
248 : :
249 : : /* And write... */
7034 tgl@sss.pgh.pa.us 250 :CBC 447 : smgrwrite(oreln,
599 rhaas@postgresql.org 251 : 447 : BufTagGetForkNum(&bufHdr->tag),
252 : : bufHdr->tag.blockNum,
253 : : localpage,
254 : : false);
255 : :
256 : : /* Temporary table I/O does not use Buffer Access Strategies */
373 andres@anarazel.de 257 : 447 : pgstat_count_io_op_time(IOOBJECT_TEMP_RELATION, IOCONTEXT_NORMAL,
258 : : IOOP_WRITE, io_start, 1);
259 : :
260 : : /* Mark not-dirty now in case we error out below */
2926 261 : 447 : buf_state &= ~BM_DIRTY;
2746 262 : 447 : pg_atomic_unlocked_write_u32(&bufHdr->state, buf_state);
263 : :
5234 rhaas@postgresql.org 264 : 447 : pgBufferUsage.local_blks_written++;
265 : : }
266 : :
267 : : /*
268 : : * Remove the victim buffer from the hashtable and mark as invalid.
269 : : */
2926 andres@anarazel.de 270 [ + + ]: 16532 : if (buf_state & BM_TAG_VALID)
271 : : {
272 : : LocalBufferLookupEnt *hresult;
273 : :
274 : : hresult = (LocalBufferLookupEnt *)
433 peter@eisentraut.org 275 : 450 : hash_search(LocalBufHash, &bufHdr->tag, HASH_REMOVE, NULL);
6756 bruce@momjian.us 276 [ - + ]: 450 : if (!hresult) /* shouldn't happen */
6966 tgl@sss.pgh.pa.us 277 [ # # ]:UBC 0 : elog(ERROR, "local buffer hash table corrupted");
278 : : /* mark buffer invalid just in case hash insert fails */
627 rhaas@postgresql.org 279 :CBC 450 : ClearBufferTag(&bufHdr->tag);
375 andres@anarazel.de 280 : 450 : buf_state &= ~(BUF_FLAG_MASK | BUF_USAGECOUNT_MASK);
2746 281 : 450 : pg_atomic_unlocked_write_u32(&bufHdr->state, buf_state);
430 282 : 450 : pgstat_count_io_op(IOOBJECT_TEMP_RELATION, IOCONTEXT_NORMAL, IOOP_EVICT);
283 : : }
284 : :
375 285 : 16532 : return BufferDescriptorGetBuffer(bufHdr);
286 : : }
287 : :
288 : : /* see LimitAdditionalPins() */
289 : : void
290 : 14388 : LimitAdditionalLocalPins(uint32 *additional_pins)
291 : : {
292 : : uint32 max_pins;
293 : :
294 [ + + ]: 14388 : if (*additional_pins <= 1)
295 : 8733 : return;
296 : :
297 : : /*
298 : : * In contrast to LimitAdditionalPins() other backends don't play a role
299 : : * here. We can allow up to NLocBuffer pins in total, but it might not be
300 : : * initialized yet so read num_temp_buffers.
301 : : */
11 tmunro@postgresql.or 302 :GNC 5655 : max_pins = (num_temp_buffers - NLocalPinnedBuffers);
303 : :
375 andres@anarazel.de 304 [ - + ]:CBC 5655 : if (*additional_pins >= max_pins)
375 andres@anarazel.de 305 :UBC 0 : *additional_pins = max_pins;
306 : : }
307 : :
308 : : /*
309 : : * Implementation of ExtendBufferedRelBy() and ExtendBufferedRelTo() for
310 : : * temporary buffers.
311 : : */
312 : : BlockNumber
235 tmunro@postgresql.or 313 :CBC 9044 : ExtendBufferedRelLocal(BufferManagerRelation bmr,
314 : : ForkNumber fork,
315 : : uint32 flags,
316 : : uint32 extend_by,
317 : : BlockNumber extend_upto,
318 : : Buffer *buffers,
319 : : uint32 *extended_by)
320 : : {
321 : : BlockNumber first_block;
322 : : instr_time io_start;
323 : :
324 : : /* Initialize local buffers if first request in this session */
375 andres@anarazel.de 325 [ + + ]: 9044 : if (LocalBufHash == NULL)
326 : 239 : InitLocalBuffers();
327 : :
328 : 9044 : LimitAdditionalLocalPins(&extend_by);
329 : :
330 [ + + ]: 21777 : for (uint32 i = 0; i < extend_by; i++)
331 : : {
332 : : BufferDesc *buf_hdr;
333 : : Block buf_block;
334 : :
335 : 12733 : buffers[i] = GetLocalVictimBuffer();
336 : 12733 : buf_hdr = GetLocalBufferDescriptor(-buffers[i] - 1);
337 : 12733 : buf_block = LocalBufHdrGetBlock(buf_hdr);
338 : :
339 : : /* new buffers are zero-filled */
340 [ + - + - : 12733 : MemSet((char *) buf_block, 0, BLCKSZ);
+ - - + -
- ]
341 : : }
342 : :
235 tmunro@postgresql.or 343 : 9044 : first_block = smgrnblocks(bmr.smgr, fork);
344 : :
375 andres@anarazel.de 345 [ + + ]: 9044 : if (extend_upto != InvalidBlockNumber)
346 : : {
347 : : /*
348 : : * In contrast to shared relations, nothing could change the relation
349 : : * size concurrently. Thus we shouldn't end up finding that we don't
350 : : * need to do anything.
351 : : */
352 [ - + ]: 133 : Assert(first_block <= extend_upto);
353 : :
354 [ - + ]: 133 : Assert((uint64) first_block + extend_by <= extend_upto);
355 : : }
356 : :
357 : : /* Fail if relation is already at maximum possible length */
358 [ - + ]: 9044 : if ((uint64) first_block + extend_by >= MaxBlockNumber)
375 andres@anarazel.de 359 [ # # ]:UBC 0 : ereport(ERROR,
360 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
361 : : errmsg("cannot extend relation %s beyond %u blocks",
362 : : relpath(bmr.smgr->smgr_rlocator, fork),
363 : : MaxBlockNumber)));
364 : :
208 peter@eisentraut.org 365 [ + + ]:GNC 21777 : for (uint32 i = 0; i < extend_by; i++)
366 : : {
367 : : int victim_buf_id;
368 : : BufferDesc *victim_buf_hdr;
369 : : BufferTag tag;
370 : : LocalBufferLookupEnt *hresult;
371 : : bool found;
372 : :
375 andres@anarazel.de 373 :CBC 12733 : victim_buf_id = -buffers[i] - 1;
374 : 12733 : victim_buf_hdr = GetLocalBufferDescriptor(victim_buf_id);
375 : :
376 : : /* in case we need to pin an existing buffer below */
72 heikki.linnakangas@i 377 :GNC 12733 : ResourceOwnerEnlarge(CurrentResourceOwner);
378 : :
235 tmunro@postgresql.or 379 :CBC 12733 : InitBufferTag(&tag, &bmr.smgr->smgr_rlocator.locator, fork, first_block + i);
380 : :
381 : : hresult = (LocalBufferLookupEnt *)
375 andres@anarazel.de 382 : 12733 : hash_search(LocalBufHash, (void *) &tag, HASH_ENTER, &found);
383 [ - + ]: 12733 : if (found)
384 : : {
385 : : BufferDesc *existing_hdr;
386 : : uint32 buf_state;
387 : :
375 andres@anarazel.de 388 :UBC 0 : UnpinLocalBuffer(BufferDescriptorGetBuffer(victim_buf_hdr));
389 : :
390 : 0 : existing_hdr = GetLocalBufferDescriptor(hresult->id);
391 : 0 : PinLocalBuffer(existing_hdr, false);
392 : 0 : buffers[i] = BufferDescriptorGetBuffer(existing_hdr);
393 : :
394 : 0 : buf_state = pg_atomic_read_u32(&existing_hdr->state);
395 [ # # ]: 0 : Assert(buf_state & BM_TAG_VALID);
396 [ # # ]: 0 : Assert(!(buf_state & BM_DIRTY));
100 michael@paquier.xyz 397 : 0 : buf_state &= ~BM_VALID;
375 andres@anarazel.de 398 : 0 : pg_atomic_unlocked_write_u32(&existing_hdr->state, buf_state);
399 : : }
400 : : else
401 : : {
375 andres@anarazel.de 402 :CBC 12733 : uint32 buf_state = pg_atomic_read_u32(&victim_buf_hdr->state);
403 : :
404 [ - + ]: 12733 : Assert(!(buf_state & (BM_VALID | BM_TAG_VALID | BM_DIRTY | BM_JUST_DIRTIED)));
405 : :
406 : 12733 : victim_buf_hdr->tag = tag;
407 : :
408 : 12733 : buf_state |= BM_TAG_VALID | BUF_USAGECOUNT_ONE;
409 : :
410 : 12733 : pg_atomic_unlocked_write_u32(&victim_buf_hdr->state, buf_state);
411 : :
412 : 12733 : hresult->id = victim_buf_id;
413 : : }
414 : : }
415 : :
120 michael@paquier.xyz 416 :GNC 9044 : io_start = pgstat_prepare_io_time(track_io_timing);
417 : :
418 : : /* actually extend relation */
235 tmunro@postgresql.or 419 :CBC 9044 : smgrzeroextend(bmr.smgr, fork, first_block, extend_by, false);
420 : :
373 andres@anarazel.de 421 : 9044 : pgstat_count_io_op_time(IOOBJECT_TEMP_RELATION, IOCONTEXT_NORMAL, IOOP_EXTEND,
422 : : io_start, extend_by);
423 : :
208 peter@eisentraut.org 424 [ + + ]:GNC 21777 : for (uint32 i = 0; i < extend_by; i++)
425 : : {
375 andres@anarazel.de 426 :CBC 12733 : Buffer buf = buffers[i];
427 : : BufferDesc *buf_hdr;
428 : : uint32 buf_state;
429 : :
430 : 12733 : buf_hdr = GetLocalBufferDescriptor(-buf - 1);
431 : :
432 : 12733 : buf_state = pg_atomic_read_u32(&buf_hdr->state);
433 : 12733 : buf_state |= BM_VALID;
434 : 12733 : pg_atomic_unlocked_write_u32(&buf_hdr->state, buf_state);
435 : : }
436 : :
437 : 9044 : *extended_by = extend_by;
438 : :
214 439 : 9044 : pgBufferUsage.local_blks_written += extend_by;
440 : :
375 441 : 9044 : return first_block;
442 : : }
443 : :
444 : : /*
445 : : * MarkLocalBufferDirty -
446 : : * mark a local buffer dirty
447 : : */
448 : : void
6589 tgl@sss.pgh.pa.us 449 : 1820389 : MarkLocalBufferDirty(Buffer buffer)
450 : : {
451 : : int bufid;
452 : : BufferDesc *bufHdr;
453 : : uint32 buf_state;
454 : :
9716 bruce@momjian.us 455 [ - + ]: 1820389 : Assert(BufferIsLocal(buffer));
456 : :
457 : : #ifdef LBDEBUG
458 : : fprintf(stderr, "LB DIRTY %d\n", buffer);
459 : : #endif
460 : :
381 andres@anarazel.de 461 : 1820389 : bufid = -buffer - 1;
462 : :
6981 tgl@sss.pgh.pa.us 463 [ - + ]: 1820389 : Assert(LocalRefCount[bufid] > 0);
464 : :
3363 andres@anarazel.de 465 : 1820389 : bufHdr = GetLocalBufferDescriptor(bufid);
466 : :
2923 467 : 1820389 : buf_state = pg_atomic_read_u32(&bufHdr->state);
468 : :
2926 469 [ + + ]: 1820389 : if (!(buf_state & BM_DIRTY))
470 : 11820 : pgBufferUsage.local_blks_dirtied++;
471 : :
2923 472 : 1820389 : buf_state |= BM_DIRTY;
473 : :
2746 474 : 1820389 : pg_atomic_unlocked_write_u32(&bufHdr->state, buf_state);
10141 scrappy@hub.org 475 : 1820389 : }
476 : :
477 : : /*
478 : : * DropRelationLocalBuffers
479 : : * This function removes from the buffer pool all the pages of the
480 : : * specified relation that have block numbers >= firstDelBlock.
481 : : * (In particular, with firstDelBlock = 0, all pages are removed.)
482 : : * Dirty pages are simply dropped, without bothering to write them
483 : : * out first. Therefore, this is NOT rollback-able, and so should be
484 : : * used only with extreme caution!
485 : : *
486 : : * See DropRelationBuffers in bufmgr.c for more notes.
487 : : */
488 : : void
642 rhaas@postgresql.org 489 : 346 : DropRelationLocalBuffers(RelFileLocator rlocator, ForkNumber forkNum,
490 : : BlockNumber firstDelBlock)
491 : : {
492 : : int i;
493 : :
6723 tgl@sss.pgh.pa.us 494 [ + + ]: 322906 : for (i = 0; i < NLocBuffer; i++)
495 : : {
3363 andres@anarazel.de 496 : 322560 : BufferDesc *bufHdr = GetLocalBufferDescriptor(i);
497 : : LocalBufferLookupEnt *hresult;
498 : : uint32 buf_state;
499 : :
2926 500 : 322560 : buf_state = pg_atomic_read_u32(&bufHdr->state);
501 : :
502 [ + + + + ]: 352710 : if ((buf_state & BM_TAG_VALID) &&
599 rhaas@postgresql.org 503 [ + + ]: 31035 : BufTagMatchesRelFileLocator(&bufHdr->tag, &rlocator) &&
504 : 885 : BufTagGetForkNum(&bufHdr->tag) == forkNum &&
6723 tgl@sss.pgh.pa.us 505 [ + + ]: 818 : bufHdr->tag.blockNum >= firstDelBlock)
506 : : {
507 [ - + ]: 792 : if (LocalRefCount[i] != 0)
5633 heikki.linnakangas@i 508 [ # # ]:UBC 0 : elog(ERROR, "block %u of %s is still referenced (local %u)",
509 : : bufHdr->tag.blockNum,
510 : : relpathbackend(BufTagGetRelFileLocator(&bufHdr->tag),
511 : : MyProcNumber,
512 : : BufTagGetForkNum(&bufHdr->tag)),
513 : : LocalRefCount[i]);
514 : :
515 : : /* Remove entry from hashtable */
516 : : hresult = (LocalBufferLookupEnt *)
433 peter@eisentraut.org 517 :CBC 792 : hash_search(LocalBufHash, &bufHdr->tag, HASH_REMOVE, NULL);
6718 bruce@momjian.us 518 [ - + ]: 792 : if (!hresult) /* shouldn't happen */
6723 tgl@sss.pgh.pa.us 519 [ # # ]:UBC 0 : elog(ERROR, "local buffer hash table corrupted");
520 : : /* Mark buffer invalid */
627 rhaas@postgresql.org 521 :CBC 792 : ClearBufferTag(&bufHdr->tag);
2926 andres@anarazel.de 522 : 792 : buf_state &= ~BUF_FLAG_MASK;
523 : 792 : buf_state &= ~BUF_USAGECOUNT_MASK;
2746 524 : 792 : pg_atomic_unlocked_write_u32(&bufHdr->state, buf_state);
525 : : }
526 : : }
6723 tgl@sss.pgh.pa.us 527 : 346 : }
528 : :
529 : : /*
530 : : * DropRelationAllLocalBuffers
531 : : * This function removes from the buffer pool all pages of all forks
532 : : * of the specified relation.
533 : : *
534 : : * See DropRelationsAllBuffers in bufmgr.c for more notes.
535 : : */
536 : : void
642 rhaas@postgresql.org 537 : 2903 : DropRelationAllLocalBuffers(RelFileLocator rlocator)
538 : : {
539 : : int i;
540 : :
4329 tgl@sss.pgh.pa.us 541 [ + + ]: 2809439 : for (i = 0; i < NLocBuffer; i++)
542 : : {
3363 andres@anarazel.de 543 : 2806536 : BufferDesc *bufHdr = GetLocalBufferDescriptor(i);
544 : : LocalBufferLookupEnt *hresult;
545 : : uint32 buf_state;
546 : :
2926 547 : 2806536 : buf_state = pg_atomic_read_u32(&bufHdr->state);
548 : :
549 [ + + + + ]: 2999985 : if ((buf_state & BM_TAG_VALID) &&
599 rhaas@postgresql.org 550 : 193449 : BufTagMatchesRelFileLocator(&bufHdr->tag, &rlocator))
551 : : {
4329 tgl@sss.pgh.pa.us 552 [ - + ]: 15288 : if (LocalRefCount[i] != 0)
4329 tgl@sss.pgh.pa.us 553 [ # # ]:UBC 0 : elog(ERROR, "block %u of %s is still referenced (local %u)",
554 : : bufHdr->tag.blockNum,
555 : : relpathbackend(BufTagGetRelFileLocator(&bufHdr->tag),
556 : : MyProcNumber,
557 : : BufTagGetForkNum(&bufHdr->tag)),
558 : : LocalRefCount[i]);
559 : : /* Remove entry from hashtable */
560 : : hresult = (LocalBufferLookupEnt *)
433 peter@eisentraut.org 561 :CBC 15288 : hash_search(LocalBufHash, &bufHdr->tag, HASH_REMOVE, NULL);
4329 tgl@sss.pgh.pa.us 562 [ - + ]: 15288 : if (!hresult) /* shouldn't happen */
4329 tgl@sss.pgh.pa.us 563 [ # # ]:UBC 0 : elog(ERROR, "local buffer hash table corrupted");
564 : : /* Mark buffer invalid */
627 rhaas@postgresql.org 565 :CBC 15288 : ClearBufferTag(&bufHdr->tag);
2926 andres@anarazel.de 566 : 15288 : buf_state &= ~BUF_FLAG_MASK;
567 : 15288 : buf_state &= ~BUF_USAGECOUNT_MASK;
2746 568 : 15288 : pg_atomic_unlocked_write_u32(&bufHdr->state, buf_state);
569 : : }
570 : : }
4329 tgl@sss.pgh.pa.us 571 : 2903 : }
572 : :
573 : : /*
574 : : * InitLocalBuffers -
575 : : * init the local buffer cache. Since most queries (esp. multi-user ones)
576 : : * don't involve local buffers, we delay allocating actual memory for the
577 : : * buffers until we need them; just make the buffer headers here.
578 : : */
579 : : static void
6966 580 : 252 : InitLocalBuffers(void)
581 : : {
582 : 252 : int nbufs = num_temp_buffers;
583 : : HASHCTL info;
584 : : int i;
585 : :
586 : : /*
587 : : * Parallel workers can't access data in temporary tables, because they
588 : : * have no visibility into the local buffers of their leader. This is a
589 : : * convenient, low-cost place to provide a backstop check for that. Note
590 : : * that we don't wish to prevent a parallel worker from accessing catalog
591 : : * metadata about a temp table, so checks at higher levels would be
592 : : * inappropriate.
593 : : */
2866 594 [ - + ]: 252 : if (IsParallelWorker())
2866 tgl@sss.pgh.pa.us 595 [ # # ]:UBC 0 : ereport(ERROR,
596 : : (errcode(ERRCODE_INVALID_TRANSACTION_STATE),
597 : : errmsg("cannot access temporary tables during a parallel operation")));
598 : :
599 : : /* Allocate and zero buffer headers and auxiliary arrays */
6812 tgl@sss.pgh.pa.us 600 :CBC 252 : LocalBufferDescriptors = (BufferDesc *) calloc(nbufs, sizeof(BufferDesc));
601 : 252 : LocalBufferBlockPointers = (Block *) calloc(nbufs, sizeof(Block));
602 : 252 : LocalRefCount = (int32 *) calloc(nbufs, sizeof(int32));
603 [ + - + - : 252 : if (!LocalBufferDescriptors || !LocalBufferBlockPointers || !LocalRefCount)
- + ]
6812 tgl@sss.pgh.pa.us 604 [ # # ]:UBC 0 : ereport(FATAL,
605 : : (errcode(ERRCODE_OUT_OF_MEMORY),
606 : : errmsg("out of memory")));
607 : :
375 andres@anarazel.de 608 :CBC 252 : nextFreeLocalBufId = 0;
609 : :
610 : : /* initialize fields that need to start off nonzero */
6966 tgl@sss.pgh.pa.us 611 [ + + ]: 255528 : for (i = 0; i < nbufs; i++)
612 : : {
3363 andres@anarazel.de 613 : 255276 : BufferDesc *buf = GetLocalBufferDescriptor(i);
614 : :
615 : : /*
616 : : * negative to indicate local buffer. This is tricky: shared buffers
617 : : * start with 0. We have to start with -2. (Note that the routine
618 : : * BufferDescriptorGetBuffer adds 1 to buf_id so our first buffer id
619 : : * is -1.)
620 : : */
9716 bruce@momjian.us 621 : 255276 : buf->buf_id = -i - 2;
622 : :
623 : : /*
624 : : * Intentionally do not initialize the buffer's atomic variable
625 : : * (besides zeroing the underlying memory above). That way we get
626 : : * errors on platforms without atomics, if somebody (re-)introduces
627 : : * atomic operations for local buffers.
628 : : */
629 : : }
630 : :
631 : : /* Create the lookup hash table */
6966 tgl@sss.pgh.pa.us 632 : 252 : info.keysize = sizeof(BufferTag);
633 : 252 : info.entrysize = sizeof(LocalBufferLookupEnt);
634 : :
635 : 252 : LocalBufHash = hash_create("Local Buffer Lookup Table",
636 : : nbufs,
637 : : &info,
638 : : HASH_ELEM | HASH_BLOBS);
639 : :
640 [ - + ]: 252 : if (!LocalBufHash)
6966 tgl@sss.pgh.pa.us 641 [ # # ]:UBC 0 : elog(ERROR, "could not initialize local buffer hash table");
642 : :
643 : : /* Initialization done, mark buffers allocated */
6966 tgl@sss.pgh.pa.us 644 :CBC 252 : NLocBuffer = nbufs;
10141 scrappy@hub.org 645 : 252 : }
646 : :
647 : : /*
648 : : * XXX: We could have a slightly more efficient version of PinLocalBuffer()
649 : : * that does not support adjusting the usagecount - but so far it does not
650 : : * seem worth the trouble.
651 : : *
652 : : * Note that ResourceOwnerEnlarge() must have been done already.
653 : : */
654 : : bool
375 andres@anarazel.de 655 : 1266937 : PinLocalBuffer(BufferDesc *buf_hdr, bool adjust_usagecount)
656 : : {
657 : : uint32 buf_state;
658 : 1266937 : Buffer buffer = BufferDescriptorGetBuffer(buf_hdr);
659 : 1266937 : int bufid = -buffer - 1;
660 : :
661 : 1266937 : buf_state = pg_atomic_read_u32(&buf_hdr->state);
662 : :
663 [ + + ]: 1266937 : if (LocalRefCount[bufid] == 0)
664 : : {
665 : 1182312 : NLocalPinnedBuffers++;
666 [ + + ]: 1182312 : if (adjust_usagecount &&
667 [ + + ]: 1165780 : BUF_STATE_GET_USAGECOUNT(buf_state) < BM_MAX_USAGE_COUNT)
668 : : {
669 : 54837 : buf_state += BUF_USAGECOUNT_ONE;
670 : 54837 : pg_atomic_unlocked_write_u32(&buf_hdr->state, buf_state);
671 : : }
672 : : }
673 : 1266937 : LocalRefCount[bufid]++;
674 : 1266937 : ResourceOwnerRememberBuffer(CurrentResourceOwner,
675 : : BufferDescriptorGetBuffer(buf_hdr));
676 : :
677 : 1266937 : return buf_state & BM_VALID;
678 : : }
679 : :
680 : : void
681 : 1613656 : UnpinLocalBuffer(Buffer buffer)
682 : : {
158 heikki.linnakangas@i 683 :GNC 1613656 : UnpinLocalBufferNoOwner(buffer);
684 : 1613656 : ResourceOwnerForgetBuffer(CurrentResourceOwner, buffer);
685 : 1613656 : }
686 : :
687 : : void
688 : 1614033 : UnpinLocalBufferNoOwner(Buffer buffer)
689 : : {
375 andres@anarazel.de 690 :CBC 1614033 : int buffid = -buffer - 1;
691 : :
692 [ - + ]: 1614033 : Assert(BufferIsLocal(buffer));
693 [ - + ]: 1614033 : Assert(LocalRefCount[buffid] > 0);
694 [ - + ]: 1614033 : Assert(NLocalPinnedBuffers > 0);
695 : :
696 [ + + ]: 1614033 : if (--LocalRefCount[buffid] == 0)
697 : 1182312 : NLocalPinnedBuffers--;
698 : 1614033 : }
699 : :
700 : : /*
701 : : * GUC check_hook for temp_buffers
702 : : */
703 : : bool
579 tgl@sss.pgh.pa.us 704 : 932 : check_temp_buffers(int *newval, void **extra, GucSource source)
705 : : {
706 : : /*
707 : : * Once local buffers have been initialized, it's too late to change this.
708 : : * However, if this is only a test call, allow it.
709 : : */
710 [ + - - + : 932 : if (source != PGC_S_TEST && NLocBuffer && NLocBuffer != *newval)
- - ]
711 : : {
136 michael@paquier.xyz 712 :UNC 0 : GUC_check_errdetail("temp_buffers cannot be changed after any temporary tables have been accessed in the session.");
579 tgl@sss.pgh.pa.us 713 :UBC 0 : return false;
714 : : }
579 tgl@sss.pgh.pa.us 715 :CBC 932 : return true;
716 : : }
717 : :
718 : : /*
719 : : * GetLocalBufferStorage - allocate memory for a local buffer
720 : : *
721 : : * The idea of this function is to aggregate our requests for storage
722 : : * so that the memory manager doesn't see a whole lot of relatively small
723 : : * requests. Since we'll never give back a local buffer once it's created
724 : : * within a particular process, no point in burdening memmgr with separately
725 : : * managed chunks.
726 : : */
727 : : static Block
6318 728 : 15287 : GetLocalBufferStorage(void)
729 : : {
730 : : static char *cur_block = NULL;
731 : : static int next_buf_in_block = 0;
732 : : static int num_bufs_in_block = 0;
733 : : static int total_bufs_allocated = 0;
734 : : static MemoryContext LocalBufferContext = NULL;
735 : :
736 : : char *this_buf;
737 : :
738 [ - + ]: 15287 : Assert(total_bufs_allocated < NLocBuffer);
739 : :
740 [ + + ]: 15287 : if (next_buf_in_block >= num_bufs_in_block)
741 : : {
742 : : /* Need to make a new request to memmgr */
743 : : int num_bufs;
744 : :
745 : : /*
746 : : * We allocate local buffers in a context of their own, so that the
747 : : * space eaten for them is easily recognizable in MemoryContextStats
748 : : * output. Create the context on first use.
749 : : */
4987 750 [ + + ]: 396 : if (LocalBufferContext == NULL)
751 : 252 : LocalBufferContext =
752 : 252 : AllocSetContextCreate(TopMemoryContext,
753 : : "LocalBufferContext",
754 : : ALLOCSET_DEFAULT_SIZES);
755 : :
756 : : /* Start with a 16-buffer request; subsequent ones double each time */
6318 757 : 396 : num_bufs = Max(num_bufs_in_block * 2, 16);
758 : : /* But not more than what we need for all remaining local bufs */
759 : 396 : num_bufs = Min(num_bufs, NLocBuffer - total_bufs_allocated);
760 : : /* And don't overflow MaxAllocSize, either */
761 [ + - ]: 396 : num_bufs = Min(num_bufs, MaxAllocSize / BLCKSZ);
762 : :
763 : : /* Buffers should be I/O aligned. */
372 tmunro@postgresql.or 764 : 396 : cur_block = (char *)
765 : 396 : TYPEALIGN(PG_IO_ALIGN_SIZE,
766 : : MemoryContextAlloc(LocalBufferContext,
767 : : num_bufs * BLCKSZ + PG_IO_ALIGN_SIZE));
6318 tgl@sss.pgh.pa.us 768 : 396 : next_buf_in_block = 0;
769 : 396 : num_bufs_in_block = num_bufs;
770 : : }
771 : :
772 : : /* Allocate next buffer in current memory block */
773 : 15287 : this_buf = cur_block + next_buf_in_block * BLCKSZ;
774 : 15287 : next_buf_in_block++;
775 : 15287 : total_bufs_allocated++;
776 : :
777 : 15287 : return (Block) this_buf;
778 : : }
779 : :
780 : : /*
781 : : * CheckForLocalBufferLeaks - ensure this backend holds no local buffer pins
782 : : *
783 : : * This is just like CheckForBufferLeaks(), but for local buffers.
784 : : */
785 : : static void
3586 andres@anarazel.de 786 : 450954 : CheckForLocalBufferLeaks(void)
787 : : {
788 : : #ifdef USE_ASSERT_CHECKING
789 [ + + ]: 450954 : if (LocalRefCount)
790 : : {
4048 tgl@sss.pgh.pa.us 791 : 50608 : int RefCountErrors = 0;
792 : : int i;
793 : :
6824 794 [ + + ]: 51751232 : for (i = 0; i < NLocBuffer; i++)
795 : : {
4048 796 [ - + ]: 51700624 : if (LocalRefCount[i] != 0)
797 : : {
3973 bruce@momjian.us 798 :UBC 0 : Buffer b = -i - 1;
799 : : char *s;
800 : :
158 heikki.linnakangas@i 801 :UNC 0 : s = DebugPrintBufferRefcount(b);
802 [ # # ]: 0 : elog(WARNING, "local buffer refcount leak: %s", s);
803 : 0 : pfree(s);
804 : :
4048 tgl@sss.pgh.pa.us 805 :UBC 0 : RefCountErrors++;
806 : : }
807 : : }
4048 tgl@sss.pgh.pa.us 808 [ - + ]:CBC 50608 : Assert(RefCountErrors == 0);
809 : : }
810 : : #endif
10141 scrappy@hub.org 811 : 450954 : }
812 : :
813 : : /*
814 : : * AtEOXact_LocalBuffers - clean up at end of transaction.
815 : : *
816 : : * This is just like AtEOXact_Buffers, but for local buffers.
817 : : */
818 : : void
3586 andres@anarazel.de 819 : 432909 : AtEOXact_LocalBuffers(bool isCommit)
820 : : {
821 : 432909 : CheckForLocalBufferLeaks();
822 : 432909 : }
823 : :
824 : : /*
825 : : * AtProcExit_LocalBuffers - ensure we have dropped pins during backend exit.
826 : : *
827 : : * This is just like AtProcExit_Buffers, but for local buffers.
828 : : */
829 : : void
6967 tgl@sss.pgh.pa.us 830 : 18045 : AtProcExit_LocalBuffers(void)
831 : : {
832 : : /*
833 : : * We shouldn't be holding any remaining pins; if we are, and assertions
834 : : * aren't enabled, we'll fail later in DropRelationBuffers while trying to
835 : : * drop the temp rels.
836 : : */
3586 andres@anarazel.de 837 : 18045 : CheckForLocalBufferLeaks();
6967 tgl@sss.pgh.pa.us 838 : 18045 : }
|