[PATCH v3] nvmet-tcp: Enforce update ordering between queue->cmd and rcv_state
Sagi Grimberg
sagi at grimberg.me
Thu Feb 20 02:42:51 PST 2025
On 19/02/2025 17:58, Meir Elisha wrote:
> 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().
>
> Signed-off-by: Meir Elisha <meir.elisha at volumez.com>
I think we need a fixes tag, and also rephrase the patch title to
describe it is
fixing a bug rather than what it is doing.
Something like:
"nvmet-tcp: Fix a possible sporadic response drops in weakly ordered
architectures"
> ---
> Changes from v2:
> - Read queue_state and queue_cmd locally and use load barrier
> - Remove unnecessary barrier in nvmet_tcp_handle_h2c_data_pdu
>
> Changes from v1:
> - Change comments to c-style
>
> drivers/nvme/target/tcp.c | 15 +++++++++++----
> 1 file changed, 11 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c
> index 7c51c2a8c109..20fc1a27b720 100644
> --- a/drivers/nvme/target/tcp.c
> +++ b/drivers/nvme/target/tcp.c
> @@ -572,9 +572,15 @@ static void nvmet_tcp_queue_response(struct nvmet_req *req)
> container_of(req, struct nvmet_tcp_cmd, req);
> struct nvmet_tcp_queue *queue = cmd->queue;
> struct nvme_sgl_desc *sgl;
> + enum nvmet_tcp_queue_state queue_state = READ_ONCE(queue->state);
> + /*
> + * Use an acquire load to ensure that any updates to queue->state are visible
> + * before loading queue->cmd.
> + */
> + struct nvmet_tcp_cmd *queue_cmd = smp_load_acquire(&queue->cmd);
Nit, lets move this above the sgl.
> u32 len;
>
> - if (unlikely(cmd == queue->cmd)) {
> + if (unlikely(cmd == queue_cmd)) {
> sgl = &cmd->req.cmd->common.dptr.sgl;
> len = le32_to_cpu(sgl->length);
>
> @@ -583,7 +589,7 @@ static void nvmet_tcp_queue_response(struct nvmet_req *req)
> * Avoid using helpers, this might happen before
> * nvmet_req_init is completed.
> */
> - if (queue->rcv_state == NVMET_TCP_RECV_PDU &&
> + if (queue_state == NVMET_TCP_RECV_PDU &&
> len && len <= cmd->req.port->inline_data_size &&
> nvme_is_write(cmd->req.cmd))
> return;
> @@ -847,8 +853,9 @@ static void nvmet_prepare_receive_pdu(struct nvmet_tcp_queue *queue)
> {
> queue->offset = 0;
> queue->left = sizeof(struct nvme_tcp_hdr);
> - queue->cmd = NULL;
> - queue->rcv_state = NVMET_TCP_RECV_PDU;
> + WRITE_ONCE(queue->cmd, NULL);
> + /* Ensure rcv_state is visible only after queue->cmd is set */
> + smp_store_release(&queue->rcv_state, NVMET_TCP_RECV_PDU);
> }
>
> static void nvmet_tcp_free_crypto(struct nvmet_tcp_queue *queue)
More information about the Linux-nvme
mailing list