支持非对称命名空间访问的SPDK多路径验证

这是一个有点绕口的标题。我们先来拆分一下,里面包含了三个概念:命名空间非对称访问,以及多路径。多路径(multipath),可以针对单点故障提供容错能力,还可以为I/O流量提供负载平衡。当多路径驱动程序检测到活动路径的I/O错误时,它会将流量从故障路径转移到设备指定的辅助路径。当首选路径恢复正常时,可以将控制权返回到首选路径。本文主要围绕一个测试用例来介绍这些功能在SPDK中的支持和实现。

先看几个名词

1

非对称命名空间访问 (ANA)

共享命名空间(namespace)可以借助于多PCIe端口或NVMe over Fabrics等网络端口,通过NVMe存储控制器(controller)被访问。如果,controller的namespace访问特征受到NVM subsystem内部配置的影响,那么就会发生非对称访问(Asymmetric Namespace Access , 简称ANA),反之称为对称访问。

ANA的状态,有Optimized(最优访问)和Non-Optimized(非最优访问),还有Inaccessible(不可访问)等,SPDK暂不支持Persistent Loss(持久消失)。

图1.1展示了一个非对称访问的例子,这是一个包含双端口NVMe SSD的subsystem,呈现了两个独立的域。NVMe namespace B (图中NS B)和namespace C (图中NS C)都包含在controller2所在的域里。也就是说,controller 2提供对namespace B和namespace C的最优访问,而controller 1不能。

支持非对称命名空间访问的SPDK多路径验证_第1张图片

图1.1 controller 2提供对namespace B的最优访问

为了让controller 1提供对namespace B最优访问,NVM subsystem可以重新配置或可以自动执行内部配置动作来改变,如图1.2所示。controller 2提供对namespace C的最优访问,而controller 1提供对namespace B的最优访问。 

支持非对称命名空间访问的SPDK多路径验证_第2张图片

图1.2 controller 1提供对namespace B的最优访问

2

多路径I / O和命名空间共享 

根据NVMe协议,多路径I/O指的是主机和命名空间之间,两个或多个完全独立的PCIe路径。命名空间共享,是指两个或多个主机使用不同的NVMe控制器,访问公共共享命名空间的能力,如图1.3所示。(注:多路径I/O和命名空间共享都需要NVM系统包含两个或多个控制器。)

支持非对称命名空间访问的SPDK多路径验证_第3张图片

图1.3:具有两个控制器和两个端口的NVM子系统

3

bpftrace脚本

bpftrace.sh 是一个帮助脚本,有助于针对正在运行的SPDK 应用程序,运行 bpftrace 脚本。它使用了数据包筛选器(Berkeley Packet Filter,简称BPF),所以要求SPDK在配置编译选项时使能userspace DTrace探针,./configure --with-usdt。

这是一个典型的用法:

scripts/bpftrace.sh `pid of spdk_tgt` scripts/bpf/nvmf.bt

通过nvmf.bt的筛选,SPDK 应用程序将打印出有关 NVMF子系统和轮询组信息状态转换的信息,如下所示: 

2110.935735: nvmf_tgt reached state NONE

2110.954316: nvmf_tgt reached state CREATE_TARGET

2110.967905: nvmf_tgt reached state CREATE_POLL_GROUPS

2111.235982: nvmf_tgt reached state START_SUBSYSTEMS

2189.921492: nqn.2016-06.io.spdk:cnode1 change state from INACTIVE to ACTIVE start

2189.952508: nqn.2016-06.io.spdk:cnode1 on thread 2 state to ACTIVE start

2189.959125: nqn.2016-06.io.spdk:cnode1 on thread 2 state to ACTIVE done

2190.005832: nqn.2016-06.io.spdk:cnode1 change state from INACTIVE to ACTIVE done

关于SPDK BPF的详细使用方法,请参考之前的微信文章:SPDK的BPF Tracing

ANA multipath的验证

SPDK新增了支持ANA multipath验证的实例。在这个例子中,我们首先在target和host之间建立两条I/O路径(含两个端口),然后改变其中一个端口的ANA state。通过bpfrace脚本产生实时的与NVMF相关的I/O路径直方图,进而验证当前port是否是active port,确定活动的I/O路径。

