diff mbox

[v6] mac80211: minstrel_ht: add basic support for VHT rates <= 3SS@80MHz

Message ID 1413880718-31273-1-git-send-email-karl.beldan@gmail.com (mailing list archive)
State Not Applicable, archived
Headers show

Commit Message

Karl Beldan Oct. 21, 2014, 8:38 a.m. UTC
From: Karl Beldan <karl.beldan@rivierawaves.com>

When the new CONFIG_MAC80211_RC_MINSTREL_VHT is not set (default 'N'),
there is no behavioral change including in sampling and MCS_GROUP_RATES
remains 8.
Otherwise MCS_GROUP_RATES is 10, and a module parameter *vht_only*
(default 'true'), restricts the rates selection to VHT when vht is
supported.

Regarding the debugfs stats buffer:
It is explicitly increased from 8k to 32k to fit every rates incl. when
both ht and vht rates are enabled, as for the format, before:
type           rate     tpt eprob *prob ret  *ok(*cum)        ok(      cum)
HT20/LGI ABCDP MCS0     0.0   0.0   0.0   1    0(   0)         0(        0)
after:
 type           rate      tpt eprob *prob ret  *ok(*cum)        ok(      cum)
 HT20/LGI ABCDP MCS0      0.0   0.0   0.0   1    0(   0)         0(        0)
VHT40/LGI       MCS5/2    0.0   0.0   0.0   0    0(   0)         0(        0)

Signed-off-by: Karl Beldan <karl.beldan@rivierawaves.com>
Cc: Felix Fietkau <nbd@openwrt.org>
---

