PyNVMe3脚本开发指南

您所在的位置:网站首页 nvme驱动导入 PyNVMe3脚本开发指南

PyNVMe3脚本开发指南

2024-07-11 01:21| 来源: 网络整理| 查看: 265

PyNVMe3脚本开发指南

无论在PC电脑还是数据中心的服务器上,存储都是和计算、网络同等重要的一个核心部件。计算通常指CPU,网络通常指网卡,而存储当下最热门的就是NVMe SSD了。国内外越来越多的厂商在进入这个市场,但是不同厂商的产品,甚至同一个厂商的不同产品,他们的质量都是良莠不齐的。

SSD需要有完整的功能,优异的性能,以及高度的数据可靠性。个人使用的消费级SSD还需要有良好的兼容性,以及低功耗表现。不论是刚起步3-5年的初创公司,还是已经在这个行业里面有10年多的老公司,要能做一款优秀的达到上述各种要求的SSD盘并不容易。特别从存储的数据可靠性来看,SSD的质量尤其重要。毕竟,网络断了可以重连,包丢了可以重发;CPU坏了可以换,AMD不行换Intel,Intel不行换AMD。但是SSD市场上那么多厂商那么多产品,如果用了不好的SSD盘导致数据丢失,那换什么都来不及了。

目前很多厂商在测试NVMe SSD的时候大量依赖传统的测试工具,譬如fio/nvme-cli/dnvme和其他一些商业工具。这些工具可以做一些功能性测试,但它们的性能无法满足越来越快的NVMe/PCIe的规格。性能跟不上,测试压力就不够,有些缺陷就压不出来。另外,这些工具对测试脚本的二次开发并不友好,无法满足不同场景下成百上千的测试用例开发的需要。我们有近十年的SSD固件开发经验,在经历过种种来自测试工具的痛苦和教训之后,我们决定自己做一个灵活的NVMe SSD测试工具。我们的目的是帮助厂商测好SSD,帮助客户选好SSD。

这就是PyNVMe3。经历了5年多的研发和推广,PyNVMe3作为一个第三方独立的测试工具,已经被国内外许多SSD厂商和客户采用。各个厂商对使用PyNVMe3自行开发测试脚本的兴趣也与日俱增,所以我们整理了这份PyNVMe3脚本开发指南,让大家能更快开始PyNVMe3的测试脚本开发。

测试平台

PyNVMe3是一个纯软件的测试工具,可以工作在各种电脑或者服务器上面,用户不需要花大价钱购买专用的测试硬件平台,方便厂商低成本大规模地部署测试。在安装PyNVMe3之前,请检查平台是否满足以下要求:

 CPU: x86_64平台。AMD平台需要添加内核启动参数,请参考下文的安装和配置部分。  OS: Ubuntu 20.04.x,推荐将OS安装到SATA盘上。目前只支持Ubuntu 20.04版本。  需要sudo/root权限。  RAID mode (Intel® RST)需要在BIOS中关闭。  Secure boot需要在BIOS中关闭。

服务器需要额外检查以下BIOS配置:

 IOMMU: (a.k.a. VT for Direct I/O)需要在BIOS中关闭。  VMD: 需要在BIOS中关闭。  NUMA: 需要在BIOS中关闭。 准备工作

首先要安装Ubuntu 20.04,建议使用SATA SSD作为OS系统盘。

普通用户使用sudo获取root权限时经常需要输入密码,稍显麻烦。我们建议先做如下免密配置:

在Ubuntu 20.04命令行环境下执行下面的命令,系统会自动打开默认的编辑器nano。 > sudo visudo 在最后一行输入: your_username ALL=(ALL) NOPASSWD: ALL Ctrl-o,回车,写入配置文件。然后Ctrl-x退出编辑器。后面再用到sudo就不需要输入密码了。 安装和配置

PyNVMe3需要在终端中通过命令行来安装,大部分环节我们做了自动化处理,所以安装并不复杂。具体过程如下:

