当前位置: 首页 > news >正文

PCIe序与死锁

Background

PCIe遵循x86的TSO模型,协议规定了各种TLP之间序的关系来保证memory consistency和避免死锁。在没有打开RO或IDO的情况下,order rule可以做如下简化

下面以一个RC直连EP的简单拓扑对上述ordering rule做说明

Overtaking Rule

Both request in the same direction

[A3] Posted Write must be able to overtake Read Request

PCIe必须保证收到的inbound/outbound写请求不能被前序的读请求所反压;

对于RC内部的P2P传输,从inbound接收再路由到outbound的写请求也不能被其通路上任何读请求所反压;

常见的风险点发生在:

  1. AXI读写排队的耦合点,制造了写等读,配合PCIe读等写,若成环必死锁
  1. SMMU内部做地址翻译依赖于PTW结果,制造了写等读(PTW属于读请求),若成环比死锁
  1. P2P传输对端有某种依赖才能接收写请求

[A4] Posted Write must be able to overtake Configuration Write

对于RC,只有outbound configuration write方向,其Master通常是Root-SoC内的ARM,常见的风险点发生在:

  1. P2P传输,某一笔inbound写请求路由到outbound时排在某个configuration write A后面,此时若无法超过该configuration write A,则会反压inbound写通路,进而反压后续inbound的completion,若该configuration write A的completion刚好排在其中,则会产生死锁

对于EP,只有inbound configuration write方向,此类TLP通常不会出PCIe控制器,所以一般没有死锁风险

Request and Completion in the same direction

[D3, D4] Inbound completion must be able to overtake Outbound Read Request or Configuration Write; Outbound Completion must be able to overtake Inbound Read Request or Configuration Write.

这两条通常没有风险,因为AXI协议的rdata和wdata是两个通道,之间没有耦合。即使是NSP这种读写合并的协议也是把wdata放在request,rdata放在response通道,互相没有依赖关系。

Ordering Rule

Both requests in the same direction

[A2, B2, C2] Posted Write, Read Request, or Configuration Write must not overtake Posted Write

写后写保序:比如data和flag都在对侧,生产者先写data,后写flag。预期消费者看到flag生效一定在data生效之后。所以通常PCIe转到AXI域之后awid都是相同的。

写后读保序:比如设备A通过P2P经过RC写data到设备B,然后写flag到RC,RC读到flag之后读设备B的data。消费者用read request把data推过去,预期RC一定能读到有效的data。

读后读不保序:no requirement。所以通常PCIe转到AXI域之后arid都是unique ID。

读后写不保序:一些特殊的应用中,软件可以根据返回的数值确定序是否正确

Request and Completion in the same direction

[D2] Inbound Completion must not overtake Outbound Posted Write; Outbound completion must not overtake Inbound Posted Write.

生产者的data在远端,flag在本地;消费者读生产者那一侧的flag,用completion把data勾回来,预期一定能读到有效的data。

Case Analysis

场景1:首先看一个最简单的EP单卡死锁场景

  1. J6作为EP与RC相连,PCIe的inbound wr和outbound rd业务同时执行,某一时刻EP SoC的interconnect内buffer资源占满,开始反压后续的请求(该interconnect内部读写排队,所以后续所有读写请求都会被反压)
  1. Inbound MemWr0在某个读写排队的interconnect(可能是FlexNoC的switch或者某一级mux,demux结构)等待outbound MemRd0的完成才能下发
  1. MemRd0需要等到RC返回CmplD0才能完成,并释放interconnect的outstanding资源
  1. CmplD0在PCIe链路上排在MemWr0后面,按照PCIe序的要求不能超过前面的Posted write(比如CmplD0在RC PCIe侧的tx buffer中等待前序MemWr0)
  1. 因为MemWr0被interconnect反压,所以一直无法下发,进而导致CmplD0一直在tx buffer里排队
  1. MemRd0等不到CmplD0无法完成,所以无法释放interconnect,进而继续反压MemWr0,最终导致死锁

场景2:如果没有读写排队,在Root-SoC中会发出CfgWr TLP,也有类似的问题

  1. MemWr0等待CfgWr0完成
  1. CfgWr0等带Cmpl0返回
  1. Cmpl0等待MemWr0下发,导致死锁

