Compare commits

...

9 Commits

Author SHA1 Message Date
Venkat Chimata
233cc97b99 Merge branch 'staging-WIFI-15202' of https://github.com/Telecominfraproject/wlan-ap into staging-WIFI-15202 2025-11-27 02:53:28 +05:30
Venkat Chimata
98c177928e ath11k: Fix memory leak in ath11k_qmi_driver_event_work
The buffer pointed to by event is not freed in case
ATH11K_FLAG_UNREGISTERING bit is set, resulting in
memory leak, so fix it.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1

Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices")
Signed-off-by: Baochen Qiang <bqiang@codeaurora.org>
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20210913180246.193388-4-jouni@codeaurora.org

Signed-off-by: Venkat Chimata <venkat@nearhop.com>
2025-11-27 02:39:35 +05:30
Venkat Chimata
c4bdc763d1 wifi: ath11k: Fix memory leak in ath11k_peer_rx_frag_setup
crypto_alloc_shash() allocates resources, which should be released by
crypto_free_shash(). When ath11k_peer_find() fails, there has memory
leak. Add missing crypto_free_shash() to fix this.

Fixes: 243874c64c81 ("ath11k: handle RX fragments")
Signed-off-by: Miaoqian Lin <linmq006@gmail.com>
Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20230102081142.3937570-1-linmq006@gmail.com

Signed-off-by: Venkat Chimata <venkat@nearhop.com>
2025-11-27 02:38:58 +05:30
Oleksandr Mazur
fc5f00817d ath11k: fix memory leak when peer lookup fails in dp rx path
In the DP RX path, fast_rx is set to true by default.
Currently, if peer lookup fails in ath11k_dp_rx_h_mpdu(), the SKB is not sent
to the network stack or mac80211 because fast_rx remains true. This results
in a memory leak.

Fix this by setting fast_rx = false when peer lookup fails in
ath11k_dp_rx_h_mpdu(), ensuring the SKB is properly delivered to mac80211
via ath11k_dp_rx_deliver_msdu().

Fixes: WIFI-15202

Signed-off-by: Marek Kwaczynski <marek@shasta.cloud>
2025-11-26 17:05:07 +01:00
Venkat Chimata
a3ac7deced This is a work in progress patch.
num_peers was decremented at one place and list_del was called at another place.
There are chances that they miss sync.

So reset num_peers when out of sync

Signed-off-by: Venkat Chimata <venkat@nearhop.com>
2025-11-25 21:05:22 +05:30
Venkat Chimata
50eee0ef3c Revert "ath11k: add rhashtable-based peer count recovery to fix peer"
This reverts commit c026b2d448.
2025-11-25 20:30:45 +05:30
John Crispin
d359213bf0 ath11k: fix num_peers counter corruption and add debug logging
The num_peers counter becomes corrupted during peer deletion due to race
conditions between ath11k_peer_delete() and ath11k_peer_unmap_event().
The firmware may or may not send unmap events, and the timing varies,
causing the counter to either leak (increment without decrement) or
underflow (double decrement).

Root causes:
1. ath11k_peer_delete() doesn't decrement num_peers, relying on
   ath11k_peer_unmap_event() to do it
2. Firmware sometimes doesn't send unmap events, leaving num_peers
   inflated
3. When unmap events do arrive, timing races with ath11k_peer_delete()
   can cause missed decrements
4. Cleanup paths may double-decrement if delete_in_progress not checked
5. num_peers modified outside proper locking in some paths

This fix:
- Moves num_peers decrement into ath11k_peer_delete() after successful
  peer deletion wait, ensuring exactly one decrement per deletion
- Handles both cases: peer removed by unmap event, or peer still in list
- Removes num_peers decrement from ath11k_peer_unmap_event() to prevent
  double-decrement when unmap event arrives
- Adds ath11k_dp_peer_cleanup() call before ath11k_peer_delete() in
  roaming path to ensure datapath structures properly cleaned up
- Adds delete_in_progress checks in cleanup paths to prevent
  double-delete
- Ensures all num_peers modifications happen under base_lock
- Adds comprehensive debug logging to track num_peers throughout peer
  lifecycle

Signed-off-by: Arif Alam <arif.alam@netexperience.com>
Signed-off-by: John Crispin <john@phrozen.org>
2025-11-25 13:03:40 +05:30
Venkat Chimata
c026b2d448 ath11k: add rhashtable-based peer count recovery to fix peer
resource desynchronization

Description:

This change introduces a helper function ath11k_get_peer_count() that walks the
 peer rhashtable and returns the total number of peer entries currently tracked
  by the driver. The function uses rhashtable_walk_*() APIs and handles -EAGAIN
to correctly iterate even when the hash table is resized during traversal.

During roaming and frequent peer create/delete sequences, the driver’s internal
peer counter (ar->num_peers) can become desynchronized from the actual number
of peer entries stored in the rhashtable. This mismatch may occur when peer
delete operations timeout or when the firmware and driver get out of sync,
resulting in the driver incorrectly reporting peer exhaustion:

failed to create peer due to insufficient peer entry resource in firmware

When ar->num_peers becomes negative or exceeds ar->max_num_peers - 1, the
driver now logs a warning and recomputes the peer count directly from the
rhashtable. If the recalculated rhashtable count is within limits,
ar->num_peers is corrected. If the recalculated count still exceeds the
firmware peer limit, peer creation is aborted with -ENOBUFS.

This recovery mechanism prevents permanent peer allocation failures caused
 by stale or leaked peer entries and helps keep the driver’s peer accounting
 consistent with the actual peer table state. It is especially useful in
 environments with rapid roaming or random-MAC clients where peer churn is high.

This change also changes the way num_peers is decremented. Per this change,
num_peers is decremented only during the peer is deleted from the list ie
list_del

Signed-off-by: Venkat Chimata <venkat@nearhop.com>
2025-11-24 00:00:03 +05:30
Venkat Chimata
531a0c0b97 WIFI-14998: wifi: ap: mitigate peer-delete WMI timeout to reduce blind period & prevent peer leaks
1. When a connected client roams to another AP, the AP is trying to delete the peer
   but for some reason the WMI command times out and while driver is waiting for
   the response, we observed that the AP doesn't respond to any frames from STA
   (probe requests, authentication etc) and once the response times out (3seconds default)
   then AP starts responding to the older requets but client has already connected to
   another AP. As the root cause for the response timing out is in the FW, we added
   a WAR to reduce the timeout to minimize this blind period, with this AP responds
   after 100ms and client connects successfully. And 100ms timeout is also reasonable
   for this internal operation.
2. In case of peer deletion timeout, the driver peer database is not cleared, so,
   if this happens often (which it is) then eventually we hit the max peers in the
   driver and all subsequent operations fail, so, in case of timeout ignore the failure
   and proceed with driver peer database cleanup.

Signed-off-by: Venkat Chimata <venkat@nearhop.com>
2025-11-23 19:10:01 +05:30
6 changed files with 604 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
Index: backports-20210222_001-5.4.164-b157d2276/drivers/net/wireless/ath/ath11k/dp_rx.c
===================================================================
--- backports-20210222_001-5.4.164-b157d2276.orig/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ backports-20210222_001-5.4.164-b157d2276/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -2847,8 +2847,6 @@ static void ath11k_dp_rx_h_mpdu(struct a
}
}
- *fast_rx = false;
-
if (rxcb->is_mcbc)
enctype = peer->sec_type_grp;
else
@@ -2858,6 +2856,8 @@ static void ath11k_dp_rx_h_mpdu(struct a
}
spin_unlock_bh(&ar->ab->base_lock);
+ *fast_rx = false;
+
rx_attention = ath11k_dp_rx_get_attention(ar->ab, rx_desc);
err_bitmap = ath11k_dp_rx_h_attn_mpdu_err(rx_attention);
if (enctype != HAL_ENCRYPT_TYPE_OPEN && !err_bitmap)

View File