v2:
- typo in Kconfig
- adjust room in debugfs rc_stats buffer
- add a module param to mix ht rates with vht ones
- default to 3SS instead of 2SS (with "mac80211: minstrel_ht: macros
  adjustments for future VHT_GROUPs")
- use common MINSTREL_MAX_STREAMS both for VHT and HT so that we can get
  rid of the 'MINSTREL_MAX_STREAMS >= 3' checks for minstrel_mcs_groups
v3:
- put module param *vht_only* under CONFIG_MAC80211_RC_MINSTREL_VHT
v4:
- rebase on v2 of "mac80211: minstrels: fix buffer overflow in HT debugfs rc_stats"
v5:
- fix some checkpatch complaints this, leaves 2 false positives 
  "ERROR: Macros with complex values should be enclosed in parenthesis"
  in array initialization
v6:
- s/u16/__le16/ addressing Johannes' sparse pass

 net/mac80211/Kconfig                       |   7 +
 net/mac80211/rc80211_minstrel_ht.c         | 216 +++++++++++++++++++++++++++--
 net/mac80211/rc80211_minstrel_ht.h         |  17 ++-
 net/mac80211/rc80211_minstrel_ht_debugfs.c |  23 +--
 4 files changed, 241 insertions(+), 22 deletions(-)

Comments

Karl Beldan Oct. 24, 2014, 11:48 a.m. UTC | #1
Hi,

On Tue, Oct 21, 2014 at 10:38:38AM +0200, Karl Beldan wrote:
> From: Karl Beldan <karl.beldan@rivierawaves.com>
> 
> @@ -1026,13 +1184,47 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
>  		    sta->bandwidth < IEEE80211_STA_RX_BW_40)
>  			continue;
>  
> +		nss = minstrel_mcs_groups[i].streams;
> +
>  		/* Mark MCS > 7 as unsupported if STA is in static SMPS mode */
> -		if (sta->smps_mode == IEEE80211_SMPS_STATIC &&
> -		    minstrel_mcs_groups[i].streams > 1)
> +		if (sta->smps_mode == IEEE80211_SMPS_STATIC && nss > 1)
> +			continue;
> +
> +		/* HT rate */
> +		if (gflags & IEEE80211_TX_RC_MCS) {
> +#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
> +			if (minstrel_vht_only)
> +				continue;
> +#endif

When reformatting for 80chars I introduced a pb, the test should be 
'if (use_vht && minstrel_vht_only)' instead.
The consequence is VHT-unable devices with
CONFIG_MAC80211_RC_MINSTREL_VHT set won't have HT rates enabled.
Johannes can you squash it in or should I send you a patch ?

 
Karl
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Johannes Berg Oct. 24, 2014, 12:01 p.m. UTC | #2
On Fri, 2014-10-24 at 13:48 +0200, Karl Beldan wrote:
> Hi,
> 
> On Tue, Oct 21, 2014 at 10:38:38AM +0200, Karl Beldan wrote:
> > From: Karl Beldan <karl.beldan@rivierawaves.com>
> > 
> > @@ -1026,13 +1184,47 @@ minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
> >  		    sta->bandwidth < IEEE80211_STA_RX_BW_40)
> >  			continue;
> >  
> > +		nss = minstrel_mcs_groups[i].streams;
> > +
> >  		/* Mark MCS > 7 as unsupported if STA is in static SMPS mode */
> > -		if (sta->smps_mode == IEEE80211_SMPS_STATIC &&
> > -		    minstrel_mcs_groups[i].streams > 1)
> > +		if (sta->smps_mode == IEEE80211_SMPS_STATIC && nss > 1)
> > +			continue;
> > +
> > +		/* HT rate */
> > +		if (gflags & IEEE80211_TX_RC_MCS) {
> > +#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
> > +			if (minstrel_vht_only)
> > +				continue;
> > +#endif
> 
> When reformatting for 80chars I introduced a pb, the test should be 
> 'if (use_vht && minstrel_vht_only)' instead.
> The consequence is VHT-unable devices with
> CONFIG_MAC80211_RC_MINSTREL_VHT set won't have HT rates enabled.
> Johannes can you squash it in or should I send you a patch ?

I'm not sure I'm following, please send a patch, and I may decide to
fold that.

johannes

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jouni Malinen Nov. 14, 2014, 4:43 p.m. UTC | #3
On Tue, Oct 21, 2014 at 10:38:38AM +0200, Karl Beldan wrote:
> When the new CONFIG_MAC80211_RC_MINSTREL_VHT is not set (default 'N'),
> there is no behavioral change including in sampling and MCS_GROUP_RATES
> remains 8.
> Otherwise MCS_GROUP_RATES is 10, and a module parameter *vht_only*
> (default 'true'), restricts the rates selection to VHT when vht is
> supported.

Have you tested this with mac80211_hwsim? I'm seeing number of issues in
hwsim test cases with CONFIG_MAC80211_RC_MINSTREL_VHT=y with
wireless-testing.git. These disappear if I disable this build option.

For example, with nfc_p2p_go_neg test case:

[    7.023869] wlan1: authenticate with 02:00:00:00:00:00
[    7.024421] wlan1: Allocated STA 02:00:00:00:00:00
[    7.024426] wlan1: capabilities/regulatory prevented using AP HT/VHT configuration, downgraded

(note: this part happens also with minstrel_ht VHT supported disabled,
but the issues below don't)

...

[    7.255983] wlan0: Allocated STA 02:00:00:00:01:00
[    7.255988] wlan0: moving STA 02:00:00:00:01:00 to state 2
[    7.255991] wlan0: moving STA 02:00:00:00:01:00 to state 3
[    7.256232] wlan0: Inserted STA 02:00:00:00:01:00
[    7.270176] wlan1: dropped frame to 02:00:00:00:00:00 (unauthorized port)
[    7.494771] ------------[ cut here ]------------
[    7.495194] WARNING: CPU: 1 PID: 584 at net/mac80211/rate.c:501 ieee80211_get_tx_rates+0x2c2/0x810()
[    7.495972] CPU: 1 PID: 584 Comm: wpa_supplicant Not tainted 3.18.0-rc4-wl #347
[    7.496607] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
[    7.497322]  0000000000000009 ffff88001e327798 ffffffff814d7f34 ffffffff81089c41
[    7.498010]  0000000000000000 ffff88001e3277d8 ffffffff8103ab6c 0000000000000246
[    7.498703]  ffff88001dcb7600 ffff88001dcb7630 0000000000000000 ffff88001dcb763c
[    7.499399] Call Trace:
[    7.499628]  [<ffffffff814d7f34>] dump_stack+0x4e/0x71
[    7.500108]  [<ffffffff81089c41>] ? console_unlock+0x1f1/0x4d0
[    7.500628]  [<ffffffff8103ab6c>] warn_slowpath_common+0x7c/0xa0
[    7.501184]  [<ffffffff8103ac7a>] warn_slowpath_null+0x1a/0x20
[    7.501702]  [<ffffffff814598e2>] ieee80211_get_tx_rates+0x2c2/0x810
[    7.502256]  [<ffffffff81459f0a>] rate_control_get_rate+0xda/0xf0
[    7.502790]  [<ffffffff8146f8f2>] ieee80211_tx_h_rate_ctrl+0x1a2/0x430
[    7.503368]  [<ffffffff8146fda8>] invoke_tx_handlers+0x108/0x180
[    7.503898]  [<ffffffff81470d15>] ieee80211_tx+0x75/0xf0
[    7.504370]  [<ffffffff81470ef1>] ieee80211_xmit+0xa1/0x100
[    7.504862]  [<ffffffff8147239a>] ieee80211_subif_start_xmit+0xf9a/0x1150
[    7.505494]  [<ffffffff81471442>] ?  ieee80211_subif_start_xmit+0x42/0x1150
[    7.506039]  [<ffffffff812e9ee1>] dev_hard_start_xmit+0x271/0x830
[    7.506394]  [<ffffffff812e9cc8>] ? dev_hard_start_xmit+0x58/0x830
[    7.506754]  [<ffffffff8130d822>] sch_direct_xmit+0xf2/0x1f0
[    7.507086]  [<ffffffff812ea704>] __dev_queue_xmit+0x264/0x8e0
[    7.507426]  [<ffffffff812ea4f0>] ? __dev_queue_xmit+0x50/0x8e0
[    7.507774]  [<ffffffff812eae00>] dev_queue_xmit+0x10/0x20
[    7.508110]  [<ffffffff813cdcba>] packet_sendmsg+0xdfa/0x10a0
[    7.508450]  [<ffffffff812cec59>] sock_sendmsg+0x69/0x90
[    7.508762]  [<ffffffff812ce8f5>] ? move_addr_to_kernel+0x45/0x70
[    7.509130]  [<ffffffff812d02e2>] SyS_sendto+0x112/0x150
[    7.509469]  [<ffffffff814e2995>] ? sysret_check+0x22/0x5d
[    7.509793]  [<ffffffff81080de5>] ?  trace_hardirqs_on_caller+0x105/0x1d0
[    7.510208]  [<ffffffff811e7eab>] ? trace_hardirqs_on_thunk+0x3a/0x3f
[    7.510587]  [<ffffffff814e2969>] system_call_fastpath+0x12/0x17
[    7.510941] ---[ end trace a7659d7a05f5ad35 ]---
[    7.511249] ------------[ cut here ]------------
[    7.511525] WARNING: CPU: 1 PID: 584 at net/mac80211/rx.c:3452 ieee80211_rx+0xb54/0xb60()
[    7.512001] Rate marked as a VHT rate but data is invalid: MCS: 11, NSS: 1
[    7.512542] CPU: 1 PID: 584 Comm: wpa_supplicant Tainted: G        W 3.18.0-rc4-wl #347
[    7.513210] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
[    7.513839]  0000000000000009 ffff88001fc83d98 ffffffff814d7f34 ffffffff81089c41
[    7.514463]  ffff88001fc83de8 ffff88001fc83dd8 ffffffff8103ab6c 0000000000000001
[    7.514926]  ffff88001dcb7d00 ffff88001e1f07c0 ffff88001e1f07c0 ffff88001e1f0ce0
[    7.515388] Call Trace:
[    7.515536]  <IRQ>  [<ffffffff814d7f34>] dump_stack+0x4e/0x71
[    7.515895]  [<ffffffff81089c41>] ? console_unlock+0x1f1/0x4d0
[    7.516242]  [<ffffffff8103ab6c>] warn_slowpath_common+0x7c/0xa0
[    7.516597]  [<ffffffff8103ac06>] warn_slowpath_fmt+0x46/0x50
[    7.516949]  [<ffffffff8146e404>] ieee80211_rx+0xb54/0xb60
[    7.517316]  [<ffffffff81080d92>] ?  trace_hardirqs_on_caller+0xb2/0x1d0
[    7.517710]  [<ffffffff81080ebd>] ? trace_hardirqs_on+0xd/0x10
[    7.518056]  [<ffffffff81430d94>] ieee80211_tasklet_handler+0xd4/0xe0
[    7.518435]  [<ffffffff8103f3d7>] tasklet_action+0xe7/0xf0
[    7.518761]  [<ffffffff8103e780>] __do_softirq+0x150/0x610
[    7.519083]  [<ffffffff812ea74a>] ? __dev_queue_xmit+0x2aa/0x8e0
[    7.519453]  [<ffffffff814e43cc>] do_softirq_own_stack+0x1c/0x30
[    7.519804]  <EOI>  [<ffffffff8103ed2d>] do_softirq+0x7d/0x90
[    7.520193]  [<ffffffff8103ee27>] __local_bh_enable_ip+0xe7/0xf0
[    7.520707]  [<ffffffff812ea773>] __dev_queue_xmit+0x2d3/0x8e0
[    7.521047]  [<ffffffff812ea4f0>] ? __dev_queue_xmit+0x50/0x8e0
[    7.521420]  [<ffffffff812eae00>] dev_queue_xmit+0x10/0x20
[    7.521743]  [<ffffffff813cdcba>] packet_sendmsg+0xdfa/0x10a0
[    7.522082]  [<ffffffff812cec59>] sock_sendmsg+0x69/0x90
[    7.522396]  [<ffffffff812ce8f5>] ? move_addr_to_kernel+0x45/0x70
[    7.522759]  [<ffffffff812d02e2>] SyS_sendto+0x112/0x150
[    7.523078]  [<ffffffff814e2995>] ? sysret_check+0x22/0x5d
[    7.523403]  [<ffffffff81080de5>] ?  trace_hardirqs_on_caller+0x105/0x1d0
[    7.523797]  [<ffffffff811e7eab>] ? trace_hardirqs_on_thunk+0x3a/0x3f
[    7.524176]  [<ffffffff814e2969>] system_call_fastpath+0x12/0x17
[    7.524531] ---[ end trace a7659d7a05f5ad36 ]---
[    7.910139] wlan1: dropped frame to 02:00:00:00:00:00 (unauthorized port)
[    8.050101] wlan1: dropped frame to 02:00:00:00:00:00 (unauthorized port)
[    8.910107] wlan1: dropped frame to 02:00:00:00:00:00 (unauthorized port)
[    8.910754] wlan1: dropped frame to 02:00:00:00:00:00 (unauthorized port)
[    9.210099] wlan1: dropped frame to 02:00:00:00:00:00 (unauthorized port)
[   12.512254] ------------[ cut here ]------------
[   12.512542] WARNING: CPU: 1 PID: 585 at net/mac80211/rate.c:501 ieee80211_get_tx_rates+0x2c2/0x810()
...
Karl Beldan Nov. 14, 2014, 5:18 p.m. UTC | #4
Hi,

On Fri, Nov 14, 2014 at 06:43:28PM +0200, Jouni Malinen wrote:
> On Tue, Oct 21, 2014 at 10:38:38AM +0200, Karl Beldan wrote:
> > When the new CONFIG_MAC80211_RC_MINSTREL_VHT is not set (default 'N'),
> > there is no behavioral change including in sampling and MCS_GROUP_RATES
> > remains 8.
> > Otherwise MCS_GROUP_RATES is 10, and a module parameter *vht_only*
> > (default 'true'), restricts the rates selection to VHT when vht is
> > supported.
> 
> Have you tested this with mac80211_hwsim? I'm seeing number of issues in

Yes, only with iw and nothing fancy, I also have had it running on some 
boards for some weeks as is and with rfc version for more than a year at
work (minstrel not the whole tree).
I guess I can take the tip of wireless-testing but can you give your
HEAD though ?

> hwsim test cases with CONFIG_MAC80211_RC_MINSTREL_VHT=y with
> wireless-testing.git. These disappear if I disable this build option.
> 
> For example, with nfc_p2p_go_neg test case:
> 

Have you reproduced it with other testcases (maybe more regular so that
I can get my hands on it faster) ?
Is it also happening when VHT support is not advertized by upper layers
?


Rgds,
 
Karl
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jouni Malinen Nov. 14, 2014, 5:36 p.m. UTC | #5
On Fri, Nov 14, 2014 at 06:18:32PM +0100, Karl Beldan wrote:
> Yes, only with iw and nothing fancy, I also have had it running on some 
> boards for some weeks as is and with rfc version for more than a year at
> work (minstrel not the whole tree).
> I guess I can take the tip of wireless-testing but can you give your
> HEAD though ?

It looks like pretty much any snapshot between master-2014-11-04 and now
is affected.. Anyway, the current master snapshot in my tests was
0e204e2422876c18034ca960c4fccf727a02a5c1.

> > For example, with nfc_p2p_go_neg test case:
> 
> Have you reproduced it with other testcases (maybe more regular so that
> I can get my hands on it faster) ?
> Is it also happening when VHT support is not advertized by upper layers
> ?

This seems to be specific to exact timing of frames since the same issue
does not show up in non-NFC P2P test cases. So no, this does not show up
with anything else than the nfc_p2p_* test cases (well, at least not in
my desktop+kvm setup; YMMV with other CPU speeds that could potentially
affect timing):
nfc_p2p_go_neg
nfc_p2p_go_neg_reverse
nfc_p2p_ip_addr_assignment
nfc_p2p_static_handover_tagdev_client
nfc_p2p_static_handover_tagdev_client_group_iface
nfc_p2p_static_handover_tagdev_go

I'd assume the key here is in P2P with NFC trigger having the fastest
possible connection process due to all the optimizations in channel
selection on WPS (which is those EAPOL Data frames that hit these rate
selection issues immediately after association).

That said, if you use the tests/hwsim scripts from
git://w1.fi/hostap.git there should be no difference on running some of
these cases vs. something more regular..

If you have not used these test scripts previously, you can find more
information about the setup here:
http://w1.fi/cgit/hostap/plain/tests/hwsim/README
http://w1.fi/cgit/hostap/plain/tests/hwsim/vm/README
Karl Beldan Nov. 14, 2014, 6:29 p.m. UTC | #6
On Fri, Nov 14, 2014 at 07:36:48PM +0200, Jouni Malinen wrote:
> On Fri, Nov 14, 2014 at 06:18:32PM +0100, Karl Beldan wrote:
> > Yes, only with iw and nothing fancy, I also have had it running on some 
> > boards for some weeks as is and with rfc version for more than a year at
> > work (minstrel not the whole tree).
> > I guess I can take the tip of wireless-testing but can you give your
> > HEAD though ?
> 
> It looks like pretty much any snapshot between master-2014-11-04 and now
> is affected.. Anyway, the current master snapshot in my tests was
> 0e204e2422876c18034ca960c4fccf727a02a5c1.
> 
> > > For example, with nfc_p2p_go_neg test case:
> > 
> > Have you reproduced it with other testcases (maybe more regular so that
> > I can get my hands on it faster) ?
> > Is it also happening when VHT support is not advertized by upper layers
> > ?
> 
> This seems to be specific to exact timing of frames since the same issue
> does not show up in non-NFC P2P test cases. So no, this does not show up
> with anything else than the nfc_p2p_* test cases (well, at least not in
> my desktop+kvm setup; YMMV with other CPU speeds that could potentially
> affect timing):
> nfc_p2p_go_neg
> nfc_p2p_go_neg_reverse
> nfc_p2p_ip_addr_assignment
> nfc_p2p_static_handover_tagdev_client
> nfc_p2p_static_handover_tagdev_client_group_iface
> nfc_p2p_static_handover_tagdev_go
> 
> I'd assume the key here is in P2P with NFC trigger having the fastest
> possible connection process due to all the optimizations in channel
> selection on WPS (which is those EAPOL Data frames that hit these rate
> selection issues immediately after association).
> 
> That said, if you use the tests/hwsim scripts from
> git://w1.fi/hostap.git there should be no difference on running some of
> these cases vs. something more regular..
> 
> If you have not used these test scripts previously, you can find more
> information about the setup here:
> http://w1.fi/cgit/hostap/plain/tests/hwsim/README
> http://w1.fi/cgit/hostap/plain/tests/hwsim/vm/README
> 

Ok, thanks for your pointers Jouni, I should get back to you soon.
 
Karl
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Karl Beldan Nov. 14, 2014, 10:40 p.m. UTC | #7
On Fri, Nov 14, 2014 at 07:36:48PM +0200, Jouni Malinen wrote:
> On Fri, Nov 14, 2014 at 06:18:32PM +0100, Karl Beldan wrote:
> > Yes, only with iw and nothing fancy, I also have had it running on some 
> > boards for some weeks as is and with rfc version for more than a year at
> > work (minstrel not the whole tree).
> > I guess I can take the tip of wireless-testing but can you give your
> > HEAD though ?
> 
> It looks like pretty much any snapshot between master-2014-11-04 and now
> is affected.. Anyway, the current master snapshot in my tests was
> 0e204e2422876c18034ca960c4fccf727a02a5c1.
> 

I tried to trigger the warning with it and nfc_p2p_* (even looping with
stop.sh; start.sh; run-tests.py nfc_p2p_*), to no avail, the tests are
successful and 0 warning.
I'll see if anything stands out in the code tomorrow.
 
Karl

> > > For example, with nfc_p2p_go_neg test case:
> > 
> > Have you reproduced it with other testcases (maybe more regular so that
> > I can get my hands on it faster) ?
> > Is it also happening when VHT support is not advertized by upper layers
> > ?
> 
> This seems to be specific to exact timing of frames since the same issue
> does not show up in non-NFC P2P test cases. So no, this does not show up
> with anything else than the nfc_p2p_* test cases (well, at least not in
> my desktop+kvm setup; YMMV with other CPU speeds that could potentially
> affect timing):
> nfc_p2p_go_neg
> nfc_p2p_go_neg_reverse
> nfc_p2p_ip_addr_assignment
> nfc_p2p_static_handover_tagdev_client
> nfc_p2p_static_handover_tagdev_client_group_iface
> nfc_p2p_static_handover_tagdev_go
> 
> I'd assume the key here is in P2P with NFC trigger having the fastest
> possible connection process due to all the optimizations in channel
> selection on WPS (which is those EAPOL Data frames that hit these rate
> selection issues immediately after association).
> 
> That said, if you use the tests/hwsim scripts from
> git://w1.fi/hostap.git there should be no difference on running some of
> these cases vs. something more regular..
> 
> If you have not used these test scripts previously, you can find more
> information about the setup here:
> http://w1.fi/cgit/hostap/plain/tests/hwsim/README
> http://w1.fi/cgit/hostap/plain/tests/hwsim/vm/README
> 
> -- 
> Jouni Malinen                                            PGP id EFC895FA
--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/net/mac80211/Kconfig b/net/mac80211/Kconfig
index aeb6a48..e8049ed 100644
--- a/net/mac80211/Kconfig
+++ b/net/mac80211/Kconfig
@@ -33,6 +33,13 @@  config MAC80211_RC_MINSTREL_HT
 	---help---
 	  This option enables the 'minstrel_ht' TX rate control algorithm
 
+config MAC80211_RC_MINSTREL_VHT
+	bool "Minstrel 802.11ac support" if EXPERT
+	depends on MAC80211_RC_MINSTREL_HT
+	default n
+	---help---
+	  This option enables vht in the 'minstrel_ht' TX rate control algorithm
+
 choice
 	prompt "Default rate control algorithm"
 	depends on MAC80211_HAS_RC
diff --git a/net/mac80211/rc80211_minstrel_ht.c b/net/mac80211/rc80211_minstrel_ht.c
index e760d3d..4666681 100644
--- a/net/mac80211/rc80211_minstrel_ht.c
+++ b/net/mac80211/rc80211_minstrel_ht.c
@@ -10,6 +10,7 @@ 
 #include <linux/skbuff.h>
 #include <linux/debugfs.h>
 #include <linux/random.h>
+#include <linux/moduleparam.h>
 #include <linux/ieee80211.h>
 #include <net/mac80211.h>
 #include "rate.h"
@@ -36,6 +37,7 @@ 
 
 #define BW_20			0
 #define BW_40			1
+#define BW_80			2
 
 /*
  * Define group sort order: HT40 -> SGI -> #streams
@@ -66,6 +68,47 @@ 
 	}								\
 }
 
+#define VHT_GROUP_IDX(_streams, _sgi, _bw)				\
+	(MINSTREL_VHT_GROUP_0 +						\
+	 MINSTREL_MAX_STREAMS * 2 * (_bw) +				\
+	 MINSTREL_MAX_STREAMS * (_sgi) +				\
+	 (_streams) - 1)
+
+#define BW2VBPS(_bw, r3, r2, r1)					\
+	(_bw == BW_80 ? r3 : _bw == BW_40 ? r2 : r1)
+
+#define VHT_GROUP(_streams, _sgi, _bw)					\
+	[VHT_GROUP_IDX(_streams, _sgi, _bw)] = {			\
+	.streams = _streams,						\
+	.flags =							\
+		IEEE80211_TX_RC_VHT_MCS |				\
+		(_sgi ? IEEE80211_TX_RC_SHORT_GI : 0) |			\
+		(_bw == BW_80 ? IEEE80211_TX_RC_80_MHZ_WIDTH :		\
+		 _bw == BW_40 ? IEEE80211_TX_RC_40_MHZ_WIDTH : 0),	\
+	.duration = {							\
+		MCS_DURATION(_streams, _sgi,				\
+			     BW2VBPS(_bw,  117,  54,  26)),		\
+		MCS_DURATION(_streams, _sgi,				\
+			     BW2VBPS(_bw,  234, 108,  52)),		\
+		MCS_DURATION(_streams, _sgi,				\
+			     BW2VBPS(_bw,  351, 162,  78)),		\
+		MCS_DURATION(_streams, _sgi,				\
+			     BW2VBPS(_bw,  468, 216, 104)),		\
+		MCS_DURATION(_streams, _sgi,				\
+			     BW2VBPS(_bw,  702, 324, 156)),		\
+		MCS_DURATION(_streams, _sgi,				\
+			     BW2VBPS(_bw,  936, 432, 208)),		\
+		MCS_DURATION(_streams, _sgi,				\
+			     BW2VBPS(_bw, 1053, 486, 234)),		\
+		MCS_DURATION(_streams, _sgi,				\
+			     BW2VBPS(_bw, 1170, 540, 260)),		\
+		MCS_DURATION(_streams, _sgi,				\
+			     BW2VBPS(_bw, 1404, 648, 312)),		\
+		MCS_DURATION(_streams, _sgi,				\
+			     BW2VBPS(_bw, 1560, 720, 346))		\
+	}								\
+}
+
 #define CCK_DURATION(_bitrate, _short, _len)		\
 	(1000 * (10 /* SIFS */ +			\
 	 (_short ? 72 + 24 : 144 + 48) +		\
@@ -91,6 +134,13 @@ 
 		}					\
 	}
 
