diff options
| author | Sebastian Ott <[email protected]> | 2017-12-01 18:47:32 +0100 | 
|---|---|---|
| committer | Martin Schwidefsky <[email protected]> | 2017-12-13 10:51:33 +0100 | 
| commit | a5f1005517534aeb1fac20180badfbf0896c183c (patch) | |
| tree | 24c450680c2879193ff6d12f71411c3a479c4919 | |
| parent | d39a01eff9af1045f6e30ff9db40310517c4b45f (diff) | |
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 <[email protected]>
Reviewed-by: Gerald Schaefer <[email protected]>
Reviewed-by: Pierre Morel <[email protected]>
Signed-off-by: Martin Schwidefsky <[email protected]>
| -rw-r--r-- | arch/s390/pci/pci_dma.c | 21 | ||||
| -rw-r--r-- | arch/s390/pci/pci_insn.c | 3 | 
2 files changed, 22 insertions, 2 deletions
| diff --git a/arch/s390/pci/pci_dma.c b/arch/s390/pci/pci_dma.c index f7aa5a77827e..2d15d84c20ed 100644 --- a/arch/s390/pci/pci_dma.c +++ b/arch/s390/pci/pci_dma.c @@ -181,6 +181,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 @@ -196,8 +199,22 @@ static int __dma_purge_tlb(struct zpci_dev *zdev, dma_addr_t dma_addr,  			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, diff --git a/arch/s390/pci/pci_insn.c b/arch/s390/pci/pci_insn.c index 19bcb3b45a70..f069929e8211 100644 --- a/arch/s390/pci/pci_insn.c +++ b/arch/s390/pci/pci_insn.c @@ -89,6 +89,9 @@ int zpci_refresh_trans(u64 fn, u64 addr, u64 range)  	if (cc)  		zpci_err_insn(cc, status, addr, range); +	if (cc == 1 && (status == 4 || status == 16)) +		return -ENOMEM; +  	return (cc) ? -EIO : 0;  } |