xref: /linux/tools/power/cpupower/lib/cpuidle.c (revision 991053178e08fb4d1f80398367db05c2cc4f20b4)
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
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
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
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
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 
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  */
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  */
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 	int len;
237 
238 	if (cpuidle_state_count(cpu) <= idlestate)
239 		return -1;
240 
241 	if (!cpuidle_state_file_exists(cpu, idlestate,
242 				 idlestate_value_files[IDLESTATE_DISABLE]))
243 		return -2;
244 
245 	len = snprintf(value, SYSFS_PATH_MAX, "%u", disable);
246 
247 	bytes_written = cpuidle_state_write_file(cpu, idlestate, "disable",
248 						   value, len);
249 	if (bytes_written)
250 		return 0;
251 	return -3;
252 }
253 
254 unsigned long cpuidle_state_latency(unsigned int cpu,
255 					  unsigned int idlestate)
256 {
257 	return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);
258 }
259 
260 unsigned long cpuidle_state_residency(unsigned int cpu,
261 					  unsigned int idlestate)
262 {
263 	return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_RESIDENCY);
264 }
265 
266 unsigned long cpuidle_state_usage(unsigned int cpu,
267 					unsigned int idlestate)
268 {
269 	return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_USAGE);
270 }
271 
272 unsigned long long cpuidle_state_time(unsigned int cpu,
273 					unsigned int idlestate)
274 {
275 	return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_TIME);
276 }
277 
278 char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate)
279 {
280 	return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME);
281 }
282 
283 char *cpuidle_state_desc(unsigned int cpu, unsigned int idlestate)
284 {
285 	return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_DESC);
286 }
287 
288 /*
289  * Returns number of supported C-states of CPU core cpu
290  * Negativ in error case
291  * Zero if cpuidle does not export any C-states
292  */
293 unsigned int cpuidle_state_count(unsigned int cpu)
294 {
295 	char file[SYSFS_PATH_MAX];
296 	struct stat statbuf;
297 	int idlestates = 1;
298 
299 
300 	snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");
301 	if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
302 		return 0;
303 
304 	snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);
305 	if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
306 		return 0;
307 
308 	while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
309 		snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU
310 			 "cpu%u/cpuidle/state%d", cpu, idlestates);
311 		idlestates++;
312 	}
313 	idlestates--;
314 	return idlestates;
315 }
316 
317 /* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/
318 
319 /*
320  * helper function to read file from /sys into given buffer
321  * fname is a relative path under "cpu/cpuidle/" dir
322  */
323 static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,
324 					    size_t buflen)
325 {
326 	char path[SYSFS_PATH_MAX];
327 
328 	snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);
329 
330 	return cpupower_read_sysfs(path, buf, buflen);
331 }
332 
333 
334 
335 /* read access to files which contain one string */
336 
337 enum cpuidle_string {
338 	CPUIDLE_GOVERNOR,
339 	CPUIDLE_GOVERNOR_RO,
340 	CPUIDLE_DRIVER,
341 	MAX_CPUIDLE_STRING_FILES
342 };
343 
344 static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {
345 	[CPUIDLE_GOVERNOR]	= "current_governor",
346 	[CPUIDLE_GOVERNOR_RO]	= "current_governor_ro",
347 	[CPUIDLE_DRIVER]	= "current_driver",
348 };
349 
350 
351 static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)
352 {
353 	char linebuf[MAX_LINE_LEN];
354 	char *result;
355 	unsigned int len;
356 
357 	if (which >= MAX_CPUIDLE_STRING_FILES)
358 		return NULL;
359 
360 	len = sysfs_cpuidle_read_file(cpuidle_string_files[which],
361 				linebuf, sizeof(linebuf));
362 	if (len == 0)
363 		return NULL;
364 
365 	result = strdup(linebuf);
366 	if (result == NULL)
367 		return NULL;
368 
369 	if (result[strlen(result) - 1] == '\n')
370 		result[strlen(result) - 1] = '\0';
371 
372 	return result;
373 }
374 
375 char *cpuidle_get_governor(void)
376 {
377 	char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);
378 	if (!tmp)
379 		return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);
380 	else
381 		return tmp;
382 }
383 
384 char *cpuidle_get_driver(void)
385 {
386 	return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);
387 }
388 /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
389