Blame SOURCES/kvm-hw-arm-smmu-common-Manage-IOTLB-block-entries.patch

8fced6
From 4770f43dab482e4585d3555933a473cf24e796db Mon Sep 17 00:00:00 2001
8fced6
From: eperezma <eperezma@redhat.com>
8fced6
Date: Tue, 12 Jan 2021 14:36:30 -0500
8fced6
Subject: [PATCH 06/17] hw/arm/smmu-common: Manage IOTLB block entries
8fced6
MIME-Version: 1.0
8fced6
Content-Type: text/plain; charset=UTF-8
8fced6
Content-Transfer-Encoding: 8bit
8fced6
8fced6
RH-Author: eperezma <eperezma@redhat.com>
8fced6
Message-id: <20210112143638.374060-6-eperezma@redhat.com>
8fced6
Patchwork-id: 100598
8fced6
O-Subject: [RHEL-8.4.0 qemu-kvm PATCH v2 05/13] hw/arm/smmu-common: Manage IOTLB block entries
8fced6
Bugzilla: 1843852
8fced6
RH-Acked-by: Xiao Wang <jasowang@redhat.com>
8fced6
RH-Acked-by: Peter Xu <peterx@redhat.com>
8fced6
RH-Acked-by: Auger Eric <eric.auger@redhat.com>
8fced6
8fced6
From: Eric Auger <eric.auger@redhat.com>
8fced6
8fced6
At the moment each entry in the IOTLB corresponds to a page sized
8fced6
mapping (4K, 16K or 64K), even if the page belongs to a mapped
8fced6
block. In case of block mapping this unefficiently consumes IOTLB
8fced6
entries.
8fced6
8fced6
Change the value of the entry so that it reflects the actual
8fced6
mapping it belongs to (block or page start address and size).
8fced6
8fced6
Also the level/tg of the entry is encoded in the key. In subsequent
8fced6
patches we will enable range invalidation. This latter is able
8fced6
to provide the level/tg of the entry.
8fced6
8fced6
Encoding the level/tg directly in the key will allow to invalidate
8fced6
using g_hash_table_remove() when num_pages equals to 1.
8fced6
8fced6
Signed-off-by: Eric Auger <eric.auger@redhat.com>
8fced6
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
8fced6
Message-id: 20200728150815.11446-6-eric.auger@redhat.com
8fced6
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
8fced6
(cherry picked from commit 9e54dee71fcfaae69f87b8e1f51485a832266a39)
8fced6
Signed-off-by: Eugenio PĂ©rez <eperezma@redhat.com>
8fced6
Signed-off-by: Danilo C. L. de Paula <ddepaula@redhat.com>
8fced6
---
8fced6
 hw/arm/smmu-common.c         | 67 ++++++++++++++++++++++++++----------
8fced6
 hw/arm/smmu-internal.h       |  7 ++++
8fced6
 hw/arm/smmuv3.c              |  6 ++--
8fced6
 hw/arm/trace-events          |  2 +-
8fced6
 include/hw/arm/smmu-common.h | 10 ++++--
8fced6
 5 files changed, 67 insertions(+), 25 deletions(-)
8fced6
8fced6
diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c
8fced6
index 06e9e38b007..8007edeaaa2 100644
8fced6
--- a/hw/arm/smmu-common.c
8fced6
+++ b/hw/arm/smmu-common.c
8fced6
@@ -39,7 +39,7 @@ static guint smmu_iotlb_key_hash(gconstpointer v)
8fced6
 
8fced6
     /* Jenkins hash */
8fced6
     a = b = c = JHASH_INITVAL + sizeof(*key);
8fced6
-    a += key->asid;
8fced6
+    a += key->asid + key->level + key->tg;
8fced6
     b += extract64(key->iova, 0, 32);
8fced6
     c += extract64(key->iova, 32, 32);
8fced6
 
8fced6
@@ -51,24 +51,41 @@ static guint smmu_iotlb_key_hash(gconstpointer v)
8fced6
 
8fced6
 static gboolean smmu_iotlb_key_equal(gconstpointer v1, gconstpointer v2)
