支持网络的文件系统底层技术

作为GPU Direct IO技术的延续,这篇讨论下基于网络的文件系统的底层技术。

综述

借用一幅图,基本上把所有网络存储技术都包含了进来

storage network introduction

解释下上面涉及到的名词:

  • ULP: upper layer protocol,上层协议栈
  • libfabric: open fabric(OFED的维护组织)定义的用户态库
  • kfabric: open fabric定义的内核模块
  • srp: scsi rdma protocl over infiniband
  • iser: iscsi extension for rdma
  • NVMe/F: NVMe over fabric
  • NFSoRDMA: NFS over RDMA
  • SMB Direct: server message block, Windows服务器上的技术, MS-SMB
  • LNET/LND: Lustre networking,分布式文件系统Lustre的网络技术,其它著名的分布式文件系统还有GlusterFS
  • iscsi: scsi based on tcp/ip

DataStorage 和DataAcces的差异

这里要重点强调下这两个概念,数据的存储和数据的访问是两个不同的概念,
存储一般要经过文件系统,而访问是不用的,由于要经过文件系统,他们的延时差异也很大。

ds vs da

ds da latency

本文只介绍数据存储,接下来围绕开篇提到的几个技术展开。

DAS 直连式存储

讲网络存储之前,还是先介绍下传统的Direct Attached Storage直连式存储。
说白了,就是常见的PC或者服务器直接连着硬盘的。
以常见的scsi为例子,应用下发的文件访问,在scsi层都转换为scsi命令发给scsi存储控制器处理,
对应到kernel中,就是常见的 scsi驱动 了。

如果对整个层次不清楚的话,可以了解下linux storage的全景图:

linux storage stack overview

ISCSI

为了支持网络存储,linux kernel在scsi层下加了一个传输层,把scsi命令通过网络报文发出来。
这个发出报文的client,在scsi命名空间中称为initiator, 而接受处理报文的称为target,这两个名词概念后文会税涉及到。

iscsi packet

SRP & ISER

SRP和ISER均能通过RDMA支持SCSI,且都已经被内核支持。
相对来讲,ISER比SRP更好,具体可以从以下几个方面对比:

srp & iser

iser典型应用场景如下:

iser case

其中的数据流如下:

iser dataflow

以mlx为例,kernel中的逻辑视图如下:

iser kernel

ISER Initiator

按照道理讲,应该先说Target,再说Initiator比较好,但是由于Target比较复杂,所以放在后G面讲。
假设Target已经配置好,Initator怎么用呢?主要是以下几步:

  • discovery: iscsiadm -m discovery -t st -p target-ip-address
  • login: iscsiadm -m node -T iqn.2006-04.com.example:3260 -l
  • mkfs & mount: mkfs.ext4 /dev/disk_name; mount /dev/disk_name /mount/point

整个流程如下:

+--------------------------------------------------------+             
| Targets & Sessions configuration files and directories |             
+--------------------------------------------------------+             

+--------------------------+     +----------------------------------+ 
| iscsiadm                 |     | iscsid: iSCSI daemon             | 
|                          |     |                                  | 
|  * Command line tool     |<--->|  * Implements Session management | 
|  * Manages database of   |     |  * Communicates with iscsiadm    | 
|    sessions and targets  |     |    and iscsi kernel modules      | 
+--------------------------+     +---------------+------------------+ 
|                    
User space                                       |                    
- - - - - - - - - - - - - - - - - - - - - - - - - | - - - - - - - - - -
Kernel                                           v                    
+-----------------------------------------------------------+ 
| kernel modules: scsi_transport_iscsi, iscsi_tcp, libiscsi | 
+-----------------------------------------------------------+ 

内核中iser intiator位于infiniband/ulp中,它会注册到scsi transport里面,把scsi报文封装到IB报文中。

iser kernel configure

调用栈如下(不包括scsi层之上)

scsi_transport_iscsi.c
    netlink_kernel_create(&iscsi_if_rx)
    iscsi_if_rx
     iscsi_if_recv_msg
      transport->create_session
      transport->create_conn
      transport->bind_conn
      transport->create_session
      transport->create_session

ib_isert.ko:
    iser_init
     iscsi_register_transport(&iscsi_iser_transport)

scsi_lib.c:
    scsi_dispatch_cmd
     transport::queuecommand
      iscsi_queuecommand
       iscsi_data_xmit
        xmit_task
         iser_send_control(registered completed callback iser_ctrl_comp)
          ib_dma_sync_xxx

