]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/readahead/readahead-replay.c
readahead: don't monopolize IO when replaying
[thirdparty/systemd.git] / src / readahead / readahead-replay.c
CommitLineData
22be093f
LP
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 General Public License as published by
10 the Free Software Foundation; either version 2 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 General Public License for more details.
17
18 You should have received a copy of the GNU 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>
8260358d 35#include <getopt.h>
6624768c 36#include <sys/inotify.h>
22be093f 37
81527be1
LP
38#include <systemd/sd-daemon.h>
39
22be093f
LP
40#include "missing.h"
41#include "util.h"
42#include "set.h"
22be093f
LP
43#include "ioprio.h"
44#include "readahead-common.h"
b52aae1d 45#include "virt.h"
22be093f 46
8260358d
LP
47static off_t arg_file_size_max = READAHEAD_FILE_SIZE_MAX;
48
d9c7a87b
LP
49static ReadaheadShared *shared = NULL;
50
22be093f
LP
51static 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
a78899f5
LP
65 if ((fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW)) < 0) {
66
a76fad09 67 if (errno != ENOENT && errno != EPERM && errno != EACCES)
a78899f5
LP
68 log_warning("open(%s) failed: %m", fn);
69
70 } else if (file_verify(fd, fn, arg_file_size_max, &st) <= 0) {
22be093f
LP
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)
37f85e66 99 if (posix_fadvise(fd, b * page_size(), (c - b) * page_size(), POSIX_FADV_WILLNEED) < 0) {
6e66797a 100 log_warning("posix_fadvise() failed: %m");
22be093f
LP
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
6e66797a
HH
110 if (posix_fadvise(fd, 0, st.st_size, POSIX_FADV_WILLNEED) < 0) {
111 log_warning("posix_fadvise() failed: %m");
22be093f
LP
112 goto finish;
113 }
114 }
115
116finish:
117 if (fd >= 0)
118 close_nointr_nofail(fd);
119
120 return r;
121}
122
123static int replay(const char *root) {
858209c5 124 FILE *pack = NULL;
22be093f
LP
125 char line[LINE_MAX];
126 int r = 0;
da19d5c1
LP
127 char *pack_fn = NULL;
128 int c;
902a339c 129 bool on_ssd, ready = false;
22be093f 130 int prio;
6624768c 131 int inotify_fd = -1;
22be093f
LP
132
133 assert(root);
134
75a010e0 135 write_one_line_file("/proc/self/oom_score_adj", "1000");
de58283f 136 bump_request_nr(root);
75a010e0 137
22be093f
LP
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")))) {
a78899f5 145 if (errno == ENOENT)
22be093f
LP
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
bdb0e14d
LP
155 posix_fadvise(fileno(pack), 0, 0, POSIX_FADV_WILLNEED);
156
6624768c
LP
157 if ((inotify_fd = open_inotify()) < 0) {
158 r = inotify_fd;
159 goto finish;
160 }
161
22be093f
LP
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
05aa9edd
LP
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);
22be093f
LP
196
197 if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), prio) < 0)
198 log_warning("Failed to set IDLE IO priority class: %m");
199
902a339c 200 sd_notify(0, "STATUS=Replaying readahead data");
22be093f
LP
201
202 log_debug("Replaying...");
203
2b583ce6 204 if (access("/run/systemd/readahead/noreplay", F_OK) >= 0) {
6624768c
LP
205 log_debug("Got termination request");
206 goto done;
207 }
208
22be093f 209 while (!feof(pack) && !ferror(pack)) {
6624768c 210 uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
22be093f 211 int k;
6624768c
LP
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 }
22be093f
LP
238
239 if ((k = unpack_file(pack)) < 0) {
240 r = k;
241 goto finish;
242 }
902a339c
LP
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 }
22be093f
LP
250 }
251
6624768c 252done:
902a339c
LP
253 if (!ready)
254 sd_notify(0, "READY=1");
255
22be093f
LP
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
264finish:
265 if (pack)
266 fclose(pack);
267
6624768c
LP
268 if (inotify_fd >= 0)
269 close_nointr_nofail(inotify_fd);
270
22be093f
LP
271 free(pack_fn);
272
273 return r;
274}
275
8260358d
LP
276
277static 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
288static 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
22be093f 343int main(int argc, char*argv[]) {
8260358d 344 int r;
2b590e13 345 const char *root;
4030d7a9 346
4cfa2c99 347 log_set_target(LOG_TARGET_AUTO);
22be093f
LP
348 log_parse_environment();
349 log_open();
350
4c12626c
LP
351 umask(0022);
352
8260358d
LP
353 if ((r = parse_argv(argc, argv)) <= 0)
354 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
355
2b590e13
LP
356 root = optind < argc ? argv[optind] : "/";
357
41a598e2
LP
358 if (!enough_ram()) {
359 log_info("Disabling readahead replay due to low memory.");
360 return 0;
361 }
362
07faed4f
LP
363 if (detect_virtualization(NULL) > 0) {
364 log_info("Disabling readahead replay due to execution in virtualized environment.");
46a08e38
LP
365 return 0;
366 }
367
d9c7a87b
LP
368 if (!(shared = shared_get()))
369 return 1;
370
371 shared->replay = getpid();
372 __sync_synchronize();
373
2b590e13 374 if (replay(root) < 0)
22be093f
LP
375 return 1;
376
377 return 0;
378}