Commit f361bdde authored by David S. Miller's avatar David S. Miller

Merge branch 'rhashtable-dups'

Herbert Xu says:

====================
rhashtable: rhashtable with duplicate objects

v3 fixes a bug in the remove path that causes the element count
to decrease when it shouldn't, leading to a gigantic hash table
when it underflows.

v2 contains a reworked insertion slowpath to ensure that the
spinlock for the table we're inserting into is taken.

This series contains two patches.  The first adds the rhlist
interface and the second converts mac80211 to use it.  If this
works out I'll then proceed to convert the other insecure_elasticity
users over to this.

I've tested the rhlist code with test_rhashtable but I haven't
tested the mac80211 conversion.  So please give it a go and see
if it still works.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents fd07160b 83e7e4ce
This diff is collapsed.
...@@ -378,22 +378,8 @@ static void rht_deferred_worker(struct work_struct *work) ...@@ -378,22 +378,8 @@ static void rht_deferred_worker(struct work_struct *work)
schedule_work(&ht->run_work); schedule_work(&ht->run_work);
} }
static bool rhashtable_check_elasticity(struct rhashtable *ht, static int rhashtable_insert_rehash(struct rhashtable *ht,
struct bucket_table *tbl, struct bucket_table *tbl)
unsigned int hash)
{
unsigned int elasticity = ht->elasticity;
struct rhash_head *head;
rht_for_each(head, tbl, hash)
if (!--elasticity)
return true;
return false;
}
int rhashtable_insert_rehash(struct rhashtable *ht,
struct bucket_table *tbl)
{ {
struct bucket_table *old_tbl; struct bucket_table *old_tbl;
struct bucket_table *new_tbl; struct bucket_table *new_tbl;
...@@ -439,57 +425,165 @@ int rhashtable_insert_rehash(struct rhashtable *ht, ...@@ -439,57 +425,165 @@ int rhashtable_insert_rehash(struct rhashtable *ht,
return err; return err;
} }
EXPORT_SYMBOL_GPL(rhashtable_insert_rehash);
struct bucket_table *rhashtable_insert_slow(struct rhashtable *ht, static void *rhashtable_lookup_one(struct rhashtable *ht,
const void *key, struct bucket_table *tbl, unsigned int hash,
struct rhash_head *obj, const void *key, struct rhash_head *obj)
struct bucket_table *tbl,
void **data)
{ {
struct rhashtable_compare_arg arg = {
.ht = ht,
.key = key,
};
struct rhash_head __rcu **pprev;
struct rhash_head *head; struct rhash_head *head;
unsigned int hash; int elasticity;
int err;
tbl = rhashtable_last_table(ht, tbl); elasticity = ht->elasticity;
hash = head_hashfn(ht, tbl, obj); pprev = &tbl->buckets[hash];
spin_lock_nested(rht_bucket_lock(tbl, hash), SINGLE_DEPTH_NESTING); rht_for_each(head, tbl, hash) {
struct rhlist_head *list;
err = -EEXIST; struct rhlist_head *plist;
if (key) {
*data = rhashtable_lookup_fast(ht, key, ht->p); elasticity--;
if (*data) if (!key ||
goto exit; (ht->p.obj_cmpfn ?
ht->p.obj_cmpfn(&arg, rht_obj(ht, head)) :
rhashtable_compare(&arg, rht_obj(ht, head))))
continue;
if (!ht->rhlist)
return rht_obj(ht, head);
list = container_of(obj, struct rhlist_head, rhead);
plist = container_of(head, struct rhlist_head, rhead);
RCU_INIT_POINTER(list->next, plist);
head = rht_dereference_bucket(head->next, tbl, hash);
RCU_INIT_POINTER(list->rhead.next, head);
rcu_assign_pointer(*pprev, obj);
return NULL;
} }
err = -E2BIG; if (elasticity <= 0)
if (unlikely(rht_grow_above_max(ht, tbl))) return ERR_PTR(-EAGAIN);
goto exit;
return ERR_PTR(-ENOENT);
}
static struct bucket_table *rhashtable_insert_one(struct rhashtable *ht,
struct bucket_table *tbl,
unsigned int hash,
struct rhash_head *obj,
void *data)
{
struct bucket_table *new_tbl;
struct rhash_head *head;
if (!IS_ERR_OR_NULL(data))
return ERR_PTR(-EEXIST);
err = -EAGAIN; if (PTR_ERR(data) != -EAGAIN && PTR_ERR(data) != -ENOENT)
if (rhashtable_check_elasticity(ht, tbl, hash) || return ERR_CAST(data);
rht_grow_above_100(ht, tbl))
goto exit;
err = 0; new_tbl = rcu_dereference(tbl->future_tbl);
if (new_tbl)
return new_tbl;
if (PTR_ERR(data) != -ENOENT)
return ERR_CAST(data);
if (unlikely(rht_grow_above_max(ht, tbl)))
return ERR_PTR(-E2BIG);
if (unlikely(rht_grow_above_100(ht, tbl)))
return ERR_PTR(-EAGAIN);
head = rht_dereference_bucket(tbl->buckets[hash], tbl, hash); head = rht_dereference_bucket(tbl->buckets[hash], tbl, hash);
RCU_INIT_POINTER(obj->next, head); RCU_INIT_POINTER(obj->next, head);
if (ht->rhlist) {
struct rhlist_head *list;
list = container_of(obj, struct rhlist_head, rhead);
RCU_INIT_POINTER(list->next, NULL);
}
rcu_assign_pointer(tbl->buckets[hash], obj); rcu_assign_pointer(tbl->buckets[hash], obj);
atomic_inc(&ht->nelems); atomic_inc(&ht->nelems);
if (rht_grow_above_75(ht, tbl))
schedule_work(&ht->run_work);
exit: return NULL;
spin_unlock(rht_bucket_lock(tbl, hash)); }
if (err == 0) static void *rhashtable_try_insert(struct rhashtable *ht, const void *key,
return NULL; struct rhash_head *obj)
else if (err == -EAGAIN) {
return tbl; struct bucket_table *new_tbl;
else struct bucket_table *tbl;
return ERR_PTR(err); unsigned int hash;
spinlock_t *lock;
void *data;
tbl = rcu_dereference(ht->tbl);
/* All insertions must grab the oldest table containing
* the hashed bucket that is yet to be rehashed.
*/
for (;;) {
hash = rht_head_hashfn(ht, tbl, obj, ht->p);
lock = rht_bucket_lock(tbl, hash);
spin_lock_bh(lock);
if (tbl->rehash <= hash)
break;
spin_unlock_bh(lock);
tbl = rcu_dereference(tbl->future_tbl);
}
data = rhashtable_lookup_one(ht, tbl, hash, key, obj);
new_tbl = rhashtable_insert_one(ht, tbl, hash, obj, data);
if (PTR_ERR(new_tbl) != -EEXIST)
data = ERR_CAST(new_tbl);
while (!IS_ERR_OR_NULL(new_tbl)) {
tbl = new_tbl;
hash = rht_head_hashfn(ht, tbl, obj, ht->p);
spin_lock_nested(rht_bucket_lock(tbl, hash),
SINGLE_DEPTH_NESTING);
data = rhashtable_lookup_one(ht, tbl, hash, key, obj);
new_tbl = rhashtable_insert_one(ht, tbl, hash, obj, data);
if (PTR_ERR(new_tbl) != -EEXIST)
data = ERR_CAST(new_tbl);
spin_unlock(rht_bucket_lock(tbl, hash));
}
spin_unlock_bh(lock);
if (PTR_ERR(data) == -EAGAIN)
data = ERR_PTR(rhashtable_insert_rehash(ht, tbl) ?:
-EAGAIN);
return data;
}
void *rhashtable_insert_slow(struct rhashtable *ht, const void *key,
struct rhash_head *obj)
{
void *data;
do {
rcu_read_lock();
data = rhashtable_try_insert(ht, key, obj);
rcu_read_unlock();
} while (PTR_ERR(data) == -EAGAIN);
return data;
} }
EXPORT_SYMBOL_GPL(rhashtable_insert_slow); EXPORT_SYMBOL_GPL(rhashtable_insert_slow);
...@@ -593,11 +687,16 @@ EXPORT_SYMBOL_GPL(rhashtable_walk_start); ...@@ -593,11 +687,16 @@ EXPORT_SYMBOL_GPL(rhashtable_walk_start);
void *rhashtable_walk_next(struct rhashtable_iter *iter) void *rhashtable_walk_next(struct rhashtable_iter *iter)
{ {
struct bucket_table *tbl = iter->walker.tbl; struct bucket_table *tbl = iter->walker.tbl;
struct rhlist_head *list = iter->list;
struct rhashtable *ht = iter->ht; struct rhashtable *ht = iter->ht;
struct rhash_head *p = iter->p; struct rhash_head *p = iter->p;
bool rhlist = ht->rhlist;
if (p) { if (p) {
p = rht_dereference_bucket_rcu(p->next, tbl, iter->slot); if (!rhlist || !(list = rcu_dereference(list->next))) {
p = rcu_dereference(p->next);
list = container_of(p, struct rhlist_head, rhead);
}
goto next; goto next;
} }
...@@ -605,6 +704,18 @@ void *rhashtable_walk_next(struct rhashtable_iter *iter) ...@@ -605,6 +704,18 @@ void *rhashtable_walk_next(struct rhashtable_iter *iter)
int skip = iter->skip; int skip = iter->skip;
rht_for_each_rcu(p, tbl, iter->slot) { rht_for_each_rcu(p, tbl, iter->slot) {
if (rhlist) {
list = container_of(p, struct rhlist_head,
rhead);
do {
if (!skip)
goto next;
skip--;
list = rcu_dereference(list->next);
} while (list);
continue;
}
if (!skip) if (!skip)
break; break;
skip--; skip--;
...@@ -614,7 +725,8 @@ void *rhashtable_walk_next(struct rhashtable_iter *iter) ...@@ -614,7 +725,8 @@ void *rhashtable_walk_next(struct rhashtable_iter *iter)
if (!rht_is_a_nulls(p)) { if (!rht_is_a_nulls(p)) {
iter->skip++; iter->skip++;
iter->p = p; iter->p = p;
return rht_obj(ht, p); iter->list = list;
return rht_obj(ht, rhlist ? &list->rhead : p);
} }
iter->skip = 0; iter->skip = 0;
...@@ -802,6 +914,48 @@ int rhashtable_init(struct rhashtable *ht, ...@@ -802,6 +914,48 @@ int rhashtable_init(struct rhashtable *ht,
} }
EXPORT_SYMBOL_GPL(rhashtable_init); EXPORT_SYMBOL_GPL(rhashtable_init);
/**
* rhltable_init - initialize a new hash list table
* @hlt: hash list table to be initialized
* @params: configuration parameters
*
* Initializes a new hash list table.
*
* See documentation for rhashtable_init.
*/
int rhltable_init(struct rhltable *hlt, const struct rhashtable_params *params)
{
int err;
/* No rhlist NULLs marking for now. */
if (params->nulls_base)
return -EINVAL;
err = rhashtable_init(&hlt->ht, params);
hlt->ht.rhlist = true;
return err;
}
EXPORT_SYMBOL_GPL(rhltable_init);
static void rhashtable_free_one(struct rhashtable *ht, struct rhash_head *obj,
void (*free_fn)(void *ptr, void *arg),
void *arg)
{
struct rhlist_head *list;
if (!ht->rhlist) {
free_fn(rht_obj(ht, obj), arg);
return;
}
list = container_of(obj, struct rhlist_head, rhead);
do {
obj = &list->rhead;
list = rht_dereference(list->next, ht);
free_fn(rht_obj(ht, obj), arg);
} while (list);
}
/** /**
* rhashtable_free_and_destroy - free elements and destroy hash table * rhashtable_free_and_destroy - free elements and destroy hash table
* @ht: the hash table to destroy * @ht: the hash table to destroy
...@@ -839,7 +993,7 @@ void rhashtable_free_and_destroy(struct rhashtable *ht, ...@@ -839,7 +993,7 @@ void rhashtable_free_and_destroy(struct rhashtable *ht,
pos = next, pos = next,
next = !rht_is_a_nulls(pos) ? next = !rht_is_a_nulls(pos) ?
rht_dereference(pos->next, ht) : NULL) rht_dereference(pos->next, ht) : NULL)
free_fn(rht_obj(ht, pos), arg); rhashtable_free_one(ht, pos, free_fn, arg);
} }
} }
......
...@@ -1213,7 +1213,7 @@ struct ieee80211_local { ...@@ -1213,7 +1213,7 @@ struct ieee80211_local {
spinlock_t tim_lock; spinlock_t tim_lock;
unsigned long num_sta; unsigned long num_sta;
struct list_head sta_list; struct list_head sta_list;
struct rhashtable sta_hash; struct rhltable sta_hash;
struct timer_list sta_cleanup; struct timer_list sta_cleanup;
int sta_generation; int sta_generation;
......
...@@ -4003,7 +4003,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, ...@@ -4003,7 +4003,7 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
__le16 fc; __le16 fc;
struct ieee80211_rx_data rx; struct ieee80211_rx_data rx;
struct ieee80211_sub_if_data *prev; struct ieee80211_sub_if_data *prev;
struct rhash_head *tmp; struct rhlist_head *tmp;
int err = 0; int err = 0;
fc = ((struct ieee80211_hdr *)skb->data)->frame_control; fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
...@@ -4046,13 +4046,10 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw, ...@@ -4046,13 +4046,10 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
goto out; goto out;
} else if (ieee80211_is_data(fc)) { } else if (ieee80211_is_data(fc)) {
struct sta_info *sta, *prev_sta; struct sta_info *sta, *prev_sta;
const struct bucket_table *tbl;
prev_sta = NULL; prev_sta = NULL;
tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash); for_each_sta_info(local, hdr->addr2, sta, tmp) {
for_each_sta_info(local, tbl, hdr->addr2, sta, tmp) {
if (!prev_sta) { if (!prev_sta) {
prev_sta = sta; prev_sta = sta;
continue; continue;
......
...@@ -67,12 +67,10 @@ ...@@ -67,12 +67,10 @@
static const struct rhashtable_params sta_rht_params = { static const struct rhashtable_params sta_rht_params = {
.nelem_hint = 3, /* start small */ .nelem_hint = 3, /* start small */
.insecure_elasticity = true, /* Disable chain-length checks. */
.automatic_shrinking = true, .automatic_shrinking = true,
.head_offset = offsetof(struct sta_info, hash_node), .head_offset = offsetof(struct sta_info, hash_node),
.key_offset = offsetof(struct sta_info, addr), .key_offset = offsetof(struct sta_info, addr),
.key_len = ETH_ALEN, .key_len = ETH_ALEN,
.hashfn = sta_addr_hash,
.max_size = CONFIG_MAC80211_STA_HASH_MAX_SIZE, .max_size = CONFIG_MAC80211_STA_HASH_MAX_SIZE,
}; };
...@@ -80,8 +78,8 @@ static const struct rhashtable_params sta_rht_params = { ...@@ -80,8 +78,8 @@ static const struct rhashtable_params sta_rht_params = {
static int sta_info_hash_del(struct ieee80211_local *local, static int sta_info_hash_del(struct ieee80211_local *local,
struct sta_info *sta) struct sta_info *sta)
{ {
return rhashtable_remove_fast(&local->sta_hash, &sta->hash_node, return rhltable_remove(&local->sta_hash, &sta->hash_node,
sta_rht_params); sta_rht_params);
} }
static void __cleanup_single_sta(struct sta_info *sta) static void __cleanup_single_sta(struct sta_info *sta)
...@@ -157,19 +155,22 @@ 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); 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 */ /* protected by RCU */
struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
const u8 *addr) const u8 *addr)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct rhlist_head *tmp;
struct sta_info *sta; struct sta_info *sta;
struct rhash_head *tmp;
const struct bucket_table *tbl;
rcu_read_lock(); rcu_read_lock();
tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash); for_each_sta_info(local, addr, sta, tmp) {
for_each_sta_info(local, tbl, addr, sta, tmp) {
if (sta->sdata == sdata) { if (sta->sdata == sdata) {
rcu_read_unlock(); rcu_read_unlock();
/* this is safe as the caller must already hold /* 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, ...@@ -190,14 +191,11 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
const u8 *addr) const u8 *addr)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct rhlist_head *tmp;
struct sta_info *sta; struct sta_info *sta;
struct rhash_head *tmp;
const struct bucket_table *tbl;
rcu_read_lock(); rcu_read_lock();
tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash); for_each_sta_info(local, addr, sta, tmp) {
for_each_sta_info(local, tbl, addr, sta, tmp) {
if (sta->sdata == sdata || if (sta->sdata == sdata ||
(sta->sdata->bss && sta->sdata->bss == sdata->bss)) { (sta->sdata->bss && sta->sdata->bss == sdata->bss)) {
rcu_read_unlock(); rcu_read_unlock();
...@@ -263,8 +261,8 @@ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) ...@@ -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, static int sta_info_hash_add(struct ieee80211_local *local,
struct sta_info *sta) struct sta_info *sta)
{ {
return rhashtable_insert_fast(&local->sta_hash, &sta->hash_node, return rhltable_insert(&local->sta_hash, &sta->hash_node,
sta_rht_params); sta_rht_params);
} }
static void sta_deliver_ps_frames(struct work_struct *wk) static void sta_deliver_ps_frames(struct work_struct *wk)
...@@ -453,9 +451,9 @@ static int sta_info_insert_check(struct sta_info *sta) ...@@ -453,9 +451,9 @@ static int sta_info_insert_check(struct sta_info *sta)
is_multicast_ether_addr(sta->sta.addr))) is_multicast_ether_addr(sta->sta.addr)))
return -EINVAL; return -EINVAL;
/* Strictly speaking this isn't necessary as we hold the mutex, but /* The RCU read lock is required by rhashtable due to
* the rhashtable code can't really deal with that distinction. We * asynchronous resize/rehash. We also require the mutex
* do require the mutex for correctness though. * for correctness.
*/ */
rcu_read_lock(); rcu_read_lock();
lockdep_assert_held(&sdata->local->sta_mtx); lockdep_assert_held(&sdata->local->sta_mtx);
...@@ -1043,16 +1041,11 @@ static void sta_info_cleanup(unsigned long data) ...@@ -1043,16 +1041,11 @@ static void sta_info_cleanup(unsigned long data)
round_jiffies(jiffies + STA_INFO_CLEANUP_INTERVAL)); 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 sta_info_init(struct ieee80211_local *local)
{ {
int err; int err;
err = rhashtable_init(&local->sta_hash, &sta_rht_params); err = rhltable_init(&local->sta_hash, &sta_rht_params);
if (err) if (err)
return err; return err;
...@@ -1068,7 +1061,7 @@ int sta_info_init(struct ieee80211_local *local) ...@@ -1068,7 +1061,7 @@ int sta_info_init(struct ieee80211_local *local)
void sta_info_stop(struct ieee80211_local *local) void sta_info_stop(struct ieee80211_local *local)
{ {
del_timer_sync(&local->sta_cleanup); 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, ...@@ -1138,17 +1131,14 @@ struct ieee80211_sta *ieee80211_find_sta_by_ifaddr(struct ieee80211_hw *hw,
const u8 *localaddr) const u8 *localaddr)
{ {
struct ieee80211_local *local = hw_to_local(hw); struct ieee80211_local *local = hw_to_local(hw);
struct rhlist_head *tmp;
struct sta_info *sta; 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 * Just return a random station if localaddr is NULL
* ... first in list. * ... first in list.
*/ */
for_each_sta_info(local, tbl, addr, sta, tmp) { for_each_sta_info(local, addr, sta, tmp) {
if (localaddr && if (localaddr &&
!ether_addr_equal(sta->sdata->vif.addr, localaddr)) !ether_addr_equal(sta->sdata->vif.addr, localaddr))
continue; continue;
......
...@@ -455,7 +455,7 @@ struct sta_info { ...@@ -455,7 +455,7 @@ struct sta_info {
/* General information, mostly static */ /* General information, mostly static */
struct list_head list, free_list; struct list_head list, free_list;
struct rcu_head rcu_head; struct rcu_head rcu_head;
struct rhash_head hash_node; struct rhlist_head hash_node;
u8 addr[ETH_ALEN]; u8 addr[ETH_ALEN];
struct ieee80211_local *local; struct ieee80211_local *local;
struct ieee80211_sub_if_data *sdata; struct ieee80211_sub_if_data *sdata;
...@@ -638,6 +638,9 @@ rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid) ...@@ -638,6 +638,9 @@ rcu_dereference_protected_tid_tx(struct sta_info *sta, int tid)
*/ */
#define STA_INFO_CLEANUP_INTERVAL (10 * HZ) #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. * 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, ...@@ -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, struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
const u8 *addr); const u8 *addr);
u32 sta_addr_hash(const void *key, u32 length, u32 seed); #define for_each_sta_info(local, _addr, _sta, _tmp) \
rhl_for_each_entry_rcu(_sta, _tmp, \
#define _sta_bucket_idx(_tbl, _a) \ sta_info_hash_lookup(local, _addr), hash_node)
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)))
/* /*
* Get STA info by index, BROKEN! * Get STA info by index, BROKEN!
......
...@@ -746,8 +746,8 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) ...@@ -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); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
__le16 fc; __le16 fc;
struct ieee80211_supported_band *sband; struct ieee80211_supported_band *sband;
struct rhlist_head *tmp;
struct sta_info *sta; struct sta_info *sta;
struct rhash_head *tmp;
int retry_count; int retry_count;
int rates_idx; int rates_idx;
bool send_to_cooked; bool send_to_cooked;
...@@ -755,7 +755,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb) ...@@ -755,7 +755,6 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
struct ieee80211_bar *bar; struct ieee80211_bar *bar;
int shift = 0; int shift = 0;
int tid = IEEE80211_NUM_TIDS; int tid = IEEE80211_NUM_TIDS;
const struct bucket_table *tbl;
rates_idx = ieee80211_tx_get_rates(hw, info, &retry_count); 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) ...@@ -764,9 +763,7 @@ void ieee80211_tx_status(struct ieee80211_hw *hw, struct sk_buff *skb)
sband = local->hw.wiphy->bands[info->band]; sband = local->hw.wiphy->bands[info->band];
fc = hdr->frame_control; fc = hdr->frame_control;
tbl = rht_dereference_rcu(local->sta_hash.tbl, &local->sta_hash); for_each_sta_info(local, hdr->addr1, sta, tmp) {
for_each_sta_info(local, tbl, hdr->addr1, sta, tmp) {
/* skip wrong virtual interface */ /* skip wrong virtual interface */
if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr)) if (!ether_addr_equal(hdr->addr2, sta->sdata->vif.addr))
continue; continue;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment