|
@@ -91,6 +91,70 @@ static int sta_info_hash_del(struct ieee80211_local *local,
|
|
|
return -ENOENT;
|
|
|
}
|
|
|
|
|
|
+static void free_sta_work(struct work_struct *wk)
|
|
|
+{
|
|
|
+ struct sta_info *sta = container_of(wk, struct sta_info, free_sta_wk);
|
|
|
+ int ac, i;
|
|
|
+ struct tid_ampdu_tx *tid_tx;
|
|
|
+ struct ieee80211_sub_if_data *sdata = sta->sdata;
|
|
|
+ struct ieee80211_local *local = sdata->local;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * At this point, when being called as call_rcu callback,
|
|
|
+ * neither mac80211 nor the driver can reference this
|
|
|
+ * sta struct any more except by still existing timers
|
|
|
+ * associated with this station that we clean up below.
|
|
|
+ */
|
|
|
+
|
|
|
+ if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
|
|
|
+ BUG_ON(!sdata->bss);
|
|
|
+
|
|
|
+ clear_sta_flag(sta, WLAN_STA_PS_STA);
|
|
|
+
|
|
|
+ atomic_dec(&sdata->bss->num_sta_ps);
|
|
|
+ sta_info_recalc_tim(sta);
|
|
|
+ }
|
|
|
+
|
|
|
+ for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
|
|
+ local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
|
|
|
+ __skb_queue_purge(&sta->ps_tx_buf[ac]);
|
|
|
+ __skb_queue_purge(&sta->tx_filtered[ac]);
|
|
|
+ }
|
|
|
+
|
|
|
+#ifdef CONFIG_MAC80211_MESH
|
|
|
+ if (ieee80211_vif_is_mesh(&sdata->vif)) {
|
|
|
+ mesh_accept_plinks_update(sdata);
|
|
|
+ mesh_plink_deactivate(sta);
|
|
|
+ del_timer_sync(&sta->plink_timer);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
+ cancel_work_sync(&sta->drv_unblock_wk);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Destroy aggregation state here. It would be nice to wait for the
|
|
|
+ * driver to finish aggregation stop and then clean up, but for now
|
|
|
+ * drivers have to handle aggregation stop being requested, followed
|
|
|
+ * directly by station destruction.
|
|
|
+ */
|
|
|
+ for (i = 0; i < STA_TID_NUM; i++) {
|
|
|
+ tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
|
|
|
+ if (!tid_tx)
|
|
|
+ continue;
|
|
|
+ __skb_queue_purge(&tid_tx->pending);
|
|
|
+ kfree(tid_tx);
|
|
|
+ }
|
|
|
+
|
|
|
+ sta_info_free(local, sta);
|
|
|
+}
|
|
|
+
|
|
|
+static void free_sta_rcu(struct rcu_head *h)
|
|
|
+{
|
|
|
+ struct sta_info *sta = container_of(h, struct sta_info, rcu_head);
|
|
|
+
|
|
|
+ ieee80211_queue_work(&sta->local->hw, &sta->free_sta_wk);
|
|
|
+}
|
|
|
+
|
|
|
/* protected by RCU */
|
|
|
struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
|
|
|
const u8 *addr)
|
|
@@ -241,6 +305,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
|
|
|
|
|
|
spin_lock_init(&sta->lock);
|
|
|
INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
|
|
|
+ INIT_WORK(&sta->free_sta_wk, free_sta_work);
|
|
|
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
|
|
|
mutex_init(&sta->ampdu_mlme.mtx);
|
|
|
|
|
@@ -654,8 +719,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
|
|
|
{
|
|
|
struct ieee80211_local *local;
|
|
|
struct ieee80211_sub_if_data *sdata;
|
|
|
- int ret, i, ac;
|
|
|
- struct tid_ampdu_tx *tid_tx;
|
|
|
+ int ret, i;
|
|
|
|
|
|
might_sleep();
|
|
|
|
|
@@ -711,65 +775,14 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
|
|
|
WARN_ON_ONCE(ret != 0);
|
|
|
}
|
|
|
|
|
|
- /*
|
|
|
- * At this point, after we wait for an RCU grace period,
|
|
|
- * neither mac80211 nor the driver can reference this
|
|
|
- * sta struct any more except by still existing timers
|
|
|
- * associated with this station that we clean up below.
|
|
|
- */
|
|
|
- synchronize_rcu();
|
|
|
-
|
|
|
- if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
|
|
|
- BUG_ON(!sdata->bss);
|
|
|
-
|
|
|
- clear_sta_flag(sta, WLAN_STA_PS_STA);
|
|
|
-
|
|
|
- atomic_dec(&sdata->bss->num_sta_ps);
|
|
|
- sta_info_recalc_tim(sta);
|
|
|
- }
|
|
|
-
|
|
|
- for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
|
|
- local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]);
|
|
|
- __skb_queue_purge(&sta->ps_tx_buf[ac]);
|
|
|
- __skb_queue_purge(&sta->tx_filtered[ac]);
|
|
|
- }
|
|
|
-
|
|
|
-#ifdef CONFIG_MAC80211_MESH
|
|
|
- if (ieee80211_vif_is_mesh(&sdata->vif))
|
|
|
- mesh_accept_plinks_update(sdata);
|
|
|
-#endif
|
|
|
-
|
|
|
sta_dbg(sdata, "Removed STA %pM\n", sta->sta.addr);
|
|
|
|
|
|
- cancel_work_sync(&sta->drv_unblock_wk);
|
|
|
-
|
|
|
cfg80211_del_sta(sdata->dev, sta->sta.addr, GFP_KERNEL);
|
|
|
|
|
|
rate_control_remove_sta_debugfs(sta);
|
|
|
ieee80211_sta_debugfs_remove(sta);
|
|
|
|
|
|
-#ifdef CONFIG_MAC80211_MESH
|
|
|
- if (ieee80211_vif_is_mesh(&sta->sdata->vif)) {
|
|
|
- mesh_plink_deactivate(sta);
|
|
|
- del_timer_sync(&sta->plink_timer);
|
|
|
- }
|
|
|
-#endif
|
|
|
-
|
|
|
- /*
|
|
|
- * Destroy aggregation state here. It would be nice to wait for the
|
|
|
- * driver to finish aggregation stop and then clean up, but for now
|
|
|
- * drivers have to handle aggregation stop being requested, followed
|
|
|
- * directly by station destruction.
|
|
|
- */
|
|
|
- for (i = 0; i < STA_TID_NUM; i++) {
|
|
|
- tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
|
|
|
- if (!tid_tx)
|
|
|
- continue;
|
|
|
- __skb_queue_purge(&tid_tx->pending);
|
|
|
- kfree(tid_tx);
|
|
|
- }
|
|
|
-
|
|
|
- sta_info_free(local, sta);
|
|
|
+ call_rcu(&sta->rcu_head, free_sta_rcu);
|
|
|
|
|
|
return 0;
|
|
|
}
|