使用 DPDK 的用户空间网络

作者:Rami Rosen

DPDK 是一个完全开源的项目,在用户空间中运行。它是一个多供应商和多架构的项目,旨在实现高 I/O 性能和高数据包处理速率,这些都是网络领域中最重要的特性。它由英特尔于 2010 年创建,并于 2017 年 4 月转移到 Linux 基金会。此举将其定位为最主要和最重要的开源 Linux 项目之一。DPDK 最初是为电信/数据通信基础设施创建的,但如今,它几乎无处不在,包括云、数据中心、设备、容器等等。在本文中,我将对该项目进行高级概述,并讨论 DPDK 17.08(2017 年 8 月)版本中发布的功能。

毫无疑问,许多网络项目都在努力实现高速和高性能。DPDK 通过多种因素来实现此目标。其中之一是 DPDK 是一个用户空间应用程序,它绕过了 Linux 内核网络堆栈的繁重层,并直接与网络硬件对话。另一个因素是使用内存大页。通过使用大页(大小为 2MB 或 1GB),与使用标准内存页(在许多平台上大小为 4k)相比,所需的内存页数量更少。因此,转换后备缓冲区 (TLB) 未命中的次数显着减少,性能得到提高。另一个因素是在代码中进行了低级优化,其中一些与内存缓存行对齐有关,旨在实现最佳缓存使用、预取等等。(深入研究这些优化的技术细节超出了本文的范围。)

DPDK 近年来广受欢迎,并在许多开源项目中得到应用。许多 Linux 发行版(Fedora、Ubuntu 等)也在其软件包系统中包含了 DPDK 支持。

DPDK 的核心组成部分是库和驱动程序,也称为轮询模式驱动程序 (PMD)。在撰写本文时,有 35 多个库。这些库抽象了低级实现细节,从而提供了灵活性,因为每个供应商都实现了自己的低级层。

DPDK 开发模型

DPDK 主要用 C 语言编写,但该项目也有一些用 Python 编写的工具。对 DPDK 的所有代码贡献都是通过在 dpdk-dev 邮件列表中发送和讨论补丁来完成的。旨在首先获得反馈的补丁通常标题为 RFC(请求评论)。为了尽可能保持代码的稳定性,首选尽可能保留 ABI(应用程序二进制接口)。当似乎别无选择时,开发人员应遵循严格的 ABI 弃用流程,包括提前在 dpdk-dev 邮件列表中宣布请求的 ABI 更改。已批准和合并的 ABI 更改记录在发行说明中。当对新功能的接受有疑问,但相应的补丁无论如何都合并到主树中时,它们将被标记为“EXPERIMENTAL”。这意味着这些补丁可能会被更改甚至删除,恕不另行通知。因此,例如,新的 rte_bus 实验性 API 已在 DPDK 17.08 中添加。我还应该注意到,通常每当为新的通用 API(应该支持来自不同供应商的多个硬件设备)发送补丁到邮件列表时,都希望市场上至少有一个支持新功能的硬件设备可用(如果设备只是宣布而不可用,则开发人员无法对其进行测试)。

有一个由来自多家公司(英特尔、NXP、6WIND、Cavium 等)的九名成员组成的技术委员会。会议通常每两周通过 IRC 举行一次,会议纪要在 dpdk-dev 邮件列表中发布。

与其他大型开源项目一样,每年在全球范围内定期举办社区驱动的 DPDK 活动。首先,有各种 DPDK 峰会。其中,DPDK Summit Userspace 专注于更具互动性并从社区获得反馈。世界各地还有几个 DPDK 聚会。此外,不时会进行在线社区调查,在 dpdk-dev 邮件列表中宣布,以便从社区获得反馈,每个人都可以参与其中。

DPDK 网站托管着主 DPDK 代码仓库,但其他几个代码仓库专门用于新功能。DPDK 树中存在几个工具和实用程序,其中包括 dpdk-devbind.py 脚本,用于将网络设备或加密设备与 DPDK 关联,以及 testpmd,这是一个用于各种任务的 CLI 工具,例如转发、监控统计信息等。“examples”文件夹下有近 50 个示例应用程序,捆绑了完整的详细文档。

除了 DPDK 本身,DPDK 站点还托管着其他几个开源项目。其中之一是 DPDK 测试套件 (DTS),这是一个基于 Python 的 DPDK 框架。它有 100 多个测试模块,用于各种功能,包括最先进和最新的功能。它与 IXIA 和 Scapy 流量生成器一起运行。它包括功能测试和基准测试,并且非常容易配置和运行,因为您只需要设置三到四个配置文件即可。您还可以设置要运行它的 DPDK 版本。DTS 开发由专门的邮件列表处理,目前支持英特尔网卡和 Mallanox 网卡。

