wifi: ath12k: Add hash table for ath12k_link_sta in ath12k_base

Currently ath12k_base maintains a linked list of ath12k_dp_link_peer as "peers".
This linked list is used for all kinds of peer search operations.

In the control path, if there is a requirement to search for ath12k_link_sta
using mac address, then below sequence is to be followed:
  1. Find ath12k_dp_link_peer in the linked list using mac address
  2. Fetch ieee80211_sta from ath12k_dp_link_peer
  3. Fetch ath12k_sta from ieee80211_sta
  4. Finally fetch ath12k_link_sta from ath12k_sta using link_id

In the above sequence, there are too many indirections involved.

In order to simplify this, add hash table for ath12k_link_sta in ath12k_base.
This hash table is lock protected by existing spinlock "base_lock" in
ath12k_base as this table can be concurrently accessed in different contexts.

Use this table for ath12k_link_sta search operations using mac address in the
control path.
Ex: For WMI interface and mac80211_ops interface in the control path, sta mac
address is received and this hash table can be used to search for
ath12k_link_sta directly instead of following the longer route mentioned above.

Helper APIs added:
 - ath12k_link_sta_rhash_add() - To add arsta entry to hash table
 - ath12k_link_sta_rhash_delete() - To remove arsta entry from hash table
 - ath12k_link_sta_find_by_addr() - To find arsta from hash table using
				    mac address

Make changes in API ath12k_peer_sta_kickout_event() to find arsta using above
mechanism.

ath11k driver has been taken as reference for implementation of hash table.

Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.4.1-00199-QCAHKSWPL_SILICONZ-1
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3

Signed-off-by: Harsh Kumar Bijlani <quic_hbijlani@quicinc.com>
Signed-off-by: Ripan Deuri <quic_rdeuri@quicinc.com>
Reviewed-by: Vasanthakumar Thiagarajan <vasanthakumar.thiagarajan@oss.qualcomm.com>
Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
Link: https://patch.msgid.link/20251024181548.3255166-4-quic_rdeuri@quicinc.com
Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
This commit is contained in:
Harsh Kumar Bijlani 2025-10-24 23:45:42 +05:30 committed by Jeff Johnson
parent 9e0b56a333
commit 57ccca4102
6 changed files with 218 additions and 7 deletions

View File

@ -23,6 +23,7 @@
#include "pci.h"
#include "wow.h"
#include "dp_cmn.h"
#include "peer.h"
unsigned int ath12k_debug_mask;
module_param_named(debug_mask, ath12k_debug_mask, uint, 0644);
@ -702,6 +703,8 @@ void ath12k_core_to_group_ref_put(struct ath12k_base *ab)
static void ath12k_core_stop(struct ath12k_base *ab)
{
ath12k_link_sta_rhash_tbl_destroy(ab);
ath12k_core_to_group_ref_put(ab);
if (!test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags))
@ -964,6 +967,12 @@ static int ath12k_core_start(struct ath12k_base *ab)
/* Indicate the core start in the appropriate group */
ath12k_core_to_group_ref_get(ab);
ret = ath12k_link_sta_rhash_tbl_init(ab);
if (ret) {
ath12k_warn(ab, "failed to init peer addr rhash table %d\n", ret);
goto err_reo_cleanup;
}
return 0;
err_reo_cleanup:
@ -1351,6 +1360,7 @@ static int ath12k_core_reconfigure_on_crash(struct ath12k_base *ab)
int ret, total_vdev;
mutex_lock(&ab->core_lock);
ath12k_link_sta_rhash_tbl_destroy(ab);
ath12k_dp_pdev_free(ab);
ath12k_ce_cleanup_pipes(ab);
ath12k_wmi_detach(ab);

View File

@ -18,6 +18,7 @@
#include <linux/panic_notifier.h>
#include <linux/average.h>
#include <linux/of.h>
#include <linux/rhashtable.h>
#include "qmi.h"
#include "htc.h"
#include "wmi.h"
@ -560,6 +561,9 @@ struct ath12k_link_sta {
u8 link_idx;
u32 tx_retry_failed;
u32 tx_retry_count;
/* peer addr based rhashtable list pointer */
struct rhash_head rhash_addr;
};
struct ath12k_reoq_buf {
@ -1220,6 +1224,9 @@ struct ath12k_base {
*/
const struct ieee80211_ops *ath12k_ops;
struct rhashtable *rhead_sta_addr;
struct rhashtable_params rhash_sta_addr_param;
/* must be last */
u8 drv_priv[] __aligned(sizeof(void *));
};

View File

@ -1145,6 +1145,35 @@ static int ath12k_mac_set_kickout(struct ath12k_link_vif *arvif)
return 0;
}
static void ath12k_mac_link_sta_rhash_cleanup(void *data, struct ieee80211_sta *sta)
{
u8 link_id;
unsigned long links_map;
struct ath12k_sta *ahsta;
struct ath12k *ar = data;
struct ath12k_link_sta *arsta;
struct ath12k_link_vif *arvif;
struct ath12k_base *ab = ar->ab;
ahsta = ath12k_sta_to_ahsta(sta);
links_map = ahsta->links_map;
rcu_read_lock();
for_each_set_bit(link_id, &links_map, IEEE80211_MLD_MAX_NUM_LINKS) {
arsta = rcu_dereference(ahsta->link[link_id]);
if (!arsta)
continue;
arvif = arsta->arvif;
if (!(arvif->ar == ar))
continue;
spin_lock_bh(&ab->base_lock);
ath12k_link_sta_rhash_delete(ab, arsta);
spin_unlock_bh(&ab->base_lock);
}
rcu_read_unlock();
}
void ath12k_mac_peer_cleanup_all(struct ath12k *ar)
{
struct ath12k_dp_link_peer *peer, *tmp;
@ -1165,6 +1194,10 @@ void ath12k_mac_peer_cleanup_all(struct ath12k *ar)
ar->num_peers = 0;
ar->num_stations = 0;
/* Cleanup rhash table maintained for arsta by iterating over sta */
ieee80211_iterate_stations_mtx(ar->ah->hw, ath12k_mac_link_sta_rhash_cleanup,
ar);
}
static int ath12k_mac_vdev_setup_sync(struct ath12k *ar)
@ -6349,6 +6382,7 @@ static int ath12k_mac_station_remove(struct ath12k *ar,
struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta);
struct ath12k_vif *ahvif = arvif->ahvif;
int ret = 0;
struct ath12k_link_sta *temp_arsta;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
@ -6377,6 +6411,15 @@ static int ath12k_mac_station_remove(struct ath12k *ar,
ath12k_mac_station_post_remove(ar, arvif, arsta);
spin_lock_bh(&ar->ab->base_lock);
/* To handle roaming and split phy scenario */
temp_arsta = ath12k_link_sta_find_by_addr(ar->ab, arsta->addr);
if (temp_arsta && temp_arsta->arvif->ar == ar)
ath12k_link_sta_rhash_delete(ar->ab, arsta);
spin_unlock_bh(&ar->ab->base_lock);
if (sta->valid_links)
ath12k_mac_free_unassign_link_sta(ahvif->ah,
arsta->ahsta, arsta->link_id);
@ -6393,6 +6436,7 @@ static int ath12k_mac_station_add(struct ath12k *ar,
struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta);
struct ath12k_wmi_peer_create_arg peer_param = {};
int ret;
struct ath12k_link_sta *temp_arsta;
lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy);
@ -6411,6 +6455,26 @@ static int ath12k_mac_station_add(struct ath12k *ar,
}
}
spin_lock_bh(&ab->base_lock);
/*
* In case of Split PHY and roaming scenario, pdev idx
* might differ but both the pdev will share same rhash
* table. In that case update the rhash table if link_sta is
* already present
*/
temp_arsta = ath12k_link_sta_find_by_addr(ab, arsta->addr);
if (temp_arsta && temp_arsta->arvif->ar != ar)
ath12k_link_sta_rhash_delete(ab, temp_arsta);
ret = ath12k_link_sta_rhash_add(ab, arsta);
spin_unlock_bh(&ab->base_lock);
if (ret) {
ath12k_warn(ab, "Failed to add arsta: %pM to hash table, ret: %d",
arsta->addr, ret);
goto free_rx_stats;
}
peer_param.vdev_id = arvif->vdev_id;
peer_param.peer_addr = arsta->addr;
peer_param.peer_type = WMI_PEER_TYPE_DEFAULT;
@ -6459,6 +6523,10 @@ static int ath12k_mac_station_add(struct ath12k *ar,
free_peer:
ath12k_peer_delete(ar, arvif->vdev_id, arsta->addr);
spin_lock_bh(&ab->base_lock);
ath12k_link_sta_rhash_delete(ab, arsta);
spin_unlock_bh(&ab->base_lock);
free_rx_stats:
kfree(arsta->rx_stats);
arsta->rx_stats = NULL;
dec_num_station:
@ -6537,6 +6605,10 @@ static void ath12k_mac_ml_station_remove(struct ath12k_vif *ahvif,
ath12k_mac_station_post_remove(ar, arvif, arsta);
spin_lock_bh(&ar->ab->base_lock);
ath12k_link_sta_rhash_delete(ar->ab, arsta);
spin_unlock_bh(&ar->ab->base_lock);
ath12k_mac_free_unassign_link_sta(ah, ahsta, link_id);
}

View File

@ -406,3 +406,119 @@ int ath12k_peer_mlo_link_peers_delete(struct ath12k_vif *ahvif, struct ath12k_st
return err_ret;
}
static int ath12k_link_sta_rhash_insert(struct ath12k_base *ab,
struct ath12k_link_sta *arsta)
{
struct ath12k_link_sta *tmp;
lockdep_assert_held(&ab->base_lock);
tmp = rhashtable_lookup_get_insert_fast(ab->rhead_sta_addr, &arsta->rhash_addr,
ab->rhash_sta_addr_param);
if (!tmp)
return 0;
else if (IS_ERR(tmp))
return PTR_ERR(tmp);
else
return -EEXIST;
}
static int ath12k_link_sta_rhash_remove(struct ath12k_base *ab,
struct ath12k_link_sta *arsta)
{
int ret;
lockdep_assert_held(&ab->base_lock);
ret = rhashtable_remove_fast(ab->rhead_sta_addr, &arsta->rhash_addr,
ab->rhash_sta_addr_param);
if (ret && ret != -ENOENT)
return ret;
return 0;
}
int ath12k_link_sta_rhash_add(struct ath12k_base *ab,
struct ath12k_link_sta *arsta)
{
int ret;
lockdep_assert_held(&ab->base_lock);
ret = ath12k_link_sta_rhash_insert(ab, arsta);
if (ret)
ath12k_warn(ab, "failed to add arsta %pM in rhash_addr ret %d\n",
arsta->addr, ret);
return ret;
}
void ath12k_link_sta_rhash_delete(struct ath12k_base *ab,
struct ath12k_link_sta *arsta)
{
/*
* Return type of this function is void since there is nothing to be
* done in failure case
*/
int ret;
lockdep_assert_held(&ab->base_lock);
ret = ath12k_link_sta_rhash_remove(ab, arsta);
if (ret)
ath12k_warn(ab,
"failed to remove arsta %pM in rhash_addr ret %d\n",
arsta->addr, ret);
}
int ath12k_link_sta_rhash_tbl_init(struct ath12k_base *ab)
{
struct rhashtable_params *param;
struct rhashtable *rhash_addr_tbl;
int ret;
rhash_addr_tbl = kzalloc(sizeof(*ab->rhead_sta_addr), GFP_KERNEL);
if (!rhash_addr_tbl)
return -ENOMEM;
param = &ab->rhash_sta_addr_param;
param->key_offset = offsetof(struct ath12k_link_sta, addr);
param->head_offset = offsetof(struct ath12k_link_sta, rhash_addr);
param->key_len = sizeof_field(struct ath12k_link_sta, addr);
param->automatic_shrinking = true;
param->nelem_hint = ab->num_radios * ath12k_core_get_max_peers_per_radio(ab);
ret = rhashtable_init(rhash_addr_tbl, param);
if (ret) {
ath12k_warn(ab, "failed to init peer addr rhash table %d\n",
ret);
goto err_free;
}
ab->rhead_sta_addr = rhash_addr_tbl;
return 0;
err_free:
kfree(rhash_addr_tbl);
return ret;
}
void ath12k_link_sta_rhash_tbl_destroy(struct ath12k_base *ab)
{
rhashtable_destroy(ab->rhead_sta_addr);
kfree(ab->rhead_sta_addr);
ab->rhead_sta_addr = NULL;
}
struct ath12k_link_sta *ath12k_link_sta_find_by_addr(struct ath12k_base *ab,
const u8 *addr)
{
lockdep_assert_held(&ab->base_lock);
return rhashtable_lookup_fast(ab->rhead_sta_addr, addr,
ab->rhash_sta_addr_param);
}

View File

@ -27,4 +27,10 @@ int ath12k_peer_ml_delete(struct ath12k_hw *ah, struct ieee80211_sta *sta);
int ath12k_peer_mlo_link_peers_delete(struct ath12k_vif *ahvif, struct ath12k_sta *ahsta);
struct ath12k_ml_peer *ath12k_peer_ml_find(struct ath12k_hw *ah,
const u8 *addr);
int ath12k_link_sta_rhash_tbl_init(struct ath12k_base *ab);
void ath12k_link_sta_rhash_tbl_destroy(struct ath12k_base *ab);
void ath12k_link_sta_rhash_delete(struct ath12k_base *ab, struct ath12k_link_sta *arsta);
int ath12k_link_sta_rhash_add(struct ath12k_base *ab, struct ath12k_link_sta *arsta);
struct ath12k_link_sta *ath12k_link_sta_find_by_addr(struct ath12k_base *ab,
const u8 *addr);
#endif /* _PEER_H_ */

View File

@ -7202,7 +7202,7 @@ static void ath12k_peer_sta_kickout_event(struct ath12k_base *ab, struct sk_buff
{
struct wmi_peer_sta_kickout_arg arg = {};
struct ieee80211_sta *sta;
struct ath12k_dp_link_peer *peer;
struct ath12k_link_sta *arsta;
struct ath12k *ar;
if (ath12k_pull_peer_sta_kickout_ev(ab, skb, &arg) != 0) {
@ -7214,18 +7214,18 @@ static void ath12k_peer_sta_kickout_event(struct ath12k_base *ab, struct sk_buff
spin_lock_bh(&ab->base_lock);
peer = ath12k_dp_link_peer_find_by_addr(ab, arg.mac_addr);
arsta = ath12k_link_sta_find_by_addr(ab, arg.mac_addr);
if (!peer) {
ath12k_warn(ab, "peer not found %pM\n",
if (!arsta) {
ath12k_warn(ab, "arsta not found %pM\n",
arg.mac_addr);
goto exit;
}
ar = ath12k_mac_get_ar_by_vdev_id(ab, peer->vdev_id);
ar = arsta->arvif->ar;
if (!ar) {
ath12k_warn(ab, "invalid vdev id in peer sta kickout ev %d",
peer->vdev_id);
ath12k_warn(ab, "invalid ar in peer sta kickout ev for STA %pM\n",
arg.mac_addr);
goto exit;
}