PostgreSQL Source Code git master
Loading...
Searching...
No Matches
slru.c File Reference
#include "postgres.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include "access/slru.h"
#include "access/transam.h"
#include "access/xlog.h"
#include "access/xlogutils.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "storage/fd.h"
#include "storage/shmem.h"
#include "utils/guc.h"
#include "utils/wait_event.h"
Include dependency graph for slru.c:

Go to the source code of this file.

Data Structures

struct  SlruWriteAllData
 

Macros

#define MAX_WRITEALL_BUFFERS   16
 
#define SLRU_BANK_BITSHIFT   4
 
#define SLRU_BANK_SIZE   (1 << SLRU_BANK_BITSHIFT)
 
#define SlotGetBankNumber(slotno)   ((slotno) >> SLRU_BANK_BITSHIFT)
 
#define INIT_SLRUFILETAG(a, xx_handler, xx_segno)
 

Typedefs

typedef struct SlruWriteAllData SlruWriteAllData
 
typedef struct SlruWriteAllDataSlruWriteAll
 

Enumerations

enum  SlruErrorCause {
  SLRU_OPEN_FAILED , SLRU_SEEK_FAILED , SLRU_READ_FAILED , SLRU_WRITE_FAILED ,
  SLRU_FSYNC_FAILED , SLRU_CLOSE_FAILED
}
 

Functions

static int SlruFileName (SlruCtl ctl, char *path, int64 segno)
 
static void SimpleLruZeroLSNs (SlruCtl ctl, int slotno)
 
static void SimpleLruWaitIO (SlruCtl ctl, int slotno)
 
static void SlruInternalWritePage (SlruCtl ctl, int slotno, SlruWriteAll fdata)
 
static bool SlruPhysicalReadPage (SlruCtl ctl, int64 pageno, int slotno)
 
static bool SlruPhysicalWritePage (SlruCtl ctl, int64 pageno, int slotno, SlruWriteAll fdata)
 
static void SlruReportIOError (SlruCtl ctl, int64 pageno, const void *opaque_data)
 
static int SlruSelectLRUPage (SlruCtl ctl, int64 pageno)
 
static bool SlruScanDirCbDeleteCutoff (SlruCtl ctl, char *filename, int64 segpage, void *data)
 
static void SlruInternalDeleteSegment (SlruCtl ctl, int64 segno)
 
static void SlruRecentlyUsed (SlruShared shared, int slotno)
 
Size SimpleLruShmemSize (int nslots, int nlsns)
 
int SimpleLruAutotuneBuffers (int divisor, int max)
 
void SimpleLruInit (SlruCtl ctl, const char *name, int nslots, int nlsns, const char *subdir, int buffer_tranche_id, int bank_tranche_id, SyncRequestHandler sync_handler, bool long_segment_names)
 
bool check_slru_buffers (const char *name, int *newval)
 
int SimpleLruZeroPage (SlruCtl ctl, int64 pageno)
 
void SimpleLruZeroAndWritePage (SlruCtl ctl, int64 pageno)
 
int SimpleLruReadPage (SlruCtl ctl, int64 pageno, bool write_ok, const void *opaque_data)
 
int SimpleLruReadPage_ReadOnly (SlruCtl ctl, int64 pageno, const void *opaque_data)
 
void SimpleLruWritePage (SlruCtl ctl, int slotno)
 
bool SimpleLruDoesPhysicalPageExist (SlruCtl ctl, int64 pageno)
 
void SimpleLruWriteAll (SlruCtl ctl, bool allow_redirtied)
 
void SimpleLruTruncate (SlruCtl ctl, int64 cutoffPage)
 
void SlruDeleteSegment (SlruCtl ctl, int64 segno)
 
static bool SlruMayDeleteSegment (SlruCtl ctl, int64 segpage, int64 cutoffPage)
 
bool SlruScanDirCbReportPresence (SlruCtl ctl, char *filename, int64 segpage, void *data)
 
bool SlruScanDirCbDeleteAll (SlruCtl ctl, char *filename, int64 segpage, void *data)
 
static bool SlruCorrectSegmentFilenameLength (SlruCtl ctl, size_t len)
 
bool SlruScanDirectory (SlruCtl ctl, SlruScanCallback callback, void *data)
 
int SlruSyncFileTag (SlruCtl ctl, const FileTag *ftag, char *path)
 

Variables

static SlruErrorCause slru_errcause
 
static int slru_errno
 

Macro Definition Documentation

◆ INIT_SLRUFILETAG

#define INIT_SLRUFILETAG (   a,
  xx_handler,
  xx_segno 
)
Value:
( \
memset(&(a), 0, sizeof(FileTag)), \
(a).handler = (xx_handler), \
(a).segno = (xx_segno) \
)
int a
Definition isn.c:73
static int fb(int x)
Definition sync.h:51

Definition at line 157 of file slru.c.

