]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/readahead/readahead-replay.c
readahead: rather than checking for virtualization in the C code, use ConditionVirtua...
[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 off_t arg_file_size_max = READAHEAD_FILE_SIZE_MAX;
48
49 static ReadaheadShared *shared = NULL;
50
51 static int unpack_file(FILE *pack) {
52 char fn[PATH_MAX];
53 int r = 0, fd = -1;
54 bool any = false;
55 struct stat st;
56
57 assert(pack);
58
59 if (!fgets(fn, sizeof(fn), pack))
60 return 0;
61
62 char_array_0(fn);
63 truncate_nl(fn);
64
65 if ((fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW)) < 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 for (;;) {
76 uint32_t b, c;
77
78 if (fread(&b, sizeof(b), 1, pack) != 1 ||
79 fread(&c, sizeof(c), 1, pack) != 1) {
80 log_error("Premature end of pack file.");
81 r = -EIO;
82 goto finish;
83 }
84
85 if (b == 0 && c == 0)
86 break;
87
88 if (c <= b) {
89 log_error("Invalid pack file.");
90 r = -EIO;
91 goto finish;
92 }
93
94 log_debug("%s: page %u to %u", fn, b, c);
95
96 any = true;
97
98 if (fd >= 0)
99 if (posix_fadvise(fd, b * page_size(), (c - b) * page_size(), POSIX_FADV_WILLNEED) < 0) {
100 log_warning("posix_fadvise() failed: %m");
101 goto finish;
102 }
103 }
104
105 if (!any && fd >= 0) {
106 /* if no range is encoded in the pack file this is
107 * intended to mean that the whole file shall be
108 * read */
109
110 if (posix_fadvise(fd, 0, st.st_size, POSIX_FADV_WILLNEED) < 0) {
111 log_warning("posix_fadvise() failed: %m");
112 goto finish;
113 }
114 }
115
116 finish:
117 if (fd >= 0)
118 close_nointr_nofail(fd);
119
120 return r;
121 }
122
123 static int replay(const char *root) {
124 FILE *pack = NULL;
125 char line[LINE_MAX];
126 int r = 0;
127 char *pack_fn = NULL;
128 int c;
129 bool on_ssd, ready = false;
130 int prio;
131 int inotify_fd = -1;
132
133 assert(root);
134
135 write_one_line_file("/proc/self/oom_score_adj", "1000");
136 bump_request_nr(root);
137
138 if (asprintf(&pack_fn, "%s/.readahead", root) < 0) {
139 log_error("Out of memory");
140 r = -ENOMEM;
141 goto finish;
142 }
143
144 if ((!(pack = fopen(pack_fn, "re")))) {
145 if (errno == ENOENT)
146 log_debug("No pack file found.");
147 else {
148 log_error("Failed to open pack file: %m");
149 r = -errno;
150 }
151
152 goto finish;
153 }
154
155 posix_fadvise(fileno(pack), 0, 0, POSIX_FADV_WILLNEED);
156
157 if ((inotify_fd = open_inotify()) < 0) {
158 r = inotify_fd;
159 goto finish;
160 }
161
162 if (!(fgets(line, sizeof(line), pack))) {
163 log_error("Premature end of pack file.");
164 r = -EIO;
165 goto finish;
166 }
167
168 char_array_0(line);
169
170 if (!streq(line, CANONICAL_HOST "\n")) {
171 log_debug("Pack file host type mismatch.");
172 goto finish;
173 }
174
175 if ((c = getc(pack)) == EOF) {
176 log_debug("Premature end of pack file.");
177 r = -EIO;
178 goto finish;
179 }
180
181 /* We do not retest SSD here, so that we can start replaying
182 * before udev is up.*/
183 on_ssd = c == 'S';
184 log_debug("On SSD: %s", yes_no(on_ssd));
185
186 if (on_ssd)
187 prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0);
188 else
189 /* We are not using RT here, since we'd starve IO that
190 we didn't record (which is for example blkid, since
191 its disk accesses go directly to the block device and
192 are thus not visible in fallocate) to death. However,
193 we do ask for an IO prio that is slightly higher than
194 the default (which is BE. 4) */
195 prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 2);
196
197 if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), prio) < 0)
198 log_warning("Failed to set IDLE IO priority class: %m");
199
200 sd_notify(0, "STATUS=Replaying readahead data");
201
202 log_debug("Replaying...");
203
204 if (access("/run/systemd/readahead/noreplay", F_OK) >= 0) {
205 log_debug("Got termination request");
206 goto done;
207 }
208
209 while (!feof(pack) && !ferror(pack)) {
210 uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
211 int k;
212 ssize_t n;
213
214 if ((n = read(inotify_fd, &inotify_buffer, sizeof(inotify_buffer))) < 0) {
215 if (errno != EINTR && errno != EAGAIN) {
216 log_error("Failed to read inotify event: %m");
217 r = -errno;
218 goto finish;
219 }
220 } else {
221 struct inotify_event *e = (struct inotify_event*) inotify_buffer;
222
223 while (n > 0) {
224 size_t step;
225
226 if ((e->mask & IN_CREATE) && streq(e->name, "noreplay")) {
227 log_debug("Got termination request");
228 goto done;
229 }
230
231 step = sizeof(struct inotify_event) + e->len;
232 assert(step <= (size_t) n);
233
234 e = (struct inotify_event*) ((uint8_t*) e + step);
235 n -= step;
236 }
237 }
238
239 if ((k = unpack_file(pack)) < 0) {
240 r = k;
241 goto finish;
242 }
243
244 if (!ready) {
245 /* We delay the ready notification until we
246 * queued at least one read */
247 sd_notify(0, "READY=1");
248 ready = true;
249 }
250 }
251
252 done:
253 if (!ready)
254 sd_notify(0, "READY=1");
255
256 if (ferror(pack)) {
257 log_error("Failed to read pack file.");
258 r = -EIO;
259 goto finish;
260 }
261
262 log_debug("Done.");
263
264 finish:
265 if (pack)
266 fclose(pack);
267
268 if (inotify_fd >= 0)
269 close_nointr_nofail(inotify_fd);
270
271 free(pack_fn);
272
273 return r;
274 }
275
276
277 static int help(void) {
278
279 printf("%s [OPTIONS...] [DIRECTORY]\n\n"
280 "Replay collected read-ahead data on early boot.\n\n"
281 " -h --help Show this help\n"
282 " --max-file-size=BYTES Maximum size of files to read ahead\n",
283 program_invocation_short_name);
284
285 return 0;
286 }
287
288 static int parse_argv(int argc, char *argv[]) {
289
290 enum {
291 ARG_FILE_SIZE_MAX
292 };
293
294 static const struct option options[] = {
295 { "help", no_argument, NULL, 'h' },
296 { "file-size-max", required_argument, NULL, ARG_FILE_SIZE_MAX },
297 { NULL, 0, NULL, 0 }
298 };
299
300 int c;
301
302 assert(argc >= 0);
303 assert(argv);
304
305 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
306
307 switch (c) {
308
309 case 'h':
310 help();
311 return 0;
312
313 case ARG_FILE_SIZE_MAX: {
314 unsigned long long ull;
315
316 if (safe_atollu(optarg, &ull) < 0 || ull <= 0) {
317 log_error("Failed to parse maximum file size %s.", optarg);
318 return -EINVAL;
319 }
320
321 arg_file_size_max = (off_t) ull;
322 break;
323 }
324
325 case '?':
326 return -EINVAL;
327
328 default:
329 log_error("Unknown option code %c", c);
330 return -EINVAL;
331 }
332 }
333
334 if (optind != argc &&
335 optind != argc-1) {
336 help();
337 return -EINVAL;
338 }
339
340 return 1;
341 }
342
343 int main(int argc, char*argv[]) {
344 int r;
345 const char *root;
346
347 log_set_target(LOG_TARGET_AUTO);
348 log_parse_environment();
349 log_open();
350
351 umask(0022);
352
353 r = parse_argv(argc, argv);
354 if (r <= 0)
355 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
356
357 root = optind < argc ? argv[optind] : "/";
358
359 if (!enough_ram()) {
360 log_info("Disabling readahead replay due to low memory.");
361 return 0;
362 }
363
364 shared = shared_get();
365 if (!shared)
366 return 1;
367
368 shared->replay = getpid();
369 __sync_synchronize();
370
371 if (replay(root) < 0)
372 return 1;
373
374 return 0;
375 }