Skip to content

Commit ce8c6dc

Browse files
committed
refactor: Move VcpuFd out of KvmVcpu
This separate emulation state from "live" data (aka the KVM file descriptor). This is a first step towards removing the need for mocking the `.emulate()` call in unit tests for KVM_EXIT handling. Signed-off-by: Patrick Roy <[email protected]>
1 parent fd40204 commit ce8c6dc

File tree

4 files changed

+163
-157
lines changed

4 files changed

+163
-157
lines changed

src/vmm/src/builder.rs

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -487,9 +487,9 @@ pub fn build_microvm_from_snapshot(
487487
if let Some(state_tsc) = microvm_state.vcpu_states[0].tsc_khz {
488488
// Scale the TSC frequency for all VCPUs. If a TSC frequency is not specified in the
489489
// snapshot, by default it uses the host frequency.
490-
if vcpus[0].kvm_vcpu.is_tsc_scaling_required(state_tsc)? {
490+
if vcpus[0].is_tsc_scaling_required(state_tsc)? {
491491
for vcpu in &vcpus {
492-
vcpu.kvm_vcpu.set_tsc_khz(state_tsc)?;
492+
vcpu.set_tsc_khz(state_tsc)?;
493493
}
494494
}
495495
}
@@ -499,8 +499,7 @@ pub fn build_microvm_from_snapshot(
499499
#[cfg(target_arch = "aarch64")]
500500
{
501501
for (vcpu, state) in vcpus.iter_mut().zip(microvm_state.vcpu_states.iter()) {
502-
vcpu.kvm_vcpu
503-
.restore_state(vmm.vm.fd(), state)
502+
vcpu.restore_state(vmm.vm.fd(), state)
504503
.map_err(VcpuError::VcpuResponse)
505504
.map_err(BuildMicrovmFromSnapshotError::RestoreVcpus)?;
506505
}
@@ -511,8 +510,7 @@ pub fn build_microvm_from_snapshot(
511510

512511
#[cfg(target_arch = "x86_64")]
513512
for (vcpu, state) in vcpus.iter_mut().zip(microvm_state.vcpu_states.iter()) {
514-
vcpu.kvm_vcpu
515-
.restore_state(state)
513+
vcpu.restore_state(state)
516514
.map_err(VcpuError::VcpuResponse)
517515
.map_err(BuildMicrovmFromSnapshotError::RestoreVcpus)?;
518516
}
@@ -779,7 +777,6 @@ pub fn configure_system_for_boot(
779777
.map_err(GuestConfigError::CpuidFromKvmCpuid)?;
780778
let msr_index_list = cpu_template.get_msr_index_list();
781779
let msrs = vcpus[0]
782-
.kvm_vcpu
783780
.get_msrs(&msr_index_list)
784781
.map_err(GuestConfigError::VcpuIoctl)?;
785782
CpuConfiguration { cpuid, msrs }
@@ -791,14 +788,13 @@ pub fn configure_system_for_boot(
791788
use crate::arch::aarch64::vcpu::get_registers;
792789

793790
for vcpu in vcpus.iter_mut() {
794-
vcpu.kvm_vcpu
795-
.init(vmm.vm.fd(), &cpu_template.vcpu_features)
791+
vcpu.init(vmm.vm.fd(), &cpu_template.vcpu_features)
796792
.map_err(VmmError::VcpuInit)
797793
.map_err(Internal)?;
798794
}
799795

800796
let mut regs = Aarch64RegisterVec::default();
801-
get_registers(&vcpus[0].kvm_vcpu.fd, &cpu_template.reg_list(), &mut regs)
797+
get_registers(&vcpus[0].fd, &cpu_template.reg_list(), &mut regs)
802798
.map_err(GuestConfigError)?;
803799
CpuConfiguration { regs }
804800
};
@@ -814,8 +810,7 @@ pub fn configure_system_for_boot(
814810

815811
// Configure vCPUs with normalizing and setting the generated CPU configuration.
816812
for vcpu in vcpus.iter_mut() {
817-
vcpu.kvm_vcpu
818-
.configure(vmm.guest_memory(), entry_addr, &vcpu_config)
813+
vcpu.configure(vmm.guest_memory(), entry_addr, &vcpu_config)
819814
.map_err(VmmError::VcpuConfigure)
820815
.map_err(Internal)?;
821816
}

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

Lines changed: 39 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use crate::vcpu::{VcpuConfig, VcpuError};
2222
use crate::vstate::memory::{Address, GuestAddress, GuestMemoryMmap};
2323
use crate::vstate::vcpu::VcpuEmulation;
2424
use crate::vstate::vm::Vm;
25+
use crate::Vcpu;
2526

2627
/// Errors associated with the wrappers over KVM ioctls.
2728
#[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)]
@@ -52,8 +53,6 @@ pub type KvmVcpuConfigureError = KvmVcpuError;
5253
pub struct KvmVcpu {
5354
/// Index of vcpu.
5455
pub index: u8,
55-
/// KVM vcpu fd.
56-
pub fd: VcpuFd,
5756
/// Mmio bus.
5857
pub mmio_bus: Option<crate::devices::Bus>,
5958
mpidr: u64,
@@ -67,26 +66,33 @@ impl KvmVcpu {
6766
///
6867
/// * `index` - Represents the 0-based CPU index between [0, max vcpus).
6968
/// * `vm` - The vm to which this vcpu will get attached.
70-
pub fn new(index: u8, vm: &Vm) -> Result<Self, KvmVcpuError> {
71-
let kvm_vcpu = vm
72-
.fd()
73-
.create_vcpu(index.into())
74-
.map_err(KvmVcpuError::CreateVcpu)?;
75-
76-
Ok(KvmVcpu {
69+
pub fn new(index: u8, _vm: &Vm) -> Self {
70+
KvmVcpu {
7771
index,
78-
fd: kvm_vcpu,
7972
mmio_bus: None,
8073
mpidr: 0,
8174
kvi: None,
82-
})
75+
}
8376
}
8477

8578
/// Gets the MPIDR register value.
8679
pub fn get_mpidr(&self) -> u64 {
8780
self.mpidr
8881
}
8982

83+
/// Runs the vCPU in KVM context and handles the kvm exit reason.
84+
///
85+
/// Returns error or enum specifying whether emulation was handled or interrupted.
86+
pub fn run_arch_emulation(&self, exit: VcpuExit) -> Result<VcpuEmulation, VcpuError> {
87+
METRICS.vcpu.failures.inc();
88+
// TODO: Are we sure we want to finish running a vcpu upon
89+
// receiving a vm exit that is not necessarily an error?
90+
error!("Unexpected exit reason on vcpu run: {:?}", exit);
91+
Err(VcpuError::UnhandledKvmExit(format!("{:?}", exit)))
92+
}
93+
}
94+
95+
impl Vcpu {
9096
/// Configures an aarch64 specific vcpu for booting Linux.
9197
///
9298
/// # Arguments
@@ -108,13 +114,13 @@ impl KvmVcpu {
108114

109115
setup_boot_regs(
110116
&self.fd,
111-
self.index,
117+
self.kvm_vcpu.index,
112118
kernel_load_addr.raw_value(),
113119
guest_mem,
114120
)
115121
.map_err(KvmVcpuError::ConfigureRegisters)?;
116122

117-
self.mpidr = get_mpidr(&self.fd).map_err(KvmVcpuError::ConfigureRegisters)?;
123+
self.kvm_vcpu.mpidr = get_mpidr(&self.fd).map_err(KvmVcpuError::ConfigureRegisters)?;
118124

119125
Ok(())
120126
}
@@ -129,7 +135,7 @@ impl KvmVcpu {
129135
vm_fd: &VmFd,
130136
vcpu_features: &[VcpuFeatures],
131137
) -> Result<(), KvmVcpuError> {
132-
let mut kvi = Self::default_kvi(vm_fd, self.index)?;
138+
let mut kvi = Self::default_kvi(vm_fd, self.kvm_vcpu.index)?;
133139

134140
for feature in vcpu_features.iter() {
135141
let index = feature.index as usize;
@@ -139,7 +145,7 @@ impl KvmVcpu {
139145
self.init_vcpu(&kvi)?;
140146
self.finalize_vcpu(&kvi)?;
141147

142-
self.kvi = if !vcpu_features.is_empty() {
148+
self.kvm_vcpu.kvi = if !vcpu_features.is_empty() {
143149
Some(kvi)
144150
} else {
145151
None
@@ -177,17 +183,17 @@ impl KvmVcpu {
177183
};
178184
get_all_registers(&self.fd, &mut state.regs).map_err(KvmVcpuError::SaveState)?;
179185
state.mpidr = get_mpidr(&self.fd).map_err(KvmVcpuError::SaveState)?;
180-
state.kvi = self.kvi;
186+
state.kvi = self.kvm_vcpu.kvi;
181187
Ok(state)
182188
}
183189

184190
/// Use provided state to populate KVM internal state.
185191
pub fn restore_state(&mut self, vm_fd: &VmFd, state: &VcpuState) -> Result<(), KvmVcpuError> {
186192
let kvi = match state.kvi {
187193
Some(kvi) => kvi,
188-
None => Self::default_kvi(vm_fd, self.index)?,
194+
None => Self::default_kvi(vm_fd, self.kvm_vcpu.index)?,
189195
};
190-
self.kvi = state.kvi;
196+
self.kvm_vcpu.kvi = state.kvi;
191197

192198
self.init_vcpu(&kvi)?;
193199

@@ -226,17 +232,6 @@ impl KvmVcpu {
226232
Ok(CpuConfiguration { regs })
227233
}
228234

229-
/// Runs the vCPU in KVM context and handles the kvm exit reason.
230-
///
231-
/// Returns error or enum specifying whether emulation was handled or interrupted.
232-
pub fn run_arch_emulation(&self, exit: VcpuExit) -> Result<VcpuEmulation, VcpuError> {
233-
METRICS.vcpu.failures.inc();
234-
// TODO: Are we sure we want to finish running a vcpu upon
235-
// receiving a vm exit that is not necessarily an error?
236-
error!("Unexpected exit reason on vcpu run: {:?}", exit);
237-
Err(VcpuError::UnhandledKvmExit(format!("{:?}", exit)))
238-
}
239-
240235
/// Initializes internal vcpufd.
241236
fn init_vcpu(&self, kvi: &kvm_bindings::kvm_vcpu_init) -> Result<(), KvmVcpuError> {
242237
self.fd.vcpu_init(kvi).map_err(KvmVcpuError::Init)?;
@@ -301,6 +296,7 @@ mod tests {
301296
use std::os::unix::io::AsRawFd;
302297

303298
use kvm_bindings::KVM_REG_SIZE_U64;
299+
use utils::eventfd::EventFd;
304300

305301
use super::*;
306302
use crate::arch::aarch64::regs::Aarch64RegisterRef;
@@ -311,9 +307,9 @@ mod tests {
311307
use crate::vstate::vm::tests::setup_vm;
312308
use crate::vstate::vm::Vm;
313309

314-
fn setup_vcpu(mem_size: usize) -> (Vm, KvmVcpu, GuestMemoryMmap) {
310+
fn setup_vcpu(mem_size: usize) -> (Vm, Vcpu, GuestMemoryMmap) {
315311
let (mut vm, vm_mem) = setup_vm(mem_size);
316-
let mut vcpu = KvmVcpu::new(0, &vm).unwrap();
312+
let mut vcpu = Vcpu::new(0, &vm, EventFd::new(libc::EFD_NONBLOCK).unwrap()).unwrap();
317313
vcpu.init(vm.fd(), &[]).unwrap();
318314
vm.setup_irqchip(1).unwrap();
319315

@@ -326,7 +322,7 @@ mod tests {
326322

327323
unsafe { libc::close(vm.fd().as_raw_fd()) };
328324

329-
let err = KvmVcpu::new(0, &vm);
325+
let err = Vcpu::new(0, &vm, EventFd::new(libc::EFD_NONBLOCK).unwrap());
330326
assert_eq!(
331327
err.err().unwrap().to_string(),
332328
"Error creating vcpu: Bad file descriptor (os error 9)".to_string()
@@ -367,8 +363,8 @@ mod tests {
367363

368364
#[test]
369365
fn test_init_vcpu() {
370-
let (mut vm, _vm_mem) = setup_vm(0x1000);
371-
let mut vcpu = KvmVcpu::new(0, &vm).unwrap();
366+
let (mut vm, _) = setup_vm(0x1000);
367+
let mut vcpu = Vcpu::new(0, &vm, EventFd::new(libc::EFD_NONBLOCK).unwrap()).unwrap();
372368
vm.setup_irqchip(1).unwrap();
373369

374370
// KVM_ARM_VCPU_PSCI_0_2 is set by default.
@@ -384,7 +380,7 @@ mod tests {
384380

385381
// Because vcpu_features vector is not empty,
386382
// kvi field should be non empty as well.
387-
let vcpu_kvi = vcpu.kvi.unwrap();
383+
let vcpu_kvi = vcpu.kvm_vcpu.kvi.unwrap();
388384
assert!((vcpu_kvi.features[0] & (1 << kvm_bindings::KVM_ARM_VCPU_PSCI_0_2)) == 0)
389385
}
390386

@@ -401,8 +397,8 @@ mod tests {
401397

402398
#[test]
403399
fn test_vcpu_save_restore_state() {
404-
let (mut vm, _vm_mem) = setup_vm(0x1000);
405-
let mut vcpu = KvmVcpu::new(0, &vm).unwrap();
400+
let (mut vm, _) = setup_vm(0x1000);
401+
let mut vcpu = Vcpu::new(0, &vm, EventFd::new(libc::EFD_NONBLOCK).unwrap()).unwrap();
406402
vm.setup_irqchip(1).unwrap();
407403

408404
// Calling KVM_GET_REGLIST before KVM_VCPU_INIT will result in error.
@@ -440,8 +436,8 @@ mod tests {
440436
//
441437
// This should fail with ENOEXEC.
442438
// https://elixir.bootlin.com/linux/v5.10.176/source/arch/arm64/kvm/arm.c#L1165
443-
let (mut vm, _vm_mem) = setup_vm(0x1000);
444-
let vcpu = KvmVcpu::new(0, &vm).unwrap();
439+
let (mut vm, _) = setup_vm(0x1000);
440+
let mut vcpu = Vcpu::new(0, &vm, EventFd::new(libc::EFD_NONBLOCK).unwrap()).unwrap();
445441
vm.setup_irqchip(1).unwrap();
446442

447443
vcpu.dump_cpu_config().unwrap_err();
@@ -450,8 +446,8 @@ mod tests {
450446
#[test]
451447
fn test_dump_cpu_config_after_init() {
452448
// Test `dump_cpu_config()` after `KVM_VCPU_INIT`.
453-
let (mut vm, _vm_mem) = setup_vm(0x1000);
454-
let mut vcpu = KvmVcpu::new(0, &vm).unwrap();
449+
let (mut vm, _) = setup_vm(0x1000);
450+
let mut vcpu = Vcpu::new(0, &vm, EventFd::new(libc::EFD_NONBLOCK).unwrap()).unwrap();
455451
vm.setup_irqchip(1).unwrap();
456452
vcpu.init(vm.fd(), &[]).unwrap();
457453

@@ -461,9 +457,9 @@ mod tests {
461457
#[test]
462458
fn test_setup_non_boot_vcpu() {
463459
let (vm, _) = setup_vm(0x1000);
464-
let mut vcpu1 = KvmVcpu::new(0, &vm).unwrap();
460+
let mut vcpu1 = Vcpu::new(0, &vm, EventFd::new(libc::EFD_NONBLOCK).unwrap()).unwrap();
465461
vcpu1.init(vm.fd(), &[]).unwrap();
466-
let mut vcpu2 = KvmVcpu::new(1, &vm).unwrap();
462+
let mut vcpu2 = Vcpu::new(1, &vm, EventFd::new(libc::EFD_NONBLOCK).unwrap()).unwrap();
467463
vcpu2.init(vm.fd(), &[]).unwrap();
468464
}
469465

0 commit comments

Comments
 (0)