diff mbox

ARM: dma-mapping: Fix dma_get_sgtable() for regions without struct page

Message ID 20170329163335.GA7909@n2100.armlinux.org.uk (mailing list archive)
State New, archived
Headers show

Commit Message

Russell King (Oracle) March 29, 2017, 4:33 p.m. UTC
Okay, I'm about to merge the following patch for -rc, since refusing
to create a scattertable for non-page backed memory is the only valid
solution for that case.  I'm intending to queue this for -rc this
evening.

8<====
ARM: dma-mapping: disallow dma_get_sgtable() for non-kernel managed memory

dma_get_sgtable() tries to create a scatterlist table containing valid
struct page pointers for the coherent memory allocation passed in to it.

However, memory can be declared via dma_declare_coherent_memory(), or
via other reservation schemes which means that coherent memory is not
guaranteed to be backed by struct pages.  This means it is not possible
to create a valid scatterlist via this mechanism.

This patch adds detection of such memory, and refuses to create a
scatterlist table for such memory.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
---
 arch/arm/mm/dma-mapping.c | 20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

Comments

Shuah Khan March 29, 2017, 4:51 p.m. UTC | #1
Hi Russell,

On Wed, Mar 29, 2017 at 10:33 AM, Russell King - ARM Linux
<linux@armlinux.org.uk> wrote:
> Okay, I'm about to merge the following patch for -rc, since refusing
> to create a scattertable for non-page backed memory is the only valid
> solution for that case.  I'm intending to queue this for -rc this
> evening.

I was bit sidetracked and getting back to this today. I am just about
to test this change on my system. I will test your patch and send you
results.

This might not help my use-case - I suspect it will trip this check.
That said I will send you results very soon.

>
> 8<====
> ARM: dma-mapping: disallow dma_get_sgtable() for non-kernel managed memory
>
> dma_get_sgtable() tries to create a scatterlist table containing valid
> struct page pointers for the coherent memory allocation passed in to it.
>
> However, memory can be declared via dma_declare_coherent_memory(), or
> via other reservation schemes which means that coherent memory is not
> guaranteed to be backed by struct pages.  This means it is not possible
> to create a valid scatterlist via this mechanism.

I have been thinking about this some. I would like to see if we can
provide a way forward to address the cases where coherent memory is
not guaranteed to be backed by struct pages. We know the memory isn't
backed at alloc time in dma_alloc_coherent(). Can we key off of that
maybe add a new attr flag to avoid page lookups. I am willing to work
on the fixing it.

Let me first send you the results after testing your patch. Maybe we
can explore ways to fix the problem.

thanks,
-- Shuah