图2.1显示了I/O路径的基本框架,本例的单网卡双端口环境比较简单,仅作为验证需要。如果用户环境允许的话可以用两个物理网卡测试。需要说明的是,测试脚本是建立在RPC交互命令之上,涉及到subsystem、transport等与NVMF相关的一些名词,关于这些名词的说明请参考附录。接下来看下具体的步骤。

支持非对称命名空间访问的SPDK多路径验证_第4张图片

图2.1:ANA multipath框架示例

1

启动target server

我们在target端运行nvmf_tgt,然后创建nvmf tcp传输、侦听tcp端口的nvmf子系统,以及作为命名空间添加到nvmf子系统的malloc bdev。

build/bin/nvmf_tgt -i 0 -e 0xFFFF -m 0x3 &

rpc.py nvmf_create_transport -t tcp -o -u 8192

rpc.py bdev_malloc_create 64 512 -b Malloc0

rpc.py nvmf_create_subsystem nqn.2016-06.io.spdk:cnode1 -a -s SPDK1 -r -m 2

rpc.py nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 Malloc0

还是在target端,添加两个listener,

rpc.py nvmf_subsystem_add_listener nqn.2016-06.io.spdk:cnode1 -t tcp -a 10.0.0.2 -s 4420

rpc.py nvmf_subsystem_add_listener nqn.2016-06.io.spdk:cnode1 -t tcp -a 10.0.0.2 -s 4421

并设置ANA state,选择4421端口所在的路径为最优路径,并打印。

rpc.py nvmf_subsystem_listener_set_ana_state nqn.2016-06.io.spdk:cnode1 -t tcp -a 10.0.0.2 -s 4420 -n non_optimized

rpc.py nvmf_subsystem_listener_set_ana_state nqn.2016-06.io.spdk:cnode1 -t tcp -a 10.0.0.2 -s 4421 -n optimized

rpc.py nvmf_subsystem_get_listeners nqn.2016-06.io.spdk:cnode1 | jq '.[] | .ana_states[0]'

配置好的ana_states数组显示如下:

{

  "ana_group": 1,

  "ana_state": "optimized"

}

{

  "ana_group": 1,

  "ana_state": "non_optimized"

}

2

启动host端

这时我们在host端启动一个app(比如bdevperf)来接收RPC 命令,

test/bdev/bdevperf/bdevperf -m 0x4 -z -q 128 -o 4096 -w verify -t 60 &

同样在host端创建NVMe控制器,设置multipath行为类型(failover或multipath)。回看图2.1,host端看到的Nvme0就是target端的Malloc0。

rpc.py bdev_nvme_attach_controller -b Nvme0 -t tcp -a 10.0.0.2 -s 4420 -f ipv4 -n nqn.2016-06.io.spdk:cnode1 -l -1 -o 10

rpc.py bdev_nvme_attach_controller -b Nvme0 -t tcp -a 10.0.0.2 -s 4421 -f ipv4 -n nqn.2016-06.io.spdk:cnode1 -x multipath -l -1 -o 10

回到target端,尝试改变4421端口listener的ANA state。

rpc.py nvmf_subsystem_listener_set_ana_state nqn.2016-06.io.spdk:cnode1 -t tcp -a 10.0.0.2-s 4420 -n non_optimized

rpc.py nvmf_subsystem_listener_set_ana_state nqn.2016-06.io.spdk:cnode1 -t tcp -a 10.0.0.2 -s 4421 -n inaccessible

再在host端运行bdevperf脚本,设置时长2分钟。

test/bdev/bdevperf/bdevperf.py -t 120 perform_tests

在target端使用bpftrace.sh脚本产生NVMF路径相关的trace日志文件。

scripts/bpftrace.sh “nvmf_tgt的进程号” scripts/bpf/nvmf_path.bt &> trace.txt

打印trace.txt获得I/O路径直方图。

Attaching 4 probes...

@path[10.0.0.2, 4421]: 20764

@path[10.0.0.2, 4421]: 21053

@path[10.0.0.2, 4421]: 21008

在target端查看当前活动的端口,是不是trace文件里记录的端口,这里应是4421。

rpc.py nvmf_subsystem_get_listeners nqn.2016-06.io.spdk:cnode1 |

jq -r '.[] | select (.ana_states[0].ana_state=="optimized")'

支持非对称命名空间访问的SPDK多路径验证_第5张图片

图2.2:两个端口ANA state的变化过程

当断开port2的listener,我们会看到SPDK会选择仅存的non_optimized 状态的端口作为I/O路径。

rpc.py nvmf_subsystem_remove_listener nqn.2016-06.io.spdk:cnode1 -t tcp -a 10.0.0.2 -s 4421

再重连port2的listener,并恢复端口的optimized 状态,active I/O路径为又会回到port2 。

rpc.py nvmf_subsystem_add_listener nqn.2016-06.io.spdk:cnode1 -t tcp -a 10.0.0.2 -s 4421

rpc.py nvmf_subsystem_listener_set_ana_state nqn.2016-06.io.spdk:cnode1 -t tcp -a 10.0.0.2 -s 4421 -n optimized

到这里,这个实例算是演示结束了。以上命令和步骤会整理到/test/nvmf/host/multipath.sh,并随新版本发布。

3

multipath和failover

为了弄明白multipath和failover的区别,我们来看下SPDK代码里RPC命令bdev_nvme_attach_controller是怎么处理这两个参数的。先看这3个参数,

  • -x    设置多路径行为 (disable关闭, failover故障切换, multipath多路径)

  • -l     删除ctrlr之前,等待ctrlr重新连接的时间,-1表示无限次重试。0表示没有重新连接重试。

  • -o    延迟重新连接重试的时间。

rpc.py bdev_nvme_attach_controller -b Nvme0 -t tcp -a 10.0.0.2 -s 4420 -f ipv4 -n nqn.2016-06.io.spdk:cnode1 -l -1 -o 10 -----①

rpc.py bdev_nvme_attach_controller -b Nvme0 -t tcp -a 10.0.0.2 -s 4421 -f ipv4 -n nqn.2016-06.io.spdk:cnode1 -x multipath -l -1 -o 10 -----②

再回看上面示例里的命令,首先由rpc_bdev_nvme_attach_controller( ) 函数解析multipath参数,然后由bdev_nvme_create( )函数创建这个控制器。

1.  int bdev_nvme_create(…)  

2.  /* 处理multipath和failover参数的回调函数不一样 */  

3.      if (nvme_bdev_ctrlr_get_by_name(base_name) == NULL || multipath)  

4.          attach_cb = connect_attach_cb;  

5.       else

6.          attach_cb = connect_set_failover_cb;

connect_attach_cb( )会调用nvme_bdev_ctrlr_create( ),

1.  static int nvme_bdev_ctrlr_create(…)  

2.      nbdev_ctrlr = nvme_bdev_ctrlr_get_by_name(name); /* 获得Nvme0控制器指针*/

3.      if (nbdev_ctrlr != NULL)  

4.          /* 如果已经存在,检查是否支持multipath,对应上面第②条命令 */  

5.          bdev_nvme_check_multipath(nbdev_ctrlr, ctrlr);  

6.      else  

7.          /* 如果是新的NVMe控制器,则将该控制器入队,对应上面第①命令 */  

8.          TAILQ_INSERT_TAIL(&g_nvme_bdev_ctrlrs, nbdev_ctrlr, tailq);  

9.      /* 把指定的控制器添加到现有控制器队列 */  

10.    nvme_ctrlr->nbdev_ctrlr = nbdev_ctrlr;  

11.    TAILQ_INSERT_TAIL(&nbdev_ctrlr->ctrlrs, nvme_ctrlr, tailq);  

而在failover的情况下,connect_set_failover_cb则会调用bdev_nvme_add_secondary_trid( ),将辅助路径添加到现有nvme_ctrlr路径队列用以进行故障切换,也就是一次入队操作。

