VPP中的DPDK插件源码详解第一篇:DPDK插件的作用和意义以及整体架构

目录

第一部分:DPDK插件的作用和意义

  • 第1章:DPDK插件概述
    • 1.1 DPDK插件在VPP中的作用和意义
    • 1.2 DPDK插件与DPDK库的关系
    • 1.3 DPDK插件在VPP数据包转发流程中的位置
    • 1.4 DPDK插件的主要功能概述
    • 1.5 与其他输入/输出模块的对比

第二部分:DPDK插件的整体架构

  • 第2章:模块架构和文件组织

    • 2.1 DPDK插件的文件组织结构
    • 2.2 各文件的功能和职责
    • 2.3 模块间的依赖关系
    • 2.4 模块与外部系统的关系
  • 第3章:核心数据结构

    • 3.1 DPDK设备结构体(dpdk_device_t)
    • 3.2 接收队列结构体(dpdk_rx_queue_t)
    • 3.3 发送队列结构体(dpdk_tx_queue_t)
    • 3.4 每线程数据结构体(dpdk_per_thread_data_t)
    • 3.5 DPDK主结构体(dpdk_main_t)
    • 3.6 流表相关结构体
    • 3.7 数据结构之间的关系

第三部分:DPDK插件的初始化和管理

  • 第4章:模块初始化

    • 4.1 插件注册和入口(VLIB_PLUGIN_REGISTER)
    • 4.2 DPDK EAL初始化(dpdk_lib_init)
    • 4.3 DPDK设备发现和枚举
    • 4.4 缓冲区池创建(dpdk_buffer_pool_init)
    • 4.5 设备接口创建(vnet_eth_register_interface)
    • 4.6 RX/TX队列分配和配置(一起分配)
    • 4.7 dpdk-input节点注册
    • 4.8 dpdk-output节点注册(VNET_DEVICE_CLASS)
    • 4.9 线程初始化(dpdk_worker_thread_init)
    • 4.10 后台进程注册(dpdk_process、admin_up_down_process)
  • 第5章:DPDK设备管理

    • 5.1 设备发现和枚举
    • 5.2 设备配置(队列数、描述符数等)
    • 5.3 设备设置(dpdk_device_setup)
    • 5.4 RX/TX硬件卸载配置(offload配置)
    • 5.5 设备启动和停止(dpdk_device_start/stop)
    • 5.6 设备状态管理(ADMIN_UP标志)
    • 5.7 MAC地址管理(添加/删除MAC地址)
    • 5.8 链路状态管理(dpdk_update_link_state)
    • 5.9 统计信息管理(dpdk_update_counters)
    • 5.10 子接口(Sub-interface)和VLAN管理
    • 5.11 中断模式配置(dpdk_setup_interrupts)
    • 5.12 admin_up_down_process后台进程
    • 5.13 dpdk_process后台进程(统计和链路状态轮询)
  • 第6章:驱动管理

    • 6.1 驱动匹配机制
    • 6.2 驱动特性配置
    • 6.3 支持的驱动列表
    • 6.4 驱动特定优化
  • 第7章:缓冲区管理

    • 7.1 DPDK mbuf与VPP buffer的转换
    • 7.2 缓冲区池(mempool)的创建和管理
    • 7.3 缓冲区模板(buffer template)的使用
    • 7.4 缓冲区预取(prefetch)优化
    • 7.5 内存布局和兼容性
    • 7.6 mbuf验证(dpdk_validate_rte_mbuf)

