diff options
Diffstat (limited to 'tools/power/cpupower/utils/helpers/sysfs.c')
-rw-r--r-- | tools/power/cpupower/utils/helpers/sysfs.c | 350 |
1 files changed, 350 insertions, 0 deletions
diff --git a/tools/power/cpupower/utils/helpers/sysfs.c b/tools/power/cpupower/utils/helpers/sysfs.c new file mode 100644 index 000000000000..0c534e79652b --- /dev/null +++ b/tools/power/cpupower/utils/helpers/sysfs.c @@ -0,0 +1,350 @@ +/* + * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de> + * (C) 2011 Thomas Renninger <trenn@novell.com> Novell Inc. + * + * Licensed under the terms of the GNU GPL License version 2. + */ + +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + +#include "helpers/sysfs.h" + +unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen) +{ + int fd; + size_t numread; + + if ( ( fd = open(path, O_RDONLY) ) == -1 ) + return 0; + + numread = read(fd, buf, buflen - 1); + if ( numread < 1 ) + { + close(fd); + return 0; + } + + buf[numread] = '\0'; + close(fd); + + return numread; +} + +static unsigned int sysfs_write_file(const char *path, + const char *value, size_t len) +{ + int fd; + size_t numwrite; + + if ( ( fd = open(path, O_WRONLY) ) == -1 ) + return 0; + + numwrite = write(fd, value, len); + if ( numwrite < 1 ) + { + close(fd); + return 0; + } + close(fd); + return numwrite; +} + +/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ + +/* + * helper function to read file from /sys into given buffer + * fname is a relative path under "cpuX/cpuidle/stateX/" dir + * cstates starting with 0, C0 is not counted as cstate. + * This means if you want C1 info, pass 0 as idlestate param + */ +unsigned int sysfs_idlestate_read_file(unsigned int cpu, unsigned int idlestate, + const char *fname, char *buf, size_t buflen) +{ + char path[SYSFS_PATH_MAX]; + int fd; + size_t numread; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", + cpu, idlestate, fname); + + if ( ( fd = open(path, O_RDONLY) ) == -1 ) + return 0; + + numread = read(fd, buf, buflen - 1); + if ( numread < 1 ) + { + close(fd); + return 0; + } + + buf[numread] = '\0'; + close(fd); + + return numread; +} + +/* read access to files which contain one numeric value */ + +enum idlestate_value { + IDLESTATE_USAGE, + IDLESTATE_POWER, + IDLESTATE_LATENCY, + IDLESTATE_TIME, + MAX_IDLESTATE_VALUE_FILES +}; + +static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = { + [IDLESTATE_USAGE] = "usage", + [IDLESTATE_POWER] = "power", + [IDLESTATE_LATENCY] = "latency", + [IDLESTATE_TIME] = "time", +}; + +static unsigned long long sysfs_idlestate_get_one_value(unsigned int cpu, + unsigned int idlestate, + enum idlestate_value which) +{ + unsigned long long value; + unsigned int len; + char linebuf[MAX_LINE_LEN]; + char *endp; + + if ( which >= MAX_IDLESTATE_VALUE_FILES ) + return 0; + + if ( ( len = sysfs_idlestate_read_file(cpu, idlestate, + idlestate_value_files[which], + linebuf, sizeof(linebuf))) == 0 ) + { + return 0; + } + + value = strtoull(linebuf, &endp, 0); + + if ( endp == linebuf || errno == ERANGE ) + return 0; + + return value; +} + +/* read access to files which contain one string */ + +enum idlestate_string { + IDLESTATE_DESC, + IDLESTATE_NAME, + MAX_IDLESTATE_STRING_FILES +}; + +static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = { + [IDLESTATE_DESC] = "desc", + [IDLESTATE_NAME] = "name", +}; + + +static char * sysfs_idlestate_get_one_string(unsigned int cpu, + unsigned int idlestate, + enum idlestate_string which) +{ + char linebuf[MAX_LINE_LEN]; + char *result; + unsigned int len; + + if (which >= MAX_IDLESTATE_STRING_FILES) + return NULL; + + if ( ( len = sysfs_idlestate_read_file(cpu, idlestate, + idlestate_string_files[which], + linebuf, sizeof(linebuf))) == 0 ) + return NULL; + + if ( ( result = strdup(linebuf) ) == NULL ) + return NULL; + + if (result[strlen(result) - 1] == '\n') + result[strlen(result) - 1] = '\0'; + + return result; +} + +unsigned long sysfs_get_idlestate_latency(unsigned int cpu, unsigned int idlestate) +{ + return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_LATENCY); +} + +unsigned long sysfs_get_idlestate_usage(unsigned int cpu, unsigned int idlestate) +{ + return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_USAGE); +} + +unsigned long long sysfs_get_idlestate_time(unsigned int cpu, unsigned int idlestate) +{ + return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_TIME); +} + +char * sysfs_get_idlestate_name(unsigned int cpu, unsigned int idlestate) +{ + return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_NAME); +} + +char * sysfs_get_idlestate_desc(unsigned int cpu, unsigned int idlestate) +{ + return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_DESC); +} + +/* + * Returns number of supported C-states of CPU core cpu + * Negativ in error case + * Zero if cpuidle does not export any C-states + */ +int sysfs_get_idlestate_count(unsigned int cpu) +{ + char file[SYSFS_PATH_MAX]; + struct stat statbuf; + int idlestates = 1; + + + snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle"); + if ( stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) + return -ENODEV; + + snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu); + if ( stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) + return 0; + + while(stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) { + snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU + "cpu%u/cpuidle/state%d", cpu, idlestates); + idlestates++; + } + idlestates--; + return idlestates; +} + +/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/ + +/* + * helper function to read file from /sys into given buffer + * fname is a relative path under "cpu/cpuidle/" dir + */ +static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf, + size_t buflen) +{ + char path[SYSFS_PATH_MAX]; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname); + + return sysfs_read_file(path, buf, buflen); +} + + + +/* read access to files which contain one string */ + +enum cpuidle_string { + CPUIDLE_GOVERNOR, + CPUIDLE_GOVERNOR_RO, + CPUIDLE_DRIVER, + MAX_CPUIDLE_STRING_FILES +}; + +static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = { + [CPUIDLE_GOVERNOR] = "current_governor", + [CPUIDLE_GOVERNOR_RO] = "current_governor_ro", + [CPUIDLE_DRIVER] = "current_driver", +}; + + +static char * sysfs_cpuidle_get_one_string(enum cpuidle_string which) +{ + char linebuf[MAX_LINE_LEN]; + char *result; + unsigned int len; + + if (which >= MAX_CPUIDLE_STRING_FILES) + return NULL; + + if ( ( len = sysfs_cpuidle_read_file(cpuidle_string_files[which], + linebuf, sizeof(linebuf))) == 0 ) + return NULL; + + if ( ( result = strdup(linebuf) ) == NULL ) + return NULL; + + if (result[strlen(result) - 1] == '\n') + result[strlen(result) - 1] = '\0'; + + return result; +} + +char * sysfs_get_cpuidle_governor(void) +{ + char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO); + if (!tmp) + return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR); + else + return tmp; +} + +char * sysfs_get_cpuidle_driver(void) +{ + return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER); +} +/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ + +/* + * Get sched_mc or sched_smt settings + * Pass "mc" or "smt" as argument + * + * Returns negative value on failure + */ +int sysfs_get_sched(const char* smt_mc) +{ + unsigned long value; + char linebuf[MAX_LINE_LEN]; + char *endp; + char path[SYSFS_PATH_MAX]; + + if (strcmp("mc", smt_mc) && strcmp("smt", smt_mc)) + return -EINVAL; + + snprintf(path, sizeof(path), PATH_TO_CPU "sched_%s_power_savings", smt_mc); + if (sysfs_read_file(path, linebuf, MAX_LINE_LEN) == 0 ) + return -1; + value = strtoul(linebuf, &endp, 0); + if ( endp == linebuf || errno == ERANGE ) + return -1; + return value; +} + +/* + * Get sched_mc or sched_smt settings + * Pass "mc" or "smt" as argument + * + * Returns negative value on failure + */ +int sysfs_set_sched(const char* smt_mc, int val) +{ + char linebuf[MAX_LINE_LEN]; + char path[SYSFS_PATH_MAX]; + struct stat statbuf; + + if (strcmp("mc", smt_mc) && strcmp("smt", smt_mc)) + return -EINVAL; + + snprintf(path, sizeof(path), PATH_TO_CPU "sched_%s_power_savings", smt_mc); + sprintf(linebuf, "%d", val); + + if ( stat(path, &statbuf) != 0 ) + return -ENODEV; + + if (sysfs_write_file(path, linebuf, MAX_LINE_LEN) == 0 ) + return -1; + return 0; +} |