xref: /openbmc/linux/tools/power/cpupower/lib/cpuidle.c (revision 7587eb18)
1 /*
2  *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>
3  *  (C) 2011       Thomas Renninger <trenn@novell.com> 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 
322 	return sysfs_read_file(path, buf, buflen);
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 */
381