From d7a73a417e0404a53a0f9dc12129011c7b658e41 Mon Sep 17 00:00:00 2001 From: Teddy Astie Date: Wed, 15 Jan 2025 11:49:08 +0100 Subject: [PATCH] Improve xen plugin, add CpuFreq reporting. * xen plugin: Provide physinfo in read_host_metrics * split structures and wrappers in separate files in xen crate Signed-off-by: Teddy Astie --- external/xen/src/domctl/ffi.rs | 139 +++++++++++++++++ external/xen/src/domctl/mod.rs | 145 +----------------- external/xen/src/sysctl/ffi.rs | 95 ++++++++++++ external/xen/src/sysctl/mod.rs | 113 +++++--------- plugins/xcp-metrics-plugin-xen/src/plugin.rs | 35 +++-- .../xcp-metrics-plugin-xen/src/plugin/cpu.rs | 74 +++++++-- .../src/plugin/memory.rs | 5 +- .../xcp-metrics-plugin-xen/src/plugin/vcpu.rs | 9 +- 8 files changed, 369 insertions(+), 246 deletions(-) create mode 100644 external/xen/src/domctl/ffi.rs create mode 100644 external/xen/src/sysctl/ffi.rs diff --git a/external/xen/src/domctl/ffi.rs b/external/xen/src/domctl/ffi.rs new file mode 100644 index 0000000..d582a9d --- /dev/null +++ b/external/xen/src/domctl/ffi.rs @@ -0,0 +1,139 @@ +use bitflags::bitflags; +use uuid::Uuid; + +use crate::{Align64, DomId}; + +bitflags! { + #[derive(Clone, Copy, Debug, Default)] + pub struct XenDomctlDominf: u32 { + /// Domain is scheduled to die. + const DYING = 1; + /// Domain is an HVM guest (as opposed to a PV guest). + const HVM_GUEST = 1 << 1; + /// The guest OS has shut down. + const SHUTDOWN = 1 << 2; + /// Currently paused by control software. + const PAUSED = 1 << 3; + /// Currently blocked pending an event. + const BLOCKED = 1 << 4; + /// Domain is currently running + const RUNNING = 1 << 5; + /// Being debugged. + const DEBUGGED = 1 << 6; + /// domain is a xenstore domain + const XS_DOMAIN = 1 << 7; + /// domain has hardware assisted paging + const HAP = 1 << 8; + } +} + +bitflags! { + /// Content of the `emulation_flags` field of the domain creation hypercall. + #[repr(C)] + #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] + pub struct XenX86Emu: u32 { + /// Emulate Local APICs. + const Lapic = 1 << 0; + /// Emulate a HPET timer. + const Hpet = 1 << 1; + /// Emulate the ACPI PM timer. + const Pm = 1 << 2; + /// Emulate the RTC clock. + const Rtc = 1 << 3; + /// Emulate an IOAPIC device. + const Ioapic = 1 << 4; + /// Emulate PIC devices. + const Pic = 1 << 5; + /// Emulate standard VGA. + const Vga = 1 << 6; + /// Emulate an IOMMU. + const Iommu = 1 << 7; + /// Emulate a PIT timer. + const Pit = 1 << 8; + /// Route physical IRQs over event channels. + const UsePirq = 1 << 9; + /// Handle PCI configuration space traps from within Xen. + const Vpci = 1 << 10; + } +} + +bitflags! { + /// Contents of the `misc_flags` field of the domain creation hypercall + #[repr(C)] + #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] + pub struct XenX86Misc: u32 { + /// Grants access to the real physical MSR registers of the host. + const MsrRelaxed = 1 << 0; + } +} + +/// x86-specific domain settings. +#[repr(C)] +#[derive(Copy, Clone, Debug, Default)] +pub struct XenArchDomainconfig { + /// IN: Bitmap of devices to emulate. + pub emulation_flags: XenX86Emu, + /// IN: Miscellaneous x86-specific toggles. + pub misc_flags: XenX86Misc, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct XenDomctlGetDomainInfo { + pub domain: DomId, + pub pad: u16, + pub flags: XenDomctlDominf, + pub tot_pages: Align64, + pub max_pages: Align64, + pub outstanding_pages: Align64, + pub shr_pages: Align64, + pub paged_pages: Align64, + /// GMFN of shared_info struct + pub shared_info_frame: Align64, + pub cpu_time: Align64, + /// Number of VCPUs currently online + pub nr_online_vcpus: u32, + /// Maximum VCPUID in use by this domain. + pub max_vcpu_id: u32, + pub ssidref: u32, + pub handle: Uuid, + pub cpupool: u32, + pub gpaddr_bits: u8, + pub pad2: [u8; 7], + pub arch_config: XenArchDomainconfig, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct XenDomctlGetVCpuInfo { + // IN + pub vcpu: u32, + /// OUT: currently online (not hotplugged)? + pub online: u8, + /// OUT: blocked waiting for an event? + pub blocked: u8, + /// OUT: currently scheduled on its CPU? + pub running: u8, + /// OUT: total cpu time consumed (ns) + pub cpu_time: Align64, + /// OUT: current mapping + pub cpu: u32, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union XenDomctlParam { + pub getdomaininfo: XenDomctlGetDomainInfo, + pub getvcpuinfo: XenDomctlGetVCpuInfo, + pub pad: [u8; 128], +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct XenDomctl { + pub cmd: u32, + pub interface_version: u32, + pub domain: DomId, + pub pad: [u16; 3], + pub param: XenDomctlParam, +} \ No newline at end of file diff --git a/external/xen/src/domctl/mod.rs b/external/xen/src/domctl/mod.rs index 4177fe6..201b44a 100644 --- a/external/xen/src/domctl/mod.rs +++ b/external/xen/src/domctl/mod.rs @@ -1,147 +1,12 @@ -use bitflags::bitflags; -use uuid::Uuid; +mod ffi; +pub use ffi::*; use crate::{ abi::get_xen_abi, hypercall::{XenHypercall, XenMutBuffer}, - Align64, DomId, + DomId, }; -bitflags! { - #[derive(Clone, Copy, Debug, Default)] - pub struct XenDomctlDominf: u32 { - /// Domain is scheduled to die. - const DYING = 1; - /// Domain is an HVM guest (as opposed to a PV guest). - const HVM_GUEST = 1 << 1; - /// The guest OS has shut down. - const SHUTDOWN = 1 << 2; - /// Currently paused by control software. - const PAUSED = 1 << 3; - /// Currently blocked pending an event. - const BLOCKED = 1 << 4; - /// Domain is currently running - const RUNNING = 1 << 5; - /// Being debugged. - const DEBUGGED = 1 << 6; - /// domain is a xenstore domain - const XS_DOMAIN = 1 << 7; - /// domain has hardware assisted paging - const HAP = 1 << 8; - } -} - -bitflags! { - /// Content of the `emulation_flags` field of the domain creation hypercall. - #[repr(C)] - #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] - pub struct XenX86Emu: u32 { - /// Emulate Local APICs. - const Lapic = 1 << 0; - /// Emulate a HPET timer. - const Hpet = 1 << 1; - /// Emulate the ACPI PM timer. - const Pm = 1 << 2; - /// Emulate the RTC clock. - const Rtc = 1 << 3; - /// Emulate an IOAPIC device. - const Ioapic = 1 << 4; - /// Emulate PIC devices. - const Pic = 1 << 5; - /// Emulate standard VGA. - const Vga = 1 << 6; - /// Emulate an IOMMU. - const Iommu = 1 << 7; - /// Emulate a PIT timer. - const Pit = 1 << 8; - /// Route physical IRQs over event channels. - const UsePirq = 1 << 9; - /// Handle PCI configuration space traps from within Xen. - const Vpci = 1 << 10; - } -} - -bitflags! { - /// Contents of the `misc_flags` field of the domain creation hypercall - #[repr(C)] - #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] - pub struct XenX86Misc: u32 { - /// Grants access to the real physical MSR registers of the host. - const MsrRelaxed = 1 << 0; - } -} - -/// x86-specific domain settings. -#[repr(C)] -#[derive(Copy, Clone, Debug, Default)] -pub struct XenArchDomainconfig { - /// IN: Bitmap of devices to emulate. - pub emulation_flags: XenX86Emu, - /// IN: Miscellaneous x86-specific toggles. - pub misc_flags: XenX86Misc, -} - -#[repr(C)] -#[derive(Clone, Copy, Debug, Default)] -pub struct XenDomctlGetDomainInfo { - pub domain: DomId, - pub pad: u16, - pub flags: XenDomctlDominf, - pub tot_pages: Align64, - pub max_pages: Align64, - pub outstanding_pages: Align64, - pub shr_pages: Align64, - pub paged_pages: Align64, - /// GMFN of shared_info struct - pub shared_info_frame: Align64, - pub cpu_time: Align64, - /// Number of VCPUs currently online - pub nr_online_vcpus: u32, - /// Maximum VCPUID in use by this domain. - pub max_vcpu_id: u32, - pub ssidref: u32, - pub handle: Uuid, - pub cpupool: u32, - pub gpaddr_bits: u8, - pub pad2: [u8; 7], - pub arch_config: XenArchDomainconfig, -} - -#[repr(C)] -#[derive(Clone, Copy, Debug, Default)] -pub struct XenDomctlGetVCpuInfo { - // IN - pub vcpu: u32, - /// OUT: currently online (not hotplugged)? - pub online: u8, - /// OUT: blocked waiting for an event? - pub blocked: u8, - /// OUT: currently scheduled on its CPU? - pub running: u8, - /// OUT: total cpu time consumed (ns) - pub cpu_time: Align64, - /// OUT: current mapping - pub cpu: u32, -} - -#[repr(C)] -#[derive(Clone, Copy)] -union XenDomctlParam { - pub getdomaininfo: XenDomctlGetDomainInfo, - pub getvcpuinfo: XenDomctlGetVCpuInfo, - pub pad: [u8; 128], -} - -#[repr(C)] -#[derive(Clone, Copy)] -struct XenDomctl { - pub cmd: u32, - pub interface_version: u32, - pub domain: DomId, - pub pad: [u16; 3], - pub param: XenDomctlParam, -} - fn domctl_interface_version() -> u32 { match get_xen_abi() { crate::abi::XenAbi::Xen417 => 0x15, @@ -199,7 +64,7 @@ where getvcpuinfo: XenDomctlGetVCpuInfo::default(), }, }; - + domctl.param.getvcpuinfo.vcpu = vcpu; unsafe { @@ -216,4 +81,4 @@ where } } -impl DomctlGetVCpuInfo for T {} +impl DomctlGetVCpuInfo for T {} \ No newline at end of file diff --git a/external/xen/src/sysctl/ffi.rs b/external/xen/src/sysctl/ffi.rs new file mode 100644 index 0000000..4e57c98 --- /dev/null +++ b/external/xen/src/sysctl/ffi.rs @@ -0,0 +1,95 @@ +use crate::{domctl::XenDomctlGetDomainInfo, Align64, DomId}; + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct XenSysctlGetDomainInfoList { + /// IN + pub first_domain: DomId, + /// IN + pub max_domains: u32, + /// IN + pub buffer: Align64<*mut XenDomctlGetDomainInfo>, + /// OUT variables. + pub num_domains: u32, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct XenSysctlPhysInfo { + 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: Align64, + pub free_pages: Align64, + pub scrub_pages: Align64, + pub outstanding_pages: Align64, + pub max_mfn: Align64, + pub hw_cap: [u32; 8], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct XenSysctlCpuinfo { + pub idletime: Align64, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, Default)] +pub struct XenSysctlGetCpuInfo { + /// IN + pub max_cpus: u32, + /// IN + pub info: Align64<*mut XenSysctlCpuinfo>, + /// OUT + pub nr_cpus: u32, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union XenSysctlPmOpParam { + pub get_avgfreq: Align64, + _pad: [u8; 128], // Just to make sure we are large enough +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct XenSysctlPmOp { + pub cmd: u32, + pub cpuid: u32, + pub param: XenSysctlPmOpParam, +} + +// GET_CPUFREQ_AVGFREQ = CPUFREQ_PARA | 0x04 +pub const XEN_SYSCTL_PM_OP_CPUFREQ_AVG: u32 = 0x10 | 0x04; + +#[repr(C)] +#[derive(Clone, Copy)] +pub union XenSysctlParam { + pub getdomaininfolist: XenSysctlGetDomainInfoList, + pub physinfo: XenSysctlPhysInfo, + pub getcpuinfo: XenSysctlGetCpuInfo, + pub pm_op: XenSysctlPmOp, + _pad: [u8; 128], +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct XenSysctl { + pub cmd: u32, + pub interface_version: u32, + pub param: XenSysctlParam, +} + +pub const HYPERVISOR_SYSCTL: usize = 35; + +pub const XEN_SYSCTL_PHYSINFO: u32 = 3; +pub const XEN_SYSCTL_GETDOMAININFOLIST: u32 = 6; +pub const XEN_SYSCTL_GETCPUINFO: u32 = 8; +pub const XEN_SYSCTL_PM_OP: u32 = 12; diff --git a/external/xen/src/sysctl/mod.rs b/external/xen/src/sysctl/mod.rs index 7e57e39..c3f9389 100644 --- a/external/xen/src/sysctl/mod.rs +++ b/external/xen/src/sysctl/mod.rs @@ -1,3 +1,6 @@ +mod ffi; +pub use ffi::*; + use crate::{ abi::get_xen_abi, domctl::XenDomctlGetDomainInfo, @@ -5,74 +8,6 @@ use crate::{ Align64, DomId, }; -#[repr(C)] -#[derive(Clone, Copy, Debug, Default)] -struct XenSysctlGetDomainInfoList { - /// IN - pub first_domain: DomId, - /// IN - pub max_domains: u32, - /// IN - pub buffer: Align64<*mut XenDomctlGetDomainInfo>, - /// OUT variables. - pub num_domains: u32, -} - -#[repr(C)] -#[derive(Clone, Copy, Debug, Default)] -pub struct XenSysctlPhysInfo { - 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: Align64, - pub free_pages: Align64, - pub scrub_pages: Align64, - pub outstanding_pages: Align64, - pub max_mfn: Align64, - pub hw_cap: [u32; 8], -} - -#[repr(C)] -#[derive(Clone, Copy, Debug, Default)] -pub struct XenSysctlCpuinfo { - pub idletime: Align64, -} - -#[repr(C)] -#[derive(Clone, Copy, Debug, Default)] -pub struct XenSysctlGetCpuInfo { - /// IN - pub max_cpus: u32, - /// IN - pub info: Align64<*mut XenSysctlCpuinfo>, - /// OUT - pub nr_cpus: u32, -} - -#[repr(C)] -#[derive(Clone, Copy)] -union XenSysctlParam { - pub getdomaininfolist: XenSysctlGetDomainInfoList, - pub physinfo: XenSysctlPhysInfo, - pub getcpuinfo: XenSysctlGetCpuInfo, - pub pad: [u8; 128], -} - -#[repr(C)] -#[derive(Clone, Copy)] -struct XenSysctl { - pub cmd: u32, - pub interface_version: u32, - pub param: XenSysctlParam, -} - fn sysctl_interface_version() -> u32 { match get_xen_abi() { crate::abi::XenAbi::Xen417 => 0x15, @@ -80,12 +15,6 @@ fn sysctl_interface_version() -> u32 { } } -const HYPERVISOR_SYSCTL: usize = 35; - -const XEN_SYSCTL_PHYSINFO: u32 = 3; -const XEN_SYSCTL_GETDOMAININFOLIST: u32 = 6; -const XEN_SYSCTL_GETCPUINFO: u32 = 8; - pub trait SysctlGetDomainInfoList where Self: XenHypercall, @@ -254,3 +183,39 @@ where } impl SysctlGetCpuInfo for H {} + +pub trait SysctlGetPmOp +where + Self: XenHypercall, +{ + fn get_cpufreq_avgfreq(&self, cpuid: u32) -> anyhow::Result { + let mut sysctl = XenSysctl { + cmd: XEN_SYSCTL_PM_OP, + interface_version: sysctl_interface_version(), + param: XenSysctlParam { + pm_op: XenSysctlPmOp { + cmd: XEN_SYSCTL_PM_OP_CPUFREQ_AVG, + cpuid, + param: XenSysctlPmOpParam { + get_avgfreq: Align64(0), + }, + }, + }, + }; + + unsafe { + let mut sysctl_buffer = self.make_mut_buffer(&mut sysctl)?; + let res = self.hypercall1(HYPERVISOR_SYSCTL, sysctl_buffer.as_hypercall_ptr() as _); + + if res != 0 { + anyhow::bail!("sysctl_pm_op:cpufreq_avgfreq failed {}", res as isize) + } + + sysctl_buffer.update(); + drop(sysctl_buffer); + Ok(sysctl.param.pm_op.param.get_avgfreq.0) + } + } +} + +impl SysctlGetPmOp for H {} diff --git a/plugins/xcp-metrics-plugin-xen/src/plugin.rs b/plugins/xcp-metrics-plugin-xen/src/plugin.rs index d27fbea..bbd1df2 100644 --- a/plugins/xcp-metrics-plugin-xen/src/plugin.rs +++ b/plugins/xcp-metrics-plugin-xen/src/plugin.rs @@ -4,22 +4,26 @@ mod vcpu; use std::{collections::HashMap, os::unix::net::UnixStream, thread, time::Duration}; -use cpu::PCpuXenMetric; use enum_dispatch::enum_dispatch; -use memory::MemoryXenMetric; use smallvec::{smallvec, SmallVec}; use smol_str::{SmolStr, ToSmolStr}; use uuid::Uuid; -use vcpu::VCpuXenMetric; + use xcp_metrics_common::{ metrics::{Label, Metric}, protocol::{ProtocolMessage, RemoveMetric, UpdateMetric, XcpMetricsStream}, }; use xen::{ - domctl::XenDomctlGetDomainInfo, hypercall::unix::UnixXenHypercall, - sysctl::SysctlGetDomainInfoList, DomId, + domctl::XenDomctlGetDomainInfo, + hypercall::unix::UnixXenHypercall, + sysctl::{SysctlGetDomainInfoList, SysctlPhysInfo, XenSysctlPhysInfo}, + DomId, }; +use cpu::{PCpuFreq, PCpuUsage}; +use memory::DomainMemory; +use vcpu::VCpuUsage; + #[derive(Default)] struct PluginState { domid_metrics: HashMap>, @@ -38,6 +42,7 @@ pub(crate) trait XenMetric { fn read_host_metrics( &mut self, + _physinfo: XenSysctlPhysInfo, _hyp: &UnixXenHypercall, ) -> SmallVec<[(PluginMetricKind, Metric); 3]> { smallvec![] @@ -58,9 +63,10 @@ pub(crate) trait XenMetric { #[enum_dispatch(XenMetric)] pub(crate) enum XenMetricEnum { - Memory(MemoryXenMetric), - PCpu(PCpuXenMetric), - VCpu(VCpuXenMetric), + Memory(DomainMemory), + PCpu(PCpuUsage), + VCpu(VCpuUsage), + CpuFreq(PCpuFreq), } impl PluginState { @@ -121,9 +127,10 @@ impl PluginState { pub fn run_plugin(stream: &mut UnixStream, hyp: &UnixXenHypercall) -> anyhow::Result<()> { let mut state = PluginState::default(); let metrics: &mut [XenMetricEnum] = &mut [ - MemoryXenMetric.into(), - VCpuXenMetric::new().into(), - PCpuXenMetric::new(hyp).into(), + DomainMemory.into(), + VCpuUsage::new().into(), + PCpuUsage::new().into(), + PCpuFreq.into(), ]; for xen_metric in metrics.as_ref() { @@ -134,10 +141,14 @@ pub fn run_plugin(stream: &mut UnixStream, hyp: &UnixXenHypercall) -> anyhow::Re // Track what domains (still) exists. let mut found_domain = vec![0; 0]; + let physinfo = hyp + .physinfo() + .inspect_err(|e| tracing::error!("physinfo hypercall failure {e}"))?; + // Get host metrics for metric in metrics .iter_mut() - .map(|xen_metric| xen_metric.read_host_metrics(hyp)) + .map(|xen_metric| xen_metric.read_host_metrics(physinfo, hyp)) .flatten() { tracing::debug!("Pushing {metric:?}"); diff --git a/plugins/xcp-metrics-plugin-xen/src/plugin/cpu.rs b/plugins/xcp-metrics-plugin-xen/src/plugin/cpu.rs index d043415..1162fe6 100644 --- a/plugins/xcp-metrics-plugin-xen/src/plugin/cpu.rs +++ b/plugins/xcp-metrics-plugin-xen/src/plugin/cpu.rs @@ -2,34 +2,28 @@ use std::{iter, os::unix::net::UnixStream, time::Instant}; use smallvec::{smallvec, SmallVec}; use smol_str::ToSmolStr; + use xcp_metrics_common::{ metrics::{Label, Metric, MetricType, MetricValue, NumberValue}, protocol::{CreateFamily, ProtocolMessage, XcpMetricsStream}, }; use xen::{ - hypercall::{unix::UnixXenHypercall, XenHypercall}, - sysctl::{SysctlGetCpuInfo, SysctlPhysInfo, XenSysctlCpuinfo}, + hypercall::unix::UnixXenHypercall, + sysctl::{SysctlGetCpuInfo, SysctlGetPmOp, XenSysctlCpuinfo, XenSysctlPhysInfo}, }; use super::{PluginMetricKind, XenMetric}; -pub struct PCpuXenMetric { +// TODO: use a passed physinfo +pub struct PCpuUsage { latest_instant: Instant, - nr_cpus: usize, prev_pcpu_infos: Option>, } -impl PCpuXenMetric { - pub fn new(hyp: &impl XenHypercall) -> Self { - let nr_cpus = hyp - .physinfo() - .map(|physinfo| physinfo.max_cpu_id) - .unwrap_or_default() as usize - + 1; - +impl PCpuUsage { + pub fn new() -> Self { Self { latest_instant: Instant::now(), - nr_cpus, prev_pcpu_infos: None, } } @@ -64,7 +58,7 @@ fn generate_pcpu_usage( ) } -impl XenMetric for PCpuXenMetric { +impl XenMetric for PCpuUsage { fn make_families(&self, stream: &mut UnixStream) -> anyhow::Result<()> { stream.send_message(ProtocolMessage::CreateFamily(CreateFamily { help: "Time taken running a CPU core".into(), @@ -78,10 +72,11 @@ impl XenMetric for PCpuXenMetric { fn read_host_metrics( &mut self, + physinfo: XenSysctlPhysInfo, hyp: &UnixXenHypercall, ) -> SmallVec<[(PluginMetricKind, Metric); 3]> { let mut new_pcpu_infos: Vec = - vec![XenSysctlCpuinfo::default(); self.nr_cpus]; + vec![XenSysctlCpuinfo::default(); (physinfo.max_cpu_id + 1) as _]; match hyp.get_cpu_info(&mut new_pcpu_infos) { Ok(count) => new_pcpu_infos.truncate(count), @@ -107,3 +102,52 @@ impl XenMetric for PCpuXenMetric { metrics } } + +pub struct PCpuFreq; + +impl XenMetric for PCpuFreq { + fn make_families(&self, stream: &mut UnixStream) -> anyhow::Result<()> { + stream.send_message(ProtocolMessage::CreateFamily(CreateFamily { + help: "Average frequency of a CPU core".into(), + name: "xen_cpu_freq".into(), + metric_type: MetricType::Gauge, + unit: "hz".into(), + }))?; + + Ok(()) + } + + fn read_host_metrics( + &mut self, + physinfo: XenSysctlPhysInfo, + hyp: &UnixXenHypercall, + ) -> SmallVec<[(PluginMetricKind, Metric); 3]> { + (0..=physinfo.max_cpu_id) + .filter_map(|cpuid| { + // Ignore all failing reads. + hyp.get_cpufreq_avgfreq(cpuid) + .inspect_err(|e| { + tracing::error!("get_cpufreq_avg failure for cpuid:{cpuid}: {e}") + }) + .ok() + .map(|freq| (cpuid, freq)) + }) + .map(|(cpuid, freq)| { + ( + PluginMetricKind { + family_name: "xen_cpu_freq", + submetric: Some(cpuid.to_smolstr()), + }, + Metric { + labels: vec![Label { + name: "cpu_id".into(), + value: cpuid.to_smolstr(), + }] + .into_boxed_slice(), + value: MetricValue::Gauge(NumberValue::Int64(freq as i64)), + }, + ) + }) + .collect() + } +} diff --git a/plugins/xcp-metrics-plugin-xen/src/plugin/memory.rs b/plugins/xcp-metrics-plugin-xen/src/plugin/memory.rs index 1f7bd05..ee4dcf9 100644 --- a/plugins/xcp-metrics-plugin-xen/src/plugin/memory.rs +++ b/plugins/xcp-metrics-plugin-xen/src/plugin/memory.rs @@ -1,6 +1,7 @@ use std::os::unix::net::UnixStream; use smallvec::smallvec; + use xcp_metrics_common::{ metrics::{Metric, MetricType, MetricValue, NumberValue}, protocol::{CreateFamily, ProtocolMessage, XcpMetricsStream}, @@ -11,9 +12,9 @@ use super::{PluginMetricKind, XenMetric}; const PAGE_SIZE: u64 = 4096; -pub struct MemoryXenMetric; +pub struct DomainMemory; -impl XenMetric for MemoryXenMetric { +impl XenMetric for DomainMemory { fn make_families(&self, stream: &mut UnixStream) -> anyhow::Result<()> { stream.send_message(ProtocolMessage::CreateFamily(CreateFamily { help: "Memory reserved to a guest.".into(), diff --git a/plugins/xcp-metrics-plugin-xen/src/plugin/vcpu.rs b/plugins/xcp-metrics-plugin-xen/src/plugin/vcpu.rs index a75ffff..b253c04 100644 --- a/plugins/xcp-metrics-plugin-xen/src/plugin/vcpu.rs +++ b/plugins/xcp-metrics-plugin-xen/src/plugin/vcpu.rs @@ -2,6 +2,7 @@ use std::{collections::HashMap, iter, os::unix::net::UnixStream, time::Instant}; use smallvec::{smallvec, SmallVec}; use smol_str::ToSmolStr; + use xcp_metrics_common::{ metrics::{Label, Metric, MetricType, MetricValue, NumberValue}, protocol::{CreateFamily, ProtocolMessage, XcpMetricsStream}, @@ -9,18 +10,19 @@ use xcp_metrics_common::{ use xen::{ domctl::{DomctlGetVCpuInfo, XenDomctlGetDomainInfo, XenDomctlGetVCpuInfo}, hypercall::unix::UnixXenHypercall, + sysctl::XenSysctlPhysInfo, }; use super::{PluginMetricKind, XenMetric}; -pub struct VCpuXenMetric { +pub struct VCpuUsage { // We need to keep track of the previous instant as latest_instant is ~now. previous_instant: Option, latest_instant: Instant, prev_vcpu_infos: HashMap>, } -impl VCpuXenMetric { +impl VCpuUsage { pub fn new() -> Self { Self { previous_instant: None, @@ -59,7 +61,7 @@ fn generate_vcpu_usage( ) } -impl XenMetric for VCpuXenMetric { +impl XenMetric for VCpuUsage { fn make_families(&self, stream: &mut UnixStream) -> anyhow::Result<()> { stream.send_message(ProtocolMessage::CreateFamily(CreateFamily { help: "Time taken running a VCPU".into(), @@ -103,6 +105,7 @@ impl XenMetric for VCpuXenMetric { fn read_host_metrics( &mut self, + _physinfo: XenSysctlPhysInfo, _hyp: &UnixXenHypercall, ) -> SmallVec<[(PluginMetricKind, Metric); 3]> { self.previous_instant = Some(self.latest_instant);