Skip to contentSkip to conversation

two dma-direct fixes for 4.18

PATCH In review Christoph Hellwig 2 participants 7 messages Aug 24, 2018 – Aug 29, 2018 5 messages not in the archive DMA mapping helpers

lore
Share & tools
lore
#0 lore raw

Ensure we properly take the phys_to_dma translations into account when checking dma masks.

A message that is not in the archive (sent off-list or not yet received): [email protected]

#2 lore raw

PATCH 1/2 kernel/dma/direct: take DMA offset into account in dma_direct_supported

When a device has a DMA offset the dma capable result will change due to the difference between the physical and DMA address. Take that into account.

---
 kernel/dma/direct.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
kernel/dma/direct.c +2 -2
index 1c35b7b945d0..de87b0282e74 100644
@@ -168,7 +168,7 @@ int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents,
 int dma_direct_supported(struct device *dev, u64 mask)
 {
 #ifdef CONFIG_ZONE_DMA
-	if (mask < DMA_BIT_MASK(ARCH_ZONE_DMA_BITS))
+	if (mask < phys_to_dma(dev, DMA_BIT_MASK(ARCH_ZONE_DMA_BITS)))
 		return 0;
 #else
 	/*
@@ -177,7 +177,7 @@ int dma_direct_supported(struct device *dev, u64 mask)
 	 * memory, or by providing a ZONE_DMA32.  If neither is the case, the
 	 * architecture needs to use an IOMMU instead of the direct mapping.
 	 */
-	if (mask < DMA_BIT_MASK(32))
+	if (mask < phys_to_dma(dev, DMA_BIT_MASK(32)))
 		return 0;
 #endif
 	/*
Signature · 1 line
2.18.0

A message that is not in the archive (sent off-list or not yet received): [email protected]

Reviewed-by
#4 lore raw

Re: [PATCH 1/2] kernel/dma/direct: take DMA offset into account in dma_direct_supported

Quoted 3 lines · Christoph Hellwig

When a device has a DMA offset the dma capable result will change due to the difference between the physical and DMA address. Take that into account.

The "phys_to_dma(..., DMA_BIT_MASK(...))" idiom always looks like a glaring error at first glance, but this whole function is fairly unintuitive anyway, and ultimately I think the change does work out to be correct.

It might be nicer if we could reference max_zone_pfns[] for a bit more clarity, but I guess that's not arch-independent.

Quoted 29 lines
---
  kernel/dma/direct.c | 4 ++--
  1 file changed, 2 insertions(+), 2 deletions(-)
kernel/dma/direct.c +2 -2
index 1c35b7b945d0..de87b0282e74 100644
@@ -168,7 +168,7 @@ int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents,
  int dma_direct_supported(struct device *dev, u64 mask)
  {
  #ifdef CONFIG_ZONE_DMA
-	if (mask < DMA_BIT_MASK(ARCH_ZONE_DMA_BITS))
+	if (mask < phys_to_dma(dev, DMA_BIT_MASK(ARCH_ZONE_DMA_BITS)))
  		return 0;
  #else
  	/*
@@ -177,7 +177,7 @@ int dma_direct_supported(struct device *dev, u64 mask)
  	 * memory, or by providing a ZONE_DMA32.  If neither is the case, the
  	 * architecture needs to use an IOMMU instead of the direct mapping.
  	 */
-	if (mask < DMA_BIT_MASK(32))
+	if (mask < phys_to_dma(dev, DMA_BIT_MASK(32)))
  		return 0;
  #endif
  	/*

A message that is not in the archive (sent off-list or not yet received): 0e95df80-38f7-7970-ef74-419567f13fdc-5wv7dgnIgG8@public.gmane.org

#6 lore raw

Re: [PATCH 1/2] kernel/dma/direct: take DMA offset into account in dma_direct_supported

Quoted 11 lines · Robin Murphy
Quoted 3 lines · Christoph Hellwig

When a device has a DMA offset the dma capable result will change due to the difference between the physical and DMA address. Take that into account.

The "phys_to_dma(..., DMA_BIT_MASK(...))" idiom always looks like a glaring error at first glance, but this whole function is fairly unintuitive anyway, and ultimately I think the change does work out to be correct.

It might be nicer if we could reference max_zone_pfns[] for a bit more clarity, but I guess that's not arch-independent.

Not just arch specific, but also local variables. There is arch_zone_lowest_possible_pfn in page_alloc.c, but that gets discarded after init. That being said this is the right direction and I'll look into it for 4.20 or later.

#7 lore raw

PATCH 2/2 kernel/dma/direct: refine dma_direct_alloc zone selection

In reply to a message not in the archive

We need to take the DMA offset and encryption bit into account when selecting a zone. Add a helper that takes those into account and use it.

---
 kernel/dma/direct.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)
kernel/dma/direct.c +12 -4
index de87b0282e74..3f8b596ea393 100644
@@ -58,6 +58,14 @@ static bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
 	return addr + size - 1 <= dev->coherent_dma_mask;
 }
 
+static bool dma_coherent_below(struct device *dev, u64 mask)
+{
+	dma_addr_t addr = force_dma_unencrypted() ?
+		__phys_to_dma(dev, mask) : phys_to_dma(dev, mask);
+
+	return dev->coherent_dma_mask <= addr;
+}
+
 void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
 		gfp_t gfp, unsigned long attrs)
 {
@@ -70,9 +78,9 @@ void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
 	gfp &= ~__GFP_ZERO;
 
 	/* GFP_DMA32 and GFP_DMA are no ops without the corresponding zones: */
-	if (dev->coherent_dma_mask <= DMA_BIT_MASK(ARCH_ZONE_DMA_BITS))
+	if (dma_coherent_below(dev, DMA_BIT_MASK(ARCH_ZONE_DMA_BITS)))
 		gfp |= GFP_DMA;
-	if (dev->coherent_dma_mask <= DMA_BIT_MASK(32) && !(gfp & GFP_DMA))
+	if (dma_coherent_below(dev, DMA_BIT_MASK(32) && !(gfp & GFP_DMA)))
 		gfp |= GFP_DMA32;
 
 again:
@@ -93,14 +101,14 @@ void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
 		page = NULL;
 
 		if (IS_ENABLED(CONFIG_ZONE_DMA32) &&
-		    dev->coherent_dma_mask < DMA_BIT_MASK(64) &&
+		    dma_coherent_below(dev, DMA_BIT_MASK(64)) &&
 		    !(gfp & (GFP_DMA32 | GFP_DMA))) {
 			gfp |= GFP_DMA32;
 			goto again;
 		}
 
 		if (IS_ENABLED(CONFIG_ZONE_DMA) &&
-		    dev->coherent_dma_mask < DMA_BIT_MASK(32) &&
+		    dma_coherent_below(dev, DMA_BIT_MASK(32)) &&
 		    !(gfp & GFP_DMA)) {
 			gfp = (gfp & ~GFP_DMA32) | GFP_DMA;
 			goto again;
Signature · 1 line
2.18.0

A message that is not in the archive (sent off-list or not yet received): [email protected]

#9 lore raw

Re: [PATCH 2/2] kernel/dma/direct: refine dma_direct_alloc zone selection

Quoted 40 lines · Christoph Hellwig

We need to take the DMA offset and encryption bit into account when selecting a zone. Add a helper that takes those into account and use it.

---
  kernel/dma/direct.c | 16 ++++++++++++----
  1 file changed, 12 insertions(+), 4 deletions(-)
kernel/dma/direct.c +10 -2
index de87b0282e74..3f8b596ea393 100644
@@ -58,6 +58,14 @@ static bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
  	return addr + size - 1 <= dev->coherent_dma_mask;
  }
 
+static bool dma_coherent_below(struct device *dev, u64 mask)
+{
+	dma_addr_t addr = force_dma_unencrypted() ?
+		__phys_to_dma(dev, mask) : phys_to_dma(dev, mask);
+
+	return dev->coherent_dma_mask <= addr;
+}
+
  void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
  		gfp_t gfp, unsigned long attrs)
  {
@@ -70,9 +78,9 @@ void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
  	gfp &= ~__GFP_ZERO;
 
  	/* GFP_DMA32 and GFP_DMA are no ops without the corresponding zones: */
-	if (dev->coherent_dma_mask <= DMA_BIT_MASK(ARCH_ZONE_DMA_BITS))
+	if (dma_coherent_below(dev, DMA_BIT_MASK(ARCH_ZONE_DMA_BITS)))
  		gfp |= GFP_DMA;
-	if (dev->coherent_dma_mask <= DMA_BIT_MASK(32) && !(gfp & GFP_DMA))
+	if (dma_coherent_below(dev, DMA_BIT_MASK(32) && !(gfp & GFP_DMA)))
  		gfp |= GFP_DMA32;
 
  again:

kernel/dma/direct.c void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, · line 96

Quoted 4 lines
kernel/dma/direct.c +0 -1
@@ -93,14 +101,14 @@ void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
  		page = NULL;
 
  		if (IS_ENABLED(CONFIG_ZONE_DMA32) &&
-		    dev->coherent_dma_mask < DMA_BIT_MASK(64) &&

We're effectively changing the "<" into a "<="...

kernel/dma/direct.c void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, · line 104

Quoted 1 line
kernel/dma/direct.c +1 -0
+		    dma_coherent_below(dev, DMA_BIT_MASK(64)) &&

...which I think means this check now *always* returns true...

kernel/dma/direct.c void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle, · line 111

Quoted 8 lines
kernel/dma/direct.c +1 -1
  		    !(gfp & (GFP_DMA32 | GFP_DMA))) {
  			gfp |= GFP_DMA32;
  			goto again;
  		}
 
  		if (IS_ENABLED(CONFIG_ZONE_DMA) &&
-		    dev->coherent_dma_mask < DMA_BIT_MASK(32) &&
+		    dma_coherent_below(dev, DMA_BIT_MASK(32)) &&

...and this one subtly changes WRT ZONE_DMA32, but I can't quite figure out how much that matters :/

Robin.

Quoted 4 lines
kernel/dma/direct.c +0 -0
  		    !(gfp & GFP_DMA)) {
  			gfp = (gfp & ~GFP_DMA32) | GFP_DMA;
  			goto again;

A message that is not in the archive (sent off-list or not yet received): bfba5bae-a88c-f777-ba1f-f0db310904dc-5wv7dgnIgG8@public.gmane.org

#11 lore raw

Re: [PATCH 2/2] kernel/dma/direct: refine dma_direct_alloc zone selection

Quoted 47 lines · Robin Murphy
Quoted 41 lines · Christoph Hellwig

We need to take the DMA offset and encryption bit into account when selecting a zone.  Add a helper that takes those into account and use it.

---
  kernel/dma/direct.c | 16 ++++++++++++----

  1 file changed, 12 insertions(+), 4 deletions(-)
kernel/dma/direct.c +10 -2
index de87b0282e74..3f8b596ea393 100644
@@ -58,6 +58,14 @@ static bool dma_coherent_ok(struct device *dev, 
 phys_addr_t phys, size_t size)
       return addr + size - 1 <= dev->coherent_dma_mask;
   }
+static bool dma_coherent_below(struct device *dev, u64 mask)
+{
+    dma_addr_t addr = force_dma_unencrypted() ?
+        __phys_to_dma(dev, mask) : phys_to_dma(dev, mask);
+
+    return dev->coherent_dma_mask <= addr;
+}
+
   void *dma_direct_alloc(struct device *dev, size_t size, dma_addr_t *dma_handle,
           gfp_t gfp, unsigned long attrs)
   {
@@ -70,9 +78,9 @@ void *dma_direct_alloc(struct device *dev, size_t 
 size, dma_addr_t *dma_handle,
       gfp &= ~__GFP_ZERO;
       /* GFP_DMA32 and GFP_DMA are no ops without the corresponding zones: */
-    if (dev->coherent_dma_mask <= DMA_BIT_MASK(ARCH_ZONE_DMA_BITS))
+    if (dma_coherent_below(dev, DMA_BIT_MASK(ARCH_ZONE_DMA_BITS)))
           gfp |= GFP_DMA;
-    if (dev->coherent_dma_mask <= DMA_BIT_MASK(32) && !(gfp & GFP_DMA))
+    if (dma_coherent_below(dev, DMA_BIT_MASK(32) && !(gfp & GFP_DMA)))
           gfp |= GFP_DMA32;

  again:

kernel/dma/direct.c +0 -0
@@ -93,14 +101,14 @@ void *dma_direct_alloc(struct device *dev, size_t 
 size, dma_addr_t *dma_handle,
           page = NULL;
Quoted 2 lines

          if (IS_ENABLED(CONFIG_ZONE_DMA32) &&
-            dev->coherent_dma_mask < DMA_BIT_MASK(64) &&

We're effectively changing the "<" into a "<="...

Quoted 3 lines
Quoted 1 line

+            dma_coherent_below(dev, DMA_BIT_MASK(64)) &&

...which I think means this check now *always* returns true...

Although on closer inspection that was effectively already the case - we could never get here if dev->coherent_dma_mask == DMA_BIT_MASK(64), because dma_coherent_ok() couldn't possibly have returned false in the first place. So this one was always unnecessary.

Robin.
_______________________________________________
iommu mailing list
[email protected]
https://lists.linuxfoundation.org/mailman/listinfo/iommu