chiark / gitweb /
event: make gcc shut up
[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 && errno != ELOOP)
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                 r = log_oom();
154                 goto finish;
155         }
156
157         pack = fopen(pack_fn, "re");
158         if (!pack) {
159                 if (errno == ENOENT)
160                         log_debug("No pack file found.");
161                 else {
162                         log_error("Failed to open pack file: %m");
163                         r = -errno;
164                 }
165
166                 goto finish;
167         }
168
169         posix_fadvise(fileno(pack), 0, 0, POSIX_FADV_WILLNEED);
170
171         if ((inotify_fd = open_inotify()) < 0) {
172                 r = inotify_fd;
173                 goto finish;
174         }
175
176         if (!(fgets(line, sizeof(line), pack))) {
177                 log_error("Premature end of pack file.");
178                 r = -EIO;
179                 goto finish;
180         }
181
182         char_array_0(line);
183
184         if (!streq(line, CANONICAL_HOST READAHEAD_PACK_FILE_VERSION)) {
185                 log_debug("Pack file host or version type mismatch.");
186                 goto finish;
187         }
188
189         if ((c = getc(pack)) == EOF) {
190                 log_debug("Premature end of pack file.");
191                 r = -EIO;
192                 goto finish;
193         }
194
195         /* We do not retest SSD here, so that we can start replaying
196          * before udev is up.*/
197         on_ssd = c == 'S';
198         log_debug("On SSD: %s", yes_no(on_ssd));
199
200         if (on_ssd)
201                 prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_IDLE, 0);
202         else
203                 /* We are not using RT here, since we'd starve IO that
204                 we didn't record (which is for example blkid, since
205                 its disk accesses go directly to the block device and
206                 are thus not visible in fallocate) to death. However,
207                 we do ask for an IO prio that is slightly higher than
208                 the default (which is BE. 4) */
209                 prio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_BE, 2);
210
211         if (ioprio_set(IOPRIO_WHO_PROCESS, getpid(), prio) < 0)
212                 log_warning("Failed to set IDLE IO priority class: %m");
213
214         sd_notify(0, "STATUS=Replaying readahead data");
215
216         log_debug("Replaying...");
217
218         if (access("/run/systemd/readahead/noreplay", F_OK) >= 0) {
219                 log_debug("Got termination request");
220                 goto done;
221         }
222
223         while (!feof(pack) && !ferror(pack)) {
224                 uint8_t inotify_buffer[sizeof(struct inotify_event) + FILENAME_MAX];
225                 int k;
226                 ssize_t n;
227
228                 if ((n = read(inotify_fd, &inotify_buffer, sizeof(inotify_buffer))) < 0) {
229                         if (errno != EINTR && errno != EAGAIN) {
230                                 log_error("Failed to read inotify event: %m");
231                                 r = -errno;
232                                 goto finish;
233                         }
234                 } else {
235                         struct inotify_event *e = (struct inotify_event*) inotify_buffer;
236
237                         while (n > 0) {
238                                 size_t step;
239
240                                 if ((e->mask & IN_CREATE) && streq(e->name, "noreplay")) {
241                                         log_debug("Got termination request");
242                                         goto done;
243                                 }
244
245                                 step = sizeof(struct inotify_event) + e->len;
246                                 assert(step <= (size_t) n);
247
248                                 e = (struct inotify_event*) ((uint8_t*) e + step);
249                                 n -= step;
250                         }
251                 }
252
253                 if ((k = unpack_file(pack)) < 0) {
254                         r = k;
255                         goto finish;
256                 }
257
258                 if (!ready) {
259                         /* We delay the ready notification until we
260                          * queued at least one read */
261                         sd_notify(0, "READY=1");
262                         ready = true;
263                 }
264         }
265
266 done:
267         if (!ready)
268                 sd_notify(0, "READY=1");
269
270         if (ferror(pack)) {
271                 log_error("Failed to read pack file.");
272                 r = -EIO;
273                 goto finish;
274         }
275
276         log_debug("Done.");
277
278 finish:
279         if (pack)
280                 fclose(pack);
281
282         if (inotify_fd >= 0)
283                 close_nointr_nofail(inotify_fd);
284
285         free(pack_fn);
286
287         return r;
288 }
289
290 int main_replay(const char *root) {
291
292         if (!root)
293                 root = "/";
294
295         if (!enough_ram()) {
296                 log_info("Disabling readahead replay due to low memory.");
297                 return EXIT_SUCCESS;
298         }
299
300         shared = shared_get();
301         if (!shared)
302                 return EXIT_FAILURE;
303
304         shared->replay = getpid();
305         __sync_synchronize();
306
307         if (replay(root) < 0)
308                 return EXIT_FAILURE;
309
310         return EXIT_SUCCESS;
311 }