mac80211: optimise roaming time again
[deliverable/linux.git] / net / mac80211 / sta_info.c
index d2eb64e1235383e9b0e6aade66710f74ec1b4f3f..7199b9d5b2f45995ca92d11aebfa6ed273075aef 100644 (file)
@@ -91,27 +91,41 @@ static int sta_info_hash_del(struct ieee80211_local *local,
        return -ENOENT;
 }
 
-static void free_sta_work(struct work_struct *wk)
+static void cleanup_single_sta(struct sta_info *sta)
 {
-       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;
+       struct ps_data *ps;
 
        /*
         * 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.
+        *
+        * Note though that this still uses the sdata and even
+        * calls the driver in AP and mesh mode, so interfaces
+        * of those types mush use call sta_info_flush_cleanup()
+        * (typically via sta_info_flush()) before deconfiguring
+        * the driver.
+        *
+        * In station mode, nothing happens here so it doesn't
+        * have to (and doesn't) do that, this is intentional to
+        * speed up roaming.
         */
 
        if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
-               BUG_ON(!sdata->bss);
+               if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
+                   sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+                       ps = &sdata->bss->ps;
+               else
+                       return;
 
                clear_sta_flag(sta, WLAN_STA_PS_STA);
 
-               atomic_dec(&sdata->bss->num_sta_ps);
+               atomic_dec(&ps->num_sta_ps);
                sta_info_recalc_tim(sta);
        }
 
@@ -137,7 +151,7 @@ static void free_sta_work(struct work_struct *wk)
         * drivers have to handle aggregation stop being requested, followed
         * directly by station destruction.
         */
-       for (i = 0; i < STA_TID_NUM; i++) {
+       for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
                tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]);
                if (!tid_tx)
                        continue;
@@ -148,11 +162,35 @@ static void free_sta_work(struct work_struct *wk)
        sta_info_free(local, sta);
 }
 
+void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata)
+{
+       struct sta_info *sta;
+
+       spin_lock_bh(&sdata->cleanup_stations_lock);
+       while (!list_empty(&sdata->cleanup_stations)) {
+               sta = list_first_entry(&sdata->cleanup_stations,
+                                      struct sta_info, list);
+               list_del(&sta->list);
+               spin_unlock_bh(&sdata->cleanup_stations_lock);
+
+               cleanup_single_sta(sta);
+
+               spin_lock_bh(&sdata->cleanup_stations_lock);
+       }
+
+       spin_unlock_bh(&sdata->cleanup_stations_lock);
+}
+
 static void free_sta_rcu(struct rcu_head *h)
 {
        struct sta_info *sta = container_of(h, struct sta_info, rcu_head);
+       struct ieee80211_sub_if_data *sdata = sta->sdata;
 
-       ieee80211_queue_work(&sta->local->hw, &sta->free_sta_wk);
+       spin_lock(&sdata->cleanup_stations_lock);
+       list_add_tail(&sta->list, &sdata->cleanup_stations);
+       spin_unlock(&sdata->cleanup_stations_lock);
+
+       ieee80211_queue_work(&sdata->local->hw, &sdata->cleanup_stations_wk);
 }
 
 /* protected by RCU */
@@ -305,7 +343,6 @@ 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);
 
@@ -325,7 +362,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
                return NULL;
        }
 