第四部分:数据包接收(Input)

  • 第8章:dpdk-input节点核心处理

    • 8.1 dpdk-input节点的注册和类型
    • 8.2 节点的主要处理函数(dpdk_input_node)
    • 8.3 轮询向量(poll vector)的获取
    • 8.4 多设备轮询机制
    • 8.5 节点在VLIB图中的位置
  • 第9章:数据包接收处理

    • 9.1 dpdk_device_input函数详解
    • 9.2 rte_eth_rx_burst调用和批量接收
    • 9.3 dpdk_process_rx_burst函数处理接收的数据包
    • 9.4 mbuf到vlib_buffer的转换
    • 9.5 数据包元数据设置(sw_if_index、flags等)
    • 9.6 缓冲区模板的应用
  • 第10章:硬件卸载处理(接收侧)

    • 10.1 DPDK硬件卸载标志(ol_flags)的提取
    • 10.2 IP校验和卸载(IP checksum offload)
    • 10.3 L4校验和卸载(L4 checksum offload)
    • 10.4 VLAN处理
    • 10.5 RSS哈希处理
    • 10.6 硬件卸载标志的传递和使用
  • 第11章:多段数据包处理

    • 11.1 多段数据包(multi-segment packet)的识别
    • 11.2 dpdk_process_subseq_segs函数处理后续段
    • 11.3 缓冲区链的构建
    • 11.4 VLIB_BUFFER_NEXT_PRESENT标志的使用
    • 11.5 总长度计算
  • [第12章:流表处理(Flow Offload)](#第12章:流表处理(Flow Offload))

    • 12.1 Flow Offload的概念和作用
    • 12.2 dpdk_process_flow_offload函数处理流表
    • 12.3 FDIR(Flow Director)标志的处理
    • 12.4 流表查找和下一跳选择
    • 12.5 流ID和buffer advance的处理
  • [第13章:LRO处理(Large Receive Offload)](#第13章:LRO处理(Large Receive Offload))

    • 13.1 LRO的概念和作用
    • 13.2 dpdk_process_lro_offload函数处理LRO
    • 13.3 GSO(Generic Segmentation Offload)标志的设置
    • 13.4 L4头部大小的计算(dpdk_lro_find_l4_hdr_sz)
    • 13.5 GSO相关元数据的设置
  • 第14章:数据包分发和下一跳选择

    • 14.1 下一跳节点的选择机制(默认ethernet-input)
    • 14.2 默认下一跳:ethernet-input节点
    • 14.3 ethernet-input的优化标志(ETH_INPUT_FRAME_F_IP4_CKSUM_OK)
    • 14.4 Feature Arc的处理(vnet_feature_start_device_input)
    • 14.5 每接口下一跳索引(per_interface_next_index)重定向
    • 14.6 Flow Offload的每包下一跳选择
    • 14.7 数据包到下一节点的入队机制
    • 14.8 单一下一跳vs多下一跳的处理

第五部分:数据包发送(Output)

  • 第15章:dpdk-output节点核心处理

    • 15.1 dpdk-output节点的注册和类型
    • 15.2 节点的主要处理函数(VNET_DEVICE_CLASS_TX_FN)
    • 15.3 发送队列的选择机制
    • 15.4 节点在VLIB图中的位置
    • 15.5 Input和Output的协同工作
  • 第16章:数据包发送处理

    • 16.1 tx_burst_vector_internal函数详解
    • 16.2 rte_eth_tx_burst调用和批量发送
    • 16.3 vlib_buffer到mbuf的转换
    • 16.4 发送队列锁定机制
    • 16.5 批量发送优化
  • 第17章:硬件卸载处理(发送侧)

    • 17.1 dpdk_buffer_tx_offload函数详解
    • 17.2 TX硬件卸载标志的设置
    • 17.3 IP校验和卸载
    • 17.4 L4校验和卸载
    • 17.5 TSO(TCP Segmentation Offload)处理
    • 17.6 VXLAN隧道卸载
    • 17.7 头部长度计算(l2_len、l3_len、l4_len)
  • 第18章:发送队列管理

    • 18.1 发送队列的分配和配置
    • 18.2 队列与线程的绑定关系
    • 18.3 共享队列和独占队列
    • 18.4 队列锁定机制
    • 18.5 发送失败处理和mbuf释放
    • 18.6 mbuf验证(dpdk_validate_rte_mbuf)

第六部分:高级功能和优化

  • [第19章:流表管理(Flow Offload)](#第19章:流表管理(Flow Offload))

    • 19.1 Flow Offload流表的创建和删除
    • 19.2 流表条目的管理
    • 19.3 流表匹配结果的处理
    • 19.4 VPP流表规则到DPDK流表规则的转换
  • 第20章:统计和计数

    • 20.1 接收数据包和字节数的统计
    • 20.2 发送数据包和字节数的统计
    • 20.3 接口计数器的更新
    • 20.4 DPDK统计信息的获取(dpdk_update_counters)
    • 20.5 XSTATS统计的处理
    • 20.6 统计更新的时机和频率
  • 第21章:线程和队列管理

    • 21.1 多线程支持机制
    • 21.2 每线程数据结构(per_thread_data)
    • 21.3 接收队列(RX queue)的分配和管理
    • 21.4 发送队列(TX queue)的分配和管理
    • 21.5 队列与线程的绑定关系
    • 21.6 轮询向量(poll vector)的构建
    • 21.7 RSS(Receive Side Scaling)配置
    • 21.8 RSS队列配置(dpdk_interface_set_rss_queues)
    • 21.9 RETA(Redirection Table)配置
  • 第22章:性能优化

    • 22.1 批量处理优化(burst processing)
    • 22.2 预取(prefetch)优化
    • 22.3 缓冲区模板优化
    • 22.4 向量化处理
    • 22.5 缓存行对齐
    • 22.6 分支预测优化(PREDICT_TRUE/PREDICT_FALSE)
    • 22.7 零拷贝技术
  • 第23章:错误处理

    • 23.1 DPDK错误类型定义
    • 23.2 错误处理机制
    • 23.3 错误统计和报告
    • 23.4 数据包丢弃的原因和处理
    • 23.5 设备错误恢复

第七部分:管理和接口

第八部分:总结

  • 第26章:DPDK插件总结
    • 26.1 DPDK插件的关键特点
    • 26.2 在VPP数据包转发中的作用
    • 26.3 性能优化要点
    • 26.4 与其他模块的关系
    • 26.5 最佳实践和注意事项

第1章:DPDK插件概述

1.1 DPDK插件在VPP中的作用和意义

作用和实现原理:

DPDK插件是VPP中负责与DPDK(Data Plane Development Kit)库集成的核心插件,它充当了VPP与物理网卡之间的"桥梁"。就像"快递分拣中心"需要"收货窗口"和"发货窗口"一样,DPDK插件提供了高性能的"数据包接收窗口"(dpdk-input)和"数据包发送窗口"(dpdk-output),让VPP能够直接操作物理网卡,实现高性能的数据包转发。

通俗理解:

想象一下VPP是一个大型的"快递分拣中心":

  • DPDK插件 = "专业快递窗口":提供高性能的"收货"和"发货"服务
  • DPDK库 = "专业快递公司":提供专业的"快递车"(网卡驱动)和"运输服务"
  • 物理网卡 = "快递车":实际运输"包裹"(数据包)的"车辆"
  • 数据包 = "包裹":需要分拣和转发的"快递包裹"

DPDK插件的核心价值:

  1. 高性能数据包处理:

    • 利用DPDK的用户态驱动,绕过Linux内核,减少系统调用开销
    • 实现零拷贝技术,数据包直接从网卡DMA到用户态内存
    • 批量处理(burst),一次处理多个数据包,提高CPU缓存命中率
  2. 硬件卸载支持:

    • IP校验和卸载:硬件计算IP校验和,减轻CPU负担
    • L4校验和卸载:硬件计算TCP/UDP校验和
    • TSO(TCP Segmentation Offload):硬件分片,提高大包发送效率
    • LRO(Large Receive Offload):硬件合并,减少小包处理开销
    • RSS(Receive Side Scaling):硬件负载均衡,多队列分发
  3. 生产环境就绪:

    • 支持主流网卡(Intel、Mellanox等)
    • 支持多线程、多队列
    • 支持统计和监控
    • 支持Flow Offload等高级功能

源码证据 (src/plugins/dpdk/main.c:88):

c 复制代码
VLIB_PLUGIN_REGISTER () = {
    .version = VPP_BUILD_VER,
    .description = "Data Plane Development Kit (DPDK)",
};

这段代码表明DPDK插件是VPP的一个标准插件,通过VLIB_PLUGIN_REGISTER宏注册到VPP系统中。


1.2 DPDK插件与DPDK库的关系

作用和实现原理:

DPDK插件是VPP与DPDK库之间的"适配层",它封装了DPDK的接口,将DPDK的功能集成到VPP的转发图中。DPDK插件不直接操作硬件,而是通过DPDK库提供的接口来操作硬件。

DPDK插件与DPDK库的关系图:

复制代码
┌─────────────────────────────────────────────────────────────┐
│                        VPP系统                                │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  VLIB转发图                                           │  │
│  │  ┌──────────────────────────────────────────────┐  │  │
│  │  │  dpdk-input节点                               │  │  │
│  │  │  ┌────────────────────────────────────────┐ │  │  │
│  │  │  │  DPDK插件(适配层)                      │ │  │  │
│  │  │  │  - 封装DPDK接口                          │ │  │  │
│  │  │  │  - 转换数据格式                          │ │  │  │
│  │  │  │  - 集成到VPP转发图                       │ │  │  │
│  │  │  └────────────────────────────────────────┘ │  │  │
│  │  └──────────────┬───────────────────────────────┘  │  │
│  └─────────────────┼──────────────────────────────────┘  │
└─────────────────────┼──────────────────────────────────────┘
                      │
                      │ 调用DPDK API
                      ▼
┌─────────────────────────────────────────────────────────────┐
│                        DPDK库                                │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  DPDK API接口                                        │  │
│  │  - rte_eth_rx_burst()  (接收数据包)                 │  │
│  │  - rte_eth_tx_burst()  (发送数据包)                 │  │
│  │  - rte_eth_dev_configure()  (配置设备)              │  │
│  │  - rte_mempool_*()  (内存池管理)                    │  │
│  │  - rte_flow_*()  (流表管理)                         │  │
│  └──────────────┬───────────────────────────────────────┘  │
│                 │                                           │
│  ┌──────────────┼───────────────────────────────────────┐  │
│  │  PMD (Poll Mode Driver)                            │  │
│  │  - 用户态网卡驱动                                    │  │
│  │  - 零拷贝技术                                        │  │
│  │  - 批量处理                                         │  │
│  └──────────────┬───────────────────────────────────────┘  │
└─────────────────┼──────────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────────────────────────┐
│                    硬件网卡                                   │
│  - Intel 82599/X710                                        │
│  - Mellanox ConnectX                                       │
│  - 其他支持DPDK的网卡                                        │
└─────────────────────────────────────────────────────────────┘

DPDK插件如何使用DPDK库:

  1. 初始化阶段:

    • 调用rte_eal_init()初始化DPDK的EAL(Environment Abstraction Layer)
    • 调用rte_eth_dev_configure()配置DPDK设备
    • 调用rte_mempool_create()创建内存池
  2. 数据包接收阶段:

    • 调用rte_eth_rx_burst()批量接收数据包
    • 获取DPDK的mbuf结构体
    • 转换为VPP的buffer格式
  3. 数据包发送阶段:

    • 将VPP的buffer转换为DPDK的mbuf格式
    • 调用rte_eth_tx_burst()批量发送数据包
  4. 设备管理阶段:

    • 调用rte_eth_dev_start()启动设备
    • 调用rte_eth_dev_stop()停止设备
    • 调用rte_eth_stats_get()获取统计信息

通俗理解:

就像"快递分拣中心"与"快递公司"的关系:

  • DPDK库 = "快递公司":提供专业的"快递车"(网卡驱动)和"运输服务"(数据包接收/发送)
  • DPDK插件 = "分拣中心的快递窗口":使用"快递公司"的服务,接收和发送"包裹"(数据包)
  • VPP = "分拣中心":对接收到的"包裹"进行分拣和转发

DPDK插件不直接操作硬件,而是通过DPDK库提供的接口来操作硬件,这样既利用了DPDK的高性能,又保持了VPP的模块化设计。

关键特点:

  • 封装性:DPDK插件封装了DPDK的复杂性,为VPP提供统一的接口
  • 转换性:DPDK插件负责将DPDK的mbuf格式转换为VPP的buffer格式
  • 集成性:DPDK插件将DPDK的功能集成到VPP的转发图中
  • 可扩展性:DPDK插件支持DPDK的新功能和特性

1.3 DPDK插件在VPP数据包转发流程中的位置

作用和实现原理:

DPDK插件位于VPP数据包转发流程的最前端(接收)和最末端(发送),是数据包进入和离开VPP系统的"门户"。它属于VLIB的INPUT节点类型 (接收)和设备类TX函数(发送),在VLIB的调度机制中,INPUT节点具有最高优先级,会被优先调度执行。

VPP数据包转发流程图:

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    VPP数据包转发流程                          │
└─────────────────────────────────────────────────────────────┘

┌──────────────┐
│  物理网卡     │
│ (DPDK管理)   │
└──────┬───────┘
       │ 数据包接收
       ▼
┌─────────────────────────────────────────────────────────────┐
│  INPUT节点层(最高优先级)                                    │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  dpdk-input节点                                       │  │
│  │  - 从DPDK接收数据包                                    │  │
│  │  - mbuf → buffer转换                                   │  │
│  │  - 设置元数据                                          │  │
│  │  - 硬件卸载处理                                        │  │
│  └──────────────┬───────────────────────────────────────┘  │
└─────────────────┼──────────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────────────────────────┐
│  Feature Arc层                                              │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  device-input feature arc                           │  │
│  │  - worker-handoff (线程切换)                        │  │
│  │  - span-input (端口镜像)                            │  │
│  │  - l2-patch (L2补丁)                                │  │
│  └──────────────┬───────────────────────────────────────┘  │
└─────────────────┼──────────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────────────────────────┐
│  协议处理层                                                  │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  ethernet-input节点                                  │  │
│  │  └─> ip4-input / ip6-input节点                      │  │
│  │      └─> ip4-lookup / ip6-lookup节点                │  │
│  │          └─> ip4-rewrite / ip6-rewrite节点          │  │
│  │              └─> interface-output节点               │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────┼──────────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────────────────────────┐
│  设备输出层                                                  │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  dpdk-output (VNET_DEVICE_CLASS_TX_FN)              │  │
│  │  - buffer → mbuf转换                                   │  │
│  │  - 硬件卸载处理                                        │  │
│  │  - 批量发送数据包                                      │  │
│  └──────────────┬───────────────────────────────────────┘  │
└─────────────────┼──────────────────────────────────────────┘
                  │
                  ▼
┌──────────────┐
│  物理网卡     │
│ (DPDK管理)   │
└──────────────┘

VLIB节点调度机制:

复制代码
VLIB调度器
    │
    ├─ INPUT节点(最高优先级)
    │   ├─ dpdk-input
    │   ├─ memif-input
    │   ├─ af-packet-input
    │   └─ ...
    │
    ├─ PROCESS节点(进程节点)
    │   └─ dpdk-process (统计、链路状态轮询)
    │
    └─ INTERNAL节点(内部节点)
        ├─ ethernet-input
        ├─ ip4-input
        ├─ ip4-lookup
        ├─ interface-output
        └─ ...

dpdk-input节点的位置:

  1. 最前端:数据包进入VPP的第一个节点
  2. INPUT类型 :VLIB_NODE_TYPE_INPUT,具有最高调度优先级
  3. device-input的兄弟节点 :.sibling_of = "device-input",与其他设备输入节点共享相同的下一跳节点

dpdk-output节点的位置:

  1. 最末端:数据包离开VPP的最后一个节点
  2. 设备类TX函数 :VNET_DEVICE_CLASS_TX_FN,由interface-output节点调用
  3. 设备类注册 :通过VNET_DEVICE_CLASS宏注册到VPP设备系统

源码证据 (src/plugins/dpdk/device/node.c:563):

c 复制代码
VLIB_REGISTER_NODE (dpdk_input_node) = {
  .type = VLIB_NODE_TYPE_INPUT,
  .name = "dpdk-input",
  .sibling_of = "device-input",
  .flags = VLIB_NODE_FLAG_TRACE_SUPPORTED,
  .state = VLIB_NODE_STATE_DISABLED,
  // ...
};

源码证据 (src/plugins/dpdk/device/device.c:726):

c 复制代码
VNET_DEVICE_CLASS (dpdk_device_class) = {
  .name = "dpdk",
  .tx_function_n_errors = DPDK_TX_FUNC_N_ERROR,
  .tx_function_error_strings = dpdk_tx_func_error_strings,
  // ...
};

通俗理解:

就像"快递分拣中心"的流程:

  • dpdk-input = "收货窗口":第一个接触"包裹"的地方,负责从"快递车"(网卡)接收"包裹"(数据包)
  • device-input feature arc = "初步检查区":检查"包裹"是否需要特殊处理
  • ethernet-input = "分拣区":根据"包裹"类型(以太网)进行分拣
  • ip4-input / ip6-input = "详细分拣区":根据"包裹"内容(IP地址)进行详细分拣
  • ip4-lookup = "路由查询区":查询"包裹"应该发送到哪里
  • ip4-rewrite = "打包区":重新打包"包裹"准备发送
  • dpdk-output = "发货窗口":最后一个接触"包裹"的地方,负责将"包裹"发送到"快递车"(网卡)

1.4 DPDK插件的主要功能概述

作用和实现原理:

DPDK插件的主要功能可以概括为"接收、转换、处理、发送"四个核心步骤,同时提供设备管理、统计监控等辅助功能。

功能架构图:

复制代码
┌─────────────────────────────────────────────────────────────┐
│              DPDK插件功能架构                                  │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│  1. 数据包接收(Input)                                       │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  - 轮询DPDK设备队列                                   │  │
│  │  - 批量接收数据包(rte_eth_rx_burst)                │  │
│  │  - mbuf → buffer转换                                   │  │
│  │  - 设置buffer元数据                                    │  │
│  │  - 提取硬件卸载标志                                    │  │
│  │  - 处理多段数据包                                      │  │
│  │  - Flow Offload处理                                  │  │
│  │  - LRO处理                                           │  │
│  │  - 分发到下一跳节点                                   │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────┬──────────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────────────────────────┐
│  2. 数据包发送(Output)                                      │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  - buffer → mbuf转换                                   │  │
│  │  - 设置硬件卸载标志                                    │  │
│  │  - TSO处理                                           │  │
│  │  - 批量发送数据包(rte_eth_tx_burst)                │  │
│  │  - 发送失败处理                                       │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────┬──────────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────────────────────────┐
│  3. 设备管理                                                 │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  - 设备发现和枚举                                     │  │
│  │  - 设备配置(队列数、描述符数等)                     │  │
│  │  - 设备启动和停止                                     │  │
│  │  - MAC地址管理                                       │  │
│  │  - 链路状态管理                                       │  │
│  │  - 子接口和VLAN管理                                  │  │
│  │  - 中断模式配置                                       │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────┬──────────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────────────────────────┐
│  4. 高级功能                                                 │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  - Flow Offload流表管理                              │  │
│  │  - RSS配置                                           │  │
│  │  - 统计和计数                                        │  │
│  │  - 加密设备支持(Cryptodev)                         │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

主要功能详解:

1. 数据包接收(Input)
  • 批量接收 :使用rte_eth_rx_burst()一次接收多个数据包(最多32个)
  • 格式转换 :将DPDK的rte_mbuf转换为VPP的vlib_buffer_t
  • 元数据设置:设置接收接口索引、发送接口索引、buffer标志等
  • 硬件卸载处理:提取和处理硬件计算的校验和、VLAN、RSS等
  • 多段数据包处理:处理跨多个mbuf的数据包
  • Flow Offload处理:处理硬件流表匹配结果
  • LRO处理:处理硬件合并的大数据包
2. 数据包发送(Output)
  • 格式转换 :将VPP的vlib_buffer_t转换为DPDK的rte_mbuf
  • 硬件卸载设置:设置IP校验和、L4校验和、TSO等硬件卸载标志
  • 批量发送 :使用rte_eth_tx_burst()一次发送多个数据包
  • 发送失败处理:处理发送失败的数据包,释放mbuf
3. 设备管理
  • 设备发现:发现和枚举DPDK设备
  • 设备配置:配置队列数、描述符数、RSS等
  • 设备启动/停止:启动和停止DPDK设备
  • MAC地址管理:添加、删除、修改MAC地址
  • 链路状态管理:轮询和更新链路状态
  • 子接口管理:创建和管理VLAN子接口
  • 中断模式:配置中断模式(轮询/中断)
4. 高级功能
  • Flow Offload:硬件流表管理,支持流表规则创建和删除
  • RSS配置:配置RSS哈希函数和队列分配
  • 统计和计数:收集和更新接口统计信息
  • 加密设备支持:支持DPDK的加密设备(Cryptodev)

通俗理解:

就像"快递分拣中心"的工作流程:

  1. 接收:从"快递车"(DPDK)接收"包裹"(数据包)
  2. 转换:将"包裹"从"快递公司格式"(mbuf)转换为"分拣中心格式"(buffer)
  3. 处理:检查"包裹"的"标签"(硬件卸载标志),看是否需要特殊处理
  4. 发送:将"包裹"从"分拣中心格式"(buffer)转换为"快递公司格式"(mbuf),发送到"快递车"(DPDK)

同时,"分拣中心"还需要管理"快递车"(设备管理)、统计"包裹"数量(统计和计数)、提供"VIP通道"(Flow Offload)等服务。


1.5 与其他输入/输出模块的对比

作用和实现原理:

VPP中有多个类似的数据输入/输出模块,它们都是device-input的兄弟节点(sibling)或设备类(device class),具有相同的下一跳节点或接口,但使用不同的底层技术来接收和发送数据包。

VPP数据输入/输出模块架构图:

复制代码
┌─────────────────────────────────────────────────────────────┐
│              VPP数据输入/输出模块架构                           │
└─────────────────────────────────────────────────────────────┘

                    device-input (父节点)
                            │
        ┌───────────────────┼───────────────────┐
        │                   │                   │
        ▼                   ▼                   ▼
┌───────────────┐   ┌───────────────┐   ┌───────────────┐
│  dpdk-input   │   │  memif-input  │   │af-packet-input│
│  (DPDK网卡)   │   │  (内存接口)   │   │ (AF_PACKET)   │
└───────┬───────┘   └───────┬───────┘   └───────┬───────┘
        │                   │                   │
        └───────────────────┼───────────────────┘
                            │
                    (共享下一跳节点)
                            │
                            ▼
                    ethernet-input




                        数据输出
        ┌───────────────────┼───────────────────┐
        │                   │                   │
        ▼                   ▼                   ▼
┌───────────────┐   ┌───────────────┐   ┌───────────────┐
│ dpdk-output   │   │ memif-output  │   │af-packet-output│
│ (DPDK网卡)    │   │ (内存接口)    │   │ (AF_PACKET)   │
└───────────────┘   └───────────────┘   └───────────────┘

主要数据输入/输出模块对比表:

模块名称 节点名称 底层技术 主要用途 性能特点 适用场景
DPDK插件 dpdk-input dpdk-output DPDK用户态驱动 高性能物理网卡 ⭐⭐⭐⭐⭐ 最高性能、零拷贝、硬件卸载 生产环境、NFV、高性能转发
Memif memif-input memif-output 共享内存 容器间通信、VM间通信 ⭐⭐⭐⭐⭐ 零拷贝、低延迟、高吞吐 Kubernetes、Docker、VM迁移
AF_PACKET af-packet-input af-packet-output Linux AF_PACKET Linux TAP接口 ⭐⭐⭐ Linux原生支持、易于使用 开发测试、Linux环境
AF_XDP af-xdp-input af-xdp-output Linux AF_XDP 高性能Linux接口 ⭐⭐⭐⭐ Linux XDP技术、高性能 Linux环境下的高性能转发
RDMA rdma-input rdma-output RDMA (InfiniBand) RDMA网卡 ⭐⭐⭐⭐⭐ 超低延迟、RDMA技术 HPC、超低延迟应用
Virtio virtio-input virtio-output Virtio 虚拟化环境 ⭐⭐⭐ 虚拟化标准、通用性强 KVM、QEMU、虚拟化环境
Vhost-User vhost-user-input vhost-user-output Vhost-User QEMU/KVM ⭐⭐⭐⭐ 用户态vhost、高性能 虚拟化环境、NFV
TUN/TAP tuntap-input tuntap-output Linux TUN/TAP TUN/TAP接口 ⭐⭐ 简单易用、通用性强 开发测试、Linux环境

各模块的特点和用途:

1. DPDK插件(本文档主题)

特点:

  • 高性能:利用DPDK用户态驱动,绕过内核
  • 零拷贝:数据包直接从网卡DMA到用户态内存
  • 硬件卸载:支持IP校验和、L4校验和、TSO、LRO等
  • 批量处理:一次处理多个数据包,提高效率
  • 多线程支持:支持多线程、多队列

适用场景:

  • 生产环境中的高性能物理网卡接收/发送
  • 数据中心、NFV、高性能转发
  • 需要硬件卸载的场景

源码证据 (src/plugins/dpdk/device/node.c:370):

c 复制代码
n = rte_eth_rx_burst (xd->port_id, queue_id, ptd->mbufs + n_rx_packets,
                      n_to_rx);
2. Memif

特点:

  • 零拷贝:使用共享内存,无需数据拷贝
  • 低延迟:直接内存访问,延迟极低
  • 高吞吐:适合容器间、VM间通信

适用场景:

  • Kubernetes、Docker容器间通信
  • VM间通信
  • 需要零拷贝的场景
3. AF_PACKET

特点:

  • Linux原生支持:使用Linux的AF_PACKET套接字
  • 易于使用:配置简单,适合开发测试
  • 通用性强:支持所有Linux网卡

适用场景:

  • 开发测试环境
  • Linux环境下的简单应用
  • 不需要极致性能的场景
4. AF_XDP

特点:

  • Linux XDP技术:利用Linux的XDP(eXpress Data Path)
  • 高性能:比AF_PACKET性能更高
  • 需要Linux内核支持

适用场景:

  • Linux环境下的高性能转发
  • 需要比AF_PACKET更高性能的场景
5. RDMA

特点:

  • 超低延迟:RDMA技术,延迟极低
  • InfiniBand支持:支持InfiniBand网卡
  • 专用硬件:需要RDMA网卡

适用场景:

  • HPC(高性能计算)环境
  • 超低延迟应用
  • 需要RDMA的场景
6. Virtio

特点:

  • 虚拟化标准:Virtio是虚拟化标准
  • 通用性强:支持所有Virtio设备
  • 易于集成:与虚拟化环境集成简单

适用场景:

  • KVM、QEMU虚拟化环境
  • 虚拟化环境下的网络转发
  • 需要虚拟化支持的场景
7. Vhost-User

特点:

  • 用户态vhost:用户态实现的vhost
  • 高性能:比内核vhost性能更高
  • QEMU/KVM支持:与QEMU/KVM集成良好

适用场景:

  • 虚拟化环境下的高性能转发
  • NFV场景
  • 需要用户态vhost的场景

模块选择建议:

  • 生产环境高性能 :使用DPDK插件
  • 容器/VM通信 :使用Memif
  • Linux开发测试 :使用AF_PACKET 或AF_XDP
  • 虚拟化环境 :使用Virtio 或Vhost-User
  • 超低延迟 :使用RDMA

通俗理解:

就像"快递分拣中心"有多种"收货窗口"和"发货窗口":

  • DPDK插件 = "专业快递窗口":接收/发送"专业快递公司"(DPDK网卡)的"包裹",性能最高
  • Memif = "内部快递窗口":接收/发送"内部快递"(共享内存)的"包裹",零拷贝、低延迟
  • AF_PACKET = "普通快递窗口":接收/发送"普通快递"(Linux接口)的"包裹",简单易用
  • Virtio = "虚拟快递窗口":接收/发送"虚拟快递"(虚拟化环境)的"包裹",适合虚拟化

虽然"收货窗口"和"发货窗口"不同,但"包裹"进入"分拣中心"后,都会经过相同的"分拣流程"(device-input feature arc → ethernet-input → ...),最后通过相应的"发货窗口"发送出去。


1.6 本章总结

DPDK插件的关键特点:

  1. 位置:VPP数据包转发流程的最前端(接收)和最末端(发送)
  2. 作用:从DPDK管理的物理网卡接收/发送数据包,转换为VPP格式
  3. 关系:VPP与DPDK库之间的"桥梁",封装DPDK接口
  4. 功能:接收、转换、硬件卸载处理、发送、设备管理、统计监控
  5. 同类模块:与其他10+个输入/输出模块共享相同的接口,但性能最高

在VPP架构中的重要性:

DPDK插件是VPP高性能数据包转发的"基石",它:

  • 提供了高性能的数据包接收/发送能力
  • 支持硬件卸载,减轻CPU负担
  • 支持多线程、多队列,实现高并发
  • 与其他输入/输出模块统一接口,保持架构一致性
  • 是生产环境中最常用的数据包输入/输出模块

后续章节预告:

  • 第2章:详细介绍模块的文件组织和架构
  • 第3章:深入讲解核心数据结构
  • 第4章:详细分析模块初始化流程
  • 第8-14章:详细讲解数据包接收的每个步骤
  • 第15-18章:详细讲解数据包发送的每个步骤

相关源码文件:

  • src/plugins/dpdk/main.c - 插件入口
  • src/plugins/dpdk/device/dpdk.h - 模块头文件
  • src/plugins/dpdk/device/node.c - input节点实现
  • src/plugins/dpdk/device/device.c - output节点实现

第2章:模块架构和文件组织

2.1 DPDK插件的文件组织结构

作用和实现原理:

DPDK插件采用分层、模块化的文件组织结构,将不同功能划分到不同的文件中,便于维护和扩展。整个模块就像一个"大型工厂",每个文件负责不同的"生产环节"。

文件组织结构图:

复制代码
┌─────────────────────────────────────────────────────────────┐
│              DPDK插件文件组织结构                             │
└─────────────────────────────────────────────────────────────┘

src/plugins/dpdk/
│
├── main.c                    [插件入口层]
│   └── 插件注册、延迟函数注册
│
├── buffer.c / buffer.h       [缓冲区管理层]
│   └── DPDK mbuf与VPP buffer的转换
│
├── device/                   [设备管理层]
│   ├── dpdk.h                [公共头文件层]
│   │   └── 数据结构定义、公共接口
│   │
│   ├── dpdk_priv.h           [私有头文件层]
│   │   └── 私有数据结构、内部宏定义
│   │
│   ├── init.c                [初始化层]
│   │   └── DPDK EAL初始化、设备发现、接口创建
│   │
│   ├── node.c                [数据平面层 - Input]
│   │   └── dpdk-input节点实现(核心)
│   │
│   ├── device.c              [数据平面层 - Output]
│   │   └── dpdk-output节点实现、设备管理
│   │
│   ├── common.c              [设备管理层]
│   │   └── 设备设置、启动/停止、中断配置
│   │
│   ├── driver.c              [驱动管理层]
│   │   └── 驱动匹配、驱动特性配置
│   │
│   ├── flow.c                [流表管理层]
│   │   └── Flow Offload流表管理
│   │
│   ├── format.c              [格式化输出层]
│   │   └── 调试信息格式化输出
│   │
│   └── cli.c                 [CLI命令层]
│       └── 命令行接口实现
│
└── cryptodev/                [加密设备层 - 可选]
    ├── cryptodev.c
    ├── cryptodev_op_data_path.c
    └── cryptodev_raw_data_path.c

文件统计:

  • 核心文件:13个(不包括cryptodev)
  • 头文件:3个(dpdk.h、dpdk_priv.h、buffer.h)
  • 实现文件:10个(main.c、buffer.c、device目录下的8个.c文件)
  • 可选模块:cryptodev(加密设备支持)

通俗理解:

就像"快递分拣中心"的组织结构:

  • main.c = "总调度室":负责整个"分拣中心"的启动和协调
  • buffer.c/h = "包裹转换区":将"快递公司格式"(mbuf)转换为"分拣中心格式"(buffer)
  • device/ = "设备管理区":管理各种"收货设备"(网卡)
    • init.c = "设备安装区":安装和配置"收货设备"
    • node.c = "收货窗口":实际接收"包裹"的地方(核心)
    • device.c = "发货窗口":实际发送"包裹"的地方(核心)
    • common.c = "设备维护区":维护"收货设备"的状态
    • driver.c = "司机管理区":管理"快递司机"(驱动)
    • flow.c = "快速通道":管理"VIP通道"(Flow Offload)
    • format.c = "信息展示区":展示"分拣中心"的运行信息
    • cli.c = "客服窗口":提供"客户服务"(命令行接口)

2.2 各文件的功能和职责

作用和实现原理:

每个文件都有明确的职责分工,遵循"单一职责原则",便于代码维护和功能扩展。下面按照"从入口到核心,从核心到辅助"的顺序详细介绍各文件。

2.2.1 插件入口层

main.c - 插件主入口

功能:

  • 插件注册(VLIB_PLUGIN_REGISTER)
  • DPDK延迟函数覆盖(rte_delay_us_override)
  • 插件初始化(dpdk_main_init)

关键代码 (src/plugins/dpdk/main.c:88):

c 复制代码
VLIB_PLUGIN_REGISTER () = {
    .version = VPP_BUILD_VER,
    .description = "Data Plane Development Kit (DPDK)",
};

关键代码 (src/plugins/dpdk/main.c:73):

c 复制代码
static clib_error_t * dpdk_main_init (vlib_main_t * vm)
{
  clib_error_t * error = 0;
  
  /* register custom delay function */
  rte_delay_us_callback_register (rte_delay_us_override_cb);
  
  return error;
}

职责:

  • 向VPP注册DPDK插件
  • 提供插件版本和描述信息
  • 注册自定义的延迟函数,优化DPDK在VPP中的延迟行为

通俗理解:就像"快递分拣中心"的"总调度室",负责整个"分拣中心"的启动和协调。

依赖关系:

  • 依赖:dpdk/device/dpdk.h(设备相关接口)
  • 被依赖:无(这是入口文件)

2.2.2 缓冲区管理层

buffer.c / buffer.h - 缓冲区管理

功能:

  • DPDK mbuf与VPP buffer的转换
  • 内存池(mempool)的创建和管理
  • mbuf模板的创建

关键功能:

  • dpdk_buffer_pool_init:初始化缓冲区池
  • vlib_buffer_from_rte_mbuf:从mbuf获取buffer指针
  • rte_mbuf_from_vlib_buffer:从buffer获取mbuf指针

关键代码 (src/plugins/dpdk/buffer.h:19):

c 复制代码
#define rte_mbuf_from_vlib_buffer(x) (((struct rte_mbuf *)x) - 1)
#define vlib_buffer_from_rte_mbuf(x) ((vlib_buffer_t *)(x+1))

内存布局:

复制代码
┌─────────────────────────────────────────┐
│  rte_mbuf (DPDK)                        │
│  └── 元数据(128字节)                    │
├─────────────────────────────────────────┤
│  vlib_buffer_t (VPP)                   │
│  └── 元数据(128字节)                    │
├─────────────────────────────────────────┤
│  数据区域                                │
│  └── 实际数据包数据                       │
└─────────────────────────────────────────┘

职责:

  • 管理DPDK和VPP之间的缓冲区转换
  • 确保mbuf和buffer的内存布局兼容
  • 提供缓冲区池的创建和管理接口

通俗理解:就像"包裹转换区",负责将"快递公司格式"(mbuf)转换为"分拣中心格式"(buffer)。

依赖关系:

  • 依赖:rte_mbuf.h、rte_mempool.h(DPDK库)、vlib/buffer.h(VPP)
  • 被依赖:device/node.c、device/device.c、device/init.c、device/common.c

2.2.3 设备管理层 - 公共头文件

device/dpdk.h - 公共头文件

功能:

  • 定义公共数据结构(dpdk_device_t、dpdk_rx_queue_t、dpdk_tx_queue_t等)
  • 定义公共接口函数声明
  • 定义设备标志(flags)枚举
  • 导出模块的公共符号

关键数据结构:

  • dpdk_device_t:DPDK设备结构体(包含RX和TX队列)
  • dpdk_rx_queue_t:接收队列结构体
  • dpdk_tx_queue_t:发送队列结构体
  • dpdk_per_thread_data_t:每线程数据结构体
  • dpdk_main_t:DPDK主结构体
  • dpdk_flow_entry_t:流表条目结构体

关键代码 (src/plugins/dpdk/device/dpdk.h:57):

c 复制代码
extern vnet_device_class_t dpdk_device_class;
extern vlib_node_registration_t dpdk_input_node;
extern vlib_node_registration_t admin_up_down_process_node;

职责:

  • 提供模块的公共接口
  • 定义模块间交互的数据结构
  • 导出模块的公共函数

通俗理解:就像"分拣中心"的"公共规范",定义了各个"部门"之间交互的"标准格式"。

依赖关系:

  • 依赖:vnet/devices/devices.h(VPP设备接口)、rte_ethdev.h(DPDK库)
  • 被依赖:所有device目录下的.c文件

device/dpdk_priv.h - 私有头文件

功能:

  • 定义私有数据结构
  • 定义内部宏和常量
  • 定义内部辅助函数

关键内容:

  • DPDK_NB_RX_DESC_DEFAULT:默认RX描述符数(1024)
  • DPDK_NB_TX_DESC_DEFAULT:默认TX描述符数(1024)
  • dpdk_device_flag_set:设备标志设置函数

职责:

  • 封装模块内部实现细节
  • 提供内部使用的工具函数
  • 定义模块内部的常量

通俗理解:就像"分拣中心"的"内部规范",只有"内部员工"(模块内部)才能看到和使用。

依赖关系:

  • 依赖:dpdk/device/dpdk.h(公共头文件)
  • 被依赖:所有device目录下的.c文件(内部使用)

2.2.4 设备管理层 - 初始化

device/init.c - 初始化代码

功能:

  • DPDK EAL(Environment Abstraction Layer)初始化
  • DPDK设备发现和枚举
  • 设备接口创建和配置
  • 缓冲区池创建
  • dpdk-input节点注册
  • 线程初始化
  • 后台进程注册(dpdk_process、admin_up_down_process)

关键函数:

  • dpdk_init:模块初始化入口
  • dpdk_lib_init:DPDK库初始化
  • dpdk_process:后台进程(统计、链路状态轮询)
  • admin_up_down_process:设备启动/停止后台进程

关键代码 (src/plugins/dpdk/device/init.c:48):

c 复制代码
dpdk_main_t dpdk_main;
dpdk_config_main_t dpdk_config_main;

这两个全局变量存储了DPDK插件的所有状态信息。

初始化流程图:

复制代码
┌─────────────────────────────────────────┐
│  dpdk_init (VLIB_INIT_FUNCTION)        │
│  └── 注册日志类                          │
│      └── 设置默认配置                     │
└──────────────┬──────────────────────────┘
               │
               ▼
┌─────────────────────────────────────────┐
│  dpdk_lib_init (dpdk_process)          │
│  └── DPDK EAL初始化                      │
│      └── 设备发现和枚举                   │
│          └── 设备接口创建                 │
│              └── RX/TX队列分配          │
│                  └── 缓冲区池创建       │
│                      └── 节点注册       │
└─────────────────────────────────────────┘

职责:

  • 负责整个模块的初始化工作
  • 发现和配置DPDK设备
  • 创建VPP接口
  • 注册dpdk-input节点
  • 启动后台进程

通俗理解:就像"设备安装区",负责安装和配置"收货设备"(网卡),让它们能够正常工作。

依赖关系:

  • 依赖:buffer.c(缓冲区池)、dpdk.h(数据结构)、common.c(设备设置)
  • 被依赖:无(这是初始化文件)

2.2.5 数据平面层 - Input

device/node.c - 数据平面层(Input核心)

功能:

  • dpdk-input节点的实现
  • 数据包批量接收(dpdk_device_input)
  • 数据包处理(dpdk_process_rx_burst)
  • 硬件卸载处理(校验和、LRO、Flow Offload)
  • 多段数据包处理
  • 数据包分发

关键函数:

  • dpdk_input_node:dpdk-input节点主函数
  • dpdk_device_input:设备输入处理函数
  • dpdk_process_rx_burst:批量处理接收的数据包
  • dpdk_process_lro_offload:LRO处理
  • dpdk_process_flow_offload:Flow Offload处理
  • dpdk_process_subseq_segs:多段数据包处理

关键代码 (src/plugins/dpdk/device/node.c:563):

c 复制代码
VLIB_REGISTER_NODE (dpdk_input_node) = {
  .type = VLIB_NODE_TYPE_INPUT,
  .name = "dpdk-input",
  .sibling_of = "device-input",
  .flags = VLIB_NODE_FLAG_TRACE_SUPPORTED,
  .state = VLIB_NODE_STATE_DISABLED,
  // ...
};

职责:

  • 实现数据包接收的核心逻辑
  • 处理硬件卸载功能
  • 将数据包分发到VPP转发图

通俗理解:就像"收货窗口",是实际接收"包裹"(数据包)的地方,是整个"分拣中心"的核心。

这是整个模块的核心文件之一,后续章节将详细讲解其实现。

依赖关系:

  • 依赖:buffer.c(缓冲区转换)、dpdk.h(数据结构)、common.c(设备信息)
  • 被依赖:无(这是节点实现文件)

2.2.6 数据平面层 - Output

device/device.c - 数据平面层(Output核心)

功能:

  • dpdk-output节点的实现(VNET_DEVICE_CLASS_TX_FN)
  • 数据包批量发送(tx_burst_vector_internal)
  • TX硬件卸载处理(dpdk_buffer_tx_offload)
  • 设备管理(启动/停止、MAC地址、统计)
  • 设备类注册(VNET_DEVICE_CLASS)

关键函数:

  • VNET_DEVICE_CLASS_TX_FN:dpdk-output节点主函数
  • tx_burst_vector_internal:批量发送函数
  • dpdk_buffer_tx_offload:TX硬件卸载处理
  • dpdk_interface_admin_up_down:设备启动/停止
  • dpdk_add_del_mac_address:MAC地址管理
  • dpdk_set_interface_next_node:接口下一跳重定向

关键代码 (src/plugins/dpdk/device/device.c:726):

c 复制代码
VNET_DEVICE_CLASS (dpdk_device_class) = {
  .name = "dpdk",
  .tx_function_n_errors = DPDK_TX_FUNC_N_ERROR,
  .tx_function_error_strings = dpdk_tx_func_error_strings,
  .format_device_name = format_dpdk_device_name,
  .format_device = format_dpdk_device,
  .format_tx_trace = format_dpdk_tx_trace,
  .clear_counters = dpdk_clear_hw_interface_counters,
  .admin_up_down_function = dpdk_interface_admin_up_down,
  .subif_add_del_function = dpdk_subif_add_del_function,
  .rx_redirect_to_node = dpdk_set_interface_next_node,
  .mac_addr_change_function = dpdk_set_mac_address,
  .mac_addr_add_del_function = dpdk_add_del_mac_address,
  .format_flow = format_dpdk_flow,
  .flow_ops_function = dpdk_flow_ops_fn,
  .set_rss_queues_function = dpdk_interface_set_rss_queues,
  .rx_mode_change_function = dpdk_interface_rx_mode_change,
};

职责:

  • 实现数据包发送的核心逻辑
  • 处理TX硬件卸载功能
  • 管理设备的状态和配置

通俗理解:就像"发货窗口",是实际发送"包裹"(数据包)的地方,是整个"分拣中心"的另一个核心。

这是整个模块的核心文件之一,后续章节将详细讲解其实现。

依赖关系:

  • 依赖:buffer.c(缓冲区转换)、dpdk.h(数据结构)、common.c(设备设置)、flow.c(流表)
  • 被依赖:无(这是节点实现文件)

2.2.7 设备管理层 - 通用功能

device/common.c - 设备管理通用功能

功能:

  • 设备设置(dpdk_device_setup)
  • 设备启动(dpdk_device_start)
  • 设备停止(dpdk_device_stop)
  • 中断模式配置(dpdk_setup_interrupts)
  • 错误处理(dpdk_device_error)
  • RX/TX硬件卸载配置

关键函数:

  • dpdk_device_setup:设备设置(包括RX/TX队列设置、offload配置)
  • dpdk_device_start:启动设备
  • dpdk_device_stop:停止设备
  • dpdk_setup_interrupts:配置中断模式

关键代码 (src/plugins/dpdk/device/common.c:57):

c 复制代码
void
dpdk_device_setup (dpdk_device_t * xd)
{
  // ... 配置RX/TX offload ...
  // ... 设置RX/TX队列 ...
  // ... 配置RSS ...
  // ... 启动设备(如果已up) ...
}

职责:

  • 提供设备管理的通用功能
  • 处理设备的配置和状态管理
  • 配置硬件卸载功能

通俗理解:就像"设备维护区",负责维护"收货设备"(网卡)的状态,包括启动、停止、配置等。

依赖关系:

  • 依赖:buffer.c(缓冲区池)、dpdk.h(数据结构)
  • 被依赖:init.c(设备初始化)、device.c(设备管理)

2.2.8 设备管理层 - 驱动管理

device/driver.c - 驱动管理

功能:

  • 驱动匹配(dpdk_driver_find)
  • 驱动特性配置(校验和、中断、RSS等)
  • 驱动特定参数设置

关键数据结构:

  • dpdk_driver_t:驱动信息结构体
  • dpdk_driver_name_t:驱动名称结构体

支持的驱动 (src/plugins/dpdk/device/driver.c:20):

c 复制代码
static dpdk_driver_t dpdk_drivers[] = {
  {
    .drivers = DPDK_DRIVERS ({ "net_ixgbe", "Intel 82599" }),
    .enable_rxq_int = 1,
    .supported_flow_actions = supported_flow_actions_intel,
    .use_intel_phdr_cksum = 1,
  },
  {
    .drivers = DPDK_DRIVERS ({ "net_i40e", "Intel X710/XL710 Family" }),
    // ...
  },
  // ... 其他驱动
};

支持的驱动系列:

  • Intel系列(ixgbe、i40e、ice、igc等)
  • Mellanox系列
  • Cavium系列
  • 其他DPDK支持的驱动

职责:

  • 识别和匹配DPDK设备驱动
  • 根据驱动特性配置设备参数
  • 提供驱动特定的优化

通俗理解:就像"司机管理区",负责管理"快递司机"(驱动),根据"司机"的特点配置"快递车"(设备)。

依赖关系:

  • 依赖:dpdk.h(数据结构)
  • 被依赖:init.c(驱动匹配)

2.2.9 设备管理层 - 流表管理

device/flow.c - 流表管理

功能:

  • Flow Offload流表的创建和删除
  • 流表条目的管理
  • 流表匹配结果的处理
  • VPP流表规则到DPDK流表规则的转换

关键函数:

  • dpdk_flow_add:添加流表条目
  • dpdk_flow_del:删除流表条目
  • dpdk_flow_query:查询流表条目
  • dpdk_flow_convert_rss_types:转换RSS类型

职责:

  • 管理硬件流表(Flow Offload)
  • 将VPP的流表规则转换为DPDK的流表规则
  • 处理流表匹配结果

通俗理解:就像"快速通道",管理"VIP通道"(Flow Offload),让某些"包裹"(数据包)走"快速通道",提高处理效率。

依赖关系:

  • 依赖:dpdk.h(数据结构)、vnet/flow/flow.h(VPP流表接口)
  • 被依赖:device.c(流表操作)、node.c(流表匹配)

2.2.10 格式化输出层

device/format.c - 格式化输出

功能:

  • 调试信息的格式化输出
  • 设备信息的格式化显示
  • 统计信息的格式化显示
  • 流表信息的格式化显示

关键函数:

  • format_dpdk_device:格式化设备信息
  • format_dpdk_rx_trace:格式化接收跟踪信息
  • format_dpdk_tx_trace:格式化发送跟踪信息
  • format_dpdk_flow:格式化流表信息
  • 各种格式化辅助函数

职责:

  • 提供调试和诊断信息的格式化输出
  • 支持CLI命令的输出格式化
  • 支持跟踪信息的格式化

通俗理解:就像"信息展示区",负责将"分拣中心"的运行信息以可读的格式展示出来。

依赖关系:

  • 依赖:dpdk.h(数据结构)
  • 被依赖:cli.c(CLI输出)、device.c(跟踪输出)

2.2.11 CLI命令层

device/cli.c - CLI命令

功能:

  • 实现DPDK相关的CLI命令
  • 设备配置命令
  • 统计查询命令
  • 调试命令

主要CLI命令:

  • show dpdk buffer:显示缓冲区信息
  • show dpdk device:显示设备信息
  • set dpdk ...:配置DPDK参数
  • 其他调试和诊断命令

关键代码 (src/plugins/dpdk/device/cli.c:34):

c 复制代码
/**
 * @file
 * @brief CLI for DPDK Abstraction Layer and pcap Tx Trace.
 *
 * This file contains the source code for CLI for DPDK
 * Abstraction Layer and pcap Tx Trace.
 */

职责:

  • 提供命令行接口
  • 支持设备配置和管理
  • 支持统计查询和调试

通俗理解:就像"客服窗口",提供"客户服务"(命令行接口),让用户可以配置和管理"分拣中心"。

依赖关系:

  • 依赖:dpdk.h(数据结构)、format.c(格式化输出)、buffer.h(缓冲区信息)
  • 被依赖:无(这是CLI接口文件)

2.2.12 加密设备层(可选)

cryptodev/ - 加密设备支持

功能:

  • DPDK加密设备的初始化和管理
  • 加密操作的数据路径
  • 支持硬件和软件加密设备

文件:

  • cryptodev.c:加密设备初始化
  • cryptodev_op_data_path.c:操作数据路径
  • cryptodev_raw_data_path.c:原始数据路径

职责:

  • 提供加密设备支持(可选功能)
  • 支持IPSec等加密场景

通俗理解:就像"加密通道",提供"加密服务"(加密设备),让"包裹"(数据包)可以加密传输。

依赖关系:

  • 依赖:dpdk.h(数据结构)、rte_cryptodev.h(DPDK加密设备库)
  • 被依赖:init.c(加密设备初始化)

2.3 模块间的依赖关系

作用和实现原理:

DPDK插件模块内部文件之间存在清晰的依赖关系,遵循"依赖倒置原则",上层依赖下层,下层不依赖上层。

依赖关系详解:

1. main.c → device/dpdk.h
  • main.c依赖device/dpdk.h获取设备相关的接口
  • 插件注册时需要引用设备相关的接口
2. device/init.c → buffer.c 和 device/dpdk.h
  • init.c依赖buffer.c创建缓冲区池
  • init.c依赖dpdk.h中的数据结构定义
3. device/node.c → buffer.c 和 device/dpdk.h
  • node.c依赖buffer.c进行mbuf和buffer的转换
  • node.c依赖dpdk.h中的数据结构定义
4. device/device.c → device/dpdk.h 和 buffer.c
  • device.c依赖dpdk.h中的数据结构
  • device.c依赖buffer.c进行缓冲区管理
5. device/common.c → device/dpdk.h 和 buffer.c
  • common.c依赖dpdk.h中的数据结构
  • common.c依赖buffer.c进行缓冲区池管理
6. device/driver.c → device/dpdk.h
  • driver.c依赖dpdk.h中的驱动相关数据结构
7. device/flow.c → device/dpdk.h
  • flow.c依赖dpdk.h中的流表相关数据结构
8. device/cli.c → device/dpdk.h 和 buffer.h
  • cli.c依赖dpdk.h和buffer.h显示信息
9. device/format.c → device/dpdk.h
  • format.c依赖dpdk.h格式化输出
10. 所有device目录下的文件 → device/dpdk_priv.h
  • 所有device目录下的文件都依赖dpdk_priv.h获取私有定义

依赖层次:

复制代码
第1层(最底层):
    - dpdk_priv.h(私有定义)
    - buffer.h(缓冲区接口)

第2层(数据定义层):
    - dpdk.h(公共数据结构)

第3层(功能实现层):
    - buffer.c(缓冲区实现)
    - device/common.c(通用功能)

第4层(核心功能层):
    - device/init.c(初始化)
    - device/node.c(数据平面Input)
    - device/device.c(数据平面Output)
    - device/driver.c(驱动管理)
    - device/flow.c(流表管理)

第5层(接口层):
    - device/cli.c(CLI接口)
    - device/format.c(格式化输出)

第6层(入口层):
    - main.c(插件入口)

第7层(可选模块):
    - cryptodev/(加密设备)

通俗理解:

就像"快递分拣中心"的组织结构:

  • 第1层 = "基础规范":定义了"包裹格式"(数据结构)和"工具规范"(接口)
  • 第2层 = "公共规范":定义了"分拣中心"的"公共标准"
  • 第3层 = "工具库":提供了"分拣中心"使用的"工具"
  • 第4层 = "核心部门":负责"分拣中心"的核心工作
  • 第5层 = "服务窗口":提供"客户服务"(CLI)和"信息展示"(格式化)
  • 第6层 = "总调度室":负责整个"分拣中心"的启动
  • 第7层 = "增值服务":提供"加密服务"(可选)

依赖原则:

  • 上层依赖下层:上层可以使用下层的功能
  • 下层不依赖上层:下层不依赖上层的实现
  • 同层可以相互依赖:同层的模块可以相互调用

2.4 模块与外部系统的关系

作用和实现原理:

DPDK插件模块不仅内部有依赖关系,还与VPP的其他模块和DPDK库有依赖关系。

外部依赖关系图:

复制代码
┌─────────────────────────────────────────────────────────────┐
│              DPDK插件模块外部依赖关系                          │
└─────────────────────────────────────────────────────────────┘

                    ┌──────────────┐
                    │  DPDK库      │
                    │ (rte_*.h)    │
                    └──────┬───────┘
                           │
                           │ 被依赖
                           ▼
        ┌──────────────────────────────────┐
        │                                  │
        │      DPDK插件模块                │
        │  ┌──────────────────────────┐  │
        │  │  device/node.c           │  │
        │  │  device/device.c         │  │
        │  │  device/init.c           │  │
        │  │  buffer.c                │  │
        │  └──────────────────────────┘  │
        │                                  │
        └───────────┬──────────────────────┘
                    │
        ┌───────────┼───────────┐
        │           │           │
        ▼           ▼           ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ VLIB     │ │ VNET     │ │ 其他模块 │
│ (调度)   │ │ (网络)   │ │ (功能)  │
└──────────┘ └──────────┘ └──────────┘

外部依赖详解:

1. DPDK库依赖

主要DPDK头文件:

  • rte_eal.h:EAL(Environment Abstraction Layer)接口
  • rte_ethdev.h:以太网设备接口(核心)
  • rte_mbuf.h:mbuf数据结构(核心)
  • rte_mempool.h:内存池接口
  • rte_version.h:DPDK版本信息
  • rte_flow.h:流表接口(Flow Offload)
  • rte_cryptodev.h:加密设备接口(可选)

关键代码 (src/plugins/dpdk/device/node.c:370):

c 复制代码
n = rte_eth_rx_burst (xd->port_id, queue_id, ptd->mbufs + n_rx_packets,
                      n_to_rx);

关键代码 (src/plugins/dpdk/device/device.c:173):

c 复制代码
n_sent = rte_eth_tx_burst (xd->port_id, queue_id, mb, n_left);
2. VPP VLIB依赖

主要VLIB头文件:

  • vlib/vlib.h:VLIB核心接口(节点注册、调度)
  • vlib/node.h:节点相关接口
  • vlib/buffer.h:缓冲区接口
  • vlib/threads.h:线程管理接口

关键代码 (src/plugins/dpdk/device/node.c:563):

c 复制代码
VLIB_REGISTER_NODE (dpdk_input_node) = {
  .type = VLIB_NODE_TYPE_INPUT,
  .name = "dpdk-input",
  // ...
};
3. VPP VNET依赖

主要VNET头文件:

  • vnet/vnet.h:VNET核心接口
  • vnet/devices/devices.h:设备接口(核心)
  • vnet/ethernet/ethernet.h:以太网接口
  • vnet/interface/rx_queue_funcs.h:接收队列接口
  • vnet/interface/tx_queue_funcs.h:发送队列接口
  • vnet/feature/feature.h:Feature Arc接口

关键代码 (src/plugins/dpdk/device/device.c:726):

c 复制代码
VNET_DEVICE_CLASS (dpdk_device_class) = {
  .name = "dpdk",
  // ...
};
4. VPP其他模块依赖

主要其他模块:

  • vnet/flow/flow.h:流表接口
  • vnet/classify/vnet_classify.h:分类接口
  • vnet/mpls/packet.h:MPLS接口
  • vnet/tcp/tcp_packet.h:TCP接口
  • vlib/pci/pci.h:PCI接口
  • vlib/vmbus/vmbus.h:VMBus接口

依赖方向:

复制代码
DPDK库
  ↑
  │ 被依赖
  │
DPDK插件模块
  ↑
  │ 被依赖
  │
VPP核心模块(VLIB、VNET)
  ↑
  │ 被依赖
  │
VPP其他功能模块

通俗理解:

就像"快递分拣中心"与外部系统的关系:

  • DPDK库 = "快递公司":提供专业的"快递服务"(网卡驱动)
  • DPDK插件模块 = "分拣中心":使用"快递公司"的服务
  • VPP核心模块 = "分拣中心管理系统":提供"分拣中心"的基础设施
  • VPP其他模块 = "分拣中心的其他部门":提供各种"分拣服务"

关键特点:

  • 封装性:DPDK插件封装了DPDK的复杂性,为VPP提供统一的接口
  • 集成性:DPDK插件将DPDK的功能集成到VPP的转发图中
  • 可扩展性:DPDK插件支持DPDK的新功能和特性
  • 模块化:DPDK插件是VPP的一个独立模块,可以单独编译和加载

2.5 文件编译和链接

作用和实现原理:

DPDK插件的编译和链接通过CMakeLists.txt文件配置,支持多架构优化和可选模块。

编译配置 (src/plugins/dpdk/CMakeLists.txt:130):

cmake 复制代码
add_vpp_plugin(dpdk
  SOURCES
  buffer.c
  main.c
  device/cli.c
  device/common.c
  device/device.c
  device/driver.c
  device/flow.c
  device/format.c
  device/init.c
  device/node.c
  ${DPDK_CRYPTODEV_OP_SOURCE}
  ${DPDK_CRYPTODEV_SOURCE}
  ${DPDK_CRYPTODEV_RAW_SOURCE}

  MULTIARCH_SOURCES
  buffer.c
  device/device.c
  device/node.c

  INSTALL_HEADERS
  device/dpdk.h

  LINK_FLAGS
  "${DPDK_LINK_FLAGS}"

  LINK_LIBRARIES
  ${DPDK_LINK_LIBRARIES}
  ${OPENSSL_CRYPTO_LIBRARIES}
)

关键配置说明:

  1. SOURCES:所有源文件列表
  2. MULTIARCH_SOURCES:支持多架构优化的源文件(性能关键路径)
  3. INSTALL_HEADERS:安装的公共头文件
  4. LINK_FLAGS:链接标志(DPDK库的特殊链接要求)
  5. LINK_LIBRARIES:链接的库(DPDK库、OpenSSL库)

多架构优化:

MULTIARCH_SOURCES中的文件会针对不同的CPU架构(如AVX、AVX2、AVX512)进行优化编译,提高性能关键路径的执行效率。

通俗理解:

就像"快递分拣中心"的"建设规划":

  • SOURCES = "需要建设的部门":列出所有需要建设的"部门"(源文件)
  • MULTIARCH_SOURCES = "重点优化部门":对"核心部门"(性能关键路径)进行"特殊优化"(多架构编译)
  • INSTALL_HEADERS = "对外接口":安装"公共接口"(头文件),供其他模块使用
  • LINK_FLAGS/LINK_LIBRARIES = "外部资源":链接"外部资源"(DPDK库、OpenSSL库)

2.6 本章总结

DPDK插件模块的文件组织特点:

  1. 分层清晰:按照功能分层,每层职责明确

    • 入口层 → 数据定义层 → 功能实现层 → 核心功能层 → 接口层
  2. 模块化设计:每个文件负责特定功能,便于维护

    • Input和Output分离,但共享数据结构和管理功能
  3. 依赖关系清晰:遵循依赖倒置原则,上层依赖下层

    • 避免循环依赖,保持代码结构清晰
  4. 接口统一:通过公共头文件提供统一接口

    • dpdk.h提供公共接口,dpdk_priv.h封装内部实现

关键文件总结:

文件 层级 主要职责 重要性 是否多架构优化
main.c 入口层 插件注册 ⭐⭐ ❌
buffer.c/h 基础层 缓冲区转换 ⭐⭐⭐ ✅
device/dpdk.h 数据层 数据结构定义 ⭐⭐⭐ ❌
device/dpdk_priv.h 数据层 私有数据结构 ⭐⭐ ❌
device/init.c 功能层 模块初始化 ⭐⭐⭐ ❌
device/node.c 核心层 数据包接收(核心) ⭐⭐⭐⭐⭐ ✅
device/device.c 核心层 数据包发送(核心) ⭐⭐⭐⭐⭐ ✅
device/common.c 功能层 设备管理 ⭐⭐⭐ ❌
device/driver.c 功能层 驱动管理 ⭐⭐ ❌
device/flow.c 功能层 流表管理 ⭐⭐ ❌
device/cli.c 接口层 CLI命令 ⭐⭐ ❌
device/format.c 接口层 格式化输出 ⭐ ❌
cryptodev/ 可选层 加密设备 ⭐⭐ ❌

文件组织优势:

  1. 可维护性:每个文件职责单一,便于理解和修改
  2. 可扩展性:新功能可以添加新文件,不影响现有代码
  3. 可测试性:每个模块可以独立测试
  4. 性能优化:关键路径文件支持多架构优化

后续章节预告:

  • 第3章 :详细讲解核心数据结构(dpdk_device_t、dpdk_rx_queue_t、dpdk_tx_queue_t等)
  • 第4章 :详细分析模块初始化流程(init.c)
  • 第8章 :详细讲解dpdk-input节点核心处理(node.c)
  • 第15章 :详细讲解dpdk-output节点核心处理(device.c)

相关源码文件:

  • src/plugins/dpdk/ - 所有文件
  • src/plugins/dpdk/CMakeLists.txt - 编译配置

第3章:核心数据结构

3.1 DPDK设备结构体(dpdk_device_t)

作用和实现原理:

dpdk_device_t是DPDK插件的核心数据结构,代表一个DPDK设备(网卡)。它包含了设备的所有信息,包括队列、统计、配置、流表等。就像"快递分拣中心"的"设备档案",记录了每个"收货设备"(网卡)的所有信息。

数据结构定义 (src/plugins/dpdk/device/dpdk.h:164):

c 复制代码
typedef struct
{
  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);

  dpdk_rx_queue_t *rx_queues;      // 接收队列数组
  dpdk_tx_queue_t *tx_queues;      // 发送队列数组

  /* Instance ID to access internal device array. */
  u32 device_index;                // 设备索引(在devices数组中的位置)

  u32 hw_if_index;                 // 硬件接口索引(VPP)
  u32 sw_if_index;                  // 软件接口索引(VPP)
  u32 buffer_flags;                // 缓冲区标志

  /* next node index if we decide to steal the rx graph arc */
  u32 per_interface_next_index;    // 接口特定的下一跳节点索引

  u16 flags;                        // 设备标志(ADMIN_UP、PROMISC等)

  /* DPDK device port number */
  dpdk_portid_t port_id;           // DPDK端口ID
  i8 cpu_socket;                   // CPU socket(NUMA节点)

  CLIB_CACHE_LINE_ALIGN_MARK (cacheline1);

  u64 enabled_tx_off;              // 启用的TX硬件卸载功能
  u64 enabled_rx_off;              // 启用的RX硬件卸载功能
  dpdk_driver_t *driver;            // 驱动信息
  u8 *name;                         // 设备名称
  const char *if_desc;              // 接口描述

  /* number of sub-interfaces */
  u16 num_subifs;                  // 子接口数量

  /* flow related */
  u32 supported_flow_actions;       // 支持的流表动作
  dpdk_flow_entry_t *flow_entries; // 流表条目池
  dpdk_flow_lookup_entry_t *flow_lookup_entries; // 流表查找条目池
  u32 *parked_lookup_indexes;      // 暂停的查找索引向量
  u32 parked_loop_count;            // 暂停循环计数
  struct rte_flow_error last_flow_error; // 最后一个流表错误

  struct rte_eth_link link;         // 链路状态
  f64 time_last_link_update;       // 上次链路状态更新时间

  struct rte_eth_stats stats;       // 统计信息
  struct rte_eth_stats last_stats;  // 上次统计信息
  struct rte_eth_xstat *xstats;     // 扩展统计信息
  f64 time_last_stats_update;      // 上次统计更新时间
  vlib_simple_counter_main_t xstats_counters; // 扩展统计计数器
  u32 *xstats_symlinks;             // 扩展统计符号链接

  /* mac address */
  u8 *default_mac_address;          // 默认MAC地址

  /* maximum supported max frame size */
  u32 max_supported_frame_size;     // 最大支持的帧大小

  /* due to lack of API to get ethernet max_frame_size we store information
   * deducted from device info */
  u8 driver_frame_overhead;         // 驱动帧开销

  /* error string */
  clib_error_t *errors;             // 错误信息
  dpdk_port_conf_t conf;            // 端口配置
} dpdk_device_t;

关键字段详解:

1. 队列相关字段
  • rx_queues:接收队列数组指针

    • 作用:存储所有接收队列的信息
    • 类型 :dpdk_rx_queue_t *(动态数组)
    • 通俗理解:就像"收货窗口"的"窗口列表",每个"窗口"(队列)负责接收"包裹"(数据包)
  • tx_queues:发送队列数组指针

    • 作用:存储所有发送队列的信息
    • 类型 :dpdk_tx_queue_t *(动态数组)
    • 通俗理解:就像"发货窗口"的"窗口列表",每个"窗口"(队列)负责发送"包裹"(数据包)
2. 索引相关字段
  • device_index:设备索引

    • 作用 :在dpdk_main.devices数组中的位置
    • 类型 :u32
    • 通俗理解:就像"设备档案"的"档案编号",用于快速查找
  • hw_if_index:硬件接口索引

    • 作用:VPP硬件接口索引
    • 类型 :u32
    • 通俗理解:就像"设备"在VPP系统中的"硬件ID"
  • sw_if_index:软件接口索引

    • 作用:VPP软件接口索引
    • 类型 :u32
    • 通俗理解:就像"设备"在VPP系统中的"软件ID"
3. DPDK相关字段
  • port_id:DPDK端口ID

    • 作用:DPDK库中的端口标识符
    • 类型 :dpdk_portid_t(uint16_t)
    • 通俗理解:就像"设备"在DPDK库中的"身份证号"
  • cpu_socket:CPU socket(NUMA节点)

    • 作用:设备所在的NUMA节点
    • 类型 :i8
    • 通俗理解:就像"设备"所在的"区域"(NUMA节点),用于优化内存访问
4. 硬件卸载相关字段
  • enabled_tx_off:启用的TX硬件卸载功能

    • 作用:记录启用的TX硬件卸载功能(校验和、TSO等)
    • 类型 :u64(位掩码)
    • 通俗理解:就像"发货窗口"的"自动化功能开关",记录哪些功能已经开启
  • enabled_rx_off:启用的RX硬件卸载功能

    • 作用:记录启用的RX硬件卸载功能(校验和、LRO等)
    • 类型 :u64(位掩码)
    • 通俗理解:就像"收货窗口"的"自动化功能开关",记录哪些功能已经开启
5. 驱动相关字段
  • driver :驱动信息指针
    • 作用 :指向驱动信息结构体(dpdk_driver_t)
    • 类型 :dpdk_driver_t *
    • 通俗理解:就像"设备"的"司机信息",记录了"司机"(驱动)的特点和能力
6. 流表相关字段
  • flow_entries:流表条目池

    • 作用:存储Flow Offload流表条目
    • 类型 :dpdk_flow_entry_t *(pool)
    • 通俗理解:就像"快速通道"的"通道列表",记录了哪些"包裹"(数据包)可以走"快速通道"
  • flow_lookup_entries:流表查找条目池

    • 作用:存储流表查找条目(用于快速匹配)
    • 类型 :dpdk_flow_lookup_entry_t *(pool)
    • 通俗理解:就像"快速通道"的"查找表",用于快速查找"包裹"应该走哪个"通道"
7. 统计相关字段
  • stats:统计信息

    • 作用:存储DPDK设备的统计信息(收发包数、错误数等)
    • 类型 :struct rte_eth_stats
    • 通俗理解:就像"设备"的"工作记录",记录了"收货"和"发货"的数量
  • xstats:扩展统计信息

    • 作用:存储DPDK设备的扩展统计信息(更详细的统计)
    • 类型 :struct rte_eth_xstat *
    • 通俗理解:就像"设备"的"详细工作记录",记录了更详细的工作信息
8. 配置相关字段
  • conf :端口配置
    • 作用:存储端口的配置信息(队列数、描述符数、RSS等)
    • 类型 :dpdk_port_conf_t
    • 通俗理解:就像"设备"的"配置档案",记录了"设备"的各种配置参数

缓存行对齐:

结构体使用了CLIB_CACHE_LINE_ALIGN_MARK宏来标记缓存行边界,这是为了优化多核性能,避免false sharing(伪共享)。

通俗理解:

dpdk_device_t就像"快递分拣中心"的"设备档案",记录了每个"收货设备"(网卡)的所有信息:

  • 基本信息:设备名称、索引、端口ID
  • 队列信息:接收队列、发送队列
  • 能力信息:硬件卸载功能、流表支持
  • 状态信息:链路状态、统计信息
  • 配置信息:端口配置、驱动信息

使用场景:

  • 设备初始化 :创建dpdk_device_t并填充信息
  • 数据包接收 :通过device_index查找设备,使用rx_queues接收数据包
  • 数据包发送 :通过device_index查找设备,使用tx_queues发送数据包
  • 统计查询 :读取stats和xstats获取统计信息
  • 流表管理 :使用flow_entries和flow_lookup_entries管理流表

3.2 接收队列结构体(dpdk_rx_queue_t)

作用和实现原理:

dpdk_rx_queue_t代表一个DPDK接收队列,用于接收数据包。每个设备可以有多个接收队列,用于多队列接收和RSS(Receive Side Scaling)。就像"快递分拣中心"的"收货窗口",每个"窗口"(队列)负责接收"包裹"(数据包)。

数据结构定义 (src/plugins/dpdk/device/dpdk.h:97):

c 复制代码
typedef struct
{
  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
  u8 buffer_pool_index;            // 缓冲区池索引
  u32 queue_index;                 // 队列索引(在队列数组中的位置)
  int efd;                         // 事件文件描述符(用于中断模式)
  uword clib_file_index;           // CLIB文件索引
} dpdk_rx_queue_t;

关键字段详解:

1. buffer_pool_index:缓冲区池索引
  • 作用:指定该队列使用的缓冲区池
  • 类型 :u8
  • 通俗理解:就像"收货窗口"使用的"包裹存放区"(缓冲区池)的编号

关键代码 (src/plugins/dpdk/device/node.c:370):

c 复制代码
n = rte_eth_rx_burst (xd->port_id, queue_id, ptd->mbufs + n_rx_packets,
                      n_to_rx);
2. queue_index:队列索引
  • 作用:队列在队列数组中的位置
  • 类型 :u32
  • 通俗理解:就像"收货窗口"的"窗口编号",用于标识是第几个"窗口"
3. efd:事件文件描述符
  • 作用:用于中断模式的事件文件描述符
  • 类型 :int
  • 通俗理解:就像"收货窗口"的"通知铃",当有"包裹"到达时,"通知铃"会响(中断)

关键代码 (src/plugins/dpdk/device/common.c:320):

c 复制代码
static void
dpdk_setup_interrupts (dpdk_device_t *xd)
{
  // ... 设置中断模式 ...
  rq->efd = eventfd (0, EFD_NONBLOCK | EFD_CLOEXEC);
  // ...
}
4. clib_file_index:CLIB文件索引
  • 作用:CLIB文件系统的文件索引(用于事件处理)
  • 类型 :uword
  • 通俗理解:就像"通知铃"在"管理系统"中的"注册号"

缓存行对齐:

结构体使用CLIB_CACHE_LINE_ALIGN_MARK标记缓存行边界,避免false sharing。

队列与设备的关系:

复制代码
┌─────────────────────────────────────────┐
│      dpdk_device_t                      │
│  ┌───────────────────────────────────┐  │
│  │  rx_queues (数组指针)             │  │
│  └──────────────┬────────────────────┘  │
│                 │                        │
│                 ▼                        │
│  ┌───────────────────────────────────┐  │
│  │  dpdk_rx_queue_t[0]              │  │
│  │    buffer_pool_index = 0          │  │
│  │    queue_index = 0                │  │
│  │    efd = ...                      │  │
│  └───────────────────────────────────┘  │
│  ┌───────────────────────────────────┐  │
│  │  dpdk_rx_queue_t[1]              │  │
│  │    buffer_pool_index = 0          │  │
│  │    queue_index = 1                │  │
│  │    efd = ...                      │  │
│  └───────────────────────────────────┘  │
│  ... (更多队列)                         │
└─────────────────────────────────────────┘

通俗理解:

dpdk_rx_queue_t就像"快递分拣中心"的"收货窗口":

  • 基本信息 :窗口编号(queue_index)、使用的存放区(buffer_pool_index)
  • 通知机制 :通知铃(efd),当有"包裹"到达时会响

使用场景:

  • 队列创建:在设备初始化时创建接收队列
  • 数据包接收:使用队列索引从DPDK接收数据包
  • 中断模式 :使用efd实现中断驱动的接收
  • RSS:多个队列用于RSS负载均衡

3.3 发送队列结构体(dpdk_tx_queue_t)

作用和实现原理:

dpdk_tx_queue_t代表一个DPDK发送队列,用于发送数据包。每个设备可以有多个发送队列,用于多队列发送。就像"快递分拣中心"的"发货窗口",每个"窗口"(队列)负责发送"包裹"(数据包)。

数据结构定义 (src/plugins/dpdk/device/dpdk.h:106):

c 复制代码
typedef struct
{
  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
  clib_spinlock_t lock;            // 自旋锁(用于多线程保护)
  u32 queue_index;                 // 队列索引(在队列数组中的位置)
} dpdk_tx_queue_t;

关键字段详解:

1. lock:自旋锁
  • 作用:保护队列的并发访问(多线程安全)
  • 类型 :clib_spinlock_t
  • 通俗理解:就像"发货窗口"的"门锁",确保同一时间只有一个"工作人员"(线程)可以操作"窗口"

为什么需要锁:

发送队列可能被多个线程同时访问(多线程发送),需要使用锁来保护队列的并发访问。

关键代码 (src/plugins/dpdk/device/device.c:173):

c 复制代码
n_sent = rte_eth_tx_burst (xd->port_id, queue_id, mb, n_left);
2. queue_index:队列索引
  • 作用:队列在队列数组中的位置
  • 类型 :u32
  • 通俗理解:就像"发货窗口"的"窗口编号",用于标识是第几个"窗口"

缓存行对齐:

结构体使用CLIB_CACHE_LINE_ALIGN_MARK标记缓存行边界,避免false sharing。

队列与设备的关系:

复制代码
┌─────────────────────────────────────────┐
│      dpdk_device_t                      │
│  ┌───────────────────────────────────┐  │
│  │  tx_queues (数组指针)             │  │
│  └──────────────┬────────────────────┘  │
│                 │                        │
│                 ▼                        │
│  ┌───────────────────────────────────┐  │
│  │  dpdk_tx_queue_t[0]              │  │
│  │    lock = ...                    │  │
│  │    queue_index = 0                │  │
│  └───────────────────────────────────┘  │
│  ┌───────────────────────────────────┐  │
│  │  dpdk_tx_queue_t[1]              │  │
│  │    lock = ...                    │  │
│  │    queue_index = 1                │  │
│  └───────────────────────────────────┘  │
│  ... (更多队列)                         │
└─────────────────────────────────────────┘

为什么RX队列不需要锁,而TX队列需要锁:

  • RX队列:通常每个线程绑定一个队列,不需要锁
  • TX队列:多个线程可能共享一个队列,需要锁保护

通俗理解:

dpdk_tx_queue_t就像"快递分拣中心"的"发货窗口":

  • 基本信息 :窗口编号(queue_index)
  • 并发保护 :门锁(lock),确保多线程安全

使用场景:

  • 队列创建:在设备初始化时创建发送队列
  • 数据包发送:使用队列索引向DPDK发送数据包
  • 多线程发送:使用锁保护队列的并发访问

3.4 每线程数据结构体(dpdk_per_thread_data_t)

作用和实现原理:

dpdk_per_thread_data_t是每个线程的私有数据结构,用于存储线程特定的数据(如批量接收缓冲区、下一跳索引等)。就像"快递分拣中心"的"工作人员工作台",每个"工作人员"(线程)都有自己的"工作台"(每线程数据)。

数据结构定义 (src/plugins/dpdk/device/dpdk.h:318):

c 复制代码
#define DPDK_RX_BURST_SZ VLIB_FRAME_SIZE

typedef struct
{
  CLIB_CACHE_LINE_ALIGN_MARK (cacheline0);
  struct rte_mbuf *mbufs[DPDK_RX_BURST_SZ];  // mbuf指针数组(批量接收)
  u32 buffers[DPDK_RX_BURST_SZ];            // buffer索引数组
  u16 next[DPDK_RX_BURST_SZ];                // 下一跳节点索引数组
  u16 etype[DPDK_RX_BURST_SZ];               // 以太网类型数组
  u32 flags[DPDK_RX_BURST_SZ];               // 标志数组
  vlib_buffer_t buffer_template;              // buffer模板
} dpdk_per_thread_data_t;

关键字段详解:

1. mbufs:mbuf指针数组
  • 作用:存储批量接收的mbuf指针
  • 类型 :struct rte_mbuf *[DPDK_RX_BURST_SZ]
  • 大小 :DPDK_RX_BURST_SZ(等于VLIB_FRAME_SIZE,通常为256)
  • 通俗理解:就像"工作台"上的"包裹临时存放区",用于存放批量接收的"包裹"(数据包)

关键代码 (src/plugins/dpdk/device/node.c:370):

c 复制代码
n = rte_eth_rx_burst (xd->port_id, queue_id, ptd->mbufs + n_rx_packets,
                      n_to_rx);
2. buffers:buffer索引数组
  • 作用:存储VPP buffer的索引
  • 类型 :u32[DPDK_RX_BURST_SZ]
  • 通俗理解:就像"工作台"上的"包裹编号列表",记录了每个"包裹"(数据包)的"编号"(buffer索引)
3. next:下一跳节点索引数组
  • 作用:存储每个数据包的下一跳节点索引
  • 类型 :u16[DPDK_RX_BURST_SZ]
  • 通俗理解:就像"工作台"上的"包裹去向列表",记录了每个"包裹"(数据包)应该送到哪个"部门"(下一跳节点)

关键代码 (src/plugins/dpdk/device/node.c:443):

c 复制代码
vlib_buffer_enqueue_to_next (vm, node, ptd->buffers, ptd->next,
                              n_rx_packets);
4. etype:以太网类型数组
  • 作用:存储每个数据包的以太网类型(EtherType)
  • 类型 :u16[DPDK_RX_BURST_SZ]
  • 通俗理解:就像"工作台"上的"包裹类型列表",记录了每个"包裹"(数据包)的类型(IPv4、IPv6、ARP等)
5. flags:标志数组
  • 作用:存储每个数据包的标志(硬件卸载标志等)
  • 类型 :u32[DPDK_RX_BURST_SZ]
  • 通俗理解:就像"工作台"上的"包裹标记列表",记录了每个"包裹"(数据包)的特殊标记(校验和、LRO等)
6. buffer_template:buffer模板
  • 作用:用于初始化buffer的模板
  • 类型 :vlib_buffer_t
  • 通俗理解:就像"工作台"上的"标准模板",用于快速初始化"包裹"(数据包)的"标签"(buffer)

关键代码 (src/plugins/dpdk/device/init.c:316):

c 复制代码
dpdk_per_thread_data_t *ptd = vec_elt_at_index (dm->per_thread_data, i);
clib_memset (&ptd->buffer_template, 0, sizeof (vlib_buffer_t));
vnet_buffer (&ptd->buffer_template)->sw_if_index[VLIB_TX] = (u32) ~ 0;

为什么需要每线程数据:

  • 性能优化:避免多线程之间的数据竞争,提高缓存命中率
  • 批量处理:支持批量接收和处理数据包
  • 减少锁竞争:每个线程有独立的数据结构,减少锁竞争

数据结构布局:

复制代码
┌─────────────────────────────────────────┐
│  dpdk_main_t                            │
│  ┌───────────────────────────────────┐  │
│  │  per_thread_data (数组指针)        │  │
│  └──────────────┬────────────────────┘  │
│                 │                        │
│                 ▼                        │
│  ┌───────────────────────────────────┐  │
│  │  dpdk_per_thread_data_t[0]        │  │
│  │    mbufs[256]                     │  │
│  │    buffers[256]                   │  │
│  │    next[256]                      │  │
│  │    etype[256]                     │  │
│  │    flags[256]                     │  │
│  │    buffer_template                │  │
│  └───────────────────────────────────┘  │
│  ┌───────────────────────────────────┐  │
│  │  dpdk_per_thread_data_t[1]        │  │
│  │    ... (线程1的数据)              │  │
│  └───────────────────────────────────┘  │
│  ... (更多线程)                         │
└─────────────────────────────────────────┘

通俗理解:

dpdk_per_thread_data_t就像"快递分拣中心"的"工作人员工作台":

  • 临时存放区 :mbufs和buffers,用于存放批量接收的"包裹"(数据包)
  • 处理列表 :next、etype、flags,记录了每个"包裹"的处理信息
  • 标准模板 :buffer_template,用于快速初始化"包裹"的"标签"

使用场景:

  • 批量接收 :使用mbufs数组批量接收数据包
  • 批量处理 :使用buffers、next、etype、flags数组批量处理数据包
  • 性能优化:每个线程有独立的数据结构,避免数据竞争

3.5 DPDK主结构体(dpdk_main_t)

作用和实现原理:

dpdk_main_t是DPDK插件的全局主结构体,存储了所有设备、每线程数据、配置等信息。就像"快递分拣中心"的"总档案室",记录了整个"分拣中心"的所有信息。

数据结构定义 (src/plugins/dpdk/device/dpdk.h:329):

c 复制代码
typedef struct
{
  /* Devices */
  dpdk_device_t *devices;                    // 设备数组
  dpdk_per_thread_data_t *per_thread_data;    // 每线程数据数组

  /*
   * flag indicating that a posted admin up/down
   * (via post_sw_interface_set_flags) is in progress
   */
  u8 admin_up_down_in_progress;               // 管理启动/停止进行中标志

  /* control interval of dpdk link state and stat polling */
  f64 link_state_poll_interval;               // 链路状态轮询间隔
  f64 stat_poll_interval;                     // 统计轮询间隔

  dpdk_config_main_t *conf;                   // 配置主结构体指针
  dpdk_port_conf_t default_port_conf;         // 默认端口配置

  /* API message ID base */
  u16 msg_id_base;                            // API消息ID基址

  /* logging */
  vlib_log_class_t log_default;               // 默认日志类
  vlib_log_class_t log_cryptodev;             // 加密设备日志类
} dpdk_main_t;

extern dpdk_main_t dpdk_main;

关键字段详解:

1. devices:设备数组
  • 作用:存储所有DPDK设备
  • 类型 :dpdk_device_t *(动态数组)
  • 通俗理解:就像"总档案室"的"设备档案柜",存放了所有"收货设备"(网卡)的"档案"

关键代码 (src/plugins/dpdk/device/init.c:48):

c 复制代码
dpdk_main_t dpdk_main;
2. per_thread_data:每线程数据数组
  • 作用:存储所有线程的每线程数据
  • 类型 :dpdk_per_thread_data_t *(动态数组)
  • 通俗理解:就像"总档案室"的"工作人员工作台档案柜",存放了所有"工作人员"(线程)的"工作台"信息

关键代码 (src/plugins/dpdk/device/init.c:311):

c 复制代码
vec_validate_aligned (dm->per_thread_data, tm->n_vlib_mains - 1,
                      CLIB_CACHE_LINE_BYTES);
3. admin_up_down_in_progress:管理启动/停止进行中标志
  • 作用:标志是否有管理启动/停止操作正在进行
  • 类型 :u8
  • 通俗理解:就像"总档案室"的"正在操作"标志,表示有"设备"正在启动或停止
  • 作用:控制链路状态轮询的间隔时间
  • 类型 :f64(秒)
  • 默认值 :DPDK_LINK_POLL_INTERVAL(3.0秒)
  • 通俗理解:就像"总档案室"的"设备状态检查频率",每隔一段时间检查一次"设备"的"连接状态"
5. stat_poll_interval:统计轮询间隔
  • 作用:控制统计信息轮询的间隔时间
  • 类型 :f64(秒)
  • 默认值 :DPDK_STATS_POLL_INTERVAL(10.0秒)
  • 通俗理解:就像"总档案室"的"工作记录更新频率",每隔一段时间更新一次"设备"的"工作记录"(统计信息)
6. conf:配置主结构体指针
  • 作用 :指向配置主结构体(dpdk_config_main_t)
  • 类型 :dpdk_config_main_t *
  • 通俗理解:就像"总档案室"的"配置档案柜",存放了"分拣中心"的"配置信息"
7. default_port_conf:默认端口配置
  • 作用:存储默认的端口配置
  • 类型 :dpdk_port_conf_t
  • 通俗理解:就像"总档案室"的"默认配置模板",新"设备"使用这个"模板"进行配置

关键代码 (src/plugins/dpdk/device/init.c:324):

c 复制代码
dm->default_port_conf.n_rx_desc = DPDK_NB_RX_DESC_DEFAULT;
dm->default_port_conf.n_tx_desc = DPDK_NB_TX_DESC_DEFAULT;
dm->default_port_conf.n_rx_queues = 1;
dm->default_port_conf.n_tx_queues = tm->n_vlib_mains;
8. msg_id_base:API消息ID基址
  • 作用:API消息ID的基址(用于API消息编号)
  • 类型 :u16
  • 通俗理解:就像"总档案室"的"消息编号起始值",用于给API消息编号
9. log_default 和 log_cryptodev:日志类
  • 作用:用于日志记录
  • 类型 :vlib_log_class_t
  • 通俗理解:就像"总档案室"的"日志记录本",用于记录"分拣中心"的"运行日志"

全局变量:

dpdk_main是全局变量,在src/plugins/dpdk/device/init.c:48中定义:

c 复制代码
dpdk_main_t dpdk_main;

数据结构关系图:

复制代码
┌─────────────────────────────────────────┐
│      dpdk_main_t (全局)                  │
│  ┌───────────────────────────────────┐  │
│  │  devices (设备数组)                │  │
│  │    └── dpdk_device_t[0]           │  │
│  │    └── dpdk_device_t[1]           │  │
│  │    └── ...                        │  │
│  └───────────────────────────────────┘  │
│  ┌───────────────────────────────────┐  │
│  │  per_thread_data (每线程数据数组)  │  │
│  │    └── dpdk_per_thread_data_t[0]  │  │
│  │    └── dpdk_per_thread_data_t[1]  │  │
│  │    └── ...                        │  │
│  └───────────────────────────────────┘  │
│  ┌───────────────────────────────────┐  │
│  │  conf (配置主结构体指针)            │  │
│  │    └── dpdk_config_main_t          │  │
│  └───────────────────────────────────┘  │
│  ┌───────────────────────────────────┐  │
│  │  default_port_conf (默认端口配置)   │  │
│  │    └── dpdk_port_conf_t            │  │
│  └───────────────────────────────────┘  │
└─────────────────────────────────────────┘

通俗理解:

dpdk_main_t就像"快递分拣中心"的"总档案室":

  • 设备档案柜 :devices,存放所有"收货设备"(网卡)的"档案"
  • 工作台档案柜 :per_thread_data,存放所有"工作人员"(线程)的"工作台"信息
  • 配置档案柜 :conf,存放"分拣中心"的"配置信息"
  • 默认配置模板 :default_port_conf,新"设备"使用的"配置模板"
  • 运行参数:轮询间隔、日志类等

使用场景:

  • 设备查找 :通过devices数组查找设备
  • 线程数据访问 :通过per_thread_data数组访问线程数据
  • 配置管理 :通过conf访问配置信息
  • 统计轮询 :使用stat_poll_interval控制统计轮询
  • 链路状态轮询 :使用link_state_poll_interval控制链路状态轮询

3.6 流表相关结构体

作用和实现原理:

流表相关结构体用于管理Flow Offload功能,将VPP的流表规则转换为DPDK的流表规则,实现硬件加速。就像"快递分拣中心"的"快速通道",某些"包裹"(数据包)可以走"快速通道"(Flow Offload),提高处理效率。

3.6.1 流表条目结构体(dpdk_flow_entry_t)

数据结构定义 (src/plugins/dpdk/device/dpdk.h:83):

c 复制代码
typedef struct
{
  u32 flow_index;                  // VPP流表索引
  u32 mark;                         // 流表标记(用于查找)
  struct rte_flow *handle;          // DPDK流表句柄
} dpdk_flow_entry_t;

关键字段详解:

  • flow_index:VPP流表索引

    • 作用:指向VPP流表的索引
    • 类型 :u32
    • 通俗理解:就像"快速通道"在VPP系统中的"通道编号"
  • mark:流表标记

    • 作用:用于快速查找流表条目
    • 类型 :u32
    • 通俗理解:就像"快速通道"的"标记",用于快速识别
  • handle:DPDK流表句柄

    • 作用:DPDK流表的句柄(用于操作DPDK流表)
    • 类型 :struct rte_flow *
    • 通俗理解:就像"快速通道"在DPDK系统中的"通道句柄",用于操作"通道"
3.6.2 流表查找条目结构体(dpdk_flow_lookup_entry_t)

数据结构定义 (src/plugins/dpdk/device/dpdk.h:90):

c 复制代码
typedef struct
{
  u32 flow_id;                      // 流表ID
  u16 next_index;                   // 下一跳节点索引
  i16 buffer_advance;                // 缓冲区前进字节数
} dpdk_flow_lookup_entry_t;

关键字段详解:

  • flow_id:流表ID

    • 作用:流表的唯一标识符
    • 类型 :u32
    • 通俗理解:就像"快速通道"的"通道ID",用于唯一标识"通道"
  • next_index:下一跳节点索引

    • 作用:数据包匹配流表后的下一跳节点
    • 类型 :u16
    • 通俗理解:就像"快速通道"的"出口","包裹"(数据包)匹配后应该送到哪个"部门"(下一跳节点)
  • buffer_advance:缓冲区前进字节数

    • 作用:数据包匹配流表后,缓冲区指针需要前进的字节数
    • 类型 :i16
    • 通俗理解:就像"快速通道"的"位置调整","包裹"(数据包)匹配后需要调整"位置"(缓冲区指针)

流表关系图:

复制代码
┌─────────────────────────────────────────┐
│      dpdk_device_t                      │
│  ┌───────────────────────────────────┐  │
│  │  flow_entries (流表条目池)         │  │
│  │    └── dpdk_flow_entry_t[0]        │  │
│  │        flow_index = 0             │  │
│  │        mark = 1                   │  │
│  │        handle = ...                │  │
│  │    └── dpdk_flow_entry_t[1]        │  │
│  │        ...                        │  │
│  └───────────────────────────────────┘  │
│  ┌───────────────────────────────────┐  │
│  │  flow_lookup_entries (查找条目池)  │  │
│  │    └── dpdk_flow_lookup_entry_t[0] │  │
│  │        flow_id = 1                │  │
│  │        next_index = ...           │  │
│  │        buffer_advance = ...       │  │
│  │    └── dpdk_flow_lookup_entry_t[1] │  │
│  │        ...                        │  │
│  └───────────────────────────────────┘  │
└─────────────────────────────────────────┘

通俗理解:

流表相关结构体就像"快递分拣中心"的"快速通道":

  • 通道档案 :dpdk_flow_entry_t,记录了"快速通道"的"档案"(VPP索引、标记、DPDK句柄)
  • 通道查找表 :dpdk_flow_lookup_entry_t,记录了"快速通道"的"查找表"(流表ID、下一跳、位置调整)

使用场景:

  • 流表创建 :创建dpdk_flow_entry_t并添加到flow_entries池
  • 流表匹配 :使用mark快速查找流表条目
  • 流表删除 :使用handle删除DPDK流表
  • 数据包处理 :匹配流表后,使用flow_lookup_entries获取下一跳和位置调整

3.7 数据结构之间的关系

作用和实现原理:

DPDK插件的各个数据结构之间存在清晰的层次关系和引用关系,形成了一个完整的数据结构体系。就像"快递分拣中心"的"组织架构",各个"部门"(数据结构)之间有明确的"上下级关系"和"协作关系"。

数据结构关系图:

复制代码
┌─────────────────────────────────────────────────────────────┐
│              DPDK插件数据结构关系图                           │
└─────────────────────────────────────────────────────────────┘

                    ┌─────────────────┐
                    │  dpdk_main_t    │  [全局主结构体]
                    │  (总档案室)     │
                    └────────┬────────┘
                             │
        ┌────────────────────┼────────────────────┐
        │                    │                    │
        ▼                    ▼                    ▼
┌──────────────┐   ┌──────────────────┐   ┌──────────────┐
│  devices[]   │   │ per_thread_data[]│   │  conf        │
│  (设备数组)  │   │ (每线程数据数组)  │   │ (配置)       │
└──────┬───────┘   └──────────────────┘   └──────────────┘
       │
       ▼
┌─────────────────────────────────────────┐
│  dpdk_device_t                          │
│  (设备档案)                             │
│  ┌───────────────────────────────────┐ │
│  │  rx_queues[]                      │ │
│  │    └── dpdk_rx_queue_t[0]         │ │
│  │    └── dpdk_rx_queue_t[1]         │ │
│  │    └── ...                        │ │
│  └───────────────────────────────────┘ │
│  ┌───────────────────────────────────┐ │
│  │  tx_queues[]                      │ │
│  │    └── dpdk_tx_queue_t[0]         │ │
│  │    └── dpdk_tx_queue_t[1]         │ │
│  │    └── ...                        │ │
│  └───────────────────────────────────┘ │
│  ┌───────────────────────────────────┐ │
│  │  flow_entries (pool)              │ │
│  │    └── dpdk_flow_entry_t          │ │
│  └───────────────────────────────────┘ │
│  ┌───────────────────────────────────┐ │
│  │  flow_lookup_entries (pool)        │ │
│  │    └── dpdk_flow_lookup_entry_t    │ │
│  └───────────────────────────────────┘ │
│  ┌───────────────────────────────────┐ │
│  │  driver                            │ │
│  │    └── dpdk_driver_t              │ │
│  └───────────────────────────────────┘ │
│  ┌───────────────────────────────────┐ │
│  │  conf                             │ │
│  │    └── dpdk_port_conf_t           │ │
│  └───────────────────────────────────┘ │
└─────────────────────────────────────────┘

关系详解:

1. dpdk_main_t → dpdk_device_t
  • 关系:一对多(一个主结构体包含多个设备)
  • 引用方式 :dpdk_main.devices[device_index]
  • 通俗理解:就像"总档案室"包含多个"设备档案"

关键代码 (src/plugins/dpdk/device/device.c:49):

c 复制代码
dpdk_main_t *dm = &dpdk_main;
dpdk_device_t *xd = vec_elt_at_index (dm->devices, hi->dev_instance);
2. dpdk_main_t → dpdk_per_thread_data_t
  • 关系:一对多(一个主结构体包含多个线程数据)
  • 引用方式 :dpdk_main.per_thread_data[thread_index]
  • 通俗理解:就像"总档案室"包含多个"工作人员工作台档案"

关键代码 (src/plugins/dpdk/device/device.c:286):

c 复制代码
dpdk_per_thread_data_t *ptd = vec_elt_at_index (dm->per_thread_data,
                                                  thread_index);
3. dpdk_device_t → dpdk_rx_queue_t
  • 关系:一对多(一个设备包含多个接收队列)
  • 引用方式 :dpdk_device.rx_queues[queue_index]
  • 通俗理解:就像"设备档案"包含多个"收货窗口档案"

关键代码 (src/plugins/dpdk/device/node.c:370):

c 复制代码
dpdk_rx_queue_t *rq = vec_elt_at_index (xd->rx_queues, queue_id);
4. dpdk_device_t → dpdk_tx_queue_t
  • 关系:一对多(一个设备包含多个发送队列)
  • 引用方式 :dpdk_device.tx_queues[queue_index]
  • 通俗理解:就像"设备档案"包含多个"发货窗口档案"

关键代码 (src/plugins/dpdk/device/device.c:173):

c 复制代码
dpdk_tx_queue_t *tq = vec_elt_at_index (xd->tx_queues, queue_id);
5. dpdk_device_t → dpdk_flow_entry_t
  • 关系:一对多(一个设备包含多个流表条目)
  • 引用方式 :pool_elt_at_index(dpdk_device.flow_entries, index)
  • 通俗理解:就像"设备档案"包含多个"快速通道档案"

关键代码 (src/plugins/dpdk/device/flow.c:754):

c 复制代码
fe = vec_elt_at_index (xd->flow_entries, *private_data);
6. dpdk_device_t → dpdk_flow_lookup_entry_t
  • 关系:一对多(一个设备包含多个流表查找条目)
  • 引用方式 :pool_elt_at_index(dpdk_device.flow_lookup_entries, index)
  • 通俗理解:就像"设备档案"包含多个"快速通道查找表条目"
7. dpdk_device_t → dpdk_driver_t
  • 关系:多对一(多个设备可以共享一个驱动)
  • 引用方式 :dpdk_device.driver
  • 通俗理解:就像多个"设备档案"可以共享一个"司机信息"
8. dpdk_device_t → dpdk_port_conf_t
  • 关系:一对一(一个设备有一个端口配置)
  • 引用方式 :dpdk_device.conf
  • 通俗理解:就像"设备档案"包含一个"配置档案"

数据访问流程示例:

示例1:通过设备索引访问设备

c 复制代码
// 1. 获取全局主结构体
dpdk_main_t *dm = &dpdk_main;

// 2. 通过设备索引访问设备
dpdk_device_t *xd = vec_elt_at_index (dm->devices, device_index);

// 3. 访问设备的接收队列
dpdk_rx_queue_t *rq = vec_elt_at_index (xd->rx_queues, queue_index);

示例2:通过线程索引访问每线程数据

c 复制代码
// 1. 获取全局主结构体
dpdk_main_t *dm = &dpdk_main;

// 2. 获取线程索引
u32 thread_index = vlib_get_thread_index ();

// 3. 通过线程索引访问每线程数据
dpdk_per_thread_data_t *ptd = vec_elt_at_index (dm->per_thread_data,
                                                  thread_index);

// 4. 使用每线程数据进行批量接收
n = rte_eth_rx_burst (xd->port_id, queue_id, ptd->mbufs, n_to_rx);

通俗理解:

数据结构之间的关系就像"快递分拣中心"的"组织架构":

  • 总档案室 (dpdk_main_t):包含所有"部门"(数据结构)的"档案"
  • 设备档案柜 (devices[]):存放所有"收货设备"(网卡)的"档案"
  • 工作台档案柜 (per_thread_data[]):存放所有"工作人员"(线程)的"工作台"信息
  • 设备档案 (dpdk_device_t):包含"设备"的所有信息
    • 收货窗口列表 (rx_queues[]):包含多个"收货窗口"(接收队列)
    • 发货窗口列表 (tx_queues[]):包含多个"发货窗口"(发送队列)
    • 快速通道档案 (flow_entries):包含多个"快速通道"(流表条目)
    • 司机信息 (driver):指向"司机信息"(驱动信息)
    • 配置档案 (conf):包含"设备"的"配置信息"

设计优势:

  1. 层次清晰:数据结构层次分明,便于理解和维护
  2. 访问高效:通过索引直接访问,O(1)时间复杂度
  3. 内存局部性:相关数据集中存储,提高缓存命中率
  4. 扩展性好:动态数组支持动态扩展,适应不同规模的系统

3.8 本章总结

核心数据结构总结:

数据结构 作用 关键字段 重要性
dpdk_device_t 代表一个DPDK设备 rx_queues、tx_queues、port_id、stats ⭐⭐⭐⭐⭐
dpdk_rx_queue_t 代表一个接收队列 buffer_pool_index、queue_index、efd ⭐⭐⭐⭐
dpdk_tx_queue_t 代表一个发送队列 lock、queue_index ⭐⭐⭐⭐
dpdk_per_thread_data_t 每线程数据 mbufs、buffers、next、etype、flags ⭐⭐⭐⭐⭐
dpdk_main_t 全局主结构体 devices、per_thread_data、conf ⭐⭐⭐⭐⭐
dpdk_flow_entry_t 流表条目 flow_index、mark、handle ⭐⭐⭐
dpdk_flow_lookup_entry_t 流表查找条目 flow_id、next_index、buffer_advance ⭐⭐⭐

数据结构设计特点:

  1. 缓存行对齐 :使用CLIB_CACHE_LINE_ALIGN_MARK避免false sharing
  2. 动态数组 :使用VPP的vec机制实现动态数组
  3. 内存池 :使用VPP的pool机制实现内存池(流表条目)
  4. 索引访问:通过索引直接访问,O(1)时间复杂度
  5. 线程安全:TX队列使用锁保护,RX队列通常单线程访问

后续章节预告:

  • 第4章:详细分析模块初始化流程,讲解这些数据结构是如何创建和初始化的
  • 第8章:详细讲解dpdk-input节点核心处理,讲解如何使用这些数据结构接收数据包
  • 第15章:详细讲解dpdk-output节点核心处理,讲解如何使用这些数据结构发送数据包

相关源码文件:

  • src/plugins/dpdk/device/dpdk.h - 数据结构定义
  • src/plugins/dpdk/device/dpdk_priv.h - 私有数据结构定义

相关推荐
承渊政道2 小时前
Linux网络学习【Reactor反应堆模式】
linux·网络·学习·ubuntu·reactor
讯捷蓝达4 小时前
戴尔R720xd服务器卡死在 “Configuring Memory...Done“,换了内存还是卡?问题根本不在内存
运维·服务器
数字新视界6 小时前
动环监控信创适配在智能机房管理中的应用价值与未来展望
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
爱和冰阔落6 小时前
【Linux网络】TCP 服务器如何并发处理多个客户端?从 fork、僵尸进程到双重 fork
linux·运维·服务器·网络
lisanmengmeng7 小时前
alertmanger 标签 rules 打上分组标签
linux·运维·服务器
严肃小白兔7 小时前
Cloudflare 更新 API Token 管理:从最小权限到自动化凭据安全
网络·安全·自动化
Android系统攻城狮8 小时前
Linux FFmpeg深度解析之av_write_frame调用流程与实战(三十三)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
上火的金鱼妹8 小时前
k8s生产实际问题排查(网关,数据库读写等)
运维·服务器
孙克旭_8 小时前
k8s基础组件与创建资源
运维·docker·容器·kubernetes
孙克旭_8 小时前
云原生基础之Containerd容器运行时
运维·云原生