165{
173
175static int slru_errno;
176
177
178static void SimpleLruZeroLSNs(SlruCtl ctl, int slotno);
179static void SimpleLruWaitIO(SlruCtl ctl, int slotno);
181static bool SlruPhysicalReadPage(SlruCtl ctl, int64 pageno, int slotno);
182static bool SlruPhysicalWritePage(SlruCtl ctl, int64 pageno, int slotno,
184static void SlruReportIOError(SlruCtl ctl, int64 pageno,
185 const void *opaque_data);
186static int SlruSelectLRUPage(SlruCtl ctl, int64 pageno);
187
189 int64 segpage, void *data);
190static void SlruInternalDeleteSegment(SlruCtl ctl, int64 segno);
191static inline void SlruRecentlyUsed(SlruShared shared, int slotno);
192
193
194/*
195 * Initialization of shared memory
196 */
197
198Size
199SimpleLruShmemSize(int nslots, int nlsns)
200{
201 int nbanks = nslots / SLRU_BANK_SIZE;
202 Size sz;
203
205 Assert(nslots % SLRU_BANK_SIZE == 0);
206
207 /* we assume nslots isn't so large as to risk overflow */
208 sz = MAXALIGN(sizeof(SlruSharedData));
209 sz += MAXALIGN(nslots * sizeof(char *)); /* page_buffer[] */
210 sz += MAXALIGN(nslots * sizeof(SlruPageStatus)); /* page_status[] */
211 sz += MAXALIGN(nslots * sizeof(bool)); /* page_dirty[] */
212 sz += MAXALIGN(nslots * sizeof(int64)); /* page_number[] */
213 sz += MAXALIGN(nslots * sizeof(int)); /* page_lru_count[] */
214 sz += MAXALIGN(nslots * sizeof(LWLockPadded)); /* buffer_locks[] */
215 sz += MAXALIGN(nbanks * sizeof(LWLockPadded)); /* bank_locks[] */
216 sz += MAXALIGN(nbanks * sizeof(int)); /* bank_cur_lru_count[] */
217
218 if (nlsns > 0)
219 sz += MAXALIGN(nslots * nlsns * sizeof(XLogRecPtr)); /* group_lsn[] */
220
221 return BUFFERALIGN(sz) + BLCKSZ * nslots;
222}
223
224/*
225 * Determine a number of SLRU buffers to use.
226 *
227 * We simply divide shared_buffers by the divisor given and cap
228 * that at the maximum given; but always at least SLRU_BANK_SIZE.
229 * Round down to the nearest multiple of SLRU_BANK_SIZE.
230 */
231int
233{
234 return Min(max - (max % SLRU_BANK_SIZE),
237}
238
239/*
240 * Initialize, or attach to, a simple LRU cache in shared memory.
241 *
242 * ctl: address of local (unshared) control structure.
243 * name: name of SLRU. (This is user-visible, pick with care!)
244 * nslots: number of page slots to use.
245 * nlsns: number of LSN groups per page (set to zero if not relevant).
246 * subdir: PGDATA-relative subdirectory that will contain the files.
247 * buffer_tranche_id: tranche ID to use for the SLRU's per-buffer LWLocks.
248 * bank_tranche_id: tranche ID to use for the bank LWLocks.
249 * sync_handler: which set of functions to use to handle sync requests
250 * long_segment_names: use short or long segment names
251 */
252void
253SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
254 const char *subdir, int buffer_tranche_id, int bank_tranche_id,
255 SyncRequestHandler sync_handler, bool long_segment_names)
256{
257 SlruShared shared;
258 bool found;
259 int nbanks = nslots / SLRU_BANK_SIZE;
260
262
263 Assert(ctl->PagePrecedes != NULL);
264 Assert(ctl->errdetail_for_io_error != NULL);
265
267 SimpleLruShmemSize(nslots, nlsns),
268 &found);
269
271 {
272 /* Initialize locks and shared memory area */
273 char *ptr;
274 Size offset;
275
276 Assert(!found);
277
278 memset(shared, 0, sizeof(SlruSharedData));
279
280 shared->num_slots = nslots;
281 shared->lsn_groups_per_page = nlsns;
282
284
286
287 ptr = (char *) shared;
288 offset = MAXALIGN(sizeof(SlruSharedData));
289 shared->page_buffer = (char **) (ptr + offset);
290 offset += MAXALIGN(nslots * sizeof(char *));
291 shared->page_status = (SlruPageStatus *) (ptr + offset);
292 offset += MAXALIGN(nslots * sizeof(SlruPageStatus));
293 shared->page_dirty = (bool *) (ptr + offset);
294 offset += MAXALIGN(nslots * sizeof(bool));
295 shared->page_number = (int64 *) (ptr + offset);
296 offset += MAXALIGN(nslots * sizeof(int64));
297 shared->page_lru_count = (int *) (ptr + offset);
298 offset += MAXALIGN(nslots * sizeof(int));
299
300 /* Initialize LWLocks */
301 shared->buffer_locks = (LWLockPadded *) (ptr + offset);
302 offset += MAXALIGN(nslots * sizeof(LWLockPadded));
303 shared->bank_locks = (LWLockPadded *) (ptr + offset);
304 offset += MAXALIGN(nbanks * sizeof(LWLockPadded));
305 shared->bank_cur_lru_count = (int *) (ptr + offset);
306 offset += MAXALIGN(nbanks * sizeof(int));
307
308 if (nlsns > 0)
309 {
310 shared->group_lsn = (XLogRecPtr *) (ptr + offset);
311 offset += MAXALIGN(nslots * nlsns * sizeof(XLogRecPtr));
312 }
313
314 ptr += BUFFERALIGN(offset);
315 for (int slotno = 0; slotno < nslots; slotno++)
316 {
319
320 shared->page_buffer[slotno] = ptr;
322 shared->page_dirty[slotno] = false;
323 shared->page_lru_count[slotno] = 0;
324 ptr += BLCKSZ;
325 }
326
327 /* Initialize the slot banks. */
328 for (int bankno = 0; bankno < nbanks; bankno++)
329 {
331 shared->bank_cur_lru_count[bankno] = 0;
332 }
333
334 /* Should fit to estimated shmem size */
335 Assert(ptr - (char *) shared <= SimpleLruShmemSize(nslots, nlsns));
336 }
337 else
338 {
339 Assert(found);
340 Assert(shared->num_slots == nslots);
341 }
342
343 /*
344 * Initialize the unshared control struct, including directory path. We
345 * assume caller set PagePrecedes.
346 */
347 ctl->shared = shared;
348 ctl->sync_handler = sync_handler;
349 ctl->long_segment_names = long_segment_names;
350 ctl->nbanks = nbanks;
351 strlcpy(ctl->Dir, subdir, sizeof(ctl->Dir));
352}
353
354/*
355 * Helper function for GUC check_hook to check whether slru buffers are in
356 * multiples of SLRU_BANK_SIZE.
357 */
358bool
359check_slru_buffers(const char *name, int *newval)
360{
361 /* Valid values are multiples of SLRU_BANK_SIZE */
362 if (*newval % SLRU_BANK_SIZE == 0)
363 return true;
364
365 GUC_check_errdetail("\"%s\" must be a multiple of %d.", name,
367 return false;
368}
369
370/*
371 * Initialize (or reinitialize) a page to zeroes.
372 *
373 * The page is not actually written, just set up in shared memory.
374 * The slot number of the new page is returned.
375 *
376 * Bank lock must be held at entry, and will be held at exit.
377 */
378int
380{
381 SlruShared shared = ctl->shared;
382 int slotno;
383
385
386 /* Find a suitable buffer slot for the page */
387 slotno = SlruSelectLRUPage(ctl, pageno);
389 (shared->page_status[slotno] == SLRU_PAGE_VALID &&
390 !shared->page_dirty[slotno]) ||
391 shared->page_number[slotno] == pageno);
392
393 /* Mark the slot as containing this page */
394 shared->page_number[slotno] = pageno;
396 shared->page_dirty[slotno] = true;
397 SlruRecentlyUsed(shared, slotno);
398
399 /* Set the buffer to zeroes */
400 MemSet(shared->page_buffer[slotno], 0, BLCKSZ);
401
402 /* Set the LSNs for this new page to zero */
404
405 /*
406 * Assume this page is now the latest active page.
407 *
408 * Note that because both this routine and SlruSelectLRUPage run with a
409 * SLRU bank lock held, it is not possible for this to be zeroing a page
410 * that SlruSelectLRUPage is going to evict simultaneously. Therefore,
411 * there's no memory barrier here.
412 */
413 pg_atomic_write_u64(&shared->latest_page_number, pageno);
414
415 /* update the stats counter of zeroed pages */
417
418 return slotno;
419}
420
421/*
422 * Zero all the LSNs we store for this slru page.
423 *
424 * This should be called each time we create a new page, and each time we read
425 * in a page from disk into an existing buffer. (Such an old page cannot
426 * have any interesting LSNs, since we'd have flushed them before writing
427 * the page in the first place.)
428 *
429 * This assumes that InvalidXLogRecPtr is bitwise-all-0.
430 */
431static void
433{
434 SlruShared shared = ctl->shared;
435
436 if (shared->lsn_groups_per_page > 0)
437 MemSet(&shared->group_lsn[slotno * shared->lsn_groups_per_page], 0,
438 shared->lsn_groups_per_page * sizeof(XLogRecPtr));
439}
440
441/*
442 * This is a convenience wrapper for the common case of zeroing a page and
443 * immediately flushing it to disk.
444 *
445 * SLRU bank lock is acquired and released here.
446 */
447void
449{
450 int slotno;
451 LWLock *lock;
452
453 lock = SimpleLruGetBankLock(ctl, pageno);
455
456 /* Create and zero the page */
457 slotno = SimpleLruZeroPage(ctl, pageno);
458
459 /* Make sure it's written out */
461 Assert(!ctl->shared->page_dirty[slotno]);
462
463 LWLockRelease(lock);
464}
465
466/*
467 * Wait for any active I/O on a page slot to finish. (This does not
468 * guarantee that new I/O hasn't been started before we return, though.
469 * In fact the slot might not even contain the same page anymore.)
470 *
471 * Bank lock must be held at entry, and will be held at exit.
472 */
473static void
475{
476 SlruShared shared = ctl->shared;
478
480
481 /* See notes at top of file */
486
487 /*
488 * If the slot is still in an io-in-progress state, then either someone
489 * already started a new I/O on the slot, or a previous I/O failed and
490 * neglected to reset the page state. That shouldn't happen, really, but
491 * it seems worth a few extra cycles to check and recover from it. We can
492 * cheaply test for failure by seeing if the buffer lock is still held (we
493 * assume that transaction abort would release the lock).
494 */
497 {
499 {
500 /* indeed, the I/O must have failed */
503 else /* write_in_progress */
504 {
506 shared->page_dirty[slotno] = true;
507 }
509 }
510 }
511}
512
513/*
514 * Find a page in a shared buffer, reading it in if necessary.
515 * The page number must correspond to an already-initialized page.
516 *
517 * If write_ok is true then it is OK to return a page that is in
518 * WRITE_IN_PROGRESS state; it is the caller's responsibility to be sure
519 * that modification of the page is safe. If write_ok is false then we
520 * will not return the page until it is not undergoing active I/O.
521 *
522 * On error, the passed-in 'opaque_data' is passed to the
523 * 'errdetail_for_io_error' callback, to provide details on the operation that
524 * failed. It is only used for error reporting.
525 *
526 * Return value is the shared-buffer slot number now holding the page.
527 * The buffer's LRU access info is updated.
528 *
529 * The correct bank lock must be held at entry, and will be held at exit.
530 */
531int
533 const void *opaque_data)
534{
535 SlruShared shared = ctl->shared;
537
539
540 /* Outer loop handles restart if we must wait for someone else's I/O */
541 for (;;)
542 {
543 int slotno;
544 bool ok;
545
546 /* See if page already is in memory; if not, pick victim slot */
547 slotno = SlruSelectLRUPage(ctl, pageno);
548
549 /* Did we find the page in memory? */
550 if (shared->page_status[slotno] != SLRU_PAGE_EMPTY &&
551 shared->page_number[slotno] == pageno)
552 {
553 /*
554 * If page is still being read in, we must wait for I/O. Likewise
555 * if the page is being written and the caller said that's not OK.
556 */
559 !write_ok))
560 {
562 /* Now we must recheck state from the top */
563 continue;
564 }
565 /* Otherwise, it's ready to use */
566 SlruRecentlyUsed(shared, slotno);
567
568 /* update the stats counter of pages found in the SLRU */
570
571 return slotno;
572 }
573
574 /* We found no match; assert we selected a freeable slot */
576 (shared->page_status[slotno] == SLRU_PAGE_VALID &&
577 !shared->page_dirty[slotno]));
578
579 /* Mark the slot read-busy */
580 shared->page_number[slotno] = pageno;
582 shared->page_dirty[slotno] = false;
583
584 /* Acquire per-buffer lock (cannot deadlock, see notes at top) */
586
587 /* Release bank lock while doing I/O */
589
590 /* Do the read */
591 ok = SlruPhysicalReadPage(ctl, pageno, slotno);
592
593 /* Set the LSNs for this newly read-in page to zero */
595
596 /* Re-acquire bank control lock and update page state */
598
599 Assert(shared->page_number[slotno] == pageno &&
601 !shared->page_dirty[slotno]);
602
604
606
607 /* Now it's okay to ereport if we failed */
608 if (!ok)
610
611 SlruRecentlyUsed(shared, slotno);
612
613 /* update the stats counter of pages not found in SLRU */
615
616 return slotno;
617 }
618}
619
620/*
621 * Find a page in a shared buffer, reading it in if necessary.
622 * The page number must correspond to an already-initialized page.
623 * The caller must intend only read-only access to the page.
624 *
625 * On error, the passed-in 'opaque_data' is passed to the
626 * 'errdetail_for_io_error' callback, to provide details on the operation that
627 * failed. It is only used for error reporting.
628 *
629 * Return value is the shared-buffer slot number now holding the page.
630 * The buffer's LRU access info is updated.
631 *
632 * Bank control lock must NOT be held at entry, but will be held at exit.
633 * It is unspecified whether the lock will be shared or exclusive.
634 */
635int
637{
638 SlruShared shared = ctl->shared;
640 int bankno = pageno % ctl->nbanks;
643
644 /* Try to find the page while holding only shared lock */
646
647 /* See if page is already in a buffer */
648 for (int slotno = bankstart; slotno < bankend; slotno++)
649 {
650 if (shared->page_status[slotno] != SLRU_PAGE_EMPTY &&
651 shared->page_number[slotno] == pageno &&
653 {
654 /* See comments for SlruRecentlyUsed() */
655 SlruRecentlyUsed(shared, slotno);
656
657 /* update the stats counter of pages found in the SLRU */
659
660 return slotno;
661 }
662 }
663
664 /* No luck, so switch to normal exclusive lock and do regular read */
667
668 return SimpleLruReadPage(ctl, pageno, true, opaque_data);
669}
670
671/*
672 * Write a page from a shared buffer, if necessary.
673 * Does nothing if the specified slot is not dirty.
674 *
675 * NOTE: only one write attempt is made here. Hence, it is possible that
676 * the page is still dirty at exit (if someone else re-dirtied it during
677 * the write). However, we *do* attempt a fresh write even if the page
678 * is already being written; this is for checkpoints.
679 *
680 * Bank lock must be held at entry, and will be held at exit.
681 */
682static void
684{
685 SlruShared shared = ctl->shared;
686 int64 pageno = shared->page_number[slotno];
688 bool ok;
689
692
693 /* If a write is in progress, wait for it to finish */
695 shared->page_number[slotno] == pageno)
696 {
698 }
699
700 /*
701 * Do nothing if page is not dirty, or if buffer no longer contains the
702 * same page we were called for.
703 */
704 if (!shared->page_dirty[slotno] ||
705 shared->page_status[slotno] != SLRU_PAGE_VALID ||
706 shared->page_number[slotno] != pageno)
707 return;
708
709 /*
710 * Mark the slot write-busy, and clear the dirtybit. After this point, a
711 * transaction status update on this page will mark it dirty again.
712 */
714 shared->page_dirty[slotno] = false;
715
716 /* Acquire per-buffer lock (cannot deadlock, see notes at top) */
718
719 /* Release bank lock while doing I/O */
721
722 /* Do the write */
724
725 /* If we failed, and we're in a flush, better close the files */
726 if (!ok && fdata)
727 {
728 for (int i = 0; i < fdata->num_files; i++)
730 }
731
732 /* Re-acquire bank lock and update page state */
734
735 Assert(shared->page_number[slotno] == pageno &&
737
738 /* If we failed to write, mark the page dirty again */
739 if (!ok)
740 shared->page_dirty[slotno] = true;
741
743
745
746 /* Now it's okay to ereport if we failed */
747 if (!ok)
748 SlruReportIOError(ctl, pageno, NULL);
749
750 /* If part of a checkpoint, count this as a SLRU buffer written. */
751 if (fdata)
752 {
755 }
756}
757
758/*
759 * Wrapper of SlruInternalWritePage, for external callers.
760 * fdata is always passed a NULL here.
761 */
762void
764{
765 Assert(ctl->shared->page_status[slotno] != SLRU_PAGE_EMPTY);
766
768}
769
770/*
771 * Return whether the given page exists on disk.
772 *
773 * A false return means that either the file does not exist, or that it's not
774 * large enough to contain the given page.
775 */
776bool
778{
779 int64 segno = pageno / SLRU_PAGES_PER_SEGMENT;
780 int rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
781 int offset = rpageno * BLCKSZ;
782 char path[MAXPGPATH];
783 int fd;
784 bool result;
786
787 /* update the stats counter of checked pages */
788 pgstat_count_slru_blocks_exists(ctl->shared->slru_stats_idx);
789
790 SlruFileName(ctl, path, segno);
791
793 if (fd < 0)
794 {
795 /* expected: file doesn't exist */
796 if (errno == ENOENT)
797 return false;
798
799 /* report error normally */
802 SlruReportIOError(ctl, pageno, NULL);
803 }
804
805 if ((endpos = lseek(fd, 0, SEEK_END)) < 0)
806 {
809 SlruReportIOError(ctl, pageno, NULL);
810 }
811
812 result = endpos >= (off_t) (offset + BLCKSZ);
813
814 if (CloseTransientFile(fd) != 0)
815 {
818 return false;
819 }
820
821 return result;
822}
823
824/*
825 * Physical read of a (previously existing) page into a buffer slot
826 *
827 * On failure, we cannot just ereport(ERROR) since caller has put state in
828 * shared memory that must be undone. So, we return false and save enough
829 * info in static variables to let SlruReportIOError make the report.
830 *
831 * For now, assume it's not worth keeping a file pointer open across
832 * read/write operations. We could cache one virtual file pointer ...
833 */
834static bool
836{
837 SlruShared shared = ctl->shared;
838 int64 segno = pageno / SLRU_PAGES_PER_SEGMENT;
839 int rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
840 off_t offset = rpageno * BLCKSZ;
841 char path[MAXPGPATH];
842 int fd;
843
844 SlruFileName(ctl, path, segno);
845
846 /*
847 * In a crash-and-restart situation, it's possible for us to receive
848 * commands to set the commit status of transactions whose bits are in
849 * already-truncated segments of the commit log (see notes in
850 * SlruPhysicalWritePage). Hence, if we are InRecovery, allow the case
851 * where the file doesn't exist, and return zeroes instead.
852 */
854 if (fd < 0)
855 {
856 if (errno != ENOENT || !InRecovery)
857 {
860 return false;
861 }
862
863 ereport(LOG,
864 (errmsg("file \"%s\" doesn't exist, reading as zeroes",
865 path)));
866 MemSet(shared->page_buffer[slotno], 0, BLCKSZ);
867 return true;
868 }
869
870 errno = 0;
872 if (pg_pread(fd, shared->page_buffer[slotno], BLCKSZ, offset) != BLCKSZ)
873 {
878 return false;
879 }
881
882 if (CloseTransientFile(fd) != 0)
883 {
886 return false;
887 }
888
889 return true;
890}
891
892/*
893 * Physical write of a page from a buffer slot
894 *
895 * On failure, we cannot just ereport(ERROR) since caller has put state in
896 * shared memory that must be undone. So, we return false and save enough
897 * info in static variables to let SlruReportIOError make the report.
898 *
899 * For now, assume it's not worth keeping a file pointer open across
900 * independent read/write operations. We do batch operations during
901 * SimpleLruWriteAll, though.
902 *
903 * fdata is NULL for a standalone write, pointer to open-file info during
904 * SimpleLruWriteAll.
905 */
906static bool
908{
909 SlruShared shared = ctl->shared;
910 int64 segno = pageno / SLRU_PAGES_PER_SEGMENT;
911 int rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
912 off_t offset = rpageno * BLCKSZ;
913 char path[MAXPGPATH];
914 int fd = -1;
915
916 /* update the stats counter of written pages */
918
919 /*
920 * Honor the write-WAL-before-data rule, if appropriate, so that we do not
921 * write out data before associated WAL records. This is the same action
922 * performed during FlushBuffer() in the main buffer manager.
923 */
924 if (shared->group_lsn != NULL)
925 {
926 /*
927 * We must determine the largest async-commit LSN for the page. This
928 * is a bit tedious, but since this entire function is a slow path
929 * anyway, it seems better to do this here than to maintain a per-page
930 * LSN variable (which'd need an extra comparison in the
931 * transaction-commit path).
932 */
934 int lsnindex;
935
937 max_lsn = shared->group_lsn[lsnindex++];
938 for (int lsnoff = 1; lsnoff < shared->lsn_groups_per_page; lsnoff++)
939 {
941
942 if (max_lsn < this_lsn)
944 }
945
947 {
948 /*
949 * As noted above, elog(ERROR) is not acceptable here, so if
950 * XLogFlush were to fail, we must PANIC. This isn't much of a
951 * restriction because XLogFlush is just about all critical
952 * section anyway, but let's make sure.
953 */
957 }
958 }
959
960 /*
961 * During a SimpleLruWriteAll, we may already have the desired file open.
962 */
963 if (fdata)
964 {
965 for (int i = 0; i < fdata->num_files; i++)
966 {
967 if (fdata->segno[i] == segno)
968 {
969 fd = fdata->fd[i];
970 break;
971 }
972 }
973 }
974
975 if (fd < 0)
976 {
977 /*
978 * If the file doesn't already exist, we should create it. It is
979 * possible for this to need to happen when writing a page that's not
980 * first in its segment; we assume the OS can cope with that. (Note:
981 * it might seem that it'd be okay to create files only when
982 * SimpleLruZeroPage is called for the first page of a segment.
983 * However, if after a crash and restart the REDO logic elects to
984 * replay the log from a checkpoint before the latest one, then it's
985 * possible that we will get commands to set transaction status of
986 * transactions that have already been truncated from the commit log.
987 * Easiest way to deal with that is to accept references to
988 * nonexistent files here and in SlruPhysicalReadPage.)
989 *
990 * Note: it is possible for more than one backend to be executing this
991 * code simultaneously for different pages of the same file. Hence,
992 * don't use O_EXCL or O_TRUNC or anything like that.
993 */
994 SlruFileName(ctl, path, segno);
996 if (fd < 0)
997 {
1000 return false;
1001 }
1002
1003 if (fdata)
1004 {
1005 if (fdata->num_files < MAX_WRITEALL_BUFFERS)
1006 {
1007 fdata->fd[fdata->num_files] = fd;
1008 fdata->segno[fdata->num_files] = segno;
1009 fdata->num_files++;
1010 }
1011 else
1012 {
1013 /*
1014 * In the unlikely event that we exceed MAX_WRITEALL_BUFFERS,
1015 * fall back to treating it as a standalone write.
1016 */
1017 fdata = NULL;
1018 }
1019 }
1020 }
1021
1022 errno = 0;
1024 if (pg_pwrite(fd, shared->page_buffer[slotno], BLCKSZ, offset) != BLCKSZ)
1025 {
1027 /* if write didn't set errno, assume problem is no disk space */
1028 if (errno == 0)
1029 errno = ENOSPC;
1031 slru_errno = errno;
1032 if (!fdata)
1034 return false;
1035 }
1037
1038 /* Queue up a sync request for the checkpointer. */
1039 if (ctl->sync_handler != SYNC_HANDLER_NONE)
1040 {
1041 FileTag tag;
1042
1043 INIT_SLRUFILETAG(tag, ctl->sync_handler, segno);
1044 if (!RegisterSyncRequest(&tag, SYNC_REQUEST, false))
1045 {
1046 /* No space to enqueue sync request. Do it synchronously. */
1048 if (pg_fsync(fd) != 0)
1049 {
1052 slru_errno = errno;
1054 return false;
1055 }
1057 }
1058 }
1059
1060 /* Close file, unless part of flush request. */
1061 if (!fdata)
1062 {
1063 if (CloseTransientFile(fd) != 0)
1064 {
1066 slru_errno = errno;
1067 return false;
1068 }
1069 }
1070
1071 return true;
1072}
1073
1074/*
1075 * Issue the error message after failure of SlruPhysicalReadPage or
1076 * SlruPhysicalWritePage. Call this after cleaning up shared-memory state.
1077 */
1078static void
1079SlruReportIOError(SlruCtl ctl, int64 pageno, const void *opaque_data)
1080{
1081 int64 segno = pageno / SLRU_PAGES_PER_SEGMENT;
1082 int rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
1083 int offset = rpageno * BLCKSZ;
1084 char path[MAXPGPATH];
1085
1086 SlruFileName(ctl, path, segno);
1087 errno = slru_errno;
1088 switch (slru_errcause)
1089 {
1090 case SLRU_OPEN_FAILED:
1091 ereport(ERROR,
1093 errmsg("could not open file \"%s\": %m", path),
1094 opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
1095 break;
1096 case SLRU_SEEK_FAILED:
1097 ereport(ERROR,
1099 errmsg("could not seek in file \"%s\" to offset %d: %m",
1100 path, offset),
1101 opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
1102 break;
1103 case SLRU_READ_FAILED:
1104 if (errno)
1105 ereport(ERROR,
1107 errmsg("could not read from file \"%s\" at offset %d: %m",
1108 path, offset),
1109 opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
1110 else
1111 ereport(ERROR,
1112 (errmsg("could not read from file \"%s\" at offset %d: read too few bytes",
1113 path, offset),
1114 opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
1115 break;
1116 case SLRU_WRITE_FAILED:
1117 if (errno)
1118 ereport(ERROR,
1120 errmsg("Could not write to file \"%s\" at offset %d: %m",
1121 path, offset),
1122 opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
1123 else
1124 ereport(ERROR,
1125 (errmsg("Could not write to file \"%s\" at offset %d: wrote too few bytes.",
1126 path, offset),
1127 opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
1128 break;
1129 case SLRU_FSYNC_FAILED:
1132 errmsg("could not fsync file \"%s\": %m",
1133 path),
1134 opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
1135 break;
1136 case SLRU_CLOSE_FAILED:
1137 ereport(ERROR,
1139 errmsg("could not close file \"%s\": %m",
1140 path),
1141 opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
1142 break;
1143 default:
1144 /* can't get here, we trust */
1145 elog(ERROR, "unrecognized SimpleLru error cause: %d",
1146 (int) slru_errcause);
1147 break;
1148 }
1149}
1150
1151/*
1152 * Mark a buffer slot "most recently used".
1153 */
1154static inline void
1156{
1159
1161
1162 /*
1163 * The reason for the if-test is that there are often many consecutive
1164 * accesses to the same page (particularly the latest page). By
1165 * suppressing useless increments of bank_cur_lru_count, we reduce the
1166 * probability that old pages' counts will "wrap around" and make them
1167 * appear recently used.
1168 *
1169 * We allow this code to be executed concurrently by multiple processes
1170 * within SimpleLruReadPage_ReadOnly(). As long as int reads and writes
1171 * are atomic, this should not cause any completely-bogus values to enter
1172 * the computation. However, it is possible for either bank_cur_lru_count
1173 * or individual page_lru_count entries to be "reset" to lower values than
1174 * they should have, in case a process is delayed while it executes this
1175 * function. With care in SlruSelectLRUPage(), this does little harm, and
1176 * in any case the absolute worst possible consequence is a nonoptimal
1177 * choice of page to evict. The gain from allowing concurrent reads of
1178 * SLRU pages seems worth it.
1179 */
1180 if (new_lru_count != shared->page_lru_count[slotno])
1181 {
1184 }
1185}
1186
1187/*
1188 * Select the slot to re-use when we need a free slot for the given page.
1189 *
1190 * The target page number is passed not only because we need to know the
1191 * correct bank to use, but also because we need to consider the possibility
1192 * that some other process reads in the target page while we are doing I/O to
1193 * free a slot. Hence, check or recheck to see if any slot already holds the
1194 * target page, and return that slot if so. Thus, the returned slot is
1195 * *either* a slot already holding the pageno (could be any state except
1196 * EMPTY), *or* a freeable slot (state EMPTY or CLEAN).
1197 *
1198 * The correct bank lock must be held at entry, and will be held at exit.
1199 */
1200static int
1202{
1203 SlruShared shared = ctl->shared;
1204
1205 /* Outer loop handles restart after I/O */
1206 for (;;)
1207 {
1208 int cur_count;
1209 int bestvalidslot = 0; /* keep compiler quiet */
1210 int best_valid_delta = -1;
1211 int64 best_valid_page_number = 0; /* keep compiler quiet */
1212 int bestinvalidslot = 0; /* keep compiler quiet */
1213 int best_invalid_delta = -1;
1214 int64 best_invalid_page_number = 0; /* keep compiler quiet */
1215 int bankno = pageno % ctl->nbanks;
1218
1220
1221 /* See if page already has a buffer assigned */
1222 for (int slotno = bankstart; slotno < bankend; slotno++)
1223 {
1224 if (shared->page_status[slotno] != SLRU_PAGE_EMPTY &&
1225 shared->page_number[slotno] == pageno)
1226 return slotno;
1227 }
1228
1229 /*
1230 * If we find any EMPTY slot, just select that one. Else choose a
1231 * victim page to replace. We normally take the least recently used
1232 * valid page, but we will never take the slot containing
1233 * latest_page_number, even if it appears least recently used. We
1234 * will select a slot that is already I/O busy only if there is no
1235 * other choice: a read-busy slot will not be least recently used once
1236 * the read finishes, and waiting for an I/O on a write-busy slot is
1237 * inferior to just picking some other slot. Testing shows the slot
1238 * we pick instead will often be clean, allowing us to begin a read at
1239 * once.
1240 *
1241 * Normally the page_lru_count values will all be different and so
1242 * there will be a well-defined LRU page. But since we allow
1243 * concurrent execution of SlruRecentlyUsed() within
1244 * SimpleLruReadPage_ReadOnly(), it is possible that multiple pages
1245 * acquire the same lru_count values. In that case we break ties by
1246 * choosing the furthest-back page.
1247 *
1248 * Notice that this next line forcibly advances cur_lru_count to a
1249 * value that is certainly beyond any value that will be in the
1250 * page_lru_count array after the loop finishes. This ensures that
1251 * the next execution of SlruRecentlyUsed will mark the page newly
1252 * used, even if it's for a page that has the current counter value.
1253 * That gets us back on the path to having good data when there are
1254 * multiple pages with the same lru_count.
1255 */
1256 cur_count = (shared->bank_cur_lru_count[bankno])++;
1257 for (int slotno = bankstart; slotno < bankend; slotno++)
1258 {
1259 int this_delta;
1261
1262 if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
1263 return slotno;
1264
1266 if (this_delta < 0)
1267 {
1268 /*
1269 * Clean up in case shared updates have caused cur_count
1270 * increments to get "lost". We back off the page counts,
1271 * rather than trying to increase cur_count, to avoid any
1272 * question of infinite loops or failure in the presence of
1273 * wrapped-around counts.
1274 */
1275 shared->page_lru_count[slotno] = cur_count;
1276 this_delta = 0;
1277 }
1278
1279 /*
1280 * If this page is the one most recently zeroed, don't consider it
1281 * an eviction candidate. See comments in SimpleLruZeroPage for an
1282 * explanation about the lack of a memory barrier here.
1283 */
1285 if (this_page_number ==
1287 continue;
1288
1289 if (shared->page_status[slotno] == SLRU_PAGE_VALID)
1290 {
1293 ctl->PagePrecedes(this_page_number,
1295 {
1299 }
1300 }
1301 else
1302 {
1305 ctl->PagePrecedes(this_page_number,
1307 {
1311 }
1312 }
1313 }
1314
1315 /*
1316 * If all pages (except possibly the latest one) are I/O busy, we'll
1317 * have to wait for an I/O to complete and then retry. In that
1318 * unhappy case, we choose to wait for the I/O on the least recently
1319 * used slot, on the assumption that it was likely initiated first of
1320 * all the I/Os in progress and may therefore finish first.
1321 */
1322 if (best_valid_delta < 0)
1323 {
1325 continue;
1326 }
1327
1328 /*
1329 * If the selected page is clean, we're set.
1330 */
1331 if (!shared->page_dirty[bestvalidslot])
1332 return bestvalidslot;
1333
1334 /*
1335 * Write the page.
1336 */
1338
1339 /*
1340 * Now loop back and try again. This is the easiest way of dealing
1341 * with corner cases such as the victim page being re-dirtied while we
1342 * wrote it.
1343 */
1344 }
1345}
1346
1347/*
1348 * Write dirty pages to disk during checkpoint or database shutdown. Flushing
1349 * is deferred until the next call to ProcessSyncRequests(), though we do fsync
1350 * the containing directory here to make sure that newly created directory
1351 * entries are on disk.
1352 */
1353void
1355{
1356 SlruShared shared = ctl->shared;
1358 int64 pageno = 0;
1359 int prevbank = SlotGetBankNumber(0);
1360 bool ok;
1361
1362 /* update the stats counter of flushes */
1364
1365 /*
1366 * Find and write dirty pages
1367 */
1368 fdata.num_files = 0;
1369
1371
1372 for (int slotno = 0; slotno < shared->num_slots; slotno++)
1373 {
1375
1376 /*
1377 * If the current bank lock is not same as the previous bank lock then
1378 * release the previous lock and acquire the new lock.
1379 */
1380 if (curbank != prevbank)
1381 {
1384 prevbank = curbank;
1385 }
1386
1387 /* Do nothing if slot is unused */
1388 if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
1389 continue;
1390
1392
1393 /*
1394 * In some places (e.g. checkpoints), we cannot assert that the slot
1395 * is clean now, since another process might have re-dirtied it
1396 * already. That's okay.
1397 */
1399 shared->page_status[slotno] == SLRU_PAGE_EMPTY ||
1400 (shared->page_status[slotno] == SLRU_PAGE_VALID &&
1401 !shared->page_dirty[slotno]));
1402 }
1403
1405
1406 /*
1407 * Now close any files that were open
1408 */
1409 ok = true;
1410 for (int i = 0; i < fdata.num_files; i++)
1411 {
1412 if (CloseTransientFile(fdata.fd[i]) != 0)
1413 {
1415 slru_errno = errno;
1416 pageno = fdata.segno[i] * SLRU_PAGES_PER_SEGMENT;
1417 ok = false;
1418 }
1419 }
1420 if (!ok)
1421 SlruReportIOError(ctl, pageno, NULL);
1422
1423 /* Ensure that directory entries for new files are on disk. */
1424 if (ctl->sync_handler != SYNC_HANDLER_NONE)
1425 fsync_fname(ctl->Dir, true);
1426}
1427
1428/*
1429 * Remove all segments before the one holding the passed page number
1430 *
1431 * All SLRUs prevent concurrent calls to this function, either with an LWLock
1432 * or by calling it only as part of a checkpoint. Mutual exclusion must begin
1433 * before computing cutoffPage. Mutual exclusion must end after any limit
1434 * update that would permit other backends to write fresh data into the
1435 * segment immediately preceding the one containing cutoffPage. Otherwise,
1436 * when the SLRU is quite full, SimpleLruTruncate() might delete that segment
1437 * after it has accrued freshly-written data.
1438 */
1439void
1441{
1442 SlruShared shared = ctl->shared;
1443 int prevbank;
1444
1445 /* update the stats counter of truncates */
1447
1448 /*
1449 * Scan shared memory and remove any pages preceding the cutoff page, to
1450 * ensure we won't rewrite them later. (Since this is normally called in
1451 * or just after a checkpoint, any dirty pages should have been flushed
1452 * already ... we're just being extra careful here.)
1453 */
1454restart:
1455
1456 /*
1457 * An important safety check: the current endpoint page must not be
1458 * eligible for removal. This check is just a backstop against wraparound
1459 * bugs elsewhere in SLRU handling, so we don't care if we read a slightly
1460 * outdated value; therefore we don't add a memory barrier.
1461 */
1462 if (ctl->PagePrecedes(pg_atomic_read_u64(&shared->latest_page_number),
1463 cutoffPage))
1464 {
1465 ereport(LOG,
1466 (errmsg("could not truncate directory \"%s\": apparent wraparound",
1467 ctl->Dir)));
1468 return;
1469 }
1470
1473 for (int slotno = 0; slotno < shared->num_slots; slotno++)
1474 {
1476
1477 /*
1478 * If the current bank lock is not same as the previous bank lock then
1479 * release the previous lock and acquire the new lock.
1480 */
1481 if (curbank != prevbank)
1482 {
1485 prevbank = curbank;
1486 }
1487
1488 if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
1489 continue;
1490 if (!ctl->PagePrecedes(shared->page_number[slotno], cutoffPage))
1491 continue;
1492
1493 /*
1494 * If page is clean, just change state to EMPTY (expected case).
1495 */
1496 if (shared->page_status[slotno] == SLRU_PAGE_VALID &&
1497 !shared->page_dirty[slotno])
1498 {
1500 continue;
1501 }
1502
1503 /*
1504 * Hmm, we have (or may have) I/O operations acting on the page, so
1505 * we've got to wait for them to finish and then start again. This is
1506 * the same logic as in SlruSelectLRUPage. (XXX if page is dirty,
1507 * wouldn't it be OK to just discard it without writing it?
1508 * SlruMayDeleteSegment() uses a stricter qualification, so we might
1509 * not delete this page in the end; even if we don't delete it, we
1510 * won't have cause to read its data again. For now, keep the logic
1511 * the same as it was.)
1512 */
1513 if (shared->page_status[slotno] == SLRU_PAGE_VALID)
1515 else
1517
1519 goto restart;
1520 }
1521
1523
1524 /* Now we can remove the old segment(s) */
1526}
1527
1528/*
1529 * Delete an individual SLRU segment.
1530 *
1531 * NB: This does not touch the SLRU buffers themselves, callers have to ensure
1532 * they either can't yet contain anything, or have already been cleaned out.
1533 */
1534static void
1536{
1537 char path[MAXPGPATH];
1538
1539 /* Forget any fsync requests queued for this segment. */
1540 if (ctl->sync_handler != SYNC_HANDLER_NONE)
1541 {
1542 FileTag tag;
1543
1544 INIT_SLRUFILETAG(tag, ctl->sync_handler, segno);
1546 }
1547
1548 /* Unlink the file. */
1549 SlruFileName(ctl, path, segno);
1550 ereport(DEBUG2, (errmsg_internal("removing file \"%s\"", path)));
1551 unlink(path);
1552}
1553
1554/*
1555 * Delete an individual SLRU segment, identified by the segment number.
1556 */
1557void
1559{
1560 SlruShared shared = ctl->shared;
1561 int prevbank = SlotGetBankNumber(0);
1562 bool did_write;
1563
1564 /* Clean out any possibly existing references to the segment. */
1566restart:
1567 did_write = false;
1568 for (int slotno = 0; slotno < shared->num_slots; slotno++)
1569 {
1572
1573 /*
1574 * If the current bank lock is not same as the previous bank lock then
1575 * release the previous lock and acquire the new lock.
1576 */
1577 if (curbank != prevbank)
1578 {
1581 prevbank = curbank;
1582 }
1583
1584 if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
1585 continue;
1586
1588 /* not the segment we're looking for */
1589 if (pagesegno != segno)
1590 continue;
1591
1592 /* If page is clean, just change state to EMPTY (expected case). */
1593 if (shared->page_status[slotno] == SLRU_PAGE_VALID &&
1594 !shared->page_dirty[slotno])
1595 {
1597 continue;
1598 }
1599
1600 /* Same logic as SimpleLruTruncate() */
1601 if (shared->page_status[slotno] == SLRU_PAGE_VALID)
1603 else
1605
1606 did_write = true;
1607 }
1608
1609 /*
1610 * Be extra careful and re-check. The IO functions release the control
1611 * lock, so new pages could have been read in.
1612 */
1613 if (did_write)
1614 goto restart;
1615
1617
1619}
1620
1621/*
1622 * Determine whether a segment is okay to delete.
1623 *
1624 * segpage is the first page of the segment, and cutoffPage is the oldest (in
1625 * PagePrecedes order) page in the SLRU containing still-useful data. Since
1626 * every core PagePrecedes callback implements "wrap around", check the
1627 * segment's first and last pages:
1628 *
1629 * first<cutoff && last<cutoff: yes
1630 * first<cutoff && last>=cutoff: no; cutoff falls inside this segment
1631 * first>=cutoff && last<cutoff: no; wrap point falls inside this segment
1632 * first>=cutoff && last>=cutoff: no; every page of this segment is too young
1633 */
1634static bool
1636{
1638
1640
1641 return (ctl->PagePrecedes(segpage, cutoffPage) &&
1642 ctl->PagePrecedes(seg_last_page, cutoffPage));
1643}
1644
1645#ifdef USE_ASSERT_CHECKING
1646static void
1648{
1650 rhs;
1652 oldestPage;
1654 oldestXact;
1655
1656 /*
1657 * Compare an XID pair having undefined order (see RFC 1982), a pair at
1658 * "opposite ends" of the XID space. TransactionIdPrecedes() treats each
1659 * as preceding the other. If RHS is oldestXact, LHS is the first XID we
1660 * must not assign.
1661 */
1662 lhs = per_page + offset; /* skip first page to avoid non-normal XIDs */
1663 rhs = lhs + (1U << 31);
1672 Assert(!ctl->PagePrecedes(lhs / per_page, lhs / per_page));
1673 Assert(!ctl->PagePrecedes(lhs / per_page, rhs / per_page));
1674 Assert(!ctl->PagePrecedes(rhs / per_page, lhs / per_page));
1675 Assert(!ctl->PagePrecedes((lhs - per_page) / per_page, rhs / per_page));
1676 Assert(ctl->PagePrecedes(rhs / per_page, (lhs - 3 * per_page) / per_page));
1677 Assert(ctl->PagePrecedes(rhs / per_page, (lhs - 2 * per_page) / per_page));
1678 Assert(ctl->PagePrecedes(rhs / per_page, (lhs - 1 * per_page) / per_page)
1679 || (1U << 31) % per_page != 0); /* See CommitTsPagePrecedes() */
1680 Assert(ctl->PagePrecedes((lhs + 1 * per_page) / per_page, rhs / per_page)
1681 || (1U << 31) % per_page != 0);
1682 Assert(ctl->PagePrecedes((lhs + 2 * per_page) / per_page, rhs / per_page));
1683 Assert(ctl->PagePrecedes((lhs + 3 * per_page) / per_page, rhs / per_page));
1684 Assert(!ctl->PagePrecedes(rhs / per_page, (lhs + per_page) / per_page));
1685
1686 /*
1687 * GetNewTransactionId() has assigned the last XID it can safely use, and
1688 * that XID is in the *LAST* page of the second segment. We must not
1689 * delete that segment.
1690 */
1692 newestXact = newestPage * per_page + offset;
1694 oldestXact = newestXact + 1;
1695 oldestXact -= 1U << 31;
1696 oldestPage = oldestXact / per_page;
1698 (newestPage -
1700 oldestPage));
1701
1702 /*
1703 * GetNewTransactionId() has assigned the last XID it can safely use, and
1704 * that XID is in the *FIRST* page of the second segment. We must not
1705 * delete that segment.
1706 */
1708 newestXact = newestPage * per_page + offset;
1710 oldestXact = newestXact + 1;
1711 oldestXact -= 1U << 31;
1712 oldestPage = oldestXact / per_page;
1714 (newestPage -
1716 oldestPage));
1717}
1718
1719/*
1720 * Unit-test a PagePrecedes function.
1721 *
1722 * This assumes every uint32 >= FirstNormalTransactionId is a valid key. It
1723 * assumes each value occupies a contiguous, fixed-size region of SLRU bytes.
1724 * (MultiXactMemberCtl separates flags from XIDs. NotifyCtl has
1725 * variable-length entries, no keys, and no random access. These unit tests
1726 * do not apply to them.)
1727 */
1728void
1730{
1731 /* Test first, middle and last entries of a page. */
1735}
1736#endif
1737
1738/*
1739 * SlruScanDirectory callback
1740 * This callback reports true if there's any segment wholly prior to the
1741 * one containing the page passed as "data".
1742 */
1743bool
1745 void *data)
1746{
1747 int64 cutoffPage = *(int64 *) data;
1748
1750 return true; /* found one; don't iterate any more */
1751
1752 return false; /* keep going */
1753}
1754
1755/*
1756 * SlruScanDirectory callback.
1757 * This callback deletes segments prior to the one passed in as "data".
1758 */
1759static bool
1761 void *data)
1762{
1763 int64 cutoffPage = *(int64 *) data;
1764
1767
1768 return false; /* keep going */
1769}
1770
1771/*
1772 * SlruScanDirectory callback.
1773 * This callback deletes all segments.
1774 */
1775bool
1777{
1779
1780 return false; /* keep going */
1781}
1782
1783/*
1784 * An internal function used by SlruScanDirectory().
1785 *
1786 * Returns true if a file with a name of a given length may be a correct
1787 * SLRU segment.
1788 */
1789static inline bool
1791{
1792 if (ctl->long_segment_names)
1793 return (len == 15); /* see SlruFileName() */
1794 else
1795
1796 /*
1797 * Commit 638cf09e76d allowed 5-character lengths. Later commit
1798 * 73c986adde5 allowed 6-character length.
1799 *
1800 * Note: There is an ongoing plan to migrate all SLRUs to 64-bit page
1801 * numbers, and the corresponding 15-character file names, which may
1802 * eventually deprecate the support for 4, 5, and 6-character names.
1803 */
1804 return (len == 4 || len == 5 || len == 6);
1805}
1806
1807/*
1808 * Scan the SimpleLru directory and apply a callback to each file found in it.
1809 *
1810 * If the callback returns true, the scan is stopped. The last return value
1811 * from the callback is returned.
1812 *
1813 * The callback receives the following arguments: 1. the SlruCtl struct for the
1814 * slru being truncated; 2. the filename being considered; 3. the page number
1815 * for the first page of that file; 4. a pointer to the opaque data given to us
1816 * by the caller.
1817 *
1818 * Note that the ordering in which the directory is scanned is not guaranteed.
1819 *
1820 * Note that no locking is applied.
1821 */
1822bool
1824{
1825 bool retval = false;
1826 DIR *cldir;
1827 struct dirent *clde;
1828 int64 segno;
1829 int64 segpage;
1830
1831 cldir = AllocateDir(ctl->Dir);
1832 while ((clde = ReadDir(cldir, ctl->Dir)) != NULL)
1833 {
1834 size_t len;
1835
1836 len = strlen(clde->d_name);
1837
1839 strspn(clde->d_name, "0123456789ABCDEF") == len)
1840 {
1841 segno = strtoi64(clde->d_name, NULL, 16);
1843
1844 elog(DEBUG2, "SlruScanDirectory invoking callback on %s/%s",
1845 ctl->Dir, clde->d_name);
1846 retval = callback(ctl, clde->d_name, segpage, data);
1847 if (retval)
1848 break;
1849 }
1850 }
1851 FreeDir(cldir);
1852
1853 return retval;
1854}
1855
1856/*
1857 * Individual SLRUs (clog, ...) have to provide a sync.c handler function so
1858 * that they can provide the correct "SlruCtl" (otherwise we don't know how to
1859 * build the path), but they just forward to this common implementation that
1860 * performs the fsync.
1861 */
1862int
1863SlruSyncFileTag(SlruCtl ctl, const FileTag *ftag, char *path)
1864{
1865 int fd;
1866 int save_errno;
1867 int result;
1868
1869 SlruFileName(ctl, path, ftag->segno);
1870
1872 if (fd < 0)
1873 return -1;
1874
1876 result = pg_fsync(fd);
1878 save_errno = errno;
1879
1881
1882 errno = save_errno;
1883 return result;
1884}
static void pg_atomic_write_u64(volatile pg_atomic_uint64 *ptr, uint64 val)
Definition atomics.h:485
static void pg_atomic_init_u64(volatile pg_atomic_uint64 *ptr, uint64 val)
Definition atomics.h:453
static uint64 pg_atomic_read_u64(volatile pg_atomic_uint64 *ptr)
Definition atomics.h:467
#define Min(x, y)
Definition c.h:1093
#define MAXALIGN(LEN)
Definition c.h:898
#define Max(x, y)
Definition c.h:1087
#define BUFFERALIGN(LEN)
Definition c.h:900
#define Assert(condition)
Definition c.h:945
int64_t int64
Definition c.h:615
#define PG_BINARY
Definition c.h:1376
uint32_t uint32
Definition c.h:618
#define MemSet(start, val, len)
Definition c.h:1109
uint32 TransactionId
Definition c.h:738
size_t Size
Definition c.h:691
int errcode_for_file_access(void)
Definition elog.c:897
#define LOG
Definition elog.h:31
int int errmsg_internal(const char *fmt,...) pg_attribute_printf(1
#define DEBUG2
Definition elog.h:29
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
#define ereport(elevel,...)
Definition elog.h:150
int FreeDir(DIR *dir)
Definition fd.c:3009
int CloseTransientFile(int fd)
Definition fd.c:2855
void fsync_fname(const char *fname, bool isdir)
Definition fd.c:757
int data_sync_elevel(int elevel)
Definition fd.c:3986
DIR * AllocateDir(const char *dirname)
Definition fd.c:2891
struct dirent * ReadDir(DIR *dir, const char *dirname)
Definition fd.c:2957
int pg_fsync(int fd)
Definition fd.c:390
int OpenTransientFile(const char *fileName, int fileFlags)
Definition fd.c:2678
int NBuffers
Definition globals.c:142
bool IsUnderPostmaster
Definition globals.c:120
#define newval
#define GUC_check_errdetail
Definition guc.h:507
int i
Definition isn.c:77
bool LWLockHeldByMe(LWLock *lock)
Definition lwlock.c:1912
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition lwlock.c:1177
bool LWLockHeldByMeInMode(LWLock *lock, LWLockMode mode)
Definition lwlock.c:1956
void LWLockRelease(LWLock *lock)
Definition lwlock.c:1794
void LWLockInitialize(LWLock *lock, int tranche_id)
Definition lwlock.c:699
bool LWLockConditionalAcquire(LWLock *lock, LWLockMode mode)
Definition lwlock.c:1348
@ LW_SHARED
Definition lwlock.h:113
@ LW_EXCLUSIVE
Definition lwlock.h:112
#define START_CRIT_SECTION()
Definition miscadmin.h:150
#define END_CRIT_SECTION()
Definition miscadmin.h:152
static char * errmsg
#define MAXPGPATH
#define SLRU_PAGES_PER_SEGMENT
const void size_t len
const void * data
static char * filename
Definition pg_dumpall.c:133
static XLogRecPtr endpos
void pgstat_count_slru_blocks_zeroed(int slru_idx)
void pgstat_count_slru_blocks_hit(int slru_idx)
void pgstat_count_slru_truncate(int slru_idx)
void pgstat_count_slru_blocks_read(int slru_idx)
void pgstat_count_slru_blocks_written(int slru_idx)
void pgstat_count_slru_flush(int slru_idx)
void pgstat_count_slru_blocks_exists(int slru_idx)
PgStat_CheckpointerStats PendingCheckpointerStats
int pgstat_get_slru_index(const char *name)
#define pg_pwrite
Definition port.h:248
#define pg_pread
Definition port.h:247
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition strlcpy.c:45
static int fd(const char *x, int i)
tree ctl
Definition radixtree.h:1838
void * ShmemInitStruct(const char *name, Size size, bool *foundPtr)
Definition shmem.c:381
void SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns, const char *subdir, int buffer_tranche_id, int bank_tranche_id, SyncRequestHandler sync_handler, bool long_segment_names)
Definition slru.c:254
static int SlruFileName(SlruCtl ctl, char *path, int64 segno)
Definition slru.c:92
static bool SlruPhysicalReadPage(SlruCtl ctl, int64 pageno, int slotno)
Definition slru.c:836
#define INIT_SLRUFILETAG(a, xx_handler, xx_segno)
Definition slru.c:157
void SimpleLruWritePage(SlruCtl ctl, int slotno)
Definition slru.c:764
void SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied)
Definition slru.c:1355
static bool SlruMayDeleteSegment(SlruCtl ctl, int64 segpage, int64 cutoffPage)
Definition slru.c:1636
static void SimpleLruZeroLSNs(SlruCtl ctl, int slotno)
Definition slru.c:433
#define SLRU_BANK_SIZE
Definition slru.c:144
int SimpleLruAutotuneBuffers(int divisor, int max)
Definition slru.c:233
static void SlruReportIOError(SlruCtl ctl, int64 pageno, const void *opaque_data)
Definition slru.c:1080
static bool SlruPhysicalWritePage(SlruCtl ctl, int64 pageno, int slotno, SlruWriteAll fdata)
Definition slru.c:908
static bool SlruCorrectSegmentFilenameLength(SlruCtl ctl, size_t len)
Definition slru.c:1791
static SlruErrorCause slru_errcause
Definition slru.c:175
#define MAX_WRITEALL_BUFFERS
Definition slru.c:124
int SimpleLruReadPage(SlruCtl ctl, int64 pageno, bool write_ok, const void *opaque_data)
Definition slru.c:533
static void SimpleLruWaitIO(SlruCtl ctl, int slotno)
Definition slru.c:475
static int slru_errno
Definition slru.c:176
bool SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int64 pageno)
Definition slru.c:778
void SlruDeleteSegment(SlruCtl ctl, int64 segno)
Definition slru.c:1559
static void SlruInternalWritePage(SlruCtl ctl, int slotno, SlruWriteAll fdata)
Definition slru.c:684
bool SlruScanDirectory(SlruCtl ctl, SlruScanCallback callback, void *data)
Definition slru.c:1824
bool SlruScanDirCbDeleteAll(SlruCtl ctl, char *filename, int64 segpage, void *data)
Definition slru.c:1777
int SlruSyncFileTag(SlruCtl ctl, const FileTag *ftag, char *path)
Definition slru.c:1864
static int SlruSelectLRUPage(SlruCtl ctl, int64 pageno)
Definition slru.c:1202
#define SlotGetBankNumber(slotno)
Definition slru.c:149
int SimpleLruZeroPage(SlruCtl ctl, int64 pageno)
Definition slru.c:380
void SimpleLruZeroAndWritePage(SlruCtl ctl, int64 pageno)
Definition slru.c:449
void SimpleLruTruncate(SlruCtl ctl, int64 cutoffPage)
Definition slru.c:1441
static void SlruInternalDeleteSegment(SlruCtl ctl, int64 segno)
Definition slru.c:1536
int SimpleLruReadPage_ReadOnly(SlruCtl ctl, int64 pageno, const void *opaque_data)
Definition slru.c:637
SlruErrorCause
Definition slru.c:166
@ SLRU_WRITE_FAILED
Definition slru.c:170
@ SLRU_FSYNC_FAILED
Definition slru.c:171
@ SLRU_SEEK_FAILED
Definition slru.c:168
@ SLRU_OPEN_FAILED
Definition slru.c:167
@ SLRU_CLOSE_FAILED
Definition slru.c:172
@ SLRU_READ_FAILED
Definition slru.c:169
Size SimpleLruShmemSize(int nslots, int nlsns)
Definition slru.c:200
bool SlruScanDirCbReportPresence(SlruCtl ctl, char *filename, int64 segpage, void *data)
Definition slru.c:1745
static bool SlruScanDirCbDeleteCutoff(SlruCtl ctl, char *filename, int64 segpage, void *data)
Definition slru.c:1761
static void SlruRecentlyUsed(SlruShared shared, int slotno)
Definition slru.c:1156
bool check_slru_buffers(const char *name, int *newval)
Definition slru.c:360
static LWLock * SimpleLruGetBankLock(SlruCtl ctl, int64 pageno)
Definition slru.h:171
SlruSharedData * SlruShared
Definition slru.h:107
#define SlruPagePrecedesUnitTests(ctl, per_page)
Definition slru.h:196
bool(* SlruScanCallback)(SlruCtl ctl, char *filename, int64 segpage, void *data)
Definition slru.h:201
#define SLRU_MAX_ALLOWED_BUFFERS
Definition slru.h:25
SlruPageStatus
Definition slru.h:34
@ SLRU_PAGE_VALID
Definition slru.h:37
@ SLRU_PAGE_WRITE_IN_PROGRESS
Definition slru.h:38
@ SLRU_PAGE_READ_IN_PROGRESS
Definition slru.h:36
@ SLRU_PAGE_EMPTY
Definition slru.h:35
int ckpt_slru_written
Definition xlog.h:179
Definition dirent.c:26
uint64 segno
Definition sync.h:55
PgStat_Counter slru_written
Definition pgstat.h:270
int slru_stats_idx
Definition slru.h:104
int64 * page_number
Definition slru.h:59
int num_slots
Definition slru.h:50
LWLockPadded * bank_locks
Definition slru.h:66
int * page_lru_count
Definition slru.h:60
pg_atomic_uint64 latest_page_number
Definition slru.h:101
XLogRecPtr * group_lsn
Definition slru.h:93
int * bank_cur_lru_count
Definition slru.h:83
int lsn_groups_per_page
Definition slru.h:94
SlruPageStatus * page_status
Definition slru.h:57
bool * page_dirty
Definition slru.h:58
LWLockPadded * buffer_locks
Definition slru.h:63
char ** page_buffer
Definition slru.h:56
bool RegisterSyncRequest(const FileTag *ftag, SyncRequestType type, bool retryOnError)
Definition sync.c:581
SyncRequestHandler
Definition sync.h:36
@ SYNC_HANDLER_NONE
Definition sync.h:42
@ SYNC_FORGET_REQUEST
Definition sync.h:27
@ SYNC_REQUEST
Definition sync.h:25
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
static bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
Definition transam.h:312
static bool TransactionIdPrecedes(TransactionId id1, TransactionId id2)
Definition transam.h:263
LWLock lock
Definition lwlock.h:70
static void pgstat_report_wait_start(uint32 wait_event_info)
Definition wait_event.h:69
static void pgstat_report_wait_end(void)
Definition wait_event.h:85
const char * name
CheckpointStatsData CheckpointStats
Definition xlog.c:213
void XLogFlush(XLogRecPtr record)
Definition xlog.c:2767
#define XLogRecPtrIsValid(r)
Definition xlogdefs.h:29
uint64 XLogRecPtr
Definition xlogdefs.h:21
bool InRecovery
Definition xlogutils.c:50

◆ MAX_WRITEALL_BUFFERS

#define MAX_WRITEALL_BUFFERS   16

Definition at line 124 of file slru.c.

◆ SlotGetBankNumber

#define SlotGetBankNumber (   slotno)    ((slotno) >> SLRU_BANK_BITSHIFT)

Definition at line 149 of file slru.c.

◆ SLRU_BANK_BITSHIFT

#define SLRU_BANK_BITSHIFT   4

Definition at line 143 of file slru.c.

◆ SLRU_BANK_SIZE

#define SLRU_BANK_SIZE   (1 << SLRU_BANK_BITSHIFT)

Definition at line 144 of file slru.c.

Typedef Documentation

◆ SlruWriteAll

Definition at line 133 of file slru.c.

◆ SlruWriteAllData

Enumeration Type Documentation

◆ SlruErrorCause

Enumerator
SLRU_OPEN_FAILED 
SLRU_SEEK_FAILED 
SLRU_READ_FAILED 
SLRU_WRITE_FAILED 
SLRU_FSYNC_FAILED 
SLRU_CLOSE_FAILED 

Definition at line 165 of file slru.c.

Function Documentation

◆ check_slru_buffers()

bool check_slru_buffers ( const char name,
int newval 
)

Definition at line 360 of file slru.c.

361{
362 /* Valid values are multiples of SLRU_BANK_SIZE */
363 if (*newval % SLRU_BANK_SIZE == 0)
364 return true;
365
366 GUC_check_errdetail("\"%s\" must be a multiple of %d.", name,
368 return false;
369}

References GUC_check_errdetail, name, newval, and SLRU_BANK_SIZE.

Referenced by check_commit_ts_buffers(), check_multixact_member_buffers(), check_multixact_offset_buffers(), check_notify_buffers(), check_serial_buffers(), check_subtrans_buffers(), and check_transaction_buffers().

◆ SimpleLruAutotuneBuffers()

int SimpleLruAutotuneBuffers ( int  divisor,
int  max 
)

Definition at line 233 of file slru.c.

234{
235 return Min(max - (max % SLRU_BANK_SIZE),
238}

References fb(), Max, Min, NBuffers, and SLRU_BANK_SIZE.

Referenced by CLOGShmemBuffers(), CommitTsShmemBuffers(), and SUBTRANSShmemBuffers().

◆ SimpleLruDoesPhysicalPageExist()

bool SimpleLruDoesPhysicalPageExist ( SlruCtl  ctl,
int64  pageno 
)

Definition at line 778 of file slru.c.

779{
780 int64 segno = pageno / SLRU_PAGES_PER_SEGMENT;
781 int rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
782 int offset = rpageno * BLCKSZ;
783 char path[MAXPGPATH];
784 int fd;
785 bool result;
787
788 /* update the stats counter of checked pages */
789 pgstat_count_slru_blocks_exists(ctl->shared->slru_stats_idx);
790
791 SlruFileName(ctl, path, segno);
792
794 if (fd < 0)
795 {
796 /* expected: file doesn't exist */
797 if (errno == ENOENT)
798 return false;
799
800 /* report error normally */
803 SlruReportIOError(ctl, pageno, NULL);
804 }
805
806 if ((endpos = lseek(fd, 0, SEEK_END)) < 0)
807 {
810 SlruReportIOError(ctl, pageno, NULL);
811 }
812
813 result = endpos >= (off_t) (offset + BLCKSZ);
814
815 if (CloseTransientFile(fd) != 0)
816 {
819 return false;
820 }
821
822 return result;
823}

References CloseTransientFile(), ctl, endpos, fb(), fd(), MAXPGPATH, OpenTransientFile(), PG_BINARY, pgstat_count_slru_blocks_exists(), SlruWriteAllData::segno, SLRU_CLOSE_FAILED, slru_errcause, slru_errno, SLRU_OPEN_FAILED, SLRU_PAGES_PER_SEGMENT, SLRU_SEEK_FAILED, SlruFileName(), and SlruReportIOError().

Referenced by ActivateCommitTs(), find_multixact_start(), and test_slru_page_exists().

◆ SimpleLruInit()

void SimpleLruInit ( SlruCtl  ctl,
const char name,
int  nslots,
int  nlsns,
const char subdir,
int  buffer_tranche_id,
int  bank_tranche_id,
SyncRequestHandler  sync_handler,
bool  long_segment_names 
)

Definition at line 254 of file slru.c.

257{
258 SlruShared shared;
259 bool found;
260 int nbanks = nslots / SLRU_BANK_SIZE;
261
263
264 Assert(ctl->PagePrecedes != NULL);
265 Assert(ctl->errdetail_for_io_error != NULL);
266
268 SimpleLruShmemSize(nslots, nlsns),
269 &found);
270
272 {
273 /* Initialize locks and shared memory area */
274 char *ptr;
275 Size offset;
276
277 Assert(!found);
278
279 memset(shared, 0, sizeof(SlruSharedData));
280
281 shared->num_slots = nslots;
282 shared->lsn_groups_per_page = nlsns;
283
285
287
288 ptr = (char *) shared;
289 offset = MAXALIGN(sizeof(SlruSharedData));
290 shared->page_buffer = (char **) (ptr + offset);
291 offset += MAXALIGN(nslots * sizeof(char *));
292 shared->page_status = (SlruPageStatus *) (ptr + offset);
293 offset += MAXALIGN(nslots * sizeof(SlruPageStatus));
294 shared->page_dirty = (bool *) (ptr + offset);
295 offset += MAXALIGN(nslots * sizeof(bool));
296 shared->page_number = (int64 *) (ptr + offset);
297 offset += MAXALIGN(nslots * sizeof(int64));
298 shared->page_lru_count = (int *) (ptr + offset);
299 offset += MAXALIGN(nslots * sizeof(int));
300
301 /* Initialize LWLocks */
302 shared->buffer_locks = (LWLockPadded *) (ptr + offset);
303 offset += MAXALIGN(nslots * sizeof(LWLockPadded));
304 shared->bank_locks = (LWLockPadded *) (ptr + offset);
305 offset += MAXALIGN(nbanks * sizeof(LWLockPadded));
306 shared->bank_cur_lru_count = (int *) (ptr + offset);
307 offset += MAXALIGN(nbanks * sizeof(int));
308
309 if (nlsns > 0)
310 {
311 shared->group_lsn = (XLogRecPtr *) (ptr + offset);
312 offset += MAXALIGN(nslots * nlsns * sizeof(XLogRecPtr));
313 }
314
315 ptr += BUFFERALIGN(offset);
316 for (int slotno = 0; slotno < nslots; slotno++)
317 {
320
321 shared->page_buffer[slotno] = ptr;
323 shared->page_dirty[slotno] = false;
324 shared->page_lru_count[slotno] = 0;
325 ptr += BLCKSZ;
326 }
327
328 /* Initialize the slot banks. */
329 for (int bankno = 0; bankno < nbanks; bankno++)
330 {
332 shared->bank_cur_lru_count[bankno] = 0;
333 }
334
335 /* Should fit to estimated shmem size */
336 Assert(ptr - (char *) shared <= SimpleLruShmemSize(nslots, nlsns));
337 }
338 else
339 {
340 Assert(found);
341 Assert(shared->num_slots == nslots);
342 }
343
344 /*
345 * Initialize the unshared control struct, including directory path. We
346 * assume caller set PagePrecedes.
347 */
348 ctl->shared = shared;
349 ctl->sync_handler = sync_handler;
350 ctl->long_segment_names = long_segment_names;
351 ctl->nbanks = nbanks;
352 strlcpy(ctl->Dir, subdir, sizeof(ctl->Dir));
353}

References Assert, SlruSharedData::bank_cur_lru_count, SlruSharedData::bank_locks, SlruSharedData::buffer_locks, BUFFERALIGN, ctl, fb(), SlruSharedData::group_lsn, IsUnderPostmaster, SlruSharedData::latest_page_number, LWLockPadded::lock, SlruSharedData::lsn_groups_per_page, LWLockInitialize(), MAXALIGN, name, SlruSharedData::num_slots, SlruSharedData::page_buffer, SlruSharedData::page_dirty, SlruSharedData::page_lru_count, SlruSharedData::page_number, SlruSharedData::page_status, pg_atomic_init_u64(), pgstat_get_slru_index(), ShmemInitStruct(), SimpleLruShmemSize(), SLRU_BANK_SIZE, SLRU_MAX_ALLOWED_BUFFERS, SLRU_PAGE_EMPTY, SlruSharedData::slru_stats_idx, and strlcpy().

Referenced by AsyncShmemInit(), CLOGShmemInit(), CommitTsShmemInit(), MultiXactShmemInit(), SerialInit(), SUBTRANSShmemInit(), and test_slru_shmem_startup().

◆ SimpleLruReadPage()

int SimpleLruReadPage ( SlruCtl  ctl,
int64  pageno,
bool  write_ok,
const void opaque_data 
)

Definition at line 533 of file slru.c.

535{
536 SlruShared shared = ctl->shared;
538
540
541 /* Outer loop handles restart if we must wait for someone else's I/O */
542 for (;;)
543 {
544 int slotno;
545 bool ok;
546
547 /* See if page already is in memory; if not, pick victim slot */
548 slotno = SlruSelectLRUPage(ctl, pageno);
549
550 /* Did we find the page in memory? */
551 if (shared->page_status[slotno] != SLRU_PAGE_EMPTY &&
552 shared->page_number[slotno] == pageno)
553 {
554 /*
555 * If page is still being read in, we must wait for I/O. Likewise
556 * if the page is being written and the caller said that's not OK.
557 */
560 !write_ok))
561 {
563 /* Now we must recheck state from the top */
564 continue;
565 }
566 /* Otherwise, it's ready to use */
567 SlruRecentlyUsed(shared, slotno);
568
569 /* update the stats counter of pages found in the SLRU */
571
572 return slotno;
573 }
574
575 /* We found no match; assert we selected a freeable slot */
577 (shared->page_status[slotno] == SLRU_PAGE_VALID &&
578 !shared->page_dirty[slotno]));
579
580 /* Mark the slot read-busy */
581 shared->page_number[slotno] = pageno;
583 shared->page_dirty[slotno] = false;
584
585 /* Acquire per-buffer lock (cannot deadlock, see notes at top) */
587
588 /* Release bank lock while doing I/O */
590
591 /* Do the read */
592 ok = SlruPhysicalReadPage(ctl, pageno, slotno);
593
594 /* Set the LSNs for this newly read-in page to zero */
596
597 /* Re-acquire bank control lock and update page state */
599
600 Assert(shared->page_number[slotno] == pageno &&
602 !shared->page_dirty[slotno]);
603
605
607
608 /* Now it's okay to ereport if we failed */
609 if (!ok)
611
612 SlruRecentlyUsed(shared, slotno);
613
614 /* update the stats counter of pages not found in SLRU */
616
617 return slotno;
618 }
619}

References Assert, SlruSharedData::buffer_locks, ctl, fb(), LWLockPadded::lock, LW_EXCLUSIVE, LWLockAcquire(), LWLockHeldByMeInMode(), LWLockRelease(), SlruSharedData::page_dirty, SlruSharedData::page_number, SlruSharedData::page_status, pgstat_count_slru_blocks_hit(), pgstat_count_slru_blocks_read(), SimpleLruGetBankLock(), SimpleLruWaitIO(), SimpleLruZeroLSNs(), SLRU_PAGE_EMPTY, SLRU_PAGE_READ_IN_PROGRESS, SLRU_PAGE_VALID, SLRU_PAGE_WRITE_IN_PROGRESS, SlruSharedData::slru_stats_idx, SlruPhysicalReadPage(), SlruRecentlyUsed(), SlruReportIOError(), and SlruSelectLRUPage().

Referenced by AsyncNotifyFreezeXids(), asyncQueueAddEntries(), GetMultiXactIdMembers(), RecordNewMultiXact(), SerialAdd(), SetXidCommitTsInPage(), SimpleLruReadPage_ReadOnly(), SubTransSetParent(), test_slru_page_read(), TransactionIdSetPageStatusInternal(), TrimCLOG(), and TrimMultiXact().

◆ SimpleLruReadPage_ReadOnly()

int SimpleLruReadPage_ReadOnly ( SlruCtl  ctl,
int64  pageno,
const void opaque_data 
)

Definition at line 637 of file slru.c.

638{
639 SlruShared shared = ctl->shared;
641 int bankno = pageno % ctl->nbanks;
644
645 /* Try to find the page while holding only shared lock */
647
648 /* See if page is already in a buffer */
649 for (int slotno = bankstart; slotno < bankend; slotno++)
650 {
651 if (shared->page_status[slotno] != SLRU_PAGE_EMPTY &&
652 shared->page_number[slotno] == pageno &&
654 {
655 /* See comments for SlruRecentlyUsed() */
656 SlruRecentlyUsed(shared, slotno);
657
658 /* update the stats counter of pages found in the SLRU */
660
661 return slotno;
662 }
663 }
664
665 /* No luck, so switch to normal exclusive lock and do regular read */
668
669 return SimpleLruReadPage(ctl, pageno, true, opaque_data);
670}

References ctl, fb(), LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), SlruSharedData::page_number, SlruSharedData::page_status, pgstat_count_slru_blocks_hit(), SimpleLruGetBankLock(), SimpleLruReadPage(), SLRU_BANK_SIZE, SLRU_PAGE_EMPTY, SLRU_PAGE_READ_IN_PROGRESS, SlruSharedData::slru_stats_idx, and SlruRecentlyUsed().

Referenced by asyncQueueProcessPageEntries(), find_multixact_start(), SerialGetMinConflictCommitSeqNo(), SubTransGetParent(), test_slru_page_readonly(), TransactionIdGetCommitTsData(), and TransactionIdGetStatus().

◆ SimpleLruShmemSize()

Size SimpleLruShmemSize ( int  nslots,
int  nlsns 
)

Definition at line 200 of file slru.c.

201{
202 int nbanks = nslots / SLRU_BANK_SIZE;
203 Size sz;
204
206 Assert(nslots % SLRU_BANK_SIZE == 0);
207
208 /* we assume nslots isn't so large as to risk overflow */
209 sz = MAXALIGN(sizeof(SlruSharedData));
210 sz += MAXALIGN(nslots * sizeof(char *)); /* page_buffer[] */
211 sz += MAXALIGN(nslots * sizeof(SlruPageStatus)); /* page_status[] */
212 sz += MAXALIGN(nslots * sizeof(bool)); /* page_dirty[] */
213 sz += MAXALIGN(nslots * sizeof(int64)); /* page_number[] */
214 sz += MAXALIGN(nslots * sizeof(int)); /* page_lru_count[] */
215 sz += MAXALIGN(nslots * sizeof(LWLockPadded)); /* buffer_locks[] */
216 sz += MAXALIGN(nbanks * sizeof(LWLockPadded)); /* bank_locks[] */
217 sz += MAXALIGN(nbanks * sizeof(int)); /* bank_cur_lru_count[] */
218
219 if (nlsns > 0)
220 sz += MAXALIGN(nslots * nlsns * sizeof(XLogRecPtr)); /* group_lsn[] */
221
222 return BUFFERALIGN(sz) + BLCKSZ * nslots;
223}

References Assert, BUFFERALIGN, fb(), MAXALIGN, SLRU_BANK_SIZE, and SLRU_MAX_ALLOWED_BUFFERS.

Referenced by AsyncShmemSize(), CLOGShmemSize(), CommitTsShmemSize(), MultiXactShmemSize(), PredicateLockShmemSize(), SimpleLruInit(), SUBTRANSShmemSize(), and test_slru_shmem_request().

◆ SimpleLruTruncate()

void SimpleLruTruncate ( SlruCtl  ctl,
int64  cutoffPage 
)

Definition at line 1441 of file slru.c.

1442{
1443 SlruShared shared = ctl->shared;
1444 int prevbank;
1445
1446 /* update the stats counter of truncates */
1448
1449 /*
1450 * Scan shared memory and remove any pages preceding the cutoff page, to
1451 * ensure we won't rewrite them later. (Since this is normally called in
1452 * or just after a checkpoint, any dirty pages should have been flushed
1453 * already ... we're just being extra careful here.)
1454 */
1455restart:
1456
1457 /*
1458 * An important safety check: the current endpoint page must not be
1459 * eligible for removal. This check is just a backstop against wraparound
1460 * bugs elsewhere in SLRU handling, so we don't care if we read a slightly
1461 * outdated value; therefore we don't add a memory barrier.
1462 */
1463 if (ctl->PagePrecedes(pg_atomic_read_u64(&shared->latest_page_number),
1464 cutoffPage))
1465 {
1466 ereport(LOG,
1467 (errmsg("could not truncate directory \"%s\": apparent wraparound",
1468 ctl->Dir)));
1469 return;
1470 }
1471
1474 for (int slotno = 0; slotno < shared->num_slots; slotno++)
1475 {
1477
1478 /*
1479 * If the current bank lock is not same as the previous bank lock then
1480 * release the previous lock and acquire the new lock.
1481 */
1482 if (curbank != prevbank)
1483 {
1486 prevbank = curbank;
1487 }
1488
1489 if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
1490 continue;
1491 if (!ctl->PagePrecedes(shared->page_number[slotno], cutoffPage))
1492 continue;
1493
1494 /*
1495 * If page is clean, just change state to EMPTY (expected case).
1496 */
1497 if (shared->page_status[slotno] == SLRU_PAGE_VALID &&
1498 !shared->page_dirty[slotno])
1499 {
1501 continue;
1502 }
1503
1504 /*
1505 * Hmm, we have (or may have) I/O operations acting on the page, so
1506 * we've got to wait for them to finish and then start again. This is
1507 * the same logic as in SlruSelectLRUPage. (XXX if page is dirty,
1508 * wouldn't it be OK to just discard it without writing it?
1509 * SlruMayDeleteSegment() uses a stricter qualification, so we might
1510 * not delete this page in the end; even if we don't delete it, we
1511 * won't have cause to read its data again. For now, keep the logic
1512 * the same as it was.)
1513 */
1514 if (shared->page_status[slotno] == SLRU_PAGE_VALID)
1516 else
1518
1520 goto restart;
1521 }
1522
1524
1525 /* Now we can remove the old segment(s) */
1527}

References SlruSharedData::bank_locks, ctl, ereport, errmsg, fb(), SlruSharedData::latest_page_number, LWLockPadded::lock, LOG, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), SlruSharedData::num_slots, SlruSharedData::page_dirty, SlruSharedData::page_number, SlruSharedData::page_status, pg_atomic_read_u64(), pgstat_count_slru_truncate(), SimpleLruWaitIO(), SlotGetBankNumber, SLRU_PAGE_EMPTY, SLRU_PAGE_VALID, SlruSharedData::slru_stats_idx, SlruInternalWritePage(), SlruScanDirCbDeleteCutoff(), and SlruScanDirectory().

Referenced by asyncQueueAdvanceTail(), CheckPointPredicate(), clog_redo(), commit_ts_redo(), PerformMembersTruncation(), PerformOffsetsTruncation(), test_slru_page_truncate(), TruncateCLOG(), TruncateCommitTs(), and TruncateSUBTRANS().

◆ SimpleLruWaitIO()

static void SimpleLruWaitIO ( SlruCtl  ctl,
int  slotno 
)
static

Definition at line 475 of file slru.c.

476{
477 SlruShared shared = ctl->shared;
479
481
482 /* See notes at top of file */
487
488 /*
489 * If the slot is still in an io-in-progress state, then either someone
490 * already started a new I/O on the slot, or a previous I/O failed and
491 * neglected to reset the page state. That shouldn't happen, really, but
492 * it seems worth a few extra cycles to check and recover from it. We can
493 * cheaply test for failure by seeing if the buffer lock is still held (we
494 * assume that transaction abort would release the lock).
495 */
498 {
500 {
501 /* indeed, the I/O must have failed */
504 else /* write_in_progress */
505 {
507 shared->page_dirty[slotno] = true;
508 }
510 }
511 }
512}

References Assert, SlruSharedData::bank_locks, SlruSharedData::buffer_locks, ctl, fb(), LWLockPadded::lock, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockConditionalAcquire(), LWLockRelease(), SlruSharedData::page_dirty, SlruSharedData::page_status, SlotGetBankNumber, SLRU_PAGE_EMPTY, SLRU_PAGE_READ_IN_PROGRESS, SLRU_PAGE_VALID, and SLRU_PAGE_WRITE_IN_PROGRESS.

Referenced by SimpleLruReadPage(), SimpleLruTruncate(), SlruDeleteSegment(), SlruInternalWritePage(), and SlruSelectLRUPage().

◆ SimpleLruWriteAll()

void SimpleLruWriteAll ( SlruCtl  ctl,
bool  allow_redirtied 
)

Definition at line 1355 of file slru.c.

1356{
1357 SlruShared shared = ctl->shared;
1359 int64 pageno = 0;
1360 int prevbank = SlotGetBankNumber(0);
1361 bool ok;
1362
1363 /* update the stats counter of flushes */
1365
1366 /*
1367 * Find and write dirty pages
1368 */
1369 fdata.num_files = 0;
1370
1372
1373 for (int slotno = 0; slotno < shared->num_slots; slotno++)
1374 {
1376
1377 /*
1378 * If the current bank lock is not same as the previous bank lock then
1379 * release the previous lock and acquire the new lock.
1380 */
1381 if (curbank != prevbank)
1382 {
1385 prevbank = curbank;
1386 }
1387
1388 /* Do nothing if slot is unused */
1389 if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
1390 continue;
1391
1393
1394 /*
1395 * In some places (e.g. checkpoints), we cannot assert that the slot
1396 * is clean now, since another process might have re-dirtied it
1397 * already. That's okay.
1398 */
1400 shared->page_status[slotno] == SLRU_PAGE_EMPTY ||
1401 (shared->page_status[slotno] == SLRU_PAGE_VALID &&
1402 !shared->page_dirty[slotno]));
1403 }
1404
1406
1407 /*
1408 * Now close any files that were open
1409 */
1410 ok = true;
1411 for (int i = 0; i < fdata.num_files; i++)
1412 {
1413 if (CloseTransientFile(fdata.fd[i]) != 0)
1414 {
1416 slru_errno = errno;
1417 pageno = fdata.segno[i] * SLRU_PAGES_PER_SEGMENT;
1418 ok = false;
1419 }
1420 }
1421 if (!ok)
1422 SlruReportIOError(ctl, pageno, NULL);
1423
1424 /* Ensure that directory entries for new files are on disk. */
1425 if (ctl->sync_handler != SYNC_HANDLER_NONE)
1426 fsync_fname(ctl->Dir, true);
1427}

References Assert, SlruSharedData::bank_locks, CloseTransientFile(), ctl, fb(), fsync_fname(), i, LWLockPadded::lock, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), SlruSharedData::num_slots, SlruSharedData::page_dirty, SlruSharedData::page_status, pgstat_count_slru_flush(), SlotGetBankNumber, SLRU_CLOSE_FAILED, slru_errcause, slru_errno, SLRU_PAGE_EMPTY, SLRU_PAGE_VALID, SLRU_PAGES_PER_SEGMENT, SlruSharedData::slru_stats_idx, SlruInternalWritePage(), SlruReportIOError(), and SYNC_HANDLER_NONE.

