PostgreSQL Source Code  git master
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 "pgstat.h"
#include "storage/fd.h"
#include "storage/shmem.h"
#include "miscadmin.h"
Include dependency graph for slru.c:

Go to the source code of this file.

Data Structures

struct  SlruFlushData
 

Macros

#define SlruFileName(ctl, path, seg)   snprintf(path, MAXPGPATH, "%s/%04X", (ctl)->Dir, seg)
 
#define MAX_FLUSH_BUFFERS   16
 
#define SlruRecentlyUsed(shared, slotno)
 

Typedefs

typedef struct SlruFlushData SlruFlushData
 
typedef struct SlruFlushDataSlruFlush
 

Enumerations

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

Functions

static void SimpleLruZeroLSNs (SlruCtl ctl, int slotno)
 
static void SimpleLruWaitIO (SlruCtl ctl, int slotno)
 
static void SlruInternalWritePage (SlruCtl ctl, int slotno, SlruFlush fdata)
 
static bool SlruPhysicalReadPage (SlruCtl ctl, int pageno, int slotno)
 
static bool SlruPhysicalWritePage (SlruCtl ctl, int pageno, int slotno, SlruFlush fdata)
 
static void SlruReportIOError (SlruCtl ctl, int pageno, TransactionId xid)
 
static int SlruSelectLRUPage (SlruCtl ctl, int pageno)
 
static bool SlruScanDirCbDeleteCutoff (SlruCtl ctl, char *filename, int segpage, void *data)
 
static void SlruInternalDeleteSegment (SlruCtl ctl, char *filename)
 
Size SimpleLruShmemSize (int nslots, int nlsns)
 
void SimpleLruInit (SlruCtl ctl, const char *name, int nslots, int nlsns, LWLock *ctllock, const char *subdir, int tranche_id)
 
int SimpleLruZeroPage (SlruCtl ctl, int pageno)
 
int SimpleLruReadPage (SlruCtl ctl, int pageno, bool write_ok, TransactionId xid)
 
int SimpleLruReadPage_ReadOnly (SlruCtl ctl, int pageno, TransactionId xid)
 
void SimpleLruWritePage (SlruCtl ctl, int slotno)
 
bool SimpleLruDoesPhysicalPageExist (SlruCtl ctl, int pageno)
 
void SimpleLruFlush (SlruCtl ctl, bool allow_redirtied)
 
void SimpleLruTruncate (SlruCtl ctl, int cutoffPage)
 
void SlruDeleteSegment (SlruCtl ctl, int segno)
 
bool SlruScanDirCbReportPresence (SlruCtl ctl, char *filename, int segpage, void *data)
 
bool SlruScanDirCbDeleteAll (SlruCtl ctl, char *filename, int segpage, void *data)
 
bool SlruScanDirectory (SlruCtl ctl, SlruScanCallback callback, void *data)
 

Variables

static SlruErrorCause slru_errcause
 
static int slru_errno
 

Macro Definition Documentation

◆ MAX_FLUSH_BUFFERS

#define MAX_FLUSH_BUFFERS   16

Definition at line 73 of file slru.c.

Referenced by SlruPhysicalWritePage().

◆ SlruFileName

#define SlruFileName (   ctl,
  path,
  seg 
)    snprintf(path, MAXPGPATH, "%s/%04X", (ctl)->Dir, seg)

◆ SlruRecentlyUsed

#define SlruRecentlyUsed (   shared,
  slotno 
)
Value:
do { \
int new_lru_count = (shared)->cur_lru_count; \
if (new_lru_count != (shared)->page_lru_count[slotno]) { \
(shared)->cur_lru_count = ++new_lru_count; \
(shared)->page_lru_count[slotno] = new_lru_count; \
} \
} while (0)

Definition at line 103 of file slru.c.

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

Typedef Documentation

◆ SlruFlush

Definition at line 82 of file slru.c.

◆ SlruFlushData

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 113 of file slru.c.

Function Documentation

◆ SimpleLruDoesPhysicalPageExist()

bool SimpleLruDoesPhysicalPageExist ( SlruCtl  ctl,
int  pageno 
)

Definition at line 590 of file slru.c.

References CloseTransientFile(), endpos, SlruFlushData::fd, MAXPGPATH, OpenTransientFile(), PG_BINARY, SlruFlushData::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 MaybeExtendOffsetSlru().

591 {
592  int segno = pageno / SLRU_PAGES_PER_SEGMENT;
593  int rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
594  int offset = rpageno * BLCKSZ;
595  char path[MAXPGPATH];
596  int fd;
597  bool result;
598  off_t endpos;
599 
600  SlruFileName(ctl, path, segno);
601 
602  fd = OpenTransientFile(path, O_RDONLY | PG_BINARY);
603  if (fd < 0)
604  {
605  /* expected: file doesn't exist */
606  if (errno == ENOENT)
607  return false;
608 
609  /* report error normally */
611  slru_errno = errno;
612  SlruReportIOError(ctl, pageno, 0);
613  }
614 
615  if ((endpos = lseek(fd, 0, SEEK_END)) < 0)
616  {
618  slru_errno = errno;
619  SlruReportIOError(ctl, pageno, 0);
620  }
621 
622  result = endpos >= (off_t) (offset + BLCKSZ);
623 
624  if (CloseTransientFile(fd) != 0)
625  {
627  slru_errno = errno;
628  return false;
629  }
630 
631  return result;
632 }
static SlruErrorCause slru_errcause
Definition: slru.c:123
static int fd(const char *x, int i)
Definition: preproc-init.c:105
static void SlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid)
Definition: slru.c:898
#define PG_BINARY
Definition: c.h:1191
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2257
#define MAXPGPATH
static XLogRecPtr endpos
Definition: pg_receivewal.c:48
int CloseTransientFile(int fd)
Definition: fd.c:2434
#define SlruFileName(ctl, path, seg)
Definition: slru.c:63
static int slru_errno
Definition: slru.c:124
#define SLRU_PAGES_PER_SEGMENT
Definition: slru.h:33

◆ SimpleLruFlush()

void SimpleLruFlush ( SlruCtl  ctl,
bool  allow_redirtied 
)

Definition at line 1109 of file slru.c.

References Assert, CloseTransientFile(), SlruSharedData::ControlLock, SlruCtlData::do_fsync, SlruFlushData::fd, i, InvalidTransactionId, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), SlruFlushData::num_files, SlruSharedData::num_slots, SlruSharedData::page_dirty, SlruSharedData::page_status, pg_fsync(), pgstat_report_wait_end(), pgstat_report_wait_start(), SlruFlushData::segno, SlruCtlData::shared, SLRU_CLOSE_FAILED, slru_errcause, slru_errno, SLRU_FSYNC_FAILED, SLRU_PAGE_EMPTY, SLRU_PAGE_VALID, SLRU_PAGES_PER_SEGMENT, SlruInternalWritePage(), SlruReportIOError(), and WAIT_EVENT_SLRU_FLUSH_SYNC.

Referenced by CheckPointCLOG(), CheckPointCommitTs(), CheckPointMultiXact(), CheckPointPredicate(), CheckPointSUBTRANS(), find_multixact_start(), ShutdownCLOG(), ShutdownCommitTs(), ShutdownMultiXact(), and ShutdownSUBTRANS().

