Path: blob/master/tools/power/cpupower/utils/helpers/sysfs.c
26299 views
// SPDX-License-Identifier: GPL-2.0-only1/*2* (C) 2004-2009 Dominik Brodowski <[email protected]>3* (C) 2011 Thomas Renninger <[email protected]> Novell Inc.4*/56#include <stdio.h>7#include <errno.h>8#include <stdlib.h>9#include <string.h>10#include <sys/types.h>11#include <sys/stat.h>12#include <fcntl.h>13#include <unistd.h>1415#include "helpers/sysfs.h"1617unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen)18{19int fd;20ssize_t numread;2122fd = open(path, O_RDONLY);23if (fd == -1)24return 0;2526numread = read(fd, buf, buflen - 1);27if (numread < 1) {28close(fd);29return 0;30}3132buf[numread] = '\0';33close(fd);3435return (unsigned int) numread;36}3738/*39* Detect whether a CPU is online40*41* Returns:42* 1 -> if CPU is online43* 0 -> if CPU is offline44* negative errno values in error case45*/46int sysfs_is_cpu_online(unsigned int cpu)47{48char path[SYSFS_PATH_MAX];49int fd;50ssize_t numread;51unsigned long long value;52char linebuf[MAX_LINE_LEN];53char *endp;54struct stat statbuf;5556snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu);5758if (stat(path, &statbuf) != 0)59return 0;6061/*62* kernel without CONFIG_HOTPLUG_CPU63* -> cpuX directory exists, but not cpuX/online file64*/65snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu);66if (stat(path, &statbuf) != 0)67return 1;6869fd = open(path, O_RDONLY);70if (fd == -1)71return -errno;7273numread = read(fd, linebuf, MAX_LINE_LEN - 1);74if (numread < 1) {75close(fd);76return -EIO;77}78linebuf[numread] = '\0';79close(fd);8081value = strtoull(linebuf, &endp, 0);82if (value > 1)83return -EINVAL;8485return value;86}8788/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */899091/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */9293/*94* helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir95* exists.96* For example the functionality to disable c-states was introduced in later97* kernel versions, this function can be used to explicitly check for this98* feature.99*100* returns 1 if the file exists, 0 otherwise.101*/102unsigned int sysfs_idlestate_file_exists(unsigned int cpu,103unsigned int idlestate,104const char *fname)105{106char path[SYSFS_PATH_MAX];107struct stat statbuf;108109110snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",111cpu, idlestate, fname);112if (stat(path, &statbuf) != 0)113return 0;114return 1;115}116117/*118* helper function to read file from /sys into given buffer119* fname is a relative path under "cpuX/cpuidle/stateX/" dir120* cstates starting with 0, C0 is not counted as cstate.121* This means if you want C1 info, pass 0 as idlestate param122*/123unsigned int sysfs_idlestate_read_file(unsigned int cpu, unsigned int idlestate,124const char *fname, char *buf, size_t buflen)125{126char path[SYSFS_PATH_MAX];127int fd;128ssize_t numread;129130snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",131cpu, idlestate, fname);132133fd = open(path, O_RDONLY);134if (fd == -1)135return 0;136137numread = read(fd, buf, buflen - 1);138if (numread < 1) {139close(fd);140return 0;141}142143buf[numread] = '\0';144close(fd);145146return (unsigned int) numread;147}148149/*150* helper function to write a new value to a /sys file151* fname is a relative path under "../cpuX/cpuidle/cstateY/" dir152*153* Returns the number of bytes written or 0 on error154*/155static156unsigned int sysfs_idlestate_write_file(unsigned int cpu,157unsigned int idlestate,158const char *fname,159const char *value, size_t len)160{161char path[SYSFS_PATH_MAX];162int fd;163ssize_t numwrite;164165snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",166cpu, idlestate, fname);167168fd = open(path, O_WRONLY);169if (fd == -1)170return 0;171172numwrite = write(fd, value, len);173if (numwrite < 1) {174close(fd);175return 0;176}177178close(fd);179180return (unsigned int) numwrite;181}182183/* read access to files which contain one numeric value */184185enum idlestate_value {186IDLESTATE_USAGE,187IDLESTATE_POWER,188IDLESTATE_LATENCY,189IDLESTATE_TIME,190IDLESTATE_DISABLE,191MAX_IDLESTATE_VALUE_FILES192};193194static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = {195[IDLESTATE_USAGE] = "usage",196[IDLESTATE_POWER] = "power",197[IDLESTATE_LATENCY] = "latency",198[IDLESTATE_TIME] = "time",199[IDLESTATE_DISABLE] = "disable",200};201202static unsigned long long sysfs_idlestate_get_one_value(unsigned int cpu,203unsigned int idlestate,204enum idlestate_value which)205{206unsigned long long value;207unsigned int len;208char linebuf[MAX_LINE_LEN];209char *endp;210211if (which >= MAX_IDLESTATE_VALUE_FILES)212return 0;213214len = sysfs_idlestate_read_file(cpu, idlestate,215idlestate_value_files[which],216linebuf, sizeof(linebuf));217if (len == 0)218return 0;219220value = strtoull(linebuf, &endp, 0);221222if (endp == linebuf || errno == ERANGE)223return 0;224225return value;226}227228/* read access to files which contain one string */229230enum idlestate_string {231IDLESTATE_DESC,232IDLESTATE_NAME,233MAX_IDLESTATE_STRING_FILES234};235236static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {237[IDLESTATE_DESC] = "desc",238[IDLESTATE_NAME] = "name",239};240241242static char *sysfs_idlestate_get_one_string(unsigned int cpu,243unsigned int idlestate,244enum idlestate_string which)245{246char linebuf[MAX_LINE_LEN];247char *result;248unsigned int len;249250if (which >= MAX_IDLESTATE_STRING_FILES)251return NULL;252253len = sysfs_idlestate_read_file(cpu, idlestate,254idlestate_string_files[which],255linebuf, sizeof(linebuf));256if (len == 0)257return NULL;258259result = strdup(linebuf);260if (result == NULL)261return NULL;262263if (result[strlen(result) - 1] == '\n')264result[strlen(result) - 1] = '\0';265266return result;267}268269/*270* Returns:271* 1 if disabled272* 0 if enabled273* -1 if idlestate is not available274* -2 if disabling is not supported by the kernel275*/276int sysfs_is_idlestate_disabled(unsigned int cpu,277unsigned int idlestate)278{279if (sysfs_get_idlestate_count(cpu) <= idlestate)280return -1;281282if (!sysfs_idlestate_file_exists(cpu, idlestate,283idlestate_value_files[IDLESTATE_DISABLE]))284return -2;285return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_DISABLE);286}287288/*289* Pass 1 as last argument to disable or 0 to enable the state290* Returns:291* 0 on success292* negative values on error, for example:293* -1 if idlestate is not available294* -2 if disabling is not supported by the kernel295* -3 No write access to disable/enable C-states296*/297int sysfs_idlestate_disable(unsigned int cpu,298unsigned int idlestate,299unsigned int disable)300{301char value[SYSFS_PATH_MAX];302int bytes_written;303304if (sysfs_get_idlestate_count(cpu) <= idlestate)305return -1;306307if (!sysfs_idlestate_file_exists(cpu, idlestate,308idlestate_value_files[IDLESTATE_DISABLE]))309return -2;310311snprintf(value, SYSFS_PATH_MAX, "%u", disable);312313bytes_written = sysfs_idlestate_write_file(cpu, idlestate, "disable",314value, sizeof(disable));315if (bytes_written)316return 0;317return -3;318}319320unsigned long sysfs_get_idlestate_latency(unsigned int cpu,321unsigned int idlestate)322{323return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);324}325326unsigned long sysfs_get_idlestate_usage(unsigned int cpu,327unsigned int idlestate)328{329return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_USAGE);330}331332unsigned long long sysfs_get_idlestate_time(unsigned int cpu,333unsigned int idlestate)334{335return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_TIME);336}337338char *sysfs_get_idlestate_name(unsigned int cpu, unsigned int idlestate)339{340return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_NAME);341}342343char *sysfs_get_idlestate_desc(unsigned int cpu, unsigned int idlestate)344{345return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_DESC);346}347348/*349* Returns number of supported C-states of CPU core cpu350* Negativ in error case351* Zero if cpuidle does not export any C-states352*/353unsigned int sysfs_get_idlestate_count(unsigned int cpu)354{355char file[SYSFS_PATH_MAX];356struct stat statbuf;357int idlestates = 1;358359360snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");361if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))362return 0;363364snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);365if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))366return 0;367368while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {369snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU370"cpu%u/cpuidle/state%d", cpu, idlestates);371idlestates++;372}373idlestates--;374return idlestates;375}376377/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/378379/*380* helper function to read file from /sys into given buffer381* fname is a relative path under "cpu/cpuidle/" dir382*/383static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,384size_t buflen)385{386char path[SYSFS_PATH_MAX];387388snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);389390return sysfs_read_file(path, buf, buflen);391}392393394395/* read access to files which contain one string */396397enum cpuidle_string {398CPUIDLE_GOVERNOR,399CPUIDLE_GOVERNOR_RO,400CPUIDLE_DRIVER,401MAX_CPUIDLE_STRING_FILES402};403404static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {405[CPUIDLE_GOVERNOR] = "current_governor",406[CPUIDLE_GOVERNOR_RO] = "current_governor_ro",407[CPUIDLE_DRIVER] = "current_driver",408};409410411static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)412{413char linebuf[MAX_LINE_LEN];414char *result;415unsigned int len;416417if (which >= MAX_CPUIDLE_STRING_FILES)418return NULL;419420len = sysfs_cpuidle_read_file(cpuidle_string_files[which],421linebuf, sizeof(linebuf));422if (len == 0)423return NULL;424425result = strdup(linebuf);426if (result == NULL)427return NULL;428429if (result[strlen(result) - 1] == '\n')430result[strlen(result) - 1] = '\0';431432return result;433}434435char *sysfs_get_cpuidle_governor(void)436{437char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);438if (!tmp)439return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);440else441return tmp;442}443444char *sysfs_get_cpuidle_driver(void)445{446return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);447}448/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */449450/*451* Get sched_mc or sched_smt settings452* Pass "mc" or "smt" as argument453*454* Returns negative value on failure455*/456int sysfs_get_sched(const char *smt_mc)457{458return -ENODEV;459}460461/*462* Get sched_mc or sched_smt settings463* Pass "mc" or "smt" as argument464*465* Returns negative value on failure466*/467int sysfs_set_sched(const char *smt_mc, int val)468{469return -ENODEV;470}471472473