[PATCH ath-next] wifi: ath12k: move arsta grouping from rhashtable to per-radio hashtable
Baochen Qiang
baochen.qiang at oss.qualcomm.com
Sun Nov 1 17:47:47 PST 2026
On 10/22/2026 1:33 AM, Maharaja Kennadyrajan wrote:
> From: Nagarajan Maran <nagarajan.maran at oss.qualcomm.com>
>
> The link-station lookup table is a single rhashtable at the ath12k_base
> level, keyed only by station address. Rhashtable does not allow duplicate
> keys, so the same address cannot be represented on two radios at once;
> a roam or split-PHY overlap where an address briefly exists on more than
> one radio is instead handled by deleting the previous radio's entry
> before inserting the new one. Because the table is keyed only by address
> and shared device-wide, a lookup also cannot be scoped to a specific
> radio: during that overlap window, or due to stale remnants, the single
> entry present for an address can belong to a different radio than the
> one being looked up, silently attaching the wrong radio's data to the
> wrong arsta.
>
> Move the lookup table down to the ath12k (per-radio) level, and switch its
> backing structure from rhashtable to hlist, which is simpler to walk in
> full for the iteration that some code paths already need. Each radio now
> owns its own table, so a lookup is inherently scoped to that radio, and
> the same address can occupy an entry on two different radios at once
> without any delete-then-insert workaround.
>
> Update ath12k_peer_sta_kickout_event(), ath12k_wmi_tlv_fw_stats_data_parse()
> and ath12k_wmi_tlv_rssi_chain_parse() to use the new per-radio arsta lookup
> helpers accordingly.
>
> The datapath's link-peer table (struct ath12k_dp_link_peer, in dp_peer.c) has
> the same device-level rhashtable design and delete-then-insert workaround; it
> is unaffected by this change and remains out of scope here.
>
> Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.5-01651-QCAHKSWPL_SILICONZ-1
>
> Signed-off-by: Nagarajan Maran <nagarajan.maran at oss.qualcomm.com>
> Co-developed-by: Maharaja Kennadyrajan <maharaja.kennadyrajan at oss.qualcomm.com>
> Signed-off-by: Maharaja Kennadyrajan <maharaja.kennadyrajan at oss.qualcomm.com>
> ---
> drivers/net/wireless/ath/ath12k/core.c | 9 --
> drivers/net/wireless/ath/ath12k/core.h | 21 ++--
> drivers/net/wireless/ath/ath12k/mac.c | 122 ++++++++++-----------
> drivers/net/wireless/ath/ath12k/peer.c | 144 +++++++++++--------------
> drivers/net/wireless/ath/ath12k/peer.h | 11 +-
> drivers/net/wireless/ath/ath12k/wmi.c | 112 ++++++++++++-------
> 6 files changed, 213 insertions(+), 206 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
> index 25987bc58556..2309ccae5338 100644
> --- a/drivers/net/wireless/ath/ath12k/core.c
> +++ b/drivers/net/wireless/ath/ath12k/core.c
> @@ -716,8 +716,6 @@ 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)) {
> @@ -1016,12 +1014,6 @@ 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:
> @@ -1425,7 +1417,6 @@ 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_thermal_unregister(ab);
> ath12k_dp_pdev_free(ab);
> ath12k_ce_cleanup_pipes(ab);
> diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
> index 99d71dab7c04..bde3b4985023 100644
> --- a/drivers/net/wireless/ath/ath12k/core.h
> +++ b/drivers/net/wireless/ath/ath12k/core.h
> @@ -17,7 +17,7 @@
> #include <linux/of_reserved_mem.h>
> #include <linux/average.h>
> #include <linux/of.h>
> -#include <linux/rhashtable.h>
> +#include <linux/hashtable.h>
> #include "qmi.h"
> #include "htc.h"
> #include "wmi.h"
> @@ -510,8 +510,8 @@ struct ath12k_link_sta {
> /* for firmware use only */
> u8 link_idx;
>
> - /* peer addr based rhashtable list pointer */
> - struct rhash_head rhash_addr;
> + /* node for ar->arsta_hlist, the peer addr keyed hash table */
> + struct hlist_node hash_node;
> };
>
> struct ath12k_sta {
> @@ -658,11 +658,21 @@ struct ath12k {
> /* protects the radio specific data like debug stats, ppdu_stats_info stats,
> * vdev_stop_status info, scan data, ath12k_sta info, ath12k_link_vif info,
> * channel context data, test mode data, regd_channel_update_queue,
> - * peer_delete_waits.
> + * peer_delete_waits, arsta hash table (arsta_hlist/arsta_hash_bits).
> */
> spinlock_t data_lock;
>
> struct list_head arvifs;
> +
> + /* per-radio hash buckets for station address lookup */
> + struct hlist_head *arsta_hlist;
> +
> + /*
> + * Number of bits used to size arsta_hlist (arsta_hlist has
> + * BIT(arsta_hash_bits) buckets).
> + */
> + u8 arsta_hash_bits;
> +
> /* should never be NULL; needed for regular htt rx */
> struct ieee80211_channel *rx_channel;
>
> @@ -1149,9 +1159,6 @@ 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 *));
> };
> diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
> index 01f8c6572f65..d21b0b50853e 100644
> --- a/drivers/net/wireless/ath/ath12k/mac.c
> +++ b/drivers/net/wireless/ath/ath12k/mac.c
> @@ -1185,35 +1185,6 @@ 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;
> @@ -1262,9 +1233,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);
> + /* Cleanup address hash maintained for arsta on this radio */
> + spin_lock_bh(&ar->data_lock);
> + ath12k_link_sta_hlist_flush(ar);
> + spin_unlock_bh(&ar->data_lock);
>
> /* Delete all the self dp_peers on asserted radio */
> list_for_each_entry_safe_reverse(arvif, tmp_vif, &ar->arvifs, list) {
> @@ -7101,7 +7073,6 @@ 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);
>
> @@ -7130,14 +7101,9 @@ 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);
> + spin_lock_bh(&ar->data_lock);
> + ath12k_link_sta_hlist_delete(ar, arsta);
> + spin_unlock_bh(&ar->data_lock);
>
> if (sta->valid_links)
> ath12k_mac_free_unassign_link_sta(ahvif->ah,
> @@ -7155,7 +7121,6 @@ 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);
>
> @@ -7166,22 +7131,11 @@ static int ath12k_mac_station_add(struct ath12k *ar,
> goto exit;
> }
>
> - 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);
> + spin_lock_bh(&ar->data_lock);
> + ret = ath12k_link_sta_hlist_add(ar, arsta);
> + spin_unlock_bh(&ar->data_lock);
> if (ret) {
> - ath12k_warn(ab, "Failed to add arsta: %pM to hash table, ret: %d",
> + ath12k_warn(ab, "failed to add %pM to hash table %d\n",
it is warning, so you can have colons before error code:
"failed to add %pM to hash table: %d\n"
with that fixed:
Reviewed-by: Baochen Qiang <baochen.qiang at oss.qualcomm.com>
More information about the ath12k
mailing list