Referenced by CheckPointCLOG(), CheckPointCommitTs(), CheckPointMultiXact(), CheckPointPredicate(), CheckPointSUBTRANS(), find_multixact_start(), and test_slru_page_writeall().

◆ SimpleLruWritePage()

void SimpleLruWritePage ( SlruCtl  ctl,
int  slotno 
)

Definition at line 764 of file slru.c.

765{
766 Assert(ctl->shared->page_status[slotno] != SLRU_PAGE_EMPTY);
767
769}

References Assert, ctl, fb(), SLRU_PAGE_EMPTY, and SlruInternalWritePage().

Referenced by SimpleLruZeroAndWritePage(), and test_slru_page_write().

◆ SimpleLruZeroAndWritePage()

void SimpleLruZeroAndWritePage ( SlruCtl  ctl,
int64  pageno 
)

Definition at line 449 of file slru.c.

450{
451 int slotno;
452 LWLock *lock;
453
454 lock = SimpleLruGetBankLock(ctl, pageno);
456
457 /* Create and zero the page */
458 slotno = SimpleLruZeroPage(ctl, pageno);
459
460 /* Make sure it's written out */
462 Assert(!ctl->shared->page_dirty[slotno]);
463
464 LWLockRelease(lock);
465}

References Assert, ctl, fb(), LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), SimpleLruGetBankLock(), SimpleLruWritePage(), and SimpleLruZeroPage().

