chiark / gitweb /
readahead: a bit of reformatting
[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 #define BUMP_REQUEST_NR (16*1024)
218
219 int block_bump_request_nr(const char *p) {
220         struct stat st;
221         uint64_t u;
222         char *ap = NULL, *line = NULL;
223         int r;
224         dev_t d;
225
226         assert(p);
227
228         if (stat(p, &st) < 0)
229                 return -errno;
230
231         if (major(st.st_dev) == 0)
232                 return 0;
233
234         d = st.st_dev;
235         block_get_whole_disk(d, &d);
236
237         if (asprintf(&ap, "/sys/dev/block/%u:%u/queue/nr_requests", major(d), minor(d)) < 0) {
238                 r= -ENOMEM;
239                 goto finish;
240         }
241
242         r = read_one_line_file(ap, &line);
243         if (r < 0) {
244                 if (r == -ENOENT)
245                         r = 0;
246                 goto finish;
247         }
248
249         r = safe_atou64(line, &u);
250         if (r >= 0 && u >= BUMP_REQUEST_NR) {
251                 r = 0;
252                 goto finish;
253         }
254
255         free(line);
256         line = NULL;
257
258         if (asprintf(&line, "%lu", (unsigned long) BUMP_REQUEST_NR) < 0) {
259                 r = -ENOMEM;
260                 goto finish;
261         }
262
263         r = write_one_line_file(ap, line);
264         if (r < 0)
265                 goto finish;
266
267         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);
268         r = 1;
269
270 finish:
271         free(ap);
272         free(line);
273
274         return r;
275 }
276
277 int block_get_readahead(const char *p, uint64_t *bytes) {
278         struct stat st;
279         char *ap = NULL, *line = NULL;
280         int r;
281         dev_t d;
282         uint64_t u;
283
284         assert(p);
285         assert(bytes);
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/bdi/read_ahead_kb", 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                 goto finish;
304
305         r = safe_atou64(line, &u);
306         if (r < 0)
307                 goto finish;
308
309         *bytes = u * 1024ULL;
310
311 finish:
312         free(ap);
313         free(line);
314
315         return r;
316 }
317
318 int block_set_readahead(const char *p, uint64_t bytes) {
319         struct stat st;
320         char *ap = NULL, *line = NULL;
321         int r;
322         dev_t d;
323
324         assert(p);
325         assert(bytes);
326
327         if (stat(p, &st) < 0)
328                 return -errno;
329
330         if (major(st.st_dev) == 0)
331                 return 0;
332
333         d = st.st_dev;
334         block_get_whole_disk(d, &d);
335
336         if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
337                 r = -ENOMEM;
338                 goto finish;
339         }
340
341         if (asprintf(&line, "%llu", (unsigned long long) bytes / 1024ULL) < 0) {
342                 r = -ENOMEM;
343                 goto finish;
344         }
345
346         r = write_one_line_file(ap, line);
347         if (r < 0)
348                 goto finish;
349
350 finish:
351         free(ap);
352         free(line);
353
354         return r;
355 }