1110 {
1111  SlruShared shared = ctl->shared;
1112  SlruFlushData fdata;
1113  int slotno;
1114  int pageno = 0;
1115  int i;
1116  bool ok;
1117 
1118  /*
1119  * Find and write dirty pages
1120  */
1121  fdata.num_files = 0;
1122 
1124 
1125  for (slotno = 0; slotno < shared->num_slots; slotno++)
1126  {
1127  SlruInternalWritePage(ctl, slotno, &fdata);
1128 
1129  /*
1130  * In some places (e.g. checkpoints), we cannot assert that the slot
1131  * is clean now, since another process might have re-dirtied it
1132  * already. That's okay.
1133  */
1134  Assert(allow_redirtied ||
1135  shared->page_status[slotno] == SLRU_PAGE_EMPTY ||
1136  (shared->page_status[slotno] == SLRU_PAGE_VALID &&
1137  !shared->page_dirty[slotno]));
1138  }
1139 
1140  LWLockRelease(shared->ControlLock);
1141 
1142  /*
1143  * Now fsync and close any files that were open
1144  */
1145  ok = true;
1146  for (i = 0; i < fdata.num_files; i++)
1147  {
1149  if (ctl->do_fsync && pg_fsync(fdata.fd[i]) != 0)
1150  {
1152  slru_errno = errno;
1153  pageno = fdata.segno[i] * SLRU_PAGES_PER_SEGMENT;
1154  ok = false;
1155  }
1157 
1158  if (CloseTransientFile(fdata.fd[i]) != 0)
1159  {
1161  slru_errno = errno;
1162  pageno = fdata.segno[i] * SLRU_PAGES_PER_SEGMENT;
1163  ok = false;
1164  }
1165  }
1166  if (!ok)
1168 }
LWLock * ControlLock
Definition: slru.h:57
static void SlruInternalWritePage(SlruCtl ctl, int slotno, SlruFlush fdata)
Definition: slru.c:507
static SlruErrorCause slru_errcause
Definition: slru.c:123
int segno[MAX_FLUSH_BUFFERS]
Definition: slru.c:79
static void SlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid)
Definition: slru.c:898
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1726
SlruPageStatus * page_status
Definition: slru.h:67
#define InvalidTransactionId
Definition: transam.h:31
static void pgstat_report_wait_end(void)
Definition: pgstat.h:1342
bool do_fsync
Definition: slru.h:121
int CloseTransientFile(int fd)
Definition: fd.c:2434
int num_files
Definition: slru.c:77
#define Assert(condition)
Definition: c.h:732
static void pgstat_report_wait_start(uint32 wait_event_info)
Definition: pgstat.h:1318
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1122
int num_slots
Definition: slru.h:60
static int slru_errno
Definition: slru.c:124
bool * page_dirty
Definition: slru.h:68
int i
SlruShared shared
Definition: slru.h:115
int pg_fsync(int fd)
Definition: fd.c:333
#define SLRU_PAGES_PER_SEGMENT
Definition: slru.h:33
int fd[MAX_FLUSH_BUFFERS]
Definition: slru.c:78

◆ SimpleLruInit()

void SimpleLruInit ( SlruCtl  ctl,
const char *  name,
int  nslots,
int  nlsns,
LWLock ctllock,
const char *  subdir,
int  tranche_id 
)

Definition at line 165 of file slru.c.

References Assert, SlruSharedData::buffer_locks, BUFFERALIGN, SlruSharedData::ControlLock, SlruSharedData::cur_lru_count, SlruCtlData::Dir, SlruCtlData::do_fsync, SlruSharedData::group_lsn, IsUnderPostmaster, LWLockPadded::lock, SlruSharedData::lsn_groups_per_page, SlruSharedData::lwlock_tranche_id, SlruSharedData::lwlock_tranche_name, LWLockInitialize(), LWLockRegisterTranche(), MAXALIGN, SlruSharedData::num_slots, SlruSharedData::page_buffer, SlruSharedData::page_dirty, SlruSharedData::page_lru_count, SlruSharedData::page_number, SlruSharedData::page_status, SlruCtlData::shared, ShmemInitStruct(), SimpleLruShmemSize(), SLRU_MAX_NAME_LENGTH, SLRU_PAGE_EMPTY, strlcpy(), and StrNCpy.

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

167 {
168  SlruShared shared;
169  bool found;
170 
171  shared = (SlruShared) ShmemInitStruct(name,
172  SimpleLruShmemSize(nslots, nlsns),
173  &found);
174 
175  if (!IsUnderPostmaster)
176  {
177  /* Initialize locks and shared memory area */
178  char *ptr;
179  Size offset;
180  int slotno;
181 
182  Assert(!found);
183 
184  memset(shared, 0, sizeof(SlruSharedData));
185 
186  shared->ControlLock = ctllock;
187 
188  shared->num_slots = nslots;
189  shared->lsn_groups_per_page = nlsns;
190 
191  shared->cur_lru_count = 0;
192 
193  /* shared->latest_page_number will be set later */
194 
195  ptr = (char *) shared;
196  offset = MAXALIGN(sizeof(SlruSharedData));
197  shared->page_buffer = (char **) (ptr + offset);
198  offset += MAXALIGN(nslots * sizeof(char *));
199  shared->page_status = (SlruPageStatus *) (ptr + offset);
200  offset += MAXALIGN(nslots * sizeof(SlruPageStatus));
201  shared->page_dirty = (bool *) (ptr + offset);
202  offset += MAXALIGN(nslots * sizeof(bool));
203  shared->page_number = (int *) (ptr + offset);
204  offset += MAXALIGN(nslots * sizeof(int));
205  shared->page_lru_count = (int *) (ptr + offset);
206  offset += MAXALIGN(nslots * sizeof(int));
207 
208  /* Initialize LWLocks */
209  shared->buffer_locks = (LWLockPadded *) (ptr + offset);
210  offset += MAXALIGN(nslots * sizeof(LWLockPadded));
211 
212  if (nlsns > 0)
213  {
214  shared->group_lsn = (XLogRecPtr *) (ptr + offset);
215  offset += MAXALIGN(nslots * nlsns * sizeof(XLogRecPtr));
216  }
217 
218  Assert(strlen(name) + 1 < SLRU_MAX_NAME_LENGTH);
220  shared->lwlock_tranche_id = tranche_id;
221 
222  ptr += BUFFERALIGN(offset);
223  for (slotno = 0; slotno < nslots; slotno++)
224  {
225  LWLockInitialize(&shared->buffer_locks[slotno].lock,
226  shared->lwlock_tranche_id);
227 
228  shared->page_buffer[slotno] = ptr;
229  shared->page_status[slotno] = SLRU_PAGE_EMPTY;
230  shared->page_dirty[slotno] = false;
231  shared->page_lru_count[slotno] = 0;
232  ptr += BLCKSZ;
233  }
234 
235  /* Should fit to estimated shmem size */
236  Assert(ptr - (char *) shared <= SimpleLruShmemSize(nslots, nlsns));
237  }
238  else
239  Assert(found);
240 
241  /* Register SLRU tranche in the main tranches array */
243  shared->lwlock_tranche_name);
244 
245  /*
246  * Initialize the unshared control struct, including directory path. We
247  * assume caller set PagePrecedes.
248  */
249  ctl->shared = shared;
250  ctl->do_fsync = true; /* default behavior */
251  StrNCpy(ctl->Dir, subdir, sizeof(ctl->Dir));
252 }
LWLock * ControlLock
Definition: slru.h:57
int * page_number
Definition: slru.h:69
SlruPageStatus
Definition: slru.h:44
char ** page_buffer
Definition: slru.h:66
int cur_lru_count
Definition: slru.h:92
int lsn_groups_per_page
Definition: slru.h:81
Size SimpleLruShmemSize(int nslots, int nlsns)
Definition: slru.c:145
void LWLockRegisterTranche(int tranche_id, const char *tranche_name)
Definition: lwlock.c:603
SlruPageStatus * page_status
Definition: slru.h:67
char lwlock_tranche_name[SLRU_MAX_NAME_LENGTH]
Definition: slru.h:103
void * ShmemInitStruct(const char *name, Size size, bool *foundPtr)
Definition: shmem.c:372
bool IsUnderPostmaster
Definition: globals.c:109
LWLockPadded * buffer_locks
Definition: slru.h:104
XLogRecPtr * group_lsn
Definition: slru.h:80
bool do_fsync
Definition: slru.h:121
void LWLockInitialize(LWLock *lock, int tranche_id)
Definition: lwlock.c:678
SlruSharedData * SlruShared
Definition: slru.h:107
char Dir[64]
Definition: slru.h:134
#define SLRU_MAX_NAME_LENGTH
Definition: slru.h:36
LWLock lock
Definition: lwlock.h:79
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
int * page_lru_count
Definition: slru.h:70
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:732
#define StrNCpy(dst, src, len)
Definition: c.h:928
size_t Size
Definition: c.h:466
#define MAXALIGN(LEN)
Definition: c.h:685
int num_slots
Definition: slru.h:60
const char * name
Definition: encode.c:521
bool * page_dirty
Definition: slru.h:68
SlruShared shared
Definition: slru.h:115
#define BUFFERALIGN(LEN)
Definition: c.h:687
int lwlock_tranche_id
Definition: slru.h:102

◆ SimpleLruReadPage()

int SimpleLruReadPage ( SlruCtl  ctl,
int  pageno,
bool  write_ok,
TransactionId  xid 
)

Definition at line 375 of file slru.c.

References Assert, SlruSharedData::buffer_locks, SlruSharedData::ControlLock, LWLockPadded::lock, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), SlruSharedData::page_dirty, SlruSharedData::page_number, SlruSharedData::page_status, SlruCtlData::shared, SimpleLruWaitIO(), SimpleLruZeroLSNs(), SLRU_PAGE_EMPTY, SLRU_PAGE_READ_IN_PROGRESS, SLRU_PAGE_VALID, SLRU_PAGE_WRITE_IN_PROGRESS, SlruPhysicalReadPage(), SlruRecentlyUsed, SlruReportIOError(), and SlruSelectLRUPage().

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