Referenced by ActivateCommitTs(), BootStrapCLOG(), BootStrapMultiXact(), BootStrapSUBTRANS(), clog_redo(), commit_ts_redo(), and multixact_redo().

◆ SimpleLruZeroLSNs()

static void SimpleLruZeroLSNs ( SlruCtl  ctl,
int  slotno 
)
static

Definition at line 433 of file slru.c.

434{
435 SlruShared shared = ctl->shared;
436
437 if (shared->lsn_groups_per_page > 0)
438 MemSet(&shared->group_lsn[slotno * shared->lsn_groups_per_page], 0,
439 shared->lsn_groups_per_page * sizeof(XLogRecPtr));
440}

References ctl, fb(), SlruSharedData::group_lsn, SlruSharedData::lsn_groups_per_page, and MemSet.

Referenced by SimpleLruReadPage(), and SimpleLruZeroPage().

◆ SimpleLruZeroPage()

int SimpleLruZeroPage ( SlruCtl  ctl,
int64  pageno 
)

Definition at line 380 of file slru.c.

381{
382 SlruShared shared = ctl->shared;
383 int slotno;
384
386
387 /* Find a suitable buffer slot for the page */
388 slotno = SlruSelectLRUPage(ctl, pageno);
390 (shared->page_status[slotno] == SLRU_PAGE_VALID &&
391 !shared->page_dirty[slotno]) ||
392 shared->page_number[slotno] == pageno);
393
394 /* Mark the slot as containing this page */
395 shared->page_number[slotno] = pageno;
397 shared->page_dirty[slotno] = true;
398 SlruRecentlyUsed(shared, slotno);
399
400 /* Set the buffer to zeroes */
401 MemSet(shared->page_buffer[slotno], 0, BLCKSZ);
402
403 /* Set the LSNs for this new page to zero */
405
406 /*
407 * Assume this page is now the latest active page.
408 *
409 * Note that because both this routine and SlruSelectLRUPage run with a
410 * SLRU bank lock held, it is not possible for this to be zeroing a page
411 * that SlruSelectLRUPage is going to evict simultaneously. Therefore,
412 * there's no memory barrier here.
413 */
414 pg_atomic_write_u64(&shared->latest_page_number, pageno);
415
416 /* update the stats counter of zeroed pages */
418
419 return slotno;
420}

