diff mbox

[V2,2/3] dmaselftest: add memcpy selftest support functions

Message ID 1446444460-21600-3-git-send-email-okaya@codeaurora.org (mailing list archive)
State Changes Requested
Headers show

Commit Message

Sinan Kaya Nov. 2, 2015, 6:07 a.m. UTC
This patch adds supporting utility functions
for selftest. The intention is to share the self
test code between different drivers.

Supported test cases include:
1. dma_map_single
2. streaming DMA
3. coherent DMA
4. scatter-gather DMA

Signed-off-by: Sinan Kaya <okaya@codeaurora.org>
---
 drivers/dma/dmaengine.h   |   2 +
 drivers/dma/dmaselftest.c | 669 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 671 insertions(+)
 create mode 100644 drivers/dma/dmaselftest.c

Comments

Vinod Koul Nov. 3, 2015, 4:15 a.m. UTC | #1
On Mon, Nov 02, 2015 at 01:07:38AM -0500, Sinan Kaya wrote:
> This patch adds supporting utility functions
> for selftest. The intention is to share the self
> test code between different drivers.
> 
> Supported test cases include:
> 1. dma_map_single
> 2. streaming DMA
> 3. coherent DMA
> 4. scatter-gather DMA

This seems quite similar to dmatest, any reason why you cannot use/enhance
that?
Sinan Kaya Nov. 3, 2015, 4:18 a.m. UTC | #2
On 11/2/2015 11:15 PM, Vinod Koul wrote:
> On Mon, Nov 02, 2015 at 01:07:38AM -0500, Sinan Kaya wrote:
>> This patch adds supporting utility functions
>> for selftest. The intention is to share the self
>> test code between different drivers.
>>
>> Supported test cases include:
>> 1. dma_map_single
>> 2. streaming DMA
>> 3. coherent DMA
>> 4. scatter-gather DMA
>
> This seems quite similar to dmatest, any reason why you cannot use/enhance
> that?
>
Dmatest is a standalone kernel module intended for stress testing DMA 
engines from userspace with N number of threads and M size combinations etc.

This one; on the other hand, is selftest to verify hardware is working 
as expected during power up.

Almost all DMA engine drivers come with some sort of selftest code 
called from probe. I followed the same design pattern.

I think the goal is to remove the duplicate self test code in all 
drivers over time.
Vinod Koul Nov. 3, 2015, 6:30 a.m. UTC | #3
On Mon, Nov 02, 2015 at 11:18:37PM -0500, Sinan Kaya wrote:
> 
> 
> On 11/2/2015 11:15 PM, Vinod Koul wrote:
> >On Mon, Nov 02, 2015 at 01:07:38AM -0500, Sinan Kaya wrote:
> >>This patch adds supporting utility functions
> >>for selftest. The intention is to share the self
> >>test code between different drivers.
> >>
> >>Supported test cases include:
> >>1. dma_map_single
> >>2. streaming DMA
> >>3. coherent DMA
> >>4. scatter-gather DMA
> >
> >This seems quite similar to dmatest, any reason why you cannot use/enhance
> >that?
> >
> Dmatest is a standalone kernel module intended for stress testing
> DMA engines from userspace with N number of threads and M size
> combinations etc.
> 
> This one; on the other hand, is selftest to verify hardware is
> working as expected during power up.
> 
> Almost all DMA engine drivers come with some sort of selftest code
> called from probe. I followed the same design pattern.

which ones ?

> 
> I think the goal is to remove the duplicate self test code in all
> drivers over time.

and what prevents us from having common selftest plus dmatest code. Most of
the code here to do selftest is _same_ dmaengine routine code used in
dmatest

We can have common code which is used for dmatest as well as selftest. I do
not want to see same code duplicated..
Dan Williams Nov. 3, 2015, 7:44 a.m. UTC | #4
On Mon, Nov 2, 2015 at 10:30 PM, Vinod Koul <vinod.koul@intel.com> wrote:
> On Mon, Nov 02, 2015 at 11:18:37PM -0500, Sinan Kaya wrote:
>>
>>
>> On 11/2/2015 11:15 PM, Vinod Koul wrote:
>> >On Mon, Nov 02, 2015 at 01:07:38AM -0500, Sinan Kaya wrote:
>> >>This patch adds supporting utility functions
>> >>for selftest. The intention is to share the self
>> >>test code between different drivers.
>> >>
>> >>Supported test cases include:
>> >>1. dma_map_single
>> >>2. streaming DMA
>> >>3. coherent DMA
>> >>4. scatter-gather DMA
>> >
>> >This seems quite similar to dmatest, any reason why you cannot use/enhance
>> >that?
>> >
>> Dmatest is a standalone kernel module intended for stress testing
>> DMA engines from userspace with N number of threads and M size
>> combinations etc.
>>
>> This one; on the other hand, is selftest to verify hardware is
>> working as expected during power up.
>>
>> Almost all DMA engine drivers come with some sort of selftest code
>> called from probe. I followed the same design pattern.
>
> which ones ?
>
>>
>> I think the goal is to remove the duplicate self test code in all
>> drivers over time.
>
> and what prevents us from having common selftest plus dmatest code. Most of
> the code here to do selftest is _same_ dmaengine routine code used in
> dmatest
>
> We can have common code which is used for dmatest as well as selftest. I do
> not want to see same code duplicated..

Originally ioatdma and iop-adma had local self tests before Haavard
created dmatest.  I agree having the drivers also do a test each boot
is redundant, but then again dmatest is not automatic and I saw the
local self test catch an interrupt setup regression.

Maybe you could arrange for drivers to do a quick autorun through
dmatest on load if dmatest is enabled, but otherwise load without
testing?  Just my 2 cents from a dmaengine spectator.
--
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andy Shevchenko Nov. 3, 2015, 8:22 a.m. UTC | #5
On Tue, Nov 3, 2015 at 9:44 AM, Dan Williams <dan.j.williams@intel.com> wrote:
> On Mon, Nov 2, 2015 at 10:30 PM, Vinod Koul <vinod.koul@intel.com> wrote:
>> On Mon, Nov 02, 2015 at 11:18:37PM -0500, Sinan Kaya wrote:
>>> On 11/2/2015 11:15 PM, Vinod Koul wrote:
>>> >On Mon, Nov 02, 2015 at 01:07:38AM -0500, Sinan Kaya wrote:


>>> This one; on the other hand, is selftest to verify hardware is
>>> working as expected during power up.

I prefer to have such nice case by run time parameter (let's say
common to all DMA Engine drivers)

>> We can have common code which is used for dmatest as well as selftest. I do
>> not want to see same code duplicated..

