]>
Commit | Line | Data |
---|---|---|
ac5a181d TR |
1 | /* |
2 | * (C) 2004-2009 Dominik Brodowski <[email protected]> | |
3 | * (C) 2011 Thomas Renninger <[email protected]> Novell Inc. | |
4 | * | |
5 | * Licensed under the terms of the GNU GPL License version 2. | |
6 | */ | |
7 | ||
8 | #include <stdio.h> | |
9 | #include <errno.h> | |
10 | #include <stdlib.h> | |
11 | #include <string.h> | |
12 | #include <sys/types.h> | |
13 | #include <sys/stat.h> | |
14 | #include <fcntl.h> | |
15 | #include <unistd.h> | |
16 | ||
17 | #include "cpuidle.h" | |
18 | #include "cpupower_intern.h" | |
19 | ||
20 | /* | |
21 | * helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir | |
22 | * exists. | |
23 | * For example the functionality to disable c-states was introduced in later | |
24 | * kernel versions, this function can be used to explicitly check for this | |
25 | * feature. | |
26 | * | |
27 | * returns 1 if the file exists, 0 otherwise. | |
28 | */ | |
29 | static | |
30 | unsigned int cpuidle_state_file_exists(unsigned int cpu, | |
31 | unsigned int idlestate, | |
32 | const char *fname) | |
33 | { | |
34 | char path[SYSFS_PATH_MAX]; | |
35 | struct stat statbuf; | |
36 | ||
37 | ||
38 | snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", | |
39 | cpu, idlestate, fname); | |
40 | if (stat(path, &statbuf) != 0) | |
41 | return 0; | |
42 | return 1; | |
43 | } | |
44 | ||
45 | /* | |
46 | * helper function to read file from /sys into given buffer | |
47 | * fname is a relative path under "cpuX/cpuidle/stateX/" dir | |
48 | * cstates starting with 0, C0 is not counted as cstate. | |
49 | * This means if you want C1 info, pass 0 as idlestate param | |
50 | */ | |
51 | static | |
52 | unsigned int cpuidle_state_read_file(unsigned int cpu, | |
53 | unsigned int idlestate, | |
54 | const char *fname, char *buf, | |
55 | size_t buflen) | |
56 | { | |
57 | char path[SYSFS_PATH_MAX]; | |
58 | int fd; | |
59 | ssize_t numread; | |
60 | ||
61 | snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", | |
62 | cpu, idlestate, fname); | |
63 | ||
64 | fd = open(path, O_RDONLY); | |
65 | if (fd == -1) | |
66 | return 0; | |
67 | ||
68 | numread = read(fd, buf, buflen - 1); | |
69 | if (numread < 1) { | |
70 | close(fd); | |
71 | return 0; | |
72 | } | |
73 | ||
74 | buf[numread] = '\0'; | |
75 | close(fd); | |
76 | ||
77 | return (unsigned int) numread; | |
78 | } | |
79 | ||
80 | /* | |
81 | * helper function to write a new value to a /sys file | |
82 | * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir | |
83 | * | |
84 | * Returns the number of bytes written or 0 on error | |
85 | */ | |
86 | static | |
87 | unsigned int cpuidle_state_write_file(unsigned int cpu, | |
88 | unsigned int idlestate, | |
89 | const char *fname, | |
90 | const char *value, size_t len) | |
91 | { | |
92 | char path[SYSFS_PATH_MAX]; | |
93 | int fd; | |
94 | ssize_t numwrite; | |
95 | ||
96 | snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", | |
97 | cpu, idlestate, fname); | |
98 | ||
99 | fd = open(path, O_WRONLY); | |
100 | if (fd == -1) | |
101 | return 0; | |
102 | ||
103 | numwrite = write(fd, value, len); | |
104 | if (numwrite < 1) { | |
105 | close(fd); | |
106 | return 0; | |
107 | } | |
108 | ||
109 | close(fd); | |
110 | ||
111 | return (unsigned int) numwrite; | |
112 | } | |
113 | ||
114 | /* read access to files which contain one numeric value */ | |
115 | ||
116 | enum idlestate_value { | |
117 | IDLESTATE_USAGE, | |
118 | IDLESTATE_POWER, | |
119 | IDLESTATE_LATENCY, | |
120 | IDLESTATE_TIME, | |
121 | IDLESTATE_DISABLE, | |
122 | MAX_IDLESTATE_VALUE_FILES | |
123 | }; | |
124 | ||
125 | static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = { | |
126 | [IDLESTATE_USAGE] = "usage", | |
127 | [IDLESTATE_POWER] = "power", | |
128 | [IDLESTATE_LATENCY] = "latency", | |
129 | [IDLESTATE_TIME] = "time", | |
130 | [IDLESTATE_DISABLE] = "disable", | |
131 | }; | |
132 | ||
133 | static | |
134 | unsigned long long cpuidle_state_get_one_value(unsigned int cpu, | |
135 | unsigned int idlestate, | |
136 | enum idlestate_value which) | |
137 | { | |
138 | unsigned long long value; | |
139 | unsigned int len; | |
140 | char linebuf[MAX_LINE_LEN]; | |
141 | char *endp; | |
142 | ||
143 | if (which >= MAX_IDLESTATE_VALUE_FILES) | |
144 | return 0; | |
145 | ||
146 | len = cpuidle_state_read_file(cpu, idlestate, | |
147 | idlestate_value_files[which], | |
148 | linebuf, sizeof(linebuf)); | |
149 | if (len == 0) | |
150 | return 0; | |
151 | ||
152 | value = strtoull(linebuf, &endp, 0); | |
153 | ||
154 | if (endp == linebuf || errno == ERANGE) | |
155 | return 0; | |
156 | ||
157 | return value; | |
158 | } | |
159 | ||
160 | /* read access to files which contain one string */ | |
161 | ||
162 | enum idlestate_string { | |
163 | IDLESTATE_DESC, | |
164 | IDLESTATE_NAME, | |
165 | MAX_IDLESTATE_STRING_FILES | |
166 | }; | |
167 | ||
168 | static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = { | |
169 | [IDLESTATE_DESC] = "desc", | |
170 | [IDLESTATE_NAME] = "name", | |
171 | }; | |
172 | ||
173 | ||
174 | static char *cpuidle_state_get_one_string(unsigned int cpu, | |
175 | unsigned int idlestate, | |
176 | enum idlestate_string which) | |
177 | { | |
178 | char linebuf[MAX_LINE_LEN]; | |
179 | char *result; | |
180 | unsigned int len; | |
181 | ||
182 | if (which >= MAX_IDLESTATE_STRING_FILES) | |
183 | return NULL; | |
184 | ||
185 | len = cpuidle_state_read_file(cpu, idlestate, | |
186 | idlestate_string_files[which], | |
187 | linebuf, sizeof(linebuf)); | |
188 | if (len == 0) | |
189 | return NULL; | |
190 | ||
191 | result = strdup(linebuf); | |
192 | if (result == NULL) | |
193 | return NULL; | |
194 | ||
195 | if (result[strlen(result) - 1] == '\n') | |
196 | result[strlen(result) - 1] = '\0'; | |
197 | ||
198 | return result; | |
199 | } | |
200 | ||
201 | /* | |
202 | * Returns: | |
203 | * 1 if disabled | |
204 | * 0 if enabled | |
205 | * -1 if idlestate is not available | |
206 | * -2 if disabling is not supported by the kernel | |
207 | */ | |
208 | int cpuidle_is_state_disabled(unsigned int cpu, | |
209 | unsigned int idlestate) | |
210 | { | |
211 | if (cpuidle_state_count(cpu) <= idlestate) | |
212 | return -1; | |
213 | ||
214 | if (!cpuidle_state_file_exists(cpu, idlestate, | |
215 | idlestate_value_files[IDLESTATE_DISABLE])) | |
216 | return -2; | |
217 | return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_DISABLE); | |
218 | } | |
219 | ||
220 | /* | |
221 | * Pass 1 as last argument to disable or 0 to enable the state | |
222 | * Returns: | |
223 | * 0 on success | |
224 | * negative values on error, for example: | |
225 | * -1 if idlestate is not available | |
226 | * -2 if disabling is not supported by the kernel | |
227 | * -3 No write access to disable/enable C-states | |
228 | */ | |
229 | int cpuidle_state_disable(unsigned int cpu, | |
230 | unsigned int idlestate, | |
231 | unsigned int disable) | |
232 | { | |
233 | char value[SYSFS_PATH_MAX]; | |
234 | int bytes_written; | |
235 | ||
236 | if (cpuidle_state_count(cpu) <= idlestate) | |
237 | return -1; | |
238 | ||
239 | if (!cpuidle_state_file_exists(cpu, idlestate, | |
240 | idlestate_value_files[IDLESTATE_DISABLE])) | |
241 | return -2; | |
242 | ||
243 | snprintf(value, SYSFS_PATH_MAX, "%u", disable); | |
244 | ||
245 | bytes_written = cpuidle_state_write_file(cpu, idlestate, "disable", | |
246 | value, sizeof(disable)); | |
247 | if (bytes_written) | |
248 | return 0; | |
249 | return -3; | |
250 | } | |
251 | ||
252 | unsigned long cpuidle_state_latency(unsigned int cpu, | |
253 | unsigned int idlestate) | |
254 | { | |
255 | return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_LATENCY); | |
256 | } | |
257 | ||
258 | unsigned long cpuidle_state_usage(unsigned int cpu, | |
259 | unsigned int idlestate) | |
260 | { | |
261 | return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_USAGE); | |
262 | } | |
263 | ||
264 | unsigned long long cpuidle_state_time(unsigned int cpu, | |
265 | unsigned int idlestate) | |
266 | { | |
267 | return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_TIME); | |
268 | } | |
269 | ||
270 | char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate) | |
271 | { | |
272 | return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME); | |
273 | } | |
274 | ||
275 | char *cpuidle_state_desc(unsigned int cpu, unsigned int idlestate) | |
276 | { | |
277 | return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_DESC); | |
278 | } | |
279 | ||
280 | /* | |
281 | * Returns number of supported C-states of CPU core cpu | |
282 | * Negativ in error case | |
283 | * Zero if cpuidle does not export any C-states | |
284 | */ | |
285 | unsigned int cpuidle_state_count(unsigned int cpu) | |
286 | { | |
287 | char file[SYSFS_PATH_MAX]; | |
288 | struct stat statbuf; | |
289 | int idlestates = 1; | |
290 | ||
291 | ||
292 | snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle"); | |
293 | if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) | |
294 | return 0; | |
295 | ||
296 | snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu); | |
297 | if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) | |
298 | return 0; | |
299 | ||
300 | while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) { | |
301 | snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU | |
302 | "cpu%u/cpuidle/state%d", cpu, idlestates); | |
303 | idlestates++; | |
304 | } | |
305 | idlestates--; | |
306 | return idlestates; | |
307 | } | |
308 | ||
309 | /* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/ | |
310 | ||
311 | /* | |
312 | * helper function to read file from /sys into given buffer | |
313 | * fname is a relative path under "cpu/cpuidle/" dir | |
314 | */ | |
315 | static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf, | |
316 | size_t buflen) | |
317 | { | |
318 | char path[SYSFS_PATH_MAX]; | |
319 | ||
320 | snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname); | |
321 | ||
9de9aa45 | 322 | return cpupower_read_sysfs(path, buf, buflen); |
ac5a181d TR |
323 | } |
324 | ||
325 | ||
326 | ||
327 | /* read access to files which contain one string */ | |
328 | ||
329 | enum cpuidle_string { | |
330 | CPUIDLE_GOVERNOR, | |
331 | CPUIDLE_GOVERNOR_RO, | |
332 | CPUIDLE_DRIVER, | |
333 | MAX_CPUIDLE_STRING_FILES | |
334 | }; | |
335 | ||
336 | static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = { | |
337 | [CPUIDLE_GOVERNOR] = "current_governor", | |
338 | [CPUIDLE_GOVERNOR_RO] = "current_governor_ro", | |
339 | [CPUIDLE_DRIVER] = "current_driver", | |
340 | }; | |
341 | ||
342 | ||
343 | static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which) | |
344 | { | |
345 | char linebuf[MAX_LINE_LEN]; | |
346 | char *result; | |
347 | unsigned int len; | |
348 | ||
349 | if (which >= MAX_CPUIDLE_STRING_FILES) | |
350 | return NULL; | |
351 | ||
352 | len = sysfs_cpuidle_read_file(cpuidle_string_files[which], | |
353 | linebuf, sizeof(linebuf)); | |
354 | if (len == 0) | |
355 | return NULL; | |
356 | ||
357 | result = strdup(linebuf); | |
358 | if (result == NULL) | |
359 | return NULL; | |
360 | ||
361 | if (result[strlen(result) - 1] == '\n') | |
362 | result[strlen(result) - 1] = '\0'; | |
363 | ||
364 | return result; | |
365 | } | |
366 | ||
367 | char *cpuidle_get_governor(void) | |
368 | { | |
369 | char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO); | |
370 | if (!tmp) | |
371 | return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR); | |
372 | else | |
373 | return tmp; | |
374 | } | |
375 | ||
376 | char *cpuidle_get_driver(void) | |
377 | { | |
378 | return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER); | |
379 | } | |
380 | /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ |