PostgreSQL Source Code git master
slru_io.c
Go to the documentation of this file.
1/*
2 * slru_io.c
3 *
4 * Routines for reading and writing SLRU files during upgrade.
5 *
6 * Copyright (c) 2025, PostgreSQL Global Development Group
7 * src/bin/pg_upgrade/slru_io.c
8 */
9
10#include "postgres_fe.h"
11
12#include <fcntl.h>
13
14#include "common/fe_memutils.h"
15#include "common/file_perm.h"
16#include "common/file_utils.h"
17#include "pg_upgrade.h"
18#include "port/pg_iovec.h"
19#include "slru_io.h"
20
21static SlruSegState *AllocSlruSegState(const char *dir);
22static char *SlruFileName(SlruSegState *state, int64 segno);
23static void SlruFlush(SlruSegState *state);
24
25/* common parts of AllocSlruRead and AllocSlruWrite */
26static SlruSegState *
27AllocSlruSegState(const char *dir)
28{
29 SlruSegState *state = pg_malloc(sizeof(*state));
30
31 state->dir = pstrdup(dir);
32 state->fn = NULL;
33 state->fd = -1;
34 state->segno = -1;
35 state->pageno = 0;
36
37 /* state->writing and state->long_segment_names must be set by caller! */
38
39 return state;
40}
41
42/* similar to the backend function with the same name */
43static char *
45{
46 if (state->long_segment_names)
47 {
48 Assert(segno >= 0 && segno <= INT64CONST(0xFFFFFFFFFFFFFFF));
49 return psprintf("%s/%015" PRIX64, state->dir, segno);
50 }
51 else
52 {
53 Assert(segno >= 0 && segno <= INT64CONST(0xFFFFFF));
54 return psprintf("%s/%04X", state->dir, (unsigned int) segno);
55 }
56}
57
58/*
59 * Create SLRU reader for dir.
60 */
62AllocSlruRead(const char *dir, bool long_segment_names)
63{
65
66 state->writing = false;
67 state->long_segment_names = long_segment_names;
68
69 return state;
70}
71
72/*
73 * Read the given page into memory buffer.
74 *
75 * Reading can be done in random order.
76 *
77 * If the file containing 'pageno' does not exist, a fatal error is raised.
78 * If the file exists but is shorter than expected, the missing part is read
79 * as zeros and a warning is logged. That is reasonable behavior for current
80 * callers.
81 *
82 * This is the slow path of the inlineable SlruReadSwitchPage() function.
83 */
84char *
86{
87 int64 segno;
88 off_t offset;
89 ssize_t bytes_read;
90
91 Assert(!state->writing); /* read only mode */
92
93 if (state->segno != -1 && pageno == state->pageno)
94 return state->buf.data;
95
96 /* If the new page is on a different SLRU segment, open the new segment */
97 segno = pageno / SLRU_PAGES_PER_SEGMENT;
98 if (segno != state->segno)
99 {
100 if (state->segno != -1)
101 {
102 close(state->fd);
103 state->fd = -1;
104
105 pg_free(state->fn);
106 state->fn = NULL;
107
108 state->segno = -1;
109 }
110
111 state->fn = SlruFileName(state, segno);
112 if ((state->fd = open(state->fn, O_RDONLY | PG_BINARY, 0)) < 0)
113 pg_fatal("could not open file \"%s\": %m", state->fn);
114 state->segno = segno;
115 }
116
117 offset = (pageno % SLRU_PAGES_PER_SEGMENT) * BLCKSZ;
118 bytes_read = 0;
119 while (bytes_read < BLCKSZ)
120 {
121 ssize_t rc;
122
123 rc = pg_pread(state->fd,
124 &state->buf.data + bytes_read,
125 BLCKSZ - bytes_read,
126 offset);
127 if (rc < 0)
128 {
129 if (errno == EINTR)
130 continue;
131 pg_fatal("could not read file \"%s\": %m", state->fn);
132 }
133 if (rc == 0)
134 {
135 /* unexpected EOF */
136 pg_log(PG_WARNING, "unexpected EOF reading file \"%s\" at offset %u, reading as zeros",
137 state->fn, (unsigned int) offset);
138 memset(&state->buf.data + bytes_read, 0, BLCKSZ - bytes_read);
139 break;
140 }
141 bytes_read += rc;
142 offset += rc;
143 }
144 state->pageno = pageno;
145
146 return state->buf.data;
147}
148
149/*
150 * Free the reader.
151 */
152void
154{
155 Assert(!state->writing); /* read only mode */
156
157 if (state->fd != -1)
158 close(state->fd);
159 pg_free(state);
160}
161
162/*
163 * Create SLRU writer for dir.
164 */
166AllocSlruWrite(const char *dir, bool long_segment_names)
167{
169
170 state->writing = true;
171 state->long_segment_names = long_segment_names;
172
173 return state;
174}
175
176/*
177 * Open the given page for writing.
178 *
179 * NOTE: This uses O_EXCL when stepping to a new segment, so this assumes that
180 * each segment is written in full before moving on to the next one. This
181 * limitation would be easy to lift if needed, but it fits the usage pattern
182 * of current callers.
183 *
184 * This is the slow path of the inlineable SlruWriteSwitchPage() function.
185 */
186char *
188{
189 int64 segno;
190 off_t offset;
191
192 Assert(state->writing);
193
194 if (state->segno != -1 && pageno == state->pageno)
195 return state->buf.data;
196
197 segno = pageno / SLRU_PAGES_PER_SEGMENT;
198 offset = (pageno % SLRU_PAGES_PER_SEGMENT) * BLCKSZ;
199
201 memset(state->buf.data, 0, BLCKSZ);
202
203 if (segno != state->segno)
204 {
205 if (state->segno != -1)
206 {
207 close(state->fd);
208 state->fd = -1;
209
210 pg_free(state->fn);
211 state->fn = NULL;
212
213 state->segno = -1;
214 }
215
216 /* Create the segment */
217 state->fn = SlruFileName(state, segno);
218 if ((state->fd = open(state->fn, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
220 {
221 pg_fatal("could not create file \"%s\": %m", state->fn);
222 }
223
224 state->segno = segno;
225
226 if (offset > 0)
227 {
228 if (pg_pwrite_zeros(state->fd, offset, 0) < 0)
229 pg_fatal("could not write file \"%s\": %m", state->fn);
230 }
231 }
232
233 state->pageno = pageno;
234
235 return state->buf.data;
236}
237
238static void
240{
241 struct iovec iovec = {
242 .iov_base = &state->buf,
243 .iov_len = BLCKSZ,
244 };
245 off_t offset;
246
247 if (state->segno == -1)
248 return;
249
250 offset = (state->pageno % SLRU_PAGES_PER_SEGMENT) * BLCKSZ;
251
252 if (pg_pwritev_with_retry(state->fd, &iovec, 1, offset) < 0)
253 pg_fatal("could not write file \"%s\": %m", state->fn);
254}
255
256/*
257 * Free the writer.
258 */
259void
261{
262 Assert(state->writing);
263
265
266 if (state->fd != -1)
267 close(state->fd);
268 pg_free(state);
269}
#define INT64CONST(x)
Definition: c.h:566
int64_t int64
Definition: c.h:549
#define PG_BINARY
Definition: c.h:1271
uint64_t uint64
Definition: c.h:553
void * pg_malloc(size_t size)
Definition: fe_memutils.c:47
void pg_free(void *ptr)
Definition: fe_memutils.c:105
int pg_file_create_mode
Definition: file_perm.c:19
ssize_t pg_pwritev_with_retry(int fd, const struct iovec *iov, int iovcnt, pgoff_t offset)
Definition: file_utils.c:659
ssize_t pg_pwrite_zeros(int fd, size_t size, pgoff_t offset)
Definition: file_utils.c:709
Assert(PointerIsAligned(start, uint64))
#define close(a)
Definition: win32.h:12
char * pstrdup(const char *in)
Definition: mcxt.c:1759
#define pg_fatal(...)
#define SLRU_PAGES_PER_SEGMENT
void void pg_log(eLogType type, const char *fmt,...) pg_attribute_printf(2
@ PG_WARNING
Definition: pg_upgrade.h:284
#define pg_pread
Definition: port.h:247
char * psprintf(const char *fmt,...)
Definition: psprintf.c:43
char * SlruWriteSwitchPageSlow(SlruSegState *state, uint64 pageno)
Definition: slru_io.c:187
static void SlruFlush(SlruSegState *state)
Definition: slru_io.c:239
void FreeSlruWrite(SlruSegState *state)
Definition: slru_io.c:260
static char * SlruFileName(SlruSegState *state, int64 segno)
Definition: slru_io.c:44
static SlruSegState * AllocSlruSegState(const char *dir)
Definition: slru_io.c:27
SlruSegState * AllocSlruWrite(const char *dir, bool long_segment_names)
Definition: slru_io.c:166
char * SlruReadSwitchPageSlow(SlruSegState *state, uint64 pageno)
Definition: slru_io.c:85
SlruSegState * AllocSlruRead(const char *dir, bool long_segment_names)
Definition: slru_io.c:62
void FreeSlruRead(SlruSegState *state)
Definition: slru_io.c:153
Definition: regguts.h:323
#define EINTR
Definition: win32_port.h:361