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