DPDK 每三个月发布一次。此发布节奏旨在使 DPDK 能够快速发展,同时为审查、讨论和改进贡献提供足够的机会。最终发布之前通常有 3-5 个候选版本 (RC)。对于 17.08 版本,有来自 125 位作者的 1,023 个补丁,包括来自英特尔、Cavium、6WIND、NXP 等的补丁。发布编号遵循 Ubuntu 版本约定。长期稳定 (LTS) 版本维护两年。DPDK 社区目前正在讨论未来 LTS 版本的计划。该计划是将偶数年(16.11、18.11 等)的每个 .11 版本都作为 LTS 版本,并维护两年。

近期功能和新思路

去年添加了几个有趣的功能。最令人着迷的功能之一(在 DPDK 17.05 中添加,并在 17.08 和 17.11 中启用了新功能)是英特尔 I40E 驱动程序(10Gb/25Gb/40Gb)的“动态设备个性化”(DDP)。此功能允许动态地将每个设备的配置文件应用于 I40E 固件。您可以通过运行 testpmd CLI 命令 (ddp add) 加载配置文件,也可以使用 ddp del 删除它。您还可以在流量流动时应用或删除配置文件,在处理配置文件期间会丢弃少量数据包。这些配置文件由英特尔创建,而不是由客户创建,因为 I40E 固件编程需要深入了解 I40E 设备内部结构。

其他值得一提的功能包括 Bruce Richardson 的构建系统补丁,它为 DPDK 提供了更高效的构建系统,包括 meson 和 ninja、一个新的内核模块,称为内核控制路径 (KCP)、端口表示等等。

DPDK 和网络项目

DPDK 用于各种重要的网络项目。列表很长,但我想简要提及其中一些

  • Open vSwitch (OvS):OvS 项目实现了一个虚拟网络交换机。它于 2016 年 8 月过渡到 Linux 基金会,并在业界广受欢迎。DPDK 于 2015 年首次集成到 OvS 2.2 中。后来,在 OvS 2.4 中,添加了对 vHost 用户(一种虚拟设备)的支持。在后续版本中添加了对多队列和 numa 感知等高级功能的支持。
  • Contrail vRouter:Contrail Systems 是一家开发 SDN 控制器的初创公司。瞻博网络于 2012 年收购了它,瞻博网络后来发布了 Contrail vRouter 作为开源项目。它使用 DPDK 来实现更好的网络性能。
  • pktgen-dpdk:一个基于 DPDK 的开源流量生成器(托管在 DPDK 站点上)。
  • TREX:一个基于 DPDK 的有状态和无状态开源流量生成器。
  • 矢量数据包处理 (VPP):一个 FD.io 项目。
DPDK 入门

对于 DPDK 的新手,无论是用户还是开发人员,DPDK 站点上都有优秀的文档。建议您实际尝试运行几个示例应用程序(按照“示例应用程序用户指南”),从“Hello World”应用程序开始。定期关注 dpdk-users 邮件列表也是一个好主意。对于那些对开发感兴趣的人,程序员指南是了解架构和开发环境的良好信息来源,开发人员也应该关注 dpdk-dev 邮件列表。

DPDK 和 SR-IOV 示例

我想用一个非常基本的示例(基于 SR-IOV)来结束本文,该示例说明如何创建 DPDK VF 以及如何使用 qemu 将其附加到 VM。我还展示了如何创建非 DPDK VF(“内核 VF”),将其附加到 VM,在该 VF 上运行 DPDK 应用程序,并从主机与其通信。

作为准备步骤,您需要在主机上启用 IOMMU 和虚拟化。为了支持这一点,请将 intel_iommu=on iommu=pt 作为内核参数添加到内核命令行 (grub.cfg) 中,并在 BIOS 中启用虚拟化和 VT-d(VT-d 代表“用于定向 I/O 的英特尔虚拟化技术”)。在此示例中,您将使用英特尔 I40E 网络接口卡。I40E 设备驱动程序每个设备最多支持 128 个 VF,平均分配到各个端口,因此如果您有四端口 I40E 网卡,则可以在每个端口上创建最多 32 个 VF。