First thought was to merge this to dmatest, however, some DMA
controllers doesn't have a memcpy capability.

How would we test them?
Timur Tabi Nov. 3, 2015, 2:31 p.m. UTC | #6
Sinan Kaya wrote:
>
> Almost all DMA engine drivers come with some sort of selftest code
> called from probe. I followed the same design pattern.

As others have said, it appears that's outdated.

Is there a real possibility that the hardware could fail the test 
without trashing the system?  It seems that if the DMA engine is faulty, 
it won't "politely" fail.  The whole system will crash or some memory 
will get corrupted and you won't know it.
Sinan Kaya Nov. 3, 2015, 3:51 p.m. UTC | #7
On 11/3/2015 2:44 AM, Dan Williams wrote:
> On Mon, Nov 2, 2015 at 10:30 PM, Vinod Koul <vinod.koul@intel.com> wrote:
>> On Mon, Nov 02, 2015 at 11:18:37PM -0500, Sinan Kaya wrote:
>>>
>>>
>>> On 11/2/2015 11:15 PM, Vinod Koul wrote:
>>>> On Mon, Nov 02, 2015 at 01:07:38AM -0500, Sinan Kaya wrote:
>>>>> This patch adds supporting utility functions
>>>>> for selftest. The intention is to share the self
>>>>> test code between different drivers.
>>>>>
>>>>> Supported test cases include:
>>>>> 1. dma_map_single
>>>>> 2. streaming DMA
>>>>> 3. coherent DMA
>>>>> 4. scatter-gather DMA
>>>>
>>>> This seems quite similar to dmatest, any reason why you cannot use/enhance
>>>> that?
>>>>
>>> Dmatest is a standalone kernel module intended for stress testing
>>> DMA engines from userspace with N number of threads and M size
>>> combinations etc.
>>>
>>> This one; on the other hand, is selftest to verify hardware is
>>> working as expected during power up.
>>>
>>> Almost all DMA engine drivers come with some sort of selftest code
>>> called from probe. I followed the same design pattern.
>>
>> which ones ?
>>
>>>


>>> I think the goal is to remove the duplicate self test code in all
>>> drivers over time.
>>
>> and what prevents us from having common selftest plus dmatest code. Most of
>> the code here to do selftest is _same_ dmaengine routine code used in
>> dmatest
>>
>> We can have common code which is used for dmatest as well as selftest. I do
>> not want to see same code duplicated..
>
> Originally ioatdma and iop-adma had local self tests before Haavard
> created dmatest.  I agree having the drivers also do a test each boot
> is redundant, but then again dmatest is not automatic and I saw the
> local self test catch an interrupt setup regression.

I see the following files still have some sort of self test code in them.

ioat\dma.c
ioat\dma_v2.c
ioat\dma_v3.c
iop-adma.c
mv_xor.c


>
> Maybe you could arrange for drivers to do a quick autorun through
> dmatest on load if dmatest is enabled, but otherwise load without
> testing?  Just my 2 cents from a dmaengine spectator.
>

I'm on the same boat. Almost all kernel configurations that I have seen 
do not have dmatest enabled. Dmatest is considered debug only and is not 
included into the kernel binaries.

I have no problem for moving the code from one location to the other but 
I still want to be able to run self-test code in mission mode before 
enabling DMA as today.

I'm open to suggestions.
Vinod Koul Nov. 3, 2015, 4:06 p.m. UTC | #8
On Mon, Nov 02, 2015 at 11:44:23PM -0800, Dan Williams wrote:
> Originally ioatdma and iop-adma had local self tests before Haavard
> created dmatest.  I agree having the drivers also do a test each boot
> is redundant, but then again dmatest is not automatic and I saw the
> local self test catch an interrupt setup regression.

Well the question here is not about why selftest but rather code duplication
and using same routines for both selftest as well as dmatest
> 
> Maybe you could arrange for drivers to do a quick autorun through
> dmatest on load if dmatest is enabled, but otherwise load without
> testing?

That would be a good thing to do, but then most distro just enable all
config options, so this should be protected for "test" builds

>  Just my 2 cents from a dmaengine spectator.

Thats an understatement :)

> --
> To unsubscribe from this list: send the line "unsubscribe dmaengine" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
Vinod Koul Nov. 3, 2015, 4:08 p.m. UTC | #9
On Tue, Nov 03, 2015 at 10:22:25AM +0200, Andy Shevchenko wrote:
> On Tue, Nov 3, 2015 at 9:44 AM, Dan Williams <dan.j.williams@intel.com> wrote:
> > On Mon, Nov 2, 2015 at 10:30 PM, Vinod Koul <vinod.koul@intel.com> wrote:
> >> On Mon, Nov 02, 2015 at 11:18:37PM -0500, Sinan Kaya wrote:
> >>> On 11/2/2015 11:15 PM, Vinod Koul wrote:
> >>> >On Mon, Nov 02, 2015 at 01:07:38AM -0500, Sinan Kaya wrote:
> 
> 
> >>> This one; on the other hand, is selftest to verify hardware is
> >>> working as expected during power up.
> 
> I prefer to have such nice case by run time parameter (let's say
> common to all DMA Engine drivers)
> 
> >> We can have common code which is used for dmatest as well as selftest. I do
> >> not want to see same code duplicated..
> 
> First thought was to merge this to dmatest, however, some DMA
> controllers doesn't have a memcpy capability.

The tests should be based on capablity of memcpy

> How would we test them?

That part is tricky, you need to do so thru clients, spi/audio/serial etc
Vinod Koul Nov. 3, 2015, 4:10 p.m. UTC | #10
On Tue, Nov 03, 2015 at 08:31:57AM -0600, Timur Tabi wrote:
> Sinan Kaya wrote:
> >
> >Almost all DMA engine drivers come with some sort of selftest code
> >called from probe. I followed the same design pattern.
> 
> As others have said, it appears that's outdated.
> 
> Is there a real possibility that the hardware could fail the test
> without trashing the system?  It seems that if the DMA engine is
> faulty, it won't "politely" fail.  The whole system will crash or
> some memory will get corrupted and you won't know it.

Failing politely would be right thing to do. If DMA starts sending data to
anywhere in system memory due to bug or wrong addresses we can't do
anything to prevent that
Sinan Kaya Nov. 3, 2015, 4:28 p.m. UTC | #11
On 11/3/2015 11:10 AM, Vinod Koul wrote:
> On Tue, Nov 03, 2015 at 08:31:57AM -0600, Timur Tabi wrote:
>> Sinan Kaya wrote:
>>>
>>> Almost all DMA engine drivers come with some sort of selftest code
>>> called from probe. I followed the same design pattern.
>>
>> As others have said, it appears that's outdated.
>>
>> Is there a real possibility that the hardware could fail the test
>> without trashing the system?  It seems that if the DMA engine is
>> faulty, it won't "politely" fail.  The whole system will crash or
>> some memory will get corrupted and you won't know it.
>
> Failing politely would be right thing to do. If DMA starts sending data to
> anywhere in system memory due to bug or wrong addresses we can't do
> anything to prevent that
>
I have seen failures in three cases so far. These are the reasons, why I 
want to keep these runtime tests around.

