DMA (Slideshow)

For a much more comprehensive guide see

Address Types

  • Virtual address

    • Everyting that can be accessed through pointers

    • ⟶ MMU knows about it

    • Result of kmalloc(), vmalloc(), and friends

  • Physical address

    • Not directly accessed by anyone

    • unsigned long, usually

    • Used for example as input to mapping functions like ioremap() (creates a virtual address to represent what is otherwise not accessible)

  • Device address, or DMA address, bus address

    • Main memory address as seen by the device

    • Conceptually a mapping just like the MMU mapping

      • IOMMU in intelligent systems

      • Direct access to pysical addresses (null mapping) in dumb systems

    • dma_addr_t

Physical Memory Allocation

Typically done in page granularity [1]

struct page* alloc_page(unsigned int gfp_mask)

Allocate a single page and return a struct address

struct page* alloc_pages(unsigned int gfp_mask, unsigned int order)

Allocate 2**order number of pages and returns a struct page

unsigned long get_free_page(unsigned int gfp_mask)

Allocate a single page, zero it and return a virtual address

unsigned long __get_free_page(unsigned int gfp_mask)

Allocate a single page and return a virtual address

unsigned long __get_free_pages(unsigned int gfp_mask, unsigned int order)

Allocate 2order number of pages and return a virtual address

struct page * __get_dma_pages(unsigned int gfp_mask, unsigned int order)

Allocate 2**order number of pages from the DMA zone and return a struct page

Freeing is done using a similar set of functions …

void __free_pages(struct page *page, unsigned int order)

Free an order number of pages from the given page

void __free_page(struct page *page)

Free a single page

void free_page(void *addr)

Free a page from the given virtual address

DMA Mappings: Overview

#include <linux/dma-mapping.h>
  • Consistent (or coherent) mapping

    • Accessible in both directions; visible without any explicit flush operations

    • Usually set up at device/driver initialization time, to exchange management information such as DMA addresses for further streaming.

  • Streaming mappings

    • Have a direction (to/from device)

    • Writes are not guaranteed to be visible to the receiving party (⟶ explicit flush)

    • Flush at “DMA done” interrupt (from device)

    • FLush by CPU (to device), usually signalled to the device by writing a device register

Consistent DMA Mappings

dma_addr_t dma_handle;

void* cpu_addr = dma_alloc_coherent(dev, size, &dma_handle, gfp);
dma_free_coherent(dev, size, cpu_addr, dma_handle);
  • dev: struct device* (contained inside struct pci_dev and others)

  • gfp: GFP_KERNEL, GFP_ATOMIC, …

Streaming Mappings: Direction

DMA direction

  • DMA_BIDIRECTIONAL

  • DMA_TO_DEVICE

  • DMA_FROM_DEVICE

Streaming Mappings: To Virtual Address

Create a DMA mapping to a virtual address

dma_addr_t dma_handle = dma_map_single(dev, addr, size, direction);
if (dma_mapping_error(dev, dma_handle)) {
    /*fucked up*/
}
... DMA operation ...
dma_unmap_single(dev, dma_handle, size, direction);

Streaming Mappings: To Page

Create a DMA mapping to a page

dma_addr_t dma_handle = dma_map_page(dev, page, offset, size, direction);
if (dma_mapping_error(dev, dma_handle)) {
    /*fucked up*/
}
... DMA operation ...
dma_unmap_page(dev, dma_handle, size, direction);

Streaming Mappings: Scatterlists (1)

  • Larger DMA transfers usually involve multiple physically distributed (scattered) pages

  • Represented by an array of struct scatterlist

struct scatterlist {
    struct page *page;
    unsigned int offset;
    dma_addr_t dma_address;
    unsigned int length;
};
  • User fills in the known parts ⟶ all but dma_address

Streaming Mappings: Scatterlists (2)

Create mappings

  • Fill in DMA addresses

  • Buckets used could be less than scatterlist size when adjacent pages are found

struct scatterlist sglist[100];
struct scatterlist* sg_run;
int i;
int count = dma_map_sg(dev, sglist, 100, direction);
// program into device
for_each_sg(sglist, sg_run, count, i) {
    hw_address[i] = sg_dma_address(sg_run);
    hw_len[i] = sg_dma_len(sg_run);
}

... DMA operation ...

dma_unmap_sg(dev, sglist, nents /*NOT count!*/, direction);

Streaming Mappings: Syncing

  • Unmapping does automatic flush of memory in the given direction

  • Usually mapping are not recreated for every transfer ⟶ explicit flush

dma_sync_single_for_cpu(dev, dma_handle, size, direction);
dma_sync_sg_for_cpu(dev, sglist, nents, direction);

Footnotes