Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "cpupower_intern.h"
#include "acpi_cppc.h"
/* ACPI CPPC sysfs access ***********************************************/
static int acpi_cppc_read_file(unsigned int cpu, const char *fname,
char *buf, size_t buflen)
{
char path[SYSFS_PATH_MAX];
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/acpi_cppc/%s",
cpu, fname);
return cpupower_read_sysfs(path, buf, buflen);
}
static const char * const acpi_cppc_value_files[] = {
[HIGHEST_PERF] = "highest_perf",
[LOWEST_PERF] = "lowest_perf",
[NOMINAL_PERF] = "nominal_perf",
[LOWEST_NONLINEAR_PERF] = "lowest_nonlinear_perf",
[LOWEST_FREQ] = "lowest_freq",
[NOMINAL_FREQ] = "nominal_freq",
[REFERENCE_PERF] = "reference_perf",
[WRAPAROUND_TIME] = "wraparound_time"
};
unsigned long acpi_cppc_get_data(unsigned int cpu, enum acpi_cppc_value which)
{
unsigned long long value;
unsigned int len;
char linebuf[MAX_LINE_LEN];
char *endp;
if (which >= MAX_CPPC_VALUE_FILES)
return 0;
len = acpi_cppc_read_file(cpu, acpi_cppc_value_files[which],
linebuf, sizeof(linebuf));
if (len == 0)
return 0;
value = strtoull(linebuf, &endp, 0);
if (endp == linebuf || errno == ERANGE)
return 0;
return value;
}
@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __ACPI_CPPC_H__
#define __ACPI_CPPC_H__
enum acpi_cppc_value {
HIGHEST_PERF,
LOWEST_PERF,
NOMINAL_PERF,
LOWEST_NONLINEAR_PERF,
LOWEST_FREQ,
NOMINAL_FREQ,
REFERENCE_PERF,
WRAPAROUND_TIME,
MAX_CPPC_VALUE_FILES
};
unsigned long acpi_cppc_get_data(unsigned int cpu,
enum acpi_cppc_value which);
#endif /* _ACPI_CPPC_H */
@@ -0,0 +1,803 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
*/
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "cpufreq.h"
#include "cpupower_intern.h"
/* CPUFREQ sysfs access **************************************************/
/* helper function to read file from /sys into given buffer */
/* fname is a relative path under "cpuX/cpufreq" dir */
static unsigned int sysfs_cpufreq_read_file(unsigned int cpu, const char *fname,
char *buf, size_t buflen)
{
char path[SYSFS_PATH_MAX];
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
cpu, fname);
return cpupower_read_sysfs(path, buf, buflen);
}
/* helper function to write a new value to a /sys file */
/* fname is a relative path under "cpuX/cpufreq" dir */
static unsigned int sysfs_cpufreq_write_file(unsigned int cpu,
const char *fname,
const char *value, size_t len)
{
char path[SYSFS_PATH_MAX];
int fd;
ssize_t numwrite;
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpufreq/%s",
cpu, fname);
fd = open(path, O_WRONLY);
if (fd == -1)
return 0;
numwrite = write(fd, value, len);
if (numwrite < 1) {
close(fd);
return 0;
}
close(fd);
return (unsigned int) numwrite;
}
/* read access to files which contain one numeric value */
enum cpufreq_value {
CPUINFO_CUR_FREQ,
CPUINFO_MIN_FREQ,
CPUINFO_MAX_FREQ,
CPUINFO_LATENCY,
SCALING_CUR_FREQ,
SCALING_MIN_FREQ,
SCALING_MAX_FREQ,
STATS_NUM_TRANSITIONS,
MAX_CPUFREQ_VALUE_READ_FILES
};
static const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = {
[CPUINFO_CUR_FREQ] = "cpuinfo_cur_freq",
[CPUINFO_MIN_FREQ] = "cpuinfo_min_freq",
[CPUINFO_MAX_FREQ] = "cpuinfo_max_freq",
[CPUINFO_LATENCY] = "cpuinfo_transition_latency",
[SCALING_CUR_FREQ] = "scaling_cur_freq",
[SCALING_MIN_FREQ] = "scaling_min_freq",
[SCALING_MAX_FREQ] = "scaling_max_freq",
[STATS_NUM_TRANSITIONS] = "stats/total_trans"
};
unsigned long cpufreq_get_sysfs_value_from_table(unsigned int cpu,
const char **table,
unsigned int index,
unsigned int size)
{
unsigned long value;
unsigned int len;
char linebuf[MAX_LINE_LEN];
char *endp;
if (!table || index >= size || !table[index])
return 0;
len = sysfs_cpufreq_read_file(cpu, table[index], linebuf,
sizeof(linebuf));
if (len == 0)
return 0;
if (!strcmp(linebuf, "enabled\n"))
return 1;
if (!strcmp(linebuf, "disabled\n"))
return 0;
value = strtoul(linebuf, &endp, 0);
if (endp == linebuf || errno == ERANGE)
return 0;
return value;
}
static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu,
enum cpufreq_value which)
{
return cpufreq_get_sysfs_value_from_table(cpu, cpufreq_value_files,
which,
MAX_CPUFREQ_VALUE_READ_FILES);
}
/* read access to files which contain one string */
enum cpufreq_string {
SCALING_DRIVER,
SCALING_GOVERNOR,
ENERGY_PERFORMANCE_PREFERENCE,
MAX_CPUFREQ_STRING_FILES
};
static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = {
[SCALING_DRIVER] = "scaling_driver",
[SCALING_GOVERNOR] = "scaling_governor",
[ENERGY_PERFORMANCE_PREFERENCE] = "energy_performance_preference",
};
static char *sysfs_cpufreq_get_one_string(unsigned int cpu,
enum cpufreq_string which)
{
char linebuf[MAX_LINE_LEN];
char *result;
unsigned int len;
if (which >= MAX_CPUFREQ_STRING_FILES)
return NULL;
len = sysfs_cpufreq_read_file(cpu, cpufreq_string_files[which],
linebuf, sizeof(linebuf));
if (len == 0)
return NULL;
result = strdup(linebuf);
if (result == NULL)
return NULL;
if (result[strlen(result) - 1] == '\n')
result[strlen(result) - 1] = '\0';
return result;
}
/* write access */
enum cpufreq_write {
WRITE_SCALING_MIN_FREQ,
WRITE_SCALING_MAX_FREQ,
WRITE_SCALING_GOVERNOR,
WRITE_SCALING_SET_SPEED,
MAX_CPUFREQ_WRITE_FILES
};
static const char *cpufreq_write_files[MAX_CPUFREQ_WRITE_FILES] = {
[WRITE_SCALING_MIN_FREQ] = "scaling_min_freq",
[WRITE_SCALING_MAX_FREQ] = "scaling_max_freq",
[WRITE_SCALING_GOVERNOR] = "scaling_governor",
[WRITE_SCALING_SET_SPEED] = "scaling_setspeed",
};
static int sysfs_cpufreq_write_one_value(unsigned int cpu,
enum cpufreq_write which,
const char *new_value, size_t len)
{
if (which >= MAX_CPUFREQ_WRITE_FILES)
return 0;
if (sysfs_cpufreq_write_file(cpu, cpufreq_write_files[which],
new_value, len) != len)
return -ENODEV;
return 0;
};
unsigned long cpufreq_get_freq_kernel(unsigned int cpu)
{
return sysfs_cpufreq_get_one_value(cpu, SCALING_CUR_FREQ);
}
unsigned long cpufreq_get_freq_hardware(unsigned int cpu)
{
return sysfs_cpufreq_get_one_value(cpu, CPUINFO_CUR_FREQ);
}
unsigned long cpufreq_get_transition_latency(unsigned int cpu)
{
return sysfs_cpufreq_get_one_value(cpu, CPUINFO_LATENCY);
}
char *cpufreq_get_energy_performance_preference(unsigned int cpu)
{
return sysfs_cpufreq_get_one_string(cpu, ENERGY_PERFORMANCE_PREFERENCE);
}
void cpufreq_put_energy_performance_preference(char *ptr)
{
if (!ptr)
return;
free(ptr);
}
int cpufreq_get_hardware_limits(unsigned int cpu,
unsigned long *min,
unsigned long *max)
{
if ((!min) || (!max))
return -EINVAL;
*min = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MIN_FREQ);
if (!*min)
return -ENODEV;
*max = sysfs_cpufreq_get_one_value(cpu, CPUINFO_MAX_FREQ);
if (!*max)
return -ENODEV;
return 0;
}
char *cpufreq_get_driver(unsigned int cpu)
{
return sysfs_cpufreq_get_one_string(cpu, SCALING_DRIVER);
}
void cpufreq_put_driver(char *ptr)
{
if (!ptr)
return;
free(ptr);
}
struct cpufreq_policy *cpufreq_get_policy(unsigned int cpu)
{
struct cpufreq_policy *policy;
policy = malloc(sizeof(struct cpufreq_policy));
if (!policy)
return NULL;
policy->governor = sysfs_cpufreq_get_one_string(cpu, SCALING_GOVERNOR);
if (!policy->governor) {
free(policy);
return NULL;
}
policy->min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
policy->max = sysfs_cpufreq_get_one_value(cpu, SCALING_MAX_FREQ);
if ((!policy->min) || (!policy->max)) {
free(policy->governor);
free(policy);
return NULL;
}
return policy;
}
void cpufreq_put_policy(struct cpufreq_policy *policy)
{
if ((!policy) || (!policy->governor))
return;
free(policy->governor);
policy->governor = NULL;
free(policy);
}
struct cpufreq_available_governors *cpufreq_get_available_governors(unsigned
int cpu)
{
struct cpufreq_available_governors *first = NULL;
struct cpufreq_available_governors *current = NULL;
char linebuf[MAX_LINE_LEN];
unsigned int pos, i;
unsigned int len;
len = sysfs_cpufreq_read_file(cpu, "scaling_available_governors",
linebuf, sizeof(linebuf));
if (len == 0)
return NULL;
pos = 0;
for (i = 0; i < len; i++) {
if (linebuf[i] == ' ' || linebuf[i] == '\n') {
if (i - pos < 2)
continue;
if (current) {
current->next = malloc(sizeof(*current));
if (!current->next)
goto error_out;
current = current->next;
} else {
first = malloc(sizeof(*first));
if (!first)
return NULL;
current = first;
}
current->first = first;
current->next = NULL;
current->governor = malloc(i - pos + 1);
if (!current->governor)
goto error_out;
memcpy(current->governor, linebuf + pos, i - pos);
current->governor[i - pos] = '\0';
pos = i + 1;
}
}
return first;
error_out:
while (first) {
current = first->next;
if (first->governor)
free(first->governor);
free(first);
first = current;
}
return NULL;
}
void cpufreq_put_available_governors(struct cpufreq_available_governors *any)
{
struct cpufreq_available_governors *tmp, *next;
if (!any)
return;
tmp = any->first;
while (tmp) {
next = tmp->next;
if (tmp->governor)
free(tmp->governor);
free(tmp);
tmp = next;
}
}
struct cpufreq_available_frequencies
*cpufreq_get_available_frequencies(unsigned int cpu)
{
struct cpufreq_available_frequencies *first = NULL;
struct cpufreq_available_frequencies *current = NULL;
char one_value[SYSFS_PATH_MAX];
char linebuf[MAX_LINE_LEN];
unsigned int pos, i;
unsigned int len;
len = sysfs_cpufreq_read_file(cpu, "scaling_available_frequencies",
linebuf, sizeof(linebuf));
if (len == 0)
return NULL;
pos = 0;
for (i = 0; i < len; i++) {
if (linebuf[i] == ' ' || linebuf[i] == '\n') {
if (i - pos < 2)
continue;
if (i - pos >= SYSFS_PATH_MAX)
goto error_out;
if (current) {
current->next = malloc(sizeof(*current));
if (!current->next)
goto error_out;
current = current->next;
} else {
first = malloc(sizeof(*first));
if (!first)
return NULL;
current = first;
}
current->first = first;
current->next = NULL;
memcpy(one_value, linebuf + pos, i - pos);
one_value[i - pos] = '\0';
if (sscanf(one_value, "%lu", &current->frequency) != 1)
goto error_out;
pos = i + 1;
}
}
return first;
error_out:
while (first) {
current = first->next;
free(first);
first = current;
}
return NULL;
}
struct cpufreq_available_frequencies
*cpufreq_get_boost_frequencies(unsigned int cpu)
{
struct cpufreq_available_frequencies *first = NULL;
struct cpufreq_available_frequencies *current = NULL;
char one_value[SYSFS_PATH_MAX];
char linebuf[MAX_LINE_LEN];
unsigned int pos, i;
unsigned int len;
len = sysfs_cpufreq_read_file(cpu, "scaling_boost_frequencies",
linebuf, sizeof(linebuf));
if (len == 0)
return NULL;
pos = 0;
for (i = 0; i < len; i++) {
if (linebuf[i] == ' ' || linebuf[i] == '\n') {
if (i - pos < 2)
continue;
if (i - pos >= SYSFS_PATH_MAX)
goto error_out;
if (current) {
current->next = malloc(sizeof(*current));
if (!current->next)
goto error_out;
current = current->next;
} else {
first = malloc(sizeof(*first));
if (!first)
return NULL;
current = first;
}
current->first = first;
current->next = NULL;
memcpy(one_value, linebuf + pos, i - pos);
one_value[i - pos] = '\0';
if (sscanf(one_value, "%lu", &current->frequency) != 1)
goto error_out;
pos = i + 1;
}
}
return first;
error_out:
while (first) {
current = first->next;
free(first);
first = current;
}
return NULL;
}
void cpufreq_put_available_frequencies(struct cpufreq_available_frequencies *any)
{
struct cpufreq_available_frequencies *tmp, *next;
if (!any)
return;
tmp = any->first;
while (tmp) {
next = tmp->next;
free(tmp);
tmp = next;
}
}
void cpufreq_put_boost_frequencies(struct cpufreq_available_frequencies *any)
{
cpufreq_put_available_frequencies(any);
}
static struct cpufreq_affected_cpus *sysfs_get_cpu_list(unsigned int cpu,
const char *file)
{
struct cpufreq_affected_cpus *first = NULL;
struct cpufreq_affected_cpus *current = NULL;
char one_value[SYSFS_PATH_MAX];
char linebuf[MAX_LINE_LEN];
unsigned int pos, i;
unsigned int len;
len = sysfs_cpufreq_read_file(cpu, file, linebuf, sizeof(linebuf));
if (len == 0)
return NULL;
pos = 0;
for (i = 0; i < len; i++) {
if (i == len || linebuf[i] == ' ' || linebuf[i] == '\n') {
if (i - pos < 1)
continue;
if (i - pos >= SYSFS_PATH_MAX)
goto error_out;
if (current) {
current->next = malloc(sizeof(*current));
if (!current->next)
goto error_out;
current = current->next;
} else {
first = malloc(sizeof(*first));
if (!first)
return NULL;
current = first;
}
current->first = first;
current->next = NULL;
memcpy(one_value, linebuf + pos, i - pos);
one_value[i - pos] = '\0';
if (sscanf(one_value, "%u", &current->cpu) != 1)
goto error_out;
pos = i + 1;
}
}
return first;
error_out:
while (first) {
current = first->next;
free(first);
first = current;
}
return NULL;
}
struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned int cpu)
{
return sysfs_get_cpu_list(cpu, "affected_cpus");
}
void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *any)
{
struct cpufreq_affected_cpus *tmp, *next;
if (!any)
return;
tmp = any->first;
while (tmp) {
next = tmp->next;
free(tmp);
tmp = next;
}
}
struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned int cpu)
{
return sysfs_get_cpu_list(cpu, "related_cpus");
}
void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *any)
{
cpufreq_put_affected_cpus(any);
}
static int verify_gov(char *new_gov, char *passed_gov)
{
unsigned int i, j = 0;
if (!passed_gov || (strlen(passed_gov) > 19))
return -EINVAL;
strncpy(new_gov, passed_gov, 20);
for (i = 0; i < 20; i++) {
if (j) {
new_gov[i] = '\0';
continue;
}
if ((new_gov[i] >= 'a') && (new_gov[i] <= 'z'))
continue;
if ((new_gov[i] >= 'A') && (new_gov[i] <= 'Z'))
continue;
if (new_gov[i] == '-')
continue;
if (new_gov[i] == '_')
continue;
if (new_gov[i] == '\0') {
j = 1;
continue;
}
return -EINVAL;
}
new_gov[19] = '\0';
return 0;
}
int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy)
{
char min[SYSFS_PATH_MAX];
char max[SYSFS_PATH_MAX];
char gov[SYSFS_PATH_MAX];
int ret;
unsigned long old_min;
int write_max_first;
if (!policy || !(policy->governor))
return -EINVAL;
if (policy->max < policy->min)
return -EINVAL;
if (verify_gov(gov, policy->governor))
return -EINVAL;
snprintf(min, SYSFS_PATH_MAX, "%lu", policy->min);
snprintf(max, SYSFS_PATH_MAX, "%lu", policy->max);
old_min = sysfs_cpufreq_get_one_value(cpu, SCALING_MIN_FREQ);
write_max_first = (old_min && (policy->max < old_min) ? 0 : 1);
if (write_max_first) {
ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
max, strlen(max));
if (ret)
return ret;
}
ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ, min,
strlen(min));
if (ret)
return ret;
if (!write_max_first) {
ret = sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
max, strlen(max));
if (ret)
return ret;
}
return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
gov, strlen(gov));
}
int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq)
{
char value[SYSFS_PATH_MAX];
snprintf(value, SYSFS_PATH_MAX, "%lu", min_freq);
return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MIN_FREQ,
value, strlen(value));
}
int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq)
{
char value[SYSFS_PATH_MAX];
snprintf(value, SYSFS_PATH_MAX, "%lu", max_freq);
return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_MAX_FREQ,
value, strlen(value));
}
int cpufreq_modify_policy_governor(unsigned int cpu, char *governor)
{
char new_gov[SYSFS_PATH_MAX];
if ((!governor) || (strlen(governor) > 19))
return -EINVAL;
if (verify_gov(new_gov, governor))
return -EINVAL;
return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_GOVERNOR,
new_gov, strlen(new_gov));
}
int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency)
{
struct cpufreq_policy *pol = cpufreq_get_policy(cpu);
char userspace_gov[] = "userspace";
char freq[SYSFS_PATH_MAX];
int ret;
if (!pol)
return -ENODEV;
if (strncmp(pol->governor, userspace_gov, 9) != 0) {
ret = cpufreq_modify_policy_governor(cpu, userspace_gov);
if (ret) {
cpufreq_put_policy(pol);
return ret;
}
}
cpufreq_put_policy(pol);
snprintf(freq, SYSFS_PATH_MAX, "%lu", target_frequency);
return sysfs_cpufreq_write_one_value(cpu, WRITE_SCALING_SET_SPEED,
freq, strlen(freq));
}
struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu,
unsigned long long *total_time)
{
struct cpufreq_stats *first = NULL;
struct cpufreq_stats *current = NULL;
char one_value[SYSFS_PATH_MAX];
char linebuf[MAX_LINE_LEN];
unsigned int pos, i;
unsigned int len;
len = sysfs_cpufreq_read_file(cpu, "stats/time_in_state",
linebuf, sizeof(linebuf));
if (len == 0)
return NULL;
*total_time = 0;
pos = 0;
for (i = 0; i < len; i++) {
if (i == strlen(linebuf) || linebuf[i] == '\n') {
if (i - pos < 2)
continue;
if ((i - pos) >= SYSFS_PATH_MAX)
goto error_out;
if (current) {
current->next = malloc(sizeof(*current));
if (!current->next)
goto error_out;
current = current->next;
} else {
first = malloc(sizeof(*first));
if (!first)
return NULL;
current = first;
}
current->first = first;
current->next = NULL;
memcpy(one_value, linebuf + pos, i - pos);
one_value[i - pos] = '\0';
if (sscanf(one_value, "%lu %llu",
&current->frequency,
&current->time_in_state) != 2)
goto error_out;
*total_time = *total_time + current->time_in_state;
pos = i + 1;
}
}
return first;
error_out:
while (first) {
current = first->next;
free(first);
first = current;
}
return NULL;
}
void cpufreq_put_stats(struct cpufreq_stats *any)
{
struct cpufreq_stats *tmp, *next;
if (!any)
return;
tmp = any->first;
while (tmp) {
next = tmp->next;
free(tmp);
tmp = next;
}
}
unsigned long cpufreq_get_transitions(unsigned int cpu)
{
return sysfs_cpufreq_get_one_value(cpu, STATS_NUM_TRANSITIONS);
}
@@ -0,0 +1,230 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* cpufreq.h - definitions for libcpufreq
*
* Copyright (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
*/
#ifndef __CPUPOWER_CPUFREQ_H__
#define __CPUPOWER_CPUFREQ_H__
struct cpufreq_policy {
unsigned long min;
unsigned long max;
char *governor;
};
struct cpufreq_available_governors {
char *governor;
struct cpufreq_available_governors *next;
struct cpufreq_available_governors *first;
};
struct cpufreq_available_frequencies {
unsigned long frequency;
struct cpufreq_available_frequencies *next;
struct cpufreq_available_frequencies *first;
};
struct cpufreq_affected_cpus {
unsigned int cpu;
struct cpufreq_affected_cpus *next;
struct cpufreq_affected_cpus *first;
};
struct cpufreq_stats {
unsigned long frequency;
unsigned long long time_in_state;
struct cpufreq_stats *next;
struct cpufreq_stats *first;
};
#ifdef __cplusplus
extern "C" {
#endif
/* determine current CPU frequency
* - _kernel variant means kernel's opinion of CPU frequency
* - _hardware variant means actual hardware CPU frequency,
* which is only available to root.
*
* returns 0 on failure, else frequency in kHz.
*/
unsigned long cpufreq_get_freq_kernel(unsigned int cpu);
unsigned long cpufreq_get_freq_hardware(unsigned int cpu);
#define cpufreq_get(cpu) cpufreq_get_freq_kernel(cpu);
/* determine CPU transition latency
*
* returns 0 on failure, else transition latency in 10^(-9) s = nanoseconds
*/
unsigned long cpufreq_get_transition_latency(unsigned int cpu);
/* determine energy performance preference
*
* returns NULL on failure, else the string that represents the energy performance
* preference requested.
*/
char *cpufreq_get_energy_performance_preference(unsigned int cpu);
void cpufreq_put_energy_performance_preference(char *ptr);
/* determine hardware CPU frequency limits
*
* These may be limited further by thermal, energy or other
* considerations by cpufreq policy notifiers in the kernel.
*/
int cpufreq_get_hardware_limits(unsigned int cpu,
unsigned long *min,
unsigned long *max);
/* determine CPUfreq driver used
*
* Remember to call cpufreq_put_driver when no longer needed
* to avoid memory leakage, please.
*/
char *cpufreq_get_driver(unsigned int cpu);
void cpufreq_put_driver(char *ptr);
/* determine CPUfreq policy currently used
*
* Remember to call cpufreq_put_policy when no longer needed
* to avoid memory leakage, please.
*/
struct cpufreq_policy *cpufreq_get_policy(unsigned int cpu);
void cpufreq_put_policy(struct cpufreq_policy *policy);
/* determine CPUfreq governors currently available
*
* may be modified by modprobe'ing or rmmod'ing other governors. Please
* free allocated memory by calling cpufreq_put_available_governors
* after use.
*/
struct cpufreq_available_governors
*cpufreq_get_available_governors(unsigned int cpu);
void cpufreq_put_available_governors(
struct cpufreq_available_governors *first);
/* determine CPU frequency states available
*
* Only present on _some_ ->target() cpufreq drivers. For information purposes
* only. Please free allocated memory by calling
* cpufreq_put_frequencies after use.
*/
struct cpufreq_available_frequencies
*cpufreq_get_available_frequencies(unsigned int cpu);
void cpufreq_put_available_frequencies(
struct cpufreq_available_frequencies *first);
struct cpufreq_available_frequencies
*cpufreq_get_boost_frequencies(unsigned int cpu);
void cpufreq_put_boost_frequencies(
struct cpufreq_available_frequencies *first);
/* determine affected CPUs
*
* Remember to call cpufreq_put_affected_cpus when no longer needed
* to avoid memory leakage, please.
*/
struct cpufreq_affected_cpus *cpufreq_get_affected_cpus(unsigned
int cpu);
void cpufreq_put_affected_cpus(struct cpufreq_affected_cpus *first);
/* determine related CPUs
*
* Remember to call cpufreq_put_related_cpus when no longer needed
* to avoid memory leakage, please.
*/
struct cpufreq_affected_cpus *cpufreq_get_related_cpus(unsigned
int cpu);
void cpufreq_put_related_cpus(struct cpufreq_affected_cpus *first);
/* determine stats for cpufreq subsystem
*
* This is not available in all kernel versions or configurations.
*/
struct cpufreq_stats *cpufreq_get_stats(unsigned int cpu,
unsigned long long *total_time);
void cpufreq_put_stats(struct cpufreq_stats *stats);
unsigned long cpufreq_get_transitions(unsigned int cpu);
/* set new cpufreq policy
*
* Tries to set the passed policy as new policy as close as possible,
* but results may differ depending e.g. on governors being available.
*/
int cpufreq_set_policy(unsigned int cpu, struct cpufreq_policy *policy);
/* modify a policy by only changing min/max freq or governor
*
* Does not check whether result is what was intended.
*/
int cpufreq_modify_policy_min(unsigned int cpu, unsigned long min_freq);
int cpufreq_modify_policy_max(unsigned int cpu, unsigned long max_freq);
int cpufreq_modify_policy_governor(unsigned int cpu, char *governor);
/* set a specific frequency
*
* Does only work if userspace governor can be used and no external
* interference (other calls to this function or to set/modify_policy)
* occurs. Also does not work on ->range() cpufreq drivers.
*/
int cpufreq_set_frequency(unsigned int cpu,
unsigned long target_frequency);
/*
* get the sysfs value from specific table
*
* Read the value with the sysfs file name from specific table. Does
* only work if the cpufreq driver has the specific sysfs interfaces.
*/
unsigned long cpufreq_get_sysfs_value_from_table(unsigned int cpu,
const char **table,
unsigned int index,
unsigned int size);
#ifdef __cplusplus
}
#endif
#endif /* _CPUFREQ_H */
@@ -0,0 +1,387 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
* (C) 2011 Thomas Renninger <trenn@novell.com> Novell Inc.
*/
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "cpuidle.h"
#include "cpupower_intern.h"
/*
* helper function to check whether a file under "../cpuX/cpuidle/stateX/" dir
* exists.
* For example the functionality to disable c-states was introduced in later
* kernel versions, this function can be used to explicitly check for this
* feature.
*
* returns 1 if the file exists, 0 otherwise.
*/
static
unsigned int cpuidle_state_file_exists(unsigned int cpu,
unsigned int idlestate,
const char *fname)
{
char path[SYSFS_PATH_MAX];
struct stat statbuf;
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
cpu, idlestate, fname);
if (stat(path, &statbuf) != 0)
return 0;
return 1;
}
/*
* helper function to read file from /sys into given buffer
* fname is a relative path under "cpuX/cpuidle/stateX/" dir
* cstates starting with 0, C0 is not counted as cstate.
* This means if you want C1 info, pass 0 as idlestate param
*/
static
unsigned int cpuidle_state_read_file(unsigned int cpu,
unsigned int idlestate,
const char *fname, char *buf,
size_t buflen)
{
char path[SYSFS_PATH_MAX];
int fd;
ssize_t numread;
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
cpu, idlestate, fname);
fd = open(path, O_RDONLY);
if (fd == -1)
return 0;
numread = read(fd, buf, buflen - 1);
if (numread < 1) {
close(fd);
return 0;
}
buf[numread] = '\0';
close(fd);
return (unsigned int) numread;
}
/*
* helper function to write a new value to a /sys file
* fname is a relative path under "../cpuX/cpuidle/cstateY/" dir
*
* Returns the number of bytes written or 0 on error
*/
static
unsigned int cpuidle_state_write_file(unsigned int cpu,
unsigned int idlestate,
const char *fname,
const char *value, size_t len)
{
char path[SYSFS_PATH_MAX];
int fd;
ssize_t numwrite;
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
cpu, idlestate, fname);
fd = open(path, O_WRONLY);
if (fd == -1)
return 0;
numwrite = write(fd, value, len);
if (numwrite < 1) {
close(fd);
return 0;
}
close(fd);
return (unsigned int) numwrite;
}
/* read access to files which contain one numeric value */
enum idlestate_value {
IDLESTATE_USAGE,
IDLESTATE_POWER,
IDLESTATE_LATENCY,
IDLESTATE_RESIDENCY,
IDLESTATE_TIME,
IDLESTATE_DISABLE,
MAX_IDLESTATE_VALUE_FILES
};
static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = {
[IDLESTATE_USAGE] = "usage",
[IDLESTATE_POWER] = "power",
[IDLESTATE_LATENCY] = "latency",
[IDLESTATE_RESIDENCY] = "residency",
[IDLESTATE_TIME] = "time",
[IDLESTATE_DISABLE] = "disable",
};
static
unsigned long long cpuidle_state_get_one_value(unsigned int cpu,
unsigned int idlestate,
enum idlestate_value which)
{
unsigned long long value;
unsigned int len;
char linebuf[MAX_LINE_LEN];
char *endp;
if (which >= MAX_IDLESTATE_VALUE_FILES)
return 0;
len = cpuidle_state_read_file(cpu, idlestate,
idlestate_value_files[which],
linebuf, sizeof(linebuf));
if (len == 0)
return 0;
errno = 0;
value = strtoull(linebuf, &endp, 0);
if (endp == linebuf || errno == ERANGE)
return 0;
return value;
}
/* read access to files which contain one string */
enum idlestate_string {
IDLESTATE_DESC,
IDLESTATE_NAME,
MAX_IDLESTATE_STRING_FILES
};
static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {
[IDLESTATE_DESC] = "desc",
[IDLESTATE_NAME] = "name",
};
static char *cpuidle_state_get_one_string(unsigned int cpu,
unsigned int idlestate,
enum idlestate_string which)
{
char linebuf[MAX_LINE_LEN];
char *result;
unsigned int len;
if (which >= MAX_IDLESTATE_STRING_FILES)
return NULL;
len = cpuidle_state_read_file(cpu, idlestate,
idlestate_string_files[which],
linebuf, sizeof(linebuf));
if (len == 0)
return NULL;
result = strdup(linebuf);
if (result == NULL)
return NULL;
result[strcspn(result, "\n")] = '\0';
return result;
}
/*
* Returns:
* 1 if disabled
* 0 if enabled
* -1 if idlestate is not available
* -2 if disabling is not supported by the kernel
*/
int cpuidle_is_state_disabled(unsigned int cpu,
unsigned int idlestate)
{
if (cpuidle_state_count(cpu) <= idlestate)
return -1;
if (!cpuidle_state_file_exists(cpu, idlestate,
idlestate_value_files[IDLESTATE_DISABLE]))
return -2;
return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_DISABLE);
}
/*
* Pass 1 as last argument to disable or 0 to enable the state
* Returns:
* 0 on success
* negative values on error, for example:
* -1 if idlestate is not available
* -2 if disabling is not supported by the kernel
* -3 No write access to disable/enable C-states
*/
int cpuidle_state_disable(unsigned int cpu,
unsigned int idlestate,
unsigned int disable)
{
char value[SYSFS_PATH_MAX];
int bytes_written;
int len;
if (cpuidle_state_count(cpu) <= idlestate)
return -1;
if (!cpuidle_state_file_exists(cpu, idlestate,
idlestate_value_files[IDLESTATE_DISABLE]))
return -2;
len = snprintf(value, SYSFS_PATH_MAX, "%u", disable);
bytes_written = cpuidle_state_write_file(cpu, idlestate, "disable",
value, len);
if (bytes_written)
return 0;
return -3;
}
unsigned long cpuidle_state_latency(unsigned int cpu,
unsigned int idlestate)
{
return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);
}
unsigned long cpuidle_state_residency(unsigned int cpu,
unsigned int idlestate)
{
return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_RESIDENCY);
}
unsigned long cpuidle_state_usage(unsigned int cpu,
unsigned int idlestate)
{
return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_USAGE);
}
unsigned long long cpuidle_state_time(unsigned int cpu,
unsigned int idlestate)
{
return cpuidle_state_get_one_value(cpu, idlestate, IDLESTATE_TIME);
}
char *cpuidle_state_name(unsigned int cpu, unsigned int idlestate)
{
return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_NAME);
}
char *cpuidle_state_desc(unsigned int cpu, unsigned int idlestate)
{
return cpuidle_state_get_one_string(cpu, idlestate, IDLESTATE_DESC);
}
/*
* Returns number of supported C-states of CPU core cpu
* Negativ in error case
* Zero if cpuidle does not export any C-states
*/
unsigned int cpuidle_state_count(unsigned int cpu)
{
char file[SYSFS_PATH_MAX];
struct stat statbuf;
int idlestates = 1;
snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");
if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
return 0;
snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);
if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
return 0;
while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU
"cpu%u/cpuidle/state%d", cpu, idlestates);
idlestates++;
}
idlestates--;
return idlestates;
}
/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/
/*
* helper function to read file from /sys into given buffer
* fname is a relative path under "cpu/cpuidle/" dir
*/
static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,
size_t buflen)
{
char path[SYSFS_PATH_MAX];
snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);
return cpupower_read_sysfs(path, buf, buflen);
}
/* read access to files which contain one string */
enum cpuidle_string {
CPUIDLE_GOVERNOR,
CPUIDLE_GOVERNOR_RO,
CPUIDLE_DRIVER,
MAX_CPUIDLE_STRING_FILES
};
static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {
[CPUIDLE_GOVERNOR] = "current_governor",
[CPUIDLE_GOVERNOR_RO] = "current_governor_ro",
[CPUIDLE_DRIVER] = "current_driver",
};
static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)
{
char linebuf[MAX_LINE_LEN];
char *result;
unsigned int len;
if (which >= MAX_CPUIDLE_STRING_FILES)
return NULL;
len = sysfs_cpuidle_read_file(cpuidle_string_files[which],
linebuf, sizeof(linebuf));
if (len == 0)
return NULL;
result = strdup(linebuf);
if (result == NULL)
return NULL;
result[strcspn(result, "\n")] = '\0';
return result;
}
char *cpuidle_get_governor(void)
{
char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);
if (!tmp)
return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);
else
return tmp;
}
char *cpuidle_get_driver(void)
{
return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);
}
/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */
@@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __CPUPOWER_CPUIDLE_H__
#define __CPUPOWER_CPUIDLE_H__
int cpuidle_is_state_disabled(unsigned int cpu,
unsigned int idlestate);
int cpuidle_state_disable(unsigned int cpu, unsigned int idlestate,
unsigned int disable);
unsigned long cpuidle_state_latency(unsigned int cpu,
unsigned int idlestate);
unsigned long cpuidle_state_residency(unsigned int cpu,
unsigned int idlestate);
unsigned long cpuidle_state_usage(unsigned int cpu,
unsigned int idlestate);
unsigned long long cpuidle_state_time(unsigned int cpu,
unsigned int idlestate);
char *cpuidle_state_name(unsigned int cpu,
unsigned int idlestate);
char *cpuidle_state_desc(unsigned int cpu,
unsigned int idlestate);
unsigned int cpuidle_state_count(unsigned int cpu);
char *cpuidle_get_governor(void);
char *cpuidle_get_driver(void);
#endif /* __CPUPOWER_HELPERS_SYSFS_H__ */
@@ -0,0 +1,251 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2004-2009 Dominik Brodowski <linux@dominikbrodowski.de>
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "cpupower.h"
#include "cpupower_intern.h"
int is_valid_path(const char *path)
{
if (access(path, F_OK) == -1)
return 0;
return 1;
}
unsigned int cpupower_read_sysfs(const char *path, char *buf, size_t buflen)
{
ssize_t numread;
int fd;
fd = open(path, O_RDONLY);
if (fd == -1)
return 0;
numread = read(fd, buf, buflen - 1);
if (numread < 1) {
close(fd);
return 0;
}
buf[numread] = '\0';
close(fd);
return (unsigned int) numread;
}
unsigned int cpupower_write_sysfs(const char *path, char *buf, size_t buflen)
{
ssize_t numwritten;
int fd;
fd = open(path, O_WRONLY);
if (fd == -1)
return 0;
numwritten = write(fd, buf, buflen - 1);
if (numwritten < 1) {
perror(path);
close(fd);
return 0;
}
close(fd);
return (unsigned int) numwritten;
}
/*
* Detect whether a CPU is online
*
* Returns:
* 1 -> if CPU is online
* 0 -> if CPU is offline
* negative errno values in error case
*/
int cpupower_is_cpu_online(unsigned int cpu)
{
char path[SYSFS_PATH_MAX];
int fd;
ssize_t numread;
unsigned long long value;
char linebuf[MAX_LINE_LEN];
char *endp;
struct stat statbuf;
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu);
if (stat(path, &statbuf) != 0)
return 0;
/*
* kernel without CONFIG_HOTPLUG_CPU
* -> cpuX directory exists, but not cpuX/online file
*/
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu);
if (stat(path, &statbuf) != 0)
return 1;
fd = open(path, O_RDONLY);
if (fd == -1)
return -errno;
numread = read(fd, linebuf, MAX_LINE_LEN - 1);
if (numread < 1) {
close(fd);
return -EIO;
}
linebuf[numread] = '\0';
close(fd);
value = strtoull(linebuf, &endp, 0);
if (value > 1)
return -EINVAL;
return value;
}
/* returns -1 on failure, 0 on success */
static int sysfs_topology_read_file(unsigned int cpu, const char *fname, int *result)
{
char linebuf[MAX_LINE_LEN];
char *endp;
char path[SYSFS_PATH_MAX];
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/topology/%s",
cpu, fname);
if (cpupower_read_sysfs(path, linebuf, MAX_LINE_LEN) == 0)
return -1;
*result = strtol(linebuf, &endp, 0);
if (endp == linebuf || errno == ERANGE)
return -1;
return 0;
}
static int __compare(const void *t1, const void *t2)
{
struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1;
struct cpuid_core_info *top2 = (struct cpuid_core_info *)t2;
if (top1->pkg < top2->pkg)
return -1;
else if (top1->pkg > top2->pkg)
return 1;
else if (top1->core < top2->core)
return -1;
else if (top1->core > top2->core)
return 1;
else if (top1->cpu < top2->cpu)
return -1;
else if (top1->cpu > top2->cpu)
return 1;
else
return 0;
}
static int __compare_core_cpu_list(const void *t1, const void *t2)
{
struct cpuid_core_info *top1 = (struct cpuid_core_info *)t1;
struct cpuid_core_info *top2 = (struct cpuid_core_info *)t2;
return strcmp(top1->core_cpu_list, top2->core_cpu_list);
}
/*
* Returns amount of cpus, negative on error, cpu_top must be
* passed to cpu_topology_release to free resources
*
* Array is sorted after ->cpu_smt_list ->pkg, ->core
*/
int get_cpu_topology(struct cpupower_topology *cpu_top)
{
int cpu, last_pkg, cpus = sysconf(_SC_NPROCESSORS_CONF);
char path[SYSFS_PATH_MAX];
char *last_cpu_list;
cpu_top->core_info = malloc(sizeof(struct cpuid_core_info) * cpus);
if (cpu_top->core_info == NULL)
return -ENOMEM;
cpu_top->pkgs = cpu_top->cores = 0;
for (cpu = 0; cpu < cpus; cpu++) {
cpu_top->core_info[cpu].cpu = cpu;
cpu_top->core_info[cpu].is_online = cpupower_is_cpu_online(cpu);
if(sysfs_topology_read_file(
cpu,
"physical_package_id",
&(cpu_top->core_info[cpu].pkg)) < 0) {
cpu_top->core_info[cpu].pkg = -1;
cpu_top->core_info[cpu].core = -1;
continue;
}
if(sysfs_topology_read_file(
cpu,
"core_id",
&(cpu_top->core_info[cpu].core)) < 0) {
cpu_top->core_info[cpu].pkg = -1;
cpu_top->core_info[cpu].core = -1;
continue;
}
if (cpu_top->core_info[cpu].core == -1) {
strncpy(cpu_top->core_info[cpu].core_cpu_list, "-1", CPULIST_BUFFER);
continue;
}
snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/topology/%s",
cpu, "core_cpus_list");
if (cpupower_read_sysfs(
path,
cpu_top->core_info[cpu].core_cpu_list,
CPULIST_BUFFER) < 1) {
printf("Warning CPU%u has a 0 size core_cpus_list string", cpu);
}
}
/* Count the number of distinct cpu lists to get the physical core
* count.
*/
qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info),
__compare_core_cpu_list);
last_cpu_list = cpu_top->core_info[0].core_cpu_list;
cpu_top->cores = 1;
for (cpu = 1; cpu < cpus; cpu++) {
if (strcmp(cpu_top->core_info[cpu].core_cpu_list, last_cpu_list) != 0 &&
cpu_top->core_info[cpu].pkg != -1) {
last_cpu_list = cpu_top->core_info[cpu].core_cpu_list;
cpu_top->cores++;
}
}
qsort(cpu_top->core_info, cpus, sizeof(struct cpuid_core_info),
__compare);
/* Count the number of distinct pkgs values. This works
because the primary sort of the core_info struct was just
done by pkg value. */
last_pkg = cpu_top->core_info[0].pkg;
for(cpu = 1; cpu < cpus; cpu++) {
if (cpu_top->core_info[cpu].pkg != last_pkg &&
cpu_top->core_info[cpu].pkg != -1) {
last_pkg = cpu_top->core_info[cpu].pkg;
cpu_top->pkgs++;
}
}
if (!(cpu_top->core_info[0].pkg == -1))
cpu_top->pkgs++;
return cpus;
}
void cpu_topology_release(struct cpupower_topology cpu_top)
{
free(cpu_top.core_info);
}
@@ -0,0 +1,39 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __CPUPOWER_CPUPOWER_H__
#define __CPUPOWER_CPUPOWER_H__
#define CPULIST_BUFFER 5
struct cpupower_topology {
/* Amount of CPU cores, packages and threads per core in the system */
unsigned int cores;
unsigned int pkgs;
unsigned int threads; /* per core */
/* Array gets mallocated with cores entries, holding per core info */
struct cpuid_core_info *core_info;
};
struct cpuid_core_info {
int pkg;
int core;
int cpu;
char core_cpu_list[CPULIST_BUFFER];
/* flags */
unsigned int is_online:1;
};
#ifdef __cplusplus
extern "C" {
#endif
int get_cpu_topology(struct cpupower_topology *cpu_top);
void cpu_topology_release(struct cpupower_topology cpu_top);
int cpupower_is_cpu_online(unsigned int cpu);
#ifdef __cplusplus
}
#endif
#endif
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0 */
#define PATH_TO_CPU "/sys/devices/system/cpu/"
#ifndef MAX_LINE_LEN
#define MAX_LINE_LEN 4096
#endif
#define SYSFS_PATH_MAX 255
int is_valid_path(const char *path);
unsigned int cpupower_read_sysfs(const char *path, char *buf, size_t buflen);
unsigned int cpupower_write_sysfs(const char *path, char *buf, size_t buflen);
@@ -0,0 +1,307 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* (C) 2016 SUSE Software Solutions GmbH
* Thomas Renninger <trenn@suse.de>
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>
#include <dirent.h>
#include "powercap.h"
static unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen)
{
int fd;
ssize_t numread;
fd = open(path, O_RDONLY);
if (fd == -1)
return 0;
numread = read(fd, buf, buflen - 1);
if (numread < 1) {
close(fd);
return 0;
}
buf[numread] = '\0';
close(fd);
return (unsigned int) numread;
}
static int sysfs_get_enabled(char *path, int *mode)
{
int fd;
char yes_no;
int ret = 0;
*mode = 0;
fd = open(path, O_RDONLY);
if (fd == -1) {
ret = -1;
goto out;
}
if (read(fd, &yes_no, 1) != 1) {
ret = -1;
goto out_close;
}
if (yes_no == '1') {
*mode = 1;
goto out_close;
} else if (yes_no == '0') {
goto out_close;
} else {
ret = -1;
goto out_close;
}
out_close:
close(fd);
out:
return ret;
}
int powercap_get_enabled(int *mode)
{
char path[SYSFS_PATH_MAX] = PATH_TO_POWERCAP "/intel-rapl/enabled";
return sysfs_get_enabled(path, mode);
}
/*
* TODO: implement function. Returns dummy 0 for now.
*/
int powercap_set_enabled(int mode)
{
return 0;
}
/*
* Hardcoded, because rapl is the only powercap implementation
- * this needs to get more generic if more powercap implementations
* should show up
*/
int powercap_get_driver(char *driver, int buflen)
{
char file[SYSFS_PATH_MAX] = PATH_TO_RAPL;
struct stat statbuf;
if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode)) {
driver = "";
return -1;
} else if (buflen > 10) {
strcpy(driver, "intel-rapl");
return 0;
} else
return -1;
}
enum powercap_get64 {
GET_ENERGY_UJ,
GET_MAX_ENERGY_RANGE_UJ,
GET_POWER_UW,
GET_MAX_POWER_RANGE_UW,
MAX_GET_64_FILES
};
static const char *powercap_get64_files[MAX_GET_64_FILES] = {
[GET_POWER_UW] = "power_uw",
[GET_MAX_POWER_RANGE_UW] = "max_power_range_uw",
[GET_ENERGY_UJ] = "energy_uj",
[GET_MAX_ENERGY_RANGE_UJ] = "max_energy_range_uj",
};
static int sysfs_powercap_get64_val(struct powercap_zone *zone,
enum powercap_get64 which,
uint64_t *val)
{
char file[SYSFS_PATH_MAX] = PATH_TO_POWERCAP "/";
int ret;
char buf[MAX_LINE_LEN];
strcat(file, zone->sys_name);
strcat(file, "/");
strcat(file, powercap_get64_files[which]);
ret = sysfs_read_file(file, buf, MAX_LINE_LEN);
if (ret < 0)
return ret;
if (ret == 0)
return -1;
*val = strtoll(buf, NULL, 10);
return 0;
}
int powercap_get_max_energy_range_uj(struct powercap_zone *zone, uint64_t *val)
{
return sysfs_powercap_get64_val(zone, GET_MAX_ENERGY_RANGE_UJ, val);
}
int powercap_get_energy_uj(struct powercap_zone *zone, uint64_t *val)
{
return sysfs_powercap_get64_val(zone, GET_ENERGY_UJ, val);
}
int powercap_get_max_power_range_uw(struct powercap_zone *zone, uint64_t *val)
{
return sysfs_powercap_get64_val(zone, GET_MAX_POWER_RANGE_UW, val);
}
int powercap_get_power_uw(struct powercap_zone *zone, uint64_t *val)
{
return sysfs_powercap_get64_val(zone, GET_POWER_UW, val);
}
int powercap_zone_get_enabled(struct powercap_zone *zone, int *mode)
{
char path[SYSFS_PATH_MAX] = PATH_TO_POWERCAP;
if ((strlen(PATH_TO_POWERCAP) + strlen(zone->sys_name)) +
strlen("/enabled") + 1 >= SYSFS_PATH_MAX)
return -1;
strcat(path, "/");
strcat(path, zone->sys_name);
strcat(path, "/enabled");
return sysfs_get_enabled(path, mode);
}
int powercap_zone_set_enabled(struct powercap_zone *zone, int mode)
{
/* To be done if needed */
return 0;
}
int powercap_read_zone(struct powercap_zone *zone)
{
struct dirent *dent;
DIR *zone_dir;
char sysfs_dir[SYSFS_PATH_MAX] = PATH_TO_POWERCAP;
struct powercap_zone *child_zone;
char file[SYSFS_PATH_MAX] = PATH_TO_POWERCAP;
int i, ret = 0;
uint64_t val = 0;
strcat(sysfs_dir, "/");
strcat(sysfs_dir, zone->sys_name);
zone_dir = opendir(sysfs_dir);
if (zone_dir == NULL)
return -1;
strcat(file, "/");
strcat(file, zone->sys_name);
strcat(file, "/name");
sysfs_read_file(file, zone->name, MAX_LINE_LEN);
if (zone->parent)
zone->tree_depth = zone->parent->tree_depth + 1;
ret = powercap_get_energy_uj(zone, &val);
if (ret == 0)
zone->has_energy_uj = 1;
ret = powercap_get_power_uw(zone, &val);
if (ret == 0)
zone->has_power_uw = 1;
while ((dent = readdir(zone_dir)) != NULL) {
struct stat st;
if (strcmp(dent->d_name, ".") == 0 || strcmp(dent->d_name, "..") == 0)
continue;
if (stat(dent->d_name, &st) != 0 || !S_ISDIR(st.st_mode))
if (fstatat(dirfd(zone_dir), dent->d_name, &st, 0) < 0)
continue;
if (strncmp(dent->d_name, "intel-rapl:", 11) != 0)
continue;
child_zone = calloc(1, sizeof(struct powercap_zone));
if (child_zone == NULL)
return -1;
for (i = 0; i < POWERCAP_MAX_CHILD_ZONES; i++) {
if (zone->children[i] == NULL) {
zone->children[i] = child_zone;
break;
}
if (i == POWERCAP_MAX_CHILD_ZONES - 1) {
free(child_zone);
fprintf(stderr, "Reached POWERCAP_MAX_CHILD_ZONES %d\n",
POWERCAP_MAX_CHILD_ZONES);
return -1;
}
}
strcpy(child_zone->sys_name, zone->sys_name);
strcat(child_zone->sys_name, "/");
strcat(child_zone->sys_name, dent->d_name);
child_zone->parent = zone;
if (zone->tree_depth >= POWERCAP_MAX_TREE_DEPTH) {
fprintf(stderr, "Maximum zone hierarchy depth[%d] reached\n",
POWERCAP_MAX_TREE_DEPTH);
ret = -1;
break;
}
powercap_read_zone(child_zone);
}
closedir(zone_dir);
return ret;
}
struct powercap_zone *powercap_init_zones(void)
{
int enabled;
struct powercap_zone *root_zone;
int ret;
char file[SYSFS_PATH_MAX] = PATH_TO_RAPL "/enabled";
ret = sysfs_get_enabled(file, &enabled);
if (ret)
return NULL;
if (!enabled)
return NULL;
root_zone = calloc(1, sizeof(struct powercap_zone));
if (!root_zone)
return NULL;
strcpy(root_zone->sys_name, "intel-rapl/intel-rapl:0");
powercap_read_zone(root_zone);
return root_zone;
}
/* Call function *f on the passed zone and all its children */
int powercap_walk_zones(struct powercap_zone *zone,
int (*f)(struct powercap_zone *zone))
{
int i, ret;
if (!zone)
return -1;
ret = f(zone);
if (ret)
return ret;
for (i = 0; i < POWERCAP_MAX_CHILD_ZONES; i++) {
if (zone->children[i] != NULL)
powercap_walk_zones(zone->children[i], f);
}
return 0;
}
@@ -0,0 +1,54 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* (C) 2016 SUSE Software Solutions GmbH
* Thomas Renninger <trenn@suse.de>
*/
#ifndef __CPUPOWER_RAPL_H__
#define __CPUPOWER_RAPL_H__
#define PATH_TO_POWERCAP "/sys/devices/virtual/powercap"
#define PATH_TO_RAPL "/sys/devices/virtual/powercap/intel-rapl"
#define PATH_TO_RAPL_CLASS "/sys/devices/virtual/powercap/intel-rapl"
#define POWERCAP_MAX_CHILD_ZONES 10
#define POWERCAP_MAX_TREE_DEPTH 10
#define MAX_LINE_LEN 4096
#define SYSFS_PATH_MAX 255
#include <stdint.h>
struct powercap_zone {
char name[MAX_LINE_LEN];
/*
* sys_name relative to PATH_TO_POWERCAP,
* do not forget the / in between
*/
char sys_name[SYSFS_PATH_MAX];
int tree_depth;
struct powercap_zone *parent;
struct powercap_zone *children[POWERCAP_MAX_CHILD_ZONES];
/* More possible caps or attributes to be added? */
uint32_t has_power_uw:1,
has_energy_uj:1;
};
int powercap_walk_zones(struct powercap_zone *zone,
int (*f)(struct powercap_zone *zone));
struct powercap_zone *powercap_init_zones(void);
int powercap_get_enabled(int *mode);
int powercap_set_enabled(int mode);
int powercap_get_driver(char *driver, int buflen);
int powercap_get_max_energy_range_uj(struct powercap_zone *zone, uint64_t *val);
int powercap_get_energy_uj(struct powercap_zone *zone, uint64_t *val);
int powercap_get_max_power_range_uw(struct powercap_zone *zone, uint64_t *val);
int powercap_get_power_uw(struct powercap_zone *zone, uint64_t *val);
int powercap_zone_get_enabled(struct powercap_zone *zone, int *mode);
int powercap_zone_set_enabled(struct powercap_zone *zone, int mode);
#endif /* __CPUPOWER_RAPL_H__ */