场景3:下面这种场景与场景1类似,但是否发生死锁取决于控制器的微架构

  1. 控制器内部的寄存器如DMA,ATU等通常会作为memory mapped register映射到BAR空间中,Host可以通过inbound访问启动EP的DMA或对ATU进行配置
  1. 从inbound下发的配置控制器内部寄存器的访问通常又会从控制器的master接口出来经过一个interconnect绕回到控制器的某个slave接口(对于SNPS就是dbi接口,对于PLDA是一个axi slave)
  1. 如果这个slave接口同普通的outbound接口一样也对read和write做order check,则会发生如下图的死锁

场景4:作为RC时需要处理P2P访问,此时即使没有片内master参与outbound也会产生下述死锁

  1. EP0发送第一笔P2P MemRd0访问,到RC后经过地址路由绕回到outbound映射到EP1的BAR地址
  1. EP0发送第二笔MemWr0访问(可以是P2P,也可以是访问RC的DDR),在读写排队的interconnect等待MemRd0完成
  1. MemRd0对应的CmplD0排在MemWr0后面无法完成,最终形成死锁

场景5:即使片内NoC把读写通路拆分,对于inbound通路上有SMMUEPRC,如果其PTW read通路与outbound read有耦合,也会产生死锁

  1. MemWr0触发SMMU执行PTW
  1. 该PTW read请求在某个interconnect等待outbound read MemRd0的完成才能下发
  1. PTW一直无法取回页表,导致MemWr0 stall在SMMU,进而阻塞后面的CmplD0
  1. CmplD0无法返回,MemRd0无法释放interconnect,继续阻塞PTW,形成死锁

场景6:同样的场景也会发生在有ATCEP设备中,只是死锁的源头从inbound write变成outbound write

  1. Inbound MemWr0在某个interconnect排在outbound MemWr1的后面无法下发
  1. MemWr1触发ATC产生translation request向RC IOMMU请求页表
  1. RC返回的translation completion排在MemWr0后面无法返回到ATC,则MemWr1始终被stall在ATC中,进而产生死锁

场景7:在P2P访问的拓扑中,只要两个EP结构对称,都有inboundoutbound耦合点,且读写排队,就会产生如下死锁

  1. EP0读EP1的BAR,发了很多read,并将对方的credit耗尽,此时R0攒在interconnect的buffer里,导致interconnect开始反压后续的请求
  1. 同样EP1读EP0的BAR,也最终导致R1攒在interconnect的buffer里,开始反压后续请求
  1. 此时在这一堆read前面的两笔write,w0和w1刚好被反压在interconnect前面
  1. 这两笔Posted MemWr阻塞后面的所有Non-Posted MemRd,outbound的read反压无法解除,interconnect无法释放,导致死锁

场景8:在某种结构中,即使没有读写排队或inbound/outbound耦合点,因为某些masterbufferoutstanding设计不合理,也会导致死锁

  1. EP0的DMA做P2P Push操作,发起read0从本地的DDR读数据;拿到数据之后发起write1写到EP2的BAR地址
  1. 如果write1产生反压,且DMA内部的buffer设计不合理,无法吸收后续read2的rdata,则inbound的response通路产生反压
  1. 如果SMMU的PTW通路没有专门设计,在某个地方与inbound read通路有耦合。则write0在SMMU触发的ptw0返回的页表刚好排在rdata2的后面,导致write0被stall在SMMU中。
  1. write0无法下发,进而反压EP1的DMA写。两边具有对称结构,EP1的ptw1也无法完成,导致write1无法下发。
  1. 两边DMA对称地都无法吸收后续rdata,ptw无法完成,write无法下发,最终产生死锁

Rule of Thumb

Bridge Topology Consideration

下图为一个典型的PCIe SoC架构:

  • Bridge A位于PCIe inbound通路,与其他agent共享(本例子为DMA)
  • Bridge B位域PCIe outbound通路,与其他peripheral共享(本例子为UART)

为了避免死锁,在Bridge上必须保证Posted writededicated resource可以下发而不被阻塞。

比如在Bridge A上,必须保证P1/P2上的posted write不被阻塞,即使其条件是让DMA的read D1或者DMA可以发Cfg write D1被反压。

