]> www.infradead.org Git - nvme.git/commitdiff
nvmet-rdma: recheck queue state is LIVE in state lock in recv done
authorRuozhu Li <david.li@jaguarmicro.com>
Sun, 16 Feb 2025 12:49:56 +0000 (20:49 +0800)
committerKeith Busch <kbusch@kernel.org>
Tue, 18 Feb 2025 15:22:55 +0000 (07:22 -0800)
The queue state checking in nvmet_rdma_recv_done is not in queue state
lock.Queue state can transfer to LIVE in cm establish handler between
state checking and state lock here, cause a silent drop of nvme connect
cmd.
Recheck queue state whether in LIVE state in state lock to prevent this
issue.

Signed-off-by: Ruozhu Li <david.li@jaguarmicro.com>
Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
Signed-off-by: Keith Busch <kbusch@kernel.org>
drivers/nvme/target/rdma.c

index 1afd93026f9bf0481de5910e84502078d5912766..2a4536ef618487455ada997d0285d9dce657c2ef 100644 (file)
@@ -996,6 +996,27 @@ out_err:
        nvmet_req_complete(&cmd->req, status);
 }
 
+static bool nvmet_rdma_recv_not_live(struct nvmet_rdma_queue *queue,
+               struct nvmet_rdma_rsp *rsp)
+{
+       unsigned long flags;
+       bool ret = true;
+
+       spin_lock_irqsave(&queue->state_lock, flags);
+       /*
+        * recheck queue state is not live to prevent a race condition
+        * with RDMA_CM_EVENT_ESTABLISHED handler.
+        */
+       if (queue->state == NVMET_RDMA_Q_LIVE)
+               ret = false;
+       else if (queue->state == NVMET_RDMA_Q_CONNECTING)
+               list_add_tail(&rsp->wait_list, &queue->rsp_wait_list);
+       else
+               nvmet_rdma_put_rsp(rsp);
+       spin_unlock_irqrestore(&queue->state_lock, flags);
+       return ret;
+}
+
 static void nvmet_rdma_recv_done(struct ib_cq *cq, struct ib_wc *wc)
 {
        struct nvmet_rdma_cmd *cmd =
@@ -1038,17 +1059,9 @@ static void nvmet_rdma_recv_done(struct ib_cq *cq, struct ib_wc *wc)
        rsp->n_rdma = 0;
        rsp->invalidate_rkey = 0;
 
-       if (unlikely(queue->state != NVMET_RDMA_Q_LIVE)) {
-               unsigned long flags;
-
-               spin_lock_irqsave(&queue->state_lock, flags);
-               if (queue->state == NVMET_RDMA_Q_CONNECTING)
-                       list_add_tail(&rsp->wait_list, &queue->rsp_wait_list);
-               else
-                       nvmet_rdma_put_rsp(rsp);
-               spin_unlock_irqrestore(&queue->state_lock, flags);
+       if (unlikely(queue->state != NVMET_RDMA_Q_LIVE) &&
+           nvmet_rdma_recv_not_live(queue, rsp))
                return;
-       }
 
        nvmet_rdma_handle_command(queue, rsp);
 }