brintos

brintos / linux-shallow public Read only

0
0
Text · 10.3 KiB · e13ff38 Raw
472 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 "helpers/sysfs.h"17 18unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen)19{20	int fd;21	ssize_t numread;22 23	fd = open(path, O_RDONLY);24	if (fd == -1)25		return 0;26 27	numread = read(fd, buf, buflen - 1);28	if (numread < 1) {29		close(fd);30		return 0;31	}32 33	buf[numread] = '\0';34	close(fd);35 36	return (unsigned int) numread;37}38 39/*40 * Detect whether a CPU is online41 *42 * Returns:43 *     1 -> if CPU is online44 *     0 -> if CPU is offline45 *     negative errno values in error case46 */47int sysfs_is_cpu_online(unsigned int cpu)48{49	char path[SYSFS_PATH_MAX];50	int fd;51	ssize_t numread;52	unsigned long long value;53	char linebuf[MAX_LINE_LEN];54	char *endp;55	struct stat statbuf;56 57	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu);58 59	if (stat(path, &statbuf) != 0)60		return 0;61 62	/*63	 * kernel without CONFIG_HOTPLUG_CPU64	 * -> cpuX directory exists, but not cpuX/online file65	 */66	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu);67	if (stat(path, &statbuf) != 0)68		return 1;69 70	fd = open(path, O_RDONLY);71	if (fd == -1)72		return -errno;73 74	numread = read(fd, linebuf, MAX_LINE_LEN - 1);75	if (numread < 1) {76		close(fd);77		return -EIO;78	}79	linebuf[numread] = '\0';80	close(fd);81 82	value = strtoull(linebuf, &endp, 0);83	if (value > 1)84		return -EINVAL;85 86	return value;87}88 89/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */90 91 92/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */93 94/*95 * helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir96 * exists.97 * For example the functionality to disable c-states was introduced in later98 * kernel versions, this function can be used to explicitly check for this99 * feature.100 *101 * returns 1 if the file exists, 0 otherwise.102 */103unsigned int sysfs_idlestate_file_exists(unsigned int cpu,104					 unsigned int idlestate,105					 const char *fname)106{107	char path[SYSFS_PATH_MAX];108	struct stat statbuf;109 110 111	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",112		 cpu, idlestate, fname);113	if (stat(path, &statbuf) != 0)114		return 0;115	return 1;116}117 118/*119 * helper function to read file from /sys into given buffer120 * fname is a relative path under "cpuX/cpuidle/stateX/" dir121 * cstates starting with 0, C0 is not counted as cstate.122 * This means if you want C1 info, pass 0 as idlestate param123 */124unsigned int sysfs_idlestate_read_file(unsigned int cpu, unsigned int idlestate,125			     const char *fname, char *buf, size_t buflen)126{127	char path[SYSFS_PATH_MAX];128	int fd;129	ssize_t numread;130 131	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",132		 cpu, idlestate, fname);133 134	fd = open(path, O_RDONLY);135	if (fd == -1)136		return 0;137 138	numread = read(fd, buf, buflen - 1);139	if (numread < 1) {140		close(fd);141		return 0;142	}143 144	buf[numread] = '\0';145	close(fd);146 147	return (unsigned int) numread;148}149 150/* 151 * helper function to write a new value to a /sys file152 * fname is a relative path under "../cpuX/cpuidle/cstateY/" dir153 *154 * Returns the number of bytes written or 0 on error155 */156static157unsigned int sysfs_idlestate_write_file(unsigned int cpu,158					unsigned int idlestate,159					const char *fname,160					const char *value, size_t len)161{162	char path[SYSFS_PATH_MAX];163	int fd;164	ssize_t numwrite;165 166	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",167		 cpu, idlestate, fname);168 169	fd = open(path, O_WRONLY);170	if (fd == -1)171		return 0;172 173	numwrite = write(fd, value, len);174	if (numwrite < 1) {175		close(fd);176		return 0;177	}178 179	close(fd);180 181	return (unsigned int) numwrite;182}183 184/* read access to files which contain one numeric value */185 186enum idlestate_value {187	IDLESTATE_USAGE,188	IDLESTATE_POWER,189	IDLESTATE_LATENCY,190	IDLESTATE_TIME,191	IDLESTATE_DISABLE,192	MAX_IDLESTATE_VALUE_FILES193};194 195static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = {196	[IDLESTATE_USAGE] = "usage",197	[IDLESTATE_POWER] = "power",198	[IDLESTATE_LATENCY] = "latency",199	[IDLESTATE_TIME]  = "time",200	[IDLESTATE_DISABLE]  = "disable",201};202 203static unsigned long long sysfs_idlestate_get_one_value(unsigned int cpu,204						     unsigned int idlestate,205						     enum idlestate_value which)206{207	unsigned long long value;208	unsigned int len;209	char linebuf[MAX_LINE_LEN];210	char *endp;211 212	if (which >= MAX_IDLESTATE_VALUE_FILES)213		return 0;214 215	len = sysfs_idlestate_read_file(cpu, idlestate,216					idlestate_value_files[which],217					linebuf, sizeof(linebuf));218	if (len == 0)219		return 0;220 221	value = strtoull(linebuf, &endp, 0);222 223	if (endp == linebuf || errno == ERANGE)224		return 0;225 226	return value;227}228 229/* read access to files which contain one string */230 231enum idlestate_string {232	IDLESTATE_DESC,233	IDLESTATE_NAME,234	MAX_IDLESTATE_STRING_FILES235};236 237static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {238	[IDLESTATE_DESC] = "desc",239	[IDLESTATE_NAME] = "name",240};241 242 243static char *sysfs_idlestate_get_one_string(unsigned int cpu,244					unsigned int idlestate,245					enum idlestate_string which)246{247	char linebuf[MAX_LINE_LEN];248	char *result;249	unsigned int len;250 251	if (which >= MAX_IDLESTATE_STRING_FILES)252		return NULL;253 254	len = sysfs_idlestate_read_file(cpu, idlestate,255					idlestate_string_files[which],256					linebuf, sizeof(linebuf));257	if (len == 0)258		return NULL;259 260	result = strdup(linebuf);261	if (result == NULL)262		return NULL;263 264	if (result[strlen(result) - 1] == '\n')265		result[strlen(result) - 1] = '\0';266 267	return result;268}269 270/*271 * Returns:272 *    1  if disabled273 *    0  if enabled274 *    -1 if idlestate is not available275 *    -2 if disabling is not supported by the kernel276 */277int sysfs_is_idlestate_disabled(unsigned int cpu,278				unsigned int idlestate)279{280	if (sysfs_get_idlestate_count(cpu) <= idlestate)281		return -1;282 283	if (!sysfs_idlestate_file_exists(cpu, idlestate,284				 idlestate_value_files[IDLESTATE_DISABLE]))285		return -2;286	return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_DISABLE);287}288 289/*290 * Pass 1 as last argument to disable or 0 to enable the state291 * Returns:292 *    0  on success293 *    negative values on error, for example:294 *      -1 if idlestate is not available295 *      -2 if disabling is not supported by the kernel296 *      -3 No write access to disable/enable C-states297 */298int sysfs_idlestate_disable(unsigned int cpu,299			    unsigned int idlestate,300			    unsigned int disable)301{302	char value[SYSFS_PATH_MAX];303	int bytes_written;304 305	if (sysfs_get_idlestate_count(cpu) <= idlestate)306		return -1;307 308	if (!sysfs_idlestate_file_exists(cpu, idlestate,309				 idlestate_value_files[IDLESTATE_DISABLE]))310		return -2;311 312	snprintf(value, SYSFS_PATH_MAX, "%u", disable);313 314	bytes_written = sysfs_idlestate_write_file(cpu, idlestate, "disable",315						   value, sizeof(disable));316	if (bytes_written)317		return 0;318	return -3;319}320 321unsigned long sysfs_get_idlestate_latency(unsigned int cpu,322					  unsigned int idlestate)323{324	return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);325}326 327unsigned long sysfs_get_idlestate_usage(unsigned int cpu,328					unsigned int idlestate)329{330	return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_USAGE);331}332 333unsigned long long sysfs_get_idlestate_time(unsigned int cpu,334					unsigned int idlestate)335{336	return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_TIME);337}338 339char *sysfs_get_idlestate_name(unsigned int cpu, unsigned int idlestate)340{341	return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_NAME);342}343 344char *sysfs_get_idlestate_desc(unsigned int cpu, unsigned int idlestate)345{346	return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_DESC);347}348 349/*350 * Returns number of supported C-states of CPU core cpu351 * Negativ in error case352 * Zero if cpuidle does not export any C-states353 */354unsigned int sysfs_get_idlestate_count(unsigned int cpu)355{356	char file[SYSFS_PATH_MAX];357	struct stat statbuf;358	int idlestates = 1;359 360 361	snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");362	if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))363		return 0;364 365	snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);366	if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))367		return 0;368 369	while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {370		snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU371			 "cpu%u/cpuidle/state%d", cpu, idlestates);372		idlestates++;373	}374	idlestates--;375	return idlestates;376}377 378/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/379 380/*381 * helper function to read file from /sys into given buffer382 * fname is a relative path under "cpu/cpuidle/" dir383 */384static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,385					    size_t buflen)386{387	char path[SYSFS_PATH_MAX];388 389	snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);390 391	return sysfs_read_file(path, buf, buflen);392}393 394 395 396/* read access to files which contain one string */397 398enum cpuidle_string {399	CPUIDLE_GOVERNOR,400	CPUIDLE_GOVERNOR_RO,401	CPUIDLE_DRIVER,402	MAX_CPUIDLE_STRING_FILES403};404 405static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {406	[CPUIDLE_GOVERNOR]	= "current_governor",407	[CPUIDLE_GOVERNOR_RO]	= "current_governor_ro",408	[CPUIDLE_DRIVER]	= "current_driver",409};410 411 412static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)413{414	char linebuf[MAX_LINE_LEN];415	char *result;416	unsigned int len;417 418	if (which >= MAX_CPUIDLE_STRING_FILES)419		return NULL;420 421	len = sysfs_cpuidle_read_file(cpuidle_string_files[which],422				linebuf, sizeof(linebuf));423	if (len == 0)424		return NULL;425 426	result = strdup(linebuf);427	if (result == NULL)428		return NULL;429 430	if (result[strlen(result) - 1] == '\n')431		result[strlen(result) - 1] = '\0';432 433	return result;434}435 436char *sysfs_get_cpuidle_governor(void)437{438	char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);439	if (!tmp)440		return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);441	else442		return tmp;443}444 445char *sysfs_get_cpuidle_driver(void)446{447	return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);448}449/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */450 451/*452 * Get sched_mc or sched_smt settings453 * Pass "mc" or "smt" as argument454 *455 * Returns negative value on failure456 */457int sysfs_get_sched(const char *smt_mc)458{459	return -ENODEV;460}461 462/*463 * Get sched_mc or sched_smt settings464 * Pass "mc" or "smt" as argument465 *466 * Returns negative value on failure467 */468int sysfs_set_sched(const char *smt_mc, int val)469{470	return -ENODEV;471}472