mrigendra
mrigendra

Reputation: 1566

Why DMA driver memcpy is not working in some cases

I have written somewhat working driver for mem2mem for buffer copy without device tree using only kernel dma api as below (without error handling),

Tested on beaglebone black rev C

# uname -r
5.4.242 
#include <linux/module.h>
#include <linux/init.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>

void *src,  *dst;
struct dma_chan *pChan;
static int dmaTransfer(const void *pSrc, void *pDst, unsigned int len);

static int dmaTransfer(const void *pSrc, void *pDst, unsigned int len)
{
    enum dma_status status = DMA_ERROR;
    struct dmaengine_unmap_data *pUmData;
    unsigned char align;
    unsigned int alignLen;
    struct page *pPage;
    unsigned long pgOff;
    struct dma_device *pDev;
    struct dma_async_tx_descriptor *pTx = NULL;
    dma_addr_t dmaSrcAddr, dmaDstAddr;
    dma_cookie_t dmaCookie;
    pr_info("dma4-module dmaTransfer: before dst: %d, src: %d", *(int *)dst, *(int *)src);
    dma_cap_mask_t mask;
    dma_cap_zero(mask);
    dma_cap_set(DMA_MEMCPY, mask);  

    align = 0;
    alignLen = len;

    alignLen = (alignLen >> align) << align;
    if (alignLen == 0) {
            alignLen = 1 << align;
    }
        
    pChan = dma_request_channel(mask, NULL, NULL);
    pr_info("dma4-module dmaTransfer: dma channel name : %s", dma_chan_name(pChan));
    pDev = pChan->device;
    pUmData = dmaengine_get_unmap_data(pDev->dev, 2, GFP_KERNEL);
    pPage = virt_to_page(pSrc);
    pgOff = offset_in_page(pSrc);
    pUmData->addr[0] = dma_map_page(pDev->dev, pPage, pgOff, pUmData->len, DMA_TO_DEVICE);
    dmaSrcAddr = pUmData->addr[0];
    dma_mapping_error(pDev->dev, pUmData->addr[0]);

    pPage = virt_to_page(pDst);
    pgOff = offset_in_page(pDst);
    pUmData->addr[1] = dma_map_page(pDev->dev, pPage, pgOff, pUmData->len, DMA_FROM_DEVICE);
    dmaDstAddr = pUmData->addr[1];
    dma_mapping_error(pDev->dev, pUmData->addr[1]);

    pTx = pDev->device_prep_dma_memcpy(pChan, dmaDstAddr, dmaSrcAddr, alignLen, DMA_CTRL_ACK | DMA_PREP_INTERRUPT);
    dmaCookie = pTx->tx_submit(pTx);
    dma_submit_error(dmaCookie);

    status = dma_sync_wait(pChan, dmaCookie);
    dmaengine_terminate_sync(pChan);
    if (status != DMA_COMPLETE) {
                    switch (status) {
                    case DMA_IN_PROGRESS:
                        pr_info("dma4-module dmaTransfer: DMA in progres...");
                        break;
                    case DMA_PAUSED:
                        pr_info("dma4-module dmaTransfer: DMA paused...");
                        break;
                    case DMA_ERROR:
                        pr_info("dma4-module dmaTransfer: DMA error");
                        break;
                    default:
                        ;
                    }
                }
                else {
                    pr_info("dma4-module dmaTransfer: DMA transfer complete...");
                }
    pr_info("dma4-module dmaTransfer: after dst: %d, src: %d", *(int *)dst, *(int *)src);
    dmaengine_unmap_put(pUmData);
    dmaengine_terminate_sync(pChan);
    dma_release_channel(pChan);

    return 0;
}

static int __init mod_init(void)
{
    int *writer;
    pr_info("dma4-module: in init\n");

    src = kzalloc(16,GFP_KERNEL);
    dst = kzalloc(16,GFP_KERNEL);
    writer = (int *)src;
    *writer = 65;
    pr_info("dma4-module mod_init: before dst: %d, src: %d", *(int *)dst, *(int *)src);
    dmaTransfer(src, dst, 16);
    pr_info("dma4-module mod_init: after dst: %d, src: %d", *(int *)dst, *(int *)src);
    pr_info("dma4-module mod_init: init done\n");

    return 0;
}

static void __exit mod_exit(void)
{
    pr_info("dma4-module mod_exit: mod_exit called\n");
    kfree(src);
    kfree(dst);
    pr_info("dma4-module mod_exit: released channel");
}

module_init( mod_init );
module_exit( mod_exit );

MODULE_AUTHOR("[email protected]");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("DMA engine api/memcpy test.");

Above module sometimes work, and sometimes it doesn't work. Log for same,

[  471.278017] dma4-module: in init
[  471.281548] dma4-module: before dst: 0, src: 65
[  471.281577] edma 49000000.edma: private_candidate: wrong capabilities
[  471.286149] edma 49000000.edma: EER0 00000000
[  471.286168] edma 49000000.edma: Got eDMA channel 20 for virt channel 0 (SW trigger)
[  471.286180] dmaengine: __dma_request_channel: success (dma1chan0)
[  471.286186] dma4-module: dma channel name : dma1chan0
[  471.286233] edma 49000000.edma: vchan 8d3312c7: txd be193a83[9]: submitted
[  471.291413] edma 49000000.edma: first transfer starting on channel 20
[  471.291426] edma 49000000.edma: ESR0 00000000
[  471.291448] edma 49000000.edma: EER0 00000000
[  471.291461] edma 49000000.edma: Transfer completed on channel 20
[  471.291487] dma4-module: DMA transfer complete...
[  471.291505] edma 49000000.edma: EER0 00000000
[  471.296242] edma 49000000.edma: EER0 00000000
[  471.296254] edma 49000000.edma: Free eDMA channel 20 for virt channel 0
[  471.296262] dma4-module: after dst: 0, src: 65 <<-- value is not updated in dst buffer
[  471.296265] dma4-module: init done

rmmod'd module and ten again insmoded

[  638.845004] dma4-module: in init
[  638.848295] dma4-module: before dst: 0, src: 65
[  638.848324] edma 49000000.edma: private_candidate: wrong capabilities
[  638.853117] edma 49000000.edma: EER0 00000000
[  638.853139] edma 49000000.edma: Got eDMA channel 20 for virt channel 0 (SW trigger)
[  638.853152] dmaengine: __dma_request_channel: success (dma1chan0)
[  638.853159] dma4-module: dma channel name : dma1chan0
[  638.853202] edma 49000000.edma: vchan 8d3312c7: txd 2c0b7c5f[11]: submitted
[  638.858297] edma 49000000.edma: first transfer starting on channel 20
[  638.858309] edma 49000000.edma: ESR0 00000000
[  638.858330] edma 49000000.edma: EER0 00000000
[  638.858343] edma 49000000.edma: Transfer completed on channel 20
[  638.858371] dma4-module: DMA transfer complete...
[  638.858390] edma 49000000.edma: EER0 00000000
[  638.863206] edma 49000000.edma: EER0 00000000
[  638.863219] edma 49000000.edma: Free eDMA channel 20 for virt channel 0
[  638.863228] dma4-module: after dst: 65, src: 65  <<-- correct value updated in dst buffer
[  638.863232] dma4-module: init done

Any idea why sometimes I get correct buffer values (65) and sometimes don't?

Upvotes: 1

Views: 121

Answers (0)

Related Questions