References Assert, ctl, fb(), SlruSharedData::latest_page_number, LW_EXCLUSIVE, LWLockHeldByMeInMode(), MemSet, SlruSharedData::page_buffer, SlruSharedData::page_dirty, SlruSharedData::page_number, SlruSharedData::page_status, pg_atomic_write_u64(), pgstat_count_slru_blocks_zeroed(), SimpleLruGetBankLock(), SimpleLruZeroLSNs(), SLRU_PAGE_EMPTY, SLRU_PAGE_VALID, SlruSharedData::slru_stats_idx, SlruRecentlyUsed(), and SlruSelectLRUPage().

Referenced by asyncQueueAddEntries(), ExtendCLOG(), ExtendCommitTs(), ExtendMultiXactMember(), ExtendMultiXactOffset(), ExtendSUBTRANS(), SerialAdd(), SimpleLruZeroAndWritePage(), StartupSUBTRANS(), test_slru_page_write(), and TrimMultiXact().

◆ SlruCorrectSegmentFilenameLength()

static bool SlruCorrectSegmentFilenameLength ( SlruCtl  ctl,
size_t  len 
)
inlinestatic

Definition at line 1791 of file slru.c.

1792{
1793 if (ctl->long_segment_names)
1794 return (len == 15); /* see SlruFileName() */
1795 else
1796
1797 /*
1798 * Commit 638cf09e76d allowed 5-character lengths. Later commit
1799 * 73c986adde5 allowed 6-character length.
1800 *
1801 * Note: There is an ongoing plan to migrate all SLRUs to 64-bit page
1802 * numbers, and the corresponding 15-character file names, which may
1803 * eventually deprecate the support for 4, 5, and 6-character names.
1804 */
1805 return (len == 4 || len == 5 || len == 6);
1806}

