1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//! Processor topology types.
pub mod aarch64;
pub mod x86;
cfg_if::cfg_if! {
if #[cfg(guest_arch = "aarch64")] {
pub use aarch64 as arch;
pub use aarch64::Aarch64Topology as TargetTopology;
pub use aarch64::Aarch64VpInfo as TargetVpInfo;
} else if #[cfg(guest_arch = "x86_64")] {
pub use x86 as arch;
pub use x86::X86Topology as TargetTopology;
pub use x86::X86VpInfo as TargetVpInfo;
} else {
compile_error!("Unsupported architecture");
}
}
use thiserror::Error;
/// A description of the VM's processor topology.
///
/// Currently this just tracks the APIC IDs for the processors.
///
/// Build one with [`TopologyBuilder`].
#[cfg_attr(
feature = "inspect",
derive(inspect::Inspect),
inspect(bound = "T: inspect::Inspect, T::ArchVpInfo: inspect::Inspect")
)]
#[derive(Debug, Clone)]
pub struct ProcessorTopology<T: ArchTopology = TargetTopology> {
#[cfg_attr(feature = "inspect", inspect(iter_by_index))]
vps: Vec<T::ArchVpInfo>,
smt_enabled: bool,
vps_per_socket: u32,
arch: T,
}
/// Architecture-specific topology types.
pub trait ArchTopology: Sized {
/// The architecture-specific VP info type.
type ArchVpInfo: Copy + AsRef<VpInfo>;
/// The architecture-specific [`TopologyBuilder`] generic.
type BuilderState;
/// Compute VP topology from a VP.
fn vp_topology(topology: &ProcessorTopology<Self>, info: &Self::ArchVpInfo) -> VpTopologyInfo;
}
/// A builder for [`ProcessorTopology`].
#[derive(Debug)]
pub struct TopologyBuilder<T: ArchTopology> {
vps_per_socket: u32,
smt_enabled: bool,
arch: T::BuilderState,
}
/// Error returned by [`TopologyBuilder::from_host_topology`].
#[derive(Debug, Error)]
pub enum HostTopologyError {
/// Could not find the host topology.
#[error("could not compute host topology via cpuid")]
NotFound,
/// The host topology has more than 2 threads per core.
#[error("unsupported thread-per-core count {0}")]
UnsupportedThreadsPerCore(u32),
}
/// Error when building a [`ProcessorTopology`].
#[derive(Debug, Error)]
pub enum InvalidTopology {
/// Failed to configure at least one VP.
#[error("must have at least one processor")]
NoVps,
/// Too many virtual processors.
#[error("too many processors requested: {requested}, max {max}")]
TooManyVps {
/// The number of processors requested.
requested: u32,
/// The maximum number of processors.
max: u32,
},
/// Not all processors will be addressable in XAPIC mode.
#[error("too many processors or too high an APIC ID {0} for xapic mode")]
ApicIdLimitExceeded(u32),
/// VpInfo indices must be linear and start at 0
#[error("vp indices don't start at 0 or don't count up")]
InvalidVpIndices,
/// Failed to query the topology information from Device Tree.
#[error("failed to query memory topology from device tree")]
StdIoError(#[source] std::io::Error),
}
impl<T: ArchTopology> TopologyBuilder<T> {
/// Sets the number of VPs per socket.
///
/// This does not need to be a power of 2, but it should be a multiple of 2
/// if SMT is enabled.
///
/// The number of VPs per socket will be rounded up to a power of 2 for
/// purposes of defining the x2APIC ID.
pub fn vps_per_socket(&mut self, count: u32) -> &mut Self {
self.vps_per_socket = count.clamp(1, 32768);
self
}
/// Sets whether SMT (hyperthreading) is enabled.
///
/// This is ignored if `vps_per_socket` is 1.
pub fn smt_enabled(&mut self, enabled: bool) -> &mut Self {
self.smt_enabled = enabled;
self
}
}
impl<
#[cfg(feature = "inspect")] T: ArchTopology + inspect::Inspect,
#[cfg(not(feature = "inspect"))] T: ArchTopology,
> ProcessorTopology<T>
{
/// Returns the number of VPs.
pub fn vp_count(&self) -> u32 {
self.vps.len() as u32
}
/// Returns information for the given processor by VP index.
///
/// Panics if the VP index is out of range.
pub fn vp(&self, vp_index: VpIndex) -> VpInfo {
*self.vps[vp_index.index() as usize].as_ref()
}
/// Returns information for the given processor by VP index, including
/// architecture-specific information.
///
/// Panics if the VP index is out of range.
pub fn vp_arch(&self, vp_index: VpIndex) -> T::ArchVpInfo {
self.vps[vp_index.index() as usize]
}
/// Returns an iterator over all VPs.
pub fn vps(&self) -> impl '_ + ExactSizeIterator<Item = VpInfo> + Clone {
self.vps.iter().map(|vp| *vp.as_ref())
}
/// Returns an iterator over all VPs, including architecture-specific information.
pub fn vps_arch(&self) -> impl '_ + ExactSizeIterator<Item = T::ArchVpInfo> + Clone {
self.vps.iter().copied()
}
/// Returns whether SMT (hyperthreading) is enabled.
pub fn smt_enabled(&self) -> bool {
self.smt_enabled
}
/// Returns the number of VPs per socket.
///
/// This will always be a power of 2. The number of VPs actually populated
/// in a socket may be smaller than this.
pub fn reserved_vps_per_socket(&self) -> u32 {
self.vps_per_socket.next_power_of_two()
}
/// Computes the processor topology information for a VP.
pub fn vp_topology(&self, vp_index: VpIndex) -> VpTopologyInfo {
T::vp_topology(self, &self.vp_arch(vp_index))
}
}
/// Per-processor topology information.
#[cfg_attr(feature = "inspect", derive(inspect::Inspect))]
#[derive(Debug, Copy, Clone)]
pub struct VpInfo {
/// The VP index of the processor.
pub vp_index: VpIndex,
/// The virtual NUMA node of the processor.
pub vnode: u32,
}
impl AsRef<VpInfo> for VpInfo {
fn as_ref(&self) -> &VpInfo {
self
}
}
impl VpInfo {
/// Returns true if this is the BSP.
pub fn is_bsp(&self) -> bool {
self.vp_index.is_bsp()
}
}
/// Topology information about a virtual processor.
pub struct VpTopologyInfo {
/// The socket index.
pub socket: u32,
/// The core index within the socket.
pub core: u32,
/// The thread index within the core.
pub thread: u32,
}
/// The virtual processor index.
///
/// This value is used inside the VMM to identify the processor. It is expected
/// to be used as an index into processor arrays, so it starts at zero and has
/// no gaps.
///
/// VP index zero is special in that it is always present and is always the BSP.
///
/// The same value is exposed to the guest operating system as the HV VP index,
/// via the Microsoft hypervisor guest interface. This constrains the HV VP
/// index to start at zero and have no gaps, which is not required by the
/// hypervisor interface, but it matches the behavior of Hyper-V and is not a
/// practical limitation.
///
/// This value is distinct from the APIC ID, although they are often the same
/// for all processors in small VMs and some in large VMs. Be careful not to use
/// them interchangeably.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "inspect", derive(inspect::Inspect), inspect(transparent))]
pub struct VpIndex(u32);
impl VpIndex {
/// Returns `index` as a VP index.
pub const fn new(index: u32) -> Self {
Self(index)
}
/// VP index zero, corresponding to the boot processor (BSP).
///
/// Note that this being a constant means that the BSP's HV VP index
/// observed by the guest will always be zero. This is consistent with
/// Hyper-V and is not a practical limitation.
///
/// Note that the APIC ID of the BSP might not be zero.
pub const BSP: Self = Self::new(0);
/// Returns the VP index value.
pub fn index(&self) -> u32 {
self.0
}
/// Returns true if this is the index of the BSP (0).
pub fn is_bsp(&self) -> bool {
*self == Self::BSP
}
}