748f0d7008
When a user tries to modify cpuidle or cpufreq properties on offline CPUs, the tool returns success (exit status 0) but also does not provide any warning message regarding offline cpus that may have been specified but left unchanged. In case of all or a few CPUs being offline, it can be difficult to keep track of which CPUs didn't get the new frequency or idle state set. Silent failures are difficult to keep track of when there are a huge number of CPUs on which the action is performed. This patch adds helper functions to find both online and offline CPUs and print them out accordingly. We use these helper functions in cpuidle-set and cpufreq-set to print an additional message if the user attempts to modify offline cpus. Reported-by: Pavithra R. Prakash <pavrampu@in.ibm.com> Signed-off-by: Brahadambal Srinivasan <latha@linux.vnet.ibm.com> Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
340 lines
7.1 KiB
C
340 lines
7.1 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
|
|
*/
|
|
|
|
|
|
#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 { /* too 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) != 1)
|
|
continue;
|
|
|
|
cpus = cpufreq_get_related_cpus(cpu);
|
|
if (!cpus)
|
|
break;
|
|
while (cpus->next) {
|
|
bitmask_setbit(cpus_chosen, cpus->cpu);
|
|
cpus = cpus->next;
|
|
}
|
|
/* Set the last cpu in related cpus list */
|
|
bitmask_setbit(cpus_chosen, cpus->cpu);
|
|
cpufreq_put_related_cpus(cpus);
|
|
}
|
|
}
|
|
|
|
get_cpustate();
|
|
|
|
/* 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) != 1)
|
|
continue;
|
|
|
|
printf(_("Setting cpu: %d\n"), cpu);
|
|
ret = do_one_cpu(cpu, &new_pol, freq, policychange);
|
|
if (ret) {
|
|
print_error();
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
print_offline_cpus();
|
|
|
|
return 0;
|
|
}
|