From 2d0ce5b48b929caa5548b9a7f2ceec56501156e7 Mon Sep 17 00:00:00 2001 From: Ziye Yang Date: Thu, 17 Jan 2019 18:18:16 +0800 Subject: [PATCH] nvmf/tcp: Implement correct behavior of timeout for C2Htermreq case From TP8000 spec 7.4.7, "In response to a C2HTermReq PDU, the host shall terminate the connection. If the host does not terminate the connection in an implementation specific period that does not exceed 30 seconds, the controller may terminate the connection on its own". It means that the timeout is designed for: when the target is sending out C2hTermReq, if the host does not terminate the connection, the target should terminate the connection. PS: For detecting the malicous connection without sending response (such as no response of R2T PDU) which should be another patch. Change-Id: I586dbb235d99aeab5d748a19b9128cd8b0cef183 Signed-off-by: Ziye Yang Reviewed-on: https://review.gerrithub.io/c/440831 Tested-by: SPDK CI Jenkins Reviewed-by: Shuhei Matsumoto Reviewed-by: Ben Walker Reviewed-by: Jim Harris --- lib/nvmf/tcp.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/nvmf/tcp.c b/lib/nvmf/tcp.c index d34c980df..ac407a17d 100644 --- a/lib/nvmf/tcp.c +++ b/lib/nvmf/tcp.c @@ -260,7 +260,12 @@ struct spdk_nvmf_tcp_qpair { uint16_t initiator_port; uint16_t target_port; - TAILQ_ENTRY(spdk_nvmf_tcp_qpair) link; + /* Timer used to destroy qpair after detecting transport error issue if initiator does + * not close the connection. + */ + struct spdk_poller *timeout_poller; + + TAILQ_ENTRY(spdk_nvmf_tcp_qpair) link; }; struct spdk_nvmf_tcp_poll_group { @@ -1245,9 +1250,32 @@ spdk_nvmf_tcp_qpair_set_recv_state(struct spdk_nvmf_tcp_qpair *tqpair, } } +static int +spdk_nvmf_tcp_qpair_handle_timeout(void *ctx) +{ + struct spdk_nvmf_tcp_qpair *tqpair = ctx; + + assert(tqpair->recv_state == NVME_TCP_PDU_RECV_STATE_ERROR); + + SPDK_ERRLOG("No pdu coming for tqpair=%p within %d seconds\n", tqpair, + SPDK_NVME_TCP_QPAIR_EXIT_TIMEOUT); + tqpair->state = NVME_TCP_QPAIR_STATE_EXITED; + SPDK_DEBUGLOG(SPDK_LOG_NVMF_TCP, "will disconect the tqpair=%p\n", tqpair); + spdk_poller_unregister(&tqpair->timeout_poller); + spdk_nvmf_qpair_disconnect(&tqpair->qpair, NULL, NULL); + + return 0; +} + static void spdk_nvmf_tcp_send_c2h_term_req_complete(void *cb_arg) { + struct spdk_nvmf_tcp_qpair *tqpair = (struct spdk_nvmf_tcp_qpair *)cb_arg; + + if (!tqpair->timeout_poller) { + tqpair->timeout_poller = spdk_poller_register(spdk_nvmf_tcp_qpair_handle_timeout, tqpair, + SPDK_NVME_TCP_QPAIR_EXIT_TIMEOUT * 1000000); + } } static void @@ -2591,6 +2619,7 @@ spdk_nvmf_tcp_sock_cb(void *arg, struct spdk_sock_group *group, struct spdk_sock tqpair->state = NVME_TCP_QPAIR_STATE_EXITED; spdk_nvmf_tcp_qpair_flush_pdus(tqpair); SPDK_DEBUGLOG(SPDK_LOG_NVMF_TCP, "will disconect the tqpair=%p\n", tqpair); + spdk_poller_unregister(&tqpair->timeout_poller); spdk_nvmf_qpair_disconnect(&tqpair->qpair, NULL, NULL); } }