* [PATCH v1 0/1] nvme-tcp: fix wrong status on deferred digest error
@ 2026-08-26 2:15 Xixin Liu
2026-08-26 1:30 ` [PATCH v1 1/1] " Xixin Liu
2026-08-27 1:05 ` [PATCH v2 " Xixin Liu
0 siblings, 2 replies; 10+ messages in thread
From: Xixin Liu @ 2026-08-26 2:15 UTC (permalink / raw)
To: linux-nvme
Cc: kbusch, axboe, hch, sagi, hare, dwagner, linux-kernel, liuxixin
Hi,
A C2HData digest error stores a host status code in the per-request
status field. Without DATA_SUCCESS the request is completed later from
the rsp path. That path used to pass the stored value straight into
complete, which expects CQE status field encoding and shifts right, so
DATA_XFER_ERROR could be reported as INVALID_FIELD.
This series keeps the per-request status as a host status code. On the
rsp path, shift left when a host error was already stored, otherwise use
the completion status field.
To hit the TCP DATA_XFER_ERROR path without DATA_SUCCESS, enable data
digest and temporarily change the target in
drivers/nvme/target/tcp.c, nvmet_setup_c2h_data_pdu, after the digest
is calculated:
if (queue->data_digest) {
pdu->hdr.flags |= NVME_TCP_F_DDGST;
nvmet_tcp_calc_ddgst(cmd);
cmd->exp_ddgst ^= cpu_to_le32(1);
}
The default target path does not set DATA_SUCCESS on C2HData, so the
host stores the digest error and completes from the later rsp.
Verified on linux-next with an nvmet-tcp loopback target. Unpatched:
nvme nvme0: data digest error: recv 0x2542708 expected 0x2542709
nvme nvme0: Identify Controller failed (2)
Patched:
nvme nvme0: data digest error: recv 0x7ebd5719 expected 0x7ebd5718
nvme nvme0: Identify Controller failed (4)
Status code 0x2 is INVALID_FIELD. Status code 0x4 is DATA_XFER_ERROR.
recv and expected differ by one in both runs.
Thanks,
Xixin Liu
---
Xixin Liu (1):
nvme-tcp: fix wrong status on deferred digest error
drivers/nvme/host/tcp.c | 20 +++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
--
2.53.0
^ permalink raw reply [flat|nested] 10+ messages in thread* [PATCH v1 1/1] nvme-tcp: fix wrong status on deferred digest error 2026-08-26 2:15 [PATCH v1 0/1] nvme-tcp: fix wrong status on deferred digest error Xixin Liu @ 2026-08-26 1:30 ` Xixin Liu 2026-08-26 9:39 ` Hannes Reinecke 2026-08-27 1:05 ` [PATCH v2 " Xixin Liu 1 sibling, 1 reply; 10+ messages in thread From: Xixin Liu @ 2026-08-26 1:30 UTC (permalink / raw) To: linux-nvme Cc: kbusch, axboe, hch, sagi, hare, dwagner, linux-kernel, liuxixin A C2HData digest error stores a host status code in req status. Without DATA_SUCCESS the request is completed later from the rsp path, which passed that value straight into complete and could report the wrong status code. Keep req status as a host status code. On the rsp path, shift left when a host error was already stored, otherwise use the completion status field. Fixes: 1ba2e507f55c ("nvme-tcp: Do not reset transport on data digest errors") Signed-off-by: Xixin Liu <liuxixin@kylinos.cn> --- drivers/nvme/host/tcp.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) --- a/drivers/nvme/host/tcp.c 2026-08-26 09:24:50.988099281 +0800 +++ b/drivers/nvme/host/tcp.c 2026-08-26 09:25:12.474510519 +0800 @@ -73,7 +73,7 @@ u32 h2cdata_left; u32 h2cdata_offset; u16 ttag; - __le16 status; + u16 status; struct list_head entry; struct llist_node lentry; __le32 ddgst; @@ -617,6 +617,7 @@ { struct nvme_tcp_request *req; struct request *rq; + __le16 status; rq = nvme_find_rq(nvme_tcp_tagset(queue), cqe->command_id); if (!rq) { @@ -628,10 +629,12 @@ } req = blk_mq_rq_to_pdu(rq); - if (req->status == cpu_to_le16(NVME_SC_SUCCESS)) - req->status = cqe->status; + if (req->status != NVME_SC_SUCCESS) + status = cpu_to_le16(req->status << 1); + else + status = cqe->status; - if (!nvme_try_complete_req(rq, req->status, cqe->result)) + if (!nvme_try_complete_req(rq, status, cqe->result)) nvme_complete_rq(rq); queue->nr_cqe++; @@ -961,8 +964,7 @@ queue->ddgst_remaining = NVME_TCP_DIGEST_LENGTH; } else { if (pdu->hdr.flags & NVME_TCP_F_DATA_SUCCESS) { - nvme_tcp_end_request(rq, - le16_to_cpu(req->status)); + nvme_tcp_end_request(rq, req->status); queue->nr_cqe++; } nvme_tcp_init_recv_ctx(queue); @@ -996,7 +998,7 @@ pdu->command_id); struct nvme_tcp_request *req = blk_mq_rq_to_pdu(rq); - req->status = cpu_to_le16(NVME_SC_DATA_XFER_ERROR); + req->status = NVME_SC_DATA_XFER_ERROR; dev_err(queue->ctrl->ctrl.device, "data digest error: recv %#x expected %#x\n", @@ -1009,7 +1011,7 @@ pdu->command_id); struct nvme_tcp_request *req = blk_mq_rq_to_pdu(rq); - nvme_tcp_end_request(rq, le16_to_cpu(req->status)); + nvme_tcp_end_request(rq, req->status); queue->nr_cqe++; } @@ -2733,7 +2735,7 @@ return ret; req->state = NVME_TCP_SEND_CMD_PDU; - req->status = cpu_to_le16(NVME_SC_SUCCESS); + req->status = NVME_SC_SUCCESS; req->offset = 0; req->data_sent = 0; req->pdu_len = 0; -- 2.53.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v1 1/1] nvme-tcp: fix wrong status on deferred digest error 2026-08-26 1:30 ` [PATCH v1 1/1] " Xixin Liu @ 2026-08-26 9:39 ` Hannes Reinecke 2026-08-27 2:40 ` [PATCH v1 0/1] " Xixin Liu 0 siblings, 1 reply; 10+ messages in thread From: Hannes Reinecke @ 2026-08-26 9:39 UTC (permalink / raw) To: Xixin Liu, linux-nvme; +Cc: kbusch, axboe, hch, sagi, dwagner, linux-kernel On 8/26/26 3:30 AM, Xixin Liu wrote: > A C2HData digest error stores a host status code in req status. Without > DATA_SUCCESS the request is completed later from the rsp path, which > passed that value straight into complete and could report the wrong > status code. > > Keep req status as a host status code. On the rsp path, shift left when > a host error was already stored, otherwise use the completion status > field. > I really don't see it. Where is the issue? AFAICS we're just changing the type of 'status' from holding the wire-value to the host value. Can you make the patch simpler by just keeping 'status' as __le16? Cheers, Hannes -- Dr. Hannes Reinecke Kernel Storage Architect hare@suse.de +49 911 74053 688 SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v1 0/1] nvme-tcp: fix wrong status on deferred digest error 2026-08-26 9:39 ` Hannes Reinecke @ 2026-08-27 2:40 ` Xixin Liu 0 siblings, 0 replies; 10+ messages in thread From: Xixin Liu @ 2026-08-27 2:40 UTC (permalink / raw) To: hare; +Cc: kbusch, axboe, hch, sagi, dwagner, linux-nvme, linux-kernel, liuxixin Hi Hannes, Thanks for the review. This is not a wire to host conversion. req->status already mixes wire and host values. Digest error stores a host SC. u16 makes the host SC explicit. __le16 is endian packaging for wire layout. The SUCCESS rsp branch overwrites with cqe->status, which is wire. A host SC must be shifted left before nvme_try_complete_req. Without DATA_SUCCESS, digest error is deferred to the rsp path: req->status = cpu_to_le16(NVME_SC_DATA_XFER_ERROR); /* host SC */ if (req->status == cpu_to_le16(NVME_SC_SUCCESS)) req->status = cqe->status; /* wire */ if (!nvme_try_complete_req(rq, req->status, cqe->result)) Both branches pass req->status into nvme_try_complete_req, which expects wire: nvme_req(req)->status = le16_to_cpu(status) >> 1; SUCCESS is fine. Non-SUCCESS still holds host SC 04h Data Transfer Error, so 0x4 becomes INVALID_FIELD, 0x2. v1 changed req->status to u16. v2 keeps __le16 as you suggested. Please take a look at both and say which you prefer for merge. I will send v2 shortly. Thanks, Xixin Liu ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 0/1] nvme-tcp: fix wrong status on deferred digest error 2026-08-26 2:15 [PATCH v1 0/1] nvme-tcp: fix wrong status on deferred digest error Xixin Liu 2026-08-26 1:30 ` [PATCH v1 1/1] " Xixin Liu @ 2026-08-27 1:05 ` Xixin Liu 2026-08-27 1:05 ` [PATCH v2 1/1] " Xixin Liu 2026-08-31 2:05 ` [PATCH v3 0/1] " Xixin Liu 1 sibling, 2 replies; 10+ messages in thread From: Xixin Liu @ 2026-08-27 1:05 UTC (permalink / raw) To: linux-nvme Cc: kbusch, axboe, hch, sagi, hare, dwagner, linux-kernel, liuxixin Hi, Thanks Hannes for the review on v1. Changes since v1: - keep req->status as __le16 - only fix the rsp path: shift left when a deferred host error is stored Same loopback test as v1, PASS. Thanks, Xixin Liu --- Xixin Liu (1): nvme-tcp: fix wrong status on deferred digest error drivers/nvme/host/tcp.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) -- 2.53.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 1/1] nvme-tcp: fix wrong status on deferred digest error 2026-08-27 1:05 ` [PATCH v2 " Xixin Liu @ 2026-08-27 1:05 ` Xixin Liu 2026-08-30 21:43 ` Sagi Grimberg 2026-08-31 2:05 ` [PATCH v3 0/1] " Xixin Liu 1 sibling, 1 reply; 10+ messages in thread From: Xixin Liu @ 2026-08-27 1:05 UTC (permalink / raw) To: linux-nvme Cc: kbusch, axboe, hch, sagi, hare, dwagner, linux-kernel, liuxixin A C2HData digest error stores a host status code in req->status. Without DATA_SUCCESS completion goes through the rsp path, which passed that host value straight into complete as CQE Status wire encoding. On the rsp path, shift left when a deferred host error is stored, otherwise use cqe->status. Fixes: 1ba2e507f55c ("nvme-tcp: Do not reset transport on data digest errors") Signed-off-by: Xixin Liu <liuxixin@kylinos.cn> --- drivers/nvme/host/tcp.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -617,6 +617,7 @@ static int nvme_tcp_process_nvme_cqe(struct nvme_tcp_queue *queue, { struct nvme_tcp_request *req; struct request *rq; + __le16 status; rq = nvme_find_rq(nvme_tcp_tagset(queue), cqe->command_id); if (!rq) { @@ -628,10 +629,12 @@ static int nvme_tcp_process_nvme_cqe(struct nvme_tcp_queue *queue, } req = blk_mq_rq_to_pdu(rq); if (req->status == cpu_to_le16(NVME_SC_SUCCESS)) - req->status = cqe->status; + status = cqe->status; + else + status = cpu_to_le16(le16_to_cpu(req->status) << 1); - if (!nvme_try_complete_req(rq, req->status, cqe->result)) + if (!nvme_try_complete_req(rq, status, cqe->result)) nvme_complete_rq(rq); queue->nr_cqe++; -- 2.53.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/1] nvme-tcp: fix wrong status on deferred digest error 2026-08-27 1:05 ` [PATCH v2 1/1] " Xixin Liu @ 2026-08-30 21:43 ` Sagi Grimberg 2026-08-31 2:15 ` Xixin Liu 0 siblings, 1 reply; 10+ messages in thread From: Sagi Grimberg @ 2026-08-30 21:43 UTC (permalink / raw) To: Xixin Liu, linux-nvme; +Cc: kbusch, axboe, hch, hare, dwagner, linux-kernel On 27/08/2026 4:05, Xixin Liu wrote: > A C2HData digest error stores a host status code in req->status. Without > DATA_SUCCESS completion goes through the rsp path, which passed that host > value straight into complete as CQE Status wire encoding. > > On the rsp path, shift left when a deferred host error is stored, > otherwise use cqe->status. Why not just assign with a shifted status? We already do this in nvme_tcp_end_request() ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/1] nvme-tcp: fix wrong status on deferred digest error 2026-08-30 21:43 ` Sagi Grimberg @ 2026-08-31 2:15 ` Xixin Liu 0 siblings, 0 replies; 10+ messages in thread From: Xixin Liu @ 2026-08-31 2:15 UTC (permalink / raw) To: sagi; +Cc: axboe, dwagner, hare, hch, kbusch, linux-kernel, linux-nvme, liuxixin Hi Sagi, Thanks for the review. v3 follows your suggestion: host status on the rsp path, complete through nvme_tcp_end_request with cqe->result. I will send v3 shortly. Thanks, Xixin Liu ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 0/1] nvme-tcp: fix wrong status on deferred digest error 2026-08-27 1:05 ` [PATCH v2 " Xixin Liu 2026-08-27 1:05 ` [PATCH v2 1/1] " Xixin Liu @ 2026-08-31 2:05 ` Xixin Liu 2026-08-31 2:10 ` [PATCH v3 1/1] " Xixin Liu 1 sibling, 1 reply; 10+ messages in thread From: Xixin Liu @ 2026-08-31 2:05 UTC (permalink / raw) To: linux-nvme Cc: kbusch, axboe, hch, sagi, hare, dwagner, linux-kernel, liuxixin Hi, Thanks for the review on v2. Changes since v2: - complete the rsp path through nvme_tcp_end_request with host status Thanks, Xixin Liu --- Xixin Liu (1): nvme-tcp: fix wrong status on deferred digest error drivers/nvme/host/tcp.c | 36 ++++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) -- 2.53.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v3 1/1] nvme-tcp: fix wrong status on deferred digest error 2026-08-31 2:05 ` [PATCH v3 0/1] " Xixin Liu @ 2026-08-31 2:10 ` Xixin Liu 0 siblings, 0 replies; 10+ messages in thread From: Xixin Liu @ 2026-08-31 2:10 UTC (permalink / raw) To: linux-nvme Cc: kbusch, axboe, hch, sagi, hare, dwagner, linux-kernel, liuxixin A C2HData digest error stores a host status code in req->status. Without DATA_SUCCESS completion goes through the rsp path, which passed that host value straight into complete as CQE Status wire encoding. On the rsp path, pick host status from the CQE status field or from the stored deferred error, then complete through nvme_tcp_end_request with the CQE result. Fixes: 1ba2e507f55c ("nvme-tcp: Do not reset transport on data digest errors") Signed-off-by: Xixin Liu <liuxixin@kylinos.cn> --- drivers/nvme/host/tcp.c | 36 +++++++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 15 deletions(-) --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -612,11 +612,19 @@ static void nvme_tcp_error_recovery(struct nvme_ctrl *ctrl) queue_work(nvme_reset_wq, &to_tcp_ctrl(ctrl)->err_work); } +static inline void nvme_tcp_end_request(struct request *rq, u16 status, + union nvme_result result) +{ + if (!nvme_try_complete_req(rq, cpu_to_le16(status << 1), result)) + nvme_complete_rq(rq); +} + static int nvme_tcp_process_nvme_cqe(struct nvme_tcp_queue *queue, struct nvme_completion *cqe) { struct nvme_tcp_request *req; struct request *rq; + u16 status; rq = nvme_find_rq(nvme_tcp_tagset(queue), cqe->command_id); if (!rq) { @@ -629,10 +637,11 @@ static int nvme_tcp_process_nvme_cqe(struct nvme_tcp_queue *queue, req = blk_mq_rq_to_pdu(rq); if (req->status == cpu_to_le16(NVME_SC_SUCCESS)) - req->status = cqe->status; + status = le16_to_cpu(cqe->status) >> 1; + else + status = le16_to_cpu(req->status); - if (!nvme_try_complete_req(rq, req->status, cqe->result)) - nvme_complete_rq(rq); + nvme_tcp_end_request(rq, status, cqe->result); queue->nr_cqe++; return 0; @@ -893,14 +902,6 @@ static int nvme_tcp_recv_pdu(struct nvme_tcp_queue *queue, struct sk_buff *skb, return -EINVAL; } -static inline void nvme_tcp_end_request(struct request *rq, u16 status) -{ - union nvme_result res = {}; - - if (!nvme_try_complete_req(rq, cpu_to_le16(status << 1), res)) - nvme_complete_rq(rq); -} - static int nvme_tcp_recv_data(struct nvme_tcp_queue *queue, struct sk_buff *skb, unsigned int *offset, size_t *len) { @@ -961,8 +962,9 @@ static int nvme_tcp_recv_data(struct nvme_tcp_queue *queue, struct sk_buff *skb, queue->ddgst_remaining = NVME_TCP_DIGEST_LENGTH; } else { if (pdu->hdr.flags & NVME_TCP_F_DATA_SUCCESS) { - nvme_tcp_end_request(rq, - le16_to_cpu(req->status)); + union nvme_result res = {}; + + nvme_tcp_end_request(rq, le16_to_cpu(req->status), res); queue->nr_cqe++; } nvme_tcp_init_recv_ctx(queue); @@ -1009,7 +1011,9 @@ static int nvme_tcp_recv_ddgst(struct nvme_tcp_queue *queue, pdu->command_id); struct nvme_tcp_request *req = blk_mq_rq_to_pdu(rq); - nvme_tcp_end_request(rq, le16_to_cpu(req->status)); + union nvme_result res = {}; + + nvme_tcp_end_request(rq, le16_to_cpu(req->status), res); queue->nr_cqe++; } @@ -1124,8 +1128,10 @@ static void nvme_tcp_fail_request(struct nvme_tcp_request *req) nvme_complete_async_event(&req->queue->ctrl->ctrl, cpu_to_le16(NVME_SC_HOST_PATH_ERROR), &res); } else { + union nvme_result res = {}; + nvme_tcp_end_request(blk_mq_rq_from_pdu(req), - NVME_SC_HOST_PATH_ERROR); + NVME_SC_HOST_PATH_ERROR, res); } } -- 2.53.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-08-31 2:00 UTC | newest] Thread overview: 10+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-08-26 2:15 [PATCH v1 0/1] nvme-tcp: fix wrong status on deferred digest error Xixin Liu 2026-08-26 1:30 ` [PATCH v1 1/1] " Xixin Liu 2026-08-26 9:39 ` Hannes Reinecke 2026-08-27 2:40 ` [PATCH v1 0/1] " Xixin Liu 2026-08-27 1:05 ` [PATCH v2 " Xixin Liu 2026-08-27 1:05 ` [PATCH v2 1/1] " Xixin Liu 2026-08-30 21:43 ` Sagi Grimberg 2026-08-31 2:15 ` Xixin Liu 2026-08-31 2:05 ` [PATCH v3 0/1] " Xixin Liu 2026-08-31 2:10 ` [PATCH v3 1/1] " Xixin Liu
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox