chiark / gitweb /
macro.h: let F_TYPE_CMP() macro fail to compile, if second parameter is not const
[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 int open_inotify(void) {
220         int fd;
221
222         if ((fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
223                 log_error("Failed to create inotify handle: %m");
224                 return -errno;
225         }
226
227         mkdir("/run/systemd", 0755);
228         mkdir("/run/systemd/readahead", 0755);
229
230         if (inotify_add_watch(fd, "/run/systemd/readahead", IN_CREATE) < 0) {
231                 log_error("Failed to watch /run/systemd/readahead: %m");
232                 close_nointr_nofail(fd);
233                 return -errno;
234         }
235
236         return fd;
237 }
238
239 ReadaheadShared *shared_get(void) {
240         int fd;
241         ReadaheadShared *m = NULL;
242
243         mkdir("/run/systemd", 0755);
244         mkdir("/run/systemd/readahead", 0755);
245
246         if ((fd = open("/run/systemd/readahead/shared", O_CREAT|O_RDWR|O_CLOEXEC, 0644)) < 0) {
247                 log_error("Failed to create shared memory segment: %m");
248                 goto finish;
249         }
250
251         if (ftruncate(fd, sizeof(ReadaheadShared)) < 0) {
252                 log_error("Failed to truncate shared memory segment: %m");
253                 goto finish;
254         }
255
256         if ((m = mmap(NULL, sizeof(ReadaheadShared), PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
257                 log_error("Failed to mmap shared memory segment: %m");
258                 m = NULL;
259                 goto finish;
260         }
261
262 finish:
263         if (fd >= 0)
264                 close_nointr_nofail(fd);
265
266         return m;
267 }
268
269 /* We use 20K instead of the more human digestable 16K here. Why?
270    Simply so that it is more unlikely that users end up picking this
271    value too so that we can recognize better whether the user changed
272    the value while we had it temporarily bumped. */
273 #define BUMP_REQUEST_NR (20*1024)
274
275 int block_bump_request_nr(const char *p) {
276         struct stat st;
277         uint64_t u;
278         char *ap = NULL, *line = NULL;
279         int r;
280         dev_t d;
281
282         assert(p);
283
284         if (stat(p, &st) < 0)
285                 return -errno;
286
287         if (major(st.st_dev) == 0)
288                 return 0;
289
290         d = st.st_dev;
291         block_get_whole_disk(d, &d);
292
293         if (asprintf(&ap, "/sys/dev/block/%u:%u/queue/nr_requests", major(d), minor(d)) < 0) {
294                 r= -ENOMEM;
295                 goto finish;
296         }
297
298         r = read_one_line_file(ap, &line);
299         if (r < 0) {
300                 if (r == -ENOENT)
301                         r = 0;
302                 goto finish;
303         }
304
305         r = safe_atou64(line, &u);
306         if (r >= 0 && u >= BUMP_REQUEST_NR) {
307                 r = 0;
308                 goto finish;
309         }
310
311         free(line);
312         line = NULL;
313
314         if (asprintf(&line, "%lu", (unsigned long) BUMP_REQUEST_NR) < 0) {
315                 r = -ENOMEM;
316                 goto finish;
317         }
318
319         r = write_string_file(ap, line);
320         if (r < 0)
321                 goto finish;
322
323         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);
324         r = 1;
325
326 finish:
327         free(ap);
328         free(line);
329
330         return r;
331 }
332
333 int block_get_readahead(const char *p, uint64_t *bytes) {
334         struct stat st;
335         char *ap = NULL, *line = NULL;
336         int r;
337         dev_t d;
338         uint64_t u;
339
340         assert(p);
341         assert(bytes);
342
343         if (stat(p, &st) < 0)
344                 return -errno;
345
346         if (major(st.st_dev) == 0)
347                 return 0;
348
349         d = st.st_dev;
350         block_get_whole_disk(d, &d);
351
352         if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
353                 r = -ENOMEM;
354                 goto finish;
355         }
356
357         r = read_one_line_file(ap, &line);
358         if (r < 0)
359                 goto finish;
360
361         r = safe_atou64(line, &u);
362         if (r < 0)
363                 goto finish;
364
365         *bytes = u * 1024ULL;
366
367 finish:
368         free(ap);
369         free(line);
370
371         return r;
372 }
373
374 int block_set_readahead(const char *p, uint64_t bytes) {
375         struct stat st;
376         char *ap = NULL, *line = NULL;
377         int r;
378         dev_t d;
379
380         assert(p);
381         assert(bytes);
382
383         if (stat(p, &st) < 0)
384                 return -errno;
385
386         if (major(st.st_dev) == 0)
387                 return 0;
388
389         d = st.st_dev;
390         block_get_whole_disk(d, &d);
391
392         if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
393                 r = -ENOMEM;
394                 goto finish;
395         }
396
397         if (asprintf(&line, "%llu", (unsigned long long) bytes / 1024ULL) < 0) {
398                 r = -ENOMEM;
399                 goto finish;
400         }
401
402         r = write_string_file(ap, line);
403         if (r < 0)
404                 goto finish;
405
406 finish:
407         free(ap);
408         free(line);
409
410         return r;
411 }