devices: vfio: prepare for vIOMMU support: multiple VFIO containers

In preparation for virtio IOMMU support, implement global variables
IOMMU_CONTAINERS and NO_IOMMU_CONTAINER to hold the VFIO containers
for VFIO devices with or without IOMMu enabled respectively.

Also implement vfio_get_container()help create VFIO container based
on the more complicated policies.

- all VFIO devices without attaching to virtio IOMMU devices share
  one VFIO container.

- for IOMMU enabled devices, one VFIO container manages all devices
  under one VFIO group.

- we don't support multiple IOMMU groups set to one VFIO container.
  Currently don't see an user case for this.

BUG=b:181736020
TEST=unit tests.

Change-Id: I44d792cbc8ca9696c1da54c571aad1b94c7f665d
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/crosvm/+/2976054
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Reviewed-by: David Stevens <stevensd@chromium.org>
Commit-Queue: David Stevens <stevensd@chromium.org>
diff --git a/src/linux.rs b/src/linux.rs
index 06004ca..974b4d6 100644
--- a/src/linux.rs
+++ b/src/linux.rs
@@ -2,7 +2,6 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use std::cell::RefCell;
 use std::cmp::Reverse;
 use std::convert::TryFrom;
 #[cfg(feature = "gpu")]
@@ -33,6 +32,7 @@
 
 use base::net::{UnixSeqpacket, UnixSeqpacketListener, UnlinkUnixSeqpacketListener};
 use base::*;
+use devices::vfio::{VfioCommonSetup, VfioCommonTrait};
 #[cfg(feature = "gpu")]
 use devices::virtio::gpu::{DEFAULT_DISPLAY_HEIGHT, DEFAULT_DISPLAY_WIDTH};
 use devices::virtio::vhost::user::{
@@ -45,8 +45,8 @@
 #[cfg(feature = "audio")]
 use devices::Ac97Dev;
 use devices::{
-    self, IrqChip, IrqEventIndex, KvmKernelIrqChip, PciDevice, VcpuRunState, VfioContainer,
-    VfioDevice, VfioPciDevice, VirtioPciDevice,
+    self, IrqChip, IrqEventIndex, KvmKernelIrqChip, PciDevice, VcpuRunState, VfioDevice,
+    VfioPciDevice, VirtioPciDevice,
 };
 #[cfg(feature = "usb")]
 use devices::{HostBackendDeviceProvider, XhciController};
@@ -97,7 +97,6 @@
     BalloonDeviceNew(virtio::BalloonError),
     BlockDeviceNew(base::Error),
     BlockSignal(base::signal::Error),
-    BorrowVfioContainer,
     BuildVm(<Arch as LinuxArch>::Error),
     ChownTpmStorage(base::Error),
     CloneEvent(base::Error),
@@ -225,7 +224,6 @@
             BalloonDeviceNew(e) => write!(f, "failed to create balloon: {}", e),
             BlockDeviceNew(e) => write!(f, "failed to create block device: {}", e),
             BlockSignal(e) => write!(f, "failed to block signal: {}", e),
-            BorrowVfioContainer => write!(f, "failed to borrow global vfio container"),
             BuildVm(e) => write!(f, "The architecture failed to build the vm: {}", e),
             ChownTpmStorage(e) => write!(f, "failed to chown tpm storage: {}", e),
             CloneEvent(e) => write!(f, "failed to clone event: {}", e),
@@ -1730,7 +1728,6 @@
     Ok(devs)
 }
 
-thread_local!(static VFIO_CONTAINER: RefCell<Option<Arc<Mutex<VfioContainer>>>> = RefCell::new(None));
 fn create_vfio_device(
     cfg: &Config,
     vm: &impl Vm,
@@ -1740,21 +1737,7 @@
     kvm_vfio_file: &SafeDescriptor,
 ) -> DeviceResult<(Box<VfioPciDevice>, Option<Minijail>)> {
     let vfio_container =
-        VFIO_CONTAINER.with::<_, DeviceResult<Arc<Mutex<VfioContainer>>>>(|v| {
-            if v.borrow().is_some() {
-                if let Some(container) = &(*v.borrow()) {
-                    Ok(container.clone())
-                } else {
-                    Err(Error::BorrowVfioContainer)
-                }
-            } else {
-                let container = Arc::new(Mutex::new(
-                    VfioContainer::new().map_err(Error::CreateVfioDevice)?,
-                ));
-                *v.borrow_mut() = Some(container.clone());
-                Ok(container)
-            }
-        })?;
+        VfioCommonSetup::vfio_get_container(vfio_path, false).map_err(Error::CreateVfioDevice)?;
 
     // create MSI, MSI-X, and Mem request sockets for each vfio device
     let (vfio_host_tube_msi, vfio_device_tube_msi) = Tube::pair().map_err(Error::CreateTube)?;