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