diff options
author | Yi Min Zhao <zyimin@linux.vnet.ibm.com> | 2018-02-05 15:22:57 +0800 |
---|---|---|
committer | Cornelia Huck <cohuck@redhat.com> | 2018-02-09 09:37:13 +0100 |
commit | b3f05d8c7f5ef499e7bf7384d7a0507b8a33afe4 (patch) | |
tree | 6ed5b4cea3e7d858b4c4cec15027b838212e8baa /hw/s390x/s390-pci-inst.c | |
parent | 0125861eacc37478308b462dc9f41438c94fb5e2 (diff) |
s390x/pci: fixup global refresh
The VFIO common code doesn't provide the possibility to modify a
previous mapping entry in another way than unmapping and mapping again
with new properties.
To avoid -EEXIST DMA mapping error, we introduce a GHashTable to store
S390IOTLBEntry instances in order to cache the mapped entries. When
intercepting rpcit instruction, ignore the identical mapped entries to
avoid doing map operations multiple times and do unmap and re-map
operations for the case of updating the valid entries.
Acked-by: Pierre Morel <pmorel@linux.vnet.ibm.com>
Signed-off-by: Yi Min Zhao <zyimin@linux.vnet.ibm.com>
Message-Id: <20180205072258.5968-3-zyimin@linux.vnet.ibm.com>
Signed-off-by: Cornelia Huck <cohuck@redhat.com>
Diffstat (limited to 'hw/s390x/s390-pci-inst.c')
-rw-r--r-- | hw/s390x/s390-pci-inst.c | 53 |
1 files changed, 45 insertions, 8 deletions
diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 1d33a89351..997a9cc2e9 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -571,6 +571,45 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) return 0; } +static void s390_pci_update_iotlb(S390PCIIOMMU *iommu, S390IOTLBEntry *entry) +{ + S390IOTLBEntry *cache = g_hash_table_lookup(iommu->iotlb, &entry->iova); + IOMMUTLBEntry notify = { + .target_as = &address_space_memory, + .iova = entry->iova, + .translated_addr = entry->translated_addr, + .perm = entry->perm, + .addr_mask = ~PAGE_MASK, + }; + + if (entry->perm == IOMMU_NONE) { + if (!cache) { + return; + } + g_hash_table_remove(iommu->iotlb, &entry->iova); + } else { + if (cache) { + if (cache->perm == entry->perm && + cache->translated_addr == entry->translated_addr) { + return; + } + + notify.perm = IOMMU_NONE; + memory_region_notify_iommu(&iommu->iommu_mr, notify); + notify.perm = entry->perm; + } + + cache = g_new(S390IOTLBEntry, 1); + cache->iova = entry->iova; + cache->translated_addr = entry->translated_addr; + cache->len = PAGE_SIZE; + cache->perm = entry->perm; + g_hash_table_replace(iommu->iotlb, &cache->iova, cache); + } + + memory_region_notify_iommu(&iommu->iommu_mr, notify); +} + int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) { CPUS390XState *env = &cpu->env; @@ -580,7 +619,6 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) S390PCIIOMMU *iommu; S390IOTLBEntry entry; hwaddr start, end; - IOMMUTLBEntry notify; cpu_synchronize_state(CPU(cpu)); @@ -636,15 +674,14 @@ int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) if (error) { break; } - notify.target_as = &address_space_memory; - notify.iova = entry.iova; - notify.translated_addr = entry.translated_addr; - notify.addr_mask = entry.len - 1; - notify.perm = entry.perm; - memory_region_notify_iommu(&iommu->iommu_mr, notify); + start += entry.len; + while (entry.iova < start && entry.iova < end) { + s390_pci_update_iotlb(iommu, &entry); + entry.iova += PAGE_SIZE; + entry.translated_addr += PAGE_SIZE; + } } - err: if (error) { pbdev->state = ZPCI_FS_ERROR; |