nvme 磁盘故障注入方法
本文分享自天翼云开发者社区《nvme磁盘故障注入方法》,作者:曹****飞
在存储系统中,磁盘的故障是很可能出现的问题。存储软件的设计需要对故障进行处理,提高系统的健壮性。然而磁盘的故障是不可控的,当我们想测试软件故障处理的分支时,不是很方便。用软件模拟的方法能覆盖的场景比较少,而且和实际故障的差距会比较大。因此,如果能让故障下沉到磁盘,尽可能的靠近磁盘,才能构造出尽可能真实的故障场景。本文针对 nvme 磁盘,在磁盘驱动这一层调研了几种可以注入磁盘故障的方法。
1. write uncorrectable
通过向 nvme 控制器发送 write uncor 命令,标记指定的 LBA 范围为 invalid,当读到这个 LBA 范围时,ctrl 会返回 Unrecovered Read Error 错误。可以用于模拟读的 media error。如果要清除 invalid 状态,需要向这个 LBA 范围写入数据,覆盖调 invalid 标记。
不过,write uncor 命令不是所有厂家的盘都支持。如果不支持,会返回 invalid op code 错误。查看是否支持的方法,可以看 controller 数据结构中的 oncs->write_unc 是否为 1。
还有一个缺点是只能注入读故障。
2. write protect
通过设置 namespace 的属性,可以让 namespace 写保护,当这个 namespace 收到写请求时,会返回 write error。
缺点是有的厂商的盘不支持这个属性,而且只能注入写故障。
3. 驱动层模拟故障
这是最通用的一种方法,可以构造出各种故障。spdk 的代码中就实现了这一功能。下面详细介绍下它的接口。
/**
* \brief Inject an error for the next request with a given opcode.
* \param ctrlr NVMe controller.
* \param qpair I/O queue pair to add the error command,
* NULL for Admin queue pair.
* \param opc Opcode for Admin or I/O commands.
* \param do_not_submit True if matching requests should not be submitted
* to the controller, but instead completed manually
* after timeout_in_us has expired. False if matching
* requests should be submitted to the controller and
* have their completion status modified after the
* controller completes the request.
* \param timeout_in_us Wait specified microseconds when do_not_submit is true.
* \param err_count Number of matching requests to inject errors.
* \param sct Status code type.
* \param sc Status code.
* \return 0 if successfully enabled, ENOMEM if an error command
* structure cannot be allocated.
* The function can be called multiple times to inject errors for different
* commands. If the opcode matches an existing entry, the existing entry
* will be updated with the values specified.
*/
int spdk_nvme_qpair_add_cmd_error_injection(struct spdk_nvme_ctrlr *ctrlr,
struct spdk_nvme_qpair *qpair,
uint8_t opc,
bool do_not_submit,
uint64_t timeout_in_us,
uint32_t err_count,
uint8_t sct, uint8_t sc);
ctrlr: nvme 盘的控制器数据结构,每个盘有一个;
qpair: 注入故障的 qpair,可以是 io qpair,也可以是 admin qpair。使用哪一种取决于希望在 IO 路径上还是在管理路径上产生故障;
opc: 操作类型,比如 read,write 等;
do_not_submit:如果是 true,则这个 opc 的 cmd 不会提交到 ctrl,而是在 timeout_in_us 到期后手动返回指定的 sct,sc。如果是 false,则这个 opc 的 cmd 会提交到 ctrl,等 ctrl 返回 cpl 后,将 sct 和 sc 改成指定的 sct,sc,回调的时候用改过的 cpl。命令实际上是执行成功的;
timeout_in_us:do_not_submit=true 时有效,等待 timeout_in_us 后再返回 sct,sc。范围是(0-18446744073709551615)
err_count: 表示注入故障后,此后的多少个该 opc 的 cmd 会按照注入的故障来处理,每返回一个注入的故障,err_count 减 1。减为 0 的时候不再注入故障,但 cmd 也不会从 qpair->err_cmd_head 中摘除;
sct: 返回的状态码类型;
sc: 返回的状态码;
实现的原理是向指定 qpair 的 err_cmd_head 链表中添加一个错误的 cmd,当下个该类型的 cmd 提交到该 qpair 的时候在 _nvme_qpair_submit_request 中遍历这个链表,处理这个 cmd,强制修改 cpl 中的返回值,然后再调用回调,从而模拟故障。
使用此接口可以模拟出读写错误,IO hang 住或超时的错误等,在开发测试中比较实用。
评论