aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2016-03-31 13:02:08 -0400
committerJohannes Berg <johannes.berg@intel.com>2016-04-06 07:18:17 -0400
commit4f6b1b3daaf167bf927174224e07efd17ed95984 (patch)
tree5591e35073577767d0ffb3eadaf74a3a2e0ce9ae
parentb8da6b6a99b4b0d8d464b621ba7dcbcb08172b7d (diff)
mac80211: fix last RX rate data consistency
When storing the last_rate_* values in the RX code, there's nothing to guarantee consistency, so a concurrent reader could see, e.g. last_rate_idx on the new value, but last_rate_flag still on the old, getting completely bogus values in the end. To fix this, I lifted the sta_stats_encode_rate() function from my old rate statistics code, which encodes the entire rate data into a single 16-bit value, avoiding the consistency issue. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r--net/mac80211/rx.c21
-rw-r--r--net/mac80211/sta_info.c60
-rw-r--r--net/mac80211/sta_info.h45
3 files changed, 77 insertions, 49 deletions
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index d14c66df9e86..5a6c36c3aed6 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1421,16 +1421,9 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
1421 test_sta_flag(sta, WLAN_STA_AUTHORIZED)) { 1421 test_sta_flag(sta, WLAN_STA_AUTHORIZED)) {
1422 sta->rx_stats.last_rx = jiffies; 1422 sta->rx_stats.last_rx = jiffies;
1423 if (ieee80211_is_data(hdr->frame_control) && 1423 if (ieee80211_is_data(hdr->frame_control) &&
1424 !is_multicast_ether_addr(hdr->addr1)) { 1424 !is_multicast_ether_addr(hdr->addr1))
1425 sta->rx_stats.last_rate_idx = 1425 sta->rx_stats.last_rate =
1426 status->rate_idx; 1426 sta_stats_encode_rate(status);
1427 sta->rx_stats.last_rate_flag =
1428 status->flag;
1429 sta->rx_stats.last_rate_vht_flag =
1430 status->vht_flag;
1431 sta->rx_stats.last_rate_vht_nss =
1432 status->vht_nss;
1433 }
1434 } 1427 }
1435 } else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) { 1428 } else if (rx->sdata->vif.type == NL80211_IFTYPE_OCB) {
1436 sta->rx_stats.last_rx = jiffies; 1429 sta->rx_stats.last_rx = jiffies;
@@ -1440,12 +1433,8 @@ ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
1440 * match the current local configuration when processed. 1433 * match the current local configuration when processed.
1441 */ 1434 */
1442 sta->rx_stats.last_rx = jiffies; 1435 sta->rx_stats.last_rx = jiffies;
1443 if (ieee80211_is_data(hdr->frame_control)) { 1436 if (ieee80211_is_data(hdr->frame_control))
1444 sta->rx_stats.last_rate_idx = status->rate_idx; 1437 sta->rx_stats.last_rate = sta_stats_encode_rate(status);
1445 sta->rx_stats.last_rate_flag = status->flag;
1446 sta->rx_stats.last_rate_vht_flag = status->vht_flag;
1447 sta->rx_stats.last_rate_vht_nss = status->vht_nss;
1448 }
1449 } 1438 }
1450 1439
1451 if (rx->sdata->vif.type == NL80211_IFTYPE_STATION) 1440 if (rx->sdata->vif.type == NL80211_IFTYPE_STATION)
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index ac73b9c7e8d8..0b50ae3f0b05 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -1928,43 +1928,47 @@ u8 sta_info_tx_streams(struct sta_info *sta)
1928 >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1; 1928 >> IEEE80211_HT_MCS_TX_MAX_STREAMS_SHIFT) + 1;
1929} 1929}
1930 1930
1931static void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo) 1931static void sta_stats_decode_rate(struct ieee80211_local *local, u16 rate,
1932 struct rate_info *rinfo)
1932{ 1933{
1933 rinfo->flags = 0; 1934 rinfo->bw = (rate & STA_STATS_RATE_BW_MASK) >>
1934 1935 STA_STATS_RATE_BW_SHIFT;
1935 if (sta->rx_stats.last_rate_flag & RX_FLAG_HT) { 1936
1936 rinfo->flags |= RATE_INFO_FLAGS_MCS; 1937 if (rate & STA_STATS_RATE_VHT) {
1937 rinfo->mcs = sta->rx_stats.last_rate_idx; 1938 rinfo->flags = RATE_INFO_FLAGS_VHT_MCS;
1938 } else if (sta->rx_stats.last_rate_flag & RX_FLAG_VHT) { 1939 rinfo->mcs = rate & 0xf;
1939 rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS; 1940 rinfo->nss = (rate & 0xf0) >> 4;
1940 rinfo->nss = sta->rx_stats.last_rate_vht_nss; 1941 } else if (rate & STA_STATS_RATE_HT) {
1941 rinfo->mcs = sta->rx_stats.last_rate_idx; 1942 rinfo->flags = RATE_INFO_FLAGS_MCS;
1942 } else { 1943 rinfo->mcs = rate & 0xff;
1944 } else if (rate & STA_STATS_RATE_LEGACY) {
1943 struct ieee80211_supported_band *sband; 1945 struct ieee80211_supported_band *sband;
1944 int shift = ieee80211_vif_get_shift(&sta->sdata->vif);
1945 u16 brate; 1946 u16 brate;
1946 1947 unsigned int shift;
1947 sband = sta->local->hw.wiphy->bands[ 1948
1948 ieee80211_get_sdata_band(sta->sdata)]; 1949 sband = local->hw.wiphy->bands[(rate >> 4) & 0xf];
1949 brate = sband->bitrates[sta->rx_stats.last_rate_idx].bitrate; 1950 brate = sband->bitrates[rate & 0xf].bitrate;
1951 if (rinfo->bw == RATE_INFO_BW_5)
1952 shift = 2;
1953 else if (rinfo->bw == RATE_INFO_BW_10)
1954 shift = 1;
1955 else
1956 shift = 0;
1950 rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift); 1957 rinfo->legacy = DIV_ROUND_UP(brate, 1 << shift);
1951 } 1958 }
1952 1959
1953 if (sta->rx_stats.last_rate_flag & RX_FLAG_SHORT_GI) 1960 if (rate & STA_STATS_RATE_SGI)
1954 rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; 1961 rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI;
1962}
1963
1964static void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo)
1965{
1966 u16 rate = ACCESS_ONCE(sta->rx_stats.last_rate);
1955 1967
1956 if (sta->rx_stats.last_rate_flag & RX_FLAG_5MHZ) 1968 if (rate == STA_STATS_RATE_INVALID)
1957 rinfo->bw = RATE_INFO_BW_5; 1969 rinfo->flags = 0;
1958 else if (sta->rx_stats.last_rate_flag & RX_FLAG_10MHZ)
1959 rinfo->bw = RATE_INFO_BW_10;
1960 else if (sta->rx_stats.last_rate_flag & RX_FLAG_40MHZ)
1961 rinfo->bw = RATE_INFO_BW_40;
1962 else if (sta->rx_stats.last_rate_vht_flag & RX_VHT_FLAG_80MHZ)
1963 rinfo->bw = RATE_INFO_BW_80;
1964 else if (sta->rx_stats.last_rate_vht_flag & RX_VHT_FLAG_160MHZ)
1965 rinfo->bw = RATE_INFO_BW_160;
1966 else 1970 else
1967 rinfo->bw = RATE_INFO_BW_20; 1971 sta_stats_decode_rate(sta->local, rate, rinfo);
1968} 1972}
1969 1973
1970void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) 1974void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 8a8e84bfe3d2..5549ceb9cbb3 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -1,7 +1,7 @@
1/* 1/*
2 * Copyright 2002-2005, Devicescape Software, Inc. 2 * Copyright 2002-2005, Devicescape Software, Inc.
3 * Copyright 2013-2014 Intel Mobile Communications GmbH 3 * Copyright 2013-2014 Intel Mobile Communications GmbH
4 * Copyright(c) 2015 Intel Deutschland GmbH 4 * Copyright(c) 2015-2016 Intel Deutschland GmbH
5 * 5 *
6 * This program is free software; you can redistribute it and/or modify 6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as 7 * it under the terms of the GNU General Public License version 2 as
@@ -452,10 +452,7 @@ struct sta_info {
452 int last_signal; 452 int last_signal;
453 u8 chains; 453 u8 chains;
454 s8 chain_signal_last[IEEE80211_MAX_CHAINS]; 454 s8 chain_signal_last[IEEE80211_MAX_CHAINS];
455 int last_rate_idx; 455 u16 last_rate;
456 u32 last_rate_flag;
457 u32 last_rate_vht_flag;
458 u8 last_rate_vht_nss;
459 u64 msdu[IEEE80211_NUM_TIDS + 1]; 456 u64 msdu[IEEE80211_NUM_TIDS + 1];
460 } rx_stats; 457 } rx_stats;
461 struct { 458 struct {
@@ -683,4 +680,42 @@ void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta);
683 680
684unsigned long ieee80211_sta_last_active(struct sta_info *sta); 681unsigned long ieee80211_sta_last_active(struct sta_info *sta);
685 682
683#define STA_STATS_RATE_INVALID 0
684#define STA_STATS_RATE_VHT 0x8000
685#define STA_STATS_RATE_HT 0x4000
686#define STA_STATS_RATE_LEGACY 0x2000
687#define STA_STATS_RATE_SGI 0x1000
688#define STA_STATS_RATE_BW_SHIFT 9
689#define STA_STATS_RATE_BW_MASK (0x7 << STA_STATS_RATE_BW_SHIFT)
690
691static inline u16 sta_stats_encode_rate(struct ieee80211_rx_status *s)
692{
693 u16 r = s->rate_idx;
694
695 if (s->vht_flag & RX_VHT_FLAG_80MHZ)
696 r |= RATE_INFO_BW_80 << STA_STATS_RATE_BW_SHIFT;
697 else if (s->vht_flag & RX_VHT_FLAG_160MHZ)
698 r |= RATE_INFO_BW_160 << STA_STATS_RATE_BW_SHIFT;
699 else if (s->flag & RX_FLAG_40MHZ)
700 r |= RATE_INFO_BW_40 << STA_STATS_RATE_BW_SHIFT;
701 else if (s->flag & RX_FLAG_10MHZ)
702 r |= RATE_INFO_BW_10 << STA_STATS_RATE_BW_SHIFT;
703 else if (s->flag & RX_FLAG_5MHZ)
704 r |= RATE_INFO_BW_5 << STA_STATS_RATE_BW_SHIFT;
705 else
706 r |= RATE_INFO_BW_20 << STA_STATS_RATE_BW_SHIFT;
707
708 if (s->flag & RX_FLAG_SHORT_GI)
709 r |= STA_STATS_RATE_SGI;
710
711 if (s->flag & RX_FLAG_VHT)
712 r |= STA_STATS_RATE_VHT | (s->vht_nss << 4);
713 else if (s->flag & RX_FLAG_HT)
714 r |= STA_STATS_RATE_HT;
715 else
716 r |= STA_STATS_RATE_LEGACY | (s->band << 4);
717
718 return r;
719}
720
686#endif /* STA_INFO_H */ 721#endif /* STA_INFO_H */