]> Pileus Git - ~andy/linux/blobdiff - net/mac80211/sta_info.c
mac80211: use blacklist for duplicate IE check
[~andy/linux] / net / mac80211 / sta_info.c
index 06fa75ceb0251e6064d90661bf7b5e61f979b72c..797dd36a220d92ac549067a9cd4d59a7ebfc09a3 100644 (file)
@@ -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();
 
@@ -674,7 +738,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
         * will be sufficient.
         */
        set_sta_flag(sta, WLAN_STA_BLOCK_BA);
-       ieee80211_sta_tear_down_BA_sessions(sta, true);
+       ieee80211_sta_tear_down_BA_sessions(sta, false);
 
        ret = sta_info_hash_del(local, sta);
        if (ret)
@@ -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;
 }