With two flush queue variants add an IOMMU operation that allows the IOMMU driver to choose its preferred flush queue variant on a per device basis. For s390 use this callback to choose the single queue variant whenever the device requires explicit IOTLB flushes on map indicating that we're running in a paged memory guest with expensive IOTLB flushes. Signed-off-by: Niklas Schnelle <schnelle@xxxxxxxxxxxxx> --- drivers/iommu/iommu.c | 13 +++++++++++++ drivers/iommu/s390-iommu.c | 11 +++++++++++ include/linux/iommu.h | 5 +++++ 3 files changed, 29 insertions(+) diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c index 7ae2ff35b88e..c4699a0e5feb 100644 --- a/drivers/iommu/iommu.c +++ b/drivers/iommu/iommu.c @@ -1616,6 +1616,16 @@ static int iommu_get_def_domain_type(struct device *dev) return 0; } +static int iommu_get_lazy_domain_type(struct device *dev) +{ + const struct iommu_ops *ops = dev_iommu_ops(dev); + + if (ops->lazy_domain_type) + return ops->lazy_domain_type(dev); + + return 0; +} + static int iommu_group_alloc_default_domain(struct bus_type *bus, struct iommu_group *group, unsigned int type) @@ -1649,6 +1659,9 @@ static int iommu_alloc_default_domain(struct iommu_group *group, type = iommu_get_def_domain_type(dev) ? : iommu_def_domain_type; + if (!!(type & __IOMMU_DOMAIN_DMA_LAZY)) + type = iommu_get_lazy_domain_type(dev) ? : type; + return iommu_group_alloc_default_domain(dev->bus, group, type); } diff --git a/drivers/iommu/s390-iommu.c b/drivers/iommu/s390-iommu.c index ff73b75be886..b8aab37e8b15 100644 --- a/drivers/iommu/s390-iommu.c +++ b/drivers/iommu/s390-iommu.c @@ -459,6 +459,16 @@ static void s390_iommu_get_resv_regions(struct device *dev, } } +static int s390_iommu_lazy_domain_type(struct device *dev) +{ + struct zpci_dev *zdev = to_zpci_dev(dev); + + if (zdev->tlb_refresh) + return IOMMU_DOMAIN_DMA_SQ; + + return IOMMU_DOMAIN_DMA_FQ; +} + static struct iommu_device *s390_iommu_probe_device(struct device *dev) { struct zpci_dev *zdev; @@ -798,6 +808,7 @@ static const struct iommu_ops s390_iommu_ops = { .device_group = generic_device_group, .pgsize_bitmap = SZ_4K, .get_resv_regions = s390_iommu_get_resv_regions, + .lazy_domain_type = s390_iommu_lazy_domain_type, .default_domain_ops = &(const struct iommu_domain_ops) { .attach_dev = s390_iommu_attach_device, .detach_dev = s390_iommu_detach_device, diff --git a/include/linux/iommu.h b/include/linux/iommu.h index 74cee59516aa..aec895087f63 100644 --- a/include/linux/iommu.h +++ b/include/linux/iommu.h @@ -250,6 +250,10 @@ struct iommu_iotlb_gather { * - IOMMU_DOMAIN_IDENTITY: must use an identity domain * - IOMMU_DOMAIN_DMA: must use a dma domain * - 0: use the default setting + * @lazy_domain_type: Domain type for lazy TLB invalidation, return value: + * - IOMMU_DOMAIN_DMA_FQ: Use per-CPU flush queue + * - IOMMU_DOMAIN_DMA_SQ: Use single flush queue + * - 0: use the default setting * @default_domain_ops: the default ops for domains * @remove_dev_pasid: Remove any translation configurations of a specific * pasid, so that any DMA transactions with this pasid @@ -283,6 +287,7 @@ struct iommu_ops { struct iommu_page_response *msg); int (*def_domain_type)(struct device *dev); + int (*lazy_domain_type)(struct device *dev); void (*remove_dev_pasid)(struct device *dev, ioasid_t pasid); const struct iommu_domain_ops *default_domain_ops; -- 2.34.1