blob: e13ff38329a0e1fb21920233600ef69ab651e926 [file] [log] [blame]
Thomas Gleixner4f190482019-05-27 08:55:14 +02001// SPDX-License-Identifier: GPL-2.0-only
Dominik Brodowski7fe2f632011-03-30 16:30:11 +02002/*
3 * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
4 * (C) 2011 Thomas Renninger <trenn@novell.com> Novell Inc.
Dominik Brodowski7fe2f632011-03-30 16:30:11 +02005 */
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
18unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen)
19{
20 int fd;
Dominik Brodowski2cd005c2011-04-19 20:16:05 +020021 ssize_t numread;
Dominik Brodowski7fe2f632011-03-30 16:30:11 +020022
Dominik Brodowski2cd005c2011-04-19 20:16:05 +020023 fd = open(path, O_RDONLY);
24 if (fd == -1)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +020025 return 0;
26
27 numread = read(fd, buf, buflen - 1);
Dominik Brodowski2cd005c2011-04-19 20:16:05 +020028 if (numread < 1) {
Dominik Brodowski7fe2f632011-03-30 16:30:11 +020029 close(fd);
30 return 0;
31 }
32
33 buf[numread] = '\0';
34 close(fd);
35
Dominik Brodowski2cd005c2011-04-19 20:16:05 +020036 return (unsigned int) numread;
Dominik Brodowski7fe2f632011-03-30 16:30:11 +020037}
38
Thomas Renninger7c74d2b2011-08-12 01:11:37 +020039/*
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 */
47int 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);
Andrey Utkin788606c2014-07-19 22:14:04 +030083 if (value > 1)
Thomas Renninger7c74d2b2011-08-12 01:11:37 +020084 return -EINVAL;
85
86 return value;
87}
88
Dominik Brodowski7fe2f632011-03-30 16:30:11 +020089/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
90
Thomas Renninger0924c362013-06-28 15:34:30 +020091
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 */
103unsigned 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
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200118/*
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 */
124unsigned 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;
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200129 ssize_t numread;
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200130
131 snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
132 cpu, idlestate, fname);
133
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200134 fd = open(path, O_RDONLY);
135 if (fd == -1)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200136 return 0;
137
138 numread = read(fd, buf, buflen - 1);
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200139 if (numread < 1) {
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200140 close(fd);
141 return 0;
142 }
143
144 buf[numread] = '\0';
145 close(fd);
146
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200147 return (unsigned int) numread;
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200148}
149
Thomas Renninger0924c362013-06-28 15:34:30 +0200150/*
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 */
156static
157unsigned 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
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200184/* read access to files which contain one numeric value */
185
186enum idlestate_value {
187 IDLESTATE_USAGE,
188 IDLESTATE_POWER,
189 IDLESTATE_LATENCY,
190 IDLESTATE_TIME,
Thomas Renninger0924c362013-06-28 15:34:30 +0200191 IDLESTATE_DISABLE,
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200192 MAX_IDLESTATE_VALUE_FILES
193};
194
195static 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",
Thomas Renninger0924c362013-06-28 15:34:30 +0200200 [IDLESTATE_DISABLE] = "disable",
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200201};
202
203static 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
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200212 if (which >= MAX_IDLESTATE_VALUE_FILES)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200213 return 0;
214
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200215 len = sysfs_idlestate_read_file(cpu, idlestate,
216 idlestate_value_files[which],
217 linebuf, sizeof(linebuf));
218 if (len == 0)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200219 return 0;
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200220
221 value = strtoull(linebuf, &endp, 0);
222
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200223 if (endp == linebuf || errno == ERANGE)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200224 return 0;
225
226 return value;
227}
228
229/* read access to files which contain one string */
230
231enum idlestate_string {
232 IDLESTATE_DESC,
233 IDLESTATE_NAME,
234 MAX_IDLESTATE_STRING_FILES
235};
236
237static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {
238 [IDLESTATE_DESC] = "desc",
239 [IDLESTATE_NAME] = "name",
240};
241
242
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200243static char *sysfs_idlestate_get_one_string(unsigned int cpu,
244 unsigned int idlestate,
245 enum idlestate_string which)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200246{
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
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200254 len = sysfs_idlestate_read_file(cpu, idlestate,
255 idlestate_string_files[which],
256 linebuf, sizeof(linebuf));
257 if (len == 0)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200258 return NULL;
259
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200260 result = strdup(linebuf);
261 if (result == NULL)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200262 return NULL;
263
264 if (result[strlen(result) - 1] == '\n')
265 result[strlen(result) - 1] = '\0';
266
267 return result;
268}
269
Thomas Renninger0924c362013-06-28 15:34:30 +0200270/*
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 */
277int sysfs_is_idlestate_disabled(unsigned int cpu,
278 unsigned int idlestate)
279{
Thomas Renningerf4a5d172013-11-16 14:39:22 +0100280 if (sysfs_get_idlestate_count(cpu) <= idlestate)
Thomas Renninger0924c362013-06-28 15:34:30 +0200281 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 */
298int 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
Thomas Renningerf4a5d172013-11-16 14:39:22 +0100305 if (sysfs_get_idlestate_count(cpu) <= idlestate)
Thomas Renninger0924c362013-06-28 15:34:30 +0200306 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
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200321unsigned long sysfs_get_idlestate_latency(unsigned int cpu,
Thomas Renninger0924c362013-06-28 15:34:30 +0200322 unsigned int idlestate)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200323{
324 return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);
325}
326
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200327unsigned long sysfs_get_idlestate_usage(unsigned int cpu,
328 unsigned int idlestate)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200329{
330 return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_USAGE);
331}
332
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200333unsigned long long sysfs_get_idlestate_time(unsigned int cpu,
334 unsigned int idlestate)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200335{
336 return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_TIME);
337}
338
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200339char *sysfs_get_idlestate_name(unsigned int cpu, unsigned int idlestate)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200340{
341 return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_NAME);
342}
343
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200344char *sysfs_get_idlestate_desc(unsigned int cpu, unsigned int idlestate)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200345{
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 */
Thomas Renningerf6051812013-06-28 15:34:29 +0200354unsigned int sysfs_get_idlestate_count(unsigned int cpu)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200355{
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");
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200362 if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
Prarit Bhargavaa59e5102014-12-14 09:06:38 -0500363 return 0;
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200364
365 snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200366 if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200367 return 0;
368
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200369 while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200370 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 */
384static 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
398enum cpuidle_string {
399 CPUIDLE_GOVERNOR,
400 CPUIDLE_GOVERNOR_RO,
401 CPUIDLE_DRIVER,
402 MAX_CPUIDLE_STRING_FILES
403};
404
405static 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
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200412static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200413{
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
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200421 len = sysfs_cpuidle_read_file(cpuidle_string_files[which],
422 linebuf, sizeof(linebuf));
423 if (len == 0)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200424 return NULL;
425
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200426 result = strdup(linebuf);
427 if (result == NULL)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200428 return NULL;
429
430 if (result[strlen(result) - 1] == '\n')
431 result[strlen(result) - 1] = '\0';
432
433 return result;
434}
435
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200436char *sysfs_get_cpuidle_governor(void)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200437{
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
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200445char *sysfs_get_cpuidle_driver(void)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200446{
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 */
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200457int sysfs_get_sched(const char *smt_mc)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200458{
Peter Zijlstra8e7fbcb2012-01-09 11:28:35 +0100459 return -ENODEV;
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200460}
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 */
Dominik Brodowski2cd005c2011-04-19 20:16:05 +0200468int sysfs_set_sched(const char *smt_mc, int val)
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200469{
Peter Zijlstra8e7fbcb2012-01-09 11:28:35 +0100470 return -ENODEV;
Dominik Brodowski7fe2f632011-03-30 16:30:11 +0200471}