【90】RootPort的completion timeout为什么不能阻止CPU发生MCE

PCIe split transaction协议

    在解释completion timeout机制前,我们首先说一下PCIe split transaction协议是什么,以及为什么PCIe要选择split transaction?

    Split transaction协议是从PCI-X总线的引入的一个重要特性,该传输协议替代了PCI总线的delayed 数据传输方式,提高了Non-Posted transaction的传输效率。

Split transaction交互过程,如果上图所示:

  1. requester 发起一个read transaction。

  2. read请求到达completer,但是completer不能马上返回data(completer需要准备对应的数据)。

  3. completer会记住这个transaction(address、transaction type、byte count、requester ID等)。

  4. completer 返回split的response(ACK或者NCK),此时对PCI-X总线而言, requester可以结束整个read request的bus cycle,其他的request可以继续占用总线了

  5. completer准备好数据后,重新申请总线,初始化一个split completion bus cycle并使用split completion返回数据。

  6. requester收到completion with data,整个read request完成。

    相对PCI而言,PCI-X这种completer准备好数据后,主动发起split completion的方式类似write bus cycle。在两个bus transaction(read request和split completion transaction直接)之间,bus可以被其他的transaction利用,这种处理方式有效提高了PCI-X总线的使用效率。PCIe总线继承了PCI-X的这种 splittransaction。

    根据PCI Express System Architecture的数据,相对PCI的50%-60%的传输效率,这种split的方式可以把PCI-X传输效率提高到85%。

Completion timeout机制

    Split transaction带来效率提升的好处,也带来了风险:requester可能无法接收到期望的completion(可能是completion被错误地路由到别的地方导致requester永远不能收到completion,也可能是completer返回completion比较慢,或者requester和completer之间链路的问题导致超过了requester的等待时间,completion都没有返回到requester)。当这种情况发生时,为了让requester有一个恢复机制,就定义了completion timeout机制。

    针对completion timeout机制,协议规定如下:

(1)具备发出需要completion的request(Non-Posted request)能力的PCIe设备,必现要实现completion timeout机制。

    由于switch不会主动发出需要completion的请求(即switch不会主动发起non posted请求),因此,completion timeout机制仅仅适用于RC、PCIe-PCI bridge和endpoint。

(2)Configuration software可以disable掉completion timeout机制。Completion timeout的大小可以通过Device ctrl 2 寄存器的completion timeout value字段设置。Completion timeout通过requester来报错,也就说了completion timeout的错误是requester发出的。

(3)对于memory read request,可能需要多个completion,只有当requester收到了所有的completion后才认为request完成了。如果在completion timeout前,requester只收到了部分但不是全部的completion,requester可以保存或者丢弃completiontimeout前接收到的数据。

Completion timeout相关的reg

    Device capabilities 2 reg 的bit[3:0] Completion Timeout Ranges Supported,这个字段规定device function支持的completion timeout value。值得注意的是It is strongly recommended that theCompletion Timeout mechanism not expire in less than 10 ms,也就说Range A 50us to 10ms的这个Range是不被推荐的。

    Device capabilities 2 reg 的bit[4] Completion Timeout Disable Supported字段显示是否支持disable completion timeou机制。

    对于RootPort来说,Completion Timeout Disable Supported是可选的。

    Device control 2 reg 的bit[3:0] Completion Timeout Value,这个字段规定device function等待completion的时间。该字段必须是Devicecapabilities 2 reg 的bit[3:0] Completion Timeout Ranges Supported的range。

    Software可以在任何时候改变completion timeout value字段。如果在completion timeout字段被修改时,还有pending的request,对于outstanding的request,hardware可以使用新的或者旧的completion timeout valuehardware计算completion超时的起始时间可以从completion timeout value被修改的时间点算,也可以从request发出的时间点算

    值得注意的是Spec再一次强调:It is strongly recommended that the Completion Timeout mechanism not expire inless than 10 ms

    Device control 2 reg 的bit4 Completion Timeout Disable用来disable completion timeout机制(必须要Device capabilities 2 reg 的bit[4] Completion Timeout Disable Supported字段为1b时,才能设置该bit)。

Completion timeout产生的场景

    当requester发出Non-Posted request,并且直到requester的completion timeout超时,requester都没有收到对应的completion。那么,允许requester通过发出新的请求来recover这个错误。允许requester尝试恢复0次,1次或者多次,但是次数必须是有限的如果没有后续的recovery动作,requester必须通过uncorrectable error message来标识错误

    如果completion timeout的severity是non-fatal的,并且requester 选择了通过发送一个新的请求尝试recovery,requester必须首先把当前的错误当做Avisroy Non-Fatal的错误处理。支持AER的requester通过发送ERR_CORmessage来标识这个错误。

常见的CPU的Completion timeout value

CPU型号

Code name

Cap: Completion Timeout Ranges Supported

Ctrl: Completion Timeout Value

11th Gen Intel(R) Core(TM) i7-11700K

Rocket Lake

ABC

50us-50ms

