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 <shuhei.matsumoto.xt@hitachi.com>
Change-Id: I9680af2556c08f5888aeaf0a772097e4744182be
Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/458921
Tested-by: SPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: Darek Stojaczyk <dariusz.stojaczyk@intel.com>
Reviewed-by: Changpeng Liu <changpeng.liu@intel.com>
This commit is contained in:
Shuhei Matsumoto 2019-06-21 15:01:11 +09:00 committed by Changpeng Liu
parent a7a32ea052
commit 8448adaefa

View File

@ -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);