术语
- NVMe OTCP: 在TCP上跑Nvme存储协议
- PDU: 协议数据单元
- TOE: TCP 卸载引擎
- HDS: header-data-split, 报文头数据分割, 通过将接收到的以太网帧中的标头和数据拆分到单独的缓冲区来提高网络性能。此功能默认处于禁用状态,可以在“属性”窗口的“高级”选项卡(性能选项)中启用
- ULP: Linux kernel upper layer protocol (ULP) ,Linux上层协议
- DDP: direct data placement (DDP), 直接数据放置
Nvme Over TCP简介
NVMe-TCP 是一种基于 TCP 的高性能流水线存储协议,它抽象了对存储控制器的远程访问,为主机提供了本地存储的幻觉。在 NVMe-TCP 中,每个存储队列都映射到一个 TCP 套接字。读写 IO 操作都有一个唯一标识符,称为命令标识符 (CID),服务器可以无序处理 CID,以允许小 IO 绕过大 IO 并提高性能。此外,每个 PDU 都受发送方生成并在接收方验证的应用层 CRC 保护。
正如去年在“自主 NVMe TCP 卸载”会议上所介绍的那样,为了卸载 NVMeTCP,但又不至于有完整卸载解决方案的缺点,引入了 Linux 内核上层协议 (ULP) 直接数据放置 (DDP) 卸载基础设施。它为请求-响应 ULP 协议(例如 NVMe-TCP)提供了根据标头标记将响应数据直接放置在预注册缓冲区中的能力。DDP 对于响应可能被重新排序的数据密集型流水线协议特别有用。
在此设计中,通过直接向 NIC 提供目标缓冲区,避免了 TCP 和目标缓冲区之间的复制。在处理接收路径时,NIC 还可以计算和验证 CRC,并在传输路径上计算 CRC。
议题
- 存储的分类
- DPU上Nvme Over TCP存储协议卸载的问题和解决方案
- 未来工作
存储分类及特点
- 计算和存储资源是分离的
- 存储一般放在专门集中的数据中心
- 这样做的优点
- 允许横向扩展(加机器方便)和更高效率(专用)
- 支持弹性升级, 类似金丝雀, 灰度, A/B发布等
- 不需要专用的SAN硬件, 用以太网就可以工作
- NVMe协议提供了简单和快速的控制和数据接口
- NvmeOTCP构建在无处不在的以太网上(TCP/IP + 以太网)
- 智能网卡和DPU已经支持云或虚机下的NVMeOTCP
- 对于NvmeOTCP, DPU提供额外的控制面Initiator端卸载能力
智能网卡与虚拟机下的存储特点
- 智能网卡提供以太连接和协议卸载(CSUM/TSO/LRO)
- NvmeOTCP Initiator端运行在HOST上的内核或用户态的SPDK上
- Virtio-blk/NVME后端仿真程序也跑在HOST上
- 所以这样架构需要消耗HOST上的CPU周期, 从而压缩租户的CPU使用
- 由于网络与存储共享带宽, 导致实现QOS变得复杂
DPU上的存储特点
- DPU提供本地NVMe/Virtio-Blk-PCI PF/VF设备给HOST
- Virtio-blk/NVme后端以及NvmeOTCP Initiator运行在DPU上
- Guest虚机使用直通的NVMe/Virtio-blk设备
- NvmeOF I端在DPU侧完成配置和卸载
- 此方案节约了HOST侧的CPU周期, 主要是NVME/VIRTIO仿真和NVMeOTCP协议卸载
- 通过对VF的单独节流, 提供对上层虚机更好的QOS带宽控制
DPU上NVMeOTCP卸载的问题
- 弹跳BUF:
- 第一次DMA从DPU网卡 -> DPU接收队列缓冲区
- 第二次DMA从DPU应用缓冲区 -> 主机内核缓冲区
- 2次DMA操作增加了DPU内存使用率
- 多个APP在DPU上的调度干扰问题
- 用户拷贝
- DPU用户态和内核态, HOST上用户态和内核态间, 增加了额外的数据拷贝
- 如果不消除这些拷贝, 会增加DPU内存使用率, 资源变得紧张后, 影响多租户业务
- PDU CRC
- NVMe Over TCP PDU有摘要算法
- 这些摘要算法消耗CPU周期
- PDU乱序
- 允许远端对PDU重新排序
- 虽然TCP是有序的, 但PDUs可能是乱序的
- 这种场景下, 不能只用直接内存拷贝
- 安全
- 传输中的数据, TLS安全
- OPAL TCG SED (静态数据)
- 卸载TLS变得很复杂
- 延迟
- 由于增加了DPU, 网络额外多了一跳, 增加了延迟
TCP卸载引擎(用户自定义TCP协议栈)
- 基于TOE在硬件中卸载TCP/IP协议
- 这种方式带来了以下弊端
- 消除了用户拷贝, 以及中间的弹跳BUF, 节约DPU内存
- 安全性可由网卡保证
零拷贝的套接字
- 发送时, 携带零拷贝MSG_ZEROCOPY的socket选项
- 用TCP_ZEROCOPY_RECIEVE选项, 将内核页映射到用户态
- 这种零拷贝要求内存页对齐
- 需要用户态的Initiator端做PDU的CRC校验
- 如果不是HDS, 则需要从网卡分散接收
- 每个接收需要执行map/unmap
- 消除了拷贝
- 但是无法避免弹跳缓冲区
- 由内核态保证数据安全
ULP DDP卸载
- 在内核上做Nvme Over TCP的卸载
- 其他方案不解决PDU CRC验证问题
- PDUs可以重新排序
- 支持HDS, 从接收环中获取TCP和PDU头, 从用户态缓冲区获取负载
- 存储协议免拷贝
- 消除了用户态拷贝
- 卸载PDU的CRC处理
- 无需PDU是一个问题
- 无法避免弹跳缓冲区
- 支持安全卸载到硬件
用IO URING实现零拷贝
- 在用户态和内核态间共享数据环(IO URING)
- 用户态通过SQ提交IO
- 内核态通过CQ完成IO
- IO_URING通过用户态内存进行注册
- 支持HDS(数据头分割) + RSS 流控
- 将用户态缓冲区填充到硬件接收队列
- 应用需要感知并适配从系统调用切换到IO_URING框架(lib_uring)
- 需要用户态的I端, 如SPDK
- 消除了用户态拷贝
- 无法避免弹跳BUF
- 在内核中处理安全
TCP设备内存(dmabuf机制)
- 使用P2P DMA将TCP负载通过数据直接从网卡放置到GPU中
- Linux的DMABUF机制允许在多个设备驱动间共享内存, 比如NIC和GPU间
- 从DMA缓冲池将Netlink消息填充到网络设备的接收队列中
- 避免将接收缓冲区映射和取消映射到用户态
- 网卡的HDS特性需要分割帧头和负载
- 网卡流控将流量引导到指定的接收队列
- 发送队列暂不支持
- 上行暂不支持
为NVMe over TCP适配到TCP 设备内存架构
- Udmabuf是一个内核特性, 可分配连续的DMA内存, 并提供给用户态使用
- 用户态I端支持TCP Devicemem框架, 将BUF通过Netlink发送到网卡
- 网卡需要支持HDS和流控
- 无法避免DMA弹跳BUF
- 不支持安全卸载
不同TCP性能优化的总结和对比
接下来的工作
- 探索在不同自定义TCP堆栈下, 避免DMA弹跳BUF的方案
- 探索TLS卸载, 避免DMA弹跳BUF
- 用udmabuf和TCP Devicemem扩展SPDK I端
- 使用TCP Devicemem扩展内核态的Nvme over TCP Initiator
参考
SNIA SDC 2024(9月16~18日, 加利福利亚州-圣克拉拉): 存储网络行业协会(SNIA)开发者大会(SDC’24)是一场非同寻常的活动!今年的议程安排十分紧凑,包含了 80 多场技术讲座,能让人深入了解人工智能 / 机器学习、CXL、Computational Storage(计算型存储)、数据基础设施、文件系统与协议等诸多领域的最新进展。我们的专家演讲者分享了对于塑造行业未来的前沿技术的宝贵见解: https://mp.weixin.qq.com/s/EfJkAlSSBYn9dXekIf1New
HDS(报文头数据分割): https://docs.nvidia.com/networking/display/winofv55054000/header+data+split
网卡卸载(TSO/UFO/LRO/GSO等): https://docs.redhat.com/ja/documentation/red_hat_enterprise_linux/6/html/performance_tuning_guide/network-nic-offloads#idm140718723667088
NVMeTCP 卸载实施和性能提升: https://netdevconf.info/0x16/sessions/talk/nvmetcp-offload-implementation-and-performance-gains.html, 在本次演讲中,我们将介绍 NVMeTCP 直接数据放置和 CRC(数据摘要)卸载设计以及支持它的驱动程序-硬件交互。我们将在各种比较和不同条件下展示卸载的性能优势。我们还将介绍我们面临的挑战以及我们如何解决这些挑战。
Linux ULP和DDP文档: https://patchwork.kernel.org/project/netdevbpf/patch/20240404123717.11857-10-aaptel@nvidia.com/
> +Request TCP byte-stream: TCP请求字节流
> +
> +.. parsed-literal::
> +
> + +---------------+-------+---------------+-------+---------------+-------+
> + | PDU hdr CID=1 | Req 1 | PDU hdr CID=2 | Req 2 | PDU hdr CID=3 | Req 3 |
> + +---------------+-------+---------------+-------+---------------+-------+
> +
> +Response TCP byte-stream: TCP响应字节流
> +
> +.. parsed-literal::
> +
> + +---------------+--------+---------------+--------+---------------+--------+
> + | PDU hdr CID=2 | Resp 2 | PDU hdr CID=3 | Resp 3 | PDU hdr CID=1 | Resp 1 |
> + +---------------+--------+---------------+--------+---------------+--------+
> +