SPDK Delay Bdev 介绍及应用实例

温馨提示

由于英文函数名较长,为了获得更佳的阅读体验,建议在手机横屏或电脑网页阅读~

一、背景介绍

2019年3月,SPDK社区首次实现了Delay Bdev,如这个Bdev的名称,主要用来模拟IO的Delay,并在SPDK 19. 07版本中正式发布。本次发布添加了新的bdev 模块“延迟(Delay)”,delay_bdev基于base_bdev,用于模拟驱动设备的延迟,此模块更多用于测试和调试不同IO情况来使用。可以使用新的RPC命令‘bdev_delay_create’来创建delay_bdev。

该RPC测参数包括base_bdev的名称,读写操作的平均延迟和读写操作的P99延迟。平均延迟即性能测试工具比如fio提交到驱动程序的所有I/O的平均延迟。P99 延迟指的是所有I/O中最慢的1%的I/O的延迟。对于具有显著延迟的底层驱动程序来说,这个延迟是一个很好的附加。如果试图在nvme驱动程序或aio设备上实现人工延迟,应该考虑使用Delay Bdev。

二、SPDK为什么要使用Delay Bdev模块

Delay_bdev模块是基于底层的base_bdev之上预先定义的延迟。从而在SPDK应用程序的功能性测试及扩展性测试期间来模拟设备的延迟。例如,为了模拟处理I/O时驱动程序的时延的影响,我们可以基于NULL bdev配置一个delay_bdev来达到同样的效果。Delay Bdev模块不是为了提供特定NVMe驱动延迟的高度模拟,相反,其主要目的是从大的方向上了解我们一般来说的延迟对某些特定应用程序的影响。

三、SPDK Delay Bdev模块介绍

首先,我们介绍SPDK最新代码中几个和delay_bdev相关的函数。主要包括创建delay_bdev、删除delay_bdev和更新delay_bdev的延迟的函数。然后介绍通过使用bdev_nvme_set_options函数,设置spdk host端超时时间设置和超时操作等。

1.

Bdev_delay_create函数

使用bdev_delay_create RPC创建delay_bdev。这个rpc函数有6个参数,第一个表示delay_bdev的名称,第二个代表base_bdev的名称。其余四个参数表示以下延迟值:平均读延迟、平均写延迟、P99读延迟和P99写延迟。在delay_bdev的语境中,P99延迟表示在完成I/O并提交到上层协议之前,1%的I/O至少会达到P99延迟的值。这里所有的延迟都以微秒为单位。bdev_delay_create函数的返回值为新创建bdev的名称。示例命令如下:

rpc.py bdev_delay_create -b Null0 -d delay0 -r 10 --nine-nine-read-latency 50 -w 30 --nine-nine-write-latency 90

该命令将创建一个delay_bdev,平均读和写延迟分别为10微秒和30微秒,P99读和P99写延迟分别为50微秒和90微秒。

名字

可选参数

参数类型

描述

Base_bdev_name

Required

String

Bdev name

Avg_read_latency

Required

Number

平均读延迟(us)

P99_read_latency

Required

Number

P99读延迟(us)

Avg_write_latency

Required

Number

平均写延迟(us)

P99_write_latency

Required

Number

P99 写延迟(us)

表1 bdev_delay_create函数参数及说明

2.

Bdev_delay_delete函数

使用bdev_delay_delete RPC函数来删除delay bdev。示例命令如下:

rpc.py bdev_delay_delete delay0

此函数用于删除创建的delay_bdev,函数的返回值是 true 或者 false。

名字

可选参数

参数类型

描述

name

Required

String

Bdev name

表 2 bdev_delay_delete 函数参数和说明

3.

Bdev_delay_update_latency函数

使用bdev_delay_update_latency函数,用户可以更新给定delay_bdev的延迟。可以实现延迟的重新配置。从而可以在测试上做一些有趣的尝试,例如设置较长延迟配置bdev,建立好target和host之间的I/O路径之后,不需要删除delay_bdev而是通过重新配置delay_bev来减少延迟,从而允许I/O正常流通。实例命令如下:

rpc.py bdev_delay_update_latency delay0 avg_read 1000000

此函数更新与给定delay_bdev相关I/O类型的目标延迟值。当前任何未完成的I/O将使用旧的延迟来完成I/O处理。返回值是true或者false。

名字

可选参数

参数类型

描述

Delay_bdev_name

Required

String

delay bdev名称

Latency_type

Required

String

avg_read, avg_write, p99_read,  p99_write其中之一

Latency_us

Required

Number

新的延迟值latency(us)

表 3 bdev_delay_update_latency 函数参数和说明

