A command that has received all its data but whose data digest is still outstanding is stalled in NVMET_TCP_RECV_DDGST, where nvmet_tcp_need_data_in() is false. nvmet_tcp_uninit_data_in_cmds() therefore skips it during teardown, leaking its submission queue reference, and nvmet_sq_destroy() then blocks forever waiting on it. Release such a command in nvmet_tcp_uninit_data_in_cmds(), and reset rcv_state after that call. Fixes: 872d26a391da ("nvmet-tcp: add NVMe over TCP target driver") Cc: stable@vger.kernel.org Signed-off-by: Shivam Kumar --- drivers/nvme/target/tcp.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index e59810175262..3bf344b184e8 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -1608,6 +1608,11 @@ static void nvmet_tcp_uninit_data_in_cmds(struct nvmet_tcp_queue *queue) nvmet_req_uninit(&cmd->req); } + if (queue->rcv_state == NVMET_TCP_RECV_DDGST && queue->cmd && + !nvmet_tcp_need_data_in(queue->cmd) && + !(queue->cmd->flags & NVMET_TCP_F_INIT_FAILED)) + nvmet_req_uninit(&queue->cmd->req); + if (!queue->nr_cmds && nvmet_tcp_need_data_in(&queue->connect)) { /* failed in connect */ nvmet_req_uninit(&queue->connect.req); @@ -1636,11 +1641,12 @@ static void nvmet_tcp_release_queue_work(struct work_struct *w) nvmet_tcp_restore_socket_callbacks(queue); cancel_delayed_work_sync(&queue->tls_handshake_tmo_work); cancel_work_sync(&queue->io_work); - /* stop accepting incoming data */ - queue->rcv_state = NVMET_TCP_RECV_ERR; nvmet_sq_put_tls_key(&queue->nvme_sq); nvmet_tcp_uninit_data_in_cmds(queue); + /* stop accepting incoming data */ + queue->rcv_state = NVMET_TCP_RECV_ERR; + nvmet_sq_destroy(&queue->nvme_sq); nvmet_cq_put(&queue->nvme_cq); cancel_work_sync(&queue->io_work); -- 2.53.0