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"
36 int file_verify(int fd, const char *fn, off_t file_size_max, struct stat *st) {
41 if (fstat(fd, st) < 0) {
42 log_warning("fstat(%s) failed: %m", fn);
46 if (!S_ISREG(st->st_mode)) {
47 log_debug("Not preloading special file %s", fn);
51 if (st->st_size <= 0 || st->st_size > file_size_max) {
52 log_debug("Not preloading file %s with size out of bounds %llu", fn, (unsigned long long) st->st_size);
59 int fs_on_ssd(const char *p) {
61 struct udev *udev = NULL;
62 struct udev_device *udev_device = NULL, *look_at = NULL;
64 const char *devtype, *rotational, *model, *id;
71 if (major(st.st_dev) == 0)
78 udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev);
82 devtype = udev_device_get_property_value(udev_device, "DEVTYPE");
83 if (devtype && streq(devtype, "partition"))
84 look_at = udev_device_get_parent(udev_device);
86 look_at = udev_device;
91 /* First, try high-level property */
92 id = udev_device_get_property_value(look_at, "ID_SSD");
98 /* Second, try kernel attribute */
99 rotational = udev_device_get_sysattr_value(look_at, "queue/rotational");
101 if ((b = streq(rotational, "0")))
104 /* Finally, fallback to heuristics */
105 look_at = udev_device_get_parent(look_at);
109 model = udev_device_get_sysattr_value(look_at, "model");
111 b = !!strstr(model, "SSD");
115 udev_device_unref(udev_device);
123 int fs_on_read_only(const char *p) {
125 struct udev *udev = NULL;
126 struct udev_device *udev_device = NULL;
128 const char *read_only;
132 if (stat(p, &st) < 0)
135 if (major(st.st_dev) == 0)
138 if (!(udev = udev_new()))
141 if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev)))
144 if ((read_only = udev_device_get_sysattr_value(udev_device, "ro")))
145 if ((b = streq(read_only, "1")))
150 udev_device_unref(udev_device);
158 bool enough_ram(void) {
161 assert_se(sysinfo(&si) >= 0);
163 /* Enable readahead only with at least 128MB memory */
164 return si.totalram > 127 * 1024*1024 / si.mem_unit;
167 int open_inotify(void) {
170 if ((fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
171 log_error("Failed to create inotify handle: %m");
175 mkdir("/run/systemd", 0755);
176 mkdir("/run/systemd/readahead", 0755);
178 if (inotify_add_watch(fd, "/run/systemd/readahead", IN_CREATE) < 0) {
179 log_error("Failed to watch /run/systemd/readahead: %m");
180 close_nointr_nofail(fd);
187 ReadaheadShared *shared_get(void) {
189 ReadaheadShared *m = NULL;
191 mkdir("/run/systemd", 0755);
192 mkdir("/run/systemd/readahead", 0755);
194 if ((fd = open("/run/systemd/readahead/shared", O_CREAT|O_RDWR|O_CLOEXEC, 0644)) < 0) {
195 log_error("Failed to create shared memory segment: %m");
199 if (ftruncate(fd, sizeof(ReadaheadShared)) < 0) {
200 log_error("Failed to truncate shared memory segment: %m");
204 if ((m = mmap(NULL, sizeof(ReadaheadShared), PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
205 log_error("Failed to mmap shared memory segment: %m");
212 close_nointr_nofail(fd);
217 /* We use 20K instead of the more human digestable 16K here. Why?
218 Simply so that it is more unlikely that users end up picking this
219 value too so that we can recognize better whether the user changed
220 the value while we had it temporarily bumped. */
221 #define BUMP_REQUEST_NR (20*1024)
223 int block_bump_request_nr(const char *p) {
226 char *ap = NULL, *line = NULL;
232 if (stat(p, &st) < 0)
235 if (major(st.st_dev) == 0)
239 block_get_whole_disk(d, &d);
241 if (asprintf(&ap, "/sys/dev/block/%u:%u/queue/nr_requests", major(d), minor(d)) < 0) {
246 r = read_one_line_file(ap, &line);
253 r = safe_atou64(line, &u);
254 if (r >= 0 && u >= BUMP_REQUEST_NR) {
262 if (asprintf(&line, "%lu", (unsigned long) BUMP_REQUEST_NR) < 0) {
267 r = write_one_line_file(ap, line);
271 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);
281 int block_get_readahead(const char *p, uint64_t *bytes) {
283 char *ap = NULL, *line = NULL;
291 if (stat(p, &st) < 0)
294 if (major(st.st_dev) == 0)
298 block_get_whole_disk(d, &d);
300 if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
305 r = read_one_line_file(ap, &line);
309 r = safe_atou64(line, &u);
313 *bytes = u * 1024ULL;
322 int block_set_readahead(const char *p, uint64_t bytes) {
324 char *ap = NULL, *line = NULL;
331 if (stat(p, &st) < 0)
334 if (major(st.st_dev) == 0)
338 block_get_whole_disk(d, &d);
340 if (asprintf(&ap, "/sys/dev/block/%u:%u/bdi/read_ahead_kb", major(d), minor(d)) < 0) {
345 if (asprintf(&line, "%llu", (unsigned long long) bytes / 1024ULL) < 0) {
350 r = write_one_line_file(ap, line);