Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next into for-davem

This commit is contained in:
John W. Linville
2011-08-29 14:52:20 -04:00
121 changed files with 5629 additions and 1506 deletions

View File

@ -96,6 +96,27 @@ struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)],
lockdep_is_held(&local->sta_lock) ||
lockdep_is_held(&local->sta_mtx));
while (sta) {
if (sta->sdata == sdata && !sta->dummy &&
memcmp(sta->sta.addr, addr, ETH_ALEN) == 0)
break;
sta = rcu_dereference_check(sta->hnext,
lockdep_is_held(&local->sta_lock) ||
lockdep_is_held(&local->sta_mtx));
}
return sta;
}
/* get a station info entry even if it is a dummy station*/
struct sta_info *sta_info_get_rx(struct ieee80211_sub_if_data *sdata,
const u8 *addr)
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)],
lockdep_is_held(&local->sta_lock) ||
lockdep_is_held(&local->sta_mtx));
@ -120,6 +141,32 @@ struct sta_info *sta_info_get_bss(struct ieee80211_sub_if_data *sdata,
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)],
lockdep_is_held(&local->sta_lock) ||
lockdep_is_held(&local->sta_mtx));
while (sta) {
if ((sta->sdata == sdata ||
(sta->sdata->bss && sta->sdata->bss == sdata->bss)) &&
!sta->dummy &&
memcmp(sta->sta.addr, addr, ETH_ALEN) == 0)
break;
sta = rcu_dereference_check(sta->hnext,
lockdep_is_held(&local->sta_lock) ||
lockdep_is_held(&local->sta_mtx));
}
return sta;
}
/*
* Get sta info either from the specified interface
* or from one of its vlans (including dummy stations)
*/
struct sta_info *sta_info_get_bss_rx(struct ieee80211_sub_if_data *sdata,
const u8 *addr)
{
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
sta = rcu_dereference_check(local->sta_hash[STA_HASH(addr)],
lockdep_is_held(&local->sta_lock) ||
lockdep_is_held(&local->sta_mtx));
@ -280,7 +327,8 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
return sta;
}
static int sta_info_finish_insert(struct sta_info *sta, bool async)
static int sta_info_finish_insert(struct sta_info *sta,
bool async, bool dummy_reinsert)
{
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
@ -290,51 +338,58 @@ static int sta_info_finish_insert(struct sta_info *sta, bool async)
lockdep_assert_held(&local->sta_mtx);
/* notify driver */
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data,
u.ap);
err = drv_sta_add(local, sdata, &sta->sta);
if (err) {
if (!async)
return err;
printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to driver (%d)"
" - keeping it anyway.\n",
sdata->name, sta->sta.addr, err);
} else {
sta->uploaded = true;
if (!sta->dummy || dummy_reinsert) {
/* notify driver */
if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
sdata = container_of(sdata->bss,
struct ieee80211_sub_if_data,
u.ap);
err = drv_sta_add(local, sdata, &sta->sta);
if (err) {
if (!async)
return err;
printk(KERN_DEBUG "%s: failed to add IBSS STA %pM to "
"driver (%d) - keeping it anyway.\n",
sdata->name, sta->sta.addr, err);
} else {
sta->uploaded = true;
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
if (async)
wiphy_debug(local->hw.wiphy,
"Finished adding IBSS STA %pM\n",
sta->sta.addr);
if (async)
wiphy_debug(local->hw.wiphy,
"Finished adding IBSS STA %pM\n",
sta->sta.addr);
#endif
}
sdata = sta->sdata;
}
sdata = sta->sdata;
if (!dummy_reinsert) {
if (!async) {
local->num_sta++;
local->sta_generation++;
smp_mb();
if (!async) {
local->num_sta++;
local->sta_generation++;
smp_mb();
/* make the station visible */
spin_lock_irqsave(&local->sta_lock, flags);
sta_info_hash_add(local, sta);
spin_unlock_irqrestore(&local->sta_lock, flags);
}
/* make the station visible */
spin_lock_irqsave(&local->sta_lock, flags);
sta_info_hash_add(local, sta);
spin_unlock_irqrestore(&local->sta_lock, flags);
list_add(&sta->list, &local->sta_list);
} else {
sta->dummy = false;
}
list_add(&sta->list, &local->sta_list);
ieee80211_sta_debugfs_add(sta);
rate_control_add_sta_debugfs(sta);
memset(&sinfo, 0, sizeof(sinfo));
sinfo.filled = 0;
sinfo.generation = local->sta_generation;
cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL);
if (!sta->dummy) {
ieee80211_sta_debugfs_add(sta);
rate_control_add_sta_debugfs(sta);
memset(&sinfo, 0, sizeof(sinfo));
sinfo.filled = 0;
sinfo.generation = local->sta_generation;
cfg80211_new_sta(sdata->dev, sta->sta.addr, &sinfo, GFP_KERNEL);
}
return 0;
}
@ -351,7 +406,7 @@ static void sta_info_finish_pending(struct ieee80211_local *local)
list_del(&sta->list);
spin_unlock_irqrestore(&local->sta_lock, flags);
sta_info_finish_insert(sta, true);
sta_info_finish_insert(sta, true, false);
spin_lock_irqsave(&local->sta_lock, flags);
}
@ -368,27 +423,137 @@ static void sta_info_finish_work(struct work_struct *work)
mutex_unlock(&local->sta_mtx);
}
int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
static int sta_info_insert_check(struct sta_info *sta)
{
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
unsigned long flags;
int err = 0;
/*
* Can't be a WARN_ON because it can be triggered through a race:
* something inserts a STA (on one CPU) without holding the RTNL
* and another CPU turns off the net device.
*/
if (unlikely(!ieee80211_sdata_running(sdata))) {
err = -ENETDOWN;
rcu_read_lock();
goto out_free;
}
if (unlikely(!ieee80211_sdata_running(sdata)))
return -ENETDOWN;
if (WARN_ON(compare_ether_addr(sta->sta.addr, sdata->vif.addr) == 0 ||
is_multicast_ether_addr(sta->sta.addr))) {
err = -EINVAL;
is_multicast_ether_addr(sta->sta.addr)))
return -EINVAL;
return 0;
}
static int sta_info_insert_ibss(struct sta_info *sta) __acquires(RCU)
{
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
unsigned long flags;
spin_lock_irqsave(&local->sta_lock, flags);
/* check if STA exists already */
if (sta_info_get_bss_rx(sdata, sta->sta.addr)) {
spin_unlock_irqrestore(&local->sta_lock, flags);
rcu_read_lock();
return -EEXIST;
}
local->num_sta++;
local->sta_generation++;
smp_mb();
sta_info_hash_add(local, sta);
list_add_tail(&sta->list, &local->sta_pending_list);
rcu_read_lock();
spin_unlock_irqrestore(&local->sta_lock, flags);
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n",
sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
ieee80211_queue_work(&local->hw, &local->sta_finish_work);
return 0;
}
/*
* should be called with sta_mtx locked
* this function replaces the mutex lock
* with a RCU lock
*/
static int sta_info_insert_non_ibss(struct sta_info *sta) __acquires(RCU)
{
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
unsigned long flags;
struct sta_info *exist_sta;
bool dummy_reinsert = false;
int err = 0;
lockdep_assert_held(&local->sta_mtx);
/*
* On first glance, this will look racy, because the code
* in this function, which inserts a station with sleeping,
* unlocks the sta_lock between checking existence in the
* hash table and inserting into it.
*
* However, it is not racy against itself because it keeps
* the mutex locked.
*/
spin_lock_irqsave(&local->sta_lock, flags);
/*
* check if STA exists already.
* only accept a scenario of a second call to sta_info_insert_non_ibss
* with a dummy station entry that was inserted earlier
* in that case - assume that the dummy station flag should
* be removed.
*/
exist_sta = sta_info_get_bss_rx(sdata, sta->sta.addr);
if (exist_sta) {
if (exist_sta == sta && sta->dummy) {
dummy_reinsert = true;
} else {
spin_unlock_irqrestore(&local->sta_lock, flags);
mutex_unlock(&local->sta_mtx);
rcu_read_lock();
return -EEXIST;
}
}
spin_unlock_irqrestore(&local->sta_lock, flags);
err = sta_info_finish_insert(sta, false, dummy_reinsert);
if (err) {
mutex_unlock(&local->sta_mtx);
rcu_read_lock();
return err;
}
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
wiphy_debug(local->hw.wiphy, "Inserted %sSTA %pM\n",
sta->dummy ? "dummy " : "", sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
/* move reference to rcu-protected */
rcu_read_lock();
mutex_unlock(&local->sta_mtx);
if (ieee80211_vif_is_mesh(&sdata->vif))
mesh_accept_plinks_update(sdata);
return 0;
}
int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
{
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
int err = 0;
err = sta_info_insert_check(sta);
if (err) {
rcu_read_lock();
goto out_free;
}
@ -399,44 +564,16 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
* always do so in that case -- see the comment below.
*/
if (sdata->vif.type == NL80211_IFTYPE_ADHOC) {
spin_lock_irqsave(&local->sta_lock, flags);
/* check if STA exists already */
if (sta_info_get_bss(sdata, sta->sta.addr)) {
spin_unlock_irqrestore(&local->sta_lock, flags);
rcu_read_lock();
err = -EEXIST;
err = sta_info_insert_ibss(sta);
if (err)
goto out_free;
}
local->num_sta++;
local->sta_generation++;
smp_mb();
sta_info_hash_add(local, sta);
list_add_tail(&sta->list, &local->sta_pending_list);
rcu_read_lock();
spin_unlock_irqrestore(&local->sta_lock, flags);
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
wiphy_debug(local->hw.wiphy, "Added IBSS STA %pM\n",
sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
ieee80211_queue_work(&local->hw, &local->sta_finish_work);
return 0;
}
/*
* On first glance, this will look racy, because the code
* below this point, which inserts a station with sleeping,
* unlocks the sta_lock between checking existence in the
* hash table and inserting into it.
*
* However, it is not racy against itself because it keeps
* the mutex locked. It still seems to race against the
* above code that atomically inserts the station... That,
* It might seem that the function called below is in race against
* the function call above that atomically inserts the station... That,
* however, is not true because the above code can only
* be invoked for IBSS interfaces, and the below code will
* not be -- and the two do not race against each other as
@ -447,35 +584,9 @@ int sta_info_insert_rcu(struct sta_info *sta) __acquires(RCU)
mutex_lock(&local->sta_mtx);
spin_lock_irqsave(&local->sta_lock, flags);
/* check if STA exists already */
if (sta_info_get_bss(sdata, sta->sta.addr)) {
spin_unlock_irqrestore(&local->sta_lock, flags);
mutex_unlock(&local->sta_mtx);
rcu_read_lock();
err = -EEXIST;
err = sta_info_insert_non_ibss(sta);
if (err)
goto out_free;
}
spin_unlock_irqrestore(&local->sta_lock, flags);
err = sta_info_finish_insert(sta, false);
if (err) {
mutex_unlock(&local->sta_mtx);
rcu_read_lock();
goto out_free;
}
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
wiphy_debug(local->hw.wiphy, "Inserted STA %pM\n", sta->sta.addr);
#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
/* move reference to rcu-protected */
rcu_read_lock();
mutex_unlock(&local->sta_mtx);
if (ieee80211_vif_is_mesh(&sdata->vif))
mesh_accept_plinks_update(sdata);
return 0;
out_free:
@ -493,6 +604,25 @@ int sta_info_insert(struct sta_info *sta)
return err;
}
/* Caller must hold sta->local->sta_mtx */
int sta_info_reinsert(struct sta_info *sta)
{
struct ieee80211_local *local = sta->local;
int err = 0;
err = sta_info_insert_check(sta);
if (err) {
mutex_unlock(&local->sta_mtx);
return err;
}
might_sleep();
err = sta_info_insert_non_ibss(sta);
rcu_read_unlock();
return err;
}
static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
{
/*
@ -733,7 +863,7 @@ int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata, const u8 *addr)
int ret;
mutex_lock(&sdata->local->sta_mtx);
sta = sta_info_get(sdata, addr);
sta = sta_info_get_rx(sdata, addr);
ret = __sta_info_destroy(sta);
mutex_unlock(&sdata->local->sta_mtx);
@ -747,7 +877,7 @@ int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata,
int ret;
mutex_lock(&sdata->local->sta_mtx);
sta = sta_info_get_bss(sdata, addr);
sta = sta_info_get_bss_rx(sdata, addr);
ret = __sta_info_destroy(sta);
mutex_unlock(&sdata->local->sta_mtx);