Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * freelist.c
4 : : * routines for managing the buffer pool's replacement strategy.
5 : : *
6 : : *
7 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
8 : : * Portions Copyright (c) 1994, Regents of the University of California
9 : : *
10 : : *
11 : : * IDENTIFICATION
12 : : * src/backend/storage/buffer/freelist.c
13 : : *
14 : : *-------------------------------------------------------------------------
15 : : */
16 : : #include "postgres.h"
17 : :
18 : : #include "pgstat.h"
19 : : #include "port/atomics.h"
20 : : #include "storage/buf_internals.h"
21 : : #include "storage/bufmgr.h"
22 : : #include "storage/proc.h"
23 : : #include "storage/shmem.h"
24 : : #include "storage/subsystems.h"
25 : :
26 : : #define INT_ACCESS_ONCE(var) ((int)(*((volatile int *)&(var))))
27 : :
28 : :
29 : : /*
30 : : * The shared freelist control information.
31 : : */
32 : : typedef struct
33 : : {
34 : : /* Spinlock: protects the values below */
35 : : slock_t buffer_strategy_lock;
36 : :
37 : : /*
38 : : * clock-sweep hand: index of next buffer to consider grabbing. Note that
39 : : * this isn't a concrete buffer - we only ever increase the value. So, to
40 : : * get an actual buffer, it needs to be used modulo NBuffers.
41 : : */
42 : : pg_atomic_uint32 nextVictimBuffer;
43 : :
44 : : /*
45 : : * Statistics. These counters should be wide enough that they can't
46 : : * overflow during a single bgwriter cycle.
47 : : */
48 : : uint32 completePasses; /* Complete cycles of the clock-sweep */
49 : : pg_atomic_uint32 numBufferAllocs; /* Buffers allocated since last reset */
50 : :
51 : : /*
52 : : * Bgworker process to be notified upon activity or -1 if none. See
53 : : * StrategyNotifyBgWriter.
54 : : */
55 : : int bgwprocno;
56 : : } BufferStrategyControl;
57 : :
58 : : /* Pointers to shared state */
59 : : static BufferStrategyControl *StrategyControl = NULL;
60 : :
61 : : static void StrategyCtlShmemRequest(void *arg);
62 : : static void StrategyCtlShmemInit(void *arg);
63 : :
64 : : const ShmemCallbacks StrategyCtlShmemCallbacks = {
65 : : .request_fn = StrategyCtlShmemRequest,
66 : : .init_fn = StrategyCtlShmemInit,
67 : : };
68 : :
69 : : /*
70 : : * Private (non-shared) state for managing a ring of shared buffers to re-use.
71 : : * This is currently the only kind of BufferAccessStrategy object, but someday
72 : : * we might have more kinds.
73 : : */
74 : : typedef struct BufferAccessStrategyData
75 : : {
76 : : /* Overall strategy type */
77 : : BufferAccessStrategyType btype;
78 : : /* Number of elements in buffers[] array */
79 : : int nbuffers;
80 : :
81 : : /*
82 : : * Index of the "current" slot in the ring, ie, the one most recently
83 : : * returned by GetBufferFromRing.
84 : : */
85 : : int current;
86 : :
87 : : /*
88 : : * Array of buffer numbers. InvalidBuffer (that is, zero) indicates we
89 : : * have not yet selected a buffer for this ring slot. For allocation
90 : : * simplicity this is palloc'd together with the fixed fields of the
91 : : * struct.
92 : : */
93 : : pg_attribute_counted_by(nbuffers) Buffer buffers[FLEXIBLE_ARRAY_MEMBER];
94 : : } BufferAccessStrategyData;
95 : :
96 : :
97 : : /* Prototypes for internal functions */
98 : : static BufferDesc *GetBufferFromRing(BufferAccessStrategy strategy,
99 : : uint64 *buf_state);
100 : : static void AddBufferToRing(BufferAccessStrategy strategy,
101 : : BufferDesc *buf);
102 : :
103 : : /*
104 : : * ClockSweepTick - Helper routine for StrategyGetBuffer()
105 : : *
106 : : * Move the clock hand one buffer ahead of its current position and return the
107 : : * id of the buffer now under the hand.
108 : : */
109 : : static inline uint32
4333 andres@anarazel.de 110 :CBC 5316736 : ClockSweepTick(void)
111 : : {
112 : : uint32 victim;
113 : :
114 : : /*
115 : : * Atomically move hand ahead one buffer - if there's several processes
116 : : * doing this, this can lead to buffers being returned slightly out of
117 : : * apparent order.
118 : : */
119 : : victim =
120 : 5316736 : pg_atomic_fetch_add_u32(&StrategyControl->nextVictimBuffer, 1);
121 : :
122 [ + + ]: 5316736 : if (victim >= NBuffers)
123 : : {
4184 bruce@momjian.us 124 : 35997 : uint32 originalVictim = victim;
125 : :
126 : : /* always wrap what we look up in BufferDescriptors */
4333 andres@anarazel.de 127 : 35997 : victim = victim % NBuffers;
128 : :
129 : : /*
130 : : * If we're the one that just caused a wraparound, force
131 : : * completePasses to be incremented while holding the spinlock. We
132 : : * need the spinlock so StrategySyncStart() can return a consistent
133 : : * value consisting of nextVictimBuffer and completePasses.
134 : : */
135 [ + + ]: 35997 : if (victim == 0)
136 : : {
137 : : uint32 expected;
138 : : uint32 wrapped;
4184 bruce@momjian.us 139 : 35883 : bool success = false;
140 : :
4333 andres@anarazel.de 141 : 35883 : expected = originalVictim + 1;
142 : :
143 [ + + ]: 71846 : while (!success)
144 : : {
145 : : /*
146 : : * Acquire the spinlock while increasing completePasses. That
147 : : * allows other readers to read nextVictimBuffer and
148 : : * completePasses in a consistent manner which is required for
149 : : * StrategySyncStart(). In theory delaying the increment
150 : : * could lead to an overflow of nextVictimBuffers, but that's
151 : : * highly unlikely and wouldn't be particularly harmful.
152 : : */
153 : 35963 : SpinLockAcquire(&StrategyControl->buffer_strategy_lock);
154 : :
155 : 35963 : wrapped = expected % NBuffers;
156 : :
157 : 35963 : success = pg_atomic_compare_exchange_u32(&StrategyControl->nextVictimBuffer,
158 : : &expected, wrapped);
159 [ + + ]: 35963 : if (success)
160 : 35883 : StrategyControl->completePasses++;
161 : 35963 : SpinLockRelease(&StrategyControl->buffer_strategy_lock);
162 : : }
163 : : }
164 : : }
165 : 5316736 : return victim;
166 : : }
167 : :
168 : : /*
169 : : * StrategyGetBuffer
170 : : *
171 : : * Called by the bufmgr to get the next candidate buffer to use in
172 : : * GetVictimBuffer(). The only hard requirement GetVictimBuffer() has is that
173 : : * the selected buffer must not currently be pinned by anyone.
174 : : *
175 : : * strategy is a BufferAccessStrategy object, or NULL for default strategy.
176 : : *
177 : : * It is the callers responsibility to ensure the buffer ownership can be
178 : : * tracked via TrackNewBufferPin().
179 : : *
180 : : * The buffer is pinned and marked as owned, using TrackNewBufferPin(),
181 : : * before returning.
182 : : */
183 : : BufferDesc *
294 184 : 2162403 : StrategyGetBuffer(BufferAccessStrategy strategy, uint64 *buf_state, bool *from_ring)
185 : : {
186 : : BufferDesc *buf;
187 : : int bgwprocno;
188 : : int trycounter;
189 : :
1365 190 : 2162403 : *from_ring = false;
191 : :
192 : : /*
193 : : * If given a strategy object, see whether it can select a buffer. We
194 : : * assume strategy objects don't need buffer_strategy_lock.
195 : : */
7099 tgl@sss.pgh.pa.us 196 [ + + ]: 2162403 : if (strategy != NULL)
197 : : {
3861 andres@anarazel.de 198 : 913895 : buf = GetBufferFromRing(strategy, buf_state);
7099 tgl@sss.pgh.pa.us 199 [ + + ]: 913895 : if (buf != NULL)
200 : : {
1365 andres@anarazel.de 201 : 346289 : *from_ring = true;
7099 tgl@sss.pgh.pa.us 202 : 346289 : return buf;
203 : : }
204 : : }
205 : :
206 : : /*
207 : : * If asked, we need to waken the bgwriter. Since we don't want to rely on
208 : : * a spinlock for this we force a read from shared memory once, and then
209 : : * set the latch based on that value. We need to go through that length
210 : : * because otherwise bgwprocno might be reset while/after we check because
211 : : * the compiler might just reread from memory.
212 : : *
213 : : * This can possibly set the latch of the wrong process if the bgwriter
214 : : * dies in the wrong moment. But since PGPROC->procLatch is never
215 : : * deallocated the worst consequence of that is that we set the latch of
216 : : * some arbitrary process.
217 : : */
4333 andres@anarazel.de 218 : 1816114 : bgwprocno = INT_ACCESS_ONCE(StrategyControl->bgwprocno);
219 [ + + ]: 1816114 : if (bgwprocno != -1)
220 : : {
221 : : /* reset bgwprocno first, before setting the latch */
222 : 756 : StrategyControl->bgwprocno = -1;
223 : :
224 : : /*
225 : : * Not acquiring ProcArrayLock here which is slightly icky. It's
226 : : * actually fine because procLatch isn't ever freed, so we just can
227 : : * potentially set the wrong process' (or no process') latch.
228 : : */
304 drowley@postgresql.o 229 : 756 : SetLatch(&GetPGProcByNumber(bgwprocno)->procLatch);
230 : : }
231 : :
232 : : /*
233 : : * We count buffer allocation requests so that the bgwriter can estimate
234 : : * the rate of buffer consumption. Note that buffers recycled by a
235 : : * strategy object are intentionally not counted here.
236 : : */
4333 andres@anarazel.de 237 : 1816114 : pg_atomic_fetch_add_u32(&StrategyControl->numBufferAllocs, 1);
238 : :
239 : : /* Use the "clock sweep" algorithm to find a free buffer */
7916 tgl@sss.pgh.pa.us 240 : 1816114 : trycounter = NBuffers;
241 : : for (;;)
8393 JanWieck@Yahoo.com 242 : 3500622 : {
243 : : uint64 old_buf_state;
244 : : uint64 local_buf_state;
245 : :
4298 andres@anarazel.de 246 : 5316736 : buf = GetBufferDescriptor(ClockSweepTick());
247 : :
248 : : /*
249 : : * Check whether the buffer can be used and pin it if so. Do this
250 : : * using a CAS loop, to avoid having to lock the buffer header.
251 : : */
294 252 : 5316736 : old_buf_state = pg_atomic_read_u64(&buf->state);
253 : : for (;;)
254 : : {
393 255 : 5316782 : local_buf_state = old_buf_state;
256 : :
257 : : /*
258 : : * If the buffer is pinned or has a nonzero usage_count, we cannot
259 : : * use it; decrement the usage_count (unless pinned) and keep
260 : : * scanning.
261 : : */
262 : :
263 [ + + ]: 5316782 : if (BUF_STATE_GET_REFCOUNT(local_buf_state) != 0)
264 : : {
265 [ - + ]: 102592 : if (--trycounter == 0)
266 : : {
267 : : /*
268 : : * We've scanned all the buffers without making any state
269 : : * changes, so all the buffers are pinned (or were when we
270 : : * looked at them). We could hope that someone will free
271 : : * one eventually, but it's probably better to fail than
272 : : * to risk getting stuck in an infinite loop.
273 : : */
15 fujii@postgresql.org 274 [ # # ]:UNC 0 : ereport(ERROR,
275 : : errcode(ERRCODE_INSUFFICIENT_RESOURCES),
276 : : errmsg("could not find an unpinned shared buffer"));
277 : : }
393 andres@anarazel.de 278 :CBC 102592 : break;
279 : : }
280 : :
281 : : /* See equivalent code in PinBuffer() */
282 [ + + ]: 5214190 : if (unlikely(local_buf_state & BM_LOCKED))
283 : : {
284 : 9 : old_buf_state = WaitBufHdrUnlocked(buf);
285 : 9 : continue;
286 : : }
287 : :
3861 288 [ + + ]: 5214181 : if (BUF_STATE_GET_USAGECOUNT(local_buf_state) != 0)
289 : : {
290 : 3398065 : local_buf_state -= BUF_USAGECOUNT_ONE;
291 : :
294 292 [ + + ]: 3398065 : if (pg_atomic_compare_exchange_u64(&buf->state, &old_buf_state,
293 : : local_buf_state))
294 : : {
393 295 : 3398030 : trycounter = NBuffers;
296 : 3398030 : break;
297 : : }
298 : : }
299 : : else
300 : : {
301 : : /* pin the buffer if the CAS succeeds */
302 : 1816116 : local_buf_state += BUF_REFCOUNT_ONE;
303 : :
294 304 [ + + ]: 1816116 : if (pg_atomic_compare_exchange_u64(&buf->state, &old_buf_state,
305 : : local_buf_state))
306 : : {
307 : : /* Found a usable buffer */
393 308 [ + + ]: 1816114 : if (strategy != NULL)
309 : 567606 : AddBufferToRing(strategy, buf);
310 : 1816114 : *buf_state = local_buf_state;
311 : :
312 : 1816114 : TrackNewBufferPin(BufferDescriptorGetBuffer(buf));
313 : :
314 : 1816114 : return buf;
315 : : }
316 : : }
317 : : }
318 : : }
319 : : }
320 : :
321 : : /*
322 : : * StrategySyncStart -- tell BgBufferSync where to start syncing
323 : : *
324 : : * The result is the buffer index of the best buffer to sync first.
325 : : * BgBufferSync() will proceed circularly around the buffer array from there.
326 : : *
327 : : * In addition, we return the completed-pass count (which is effectively
328 : : * the higher-order bits of nextVictimBuffer) and the count of recent buffer
329 : : * allocs if non-NULL pointers are passed. The alloc count is reset after
330 : : * being read.
331 : : */
332 : : int
6981 tgl@sss.pgh.pa.us 333 : 21858 : StrategySyncStart(uint32 *complete_passes, uint32 *num_buf_alloc)
334 : : {
335 : : uint32 nextVictimBuffer;
336 : : int result;
337 : :
4424 rhaas@postgresql.org 338 : 21858 : SpinLockAcquire(&StrategyControl->buffer_strategy_lock);
4333 andres@anarazel.de 339 : 21858 : nextVictimBuffer = pg_atomic_read_u32(&StrategyControl->nextVictimBuffer);
340 : 21858 : result = nextVictimBuffer % NBuffers;
341 : :
6981 tgl@sss.pgh.pa.us 342 [ + - ]: 21858 : if (complete_passes)
343 : : {
344 : 21858 : *complete_passes = StrategyControl->completePasses;
345 : :
346 : : /*
347 : : * Additionally add the number of wraparounds that happened before
348 : : * completePasses could be incremented. C.f. ClockSweepTick().
349 : : */
4333 andres@anarazel.de 350 : 21858 : *complete_passes += nextVictimBuffer / NBuffers;
351 : : }
352 : :
6981 tgl@sss.pgh.pa.us 353 [ + - ]: 21858 : if (num_buf_alloc)
354 : : {
4333 andres@anarazel.de 355 : 21858 : *num_buf_alloc = pg_atomic_exchange_u32(&StrategyControl->numBufferAllocs, 0);
356 : : }
4424 rhaas@postgresql.org 357 : 21858 : SpinLockRelease(&StrategyControl->buffer_strategy_lock);
7916 tgl@sss.pgh.pa.us 358 : 21858 : return result;
359 : : }
360 : :
361 : : /*
362 : : * StrategyNotifyBgWriter -- set or clear allocation notification latch
363 : : *
364 : : * If bgwprocno isn't -1, the next invocation of StrategyGetBuffer will
365 : : * set that latch. Pass -1 to clear the pending notification before it
366 : : * happens. This feature is used by the bgwriter process to wake itself up
367 : : * from hibernation, and is not meant for anybody else to use.
368 : : */
369 : : void
4333 andres@anarazel.de 370 : 1244 : StrategyNotifyBgWriter(int bgwprocno)
371 : : {
372 : : /*
373 : : * We acquire buffer_strategy_lock just to ensure that the store appears
374 : : * atomic to StrategyGetBuffer. The bgwriter should call this rather
375 : : * infrequently, so there's no performance penalty from being safe.
376 : : */
4424 rhaas@postgresql.org 377 : 1244 : SpinLockAcquire(&StrategyControl->buffer_strategy_lock);
4333 andres@anarazel.de 378 : 1244 : StrategyControl->bgwprocno = bgwprocno;
4424 rhaas@postgresql.org 379 : 1244 : SpinLockRelease(&StrategyControl->buffer_strategy_lock);
5293 tgl@sss.pgh.pa.us 380 : 1244 : }
381 : :
382 : :
383 : : /*
384 : : * StrategyCtlShmemRequest -- request shared memory for the buffer
385 : : * cache replacement strategy.
386 : : */
387 : : static void
213 heikki.linnakangas@i 388 : 1307 : StrategyCtlShmemRequest(void *arg)
389 : : {
390 : 1307 : ShmemRequestStruct(.name = "Buffer Strategy Status",
391 : : .size = sizeof(BufferStrategyControl),
392 : : .ptr = (void **) &StrategyControl
393 : : );
7945 tgl@sss.pgh.pa.us 394 : 1307 : }
395 : :
396 : : /*
397 : : * StrategyCtlShmemInit -- initialize the buffer cache replacement strategy.
398 : : */
399 : : static void
213 heikki.linnakangas@i 400 : 1303 : StrategyCtlShmemInit(void *arg)
401 : : {
402 : 1303 : SpinLockInit(&StrategyControl->buffer_strategy_lock);
403 : :
404 : : /* Initialize the clock-sweep pointer */
405 : 1303 : pg_atomic_init_u32(&StrategyControl->nextVictimBuffer, 0);
406 : :
407 : : /* Clear statistics */
408 : 1303 : StrategyControl->completePasses = 0;
409 : 1303 : pg_atomic_init_u32(&StrategyControl->numBufferAllocs, 0);
410 : :
411 : : /* No pending notification */
412 : 1303 : StrategyControl->bgwprocno = -1;
8393 JanWieck@Yahoo.com 413 : 1303 : }
414 : :
415 : :
416 : : /* ----------------------------------------------------------------
417 : : * Backend-private buffer ring management
418 : : * ----------------------------------------------------------------
419 : : */
420 : :
421 : :
422 : : /*
423 : : * GetAccessStrategy -- create a BufferAccessStrategy object
424 : : *
425 : : * The object is allocated in the current memory context.
426 : : */
427 : : BufferAccessStrategy
7099 tgl@sss.pgh.pa.us 428 : 158354 : GetAccessStrategy(BufferAccessStrategyType btype)
429 : : {
430 : : int ring_size_kb;
431 : :
432 : : /*
433 : : * Select ring size to use. See buffer/README for rationales.
434 : : *
435 : : * Note: if you change the ring size for BAS_BULKREAD, see also
436 : : * SYNC_SCAN_REPORT_INTERVAL in access/heap/syncscan.c.
437 : : */
438 [ - + + + : 158354 : switch (btype)
- ]
439 : : {
7099 tgl@sss.pgh.pa.us 440 :UBC 0 : case BAS_NORMAL:
441 : : /* if someone asks for NORMAL, just give 'em a "default" object */
442 : 0 : return NULL;
443 : :
7099 tgl@sss.pgh.pa.us 444 :CBC 87722 : case BAS_BULKREAD:
445 : : {
446 : : int ring_max_kb;
447 : :
448 : : /*
449 : : * The ring always needs to be large enough to allow some
450 : : * separation in time between providing a buffer to the user
451 : : * of the strategy and that buffer being reused. Otherwise the
452 : : * user's pin will prevent reuse of the buffer, even without
453 : : * concurrent activity.
454 : : *
455 : : * We also need to ensure the ring always is large enough for
456 : : * SYNC_SCAN_REPORT_INTERVAL, as noted above.
457 : : *
458 : : * Thus we start out a minimal size and increase the size
459 : : * further if appropriate.
460 : : */
576 andres@anarazel.de 461 : 87722 : ring_size_kb = 256;
462 : :
463 : : /*
464 : : * There's no point in a larger ring if we won't be allowed to
465 : : * pin sufficiently many buffers. But we never limit to less
466 : : * than the minimal size above.
467 : : */
468 : 87722 : ring_max_kb = GetPinLimit() * (BLCKSZ / 1024);
469 : 87722 : ring_max_kb = Max(ring_size_kb, ring_max_kb);
470 : :
471 : : /*
472 : : * We would like the ring to additionally have space for the
473 : : * configured degree of IO concurrency. While being read in,
474 : : * buffers can obviously not yet be reused.
475 : : *
476 : : * Each IO can be up to io_combine_limit blocks large, and we
477 : : * want to start up to effective_io_concurrency IOs.
478 : : *
479 : : * Note that effective_io_concurrency may be 0, which disables
480 : : * AIO.
481 : : */
482 : 87722 : ring_size_kb += (BLCKSZ / 1024) *
483 : 87722 : io_combine_limit * effective_io_concurrency;
484 : :
485 [ + - ]: 87722 : if (ring_size_kb > ring_max_kb)
486 : 87722 : ring_size_kb = ring_max_kb;
487 : 87722 : break;
488 : : }
6573 tgl@sss.pgh.pa.us 489 : 70591 : case BAS_BULKWRITE:
1308 drowley@postgresql.o 490 : 70591 : ring_size_kb = 16 * 1024;
6573 tgl@sss.pgh.pa.us 491 : 70591 : break;
7099 tgl@sss.pgh.pa.us 492 :GBC 41 : case BAS_VACUUM:
943 tmunro@postgresql.or 493 : 41 : ring_size_kb = 2048;
7099 tgl@sss.pgh.pa.us 494 : 41 : break;
495 : :
7099 tgl@sss.pgh.pa.us 496 :UBC 0 : default:
497 [ # # ]: 0 : elog(ERROR, "unrecognized buffer access strategy: %d",
498 : : (int) btype);
499 : : return NULL; /* keep compiler quiet */
500 : : }
501 : :
1308 drowley@postgresql.o 502 :CBC 158354 : return GetAccessStrategyWithSize(btype, ring_size_kb);
503 : : }
504 : :
505 : : /*
506 : : * GetAccessStrategyWithSize -- create a BufferAccessStrategy object with a
507 : : * number of buffers equivalent to the passed in size.
508 : : *
509 : : * If the given ring size is 0, no BufferAccessStrategy will be created and
510 : : * the function will return NULL. ring_size_kb must not be negative.
511 : : */
512 : : BufferAccessStrategy
513 : 166698 : GetAccessStrategyWithSize(BufferAccessStrategyType btype, int ring_size_kb)
514 : : {
515 : : int ring_buffers;
516 : : BufferAccessStrategy strategy;
517 : :
518 [ - + ]: 166698 : Assert(ring_size_kb >= 0);
519 : :
520 : : /* Figure out how many buffers ring_size_kb is */
521 : 166698 : ring_buffers = ring_size_kb / (BLCKSZ / 1024);
522 : :
523 : : /* 0 means unlimited, so no BufferAccessStrategy required */
524 [ + + ]: 166698 : if (ring_buffers == 0)
525 : 8 : return NULL;
526 : :
527 : : /* Cap to 1/8th of shared_buffers */
528 : 166690 : ring_buffers = Min(NBuffers / 8, ring_buffers);
529 : :
530 : : /* NBuffers should never be less than 16, so this shouldn't happen */
531 [ - + ]: 166690 : Assert(ring_buffers > 0);
532 : :
533 : : /* Allocate the object and initialize all elements to zeroes */
534 : : strategy = (BufferAccessStrategy)
7099 tgl@sss.pgh.pa.us 535 : 166690 : palloc0(offsetof(BufferAccessStrategyData, buffers) +
536 : : ring_buffers * sizeof(Buffer));
537 : :
538 : : /* Set fields that don't start out zero */
539 : 166690 : strategy->btype = btype;
1308 drowley@postgresql.o 540 : 166690 : strategy->nbuffers = ring_buffers;
541 : :
7099 tgl@sss.pgh.pa.us 542 : 166690 : return strategy;
543 : : }
544 : :
545 : : /*
546 : : * GetAccessStrategyBufferCount -- an accessor for the number of buffers in
547 : : * the ring
548 : : *
549 : : * Returns 0 on NULL input to match behavior of GetAccessStrategyWithSize()
550 : : * returning NULL with 0 size.
551 : : */
552 : : int
1308 drowley@postgresql.o 553 : 27 : GetAccessStrategyBufferCount(BufferAccessStrategy strategy)
554 : : {
555 [ - + ]: 27 : if (strategy == NULL)
1308 drowley@postgresql.o 556 :UBC 0 : return 0;
557 : :
1308 drowley@postgresql.o 558 :CBC 27 : return strategy->nbuffers;
559 : : }
560 : :
561 : : /*
562 : : * GetAccessStrategyPinLimit -- get cap of number of buffers that should be pinned
563 : : *
564 : : * When pinning extra buffers to look ahead, users of a ring-based strategy are
565 : : * in danger of pinning too much of the ring at once while performing look-ahead.
566 : : * For some strategies, that means "escaping" from the ring, and in others it
567 : : * means forcing dirty data to disk very frequently with associated WAL
568 : : * flushing. Since external code has no insight into any of that, allow
569 : : * individual strategy types to expose a clamp that should be applied when
570 : : * deciding on a maximum number of buffers to pin at once.
571 : : *
572 : : * Callers should combine this number with other relevant limits and take the
573 : : * minimum.
574 : : */
575 : : int
943 tmunro@postgresql.or 576 : 556493 : GetAccessStrategyPinLimit(BufferAccessStrategy strategy)
577 : : {
578 [ + + ]: 556493 : if (strategy == NULL)
579 : 442649 : return NBuffers;
580 : :
581 [ + + ]: 113844 : switch (strategy->btype)
582 : : {
583 : 83390 : case BAS_BULKREAD:
584 : :
585 : : /*
586 : : * Since BAS_BULKREAD uses StrategyRejectBuffer(), dirty buffers
587 : : * shouldn't be a problem and the caller is free to pin up to the
588 : : * entire ring at once.
589 : : */
590 : 83390 : return strategy->nbuffers;
591 : :
592 : 30454 : default:
593 : :
594 : : /*
595 : : * Tell caller not to pin more than half the buffers in the ring.
596 : : * This is a trade-off between look ahead distance and deferring
597 : : * writeback and associated WAL traffic.
598 : : */
599 : 30454 : return strategy->nbuffers / 2;
600 : : }
601 : : }
602 : :
603 : : /*
604 : : * FreeAccessStrategy -- release a BufferAccessStrategy object
605 : : *
606 : : * A simple pfree would do at the moment, but we would prefer that callers
607 : : * don't assume that much about the representation of BufferAccessStrategy.
608 : : */
609 : : void
7099 tgl@sss.pgh.pa.us 610 : 152317 : FreeAccessStrategy(BufferAccessStrategy strategy)
611 : : {
612 : : /* don't crash if called on a "default" strategy */
613 [ + - ]: 152317 : if (strategy != NULL)
614 : 152317 : pfree(strategy);
615 : 152317 : }
616 : :
617 : : /*
618 : : * GetBufferFromRing -- returns a buffer from the ring, or NULL if the
619 : : * ring is empty / not usable.
620 : : *
621 : : * The buffer is pinned and marked as owned, using TrackNewBufferPin(), before
622 : : * returning.
623 : : */
624 : : static BufferDesc *
294 andres@anarazel.de 625 : 913895 : GetBufferFromRing(BufferAccessStrategy strategy, uint64 *buf_state)
626 : : {
627 : : BufferDesc *buf;
628 : : Buffer bufnum;
629 : : uint64 old_buf_state;
630 : : uint64 local_buf_state; /* to avoid repeated (de-)referencing */
631 : :
632 : :
633 : : /* Advance to next ring slot */
1312 drowley@postgresql.o 634 [ + + ]: 913895 : if (++strategy->current >= strategy->nbuffers)
7099 tgl@sss.pgh.pa.us 635 : 26595 : strategy->current = 0;
636 : :
637 : : /*
638 : : * If the slot hasn't been filled yet, tell the caller to allocate a new
639 : : * buffer with the normal allocation strategy. He will then fill this
640 : : * slot by calling AddBufferToRing with the new buffer.
641 : : */
642 : 913895 : bufnum = strategy->buffers[strategy->current];
643 [ + + ]: 913895 : if (bufnum == InvalidBuffer)
644 : 555075 : return NULL;
645 : :
393 andres@anarazel.de 646 : 358820 : buf = GetBufferDescriptor(bufnum - 1);
647 : :
648 : : /*
649 : : * Check whether the buffer can be used and pin it if so. Do this using a
650 : : * CAS loop, to avoid having to lock the buffer header.
651 : : */
294 652 : 358820 : old_buf_state = pg_atomic_read_u64(&buf->state);
653 : : for (;;)
654 : : {
393 655 : 358822 : local_buf_state = old_buf_state;
656 : :
657 : : /*
658 : : * If the buffer is pinned we cannot use it under any circumstances.
659 : : *
660 : : * If usage_count is 0 or 1 then the buffer is fair game (we expect 1,
661 : : * since our own previous usage of the ring element would have left it
662 : : * there, but it might've been decremented by clock-sweep since then).
663 : : * A higher usage_count indicates someone else has touched the buffer,
664 : : * so we shouldn't re-use it.
665 : : */
666 [ + + ]: 358822 : if (BUF_STATE_GET_REFCOUNT(local_buf_state) != 0
667 [ + + ]: 352011 : || BUF_STATE_GET_USAGECOUNT(local_buf_state) > 1)
668 : : break;
669 : :
670 : : /* See equivalent code in PinBuffer() */
671 [ - + ]: 346291 : if (unlikely(local_buf_state & BM_LOCKED))
672 : : {
393 andres@anarazel.de 673 :UBC 0 : old_buf_state = WaitBufHdrUnlocked(buf);
674 : 0 : continue;
675 : : }
676 : :
677 : : /* pin the buffer if the CAS succeeds */
393 andres@anarazel.de 678 :CBC 346291 : local_buf_state += BUF_REFCOUNT_ONE;
679 : :
294 680 [ + + ]: 346291 : if (pg_atomic_compare_exchange_u64(&buf->state, &old_buf_state,
681 : : local_buf_state))
682 : : {
393 683 : 346289 : *buf_state = local_buf_state;
684 : :
685 : 346289 : TrackNewBufferPin(BufferDescriptorGetBuffer(buf));
686 : 346289 : return buf;
687 : : }
688 : : }
689 : :
690 : : /*
691 : : * Tell caller to allocate a new buffer with the normal allocation
692 : : * strategy. He'll then replace this ring element via AddBufferToRing.
693 : : */
7099 tgl@sss.pgh.pa.us 694 : 12531 : return NULL;
695 : : }
696 : :
697 : : /*
698 : : * AddBufferToRing -- add a buffer to the buffer ring
699 : : *
700 : : * Caller must hold the buffer header spinlock on the buffer. Since this
701 : : * is called with the spinlock held, it had better be quite cheap.
702 : : */
703 : : static void
4007 rhaas@postgresql.org 704 : 567606 : AddBufferToRing(BufferAccessStrategy strategy, BufferDesc *buf)
705 : : {
7099 tgl@sss.pgh.pa.us 706 : 567606 : strategy->buffers[strategy->current] = BufferDescriptorGetBuffer(buf);
707 : 567606 : }
708 : :
709 : : /*
710 : : * Utility function returning the IOContext of a given BufferAccessStrategy's
711 : : * strategy ring.
712 : : */
713 : : IOContext
1365 andres@anarazel.de 714 : 85275036 : IOContextForStrategy(BufferAccessStrategy strategy)
715 : : {
716 [ + + ]: 85275036 : if (!strategy)
717 : 82939923 : return IOCONTEXT_NORMAL;
718 : :
719 [ - + + + : 2335113 : switch (strategy->btype)
- ]
720 : : {
1365 andres@anarazel.de 721 :UBC 0 : case BAS_NORMAL:
722 : :
723 : : /*
724 : : * Currently, GetAccessStrategy() returns NULL for
725 : : * BufferAccessStrategyType BAS_NORMAL, so this case is
726 : : * unreachable.
727 : : */
728 : 0 : pg_unreachable();
729 : : return IOCONTEXT_NORMAL;
1365 andres@anarazel.de 730 :CBC 1539908 : case BAS_BULKREAD:
731 : 1539908 : return IOCONTEXT_BULKREAD;
732 : 323719 : case BAS_BULKWRITE:
733 : 323719 : return IOCONTEXT_BULKWRITE;
734 : 471486 : case BAS_VACUUM:
735 : 471486 : return IOCONTEXT_VACUUM;
736 : : }
737 : :
1365 andres@anarazel.de 738 [ # # ]:UBC 0 : elog(ERROR, "unrecognized BufferAccessStrategyType: %d", strategy->btype);
739 : : pg_unreachable();
740 : : }
741 : :
742 : : /*
743 : : * StrategyRejectBuffer -- consider rejecting a dirty buffer
744 : : *
745 : : * When a nondefault strategy is used, the buffer manager calls this function
746 : : * when it turns out that the buffer selected by StrategyGetBuffer needs to
747 : : * be written out and doing so would require flushing WAL too. This gives us
748 : : * a chance to choose a different victim.
749 : : *
750 : : * Returns true if buffer manager should ask for a new victim, and false
751 : : * if this buffer should be written and re-used.
752 : : */
753 : : bool
1365 andres@anarazel.de 754 :CBC 27026 : StrategyRejectBuffer(BufferAccessStrategy strategy, BufferDesc *buf, bool from_ring)
755 : : {
756 : : /* We only do this in bulkread mode */
7099 tgl@sss.pgh.pa.us 757 [ + + ]: 27026 : if (strategy->btype != BAS_BULKREAD)
758 : 4866 : return false;
759 : :
760 : : /* Don't muck with behavior of normal buffer-replacement strategy */
1365 andres@anarazel.de 761 [ + - - + ]: 44320 : if (!from_ring ||
3424 tgl@sss.pgh.pa.us 762 : 22160 : strategy->buffers[strategy->current] != BufferDescriptorGetBuffer(buf))
7099 tgl@sss.pgh.pa.us 763 :UBC 0 : return false;
764 : :
765 : : /*
766 : : * Remove the dirty buffer from the ring; necessary to prevent infinite
767 : : * loop if all ring members are dirty.
768 : : */
7099 tgl@sss.pgh.pa.us 769 :CBC 22160 : strategy->buffers[strategy->current] = InvalidBuffer;
770 : :
771 : 22160 : return true;
772 : : }
|