summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/ath/ath12k/dp.c14
-rw-r--r--drivers/net/wireless/ath/ath12k/dp.h10
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_peer.c138
-rw-r--r--drivers/net/wireless/ath/ath12k/dp_peer.h9
-rw-r--r--drivers/net/wireless/ath/ath12k/mac.c2
5 files changed, 160 insertions, 13 deletions
diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c
index 40057df4cd66..676af752f069 100644
--- a/drivers/net/wireless/ath/ath12k/dp.c
+++ b/drivers/net/wireless/ath/ath12k/dp.c
@@ -1122,6 +1122,8 @@ static void ath12k_dp_cleanup(struct ath12k_base *ab)
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
int i;
+ ath12k_dp_link_peer_rhash_tbl_destroy(dp);
+
if (!dp->ab)
return;
@@ -1487,14 +1489,22 @@ static int ath12k_dp_setup(struct ath12k_base *ab)
spin_lock_init(&dp->dp_lock);
INIT_LIST_HEAD(&dp->peers);
+ mutex_init(&dp->link_peer_rhash_tbl_lock);
+
dp->reo_cmd_cache_flush_count = 0;
dp->idle_link_rbm =
ath12k_hal_get_idle_link_rbm(&ab->hal, ab->device_id);
+ ret = ath12k_dp_link_peer_rhash_tbl_init(dp);
+ if (ret) {
+ ath12k_warn(ab, "failed to init link_peer rhash table: %d\n", ret);
+ return ret;
+ }
+
ret = ath12k_wbm_idle_ring_setup(ab, &n_link_desc);
if (ret) {
ath12k_warn(ab, "failed to setup wbm_idle_ring: %d\n", ret);
- return ret;
+ goto rhash_destroy;
}
srng = &ab->hal.srng_list[dp->wbm_idle_ring.ring_id];
@@ -1575,6 +1585,8 @@ fail_hw_cc_cleanup:
fail_link_desc_cleanup:
ath12k_dp_link_desc_cleanup(ab, dp->link_desc_banks,
HAL_WBM_IDLE_LINK, &dp->wbm_idle_ring);
+rhash_destroy:
+ ath12k_dp_link_peer_rhash_tbl_destroy(dp);
return ret;
}
diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h
index fc87b749a040..b90725094111 100644
--- a/drivers/net/wireless/ath/ath12k/dp.h
+++ b/drivers/net/wireless/ath/ath12k/dp.h
@@ -10,6 +10,7 @@
#include "hw.h"
#include "dp_htt.h"
#include "dp_cmn.h"
+#include <linux/rhashtable.h>
#define MAX_RXDMA_PER_PDEV 2
@@ -471,13 +472,20 @@ struct ath12k_dp {
struct ath12k_hw_group *ag;
u8 device_id;
- /* Lock for protection of peers */
+ /* Lock for protection of peers and rhead_peer_addr */
spinlock_t dp_lock;
struct ath12k_dp_arch_ops *ops;
/* Linked list of struct ath12k_dp_link_peer */
struct list_head peers;
+
+ /* For rhash table init and deinit protection */
+ struct mutex link_peer_rhash_tbl_lock;
+
+ /* The rhashtable containing struct ath12k_link_peer keyed by mac addr */
+ struct rhashtable *rhead_peer_addr;
+ struct rhashtable_params rhash_peer_addr_param;
};
static inline void ath12k_dp_get_mac_addr(u32 addr_l32, u16 addr_h16, u8 *addr)
diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.c b/drivers/net/wireless/ath/ath12k/dp_peer.c
index 0267f68f8573..0cf28791568e 100644
--- a/drivers/net/wireless/ath/ath12k/dp_peer.c
+++ b/drivers/net/wireless/ath/ath12k/dp_peer.c
@@ -51,18 +51,10 @@ ath12k_dp_link_peer_find_by_pdev_and_addr(struct ath12k_dp *dp, u8 pdev_idx,
struct ath12k_dp_link_peer *
ath12k_dp_link_peer_find_by_addr(struct ath12k_dp *dp, const u8 *addr)
{
- struct ath12k_dp_link_peer *peer;
-
lockdep_assert_held(&dp->dp_lock);
- list_for_each_entry(peer, &dp->peers, list) {
- if (!ether_addr_equal(peer->addr, addr))
- continue;
-
- return peer;
- }
-
- return NULL;
+ return rhashtable_lookup_fast(dp->rhead_peer_addr, addr,
+ dp->rhash_peer_addr_param);
}
EXPORT_SYMBOL(ath12k_dp_link_peer_find_by_addr);
@@ -147,6 +139,7 @@ void ath12k_dp_link_peer_unmap_event(struct ath12k_base *ab, u16 peer_id)
ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "htt peer unmap vdev %d peer %pM id %d\n",
peer->vdev_id, peer->addr, peer_id);
+ ath12k_dp_link_peer_rhash_delete(dp, peer);
list_del(&peer->list);
kfree(peer);
wake_up(&ab->peer_mapping_wq);
@@ -160,6 +153,7 @@ void ath12k_dp_link_peer_map_event(struct ath12k_base *ab, u8 vdev_id, u16 peer_
{
struct ath12k_dp_link_peer *peer;
struct ath12k_dp *dp = ath12k_ab_to_dp(ab);
+ int ret;
spin_lock_bh(&dp->dp_lock);
peer = ath12k_dp_link_peer_find_by_vdev_and_addr(dp, vdev_id, mac_addr);
@@ -173,7 +167,11 @@ void ath12k_dp_link_peer_map_event(struct ath12k_base *ab, u8 vdev_id, u16 peer_
peer->ast_hash = ast_hash;
peer->hw_peer_id = hw_peer_id;
ether_addr_copy(peer->addr, mac_addr);
- list_add(&peer->list, &dp->peers);
+ ret = ath12k_dp_link_peer_rhash_add(dp, peer);
+ if (!ret)
+ list_add(&peer->list, &dp->peers);
+ else
+ kfree(peer);
wake_up(&ab->peer_mapping_wq);
}
@@ -209,3 +207,121 @@ struct ath12k_link_sta *ath12k_dp_link_peer_to_link_sta(struct ath12k_base *ab,
}
return arsta;
}
+
+static int ath12k_dp_link_peer_rhash_addr_tbl_init(struct ath12k_dp *dp)
+{
+ struct ath12k_base *ab = dp->ab;
+ struct rhashtable_params *param;
+ struct rhashtable *rhash_addr_tbl;
+ int ret;
+
+ lockdep_assert_held(&dp->link_peer_rhash_tbl_lock);
+
+ rhash_addr_tbl = kzalloc(sizeof(*dp->rhead_peer_addr), GFP_KERNEL);
+ if (!rhash_addr_tbl)
+ return -ENOMEM;
+
+ param = &dp->rhash_peer_addr_param;
+
+ param->key_offset = offsetof(struct ath12k_dp_link_peer, addr);
+ param->head_offset = offsetof(struct ath12k_dp_link_peer, rhash_addr);
+ param->key_len = sizeof_field(struct ath12k_dp_link_peer, 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;
+ }
+
+ dp->rhead_peer_addr = rhash_addr_tbl;
+
+ return 0;
+
+err_free:
+ kfree(rhash_addr_tbl);
+
+ return ret;
+}
+
+int ath12k_dp_link_peer_rhash_tbl_init(struct ath12k_dp *dp)
+{
+ int ret;
+
+ mutex_lock(&dp->link_peer_rhash_tbl_lock);
+ ret = ath12k_dp_link_peer_rhash_addr_tbl_init(dp);
+ mutex_unlock(&dp->link_peer_rhash_tbl_lock);
+
+ return ret;
+}
+
+void ath12k_dp_link_peer_rhash_tbl_destroy(struct ath12k_dp *dp)
+{
+ mutex_lock(&dp->link_peer_rhash_tbl_lock);
+ rhashtable_destroy(dp->rhead_peer_addr);
+ kfree(dp->rhead_peer_addr);
+ dp->rhead_peer_addr = NULL;
+ mutex_unlock(&dp->link_peer_rhash_tbl_lock);
+}
+
+static int ath12k_dp_link_peer_rhash_insert(struct ath12k_dp *dp,
+ struct ath12k_dp_link_peer *peer)
+{
+ struct ath12k_dp_link_peer *tmp;
+
+ lockdep_assert_held(&dp->dp_lock);
+
+ tmp = rhashtable_lookup_get_insert_fast(dp->rhead_peer_addr, &peer->rhash_addr,
+ dp->rhash_peer_addr_param);
+ if (!tmp)
+ return 0;
+ else if (IS_ERR(tmp))
+ return PTR_ERR(tmp);
+ else
+ return -EEXIST;
+}
+
+static int ath12k_dp_link_peer_rhash_remove(struct ath12k_dp *dp,
+ struct ath12k_dp_link_peer *peer)
+{
+ int ret;
+
+ lockdep_assert_held(&dp->dp_lock);
+
+ ret = rhashtable_remove_fast(dp->rhead_peer_addr, &peer->rhash_addr,
+ dp->rhash_peer_addr_param);
+ if (ret && ret != -ENOENT)
+ return ret;
+
+ return 0;
+}
+
+int ath12k_dp_link_peer_rhash_add(struct ath12k_dp *dp,
+ struct ath12k_dp_link_peer *peer)
+{
+ int ret;
+
+ lockdep_assert_held(&dp->dp_lock);
+
+ ret = ath12k_dp_link_peer_rhash_insert(dp, peer);
+ if (ret)
+ ath12k_warn(dp, "failed to add peer %pM with id %d in rhash_addr ret %d\n",
+ peer->addr, peer->peer_id, ret);
+
+ return ret;
+}
+
+void ath12k_dp_link_peer_rhash_delete(struct ath12k_dp *dp,
+ struct ath12k_dp_link_peer *peer)
+{
+ /* No failure handling and hence return type is void */
+ int ret;
+
+ lockdep_assert_held(&dp->dp_lock);
+
+ ret = ath12k_dp_link_peer_rhash_remove(dp, peer);
+ if (ret)
+ ath12k_warn(dp, "failed to remove peer %pM with id %d in rhash_addr ret %d\n",
+ peer->addr, peer->peer_id, ret);
+}
diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.h b/drivers/net/wireless/ath/ath12k/dp_peer.h
index ecc90df05b44..b94a9a5cb311 100644
--- a/drivers/net/wireless/ath/ath12k/dp_peer.h
+++ b/drivers/net/wireless/ath/ath12k/dp_peer.h
@@ -63,6 +63,9 @@ struct ath12k_dp_link_peer {
/* for reference to ath12k_link_sta */
u8 link_id;
bool ucast_ra_only;
+
+ /* peer addr based rhashtable list pointer */
+ struct rhash_head rhash_addr;
};
void ath12k_dp_link_peer_unmap_event(struct ath12k_base *ab, u16 peer_id);
@@ -83,4 +86,10 @@ ath12k_dp_link_peer_find_by_pdev_and_addr(struct ath12k_dp *dp, u8 pdev_idx,
const u8 *addr);
struct ath12k_link_sta *ath12k_dp_link_peer_to_link_sta(struct ath12k_base *ab,
struct ath12k_dp_link_peer *peer);
+int ath12k_dp_link_peer_rhash_tbl_init(struct ath12k_dp *dp);
+void ath12k_dp_link_peer_rhash_tbl_destroy(struct ath12k_dp *dp);
+int ath12k_dp_link_peer_rhash_add(struct ath12k_dp *dp,
+ struct ath12k_dp_link_peer *peer);
+void ath12k_dp_link_peer_rhash_delete(struct ath12k_dp *dp,
+ struct ath12k_dp_link_peer *peer);
#endif
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index fd6819ec390c..c7eeaf586b83 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -1188,6 +1188,8 @@ void ath12k_mac_peer_cleanup_all(struct ath12k *ar)
if (peer->sta)
ath12k_dp_rx_peer_tid_cleanup(ar, peer);
+ ath12k_dp_link_peer_rhash_delete(dp, peer);
+
list_del(&peer->list);
kfree(peer);
}