这里需要特别提到的是,一般情况下,I/O都会被SPDK Bdev和驱动层及时处理。通过配置Delay Bdev,根据配置Delay的时间,超时可能会被触发,情况则会有所不同。本质上这意味着从低延迟变更为高延迟后,会因为延迟更新后新提交的I/O造成阻塞。这也是我们设计这个Delay Bdev结合host initiator端预定义的超时用例所期望得到的结果。

4.

Bdev_nvme_set_options

使用bdev_nvme_set_options为所有NVMe Bdev设置全局参数。只能在SPDK子系统初始化之前调用该RPC命令。此函数的返回值是 true 或 false。该PRC可以配置 NVMe Bdev针对IO超时的处理,比如尝试abort,reset,超时的时长等等。我们通过NVMe Bdev来结合Delay Bdev可以进行 Host 到 Target的端到端IO长延时处理。

名字

可选参数

参数类型

参数描述

Action_on_timeout

Optional

String

命令超时时要采取的操作:none, reset或abort

Timeout_us

Optional

Number

每个I/O命令超时时间(us)如果为 0,则不跟踪超时

Timeout-admin-us

Optional

Number

每个管理命令的超时时间,单位为微秒。如果为 0,则视为io超时

Retry_count

Optional

Number

每次I/O失败前的重试次数

Arbitration_burst

Optional

Number

以二的幂表示该值,值为111b表示没有限制

Low_priority_weight

Optional

Number

控制器可以一次性从低优先级队列启动的最大命令数

Medium_priority_weight

Optional

Number

控制器可以一次性从中优先级队列启动的最大命令数

High_priority_weight

Optional

Number

控制器可以一次性从高优先级队列启动的最大命令数

Nvme_adminq_poll_period_us

Optional

Number

管理队列轮询异步事件的频率,以微秒为单位。

Nvme_ioq_poll_period_us

Optional

Number

I/O队列轮询完成的频率,以微秒为单位。

Default: 0(as fast as possible)

Io_queue_reqeusts

Optional

Number

为每个NVMe I/O队列分配的请求数。Default: 512

Delay_cmd_submit

Optional

Boolean

启用延迟NVMe命令提交以允许批量处理多个命令。Default: ‘true’.

表 4 bdev_nvme_set_options 函数参数和说明

四、使用SPDK和Kernel测试Delay Bdev的两个实例

1.

SPDK Target和Initiator的拓扑图如下所示

Physical Machine 1即SPDK Target是标准的baremetal的服务器,上面运行spdk_tgt应用程序,并且配置相应的base bdev和delay bdev。Physical Machine2即Initiator,这里主要包括两类initiator,一个是kernel initiator,另一个是spdk initiator。两台物理机使用E810网卡由100G的网线背靠背连接。同时这两台机器通过LAN Ethernet Switch连接到同一个局域网。

SPDK Delay Bdev 介绍及应用实例_第1张图片

图1 SPDK NVMF Target和Initiator拓扑结构图

2.

SPDK NVMF Target + Delay_bdev -> SPDK Initiator实例

此测试旨在模拟非正常的网络情况下,即大量I/O在到达SPDK Initiator之前被丢弃的情况。本质上,我们试图在SPDK Initiator端触发Initiator超时,并设置对应超时产生不同的行为,比如abort,reset以及none等。

这个测试与我们所做的其他测试不同,因为它无需杀掉target或initiator端应用。相反,该测试希望target端应用能够持续运行,并适当回应SPDK initiator端应用产生的各种错误条件。我们希望在initiator上运行的bdevperf仍然可以成功完成,并且target端应用不会由于未正确处理断开连接和重新连接而崩溃。SPDK Target和SPDK Initiator的脚本示例如下:

SPDK Target端

在SPDK Target服务器端启动nvmf_tgt应用程序,并启用对 “nvmf”,“bdev”,“bdev_malloc”和“vbdev_delay”的trace跟踪

./build/bin/nvmf_tgt -i 0 -e 0xFFFF -m 0xF -L "nvmf" -L "bdev" -L"bdev_malloc" -L "vbdev_delay"

运行时使用spdk_trace命令捕获事件快照

./build/bin/spdk_trace -s nvmf -i 0 

创建一个base_bdev

./scripts/rpc.py bdev_malloc_create 64 512 -b Malloc0

创建delay_bdev,我们不能在刚开始的时候将delay_bdev配置为过高延迟,否则连接操作将无法正常工作

./scripts/rpc.py bdev_delay_create -b Malloc0   -d Delay0 -r 30 -t 30 -w 30 -n 30

创建传输、子系统、添加命名空间并添加监听器

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

./scripts/rpc.py nvmf_create_subsystem nqn.2016-06.io.spdk:cnode1 -a -s SPDK00000000000001

./scripts/rpc.py nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 Delay0

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

SPDK Initiator端(使用bdev_nvme_set_options rpc命令定义超时以及超时的操作等)

导出PYTHONPATH的环境路径

export PYTHONPATH=$PYTHONPATH:/path_to_spdk/scripts/