@@ -0,0 +1,53 @@
From 375d0d25e6c02991392e44956c81cbac84909f49 Mon Sep 17 00:00:00 2001
From: Venkat Chimata <venkat@nearhop.com>
Date: Thu, 4 Sep 2025 00:09:17 +0530
Subject: [PATCH] wifi: ap: mitigate peer-delete WMI timeout to reduce blind
period & prevent peer leaks
1. When a connected client roams to another AP, the AP is trying to delete the peer
but for some reason the WMI command times out and while driver is waiting for
the response, we observed that the AP doesn't respond to any frames from STA
(probe requests, authentication etc) and once the response times out (3seconds default)
then AP starts responding to the older requets but client has already connected to
another AP. As the root cause for the response timing out is in the FW, we added
a WAR to reduce the timeout to minimize this blind period, with this AP responds
after 100ms and client connects successfully. And 100ms timeout is also reasonable
for this internal operation.
2. In case of peer deletion timeout, the driver peer database is not cleared, so,
if this happens often (which it is) then eventually we hit the max peers in the
driver and all subsequent operations fail, so, in case of timeout ignore the failure
and proceed with driver peer database cleanup.
Signed-off-by: Venkat Chimata <venkat@nearhop.com>
---
drivers/net/wireless/ath/ath11k/peer.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/ath/ath11k/peer.c b/drivers/net/wireless/ath/ath11k/peer.c
index 1907067..aefc6ba 100644
--- a/drivers/net/wireless/ath/ath11k/peer.c
+++ b/drivers/net/wireless/ath/ath11k/peer.c
@@ -771,7 +771,7 @@ int ath11k_wait_for_peer_delete_done(struct ath11k *ar, u32 vdev_id,
}
time_left = wait_for_completion_timeout(&ar->peer_delete_done,
- 3 * HZ);
+ 100 * HZ / 1000);
if (time_left == 0) {
ath11k_warn(ar->ab, "Timeout in receiving peer delete response\n");
return -ETIMEDOUT;
@@ -857,7 +857,10 @@ int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr)
}
ret = ath11k_wait_for_peer_delete_done(ar, vdev_id, addr);
- if (ret)
+ /* WAR: For the timeout case, proceed to delete the peer anyway, as FW is
+ * still functional, without this, driver ends up hitting max peers
+ */
+ if (ret && ret != -ETIMEDOUT)
return ret;
ATH11K_MEMORY_STATS_DEC(ar->ab, per_peer_object,
--
2.34.1

View File

@@ -0,0 +1,193 @@
From: John Crispin <john@phrozen.org>
Date: Thu, 2 Oct 2025 09:00:00 +0000
Subject: [PATCH] ath11k: fix num_peers counter corruption and add debug
logging
The num_peers counter becomes corrupted during peer deletion due to race
conditions between ath11k_peer_delete() and ath11k_peer_unmap_event().
The firmware may or may not send unmap events, and the timing varies,
causing the counter to either leak (increment without decrement) or
underflow (double decrement).
Root causes:
1. ath11k_peer_delete() doesn't decrement num_peers, relying on
ath11k_peer_unmap_event() to do it
2. Firmware sometimes doesn't send unmap events, leaving num_peers
inflated
3. When unmap events do arrive, timing races with ath11k_peer_delete()
can cause missed decrements
4. Cleanup paths may double-decrement if delete_in_progress not checked
5. num_peers modified outside proper locking in some paths
This fix:
- Moves num_peers decrement into ath11k_peer_delete() after successful
peer deletion wait, ensuring exactly one decrement per deletion
- Handles both cases: peer removed by unmap event, or peer still in list
- Removes num_peers decrement from ath11k_peer_unmap_event() to prevent
double-decrement when unmap event arrives
- Adds ath11k_dp_peer_cleanup() call before ath11k_peer_delete() in
roaming path to ensure datapath structures properly cleaned up
- Adds delete_in_progress checks in cleanup paths to prevent
double-delete
- Ensures all num_peers modifications happen under base_lock
- Adds comprehensive debug logging to track num_peers throughout peer
lifecycle
Signed-off-by: Arif Alam <arif.alam@netexperience.com>
Signed-off-by: John Crispin <john@phrozen.org>
---
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -5742,14 +5742,22 @@ static int ath11k_mac_op_sta_state(struc
mutex_lock(&ar->ab->tbl_mtx_lock);
spin_lock_bh(&ar->ab->base_lock);
peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr);
- if (peer && peer->sta == sta) {
+ /* Skip if peer deletion already in progress to prevent
+ * double-delete and num_peers underflow
+ */
+ if (peer && peer->sta == sta && !peer->delete_in_progress) {
ath11k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n",
vif->addr, arvif->vdev_id);
ath11k_peer_rhash_delete(ar->ab, peer);
peer->sta = NULL;
+ /* num_peers decrement now happens under base_lock when
+ * peer is actually removed from list
+ */
list_del(&peer->list);
kfree(peer);
ar->num_peers--;
+ ath11k_dbg(ar->ab, ATH11K_DBG_PEER, "%s peer deleted %pM vdev_id: %d num_peers: %d\n",
+ __func__, sta->addr, arvif->vdev_id, ar->num_peers);
}
spin_unlock_bh(&ar->ab->base_lock);
mutex_unlock(&ar->ab->tbl_mtx_lock);
@@ -7847,6 +7855,8 @@ err_peer_del:
goto err_keyid;
ar->num_peers--;
+ ath11k_dbg(ar->ab, ATH11K_DBG_PEER, "%s vif peer deleted %pM vdev_id: %d num_peers: %d\n",
+ __func__, vif->addr, arvif->vdev_id, ar->num_peers);
}
err_vdev_del:
--- a/drivers/net/wireless/ath/ath11k/peer.c
+++ b/drivers/net/wireless/ath/ath11k/peer.c
@@ -461,6 +461,9 @@ void ath11k_peer_unmap_event(struct ath1
ath11k_dbg(ab, ATH11K_DBG_PEER, "peer unmap vdev %d peer %pM id %d\n",
peer->vdev_id, peer->addr, peer_id);
+ /* Don't decrement num_peers here - it's already decremented in
+ * ath11k_peer_delete() after successful wait. Just clean up the peer.
+ */
list_del(&peer->list);
kfree(peer);
wake_up(&ab->peer_mapping_wq);
@@ -726,6 +729,10 @@ void ath11k_peer_cleanup(struct ath11k *
if (peer->vdev_id != vdev_id)
continue;
+ /* Skip peers that are being deleted to prevent double-free */
+ if (peer->delete_in_progress)
+ continue;
+
ath11k_warn(ab, "removing stale peer %pM from vdev_id %d\n",
peer->addr, vdev_id);
@@ -743,7 +750,10 @@ void ath11k_peer_cleanup(struct ath11k *
ath11k_peer_rhash_delete(ab, peer);
list_del(&peer->list);
kfree(peer);
+ /* num_peers decrement happens here under base_lock */
ar->num_peers--;
+ ath11k_dbg(ar->ab, ATH11K_DBG_PEER, "%s peer cleanup %pM vdev_id: %d num_peers: %d\n",
+ __func__, peer->addr, vdev_id, ar->num_peers);
}
spin_unlock_bh(&ab->base_lock);
@@ -824,6 +834,12 @@ int ath11k_peer_delete(struct ath11k *ar
#ifdef CPTCFG_ATH11K_NSS_SUPPORT
peer->delete_in_progress = true;
+#else
+ if (peer)
+ peer->delete_in_progress = true;
+#endif
+
+#ifdef CPTCFG_ATH11K_NSS_SUPPORT
if (peer->self_ast_entry) {
ath11k_peer_del_ast(ar, peer->self_ast_entry);
peer->self_ast_entry = NULL;
@@ -863,10 +879,51 @@ int ath11k_peer_delete(struct ath11k *ar
if (ret && ret != -ETIMEDOUT)
return ret;
- ATH11K_MEMORY_STATS_DEC(ar->ab, per_peer_object,
- sizeof(struct ath11k_peer));
+ /* If timeout occurred, manually remove peer from list since firmware
+ * won't send unmap event. This prevents peer leaks and num_peers corruption.
+ */
+ if (ret == -ETIMEDOUT) {
+ ath11k_warn(ar->ab, "peer delete timeout %pM vdev %d, manually cleaning up\n",
+ addr, vdev_id);
- ar->num_peers--;
+ mutex_lock(&ar->ab->tbl_mtx_lock);
+ spin_lock_bh(&ar->ab->base_lock);
+ peer = ath11k_peer_find(ar->ab, vdev_id, addr);
+ if (peer) {
+ list_del(&peer->list);
+ kfree(peer);
+ ar->num_peers--;
+ ath11k_dbg(ar->ab, ATH11K_DBG_PEER,
+ "%s peer deleted (timeout) %pM vdev_id: %d num_peers: %d\n",
+ __func__, addr, vdev_id, ar->num_peers);
+ }
+ spin_unlock_bh(&ar->ab->base_lock);
+ mutex_unlock(&ar->ab->tbl_mtx_lock);
+ } else {
+ /* Normal path - but firmware may not send unmap event, so decrement here
+ * after successful peer deletion wait
+ */
+ mutex_lock(&ar->ab->tbl_mtx_lock);
+ spin_lock_bh(&ar->ab->base_lock);
+ peer = ath11k_peer_find(ar->ab, vdev_id, addr);
+ if (peer) {
+ /* Peer still in list - firmware didn't send unmap event yet */
+ list_del(&peer->list);
+ kfree(peer);
+ ar->num_peers--;
+ ath11k_dbg(ar->ab, ATH11K_DBG_PEER,
+ "%s peer deleted (no unmap event) %pM vdev_id: %d num_peers: %d\n",
+ __func__, addr, vdev_id, ar->num_peers);
+ } else {
+ /* Peer already removed by unmap event - still need to decrement */
+ ar->num_peers--;
+ ath11k_dbg(ar->ab, ATH11K_DBG_PEER,
+ "%s peer deleted (via unmap event) %pM vdev_id: %d num_peers: %d\n",
+ __func__, addr, vdev_id, ar->num_peers);
+ }
+ spin_unlock_bh(&ar->ab->base_lock);
+ mutex_unlock(&ar->ab->tbl_mtx_lock);
+ }
return 0;
}
@@ -905,6 +962,7 @@ int ath11k_peer_create(struct ath11k *ar
if (vdev_id == param->vdev_id)
return -EINVAL;
+ ath11k_dp_peer_cleanup(ar, vdev_id, param->peer_addr);
ath11k_peer_delete(ar, vdev_id, param->peer_addr);
}
@@ -970,7 +1028,8 @@ int ath11k_peer_create(struct ath11k *ar
ar->num_peers++;
if (ath11k_mac_sta_level_info(arvif, sta)) {
- ath11k_dbg(ar->ab, ATH11K_DBG_PEER, "peer created %pM\n", param->peer_addr);
+ ath11k_dbg(ar->ab, ATH11K_DBG_PEER, "peer created %pM vdev_id: %d num_peers: %d\n",
+ param->peer_addr, param->vdev_id, ar->num_peers);
peer->peer_logging_enabled = true;
}

View File

@@ -0,0 +1,264 @@
From 625b8692332ee44e514be88400646e900710644b Mon Sep 17 00:00:00 2001
From: Venkat Chimata <venkat@nearhop.com>
Date: Tue, 25 Nov 2025 20:34:34 +0530
Subject: [PATCH] This is a work in progress patch.
num_peers was decremented at one place and list_del was called at another place.
There are chances that they miss sync.
So reset num_peers when out of sync
Signed-off-by: Venkat Chimata <venkat@nearhop.com>
---
drivers/net/wireless/ath/ath11k/mac.c | 39 ++++++++----
drivers/net/wireless/ath/ath11k/peer.c | 82 +++++++++++++++++++++++---
2 files changed, 102 insertions(+), 19 deletions(-)
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index f300c4f..a3eeac8 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -5383,6 +5383,8 @@ static int ath11k_mac_station_add(struct ath11k *ar,
peer_param.peer_addr = sta->addr;
peer_param.peer_type = WMI_PEER_TYPE_DEFAULT;
+ ath11k_warn(ab, "%s: Peer will be added: %pM for VDEV: %d\n", __func__,
+ sta->addr, arvif->vdev_id);
ret = ath11k_peer_create(ar, arvif, sta, &peer_param);
if (ret) {
ath11k_warn(ab, "Failed to add peer: %pM for VDEV: %d\n",
@@ -5457,7 +5459,13 @@ free_tx_stats:
kfree(arsta->wbm_tx_stats);
arsta->wbm_tx_stats = NULL;
free_peer:
- ath11k_peer_delete(ar, arvif->vdev_id, sta->addr);
+ ret = ath11k_peer_delete(ar, arvif->vdev_id, sta->addr);
+ if (ret)
+ ath11k_warn(ar->ab, "%s: Failed to delete peer: %pM for VDEV: %d\n", __func__,
+ sta->addr, arvif->vdev_id);
+ else
+ ath11k_warn(ar->ab, "%s: Removed peer: %pM for VDEV: %d\n", __func__,
+ sta->addr, arvif->vdev_id);
free_rx_stats:
kfree(arsta->rx_stats);
arsta->rx_stats = NULL;
@@ -5735,18 +5743,15 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw,
ath11k_warn(ar->ab, "Failed to delete peer: %pM for VDEV: %d\n",
sta->addr, arvif->vdev_id);
else
- ath11k_dbg(ar->ab, ATH11K_DBG_PEER, "Removed peer: %pM for VDEV: %d\n",
+ ath11k_warn(ar->ab, "%s: Removed peer: %pM for VDEV: %d\n", __func__,
sta->addr, arvif->vdev_id);
ath11k_mac_dec_num_stations(arvif, sta);
mutex_lock(&ar->ab->tbl_mtx_lock);
spin_lock_bh(&ar->ab->base_lock);
peer = ath11k_peer_find(ar->ab, arvif->vdev_id, sta->addr);
- /* Skip if peer deletion already in progress to prevent
- * double-delete and num_peers underflow
- */
- if (peer && peer->sta == sta && !peer->delete_in_progress) {
- ath11k_warn(ar->ab, "Found peer entry %pM n vdev %i after it was supposedly removed\n",
+ if (peer && peer->sta == sta) {
+ ath11k_warn(ar->ab, "%s: Found peer entry %pM n vdev %i after it was supposedly removed\n", __func__,
vif->addr, arvif->vdev_id);
ath11k_peer_rhash_delete(ar->ab, peer);
peer->sta = NULL;
@@ -7746,6 +7751,8 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw,
peer_param.vdev_id = arvif->vdev_id;
peer_param.peer_addr = vif->addr;
peer_param.peer_type = WMI_PEER_TYPE_DEFAULT;
+ ath11k_warn(ab, "%s: Peer will be added: %pM for VDEV: %d\n", __func__,
+ vif->addr, arvif->vdev_id);
ret = ath11k_peer_create(ar, arvif, NULL, &peer_param);
if (ret) {
ath11k_warn(ab, "failed to vdev %d create peer for AP: %d\n",
@@ -7947,8 +7954,11 @@ static void ath11k_mac_op_remove_interface(struct ieee80211_hw *hw,
if (arvif->vdev_type == WMI_VDEV_TYPE_AP) {
ret = ath11k_peer_delete(ar, arvif->vdev_id, vif->addr);
if (ret)
- ath11k_warn(ab, "failed to submit AP self-peer removal on vdev %d: %d\n",
+ ath11k_warn(ab, "%s: failed to submit AP self-peer removal on vdev %d: %d\n", __func__,
arvif->vdev_id, ret);
+ else
+ ath11k_warn(ar->ab, "%s: Removed peer: %pM for VDEV: %d\n", __func__,
+ vif->addr, arvif->vdev_id);
list_for_each_entry_safe(ap_vlan_arvif, tmp, &arvif->ap_vlan_arvifs,
list) {
@@ -9305,6 +9315,8 @@ ath11k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw,
param.peer_type = WMI_PEER_TYPE_DEFAULT;
param.peer_addr = ar->mac_addr;
+ ath11k_warn(ab, "%s: Peer will be added: %pM for VDEV: %d\n", __func__,
+ ar->mac_addr, arvif->vdev_id);
ret = ath11k_peer_create(ar, arvif, NULL, &param);
if (ret) {
ath11k_warn(ab, "failed to create peer after vdev start delay: %d",
@@ -9380,8 +9392,15 @@ ath11k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw,
if (ab->hw_params.vdev_start_delay &&
arvif->vdev_type == WMI_VDEV_TYPE_MONITOR &&
- ath11k_peer_find_by_addr(ab, ar->mac_addr))
- ath11k_peer_delete(ar, arvif->vdev_id, ar->mac_addr);
+ ath11k_peer_find_by_addr(ab, ar->mac_addr)) {
+ ret = ath11k_peer_delete(ar, arvif->vdev_id, ar->mac_addr);
+ if (ret)
+ ath11k_warn(ar->ab, "%s: Failed to delete peer: %pM for VDEV: %d\n", __func__,
+ ar->mac_addr, arvif->vdev_id);
+ else
+ ath11k_warn(ar->ab, "%s: Removed peer: %pM for VDEV: %d\n", __func__,
+ ar->mac_addr, arvif->vdev_id);
+ }
if (arvif->vdev_type == WMI_VDEV_TYPE_MONITOR) {
ret = ath11k_mac_monitor_stop(ar);
diff --git a/drivers/net/wireless/ath/ath11k/peer.c b/drivers/net/wireless/ath/ath11k/peer.c
index 877ea30..9ec297f 100644
--- a/drivers/net/wireless/ath/ath11k/peer.c
+++ b/drivers/net/wireless/ath/ath11k/peer.c
@@ -457,8 +457,8 @@ void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id)
goto exit;
}
- if (peer->peer_logging_enabled)
- ath11k_dbg(ab, ATH11K_DBG_PEER, "peer unmap vdev %d peer %pM id %d\n",
+// if (peer->peer_logging_enabled)
+ ath11k_warn(ab, "peer unmap vdev %d peer %pM id %d\n",
peer->vdev_id, peer->addr, peer_id);
/* Don't decrement num_peers here - it's already decremented in
@@ -466,6 +466,7 @@ void ath11k_peer_unmap_event(struct ath11k_base *ab, u16 peer_id)
*/
list_del(&peer->list);
kfree(peer);
+ ath11k_mac_get_ar_by_vdev_id(ab, peer->vdev_id);
wake_up(&ab->peer_mapping_wq);
exit:
@@ -617,7 +618,14 @@ void ath11k_peer_map_v2_event(struct ath11k_base *ab, u8 vdev_id, u16 peer_id,
peer_free:
spin_unlock_bh(&ab->base_lock);
mutex_lock(&ar->conf_mutex);
- ath11k_peer_delete(ar, vdev_id, mac_addr);
+ ret = ath11k_peer_delete(ar, vdev_id, mac_addr);
+ if (ret)
+ ath11k_warn(ar->ab, "%s: Failed to delete peer: %pM for VDEV: %d\n", __func__,
+ mac_addr, vdev_id);
+ else
+ ath11k_warn(ar->ab, "%s: Removed peer: %pM for VDEV: %d\n", __func__,
+ mac_addr, vdev_id);
+
mutex_unlock(&ar->conf_mutex);
exit:
rcu_read_unlock();
@@ -893,9 +901,16 @@ int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr)
list_del(&peer->list);
kfree(peer);
ar->num_peers--;
- ath11k_dbg(ar->ab, ATH11K_DBG_PEER,
+ ath11k_warn(ar->ab,
"%s peer deleted (timeout) %pM vdev_id: %d num_peers: %d\n",
__func__, addr, vdev_id, ar->num_peers);
+ } else {
+ // Peer already deleted
+ // Who deleted it? Firmware unmap event after the timeout?
+ ar->num_peers--;
+ ath11k_warn(ar->ab,
+ "%s peer already deleted (timeout) %pM vdev_id: %d num_peers: %d\n",
+ __func__, addr, vdev_id, ar->num_peers);
}
spin_unlock_bh(&ar->ab->base_lock);
mutex_unlock(&ar->ab->tbl_mtx_lock);
@@ -911,13 +926,13 @@ int ath11k_peer_delete(struct ath11k *ar, u32 vdev_id, u8 *addr)
list_del(&peer->list);
kfree(peer);
ar->num_peers--;
- ath11k_dbg(ar->ab, ATH11K_DBG_PEER,
+ ath11k_warn(ar->ab,
"%s peer deleted (no unmap event) %pM vdev_id: %d num_peers: %d\n",
__func__, addr, vdev_id, ar->num_peers);
} else {
/* Peer already removed by unmap event - still need to decrement */
ar->num_peers--;
- ath11k_dbg(ar->ab, ATH11K_DBG_PEER,
+ ath11k_warn(ar->ab,
"%s peer deleted (via unmap event) %pM vdev_id: %d num_peers: %d\n",
__func__, addr, vdev_id, ar->num_peers);
}
@@ -933,6 +948,31 @@ static int ath11k_wait_for_peer_created(struct ath11k *ar, int vdev_id, const u8
return ath11k_wait_for_peer_common(ar->ab, vdev_id, addr, true);
}
+static int ath11k_get_peer_count(struct rhashtable *ht)
+{
+ struct rhashtable_iter iter;
+ struct rhash_head *pos;
+ int count = 0;
+
+ rhashtable_walk_enter(ht, &iter);
+ rhashtable_walk_start(&iter);
+
+ while ((pos = rhashtable_walk_next(&iter))) {
+ if (IS_ERR(pos)) {
+ if (PTR_ERR(pos) == -EAGAIN)
+ continue; // retry due to resize
+ break; // some other error
+ }
+ count++;
+ }
+
+ rhashtable_walk_stop(&iter);
+ rhashtable_walk_exit(&iter);
+
+ return count;
+}
+
+
int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif,
struct ieee80211_sta *sta, struct peer_create_params *param)
{
@@ -941,13 +981,37 @@ int ath11k_peer_create(struct ath11k *ar, struct ath11k_vif *arvif,
struct ath11k_sta *arsta;
int ret, fbret;
u8 vdev_id = 0;
+ int rhash_count;
lockdep_assert_held(&ar->conf_mutex);
- if (ar->num_peers > (ar->max_num_peers - 1)) {
+ mutex_lock(&ar->ab->tbl_mtx_lock);
+ spin_lock_bh(&ar->ab->base_lock);
+ rhash_count = ath11k_get_peer_count(ar->ab->rhead_peer_addr);
+ spin_unlock_bh(&ar->ab->base_lock);
+ mutex_unlock(&ar->ab->tbl_mtx_lock);
+ ath11k_warn(ar->ab, "address = %pM rhash_count = %d ar->num_peers = %d "
+ "ar->max_num_peers = %d ar->num_stations = %d\n", param->peer_addr, rhash_count, ar->num_peers, ar->max_num_peers, ar->num_stations);
+ // Check for peer count desynchronization
+ // If num_peers is negative or exceeds max_num_peers -1, recalculate from rhashtable
+ if ((ar->num_peers < 0) || (ar->num_peers > (ar->max_num_peers - 1))) {
+ // This can happen if rhash table and num_peers get out of sync
+ // e.g. during peer delete for some unknown reason
+ // Recalculate num_peers from rhash table
ath11k_warn(ar->ab,
- "failed to create peer due to insufficient peer entry resource in firmware\n");
- return -ENOBUFS;
+ "failed to create peer due to insufficient peer entry resource in firmware ar->num_peers = %d "
+ "ar->max_num_peers = %d ar->num_stations = %d\n", ar->num_peers, ar->max_num_peers, ar->num_stations);
+ mutex_lock(&ar->ab->tbl_mtx_lock);
+ spin_lock_bh(&ar->ab->base_lock);
+ rhash_count = ath11k_get_peer_count(ar->ab->rhead_peer_addr);
+ spin_unlock_bh(&ar->ab->base_lock);
+ mutex_unlock(&ar->ab->tbl_mtx_lock);
+ if (rhash_count > ar->max_num_peers -1 ) {
+ ath11k_warn(ar->ab,
+ "rhash_count %d exceeds max_num_peers %d\n", rhash_count, ar->max_num_peers);
+ return -ENOBUFS;
+ }
+ ar->num_peers = rhash_count;
}
mutex_lock(&ar->ab->tbl_mtx_lock);
--
2.34.1

View File

@@ -0,0 +1,33 @@
From ed3f83b3459a67a3ab9d806490ac304b567b1c2d Mon Sep 17 00:00:00 2001
From: Miaoqian Lin <linmq006@gmail.com>
Date: Mon, 2 Jan 2023 12:11:42 +0400
Subject: wifi: ath11k: Fix memory leak in ath11k_peer_rx_frag_setup
crypto_alloc_shash() allocates resources, which should be released by
crypto_free_shash(). When ath11k_peer_find() fails, there has memory
leak. Add missing crypto_free_shash() to fix this.
Fixes: 243874c64c81 ("ath11k: handle RX fragments")
Signed-off-by: Miaoqian Lin <linmq006@gmail.com>
Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
Link: https://lore.kernel.org/r/20230102081142.3937570-1-linmq006@gmail.com
---
drivers/net/wireless/ath/ath11k/dp_rx.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index be391322956fec..b65a84a8826413 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -3126,6 +3126,7 @@ int ath11k_peer_rx_frag_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id
if (!peer) {
ath11k_warn(ab, "failed to find the peer to set up fragment info\n");
spin_unlock_bh(&ab->base_lock);
+ crypto_free_shash(tfm);
return -ENOENT;
}
--
cgit 1.2.3-korg

View File

@@ -0,0 +1,39 @@
From 72de799aa9e3e064b35238ef053d2f0a49db055a Mon Sep 17 00:00:00 2001
From: Baochen Qiang <bqiang@codeaurora.org>
Date: Tue, 28 Sep 2021 14:00:44 +0300
Subject: ath11k: Fix memory leak in ath11k_qmi_driver_event_work
The buffer pointed to by event is not freed in case
ATH11K_FLAG_UNREGISTERING bit is set, resulting in
memory leak, so fix it.
Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-01720.1-QCAHSPSWPL_V1_V2_SILICONZ_LITE-1
Fixes: d5c65159f289 ("ath11k: driver for Qualcomm IEEE 802.11ax devices")
Signed-off-by: Baochen Qiang <bqiang@codeaurora.org>
Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20210913180246.193388-4-jouni@codeaurora.org
---
drivers/net/wireless/ath/ath11k/qmi.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c
index babadd574e4b9c..8c615bc788cacf 100644
--- a/drivers/net/wireless/ath/ath11k/qmi.c
+++ b/drivers/net/wireless/ath/ath11k/qmi.c
@@ -2759,8 +2759,10 @@ static void ath11k_qmi_driver_event_work(struct work_struct *work)
list_del(&event->list);
spin_unlock(&qmi->event_lock);
- if (test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags))
+ if (test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags)) {
+ kfree(event);
return;
+ }
switch (event->type) {
case ATH11K_QMI_EVENT_SERVER_ARRIVE:
--
cgit 1.2.3-korg