1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2010 Lennart Poettering
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.
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.
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/>.
25 #include <sys/sysinfo.h>
26 #include <sys/inotify.h>
32 #include "readahead-common.h"
37 #include "udev-util.h"
39 int file_verify(int fd, const char *fn, off_t file_size_max, struct stat *st) {
44 if (fstat(fd, st) < 0) {
45 log_warning("fstat(%s) failed: %m", fn);
49 if (!S_ISREG(st->st_mode)) {
50 log_debug("Not preloading special file %s", fn);
54 if (st->st_size <= 0 || st->st_size > file_size_max) {
55 log_debug("Not preloading file %s with size out of bounds %llu", fn, (unsigned long long) st->st_size);
62 int fs_on_ssd(const char *p) {
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;
67 const char *devtype, *rotational, *model, *id;
75 if (major(st.st_dev) == 0) {
76 _cleanup_fclose_ FILE *f = NULL;
78 struct file_handle *h;
80 /* Might be btrfs, which exposes "ssd" as mount flag if it is on ssd.
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. */
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);
92 f = fopen("/proc/self/mountinfo", "re");
97 char line[LINE_MAX], *e;
98 _cleanup_free_ char *opts = NULL;
101 if (!fgets(line, sizeof(line), f))
106 if (sscanf(line, "%i", &mid) != 1)
112 e = strstr(line, " - ");
116 if (sscanf(e+3, "%*s %*s %ms", &opts) != 1)
119 if (streq(opts, "ssd") || startswith(opts, "ssd,") || endswith(opts, ",ssd") || strstr(opts, ",ssd,"))
130 udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev);
134 devtype = udev_device_get_property_value(udev_device, "DEVTYPE");
135 if (devtype && streq(devtype, "partition"))
136 look_at = udev_device_get_parent(udev_device);
138 look_at = udev_device;
143 /* First, try high-level property */
144 id = udev_device_get_property_value(look_at, "ID_SSD");
146 return streq(id, "1");
148 /* Second, try kernel attribute */
149 rotational = udev_device_get_sysattr_value(look_at, "queue/rotational");
151 return streq(rotational, "0");
153 /* Finally, fallback to heuristics */
154 look_at = udev_device_get_parent(look_at);
158 model = udev_device_get_sysattr_value(look_at, "model");
160 return !!strstr(model, "SSD");
165 int fs_on_read_only(const char *p) {
167 _cleanup_udev_unref_ struct udev *udev = NULL;
168 _cleanup_udev_device_unref_ struct udev_device *udev_device = NULL;
169 const char *read_only;
173 if (stat(p, &st) < 0)
176 if (major(st.st_dev) == 0)
183 udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev);
187 read_only = udev_device_get_sysattr_value(udev_device, "ro");
189 return streq(read_only, "1");
194 bool enough_ram(void) {
197 assert_se(sysinfo(&si) >= 0);
199 /* Enable readahead only with at least 128MB memory */
200 return si.totalram > 127 * 1024*1024 / si.mem_unit;
203 static 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");
210 int open_inotify(void) {
213 fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
215 log_error("Failed to create inotify handle: %m");
221 if (inotify_add_watch(fd, "/run/systemd/readahead", IN_CREATE) < 0) {
222 log_error("Failed to watch /run/systemd/readahead: %m");
230 ReadaheadShared *shared_get(void) {
231 int _cleanup_close_ fd = -1;
232 ReadaheadShared *m = NULL;
236 fd = open("/run/systemd/readahead/shared", O_CREAT|O_RDWR|O_CLOEXEC, 0644);
238 log_error("Failed to create shared memory segment: %m");
242 if (ftruncate(fd, sizeof(ReadaheadShared)) < 0) {
243 log_error("Failed to truncate shared memory segment: %m");
247 m = mmap(NULL, sizeof(ReadaheadShared), PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);
248 if (m == MAP_FAILED) {
249 log_error("Failed to mmap shared memory segment: %m");
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)
262 int block_bump_request_nr(const char *p) {
265 char *ap = NULL, *line = NULL;
271 if (stat(p, &st) < 0)
274 if (major(st.st_dev) == 0)
278 block_get_whole_disk(d, &d);
280 if (asprintf(&ap, "/sys/dev/block/%u:%u/queue/nr_requests", major(d), minor(d)) < 0) {
285 r = read_one_line_file(ap, &line);
292 r = safe_atou64(line, &u);
293 if (r >= 0 && u >= BUMP_REQUEST_NR) {
301 if (asprintf(&line, "%lu", (unsigned long) BUMP_REQUEST_NR) < 0) {
306 r = write_string_file(ap, line);
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);
320 int block_get_readahead(const char *p, uint64_t *bytes) {
322 char *ap = NULL, *line = NULL;
330 if (stat(p, &st) < 0)
333 if (major(st.st_dev) == 0)
337 block_get_whole_disk(d, &d);
339 if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
344 r = read_one_line_file(ap, &line);
348 r = safe_atou64(line, &u);
352 *bytes = u * 1024ULL;
361 int block_set_readahead(const char *p, uint64_t bytes) {
363 char *ap = NULL, *line = NULL;
370 if (stat(p, &st) < 0)
373 if (major(st.st_dev) == 0)
377 block_get_whole_disk(d, &d);
379 if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
384 if (asprintf(&line, "%llu", (unsigned long long) bytes / 1024ULL) < 0) {
389 r = write_string_file(ap, line);