在NVMe over PCIe中,I/O命令支持SGL(Scatter Gather List 分散聚合表)和PRP(Physical Region Page 物理(内存)区域页), 而管理命令只支持PRP;而在NVMe over Fabrics中,无论是管理命令还是I/O命令都只支持SGL。NVMe over Fabrics既支持FC网络,又支持RDMA网络。众所周知,在RDMA编程中,SGL(Scatter/Gather List)是最基本的数据组织形式。 SGL是一个数组,该数组中的元素被称之为SGE(Scatter/Gather Element),每一个SGE就是一个Data Segment(数据段)。其中,SGE的定义如下(参见verbs.h):

struct ibv_sge {
uint64_t addr;
uint32_t length;
uint32_t lkey;
};
  • addr: 数据段所在的虚拟内存的起始地址 (Virtual Address of the Data Segment (i.e. Buffer))
  • length: 数据段长度(Length of the Data Segment)
  • lkey: 该数据段对应的L_Key (Key of the local Memory Region)

而在数据传输中,发送/接收使用的Verbs API为:

  • ibv_post_send() - post a list of work requests (WRs) to a send queue 将一个WR列表放置到发送队列中
  • ibv_post_recv() - post a list of work requests (WRs) to a receive queue 将一个WR列表放置到接收队列中

下面以ibv_post_send()为例,说明SGL是如何被放置到RDMA硬件的线缆(Wire)上的。

  • ibv_post_send()的函数原型
#include <infiniband/verbs.h>

int ibv_post_send(struct ibv_qp *qp,
struct ibv_send_wr *wr,
struct ibv_send_wr **bad_wr);

ibv_post_send() posts the linked list of work requests (WRs) starting with wr to the send queue of the queue pair qp. It stops processing WRs from this list at the first failure (that can be detected immediately while requests are being posted), and returns this failing WR through bad_wr. 

The argument wr is an ibv_send_wr struct, as defined in <infiniband/verbs.h>.

struct ibv_send_wr {
uint64_t wr_id; /* User defined WR ID */
struct ibv_send_wr *next; /* Pointer to next WR in list, NULL if last WR */
struct ibv_sge *sg_list; /* Pointer to the s/g array */
int num_sge; /* Size of the s/g array */
enum ibv_wr_opcode opcode; /* Operation type */
int send_flags; /* Flags of the WR properties */
uint32_t imm_data; /* Immediate data (in network byte order) */
union {
struct {
uint64_t remote_addr; /* Start address of remote memory buffer */
uint32_t rkey; /* Key of the remote Memory Region */
} rdma;
struct {
uint64_t remote_addr; /* Start address of remote memory buffer */
uint64_t compare_add; /* Compare operand */
uint64_t swap; /* Swap operand */
uint32_t rkey; /* Key of the remote Memory Region */
} atomic;
struct {
struct ibv_ah *ah; /* Address handle (AH) for the remote node address */
uint32_t remote_qpn; /* QP number of the destination QP */
uint32_t remote_qkey; /* Q_Key number of the destination QP */
} ud;
} wr;
}; struct ibv_sge {
uint64_t addr; /* Start address of the local memory buffer */
uint32_t length; /* Length of the buffer */
uint32_t lkey; /* Key of the local Memory Region */
};

在调用ibv_post_send()之前,必须填充好数据结构wr。 wr是一个链表,每一个结点包含了一个sg_list(i.e. SGL: 由一个或多个SGE构成的数组), sg_list的长度为num_sge。

下面图解一下SGL和WR链表的对应关系,并说明一个SGL (struct ibv_sge *sg_list)里包含的多个数据段是如何被RDMA硬件聚合成一个连续的数据段的。

  • 01 - 创建SGL

从上图中,我们可以看到wr链表中的每一个结点都包含了一个SGL,SGL是一个数组,包含一个或多个SGE。

  • 02 - 使用PD做内存保护

