Skip to content

Commit a16f889

Browse files
Meir Elishakeithbusch
authored andcommitted
nvmet-tcp: Fix a possible sporadic response drops in weakly ordered arch
The order in which queue->cmd and rcv_state are updated is crucial. If these assignments are reordered by the compiler, the worker might not get queued in nvmet_tcp_queue_response(), hanging the IO. to enforce the the correct reordering, set rcv_state using smp_store_release(). Fixes: bdaf132 ("nvmet-tcp: fix a segmentation fault during io parsing error") Signed-off-by: Meir Elisha <meir.elisha@volumez.com> Reviewed-by: Sagi Grimberg <sagi@grimberg.me> Signed-off-by: Keith Busch <kbusch@kernel.org>
1 parent ad95bab commit a16f889

File tree

1 file changed

+11
-4
lines changed

1 file changed

+11
-4
lines changed

drivers/nvme/target/tcp.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -571,10 +571,16 @@ static void nvmet_tcp_queue_response(struct nvmet_req *req)
571571
struct nvmet_tcp_cmd *cmd =
572572
container_of(req, struct nvmet_tcp_cmd, req);
573573
struct nvmet_tcp_queue *queue = cmd->queue;
574+
enum nvmet_tcp_recv_state queue_state;
575+
struct nvmet_tcp_cmd *queue_cmd;
574576
struct nvme_sgl_desc *sgl;
575577
u32 len;
576578

577-
if (unlikely(cmd == queue->cmd)) {
579+
/* Pairs with store_release in nvmet_prepare_receive_pdu() */
580+
queue_state = smp_load_acquire(&queue->rcv_state);
581+
queue_cmd = READ_ONCE(queue->cmd);
582+
583+
if (unlikely(cmd == queue_cmd)) {
578584
sgl = &cmd->req.cmd->common.dptr.sgl;
579585
len = le32_to_cpu(sgl->length);
580586

@@ -583,7 +589,7 @@ static void nvmet_tcp_queue_response(struct nvmet_req *req)
583589
* Avoid using helpers, this might happen before
584590
* nvmet_req_init is completed.
585591
*/
586-
if (queue->rcv_state == NVMET_TCP_RECV_PDU &&
592+
if (queue_state == NVMET_TCP_RECV_PDU &&
587593
len && len <= cmd->req.port->inline_data_size &&
588594
nvme_is_write(cmd->req.cmd))
589595
return;
@@ -847,8 +853,9 @@ static void nvmet_prepare_receive_pdu(struct nvmet_tcp_queue *queue)
847853
{
848854
queue->offset = 0;
849855
queue->left = sizeof(struct nvme_tcp_hdr);
850-
queue->cmd = NULL;
851-
queue->rcv_state = NVMET_TCP_RECV_PDU;
856+
WRITE_ONCE(queue->cmd, NULL);
857+
/* Ensure rcv_state is visible only after queue->cmd is set */
858+
smp_store_release(&queue->rcv_state, NVMET_TCP_RECV_PDU);
852859
}
853860

854861
static void nvmet_tcp_free_crypto(struct nvmet_tcp_queue *queue)

0 commit comments

Comments
 (0)