377 {
378  SlruShared shared = ctl->shared;
379 
380  /* Outer loop handles restart if we must wait for someone else's I/O */
381  for (;;)
382  {
383  int slotno;
384  bool ok;
385 
386  /* See if page already is in memory; if not, pick victim slot */
387  slotno = SlruSelectLRUPage(ctl, pageno);
388 
389  /* Did we find the page in memory? */
390  if (shared->page_number[slotno] == pageno &&
391  shared->page_status[slotno] != SLRU_PAGE_EMPTY)
392  {
393  /*
394  * If page is still being read in, we must wait for I/O. Likewise
395  * if the page is being written and the caller said that's not OK.
396  */
397  if (shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS ||
398  (shared->page_status[slotno] == SLRU_PAGE_WRITE_IN_PROGRESS &&
399  !write_ok))
400  {
401  SimpleLruWaitIO(ctl, slotno);
402  /* Now we must recheck state from the top */
403  continue;
404  }
405  /* Otherwise, it's ready to use */
406  SlruRecentlyUsed(shared, slotno);
407  return slotno;
408  }
409 
410  /* We found no match; assert we selected a freeable slot */
411  Assert(shared->page_status[slotno] == SLRU_PAGE_EMPTY ||
412  (shared->page_status[slotno] == SLRU_PAGE_VALID &&
413  !shared->page_dirty[slotno]));
414 
415  /* Mark the slot read-busy */
416  shared->page_number[slotno] = pageno;
417  shared->page_status[slotno] = SLRU_PAGE_READ_IN_PROGRESS;
418  shared->page_dirty[slotno] = false;
419 
420  /* Acquire per-buffer lock (cannot deadlock, see notes at top) */
421  LWLockAcquire(&shared->buffer_locks[slotno].lock, LW_EXCLUSIVE);
422 
423  /* Release control lock while doing I/O */
424  LWLockRelease(shared->ControlLock);
425 
426  /* Do the read */
427  ok = SlruPhysicalReadPage(ctl, pageno, slotno);
428 
429  /* Set the LSNs for this newly read-in page to zero */
430  SimpleLruZeroLSNs(ctl, slotno);
431 
432  /* Re-acquire control lock and update page state */
434 
435  Assert(shared->page_number[slotno] == pageno &&
436  shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS &&
437  !shared->page_dirty[slotno]);
438 
439  shared->page_status[slotno] = ok ? SLRU_PAGE_VALID : SLRU_PAGE_EMPTY;
440 
441  LWLockRelease(&shared->buffer_locks[slotno].lock);
442 
443  /* Now it's okay to ereport if we failed */
444  if (!ok)
445  SlruReportIOError(ctl, pageno, xid);
446 
447  SlruRecentlyUsed(shared, slotno);
448  return slotno;
449  }
450 }
LWLock * ControlLock
Definition: slru.h:57
int * page_number
Definition: slru.h:69
static void SimpleLruZeroLSNs(SlruCtl ctl, int slotno)
Definition: slru.c:304
static void SlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid)
Definition: slru.c:898
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1726
SlruPageStatus * page_status
Definition: slru.h:67
LWLockPadded * buffer_locks
Definition: slru.h:104
static void SimpleLruWaitIO(SlruCtl ctl, int slotno)
Definition: slru.c:321
LWLock lock
Definition: lwlock.h:79
static bool SlruPhysicalReadPage(SlruCtl ctl, int pageno, int slotno)
Definition: slru.c:645
#define Assert(condition)
Definition: c.h:732
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1122
static int SlruSelectLRUPage(SlruCtl ctl, int pageno)
Definition: slru.c:972
bool * page_dirty
Definition: slru.h:68
SlruShared shared
Definition: slru.h:115
#define SlruRecentlyUsed(shared, slotno)
Definition: slru.c:103

◆ SimpleLruReadPage_ReadOnly()

int SimpleLruReadPage_ReadOnly ( SlruCtl  ctl,
int  pageno,
TransactionId  xid 
)

Definition at line 467 of file slru.c.

References SlruSharedData::ControlLock, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockRelease(), SlruSharedData::num_slots, SlruSharedData::page_number, SlruSharedData::page_status, SlruCtlData::shared, SimpleLruReadPage(), SLRU_PAGE_EMPTY, SLRU_PAGE_READ_IN_PROGRESS, and SlruRecentlyUsed.

Referenced by asyncQueueReadAllNotifications(), find_multixact_start(), OldSerXidGetMinConflictCommitSeqNo(), SubTransGetParent(), TransactionIdGetCommitTsData(), and TransactionIdGetStatus().

468 {
469  SlruShared shared = ctl->shared;
470  int slotno;
471 
472  /* Try to find the page while holding only shared lock */
474 
475  /* See if page is already in a buffer */
476  for (slotno = 0; slotno < shared->num_slots; slotno++)
477  {
478  if (shared->page_number[slotno] == pageno &&
479  shared->page_status[slotno] != SLRU_PAGE_EMPTY &&
480  shared->page_status[slotno] != SLRU_PAGE_READ_IN_PROGRESS)
481  {
482  /* See comments for SlruRecentlyUsed macro */
483  SlruRecentlyUsed(shared, slotno);
484  return slotno;
485  }
486  }
487 
488  /* No luck, so switch to normal exclusive lock and do regular read */
489  LWLockRelease(shared->ControlLock);
491 
492  return SimpleLruReadPage(ctl, pageno, true, xid);
493 }
LWLock * ControlLock
Definition: slru.h:57
int * page_number
Definition: slru.h:69
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1726
SlruPageStatus * page_status
Definition: slru.h:67
int SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok, TransactionId xid)
Definition: slru.c:375
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1122
int num_slots
Definition: slru.h:60
SlruShared shared
Definition: slru.h:115
#define SlruRecentlyUsed(shared, slotno)
Definition: slru.c:103

◆ SimpleLruShmemSize()

Size SimpleLruShmemSize ( int  nslots,
int  nlsns 
)

Definition at line 145 of file slru.c.

References BUFFERALIGN, and MAXALIGN.

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

146 {
147  Size sz;
148 
149  /* we assume nslots isn't so large as to risk overflow */
150  sz = MAXALIGN(sizeof(SlruSharedData));
151  sz += MAXALIGN(nslots * sizeof(char *)); /* page_buffer[] */
152  sz += MAXALIGN(nslots * sizeof(SlruPageStatus)); /* page_status[] */
153  sz += MAXALIGN(nslots * sizeof(bool)); /* page_dirty[] */
154  sz += MAXALIGN(nslots * sizeof(int)); /* page_number[] */
155  sz += MAXALIGN(nslots * sizeof(int)); /* page_lru_count[] */
156  sz += MAXALIGN(nslots * sizeof(LWLockPadded)); /* buffer_locks[] */
157 
158  if (nlsns > 0)
159  sz += MAXALIGN(nslots * nlsns * sizeof(XLogRecPtr)); /* group_lsn[] */
160 
161  return BUFFERALIGN(sz) + BLCKSZ * nslots;
162 }
SlruPageStatus
Definition: slru.h:44
uint64 XLogRecPtr
Definition: xlogdefs.h:21
size_t Size
Definition: c.h:466
#define MAXALIGN(LEN)
Definition: c.h:685
#define BUFFERALIGN(LEN)
Definition: c.h:687

◆ SimpleLruTruncate()

void SimpleLruTruncate ( SlruCtl  ctl,
int  cutoffPage 
)

Definition at line 1174 of file slru.c.

References SlruSharedData::ControlLock, SlruCtlData::Dir, ereport, errmsg(), SlruSharedData::latest_page_number, LOG, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), SlruSharedData::num_slots, SlruSharedData::page_dirty, SlruSharedData::page_number, SlruSharedData::page_status, SlruCtlData::PagePrecedes, SlruCtlData::shared, SimpleLruWaitIO(), SLRU_PAGE_EMPTY, SLRU_PAGE_VALID, SLRU_PAGES_PER_SEGMENT, SlruInternalWritePage(), SlruScanDirCbDeleteCutoff(), and SlruScanDirectory().

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