一个SGL至少被一个MR保护, 多个MR存在同一个PD中。

  • 03 - 调用ibv_post_send()将SGL发送到wire上去

在上图中,一个SGL数组包含了3个SGE, 长度分别为N1, N2, N3字节。我们可以看到,这3个buffer并不连续,它们Scatter(分散)在内存中的各个地方。RDMA硬件读取到SGL后,进行Gather(聚合)操作,于是在RDMA硬件的Wire上看到的就是N3+N2+N1个连续的字节。换句话说,通过使用SGL, 我们可以把分散(Scatter)在内存中的多个数据段(不连续)交给RDMA硬件去聚合(Gather)成连续的数据段

最后,作为一个代码控(不喜欢纸上谈兵),贴一小段代码展示一下如何为调用ibv_post_send()准备SGL和WR以加深理解。

 1 #define BUFFER_SIZE     1024
2
3 struct connection {
4 struct rdma_cm_id *id;
5 struct ibv_qp *qp;
6
7 struct ibv_mr *recv_mr;
8 struct ibv_mr *send_mr;
9
10 char recv_region[BUFFER_SIZE];
11 char send_region[BUFFER_SIZE];
12
13 int num_completions;
14 };
15
16 void foo_send(void *context)
17 {
18 struct connection *conn = (struct connection *)context;
19
20 /* 1. Fill the array SGL having only one element */
21 struct ibv_sge sge;
22
23 memset(&sge, 0, sizeof(sge));
24 sge.addr = (uintptr_t)conn->send_region;
25 sge.length = BUFFER_SIZE;
26 sge.lkey = conn->send_mr->lkey;
27
28 /* 2. Fill the singly-linked list WR having only one node */
29 struct ibv_send_wr wr;
30 struct ibv_send_wr *bad_wr = NULL;
31
32 memset(&wr, 0, sizeof(wr));
33 wr.wr_id = (uintptr_t)conn;
34 wr.opcode = IBV_WR_SEND;
35 wr.sg_list = &sge;
36 wr.num_sge = 1;
37 wr.send_flags = IBV_SEND_SIGNALED;
38
39 /* 3. Now send ... */
40 ibv_post_send(conn->qp, &wr, &bad_wr);
41
42 ...<snip>...
43 }

附录一: OFED Verbs

A great ship asks deep waters. | 是大船就得走深水,是蛟龙就得去大海里畅游。

[SPDK/NVMe存储技术分析]010 - 理解SGL的更多相关文章

  1. [SPDK/NVMe存储技术分析]015 - 理解内存注册(Memory Registration)

    使用RDMA, 必然关系到内存区域(Memory Region)的注册问题.在本文中,我们将以mlx5 HCA卡为例回答如下几个问题: 为什么需要注册内存区域? 注册内存区域有嘛好处? 注册内存区域的 ...

  2. [SPDK/NVMe存储技术分析]003 - NVMeDirect论文

    说明: 之所以要翻译这篇论文,是因为参考此论文可以很好地理解SPDK/NVMe的设计思想. NVMeDirect: A User-space I/O Framework for Application ...

  3. [SPDK/NVMe存储技术分析]002 - SPDK官方介绍

    Introduction to the Storage Performance Development Kit (SPDK) | SPDK概述 By Jonathan S. (Intel), Upda ...

  4. [SPDK/NVMe存储技术分析]004 - SSD设备的发现

    源代码及NVMe协议版本 SPDK : spdk-17.07.1 DPDK : dpdk-17.08 NVMe Spec: 1.2.1 基本分析方法 01 - 到官网http://www.spdk.i ...

  5. [SPDK/NVMe存储技术分析]001 - SPDK/NVMe概述

    1. NVMe概述 NVMe是一个针对基于PCIe的固态硬盘的高性能的.可扩展的主机控制器接口. NVMe的显著特征是提供多个队列来处理I/O命令.单个NVMe设备支持多达64K个I/O 队列,每个I ...

  6. [SPDK/NVMe存储技术分析]008 - RDMA概述

    毫无疑问地,用来取代iSCSI/iSER(iSCSI Extensions for RDMA)技术的NVMe over Fabrics着实让RDMA又火了一把.在介绍NVMe over Fabrics ...

  7. [SPDK/NVMe存储技术分析]005 - DPDK概述

    注: 之所以要中英文对照翻译下面的文章,是因为SPDK严重依赖于DPDK的实现. Introduction to DPDK: Architecture and PrinciplesDPDK概论:体系结 ...

  8. [SPDK/NVMe存储技术分析]012 - 用户态ibv_post_send()源码分析

    OFA定义了一组标准的Verbs,并提供了一个标准库libibvers.在用户态实现NVMe over RDMA的Host(i.e. Initiator)和Target, 少不了要跟OFA定义的Ver ...

  9. [SPDK/NVMe存储技术分析]011 - 内核态ib_post_send()源码剖析

    OFA定义了一组标准的Verbs,并在用户态提供了一个标准库libibverbs.例如将一个工作请求(WR)放置到发送队列的Verb API是ibv_post_send(), 但是在Linux内核,对 ...

