vtd_find_add_as can be called by multiple threads which leads to a race
condition on address space creation. The IOMMU lock must be taken to
avoid such a race.

Signed-off-by: Clement Mathieu--Drif <clement.mathieu--d...@eviden.com>
---
 hw/i386/intel_iommu.c | 28 ++++++++++++++++++++++++++--
 1 file changed, 26 insertions(+), 2 deletions(-)

diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c
index b7855f4b87..931ac01ef0 100644
--- a/hw/i386/intel_iommu.c
+++ b/hw/i386/intel_iommu.c
@@ -4203,11 +4203,15 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, 
PCIBus *bus,
         .pasid = pasid,
     };
     VTDAddressSpace *vtd_dev_as;
+    struct vtd_as_key *new_key = NULL;
     char name[128];
 
+    vtd_iommu_lock(s);
     vtd_dev_as = g_hash_table_lookup(s->vtd_address_spaces, &key);
+    vtd_iommu_unlock(s);
+
     if (!vtd_dev_as) {
-        struct vtd_as_key *new_key = g_malloc(sizeof(*new_key));
+        new_key = g_malloc(sizeof(*new_key));
 
         new_key->bus = bus;
         new_key->devfn = devfn;
@@ -4302,9 +4306,29 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, 
PCIBus *bus,
                                             &vtd_dev_as->nodmar, 0);
 
         vtd_switch_address_space(vtd_dev_as);
+    }
 
-        g_hash_table_insert(s->vtd_address_spaces, new_key, vtd_dev_as);
+    if (new_key != NULL) {
+        VTDAddressSpace *second_vtd_dev_as;
+
+        /*
+         * Take the lock again and recheck as the AS might have
+         * been created in the meantime.
+         */
+        vtd_iommu_lock(s);
+
+        second_vtd_dev_as = g_hash_table_lookup(s->vtd_address_spaces, &key);
+        if (!second_vtd_dev_as) {
+            g_hash_table_insert(s->vtd_address_spaces, new_key, vtd_dev_as);
+        } else {
+            vtd_dev_as = second_vtd_dev_as;
+            g_free(vtd_dev_as);
+            g_free(new_key);
+        }
+
+        vtd_iommu_unlock(s);
     }
+
     return vtd_dev_as;
 }
 
-- 
2.49.0

Reply via email to