chiark / gitweb /
relicense to LGPLv2.1 (with exceptions)
[elogind.git] / src / readahead / readahead-common.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2010 Lennart Poettering
7
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.
12
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.
17
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/>.
20 ***/
21
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <sys/sysinfo.h>
26 #include <sys/inotify.h>
27 #include <fcntl.h>
28 #include <sys/mman.h>
29 #include <unistd.h>
30 #include <libudev.h>
31
32 #include "log.h"
33 #include "readahead-common.h"
34 #include "util.h"
35
36 int file_verify(int fd, const char *fn, off_t file_size_max, struct stat *st) {
37         assert(fd >= 0);
38         assert(fn);
39         assert(st);
40
41         if (fstat(fd, st) < 0) {
42                 log_warning("fstat(%s) failed: %m", fn);
43                 return -errno;
44         }
45
46         if (!S_ISREG(st->st_mode)) {
47                 log_debug("Not preloading special file %s", fn);
48                 return 0;
49         }
50
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);
53                 return 0;
54         }
55
56         return 1;
57 }
58
59 int fs_on_ssd(const char *p) {
60         struct stat st;
61         struct udev *udev = NULL;
62         struct udev_device *udev_device = NULL, *look_at = NULL;
63         bool b = false;
64         const char *devtype, *rotational, *model, *id;
65
66         assert(p);
67
68         if (stat(p, &st) < 0)
69                 return -errno;
70
71         if (major(st.st_dev) == 0)
72                 return false;
73
74         if (!(udev = udev_new()))
75                 return -ENOMEM;
76
77         if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev)))
78                 goto finish;
79
80         if ((devtype = udev_device_get_property_value(udev_device, "DEVTYPE")) &&
81             streq(devtype, "partition"))
82                 look_at = udev_device_get_parent(udev_device);
83         else
84                 look_at = udev_device;
85
86         if (!look_at)
87                 goto finish;
88
89         /* First, try high-level property */
90         if ((id = udev_device_get_property_value(look_at, "ID_SSD"))) {
91                 b = streq(id, "1");
92                 goto finish;
93         }
94
95         /* Second, try kernel attribute */
96         if ((rotational = udev_device_get_sysattr_value(look_at, "queue/rotational")))
97                 if ((b = streq(rotational, "0")))
98                         goto finish;
99
100         /* Finally, fallback to heuristics */
101         if (!(look_at = udev_device_get_parent(look_at)))
102                 goto finish;
103
104         if ((model = udev_device_get_sysattr_value(look_at, "model")))
105                 b = !!strstr(model, "SSD");
106
107 finish:
108         if (udev_device)
109                 udev_device_unref(udev_device);
110
111         if (udev)
112                 udev_unref(udev);
113
114         return b;
115 }
116
117 int fs_on_read_only(const char *p) {
118         struct stat st;
119         struct udev *udev = NULL;
120         struct udev_device *udev_device = NULL;
121         bool b = false;
122         const char *read_only;
123
124         assert(p);
125
126         if (stat(p, &st) < 0)
127                 return -errno;
128
129         if (major(st.st_dev) == 0)
130                 return false;
131
132         if (!(udev = udev_new()))
133                 return -ENOMEM;
134
135         if (!(udev_device = udev_device_new_from_devnum(udev, 'b', st.st_dev)))
136                 goto finish;
137
138         if ((read_only = udev_device_get_sysattr_value(udev_device, "ro")))
139                 if ((b = streq(read_only, "1")))
140                         goto finish;
141
142 finish:
143         if (udev_device)
144                 udev_device_unref(udev_device);
145
146         if (udev)
147                 udev_unref(udev);
148
149         return b;
150 }
151
152 bool enough_ram(void) {
153         struct sysinfo si;
154
155         assert_se(sysinfo(&si) >= 0);
156
157         /* Enable readahead only with at least 128MB memory */
158         return si.totalram > 127 * 1024*1024 / si.mem_unit;
159 }
160
161 int open_inotify(void) {
162         int fd;
163
164         if ((fd = inotify_init1(IN_CLOEXEC|IN_NONBLOCK)) < 0) {
165                 log_error("Failed to create inotify handle: %m");
166                 return -errno;
167         }
168
169         mkdir("/run/systemd", 0755);
170         mkdir("/run/systemd/readahead", 0755);
171
172         if (inotify_add_watch(fd, "/run/systemd/readahead", IN_CREATE) < 0) {
173                 log_error("Failed to watch /run/systemd/readahead: %m");
174                 close_nointr_nofail(fd);
175                 return -errno;
176         }
177
178         return fd;
179 }
180
181 ReadaheadShared *shared_get(void) {
182         int fd;
183         ReadaheadShared *m = NULL;
184
185         mkdir("/run/systemd", 0755);
186         mkdir("/run/systemd/readahead", 0755);
187
188         if ((fd = open("/run/systemd/readahead/shared", O_CREAT|O_RDWR|O_CLOEXEC, 0644)) < 0) {
189                 log_error("Failed to create shared memory segment: %m");
190                 goto finish;
191         }
192
193         if (ftruncate(fd, sizeof(ReadaheadShared)) < 0) {
194                 log_error("Failed to truncate shared memory segment: %m");
195                 goto finish;
196         }
197
198         if ((m = mmap(NULL, sizeof(ReadaheadShared), PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
199                 log_error("Failed to mmap shared memory segment: %m");
200                 m = NULL;
201                 goto finish;
202         }
203
204 finish:
205         if (fd >= 0)
206                 close_nointr_nofail(fd);
207
208         return m;
209 }
210
211 #define BUMP_REQUEST_NR (16*1024)
212
213 int bump_request_nr(const char *p) {
214         struct stat st;
215         uint64_t u;
216         char *ap = NULL, *line = NULL;
217         int r;
218         dev_t d;
219
220         assert(p);
221
222         if (stat(p, &st) < 0)
223                 return -errno;
224
225         if (major(st.st_dev) == 0)
226                 return 0;
227
228         d = st.st_dev;
229         block_get_whole_disk(d, &d);
230
231         if (asprintf(&ap, "/sys/dev/block/%u:%u/queue/nr_requests", major(d), minor(d)) < 0) {
232                 r= -ENOMEM;
233                 goto finish;
234         }
235
236         r = read_one_line_file(ap, &line);
237         if (r < 0) {
238                 if (r == -ENOENT)
239                         r = 0;
240                 goto finish;
241         }
242
243         r = safe_atou64(line, &u);
244         if (r >= 0 && u >= BUMP_REQUEST_NR) {
245                 r = 0;
246                 goto finish;
247         }
248
249         free(line);
250         line = NULL;
251
252         if (asprintf(&line, "%lu", (unsigned long) BUMP_REQUEST_NR) < 0) {
253                 r = -ENOMEM;
254                 goto finish;
255         }
256
257         r = write_one_line_file(ap, line);
258         if (r < 0)
259                 goto finish;
260
261         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);
262         r = 1;
263
264 finish:
265         free(ap);
266         free(line);
267
268         return r;
269 }