[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