Converting device driver to DMA API

Posted by Tech Enthusiast on January 15, 2016

This post is about what I have learnt through my recent work on using DMA API in kernel code.

DMA : What, Why and Where

DMA is a mechanism which allows data transfer between computer peripherals and system memory without the involvement of CPU. There are essentially three kinds of addresses: virtual memory address that is used by the CPU, physical address and bus addresses, both of which are used by devices.

Coherent Mappings

dma_addr_t dma_handle; cpu_addr = dma_alloc_coherent(dev, size, &dma_handle, gfp);

The buffer allocated with this function is situated in an area which works with DMA and the memory is allocated with get_free_pages. When the buffer is no longer needed, it is returned to the system with dma_free_coherent

dma_free_coherent(dev, size, cpu_addr, dma_handle);

Streaming Mappings

dma_map_single takes as input a virtual memory address and gives you a bus address that you can provide to the device so that it can interact with your virtual memory region.

dma_addr_t dma_map_single(struct device *dev, dma_addr_t dma_addr, size_t size, enum dma_data_direction direction);

Once the buffer has been mapped, the processor shouldn’t unmap it or alter it’s contents until dma_unmap_single is called.

Coherent or Streaming?

Coherent mappings can be used when a permanent communication between the driver and the device is required. Coherent mapping guarantees synchronisation between the CPU and the device. When using streaming mappings, if you write something in the kzallocd or kmallocd region, the device doesn’t see it immediately because essentially you are writing in the cache. You need to synchronize the CPU and the device using dma_sync_sg_for_cpu() or dma_sync_single_for_cpu() before the device can read it. Likewise, what the device writes goes into memory but doesn’t get pulled up into the cache so you have to synchronize before being able to read it. But coherent mappings can monopolize registers even when they are not being used. It ultimately depends upon the situation at hand.

Using the managed version

Devm allocated memory is freed automatically in the opposite of the order in which it is allocated. Devm functions can be used when the function which needs to be converted to DMA is called from the probe function. If it is not and the if there is a failure during memory allocation, then the memory will hang around until the remove function. If a driver uses the managed version dmam_alloc_coherent(), the area is guaranteed to be freed whether the initialization fails half-way or the device gets detached.

void * dmam_alloc_coherent (struct device * dev, size_t size, dma_addr_t * dma_handle, gfp_t gfp);

dev is the device for which coherent memory is to be allocated, the return value is a kernel virtual address for the buffer which can be used to access the device from the CPU and the address returned in dma_handle is the associated bus address.

Error Checking

Failure to check memory allocation errors during DMA mapping can result in a variety of problems ranging from kernel panics to data corruptions. When an error occurs while a DMA mapping attempt is in progress, the device driver might fail to unmap the buffers. Thus there is the need to call dma_mapping_error() to check for mapping errors before using the returned dma_handle to communicate with the device for reading or writing.

I will update my patches here as soon as they get accepted by Greg when the merge window closes.

Add DMA Support using dmam_alloc_coherent