Devlink DPIPE¶
背景¶
在执行硬件卸载过程中,许多硬件细节无法呈现。这些细节对于调试非常有用,devlink-dpipe
提供了一种标准化的方式来提供对卸载过程的可见性。
例如,Linux内核使用的路由最长前缀匹配(LPM)算法可能与硬件实现不同。管道调试API(DPIPE)旨在以通用方式向用户提供对ASIC管道的可见性。
硬件卸载过程应该以用户无法区分硬件与软件实现的方式进行。在这个过程中,硬件细节被忽略。实际上,这些细节可能具有很多意义,应该以某种标准方式公开。
当一个人希望将整个网络堆栈的控制路径卸载到交换机ASIC时,这个问题变得更加复杂。由于硬件和软件模型之间的差异,一些过程无法正确表示。
一个例子是内核的LPM算法,在许多情况下,它与硬件实现有很大差异。配置API是相同的,但人们不能依赖转发信息库(FIB)看起来像硬件中的Level Path Compression trie(LPC-trie)。
在许多情况下,仅基于内核转储来分析系统故障可能不够。通过将此数据与有关底层硬件的补充信息结合起来,可以使调试更容易;此外,该信息在调试性能问题时可能很有用。
概述¶
devlink-dpipe
接口弥补了这个差距。硬件的管道被建模为匹配/动作表的图。每个表代表一个特定的硬件块。这种模型并不新鲜,首先被P4语言使用。
传统上,它已被用作硬件配置的替代模型,但 devlink-dpipe
接口将其用于可见性目的,作为一种标准的补充工具。devlink-dpipe
中的系统视图应根据标准配置工具所做的更改而更改。
例如,使用三态内容寻址存储器(TCAM)实现访问控制列表(ACL)是很常见的。TCAM存储器可以分为TCAM区域。复杂的TC过滤器可以有多个具有不同优先级和不同查找键的规则。另一方面,硬件TCAM区域具有预定义的查找键。使用TCAM引擎卸载TC过滤器规则可能导致多个TCAM区域以链式方式互连(这可能会影响数据路径延迟)。为了响应新的TC过滤器,应创建描述这些区域的新表。
模型¶
DPIPE
模型引入了几个对象
头部
表
条目
header
描述数据包格式,并为数据包中的字段提供名称。table
描述硬件块。entry
描述特定表的实际内容。
硬件管道不是端口特定的,而是描述整个ASIC。因此,它与 devlink
基础设施的顶部相关联。
驱动程序可以在运行时注册和注销表,以支持动态行为。这种动态行为对于描述像TCAM区域这样的硬件块是强制性的,TCAM区域可以动态分配和释放。
devlink-dpipe
通常不用于配置。例外情况是特定表的硬件计数。
以下命令用于从用户空间获取 dpipe
对象
table_get
:接收表的描述。
headers_get
:接收设备支持的头部。
entries_get
:接收表的当前条目。
counters_set
:启用或禁用表上的计数器。
表¶
驱动程序应该为每个表实现以下操作
matches_dump
:转储支持的匹配项。
actions_dump
:转储支持的动作。
entries_dump
:转储表的实际内容。
counters_set_update
:同步硬件与启用或禁用的计数器。
头部/字段¶
以类似于 P4 的方式,头部和字段用于描述表的行为。标准协议头部和特定 ASIC 元数据之间略有不同。协议头部应在 devlink
核心 API 中声明。另一方面,ASIC 元数据是驱动程序特定的,应在驱动程序中定义。此外,每个驱动程序特定的 devlink 文档文件都应记录它实现的驱动程序特定的 dpipe
头部。头部和字段由枚举标识。
为了提供进一步的可见性,一些 ASIC 元数据字段可以映射到内核对象。例如,内部路由器接口索引可以直接映射到网络设备 ifindex。由不同虚拟路由和转发(VRF)表使用的 FIB 表索引可以映射到内部路由表索引。
匹配¶
匹配保持原始状态并接近硬件操作。由于这正是我们希望完整描述的过程,因此不支持像 LPM 这样的匹配类型。匹配示例
field_exact
:完全匹配特定字段。
field_exact_mask
:在屏蔽后完全匹配特定字段。
field_range
:匹配特定范围。
为了识别特定字段,应指定头部和字段的 ID。此外,应指定头部索引,以便区分数据包中同一类型的多个头部(隧道)。
动作¶
与匹配类似,动作保持原始状态并接近硬件操作。例如
field_modify
:修改字段值。
field_inc
:增加字段值。
push_header
:添加头部。
pop_header
:删除头部。
条目¶
可以根据需要转储特定表的条目。每个条目都由一个索引标识,其属性由匹配/动作值和特定计数器的列表描述。通过转储表内容,可以解决表之间的交互。
抽象示例¶
以下是 Mellanox Spectrum ASIC 的 L3 部分的抽象模型示例。这些块按照它们在管道中出现的顺序描述。以下示例中的表大小不是实际的硬件大小,仅用于演示目的。
LPM¶
LPM 算法可以实现为哈希表列表。每个哈希表包含具有相同前缀长度的路由。列表的根是/32,如果未命中,硬件将继续到下一个哈希表。搜索的深度将影响数据路径延迟。
如果命中,该条目包含有关管道下一阶段的信息,该阶段解析 MAC 地址。下一阶段可以是直接连接路由的本地主机表,也可以是下一跳的邻接表。meta.lpm_prefix
字段用于连接两个 LPM 表。
table lpm_prefix_16 {
size: 4096,
counters_enabled: true,
match: { meta.vr_id: exact,
ipv4.dst_addr: exact_mask,
ipv6.dst_addr: exact_mask,
meta.lpm_prefix: exact },
action: { meta.adj_index: set,
meta.adj_group_size: set,
meta.rif_port: set,
meta.lpm_prefix: set },
}
本地主机¶
在本地路由的情况下,LPM 查找已经解析了出口路由器接口(RIF),但确切的 MAC 地址未知。本地主机表是一个哈希表,它将输出接口 ID 与目标 IP 地址组合作为键。结果是 MAC 地址。
table local_host {
size: 4096,
counters_enabled: true,
match: { meta.rif_port: exact,
ipv4.dst_addr: exact},
action: { ethernet.daddr: set }
}
邻接¶
在远程路由的情况下,此表执行 ECMP。LPM 查找导致 ECMP 组大小和索引,该索引用作此表中的全局偏移量。同时,生成数据包的哈希。基于 ECMP 组大小和数据包的哈希,生成本地偏移量。多个 LPM 条目可以指向同一个邻接组。
table adjacency {
size: 4096,
counters_enabled: true,
match: { meta.adj_index: exact,
meta.adj_group_size: exact,
meta.packet_hash_index: exact },
action: { ethernet.daddr: set,
meta.erif: set }
}
ERIF¶
如果出口 RIF 和目标 MAC 已被先前的表解析,则此表执行多个操作,例如 TTL 减少和 MTU 检查。然后做出转发/丢弃的决定,并根据数据包的类型(广播、单播、多播)更新端口 L3 统计信息。
table erif {
size: 800,
counters_enabled: true,
match: { meta.rif_port: exact,
meta.is_l3_unicast: exact,
meta.is_l3_broadcast: exact,
meta.is_l3_multicast, exact },
action: { meta.l3_drop: set,
meta.l3_forward: set }
}