From: Felix Fietkau Date: Mon, 11 Feb 2013 18:59:14 +0000 (+0000) Subject: mac80211: add CCK rate support to minstrel_ht to improve range X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=0564b6beef56faf3ff822f797f35b8f075c7b1c0;p=openwrt%2Fstaging%2F981213.git mac80211: add CCK rate support to minstrel_ht to improve range SVN-Revision: 35566 --- diff --git a/package/mac80211/patches/300-pending_work.patch b/package/mac80211/patches/300-pending_work.patch index 09728266cc..a0ddf42ad2 100644 --- a/package/mac80211/patches/300-pending_work.patch +++ b/package/mac80211/patches/300-pending_work.patch @@ -2637,3 +2637,521 @@ static void ath_tx_processq(struct ath_softc *sc, struct ath_txq *txq) { struct ath_hw *ah = sc->sc_ah; +--- a/net/mac80211/rc80211_minstrel.c ++++ b/net/mac80211/rc80211_minstrel.c +@@ -494,6 +494,33 @@ minstrel_free_sta(void *priv, struct iee + kfree(mi); + } + ++static void ++minstrel_init_cck_rates(struct minstrel_priv *mp) ++{ ++ static const int bitrates[4] = { 10, 20, 55, 110 }; ++ struct ieee80211_supported_band *sband; ++ int i, j; ++ ++ sband = mp->hw->wiphy->bands[IEEE80211_BAND_2GHZ]; ++ if (!sband) ++ return; ++ ++ for (i = 0, j = 0; i < sband->n_bitrates; i++) { ++ struct ieee80211_rate *rate = &sband->bitrates[i]; ++ ++ if (rate->flags & IEEE80211_RATE_ERP_G) ++ continue; ++ ++ for (j = 0; j < ARRAY_SIZE(bitrates); j++) { ++ if (rate->bitrate != bitrates[j]) ++ continue; ++ ++ mp->cck_rates[j] = i; ++ break; ++ } ++ } ++} ++ + static void * + minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) + { +@@ -539,6 +566,8 @@ minstrel_alloc(struct ieee80211_hw *hw, + S_IRUGO | S_IWUGO, debugfsdir, &mp->fixed_rate_idx); + #endif + ++ minstrel_init_cck_rates(mp); ++ + return mp; + } + +--- a/net/mac80211/rc80211_minstrel.h ++++ b/net/mac80211/rc80211_minstrel.h +@@ -79,6 +79,8 @@ struct minstrel_priv { + unsigned int lookaround_rate; + unsigned int lookaround_rate_mrr; + ++ u8 cck_rates[4]; ++ + #ifdef CONFIG_MAC80211_DEBUGFS + /* + * enable fixed rate processing per RC +--- a/net/mac80211/rc80211_minstrel_ht.c ++++ b/net/mac80211/rc80211_minstrel_ht.c +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2010 Felix Fietkau ++ * Copyright (C) 2010-2013 Felix Fietkau + * + * 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 +@@ -63,6 +63,30 @@ + } \ + } + ++#define CCK_DURATION(_bitrate, _short, _len) \ ++ (10 /* SIFS */ + \ ++ (_short ? 72 + 24 : 144 + 48 ) + \ ++ (8 * (_len + 4) * 10) / (_bitrate)) ++ ++#define CCK_ACK_DURATION(_bitrate, _short) \ ++ (CCK_DURATION((_bitrate > 10 ? 20 : 10), false, 60) + \ ++ CCK_DURATION(_bitrate, _short, AVG_PKT_SIZE)) ++ ++#define CCK_DURATION_LIST(_short) \ ++ CCK_ACK_DURATION(10, _short), \ ++ CCK_ACK_DURATION(20, _short), \ ++ CCK_ACK_DURATION(55, _short), \ ++ CCK_ACK_DURATION(110, _short) ++ ++#define CCK_GROUP \ ++ { \ ++ .streams = 0, \ ++ .duration = { \ ++ CCK_DURATION_LIST(false), \ ++ CCK_DURATION_LIST(true) \ ++ } \ ++ } ++ + /* + * To enable sufficiently targeted rate sampling, MCS rates are divided into + * groups, based on the number of streams and flags (HT40, SGI) that they +@@ -95,8 +119,13 @@ const struct mcs_group minstrel_mcs_grou + #if MINSTREL_MAX_STREAMS >= 3 + MCS_GROUP(3, 1, 1), + #endif ++ ++ /* must be last */ ++ CCK_GROUP + }; + ++#define MINSTREL_CCK_GROUP (ARRAY_SIZE(minstrel_mcs_groups) - 1) ++ + static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES]; + + /* +@@ -119,6 +148,29 @@ minstrel_ht_get_group_idx(struct ieee802 + !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)); + } + ++struct minstrel_rate_stats * ++minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, ++ struct ieee80211_tx_rate *rate) ++{ ++ int group, idx; ++ ++ if (rate->flags & IEEE80211_TX_RC_MCS) { ++ group = minstrel_ht_get_group_idx(rate); ++ idx = rate->idx % MCS_GROUP_RATES; ++ } else { ++ group = MINSTREL_CCK_GROUP; ++ ++ for (idx = 0; idx <= ARRAY_SIZE(mp->cck_rates); idx++) ++ if (rate->idx == mp->cck_rates[idx]) ++ break; ++ ++ /* short preamble */ ++ if (!(mi->groups[group].supported & BIT(idx))) ++ idx += 4; ++ } ++ return &mi->groups[group].rates[idx]; ++} ++ + static inline struct minstrel_rate_stats * + minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index) + { +@@ -159,7 +211,7 @@ static void + minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate) + { + struct minstrel_rate_stats *mr; +- unsigned int usecs; ++ unsigned int usecs = 0; + + mr = &mi->groups[group].rates[rate]; + +@@ -168,7 +220,9 @@ minstrel_ht_calc_tp(struct minstrel_ht_s + return; + } + +- usecs = mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len); ++ if (group != MINSTREL_CCK_GROUP) ++ usecs = mi->overhead / MINSTREL_TRUNC(mi->avg_ampdu_len); ++ + usecs += minstrel_mcs_groups[group].duration[rate]; + mr->cur_tp = MINSTREL_TRUNC((1000000 / usecs) * mr->probability); + } +@@ -231,10 +285,6 @@ minstrel_ht_update_stats(struct minstrel + if (!mr->cur_tp) + continue; + +- /* ignore the lowest rate of each single-stream group */ +- if (!i && minstrel_mcs_groups[group].streams == 1) +- continue; +- + if ((mr->cur_tp > cur_prob_tp && mr->probability > + MINSTREL_FRAC(3, 4)) || mr->probability > cur_prob) { + mg->max_prob_rate = index; +@@ -297,7 +347,7 @@ minstrel_ht_update_stats(struct minstrel + } + + static bool +-minstrel_ht_txstat_valid(struct ieee80211_tx_rate *rate) ++minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rate) + { + if (rate->idx < 0) + return false; +@@ -305,7 +355,13 @@ minstrel_ht_txstat_valid(struct ieee8021 + if (!rate->count) + return false; + +- return !!(rate->flags & IEEE80211_TX_RC_MCS); ++ if (rate->flags & IEEE80211_TX_RC_MCS); ++ return true; ++ ++ return rate->idx == mp->cck_rates[0] || ++ rate->idx == mp->cck_rates[1] || ++ rate->idx == mp->cck_rates[2] || ++ rate->idx == mp->cck_rates[3]; + } + + static void +@@ -390,7 +446,6 @@ minstrel_ht_tx_status(void *priv, struct + struct minstrel_rate_stats *rate, *rate2; + struct minstrel_priv *mp = priv; + bool last; +- int group; + int i; + + if (!msp->is_ht) +@@ -419,13 +474,12 @@ minstrel_ht_tx_status(void *priv, struct + if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) + mi->sample_packets += info->status.ampdu_len; + +- last = !minstrel_ht_txstat_valid(&ar[0]); ++ last = !minstrel_ht_txstat_valid(mp, &ar[0]); + for (i = 0; !last; i++) { + last = (i == IEEE80211_TX_MAX_RATES - 1) || +- !minstrel_ht_txstat_valid(&ar[i + 1]); ++ !minstrel_ht_txstat_valid(mp, &ar[i + 1]); + +- group = minstrel_ht_get_group_idx(&ar[i]); +- rate = &mi->groups[group].rates[ar[i].idx % 8]; ++ rate = minstrel_ht_get_stats(mp, mi, &ar[i]); + + if (last) + rate->success += info->status.ampdu_ack_len; +@@ -451,7 +505,8 @@ minstrel_ht_tx_status(void *priv, struct + + if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) { + minstrel_ht_update_stats(mp, mi); +- if (!(info->flags & IEEE80211_TX_CTL_AMPDU)) ++ if (!(info->flags & IEEE80211_TX_CTL_AMPDU) && ++ mi->max_prob_rate / MCS_GROUP_RATES != MINSTREL_CCK_GROUP) + minstrel_aggr_check(sta, skb); + } + } +@@ -467,6 +522,7 @@ minstrel_calc_retransmit(struct minstrel + unsigned int ctime = 0; + unsigned int t_slot = 9; /* FIXME */ + unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len); ++ unsigned int overhead = 0, overhead_rtscts = 0; + + mr = minstrel_get_ratestats(mi, index); + if (mr->probability < MINSTREL_FRAC(1, 10)) { +@@ -488,9 +544,14 @@ minstrel_calc_retransmit(struct minstrel + ctime += (t_slot * cw) >> 1; + cw = min((cw << 1) | 1, mp->cw_max); + ++ if (index / MCS_GROUP_RATES != MINSTREL_CCK_GROUP) { ++ overhead = mi->overhead; ++ overhead_rtscts = mi->overhead_rtscts; ++ } ++ + /* Total TX time for data and Contention after first 2 tries */ +- tx_time = ctime + 2 * (mi->overhead + tx_time_data); +- tx_time_rtscts = ctime + 2 * (mi->overhead_rtscts + tx_time_data); ++ tx_time = ctime + 2 * (overhead + tx_time_data); ++ tx_time_rtscts = ctime + 2 * (overhead_rtscts + tx_time_data); + + /* See how many more tries we can fit inside segment size */ + do { +@@ -499,8 +560,8 @@ minstrel_calc_retransmit(struct minstrel + cw = min((cw << 1) | 1, mp->cw_max); + + /* Total TX time after this try */ +- tx_time += ctime + mi->overhead + tx_time_data; +- tx_time_rtscts += ctime + mi->overhead_rtscts + tx_time_data; ++ tx_time += ctime + overhead + tx_time_data; ++ tx_time_rtscts += ctime + overhead_rtscts + tx_time_data; + + if (tx_time_rtscts < mp->segment_size) + mr->retry_count_rtscts++; +@@ -530,9 +591,16 @@ minstrel_ht_set_rate(struct minstrel_pri + else + rate->count = mr->retry_count; + +- rate->flags = IEEE80211_TX_RC_MCS | group->flags; ++ rate->flags = 0; + if (rtscts) + rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS; ++ ++ if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) { ++ rate->idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)]; ++ return; ++ } ++ ++ rate->flags |= IEEE80211_TX_RC_MCS | group->flags; + rate->idx = index % MCS_GROUP_RATES + (group->streams - 1) * MCS_GROUP_RATES; + } + +@@ -596,6 +664,22 @@ minstrel_get_sample_rate(struct minstrel + } + + static void ++minstrel_ht_check_cck_shortpreamble(struct minstrel_priv *mp, ++ struct minstrel_ht_sta *mi, bool val) ++{ ++ u8 supported = mi->groups[MINSTREL_CCK_GROUP].supported; ++ ++ if (!supported || !mi->cck_supported_short) ++ return; ++ ++ if (supported & (mi->cck_supported_short << (val * 4))) ++ return; ++ ++ supported ^= mi->cck_supported_short | (mi->cck_supported_short << 4); ++ mi->groups[MINSTREL_CCK_GROUP].supported = supported; ++} ++ ++static void + minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, + struct ieee80211_tx_rate_control *txrc) + { +@@ -614,6 +698,7 @@ minstrel_ht_get_rate(void *priv, struct + return mac80211_minstrel.get_rate(priv, sta, &msp->legacy, txrc); + + info->flags |= mi->tx_flags; ++ minstrel_ht_check_cck_shortpreamble(mp, mi, txrc->short_preamble); + + /* Don't use EAPOL frames for sampling on non-mrr hw */ + if (mp->hw->max_rates == 1 && +@@ -687,6 +772,30 @@ minstrel_ht_get_rate(void *priv, struct + } + + static void ++minstrel_ht_update_cck(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, ++ struct ieee80211_supported_band *sband, ++ struct ieee80211_sta *sta) ++{ ++ int i; ++ ++ if (sband->band != IEEE80211_BAND_2GHZ) ++ return; ++ ++ mi->cck_supported = 0; ++ mi->cck_supported_short = 0; ++ for (i = 0; i < 4; i++) { ++ if (!rate_supported(sta, sband->band, mp->cck_rates[i])) ++ continue; ++ ++ mi->cck_supported |= BIT(i); ++ if (sband->bitrates[i].flags & IEEE80211_RATE_SHORT_PREAMBLE) ++ mi->cck_supported_short |= BIT(i); ++ } ++ ++ mi->groups[MINSTREL_CCK_GROUP].supported = mi->cck_supported; ++} ++ ++static void + minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, + struct ieee80211_sta *sta, void *priv_sta) + { +@@ -706,7 +815,7 @@ minstrel_ht_update_caps(void *priv, stru + goto use_legacy; + + BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != +- MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS); ++ MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1); + + msp->is_ht = true; + memset(mi, 0, sizeof(*mi)); +@@ -742,6 +851,11 @@ minstrel_ht_update_caps(void *priv, stru + u16 req = 0; + + mi->groups[i].supported = 0; ++ if (i == MINSTREL_CCK_GROUP) { ++ minstrel_ht_update_cck(mp, mi, sband, sta); ++ continue; ++ } ++ + if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) { + if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + req |= IEEE80211_HT_CAP_SGI_40; +--- a/net/mac80211/rc80211_minstrel_ht.h ++++ b/net/mac80211/rc80211_minstrel_ht.h +@@ -107,8 +107,11 @@ struct minstrel_ht_sta { + /* current MCS group to be sampled */ + u8 sample_group; + ++ u8 cck_supported; ++ u8 cck_supported_short; ++ + /* MCS rate group info and statistics */ +- struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS]; ++ struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS + 1]; + }; + + struct minstrel_ht_sta_priv { +--- a/net/mac80211/rc80211_minstrel_ht_debugfs.c ++++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c +@@ -15,13 +15,76 @@ + #include "rc80211_minstrel.h" + #include "rc80211_minstrel_ht.h" + ++static char * ++minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p) ++{ ++ unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS; ++ const struct mcs_group *mg; ++ unsigned int j, tp, prob, eprob; ++ char htmode = '2'; ++ char gimode = 'L'; ++ ++ if (!mi->groups[i].supported) ++ return p; ++ ++ mg = &minstrel_mcs_groups[i]; ++ if (mg->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) ++ htmode = '4'; ++ if (mg->flags & IEEE80211_TX_RC_SHORT_GI) ++ gimode = 'S'; ++ ++ for (j = 0; j < MCS_GROUP_RATES; j++) { ++ struct minstrel_rate_stats *mr = &mi->groups[i].rates[j]; ++ static const int bitrates[4] = { 10, 20, 55, 110 }; ++ int idx = i * MCS_GROUP_RATES + j; ++ ++ if (!(mi->groups[i].supported & BIT(j))) ++ continue; ++ ++ if (i == max_mcs) ++ p += sprintf(p, "CCK/%cP ", j < 4 ? 'L' : 'S'); ++ else ++ p += sprintf(p, "HT%c0/%cGI ", htmode, gimode); ++ ++ *(p++) = (idx == mi->max_tp_rate) ? 'T' : ' '; ++ *(p++) = (idx == mi->max_tp_rate2) ? 't' : ' '; ++ *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' '; ++ ++ if (i == max_mcs) { ++ int r = bitrates[j % 4]; ++ p += sprintf(p, " %2u.%1uM", r / 10, r % 10); ++ } else { ++ p += sprintf(p, " MCS%-2u", (mg->streams - 1) * ++ MCS_GROUP_RATES + j); ++ } ++ ++ tp = mr->cur_tp / 10; ++ prob = MINSTREL_TRUNC(mr->cur_prob * 1000); ++ eprob = MINSTREL_TRUNC(mr->probability * 1000); ++ ++ p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u " ++ "%3u %3u(%3u) %8llu %8llu\n", ++ tp / 10, tp % 10, ++ eprob / 10, eprob % 10, ++ prob / 10, prob % 10, ++ mr->retry_count, ++ mr->last_success, ++ mr->last_attempts, ++ (unsigned long long)mr->succ_hist, ++ (unsigned long long)mr->att_hist); ++ } ++ ++ return p; ++} ++ + static int + minstrel_ht_stats_open(struct inode *inode, struct file *file) + { + struct minstrel_ht_sta_priv *msp = inode->i_private; + struct minstrel_ht_sta *mi = &msp->ht; + struct minstrel_debugfs_info *ms; +- unsigned int i, j, tp, prob, eprob; ++ unsigned int i; ++ unsigned int max_mcs = MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS; + char *p; + int ret; + +@@ -38,50 +101,13 @@ minstrel_ht_stats_open(struct inode *ino + + file->private_data = ms; + p = ms->buf; +- p += sprintf(p, "type rate throughput ewma prob this prob " +- "this succ/attempt success attempts\n"); +- for (i = 0; i < MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS; i++) { +- char htmode = '2'; +- char gimode = 'L'; +- +- if (!mi->groups[i].supported) +- continue; +- +- if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) +- htmode = '4'; +- if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_SHORT_GI) +- gimode = 'S'; +- +- for (j = 0; j < MCS_GROUP_RATES; j++) { +- struct minstrel_rate_stats *mr = &mi->groups[i].rates[j]; +- int idx = i * MCS_GROUP_RATES + j; ++ p += sprintf(p, "type rate throughput ewma prob this prob " ++ "retry this succ/attempt success attempts\n"); + +- if (!(mi->groups[i].supported & BIT(j))) +- continue; ++ p = minstrel_ht_stats_dump(mi, max_mcs, p); ++ for (i = 0; i < max_mcs; i++) ++ p = minstrel_ht_stats_dump(mi, i, p); + +- p += sprintf(p, "HT%c0/%cGI ", htmode, gimode); +- +- *(p++) = (idx == mi->max_tp_rate) ? 'T' : ' '; +- *(p++) = (idx == mi->max_tp_rate2) ? 't' : ' '; +- *(p++) = (idx == mi->max_prob_rate) ? 'P' : ' '; +- p += sprintf(p, "MCS%-2u", (minstrel_mcs_groups[i].streams - 1) * +- MCS_GROUP_RATES + j); +- +- tp = mr->cur_tp / 10; +- prob = MINSTREL_TRUNC(mr->cur_prob * 1000); +- eprob = MINSTREL_TRUNC(mr->probability * 1000); +- +- p += sprintf(p, " %6u.%1u %6u.%1u %6u.%1u " +- "%3u(%3u) %8llu %8llu\n", +- tp / 10, tp % 10, +- eprob / 10, eprob % 10, +- prob / 10, prob % 10, +- mr->last_success, +- mr->last_attempts, +- (unsigned long long)mr->succ_hist, +- (unsigned long long)mr->att_hist); +- } +- } + p += sprintf(p, "\nTotal packet count:: ideal %d " + "lookaround %d\n", + max(0, (int) mi->total_packets - (int) mi->sample_packets),