8fced6
 {
8fced6
-    const SMMUIOTLBKey *k1 = v1;
8fced6
-    const SMMUIOTLBKey *k2 = v2;
8fced6
+    SMMUIOTLBKey *k1 = (SMMUIOTLBKey *)v1, *k2 = (SMMUIOTLBKey *)v2;
8fced6
 
8fced6
-    return (k1->asid == k2->asid) && (k1->iova == k2->iova);
8fced6
+    return (k1->asid == k2->asid) && (k1->iova == k2->iova) &&
8fced6
+           (k1->level == k2->level) && (k1->tg == k2->tg);
8fced6
 }
8fced6
 
8fced6
-SMMUIOTLBKey smmu_get_iotlb_key(uint16_t asid, uint64_t iova)
8fced6
+SMMUIOTLBKey smmu_get_iotlb_key(uint16_t asid, uint64_t iova,
8fced6
+                                uint8_t tg, uint8_t level)
8fced6
 {
8fced6
-    SMMUIOTLBKey key = {.asid = asid, .iova = iova};
8fced6
+    SMMUIOTLBKey key = {.asid = asid, .iova = iova, .tg = tg, .level = level};
8fced6
 
8fced6
     return key;
8fced6
 }
8fced6
 
8fced6
 SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg,
8fced6
-                                hwaddr iova)
8fced6
+                                SMMUTransTableInfo *tt, hwaddr iova)
8fced6
 {
8fced6
-    SMMUIOTLBKey key = smmu_get_iotlb_key(cfg->asid, iova);
8fced6
-    SMMUTLBEntry *entry = g_hash_table_lookup(bs->iotlb, &key);
8fced6
+    uint8_t tg = (tt->granule_sz - 10) / 2;
8fced6
+    uint8_t inputsize = 64 - tt->tsz;
8fced6
+    uint8_t stride = tt->granule_sz - 3;
8fced6
+    uint8_t level = 4 - (inputsize - 4) / stride;
8fced6
+    SMMUTLBEntry *entry = NULL;
8fced6
+
8fced6
+    while (level <= 3) {
8fced6
+        uint64_t subpage_size = 1ULL << level_shift(level, tt->granule_sz);
8fced6
+        uint64_t mask = subpage_size - 1;
8fced6
+        SMMUIOTLBKey key;
8fced6
+
8fced6
+        key = smmu_get_iotlb_key(cfg->asid, iova & ~mask, tg, level);
8fced6
+        entry = g_hash_table_lookup(bs->iotlb, &key);
8fced6
+        if (entry) {
8fced6
+            break;
8fced6
+        }
8fced6
+        level++;
8fced6
+    }
8fced6
 
8fced6
     if (entry) {
8fced6
         cfg->iotlb_hits++;
8fced6
@@ -89,13 +106,14 @@ SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg,
8fced6
 void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *new)
8fced6
 {
8fced6
     SMMUIOTLBKey *key = g_new0(SMMUIOTLBKey, 1);
8fced6
+    uint8_t tg = (new->granule - 10) / 2;
8fced6
 
8fced6
     if (g_hash_table_size(bs->iotlb) >= SMMU_IOTLB_MAX_SIZE) {
8fced6
         smmu_iotlb_inv_all(bs);
8fced6
     }
8fced6
 
8fced6
-    *key = smmu_get_iotlb_key(cfg->asid, new->entry.iova);
8fced6
-    trace_smmu_iotlb_insert(cfg->asid, new->entry.iova);
8fced6
+    *key = smmu_get_iotlb_key(cfg->asid, new->entry.iova, tg, new->level);
8fced6
+    trace_smmu_iotlb_insert(cfg->asid, new->entry.iova, tg, new->level);
8fced6
     g_hash_table_insert(bs->iotlb, key, new);
8fced6
 }
8fced6
 
8fced6
@@ -114,12 +132,26 @@ static gboolean smmu_hash_remove_by_asid(gpointer key, gpointer value,
8fced6
     return SMMU_IOTLB_ASID(*iotlb_key) == asid;
8fced6
 }
8fced6
 
8fced6
-inline void smmu_iotlb_inv_iova(SMMUState *s, uint16_t asid, dma_addr_t iova)
8fced6
+static gboolean smmu_hash_remove_by_asid_iova(gpointer key, gpointer value,
8fced6
+                                              gpointer user_data)
8fced6
 {
8fced6
-    SMMUIOTLBKey key = smmu_get_iotlb_key(asid, iova);
8fced6
+    SMMUTLBEntry *iter = (SMMUTLBEntry *)value;
8fced6
+    IOMMUTLBEntry *entry = &iter->entry;
8fced6
+    SMMUIOTLBPageInvInfo *info = (SMMUIOTLBPageInvInfo *)user_data;
8fced6
+    SMMUIOTLBKey iotlb_key = *(SMMUIOTLBKey *)key;
8fced6
+
8fced6
+    if (info->asid >= 0 && info->asid != SMMU_IOTLB_ASID(iotlb_key)) {
8fced6
+        return false;
8fced6
+    }
8fced6
+    return (info->iova & ~entry->addr_mask) == entry->iova;
8fced6
+}
8fced6
+
8fced6
+inline void smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova)
8fced6
+{
8fced6
+    SMMUIOTLBPageInvInfo info = {.asid = asid, .iova = iova};
8fced6
 
8fced6
     trace_smmu_iotlb_inv_iova(asid, iova);
8fced6
-    g_hash_table_remove(s->iotlb, &key);
8fced6
+    g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_asid_iova, &info;;
8fced6
 }