1175 {
1176  SlruShared shared = ctl->shared;
1177  int slotno;
1178 
1179  /*
1180  * The cutoff point is the start of the segment containing cutoffPage.
1181  */
1182  cutoffPage -= cutoffPage % SLRU_PAGES_PER_SEGMENT;
1183 
1184  /*
1185  * Scan shared memory and remove any pages preceding the cutoff page, to
1186  * ensure we won't rewrite them later. (Since this is normally called in
1187  * or just after a checkpoint, any dirty pages should have been flushed
1188  * already ... we're just being extra careful here.)
1189  */
1191 
1192 restart:;
1193 
1194  /*
1195  * While we are holding the lock, make an important safety check: the
1196  * planned cutoff point must be <= the current endpoint page. Otherwise we
1197  * have already wrapped around, and proceeding with the truncation would
1198  * risk removing the current segment.
1199  */
1200  if (ctl->PagePrecedes(shared->latest_page_number, cutoffPage))
1201  {
1202  LWLockRelease(shared->ControlLock);
1203  ereport(LOG,
1204  (errmsg("could not truncate directory \"%s\": apparent wraparound",
1205  ctl->Dir)));
1206  return;
1207  }
1208 
1209  for (slotno = 0; slotno < shared->num_slots; slotno++)
1210  {
1211  if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
1212  continue;
1213  if (!ctl->PagePrecedes(shared->page_number[slotno], cutoffPage))
1214  continue;
1215 
1216  /*
1217  * If page is clean, just change state to EMPTY (expected case).
1218  */
1219  if (shared->page_status[slotno] == SLRU_PAGE_VALID &&
1220  !shared->page_dirty[slotno])
1221  {
1222  shared->page_status[slotno] = SLRU_PAGE_EMPTY;
1223  continue;
1224  }
1225 
1226  /*
1227  * Hmm, we have (or may have) I/O operations acting on the page, so
1228  * we've got to wait for them to finish and then start again. This is
1229  * the same logic as in SlruSelectLRUPage. (XXX if page is dirty,
1230  * wouldn't it be OK to just discard it without writing it? For now,
1231  * keep the logic the same as it was.)
1232  */
1233  if (shared->page_status[slotno] == SLRU_PAGE_VALID)
1234  SlruInternalWritePage(ctl, slotno, NULL);
1235  else
1236  SimpleLruWaitIO(ctl, slotno);
1237  goto restart;
1238  }
1239 
1240  LWLockRelease(shared->ControlLock);
1241 
1242  /* Now we can remove the old segment(s) */
1243  (void) SlruScanDirectory(ctl, SlruScanDirCbDeleteCutoff, &cutoffPage);
1244 }
LWLock * ControlLock
Definition: slru.h:57
int * page_number
Definition: slru.h:69
static void SlruInternalWritePage(SlruCtl ctl, int slotno, SlruFlush fdata)
Definition: slru.c:507
int latest_page_number
Definition: slru.h:99
#define LOG
Definition: elog.h:26
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1726
SlruPageStatus * page_status
Definition: slru.h:67
#define ereport(elevel, rest)
Definition: elog.h:141
static void SimpleLruWaitIO(SlruCtl ctl, int slotno)
Definition: slru.c:321
static bool SlruScanDirCbDeleteCutoff(SlruCtl ctl, char *filename, int segpage, void *data)
Definition: slru.c:1344
char Dir[64]
Definition: slru.h:134
bool SlruScanDirectory(SlruCtl ctl, SlruScanCallback callback, void *data)
Definition: slru.c:1382
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1122
int num_slots
Definition: slru.h:60
int errmsg(const char *fmt,...)
Definition: elog.c:784
bool * page_dirty
Definition: slru.h:68
SlruShared shared
Definition: slru.h:115
bool(* PagePrecedes)(int, int)
Definition: slru.h:128
#define SLRU_PAGES_PER_SEGMENT
Definition: slru.h:33

◆ SimpleLruWaitIO()

static void SimpleLruWaitIO ( SlruCtl  ctl,
int  slotno 
)
static

Definition at line 321 of file slru.c.

References SlruSharedData::buffer_locks, SlruSharedData::ControlLock, LWLockPadded::lock, LW_EXCLUSIVE, LW_SHARED, LWLockAcquire(), LWLockConditionalAcquire(), LWLockRelease(), SlruSharedData::page_dirty, SlruSharedData::page_status, SlruCtlData::shared, 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().

322 {
323  SlruShared shared = ctl->shared;
324 
325  /* See notes at top of file */
326  LWLockRelease(shared->ControlLock);
327  LWLockAcquire(&shared->buffer_locks[slotno].lock, LW_SHARED);
328  LWLockRelease(&shared->buffer_locks[slotno].lock);
330 
331  /*
332  * If the slot is still in an io-in-progress state, then either someone
333  * already started a new I/O on the slot, or a previous I/O failed and
334  * neglected to reset the page state. That shouldn't happen, really, but
335  * it seems worth a few extra cycles to check and recover from it. We can
336  * cheaply test for failure by seeing if the buffer lock is still held (we
337  * assume that transaction abort would release the lock).
338  */
339  if (shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS ||
340  shared->page_status[slotno] == SLRU_PAGE_WRITE_IN_PROGRESS)
341  {
342  if (LWLockConditionalAcquire(&shared->buffer_locks[slotno].lock, LW_SHARED))
343  {
344  /* indeed, the I/O must have failed */
345  if (shared->page_status[slotno] == SLRU_PAGE_READ_IN_PROGRESS)
346  shared->page_status[slotno] = SLRU_PAGE_EMPTY;
347  else /* write_in_progress */
348  {
349  shared->page_status[slotno] = SLRU_PAGE_VALID;
350  shared->page_dirty[slotno] = true;
351  }
352  LWLockRelease(&shared->buffer_locks[slotno].lock);
353  }
354  }
355 }
LWLock * ControlLock
Definition: slru.h:57
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1726
SlruPageStatus * page_status
Definition: slru.h:67
bool LWLockConditionalAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1294
LWLockPadded * buffer_locks
Definition: slru.h:104
LWLock lock
Definition: lwlock.h:79
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1122
bool * page_dirty
Definition: slru.h:68
SlruShared shared
Definition: slru.h:115

◆ SimpleLruWritePage()

void SimpleLruWritePage ( SlruCtl  ctl,
int  slotno 
)

Definition at line 578 of file slru.c.

References SlruInternalWritePage().

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

579 {
580  SlruInternalWritePage(ctl, slotno, NULL);
581 }
static void SlruInternalWritePage(SlruCtl ctl, int slotno, SlruFlush fdata)
Definition: slru.c:507

◆ SimpleLruZeroLSNs()

static void SimpleLruZeroLSNs ( SlruCtl  ctl,
int  slotno 
)
static

Definition at line 304 of file slru.c.

References SlruSharedData::group_lsn, SlruSharedData::lsn_groups_per_page, MemSet, and SlruCtlData::shared.

Referenced by SimpleLruReadPage(), and SimpleLruZeroPage().

305 {
306  SlruShared shared = ctl->shared;
307 
308  if (shared->lsn_groups_per_page > 0)
309  MemSet(&shared->group_lsn[slotno * shared->lsn_groups_per_page], 0,
310  shared->lsn_groups_per_page * sizeof(XLogRecPtr));
311 }
#define MemSet(start, val, len)
Definition: c.h:955
int lsn_groups_per_page
Definition: slru.h:81
XLogRecPtr * group_lsn
Definition: slru.h:80
uint64 XLogRecPtr
Definition: xlogdefs.h:21
SlruShared shared
Definition: slru.h:115

◆ SimpleLruZeroPage()

int SimpleLruZeroPage ( SlruCtl  ctl,
int  pageno 
)

Definition at line 263 of file slru.c.

References Assert, SlruSharedData::latest_page_number, MemSet, SlruSharedData::page_buffer, SlruSharedData::page_dirty, SlruSharedData::page_number, SlruSharedData::page_status, SlruCtlData::shared, SimpleLruZeroLSNs(), SLRU_PAGE_EMPTY, SLRU_PAGE_VALID, SlruRecentlyUsed, and SlruSelectLRUPage().

Referenced by asyncQueueAddEntries(), AsyncShmemInit(), OldSerXidAdd(), ZeroCLOGPage(), ZeroCommitTsPage(), ZeroMultiXactMemberPage(), ZeroMultiXactOffsetPage(), and ZeroSUBTRANSPage().

