mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2026-08-05 02:19:16 +00:00
vmm: release vIOMMU VFIO container on device eject
Currently ejecting a device leaks its mapping keeping the container fd open. Remove the mapping so the fd can be closed. Assisted-by: Claude:Opus-4.8 Signed-off-by: Dylan Reid <dgreid@fb.com>
This commit is contained in:
@@ -1244,6 +1244,14 @@ impl Iommu {
|
||||
self.ext_mapping.lock().unwrap().insert(device_id, mapping);
|
||||
}
|
||||
|
||||
/// Removes a mapping added with `add_external_mapping`.
|
||||
pub fn remove_external_mapping(
|
||||
&mut self,
|
||||
device_id: u32,
|
||||
) -> Option<Arc<dyn ExternalDmaMapping>> {
|
||||
self.ext_mapping.lock().unwrap().remove(&device_id)
|
||||
}
|
||||
|
||||
#[cfg(fuzzing)]
|
||||
pub fn wait_for_epoll_threads(&mut self) {
|
||||
self.common.wait_for_epoll_threads();
|
||||
@@ -1362,3 +1370,73 @@ impl Snapshottable for Iommu {
|
||||
}
|
||||
impl Transportable for Iommu {}
|
||||
impl Migratable for Iommu {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io;
|
||||
use std::sync::{Arc, Weak};
|
||||
|
||||
use seccompiler::SeccompAction;
|
||||
use vm_device::dma_mapping::ExternalDmaMapping;
|
||||
use vmm_sys_util::eventfd::{EFD_NONBLOCK, EventFd};
|
||||
|
||||
use super::Iommu;
|
||||
|
||||
/// Test stub for VfioDmaMapping.
|
||||
struct MockMapping;
|
||||
|
||||
impl ExternalDmaMapping for MockMapping {
|
||||
fn map(&self, _iova: u64, _gpa: u64, _size: u64) -> Result<(), io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unmap(&self, _iova: u64, _size: u64) -> Result<(), io::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn new_iommu() -> Iommu {
|
||||
let (iommu, _mapping) = Iommu::new(
|
||||
"test-iommu".to_string(),
|
||||
SeccompAction::Allow,
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
(0, 0),
|
||||
64,
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
iommu
|
||||
}
|
||||
|
||||
/// Tests removing a mapping works and releases the ref.
|
||||
#[test]
|
||||
fn remove_external_mapping() {
|
||||
let mut iommu = new_iommu();
|
||||
|
||||
let mapping: Arc<dyn ExternalDmaMapping> = Arc::new(MockMapping);
|
||||
let weak: Weak<dyn ExternalDmaMapping> = Arc::downgrade(&mapping);
|
||||
|
||||
iommu.add_external_mapping(0x100, mapping);
|
||||
|
||||
// Removing the mapping succeeds.
|
||||
let removed = iommu.remove_external_mapping(0x100);
|
||||
assert!(removed.is_some());
|
||||
|
||||
// Dropping the returned Arc drops the last reference.
|
||||
drop(removed);
|
||||
assert!(
|
||||
weak.upgrade().is_none(),
|
||||
"iommu must not retain a reference after removal"
|
||||
);
|
||||
|
||||
// Removing the same id again is a nop.
|
||||
assert!(
|
||||
iommu.remove_external_mapping(0x100).is_none(),
|
||||
"removal is idempotent"
|
||||
);
|
||||
|
||||
// Removing a bogus ID doesn't crash.
|
||||
assert!(iommu.remove_external_mapping(0x999).is_none());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5020,6 +5020,13 @@ impl DeviceManager {
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(iommu) = &self.iommu_device {
|
||||
iommu
|
||||
.lock()
|
||||
.unwrap()
|
||||
.remove_external_mapping(pci_device_bdf.into());
|
||||
}
|
||||
|
||||
if remove_dma_handler {
|
||||
for virtio_mem_device in self.virtio_mem_devices.iter() {
|
||||
let source = VirtioMemMappingSource::Device(pci_device_bdf.into());
|
||||
|
||||
Reference in New Issue
Block a user