From: Omar Elghoul <[email protected]> When s390_pci_update_iotlb() does a mapping update for an IOVA that has an already-existing TLB entry with different permissions or translated address, it unmaps then remaps it. However, at the end of the map path, it unconditionally decrements the available DMA slot counter without a corresponding increment in the intermediate unmap branch.
This causes the DMA slot count to be decremented on every remapping of an active IOVA, leading to a permanent DMA slot leak. This remapping without a prior invalidation and sync is not seen today in well-behaved guests but is allowed by the architecture. Fix it by only decrementing available DMA slots when inserting a brand new mapping. Cc: [email protected] Fixes: 37fa32de70 ("s390x/pci: Honor DMA limits set by vfio") Signed-off-by: Omar Elghoul <[email protected]> Reviewed-by: Farhan Ali <[email protected]> Reviewed-by: Matthew Rosato <[email protected]> Link: https://lore.kernel.org/qemu-devel/[email protected] Signed-off-by: Eric Farman <[email protected]> (cherry picked from commit 9b64b3cc833eaec290e65bc0958aeb51c6892152) Signed-off-by: Michael Tokarev <[email protected]> diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 3513a93b9d1..d9eb9c9efa0 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -653,6 +653,7 @@ static uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu, goto out; } else { if (cache) { + /* valid->valid transitions reuse the DMA slot */ if (cache->perm == entry->perm && cache->translated_addr == entry->translated_addr) { goto out; @@ -663,6 +664,9 @@ static uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu, memory_region_notify_iommu(&iommu->iommu_mr, 0, event); event.type = IOMMU_NOTIFIER_MAP; event.entry.perm = entry->perm; + } else { + /* invalid->valid transitions consume a new DMA slot */ + dec_dma_avail(iommu); } cache = g_new(S390IOTLBEntry, 1); @@ -671,7 +675,6 @@ static uint32_t s390_pci_update_iotlb(S390PCIIOMMU *iommu, cache->len = TARGET_PAGE_SIZE; cache->perm = entry->perm; g_hash_table_replace(iommu->iotlb, &cache->iova, cache); - dec_dma_avail(iommu); } /* -- 2.47.3
