brintos

brintos / linux-shallow public Read only

0
0
Text · 7.1 KiB · c5e60a3 Raw
340 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>4 */5 6 7#include <unistd.h>8#include <stdio.h>9#include <errno.h>10#include <stdlib.h>11#include <limits.h>12#include <string.h>13#include <ctype.h>14 15#include <getopt.h>16 17#include "cpufreq.h"18#include "cpuidle.h"19#include "helpers/helpers.h"20 21#define NORM_FREQ_LEN 3222 23static struct option set_opts[] = {24	{"min",		required_argument,	NULL, 'd'},25	{"max",		required_argument,	NULL, 'u'},26	{"governor",	required_argument,	NULL, 'g'},27	{"freq",	required_argument,	NULL, 'f'},28	{"related",	no_argument,		NULL, 'r'},29	{ },30};31 32static void print_error(void)33{34	printf(_("Error setting new values. Common errors:\n"35			"- Do you have proper administration rights? (super-user?)\n"36			"- Is the governor you requested available and modprobed?\n"37			"- Trying to set an invalid policy?\n"38			"- Trying to set a specific frequency, but userspace governor is not available,\n"39			"   for example because of hardware which cannot be set to a specific frequency\n"40			"   or because the userspace governor isn't loaded?\n"));41};42 43struct freq_units {44	char		*str_unit;45	int		power_of_ten;46};47 48const struct freq_units def_units[] = {49	{"hz", -3},50	{"khz", 0}, /* default */51	{"mhz", 3},52	{"ghz", 6},53	{"thz", 9},54	{NULL, 0}55};56 57static void print_unknown_arg(void)58{59	printf(_("invalid or unknown argument\n"));60}61 62static unsigned long string_to_frequency(const char *str)63{64	char normalized[NORM_FREQ_LEN];65	const struct freq_units *unit;66	const char *scan;67	char *end;68	unsigned long freq;69	int power = 0, match_count = 0, i, cp, pad;70 71	while (*str == '0')72		str++;73 74	for (scan = str; isdigit(*scan) || *scan == '.'; scan++) {75		if (*scan == '.' && match_count == 0)76			match_count = 1;77		else if (*scan == '.' && match_count == 1)78			return 0;79	}80 81	if (*scan) {82		match_count = 0;83		for (unit = def_units; unit->str_unit; unit++) {84			for (i = 0;85			     scan[i] && tolower(scan[i]) == unit->str_unit[i];86			     ++i)87				continue;88			if (scan[i])89				continue;90			match_count++;91			power = unit->power_of_ten;92		}93		if (match_count != 1)94			return 0;95	}96 97	/* count the number of digits to be copied */98	for (cp = 0; isdigit(str[cp]); cp++)99		continue;100 101	if (str[cp] == '.') {102		while (power > -1 && isdigit(str[cp+1])) {103			cp++;104			power--;105		}106	}107	if (power >= -1) {		/* not enough => pad */108		pad = power + 1;109	} else {			/* too much => strip */110		pad = 0;111		cp += power + 1;112	}113	/* check bounds */114	if (cp <= 0 || cp + pad > NORM_FREQ_LEN - 1)115		return 0;116 117	/* copy digits */118	for (i = 0; i < cp; i++, str++) {119		if (*str == '.')120			str++;121		normalized[i] = *str;122	}123	/* and pad */124	for (; i < cp + pad; i++)125		normalized[i] = '0';126 127	/* round up, down ? */128	match_count = (normalized[i-1] >= '5');129	/* and drop the decimal part */130	normalized[i-1] = 0; /* cp > 0 && pad >= 0 ==> i > 0 */131 132	/* final conversion (and applying rounding) */133	errno = 0;134	freq = strtoul(normalized, &end, 10);135	if (errno)136		return 0;137	else {138		if (match_count && freq != ULONG_MAX)139			freq++;140		return freq;141	}142}143 144static int do_new_policy(unsigned int cpu, struct cpufreq_policy *new_pol)145{146	struct cpufreq_policy *cur_pol = cpufreq_get_policy(cpu);147	int ret;148 149	if (!cur_pol) {150		printf(_("wrong, unknown or unhandled CPU?\n"));151		return -EINVAL;152	}153 154	if (!new_pol->min)155		new_pol->min = cur_pol->min;156 157	if (!new_pol->max)158		new_pol->max = cur_pol->max;159 160	if (!new_pol->governor)161		new_pol->governor = cur_pol->governor;162 163	ret = cpufreq_set_policy(cpu, new_pol);164 165	cpufreq_put_policy(cur_pol);166 167	return ret;168}169 170 171static int do_one_cpu(unsigned int cpu, struct cpufreq_policy *new_pol,172		unsigned long freq, unsigned int pc)173{174	switch (pc) {175	case 0:176		return cpufreq_set_frequency(cpu, freq);177 178	case 1:179		/* if only one value of a policy is to be changed, we can180		 * use a "fast path".181		 */182		if (new_pol->min)183			return cpufreq_modify_policy_min(cpu, new_pol->min);184		else if (new_pol->max)185			return cpufreq_modify_policy_max(cpu, new_pol->max);186		else if (new_pol->governor)187			return cpufreq_modify_policy_governor(cpu,188							new_pol->governor);189 190	default:191		/* slow path */192		return do_new_policy(cpu, new_pol);193	}194}195 196int cmd_freq_set(int argc, char **argv)197{198	extern char *optarg;199	extern int optind, opterr, optopt;200	int ret = 0, cont = 1;201	int double_parm = 0, related = 0, policychange = 0;202	unsigned long freq = 0;203	char gov[20];204	unsigned int cpu;205 206	struct cpufreq_policy new_pol = {207		.min = 0,208		.max = 0,209		.governor = NULL,210	};211 212	/* parameter parsing */213	do {214		ret = getopt_long(argc, argv, "d:u:g:f:r", set_opts, NULL);215		switch (ret) {216		case '?':217			print_unknown_arg();218			return -EINVAL;219		case -1:220			cont = 0;221			break;222		case 'r':223			if (related)224				double_parm++;225			related++;226			break;227		case 'd':228			if (new_pol.min)229				double_parm++;230			policychange++;231			new_pol.min = string_to_frequency(optarg);232			if (new_pol.min == 0) {233				print_unknown_arg();234				return -EINVAL;235			}236			break;237		case 'u':238			if (new_pol.max)239				double_parm++;240			policychange++;241			new_pol.max = string_to_frequency(optarg);242			if (new_pol.max == 0) {243				print_unknown_arg();244				return -EINVAL;245			}246			break;247		case 'f':248			if (freq)249				double_parm++;250			freq = string_to_frequency(optarg);251			if (freq == 0) {252				print_unknown_arg();253				return -EINVAL;254			}255			break;256		case 'g':257			if (new_pol.governor)258				double_parm++;259			policychange++;260			if ((strlen(optarg) < 3) || (strlen(optarg) > 18)) {261				print_unknown_arg();262				return -EINVAL;263			}264			if ((sscanf(optarg, "%19s", gov)) != 1) {265				print_unknown_arg();266				return -EINVAL;267			}268			new_pol.governor = gov;269			break;270		}271	} while (cont);272 273	/* parameter checking */274	if (double_parm) {275		printf("the same parameter was passed more than once\n");276		return -EINVAL;277	}278 279	if (freq && policychange) {280		printf(_("the -f/--freq parameter cannot be combined with -d/--min, -u/--max or\n"281				"-g/--governor parameters\n"));282		return -EINVAL;283	}284 285	if (!freq && !policychange) {286		printf(_("At least one parameter out of -f/--freq, -d/--min, -u/--max, and\n"287				"-g/--governor must be passed\n"));288		return -EINVAL;289	}290 291	/* Default is: set all CPUs */292	if (bitmask_isallclear(cpus_chosen))293		bitmask_setall(cpus_chosen);294 295	/* Also set frequency settings for related CPUs if -r is passed */296	if (related) {297		for (cpu = bitmask_first(cpus_chosen);298		     cpu <= bitmask_last(cpus_chosen); cpu++) {299			struct cpufreq_affected_cpus *cpus;300 301			if (!bitmask_isbitset(cpus_chosen, cpu) ||302			    cpupower_is_cpu_online(cpu) != 1)303				continue;304 305			cpus = cpufreq_get_related_cpus(cpu);306			if (!cpus)307				break;308			while (cpus->next) {309				bitmask_setbit(cpus_chosen, cpus->cpu);310				cpus = cpus->next;311			}312			/* Set the last cpu in related cpus list */313			bitmask_setbit(cpus_chosen, cpus->cpu);314			cpufreq_put_related_cpus(cpus);315		}316	}317 318	get_cpustate();319 320	/* loop over CPUs */321	for (cpu = bitmask_first(cpus_chosen);322	     cpu <= bitmask_last(cpus_chosen); cpu++) {323 324		if (!bitmask_isbitset(cpus_chosen, cpu) ||325		    cpupower_is_cpu_online(cpu) != 1)326			continue;327 328		printf(_("Setting cpu: %d\n"), cpu);329		ret = do_one_cpu(cpu, &new_pol, freq, policychange);330		if (ret) {331			print_error();332			return ret;333		}334	}335 336	print_offline_cpus();337 338	return 0;339}340