vm_control: skip VfioDmabufUnmap when possible

Add tracking to VmMemoryRequest.execute so that only requests which were
mapped with VfioDmabufMap get unmapped with VfioDmabufUnmap. This fixes
a deadlock that occurs when unregistering memory before the viommu
device is started, which is triggered by VfioPciDevice on ManaTEE.

Note that although a deadlock with registering memory is still possible,
this doesn't seem to be a problem in practice. Registration of such
buffers is driven by the virtio-gpu driver, and thus tends to occur
later during guest boot.

BUG=b:235508487
TEST=boot brya-manatee

Change-Id: I080d27b43d01a01b7d962b62c986d837be72b288
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/3830133
Tested-by: David Stevens <stevensd@chromium.org>
Commit-Queue: Keiichi Watanabe <keiichiw@chromium.org>
Auto-Submit: David Stevens <stevensd@chromium.org>
Commit-Queue: David Stevens <stevensd@chromium.org>
Reviewed-by: Keiichi Watanabe <keiichiw@chromium.org>
This commit is contained in:
David Stevens 2022-08-15 11:00:51 +09:00 committed by crosvm LUCI
parent 0a826cc508
commit 88e0828aa3
3 changed files with 44 additions and 18 deletions

View file

@ -2188,6 +2188,10 @@ fn run_control<V: VmArch + 'static, Vcpu: VcpuArch + 'static>(
DelayedIrqFd,
}
let mut iommu_client = iommu_host_tube
.as_ref()
.map(VmMemoryRequestIommuClient::new);
stdin()
.set_raw_mode()
.expect("failed to set terminal raw mode");
@ -2562,7 +2566,7 @@ fn run_control<V: VmArch + 'static, Vcpu: VcpuArch + 'static>(
&mut sys_allocator,
Arc::clone(&map_request),
&mut gralloc,
&iommu_host_tube,
&mut iommu_client,
);
if let Err(e) = tube.send(&response) {
error!("failed to send VmMemoryControlResponse: {}", e);

View file

@ -1002,7 +1002,7 @@ fn run_control<V: VmArch + 'static, Vcpu: VcpuArch + 'static>(
&mut sys_allocator_mutex.lock(),
Arc::clone(&map_request),
&mut gralloc,
&None,
&mut None,
);
if let Err(e) = tube.send(&response) {
error!("failed to send VmMemoryControlResponse: {}", e);

View file

@ -22,6 +22,7 @@ pub mod client;
pub mod display;
pub mod sys;
use std::collections::BTreeSet;
use std::convert::TryInto;
use std::fmt;
use std::fmt::Display;
@ -392,6 +393,22 @@ pub enum VmMemoryRequest {
UnregisterMemory(MemSlot),
}
/// Struct for managing `VmMemoryRequest`s IOMMU related state.
pub struct VmMemoryRequestIommuClient<'a> {
tube: &'a Tube,
gpu_memory: BTreeSet<MemSlot>,
}
impl<'a> VmMemoryRequestIommuClient<'a> {
/// Constructs `VmMemoryRequestIommuClient` from a tube for communication with the viommu.
pub fn new(tube: &'a Tube) -> Self {
Self {
tube,
gpu_memory: BTreeSet::new(),
}
}
}
impl VmMemoryRequest {
/// Executes this request on the given Vm.
///
@ -408,7 +425,7 @@ impl VmMemoryRequest {
sys_allocator: &mut SystemAllocator,
map_request: Arc<Mutex<Option<ExternalMapping>>>,
gralloc: &mut RutabagaGralloc,
iommu_host_tube: &Option<Tube>,
iommu_client: &mut Option<VmMemoryRequestIommuClient>,
) -> VmMemoryResponse {
use self::VmMemoryRequest::*;
match self {
@ -436,7 +453,7 @@ impl VmMemoryRequest {
Err(e) => return VmMemoryResponse::Err(e),
};
if let (Some(descriptor), Some(iommu_tube)) = (descriptor, iommu_host_tube) {
if let (Some(descriptor), Some(iommu_client)) = (descriptor, iommu_client) {
let request =
VirtioIOMMURequest::VfioCommand(VirtioIOMMUVfioCommand::VfioDmabufMap {
mem_slot: slot,
@ -445,7 +462,7 @@ impl VmMemoryRequest {
dma_buf: descriptor,
});
match virtio_iommu_request(iommu_tube, &request) {
match virtio_iommu_request(iommu_client.tube, &request) {
Ok(VirtioIOMMUResponse::VfioResponse(VirtioIOMMUVfioResult::Ok)) => (),
resp => {
error!("Unexpected message response: {:?}", resp);
@ -453,7 +470,9 @@ impl VmMemoryRequest {
let _ = vm.remove_memory_region(slot);
return VmMemoryResponse::Err(SysError::new(EINVAL));
}
}
};
iommu_client.gpu_memory.insert(slot);
}
let pfn = guest_addr.0 >> 12;
@ -461,20 +480,23 @@ impl VmMemoryRequest {
}
UnregisterMemory(slot) => match vm.remove_memory_region(slot) {
Ok(_) => {
if let Some(iommu_tube) = iommu_host_tube {
let request = VirtioIOMMURequest::VfioCommand(
VirtioIOMMUVfioCommand::VfioDmabufUnmap(slot),
);
if let Some(iommu_client) = iommu_client {
if iommu_client.gpu_memory.remove(&slot) {
let request = VirtioIOMMURequest::VfioCommand(
VirtioIOMMUVfioCommand::VfioDmabufUnmap(slot),
);
match virtio_iommu_request(iommu_tube, &request) {
Ok(VirtioIOMMUResponse::VfioResponse(VirtioIOMMUVfioResult::Ok))
| Ok(VirtioIOMMUResponse::VfioResponse(
VirtioIOMMUVfioResult::NoSuchMappedDmabuf,
)) => VmMemoryResponse::Ok,
resp => {
error!("Unexpected message response: {:?}", resp);
return VmMemoryResponse::Err(SysError::new(EINVAL));
match virtio_iommu_request(iommu_client.tube, &request) {
Ok(VirtioIOMMUResponse::VfioResponse(
VirtioIOMMUVfioResult::Ok,
)) => VmMemoryResponse::Ok,
resp => {
error!("Unexpected message response: {:?}", resp);
return VmMemoryResponse::Err(SysError::new(EINVAL));
}
}
} else {
VmMemoryResponse::Ok
}
} else {
VmMemoryResponse::Ok