References ctl, and len.

Referenced by SlruScanDirectory().

◆ SlruDeleteSegment()

void SlruDeleteSegment ( SlruCtl  ctl,
int64  segno 
)

Definition at line 1559 of file slru.c.

1560{
1561 SlruShared shared = ctl->shared;
1562 int prevbank = SlotGetBankNumber(0);
1563 bool did_write;
1564
1565 /* Clean out any possibly existing references to the segment. */
1567restart:
1568 did_write = false;
1569 for (int slotno = 0; slotno < shared->num_slots; slotno++)
1570 {
1573
1574 /*
1575 * If the current bank lock is not same as the previous bank lock then
1576 * release the previous lock and acquire the new lock.
1577 */
1578 if (curbank != prevbank)
1579 {
1582 prevbank = curbank;
1583 }
1584
1585 if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
1586 continue;
1587
1589 /* not the segment we're looking for */
1590 if (pagesegno != segno)
1591 continue;
1592
1593 /* If page is clean, just change state to EMPTY (expected case). */
1594 if (shared->page_status[slotno] == SLRU_PAGE_VALID &&
1595 !shared->page_dirty[slotno])
1596 {
1598 continue;
1599 }
1600
1601 /* Same logic as SimpleLruTruncate() */
1602 if (shared->page_status[slotno] == SLRU_PAGE_VALID)
1604 else
1606
1607 did_write = true;
1608 }
1609
1610 /*
1611 * Be extra careful and re-check. The IO functions release the control
1612 * lock, so new pages could have been read in.
1613 */
1614 if (did_write)
1615 goto restart;
1616
1618
1620}

