s390_pci_iommu_disable() destroys the I/O TLB and unpins the host's mappings, but it doesn't account for reclaiming the freed DMA slots. This is problematic when an IOMMU is disabled and re-enabled while reusing the container (e.g. during a guest reboot), which may cause the RPCIT path to fail with DMA slots exhaustion errors, manifesting as unexplained ENOMEMs in the guest.
Fix it by reclaiming all the entries in the I/O TLB's hash table before removing all its entries. The actual DMA slots consumed by the IOMMU are already relinquished to vfio when the memory subregion is deleted in the same function. Cc: [email protected] Fixes: 37fa32de7073 ("s390x/pci: Honor DMA limits set by vfio") Signed-off-by: Omar Elghoul <[email protected]> --- hw/s390x/s390-pci-bus.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 2724b27ec2..cdd75cdfd2 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -826,6 +826,14 @@ void s390_pci_iommu_disable(S390PCIBusDevice *pbdev) { S390PCIIOMMU *iommu = pbdev->iommu; pbdev->iommu_enabled = false; + if (pbdev->dma_limit) { + /* + * Account for all the DMA mappings still in the IOTLB; they will be + * returned to vfio when we delete the memory subregion via + * memory_region_del_subregion() below. + */ + pbdev->dma_limit->avail += g_hash_table_size(pbdev->iotlb); + } g_hash_table_remove_all(pbdev->iotlb); if (pbdev->dm_mr) { memory_region_del_subregion(&iommu->mr, pbdev->dm_mr); -- 2.56.0
