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