264 {
265  SlruShared shared = ctl->shared;
266  int slotno;
267 
268  /* Find a suitable buffer slot for the page */
269  slotno = SlruSelectLRUPage(ctl, pageno);
270  Assert(shared->page_status[slotno] == SLRU_PAGE_EMPTY ||
271  (shared->page_status[slotno] == SLRU_PAGE_VALID &&
272  !shared->page_dirty[slotno]) ||
273  shared->page_number[slotno] == pageno);
274 
275  /* Mark the slot as containing this page */
276  shared->page_number[slotno] = pageno;
277  shared->page_status[slotno] = SLRU_PAGE_VALID;
278  shared->page_dirty[slotno] = true;
279  SlruRecentlyUsed(shared, slotno);
280 
281  /* Set the buffer to zeroes */
282  MemSet(shared->page_buffer[slotno], 0, BLCKSZ);
283 
284  /* Set the LSNs for this new page to zero */
285  SimpleLruZeroLSNs(ctl, slotno);
286 
287  /* Assume this page is now the latest active page */
288  shared->latest_page_number = pageno;
289 
290  return slotno;
291 }
int * page_number
Definition: slru.h:69
int latest_page_number
Definition: slru.h:99
char ** page_buffer
Definition: slru.h:66
#define MemSet(start, val, len)
Definition: c.h:955
static void SimpleLruZeroLSNs(SlruCtl ctl, int slotno)
Definition: slru.c:304
SlruPageStatus * page_status
Definition: slru.h:67
#define Assert(condition)
Definition: c.h:732
static int SlruSelectLRUPage(SlruCtl ctl, int pageno)
Definition: slru.c:972
bool * page_dirty
Definition: slru.h:68
SlruShared shared
Definition: slru.h:115
#define SlruRecentlyUsed(shared, slotno)
Definition: slru.c:103

◆ SlruDeleteSegment()

void SlruDeleteSegment ( SlruCtl  ctl,
int  segno 
)

Definition at line 1267 of file slru.c.

References SlruSharedData::ControlLock, DEBUG2, SlruCtlData::Dir, ereport, errmsg(), LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), MAXPGPATH, SlruSharedData::num_slots, SlruSharedData::page_dirty, SlruSharedData::page_number, SlruSharedData::page_status, SlruCtlData::shared, SimpleLruWaitIO(), SLRU_PAGE_EMPTY, SLRU_PAGE_VALID, SLRU_PAGES_PER_SEGMENT, SlruInternalWritePage(), and snprintf.

Referenced by PerformMembersTruncation().

1268 {
1269  SlruShared shared = ctl->shared;
1270  int slotno;
1271  char path[MAXPGPATH];
1272  bool did_write;
1273 
1274  /* Clean out any possibly existing references to the segment. */
1276 restart:
1277  did_write = false;
1278  for (slotno = 0; slotno < shared->num_slots; slotno++)
1279  {
1280  int pagesegno = shared->page_number[slotno] / SLRU_PAGES_PER_SEGMENT;
1281 
1282  if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
1283  continue;
1284 
1285  /* not the segment we're looking for */
1286  if (pagesegno != segno)
1287  continue;
1288 
1289  /* If page is clean, just change state to EMPTY (expected case). */
1290  if (shared->page_status[slotno] == SLRU_PAGE_VALID &&
1291  !shared->page_dirty[slotno])
1292  {
1293  shared->page_status[slotno] = SLRU_PAGE_EMPTY;
1294  continue;
1295  }
1296 
1297  /* Same logic as SimpleLruTruncate() */
1298  if (shared->page_status[slotno] == SLRU_PAGE_VALID)
1299  SlruInternalWritePage(ctl, slotno, NULL);
1300  else
1301  SimpleLruWaitIO(ctl, slotno);
1302 
1303  did_write = true;
1304  }
1305 
1306  /*
1307  * Be extra careful and re-check. The IO functions release the control
1308  * lock, so new pages could have been read in.
1309  */
1310  if (did_write)
1311  goto restart;
1312 
1313  snprintf(path, MAXPGPATH, "%s/%04X", ctl->Dir, segno);
1314  ereport(DEBUG2,
1315  (errmsg("removing file \"%s\"", path)));
1316  unlink(path);
1317 
1318  LWLockRelease(shared->ControlLock);
1319 }
LWLock * ControlLock
Definition: slru.h:57
int * page_number
Definition: slru.h:69
static void SlruInternalWritePage(SlruCtl ctl, int slotno, SlruFlush fdata)
Definition: slru.c:507
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1726
SlruPageStatus * page_status
Definition: slru.h:67
#define MAXPGPATH
#define DEBUG2
Definition: elog.h:24
#define ereport(elevel, rest)
Definition: elog.h:141
static void SimpleLruWaitIO(SlruCtl ctl, int slotno)
Definition: slru.c:321
char Dir[64]
Definition: slru.h:134
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1122
int num_slots
Definition: slru.h:60
int errmsg(const char *fmt,...)
Definition: elog.c:784
bool * page_dirty
Definition: slru.h:68
SlruShared shared
Definition: slru.h:115
#define SLRU_PAGES_PER_SEGMENT
Definition: slru.h:33
#define snprintf
Definition: port.h:192

◆ SlruInternalDeleteSegment()

static void SlruInternalDeleteSegment ( SlruCtl  ctl,
char *  filename 
)
static

Definition at line 1253 of file slru.c.

References DEBUG2, SlruCtlData::Dir, ereport, errmsg(), MAXPGPATH, and snprintf.

Referenced by SlruScanDirCbDeleteAll(), and SlruScanDirCbDeleteCutoff().

1254 {
1255  char path[MAXPGPATH];
1256 
1257  snprintf(path, MAXPGPATH, "%s/%s", ctl->Dir, filename);
1258  ereport(DEBUG2,
1259  (errmsg("removing file \"%s\"", path)));
1260  unlink(path);
1261 }
#define MAXPGPATH
#define DEBUG2
Definition: elog.h:24
#define ereport(elevel, rest)
Definition: elog.h:141
char Dir[64]
Definition: slru.h:134
static char * filename
Definition: pg_dumpall.c:91
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define snprintf
Definition: port.h:192

◆ SlruInternalWritePage()

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

Definition at line 507 of file slru.c.

References Assert, SlruSharedData::buffer_locks, CloseTransientFile(), SlruSharedData::ControlLock, SlruFlushData::fd, i, InvalidTransactionId, LWLockPadded::lock, LW_EXCLUSIVE, LWLockAcquire(), LWLockRelease(), SlruFlushData::num_files, SlruSharedData::page_dirty, SlruSharedData::page_number, SlruSharedData::page_status, SlruCtlData::shared, SimpleLruWaitIO(), SLRU_PAGE_VALID, SLRU_PAGE_WRITE_IN_PROGRESS, SlruPhysicalWritePage(), and SlruReportIOError().

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

508 {
509  SlruShared shared = ctl->shared;
510  int pageno = shared->page_number[slotno];
511  bool ok;
512 
513  /* If a write is in progress, wait for it to finish */
514  while (shared->page_status[slotno] == SLRU_PAGE_WRITE_IN_PROGRESS &&
515  shared->page_number[slotno] == pageno)
516  {
517  SimpleLruWaitIO(ctl, slotno);
518  }
519 
520  /*
521  * Do nothing if page is not dirty, or if buffer no longer contains the
522  * same page we were called for.
523  */
524  if (!shared->page_dirty[slotno] ||
525  shared->page_status[slotno] != SLRU_PAGE_VALID ||
526  shared->page_number[slotno] != pageno)
527  return;
528 
529  /*
530  * Mark the slot write-busy, and clear the dirtybit. After this point, a
531  * transaction status update on this page will mark it dirty again.
532  */
533  shared->page_status[slotno] = SLRU_PAGE_WRITE_IN_PROGRESS;
534  shared->page_dirty[slotno] = false;
535 
536  /* Acquire per-buffer lock (cannot deadlock, see notes at top) */
537  LWLockAcquire(&shared->buffer_locks[slotno].lock, LW_EXCLUSIVE);
538 
539  /* Release control lock while doing I/O */
540  LWLockRelease(shared->ControlLock);
541 
542  /* Do the write */
543  ok = SlruPhysicalWritePage(ctl, pageno, slotno, fdata);
544 
545  /* If we failed, and we're in a flush, better close the files */
546  if (!ok && fdata)
547  {
548  int i;
549 
550  for (i = 0; i < fdata->num_files; i++)
551  CloseTransientFile(fdata->fd[i]);
552  }
553 
554  /* Re-acquire control lock and update page state */
556 
557  Assert(shared->page_number[slotno] == pageno &&
558  shared->page_status[slotno] == SLRU_PAGE_WRITE_IN_PROGRESS);
559 
560  /* If we failed to write, mark the page dirty again */
561  if (!ok)
562  shared->page_dirty[slotno] = true;
563 
564  shared->page_status[slotno] = SLRU_PAGE_VALID;
565 
566  LWLockRelease(&shared->buffer_locks[slotno].lock);
567 
568  /* Now it's okay to ereport if we failed */
569  if (!ok)
571 }
LWLock * ControlLock
Definition: slru.h:57
int * page_number
Definition: slru.h:69
static bool SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno, SlruFlush fdata)
Definition: slru.c:725
static void SlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid)
Definition: slru.c:898
void LWLockRelease(LWLock *lock)
Definition: lwlock.c:1726
SlruPageStatus * page_status
Definition: slru.h:67
LWLockPadded * buffer_locks
Definition: slru.h:104
#define InvalidTransactionId
Definition: transam.h:31
static void SimpleLruWaitIO(SlruCtl ctl, int slotno)
Definition: slru.c:321
int CloseTransientFile(int fd)
Definition: fd.c:2434
int num_files
Definition: slru.c:77
LWLock lock
Definition: lwlock.h:79
#define Assert(condition)
Definition: c.h:732
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
Definition: lwlock.c:1122
bool * page_dirty
Definition: slru.h:68
int i
SlruShared shared
Definition: slru.h:115
int fd[MAX_FLUSH_BUFFERS]
Definition: slru.c:78

