| From e5aa3e198ca4ed603c1d6af66dc5128c13de5f05 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 20 Oct 2023 16:33:21 +0100 |
| Subject: crypto: qat - fix deadlock in backlog processing |
| |
| From: Giovanni Cabiddu <giovanni.cabiddu@intel.com> |
| |
| [ Upstream commit 203b01001c4d741205b9c329acddc5193ed56fbd ] |
| |
| If a request has the flag CRYPTO_TFM_REQ_MAY_BACKLOG set, the function |
| qat_alg_send_message_maybacklog(), enqueues it in a backlog list if |
| either (1) there is already at least one request in the backlog list, or |
| (2) the HW ring is nearly full or (3) the enqueue to the HW ring fails. |
| If an interrupt occurs right before the lock in qat_alg_backlog_req() is |
| taken and the backlog queue is being emptied, then there is no request |
| in the HW queues that can trigger a subsequent interrupt that can clear |
| the backlog queue. In addition subsequent requests are enqueued to the |
| backlog list and not sent to the hardware. |
| |
| Fix it by holding the lock while taking the decision if the request |
| needs to be included in the backlog queue or not. This synchronizes the |
| flow with the interrupt handler that drains the backlog queue. |
| |
| For performance reasons, the logic has been changed to try to enqueue |
| first without holding the lock. |
| |
| Fixes: 386823839732 ("crypto: qat - add backlog mechanism") |
| Reported-by: Mikulas Patocka <mpatocka@redhat.com> |
| Closes: https://lore.kernel.org/all/af9581e2-58f9-cc19-428f-6f18f1f83d54@redhat.com/T/ |
| Signed-off-by: Giovanni Cabiddu <giovanni.cabiddu@intel.com> |
| Reviewed-by: Mikulas Patocka <mpatocka@redhat.com> |
| Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/crypto/qat/qat_common/qat_algs_send.c | 46 ++++++++++--------- |
| 1 file changed, 25 insertions(+), 21 deletions(-) |
| |
| diff --git a/drivers/crypto/qat/qat_common/qat_algs_send.c b/drivers/crypto/qat/qat_common/qat_algs_send.c |
| index ff5b4347f7831..607ed88f4b197 100644 |
| --- a/drivers/crypto/qat/qat_common/qat_algs_send.c |
| +++ b/drivers/crypto/qat/qat_common/qat_algs_send.c |
| @@ -39,40 +39,44 @@ void qat_alg_send_backlog(struct qat_instance_backlog *backlog) |
| spin_unlock_bh(&backlog->lock); |
| } |
| |
| -static void qat_alg_backlog_req(struct qat_alg_req *req, |
| - struct qat_instance_backlog *backlog) |
| -{ |
| - INIT_LIST_HEAD(&req->list); |
| - |
| - spin_lock_bh(&backlog->lock); |
| - list_add_tail(&req->list, &backlog->list); |
| - spin_unlock_bh(&backlog->lock); |
| -} |
| - |
| -static int qat_alg_send_message_maybacklog(struct qat_alg_req *req) |
| +static bool qat_alg_try_enqueue(struct qat_alg_req *req) |
| { |
| struct qat_instance_backlog *backlog = req->backlog; |
| struct adf_etr_ring_data *tx_ring = req->tx_ring; |
| u32 *fw_req = req->fw_req; |
| |
| - /* If any request is already backlogged, then add to backlog list */ |
| + /* Check if any request is already backlogged */ |
| if (!list_empty(&backlog->list)) |
| - goto enqueue; |
| + return false; |
| |
| - /* If ring is nearly full, then add to backlog list */ |
| + /* Check if ring is nearly full */ |
| if (adf_ring_nearly_full(tx_ring)) |
| - goto enqueue; |
| + return false; |
| |
| - /* If adding request to HW ring fails, then add to backlog list */ |
| + /* Try to enqueue to HW ring */ |
| if (adf_send_message(tx_ring, fw_req)) |
| - goto enqueue; |
| + return false; |
| |
| - return -EINPROGRESS; |
| + return true; |
| +} |
| |
| -enqueue: |
| - qat_alg_backlog_req(req, backlog); |
| |
| - return -EBUSY; |
| +static int qat_alg_send_message_maybacklog(struct qat_alg_req *req) |
| +{ |
| + struct qat_instance_backlog *backlog = req->backlog; |
| + int ret = -EINPROGRESS; |
| + |
| + if (qat_alg_try_enqueue(req)) |
| + return ret; |
| + |
| + spin_lock_bh(&backlog->lock); |
| + if (!qat_alg_try_enqueue(req)) { |
| + list_add_tail(&req->list, &backlog->list); |
| + ret = -EBUSY; |
| + } |
| + spin_unlock_bh(&backlog->lock); |
| + |
| + return ret; |
| } |
| |
| int qat_alg_send_message(struct qat_alg_req *req) |
| -- |
| 2.42.0 |
| |