>
> This patch adds detection of such memory, and refuses to create a
> scatterlist table for such memory.
>
> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
> ---
>  arch/arm/mm/dma-mapping.c | 20 +++++++++++++++++++-
>  1 file changed, 19 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
> index 63eabb06f9f1..475811f5383a 100644
> --- a/arch/arm/mm/dma-mapping.c
> +++ b/arch/arm/mm/dma-mapping.c
> @@ -935,13 +935,31 @@ static void arm_coherent_dma_free(struct device *dev, size_t size, void *cpu_add
>         __arm_dma_free(dev, size, cpu_addr, handle, attrs, true);
>  }
>
> +/*
> + * The whole dma_get_sgtable() idea is fundamentally unsafe - it seems
> + * that the intention is to allow exporting memory allocated via the
> + * coherent DMA APIs through the dma_buf API, which only accepts a
> + * scattertable.  This presents a couple of problems:
> + * 1. Not all memory allocated via the coherent DMA APIs is backed by
> + *    a struct page
> + * 2. Passing coherent DMA memory into the streaming APIs is not allowed
> + *    as we will try to flush the memory through a different alias to that
> + *    actually being used (and the flushes are redundant.)
> + */
>  int arm_dma_get_sgtable(struct device *dev, struct sg_table *sgt,
>                  void *cpu_addr, dma_addr_t handle, size_t size,
>                  unsigned long attrs)
>  {
> -       struct page *page = pfn_to_page(dma_to_pfn(dev, handle));
> +       unsigned long pfn = dma_to_pfn(dev, handle);
> +       struct page *page;
>         int ret;
>
> +       /* If the PFN is not valid, we do not have a struct page */
> +       if (!pfn_valid(pfn))
> +               return -ENXIO;
> +
> +       page = pfn_to_page(pfn);
> +
>         ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
>         if (unlikely(ret))
>                 return ret;
> --
> 2.7.4
>
> --
> RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
> FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
> according to speedtest.net.
Russell King (Oracle) March 29, 2017, 5:03 p.m. UTC | #2
On Wed, Mar 29, 2017 at 10:51:05AM -0600, Shuah Khan wrote:
> Hi Russell,
> 
> On Wed, Mar 29, 2017 at 10:33 AM, Russell King - ARM Linux
> <linux@armlinux.org.uk> wrote:
> > Okay, I'm about to merge the following patch for -rc, since refusing
> > to create a scattertable for non-page backed memory is the only valid
> > solution for that case.  I'm intending to queue this for -rc this
> > evening.
> 
> I was bit sidetracked and getting back to this today. I am just about
> to test this change on my system. I will test your patch and send you
> results.
> 
> This might not help my use-case - I suspect it will trip this check.
> That said I will send you results very soon.

I do hope that it _does_ trip this check - the result should be that you
get an error without the kernel oopsing.

> I have been thinking about this some. I would like to see if we can
> provide a way forward to address the cases where coherent memory is
> not guaranteed to be backed by struct pages. We know the memory isn't
> backed at alloc time in dma_alloc_coherent(). Can we key off of that
> maybe add a new attr flag to avoid page lookups. I am willing to work
> on the fixing it.

What we need is to revise the dma-buf API and DMA APIs to allow passing
coherent memory through it - there is no current provision in the DMA APIs
to have coherent memory mapped for two peripherals.
Shuah Khan March 29, 2017, 5:44 p.m. UTC | #3
Hi Russell,

On Wed, Mar 29, 2017 at 11:03 AM, Russell King - ARM Linux
<linux@armlinux.org.uk> wrote:
> On Wed, Mar 29, 2017 at 10:51:05AM -0600, Shuah Khan wrote:
>> Hi Russell,
>>
>> On Wed, Mar 29, 2017 at 10:33 AM, Russell King - ARM Linux
>> <linux@armlinux.org.uk> wrote:
>> > Okay, I'm about to merge the following patch for -rc, since refusing
>> > to create a scattertable for non-page backed memory is the only valid
>> > solution for that case.  I'm intending to queue this for -rc this
>> > evening.
>>
>> I was bit sidetracked and getting back to this today. I am just about
>> to test this change on my system. I will test your patch and send you
>> results.
>>
>> This might not help my use-case - I suspect it will trip this check.
>> That said I will send you results very soon.
>
> I do hope that it _does_ trip this check - the result should be that you
> get an error without the kernel oopsing.

Agreed. I would rather have it fail gracefully earlier than limp along
and die later. I would make a suggestion based on these results.

Please consider adding a dev_err() to if (!pfn_valid(pfn)) condition
to indicate why get_sgtable failed including __func__

I worry about adding dev_err() to a core function, but in this case,
it would be helpful to do to make it very clear that the requested
use-case can't be supported.

Results:

[  235.706048] platform 11000000.codec:left: failed to get scatterlist
from DMA API

The following is a WARN_ON from vb2_dc_get_dmabuf() - probably should
go away. I will send a patch removing the WARN_ON to linux-media


[  235.711985] ------------[ cut here ]------------
[  235.712001] WARNING: CPU: 6 PID: 1987 at
drivers/media/v4l2-core/videobuf2-dma-contig.c:402
vb2_dc_get_dmabuf+0x130/0x168 [videobuf2_dma_contig]
[  235.712005] Modules linked in: cpufreq_userspace cpufreq_powersave
cpufreq_conservative s5p_jpeg s5p_mfc exynos_gsc v4l2_mem2mem
videobuf2_dma_contig v4l2_common videobuf2_memops videobuf2_v4l2
videobuf2_core videodev media
[  235.712063] CPU: 6 PID: 1987 Comm: qtdemux0:sink Not tainted
4.11.0-rc2-00001-g49214d0-dirty #11
[  235.712068] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree)
[  235.712085] [<c010e244>] (unwind_backtrace) from [<c010aea0>]
(show_stack+0x10/0x14)
[  235.712096] [<c010aea0>] (show_stack) from [<c033ef50>]
(dump_stack+0x78/0x8c)
[  235.712106] [<c033ef50>] (dump_stack) from [<c011b3c8>] (__warn+0xe8/0x100)
[  235.712113] [<c011b3c8>] (__warn) from [<c011b490>]
(warn_slowpath_null+0x20/0x28)
[  235.712122] [<c011b490>] (warn_slowpath_null) from [<bf20d8b8>]
(vb2_dc_get_dmabuf+0x130/0x168 [videobuf2_dma_contig])
[  235.712141] [<bf20d8b8>] (vb2_dc_get_dmabuf [videobuf2_dma_contig])
from [<bf151ec4>] (vb2_core_expbuf+0x1d4/0x2d8 [videobuf2_core])
[  235.712159] [<bf151ec4>] (vb2_core_expbuf [videobuf2_core]) from
[<bf19c198>] (vb2_expbuf+0x2c/0x34 [videobuf2_v4l2])
[  235.712195] [<bf19c198>] (vb2_expbuf [videobuf2_v4l2]) from
[<bf04b834>] (__video_do_ioctl+0x204/0x2fc [videodev])
[  235.712241] [<bf04b834>] (__video_do_ioctl [videodev]) from
[<bf04b390>] (video_usercopy+0x24c/0x4e0 [videodev])
[  235.712285] [<bf04b390>] (video_usercopy [videodev]) from
[<bf0465a4>] (v4l2_ioctl+0xa0/0xd8 [videodev])
[  235.712316] [<bf0465a4>] (v4l2_ioctl [videodev]) from [<c01fe920>]
(do_vfs_ioctl+0x9c/0x8e0)
[  235.712325] [<c01fe920>] (do_vfs_ioctl) from [<c01ff198>]
(SyS_ioctl+0x34/0x5c)
[  235.712334] [<c01ff198>] (SyS_ioctl) from [<c0107740>]
(ret_fast_syscall+0x0/0x3c)
[  235.712339] ---[ end trace cdfda037de46497c ]---