1. Bug in ARM64 DMA subsystem.
2. Bug in IOMMU driver.
3. Bug in another newly introduced driver. The new driver would hog the 
CPU and won't allow HIDMA interrupts to execute. Therefore, the test 
times out.

In my code, when test fails; I abort all transactions in flight and 
shutdown and deregister the HIDMA driver.

Of course, there is a small window of oppurtunity; where DMA can dump 
data to incorrect place if HW was faulty right before I abort the 
transaction.
Timur Tabi Nov. 3, 2015, 4:46 p.m. UTC | #12
Sinan Kaya wrote:
> 1. Bug in ARM64 DMA subsystem.
> 2. Bug in IOMMU driver.
> 3. Bug in another newly introduced driver. The new driver would hog the
> CPU and won't allow HIDMA interrupts to execute. Therefore, the test
> times out.

Which driver?

Wouldn't these problems already be exposed by dmatest?  I was asking 
whether it's possible that, every now and then, your DMA internal test 
could fail and then the driver would unload.  I'm not talking about hard 
bugs in other code that always causes the DMA driver test to fail.
Timur Tabi Nov. 3, 2015, 4:48 p.m. UTC | #13
Vinod Koul wrote:
> Failing politely would be right thing to do. If DMA starts sending data to
> anywhere in system memory due to bug or wrong addresses we can't do
> anything to prevent that

My point is that I have a hard time believing that a DMA failure would 
likely result the driver politely detecting the problem and exiting 
properly.  I just don't think it's realistic to expect that.

Now maybe it's better for a catastrophic failure to occur when the 
driver loads instead of during some critical network operation.
Sinan Kaya Nov. 3, 2015, 4:57 p.m. UTC | #14
On 11/3/2015 11:46 AM, Timur Tabi wrote:
> Sinan Kaya wrote:
>> 1. Bug in ARM64 DMA subsystem.
>> 2. Bug in IOMMU driver.
>> 3. Bug in another newly introduced driver. The new driver would hog the
>> CPU and won't allow HIDMA interrupts to execute. Therefore, the test
>> times out.
>
> Which driver?
Some other driver that is not upstream yet.

>
> Wouldn't these problems already be exposed by dmatest?  I was asking
> whether it's possible that, every now and then, your DMA internal test
> could fail and then the driver would unload.  I'm not talking about hard
> bugs in other code that always causes the DMA driver test to fail.
>
The point is that dmatest is a kernel module that requires manual 
interaction. It does not run automatically and is generally used by the 
dma engine driver developer during development only.

Other developers like UART, SATA, USB, IOMMU etc. They don't care about 
dmatest and I have seen their changes broke self-test multiple times. I 
see the value of self test all the time.

I can make dma-self test a new kernel module. It could discover all DMA 
devices with MEMCPY capability and run a self test on them. We could 
tell the self test code deregister all DMA devices that fail the test.

I can also make it kernel command line dependent and disabled by 
default. Those who want this functionality all the time can change their 
kernel command line.

I hope this addresses concerns.
Sinan Kaya Nov. 5, 2015, 2:42 a.m. UTC | #15
On 11/3/2015 11:08 AM, Vinod Koul wrote:
> On Tue, Nov 03, 2015 at 10:22:25AM +0200, Andy Shevchenko wrote:
>> On Tue, Nov 3, 2015 at 9:44 AM, Dan Williams <dan.j.williams@intel.com> wrote:
>>> On Mon, Nov 2, 2015 at 10:30 PM, Vinod Koul <vinod.koul@intel.com> wrote:
>>>> On Mon, Nov 02, 2015 at 11:18:37PM -0500, Sinan Kaya wrote:
>>>>> On 11/2/2015 11:15 PM, Vinod Koul wrote:
>>>>>> On Mon, Nov 02, 2015 at 01:07:38AM -0500, Sinan Kaya wrote:
>>
>>
>>>>> This one; on the other hand, is selftest to verify hardware is
>>>>> working as expected during power up.
>>
>> I prefer to have such nice case by run time parameter (let's say
>> common to all DMA Engine drivers)
>>
>>>> We can have common code which is used for dmatest as well as selftest. I do
>>>> not want to see same code duplicated..
>>
>> First thought was to merge this to dmatest, however, some DMA
>> controllers doesn't have a memcpy capability.
>
> The tests should be based on capablity of memcpy
>
>> How would we test them?

Here is what I proposed.

- a common file that gets compiled into a module that wants to use 
self-test with a public API. It can be called from driver's probe routine.
- the test is independent of my implementation. It uses dmaengine API 
and should be portable to most drivers.
- there *are* still drivers in the kernel that has selftest code 
embedded inside them. I followed the design pattern from other drivers 
thinking this must have been a good idea and it paid off for me.

As far as I understand, there is interest in doing more than this and 
reusing the dmatest code for code duplication.

Facts:
- Dmatest can be actually configured to run during boot.
- Nobody besides the dma driver developer uses dmatest. This leaves 
holes for regressions that are really hard to debug due to interaction 
with the rest of the system.
- Dmatest doesn't exist in most distribution kernels.

If we want to do something else, I need clear directions. I can remove 
the self test code completely from my driver. But, I need an equivalent 
functionality.

 >
 > That part is tricky, you need to do so thru clients, spi/audio/serial etc
 >

My selftest code actually attaches to all slave devices and issues a 
memcpy command and then detaches from the slave devices.
Vinod Koul Nov. 5, 2015, 12:05 p.m. UTC | #16
On Wed, Nov 04, 2015 at 09:42:46PM -0500, Sinan Kaya wrote:
> Here is what I proposed.
> 
> - a common file that gets compiled into a module that wants to use
> self-test with a public API. It can be called from driver's probe
> routine.
> - the test is independent of my implementation. It uses dmaengine
> API and should be portable to most drivers.
> - there *are* still drivers in the kernel that has selftest code
> embedded inside them. I followed the design pattern from other
> drivers thinking this must have been a good idea and it paid off for
> me.
> 
> As far as I understand, there is interest in doing more than this
> and reusing the dmatest code for code duplication.

