Power management core functionality (#217)

* feat(power-management-core): add core power management control messages for kratad

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>

* feat(power-management-core): expose xen hypercall client publicly

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>

* feat(power-management-core): add indexmap to kratart crate dependencies

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>

* feat(power-management-core): implement power management core in kratart

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>

* feat(power-management-core): bubble up runtime context in daemon/control service

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>

* feat(power-management-core): expose performance/efficiency core data in protobuf

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>

* feat(power-management-core): fix up some protobuf message names

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>

* feat(power-management-core): fix up performance core heuristic

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>

* feat(power-management-core): implement GetHostCpuTopology RPC

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>

* feat(power-management-core): hackfix to get sysctls working with tokio

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>

* feat(power-management-core): borrow the PowerManagementContext when calling functions belonging to it

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>

* feat(power-management-core): remove GetHostPowerManagementPolicy RPC for now

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>

* feat(power-management-core): implement SetHostPowerManagementPolicy RPC

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>

* feat(power-management-core): add cpu-topology command

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>

* feat(power-management-core): appease format checking

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>

* fix(runtime): cpu topology corrections

---------

Signed-off-by: Ariadne Conill <ariadne@ariadne.space>
Co-authored-by: Alex Zenla <alex@edera.dev>
This commit is contained in:
Ariadne Conill
2024-06-29 15:43:08 -07:00
committed by GitHub
parent 39ded9c7f4
commit a79320b4fc
15 changed files with 349 additions and 30 deletions

View File

@ -13,6 +13,7 @@ use xenstore::{XsdClient, XsdInterface};
use self::{
autoloop::AutoLoop,
launch::{GuestLaunchRequest, GuestLauncher},
power::PowerManagementContext,
};
pub mod autoloop;
@ -20,6 +21,7 @@ pub mod cfgblk;
pub mod channel;
pub mod ip;
pub mod launch;
pub mod power;
#[cfg(target_arch = "x86_64")]
type RuntimePlatform = xenplatform::x86pv::X86PvPlatform;
@ -321,4 +323,9 @@ impl Runtime {
pub async fn dupe(&self) -> Result<Runtime> {
Runtime::new(self.host_uuid).await
}
pub async fn power_management_context(&self) -> Result<PowerManagementContext> {
let context = RuntimeContext::new(self.host_uuid).await?;
Ok(PowerManagementContext { context })
}
}

162
crates/runtime/src/power.rs Normal file
View File

@ -0,0 +1,162 @@
use anyhow::Result;
use indexmap::IndexMap;
use xencall::sys::{CpuId, SysctlCputopo};
use crate::RuntimeContext;
#[derive(Clone)]
pub struct PowerManagementContext {
pub context: RuntimeContext,
}
#[derive(Clone, Copy, Debug)]
pub enum CpuClass {
Standard,
Performance,
Efficiency,
}
#[derive(Clone, Copy, Debug)]
pub struct CpuTopologyInfo {
pub core: u32,
pub socket: u32,
pub node: u32,
pub thread: u32,
pub class: CpuClass,
}
fn labelled_topo(input: &[SysctlCputopo]) -> Vec<CpuTopologyInfo> {
let mut cores: IndexMap<(u32, u32, u32), Vec<CpuTopologyInfo>> = IndexMap::new();
let mut pe_cores = false;
let mut last: Option<SysctlCputopo> = None;
for item in input {
if cores.is_empty() {
cores.insert(
(item.core, item.socket, item.node),
vec![CpuTopologyInfo {
core: item.core,
socket: item.socket,
thread: 0,
node: item.node,
class: CpuClass::Standard,
}],
);
last = Some(*item);
continue;
}
if last
.map(|last| (item.core - last.core) >= 2)
.unwrap_or(false)
{
// detect if performance cores seem to be kicking in.
if let Some(last) = last {
if let Some(list) = cores.get_mut(&(last.core, last.socket, last.node)) {
for other in list {
other.class = CpuClass::Performance;
}
}
}
let list = cores
.entry((item.core, item.socket, item.node))
.or_default();
for old in &mut *list {
old.class = CpuClass::Performance;
}
list.push(CpuTopologyInfo {
core: item.core,
socket: item.socket,
thread: 0,
node: item.node,
class: CpuClass::Performance,
});
pe_cores = true;
} else if pe_cores && last.map(|last| item.core == last.core + 1).unwrap_or(false) {
// detect efficiency cores if P/E cores are in use.
if let Some(last) = last {
if let Some(list) = cores.get_mut(&(last.core, last.socket, last.node)) {
for other in list {
other.class = CpuClass::Efficiency;
}
}
}
let list = cores
.entry((item.core, item.socket, item.node))
.or_default();
list.push(CpuTopologyInfo {
core: item.core,
socket: item.socket,
thread: 0,
node: item.node,
class: CpuClass::Efficiency,
});
} else {
let list = cores
.entry((item.core, item.socket, item.node))
.or_default();
if list.is_empty() {
list.push(CpuTopologyInfo {
core: item.core,
socket: item.socket,
thread: 0,
node: item.node,
class: CpuClass::Standard,
});
} else {
list.push(CpuTopologyInfo {
core: item.core,
socket: item.socket,
thread: 0,
node: item.node,
class: list
.first()
.map(|first| first.class)
.unwrap_or(CpuClass::Standard),
});
}
}
last = Some(*item);
}
for threads in cores.values_mut() {
for (index, thread) in threads.iter_mut().enumerate() {
thread.thread = index as u32;
}
}
cores.into_values().flatten().collect::<Vec<_>>()
}
impl PowerManagementContext {
/// Get the CPU topology, with SMT awareness.
/// Also translates Intel p-core/e-core nonsense: non-sequential core identifiers
/// are treated as p-cores, while e-cores behave as standard cores.
/// If there is a p-core/e-core split, then CPU class will be defined as
/// `CpuClass::Performance` or `CpuClass::Efficiency`, else `CpuClass::Standard`.
pub async fn cpu_topology(&self) -> Result<Vec<CpuTopologyInfo>> {
let xentopo = self.context.xen.call.cpu_topology().await?;
let logicaltopo = labelled_topo(&xentopo);
Ok(logicaltopo)
}
/// Enable or disable SMT awareness in the scheduler.
pub async fn set_smt_policy(&self, enable: bool) -> Result<()> {
self.context
.xen
.call
.set_turbo_mode(CpuId::All, enable)
.await?;
Ok(())
}
/// Set scheduler policy name.
pub async fn set_scheduler_policy(&self, policy: impl AsRef<str>) -> Result<()> {
self.context
.xen
.call
.set_cpufreq_gov(CpuId::All, policy)
.await?;
Ok(())
}
}