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
, usuallyUsed 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] …
|
Allocate a single page and return a struct address |
|
Allocate 2**order number of pages and returns a struct page |
|
Allocate a single page, zero it and return a virtual address |
|
Allocate a single page and return a virtual address |
|
Allocate 2order number of pages and return a virtual address |
|
Allocate 2**order number of pages from the DMA zone and return a struct page |
Freeing is done using a similar set of functions …
|
Free an order number of pages from the given page |
|
Free a single page |
|
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 insidestruct 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