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