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