mac80211: add fast-rx path
[cascardo/linux.git] / net / mac80211 / sta_info.c
index d20bab5..a0ce7e4 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright 2002-2005, Instant802 Networks, Inc.
  * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
- * Copyright (C) 2015 Intel Deutschland GmbH
+ * Copyright (C) 2015 - 2016 Intel Deutschland GmbH
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -335,15 +335,17 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
        sta->sdata = sdata;
        sta->rx_stats.last_rx = jiffies;
 
+       u64_stats_init(&sta->rx_stats.syncp);
+
        sta->sta_state = IEEE80211_STA_NONE;
 
        /* Mark TID as unreserved */
        sta->reserved_tid = IEEE80211_TID_UNRESERVED;
 
        sta->last_connected = ktime_get_seconds();
-       ewma_signal_init(&sta->rx_stats.avg_signal);
-       for (i = 0; i < ARRAY_SIZE(sta->rx_stats.chain_signal_avg); i++)
-               ewma_signal_init(&sta->rx_stats.chain_signal_avg[i]);
+       ewma_signal_init(&sta->rx_stats_avg.signal);
+       for (i = 0; i < ARRAY_SIZE(sta->rx_stats_avg.chain_signal); i++)
+               ewma_signal_init(&sta->rx_stats_avg.chain_signal[i]);
 
        if (local->ops->wake_tx_queue) {
                void *txq_data;
@@ -875,6 +877,13 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
        set_sta_flag(sta, WLAN_STA_BLOCK_BA);
        ieee80211_sta_tear_down_BA_sessions(sta, AGG_STOP_DESTROY_STA);
 
+       /*
+        * Before removing the station from the driver there might be pending
+        * rx frames on RSS queues sent prior to the disassociation - wait for
+        * all such frames to be processed.
+        */
+       drv_sync_rx_queues(local, sta);
+
        ret = sta_info_hash_del(local, sta);
        if (WARN_ON(ret))
                return ret;
@@ -1087,10 +1096,12 @@ void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
        mutex_lock(&local->sta_mtx);
 
        list_for_each_entry_safe(sta, tmp, &local->sta_list, list) {
+               unsigned long last_active = ieee80211_sta_last_active(sta);
+
                if (sdata != sta->sdata)
                        continue;
 
-               if (time_after(jiffies, sta->rx_stats.last_rx + exp_time)) {
+               if (time_is_before_jiffies(last_active + exp_time)) {
                        sta_dbg(sta->sdata, "expiring inactive STA %pM\n",
                                sta->sta.addr);
 
@@ -1760,6 +1771,31 @@ void ieee80211_sta_set_buffered(struct ieee80211_sta *pubsta,
 }
 EXPORT_SYMBOL(ieee80211_sta_set_buffered);
 
+static void
+ieee80211_recalc_p2p_go_ps_allowed(struct ieee80211_sub_if_data *sdata)
+{
+       struct ieee80211_local *local = sdata->local;
+       bool allow_p2p_go_ps = sdata->vif.p2p;
+       struct sta_info *sta;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(sta, &local->sta_list, list) {
+               if (sdata != sta->sdata ||
+                   !test_sta_flag(sta, WLAN_STA_ASSOC))
+                       continue;
+               if (!sta->sta.support_p2p_ps) {
+                       allow_p2p_go_ps = false;
+                       break;
+               }
+       }
+       rcu_read_unlock();
+
+       if (allow_p2p_go_ps != sdata->vif.bss_conf.allow_p2p_go_ps) {
+               sdata->vif.bss_conf.allow_p2p_go_ps = allow_p2p_go_ps;
+               ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_P2P_PS);
+       }
+}
+
 int sta_info_move_state(struct sta_info *sta,
                        enum ieee80211_sta_state new_state)
 {
@@ -1821,12 +1857,16 @@ int sta_info_move_state(struct sta_info *sta,
                } else if (sta->sta_state == IEEE80211_STA_ASSOC) {
                        clear_bit(WLAN_STA_ASSOC, &sta->_flags);
                        ieee80211_recalc_min_chandef(sta->sdata);
+                       if (!sta->sta.support_p2p_ps)
+                               ieee80211_recalc_p2p_go_ps_allowed(sta->sdata);
                }
                break;
        case IEEE80211_STA_ASSOC:
                if (sta->sta_state == IEEE80211_STA_AUTH) {
                        set_bit(WLAN_STA_ASSOC, &sta->_flags);
                        ieee80211_recalc_min_chandef(sta->sdata);
+                       if (!sta->sta.support_p2p_ps)
+                               ieee80211_recalc_p2p_go_ps_allowed(sta->sdata);
                } else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) {
                        if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
                            (sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN &&
@@ -1834,6 +1874,7 @@ int sta_info_move_state(struct sta_info *sta,
                                atomic_dec(&sta->sdata->bss->num_mcast_sta);
                        clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
                        ieee80211_clear_fast_xmit(sta);
+                       ieee80211_clear_fast_rx(sta);
                }
                break;
        case IEEE80211_STA_AUTHORIZED:
@@ -1844,6 +1885,7 @@ int sta_info_move_state(struct sta_info *sta,
                                atomic_inc(&sta->sdata->bss->num_mcast_sta);
                        set_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
                        ieee80211_check_fast_xmit(sta);
+                       ieee80211_check_fast_rx(sta);
                }
                break;
        default:
@@ -1890,43 +1932,82 @@ u8 sta_info_tx_streams(struct sta_info *sta)
                        >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
 }
 
-static void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
+static void sta_stats_decode_rate(struct ieee80211_local *local, u16 rate,
+                                 struct rate_info *rinfo)
 {
-       rinfo->flags = 0;
-
-       if (sta->rx_stats.last_rate_flag & RX_FLAG_HT) {
-               rinfo->flags |= RATE_INFO_FLAGS_MCS;
-               rinfo->mcs = sta->rx_stats.last_rate_idx;
-       } else if (sta->rx_stats.last_rate_flag & RX_FLAG_VHT) {
-               rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS;
-               rinfo->nss = sta->rx_stats.last_rate_vht_nss;
-               rinfo->mcs = sta->rx_stats.last_rate_idx;
-       } else {
+       rinfo->bw = (rate & STA_STATS_RATE_BW_MASK) >>
+               STA_STATS_RATE_BW_SHIFT;
+
+       if (rate & STA_STATS_RATE_VHT) {
+               rinfo->flags = RATE_INFO_FLAGS_VHT_MCS;
+               rinfo->mcs = rate & 0xf;
+               rinfo->nss = (rate & 0xf0) >> 4;
+       } else if (rate & STA_STATS_RATE_HT) {
+               rinfo->flags = RATE_INFO_FLAGS_MCS;
+               rinfo->mcs = rate & 0xff;
+       } else if (rate & STA_STATS_RATE_LEGACY) {
                struct ieee80211_supported_band *sband;
-               int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
                u16 brate;
-
-               sband = sta->local->hw.wiphy->bands[
-                               ieee80211_get_sdata_band(sta->sdata)];
-               brate = sband->bitrates[sta->rx_stats.last_rate_idx].bitrate;
+               unsigned int shift;
+
+               sband = local->hw.wiphy->bands[(rate >> 4) & 0xf];
+               brate = sband->bitrates[rate & 0xf].bitrate;
+               if (rinfo->bw == RATE_INFO_BW_5)
+                       shift = 2;
+               else if (rinfo->bw == RATE_INFO_BW_10)
+                       shift = 1;
+               else
+                       shift = 0;
                rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
        }
 
-       if (sta->rx_stats.last_rate_flag & RX_FLAG_SHORT_GI)
+       if (rate & STA_STATS_RATE_SGI)
                rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
+}
+
+static void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
+{
+       u16 rate = ACCESS_ONCE(sta->rx_stats.last_rate);
 
-       if (sta->rx_stats.last_rate_flag & RX_FLAG_5MHZ)
-               rinfo->bw = RATE_INFO_BW_5;
-       else if (sta->rx_stats.last_rate_flag & RX_FLAG_10MHZ)
-               rinfo->bw = RATE_INFO_BW_10;
-       else if (sta->rx_stats.last_rate_flag & RX_FLAG_40MHZ)
-               rinfo->bw = RATE_INFO_BW_40;
-       else if (sta->rx_stats.last_rate_vht_flag & RX_VHT_FLAG_80MHZ)
-               rinfo->bw = RATE_INFO_BW_80;
-       else if (sta->rx_stats.last_rate_vht_flag & RX_VHT_FLAG_160MHZ)
-               rinfo->bw = RATE_INFO_BW_160;
+       if (rate == STA_STATS_RATE_INVALID)
+               rinfo->flags = 0;
        else
-               rinfo->bw = RATE_INFO_BW_20;
+               sta_stats_decode_rate(sta->local, rate, rinfo);
+}
+
+static void sta_set_tidstats(struct sta_info *sta,
+                            struct cfg80211_tid_stats *tidstats,
+                            int tid)
+{
+       struct ieee80211_local *local = sta->local;
+
+       if (!(tidstats->filled & BIT(NL80211_TID_STATS_RX_MSDU))) {
+               unsigned int start;
+
+               do {
+                       start = u64_stats_fetch_begin(&sta->rx_stats.syncp);
+                       tidstats->rx_msdu = sta->rx_stats.msdu[tid];
+               } while (u64_stats_fetch_retry(&sta->rx_stats.syncp, start));
+
+               tidstats->filled |= BIT(NL80211_TID_STATS_RX_MSDU);
+       }
+
+       if (!(tidstats->filled & BIT(NL80211_TID_STATS_TX_MSDU))) {
+               tidstats->filled |= BIT(NL80211_TID_STATS_TX_MSDU);
+               tidstats->tx_msdu = sta->tx_stats.msdu[tid];
+       }
+
+       if (!(tidstats->filled & BIT(NL80211_TID_STATS_TX_MSDU_RETRIES)) &&
+           ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
+               tidstats->filled |= BIT(NL80211_TID_STATS_TX_MSDU_RETRIES);
+               tidstats->tx_msdu_retries = sta->status_stats.msdu_retries[tid];
+       }
+
+       if (!(tidstats->filled & BIT(NL80211_TID_STATS_TX_MSDU_FAILED)) &&
+           ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
+               tidstats->filled |= BIT(NL80211_TID_STATS_TX_MSDU_FAILED);
+               tidstats->tx_msdu_failed = sta->status_stats.msdu_failed[tid];
+       }
 }
 
 void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
@@ -1964,7 +2045,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
 
        sinfo->connected_time = ktime_get_seconds() - sta->last_connected;
        sinfo->inactive_time =
-               jiffies_to_msecs(jiffies - sta->rx_stats.last_rx);
+               jiffies_to_msecs(jiffies - ieee80211_sta_last_active(sta));
 
        if (!(sinfo->filled & (BIT(NL80211_STA_INFO_TX_BYTES64) |
                               BIT(NL80211_STA_INFO_TX_BYTES)))) {
@@ -1983,7 +2064,12 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
 
        if (!(sinfo->filled & (BIT(NL80211_STA_INFO_RX_BYTES64) |
                               BIT(NL80211_STA_INFO_RX_BYTES)))) {
-               sinfo->rx_bytes = sta->rx_stats.bytes;
+               unsigned int start;
+
+               do {
+                       start = u64_stats_fetch_begin(&sta->rx_stats.syncp);
+                       sinfo->rx_bytes = sta->rx_stats.bytes;
+               } while (u64_stats_fetch_retry(&sta->rx_stats.syncp, start));
                sinfo->filled |= BIT(NL80211_STA_INFO_RX_BYTES64);
        }
 
@@ -2020,7 +2106,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
 
                if (!(sinfo->filled & BIT(NL80211_STA_INFO_SIGNAL_AVG))) {
                        sinfo->signal_avg =
-                               -ewma_signal_read(&sta->rx_stats.avg_signal);
+                               -ewma_signal_read(&sta->rx_stats_avg.signal);
                        sinfo->filled |= BIT(NL80211_STA_INFO_SIGNAL_AVG);
                }
        }
@@ -2036,7 +2122,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                        sinfo->chain_signal[i] =
                                sta->rx_stats.chain_signal_last[i];
                        sinfo->chain_signal_avg[i] =
-                               -ewma_signal_read(&sta->rx_stats.chain_signal_avg[i]);
+                               -ewma_signal_read(&sta->rx_stats_avg.chain_signal[i]);
                }
        }
 
@@ -2055,33 +2141,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
        for (i = 0; i < IEEE80211_NUM_TIDS + 1; i++) {
                struct cfg80211_tid_stats *tidstats = &sinfo->pertid[i];
 
-               if (!(tidstats->filled & BIT(NL80211_TID_STATS_RX_MSDU))) {
-                       tidstats->filled |= BIT(NL80211_TID_STATS_RX_MSDU);
-                       tidstats->rx_msdu = sta->rx_stats.msdu[i];
-               }
-
-               if (!(tidstats->filled & BIT(NL80211_TID_STATS_TX_MSDU))) {
-                       tidstats->filled |= BIT(NL80211_TID_STATS_TX_MSDU);
-                       tidstats->tx_msdu = sta->tx_stats.msdu[i];
-               }
-
-               if (!(tidstats->filled &
-                               BIT(NL80211_TID_STATS_TX_MSDU_RETRIES)) &&
-                   ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
-                       tidstats->filled |=
-                               BIT(NL80211_TID_STATS_TX_MSDU_RETRIES);
-                       tidstats->tx_msdu_retries =
-                               sta->status_stats.msdu_retries[i];
-               }
-
-               if (!(tidstats->filled &
-                               BIT(NL80211_TID_STATS_TX_MSDU_FAILED)) &&
-                   ieee80211_hw_check(&local->hw, REPORTS_TX_ACK_STATUS)) {
-                       tidstats->filled |=
-                               BIT(NL80211_TID_STATS_TX_MSDU_FAILED);
-                       tidstats->tx_msdu_failed =
-                               sta->status_stats.msdu_failed[i];
-               }
+               sta_set_tidstats(sta, tidstats, i);
        }
 
        if (ieee80211_vif_is_mesh(&sdata->vif)) {
@@ -2150,3 +2210,10 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
                sinfo->expected_throughput = thr;
        }
 }
+
+unsigned long ieee80211_sta_last_active(struct sta_info *sta)
+{
+       if (time_after(sta->rx_stats.last_rx, sta->status_stats.last_ack))
+               return sta->rx_stats.last_rx;
+       return sta->status_stats.last_ack;
+}