Skip to contentSkip to conversation

iommu: Avoid DMA ops domain refcount contention

PATCH New Robin Murphy 5 participants 14 messages Aug 14, 2018 – Aug 20, 2018

Subsystems (3)
lore
Share & tools
lore

Robin Murphy

author maintainer
#0 lore raw

cover iommu: Avoid DMA ops domain refcount contention

John raised the issue[1] that we have some unnecessary refcount contention in the DMA ops path which shows scalability problems now that we have more real high-performance hardware using iommu-dma. The x86 IOMMU drivers are sidestepping this by stashing domain references in archdata, but since that's not very nice for architecture-agnostic code, I think it's time to look at a generic API-level solution.

These are a couple of quick patches based on the idea I had back when first implementing this lot, but didn't have any way to justify at the time. The third patch can be ignored for the sake of API discussion, but is included for completeness.

Robin.

[1] https://lists.linuxfoundation.org/pipermail/iommu/2018-August/029303.html

Robin Murphy (3):
iommu: Add fast hook for getting DMA domains iommu/dma: Use fast DMA domain lookup arm64/dma-mapping: Mildly optimise non-coherent IOMMU ops

 arch/arm64/mm/dma-mapping.c | 10 +++++-----
 drivers/iommu/dma-iommu.c   | 23 ++++++++++++-----------
 drivers/iommu/iommu.c       |  9 +++++++++
 include/linux/iommu.h       |  1 +
 4 files changed, 27 insertions(+), 16 deletions(-)
Signature · 1 line
2.17.1.dirty
author maintainer
#1 lore raw

PATCH 1/3 iommu: Add fast hook for getting DMA domains

While iommu_get_domain_for_dev() is the robust way for arbitrary IOMMU API callers to retrieve the domain pointer, for DMA ops domains it doesn't scale well for large systems and multi-queue devices, since the momentary refcount adjustment will lead to exclusive cacheline contention when multiple CPUs are operating in parallel on different mappings for the same device.

In the case of DMA ops domains, however, this refcounting is actually unnecessary, since they already imply that the group exists and is managed by platform code and IOMMU internals (by virtue of iommu_group_get_for_dev()) such that a reference will already be held for the lifetime of the device. Thus we can avoid the bottleneck by providing a fast lookup specifically for the DMA code to retrieve the default domain it already knows it has set up - a simple read-only dereference plays much nicer with cache-coherency protocols.

---
 drivers/iommu/iommu.c | 9 +++++++++
 include/linux/iommu.h | 1 +
 2 files changed, 10 insertions(+)
