chiark / gitweb /
7bd079aae591c5459617313401fd102ae282fcca
[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 ReadaheadShared *shared = NULL;
48
49 static int unpack_file(FILE *pack) {
50         char fn[PATH_MAX];
51         int r = 0, fd = -1;
52         bool any = false;
53         struct stat st;
54         uint64_t inode;
55
56         assert(pack);
57
58         if (!fgets(fn, sizeof(fn), pack))
59                 return 0;
60
61         char_array_0(fn);
62         truncate_nl(fn);
63
64         fd = open(fn, O_RDONLY|O_CLOEXEC|O_NOATIME|O_NOCTTY|O_NOFOLLOW);
65         if (fd < 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         if (fread(&inode, sizeof(inode), 1, pack) != 1) {
76                 log_error("Premature end of pack file.");
77                 r = -EIO;
78                 goto finish;
79         }
80
81         if (fd >= 0) {
82                 /* If the inode changed the file got deleted, so just
83                  * ignore this entry */
84                 if (st.st_ino != (uint64_t) inode) {
85                         close_nointr_nofail(fd);
86                         fd = -1;
87                 }
88         }
89
90         for (;;) {
91                 uint32_t b, c;
92
93                 if (fread(&b, sizeof(b), 1, pack) != 1 ||
94                     fread(&c, sizeof(c), 1, pack) != 1) {
95                         log_error("Premature end of pack file.");
96                         r = -EIO;
97                         goto finish;
98                 }
99
100                 if (b == 0 && c == 0)
101                         break;
102
103                 if (c <= b) {
104                         log_error("Invalid pack file.");
105                         r = -EIO;
106                         goto finish;
107                 }
108
109                 log_debug("%s: page %u to %u", fn, b, c);
110
111                 any = true;
112
113                 if (fd >= 0)
114                         if (posix_fadvise(fd, b * page_size(), (c - b) * page_size(), POSIX_FADV_WILLNEED) < 0) {
115                                 log_warning("posix_fadvise() failed: %m");
116                                 goto finish;
117                         }
118         }
119
120         if (!any && fd >= 0) {
121                 /* if no range is encoded in the pack file this is
122                  * intended to mean that the whole file shall be
123                  * read */
124
125                 if (posix_fadvise(fd, 0, st.st_size, POSIX_FADV_WILLNEED) < 0) {
126                         log_warning("posix_fadvise() failed: %m");
127                         goto finish;
128                 }
129         }
130
131 finish:
132         if (fd >= 0)
133                 close_nointr_nofail(fd);
134
135         return r;
136 }
137
138 static int replay(const char *root) {
139         FILE *pack = NULL;
140         char line[LINE_MAX];
141         int r = 0;
142         char *pack_fn = NULL;
143         int c;
144         bool on_ssd, ready = false;
145         int prio;
146         int inotify_fd = -1;
147
148         assert(root);
149
150         block_bump_request_nr(root);
151
152         if (asprintf(&pack_fn, "%s/.readahead", root) < 0) {
153                 log_error("Out of memory");
154                 r = -ENOMEM;
155                 goto finish;
156         }
157
158         pack = fopen(pack_fn, "re");
159         if (!pack) {
160                 if (errno == ENOENT)
161                         log_debug("No pack file found.");
162                 else {
163                         log_error("Failed to open pack file: %m");
164                         r = -errno;
165                 }
166
167                 goto finish;
168         }
169
170         posix_fadvise(fileno(pack), 0, 0, POSIX_FADV_WILLNEED);
171
172         if ((inotify_fd = open_inotify()) < 0) {
173                 r = inotify_fd;
174                 goto finish;
175         }
176
177         if (!(fgets(line, sizeof(line), pack))) {
178                 log_error("Premature end of pack file.");
179                 r = -EIO;
180                 goto finish;
181         }
182
183         char_array_0(line);
184
185         if (!streq(line, CANONICAL_HOST READAHEAD_PACK_FILE_VERSION)) {
186                 log_debug("Pack file host or version type mismatch.");
187                 goto finish;
188         }
189
190         if ((c = getc(pack)) == EOF) {
191                 log_debug("Premature end of pack file.");
192                 r = -EIO;
193                 goto finish;
194         }
195
196         /* We do not retest SSD here, so that we can start replaying
197          * before udev is up.*/
198         on_ssd = c == 'S';
199         log_debug("On SSD: %s", yes_no(on_ssd));
200
201         if (on_ssd)
202                 prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0);
203         else
204                 /* We are not using RT here, since we'd starve IO that
205                 we didn't record (which is for example blkid, since
206                 its disk accesses go directly to the block device and
207                 are thus not visible in fallocate) to death. However,
208                 we do ask for an IO prio that is slightly higher than
209                 the default (which is BE. 4) */
210                 prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 2);
211
212         if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), prio) < 0)
213                 log_warning("Failed to set IDLE IO priority class: %m");
214
215         sd_notify(0, "STATUS=Replaying readahead data");
216
217         log_debug("Replaying...");
218
219         if (access("/run/systemd/readahead/noreplay", F_OK) >= 0) {
220                 log_debug("Got termination request");
221                 goto done;
222         }
223
224         while (!feof(pack) && !ferror(pack)) {
225                 uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
226                 int k;
227                 ssize_t n;
228
229                 if ((n = read(inotify_fd, &inotify_buffer, sizeof(inotify_buffer))) < 0) {
230                         if (errno != EINTR && errno != EAGAIN) {
231                                 log_error("Failed to read inotify event: %m");
232                                 r = -errno;
233                                 goto finish;
234                         }
235                 } else {
236                         struct inotify_event *e = (struct inotify_event*) inotify_buffer;
237
238                         while (n > 0) {
239                                 size_t step;
240
241                                 if ((e->mask & IN_CREATE) && streq(e->name, "noreplay")) {
242                                         log_debug("Got termination request");
243                                         goto done;
244                                 }
245
246                                 step = sizeof(struct inotify_event) + e->len;
247                                 assert(step <= (size_t) n);
248
249                                 e = (struct inotify_event*) ((uint8_t*) e + step);
250                                 n -= step;
251                         }
252                 }
253
254                 if ((k = unpack_file(pack)) < 0) {
255                         r = k;
256                         goto finish;
257                 }
258
259                 if (!ready) {
260                         /* We delay the ready notification until we
261                          * queued at least one read */
262                         sd_notify(0, "READY=1");
263                         ready = true;
264                 }
265         }
266
267 done:
268         if (!ready)
269                 sd_notify(0, "READY=1");
270
271         if (ferror(pack)) {
272                 log_error("Failed to read pack file.");
273                 r = -EIO;
274                 goto finish;
275         }
276
277         log_debug("Done.");
278
279 finish:
280         if (pack)
281                 fclose(pack);
282
283         if (inotify_fd >= 0)
284                 close_nointr_nofail(inotify_fd);
285
286         free(pack_fn);
287
288         return r;
289 }
290
291 int main_replay(const char *root) {
292
293         if (!root)
294                 root = "/";
295
296         if (!enough_ram()) {
297                 log_info("Disabling readahead replay due to low memory.");
298                 return EXIT_SUCCESS;
299         }
300
301         shared = shared_get();
302         if (!shared)
303                 return EXIT_FAILURE;
304
305         shared->replay = getpid();
306         __sync_synchronize();
307
308         if (replay(root) < 0)
309                 return EXIT_FAILURE;
310
311         return EXIT_SUCCESS;
312 }