aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/sta_info.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2015-02-13 15:55:15 -0500
committerJohannes Berg <johannes.berg@intel.com>2015-04-01 04:06:26 -0400
commit7bedd0cfad4e122bc0ddaf3fc955a38c88c95d35 (patch)
treef156540ff672dec031e81a64fc60796dd34ea867 /net/mac80211/sta_info.c
parent9911674fcf1f239ff3c87e56177c4826e33dfd95 (diff)
mac80211: use rhashtable for station table
We currently have a hand-rolled table with 256 entries and are using the last byte of the MAC address as the hash. This hash is obviously very fast, but collisions are easily created and we waste a lot of space in the common case of just connecting as a client to an AP where we just have a single station. The other common case of an AP is also suboptimal due to the size of the hash table and the ease of causing collisions. Convert all of this to use rhashtable with jhash, which gives us the advantage of a far better hash function (with random perturbation to avoid hash collision attacks) and of course that the hash table grows and shrinks dynamically with chain length, improving both cases above. Use a specialised hash function (using jhash, but with fixed length) to achieve better compiler optimisation as suggested by Sergey Ryazanov. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/sta_info.c')
-rw-r--r--net/mac80211/sta_info.c103
1 files changed, 52 insertions, 51 deletions
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index aacaa1a85e63..81cc499fa4a9 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -64,32 +64,20 @@
64 * freed before they are done using it. 64 * freed before they are done using it.
65 */ 65 */
66 66
67static const struct rhashtable_params sta_rht_params = {
68 .nelem_hint = 3, /* start small */
69 .head_offset = offsetof(struct sta_info, hash_node),
70 .key_offset = offsetof(struct sta_info, sta.addr),
71 .key_len = ETH_ALEN,
72 .hashfn = sta_addr_hash,
73};
74
67/* Caller must hold local->sta_mtx */ 75/* Caller must hold local->sta_mtx */
68static int sta_info_hash_del(struct ieee80211_local *local, 76static int sta_info_hash_del(struct ieee80211_local *local,
69 struct sta_info *sta) 77 struct sta_info *sta)
70{ 78{
71 struct sta_info *s; 79 return rhashtable_remove_fast(&local->sta_hash, &sta->hash_node,
72 80 sta_rht_params);
73 s = rcu_dereference_protected(local->sta_hash[STA_HASH(sta->sta.addr)],
74 lockdep_is_held(&local->sta_mtx));
75 if (!s)
76 return -ENOENT;
77 if (s == sta) {
78 rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)],
79 s->hnext);
80 return 0;
81 }
82
83 while (rcu_access_pointer(s->hnext) &&
84 rcu_access_pointer(s->hnext) != sta)
85 s = rcu_dereference_protected(s->hnext,
86 lockdep_is_held(&local->sta_mtx));
87 if (rcu_access_pointer(s->hnext)) {
88 rcu_assign_pointer(s->hnext, sta->hnext);
89 return 0;
90 }
91
92 return -ENOENT;
93} 81}
94 82
95static void __cleanup_single_sta(struct sta_info *sta) 83static void __cleanup_single_sta(struct sta_info *sta)
@@ -159,18 +147,8 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
159 const u8 *addr) 147 const u8 *addr)
160{ 148{
161 struct ieee80211_local *local = sdata->local; 149 struct ieee80211_local *local = sdata->local;
162 struct sta_info *sta;
163 150
164 sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], 151 return rhashtable_lookup_fast(&local->sta_hash, addr, sta_rht_params);
165 lockdep_is_held(&local->sta_mtx));
166 while (sta) {
167 if (sta->sdata == sdata &&
168 ether_addr_equal(sta->sta.addr, addr))
169 break;
170 sta = rcu_dereference_check(sta->hnext,
171 lockdep_is_held(&local->sta_mtx));
172 }
173 return sta;
174} 152}
175 153
176/* 154/*
@@ -182,18 +160,24 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
182{ 160{
183 struct ieee80211_local *local = sdata->local; 161 struct ieee80211_local *local = sdata->local;
184 struct sta_info *sta; 162 struct sta_info *sta;
163 struct rhash_head *tmp;
164 const struct bucket_table *tbl;
185 165
186 sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)], 166 rcu_read_lock();
187 lockdep_is_held(&local->sta_mtx)); 167 tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
188 while (sta) { 168
189 if ((sta->sdata == sdata || 169 for_each_sta_info(local, tbl, addr, sta, tmp) {
190 (sta->sdata->bss && sta->sdata->bss == sdata->bss)) && 170 if (sta->sdata == sdata ||
191 ether_addr_equal(sta->sta.addr, addr)) 171 (sta->sdata->bss && sta->sdata->bss == sdata->bss)) {
192 break; 172 rcu_read_unlock();
193 sta = rcu_dereference_check(sta->hnext, 173 /* this is safe as the caller must already hold
194 lockdep_is_held(&local->sta_mtx)); 174 * another rcu read section or the mutex
175 */
176 return sta;
177 }
195 } 178 }
196 return sta; 179 rcu_read_unlock();
180 return NULL;
197} 181}
198 182
199struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata, 183struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata,
@@ -242,9 +226,8 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
242static void sta_info_hash_add(struct ieee80211_local *local, 226static void sta_info_hash_add(struct ieee80211_local *local,
243 struct sta_info *sta) 227 struct sta_info *sta)
244{ 228{
245 lockdep_assert_held(&local->sta_mtx); 229 rhashtable_insert_fast(&local->sta_hash, &sta->hash_node,
246 sta->hnext = local->sta_hash[STA_HASH(sta->sta.addr)]; 230 sta_rht_params);
247 rcu_assign_pointer(local->sta_hash[STA_HASH(sta->sta.addr)], sta);
248} 231}
249 232
250static void sta_deliver_ps_frames(struct work_struct *wk) 233static void sta_deliver_ps_frames(struct work_struct *wk)
@@ -948,19 +931,32 @@ static void sta_info_cleanup(unsigned long data)
948 round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL)); 931 round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL));
949} 932}
950 933
951void sta_info_init(struct ieee80211_local *local) 934u32 sta_addr_hash(const void *key, u32 length, u32 seed)
935{
936 return jhash(key, ETH_ALEN, seed);
937}
938
939int sta_info_init(struct ieee80211_local *local)
952{ 940{
941 int err;
942
943 err = rhashtable_init(&local->sta_hash, &sta_rht_params);
944 if (err)
945 return err;
946
953 spin_lock_init(&local->tim_lock); 947 spin_lock_init(&local->tim_lock);
954 mutex_init(&local->sta_mtx); 948 mutex_init(&local->sta_mtx);
955 INIT_LIST_HEAD(&local->sta_list); 949 INIT_LIST_HEAD(&local->sta_list);
956 950
957 setup_timer(&local->sta_cleanup, sta_info_cleanup, 951 setup_timer(&local->sta_cleanup, sta_info_cleanup,
958 (unsigned long)local); 952 (unsigned long)local);
953 return 0;
959} 954}
960 955
961void sta_info_stop(struct ieee80211_local *local) 956void sta_info_stop(struct ieee80211_local *local)
962{ 957{
963 del_timer_sync(&local->sta_cleanup); 958 del_timer_sync(&local->sta_cleanup);
959 rhashtable_destroy(&local->sta_hash);
964} 960}
965 961
966 962
@@ -1024,16 +1020,21 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
1024} 1020}
1025 1021
1026struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw, 1022struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw,
1027 const u8 *addr, 1023 const u8 *addr,
1028 const u8 *localaddr) 1024 const u8 *localaddr)
1029{ 1025{
1030 struct sta_info *sta, *nxt; 1026 struct ieee80211_local *local = hw_to_local(hw);
1027 struct sta_info *sta;
1028 struct rhash_head *tmp;
1029 const struct bucket_table *tbl;
1030
1031 tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
1031 1032
1032 /* 1033 /*
1033 * Just return a random station if localaddr is NULL 1034 * Just return a random station if localaddr is NULL
1034 * ... first in list. 1035 * ... first in list.
1035 */ 1036 */
1036 for_each_sta_info(hw_to_local(hw), addr, sta, nxt) { 1037 for_each_sta_info(local, tbl, addr, sta, tmp) {
1037 if (localaddr && 1038 if (localaddr &&
1038 !ether_addr_equal(sta->sdata->vif.addr, localaddr)) 1039 !ether_addr_equal(sta->sdata->vif.addr, localaddr))
1039 continue; 1040 continue;