+#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
+static bool minstrel_vht_only = true;
+module_param(minstrel_vht_only, bool, 0644);
+MODULE_PARM_DESC(minstrel_vht_only,
+		 "Use only VHT rates when VHT is supported by sta.");
+#endif
+
 /*
  * To enable sufficiently targeted rate sampling, MCS rates are divided into
  * groups, based on the number of streams and flags (HT40, SGI) that they
@@ -124,9 +174,46 @@  const struct mcs_group minstrel_mcs_groups[] = {
 	MCS_GROUP(3, 1, BW_40),
 #endif
 
-	CCK_GROUP
-};
+	CCK_GROUP,
+
+#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
+	VHT_GROUP(1, 0, BW_20),
+	VHT_GROUP(2, 0, BW_20),
+#if MINSTREL_MAX_STREAMS >= 3
+	VHT_GROUP(3, 0, BW_20),
+#endif
+
+	VHT_GROUP(1, 1, BW_20),
+	VHT_GROUP(2, 1, BW_20),
+#if MINSTREL_MAX_STREAMS >= 3
+	VHT_GROUP(3, 1, BW_20),
+#endif
 
+	VHT_GROUP(1, 0, BW_40),
+	VHT_GROUP(2, 0, BW_40),
+#if MINSTREL_MAX_STREAMS >= 3
+	VHT_GROUP(3, 0, BW_40),
+#endif
+
+	VHT_GROUP(1, 1, BW_40),
+	VHT_GROUP(2, 1, BW_40),
+#if MINSTREL_MAX_STREAMS >= 3
+	VHT_GROUP(3, 1, BW_40),
+#endif
+
+	VHT_GROUP(1, 0, BW_80),
+	VHT_GROUP(2, 0, BW_80),
+#if MINSTREL_MAX_STREAMS >= 3
+	VHT_GROUP(3, 0, BW_80),
+#endif
+
+	VHT_GROUP(1, 1, BW_80),
+	VHT_GROUP(2, 1, BW_80),
+#if MINSTREL_MAX_STREAMS >= 3
+	VHT_GROUP(3, 1, BW_80),
+#endif
+#endif
+};
 
 static u8 sample_table[SAMPLE_COLUMNS][MCS_GROUP_RATES] __read_mostly;
 
@@ -134,6 +221,45 @@  static void
 minstrel_ht_update_rates(struct minstrel_priv *mp, struct minstrel_ht_sta *mi);
 
 /*
+ * Some VHT MCSes are invalid (when Ndbps / Nes is not an integer)
+ * e.g for MCS9@20MHzx1Nss: Ndbps=8x52*(5/6) Nes=1
+ *
+ * Returns the valid mcs map for struct minstrel_mcs_group_data.supported
+ */
+static u16
+minstrel_get_valid_vht_rates(int bw, int nss, __le16 mcs_map)
+{
+	u16 mask = 0;
+
+	if (bw == BW_20) {
+		if (nss != 3 && nss != 6)
+			mask = BIT(9);
+	} else if (bw == BW_80) {
+		if (nss == 3 || nss == 7)
+			mask = BIT(6);
+		else if (nss == 6)
+			mask = BIT(9);
+	} else {
+		WARN_ON(bw != BW_40);
+	}
+
+	switch ((le16_to_cpu(mcs_map) >> (2 * (nss - 1))) & 3) {
+	case IEEE80211_VHT_MCS_SUPPORT_0_7:
+		mask |= 0x300;
+		break;
+	case IEEE80211_VHT_MCS_SUPPORT_0_8:
+		mask |= 0x200;
+		break;
+	case IEEE80211_VHT_MCS_SUPPORT_0_9:
+		break;
+	default:
+		mask = 0x3ff;
+	}
+
+	return 0x3ff & ~mask;
+}
+
+/*
  * Look up an MCS group index based on mac80211 rate information
  */
 static int
