From: Chao Leng <lengc...@huawei.com>

[ Upstream commit 9ebbfe495ecd2e51bc92ac21ed5817c3b9e223ce ]

Each name space has a request queue, if complete request long time,
multi request queues may have time out requests at the same time,
nvme_tcp_timeout will execute concurrently. Multi requests in different
request queues may be queued in the same tcp queue, multi
nvme_tcp_timeout may call nvme_tcp_stop_queue at the same time.
The first nvme_tcp_stop_queue will clear NVME_TCP_Q_LIVE and continue
stopping the tcp queue(cancel io_work), but the others check
NVME_TCP_Q_LIVE is already cleared, and then directly complete the
requests, complete request before the io work is completely canceled may
lead to a use-after-free condition.
Add a multex lock to serialize nvme_tcp_stop_queue.

Signed-off-by: Chao Leng <lengc...@huawei.com>
Signed-off-by: Christoph Hellwig <h...@lst.de>
Signed-off-by: Sasha Levin <sas...@kernel.org>
---
 drivers/nvme/host/tcp.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c
index 81db2331f6d78..6487b7897d1fb 100644
--- a/drivers/nvme/host/tcp.c
+++ b/drivers/nvme/host/tcp.c
@@ -76,6 +76,7 @@ struct nvme_tcp_queue {
        struct work_struct      io_work;
        int                     io_cpu;
 
+       struct mutex            queue_lock;
        struct mutex            send_mutex;
        struct llist_head       req_list;
        struct list_head        send_list;
@@ -1219,6 +1220,7 @@ static void nvme_tcp_free_queue(struct nvme_ctrl *nctrl, 
int qid)
 
        sock_release(queue->sock);
        kfree(queue->pdu);
+       mutex_destroy(&queue->queue_lock);
 }
 
 static int nvme_tcp_init_connection(struct nvme_tcp_queue *queue)
@@ -1380,6 +1382,7 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl,
        struct nvme_tcp_queue *queue = &ctrl->queues[qid];
        int ret, rcv_pdu_size;
 
+       mutex_init(&queue->queue_lock);
        queue->ctrl = ctrl;
        init_llist_head(&queue->req_list);
        INIT_LIST_HEAD(&queue->send_list);
@@ -1398,7 +1401,7 @@ static int nvme_tcp_alloc_queue(struct nvme_ctrl *nctrl,
        if (ret) {
                dev_err(nctrl->device,
                        "failed to create socket: %d\n", ret);
-               return ret;
+               goto err_destroy_mutex;
        }
 
        /* Single syn retry */
@@ -1507,6 +1510,8 @@ err_crypto:
 err_sock:
        sock_release(queue->sock);
        queue->sock = NULL;
+err_destroy_mutex:
+       mutex_destroy(&queue->queue_lock);
        return ret;
 }
 
@@ -1534,9 +1539,10 @@ static void nvme_tcp_stop_queue(struct nvme_ctrl *nctrl, 
int qid)
        struct nvme_tcp_ctrl *ctrl = to_tcp_ctrl(nctrl);
        struct nvme_tcp_queue *queue = &ctrl->queues[qid];
 
-       if (!test_and_clear_bit(NVME_TCP_Q_LIVE, &queue->flags))
-               return;
-       __nvme_tcp_stop_queue(queue);
+       mutex_lock(&queue->queue_lock);
+       if (test_and_clear_bit(NVME_TCP_Q_LIVE, &queue->flags))
+               __nvme_tcp_stop_queue(queue);
+       mutex_unlock(&queue->queue_lock);
 }
 
 static int nvme_tcp_start_queue(struct nvme_ctrl *nctrl, int idx)
-- 
2.27.0



Reply via email to