PyNVMe3会用到很多python库,需要先安装pip3。 > sudo apt install -y python3-pip 国内用户需要更改pip3的下载源。创建或修改~/.pip/pip.conf配置文件,添加一下内容并保存。 [global] index-url=https://pypi.tuna.tsinghua.edu.cn/simple/ [install] trusted-host=pypi.tuna.tsinghua.edu.cn 如果之前安装过PyNVMe3,请先卸载PyNVMe3。 > sudo pip3 uninstall PyNVMe3 > sudo rm -rf /usr/local/PyNVMe3 用pip3安装PyNVMe3,PyNVMe3会安装在/usr/local/PyNVMe3目录下。如果没有安装文件,请联系[email protected]。 > sudo pip3 install PyNVMe3-22.11.tar.gz 用root权限打开/etc/default/grub文件,并修改GRUB_CMDLINE_LINUX_DEFAULT这一行如下: GRUB_CMDLINE_LINUX_DEFAULT="quiet splash default_hugepagesz=2M hugepagesz=1G hugepages=1 amd_iommu=off" 在命令行中执行: > sudo update-grub 在 /etc/fstab文件最后一行添加: none /mnt/huge hugetlbfs pagesize=1G,size=1G 0 0 至此我们完成了PyNVMe3的安装和配置工作,重启一下测试机器就可以开始使用PyNVMe3了。 > sudo reboot 执行测试

PyNVMe3有几种不同的执行方式:

在VSCode下执行,主要用来调试新的脚本。 在命令行环境下执行。 在Jenkins等CI环境下执行。

我们这里通过命令行环境来介绍PyNVMe3的测试执行。

进入PyNVMe3目录: > cd /usr/local/PyNVMe3/ 切换到root用户: > sudo su 配置运行时环境。这一步会把NVMe设备的内核驱动更换成PyNVMe3的用户态驱动,并预留大页内存给测试使用。 > make setup

当我们测试消耗的大页内存比较多时,需要预留更多大页内存。用户可以用memsize参数来预留更多大页内存:

> make setup memsize=10000

默认情况下,PyNVMe3会尝试预留10G大页内存,这个能满足4TB容量盘(LBA大小为512字节)的测试需要。建议测试机配置16G或以上的内存。关于大页内存可以参考后文Buffer部分。

执行测试使用如下命令: > make test

这条命令默认执行scripts/conformance目录下的所有测试项目。conformance目录下包含了完整的NVMe协议规范测试,整体测试时间大概需要2-3小时。在scripts/benchmark目录下有其他更多测试,这些benchmark测试需要指定具体的文件名字,并且需要完整执行整个文件的测试,而不是其中的某个函数。通常执行的时间比较长,几个小时、几天、几周不等。譬如执行我们的性能测试脚本:

> make test TESTS=scripts/benchmark/performance.py

如果测试平台上面有多个NVMe待测设备,需要通过pciaddr参数指定被测设备的BDF地址。

> make test pciaddr=0000:03:00.0

如果平台上只有唯一的NVMe盘,就不需要指定这个参数,PyNVMe3会自动找到这个盘的BDF地址。这也是我们推荐使用SATA盘安装OS系统的原因。

对于多Namespace的NVMe盘,可以通过nsid参数来指定测试的Namespace,默认情况是1。

> make test nsid=2

make test命令行可以组合使用TESTS/pciaddr/nsid这些参数。

收集测试结果。测试开始后会在终端实时打印测试日志,也可以在results目录下找到测试日志文件。测试日志文件会比终端打印的测试日志包含更多调试信息。每一个测试项目可能有如下结果: SKIPPED: 测试被跳过,由于某些条件不成立,测试不需要执行。 FAILED: 测试失败。日志文件里面可以看到测试失败的具体原因,通常是某个assert断言不满足。当一个assert失败以后,这个测试项目会立刻退出,不会继续执行后续的测试步骤。 PASSED: 测试通过。 ERROR:测试无法执行。可能被测NVMe盘已经丢失。如果遇到ERROR,建议检查上一个FAILED测试项目的日志,可能是这里的FAILED导致丢盘以及后续的ERROR。

不论测试结果如何,都有可能在测试过程中产生warning。测试日志中包含了所有warning的列表。大部分warning可能和AER返回或者NVMe命令的error code相关,是正常现象。但我们还是建议仔细检查所有warning信息。

results目录下不仅包含测试的日志文件,还有一个excel格式的测试报告,以及测试脚本生成的其他文件(譬如测试记录的原始数据和图表等)。