@@ -144,6 +270,15 @@  minstrel_ht_get_group_idx(struct ieee80211_tx_rate *rate)
 			 !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH));
 }
 
+static int
+minstrel_vht_get_group_idx(struct ieee80211_tx_rate *rate)
+{
+	return VHT_GROUP_IDX(ieee80211_rate_get_vht_nss(rate),
+			     !!(rate->flags & IEEE80211_TX_RC_SHORT_GI),
+			     !!(rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) +
+			     2*!!(rate->flags & IEEE80211_TX_RC_80_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)
@@ -153,6 +288,9 @@  minstrel_ht_get_stats(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
 	if (rate->flags & IEEE80211_TX_RC_MCS) {
 		group = minstrel_ht_get_group_idx(rate);
 		idx = rate->idx % 8;
+	} else if (rate->flags & IEEE80211_TX_RC_VHT_MCS) {
+		group = minstrel_vht_get_group_idx(rate);
+		idx = ieee80211_rate_get_vht_mcs(rate);
 	} else {
 		group = MINSTREL_CCK_GROUP;
 
@@ -489,7 +627,8 @@  minstrel_ht_txstat_valid(struct minstrel_priv *mp, struct ieee80211_tx_rate *rat
 	if (!rate->count)
 		return false;
 
-	if (rate->flags & IEEE80211_TX_RC_MCS)
+	if (rate->flags & IEEE80211_TX_RC_MCS ||
+	    rate->flags & IEEE80211_TX_RC_VHT_MCS)
 		return true;
 
 	return rate->idx == mp->cck_rates[0] ||
@@ -736,6 +875,9 @@  minstrel_ht_set_rate(struct minstrel_priv *mp, struct minstrel_ht_sta *mi,
 
 	if (index / MCS_GROUP_RATES == MINSTREL_CCK_GROUP)
 		idx = mp->cck_rates[index % ARRAY_SIZE(mp->cck_rates)];
+	else if (flags & IEEE80211_TX_RC_VHT_MCS)
+		idx = ((group->streams - 1) << 4) |
+		      ((index % MCS_GROUP_RATES) & 0xF);
 	else
 		idx = index % MCS_GROUP_RATES + (group->streams - 1) * 8;
 
@@ -917,6 +1059,9 @@  minstrel_ht_get_rate(void *priv, struct ieee80211_sta *sta, void *priv_sta,
 	if (sample_idx / MCS_GROUP_RATES == MINSTREL_CCK_GROUP) {
 		int idx = sample_idx % ARRAY_SIZE(mp->cck_rates);
 		rate->idx = mp->cck_rates[idx];
+	} else if (sample_group->flags & IEEE80211_TX_RC_VHT_MCS) {
+		ieee80211_rate_set_vht(rate, sample_idx % MCS_GROUP_RATES,
+				       sample_group->streams);
 	} else {
 		rate->idx = sample_idx % MCS_GROUP_RATES +
 			    (sample_group->streams - 1) * 8;
@@ -962,6 +1107,8 @@  minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
 	struct minstrel_ht_sta *mi = &msp->ht;
 	struct ieee80211_mcs_info *mcs = &sta->ht_cap.mcs;
 	u16 sta_cap = sta->ht_cap.cap;
+	struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+	int use_vht;
 	int n_supported = 0;
 	int ack_dur;
 	int stbc;
@@ -973,6 +1120,13 @@  minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
 
 	BUILD_BUG_ON(ARRAY_SIZE(minstrel_mcs_groups) != MINSTREL_GROUPS_NB);
 
+#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
+	if (vht_cap->vht_supported)
+		use_vht = vht_cap->vht_mcs.tx_mcs_map != cpu_to_le16(~0);
+	else
+#endif
+	use_vht = 0;
+
 	msp->is_ht = true;
 	memset(mi, 0, sizeof(*mi));
 
@@ -996,15 +1150,19 @@  minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
 	}
 	mi->sample_tries = 4;
 
-	stbc = (sta_cap & IEEE80211_HT_CAP_RX_STBC) >>
-		IEEE80211_HT_CAP_RX_STBC_SHIFT;
-	mi->tx_flags |= stbc << IEEE80211_TX_CTL_STBC_SHIFT;
+	/* TODO tx_flags for vht - ATM the RC API is not fine-grained enough */
+	if (!use_vht) {
+		stbc = (sta_cap & IEEE80211_HT_CAP_RX_STBC) >>
+			IEEE80211_HT_CAP_RX_STBC_SHIFT;
+		mi->tx_flags |= stbc << IEEE80211_TX_CTL_STBC_SHIFT;
 
-	if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING)
-		mi->tx_flags |= IEEE80211_TX_CTL_LDPC;
+		if (sta_cap & IEEE80211_HT_CAP_LDPC_CODING)
+			mi->tx_flags |= IEEE80211_TX_CTL_LDPC;
+	}
 
 	for (i = 0; i < ARRAY_SIZE(mi->groups); i++) {
 		u32 gflags = minstrel_mcs_groups[i].flags;
+		int bw, nss;
 
 		mi->groups[i].supported = 0;
 		if (i == MINSTREL_CCK_GROUP) {
@@ -1026,13 +1184,47 @@  minstrel_ht_update_caps(void *priv, struct ieee80211_supported_band *sband,
 		    sta->bandwidth < IEEE80211_STA_RX_BW_40)
 			continue;
 
+		nss = minstrel_mcs_groups[i].streams;
+
 		/* Mark MCS > 7 as unsupported if STA is in static SMPS mode */
-		if (sta->smps_mode == IEEE80211_SMPS_STATIC &&
-		    minstrel_mcs_groups[i].streams > 1)
+		if (sta->smps_mode == IEEE80211_SMPS_STATIC && nss > 1)
+			continue;
+
+		/* HT rate */
+		if (gflags & IEEE80211_TX_RC_MCS) {
+#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
+			if (minstrel_vht_only)
+				continue;
+#endif
+			mi->groups[i].supported = mcs->rx_mask[nss - 1];
+			if (mi->groups[i].supported)
+				n_supported++;
+			continue;
+		}
+
+		/* VHT rate */
+		if (!vht_cap->vht_supported ||
+		    WARN_ON(!(gflags & IEEE80211_TX_RC_VHT_MCS)) ||
+		    WARN_ON(gflags & IEEE80211_TX_RC_160_MHZ_WIDTH))
 			continue;
 
-		mi->groups[i].supported =
-			mcs->rx_mask[minstrel_mcs_groups[i].streams - 1];
+		if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH) {
+			if (sta->bandwidth < IEEE80211_STA_RX_BW_80 ||
+			    ((gflags & IEEE80211_TX_RC_SHORT_GI) &&
+			     !(vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80))) {
+				continue;
+			}
+		}
+
+		if (gflags & IEEE80211_TX_RC_40_MHZ_WIDTH)
+			bw = BW_40;
+		else if (gflags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+			bw = BW_80;
+		else
+			bw = BW_20;
+
+		mi->groups[i].supported = minstrel_get_valid_vht_rates(bw, nss,
+				vht_cap->vht_mcs.tx_mcs_map);
 
 		if (mi->groups[i].supported)
 			n_supported++;
diff --git a/net/mac80211/rc80211_minstrel_ht.h b/net/mac80211/rc80211_minstrel_ht.h
index e747ac6..f2217d6 100644
--- a/net/mac80211/rc80211_minstrel_ht.h
+++ b/net/mac80211/rc80211_minstrel_ht.h
@@ -15,17 +15,30 @@ 
  */
 #define MINSTREL_MAX_STREAMS		3
 #define MINSTREL_HT_STREAM_GROUPS	4 /* BW(=2) * SGI(=2) */
+#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
+#define MINSTREL_VHT_STREAM_GROUPS	6 /* BW(=3) * SGI(=2) */
+#else
+#define MINSTREL_VHT_STREAM_GROUPS	0
+#endif
 
 #define MINSTREL_HT_GROUPS_NB	(MINSTREL_MAX_STREAMS *		\
 				 MINSTREL_HT_STREAM_GROUPS)
+#define MINSTREL_VHT_GROUPS_NB	(MINSTREL_MAX_STREAMS *		\
+				 MINSTREL_VHT_STREAM_GROUPS)
 #define MINSTREL_CCK_GROUPS_NB	1
 #define MINSTREL_GROUPS_NB	(MINSTREL_HT_GROUPS_NB +	\
+				 MINSTREL_VHT_GROUPS_NB +	\
 				 MINSTREL_CCK_GROUPS_NB)
 
 #define MINSTREL_HT_GROUP_0	0
 #define MINSTREL_CCK_GROUP	(MINSTREL_HT_GROUP_0 + MINSTREL_HT_GROUPS_NB)
+#define MINSTREL_VHT_GROUP_0	(MINSTREL_CCK_GROUP + 1)
 
-#define MCS_GROUP_RATES	8
+#ifdef CONFIG_MAC80211_RC_MINSTREL_VHT
+#define MCS_GROUP_RATES		10
+#else
+#define MCS_GROUP_RATES		8
+#endif
 
 struct mcs_group {
 	u32 flags;
@@ -40,7 +53,7 @@  struct minstrel_mcs_group_data {
 	u8 column;
 
 	/* bitfield of supported MCS rates of this group */
-	u8 supported;
+	u16 supported;
 
 	/* sorted rate set within a MCS group*/
 	u16 max_group_tp_rate[MAX_THR_RATES];
diff --git a/net/mac80211/rc80211_minstrel_ht_debugfs.c b/net/mac80211/rc80211_minstrel_ht_debugfs.c
index d2f53b8..52bb6ef 100644
--- a/net/mac80211/rc80211_minstrel_ht_debugfs.c
+++ b/net/mac80211/rc80211_minstrel_ht_debugfs.c
@@ -29,6 +29,8 @@  minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
 	mg = &minstrel_mcs_groups[i];
 	if (mg->flags & IEEE80211_TX_RC_40_MHZ_WIDTH)
 		htmode = '4';
+	else if (mg->flags & IEEE80211_TX_RC_80_MHZ_WIDTH)
+		htmode = '8';
 	if (mg->flags & IEEE80211_TX_RC_SHORT_GI)
 		gimode = 'S';
 
@@ -41,9 +43,11 @@  minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
 			continue;
 
 		if (i == MINSTREL_CCK_GROUP)
-			p += sprintf(p, "CCK/%cP   ", j < 4 ? 'L' : 'S');
+			p += sprintf(p, " CCK/%cP   ", j < 4 ? 'L' : 'S');
+		else if (i >= MINSTREL_VHT_GROUP_0)
+			p += sprintf(p, "VHT%c0/%cGI ", htmode, gimode);
 		else
-			p += sprintf(p, "HT%c0/%cGI ", htmode, gimode);
+			p += sprintf(p, " HT%c0/%cGI ", htmode, gimode);
 
 		*(p++) = (idx == mi->max_tp_rate[0]) ? 'A' : ' ';
 		*(p++) = (idx == mi->max_tp_rate[1]) ? 'B' : ' ';
@@ -53,9 +57,11 @@  minstrel_ht_stats_dump(struct minstrel_ht_sta *mi, int i, char *p)
 
 		if (i == MINSTREL_CCK_GROUP) {
 			int r = bitrates[j % 4];
-			p += sprintf(p, " %2u.%1uM", r / 10, r % 10);
+			p += sprintf(p, " %2u.%1uM ", r / 10, r % 10);
+		} else if (i >= MINSTREL_VHT_GROUP_0) {
+			p += sprintf(p, " MCS%-1u/%1u", j, mg->streams);
 		} else {
-			p += sprintf(p, " MCS%-2u", (mg->streams - 1) * 8 + j);
+			p += sprintf(p, " MCS%-2u ", (mg->streams - 1) * 8 + j);
 		}
 
 		tp = mr->cur_tp / 10;
@@ -94,19 +100,20 @@  minstrel_ht_stats_open(struct inode *inode, struct file *file)
 		return ret;
 	}
 
-	ms = kmalloc(8192, GFP_KERNEL);
+	ms = kmalloc(32768, GFP_KERNEL);
 	if (!ms)
 		return -ENOMEM;
 
 	file->private_data = ms;
 	p = ms->buf;
-	p += sprintf(p, "type           rate     tpt eprob *prob "
+	p += sprintf(p, " type           rate      tpt eprob *prob "
 			"ret  *ok(*cum)        ok(      cum)\n");
 
-
 	p = minstrel_ht_stats_dump(mi, MINSTREL_CCK_GROUP, p);
 	for (i = 0; i < MINSTREL_CCK_GROUP; i++)
 		p = minstrel_ht_stats_dump(mi, i, p);
+	for (i++; i < ARRAY_SIZE(mi->groups); i++)
+		p = minstrel_ht_stats_dump(mi, i, p);
 
 	p += sprintf(p, "\nTotal packet count::    ideal %d      "
 			"lookaround %d\n",
@@ -117,7 +124,7 @@  minstrel_ht_stats_open(struct inode *inode, struct file *file)
 		MINSTREL_TRUNC(mi->avg_ampdu_len * 10) % 10);
 	ms->len = p - ms->buf;
 
-	WARN_ON(ms->len + sizeof(*ms) > 8192);
+	WARN_ON(ms->len + sizeof(*ms) > 32768);
 
 	return nonseekable_open(inode, file);
 }