276 lines
8.7 KiB
Diff
276 lines
8.7 KiB
Diff
|
From: Herbert Xu <herbert@gondor.apana.org.au>
|
||
|
Date: Mon, 19 Sep 2016 19:00:10 +0800
|
||
|
Subject: [PATCH] mac80211: Use rhltable instead of rhashtable
|
||
|
|
||
|
mac80211 currently uses rhashtable with insecure_elasticity set
|
||
|
to true. The latter is because of duplicate objects. What's
|
||
|
more, mac80211 walks the rhashtable chains by hand which is broken
|
||
|
as rhashtable may contain multiple tables due to resizing or
|
||
|
rehashing.
|
||
|
|
||
|
This patch fixes it by converting it to the newly added rhltable
|
||
|
interface which is designed for use with duplicate objects.
|
||
|
|
||
|
With rhltable a lookup returns a list of objects instead of a
|
||
|
single one. This is then fed into the existing for_each_sta_info
|
||
|
macro.
|
||
|
|
||
|
This patch also deletes the sta_addr_hash function since rhashtable
|
||
|
defaults to jhash.
|
||
|
|
||
|
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
|
||
|
---
|
||
|
|
||
|
--- a/net/mac80211/ieee80211_i.h
|
||
|
+++ b/net/mac80211/ieee80211_i.h
|
||
|
@@ -1233,7 +1233,7 @@ struct ieee80211_local {
|
||
|
spinlock_t tim_lock;
|
||
|
unsigned long num_sta;
|
||
|
struct list_head sta_list;
|
||
|
- struct rhashtable sta_hash;
|
||
|
+ struct rhltable sta_hash;
|
||
|
struct timer_list sta_cleanup;
|
||
|
int sta_generation;
|
||
|
|
||
|
--- a/net/mac80211/rx.c
|
||
|
+++ b/net/mac80211/rx.c
|
||
|
@@ -4004,7 +4004,7 @@ static void __ieee80211_rx_handle_packet
|
||
|
__le16 fc;
|
||
|
struct ieee80211_rx_data rx;
|
||
|
struct ieee80211_sub_if_data *prev;
|
||
|
- struct rhash_head *tmp;
|
||
|
+ struct rhlist_head *tmp;
|
||
|
int err = 0;
|
||
|
|
||
|
fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
|
||
|
@@ -4047,13 +4047,10 @@ static void __ieee80211_rx_handle_packet
|
||
|
goto out;
|
||
|
} else if (ieee80211_is_data(fc)) {
|
||
|
struct sta_info *sta, *prev_sta;
|
||
|
- const struct bucket_table *tbl;
|
||
|
|
||
|
prev_sta = NULL;
|
||
|
|
||
|
- tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
|
||
|
-
|
||
|
- for_each_sta_info(local, tbl, hdr->addr2, sta, tmp) {
|
||
|
+ for_each_sta_info(local, hdr->addr2, sta, tmp) {
|
||
|
if (!prev_sta) {
|
||
|
prev_sta = sta;
|
||
|
continue;
|
||
|
--- a/net/mac80211/sta_info.c
|
||
|
+++ b/net/mac80211/sta_info.c
|
||
|
@@ -67,12 +67,10 @@
|
||
|
|
||
|
static const struct rhashtable_params sta_rht_params = {
|
||
|
.nelem_hint = 3, /* start small */
|
||
|
- .insecure_elasticity = true, /* Disable chain-length checks. */
|
||
|
.automatic_shrinking = true,
|
||
|
.head_offset = offsetof(struct sta_info, hash_node),
|
||
|
.key_offset = offsetof(struct sta_info, addr),
|
||
|
.key_len = ETH_ALEN,
|
||
|
- .hashfn = sta_addr_hash,
|
||
|
.max_size = CPTCFG_MAC80211_STA_HASH_MAX_SIZE,
|
||
|
};
|
||
|
|
||
|
@@ -80,8 +78,8 @@ static const struct rhashtable_params st
|
||
|
static int sta_info_hash_del(struct ieee80211_local *local,
|
||
|
struct sta_info *sta)
|
||
|
{
|
||
|
- return rhashtable_remove_fast(&local->sta_hash, &sta->hash_node,
|
||
|
- sta_rht_params);
|
||
|
+ return rhltable_remove(&local->sta_hash, &sta->hash_node,
|
||
|
+ sta_rht_params);
|
||
|
}
|
||
|
|
||
|
static void __cleanup_single_sta(struct sta_info *sta)
|
||
|
@@ -157,19 +155,22 @@ static void cleanup_single_sta(struct st
|
||
|
sta_info_free(local, sta);
|
||
|
}
|
||
|
|
||
|
+struct rhlist_head *sta_info_hash_lookup(struct ieee80211_local *local,
|
||
|
+ const u8 *addr)
|
||
|
+{
|
||
|
+ return rhltable_lookup(&local->sta_hash, addr, sta_rht_params);
|
||
|
+}
|
||
|
+
|
||
|
/* protected by RCU */
|
||
|
struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
|
||
|
const u8 *addr)
|
||
|
{
|
||
|
struct ieee80211_local *local = sdata->local;
|
||
|
+ struct rhlist_head *tmp;
|
||
|
struct sta_info *sta;
|
||
|
- struct rhash_head *tmp;
|
||
|
- const struct bucket_table *tbl;
|
||
|
|
||
|
rcu_read_lock();
|
||
|
- tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
|
||
|
-
|
||
|
- for_each_sta_info(local, tbl, addr, sta, tmp) {
|
||
|
+ for_each_sta_info(local, addr, sta, tmp) {
|
||
|
if (sta->sdata == sdata) {
|
||
|
rcu_read_unlock();
|
||
|
/* this is safe as the caller must already hold
|
||
|
@@ -190,14 +191,11 @@ struct sta_info *sta_info_get_bss(struct
|
||
|
const u8 *addr)
|
||
|
{
|
||
|
struct ieee80211_local *local = sdata->local;
|
||
|
+ struct rhlist_head *tmp;
|
||
|
struct sta_info *sta;
|
||
|
- struct rhash_head *tmp;
|
||
|
- const struct bucket_table *tbl;
|
||
|
|
||
|
rcu_read_lock();
|
||
|
- tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
|
||
|
-
|
||
|
- for_each_sta_info(local, tbl, addr, sta, tmp) {
|
||
|
+ for_each_sta_info(local, addr, sta, tmp) {
|
||
|
if (sta->sdata == sdata ||
|
||
|
(sta->sdata->bss && sta->sdata->bss == sdata->bss)) {
|
||
|
rcu_read_unlock();
|
||
|
@@ -263,8 +261,8 @@ void sta_info_free(struct ieee80211_loca
|
||
|
static int sta_info_hash_add(struct ieee80211_local *local,
|
||
|
struct sta_info *sta)
|
||
|
{
|
||
|
- return rhashtable_insert_fast(&local->sta_hash, &sta->hash_node,
|
||
|
- sta_rht_params);
|
||
|
+ return rhltable_insert(&local->sta_hash, &sta->hash_node,
|
||
|
+ sta_rht_params);
|
||
|
}
|
||
|
|
||
|
static void sta_deliver_ps_frames(struct work_struct *wk)
|
||
|
@@ -453,9 +451,9 @@ static int sta_info_insert_check(struct
|
||
|
is_multicast_ether_addr(sta->sta.addr)))
|
||
|
return -EINVAL;
|
||
|
|
||
|
- /* Strictly speaking this isn't necessary as we hold the mutex, but
|
||
|
- * the rhashtable code can't really deal with that distinction. We
|
||
|
- * do require the mutex for correctness though.
|
||
|
+ /* The RCU read lock is required by rhashtable due to
|
||
|
+ * asynchronous resize/rehash. We also require the mutex
|
||
|
+ * for correctness.
|
||
|
*/
|
||
|
rcu_read_lock();
|
||
|
lockdep_assert_held(&sdata->local->sta_mtx);
|
||
|
@@ -1043,16 +1041,11 @@ static void sta_info_cleanup(unsigned lo
|
||
|
round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL));
|
||
|
}
|
||
|
|
||
|
-u32 sta_addr_hash(const void *key, u32 length, u32 seed)
|
||
|
-{
|
||
|
- return jhash(key, ETH_ALEN, seed);
|
||
|
-}
|
||
|
-
|
||
|
int sta_info_init(struct ieee80211_local *local)
|
||
|
{
|
||
|
int err;
|
||
|
|
||
|
- err = rhashtable_init(&local->sta_hash, &sta_rht_params);
|
||
|
+ err = rhltable_init(&local->sta_hash, &sta_rht_params);
|
||
|
if (err)
|
||
|
return err;
|
||
|
|
||
|
@@ -1068,7 +1061,7 @@ int sta_info_init(struct ieee80211_local
|
||
|
void sta_info_stop(struct ieee80211_local *local)
|
||
|
{
|
||
|
del_timer_sync(&local->sta_cleanup);
|
||
|
- rhashtable_destroy(&local->sta_hash);
|
||
|
+ rhltable_destroy(&local->sta_hash);
|
||
|
}
|
||
|
|
||
|
|
||
|
@@ -1138,17 +1131,14 @@ struct ieee80211_sta *ieee80211_find_sta
|
||
|
const u8 *localaddr)
|
||
|
{
|
||
|
struct ieee80211_local *local = hw_to_local(hw);
|
||
|
+ struct rhlist_head *tmp;
|
||
|
struct sta_info *sta;
|
||
|
- struct rhash_head *tmp;
|
||
|
- const struct bucket_table *tbl;
|
||
|
-
|
||
|
- tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
|
||
|
|
||
|
/*
|
||
|
* Just return a random station if localaddr is NULL
|
||
|
* ... first in list.
|
||
|
*/
|
||
|
- for_each_sta_info(local, tbl, addr, sta, tmp) {
|
||
|
+ for_each_sta_info(local, addr, sta, tmp) {
|
||
|
if (localaddr &&
|
||
|
!ether_addr_equal(sta->sdata->vif.addr, localaddr))
|
||
|
continue;
|
||
|
--- a/net/mac80211/sta_info.h
|
||
|
+++ b/net/mac80211/sta_info.h
|
||
|
@@ -455,7 +455,7 @@ struct sta_info {
|
||
|
/* General information, mostly static */
|
||
|
struct list_head list, free_list;
|
||
|
struct rcu_head rcu_head;
|
||
|
- struct rhash_head hash_node;
|
||
|
+ struct rhlist_head hash_node;
|
||
|
u8 addr[ETH_ALEN];
|
||
|
struct ieee80211_local *local;
|
||
|
struct ieee80211_sub_if_data *sdata;
|
||
|
@@ -638,6 +638,9 @@ rcu_dereference_protected_tid_tx(struct
|
||
|
*/
|
||
|
#define STA_INFO_CLEANUP_INTERVAL (10 * HZ)
|
||
|
|
||
|
+struct rhlist_head *sta_info_hash_lookup(struct ieee80211_local *local,
|
||
|
+ const u8 *addr);
|
||
|
+
|
||
|
/*
|
||
|
* Get a STA info, must be under RCU read lock.
|
||
|
*/
|
||
|
@@ -647,17 +650,9 @@ struct sta_info *sta_info_get(struct iee
|
||
|
struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
|
||
|
const u8 *addr);
|
||
|
|
||
|
-u32 sta_addr_hash(const void *key, u32 length, u32 seed);
|
||
|
-
|
||
|
-#define _sta_bucket_idx(_tbl, _a) \
|
||
|
- rht_bucket_index(_tbl, sta_addr_hash(_a, ETH_ALEN, (_tbl)->hash_rnd))
|
||
|
-
|
||
|
-#define for_each_sta_info(local, tbl, _addr, _sta, _tmp) \
|
||
|
- rht_for_each_entry_rcu(_sta, _tmp, tbl, \
|
||
|
- _sta_bucket_idx(tbl, _addr), \
|
||
|
- hash_node) \
|
||
|
- /* compare address and run code only if it matches */ \
|
||
|
- if (ether_addr_equal(_sta->addr, (_addr)))
|
||
|
+#define for_each_sta_info(local, _addr, _sta, _tmp) \
|
||
|
+ rhl_for_each_entry_rcu(_sta, _tmp, \
|
||
|
+ sta_info_hash_lookup(local, _addr), hash_node)
|
||
|
|
||
|
/*
|
||
|
* Get STA info by index, BROKEN!
|
||
|
--- a/net/mac80211/status.c
|
||
|
+++ b/net/mac80211/status.c
|
||
|
@@ -759,8 +759,8 @@ void ieee80211_tx_status(struct ieee8021
|
||
|
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
|
||
|
__le16 fc;
|
||
|
struct ieee80211_supported_band *sband;
|
||
|
+ struct rhlist_head *tmp;
|
||
|
struct sta_info *sta;
|
||
|
- struct rhash_head *tmp;
|
||
|
int retry_count;
|
||
|
int rates_idx;
|
||
|
bool send_to_cooked;
|
||
|
@@ -768,7 +768,6 @@ void ieee80211_tx_status(struct ieee8021
|
||
|
struct ieee80211_bar *bar;
|
||
|
int shift = 0;
|
||
|
int tid = IEEE80211_NUM_TIDS;
|
||
|
- const struct bucket_table *tbl;
|
||
|
|
||
|
rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
|
||
|
|
||
|
@@ -777,9 +776,7 @@ void ieee80211_tx_status(struct ieee8021
|
||
|
sband = local->hw.wiphy->bands[info->band];
|
||
|
fc = hdr->frame_control;
|
||
|
|
||
|
- tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
|
||
|
-
|
||
|
- for_each_sta_info(local, tbl, hdr->addr1, sta, tmp) {
|
||
|
+ for_each_sta_info(local, hdr->addr1, sta, tmp) {
|
||
|
/* skip wrong virtual interface */
|
||
|
if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr))
|
||
|
continue;
|