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"
37 int file_verify(int fd, const char *fn, off_t file_size_max, struct stat *st) {
42 if (fstat(fd, st) < 0) {
43 log_warning("fstat(%s) failed: %m", fn);
47 if (!S_ISREG(st->st_mode)) {
48 log_debug("Not preloading special file %s", fn);
52 if (st->st_size <= 0 || st->st_size > file_size_max) {
53 log_debug("Not preloading file %s with size out of bounds %llu", fn, (unsigned long long) st->st_size);
60 int fs_on_ssd(const char *p) {
62 struct udev *udev = NULL;
63 struct udev_device *udev_device = NULL, *look_at = NULL;
65 const char *devtype, *rotational, *model, *id;
73 if (major(st.st_dev) == 0) {
74 _cleanup_fclose_ FILE *f = NULL;
76 struct file_handle *h;
78 /* Might be btrfs, which exposes "ssd" as mount flag if it is on ssd.
80 * We first determine the mount ID here, if we can,
81 * and then lookup the mount ID in mountinfo to find
82 * the mount options. */
84 h = alloca(MAX_HANDLE_SZ);
85 h->handle_bytes = MAX_HANDLE_SZ;
86 r = name_to_handle_at(AT_FDCWD, p, h, &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");
148 /* Second, try kernel attribute */
149 rotational = udev_device_get_sysattr_value(look_at, "queue/rotational");
151 b = streq(rotational, "0");
155 /* Finally, fallback to heuristics */
156 look_at = udev_device_get_parent(look_at);
160 model = udev_device_get_sysattr_value(look_at, "model");
162 b = !!strstr(model, "SSD");
166 udev_device_unref(udev_device);
174 int fs_on_read_only(const char *p) {
176 struct udev *udev = NULL;
177 struct udev_device *udev_device = NULL;
179 const char *read_only;
183 if (stat(p, &st) < 0)
186 if (major(st.st_dev) == 0)
189 if (!(udev = udev_new()))
192 if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev)))
195 if ((read_only = udev_device_get_sysattr_value(udev_device, "ro")))
196 if ((b = streq(read_only, "1")))
201 udev_device_unref(udev_device);
209 bool enough_ram(void) {
212 assert_se(sysinfo(&si) >= 0);
214 /* Enable readahead only with at least 128MB memory */
215 return si.totalram > 127 * 1024*1024 / si.mem_unit;
218 int open_inotify(void) {
221 if ((fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
222 log_error("Failed to create inotify handle: %m");
226 mkdir("/run/systemd", 0755);
227 mkdir("/run/systemd/readahead", 0755);
229 if (inotify_add_watch(fd, "/run/systemd/readahead", IN_CREATE) < 0) {
230 log_error("Failed to watch /run/systemd/readahead: %m");
231 close_nointr_nofail(fd);
238 ReadaheadShared *shared_get(void) {
240 ReadaheadShared *m = NULL;
242 mkdir("/run/systemd", 0755);
243 mkdir("/run/systemd/readahead", 0755);
245 if ((fd = open("/run/systemd/readahead/shared", O_CREAT|O_RDWR|O_CLOEXEC, 0644)) < 0) {
246 log_error("Failed to create shared memory segment: %m");
250 if (ftruncate(fd, sizeof(ReadaheadShared)) < 0) {
251 log_error("Failed to truncate shared memory segment: %m");
255 if ((m = mmap(NULL, sizeof(ReadaheadShared), PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
256 log_error("Failed to mmap shared memory segment: %m");
263 close_nointr_nofail(fd);
268 /* We use 20K instead of the more human digestable 16K here. Why?
269 Simply so that it is more unlikely that users end up picking this
270 value too so that we can recognize better whether the user changed
271 the value while we had it temporarily bumped. */
272 #define BUMP_REQUEST_NR (20*1024)
274 int block_bump_request_nr(const char *p) {
277 char *ap = NULL, *line = NULL;
283 if (stat(p, &st) < 0)
286 if (major(st.st_dev) == 0)
290 block_get_whole_disk(d, &d);
292 if (asprintf(&ap, "/sys/dev/block/%u:%u/queue/nr_requests", major(d), minor(d)) < 0) {
297 r = read_one_line_file(ap, &line);
304 r = safe_atou64(line, &u);
305 if (r >= 0 && u >= BUMP_REQUEST_NR) {
313 if (asprintf(&line, "%lu", (unsigned long) BUMP_REQUEST_NR) < 0) {
318 r = write_one_line_file(ap, line);
322 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);
332 int block_get_readahead(const char *p, uint64_t *bytes) {
334 char *ap = NULL, *line = NULL;
342 if (stat(p, &st) < 0)
345 if (major(st.st_dev) == 0)
349 block_get_whole_disk(d, &d);
351 if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
356 r = read_one_line_file(ap, &line);
360 r = safe_atou64(line, &u);
364 *bytes = u * 1024ULL;
373 int block_set_readahead(const char *p, uint64_t bytes) {
375 char *ap = NULL, *line = NULL;
382 if (stat(p, &st) < 0)
385 if (major(st.st_dev) == 0)
389 block_get_whole_disk(d, &d);
391 if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
396 if (asprintf(&line, "%llu", (unsigned long long) bytes / 1024ULL) < 0) {
401 r = write_one_line_file(ap, line);