ISER Target

Target侧的实现有很多种方案,主流主要有三种:

  • LIO/TCMU: LIO后面名字改成了TCM(Target Core Mod), TCMU是TCM的用户态,已经被Linux Kernel主线支持。由linux-iscsi.org运营维护,主要的开发者是Nicholas A.Bellinger。
  • SCST: the generic SCSI target subsystem for Linux,名字最官方,可惜一直没合入到Linux社区,且和LIO的作者一直不合。
  • STGT(也叫TGT): 作者是NTT的FUJITA Tomonori和Redhat的Mike Christie,最早的版本是包含内核态的,后面去掉了。

推荐使用LIO/TCMU方案。
其中STGT是纯用户态的,但基本不用了,SCST和LIO/TCMU都有内核模块,但SCST的内核模块并没有合入到内核主线。
更详细的对比可以参考 SCST的官网

iser targets

STGT(TGT)

网上的材料都比较老了,很多图里面都画着它有内核态,最初的版本确实是有,只是后面都去掉了,
这个链接的patch是最早在kernel中加入netlink的patch:scsi tgt: scsi target netlink interface
喜欢考古的兄弟可以结合github上老的tag版本结合一起看看。

最新的版本是纯用户态的,iser的支持都在user/iscsi/iser.c中,简单的调用流如下:

iser.c:
 iser_device_init
  ibv_create_cq
  ibv_req_notify_cq
  tgt_event_add(cq->fd, &iser_handle_cq_event)

 iser_handle_cq_event(如果ib有cq产生,fd唤醒epoll)
  iser_poll_cq(ibv_poll_cq)
   handle_wc
   iser_queue_task
    iser_sched_iosubmit
     iser_scsi_cmd_iosubmit
      target_cmd_queue
          cmd_perform
     sbc_rw
      bs_cmd_submit
       bs_rdwr_request
        pread/pwrite(系统调用,读写)

tgtd.c:
 main
  event_loop
   epoll_wait(cq->fd)
    handler

iser的流程图就不画了,和下面这个tcp的图很类似:

iser tgt tcp

SCST

源码除了从官网下载,也可以从github下载 SCST-project, 其实还是很活跃的,
从2.6到5.14的内核都可以支持,最新的tag版本是v3.6,发布与2022年1月。

SCST的作者和LIO的作者似乎有些不同的意见,在其官网中也一直强调它的优势,由于精力实在有限,就不深入挖掘它的用法了。
David画了一个很漂亮的图:

iser SCST

LIO/TCMU

中国移动苏研主推这个方案,且在社区中贡献了不少patch。
主要配置工具是python做的targetcli,它提供了一个交互shell,步骤如下:

  • Creating a Backstore
  • Creating an iSCSI Target
  • Configuring an iSCSI Portal
  • Configuring LUNs
  • Configuring ACLs

TCMU分为kernel和用户态两部分,用户态现在主要是tcmu-runner, 内核部分主要有以下几个ko:

  • target_core_user
  • target_core_mod
  • iscsi_target_mod
  • ib_isert

他们之间的交互关系如下图:

iser tgt tcp

在配置iscsi portal时,会通过ib_isert创建QP,通过中断的方式监听IB报文

lio_target_call_addnptotpg
 iscsit_add_np
  iscis_target_setup_login_socket
   isert_setup_np
    isert_setup_id
     isert_connect_request
      isert_create_qp
       ib_cq_pool_get(IB_POLL_WORKQUEUE 内核的IB poll线程监听有没有cq事件,如果有则调用isert_recv_done,监听intiator发过来的报文)
       rdma_create_np(isert_qp_event_callback 中断回调函数, 监听硬件中断)

收到报文

isert_recv_done
 isert_rx_opcode
  isert_handle_scsi_cmd
   iscsit_setup_scsi_cmd
    target_cmd_parse_cdb
     transport->parse_cdb
      tcmu_parse_cdb
       tcmu_queue_cmd
        uio_event_notify 通知用户态

用户态(tcmu-runner)

