chiark / gitweb /
a6529f8bace696c8a01393dcfafe7e62a9b71ebf
[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         block_bump_request_nr(root);
136
137         if (asprintf(&pack_fn, "%s/.readahead", root) < 0) {
138                 log_error("Out of memory");
139                 r = -ENOMEM;
140                 goto finish;
141         }
142
143         if ((!(pack = fopen(pack_fn, "re")))) {
144                 if (errno == ENOENT)
145                         log_debug("No pack file found.");
146                 else {
147                         log_error("Failed to open pack file: %m");
148                         r = -errno;
149                 }
150
151                 goto finish;
152         }
153
154         posix_fadvise(fileno(pack), 0, 0, POSIX_FADV_WILLNEED);
155
156         if ((inotify_fd = open_inotify()) < 0) {
157                 r = inotify_fd;
158                 goto finish;
159         }
160
161         if (!(fgets(line, sizeof(line), pack))) {
162                 log_error("Premature end of pack file.");
163                 r = -EIO;
164                 goto finish;
165         }
166
167         char_array_0(line);
168
169         if (!streq(line, CANONICAL_HOST "\n")) {
170                 log_debug("Pack file host type mismatch.");
171                 goto finish;
172         }
173
174         if ((c = getc(pack)) == EOF) {
175                 log_debug("Premature end of pack file.");
176                 r = -EIO;
177                 goto finish;
178         }
179
180         /* We do not retest SSD here, so that we can start replaying
181          * before udev is up.*/
182         on_ssd = c == 'S';
183         log_debug("On SSD: %s", yes_no(on_ssd));
184
185         if (on_ssd)
186                 prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0);
187         else
188                 /* We are not using RT here, since we'd starve IO that
189                 we didn't record (which is for example blkid, since
190                 its disk accesses go directly to the block device and
191                 are thus not visible in fallocate) to death. However,
192                 we do ask for an IO prio that is slightly higher than
193                 the default (which is BE. 4) */
194                 prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 2);
195
196         if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), prio) < 0)
197                 log_warning("Failed to set IDLE IO priority class: %m");
198
199         sd_notify(0, "STATUS=Replaying readahead data");
200
201         log_debug("Replaying...");
202
203         if (access("/run/systemd/readahead/noreplay", F_OK) >= 0) {
204                 log_debug("Got termination request");
205                 goto done;
206         }
207
208         while (!feof(pack) && !ferror(pack)) {
209                 uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
210                 int k;
211                 ssize_t n;
212
213                 if ((n = read(inotify_fd, &inotify_buffer, sizeof(inotify_buffer))) < 0) {
214                         if (errno != EINTR && errno != EAGAIN) {
215                                 log_error("Failed to read inotify event: %m");
216                                 r = -errno;
217                                 goto finish;
218                         }
219                 } else {
220                         struct inotify_event *e = (struct inotify_event*) inotify_buffer;
221
222                         while (n > 0) {
223                                 size_t step;
224
225                                 if ((e->mask & IN_CREATE) && streq(e->name, "noreplay")) {
226                                         log_debug("Got termination request");
227                                         goto done;
228                                 }
229
230                                 step = sizeof(struct inotify_event) + e->len;
231                                 assert(step <= (size_t) n);
232
233                                 e = (struct inotify_event*) ((uint8_t*) e + step);
234                                 n -= step;
235                         }
236                 }
237
238                 if ((k = unpack_file(pack)) < 0) {
239                         r = k;
240                         goto finish;
241                 }
242
243                 if (!ready) {
244                         /* We delay the ready notification until we
245                          * queued at least one read */
246                         sd_notify(0, "READY=1");
247                         ready = true;
248                 }
249         }
250
251 done:
252         if (!ready)
253                 sd_notify(0, "READY=1");
254
255         if (ferror(pack)) {
256                 log_error("Failed to read pack file.");
257                 r = -EIO;
258                 goto finish;
259         }
260
261         log_debug("Done.");
262
263 finish:
264         if (pack)
265                 fclose(pack);
266
267         if (inotify_fd >= 0)
268                 close_nointr_nofail(inotify_fd);
269
270         free(pack_fn);
271
272         return r;
273 }
274
275
276 static int help(void) {
277
278         printf("%s [OPTIONS...] [DIRECTORY]\n\n"
279                "Replay collected read-ahead data on early boot.\n\n"
280                "  -h --help                 Show this help\n"
281                "     --max-file-size=BYTES  Maximum size of files to read ahead\n",
282                program_invocation_short_name);
283
284         return 0;
285 }
286
287 static int parse_argv(int argc, char *argv[]) {
288
289         enum {
290                 ARG_FILE_SIZE_MAX
291         };
292
293         static const struct option options[] = {
294                 { "help",          no_argument,       NULL, 'h'                },
295                 { "file-size-max", required_argument, NULL, ARG_FILE_SIZE_MAX  },
296                 { NULL,            0,                 NULL, 0                  }
297         };
298
299         int c;
300
301         assert(argc >= 0);
302         assert(argv);
303
304         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
305
306                 switch (c) {
307
308                 case 'h':
309                         help();
310                         return 0;
311
312                 case ARG_FILE_SIZE_MAX: {
313                         unsigned long long ull;
314
315                         if (safe_atollu(optarg, &ull) < 0 || ull <= 0) {
316                                 log_error("Failed to parse maximum file size %s.", optarg);
317                                 return -EINVAL;
318                         }
319
320                         arg_file_size_max = (off_t) ull;
321                         break;
322                 }
323
324                 case '?':
325                         return -EINVAL;
326
327                 default:
328                         log_error("Unknown option code %c", c);
329                         return -EINVAL;
330                 }
331         }
332
333         if (optind != argc &&
334             optind != argc-1) {
335                 help();
336                 return -EINVAL;
337         }
338
339         return 1;
340 }
341
342 int main(int argc, char*argv[]) {
343         int r;
344         const char *root;
345
346         log_set_target(LOG_TARGET_AUTO);
347         log_parse_environment();
348         log_open();
349
350         umask(0022);
351
352         r = parse_argv(argc, argv);
353         if (r <= 0)
354                 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
355
356         root = optind < argc ? argv[optind] : "/";
357
358         if (!enough_ram()) {
359                 log_info("Disabling readahead replay due to low memory.");
360                 return 0;
361         }
362
363         shared = shared_get();
364         if (!shared)
365                 return 1;
366
367         shared->replay = getpid();
368         __sync_synchronize();
369
370         if (replay(root) < 0)
371                 return 1;
372
373         return 0;
374 }