>
>> I have been thinking about this some. I would like to see if we can
>> provide a way forward to address the cases where coherent memory is
>> not guaranteed to be backed by struct pages. We know the memory isn't
>> backed at alloc time in dma_alloc_coherent(). Can we key off of that
>> maybe add a new attr flag to avoid page lookups. I am willing to work
>> on the fixing it.
>
> What we need is to revise the dma-buf API and DMA APIs to allow passing
> coherent memory through it - there is no current provision in the DMA APIs
> to have coherent memory mapped for two peripherals.

I will explore adding the support. I do think there is value in being
able to use coherent memory
buffers for DMA.

thanks,
-- Shuah

>
> --
> RMK's Patch system: http://www.armlinux.org.uk/developer/patches/
> FTTC broadband for 0.8mile line: currently at 9.6Mbps down 400kbps up
> according to speedtest.net.
diff mbox

Patch

diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index 63eabb06f9f1..475811f5383a 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -935,13 +935,31 @@  static void arm_coherent_dma_free(struct device *dev, size_t size, void *cpu_add
 	__arm_dma_free(dev, size, cpu_addr, handle, attrs, true);
 }
 
+/*
+ * The whole dma_get_sgtable() idea is fundamentally unsafe - it seems
+ * that the intention is to allow exporting memory allocated via the
+ * coherent DMA APIs through the dma_buf API, which only accepts a
+ * scattertable.  This presents a couple of problems:
+ * 1. Not all memory allocated via the coherent DMA APIs is backed by
+ *    a struct page
+ * 2. Passing coherent DMA memory into the streaming APIs is not allowed
+ *    as we will try to flush the memory through a different alias to that
+ *    actually being used (and the flushes are redundant.)
+ */
 int arm_dma_get_sgtable(struct device *dev, struct sg_table *sgt,
 		 void *cpu_addr, dma_addr_t handle, size_t size,
 		 unsigned long attrs)
 {
-	struct page *page = pfn_to_page(dma_to_pfn(dev, handle));
+	unsigned long pfn = dma_to_pfn(dev, handle);
+	struct page *page;
 	int ret;
 
+	/* If the PFN is not valid, we do not have a struct page */
+	if (!pfn_valid(pfn))
+		return -ENXIO;
+
+	page = pfn_to_page(pfn);
+
 	ret = sg_alloc_table(sgt, 1, GFP_KERNEL);
 	if (unlikely(ret))
 		return ret;