]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/readahead/readahead-replay.c
f90821e831e67b28f6ad363b33d1e8f0ccf29ebf
[thirdparty/systemd.git] / src / readahead / readahead-replay.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <errno.h>
23 #include <inttypes.h>
24 #include <fcntl.h>
25 #include <linux/limits.h>
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/select.h>
31 #include <sys/time.h>
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <unistd.h>
35 #include <getopt.h>
36 #include <sys/inotify.h>
37
38 #include <systemd/sd-daemon.h>
39
40 #include "missing.h"
41 #include "util.h"
42 #include "set.h"
43 #include "ioprio.h"
44 #include "readahead-common.h"
45 #include "virt.h"
46
47 static ReadaheadShared *shared = NULL;
48
49 static int unpack_file(FILE *pack) {
50 char fn[PATH_MAX];
51 int r = 0, fd = -1;
52 bool any = false;
53 struct stat st;
54 uint64_t inode;
55
56 assert(pack);
57
58 if (!fgets(fn, sizeof(fn), pack))
59 return 0;
60
61 char_array_0(fn);
62 truncate_nl(fn);
63
64 fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW);
65 if (fd < 0) {
66
67 if (errno != ENOENT && errno != EPERM && errno != EACCES)
68 log_warning("open(%s) failed: %m", fn);
69
70 } else if (file_verify(fd, fn, arg_file_size_max, &st) <= 0) {
71 close_nointr_nofail(fd);
72 fd = -1;
73 }
74
75 if (fread(&inode, sizeof(inode), 1, pack) != 1) {
76 log_error("Premature end of pack file.");
77 r = -EIO;
78 goto finish;
79 }
80
81 if (fd >= 0) {
82 /* If the inode changed the file got deleted, so just
83 * ignore this entry */
84 if (st.st_ino != (uint64_t) inode) {
85 close_nointr_nofail(fd);
86 fd = -1;
87 }
88 }
89
90 for (;;) {
91 uint32_t b, c;
92
93 if (fread(&b, sizeof(b), 1, pack) != 1 ||
94 fread(&c, sizeof(c), 1, pack) != 1) {
95 log_error("Premature end of pack file.");
96 r = -EIO;
97 goto finish;
98 }
99
100 if (b == 0 && c == 0)
101 break;
102
103 if (c <= b) {
104 log_error("Invalid pack file.");
105 r = -EIO;
106 goto finish;
107 }
108
109 log_debug("%s: page %u to %u", fn, b, c);
110
111 any = true;
112
113 if (fd >= 0)
114 if (posix_fadvise(fd, b * page_size(), (c - b) * page_size(), POSIX_FADV_WILLNEED) < 0) {
115 log_warning("posix_fadvise() failed: %m");
116 goto finish;
117 }
118 }
119
120 if (!any && fd >= 0) {
121 /* if no range is encoded in the pack file this is
122 * intended to mean that the whole file shall be
123 * read */
124
125 if (posix_fadvise(fd, 0, st.st_size, POSIX_FADV_WILLNEED) < 0) {
126 log_warning("posix_fadvise() failed: %m");
127 goto finish;
128 }
129 }
130
131 finish:
132 if (fd >= 0)
133 close_nointr_nofail(fd);
134
135 return r;
136 }
137
138 static int replay(const char *root) {
139 FILE *pack = NULL;
140 char line[LINE_MAX];
141 int r = 0;
142 char *pack_fn = NULL;
143 int c;
144 bool on_ssd, ready = false;
145 int prio;
146 int inotify_fd = -1;
147
148 assert(root);
149
150 block_bump_request_nr(root);
151
152 if (asprintf(&pack_fn, "%s/.readahead", root) < 0) {
153 log_error("Out of memory.");
154 r = -ENOMEM;
155 goto finish;
156 }
157
158 pack = fopen(pack_fn, "re");
159 if (!pack) {
160 if (errno == ENOENT)
161 log_debug("No pack file found.");
162 else {
163 log_error("Failed to open pack file: %m");
164 r = -errno;
165 }
166
167 goto finish;
168 }
169
170 posix_fadvise(fileno(pack), 0, 0, POSIX_FADV_WILLNEED);
171
172 if ((inotify_fd = open_inotify()) < 0) {
173 r = inotify_fd;
174 goto finish;
175 }
176
177 if (!(fgets(line, sizeof(line), pack))) {
178 log_error("Premature end of pack file.");
179 r = -EIO;
180 goto finish;
181 }
182
183 char_array_0(line);
184
185 if (!streq(line, CANONICAL_HOST READAHEAD_PACK_FILE_VERSION)) {
186 log_debug("Pack file host or version type mismatch.");
187 goto finish;
188 }
189
190 if ((c = getc(pack)) == EOF) {
191 log_debug("Premature end of pack file.");
192 r = -EIO;
193 goto finish;
194 }
195
196 /* We do not retest SSD here, so that we can start replaying
197 * before udev is up.*/
198 on_ssd = c == 'S';
199 log_debug("On SSD: %s", yes_no(on_ssd));
200
201 if (on_ssd)
202 prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0);
203 else
204 /* We are not using RT here, since we'd starve IO that
205 we didn't record (which is for example blkid, since
206 its disk accesses go directly to the block device and
207 are thus not visible in fallocate) to death. However,
208 we do ask for an IO prio that is slightly higher than
209 the default (which is BE. 4) */
210 prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 2);
211
212 if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), prio) < 0)
213 log_warning("Failed to set IDLE IO priority class: %m");
214
215 sd_notify(0, "STATUS=Replaying readahead data");
216
217 log_debug("Replaying...");
218
219 if (access("/run/systemd/readahead/noreplay", F_OK) >= 0) {
220 log_debug("Got termination request");
221 goto done;
222 }
223
224 while (!feof(pack) && !ferror(pack)) {
225 uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
226 int k;
227 ssize_t n;
228
229 if ((n = read(inotify_fd, &inotify_buffer, sizeof(inotify_buffer))) < 0) {
230 if (errno != EINTR && errno != EAGAIN) {
231 log_error("Failed to read inotify event: %m");
232 r = -errno;
233 goto finish;
234 }
235 } else {
236 struct inotify_event *e = (struct inotify_event*) inotify_buffer;
237
238 while (n > 0) {
239 size_t step;
240
241 if ((e->mask & IN_CREATE) && streq(e->name, "noreplay")) {
242 log_debug("Got termination request");
243 goto done;
244 }
245
246 step = sizeof(struct inotify_event) + e->len;
247 assert(step <= (size_t) n);
248
249 e = (struct inotify_event*) ((uint8_t*) e + step);
250 n -= step;
251 }
252 }
253
254 if ((k = unpack_file(pack)) < 0) {
255 r = k;
256 goto finish;
257 }
258
259 if (!ready) {
260 /* We delay the ready notification until we
261 * queued at least one read */
262 sd_notify(0, "READY=1");
263 ready = true;
264 }
265 }
266
267 done:
268 if (!ready)
269 sd_notify(0, "READY=1");
270
271 if (ferror(pack)) {
272 log_error("Failed to read pack file.");
273 r = -EIO;
274 goto finish;
275 }
276
277 log_debug("Done.");
278
279 finish:
280 if (pack)
281 fclose(pack);
282
283 if (inotify_fd >= 0)
284 close_nointr_nofail(inotify_fd);
285
286 free(pack_fn);
287
288 return r;
289 }
290
291 int main_replay(const char *root) {
292
293 if (!root)
294 root = "/";
295
296 if (!enough_ram()) {
297 log_info("Disabling readahead replay due to low memory.");
298 return EXIT_SUCCESS;
299 }
300
301 shared = shared_get();
302 if (!shared)
303 return EXIT_FAILURE;
304
305 shared->replay = getpid();
306 __sync_synchronize();
307
308 if (replay(root) < 0)
309 return EXIT_FAILURE;
310
311 return EXIT_SUCCESS;
312 }