two dma-direct fixes for 4.18
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]
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
@@ -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]
Robin Murphy
Reviewed-byRe: [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
Signed-off-by Christoph Hellwig <[email protected]>Reviewed-by Benjamin Herrenschmidt <[email protected]>--- kernel/dma/direct.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)kernel/dma/direct.c +2
-2@@ -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
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.
PATCH 2/2 kernel/dma/direct: refine dma_direct_alloc zone selection
In reply to a message not in the archiveWe 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
@@ -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]
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.
Signed-off-by Christoph Hellwig <[email protected]>--- kernel/dma/direct.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-)kernel/dma/direct.c +10
-2@@ -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
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.
Signed-off-by Christoph Hellwig <[email protected]>---
kernel/dma/direct.c| 16 ++++++++++++----1 file changed, 12 insertions(+), 4 deletions(-)kernel/dma/direct.c +10
-2@@ -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