summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73>2013-02-11 18:59:14 +0000
committernbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73>2013-02-11 18:59:14 +0000
commitad1503d4c1643e7bdd762f1372a070ee43c7aee8 (patch)
treeb3dd22091ca399224370288056b42505bd4845d9
parentbd93518c7bf055677fea8b489d1da76f9038f58b (diff)
mac80211: add CCK rate support to minstrel_ht to improve range
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@35566 3c298f89-4303-0410-b956-a3cf2f4a3e73
-rw-r--r--package/mac80211/patches/300-pending_work.patch518
1 files changed, 518 insertions, 0 deletions
diff --git a/package/mac80211/patches/300-pending_work.patch b/package/mac80211/patches/300-pending_work.patch
index 09728266c..a0ddf42ad 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 <nbd@openwrt.org>
++ * Copyright (C) 2010-2013 Felix Fietkau <nbd@openwrt.org>
+ *
+ * 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),