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