12th Gen Intel(R) Core(TM) i5-12600K

Alder Lake

ABC

50us to 50ms

Intel(R) Xeon(R) Silver 4309Y

Ice Lake

ABC

260ms-900ms

Intel(R) Xeon(R) Gold 6130

Sky lake

BCD

260ms-900ms

Intel(R) Xeon(R) Gold 6430

Sapphire Rapids

ABC

260ms to 900ms

AMD Ryzen 7 5700G

ABCD

65ms-210ms

    PCIe spec强烈推荐completion timeout的时间不要少于10ms。按照经验,intel CPU一般会设置在65ms-210ms(sandy bridge)或者下一档次260ms-900ms(Ivy bridge及其之后的微架构)。

Intel CPU的架构和timer

    intel CPU不同代的CPU微架构改变很大,下面是sky lake的CPU架构。

    在sky lake CPU中有非常多的timer(见下图table6-30),每个timer对应不同sub-modules的超时时间。其中一个sub-moudule叫做IIO,PCIe错误仅仅是IIO中的一种错误,RootPort的completion timeout只是PCIe错误中的,IIO后面还有一个Cbo的TOR timeout和Core的3-strike。如果CPU的RAS是银河系的话,RootPort的completion timeout只是地球,仅仅是CPU RAS中的沧海一粟。

    下图是purley 平台IIO错误分类

    sky lake CPU中有个CHA(caching home agent)模块,该模块内有个Cbo的模块,在CBo内部有个TOR的模块,其全称是 table of request,TOR用来track pending CBo transaction。sky lake CPU的MCE的一种叫做TOR timeout就是说这个模块中的transaction超时了。

Purley platform fault coverage


    由于CPU内部每个timer计算超时的起始时间点不同,CPU操作都是流水线的,再加上PCIe是spilt transaction协议,不用等上一笔request返回completion,只需要completer返回ACK或者NCK,CPU(requester)就可以发起下一笔请求,并且Non post、post、completion之间是有order要求的,这样会导致大量的transaction的累积效应和反压。因此,尽管Root Port的completion timeout的超时时间(通常都是几百毫秒级别的)小于MCE的时间(通常都是秒级别的)。在外设异常的场景,completion timeout仍然无法保证CPU访问外设时不出现MCE。

    如果RootPort出现completion timeout的错误,应该去读header log reg或者抓trace,来看看是否是访问外设导致的,如果是访问外设导致,则需要分析是RootPort的completion timeout value设置不合理,还是switch 路由问题,或者外设响应缓慢。如果不做分析,也不管CPU架构和其他的几个timer的大小,贸然把RootPort的completion timeout改大。当外设异常时,可能会掩盖RootPort的completion timeout错误,但是这种做法可能会导致TOR timeout或者3-strike。

