5

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

我用 .kmalloc 内存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 内存卫生。

谢谢!

4

3 回答 3

5

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

随着dma_unmap_single您取消映射上面映射的内存。您应该在转帐结束时执行此操作。

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

于 2013-04-23T21:57:58.720 回答
0

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

于 2017-10-04T00:07:21.020 回答
0

简短的回答:

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

长答案:

也是的。
您应该dma_unmap_single在每个 DMA 事务结束时调用。

但由于这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 */                 
    }
于 2020-10-16T13:33:46.007 回答