diff options
author | Eric Auger | 2020-07-28 17:08:09 +0200 |
---|---|---|
committer | Peter Maydell | 2020-08-24 11:02:06 +0200 |
commit | 9e54dee71fcfaae69f87b8e1f51485a832266a39 (patch) | |
tree | e59dac56f96c1cd6318d8cd447dc2c3dd21de392 /hw/arm/smmu-common.c | |
parent | hw/arm/smmu: Introduce SMMUTLBEntry for PTW and IOTLB value (diff) | |
download | qemu-9e54dee71fcfaae69f87b8e1f51485a832266a39.tar.gz qemu-9e54dee71fcfaae69f87b8e1f51485a832266a39.tar.xz qemu-9e54dee71fcfaae69f87b8e1f51485a832266a39.zip |
hw/arm/smmu-common: Manage IOTLB block entries
At the moment each entry in the IOTLB corresponds to a page sized
mapping (4K, 16K or 64K), even if the page belongs to a mapped
block. In case of block mapping this unefficiently consumes IOTLB
entries.
Change the value of the entry so that it reflects the actual
mapping it belongs to (block or page start address and size).
Also the level/tg of the entry is encoded in the key. In subsequent
patches we will enable range invalidation. This latter is able
to provide the level/tg of the entry.
Encoding the level/tg directly in the key will allow to invalidate
using g_hash_table_remove() when num_pages equals to 1.
Signed-off-by: Eric Auger <eric.auger@redhat.com>
Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Message-id: 20200728150815.11446-6-eric.auger@redhat.com
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/arm/smmu-common.c')
-rw-r--r-- | hw/arm/smmu-common.c | 67 |
1 files changed, 49 insertions, 18 deletions
diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 398e958bb4..2922deec6f 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -39,7 +39,7 @@ static guint smmu_iotlb_key_hash(gconstpointer v) /* Jenkins hash */ a = b = c = JHASH_INITVAL + sizeof(*key); - a += key->asid; + a += key->asid + key->level + key->tg; b += extract64(key->iova, 0, 32); c += extract64(key->iova, 32, 32); @@ -51,24 +51,41 @@ static guint smmu_iotlb_key_hash(gconstpointer v) static gboolean smmu_iotlb_key_equal(gconstpointer v1, gconstpointer v2) { - const SMMUIOTLBKey *k1 = v1; - const SMMUIOTLBKey *k2 = v2; + SMMUIOTLBKey *k1 = (SMMUIOTLBKey *)v1, *k2 = (SMMUIOTLBKey *)v2; - return (k1->asid == k2->asid) && (k1->iova == k2->iova); + return (k1->asid == k2->asid) && (k1->iova == k2->iova) && + (k1->level == k2->level) && (k1->tg == k2->tg); } -SMMUIOTLBKey smmu_get_iotlb_key(uint16_t asid, uint64_t iova) +SMMUIOTLBKey smmu_get_iotlb_key(uint16_t asid, uint64_t iova, + uint8_t tg, uint8_t level) { - SMMUIOTLBKey key = {.asid = asid, .iova = iova}; + SMMUIOTLBKey key = {.asid = asid, .iova = iova, .tg = tg, .level = level}; return key; } SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg, - hwaddr iova) + SMMUTransTableInfo *tt, hwaddr iova) { - SMMUIOTLBKey key = smmu_get_iotlb_key(cfg->asid, iova); - SMMUTLBEntry *entry = g_hash_table_lookup(bs->iotlb, &key); + uint8_t tg = (tt->granule_sz - 10) / 2; + uint8_t inputsize = 64 - tt->tsz; + uint8_t stride = tt->granule_sz - 3; + uint8_t level = 4 - (inputsize - 4) / stride; + SMMUTLBEntry *entry = NULL; + + while (level <= 3) { + uint64_t subpage_size = 1ULL << level_shift(level, tt->granule_sz); + uint64_t mask = subpage_size - 1; + SMMUIOTLBKey key; + + key = smmu_get_iotlb_key(cfg->asid, iova & ~mask, tg, level); + entry = g_hash_table_lookup(bs->iotlb, &key); + if (entry) { + break; + } + level++; + } if (entry) { cfg->iotlb_hits++; @@ -89,13 +106,14 @@ SMMUTLBEntry *smmu_iotlb_lookup(SMMUState *bs, SMMUTransCfg *cfg, void smmu_iotlb_insert(SMMUState *bs, SMMUTransCfg *cfg, SMMUTLBEntry *new) { SMMUIOTLBKey *key = g_new0(SMMUIOTLBKey, 1); + uint8_t tg = (new->granule - 10) / 2; if (g_hash_table_size(bs->iotlb) >= SMMU_IOTLB_MAX_SIZE) { smmu_iotlb_inv_all(bs); } - *key = smmu_get_iotlb_key(cfg->asid, new->entry.iova); - trace_smmu_iotlb_insert(cfg->asid, new->entry.iova); + *key = smmu_get_iotlb_key(cfg->asid, new->entry.iova, tg, new->level); + trace_smmu_iotlb_insert(cfg->asid, new->entry.iova, tg, new->level); g_hash_table_insert(bs->iotlb, key, new); } @@ -114,12 +132,26 @@ static gboolean smmu_hash_remove_by_asid(gpointer key, gpointer value, return SMMU_IOTLB_ASID(*iotlb_key) == asid; } -inline void smmu_iotlb_inv_iova(SMMUState *s, uint16_t asid, dma_addr_t iova) +static gboolean smmu_hash_remove_by_asid_iova(gpointer key, gpointer value, + gpointer user_data) { - SMMUIOTLBKey key = smmu_get_iotlb_key(asid, iova); + SMMUTLBEntry *iter = (SMMUTLBEntry *)value; + IOMMUTLBEntry *entry = &iter->entry; + SMMUIOTLBPageInvInfo *info = (SMMUIOTLBPageInvInfo *)user_data; + SMMUIOTLBKey iotlb_key = *(SMMUIOTLBKey *)key; + + if (info->asid >= 0 && info->asid != SMMU_IOTLB_ASID(iotlb_key)) { + return false; + } + return (info->iova & ~entry->addr_mask) == entry->iova; +} + +inline void smmu_iotlb_inv_iova(SMMUState *s, int asid, dma_addr_t iova) +{ + SMMUIOTLBPageInvInfo info = {.asid = asid, .iova = iova}; trace_smmu_iotlb_inv_iova(asid, iova); - g_hash_table_remove(s->iotlb, &key); + g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_asid_iova, &info); } inline void smmu_iotlb_inv_asid(SMMUState *s, uint16_t asid) @@ -246,9 +278,6 @@ static int smmu_ptw_64(SMMUTransCfg *cfg, baseaddr = extract64(tt->ttb, 0, 48); baseaddr &= ~indexmask; - tlbe->entry.iova = iova; - tlbe->entry.addr_mask = (1 << granule_sz) - 1; - while (level <= 3) { uint64_t subpage_size = 1ULL << level_shift(level, granule_sz); uint64_t mask = subpage_size - 1; @@ -298,7 +327,9 @@ static int smmu_ptw_64(SMMUTransCfg *cfg, goto error; } - tlbe->entry.translated_addr = gpa + (iova & mask); + tlbe->entry.translated_addr = gpa; + tlbe->entry.iova = iova & ~mask; + tlbe->entry.addr_mask = mask; tlbe->entry.perm = PTE_AP_TO_PERM(ap); tlbe->level = level; tlbe->granule = granule_sz; |