<think>好的,我现在需要解释`wait_for_completion_interruptible_timeout`函数的作用。首先,我得回想一下Linux内核中的同步机制。用户可能是在内核开发或驱动编写时遇到了这个函数,想了解它的用途和使用场景。 用户之前问了`spi_async`,现在转向`wait_for_completion_interruptible_timeout`,可能是在处理异步操作后的等待机制。这个函数属于内核的completion机制的一部分,用于线程间的同步。我需要明确这个函数的功能、参数、返回值以及注意事项。 首先,函数名分解:`wait_for_completion_interruptible_timeout`。关键词是“completion”,“interruptible”,“timeout”。所以,它应该是在等待某个完成事件,可以被打断,且有超时机制。 接下来,我需要确认它的作用。根据记忆,completion机制用于一个线程等待另一个线程完成某个任务。比如,一个线程启动一个操作,然后等待另一个线程发出完成信号。`wait_for_completion_interruptible_timeout`允许在等待时被信号中断,同时设置超时,避免无限等待。 参数方面,函数通常需要指向`struct completion`的指针,超时时间(可能是jiffies)。返回值可能表示剩余时间,或者错误码,比如被信号打断或超时。 使用场景可能是在驱动程序中,当需要等待某个硬件操作完成,但不想无限阻塞,同时允许用户空间进程被信号中断。比如,等待DMA传输完成,但用户按Ctrl+C时可以中断等待。 需要对比其他类似函数,比如`wait_for_completion`(不可中断,无超时)和`wait_for_completion_timeout`(可超时但不可中断)。而`wait_for_completion_interruptible_timeout`结合了两者:可中断且有超时。 在解释时,要强调它的三个特性:等待完成、可中断、超时。同时,给出典型的使用示例,比如在驱动中启动一个异步操作后,调用此函数等待完成,处理可能的返回值,如超时或中断。 还需要注意,调用前需要初始化completion变量,通常用`init_completion`或`DECLARE_COMPLETION`。另外,完成事件由`complete()`或`complete_all()`触发。 可能的错误处理包括检查返回值是否为0(超时)、正数(剩余时间),或负数(被信号中断)。使用时需确保在正确的上下文中调用,比如进程上下文,因为可能睡眠。 需要验证这些信息是否正确,可能需要回忆内核源码或文档中的相关内容,确保没有记错函数名、参数顺序或返回值含义。如果有不确定的地方,比如timeout的单位是否是jiffies,需要确认。是的,超时时间通常以jiffies为单位。 总结,这个函数的作用是允许当前线程等待一个完成事件,直到事件发生、超时或被信号中断。适用于需要灵活控制等待时间的场景,同时提高系统的响应性。</think>`wait_for_completion_interruptible_timeout` 是 Linux 内核中用于 **等待某个“完成事件”(Completion Event)** 的函数。它结合了 **可中断等待** 和 **超时机制**,适用于需要灵活控制线程/进程阻塞时间的场景。以下是详细说明: --- ### **1. 核心功能** - **等待完成事件**:阻塞当前线程/进程,直到某个任务完成(由其他线程触发完成信号)。 - **可被信号中断**:在等待过程中,如果线程收到用户空间的信号(如 `SIGKILL`),可提前退出等待。 - **超时限制**:若在指定时间内未等到完成事件,函数自动返回,避免无限阻塞。 --- ### **2. 函数原型** ```c long wait_for_completion_interruptible_timeout( struct completion *comp, unsigned long timeout ); ``` - **参数**: - `comp`:指向 `struct completion` 的指针,表示要等待的完成事件。 - `timeout`:超时时间(单位为 **jiffies**,需用 `msecs_to_jiffies()` 转换毫秒)。 - **返回值**: - `> 0`:剩余未超时的时间(jiffies)。 - `0`:超时后仍未等到完成事件。 - `-ERESTARTSYS`:等待被信号中断。 --- ### **3. 典型使用场景** 1. **驱动开发**:等待硬件操作完成(如 DMA 传输、设备响应)。 2. **内核线程同步**:协调多个线程的执行顺序。 3. **超时控制**:避免因设备无响应导致系统死锁。 --- ### **4. 工作流程** 1. **初始化完成事件**: ```c struct completion comp; init_completion(&comp); // 或 DECLARE_COMPLETION(comp); ``` 2. **在等待方调用**: ```c long ret = wait_for_completion_interruptible_timeout(&comp, timeout); ``` 3. **在完成方触发事件**: ```c complete(&comp); // 唤醒一个等待者 complete_all(&comp); // 唤醒所有等待者 ``` --- ### **5. 关键特性** #### **可中断性(Interruptible)** - 允许等待过程被用户空间的 **信号(Signal)** 打断。 - 若被中断,返回 `-ERESTARTSYS`,通常需在调用处处理信号(如重启系统调用)。 #### **超时机制(Timeout)** - 超时时间单位为 **jiffies**(内核时间单位),需用 `msecs_to_jiffies(ms)` 转换毫秒值。 ```c unsigned long timeout = msecs_to_jiffies(5000); // 5秒超时 ``` - 若超时前完成事件未触发,返回 `0`。 --- ### **6. 与其他完成函数的对比** | 函数名 | 可中断 | 超时支持 | 行为 | |---------------------------------------|--------|----------|----------------------------------------------------------------------| | `wait_for_completion()` | ❌ | ❌ | 无限阻塞,直到完成事件触发。 | | `wait_for_completion_interruptible()` | ✔️ | ❌ | 可被信号中断,但无超时。 | | `wait_for_completion_timeout()` | ❌ | ✔️ | 支持超时,但不可被信号中断。 | | **`wait_for_completion_interruptible_timeout()`** | ✔️ | ✔️ | **同时支持中断和超时**。 | --- ### **7. 使用示例(驱动代码片段)** ```c #include <linux/completion.h> struct completion data_ready; // 初始化完成事件 init_completion(&data_ready); // 等待方(例如读取数据的线程) long ret = wait_for_completion_interruptible_timeout(&data_ready, msecs_to_jiffies(3000)); if (ret == 0) { printk("等待超时!\n"); } else if (ret == -ERESTARTSYS) { printk("等待被信号中断!\n"); } else { printk("数据已就绪!\n"); } // 完成方(例如中断处理函数) void irq_handler(void) { complete(&data_ready); // 触发完成事件 } ``` --- ### **8. 注意事项** 1. **上下文限制**: - 需在 **进程上下文** 中调用(可能睡眠),不可在原子上下文(如中断)使用。 2. **资源竞争**: - 确保在调用 `wait_for_completion_*` 前,完成事件尚未被触发。 3. **超时精度**: - 超时时间依赖内核的 jiffies 时钟,精度受 `HZ` 配置影响(通常为 1ms~10ms)。 4. **信号处理**: - 若返回 `-ERESTARTSYS`,通常需向上层返回 `-EINTR`,由用户空间决定是否重试。 --- ### **总结** `wait_for_completion_interruptible_timeout` 是 Linux 内核中 **兼顾灵活性和安全性的同步机制**,适用于需控制等待时间且允许用户中断的场景(如设备驱动、异步任务协调)。正确使用需结合超时设置和信号处理,以确保系统稳定性和响应性。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

linjiasen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值