make test在测试正常结束后会自动切换回NVMe设备的内核驱动。用户也可以在命令行中手动切换内核驱动,以执行其他基于内核驱动的工具(譬如fio/nvme-cli等)。 > make reset

作为和make setup相反的操作,make reset切换回NVMe设备的内核驱动。但需要注意的是,make reset并不会释放make setup申请到的大页内存。因为这些内存一旦释放,由于内存碎片化,后续的make setup很可能无法再次申请到同样大小的大页内存。因此,我们也建议用户在OS启动后的第一时间就执行make setup,确保可以预留所需的大页内存。

pytest

PyNVMe3是一个完整的NVMe SSD测试平台,但我们的主要工作集中在设备驱动部分,并将这个驱动封装成Python库,提供Python API给用户脚本使用。这样,我们的工具可以完全融入Python的生态,整合Python生态中的各种第三方工具。pytest正是其中一个非常重要的第三方工具。

pytest是一个通用的测试框架,可以帮助测试开发人员编写从简单到复杂的各种测试脚本。下面我们介绍pytest的一些基本用法和特性,更多详情请参考pytest官方文档。

编写测试

pytest会自动收集测试脚本中以test_开头的函数,作为测试函数。然后pytest会逐个调用这些测试函数。下面是一个完整的测试文件。

import pytest def test_format(nvme0n1): nvme0n1.format()

是的,只有3行,这就是一个完整的pytest测试文件。pytest的测试脚本是非常简洁的。 测试文件只需要导入pytest模块,然后就可以直接写测试函数。

当我们不想执行某个测试函数时,建议在函数名开头加下划线_,pytest就不会收集这个测试函数,譬如:

import pytest def _test_format(nvme0n1): nvme0n1.format() 命令行

pytest支持多种从命令行执行测试的方法。如前文所述,PyNVMe3使用make test命令行执行测试,但也可以直接使用pytest命令行来执行测试。例如以下几种命令行,分别用来执行一个目录下面的所有测试,一个文件里面的所有测试,以及一个指定的测试函数。

> sudo python3 -B -m pytest scripts/conformance > sudo python3 -B -m pytest scripts/conformance/01_admin/abort_test.py > sudo python3 -B -m pytest scripts/conformance/01_admin/abort_test.py::test_dut_firmware_and_model_name assert断言

pytest允许使用Python的断言语句assert来检查测试中的数据,例如:

def inc(x): return x + 2 def test_inc(): assert inc(1) == 2, "inc wrong: %d" % inc(1)

由于inc函数的错误实现,上面的assert断言会失败。pytest会报告测试FAIL,并打印失败的具体信息,包括assert语句逗号后面的字符串。我们可以根据日志里面的这些信息来调试脚本和固件。好的断言可以提供FAIL现场的足够信息,以提高开发和调试的效率。

fixture

pytest有一个非常重要的特性叫做fixture。fixture可以说是pytest的精髓,我们非常粗浅地通过一些例子来介绍fixture的基本用法,更详细的内容可以参考官方文档。

下面脚本包含两个fixture:nvme0和nvme0n1,以及一个测试函数使用了这两个fixture。

@pytest.fixture() def nvme0(pcie): return Controller(pcie) @pytest.fixture(scope="function") def nvme0n1(nvme0): ret = Namespace(nvme0, 1) yield ret ret.close() def test_dut_firmware_and_model_name(nvme0: Controller, nvme0n1: Namespace): # print Model Number logging.info(nvme0.id_data(63, 24, str)) # format namespace nvme0n1.format()

fixture是一个特别的函数,用来初始化测试所需的对象。定义fixture只需要在函数上加装饰器@pytest.fixture(),fixture命名不要用test_开头,跟测试函数区分开。要使用一个fixture时,直接在测试函数的参数列表里面添加这个fixture的名字就可以了。在执行测试的时候,pytest会调用对应fixture的实现函数,并将返回对象传递给这个fixture的名字。这样测试函数就可以直接使用这个测试对象了。

fixture可以将测试对象的创建和释放过程封装起来给测试函数使用。PyNVMe3中定义了很多这样的NVMe盘的测试对象,譬如:pcie, nvme0, nvme0n1, cq, sq等等。有了fixture,我们就不需要在每个测试函数中去反复写代码创建或释放测试对象了。测试函数的参数列表里面可以添加任意多个fixture,而且无需特别注意他们的先后关系。

