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>
This commit is contained in:
Johannes Berg
2016-03-31 20:02:08 +03:00
rodzic b8da6b6a99
commit 4f6b1b3daa
3 zmienionych plików z 75 dodań i 47 usunięć

Wyświetl plik

@@ -1928,43 +1928,47 @@ 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;
rinfo->bw = (rate & STA_STATS_RATE_BW_MASK) >>
STA_STATS_RATE_BW_SHIFT;
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 {
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;
unsigned int shift;
sband = sta->local->hw.wiphy->bands[
ieee80211_get_sdata_band(sta->sdata)];
brate = sband->bitrates[sta->rx_stats.last_rate_idx].bitrate;
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;
}
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;
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 (rate == STA_STATS_RATE_INVALID)
rinfo->flags = 0;
else
rinfo->bw = RATE_INFO_BW_20;
sta_stats_decode_rate(sta->local, rate, rinfo);
}
void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)