set better governor hierarchy and add cli command to change (#2519)

This commit is contained in:
Aiden McClelland
2023-11-15 12:17:10 -07:00
committed by GitHub
parent 5f3db8e567
commit a1495dd33d
6 changed files with 100 additions and 36 deletions

View File

@@ -18,7 +18,7 @@ fn select_executable(name: &str) -> Option<fn()> {
match name { match name {
#[cfg(feature = "avahi-alias")] #[cfg(feature = "avahi-alias")]
"avahi-alias" => Some(avahi_alias::main), "avahi-alias" => Some(avahi_alias::main),
#[cfg(feature = "js_engine")] #[cfg(feature = "js-engine")]
"start-deno" => Some(start_deno::main), "start-deno" => Some(start_deno::main),
#[cfg(feature = "cli")] #[cfg(feature = "cli")]
"start-cli" => Some(start_cli::main), "start-cli" => Some(start_cli::main),
@@ -36,24 +36,14 @@ fn select_executable(name: &str) -> Option<fn()> {
pub fn startbox() { pub fn startbox() {
let args = std::env::args().take(2).collect::<Vec<_>>(); let args = std::env::args().take(2).collect::<Vec<_>>();
if let Some(x) = args let executable = args
.get(0) .get(0)
.and_then(|s| Path::new(&*s).file_name()) .and_then(|s| Path::new(&*s).file_name())
.and_then(|s| s.to_str()) .and_then(|s| s.to_str());
.and_then(|s| select_executable(&s)) if let Some(x) = executable.and_then(|s| select_executable(&s)) {
{
x()
} else if let Some(x) = args.get(1).and_then(|s| select_executable(&s)) {
x() x()
} else { } else {
eprintln!( eprintln!("unknown executable: {}", executable.unwrap_or("N/A"));
"unknown executable: {}",
args.get(0)
.filter(|x| &**x != "startbox")
.or_else(|| args.get(1))
.map(|s| s.as_str())
.unwrap_or("N/A")
);
std::process::exit(1); std::process::exit(1);
} }
} }

View File

@@ -22,6 +22,7 @@ use crate::net::utils::{get_iface_ipv4_addr, get_iface_ipv6_addr};
use crate::prelude::*; use crate::prelude::*;
use crate::s9pk::manifest::{Manifest, PackageId}; use crate::s9pk::manifest::{Manifest, PackageId};
use crate::status::Status; use crate::status::Status;
use crate::util::cpupower::{get_preferred_governor, Governor};
use crate::util::Version; use crate::util::Version;
use crate::version::{Current, VersionT}; use crate::version::{Current, VersionT};
use crate::{ARCH, PLATFORM}; use crate::{ARCH, PLATFORM};
@@ -83,6 +84,7 @@ impl Database {
.join(":"), .join(":"),
ntp_synced: false, ntp_synced: false,
zram: true, zram: true,
governor: None,
}, },
package_data: AllPackageData::default(), package_data: AllPackageData::default(),
ui: serde_json::from_str(include_str!(concat!( ui: serde_json::from_str(include_str!(concat!(
@@ -134,6 +136,7 @@ pub struct ServerInfo {
pub ntp_synced: bool, pub ntp_synced: bool,
#[serde(default)] #[serde(default)]
pub zram: bool, pub zram: bool,
pub governor: Option<Governor>,
} }
#[derive(Debug, Deserialize, Serialize, HasModel)] #[derive(Debug, Deserialize, Serialize, HasModel)]

View File

@@ -20,7 +20,7 @@ use crate::middleware::auth::LOCAL_AUTH_COOKIE_PATH;
use crate::prelude::*; use crate::prelude::*;
use crate::sound::BEP; use crate::sound::BEP;
use crate::util::cpupower::{ use crate::util::cpupower::{
current_governor, get_available_governors, set_governor, GOVERNOR_PERFORMANCE, current_governor, get_available_governors, get_preferred_governor, set_governor,
}; };
use crate::util::docker::{create_bridge_network, CONTAINER_DATADIR, CONTAINER_TOOL}; use crate::util::docker::{create_bridge_network, CONTAINER_DATADIR, CONTAINER_TOOL};
use crate::util::Invoke; use crate::util::Invoke;
@@ -354,21 +354,20 @@ pub async fn init(cfg: &RpcContextConfig) -> Result<InitResult, Error> {
.await?; .await?;
tracing::info!("Enabled Docker QEMU Emulation"); tracing::info!("Enabled Docker QEMU Emulation");
if current_governor() let governor = if let Some(governor) = &server_info.governor {
.await? if get_available_governors().await?.contains(governor) {
.map(|g| &g != &GOVERNOR_PERFORMANCE) Some(governor)
.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 { } else {
tracing::warn!("CPU Governor \"{}\" Not Available", GOVERNOR_PERFORMANCE) tracing::warn!("CPU Governor \"{governor}\" Not Available");
None
} }
} else {
get_preferred_governor().await?
};
if let Some(governor) = governor {
tracing::info!("Setting CPU Governor to \"{governor}\"");
set_governor(governor).await?;
tracing::info!("Set CPU Governor");
} }
let mut time_not_synced = true; let mut time_not_synced = true;

View File

@@ -226,18 +226,18 @@ async fn test_start_deno_command() -> Result<Command, Error> {
.arg("build") .arg("build")
.invoke(ErrorKind::Unknown) .invoke(ErrorKind::Unknown)
.await?; .await?;
if tokio::fs::metadata("target/debug/start-deno") if tokio::fs::metadata("../target/debug/start-deno")
.await .await
.is_err() .is_err()
{ {
Command::new("ln") Command::new("ln")
.arg("-rsf") .arg("-rsf")
.arg("target/debug/startbox") .arg("../target/debug/startbox")
.arg("target/debug/start-deno") .arg("../target/debug/start-deno")
.invoke(crate::ErrorKind::Filesystem) .invoke(crate::ErrorKind::Filesystem)
.await?; .await?;
} }
Ok(Command::new("target/debug/start-deno")) Ok(Command::new("../target/debug/start-deno"))
} }
#[tokio::test] #[tokio::test]

View File

@@ -1,3 +1,4 @@
use std::collections::BTreeSet;
use std::fmt; use std::fmt;
use chrono::Utc; use chrono::Utc;
@@ -20,11 +21,12 @@ use crate::logs::{
}; };
use crate::prelude::*; use crate::prelude::*;
use crate::shutdown::Shutdown; use crate::shutdown::Shutdown;
use crate::util::cpupower::{get_available_governors, set_governor, Governor};
use crate::util::serde::{display_serializable, IoFormat}; use crate::util::serde::{display_serializable, IoFormat};
use crate::util::{display_none, Invoke}; use crate::util::{display_none, Invoke};
use crate::{Error, ErrorKind, ResultExt}; use crate::{Error, ErrorKind, ResultExt};
#[command(subcommands(zram))] #[command(subcommands(zram, governor))]
pub async fn experimental() -> Result<(), Error> { pub async fn experimental() -> Result<(), Error> {
Ok(()) Ok(())
} }
@@ -85,6 +87,56 @@ pub async fn zram(#[context] ctx: RpcContext, #[arg] enable: bool) -> Result<(),
Ok(()) Ok(())
} }
#[derive(Debug, Deserialize, Serialize)]
pub struct GovernorInfo {
current: Option<Governor>,
available: BTreeSet<Governor>,
}
fn display_governor_info(arg: GovernorInfo, matches: &ArgMatches) {
use prettytable::*;
if matches.is_present("format") {
return display_serializable(arg, matches);
}
let mut table = Table::new();
table.add_row(row![bc -> "GOVERNORS"]);
for entry in arg.available {
if Some(&entry) == arg.current.as_ref() {
table.add_row(row![g -> format!("* {entry} (current)")]);
} else {
table.add_row(row![entry]);
}
}
table.print_tty(false).unwrap();
}
#[command(display(display_governor_info))]
pub async fn governor(
#[context] ctx: RpcContext,
#[allow(unused_variables)]
#[arg(long = "format")]
format: Option<IoFormat>,
#[arg] set: Option<Governor>,
) -> Result<GovernorInfo, Error> {
let available = get_available_governors().await?;
if let Some(set) = set {
if !available.contains(&set) {
return Err(Error::new(
eyre!("Governor {set} not available"),
ErrorKind::InvalidRequest,
));
}
set_governor(&set).await?;
ctx.db
.mutate(|d| d.as_server_info_mut().as_governor_mut().ser(&Some(set)))
.await?;
}
let current = ctx.db.peek().await.as_server_info().as_governor().de()?;
Ok(GovernorInfo { current, available })
}
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct TimeInfo { pub struct TimeInfo {
now: String, now: String,

View File

@@ -7,10 +7,20 @@ use tokio::process::Command;
use crate::prelude::*; use crate::prelude::*;
use crate::util::Invoke; use crate::util::Invoke;
pub const GOVERNOR_PERFORMANCE: Governor = Governor(Cow::Borrowed("performance")); pub const GOVERNOR_HEIRARCHY: &[Governor] = &[
Governor(Cow::Borrowed("ondemand")),
Governor(Cow::Borrowed("schedutil")),
Governor(Cow::Borrowed("conservative")),
];
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
pub struct Governor(Cow<'static, str>); pub struct Governor(Cow<'static, str>);
impl std::str::FromStr for Governor {
type Err = std::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(s.to_owned().into()))
}
}
impl std::fmt::Display for Governor { impl std::fmt::Display for Governor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f) self.0.fmt(f)
@@ -114,6 +124,16 @@ pub async fn current_governor() -> Result<Option<Governor>, Error> {
)) ))
} }
pub async fn get_preferred_governor() -> Result<Option<&'static Governor>, Error> {
let governors = get_available_governors().await?;
for governor in GOVERNOR_HEIRARCHY {
if governors.contains(governor) {
return Ok(Some(governor));
}
}
Ok(None)
}
pub async fn set_governor(governor: &Governor) -> Result<(), Error> { pub async fn set_governor(governor: &Governor) -> Result<(), Error> {
Command::new("cpupower") Command::new("cpupower")
.arg("frequency-set") .arg("frequency-set")