|
@@ -36,16 +36,23 @@
|
|
* (which is pretty useless) or insert it into the hash table using
|
|
* (which is pretty useless) or insert it into the hash table using
|
|
* sta_info_insert() which demotes the reference from ownership to a regular
|
|
* sta_info_insert() which demotes the reference from ownership to a regular
|
|
* RCU-protected reference; if the function is called without protection by an
|
|
* RCU-protected reference; if the function is called without protection by an
|
|
- * RCU critical section the reference is instantly invalidated.
|
|
|
|
|
|
+ * RCU critical section the reference is instantly invalidated. Note that the
|
|
|
|
+ * caller may not do much with the STA info before inserting it, in particular,
|
|
|
|
+ * it may not start any mesh peer link management or add encryption keys.
|
|
|
|
+ *
|
|
|
|
+ * When the insertion fails (sta_info_insert()) returns non-zero), the
|
|
|
|
+ * structure will have been freed by sta_info_insert()!
|
|
*
|
|
*
|
|
* Because there are debugfs entries for each station, and adding those
|
|
* Because there are debugfs entries for each station, and adding those
|
|
* must be able to sleep, it is also possible to "pin" a station entry,
|
|
* must be able to sleep, it is also possible to "pin" a station entry,
|
|
* that means it can be removed from the hash table but not be freed.
|
|
* that means it can be removed from the hash table but not be freed.
|
|
- * See the comment in __sta_info_unlink() for more information.
|
|
|
|
|
|
+ * See the comment in __sta_info_unlink() for more information, this is
|
|
|
|
+ * an internal capability only.
|
|
*
|
|
*
|
|
* In order to remove a STA info structure, the caller needs to first
|
|
* In order to remove a STA info structure, the caller needs to first
|
|
* unlink it (sta_info_unlink()) from the list and hash tables and
|
|
* unlink it (sta_info_unlink()) from the list and hash tables and
|
|
- * then wait for an RCU synchronisation before it can be freed. Due to
|
|
|
|
|
|
+ * then destroy it while holding the RTNL; sta_info_destroy() will wait
|
|
|
|
+ * for an RCU grace period to elapse before actually freeing it. Due to
|
|
* the pinning and the possibility of multiple callers trying to remove
|
|
* the pinning and the possibility of multiple callers trying to remove
|
|
* the same STA info at the same time, sta_info_unlink() can clear the
|
|
* the same STA info at the same time, sta_info_unlink() can clear the
|
|
* STA info pointer it is passed to indicate that the STA info is owned
|
|
* STA info pointer it is passed to indicate that the STA info is owned
|
|
@@ -127,12 +134,35 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_local *local, int idx,
|
|
return NULL;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/**
|
|
|
|
+ * __sta_info_free - internal STA free helper
|
|
|
|
+ *
|
|
|
|
+ * @sta: STA info to free
|
|
|
|
+ *
|
|
|
|
+ * This function must undo everything done by sta_info_alloc()
|
|
|
|
+ * that may happen before sta_info_insert().
|
|
|
|
+ */
|
|
|
|
+static void __sta_info_free(struct ieee80211_local *local,
|
|
|
|
+ struct sta_info *sta)
|
|
|
|
+{
|
|
|
|
+ DECLARE_MAC_BUF(mbuf);
|
|
|
|
+
|
|
|
|
+ rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv);
|
|
|
|
+ rate_control_put(sta->rate_ctrl);
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
|
|
|
+ printk(KERN_DEBUG "%s: Destroyed STA %s\n",
|
|
|
|
+ wiphy_name(local->hw.wiphy), print_mac(mbuf, sta->addr));
|
|
|
|
+#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
|
|
|
|
+
|
|
|
|
+ kfree(sta);
|
|
|
|
+}
|
|
|
|
+
|
|
void sta_info_destroy(struct sta_info *sta)
|
|
void sta_info_destroy(struct sta_info *sta)
|
|
{
|
|
{
|
|
struct ieee80211_local *local;
|
|
struct ieee80211_local *local;
|
|
struct sk_buff *skb;
|
|
struct sk_buff *skb;
|
|
int i;
|
|
int i;
|
|
- DECLARE_MAC_BUF(mbuf);
|
|
|
|
|
|
|
|
ASSERT_RTNL();
|
|
ASSERT_RTNL();
|
|
might_sleep();
|
|
might_sleep();
|
|
@@ -182,15 +212,7 @@ void sta_info_destroy(struct sta_info *sta)
|
|
spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
|
|
spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
|
|
}
|
|
}
|
|
|
|
|
|
- rate_control_free_sta(sta->rate_ctrl, sta->rate_ctrl_priv);
|
|
|
|
- rate_control_put(sta->rate_ctrl);
|
|
|
|
-
|
|
|
|
-#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
|
|
|
|
- printk(KERN_DEBUG "%s: Destroyed STA %s\n",
|
|
|
|
- wiphy_name(local->hw.wiphy), print_mac(mbuf, sta->addr));
|
|
|
|
-#endif /* CONFIG_MAC80211_VERBOSE_DEBUG */
|
|
|
|
-
|
|
|
|
- kfree(sta);
|
|
|
|
|
|
+ __sta_info_free(local, sta);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -266,6 +288,7 @@ int sta_info_insert(struct sta_info *sta)
|
|
struct ieee80211_local *local = sta->local;
|
|
struct ieee80211_local *local = sta->local;
|
|
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
|
struct ieee80211_sub_if_data *sdata = sta->sdata;
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
|
|
+ int err = 0;
|
|
DECLARE_MAC_BUF(mac);
|
|
DECLARE_MAC_BUF(mac);
|
|
|
|
|
|
/*
|
|
/*
|
|
@@ -273,20 +296,23 @@ int sta_info_insert(struct sta_info *sta)
|
|
* something inserts a STA (on one CPU) without holding the RTNL
|
|
* something inserts a STA (on one CPU) without holding the RTNL
|
|
* and another CPU turns off the net device.
|
|
* and another CPU turns off the net device.
|
|
*/
|
|
*/
|
|
- if (unlikely(!netif_running(sdata->dev)))
|
|
|
|
- return -ENETDOWN;
|
|
|
|
-
|
|
|
|
- if (WARN_ON(compare_ether_addr(sta->addr, sdata->dev->dev_addr) == 0))
|
|
|
|
- return -EINVAL;
|
|
|
|
|
|
+ if (unlikely(!netif_running(sdata->dev))) {
|
|
|
|
+ err = -ENETDOWN;
|
|
|
|
+ goto out_free;
|
|
|
|
+ }
|
|
|
|
|
|
- if (WARN_ON(is_multicast_ether_addr(sta->addr)))
|
|
|
|
- return -EINVAL;
|
|
|
|
|
|
+ if (WARN_ON(compare_ether_addr(sta->addr, sdata->dev->dev_addr) == 0 ||
|
|
|
|
+ is_multicast_ether_addr(sta->addr))) {
|
|
|
|
+ err = -EINVAL;
|
|
|
|
+ goto out_free;
|
|
|
|
+ }
|
|
|
|
|
|
spin_lock_irqsave(&local->sta_lock, flags);
|
|
spin_lock_irqsave(&local->sta_lock, flags);
|
|
/* check if STA exists already */
|
|
/* check if STA exists already */
|
|
if (__sta_info_find(local, sta->addr)) {
|
|
if (__sta_info_find(local, sta->addr)) {
|
|
spin_unlock_irqrestore(&local->sta_lock, flags);
|
|
spin_unlock_irqrestore(&local->sta_lock, flags);
|
|
- return -EEXIST;
|
|
|
|
|
|
+ err = -EEXIST;
|
|
|
|
+ goto out_free;
|
|
}
|
|
}
|
|
list_add(&sta->list, &local->sta_list);
|
|
list_add(&sta->list, &local->sta_list);
|
|
local->num_sta++;
|
|
local->num_sta++;
|
|
@@ -309,9 +335,13 @@ int sta_info_insert(struct sta_info *sta)
|
|
spin_unlock_irqrestore(&local->sta_lock, flags);
|
|
spin_unlock_irqrestore(&local->sta_lock, flags);
|
|
|
|
|
|
#ifdef CONFIG_MAC80211_DEBUGFS
|
|
#ifdef CONFIG_MAC80211_DEBUGFS
|
|
- /* debugfs entry adding might sleep, so schedule process
|
|
|
|
|
|
+ /*
|
|
|
|
+ * Debugfs entry adding might sleep, so schedule process
|
|
* context task for adding entry for STAs that do not yet
|
|
* context task for adding entry for STAs that do not yet
|
|
- * have one. */
|
|
|
|
|
|
+ * have one.
|
|
|
|
+ * NOTE: due to auto-freeing semantics this may only be done
|
|
|
|
+ * if the insertion is successful!
|
|
|
|
+ */
|
|
queue_work(local->hw.workqueue, &local->sta_debugfs_add);
|
|
queue_work(local->hw.workqueue, &local->sta_debugfs_add);
|
|
#endif
|
|
#endif
|
|
|
|
|
|
@@ -319,6 +349,10 @@ int sta_info_insert(struct sta_info *sta)
|
|
mesh_accept_plinks_update(sdata);
|
|
mesh_accept_plinks_update(sdata);
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
|
|
+ out_free:
|
|
|
|
+ BUG_ON(!err);
|
|
|
|
+ __sta_info_free(local, sta);
|
|
|
|
+ return err;
|
|
}
|
|
}
|
|
|
|
|
|
static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
|
|
static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
|