简答:
是的。你应该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 */
}