mac80211: Use rhltable instead of rhashtable
authorHerbert Xu <herbert@gondor.apana.org.au>
Mon, 19 Sep 2016 11:00:10 +0000 (19:00 +0800)
committerDavid S. Miller <davem@davemloft.net>
Tue, 20 Sep 2016 08:43:36 +0000 (04:43 -0400)
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>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/mac80211/ieee80211_i.h
net/mac80211/rx.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h
net/mac80211/status.c

index c71c73594790747115451e270552e5dc2e4d50d5..e496dee5af089322f42eb273091c754b77b1d192 100644 (file)
@@ -1213,7 +1213,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;
 
index e796060b7c5ee219d50ee6c58a732281edd3eb72..f7cf342bab523c52d4a39d63890e4c5997a6db5f 100644 (file)
@@ -4003,7 +4003,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
        __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;
@@ -4046,13 +4046,10 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
                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;
index 1b1b28ff4fdb5bbefad5e61783eac4e53da06a60..c803e2cb58bc6f893b1646f297956162311c3da4 100644 (file)
 
 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 = CONFIG_MAC80211_STA_HASH_MAX_SIZE,
 };
 
@@ -80,8 +78,8 @@ static const struct rhashtable_params sta_rht_params = {
 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 sta_info *sta)
        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 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 ||
                    (sta->sdata->bss && sta->sdata->bss == sdata->bss)) {
                        rcu_read_unlock();
@@ -263,8 +261,8 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta)
 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 sta_info *sta)
                    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 long data)
                  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 *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_by_ifaddr(struct ieee80211_hw *hw,
                                                   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;
index 530231b73278cd1518ecd994aea02877eb7f11da..ed5fcb984a01a51ce08caa32101b50265509eb3f 100644 (file)
@@ -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 sta_info *sta, int tid)
  */
 #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 ieee80211_sub_if_data *sdata,
 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!
index ea39f8a7baf3816fe843fe8a6c364149a38b3207..ddf71c648cab008baaff8ed430b9b222410f8ce1 100644 (file)
@@ -746,8 +746,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
        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;
@@ -755,7 +755,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
        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);
 
@@ -764,9 +763,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
        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;