Linux-libre 5.3.12-gnu
[librecmc/linux-libre.git] / tools / power / cpupower / utils / helpers / sysfs.c
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 "helpers/sysfs.h"
17
18 unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen)
19 {
20         int fd;
21         ssize_t numread;
22
23         fd = open(path, O_RDONLY);
24         if (fd == -1)
25                 return 0;
26
27         numread = read(fd, buf, buflen - 1);
28         if (numread < 1) {
29                 close(fd);
30                 return 0;
31         }
32
33         buf[numread] = '\0';
34         close(fd);
35
36         return (unsigned int) numread;
37 }
38
39 /*
40  * Detect whether a CPU is online
41  *
42  * Returns:
43  *     1 -> if CPU is online
44  *     0 -> if CPU is offline
45  *     negative errno values in error case
46  */
47 int sysfs_is_cpu_online(unsigned int cpu)
48 {
49         char path[SYSFS_PATH_MAX];
50         int fd;
51         ssize_t numread;
52         unsigned long long value;
53         char linebuf[MAX_LINE_LEN];
54         char *endp;
55         struct stat statbuf;
56
57         snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu);
58
59         if (stat(path, &statbuf) != 0)
60                 return 0;
61
62         /*
63          * kernel without CONFIG_HOTPLUG_CPU
64          * -> cpuX directory exists, but not cpuX/online file
65          */
66         snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu);
67         if (stat(path, &statbuf) != 0)
68                 return 1;
69
70         fd = open(path, O_RDONLY);
71         if (fd == -1)
72                 return -errno;
73
74         numread = read(fd, linebuf, MAX_LINE_LEN - 1);
75         if (numread < 1) {
76                 close(fd);
77                 return -EIO;
78         }
79         linebuf[numread] = '\0';
80         close(fd);
81
82         value = strtoull(linebuf, &endp, 0);
83         if (value > 1)
84                 return -EINVAL;
85
86         return value;
87 }
88
89 /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
90
91
92 /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
93
94 /*
95  * helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir
96  * exists.
97  * For example the functionality to disable c-states was introduced in later
98  * kernel versions, this function can be used to explicitly check for this
99  * feature.
100  *
101  * returns 1 if the file exists, 0 otherwise.
102  */
103 unsigned int sysfs_idlestate_file_exists(unsigned int cpu,
104                                          unsigned int idlestate,
105                                          const char *fname)
106 {
107         char path[SYSFS_PATH_MAX];
108         struct stat statbuf;
109
110
111         snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
112                  cpu, idlestate, fname);
113         if (stat(path, &statbuf) != 0)
114                 return 0;
115         return 1;
116 }
117
118 /*
119  * helper function to read file from /sys into given buffer
120  * fname is a relative path under "cpuX/cpuidle/stateX/" dir
121  * cstates starting with 0, C0 is not counted as cstate.
122  * This means if you want C1 info, pass 0 as idlestate param
123  */
124 unsigned int sysfs_idlestate_read_file(unsigned int cpu, unsigned int idlestate,
125                              const char *fname, char *buf, size_t buflen)
126 {
127         char path[SYSFS_PATH_MAX];
128         int fd;
129         ssize_t numread;
130
131         snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
132                  cpu, idlestate, fname);
133
134         fd = open(path, O_RDONLY);
135         if (fd == -1)
136                 return 0;
137
138         numread = read(fd, buf, buflen - 1);
139         if (numread < 1) {
140                 close(fd);
141                 return 0;
142         }
143
144         buf[numread] = '\0';
145         close(fd);
146
147         return (unsigned int) numread;
148 }
149
150 /* 
151  * helper function to write a new value to a /sys file
152  * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir
153  *
154  * Returns the number of bytes written or 0 on error
155  */
156 static
157 unsigned int sysfs_idlestate_write_file(unsigned int cpu,
158                                         unsigned int idlestate,
159                                         const char *fname,
160                                         const char *value, size_t len)
161 {
162         char path[SYSFS_PATH_MAX];
163         int fd;
164         ssize_t numwrite;
165
166         snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
167                  cpu, idlestate, fname);
168
169         fd = open(path, O_WRONLY);
170         if (fd == -1)
171                 return 0;
172
173         numwrite = write(fd, value, len);
174         if (numwrite < 1) {
175                 close(fd);
176                 return 0;
177         }
178
179         close(fd);
180
181         return (unsigned int) numwrite;
182 }
183
184 /* read access to files which contain one numeric value */
185
186 enum idlestate_value {
187         IDLESTATE_USAGE,
188         IDLESTATE_POWER,
189         IDLESTATE_LATENCY,
190         IDLESTATE_TIME,
191         IDLESTATE_DISABLE,
192         MAX_IDLESTATE_VALUE_FILES
193 };
194
195 static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = {
196         [IDLESTATE_USAGE] = "usage",
197         [IDLESTATE_POWER] = "power",
198         [IDLESTATE_LATENCY] = "latency",
199         [IDLESTATE_TIME]  = "time",
200         [IDLESTATE_DISABLE]  = "disable",
201 };
202
203 static unsigned long long sysfs_idlestate_get_one_value(unsigned int cpu,
204                                                      unsigned int idlestate,
205                                                      enum idlestate_value which)
206 {
207         unsigned long long value;
208         unsigned int len;
209         char linebuf[MAX_LINE_LEN];
210         char *endp;
211
212         if (which >= MAX_IDLESTATE_VALUE_FILES)
213                 return 0;
214
215         len = sysfs_idlestate_read_file(cpu, idlestate,
216                                         idlestate_value_files[which],
217                                         linebuf, sizeof(linebuf));
218         if (len == 0)
219                 return 0;
220
221         value = strtoull(linebuf, &endp, 0);
222
223         if (endp == linebuf || errno == ERANGE)
224                 return 0;
225
226         return value;
227 }
228
229 /* read access to files which contain one string */
230
231 enum idlestate_string {
232         IDLESTATE_DESC,
233         IDLESTATE_NAME,
234         MAX_IDLESTATE_STRING_FILES
235 };
236
237 static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {
238         [IDLESTATE_DESC] = "desc",
239         [IDLESTATE_NAME] = "name",
240 };
241
242
243 static char *sysfs_idlestate_get_one_string(unsigned int cpu,
244                                         unsigned int idlestate,
245                                         enum idlestate_string which)
246 {
247         char linebuf[MAX_LINE_LEN];
248         char *result;
249         unsigned int len;
250
251         if (which >= MAX_IDLESTATE_STRING_FILES)
252                 return NULL;
253
254         len = sysfs_idlestate_read_file(cpu, idlestate,
255                                         idlestate_string_files[which],
256                                         linebuf, sizeof(linebuf));
257         if (len == 0)
258                 return NULL;
259
260         result = strdup(linebuf);
261         if (result == NULL)
262                 return NULL;
263
264         if (result[strlen(result) - 1] == '\n')
265                 result[strlen(result) - 1] = '\0';
266
267         return result;
268 }
269
270 /*
271  * Returns:
272  *    1  if disabled
273  *    0  if enabled
274  *    -1 if idlestate is not available
275  *    -2 if disabling is not supported by the kernel
276  */
277 int sysfs_is_idlestate_disabled(unsigned int cpu,
278                                 unsigned int idlestate)
279 {
280         if (sysfs_get_idlestate_count(cpu) <= idlestate)
281                 return -1;
282
283         if (!sysfs_idlestate_file_exists(cpu, idlestate,
284                                  idlestate_value_files[IDLESTATE_DISABLE]))
285                 return -2;
286         return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_DISABLE);
287 }
288
289 /*
290  * Pass 1 as last argument to disable or 0 to enable the state
291  * Returns:
292  *    0  on success
293  *    negative values on error, for example:
294  *      -1 if idlestate is not available
295  *      -2 if disabling is not supported by the kernel
296  *      -3 No write access to disable/enable C-states
297  */
298 int sysfs_idlestate_disable(unsigned int cpu,
299                             unsigned int idlestate,
300                             unsigned int disable)
301 {
302         char value[SYSFS_PATH_MAX];
303         int bytes_written;
304
305         if (sysfs_get_idlestate_count(cpu) <= idlestate)
306                 return -1;
307
308         if (!sysfs_idlestate_file_exists(cpu, idlestate,
309                                  idlestate_value_files[IDLESTATE_DISABLE]))
310                 return -2;
311
312         snprintf(value, SYSFS_PATH_MAX, "%u", disable);
313
314         bytes_written = sysfs_idlestate_write_file(cpu, idlestate, "disable",
315                                                    value, sizeof(disable));
316         if (bytes_written)
317                 return 0;
318         return -3;
319 }
320
321 unsigned long sysfs_get_idlestate_latency(unsigned int cpu,
322                                           unsigned int idlestate)
323 {
324         return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);
325 }
326
327 unsigned long sysfs_get_idlestate_usage(unsigned int cpu,
328                                         unsigned int idlestate)
329 {
330         return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_USAGE);
331 }
332
333 unsigned long long sysfs_get_idlestate_time(unsigned int cpu,
334                                         unsigned int idlestate)
335 {
336         return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_TIME);
337 }
338
339 char *sysfs_get_idlestate_name(unsigned int cpu, unsigned int idlestate)
340 {
341         return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_NAME);
342 }
343
344 char *sysfs_get_idlestate_desc(unsigned int cpu, unsigned int idlestate)
345 {
346         return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_DESC);
347 }
348
349 /*
350  * Returns number of supported C-states of CPU core cpu
351  * Negativ in error case
352  * Zero if cpuidle does not export any C-states
353  */
354 unsigned int sysfs_get_idlestate_count(unsigned int cpu)
355 {
356         char file[SYSFS_PATH_MAX];
357         struct stat statbuf;
358         int idlestates = 1;
359
360
361         snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");
362         if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
363                 return 0;
364
365         snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);
366         if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
367                 return 0;
368
369         while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
370                 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU
371                          "cpu%u/cpuidle/state%d", cpu, idlestates);
372                 idlestates++;
373         }
374         idlestates--;
375         return idlestates;
376 }
377
378 /* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/
379
380 /*
381  * helper function to read file from /sys into given buffer
382  * fname is a relative path under "cpu/cpuidle/" dir
383  */
384 static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,
385                                             size_t buflen)
386 {
387         char path[SYSFS_PATH_MAX];
388
389         snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);
390
391         return sysfs_read_file(path, buf, buflen);
392 }
393
394
395
396 /* read access to files which contain one string */
397
398 enum cpuidle_string {
399         CPUIDLE_GOVERNOR,
400         CPUIDLE_GOVERNOR_RO,
401         CPUIDLE_DRIVER,
402         MAX_CPUIDLE_STRING_FILES
403 };
404
405 static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {
406         [CPUIDLE_GOVERNOR]      = "current_governor",
407         [CPUIDLE_GOVERNOR_RO]   = "current_governor_ro",
408         [CPUIDLE_DRIVER]        = "current_driver",
409 };
410
411
412 static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)
413 {
414         char linebuf[MAX_LINE_LEN];
415         char *result;
416         unsigned int len;
417
418         if (which >= MAX_CPUIDLE_STRING_FILES)
419                 return NULL;
420
421         len = sysfs_cpuidle_read_file(cpuidle_string_files[which],
422                                 linebuf, sizeof(linebuf));
423         if (len == 0)
424                 return NULL;
425
426         result = strdup(linebuf);
427         if (result == NULL)
428                 return NULL;
429
430         if (result[strlen(result) - 1] == '\n')
431                 result[strlen(result) - 1] = '\0';
432
433         return result;
434 }
435
436 char *sysfs_get_cpuidle_governor(void)
437 {
438         char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);
439         if (!tmp)
440                 return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);
441         else
442                 return tmp;
443 }
444
445 char *sysfs_get_cpuidle_driver(void)
446 {
447         return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);
448 }
449 /* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
450
451 /*
452  * Get sched_mc or sched_smt settings
453  * Pass "mc" or "smt" as argument
454  *
455  * Returns negative value on failure
456  */
457 int sysfs_get_sched(const char *smt_mc)
458 {
459         return -ENODEV;
460 }
461
462 /*
463  * Get sched_mc or sched_smt settings
464  * Pass "mc" or "smt" as argument
465  *
466  * Returns negative value on failure
467  */
468 int sysfs_set_sched(const char *smt_mc, int val)
469 {
470         return -ENODEV;
471 }