mac80211: fix RX u64 stats consistency on 32-bit platforms
authorJohannes Berg <johannes.berg@intel.com>
Thu, 31 Mar 2016 17:02:09 +0000 (20:02 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 6 Apr 2016 11:18:17 +0000 (13:18 +0200)
On 32-bit platforms, the 64-bit counters we keep need to be protected
to be consistently read. Use the u64_stats_sync mechanism to do that.

In order to not end up with overly long lines, refactor the tidstats
assignments a bit.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
net/mac80211/rx.c
net/mac80211/sta_info.c
net/mac80211/sta_info.h

index 5a6c36c..2863832 100644 (file)
@@ -1441,7 +1441,11 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
                ieee80211_sta_rx_notify(rx->sdata, hdr);
 
        sta->rx_stats.fragments++;
+
+       u64_stats_update_begin(&rx->sta->rx_stats.syncp);
        sta->rx_stats.bytes += rx->skb->len;
+       u64_stats_update_end(&rx->sta->rx_stats.syncp);
+
        if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
                sta->rx_stats.last_signal = status->signal;
                ewma_signal_add(&sta->rx_stats_avg.signal, -status->signal);
@@ -2124,7 +2128,9 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
                 * for non-QoS-data frames. Here we know it's a data
                 * frame, so count MSDUs.
                 */
+               u64_stats_update_begin(&rx->sta->rx_stats.syncp);
                rx->sta->rx_stats.msdu[rx->seqno_idx]++;
+               u64_stats_update_end(&rx->sta->rx_stats.syncp);
        }
 
        if ((sdata->vif.type == NL80211_IFTYPE_AP ||
index 0b50ae3..bdd303e 100644 (file)
@@ -335,6 +335,8 @@ 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 */
@@ -1971,6 +1973,41 @@ static void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
                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)
 {
        struct ieee80211_sub_if_data *sdata = sta->sdata;
@@ -2025,7 +2062,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);
        }
 
@@ -2097,33 +2139,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)) {
index 5549ceb..7c23b57 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/average.h>
 #include <linux/etherdevice.h>
 #include <linux/rhashtable.h>
+#include <linux/u64_stats_sync.h>
 #include "key.h"
 
 /**
@@ -444,7 +445,6 @@ struct sta_info {
        /* Updated from RX path only, no locking requirements */
        struct {
                unsigned long packets;
-               u64 bytes;
                unsigned long last_rx;
                unsigned long num_duplicates;
                unsigned long fragments;
@@ -453,6 +453,9 @@ struct sta_info {
                u8 chains;
                s8 chain_signal_last[IEEE80211_MAX_CHAINS];
                u16 last_rate;
+
+               struct u64_stats_sync syncp;
+               u64 bytes;
                u64 msdu[IEEE80211_NUM_TIDS + 1];
        } rx_stats;
        struct {