运行bdevperf 应用程序,并启用对“bdev_nvme”,“bdev”,“bdev_malloc”和“vbdev_delay”的trace跟踪

./test/bdev/bdevperf/bdevperf -z -r /var/tmp/spdk.sock -q 1 -o 4096 -w read -t 60 -f -e 0x8 --wait-for-rpc -Lbdev_nvme -L bdev -L bdev_malloc -L vbdev_delay

设置SPDK Initiator超时时间以以及超时操作等。

./scripts/rpc.py bdev_nvme_set_options --timeout-us=50000 --timeout-admin-us=0--action-on-timeout=abort -r 1

执行bdev_nvme_set_option命令后启动框架初始化

./scripts/rpc.py framework_start_init

连接控制器

./scripts/rpc.py -s/var/tmp/spdk.sock bdev_nvme_attach_controller -b NVMe0 -t tcp -a 192.168.3.8-s 4420 -f ipv4 -n nqn.2016-06.io.spdk:cnode1

执行测试

./test/bdev/bdevperf/bdevperf.py -s /var/tmp/spdk.sock perform_tests

SPDK Target端

当I/O经过SPDK Target和SPDK Initiator时,将avg_read/avg_write/p99_read和p99_write的delay_bdev延迟时间更新为1秒,睡眠3秒之后,恢复delay_bdev延迟时间为之前设定的30微秒

./scripts/rpc.py bdev_delay_update_latency Delay0 avg_read 1000000

./scripts/rpc.py bdev_delay_update_latency Delay0 avg_write 1000000

./scripts/rpc.py bdev_delay_update_latency Delay0 p99_read 1000000

./scripts/rpc.py bdev_delay_update_latency Delay0 p99_write 1000000

sleep 3

./scripts/rpc.py bdev_delay_update_latency Delay0 avg_read 30

./scripts/rpc.py bdev_delay_update_latency Delay0 avg_write 30

./scripts/rpc.py bdev_delay_update_latency Delay0 p99_read 30

./scripts/rpc.py bdev_delay_update_latency Delay0 p99_write 30

在所有脚本完成运行后,尝试获取日志消息,并在SPDK Target端log中跟踪以下关键字,以检查超时操作是否按预期工作


Key  message

SPDK

 Target

nvme_qpair.c: 252:nvme_admin_qpair_print_command:  *NOTICE*: ABORT (08) qid:0 cid:4 nsid:0 cdw10:00000001 cdw11:00000000

nvme_qpair.c: 457:spdk_nvme_print_completion:  *NOTICE*: ABORTED - BY REQUEST (00/07) qid:1 cid:0 cdw0:0 sqhd:0000  p:0 m:0 dnr:0

这里需要描述一下,SPDK Initiator端超时的不同操作以及查找相应的关键字的方法,如下表所示

超时操作

日志消息描述

描述

Abort

[2022-04-18 09:43:16.566576] nvme_qpair.c:  252:nvme_admin_qpair_print_command: *NOTICE*: ABORT (08) qid:0 cid:4 nsid:0  cdw10:00000001 cdw11:00000000

[2022-04-18 09:43:16.566610] nvme_qpair.c:  457:spdk_nvme_print_completion: *NOTICE*: ABORTED - BY REQUEST (00/07) qid:1  cid:0 cdw0:0 sqhd:0000 p:0 m:0 dnr:0

向nvmf target发送“Abort”命令

Reset

2022-04-16 13:38:17.417393]  bdev_nvme.c:2866:timeout_cb: *WARNING*: Warning: Detected a timeout.  ctrlr=0x29a4460 qpair=0x29f1c70 cid=0

[2022-04-16 13:38:17.417802]  bdev_nvme.c:1150:bdev_nvme_disconnected_qpair_cb: *NOTICE*: qpair 0x29f1c70  is disconnected, free the qpair and reset controller.

[2022-04-16 13:38:17.417868]  nvme_ctrlr.c:1650:nvme_ctrlr_disconnect: *NOTICE*:  [nqn.2016-06.io.spdk:cnode1] resetting controller

[2022-04-16 13:38:17.428914]  bdev_nvme.c:5371:bdev_nvme_readv: *DEBUG*: read 8 blocks with offset 0xb638

[2022-04-16 13:38:17.428932]  bdev_nvme.c:1543:_bdev_nvme_reset_complete: *NOTICE*: Resetting controller  successful.

向nvmf target发送“Reset”命令

None

[2022-04-16 13:45:52.041266]  bdev_nvme.c:2866:timeout_cb: *WARNING*: Warning: Detected a timeout.  ctrlr=0x2212460 qpair=0x225fc70 cid=0

[2022-04-16 13:45:52.041684]  bdev_nvme.c:2908:timeout_cb: *DEBUG*: No action for nvme controller timeout.

