本文将mark下NVMe协议中的command retry机制。

DNR

1
NVME_SC_DNR         = 0x4000,
1
2
3
4
5
6
7
8
9
10
void nvme_complete_rq(struct request *req)
{
...
switch (nvme_decide_disposition(req)) {
...
case RETRY:
nvme_retry_req(req);
return;
...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
enum nvme_disposition {
COMPLETE,
RETRY,
FAILOVER,
AUTHENTICATE,
};

static inline enum nvme_disposition nvme_decide_disposition(struct request *req)
{
if (likely(nvme_req(req)->status == 0))
return COMPLETE;

if ((nvme_req(req)->status & 0x7ff) == NVME_SC_AUTH_REQUIRED)
return AUTHENTICATE;

if (blk_noretry_request(req) ||
(nvme_req(req)->status & NVME_SC_DNR) ||
nvme_req(req)->retries >= nvme_max_retries)
return COMPLETE;

if (req->cmd_flags & REQ_NVME_MPATH) {
if (nvme_is_path_error(nvme_req(req)->status) ||
blk_queue_dying(req->q))
return FAILOVER;
} else {
if (blk_queue_dying(req->q))
return COMPLETE;
}

return RETRY;
}
1
2
3
static u8 nvme_max_retries = 5;
module_param_named(max_retries, nvme_max_retries, byte, 0644);
MODULE_PARM_DESC(max_retries, "max number of retries a command may have");

默认情况下,每个command最多retry5次。

CRD和CRDT

CRD(Command Retry Delay)和 CRDT(Command Retry Delay Time)是 NVMe 协议提供的一套控制器建议的重试等待时间机制。

CRD/CRDT 的设计目的并不是决定“是否重试”,而是指导 Host“什么时候重试最合适”,避免 Host 无意义地快速重试,减轻控制器压力,提高恢复成功率。

含义如下:

  • CRD:位于 CQE Status Field 中,由本次 CQE 返回
  • CRDT1/2/3:位于 Identify Controller 数据结构中,由控制器预先公布三个可选的延迟值(单位通常为 100ms)

例如:

1
2
3
4
5
6
7
8
CQE:
DNR = 0
CRD = 2

Identify Controller:
CRDT1 = 5
CRDT2 = 20
CRDT3 = 100

那么表示:

1
2
本次失败可以重试,
但建议等待 CRDT2 = 20 × 100ms = 2s 后再重试

因此

如果 CQE 设置了 CRD,重试会按控制器的 CRDT 延迟

意思就是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
收到错误 CQE


DNR = 0 ?

Yes


CQE 中是否携带 CRD?

Yes


查 Identify Controller 中对应的
CRDT1 / CRDT2 / CRDT3


等待对应时间


重新提交 I/O

参考资料:

  1. NVMe 1.3 spec
  2. Linux kernel source code