]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/readahead/readahead-common.c
readhead: temporarily lower the kernel's read_ahead_kb setting while collecting
[thirdparty/systemd.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 if (!(udev = udev_new()))
75 return -ENOMEM;
76
77 if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev)))
78 goto finish;
79
80 if ((devtype = udev_device_get_property_value(udev_device, "DEVTYPE")) &&
81 streq(devtype, "partition"))
82 look_at = udev_device_get_parent(udev_device);
83 else
84 look_at = udev_device;
85
86 if (!look_at)
87 goto finish;
88
89 /* First, try high-level property */
90 if ((id = udev_device_get_property_value(look_at, "ID_SSD"))) {
91 b = streq(id, "1");
92 goto finish;
93 }
94
95 /* Second, try kernel attribute */
96 if ((rotational = udev_device_get_sysattr_value(look_at, "queue/rotational")))
97 if ((b = streq(rotational, "0")))
98 goto finish;
99
100 /* Finally, fallback to heuristics */
101 if (!(look_at = udev_device_get_parent(look_at)))
102 goto finish;
103
104 if ((model = udev_device_get_sysattr_value(look_at, "model")))
105 b = !!strstr(model, "SSD");
106
107 finish:
108 if (udev_device)
109 udev_device_unref(udev_device);
110
111 if (udev)
112 udev_unref(udev);
113
114 return b;
115 }
116
117 int fs_on_read_only(const char *p) {
118 struct stat st;
119 struct udev *udev = NULL;
120 struct udev_device *udev_device = NULL;
121 bool b = false;
122 const char *read_only;
123
124 assert(p);
125
126 if (stat(p, &st) < 0)
127 return -errno;
128
129 if (major(st.st_dev) == 0)
130 return false;
131
132 if (!(udev = udev_new()))
133 return -ENOMEM;
134
135 if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev)))
136 goto finish;
137
138 if ((read_only = udev_device_get_sysattr_value(udev_device, "ro")))
139 if ((b = streq(read_only, "1")))
140 goto finish;
141
142 finish:
143 if (udev_device)
144 udev_device_unref(udev_device);
145
146 if (udev)
147 udev_unref(udev);
148
149 return b;
150 }
151
152 bool enough_ram(void) {
153 struct sysinfo si;
154
155 assert_se(sysinfo(&si) >= 0);
156
157 /* Enable readahead only with at least 128MB memory */
158 return si.totalram > 127 * 1024*1024 / si.mem_unit;
159 }
160
161 int open_inotify(void) {
162 int fd;
163
164 if ((fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
165 log_error("Failed to create inotify handle: %m");
166 return -errno;
167 }
168
169 mkdir("/run/systemd", 0755);
170 mkdir("/run/systemd/readahead", 0755);
171
172 if (inotify_add_watch(fd, "/run/systemd/readahead", IN_CREATE) < 0) {
173 log_error("Failed to watch /run/systemd/readahead: %m");
174 close_nointr_nofail(fd);
175 return -errno;
176 }
177
178 return fd;
179 }
180
181 ReadaheadShared *shared_get(void) {
182 int fd;
183 ReadaheadShared *m = NULL;
184
185 mkdir("/run/systemd", 0755);
186 mkdir("/run/systemd/readahead", 0755);
187
188 if ((fd = open("/run/systemd/readahead/shared", O_CREAT|O_RDWR|O_CLOEXEC, 0644)) < 0) {
189 log_error("Failed to create shared memory segment: %m");
190 goto finish;
191 }
192
193 if (ftruncate(fd, sizeof(ReadaheadShared)) < 0) {
194 log_error("Failed to truncate shared memory segment: %m");
195 goto finish;
196 }
197
198 if ((m = mmap(NULL, sizeof(ReadaheadShared), PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
199 log_error("Failed to mmap shared memory segment: %m");
200 m = NULL;
201 goto finish;
202 }
203
204 finish:
205 if (fd >= 0)
206 close_nointr_nofail(fd);
207
208 return m;
209 }
210
211 #define BUMP_REQUEST_NR (16*1024)
212
213 int block_bump_request_nr(const char *p) {
214 struct stat st;
215 uint64_t u;
216 char *ap = NULL, *line = NULL;
217 int r;
218 dev_t d;
219
220 assert(p);
221
222 if (stat(p, &st) < 0)
223 return -errno;
224
225 if (major(st.st_dev) == 0)
226 return 0;
227
228 d = st.st_dev;
229 block_get_whole_disk(d, &d);
230
231 if (asprintf(&ap, "/sys/dev/block/%u:%u/queue/nr_requests", major(d), minor(d)) < 0) {
232 r= -ENOMEM;
233 goto finish;
234 }
235
236 r = read_one_line_file(ap, &line);
237 if (r < 0) {
238 if (r == -ENOENT)
239 r = 0;
240 goto finish;
241 }
242
243 r = safe_atou64(line, &u);
244 if (r >= 0 && u >= BUMP_REQUEST_NR) {
245 r = 0;
246 goto finish;
247 }
248
249 free(line);
250 line = NULL;
251
252 if (asprintf(&line, "%lu", (unsigned long) BUMP_REQUEST_NR) < 0) {
253 r = -ENOMEM;
254 goto finish;
255 }
256
257 r = write_one_line_file(ap, line);
258 if (r < 0)
259 goto finish;
260
261 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);
262 r = 1;
263
264 finish:
265 free(ap);
266 free(line);
267
268 return r;
269 }
270
271 int block_get_readahead(const char *p, uint64_t *bytes) {
272 struct stat st;
273 char *ap = NULL, *line = NULL;
274 int r;
275 dev_t d;
276 uint64_t u;
277
278 assert(p);
279 assert(bytes);
280
281 if (stat(p, &st) < 0)
282 return -errno;
283
284 if (major(st.st_dev) == 0)
285 return 0;
286
287 d = st.st_dev;
288 block_get_whole_disk(d, &d);
289
290 if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
291 r = -ENOMEM;
292 goto finish;
293 }
294
295 r = read_one_line_file(ap, &line);
296 if (r < 0)
297 goto finish;
298
299 r = safe_atou64(line, &u);
300 if (r < 0)
301 goto finish;
302
303 *bytes = u * 1024ULL;
304
305 finish:
306 free(ap);
307 free(line);
308
309 return r;
310 }
311
312 int block_set_readahead(const char *p, uint64_t bytes) {
313 struct stat st;
314 char *ap = NULL, *line = NULL;
315 int r;
316 dev_t d;
317
318 assert(p);
319 assert(bytes);
320
321 if (stat(p, &st) < 0)
322 return -errno;
323
324 if (major(st.st_dev) == 0)
325 return 0;
326
327 d = st.st_dev;
328 block_get_whole_disk(d, &d);
329
330 if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
331 r = -ENOMEM;
332 goto finish;
333 }
334
335 if (asprintf(&line, "%llu", (unsigned long long) bytes / 1024ULL) < 0) {
336 r = -ENOMEM;
337 goto finish;
338 }
339
340 r = write_one_line_file(ap, line);
341 if (r < 0)
342 goto finish;
343
344 finish:
345 free(ap);
346 free(line);
347
348 return r;
349 }