本文将mark下NVMe协议中的command retry机制。
DNR
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
参考资料:
NVMe 1.3 spec
Linux kernel source code