mirror of
https://github.com/RGBCube/superfreq
synced 2025-07-27 17:07:44 +00:00
Merge branch 'main' into better-battery-mgnmnt
This commit is contained in:
commit
f4fceed893
7 changed files with 79 additions and 44 deletions
|
@ -135,7 +135,7 @@ more than issue reports, as supporting hardware _needs_ hardware.
|
||||||
|
|
||||||
Superfreq uses TOML configuration files. Default locations:
|
Superfreq uses TOML configuration files. Default locations:
|
||||||
|
|
||||||
- `/etc/superfreq/config.toml`
|
- `/etc/xdg/superfreq/config.toml`
|
||||||
- `/etc/superfreq.toml`
|
- `/etc/superfreq.toml`
|
||||||
|
|
||||||
You can also specify a custom path by setting the `SUPERFREQ_CONFIG` environment
|
You can also specify a custom path by setting the `SUPERFREQ_CONFIG` environment
|
||||||
|
|
|
@ -5,18 +5,21 @@ inputs: {
|
||||||
...
|
...
|
||||||
}: let
|
}: let
|
||||||
inherit (lib.modules) mkIf;
|
inherit (lib.modules) mkIf;
|
||||||
inherit (lib.options) mkOption mkEnableOption;
|
inherit (lib.options) mkOption mkEnableOption mkPackageOption;
|
||||||
inherit (lib.types) submodule;
|
inherit (lib.types) submodule;
|
||||||
|
inherit (lib.lists) optional;
|
||||||
inherit (lib.meta) getExe;
|
inherit (lib.meta) getExe;
|
||||||
|
|
||||||
cfg = config.programs.superfreq;
|
cfg = config.services.superfreq;
|
||||||
|
|
||||||
defaultPackage = inputs.self.packages.${pkgs.stdenv.system}.default;
|
|
||||||
|
|
||||||
format = pkgs.formats.toml {};
|
format = pkgs.formats.toml {};
|
||||||
|
cfgFile = format.generate "superfreq-config.toml" cfg.settings;
|
||||||
in {
|
in {
|
||||||
options.programs.superfreq = {
|
options.services.superfreq = {
|
||||||
enable = mkEnableOption "Automatic CPU speed & power optimizer for Linux";
|
enable = mkEnableOption "Automatic CPU speed & power optimizer for Linux";
|
||||||
|
package = mkPackageOption inputs.self.packages.${pkgs.stdenv.system} "superfreq" {
|
||||||
|
pkgsText = "self.packages.\${pkgs.stdenv.system}";
|
||||||
|
};
|
||||||
|
|
||||||
settings = mkOption {
|
settings = mkOption {
|
||||||
default = {};
|
default = {};
|
||||||
|
@ -26,23 +29,18 @@ in {
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
environment.systemPackages = [defaultPackage];
|
environment.systemPackages = [cfg.package];
|
||||||
|
|
||||||
systemd = {
|
systemd.services.superfreq = {
|
||||||
packages = [defaultPackage];
|
wantedBy = ["multi-user.target"];
|
||||||
services.superfreq = {
|
serviceConfig = {
|
||||||
wantedBy = ["multi-user.target"];
|
Environment = optional (cfg.settings != {}) ["SUPERFREQ_CONFIG=${cfgFile}"];
|
||||||
serviceConfig = let
|
WorkingDirectory = "";
|
||||||
cfgFile = format.generate "superfreq-config.toml" cfg.settings;
|
ExecStart = "${getExe cfg.package} daemon --verbose";
|
||||||
in {
|
Restart = "on-failure";
|
||||||
Environment = ["SUPERFREQ_CONFIG=${cfgFile}"];
|
|
||||||
WorkingDirectory = "";
|
|
||||||
ExecStart = "${getExe defaultPackage} daemon --verbose";
|
|
||||||
Restart = "on-failure";
|
|
||||||
|
|
||||||
RuntimeDirectory = "superfreq";
|
RuntimeDirectory = "superfreq";
|
||||||
RuntimeDirectoryMode = "0755";
|
RuntimeDirectoryMode = "0755";
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -55,9 +53,9 @@ in {
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
assertion = !config.programs.auto-cpufreq.enable;
|
assertion = !config.services.auto-cpufreq.enable;
|
||||||
message = ''
|
message = ''
|
||||||
You have set programs.auto-cpufreq.enable = true;
|
You have set services.auto-cpufreq.enable = true;
|
||||||
which conflicts with Superfreq.
|
which conflicts with Superfreq.
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ pub fn load_config_from_path(specific_path: Option<&str>) -> Result<AppConfig, C
|
||||||
}
|
}
|
||||||
|
|
||||||
// System-wide paths
|
// System-wide paths
|
||||||
config_paths.push(PathBuf::from("/etc/superfreq/config.toml"));
|
config_paths.push(PathBuf::from("/etc/xdg/superfreq/config.toml"));
|
||||||
config_paths.push(PathBuf::from("/etc/superfreq.toml"));
|
config_paths.push(PathBuf::from("/etc/superfreq.toml"));
|
||||||
|
|
||||||
for path in config_paths {
|
for path in config_paths {
|
||||||
|
|
15
src/cpu.rs
15
src/cpu.rs
|
@ -1,6 +1,7 @@
|
||||||
use crate::core::{GovernorOverrideMode, TurboSetting};
|
use crate::core::{GovernorOverrideMode, TurboSetting};
|
||||||
use crate::util::error::ControlError;
|
use crate::util::error::ControlError;
|
||||||
use core::str;
|
use core::str;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::{fs, io, path::Path, string::ToString};
|
use std::{fs, io, path::Path, string::ToString};
|
||||||
|
|
||||||
pub type Result<T, E = ControlError> = std::result::Result<T, E>;
|
pub type Result<T, E = ControlError> = std::result::Result<T, E>;
|
||||||
|
@ -18,12 +19,15 @@ const VALID_EPB_STRINGS: &[&str] = &[
|
||||||
// Write a value to a sysfs file
|
// Write a value to a sysfs file
|
||||||
fn write_sysfs_value(path: impl AsRef<Path>, value: &str) -> Result<()> {
|
fn write_sysfs_value(path: impl AsRef<Path>, value: &str) -> Result<()> {
|
||||||
let p = path.as_ref();
|
let p = path.as_ref();
|
||||||
|
|
||||||
fs::write(p, value).map_err(|e| {
|
fs::write(p, value).map_err(|e| {
|
||||||
let error_msg = format!("Path: {:?}, Value: '{}', Error: {}", p.display(), value, e);
|
let error_msg = format!("Path: {:?}, Value: '{}', Error: {}", p.display(), value, e);
|
||||||
if e.kind() == io::ErrorKind::PermissionDenied {
|
match e.kind() {
|
||||||
ControlError::PermissionDenied(error_msg)
|
io::ErrorKind::PermissionDenied => ControlError::PermissionDenied(error_msg),
|
||||||
} else {
|
io::ErrorKind::NotFound => {
|
||||||
ControlError::WriteError(error_msg)
|
ControlError::PathMissing(format!("Path '{}' does not exist", p.display()))
|
||||||
|
}
|
||||||
|
_ => ControlError::WriteError(error_msg),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -136,6 +140,7 @@ fn get_available_governors() -> Result<Vec<String>> {
|
||||||
let path = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors";
|
let path = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors";
|
||||||
|
|
||||||
if !Path::new(path).exists() {
|
if !Path::new(path).exists() {
|
||||||
|
|
||||||
return Err(ControlError::NotSupported(
|
return Err(ControlError::NotSupported(
|
||||||
"Could not determine available governors".to_string(),
|
"Could not determine available governors".to_string(),
|
||||||
));
|
));
|
||||||
|
@ -487,7 +492,7 @@ const GOVERNOR_OVERRIDE_PATH: &str = "/etc/superfreq/governor_override";
|
||||||
/// Force a specific CPU governor or reset to automatic mode
|
/// Force a specific CPU governor or reset to automatic mode
|
||||||
pub fn force_governor(mode: GovernorOverrideMode) -> Result<()> {
|
pub fn force_governor(mode: GovernorOverrideMode) -> Result<()> {
|
||||||
// Create directory if it doesn't exist
|
// Create directory if it doesn't exist
|
||||||
let dir_path = Path::new("/etc/superfreq");
|
let dir_path = Path::new("/etc/xdg/superfreq");
|
||||||
if !dir_path.exists() {
|
if !dir_path.exists() {
|
||||||
fs::create_dir_all(dir_path).map_err(|e| {
|
fs::create_dir_all(dir_path).map_err(|e| {
|
||||||
if e.kind() == io::ErrorKind::PermissionDenied {
|
if e.kind() == io::ErrorKind::PermissionDenied {
|
||||||
|
|
|
@ -65,7 +65,7 @@ pub fn run_daemon(mut config: AppConfig, verbose: bool) -> Result<(), Box<dyn st
|
||||||
Some(path)
|
Some(path)
|
||||||
} else {
|
} else {
|
||||||
// Check standard config paths
|
// Check standard config paths
|
||||||
let default_paths = ["/etc/superfreq/config.toml", "/etc/superfreq.toml"];
|
let default_paths = ["/etc/xdg/superfreq/config.toml", "/etc/superfreq.toml"];
|
||||||
|
|
||||||
default_paths
|
default_paths
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -390,25 +390,41 @@ pub fn get_all_cpu_core_info() -> Result<Vec<CpuCoreInfo>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cpu_global_info(cpu_cores: &[CpuCoreInfo]) -> CpuGlobalInfo {
|
pub fn get_cpu_global_info(cpu_cores: &[CpuCoreInfo]) -> CpuGlobalInfo {
|
||||||
// FIXME: Assume global settings can be read from cpu0 or are consistent.
|
// Find a valid CPU to read global settings from
|
||||||
// This might not work properly for heterogeneous systems (e.g. big.LITTLE)
|
// Try cpu0 first, then fall back to any available CPU with cpufreq
|
||||||
let cpufreq_base_path = Path::new("/sys/devices/system/cpu/cpu0/cpufreq/");
|
let mut cpufreq_base_path_buf = PathBuf::from("/sys/devices/system/cpu/cpu0/cpufreq/");
|
||||||
|
|
||||||
|
if !cpufreq_base_path_buf.exists() {
|
||||||
|
let core_count = get_logical_core_count().unwrap_or_else(|e| {
|
||||||
|
eprintln!("Warning: {e}");
|
||||||
|
0
|
||||||
|
});
|
||||||
|
let path = (0..core_count)
|
||||||
|
.map(|i| PathBuf::from(format!("/sys/devices/system/cpu/cpu{i}/cpufreq/")))
|
||||||
|
.find(|path| path.exists());
|
||||||
|
if let Some(test_path_buf) = path {
|
||||||
|
cpufreq_base_path_buf = test_path_buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let turbo_status_path = Path::new("/sys/devices/system/cpu/intel_pstate/no_turbo");
|
let turbo_status_path = Path::new("/sys/devices/system/cpu/intel_pstate/no_turbo");
|
||||||
let boost_path = Path::new("/sys/devices/system/cpu/cpufreq/boost");
|
let boost_path = Path::new("/sys/devices/system/cpu/cpufreq/boost");
|
||||||
let current_governor = if cpufreq_base_path.join("scaling_governor").exists() {
|
|
||||||
read_sysfs_file_trimmed(cpufreq_base_path.join("scaling_governor")).ok()
|
let current_governor = if cpufreq_base_path_buf.join("scaling_governor").exists() {
|
||||||
|
read_sysfs_file_trimmed(cpufreq_base_path_buf.join("scaling_governor")).ok()
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let available_governors = if cpufreq_base_path
|
let available_governors = if cpufreq_base_path_buf
|
||||||
.join("scaling_available_governors")
|
.join("scaling_available_governors")
|
||||||
.exists()
|
.exists()
|
||||||
{
|
{
|
||||||
read_sysfs_file_trimmed(cpufreq_base_path.join("scaling_available_governors")).map_or_else(
|
read_sysfs_file_trimmed(cpufreq_base_path_buf.join("scaling_available_governors"))
|
||||||
|_| vec![],
|
.map_or_else(
|
||||||
|s| s.split_whitespace().map(String::from).collect(),
|
|_| vec![],
|
||||||
)
|
|s| s.split_whitespace().map(String::from).collect(),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
|
@ -424,17 +440,16 @@ pub fn get_cpu_global_info(cpu_cores: &[CpuCoreInfo]) -> CpuGlobalInfo {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// EPP (Energy Performance Preference)
|
// EPP (Energy Performance Preference)
|
||||||
let energy_perf_pref =
|
let energy_perf_pref =
|
||||||
read_sysfs_file_trimmed(cpufreq_base_path.join("energy_performance_preference")).ok();
|
read_sysfs_file_trimmed(cpufreq_base_path_buf.join("energy_performance_preference")).ok();
|
||||||
|
|
||||||
// EPB (Energy Performance Bias)
|
// EPB (Energy Performance Bias)
|
||||||
let energy_perf_bias =
|
let energy_perf_bias =
|
||||||
read_sysfs_file_trimmed(cpufreq_base_path.join("energy_performance_bias")).ok();
|
read_sysfs_file_trimmed(cpufreq_base_path_buf.join("energy_performance_bias")).ok();
|
||||||
|
|
||||||
let platform_profile = read_sysfs_file_trimmed("/sys/firmware/acpi/platform_profile").ok();
|
let platform_profile = read_sysfs_file_trimmed("/sys/firmware/acpi/platform_profile").ok();
|
||||||
let _platform_profile_choices =
|
|
||||||
read_sysfs_file_trimmed("/sys/firmware/acpi/platform_profile_choices").ok();
|
|
||||||
|
|
||||||
// Calculate average CPU temperature from the core temperatures
|
// Calculate average CPU temperature from the core temperatures
|
||||||
let average_temperature_celsius = if cpu_cores.is_empty() {
|
let average_temperature_celsius = if cpu_cores.is_empty() {
|
||||||
|
@ -458,6 +473,7 @@ pub fn get_cpu_global_info(cpu_cores: &[CpuCoreInfo]) -> CpuGlobalInfo {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Return the constructed CpuGlobalInfo
|
||||||
CpuGlobalInfo {
|
CpuGlobalInfo {
|
||||||
current_governor,
|
current_governor,
|
||||||
available_governors,
|
available_governors,
|
||||||
|
|
|
@ -9,6 +9,10 @@ pub enum ControlError {
|
||||||
NotSupported(String),
|
NotSupported(String),
|
||||||
PermissionDenied(String),
|
PermissionDenied(String),
|
||||||
InvalidProfile(String),
|
InvalidProfile(String),
|
||||||
|
InvalidGovernor(String),
|
||||||
|
ParseError(String),
|
||||||
|
ReadError(String),
|
||||||
|
PathMissing(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for ControlError {
|
impl From<io::Error> for ControlError {
|
||||||
|
@ -37,6 +41,18 @@ impl std::fmt::Display for ControlError {
|
||||||
"Invalid platform control profile {s} supplied, please provide a valid one."
|
"Invalid platform control profile {s} supplied, please provide a valid one."
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Self::InvalidGovernor(s) => {
|
||||||
|
write!(f, "Invalid governor: {s}")
|
||||||
|
}
|
||||||
|
Self::ParseError(s) => {
|
||||||
|
write!(f, "Failed to parse value: {s}")
|
||||||
|
}
|
||||||
|
Self::ReadError(s) => {
|
||||||
|
write!(f, "Failed to read sysfs path: {s}")
|
||||||
|
}
|
||||||
|
Self::PathMissing(s) => {
|
||||||
|
write!(f, "Path missing: {s}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue