最近发现由于PCI数据传输错误导致IO request得到的数据不正确,这种现象让我们思考,为什么系统都已经发现了PCI错误,IO request还能正确结束呢?按照惯例思考,PCI传输出错,IO request就应该fail掉,但是,事实不是这样。难道这个问题和PCI的中断异步有关系?因此,这里详细讨论一下PCI的中断异步问题及PCI解决办法。
 
PCI拥有多种数据传输模式,post、non-post、Delayed以及split方式,为了提高传输效率,充分利用总线带宽资源,PCI在memory write的时候通常采用post方式。即PCI设备通过DMA控制器在向memory写数据的时候,先将数据提交给bridge的FIFO中,然后马上释放本级总线资源,完成memory写操作。但实际上此时数据还没有抵达memory,这是一种典型的异步写操作行为。
 

 

如上图所示,设备2通过DMA控制器向内存写数据,当DMA将数据post到桥2之后,随即释放bus1,完成DMA操作,并且向处理器提交DMA完成中断请求。位于处理器端的驱动程序在接收到中断请求之后调用中断服务程序,但是,此时设备2写入的数据可能还没有抵达内存,这就是PCI总线的“中断异步”问题。
 
计算机系统是如何解决这个中断异步问题的呢?其关键就在于PCI协议规范中定义的“执行序”。对于传统PCI总线而言,需要严格遵守PCI请求的执行顺序。在上述问题中,进入驱动程序之后,需要获取对应设备的中断状态寄存器内容,这是一个读操作,这个读操作会将post在桥2、主桥中的数据最终写入内存。这就是PCI总线规范遵守的数据一致性原则。
 

 

如上图所示,进入位于处理器的驱动程序之后,设备驱动会向设备2发送中断状态寄存器读request。在PCI中,读操作不能采用post的方式,只能采用non-post、Delayed或者split方式。读request到达主桥或者桥2时,如果存在post到设备2的数据,那么主桥或者桥2会将这些post的数据最终刷新到设备2,这样保证后继读请求数据的一致性。在数据返回时(蓝线路径),桥2和主桥同样需要首先将post在那里的数据写入内存,然后再返回request请求的数据,这样才能保证数据的一致性。但是,分析到这里,我们遇到了一个总线互锁的问题:由于读请求有可能采用non-post的方式,红色路径将无法获取总线控制权,也就无法将数据写入内存;而蓝色路径却要等待红色路径完成之后才能继续执行,这是典型的总线互锁问题。PCI总线如何处理上述问题呢?
 
在PCI协议规范中,Post写总线事务可以穿越non-post的总线事务。例如,主桥在等待non-post总线事务,此时桥2仍然可以将数据post写入到主桥。这种处理方法就可以避免上述总线互锁问题。
 
通过分析,我们可以知道通过中断服务程序的读操作将post到bridge中的数据刷新到了内存方向,等驱动程序访问DMA内存时,数据一定已经抵达内存了。PCI总线执行序保证了数据的一致性。
 
在这里我们假设桥2或者主桥在将post的数据写入内存时,PCI传输出现了问题,而产生的这种错误会以中断的形式发送给处理器。如果系统不对这个中断进行处理,那么,从DMA内存中获取的数据将会存在data corruption问题,并且上层软件无法知晓。