Blob Blame History Raw
From: Sebastian Ott <sebott@linux.vnet.ibm.com>
Subject: s390/pci: handle insufficient resources during dma tlb flush
Patch-mainline: v4.15-rc7
Git-commit: a5f1005517534aeb1fac20180badfbf0896c183c
References: bnc#1072915, LTC#163393

Description:  s390/pci: improve lazy IOTLB flushing mode
Symptom:      Some PCI workloads don't reach full
              performance potential.
Problem:      Even with lazy IOTLB flushing it is done
              more often than needed.
Solution:     Improve that and be as lazy as possible.
Reproduction: -

Upstream-Description:

              s390/pci: handle insufficient resources during dma tlb flush

              In a virtualized setup lazy flushing can lead to the hypervisor
              running out of resources when lots of guest pages need to be
              pinned. In this situation simply trigger a global flush to give
              the hypervisor a chance to free some of these resources.

              Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
              Reviewed-by: Gerald Schaefer <gerald.schaefer@de.ibm.com>
              Reviewed-by: Pierre Morel <pmorel@linux.vnet.ibm.com>
              Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>


Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Acked-by: Johannes Thumshirn <jthumshirn@suse.de>
---
 arch/s390/pci/pci_dma.c  |   21 +++++++++++++++++++--
 arch/s390/pci/pci_insn.c |    3 +++
 2 files changed, 22 insertions(+), 2 deletions(-)

--- a/arch/s390/pci/pci_dma.c
+++ b/arch/s390/pci/pci_dma.c
@@ -178,6 +178,9 @@ out_unlock:
 static int __dma_purge_tlb(struct zpci_dev *zdev, dma_addr_t dma_addr,
 			   size_t size, int flags)
 {
+	unsigned long irqflags;
+	int ret;
+
 	/*
 	 * With zdev->tlb_refresh == 0, rpcit is not required to establish new
 	 * translations when previously invalid translation-table entries are
@@ -193,8 +196,22 @@ static int __dma_purge_tlb(struct zpci_d
 			return 0;
 	}
 
-	return zpci_refresh_trans((u64) zdev->fh << 32, dma_addr,
-				  PAGE_ALIGN(size));
+	ret = zpci_refresh_trans((u64) zdev->fh << 32, dma_addr,
+				 PAGE_ALIGN(size));
+	if (ret == -ENOMEM && !s390_iommu_strict) {
+		/* enable the hypervisor to free some resources */
+		if (zpci_refresh_global(zdev))
+			goto out;
+
+		spin_lock_irqsave(&zdev->iommu_bitmap_lock, irqflags);
+		bitmap_andnot(zdev->iommu_bitmap, zdev->iommu_bitmap,
+			      zdev->lazy_bitmap, zdev->iommu_pages);
+		bitmap_zero(zdev->lazy_bitmap, zdev->iommu_pages);
+		spin_unlock_irqrestore(&zdev->iommu_bitmap_lock, irqflags);
+		ret = 0;
+	}
+out:
+	return ret;
 }
 
 static int dma_update_trans(struct zpci_dev *zdev, unsigned long pa,
--- a/arch/s390/pci/pci_insn.c
+++ b/arch/s390/pci/pci_insn.c
@@ -87,6 +87,9 @@ int zpci_refresh_trans(u64 fn, u64 addr,
 	if (cc)
 		zpci_err_insn(cc, status, addr, range);
 
+	if (cc == 1 && (status == 4 || status == 16))
+		return -ENOMEM;
+
 	return (cc) ? -EIO : 0;
 }