searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

DPDK vdev 的使用

2025-06-17 09:18:19
0
0

本文介绍 DPDK 中 vdev 的创建方式和简单的使用方式。

用户编写的 DPDK 应用使用 DPDK 的 API 来发送和接收网络数据包。这些 API 隐藏了底下网卡硬件的差异和编程环境的差异,提供统一的编程接口。这些 API 统称为环境抽象层 EAL(Environment Abstraction Layer)。

PMD (Poll Mode Driver) 是 DPDK 提供的用户态网卡驱动,不同网卡有自己对应的 PMD,以静态或动态链接方式使用。一般 DPDK 应用编译时链接了很多类型的 PMD,我们也可以适当裁剪来去除平时用不到的 PMD。

携带多种 PMD 时,应用程序用一套业务逻辑代码和收发网络数据包的 API 就能操作不同的网卡来完成工作。

 

为了这套网络数据包从应用层到网卡物理层能对接起来,需要打通从 EAL 层的收发包 API 到物理网卡的收发包。

这里一共分为以下几个层次:

  1. 用户配置应用程序使用到哪些物理网卡
  2. 应用层使用物理网卡对应的网口号调用统一的 EAL 收发包 API
  3. EAL 收发包 API 使用物理网卡对应的 PMD 的收发包 API
  4. PMD 从物理网卡收发网络数据包

其中物理网口的网口号分配,EAL API 与 PMD API 的对应,PMD 与物理网卡的对应都是 DPDK 自动完成的。

上面的 4 个步骤的执行过程如下(跟上面不是一一对应)。

  1. 用户为使用到的网卡绑定 DPDK 需要的设备驱动,例如 igb_uio/uio_generic_pci/vfio_pci,有些网卡不需要绑定,例如 MLNX 的网卡。
  2. 应用程序链接的 PMD 在应用程序全局初始化时注册到对应的 bus 的驱动列表上。一般的物理网卡对应 pci bus, 虚拟类型的网络设备对应 vdev bus。
  3. DPDK 框架初始化时,会查找系统的网络设备,这步由不同的 bus 自己查找自己所管理的设备。
  4. DPDK 初始化过程中,调用 bus 的 probe 过程来关联 PMD 与对应的设备,例如物理网卡或虚拟网络设备。DPDK 会为查找到的网络设备分配网口号,并创建一个以太网设备对象 ethdev, ethdev 的收发包 API 关联到 PMD 的 API。
  5. PMD 收发网络包时,直接操作物理网卡对应的内存和寄存器来完成收发功能。

DPDK 中虚拟网络设备 vdev 与物理网卡不一样,没有直接对应的物理设备,不像在 linux 中可以通过查找 pci bus 来收集设备列表,vdev 只能通过 api 来创建虚拟设备,并且有自己对应的 bus 类型 vdev。

vdev 的设备来源包括命令行参数和直接的 api 调用。其中命令行参数最终也是调 api 来创建虚拟设备。

常用的虚拟设备包括 bonding, virtio_user, net_pcap 等。

vdev 介绍

下面介绍一下 vdev 的创建和设备参数的使用

通过 EAL 参数创建

./dpdk-testpmd -l 1-3 -n 4 -a 01:00.0 -a 01:00.1 \
    --vdev "net_bonding0,mode=2,xmit_policy=l23,member=0000:01:00.0,member=0000:01:00.1" \
    -- -i --portmask=0x3 --nb-cores=2

上面命令行参数指定了 2 个物理设备 01:00.0 和 01:00.1,还有一个 vdev, 类型是 net_bonding

通过命令行方式创建 vdev 时,设备类型后面带了详细的设备参数。

DPDK EAL 的命令行参数可以指定三种设备类型参数

enum rte_devtype {
	RTE_DEVTYPE_ALLOWED, // 对应 -a
	RTE_DEVTYPE_BLOCKED, // 对应 -b
	RTE_DEVTYPE_VIRTUAL, // 对应 --vdev
};

allow list 是允许使用的 pci 设备列表,block list 是禁止使用的 pci 设备列表,virtual 的 vdev 设备的参数。

vdev 设备初始化分为四步:

  1. 注册 vdev bus, 注册 vdev 对应的 PMD 到 vdev bus 的驱动列表
  2. 处理 EAL 参数 - eal_parse_args(argc, argv) ,包括 allow/block list, vdev
  3. scan bus 上注册的设备 - rte_bus_scan(),
    1. pci 设备从系统目录寻找
    2. vdev 设备从 EAL 参数解析得到的设备参数列表中找
  1. 已找到的设备,查找设备匹配的驱动并进行驱动 probe,处理设备参数 - rte_bus_probe()

对应 eal 初始化函数

// lib/eal/linux/eal.c
eal_parse_args(int argc, char **argv) {
    ...
    eal_parse_args(argc, argv);
    ...
    rte_bus_scan(); // pci 存入 pci bus 的 device list 中,vdev 存入 vdev device list 中
    ...
    rte_bus_probe();
    ...
}

在 pci 设备初始化完后,会为 vdev 分配网口号。

通过封装过的 API 创建