-       for (i = 0; i < STA_TID_NUM; i++) {
+       for (i = 0; i < IEEE80211_NUM_TIDS; i++) {
                /*
                 * timer_to_tid must be initialized with identity mapping
                 * to enable session_timer's data differentiation. See
@@ -338,7 +375,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
                skb_queue_head_init(&sta->tx_filtered[i]);
        }
 
-       for (i = 0; i < NUM_RX_DATA_QUEUES; i++)
+       for (i = 0; i < IEEE80211_NUM_TIDS; i++)
                sta->last_seq_ctrl[i] = cpu_to_le16(USHRT_MAX);
 
        sta_dbg(sdata, "Allocated STA %pM\n", sta->sta.addr);
@@ -502,22 +539,22 @@ int sta_info_insert(struct sta_info *sta)
        return err;
 }
 
-static inline void __bss_tim_set(struct ieee80211_if_ap *bss, u16 aid)
+static inline void __bss_tim_set(u8 *tim, u16 id)
 {
        /*
         * This format has been mandated by the IEEE specifications,
         * so this line may not be changed to use the __set_bit() format.
         */
-       bss->tim[aid / 8] |= (1 << (aid % 8));
+       tim[id / 8] |= (1 << (id % 8));
 }
 
-static inline void __bss_tim_clear(struct ieee80211_if_ap *bss, u16 aid)
+static inline void __bss_tim_clear(u8 *tim, u16 id)
 {
        /*
         * This format has been mandated by the IEEE specifications,
         * so this line may not be changed to use the __clear_bit() format.
         */
-       bss->tim[aid / 8] &= ~(1 << (aid % 8));
+       tim[id / 8] &= ~(1 << (id % 8));
 }
 
 static unsigned long ieee80211_tids_for_ac(int ac)
@@ -541,14 +578,23 @@ static unsigned long ieee80211_tids_for_ac(int ac)
 void sta_info_recalc_tim(struct sta_info *sta)
 {
        struct ieee80211_local *local = sta->local;
-       struct ieee80211_if_ap *bss = sta->sdata->bss;
+       struct ps_data *ps;
        unsigned long flags;
        bool indicate_tim = false;
        u8 ignore_for_tim = sta->sta.uapsd_queues;
        int ac;
+       u16 id;
+
+       if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
+           sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+               if (WARN_ON_ONCE(!sta->sdata->bss))
+                       return;
 
-       if (WARN_ON_ONCE(!sta->sdata->bss))
+               ps = &sta->sdata->bss->ps;
+               id = sta->sta.aid;
+       } else {
                return;
+       }
 
        /* No need to do anything if the driver does all */
        if (local->hw.flags & IEEE80211_HW_AP_LINK_PS)
@@ -587,9 +633,9 @@ void sta_info_recalc_tim(struct sta_info *sta)
        spin_lock_irqsave(&local->tim_lock, flags);
 
        if (indicate_tim)
-               __bss_tim_set(bss, sta->sta.aid);
+               __bss_tim_set(ps->tim, id);
        else
-               __bss_tim_clear(bss, sta->sta.aid);
+               __bss_tim_clear(ps->tim, id);
 
        if (local->ops->set_tim) {
                local->tim_in_locked_section = true;
@@ -848,21 +894,13 @@ void sta_info_init(struct ieee80211_local *local)
 
 void sta_info_stop(struct ieee80211_local *local)
 {
-       del_timer(&local->sta_cleanup);
-       sta_info_flush(local, NULL);
+       del_timer_sync(&local->sta_cleanup);
 }
 
-/**
- * sta_info_flush - flush matching STA entries from the STA table
- *
- * Returns the number of removed STA entries.
- *
- * @local: local interface data
- * @sdata: matching rule for the net device (sta->dev) or %NULL to match all STAs
- */
-int sta_info_flush(struct ieee80211_local *local,
-                  struct ieee80211_sub_if_data *sdata)
+
+int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata)
 {
+       struct ieee80211_local *local = sdata->local;
        struct sta_info *sta, *tmp;
        int ret = 0;
 
@@ -870,7 +908,7 @@ int sta_info_flush(struct ieee80211_local *local,
 
        mutex_lock(&local->sta_mtx);
        list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
-               if (!sdata || sdata == sta->sdata) {
+               if (sdata == sta->sdata) {
                        WARN_ON(__sta_info_destroy(sta));
                        ret++;
                }
@@ -880,6 +918,14 @@ int sta_info_flush(struct ieee80211_local *local,
        return ret;
 }
 
+void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata)
+{
+       rcu_barrier();
+
+       ieee80211_cleanup_sdata_stas(sdata);
+       cancel_work_sync(&sdata->cleanup_stations_wk);
+}
+
 void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
                          unsigned long exp_time)
 {
@@ -893,8 +939,8 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
                        continue;
 
                if (time_after(jiffies, sta->last_rx + exp_time)) {
-                       ibss_dbg(sdata, "expiring inactive STA %pM\n",
-                                sta->sta.addr);
+                       sta_dbg(sta->sdata, "expiring inactive STA %pM\n",
+                               sta->sta.addr);
                        WARN_ON(__sta_info_destroy(sta));
                }
        }
@@ -948,10 +994,17 @@ static void clear_sta_ps_flags(void *_sta)
 {
        struct sta_info *sta = _sta;
        struct ieee80211_sub_if_data *sdata = sta->sdata;
+       struct ps_data *ps;
+
+       if (sdata->vif.type == NL80211_IFTYPE_AP ||
+           sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
+               ps = &sdata->bss->ps;
+       else
+               return;
 
        clear_sta_flag(sta, WLAN_STA_PS_DRIVER);
        if (test_and_clear_sta_flag(sta, WLAN_STA_PS_STA))
-               atomic_dec(&sdata->bss->num_sta_ps);
+               atomic_dec(&ps->num_sta_ps);
 }
 
 /* powersave support code */
@@ -965,7 +1018,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
 
        clear_sta_flag(sta, WLAN_STA_SP);
 
-       BUILD_BUG_ON(BITS_TO_LONGS(STA_TID_NUM) > 1);
+       BUILD_BUG_ON(BITS_TO_LONGS(IEEE80211_NUM_TIDS) > 1);
        sta->driver_buffered_tids = 0;
 
        if (!(local->hw.flags & IEEE80211_HW_AP_LINK_PS))
@@ -1013,6 +1066,7 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
        __le16 fc;
        bool qos = test_sta_flag(sta, WLAN_STA_WME);
        struct ieee80211_tx_info *info;
+       struct ieee80211_chanctx_conf *chanctx_conf;
 
        if (qos) {
                fc = cpu_to_le16(IEEE80211_FTYPE_DATA |
@@ -1062,7 +1116,16 @@ static void ieee80211_send_null_response(struct ieee80211_sub_if_data *sdata,
 
        drv_allow_buffered_frames(local, sta, BIT(tid), 1, reason, false);
 
-       ieee80211_xmit(sdata, skb);
+       rcu_read_lock();
+       chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
+       if (WARN_ON(!chanctx_conf)) {
+               rcu_read_unlock();
+               kfree_skb(skb);
+               return;
+       }
+
+       ieee80211_xmit(sdata, skb, chanctx_conf->def.chan->band);
+       rcu_read_unlock();
 }
 
 static void
@@ -1343,7 +1406,7 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
 {
        struct sta_info *sta = container_of(pubsta, struct sta_info, sta);
 
-       if (WARN_ON(tid >= STA_TID_NUM))
+       if (WARN_ON(tid >= IEEE80211_NUM_TIDS))
                return;
 
        if (buffered)
This page took 0.054005 seconds and 5 git commands to generate.