8fced6
 
8fced6
 inline void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid)
8fced6
@@ -247,9 +279,6 @@ static int smmu_ptw_64(SMMUTransCfg *cfg,
8fced6
     baseaddr = extract64(tt->ttb, 0, 48);
8fced6
     baseaddr &= ~indexmask;
8fced6
 
8fced6
-    tlbe->entry.iova = iova;
8fced6
-    tlbe->entry.addr_mask = (1 << granule_sz) - 1;
8fced6
-
8fced6
     while (level <= 3) {
8fced6
         uint64_t subpage_size = 1ULL << level_shift(level, granule_sz);
8fced6
         uint64_t mask = subpage_size - 1;
8fced6
@@ -299,7 +328,9 @@ static int smmu_ptw_64(SMMUTransCfg *cfg,
8fced6
             goto error;
8fced6
         }
8fced6
 
8fced6
-        tlbe->entry.translated_addr = gpa + (iova & mask);
8fced6
+        tlbe->entry.translated_addr = gpa;
8fced6
+        tlbe->entry.iova = iova & ~mask;
8fced6
+        tlbe->entry.addr_mask = mask;
8fced6
         tlbe->entry.perm = PTE_AP_TO_PERM(ap);
8fced6
         tlbe->level = level;
8fced6
         tlbe->granule = granule_sz;
8fced6
diff --git a/hw/arm/smmu-internal.h b/hw/arm/smmu-internal.h
8fced6
index 3104f768cd2..55147f29be4 100644
8fced6
--- a/hw/arm/smmu-internal.h
8fced6
+++ b/hw/arm/smmu-internal.h
8fced6
@@ -97,4 +97,11 @@ uint64_t iova_level_offset(uint64_t iova, int inputsize,
8fced6
 }
8fced6
 
8fced6
 #define SMMU_IOTLB_ASID(key) ((key).asid)
8fced6
+
8fced6
+typedef struct SMMUIOTLBPageInvInfo {
8fced6
+    int asid;
8fced6
+    uint64_t iova;
8fced6
+    uint64_t mask;
8fced6
+} SMMUIOTLBPageInvInfo;
8fced6
+
8fced6
 #endif
8fced6
diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c
8fced6
index ad8212779d3..067c9480a03 100644
8fced6
--- a/hw/arm/smmuv3.c
8fced6
+++ b/hw/arm/smmuv3.c
8fced6
@@ -662,7 +662,7 @@ static IOMMUTLBEntry smmuv3_translate(IOMMUMemoryRegion *mr, hwaddr addr,
8fced6
     page_mask = (1ULL << (tt->granule_sz)) - 1;
8fced6
     aligned_addr = addr & ~page_mask;
8fced6
 
8fced6
-    cached_entry = smmu_iotlb_lookup(bs, cfg, aligned_addr);
8fced6
+    cached_entry = smmu_iotlb_lookup(bs, cfg, tt, aligned_addr);
8fced6
     if (cached_entry) {
8fced6
         if ((flag & IOMMU_WO) && !(cached_entry->entry.perm & IOMMU_WO)) {
8fced6
             status = SMMU_TRANS_ERROR;
8fced6
@@ -732,7 +732,7 @@ epilogue:
8fced6
     case SMMU_TRANS_SUCCESS:
8fced6
         entry.perm = flag;
8fced6
         entry.translated_addr = cached_entry->entry.translated_addr +
8fced6
-                                    (addr & page_mask);
8fced6
+                                    (addr & cached_entry->entry.addr_mask);
8fced6
         entry.addr_mask = cached_entry->entry.addr_mask;
8fced6
         trace_smmuv3_translate_success(mr->parent_obj.name, sid, addr,
8fced6
                                        entry.translated_addr, entry.perm);
8fced6
@@ -960,7 +960,7 @@ static int smmuv3_cmdq_consume(SMMUv3State *s)
8fced6
 
8fced6
             trace_smmuv3_cmdq_tlbi_nh_vaa(vmid, addr);
8fced6
             smmuv3_inv_notifiers_iova(bs, -1, addr);
8fced6
-            smmu_iotlb_inv_all(bs);
8fced6
+            smmu_iotlb_inv_iova(bs, -1, addr);
8fced6
             break;
8fced6
         }
8fced6
         case SMMU_CMD_TLBI_NH_VA:
8fced6
diff --git a/hw/arm/trace-events b/hw/arm/trace-events
8fced6
index b808a1bfc19..f74d3e920f1 100644
8fced6
--- a/hw/arm/trace-events
8fced6
+++ b/hw/arm/trace-events
8fced6
@@ -16,7 +16,7 @@ smmu_iotlb_inv_iova(uint16_t asid, uint64_t addr) "IOTLB invalidate asid=%d addr
8fced6
 smmu_inv_notifiers_mr(const char *name) "iommu mr=%s"
8fced6
 smmu_iotlb_lookup_hit(uint16_t asid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache HIT asid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d"
8fced6
 smmu_iotlb_lookup_miss(uint16_t asid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache MISS asid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d"
8fced6
-smmu_iotlb_insert(uint16_t asid, uint64_t addr) "IOTLB ++ asid=%d addr=0x%"PRIx64
8fced6
+smmu_iotlb_insert(uint16_t asid, uint64_t addr, uint8_t tg, uint8_t level) "IOTLB ++ asid=%d addr=0x%"PRIx64" tg=%d level=%d"
8fced6
 
8fced6
 # smmuv3.c
8fced6
 smmuv3_read_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)"
8fced6
diff --git a/include/hw/arm/smmu-common.h b/include/hw/arm/smmu-common.h
8fced6
index 277923bdc0a..bbf3abc41fd 100644
8fced6
--- a/include/hw/arm/smmu-common.h
8fced6
+++ b/include/hw/arm/smmu-common.h
8fced6
@@ -97,6 +97,8 @@ typedef struct SMMUPciBus {
8fced6
 typedef struct SMMUIOTLBKey {
8fced6
     uint64_t iova;
8fced6
     uint16_t asid;
8fced6
+    uint8_t tg;
8fced6
+    uint8_t level;
8fced6
 } SMMUIOTLBKey;
8fced6
 
8fced6
 typedef struct SMMUState {
8fced6
@@ -159,12 +161,14 @@ IOMMUMemoryRegion *smmu_iommu_mr(SMMUState *s, uint32_t sid);
8fced6
 
8fced6
 #define SMMU_IOTLB_MAX_SIZE 256
8fced6
 
8fced6
-SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg, hwaddr iova);
8fced6
+SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg,
8fced6
+                                SMMUTransTableInfo *tt, hwaddr iova);
8fced6
 void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *entry);
8fced6
-SMMUIOTLBKey smmu_get_iotlb_key(uint16_t asid, uint64_t iova);
8fced6
+SMMUIOTLBKey smmu_get_iotlb_key(uint16_t asid, uint64_t iova,
8fced6
+                                uint8_t tg, uint8_t level);
8fced6
 void smmu_iotlb_inv_all(SMMUState *s);
8fced6
 void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid);
8fced6
-void smmu_iotlb_inv_iova(SMMUState *s, uint16_t asid, dma_addr_t iova);
8fced6
+void smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova);
8fced6
 
8fced6
 /* Unmap the range of all the notifiers registered to any IOMMU mr */
8fced6
 void smmu_inv_notifiers_all(SMMUState *s);
8fced6
-- 
8fced6
2.27.0
8fced6