Short Answer:
Yes. You should dma_unmap_single
every buffer you have been mapped with dma_map_single
.
Long Answer:
Also yes.
You should call dma_unmap_single
at the end of every DMA transaction.
But since dma_map_single / dma_unmap_single
is an expensive operation, sometimes we may prefer (when the packet is not too big) to reuse the DMA-mapped buffer, so instead of calling dma_unmap_single
at the end of the DMA transaction, we are calling dma_sync_single_for_cpu
, then copying the data from the DMA-mapped buffer into some other buffer, and then calling dma_sync_single_for_device
, so now we can reuse the already DMA-mapped buffer without unmapping and remapping it again before our next DMA transaction.
I guess the threshold of how big the packet varies between architectures and should be measured.
measure_time(dma_sync_single_for_cpu + memcpy(packet_size) + dma_sync_single_for_device)
>?<
measure_time(dma_unmap_single + dma_map_single)
Short Example:
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 */
}