前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >DPDK之PMD原理

DPDK之PMD原理

原创
作者头像
glinuxer
发布2019-03-31 21:28:10
5.7K0
发布2019-03-31 21:28:10
举报

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

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

图1. 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. igbuio_setup_bars

2. 设置uio_info的其他成员

图3. igb_uio设置uio_info
图3. igb_uio设置uio_info

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

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

图4. uio设备
图4. uio设备

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

图5. 打开uio设备
图5. 打开uio设备

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

图6. open uio设备
图6. open uio设备

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

图7. 设置中断信息
图7. 设置中断信息
图8. 注册中断
图8. 注册中断

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

图9. igb_uio中断处理函数
图9. igb_uio中断处理函数

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

图10. 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
图11.uio_mmap

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

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

图12. 应用层uio初始化
图12. 应用层uio初始化

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

图13. 打开ui设备
图13. 打开ui设备

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

图14. pci_uio_map_resource
图14. pci_uio_map_resource

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

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

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

图16. 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
图17.rte_eth_rx_burst

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

图18 eth_igb_recv_pkts
图18 eth_igb_recv_pkts
图19 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的上限。然后进行报文处理,处理完毕,再次循环。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档