[PATCH] NVME:target:rdma fix bug nonemtpy wait list of queue causing kernel panic

From: jackygam2001
Date: Mon Nov 16 2020 - 03:15:37 EST


in our lab, when target is processing queue's connection
(queue->state==NVMET_RDMA_Q_CONNECTING), and the host sends
a command which will be put into wait list to target,
Before the command was processed, the host sent a discconnect
command to target, then the target will process disconnect command,
it will schedule a kworker to free the pre allocatedrsps array, and
it will cause kernel panic, because some items of rsps are in wait list.

Signed-off-by: jackygam2001 <jacky_gam_2001@xxxxxxx>
---
drivers/nvme/target/rdma.c | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)

diff --git a/drivers/nvme/target/rdma.c b/drivers/nvme/target/rdma.c
index ae6620489457..c6c892a43f68 100644
--- a/drivers/nvme/target/rdma.c
+++ b/drivers/nvme/target/rdma.c
@@ -1335,6 +1335,35 @@ static void nvmet_rdma_destroy_queue_ib(struct nvmet_rdma_queue *queue)
queue->send_queue_size + 1);
}

+static void nvmet_rdma_destroy_queue_wait_list(struct nvmet_rdma_queue *queue)
+{
+ unsigned long flags;
+ struct nvmet_rdma_rsp *rsp;
+
+ spin_lock_irqsave(&queue->state_lock, flags);
+ while (!list_empty(&queue->rsp_wait_list)) {
+ rsp = list_first_entry(&queue->rsp_wait_list,
+ struct nvmet_rdma_rsp, wait_list);
+ list_del(&rsp->wait_list);
+ nvmet_rdma_put_rsp(rsp);
+ }
+ spin_unlock_irqrestore(&queue->state_lock, flags);
+}
+
+static void nvmet_rdma_destroy_queue_wr_wait_list(struct nvmet_rdma_queue *queue)
+{
+ struct nvmet_rdma_rsp *rsp;
+
+ spin_lock(&queue->rsp_wr_wait_lock);
+ while (!list_empty(&queue->rsp_wr_wait_list)) {
+ rsp = list_first_entry(&queue->rsp_wr_wait_list,
+ struct nvmet_rdma_rsp, wait_list);
+ list_del(&rsp->wait_list);
+ nvmet_rdma_put_rsp(rsp);
+ }
+ spin_unlock(&queue->rsp_wr_wait_lock);
+}
+
static void nvmet_rdma_free_queue(struct nvmet_rdma_queue *queue)
{
pr_debug("freeing queue %d\n", queue->idx);
@@ -1347,6 +1376,8 @@ static void nvmet_rdma_free_queue(struct nvmet_rdma_queue *queue)
queue->recv_queue_size,
!queue->host_qid);
}
+ nvmet_rdma_destroy_queue_wr_wait_list(queue);
+ nvmet_rdma_destroy_queue_wait_list(queue);
nvmet_rdma_free_rsps(queue);
ida_simple_remove(&nvmet_rdma_queue_ida, queue->idx);
kfree(queue);
--
2.17.1