aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--net/mac80211/ieee80211_i.h3
-rw-r--r--net/mac80211/main.c9
-rw-r--r--net/mac80211/rx.c9
-rw-r--r--net/mac80211/sta_info.c103
-rw-r--r--net/mac80211/sta_info.h38
-rw-r--r--net/mac80211/status.c8
6 files changed, 85 insertions, 85 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 487f5e2a9283..3c1512b0442c 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -26,6 +26,7 @@
26#include <linux/etherdevice.h> 26#include <linux/etherdevice.h>
27#include <linux/leds.h> 27#include <linux/leds.h>
28#include <linux/idr.h> 28#include <linux/idr.h>
29#include <linux/rhashtable.h>
29#include <net/ieee80211_radiotap.h> 30#include <net/ieee80211_radiotap.h>
30#include <net/cfg80211.h> 31#include <net/cfg80211.h>
31#include <net/mac80211.h> 32#include <net/mac80211.h>
@@ -1187,7 +1188,7 @@ struct ieee80211_local {
1187 spinlock_t tim_lock; 1188 spinlock_t tim_lock;
1188 unsigned long num_sta; 1189 unsigned long num_sta;
1189 struct list_head sta_list; 1190 struct list_head sta_list;
1190 struct sta_info __rcu *sta_hash[STA_HASH_SIZE]; 1191 struct rhashtable sta_hash;
1191 struct timer_list sta_cleanup; 1192 struct timer_list sta_cleanup;
1192 int sta_generation; 1193 int sta_generation;
1193 1194
diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index 4977967c8b00..51e0332a4589 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -557,6 +557,9 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
557 557
558 local = wiphy_priv(wiphy); 558 local = wiphy_priv(wiphy);
559 559
560 if (sta_info_init(local))
561 goto err_free;
562
560 local->hw.wiphy = wiphy; 563 local->hw.wiphy = wiphy;
561 564
562 local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN); 565 local->hw.priv = (char *)local + ALIGN(sizeof(*local), NETDEV_ALIGN);
@@ -629,8 +632,6 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
629 spin_lock_init(&local->ack_status_lock); 632 spin_lock_init(&local->ack_status_lock);
630 idr_init(&local->ack_status_frames); 633 idr_init(&local->ack_status_frames);
631 634
632 sta_info_init(local);
633
634 for (i = 0; i < IEEE80211_MAX_QUEUES; i++) { 635 for (i = 0; i < IEEE80211_MAX_QUEUES; i++) {
635 skb_queue_head_init(&local->pending[i]); 636 skb_queue_head_init(&local->pending[i]);
636 atomic_set(&local->agg_queue_stop[i], 0); 637 atomic_set(&local->agg_queue_stop[i], 0);
@@ -650,6 +651,9 @@ struct ieee80211_hw *ieee80211_alloc_hw_nm(size_t priv_data_len,
650 ieee80211_roc_setup(local); 651 ieee80211_roc_setup(local);
651 652
652 return &local->hw; 653 return &local->hw;
654 err_free:
655 wiphy_free(wiphy);
656 return NULL;
653} 657}
654EXPORT_SYMBOL(ieee80211_alloc_hw_nm); 658EXPORT_SYMBOL(ieee80211_alloc_hw_nm);
655 659
@@ -1173,7 +1177,6 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
1173 1177
1174 destroy_workqueue(local->workqueue); 1178 destroy_workqueue(local->workqueue);
1175 wiphy_unregister(local->hw.wiphy); 1179 wiphy_unregister(local->hw.wiphy);
1176 sta_info_stop(local);
1177 ieee80211_wep_free(local); 1180 ieee80211_wep_free(local);
1178 ieee80211_led_exit(local); 1181 ieee80211_led_exit(local);
1179 kfree(local->int_scan_req); 1182 kfree(local->int_scan_req);
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 4f7b922cfda4..5b60bcf00ec3 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -3423,7 +3423,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
3423 __le16 fc; 3423 __le16 fc;
3424 struct ieee80211_rx_data rx; 3424 struct ieee80211_rx_data rx;
3425 struct ieee80211_sub_if_data *prev; 3425 struct ieee80211_sub_if_data *prev;
3426 struct sta_info *sta, *tmp, *prev_sta; 3426 struct sta_info *sta, *prev_sta;
3427 struct rhash_head *tmp;
3427 int err = 0; 3428 int err = 0;
3428 3429
3429 fc = ((struct ieee80211_hdr *)skb->data)->frame_control; 3430 fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
@@ -3458,9 +3459,13 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
3458 ieee80211_scan_rx(local, skb); 3459 ieee80211_scan_rx(local, skb);
3459 3460
3460 if (ieee80211_is_data(fc)) { 3461 if (ieee80211_is_data(fc)) {
3462 const struct bucket_table *tbl;
3463
3461 prev_sta = NULL; 3464 prev_sta = NULL;
3462 3465
3463 for_each_sta_info(local, hdr->addr2, sta, tmp) { 3466 tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
3467
3468 for_each_sta_info(local, tbl, hdr->addr2, sta, tmp) {
3464 if (!prev_sta) { 3469 if (!prev_sta) {
3465 prev_sta = sta; 3470 prev_sta = sta;
3466 continue; 3471 continue;
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;
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 248f56e59ebc..97f25b9e52be 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -16,6 +16,7 @@
16#include <linux/workqueue.h> 16#include <linux/workqueue.h>
17#include <linux/average.h> 17#include <linux/average.h>
18#include <linux/etherdevice.h> 18#include <linux/etherdevice.h>
19#include <linux/rhashtable.h>
19#include "key.h" 20#include "key.h"
20 21
21/** 22/**
@@ -246,7 +247,7 @@ struct sta_ampdu_mlme {
246 * 247 *
247 * @list: global linked list entry 248 * @list: global linked list entry
248 * @free_list: list entry for keeping track of stations to free 249 * @free_list: list entry for keeping track of stations to free
249 * @hnext: hash table linked list pointer 250 * @hash_node: hash node for rhashtable
250 * @local: pointer to the global information 251 * @local: pointer to the global information
251 * @sdata: virtual interface this station belongs to 252 * @sdata: virtual interface this station belongs to
252 * @ptk: peer keys negotiated with this station, if any 253 * @ptk: peer keys negotiated with this station, if any
@@ -339,7 +340,7 @@ struct sta_info {
339 /* General information, mostly static */ 340 /* General information, mostly static */
340 struct list_head list, free_list; 341 struct list_head list, free_list;
341 struct rcu_head rcu_head; 342 struct rcu_head rcu_head;
342 struct sta_info __rcu *hnext; 343 struct rhash_head hash_node;
343 struct ieee80211_local *local; 344 struct ieee80211_local *local;
344 struct ieee80211_sub_if_data *sdata; 345 struct ieee80211_sub_if_data *sdata;
345 struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS]; 346 struct ieee80211_key __rcu *gtk[NUM_DEFAULT_KEYS + NUM_DEFAULT_MGMT_KEYS];
@@ -535,10 +536,6 @@ rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid)
535 lockdep_is_held(&sta->ampdu_mlme.mtx)); 536 lockdep_is_held(&sta->ampdu_mlme.mtx));
536} 537}
537 538
538#define STA_HASH_SIZE 256
539#define STA_HASH(sta) (sta[5])
540
541
542/* Maximum number of frames to buffer per power saving station per AC */ 539/* Maximum number of frames to buffer per power saving station per AC */
543#define STA_MAX_TX_BUFFER 64 540#define STA_MAX_TX_BUFFER 64
544 541
@@ -559,26 +556,15 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
559struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata, 556struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
560 const u8 *addr); 557 const u8 *addr);
561 558
562static inline 559u32 sta_addr_hash(const void *key, u32 length, u32 seed);
563void for_each_sta_info_type_check(struct ieee80211_local *local, 560
564 const u8 *addr, 561#define _sta_bucket_idx(_tbl, _a) \
565 struct sta_info *sta, 562 rht_bucket_index(_tbl, sta_addr_hash(_a, ETH_ALEN, (_tbl)->hash_rnd))
566 struct sta_info *nxt)
567{
568}
569 563
570#define for_each_sta_info(local, _addr, _sta, nxt) \ 564#define for_each_sta_info(local, tbl, _addr, _sta, _tmp) \
571 for ( /* initialise loop */ \ 565 rht_for_each_entry_rcu(_sta, _tmp, tbl, \
572 _sta = rcu_dereference(local->sta_hash[STA_HASH(_addr)]),\ 566 _sta_bucket_idx(tbl, _addr), \
573 nxt = _sta ? rcu_dereference(_sta->hnext) : NULL; \ 567 hash_node) \
574 /* typecheck */ \
575 for_each_sta_info_type_check(local, (_addr), _sta, nxt),\
576 /* continue condition */ \
577 _sta; \
578 /* advance loop */ \
579 _sta = nxt, \
580 nxt = _sta ? rcu_dereference(_sta->hnext) : NULL \
581 ) \
582 /* compare address and run code only if it matches */ \ 568 /* compare address and run code only if it matches */ \
583 if (ether_addr_equal(_sta->sta.addr, (_addr))) 569 if (ether_addr_equal(_sta->sta.addr, (_addr)))
584 570
@@ -615,7 +601,7 @@ int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata,
615 601
616void sta_info_recalc_tim(struct sta_info *sta); 602void sta_info_recalc_tim(struct sta_info *sta);
617 603
618void sta_info_init(struct ieee80211_local *local); 604int sta_info_init(struct ieee80211_local *local);
619void sta_info_stop(struct ieee80211_local *local); 605void sta_info_stop(struct ieee80211_local *local);
620 606
621/** 607/**
diff --git a/net/mac80211/status.c b/net/mac80211/status.c
index 2c51742428d5..005fdbe39a8b 100644
--- a/net/mac80211/status.c
+++ b/net/mac80211/status.c
@@ -654,7 +654,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
654 struct ieee80211_supported_band *sband; 654 struct ieee80211_supported_band *sband;
655 struct ieee80211_sub_if_data *sdata; 655 struct ieee80211_sub_if_data *sdata;
656 struct net_device *prev_dev = NULL; 656 struct net_device *prev_dev = NULL;
657 struct sta_info *sta, *tmp; 657 struct sta_info *sta;
658 struct rhash_head *tmp;
658 int retry_count; 659 int retry_count;
659 int rates_idx; 660 int rates_idx;
660 bool send_to_cooked; 661 bool send_to_cooked;
@@ -663,6 +664,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
663 int rtap_len; 664 int rtap_len;
664 int shift = 0; 665 int shift = 0;
665 int tid = IEEE80211_NUM_TIDS; 666 int tid = IEEE80211_NUM_TIDS;
667 const struct bucket_table *tbl;
666 668
667 rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count); 669 rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count);
668 670
@@ -671,7 +673,9 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
671 sband = local->hw.wiphy->bands[info->band]; 673 sband = local->hw.wiphy->bands[info->band];
672 fc = hdr->frame_control; 674 fc = hdr->frame_control;
673 675
674 for_each_sta_info(local, hdr->addr1, sta, tmp) { 676 tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash);
677
678 for_each_sta_info(local, tbl, hdr->addr1, sta, tmp) {
675 /* skip wrong virtual interface */ 679 /* skip wrong virtual interface */
676 if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr)) 680 if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr))
677 continue; 681 continue;