the code that selftest uses to test will be very similar to dmatest code,
both of these _should_ share this common code so that fixes get done for
both!

> Facts:
> - Dmatest can be actually configured to run during boot.
> - Nobody besides the dma driver developer uses dmatest. This leaves
> holes for regressions that are really hard to debug due to
> interaction with the rest of the system.
> - Dmatest doesn't exist in most distribution kernels.

That doesn't mean it is not useful. This line of thought is not quite right.
You are trying to say dmatest in not important and selftest is. Sorry but
you are wrong, both are equally important and since both try to test and use
similar routines (dmaengien API) they need to share the code and not
duplicate it

> If we want to do something else, I need clear directions. I can
> remove the self test code completely from my driver. But, I need an
> equivalent functionality.

Add selftest to dmatest, we need both!

> 
> >
> > That part is tricky, you need to do so thru clients, spi/audio/serial etc
> >
> 
> My selftest code actually attaches to all slave devices and issues a
> memcpy command and then detaches from the slave devices.

Not everyone supports memcpy!
Sinan Kaya Nov. 5, 2015, 4:17 p.m. UTC | #17
On 11/5/2015 7:05 AM, Vinod Koul wrote:
> On Wed, Nov 04, 2015 at 09:42:46PM -0500, Sinan Kaya wrote:
>> Here is what I proposed.
>>
>> - a common file that gets compiled into a module that wants to use
>> self-test with a public API. It can be called from driver's probe
>> routine.
>> - the test is independent of my implementation. It uses dmaengine
>> API and should be portable to most drivers.
>> - there *are* still drivers in the kernel that has selftest code
>> embedded inside them. I followed the design pattern from other
>> drivers thinking this must have been a good idea and it paid off for
>> me.
>>
>> As far as I understand, there is interest in doing more than this
>> and reusing the dmatest code for code duplication.
>
> the code that selftest uses to test will be very similar to dmatest code,
> both of these _should_ share this common code so that fixes get done for
> both!
>

OK, I can move the code around and try to combine it if possible.

>> Facts:
>> - Dmatest can be actually configured to run during boot.
>> - Nobody besides the dma driver developer uses dmatest. This leaves
>> holes for regressions that are really hard to debug due to
>> interaction with the rest of the system.
>> - Dmatest doesn't exist in most distribution kernels.
>
> That doesn't mean it is not useful. This line of thought is not quite right.
> You are trying to say dmatest in not important and selftest is. Sorry but
> you are wrong, both are equally important and since both try to test and use
> similar routines (dmaengien API) they need to share the code and not
> duplicate it
>
>> If we want to do something else, I need clear directions. I can
>> remove the self test code completely from my driver. But, I need an
>> equivalent functionality.
>
> Add selftest to dmatest, we need both!
>

OK, do you have any objections to compiling dmatest along with hidma in 
the same module and calling a function from there ? or do you have 
something else in your mind ?

>>
>>>
>>> That part is tricky, you need to do so thru clients, spi/audio/serial etc
>>>
>>
>> My selftest code actually attaches to all slave devices and issues a
>> memcpy command and then detaches from the slave devices.
>
> Not everyone supports memcpy!
>
Right, last time I checked; you can request a DMA channel that supports 
MEMCPY specifically.
Sinan Kaya Nov. 7, 2015, 6:23 a.m. UTC | #18
On 11/5/2015 11:17 AM, Sinan Kaya wrote:
>
>
> On 11/5/2015 7:05 AM, Vinod Koul wrote:
>> On Wed, Nov 04, 2015 at 09:42:46PM -0500, Sinan Kaya wrote:
>>> Here is what I proposed.
>>>
>>> - a common file that gets compiled into a module that wants to use
>>> self-test with a public API. It can be called from driver's probe
>>> routine.
>>> - the test is independent of my implementation. It uses dmaengine
>>> API and should be portable to most drivers.
>>> - there *are* still drivers in the kernel that has selftest code
>>> embedded inside them. I followed the design pattern from other
>>> drivers thinking this must have been a good idea and it paid off for
>>> me.
>>>
>>> As far as I understand, there is interest in doing more than this
>>> and reusing the dmatest code for code duplication.
>>
>> the code that selftest uses to test will be very similar to dmatest code,
>> both of these _should_ share this common code so that fixes get done for
>> both!
>>
>
> OK, I can move the code around and try to combine it if possible.
>

I looked at this. IMO, merging selftest code and dmatest code is not a 
good idea.

Dmatest code has been well written and structured so that multiple DMA 
capabilities (XOR, PQ, MEMCPY) can be tested with the same code.

It supports threads and user space interaction.

The code I want to change (dmatest_func) is 3 levels deep in structure. 
My refactored code looked really ugly compared to the original code.


>>> Facts:
>>> - Dmatest can be actually configured to run during boot.
>>> - Nobody besides the dma driver developer uses dmatest. This leaves
>>> holes for regressions that are really hard to debug due to
>>> interaction with the rest of the system.
>>> - Dmatest doesn't exist in most distribution kernels.
>>
>> That doesn't mean it is not useful. This line of thought is not quite
>> right.
>> You are trying to say dmatest in not important and selftest is. Sorry but
>> you are wrong, both are equally important and since both try to test
>> and use
>> similar routines (dmaengien API) they need to share the code and not
>> duplicate it
>>
>>> If we want to do something else, I need clear directions. I can
>>> remove the self test code completely from my driver. But, I need an
>>> equivalent functionality.
>>
>> Add selftest to dmatest, we need both!
>>
>
> OK, do you have any objections to compiling dmatest along with hidma in
> the same module and calling a function from there ? or do you have
> something else in your mind ?
>

ping

