Skip to content

Commit

Permalink
mac80211/minstrel_ht: add support for using CCK rates
Browse files Browse the repository at this point in the history
When MCS rates start to get bad in 2.4 GHz because of long range or
strong interference, CCK rates can be a lot more robust.

This patch adds a pseudo MCS group containing CCK rates (long preamble
in the lower 4 slots, short preamble in the upper slots).

Signed-off-by: Felix Fietkau <nbd@openwrt.org>
[make minstrel_ht_get_stats static]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
Felix Fietkau authored and Johannes Berg committed Feb 13, 2013
1 parent 6719429 commit a0497f9
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 60 deletions.
29 changes: 29 additions & 0 deletions net/mac80211/rc80211_minstrel.c
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,33 @@ minstrel_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta)
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)
{
Expand Down Expand Up @@ -539,6 +566,8 @@ minstrel_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir)
S_IRUGO | S_IWUGO, debugfsdir, &mp->fixed_rate_idx);
#endif

minstrel_init_cck_rates(mp);

return mp;
}

Expand Down
2 changes: 2 additions & 0 deletions net/mac80211/rc80211_minstrel.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
152 changes: 135 additions & 17 deletions net/mac80211/rc80211_minstrel_ht.c
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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 \
[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS] = { \
.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
Expand Down Expand Up @@ -95,8 +119,13 @@ const struct mcs_group minstrel_mcs_groups[] = {
#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];

/*
Expand All @@ -119,6 +148,29 @@ minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate)
!!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH));
}

static 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)
{
Expand Down Expand Up @@ -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];

Expand All @@ -168,7 +220,9 @@ minstrel_ht_calc_tp(struct minstrel_ht_sta *mi, int group, int rate)
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);
}
Expand Down Expand Up @@ -293,15 +347,21 @@ minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
}

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;

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
Expand Down Expand Up @@ -386,7 +446,6 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
struct minstrel_rate_stats *rate, *rate2;
struct minstrel_priv *mp = priv;
bool last;
int group;
int i;

if (!msp->is_ht)
Expand Down Expand Up @@ -415,13 +474,12 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,
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;
Expand All @@ -447,7 +505,8 @@ minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband,

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);
}
}
Expand All @@ -463,6 +522,7 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
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)) {
Expand All @@ -484,9 +544,14 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
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 {
Expand All @@ -495,8 +560,8 @@ minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
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++;
Expand Down Expand Up @@ -526,9 +591,16 @@ minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
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;
}

Expand Down Expand Up @@ -591,6 +663,22 @@ minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi)
return sample_idx;
}

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)
Expand All @@ -610,6 +698,7 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
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 &&
Expand Down Expand Up @@ -682,6 +771,30 @@ minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
}
}

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)
Expand All @@ -702,7 +815,7 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
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));
Expand Down Expand Up @@ -738,6 +851,11 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
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;
Expand Down
5 changes: 4 additions & 1 deletion net/mac80211/rc80211_minstrel_ht.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading

0 comments on commit a0497f9

Please sign in to comment.