Skip to content

Commit 25cadd5

Browse files
committed
feat: only reset SYS_CNTPCT_EL0 if KVM_CAP_COUNTER_OFFSET is present
Now when KVM is a separate object, we can query optional capabilities when configuring vcpus on aarch64 and only reset the SYS_CNTPCT_EL0 if KVM_CAP_COUNTER_OFFSET is present. Signed-off-by: Egor Lazarchuk <[email protected]>
1 parent dbf8b68 commit 25cadd5

File tree

4 files changed

+53
-21
lines changed

4 files changed

+53
-21
lines changed

src/vmm/src/arch/aarch64/vcpu.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use log::warn;
1414

1515
use super::get_fdt_addr;
1616
use super::regs::*;
17+
use crate::vstate::kvm::OptionalCapabilities;
1718
use crate::vstate::memory::GuestMemoryMmap;
1819

1920
/// Errors thrown while setting aarch64 registers.
@@ -79,6 +80,7 @@ pub fn setup_boot_regs(
7980
cpu_id: u8,
8081
boot_ip: u64,
8182
mem: &GuestMemoryMmap,
83+
optional_capabilities: &OptionalCapabilities,
8284
) -> Result<(), VcpuError> {
8385
let kreg_off = offset_of!(kvm_regs, regs);
8486

@@ -113,17 +115,16 @@ pub fn setup_boot_regs(
113115
// Resetting KVM_REG_ARM_PTIMER_CNT for single vcpu is enough because there is only
114116
// one timer struct with offsets per VM.
115117
// Because the access to KVM_REG_ARM_PTIMER_CNT is only present starting 6.4 kernel,
116-
// we don't fail if ioctl returns an error.
118+
// we only do the reset if KVM_CAP_COUNTER_OFFSET is present as it was added
119+
// in the same patch series as the ability to set the KVM_REG_ARM_PTIMER_CNT register.
117120
// Path series which introduced the needed changes:
118121
// https://lore.kernel.org/all/[email protected]/
119122
// Note: the value observed by the guest will still be above 0, because there is a delta
120123
// time between this resetting and first call to KVM_RUN.
121-
let zero: u64 = 0;
122-
if vcpufd
123-
.set_one_reg(KVM_REG_ARM_PTIMER_CNT, &zero.to_le_bytes())
124-
.is_err()
125-
{
126-
warn!("Unable to reset VM physical counter. VM will use host value instead.");
124+
if optional_capabilities.counter_offset {
125+
vcpufd
126+
.set_one_reg(KVM_REG_ARM_PTIMER_CNT, &[0; 8])
127+
.map_err(|err| VcpuError::SetOneReg(id, err))?;
127128
}
128129
}
129130
Ok(())
@@ -246,7 +247,9 @@ mod tests {
246247
let vcpu = vm.create_vcpu(0).unwrap();
247248
let mem = arch_mem(layout::FDT_MAX_SIZE + 0x1000);
248249

249-
let res = setup_boot_regs(&vcpu, 0, 0x0, &mem);
250+
let optional_capabilities = kvm.optional_capabilities();
251+
252+
let res = setup_boot_regs(&vcpu, 0, 0x0, &mem, &optional_capabilities);
250253
assert!(matches!(
251254
res.unwrap_err(),
252255
VcpuError::SetOneReg(0x6030000000100042, _)
@@ -256,14 +259,17 @@ mod tests {
256259
vm.get_preferred_target(&mut kvi).unwrap();
257260
vcpu.vcpu_init(&kvi).unwrap();
258261

259-
setup_boot_regs(&vcpu, 0, 0x0, &mem).unwrap();
262+
setup_boot_regs(&vcpu, 0, 0x0, &mem, &optional_capabilities).unwrap();
260263

261264
// Check that the register is reset on compatible kernels.
262265
// Because there is a delta in time between we reset the register and time we
263266
// read it, we cannot compare with 0. Instead we read CNTVNT_EL0 (Virtual counter)
264267
// from the host. The host value should be much bigger than the VM physical counter.
265-
let mut reg_bytes = [0_u8; 8];
266-
if vcpu.get_one_reg(SYS_CNTPCT_EL0, &mut reg_bytes).is_ok() {
268+
if optional_capabilities.counter_offset {
269+
let mut reg_bytes = [0_u8; 8];
270+
vcpu.get_one_reg(SYS_CNTPCT_EL0, &mut reg_bytes).unwrap();
271+
let counter_value = u64::from_le_bytes(reg_bytes);
272+
267273
let virt_count = unsafe {
268274
// Rust is so smart, it cannot figure out that `virt_count`
269275
// is read later in the assert!, so it complains about unused assignment.
@@ -276,7 +282,7 @@ mod tests {
276282
);
277283
virt_count
278284
};
279-
let counter_value = u64::from_le_bytes(reg_bytes);
285+
280286
assert!(counter_value < virt_count);
281287
}
282288
}

src/vmm/src/builder.rs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -811,16 +811,16 @@ pub fn configure_system_for_boot(
811811
cpu_config,
812812
};
813813

814-
// Configure vCPUs with normalizing and setting the generated CPU configuration.
815-
for vcpu in vcpus.iter_mut() {
816-
vcpu.kvm_vcpu
817-
.configure(vmm.guest_memory(), entry_addr, &vcpu_config)
818-
.map_err(VmmError::VcpuConfigure)
819-
.map_err(Internal)?;
820-
}
821-
822814
#[cfg(target_arch = "x86_64")]
823815
{
816+
// Configure vCPUs with normalizing and setting the generated CPU configuration.
817+
for vcpu in vcpus.iter_mut() {
818+
vcpu.kvm_vcpu
819+
.configure(vmm.guest_memory(), entry_addr, &vcpu_config)
820+
.map_err(VmmError::VcpuConfigure)
821+
.map_err(Internal)?;
822+
}
823+
824824
// Write the kernel command line to guest memory. This is x86_64 specific, since on
825825
// aarch64 the command line will be specified through the FDT.
826826
let cmdline_size = boot_cmdline
@@ -855,6 +855,19 @@ pub fn configure_system_for_boot(
855855
}
856856
#[cfg(target_arch = "aarch64")]
857857
{
858+
let optional_capabilities = vmm.kvm.optional_capabilities();
859+
// Configure vCPUs with normalizing and setting the generated CPU configuration.
860+
for vcpu in vcpus.iter_mut() {
861+
vcpu.kvm_vcpu
862+
.configure(
863+
vmm.guest_memory(),
864+
entry_addr,
865+
&vcpu_config,
866+
&optional_capabilities,
867+
)
868+
.map_err(VmmError::VcpuConfigure)
869+
.map_err(Internal)?;
870+
}
858871
let vcpu_mpidr = vcpus
859872
.iter_mut()
860873
.map(|cpu| cpu.kvm_vcpu.get_mpidr())

src/vmm/src/vstate/kvm.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,11 @@ impl Kvm {
127127
Ok(())
128128
}
129129
}
130-
130+
#[cfg(target_arch = "aarch64")]
131+
#[derive(Default)]
132+
pub struct OptionalCapabilities {
133+
pub counter_offset: bool,
134+
}
131135
#[cfg(target_arch = "aarch64")]
132136
impl Kvm {
133137
const DEFAULT_CAPABILITIES: [u32; 7] = [
@@ -140,6 +144,12 @@ impl Kvm {
140144
kvm_bindings::KVM_CAP_ONE_REG,
141145
];
142146

147+
pub fn optional_capabilities(&self) -> OptionalCapabilities {
148+
OptionalCapabilities {
149+
counter_offset: self.fd.check_extension(kvm_bindings::KVM_CAP_COUNTER_OFFSET) != 0,
150+
}
151+
}
152+
143153
/// Saves and returns the Kvm Vm state.
144154
pub fn save_state(&self) -> KvmState {
145155
KvmState {

src/vmm/src/vstate/vcpu/aarch64.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use crate::cpu_config::aarch64::custom_cpu_template::VcpuFeatures;
2222
use crate::cpu_config::templates::CpuConfiguration;
2323
use crate::logger::{error, IncMetric, METRICS};
2424
use crate::vcpu::{VcpuConfig, VcpuError};
25+
use crate::vstate::kvm::OptionalCapabilities;
2526
use crate::vstate::memory::{Address, GuestAddress, GuestMemoryMmap};
2627
use crate::vstate::vcpu::VcpuEmulation;
2728
use crate::vstate::vm::Vm;
@@ -115,6 +116,7 @@ impl KvmVcpu {
115116
guest_mem: &GuestMemoryMmap,
116117
kernel_load_addr: GuestAddress,
117118
vcpu_config: &VcpuConfig,
119+
optional_capabilities: &OptionalCapabilities,
118120
) -> Result<(), KvmVcpuError> {
119121
for reg in vcpu_config.cpu_config.regs.iter() {
120122
self.fd
@@ -127,6 +129,7 @@ impl KvmVcpu {
127129
self.index,
128130
kernel_load_addr.raw_value(),
129131
guest_mem,
132+
optional_capabilities,
130133
)
131134
.map_err(KvmVcpuError::ConfigureRegisters)?;
132135

0 commit comments

Comments
 (0)