From ac5a181d065d74fb6b213d538f743392f27bcdbd Mon Sep 17 00:00:00 2001 From: Thomas Renninger Date: Thu, 28 Apr 2016 15:24:40 +0200 Subject: cpupower: Add cpuidle parts into library This more or less is a renaming and moving of functions and should not introduce any functional change. cpupower was built from cpufrequtils (which had a C library providing easy access to cpu frequency platform info). In the meantime it got enhanced by quite some neat cpuidle userspace tools. Now the cpu idle functions have been separated and added to the cpupower.so library. So beside an already existing public header file: cpufreq.h cpupower now also exports these cpu idle functions in: cpuidle.h Here again pasted for better review of the interfaces: ====================================== int cpuidle_is_state_disabled(unsigned int cpu, unsigned int idlestate); int cpuidle_state_disable(unsigned int cpu, unsigned int idlestate, unsigned int disable); unsigned long cpuidle_state_latency(unsigned int cpu, unsigned int idlestate); unsigned long cpuidle_state_usage(unsigned int cpu, unsigned int idlestate); unsigned long long cpuidle_state_time(unsigned int cpu, unsigned int idlestate); char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate); char *cpuidle_state_desc(unsigned int cpu, unsigned int idlestate); unsigned int cpuidle_state_count(unsigned int cpu); char *cpuidle_get_governor(void); char *cpuidle_get_driver(void); ====================================== Signed-off-by: Thomas Renninger Signed-off-by: Rafael J. Wysocki --- tools/power/cpupower/lib/cpufreq.c | 550 ++++++++++++++++++++++- tools/power/cpupower/lib/cpufreq.h | 59 ++- tools/power/cpupower/lib/cpuidle.c | 380 ++++++++++++++++ tools/power/cpupower/lib/cpuidle.h | 23 + tools/power/cpupower/lib/cpupower.c | 192 +++++++++ tools/power/cpupower/lib/cpupower.h | 35 ++ tools/power/cpupower/lib/cpupower_intern.h | 5 + tools/power/cpupower/lib/sysfs.c | 672 ----------------------------- tools/power/cpupower/lib/sysfs.h | 31 -- 9 files changed, 1190 insertions(+), 757 deletions(-) create mode 100644 tools/power/cpupower/lib/cpuidle.c create mode 100644 tools/power/cpupower/lib/cpuidle.h create mode 100644 tools/power/cpupower/lib/cpupower.c create mode 100644 tools/power/cpupower/lib/cpupower.h create mode 100644 tools/power/cpupower/lib/cpupower_intern.h delete mode 100644 tools/power/cpupower/lib/sysfs.c delete mode 100644 tools/power/cpupower/lib/sysfs.h (limited to 'tools/power/cpupower/lib') diff --git a/tools/power/cpupower/lib/cpufreq.c b/tools/power/cpupower/lib/cpufreq.c index d961101d1cea..1b993fe1ce23 100644 --- a/tools/power/cpupower/lib/cpufreq.c +++ b/tools/power/cpupower/lib/cpufreq.c @@ -9,28 +9,190 @@ #include #include #include +#include +#include +#include +#include #include "cpufreq.h" -#include "sysfs.h" +#include "cpupower_intern.h" -int cpufreq_cpu_exists(unsigned int cpu) +/* CPUFREQ sysfs access **************************************************/ + +/* helper function to read file from /sys into given buffer */ +/* fname is a relative path under "cpuX/cpufreq" dir */ +static unsigned int sysfs_cpufreq_read_file(unsigned int cpu, const char *fname, + char *buf, size_t buflen) { - return sysfs_cpu_exists(cpu); + char path[SYSFS_PATH_MAX]; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s", + cpu, fname); + return sysfs_read_file(path, buf, buflen); } +/* helper function to write a new value to a /sys file */ +/* fname is a relative path under "cpuX/cpufreq" dir */ +static unsigned int sysfs_cpufreq_write_file(unsigned int cpu, + const char *fname, + const char *value, size_t len) +{ + char path[SYSFS_PATH_MAX]; + int fd; + ssize_t numwrite; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s", + cpu, fname); + + fd = open(path, O_WRONLY); + if (fd == -1) + return 0; + + numwrite = write(fd, value, len); + if (numwrite < 1) { + close(fd); + return 0; + } + + close(fd); + + return (unsigned int) numwrite; +} + +/* read access to files which contain one numeric value */ + +enum cpufreq_value { + CPUINFO_CUR_FREQ, + CPUINFO_MIN_FREQ, + CPUINFO_MAX_FREQ, + CPUINFO_LATENCY, + SCALING_CUR_FREQ, + SCALING_MIN_FREQ, + SCALING_MAX_FREQ, + STATS_NUM_TRANSITIONS, + MAX_CPUFREQ_VALUE_READ_FILES +}; + +static const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = { + [CPUINFO_CUR_FREQ] = "cpuinfo_cur_freq", + [CPUINFO_MIN_FREQ] = "cpuinfo_min_freq", + [CPUINFO_MAX_FREQ] = "cpuinfo_max_freq", + [CPUINFO_LATENCY] = "cpuinfo_transition_latency", + [SCALING_CUR_FREQ] = "scaling_cur_freq", + [SCALING_MIN_FREQ] = "scaling_min_freq", + [SCALING_MAX_FREQ] = "scaling_max_freq", + [STATS_NUM_TRANSITIONS] = "stats/total_trans" +}; + + +static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu, + enum cpufreq_value which) +{ + unsigned long value; + unsigned int len; + char linebuf[MAX_LINE_LEN]; + char *endp; + + if (which >= MAX_CPUFREQ_VALUE_READ_FILES) + return 0; + + len = sysfs_cpufreq_read_file(cpu, cpufreq_value_files[which], + linebuf, sizeof(linebuf)); + + if (len == 0) + return 0; + + value = strtoul(linebuf, &endp, 0); + + if (endp == linebuf || errno == ERANGE) + return 0; + + return value; +} + +/* read access to files which contain one string */ + +enum cpufreq_string { + SCALING_DRIVER, + SCALING_GOVERNOR, + MAX_CPUFREQ_STRING_FILES +}; + +static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = { + [SCALING_DRIVER] = "scaling_driver", + [SCALING_GOVERNOR] = "scaling_governor", +}; + + +static char *sysfs_cpufreq_get_one_string(unsigned int cpu, + enum cpufreq_string which) +{ + char linebuf[MAX_LINE_LEN]; + char *result; + unsigned int len; + + if (which >= MAX_CPUFREQ_STRING_FILES) + return NULL; + + len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which], + linebuf, sizeof(linebuf)); + if (len == 0) + return NULL; + + result = strdup(linebuf); + if (result == NULL) + return NULL; + + if (result[strlen(result) - 1] == '\n') + result[strlen(result) - 1] = '\0'; + + return result; +} + +/* write access */ + +enum cpufreq_write { + WRITE_SCALING_MIN_FREQ, + WRITE_SCALING_MAX_FREQ, + WRITE_SCALING_GOVERNOR, + WRITE_SCALING_SET_SPEED, + MAX_CPUFREQ_WRITE_FILES +}; + +static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = { + [WRITE_SCALING_MIN_FREQ] = "scaling_min_freq", + [WRITE_SCALING_MAX_FREQ] = "scaling_max_freq", + [WRITE_SCALING_GOVERNOR] = "scaling_governor", + [WRITE_SCALING_SET_SPEED] = "scaling_setspeed", +}; + +static int sysfs_cpufreq_write_one_value(unsigned int cpu, + enum cpufreq_write which, + const char *new_value, size_t len) +{ + if (which >= MAX_CPUFREQ_WRITE_FILES) + return 0; + + if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which], + new_value, len) != len) + return -ENODEV; + + return 0; +}; + unsigned long cpufreq_get_freq_kernel(unsigned int cpu) { - return sysfs_get_freq_kernel(cpu); + return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ); } unsigned long cpufreq_get_freq_hardware(unsigned int cpu) { - return sysfs_get_freq_hardware(cpu); + return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ); } unsigned long cpufreq_get_transition_latency(unsigned int cpu) { - return sysfs_get_freq_transition_latency(cpu); + return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY); } int cpufreq_get_hardware_limits(unsigned int cpu, @@ -39,12 +201,21 @@ int cpufreq_get_hardware_limits(unsigned int cpu, { if ((!min) || (!max)) return -EINVAL; - return sysfs_get_freq_hardware_limits(cpu, min, max); + + *min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ); + if (!*min) + return -ENODEV; + + *max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ); + if (!*max) + return -ENODEV; + + return 0; } char *cpufreq_get_driver(unsigned int cpu) { - return sysfs_get_freq_driver(cpu); + return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER); } void cpufreq_put_driver(char *ptr) @@ -56,7 +227,26 @@ void cpufreq_put_driver(char *ptr) struct cpufreq_policy *cpufreq_get_policy(unsigned int cpu) { - return sysfs_get_freq_policy(cpu); + struct cpufreq_policy *policy; + + policy = malloc(sizeof(struct cpufreq_policy)); + if (!policy) + return NULL; + + policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR); + if (!policy->governor) { + free(policy); + return NULL; + } + policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ); + policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ); + if ((!policy->min) || (!policy->max)) { + free(policy->governor); + free(policy); + return NULL; + } + + return policy; } void cpufreq_put_policy(struct cpufreq_policy *policy) @@ -72,7 +262,57 @@ void cpufreq_put_policy(struct cpufreq_policy *policy) struct cpufreq_available_governors *cpufreq_get_available_governors(unsigned int cpu) { - return sysfs_get_freq_available_governors(cpu); + struct cpufreq_available_governors *first = NULL; + struct cpufreq_available_governors *current = NULL; + char linebuf[MAX_LINE_LEN]; + unsigned int pos, i; + unsigned int len; + + len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors", + linebuf, sizeof(linebuf)); + if (len == 0) + return NULL; + + pos = 0; + for (i = 0; i < len; i++) { + if (linebuf[i] == ' ' || linebuf[i] == '\n') { + if (i - pos < 2) + continue; + if (current) { + current->next = malloc(sizeof(*current)); + if (!current->next) + goto error_out; + current = current->next; + } else { + first = malloc(sizeof(*first)); + if (!first) + goto error_out; + current = first; + } + current->first = first; + current->next = NULL; + + current->governor = malloc(i - pos + 1); + if (!current->governor) + goto error_out; + + memcpy(current->governor, linebuf + pos, i - pos); + current->governor[i - pos] = '\0'; + pos = i + 1; + } + } + + return first; + + error_out: + while (first) { + current = first->next; + if (first->governor) + free(first->governor); + free(first); + first = current; + } + return NULL; } void cpufreq_put_available_governors(struct cpufreq_available_governors *any) @@ -96,7 +336,57 @@ void cpufreq_put_available_governors(struct cpufreq_available_governors *any) struct cpufreq_available_frequencies *cpufreq_get_available_frequencies(unsigned int cpu) { - return sysfs_get_available_frequencies(cpu); + struct cpufreq_available_frequencies *first = NULL; + struct cpufreq_available_frequencies *current = NULL; + char one_value[SYSFS_PATH_MAX]; + char linebuf[MAX_LINE_LEN]; + unsigned int pos, i; + unsigned int len; + + len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies", + linebuf, sizeof(linebuf)); + if (len == 0) + return NULL; + + pos = 0; + for (i = 0; i < len; i++) { + if (linebuf[i] == ' ' || linebuf[i] == '\n') { + if (i - pos < 2) + continue; + if (i - pos >= SYSFS_PATH_MAX) + goto error_out; + if (current) { + current->next = malloc(sizeof(*current)); + if (!current->next) + goto error_out; + current = current->next; + } else { + first = malloc(sizeof(*first)); + if (!first) + goto error_out; + current = first; + } + current->first = first; + current->next = NULL; + + memcpy(one_value, linebuf + pos, i - pos); + one_value[i - pos] = '\0'; + if (sscanf(one_value, "%lu", ¤t->frequency) != 1) + goto error_out; + + pos = i + 1; + } + } + + return first; + + error_out: + while (first) { + current = first->next; + free(first); + first = current; + } + return NULL; } void cpufreq_put_available_frequencies(struct cpufreq_available_frequencies @@ -114,10 +404,65 @@ void cpufreq_put_available_frequencies(struct cpufreq_available_frequencies } } +static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu, + const char *file) +{ + struct cpufreq_affected_cpus *first = NULL; + struct cpufreq_affected_cpus *current = NULL; + char one_value[SYSFS_PATH_MAX]; + char linebuf[MAX_LINE_LEN]; + unsigned int pos, i; + unsigned int len; + + len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf)); + if (len == 0) + return NULL; + + pos = 0; + for (i = 0; i < len; i++) { + if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') { + if (i - pos < 1) + continue; + if (i - pos >= SYSFS_PATH_MAX) + goto error_out; + if (current) { + current->next = malloc(sizeof(*current)); + if (!current->next) + goto error_out; + current = current->next; + } else { + first = malloc(sizeof(*first)); + if (!first) + goto error_out; + current = first; + } + current->first = first; + current->next = NULL; + + memcpy(one_value, linebuf + pos, i - pos); + one_value[i - pos] = '\0'; + + if (sscanf(one_value, "%u", ¤t->cpu) != 1) + goto error_out; + + pos = i + 1; + } + } + + return first; + + error_out: + while (first) { + current = first->next; + free(first); + first = current; + } + return NULL; +} struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu) { - return sysfs_get_freq_affected_cpus(cpu); + return sysfs_get_cpu_list(cpu, "affected_cpus"); } void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *any) @@ -138,7 +483,7 @@ void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *any) struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu) { - return sysfs_get_freq_related_cpus(cpu); + return sysfs_get_cpu_list(cpu, "related_cpus"); } void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any) @@ -146,45 +491,208 @@ void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any) cpufreq_put_affected_cpus(any); } +static int verify_gov(char *new_gov, char *passed_gov) +{ + unsigned int i, j = 0; + + if (!passed_gov || (strlen(passed_gov) > 19)) + return -EINVAL; + + strncpy(new_gov, passed_gov, 20); + for (i = 0; i < 20; i++) { + if (j) { + new_gov[i] = '\0'; + continue; + } + if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z')) + continue; + + if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z')) + continue; + + if (new_gov[i] == '-') + continue; + + if (new_gov[i] == '_') + continue; + + if (new_gov[i] == '\0') { + j = 1; + continue; + } + return -EINVAL; + } + new_gov[19] = '\0'; + return 0; +} int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy) { + char min[SYSFS_PATH_MAX]; + char max[SYSFS_PATH_MAX]; + char gov[SYSFS_PATH_MAX]; + int ret; + unsigned long old_min; + int write_max_first; + if (!policy || !(policy->governor)) return -EINVAL; - return sysfs_set_freq_policy(cpu, policy); + if (policy->max < policy->min) + return -EINVAL; + + if (verify_gov(gov, policy->governor)) + return -EINVAL; + + snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min); + snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max); + + old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ); + write_max_first = (old_min && (policy->max < old_min) ? 0 : 1); + + if (write_max_first) { + ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, + max, strlen(max)); + if (ret) + return ret; + } + + ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min, + strlen(min)); + if (ret) + return ret; + + if (!write_max_first) { + ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, + max, strlen(max)); + if (ret) + return ret; + } + + return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR, + gov, strlen(gov)); } int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq) { - return sysfs_modify_freq_policy_min(cpu, min_freq); + char value[SYSFS_PATH_MAX]; + + snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq); + + return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, + value, strlen(value)); } int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq) { - return sysfs_modify_freq_policy_max(cpu, max_freq); -} + char value[SYSFS_PATH_MAX]; + + snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq); + return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, + value, strlen(value)); +} int cpufreq_modify_policy_governor(unsigned int cpu, char *governor) { + char new_gov[SYSFS_PATH_MAX]; + if ((!governor) || (strlen(governor) > 19)) return -EINVAL; - return sysfs_modify_freq_policy_governor(cpu, governor); + if (verify_gov(new_gov, governor)) + return -EINVAL; + + return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR, + new_gov, strlen(new_gov)); } int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency) { - return sysfs_set_frequency(cpu, target_frequency); + struct cpufreq_policy *pol = cpufreq_get_policy(cpu); + char userspace_gov[] = "userspace"; + char freq[SYSFS_PATH_MAX]; + int ret; + + if (!pol) + return -ENODEV; + + if (strncmp(pol->governor, userspace_gov, 9) != 0) { + ret = cpufreq_modify_policy_governor(cpu, userspace_gov); + if (ret) { + cpufreq_put_policy(pol); + return ret; + } + } + + cpufreq_put_policy(pol); + + snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency); + + return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED, + freq, strlen(freq)); } struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu, unsigned long long *total_time) { - return sysfs_get_freq_stats(cpu, total_time); + struct cpufreq_stats *first = NULL; + struct cpufreq_stats *current = NULL; + char one_value[SYSFS_PATH_MAX]; + char linebuf[MAX_LINE_LEN]; + unsigned int pos, i; + unsigned int len; + + len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state", + linebuf, sizeof(linebuf)); + if (len == 0) + return NULL; + + *total_time = 0; + pos = 0; + for (i = 0; i < len; i++) { + if (i == strlen(linebuf) || linebuf[i] == '\n') { + if (i - pos < 2) + continue; + if ((i - pos) >= SYSFS_PATH_MAX) + goto error_out; + if (current) { + current->next = malloc(sizeof(*current)); + if (!current->next) + goto error_out; + current = current->next; + } else { + first = malloc(sizeof(*first)); + if (!first) + goto error_out; + current = first; + } + current->first = first; + current->next = NULL; + + memcpy(one_value, linebuf + pos, i - pos); + one_value[i - pos] = '\0'; + if (sscanf(one_value, "%lu %llu", + ¤t->frequency, + ¤t->time_in_state) != 2) + goto error_out; + + *total_time = *total_time + current->time_in_state; + pos = i + 1; + } + } + + return first; + + error_out: + while (first) { + current = first->next; + free(first); + first = current; + } + return NULL; } void cpufreq_put_stats(struct cpufreq_stats *any) @@ -204,5 +712,5 @@ void cpufreq_put_stats(struct cpufreq_stats *any) unsigned long cpufreq_get_transitions(unsigned int cpu) { - return sysfs_get_freq_transitions(cpu); + return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS); } diff --git a/tools/power/cpupower/lib/cpufreq.h b/tools/power/cpupower/lib/cpufreq.h index 3aae8e7a0839..3b005c39f068 100644 --- a/tools/power/cpupower/lib/cpufreq.h +++ b/tools/power/cpupower/lib/cpufreq.h @@ -17,8 +17,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _CPUFREQ_H -#define _CPUFREQ_H 1 +#ifndef __CPUPOWER_CPUFREQ_H__ +#define __CPUPOWER_CPUFREQ_H__ struct cpufreq_policy { unsigned long min; @@ -58,13 +58,6 @@ struct cpufreq_stats { extern "C" { #endif -/* - * returns 0 if the specified CPU is present (it doesn't say - * whether it is online!), and an error value if not. - */ - -extern int cpufreq_cpu_exists(unsigned int cpu); - /* determine current CPU frequency * - _kernel variant means kernel's opinion of CPU frequency * - _hardware variant means actual hardware CPU frequency, @@ -73,9 +66,9 @@ extern int cpufreq_cpu_exists(unsigned int cpu); * returns 0 on failure, else frequency in kHz. */ -extern unsigned long cpufreq_get_freq_kernel(unsigned int cpu); +unsigned long cpufreq_get_freq_kernel(unsigned int cpu); -extern unsigned long cpufreq_get_freq_hardware(unsigned int cpu); +unsigned long cpufreq_get_freq_hardware(unsigned int cpu); #define cpufreq_get(cpu) cpufreq_get_freq_kernel(cpu); @@ -84,7 +77,7 @@ extern unsigned long cpufreq_get_freq_hardware(unsigned int cpu); * * returns 0 on failure, else transition latency in 10^(-9) s = nanoseconds */ -extern unsigned long cpufreq_get_transition_latency(unsigned int cpu); +unsigned long cpufreq_get_transition_latency(unsigned int cpu); /* determine hardware CPU frequency limits @@ -93,7 +86,7 @@ extern unsigned long cpufreq_get_transition_latency(unsigned int cpu); * considerations by cpufreq policy notifiers in the kernel. */ -extern int cpufreq_get_hardware_limits(unsigned int cpu, +int cpufreq_get_hardware_limits(unsigned int cpu, unsigned long *min, unsigned long *max); @@ -104,9 +97,9 @@ extern int cpufreq_get_hardware_limits(unsigned int cpu, * to avoid memory leakage, please. */ -extern char *cpufreq_get_driver(unsigned int cpu); +char *cpufreq_get_driver(unsigned int cpu); -extern void cpufreq_put_driver(char *ptr); +void cpufreq_put_driver(char *ptr); /* determine CPUfreq policy currently used @@ -116,9 +109,9 @@ extern void cpufreq_put_driver(char *ptr); */ -extern struct cpufreq_policy *cpufreq_get_policy(unsigned int cpu); +struct cpufreq_policy *cpufreq_get_policy(unsigned int cpu); -extern void cpufreq_put_policy(struct cpufreq_policy *policy); +void cpufreq_put_policy(struct cpufreq_policy *policy); /* determine CPUfreq governors currently available @@ -129,10 +122,10 @@ extern void cpufreq_put_policy(struct cpufreq_policy *policy); */ -extern struct cpufreq_available_governors +struct cpufreq_available_governors *cpufreq_get_available_governors(unsigned int cpu); -extern void cpufreq_put_available_governors( +void cpufreq_put_available_governors( struct cpufreq_available_governors *first); @@ -143,10 +136,10 @@ extern void cpufreq_put_available_governors( * cpufreq_put_available_frequencies after use. */ -extern struct cpufreq_available_frequencies +struct cpufreq_available_frequencies *cpufreq_get_available_frequencies(unsigned int cpu); -extern void cpufreq_put_available_frequencies( +void cpufreq_put_available_frequencies( struct cpufreq_available_frequencies *first); @@ -156,10 +149,10 @@ extern void cpufreq_put_available_frequencies( * to avoid memory leakage, please. */ -extern struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned +struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu); -extern void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *first); +void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *first); /* determine related CPUs @@ -168,10 +161,10 @@ extern void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *first); * to avoid memory leakage, please. */ -extern struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned +struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu); -extern void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *first); +void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *first); /* determine stats for cpufreq subsystem @@ -179,12 +172,12 @@ extern void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *first); * This is not available in all kernel versions or configurations. */ -extern struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu, +struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu, unsigned long long *total_time); -extern void cpufreq_put_stats(struct cpufreq_stats *stats); +void cpufreq_put_stats(struct cpufreq_stats *stats); -extern unsigned long cpufreq_get_transitions(unsigned int cpu); +unsigned long cpufreq_get_transitions(unsigned int cpu); /* set new cpufreq policy @@ -193,7 +186,7 @@ extern unsigned long cpufreq_get_transitions(unsigned int cpu); * but results may differ depending e.g. on governors being available. */ -extern int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy); +int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy); /* modify a policy by only changing min/max freq or governor @@ -201,9 +194,9 @@ extern int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy); * Does not check whether result is what was intended. */ -extern int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq); -extern int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq); -extern int cpufreq_modify_policy_governor(unsigned int cpu, char *governor); +int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq); +int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq); +int cpufreq_modify_policy_governor(unsigned int cpu, char *governor); /* set a specific frequency @@ -213,7 +206,7 @@ extern int cpufreq_modify_policy_governor(unsigned int cpu, char *governor); * occurs. Also does not work on ->range() cpufreq drivers. */ -extern int cpufreq_set_frequency(unsigned int cpu, +int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency); #ifdef __cplusplus diff --git a/tools/power/cpupower/lib/cpuidle.c b/tools/power/cpupower/lib/cpuidle.c new file mode 100644 index 000000000000..9bd4c7655fdb --- /dev/null +++ b/tools/power/cpupower/lib/cpuidle.c @@ -0,0 +1,380 @@ +/* + * (C) 2004-2009 Dominik Brodowski + * (C) 2011 Thomas Renninger Novell Inc. + * + * Licensed under the terms of the GNU GPL License version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpuidle.h" +#include "cpupower_intern.h" + +/* + * helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir + * exists. + * For example the functionality to disable c-states was introduced in later + * kernel versions, this function can be used to explicitly check for this + * feature. + * + * returns 1 if the file exists, 0 otherwise. + */ +static +unsigned int cpuidle_state_file_exists(unsigned int cpu, + unsigned int idlestate, + const char *fname) +{ + char path[SYSFS_PATH_MAX]; + struct stat statbuf; + + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", + cpu, idlestate, fname); + if (stat(path, &statbuf) != 0) + return 0; + return 1; +} + +/* + * 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 + */ +static +unsigned int cpuidle_state_read_file(unsigned int cpu, + unsigned int idlestate, + const char *fname, char *buf, + size_t buflen) +{ + char path[SYSFS_PATH_MAX]; + int fd; + ssize_t numread; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", + cpu, idlestate, fname); + + fd = open(path, O_RDONLY); + if (fd == -1) + return 0; + + numread = read(fd, buf, buflen - 1); + if (numread < 1) { + close(fd); + return 0; + } + + buf[numread] = '\0'; + close(fd); + + return (unsigned int) numread; +} + +/* + * helper function to write a new value to a /sys file + * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir + * + * Returns the number of bytes written or 0 on error + */ +static +unsigned int cpuidle_state_write_file(unsigned int cpu, + unsigned int idlestate, + const char *fname, + const char *value, size_t len) +{ + char path[SYSFS_PATH_MAX]; + int fd; + ssize_t numwrite; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s", + cpu, idlestate, fname); + + fd = open(path, O_WRONLY); + if (fd == -1) + return 0; + + numwrite = write(fd, value, len); + if (numwrite < 1) { + close(fd); + return 0; + } + + close(fd); + + return (unsigned int) numwrite; +} + +/* read access to files which contain one numeric value */ + +enum idlestate_value { + IDLESTATE_USAGE, + IDLESTATE_POWER, + IDLESTATE_LATENCY, + IDLESTATE_TIME, + IDLESTATE_DISABLE, + 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", + [IDLESTATE_DISABLE] = "disable", +}; + +static +unsigned long long cpuidle_state_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; + + len = cpuidle_state_read_file(cpu, idlestate, + idlestate_value_files[which], + linebuf, sizeof(linebuf)); + if (len == 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 *cpuidle_state_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; + + len = cpuidle_state_read_file(cpu, idlestate, + idlestate_string_files[which], + linebuf, sizeof(linebuf)); + if (len == 0) + return NULL; + + result = strdup(linebuf); + if (result == NULL) + return NULL; + + if (result[strlen(result) - 1] == '\n') + result[strlen(result) - 1] = '\0'; + + return result; +} + +/* + * Returns: + * 1 if disabled + * 0 if enabled + * -1 if idlestate is not available + * -2 if disabling is not supported by the kernel + */ +int cpuidle_is_state_disabled(unsigned int cpu, + unsigned int idlestate) +{ + if (cpuidle_state_count(cpu) <= idlestate) + return -1; + + if (!cpuidle_state_file_exists(cpu, idlestate, + idlestate_value_files[IDLESTATE_DISABLE])) + return -2; + return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_DISABLE); +} + +/* + * Pass 1 as last argument to disable or 0 to enable the state + * Returns: + * 0 on success + * negative values on error, for example: + * -1 if idlestate is not available + * -2 if disabling is not supported by the kernel + * -3 No write access to disable/enable C-states + */ +int cpuidle_state_disable(unsigned int cpu, + unsigned int idlestate, + unsigned int disable) +{ + char value[SYSFS_PATH_MAX]; + int bytes_written; + + if (cpuidle_state_count(cpu) <= idlestate) + return -1; + + if (!cpuidle_state_file_exists(cpu, idlestate, + idlestate_value_files[IDLESTATE_DISABLE])) + return -2; + + snprintf(value, SYSFS_PATH_MAX, "%u", disable); + + bytes_written = cpuidle_state_write_file(cpu, idlestate, "disable", + value, sizeof(disable)); + if (bytes_written) + return 0; + return -3; +} + +unsigned long cpuidle_state_latency(unsigned int cpu, + unsigned int idlestate) +{ + return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_LATENCY); +} + +unsigned long cpuidle_state_usage(unsigned int cpu, + unsigned int idlestate) +{ + return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_USAGE); +} + +unsigned long long cpuidle_state_time(unsigned int cpu, + unsigned int idlestate) +{ + return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_TIME); +} + +char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate) +{ + return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME); +} + +char *cpuidle_state_desc(unsigned int cpu, unsigned int idlestate) +{ + return cpuidle_state_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 + */ +unsigned int cpuidle_state_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 0; + + 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; + + len = sysfs_cpuidle_read_file(cpuidle_string_files[which], + linebuf, sizeof(linebuf)); + if (len == 0) + return NULL; + + result = strdup(linebuf); + if (result == NULL) + return NULL; + + if (result[strlen(result) - 1] == '\n') + result[strlen(result) - 1] = '\0'; + + return result; +} + +char *cpuidle_get_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 *cpuidle_get_driver(void) +{ + return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER); +} +/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */ diff --git a/tools/power/cpupower/lib/cpuidle.h b/tools/power/cpupower/lib/cpuidle.h new file mode 100644 index 000000000000..04eb3cfa6e42 --- /dev/null +++ b/tools/power/cpupower/lib/cpuidle.h @@ -0,0 +1,23 @@ +#ifndef __CPUPOWER_CPUIDLE_H__ +#define __CPUPOWER_CPUIDLE_H__ + +int cpuidle_is_state_disabled(unsigned int cpu, + unsigned int idlestate); +int cpuidle_state_disable(unsigned int cpu, unsigned int idlestate, + unsigned int disable); +unsigned long cpuidle_state_latency(unsigned int cpu, + unsigned int idlestate); +unsigned long cpuidle_state_usage(unsigned int cpu, + unsigned int idlestate); +unsigned long long cpuidle_state_time(unsigned int cpu, + unsigned int idlestate); +char *cpuidle_state_name(unsigned int cpu, + unsigned int idlestate); +char *cpuidle_state_desc(unsigned int cpu, + unsigned int idlestate); +unsigned int cpuidle_state_count(unsigned int cpu); + +char *cpuidle_get_governor(void); +char *cpuidle_get_driver(void); + +#endif /* __CPUPOWER_HELPERS_SYSFS_H__ */ diff --git a/tools/power/cpupower/lib/cpupower.c b/tools/power/cpupower/lib/cpupower.c new file mode 100644 index 000000000000..9c395ec924de --- /dev/null +++ b/tools/power/cpupower/lib/cpupower.c @@ -0,0 +1,192 @@ +/* + * (C) 2004-2009 Dominik Brodowski + * + * Licensed under the terms of the GNU GPL License version 2. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "cpupower.h" +#include "cpupower_intern.h" + +unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen) +{ + int fd; + ssize_t numread; + + fd = open(path, O_RDONLY); + if (fd == -1) + return 0; + + numread = read(fd, buf, buflen - 1); + if (numread < 1) { + close(fd); + return 0; + } + + buf[numread] = '\0'; + close(fd); + + return (unsigned int) numread; +} + +/* + * Detect whether a CPU is online + * + * Returns: + * 1 -> if CPU is online + * 0 -> if CPU is offline + * negative errno values in error case + */ +int cpupower_is_cpu_online(unsigned int cpu) +{ + char path[SYSFS_PATH_MAX]; + int fd; + ssize_t numread; + unsigned long long value; + char linebuf[MAX_LINE_LEN]; + char *endp; + struct stat statbuf; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu); + + if (stat(path, &statbuf) != 0) + return 0; + + /* + * kernel without CONFIG_HOTPLUG_CPU + * -> cpuX directory exists, but not cpuX/online file + */ + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu); + if (stat(path, &statbuf) != 0) + return 1; + + fd = open(path, O_RDONLY); + if (fd == -1) + return -errno; + + numread = read(fd, linebuf, MAX_LINE_LEN - 1); + if (numread < 1) { + close(fd); + return -EIO; + } + linebuf[numread] = '\0'; + close(fd); + + value = strtoull(linebuf, &endp, 0); + if (value > 1) + return -EINVAL; + + return value; +} + +/* returns -1 on failure, 0 on success */ +static int sysfs_topology_read_file(unsigned int cpu, const char *fname, int *result) +{ + char linebuf[MAX_LINE_LEN]; + char *endp; + char path[SYSFS_PATH_MAX]; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/topology/%s", + cpu, fname); + if (sysfs_read_file(path, linebuf, MAX_LINE_LEN) == 0) + return -1; + *result = strtol(linebuf, &endp, 0); + if (endp == linebuf || errno == ERANGE) + return -1; + return 0; +} + +static int __compare(const void *t1, const void *t2) +{ + struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1; + struct cpuid_core_info *top2 = (struct cpuid_core_info *)t2; + if (top1->pkg < top2->pkg) + return -1; + else if (top1->pkg > top2->pkg) + return 1; + else if (top1->core < top2->core) + return -1; + else if (top1->core > top2->core) + return 1; + else if (top1->cpu < top2->cpu) + return -1; + else if (top1->cpu > top2->cpu) + return 1; + else + return 0; +} + +/* + * Returns amount of cpus, negative on error, cpu_top must be + * passed to cpu_topology_release to free resources + * + * Array is sorted after ->pkg, ->core, then ->cpu + */ +int get_cpu_topology(struct cpupower_topology *cpu_top) +{ + int cpu, last_pkg, cpus = sysconf(_SC_NPROCESSORS_CONF); + + cpu_top->core_info = malloc(sizeof(struct cpuid_core_info) * cpus); + if (cpu_top->core_info == NULL) + return -ENOMEM; + cpu_top->pkgs = cpu_top->cores = 0; + for (cpu = 0; cpu < cpus; cpu++) { + cpu_top->core_info[cpu].cpu = cpu; + cpu_top->core_info[cpu].is_online = cpupower_is_cpu_online(cpu); + if(sysfs_topology_read_file( + cpu, + "physical_package_id", + &(cpu_top->core_info[cpu].pkg)) < 0) { + cpu_top->core_info[cpu].pkg = -1; + cpu_top->core_info[cpu].core = -1; + continue; + } + if(sysfs_topology_read_file( + cpu, + "core_id", + &(cpu_top->core_info[cpu].core)) < 0) { + cpu_top->core_info[cpu].pkg = -1; + cpu_top->core_info[cpu].core = -1; + continue; + } + } + + qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info), + __compare); + + /* Count the number of distinct pkgs values. This works + because the primary sort of the core_info struct was just + done by pkg value. */ + last_pkg = cpu_top->core_info[0].pkg; + for(cpu = 1; cpu < cpus; cpu++) { + if (cpu_top->core_info[cpu].pkg != last_pkg && + cpu_top->core_info[cpu].pkg != -1) { + + last_pkg = cpu_top->core_info[cpu].pkg; + cpu_top->pkgs++; + } + } + if (!(cpu_top->core_info[0].pkg == -1)) + cpu_top->pkgs++; + + /* Intel's cores count is not consecutively numbered, there may + * be a core_id of 3, but none of 2. Assume there always is 0 + * Get amount of cores by counting duplicates in a package + for (cpu = 0; cpu_top->core_info[cpu].pkg = 0 && cpu < cpus; cpu++) { + if (cpu_top->core_info[cpu].core == 0) + cpu_top->cores++; + */ + return cpus; +} + +void cpu_topology_release(struct cpupower_topology cpu_top) +{ + free(cpu_top.core_info); +} diff --git a/tools/power/cpupower/lib/cpupower.h b/tools/power/cpupower/lib/cpupower.h new file mode 100644 index 000000000000..fa031fcc7710 --- /dev/null +++ b/tools/power/cpupower/lib/cpupower.h @@ -0,0 +1,35 @@ +#ifndef __CPUPOWER_CPUPOWER_H__ +#define __CPUPOWER_CPUPOWER_H__ + +struct cpupower_topology { + /* Amount of CPU cores, packages and threads per core in the system */ + unsigned int cores; + unsigned int pkgs; + unsigned int threads; /* per core */ + + /* Array gets mallocated with cores entries, holding per core info */ + struct cpuid_core_info *core_info; +}; + +struct cpuid_core_info { + int pkg; + int core; + int cpu; + + /* flags */ + unsigned int is_online:1; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +int get_cpu_topology(struct cpupower_topology *cpu_top); +void cpu_topology_release(struct cpupower_topology cpu_top); +int cpupower_is_cpu_online(unsigned int cpu); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/tools/power/cpupower/lib/cpupower_intern.h b/tools/power/cpupower/lib/cpupower_intern.h new file mode 100644 index 000000000000..f8ec4009621c --- /dev/null +++ b/tools/power/cpupower/lib/cpupower_intern.h @@ -0,0 +1,5 @@ +#define PATH_TO_CPU "/sys/devices/system/cpu/" +#define MAX_LINE_LEN 4096 +#define SYSFS_PATH_MAX 255 + +unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen); diff --git a/tools/power/cpupower/lib/sysfs.c b/tools/power/cpupower/lib/sysfs.c deleted file mode 100644 index 870713a75a81..000000000000 --- a/tools/power/cpupower/lib/sysfs.c +++ /dev/null @@ -1,672 +0,0 @@ -/* - * (C) 2004-2009 Dominik Brodowski - * - * Licensed under the terms of the GNU GPL License version 2. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "cpufreq.h" - -#define PATH_TO_CPU "/sys/devices/system/cpu/" -#define MAX_LINE_LEN 4096 -#define SYSFS_PATH_MAX 255 - - -static unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen) -{ - int fd; - ssize_t numread; - - fd = open(path, O_RDONLY); - if (fd == -1) - return 0; - - numread = read(fd, buf, buflen - 1); - if (numread < 1) { - close(fd); - return 0; - } - - buf[numread] = '\0'; - close(fd); - - return (unsigned int) numread; -} - - -/* CPUFREQ sysfs access **************************************************/ - -/* helper function to read file from /sys into given buffer */ -/* fname is a relative path under "cpuX/cpufreq" dir */ -static unsigned int sysfs_cpufreq_read_file(unsigned int cpu, const char *fname, - char *buf, size_t buflen) -{ - char path[SYSFS_PATH_MAX]; - - snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s", - cpu, fname); - return sysfs_read_file(path, buf, buflen); -} - -/* helper function to write a new value to a /sys file */ -/* fname is a relative path under "cpuX/cpufreq" dir */ -static unsigned int sysfs_cpufreq_write_file(unsigned int cpu, - const char *fname, - const char *value, size_t len) -{ - char path[SYSFS_PATH_MAX]; - int fd; - ssize_t numwrite; - - snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s", - cpu, fname); - - fd = open(path, O_WRONLY); - if (fd == -1) - return 0; - - numwrite = write(fd, value, len); - if (numwrite < 1) { - close(fd); - return 0; - } - - close(fd); - - return (unsigned int) numwrite; -} - -/* read access to files which contain one numeric value */ - -enum cpufreq_value { - CPUINFO_CUR_FREQ, - CPUINFO_MIN_FREQ, - CPUINFO_MAX_FREQ, - CPUINFO_LATENCY, - SCALING_CUR_FREQ, - SCALING_MIN_FREQ, - SCALING_MAX_FREQ, - STATS_NUM_TRANSITIONS, - MAX_CPUFREQ_VALUE_READ_FILES -}; - -static const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = { - [CPUINFO_CUR_FREQ] = "cpuinfo_cur_freq", - [CPUINFO_MIN_FREQ] = "cpuinfo_min_freq", - [CPUINFO_MAX_FREQ] = "cpuinfo_max_freq", - [CPUINFO_LATENCY] = "cpuinfo_transition_latency", - [SCALING_CUR_FREQ] = "scaling_cur_freq", - [SCALING_MIN_FREQ] = "scaling_min_freq", - [SCALING_MAX_FREQ] = "scaling_max_freq", - [STATS_NUM_TRANSITIONS] = "stats/total_trans" -}; - - -static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu, - enum cpufreq_value which) -{ - unsigned long value; - unsigned int len; - char linebuf[MAX_LINE_LEN]; - char *endp; - - if (which >= MAX_CPUFREQ_VALUE_READ_FILES) - return 0; - - len = sysfs_cpufreq_read_file(cpu, cpufreq_value_files[which], - linebuf, sizeof(linebuf)); - - if (len == 0) - return 0; - - value = strtoul(linebuf, &endp, 0); - - if (endp == linebuf || errno == ERANGE) - return 0; - - return value; -} - -/* read access to files which contain one string */ - -enum cpufreq_string { - SCALING_DRIVER, - SCALING_GOVERNOR, - MAX_CPUFREQ_STRING_FILES -}; - -static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = { - [SCALING_DRIVER] = "scaling_driver", - [SCALING_GOVERNOR] = "scaling_governor", -}; - - -static char *sysfs_cpufreq_get_one_string(unsigned int cpu, - enum cpufreq_string which) -{ - char linebuf[MAX_LINE_LEN]; - char *result; - unsigned int len; - - if (which >= MAX_CPUFREQ_STRING_FILES) - return NULL; - - len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which], - linebuf, sizeof(linebuf)); - if (len == 0) - return NULL; - - result = strdup(linebuf); - if (result == NULL) - return NULL; - - if (result[strlen(result) - 1] == '\n') - result[strlen(result) - 1] = '\0'; - - return result; -} - -/* write access */ - -enum cpufreq_write { - WRITE_SCALING_MIN_FREQ, - WRITE_SCALING_MAX_FREQ, - WRITE_SCALING_GOVERNOR, - WRITE_SCALING_SET_SPEED, - MAX_CPUFREQ_WRITE_FILES -}; - -static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = { - [WRITE_SCALING_MIN_FREQ] = "scaling_min_freq", - [WRITE_SCALING_MAX_FREQ] = "scaling_max_freq", - [WRITE_SCALING_GOVERNOR] = "scaling_governor", - [WRITE_SCALING_SET_SPEED] = "scaling_setspeed", -}; - -static int sysfs_cpufreq_write_one_value(unsigned int cpu, - enum cpufreq_write which, - const char *new_value, size_t len) -{ - if (which >= MAX_CPUFREQ_WRITE_FILES) - return 0; - - if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which], - new_value, len) != len) - return -ENODEV; - - return 0; -}; - -unsigned long sysfs_get_freq_kernel(unsigned int cpu) -{ - return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ); -} - -unsigned long sysfs_get_freq_hardware(unsigned int cpu) -{ - return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ); -} - -unsigned long sysfs_get_freq_transition_latency(unsigned int cpu) -{ - return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY); -} - -int sysfs_get_freq_hardware_limits(unsigned int cpu, - unsigned long *min, - unsigned long *max) -{ - if ((!min) || (!max)) - return -EINVAL; - - *min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ); - if (!*min) - return -ENODEV; - - *max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ); - if (!*max) - return -ENODEV; - - return 0; -} - -char *sysfs_get_freq_driver(unsigned int cpu) -{ - return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER); -} - -struct cpufreq_policy *sysfs_get_freq_policy(unsigned int cpu) -{ - struct cpufreq_policy *policy; - - policy = malloc(sizeof(struct cpufreq_policy)); - if (!policy) - return NULL; - - policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR); - if (!policy->governor) { - free(policy); - return NULL; - } - policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ); - policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ); - if ((!policy->min) || (!policy->max)) { - free(policy->governor); - free(policy); - return NULL; - } - - return policy; -} - -struct cpufreq_available_governors * -sysfs_get_freq_available_governors(unsigned int cpu) { - struct cpufreq_available_governors *first = NULL; - struct cpufreq_available_governors *current = NULL; - char linebuf[MAX_LINE_LEN]; - unsigned int pos, i; - unsigned int len; - - len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors", - linebuf, sizeof(linebuf)); - if (len == 0) - return NULL; - - pos = 0; - for (i = 0; i < len; i++) { - if (linebuf[i] == ' ' || linebuf[i] == '\n') { - if (i - pos < 2) - continue; - if (current) { - current->next = malloc(sizeof(*current)); - if (!current->next) - goto error_out; - current = current->next; - } else { - first = malloc(sizeof(*first)); - if (!first) - goto error_out; - current = first; - } - current->first = first; - current->next = NULL; - - current->governor = malloc(i - pos + 1); - if (!current->governor) - goto error_out; - - memcpy(current->governor, linebuf + pos, i - pos); - current->governor[i - pos] = '\0'; - pos = i + 1; - } - } - - return first; - - error_out: - while (first) { - current = first->next; - if (first->governor) - free(first->governor); - free(first); - first = current; - } - return NULL; -} - - -struct cpufreq_available_frequencies * -sysfs_get_available_frequencies(unsigned int cpu) { - struct cpufreq_available_frequencies *first = NULL; - struct cpufreq_available_frequencies *current = NULL; - char one_value[SYSFS_PATH_MAX]; - char linebuf[MAX_LINE_LEN]; - unsigned int pos, i; - unsigned int len; - - len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies", - linebuf, sizeof(linebuf)); - if (len == 0) - return NULL; - - pos = 0; - for (i = 0; i < len; i++) { - if (linebuf[i] == ' ' || linebuf[i] == '\n') { - if (i - pos < 2) - continue; - if (i - pos >= SYSFS_PATH_MAX) - goto error_out; - if (current) { - current->next = malloc(sizeof(*current)); - if (!current->next) - goto error_out; - current = current->next; - } else { - first = malloc(sizeof(*first)); - if (!first) - goto error_out; - current = first; - } - current->first = first; - current->next = NULL; - - memcpy(one_value, linebuf + pos, i - pos); - one_value[i - pos] = '\0'; - if (sscanf(one_value, "%lu", ¤t->frequency) != 1) - goto error_out; - - pos = i + 1; - } - } - - return first; - - error_out: - while (first) { - current = first->next; - free(first); - first = current; - } - return NULL; -} - -static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu, - const char *file) -{ - struct cpufreq_affected_cpus *first = NULL; - struct cpufreq_affected_cpus *current = NULL; - char one_value[SYSFS_PATH_MAX]; - char linebuf[MAX_LINE_LEN]; - unsigned int pos, i; - unsigned int len; - - len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf)); - if (len == 0) - return NULL; - - pos = 0; - for (i = 0; i < len; i++) { - if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') { - if (i - pos < 1) - continue; - if (i - pos >= SYSFS_PATH_MAX) - goto error_out; - if (current) { - current->next = malloc(sizeof(*current)); - if (!current->next) - goto error_out; - current = current->next; - } else { - first = malloc(sizeof(*first)); - if (!first) - goto error_out; - current = first; - } - current->first = first; - current->next = NULL; - - memcpy(one_value, linebuf + pos, i - pos); - one_value[i - pos] = '\0'; - - if (sscanf(one_value, "%u", ¤t->cpu) != 1) - goto error_out; - - pos = i + 1; - } - } - - return first; - - error_out: - while (first) { - current = first->next; - free(first); - first = current; - } - return NULL; -} - -struct cpufreq_affected_cpus *sysfs_get_freq_affected_cpus(unsigned int cpu) -{ - return sysfs_get_cpu_list(cpu, "affected_cpus"); -} - -struct cpufreq_affected_cpus *sysfs_get_freq_related_cpus(unsigned int cpu) -{ - return sysfs_get_cpu_list(cpu, "related_cpus"); -} - -struct cpufreq_stats *sysfs_get_freq_stats(unsigned int cpu, - unsigned long long *total_time) { - struct cpufreq_stats *first = NULL; - struct cpufreq_stats *current = NULL; - char one_value[SYSFS_PATH_MAX]; - char linebuf[MAX_LINE_LEN]; - unsigned int pos, i; - unsigned int len; - - len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state", - linebuf, sizeof(linebuf)); - if (len == 0) - return NULL; - - *total_time = 0; - pos = 0; - for (i = 0; i < len; i++) { - if (i == strlen(linebuf) || linebuf[i] == '\n') { - if (i - pos < 2) - continue; - if ((i - pos) >= SYSFS_PATH_MAX) - goto error_out; - if (current) { - current->next = malloc(sizeof(*current)); - if (!current->next) - goto error_out; - current = current->next; - } else { - first = malloc(sizeof(*first)); - if (!first) - goto error_out; - current = first; - } - current->first = first; - current->next = NULL; - - memcpy(one_value, linebuf + pos, i - pos); - one_value[i - pos] = '\0'; - if (sscanf(one_value, "%lu %llu", - ¤t->frequency, - ¤t->time_in_state) != 2) - goto error_out; - - *total_time = *total_time + current->time_in_state; - pos = i + 1; - } - } - - return first; - - error_out: - while (first) { - current = first->next; - free(first); - first = current; - } - return NULL; -} - -unsigned long sysfs_get_freq_transitions(unsigned int cpu) -{ - return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS); -} - -static int verify_gov(char *new_gov, char *passed_gov) -{ - unsigned int i, j = 0; - - if (!passed_gov || (strlen(passed_gov) > 19)) - return -EINVAL; - - strncpy(new_gov, passed_gov, 20); - for (i = 0; i < 20; i++) { - if (j) { - new_gov[i] = '\0'; - continue; - } - if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z')) - continue; - - if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z')) - continue; - - if (new_gov[i] == '-') - continue; - - if (new_gov[i] == '_') - continue; - - if (new_gov[i] == '\0') { - j = 1; - continue; - } - return -EINVAL; - } - new_gov[19] = '\0'; - return 0; -} - -int sysfs_modify_freq_policy_governor(unsigned int cpu, char *governor) -{ - char new_gov[SYSFS_PATH_MAX]; - - if (!governor) - return -EINVAL; - - if (verify_gov(new_gov, governor)) - return -EINVAL; - - return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR, - new_gov, strlen(new_gov)); -}; - -int sysfs_modify_freq_policy_max(unsigned int cpu, unsigned long max_freq) -{ - char value[SYSFS_PATH_MAX]; - - snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq); - - return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, - value, strlen(value)); -}; - - -int sysfs_modify_freq_policy_min(unsigned int cpu, unsigned long min_freq) -{ - char value[SYSFS_PATH_MAX]; - - snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq); - - return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, - value, strlen(value)); -}; - - -int sysfs_set_freq_policy(unsigned int cpu, struct cpufreq_policy *policy) -{ - char min[SYSFS_PATH_MAX]; - char max[SYSFS_PATH_MAX]; - char gov[SYSFS_PATH_MAX]; - int ret; - unsigned long old_min; - int write_max_first; - - if (!policy || !(policy->governor)) - return -EINVAL; - - if (policy->max < policy->min) - return -EINVAL; - - if (verify_gov(gov, policy->governor)) - return -EINVAL; - - snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min); - snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max); - - old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ); - write_max_first = (old_min && (policy->max < old_min) ? 0 : 1); - - if (write_max_first) { - ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, - max, strlen(max)); - if (ret) - return ret; - } - - ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min, - strlen(min)); - if (ret) - return ret; - - if (!write_max_first) { - ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ, - max, strlen(max)); - if (ret) - return ret; - } - - return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR, - gov, strlen(gov)); -} - -int sysfs_set_frequency(unsigned int cpu, unsigned long target_frequency) -{ - struct cpufreq_policy *pol = sysfs_get_freq_policy(cpu); - char userspace_gov[] = "userspace"; - char freq[SYSFS_PATH_MAX]; - int ret; - - if (!pol) - return -ENODEV; - - if (strncmp(pol->governor, userspace_gov, 9) != 0) { - ret = sysfs_modify_freq_policy_governor(cpu, userspace_gov); - if (ret) { - cpufreq_put_policy(pol); - return ret; - } - } - - cpufreq_put_policy(pol); - - snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency); - - return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED, - freq, strlen(freq)); -} - -/* CPUFREQ sysfs access **************************************************/ - -/* General sysfs access **************************************************/ -int sysfs_cpu_exists(unsigned int cpu) -{ - char file[SYSFS_PATH_MAX]; - struct stat statbuf; - - snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/", cpu); - - if (stat(file, &statbuf) != 0) - return -ENOSYS; - - return S_ISDIR(statbuf.st_mode) ? 0 : -ENOSYS; -} - -/* General sysfs access **************************************************/ diff --git a/tools/power/cpupower/lib/sysfs.h b/tools/power/cpupower/lib/sysfs.h deleted file mode 100644 index c76a5e0af501..000000000000 --- a/tools/power/cpupower/lib/sysfs.h +++ /dev/null @@ -1,31 +0,0 @@ -/* General */ -extern unsigned int sysfs_cpu_exists(unsigned int cpu); - -/* CPUfreq */ -extern unsigned long sysfs_get_freq_kernel(unsigned int cpu); -extern unsigned long sysfs_get_freq_hardware(unsigned int cpu); -extern unsigned long sysfs_get_freq_transition_latency(unsigned int cpu); -extern int sysfs_get_freq_hardware_limits(unsigned int cpu, - unsigned long *min, unsigned long *max); -extern char *sysfs_get_freq_driver(unsigned int cpu); -extern struct cpufreq_policy *sysfs_get_freq_policy(unsigned int cpu); -extern struct cpufreq_available_governors *sysfs_get_freq_available_governors( - unsigned int cpu); -extern struct cpufreq_available_frequencies *sysfs_get_available_frequencies( - unsigned int cpu); -extern struct cpufreq_affected_cpus *sysfs_get_freq_affected_cpus( - unsigned int cpu); -extern struct cpufreq_affected_cpus *sysfs_get_freq_related_cpus( - unsigned int cpu); -extern struct cpufreq_stats *sysfs_get_freq_stats(unsigned int cpu, - unsigned long long *total_time); -extern unsigned long sysfs_get_freq_transitions(unsigned int cpu); -extern int sysfs_set_freq_policy(unsigned int cpu, - struct cpufreq_policy *policy); -extern int sysfs_modify_freq_policy_min(unsigned int cpu, - unsigned long min_freq); -extern int sysfs_modify_freq_policy_max(unsigned int cpu, - unsigned long max_freq); -extern int sysfs_modify_freq_policy_governor(unsigned int cpu, char *governor); -extern int sysfs_set_frequency(unsigned int cpu, - unsigned long target_frequency); -- cgit v1.2.3-59-g8ed1b