int bond_port_id = rte_eth_bond_create("net_bonding0", BONDING_MODE_BALANCE, 0);
rte_eth_dev_configure(BOND_PORT, 1, 1, ...;
rte_eth_bond_member_add(bond_port_id, 0);
rte_eth_bond_member_add(bond_port_id, 1);
rte_eth_rx_queue_setup(bond_port_id, 0, ...);
rte_eth_tx_queue_setup(bond_port_id, 0, ...);
rte_eth_dev_start(bond_port_id);

net_bonding 有很多参数,通过 API 方式创建 vdev 时,rte_eth_bond_create 里只指定了 mode 和 socket_id,其他参数需要通过 API 来设置,例如 bonding 的 member 和 master。

rte_eth_bond_create 内部创建流程
rte_eth_bond_create
  rte_vdev_init
    insert_vdev // 插入全局列表 vdev_device_list
      rte_devargs_insert // 这一步是将参数对象插入全局列表 devargs_list,后续可以处理
    vdev_probe_all_drivers // probe 阶段会解析设备参数和执行初始化

通过 hotplug api 创建

rte_eal_hotplug_add("vdev", "virtio_user0", "path=/dev/vhost-net");

内部处理流程

rte_eal_hotplug_add
  build_devargs
  rte_dev_probe
    local_dev_probe
      rte_devargs_insert
      da->bus->scan() // vdev_scan
      dev=da->bus->find_device // rte_vdev_find_device
      dev->bus->plug(dev) // vdev_plug 内部调 vdev_probe_all_drivers

上面的 vdev_scan 步骤跟 rte_vdev_init 中的 insert_vdev 效果差不多,都是将 device 放入 vdev_device_list, 有什么区别,为什么不统一成一个?

secondary 进程调用 vdev_action VDEV_SCAN_ONE 时会调 insert_vdev

device 介绍常用(通用)参数。

EAL 创建和 API 创建对比

EAL 的好处是可以在不修改程序逻辑的情况下,只修改程序运行时的命令行参数就能为程序提供额外功能支持。例如应用程序原来只处理一个网口的报文,现在通过命令行创建 bonding, 应用程序只处理这个 bonding 接口,实际底下可以通过命令行参数指定多个物理口成员。

API 相比 EAL 参数的好处理是可以动态修改,例如 bonding 设备可以动态添加和删除 member。

查看 PMD 支持哪些参数

usertools/dpdk-pmdinfo.py dpdk-testpmd | jq '.[] | select(.name=="net_bonding")'

0条评论
0 / 1000
c****x
1文章数
0粉丝数
c****x
1 文章 | 0 粉丝
c****x
1文章数
0粉丝数
c****x
1 文章 | 0 粉丝
原创

DPDK vdev 的使用

2025-06-17 09:18:19
0
0

本文介绍 DPDK 中 vdev 的创建方式和简单的使用方式。

用户编写的 DPDK 应用使用 DPDK 的 API 来发送和接收网络数据包。这些 API 隐藏了底下网卡硬件的差异和编程环境的差异,提供统一的编程接口。这些 API 统称为环境抽象层 EAL(Environment Abstraction Layer)。

PMD (Poll Mode Driver) 是 DPDK 提供的用户态网卡驱动,不同网卡有自己对应的 PMD,以静态或动态链接方式使用。一般 DPDK 应用编译时链接了很多类型的 PMD,我们也可以适当裁剪来去除平时用不到的 PMD。

携带多种 PMD 时,应用程序用一套业务逻辑代码和收发网络数据包的 API 就能操作不同的网卡来完成工作。

 

为了这套网络数据包从应用层到网卡物理层能对接起来,需要打通从 EAL 层的收发包 API 到物理网卡的收发包。

这里一共分为以下几个层次:

  1. 用户配置应用程序使用到哪些物理网卡
  2. 应用层使用物理网卡对应的网口号调用统一的 EAL 收发包 API
  3. EAL 收发包 API 使用物理网卡对应的 PMD 的收发包 API
  4. PMD 从物理网卡收发网络数据包

其中物理网口的网口号分配,EAL API 与 PMD API 的对应,PMD 与物理网卡的对应都是 DPDK 自动完成的。

上面的 4 个步骤的执行过程如下(跟上面不是一一对应)。

  1. 用户为使用到的网卡绑定 DPDK 需要的设备驱动,例如 igb_uio/uio_generic_pci/vfio_pci,有些网卡不需要绑定,例如 MLNX 的网卡。
  2. 应用程序链接的 PMD 在应用程序全局初始化时注册到对应的 bus 的驱动列表上。一般的物理网卡对应 pci bus, 虚拟类型的网络设备对应 vdev bus。
  3. DPDK 框架初始化时,会查找系统的网络设备,这步由不同的 bus 自己查找自己所管理的设备。
  4. DPDK 初始化过程中,调用 bus 的 probe 过程来关联 PMD 与对应的设备,例如物理网卡或虚拟网络设备。DPDK 会为查找到的网络设备分配网口号,并创建一个以太网设备对象 ethdev, ethdev 的收发包 API 关联到 PMD 的 API。
  5. PMD 收发网络包时,直接操作物理网卡对应的内存和寄存器来完成收发功能。

DPDK 中虚拟网络设备 vdev 与物理网卡不一样,没有直接对应的物理设备,不像在 linux 中可以通过查找 pci bus 来收集设备列表,vdev 只能通过 api 来创建虚拟设备,并且有自己对应的 bus 类型 vdev。

vdev 的设备来源包括命令行参数和直接的 api 调用。其中命令行参数最终也是调 api 来创建虚拟设备。

常用的虚拟设备包括 bonding, virtio_user, net_pcap 等。

vdev 介绍

下面介绍一下 vdev 的创建和设备参数的使用

通过 EAL 参数创建

./dpdk-testpmd -l 1-3 -n 4 -a 01:00.0 -a 01:00.1 \
    --vdev "net_bonding0,mode=2,xmit_policy=l23,member=0000:01:00.0,member=0000:01:00.1" \
    -- -i --portmask=0x3 --nb-cores=2

上面命令行参数指定了 2 个物理设备 01:00.0 和 01:00.1,还有一个 vdev, 类型是 net_bonding

通过命令行方式创建 vdev 时,设备类型后面带了详细的设备参数。

DPDK EAL 的命令行参数可以指定三种设备类型参数

enum rte_devtype {
	RTE_DEVTYPE_ALLOWED, // 对应 -a
	RTE_DEVTYPE_BLOCKED, // 对应 -b
	RTE_DEVTYPE_VIRTUAL, // 对应 --vdev
};

allow list 是允许使用的 pci 设备列表,block list 是禁止使用的 pci 设备列表,virtual 的 vdev 设备的参数。

vdev 设备初始化分为四步:

  1. 注册 vdev bus, 注册 vdev 对应的 PMD 到 vdev bus 的驱动列表
  2. 处理 EAL 参数 - eal_parse_args(argc, argv) ,包括 allow/block list, vdev
  3. scan bus 上注册的设备 - rte_bus_scan(),
    1. pci 设备从系统目录寻找
    2. vdev 设备从 EAL 参数解析得到的设备参数列表中找
  1. 已找到的设备,查找设备匹配的驱动并进行驱动 probe,处理设备参数 - rte_bus_probe()

对应 eal 初始化函数

// lib/eal/linux/eal.c
eal_parse_args(int argc, char **argv) {
    ...
    eal_parse_args(argc, argv);
    ...
    rte_bus_scan(); // pci 存入 pci bus 的 device list 中,vdev 存入 vdev device list 中
    ...
    rte_bus_probe();
    ...
}

在 pci 设备初始化完后,会为 vdev 分配网口号。

通过封装过的 API 创建

int bond_port_id = rte_eth_bond_create("net_bonding0", BONDING_MODE_BALANCE, 0);
rte_eth_dev_configure(BOND_PORT, 1, 1, ...;
rte_eth_bond_member_add(bond_port_id, 0);
rte_eth_bond_member_add(bond_port_id, 1);
rte_eth_rx_queue_setup(bond_port_id, 0, ...);
rte_eth_tx_queue_setup(bond_port_id, 0, ...);
rte_eth_dev_start(bond_port_id);

net_bonding 有很多参数,通过 API 方式创建 vdev 时,rte_eth_bond_create 里只指定了 mode 和 socket_id,其他参数需要通过 API 来设置,例如 bonding 的 member 和 master。

rte_eth_bond_create 内部创建流程
rte_eth_bond_create
  rte_vdev_init
    insert_vdev // 插入全局列表 vdev_device_list
      rte_devargs_insert // 这一步是将参数对象插入全局列表 devargs_list,后续可以处理
    vdev_probe_all_drivers // probe 阶段会解析设备参数和执行初始化

通过 hotplug api 创建

rte_eal_hotplug_add("vdev", "virtio_user0", "path=/dev/vhost-net");

内部处理流程

rte_eal_hotplug_add
  build_devargs
  rte_dev_probe
    local_dev_probe
      rte_devargs_insert
      da->bus->scan() // vdev_scan
      dev=da->bus->find_device // rte_vdev_find_device
      dev->bus->plug(dev) // vdev_plug 内部调 vdev_probe_all_drivers

上面的 vdev_scan 步骤跟 rte_vdev_init 中的 insert_vdev 效果差不多,都是将 device 放入 vdev_device_list, 有什么区别,为什么不统一成一个?

secondary 进程调用 vdev_action VDEV_SCAN_ONE 时会调 insert_vdev

device 介绍常用(通用)参数。

EAL 创建和 API 创建对比

EAL 的好处是可以在不修改程序逻辑的情况下,只修改程序运行时的命令行参数就能为程序提供额外功能支持。例如应用程序原来只处理一个网口的报文,现在通过命令行创建 bonding, 应用程序只处理这个 bonding 接口,实际底下可以通过命令行参数指定多个物理口成员。

API 相比 EAL 参数的好处理是可以动态修改,例如 bonding 设备可以动态添加和删除 member。

查看 PMD 支持哪些参数

usertools/dpdk-pmdinfo.py dpdk-testpmd | jq '.[] | select(.name=="net_bonding")'

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0