深入理解SPDK NVMe/TCP transport的设计

一. 简介

NVMe/TCP transport是NVM express这个标准组织为NVMe over Fabrics(NVMe-oF) 制定的一个基于TCP的新的传输层。这个Technical proposal (TP 8000)自去年11月份发布以来,相应的代码在内核态(Linux kernel)和用户态(SPDK库)中均有了支持。

二. SPDK NVMe/TCP

代码解析

总的来讲,SPDK NVMe/TCP transport的整个设计遵循了SPDK无锁,轮询,异步I/O的理念,如Table1所示。根据TP8000 specification中的定义,每个TCP连接(TCP connection)对应于一个NVMe的qpair。在SPDK的实现中,我们依然采用group polling的方法来管理所有的TCP连接。每一个SPDK thread上运行一个TCP相关的polling group,每一个TCP连接只会被加入一个TCP polling group中,由这个polling group处理后续的所有事件,那么这个TCP连接将会被唯一的CPU core处理,这样就不会存在CPU的竞争情况:不同CPU竞争处理同一个TCP connection。这样的设计在很大的层面上避免了CPU资源的竞争。另外目前存在很多用户态的TCP协议栈,为此SPDK封装了一些socket API相关的操作。目前,NVMe/TCP的实现代码直接使用SPDK封装的API进行socket的操作,如此一来,我们可以接入不同种类的Socket API实现,诸如VPP,mTCP,fstack,seastar等。只要能够实现SPDK socket API所定义的抽象函数,就可以整合到SPDK的sock库中。

Table 1 SPDK NVMe/TCP transport设计理念

NVMe-oF transport(传输层)的抽象

SPDK定义的NVMe-oF的框架剥离出了NVMe或者NVMe-oF处理逻辑的共同代码,然后针对所有的transport提供了一个统一的抽象接口。那么每个transport只需要实现这个接口里面的函数或者数据结构即可。对于TCP transport也是一样,target端和host端完全遵循这个设计。Figure1给出了目前SPDK软件库实现的或者将要实现的transport。其中Fibre Channel的支持的patch还在review 过程中,TCP transport和VPP的stack可以整合,但是由于VPP stack的一些稳定性原因,所以也标记为“在整合过程中”。

Figure 1 SPDK NVMe-oF对各个transport的支持

另外Table2 给出了SPDK在target 和host端对transport支持的源文件的一些路径。

Table 2 SPDK对transport支持的一些文件路径

NVMe/TCP transport的信息管理

在target端的TCP传输层主要对以下信息进行了管理:

  • Portals: 可以定义为<IP address, port>,每个NVMe 子系统都可以监听几个不同的Portal。另外每个portal可以被不同的NVM subsystem共享。所有的Portal可以被TCP传输层的数据结构(struct spdk_nvmf_tcp_transport)进行统一管理。另外每个Portal都有一个引用计数,用于统计多少NVM subsystem在共享这个portal。例如,如果<127.0.0.1, 4420>被三个子系统使用,那么引用计数是3。 在对这个portal销毁的时候,只有所有3个NVM subsystem被销毁,这个Portal才会被删除。
  • 共享数据池(Data buffer Pool): NVMe/TCP transport的读/写命令,都需要分配相应的buffer,然后进行后续的读写。当每个NVMe读/写命令(包含在NVMe/TCP transport的Command Capsule PDU中)被正确解析后,我们就会在共享数据池中分配所需要的data buffer。使用共享数据缓冲池的目的是为了控制内存使用的footprint。如果所需要的内存根据每个qpair的深度(Queue depth),那么所需要的内存大小将随着qpair的增加而线性增加, 这样对于支持大量TCP 连接而言是巨大的内存消耗。在SPDK的设计中,数据缓冲池由每一个CPU core上的TCP polling group共享,那么这些polling group之间必然会存在对这个共享池的数据竞争。为了很好的缓解这个数据竞争的问题,我们采用以下策略,即每个polling group在这个共享池中预先保留了一些data buffer,这些data buffer组成了一个buffer cache。那么这将保证每个TCP polling group 都有内存可以分配,杜绝了starvation(饥饿)的产生。因为在buffer cache中分配的数据buffer在使用完毕以后,依然会被回收到相应的polling group对应的buffer cache中。
  • TCP polling group中的socket管理: 首先SPDK对Socket套接字的API进行封装,这样我们既可使用内核态的TCP/IP栈的所对应的Posix API,也可以利用用户态的API(诸如VPP,用户也可以根据SPDK 对于socket API的抽象定义,整合其他用户态协议栈)。所以我们使用的函数是以spdk_sock_为前缀的函数。 例如,我们正常使用“listen”函数来监听端口,在SPDK 里面使用spdk_sock_listen,SPDK的TCP polling group(数据结构是struct spdk_nvmf_tcp_poll_group)中有个sock_group的指针。这个sock_group会在TCP polling group创建的时候被同时创建,这个sock_group用于管理所有映射到这个TCP polling group的所有TCP连接的socket。当一个TCP scoket连接被建立的时候,一定会被加入某个TCP polling group(目前使用的是Round Robin的算法),那么这个TCP的socket同时会被加入到这个TCP polling group的sock_group 中。

