chiark / gitweb /
readahead: rather than checking for virtualization in the C code, use ConditionVirtua...
[elogind.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 }