>>>
>>>>
>>>> That part is tricky, you need to do so thru clients,
>>>> spi/audio/serial etc
>>>>
>>>
>>> My selftest code actually attaches to all slave devices and issues a
>>> memcpy command and then detaches from the slave devices.
>>
>> Not everyone supports memcpy!
>>
> Right, last time I checked; you can request a DMA channel that supports
> MEMCPY specifically.
>
Vinod Koul Nov. 8, 2015, 1:53 p.m. UTC | #19
On Sat, Nov 07, 2015 at 01:23:34AM -0500, Sinan Kaya wrote:
> 
> 
> On 11/5/2015 11:17 AM, Sinan Kaya wrote:
> >
> >
> >On 11/5/2015 7:05 AM, Vinod Koul wrote:
> >>On Wed, Nov 04, 2015 at 09:42:46PM -0500, Sinan Kaya wrote:
> >>>Here is what I proposed.
> >>>
> >>>- a common file that gets compiled into a module that wants to use
> >>>self-test with a public API. It can be called from driver's probe
> >>>routine.
> >>>- the test is independent of my implementation. It uses dmaengine
> >>>API and should be portable to most drivers.
> >>>- there *are* still drivers in the kernel that has selftest code
> >>>embedded inside them. I followed the design pattern from other
> >>>drivers thinking this must have been a good idea and it paid off for
> >>>me.
> >>>
> >>>As far as I understand, there is interest in doing more than this
> >>>and reusing the dmatest code for code duplication.
> >>
> >>the code that selftest uses to test will be very similar to dmatest code,
> >>both of these _should_ share this common code so that fixes get done for
> >>both!
> >>
> >
> >OK, I can move the code around and try to combine it if possible.
> >
> 
> I looked at this. IMO, merging selftest code and dmatest code is not
> a good idea.
> 
> Dmatest code has been well written and structured so that multiple
> DMA capabilities (XOR, PQ, MEMCPY) can be tested with the same code.
> 
> It supports threads and user space interaction.
> 
> The code I want to change (dmatest_func) is 3 levels deep in
> structure. My refactored code looked really ugly compared to the
> original code.

dmatest_func is still a bigger fn specific to dmatest. I was thinking that we
should have rather have two common functions
1) dmatest_do_dma() which does buffer allocation, invoking dmaengine APIs and
checking results, part of dmatest_func today

2) request_channels() would also be common along with cleanup routines

> >>>Facts:
> >>>- Dmatest can be actually configured to run during boot.
> >>>- Nobody besides the dma driver developer uses dmatest. This leaves
> >>>holes for regressions that are really hard to debug due to
> >>>interaction with the rest of the system.
> >>>- Dmatest doesn't exist in most distribution kernels.
> >>
> >>That doesn't mean it is not useful. This line of thought is not quite
> >>right.
> >>You are trying to say dmatest in not important and selftest is. Sorry but
> >>you are wrong, both are equally important and since both try to test
> >>and use
> >>similar routines (dmaengien API) they need to share the code and not
> >>duplicate it
> >>
> >>>If we want to do something else, I need clear directions. I can
> >>>remove the self test code completely from my driver. But, I need an
> >>>equivalent functionality.
> >>
> >>Add selftest to dmatest, we need both!
> >>
> >
> >OK, do you have any objections to compiling dmatest along with hidma in
> >the same module and calling a function from there ? or do you have
> >something else in your mind ?
Not the dmatest completely, that won't be right, but yes for the dmatest
core which is common b/w both. We cna put this is separate file to compile
along with driver if that is users requirement
Sinan Kaya Nov. 13, 2015, 8:20 p.m. UTC | #20
>>
>> I looked at this. IMO, merging selftest code and dmatest code is not
>> a good idea.
>>
>> Dmatest code has been well written and structured so that multiple
>> DMA capabilities (XOR, PQ, MEMCPY) can be tested with the same code.
>>
>> It supports threads and user space interaction.
>>
>> The code I want to change (dmatest_func) is 3 levels deep in
>> structure. My refactored code looked really ugly compared to the
>> original code.
>
> dmatest_func is still a bigger fn specific to dmatest. I was thinking that
> we
> should have rather have two common functions
> 1) dmatest_do_dma() which does buffer allocation, invoking dmaengine APIs
> and
> checking results, part of dmatest_func today
>
> 2) request_channels() would also be common along with cleanup routines
>

Tried again,

It still doesn't look right. Depending on the used API, the source and
destination buffers are different.

For instance, when you use dma_map_page, your source addresses are pages.
If you use scatter_sg, you are working on the scatter-gather list. If you
use dma_coherent, then the source cannot be a kmalloc API. Source comes
from the DMA API. If you use dma_map_single, it is a kmalloc buffer. This
is causing havoc in the compare APIs.

IMO, in the goals of sharing code, we are making it unmanageable.

I'm leaning towards dropping this patch altogether (which I'm not very
happy about) or pushing it this way and have somebody do the
reorganization in another iteration.




--
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/drivers/dma/dmaengine.h b/drivers/dma/dmaengine.h
index 17f983a..05b5a84 100644
--- a/drivers/dma/dmaengine.h
+++ b/drivers/dma/dmaengine.h
@@ -86,4 +86,6 @@  static inline void dma_set_residue(struct dma_tx_state *state, u32 residue)
 		state->residue = residue;
 }
 
+int dma_selftest_memcpy(struct dma_device *dmadev);
+
 #endif