drivers/iommu/iommu.c +9 -0
index 63b37563db7e..63c586875df5 100644
@@ -1379,6 +1379,15 @@ struct iommu_domain *iommu_get_domain_for_dev(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(iommu_get_domain_for_dev);
 
+/*
+ * For IOMMU_DOMAIN_DMA implementations which already provide their own
+ * guarantees that the group and its default domain are valid and correct.
+ */
+struct iommu_domain *iommu_get_dma_domain(struct device *dev)
+{
+	return dev->iommu_group->default_domain;
+}
+
 /*
  * IOMMU groups are really the natrual working unit of the IOMMU, but
  * the IOMMU API works on domains and devices.  Bridge that gap by
include/linux/iommu.h +1 -0
index 19938ee6eb31..16f2172698e5 100644
@@ -297,6 +297,7 @@ extern int iommu_attach_device(struct iommu_domain *domain,
 extern void iommu_detach_device(struct iommu_domain *domain,
 				struct device *dev);
 extern struct iommu_domain *iommu_get_domain_for_dev(struct device *dev);
+extern struct iommu_domain *iommu_get_dma_domain(struct device *dev);
 extern int iommu_map(struct iommu_domain *domain, unsigned long iova,
 		     phys_addr_t paddr, size_t size, int prot);
 extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova,
Signature · 1 line
2.17.1.dirty
#2 lore raw

PATCH 1/3 iommu: Add fast hook for getting DMA domains

Quoted 47 lines · Robin Murphy

While iommu_get_domain_for_dev() is the robust way for arbitrary IOMMU API callers to retrieve the domain pointer, for DMA ops domains it doesn't scale well for large systems and multi-queue devices, since the momentary refcount adjustment will lead to exclusive cacheline contention when multiple CPUs are operating in parallel on different mappings for the same device.

In the case of DMA ops domains, however, this refcounting is actually unnecessary, since they already imply that the group exists and is managed by platform code and IOMMU internals (by virtue of iommu_group_get_for_dev()) such that a reference will already be held for the lifetime of the device. Thus we can avoid the bottleneck by providing a fast lookup specifically for the DMA code to retrieve the default domain it already knows it has set up - a simple read-only dereference plays much nicer with cache-coherency protocols.

---
 drivers/iommu/iommu.c | 9 +++++++++
 include/linux/iommu.h | 1 +
 2 files changed, 10 insertions(+)
drivers/iommu/iommu.c +9 -0
index 63b37563db7e..63c586875df5 100644
@@ -1379,6 +1379,15 @@ struct iommu_domain *iommu_get_domain_for_dev(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(iommu_get_domain_for_dev);
 
+/*
+ * For IOMMU_DOMAIN_DMA implementations which already provide their own
+ * guarantees that the group and its default domain are valid and correct.
+ */
+struct iommu_domain *iommu_get_dma_domain(struct device *dev)
+{
+	return dev->iommu_group->default_domain;
+}
+
 /*
  * IOMMU groups are really the natrual working unit of the IOMMU, but
  * the IOMMU API works on domains and devices.  Bridge that gap by
include/linux/iommu.h +0 -0
index 19938ee6eb31..16f2172698e5 100644

include/linux/iommu.h extern int iommu_attach_device(struct iommu_domain *domain, · line 300

Quoted 4 lines
include/linux/iommu.h +1 -0
@@ -297,6 +297,7 @@ extern int iommu_attach_device(struct iommu_domain *domain,
 extern void iommu_detach_device(struct iommu_domain *domain,
 				struct device *dev);
 extern struct iommu_domain *iommu_get_domain_for_dev(struct device *dev);
+extern struct iommu_domain *iommu_get_dma_domain(struct device *dev);

Hi Robin,

I was wondering whether it's standard to provide a stubbed version of this function for !CONFIG_IOMMU_API?

Cheers,
John

Quoted 4 lines
include/linux/iommu.h +0 -0
 extern int iommu_map(struct iommu_domain *domain, unsigned long iova,
 		     phys_addr_t paddr, size_t size, int prot);
 extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova,
author maintainer
#3 lore raw

PATCH 1/3 iommu: Add fast hook for getting DMA domains

Quoted 59 lines · John Garry
Quoted 52 lines · Robin Murphy

While iommu_get_domain_for_dev() is the robust way for arbitrary IOMMU API callers to retrieve the domain pointer, for DMA ops domains it doesn't scale well for large systems and multi-queue devices, since the momentary refcount adjustment will lead to exclusive cacheline contention when multiple CPUs are operating in parallel on different mappings for the same device.

In the case of DMA ops domains, however, this refcounting is actually unnecessary, since they already imply that the group exists and is managed by platform code and IOMMU internals (by virtue of iommu_group_get_for_dev()) such that a reference will already be held for the lifetime of the device. Thus we can avoid the bottleneck by providing a fast lookup specifically for the DMA code to retrieve the default domain it already knows it has set up - a simple read-only dereference plays much nicer with cache-coherency protocols.

---
?drivers/iommu/iommu.c | 9 +++++++++
?include/linux/iommu.h | 1 +
?2 files changed, 10 insertions(+)

drivers/iommu/iommu.c +3 -0
index 63b37563db7e..63c586875df5 100644
@@ -1379,6 +1379,15 @@ struct iommu_domain 
 *iommu_get_domain_for_dev(struct device *dev)
 ?}
 ?EXPORT_SYMBOL_GPL(iommu_get_domain_for_dev);
 
+/*
+ * For IOMMU_DOMAIN_DMA implementations which already provide their own
+ * guarantees that the group and its default domain are valid and 

correct.
+ */

drivers/iommu/iommu.c +5 -0
+struct iommu_domain *iommu_get_dma_domain(struct device *dev)
+{
+??? return dev->iommu_group->default_domain;
+}
+

?/*
? * IOMMU groups are really the natrual working unit of the IOMMU, but ? * the IOMMU API works on domains and devices.? Bridge that gap by

include/linux/iommu.h +0 -0
index 19938ee6eb31..16f2172698e5 100644
@@ -297,6 +297,7 @@ extern int iommu_attach_device(struct iommu_domain 
 *domain,
 ?extern void iommu_detach_device(struct iommu_domain *domain,
 ???????????????? struct device *dev);
Quoted 3 lines

?extern struct iommu_domain *iommu_get_domain_for_dev(struct device *dev);
+extern struct iommu_domain *iommu_get_dma_domain(struct device *dev);

Hi Robin,

Quoted 2 lines

I was wondering whether it's standard to provide a stubbed version of this function for !CONFIG_IOMMU_API?

Nope, that's deliberate - this is one of those hooks where any legitimate caller will already be dependent on IOMMU_API itself.

Robin.

Quoted 11 lines

Cheers,
John

Quoted 5 lines

?extern int iommu_map(struct iommu_domain *domain, unsigned long iova, ????????????? phys_addr_t paddr, size_t size, int prot); ?extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova,

#4 lore raw

PATCH 1/3 iommu: Add fast hook for getting DMA domains

In reply to Robin Murphy

Hi Robin,

Quoted 28 lines · Robin Murphy

While iommu_get_domain_for_dev() is the robust way for arbitrary IOMMU API callers to retrieve the domain pointer, for DMA ops domains it doesn't scale well for large systems and multi-queue devices, since the momentary refcount adjustment will lead to exclusive cacheline contention when multiple CPUs are operating in parallel on different mappings for the same device.

In the case of DMA ops domains, however, this refcounting is actually unnecessary, since they already imply that the group exists and is managed by platform code and IOMMU internals (by virtue of iommu_group_get_for_dev()) such that a reference will already be held for the lifetime of the device. Thus we can avoid the bottleneck by providing a fast lookup specifically for the DMA code to retrieve the default domain it already knows it has set up - a simple read-only dereference plays much nicer with cache-coherency protocols.

---
  drivers/iommu/iommu.c | 9 +++++++++
  include/linux/iommu.h | 1 +
  2 files changed, 10 insertions(+)
drivers/iommu/iommu.c +0 -0
index 63b37563db7e..63c586875df5 100644
@@ -1379,6 +1379,15 @@ struct iommu_domain *iommu_get_domain_for_dev(struct device *dev)
  }

drivers/iommu/iommu.c struct iommu_domain *iommu_get_domain_for_dev(struct device *dev) · line 1389

Quoted 10 lines
drivers/iommu/iommu.c +8 -0
  EXPORT_SYMBOL_GPL(iommu_get_domain_for_dev);
 
+/*
+ * For IOMMU_DOMAIN_DMA implementations which already provide their own
+ * guarantees that the group and its default domain are valid and correct.
+ */
+struct iommu_domain *iommu_get_dma_domain(struct device *dev)
+{
+	return dev->iommu_group->default_domain;
+}

After some preliminary tests I'm seeing a ~10% performance improvement on one of our chips (nxp ls1046a) which is pretty nice. :-) Any chance of making the function inline?
If not, shouldn't an EXPORT_SYMBOL_GPL be added?

---
Thanks & Best Regards, Laurentiu

author maintainer
#5 lore raw

PATCH 2/3 iommu/dma: Use fast DMA domain lookup

In reply to Robin Murphy

Most parts of iommu-dma already assume they are operating on a default domain set up by iommu_dma_init_domain(), and can be converted straight over to avoid the refcounting bottleneck. MSI page mappings may be in an unmanaged domain with an explicit MSI-only cookie, so retain the non-specific lookup, but that's OK since they're far from a contended fast path either way.

---
 drivers/iommu/dma-iommu.c | 23 ++++++++++++-----------
 1 file changed, 12 insertions(+), 11 deletions(-)
drivers/iommu/dma-iommu.c +12 -11
index 8f549bdb866d..a27eedc16b32 100644
@@ -510,7 +510,7 @@ static struct page **__iommu_dma_alloc_pages(unsigned int count,
 void iommu_dma_free(struct device *dev, struct page **pages, size_t size,
 		dma_addr_t *handle)
 {
-	__iommu_dma_unmap(iommu_get_domain_for_dev(dev), *handle, size);
+	__iommu_dma_unmap(iommu_get_dma_domain(dev), *handle, size);
 	__iommu_dma_free_pages(pages, PAGE_ALIGN(size) >> PAGE_SHIFT);
 	*handle = IOMMU_MAPPING_ERROR;
 }
@@ -537,7 +537,7 @@ struct page **iommu_dma_alloc(struct device *dev, size_t size, gfp_t gfp,
 		unsigned long attrs, int prot, dma_addr_t *handle,
 		void (*flush_page)(struct device *, const void *, phys_addr_t))
 {
-	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
+	struct iommu_domain *domain = iommu_get_dma_domain(dev);
 	struct iommu_dma_cookie *cookie = domain->iova_cookie;
 	struct iova_domain *iovad = &cookie->iovad;
 	struct page **pages;
@@ -625,9 +625,8 @@ int iommu_dma_mmap(struct page **pages, size_t size, struct vm_area_struct *vma)
 }
 
 static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys,
-		size_t size, int prot)
+		size_t size, int prot, struct iommu_domain *domain)
 {
-	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
 	struct iommu_dma_cookie *cookie = domain->iova_cookie;
 	size_t iova_off = 0;
 	dma_addr_t iova;
@@ -651,13 +650,14 @@ static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys,
 dma_addr_t iommu_dma_map_page(struct device *dev, struct page *page,
 		unsigned long offset, size_t size, int prot)
 {
-	return __iommu_dma_map(dev, page_to_phys(page) + offset, size, prot);
+	return __iommu_dma_map(dev, page_to_phys(page) + offset, size, prot,
+			iommu_get_dma_domain(dev));
 }
 
 void iommu_dma_unmap_page(struct device *dev, dma_addr_t handle, size_t size,
 		enum dma_data_direction dir, unsigned long attrs)
 {
-	__iommu_dma_unmap(iommu_get_domain_for_dev(dev), handle, size);
+	__iommu_dma_unmap(iommu_get_dma_domain(dev), handle, size);
 }
 
 /*
@@ -745,7 +745,7 @@ static void __invalidate_sg(struct scatterlist *sg, int nents)
 int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
 		int nents, int prot)
 {
-	struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
+	struct iommu_domain *domain = iommu_get_dma_domain(dev);
 	struct iommu_dma_cookie *cookie = domain->iova_cookie;
 	struct iova_domain *iovad = &cookie->iovad;
 	struct scatterlist *s, *prev = NULL;
@@ -830,20 +830,21 @@ void iommu_dma_unmap_sg(struct device *dev, struct scatterlist *sg, int nents,
 		sg = tmp;
 	}
 	end = sg_dma_address(sg) + sg_dma_len(sg);
-	__iommu_dma_unmap(iommu_get_domain_for_dev(dev), start, end - start);
+	__iommu_dma_unmap(iommu_get_dma_domain(dev), start, end - start);
 }
 
 dma_addr_t iommu_dma_map_resource(struct device *dev, phys_addr_t phys,
 		size_t size, enum dma_data_direction dir, unsigned long attrs)
 {
 	return __iommu_dma_map(dev, phys, size,
-			dma_info_to_prot(dir, false, attrs) | IOMMU_MMIO);
+			dma_info_to_prot(dir, false, attrs) | IOMMU_MMIO,
+			iommu_get_dma_domain(dev));
 }
 
 void iommu_dma_unmap_resource(struct device *dev, dma_addr_t handle,
 		size_t size, enum dma_data_direction dir, unsigned long attrs)
 {
-	__iommu_dma_unmap(iommu_get_domain_for_dev(dev), handle, size);
+	__iommu_dma_unmap(iommu_get_dma_domain(dev), handle, size);
 }
 
 int iommu_dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
@@ -869,7 +870,7 @@ static struct iommu_dma_msi_page *iommu_dma_get_msi_page(struct device *dev,
 	if (!msi_page)
 		return NULL;
 
-	iova = __iommu_dma_map(dev, msi_addr, size, prot);
+	iova = __iommu_dma_map(dev, msi_addr, size, prot, domain);
 	if (iommu_dma_mapping_error(dev, iova))
 		goto out_free_page;
 
Signature · 1 line
2.17.1.dirty
author maintainer
#6 lore raw

PATCH 3/3 arm64/dma-mapping: Mildly optimise non-coherent IOMMU ops

In reply to Robin Murphy

Whilst the symmetry of deferring to the existing sync callback in __iommu_map_page() is nice, taking a round-trip through iommu_iova_to_phys() is a pretty heavyweight way to get an address we can trivially compute from the page we already have. Tweaking it to just perform the cache maintenance directly when appropriate doesn't really make the code any more complicated, and the runtime efficiency gain can only be a benefit.

Furthermore, the sync operations themselves know they can only be invoked on a managed DMA ops domain, so can use the fast specific domain lookup to avoid excessive manipulation of the group refcount (particularly in the scatterlist cases).

---
 arch/arm64/mm/dma-mapping.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)
arch/arm64/mm/dma-mapping.c +5 -5
index 61e93f0b5482..5d4144093c20 100644
@@ -712,7 +712,7 @@ static void __iommu_sync_single_for_cpu(struct device *dev,
 	if (is_device_dma_coherent(dev))
 		return;
 
-	phys = iommu_iova_to_phys(iommu_get_domain_for_dev(dev), dev_addr);
+	phys = iommu_iova_to_phys(iommu_get_dma_domain(dev), dev_addr);
 	__dma_unmap_area(phys_to_virt(phys), size, dir);
 }
 
@@ -725,7 +725,7 @@ static void __iommu_sync_single_for_device(struct device *dev,
 	if (is_device_dma_coherent(dev))
 		return;
 
-	phys = iommu_iova_to_phys(iommu_get_domain_for_dev(dev), dev_addr);
+	phys = iommu_iova_to_phys(iommu_get_dma_domain(dev), dev_addr);
 	__dma_map_area(phys_to_virt(phys), size, dir);
 }
 
@@ -738,9 +738,9 @@ static dma_addr_t __iommu_map_page(struct device *dev, struct page *page,
 	int prot = dma_info_to_prot(dir, coherent, attrs);
 	dma_addr_t dev_addr = iommu_dma_map_page(dev, page, offset, size, prot);
 
-	if (!iommu_dma_mapping_error(dev, dev_addr) &&
-	    (attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
-		__iommu_sync_single_for_device(dev, dev_addr, size, dir);
+	if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
+	    !iommu_dma_mapping_error(dev, dev_addr))
+		__dma_map_area(page_address(page), size, dir);
 
 	return dev_addr;
 }
Signature · 1 line
2.17.1.dirty
author maintainer
#7 lore raw

PATCH 3/3 arm64/dma-mapping: Mildly optimise non-coherent IOMMU ops

Quoted 41 lines · Robin Murphy

Whilst the symmetry of deferring to the existing sync callback in __iommu_map_page() is nice, taking a round-trip through iommu_iova_to_phys() is a pretty heavyweight way to get an address we can trivially compute from the page we already have. Tweaking it to just perform the cache maintenance directly when appropriate doesn't really make the code any more complicated, and the runtime efficiency gain can only be a benefit.

Furthermore, the sync operations themselves know they can only be invoked on a managed DMA ops domain, so can use the fast specific domain lookup to avoid excessive manipulation of the group refcount (particularly in the scatterlist cases).

---
  arch/arm64/mm/dma-mapping.c | 10 +++++-----
  1 file changed, 5 insertions(+), 5 deletions(-)
arch/arm64/mm/dma-mapping.c +2 -2
index 61e93f0b5482..5d4144093c20 100644
@@ -712,7 +712,7 @@ static void __iommu_sync_single_for_cpu(struct device *dev,
  	if (is_device_dma_coherent(dev))
  		return;
 
-	phys = iommu_iova_to_phys(iommu_get_domain_for_dev(dev), dev_addr);
+	phys = iommu_iova_to_phys(iommu_get_dma_domain(dev), dev_addr);
  	__dma_unmap_area(phys_to_virt(phys), size, dir);
  }
 
@@ -725,7 +725,7 @@ static void __iommu_sync_single_for_device(struct device *dev,
  	if (is_device_dma_coherent(dev))
  		return;
 
-	phys = iommu_iova_to_phys(iommu_get_domain_for_dev(dev), dev_addr);
+	phys = iommu_iova_to_phys(iommu_get_dma_domain(dev), dev_addr);
  	__dma_map_area(phys_to_virt(phys), size, dir);
  }
 

arch/arm64/mm/dma-mapping.c static dma_addr_t __iommu_map_page(struct device *dev, struct page *page, · line 743

Quoted 9 lines
arch/arm64/mm/dma-mapping.c +3 -3
@@ -738,9 +738,9 @@ static dma_addr_t __iommu_map_page(struct device *dev, struct page *page,
  	int prot = dma_info_to_prot(dir, coherent, attrs);
  	dma_addr_t dev_addr = iommu_dma_map_page(dev, page, offset, size, prot);
 
-	if (!iommu_dma_mapping_error(dev, dev_addr) &&
-	    (attrs & DMA_ATTR_SKIP_CPU_SYNC) == 0)
-		__iommu_sync_single_for_device(dev, dev_addr, size, dir);
+	if (!coherent && !(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
+	    !iommu_dma_mapping_error(dev, dev_addr))
+		__dma_map_area(page_address(page), size, dir);

+ offset

Sigh...

And there I was assuming that if I'd bothered to write up a proper commit message for the 15-month-old change I was cherry-picking, I must have actually tested it at the time.

Robin.

Quoted 4 lines
arch/arm64/mm/dma-mapping.c +0 -0
  	return dev_addr;
  }
 
#8 lore raw

cover iommu: Avoid DMA ops domain refcount contention

In reply to Robin Murphy
Quoted 22 lines · Robin Murphy

John raised the issue[1] that we have some unnecessary refcount contention in the DMA ops path which shows scalability problems now that we have more real high-performance hardware using iommu-dma. The x86 IOMMU drivers are sidestepping this by stashing domain references in archdata, but since that's not very nice for architecture-agnostic code, I think it's time to look at a generic API-level solution.

These are a couple of quick patches based on the idea I had back when first implementing this lot, but didn't have any way to justify at the time. The third patch can be ignored for the sake of API discussion, but is included for completeness.

Robin.

[1] https://lists.linuxfoundation.org/pipermail/iommu/2018-August/029303.html

Robin Murphy (3):
iommu: Add fast hook for getting DMA domains iommu/dma: Use fast DMA domain lookup arm64/dma-mapping: Mildly optimise non-coherent IOMMU ops

Quoted 6 lines
 arch/arm64/mm/dma-mapping.c | 10 +++++-----
 drivers/iommu/dma-iommu.c   | 23 ++++++++++++-----------
 drivers/iommu/iommu.c       |  9 +++++++++
 include/linux/iommu.h       |  1 +
 4 files changed, 27 insertions(+), 16 deletions(-)

Thanks Robin. So we'll test this now. I anticipate a decent improvement across the board since we are now simply dereferencing the device's iommu group directly.

Cheers,
John

#9 lore raw

cover iommu: Avoid DMA ops domain refcount contention

In reply to Robin Murphy

I plan to make the arm iommu dma ops generic and move them to drivers/iommu for the 4.20 merge window. Because of that it would be great to create a stable branch or even pull this in through the dma-mapping tree entirely.

#10 lore raw

cover iommu: Avoid DMA ops domain refcount contention

Quoted 4 lines · Christoph Hellwig

I plan to make the arm iommu dma ops generic and move them to drivers/iommu for the 4.20 merge window. Because of that it would be great to create a stable branch or even pull this in through the dma-mapping tree entirely.

In which case, for the arm64 bits:

Will

author maintainer
#11 lore raw

cover iommu: Avoid DMA ops domain refcount contention

In reply to Christoph Hellwig
Quoted 2 lines · Christoph Hellwig

I plan to make the arm iommu dma ops generic and move them to drivers/iommu for the 4.20 merge window.

You mean 32-bit arm? The only place that code should move to is /dev/null ;) - the plan has always been to convert it to use groups and default domains so it can just plug iommu-dma in. The tricky bit is either weaning the users of the private arm_iommu_*() API onto generic IOMMU API usage, or at least implementing transitional wrappers in a way that doesn't break anything.

Quoted 3 lines

Because of that it would
be great to create a stable branch or even pull this in through the dma-mapping tree entirely.

FWIW I might still need to tweak the first patch depending on how we choose to resolve the interaction between DMA ops and unmanaged domains[1] - if we want to add fallback paths to iommu-dma instead of swizzling device ops then the assumptions for this hook change and it will need to reference group->domain rather than group->default_domain.

Robin.

[1]
https://www.mail-archive.com/dri-devel at lists.freedesktop.org/msg230275.html

#12 lore raw

cover iommu: Avoid DMA ops domain refcount contention

Quoted 5 lines · Robin Murphy
Quoted 2 lines · Christoph Hellwig

I plan to make the arm iommu dma ops generic and move them to drivers/iommu for the 4.20 merge window.

You mean 32-bit arm?

Sorry, I meant the arm64 wrappers for dma-iommu of course.

Quoted 5 lines

The only place that code should move to is /dev/null ;)
- the plan has always been to convert it to use groups and default domains so it can just plug iommu-dma in. The tricky bit is either weaning the users of the private arm_iommu_*() API onto generic IOMMU API usage, or at least implementing transitional wrappers in a way that doesn't break anything.

Agreed on the arm32 iommu code.

#13 lore raw

cover iommu: Avoid DMA ops domain refcount contention

In reply to Robin Murphy
Quoted 7 lines · Robin Murphy

John raised the issue[1] that we have some unnecessary refcount contention in the DMA ops path which shows scalability problems now that we have more real high-performance hardware using iommu-dma. The x86 IOMMU drivers are sidestepping this by stashing domain references in archdata, but since that's not very nice for architecture-agnostic code, I think it's time to look at a generic API-level solution.

Quoted 7 lines

These are a couple of quick patches based on the idea I had back when first implementing this lot, but didn't have any way to justify at the time. The third patch can be ignored for the sake of API discussion, but is included for completeness.

Robin.

Some results:

PCIe NIC iperf test (128 processes, small packets):
Without patchset:
289232.00 rxpck/s

With patchset:
367283 rxpck/s

JFYI, with Leizhen's non-strict mode patchset + Robin's patchset:
1215539 rxpck/s

Leizhen can share non-strict mode results in his own patchset however.

We did also try the storage controller fio test with a lot of SAS SSD disks (24 disks, 24 fio processes) for Robin's patchset only, but did not see a significant change.

Thanks to Dongdong + chenxiang for testing.

Let me know if you require more info.

Thanks again,
John

Quoted 14 lines

[1] https://lists.linuxfoundation.org/pipermail/iommu/2018-August/029303.html

Robin Murphy (3):
iommu: Add fast hook for getting DMA domains iommu/dma: Use fast DMA domain lookup arm64/dma-mapping: Mildly optimise non-coherent IOMMU ops

 arch/arm64/mm/dma-mapping.c | 10 +++++-----
 drivers/iommu/dma-iommu.c   | 23 ++++++++++++-----------
 drivers/iommu/iommu.c       |  9 +++++++++
 include/linux/iommu.h       |  1 +
 4 files changed, 27 insertions(+), 16 deletions(-)