References SlruSharedData::bank_locks, ctl, fb(), LWLockPadded::lock, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), SlruSharedData::num_slots, SlruSharedData::page_dirty, SlruSharedData::page_number, SlruSharedData::page_status, SlruWriteAllData::segno, SimpleLruWaitIO(), SlotGetBankNumber, SLRU_PAGE_EMPTY, SLRU_PAGE_VALID, SLRU_PAGES_PER_SEGMENT, SlruInternalDeleteSegment(), and SlruInternalWritePage().

Referenced by test_slru_page_delete().

◆ SlruFileName()

static int SlruFileName ( SlruCtl  ctl,
char path,
int64  segno 
)
inlinestatic

Definition at line 92 of file slru.c.

93{
94 if (ctl->long_segment_names)
95 {
96 /*
97 * We could use 16 characters here but the disadvantage would be that
98 * the SLRU segments will be hard to distinguish from WAL segments.
99 *
100 * For this reason we use 15 characters. It is enough but also means
101 * that in the future we can't decrease SLRU_PAGES_PER_SEGMENT easily.
102 */
103 Assert(segno >= 0 && segno <= INT64CONST(0xFFFFFFFFFFFFFFF));
104 return snprintf(path, MAXPGPATH, "%s/%015" PRIX64, ctl->Dir, segno);
105 }
106 else
107 {
108 /*
109 * Despite the fact that %04X format string is used up to 24 bit
110 * integers are allowed. See SlruCorrectSegmentFilenameLength()
111 */
112 Assert(segno >= 0 && segno <= INT64CONST(0xFFFFFF));
113 return snprintf(path, MAXPGPATH, "%s/%04X", (ctl)->Dir,
114 (unsigned int) segno);
115 }
116}
#define INT64CONST(x)
Definition c.h:632
#define snprintf
Definition port.h:260

References Assert, ctl, fb(), INT64CONST, MAXPGPATH, and snprintf.

Referenced by SimpleLruDoesPhysicalPageExist(), SlruInternalDeleteSegment(), SlruPhysicalReadPage(), SlruPhysicalWritePage(), SlruReportIOError(), and SlruSyncFileTag().

◆ SlruInternalDeleteSegment()

static void SlruInternalDeleteSegment ( SlruCtl  ctl,
int64  segno 
)
static

Definition at line 1536 of file slru.c.

1537{
1538 char path[MAXPGPATH];
1539
1540 /* Forget any fsync requests queued for this segment. */
1541 if (ctl->sync_handler != SYNC_HANDLER_NONE)
1542 {
1543 FileTag tag;
1544
1545 INIT_SLRUFILETAG(tag, ctl->sync_handler, segno);
1547 }
1548
1549 /* Unlink the file. */
1550 SlruFileName(ctl, path, segno);
1551 ereport(DEBUG2, (errmsg_internal("removing file \"%s\"", path)));
1552 unlink(path);
1553}

References ctl, DEBUG2, ereport, errmsg_internal(), fb(), INIT_SLRUFILETAG, MAXPGPATH, RegisterSyncRequest(), SlruWriteAllData::segno, SlruFileName(), SYNC_FORGET_REQUEST, and SYNC_HANDLER_NONE.

Referenced by SlruDeleteSegment(), SlruScanDirCbDeleteAll(), and SlruScanDirCbDeleteCutoff().

◆ SlruInternalWritePage()

static void SlruInternalWritePage ( SlruCtl  ctl,
int  slotno,
SlruWriteAll  fdata 
)
static

Definition at line 684 of file slru.c.

685{
686 SlruShared shared = ctl->shared;
687 int64 pageno = shared->page_number[slotno];
689 bool ok;
690
693
694 /* If a write is in progress, wait for it to finish */
696 shared->page_number[slotno] == pageno)
697 {
699 }
700
701 /*
702 * Do nothing if page is not dirty, or if buffer no longer contains the
703 * same page we were called for.
704 */
705 if (!shared->page_dirty[slotno] ||
706 shared->page_status[slotno] != SLRU_PAGE_VALID ||
707 shared->page_number[slotno] != pageno)
708 return;
709
710 /*
711 * Mark the slot write-busy, and clear the dirtybit. After this point, a
712 * transaction status update on this page will mark it dirty again.
713 */
715 shared->page_dirty[slotno] = false;
716
717 /* Acquire per-buffer lock (cannot deadlock, see notes at top) */
719
720 /* Release bank lock while doing I/O */
722
723 /* Do the write */
725
726 /* If we failed, and we're in a flush, better close the files */
727 if (!ok && fdata)
728 {
729 for (int i = 0; i < fdata->num_files; i++)
731 }
732
733 /* Re-acquire bank lock and update page state */
735
736 Assert(shared->page_number[slotno] == pageno &&
738
739 /* If we failed to write, mark the page dirty again */
740 if (!ok)
741 shared->page_dirty[slotno] = true;
742
744
746
747 /* Now it's okay to ereport if we failed */
748 if (!ok)
749 SlruReportIOError(ctl, pageno, NULL);
750
751 /* If part of a checkpoint, count this as a SLRU buffer written. */
752 if (fdata)
753 {
756 }
757}

References Assert, SlruSharedData::bank_locks, SlruSharedData::buffer_locks, CheckpointStats, CheckpointStatsData::ckpt_slru_written, CloseTransientFile(), ctl, fb(), i, LWLockPadded::lock, LW_EXCLUSIVE, LWLockAcquire(), LWLockHeldByMeInMode(), LWLockRelease(), SlruSharedData::page_dirty, SlruSharedData::page_number, SlruSharedData::page_status, PendingCheckpointerStats, SimpleLruGetBankLock(), SimpleLruWaitIO(), SlotGetBankNumber, SLRU_PAGE_EMPTY, SLRU_PAGE_VALID, SLRU_PAGE_WRITE_IN_PROGRESS, PgStat_CheckpointerStats::slru_written, SlruPhysicalWritePage(), and SlruReportIOError().

Referenced by SimpleLruTruncate(), SimpleLruWriteAll(), SimpleLruWritePage(), SlruDeleteSegment(), and SlruSelectLRUPage().

◆ SlruMayDeleteSegment()

static bool SlruMayDeleteSegment ( SlruCtl  ctl,
int64  segpage,
int64  cutoffPage 
)
static

Definition at line 1636 of file slru.c.

1637{
1639
1641
1642 return (ctl->PagePrecedes(segpage, cutoffPage) &&
1643 ctl->PagePrecedes(seg_last_page, cutoffPage));
1644}

References Assert, ctl, fb(), and SLRU_PAGES_PER_SEGMENT.

Referenced by SlruScanDirCbDeleteCutoff(), and SlruScanDirCbReportPresence().

◆ SlruPhysicalReadPage()

static bool SlruPhysicalReadPage ( SlruCtl  ctl,
int64  pageno,
int  slotno 
)
static

Definition at line 836 of file slru.c.

837{
838 SlruShared shared = ctl->shared;
839 int64 segno = pageno / SLRU_PAGES_PER_SEGMENT;
840 int rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
841 off_t offset = rpageno * BLCKSZ;
842 char path[MAXPGPATH];
843 int fd;
844
845 SlruFileName(ctl, path, segno);
846
847 /*
848 * In a crash-and-restart situation, it's possible for us to receive
849 * commands to set the commit status of transactions whose bits are in
850 * already-truncated segments of the commit log (see notes in
851 * SlruPhysicalWritePage). Hence, if we are InRecovery, allow the case
852 * where the file doesn't exist, and return zeroes instead.
853 */
855 if (fd < 0)
856 {
857 if (errno != ENOENT || !InRecovery)
858 {
861 return false;
862 }
863
864 ereport(LOG,
865 (errmsg("file \"%s\" doesn't exist, reading as zeroes",
866 path)));
867 MemSet(shared->page_buffer[slotno], 0, BLCKSZ);
868 return true;
869 }
870
871 errno = 0;
873 if (pg_pread(fd, shared->page_buffer[slotno], BLCKSZ, offset) != BLCKSZ)
874 {
879 return false;
880 }
882
883 if (CloseTransientFile(fd) != 0)
884 {
887 return false;
888 }
889
890 return true;
891}

References CloseTransientFile(), ctl, ereport, errmsg, fb(), fd(), InRecovery, LOG, MAXPGPATH, MemSet, OpenTransientFile(), SlruSharedData::page_buffer, PG_BINARY, pg_pread, pgstat_report_wait_end(), pgstat_report_wait_start(), SlruWriteAllData::segno, SLRU_CLOSE_FAILED, slru_errcause, slru_errno, SLRU_OPEN_FAILED, SLRU_PAGES_PER_SEGMENT, SLRU_READ_FAILED, and SlruFileName().

Referenced by SimpleLruReadPage().

◆ SlruPhysicalWritePage()

static bool SlruPhysicalWritePage ( SlruCtl  ctl,
int64  pageno,
int  slotno,
SlruWriteAll  fdata 
)
static

Definition at line 908 of file slru.c.

909{
910 SlruShared shared = ctl->shared;
911 int64 segno = pageno / SLRU_PAGES_PER_SEGMENT;
912 int rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
913 off_t offset = rpageno * BLCKSZ;
914 char path[MAXPGPATH];
915 int fd = -1;
916
917 /* update the stats counter of written pages */
919
920 /*
921 * Honor the write-WAL-before-data rule, if appropriate, so that we do not
922 * write out data before associated WAL records. This is the same action
923 * performed during FlushBuffer() in the main buffer manager.
924 */
925 if (shared->group_lsn != NULL)
926 {
927 /*
928 * We must determine the largest async-commit LSN for the page. This
929 * is a bit tedious, but since this entire function is a slow path
930 * anyway, it seems better to do this here than to maintain a per-page
931 * LSN variable (which'd need an extra comparison in the
932 * transaction-commit path).
933 */
935 int lsnindex;
936
938 max_lsn = shared->group_lsn[lsnindex++];
939 for (int lsnoff = 1; lsnoff < shared->lsn_groups_per_page; lsnoff++)
940 {
942
943 if (max_lsn < this_lsn)
945 }
946
948 {
949 /*
950 * As noted above, elog(ERROR) is not acceptable here, so if
951 * XLogFlush were to fail, we must PANIC. This isn't much of a
952 * restriction because XLogFlush is just about all critical
953 * section anyway, but let's make sure.
954 */
958 }
959 }
960
961 /*
962 * During a SimpleLruWriteAll, we may already have the desired file open.
963 */
964 if (fdata)
965 {
966 for (int i = 0; i < fdata->num_files; i++)
967 {
968 if (fdata->segno[i] == segno)
969 {
970 fd = fdata->fd[i];
971 break;
972 }
973 }
974 }
975
976 if (fd < 0)
977 {
978 /*
979 * If the file doesn't already exist, we should create it. It is
980 * possible for this to need to happen when writing a page that's not
981 * first in its segment; we assume the OS can cope with that. (Note:
982 * it might seem that it'd be okay to create files only when
983 * SimpleLruZeroPage is called for the first page of a segment.
984 * However, if after a crash and restart the REDO logic elects to
985 * replay the log from a checkpoint before the latest one, then it's
986 * possible that we will get commands to set transaction status of
987 * transactions that have already been truncated from the commit log.
988 * Easiest way to deal with that is to accept references to
989 * nonexistent files here and in SlruPhysicalReadPage.)
990 *
991 * Note: it is possible for more than one backend to be executing this
992 * code simultaneously for different pages of the same file. Hence,
993 * don't use O_EXCL or O_TRUNC or anything like that.
994 */
995 SlruFileName(ctl, path, segno);
997 if (fd < 0)
998 {
1000 slru_errno = errno;
1001 return false;
1002 }
1003
1004 if (fdata)
1005 {
1006 if (fdata->num_files < MAX_WRITEALL_BUFFERS)
1007 {
1008 fdata->fd[fdata->num_files] = fd;
1009 fdata->segno[fdata->num_files] = segno;
1010 fdata->num_files++;
1011 }
1012 else
1013 {
1014 /*
1015 * In the unlikely event that we exceed MAX_WRITEALL_BUFFERS,
1016 * fall back to treating it as a standalone write.
1017 */
1018 fdata = NULL;
1019 }
1020 }
1021 }
1022
1023 errno = 0;
1025 if (pg_pwrite(fd, shared->page_buffer[slotno], BLCKSZ, offset) != BLCKSZ)
1026 {
1028 /* if write didn't set errno, assume problem is no disk space */
1029 if (errno == 0)
1030 errno = ENOSPC;
1032 slru_errno = errno;
1033 if (!fdata)
1035 return false;
1036 }
1038
1039 /* Queue up a sync request for the checkpointer. */
1040 if (ctl->sync_handler != SYNC_HANDLER_NONE)
1041 {
1042 FileTag tag;
1043
1044 INIT_SLRUFILETAG(tag, ctl->sync_handler, segno);
1045 if (!RegisterSyncRequest(&tag, SYNC_REQUEST, false))
1046 {
1047 /* No space to enqueue sync request. Do it synchronously. */
1049 if (pg_fsync(fd) != 0)
1050 {
1053 slru_errno = errno;
1055 return false;
1056 }
1058 }
1059 }
1060
1061 /* Close file, unless part of flush request. */
1062 if (!fdata)
1063 {
1064 if (CloseTransientFile(fd) != 0)
1065 {
1067 slru_errno = errno;
1068 return false;
1069 }
1070 }
1071
1072 return true;
1073}

