diff --git a/vmm/src/device_manager.rs b/vmm/src/device_manager.rs index 57c17a00a..d86aa056f 100644 --- a/vmm/src/device_manager.rs +++ b/vmm/src/device_manager.rs @@ -423,8 +423,6 @@ impl DeviceManager { vm_fd: vm_info.vm_fd.clone(), }); - let ioapic = DeviceManager::add_ioapic(vm_info, &address_manager)?; - // Create a shared list of GSI that can be shared through all PCI // devices. This way, we can maintain the full list of used GSI, // preventing one device from overriding interrupts setting from @@ -432,11 +430,38 @@ impl DeviceManager { let kvm_gsi_msi_routes: Arc>> = Arc::new(Mutex::new(HashMap::new())); + // Here we create a first interrupt manager that will be directly + // passed down to the Ioapic. The reason we need this extra interrupt + // manager is because the more global one will need a handler onto the + // Ioapic itself. We didn't want to solve this problem by adding some + // setter to the KvmInterruptManager as this would have required the + // interrupt manager to be mutable. + // + // One thing to note, it is safe to create two interrupt managers + // without risking some concurrency between the two since the list + // of GSI routes is shared and protected by a Mutex. + let ioapic_interrupt_manager: Arc = + Arc::new(KvmInterruptManager::new( + Arc::clone(&address_manager.allocator), + Arc::clone(&vm_info.vm_fd), + Arc::clone(&kvm_gsi_msi_routes), + None, + )); + + let ioapic = + DeviceManager::add_ioapic(vm_info, &address_manager, ioapic_interrupt_manager)?; + + // Creation of the global interrupt manager, which can take a hold onto + // the brand new Ioapic. + // + // Note the list of GSI routes is Arc cloned, the same way it was Arc + // cloned for the interrupt manager dedicated to the Ioapic. That's how + // both interrupt managers are going to share the list correctly. let interrupt_manager: Arc = Arc::new(KvmInterruptManager::new( - address_manager.allocator.clone(), - vm_info.vm_fd.clone(), - kvm_gsi_msi_routes, - ioapic.clone(), + Arc::clone(&address_manager.allocator), + Arc::clone(&vm_info.vm_fd), + Arc::clone(&kvm_gsi_msi_routes), + Some(ioapic.clone()), )); let console = DeviceManager::add_console_device( @@ -672,6 +697,7 @@ impl DeviceManager { fn add_ioapic( vm_info: &VmInfo, address_manager: &Arc, + _ioapic_interrupt_manager: Arc, ) -> DeviceManagerResult>> { // Create IOAPIC let ioapic = Arc::new(Mutex::new(ioapic::Ioapic::new( diff --git a/vmm/src/interrupt.rs b/vmm/src/interrupt.rs index ea51dc81b..115277b02 100644 --- a/vmm/src/interrupt.rs +++ b/vmm/src/interrupt.rs @@ -276,7 +276,7 @@ pub struct KvmInterruptManager { allocator: Arc>, vm_fd: Arc, gsi_msi_routes: Arc>>, - ioapic: Arc>, + ioapic: Option>>, } impl KvmInterruptManager { @@ -284,7 +284,7 @@ impl KvmInterruptManager { allocator: Arc>, vm_fd: Arc, gsi_msi_routes: Arc>>, - ioapic: Arc>, + ioapic: Option>>, ) -> Self { KvmInterruptManager { allocator, @@ -311,10 +311,17 @@ impl InterruptManager for KvmInterruptManager { )); } - Arc::new(Box::new(LegacyUserspaceInterruptGroup::new( - self.ioapic.clone(), - base as u32, - ))) + if let Some(ioapic) = &self.ioapic { + Arc::new(Box::new(LegacyUserspaceInterruptGroup::new( + ioapic.clone(), + base as u32, + ))) + } else { + return Err(io::Error::new( + io::ErrorKind::Other, + "No IOAPIC configured, cannot create legacy interrupt group", + )); + } } PCI_MSI_IRQ => { let mut allocator = self.allocator.lock().unwrap();