1.  static int bdev_nvme_add_secondary_trid(…)  

2.      TAILQ_FOREACH(tmp_trid, &nvme_ctrlr->trids, link)  

3.          if (tmp_trid->is_failed && tmp_trid != nvme_ctrlr->active_path_id)  

4.              TAILQ_INSERT_BEFORE(tmp_trid, new_trid, link);  

在检查它是否可以访问与主路径相同的命名空间后,它将断开连接,直到发生故障转移。值得注意的是,SPDK目前不支持transport类型为PCIe的故障切换,只支持同一transport类型,和同一NQN的故障切换。

4

listener和ANA

关于添加、删除listener和更改ANA状态的RPC命令,都是通过操作码来分配到不同分支来处理的。

1.  static void nvmf_rpc_listen_paused()  

2.      if (ctx->op == NVMF_RPC_LISTEN_ADD)

3.          spdk_nvmf_subsystem_add_listener(ctx->subsystem, &ctx->trid, nvmf_rpc_subsystem_listen, ctx);  

4.      else if (ctx->op == NVMF_RPC_LISTEN_REMOVE)  

5.              spdk_nvmf_subsystem_remove_listener(subsystem, &ctx->trid);  

6.      else if (ctx->op == NVMF_RPC_LISTEN_SET_ANA_STATE)  

7.              nvmf_subsystem_set_ana_state(subsystem, &ctx->trid, ctx->ana_state, ctx->anagrpid, nvmf_rpc_set_ana_state_done, ctx);  

RPC命令nvmf_subsystem_add_listener:首先找到对应的target和subsystem,初始化ana_state,默认为OPTIMIZED,接下来更新发现日志,继续侦听。

1.  void spdk_nvmf_subsystem_add_listener(…)  

2.      for (i = 0; i < subsystem->max_nsid; i++)   

3.          listener->ana_state[i] = SPDK_NVME_ANA_OPTIMIZED_STATE;  

4.      TAILQ_INSERT_HEAD(&listener->subsystem->listeners, listener, link);  

5.      nvmf_update_discovery_log(listener->subsystem->tgt, NULL);  

6.      nvmf_rpc_listen_resumed(…);  

RPC命令nvmf_subsystem_remove_listener:找到相对应的控制器,把它的listener指针置空,然后删除subsystem的这个listener结点,最后在每一个I/O 通道上通知更新poll group(附录表A)。

RPC命令nvmf_subsystem_listener_set_ana_state:通过nvmf_subsystem_set_ana_state( )函数,先找到指定的listener,改完ANA state以后,再在每一个I/O 通道上通知更新poll group。实际上是先断开listener,设置好再重连的过程。

附录

SPDK NVMe-oF名词

解释

target

具有关联命名空间的子系统的集合,以及传输集和关联的网络连接。

subsystem

NVMe-oF 子系统,包含命名空间和控制器,并执行访问控制。

namespace(ns)

NVMe-oF 命名空间,是指块设备层。

transport

网络结构传输方式,有RDMA,TCP等。

poll group

作为一个单元轮询的网络连接集合的抽象。

listener

侦听器,被target接受的新连接网络地址。

NQN

代表主机系统(启动器)。

ANA  state

ANA 状态是每个侦听器和每个子系统的,并存储在子系统listener中。

表A:SPDK NVMe-oF名称解释

参考资料

1.  http://www.spdk.io/doc/usdt.md

2.  NVM Express base specification revision v1.4

3.  NVM Express over Fabrics revision v1.1

支持非对称命名空间访问的SPDK多路径验证_第6张图片

转载须知

DPDK与SPDK开源社区

公众号文章转载声明

推荐阅读

SPDK的BPF Tracing

TADK v22.03 Release

手把手教你让英特尔®E810飞起来

DPDK Release 22.03

支持非对称命名空间访问的SPDK多路径验证_第7张图片

支持非对称命名空间访问的SPDK多路径验证_第8张图片

点点“赞”“在看”,给我充点儿电吧~

你可能感兴趣的:(python,java,linux,数据库,网络)