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