chiark / gitweb /
Revert "log_error() if inotify_add_watch() fails"
[elogind.git] / src / readahead / readahead-common.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 <stdlib.h>
24 #include <string.h>
25 #include <sys/sysinfo.h>
26 #include <sys/inotify.h>
27 #include <fcntl.h>
28 #include <sys/mman.h>
29 #include <unistd.h>
30 #include <libudev.h>
31
32 #include "log.h"
33 #include "readahead-common.h"
34 #include "util.h"
35 #include "missing.h"
36
37 int file_verify(int fd, const char *fn, off_t file_size_max, struct stat *st) {
38         assert(fd >= 0);
39         assert(fn);
40         assert(st);
41
42         if (fstat(fd, st) < 0) {
43                 log_warning("fstat(%s) failed: %m", fn);
44                 return -errno;
45         }
46
47         if (!S_ISREG(st->st_mode)) {
48                 log_debug("Not preloading special file %s", fn);
49                 return 0;
50         }
51
52         if (st->st_size <= 0 || st->st_size > file_size_max) {
53                 log_debug("Not preloading file %s with size out of bounds %llu", fn, (unsigned long long) st->st_size);
54                 return 0;
55         }
56
57         return 1;
58 }
59
60 int fs_on_ssd(const char *p) {
61         struct stat st;
62         struct udev *udev = NULL;
63         struct udev_device *udev_device = NULL, *look_at = NULL;
64         bool b = false;
65         const char *devtype, *rotational, *model, *id;
66         int r;
67
68         assert(p);
69
70         if (stat(p, &st) < 0)
71                 return -errno;
72
73         if (major(st.st_dev) == 0) {
74                 _cleanup_fclose_ FILE *f = NULL;
75                 int mount_id;
76                 struct file_handle *h;
77
78                 /* Might be btrfs, which exposes "ssd" as mount flag if it is on ssd.
79                  *
80                  * We first determine the mount ID here, if we can,
81                  * and then lookup the mount ID in mountinfo to find
82                  * the mount options. */
83
84                 h = alloca(MAX_HANDLE_SZ);
85                 h->handle_bytes = MAX_HANDLE_SZ;
86                 r = name_to_handle_at(AT_FDCWD, p, h, &mount_id, AT_SYMLINK_FOLLOW);
87                 if (r < 0)
88                         return false;
89
90                 f = fopen("/proc/self/mountinfo", "re");
91                 if (!f)
92                         return false;
93
94                 for (;;) {
95                         char line[LINE_MAX], *e;
96                         _cleanup_free_ char *opts = NULL;
97                         int mid;
98
99                         if (!fgets(line, sizeof(line), f))
100                                 return false;
101
102                         truncate_nl(line);
103
104                         if (sscanf(line, "%i", &mid) != 1)
105                                 continue;
106
107                         if (mid != mount_id)
108                                 continue;
109
110                         e = strstr(line, " - ");
111                         if (!e)
112                                 continue;
113
114                         if (sscanf(e+3, "%*s %*s %ms", &opts) != 1)
115                                 continue;
116
117                         if (streq(opts, "ssd") || startswith(opts, "ssd,") || endswith(opts, ",ssd") || strstr(opts, ",ssd,"))
118                                 return true;
119                 }
120
121                 return false;
122         }
123
124         udev = udev_new();
125         if (!udev)
126                 return -ENOMEM;
127
128         udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev);
129         if (!udev_device)
130                 goto finish;
131
132         devtype = udev_device_get_property_value(udev_device, "DEVTYPE");
133         if (devtype && streq(devtype, "partition"))
134                 look_at = udev_device_get_parent(udev_device);
135         else
136                 look_at = udev_device;
137
138         if (!look_at)
139                 goto finish;
140
141         /* First, try high-level property */
142         id = udev_device_get_property_value(look_at, "ID_SSD");
143         if (id) {
144                 b = streq(id, "1");
145                 goto finish;
146         }
147
148         /* Second, try kernel attribute */
149         rotational = udev_device_get_sysattr_value(look_at, "queue/rotational");
150         if (rotational) {
151                 b = streq(rotational, "0");
152                 goto finish;
153         }
154
155         /* Finally, fallback to heuristics */
156         look_at = udev_device_get_parent(look_at);
157         if (!look_at)
158                 goto finish;
159
160         model = udev_device_get_sysattr_value(look_at, "model");
161         if (model)
162                 b = !!strstr(model, "SSD");
163
164 finish:
165         if (udev_device)
166                 udev_device_unref(udev_device);
167
168         if (udev)
169                 udev_unref(udev);
170
171         return b;
172 }
173
174 int fs_on_read_only(const char *p) {
175         struct stat st;
176         struct udev *udev = NULL;
177         struct udev_device *udev_device = NULL;
178         bool b = false;
179         const char *read_only;
180
181         assert(p);
182
183         if (stat(p, &st) < 0)
184                 return -errno;
185
186         if (major(st.st_dev) == 0)
187                 return false;
188
189         if (!(udev = udev_new()))
190                 return -ENOMEM;
191
192         if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev)))
193                 goto finish;
194
195         if ((read_only = udev_device_get_sysattr_value(udev_device, "ro")))
196                 if ((b = streq(read_only, "1")))
197                         goto finish;
198
199 finish:
200         if (udev_device)
201                 udev_device_unref(udev_device);
202
203         if (udev)
204                 udev_unref(udev);
205
206         return b;
207 }
208
209 bool enough_ram(void) {
210         struct sysinfo si;
211
212         assert_se(sysinfo(&si) >= 0);
213
214         /* Enable readahead only with at least 128MB memory */
215         return si.totalram > 127 * 1024*1024 / si.mem_unit;
216 }
217
218 int open_inotify(void) {
219         int fd;
220
221         if ((fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
222                 log_error("Failed to create inotify handle: %m");
223                 return -errno;
224         }
225
226         mkdir("/run/systemd", 0755);
227         mkdir("/run/systemd/readahead", 0755);
228
229         if (inotify_add_watch(fd, "/run/systemd/readahead", IN_CREATE) < 0) {
230                 log_error("Failed to watch /run/systemd/readahead: %m");
231                 close_nointr_nofail(fd);
232                 return -errno;
233         }
234
235         return fd;
236 }
237
238 ReadaheadShared *shared_get(void) {
239         int fd;
240         ReadaheadShared *m = NULL;
241
242         mkdir("/run/systemd", 0755);
243         mkdir("/run/systemd/readahead", 0755);
244
245         if ((fd = open("/run/systemd/readahead/shared", O_CREAT|O_RDWR|O_CLOEXEC, 0644)) < 0) {
246                 log_error("Failed to create shared memory segment: %m");
247                 goto finish;
248         }
249
250         if (ftruncate(fd, sizeof(ReadaheadShared)) < 0) {
251                 log_error("Failed to truncate shared memory segment: %m");
252                 goto finish;
253         }
254
255         if ((m = mmap(NULL, sizeof(ReadaheadShared), PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
256                 log_error("Failed to mmap shared memory segment: %m");
257                 m = NULL;
258                 goto finish;
259         }
260
261 finish:
262         if (fd >= 0)
263                 close_nointr_nofail(fd);
264
265         return m;
266 }
267
268 /* We use 20K instead of the more human digestable 16K here. Why?
269    Simply so that it is more unlikely that users end up picking this
270    value too so that we can recognize better whether the user changed
271    the value while we had it temporarily bumped. */
272 #define BUMP_REQUEST_NR (20*1024)
273
274 int block_bump_request_nr(const char *p) {
275         struct stat st;
276         uint64_t u;
277         char *ap = NULL, *line = NULL;
278         int r;
279         dev_t d;
280
281         assert(p);
282
283         if (stat(p, &st) < 0)
284                 return -errno;
285
286         if (major(st.st_dev) == 0)
287                 return 0;
288
289         d = st.st_dev;
290         block_get_whole_disk(d, &d);
291
292         if (asprintf(&ap, "/sys/dev/block/%u:%u/queue/nr_requests", major(d), minor(d)) < 0) {
293                 r= -ENOMEM;
294                 goto finish;
295         }
296
297         r = read_one_line_file(ap, &line);
298         if (r < 0) {
299                 if (r == -ENOENT)
300                         r = 0;
301                 goto finish;
302         }
303
304         r = safe_atou64(line, &u);
305         if (r >= 0 && u >= BUMP_REQUEST_NR) {
306                 r = 0;
307                 goto finish;
308         }
309
310         free(line);
311         line = NULL;
312
313         if (asprintf(&line, "%lu", (unsigned long) BUMP_REQUEST_NR) < 0) {
314                 r = -ENOMEM;
315                 goto finish;
316         }
317
318         r = write_one_line_file(ap, line);
319         if (r < 0)
320                 goto finish;
321
322         log_info("Bumped block_nr parameter of %u:%u to %lu. This is a temporary hack and should be removed one day.", major(d), minor(d), (unsigned long) BUMP_REQUEST_NR);
323         r = 1;
324
325 finish:
326         free(ap);
327         free(line);
328
329         return r;
330 }
331
332 int block_get_readahead(const char *p, uint64_t *bytes) {
333         struct stat st;
334         char *ap = NULL, *line = NULL;
335         int r;
336         dev_t d;
337         uint64_t u;
338
339         assert(p);
340         assert(bytes);
341
342         if (stat(p, &st) < 0)
343                 return -errno;
344
345         if (major(st.st_dev) == 0)
346                 return 0;
347
348         d = st.st_dev;
349         block_get_whole_disk(d, &d);
350
351         if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
352                 r = -ENOMEM;
353                 goto finish;
354         }
355
356         r = read_one_line_file(ap, &line);
357         if (r < 0)
358                 goto finish;
359
360         r = safe_atou64(line, &u);
361         if (r < 0)
362                 goto finish;
363
364         *bytes = u * 1024ULL;
365
366 finish:
367         free(ap);
368         free(line);
369
370         return r;
371 }
372
373 int block_set_readahead(const char *p, uint64_t bytes) {
374         struct stat st;
375         char *ap = NULL, *line = NULL;
376         int r;
377         dev_t d;
378
379         assert(p);
380         assert(bytes);
381
382         if (stat(p, &st) < 0)
383                 return -errno;
384
385         if (major(st.st_dev) == 0)
386                 return 0;
387
388         d = st.st_dev;
389         block_get_whole_disk(d, &d);
390
391         if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
392                 r = -ENOMEM;
393                 goto finish;
394         }
395
396         if (asprintf(&line, "%llu", (unsigned long long) bytes / 1024ULL) < 0) {
397                 r = -ENOMEM;
398                 goto finish;
399         }
400
401         r = write_one_line_file(ap, line);
402         if (r < 0)
403                 goto finish;
404
405 finish:
406         free(ap);
407         free(line);
408
409         return r;
410 }