diff options
Diffstat (limited to 'tools/power/cpupower/lib')
-rw-r--r-- | tools/power/cpupower/lib/cpufreq.c | 550 | ||||
-rw-r--r-- | tools/power/cpupower/lib/cpufreq.h | 59 | ||||
-rw-r--r-- | tools/power/cpupower/lib/cpuidle.c | 380 | ||||
-rw-r--r-- | tools/power/cpupower/lib/cpuidle.h | 23 | ||||
-rw-r--r-- | tools/power/cpupower/lib/cpupower.c | 192 | ||||
-rw-r--r-- | tools/power/cpupower/lib/cpupower.h | 35 | ||||
-rw-r--r-- | tools/power/cpupower/lib/cpupower_intern.h | 5 | ||||
-rw-r--r-- | tools/power/cpupower/lib/sysfs.c | 672 | ||||
-rw-r--r-- | tools/power/cpupower/lib/sysfs.h | 31 |
9 files changed, 1190 insertions, 757 deletions
diff --git a/tools/power/cpupower/lib/cpufreq.c b/tools/power/cpupower/lib/cpufreq.c index d961101d1..1b993fe1c 100644 --- a/tools/power/cpupower/lib/cpufreq.c +++ b/tools/power/cpupower/lib/cpufreq.c @@ -9,28 +9,190 @@ #include <errno.h> #include <stdlib.h> #include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> #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 3aae8e7a0..3b005c39f 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 000000000..9bd4c7655 --- /dev/null +++ b/tools/power/cpupower/lib/cpuidle.c @@ -0,0 +1,380 @@ +/* + * (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 "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 000000000..04eb3cfa6 --- /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 000000000..9c395ec92 --- /dev/null +++ b/tools/power/cpupower/lib/cpupower.c @@ -0,0 +1,192 @@ +/* + * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de> + * + * Licensed under the terms of the GNU GPL License version 2. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> +#include <stdlib.h> + +#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 000000000..fa031fcc7 --- /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 000000000..f8ec40096 --- /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 870713a75..000000000 --- a/tools/power/cpupower/lib/sysfs.c +++ /dev/null @@ -1,672 +0,0 @@ -/* - * (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de> - * - * Licensed under the terms of the GNU GPL License version 2. - */ - -#include <stdio.h> -#include <errno.h> -#include <stdlib.h> -#include <string.h> -#include <limits.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> - -#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 c76a5e0af..000000000 --- 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); |