fixture初始化的测试对象可以通过return语句返回,也可以用yield返回。yield之后的代码会在测试完成后由pytest再次调用,一般用来释放这个测试对象。

fixture可以相互依赖,譬如上面脚本中的nvme0n1依赖于nvme0。测试脚本只需要在fixture的参数列表里面引用其他被依赖的fixture,pytest会自动调整fixture调用的先后顺序。fixture可以按照正确的顺序帮脚本初始化并释放所有测试对象,当测试涉及到很多对象的时候这个特性尤其重要。

fixture包含一个叫做scope的可选参数,用于控制fixture被调用的时机。譬如上面的nvme0,其scope是function,所以在每个测试函数被执行之前pytest会调用nvme0,这样每个测试函数都会重新初始化NVMe盘,可以隔离不同测试的错误。

fixture的名字可以被重载,在同一个测试文件内定义的fixture具有最高优先级。fixture也可以放在conftest.py文件中。conftest.py是pytest定义的专门用来集中存放fixture的文件。不同的目录可以有不同的conftest.py文件,pytest在执行测试的时候,会按照就近原则从测试文件或者某个conftest.py里面找到fixture的定义。

PyNVMe3提供了联机帮助文档,但是IDE并不知道fixture的类型,所以需要使用Python3的新特性:type hint。如下面这个脚本,通过type hint告诉IDE nvme0是一个Controller对象,这样IDE会自动跳出nvme0及其方法的帮助文档。

def test_dut_firmware_and_model_name(nvme0: Controller, nvme0n1: Namespace): # print Model Number logging.info(nvme0.id_data(63, 24, str)) # format namespace nvme0n1.format()

总而言之,pytest的fixture是一种简洁又极具扩展能力的测试工具,PyNVMe3定义并使用了大量fixture。

参数化

测试用例经常会做参数化的设计,譬如在不同的LBA起始地址写入不同LBA长度的数据。pytest也提供了一种便捷的实现方式。譬如下面这个测试脚本,pytest会用所有不同的(lba_start, lba_count)组合来执行这个测试用例,共计4×4=16个测试。

@pytest.mark.parametrize("lba_start", [0, 1, 8, 32]) @pytest.mark.parametrize("lba_count", [1, 8, 9, 32]) def test_write_lba(nvme0, nvme0n1, qpair, lba_start, lba_count): buf = Buffer(512*lba_count) nvme0n1.write(qpair, buf, lba_start, lba_count).waitdone()

pytest大概介绍到这里,这并不是pytest的全部,还有更多便利的设计等待我们去发掘和利用。

Visual Studio Code (VSCode)

工欲善其事,必先利其器。要写好脚本,就要先找一个趁手的IDE。我们推荐VSCode。VSCode是微软官方的一个开源项目,是一个轻量级但有丰富扩展的代码编辑器,可以在Windows,MacOS和Linux等系统上使用。并且,微软官方也提供了支持Python的插件,长期持续改进对Python以及pytest的支持。PyNVMe3也提供了一个VSCode下的插件,用于显示队列、命令日志以及实时性能等测试信息。VSCode主要用于开发和调试测试脚本,正式的执行测试我们依然建议使用命令行环境或者CI环境。

在通常的测试环境中,我们会有不同的测试机放在实验室中,但我们并不希望长期在实验室中调试代码。VSCode支持远程的工作方式,只需要在自己的工作机上安装VSCode,通过ssh远程链接到测试机来调试测试脚本,使用体验和本地调试一模一样。

我们以远程的工作方式来介绍VSCode的配置和使用步骤。

从官网下载VScode软件安装包,在工作机上安装VSCode。工作机可以是Windows/MacOS/Linux等系统。 安装Remote-SSH插件。 Remote 如果要使用测试机的root账号远程登录,需要更改测试机的ssh配置。首先打开配置文件: > sudo vim /etc/ssh/sshd_config

找到并用#注释掉这行:PermitRootLogin prohibit-password。然后新建一行添加:

PermitRootLogin yes

最后重启SSH:

