]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/readahead/readahead-replay.c
readahead: Add tool to analyze the contents of the pack file. v3.
[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
5430f7f2
LP
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
22be093f
LP
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
5430f7f2 16 Lesser General Public License for more details.
22be093f 17
5430f7f2 18 You should have received a copy of the GNU Lesser General Public License
22be093f
LP
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;
189455ab 56 uint64_t inode;
22be093f
LP
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
189455ab
LP
66 fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW);
67 if (fd < 0) {
a78899f5 68
a76fad09 69 if (errno != ENOENT && errno != EPERM && errno != EACCES)
a78899f5
LP
70 log_warning("open(%s) failed: %m", fn);
71
72 } else if (file_verify(fd, fn, arg_file_size_max, &st) <= 0) {
22be093f
LP
73 close_nointr_nofail(fd);
74 fd = -1;
75 }
76
189455ab
LP
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
22be093f
LP
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)
37f85e66 116 if (posix_fadvise(fd, b * page_size(), (c - b) * page_size(), POSIX_FADV_WILLNEED) < 0) {
6e66797a 117 log_warning("posix_fadvise() failed: %m");
22be093f
LP
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
6e66797a
HH
127 if (posix_fadvise(fd, 0, st.st_size, POSIX_FADV_WILLNEED) < 0) {
128 log_warning("posix_fadvise() failed: %m");
22be093f
LP
129 goto finish;
130 }
131 }
132
133finish:
134 if (fd >= 0)
135 close_nointr_nofail(fd);
136
137 return r;
138}
139
140static int replay(const char *root) {
858209c5 141 FILE *pack = NULL;
22be093f
LP
142 char line[LINE_MAX];
143 int r = 0;
da19d5c1
LP
144 char *pack_fn = NULL;
145 int c;
902a339c 146 bool on_ssd, ready = false;
22be093f 147 int prio;
6624768c 148 int inotify_fd = -1;
22be093f
LP
149
150 assert(root);
151
6de338a2 152 block_bump_request_nr(root);
75a010e0 153
22be093f
LP
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")))) {
a78899f5 161 if (errno == ENOENT)
22be093f
LP
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
bdb0e14d
LP
171 posix_fadvise(fileno(pack), 0, 0, POSIX_FADV_WILLNEED);
172
6624768c
LP
173 if ((inotify_fd = open_inotify()) < 0) {
174 r = inotify_fd;
175 goto finish;
176 }
177
22be093f
LP
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
cae544bc 186 if (!streq(line, CANONICAL_HOST READAHEAD_PACK_FILE_VERSION)) {
189455ab 187 log_debug("Pack file host or version type mismatch.");
22be093f
LP
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
05aa9edd
LP
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);
22be093f
LP
212
213 if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), prio) < 0)
214 log_warning("Failed to set IDLE IO priority class: %m");
215
902a339c 216 sd_notify(0, "STATUS=Replaying readahead data");
22be093f
LP
217
218 log_debug("Replaying...");
219
2b583ce6 220 if (access("/run/systemd/readahead/noreplay", F_OK) >= 0) {
6624768c
LP
221 log_debug("Got termination request");
222 goto done;
223 }
224
22be093f 225 while (!feof(pack) && !ferror(pack)) {
6624768c 226 uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
22be093f 227 int k;
6624768c
LP
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 }
22be093f
LP
254
255 if ((k = unpack_file(pack)) < 0) {
256 r = k;
257 goto finish;
258 }
902a339c
LP
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 }
22be093f
LP
266 }
267
6624768c 268done:
902a339c
LP
269 if (!ready)
270 sd_notify(0, "READY=1");
271
22be093f
LP
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
280finish:
281 if (pack)
282 fclose(pack);
283
6624768c
LP
284 if (inotify_fd >= 0)
285 close_nointr_nofail(inotify_fd);
286
22be093f
LP
287 free(pack_fn);
288
289 return r;
290}
291
8260358d
LP
292
293static 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
304static 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
22be093f 359int main(int argc, char*argv[]) {
8260358d 360 int r;
2b590e13 361 const char *root;
4030d7a9 362
e905f48f 363 log_set_target(LOG_TARGET_SAFE);
22be093f
LP
364 log_parse_environment();
365 log_open();
366
4c12626c
LP
367 umask(0022);
368
3b2d5b02
LP
369 r = parse_argv(argc, argv);
370 if (r <= 0)
8260358d
LP
371 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
372
2b590e13
LP
373 root = optind < argc ? argv[optind] : "/";
374
41a598e2
LP
375 if (!enough_ram()) {
376 log_info("Disabling readahead replay due to low memory.");
377 return 0;
378 }
379
3b2d5b02
LP
380 shared = shared_get();
381 if (!shared)
d9c7a87b
LP
382 return 1;
383
384 shared->replay = getpid();
385 __sync_synchronize();
386
2b590e13 387 if (replay(root) < 0)
22be093f
LP
388 return 1;
389
390 return 0;
391}