From: Felix Fietkau Date: Fri, 26 Mar 2010 22:36:12 +0000 (+0000) Subject: mac80211: update to wireless-testing 2010-03-24 X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=282981d2573c3cad60fb6b37a023bbf114eee6b8;p=openwrt%2Fstaging%2Fansuel.git mac80211: update to wireless-testing 2010-03-24 SVN-Revision: 20497 --- diff --git a/package/mac80211/Makefile b/package/mac80211/Makefile index bd569acfe7..549e838967 100644 --- a/package/mac80211/Makefile +++ b/package/mac80211/Makefile @@ -10,12 +10,12 @@ include $(INCLUDE_DIR)/kernel.mk PKG_NAME:=mac80211 -PKG_VERSION:=2010-03-03 +PKG_VERSION:=2010-03-24 PKG_RELEASE:=3 PKG_SOURCE_URL:=http://mirror2.openwrt.org/sources # http://www.orbit-lab.org/kernel/compat-wireless-2.6/2010/11 \ # http://wireless.kernel.org/download/compat-wireless-2.6 -PKG_MD5SUM:=af8da65ca4c25b1b69e3d2896d2bbb2f +PKG_MD5SUM:=73357c52b5d6888ea3228b2ca8aa5eca PKG_SOURCE:=compat-wireless-$(PKG_VERSION).tar.bz2 PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/compat-wireless-$(PKG_VERSION) @@ -676,9 +676,11 @@ MAKE_OPTS:= \ CONFIG_AT76C50X_USB= \ CONFIG_WL12XX= \ CONFIG_EEPROM_93CX6= \ + CONFIG_HERMES= \ CONFIG_AR9170_USB=$(if $(CONFIG_PACKAGE_kmod-ar9170),m) \ CONFIG_AR9170_LEDS=$(CONFIG_LEDS_TRIGGERS) \ CONFIG_IWM= \ + CONFIG_ATH9K_HTC= \ MADWIFI= \ OLD_IWL= \ KLIB_BUILD="$(LINUX_DIR)" \ diff --git a/package/mac80211/patches/001-disable_b44.patch b/package/mac80211/patches/001-disable_b44.patch index 0682e746cd..ce10e71bbe 100644 --- a/package/mac80211/patches/001-disable_b44.patch +++ b/package/mac80211/patches/001-disable_b44.patch @@ -1,6 +1,6 @@ --- a/config.mk +++ b/config.mk -@@ -271,8 +271,8 @@ endif +@@ -269,8 +269,8 @@ endif CONFIG_P54_PCI=m diff --git a/package/mac80211/patches/002-disable_rfkill.patch b/package/mac80211/patches/002-disable_rfkill.patch index 17fd787dda..2599dbb841 100644 --- a/package/mac80211/patches/002-disable_rfkill.patch +++ b/package/mac80211/patches/002-disable_rfkill.patch @@ -9,7 +9,7 @@ ifeq ($(CONFIG_MAC80211),y) $(error "ERROR: you have MAC80211 compiled into the kernel, CONFIG_MAC80211=y, as such you cannot replace its mac80211 driver. You need this set to CONFIG_MAC80211=m. If you are using Fedora upgrade your kernel as later version should this set as modular. For further information on Fedora see https://bugzilla.redhat.com/show_bug.cgi?id=470143. If you are using your own kernel recompile it and make mac80211 modular") -@@ -461,8 +461,8 @@ endif +@@ -492,8 +492,8 @@ endif # We need the backported rfkill module on kernel < 2.6.31. # In more recent kernel versions use the in kernel rfkill module. ifdef CONFIG_COMPAT_KERNEL_31 diff --git a/package/mac80211/patches/007-remove_misc_drivers.patch b/package/mac80211/patches/007-remove_misc_drivers.patch index c648b86516..a51a4b36e8 100644 --- a/package/mac80211/patches/007-remove_misc_drivers.patch +++ b/package/mac80211/patches/007-remove_misc_drivers.patch @@ -1,6 +1,6 @@ --- a/config.mk +++ b/config.mk -@@ -296,10 +296,10 @@ endif +@@ -299,10 +299,10 @@ endif CONFIG_MWL8K=m # Ethernet drivers go here @@ -13,9 +13,9 @@ +# CONFIG_ATL1E=m +# CONFIG_ATL1C=m - endif - ## end of PCI -@@ -338,10 +338,10 @@ CONFIG_USB_NET_COMPAT_RNDIS_HOST=n + CONFIG_HERMES=m + CONFIG_HERMES_CACHE_FW_ON_INIT=y +@@ -355,10 +355,10 @@ CONFIG_USB_NET_COMPAT_RNDIS_HOST=n CONFIG_USB_NET_COMPAT_RNDIS_WLAN=n CONFIG_USB_NET_COMPAT_CDCETHER=n else diff --git a/package/mac80211/patches/010-no_pcmcia.patch b/package/mac80211/patches/010-no_pcmcia.patch index 1334dbb141..a9437ce8ea 100644 --- a/package/mac80211/patches/010-no_pcmcia.patch +++ b/package/mac80211/patches/010-no_pcmcia.patch @@ -9,7 +9,7 @@ CONFIG_SSB=m else include $(KLIB_BUILD)/.config -@@ -197,7 +197,7 @@ CONFIG_B43=m +@@ -194,7 +194,7 @@ CONFIG_B43=m CONFIG_B43_HWRNG=y CONFIG_B43_PCI_AUTOSELECT=y ifneq ($(CONFIG_PCMCIA),) @@ -18,7 +18,7 @@ endif CONFIG_B43_LEDS=y CONFIG_B43_PHY_LP=y -@@ -248,7 +248,7 @@ CONFIG_SSB_BLOCKIO=y +@@ -246,7 +246,7 @@ CONFIG_SSB_BLOCKIO=y CONFIG_SSB_PCIHOST=y CONFIG_SSB_B43_PCI_BRIDGE=y ifneq ($(CONFIG_PCMCIA),) diff --git a/package/mac80211/patches/011-no_sdio.patch b/package/mac80211/patches/011-no_sdio.patch index aa651edd22..bd6bdfe78f 100644 --- a/package/mac80211/patches/011-no_sdio.patch +++ b/package/mac80211/patches/011-no_sdio.patch @@ -1,6 +1,6 @@ --- a/config.mk +++ b/config.mk -@@ -382,8 +382,8 @@ endif # end of SPI driver list +@@ -407,8 +407,8 @@ endif # end of SPI driver list ifneq ($(CONFIG_MMC),) @@ -10,4 +10,4 @@ +# CONFIG_B43_SDIO=y CONFIG_WL1251_SDIO=m - ifdef CONFIG_COMPAT_KERNEL_27 + ifneq ($(CONFIG_ARM),) diff --git a/package/mac80211/patches/201-ath5k-WAR-for-AR71xx-PCI-bug.patch b/package/mac80211/patches/201-ath5k-WAR-for-AR71xx-PCI-bug.patch index 4f34469769..81c049615d 100644 --- a/package/mac80211/patches/201-ath5k-WAR-for-AR71xx-PCI-bug.patch +++ b/package/mac80211/patches/201-ath5k-WAR-for-AR71xx-PCI-bug.patch @@ -1,6 +1,6 @@ --- a/drivers/net/wireless/ath/ath5k/reset.c +++ b/drivers/net/wireless/ath/ath5k/reset.c -@@ -1360,10 +1360,18 @@ int ath5k_hw_reset(struct ath5k_hw *ah, +@@ -1377,10 +1377,18 @@ int ath5k_hw_reset(struct ath5k_hw *ah, * guess we can tweak it and see how it goes ;-) */ if (ah->ah_version != AR5K_AR5210) { diff --git a/package/mac80211/patches/408-ath9k_tweak_rx_intr_mitigation.patch b/package/mac80211/patches/408-ath9k_tweak_rx_intr_mitigation.patch index 48401bcd19..9d8ce1a330 100644 --- a/package/mac80211/patches/408-ath9k_tweak_rx_intr_mitigation.patch +++ b/package/mac80211/patches/408-ath9k_tweak_rx_intr_mitigation.patch @@ -1,6 +1,6 @@ --- a/drivers/net/wireless/ath/ath9k/hw.c +++ b/drivers/net/wireless/ath/ath9k/hw.c -@@ -2111,7 +2111,7 @@ int ath9k_hw_reset(struct ath_hw *ah, st +@@ -2097,7 +2097,7 @@ int ath9k_hw_reset(struct ath_hw *ah, st if (ah->config.rx_intr_mitigation) { REG_RMW_FIELD(ah, AR_RIMT, AR_RIMT_LAST, 500); diff --git a/package/mac80211/patches/520-cfg80211_get_freq.patch b/package/mac80211/patches/520-cfg80211_get_freq.patch index 928cf40425..f7e478f294 100644 --- a/package/mac80211/patches/520-cfg80211_get_freq.patch +++ b/package/mac80211/patches/520-cfg80211_get_freq.patch @@ -20,7 +20,7 @@ } --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c -@@ -885,6 +885,11 @@ static int nl80211_send_iface(struct sk_ +@@ -886,6 +886,11 @@ static int nl80211_send_iface(struct sk_ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name); NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype); diff --git a/package/mac80211/patches/530-ath9k_fix_ampdu_rate_handling.patch b/package/mac80211/patches/530-ath9k_fix_ampdu_rate_handling.patch deleted file mode 100644 index 4e1d599726..0000000000 --- a/package/mac80211/patches/530-ath9k_fix_ampdu_rate_handling.patch +++ /dev/null @@ -1,42 +0,0 @@ ---- a/drivers/net/wireless/ath/ath9k/xmit.c -+++ b/drivers/net/wireless/ath/ath9k/xmit.c -@@ -1947,10 +1947,10 @@ static void ath_tx_rc_status(struct ath_ - tx_rateindex = ds->ds_txstat.ts_rateindex; - WARN_ON(tx_rateindex >= hw->max_rates); - -- if (update_rc) -- tx_info->pad[0] |= ATH_TX_INFO_UPDATE_RC; - if (ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) - tx_info->flags |= IEEE80211_TX_STAT_TX_FILTERED; -+ if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && update_rc) -+ tx_info->flags |= IEEE80211_TX_STAT_AMPDU; - - if ((ds->ds_txstat.ts_status & ATH9K_TXERR_FILT) == 0 && - (bf->bf_flags & ATH9K_TXDESC_NOACK) == 0 && update_rc) { ---- a/drivers/net/wireless/ath/ath9k/rc.h -+++ b/drivers/net/wireless/ath/ath9k/rc.h -@@ -172,7 +172,6 @@ struct ath_rate_priv { - - #define ATH_TX_INFO_FRAME_TYPE_INTERNAL (1 << 0) - #define ATH_TX_INFO_FRAME_TYPE_PAUSE (1 << 1) --#define ATH_TX_INFO_UPDATE_RC (1 << 2) - #define ATH_TX_INFO_XRETRY (1 << 3) - #define ATH_TX_INFO_UNDERRUN (1 << 4) - ---- a/drivers/net/wireless/ath/ath9k/rc.c -+++ b/drivers/net/wireless/ath/ath9k/rc.c -@@ -1226,8 +1226,12 @@ static void ath_tx_status(void *priv, st - long_retry = rate->count - 1; - } - -- if (!priv_sta || !ieee80211_is_data(fc) || -- !(tx_info->pad[0] & ATH_TX_INFO_UPDATE_RC)) -+ if (!priv_sta || !ieee80211_is_data(fc)) -+ return; -+ -+ /* This packet was aggregated but doesn't carry status info */ -+ if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && -+ !(tx_info->flags & IEEE80211_TX_STAT_AMPDU)) - return; - - if (tx_info->flags & IEEE80211_TX_STAT_TX_FILTERED) diff --git a/package/mac80211/patches/530-minstrel_ht.patch b/package/mac80211/patches/530-minstrel_ht.patch new file mode 100644 index 0000000000..c51d541fbb --- /dev/null +++ b/package/mac80211/patches/530-minstrel_ht.patch @@ -0,0 +1,1137 @@ +--- a/net/mac80211/Makefile ++++ b/net/mac80211/Makefile +@@ -47,8 +47,8 @@ CFLAGS_driver-trace.o := -I$(src) + rc80211_pid-y := rc80211_pid_algo.o + rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o + +-rc80211_minstrel-y := rc80211_minstrel.o +-rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_debugfs.o ++rc80211_minstrel-y := rc80211_minstrel.o rc80211_minstrel_ht.o ++rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_debugfs.o rc80211_minstrel_ht_debugfs.o + + mac80211-$(CONFIG_MAC80211_RC_PID) += $(rc80211_pid-y) + mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y) +--- a/net/mac80211/main.c ++++ b/net/mac80211/main.c +@@ -714,6 +714,10 @@ static int __init ieee80211_init(void) + if (ret) + return ret; + ++ ret = rc80211_minstrel_ht_init(); ++ if (ret) ++ goto err_minstrel; ++ + ret = rc80211_pid_init(); + if (ret) + goto err_pid; +@@ -726,6 +730,8 @@ static int __init ieee80211_init(void) + err_netdev: + rc80211_pid_exit(); + err_pid: ++ rc80211_minstrel_ht_exit(); ++ err_minstrel: + rc80211_minstrel_exit(); + + return ret; +@@ -734,6 +740,7 @@ static int __init ieee80211_init(void) + static void __exit ieee80211_exit(void) + { + rc80211_pid_exit(); ++ rc80211_minstrel_ht_exit(); + rc80211_minstrel_exit(); + + /* +--- a/net/mac80211/rate.h ++++ b/net/mac80211/rate.h +@@ -137,6 +137,8 @@ static inline void rc80211_pid_exit(void + #ifdef CONFIG_MAC80211_RC_MINSTREL + extern int rc80211_minstrel_init(void); + extern void rc80211_minstrel_exit(void); ++extern int rc80211_minstrel_ht_init(void); ++extern void rc80211_minstrel_ht_exit(void); + #else + static inline int rc80211_minstrel_init(void) + { +@@ -145,6 +147,13 @@ static inline int rc80211_minstrel_init( + static inline void rc80211_minstrel_exit(void) + { + } ++static inline int rc80211_minstrel_ht_init(void) ++{ ++ return 0; ++} ++static inline void rc80211_minstrel_ht_exit(void) ++{ ++} + #endif + + +--- /dev/null ++++ b/net/mac80211/rc80211_minstrel_ht.c +@@ -0,0 +1,815 @@ ++/* ++ * Copyright (C) 2010 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 ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include "rate.h" ++#include "rc80211_minstrel.h" ++#include "rc80211_minstrel_ht.h" ++ ++#define AVG_PKT_SIZE 1200 ++#define SAMPLE_COLUMNS 10 ++#define EWMA_LEVEL 75 ++ ++/* Number of bits for an average sized packet */ ++#define MCS_NBITS (AVG_PKT_SIZE << 3) ++ ++/* Number of symbols for a packet with (bps) bits per symbol */ ++#define MCS_NSYMS(bps) ((MCS_NBITS + (bps) - 1) / (bps)) ++ ++/* Transmission time for a packet containing (syms) symbols */ ++#define MCS_SYMBOL_TIME(sgi, syms) \ ++ (sgi ? \ ++ ((syms) * 18 + 4) / 5 : /* syms * 3.6 us */ \ ++ (syms) << 2 /* syms * 4 us */ \ ++ ) ++ ++/* Transmit duration for the raw data part of an average sized packet */ ++#define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps))) ++ ++/* MCS rate information for an MCS group */ ++#define MCS_GROUP(_streams, _sgi, _ht40) { \ ++ .streams = _streams, \ ++ .flags = \ ++ (_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \ ++ (_ht40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0), \ ++ .duration = { \ ++ MCS_DURATION(_streams, _sgi, _ht40 ? 54 : 26), \ ++ MCS_DURATION(_streams, _sgi, _ht40 ? 108 : 52), \ ++ MCS_DURATION(_streams, _sgi, _ht40 ? 162 : 78), \ ++ MCS_DURATION(_streams, _sgi, _ht40 ? 216 : 104), \ ++ MCS_DURATION(_streams, _sgi, _ht40 ? 324 : 156), \ ++ MCS_DURATION(_streams, _sgi, _ht40 ? 432 : 208), \ ++ MCS_DURATION(_streams, _sgi, _ht40 ? 486 : 234), \ ++ MCS_DURATION(_streams, _sgi, _ht40 ? 540 : 260) \ ++ } \ ++} ++ ++/* ++ * To enable sufficiently targeted rate sampling, MCS rates are divided into ++ * groups, based on the number of streams and flags (HT40, SGI) that they ++ * use. ++ */ ++const struct mcs_group minstrel_mcs_groups[] = { ++ MCS_GROUP(1, 0, 0), ++ MCS_GROUP(2, 0, 0), ++#if MINSTREL_MAX_STREAMS >= 3 ++ MCS_GROUP(3, 0, 0), ++#endif ++ ++ MCS_GROUP(1, 1, 0), ++ MCS_GROUP(2, 1, 0), ++#if MINSTREL_MAX_STREAMS >= 3 ++ MCS_GROUP(3, 1, 0), ++#endif ++ ++ MCS_GROUP(1, 0, 1), ++ MCS_GROUP(2, 0, 1), ++#if MINSTREL_MAX_STREAMS >= 3 ++ MCS_GROUP(3, 0, 1), ++#endif ++ ++ MCS_GROUP(1, 1, 1), ++ MCS_GROUP(2, 1, 1), ++#if MINSTREL_MAX_STREAMS >= 3 ++ MCS_GROUP(3, 1, 1), ++#endif ++}; ++ ++static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES]; ++ ++/* ++ * Perform EWMA (Exponentially Weighted Moving Average) calculation ++ */ ++static int ++minstrel_ewma(int old, int new, int weight) ++{ ++ return (new * (100 - weight) + old * weight) / 100; ++} ++ ++/* ++ * Look up an MCS group index based on mac80211 rate information ++ */ ++static int ++minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate) ++{ ++ int streams = (rate->idx / MCS_GROUP_RATES) + 1; ++ u32 flags = IEEE80211_TX_RC_SHORT_GI | IEEE80211_TX_RC_40_MHZ_WIDTH; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(minstrel_mcs_groups); i++) { ++ if (minstrel_mcs_groups[i].streams != streams) ++ continue; ++ if (minstrel_mcs_groups[i].flags != (rate->flags & flags)) ++ continue; ++ ++ return i; ++ } ++ ++ WARN_ON(1); ++ return 0; ++} ++ ++static inline struct minstrel_rate_stats * ++minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index) ++{ ++ return &mi->groups[index / MCS_GROUP_RATES].rates[index % MCS_GROUP_RATES]; ++} ++ ++ ++/* ++ * Recalculate success probabilities and counters for a rate using EWMA ++ */ ++static void ++minstrel_calc_rate_ewma(struct minstrel_priv *mp, struct minstrel_rate_stats *mr) ++{ ++ if (unlikely(mr->attempts > 0)) { ++ mr->sample_skipped = 0; ++ mr->cur_prob = MINSTREL_FRAC(mr->success, mr->attempts); ++ if (!mr->att_hist) ++ mr->probability = mr->cur_prob; ++ else ++ mr->probability = minstrel_ewma(mr->probability, ++ mr->cur_prob, EWMA_LEVEL); ++ mr->att_hist += mr->attempts; ++ mr->succ_hist += mr->success; ++ } else { ++ mr->sample_skipped++; ++ } ++ mr->last_success = mr->success; ++ mr->last_attempts = mr->attempts; ++ mr->success = 0; ++ mr->attempts = 0; ++} ++ ++/* ++ * Calculate throughput based on the average A-MPDU length, taking into account ++ * the expected number of retransmissions and their expected length ++ */ ++static void ++minstrel_ht_calc_tp(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, ++ int group, int rate) ++{ ++ struct minstrel_rate_stats *mr; ++ unsigned int usecs; ++ ++ mr = &mi->groups[group].rates[rate]; ++ ++ if (mr->probability < MINSTREL_FRAC(1, 10)) { ++ mr->cur_tp = 0; ++ return; ++ } ++ ++ 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); ++} ++ ++/* ++ * Update rate statistics and select new primary rates ++ * ++ * Rules for rate selection: ++ * - max_prob_rate must use only one stream, as a tradeoff between delivery ++ * probability and throughput during strong fluctuations ++ * - as long as the max prob rate has a probability of more than 3/4, pick ++ * higher throughput rates, even if the probablity is a bit lower ++ */ ++static void ++minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) ++{ ++ struct minstrel_mcs_group_data *mg; ++ struct minstrel_rate_stats *mr; ++ int cur_prob, cur_prob_tp, cur_tp, cur_tp2; ++ int group, i, index; ++ ++ if (mi->ampdu_packets > 0) { ++ mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len, ++ MINSTREL_FRAC(mi->ampdu_len, mi->ampdu_packets), EWMA_LEVEL); ++ mi->ampdu_len = 0; ++ mi->ampdu_packets = 0; ++ } ++ ++ mi->sample_slow = 0; ++ mi->sample_count = 0; ++ mi->max_tp_rate = 0; ++ mi->max_tp_rate2 = 0; ++ mi->max_prob_rate = 0; ++ ++ for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { ++ cur_prob = 0; ++ cur_prob_tp = 0; ++ cur_tp = 0; ++ cur_tp2 = 0; ++ ++ mg = &mi->groups[group]; ++ if (!mg->supported) ++ continue; ++ ++ mg->max_tp_rate = 0; ++ mg->max_tp_rate2 = 0; ++ mg->max_prob_rate = 0; ++ mi->sample_count++; ++ ++ for (i = 0; i < MCS_GROUP_RATES; i++) { ++ if (!(mg->supported & BIT(i))) ++ continue; ++ ++ mr = &mg->rates[i]; ++ mr->retry_updated = false; ++ index = MCS_GROUP_RATES * group + i; ++ minstrel_calc_rate_ewma(mp, mr); ++ minstrel_ht_calc_tp(mp, mi, group, i); ++ ++ 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; ++ cur_prob = mr->probability; ++ } ++ ++ if (mr->cur_tp > cur_tp) { ++ swap(index, mg->max_tp_rate); ++ cur_tp = mr->cur_tp; ++ mr = minstrel_get_ratestats(mi, index); ++ } ++ ++ if (index >= mg->max_tp_rate) ++ continue; ++ ++ if (mr->cur_tp > cur_tp2) { ++ mg->max_tp_rate2 = index; ++ cur_tp2 = mr->cur_tp; ++ } ++ } ++ } ++ ++ /* try to sample up to half of the availble rates during each interval */ ++ mi->sample_count *= 4; ++ ++ cur_prob = 0; ++ cur_prob_tp = 0; ++ cur_tp = 0; ++ cur_tp2 = 0; ++ for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { ++ mg = &mi->groups[group]; ++ if (!mg->supported) ++ continue; ++ ++ mr = minstrel_get_ratestats(mi, mg->max_prob_rate); ++ if (cur_prob_tp < mr->cur_tp && ++ minstrel_mcs_groups[group].streams == 1) { ++ mi->max_prob_rate = mg->max_prob_rate; ++ cur_prob = mr->cur_prob; ++ } ++ ++ mr = minstrel_get_ratestats(mi, mg->max_tp_rate); ++ if (cur_tp < mr->cur_tp) { ++ mi->max_tp_rate = mg->max_tp_rate; ++ cur_tp = mr->cur_tp; ++ } ++ ++ mr = minstrel_get_ratestats(mi, mg->max_tp_rate2); ++ if (cur_tp2 < mr->cur_tp) { ++ mi->max_tp_rate2 = mg->max_tp_rate2; ++ cur_tp2 = mr->cur_tp; ++ } ++ } ++ ++ mi->stats_update = jiffies; ++} ++ ++static bool ++minstrel_ht_txstat_valid(struct ieee80211_tx_rate *rate) ++{ ++ if (!rate->count) ++ return false; ++ ++ if (rate->idx < 0) ++ return false; ++ ++ return !!(rate->flags & IEEE80211_TX_RC_MCS); ++} ++ ++static void ++minstrel_next_sample_idx(struct minstrel_ht_sta *mi) ++{ ++ struct minstrel_mcs_group_data *mg; ++ ++ for (;;) { ++ mi->sample_group++; ++ mi->sample_group %= ARRAY_SIZE(minstrel_mcs_groups); ++ mg = &mi->groups[mi->sample_group]; ++ ++ if (!mg->supported) ++ continue; ++ ++ if (++mg->index > MCS_GROUP_RATES) { ++ mg->index = 0; ++ if (++mg->column > ARRAY_SIZE(sample_table)) ++ mg->column = 0; ++ } ++ break; ++ } ++} ++ ++static void ++minstrel_downgrade_rate(struct minstrel_ht_sta *mi, int *idx, bool primary) ++{ ++ int group, orig_group; ++ ++ orig_group = group = *idx / MCS_GROUP_RATES; ++ while (group > 0) { ++ group--; ++ ++ if (!mi->groups[group].supported) ++ continue; ++ ++ if (minstrel_mcs_groups[group].streams > ++ minstrel_mcs_groups[orig_group].streams) ++ continue; ++ ++ if (primary) ++ *idx = mi->groups[group].max_tp_rate; ++ else ++ *idx = mi->groups[group].max_tp_rate2; ++ break; ++ } ++} ++ ++static void ++minstrel_aggr_check(struct minstrel_priv *mp, struct ieee80211_sta *pubsta, struct sk_buff *skb) ++{ ++ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; ++ struct sta_info *sta = container_of(pubsta, struct sta_info, sta); ++ u16 tid; ++ ++ if (unlikely(!ieee80211_is_data_qos(hdr->frame_control))) ++ return; ++ ++ if (unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE))) ++ return; ++ ++ tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; ++ if (likely(sta->ampdu_mlme.tid_state_tx[tid] != HT_AGG_STATE_IDLE)) ++ return; ++ ++ ieee80211_start_tx_ba_session(pubsta, tid); ++} ++ ++static void ++minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, ++ struct ieee80211_sta *sta, void *priv_sta, ++ struct sk_buff *skb) ++{ ++ struct minstrel_ht_sta_priv *msp = priv_sta; ++ struct minstrel_ht_sta *mi = &msp->ht; ++ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); ++ struct ieee80211_tx_rate *ar = info->status.rates; ++ struct minstrel_rate_stats *rate, *rate2; ++ struct minstrel_priv *mp = priv; ++ bool last = false; ++ int group; ++ int i = 0; ++ ++ if (!msp->is_ht) ++ return mac80211_minstrel.tx_status(priv, sband, sta, &msp->legacy, skb); ++ ++ /* This packet was aggregated but doesn't carry status info */ ++ if ((info->flags & IEEE80211_TX_CTL_AMPDU) && ++ !(info->flags & IEEE80211_TX_STAT_AMPDU)) ++ return; ++ ++ if (!info->status.ampdu_len) { ++ info->status.ampdu_ack_len = 1; ++ info->status.ampdu_len = 1; ++ } ++ ++ mi->ampdu_packets++; ++ mi->ampdu_len += info->status.ampdu_len; ++ ++ if (!mi->sample_wait && !mi->sample_tries && mi->sample_count > 0) { ++ mi->sample_wait = 4 + MINSTREL_TRUNC(mi->avg_ampdu_len); ++ mi->sample_tries = 3; ++ mi->sample_count--; ++ } ++ ++ if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) { ++ mi->sample_packets += info->status.ampdu_len; ++ minstrel_next_sample_idx(mi); ++ } ++ ++ for (i = 0; !last; i++) { ++ last = (i == IEEE80211_TX_MAX_RATES - 1) || ++ !minstrel_ht_txstat_valid(&ar[i + 1]); ++ ++ if (!minstrel_ht_txstat_valid(&ar[i])) ++ break; ++ ++ group = minstrel_ht_get_group_idx(&ar[i]); ++ rate = &mi->groups[group].rates[ar[i].idx % 8]; ++ ++ if (last && (info->flags & IEEE80211_TX_STAT_ACK)) ++ rate->success += info->status.ampdu_ack_len; ++ ++ rate->attempts += ar[i].count * info->status.ampdu_len; ++ } ++ ++ /* ++ * check for sudden death of spatial multiplexing, ++ * downgrade to a lower number of streams if necessary. ++ */ ++ rate = minstrel_get_ratestats(mi, mi->max_tp_rate); ++ if (rate->attempts > 30 && ++ MINSTREL_FRAC(rate->success, rate->attempts) < ++ MINSTREL_FRAC(20, 100)) ++ minstrel_downgrade_rate(mi, &mi->max_tp_rate, true); ++ ++ rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate2); ++ if (rate->attempts > 30 && ++ MINSTREL_FRAC(rate->success, rate->attempts) < ++ MINSTREL_FRAC(20, 100)) ++ minstrel_downgrade_rate(mi, &mi->max_tp_rate2, false); ++ ++ if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) { ++ minstrel_ht_update_stats(mp, mi); ++ minstrel_aggr_check(mp, sta, skb); ++ } ++} ++ ++static void ++minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, ++ int index) ++{ ++ struct minstrel_rate_stats *mr; ++ const struct mcs_group *group; ++ unsigned int tx_time, tx_time_rtscts, tx_time_data; ++ unsigned int cw = mp->cw_min; ++ unsigned int t_slot = 9; /* FIXME */ ++ unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len); ++ ++ mr = minstrel_get_ratestats(mi, index); ++ if (mr->probability < MINSTREL_FRAC(1, 10)) { ++ mr->retry_count = 1; ++ mr->retry_count_rtscts = 1; ++ return; ++ } ++ ++ mr->retry_count = 2; ++ mr->retry_count_rtscts = 2; ++ mr->retry_updated = true; ++ ++ group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; ++ tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len; ++ tx_time = 2 * (t_slot + mi->overhead + tx_time_data); ++ tx_time_rtscts = 2 * (t_slot + mi->overhead_rtscts + tx_time_data); ++ do { ++ cw = (cw << 1) | 1; ++ cw = min(cw, mp->cw_max); ++ tx_time += cw + t_slot + mi->overhead; ++ tx_time_rtscts += cw + t_slot + mi->overhead_rtscts; ++ if (tx_time_rtscts < mp->segment_size) ++ mr->retry_count_rtscts++; ++ } while ((tx_time < mp->segment_size) && ++ (++mr->retry_count < mp->max_retry)); ++} ++ ++ ++static void ++minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, ++ struct ieee80211_tx_rate *rate, int index, ++ struct ieee80211_tx_rate_control *txrc, ++ bool sample, bool rtscts) ++{ ++ const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; ++ struct minstrel_rate_stats *mr; ++ ++ mr = minstrel_get_ratestats(mi, index); ++ if (!mr->retry_updated) ++ minstrel_calc_retransmit(mp, mi, index); ++ ++ if (mr->probability < MINSTREL_FRAC(20, 100)) ++ rate->count = 2; ++ else if (rtscts) ++ rate->count = mr->retry_count_rtscts; ++ else ++ rate->count = mr->retry_count; ++ ++ rate->flags = IEEE80211_TX_RC_MCS | group->flags; ++ if (txrc->short_preamble) ++ rate->flags |= IEEE80211_TX_RC_USE_SHORT_PREAMBLE; ++ if (txrc->rts || rtscts) ++ rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS; ++ rate->idx = index % MCS_GROUP_RATES + (group->streams - 1) * MCS_GROUP_RATES; ++} ++ ++static inline int ++minstrel_get_duration(int index) ++{ ++ const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; ++ return group->duration[index % MCS_GROUP_RATES]; ++} ++ ++static int ++minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) ++{ ++ struct minstrel_rate_stats *mr; ++ struct minstrel_mcs_group_data *mg; ++ int sample_idx = 0; ++ ++ if (mi->sample_wait > 0) { ++ mi->sample_wait--; ++ return -1; ++ } ++ ++ if (!mi->sample_tries) ++ return -1; ++ ++ mi->sample_tries--; ++ mg = &mi->groups[mi->sample_group]; ++ sample_idx = sample_table[mg->column][mg->index]; ++ mr = &mg->rates[sample_idx]; ++ sample_idx += mi->sample_group * MCS_GROUP_RATES; ++ ++ /* ++ * When not using MRR, do not sample if the probability is already ++ * higher than 95% to avoid wasting airtime ++ */ ++ if (!mp->has_mrr && (mr->probability > MINSTREL_FRAC(95, 100))) ++ goto next; ++ ++ /* ++ * Make sure that lower rates get sampled only occasionally, ++ * if the link is working perfectly. ++ */ ++ if (minstrel_get_duration(sample_idx) > ++ minstrel_get_duration(mi->max_tp_rate)) { ++ if (mr->sample_skipped < 10) ++ goto next; ++ ++ if (mi->sample_slow++ > 2) ++ goto next; ++ } ++ ++ return sample_idx; ++ ++next: ++ minstrel_next_sample_idx(mi); ++ return -1; ++} ++ ++static void ++minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, ++ struct ieee80211_tx_rate_control *txrc) ++{ ++ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb); ++ struct ieee80211_tx_rate *ar = info->status.rates; ++ struct minstrel_ht_sta_priv *msp = priv_sta; ++ struct minstrel_ht_sta *mi = &msp->ht; ++ struct minstrel_priv *mp = priv; ++ int sample_idx; ++ ++ if (rate_control_send_low(sta, priv_sta, txrc)) ++ return; ++ ++ if (!msp->is_ht) ++ return mac80211_minstrel.get_rate(priv, sta, &msp->legacy, txrc); ++ ++ sample_idx = minstrel_get_sample_rate(mp, mi); ++ if (sample_idx >= 0) { ++ minstrel_ht_set_rate(mp, mi, &ar[0], sample_idx, ++ txrc, true, false); ++ minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_tp_rate, ++ txrc, false, true); ++ info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; ++ } else { ++ minstrel_ht_set_rate(mp, mi, &ar[0], mi->max_tp_rate, ++ txrc, false, false); ++ minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_tp_rate2, ++ txrc, false, true); ++ } ++ minstrel_ht_set_rate(mp, mi, &ar[2], mi->max_prob_rate, txrc, false, true); ++ ++ ar[3].count = 0; ++ ar[3].idx = -1; ++ ++ mi->total_packets++; ++ ++ /* wraparound */ ++ if (mi->total_packets == ~0) { ++ mi->total_packets = 0; ++ mi->sample_packets = 0; ++ } ++} ++ ++static void ++minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, ++ struct ieee80211_sta *sta, void *priv_sta, ++ enum nl80211_channel_type oper_chan_type) ++{ ++ struct minstrel_priv *mp = priv; ++ struct minstrel_ht_sta_priv *msp = priv_sta; ++ struct minstrel_ht_sta *mi = &msp->ht; ++ struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs; ++ struct ieee80211_local *local = hw_to_local(mp->hw); ++ u16 sta_cap = sta->ht_cap.cap; ++ int ack_dur; ++ int i; ++ ++ /* fall back to the old minstrel for legacy stations */ ++ if (sta && !sta->ht_cap.ht_supported) { ++ msp->is_ht = false; ++ memset(&msp->legacy, 0, sizeof(msp->legacy)); ++ msp->legacy.r = msp->ratelist; ++ msp->legacy.sample_table = msp->sample_table; ++ return mac80211_minstrel.rate_init(priv, sband, sta, &msp->legacy); ++ } ++ ++ BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != ++ MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS); ++ ++ msp->is_ht = true; ++ memset(mi, 0, sizeof(*mi)); ++ mi->stats_update = jiffies; ++ ++ ack_dur = ieee80211_frame_duration(local, 10, 60, 1, 1); ++ mi->overhead = ieee80211_frame_duration(local, 0, 60, 1, 1) + ack_dur; ++ mi->overhead_rtscts = mi->overhead + 2 * ack_dur; ++ ++ mi->avg_ampdu_len = MINSTREL_FRAC(1, 1); ++ ++ /* When using MRR, sample more on the first attempt, without delay */ ++ if (mp->has_mrr) { ++ mi->sample_count = 16; ++ mi->sample_wait = 0; ++ } else { ++ mi->sample_count = 8; ++ mi->sample_wait = 8; ++ } ++ mi->sample_tries = 4; ++ ++ if (oper_chan_type != NL80211_CHAN_HT40MINUS && ++ oper_chan_type != NL80211_CHAN_HT40PLUS) ++ sta_cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; ++ ++ for (i = 0; i < ARRAY_SIZE(mi->groups); i++) { ++ u16 req = 0; ++ ++ mi->groups[i].supported = 0; ++ 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; ++ else ++ req |= IEEE80211_HT_CAP_SGI_20; ++ } ++ ++ if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) ++ req |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; ++ ++ if ((sta_cap & req) != req) ++ continue; ++ ++ mi->groups[i].supported = ++ mcs->rx_mask[minstrel_mcs_groups[i].streams - 1]; ++ } ++} ++ ++static void ++minstrel_ht_rate_init(void *priv, struct ieee80211_supported_band *sband, ++ struct ieee80211_sta *sta, void *priv_sta) ++{ ++ struct minstrel_priv *mp = priv; ++ ++ minstrel_ht_update_caps(priv, sband, sta, priv_sta, mp->hw->conf.channel_type); ++} ++ ++static void ++minstrel_ht_rate_update(void *priv, struct ieee80211_supported_band *sband, ++ struct ieee80211_sta *sta, void *priv_sta, ++ u32 changed, enum nl80211_channel_type oper_chan_type) ++{ ++ minstrel_ht_update_caps(priv, sband, sta, priv_sta, oper_chan_type); ++} ++ ++static void * ++minstrel_ht_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp) ++{ ++ struct ieee80211_supported_band *sband; ++ struct minstrel_ht_sta_priv *msp; ++ struct minstrel_priv *mp = priv; ++ struct ieee80211_hw *hw = mp->hw; ++ int max_rates = 0; ++ int i; ++ ++ for (i = 0; i < IEEE80211_NUM_BANDS; i++) { ++ sband = hw->wiphy->bands[i]; ++ if (sband && sband->n_bitrates > max_rates) ++ max_rates = sband->n_bitrates; ++ } ++ ++ msp = kzalloc(sizeof(struct minstrel_ht_sta), gfp); ++ if (!msp) ++ return NULL; ++ ++ msp->ratelist = kzalloc(sizeof(struct minstrel_rate) * max_rates, gfp); ++ if (!msp->ratelist) ++ goto error; ++ ++ msp->sample_table = kmalloc(SAMPLE_COLUMNS * max_rates, gfp); ++ if (!msp->sample_table) ++ goto error1; ++ ++ return msp; ++ ++error1: ++ kfree(msp->sample_table); ++error: ++ kfree(msp); ++ return NULL; ++} ++ ++static void ++minstrel_ht_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta) ++{ ++ struct minstrel_ht_sta_priv *msp = priv_sta; ++ ++ kfree(msp->sample_table); ++ kfree(msp->ratelist); ++ kfree(msp); ++} ++ ++static void * ++minstrel_ht_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) ++{ ++ return mac80211_minstrel.alloc(hw, debugfsdir); ++} ++ ++static void ++minstrel_ht_free(void *priv) ++{ ++ mac80211_minstrel.free(priv); ++} ++ ++static struct rate_control_ops mac80211_minstrel_ht = { ++ .name = "minstrel_ht", ++ .tx_status = minstrel_ht_tx_status, ++ .get_rate = minstrel_ht_get_rate, ++ .rate_init = minstrel_ht_rate_init, ++ .rate_update = minstrel_ht_rate_update, ++ .alloc_sta = minstrel_ht_alloc_sta, ++ .free_sta = minstrel_ht_free_sta, ++ .alloc = minstrel_ht_alloc, ++ .free = minstrel_ht_free, ++#ifdef CONFIG_MAC80211_DEBUGFS ++ .add_sta_debugfs = minstrel_ht_add_sta_debugfs, ++ .remove_sta_debugfs = minstrel_ht_remove_sta_debugfs, ++#endif ++}; ++ ++ ++static void ++init_sample_table(void) ++{ ++ int col, i, new_idx; ++ u8 rnd[MCS_GROUP_RATES]; ++ ++ memset(sample_table, 0xff, sizeof(sample_table)); ++ for (col = 0; col < SAMPLE_COLUMNS; col++) { ++ for (i = 0; i < MCS_GROUP_RATES; i++) { ++ get_random_bytes(rnd, sizeof(rnd)); ++ new_idx = (i + rnd[i]) % MCS_GROUP_RATES; ++ ++ while (sample_table[col][new_idx] != 0xff) ++ new_idx = (new_idx + 1) % MCS_GROUP_RATES; ++ ++ sample_table[col][new_idx] = i; ++ } ++ } ++} ++ ++int __init ++rc80211_minstrel_ht_init(void) ++{ ++ init_sample_table(); ++ return ieee80211_rate_control_register(&mac80211_minstrel_ht); ++} ++ ++void ++rc80211_minstrel_ht_exit(void) ++{ ++ ieee80211_rate_control_unregister(&mac80211_minstrel_ht); ++} +--- /dev/null ++++ b/net/mac80211/rc80211_minstrel_ht.h +@@ -0,0 +1,125 @@ ++/* ++ * Copyright (C) 2010 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 ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef __RC_MINSTREL_HT_H ++#define __RC_MINSTREL_HT_H ++ ++/* ++ * maximum number of spatial streams to make use of ++ * set this value to 3 once we have drivers that support it ++ */ ++#define MINSTREL_MAX_STREAMS 2 ++#define MINSTREL_STREAM_GROUPS 4 ++ ++/* scaled fraction values */ ++#define MINSTREL_SCALE 16 ++#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div) ++#define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE) ++ ++#define MCS_GROUP_RATES 8 ++ ++struct mcs_group { ++ u32 flags; ++ unsigned int streams; ++ unsigned int duration[MCS_GROUP_RATES]; ++}; ++ ++struct minstrel_rate_stats { ++ /* current / last sampling period attempts/success counters */ ++ unsigned int attempts, last_attempts; ++ unsigned int success, last_success; ++ ++ /* total attempts/success counters */ ++ u64 att_hist, succ_hist; ++ ++ /* current throughput */ ++ unsigned int cur_tp; ++ ++ /* packet delivery probabilities */ ++ unsigned int cur_prob, probability; ++ ++ /* maximum retry counts */ ++ unsigned int retry_count; ++ unsigned int retry_count_rtscts; ++ ++ bool retry_updated; ++ u8 sample_skipped; ++}; ++ ++struct minstrel_mcs_group_data { ++ u8 index; ++ u8 column; ++ ++ /* bitfield of supported MCS rates of this group */ ++ u8 supported; ++ ++ /* selected primary rates */ ++ unsigned int max_tp_rate; ++ unsigned int max_tp_rate2; ++ unsigned int max_prob_rate; ++ ++ /* MCS rate statistics */ ++ struct minstrel_rate_stats rates[MCS_GROUP_RATES]; ++}; ++ ++struct minstrel_ht_sta { ++ /* ampdu length (average, per sampling interval) */ ++ unsigned int ampdu_len; ++ unsigned int ampdu_packets; ++ ++ /* ampdu length (EWMA) */ ++ unsigned int avg_ampdu_len; ++ ++ /* best throughput rate */ ++ unsigned int max_tp_rate; ++ ++ /* second best throughput rate */ ++ unsigned int max_tp_rate2; ++ ++ /* best probability rate */ ++ unsigned int max_prob_rate; ++ ++ /* time of last status update */ ++ unsigned long stats_update; ++ ++ /* overhead time in usec for each frame */ ++ unsigned int overhead; ++ unsigned int overhead_rtscts; ++ ++ unsigned int total_packets; ++ unsigned int sample_packets; ++ ++ u8 sample_wait; ++ u8 sample_tries; ++ u8 sample_count; ++ u8 sample_slow; ++ ++ /* current MCS group to be sampled */ ++ u8 sample_group; ++ ++ /* MCS rate group info and statistics */ ++ struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS]; ++}; ++ ++struct minstrel_ht_sta_priv { ++ union { ++ struct minstrel_ht_sta ht; ++ struct minstrel_sta_info legacy; ++ }; ++#ifdef CONFIG_MAC80211_DEBUGFS ++ struct dentry *dbg_stats; ++#endif ++ void *ratelist; ++ void *sample_table; ++ bool is_ht; ++}; ++ ++void minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); ++void minstrel_ht_remove_sta_debugfs(void *priv, void *priv_sta); ++ ++#endif +--- /dev/null ++++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c +@@ -0,0 +1,120 @@ ++/* ++ * Copyright (C) 2010 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 ++ * published by the Free Software Foundation. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include "rc80211_minstrel.h" ++#include "rc80211_minstrel_ht.h" ++ ++extern const struct mcs_group minstrel_mcs_groups[]; ++ ++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; ++ char *p; ++ int ret; ++ ++ if (!msp->is_ht) { ++ inode->i_private = &msp->legacy; ++ ret = minstrel_stats_open(inode, file); ++ inode->i_private = msp; ++ return ret; ++ } ++ ++ ms = kmalloc(sizeof(*ms) + 8192, GFP_KERNEL); ++ if (!ms) ++ return -ENOMEM; ++ ++ 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; ++ ++ if (!(mi->groups[i].supported & BIT(j))) ++ continue; ++ ++ 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), ++ mi->sample_packets); ++ p += sprintf(p, "Average A-MPDU length: %d.%d\n", ++ MINSTREL_TRUNC(mi->avg_ampdu_len), ++ MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10); ++ ms->len = p - ms->buf; ++ ++ return 0; ++} ++ ++static const struct file_operations minstrel_ht_stat_fops = { ++ .owner = THIS_MODULE, ++ .open = minstrel_ht_stats_open, ++ .read = minstrel_stats_read, ++ .release = minstrel_stats_release, ++}; ++ ++void ++minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir) ++{ ++ struct minstrel_ht_sta_priv *msp = priv_sta; ++ ++ msp->dbg_stats = debugfs_create_file("rc_stats", S_IRUGO, dir, msp, ++ &minstrel_ht_stat_fops); ++} ++ ++void ++minstrel_ht_remove_sta_debugfs(void *priv, void *priv_sta) ++{ ++ struct minstrel_ht_sta_priv *msp = priv_sta; ++ ++ debugfs_remove(msp->dbg_stats); ++} diff --git a/package/mac80211/patches/540-ath9k_use_minstrel.patch b/package/mac80211/patches/540-ath9k_use_minstrel.patch new file mode 100644 index 0000000000..0983abf335 --- /dev/null +++ b/package/mac80211/patches/540-ath9k_use_minstrel.patch @@ -0,0 +1,14 @@ +--- a/drivers/net/wireless/ath/ath9k/init.c ++++ b/drivers/net/wireless/ath/ath9k/init.c +@@ -655,7 +655,11 @@ void ath9k_set_hw_capab(struct ath_softc + hw->sta_data_size = sizeof(struct ath_node); + hw->vif_data_size = sizeof(struct ath_vif); + ++#ifdef ATH9K_USE_MINSTREL ++ hw->rate_control_algorithm = "minstrel_ht"; ++#else + hw->rate_control_algorithm = "ath9k_rate_control"; ++#endif + + if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = diff --git a/package/mac80211/patches/540-minstrel_debugfs_cleanup.patch b/package/mac80211/patches/540-minstrel_debugfs_cleanup.patch deleted file mode 100644 index 9a13bae70b..0000000000 --- a/package/mac80211/patches/540-minstrel_debugfs_cleanup.patch +++ /dev/null @@ -1,80 +0,0 @@ ---- a/net/mac80211/rc80211_minstrel.h -+++ b/net/mac80211/rc80211_minstrel.h -@@ -80,6 +80,11 @@ struct minstrel_priv { - unsigned int lookaround_rate_mrr; - }; - -+struct minstrel_debugfs_info { -+ size_t len; -+ char buf[]; -+}; -+ - void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); - void minstrel_remove_sta_debugfs(void *priv, void *priv_sta); - ---- a/net/mac80211/rc80211_minstrel_debugfs.c -+++ b/net/mac80211/rc80211_minstrel_debugfs.c -@@ -52,21 +52,15 @@ - #include - #include "rc80211_minstrel.h" - --struct minstrel_stats_info { -- struct minstrel_sta_info *mi; -- char buf[4096]; -- size_t len; --}; -- - static int - minstrel_stats_open(struct inode *inode, struct file *file) - { - struct minstrel_sta_info *mi = inode->i_private; -- struct minstrel_stats_info *ms; -+ struct minstrel_debugfs_info *ms; - unsigned int i, tp, prob, eprob; - char *p; - -- ms = kmalloc(sizeof(*ms), GFP_KERNEL); -+ ms = kmalloc(sizeof(*ms) + 4096, GFP_KERNEL); - if (!ms) - return -ENOMEM; - -@@ -107,35 +101,18 @@ minstrel_stats_open(struct inode *inode, - } - - static ssize_t --minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *o) -+minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) - { -- struct minstrel_stats_info *ms; -- char *src; -+ struct minstrel_debugfs_info *ms; - - ms = file->private_data; -- src = ms->buf; -- -- len = min(len, ms->len); -- if (len <= *o) -- return 0; -- -- src += *o; -- len -= *o; -- *o += len; -- -- if (copy_to_user(buf, src, len)) -- return -EFAULT; -- -- return len; -+ return simple_read_from_buffer(buf, len, ppos, ms->buf, ms->len); - } - - static int - minstrel_stats_release(struct inode *inode, struct file *file) - { -- struct minstrel_stats_info *ms = file->private_data; -- -- kfree(ms); -- -+ kfree(file->private_data); - return 0; - } - diff --git a/package/mac80211/patches/550-minstrel_extern.patch b/package/mac80211/patches/550-minstrel_extern.patch deleted file mode 100644 index 8a6064c3e6..0000000000 --- a/package/mac80211/patches/550-minstrel_extern.patch +++ /dev/null @@ -1,56 +0,0 @@ ---- a/net/mac80211/rc80211_minstrel.h -+++ b/net/mac80211/rc80211_minstrel.h -@@ -85,7 +85,13 @@ struct minstrel_debugfs_info { - char buf[]; - }; - -+extern struct rate_control_ops mac80211_minstrel; - void minstrel_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); - void minstrel_remove_sta_debugfs(void *priv, void *priv_sta); - -+/* debugfs */ -+int minstrel_stats_open(struct inode *inode, struct file *file); -+ssize_t minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *o); -+int minstrel_stats_release(struct inode *inode, struct file *file); -+ - #endif ---- a/net/mac80211/rc80211_minstrel.c -+++ b/net/mac80211/rc80211_minstrel.c -@@ -541,7 +541,7 @@ minstrel_free(void *priv) - kfree(priv); - } - --static struct rate_control_ops mac80211_minstrel = { -+struct rate_control_ops mac80211_minstrel = { - .name = "minstrel", - .tx_status = minstrel_tx_status, - .get_rate = minstrel_get_rate, ---- a/net/mac80211/rc80211_minstrel_debugfs.c -+++ b/net/mac80211/rc80211_minstrel_debugfs.c -@@ -52,7 +52,7 @@ - #include - #include "rc80211_minstrel.h" - --static int -+int - minstrel_stats_open(struct inode *inode, struct file *file) - { - struct minstrel_sta_info *mi = inode->i_private; -@@ -100,7 +100,7 @@ minstrel_stats_open(struct inode *inode, - return 0; - } - --static ssize_t -+ssize_t - minstrel_stats_read(struct file *file, char __user *buf, size_t len, loff_t *ppos) - { - struct minstrel_debugfs_info *ms; -@@ -109,7 +109,7 @@ minstrel_stats_read(struct file *file, c - return simple_read_from_buffer(buf, len, ppos, ms->buf, ms->len); - } - --static int -+int - minstrel_stats_release(struct inode *inode, struct file *file) - { - kfree(file->private_data); diff --git a/package/mac80211/patches/560-minstrel_ht.patch b/package/mac80211/patches/560-minstrel_ht.patch deleted file mode 100644 index c51d541fbb..0000000000 --- a/package/mac80211/patches/560-minstrel_ht.patch +++ /dev/null @@ -1,1137 +0,0 @@ ---- a/net/mac80211/Makefile -+++ b/net/mac80211/Makefile -@@ -47,8 +47,8 @@ CFLAGS_driver-trace.o := -I$(src) - rc80211_pid-y := rc80211_pid_algo.o - rc80211_pid-$(CONFIG_MAC80211_DEBUGFS) += rc80211_pid_debugfs.o - --rc80211_minstrel-y := rc80211_minstrel.o --rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_debugfs.o -+rc80211_minstrel-y := rc80211_minstrel.o rc80211_minstrel_ht.o -+rc80211_minstrel-$(CONFIG_MAC80211_DEBUGFS) += rc80211_minstrel_debugfs.o rc80211_minstrel_ht_debugfs.o - - mac80211-$(CONFIG_MAC80211_RC_PID) += $(rc80211_pid-y) - mac80211-$(CONFIG_MAC80211_RC_MINSTREL) += $(rc80211_minstrel-y) ---- a/net/mac80211/main.c -+++ b/net/mac80211/main.c -@@ -714,6 +714,10 @@ static int __init ieee80211_init(void) - if (ret) - return ret; - -+ ret = rc80211_minstrel_ht_init(); -+ if (ret) -+ goto err_minstrel; -+ - ret = rc80211_pid_init(); - if (ret) - goto err_pid; -@@ -726,6 +730,8 @@ static int __init ieee80211_init(void) - err_netdev: - rc80211_pid_exit(); - err_pid: -+ rc80211_minstrel_ht_exit(); -+ err_minstrel: - rc80211_minstrel_exit(); - - return ret; -@@ -734,6 +740,7 @@ static int __init ieee80211_init(void) - static void __exit ieee80211_exit(void) - { - rc80211_pid_exit(); -+ rc80211_minstrel_ht_exit(); - rc80211_minstrel_exit(); - - /* ---- a/net/mac80211/rate.h -+++ b/net/mac80211/rate.h -@@ -137,6 +137,8 @@ static inline void rc80211_pid_exit(void - #ifdef CONFIG_MAC80211_RC_MINSTREL - extern int rc80211_minstrel_init(void); - extern void rc80211_minstrel_exit(void); -+extern int rc80211_minstrel_ht_init(void); -+extern void rc80211_minstrel_ht_exit(void); - #else - static inline int rc80211_minstrel_init(void) - { -@@ -145,6 +147,13 @@ static inline int rc80211_minstrel_init( - static inline void rc80211_minstrel_exit(void) - { - } -+static inline int rc80211_minstrel_ht_init(void) -+{ -+ return 0; -+} -+static inline void rc80211_minstrel_ht_exit(void) -+{ -+} - #endif - - ---- /dev/null -+++ b/net/mac80211/rc80211_minstrel_ht.c -@@ -0,0 +1,815 @@ -+/* -+ * Copyright (C) 2010 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 -+ * published by the Free Software Foundation. -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "rate.h" -+#include "rc80211_minstrel.h" -+#include "rc80211_minstrel_ht.h" -+ -+#define AVG_PKT_SIZE 1200 -+#define SAMPLE_COLUMNS 10 -+#define EWMA_LEVEL 75 -+ -+/* Number of bits for an average sized packet */ -+#define MCS_NBITS (AVG_PKT_SIZE << 3) -+ -+/* Number of symbols for a packet with (bps) bits per symbol */ -+#define MCS_NSYMS(bps) ((MCS_NBITS + (bps) - 1) / (bps)) -+ -+/* Transmission time for a packet containing (syms) symbols */ -+#define MCS_SYMBOL_TIME(sgi, syms) \ -+ (sgi ? \ -+ ((syms) * 18 + 4) / 5 : /* syms * 3.6 us */ \ -+ (syms) << 2 /* syms * 4 us */ \ -+ ) -+ -+/* Transmit duration for the raw data part of an average sized packet */ -+#define MCS_DURATION(streams, sgi, bps) MCS_SYMBOL_TIME(sgi, MCS_NSYMS((streams) * (bps))) -+ -+/* MCS rate information for an MCS group */ -+#define MCS_GROUP(_streams, _sgi, _ht40) { \ -+ .streams = _streams, \ -+ .flags = \ -+ (_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) | \ -+ (_ht40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0), \ -+ .duration = { \ -+ MCS_DURATION(_streams, _sgi, _ht40 ? 54 : 26), \ -+ MCS_DURATION(_streams, _sgi, _ht40 ? 108 : 52), \ -+ MCS_DURATION(_streams, _sgi, _ht40 ? 162 : 78), \ -+ MCS_DURATION(_streams, _sgi, _ht40 ? 216 : 104), \ -+ MCS_DURATION(_streams, _sgi, _ht40 ? 324 : 156), \ -+ MCS_DURATION(_streams, _sgi, _ht40 ? 432 : 208), \ -+ MCS_DURATION(_streams, _sgi, _ht40 ? 486 : 234), \ -+ MCS_DURATION(_streams, _sgi, _ht40 ? 540 : 260) \ -+ } \ -+} -+ -+/* -+ * To enable sufficiently targeted rate sampling, MCS rates are divided into -+ * groups, based on the number of streams and flags (HT40, SGI) that they -+ * use. -+ */ -+const struct mcs_group minstrel_mcs_groups[] = { -+ MCS_GROUP(1, 0, 0), -+ MCS_GROUP(2, 0, 0), -+#if MINSTREL_MAX_STREAMS >= 3 -+ MCS_GROUP(3, 0, 0), -+#endif -+ -+ MCS_GROUP(1, 1, 0), -+ MCS_GROUP(2, 1, 0), -+#if MINSTREL_MAX_STREAMS >= 3 -+ MCS_GROUP(3, 1, 0), -+#endif -+ -+ MCS_GROUP(1, 0, 1), -+ MCS_GROUP(2, 0, 1), -+#if MINSTREL_MAX_STREAMS >= 3 -+ MCS_GROUP(3, 0, 1), -+#endif -+ -+ MCS_GROUP(1, 1, 1), -+ MCS_GROUP(2, 1, 1), -+#if MINSTREL_MAX_STREAMS >= 3 -+ MCS_GROUP(3, 1, 1), -+#endif -+}; -+ -+static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES]; -+ -+/* -+ * Perform EWMA (Exponentially Weighted Moving Average) calculation -+ */ -+static int -+minstrel_ewma(int old, int new, int weight) -+{ -+ return (new * (100 - weight) + old * weight) / 100; -+} -+ -+/* -+ * Look up an MCS group index based on mac80211 rate information -+ */ -+static int -+minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate) -+{ -+ int streams = (rate->idx / MCS_GROUP_RATES) + 1; -+ u32 flags = IEEE80211_TX_RC_SHORT_GI | IEEE80211_TX_RC_40_MHZ_WIDTH; -+ int i; -+ -+ for (i = 0; i < ARRAY_SIZE(minstrel_mcs_groups); i++) { -+ if (minstrel_mcs_groups[i].streams != streams) -+ continue; -+ if (minstrel_mcs_groups[i].flags != (rate->flags & flags)) -+ continue; -+ -+ return i; -+ } -+ -+ WARN_ON(1); -+ return 0; -+} -+ -+static inline struct minstrel_rate_stats * -+minstrel_get_ratestats(struct minstrel_ht_sta *mi, int index) -+{ -+ return &mi->groups[index / MCS_GROUP_RATES].rates[index % MCS_GROUP_RATES]; -+} -+ -+ -+/* -+ * Recalculate success probabilities and counters for a rate using EWMA -+ */ -+static void -+minstrel_calc_rate_ewma(struct minstrel_priv *mp, struct minstrel_rate_stats *mr) -+{ -+ if (unlikely(mr->attempts > 0)) { -+ mr->sample_skipped = 0; -+ mr->cur_prob = MINSTREL_FRAC(mr->success, mr->attempts); -+ if (!mr->att_hist) -+ mr->probability = mr->cur_prob; -+ else -+ mr->probability = minstrel_ewma(mr->probability, -+ mr->cur_prob, EWMA_LEVEL); -+ mr->att_hist += mr->attempts; -+ mr->succ_hist += mr->success; -+ } else { -+ mr->sample_skipped++; -+ } -+ mr->last_success = mr->success; -+ mr->last_attempts = mr->attempts; -+ mr->success = 0; -+ mr->attempts = 0; -+} -+ -+/* -+ * Calculate throughput based on the average A-MPDU length, taking into account -+ * the expected number of retransmissions and their expected length -+ */ -+static void -+minstrel_ht_calc_tp(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, -+ int group, int rate) -+{ -+ struct minstrel_rate_stats *mr; -+ unsigned int usecs; -+ -+ mr = &mi->groups[group].rates[rate]; -+ -+ if (mr->probability < MINSTREL_FRAC(1, 10)) { -+ mr->cur_tp = 0; -+ return; -+ } -+ -+ 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); -+} -+ -+/* -+ * Update rate statistics and select new primary rates -+ * -+ * Rules for rate selection: -+ * - max_prob_rate must use only one stream, as a tradeoff between delivery -+ * probability and throughput during strong fluctuations -+ * - as long as the max prob rate has a probability of more than 3/4, pick -+ * higher throughput rates, even if the probablity is a bit lower -+ */ -+static void -+minstrel_ht_update_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) -+{ -+ struct minstrel_mcs_group_data *mg; -+ struct minstrel_rate_stats *mr; -+ int cur_prob, cur_prob_tp, cur_tp, cur_tp2; -+ int group, i, index; -+ -+ if (mi->ampdu_packets > 0) { -+ mi->avg_ampdu_len = minstrel_ewma(mi->avg_ampdu_len, -+ MINSTREL_FRAC(mi->ampdu_len, mi->ampdu_packets), EWMA_LEVEL); -+ mi->ampdu_len = 0; -+ mi->ampdu_packets = 0; -+ } -+ -+ mi->sample_slow = 0; -+ mi->sample_count = 0; -+ mi->max_tp_rate = 0; -+ mi->max_tp_rate2 = 0; -+ mi->max_prob_rate = 0; -+ -+ for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { -+ cur_prob = 0; -+ cur_prob_tp = 0; -+ cur_tp = 0; -+ cur_tp2 = 0; -+ -+ mg = &mi->groups[group]; -+ if (!mg->supported) -+ continue; -+ -+ mg->max_tp_rate = 0; -+ mg->max_tp_rate2 = 0; -+ mg->max_prob_rate = 0; -+ mi->sample_count++; -+ -+ for (i = 0; i < MCS_GROUP_RATES; i++) { -+ if (!(mg->supported & BIT(i))) -+ continue; -+ -+ mr = &mg->rates[i]; -+ mr->retry_updated = false; -+ index = MCS_GROUP_RATES * group + i; -+ minstrel_calc_rate_ewma(mp, mr); -+ minstrel_ht_calc_tp(mp, mi, group, i); -+ -+ 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; -+ cur_prob = mr->probability; -+ } -+ -+ if (mr->cur_tp > cur_tp) { -+ swap(index, mg->max_tp_rate); -+ cur_tp = mr->cur_tp; -+ mr = minstrel_get_ratestats(mi, index); -+ } -+ -+ if (index >= mg->max_tp_rate) -+ continue; -+ -+ if (mr->cur_tp > cur_tp2) { -+ mg->max_tp_rate2 = index; -+ cur_tp2 = mr->cur_tp; -+ } -+ } -+ } -+ -+ /* try to sample up to half of the availble rates during each interval */ -+ mi->sample_count *= 4; -+ -+ cur_prob = 0; -+ cur_prob_tp = 0; -+ cur_tp = 0; -+ cur_tp2 = 0; -+ for (group = 0; group < ARRAY_SIZE(minstrel_mcs_groups); group++) { -+ mg = &mi->groups[group]; -+ if (!mg->supported) -+ continue; -+ -+ mr = minstrel_get_ratestats(mi, mg->max_prob_rate); -+ if (cur_prob_tp < mr->cur_tp && -+ minstrel_mcs_groups[group].streams == 1) { -+ mi->max_prob_rate = mg->max_prob_rate; -+ cur_prob = mr->cur_prob; -+ } -+ -+ mr = minstrel_get_ratestats(mi, mg->max_tp_rate); -+ if (cur_tp < mr->cur_tp) { -+ mi->max_tp_rate = mg->max_tp_rate; -+ cur_tp = mr->cur_tp; -+ } -+ -+ mr = minstrel_get_ratestats(mi, mg->max_tp_rate2); -+ if (cur_tp2 < mr->cur_tp) { -+ mi->max_tp_rate2 = mg->max_tp_rate2; -+ cur_tp2 = mr->cur_tp; -+ } -+ } -+ -+ mi->stats_update = jiffies; -+} -+ -+static bool -+minstrel_ht_txstat_valid(struct ieee80211_tx_rate *rate) -+{ -+ if (!rate->count) -+ return false; -+ -+ if (rate->idx < 0) -+ return false; -+ -+ return !!(rate->flags & IEEE80211_TX_RC_MCS); -+} -+ -+static void -+minstrel_next_sample_idx(struct minstrel_ht_sta *mi) -+{ -+ struct minstrel_mcs_group_data *mg; -+ -+ for (;;) { -+ mi->sample_group++; -+ mi->sample_group %= ARRAY_SIZE(minstrel_mcs_groups); -+ mg = &mi->groups[mi->sample_group]; -+ -+ if (!mg->supported) -+ continue; -+ -+ if (++mg->index > MCS_GROUP_RATES) { -+ mg->index = 0; -+ if (++mg->column > ARRAY_SIZE(sample_table)) -+ mg->column = 0; -+ } -+ break; -+ } -+} -+ -+static void -+minstrel_downgrade_rate(struct minstrel_ht_sta *mi, int *idx, bool primary) -+{ -+ int group, orig_group; -+ -+ orig_group = group = *idx / MCS_GROUP_RATES; -+ while (group > 0) { -+ group--; -+ -+ if (!mi->groups[group].supported) -+ continue; -+ -+ if (minstrel_mcs_groups[group].streams > -+ minstrel_mcs_groups[orig_group].streams) -+ continue; -+ -+ if (primary) -+ *idx = mi->groups[group].max_tp_rate; -+ else -+ *idx = mi->groups[group].max_tp_rate2; -+ break; -+ } -+} -+ -+static void -+minstrel_aggr_check(struct minstrel_priv *mp, struct ieee80211_sta *pubsta, struct sk_buff *skb) -+{ -+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; -+ struct sta_info *sta = container_of(pubsta, struct sta_info, sta); -+ u16 tid; -+ -+ if (unlikely(!ieee80211_is_data_qos(hdr->frame_control))) -+ return; -+ -+ if (unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE))) -+ return; -+ -+ tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; -+ if (likely(sta->ampdu_mlme.tid_state_tx[tid] != HT_AGG_STATE_IDLE)) -+ return; -+ -+ ieee80211_start_tx_ba_session(pubsta, tid); -+} -+ -+static void -+minstrel_ht_tx_status(void *priv, struct ieee80211_supported_band *sband, -+ struct ieee80211_sta *sta, void *priv_sta, -+ struct sk_buff *skb) -+{ -+ struct minstrel_ht_sta_priv *msp = priv_sta; -+ struct minstrel_ht_sta *mi = &msp->ht; -+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); -+ struct ieee80211_tx_rate *ar = info->status.rates; -+ struct minstrel_rate_stats *rate, *rate2; -+ struct minstrel_priv *mp = priv; -+ bool last = false; -+ int group; -+ int i = 0; -+ -+ if (!msp->is_ht) -+ return mac80211_minstrel.tx_status(priv, sband, sta, &msp->legacy, skb); -+ -+ /* This packet was aggregated but doesn't carry status info */ -+ if ((info->flags & IEEE80211_TX_CTL_AMPDU) && -+ !(info->flags & IEEE80211_TX_STAT_AMPDU)) -+ return; -+ -+ if (!info->status.ampdu_len) { -+ info->status.ampdu_ack_len = 1; -+ info->status.ampdu_len = 1; -+ } -+ -+ mi->ampdu_packets++; -+ mi->ampdu_len += info->status.ampdu_len; -+ -+ if (!mi->sample_wait && !mi->sample_tries && mi->sample_count > 0) { -+ mi->sample_wait = 4 + MINSTREL_TRUNC(mi->avg_ampdu_len); -+ mi->sample_tries = 3; -+ mi->sample_count--; -+ } -+ -+ if (info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) { -+ mi->sample_packets += info->status.ampdu_len; -+ minstrel_next_sample_idx(mi); -+ } -+ -+ for (i = 0; !last; i++) { -+ last = (i == IEEE80211_TX_MAX_RATES - 1) || -+ !minstrel_ht_txstat_valid(&ar[i + 1]); -+ -+ if (!minstrel_ht_txstat_valid(&ar[i])) -+ break; -+ -+ group = minstrel_ht_get_group_idx(&ar[i]); -+ rate = &mi->groups[group].rates[ar[i].idx % 8]; -+ -+ if (last && (info->flags & IEEE80211_TX_STAT_ACK)) -+ rate->success += info->status.ampdu_ack_len; -+ -+ rate->attempts += ar[i].count * info->status.ampdu_len; -+ } -+ -+ /* -+ * check for sudden death of spatial multiplexing, -+ * downgrade to a lower number of streams if necessary. -+ */ -+ rate = minstrel_get_ratestats(mi, mi->max_tp_rate); -+ if (rate->attempts > 30 && -+ MINSTREL_FRAC(rate->success, rate->attempts) < -+ MINSTREL_FRAC(20, 100)) -+ minstrel_downgrade_rate(mi, &mi->max_tp_rate, true); -+ -+ rate2 = minstrel_get_ratestats(mi, mi->max_tp_rate2); -+ if (rate->attempts > 30 && -+ MINSTREL_FRAC(rate->success, rate->attempts) < -+ MINSTREL_FRAC(20, 100)) -+ minstrel_downgrade_rate(mi, &mi->max_tp_rate2, false); -+ -+ if (time_after(jiffies, mi->stats_update + (mp->update_interval / 2 * HZ) / 1000)) { -+ minstrel_ht_update_stats(mp, mi); -+ minstrel_aggr_check(mp, sta, skb); -+ } -+} -+ -+static void -+minstrel_calc_retransmit(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, -+ int index) -+{ -+ struct minstrel_rate_stats *mr; -+ const struct mcs_group *group; -+ unsigned int tx_time, tx_time_rtscts, tx_time_data; -+ unsigned int cw = mp->cw_min; -+ unsigned int t_slot = 9; /* FIXME */ -+ unsigned int ampdu_len = MINSTREL_TRUNC(mi->avg_ampdu_len); -+ -+ mr = minstrel_get_ratestats(mi, index); -+ if (mr->probability < MINSTREL_FRAC(1, 10)) { -+ mr->retry_count = 1; -+ mr->retry_count_rtscts = 1; -+ return; -+ } -+ -+ mr->retry_count = 2; -+ mr->retry_count_rtscts = 2; -+ mr->retry_updated = true; -+ -+ group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; -+ tx_time_data = group->duration[index % MCS_GROUP_RATES] * ampdu_len; -+ tx_time = 2 * (t_slot + mi->overhead + tx_time_data); -+ tx_time_rtscts = 2 * (t_slot + mi->overhead_rtscts + tx_time_data); -+ do { -+ cw = (cw << 1) | 1; -+ cw = min(cw, mp->cw_max); -+ tx_time += cw + t_slot + mi->overhead; -+ tx_time_rtscts += cw + t_slot + mi->overhead_rtscts; -+ if (tx_time_rtscts < mp->segment_size) -+ mr->retry_count_rtscts++; -+ } while ((tx_time < mp->segment_size) && -+ (++mr->retry_count < mp->max_retry)); -+} -+ -+ -+static void -+minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi, -+ struct ieee80211_tx_rate *rate, int index, -+ struct ieee80211_tx_rate_control *txrc, -+ bool sample, bool rtscts) -+{ -+ const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; -+ struct minstrel_rate_stats *mr; -+ -+ mr = minstrel_get_ratestats(mi, index); -+ if (!mr->retry_updated) -+ minstrel_calc_retransmit(mp, mi, index); -+ -+ if (mr->probability < MINSTREL_FRAC(20, 100)) -+ rate->count = 2; -+ else if (rtscts) -+ rate->count = mr->retry_count_rtscts; -+ else -+ rate->count = mr->retry_count; -+ -+ rate->flags = IEEE80211_TX_RC_MCS | group->flags; -+ if (txrc->short_preamble) -+ rate->flags |= IEEE80211_TX_RC_USE_SHORT_PREAMBLE; -+ if (txrc->rts || rtscts) -+ rate->flags |= IEEE80211_TX_RC_USE_RTS_CTS; -+ rate->idx = index % MCS_GROUP_RATES + (group->streams - 1) * MCS_GROUP_RATES; -+} -+ -+static inline int -+minstrel_get_duration(int index) -+{ -+ const struct mcs_group *group = &minstrel_mcs_groups[index / MCS_GROUP_RATES]; -+ return group->duration[index % MCS_GROUP_RATES]; -+} -+ -+static int -+minstrel_get_sample_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi) -+{ -+ struct minstrel_rate_stats *mr; -+ struct minstrel_mcs_group_data *mg; -+ int sample_idx = 0; -+ -+ if (mi->sample_wait > 0) { -+ mi->sample_wait--; -+ return -1; -+ } -+ -+ if (!mi->sample_tries) -+ return -1; -+ -+ mi->sample_tries--; -+ mg = &mi->groups[mi->sample_group]; -+ sample_idx = sample_table[mg->column][mg->index]; -+ mr = &mg->rates[sample_idx]; -+ sample_idx += mi->sample_group * MCS_GROUP_RATES; -+ -+ /* -+ * When not using MRR, do not sample if the probability is already -+ * higher than 95% to avoid wasting airtime -+ */ -+ if (!mp->has_mrr && (mr->probability > MINSTREL_FRAC(95, 100))) -+ goto next; -+ -+ /* -+ * Make sure that lower rates get sampled only occasionally, -+ * if the link is working perfectly. -+ */ -+ if (minstrel_get_duration(sample_idx) > -+ minstrel_get_duration(mi->max_tp_rate)) { -+ if (mr->sample_skipped < 10) -+ goto next; -+ -+ if (mi->sample_slow++ > 2) -+ goto next; -+ } -+ -+ return sample_idx; -+ -+next: -+ minstrel_next_sample_idx(mi); -+ return -1; -+} -+ -+static void -+minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta, -+ struct ieee80211_tx_rate_control *txrc) -+{ -+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(txrc->skb); -+ struct ieee80211_tx_rate *ar = info->status.rates; -+ struct minstrel_ht_sta_priv *msp = priv_sta; -+ struct minstrel_ht_sta *mi = &msp->ht; -+ struct minstrel_priv *mp = priv; -+ int sample_idx; -+ -+ if (rate_control_send_low(sta, priv_sta, txrc)) -+ return; -+ -+ if (!msp->is_ht) -+ return mac80211_minstrel.get_rate(priv, sta, &msp->legacy, txrc); -+ -+ sample_idx = minstrel_get_sample_rate(mp, mi); -+ if (sample_idx >= 0) { -+ minstrel_ht_set_rate(mp, mi, &ar[0], sample_idx, -+ txrc, true, false); -+ minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_tp_rate, -+ txrc, false, true); -+ info->flags |= IEEE80211_TX_CTL_RATE_CTRL_PROBE; -+ } else { -+ minstrel_ht_set_rate(mp, mi, &ar[0], mi->max_tp_rate, -+ txrc, false, false); -+ minstrel_ht_set_rate(mp, mi, &ar[1], mi->max_tp_rate2, -+ txrc, false, true); -+ } -+ minstrel_ht_set_rate(mp, mi, &ar[2], mi->max_prob_rate, txrc, false, true); -+ -+ ar[3].count = 0; -+ ar[3].idx = -1; -+ -+ mi->total_packets++; -+ -+ /* wraparound */ -+ if (mi->total_packets == ~0) { -+ mi->total_packets = 0; -+ mi->sample_packets = 0; -+ } -+} -+ -+static void -+minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband, -+ struct ieee80211_sta *sta, void *priv_sta, -+ enum nl80211_channel_type oper_chan_type) -+{ -+ struct minstrel_priv *mp = priv; -+ struct minstrel_ht_sta_priv *msp = priv_sta; -+ struct minstrel_ht_sta *mi = &msp->ht; -+ struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs; -+ struct ieee80211_local *local = hw_to_local(mp->hw); -+ u16 sta_cap = sta->ht_cap.cap; -+ int ack_dur; -+ int i; -+ -+ /* fall back to the old minstrel for legacy stations */ -+ if (sta && !sta->ht_cap.ht_supported) { -+ msp->is_ht = false; -+ memset(&msp->legacy, 0, sizeof(msp->legacy)); -+ msp->legacy.r = msp->ratelist; -+ msp->legacy.sample_table = msp->sample_table; -+ return mac80211_minstrel.rate_init(priv, sband, sta, &msp->legacy); -+ } -+ -+ BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != -+ MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS); -+ -+ msp->is_ht = true; -+ memset(mi, 0, sizeof(*mi)); -+ mi->stats_update = jiffies; -+ -+ ack_dur = ieee80211_frame_duration(local, 10, 60, 1, 1); -+ mi->overhead = ieee80211_frame_duration(local, 0, 60, 1, 1) + ack_dur; -+ mi->overhead_rtscts = mi->overhead + 2 * ack_dur; -+ -+ mi->avg_ampdu_len = MINSTREL_FRAC(1, 1); -+ -+ /* When using MRR, sample more on the first attempt, without delay */ -+ if (mp->has_mrr) { -+ mi->sample_count = 16; -+ mi->sample_wait = 0; -+ } else { -+ mi->sample_count = 8; -+ mi->sample_wait = 8; -+ } -+ mi->sample_tries = 4; -+ -+ if (oper_chan_type != NL80211_CHAN_HT40MINUS && -+ oper_chan_type != NL80211_CHAN_HT40PLUS) -+ sta_cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; -+ -+ for (i = 0; i < ARRAY_SIZE(mi->groups); i++) { -+ u16 req = 0; -+ -+ mi->groups[i].supported = 0; -+ 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; -+ else -+ req |= IEEE80211_HT_CAP_SGI_20; -+ } -+ -+ if (minstrel_mcs_groups[i].flags & IEEE80211_TX_RC_40_MHZ_WIDTH) -+ req |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; -+ -+ if ((sta_cap & req) != req) -+ continue; -+ -+ mi->groups[i].supported = -+ mcs->rx_mask[minstrel_mcs_groups[i].streams - 1]; -+ } -+} -+ -+static void -+minstrel_ht_rate_init(void *priv, struct ieee80211_supported_band *sband, -+ struct ieee80211_sta *sta, void *priv_sta) -+{ -+ struct minstrel_priv *mp = priv; -+ -+ minstrel_ht_update_caps(priv, sband, sta, priv_sta, mp->hw->conf.channel_type); -+} -+ -+static void -+minstrel_ht_rate_update(void *priv, struct ieee80211_supported_band *sband, -+ struct ieee80211_sta *sta, void *priv_sta, -+ u32 changed, enum nl80211_channel_type oper_chan_type) -+{ -+ minstrel_ht_update_caps(priv, sband, sta, priv_sta, oper_chan_type); -+} -+ -+static void * -+minstrel_ht_alloc_sta(void *priv, struct ieee80211_sta *sta, gfp_t gfp) -+{ -+ struct ieee80211_supported_band *sband; -+ struct minstrel_ht_sta_priv *msp; -+ struct minstrel_priv *mp = priv; -+ struct ieee80211_hw *hw = mp->hw; -+ int max_rates = 0; -+ int i; -+ -+ for (i = 0; i < IEEE80211_NUM_BANDS; i++) { -+ sband = hw->wiphy->bands[i]; -+ if (sband && sband->n_bitrates > max_rates) -+ max_rates = sband->n_bitrates; -+ } -+ -+ msp = kzalloc(sizeof(struct minstrel_ht_sta), gfp); -+ if (!msp) -+ return NULL; -+ -+ msp->ratelist = kzalloc(sizeof(struct minstrel_rate) * max_rates, gfp); -+ if (!msp->ratelist) -+ goto error; -+ -+ msp->sample_table = kmalloc(SAMPLE_COLUMNS * max_rates, gfp); -+ if (!msp->sample_table) -+ goto error1; -+ -+ return msp; -+ -+error1: -+ kfree(msp->sample_table); -+error: -+ kfree(msp); -+ return NULL; -+} -+ -+static void -+minstrel_ht_free_sta(void *priv, struct ieee80211_sta *sta, void *priv_sta) -+{ -+ struct minstrel_ht_sta_priv *msp = priv_sta; -+ -+ kfree(msp->sample_table); -+ kfree(msp->ratelist); -+ kfree(msp); -+} -+ -+static void * -+minstrel_ht_alloc(struct ieee80211_hw *hw, struct dentry *debugfsdir) -+{ -+ return mac80211_minstrel.alloc(hw, debugfsdir); -+} -+ -+static void -+minstrel_ht_free(void *priv) -+{ -+ mac80211_minstrel.free(priv); -+} -+ -+static struct rate_control_ops mac80211_minstrel_ht = { -+ .name = "minstrel_ht", -+ .tx_status = minstrel_ht_tx_status, -+ .get_rate = minstrel_ht_get_rate, -+ .rate_init = minstrel_ht_rate_init, -+ .rate_update = minstrel_ht_rate_update, -+ .alloc_sta = minstrel_ht_alloc_sta, -+ .free_sta = minstrel_ht_free_sta, -+ .alloc = minstrel_ht_alloc, -+ .free = minstrel_ht_free, -+#ifdef CONFIG_MAC80211_DEBUGFS -+ .add_sta_debugfs = minstrel_ht_add_sta_debugfs, -+ .remove_sta_debugfs = minstrel_ht_remove_sta_debugfs, -+#endif -+}; -+ -+ -+static void -+init_sample_table(void) -+{ -+ int col, i, new_idx; -+ u8 rnd[MCS_GROUP_RATES]; -+ -+ memset(sample_table, 0xff, sizeof(sample_table)); -+ for (col = 0; col < SAMPLE_COLUMNS; col++) { -+ for (i = 0; i < MCS_GROUP_RATES; i++) { -+ get_random_bytes(rnd, sizeof(rnd)); -+ new_idx = (i + rnd[i]) % MCS_GROUP_RATES; -+ -+ while (sample_table[col][new_idx] != 0xff) -+ new_idx = (new_idx + 1) % MCS_GROUP_RATES; -+ -+ sample_table[col][new_idx] = i; -+ } -+ } -+} -+ -+int __init -+rc80211_minstrel_ht_init(void) -+{ -+ init_sample_table(); -+ return ieee80211_rate_control_register(&mac80211_minstrel_ht); -+} -+ -+void -+rc80211_minstrel_ht_exit(void) -+{ -+ ieee80211_rate_control_unregister(&mac80211_minstrel_ht); -+} ---- /dev/null -+++ b/net/mac80211/rc80211_minstrel_ht.h -@@ -0,0 +1,125 @@ -+/* -+ * Copyright (C) 2010 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 -+ * published by the Free Software Foundation. -+ */ -+ -+#ifndef __RC_MINSTREL_HT_H -+#define __RC_MINSTREL_HT_H -+ -+/* -+ * maximum number of spatial streams to make use of -+ * set this value to 3 once we have drivers that support it -+ */ -+#define MINSTREL_MAX_STREAMS 2 -+#define MINSTREL_STREAM_GROUPS 4 -+ -+/* scaled fraction values */ -+#define MINSTREL_SCALE 16 -+#define MINSTREL_FRAC(val, div) (((val) << MINSTREL_SCALE) / div) -+#define MINSTREL_TRUNC(val) ((val) >> MINSTREL_SCALE) -+ -+#define MCS_GROUP_RATES 8 -+ -+struct mcs_group { -+ u32 flags; -+ unsigned int streams; -+ unsigned int duration[MCS_GROUP_RATES]; -+}; -+ -+struct minstrel_rate_stats { -+ /* current / last sampling period attempts/success counters */ -+ unsigned int attempts, last_attempts; -+ unsigned int success, last_success; -+ -+ /* total attempts/success counters */ -+ u64 att_hist, succ_hist; -+ -+ /* current throughput */ -+ unsigned int cur_tp; -+ -+ /* packet delivery probabilities */ -+ unsigned int cur_prob, probability; -+ -+ /* maximum retry counts */ -+ unsigned int retry_count; -+ unsigned int retry_count_rtscts; -+ -+ bool retry_updated; -+ u8 sample_skipped; -+}; -+ -+struct minstrel_mcs_group_data { -+ u8 index; -+ u8 column; -+ -+ /* bitfield of supported MCS rates of this group */ -+ u8 supported; -+ -+ /* selected primary rates */ -+ unsigned int max_tp_rate; -+ unsigned int max_tp_rate2; -+ unsigned int max_prob_rate; -+ -+ /* MCS rate statistics */ -+ struct minstrel_rate_stats rates[MCS_GROUP_RATES]; -+}; -+ -+struct minstrel_ht_sta { -+ /* ampdu length (average, per sampling interval) */ -+ unsigned int ampdu_len; -+ unsigned int ampdu_packets; -+ -+ /* ampdu length (EWMA) */ -+ unsigned int avg_ampdu_len; -+ -+ /* best throughput rate */ -+ unsigned int max_tp_rate; -+ -+ /* second best throughput rate */ -+ unsigned int max_tp_rate2; -+ -+ /* best probability rate */ -+ unsigned int max_prob_rate; -+ -+ /* time of last status update */ -+ unsigned long stats_update; -+ -+ /* overhead time in usec for each frame */ -+ unsigned int overhead; -+ unsigned int overhead_rtscts; -+ -+ unsigned int total_packets; -+ unsigned int sample_packets; -+ -+ u8 sample_wait; -+ u8 sample_tries; -+ u8 sample_count; -+ u8 sample_slow; -+ -+ /* current MCS group to be sampled */ -+ u8 sample_group; -+ -+ /* MCS rate group info and statistics */ -+ struct minstrel_mcs_group_data groups[MINSTREL_MAX_STREAMS * MINSTREL_STREAM_GROUPS]; -+}; -+ -+struct minstrel_ht_sta_priv { -+ union { -+ struct minstrel_ht_sta ht; -+ struct minstrel_sta_info legacy; -+ }; -+#ifdef CONFIG_MAC80211_DEBUGFS -+ struct dentry *dbg_stats; -+#endif -+ void *ratelist; -+ void *sample_table; -+ bool is_ht; -+}; -+ -+void minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir); -+void minstrel_ht_remove_sta_debugfs(void *priv, void *priv_sta); -+ -+#endif ---- /dev/null -+++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c -@@ -0,0 +1,120 @@ -+/* -+ * Copyright (C) 2010 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 -+ * published by the Free Software Foundation. -+ */ -+#include -+#include -+#include -+#include -+#include -+#include -+#include "rc80211_minstrel.h" -+#include "rc80211_minstrel_ht.h" -+ -+extern const struct mcs_group minstrel_mcs_groups[]; -+ -+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; -+ char *p; -+ int ret; -+ -+ if (!msp->is_ht) { -+ inode->i_private = &msp->legacy; -+ ret = minstrel_stats_open(inode, file); -+ inode->i_private = msp; -+ return ret; -+ } -+ -+ ms = kmalloc(sizeof(*ms) + 8192, GFP_KERNEL); -+ if (!ms) -+ return -ENOMEM; -+ -+ 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; -+ -+ if (!(mi->groups[i].supported & BIT(j))) -+ continue; -+ -+ 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), -+ mi->sample_packets); -+ p += sprintf(p, "Average A-MPDU length: %d.%d\n", -+ MINSTREL_TRUNC(mi->avg_ampdu_len), -+ MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10); -+ ms->len = p - ms->buf; -+ -+ return 0; -+} -+ -+static const struct file_operations minstrel_ht_stat_fops = { -+ .owner = THIS_MODULE, -+ .open = minstrel_ht_stats_open, -+ .read = minstrel_stats_read, -+ .release = minstrel_stats_release, -+}; -+ -+void -+minstrel_ht_add_sta_debugfs(void *priv, void *priv_sta, struct dentry *dir) -+{ -+ struct minstrel_ht_sta_priv *msp = priv_sta; -+ -+ msp->dbg_stats = debugfs_create_file("rc_stats", S_IRUGO, dir, msp, -+ &minstrel_ht_stat_fops); -+} -+ -+void -+minstrel_ht_remove_sta_debugfs(void *priv, void *priv_sta) -+{ -+ struct minstrel_ht_sta_priv *msp = priv_sta; -+ -+ debugfs_remove(msp->dbg_stats); -+} diff --git a/package/mac80211/patches/570-ath9k_use_minstrel.patch b/package/mac80211/patches/570-ath9k_use_minstrel.patch deleted file mode 100644 index 0983abf335..0000000000 --- a/package/mac80211/patches/570-ath9k_use_minstrel.patch +++ /dev/null @@ -1,14 +0,0 @@ ---- a/drivers/net/wireless/ath/ath9k/init.c -+++ b/drivers/net/wireless/ath/ath9k/init.c -@@ -655,7 +655,11 @@ void ath9k_set_hw_capab(struct ath_softc - hw->sta_data_size = sizeof(struct ath_node); - hw->vif_data_size = sizeof(struct ath_vif); - -+#ifdef ATH9K_USE_MINSTREL -+ hw->rate_control_algorithm = "minstrel_ht"; -+#else - hw->rate_control_algorithm = "ath9k_rate_control"; -+#endif - - if (test_bit(ATH9K_MODE_11G, sc->sc_ah->caps.wireless_modes)) - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = diff --git a/package/mac80211/patches/580-tx_status_optimization.patch b/package/mac80211/patches/580-tx_status_optimization.patch deleted file mode 100644 index 34eeff2f1f..0000000000 --- a/package/mac80211/patches/580-tx_status_optimization.patch +++ /dev/null @@ -1,48 +0,0 @@ ---- a/net/mac80211/status.c -+++ b/net/mac80211/status.c -@@ -171,7 +171,7 @@ void ieee80211_tx_status(struct ieee8021 - struct net_device *prev_dev = NULL; - struct sta_info *sta, *tmp; - int retry_count = -1, i; -- bool injected; -+ bool send_to_cooked; - - for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) { - /* the HW cannot have attempted that rate */ -@@ -296,11 +296,15 @@ void ieee80211_tx_status(struct ieee8021 - /* this was a transmitted frame, but now we want to reuse it */ - skb_orphan(skb); - -+ /* Need to make a copy before skb->cb gets cleared */ -+ send_to_cooked = !!(info->flags & IEEE80211_TX_CTL_INJECTED) || -+ (type != IEEE80211_FTYPE_DATA); -+ - /* - * This is a bit racy but we can avoid a lot of work - * with this test... - */ -- if (!local->monitors && !local->cooked_mntrs) { -+ if (!local->monitors && (!send_to_cooked || !local->cooked_mntrs)) { - dev_kfree_skb(skb); - return; - } -@@ -345,9 +349,6 @@ void ieee80211_tx_status(struct ieee8021 - /* for now report the total retry_count */ - rthdr->data_retries = retry_count; - -- /* Need to make a copy before skb->cb gets cleared */ -- injected = !!(info->flags & IEEE80211_TX_CTL_INJECTED); -- - /* XXX: is this sufficient for BPF? */ - skb_set_mac_header(skb, 0); - skb->ip_summed = CHECKSUM_UNNECESSARY; -@@ -362,8 +363,7 @@ void ieee80211_tx_status(struct ieee8021 - continue; - - if ((sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) && -- !injected && -- (type == IEEE80211_FTYPE_DATA)) -+ !send_to_cooked) - continue; - - if (prev_dev) { diff --git a/package/mac80211/patches/590-ath9k_rekey_crash_fix.patch b/package/mac80211/patches/590-ath9k_rekey_crash_fix.patch deleted file mode 100644 index 484c8c8995..0000000000 --- a/package/mac80211/patches/590-ath9k_rekey_crash_fix.patch +++ /dev/null @@ -1,37 +0,0 @@ ---- a/drivers/net/wireless/ath/ath9k/xmit.c -+++ b/drivers/net/wireless/ath/ath9k/xmit.c -@@ -1353,25 +1353,6 @@ static enum ath9k_pkt_type get_hw_packet - return htype; - } - --static bool is_pae(struct sk_buff *skb) --{ -- struct ieee80211_hdr *hdr; -- __le16 fc; -- -- hdr = (struct ieee80211_hdr *)skb->data; -- fc = hdr->frame_control; -- -- if (ieee80211_is_data(fc)) { -- if (ieee80211_is_nullfunc(fc) || -- /* Port Access Entity (IEEE 802.1X) */ -- (skb->protocol == cpu_to_be16(ETH_P_PAE))) { -- return true; -- } -- } -- -- return false; --} -- - static int get_hw_crypto_keytype(struct sk_buff *skb) - { - struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(skb); -@@ -1696,7 +1677,7 @@ static void ath_tx_start_dma(struct ath_ - goto tx_done; - } - -- if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && !is_pae(skb)) { -+ if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { - /* - * Try aggregation if it's a unicast data frame - * and the destination is HT capable.