|
@@ -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);
|
|
|
}
|
|
@@ -395,7 +450,7 @@ static int sta_info_insert_ibss(struct sta_info *sta) __acquires(RCU)
|
|
|
|
|
|
spin_lock_irqsave(&local->sta_lock, flags);
|
|
|
/* check if STA exists already */
|
|
|
- if (sta_info_get_bss(sdata, sta->sta.addr)) {
|
|
|
+ if (sta_info_get_bss_rx(sdata, sta->sta.addr)) {
|
|
|
spin_unlock_irqrestore(&local->sta_lock, flags);
|
|
|
rcu_read_lock();
|
|
|
return -EEXIST;
|
|
@@ -431,6 +486,8 @@ 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);
|
|
@@ -446,17 +503,28 @@ static int sta_info_insert_non_ibss(struct sta_info *sta) __acquires(RCU)
|
|
|
*/
|
|
|
|
|
|
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();
|
|
|
- return -EEXIST;
|
|
|
+ /*
|
|
|
+ * 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);
|
|
|
+ err = sta_info_finish_insert(sta, false, dummy_reinsert);
|
|
|
if (err) {
|
|
|
mutex_unlock(&local->sta_mtx);
|
|
|
rcu_read_lock();
|
|
@@ -464,7 +532,8 @@ static int sta_info_insert_non_ibss(struct sta_info *sta) __acquires(RCU)
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
|
|
- wiphy_debug(local->hw.wiphy, "Inserted STA %pM\n", sta->sta.addr);
|
|
|
+ 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 */
|
|
@@ -535,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)
|
|
|
{
|
|
|
/*
|
|
@@ -775,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);
|
|
|
|
|
@@ -789,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);
|
|
|
|