> sudo service ssh restart #重启ssh服务 > sudo passwd root #如果root没有密码,需要设置密码 配置Remote-SSH连接到测试机器。从左到右依次点击下图红框中图标,并输入ssh命令行,可以用参数-p指定自定义的ssh端口。然后按回车。 Remote 在VSCode远程窗口中安装PyNVMe3插件。点击install from VSIX,查找到PyNVMe3/.vscode目录下的插件,并安装。 pynvme 如下图所示,PyNVMe3的VSCode插件可以显示当前的队列和命令日志。点击速度码表的图标还可以显示当前性能。 pynvme 在VSCode远程窗口中打开PyNVMe3文件夹。 Pytest 打开终端并执行make setup命令。 Pytest 配置NVMe设备的BDF地址。可以参考make setup命令打印出的NVMe设备信息。 Pytest pytest收集测试用例。点击左侧小药瓶图标即可展开测试相关的面板,这里会显示pytest收集到的所有测试用例。目前VSCode需要测试文件名也包含test,pytest命令行没有这个限制。点击脚本文件行号左侧的三角按钮即可运行测试。对于参数化的测试用例,需要右击三角按钮选择其中一项进行测试。 Pytest VSCode执行测试时,在终端界面中也可以看到测试日志,但不会在results目录下保存日志文件。 run_test VSCode执行测试,默认使用Debug方式。可以在VSCode中添加断点,利用VSCode提供的调试面板来调试脚本。断点触发后,PyNVMe3的扩展依然在工作,可以查看这个工作场景下的队列和命令日志,调试脚本更加得心应手。这里需要注意PyNVMe3驱动有timeout时间的限制,如果断点触发的时候有outstanding命令,后续执行过程可能会看到timeout被触发。 请避免在有outstanding命令的地方设置断点。 run_test 基础脚本

PyNVMe3的软硬件平台准备就绪后,我们就可以开始写NVMe SSD的测试脚本了。

每一个测试脚本文件都需要先导入一些模块,包括pytest,logging,当然还有PyNVMe3的驱动模块(nvme.so)。下面是一个典型的完整的测试脚本文件。

import pytest import logging from nvme import * def test_dut_firmware_and_model_name(nvme0: Controller): logging.info("model name: %s" % nvme0.id_data(63, 24, str)) logging.info("current firmware version: %s" % nvme0.id_data(71, 64, str)) logging.info("PyNVMe3 conformance test: " + __version__)

我们直接导入PyNVMe3驱动模块提供的所有类和变量,常用的类包括:Controller, Namespace, Qpair等。__version__可以获取导入的PyNVMe3驱动模块的版本。

PyNVMe3可以对NVMe测试盘做各种操作和测试,但我们先通过读写操作来快速浏览几个PyNVMe3的基础脚本。PyNVMe3支持3种不同的方式来发送IO,以适应不同的测试需求。

ns.cmd

ns.cmd是一种简单直接的发送IO的方式,通过这种方式我们可以发送read、wirte、trim、wirte uncorrectable等等命令。PyNVMe3提供了几乎所有IO命令的接口。

ns.cmd通过io qpair发送命令,所以我们需要在测试函数的参数列表中引用qpair。这个fixture会在测试开始的时候创建一个io qpair,并在测试结束的时候删除这个io qpair。

io操作发生在namespace上面。PyNVMe3定义了默认的nsid为1的namespace fixture,按照内核驱动的命名习惯称为nvme0n1。这个fixture在测试开始的时候会创建namespace在驱动中的对象,并在测试结束的时候释放。nvme0n1这个fixture依赖于nvme0,一个创建Controller对象的fixture。虽然测试函数没有引用这个fixture,但pytest会自动根据依赖关系,调用这个fixture。

下面是一个发送读写命令的测试函数的示例,可以看到fixture使其实现非常简洁。

def test_write_read(nvme0n1, qpair): read_buf = Buffer(4096) write_buf = Buffer(4096) nvme0n1.write(qpair, write_buf, 0).waitdone() nvme0n1.read(qpair, read_buf, 0).waitdone()

脚本通过ns.cmd发出命令以后不会等待命令返回,而是直接继续往下执行。如果我们需要等待命令完成,可以调用waitdone()。PyNVMe3会在waitdone()中回收指定个数的CQE,默认为1。

