On 10/21/26 1:50 PM, Omar Elghoul wrote:
> 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]>

Reviewed-by: Matthew Rosato <[email protected]>
Tested-by: Matthew Rosato <[email protected]>

Thanks Omar, I like this alot better for a fix!  It is minimal and
accounts for exactly what was not getting reclaimed.

This approach does lose the re-validation with the vfio-reported value
as compared to your prior approach, which might be handy for detecting
any other leaks in the future; but that is something we could consider
adding as a non-fix add-on later.

Thanks,
Matt

> ---
>  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);


Reply via email to