diff --git a/drivers/dma/dmaselftest.c b/drivers/dma/dmaselftest.c
new file mode 100644
index 0000000..324f7c4
--- /dev/null
+++ b/drivers/dma/dmaselftest.c
@@ -0,0 +1,669 @@ 
+/*
+ * DMA self test code borrowed from Qualcomm Technologies HIDMA driver
+ *
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/list.h>
+#include <linux/atomic.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+struct test_result {
+	atomic_t counter;
+	wait_queue_head_t wq;
+	struct dma_device *dmadev;
+};
+
+static void dma_selftest_complete(void *arg)
+{
+	struct test_result *result = arg;
+	struct dma_device *dmadev = result->dmadev;
+
+	atomic_inc(&result->counter);
+	wake_up(&result->wq);
+	dev_dbg(dmadev->dev, "self test transfer complete :%d\n",
+		atomic_read(&result->counter));
+}
+
+/*
+ * Perform a transaction to verify the HW works.
+ */
+static int dma_selftest_sg(struct dma_device *dmadev,
+			struct dma_chan *dma_chanptr, u64 size,
+			unsigned long flags)
+{
+	dma_addr_t src_dma, dest_dma, dest_dma_it;
+	u8 *dest_buf;
+	u32 i, j = 0;
+	dma_cookie_t cookie;
+	struct dma_async_tx_descriptor *tx;
+	int err = 0;
+	int ret;
+	struct sg_table sg_table;
+	struct scatterlist	*sg;
+	int nents = 10, count;
+	bool free_channel = 1;
+	u8 *src_buf;
+	int map_count;
+	struct test_result result;
+
+	init_waitqueue_head(&result.wq);
+	atomic_set(&result.counter, 0);
+	result.dmadev = dmadev;
+
+	if (!dma_chanptr)
+		return -ENOMEM;
+
+	if (dmadev->device_alloc_chan_resources(dma_chanptr) < 1)
+		return -ENODEV;
+
+	if (!dma_chanptr->device || !dmadev->dev) {
+		dmadev->device_free_chan_resources(dma_chanptr);
+		return -ENODEV;
+	}
+
+	ret = sg_alloc_table(&sg_table, nents, GFP_KERNEL);
+	if (ret) {
+		err = ret;
+		goto sg_table_alloc_failed;
+	}
+
+	for_each_sg(sg_table.sgl, sg, nents, i) {
+		u64 alloc_sz;
+		void *cpu_addr;
+
+		alloc_sz = round_up(size, nents);
+		do_div(alloc_sz, nents);
+		cpu_addr = kmalloc(alloc_sz, GFP_KERNEL);
+
+		if (!cpu_addr) {
+			err = -ENOMEM;
+			goto sg_buf_alloc_failed;
+		}
+
+		dev_dbg(dmadev->dev, "set sg buf[%d] :%p\n", i, cpu_addr);
+		sg_set_buf(sg, cpu_addr, alloc_sz);
+	}
+
+	dest_buf = kmalloc(round_up(size, nents), GFP_KERNEL);
+	if (!dest_buf) {
+		err = -ENOMEM;
+		goto dst_alloc_failed;
+	}
+	dev_dbg(dmadev->dev, "dest:%p\n", dest_buf);
+
+	/* Fill in src buffer */
+	count = 0;
+	for_each_sg(sg_table.sgl, sg, nents, i) {
+		src_buf = sg_virt(sg);
+		dev_dbg(dmadev->dev,
+			"set src[%d, %d, %p] = %d\n", i, j, src_buf, count);
+
+		for (j = 0; j < sg_dma_len(sg); j++)
+			src_buf[j] = count++;
+	}
+
+	/* dma_map_sg cleans and invalidates the cache in arm64 when
+	 * DMA_TO_DEVICE is selected for src. That's why, we need to do
+	 * the mapping after the data is copied.
+	 */
+	map_count = dma_map_sg(dmadev->dev, sg_table.sgl, nents,
+				DMA_TO_DEVICE);
+	if (!map_count) {
+		err =  -EINVAL;
+		goto src_map_failed;
+	}
+
+	dest_dma = dma_map_single(dmadev->dev, dest_buf,
+				size, DMA_FROM_DEVICE);
+
+	err = dma_mapping_error(dmadev->dev, dest_dma);
+	if (err)
+		goto dest_map_failed;
+
+	/* check scatter gather list contents */
+	for_each_sg(sg_table.sgl, sg, map_count, i)
+		dev_dbg(dmadev->dev,
+			"[%d/%d] src va=%p, iova = %pa len:%d\n",
+			i, map_count, sg_virt(sg), &sg_dma_address(sg),
+			sg_dma_len(sg));
+
+	dest_dma_it = dest_dma;
+	for_each_sg(sg_table.sgl, sg, map_count, i) {
+		src_buf = sg_virt(sg);
+		src_dma = sg_dma_address(sg);
+		dev_dbg(dmadev->dev, "src_dma: %pad dest_dma:%pad\n",
+			&src_dma, &dest_dma_it);
+
+		tx = dmadev->device_prep_dma_memcpy(dma_chanptr, dest_dma_it,
+				src_dma, sg_dma_len(sg), flags);
+		if (!tx) {
+			dev_err(dmadev->dev,
+				"Self-test sg failed, disabling\n");
+			err = -ENODEV;
+			goto prep_memcpy_failed;
+		}
+
+		tx->callback_param = &result;
+		tx->callback = dma_selftest_complete;
+		cookie = tx->tx_submit(tx);
+		dest_dma_it += sg_dma_len(sg);
+	}
+
+	dmadev->device_issue_pending(dma_chanptr);
+
+	/*
+	 * It is assumed that the hardware can move the data within 1s
+	 * and signal the OS of the completion
+	 */
+	ret = wait_event_timeout(result.wq,
+		atomic_read(&result.counter) == (map_count),
+				msecs_to_jiffies(10000));
+
+	if (ret <= 0) {
+		dev_err(dmadev->dev,
+			"Self-test sg copy timed out, disabling\n");
+		err = -ENODEV;
+		goto tx_status;
+	}
+	dev_dbg(dmadev->dev,
+		"Self-test complete signal received\n");
+
+	if (dmadev->device_tx_status(dma_chanptr, cookie, NULL) !=
+				DMA_COMPLETE) {
+		dev_err(dmadev->dev,
+			"Self-test sg status not complete, disabling\n");
+		err = -ENODEV;
+		goto tx_status;
+	}
+
+	dma_sync_single_for_cpu(dmadev->dev, dest_dma, size,
+				DMA_FROM_DEVICE);
+
+	count = 0;
+	for_each_sg(sg_table.sgl, sg, map_count, i) {
+		src_buf = sg_virt(sg);
+		if (memcmp(src_buf, &dest_buf[count], sg_dma_len(sg)) == 0) {
+			count += sg_dma_len(sg);
+			continue;
+		}
+
+		for (j = 0; j < sg_dma_len(sg); j++) {
+			if (src_buf[j] != dest_buf[count]) {
+				dev_dbg(dmadev->dev,
+				"[%d, %d] (%p) src :%x dest (%p):%x cnt:%d\n",
+					i, j, &src_buf[j], src_buf[j],
+					&dest_buf[count], dest_buf[count],
+					count);
+				dev_err(dmadev->dev,
+				 "Self-test copy failed compare, disabling\n");
+				err = -EFAULT;
+				return err;
+				goto compare_failed;
+			}
+			count++;
+		}
+	}
+
+	/*
+	 * do not release the channel
+	 * we want to consume all the channels on self test
+	 */
+	free_channel = 0;
+
+compare_failed:
+tx_status:
+prep_memcpy_failed:
+	dma_unmap_single(dmadev->dev, dest_dma, size,
+			 DMA_FROM_DEVICE);
+dest_map_failed:
+	dma_unmap_sg(dmadev->dev, sg_table.sgl, nents,
+			DMA_TO_DEVICE);
+
+src_map_failed:
+	kfree(dest_buf);
+
+dst_alloc_failed:
+sg_buf_alloc_failed:
+	for_each_sg(sg_table.sgl, sg, nents, i) {
+		if (sg_virt(sg))
+			kfree(sg_virt(sg));
+	}
+	sg_free_table(&sg_table);
+sg_table_alloc_failed:
+	if (free_channel)
+		dmadev->device_free_chan_resources(dma_chanptr);
+
+	return err;
+}
+
+/*
+ * Perform a streaming transaction to verify the HW works.
+ */
+static int dma_selftest_streaming(struct dma_device *dmadev,
+			struct dma_chan *dma_chanptr, u64 size,
+			unsigned long flags)
+{
+	dma_addr_t src_dma, dest_dma;
+	u8 *dest_buf, *src_buf;
+	u32 i;
+	dma_cookie_t cookie;
+	struct dma_async_tx_descriptor *tx;
+	int err = 0;
+	int ret;
+	bool free_channel = 1;
+	struct test_result result;
+
+	init_waitqueue_head(&result.wq);
+	atomic_set(&result.counter, 0);
+	result.dmadev = dmadev;
+
+	if (!dma_chanptr)
+		return -ENOMEM;
+
+	if (dmadev->device_alloc_chan_resources(dma_chanptr) < 1)
+		return -ENODEV;
+
+	if (!dma_chanptr->device || !dmadev->dev) {
+		dmadev->device_free_chan_resources(dma_chanptr);
+		return -ENODEV;
+	}
+
+	src_buf = kmalloc(size, GFP_KERNEL);
+	if (!src_buf) {
+		err = -ENOMEM;
+		goto src_alloc_failed;
+	}
+
+	dest_buf = kmalloc(size, GFP_KERNEL);
+	if (!dest_buf) {
+		err = -ENOMEM;
+		goto dst_alloc_failed;
+	}
+
+	dev_dbg(dmadev->dev, "src: %p dest:%p\n", src_buf, dest_buf);
+
+	/* Fill in src buffer */
+	for (i = 0; i < size; i++)
+		src_buf[i] = (u8)i;
+
+	/* dma_map_single cleans and invalidates the cache in arm64 when
+	 * DMA_TO_DEVICE is selected for src. That's why, we need to do
+	 * the mapping after the data is copied.
+	 */
+	src_dma = dma_map_single(dmadev->dev, src_buf,
+				 size, DMA_TO_DEVICE);
+
+	err = dma_mapping_error(dmadev->dev, src_dma);
+	if (err)
+		goto src_map_failed;
+
+	dest_dma = dma_map_single(dmadev->dev, dest_buf,
+				size, DMA_FROM_DEVICE);
+
+	err = dma_mapping_error(dmadev->dev, dest_dma);
+	if (err)
+		goto dest_map_failed;
+	dev_dbg(dmadev->dev, "src_dma: %pad dest_dma:%pad\n", &src_dma,
+		&dest_dma);
+	tx = dmadev->device_prep_dma_memcpy(dma_chanptr, dest_dma, src_dma,
+					size, flags);
+	if (!tx) {
+		dev_err(dmadev->dev,
+			"Self-test streaming failed, disabling\n");
+		err = -ENODEV;
+		goto prep_memcpy_failed;
+	}
+
+	tx->callback_param = &result;
+	tx->callback = dma_selftest_complete;
+	cookie = tx->tx_submit(tx);
+	dmadev->device_issue_pending(dma_chanptr);
+
+	/*
+	 * It is assumed that the hardware can move the data within 1s
+	 * and signal the OS of the completion
+	 */
+	ret = wait_event_timeout(result.wq,
+				atomic_read(&result.counter) == 1,
+				msecs_to_jiffies(10000));
+
+	if (ret <= 0) {
+		dev_err(dmadev->dev,
+			"Self-test copy timed out, disabling\n");
+		err = -ENODEV;
+		goto tx_status;
+	}
+	dev_dbg(dmadev->dev, "Self-test complete signal received\n");
+
+	if (dmadev->device_tx_status(dma_chanptr, cookie, NULL) !=
+				DMA_COMPLETE) {
+		dev_err(dmadev->dev,
+			"Self-test copy timed out, disabling\n");
+		err = -ENODEV;
+		goto tx_status;
+	}
+
+	dma_sync_single_for_cpu(dmadev->dev, dest_dma, size,
+				DMA_FROM_DEVICE);
+
+	if (memcmp(src_buf, dest_buf, size)) {
+		for (i = 0; i < size/4; i++) {
+			if (((u32 *)src_buf)[i] != ((u32 *)(dest_buf))[i]) {
+				dev_dbg(dmadev->dev,
+					"[%d] src data:%x dest data:%x\n",
+					i, ((u32 *)src_buf)[i],
+					((u32 *)(dest_buf))[i]);
+				break;
+			}
+		}
+		dev_err(dmadev->dev,
+			"Self-test copy failed compare, disabling\n");
+		err = -EFAULT;
+		goto compare_failed;
+	}
+
+	/*
+	 * do not release the channel
+	 * we want to consume all the channels on self test
+	 */
+	free_channel = 0;
+
+compare_failed:
+tx_status:
+prep_memcpy_failed:
+	dma_unmap_single(dmadev->dev, dest_dma, size,
+			 DMA_FROM_DEVICE);
+dest_map_failed:
+	dma_unmap_single(dmadev->dev, src_dma, size,
+			DMA_TO_DEVICE);
+
+src_map_failed:
+	kfree(dest_buf);
+
+dst_alloc_failed:
+	kfree(src_buf);
+
+src_alloc_failed:
+	if (free_channel)
+		dmadev->device_free_chan_resources(dma_chanptr);
+
+	return err;
+}
+
+/*
+ * Perform a coherent transaction to verify the HW works.
+ */
+static int dma_selftest_one_coherent(struct dma_device *dmadev,
+			struct dma_chan *dma_chanptr, u64 size,
+			unsigned long flags)
+{
+	dma_addr_t src_dma, dest_dma;
+	u8 *dest_buf, *src_buf;
+	u32 i;
+	dma_cookie_t cookie;
+	struct dma_async_tx_descriptor *tx;
+	int err = 0;
+	int ret;
+	bool free_channel = true;
+	struct test_result result;
+
+	init_waitqueue_head(&result.wq);
+	atomic_set(&result.counter, 0);
+	result.dmadev = dmadev;
+
+	if (!dma_chanptr)
+		return -ENOMEM;
+
+	if (dmadev->device_alloc_chan_resources(dma_chanptr) < 1)
+		return -ENODEV;
+
+	if (!dma_chanptr->device || !dmadev->dev) {
+		dmadev->device_free_chan_resources(dma_chanptr);
+		return -ENODEV;
+	}
+
+	src_buf = dma_alloc_coherent(dmadev->dev, size,
+				&src_dma, GFP_KERNEL);
+	if (!src_buf) {
+		err = -ENOMEM;
+		goto src_alloc_failed;
+	}
+
+	dest_buf = dma_alloc_coherent(dmadev->dev, size,
+				&dest_dma, GFP_KERNEL);
+	if (!dest_buf) {
+		err = -ENOMEM;
+		goto dst_alloc_failed;
+	}
+
+	dev_dbg(dmadev->dev, "src: %p dest:%p\n", src_buf, dest_buf);
+
+	/* Fill in src buffer */
+	for (i = 0; i < size; i++)
+		src_buf[i] = (u8)i;
+
+	dev_dbg(dmadev->dev, "src_dma: %pad dest_dma:%pad\n", &src_dma,
+		&dest_dma);
+	tx = dmadev->device_prep_dma_memcpy(dma_chanptr, dest_dma, src_dma,
+					size,
+					flags);
+	if (!tx) {
+		dev_err(dmadev->dev,
+			"Self-test coherent failed, disabling\n");
+		err = -ENODEV;
+		goto prep_memcpy_failed;
+	}
+
+	tx->callback_param = &result;
+	tx->callback = dma_selftest_complete;
+	cookie = tx->tx_submit(tx);
+	dmadev->device_issue_pending(dma_chanptr);
+
+	/*
+	 * It is assumed that the hardware can move the data within 1s
+	 * and signal the OS of the completion
+	 */
+	ret = wait_event_timeout(result.wq,
+				atomic_read(&result.counter) == 1,
+				msecs_to_jiffies(10000));
+
+	if (ret <= 0) {
+		dev_err(dmadev->dev,
+			"Self-test copy timed out, disabling\n");
+		err = -ENODEV;
+		goto tx_status;
+	}
+	dev_dbg(dmadev->dev, "Self-test complete signal received\n");
+
+	if (dmadev->device_tx_status(dma_chanptr, cookie, NULL) !=
+				DMA_COMPLETE) {
+		dev_err(dmadev->dev,
+			"Self-test copy timed out, disabling\n");
+		err = -ENODEV;
+		goto tx_status;
+	}
+
+	if (memcmp(src_buf, dest_buf, size)) {
+		for (i = 0; i < size/4; i++) {
+			if (((u32 *)src_buf)[i] != ((u32 *)(dest_buf))[i]) {
+				dev_dbg(dmadev->dev,
+					"[%d] src data:%x dest data:%x\n",
+					i, ((u32 *)src_buf)[i],
+					((u32 *)(dest_buf))[i]);
+				break;
+			}
+		}
+		dev_err(dmadev->dev,
+			"Self-test copy failed compare, disabling\n");
+		err = -EFAULT;
+		goto compare_failed;
+	}
+
+	/*
+	 * do not release the channel
+	 * we want to consume all the channels on self test
+	 */
+	free_channel = 0;
+
+compare_failed:
+tx_status:
+prep_memcpy_failed:
+	dma_free_coherent(dmadev->dev, size, dest_buf, dest_dma);
+
+dst_alloc_failed:
+	dma_free_coherent(dmadev->dev, size, src_buf, src_dma);
+
+src_alloc_failed:
+	if (free_channel)
+		dmadev->device_free_chan_resources(dma_chanptr);
+
+	return err;
+}
+
+static int dma_selftest_all(struct dma_device *dmadev,
+				bool req_coherent, bool req_sg)
+{
+	int rc = -ENODEV, i = 0;
+	struct dma_chan **dmach_ptr = NULL;
+	u32 max_channels = 0;
+	u64 sizes[] = {PAGE_SIZE - 1, PAGE_SIZE, PAGE_SIZE + 1, 2801, 13295};
+	int count = 0;
+	u32 j;
+	u64 size;
+	int failed = 0;
+	struct dma_chan *dmach = NULL;
+
+	list_for_each_entry(dmach, &dmadev->channels,
+			device_node) {
+		max_channels++;
+	}
+
+	dmach_ptr = kcalloc(max_channels, sizeof(*dmach_ptr), GFP_KERNEL);
+	if (!dmach_ptr) {
+		rc = -ENOMEM;
+		goto failed_exit;
+	}
+
+	for (j = 0; j < ARRAY_SIZE(sizes); j++) {
+		size = sizes[j];
+		count = 0;
+		dev_dbg(dmadev->dev, "test start for size:%llx\n", size);
+		list_for_each_entry(dmach, &dmadev->channels,
+				device_node) {
+			dmach_ptr[count] = dmach;
+			if (req_coherent)
+				rc = dma_selftest_one_coherent(dmadev,
+					dmach, size,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+			else if (req_sg)
+				rc = dma_selftest_sg(dmadev,
+					dmach, size,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+			else
+				rc = dma_selftest_streaming(dmadev,
+					dmach, size,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+			if (rc) {
+				failed = 1;
+				break;
+			}
+			dev_dbg(dmadev->dev,
+				"self test passed for ch:%d\n", count);
+			count++;
+		}
+
+		/*
+		 * free the channels where the test passed
+		 * Channel resources are freed for a test that fails.
+		 */
+		for (i = 0; i < count; i++)
+			dmadev->device_free_chan_resources(dmach_ptr[i]);
+
+		if (failed)
+			break;
+	}
+
+failed_exit:
+	kfree(dmach_ptr);
+
+	return rc;
+}
+
+static int dma_selftest_mapsngle(struct device *dev)
+{
+	u32 buf_size = 256;
+	char *src;
+	int ret = -ENOMEM;
+	dma_addr_t dma_src;
+
+	src = kmalloc(buf_size, GFP_KERNEL);
+	if (!src)
+		return -ENOMEM;
+
+	strcpy(src, "hello world");
+
+	dma_src = dma_map_single(dev, src, buf_size, DMA_TO_DEVICE);
+	dev_dbg(dev, "mapsingle: src:%p src_dma:%pad\n", src, &dma_src);
+
+	ret = dma_mapping_error(dev, dma_src);
+	if (ret) {
+		dev_err(dev, "dma_mapping_error with ret:%d\n", ret);
+		ret = -ENOMEM;
+	} else {
+		if (strcmp(src, "hello world") != 0) {
+			dev_err(dev, "memory content mismatch\n");
+			ret = -EINVAL;
+		} else
+			dev_dbg(dev, "mapsingle:dma_map_single works\n");
+
+		dma_unmap_single(dev, dma_src, buf_size, DMA_TO_DEVICE);
+	}
+	kfree(src);
+	return ret;
+}
+
+/*
+ * Self test all DMA channels.
+ */
+int dma_selftest_memcpy(struct dma_device *dmadev)
+{
+	int rc;
+
+	dma_selftest_mapsngle(dmadev->dev);
+
+	/* streaming test */
+	rc = dma_selftest_all(dmadev, false, false);
+	if (rc)
+		return rc;
+	dev_dbg(dmadev->dev, "streaming self test passed\n");
+
+	/* coherent test */
+	rc = dma_selftest_all(dmadev, true, false);
+	if (rc)
+		return rc;
+
+	dev_dbg(dmadev->dev, "coherent self test passed\n");
+
+	/* scatter gather test */
+	rc = dma_selftest_all(dmadev, false, true);
+	if (rc)
+		return rc;
+
+	dev_dbg(dmadev->dev, "scatter gather self test passed\n");
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dma_selftest_memcpy);