1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
25 #include <linux/limits.h>
30 #include <sys/select.h>
32 #include <sys/types.h>
36 #include <sys/inotify.h>
38 #include <systemd/sd-daemon.h>
44 #include "readahead-common.h"
47 static off_t arg_file_size_max = READAHEAD_FILE_SIZE_MAX;
49 static ReadaheadShared *shared = NULL;
51 static int unpack_file(FILE *pack) {
59 if (!fgets(fn, sizeof(fn), pack))
65 if ((fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW)) < 0) {
67 if (errno != ENOENT && errno != EPERM && errno != EACCES)
68 log_warning("open(%s) failed: %m", fn);
70 } else if (file_verify(fd, fn, arg_file_size_max, &st) <= 0) {
71 close_nointr_nofail(fd);
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.");
89 log_error("Invalid pack file.");
94 log_debug("%s: page %u to %u", fn, b, c);
99 if (posix_fadvise(fd, b * page_size(), (c - b) * page_size(), POSIX_FADV_WILLNEED) < 0) {
100 log_warning("posix_fadvise() failed: %m");
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
110 if (posix_fadvise(fd, 0, st.st_size, POSIX_FADV_WILLNEED) < 0) {
111 log_warning("posix_fadvise() failed: %m");
118 close_nointr_nofail(fd);
123 static int replay(const char *root) {
127 char *pack_fn = NULL;
129 bool on_ssd, ready = false;
135 write_one_line_file("/proc/self/oom_score_adj", "1000");
136 bump_request_nr(root);
138 if (asprintf(&pack_fn, "%s/.readahead", root) < 0) {
139 log_error("Out of memory");
144 if ((!(pack = fopen(pack_fn, "re")))) {
146 log_debug("No pack file found.");
148 log_error("Failed to open pack file: %m");
155 posix_fadvise(fileno(pack), 0, 0, POSIX_FADV_WILLNEED);
157 if ((inotify_fd = open_inotify()) < 0) {
162 if (!(fgets(line, sizeof(line), pack))) {
163 log_error("Premature end of pack file.");
170 if (!streq(line, CANONICAL_HOST "\n")) {
171 log_debug("Pack file host type mismatch.");
175 if ((c = getc(pack)) == EOF) {
176 log_debug("Premature end of pack file.");
181 /* We do not retest SSD here, so that we can start replaying
182 * before udev is up.*/
184 log_debug("On SSD: %s", yes_no(on_ssd));
187 prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0);
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);
197 if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), prio) < 0)
198 log_warning("Failed to set IDLE IO priority class: %m");
200 sd_notify(0, "STATUS=Replaying readahead data");
202 log_debug("Replaying...");
204 if (access("/run/systemd/readahead/noreplay", F_OK) >= 0) {
205 log_debug("Got termination request");
209 while (!feof(pack) && !ferror(pack)) {
210 uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
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");
221 struct inotify_event *e = (struct inotify_event*) inotify_buffer;
226 if ((e->mask & IN_CREATE) && streq(e->name, "noreplay")) {
227 log_debug("Got termination request");
231 step = sizeof(struct inotify_event) + e->len;
232 assert(step <= (size_t) n);
234 e = (struct inotify_event*) ((uint8_t*) e + step);
239 if ((k = unpack_file(pack)) < 0) {
245 /* We delay the ready notification until we
246 * queued at least one read */
247 sd_notify(0, "READY=1");
254 sd_notify(0, "READY=1");
257 log_error("Failed to read pack file.");
269 close_nointr_nofail(inotify_fd);
277 static int help(void) {
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);
288 static int parse_argv(int argc, char *argv[]) {
294 static const struct option options[] = {
295 { "help", no_argument, NULL, 'h' },
296 { "file-size-max", required_argument, NULL, ARG_FILE_SIZE_MAX },
305 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
313 case ARG_FILE_SIZE_MAX: {
314 unsigned long long ull;
316 if (safe_atollu(optarg, &ull) < 0 || ull <= 0) {
317 log_error("Failed to parse maximum file size %s.", optarg);
321 arg_file_size_max = (off_t) ull;
329 log_error("Unknown option code %c", c);
334 if (optind != argc &&
343 int main(int argc, char*argv[]) {
347 log_set_target(LOG_TARGET_AUTO);
348 log_parse_environment();
353 if ((r = parse_argv(argc, argv)) <= 0)
354 return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
356 root = optind < argc ? argv[optind] : "/";
359 log_info("Disabling readahead replay due to low memory.");
363 if (detect_virtualization(NULL) > 0) {
364 log_info("Disabling readahead replay due to execution in virtualized environment.");
368 if (!(shared = shared_get()))
371 shared->replay = getpid();
372 __sync_synchronize();
374 if (replay(root) < 0)