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 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.
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.
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/>.
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 block_bump_request_nr(root);
137 if (asprintf(&pack_fn, "%s/.readahead", root) < 0) {
138 log_error("Out of memory");
143 if ((!(pack = fopen(pack_fn, "re")))) {
145 log_debug("No pack file found.");
147 log_error("Failed to open pack file: %m");
154 posix_fadvise(fileno(pack), 0, 0, POSIX_FADV_WILLNEED);
156 if ((inotify_fd = open_inotify()) < 0) {
161 if (!(fgets(line, sizeof(line), pack))) {
162 log_error("Premature end of pack file.");
169 if (!streq(line, CANONICAL_HOST "\n")) {
170 log_debug("Pack file host type mismatch.");
174 if ((c = getc(pack)) == EOF) {
175 log_debug("Premature end of pack file.");
180 /* We do not retest SSD here, so that we can start replaying
181 * before udev is up.*/
183 log_debug("On SSD: %s", yes_no(on_ssd));
186 prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0);
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);
196 if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), prio) < 0)
197 log_warning("Failed to set IDLE IO priority class: %m");
199 sd_notify(0, "STATUS=Replaying readahead data");
201 log_debug("Replaying...");
203 if (access("/run/systemd/readahead/noreplay", F_OK) >= 0) {
204 log_debug("Got termination request");
208 while (!feof(pack) && !ferror(pack)) {
209 uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
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");
220 struct inotify_event *e = (struct inotify_event*) inotify_buffer;
225 if ((e->mask & IN_CREATE) && streq(e->name, "noreplay")) {
226 log_debug("Got termination request");
230 step = sizeof(struct inotify_event) + e->len;
231 assert(step <= (size_t) n);
233 e = (struct inotify_event*) ((uint8_t*) e + step);
238 if ((k = unpack_file(pack)) < 0) {
244 /* We delay the ready notification until we
245 * queued at least one read */
246 sd_notify(0, "READY=1");
253 sd_notify(0, "READY=1");
256 log_error("Failed to read pack file.");
268 close_nointr_nofail(inotify_fd);
276 static int help(void) {
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);
287 static int parse_argv(int argc, char *argv[]) {
293 static const struct option options[] = {
294 { "help", no_argument, NULL, 'h' },
295 { "file-size-max", required_argument, NULL, ARG_FILE_SIZE_MAX },
304 while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
312 case ARG_FILE_SIZE_MAX: {
313 unsigned long long ull;
315 if (safe_atollu(optarg, &ull) < 0 || ull <= 0) {
316 log_error("Failed to parse maximum file size %s.", optarg);
320 arg_file_size_max = (off_t) ull;
328 log_error("Unknown option code %c", c);
333 if (optind != argc &&
342 int main(int argc, char*argv[]) {
346 log_set_target(LOG_TARGET_AUTO);
347 log_parse_environment();
352 r = parse_argv(argc, argv);
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 shared = shared_get();
367 shared->replay = getpid();
368 __sync_synchronize();
370 if (replay(root) < 0)