NVMe是一种完全异步的IO协议,系统驱动经常使用回调机制来处理这种IO完成之后的操作。PyNVMe3也提供了回调的工作方式,可以让测试脚本定义每一条IO命令结束以后的处理代码。回调函数由PyNVMe3的驱动在waitdone里面调用。

下面的测试函数和上面的例子具有一样的行为,但读命令是在write命令的回调中发出。由于回调函数是在waitdone里面调用,所以PyNVMe3规定在回调函数里面不可以再调用waitdone。

def test_io_callback(nvme0, nvme0n1, qpair): read_buf = Buffer(4096) write_buf = Buffer(4096) def write_cb(cpl): nvme0n1.read(qpair, read_buf, 0) nvme0n1.write(qpair, write_buf, 0, cb=write_cb) qpair.waitdone(2)

我们通常在回调函数中把盘返回的CQE传递出来,供后续脚本使用。

def test_io_callback(nvme0n1, qpair): write_buf = Buffer(4096) # issue write and read command cdw0 = 0 def write_cb(cqe): # command callback function nonlocal cdw0 cdw0 = cqe[0] nvme0n1.write(qpair, write_buf, 0, 1, cb=write_cb).waitdone() ioworker

我们可以用脚本通过ns.cmd发送大量IO,但不论开发还是执行的效率都很低。PyNVMe3提供了一个IO发生器:ioworker。ioworker在子进程中按照脚本指定的workload来自主地发送并回收IO。ioworker可以制造出高性能大压力的准确的IO操作,在主流测试平台中单核CPU可以实现120万以上的IOPS性能。脚本可以通过Namespace.ioworker()这个API来创建ioworker对象,通过ioworker对象的start方法来启动ioworker。这时ioworker运行在子进程中,并且脚本的主进程可以执行其他操作。脚本通过ioworker对象的close方法来等待ioworker子进程结束。下面是用ioworker做4K对齐随机写2秒的例子。

