mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
set better governor hierarchy and add cli command to change (#2519)
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user