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 %zu", fn, 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 union file_handle_union h = { .handle.handle_bytes = MAX_HANDLE_SZ, };
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 r = name_to_handle_at(AT_FDCWD, p, &h.handle, &mount_id, AT_SYMLINK_FOLLOW);
90 f = fopen("/proc/self/mountinfo", "re");
95 char line[LINE_MAX], *e;
96 _cleanup_free_ char *opts = NULL;
99 if (!fgets(line, sizeof(line), f))
104 if (sscanf(line, "%i", &mid) != 1)
110 e = strstr(line, " - ");
114 if (sscanf(e+3, "%*s %*s %ms", &opts) != 1)
117 if (streq(opts, "ssd") || startswith(opts, "ssd,") || endswith(opts, ",ssd") || strstr(opts, ",ssd,"))
128 udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev);
132 devtype = udev_device_get_property_value(udev_device, "DEVTYPE");
133 if (devtype && streq(devtype, "partition"))
134 look_at = udev_device_get_parent(udev_device);
136 look_at = udev_device;
141 /* First, try high-level property */
142 id = udev_device_get_property_value(look_at, "ID_SSD");
144 return streq(id, "1");
146 /* Second, try kernel attribute */
147 rotational = udev_device_get_sysattr_value(look_at, "queue/rotational");
149 return streq(rotational, "0");
151 /* Finally, fallback to heuristics */
152 look_at = udev_device_get_parent(look_at);
156 model = udev_device_get_sysattr_value(look_at, "model");
158 return !!strstr(model, "SSD");
163 int fs_on_read_only(const char *p) {
165 _cleanup_udev_unref_ struct udev *udev = NULL;
166 _cleanup_udev_device_unref_ struct udev_device *udev_device = NULL;
167 const char *read_only;
171 if (stat(p, &st) < 0)
174 if (major(st.st_dev) == 0)
181 udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev);
185 read_only = udev_device_get_sysattr_value(udev_device, "ro");
187 return streq(read_only, "1");
192 bool enough_ram(void) {
195 assert_se(sysinfo(&si) >= 0);
197 /* Enable readahead only with at least 128MB memory */
198 return si.totalram > 127 * 1024*1024 / si.mem_unit;
201 static void mkdirs(void) {
202 if (mkdir("/run/systemd", 0755) && errno != EEXIST)
203 log_warning("Failed to create /run/systemd: %m");
204 if (mkdir("/run/systemd/readahead", 0755) && errno != EEXIST)
205 log_warning("Failed to create /run/systemd: %m");
208 int open_inotify(void) {
211 fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
213 log_error("Failed to create inotify handle: %m");
219 if (inotify_add_watch(fd, "/run/systemd/readahead", IN_CREATE) < 0) {
220 log_error("Failed to watch /run/systemd/readahead: %m");
228 ReadaheadShared *shared_get(void) {
229 int _cleanup_close_ fd = -1;
230 ReadaheadShared *m = NULL;
234 fd = open("/run/systemd/readahead/shared", O_CREAT|O_RDWR|O_CLOEXEC, 0644);
236 log_error("Failed to create shared memory segment: %m");
240 if (ftruncate(fd, sizeof(ReadaheadShared)) < 0) {
241 log_error("Failed to truncate shared memory segment: %m");
245 m = mmap(NULL, sizeof(ReadaheadShared), PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);
246 if (m == MAP_FAILED) {
247 log_error("Failed to mmap shared memory segment: %m");
254 /* We use 20K instead of the more human digestable 16K here. Why?
255 Simply so that it is more unlikely that users end up picking this
256 value too so that we can recognize better whether the user changed
257 the value while we had it temporarily bumped. */
258 #define BUMP_REQUEST_NR (20*1024u)
260 int block_bump_request_nr(const char *p) {
263 char *ap = NULL, *line = NULL;
269 if (stat(p, &st) < 0)
272 if (major(st.st_dev) == 0)
276 block_get_whole_disk(d, &d);
278 if (asprintf(&ap, "/sys/dev/block/%u:%u/queue/nr_requests", major(d), minor(d)) < 0) {
283 r = read_one_line_file(ap, &line);
290 r = safe_atou64(line, &u);
291 if (r >= 0 && u >= BUMP_REQUEST_NR) {
299 if (asprintf(&line, "%u", BUMP_REQUEST_NR) < 0) {
304 r = write_string_file(ap, line);
308 log_info("Bumped block_nr parameter of %u:%u to %u. This is a temporary hack and should be removed one day.", major(d), minor(d), BUMP_REQUEST_NR);
318 int block_get_readahead(const char *p, uint64_t *bytes) {
320 char *ap = NULL, *line = NULL;
328 if (stat(p, &st) < 0)
331 if (major(st.st_dev) == 0)
335 block_get_whole_disk(d, &d);
337 if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
342 r = read_one_line_file(ap, &line);
346 r = safe_atou64(line, &u);
350 *bytes = u * 1024ULL;
359 int block_set_readahead(const char *p, uint64_t bytes) {
361 char *ap = NULL, *line = NULL;
368 if (stat(p, &st) < 0)
371 if (major(st.st_dev) == 0)
375 block_get_whole_disk(d, &d);
377 if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
382 if (asprintf(&line, "%llu", bytes / 1024ULL) < 0) {
387 r = write_string_file(ap, line);