打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Arm linux dma mapping操作

Arm linux dma mapping

目 录

概述

由于处理器存在cache,cache和内存中数据可能不一致,所以驱动在使用dma在内存和device之间搬移数据前后需要cpu对cache和内存中数据进行同步。有些dma寻址能力有限,比如只能寻址内存低128m,但数据在内存的1G地址处,这时需要进行数据转移。

基本概念

cpu读取数据时先查询l1 cache,如果没有再向l2 cache
查询,如果没有在向内存查询,然后把数据从ddr 内存搬移到l2 cache和l1
cache。Cpu写数据,改变了l1 cache中内容,这时l1 cache可能把数据写会到ddr
内存,也可能过段时间再写。两个l1 cache和l2
cache之间数据同步,arm中按照moesi协议同步,这里不进行介绍。

基本概念:

PoU:在单核中在某一存储层级上,指令cache,数据cache和TLB在某一点上能够看到一致内容或相同copy,称为Pou;或者在an
Inner Shareable shareability
domain中,指令cache,数据cache和TLB在某一点上能够看到一致内容或相同copy,称为Pou;

PoC:系统中所有agents(系统中master,或者observer,例如,cpu,dma)看到的memory
一致点, 称为PoC.

例如,图中CPU core0和CPU core1为一组cluster0,CPU core2和CPU
core3为一组cluster1,

L2 cache为PoU,DDR memory为PoC。

Clean:把cache中指定地址内容写会到PoU或PoC。

Invalidate:
使cache中指定地址内容无效,如果cache中修改没有写回内存,那么这次修改会丢失。

Clean and invalidate:clean指令后执行invalidate指令。

写回(write back):修改了cache中内容后,并不把更新立即写到内存,把修改的cache
line标记为dirty。但执行clean操作时,把cache修改内容写的内存,或者,替换cache
line时,把cache修改内容写的内存。

写直通(write through):修改了cache中内容后,立即写到内存。

Cache操作

写数据到device

  1. cpu执行clean指令,把cache中写到内存

  2. cpu配置dma,dma搬移内存中数据到device

从device读数据

  1. cpu执行 invalidate指令,使cache中内容无效;

  2. dma搬移device数据到内存

  3. cpu处理数据,由于之前设置了cache无效,所以回从内存读取数据到cache

swio技术

swio技术,当dma寻址能力小于cpu分配的物理地址时,swio技术会从低端地址,比如0-64m中分配一块物理地址,建立低端物理地址和cpu分配的物理地址映射,并且后续dma使用低端dma地址进行数据搬移,如果是dma从物理内存搬移数据到设备
(DMA TO DEVICE
),那么cpu先把数据从cpu分配物理地址复制到低端物理地址,后续dma从低端物理地址搬移到设备。如果是dma从设备搬移数据到物理内存(DMA
FROM DEVICE
),那么dma从设备搬移到低端物理地址,然后通知cpu,cpu从低端物理地址复制数据到原高端物理地址,
但如果dma寻址能力够,就不需要这一步。

代码分析

arm64_dma_init

\arch\arm64\mm\dma-mapping.c 中设置dma_ops

static int \__init arm64_dma_init(void){int ret = 0;ret \|= swiotlb_late_init();ret \|= atomic_pool_init();return ret;}arch_initcall(arm64_dma_init);static int \__init swiotlb_late_init(void){size_t swiotlb_size = min(SZ_64M, MAX_ORDER_NR_PAGES \<\< PAGE_SHIFT);/\*\* These must be registered before of_platform_populate().\*/bus_register_notifier(&platform_bus_type, \&platform_bus_nb);注册notifier,当有bus注册到系统时,会回调这个bus_nb函数bus_register_notifier(&amba_bustype, \&amba_bus_nb);dma_ops = \&noncoherent_swiotlb_dma_ops; 设置了noncoherent opsreturn swiotlb_late_init_with_default_size(swiotlb_size);}static int dma_bus_notifier(struct notifier_block \*nb,unsigned long event, void \*_dev){struct device \*dev = \_dev;if (event != BUS_NOTIFY_ADD_DEVICE)return NOTIFY_DONE;如果dts中指定了bus dma是dma-coherent,那么设置ops为coherent_swiotlb_dma_ops。if (of_property_read_bool(dev-\>of_node, "dma-coherent"))set_dma_ops(dev, \&coherent_swiotlb_dma_ops);return NOTIFY_OK;}

看一下noncoherent dma ops

struct dma_map_ops noncoherent_swiotlb_dma_ops = {.alloc = \__dma_alloc_noncoherent,.free = \__dma_free_noncoherent,.mmap = \__swiotlb_mmap_noncoherent,.map_page = \__swiotlb_map_page,.unmap_page = \__swiotlb_unmap_page,.map_sg = \__swiotlb_map_sg_attrs,.unmap_sg = \__swiotlb_unmap_sg_attrs,.sync_single_for_cpu = \__swiotlb_sync_single_for_cpu,.sync_single_for_device = \__swiotlb_sync_single_for_device,.sync_sg_for_cpu = \__swiotlb_sync_sg_for_cpu,.sync_sg_for_device = \__swiotlb_sync_sg_for_device,.dma_supported = swiotlb_dma_supported,.mapping_error = swiotlb_dma_mapping_error,};EXPORT_SYMBOL(noncoherent_swiotlb_dma_ops);