同理在Bridge B上,必须保证D1上的posted write不被阻塞。

Intermediate Components

PCIe传输在某些系统组件上可能会要求先对main memory做读或写访问,才能让PCIe传输下发到目的地。

为了避免死锁,这些中间组件访问memory的通路一定不能被阻塞。

两个典型的例子如:

  • PCIe inbound访问memory经过SMMU,传输下发依赖于QTW结果(图中绿色箭头),那么该条path与PCIe outbound访问(红色箭头)不能有耦合

  • MSI经过GIC(绿色箭头),而GIC需要访问memory中存储的GIC state(蓝色箭头)才能让让MSI传输下发,那么这两条path与PCIe outbound访问(红色箭头)不能有耦合

Reference

PCIe AMBA Integration Guide

相关文章:

  • npm 和 yarn 命令对照表
  • Springboot整合ES8(Java API Client)
  • GB/T 28181联网系统通信协议结构和技术实现
  • 使用 Spark Java 框架构建 API
  • 【文献导读】XPBD: Position-Based Simulation of Compliant Constrained Dynamics
  • 直流有刷电机转速、电流双闭环调速系统及Matlab/Simulink仿真分析
  • 诡异错误 Unresolved reference: styleable
  • exception错误处理库学习
  • 蔚来、小鹏、吉利走到了跨界分叉口
  • 神卓互联SDWAN技术实现异地组网办公(无需硬件)
  • Redis分布式锁(下篇)
  • Vue--整合SVG Icon图标--方法/实例
  • STC15单片机-整合代码,完成软件设计
  • 直流有刷电机调速原理及Matlab/Simulink仿真
  • 基于bootstrap+Java+MySQL的高校成绩管理系统
  • Django 博客开发教程 16 - 统计文章阅读量
  • emacs初体验
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • markdown编辑器简评
  • NSTimer学习笔记
  • Odoo domain写法及运用
  • Rancher如何对接Ceph-RBD块存储
  • Redash本地开发环境搭建
  • SOFAMosn配置模型
  • uva 10370 Above Average
  • -- 查询加强-- 使用如何where子句进行筛选,% _ like的使用
  • 记录一下第一次使用npm
  • 前端技术周刊 2019-02-11 Serverless
  • “十年磨一剑”--有赞的HBase平台实践和应用之路 ...
  • # centos7下FFmpeg环境部署记录
  • # Python csv、xlsx、json、二进制(MP3) 文件读写基本使用
  • $.ajax中的eval及dataType
  • (9)STL算法之逆转旋转
  • (pytorch进阶之路)CLIP模型 实现图像多模态检索任务
  • (Ruby)Ubuntu12.04安装Rails环境
  • (TipsTricks)用客户端模板精简JavaScript代码
  • (附源码)springboot码头作业管理系统 毕业设计 341654
  • (附源码)流浪动物保护平台的设计与实现 毕业设计 161154
  • (没学懂,待填坑)【动态规划】数位动态规划
  • (欧拉)openEuler系统添加网卡文件配置流程、(欧拉)openEuler系统手动配置ipv6地址流程、(欧拉)openEuler系统网络管理说明
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • .NET框架类在ASP.NET中的使用(2) ——QA
  • [ HTML + CSS + Javascript ] 复盘尝试制作 2048 小游戏时遇到的问题
  • [ vulhub漏洞复现篇 ] struts2远程代码执行漏洞 S2-005 (CVE-2010-1870)
  • [ 隧道技术 ] 反弹shell的集中常见方式(二)bash反弹shell
  • []error LNK2001: unresolved external symbol _m
  • [2016.7.test1] T2 偷天换日 [codevs 1163 访问艺术馆(类似)]
  • [BUG] Authentication Error
  • [C++]AVL树怎么转
  • [C++核心编程](四):类和对象——封装
  • [EULAR文摘] 脊柱放射学持续进展是否显著影响关节功能
  • [flink总结]什么是flink背压 ,有什么危害? 如何解决flink背压?flink如何保证端到端一致性?
  • [Flutter]设置应用包名、名称、版本号、最低支持版本、Icon、启动页以及环境判断、平台判断和打包
  • [GDMEC-无人机遥感研究小组]无人机遥感小组-000-数据集制备
  • [Google Guava] 1.1-使用和避免null