From 8448adaefa8cafd957b3b240eecfa2f4fc90fabc Mon Sep 17 00:00:00 2001 From: Shuhei Matsumoto Date: Fri, 21 Jun 2019 15:01:11 +0900 Subject: [PATCH] nvmf/tcp: Verify DIF before sending C2H data in spdk_nvmf_tcp_send_c2h_data If DIF mode is local and C2H data is extended LBA payload, DIF should be verified just before sending the payload. Add a helper function nvmf_tcp_pdu_verify_dif and call it in spdk_nvmf_tcp_send_c2h_data after completing nvme_tcp_pdu_set_data_buf. When nvmf_tcp_pdu_verify_dif returns error, treat the error as fatal transport error because the error is caused by the target itself. Handle the fatal NVMe/TCP transport error by terminating the connection as described in the NVMe specification. On the other hand, data digest error is treated as a non-fatal transport error because the error is caused outside the target. This is reasonable. Signed-off-by: Shuhei Matsumoto Change-Id: I9680af2556c08f5888aeaf0a772097e4744182be Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/458921 Tested-by: SPDK CI Jenkins Reviewed-by: Darek Stojaczyk Reviewed-by: Changpeng Liu --- lib/nvmf/tcp.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/lib/nvmf/tcp.c b/lib/nvmf/tcp.c index 93687a161..cd45ac5e6 100644 --- a/lib/nvmf/tcp.c +++ b/lib/nvmf/tcp.c @@ -2177,6 +2177,23 @@ spdk_nvmf_tcp_req_parse_sgl(struct spdk_nvmf_tcp_transport *ttransport, return -1; } +static int +nvmf_tcp_pdu_verify_dif(struct nvme_tcp_pdu *pdu, + const struct spdk_dif_ctx *dif_ctx) +{ + struct spdk_dif_error err_blk = {}; + int rc; + + rc = spdk_dif_verify_stream(pdu->data_iov, pdu->data_iovcnt, + 0, pdu->data_len, pdu->dif_ctx, &err_blk); + if (rc != 0) { + SPDK_ERRLOG("DIF error detected. type=%d, offset=%" PRIu32 "\n", + err_blk.err_type, err_blk.err_offset); + } + + return rc; +} + static void spdk_nvmf_tcp_send_c2h_data(struct spdk_nvmf_tcp_qpair *tqpair, struct spdk_nvmf_tcp_req *tcp_req) @@ -2184,6 +2201,7 @@ spdk_nvmf_tcp_send_c2h_data(struct spdk_nvmf_tcp_qpair *tqpair, struct nvme_tcp_pdu *rsp_pdu; struct spdk_nvme_tcp_c2h_data_hdr *c2h_data; uint32_t plen, pdo, alignment; + int rc; SPDK_DEBUGLOG(SPDK_LOG_NVMF_TCP, "enter\n"); @@ -2228,6 +2246,21 @@ spdk_nvmf_tcp_send_c2h_data(struct spdk_nvmf_tcp_qpair *tqpair, nvme_tcp_pdu_set_data_buf(rsp_pdu, tcp_req->req.iov, tcp_req->req.iovcnt, c2h_data->datao, c2h_data->datal); + if (spdk_unlikely(rsp_pdu->dif_ctx != NULL)) { + rc = nvmf_tcp_pdu_verify_dif(rsp_pdu, rsp_pdu->dif_ctx); + if (rc != 0) { + /* Data digest error detected by the NVMe/TCP target is treated as non-fatal + * transport error because the cause will be outside the NVMe/TCP target. + * + * On the other hand, treat DIF check error as fatal transport error here + * here because the error is caused by the target itself. Fatal NVMe/TCP + * transport error is handled by terminating the connection. + */ + tqpair->state = NVME_TCP_QPAIR_STATE_EXITING; + return; + } + } + tcp_req->c2h_data_offset += c2h_data->datal; if (tcp_req->c2h_data_offset == tcp_req->req.length) { SPDK_DEBUGLOG(SPDK_LOG_NVMF_TCP, "Last pdu for tcp_req=%p on tqpair=%p\n", tcp_req, tqpair);