执行 DMA 操作期间刷新缓存数据

在某些平台中,处理器和系统 DMA 控制器 (或总线主 DMA 适配器) 出现缓存一致性异常。 以下准则使使用 DMA 操作接口版本 1 或版本 2 的驱动程序 (请参阅 DMA_OPERATIONS) 在所有受支持的处理器体系结构(包括不包含硬件的体系结构)中维护一致的缓存状态,以自动强制实施缓存一致性。

注意 本主题中的准则仅适用于使用 DMA 操作接口版本 1 和 2 的驱动程序。 使用此接口版本 3 的驱动程序必须遵循一组不同的准则。 有关详细信息,请参阅 DMA 操作接口的版本 3

若要在 DMA 操作期间维护数据完整性,最低级别的驱动程序必须遵循以下准则

  1. 在开始传输操作之前调用 KeFlushIoBuffers ,以保持处理器中可能缓存的数据与内存中的数据之间的一致性。

    如果驱动程序调用 CacheEnabled 参数设置为 TRUE的 AllocateCommonBuffer,则驱动程序必须先调用 KeFlushIoBuffers,然后才能开始向/从其缓冲区进行传输操作。

  2. 在每个设备传输操作结束时调用 FlushAdapterBuffers ,以确保系统 DMA 控制器缓冲区中的任何剩余字节已写入内存或从属设备。

    或者,在给定 IRP 的每个传输操作结束时调用 FlushAdapterBuffers ,以确保所有数据已读入系统内存或写出到总线主 DMA 设备。

下图显示了为什么在主机处理器和 DMA 控制器不自动保持缓存一致性的情况下,在使用 DMA 进行读取或写入操作之前刷新处理器缓存非常重要。

说明使用 dma 执行读取和写入操作的示意图。

异步 DMA 读取或写入操作访问内存中的数据,而不是处理器缓存中的数据。 除非在读取之前通过调用 KeFlushIoBuffers 刷新了此缓存,否则如果稍后刷新处理器缓存,则 DMA 操作传输到系统内存中的数据可能会被过时数据覆盖。 除非在写入之前通过调用 KeFlushIoBuffers 刷新处理器缓存,否则此缓存中的数据可能比内存中的副本更新。

如果可以依赖处理器和 DMA 控制器来维护缓存一致性,KeFlushIoBuffers 将不执行任何操作,因此调用此支持例程在这样的平台中几乎没有开销。

如上图所示,由适配器对象表示的 DMA 控制器可以具有内部缓冲区。 此类 DMA 控制器可以以固定大小的区块传输缓存数据,通常一次传输 8 个字节或更多字节。 此外,这些 DMA 控制器可以在每次传输操作之前等待其内部缓冲区已满。

假设最低级别驱动程序使用从属 DMA 读取可变大小的区块中的数据,或者读取不是系统 DMA 控制器缓存大小的整数倍数的固定大小区块中的数据。 除非此驱动程序在每个设备传输结束时调用 FlushAdapterBuffers ,否则无法确定驱动程序请求的每个字节何时实际传输。

总线主 DMA 设备的驱动程序还应在每个传输操作结束时调用 FlushAdapterBuffers ,以便 IRP 确保所有数据已传输到系统内存或传出设备。

FlushAdapterBuffers 返回一个布尔值,该值指示请求的刷新操作是否成功。 驱动程序可以使用此值来确定在完成 DMA 读取或写入操作的 IRP 时如何设置 I/O 状态块。