16.5.1 Flushing Cached Data during DMA Operations

In some Windows NT platforms, the processor and system DMA controller (or busmaster DMA adapters) exhibit cache coherency anomalies.

    To maintain data integrity during DMA operations, NT device drivers must follow these guidelines:
  1. Call KeFlushIoBuffers before beginning a transfer operation to maintain consistency between data that might be cached in the processor and the data in memory.

    If an NT driver calls HalAllocateCommonBuffer with the CacheEnabled parameter set to TRUE, the driver must call KeFlushIoBuffers before beginning a transfer operation to/from its buffer.

  2. Call IoFlushAdapterBuffers at the end of each device transfer operation to be sure any remainder bytes in the system DMA controller’s buffers have been written into memory or to the slave device.

    Or, call IoFlushAdapterBuffers at the end of each transfer operation for a given IRP to be sure all data has been read into system memory or written out to a busmaster DMA device.

Figure 16.4 shows why it is important to flush the processor cache before a read or write operation using DMA if the host processor and DMA controller do not automatically maintain cache coherency.

Figure 16.4 Read and Write Operations Using DMA

When an asynchronous DMA read or write operation occurs, it accesses data in memory, not in the processor cache. Unless this cache has been flushed by calling KeFlushIoBuffers just before a read, the data transferred into system memory by a DMA operation could be overwritten with stale data if the processor cache is flushed later. Unless the processor cache has been flushed by calling KeFlushIoBuffers just before a write, the data in this cache might be more up-to-date than the copy in memory.

KeFlushIoBuffers does nothing if the processor and DMA controller can be relied on to maintain cache coherency, so calls to this support routine have almost no overhead in such a Windows NT platform.

As also shown in Figure 16.4, DMA controllers, which are represented by NT adapter objects, can have internal buffers. Such a DMA controller can transfer cached data in fixed-size chunks, usually eight or more bytes at a time. Moreover, these DMA controllers can wait until their internal buffers are full before each transfer operation.

Consider the case of any NT device driver that uses slave DMA to read data in variable-sized chunks or in fixed-size chunks that are not an integral multiple of a system DMA controller’s cache size. Unless such a driver calls IoFlushAdapterBuffers at the end of each device transfer, it cannot be sure when every byte the driver requested actually will be transferred.

The driver of a busmaster DMA device also should call IoFlushAdapterBuffers at the end of each transfer operation for a given IRP to be sure that all data has been transferred into system memory or out to the device.

IoFlushAdapterBuffers returns a Boolean, indicating whether the requested flush operation was successful. NT drivers can use this value to determine how to set the I/O status block when completing an IRP for any DMA read or write operation.