◆ SlruPhysicalReadPage()

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

Definition at line 645 of file slru.c.

References CloseTransientFile(), ereport, errmsg(), SlruFlushData::fd, InRecovery, LOG, MAXPGPATH, MemSet, OpenTransientFile(), SlruSharedData::page_buffer, PG_BINARY, pgstat_report_wait_end(), pgstat_report_wait_start(), read, SlruFlushData::segno, SlruCtlData::shared, SLRU_CLOSE_FAILED, slru_errcause, slru_errno, SLRU_OPEN_FAILED, SLRU_PAGES_PER_SEGMENT, SLRU_READ_FAILED, SLRU_SEEK_FAILED, SlruFileName, and WAIT_EVENT_SLRU_READ.

Referenced by SimpleLruReadPage().

646 {
647  SlruShared shared = ctl->shared;
648  int segno = pageno / SLRU_PAGES_PER_SEGMENT;
649  int rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
650  int offset = rpageno * BLCKSZ;
651  char path[MAXPGPATH];
652  int fd;
653 
654  SlruFileName(ctl, path, segno);
655 
656  /*
657  * In a crash-and-restart situation, it's possible for us to receive
658  * commands to set the commit status of transactions whose bits are in
659  * already-truncated segments of the commit log (see notes in
660  * SlruPhysicalWritePage). Hence, if we are InRecovery, allow the case
661  * where the file doesn't exist, and return zeroes instead.
662  */
663  fd = OpenTransientFile(path, O_RDONLY | PG_BINARY);
664  if (fd < 0)
665  {
666  if (errno != ENOENT || !InRecovery)
667  {
669  slru_errno = errno;
670  return false;
671  }
672 
673  ereport(LOG,
674  (errmsg("file \"%s\" doesn't exist, reading as zeroes",
675  path)));
676  MemSet(shared->page_buffer[slotno], 0, BLCKSZ);
677  return true;
678  }
679 
680  if (lseek(fd, (off_t) offset, SEEK_SET) < 0)
681  {
683  slru_errno = errno;
684  CloseTransientFile(fd);
685  return false;
686  }
687 
688  errno = 0;
690  if (read(fd, shared->page_buffer[slotno], BLCKSZ) != BLCKSZ)
691  {
694  slru_errno = errno;
695  CloseTransientFile(fd);
696  return false;
697  }
699 
700  if (CloseTransientFile(fd) != 0)
701  {
703  slru_errno = errno;
704  return false;
705  }
706 
707  return true;
708 }
char ** page_buffer
Definition: slru.h:66
bool InRecovery
Definition: xlog.c:200
#define MemSet(start, val, len)
Definition: c.h:955
static SlruErrorCause slru_errcause
Definition: slru.c:123
#define LOG
Definition: elog.h:26
static int fd(const char *x, int i)
Definition: preproc-init.c:105
#define PG_BINARY
Definition: c.h:1191
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2257
#define MAXPGPATH
static void pgstat_report_wait_end(void)
Definition: pgstat.h:1342
#define ereport(elevel, rest)
Definition: elog.h:141
int CloseTransientFile(int fd)
Definition: fd.c:2434
#define SlruFileName(ctl, path, seg)
Definition: slru.c:63
static void pgstat_report_wait_start(uint32 wait_event_info)
Definition: pgstat.h:1318
static int slru_errno
Definition: slru.c:124
int errmsg(const char *fmt,...)
Definition: elog.c:784
SlruShared shared
Definition: slru.h:115
#define SLRU_PAGES_PER_SEGMENT
Definition: slru.h:33
#define read(a, b, c)
Definition: win32.h:13

◆ SlruPhysicalWritePage()

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

Definition at line 725 of file slru.c.

References CloseTransientFile(), SlruCtlData::do_fsync, END_CRIT_SECTION, SlruFlushData::fd, SlruSharedData::group_lsn, i, SlruSharedData::lsn_groups_per_page, MAX_FLUSH_BUFFERS, MAXPGPATH, SlruFlushData::num_files, OpenTransientFile(), SlruSharedData::page_buffer, PG_BINARY, pg_fsync(), pgstat_report_wait_end(), pgstat_report_wait_start(), SlruFlushData::segno, SlruCtlData::shared, SLRU_CLOSE_FAILED, slru_errcause, slru_errno, SLRU_FSYNC_FAILED, SLRU_OPEN_FAILED, SLRU_PAGES_PER_SEGMENT, SLRU_SEEK_FAILED, SLRU_WRITE_FAILED, SlruFileName, START_CRIT_SECTION, WAIT_EVENT_SLRU_SYNC, WAIT_EVENT_SLRU_WRITE, write, XLogFlush(), and XLogRecPtrIsInvalid.

Referenced by SlruInternalWritePage().

