每个dma_map_single调用都需要相应的dma_unmap_single吗?

时间:2013-04-23 14:34:50

标签: linux linux-kernel linux-device-driver

我正在将一个大型代码库移植到Linux内核设备驱动程序。 ASIC使用大量DMA通道。

我使用GFP_KERNEL|GFP_DMA创建了内存。在启动DMA之前,我使用dma_map_single来获取硬件(物理)内存地址以提供给硬件。 (还是从dcache中刷新/使内存无效吗?)DMA完成后,有时需要CPU访问数据,但不经常。在我通过代码访问数据之前,我执行dma_unmap_single以避免缓存一致性问题。

在我不需要CPU访问的情况下,是否仍需要拨打dma_unmap_single?我应该dma_unmap_single每个指针dma_map_single吗? dma_map_single是否会占用dma_unmap_single将要发布的资源(例如,表格条目)

DMA-API.txt不清楚良好的DMA内存卫生。

谢谢!

3 个答案:

答案 0 :(得分:3)

使用dma_map_single映射内存以进行DMA传输。您获得了指向内存的物理指针,因此设备可以DMA到该地址。

使用dma_unmap_single取消映射上面映射的内存。转移结束后,您应该这样做。

您可以映射内存区域,并将其用于多个DMA传输,然后在作业完成后取消映射。每次要访问DMA存储器时,都必须同步它。如果设备要访问内存,您应该dma_sync_single_for_device;如果主机要访问内存,你应该dma_sync_single_for_cpu

答案 1 :(得分:0)

dma_map api可能会分配反弹缓冲区,在这种情况下,您需要调用dma_unmap函数。

答案 2 :(得分:0)

简短答案:

是的。您应该dma_unmap_single dma_map_single映射的每个缓冲区。

详细答案:

也是。
您应该在每个DMA事务结束时致电dma_unmap_single

但是,由于dma_map_single / dma_unmap_single是一项昂贵的操作,因此有时我们可能更喜欢(当数据包不太大时)重用DMA映射的缓冲区,因此不要在结束时调用dma_unmap_single DMA事务,我们正在调用dma_sync_single_for_cpu,然后将数据从DMA映射的缓冲区复制到其他缓冲区,然后调用dma_sync_single_for_device,因此现在我们可以重用已经DMA映射的缓冲区而无需取消映射和在我们的下一个DMA事务之前再次重新映射它。

我想数据包在不同架构之间变化的阈值应该被测量。

measure_time(dma_sync_single_for_cpu + memcpy(packet_size) + dma_sync_single_for_device) 
>?<
measure_time(dma_unmap_single + dma_map_single)

简短示例:

    if (frame_len < FRAME_LEN_THRESHOLD) {            
        skb = netdev_alloc_skb_ip_align(priv->dev, frame_len);
        if (unlikely(!skb)) {
            printk("packet dropped\n");
            continue;
        }
        
        dma_sync_single_for_cpu(priv->device, rx_skbuff_dma[entry],                                   
                                frame_len, DMA_FROM_DEVICE);

        // same as memcpy
        skb_copy_to_linear_data(skb, rx_skbuff[entry]->data, frame_len);

        dma_sync_single_for_device(priv->device, rx_skbuff_dma[entry],
                                   frame_len, DMA_FROM_DEVICE);

        /* now we can reuse rx_skbuff_dma[entry]. 
           no need to call dma_unmap_single */                           
    } else {
        skb = rx_skbuff[entry];
        if (unlikely(!skb)) {
            printk("packet dropped\n");
            continue;
        }

        rx_skbuff[entry] = NULL;

        dma_unmap_single(priv->device, rx_skbuff_dma[entry],
                         priv->dma_buffer_size, DMA_FROM_DEVICE);

        /* if we want to use rx_skbuff_dma[entry] for another DMA transaction, 
           we will need to realocate a buffer and call dma_map_single */                 
    }