为何基于Kernel DMA API的memcpy驱动有时无法正常工作?
DMA内存拷贝驱动偶发拷贝失败问题
环境信息
- 硬件:BeagleBone Black Rev C
- 内核版本:5.4.242
- 驱动说明:不依赖设备树,基于Kernel DMA API实现的mem2mem缓冲区拷贝驱动(未实现错误处理)
内核版本验证:
# 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("Mrigendra.Chaubey@gmail.com"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("DMA engine api/memcpy test.");
问题现象
该驱动有时能正常完成内存拷贝,有时则无法更新目标缓冲区。以下是两次执行的内核日志:
拷贝失败的日志
[ 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 <<-- 目标缓冲区未更新 [ 471.296265] dma4-module: init done
拷贝成功的日志
[ 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 <<-- 目标缓冲区已正确更新 [ 638.863232] dma4-module: init done
疑问
为何驱动有时能正确更新目标缓冲区,有时却无法完成拷贝?
内容的提问来源于stack exchange,提问作者mrigendra
相关产品推荐
相关产品推荐