726 {
727  SlruShared shared = ctl->shared;
728  int segno = pageno / SLRU_PAGES_PER_SEGMENT;
729  int rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
730  int offset = rpageno * BLCKSZ;
731  char path[MAXPGPATH];
732  int fd = -1;
733 
734  /*
735  * Honor the write-WAL-before-data rule, if appropriate, so that we do not
736  * write out data before associated WAL records. This is the same action
737  * performed during FlushBuffer() in the main buffer manager.
738  */
739  if (shared->group_lsn != NULL)
740  {
741  /*
742  * We must determine the largest async-commit LSN for the page. This
743  * is a bit tedious, but since this entire function is a slow path
744  * anyway, it seems better to do this here than to maintain a per-page
745  * LSN variable (which'd need an extra comparison in the
746  * transaction-commit path).
747  */
748  XLogRecPtr max_lsn;
749  int lsnindex,
750  lsnoff;
751 
752  lsnindex = slotno * shared->lsn_groups_per_page;
753  max_lsn = shared->group_lsn[lsnindex++];
754  for (lsnoff = 1; lsnoff < shared->lsn_groups_per_page; lsnoff++)
755  {
756  XLogRecPtr this_lsn = shared->group_lsn[lsnindex++];
757 
758  if (max_lsn < this_lsn)
759  max_lsn = this_lsn;
760  }
761 
762  if (!XLogRecPtrIsInvalid(max_lsn))
763  {
764  /*
765  * As noted above, elog(ERROR) is not acceptable here, so if
766  * XLogFlush were to fail, we must PANIC. This isn't much of a
767  * restriction because XLogFlush is just about all critical
768  * section anyway, but let's make sure.
769  */
771  XLogFlush(max_lsn);
773  }
774  }
775 
776  /*
777  * During a Flush, we may already have the desired file open.
778  */
779  if (fdata)
780  {
781  int i;
782 
783  for (i = 0; i < fdata->num_files; i++)
784  {
785  if (fdata->segno[i] == segno)
786  {
787  fd = fdata->fd[i];
788  break;
789  }
790  }
791  }
792 
793  if (fd < 0)
794  {
795  /*
796  * If the file doesn't already exist, we should create it. It is
797  * possible for this to need to happen when writing a page that's not
798  * first in its segment; we assume the OS can cope with that. (Note:
799  * it might seem that it'd be okay to create files only when
800  * SimpleLruZeroPage is called for the first page of a segment.
801  * However, if after a crash and restart the REDO logic elects to
802  * replay the log from a checkpoint before the latest one, then it's
803  * possible that we will get commands to set transaction status of
804  * transactions that have already been truncated from the commit log.
805  * Easiest way to deal with that is to accept references to
806  * nonexistent files here and in SlruPhysicalReadPage.)
807  *
808  * Note: it is possible for more than one backend to be executing this
809  * code simultaneously for different pages of the same file. Hence,
810  * don't use O_EXCL or O_TRUNC or anything like that.
811  */
812  SlruFileName(ctl, path, segno);
813  fd = OpenTransientFile(path, O_RDWR | O_CREAT | PG_BINARY);
814  if (fd < 0)
815  {
817  slru_errno = errno;
818  return false;
819  }
820 
821  if (fdata)
822  {
823  if (fdata->num_files < MAX_FLUSH_BUFFERS)
824  {
825  fdata->fd[fdata->num_files] = fd;
826  fdata->segno[fdata->num_files] = segno;
827  fdata->num_files++;
828  }
829  else
830  {
831  /*
832  * In the unlikely event that we exceed MAX_FLUSH_BUFFERS,
833  * fall back to treating it as a standalone write.
834  */
835  fdata = NULL;
836  }
837  }
838  }
839 
840  if (lseek(fd, (off_t) offset, SEEK_SET) < 0)
841  {
843  slru_errno = errno;
844  if (!fdata)
845  CloseTransientFile(fd);
846  return false;
847  }
848 
849  errno = 0;
851  if (write(fd, shared->page_buffer[slotno], BLCKSZ) != BLCKSZ)
852  {
854  /* if write didn't set errno, assume problem is no disk space */
855  if (errno == 0)
856  errno = ENOSPC;
858  slru_errno = errno;
859  if (!fdata)
860  CloseTransientFile(fd);
861  return false;
862  }
864 
865  /*
866  * If not part of Flush, need to fsync now. We assume this happens
867  * infrequently enough that it's not a performance issue.
868  */
869  if (!fdata)
870  {
872  if (ctl->do_fsync && pg_fsync(fd) != 0)
873  {
876  slru_errno = errno;
877  CloseTransientFile(fd);
878  return false;
879  }
881 
882  if (CloseTransientFile(fd) != 0)
883  {
885  slru_errno = errno;
886  return false;
887  }
888  }
889 
890  return true;
891 }
#define write(a, b, c)
Definition: win32.h:14
char ** page_buffer
Definition: slru.h:66
#define END_CRIT_SECTION()
Definition: miscadmin.h:134
#define START_CRIT_SECTION()
Definition: miscadmin.h:132
static SlruErrorCause slru_errcause
Definition: slru.c:123
int lsn_groups_per_page
Definition: slru.h:81
int segno[MAX_FLUSH_BUFFERS]
Definition: slru.c:79
void XLogFlush(XLogRecPtr record)
Definition: xlog.c:2798
static int fd(const char *x, int i)
Definition: preproc-init.c:105
#define PG_BINARY
Definition: c.h:1191
int OpenTransientFile(const char *fileName, int fileFlags)
Definition: fd.c:2257
#define MAXPGPATH
#define MAX_FLUSH_BUFFERS
Definition: slru.c:73
XLogRecPtr * group_lsn
Definition: slru.h:80
static void pgstat_report_wait_end(void)
Definition: pgstat.h:1342
bool do_fsync
Definition: slru.h:121
int CloseTransientFile(int fd)
Definition: fd.c:2434
#define XLogRecPtrIsInvalid(r)
Definition: xlogdefs.h:29
#define SlruFileName(ctl, path, seg)
Definition: slru.c:63
int num_files
Definition: slru.c:77
uint64 XLogRecPtr
Definition: xlogdefs.h:21
static void pgstat_report_wait_start(uint32 wait_event_info)
Definition: pgstat.h:1318
static int slru_errno
Definition: slru.c:124
int i
SlruShared shared
Definition: slru.h:115
int pg_fsync(int fd)
Definition: fd.c:333
#define SLRU_PAGES_PER_SEGMENT
Definition: slru.h:33
int fd[MAX_FLUSH_BUFFERS]
Definition: slru.c:78

◆ SlruReportIOError()

static void SlruReportIOError ( SlruCtl  ctl,
int  pageno,
TransactionId  xid 
)
static

Definition at line 898 of file slru.c.

References data_sync_elevel(), elog, ereport, errcode_for_file_access(), errdetail(), errmsg(), ERROR, MAXPGPATH, SlruFlushData::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(), SimpleLruFlush(), SimpleLruReadPage(), and SlruInternalWritePage().

899 {
900  int segno = pageno / SLRU_PAGES_PER_SEGMENT;
901  int rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
902  int offset = rpageno * BLCKSZ;
903  char path[MAXPGPATH];
904 
905  SlruFileName(ctl, path, segno);
906  errno = slru_errno;
907  switch (slru_errcause)
908  {
909  case SLRU_OPEN_FAILED:
910  ereport(ERROR,
912  errmsg("could not access status of transaction %u", xid),
913  errdetail("Could not open file \"%s\": %m.", path)));
914  break;
915  case SLRU_SEEK_FAILED:
916  ereport(ERROR,
918  errmsg("could not access status of transaction %u", xid),
919  errdetail("Could not seek in file \"%s\" to offset %u: %m.",
920  path, offset)));
921  break;
922  case SLRU_READ_FAILED:
923  ereport(ERROR,
925  errmsg("could not access status of transaction %u", xid),
926  errdetail("Could not read from file \"%s\" at offset %u: %m.",
927  path, offset)));
928  break;
929  case SLRU_WRITE_FAILED:
930  ereport(ERROR,
932  errmsg("could not access status of transaction %u", xid),
933  errdetail("Could not write to file \"%s\" at offset %u: %m.",
934  path, offset)));
935  break;
936  case SLRU_FSYNC_FAILED:
939  errmsg("could not access status of transaction %u", xid),
940  errdetail("Could not fsync file \"%s\": %m.",
941  path)));
942  break;
943  case SLRU_CLOSE_FAILED:
944  ereport(ERROR,
946  errmsg("could not access status of transaction %u", xid),
947  errdetail("Could not close file \"%s\": %m.",
948  path)));
949  break;
950  default:
951  /* can't get here, we trust */
952  elog(ERROR, "unrecognized SimpleLru error cause: %d",
953  (int) slru_errcause);
954  break;
955  }
956 }
static SlruErrorCause slru_errcause
Definition: slru.c:123
#define ERROR
Definition: elog.h:43
#define MAXPGPATH
int errdetail(const char *fmt,...)
Definition: elog.c:860
int errcode_for_file_access(void)
Definition: elog.c:593
#define ereport(elevel, rest)
Definition: elog.h:141
#define SlruFileName(ctl, path, seg)
Definition: slru.c:63
int data_sync_elevel(int elevel)
Definition: fd.c:3485
static int slru_errno
Definition: slru.c:124
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define elog(elevel,...)
Definition: elog.h:226
#define SLRU_PAGES_PER_SEGMENT
Definition: slru.h:33

◆ SlruScanDirCbDeleteAll()

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

Definition at line 1359 of file slru.c.

References SlruInternalDeleteSegment().

Referenced by AsyncShmemInit(), and DeactivateCommitTs().

1360 {
1362 
1363  return false; /* keep going */
1364 }
static void SlruInternalDeleteSegment(SlruCtl ctl, char *filename)
Definition: slru.c:1253
static char * filename
Definition: pg_dumpall.c:91

◆ SlruScanDirCbDeleteCutoff()

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

Definition at line 1344 of file slru.c.

References SlruCtlData::PagePrecedes, and SlruInternalDeleteSegment().

Referenced by SimpleLruTruncate().

1345 {
1346  int cutoffPage = *(int *) data;
1347 
1348  if (ctl->PagePrecedes(segpage, cutoffPage))
1350 
1351  return false; /* keep going */
1352 }
static void SlruInternalDeleteSegment(SlruCtl ctl, char *filename)
Definition: slru.c:1253
static char * filename
Definition: pg_dumpall.c:91
bool(* PagePrecedes)(int, int)
Definition: slru.h:128

◆ SlruScanDirCbReportPresence()

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

Definition at line 1327 of file slru.c.

References SlruCtlData::PagePrecedes, and SLRU_PAGES_PER_SEGMENT.

Referenced by TruncateCLOG(), and TruncateCommitTs().

1328 {
1329  int cutoffPage = *(int *) data;
1330 
1331  cutoffPage -= cutoffPage % SLRU_PAGES_PER_SEGMENT;
1332 
1333  if (ctl->PagePrecedes(segpage, cutoffPage))
1334  return true; /* found one; don't iterate any more */
1335 
1336  return false; /* keep going */
1337 }
bool(* PagePrecedes)(int, int)
Definition: slru.h:128
#define SLRU_PAGES_PER_SEGMENT
Definition: slru.h:33

