brintos

brintos / linux-shallow public Read only

0
0
Text · 16.4 KiB · 1516d23 Raw
786 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>4 */5 6 7#include <stdio.h>8#include <errno.h>9#include <stdlib.h>10#include <string.h>11#include <sys/types.h>12#include <sys/stat.h>13#include <fcntl.h>14#include <unistd.h>15 16#include "cpufreq.h"17#include "cpupower_intern.h"18 19/* CPUFREQ sysfs access **************************************************/20 21/* helper function to read file from /sys into given buffer */22/* fname is a relative path under "cpuX/cpufreq" dir */23static unsigned int sysfs_cpufreq_read_file(unsigned int cpu, const char *fname,24					    char *buf, size_t buflen)25{26	char path[SYSFS_PATH_MAX];27 28	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",29			 cpu, fname);30	return cpupower_read_sysfs(path, buf, buflen);31}32 33/* helper function to write a new value to a /sys file */34/* fname is a relative path under "cpuX/cpufreq" dir */35static unsigned int sysfs_cpufreq_write_file(unsigned int cpu,36					     const char *fname,37					     const char *value, size_t len)38{39	char path[SYSFS_PATH_MAX];40	int fd;41	ssize_t numwrite;42 43	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",44			 cpu, fname);45 46	fd = open(path, O_WRONLY);47	if (fd == -1)48		return 0;49 50	numwrite = write(fd, value, len);51	if (numwrite < 1) {52		close(fd);53		return 0;54	}55 56	close(fd);57 58	return (unsigned int) numwrite;59}60 61/* read access to files which contain one numeric value */62 63enum cpufreq_value {64	CPUINFO_CUR_FREQ,65	CPUINFO_MIN_FREQ,66	CPUINFO_MAX_FREQ,67	CPUINFO_LATENCY,68	SCALING_CUR_FREQ,69	SCALING_MIN_FREQ,70	SCALING_MAX_FREQ,71	STATS_NUM_TRANSITIONS,72	MAX_CPUFREQ_VALUE_READ_FILES73};74 75static const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = {76	[CPUINFO_CUR_FREQ] = "cpuinfo_cur_freq",77	[CPUINFO_MIN_FREQ] = "cpuinfo_min_freq",78	[CPUINFO_MAX_FREQ] = "cpuinfo_max_freq",79	[CPUINFO_LATENCY]  = "cpuinfo_transition_latency",80	[SCALING_CUR_FREQ] = "scaling_cur_freq",81	[SCALING_MIN_FREQ] = "scaling_min_freq",82	[SCALING_MAX_FREQ] = "scaling_max_freq",83	[STATS_NUM_TRANSITIONS] = "stats/total_trans"84};85 86unsigned long cpufreq_get_sysfs_value_from_table(unsigned int cpu,87						 const char **table,88						 unsigned int index,89						 unsigned int size)90{91	unsigned long value;92	unsigned int len;93	char linebuf[MAX_LINE_LEN];94	char *endp;95 96	if (!table || index >= size || !table[index])97		return 0;98 99	len = sysfs_cpufreq_read_file(cpu, table[index], linebuf,100				      sizeof(linebuf));101 102	if (len == 0)103		return 0;104 105	value = strtoul(linebuf, &endp, 0);106 107	if (endp == linebuf || errno == ERANGE)108		return 0;109 110	return value;111}112 113static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu,114						 enum cpufreq_value which)115{116	return cpufreq_get_sysfs_value_from_table(cpu, cpufreq_value_files,117						  which,118						  MAX_CPUFREQ_VALUE_READ_FILES);119}120 121/* read access to files which contain one string */122 123enum cpufreq_string {124	SCALING_DRIVER,125	SCALING_GOVERNOR,126	MAX_CPUFREQ_STRING_FILES127};128 129static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = {130	[SCALING_DRIVER] = "scaling_driver",131	[SCALING_GOVERNOR] = "scaling_governor",132};133 134 135static char *sysfs_cpufreq_get_one_string(unsigned int cpu,136					  enum cpufreq_string which)137{138	char linebuf[MAX_LINE_LEN];139	char *result;140	unsigned int len;141 142	if (which >= MAX_CPUFREQ_STRING_FILES)143		return NULL;144 145	len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which],146				linebuf, sizeof(linebuf));147	if (len == 0)148		return NULL;149 150	result = strdup(linebuf);151	if (result == NULL)152		return NULL;153 154	if (result[strlen(result) - 1] == '\n')155		result[strlen(result) - 1] = '\0';156 157	return result;158}159 160/* write access */161 162enum cpufreq_write {163	WRITE_SCALING_MIN_FREQ,164	WRITE_SCALING_MAX_FREQ,165	WRITE_SCALING_GOVERNOR,166	WRITE_SCALING_SET_SPEED,167	MAX_CPUFREQ_WRITE_FILES168};169 170static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = {171	[WRITE_SCALING_MIN_FREQ] = "scaling_min_freq",172	[WRITE_SCALING_MAX_FREQ] = "scaling_max_freq",173	[WRITE_SCALING_GOVERNOR] = "scaling_governor",174	[WRITE_SCALING_SET_SPEED] = "scaling_setspeed",175};176 177static int sysfs_cpufreq_write_one_value(unsigned int cpu,178					 enum cpufreq_write which,179					 const char *new_value, size_t len)180{181	if (which >= MAX_CPUFREQ_WRITE_FILES)182		return 0;183 184	if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which],185					new_value, len) != len)186		return -ENODEV;187 188	return 0;189};190 191unsigned long cpufreq_get_freq_kernel(unsigned int cpu)192{193	return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ);194}195 196unsigned long cpufreq_get_freq_hardware(unsigned int cpu)197{198	return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ);199}200 201unsigned long cpufreq_get_transition_latency(unsigned int cpu)202{203	return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY);204}205 206int cpufreq_get_hardware_limits(unsigned int cpu,207				unsigned long *min,208				unsigned long *max)209{210	if ((!min) || (!max))211		return -EINVAL;212 213	*min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ);214	if (!*min)215		return -ENODEV;216 217	*max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ);218	if (!*max)219		return -ENODEV;220 221	return 0;222}223 224char *cpufreq_get_driver(unsigned int cpu)225{226	return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER);227}228 229void cpufreq_put_driver(char *ptr)230{231	if (!ptr)232		return;233	free(ptr);234}235 236struct cpufreq_policy *cpufreq_get_policy(unsigned int cpu)237{238	struct cpufreq_policy *policy;239 240	policy = malloc(sizeof(struct cpufreq_policy));241	if (!policy)242		return NULL;243 244	policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR);245	if (!policy->governor) {246		free(policy);247		return NULL;248	}249	policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);250	policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ);251	if ((!policy->min) || (!policy->max)) {252		free(policy->governor);253		free(policy);254		return NULL;255	}256 257	return policy;258}259 260void cpufreq_put_policy(struct cpufreq_policy *policy)261{262	if ((!policy) || (!policy->governor))263		return;264 265	free(policy->governor);266	policy->governor = NULL;267	free(policy);268}269 270struct cpufreq_available_governors *cpufreq_get_available_governors(unsigned271								int cpu)272{273	struct cpufreq_available_governors *first = NULL;274	struct cpufreq_available_governors *current = NULL;275	char linebuf[MAX_LINE_LEN];276	unsigned int pos, i;277	unsigned int len;278 279	len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors",280				linebuf, sizeof(linebuf));281	if (len == 0)282		return NULL;283 284	pos = 0;285	for (i = 0; i < len; i++) {286		if (linebuf[i] == ' ' || linebuf[i] == '\n') {287			if (i - pos < 2)288				continue;289			if (current) {290				current->next = malloc(sizeof(*current));291				if (!current->next)292					goto error_out;293				current = current->next;294			} else {295				first = malloc(sizeof(*first));296				if (!first)297					return NULL;298				current = first;299			}300			current->first = first;301			current->next = NULL;302 303			current->governor = malloc(i - pos + 1);304			if (!current->governor)305				goto error_out;306 307			memcpy(current->governor, linebuf + pos, i - pos);308			current->governor[i - pos] = '\0';309			pos = i + 1;310		}311	}312 313	return first;314 315 error_out:316	while (first) {317		current = first->next;318		if (first->governor)319			free(first->governor);320		free(first);321		first = current;322	}323	return NULL;324}325 326void cpufreq_put_available_governors(struct cpufreq_available_governors *any)327{328	struct cpufreq_available_governors *tmp, *next;329 330	if (!any)331		return;332 333	tmp = any->first;334	while (tmp) {335		next = tmp->next;336		if (tmp->governor)337			free(tmp->governor);338		free(tmp);339		tmp = next;340	}341}342 343 344struct cpufreq_available_frequencies345*cpufreq_get_available_frequencies(unsigned int cpu)346{347	struct cpufreq_available_frequencies *first = NULL;348	struct cpufreq_available_frequencies *current = NULL;349	char one_value[SYSFS_PATH_MAX];350	char linebuf[MAX_LINE_LEN];351	unsigned int pos, i;352	unsigned int len;353 354	len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies",355				      linebuf, sizeof(linebuf));356	if (len == 0)357		return NULL;358 359	pos = 0;360	for (i = 0; i < len; i++) {361		if (linebuf[i] == ' ' || linebuf[i] == '\n') {362			if (i - pos < 2)363				continue;364			if (i - pos >= SYSFS_PATH_MAX)365				goto error_out;366			if (current) {367				current->next = malloc(sizeof(*current));368				if (!current->next)369					goto error_out;370				current = current->next;371			} else {372				first = malloc(sizeof(*first));373				if (!first)374					return NULL;375				current = first;376			}377			current->first = first;378			current->next = NULL;379 380			memcpy(one_value, linebuf + pos, i - pos);381			one_value[i - pos] = '\0';382			if (sscanf(one_value, "%lu", &current->frequency) != 1)383				goto error_out;384 385			pos = i + 1;386		}387	}388 389	return first;390 391 error_out:392	while (first) {393		current = first->next;394		free(first);395		first = current;396	}397	return NULL;398}399 400struct cpufreq_available_frequencies401*cpufreq_get_boost_frequencies(unsigned int cpu)402{403	struct cpufreq_available_frequencies *first = NULL;404	struct cpufreq_available_frequencies *current = NULL;405	char one_value[SYSFS_PATH_MAX];406	char linebuf[MAX_LINE_LEN];407	unsigned int pos, i;408	unsigned int len;409 410	len = sysfs_cpufreq_read_file(cpu, "scaling_boost_frequencies",411				      linebuf, sizeof(linebuf));412	if (len == 0)413		return NULL;414 415	pos = 0;416	for (i = 0; i < len; i++) {417		if (linebuf[i] == ' ' || linebuf[i] == '\n') {418			if (i - pos < 2)419				continue;420			if (i - pos >= SYSFS_PATH_MAX)421				goto error_out;422			if (current) {423				current->next = malloc(sizeof(*current));424				if (!current->next)425					goto error_out;426				current = current->next;427			} else {428				first = malloc(sizeof(*first));429				if (!first)430					return NULL;431				current = first;432			}433			current->first = first;434			current->next = NULL;435 436			memcpy(one_value, linebuf + pos, i - pos);437			one_value[i - pos] = '\0';438			if (sscanf(one_value, "%lu", &current->frequency) != 1)439				goto error_out;440 441			pos = i + 1;442		}443	}444 445	return first;446 447 error_out:448	while (first) {449		current = first->next;450		free(first);451		first = current;452	}453	return NULL;454}455 456void cpufreq_put_available_frequencies(struct cpufreq_available_frequencies *any)457{458	struct cpufreq_available_frequencies *tmp, *next;459 460	if (!any)461		return;462 463	tmp = any->first;464	while (tmp) {465		next = tmp->next;466		free(tmp);467		tmp = next;468	}469}470 471void cpufreq_put_boost_frequencies(struct cpufreq_available_frequencies *any)472{473	cpufreq_put_available_frequencies(any);474}475 476static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu,477							const char *file)478{479	struct cpufreq_affected_cpus *first = NULL;480	struct cpufreq_affected_cpus *current = NULL;481	char one_value[SYSFS_PATH_MAX];482	char linebuf[MAX_LINE_LEN];483	unsigned int pos, i;484	unsigned int len;485 486	len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf));487	if (len == 0)488		return NULL;489 490	pos = 0;491	for (i = 0; i < len; i++) {492		if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') {493			if (i - pos  < 1)494				continue;495			if (i - pos >= SYSFS_PATH_MAX)496				goto error_out;497			if (current) {498				current->next = malloc(sizeof(*current));499				if (!current->next)500					goto error_out;501				current = current->next;502			} else {503				first = malloc(sizeof(*first));504				if (!first)505					return NULL;506				current = first;507			}508			current->first = first;509			current->next = NULL;510 511			memcpy(one_value, linebuf + pos, i - pos);512			one_value[i - pos] = '\0';513 514			if (sscanf(one_value, "%u", &current->cpu) != 1)515				goto error_out;516 517			pos = i + 1;518		}519	}520 521	return first;522 523 error_out:524	while (first) {525		current = first->next;526		free(first);527		first = current;528	}529	return NULL;530}531 532struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu)533{534	return sysfs_get_cpu_list(cpu, "affected_cpus");535}536 537void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *any)538{539	struct cpufreq_affected_cpus *tmp, *next;540 541	if (!any)542		return;543 544	tmp = any->first;545	while (tmp) {546		next = tmp->next;547		free(tmp);548		tmp = next;549	}550}551 552 553struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu)554{555	return sysfs_get_cpu_list(cpu, "related_cpus");556}557 558void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any)559{560	cpufreq_put_affected_cpus(any);561}562 563static int verify_gov(char *new_gov, char *passed_gov)564{565	unsigned int i, j = 0;566 567	if (!passed_gov || (strlen(passed_gov) > 19))568		return -EINVAL;569 570	strncpy(new_gov, passed_gov, 20);571	for (i = 0; i < 20; i++) {572		if (j) {573			new_gov[i] = '\0';574			continue;575		}576		if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z'))577			continue;578 579		if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z'))580			continue;581 582		if (new_gov[i] == '-')583			continue;584 585		if (new_gov[i] == '_')586			continue;587 588		if (new_gov[i] == '\0') {589			j = 1;590			continue;591		}592		return -EINVAL;593	}594	new_gov[19] = '\0';595	return 0;596}597 598int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy)599{600	char min[SYSFS_PATH_MAX];601	char max[SYSFS_PATH_MAX];602	char gov[SYSFS_PATH_MAX];603	int ret;604	unsigned long old_min;605	int write_max_first;606 607	if (!policy || !(policy->governor))608		return -EINVAL;609 610	if (policy->max < policy->min)611		return -EINVAL;612 613	if (verify_gov(gov, policy->governor))614		return -EINVAL;615 616	snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min);617	snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max);618 619	old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);620	write_max_first = (old_min && (policy->max < old_min) ? 0 : 1);621 622	if (write_max_first) {623		ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,624						    max, strlen(max));625		if (ret)626			return ret;627	}628 629	ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min,630					    strlen(min));631	if (ret)632		return ret;633 634	if (!write_max_first) {635		ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,636						    max, strlen(max));637		if (ret)638			return ret;639	}640 641	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,642					     gov, strlen(gov));643}644 645 646int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq)647{648	char value[SYSFS_PATH_MAX];649 650	snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq);651 652	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ,653					     value, strlen(value));654}655 656 657int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq)658{659	char value[SYSFS_PATH_MAX];660 661	snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq);662 663	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,664					     value, strlen(value));665}666 667int cpufreq_modify_policy_governor(unsigned int cpu, char *governor)668{669	char new_gov[SYSFS_PATH_MAX];670 671	if ((!governor) || (strlen(governor) > 19))672		return -EINVAL;673 674	if (verify_gov(new_gov, governor))675		return -EINVAL;676 677	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,678					     new_gov, strlen(new_gov));679}680 681int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency)682{683	struct cpufreq_policy *pol = cpufreq_get_policy(cpu);684	char userspace_gov[] = "userspace";685	char freq[SYSFS_PATH_MAX];686	int ret;687 688	if (!pol)689		return -ENODEV;690 691	if (strncmp(pol->governor, userspace_gov, 9) != 0) {692		ret = cpufreq_modify_policy_governor(cpu, userspace_gov);693		if (ret) {694			cpufreq_put_policy(pol);695			return ret;696		}697	}698 699	cpufreq_put_policy(pol);700 701	snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency);702 703	return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED,704					     freq, strlen(freq));705}706 707struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu,708					unsigned long long *total_time)709{710	struct cpufreq_stats *first = NULL;711	struct cpufreq_stats *current = NULL;712	char one_value[SYSFS_PATH_MAX];713	char linebuf[MAX_LINE_LEN];714	unsigned int pos, i;715	unsigned int len;716 717	len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state",718				linebuf, sizeof(linebuf));719	if (len == 0)720		return NULL;721 722	*total_time = 0;723	pos = 0;724	for (i = 0; i < len; i++) {725		if (i == strlen(linebuf) || linebuf[i] == '\n')	{726			if (i - pos < 2)727				continue;728			if ((i - pos) >= SYSFS_PATH_MAX)729				goto error_out;730			if (current) {731				current->next = malloc(sizeof(*current));732				if (!current->next)733					goto error_out;734				current = current->next;735			} else {736				first = malloc(sizeof(*first));737				if (!first)738					return NULL;739				current = first;740			}741			current->first = first;742			current->next = NULL;743 744			memcpy(one_value, linebuf + pos, i - pos);745			one_value[i - pos] = '\0';746			if (sscanf(one_value, "%lu %llu",747					&current->frequency,748					&current->time_in_state) != 2)749				goto error_out;750 751			*total_time = *total_time + current->time_in_state;752			pos = i + 1;753		}754	}755 756	return first;757 758 error_out:759	while (first) {760		current = first->next;761		free(first);762		first = current;763	}764	return NULL;765}766 767void cpufreq_put_stats(struct cpufreq_stats *any)768{769	struct cpufreq_stats *tmp, *next;770 771	if (!any)772		return;773 774	tmp = any->first;775	while (tmp) {776		next = tmp->next;777		free(tmp);778		tmp = next;779	}780}781 782unsigned long cpufreq_get_transitions(unsigned int cpu)783{784	return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS);785}786