linux/tools/power/cpupower/utils/cpufreq-set.c
Thomas Renninger ac5a181d06 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>
2016-04-28 16:02:29 +02:00

335 lines
7.0 KiB
C

/*
* (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
*
* Licensed under the terms of the GNU GPL License version 2.
*/
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <ctype.h>
#include <getopt.h>
#include "cpufreq.h"
#include "cpuidle.h"
#include "helpers/helpers.h"
#define NORM_FREQ_LEN 32
static struct option set_opts[] = {
{"min", required_argument, NULL, 'd'},
{"max", required_argument, NULL, 'u'},
{"governor", required_argument, NULL, 'g'},
{"freq", required_argument, NULL, 'f'},
{"related", no_argument, NULL, 'r'},
{ },
};
static void print_error(void)
{
printf(_("Error setting new values. Common errors:\n"
"- Do you have proper administration rights? (super-user?)\n"
"- Is the governor you requested available and modprobed?\n"
"- Trying to set an invalid policy?\n"
"- Trying to set a specific frequency, but userspace governor is not available,\n"
" for example because of hardware which cannot be set to a specific frequency\n"
" or because the userspace governor isn't loaded?\n"));
};
struct freq_units {
char *str_unit;
int power_of_ten;
};
const struct freq_units def_units[] = {
{"hz", -3},
{"khz", 0}, /* default */
{"mhz", 3},
{"ghz", 6},
{"thz", 9},
{NULL, 0}
};
static void print_unknown_arg(void)
{
printf(_("invalid or unknown argument\n"));
}
static unsigned long string_to_frequency(const char *str)
{
char normalized[NORM_FREQ_LEN];
const struct freq_units *unit;
const char *scan;
char *end;
unsigned long freq;
int power = 0, match_count = 0, i, cp, pad;
while (*str == '0')
str++;
for (scan = str; isdigit(*scan) || *scan == '.'; scan++) {
if (*scan == '.' && match_count == 0)
match_count = 1;
else if (*scan == '.' && match_count == 1)
return 0;
}
if (*scan) {
match_count = 0;
for (unit = def_units; unit->str_unit; unit++) {
for (i = 0;
scan[i] && tolower(scan[i]) == unit->str_unit[i];
++i)
continue;
if (scan[i])
continue;
match_count++;
power = unit->power_of_ten;
}
if (match_count != 1)
return 0;
}
/* count the number of digits to be copied */
for (cp = 0; isdigit(str[cp]); cp++)
continue;
if (str[cp] == '.') {
while (power > -1 && isdigit(str[cp+1]))
cp++, power--;
}
if (power >= -1) /* not enough => pad */
pad = power + 1;
else /* to much => strip */
pad = 0, cp += power + 1;
/* check bounds */
if (cp <= 0 || cp + pad > NORM_FREQ_LEN - 1)
return 0;
/* copy digits */
for (i = 0; i < cp; i++, str++) {
if (*str == '.')
str++;
normalized[i] = *str;
}
/* and pad */
for (; i < cp + pad; i++)
normalized[i] = '0';
/* round up, down ? */
match_count = (normalized[i-1] >= '5');
/* and drop the decimal part */
normalized[i-1] = 0; /* cp > 0 && pad >= 0 ==> i > 0 */
/* final conversion (and applying rounding) */
errno = 0;
freq = strtoul(normalized, &end, 10);
if (errno)
return 0;
else {
if (match_count && freq != ULONG_MAX)
freq++;
return freq;
}
}
static int do_new_policy(unsigned int cpu, struct cpufreq_policy *new_pol)
{
struct cpufreq_policy *cur_pol = cpufreq_get_policy(cpu);
int ret;
if (!cur_pol) {
printf(_("wrong, unknown or unhandled CPU?\n"));
return -EINVAL;
}
if (!new_pol->min)
new_pol->min = cur_pol->min;
if (!new_pol->max)
new_pol->max = cur_pol->max;
if (!new_pol->governor)
new_pol->governor = cur_pol->governor;
ret = cpufreq_set_policy(cpu, new_pol);
cpufreq_put_policy(cur_pol);
return ret;
}
static int do_one_cpu(unsigned int cpu, struct cpufreq_policy *new_pol,
unsigned long freq, unsigned int pc)
{
switch (pc) {
case 0:
return cpufreq_set_frequency(cpu, freq);
case 1:
/* if only one value of a policy is to be changed, we can
* use a "fast path".
*/
if (new_pol->min)
return cpufreq_modify_policy_min(cpu, new_pol->min);
else if (new_pol->max)
return cpufreq_modify_policy_max(cpu, new_pol->max);
else if (new_pol->governor)
return cpufreq_modify_policy_governor(cpu,
new_pol->governor);
default:
/* slow path */
return do_new_policy(cpu, new_pol);
}
}
int cmd_freq_set(int argc, char **argv)
{
extern char *optarg;
extern int optind, opterr, optopt;
int ret = 0, cont = 1;
int double_parm = 0, related = 0, policychange = 0;
unsigned long freq = 0;
char gov[20];
unsigned int cpu;
struct cpufreq_policy new_pol = {
.min = 0,
.max = 0,
.governor = NULL,
};
/* parameter parsing */
do {
ret = getopt_long(argc, argv, "d:u:g:f:r", set_opts, NULL);
switch (ret) {
case '?':
print_unknown_arg();
return -EINVAL;
case -1:
cont = 0;
break;
case 'r':
if (related)
double_parm++;
related++;
break;
case 'd':
if (new_pol.min)
double_parm++;
policychange++;
new_pol.min = string_to_frequency(optarg);
if (new_pol.min == 0) {
print_unknown_arg();
return -EINVAL;
}
break;
case 'u':
if (new_pol.max)
double_parm++;
policychange++;
new_pol.max = string_to_frequency(optarg);
if (new_pol.max == 0) {
print_unknown_arg();
return -EINVAL;
}
break;
case 'f':
if (freq)
double_parm++;
freq = string_to_frequency(optarg);
if (freq == 0) {
print_unknown_arg();
return -EINVAL;
}
break;
case 'g':
if (new_pol.governor)
double_parm++;
policychange++;
if ((strlen(optarg) < 3) || (strlen(optarg) > 18)) {
print_unknown_arg();
return -EINVAL;
}
if ((sscanf(optarg, "%19s", gov)) != 1) {
print_unknown_arg();
return -EINVAL;
}
new_pol.governor = gov;
break;
}
} while (cont);
/* parameter checking */
if (double_parm) {
printf("the same parameter was passed more than once\n");
return -EINVAL;
}
if (freq && policychange) {
printf(_("the -f/--freq parameter cannot be combined with -d/--min, -u/--max or\n"
"-g/--governor parameters\n"));
return -EINVAL;
}
if (!freq && !policychange) {
printf(_("At least one parameter out of -f/--freq, -d/--min, -u/--max, and\n"
"-g/--governor must be passed\n"));
return -EINVAL;
}
/* Default is: set all CPUs */
if (bitmask_isallclear(cpus_chosen))
bitmask_setall(cpus_chosen);
/* Also set frequency settings for related CPUs if -r is passed */
if (related) {
for (cpu = bitmask_first(cpus_chosen);
cpu <= bitmask_last(cpus_chosen); cpu++) {
struct cpufreq_affected_cpus *cpus;
if (!bitmask_isbitset(cpus_chosen, cpu) ||
cpupower_is_cpu_online(cpu))
continue;
cpus = cpufreq_get_related_cpus(cpu);
if (!cpus)
break;
while (cpus->next) {
bitmask_setbit(cpus_chosen, cpus->cpu);
cpus = cpus->next;
}
cpufreq_put_related_cpus(cpus);
}
}
/* loop over CPUs */
for (cpu = bitmask_first(cpus_chosen);
cpu <= bitmask_last(cpus_chosen); cpu++) {
if (!bitmask_isbitset(cpus_chosen, cpu) ||
cpupower_is_cpu_online(cpu))
continue;
if (cpupower_is_cpu_online(cpu) != 1)
continue;
printf(_("Setting cpu: %d\n"), cpu);
ret = do_one_cpu(cpu, &new_pol, freq, policychange);
if (ret) {
print_error();
return ret;
}
}
return 0;
}