diff options
author | Johannes Berg <johannes.berg@intel.com> | 2016-03-31 13:02:08 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2016-04-06 07:18:17 -0400 |
commit | 4f6b1b3daaf167bf927174224e07efd17ed95984 (patch) | |
tree | 5591e35073577767d0ffb3eadaf74a3a2e0ce9ae | |
parent | b8da6b6a99b4b0d8d464b621ba7dcbcb08172b7d (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.c | 21 | ||||
-rw-r--r-- | net/mac80211/sta_info.c | 60 | ||||
-rw-r--r-- | net/mac80211/sta_info.h | 45 |
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 | ||
1931 | static void sta_set_rate_info_rx(struct sta_info *sta, struct rate_info *rinfo) | 1931 | static 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 | |||
1964 | static 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 | ||
1970 | void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) | 1974 | void 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 | ||
684 | unsigned long ieee80211_sta_last_active(struct sta_info *sta); | 681 | unsigned 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 | |||
691 | static 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 */ |