xref: /linux/tools/power/cpupower/utils/helpers/sysfs.c (revision 1ccd4b7bfdcfcc8cc7ffc4a9c11d3ac5b6da8ca0)
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 "helpers/sysfs.h"
18 
19 unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen)
20 {
21 	int fd;
22 	ssize_t numread;
23 
24 	fd = open(path, O_RDONLY);
25 	if (fd == -1)
26 		return 0;
27 
28 	numread = read(fd, buf, buflen - 1);
29 	if (numread < 1) {
30 		close(fd);
31 		return 0;
32 	}
33 
34 	buf[numread] = '\0';
35 	close(fd);
36 
37 	return (unsigned int) numread;
38 }
39 
40 static unsigned int sysfs_write_file(const char *path,
41 				     const char *value, size_t len)
42 {
43 	int fd;
44 	ssize_t numwrite;
45 
46 	fd = open(path, O_WRONLY);
47 	if (fd == -1)
48 		return 0;
49 
50 	numwrite = write(fd, value, len);
51 	if (numwrite < 1) {
52 		close(fd);
53 		return 0;
54 	}
55 	close(fd);
56 	return (unsigned int) numwrite;
57 }
58 
59 /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
60 
61 /*
62  * helper function to read file from /sys into given buffer
63  * fname is a relative path under "cpuX/cpuidle/stateX/" dir
64  * cstates starting with 0, C0 is not counted as cstate.
65  * This means if you want C1 info, pass 0 as idlestate param
66  */
67 unsigned int sysfs_idlestate_read_file(unsigned int cpu, unsigned int idlestate,
68 			     const char *fname, char *buf, size_t buflen)
69 {
70 	char path[SYSFS_PATH_MAX];
71 	int fd;
72 	ssize_t numread;
73 
74 	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
75 		 cpu, idlestate, fname);
76 
77 	fd = open(path, O_RDONLY);
78 	if (fd == -1)
79 		return 0;
80 
81 	numread = read(fd, buf, buflen - 1);
82 	if (numread < 1) {
83 		close(fd);
84 		return 0;
85 	}
86 
87 	buf[numread] = '\0';
88 	close(fd);
89 
90 	return (unsigned int) numread;
91 }
92 
93 /* read access to files which contain one numeric value */
94 
95 enum idlestate_value {
96 	IDLESTATE_USAGE,
97 	IDLESTATE_POWER,
98 	IDLESTATE_LATENCY,
99 	IDLESTATE_TIME,
100 	MAX_IDLESTATE_VALUE_FILES
101 };
102 
103 static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = {
104 	[IDLESTATE_USAGE] = "usage",
105 	[IDLESTATE_POWER] = "power",
106 	[IDLESTATE_LATENCY] = "latency",
107 	[IDLESTATE_TIME]  = "time",
108 };
109 
110 static unsigned long long sysfs_idlestate_get_one_value(unsigned int cpu,
111 						     unsigned int idlestate,
112 						     enum idlestate_value which)
113 {
114 	unsigned long long value;
115 	unsigned int len;
116 	char linebuf[MAX_LINE_LEN];
117 	char *endp;
118 
119 	if (which >= MAX_IDLESTATE_VALUE_FILES)
120 		return 0;
121 
122 	len = sysfs_idlestate_read_file(cpu, idlestate,
123 					idlestate_value_files[which],
124 					linebuf, sizeof(linebuf));
125 	if (len == 0)
126 		return 0;
127 
128 	value = strtoull(linebuf, &endp, 0);
129 
130 	if (endp == linebuf || errno == ERANGE)
131 		return 0;
132 
133 	return value;
134 }
135 
136 /* read access to files which contain one string */
137 
138 enum idlestate_string {
139 	IDLESTATE_DESC,
140 	IDLESTATE_NAME,
141 	MAX_IDLESTATE_STRING_FILES
142 };
143 
144 static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {
145 	[IDLESTATE_DESC] = "desc",
146 	[IDLESTATE_NAME] = "name",
147 };
148 
149 
150 static char *sysfs_idlestate_get_one_string(unsigned int cpu,
151 					unsigned int idlestate,
152 					enum idlestate_string which)
153 {
154 	char linebuf[MAX_LINE_LEN];
155 	char *result;
156 	unsigned int len;
157 
158 	if (which >= MAX_IDLESTATE_STRING_FILES)
159 		return NULL;
160 
161 	len = sysfs_idlestate_read_file(cpu, idlestate,
162 					idlestate_string_files[which],
163 					linebuf, sizeof(linebuf));
164 	if (len == 0)
165 		return NULL;
166 
167 	result = strdup(linebuf);
168 	if (result == NULL)
169 		return NULL;
170 
171 	if (result[strlen(result) - 1] == '\n')
172 		result[strlen(result) - 1] = '\0';
173 
174 	return result;
175 }
176 
177 unsigned long sysfs_get_idlestate_latency(unsigned int cpu,
178 					unsigned int idlestate)
179 {
180 	return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);
181 }
182 
183 unsigned long sysfs_get_idlestate_usage(unsigned int cpu,
184 					unsigned int idlestate)
185 {
186 	return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_USAGE);
187 }
188 
189 unsigned long long sysfs_get_idlestate_time(unsigned int cpu,
190 					unsigned int idlestate)
191 {
192 	return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_TIME);
193 }
194 
195 char *sysfs_get_idlestate_name(unsigned int cpu, unsigned int idlestate)
196 {
197 	return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_NAME);
198 }
199 
200 char *sysfs_get_idlestate_desc(unsigned int cpu, unsigned int idlestate)
201 {
202 	return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_DESC);
203 }
204 
205 /*
206  * Returns number of supported C-states of CPU core cpu
207  * Negativ in error case
208  * Zero if cpuidle does not export any C-states
209  */
210 int sysfs_get_idlestate_count(unsigned int cpu)
211 {
212 	char file[SYSFS_PATH_MAX];
213 	struct stat statbuf;
214 	int idlestates = 1;
215 
216 
217 	snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");
218 	if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
219 		return -ENODEV;
220 
221 	snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);
222 	if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
223 		return 0;
224 
225 	while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
226 		snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU
227 			 "cpu%u/cpuidle/state%d", cpu, idlestates);
228 		idlestates++;
229 	}
230 	idlestates--;
231 	return idlestates;
232 }
233 
234 /* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/
235 
236 /*
237  * helper function to read file from /sys into given buffer
238  * fname is a relative path under "cpu/cpuidle/" dir
239  */
240 static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,
241 					    size_t buflen)
242 {
243 	char path[SYSFS_PATH_MAX];
244 
245 	snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);
246 
247 	return sysfs_read_file(path, buf, buflen);
248 }
249 
250 
251 
252 /* read access to files which contain one string */
253 
254 enum cpuidle_string {
255 	CPUIDLE_GOVERNOR,
256 	CPUIDLE_GOVERNOR_RO,
257 	CPUIDLE_DRIVER,
258 	MAX_CPUIDLE_STRING_FILES
259 };
260 
261 static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {
262 	[CPUIDLE_GOVERNOR]	= "current_governor",
263 	[CPUIDLE_GOVERNOR_RO]	= "current_governor_ro",
264 	[CPUIDLE_DRIVER]	= "current_driver",
265 };
266 
267 
268 static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)
269 {
270 	char linebuf[MAX_LINE_LEN];
271 	char *result;
272 	unsigned int len;
273 
274 	if (which >= MAX_CPUIDLE_STRING_FILES)
275 		return NULL;
276 
277 	len = sysfs_cpuidle_read_file(cpuidle_string_files[which],
278 				linebuf, sizeof(linebuf));
279 	if (len == 0)
280 		return NULL;
281 
282 	result = strdup(linebuf);
283 	if (result == NULL)
284 		return NULL;
285 
286 	if (result[strlen(result) - 1] == '\n')
287 		result[strlen(result) - 1] = '\0';
288 
289 	return result;
290 }
291 
292 char *sysfs_get_cpuidle_governor(void)
293 {
294 	char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);
295 	if (!tmp)
296 		return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);
297 	else
298 		return tmp;
299 }
300 
301 char *sysfs_get_cpuidle_driver(void)
302 {
303 	return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);
304 }
305 /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
306 
307 /*
308  * Get sched_mc or sched_smt settings
309  * Pass "mc" or "smt" as argument
310  *
311  * Returns negative value on failure
312  */
313 int sysfs_get_sched(const char *smt_mc)
314 {
315 	unsigned long value;
316 	char linebuf[MAX_LINE_LEN];
317 	char *endp;
318 	char path[SYSFS_PATH_MAX];
319 
320 	if (strcmp("mc", smt_mc) && strcmp("smt", smt_mc))
321 		return -EINVAL;
322 
323 	snprintf(path, sizeof(path),
324 		PATH_TO_CPU "sched_%s_power_savings", smt_mc);
325 	if (sysfs_read_file(path, linebuf, MAX_LINE_LEN) == 0)
326 		return -1;
327 	value = strtoul(linebuf, &endp, 0);
328 	if (endp == linebuf || errno == ERANGE)
329 		return -1;
330 	return value;
331 }
332 
333 /*
334  * Get sched_mc or sched_smt settings
335  * Pass "mc" or "smt" as argument
336  *
337  * Returns negative value on failure
338  */
339 int sysfs_set_sched(const char *smt_mc, int val)
340 {
341 	char linebuf[MAX_LINE_LEN];
342 	char path[SYSFS_PATH_MAX];
343 	struct stat statbuf;
344 
345 	if (strcmp("mc", smt_mc) && strcmp("smt", smt_mc))
346 		return -EINVAL;
347 
348 	snprintf(path, sizeof(path),
349 		PATH_TO_CPU "sched_%s_power_savings", smt_mc);
350 	sprintf(linebuf, "%d", val);
351 
352 	if (stat(path, &statbuf) != 0)
353 		return -ENODEV;
354 
355 	if (sysfs_write_file(path, linebuf, MAX_LINE_LEN) == 0)
356 		return -1;
357 	return 0;
358 }
359