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