set governor to "performance" if available (#2438)

* set governor to "performance" if available

* add linux-cpupower

* fix: Boolean blindness, thanks @dr-bones

---------

Co-authored-by: J H <2364004+Blu-J@users.noreply.github.com>
This commit is contained in:
Aiden McClelland
2023-10-04 14:52:56 -06:00
committed by GitHub
parent 20241c27ee
commit 6f588196cb
7 changed files with 208 additions and 1 deletions

59
backend/Cargo.lock generated
View File

@@ -661,6 +661,26 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "795bc6e66a8e340f075fcf6227e417a2dc976b92b91f3cdc778bb858778b6747"
[[package]]
name = "const_format"
version = "0.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c990efc7a285731f9a4378d81aff2f0e85a2c8781a05ef0f8baa8dac54d0ff48"
dependencies = [
"const_format_proc_macros",
]
[[package]]
name = "const_format_proc_macros"
version = "0.2.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e026b6ce194a874cb9cf32cd5772d1ef9767cc8fcb5765948d74f37a9d8b2bf6"
dependencies = [
"proc-macro2 1.0.66",
"quote 1.0.31",
"unicode-xid 0.2.4",
]
[[package]]
name = "constant_time_eq"
version = "0.1.5"
@@ -679,6 +699,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
[[package]]
name = "convert_case"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "cookie"
version = "0.16.2"
@@ -1074,7 +1103,7 @@ version = "0.99.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
dependencies = [
"convert_case",
"convert_case 0.4.0",
"proc-macro2 1.0.66",
"quote 1.0.31",
"rustc_version 0.4.0",
@@ -4519,6 +4548,33 @@ dependencies = [
"tokio-rustls",
]
[[package]]
name = "sscanf"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c713ebd15ce561dd4a13ed62bc2a0368e16806fc30dcaf66ecf1256b2a3fdde6"
dependencies = [
"const_format",
"lazy_static",
"regex",
"sscanf_macro",
]
[[package]]
name = "sscanf_macro"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84955aa74a157e5834d58a07be11af7f0ab923f0194a0bb2ea6b3db8b5d1611d"
dependencies = [
"convert_case 0.6.0",
"proc-macro2 1.0.66",
"quote 1.0.31",
"regex-syntax 0.6.29",
"strsim 0.10.0",
"syn 2.0.18",
"unicode-width",
]
[[package]]
name = "ssh-encoding"
version = "0.1.0"
@@ -4657,6 +4713,7 @@ dependencies = [
"sha2 0.9.9",
"simple-logging",
"sqlx",
"sscanf",
"ssh-key",
"stderrlog",
"tar",

View File

@@ -121,6 +121,7 @@ rpassword = "7.0.0"
rpc-toolkit = "0.2.2"
rust-argon2 = "1.0.0"
scopeguard = "1.1" # because avahi-sys fucks your shit up
sscanf = "0.4.1"
serde = { version = "1.0.139", features = ["derive", "rc"] }
serde_cbor = { package = "ciborium", version = "0.2.0" }
serde_json = "1.0.82"

View File

@@ -20,6 +20,9 @@ use crate::middleware::auth::LOCAL_AUTH_COOKIE_PATH;
use crate::prelude::*;
use crate::sound::BEP;
use crate::system::time;
use crate::util::cpupower::{
current_governor, get_available_governors, set_governor, GOVERNOR_PERFORMANCE,
};
use crate::util::docker::{create_bridge_network, CONTAINER_DATADIR, CONTAINER_TOOL};
use crate::util::Invoke;
use crate::{Error, ARCH};
@@ -341,6 +344,23 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
.await?;
tracing::info!("Enabled Docker QEMU Emulation");
if current_governor()
.await?
.map(|g| &g != &GOVERNOR_PERFORMANCE)
.unwrap_or(false)
{
tracing::info!("Setting CPU Governor to \"{}\"", GOVERNOR_PERFORMANCE);
if get_available_governors()
.await?
.contains(&GOVERNOR_PERFORMANCE)
{
set_governor(&GOVERNOR_PERFORMANCE).await?;
tracing::info!("Set CPU Governor");
} else {
tracing::warn!("CPU Governor \"{}\" Not Available", GOVERNOR_PERFORMANCE)
}
}
let mut warn_time_not_synced = true;
for _ in 0..60 {
if check_time_is_synchronized().await? {

View File

@@ -0,0 +1,125 @@
use std::borrow::Cow;
use std::collections::BTreeSet;
use imbl::OrdMap;
use tokio::process::Command;
use crate::prelude::*;
use crate::util::Invoke;
pub const GOVERNOR_PERFORMANCE: Governor = Governor(Cow::Borrowed("performance"));
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Governor(Cow<'static, str>);
impl std::fmt::Display for Governor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::ops::Deref for Governor {
type Target = str;
fn deref(&self) -> &Self::Target {
&*self.0
}
}
impl std::borrow::Borrow<str> for Governor {
fn borrow(&self) -> &str {
&**self
}
}
pub async fn get_available_governors() -> Result<BTreeSet<Governor>, Error> {
let raw = String::from_utf8(
Command::new("cpupower")
.arg("frequency-info")
.arg("-g")
.invoke(ErrorKind::CpuSettings)
.await?,
)?;
let mut for_cpu: OrdMap<u32, BTreeSet<Governor>> = OrdMap::new();
let mut current_cpu = None;
for line in raw.lines() {
if line.starts_with("analyzing") {
current_cpu = Some(
sscanf::sscanf!(line, "analyzing CPU {u32}:")
.map_err(|e| eyre!("{e}"))
.with_kind(ErrorKind::ParseSysInfo)?,
);
} else if let Some(rest) = line
.trim()
.strip_prefix("available cpufreq governors:")
.map(|s| s.trim())
{
if rest != "Not Available" {
for_cpu
.entry(current_cpu.ok_or_else(|| {
Error::new(
eyre!("governors listed before cpu"),
ErrorKind::ParseSysInfo,
)
})?)
.or_default()
.extend(
rest.split_ascii_whitespace()
.map(|g| Governor(Cow::Owned(g.to_owned()))),
);
}
}
}
Ok(for_cpu
.into_iter()
.fold(None, |acc: Option<BTreeSet<Governor>>, (_, x)| {
if let Some(acc) = acc {
Some(acc.intersection(&x).cloned().collect())
} else {
Some(x)
}
})
.unwrap_or_default()) // include only governors available for ALL cpus
}
pub async fn current_governor() -> Result<Option<Governor>, Error> {
let Some(raw) = Command::new("cpupower")
.arg("frequency-info")
.arg("-p")
.invoke(ErrorKind::CpuSettings)
.await
.and_then(|s| Ok(Some(String::from_utf8(s)?)))
.or_else(|e| {
if e.source
.to_string()
.contains("Unable to determine current policy")
{
Ok(None)
} else {
Err(e)
}
})?
else {
return Ok(None);
};
for line in raw.lines() {
if let Some(governor) = line
.trim()
.strip_prefix("The governor \"")
.and_then(|s| s.strip_suffix("\" may decide which speed to use"))
{
return Ok(Some(Governor(Cow::Owned(governor.to_owned()))));
}
}
Err(Error::new(
eyre!("Failed to parse cpupower output:\n{raw}"),
ErrorKind::ParseSysInfo,
))
}
pub async fn set_governor(governor: &Governor) -> Result<(), Error> {
Command::new("cpupower")
.arg("frequency-set")
.arg("-g")
.arg(&*governor.0)
.invoke(ErrorKind::CpuSettings)
.await?;
Ok(())
}

View File

@@ -24,6 +24,7 @@ use tracing::instrument;
use crate::shutdown::Shutdown;
use crate::{Error, ErrorKind, ResultExt as _};
pub mod config;
pub mod cpupower;
pub mod docker;
pub mod http_reader;
pub mod io;

View File

@@ -24,6 +24,7 @@ iw
jq
libavahi-client3
libyajl2
linux-cpupower
lm-sensors
lshw
lvm2

View File

@@ -78,6 +78,7 @@ pub enum ErrorKind {
OpenSsh = 66,
Zram = 67,
Lshw = 68,
CpuSettings = 69,
}
impl ErrorKind {
pub fn as_str(&self) -> &'static str {
@@ -151,6 +152,7 @@ impl ErrorKind {
OpenSsh => "OpenSSH Error",
Zram => "Zram Error",
Lshw => "LSHW Error",
CpuSettings => "CPU Settings Error",
}
}
}