◆ SlruScanDirectory()

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

Definition at line 1382 of file slru.c.

References AllocateDir(), callback(), dirent::d_name, DEBUG2, SlruCtlData::Dir, elog, FreeDir(), ReadDir(), SlruFlushData::segno, and SLRU_PAGES_PER_SEGMENT.

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

1383 {
1384  bool retval = false;
1385  DIR *cldir;
1386  struct dirent *clde;
1387  int segno;
1388  int segpage;
1389 
1390  cldir = AllocateDir(ctl->Dir);
1391  while ((clde = ReadDir(cldir, ctl->Dir)) != NULL)
1392  {
1393  size_t len;
1394 
1395  len = strlen(clde->d_name);
1396 
1397  if ((len == 4 || len == 5 || len == 6) &&
1398  strspn(clde->d_name, "0123456789ABCDEF") == len)
1399  {
1400  segno = (int) strtol(clde->d_name, NULL, 16);
1401  segpage = segno * SLRU_PAGES_PER_SEGMENT;
1402 
1403  elog(DEBUG2, "SlruScanDirectory invoking callback on %s/%s",
1404  ctl->Dir, clde->d_name);
1405  retval = callback(ctl, clde->d_name, segpage, data);
1406  if (retval)
1407  break;
1408  }
1409  }
1410  FreeDir(cldir);
1411 
1412  return retval;
1413 }
Definition: dirent.h:9
Definition: dirent.c:25
static void callback(struct sockaddr *addr, struct sockaddr *mask, void *unused)
Definition: test_ifaddrs.c:48
#define DEBUG2
Definition: elog.h:24
DIR * AllocateDir(const char *dirname)
Definition: fd.c:2468
char Dir[64]
Definition: slru.h:134
struct dirent * ReadDir(DIR *dir, const char *dirname)
Definition: fd.c:2534
#define elog(elevel,...)
Definition: elog.h:226
char d_name[MAX_PATH]
Definition: dirent.h:14
#define SLRU_PAGES_PER_SEGMENT
Definition: slru.h:33
int FreeDir(DIR *dir)
Definition: fd.c:2586

◆ SlruSelectLRUPage()

static int SlruSelectLRUPage ( SlruCtl  ctl,
int  pageno 
)
static

Definition at line 972 of file slru.c.

References SlruSharedData::cur_lru_count, SlruSharedData::latest_page_number, SlruSharedData::num_slots, SlruSharedData::page_dirty, SlruSharedData::page_lru_count, SlruSharedData::page_number, SlruSharedData::page_status, SlruCtlData::PagePrecedes, SlruCtlData::shared, SimpleLruWaitIO(), SLRU_PAGE_EMPTY, SLRU_PAGE_VALID, and SlruInternalWritePage().

Referenced by SimpleLruReadPage(), and SimpleLruZeroPage().

973 {
974  SlruShared shared = ctl->shared;
975 
976  /* Outer loop handles restart after I/O */
977  for (;;)
978  {
979  int slotno;
980  int cur_count;
981  int bestvalidslot = 0; /* keep compiler quiet */
982  int best_valid_delta = -1;
983  int best_valid_page_number = 0; /* keep compiler quiet */
984  int bestinvalidslot = 0; /* keep compiler quiet */
985  int best_invalid_delta = -1;
986  int best_invalid_page_number = 0; /* keep compiler quiet */
987 
988  /* See if page already has a buffer assigned */
989  for (slotno = 0; slotno < shared->num_slots; slotno++)
990  {
991  if (shared->page_number[slotno] == pageno &&
992  shared->page_status[slotno] != SLRU_PAGE_EMPTY)
993  return slotno;
994  }
995 
996  /*
997  * If we find any EMPTY slot, just select that one. Else choose a
998  * victim page to replace. We normally take the least recently used
999  * valid page, but we will never take the slot containing
1000  * latest_page_number, even if it appears least recently used. We
1001  * will select a slot that is already I/O busy only if there is no
1002  * other choice: a read-busy slot will not be least recently used once
1003  * the read finishes, and waiting for an I/O on a write-busy slot is
1004  * inferior to just picking some other slot. Testing shows the slot
1005  * we pick instead will often be clean, allowing us to begin a read at
1006  * once.
1007  *
1008  * Normally the page_lru_count values will all be different and so
1009  * there will be a well-defined LRU page. But since we allow
1010  * concurrent execution of SlruRecentlyUsed() within
1011  * SimpleLruReadPage_ReadOnly(), it is possible that multiple pages
1012  * acquire the same lru_count values. In that case we break ties by
1013  * choosing the furthest-back page.
1014  *
1015  * Notice that this next line forcibly advances cur_lru_count to a
1016  * value that is certainly beyond any value that will be in the
1017  * page_lru_count array after the loop finishes. This ensures that
1018  * the next execution of SlruRecentlyUsed will mark the page newly
1019  * used, even if it's for a page that has the current counter value.
1020  * That gets us back on the path to having good data when there are
1021  * multiple pages with the same lru_count.
1022  */
1023  cur_count = (shared->cur_lru_count)++;
1024  for (slotno = 0; slotno < shared->num_slots; slotno++)
1025  {
1026  int this_delta;
1027  int this_page_number;
1028 
1029  if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
1030  return slotno;
1031  this_delta = cur_count - shared->page_lru_count[slotno];
1032  if (this_delta < 0)
1033  {
1034  /*
1035  * Clean up in case shared updates have caused cur_count
1036  * increments to get "lost". We back off the page counts,
1037  * rather than trying to increase cur_count, to avoid any
1038  * question of infinite loops or failure in the presence of
1039  * wrapped-around counts.
1040  */
1041  shared->page_lru_count[slotno] = cur_count;
1042  this_delta = 0;
1043  }
1044  this_page_number = shared->page_number[slotno];
1045  if (this_page_number == shared->latest_page_number)
1046  continue;
1047  if (shared->page_status[slotno] == SLRU_PAGE_VALID)
1048  {
1049  if (this_delta > best_valid_delta ||
1050  (this_delta == best_valid_delta &&
1051  ctl->PagePrecedes(this_page_number,
1052  best_valid_page_number)))
1053  {
1054  bestvalidslot = slotno;
1055  best_valid_delta = this_delta;
1056  best_valid_page_number = this_page_number;
1057  }
1058  }
1059  else
1060  {
1061  if (this_delta > best_invalid_delta ||
1062  (this_delta == best_invalid_delta &&
1063  ctl->PagePrecedes(this_page_number,
1064  best_invalid_page_number)))
1065  {
1066  bestinvalidslot = slotno;
1067  best_invalid_delta = this_delta;
1068  best_invalid_page_number = this_page_number;
1069  }
1070  }
1071  }
1072 
1073  /*
1074  * If all pages (except possibly the latest one) are I/O busy, we'll
1075  * have to wait for an I/O to complete and then retry. In that
1076  * unhappy case, we choose to wait for the I/O on the least recently
1077  * used slot, on the assumption that it was likely initiated first of
1078  * all the I/Os in progress and may therefore finish first.
1079  */
1080  if (best_valid_delta < 0)
1081  {
1082  SimpleLruWaitIO(ctl, bestinvalidslot);
1083  continue;
1084  }
1085 
1086  /*
1087  * If the selected page is clean, we're set.
1088  */
1089  if (!shared->page_dirty[bestvalidslot])
1090  return bestvalidslot;
1091 
1092  /*
1093  * Write the page.
1094  */
1095  SlruInternalWritePage(ctl, bestvalidslot, NULL);
1096 
1097  /*
1098  * Now loop back and try again. This is the easiest way of dealing
1099  * with corner cases such as the victim page being re-dirtied while we
1100  * wrote it.
1101  */
1102  }
1103 }
int * page_number
Definition: slru.h:69
static void SlruInternalWritePage(SlruCtl ctl, int slotno, SlruFlush fdata)
Definition: slru.c:507
int latest_page_number
Definition: slru.h:99
int cur_lru_count
Definition: slru.h:92
SlruPageStatus * page_status
Definition: slru.h:67
static void SimpleLruWaitIO(SlruCtl ctl, int slotno)
Definition: slru.c:321
int * page_lru_count
Definition: slru.h:70
int num_slots
Definition: slru.h:60
bool * page_dirty
Definition: slru.h:68
SlruShared shared
Definition: slru.h:115
bool(* PagePrecedes)(int, int)
Definition: slru.h:128

Variable Documentation

◆ slru_errcause

◆ slru_errno

int slru_errno
static