Skip to content

Commit 8d04abe

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 59e6520 commit 8d04abe

File tree

4 files changed

+162
-156
lines changed

4 files changed

+162
-156
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: 38 additions & 42 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
}
@@ -136,14 +142,14 @@ impl KvmVcpu {
136142
kvi.features[index] = feature.bitmap.apply(kvi.features[index]);
137143
}
138144

139-
self.kvi = if !vcpu_features.is_empty() {
145+
self.kvm_vcpu.kvi = if !vcpu_features.is_empty() {
140146
Some(kvi)
141147
} else {
142148
None
143149
};
144150

145151
// Non-boot cpus are powered off initially.
146-
if 0 < self.index {
152+
if 0 < self.kvm_vcpu.index {
147153
kvi.features[0] |= 1 << kvm_bindings::KVM_ARM_VCPU_POWER_OFF;
148154
}
149155

@@ -174,7 +180,7 @@ impl KvmVcpu {
174180
};
175181
get_all_registers(&self.fd, &mut state.regs).map_err(KvmVcpuError::SaveState)?;
176182
state.mpidr = get_mpidr(&self.fd).map_err(KvmVcpuError::SaveState)?;
177-
state.kvi = self.kvi;
183+
state.kvi = self.kvm_vcpu.kvi;
178184
Ok(state)
179185
}
180186

@@ -184,7 +190,7 @@ impl KvmVcpu {
184190
Some(kvi) => kvi,
185191
None => Self::default_kvi(vm_fd)?,
186192
};
187-
self.kvi = state.kvi;
193+
self.kvm_vcpu.kvi = state.kvi;
188194

189195
self.init_vcpu(&kvi)?;
190196

@@ -223,17 +229,6 @@ impl KvmVcpu {
223229
Ok(CpuConfiguration { regs })
224230
}
225231

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

300295
use kvm_bindings::KVM_REG_SIZE_U64;
296+
use utils::eventfd::EventFd;
301297

302298
use super::*;
303299
use crate::arch::aarch64::regs::Aarch64RegisterRef;
@@ -308,9 +304,9 @@ mod tests {
308304
use crate::vstate::vm::tests::setup_vm;
309305
use crate::vstate::vm::Vm;
310306

311-
fn setup_vcpu(mem_size: usize) -> (Vm, KvmVcpu, GuestMemoryMmap) {
307+
fn setup_vcpu(mem_size: usize) -> (Vm, Vcpu, GuestMemoryMmap) {
312308
let (mut vm, vm_mem) = setup_vm(mem_size);
313-
let mut vcpu = KvmVcpu::new(0, &vm).unwrap();
309+
let mut vcpu = Vcpu::new(0, &vm, EventFd::new(libc::EFD_NONBLOCK).unwrap()).unwrap();
314310
vcpu.init(vm.fd(), &[]).unwrap();
315311
vm.setup_irqchip(1).unwrap();
316312

@@ -323,7 +319,7 @@ mod tests {
323319

324320
unsafe { libc::close(vm.fd().as_raw_fd()) };
325321

326-
let err = KvmVcpu::new(0, &vm);
322+
let err = Vcpu::new(0, &vm, EventFd::new(libc::EFD_NONBLOCK).unwrap());
327323
assert_eq!(
328324
err.err().unwrap().to_string(),
329325
"Error creating vcpu: Bad file descriptor (os error 9)".to_string()
@@ -364,8 +360,8 @@ mod tests {
364360

365361
#[test]
366362
fn test_init_vcpu() {
367-
let (mut vm, _vm_mem) = setup_vm(0x1000);
368-
let mut vcpu = KvmVcpu::new(0, &vm).unwrap();
363+
let (mut vm, _) = setup_vm(0x1000);
364+
let mut vcpu = Vcpu::new(0, &vm, EventFd::new(libc::EFD_NONBLOCK).unwrap()).unwrap();
369365
vm.setup_irqchip(1).unwrap();
370366

371367
// KVM_ARM_VCPU_PSCI_0_2 is set by default.
@@ -381,7 +377,7 @@ mod tests {
381377

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

@@ -398,8 +394,8 @@ mod tests {
398394

399395
#[test]
400396
fn test_vcpu_save_restore_state() {
401-
let (mut vm, _vm_mem) = setup_vm(0x1000);
402-
let mut vcpu = KvmVcpu::new(0, &vm).unwrap();
397+
let (mut vm, _) = setup_vm(0x1000);
398+
let mut vcpu = Vcpu::new(0, &vm, EventFd::new(libc::EFD_NONBLOCK).unwrap()).unwrap();
403399
vm.setup_irqchip(1).unwrap();
404400

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

444440
vcpu.dump_cpu_config().unwrap_err();
@@ -447,8 +443,8 @@ mod tests {
447443
#[test]
448444
fn test_dump_cpu_config_after_init() {
449445
// Test `dump_cpu_config()` after `KVM_VCPU_INIT`.
450-
let (mut vm, _vm_mem) = setup_vm(0x1000);
451-
let mut vcpu = KvmVcpu::new(0, &vm).unwrap();
446+
let (mut vm, _) = setup_vm(0x1000);
447+
let mut vcpu = Vcpu::new(0, &vm, EventFd::new(libc::EFD_NONBLOCK).unwrap()).unwrap();
452448
vm.setup_irqchip(1).unwrap();
453449
vcpu.init(vm.fd(), &[]).unwrap();
454450

@@ -458,9 +454,9 @@ mod tests {
458454
#[test]
459455
fn test_setup_non_boot_vcpu() {
460456
let (vm, _) = setup_vm(0x1000);
461-
let mut vcpu1 = KvmVcpu::new(0, &vm).unwrap();
457+
let mut vcpu1 = Vcpu::new(0, &vm, EventFd::new(libc::EFD_NONBLOCK).unwrap()).unwrap();
462458
vcpu1.init(vm.fd(), &[]).unwrap();
463-
let mut vcpu2 = KvmVcpu::new(1, &vm).unwrap();
459+
let mut vcpu2 = Vcpu::new(1, &vm, EventFd::new(libc::EFD_NONBLOCK).unwrap()).unwrap();
464460
vcpu2.init(vm.fd(), &[]).unwrap();
465461
}
466462

0 commit comments

Comments
 (0)