Merge branch 'for-linus' of git://git.kernel.dk/linux-block
[pandora-kernel.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         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 /*
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  */
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  */
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
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
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
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
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
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
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
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  */
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  */
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
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
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
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  */
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  */
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 }