找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1166

积分

1

好友

156

主题
发表于 前天 18:08 | 查看: 4| 回复: 0

随着大规模AI训练与推理的兴起,RDMA(远程直接内存访问)在多路径/自适应路由(AR)等场景中的应用日益广泛,这使得其对乱序(Out-of-Order, OOO)数据传输的支持成为一个关键技术热点。支持OOO通常与支持直接数据放置(DDP)相关联,但实现全面的OOO支持远不止于此。本文将以IRN(一个对RoCE v2的增强方案)为例,系统梳理在改造所有RDMA操作以支持乱序时,需要关注和解决的几个核心问题。

第一个数据包问题

这个问题已有多方讨论。其核心在于,对于某些操作(如RoCE v2的RETH头),DDP的关键信息仅存在于第一个数据包中。为了在支持DDP的同时避免网卡缓存报文,解决方案是让每个数据包都携带RETH头。此处不再赘述。

WQE匹配问题

在RDMA中,部分操作要求每个到达的数据包都必须与接收端对应的WQE(工作队列元素)进行匹配。例如:

  • SEND操作:每个数据包必须被放置到对应Recv WQE缓冲区的正确偏移位置。
  • WRITE-with-Immediate操作:需要将Immediate数据放入对应的WQE。
  • READ Request与Atomic Request:请求包本身可能被分片,网卡需要将这些分片“重组”到一个请求WQE上下文中。

在数据包顺序到达的传统模式下,这种匹配是隐式完成的。然而,当数据包无序到达时,隐式匹配机制将失效。解决方案是为WQE分配显式的序列号,并携带在数据包头中,用于标识每个数据包所属的WQE。具体可分为两类场景:

1. Send 与 Write-with-immediate

这类操作要求接收端的Recv WQE按照其提交顺序被消耗。因此,可以为每个Recv WQE以及对应的请求WQE维护一个recv_WQE_SN(接收WQE序列号),用于指示它们的发布顺序。该值被携带在所有SEND数据包以及最后一个Write-with-Immediate数据包中,用于识别正确的目标Recv WQE。IRN方案还要求数据包携带其在WQE数据区域内的相对偏移量(offset),以实现数据的精准放置。

实施流程

  • 发送端为每个Recv WQE分配一个单调递增的recv_WQE_SN
  • 发送端构造数据包时,在包头中携带目标recv_WQE_SN和载荷偏移量offset
  • 接收端网卡(NIC)解析到recv_WQE_SN后,在Recv队列中查找对应的WQE,并根据offset将数据直接放置到缓冲区的正确位置。
  • 当该WQE的所有数据字节都到达后,网卡生成一个完成队列元素(CQE)。

关键点recv_WQE_SN由接收端生成,但发送端如何知晓?这依赖于RDMA协议在保序条件下保证的语义一致性。发送端和接收端对“第N个消息对应第N个Recv WQE”有共识,发送端可用自身的发送序号推导出远端的recv_WQE_SN

2. Read 与 Atomic

这类操作采用请求/响应模式,其接收端不涉及Recv WQE。核心挑战在于语义保序:一个RDMA READ或ATOMIC请求,必须等到所有在它之前发布的请求都处理完毕后才能执行。乱序到达的请求包可以提前被缓存,但不能乱序执行。

例如,两个READ请求R1和R2被分片。如果R2的第二个分片(P2b)先于R1的分片到达,网卡必须能够:1)识别P2b属于请求R2;2)在R1及其自身所有分片都到达前,不执行R2。

解决方案:IRN为每个本地发起的Read/Atomic请求WQE维护一个read_WQE_SN。所有请求包都携带此序列号。接收端网卡维护内部的Read WQE上下文缓冲区(slot)。乱序到达的包按其read_WQE_SN被放入正确的slot中缓存,仅当轮到该序列号(即前面的请求均已处理)时,网卡才将其提交给执行流水线,从而在包乱序到达的情况下保证请求的顺序执行。

最后一个数据包问题

对于许多RDMA操作(如Write-with-Immediate),关键信息(如Immediate数据、完成元数据)仅包含在最后一个数据包中。启用OOO后,需要跟踪并妥善处理提前到达的“最后一个包”。

IRN方案概述

  1. 消息序列号(MSN):响应方维护一个MSN,当接收到写/发送消息的最后一个包,或接收到读/原子请求时,MSN递增。MSN通过ACK包返回给请求方,用于使相应的请求WQE失效。
  2. 包状态跟踪:响应方为每个包偏移维护一个2-bit的状态映射(bit0: 是否已到达 has_arrived;bit1: 是否为最后一个包 is_last_pkt)。
  3. 延迟完成处理:如果最后一个包提前到达,网卡会解析其中的recv_WQE_SN和完成元数据,将其存入一个临时的pending_cqe_meta区域(可在主机内存),但不会立即触发CQE生成。仅当该消息的所有前置数据包都到达后,才执行MSN递增、WQE失效并生成CQE。

对Read/Atomic的作用:同样,如果READ/Atomic请求的最后一个包先到,也必须将其缓存,直到该请求之前的所有请求都完成,才能触发执行和后续的MSN更新。

旧消息的重传覆盖问题

考虑一个场景:消息A和消息B操作的内存区域存在重叠。由于网络问题,消息A的某个包延迟到达或触发重传。此时接收端可能已在处理消息B。如果启用OOO放置,这个迟到的旧包仍会被直接写入旧地址,从而可能覆盖消息B已经写入的新数据。

解决此问题主要有两种策略:

1. 应用层 Fence
  • 工作请求级Fence:在提交WR时设置IBV_SEND_FENCE标志。这会强制发送端在执行该带Fence的WR前,等待所有先前发出的有序操作完成,防止新旧请求的包在接收端产生干扰。
  • 应用协议级Fence:在应用层设计协议,例如,只有在收到前一个操作的完成确认(ACK)后,才重用或释放对应的缓冲区,从根本上杜绝旧包写入已复用内存区域的可能。这涉及到对网络/系统底层操作语义的精确控制。
2. Fence Indicator(网卡/驱动层实现)

这是一种更底层的方案。网卡或驱动为每个接收缓冲区维护一个“围栏”指示器。当应用提交一个新的WQE,且其缓冲区地址与某个尚未完全完成的旧WQE缓冲区重叠时,驱动会标记新WQE的Fence Indicator。网卡在收到数据包时,会检查该标志,如果包属于旧的重叠请求,则将其丢弃或特殊处理,防止数据覆盖。

其他考量

某些应用(如早期的FaRM)严重依赖轮询特定内存位置来检测操作完成,这与OOO数据放置异步完成的特性不兼容。这类情况通常需要应用迁移到官方支持的完成机制(如Write-with-Immediate)。对于这类强依赖保序的场景,应继续使用强顺序(strongly ordered)模式。

值得一提的是,类似的能力在iWARP协议中已有原生支持。iWARP类似Falcon方案,具备MSN机制,可基于此进行WQE索引,天然更好地处理了部分乱序问题。




上一篇:eBPF TC程序挂载流程与Netlink原理解析
下一篇:Elasticsearch近实时查询原理与架构深度剖析
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-17 19:01 , Processed in 0.129144 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表