def test_ioworker(nvme0, nvme0n1, qpair): r = nvme0n1.ioworker(io_size=8, # 4K io size lba_align=8, # 4K aligned lba_random=True, # random read_percentage=0, # write time=2).start().close()

PyNVMe3为ioworker实现了with语句,使得脚本更加易读。譬如下面这个脚本,开启一个ioworker,当这个ioworker在运行的时候,主进程每隔一秒打印一次当前的性能数据。当然,主进程这时可以做任何事情,包括发送admin和IO命令,甚至各种reset和电源开关操作。

with nvme0n1.ioworker(io_size=8, time=10) as w: while w.running: time.sleep(1) speed, iops = nvme0.iostat() logging.info("iostat: %dB/s, %dIOPS" % (speed, iops))

ioworker为SSD测试提供了非常多的功能,可以直接在Python脚本中制造各种IO负载,并回收各种统计数据。后面我们会继续深入介绍ioworker。

metamode

ns.cmd和ioworker都工作在PyNVMe3的NVMe驱动之上,使用方便,但会受到NVMe驱动的一些限制。PyNVMe3还提供了第三种IO操作,叫做metamode。这种操作可以跳过PyNVMe3的NVMe驱动,直接把NVMe SSD作为一个PCIe设备进行测试,能对NVMe协议达到全方位无死角的测试覆盖度。metamode需要测试脚本来定义IOSQ/IOCQ/SQE/CQE/PRP/SGL等数据结构,甚至脚本也要操作Doorbell。这些要求增加了脚本实现的复杂度,但也极大地提高了测试脚本的能力和覆盖程度。

下面这段脚本利用metamode同时发出了两条具有相同cid的命令。这类错误注入对常规的软件是很难实现的。

def test_metamode_write(nvme0): cq = IOCQ(nvme0, 1, 10, PRP(10*16)) sq = IOSQ(nvme0, 1, 10, PRP(10*64), cq=cq) sqe = SQE(1 17 logging.info("identify done") nvme0.identify(buf, cb=identify_cb).waitdone()

NVMe协议要求admin命令完成必须要有中断,所以waitdone在检查admin CQ之前会先检查中断信号。如果没有中断信号,waitdone不会返回。IO命令没有这样的要求和操作。脚本在调用waitdone方法的时候可以设置interrupt_enabled=False来跳过对admin CQ的中断检查。

很多时候测试脚本需要获取CQE里面的dword0字段,waitdone()函数提供了这样的便利,会直接返回最后一条admin命令的CQE的dword0。譬如下面的脚本,不需要使用回调函数就可以获得CQE的dword0。

def test_get_num_of_queue(nvme0): cdw0 = nvme0.getfeatures(7).waitdone() num_of_queue = (cdw0&0xffff) + 1

Controller对象还提供最后一条admin命令的cid和latency(以us为单位)。

def test_latest_cid_latency(nvme0): nvme0.format().waitdone() logging.info("latest cid: %d"%nvme0.latest_cid) logging.info("latest command latency: %d"%nvme0.latest_latency)

AER是一种特殊的admin命令,系统发出AER命令后,盘并不会立刻返回CQE。要等到某些约定的事件发生,盘才会返回CQE给系统,并附带具体的事件信息。AER命令不受timeout限制,但是可以abort。PyNVMe3的脚本在发出AER命令之后,不需要为AER命令添加waitdone操作,因为我们也不知道其CQE会在什么时候出现。在其他命令的waitdone过程中如果发现AER的CQE,PyNVMe3会抛出warning通知脚本AER返回,并且补发一条AER,然后waitdone继续等待其他命令的完成。所以在期望有AER产生的地方,脚本需要发一条辅助的admin命令,借用其waitdone来捕获AER的CQE。

def test_sq_doorbell_invalid(nvme0, tail, buf): cq = IOCQ(nvme0, 1, 10, PRP()) sq = IOSQ(nvme0, 1, 10, PRP(), cq=cq) with pytest.warns(UserWarning, match="AER notification is triggered: 0x10100"): sq.tail = 10 time.sleep(0.1) nvme0.getfeatures(7).waitdone() # 辅助命令,用来捕获AER的CQE sq.delete() cq.delete() Controller常用方法

除了admin命令,Controller类还提供了一些常用的方法,譬如用downfw()来更新固件。因为这些API不是单纯的admin命令,所以后面不需要调用waitdone()。下面是一些比较常用的方法。

Controller.downfw(): 用于升级SSD固件。 Controller.reset(): 发起Controller reset,包括用户可定义的NVMe初始化过程。 def test_admin_func(nvme0): nvme0.downfw('path/firmware_image_file.bin') nvme0.reset() Controller.get_lba_format()方法需要提供LBA大小的参数,返回对应的LBA Format id。 def test_get_lba_format(nvme0, nvme0n1): fid = nvme0.get_lba_format(data_size=512, meta_size=0, nsid=1) Controller.supports()用于判断SSD是否支持某个admin命令,如果支持则返回True。 def test_controller_support_format(nvme0): if nvme0.supports(0x80): nvme0.format().waitdone() Controller.timestamp()可以获取当前盘的时间戳。 def test_timestamp(nvme0): logging.info("currect timestamp: %s"%nvme0.timestamp()) Controller.iostat()可以获取当前的性能. 为了进一步方便用户写脚本,PyNVMe3提供了一个便捷的获取identify数据的fixture,使用方式如下: def test_identify(id_ctrl, id_ns): logging.info(id_ctrl.VID) logging.info(id_ctrl.SN) logging.info(id_ns.NSZE) logging.info(id_ns.NCAP)

id_ctrl是Controller identify数据,id_ns是Namespace identify数据。identify数据的字段名字和NVMe协议文本一致。

timeout超时的配置

PyNVMe3可以配置命令的timeout时间,默认为10秒。命令超时之后,PyNVMe3驱动会抛出warning,并在这条命令的CQE中标记error code为07/ff。

脚本也可以单独更改某一种命令的timeout时间,譬如format命令通常需要比较长的时间才能完成,脚本可以单独修改其timeout时间。下面的例子中nvme0.timeout修改的是全局的timeout时间,单位都是毫秒。

def test_timeout_format(nvme0): nvme0.timeout=10000 #10s nvme0.get_timeout_ms(opcode=0x80) nvme0.set_timeout_ms(opcode=0x80, msec=30000)

调用Namespace.set_timeout_ms()方法可以配置IO命令的timeout时间。每次Controller reset之后,所有命令的timeout时间会恢复到默认的10秒。

cmdlog命令日志

PyNVMe3支持获取最近的若干命令的日志。Controller.cmdlog()用来获取admin队列的命令日志,Controller.cmdlog_megrged()用来获取测试盘所有队列的命令日志,按发送时间逆序排序。

def test_command_log(nvme0): for c in nvme0.cmdlog_merged(100): #所有队列的命令日志 logging.info(c) for c in nvme0.cmdlog(10): # admin队列的命令日志 logging.info(c)

cmdlog的打印格式如下,包含完整的SQE和CQE数据,以及命令发送和完成的时间戳。PyNVMe3在VSCode下的插件也可以看到同样的命令日志,供脚本调试使用。

2022-10-08 17:47:28.954447 [cmd000033: Identify, sqid: 0] 0x007e0006, 0x00000002, 0x00000000, 0x00000000 0x00000000, 0x00000000, 0x26cbe000, 0x00000000 0x00000000, 0x00000000, 0x00000000, 0x00000000 0x00000000, 0x00000000, 0x00000000, 0x00000000 2022-10-08 17:47:28.954568 [cpl: SUCCESS] 0x00000000, 0x00000000, 0x00000003, 0x0001007e 2022-10-08 17:47:28.954312 [cmd000034: Identify, sqid: 0] 0x007e0006, 0x00000001, 0x00000000, 0x00000000 0x00000000, 0x00000000, 0x26cbe000, 0x00000000 0x00000000, 0x00000000, 0x00000000, 0x00000000 0x00000000, 0x00000000, 0x00000000, 0x00000000 2022-10-08 17:47:28.954436 [cpl: SUCCESS] 0x00000000, 0x00000000, 0x00000002, 0x0001007e 2022-10-08 17:47:28.954082 [cmd000035: Identify, sqid: 0] 0x007e0006, 0x00000000, 0x00000000, 0x00000000 0x00000000, 0x00000000, 0x26cfe000, 0x00000000 0x00000000, 0x00000000, 0x00000001, 0x00000000 0x00000000, 0x00000000, 0x00000000, 0x00000000 2022-10-08 17:47:28.954231 [cpl: SUCCESS] 0x00000000, 0x00000000, 0x00000001, 0x0001007e MDTS

NVMe盘可以声明其支持最大的传输数据大小Max Data Transfer Size (MDTS)。PyNVMe3的NVMe驱动最大支持2MB MDTS,当测试数据的大小超过2MB时,需要使用metamode方式来写脚本。脚本可以通过Controller.mdts获取MDTS值。下面的例子中,脚本尝试发送一个读命令,其数据长度超过了MDTS,所以盘将会返回error。

def test_read_invalid_number_blocks(nvme0, nvme0n1, qpair): mdts = nvme0.mdts//nvme0n1.sector_size buf = Buffer((mdts+1)*nvme0n1.sector_size) nvme0n1.read(qpair, buf, 0, mdts+1).waitdone() Namespace命令

PyNVMe3支持多Namespace的测试,并提供相关命令的API,譬如Namespace Attachment和Management命令。为了方便测试脚本使用,PyNVMe3提供还了ns_attach, ns_detach, ns_create, ns_delete等常用方法。

def test_namespace_detach_and_delete(nvme0, nvme0n1): cntlid = nvme0.id_data(79, 78) nvme0.ns_detach(1, cntlid) nvme0.ns_delete(1) Security命令

PyNVMe3也支持security_send和security_receive命令,并且提供完整的TCG测试脚本。

NVMe初始化

上文PCIe类中我们提到NVMe初始化过程可以通过脚本来实现。通常NVMe设备的初始化由驱动程序实现,但PyNVMe3可以让测试脚本来定义NVMe设备初始化的具体过程。脚本也可以不提供NVMe初始化过程,让PyNVMe3的NVMe驱动负责初始化。下面是一段标准的NVMe初始化过程。

def nvme_init_user_defined(nvme0): # 1. disable cc.en nvme0[0x14] = 0 # 2. and wait csts.rdy to 0 nvme0.wait_csts(rdy=False) # 3. set admin queue registers if nvme0.init_adminq()


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3