From a5211f26a23566e5b2f756ebbd2b356ea76a7ad3 Mon Sep 17 00:00:00 2001 From: Krunal Soni Date: Fri, 21 Sep 2018 13:46:33 -0700 Subject: [PATCH] qcacmn: Fix deadlock in dp_delete_pending_vdev() API In Data-path, dp_delete_pending_vdev() acquires pdev->vdev_list_lock in the beginning and calls dp_tx_flow_pool_unmap_handler() API which further try to acquires same vdev_list_lock. This situation causes deadlock. To fix the situation, re-factor dp_delete_pending_vdev() API. CRs-Fixed: 2320629 Change-Id: Ie565ea07f396d1987fbe45173b49b2f50bb76cea --- dp/wifi3.0/dp_main.c | 67 ++++++++++++++++++++++++++------------------ 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/dp/wifi3.0/dp_main.c b/dp/wifi3.0/dp_main.c index e5eda38e4576..715ecc4b80dc 100644 --- a/dp/wifi3.0/dp_main.c +++ b/dp/wifi3.0/dp_main.c @@ -4648,40 +4648,44 @@ static void dp_reset_and_release_peer_mem(struct dp_soc *soc, qdf_mem_free(peer); } -static void dp_delete_pending_vdev(struct dp_pdev *pdev, uint32_t vdev_id) +/** + * dp_delete_pending_vdev() - check and process vdev delete + * @pdev: DP specific pdev pointer + * @vdev: DP specific vdev pointer + * @vdev_id: vdev id corresponding to vdev + * + * This API does following: + * 1) It releases tx flow pools buffers as vdev is + * going down and no peers are associated. + * 2) It also detaches vdev before cleaning vdev (struct dp_vdev) memory + */ +static void dp_delete_pending_vdev(struct dp_pdev *pdev, struct dp_vdev *vdev, + uint8_t vdev_id) { - struct dp_vdev *vdev = NULL; ol_txrx_vdev_delete_cb vdev_delete_cb = NULL; void *vdev_delete_context = NULL; - qdf_spin_lock_bh(&pdev->vdev_list_lock); - TAILQ_FOREACH(vdev, &pdev->vdev_list, vdev_list_elem) { - if (vdev->vdev_id == vdev_id) - break; - } - if (!vdev) { - QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, - "vdev is NULL"); - } else if (vdev->delete.pending) { - vdev_delete_cb = vdev->delete.callback; - vdev_delete_context = vdev->delete.context; + vdev_delete_cb = vdev->delete.callback; + vdev_delete_context = vdev->delete.context; - QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO_HIGH, - FL("deleting vdev object %pK (%pM)- its last peer is done"), - vdev, vdev->mac_addr.raw); - /* all peers are gone, go ahead and delete it */ - dp_tx_flow_pool_unmap_handler(pdev, vdev_id, - FLOW_TYPE_VDEV, vdev_id); - dp_tx_vdev_detach(vdev); - TAILQ_REMOVE(&pdev->vdev_list, vdev, vdev_list_elem); - QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO_HIGH, - FL("deleting vdev object %pK (%pM)"), - vdev, vdev->mac_addr.raw); - qdf_mem_free(vdev); - vdev = NULL; - } + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("deleting vdev object %pK (%pM)- its last peer is done"), + vdev, vdev->mac_addr.raw); + /* all peers are gone, go ahead and delete it */ + dp_tx_flow_pool_unmap_handler(pdev, vdev_id, + FLOW_TYPE_VDEV, vdev_id); + dp_tx_vdev_detach(vdev); + + qdf_spin_lock_bh(&pdev->vdev_list_lock); + TAILQ_REMOVE(&pdev->vdev_list, vdev, vdev_list_elem); qdf_spin_unlock_bh(&pdev->vdev_list_lock); + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO_HIGH, + FL("deleting vdev object %pK (%pM)"), + vdev, vdev->mac_addr.raw); + qdf_mem_free(vdev); + vdev = NULL; + if (vdev_delete_cb) vdev_delete_cb(vdev_delete_context); } @@ -4701,6 +4705,7 @@ void dp_peer_unref_delete(void *peer_handle) int found = 0; uint16_t peer_id; uint16_t vdev_id; + bool delete_vdev; QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_HIGH, "%s: peer %pK ref_cnt(before decrement): %d\n", __func__, @@ -4762,6 +4767,11 @@ void dp_peer_unref_delete(void *peer_handle) /* check whether the parent vdev has no peers left */ if (TAILQ_EMPTY(&vdev->peer_list)) { + /* + * capture vdev delete pending flag's status + * while holding peer_ref_mutex lock + */ + delete_vdev = vdev->delete.pending; /* * Now that there are no references to the peer, we can * release the peer reference lock. @@ -4771,7 +4781,8 @@ void dp_peer_unref_delete(void *peer_handle) * Check if the parent vdev was waiting for its peers * to be deleted, in order for it to be deleted too. */ - dp_delete_pending_vdev(pdev, vdev_id); + if (delete_vdev) + dp_delete_pending_vdev(pdev, vdev, vdev_id); } else { qdf_spin_unlock_bh(&soc->peer_ref_mutex); }