Cpu会调用dma_map_single

dma_map_single

例如:usb驱动收发数据

Cpu会调用dma_map_single进行clean或invalid

int usb_gadget_map_request(struct usb_gadget \*gadget,struct usb_request \*req, int is_in){req-\>dma = dma_map_single(&gadget-\>dev, req-\>buf, req-\>length,is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);DMA_TO_DEVICE从内存搬移数据到device,DMA_FROM_DEVICE从device搬移数据到内存return 0;}dma_map_single—》dma_map_single_attrs—》dma_map_single—》ops-\>map_page-》__swiotlb_map_pagestatic dma_addr_t \__swiotlb_map_page(struct device \*dev, struct page \*page,unsigned long offset, size_t size,enum dma_data_direction dir,struct dma_attrs \*attrs){dma_addr_t dev_addr;dev_addr = swiotlb_map_page(dev, page, offset, size, dir, attrs);swio技术,这里不再介绍,认为dma寻址能力足够;这里先进行了dma地址和cpu内存物理地址,cpu虚拟地址转换这里dma地址和cpu内存物理地址相同。这个由soc总线设计决定。Cpu看到的内存地址和dma看到的内存地址可能不同。\__dma_map_area(phys_to_virt(dma_to_phys(dev, dev_addr)), size, dir);return dev_addr;}\\arch\\arm64\\mm\\cache.S/\*\* \__dma_map_area(start, size, dir)\* - start - kernel virtual start address\* - size - size of region\* - dir - DMA direction\*/ENTRY(__dma_map_area)add x1, x1, x0cmp w2, \#DMA_FROM_DEVICE 来自设备,就是从设备搬移数据到ap ddr中。b.eq \__dma_inv_range走这里,所以要先使cache中数据无效,后续dma搬移后,从ddr中直接读取到cache中。b \__dma_clean_range 接着把cache中内容刷到内存中,clean。确保之前cache修改内容,写回内存。实际这里有没有必要执行,可以根据场景进行优化。\__dma_inv_range: invalid就是使cache中内容无效,下次使用时需要从内存中重新读取dcache_line_size x2, x3sub x3, x2, \#1tst x1, x3 // end cache line aligned?bic x1, x1, x3b.eq 1fdc civac, x1 // clean & invalidate D / U line这里检查结束地址是否cache linealigned,如果不对齐,需要把cacheline数据写到内存。因为cache是按line处理,否则其它(非本次dma搬移地址)地址数据也变为无效,导致数据无法写会内存。1: tst x0, x3 // start cache line aligned?bic x0, x0, x3b.eq 2fdc civac, x0 // clean & invalidate D / U line 同上,起时地址b 3f2: dc ivac, x0 // invalidate D / U line 使无效3: add x0, x0, x2cmp x0, x1b.lo 2bdsb syretENDPROC(__inval_cache_range)ENDPROC(__dma_inv_range)/\*\* \__dma_clean_range(start, end)\* - start - virtual start address of region\* - end - virtual end address of region\*/\__dma_clean_range:dcache_line_size x2, x3sub x3, x2, \#1bic x0, x0, x31: dc cvac, x0 // clean D / U lineadd x0, x0, x2cmp x0, x1b.lo 1bdsb syretENDPROC(__dma_clean_range)上面用到的invalidate指令,都是到PoC的。civac ,ivacDC CIVAC, Xt Clean and invalidate by virtual address to Point of CoherencyDC IVAC, Xt Invalidate by virtual address to Point of Coherency

dma_unmap_single

void usb_gadget_unmap_request(struct usb_gadget \*gadget,struct usb_request \*req, int is_in){if (req-\>length == 0)return;if (req-\>num_mapped_sgs) {dma_unmap_sg(&gadget-\>dev, req-\>sg, req-\>num_mapped_sgs,is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);req-\>num_mapped_sgs = 0;} else {dma_unmap_single(&gadget-\>dev, req-\>dma, req-\>length,is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);}}上面map_page,然后dma搬移,现在进行unmap_pagestatic void \__swiotlb_unmap_page(struct device \*dev, dma_addr_t dev_addr,size_t size, enum dma_data_direction dir,struct dma_attrs \*attrs){\__dma_unmap_area(phys_to_virt(dma_to_phys(dev, dev_addr)), size, dir);先进行cache操作swiotlb_unmap_page(dev, dev_addr, size, dir, attrs); swio技术}/\*\* \__dma_unmap_area(start, size, dir)\* - start - kernel virtual start address\* - size - size of region\* - dir - DMA direction\*/ENTRY(__dma_unmap_area)add x1, x1, x0cmp w2, \#DMA_TO_DEVICE 如果是写数据到device,这里不需要再操作,b.ne \__dma_inv_range 从device搬移到内存走这里,再次invalidcache,防止dma搬移期间,cpu操作过这段地址? 特别是dma搬移的地址不是cacheline对齐场景。retENDPROC(__dma_unmap_area)
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
DMA动态映射指南
kernel如何保证cache数据一致性
DMA映射
Cache Invalidate与Cache Flush
e1000中DMA传输的问题
C64x+Megamodule概述
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服