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
This commit is contained in:
Krunal Soni 2018-09-21 13:46:33 -07:00 • committed by nshrivas
commit a5211f26a2

View file

@ -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);
}