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