本规范的贡献均根据开放网络基金会贡献者许可协议(“OWF CLA 1.0”)(“贡献许可”)中规定的条款和条件进行:Google 本规范的使用受开放网络基金会最终规范协议(“OWFa 1.0”)中规定的条款和条件的约束
该规范符合效率原则。Falcon 通过结合三个关键见解来实现高性能,这些见解可在高带宽但有损的标准以太网数据中心网络中实现低延迟。细粒度的硬件辅助往返时间 (RTT) 测量与灵活的每流硬件强制流量整形以及快速准确的数据包重传相结合,并具有多路径功能和 PSP 加密的 Falcon 连接。在此基础上,Falcon 从头开始设计为多协议传输,能够支持具有广泛不同性能要求和应用程序语义的上层协议 (ULP)。ULP 映射层不仅提供与 Infiniband Verbs RDMA 和 NVMe ULP 的开箱即用兼容性,而且还包括对软件堆栈可扩展(warehouse-scale)的应用程序至关重要的其他创新,例如灵活排序(flexible ordering)语义和优雅的错误处理。最后但并非最不重要的一点是,硬件和软件是共同设计的,以协同工作以帮助实现所需的高消息速率、低延迟和高带宽属性,同时保持可编程性和持续创新的灵活性。 # # 2.3 影响
该规范符合影响的宗旨,引入了一项有助于业界实现以太网现代化的新技术。Falcon 提供了一种有用的解决方案来处理具有高突发带宽、高每秒操作数和低延迟的苛刻工作负载,包括大规模 AI / ML 训练、高性能计算和实时分析。
该规范符合scale的宗旨,从头开始设计以在高带宽但有损的以太网数据中心网络中提供高带宽和低延迟。此外,它由大规模交付的经过生产验证的技术组成,包括 Carousel、Snap、Swift、保护性负载平衡和拥塞信令 (CSIG)。
该规范遵循可持续性原则,提供高效的以太网传输技术,最大限度地减少以太网网络中的重传和其他浪费的精力和能源。此外,该技术允许在标准以太网上运行各种高性能工作负载。
2024年2月14日, 发布第一版, 版本为V0.9
本规范描述了为 RDMA 操作和 NVMe 命令提供可靠传输的 Falcon 传输协议。 以下内容不属于该范围: ● 支持 Falcon 的 RDMA 和 NVMe 层更改的详细信息。 ● 应用程序使用 Falcon 的详细信息。 ● 在软件堆栈或硬件 NIC 中实现 Falcon 的详细信息
本规范描述了为 RDMA 操作和 NVMe 命令提供可靠传输的 Falcon 协议。Falcon 是一种面向连接的请求-响应传输协议,可为 RDMA 和 NVMe 等上层协议 (ULP) 提供端到端可靠传输和每个连接的安全性。Falcon 连接可以是有序的,也可以是无序的。Falcon 包括一个可编程的拥塞控制引擎,可用于实现最先进的拥塞控制算法。Falcon 使用 Paddywhack 安全协议 (PSP: Paddywhack Security Protocol) 或 IPSEC ESP 协议在每个连接的基础上对所有 ULP 数据进行身份验证和加密。以下各节将进一步详细描述 Falcon 的关键架构概念
上图显示了 Falcon 的协议分层。Falcon 本身由两个子层组成:事务层和数据包传递层。
Falcon 不公开自己的 HW/SW 接口。ULP 负责实现 HW/SW 接口、操作处理、完成通知和端到端流量控制。ULP 操作 (Op) 可以映射到一个或多个 Falcon 事务。Falcon 事务由请求和响应定义。Falcon 通过网络发送和接收一个或多个数据包来可靠地完成事务。Falcon 在完成事务后通过响应或完成通知 ULP。Falcon 事务可以是推送请求或拉取请求。ULP Ops 映射到推送请求或拉取请求。Falcon 的推送和拉取请求提供了基本原语,可灵活地将不同的 ULP Ops 映射到 Falcon 事务。例如,RDMA 读取和原子操作被映射到拉取请求(Pull),而 RDMA 发送和写入操作被映射到推送请求(Push)。ULP Op 也可以映射到一组 Falcon 原语。NVMe 读取和写入操作到 Falcon 事务的映射就是一个例子。下图显示了 ULP Ops、Falcon 事务和数据包之间的关系
事务子层为 ULP 提供事务接口。Falcon 事务由 ULP 向 Falcon 发出的请求和 Falcon 向 ULP 发送的响应组成,表示事务结束。ULP 可以生成两种类型的事务:拉取和推送。所有 ULP 操作都必须映射到推送或拉取事务。例如,RDMA 读取和原子操作码映射到拉取事务,而 RDMA 发送和写入操作码映射到推送事务。每个到 Falcon 的事务都有一个请求序列号 (RSN),用于跟踪事务状态和排序
推送事务(例如写入操作)包括发起方 ULP 向 Falcon 发送推送请求,然后 Falcon 向目标 Falcon 发送推送数据包。然后,目标 Falcon 将其转发给目标 ULP,目标 ULP 确认数据。最后,目标 Falcon 通过 ack 数据包或搭载 ack 将此确认 (ACK) 发送回发起方 Falcon,并通过完成将其传送给发起方 ULP。由于目标的 Falcon 数据包传送层仅在目标 ULP 确认推送请求时才确认推送数据包,因此在 ULP 层端到端提供可靠性
拉取事务(例如读取操作)由发起方 ULP 向 Falcon 发送拉取请求组成,Falcon 将拉取请求数据包传输到目标 Falcon。目标 Falcon 将请求转发到目标 ULP,目标 ULP 使用拉取响应数据进行响应。目标 Falcon 通过拉取数据包将此响应传输回发起方 Falcon,发起方 Falcon 通过将拉取响应发送回发起方 ULP 来完成事务
数据包传送层的两个主要功能是可靠的数据包传送和从 Falcon 发送方到 Falcon 接收方的拥塞控制;它们的详细机制将在本说明书后面阐述。数据包传送子层使用两个滑动窗口来分离请求和数据。请求滑动窗口负责可靠地传送拉取请求,数据滑动窗口负责可靠地传送拉取数据和推送数据。需要两个滑动窗口来避免协议死锁,如“8.2.1死锁避免”部分所述。上图说明了滑动窗口协议的主要概念,该协议在两个窗口中大部分独立运行。发送方为每个数据包分配一个数据包序列号 (PSN),范围为 0 到 2 的32 -1次方。PSN 每传输一个数据包就增加 1,并包含在 Falcon 基本标头中。在接收方处,每个数据包有两种状态:已接收和已确认。数据包确认与 ULP 确认相关联,因此接收的数据包可能尚未确认。接收方通过发送 ACK 数据包将数据包的接收和确认状态传达给发送方。接收方发送的 ACK 包含已成功接收的序列号的指示。接收方可以使用累积 ACK,也可以将 ACK 搭载到流向相反方向的数据包上。如果发生数据包丢失或重新排序,接收方将发送扩展 ACK (EACK),其中包含已接收/已确认 PSN 的位图,以便发送方可以检测到丢失或重新排序并重新传输丢失的数据包。发送方始终维护一组允许发送的序列号。这些序列号定义一个滑动窗口(如上图中的灰色框所示)。窗口的左边缘是基本序列号 (BSN),表示已发送但尚未被接收方确认的最旧数据包。窗口的右边缘由 BSN + Tx 窗口大小定义。仅当分配给该数据包的 PSN 位于发送方的滑动窗口内时,发送方才被允许发送数据包。下一个序列号 (NSN) 表示必须分配给要传输的数据包的下一个 PSN 值
发起者和目标(Initiator and target)是事务层概念,而发送者和接收者(transmitter and receiver)是数据包传递层概念。发起者(和目标)既可以是发送者,也可以是接收者。例如,Pull 请求的发起者是 Pull 请求数据包的发送者,但也是 Pull 数据包的接收者;目标是 Pull 请求数据包的接收者,也是 Pull 数据包的发送者
连接是一种端到端Fabric,用于描述两个 Falcon 对等体之间的双向通信通道。数据包是跨连接通信的基本单位。数据包的长度最多可达 MTU 大小。连接由一对连接 ID (CID) 标识,每个方向一个。CID 由接收方在连接设置过程中分配,没有全局意义。接收方分配的 CID 称为目标 CID,并包含在 Falcon 标头中。上图显示了 CID 分配的示例。A 和 B 之间的连接使用 CID 值 5 从 A 到 B,使用 CID 值 10 从 B 到 A。A 和 C 之间的连接使用 CID 值 5 从 A 到 C,使用 CID 值 11 从 C 到 A。CID 值在发送方端不需要唯一,如本例所示,其中 A 使用相同的 CID 值 5 与 B 和 C 通信。为了消除两个连接之间的歧义,发送方端使用本地唯一标识符,称为源 CID。 Falcon 标头中未携带源 CID
Falcon 使用每个连接的拥塞控制。Falcon 数据路径硬件负责测量拥塞信号并根据每个连接强制执行计算出的拥塞窗口和速率。拥塞控制算法本身是在与主 Falcon 数据路径分开的速率更新引擎 (RUE) 中实现的。Falcon 在某些事件(例如 ACK/NACK 接收、数据包重传等)上触发 RUE 操作。RUE 计算拥塞控制输出(拥塞窗口,又称 cwnd 和速率)以响应这些事件并将它们返回给 Falcon。Falcon 和 RUE 之间的这种架构分离允许实现一类拥塞控制算法,例如下面描述的 Swift,而无需更改主 Falcon 数据路径。
Swift(https://dl.acm.org/doi/10.1145/3387514.3406591) 是一种众所周知的基于延迟的数据中心拥塞控制,可在高带宽下提供低尾部延迟和接近零的数据包丢失。以下组合使 Swift 成为一种高性能拥塞控制算法:
● 利用每个数据包的 NIC 硬件时间戳,并有效地将其用于拥塞窗口和速率计算。
● 快速反应算法处理网络中的大量传入和流冲突。
● 通过分离Fabric和终端主机组件的 cwnd(或速率)计算,消除歧义并适当响应Fabric以及终端主机/NIC 拥塞。
负载平衡对于最大限度地减少Fabric中的拥塞热点也至关重要。保护性负载平衡(https://dl.acm.org/doi/pdf/10.1145/3544216.3544226)是一种有效的负载平衡算法,它利用了 Swift 的拥塞信号。
Falcon 支持 ULP 的错误处理语义。特别是,Falcon 支持“接收方未准备好 (RNR)”指示的 ULP 概念。RNR 指示允许目标 ULP 指定必须重试事务的时间。Falcon 实现了对 ULP 透明的 RNR 重试。推送事务从发起方重试。拉取事务从目标方重试。RNR NACK 处理将在后面的章节中更详细地描述。此外,Falcon 还支持更优雅的恢复,使用“完成错误并继续 (CIE: complete in error and continue)”语义。CIE 语义允许更优雅地处理各种错误(例如 RDMA ULP 中的内存保护错误),而无需在 ULP 中将此类错误视为致命错误。目标 ULP 可以使用 CIE 指示回复推送或拉取事务。对于拉取事务,ULP 必须返回带有 CIE 错误代码的零长度拉取响应。 Falcon 处理零长度拉动响应的方式与处理任何其他拉动响应的方式相同。对于推送事务,ULP 必须返回带有错误代码的显式 CIE 指示。Falcon 将从目标向发起者传输带有 ULP 错误代码的 CIE NACK 数据包。发起者将错误地完成事务并将 CIE 错误代码返回给发起者 ULP。尽管 RNR NACK 和 CIE NACK 是不可靠的数据包,但 Falcon 具有从丢失的 NACK 中恢复的机制。在 Falcon 块本身中,错误处理完全在硬件中实现。错误处理的目标是将影响范围限制在出现错误的连接上,并且不影响其他连接的性能。使用数据包超时和重传来处理数据包丢失等协议错误。超过可配置阈值的重复重传失败将发送给软件,以便采取纠正措施,例如重新建立连接。在可配置的超时后,Falcon 中分配给事务的所有资源都可以回收。Falcon 中的所有资源都可以视为“软状态”,因为资源清理发生在发起方和目标方超时之后
Falcon 使用 Paddywhack 安全协议 (PSP) 或 IP-SEC ESP 协议对通过连接传输的所有数据进行身份验证和加密。每个连接都与两个安全关联 (SA) 相关联,每个方向一个。多个连接可以共享同一个 SA。在传输时,从连接状态获取安全关联数据库 (SADB) 中的 SA 索引,并将其提供给加密硬件。在接收时,PSP 解密硬件仅使用接收数据包中的数据以及 NIC 内部保存的机密来获取 SA 数据(包括解密密钥)。接收的数据包还必须通过 PSP SPI 检查才能被接受。对于 IP-SEC ESP,在接收方向有一个 SADB 查找以获取解密密钥和其他 SA 参数。这种传输加密的使用与上层协议采用的其他安全机制正交。例如,NVMe 上层协议可以采用静态加密(使用 AES-XTS),IB Verbs RDMA 上层协议在传输级 PSP 加密之上使用保护域和每个内存区域密钥。Falcon 实现类似于 TCP PAWS 的重放保护,以防止数据包序列号翻转。未通过重放保护检查的接收数据包将被丢弃,并且不会生成 ACK 或 NACK。重放保护方案使用 PSP/ESP 标头的 IV 字段中携带的时间戳。Falcon 在每个连接的基础上维护收到的最新时间戳。如果数据包的时间戳在该连接的最新时间戳的时间窗口内,则接受该数据包。此时间窗口允许 Falcon 接受由Fabric重新排序的数据包
以下部分描述了 Falcon 发起方和 Falcon 目标之间的事务流。这些流程描述了正常和错误条件下推送事务和拉取事务的生命周期。
RDMA 读取事务的生命周期如上图所示。假设该连接是有序连接。在发起方和目标方执行以下操作序列:
RDMA 写入事务的生命周期如上图所示。假设该连接是有序连接。在发起方和目标方执行以下操作序列:1. 软件将 RDMA 写入请求发布到发送队列。这会导致 RDMA 协议引擎向 Falcon 发出两个推送请求。创建这两个推送请求是因为每个推送请求的长度限制为一个 MTU,而原始写入请求大于一个 MTU。2. 发起方创建两个推送数据包(RSN=1 和 RSN=2)并将它们传输到目标。数据包传送子层为这两个推送数据包分配 PSN=300 和 PSN=301。3. 这两个推送数据包被网络重新排序,并以无序方式到达目标。目标根据 RSN 重新排序这两个推送数据包,并将它们传送到 RDMA 引擎。4. RDMA 块对每个推送请求执行内存写入操作,并将推送完成返回给 Falcon。收到推送完成之后,目标 Falcon 触发生成 ACK 数据包,确认已收到两个推送数据包。在上图中,目标向发起者发送单个合并的 ACK。5. 收到推送数据包的 ACK 后,发起者将两个推送完成发送回 RDMA 块。6. 发起者的 RDMA 块收到两个推送完成之后,会创建一个写入完成并将其发布到完成队列。
上图显示了推送事务的 RNR NACK 流程。假设连接是有序连接。RNR NACK 处理和相关数据包重传完全在 Falcon 中处理。这简化了 ULP 实现,因为 ULP 层可以假设可靠交付完全在 Falcon 层处理。在发起方和目标方执行以下操作序列:
上图显示了拉取事务的 RNR NACK 流程。假设该连接是有序连接。在发起方和目标方执行以下操作序列:
上图显示了推送事务的 CIE NACK 流程。假设连接是有序连接。在发起方和目标方执行以下操作序列:
当出现无序到达时,会传输 EACK(扩展 ACK),这可能会触发尽早重传。EACK 流程如上图所示。假设该连接是有序连接。在发起方和目标方执行以下操作顺序:
Falcon 数据包的格式如上图所示。Falcon 数据包被封装为加密协议(如 PSP 或 IPSEC ESP)的有效负载,这些协议提供 Falcon 数据包的身份验证和加密。Falcon 报头位于 PSP/ESP 报头之后(PSP 报头中的 Next Header 值为 252)。Falcon 数据包的有效负载包含上层协议 (ULP) 数据包有效负载,Falcon 报头中的协议类型字段指示 ULP 协议。紧随 Falcon 报头之后的是 ULP 协议特定报头。对于 PSP,PSP 加密偏移字段配置为指向 Falcon 报头的前 4 个字节之外(保持连接 ID 字段为明文)。
上图显示了 Falcon 在传输模式下最常见的数据包格式。传输模式格式具有单个 IP 标头,其中源和目标 IP 地址代表网络中机器的物理 IP 地址。传输模式格式用于非虚拟化部署中的 RDMA RC 和 RD 模式
Falcon 还可以在隧道模式下使用(如上图所示),这在虚拟化环境中很有用。在隧道模式下,PSP/ESP 有效负载携带内部 IPv4/IPv6 数据包
除 ACK 和 NACK 数据包外,所有 Falcon 数据包都带有 Falcon Base 标头。Falcon 标头的格式如上图所示,标头各个字段的规范如下表所示
发起者向目标发送拉取请求数据包,以响应由 ULP 发起的拉取事务。在目标处,拉取请求数据包被传送到目标 ULP,并且 ULP 必须通过发送拉取响应来完成拉取事务。拉取请求数据包的格式如上图所示,并由下表定义
目标端的 ULP 会向发起者发送拉取数据包,以响应目标端收到的拉取请求。拉取数据包的格式如上图所示,并由下表定义
推送数据包由发起者向目标发送,来响应由 ULP 发起的推送事务。推送数据包的格式如上图所示,并由下表定义
Resync 数据包由 Falcon 发送方发送到 Falcon 接收方,用于同步滑动窗口状态(如: PSN等)。resync 数据包的格式如上图所示,并由下表定义
重新同步码解释:
Vendor Defined: 这是一个不透明的供应商定义字段;未使用时编码为 0
ACK 数据包有两种类型:Base-ACK(BACK)和 Extended-ACK(EACK)。在本规范中,如果未指定,ACK 数据包可能指 BACK 或 EACK,具体取决于上下文
Falcon 接收方向 Falcon 发射方发送 BACK 数据包,以确认已收到数据包。接收方可能会合并确认信息。因此,BACK 数据包可能会确认先前收到的多个数据包。BACK 数据包的格式如上所示,并由下表定义
OWN: 此字段表示接收方由于滑动窗口空间不足而丢弃了数据包
EACK 数据包扩展了 BACK,包含 3 个位图:接收方数据序列号 ACK 位图 (data-ack-bitmap)、接收方数据序列号 Rx 位图 (data-rx-bitmap)、接收方请求序列号位图 (req-bitmap)。数据包传递子层解释了这三个位图存在的原因
Falcon 接收方向 Falcon 发射方发送 NACK 数据包,以响应 ULP 发出的错误信号或接收方滑动窗口之外无序接收的数据包。NACK 数据包的格式如上所示,并由下表定义
事务子层负责事务排序以及每个事务在其生命周期内所持有的资源的管理,以确保向前推进并避免协议死锁。以下各节从发起者和目标行为的角度详细介绍了事务子层的行为。Falcon 连接的每一端同时是发起者和目标
事务子层支持两种类型的事务:推送和拉取。事务可能导致在发起方和目标之间可靠地传递一个或多个数据包。
● 拉取事务:拉取事务是一种两阶段事务,其中发起方向目标发送拉取请求数据包,目标使用拉取数据包进行回复。请求数据包指定响应中预期的数据长度。拉取数据包有效负载必须与相应拉取请求数据包的请求长度相匹配。拉取数据包还用作返回给 ULP 的完成。
● 推送事务:推送事务是一种两阶段事务,其中发起方向目标发送推送数据包,目标确认收到数据包。只有在其 ULP 完成并确认收到的事务后,目标才能确认收到的推送数据包。引入这种依赖关系是因为推送数据包的 ACK 将导致在发起方向 ULP 传递完成。
每个事务在其生命周期内都会在事务子层中保存各种资源。本节介绍事务子层必须实现的最低限度的资源管理策略,以避免协议死锁。
有两个因素可能导致协议死锁。首先,由于 Falcon 协议架构引入的事务之间的依赖关系,可能会发生协议死锁。其次,Falcon 事务必须在发起方和目标处分配和保存资源。如果资源分配方式不能保证事务的向前进展,则可能会发生协议死锁。实现可以选择不同的资源管理策略。需要遵循的向前进展规则取决于 Falcon 资源是否受限。在每个连接都具有足够资源的不受约束的实现中,只需要遵守由于 Falcon 事务之间的依赖关系而产生的向前进展规则。对于受约束的实现,还需要遵守由于 Falcon 资源有限而产生的其他向前进展规则。我们现在描述无约束和约束实现的前向进展规则
在无约束实现中,需要避免由于 Falcon 事务依赖性而产生的死锁。Falcon 协议架构引入了事务之间的依赖性:
● 数据包传送层在目标处的 ULP 接受并确认推送事务之前无法确认推送数据包。这是必需的,因为推送数据包的 ACK 会导致推送完成传送到发起方的 ULP。在目标确认 ULP 已接受并确认推送事务之前,推送事务无法完成。
● 因此,这可能导致目标处的接收方滑动窗口的推进被有序连接上的事务排序(使用 RSN)阻止。当滑动窗口头部的数据包是推送数据包并且无法传送到 ULP 时,可能会发生这种情况,因为该连接上的队头 (HoL) 事务尚未传送到 ULP。HoL 事务可能是另一个推送数据包或拉取请求。
无约束实现必须遵循以下规则(以下称为 UR 规则),以确保保证转发进度并避免协议死锁:
● 对于有序连接,滑动窗口层必须按 RSN 顺序传输(和重新传输)数据包。
● 目标处的 ULP 必须保证在一个连接上收到的事务的转发进度不依赖于在另一个连接上收到的事务的转发进度
鉴于 Falcon 资源有限,具有受限资源的实现需要遵守其他规则。任何给定的 Falcon 事务在其生命周期内都会占用以下一个或多个资源:
● ULP 资源:ULP 通常占用与其发起的请求相对应的资源,直到收到相应的响应或完成。同样,对于 ULP 从 Falcon 收到的请求,它会占用资源,直到生成相应的响应并将其移交给 Falcon,例如,对于从网络接收的 RDMA 读取,传入读取请求队列槽会一直占用,直到生成响应。
● Falcon 资源:从 ULP 接收到请求/响应数据包时,每个数据包都会占用 Falcon 资源,直到远程 Falcon 接收方确认该数据包。同样,当 Falcon 从网络接收数据包时,每个数据包都会占用 Falcon 资源,直到数据包被传送到 ULP 并得到其确认。
需要通过以下规则(以下称为 CR 规则)管理这些资源,以确保它们不会导致协议死锁并阻碍前进:
● #1 传入和传出数据包的独立资源分配:对于 Falcon 资源受限的实现(其中 Falcon 资源有限),Falcon 需要确保传入数据包和传出数据包彼此独立地分配资源。此规则适用于有序和无序连接。
● #2 独立处理相同方向的发起方和目标生成的数据包对于 ULP 和/或 Falcon 资源受限的实现,Falcon 需要确保相同方向的发起方生成的数据包和目标生成的数据包彼此独立地处理。此规则及其推论适用于有序和无序连接
对于 ULP 和 Falcon 资源受限的实现,同一方向的拉取请求和拉取数据需要使用单独的滑动窗口序列号空间,我们将在数据包传送部分进一步描述。
对于 Falcon 资源受限的实现,Falcon 需要确保同一方向的拉取请求和拉取数据或推送数据和推送数据完成应彼此独立地分配资源
对于资源受限的实现,我们描述了一种方法,通过该方法将事务子层内的资源划分到多个资源池中。实现可能需要根据实现约束实施额外的资源管理策略,以满足性能隔离要求。事务子层内的事务资源可分为四个资源池,如下图所示:
● Tx 数据包池:此池实现保存传输数据包上下文的资源。这包括事务元数据、数据包头和数据包传输所需的任何其他数据包资源。事务子层生成的每个数据包都需要 Tx 数据包池中的单个资源。
● Tx 缓冲池:此池实现传输数据包缓冲区。事务从此池消耗的资源是可变的,取决于请求长度。并非事务子层生成的每个数据包都需要 Tx 缓冲池中的资源。实现通常使用称为单元的固定大小单元的缓冲区来实现 Tx 缓冲池。单元的大小定义了 Tx 缓冲池的分配粒度。
● Rx 数据包池:此池实现保存接收数据包上下文的资源。这包括从网络接收的请求数据包以及要传送到 ULP 的完成。事务子层生成的每个数据包/完成都需要 Rx 数据包池中的单个资源。
● Rx 缓冲池:此池实现接收数据包缓冲区。事务从此池消耗的资源是可变的,取决于请求长度。并非每个从网络接收的数据包或事务子层响应 ULP 事务生成的数据包都需要 Rx 缓冲池中的资源。实现通常使用固定大小单元(称为单元)的缓冲区来实现 Rx 缓冲池。单元的大小定义了 Rx 缓冲池的分配粒度。实现可能对 Tx 和 Rx 缓冲池使用不同的单元大小。
Tx 和 Rx 池之间的资源分离是必要的,以确保传出和传入数据包的独立资源分配(CR 规则 1),以及保证有序连接的 HoL 事务向前推进(UR 规则)
事务子层内的每个资源池必须划分为不同的区域,以便为 ULP 生成的请求以及从网络接收的数据包和数据包提供专用资源。需要进行这种资源划分以确保 ULP 生成的事务和网络接收的事务向前推进(满足 CR 规则 1)。所需的最小资源划分如上图所示,并在下文中说明。实现可以选择提供额外的资源划分以在 ULP 之间提供隔离。
● ULP 请求:所有四个资源池都必须为 ULP 生成的请求提供专用的资源划分。此区域用于实现 ULP 和事务子层之间的流量控制,如下一节所述。
● ULP 数据:Tx 资源池(Tx 数据包和 Tx 缓冲区)必须为 ULP 生成的数据包(即拉取数据)提供专用的资源划分。需要此区域来确保独立处理传出的请求和数据,满足 CR 规则 2,并避免协议死锁。
● 网络请求:Rx 资源池必须为从网络接收的请求提供专用的资源分配,以确保独立处理 ULP 请求和网络请求,从而避免协议死锁(满足 CR 规则 1)。在此区域内,必须为 HoL 事务提供进一步的分配,以避免协议死锁(UR 规则)。
下表显示了事务子层发送和接收的每种数据包类型分配的资源。在从 ULP 收到请求的第一阶段,在发起方主动分配特定事务的资源,以确保协议正确性。例如,拉取请求数据包在发起方主动分配事务后续拉取数据阶段所需的所有资源。如下表(B 行)所示,拉取请求主动从 ULP 请求区域分配 Rx 资源。需要主动分配这些 Rx 资源以保存协议正确性所需的元数据(例如,允许将传入的拉取数据与其对应的请求进行匹配)。主动资源分配还可以防止由于缺少 Falcon 资源而丢失传入的拉取数据。每种数据包类型分配的资源将在后面的部分中进一步详细描述
事务子层采用基于每个连接信用的流量控制来防止 ULP 超载事务资源。仅当 Falcon 连接和 ULP 队列之间存在 1:1 关联(例如 RDMA 可靠连接 QP)时才应用此流量控制。当 Falcon 连接和 ULP 队列之间存在 M:N 关联(例如 RDMA 不可靠数据报 QP)时,不使用流量控制。在这种情况下,事务子层必须在资源不足时拒绝 ULP 事务,并向 ULP 发出“错误完成并继续”指示。流量控制模型允许按连接、按功能和按主机分配上一节中描述的传输和接收资源池。资源分配可以超额认购。例如,每个连接信用的总和可以超过事务子层内可用的事务资源总量。为了防止在使用超额认购时缓冲区超载,使用了一组全局信用计数器。每个数据包资源的信用分配单位是每个事务和资源类型 1 个。每个缓冲区资源的信用分配单位是 N 字节,其中 N 是可以分配给事务的最小缓冲区大小。如果事务的请求长度为 L 字节,则该事务对于请求本身所需的缓冲区信用数量为 1,对于响应数据则为上限 (L/N)。考虑到避免协议死锁所需的上述资源分配,ULP 必须使用以下 6 个信用计数器跟踪每个连接、每个功能和每个主机级别的资源分配:
● 请求 Tx 数据包 (Req Pkt Tx) 和请求 Tx 缓冲区 (Req Buf Tx)。这些计数器用于推送和拉取请求。
● 请求 Rx 数据包 (Req Pkt Rx) 和请求 Rx 缓冲区 (Req Buf Rx)。这些计数器用于推送和拉取请求。
● 数据 Tx 数据包 (Res Pkt Tx) 和数据 Tx 缓冲区 (Res Buf Tx)。这些计数器用于拉取数据。
这些计数器在配置连接时由连接管理器 (CM) 初始化。ULP 必须根据下表所示的事务类型减少这些计数器。当事务资源被释放时,事务子层必须将信用返回给 ULP
事务子层必须从网络请求区域为从网络接收的数据包分配资源。必须为接收的数据包分配的资源如下表所示。与 ULP 资源管理一样,每个数据包资源的信用分配单位是每个事务和资源类型 1 个。每个缓冲区资源的信用分配单位是 N 字节,其中 N 是可以分配给事务的最小缓冲区大小。如果事务的请求长度为 L 字节,则该事务所需的请求信用数为 1,响应数据所需的缓冲区信用数为 ceiling(L/N)。
此外,Falcon 资源受限的实现可以始终确保 HoL 向前进展(UR 规则),方法是将网络请求 Rx 缓冲池进一步划分为两个资源占用区域 - 绿色和红色(见下图)。具体来说,如果缓冲区占用率超过阈值,Falcon 将进入红色区域并仅接受 HoL 请求。否则,Falcon 处于绿色区域并接受所有请求(HoL 或非 HoL)
连接调度程序负责传输由事务子层生成的数据包。连接调度程序必须实现两级调度层次结构:跨事务类型和跨连接。连接调度程序可以实现 QoS 策略,以提供对连接或事务类型的优先处理。连接调度程序至少必须在两个级别实现循环仲裁。每个连接都有两个队列,用于保存特定类型的数据包:
● 拉取请求:此队列保存有序和无序连接的拉取请求和推送数据。此队列中的所有数据包均由发起方生成。
● 拉取数据:此队列保存有序和无序连接的拉取数据包。此队列中的所有数据包均由目标生成
这两个队列是必要的,以确保发起方生成的数据包和目标生成的数据包得到独立处理,这是避免协议死锁所必需的(CR 规则 2)。另一级仲裁从活动连接集中选择一个连接。连接调度程序可能会考虑其他实现定义的约束,以确定连接是否有资格传输数据包。例如,来自数据包传送子层的拥塞控制信息可能会反馈到连接调度程序中,以屏蔽已耗尽拥塞窗口的连接。如果连接超出实现为其提供的资源,则也可能将其屏蔽在第二级仲裁之外
以下小节描述了发起者的行为。
发起者维护以下每个连接变量,这些变量在数据传输期间动态更新
发起方必须为每个事务维护以下变量:
● 事务状态:这表示发起方事务的当前状态。发起方事务状态的可能值如下表所示
除了有序连接所需的避免死锁的排序规则(UR 规则)之外,发起方必须按照 RSN 顺序将完成返回给 ULP,以匹配 ULP 期望的排序语义。由于拉取数据包作为拉取事务的隐式完成,发起方必须通过使用接收推送数据包的 ACK 生成的推送完成事件重新排序接收到的拉取数据包来实现完成排序。无序连接没有事务排序要求。
以下小节描述了发起方如何处理不同类型的数据包。
发起方必须根据以下规则处理从 ULP 收到的拉取请求:
● 发起方必须使用 ULP 提供的源 CID 来查找连接上下文。
● 发起方必须将事务状态更新为 PullReqUlpRx。
● 发起者必须根据连接上下文中的 NRSN 值为拉取请求数据包分配 RSN 值,并将 NRSN 加一。
● 发起者必须按照资源管理部分所述为拉取事务分配资源。
● 拉取请求数据包必须排队到连接调度程序,并根据数据包传送子层公开的拥塞控制标准进行调度。
● 连接调度程序调度数据包后,发起者必须根据连接上下文中的数据包头字段创建 Falcon 基本头。
● 发起者必须将拉取请求数据包交给数据包传送层。
● 发起者必须将事务状态更新为 PullReqTx。
● 当目标确认拉取请求数据包时,发起者必须将事务状态更新为 PullReqAckd
发起方必须根据以下规则处理从目标收到的拉取数据包:
● 发起方必须使用 Falcon 标头中的目标 CID 查找连接上下文。
● 发起方必须将拉取数据包中的 RSN 与未完成拉取请求的 RSN 进行检查。如果未找到匹配的拉取请求,则必须丢弃拉取数据。
● 发起方必须将匹配的拉取请求的请求长度与拉取数据包的有效负载长度进行检查。如果不匹配,则必须丢弃拉取数据包。
● 发起方必须使用发送拉取请求时已分配的 Rx 资源来存储拉取数据包。
● 发起方必须将事务状态更新为 PullDataRx。
● 发起方必须在有序连接上根据 RSN 对拉取数据包重新排序。
● 发起方必须将拉取数据包传送到 ULP。在有序连接上,拉取数据必须按照 RSN 顺序传送到 ULP。
● 发起者必须将事务状态更新为 PullDataUlpTx。
发起者必须根据以下规则处理 ULP 推送请求:
● 发起者必须将事务状态更新为 PushReqUlpRx。
● 发起者必须使用 ULP 提供的源 CID 来查找连接上下文。
● 发起者必须根据连接上下文中的 NRSN 值将 RSN 值分配给推送数据包,并将 NRSN 加一。
● 发起者必须按照资源管理部分所述为推送事务分配资源。
● 推送数据包必须排队到连接调度程序。
● 连接调度程序调度数据包后,发起者必须根据连接上下文中的数据包头字段创建 Falcon 基本头。
● 发起者必须将推送数据包交给数据包传送层。
● 发起者必须将事务状态更新为 PushDataTx。
● 当目标确认推送数据包时,发起方必须将事务状态更新为 PushDataAckd
● 发起方必须向 ULP 传递推送完成。在有序连接中,必须按 RSN 顺序将推送完成传递给 ULP。
● 发起方必须将事务状态更新为 PushCplTx
发起方必须根据以下规则处理从目标收到的拉取数据包:
● 发起方必须使用 Falcon 标头中的目标 CID 查找连接上下文。
● 发起方必须将拉取数据包中的 RSN 与未完成拉取请求的 RSN 进行检查。如果未找到匹配的拉取请求,则必须丢弃拉取数据。
● 发起方必须将匹配的拉取请求的请求长度与拉取数据包的有效负载长度进行检查。如果不匹配,则必须丢弃拉取数据包。
● 发起方必须使用发送拉取请求时已分配的 Rx 资源来存储拉取数据包。
● 发起方必须将事务状态更新为 PullDataRx。
● 发起方必须在有序连接上根据 RSN 对拉取数据包重新排序。
● 发起方必须将拉取数据包传送到 ULP。在有序连接上,拉取数据必须按照 RSN 顺序传送到 ULP。
● 发起者必须将事务状态更新为 PullDataUlpTx。
发起者必须根据以下规则处理 ULP 推送请求:
● 发起者必须将事务状态更新为 PushReqUlpRx。
● 发起者必须使用 ULP 提供的源 CID 来查找连接上下文。
● 发起者必须根据连接上下文中的 NRSN 值将 RSN 值分配给推送数据包,并将 NRSN 加一。
● 发起者必须按照资源管理部分所述为推送事务分配资源。
● 推送数据包必须排队到连接调度程序。
● 连接调度程序调度数据包后,发起者必须根据连接上下文中的数据包头字段创建 Falcon 基本头。
● 发起者必须将推送数据包交给数据包传送层。
● 发起者必须将事务状态更新为 PushDataTx。
● 当目标确认推送数据包时,发起方必须将事务状态更新为 PushDataAckd
● 发起方必须向 ULP 传递推送完成。在有序连接中,必须按 RSN 顺序将推送完成传递给 ULP。
● 发起方必须将事务状态更新为 PushCplTx
接收方未就绪 (RNR) NACK 由目标发送给发起方,以请求延迟重传推送数据包。推送数据传输将延迟 NACK 数据包中指定的 RNR 超时量。拉取事务的 RNR NACK 在目标端本地处理,如下所述。发起方必须根据以下规则处理从网络接收到的推送事务的 RNR NACK:
● 发起方必须使用数据包传送层传送给它的 RSN 以及 RNR NACK 指示来识别推送事务。
● 发起方必须向数据包传送层发出信号,将与推送事务关联的推送数据包的重传超时调整为 RNR NACK 超时值。
错误完成 (CIE) NACK 由目标发送给发起方,以指示推送事务遇到错误并且无法完成。发起者必须根据以下规则处理从网络收到的 CIE NACK:
● 发起者必须使用数据包传送层传送给它的 RSN 以及 CIE NACK 指示来识别推送事务。
● 发起者必须触发数据包传送子层发送 Resync 数据包以推进接收方基本 PSN。
● 发起者必须向 ULP 传送推送完成信息以及完成错误指示,并包含从数据包传送层收到的 ULP NACK 代码。在有序连接中,推送完成必须按 RSN 顺序传送给 ULP。
以下小节描述了目标的行为。
目标维护以下每个连接变量,这些变量在数据传输期间动态更新。
● 基本请求序列号 (BRSN):这是已传送给 ULP 但尚未得到 ULP 确认的最早事务的 RSN 值
○ 它在建立连接时由 CM 初始化。
○ 当 ULP 确认事务且其 RSN 值为 BRSN 时,必须将 BRSN 更新为尚未确认的事务的下一个最高 RSN(使用模 2 的32次方算法)。
● 下一个请求序列号 (NRSN):这是必须传送给 ULP 的 RSN 值。
○ 它在建立连接时由 CM 初始化。
○ 当将 RSN 等于 NRSN 的事务传送给 ULP 时,它会按如下方式递增:NRSN = (NRSN + 1) mod 2的32次方 。
目标必须为每个事务维护以下变量:
● 事务状态:这表示目标上事务的当前状态。目标上事务状态的可能值如下表所示
为了匹配 ULP 所期望的排序语义,目标必须根据以下规则在有序连接上维护事务排序:目标必须按照 RSN 顺序将从发起方收到的请求传送到 ULP。目标必须通过对收到的推送数据和拉取请求重新排序来实现此请求排序。无序连接没有事务排序要求。
以下小节描述了目标端如何处理不同类型的数据包。
目标必须根据以下规则处理从发起方收到的拉取请求数据包:
● 目标必须使用 Falcon 标头中的目标 CID 来查找连接上下文。
● 目标必须从网络请求区域为拉取请求数据包分配 Rx 资源。如果 Rx 资源不可用,目标必须丢弃拉取请求数据包并向数据包传送子层生成 NACK 事件。
● 目标必须将事务状态更新为 PullReqRx。
● 目标必须在有序连接上根据 RSN 对拉取请求数据包进行重新排序。
● 目标必须将拉取请求数据包传送到 ULP。在有序连接上,拉取请求必须按照 RSN 顺序传送到 ULP。
● 目标必须将事务状态更新为 PullReqUlpTx。
● 当 ULP 确认拉取请求时,目标必须将事务状态更新为 PullReqUlpAckd。
目标必须根据以下规则处理从 ULP 收到的拉取数据包:
● 目标必须将事务状态更新为 PullDataUlpRx。
● 目标必须使用 ULP 提供的源 CID 来查找连接上下文。
● 目标必须根据传送到 ULP 的相应拉取请求的 RSN 值将 RSN 值分配给拉取数据包。
● 目标必须按照资源管理部分所述为拉取数据包分配资源。
● 拉取数据包必须排队到连接调度程序。在数据包由连接调度程序调度后,目标必须根据来自连接上下文的数据包头字段创建 Falcon 基本头。
● 目标必须将拉取请求数据包交给数据包交付层。
● 目标必须将事务状态更新为 PullDataTx。
● 当发起者确认拉取数据包时,目标必须将事务状态更新为 PullDataAckd
目标必须根据以下规则处理从发起方收到的推送数据包:
● 目标必须使用 Falcon 标头中的目标 CID 来查找连接上下文。
● 目标必须从网络请求区域为推送数据包分配 Rx 资源。如果 Rx 资源不可用,目标必须丢弃推送数据包并向数据包传送子层生成 NACK 事件。
● 目标必须将事务状态更新为 PushDataRx。
● 目标必须在有序连接上根据 RSN 对推送数据包重新排序。
● 目标必须将推送数据包传送到 ULP。在有序连接上,推送数据包必须按照 RSN 顺序传送到 ULP。
● 目标必须将事务状态更新为 PushReqUlpTx。
● 当 ULP 确认推送数据包时,目标必须将事务状态更新为 PushReqUlpAckd。
● 目标必须从数据包传送子层为推送数据包触发 ACK 生成。
ULP 可能会用接收方未就绪 (RNR) NACK 来响应目标向其传送的推送或拉取事务。除了 RNR NACK 响应外,ULP 还提供一个超时值,该值指定何时必须将事务重新传输到 ULP。目标对 RNR NACK 的处理取决于事务的类型。由于拉取请求已向发起方确认,因此必须从目标端重新传输拉取事务。另一方面,推送数据包只有在 ULP 确认后才会向发起方确认。因此,必须从发起方重新传输推送事务。在有序连接上,如果 ULP 对 RSN 值为 N 的事务进行了 RNR NACK,则所有后续事务(RSN 大于 N)将继续进行 RNR NACK,直到 ULP 接受重新传输的 RSN 值为 N 的事务
目标必须按照以下规则处理来自 ULP 的用于拉取事务的 RNR NACK 指示:
● 目标必须根据 RSN 查找与来自 ULP 的 RNR NACK 响应相对应的拉取事务。
● 目标必须根据 RNR NACK 超时值为拉取事务初始化重传计时器。
● 当重传计时器到期时,目标必须将拉取请求重新传送给 ULP。
● 当 ULP 确认拉取请求时,目标必须将事务状态更新为 PullReqUlpAckd。
目标必须按照以下规则处理来自 ULP 的用于推送事务的 RNR NACK 指示:
● 目标必须根据 RSN 查找与来自 ULP 的 RNR NACK 响应相对应的推送事务。
● 目标必须生成 RNR NACK 事件并将其传送给数据包传送层,从而触发向发起者发送 NACK 数据包。
ULP 可以使用“错误完成 (CIE) NACK”来响应目标端向其传送的推送事务。目标端的 ULP 会通过发送长度为零的拉取数据包向发起者发出必须错误完成的拉取事务信号。对于推送事务,除了 CIE NACK 响应外,ULP 还会提供一个 NACK 代码,该代码指定事务错误完成的原因。目标端必须根据以下规则处理来自 ULP 的推送事务的 CIE NACK 指示:
● 目标端必须根据 RSN 查找与来自 ULP 的 CIE NACK 响应相对应的推送事务。
● 目标端必须生成 CIE NACK 事件并将其传送到数据包传送层,从而触发向发起者发送 NACK 数据包。
数据包传送子层的主要职责是可靠的数据包传送和拥塞控制
数据包传送子层实现两个滑动窗口以分离请求和数据。请求滑动窗口负责可靠地传送拉取请求,而数据滑动窗口负责可靠地传送推送数据和拉取数据。将拉取请求和拉取数据放在单独的滑动窗口中对于解决死锁非常重要 - CR 规则 2。
由于数据中心fabric不保证数据包排序,因此数据包可能会无序到达接收方。接收方维护自己的滑动窗口版本。如果接收到的数据包的 PSN 落在接收方的滑动窗口内(大于或等于 BSN 且小于或等于 BSN + Rx 窗口大小),则接受该数据包并将其传送到连接的事务子层。然后,接收方将 ACK 发送回发送方。由于 Falcon 不仅涵盖fabric上的可靠传送,还涵盖可能 NACK 数据包的 ULP,因此每个数据包都有 2 个状态:已接收和已确认。所有数据包在到达接收方并落在接收方的滑动窗口中时都会被接收。但何时确认取决于数据包类型:对于推送数据包,接收方等待 ULP 确认;对于所有其他数据包类型,接收方立即将数据包标记为已确认。
因此,接收方维护 3 个位图 - 2 个用于数据窗口的接收和确认状态,1 个用于请求窗口,因为其接收和确认状态相同。如果数据包已接收/确认,则该位设置为 1,否则设置为 0。接收方请求滑动窗口的大小为 64,其大小适合fabric可以引入的重新排序范围。接收方数据滑动窗口的大小为 128,其大小不仅适合fabric重新排序,还适合 ULP ACK 的延迟。接收方向发送方发送 ACK 数据包,以传达接收和确认状态。ACK 通常是 BACK 以节省带宽,并且只有当 3 个位图包含有用信息时才是 EACK。允许接收方合并 ACK。也就是说,当收到多个数据包时,允许接收方发送带有最新位图快照的单个 ACK 数据包。还允许接收方将 ACK 搭载到流向相反方向的数据包上。ACK 合并和 ACK 搭载的组合有助于减少 ACK 数据包的数量并减少 ACK 数据包所消耗的网络带宽。发送方滑动窗口是可配置的,并且必须考虑连接的网络 RTT 来调整大小。发送方还必须在每个滑动窗口内保留所有数据包的位图,如果该数据包已被接收方确认,则将某个位设置为 1。由于数据包可能在传输过程中丢失,因此发送方必须保留数据包(或数据包头和构建完整数据包所需的元数据)以备可能的重传,直到它们被接收方确认。Falcon 中有两种重传机制。大多数数据包应该可以通过 Falcon 的尽早重传非常快速地(在一个或几个 RTT 内)重传。发送方使用基于位图中的无序信息和接收方窗口外丢弃通知的启发式方法来推断哪些数据包可能丢失,并在位图更新后立即重新传输它们。在极少数情况下,丢失的数据包可能无法通过尽早重传重新传输。然后超时作为备用机制启动。发送方为其滑动窗口内的每个数据包维护一个重传计时器。当数据包被传输(重传)时,计时器启动(重置),如果数据包被接收方确认,计时器被清除。如果计时器到期(基于可配置的重传超时),发送方必须使用与原始传输相同的 PSN 值重新传输该数据包。接收方发送的 ACK 数据包也可能在传输过程中丢失。当发送方从接收方收到后续 ACK 或当重传计时器触发丢失的 ACK 数据包应该确认的数据包时,发送方将从这种情况中恢复。以下各节从发送方和接收方行为的角度详细介绍了数据包传送子层的行为。Falcon 连接的每一端同时是发送方和接收方
以下小节详细介绍了发送器的行为。
发送器维护以下每个连接变量,这些变量在数据传输期间会动态更新:
● 请求窗口基本 PSN (RBPSN):这是请求滑动窗口中尚未被接收器确认的最旧数据包的 PSN 值。
○ 它由连接管理器 (CM) 在建立连接时初始化。
○ 当收到确认已收到 PSN 值为 RBPSN 的请求滑动窗口数据包的 ACK 时,必须将 RBPSN 更新为尚未确认的数据包的下一个最高 PSN(使用模 2 的32次方算法)。更新 RBPSN 时,必须清除从前一个 RBPSN 到更新的 RBPSN 的数据包对应的序列号位图中的位。
● 请求窗口下一个 PSN (RNPSN):这是必须分配给连接上传输的请求滑动窗口中的下一个数据包的 PSN 值。
○ 建立连接时,CM 会将其初始化为 RBPSN。
○ 传输数据包时,它会按如下方式递增:RNPSN = (RNPSN + 1) mod 2 的32次方 。
● 数据窗口基本 PSN (DBPSN):这是数据滑动窗口中尚未被接收器确认的最旧数据包的 PSN 值。
○ 建立连接时,CM 会将其初始化。
○ 收到确认已收到 PSN 值为 DBPSN 的数据滑动窗口数据包的 ACK 时,必须将 DBPSN 更新为尚未确认的数据包的下一个最高 PSN(使用模 2 的32次方算法)。更新 DBPSN 时,必须清除序列号位图中与从上一个 DBPSN 到更新的 DBPSN 的数据包相对应的位。
● 数据窗口下一个 PSN (DNPSN):这是必须分配给连接上传输的数据滑动窗口中的下一个数据包的 PSN 值。
○ 建立连接时,CM 将其初始化为 DBPSN。
○ 传输数据包时,它会按如下方式递增:DNPSN = (DNPSN + 1) mod 2 的32次方。
● 请求窗口序列号位图:这是一个 64 位的位图,其中每个位代表请求滑动窗口内的数据包。如果数据包已被 ACK,则该位设置为 1。否则该位为 0。位 0 表示 RBPSN 的 PSN 值,位 n 表示 (RBPSN+n) 的 PSN 值。
○ 配置连接时,此位图初始化为全 0。
○ 收到 ACK 时,此位图根据 ACK 数据包中包含的接收器序列号位图进行更新。
● 数据窗口序列号 ACK 位图:这是一个 128 位的位图,其中每个位代表数据滑动窗口内的数据包。如果数据包已被 ACK,则该位设置为 1。否则,该位为 0。位 0 表示 PSN 值为 DBPSN,位 n 表示 PSN 值为 (DBPSN+n)。
○ 在配置连接时,此位图初始化为全 0。
○ 在收到 ACK 时,此位图根据 ACK 数据包中包含的接收器序列号位图进行更新。
● NIC 拥塞窗口 (ncwnd):这表示每个滑动窗口基于接收 NIC 的拥塞允许发送的启动新 ULP 事务(拉取请求或推送数据)的数据包数。ncwnd 由 RUE 根据接收器缓冲区占用情况动态计算。
● Fabric拥塞窗口 (fcwnd):这表示每个滑动窗口基于Fabric拥塞允许发送的数据包(任何类型的可靠数据包)数。fcwnd 由 RUE 根据Fabric中的拥塞情况动态计算。
● 请求窗口未完成请求计数器 (RORC:Request Window Outstanding Requests Counter):这是发送方已发送但尚未被接收方确认的拉取请求数据包的数量。
● 数据窗口未完成请求计数器 (DORC:Data Window Outstanding Requests Counter):这是发送方已发送但尚未被接收方确认的推送数据包的数量。
● 重传超时 (RTO):此超时值用于启动数据包的重传,以从丢失的数据包或 ACK 中恢复。当数据包的重传计时器与此超时值匹配时,必须重传数据包。
○ 必须由 CM 根据通过Fabric连接的预期往返延迟对其进行初始化。
○ RUE 可能会根据网络拥塞情况对其进行动态调整。
● ACK 合并计时器:此计时器根据以下规则确定何时必须传输合并的 ACK:
○ 当收到数据包并落在接收器的滑动窗口内或当数据包被 ULP 确认时,必须启动计时器。
○ 发送 ACK 时必须重置计时器。
○ 当另一个数据包搭载所有有用的确认和接收状态(当位图全部为零时)时,必须重置计时器。
发送器为每个数据包维护以下变量:
● 重传计时器(Retransmit Timer):此计时器确定何时必须进行重传尝试。传输数据包时必须启动计时器。计时器到期时,必须重新传输数据包并重新启动计时器。。
● 重传计数器(Retransmit Counter):此计数器跟踪数据包超时重传的次数。
以下变量在配置连接时初始化一次,并且在连接的生命周期内不会更改:
● 最大重传尝试次数:此变量确定可以重传数据包的次数。当数据包的重传计数器与此值匹配时,将向 CM 发出致命错误信号并导致连接断开。
● ACK 合并超时:此超时值确定发送器在传输 ACK 之前等待合并的最大时间。当 ACK 合并计时器值等于此超时值时,将在连接上发送 ACK。
● ACK 请求 Fcwnd 阈值:此阈值决定何时在每个可靠数据包(即不包括 ACK 和 NACK)上设置 AR(AckReq) 位。如果 fcwnd 小于或等于此阈值,则每个数据包都会设置 AR 位。
● ACK 请求百分比:当 fcwnd 大于 ACK 请求 Fcwnd 阈值时,此百分比值决定标记了 AR 位的数据包的比例。
● 无序距离阈值(Out-of-order Distance Threshold):此阈值决定尽早重传的积极程度。如果在同一个滑动窗口中有一个足够大的接收 PSN,则发送器可以尽早重传未接收的 PSN - 此阈值定义大多少才足够。该阈值本质上用作缓冲区,以消除网络中数据包丢失与重新排序之间的歧义。在无序距离阈值范围内重新排序的数据包不会被解释为在网络中丢失
拥塞控制门控功能的思维模型很简单:只要下一个数据包序列号低于基本序列号 + fcwnd,数据包就可以传输。这保证了在任何时间点最多有 fcwnd 个数据包。 所有初始数据包传输都取决于数据包类型,在发送器处受以下拥塞控制门控功能控制:
● 拉取请求:PSN < RBPSN + fcwnd 且 RORC < ncwnd
● 拉取数据:PSN < DBPSN + fcwnd
● 推送数据:PSN < DBPSN + fcwnd 且 DORC < ncwnd
所有数据包重传都取决于数据包类型,在发送器处受以下拥塞控制门控功能控制。使用每个滑动窗口的另一个变量未完成重传请求计数 (ORRC: Outstanding Retransmitted Request Count) 来解释门控(RORRC 计数重传的拉取请求,DORRC 计数重传的推送数据):
● 拉取请求:PSN < RBPSN + fcwnd RORRC < ncwnd
● 拉取数据:PSN < DBPSN + fcwnd
● 推送数据:PSN < DBPSN + fcwnd DORRC < ncwnd
● [R/D]ORRC 维护如下:
○ 当重传请求发出时,[R/D]ORRC 加 1
○ 当重传请求得到 ACK 时,[R/D]ORRC 减 1
○ 当重传请求再次符合重传条件时(符合重传条件意味着稍后重传),[R/D]ORRC 减 1。当再次重新传输请求时,[R/D]ORRC 会在重新传输的请求发出时再次递增
当发送器从事务子层接收到数据包传输请求时,它必须遵循以下规则:
● 发送器必须根据数据包类型将数据包的 PSN 设置为请求的 NSN 或数据滑动窗口,然后按前面所述增加 NSN。
● 发送器必须使用 Tx 门控功能确定数据包是否适合传输。
● 在有序连接上,发送器必须在请求和数据滑动窗口内按照 RSN 顺序传输数据包。在每个滑动窗口内,发送器必须按照 PSN 顺序传输数据包。
● 数据包传输完毕后,发送器必须对其进行缓冲,直到接收器确认。
● 发送器必须在数据包传输完毕后启动重传计时器。
发送器可以决定何时标记报头中的 AR 位,以请求接收器生成 ACK。发送器决定 AR 位的频率以进行拥塞控制。例如,作为实现的指导原则,当拥塞窗口较小时,AR 位需要更频繁地出现,以确保及时采取拥塞控制措施。具体而言,当 fcwnd 小于或等于 ACK 请求 Fcwnd 阈值时,标记每个数据包的 AR 位;当 fcwnd 大于 ACK 请求 Fcwnd 阈值时,标记 ACK-Request-Percent 分数的数据包的 AR 位,以确保 CC 反应频率足够。
发送方在收到接收方的 EACK 时可以重传数据包。EACK 的位图信息、时间戳和超出窗口通知可以帮助发送方决定哪些数据包可能丢失而应重传。尽早重传的策略如下所述。
OOO 距离启发式:一旦收到 EACK,发送方可以根据位图推断哪些数据包可能丢失。如果未收到某些 PSN,但收到了较大的 PSN,则这些 PSN 要么丢失,要么在结构中重新排序。为了消除因重新排序而导致的丢失,可以使用以下直觉:如果未收到 PSN,但收到了足够大的 PSN,则该 PSN 很可能丢失。无序距离阈值 (ooo_threshold) 定义大多少才足够。
Recency check(检查近期):我们必须保证自上次(重新)传输以来至少经过一个 RTT,然后才能重新传输数据包,以便有足够的时间确认上次(重新)传输。否则,在重新传输得到确认之前,丢失的数据包可能会被错误地重新传输多次,每次 EACK 一次。EACK 算法以下算法实现上述策略
尽早重传使用数据接收位图,而不是数据确认位图,因为尽早重传负责恢复非 ULP 丢失。ULP NACKed 数据包有自己的恢复方法。
OWN 启发式:回想一下,超出窗口通知 (OWN) 是 Base ACK 中的一个字段,指示接收器是否由于其数据或请求滑动窗口空间不足而丢失数据包。在接收到设置了 OWN 位的 EACK(以下称为 EACK-OWN)时,发送器会在 RTT 内重新传输最近未重新传输的接收位图内和之外的数据包。Falcon 发送器上的以下算法针对接收 EACK-OWN 的给定窗口(请求或数据)实施此启发式
在接收到 EACK-OWN 时,上述启发式方法的直觉是每 RTT 最多重新传输一次窗口外的数据包(即丢弃到滑动窗口右侧的数据包),同时利用位图避免错误地重新传输已经在目标处收到的数据包。
发送器必须根据以下规则处理重新传输计时器到期事件:
● 必须增加数据包的重新传输计数器。如果重新传输计数器超过最大重新传输尝试次数,则发送器必须向 CM 发出连接上的致命错误信号。
● 发送器必须使用此处描述的 Tx 门控功能确定数据包是否有资格重新传输。
● 发送器必须重新传输具有与原始数据包相同 PSN 的数据包。
● 在有序连接上,发送器必须在请求和数据滑动窗口上按照 RSN 顺序重新传输数据包。在每个滑动窗口内,发送器必须按照 PSN 顺序重新传输数据包。
● 如果在重新传输过程中收到 ACK 数据包,则允许发送器完成传输,而不考虑收到的 ACK 数据包。
● 发送器必须在重新传输数据包后启动重新传输计时器。
数据包传送子层支持基于计时器的 ACK 合并,以减少 ACK 数据包的开销。ACK 还可以搭载到反方向流动的数据包上。发送器必须根据以下规则实现 ACK 生成:
● 接收未设置 AR 位的数据包时,如果 ACK 合并计时器尚未启动,则发送器必须启动它。
● 接收设置了 AR 位的数据包时,发送器必须在数据包得到确认时(当 ULP 确认 PushData 时以及收到其他数据包时)发送 ACK 数据包;如果 ACK 合并计时器已启动,则发送器必须停止该计时器。
● 如果发送器有传出数据包,则发送器必须将 RBPSN 和 DBPSN 推进到最新值并将它们搭载到传出数据包上。如果
○ 接收器请求序列号位图为零,并且
○ 接收器数据序列号 ACK 位图为零,并且
○ 接收器数据序列号 Rx 位图为零,并且
○ 接收器 R-OWN 和 D-OWN 位为零,则发送器必须停止 ACK 合并计时器。
● 当 ACK 合并计时器等于 ACK 合并超时值时,发送器必须发送合并的 ACK 数据包并停止 ACK 合并计时器。
● ACK 数据包必须包含接收器的 RBPSN 和 DBPSN 的最新值。
● ACK 数据包必须包含接收器为 RTT 测量维护的 t1 和 t2 的最新值,如后面的部分所述。
● ACK 数据包必须包含来自发送器状态的拥塞控制元数据的最新快照。 ACK 应该是 BACK 还是 EACK 取决于位图:
● 如果接收器请求序列号位图非零,则必须发送 EACK。
● 如果接收器数据序列号 ACK 位图非零,则必须发送 EACK。
● 如果接收器数据序列号 Rx 位图缺少 PSN(“1”从 LSB 开始不连续),则必须发送 EACK。
● 如果设置了请求窗口外通知 (R-OWN) 或数据窗口外通知 (D-OWN) 位,则必须发送 EACK。
● 否则,可以发送 BACK。
○ 这并不严格 - 发送 EACK 始终是合法的。
○ 为了支持 RACK-TLP 扩展,当接收器数据序列号 Rx 位图非零时必须发送 EACK
NACK 可以响应事务子层生成的 NACK 事件或滑动窗口接收器生成的 NACK 事件而生成。发送器必须根据以下规则实现 NACK 生成:
● NACK 数据包必须包含接收器的 RBPSN 和 DBPSN 的最新值。
● NACK 数据包必须包含触发 NACK 事件的接收数据包的 PSN 以及数据包是在请求滑动窗口还是数据滑动窗口中的指示。
● 对于事务子层生成的 NACK 事件,NACK 数据包必须包含 ULP NACK 代码和 RNR NACK 超时(如果适用)。
● NACK 数据包必须包含接收器为 RTT 测量维护的 t1 和 t2 的最新值(如后面部分所述)。
● NACK 数据包必须包含来自发送器状态的拥塞控制元数据的最新快照
数据包传送子层使用重新同步数据包来同步发送器和接收器之间的 PSN。在某些错误情况下(例如 ULP NACK 或 xLR 丢失),发送器发送的数据包可能会永久丢失或被接收器 NACK,从而在序列号空间中产生空洞。在这些条件下,发送器将重新同步数据包发送到接收器以填补 PSN 空洞并推进接收器滑动窗口。重新同步数据包本身是可靠传送的,并使用丢失或 NACK 的原始数据包的 PSN。发送器必须根据以下规则实现重新同步生成:
● 发送器必须使用丢失的原始数据包的 PSN 和 RSN 传输重新同步数据包。
● 发送器必须在重新同步数据包中包含原始数据包的数据包类型。
● 发送器必须在重新同步数据包中包含原因代码。
● 发送器必须使用此处描述的 Tx 门控功能确定数据包是否适合传输
以下小节详细介绍了接收器的行为。
接收方维护以下每个连接变量,这些变量在数据传输期间动态更新:
● 请求窗口基础 PSN (RBPSN):这是请求滑动窗口中尚未被接收方接收的最旧数据包的 PSN 值。
○ 在配置连接时,CM 会对其进行初始化。
○ 在收到 PSN 值为 RBPSN 的请求滑动窗口数据包时,必须将 RBPSN 更新为尚未接收数据包的下一个最高 PSN(使用模 2的32次方算法)。BSN 的更新还必须清除从前一个 RBPSN 到更新的 RBPSN 的数据包对应的序列号位图中的位。
● 数据窗口基础 PSN (DBPSN):这是数据滑动窗口中尚未被接收方接收的最旧数据包的 PSN 值。
○ 在配置连接时,CM 会对其进行初始化。
○ 当接收到 PSN 值为 DBPSN 的数据滑动窗口数据包时,必须将 RBPSN 更新为未接收数据包的下一个最高 PSN(使用模 2 的32次方算法)。BSN 的更新还必须清除序列号位图中与从前一个 DBPSN 到更新的 DBPSN 的数据包相对应的位。
● 请求窗口序列号位图:这是一个 64 位的位图,其中每个位代表请求滑动窗口内的数据包。如果数据包已被接收方接收并确认,则该位设置为 1。否则,该位为 0。位 0 表示 PSN 值为 RBPSN,位 n 表示 PSN 值为 (RBPSN+n)。
○ 当配置连接时,此位图初始化为全 0s。
○ 当接收方接收并确认数据包时,此位图将根据数据包的 PSN 进行更新。对于某些数据包类型(推送请求),只有在数据包传送到 ULP 并得到 ULP 确认后,接收器才会发送确认。
● 数据窗口序列号 ACK 位图:这是一个 128 位的位图,其中每个位代表数据滑动窗口内的数据包。如果数据包已被接收器确认,则该位设置为 1。否则,该位为 0。位 0 表示 PSN 值 DBPSN,位 n 表示 PSN 值 (DBPSN+n)。
○ 配置连接时,此位图初始化为全 0。
○ 当接收器确认数据包时,此位图会根据数据包的 PSN 进行更新。对于某些数据包类型(推送数据),只有在数据包传送到 ULP 并得到 ULP 确认后,接收器才会发送确认。
● 数据窗口序列号 Rx 位图:这是一个 128 位的位图,其中每个位代表数据滑动窗口内的数据包。如果数据包已被接收器接收,则该位设置为 1。否则该位为 0。位 0 表示 PSN 值为 DBPSN,位 n 表示 PSN 值为 (DBPSN+n)。
○ 当配置连接时,此位图初始化为全 0。
○ 当接收器接收到数据包时,此位图根据数据包的 PSN 进行更新。
● 请求窗口外通知 (R-OWN):如果接收器必须丢弃超出请求滑动窗口右侧的数据包,则该位设置为 1。否则该位为 0。当传出的 EACK 携带通知时,该位重置为 0。
● 数据窗口外通知 (D-OWN):如果接收器必须丢弃超出数据滑动窗口右侧的数据包,则该位设置为 1。否则该位为 0。当传出的 EACK 携带通知时,该位重置为 0。
● 当前 SPI:此变量表示用于导出 PSP 加密密钥的安全参数索引 (SPI) 字段的当前值。此变量将根据接收数据包的 PSP 标头中的 SPI 字段进行检查,如下一节所述。
○ 它是 ini在配置连接时由 CM 初始化该变量。
○ 每次旋转 PSP 密钥时,CM 都会更新该变量。
● 当前 SPI 代数:此变量表示与当前 SPI 相对应的 PSP 密钥的代数。
○ 在配置连接时,CM 会初始化该变量。
○ 每次旋转 PSP 密钥时,CM 都会更新该变量。
● 上一个 SPI:此变量表示 SPI 字段的上一个值。此变量将根据接收数据包的 PSP 标头中的 SPI 字段进行检查,如下一节所述。
○ 在配置连接时,CM 会将其初始化为与当前 SPI 相同的值。
○ 每次旋转 PSP 密钥时,CM 都会更新该变量。
● 远程时间:此变量表示接收器对发射器时间的估计。
○ 在配置连接时以及每次旋转 PSP 密钥时,CM 都会将其初始化为比远程发送器时间更早的值。
○ 如果 PSP.IV 字段大于使用模数算法的远程时间字段,则将其设置为接收数据包的 PSP.IV 字段中的时间戳值。
● PSP 连接 ID (PCID):此变量是一个 64b 值,用于唯一标识 PSP 隧道。当多个 Falcon 连接共享一个公共 PSP 隧道时,使用此变量。PCID 包含在 PSP 标头的虚拟化 cookie (VC) 字段中,并作为传入数据包安全检查的一部分,由接收器根据连接上下文中存储的 PCID 值进行验证。以下变量在所有连接中都是全局的,在接收器初始化时初始化一次,并且在连接的生命周期内不会更改:
● 重放保护边界(Replay Protection Margin):此变量用于重放保护,并指定一个早于远程时间值的恒定时间窗口,接收器仍将在其中接受数据包,如下一节所述。此变量由 CM 初始化,必须足够大以适应重新排序引入的网络抖动,但也要远小于最小可能的 PSN 环绕时间。
● Current SPI Generation:此变量表示 PSP 主密钥的当前版本。
○ 它由 CM 在 NIC 首次启动时配置。
○ 每次 PSP 密钥轮换时,它都会加 1。
● 允许上一个 SPI 版本:此布尔变量用于控制接收器是否接受或拒绝具有较旧 PSP 密钥版本的数据包。它由 CM 在密钥轮换流程期间配置,如下面有关接收数据包的 SPI 检查的部分所述。
● PCID 匹配启用:必须将此布尔变量设置为 true 以启用接收器的 PCID 检查。它由 CM 根据多个 Falcon 连接是否共享单个 PSP 隧道进行配置
接收方必须执行以下小节中所述的各种数据包验收检查。通过所有验收检查的数据包必须传送到事务子层。
接收方必须根据以下规则对每个收到的数据包执行数据包完整性检查:
● 如果数据包未通过报头解析检查,或者收到的数据包存在 CRC 错误,或者数据包的校验和无效,则接收方必须丢弃该数据包。
● 接收方必须根据收到的字节数验证 UDP 报头中的长度,如果长度不匹配,则必须丢弃该数据包。
● 如果数据包解密失败,则接收方必须丢弃该数据包。或者,必须记录该数据包,并向 CM 发送错误指示
数据包传送子层实现的重放保护检查与 TCP PAWS 实现(https://tools.ietf.org/html/rfc7323)的重放保护检查类似。重放保护检查依赖于 PSP 标头的 IV 字段中携带的远程时间戳。接收器将 PSP.IV 字段中包含的远程时间戳与远程时间字段中维护的发送器时间的最新估计值进行比较。接收器必须根据以下规则实现重放保护检查:
● 如果使用模数算法得出的 (远程时间 - 重放保护边界) 的值小于 PSP.IV 值,则必须接受数据包。否则必须丢弃数据包。
● 如果由于上一步检查失败而丢弃数据包,则接收器不得生成 ACK 或 NACK 生成事件。 数据包传送子层实现的重放保护与 TCP PAWS 的不同之处在于,接收器将接受 PSP.IV 字段中时间戳值比远程时间变量旧的数据包。这是因为重放保护边界允许接收器接纳发送器先前发送但在网络中延迟的数据包。如果连接长时间处于空闲状态,远程时间变量可能会回绕。数据包传送子层不采用保持活动机制。因此,远程时间变量的回绕时间必须远大于 PSP 密钥轮换间隔。当 PSP 密钥轮换时,CM 必须根据对发送器时间的当前估计来更新远程时间变量。
接收器必须对所有数据包(包括 ACK/NACK 数据包)应用 SPI 检查。SPI 检查可验证在连接上收到的数据包是否来自预期的 PSP 隧道。如果 SPI 检查失败,接收器必须向 CM 发出错误信号。接收器不得为未通过 SPI 检查的接收数据包生成 ACK/NACK 数据包。为了允许在活动连接的生命周期内进行 PSP 密钥轮换,接收器会为每个连接维护当前 SPI 和之前的 SPI 变量。接收器还维护当前 SPI 代并计算前一个 SPI 代。当发生 PSP 密钥轮换事件时,CM 必须执行以下步骤:
○ Connection.PrevSPI = Connection.CurrentSPI
○ Global.AllowPreviousSPIGen = 0 接收器必须根据以下伪代码对所有数据包实施 SPI 检查:
接收器必须对每个数据包执行以下滑动窗口验收检查。这些检查根据数据包的类型应用于请求或数据滑动窗口。
● 如果接收到的数据包的 PSN 值小于 RBPSN / DBPSN,则该数据包被视为旧的和重复的,接收器必须丢弃该数据包。
● 如果接收到的数据包的 PSN 值大于或等于 (RBPSN + 64) / (DBPSN + 128),接收器必须丢弃该数据包并设置相应的 R-OWN/D-OWN 位,表示超出窗口丢弃。
● 如果 PSN 值在接收器的滑动窗口内,并且位置 (PSN - RBPSN/DBPSN) 处相应 req-bitmap/data-ack-bitmap 中的位为 1b,则该数据包被视为重复数据包,必须丢弃。
● 注意:丢弃的数据包也应触发 ACK 合并,但其 AR 位会被忽略。
● 如果通过上述检查,则必须将数据包传送到事务子层。 如果数据包类型为推送数据,则在 ULP 明确确认数据包之前,接收器数据确认位图中位置 (PSN - DBPSN) 的位不得设置为 1b。 对于所有其他数据包类型,位置 (PSN - RBPSN/DBPSN) 处相应的请求位图/数据确认位图中的位必须设置为 1b 并触发 ACK 合并。
● 如果需要确认的 PSN 等于 RBPSN/DBPSN,则必须将 RBPSN/DBPSN 值更新为等于尚未确认的下一个最高 PSN。 更新 RBPSN/DBPSN 值后,必须将位图移动 RBPSN/DBPSN 前进的量。
接收到的 ACK 数据包会导致发送器的滑动窗口状态发生变化,该连接由 ACK 数据包中的 CID 指示。接收方必须根据以下规则处理收到的 ACK 数据包(无论是 BACK 还是 EACK):
● 如果 ACK 数据包中的 RBPSN 小于发送方的 RBPSN,则接收方必须丢弃 ACK 数据包。
● 如果 ACK 数据包中的接收方 RBPSN 大于发送方的 RBPSN,则接收方必须更新发送方的 RBPSN,以匹配 ACK 数据包中的 RBPSN。
● 如果 ACK 数据包中的 DBPSN 小于发送方的 DBPSN,则接收方必须丢弃 ACK 数据包。
● 如果 ACK 数据包中的接收方 DBPSN 大于发送方的 DBPSN,则接收方必须更新发送方的 DBPSN,以匹配 ACK 数据包中的 DBPSN。
● 接收方必须触发释放分配给重发缓冲区中所有已确认数据包的资源。
● 接收方必须使用时间戳 T1 和 ACK 的 Rx 时间戳来计算 RTT 样本并更新连接的平均 RTT。
此外,如果 ACK 是 EACK,还需要遵循以下附加规则:
● 接收方必须在调整 RBPSN 后将 EACK 数据包中的接收方请求序列号位图与发送方的请求序列号位图进行“或”操作。
● 接收方必须在调整 DBPSN 后将 EACK 数据包中的接收方数据序列号 ACK 位图与发送方的数据序列号 Rx 位图进行“或”操作。
● 接收方必须触发释放分配给所有位图确认的数据包的资源。
● 接收方应根据 EACK 中的接收方请求序列号位图、接收方数据序列号 Rx 位图、接收方请求窗口外通知和接收方数据窗口外通知触发尽早重传
接收到的 NACK 数据包可能导致发送器针对 NACK 数据包中 CID 所指示的连接的滑动窗口状态发生变化。 接收器必须根据以下规则处理接收到的 NACK 数据包:
● 如果 NACK 数据包中的 RBPSN 小于发送器的 RBPSN,则接收器必须丢弃 NACK 数据包。
● 如果 NACK 数据包中的接收器 RBPSN 大于发送器的 RBPSN,则接收器必须更新发送器的 RBPSN,以匹配 NACK 数据包中的 RBPSN。 ● 如果 NACK 数据包中的 DBPSN 小于发送器的 DBPSN,则接收器必须丢弃 NACK 数据包。
● 如果 NACK 数据包中的接收器 DBPSN 大于发送器的 DBPSN,则接收器必须更新发送器的 DBPSN,以匹配 NACK 数据包中的 DBPSN。
● 接收器必须触发释放重发缓冲区中分配给所有已确认数据包的资源。
● 接收方必须使用时间戳 t1 和 t2 来计算 RTT 样本并更新连接的平均 RTT。除了上述规则外,接收方还必须根据下表所述的 NACK 代码处理 NACK 数据包:
接收器必须根据以下规则处理收到的重新同步数据包:
● 位置 (PSN - RBPSN/DBPSN) 处相应接收器序列号位图中的位必须设置为 1b,并且必须向发送器发送 ACK 生成事件。
● 如果收到数据包的 PSN 等于 RBPSN/DBPSN,则必须更新 RBPSN/DBPSN 值以等于未收到的下一个最高 PSN。更新 RBPSN/DBPSN 值后,必须将位图移动 RBPSN/DBPSN 前进的量。
拥塞控制 (CC) 是基于每个连接实现的,是数据包传送子层的一部分。CC 算法在速率更新引擎 (RUE) 中实现,该引擎是与数据包传送子层的主数据路径分开的逻辑块。数据包传送子层向 RUE 提供各种拥塞信号和状态,包括延迟、ECN 标记、接收缓冲区占用率、ACK 和 NACK 数据包数量等的精确测量。数据包传送子层和 RUE 之间的接口在每个方向上都使用生产者-消费者队列,并在以下章节中描述。RUE 逻辑块可以完全在硬件中实现,也可以在 NIC 上的嵌入式 CPU 上运行的软件中实现。数据包传送子层和 RUE 之间的拥塞控制功能分离至少有两个优点:
● 无论 RUE 是软件还是硬件,拥塞控制信号的测量、确认数据包的生成以及连接的拥塞窗口和速率的执行都是在靠近线路的 Falcon 硬件中实现的。这种分离使 CC 通过快速测量信号和执行速率对网络拥塞做出更快的响应。
● 将 RUE 与 Falcon 协议的其余部分分开,允许在 CC 上进行调整和迭代,而不会影响数据包传递子层的主要数据路径。
下面,我们将描述 Swift 拥塞控制算法和保护性负载平衡 (PLB: protective load balancing)。本文(https://dl.acm.org/doi/pdf/10.1145/3387514.3406591)介绍了 Swift 的设计 - Swift:延迟对于数据中心的拥塞控制来说简单而有效。本文(https://dl.acm.org/doi/10.1145/3544216.3544226)介绍了 PLB 的设计 - PLB:拥塞信号对于网络负载平衡来说简单而有效。接下来,我们将按以下顺序关注实施方面:
我们预计 RUE ↔ Falcon API 和延迟测量方法将跨越硬件代,而 RUE 中实例化的 Swift 算法的细节将随着时间的推移而迭代
数据包传送子层提供精确的延迟测量。这对于低延迟、基于延迟的拥塞控制算法(如 Swift)非常重要。下面我们将描述如何使用延迟测量来计算网络中的延迟。延迟测量使用在实现 PSP 或 IPSEC ESP 加密协议的加密硬件块中靠近线路获取的传输时间戳以及数据包传送子层获取的接收时间戳,如下图所示
如上图所示,当传输数据包时,加密块会获取时间戳 T1。当接收到数据包时,滑动窗口接收器会获取时间戳 T2。同样,当传输 ACK 或 NACK 数据包时会获取时间戳 T3,当数据包传送层接收到 ACK/NACK 数据包时会获取时间戳 T4。接收器在发送到发送器的每个 ACK/NACK 数据包中都包含时间戳 T1 和 T2。这允许计算以下两个延迟:
● rtt == T4 - T1
● fabric_delay == (T4 - T1 ) - (T3 - T2 )
此外,如果 NIC 的时钟同步,我们可以计算单向延迟:
● forward_delay == T2 - T1
● reverse_delay == T4 - T3
这里假设远程 NIC 和远程 Falcon 接收数据包之间的时间差非常小。当 ACK 合并发生在接收方时,接收方必须在生成的 ACK 数据包中包含最近收到的数据包的 T1 和 T2 时间戳。这可确保 fabric_delay 测量准确测量结构 RTT,而不会反映 ACK 合并延迟。fabric_delay 计算中涉及的四个时间戳的含义如下表所述:
有时需要使用指数加权移动平均 (EWMA) 算法来平滑延迟。EWMA 算法使用以下公式计算平滑延迟:
其中 α 是 EWMA 算法的平均常数。将 α 设置为 1 可得出瞬时延迟
Falcon 接收器为 2 个网络请求资源池(网络 Rx 数据包、网络 Rx 缓冲区)维护一个占用率 EMA。每次占用率发生变化时,都会更新占用率 EMA:
其中 α 是平均常数,可以为 2 个池单独配置。设置 α = 1 相当于获得瞬时占用率。发送 ACK/NACK 时,它会携带 Falcon 的量化缓冲级别。2 个池中的每一个都有 32 个量化级别,基于 31 个量化级别:如果:
则量化缓冲级别为 i。2 个池具有不同的量化级别集。ACK/NACK 携带 2 个池中的最大量化缓冲级别
Swift 是一种拥塞控制算法,旨在通过对测量的延迟信号做出反应来控制网络中的排队,通过对测量的远程 EMA 缓冲区占用做出反应来控制远程 NIC 缓冲区占用,以及对其他事件(如 NACK 和重传)做出反应。Swift 使用 2 个拥塞窗口(fcwnd 和 ncwnd,分别用于控制Fabric和 NIC 拥塞)来控制其速率,并且能够在 fcwnd < 1 时以踱步(pacing)模式运行。在踱步模式下,通过插入数据包间间隙延迟,使用定时轮来强制执行速率。在本节中,我们阐明了 Swift 的伪代码及其实现方面。在以下描述中,我们使用 fabric_delay 作为拥塞信号。但如果时钟同步,则可以用 forward_delay 替换它。 EMA 缓冲区占用率用 rx_buffer_leve 表示
10.3.2 重传时
Swift 为每个连接使用两个拥塞窗口。fcwnd 和 ncwnd 分别根据结构和 NIC 中的拥塞进行调制。当 fcwnd 大于或等于一时,Falcon 将未完成数据包的数量限制为 fcwnd 的整数部分。当 fcwnd 小于一时,未完成数据包的数量固定为一,并根据 RUE 指定的数据包间间隙启用数据包定步。ncwnd 始终为整数。
Swift 避免在一次往返内多次减少 fcwnd,并避免在一次往返内朝同一方向多次更改 ncwnd。因此,每个连接需要保存两个时间标记,每个窗口一个。时间标记用于标记一个时间,从该时间开始,在 RTT 期间内 fcwnd 或 ncwnd 不能朝某些方向改变(增加或减少)。但是,Falcon 使用有限的位数作为时间戳(例如 24 位数字),其中整数回绕可能很频繁。这带来了挑战,因为如果两个事件指示 fcwnd 或 ncwnd 朝同一方向变化(例如,两个 fcwnd 减少事件)相隔超过回绕时间,则第二个事件可能被视为在前一个事件的 RTT 内,因此被错误地忽略,如下图所示的稻草人窗口守卫(圆圈代表时间及其回绕),其中事件按其时间戳顺序发生(5、40、70,然后是 110)
因此,在 Falcon 中,窗口保护得到了改进,可以更好地容忍整数绕转。它利用中间指示不同方向变化的事件(如上图所示)来保持时间标记移动,同时保证正确性。因此,只要两个连续事件(无论其方向如何)之间的距离小于绕转纪元,Falcon 窗口保护就可以成功工作。这可以显著减少错误忽略的事件数量,因为即使一种特定类型的事件可能不频繁(例如,在非拥塞网络中减少事件),两个方向上事件的频率也应该主要在绕转纪元内。以下描述了两个时间标记的定义和更新策略。 fabric_window_time_marker
● 定义:在 fabric_window_time_marker 的 RTT 期间,fcwnd 不能减少。
● 更新:
○ 如果 fcwnd 减少,fabric_window_time_marker = 现在。
○ 如果 fcwnd 增加,并且现在 > RTT 晚于 fabric_window_time_marker,则 fabric_window_time_marker = now - RTT。nic_window_time_marker
● 定义:在 fabric_window_time_marker 的 RTT 期间,
○ ncwnd 不能减少
○ 如果上次 ncwnd 更改也增加,则 ncwnd 不能增加。
● 更新:
○ 如果 ncwnd 减少或增加,则 nic_window_time_marker = now。
○ 如果最后一个事件未改变 ncwnd,并且现在 > RTT 晚于 nic_window_time_marker,则 nic_window_time_marker = now - RTT。
此外,为了稳健起见,如果 fcwnd/ncwnd 没有被事件改变,但处于其最大/最小值,则出于时间标记更新目的,它们被视为增加(如果为最大值)或减少(如果为最小值)。下面是如何设置 fcwnd 时间标记的示例(ncwnd 时间标记可以根据上述策略类似地绘制)。情况 1 是针对上次我们减小 fcwnd 之后一个 RTT 内发生的事件,因此对于此事件,不能减小 fcwnd。在这种情况下,时间标记不会更新。情况 2 是针对在 RTT 过去之后发生的事件,因此可以根据需要减小 fcwnd。新的时间标记取决于事件中 fcwnd 是否减小。如果 fcwnd 减小,时间标记将设置为当前时间。如果 fcwnd 未减小,时间标记将设置为当前时间减去一个 RTT
PLB 尝试在不同的网络路径之间平衡负载。它在每个 ACK 事件时运行,主要涉及 3 个关键计算:
● 确定 ACK 是否反映拥塞(smoothed_delay > delay_target * plb_target_delay_multiplier)
● 计算在 RTT 内遇到拥塞的已确认数据包的比例。如果该比例不小于 plb_congestion_threshold,则认为 RTT 拥塞。
● 计算连续拥塞的 RTT。如果连续拥塞的 RTT 不小于 plb_attempt_threshold,则重新路由
速率更新引擎 (RUE) 负责计算给定连接的速率(或拥塞窗口)。它根据从数据包传送子层通过事件队列获得的一组信号(定义如下)计算该速率。速率更新引擎的输出通常是连接的速率(或拥塞窗口)。下面描述了其他详细输出
速率更新引擎由某些事件触发,例如收到 ACK、NACK 或重传超时。由于 RUE 中的计算有限(仅占数据路径的一小部分),我们需要决定何时为 RUE 生成事件以及何时不应该生成事件。拥塞控制的一个重要目标是提供网络带宽公平性,如果我们在 1 个往返时间 (RTT) 内更新每个活动连接,则可以保证实现这一目标。随着数据包大小的增加,数据包数量会减少,因为我们的带宽有限。例如(如下图所示),在数据包大小为 1667 B 时,嵌入式内核可以处理的最大数据包数量为 15 Mpps,在这种情况下,无需进行事件调度
但是,流量很少有统一大小的操作,Ack 总是低于该限制。因此,我们需要一个策略来实现 CC 目标。
● 带宽公平性。
● 拥塞消失后快速恢复。
我们设计了以下策略:我们针对事件队列占用率设置了三个阈值。数据包传送层使用两个谓词来决定是否将事件排入队列。
谓词 1 (Predicate, P1):自上次事件以来的时间大于配置阈值。此谓词确保拥塞事件消失时,cwnd 较低的连接可以尽快恢复。
谓词 2 (P2):自上次事件以来确认的字节/数据包大于阈值。此谓词确保具有大量小操作的连接不会使具有大操作的连接陷入困境。虽然确认的字节数是首选指标,但实现可以选择另一种方案(例如,确认的数据包、确认的单元等)作为复杂性权衡
RUE 和数据包传送子层之间的接口由一对生产者消费者队列定义:CC 事件队列和 CC 结果队列。以下部分定义了通过这些队列交换的消息的格式。
数据包传送子层是 CC 事件队列的生产者,RUE 是消费者。当收到 ACK 或 NACK 数据包或发生重传事件(由于提前重传或由于基于超时的重传)时,数据包传送子层会将 CC 事件生成到此队列中。发布到 CC 事件队列的事件的格式如下表所述
RUE 是 CC 结果队列的生产者,数据包传送子层是消费者。RUE 会针对从 CC 事件队列消费的每个 CC 事件生成一个 CC 结果并将其放入此队列。发布到 CC 结果队列的结果格式如下表所述
Falcon 错误处理包括数据包传递子层和事务层错误情况。相关资源包括物理资源(如数据包和缓冲区资源)和虚拟资源(如滑动窗口序列号)。所有 Falcon 物理资源最终都由超时保护和回收。事务子层负责回收 RX 数据包和缓冲区资源;事务子层必须与数据包传递子层合作回收 TX 数据包和缓冲区资源。为防止 TX 和 RX 资源回收之间的竞争条件,建议事务子层资源回收超时明显大于数据包传递子层的超时。在数据包传递子层,Falcon 的可靠性重传已在上面的部分中描述。Falcon 通过重新同步过程保证数据包传递子层的向前进展。下表分别总结了发送方和接收方的所有异常情况
在事务子层,下表分别总结了发起方和目标的异常情况
请注意:(1) 远程 ULP NACK 不适用于发起方拉取事务,(2) 推送事务的远程 ULP NACK-RNR 在数据包传送层处理
Falcon Transport Protocol Specification Revision 0.9, Date Submitted: 14 February, 2024 Date Approved: TBD
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。