基于Linux系统的PCIE高速数据卡驱动设计

2022-10-26 08:53中国电子科技集团公司第三十研究所王烨张颖唐璞
数字技术与应用 2022年10期
关键词:驱动程序内核中断

中国电子科技集团公司第三十研究所 王烨 张颖 唐璞

文章提出并实现了一种基于Linux系统的PCIE高速数据处理卡驱动设计方案,解决了Linux上位机与PCIE数据卡之间高效可靠的数据传输。驱动设计过程应用了DMA机制、软中断机制和多通道的思想,实现了用户层数据封装和数据交换。通过系统测试,验证了驱动设计的有效性、可靠性。

随着软硬件国产化进程的加快,基于国产Linux操作系统的软件开发越来越重要。众多外设要想在国产化平台上使用,就必须开发对应的设备驱动程序。一款CPU提供的外设接口很多,例如I2C、SPI、SDIO、USB等,但是要论高速访问,PCIE接口通常是首选。

PCI-Express,简称PCIE,是一种高速串行计算机扩展总线标准。从PCI Express X1到PCI Express X32,能满足将来一定时间内出现的低速设备到高速设备的需求。在独立显卡、独立网卡、固态硬盘领域常见PCIE的身影。本文介绍的是一款高速PCIE数据卡的驱动设计,其中数据卡使用国产FPGA(国微690T)做数据处理,硬件平台使用国产飞腾2500,数据卡与服务器之间采用PCIE总线通信。为了保证数据处理的吞吐量,驱动采用DMA数据传输技术进行用户层和数据卡的数据交换。在上位机设计了测试验证,验证了该驱动设计的有效性和稳定性。

1 数据卡驱动设计概述

本文的数据卡使用FPGA芯片做数据处理,FPGA芯片与CPU之间使用PCIE总线进行数据传输,如图1所示。

驱动程序的设计思想是申请2个大小为1024×sizeof(bd)的DMA空间tx_bd和rx_bd,其中tx_bd用于CPU向FPGA发送数据,rx_bd用于FPGA向CPU发送数据。将这2个DMA空间的物理地址分别存储在PCIE内存BAR空间的tx_bd和rx_bd基址寄存器中。同时定义tx_bd_tail、rx_bd_head、rx_bd_tail三个变量,控制收发包的顺序。由于FPGA数据处理的速度较快,为了提高数据处理性能,FPGA和驱动程序之间定义4个通道Channel,每个通道独立完成数据收发,每个Channel各自维护一组tx_bd、rx_bd、tx_bd_tail、rx_bd_head、rx_bd_tail。驱动架构设计如图2所示。

数据发送过程:

(1)驱动收到数据包后,首先数据拷贝到内核地址中,将tx_bd_tail作为索引在tx_bd空间中找到当前可用的bd元素,用数据包的发送、接收物理地址封装该bd元素;

(2)tx_bd_tail值加1;

(3)修改PCIE内存BAR空间的tx_bd_tail值。

FPGA在轮询tx_bd_tail寄存器时发现了内容变化,便会在tx_bd中找到相应的数据做数据处理。数据处理完成后,FPGA将数据处理结果填入rx_bd结构中物理地址指向位置,产生中断。

数据接收过程:

(1)驱动收到中断,将rx_bd_head(初始为0)作为索引在rx_bd中找到bd元素;

(2)判断bd结构中的标识位,以此确认这个bd代表的数据包是否已经处理完成,如果数据包未完成,结束此次中断处理。

(3)如果bd代表的数据包已经处理完成,将数据从物理地址拷贝到用户层地址;

(4)通知应用程序处理完成。

2 数据卡驱动程序设计

2.1 驱动程序的加载和卸载

本驱动定义的加载函数和卸载函数分别是:

static int __init pciedev_init ();

static void __exit pciedev_cleanup ();

在加载函数中完成两件事,(1)注册PCI驱动;(2)注册字符设备。部分代码实现如图3所示。

编译驱动后生成pciedev.ko,使用insmod pciedev.ko命令加载驱动,驱动执行pciedev_init函数,完成PCI驱动注册。

2.2 驱动程序的设备初始化

内核遍历PCI总线得到总线上所有PCI外设的列表,通过对比设备ID找到对应驱动[1]。如果ID为0x1234,0x5678,则设备和驱动匹配成功,调用pciedev_probe函数进行设备初始化,初始化流程如图4所示。