向nvmf target发送“None”命令

表5 SPDK Initiator端timeout的不同操作及对应log信息

3.

SPDK Target + Delay_bdev -> 

Kernel Initiator实例

此测试旨在模拟非正常的网络情况下,即大量I/O在到达Initiator之前被丢弃的情况。本质上,我们试图在内核态触发Initiator超时,内核态会断开与目标的连接,并尝试重新建立连接。

内核启动器的默认超时时间为30秒,在等待30秒后,内核将触发Initiator重新连接操作。当delay_bdev设置为31秒(对应启动器重新连接1次)时,测试和验证内核重新连接和超时的示例脚本。SPDK Target端和Initiator端的脚本示例如下:

SPDK Target端

在服务器端启动nvmf_tgt应用程序

./build/bin/nvmf_tgt -i 0 -e 0xFFFF -m 0xF

运行时使用spdk_trace命令捕获事件快照

./build/bin/spdk_trace -s nvmf -i 0 

创建一个base_bdev

./scripts/rpc.py bdev_malloc_create64 512 -b Malloc0

创建delay_bdev,我们在刚开始的时候不能将delay_bdev配置为过高延迟,否则连接无法正常工作

./scripts/rpc.py bdev_delay_create -b Malloc0 -d Delay0 -r 30 -t 30 -w 30 -n 30

创建传输、子系统、添加命名空间并添加监听器

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

./scripts/rpc.py nvmf_create_subsystem nqn.2016-06.io.spdk:cnode1 -a -s SPDK00000000000001

./scripts/rpc.py nvmf_subsystem_add_ns nqn.2016-06.io.spdk:cnode1 Delay0

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

内核Initiator端(Linux 内核的默认超时时间为 30 秒):

加载对应的模块

modprobe nvmet

modprobe nvmet-tcp

modprobe nvme-fabrics

连接后端设备

nvmeconnect -t tcp -n nqn.2016-06.io.spdk:cnode1 -a 192.168.3.8 -s 4420

使用fio发送 I/O,这里需要注意,超时I/O完成后,仍有10 秒的I/O

./scripts/fio-wrapper-p nvmf -i 4096 -d 1 -t write -r 60 -v

SPDK Target端

内核启动器的默认超时时间为30秒,延迟31秒以触发启动器重新连接

./scripts/rpc.py bdev_delay_update_latency Delay0 avg_read 31000000

./scripts/rpc.py bdev_delay_update_latency Delay0 avg_write 31000000

./scripts/rpc.py bdev_delay_update_latency Delay0 p99_read 31000000

./scripts/rpc.py bdev_delay_update_latency Delay0 p99_write 31000000

睡眠3秒,发送I/O

sleep 3

将这些值重置为后续I/O能够及时完成的值

./scripts/rpc.py bdev_delay_update_latency Delay0 avg_read 30

./scripts/rpc.py bdev_delay_update_latency Delay0 avg_write 30

./scripts/rpc.py bdev_delay_update_latency Delay0 p99_read 30

./scripts/rpc.py bdev_delay_update_latency Delay0 p99_write 30

在SPDK Target端,我们需要跟踪dmesg以检查重新连接是否成功。关键消息如下

# dmesg

[64770.513822] nvme nvme0: creating 40 I/O queues.

[64770.913091] nvme nvme0: mapped 40/0/0 default/read/poll queues.

[64770.928574] nvme nvme0: new ctrl: NQN "nqn.2016-06.io.spdk:cnode1", addr 192.168.3.8:4420

[64770.930075] nvme0n1: detected capacity change from 0 to 67108864

[64872.365889] nvme nvme0: queue 26: timeout request 0x34 type 4

[64872.365895] nvme nvme0: starting error recovery

[64872.372236] block nvme0n1: no usable path - requeuing I/O

[64872.384925] nvme nvme0: Reconnecting in 10 seconds...

[64882.617640] nvme nvme0: creating 40 I/O queues.

[64883.031142] nvme nvme0: Successfully reconnected (1 attempt)

五、参考文献

1. https://spdk.io/

2. https://review.gerrithub.io/c/spdk/spdk/+/462584

3. https://review.gerrithub.io/c/spdk/spdk/+/453594

4. https://review.gerrithub.io/c/spdk/spdk/+/464454

5. https://review.gerrithub.io/c/spdk/spdk/+/464459

6. https://www.youtube.com/watch?v=vRrAD1U0IRw

SPDK Delay Bdev 介绍及应用实例_第2张图片

转载须知

DPDK与SPDK开源社区

公众号文章转载声明

推荐阅读

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

TADK v22.03 Release

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

DPDK Release 22.03

SPDK Delay Bdev 介绍及应用实例_第3张图片

SPDK Delay Bdev 介绍及应用实例_第4张图片

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

你可能感兴趣的:(python,linux,java,人工智能,网络)