在网上查看了各路大牛的DMA资料,但一直未解决我的问题和疑问;
我的问题就是:这个DMA Buf地址区间在哪,是如何划分的。大部分资料都是
DMA的抽象层,没有讲到具体,比如这个dma_map_single这个函数。这个
phys_addr到底是从哪里来的,是如何受到保护的,以及这个映射关系和dma_unmap_single
取消映射之间,是否涉及到phys_addr的关联。
xfer->tx_dma = dma_map_single(dev, (void *)xfer->tx_buf,
xfer->len, DMA_TO_DEVICE);
跟踪代码,
1 . phys_addr_t map_single(struct device *hwdev, phys_addr_t phys, size_t size,
enum dma_data_direction dir) {
dma_addr_t start_dma_addr = phys_to_dma(hwdev, io_tlb_start); //关键代码
2. phys_addr_t swiotlb_tbl_map_single(struct device *hwdev,
{ ......
io_tlb_list[i] = 0;
for (i = index - 1; (OFFSET(i, IO_TLB_SEGSIZE) != IO_TLB_SEGSIZE - 1) && io_tlb_list[i]; i--)
io_tlb_list[i] = ++count;
tlb_addr = io_tlb_start + (index << IO_TLB_SHIFT); //关键代码,说明这里
有一个单独的dma内存区域,而且维护了一张list.
for (i = 0; i < nslots; i++)
io_tlb_orig_addr[index+i] = orig_addr + (i << IO_TLB_SHIFT);
swiotlb_late_init_with_tbl{
bytes = nslabs << IO_TLB_SHIFT;
io_tlb_nslabs = nslabs;
io_tlb_start = virt_to_phys(tlb);
return tlb_addr; // 说明
3. swiotlb_bounce{ ......
else if (dir == DMA_TO_DEVICE) {
memcpy(vaddr, phys_to_virt(orig_addr), size); //这里表明的意思是,需要把内容重新
拷贝到dma_phys映射的vir_ddr上,否则就没有数据。另外最重要的一点是,DMA和
RAM的地址是独立、并列,同级的关系,是一个编址的问题。
} else {
memcpy(phys_to_virt(orig_addr), vaddr, size);
4. 从上看出,其实这里所谓的dma操作都是一些逻辑性的,没涉及到phys部分,也就说理论上
dma可以随意映射ram任何地址。Kernel里面搞这么复杂,还是因为kernel里涉及各方面的内存
分配和堆栈空间,便于管理;另外有个重要的原因是,phys addr被dma映射后,其他的进程是
不知道的,而且各dma进程之间也是不清楚的,这样会导致严重的混乱问题,所以从这个原理上
看,对DMA的ram地址进行一定发分区管理是非常明智和实用的。抓个开机log
<6>[ 0.000000] cma: CMA: reserved 16 MiB at 0x00000000f5000000 for default region
<7>[ 0.000000] On node 0 totalpages: 1537024
<7>[ 0.000000] DMA zone: 13824 pages used for memmap
<7>[ 0.000000] DMA zone: 0 pages reserved
<7>[ 0.000000] DMA zone: 750592 pages, LIFO batch:31
<7>[ 0.000000] Normal zone: 13824 pages used for memmap
<7>[ 0.000000] Normal zone: 786432 pages, LIFO batch:31
<6>[ 0.000000] psci: probing for conduit method from DT.