网络设备功能混乱以及如何从中脱身

作者

Michał Mirosław <mirq-linux@rere.qmqm.pl>

第一部分:功能集

网络卡仅仅接收并原样发送数据包的日子早已远去。今天的设备添加了多种功能和错误(读作:卸载),以减轻操作系统各种任务的负担,例如生成和检查校验和、分割数据包、对其进行分类。这些功能及其状态在 Linux 内核世界中通常被称为网络设备功能。

目前,有三组功能与驱动程序相关,一组供网络核心内部使用。

  1. netdev->hw_features 集合包含的功能的状态可能通过用户的请求为特定设备更改(启用或禁用)。此集合应在 ndo_init 回调中初始化,之后不再更改。

  2. netdev->features 集合包含当前为设备启用的功能。这应该仅由网络核心更改,或者在 ndo_set_features 回调的错误路径中更改。

  3. netdev->vlan_features 集合包含其状态由子 VLAN 设备继承的功能(限制 netdev->features 集合)。目前,无论是在硬件还是软件中剥离或插入标签,这都用于所有 VLAN 设备。

  4. netdev->wanted_features 集合包含用户请求的功能集。每当它或某些特定于设备的条件发生更改时,此集合都会通过 ndo_fix_features 回调进行筛选。此集合是网络核心的内部集合,不应在驱动程序中引用。

第二部分:控制启用的功能

当需要更改当前功能集 (netdev->features) 时,会计算新的集合,并通过调用 ndo_fix_features 回调和 netdev_fix_features() 进行筛选。如果结果集与当前集不同,则会将其传递给 ndo_set_features 回调,并且(如果回调返回成功)替换存储在 netdev->features 中的值。之后,每当当前集可能已更改时,都会发出 NETDEV_FEAT_CHANGE 通知。

以下事件会触发重新计算
  1. 设备注册,在 ndo_init 返回成功之后

  2. 用户请求更改功能状态

  3. 调用 netdev_update_features()

在持有 rtnl_lock 的情况下调用 ndo_*_features 回调。缺少的回调将被视为始终返回成功。

想要触发重新计算的驱动程序必须在持有 rtnl_lock 的情况下调用 netdev_update_features() 来完成此操作。不应从 ndo_*_features 回调执行此操作。除了通过 ndo_fix_features 回调之外,驱动程序不应修改 netdev->features。

第三部分:实现提示

  • ndo_fix_features

此处应解析功能之间的所有依赖关系。网络核心施加的限制(如 netdev_fix_features() 中编码的那样)可以进一步减少结果集。因此,当不满足依赖关系时,禁用功能比强制依赖关系更安全。

此回调不应修改硬件或驱动程序状态(应是无状态的)。它可以在连续的 ndo_set_features 调用之间被多次调用。

回调不得更改 NETIF_F_SOFT_FEATURES 或 NETIF_F_NEVER_CHANGE 集中包含的功能。例外情况是 NETIF_F_VLAN_CHALLENGED,但必须小心,因为更改不会影响已配置的 VLAN。

  • ndo_set_features

应重新配置硬件以匹配传递的功能集。除非发生无法在 ndo_fix_features 中可靠检测到的一些错误情况,否则不应更改该集合。在这种情况下,回调应更新 netdev->features 以匹配结果硬件状态。返回的错误不会(并且不能)传播到任何地方,除了 dmesg。(注意:成功返回为零,>0 表示静默错误。)

第四部分:功能

有关当前功能列表,请参阅 include/linux/netdev_features.h。本节描述其中一些功能的语义。

  • 传输校验和

有关完整说明,请参阅 include/linux/skbuff.h 顶部附近的注释。

注意:NETIF_F_HW_CSUM 是 NETIF_F_IP_CSUM + NETIF_F_IPV6_CSUM 的超集。这意味着设备可以在数据包中的任何位置填充 TCP/UDP 类的校验和,无论那里可能有哪些标头。

  • 传输 TCP 分段卸载

NETIF_F_TSO_ECN 表示硬件可以正确拆分设置了 CWR 位的数据包,无论是 TCPv4(当启用 NETIF_F_TSO 时)还是 TCPv6 (NETIF_F_TSO6)。

  • 传输 UDP 分段卸载

NETIF_F_GSO_UDP_L4 接受带有超出 gso_size 的有效负载的单个 UDP 标头。在分段时,它会在 gso_size 边界上分割有效负载,并复制网络和 UDP 标头(如果小于 gso_size,则修复最后一个标头)。

  • 从高内存传输 DMA

在相关平台上,NETIF_F_HIGHDMA 表示 ndo_start_xmit 可以处理高内存中的 frags 的 skb。

  • 传输分散/收集

这些功能表明 ndo_start_xmit 可以处理分段的 skb:NETIF_F_SG --- 分页的 skb (skb_shinfo()->frags),NETIF_F_FRAGLIST --- 链接的 skb (skb->next/prev 列表)。

  • 软件功能

NETIF_F_SOFT_FEATURES 中包含的功能是网络堆栈的功能。驱动程序不应基于这些功能更改行为。

  • VLAN 挑战

对于无法处理 VLAN 标头的设备,应设置 NETIF_F_VLAN_CHALLENGED。一些驱动程序设置此项是因为卡无法处理更大的 MTU。[FIXME:这些情况可以通过仅允许减少 MTU 的 VLAN 来在 VLAN 代码中修复。但这可能没有用。]

  • rx-fcs

这请求网卡将以太网帧校验和 (FCS) 附加到 skb 数据的末尾。这允许嗅探器和其他工具读取网卡在接收数据包时记录的 CRC。

  • rx-all

这请求网卡接收所有可能的帧,包括出错的帧(例如错误的 FCS 等)。当嗅探链路上的坏包时,这可能会有所帮助。如果同时将某些网卡置于正常的混杂模式,它们可能会接收到更多数据包。

  • rx-gro-hw

这请求网卡启用硬件 GRO(通用接收卸载)。硬件 GRO 基本上是 TSO 的完全相反,并且通常比硬件 LRO 更严格。硬件 GRO 合并的数据包流必须可以通过 GSO 或 TSO 重新分段回完全原始的数据包流。硬件 GRO 依赖于 RXCSUM,因为硬件成功合并的每个数据包也必须由硬件验证校验和。

  • hsr-tag-ins-offload

对于自动插入 HSR(高可用性无缝冗余)或 PRP(并行冗余协议)标签的设备,应设置此项。

  • hsr-tag-rm-offload

对于自动删除 HSR(高可用性无缝冗余)或 PRP(并行冗余协议)标签的设备,应设置此项。

  • hsr-fwd-offload

对于在硬件中将 HSR(高可用性无缝冗余)帧从一个端口转发到另一个端口的设备,应设置此项。

  • hsr-dup-offload

对于在硬件中自动复制传出的 HSR(高可用性无缝冗余)或 PRP(并行冗余协议)标签的帧的设备,应设置此项。