From 304792abd89595be9e49e22921e076715cf978bc Mon Sep 17 00:00:00 2001 From: Krunal Soni Date: Thu, 28 Jun 2018 14:18:30 -0700 Subject: [PATCH] qcacmn: Fix peer ref counting leak while finding and deleting peer There are two instances where peer ref counting leak has been observed 1) When DUT-STA is connected to AP1 and if it tries to roam to AP2 or to same AP1 then as part of roam sync indication, it needs to delete the old peer and create the new peer. Deletion of old peer is happening through force delete operation which is No-OP for some of the platforms. In such cases, do normal peer delete operation, so that peer ref count can be deleted properly. 2) When looking up peer by its associated vdev, driver goes one by one all peers and check their associated vdev and compare with given vdev. If it doesn't find the match then it returns without releasing the reference count. CRs-Fixed: 2263607 Change-Id: Ibcad72943258e531e29498961d3d7e692fd05b36 --- dp/inc/cdp_txrx_peer_ops.h | 27 +++++++++++++++++++++++++++ dp/wifi3.0/dp_peer.c | 4 +++- dp/wifi3.0/dp_rx_defrag.c | 10 ++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) diff --git a/dp/inc/cdp_txrx_peer_ops.h b/dp/inc/cdp_txrx_peer_ops.h index 164330befcc8..81a74a62677f 100644 --- a/dp/inc/cdp_txrx_peer_ops.h +++ b/dp/inc/cdp_txrx_peer_ops.h @@ -721,4 +721,31 @@ static inline void cdp_peer_detach_force_delete(ol_txrx_soc_handle soc, return; } + +/** + * is_cdp_peer_detach_force_delete_supported() - To check if force delete + * operation is supported + * @soc: pointer to SOC handle + * + * Some of the platforms support force delete operation and some of them + * don't. This API returns true if API which handles force delete operation + * is registered and false otherwise. + * + * Return: true if API which handles force delete operation is registered + * false in all other cases + */ +static inline bool +is_cdp_peer_detach_force_delete_supported(ol_txrx_soc_handle soc) +{ + if (!soc || !soc->ops || !soc->ops->peer_ops) { + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_FATAL, + "%s invalid instance", __func__); + return false; + } + + if (soc->ops->peer_ops->peer_detach_force_delete) + return true; + + return false; +} #endif /* _CDP_TXRX_PEER_H_ */ diff --git a/dp/wifi3.0/dp_peer.c b/dp/wifi3.0/dp_peer.c index 7c77dac9fc1d..ca2e9ba6ccd2 100644 --- a/dp/wifi3.0/dp_peer.c +++ b/dp/wifi3.0/dp_peer.c @@ -1984,8 +1984,10 @@ void *dp_find_peer_by_addr_and_vdev(struct cdp_pdev *pdev_handle, if (!peer) return NULL; - if (peer->vdev != vdev) + if (peer->vdev != vdev) { + qdf_atomic_dec(&peer->ref_cnt); return NULL; + } *local_id = peer->local_id; DP_TRACE(INFO, "peer %pK vdev %pK local id %d", peer, vdev, *local_id); diff --git a/dp/wifi3.0/dp_rx_defrag.c b/dp/wifi3.0/dp_rx_defrag.c index 750472568855..26f43e25e29f 100644 --- a/dp/wifi3.0/dp_rx_defrag.c +++ b/dp/wifi3.0/dp_rx_defrag.c @@ -1270,6 +1270,16 @@ void dp_rx_defrag_cleanup(struct dp_peer *peer, unsigned tid) struct dp_rx_reorder_array_elem *rx_reorder_array_elem = peer->rx_tid[tid].array; + if (!rx_reorder_array_elem) { + /* + * if this condition is hit then somebody + * must have reset this pointer to NULL. + * array pointer usually points to base variable + * of TID queue structure: "struct dp_rx_tid" + */ + QDF_ASSERT(0); + return; + } /* Free up nbufs */ dp_rx_defrag_frames_free(rx_reorder_array_elem->head);