随机推荐

  1. 浅谈.net core如何使用EFCore为一个上下文注类型注入多个实例用于连接主从数据库

    在很多一主多从数据库的场景下,很多开发同学为了复用DbContext往往采用创建一个包含所有DbSet<Model>父类通过继承派生出Write和ReadOnly类型来实现,其实可以通过命 ...

  2. PL/SQL批量执行SQL脚本文件

    1.选择File > New > Command Windows(命令窗口) 2.输入 @ 符号,之后敲击回车键.从本地选择执行的 SQL脚本 等待批量命令全部执行成功,关闭页面即可 新增 ...

  3. 通过Dapr实现一个简单的基于.net的微服务电商系统(二十)——Saga框架实现思路分享

    今天这篇博文的主要目的是分享一下我设计Saga的实现思路来抛砖引玉,其实Saga本身非常的类似于一个简单的工作流体系,相比工作流不一样的部分在于它没有工作流的复杂逻辑处理机制(比如会签),没有条件分支 ...

  4. Maven-compiler-plugin 插件编译过程中报缺失get/set方法

    一般都是由于使用了lombok插件导致的 解决方法: 首先将compiler插件版本升级到3.3以上, 查询资料发现说有可能是版本太低导致 在configuration中添加上annotationPr ...

  5. 在命令行中输入python会跳转到商店问题解决,python环境变量的配置

    安装python出了点问题,明明安装了,在应用商店显示已获取,可是在命令行输入python检验时就直接跳转到win10系统自带的应用商店...... 这不免让我怀疑是不是没有安装好python~但是它 ...

  6. C#析构函数(方法)

    析构方法是在垃圾回收.释放资源时使用的.析构函数用于析构类的实例.备注:    不能在结构中定义析构函数.只能对类使用析构函数.    一个类只能有一个析构函数.    无法继承或重载析构函数.    ...

  7. 第九周shell脚本编程练习

    转至:http://www.178linux.com/88838 1.写一个脚本,判断当前系统上所有用户的shell是否为可登录shell(即用户的shell不是/sbin/nologin):分别这两 ...

  8. Html实现背景科技流星雨效果

    <!doctype html><html><head><meta charset="utf-8" /><title>流星 ...

  9. VT 入门篇——最小 VT 实现(上)

    写在前面   此系列是本人一个字一个字码出来的,包括示例和实验截图.由于系统内核的复杂性,故可能有错误或者不全面的地方,如有错误,欢迎批评指正,本教程将会长期更新. 如有好的建议,欢迎反馈.码字不易, ...

  10. JavaSE高级编程之多线程

    4. 多线程 4.1 基本概念:程序.进程和线程 程序.进程和线程 程序:为了完成特定的任务,用某种语言编写的一组指令的集合.程序是一段静态的代码,静态对象. 进程:是程序的一次执行过程或正在运行的程 ...