ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

RK3588 SMMUv3设备树配置指南:IOMMU环境搭建与调试

RK3588 SMMUv3设备树配置指南:IOMMU环境搭建与调试 前阵子帮朋友调一块RK3588的板子遇到一个特别典型的问题GPU跑分看着正常但一旦把显示分辨率拉高或者同时跑NPU和视频编解码整个系统就卡成幻灯片。排查了一圈发现根子根本不在GPU频率、DDR带宽或者电源管理而是内核里压根没把SMMUv3用起来所有外设DMA都走的是不做地址翻译的bypass模式大量无效的cache维护把总线带宽白白耗干设备之间还互相踩内存。说白了就是设备树里IOMMU环境没搭对。这篇文章我会把为RK3588搭建SMMUv3/IOMMU环境的完整思路走一遍重点放在设备树配置上。从RK3588的SMMUv3硬件实例分布到节点里每个字段的实际含义再到外设怎么绑定IOMMU、驱动侧需要配合什么、以及最后怎么验证配置真的生效。适合正在做RK3588、RK3568等Rockchip平台底层开发被IOMMU、DMA一致性、设备树这几个词来回折腾的工程师。也适合刚开始接触ARM设备树想搞明白SMMUv3到底是怎么接入Linux IOMMU框架的开发者。1. RK3588上的SMMUv3实例先搞清楚硬件上有哪些IOMMU1.1 从RK3399到RK3588IOMMU方案的代差老Rockchip平台的开发者对RK3399那些SMMU应该不陌生RK3399用的是比较早期的rockchip,iommu方案compatible字段基本是rockchip,iommu内部实现接近SMMUv1/v2的思路寄存器直接暴露中断上报也比较简单。到了RK3568其实还是这套老方案。但RK3588这一代不一样它换用了ARM官方的SMMUv3实现设备树里看到的compatible变成了arm,smmu-v3。这个变化不能简单理解成“寄存器地址变了”SMMUv3在架构上是重写的。它的操作方式从“操作一堆寄存器”变成了“驱动分配内存队列再通过写命令队列来下发操作”比如TLBI命令、STE配置命令都是走命令队列的。中断方面事件上报走event queue全局错误走gerror中断轻量级故障恢复还能用PRI机制。这套设计和PCIe SAS、NVMe这些高性能设备是同一代架构对大量并发DMA请求的处理能力比老方案强得多。1.2 RK3588典型SMMU实例分布RK3588的SMMU不是一颗独立的芯片而是以多个SMMU实例的形式分散在SoC内部每个电源域、每种加速器都挂着自己的SMMU。以Rockchip各版本SDK的设备树来看常见的实例大概有这些实例服务对象典型用途GPU SMMUMali-G610 GPU图形渲染、Compute ShaderNPU SMMURKNN NPUAI推理、神经网络算子VPU SMMU视频编解码单元H.264/H.265/VP9编解码VOP SMMU显示控制器图层扫描、显示链路ISP SMMU图像信号处理器Camera图像处理PCIe SMMUPCIe控制器NVMe SSD、PCIe网卡等每个SMMU实例在设备树中都是一个独立的iommu节点有自己独立的reg寄存器空间和中断号。RK3588的SDK里这些SMMU节点默认很多是status disabled状态需要板级配置去打开。这就是为什么很多人拿到开发板一开始看/sys/kernel/iommu_groups/下面空空如也或者只有PCIe产生的几个group。1.3 不开SMMU会怎样不只是安全问题对很多应用开发者来说IOMMU看起来是个“安全隔离”功能好像是军工级嵌入式系统才需要的东西。但实际上在RK3588这个级别的平台上不开SMMU首先砸的是性能。原因在于cache一致性维护。没有SMMU做地址翻译时CPU和外设共享物理内存而CPU有L1/L2/L3缓存设备直接DMA读写内存的话可能读到的是CPU缓存里的旧数据或者CPU缓存了设备刚写入内存的数据但自己不知道。为了保证数据一致Linux DMA API会做大量的cache clean/invalidate操作。GPU和NPU这种高带宽外设每一次大批量DMA都做一轮cache维护带宽损耗非常恐怖。开了SMMU之后DMA操作可以直接命中IOVA映射好的内存区域很多场景下cache维护的开销能省掉一大截。另外还有内存连续性问题。不开SMMU驱动申请DMA buffer必须找物理连续的内存这在长时间运行的设备上很容易导致内存碎片化分配失败率节节攀升。开了SMMU之后IOVA可以映射到物理上分散的页对设备来说看到的是一段连续的地址空间内存管理压力小很多。1.4 什么时候需要手动改设备树SDK自带的defconfig和设备树通常已经给官方开发板配置好了SMMU。但实战中遇到的问题是另一种情况自己画的核心板某个外设没有使用默认的SMMU实例需要改绑定的SMMU。裁剪系统时把某些SMMU实例裁掉了现在又要加回来。新加了一个FPGA或自定义IP作为PCIe外设需要调整iommu-map映射。发现某个外设驱动一直报DMA错误排查下来是SMMU状态和实际硬件不匹配。所以“从零配置”不是一个理论练习而是嵌入式开发中真实会碰到的工作。2. 设备树里SMMUv3节点长什么样逐字段拆解2.1 一个典型的SMMUv3节点先给一个RK3588平台上常见的SMMUv3节点示例。不同SDK版本节点名称和地址会有差异但节点内部结构基本一致gpu_smmu: iommufd000000 { compatible arm,smmu-v3; reg 0x0 0xfd000000 0x0 0x20000; interrupts GIC_SPI 77 IRQ_TYPE_LEVEL_HIGH, GIC_SPI 78 IRQ_TYPE_LEVEL_HIGH; interrupt-names eventq, gerror; #iommu-cells 1; clocks cru CLK_GPU; clock-names clk; power-domains power RK3588_PD_GPU; status disabled; };我见过很多新手拿到这个节点之后直接照抄到自己的板级dts里然后发现SMMU没起来。原因很简单status disabled还在光复制节点是不行的。但除了status之外这个节点里还有几个字段值得逐个搞清楚。2.2 compatible、reg与SMMUv3的硬件视图compatible arm,smmu-v3表示这是一个ARM标准SMMUv3设备Linux内核里对应的驱动是drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c。这个compatible值最好不要自己发明比如不要写什么rockchip,smmu-v3之类除非瑞芯微官方在某个BSP里明确定义了这类compatible否则内核驱动匹配不上。reg定义的是SMMU控制器的寄存器空间。在RK3588这种64位SoC上设备树通常设置#address-cells 2所以reg里的地址和长度各占两个cell。0x0 0xfd000000 0x0 0x20000表示基地址0xfd000000大小0x20000。SMMUv3的寄存器本来就比较多包括STRTAB_BASE、CMD队列基址寄存器、EVTQ基址寄存器、GERROR寄存器等等。这里的大小一般不需要开发者算太细SDK里给的通常就是正确的。有一个容易忽略的点SMMUv3标准驱动在probe时还要确认硬件是否支持某些特性比如HTTU硬件加速的页表更新、PRI、PASID等。这些能力不是设备树能直接开启的而是硬件实现里烧死的。设备树能做的是确保interrupts和power-domains配置正确让驱动能正常访问到这些寄存器。2.3 中断命名eventq和gerror不能搞混SMMUv3的中断体系中最核心的两个中断是eventq事件队列中断。当DMA请求触发了翻译错误比如访问未映射的IOVA、访问权限错误、STE没配置等SMMU会往事件队列写一条事件记录然后触发此中断。gerror全局错误中断。用于报告SMMU自身的错误比如命令队列下溢、STE表配置错误等。这两个中断在设备树里通过interrupt-names来区分。很多Rockchip SDK里SMMU节点缺省只有一个eventq中断gerror和eventq共用一个中断号这也是允许的驱动会自己识别。实战中我踩过的一个坑是从别的平台移植设备树时中断号抄错了一位结果eventq中断实际指到了另一个外设的中断。表面看SMMU初始化一切正常但一旦真的发生DMA翻译错误SMMU的中断没有被正确响应系统直接卡死或者只有日志没有任何中断上报。所以拿到设备树后一定要对照SoC的TRM逐个确认SMMU实例对应的GIC中断号。2.4 #iommu-cells与StreamID的对应逻辑#iommu-cells 1这个字段决定了一个设备在引用该SMMU时iommus属性需要带几个cell。对于标准SMMUv3这1个cell就是StreamID。StreamID是SMMU世界里最核心的一个ID它用来标识“是哪个master在发起DMA”。每个挂在SMMU下面的外设必须有一个独一无二的StreamID。这个ID由硬件设计决定通常可以在SoC的memory map或SMMU集成手册里找到。设备树中通过如下方式把这个ID告诉内核gpu { iommus gpu_smmu 0x0; };意思是GPU这个外设的StreamID是0x0当GPU发起DMA时SMMU根据StreamID去STE表里找对应的翻译上下文然后按上下文配置做地址翻译。如果写的是some_device { iommus gpu_smmu 0x80; };那就是告诉内核某个外设的StreamID是0x80它挂在gpu_smmu这个SMMU下。如果这个StreamID和实际硬件设计不一致SMMU收到DMA请求后去STE里查表查不到有效表项直接就产生一个事件并拒绝访问设备驱动就会得到一堆DMA超时或page fault错误。2.5 外设侧iommus属性与PCIe的iommu-map普通平台设备通过iommus属性绑定IOMMU例如vpu { iommus vpu_smmu 0x100; status okay; };对于PCIe控制器情况又不一样。PCIe下的每个设备Bus/Device/Function天然有一个RIDRequestor ID设备树里通过iommu-map来建立RID和StreamID的映射关系pcie2x1l2 { iommu-map 0x0 pcie_smmu 0x0 0x1000; status okay; };这段的意思是从PCIe RID 0x0开始一共0x1000个RID映射到pcie_smmu的StreamID 0x0到0xFFF。为什么要单独写这样一个映射因为PCIe设备的数量是动态的插入什么卡不确定RID范围固定但对应哪个StreamID需要平台设计者给出规则。这个规则不同SoC差异很大RK3588的PCIe SMMU映射也需要查TRM确认。顺便说一句如果你只是给GPU、NPU、VPU这些SoC内部设备配置SMMUiommus就够了完全不用碰iommu-map。3. 从零配置RK3588设备树的完整流程3.1 准备工作拿到正确的SDK和工具链动手改设备树之前先确认环境。我建议用Rockchip官方发布的Linux SDK或者你自己vendor定制的内核源码树。交叉编译工具链用aarch64-linux-gnu-gcc即可。热搜里常看到“arm交叉编译”这个词但这里要明确RK3588跑的是64位ARMv8必须用aarch64工具链用arm-linux-gnueabihf那种32位工具链编译出的内核是跑不起来的。设备树编译本身不依赖交叉编译器用内核仓库自带的dtc工具就能完成make ARCHarm64 rockchip_linux_defconfig make ARCHarm64 dtbs如果只想单独编译某个dtbmake ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- dtbs # 或者手动用dtc ./scripts/dtc/dtc -I dts -O dtb -o rk3588.dtb arch/arm64/boot/dts/rockchip/rk3588-evb1-lp4-v10.dts3.2 在SDK里找出可用的SMMU节点第一步永远是搜索而不是硬写。打开内核源码目录在arch/arm64/boot/dts/rockchip/下面找rk3588相关的dtsi文件grep -n arm,smmu-v3 arch/arm64/boot/dts/rockchip/rk3588*.dtsi正常情况下你会看到多个SMMU节点每个对应一个硬件实例。你要做的不是去重新发明这些节点而是确认它们是否被正确使能以及要给自己想启用IOMMU的设备加上iommus属性。搜索之后对照你的板级dts看这些SMMU节点的status是否已经是okay。SDK默认状态不一定比如官方EVB板子的设备树可能所有SMMU都开了但一个小批量定制板卡的BSP可能把它们全关了因为厂商觉得“用不到IOMMU还能省点启动时间”。3.3 使能SMMU节点和绑定外设在板级dts比如你自己的rk3588-custom-board.dts中做两件事第一打开SMMU节点gpu_smmu { status okay; };第二给目标外设绑定IOMMUgpu { iommus gpu_smmu 0x0; status okay; };这里建议把iommus绑定放在板级dts里而不要改dtsi的公共部分原因很简单板级dts是你们项目自己的配置公共dtsi是给所有板卡共用的改了会影响其他人。如果你能把每个SMMU实例的stream ID都从TRM里查出来那就更稳妥了。比如NPU的SMMU可能需要rknpu { iommus rknpu_smmu 0x200; status okay; };这里0x200只是举例实际StreamID要按TRM填写。3.4 内核config和启动参数的安排设备树配好了内核config不对也不行。在arm64平台上以下config是必须确认的CONFIG_IOMMU_SUPPORTy CONFIG_IOMMU_DMAy CONFIG_ARM_SMMU_V3y这三个就是SMMUv3设备树生效的基本盘。如果用的是vendor SDK可能还有老式的CONFIG_ROCKCHIP_IOMMU之类但那是给老平台用的RK3588走的是CONFIG_ARM_SMMU_V3。启动参数方面有两个cmdline参数影响IOMMU行为iommu.passthrough0/1默认是0表示所有设备都走地址翻译模式。设为1表示全部走passthrough等于把SMMU当透明桥用通常不推荐这样全局设置因为就失去意义了。iommu.strict1/0strict模式表示IOVA和页表的unmap是同步释放的性能可能受影响但内存管理简单。默认值在不同内核版本里不太一样需要按实际业务选。如果你只是想让某个外设绕过SMMU不要用cmdline全局设置而是在设备树里不给那个外设写iommus属性或者让对应的SMMU实例保持disabled状态。3.5 编译烧录与常见假象设备树改好之后编译、打包、烧录这些流程SDK文档里都有我就不重复了。但我想说一个很容易骗到人的细节你有没有确认你烧录进去的dtb真的是你改的那份Rockchip平台的内核镜像通常是boot.img里面包含了kernel和dtb。有的SDK打包脚本会把dtb直接塞进kernel image里有的会单独打包成一个resource.img。如果你只改了dts但打包脚本没把新dtb打进去或者打进去的顺序不对你看到的设备树行为还是老样子。这种情况我遇到过不止一次。烧录后第一时间验证设备树内容# 在板子上查看实际生效的设备树 cat /proc/device-tree/gpu_smmu/status ls /proc/device-tree/iommufd000000/如果读到的是okay说明你改的dtb生效了如果还是disabled回去查打包链路大概率是烧录的不是你编译产物。4. 驱动侧不配合SMMU配了也白配DMA mask与内存分配4.1 设备驱动其实不用直接碰SMMULinux对IOMMU的抽象做得比较彻底。设备侧驱动只要写好了DMA APIIOMMU框架会自动帮你完成地址翻译和页表管理。也就是说你的驱动里不需要调用任何SMMU专用API不需要读SMMU寄存器也不需要手动设置STE表项。内核的IOMMU框架会在设备probe时通过设备树中的iommus属性找到对应的IOMMU域然后给这个设备建立dma地址空间。这个设计对驱动开发者非常友好但同时也意味着如果你的驱动DMA API用得不对问题可能被掩盖成各种奇怪的现象而不是直接报“IOMMU失败”。4.2 dma_mask最容易忽略的隐性炸弹一个最常见的配合问题是dma_mask设置不当。设备树里配好了SMMU但驱动在probe时没有正确设置dma_mask和coherent_dma_mask内核DMA层会用一个极保守的默认值导致dma_alloc_coherent分配失败或者分配的地址设备根本访问不到。标准驱动代码里应该有类似这样的初始化static int my_device_probe(struct platform_device *pdev) { struct device *dev pdev-dev; int ret; ret dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40)); if (ret) { dev_err(dev, failed to set DMA mask: %d\n, ret); return ret; } ... }为什么是40位因为SMMU的IOVA地址宽度和物理地址宽度不一定相同RK3588的SMMU通常支持40位以上的地址空间但具体是多少要看TRM里的SMMU地址位宽。设置一个过高的DMA mask而不确认硬件支持可能导致IOVA分配时出错。设置得过低又会限制IOVA空间容量。这个数字最好以硬件手册为准。如果你写的是平台驱动但用的是旧式platform_get_resource(pdev, IORESOURCE_MEM, 0)加ioremap这一套也没有设置DMA mask那在SMMU开启后DMA操作经常会“时而正常时而失败”。因为有时映射到的物理内存恰好是DMA能访问的有时不是。4.3 DMA API使用的几个细节在驱动里分配DMA内存推荐用标准APIdma_addr_t dma_handle; void *cpu_addr; cpu_addr dma_alloc_coherent(dev, size, dma_handle, GFP_KERNEL);使用dma_alloc_coherent的好处是它拿到的内存同时满足cache一致性和IOVA映射两个条件。SMMU会为这块分配的内存建立页表映射返回的dma_handle是一个IOVA地址设备用这个地址发起DMA完全没问题。另外一些需要注意的点如果你在驱动里自己分配了内存比如kmalloc或alloc_pages想把这个内存的物理地址直接给设备用在SMMU场景下是行不通的。设备看的是IOVA不是物理地址。必须通过dma_map_single()/dma_map_page()这类API把内存映射到IOVA空间告诉设备使用返回的dma_addr。映射方向DMA_TO_DEVICE/DMA_FROM_DEVICE/DMA_BIDIRECTIONAL要写对尤其是双向传输的场景写错会导致数据错乱。取消映射时一定要对应成对使用dma_unmap_single()/dma_free_coherent()。漏unmap会造成IOVA泄漏长时间运行后IOVA耗尽DMA分配失败。4.4 coherent与non-coherent的内存模型差异设备树里可能出现dma-coherent属性这表示设备的DMA访问与CPU缓存是保持一致的。如果不加这个属性内核会认为设备是非coherent的在DMA操作前后需要做cache clean/invalidate性能损失明显。但在RK3588上不能简单认为“加了dma-coherent就一定好”。这个属性必须符合硬件的真实行为。如果设备实际是non-coherent你却声明了coherent那设备DMA写入的数据在CPU缓存里可能看不到表现出来就是数据莫名丢失、视频花屏、GPU输出错乱。反过来设备实际是coherent却没声明性能下降但至少数据不坏。所以在改设备树时SMMU节点本身有dma-coherent不代表它服务的外设也带dma-coherent。每个外设节点要按各自的硬件手册单独确认。设备节点的dma-coherent属性SMMU只是负责地址翻译不管cache一致性。5. 怎么确认IOMMU真的生效了日志、sysfs与一次故意的crash5.1 从启动日志里读出SMMU初始化状态配置完设备树重启板子第一件事是看内核日志dmesg | grep -iE smmu|iommu正常情况下你能看到类似这样的输出[ 1.234567] arm-smmu-v3 fd000000.iommu: probing hardware configuration... [ 1.234567] arm-smmu-v3 fd000000.iommu: SMMUv3 with (0x10) features: ... [ 1.234567] iommu: Default domain type: Translated如果你看到的是arm-smmu-v3: probe of fd000000.iommu failed with error -16那一般是中断号冲突或者power domain没起来要回头查设备树资源。更直接的一个方法是启动后检查/sys/kernel/iommu_groups/目录ls /sys/kernel/iommu_groups/每出现一个数字编号的目录就代表内核建立了一个IOMMU group。查看某个group下挂了哪些设备ls /sys/kernel/iommu_groups/0/devices/如果GPU、NPU或你想配置的外设出现在某个group下说明设备已经成功attach到了IOMMU域。如果设备树配了iommus但这里没有对应设备问题大概率出在驱动probe时没有走of_dma_configure路径或者驱动的probe直接失败了。5.2 用debugfs看IOVA分配和页表情况内核开启CONFIG_IOMMU_DEBUGFS后可以查看更详细的IOMMU状态ls /sys/kernel/debug/iommu/这里能看到每个iommu domain的页表信息、IOVA分配范围等。对调试IOVA耗尽问题特别有帮助。不过有些SDK内核没开debugfs或者挂载权限限制先用mount -t debugfs none /sys/kernel/debug挂载一下。5.3 制造一次IO page fault最靠谱的验证方式配置完SMMU之后怎么知道翻译功能真的在工作一个非常有效的方法是“主动触发一次翻译错误”。方法很简单写一个临时的字符驱动在probe里用dma_alloc_coherent分配一块缓冲但故意把dma_handle改写掉比如dma_handle | 0x80000000然后让设备往这个错误地址写数据。如果SMMU在工作设备发起的DMA会触发IO_PAGE_FAULT内核日志里出现arm-smmu-v3 fd000000.iommu: event 0x10 received: arm-smmu-v3 fd000000.iommu: 0x00000000f0100000 arm-smmu-v3 fd000000.iommu: 0x0000000000000080 arm-smmu-v3 fd000000.iommu: 0x0000000000000000 arm-smmu-v3 fd000000.iommu: 0x0000000000000000事件类型0x10表示F_TRANSLATION即地址翻译失败。看到这条日志基本可以确定SMMU已经接管了设备的DMA地址翻译。如果你改错地址后设备居然也能正常访问内存没有任何事件上报那就要怀疑SMMU是不是没在翻译模式而是走了bypass。5.4 性能对比验证IOMMU带来的收益除了看日志和sysfs也可以通过实际性能测试来验证配置效果。最简单粗放的方法是对比开启SMMU前后同一个GPU或NPU benchmark的吞吐量以及dmesg里cache维护相关日志的频率。更专业的做法是用perf统计TLB miss或者用bus tracer看总线上cache维护事务的多少。但对于大多数嵌入式团队来说帧率、FPS、推理耗时、编解码fps这几个指标足够说明问题。如果发现开启SMMU后性能反而明显降低请跳到第6.4节看strict模式的坑。6. 实际调试记录遇到的坑和排查套路6.1 eventq中断风暴一路刷屏停不下来有次调试RK3588的VPU视频解码SMMU打开后板子直接刷屏arm-smmu-v3 vpu_smmu: event 0x10 received: ...每秒钟刷出几十条翻译错误事件。第一反应是StreamID配错了。后来查TRM发现VPU模块内部有多个子模块decode、encode、post-processor它们各自有不同的StreamID而设备树里给整个VPU节点只配置了一个StreamID。子模块发DMA时用了自己没有映射的StreamIDSMMU自然拦截。解决办法不是在驱动里强行塞死子模块而是去查TRM里VPU各子模块对应的StreamID在设备树里给每个子节点配正确的iommus。如果内核里VPU是作为一个复合设备出现可能还需要看驱动的iommu attach逻辑确保每个子设备都正确绑定了SMMU。6.2 外设iommus属性cells数量和SMMU不匹配另一个高频问题SMMU节点写的是#iommu-cells 1但外设节点里写了两个cellrknpu { iommus rknpu_smmu 0x0 0x1; status okay; };内核解析时直接报错设备DMA功能整个失效。这种错误其实很容易查dmesg | grep iommu就能看到parsing failed之类。但为什么会出现很多开发者从老平台移植设备树老平台rockchip,iommu可能允许带一个附加标识cell到了SMMUv3标准绑定里就只认一个StreamID。移植时没有仔细核对bindings文档就踩进去了。规范的做法是参考内核里的Documentation/devicetree/bindings/iommu/arm,smmu-v3.yaml这个文件定义了这个compatible下所有属性的合法写法。6.3 power-domain没打开SMMU寄存器读到全F如果你在调试时发现SMMU驱动probe失败现象是访问寄存器时读回的全是0xFFFFFFFF或者系统直接触发external abort那不用急着怀疑SMMU节点本身的reg写错了先确认SMMU所在的电源域和时钟有没有被使能。RK3588的SMMU实例通常跟随服务对象的power domain比如gpu_smmu挂在GPU的power domain下。如果GPU的power domain本身是关闭状态CPU去访问SMMU寄存器就会失败。设备树里通常通过power-domains power RK3588_PD_GPU来关联。如果你在板级dts里改了外设的电源域别忘了检查对应的SMMU节点是否也需要同样的关联。时钟也一样。有的SMMU节点带clocks属性如果对应时钟被关闭或者设置了错误的clock parentSMMU也会无法正常工作。这类问题在启动日志里不一定有明确报错更多时候表现为SMMU驱动“静默失败”然后外设DMA各种超时。6.4 strict模式与性能下争论一个取舍问题有次用户反馈GPU开启IOMMU之后性能还不如不开。查下来是内核默认把iommu.strict设成了1所有IOVA和页表在unmap时立刻释放。GPU这种高频创建销毁DMA映射的外设频繁的TLBI和页表更新操作成了热点。试了几组参数之后把iommu.strict0加进cmdline让IOVA和页表异步释放GPU帧率提升明显。但strict0也有代价——内存和IOVA的释放会有延迟极端场景下可能出现IOVA分配不出去的假象。所以这个参数要结合业务取舍不是无脑开成non-strict。6.5 cache一致性问题SMMU管不了的事SMMU只负责地址翻译和访问权限控制不负责cache一致性。有次调试一个自定义FPGA通过PCIe挂在RK3588下发现FPGA读到的数据总是差一拍明显是cache stale。一开始怀疑SMMU配置问题但仔细看事件队列没有翻译错误sysfs里也正常。后来查FPGA的AXI属性发现它没有走可以自动维护cache一致性的通路设备侧也没有声明coherent。加上设备树中给PCIe外设的描述里本来就没有dma-coherent驱动里又用了dma_alloc_coherent但FPGA侧读的时候并没有让CPU做cache invalidate。这种情况下SMMU无能为力得靠驱动在正确的时机做cache操作或者确认硬件互连是否支持coherent请求不能把所有锅都甩给IOMMU设备树配置。这些坑回过头看每一个都有清晰的排查路线。设备树配置SMMUv3的难点其实不在于“写几行属性”而在于对硬件细节的理解足够细StreamID从哪来、中断号对不对、电源时钟是否齐全、驱动写的DMA API是否规范。把这些链路都打通之后IOMMU在RK3588上的价值才会真正体现出来。
返回列表