DPDK之PMD原理

PMD是Poll Mode Driver的缩写,即基于用户态的轮询机制的驱动。本文将介绍PMD的基本原理。

在不考虑vfio的情况下,PMD的结构图如下:

图1. PMD结构图

虽然PMD是在用户态实现设备驱动,但还是依赖于内核提供的策略。其中uio模块,是内核提供的用户态驱动框架,而igb_uio是DPDK kit中拥有与uio交互,bind指定网卡的内核模块。

当使用DPDK脚本dpdk-devbind来bind网卡时,会通过sysfs与内核交互,让内核使用指定驱动来匹配网卡。具体的行为向/sys/bus/pci/devices/(pci id)/driver_override写入指定驱动名称,或者向/sys/bus/pci/drivers/igb_uio(驱动名称)/new_id写入要绑定网卡的PCI ID。前者是配置设备,让其选择驱动。后者是是配置驱动,让其支持新的PCI设备。按照内核的文档https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-pci,这两个动作都会促使驱动bind新设备。但是在dpdk-devbind脚本中,还是通过向/sys/bus/pci/drivers/igb_uio(驱动名称)/bind写入pci id来保证bind。—— 也许是处于兼容性考虑吧。

当使用igb_uio bind指定设备后,内核会调用igb_uio注册的struct pci_driver的probe函数,即igbuio_pci_probe。在这个函数中,设置PCI的一些操作(如设置PCI BAR、DMA等),不是重点,那是驱动工程师的职责:) 对于PMD来说,重点是与UIO的交互。

1. 调用igbuio_setup_bars,设置uio_info的uio_mem和uio_port。

图2. igbuio_setup_bars

2. 设置uio_info的其他成员

图3. igb_uio设置uio_info

3. 调用uio_register_device,注册uio设备。

查看/dev目录,可以发现对应的uioX设备,如下图:

图4. uio设备

打开uio设备

这时,应用层已经可以使用uio设备了。DPDK的应用层代码,会打开uioX设备。在函数pci_uio_alloc_resource中,

图5. 打开uio设备

当open对应的uio设备时,对应的内核操作为uio_open,其又会调用igb_uio的open函数,流程图如下:

图6. open uio设备

igb_uio的默认中断模式为RTE_INTR_MODE_MSIX,在igbuio_pci_enable_interrupts的关键代码如下:

图7. 设置中断信息

图8. 注册中断

当打开uio设备时,igb_uio注册了一个中断。这时大家应该有个疑问,PMD不是用户态轮询设备吗?为什么还要申请中断,注册中断处理函数呢?这是因为,即使应用层可以通过uio来实现设备驱动,但是设备的某些事件还是需要内核进行响应,然后通知应用层。当然,现在的中断处理已经非常简单了。

图9. igb_uio中断处理函数

其中的关键步骤是调用uio_event_notify。

图10. uio_event_notify

这个函数很简单:1. 增加uio设备的“事件”数; 2. 唤醒在idev->wait等待队列中的task;3. 使用信号异步通知async_queue队列中的进程;目前DPDK没有使用异步IO的方式,所有对于DPDK的PMD来说,只有前两个语句有用。

uio模块除了实现了上面的“事件”通知,还支持了mmap方法,用于将注册的uio设备的“内存空间”映射到应用空间。其mmap的函数为uio_mmap,关键代码如下:

图11.uio_mmap

至此,uio已经可以让PMD的应用层访问设备的大部分资源了。接下来,要转过去看看PMD的应用层。

当DPDK的app启动时,会进行EAL初始化,如下图:

图12. 应用层uio初始化

在pci_uio_alloc_resource中,主要是打开dpdk要管理的uio设备

图13. 打开ui设备

同时,DPDK还需要把PCI设备的BAR映射到应用层。在pci_uio_map_resource函数中,除了调用上图中的pci_uio_alloc_resource,还会调用pci_uio_map_resource_by_index做资源映射。

