mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-03 21:21:32 +00:00
feature(xen): implement power management operations (#215)
This commit is contained in:
@ -33,3 +33,7 @@ path = "examples/domain_create.rs"
|
|||||||
[[example]]
|
[[example]]
|
||||||
name = "xencall-version-capabilities"
|
name = "xencall-version-capabilities"
|
||||||
path = "examples/version_capabilities.rs"
|
path = "examples/version_capabilities.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "xencall-power-management"
|
||||||
|
path = "examples/power_management.rs"
|
||||||
|
19
crates/xen/xencall/examples/power_management.rs
Normal file
19
crates/xen/xencall/examples/power_management.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use xencall::error::Result;
|
||||||
|
use xencall::sys::CpuId;
|
||||||
|
use xencall::XenCall;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> Result<()> {
|
||||||
|
env_logger::init();
|
||||||
|
|
||||||
|
let call = XenCall::open(0)?;
|
||||||
|
let physinfo = call.phys_info().await?;
|
||||||
|
println!("{:?}", physinfo);
|
||||||
|
let topology = call.cpu_topology().await?;
|
||||||
|
println!("{:?}", topology);
|
||||||
|
call.set_cpufreq_gov(CpuId::All, "performance").await?;
|
||||||
|
call.set_cpufreq_gov(CpuId::Single(0), "performance")
|
||||||
|
.await?;
|
||||||
|
call.set_turbo_mode(CpuId::All, true).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
@ -14,6 +14,8 @@ pub enum Error {
|
|||||||
PopulatePhysmapFailed,
|
PopulatePhysmapFailed,
|
||||||
#[error("mmap batch failed: {0}")]
|
#[error("mmap batch failed: {0}")]
|
||||||
MmapBatchFailed(nix::errno::Errno),
|
MmapBatchFailed(nix::errno::Errno),
|
||||||
|
#[error("specified value is too long")]
|
||||||
|
ValueTooLong,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
@ -25,9 +25,13 @@ use std::ffi::{c_long, c_uint, c_ulong, c_void};
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use sys::{
|
use sys::{
|
||||||
E820Entry, ForeignMemoryMap, PhysdevMapPirq, VcpuGuestContextAny, HYPERVISOR_PHYSDEV_OP,
|
CpuId, E820Entry, ForeignMemoryMap, PhysdevMapPirq, Sysctl, SysctlCputopo, SysctlCputopoinfo,
|
||||||
PHYSDEVOP_MAP_PIRQ, XEN_DOMCTL_MAX_INTERFACE_VERSION, XEN_DOMCTL_MIN_INTERFACE_VERSION,
|
SysctlPhysinfo, SysctlPmOp, SysctlPmOpValue, SysctlSetCpuFreqGov, SysctlValue,
|
||||||
XEN_MEM_SET_MEMORY_MAP,
|
VcpuGuestContextAny, HYPERVISOR_PHYSDEV_OP, HYPERVISOR_SYSCTL, PHYSDEVOP_MAP_PIRQ,
|
||||||
|
XEN_DOMCTL_MAX_INTERFACE_VERSION, XEN_DOMCTL_MIN_INTERFACE_VERSION, XEN_MEM_SET_MEMORY_MAP,
|
||||||
|
XEN_SYSCTL_CPUTOPOINFO, XEN_SYSCTL_MAX_INTERFACE_VERSION, XEN_SYSCTL_MIN_INTERFACE_VERSION,
|
||||||
|
XEN_SYSCTL_PHYSINFO, XEN_SYSCTL_PM_OP, XEN_SYSCTL_PM_OP_DISABLE_TURBO,
|
||||||
|
XEN_SYSCTL_PM_OP_ENABLE_TURBO,
|
||||||
};
|
};
|
||||||
use tokio::sync::Semaphore;
|
use tokio::sync::Semaphore;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
@ -42,6 +46,7 @@ pub struct XenCall {
|
|||||||
pub handle: Arc<File>,
|
pub handle: Arc<File>,
|
||||||
semaphore: Arc<Semaphore>,
|
semaphore: Arc<Semaphore>,
|
||||||
domctl_interface_version: u32,
|
domctl_interface_version: u32,
|
||||||
|
sysctl_interface_version: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XenCall {
|
impl XenCall {
|
||||||
@ -52,10 +57,12 @@ impl XenCall {
|
|||||||
.open("/dev/xen/privcmd")?;
|
.open("/dev/xen/privcmd")?;
|
||||||
let domctl_interface_version =
|
let domctl_interface_version =
|
||||||
XenCall::detect_domctl_interface_version(&handle, current_domid)?;
|
XenCall::detect_domctl_interface_version(&handle, current_domid)?;
|
||||||
|
let sysctl_interface_version = XenCall::detect_sysctl_interface_version(&handle)?;
|
||||||
Ok(XenCall {
|
Ok(XenCall {
|
||||||
handle: Arc::new(handle),
|
handle: Arc::new(handle),
|
||||||
semaphore: Arc::new(Semaphore::new(1)),
|
semaphore: Arc::new(Semaphore::new(1)),
|
||||||
domctl_interface_version,
|
domctl_interface_version,
|
||||||
|
sysctl_interface_version,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,6 +90,32 @@ impl XenCall {
|
|||||||
Err(Error::XenVersionUnsupported)
|
Err(Error::XenVersionUnsupported)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn detect_sysctl_interface_version(handle: &File) -> Result<u32> {
|
||||||
|
for version in XEN_SYSCTL_MIN_INTERFACE_VERSION..XEN_SYSCTL_MAX_INTERFACE_VERSION + 1 {
|
||||||
|
let mut sysctl = Sysctl {
|
||||||
|
cmd: XEN_SYSCTL_CPUTOPOINFO,
|
||||||
|
interface_version: version,
|
||||||
|
value: SysctlValue {
|
||||||
|
cputopoinfo: SysctlCputopoinfo {
|
||||||
|
num_cpus: 0,
|
||||||
|
handle: null_mut(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
let mut call = Hypercall {
|
||||||
|
op: HYPERVISOR_SYSCTL,
|
||||||
|
arg: [addr_of_mut!(sysctl) as u64, 0, 0, 0, 0],
|
||||||
|
};
|
||||||
|
let result = sys::hypercall(handle.as_raw_fd(), &mut call).unwrap_or(-1);
|
||||||
|
if result == 0 {
|
||||||
|
return Ok(version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Error::XenVersionUnsupported)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn mmap(&self, addr: u64, len: u64) -> Option<u64> {
|
pub async fn mmap(&self, addr: u64, len: u64) -> Option<u64> {
|
||||||
let _permit = self.semaphore.acquire().await.ok()?;
|
let _permit = self.semaphore.acquire().await.ok()?;
|
||||||
trace!(
|
trace!(
|
||||||
@ -917,4 +950,141 @@ impl XenCall {
|
|||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn cpu_topology(&self) -> Result<Vec<SysctlCputopo>> {
|
||||||
|
let mut sysctl = Sysctl {
|
||||||
|
cmd: XEN_SYSCTL_CPUTOPOINFO,
|
||||||
|
interface_version: self.sysctl_interface_version,
|
||||||
|
value: SysctlValue {
|
||||||
|
cputopoinfo: SysctlCputopoinfo {
|
||||||
|
num_cpus: 0,
|
||||||
|
handle: null_mut(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
self.hypercall1(HYPERVISOR_SYSCTL, addr_of_mut!(sysctl) as c_ulong)
|
||||||
|
.await?;
|
||||||
|
let cpus = unsafe { sysctl.value.cputopoinfo.num_cpus };
|
||||||
|
let mut topos = vec![
|
||||||
|
SysctlCputopo {
|
||||||
|
core: 0,
|
||||||
|
socket: 0,
|
||||||
|
node: 0
|
||||||
|
};
|
||||||
|
cpus as usize
|
||||||
|
];
|
||||||
|
let mut sysctl = Sysctl {
|
||||||
|
cmd: XEN_SYSCTL_CPUTOPOINFO,
|
||||||
|
interface_version: self.sysctl_interface_version,
|
||||||
|
value: SysctlValue {
|
||||||
|
cputopoinfo: SysctlCputopoinfo {
|
||||||
|
num_cpus: cpus,
|
||||||
|
handle: topos.as_mut_ptr(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
self.hypercall1(HYPERVISOR_SYSCTL, addr_of_mut!(sysctl) as c_ulong)
|
||||||
|
.await?;
|
||||||
|
Ok(topos)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn phys_info(&self) -> Result<SysctlPhysinfo> {
|
||||||
|
let mut sysctl = Sysctl {
|
||||||
|
cmd: XEN_SYSCTL_PHYSINFO,
|
||||||
|
interface_version: self.sysctl_interface_version,
|
||||||
|
value: SysctlValue {
|
||||||
|
phys_info: SysctlPhysinfo::default(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
self.hypercall1(HYPERVISOR_SYSCTL, addr_of_mut!(sysctl) as c_ulong)
|
||||||
|
.await?;
|
||||||
|
Ok(unsafe { sysctl.value.phys_info })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_cpufreq_gov(&self, cpuid: CpuId, gov: impl AsRef<str>) -> Result<()> {
|
||||||
|
match cpuid {
|
||||||
|
CpuId::All => {
|
||||||
|
let phys_info = self.phys_info().await?;
|
||||||
|
for cpuid in 0..phys_info.max_cpu_id + 1 {
|
||||||
|
self.do_set_cpufreq_gov(cpuid, gov.as_ref()).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CpuId::Single(id) => {
|
||||||
|
self.do_set_cpufreq_gov(id, gov).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_set_cpufreq_gov(&self, cpuid: u32, gov: impl AsRef<str>) -> Result<()> {
|
||||||
|
let governor = gov.as_ref().as_bytes().to_vec();
|
||||||
|
if governor.len() > 15 {
|
||||||
|
return Err(Error::ValueTooLong);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut scaling_governor = [0u8; 16];
|
||||||
|
|
||||||
|
// leave space for the last byte to be zero at all times.
|
||||||
|
for i in 0..15usize {
|
||||||
|
if i >= governor.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
scaling_governor[i] = governor[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sysctl = Sysctl {
|
||||||
|
cmd: XEN_SYSCTL_PM_OP,
|
||||||
|
interface_version: self.sysctl_interface_version,
|
||||||
|
value: SysctlValue {
|
||||||
|
pm_op: SysctlPmOp {
|
||||||
|
cmd: XEN_SYSCTL_PM_OP_ENABLE_TURBO,
|
||||||
|
cpuid,
|
||||||
|
value: SysctlPmOpValue {
|
||||||
|
set_gov: SysctlSetCpuFreqGov { scaling_governor },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
self.hypercall1(HYPERVISOR_SYSCTL, addr_of_mut!(sysctl) as c_ulong)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_turbo_mode(&self, cpuid: CpuId, enable: bool) -> Result<()> {
|
||||||
|
match cpuid {
|
||||||
|
CpuId::All => {
|
||||||
|
let phys_info = self.phys_info().await?;
|
||||||
|
for cpuid in 0..phys_info.max_cpu_id + 1 {
|
||||||
|
self.do_set_turbo_mode(cpuid, enable).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CpuId::Single(id) => {
|
||||||
|
self.do_set_turbo_mode(id, enable).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn do_set_turbo_mode(&self, cpuid: u32, enable: bool) -> Result<()> {
|
||||||
|
let mut sysctl = Sysctl {
|
||||||
|
cmd: XEN_SYSCTL_PM_OP,
|
||||||
|
interface_version: self.sysctl_interface_version,
|
||||||
|
value: SysctlValue {
|
||||||
|
pm_op: SysctlPmOp {
|
||||||
|
cmd: if enable {
|
||||||
|
XEN_SYSCTL_PM_OP_ENABLE_TURBO
|
||||||
|
} else {
|
||||||
|
XEN_SYSCTL_PM_OP_DISABLE_TURBO
|
||||||
|
},
|
||||||
|
cpuid,
|
||||||
|
value: SysctlPmOpValue { pad: [0u8; 128] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
self.hypercall1(HYPERVISOR_SYSCTL, addr_of_mut!(sysctl) as c_ulong)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -712,3 +712,92 @@ pub struct HvmContext {
|
|||||||
pub struct PagingMempool {
|
pub struct PagingMempool {
|
||||||
pub size: u64,
|
pub size: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct SysctlCputopo {
|
||||||
|
pub core: u32,
|
||||||
|
pub socket: u32,
|
||||||
|
pub node: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct SysctlSetCpuFreqGov {
|
||||||
|
pub scaling_governor: [u8; 16],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub union SysctlPmOpValue {
|
||||||
|
pub set_gov: SysctlSetCpuFreqGov,
|
||||||
|
pub opt_smt: u32,
|
||||||
|
pub pad: [u8; 128],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct SysctlPmOp {
|
||||||
|
pub cmd: u32,
|
||||||
|
pub cpuid: u32,
|
||||||
|
pub value: SysctlPmOpValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct SysctlCputopoinfo {
|
||||||
|
pub num_cpus: u32,
|
||||||
|
pub handle: *mut SysctlCputopo,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub union SysctlValue {
|
||||||
|
pub cputopoinfo: SysctlCputopoinfo,
|
||||||
|
pub pm_op: SysctlPmOp,
|
||||||
|
pub phys_info: SysctlPhysinfo,
|
||||||
|
pub pad: [u8; 128],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Sysctl {
|
||||||
|
pub cmd: u32,
|
||||||
|
pub interface_version: u32,
|
||||||
|
pub value: SysctlValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const XEN_SYSCTL_PHYSINFO: u32 = 3;
|
||||||
|
pub const XEN_SYSCTL_PM_OP: u32 = 12;
|
||||||
|
pub const XEN_SYSCTL_CPUTOPOINFO: u32 = 16;
|
||||||
|
|
||||||
|
pub const XEN_SYSCTL_MIN_INTERFACE_VERSION: u32 = 0x00000015;
|
||||||
|
pub const XEN_SYSCTL_MAX_INTERFACE_VERSION: u32 = 0x00000020;
|
||||||
|
pub const XEN_SYSCTL_PM_OP_SET_SCHED_OPT_STMT: u32 = 0x21;
|
||||||
|
pub const XEN_SYSCTL_PM_OP_ENABLE_TURBO: u32 = 0x26;
|
||||||
|
pub const XEN_SYSCTL_PM_OP_DISABLE_TURBO: u32 = 0x27;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum CpuId {
|
||||||
|
All,
|
||||||
|
Single(u32),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct SysctlPhysinfo {
|
||||||
|
pub threads_per_core: u32,
|
||||||
|
pub cores_per_socket: u32,
|
||||||
|
pub nr_cpus: u32,
|
||||||
|
pub max_cpu_id: u32,
|
||||||
|
pub nr_nodes: u32,
|
||||||
|
pub max_node_id: u32,
|
||||||
|
pub cpu_khz: u32,
|
||||||
|
pub capabilities: u32,
|
||||||
|
pub arch_capabilities: u32,
|
||||||
|
pub pad: u32,
|
||||||
|
pub total_pages: u64,
|
||||||
|
pub free_pages: u64,
|
||||||
|
pub scrub_pages: u64,
|
||||||
|
pub outstanding_pages: u64,
|
||||||
|
pub max_mfn: u64,
|
||||||
|
pub hw_cap: [u32; 8],
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user