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