初始化4个Channel。调用dma_alloc_coherent函数[2]分配2个1024×sizeof(bd)大小的DMA空间,分别作为tx_bd(驱动向FPGA发送数据)表和rx_bd(FPGA向驱动发送数据),将物理地址对应存储在PCIE的内存BAR空间的tx_bd、rx_bd地址中(地址在BAR中的偏移和FPGA协商而定),初始化本地变量tx_bd_tail为0,该值控制发送顺序,初始化rx_bd_head为0,rx_bd_tail为1023,该值控制接收顺序,将其分别存储在BAR空间的tx_bd_tail和rx_bd_tail地址中(地址在BAR中的偏移和FPGA协商而定)。初始化tx_bd和rx_bd中的每一个bd结构,部分代码实现如图5所示。

中断服务程序初始化。PCIE数据卡支持MSIX中断,FPGA通过MSIX中断通知驱动接收返回的数据包。为了提高中断响应速度,驱动程序在中断服务程序中调用软中断作为中断处理的下半部机制。首先调用tasklet_init初始化软中断,然后调用pci_enable_msix_range使能MSIX中断,最后调用request_irq申请中断。部分代码实现如图6所示。

2.3 驱动发包流程

用户发送数据到驱动后,用户数据地址是不能直接使用的,需要将内容拷贝到内核地址空间,因此驱动首先调用Kmalloc分配一段内核空间,然后通过copy_from_user函数将用户数据拷贝到内核空间,使用这部分内核空间。为例避免每次发包都分配内存,影响数据传输速率,驱动在设备初始化1024个struct pci_transaction节点构成链表,每一个struct pci_transaction结构体节点代表一次数据传输,其中的Vaddr指向一个分配的4096字节的内核地址。struct pci_transaction部分定义如图7所示。

当用户通过已经创建的字符设备执行ioctl操作时,驱动收到数据包,执行过程如下:

(1)收到一个包,判断当前链路上的正在处理的包的个数,如果大于等于1024,说明1024个发送bd表已经满了,那么驱动向应用层返回-EBUSY错误;

(2)如果发送bd表未满,从预分配1024个struct pci_transaction链表中找到第一个可用的节点,并标记已用,拷贝用户数据到该节点Vaddr成员变量指向的内核空间,并在该节点记录下用户发送地址、接收地址以及数据长度。

将当前tx_bd_tail的值作为索引找到发送bd表中的对应bd节点,对bd节点的成员变量进行封装,然后tx_bd_tail加1,将其写入PCIE的BAR空间的tx_bd_tail中,数据卡的FPGA程序读取tx_bd_tail值,并根据这个值读出数据并处理。数据发送流程和部分代码如图8、图9所示。

2.4 驱动收包流程

当PCIE数据卡处理完数据后,产生中断,驱动的中断服务程序被触发,中断服务程序需要做两件事,(1)调度Tasklet软中断,将中断处理的下半部放在软中断中执行[3],这里指收包过程;(2)返回IRQ_HANDLED。由于中断不能被相同类型的中断打断,而下半部依然可以被中断打断,使用Tasklet软中断处理收包的机制对提高通信速率大有裨益。中断处理部分代码如图10所示。

FPGA处理数据后会在rx_bd中找到下一个可用的bd结构,置Control标志位,填充bd结构,发送中断。驱动使用rx_bd_head作为索引取出rx_bd表的bd结构。首先判断Control标识位,FPGA处理完成置位;然后取出数据包加入完成链表,rx_bd_head加1,循环处理下一个bd结构。与此同时,应用程序可以通过poll机制查询是否有包返回,如果有,那么通过ioctl调用取包命令,驱动将数据从内核地址拷贝到用户层地址,完成此次数据传输。接收数据处理流程和部分代码实现如图11、图12所示。

3 系统测试

系统测试主要是测试PCIE数据卡的性能和稳定性,因此FPGA事前设计成数据回环模式,对收到的数据包直接返回,测试链路的性能和驱动稳定性。测试程序设计成4个线程,数据卡的4个中断分别绑在CPU0、CPU1、CPU2、CPU3上,测试数据长度为4000字节。测试记录如图13所示。

如图13可见,数据通信性能大于9.4Gbps。

4 结语

基于Linux系统的PCIE数据卡驱动程序,在保障数据传输性能的同时,代码具有很高的移植性,基于这种设计的数据卡驱动已经在云服务器PCIE数据卡上使用。

猜你喜欢
驱动程序内核中断
强化『高新』内核 打造农业『硅谷』
基于嵌入式Linux内核的自恢复设计
Linux内核mmap保护机制研究
计算机硬件设备驱动程序分析
跟踪导练(二)(5)
千里移防,卫勤保障不中断
基于MPC8280的CPU单元与内部总线驱动程序设计
AT89C51与中断有关的寄存器功能表解
FPGA内嵌PowerPC的中断响应分析