在本示例中,我还展示了 testpmd CLI 的简单用法,如前所述。本示例基于 DPDK-17.08,这是撰写本文时 DPDK 的最新版本。在本示例中,您将使用单根 I/O 虚拟化 (SR-IOV),它是 PCI Express (PCIe) 规范的扩展,允许跨多个虚拟环境共享单个物理 PCI Express 资源。这项技术在数据中心/云环境中非常流行,许多网络适配器都支持此功能,同样,它们的驱动程序也支持此功能。我应该注意到,SRIOV 不仅限于网络设备,也适用于其他 PCI 设备,例如显卡。

DPDK VF

您可以通过将请求的 VF 数量写入名为 max_vfs 的 DPDK sysfs 条目来创建 DPDK VF。假设 eth8 是您要在其上创建 VF 的 PF,其 PCI 地址为 0000:07:00.0。(您可以使用 ethtool -i <ethDeviceName> | grep bus-info 获取 PCI 地址。)以下是在主机上运行以创建 VF 并启动 VM 的顺序。首先,使用 usertools/dpdk-devbind.py 将 PF 绑定到 DPDK,例如


modprobe uio
insmod /build/kmod/igb_uio.k
./usertools/dpdk-devbind.py -b igb_uio 0000:07:00.0

然后,创建两个 DPDK VF,命令如下:


echo 2 > /sys/bus/pci/devices/0000:07:00.0/max_vfs

您可以通过运行以下命令检查是否已通过此操作创建了两个 VF:lspci | grep "Virtual Function",或者通过验证在 /sys/bus/pci/devices/0000:07:00.0/ 下是否为新创建的两个 VF 添加了两个新的符号链接:virtfn0 和 virtfn1。

接下来,通过 qemu 使用 PCI 直通启动 VM,例如


qemu-system-x86_64 -enable-kvm -cpu host \
    -drive file=Ubuntu_1604.qcow2,index=0,media=disk,format=qcow2 \
    -smp 5 -m 2048 -vga qxl \
    -vnc :1 \
    -device pci-assign,host=0000:07:02.0 \
    -net nic,macaddr=00:00:00:99:99:01 \
    -net tap,script=/etc/qemu-ifup.

注意:qemu-ifup 是在 VM 启动时调用的 shell 脚本,通常用于设置网络。

接下来,您可以启动 VNC 客户端(例如 RealVNC 客户端)以访问 VM,并从那里,您可以使用 lspci -n 验证 VF 是否确实已分配给它。您应该看到一个设备,其供应商 ID/设备 ID 组合为“8086 154c”;“8086 154c”是 I40E 网卡的虚拟功能 PCI ID。您可以在 guest 虚拟机中的 VF 上启动 DPDK 应用程序。

内核 VF

为了结束本示例,让我们在主机上创建一个内核 VF,并在 VM 中在其上运行 DPDK,然后让我们看看与主机 PF 的简单交互。

首先,使用以下命令创建两个内核 VF


echo 2 > /sys/bus/pci/devices/0000:07:00.0/sriov_numvfs

同样,您可以通过运行 lspci | grep "Virtual Function" 来验证是否已创建了这两个 VF。

接下来,运行以下序列


echo "8086 154c" > /sys/bus/pci/drivers/pci-stub/new_id
echo 07:02.0 > /sys/bus/pci/devices/$VF_PCI_0/driver/unbind
echo 07:02.0 > /sys/bus/pci/drivers/pci-stub/bind

然后以与之前相同的方式启动 VM,使用之前提到的相同 qemu-system-x86_64 命令。同样,在 guest 虚拟机中,您应该能够使用 lspci -n 看到 I40E VF。在主机上,执行 ip link show 将显示 eth8 的两个 VF:vf 0 和 vf 1。您可以从主机使用 ip link set 设置 VF 的 MAC 地址——例如


ip link set eth8 vf 0 mac 00:11:22:33:44:55

然后,当您在 guest 虚拟机中运行 DPDK 应用程序(如 testpmd),并从 testpmd CLI 运行例如 show port info 0 时,您将看到您在主机中设置的 MAC 地址确实反映在该 VF 的 DPDK 中。

总结

本文提供了 DPDK 项目的高级概述,该项目正在动态增长并在行业中越来越受欢迎。不久的将来可能会带来对来自不同供应商的更多网络接口的支持,以及新功能。

Rami Rosen 是 Linux 内核和虚拟化专家,《Linux Kernel Networking—Implementation and Theory》(Apress,648 页,2013 年)的作者。他在英特尔公司担任技术主管。您可以在他的主页上找到演示文稿、文章等。
加载 Disqus 评论