USB DMA¶
在 Linux 2.5 内核(以及更高版本)中,USB 设备驱动程序可以更好地控制如何使用 DMA 执行 I/O 操作。 API 详细信息请参见内核 USB 编程指南(源自源代码的 kerneldoc)。
API 概述¶
总体情况是,USB 驱动程序可以继续忽略大多数 DMA 问题,但它们仍然必须提供 DMA 就绪缓冲区(请参阅动态 DMA 映射指南)。这是它们在 2.4(和更早版本)内核中工作的方式,或者它们现在可以了解 DMA。
DMA 感知 USB 驱动程序
- 新的调用启用了 DMA 感知驱动程序,允许它们分配 DMA 缓冲区并管理现有 DMA 就绪缓冲区的 DMA 映射(见下文)。 
- URB 具有一个额外的“transfer_dma”字段,以及一个 transfer_flags 位,指示其是否有效。(控制请求也有“setup_dma”,但驱动程序不得使用它。) 
- 如果 DMA 感知驱动程序没有首先执行此操作并设置 - URB_NO_TRANSFER_DMA_MAP,“usbcore”将映射此 DMA 地址。 HCD 不管理 URB 的 DMA 映射。
- 有一个新的“通用 DMA API”,其中一部分可供 USB 设备驱动程序使用。 永远不要在任何 USB 接口或设备上使用 dma_set_mask(); 这可能会破坏共享该总线的所有设备。 
消除复制¶
最好避免让 CPU 不必要地复制数据。 成本会累加,并且像缓存刷新这样的影响会带来细微的惩罚。
- 如果您一直从同一缓冲区进行大量小数据传输,那么在系统上使用 IOMMU 来管理 DMA 映射会真正耗尽资源。 为每个请求设置和拆除 IOMMU 映射的成本可能比执行 I/O 高得多! - 对于这些特定情况,USB 具有分配成本较低内存的原语。 它们的工作方式类似于 kmalloc 和 kfree 版本,为您提供正确的地址类型以存储在 urb->transfer_buffer 和 urb->transfer_dma 中。 您还可以在 urb->transfer_flags 中设置 - URB_NO_TRANSFER_DMA_MAP- void *usb_alloc_coherent (struct usb_device *dev, size_t size, int mem_flags, dma_addr_t *dma); void usb_free_coherent (struct usb_device *dev, size_t size, void *addr, dma_addr_t dma);- 大多数驱动程序应该不使用这些原语; 它们不需要使用这种类型的内存(“dma-coherent”),并且从 - kmalloc()返回的内存就可以正常工作。- 返回的内存缓冲区是“dma-coherent”; 有时您可能需要通过使用内存屏障来强制执行一致的内存访问顺序。 它不使用流式 DMA 映射,因此它适用于在 I/O 否则会刷新 IOMMU 映射的系统上的小传输。(有关“coherent”和“streaming” DMA 映射的定义,请参阅 动态 DMA 映射指南。) - 请求 1/N 页(以及请求 N 页)在空间上是相当有效的。 - 在大多数系统上,返回的内存将是未缓存的,因为 dma-coherent 内存的语义要求绕过 CPU 缓存或使用具有总线窥探支持的缓存硬件。 虽然 x86 硬件具有这种总线窥探功能,但许多其他系统使用软件来刷新缓存行以防止 DMA 冲突。 
- 某些 EHCI 控制器上的设备可以处理与高位内存的 DMA。 - 不幸的是,当前的 Linux DMA 基础设施没有一种合理的方式来暴露这些功能... 并且在任何情况下,HIGHMEM 主要是 x86_32 特有的设计缺陷。 因此,最好的办法是确保您永远不要将高位内存缓冲区传递到 USB 驱动程序中。 这很容易; 这是默认行为。 只是不要覆盖它; 例如使用 - NETIF_F_HIGHDMA。- 这可能会迫使您的调用者进行一些反弹缓冲,从高位内存复制到“正常”DMA 内存。 如果您可以提出一个解决此问题的好方法(对于具有超过 1 GB 内存的 x86_32 机器),请随时提交补丁。 
使用现有缓冲区¶
在首先将其映射到设备的 DMA 地址空间之前,现有缓冲区不可用于 DMA。 但是,传递给您驱动程序的大多数缓冲区都可以安全地用于此类 DMA 映射。(请参阅动态 DMA 映射指南的第一部分,标题为“哪些内存是 DMA 可用的?”)
- 当您拥有已为 USB 控制器映射的 scatterlists 时,您可以使用新的 - usb_sg_*()调用,该调用会将 scatterlist 转换为 URB- int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev, unsigned pipe, unsigned period, struct scatterlist *sg, int nents, size_t length, gfp_t mem_flags); void usb_sg_wait(struct usb_sg_request *io); void usb_sg_cancel(struct usb_sg_request *io);- 当 USB 控制器不支持 DMA 时, - usb_sg_init()将尝试以 PIO 方式提交 URB,只要 scatterlists 中的页面不在 Highmem 中,这在现代架构中可能非常罕见。