文章介绍
本文会介绍优化QNX系统下io-pkt-v6-hc驱动模块cpu loading过高问题,经过优化可以降低约一半的cpu loading.
问题背景
激光雷达通过以太网发送数据到ADAS域控中,测试发现在激光功能激活的情况下,会出现比较明显的网络丢帧现象。
初步调查发现,在发生丢帧时,整个系统的cpu loading非常的高(100%),而且持续处于这种状态,整个系统cpu资源处于过载的状态.
从上面的cpu loading表现来看,QNX的io-pkt-v6-hc的整体cpu loading很高.
由于这个问题的存在已经严重影响了功能的正常使用,所以我们想在不影响性能的前提下,尽可能优化cpu loading,已达到一个合理的范围。
调查过程
这个时候第一个问题已经出现:即当前在这种测试场景下,我们cpu loading表现是否正常?是否符合芯片设计的要求?
这里引出来几个话题:
一、我们的硬件设计是怎样的?
可以看到基本上框架为:SOC-GMAC-RGMII-SWITH
二、io-pkt-v6-hc是和那个硬件模块交互?
io-pkt-v6-hc对接的MAC以太网驱动devnp-dwceqos-mv88e1512.so,而MAC是SOC片上的RGMII模块.在产生数据传输时,最先有MAC硬件接收,DMA数据传输完成之后,产生中断,有io-pkt-v6-hc调用devnp-dwceqos-mv88e1512.so中的中断处理函数,中断处理数据完成之后,调用if_input()接口注入io-pkt-v6-hc网络服务,再由应用程序读取数据.
而在芯片层面,MAC这个硬件模块大致与CPU以及其他模块的连接大致如下(不完全准确):
其中比较重要的是需要知道,MAC硬件模块与SOC的中断控制器(IC)有数条中断线进行连接,其中的中断号可以通过对应的手册查询到:
三、当前的loading状态是否正常?
针对这个问题,我们最先和我们的芯片供应商进行了讨论,但是从反馈来看,他们似乎也并没有什么比较好的解决方法(他们知道这个问题的存在).并且他们并没有计划也没有能力去进行优化(这个驱动由ip core供应商开发,他们没有参与这个部分).
查看了这个MAC的IP core状态,是synopsys公司的dwceqos模块,这个ip core不止在我们这个SOC上使用,同时在高通的多款芯片上也有应用。
基于这个背景,我们也同时测试了8155 q+a/8155 lv/8255 q+a/8295 q+a 这几个平台上的表现,发现表现基本相同,即在千兆以太网传输的case下,cpu loading普遍会很高.
从各方反馈的结果来看,测试结果似乎符合预期的?但是直觉上又感觉不合理,因为进行网络传输占用如此多都cpu资源似乎并不合理(相比其他平台进行网络传输并不会有很高的loading)。
调查思路
一般来说,cpu负荷过高通常有2个原因,一是,cpu现在的确一直在干活,那么优化的思路是优化cpu的工作内容,二是,cpu一直在被中断,反复的状态切换带来的高消耗。
优化尝试
1,确认是否启用DMA
2,修改网络模块收发buffer size
3,修改驱动加载参数
4,中断优化
基于中断合并的思路,我们翻阅了"DesignWare Cores Ethernet Quality-of-Service
Databook" 这个ip core的datasheet,其中有一段描述:
这段话简单概括来说是,如果你想优化性能,那么最好使用IOC标志位&定时器来产生中断,而不是每次DMA传输完成都产生一次中断.
其中还有一段关于中断模式的描述:
简单概括来说就是,在中断模式0(default)的情况下,只要检测到RX/TX的IOC标志位都会立即触发通用中断(sbd_intr_o),而在1/2模式下,则完全不触发sbd_intr_o中断,而只会触发sbd_perch_tx_intr_o[]或者sbd_perch_rx_intr_o[]
到这里,我们又遇到了问题: 该如何确认我当前驱动工作在那个模式下?
通过驱动代码&SOC芯片手册,我们可以确定这个GMAC控制的寄存器地址,通过devmem2直接读取其值:
note:
1,这里读到的值是我修改过之后的值,默认情况下读到的值是0x0
2,如果devmem2工具不可用,那么可以用qnx提供的in32/out32来读写寄存器
4.1 中断模式修改
到这里,我们首先尝试直接修改中断模式,修改为1/2模式之后,网络驱动完全不可用,无法传输数据。想了下,原因其实很简单,因为驱动已经指定了中断触发的源是sbd_intr_o,我们更改模式之后,因为并没有中断源与当前驱动挂钩,所以即使有数据传输产生也不会产生任何函数调用。
我们通过修改驱动更新了中断源为sbd_perch_rx_intr_o,测试之后发现效果并没有什么变化
原因在于,现在每次DMA描述符都被设置了IOC标志位,意味着,每次传输完成都会产生sbd_perch_rx_intr_o中断,与我们预期还是不符合.
4.2 中断模式修改+修改中断源+定时器触发
基于上面的原因,我们清除了驱动中大部分都DMA描述符的IOC标志位,只在特定的DMA描述符上设置IOC标志位,比如4/16/32。
不过这个时候还是有问题,因为DMA传输只在有IOC标志位的描述符传输完成时产生中断,如果没有检测到IOC标识符则不会产生中断,那么数据的实时性就没有保证了,比如你只传递了一次数据,那么这个数据并不会及时得接收到(因为还未达到IOC触发阈值).
为了解决这个问题,我们需要增加一个timer来定时触发中断,按照手册的描述:
当timer寄存器写入数值时,如果此时一个RX DMA传输完成,且IOC标志位没有被设置,那么timer将会启动,在RWT*RWTU个系统时钟后超时,并产生中断.
优化结果对比
优化前测试结果
iperf3传输速度:
cpu loading表现(每隔1s采样一次)
中断表现:
优化后测试结果
iperf3 测试表现:
cpu loading表现(每隔1s采样一次)
中断表现