From: Miles Chen <miles.chen@mediatek.com>
To: Robin Murphy <robin.murphy@arm.com>
Cc: Andrew Morton <akpm@linux-foundation.org>,
linux-kernel@vger.kernel.org, linux-mm@kvack.org,
wsd_upstream@mediatek.com, linux-mediatek@lists.infradead.org,
iommu@lists.linux-foundation.org
Subject: Re: [PATCH] dma-debug: fix incorrect pfn calculation
Date: Wed, 27 Sep 2017 09:07:43 +0800 [thread overview]
Message-ID: <1506474463.7101.5.camel@mtkswgap22> (raw)
In-Reply-To: <03870968-0060-d6db-d109-f2c299c35bf1@arm.com>
On Tue, 2017-09-26 at 15:53 +0100, Robin Murphy wrote:
> On 26/09/17 14:24, miles.chen@mediatek.com wrote:
> > From: Miles Chen <miles.chen@mediatek.com>
> >
> > dma-debug report the following warning:
> >
> > [name:panic&]WARNING: CPU: 3 PID: 298 at kernel-4.4/lib/dma-debug.c:604
> > debug _dma_assert_idle+0x1a8/0x230()
> > DMA-API: cpu touching an active dma mapped cacheline [cln=0x00000882300]
> > CPU: 3 PID: 298 Comm: vold Tainted: G W O 4.4.22+ #1
> > Hardware name: MT6739 (DT)
> > Call trace:
> > [<ffffff800808acd0>] dump_backtrace+0x0/0x1d4
> > [<ffffff800808affc>] show_stack+0x14/0x1c
> > [<ffffff800838019c>] dump_stack+0xa8/0xe0
> > [<ffffff80080a0594>] warn_slowpath_common+0xf4/0x11c
> > [<ffffff80080a061c>] warn_slowpath_fmt+0x60/0x80
> > [<ffffff80083afe24>] debug_dma_assert_idle+0x1a8/0x230
> > [<ffffff80081dca9c>] wp_page_copy.isra.96+0x118/0x520
> > [<ffffff80081de114>] do_wp_page+0x4fc/0x534
> > [<ffffff80081e0a14>] handle_mm_fault+0xd4c/0x1310
> > [<ffffff8008098798>] do_page_fault+0x1c8/0x394
> > [<ffffff800808231c>] do_mem_abort+0x50/0xec
> >
> > I found that debug_dma_alloc_coherent() and debug_dma_free_coherent()
> > always use type "dma_debug_coherent" and assume that dma_alloc_coherent()
> > always returns a linear address.
> >
> > However if a device returns false on is_device_dma_coherent(),
> > dma_alloc_coherent() will create another non-cacheable mapping
> > (also non linear). In this case, page_to_pfn(virt_to_page(virt)) will
> > return an incorrect pfn. If the pfn is valid and mapped as a COW page,
> > we will hit the warning when doing wp_page_copy().
> >
> > Fix this by calculating correct pfn if is_device_dma_coherent()
> > returns false.
>
> As the inevitable storm of kbuild robot reports will tell you soon, you
> can't do that: is_device_dma_coherent() is a private helper between
> arm{,64} arch code and xen, and should not be used anywhere else.
thanks for that. I should not use the private helper function here.
> > Signed-off-by: Miles Chen <miles.chen@mediatek.com>
> > ---
> > lib/dma-debug.c | 20 ++++++++++++++------
> > 1 file changed, 14 insertions(+), 6 deletions(-)
> >
> > diff --git a/lib/dma-debug.c b/lib/dma-debug.c
> > index ea4cc3d..b17e56e 100644
> > --- a/lib/dma-debug.c
> > +++ b/lib/dma-debug.c
> > @@ -47,6 +47,8 @@ enum {
> > dma_debug_sg,
> > dma_debug_coherent,
> > dma_debug_resource,
> > + dma_debug_noncoherent,
> > + nr_dma_debug_types,
> > };
> >
> > enum map_err_types {
> > @@ -154,9 +156,9 @@ static inline bool dma_debug_disabled(void)
> > [MAP_ERR_CHECKED] = "dma map error checked",
> > };
> >
> > -static const char *type2name[5] = { "single", "page",
> > +static const char *type2name[nr_dma_debug_types] = { "single", "page",
> > "scather-gather", "coherent",
> > - "resource" };
> > + "resource", "noncoherent" };
> >
> > static const char *dir2name[4] = { "DMA_BIDIRECTIONAL", "DMA_TO_DEVICE",
> > "DMA_FROM_DEVICE", "DMA_NONE" };
> > @@ -1484,6 +1486,7 @@ void debug_dma_alloc_coherent(struct device *dev, size_t size,
> > dma_addr_t dma_addr, void *virt)
> > {
> > struct dma_debug_entry *entry;
> > + bool coherent = is_device_dma_coherent(dev);
> >
> > if (unlikely(dma_debug_disabled()))
> > return;
> > @@ -1495,9 +1498,11 @@ void debug_dma_alloc_coherent(struct device *dev, size_t size,
> > if (!entry)
> > return;
> >
> > - entry->type = dma_debug_coherent;
> > + entry->type = coherent ? dma_debug_coherent :
> > + dma_debug_noncoherent;
> > entry->dev = dev;
> > - entry->pfn = page_to_pfn(virt_to_page(virt));
>
> There are more architectures where the virtual address returned by
> dma_alloc_coherent is not a linear map address - some just have a static
> offset between cacheable and non-cacheable aliases - so there may be
> other cases where this is the wrong calculation, but at least those
> probably don't trigger the problematic false-positive.
>
> That said, the cases where coherent allocations *are* dynamically
> remapped should be easy enough to handle properly without having to
> resort to dodgy hacks:
>
> if (is_vmalloc_addr(virt))
> pfn = vmalloc_to_pfn(virt);
> else
> pfn = page_to_pfn(virt_to_page(virt));
>
> Simple.
Thanks for your advice. I'll try this approach.
Miles
>
> > + entry->pfn = coherent ? page_to_pfn(virt_to_page(virt)) :
> > + dma_addr >> PAGE_SHIFT;
>
> And in particular, this is just as likely to just give *different* false
> positives, since there's no guarantee whatsoever that dma_addr has any
> relationship to the appropriate pfn.
>
> Robin.
>
> > entry->offset = offset_in_page(virt);
> > entry->size = size;
> > entry->dev_addr = dma_addr;
> > @@ -1510,10 +1515,13 @@ void debug_dma_alloc_coherent(struct device *dev, size_t size,
> > void debug_dma_free_coherent(struct device *dev, size_t size,
> > void *virt, dma_addr_t addr)
> > {
> > + bool coherent = is_device_dma_coherent(dev);
> > struct dma_debug_entry ref = {
> > - .type = dma_debug_coherent,
> > + .type = coherent ? dma_debug_coherent :
> > + dma_debug_noncoherent,
> > .dev = dev,
> > - .pfn = page_to_pfn(virt_to_page(virt)),
> > + .pfn = coherent ? page_to_pfn(virt_to_page(virt)) :
> > + addr >> PAGE_SHIFT,
> > .offset = offset_in_page(virt),
> > .dev_addr = addr,
> > .size = size,
> >
>
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
WARNING: multiple messages have this Message-ID (diff)
From: Miles Chen <miles.chen@mediatek.com>
To: Robin Murphy <robin.murphy@arm.com>
Cc: Andrew Morton <akpm@linux-foundation.org>,
<linux-kernel@vger.kernel.org>, <linux-mm@kvack.org>,
<wsd_upstream@mediatek.com>, <linux-mediatek@lists.infradead.org>,
<iommu@lists.linux-foundation.org>
Subject: Re: [PATCH] dma-debug: fix incorrect pfn calculation
Date: Wed, 27 Sep 2017 09:07:43 +0800 [thread overview]
Message-ID: <1506474463.7101.5.camel@mtkswgap22> (raw)
In-Reply-To: <03870968-0060-d6db-d109-f2c299c35bf1@arm.com>
On Tue, 2017-09-26 at 15:53 +0100, Robin Murphy wrote:
> On 26/09/17 14:24, miles.chen@mediatek.com wrote:
> > From: Miles Chen <miles.chen@mediatek.com>
> >
> > dma-debug report the following warning:
> >
> > [name:panic&]WARNING: CPU: 3 PID: 298 at kernel-4.4/lib/dma-debug.c:604
> > debug _dma_assert_idle+0x1a8/0x230()
> > DMA-API: cpu touching an active dma mapped cacheline [cln=0x00000882300]
> > CPU: 3 PID: 298 Comm: vold Tainted: G W O 4.4.22+ #1
> > Hardware name: MT6739 (DT)
> > Call trace:
> > [<ffffff800808acd0>] dump_backtrace+0x0/0x1d4
> > [<ffffff800808affc>] show_stack+0x14/0x1c
> > [<ffffff800838019c>] dump_stack+0xa8/0xe0
> > [<ffffff80080a0594>] warn_slowpath_common+0xf4/0x11c
> > [<ffffff80080a061c>] warn_slowpath_fmt+0x60/0x80
> > [<ffffff80083afe24>] debug_dma_assert_idle+0x1a8/0x230
> > [<ffffff80081dca9c>] wp_page_copy.isra.96+0x118/0x520
> > [<ffffff80081de114>] do_wp_page+0x4fc/0x534
> > [<ffffff80081e0a14>] handle_mm_fault+0xd4c/0x1310
> > [<ffffff8008098798>] do_page_fault+0x1c8/0x394
> > [<ffffff800808231c>] do_mem_abort+0x50/0xec
> >
> > I found that debug_dma_alloc_coherent() and debug_dma_free_coherent()
> > always use type "dma_debug_coherent" and assume that dma_alloc_coherent()
> > always returns a linear address.
> >
> > However if a device returns false on is_device_dma_coherent(),
> > dma_alloc_coherent() will create another non-cacheable mapping
> > (also non linear). In this case, page_to_pfn(virt_to_page(virt)) will
> > return an incorrect pfn. If the pfn is valid and mapped as a COW page,
> > we will hit the warning when doing wp_page_copy().
> >
> > Fix this by calculating correct pfn if is_device_dma_coherent()
> > returns false.
>
> As the inevitable storm of kbuild robot reports will tell you soon, you
> can't do that: is_device_dma_coherent() is a private helper between
> arm{,64} arch code and xen, and should not be used anywhere else.
thanks for that. I should not use the private helper function here.
> > Signed-off-by: Miles Chen <miles.chen@mediatek.com>
> > ---
> > lib/dma-debug.c | 20 ++++++++++++++------
> > 1 file changed, 14 insertions(+), 6 deletions(-)
> >
> > diff --git a/lib/dma-debug.c b/lib/dma-debug.c
> > index ea4cc3d..b17e56e 100644
> > --- a/lib/dma-debug.c
> > +++ b/lib/dma-debug.c
> > @@ -47,6 +47,8 @@ enum {
> > dma_debug_sg,
> > dma_debug_coherent,
> > dma_debug_resource,
> > + dma_debug_noncoherent,
> > + nr_dma_debug_types,
> > };
> >
> > enum map_err_types {
> > @@ -154,9 +156,9 @@ static inline bool dma_debug_disabled(void)
> > [MAP_ERR_CHECKED] = "dma map error checked",
> > };
> >
> > -static const char *type2name[5] = { "single", "page",
> > +static const char *type2name[nr_dma_debug_types] = { "single", "page",
> > "scather-gather", "coherent",
> > - "resource" };
> > + "resource", "noncoherent" };
> >
> > static const char *dir2name[4] = { "DMA_BIDIRECTIONAL", "DMA_TO_DEVICE",
> > "DMA_FROM_DEVICE", "DMA_NONE" };
> > @@ -1484,6 +1486,7 @@ void debug_dma_alloc_coherent(struct device *dev, size_t size,
> > dma_addr_t dma_addr, void *virt)
> > {
> > struct dma_debug_entry *entry;
> > + bool coherent = is_device_dma_coherent(dev);
> >
> > if (unlikely(dma_debug_disabled()))
> > return;
> > @@ -1495,9 +1498,11 @@ void debug_dma_alloc_coherent(struct device *dev, size_t size,
> > if (!entry)
> > return;
> >
> > - entry->type = dma_debug_coherent;
> > + entry->type = coherent ? dma_debug_coherent :
> > + dma_debug_noncoherent;
> > entry->dev = dev;
> > - entry->pfn = page_to_pfn(virt_to_page(virt));
>
> There are more architectures where the virtual address returned by
> dma_alloc_coherent is not a linear map address - some just have a static
> offset between cacheable and non-cacheable aliases - so there may be
> other cases where this is the wrong calculation, but at least those
> probably don't trigger the problematic false-positive.
>
> That said, the cases where coherent allocations *are* dynamically
> remapped should be easy enough to handle properly without having to
> resort to dodgy hacks:
>
> if (is_vmalloc_addr(virt))
> pfn = vmalloc_to_pfn(virt);
> else
> pfn = page_to_pfn(virt_to_page(virt));
>
> Simple.
Thanks for your advice. I'll try this approach.
Miles
>
> > + entry->pfn = coherent ? page_to_pfn(virt_to_page(virt)) :
> > + dma_addr >> PAGE_SHIFT;
>
> And in particular, this is just as likely to just give *different* false
> positives, since there's no guarantee whatsoever that dma_addr has any
> relationship to the appropriate pfn.
>
> Robin.
>
> > entry->offset = offset_in_page(virt);
> > entry->size = size;
> > entry->dev_addr = dma_addr;
> > @@ -1510,10 +1515,13 @@ void debug_dma_alloc_coherent(struct device *dev, size_t size,
> > void debug_dma_free_coherent(struct device *dev, size_t size,
> > void *virt, dma_addr_t addr)
> > {
> > + bool coherent = is_device_dma_coherent(dev);
> > struct dma_debug_entry ref = {
> > - .type = dma_debug_coherent,
> > + .type = coherent ? dma_debug_coherent :
> > + dma_debug_noncoherent,
> > .dev = dev,
> > - .pfn = page_to_pfn(virt_to_page(virt)),
> > + .pfn = coherent ? page_to_pfn(virt_to_page(virt)) :
> > + addr >> PAGE_SHIFT,
> > .offset = offset_in_page(virt),
> > .dev_addr = addr,
> > .size = size,
> >
>
next prev parent reply other threads:[~2017-09-27 1:07 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-09-26 13:24 [PATCH] dma-debug: fix incorrect pfn calculation miles.chen
2017-09-26 13:24 ` miles.chen
[not found] ` <1506432287-7214-1-git-send-email-miles.chen-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
2017-09-26 14:53 ` Robin Murphy
2017-09-26 14:53 ` Robin Murphy
2017-09-26 14:53 ` Robin Murphy
2017-09-27 1:07 ` Miles Chen [this message]
2017-09-27 1:07 ` Miles Chen
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1506474463.7101.5.camel@mtkswgap22 \
--to=miles.chen@mediatek.com \
--cc=akpm@linux-foundation.org \
--cc=iommu@lists.linux-foundation.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mediatek@lists.infradead.org \
--cc=linux-mm@kvack.org \
--cc=robin.murphy@arm.com \
--cc=wsd_upstream@mediatek.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.