图14. pci_uio_map_resource

下面就是PMD的应用层的驱动实现了。以最简单的e1000驱动为例,在其初始化函数eth_igb_dev_init中,

图15. 注册e1000的中断处理函数

从图11和图12的代码中,可以看出当uio设备有事件时,由eth_igb_interrupt_handler负责处理,实现了用户态的中断处理。

图16. eth_igb_interrupt_handler

eth_igb_interrupt_handler非常简单,只是处理设备的状态变化事件,如link status。

接下来,就是最重要的了,PMD如何读取网卡数据。DPDK的应用代码,会调用rte_eth_rx_burst读取数据报文。

图17.rte_eth_rx_burst

在这个函数中,会调用驱动dev->rx_pkt_burst来做实际的操作。以e1000为例,即eth_igb_recv_pkts。

图18 eth_igb_recv_pkts

图19 eth_igb_recv_pkts

这里的实现很简单。如果网卡接收buffer的描述符表示已经完成一个报文的接收(有E1000_RXD_STAT_DD标志),则rte_mbuf_raw_alloc一个mbuf,进行处理。如果没有报文,直接跳出循环。

对应RTC模型的DPDK应用来说,就是不断的调用rte_eth_rx_burst去“问”网卡是否有新的报文。如果有,就取走所有的报文或达到参数nb_pkts的上限。然后进行报文处理,处理完毕,再次循环。

以上就是PMD的大体流程。

DPDK提供了多种不同类型的驱动程序,包括基于物理设备的驱动程序和虚拟设备驱动程序。其中一种虚拟设备驱动程序是libpcap-PMD驱动程序,它允许使用libpcap捕获和发送数据包。在本文中,我们将介绍如何使用libpcap-PMD驱动程序在DPDK中收发数据包。 1. 安装libpcap和libpcap-dev库 在使用libpcap-PMD驱动程序之前,需要先安装libpcap和libpcap-dev库。在Ubuntu系统中,可以使用以下命令进行安装: ``` sudo apt-get install libpcap0.8 libpcap0.8-dev ``` 2. 编译DPDK 在编译DPDK时,需要启用libpcap-PMD驱动程序。可以使用以下命令进行编译: ``` make config T=x86_64-native-linuxapp-gcc O=x86_64-native-linuxapp-gcc CONFIG_RTE_LIBRTE_PMD_PCAP=y make ``` 在这个命令中,CONFIG_RTE_LIBRTE_PMD_PCAP=y用于启用libpcap-PMD驱动程序。 3. 配置DPDK环境 在使用libpcap-PMD驱动程序之前,需要进行DPDK环境的配置。可以使用以下命令进行配置: ``` sudo modprobe uio_pci_generic sudo ./usertools/dpdk-devbind.py --bind=uio_pci_generic sudo ./usertools/dpdk-devbind.py --status ``` 这将加载uio_pci_generic内核模块,并将所有网络设备绑定到uio_pci_generic驱动程序。 4. 运行应用程序 在使用libpcap-PMD驱动程序时,需要指定使用libpcap作为数据包源和目的地。可以使用以下命令运行应用程序: ``` ./build/app/testpmd -c 0x03 -n 4 --vdev 'net_pcap0,rx_iface=<iface>,tx_iface=<iface>' -- -i --port-topology=loop ``` 在这个命令中,rx_iface和tx_iface参数分别指定libpcap-PMD驱动程序的接收和发送接口。可以使用以下命令查看可用接口: ``` sudo tcpdump -D ``` 5. 测试 在运行应用程序后,可以使用testpmd命令进行测试。可以使用以下命令发送和接收数据包: ``` testpmd> start tx_first testpmd> show port stats all ``` 在这个命令中,tx_first参数将启动数据包发送,show port stats all命令将显示端口统计信息。 这就是使用libpcap-PMD驱动程序在DPDK中收发数据包的过程。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值