3. DPAA2对象模型入门

DPAA2(Data Path Acceleration Architecture 2)与传统的“寄存器 + 描述符环”网络驱动有本质区别:它把硬件资源抽象成一组可被内核和用户态共同管理的对象(Object),每个对象由 MC(Management Complex)固件创建、配置和销毁,软件通过命令接口操作对象,通过对象之间的连接关系组织数据面。理解 DPAA2,第一步不是写驱动,而是搞清楚“有哪些对象、各自负责什么、谁依赖谁、怎么发现它们”。

一、对象模型的核心思想

DPAA2 中的对象不是 C 语言结构体,而是 MC 固件内部维护的实体,软件侧只持有一个 32 位对象 ID(Object ID)。对象 ID 的高位标识对象类型,低位标识实例编号。软件通过 dprc 容器对象向 MC 发送命令,完成对象的创建、查询、连接和销毁。这种设计带来两个直接后果:一是资源生命周期由 MC 统一管理,驱动崩溃不会直接破坏硬件状态;二是对象可以跨内核态与用户态共享,用户态通过 UIO/VFIO 映射对象后可直接操作数据面队列。

可以把 DPAA2 对象模型类比为“硬件资源版的面向对象系统”:DPRC 是容器/命名空间,DPNI 是网络接口,DPBP 是缓冲区池,DPIO 是 I/O 门户,DPCON 是消息上下文,DPMCP 是管理命令门户。它们之间通过“连接”建立引用关系,而不是通过全局变量或硬编码地址。

二、主要对象职责

对象全称核心职责典型数量
DPRCData Path Resource Container资源容器,管理其他对象的创建、查询、连接与销毁,是访问 MC 的入口1 个根容器 + 若干子容器
DPNIData Path Network Interface网络接口对象,对应一个 MAC/PHY 端口,承载收发队列、分流规则、VLAN 等配置每端口 1 个
DPBPData Path Buffer Pool缓冲区池,管理帧缓冲区的分配与回收,供 DPNI 收发使用每接口 1~2 个
DPIOData Path I/O PortalI/O 门户,软件与数据面队列交互的入口,包含 QMan 与 BMan 门户每 CPU 核 1 个
DPCONData Path Concentrator消息上下文,把多个 DPIO 的通知聚合到一个中断,减少中断风暴按需创建
DPMCPData Path Management Command Portal管理命令门户,软件向 MC 发送管理命令的通道每容器若干

其中 DPNI 是最贴近业务的“网卡对象”。它内部包含 RX/TX 队列、分流表、MAC 地址表、VLAN 过滤表等。DPBP 为 DPNI 提供帧缓冲区,DPIO 为软件提供访问这些队列的门户。DPRC 则像“资源管理器”,所有对象都挂在某个 DPRC 下,通过 DPRC 的命令接口进行发现和连接。

三、对象发现流程

系统启动后,软件不能假设对象已经存在,必须通过 DPRC 逐层发现。典型流程如下:

  1. 打开根 DPRC,获取容器句柄。
  2. 查询 DPRC 属性,得到该容器内对象数量、类型分布、可用门户数等。
  3. 遍历对象列表,按类型筛选出 DPNI、DPBP、DPIO、DPCON、DPMCP。
  4. 对每个对象查询其属性,例如 DPNI 的 MAC 地址、队列数,DPBP 的缓冲区大小。
  5. 建立连接:把 DPNI 与 DPBP、DPIO 关联,把 DPIO 与 DPCON 关联。
  6. 将对象映射到软件地址空间,准备数据面收发。

下面这张图展示了对象之间的依赖与发现路径。注意箭头方向表示“发现/连接”的方向,而不是数据流方向。

DPAA2 对象发现与连接关系 DPRC 资源容器 / MC 命令入口 DPMCP 管理命令门户 DPNI 网络接口对象 DPBP 缓冲区池 DPIO I/O 门户 DPCON 消息上下文 连接 队列绑定 通知聚合 命令通道 发现顺序:DPRC → 查询对象列表 → 按类型筛选 → 查询属性 → 建立连接 → 映射地址空间

四、对象 ID 与命令接口

每个对象都有一个唯一的对象 ID,格式为 type:instance。例如 dpni.0 表示第一个 DPNI 对象。软件通过 dprc 的 get_obj 命令获取对象句柄,再通过 get_attributes 读取属性。以下是一个典型的发现命令序列(示意):

# 打开根 DPRC
dprc_open("/dev/dprc.0", &root);

# 查询容器内对象数量
dprc_get_attributes(root, &attr);
printf("objects: %d\n", attr.num_objects);

# 遍历对象,筛选 DPNI
for (i = 0; i < attr.num_objects; i++) {
    dprc_get_obj(root, i, &obj);
    if (obj.type == DPRC_OBJ_DPNI) {
        dprc_get_obj_region(root, i, "dpni", &region);
    }
}

这段代码展示了发现流程的核心:先打开容器,再遍历对象列表,按类型筛选,最后获取对象区域信息用于地址映射。实际驱动中还会进一步查询 DPNI 的队列数、MAC 地址、DPBP 的缓冲区大小等,以便初始化数据面。

五、连接关系与数据面组织

对象之间的连接不是“自动”的,需要软件显式建立。例如,DPNI 的 RX 队列需要绑定到某个 DPIO 门户,才能被软件轮询或中断处理;DPNI 的 TX 队列需要从 DPBP 获取缓冲区,才能发送帧。DPIO 与 DPCON 的连接则用于把多个门户的通知聚合到一个中断上,减少 CPU 中断负载。

一个典型的单端口配置可能包含:1 个 DPNI、1 个 DPBP、每 CPU 核 1 个 DPIO、1 个 DPCON、若干 DPMCP。软件发现这些对象后,按“DPNI → DPBP”“DPNI → DPIO”“DPIO → DPCON”的顺序建立连接,最后把 DPNI 的队列映射到用户态或内核态,开始收发。

发现流程中,DPRC 是唯一必须首先打开的对象。没有 DPRC,就无法访问其他任何对象。因此驱动初始化时,第一步永远是 dprc_open,然后才是遍历和连接。
对象 ID 在系统重启或容器重建后可能变化,软件不应硬编码对象 ID,而应在每次启动时重新发现。硬编码 dpni.0 在单端口场景可能侥幸工作,但在多端口、多容器或热插拔场景下会直接失败。

六、小结与检查要点

掌握 DPAA2 对象模型,关键不是记住每个对象的全称,而是理解“容器—对象—连接”三层结构:DPRC 是容器,DPNI/DPBP/DPIO/DPCON/DPMCP 是对象,连接关系决定数据面如何组织。发现流程则是从 DPRC 出发,逐层查询、筛选、连接、映射。实际调试时,可以用 ls /dev/dprc.* 查看容器设备节点,用 restool 工具查询对象列表和属性,验证发现结果是否符合预期。

检查要点:DPRC 是否成功打开;对象数量与类型是否与硬件配置一致;DPNI 是否绑定到正确的 DPBP 和 DPIO;DPIO 是否与 DPCON 连接;对象区域是否成功映射。任何一步失败,都会导致数据面无法启动或收发异常。