Linux - 流式 DMA - 显式刷新/无效
Posted
技术标签:
【中文标题】Linux - 流式 DMA - 显式刷新/无效【英文标题】:Linux - Streaming DMA - Explicit flush/invalidate 【发布时间】:2021-12-12 19:16:42 【问题描述】:Streaming DMA API 的文档中提到,为了保证一致性,缓存需要在 dma-mapping 到设备之前刷新,在从设备取消映射后失效。
但是,我很困惑是否需要显式执行刷新和失效,即,dma_map_single() 和 dma_sync_single_for_device() 函数是否已经负责刷新缓存线,或者驱动程序开发是否需要调用某些函数来显式刷新 dma 缓冲区的缓存线? dma_unmap_single() 和 dma_sync_single_for_cpu() 也是如此。这两个函数会自动使 dma-buffer 缓存行失效吗?
我浏览了一些使用流式 dma 的现有驱动程序,但看不到任何显式调用来刷新或使缓存线无效。
我还查看了内核源代码,似乎上述函数在其架构特定的实现中都“使缓存行无效”,这进一步增加了我的困惑..例如,在 arch/arm64/mm /cache.S
SYM_FUNC_START_PI(__dma_map_area)
add x1, x0, x1
cmp w2, #DMA_FROM_DEVICE
b.eq __dma_inv_area
b __dma_clean_area
SYM_FUNC_END_PI(__dma_map_area)
有人可以澄清一下吗?谢谢。
【问题讨论】:
映射函数将刷新 CPU 端的缓存。如果您在两者之间更新缓冲区,则需要将其同步到设备,或者如果设备有新数据到来,则需要将其同步到 CPU。您可以通过使用 DMA 相干区域来避免所有这些。 所以..我不需要调用 dma_cache_inv() 或 dma_cache_wb() 之类的函数来确保一致性..只需 map()/unmap() 或同步操作。感谢您的澄清。我被限制使用 DMA 流来尝试提高性能。 当您映射该区域时,无需刷新缓存,当您重新使用该内存时,您必须确保数据是实际,这是通过 DMA 同步 API 调用完成的。如果需要,它们会刷新缓存,但在某些平台上,您可能需要额外的工作。这完全取决于架构。 【参考方案1】:因此,根据收到的 cmets 和更多调查结果,我想自己为其他有类似疑问的人回答这个问题。以下是针对 ARM64 架构的特定内容。其他架构的实现可能略有不同。
使用 Streaming DMA API 时,不必显式刷新或使缓存线无效。函数 dma_map_single(), dma_sync_single_for_device(), dma_unmap_single(), dma_sync_single_for_cpu() 会为您解决这些问题。例如。 dma_map_single() 和 dma_sync_single_for_device() 最终都调用了依赖于架构的函数 __dma_map_area
ENTRY(__dma_map_area)
cmp w2, #DMA_FROM_DEVICE
b.eq __dma_inv_area
b __dma_clean_area
ENDPIPROC(__dma_map_area)
在这种情况下,如果指定的方向是 DMA_FROM_DEVICE,则缓存线无效(因为数据必须已经从设备到达内存并且缓存线需要无效,以便从 CPU 读取的任何数据都会从内存中获取新数据)。如果方向是 DMA_TO_DEVICE/BIDIRECTIONAL 则执行刷新操作(因为数据可能已由 CPU 写入,因此需要将缓存的数据刷新到内存中才能将有效数据写入设备)。 请注意,__dma_clean_area 中的“干净”是ARM's nomenclature for cache flush。
dma_unmap_single() 和 dma_sync_single_for_cpu() 也是如此,如果指定的方向不是 DMA_TO_DEVICE,则最终调用 __dma_unmap_area() 会使缓存线无效。
ENTRY(__dma_unmap_area)
cmp w2, #DMA_TO_DEVICE
b.ne __dma_inv_area
ret
ENDPIPROC(__dma_unmap_area)
dma_map_single() 和 dma_unmap_single() 是昂贵的操作,因为它们还包括一些额外的页面映射/取消映射操作,所以如果指定的方向保持不变,它是最好使用 dma_sync_single_for_cpu() 和 dma_sync_single_for_device() 函数。
附带说明一下,就我而言,与 Coherent DMA 相比,使用 Streaming DMA 的读取操作速度提高了约 10 倍。但是,用户代码实现会稍微复杂一些,因为您需要确保当 dma 映射到设备时,cpu 不会访问内存(或者在 cpu 访问之前/之后调用同步操作)。
【讨论】:
以上是关于Linux - 流式 DMA - 显式刷新/无效的主要内容,如果未能解决你的问题,请参考以下文章