References CloseTransientFile(), ctl, END_CRIT_SECTION, fb(), fd(), SlruSharedData::group_lsn, i, INIT_SLRUFILETAG, SlruSharedData::lsn_groups_per_page, MAX_WRITEALL_BUFFERS, MAXPGPATH, OpenTransientFile(), SlruSharedData::page_buffer, PG_BINARY, pg_fsync(), pg_pwrite, pgstat_count_slru_blocks_written(), pgstat_report_wait_end(), pgstat_report_wait_start(), RegisterSyncRequest(), SlruWriteAllData::segno, SLRU_CLOSE_FAILED, slru_errcause, slru_errno, SLRU_FSYNC_FAILED, SLRU_OPEN_FAILED, SLRU_PAGES_PER_SEGMENT, SlruSharedData::slru_stats_idx, SLRU_WRITE_FAILED, SlruFileName(), START_CRIT_SECTION, SYNC_HANDLER_NONE, SYNC_REQUEST, XLogFlush(), and XLogRecPtrIsValid.

Referenced by SlruInternalWritePage().

◆ SlruRecentlyUsed()

static void SlruRecentlyUsed ( SlruShared  shared,
int  slotno 
)
inlinestatic

Definition at line 1156 of file slru.c.

1157{
1160
1162
1163 /*
1164 * The reason for the if-test is that there are often many consecutive
1165 * accesses to the same page (particularly the latest page). By
1166 * suppressing useless increments of bank_cur_lru_count, we reduce the
1167 * probability that old pages' counts will "wrap around" and make them
1168 * appear recently used.
1169 *
1170 * We allow this code to be executed concurrently by multiple processes
1171 * within SimpleLruReadPage_ReadOnly(). As long as int reads and writes
1172 * are atomic, this should not cause any completely-bogus values to enter
1173 * the computation. However, it is possible for either bank_cur_lru_count
1174 * or individual page_lru_count entries to be "reset" to lower values than
1175 * they should have, in case a process is delayed while it executes this
1176 * function. With care in SlruSelectLRUPage(), this does little harm, and
1177 * in any case the absolute worst possible consequence is a nonoptimal
1178 * choice of page to evict. The gain from allowing concurrent reads of
1179 * SLRU pages seems worth it.
1180 */
1181 if (new_lru_count != shared->page_lru_count[slotno])
1182 {
1185 }
1186}

References Assert, SlruSharedData::bank_cur_lru_count, fb(), SlruSharedData::page_lru_count, SlruSharedData::page_status, SlotGetBankNumber, and SLRU_PAGE_EMPTY.

Referenced by SimpleLruReadPage(), SimpleLruReadPage_ReadOnly(), and SimpleLruZeroPage().

◆ SlruReportIOError()

static void SlruReportIOError ( SlruCtl  ctl,
int64  pageno,
const void opaque_data 
)
static

Definition at line 1080 of file slru.c.

1081{
1082 int64 segno = pageno / SLRU_PAGES_PER_SEGMENT;
1083 int rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
1084 int offset = rpageno * BLCKSZ;
1085 char path[MAXPGPATH];
1086
1087 SlruFileName(ctl, path, segno);
1088 errno = slru_errno;
1089 switch (slru_errcause)
1090 {
1091 case SLRU_OPEN_FAILED:
1092 ereport(ERROR,
1094 errmsg("could not open file \"%s\": %m", path),
1095 opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
1096 break;
1097 case SLRU_SEEK_FAILED:
1098 ereport(ERROR,
1100 errmsg("could not seek in file \"%s\" to offset %d: %m",
1101 path, offset),
1102 opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
1103 break;
1104 case SLRU_READ_FAILED:
1105 if (errno)
1106 ereport(ERROR,
1108 errmsg("could not read from file \"%s\" at offset %d: %m",
1109 path, offset),
1110 opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
1111 else
1112 ereport(ERROR,
1113 (errmsg("could not read from file \"%s\" at offset %d: read too few bytes",
1114 path, offset),
1115 opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
1116 break;
1117 case SLRU_WRITE_FAILED:
1118 if (errno)
1119 ereport(ERROR,
1121 errmsg("Could not write to file \"%s\" at offset %d: %m",
1122 path, offset),
1123 opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
1124 else
1125 ereport(ERROR,
1126 (errmsg("Could not write to file \"%s\" at offset %d: wrote too few bytes.",
1127 path, offset),
1128 opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
1129 break;
1130 case SLRU_FSYNC_FAILED:
1133 errmsg("could not fsync file \"%s\": %m",
1134 path),
1135 opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
1136 break;
1137 case SLRU_CLOSE_FAILED:
1138 ereport(ERROR,
1140 errmsg("could not close file \"%s\": %m",
1141 path),
1142 opaque_data ? ctl->errdetail_for_io_error(opaque_data) : 0));
1143 break;
1144 default:
1145 /* can't get here, we trust */
1146 elog(ERROR, "unrecognized SimpleLru error cause: %d",
1147 (int) slru_errcause);
1148 break;
1149 }
1150}

References ctl, data_sync_elevel(), elog, ereport, errcode_for_file_access(), errmsg, ERROR, fb(), MAXPGPATH, SlruWriteAllData::segno, SLRU_CLOSE_FAILED, slru_errcause, slru_errno, SLRU_FSYNC_FAILED, SLRU_OPEN_FAILED, SLRU_PAGES_PER_SEGMENT, SLRU_READ_FAILED, SLRU_SEEK_FAILED, SLRU_WRITE_FAILED, and SlruFileName().

Referenced by SimpleLruDoesPhysicalPageExist(), SimpleLruReadPage(), SimpleLruWriteAll(), and SlruInternalWritePage().

◆ SlruScanDirCbDeleteAll()

bool SlruScanDirCbDeleteAll ( SlruCtl  ctl,
char filename,
int64  segpage,
void data 
)

Definition at line 1777 of file slru.c.

1778{
1780
1781 return false; /* keep going */
1782}

References ctl, fb(), SLRU_PAGES_PER_SEGMENT, and SlruInternalDeleteSegment().

Referenced by AsyncShmemInit(), DeactivateCommitTs(), and test_slru_scan_cb().

◆ SlruScanDirCbDeleteCutoff()

static bool SlruScanDirCbDeleteCutoff ( SlruCtl  ctl,
char filename,
int64  segpage,
void data 
)
static

Definition at line 1761 of file slru.c.

1763{
1764 int64 cutoffPage = *(int64 *) data;
1765
1768
1769 return false; /* keep going */
1770}

References ctl, data, fb(), SLRU_PAGES_PER_SEGMENT, SlruInternalDeleteSegment(), and SlruMayDeleteSegment().

Referenced by SimpleLruTruncate().

◆ SlruScanDirCbReportPresence()

bool SlruScanDirCbReportPresence ( SlruCtl  ctl,
char filename,
int64  segpage,
void data 
)

Definition at line 1745 of file slru.c.

1747{
1748 int64 cutoffPage = *(int64 *) data;
1749
1751 return true; /* found one; don't iterate any more */
1752
1753 return false; /* keep going */
1754}

References ctl, data, fb(), and SlruMayDeleteSegment().

Referenced by TruncateCLOG(), and TruncateCommitTs().

◆ SlruScanDirectory()

bool SlruScanDirectory ( SlruCtl  ctl,
SlruScanCallback  callback,
void data 
)

Definition at line 1824 of file slru.c.

1825{
1826 bool retval = false;
1827 DIR *cldir;
1828 struct dirent *clde;
1829 int64 segno;
1830 int64 segpage;
1831
1832 cldir = AllocateDir(ctl->Dir);
1833 while ((clde = ReadDir(cldir, ctl->Dir)) != NULL)
1834 {
1835 size_t len;
1836
1837 len = strlen(clde->d_name);
1838
1840 strspn(clde->d_name, "0123456789ABCDEF") == len)
1841 {
1842 segno = strtoi64(clde->d_name, NULL, 16);
1844
1845 elog(DEBUG2, "SlruScanDirectory invoking callback on %s/%s",
1846 ctl->Dir, clde->d_name);
1847 retval = callback(ctl, clde->d_name, segpage, data);
1848 if (retval)
1849 break;
1850 }
1851 }
1852 FreeDir(cldir);
1853
1854 return retval;
1855}

References AllocateDir(), callback(), ctl, data, DEBUG2, elog, fb(), FreeDir(), len, ReadDir(), SLRU_PAGES_PER_SEGMENT, and SlruCorrectSegmentFilenameLength().

Referenced by AsyncShmemInit(), DeactivateCommitTs(), SimpleLruTruncate(), test_slru_delete_all(), TruncateCLOG(), and TruncateCommitTs().

◆ SlruSelectLRUPage()

static int SlruSelectLRUPage ( SlruCtl  ctl,
int64  pageno 
)
static

Definition at line 1202 of file slru.c.

1203{
1204 SlruShared shared = ctl->shared;
1205
1206 /* Outer loop handles restart after I/O */
1207 for (;;)
1208 {
1209 int cur_count;
1210 int bestvalidslot = 0; /* keep compiler quiet */
1211 int best_valid_delta = -1;
1212 int64 best_valid_page_number = 0; /* keep compiler quiet */
1213 int bestinvalidslot = 0; /* keep compiler quiet */
1214 int best_invalid_delta = -1;
1215 int64 best_invalid_page_number = 0; /* keep compiler quiet */
1216 int bankno = pageno % ctl->nbanks;
1219
1221
1222 /* See if page already has a buffer assigned */
1223 for (int slotno = bankstart; slotno < bankend; slotno++)
1224 {
1225 if (shared->page_status[slotno] != SLRU_PAGE_EMPTY &&
1226 shared->page_number[slotno] == pageno)
1227 return slotno;
1228 }
1229
1230 /*
1231 * If we find any EMPTY slot, just select that one. Else choose a
1232 * victim page to replace. We normally take the least recently used
1233 * valid page, but we will never take the slot containing
1234 * latest_page_number, even if it appears least recently used. We
1235 * will select a slot that is already I/O busy only if there is no
1236 * other choice: a read-busy slot will not be least recently used once
1237 * the read finishes, and waiting for an I/O on a write-busy slot is
1238 * inferior to just picking some other slot. Testing shows the slot
1239 * we pick instead will often be clean, allowing us to begin a read at
1240 * once.
1241 *
1242 * Normally the page_lru_count values will all be different and so
1243 * there will be a well-defined LRU page. But since we allow
1244 * concurrent execution of SlruRecentlyUsed() within
1245 * SimpleLruReadPage_ReadOnly(), it is possible that multiple pages
1246 * acquire the same lru_count values. In that case we break ties by
1247 * choosing the furthest-back page.
1248 *
1249 * Notice that this next line forcibly advances cur_lru_count to a
1250 * value that is certainly beyond any value that will be in the
1251 * page_lru_count array after the loop finishes. This ensures that
1252 * the next execution of SlruRecentlyUsed will mark the page newly
1253 * used, even if it's for a page that has the current counter value.
1254 * That gets us back on the path to having good data when there are
1255 * multiple pages with the same lru_count.
1256 */
1257 cur_count = (shared->bank_cur_lru_count[bankno])++;
1258 for (int slotno = bankstart; slotno < bankend; slotno++)
1259 {
1260 int this_delta;
1262
1263 if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
1264 return slotno;
1265
1267 if (this_delta < 0)
1268 {
1269 /*
1270 * Clean up in case shared updates have caused cur_count
1271 * increments to get "lost". We back off the page counts,
1272 * rather than trying to increase cur_count, to avoid any
1273 * question of infinite loops or failure in the presence of
1274 * wrapped-around counts.
1275 */
1276 shared->page_lru_count[slotno] = cur_count;
1277 this_delta = 0;
1278 }
1279
1280 /*
1281 * If this page is the one most recently zeroed, don't consider it
1282 * an eviction candidate. See comments in SimpleLruZeroPage for an
1283 * explanation about the lack of a memory barrier here.
1284 */
1286 if (this_page_number ==
1288 continue;
1289
1290 if (shared->page_status[slotno] == SLRU_PAGE_VALID)
1291 {
1294 ctl->PagePrecedes(this_page_number,
1296 {
1300 }
1301 }
1302 else
1303 {
1306 ctl->PagePrecedes(this_page_number,
1308 {
1312 }
1313 }
1314 }
1315
1316 /*
1317 * If all pages (except possibly the latest one) are I/O busy, we'll
1318 * have to wait for an I/O to complete and then retry. In that
1319 * unhappy case, we choose to wait for the I/O on the least recently
1320 * used slot, on the assumption that it was likely initiated first of
1321 * all the I/Os in progress and may therefore finish first.
1322 */
1323 if (best_valid_delta < 0)
1324 {
1326 continue;
1327 }
1328
1329 /*
1330 * If the selected page is clean, we're set.
1331 */
1332 if (!shared->page_dirty[bestvalidslot])
1333 return bestvalidslot;
1334
1335 /*
1336 * Write the page.
1337 */
1339
1340 /*
1341 * Now loop back and try again. This is the easiest way of dealing
1342 * with corner cases such as the victim page being re-dirtied while we
1343 * wrote it.
1344 */
1345 }
1346}

References Assert, SlruSharedData::bank_cur_lru_count, ctl, fb(), SlruSharedData::latest_page_number, LWLockHeldByMe(), SlruSharedData::page_dirty, SlruSharedData::page_lru_count, SlruSharedData::page_number, SlruSharedData::page_status, pg_atomic_read_u64(), SimpleLruGetBankLock(), SimpleLruWaitIO(), SLRU_BANK_SIZE, SLRU_PAGE_EMPTY, SLRU_PAGE_VALID, and SlruInternalWritePage().

Referenced by SimpleLruReadPage(), and SimpleLruZeroPage().

◆ SlruSyncFileTag()

int SlruSyncFileTag ( SlruCtl  ctl,
const FileTag ftag,
char path 
)

Definition at line 1864 of file slru.c.

1865{
1866 int fd;
1867 int save_errno;
1868 int result;
1869
1870 SlruFileName(ctl, path, ftag->segno);
1871
1873 if (fd < 0)
1874 return -1;
1875
1877 result = pg_fsync(fd);
1879 save_errno = errno;
1880
1882
1883 errno = save_errno;
1884 return result;
1885}

References CloseTransientFile(), ctl, fb(), fd(), OpenTransientFile(), PG_BINARY, pg_fsync(), pgstat_report_wait_end(), pgstat_report_wait_start(), FileTag::segno, and SlruFileName().

Referenced by clogsyncfiletag(), committssyncfiletag(), multixactmemberssyncfiletag(), multixactoffsetssyncfiletag(), and test_slru_page_sync().

Variable Documentation

◆ slru_errcause

◆ slru_errno