Skip to content

Commit 98592f7

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 98592f7

File tree

6 files changed

+92
-48
lines changed

6 files changed

+92
-48
lines changed

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

+26-21
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(())
@@ -233,20 +234,21 @@ pub fn set_mpstate(vcpufd: &VcpuFd, state: kvm_mp_state) -> Result<(), VcpuError
233234
#[cfg(test)]
234235
mod tests {
235236
#![allow(clippy::undocumented_unsafe_blocks)]
236-
use kvm_ioctls::Kvm;
237237

238238
use super::*;
239239
use crate::arch::aarch64::layout;
240240
use crate::test_utils::arch_mem;
241+
use crate::vstate::kvm::Kvm;
241242

242243
#[test]
243244
fn test_setup_regs() {
244-
let kvm = Kvm::new().unwrap();
245-
let vm = kvm.create_vm().unwrap();
245+
let kvm = Kvm::new(vec![]).unwrap();
246+
let vm = kvm.fd.create_vm().unwrap();
246247
let vcpu = vm.create_vcpu(0).unwrap();
247248
let mem = arch_mem(layout::FDT_MAX_SIZE + 0x1000);
249+
let optional_capabilities = kvm.optional_capabilities();
248250

249-
let res = setup_boot_regs(&vcpu, 0, 0x0, &mem);
251+
let res = setup_boot_regs(&vcpu, 0, 0x0, &mem, &optional_capabilities);
250252
assert!(matches!(
251253
res.unwrap_err(),
252254
VcpuError::SetOneReg(0x6030000000100042, _)
@@ -256,14 +258,17 @@ mod tests {
256258
vm.get_preferred_target(&mut kvi).unwrap();
257259
vcpu.vcpu_init(&kvi).unwrap();
258260

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

261263
// Check that the register is reset on compatible kernels.
262264
// Because there is a delta in time between we reset the register and time we
263265
// read it, we cannot compare with 0. Instead we read CNTVNT_EL0 (Virtual counter)
264266
// 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() {
267+
if optional_capabilities.counter_offset {
268+
let mut reg_bytes = [0_u8; 8];
269+
vcpu.get_one_reg(SYS_CNTPCT_EL0, &mut reg_bytes).unwrap();
270+
let counter_value = u64::from_le_bytes(reg_bytes);
271+
267272
let virt_count = unsafe {
268273
// Rust is so smart, it cannot figure out that `virt_count`
269274
// is read later in the assert!, so it complains about unused assignment.
@@ -276,15 +281,15 @@ mod tests {
276281
);
277282
virt_count
278283
};
279-
let counter_value = u64::from_le_bytes(reg_bytes);
284+
280285
assert!(counter_value < virt_count);
281286
}
282287
}
283288

284289
#[test]
285290
fn test_read_mpidr() {
286-
let kvm = Kvm::new().unwrap();
287-
let vm = kvm.create_vm().unwrap();
291+
let kvm = Kvm::new(vec![]).unwrap();
292+
let vm = kvm.fd.create_vm().unwrap();
288293
let vcpu = vm.create_vcpu(0).unwrap();
289294
let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default();
290295
vm.get_preferred_target(&mut kvi).unwrap();
@@ -302,8 +307,8 @@ mod tests {
302307

303308
#[test]
304309
fn test_get_set_regs() {
305-
let kvm = Kvm::new().unwrap();
306-
let vm = kvm.create_vm().unwrap();
310+
let kvm = Kvm::new(vec![]).unwrap();
311+
let vm = kvm.fd.create_vm().unwrap();
307312
let vcpu = vm.create_vcpu(0).unwrap();
308313
let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default();
309314
vm.get_preferred_target(&mut kvi).unwrap();
@@ -324,8 +329,8 @@ mod tests {
324329
fn test_mpstate() {
325330
use std::os::unix::io::AsRawFd;
326331

327-
let kvm = Kvm::new().unwrap();
328-
let vm = kvm.create_vm().unwrap();
332+
let kvm = Kvm::new(vec![]).unwrap();
333+
let vm = kvm.fd.create_vm().unwrap();
329334
let vcpu = vm.create_vcpu(0).unwrap();
330335
let mut kvi: kvm_bindings::kvm_vcpu_init = kvm_bindings::kvm_vcpu_init::default();
331336
vm.get_preferred_target(&mut kvi).unwrap();

src/vmm/src/builder.rs

+21-8
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

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use kvm_bindings::{CpuId, MsrList, KVM_API_VERSION, KVM_MAX_CPUID_ENTRIES};
1+
use kvm_bindings::KVM_API_VERSION;
2+
#[cfg(target_arch = "x86_64")]
3+
use kvm_bindings::{CpuId, MsrList, KVM_MAX_CPUID_ENTRIES};
24
use kvm_ioctls::Kvm as KvmFd;
35
use serde::{Deserialize, Serialize};
46

@@ -17,8 +19,10 @@ pub enum KvmError {
1719
/** Error creating KVM object: {0} Make sure the user launching the firecracker process is \
1820
configured on the /dev/kvm file's ACL. */
1921
Kvm(kvm_ioctls::Error),
22+
#[cfg(target_arch = "x86_64")]
2023
/// Failed to get MSR index list to save into snapshots: {0}
2124
GetMsrsToSave(crate::arch::x86_64::msr::MsrError),
25+
#[cfg(target_arch = "x86_64")]
2226
/// Failed to get supported cpuid: {0}
2327
GetSupportedCpuId(kvm_ioctls::Error),
2428
/// The number of configured slots is bigger than the maximum reported by KVM
@@ -127,7 +131,13 @@ impl Kvm {
127131
Ok(())
128132
}
129133
}
130-
134+
#[cfg(target_arch = "aarch64")]
135+
/// Optional capabilities.
136+
#[derive(Debug, Default)]
137+
pub struct OptionalCapabilities {
138+
// KVM_CAP_COUNTER_OFFSET
139+
pub counter_offset: bool,
140+
}
131141
#[cfg(target_arch = "aarch64")]
132142
impl Kvm {
133143
const DEFAULT_CAPABILITIES: [u32; 7] = [
@@ -140,6 +150,16 @@ impl Kvm {
140150
kvm_bindings::KVM_CAP_ONE_REG,
141151
];
142152

153+
/// Returns struct with optional capabilities statuses.
154+
pub fn optional_capabilities(&self) -> OptionalCapabilities {
155+
OptionalCapabilities {
156+
counter_offset: self
157+
.fd
158+
.check_extension_raw(kvm_bindings::KVM_CAP_COUNTER_OFFSET.into())
159+
!= 0,
160+
}
161+
}
162+
143163
/// Saves and returns the Kvm Vm state.
144164
pub fn save_state(&self) -> KvmState {
145165
KvmState {

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

+22-16
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::{Kvm, OptionalCapabilities};
2526
use crate::vstate::memory::{Address, GuestAddress, GuestMemoryMmap};
2627
use crate::vstate::vcpu::VcpuEmulation;
2728
use crate::vstate::vm::Vm;
@@ -77,7 +78,7 @@ impl KvmVcpu {
7778
///
7879
/// * `index` - Represents the 0-based CPU index between [0, max vcpus).
7980
/// * `vm` - The vm to which this vcpu will get attached.
80-
pub fn new(index: u8, vm: &Vm) -> Result<Self, KvmVcpuError> {
81+
pub fn new(index: u8, vm: &Vm, _: &Kvm) -> Result<Self, KvmVcpuError> {
8182
let kvm_vcpu = vm
8283
.fd()
8384
.create_vcpu(index.into())
@@ -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

@@ -312,7 +315,7 @@ mod tests {
312315

313316
fn setup_vcpu(mem_size: usize) -> (Kvm, Vm, KvmVcpu, GuestMemoryMmap) {
314317
let (kvm, mut vm, vm_mem) = setup_vm_with_memory(mem_size);
315-
let mut vcpu = KvmVcpu::new(0, &vm).unwrap();
318+
let mut vcpu = KvmVcpu::new(0, &vm, &kvm).unwrap();
316319
vcpu.init(&[]).unwrap();
317320
vm.setup_irqchip(1).unwrap();
318321

@@ -321,11 +324,11 @@ mod tests {
321324

322325
#[test]
323326
fn test_create_vcpu() {
324-
let (_, vm, _) = setup_vm_with_memory(0x1000);
327+
let (kvm, vm, _) = setup_vm_with_memory(0x1000);
325328

326329
unsafe { libc::close(vm.fd().as_raw_fd()) };
327330

328-
let err = KvmVcpu::new(0, &vm);
331+
let err = KvmVcpu::new(0, &vm, &kvm);
329332
assert_eq!(
330333
err.err().unwrap().to_string(),
331334
"Error creating vcpu: Bad file descriptor (os error 9)".to_string()
@@ -337,7 +340,8 @@ mod tests {
337340

338341
#[test]
339342
fn test_configure_vcpu() {
340-
let (_, _, mut vcpu, vm_mem) = setup_vcpu(0x10000);
343+
let (kvm, _, mut vcpu, vm_mem) = setup_vcpu(0x10000);
344+
let optional_capabilities = kvm.optional_capabilities();
341345

342346
let vcpu_config = VcpuConfig {
343347
vcpu_count: 1,
@@ -348,6 +352,7 @@ mod tests {
348352
&vm_mem,
349353
GuestAddress(crate::arch::get_kernel_start()),
350354
&vcpu_config,
355+
&optional_capabilities,
351356
)
352357
.unwrap();
353358

@@ -357,6 +362,7 @@ mod tests {
357362
&vm_mem,
358363
GuestAddress(crate::arch::get_kernel_start()),
359364
&vcpu_config,
365+
&optional_capabilities,
360366
);
361367
assert_eq!(
362368
err.unwrap_err(),
@@ -372,8 +378,8 @@ mod tests {
372378

373379
#[test]
374380
fn test_init_vcpu() {
375-
let (_, mut vm, _) = setup_vm_with_memory(0x1000);
376-
let mut vcpu = KvmVcpu::new(0, &vm).unwrap();
381+
let (kvm, mut vm, _) = setup_vm_with_memory(0x1000);
382+
let mut vcpu = KvmVcpu::new(0, &vm, &kvm).unwrap();
377383
vm.setup_irqchip(1).unwrap();
378384

379385
// KVM_ARM_VCPU_PSCI_0_2 is set by default.
@@ -391,8 +397,8 @@ mod tests {
391397

392398
#[test]
393399
fn test_vcpu_save_restore_state() {
394-
let (_, mut vm, _) = setup_vm_with_memory(0x1000);
395-
let mut vcpu = KvmVcpu::new(0, &vm).unwrap();
400+
let (kvm, mut vm, _) = setup_vm_with_memory(0x1000);
401+
let mut vcpu = KvmVcpu::new(0, &vm, &kvm).unwrap();
396402
vm.setup_irqchip(1).unwrap();
397403

398404
// Calling KVM_GET_REGLIST before KVM_VCPU_INIT will result in error.
@@ -435,8 +441,8 @@ mod tests {
435441
//
436442
// This should fail with ENOEXEC.
437443
// https://elixir.bootlin.com/linux/v5.10.176/source/arch/arm64/kvm/arm.c#L1165
438-
let (_, mut vm, _) = setup_vm_with_memory(0x1000);
439-
let vcpu = KvmVcpu::new(0, &vm).unwrap();
444+
let (kvm, mut vm, _) = setup_vm_with_memory(0x1000);
445+
let vcpu = KvmVcpu::new(0, &vm, &kvm).unwrap();
440446
vm.setup_irqchip(1).unwrap();
441447

442448
vcpu.dump_cpu_config().unwrap_err();
@@ -445,8 +451,8 @@ mod tests {
445451
#[test]
446452
fn test_dump_cpu_config_after_init() {
447453
// Test `dump_cpu_config()` after `KVM_VCPU_INIT`.
448-
let (_, mut vm, _) = setup_vm_with_memory(0x1000);
449-
let mut vcpu = KvmVcpu::new(0, &vm).unwrap();
454+
let (kvm, mut vm, _) = setup_vm_with_memory(0x1000);
455+
let mut vcpu = KvmVcpu::new(0, &vm, &kvm).unwrap();
450456
vm.setup_irqchip(1).unwrap();
451457
vcpu.init(&[]).unwrap();
452458

@@ -455,10 +461,10 @@ mod tests {
455461

456462
#[test]
457463
fn test_setup_non_boot_vcpu() {
458-
let (_, vm, _) = setup_vm_with_memory(0x1000);
459-
let mut vcpu1 = KvmVcpu::new(0, &vm).unwrap();
464+
let (kvm, vm, _) = setup_vm_with_memory(0x1000);
465+
let mut vcpu1 = KvmVcpu::new(0, &vm, &kvm).unwrap();
460466
vcpu1.init(&[]).unwrap();
461-
let mut vcpu2 = KvmVcpu::new(1, &vm).unwrap();
467+
let mut vcpu2 = KvmVcpu::new(1, &vm, &kvm).unwrap();
462468
vcpu2.init(&[]).unwrap();
463469
}
464470

src/vmm/src/vstate/vcpu/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1008,6 +1008,7 @@ pub(crate) mod tests {
10081008
smt: false,
10091009
cpu_config: crate::cpu_config::aarch64::CpuConfiguration::default(),
10101010
},
1011+
&kvm.optional_capabilities(),
10111012
)
10121013
.expect("failed to configure vcpu");
10131014

src/vmm/src/vstate/vm.rs

-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,6 @@ impl Vm {
204204
.get_irqchip()
205205
.save_device(mpidrs)
206206
.map_err(VmError::SaveGic)?,
207-
kvm_cap_modifiers: self.kvm_cap_modifiers.clone(),
208207
})
209208
}
210209

0 commit comments

Comments
 (0)