[PATCH v1 1/1] nvmet-tcp: complete command on H2C protocol errors

Xixin Liu liuxixin at kylinos.cn
Tue Jun 2 18:14:17 PDT 2026


When nvmet_tcp_handle_h2c_data_pdu() detects a malformed H2C PDU it
returned -EPROTO without completing the associated NVMe command.  The
host only saw a transport teardown.  Complete commands that are already
associated with a valid transfer tag before returning -EPROTO.

Use nvmet_req_complete() so the host receives a failed completion.

Pick status codes by failure class: CMD_SEQ_ERROR for offset/sequence
and command-state problems; DATA_XFER_ERROR for length/plen mismatch.
Both are fatal at the NVMe/TCP transport layer, but the command should
still be failed explicitly.  Use DNR because retrying the same malformed
PDU will not help.

Only call nvmet_req_complete() when req->sq is set (the command went
through nvmet_req_init()).  The pre-connect placeholder has no sq and
must not be completed.

When the transfer tag is out of range there is no command to complete;
only reset the receiver.  Initialize cmd to NULL so err_proto never
dereferences an uninitialized pointer.

Signed-off-by: Xixin Liu <liuxixin at kylinos.cn>
---
 drivers/nvme/target/tcp.c | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c
index 20f150d17a96..9a9d3a354566 100644
--- a/drivers/nvme/target/tcp.c
+++ b/drivers/nvme/target/tcp.c
@@ -977,10 +977,21 @@ static int nvmet_tcp_handle_req_failure(struct nvmet_tcp_queue *queue,
 	return ret;
 }
 
+static void nvmet_tcp_fail_h2c_cmd(struct nvmet_tcp_cmd *cmd, u16 status)
+{
+	if (!cmd->req.sq)
+		return;
+
+	if (!(cmd->flags & NVMET_TCP_F_INIT_FAILED))
+		nvmet_req_complete(&cmd->req, status | NVME_STATUS_DNR);
+	nvmet_tcp_free_cmd_buffers(cmd);
+}
+
 static int nvmet_tcp_handle_h2c_data_pdu(struct nvmet_tcp_queue *queue)
 {
 	struct nvme_tcp_data_pdu *data = &queue->pdu.data;
-	struct nvmet_tcp_cmd *cmd;
+	struct nvmet_tcp_cmd *cmd = NULL;
+	u16 err_status = NVME_SC_CMD_SEQ_ERROR;
 	unsigned int exp_data_len;
 
 	if (likely(queue->nr_cmds)) {
@@ -1011,6 +1022,7 @@ static int nvmet_tcp_handle_h2c_data_pdu(struct nvmet_tcp_queue *queue)
 		     cmd->pdu_len == 0 ||
 		     cmd->pdu_len > NVMET_TCP_MAXH2CDATA)) {
 		pr_err("H2CData PDU len %u is invalid\n", cmd->pdu_len);
+		err_status = NVME_SC_DATA_XFER_ERROR;
 		goto err_proto;
 	}
        /*
@@ -1036,7 +1048,9 @@ static int nvmet_tcp_handle_h2c_data_pdu(struct nvmet_tcp_queue *queue)
 	return 0;
 
 err_proto:
-	/* FIXME: use proper transport errors */
+	if (cmd)
+		nvmet_tcp_fail_h2c_cmd(cmd, err_status);
+	nvmet_prepare_receive_pdu(queue);
 	return -EPROTO;
 }
 
-- 
2.43.0




More information about the Linux-nvme mailing list