mirror of
https://github.com/edera-dev/krata.git
synced 2025-08-02 12:50:54 +00:00
feature(xen): implement power management operations (#215)
This commit is contained in:
parent
0f49d0cec4
commit
b42b730b77
@ -33,3 +33,7 @@ path = "examples/domain_create.rs"
|
||||
[[example]]
|
||||
name = "xencall-version-capabilities"
|
||||
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,
|
||||
#[error("mmap batch failed: {0}")]
|
||||
MmapBatchFailed(nix::errno::Errno),
|
||||
#[error("specified value is too long")]
|
||||
ValueTooLong,
|
||||
}
|
||||
|
||||
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::time::Duration;
|
||||
use sys::{
|
||||
E820Entry, ForeignMemoryMap, PhysdevMapPirq, VcpuGuestContextAny, HYPERVISOR_PHYSDEV_OP,
|
||||
PHYSDEVOP_MAP_PIRQ, XEN_DOMCTL_MAX_INTERFACE_VERSION, XEN_DOMCTL_MIN_INTERFACE_VERSION,
|
||||
XEN_MEM_SET_MEMORY_MAP,
|
||||
CpuId, E820Entry, ForeignMemoryMap, PhysdevMapPirq, Sysctl, SysctlCputopo, SysctlCputopoinfo,
|
||||
SysctlPhysinfo, SysctlPmOp, SysctlPmOpValue, SysctlSetCpuFreqGov, SysctlValue,
|
||||
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::time::sleep;
|
||||
@ -42,6 +46,7 @@ pub struct XenCall {
|
||||
pub handle: Arc<File>,
|
||||
semaphore: Arc<Semaphore>,
|
||||
domctl_interface_version: u32,
|
||||
sysctl_interface_version: u32,
|
||||
}
|
||||
|
||||
impl XenCall {
|
||||
@ -52,10 +57,12 @@ impl XenCall {
|
||||
.open("/dev/xen/privcmd")?;
|
||||
let domctl_interface_version =
|
||||
XenCall::detect_domctl_interface_version(&handle, current_domid)?;
|
||||
let sysctl_interface_version = XenCall::detect_sysctl_interface_version(&handle)?;
|
||||
Ok(XenCall {
|
||||
handle: Arc::new(handle),
|
||||
semaphore: Arc::new(Semaphore::new(1)),
|
||||
domctl_interface_version,
|
||||
sysctl_interface_version,
|
||||
})
|
||||
}
|
||||
|
||||
@ -83,6 +90,32 @@ impl XenCall {
|
||||
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> {
|
||||
let _permit = self.semaphore.acquire().await.ok()?;
|
||||
trace!(
|
||||
@ -917,4 +950,141 @@ impl XenCall {
|
||||
.await?;
|
||||
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 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],
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user