diff options
author | Thomas Renninger <trenn@suse.com> | 2016-04-28 15:24:40 +0200 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2016-04-28 16:02:29 +0200 |
commit | ac5a181d065d74fb6b213d538f743392f27bcdbd (patch) | |
tree | ce5eab193a80e4514c1ebe931c391af57741a99d /tools/power | |
parent | fe7656a8e8fcb3a0151b36b2f4c66763fa9553ea (diff) |
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 <trenn@suse.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'tools/power')
-rw-r--r-- | tools/power/cpupower/Makefile | 12 | ||||
-rw-r--r-- | tools/power/cpupower/bench/system.c | 3 | ||||
-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 | ||||
-rw-r--r-- | tools/power/cpupower/utils/cpufreq-set.c | 8 | ||||
-rw-r--r-- | tools/power/cpupower/utils/cpuidle-info.c | 32 | ||||
-rw-r--r-- | tools/power/cpupower/utils/cpuidle-set.c | 26 | ||||
-rw-r--r-- | tools/power/cpupower/utils/helpers/helpers.h | 26 | ||||
-rw-r--r-- | tools/power/cpupower/utils/helpers/topology.c | 107 | ||||
-rw-r--r-- | tools/power/cpupower/utils/idle_monitor/cpuidle_sysfs.c | 12 |
17 files changed, 1242 insertions, 931 deletions
diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile index 0adaf0c7c03a..8358863259c5 100644 --- a/tools/power/cpupower/Makefile +++ b/tools/power/cpupower/Makefile @@ -63,7 +63,7 @@ DESTDIR ?= # and _should_ modify the PACKAGE_BUGREPORT definition VERSION= $(shell ./utils/version-gen.sh) -LIB_MAJ= 0.0.0 +LIB_MAJ= 0.0.1 LIB_MIN= 0 PACKAGE = cpupower @@ -129,7 +129,7 @@ WARNINGS += -Wshadow CFLAGS += -DVERSION=\"$(VERSION)\" -DPACKAGE=\"$(PACKAGE)\" \ -DPACKAGE_BUGREPORT=\"$(PACKAGE_BUGREPORT)\" -D_GNU_SOURCE -UTIL_OBJS = utils/helpers/amd.o utils/helpers/topology.o utils/helpers/msr.o \ +UTIL_OBJS = utils/helpers/amd.o utils/helpers/msr.o \ utils/helpers/sysfs.o utils/helpers/misc.o utils/helpers/cpuid.o \ utils/helpers/pci.o utils/helpers/bitmask.o \ utils/idle_monitor/nhm_idle.o utils/idle_monitor/snb_idle.o \ @@ -148,9 +148,9 @@ UTIL_HEADERS = utils/helpers/helpers.h utils/idle_monitor/cpupower-monitor.h \ utils/helpers/bitmask.h \ utils/idle_monitor/idle_monitors.h utils/idle_monitor/idle_monitors.def -LIB_HEADERS = lib/cpufreq.h lib/sysfs.h -LIB_SRC = lib/cpufreq.c lib/sysfs.c -LIB_OBJS = lib/cpufreq.o lib/sysfs.o +LIB_HEADERS = lib/cpufreq.h lib/cpupower.h lib/cpuidle.h +LIB_SRC = lib/cpufreq.c lib/cpupower.c lib/cpuidle.c +LIB_OBJS = lib/cpufreq.o lib/cpupower.o lib/cpuidle.o LIB_OBJS := $(addprefix $(OUTPUT),$(LIB_OBJS)) CFLAGS += -pipe @@ -280,6 +280,7 @@ install-lib: $(CP) $(OUTPUT)libcpupower.so* $(DESTDIR)${libdir}/ $(INSTALL) -d $(DESTDIR)${includedir} $(INSTALL_DATA) lib/cpufreq.h $(DESTDIR)${includedir}/cpufreq.h + $(INSTALL_DATA) lib/cpuidle.h $(DESTDIR)${includedir}/cpuidle.h install-tools: $(INSTALL) -d $(DESTDIR)${bindir} @@ -315,6 +316,7 @@ endif uninstall: - rm -f $(DESTDIR)${libdir}/libcpupower.* - rm -f $(DESTDIR)${includedir}/cpufreq.h + - rm -f $(DESTDIR)${includedir}/cpuidle.h - rm -f $(DESTDIR)${bindir}/utils/cpupower - rm -f $(DESTDIR)${mandir}/man1/cpupower.1 - rm -f $(DESTDIR)${mandir}/man1/cpupower-frequency-set.1 diff --git a/tools/power/cpupower/bench/system.c b/tools/power/cpupower/bench/system.c index f01e3f4be84c..c25a74ae51ba 100644 --- a/tools/power/cpupower/bench/system.c +++ b/tools/power/cpupower/bench/system.c @@ -26,6 +26,7 @@ #include <sched.h> #include <cpufreq.h> +#include <cpupower.h> #include "config.h" #include "system.h" @@ -60,7 +61,7 @@ int set_cpufreq_governor(char *governor, unsigned int cpu) dprintf("set %s as cpufreq governor\n", governor); - if (cpufreq_cpu_exists(cpu) != 0) { + if (cpupower_is_cpu_online(cpu) != 0) { perror("cpufreq_cpu_exists"); fprintf(stderr, "error: cpu %u does not exist\n", cpu); return -1; 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 <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 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 <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); + |