brintos

brintos / linux-shallow public Read only

0
0
Text · 8.8 KiB · 0ecac00 Raw
388 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>4 *  (C) 2011       Thomas Renninger <trenn@novell.com> Novell Inc.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 "cpuidle.h"17#include "cpupower_intern.h"18 19/*20 * helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir21 * exists.22 * For example the functionality to disable c-states was introduced in later23 * kernel versions, this function can be used to explicitly check for this24 * feature.25 *26 * returns 1 if the file exists, 0 otherwise.27 */28static29unsigned int cpuidle_state_file_exists(unsigned int cpu,30				       unsigned int idlestate,31				       const char *fname)32{33	char path[SYSFS_PATH_MAX];34	struct stat statbuf;35 36 37	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",38		 cpu, idlestate, fname);39	if (stat(path, &statbuf) != 0)40		return 0;41	return 1;42}43 44/*45 * helper function to read file from /sys into given buffer46 * fname is a relative path under "cpuX/cpuidle/stateX/" dir47 * cstates starting with 0, C0 is not counted as cstate.48 * This means if you want C1 info, pass 0 as idlestate param49 */50static51unsigned int cpuidle_state_read_file(unsigned int cpu,52					    unsigned int idlestate,53					    const char *fname, char *buf,54					    size_t buflen)55{56	char path[SYSFS_PATH_MAX];57	int fd;58	ssize_t numread;59 60	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",61		 cpu, idlestate, fname);62 63	fd = open(path, O_RDONLY);64	if (fd == -1)65		return 0;66 67	numread = read(fd, buf, buflen - 1);68	if (numread < 1) {69		close(fd);70		return 0;71	}72 73	buf[numread] = '\0';74	close(fd);75 76	return (unsigned int) numread;77}78 79/*80 * helper function to write a new value to a /sys file81 * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir82 *83 * Returns the number of bytes written or 0 on error84 */85static86unsigned int cpuidle_state_write_file(unsigned int cpu,87				      unsigned int idlestate,88				      const char *fname,89				      const char *value, size_t len)90{91	char path[SYSFS_PATH_MAX];92	int fd;93	ssize_t numwrite;94 95	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",96		 cpu, idlestate, fname);97 98	fd = open(path, O_WRONLY);99	if (fd == -1)100		return 0;101 102	numwrite = write(fd, value, len);103	if (numwrite < 1) {104		close(fd);105		return 0;106	}107 108	close(fd);109 110	return (unsigned int) numwrite;111}112 113/* read access to files which contain one numeric value */114 115enum idlestate_value {116	IDLESTATE_USAGE,117	IDLESTATE_POWER,118	IDLESTATE_LATENCY,119	IDLESTATE_RESIDENCY,120	IDLESTATE_TIME,121	IDLESTATE_DISABLE,122	MAX_IDLESTATE_VALUE_FILES123};124 125static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = {126	[IDLESTATE_USAGE] = "usage",127	[IDLESTATE_POWER] = "power",128	[IDLESTATE_LATENCY] = "latency",129	[IDLESTATE_RESIDENCY] = "residency",130	[IDLESTATE_TIME]  = "time",131	[IDLESTATE_DISABLE]  = "disable",132};133 134static135unsigned long long cpuidle_state_get_one_value(unsigned int cpu,136					       unsigned int idlestate,137					       enum idlestate_value which)138{139	unsigned long long value;140	unsigned int len;141	char linebuf[MAX_LINE_LEN];142	char *endp;143 144	if (which >= MAX_IDLESTATE_VALUE_FILES)145		return 0;146 147	len = cpuidle_state_read_file(cpu, idlestate,148				      idlestate_value_files[which],149				      linebuf, sizeof(linebuf));150	if (len == 0)151		return 0;152 153	value = strtoull(linebuf, &endp, 0);154 155	if (endp == linebuf || errno == ERANGE)156		return 0;157 158	return value;159}160 161/* read access to files which contain one string */162 163enum idlestate_string {164	IDLESTATE_DESC,165	IDLESTATE_NAME,166	MAX_IDLESTATE_STRING_FILES167};168 169static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {170	[IDLESTATE_DESC] = "desc",171	[IDLESTATE_NAME] = "name",172};173 174 175static char *cpuidle_state_get_one_string(unsigned int cpu,176					unsigned int idlestate,177					enum idlestate_string which)178{179	char linebuf[MAX_LINE_LEN];180	char *result;181	unsigned int len;182 183	if (which >= MAX_IDLESTATE_STRING_FILES)184		return NULL;185 186	len = cpuidle_state_read_file(cpu, idlestate,187				      idlestate_string_files[which],188				      linebuf, sizeof(linebuf));189	if (len == 0)190		return NULL;191 192	result = strdup(linebuf);193	if (result == NULL)194		return NULL;195 196	if (result[strlen(result) - 1] == '\n')197		result[strlen(result) - 1] = '\0';198 199	return result;200}201 202/*203 * Returns:204 *    1  if disabled205 *    0  if enabled206 *    -1 if idlestate is not available207 *    -2 if disabling is not supported by the kernel208 */209int cpuidle_is_state_disabled(unsigned int cpu,210				unsigned int idlestate)211{212	if (cpuidle_state_count(cpu) <= idlestate)213		return -1;214 215	if (!cpuidle_state_file_exists(cpu, idlestate,216				 idlestate_value_files[IDLESTATE_DISABLE]))217		return -2;218	return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_DISABLE);219}220 221/*222 * Pass 1 as last argument to disable or 0 to enable the state223 * Returns:224 *    0  on success225 *    negative values on error, for example:226 *      -1 if idlestate is not available227 *      -2 if disabling is not supported by the kernel228 *      -3 No write access to disable/enable C-states229 */230int cpuidle_state_disable(unsigned int cpu,231			    unsigned int idlestate,232			    unsigned int disable)233{234	char value[SYSFS_PATH_MAX];235	int bytes_written;236 237	if (cpuidle_state_count(cpu) <= idlestate)238		return -1;239 240	if (!cpuidle_state_file_exists(cpu, idlestate,241				 idlestate_value_files[IDLESTATE_DISABLE]))242		return -2;243 244	snprintf(value, SYSFS_PATH_MAX, "%u", disable);245 246	bytes_written = cpuidle_state_write_file(cpu, idlestate, "disable",247						   value, sizeof(disable));248	if (bytes_written)249		return 0;250	return -3;251}252 253unsigned long cpuidle_state_latency(unsigned int cpu,254					  unsigned int idlestate)255{256	return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);257}258 259unsigned long cpuidle_state_residency(unsigned int cpu,260					  unsigned int idlestate)261{262	return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_RESIDENCY);263}264 265unsigned long cpuidle_state_usage(unsigned int cpu,266					unsigned int idlestate)267{268	return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_USAGE);269}270 271unsigned long long cpuidle_state_time(unsigned int cpu,272					unsigned int idlestate)273{274	return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_TIME);275}276 277char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate)278{279	return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME);280}281 282char *cpuidle_state_desc(unsigned int cpu, unsigned int idlestate)283{284	return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_DESC);285}286 287/*288 * Returns number of supported C-states of CPU core cpu289 * Negativ in error case290 * Zero if cpuidle does not export any C-states291 */292unsigned int cpuidle_state_count(unsigned int cpu)293{294	char file[SYSFS_PATH_MAX];295	struct stat statbuf;296	int idlestates = 1;297 298 299	snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");300	if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))301		return 0;302 303	snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);304	if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))305		return 0;306 307	while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {308		snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU309			 "cpu%u/cpuidle/state%d", cpu, idlestates);310		idlestates++;311	}312	idlestates--;313	return idlestates;314}315 316/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/317 318/*319 * helper function to read file from /sys into given buffer320 * fname is a relative path under "cpu/cpuidle/" dir321 */322static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,323					    size_t buflen)324{325	char path[SYSFS_PATH_MAX];326 327	snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);328 329	return cpupower_read_sysfs(path, buf, buflen);330}331 332 333 334/* read access to files which contain one string */335 336enum cpuidle_string {337	CPUIDLE_GOVERNOR,338	CPUIDLE_GOVERNOR_RO,339	CPUIDLE_DRIVER,340	MAX_CPUIDLE_STRING_FILES341};342 343static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {344	[CPUIDLE_GOVERNOR]	= "current_governor",345	[CPUIDLE_GOVERNOR_RO]	= "current_governor_ro",346	[CPUIDLE_DRIVER]	= "current_driver",347};348 349 350static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)351{352	char linebuf[MAX_LINE_LEN];353	char *result;354	unsigned int len;355 356	if (which >= MAX_CPUIDLE_STRING_FILES)357		return NULL;358 359	len = sysfs_cpuidle_read_file(cpuidle_string_files[which],360				linebuf, sizeof(linebuf));361	if (len == 0)362		return NULL;363 364	result = strdup(linebuf);365	if (result == NULL)366		return NULL;367 368	if (result[strlen(result) - 1] == '\n')369		result[strlen(result) - 1] = '\0';370 371	return result;372}373 374char *cpuidle_get_governor(void)375{376	char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);377	if (!tmp)378		return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);379	else380		return tmp;381}382 383char *cpuidle_get_driver(void)384{385	return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);386}387/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */388