]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: ath12k: Add hash table for ath12k_link_sta in ath12k_base
authorHarsh Kumar Bijlani <quic_hbijlani@quicinc.com>
Fri, 24 Oct 2025 18:15:42 +0000 (23:45 +0530)
committerJeff Johnson <jeff.johnson@oss.qualcomm.com>
Mon, 27 Oct 2025 14:02:00 +0000 (07:02 -0700)
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>
drivers/net/wireless/ath/ath12k/core.c
drivers/net/wireless/ath/ath12k/core.h
drivers/net/wireless/ath/ath12k/mac.c
drivers/net/wireless/ath/ath12k/peer.c
drivers/net/wireless/ath/ath12k/peer.h
drivers/net/wireless/ath/ath12k/wmi.c

index 1adbdd571a0fe793d4cccc306b90e9a6a796f341..c5aebe66d1905b9285bec5fda3aca70bbda90232 100644 (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);
index 2bbe543bcf132048035207c19c216e0995163dbc..8b5a0a99cd31c3fae9e89cce9f89aae8be6a4124 100644 (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 *));
 };
index 64609fffa7ce46e89263740549f34fd3b8e70f4b..5e6e176bf6a90aece4960d5a2ed2988263ab3c6a 100644 (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);
        }
 
index 16f4a74712d81404ff043dc10ffec89cca61e7a0..28801d87e6ed2109bf726d1876d5815489a659e8 100644 (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);
+}
index 81e9bcc067ff43b01f4aa9abb1cf533b4e7bfc7e..cf8a463d4c371ab7d1fceb3344f26d378f7d0d78 100644 (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_ */
index 817f1c917320ed101116fe22dc0d700c89b796f5..0d4a83b8005a0eaef2ae407e3bb02a99bbeb6f02 100644 (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;
        }