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 c71c735..e496dee 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 e796060..f7cf342 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 1b1b28f..c803e2c 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 530231b..ed5fcb9 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 ea39f8a..ddf71c6 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;