四、技术实现方案
现在我们来讲解一下本方案的具体实现方法,本方案主要分为三步实现。
第一步:soc通过控制csr模块的复位寄存器,下发复位命令,发起对virtio_blk模块的复位操作。virtio_blk模块收到复位信号后,会主动清除该模块内所有正在运行的状态机和数据流等,使其恢复到初始状态,实现对该模块异常情况时的初始化功能;
第二步:soc通过控制csr模块的读写寄存器,使FPGA产生对host内存读操作,间接的读取host内存队列指针信息,然后soc将读返回的信息,再次写到FPGA中的index_ram模块,更新里面的有效指针avail_index和已用指针used_index。其作用就是将FPGA内部指针信息与host侧驱动的指针信息对齐,以便后续的重传操作。
第三步:实现virito_blk的重传功能,FPGA与host和soc交互,将主动复位期间丢失的有效的存储数据和指令,重新从host侧内存获取数据,然后传输到后端,以达到无损传输的效果。
图 1 智能网卡DPU主要模块结构连接示意图
以上几步操作必须按照顺序操作,才能实现virtio_blk的纠错,达到存储数据的无损传输效果。
由于此方法是三部分操作流程构成的,这里分开讲解这三部分的细节和实现流程。
1、soc对virtio_blk模块的主动复位
此步骤的主要功能,就是复位virito_blk模块,使其恢复到初始状态。具体操作如下。
Soc通过pcie总线,可以控制FPGA中的csr模块,csr模块主要存放各种配置和统计寄存器,通过地址排列的方式进行寄存器分类。其中virtio_blk的复位寄存器就在其中。复位寄存器命名为blk_reset_reg,为32bit位宽。当soc对blk_reset_reg寄存器进行写1操作,此时blk_reset_reg寄存器对外会输出一个高电平,持续400纳秒的时间,400纳秒时间之后就会拉低这个电平信号。此电平信号命名为blk_reset(如图1所示)。此信号直接连接到virtio_blk模块,作为此模块的复位信号。
当virtio_blk模块接收到blk_reset信号拉高,代表复位开始,就会主动将模块内部的所有的寄存器、缓存器、状态机做清零等初始化操作,此时哪怕有正在运行的数据流或者控制操作,都会被强制打断并且清除。这样做的好处是,无论是正常的状态还是错误的状态,都可以使其恢复到初始状态,达到清除错误的效果。当然也有缺点,就是正常的存储数据流和控制命令,同样被强制清除了,导致数据的丢失。不过这个问题,在接下的几步操作中,可以做到重新续传已丢失的存储数据。
当virtio_blk模块接收到blk_reset信号拉低后,代表复位结束,此时virtio_blk模块的内部都处于初始状态了,但是这里强调一点,复位结束后,virtio_blk模块的输入输出,是会做特殊处理的。
virtio_blk模块的输入接口,会做数据丢弃操作。例如,当有数据从pcie_tlp模块输入过来或者数据从virtio_net_blk模块输入过来,则在virtio_blk输入接口处全部丢弃,不能让其传输到模块内部,一直使其保持初始状态,直到后续的重传操作开始后才正常运行。
virtio_blk模块的输出接口,会做数据完整性检测和保护。例如virtio_blk模块数据输出到pcie_tlp模块的地方有个缓存的空间,此空间会做包完整性检测,如果由于复位影响,导致数据传输一般就截断了,从virtio_blk模块输出的数据包不完整,则会再次缓存空间进行检测和丢弃,残包就不会传到pcie_tlp模块,保护其不受到影响。
以上就是soc对virtio_blk模块的主动复位操作了。接下来就是执行纠错方案的第二步,这一步是操作时集成在soc命令集中,当完成复位操作后,会自动执行第二步操作。
2、soc获取host内存队列指针信息,并将其更新到FPGA内部指针缓存空间。
这一步操作,主要分为两个连续的操作实现。
操作1:soc控制FPGA芯片内部的csr模块,通过读写其中的4个连续地址的寄存器,实现FPGA主动读取host内存指针信息,并返回给soc。
操作2:soc将获取到指针信息,通过写csr模块寄存器,将指针信息写到index_ram模块中,实现对FPGA内部的指针信息与host侧内存的指针信息对齐,便于后续的重传操作。
这里具体介绍一下操作1的过程,如下图所示。
图 2 soc访问host内存结构连接示意图
在csr模块中,有4组寄存器,分别为低地址、高地址、使能、数据,他们都是32bit位宽的寄存器。其中低地址和高地址两者组成一个64bit位宽的地址,代表的是准备访问host内存的地址。这两个地址寄存器,是由soc去赋值写入的。使能寄存器是指示数据寄存器是否有效,当使能寄存器值为1时候,代表从host读返回的数据已经存放在数据寄存器里面,当soc读使能寄存器时候,发现其值为1,则立马就读取数据寄存器里面的数值,这个数据就是从host内存读回来的值。
如上图所示,csr模块中,有一个dma读总线模块,主要功能就是将4组寄存器操作时序转换为dma读接口时序,以便可以对接pcie_tlp接口,访问host内存。
根据上图所示,soc访问host内存的操作流程如下:
(1)首先soc将准备读取host内存地址信息,例如队列指针信息所在的地址,写到csr模块的低地址寄存器和高地址寄存器。
(2)csr模块收到高地址和低地址信息后,就会将其传递给dma读总线模块,转换成dma读操作,去读host内存操作。当dma读返回后,就将读到的值放到数据寄存器中。并且将使能寄存器对应的值置位为1.
(3)Soc这侧,当写完csr模块的低地址寄存器和高地址寄存器后,会间断一小段时间检测一次使能寄存器对应的值是否为1,一般是循环检测10次,当发现其值为1的时候,就代表FPGA已经读到host内存的值,soc这个时候,会再次主动去读取数据寄存器里的值,作为最终的有效返回值。
(4)FPGA检测到soc读到使能寄存器的值为1的时候,会主动清零使能寄存器。防止下次soc误读。
以上就是操作1,soc访问host内存整个操作流程。
这里介绍一下操作2的过程。如上图1所示。
soc将通过操作1获取到的host侧内存的指针信息,主要信息为有效指针avail_index和已用指针used_index,写到csr模块内的2个寄存器,分别为avail_index_reg和used_index_reg。当FPGA检测到这两个寄存器都有值后,会主动将这两个值,通过wr_index总线写到index_ram模块中,数据存放在对应的ram单元中。
至此,soc读取host内存队列指针信息,并将其更新到FPGA中,以作信息对齐的操作流程已描述完成,接下来就是第三步操作,实现virtio_blk的重传功能。
3、virtio_blk重传存储数据,实现无损传输。
重传操作主要是在index_ram模块中执行的,有关index_ram模块组成示意图,如下图3所示,此模块主要有3个ram、1个比较器和1个重传启动模块组成。
(1)avail_index_ram_1模块:为FPGA内部的ram存储器,用来存放soc写进来的有效指针avail_index数值,其中的avail_index_1到avail_index_n单元,对应每个队列的有效指针数据,也就是总共有n个队列。
(2)avail_index_ram_2模块:和avail_index_ram_1模块一样,不过是用来临时缓存计数的,而且,当index_ram模块接收到soc的重传指令后,就会主动将used_index_ram中的已用指针used_index按照队列编号顺序复制到avail_index_ram_2模块。
(3)used_index_ram模块:为FPGA内部的ram存储器,用来存放soc写进来的已用指针used_index数值,其中的used_index_1到used_index_n单元,对应每个队列的已用指针数据,也就是总共有n个队列。
(4)比较器:将avail_index_ram_1模块和avail_index_ram_2模块中的数值,按照队列编号对齐的方式依次进行比较,把不相等的队列的指针数据输出到重传启动模块。
(5)重传启动模块:接收来自比较器的结果,发起相应队列的重传操作。
这里具体介绍一下实现重传操作的过程。
(1)当soc将从host获取到的队列指针信息写到index_ram模块中后,soc紧接着会向csr模块的重传寄存器写值1,代表发起重传指令,此时csr模块会产生一个重传信号传输到virtio_blk模块,virtio_blk模块接收到重传信号后,其中的index_ram模块就会主动将used_index_ram模块中的已用指针used_index赋值到avail_index_ram_2模块中。
(2)然后avail_index_ram_1模块和avail_index_ram_2模块中的指针数值按照队列编号从1到n的顺序依次输出到比较器中,比较两者值是否相同,如果不相等,就会将此队列的已用指针used_index传输到重传启动模块。
(3)重传启动模块中,会将当前的已用指针当做起点,触发virtio_blk从起点开始去从host获取对应指针的存储数据,每当获取一个完整的存储数据后,起点就会累加1,并将此起点值及时更新回avail_index_ram_2模块中。
(4)当完成从队列0到队列n的比较重传后,此时avail_index_ram_2模块中的avail_index数值重新更新了一轮。此时会再次从流程(2)开始执行。直到最后检测到avail_index_ram_1模块和avail_index_ram_2模块里面的同一个队列的存放数值都相等,即比较器无输出,则为重传结束。
至此整个系统恢复正常,virtio_blk模块可以继续进行新的存储数据的传输了。
五、总结
DPU上的virtio_blk复位及恢复的流程,主要是采用了软硬结合的设计理念,实现硬件部分模块复位初始化,从而达到清除异常、数据重传的效果。整体而言,方案设计比较新颖的,不过此方案设计只是存储数据链路中的一个节点。除此之外,如第一章节DPU架构图所示,还有virtio_net_blk模块的复位恢复,soc的重启恢复,OVS的恢复等,都是可以深入探讨的内容。