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>
33 #include "readahead-common.h"
38 int file_verify(int fd, const char *fn, off_t file_size_max, struct stat *st) {
43 if (fstat(fd, st) < 0) {
44 log_warning("fstat(%s) failed: %m", fn);
48 if (!S_ISREG(st->st_mode)) {
49 log_debug("Not preloading special file %s", fn);
53 if (st->st_size <= 0 || st->st_size > file_size_max) {
54 log_debug("Not preloading file %s with size out of bounds %llu", fn, (unsigned long long) st->st_size);
61 int fs_on_ssd(const char *p) {
63 struct udev *udev = NULL;
64 struct udev_device *udev_device = NULL, *look_at = NULL;
66 const char *devtype, *rotational, *model, *id;
74 if (major(st.st_dev) == 0) {
75 _cleanup_fclose_ FILE *f = NULL;
77 struct file_handle *h;
79 /* Might be btrfs, which exposes "ssd" as mount flag if it is on ssd.
81 * We first determine the mount ID here, if we can,
82 * and then lookup the mount ID in mountinfo to find
83 * the mount options. */
85 h = alloca(MAX_HANDLE_SZ);
86 h->handle_bytes = MAX_HANDLE_SZ;
87 r = name_to_handle_at(AT_FDCWD, p, h, &mount_id, AT_SYMLINK_FOLLOW);
91 f = fopen("/proc/self/mountinfo", "re");
96 char line[LINE_MAX], *e;
97 _cleanup_free_ char *opts = NULL;
100 if (!fgets(line, sizeof(line), f))
105 if (sscanf(line, "%i", &mid) != 1)
111 e = strstr(line, " - ");
115 if (sscanf(e+3, "%*s %*s %ms", &opts) != 1)
118 if (streq(opts, "ssd") || startswith(opts, "ssd,") || endswith(opts, ",ssd") || strstr(opts, ",ssd,"))
129 udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev);
133 devtype = udev_device_get_property_value(udev_device, "DEVTYPE");
134 if (devtype && streq(devtype, "partition"))
135 look_at = udev_device_get_parent(udev_device);
137 look_at = udev_device;
142 /* First, try high-level property */
143 id = udev_device_get_property_value(look_at, "ID_SSD");
149 /* Second, try kernel attribute */
150 rotational = udev_device_get_sysattr_value(look_at, "queue/rotational");
152 b = streq(rotational, "0");
156 /* Finally, fallback to heuristics */
157 look_at = udev_device_get_parent(look_at);
161 model = udev_device_get_sysattr_value(look_at, "model");
163 b = !!strstr(model, "SSD");
167 udev_device_unref(udev_device);
175 int fs_on_read_only(const char *p) {
177 struct udev *udev = NULL;
178 struct udev_device *udev_device = NULL;
180 const char *read_only;
184 if (stat(p, &st) < 0)
187 if (major(st.st_dev) == 0)
190 if (!(udev = udev_new()))
193 if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev)))
196 if ((read_only = udev_device_get_sysattr_value(udev_device, "ro")))
197 if ((b = streq(read_only, "1")))
202 udev_device_unref(udev_device);
210 bool enough_ram(void) {
213 assert_se(sysinfo(&si) >= 0);
215 /* Enable readahead only with at least 128MB memory */
216 return si.totalram > 127 * 1024*1024 / si.mem_unit;
219 static void mkdirs(void) {
220 if (mkdir("/run/systemd", 0755) && errno != EEXIST)
221 log_warning("Failed to create /run/systemd: %m");
222 if (mkdir("/run/systemd/readahead", 0755) && errno != EEXIST)
223 log_warning("Failed to create /run/systemd: %m");
226 int open_inotify(void) {
229 fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK);
231 log_error("Failed to create inotify handle: %m");
237 if (inotify_add_watch(fd, "/run/systemd/readahead", IN_CREATE) < 0) {
238 log_error("Failed to watch /run/systemd/readahead: %m");
239 close_nointr_nofail(fd);
246 ReadaheadShared *shared_get(void) {
247 int _cleanup_close_ fd = -1;
248 ReadaheadShared *m = NULL;
252 fd = open("/run/systemd/readahead/shared", O_CREAT|O_RDWR|O_CLOEXEC, 0644);
254 log_error("Failed to create shared memory segment: %m");
258 if (ftruncate(fd, sizeof(ReadaheadShared)) < 0) {
259 log_error("Failed to truncate shared memory segment: %m");
263 m = mmap(NULL, sizeof(ReadaheadShared), PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);
264 if (m == MAP_FAILED) {
265 log_error("Failed to mmap shared memory segment: %m");
272 /* We use 20K instead of the more human digestable 16K here. Why?
273 Simply so that it is more unlikely that users end up picking this
274 value too so that we can recognize better whether the user changed
275 the value while we had it temporarily bumped. */
276 #define BUMP_REQUEST_NR (20*1024)
278 int block_bump_request_nr(const char *p) {
281 char *ap = NULL, *line = NULL;
287 if (stat(p, &st) < 0)
290 if (major(st.st_dev) == 0)
294 block_get_whole_disk(d, &d);
296 if (asprintf(&ap, "/sys/dev/block/%u:%u/queue/nr_requests", major(d), minor(d)) < 0) {
301 r = read_one_line_file(ap, &line);
308 r = safe_atou64(line, &u);
309 if (r >= 0 && u >= BUMP_REQUEST_NR) {
317 if (asprintf(&line, "%lu", (unsigned long) BUMP_REQUEST_NR) < 0) {
322 r = write_string_file(ap, line);
326 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);
336 int block_get_readahead(const char *p, uint64_t *bytes) {
338 char *ap = NULL, *line = NULL;
346 if (stat(p, &st) < 0)
349 if (major(st.st_dev) == 0)
353 block_get_whole_disk(d, &d);
355 if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
360 r = read_one_line_file(ap, &line);
364 r = safe_atou64(line, &u);
368 *bytes = u * 1024ULL;
377 int block_set_readahead(const char *p, uint64_t bytes) {
379 char *ap = NULL, *line = NULL;
386 if (stat(p, &st) < 0)
389 if (major(st.st_dev) == 0)
393 block_get_whole_disk(d, &d);
395 if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
400 if (asprintf(&line, "%llu", (unsigned long long) bytes / 1024ULL) < 0) {
405 r = write_string_file(ap, line);