【问题标题】:Does every dma_map_single call require a corresponding dma_unmap_single?每个 dma_map_single 调用是否都需要相应的 dma_unmap_single?
【发布时间】:2013-04-23 14:34:50
【问题描述】:

我正在将大型代码库移植到 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 内存卫生。

谢谢!

【问题讨论】:

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


    【解决方案1】:

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

    使用dma_unmap_single,您可以取消映射上面映射的内存。您应该在转移结束时执行此操作。

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

    【讨论】:

    • 是不是“dma_map_single”只是简单地将虚拟地址转换为物理地址?
    • simply 是一个大词,快速而肮脏的分析器是“是”。关注implementation了解详情。还要检查DMA API documentation,因为它比stackoverflow 上的任何单词都好:)
    • 准确地说,dma_map_single 正在将虚拟地址转换为总线地址。总线地址可以不同于物理地址。我认为kernel.org/doc/Documentation/DMA-API-HOWTO.txt 更有帮助。
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

      简答:

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

      长答案:

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

      但由于dma_map_single / dma_unmap_single 是一项昂贵的操作,有时我们可能更愿意(当数据包不太大时)重用 DMA 映射的缓冲区,因此我们不是在 DMA 事务结束时调用dma_unmap_single,而是正在调用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 */                 
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-11-15
        • 2011-08-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多