一个socket在socket polling group的周期,可以分为以下三类:

1)加入某个socket polling group 我们可以在Linux 系统中可以使用epoll相关的操作(例如,epoll create创建一个event相关的fd),然后通过epoll_ctl将socket信息(实际是fd,file descriptor)绑定到一个这个由epoll 创建的event fd中。

2)在polling group 被轮询 然后我们就可以利用epoll来检查每个其中的socket是否有EPOLLIN的事件(来自远程的数据);如果有相应的数据监测到,将会读取数据进行后续处理(实际上是调用spdk_nvmf_tcp_sock_cb这个回调函数)。

3)在socket polling group被删除 另外在轮询过程中,监测到TCP 断开时,我们会将这个socket从这个polling group中删除(比如在Linux系统中是调用epoll_ctl在event fd中解绑那个socket的fd信息),那么这个socket将不会被处理。

NVMe/TCP PDU的生命周期管理

无论在target还是host 端,SPDK 都采用了同样的状态机对一个PDU的生命周期进行管理(如图2所示),其中SPDK定义了5个状态:

  • Ready: 等待处理新的PDU。
  • Handle CH: TCP 连接收到数据,收到8个bytes后,判断PDU的类型,进行一系列的检查。如果错误,则进入到错误状态(Error State); 否则进入到下一个状态(Handle PSH)
  • Handle PSH: 这个状态用于处理PDU的specific header。如果处理出错,则进入错误状态(Error State),否则要么要么进入ready状态等待处理新的PDU,要么进入处理payload的状态(Handle payload)
  • Handle payload: 是主要用于处理PDU中包含的数据。而这些PDU只可能是CapsuleCmd,C2HDATA,H2CDATA,H2CTermReq,C2HTermReq。处理结束以后,要么进入ready状态要么进入错误状态(Error State)。
  • Error State: 如果这个TCP 连接 在接收 PDU的时候处于错误状态(Error State),那么这个TCP 连接会给对端发送TermREQ命令,意味着这个TCP 连接在不久将要被关闭。

Figure 2 NVMe/TCP PDU生命周期管理

Target端NVMe/TCP request的生命周期管理

在target端我们对nvme tcp request(struct spdk_nvmf_tcp_request) 定义了10个状态(如Figure3 所示),Figure4 给出了相应的状态转化图,清晰的描述了一个NVMe/TCP request的生命周期。这和目前SPDK 主分支里面的代码是完全一致的。

Figure 3 Target端NVMe Request的状态

Figure 4 NVMe TCP/Request的生命周期管理

学习地址:http://ke.qq.com/course/5066203?flowToken=1043717

更多DPDK学习资料有需要的可以自行添加进入学习交流君 羊 793599096 免费获取,或自行报名学习,免费订阅,永久学习,关注我持续更新哦!!!

原文链接:https://blog.csdn.net/weixin_37097605/article/details/101514681

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值