tcmur_cmdproc_thread
 while(1) {
    tcmulib_processing_start
      read() // block等待,直到收到kernel通知
     tcmulib_getnext_command
      device_cmd_tail != device_cmd_head //command ringbuffer 中有新的命令
     tcmur_generic_handle_cmd
      handle_read/handle_write
       tcmur_cmd_complete
        handle_generic_cbk
         aio_command_finish
          tcmur_tcmulib_cmd_complete
          write() //通知kenrel,处理完command, 触发kernel uio的中断处理
 }

用户态处理完报文通知内核:

tcmu_irqcontrol
 tcmu_handle_completions
  target_complete_cmd
       target_complete_ok_work
    queue_data_in
     lio_queue_data_in
      iscsit_queue_data_in
       isert_put_datain
        isert_rdma_rw_ctx_post
         rdma_rw_ctx_post
          ib_post_send  //通知intiator 处理完

完整的处理流程可以参考下面这个图,虽然不太对得上,但逻辑是ok的

iser tgt tcp

NVMe/F

nvme软件栈相对于scsi来讲,就简单很多,host和target都在内核的drivers/nvme目录下,
而且支持多种传输机制的支持也包含在这个目录中。工作机制如下图:

nvme

Target侧,打开来看如下图:

nvme target

详细的流程如下:

host侧:

发包
//大部分代码在drivers/nvme/host/rdma.c
queue_rq
 nvme_rdma_queue_rq
  blk_mq_start_request
  nvme_rdma_post_send
   ib_post_send

收包
nvme_rdma_recv_done
 nvme_rdma_process_nvme_rsp
  blk_mq_rq_to_pdu

target侧:

初始化
//大部分代码在 drivers/nvme/target/rdma.c
nvmet_enable_port
 nvmet_rdma_add_port
  nvmet_rdma_enable_port
   rdma_create_id(注册中断回调函数nvmet_rdma_cm_handler, 当RDMA HCA收到RDMA报文时调用)

收到控制面报文
nvmet_rdma_cm_handler
 nvmet_rdma_queue_connect

收到数据报文
nvmet_rdma_recv_done
 nvmet_rdma_handle_command
      nvmet_req_init
   nvmet_parse_io_cmd
    nmve_cmd_read/nvme_cmd_write (分析命令,针对不同的后端,设置回调函数, nvmet_bdev_execute_rw)
  nvmet_rdma_execute_command
    req.execute(调用回调函数)
     submit_bio
      blk_mq_submit_bio
       nvme_queue_rq(又回到本地的nvme host,调用nvme读写)a
  nvmet_req_complete
   nvmet_rdma_queue_response
    ib_post_send(返回请求)

NVME Target Offload

观察上面的流程,会发现要回到block层绕一圈,于是MLX提出了直接访问NVME的优化机制,具体如下:

nvme target offload

这个改动软件上主要是rdma-core的改动,控制面上创建好share queue,并把这个传递给硬件,kernel并不需要改动,
但是这个RFC并没有合入到rdma-core的主线,具体的RFC patch和实现可以参考下面几个链接:

libfabric

libfabric一般配合libibverbs(https://github.com/linux-rdma/rdma-core)使用。

libfabric

libfabric

但亚马逊的Elastic Fabric Adapter用法不太一样,EFA直接在linux kernel实现了libfabric的provider驱动。

libfabric

kfabric

在linux kernel,由于上层SRP、iSER、NVMe/F、NFSoRDMA要调用ib的verbs,而各个厂家的驱动层也要实现
类似的回调函数,OFED组织又做了一层抽象框架命名为kfabric,但是这个模块还一直没有何如到linux kernel主线,
最近的一次更新也是在16年了,可以认为已经停止开发了。

kfabric

主要用于用LNET文件系统,可惜从linux kernel 4.18之后,lustre分布式文件系统被移除了内核,
但lustre官方仍在维护out of tree的代码,且由于ARM在hpc领域的绽放,linaro也在维护arm的版本,具体可以参考他们的官网。

kfabric

libfabric和kfabric

根据上节的介绍,其实libfabric和kfabric并不一定要配合使用,具体差异参考下图

libfabric vs kfabric

SMB

由于是windows上的技术,这里就不介绍了。

八卦

以前高端网卡基本都是intel和broadcom的市场,后面微软找MLX在数据中心合作,结果
MLX在25G时代一把上位。后面亚马逊又收购了同处于以色列的Annapuna Lab,做了自己的
RDMA,也就是后面的EFA(https://github.com/amzn/amzn-drivers)。

参考

知道是不会有人点的,但万一有人呢:)