Skip to content

Commit 9aa8357

Browse files
committed
refactor: move vcpu creation into struct Vm
This abstracts away the weird irqchip setup dance that builder.rs has been doing (where depending on architecture, irqchip needs to be created before or after vcpu creation. Removes a whole bunch of code that builder.rs that didn't really have too much business being there Signed-off-by: Patrick Roy <[email protected]>
1 parent add1d52 commit 9aa8357

File tree

6 files changed

+87
-101
lines changed

6 files changed

+87
-101
lines changed

src/vmm/src/builder.rs

Lines changed: 12 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ use crate::vmm_config::machine_config::{MachineConfig, MachineConfigError};
7171
use crate::vstate::kvm::Kvm;
7272
use crate::vstate::memory::{GuestAddress, GuestMemory, GuestMemoryMmap};
7373
use crate::vstate::vcpu::{Vcpu, VcpuConfig, VcpuError};
74-
use crate::vstate::vm::{Vm, VmError};
74+
use crate::vstate::vm::Vm;
7575
use crate::{device_manager, EventManager, Vmm, VmmError};
7676

7777
/// Errors associated with starting the instance.
@@ -175,10 +175,6 @@ fn create_vmm_and_vcpus(
175175
.map_err(VmmError::Vm)
176176
.map_err(StartMicrovmError::Internal)?;
177177

178-
let vcpus_exit_evt = EventFd::new(libc::EFD_NONBLOCK)
179-
.map_err(VmmError::EventFd)
180-
.map_err(Internal)?;
181-
182178
let resource_allocator = ResourceAllocator::new()?;
183179

184180
// Instantiate the MMIO device manager.
@@ -187,13 +183,13 @@ fn create_vmm_and_vcpus(
187183
// Instantiate ACPI device manager.
188184
let acpi_device_manager = ACPIDeviceManager::new();
189185

190-
// For x86_64 we need to create the interrupt controller before calling `KVM_CREATE_VCPUS`
191-
// while on aarch64 we need to do it the other way around.
192-
#[cfg(target_arch = "x86_64")]
193-
let (vcpus, pio_device_manager) = {
194-
setup_interrupt_controller(&mut vm)?;
195-
let vcpus = create_vcpus(&vm, vcpu_count, &vcpus_exit_evt).map_err(Internal)?;
186+
let (vcpus, vcpus_exit_evt) = vm
187+
.create_vcpus(vcpu_count)
188+
.map_err(VmmError::Vm)
189+
.map_err(Internal)?;
196190

191+
#[cfg(target_arch = "x86_64")]
192+
let pio_device_manager = {
197193
// Make stdout non blocking.
198194
set_stdout_nonblocking();
199195

@@ -208,25 +204,10 @@ fn create_vmm_and_vcpus(
208204
.map_err(Internal)?;
209205

210206
// create pio dev manager with legacy devices
211-
let pio_device_manager = {
212-
// TODO Remove these unwraps.
213-
let mut pio_dev_mgr = PortIODeviceManager::new(serial_device, reset_evt).unwrap();
214-
pio_dev_mgr.register_devices(vm.fd()).unwrap();
215-
pio_dev_mgr
216-
};
217-
218-
(vcpus, pio_device_manager)
219-
};
220-
221-
// On aarch64, the vCPUs need to be created (i.e call KVM_CREATE_VCPU) before setting up the
222-
// IRQ chip because the `KVM_CREATE_VCPU` ioctl will return error if the IRQCHIP
223-
// was already initialized.
224-
// Search for `kvm_arch_vcpu_create` in arch/arm/kvm/arm.c.
225-
#[cfg(target_arch = "aarch64")]
226-
let vcpus = {
227-
let vcpus = create_vcpus(&vm, vcpu_count, &vcpus_exit_evt).map_err(Internal)?;
228-
setup_interrupt_controller(&mut vm, vcpu_count)?;
229-
vcpus
207+
// TODO Remove these unwraps.
208+
let mut pio_dev_mgr = PortIODeviceManager::new(serial_device, reset_evt).unwrap();
209+
pio_dev_mgr.register_devices(vm.fd()).unwrap();
210+
pio_dev_mgr
230211
};
231212

232213
let vmm = Vmm {
@@ -671,24 +652,6 @@ where
671652
})
672653
}
673654

674-
/// Sets up the irqchip for a x86_64 microVM.
675-
#[cfg(target_arch = "x86_64")]
676-
pub fn setup_interrupt_controller(vm: &mut Vm) -> Result<(), StartMicrovmError> {
677-
vm.setup_irqchip()
678-
.map_err(VmError::Arch)
679-
.map_err(VmmError::Vm)
680-
.map_err(StartMicrovmError::Internal)
681-
}
682-
683-
/// Sets up the irqchip for a aarch64 microVM.
684-
#[cfg(target_arch = "aarch64")]
685-
pub fn setup_interrupt_controller(vm: &mut Vm, vcpu_count: u8) -> Result<(), StartMicrovmError> {
686-
vm.setup_irqchip(vcpu_count)
687-
.map_err(VmError::Arch)
688-
.map_err(VmmError::Vm)
689-
.map_err(StartMicrovmError::Internal)
690-
}
691-
692655
/// Sets up the serial device.
693656
pub fn setup_serial_device(
694657
event_manager: &mut EventManager,
@@ -746,16 +709,6 @@ fn attach_legacy_devices_aarch64(
746709
.map_err(VmmError::RegisterMMIODevice)
747710
}
748711

749-
fn create_vcpus(vm: &Vm, vcpu_count: u8, exit_evt: &EventFd) -> Result<Vec<Vcpu>, VmmError> {
750-
let mut vcpus = Vec::with_capacity(vcpu_count as usize);
751-
for cpu_idx in 0..vcpu_count {
752-
let exit_evt = exit_evt.try_clone().map_err(VmmError::EventFd)?;
753-
let vcpu = Vcpu::new(cpu_idx, vm, exit_evt).map_err(VmmError::VcpuCreate)?;
754-
vcpus.push(vcpu);
755-
}
756-
Ok(vcpus)
757-
}
758-
759712
/// Configures the system for booting Linux.
760713
#[cfg_attr(target_arch = "aarch64", allow(unused))]
761714
pub fn configure_system_for_boot(
@@ -1127,11 +1080,6 @@ pub(crate) mod tests {
11271080
pub(crate) fn default_vmm() -> Vmm {
11281081
let guest_memory = arch_mem(128 << 20);
11291082

1130-
let vcpus_exit_evt = EventFd::new(libc::EFD_NONBLOCK)
1131-
.map_err(VmmError::EventFd)
1132-
.map_err(StartMicrovmError::Internal)
1133-
.unwrap();
1134-
11351083
let kvm = Kvm::new(vec![]).unwrap();
11361084
let mut vm = Vm::new(&kvm).unwrap();
11371085
vm.memory_init(&guest_memory).unwrap();
@@ -1153,15 +1101,7 @@ pub(crate) mod tests {
11531101
)
11541102
.unwrap();
11551103

1156-
#[cfg(target_arch = "x86_64")]
1157-
setup_interrupt_controller(&mut vm).unwrap();
1158-
1159-
#[cfg(target_arch = "aarch64")]
1160-
{
1161-
let exit_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
1162-
let _vcpu = Vcpu::new(1, &vm, exit_evt).unwrap();
1163-
setup_interrupt_controller(&mut vm, 1).unwrap();
1164-
}
1104+
let (_, vcpus_exit_evt) = vm.create_vcpus(1).unwrap();
11651105

11661106
Vmm {
11671107
events_observer: Some(std::io::stdin()),
@@ -1380,24 +1320,6 @@ pub(crate) mod tests {
13801320
);
13811321
}
13821322

1383-
#[test]
1384-
fn test_create_vcpus() {
1385-
let vcpu_count = 2;
1386-
let guest_memory = arch_mem(128 << 20);
1387-
1388-
let kvm = Kvm::new(vec![]).expect("Cannot create Kvm");
1389-
#[allow(unused_mut)]
1390-
let mut vm = Vm::new(&kvm).unwrap();
1391-
vm.memory_init(&guest_memory).unwrap();
1392-
let evfd = EventFd::new(libc::EFD_NONBLOCK).unwrap();
1393-
1394-
#[cfg(target_arch = "x86_64")]
1395-
setup_interrupt_controller(&mut vm).unwrap();
1396-
1397-
let vcpu_vec = create_vcpus(&vm, vcpu_count, &evfd).unwrap();
1398-
assert_eq!(vcpu_vec.len(), vcpu_count as usize);
1399-
}
1400-
14011323
#[test]
14021324
fn test_attach_net_devices() {
14031325
let mut event_manager = EventManager::new().expect("Unable to create EventManager");

src/vmm/src/device_manager/legacy.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,8 @@ mod tests {
248248

249249
#[test]
250250
fn test_register_legacy_devices() {
251-
let (_, mut vm, _) = setup_vm_with_memory(0x1000);
252-
crate::builder::setup_interrupt_controller(&mut vm).unwrap();
251+
let (_, vm, _) = setup_vm_with_memory(0x1000);
252+
vm.setup_irqchip().unwrap();
253253
let mut ldm = PortIODeviceManager::new(
254254
Arc::new(Mutex::new(BusDevice::Serial(SerialDevice {
255255
serial: Serial::with_events(

src/vmm/src/device_manager/mmio.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -549,7 +549,7 @@ mod tests {
549549
use crate::test_utils::multi_region_mem;
550550
use crate::vstate::kvm::Kvm;
551551
use crate::vstate::memory::{GuestAddress, GuestMemoryMmap};
552-
use crate::{builder, Vm};
552+
use crate::Vm;
553553

554554
const QUEUE_SIZES: &[u16] = &[64];
555555

@@ -673,9 +673,9 @@ mod tests {
673673
let mut cmdline = kernel_cmdline::Cmdline::new(4096).unwrap();
674674
let dummy = Arc::new(Mutex::new(DummyDevice::new()));
675675
#[cfg(target_arch = "x86_64")]
676-
builder::setup_interrupt_controller(&mut vm).unwrap();
676+
vm.setup_irqchip().unwrap();
677677
#[cfg(target_arch = "aarch64")]
678-
builder::setup_interrupt_controller(&mut vm, 1).unwrap();
678+
vm.setup_irqchip(1).unwrap();
679679

680680
device_manager
681681
.register_virtio_test_device(
@@ -702,9 +702,9 @@ mod tests {
702702

703703
let mut cmdline = kernel_cmdline::Cmdline::new(4096).unwrap();
704704
#[cfg(target_arch = "x86_64")]
705-
builder::setup_interrupt_controller(&mut vm).unwrap();
705+
vm.setup_irqchip().unwrap();
706706
#[cfg(target_arch = "aarch64")]
707-
builder::setup_interrupt_controller(&mut vm, 1).unwrap();
707+
vm.setup_irqchip(1).unwrap();
708708

709709
for _i in crate::arch::IRQ_BASE..=crate::arch::IRQ_MAX {
710710
device_manager
@@ -756,9 +756,9 @@ mod tests {
756756
let mem_clone = guest_mem.clone();
757757

758758
#[cfg(target_arch = "x86_64")]
759-
builder::setup_interrupt_controller(&mut vm).unwrap();
759+
vm.setup_irqchip().unwrap();
760760
#[cfg(target_arch = "aarch64")]
761-
builder::setup_interrupt_controller(&mut vm, 1).unwrap();
761+
vm.setup_irqchip(1).unwrap();
762762

763763
let mut device_manager = MMIODeviceManager::new();
764764
let mut resource_allocator = ResourceAllocator::new().unwrap();

src/vmm/src/vstate/vm/aarch64.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,18 @@ impl ArchVm {
3737
})
3838
}
3939

40+
pub(super) fn arch_pre_create_vcpus(&mut self, _: u8) -> Result<(), ArchVmError> {
41+
Ok(())
42+
}
43+
44+
pub(super) fn arch_post_create_vcpus(&mut self, nr_vcpus: u8) -> Result<(), ArchVmError> {
45+
// On aarch64, the vCPUs need to be created (i.e call KVM_CREATE_VCPU) before setting up the
46+
// IRQ chip because the `KVM_CREATE_VCPU` ioctl will return error if the IRQCHIP
47+
// was already initialized.
48+
// Search for `kvm_arch_vcpu_create` in arch/arm/kvm/arm.c.
49+
self.setup_irqchip(nr_vcpus)
50+
}
51+
4052
/// Creates the GIC (Global Interrupt Controller).
4153
pub fn setup_irqchip(&mut self, vcpu_count: u8) -> Result<(), ArchVmError> {
4254
self.irqchip_handle = Some(

src/vmm/src/vstate/vm/mod.rs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
use kvm_bindings::{kvm_userspace_memory_region, KVM_MEM_LOG_DIRTY_PAGES};
99
use kvm_ioctls::VmFd;
10+
use vmm_sys_util::eventfd::EventFd;
1011

1112
#[cfg(target_arch = "x86_64")]
1213
use crate::utils::u64_to_usize;
@@ -21,10 +22,13 @@ mod arch;
2122

2223
pub use arch::{ArchVm as Vm, ArchVmError, VmState};
2324

25+
use crate::vstate::vcpu::VcpuError;
26+
use crate::Vcpu;
27+
2428
/// Errors associated with the wrappers over KVM ioctls.
2529
/// Needs `rustfmt::skip` to make multiline comments work
2630
#[rustfmt::skip]
27-
#[derive(Debug, PartialEq, Eq, thiserror::Error, displaydoc::Display)]
31+
#[derive(Debug, thiserror::Error, displaydoc::Display)]
2832
pub enum VmError {
2933
/// Cannot set the memory regions: {0}
3034
SetUserMemoryRegion(kvm_ioctls::Error),
@@ -34,10 +38,34 @@ pub enum VmError {
3438
VmSetup(kvm_ioctls::Error),
3539
/// {0}
3640
Arch(#[from] ArchVmError),
41+
/// Error during eventfd operations: {0}
42+
EventFd(std::io::Error),
43+
/// Failed to create vcpu: {0}
44+
CreateVcpu(VcpuError),
3745
}
3846

3947
/// Contains Vm functions that are usable across CPU architectures
4048
impl Vm {
49+
/// Creates the specified number of [`Vcpu`]s.
50+
///
51+
/// The returned [`EventFd`] is written to whenever any of the vcpus exit.
52+
pub fn create_vcpus(&mut self, vcpu_count: u8) -> Result<(Vec<Vcpu>, EventFd), VmError> {
53+
self.arch_pre_create_vcpus(vcpu_count)?;
54+
55+
let exit_evt = EventFd::new(libc::EFD_NONBLOCK).map_err(VmError::EventFd)?;
56+
57+
let mut vcpus = Vec::with_capacity(vcpu_count as usize);
58+
for cpu_idx in 0..vcpu_count {
59+
let exit_evt = exit_evt.try_clone().map_err(VmError::EventFd)?;
60+
let vcpu = Vcpu::new(cpu_idx, self, exit_evt).map_err(VmError::CreateVcpu)?;
61+
vcpus.push(vcpu);
62+
}
63+
64+
self.arch_post_create_vcpus(vcpu_count)?;
65+
66+
Ok((vcpus, exit_evt))
67+
}
68+
4169
/// Initializes the guest memory.
4270
pub fn memory_init(&self, guest_mem: &GuestMemoryMmap) -> Result<(), VmError> {
4371
self.set_kvm_memory_regions(guest_mem)?;
@@ -88,7 +116,7 @@ impl Vm {
88116
#[cfg(test)]
89117
pub(crate) mod tests {
90118
use super::*;
91-
use crate::test_utils::single_region_mem;
119+
use crate::test_utils::{arch_mem, single_region_mem};
92120
use crate::vstate::kvm::Kvm;
93121
use crate::vstate::memory::GuestMemoryMmap;
94122

@@ -139,4 +167,19 @@ pub(crate) mod tests {
139167
"Cannot set the memory regions: Invalid argument (os error 22)"
140168
);
141169
}
170+
171+
#[test]
172+
fn test_create_vcpus() {
173+
let vcpu_count = 2;
174+
let guest_memory = arch_mem(128 << 20);
175+
176+
let kvm = Kvm::new(vec![]).expect("Cannot create Kvm");
177+
#[allow(unused_mut)]
178+
let mut vm = Vm::new(&kvm).unwrap();
179+
vm.memory_init(&guest_memory).unwrap();
180+
181+
let (vcpu_vec, _) = vm.create_vcpus(vcpu_count).unwrap();
182+
183+
assert_eq!(vcpu_vec.len(), vcpu_count as usize);
184+
}
142185
}

src/vmm/src/vstate/vm/x86_64.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,15 @@ impl ArchVm {
5959
Ok(ArchVm { fd, msrs_to_save })
6060
}
6161

62+
pub(super) fn arch_pre_create_vcpus(&mut self, _: u8) -> Result<(), ArchVmError> {
63+
// For x86_64 we need to create the interrupt controller before calling `KVM_CREATE_VCPUS`
64+
self.setup_irqchip()
65+
}
66+
67+
pub(super) fn arch_post_create_vcpus(&mut self, _: u8) -> Result<(), ArchVmError> {
68+
Ok(())
69+
}
70+
6271
/// Restores the KVM VM state.
6372
///
6473
/// # Errors

0 commit comments

Comments
 (0)