Linux wireless drivers development
 help / color / mirror / Atom feed
* Re: Debugging RTL8192CU firmware loading on 3.12 powerpc
From: Larry Finger @ 2016-09-02 17:17 UTC (permalink / raw)
  To: Simon Wunderlich
  Cc: Sven Eckelmann, Arend Van Spriel, linux-wireless,
	Pannirselvam Kanagaratnam
In-Reply-To: <6578938.N4SWO0a2Ic@bentobox>

On 09/02/2016 06:26 AM, Sven Eckelmann wrote:
> On Freitag, 2. September 2016 13:13:20 CEST Arend Van Spriel wrote:
> [...]
>>> Do you have any recommendations where the firmware loading problems could
>>> come from, and where we could start to debug? Any pointers would be
>>> appreciated.
>> Hi Simon,
>>
>> Could it be an endian issue?
>
> Yes, it could be one (at least I've also guessed this - I could still be
> completely wrong). But the problem is now to find a good starting point for
> the debugging effort.
>
> I've only looked at Simon's screen once while he gather USB dumps but didn't
> spot any obvious at that time. There was also the problem that the comparison
> dump looked already a lot different due to some timing differences.
>
> I think Simon can give you later more details (when required).

Simon,

Yes, it is an endian issue. I can see part of the problem, but I do not have a 
fix yet.

The firmware is read in as an array of bytes, thus it is effectively in 
little-endian order. When it is written back to the device in routine 
_rtl92c_fw_block_write(), the data output uses 32-bit writes. Of course, all 
data supplied in all 2- and 4-byte writes is assumed to be in CPU order. As the 
device needs the data to be little-endian, it will be byte swapped on BE 
machines. As a result, the firmware is written out in the wrong byte order. I 
think that this problem should be fixed with:

diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.c 
b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.c
index 43fcb25..cd7ae70 100644
--- a/drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.c
+++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192c/fw_common.c
@@ -74,18 +74,19 @@ static void _rtl92c_fw_block_write(struct ieee80211_hw *hw,
         struct rtl_priv *rtlpriv = rtl_priv(hw);
         u32 blocksize = sizeof(u32);
         u8 *bufferptr = (u8 *)buffer;
-       u32 *pu4byteptr = (u32 *)buffer;
+       __le32 *pu4byteptr = (__le32 *)buffer;
         u32 i, offset, blockcount, remainsize;
+       u32 data;

         blockcount = size / blocksize;
         remainsize = size % blocksize;

         for (i = 0; i < blockcount; i++) {
                 offset = i * blocksize;
+               data = le32_to_cpu(*(pu4byteptr + i));
                 rtl_write_dword(rtlpriv, (FW_8192C_START_ADDRESS + offset),
-                               *(pu4byteptr + i));
+                               data);
         }
-
         if (remainsize) {
                 offset = blockcount * blocksize;
                 bufferptr += offset;

Unfortunately, applying the patch results in a checksum error on my PPC.

I'll let you know what I find.

Larry

^ permalink raw reply related

* Re: [PATCH v3 1/2] wcn36xx: Transition driver to SMD client
From: Bjorn Andersson @ 2016-09-02 16:46 UTC (permalink / raw)
  To: Kalle Valo
  Cc: Eugene Krasnikov, wcn36xx, linux-wireless, netdev, linux-kernel,
	linux-arm-msm
In-Reply-To: <87mvjqz2ul.fsf@kamboji.qca.qualcomm.com>

On Fri 02 Sep 09:24 PDT 2016, Kalle Valo wrote:

> Bjorn Andersson <bjorn.andersson@linaro.org> writes:
> 
> > The wcn36xx wifi driver follows the life cycle of the WLAN_CTRL SMD
> > channel, as such it should be a SMD client. This patch makes this
> > transition, now that we have the necessary frameworks available.
> >
> > Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> 
> [...]
> 
> > --- a/drivers/net/wireless/ath/wcn36xx/Kconfig
> > +++ b/drivers/net/wireless/ath/wcn36xx/Kconfig
> > @@ -1,6 +1,6 @@
> >  config WCN36XX
> >  	tristate "Qualcomm Atheros WCN3660/3680 support"
> > -	depends on MAC80211 && HAS_DMA
> > +	depends on MAC80211 && HAS_DMA && QCOM_SMD
> 
> While I had this patch on my pending branch I noticed that I can't
> compile wcn36xx on x86 anymore. This is actually quite inconvenient, for
> example then it's easy to miss mac80211 API changes etc. Is there any
> way we could continue build testing wcn36xx on x86 (or all) platforms?
> 

Sorry about that, we should at least be able to COMPILE_TEST it in
non-qcom builds. Thanks for letting me know.

And the driver should also depend on QCOM_WCNSS_CTRL, in the normal
case.

I'll respin this, unless you prefer an incremental patch for those
changes(?)

> Also what about older platforms? Earlier I used wcn36xx on my Nexus 4
> with help of backports project. I can't do that anymore, right?
> 

This has been tested on 8064, 8974 and 8916. So your Nexus 4 is covered.

Unfortunately I don't have a Nexus 4, but we currently have Nexus 7
(the 8064 version), Xperia Z, Xperia Z1 and DB410c using this chip and
all four has been tested with this code.

I've staged the PIL/remoteproc (firmware "loader") driver for v4.9, so
with this patch the only thing missing to fill in the dts files is one
clock from the RPM.


JFYI, There seems to be some race in the removal path, which I will look
into. But I would prefer if we could merge this to make the driver
usable, and more accessible to work on.

Regards,
Bjorn

^ permalink raw reply

* Re: [PATCH v3 1/2] wcn36xx: Transition driver to SMD client
From: Marcel Holtmann @ 2016-09-02 16:38 UTC (permalink / raw)
  To: Kalle Valo
  Cc: Bjorn Andersson, Eugene Krasnikov, wcn36xx, linux-wireless,
	Network Development, LKML, linux-arm-msm
In-Reply-To: <87mvjqz2ul.fsf@kamboji.qca.qualcomm.com>

Hi Kalle,

>> The wcn36xx wifi driver follows the life cycle of the WLAN_CTRL SMD
>> channel, as such it should be a SMD client. This patch makes this
>> transition, now that we have the necessary frameworks available.
>> 
>> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
> 
> [...]
> 
>> --- a/drivers/net/wireless/ath/wcn36xx/Kconfig
>> +++ b/drivers/net/wireless/ath/wcn36xx/Kconfig
>> @@ -1,6 +1,6 @@
>> config WCN36XX
>> 	tristate "Qualcomm Atheros WCN3660/3680 support"
>> -	depends on MAC80211 && HAS_DMA
>> +	depends on MAC80211 && HAS_DMA && QCOM_SMD
> 
> While I had this patch on my pending branch I noticed that I can't
> compile wcn36xx on x86 anymore. This is actually quite inconvenient, for
> example then it's easy to miss mac80211 API changes etc. Is there any
> way we could continue build testing wcn36xx on x86 (or all) platforms?

I would also like that for the btqcomsmd Bluetooth driver. Doing quick build tests on x86 would be great.

Regards

Marcel

^ permalink raw reply

* Re: ath9k: fix AR5416 access GPIO warning
From: Kalle Valo @ 2016-09-02 16:34 UTC (permalink / raw)
  To: miaoqing pan; +Cc: linux-wireless, ath9k-devel, s.l-h, Miaoqing Pan
In-Reply-To: <1470296914-9152-1-git-send-email-miaoqing@codeaurora.org>

miaoqing pan <miaoqing@codeaurora.org> wrote:
> From: Miaoqing Pan <miaoqing@codeaurora.org>
> 
> The warning was seen on AR5416 chip, which invoke ath9k_hw_gio_get()
> before the GPIO initialized correctly.
> 
>     WARNING: CPU: 1 PID: 1159 at ~/drivers/net/wireless/ath/ath9k/hw.c:2776 ath9k_hw_gpio_get+0x148/0x1a0 [ath9k_hw]
>     ...
>     CPU: 1 PID: 1159 Comm: systemd-udevd Not tainted 4.7.0-rc7-aptosid-amd64 #1 aptosid 4.7~rc7-1~git92.slh.3
>     Hardware name:                  /DH67CL, BIOS BLH6710H.86A.0160.2012.1204.1156 12/04/2012
>       0000000000000286 00000000f912d633 ffffffff81290fd3 0000000000000000
>       0000000000000000 ffffffff81063fd4 ffff88040c6dc018 0000000000000000
>       0000000000000002 0000000000000000 0000000000000100 ffff88040c6dc018
>     Call Trace:
>       [<ffffffff81290fd3>] ? dump_stack+0x5c/0x79
>       [<ffffffff81063fd4>] ? __warn+0xb4/0xd0
>       [<ffffffffa0668fb8>] ? ath9k_hw_gpio_get+0x148/0x1a0 [ath9k_hw]
> 
> Signed-off-by: Miaoqing Pan <miaoqing@codeaurora.org>
> Reported-by: Stefan Lippers-Hollmann <s.l-h@gmx.de>
> Tested-by: Stefan Lippers-Hollmann <s.l-h@gmx.de>

Thanks, 1 patch applied to ath-current branch of ath.git:

db7b542e4a78 ath9k: fix AR5416 access GPIO warning

-- 
Sent by pwcli
https://patchwork.kernel.org/patch/9262875/

^ permalink raw reply

* Re: ath10k: fix throughput regression in multi client mode
From: Valo, Kalle @ 2016-09-02 16:30 UTC (permalink / raw)
  To: Manoharan, Rajkumar
  Cc: linux-wireless@vger.kernel.org, ath10k@lists.infradead.org,
	rmanohar@codeaurora.org
In-Reply-To: <425ad8d804bf46feada839a8e0d0c77e@euamsexm01a.eu.qualcomm.com>

Kalle Valo <kvalo@qca.qualcomm.com> writes:

> Rajkumar Manoharan <rmanohar@qti.qualcomm.com> wrote:
>> commit 7a0adc83f34d ("ath10k: improve tx scheduling") is causing
>> severe throughput drop in multi client mode. This issue is originally
>> reported in veriwave setup with 50 clients with TCP downlink traffic.
>> While increasing number of clients, the average throughput drops
>> gradually. With 50 clients, the combined peak throughput is decreased
>> to 98 Mbps whereas reverting given commit restored it to 550 Mbps.
>>=20
>> Processing txqs for every tx completion is causing overhead. Ideally for
>> management frame tx completion, pending txqs processing can be avoided.
>> The change partly reverts the commit "ath10k: improve tx scheduling".
>> Processing pending txqs after all skbs tx completion will yeild enough
>> room to burst tx frames.
>>=20
>> Fixes: 7a0adc83f34d ("ath10k: improve tx scheduling")
>> Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
>
> I'm planning to queue this to 4.8 if no objections.

Actually the patch doesn't apply to ath-current branch so I'll apply to
ath-next instead.

--=20
Kalle Valo=

^ permalink raw reply

* Re: [PATCH v3 1/2] wcn36xx: Transition driver to SMD client
From: Kalle Valo @ 2016-09-02 16:24 UTC (permalink / raw)
  To: Bjorn Andersson
  Cc: Eugene Krasnikov, wcn36xx, linux-wireless, netdev, linux-kernel,
	linux-arm-msm
In-Reply-To: <1472741468-24762-1-git-send-email-bjorn.andersson@linaro.org>

Bjorn Andersson <bjorn.andersson@linaro.org> writes:

> The wcn36xx wifi driver follows the life cycle of the WLAN_CTRL SMD
> channel, as such it should be a SMD client. This patch makes this
> transition, now that we have the necessary frameworks available.
>
> Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>

[...]

> --- a/drivers/net/wireless/ath/wcn36xx/Kconfig
> +++ b/drivers/net/wireless/ath/wcn36xx/Kconfig
> @@ -1,6 +1,6 @@
>  config WCN36XX
>  	tristate "Qualcomm Atheros WCN3660/3680 support"
> -	depends on MAC80211 && HAS_DMA
> +	depends on MAC80211 && HAS_DMA && QCOM_SMD

While I had this patch on my pending branch I noticed that I can't
compile wcn36xx on x86 anymore. This is actually quite inconvenient, for
example then it's easy to miss mac80211 API changes etc. Is there any
way we could continue build testing wcn36xx on x86 (or all) platforms?

Also what about older platforms? Earlier I used wcn36xx on my Nexus 4
with help of backports project. I can't do that anymore, right?

-- 
Kalle Valo

^ permalink raw reply

* Re: [PATCH v2 1/6] rtl8723au: remove declaration of unimplemented functions
From: Kalle Valo @ 2016-09-02 16:18 UTC (permalink / raw)
  To: Luca Ceresoli
  Cc: devel, Larry Finger, Jes Sorensen, Greg Kroah-Hartman,
	linux-wireless, linux-kernel
In-Reply-To: <1472821069-5437-1-git-send-email-luca@lucaceresoli.net>

Luca Ceresoli <luca@lucaceresoli.net> writes:

> These functions have been declared without any implementation since
> the first commit (364e30ebd2dbaccba430c603da03e68746eb932a) and there
> has been no mention of them in following commits.
>
> Signed-off-by: Luca Ceresoli <luca@lucaceresoli.net>
> Cc: Larry Finger <Larry.Finger@lwfinger.net>
> Cc: Jes Sorensen <Jes.Sorensen@redhat.com>
> Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Cc: linux-wireless@vger.kernel.org
> Cc: devel@driverdev.osuosl.org
> Cc: linux-kernel@vger.kernel.org
>
> ---
>
> Changes v1 -> v2:
> - improve the commit message.
> ---
>  drivers/staging/rtl8723au/include/recv_osdep.h | 3 ---
>  1 file changed, 3 deletions(-)

Please prefix staging drivers with "staging:".

-- 
Kalle Valo

^ permalink raw reply

* Re: [v2] ath9k: mark ath_fill_led_pin() static
From: Kalle Valo @ 2016-09-02 16:09 UTC (permalink / raw)
  To: Baoyou Xie
  Cc: ath9k-devel, kvalo, linux-wireless, ath9k-devel, netdev,
	linux-kernel, arnd, baoyou.xie, xie.baoyou
In-Reply-To: <1472473273-2972-1-git-send-email-baoyou.xie@linaro.org>

Baoyou Xie <baoyou.xie@linaro.org> wrote:
> We get 1 warning about global functions without a declaration
> in the ath9k gpio driver when building with W=1:
> drivers/net/wireless/ath/ath9k/gpio.c:25:6: warning: no previous prototype for 'ath_fill_led_pin' [-Wmissing-prototypes]
> 
> In fact, this function is only used in the file in which it is declared
> and don't need a declaration, but can be made static.
> so this patch marks it 'static'.
> 
> Signed-off-by: Baoyou Xie <baoyou.xie@linaro.org>

Thanks, 1 patch applied to ath-next branch of ath.git:

c39265f72ae6 ath9k: mark ath_fill_led_pin() static

-- 
Sent by pwcli
https://patchwork.kernel.org/patch/9303795/

^ permalink raw reply

* Re: ath10k: fix spelling mistake "montior" -> "monitor"
From: Kalle Valo @ 2016-09-02 16:03 UTC (permalink / raw)
  To: Colin Ian King; +Cc: ath10k, linux-wireless, netdev, linux-kernel
In-Reply-To: <20160826180852.32248-1-colin.king@canonical.com>

Colin Ian King <colin.king@canonical.com> wrote:
> From: Colin Ian King <colin.king@canonical.com>
> 
> Trivial fix to spelling mistake in ath10k_warn message.
> 
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
> Reviewed-by: Julian Calaby <julian.calaby@gmail.com>

Thanks, 1 patch applied to ath-next branch of ath.git:

7f03d3069381 ath10k: fix spelling mistake "montior" -> "monitor"

-- 
Sent by pwcli
https://patchwork.kernel.org/patch/9301869/

^ permalink raw reply

* Re: [v5] ath10k: Fix broken NULL func data frame status for 10.4
From: Kalle Valo @ 2016-09-02 16:00 UTC (permalink / raw)
  To: Mohammed Shafi Shajakhan
  Cc: ath10k, mohammed, Tamizh chelvam, linux-wireless,
	Mohammed Shafi Shajakhan
In-Reply-To: <1472199140-8404-1-git-send-email-mohammed@qca.qualcomm.com>

Mohammed Shafi Shajakhan <mohammed@qti.qualcomm.com> wrote:
> From: Mohammed Shafi Shajakhan <mohammed@qti.qualcomm.com>
> 
> Older firmware with HTT delivers incorrect tx status for null func
> frames to driver, but this fixed in 10.2 and 10.4 firmware versions.
> Also this workaround results in reporting of incorrect null func status
> for 10.4. Fix this is by introducing a firmware feature flag for 10.4
> so that this workaround is skipped and proper tx status for null func
> frames are reported
> 
> Signed-off-by: Tamizh chelvam <c_traja@qti.qualcomm.com>
> Signed-off-by: Mohammed Shafi Shajakhan <mohammed@qti.qualcomm.com>

Thanks, 1 patch applied to ath-next branch of ath.git:

2cdce425aa33 ath10k: Fix broken NULL func data frame status for 10.4

-- 
Sent by pwcli
https://patchwork.kernel.org/patch/9300961/

^ permalink raw reply

* Re: ath10k: replace config_enabled() with IS_REACHABLE()
From: Kalle Valo @ 2016-09-02 15:57 UTC (permalink / raw)
  To: Masahiro Yamada
  Cc: ath10k, linux-wireless, Masahiro Yamada, Andrew Morton,
	linux-kernel, netdev
In-Reply-To: <1471969646-13119-1-git-send-email-yamada.masahiro@socionext.com>

Masahiro Yamada <yamada.masahiro@socionext.com> wrote:
> Commit 97f2645f358b ("tree-wide: replace config_enabled() with
> IS_ENABLED()") mostly did away with config_enabled().
> 
> This is one of the postponed TODO items as config_enabled() is used
> for a tristate option here.  Theoretically, config_enabled() is
> equivalent to IS_BUILTIN(), but I guess IS_REACHABLE() is the best
> fit for this case because both CONFIG_HWMON and CONFIG_ATH10K are
> tristate.
> 
> Signed-off-by: Masahiro Yamada <yamada.masahiro@socionext.com>

Thanks, 1 patch applied to ath-next branch of ath.git:

749bc03ae2cd ath10k: replace config_enabled() with IS_REACHABLE()

-- 
Sent by pwcli
https://patchwork.kernel.org/patch/9295945/

^ permalink raw reply

* Re: linux-4.8-rcX:  ath9k traceback
From: Mimi Zohar @ 2016-09-02 15:56 UTC (permalink / raw)
  To: Kalle Valo
  Cc: QCA ath9k Development, ath9k-devel, linux-wireless,
	Luis R. Rodriguez
In-Reply-To: <87eg5338b5.fsf@purkki.adurom.net>

On Thu, 2016-09-01 at 19:15 +0300, Kalle Valo wrote:
> Mimi Zohar <zohar@linux.vnet.ibm.com> writes:
> 
> > There weren't any problems on linux-4.7 kernels.  I'm getting the
> > following traceback on linux-4.8-rc1/-rc4 kernels.  Let me know if you
> > need any additional information.
> >
> > [   64.006529] WARNING: CPU: 3 PID: 94 at drivers/net/wireless/ath/ath9k/beacon.c:642 ath9k_beacon_config+0x12c/0x140 [ath9k]
> 
> This should fix it:
> 
> ath9k: fix client mode beacon configuration
> 
> https://git.kernel.org/cgit/linux/kernel/git/kvalo/wireless-drivers.git/commit/?id=05860bed491b114a9f2d7a4f6e09fb02c0b69056


Yep, that resolved the problem.  Thanks!

Mimi

^ permalink raw reply

* Re: ath10k: Added support for extended dbglog module id for 10.4
From: Kalle Valo @ 2016-09-02 15:56 UTC (permalink / raw)
  To: c_mkenna; +Cc: ath10k, mkenna, linux-wireless, Maharaja Kennadyrajan
In-Reply-To: <1471946736-28214-1-git-send-email-c_mkenna@qti.qualcomm.com>

c_mkenna@qti.qualcomm.com wrote:
> From: Maharaja Kennadyrajan <c_mkenna@qti.qualcomm.com>
> 
> For 10.4 fw versions, dbglog module id has been extended from u32
> to u64, hence this patch fixes the same in the ath10k driver side.
> 
> This patch doesn't break the older 10.4 releases. The FW change
> is already present in the older FWs.
> 
> Signed-off-by: Maharaja Kennadyrajan <c_mkenna@qti.qualcomm.com>

Thanks, 1 patch applied to ath-next branch of ath.git:

afcbc82cea52 ath10k: Added support for extended dbglog module id for 10.4

-- 
Sent by pwcli
https://patchwork.kernel.org/patch/9295361/

^ permalink raw reply

* Re: [2/2] ath10k: use complete() instead complete_all()
From: Kalle Valo @ 2016-09-02 15:55 UTC (permalink / raw)
  To: Daniel Wagner
  Cc: Kalle Valo, netdev, linux-wireless, linux-kernel, ath10k,
	Daniel Wagner, Luis R . Rodriguez, Christian Lamparter
In-Reply-To: <1471525926-20384-3-git-send-email-wagi@monom.org>

Daniel Wagner <wagi@monom.org> wrote:
> From: Daniel Wagner <daniel.wagner@bmw-carit.de>
> 
> There is only one waiter for the completion, therefore there
> is no need to use complete_all(). Let's make that clear by
> using complete() instead of complete_all().
> 
> The usage pattern of the completion is:
> 
> waiter context                          waker context
> 
> scan.started
> ------------
> 
> ath10k_start_scan()
>   lockdep_assert_held(conf_mutex)
>   auth10k_wmi_start_scan()
>   wait_for_completion_timeout(scan.started)
> 
> 					ath10k_wmi_event_scan_start_failed()
> 					  complete(scan.started)
> 
> 					ath10k_wmi_event_scan_started()
> 					  complete(scan.started)
> 
> scan.completed
> --------------
> 
> ath10k_scan_stop()
>   lockdep_assert_held(conf_mutex)
>   ath10k_wmi_stop_scan()
>   wait_for_completion_timeout(scan.completed)
> 
> 					__ath10k_scan_finish()
> 					  complete(scan.completed)
> 
> scan.on_channel
> ---------------
> 
> ath10k_remain_on_channel()
>   mutex_lock(conf_mutex)
>   ath10k_start_scan()
>   wait_for_completion_timeout(scan.on_channel)
> 
> 					ath10k_wmi_event_scan_foreign_chan()
> 					  complete(scan.on_channel)
> 
> offchan_tx_completed
> --------------------
> 
> ath10k_offchan_tx_work()
>   mutex_lock(conf_mutex)
>   reinit_completion(offchan_tx_completed)
>   wait_for_completion_timeout(offchan_tx_completed)
> 
> 					ath10k_report_offchain_tx()
> 					  complete(offchan_tx_completed)
> 
> install_key_done
> ----------------
> ath10k_install_key()
>   lockep_assert_held(conf_mutex)
>   reinit_completion(install_key_done)
>   wait_for_completion_timeout(install_key_done)
> 
> 				        ath10k_htt_t2h_msg_handler()
> 					  complete(install_key_done)
> 
> vdev_setup_done
> ---------------
> 
> ath10k_monitor_vdev_start()
>   lockdep_assert_held(conf_mutex)
>    reinit_completion(vdev_setup_done)
>   ath10k_vdev_setup_sync()
>     wait_for_completion_timeout(vdev_setup_done)
> 
> 					ath10k_wmi_event_vdev_start_resp()
> 					  complete(vdev_setup_done)
> 
> ath10k_monitor_vdev_stop()
>   lockdep_assert_held(conf_mutex)
>   reinit_completion(vdev_setup_done()
>   ath10k_vdev_setup_sync()
>     wait_for_completion_timeout(vdev_setup_done)
> 
> 					ath10k_wmi_event_vdev_stopped()
> 					 complete(vdev_setup_done)
> 
> thermal.wmi_sync
> ----------------
> ath10k_thermal_show_temp()
>   mutex_lock(conf_mutex)
>   reinit_completion(thermal.wmi_sync)
>   wait_for_completion_timeout(thermal.wmi_sync)
> 
> 					ath10k_thermal_event_temperature()
> 					  complete(thermal.wmi_sync)
> 
> bss_survey_done
> ---------------
> ath10k_mac_update_bss_chan_survey
>   lockdep_assert_held(conf_mutex)
>   reinit_completion(bss_survey_done)
>   wait_for_completion_timeout(bss_survey_done)
> 
> 					ath10k_wmi_event_pdev_bss_chan_info()
> 					  complete(bss_survey_done)
> 
> All complete() calls happen while the conf_mutex is taken. That means
> at max one waiter is possible.
> 
> Signed-off-by: Daniel Wagner <daniel.wagner@bmw-carit.de>

Thanks, 1 patch applied to ath-next branch of ath.git:

881ed54ecc13 ath10k: use complete() instead complete_all()

-- 
Sent by pwcli
https://patchwork.kernel.org/patch/9287731/

^ permalink raw reply

* Re: ath10k: fix sending frame in management path in push txq logic
From: Kalle Valo @ 2016-09-02 15:53 UTC (permalink / raw)
  To: Ashok Raj Nagarajan; +Cc: ath10k, Ashok Raj Nagarajan, arnagara, linux-wireless
In-Reply-To: <1471514404-24098-1-git-send-email-arnagara@qti.qualcomm.com>

Ashok Raj Nagarajan <arnagara@qti.qualcomm.com> wrote:
> In the wake tx queue path, we are not checking if the frame to be sent
> takes management path or not. For eg. QOS null func frame coming here will
> take the management path. Since we are not incrementing the descriptor
> counter (num_pending_mgmt_tx) w.r.t tx management, on tx completion it is
> possible to see negative values.
> 
> When the above counter reaches a negative value, we will not be sending a
> probe response out.
> 
>     if (is_presp &&
> 	ar->hw_params.max_probe_resp_desc_thres < htt->num_pending_mgmt_tx)
> 
> For IPQ4019, max_probe_resp_desc_thres (u32) is 24 is compared against
> num_pending_mgmt_tx (int) and the above condtions comes true if the counter
> is negative and we drop the probe response.
> 
> To avoid this, check on the wake tx queue path as well for the tx path of
> the frame and increment the appropriate counters
> 
> Fixes: cac085524cf1 "ath10k: move mgmt descriptor limit handle under mgmt_tx"
> Signed-off-by: Ashok Raj Nagarajan <arnagara@qti.qualcomm.com>

Thanks, 1 patch applied to ath-next branch of ath.git:

e4fd726f21cd ath10k: fix sending frame in management path in push txq logic

-- 
Sent by pwcli
https://patchwork.kernel.org/patch/9287191/

^ permalink raw reply

* Re: ath10k: improve wake_tx_queue ops performance
From: Kalle Valo @ 2016-09-02 15:52 UTC (permalink / raw)
  To: Rajkumar Manoharan; +Cc: ath10k, linux-wireless, Rajkumar Manoharan, rmanohar
In-Reply-To: <20160817153253.6615-1-rmanohar@qti.qualcomm.com>

Rajkumar Manoharan <rmanohar@qti.qualcomm.com> wrote:
> txqs_lock is interfering with wake_tx_queue submitting more frames.
> so queues don't get filled in and don't keep firmware/hardware busy
> enough. This change helps to reduce the txqs_lock contention and
> wake_tx_queue() blockage to being possible in txrx_unref().
> 
> To reduce turn around time of wake_tx_queue ops and to maintain fairness
> among all txqs, the callback is updated to push first txq alone from
> pending list for every wake_tx_queue call. Remaining txqs will be
> processed later upon tx completion.
> 
> Below improvements are observed in push-only mode and validated on
> IPQ4019 platform. With this change, in AP mode ~10Mbps increase is
> observed in downlink (AP -> STA) traffic and approx. 5-10% of CPU
> usage is reduced.
> 
> Major improvement is observed in 1-hop Mesh mode topology in 11ACVHT80.
> Compared to Infra mode, CPU overhead is higher in Mesh mode due to path
> lookup and no fast-xmit support. So reducing spin lock contention is
> helping in Mesh.
> 
>              TOT       +change
>            --------    --------
> TCP DL     545 Mbps    595 Mbps
> TCP UL     555 Mbps    585 Mbps
> 
> Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>

Thanks, 1 patch applied to ath-next branch of ath.git:

83e164b7679d ath10k: improve wake_tx_queue ops performance

-- 
Sent by pwcli
https://patchwork.kernel.org/patch/9286083/

^ permalink raw reply

* Re: ath10k: suppress warnings when getting wmi WDS peer event id
From: Kalle Valo @ 2016-09-02 15:50 UTC (permalink / raw)
  To: Mohammed Shafi Shajakhan
  Cc: ath10k, mohammed, linux-wireless, Mohammed Shafi Shajakhan
In-Reply-To: <1471433280-15237-1-git-send-email-mohammed@qca.qualcomm.com>

Mohammed Shafi Shajakhan <mohammed@qti.qualcomm.com> wrote:
> From: Mohammed Shafi Shajakhan <mohammed@qti.qualcomm.com>
> 
> 'WMI_10_4_WDS_PEER_EVENTID' is not yet handled/implemented for WDS mode,
> as of nowsuppress the warning message "Unknown eventid: 36903"
> 
> Signed-off-by: Mohammed Shafi Shajakhan <mohammed@qti.qualcomm.com>

Thanks, 1 patch applied to ath-next branch of ath.git:

03c41cc126c8 ath10k: suppress warnings when getting wmi WDS peer event id

-- 
Sent by pwcli
https://patchwork.kernel.org/patch/9285623/

^ permalink raw reply

* Re: [PATCH] ath10k: fix memory leak on caldata on error exit path
From: Valo, Kalle @ 2016-09-02 15:45 UTC (permalink / raw)
  To: Colin King
  Cc: ath10k@lists.infradead.org, linux-wireless@vger.kernel.org,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <1471268785-11576-1-git-send-email-colin.king@canonical.com>

Colin King <colin.king@canonical.com> writes:

> From: Colin Ian King <colin.king@canonical.com>
>
> caldata is not being free'd on the error exit path, causing
> a memory leak. kfree it to fix the leak.
>
> Signed-off-by: Colin Ian King <colin.king@canonical.com>
> ---
>  drivers/net/wireless/ath/ath10k/pci.c | 1 +
>  1 file changed, 1 insertion(+)
>
> diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless=
/ath/ath10k/pci.c
> index 9a22c47..886337c 100644
> --- a/drivers/net/wireless/ath/ath10k/pci.c
> +++ b/drivers/net/wireless/ath/ath10k/pci.c
> @@ -2725,6 +2725,7 @@ static int ath10k_pci_hif_fetch_cal_eeprom(struct a=
th10k *ar, void **data,
>  	return 0;
> =20
>  err_free:
> +	kfree(caldata);
>  	kfree(data);
> =20
>  	return -EINVAL;

I don't think we should free data at all:

static int ath10k_download_cal_eeprom(struct ath10k *ar)
{
	size_t data_len;
	void *data =3D NULL;
	int ret;

	ret =3D ath10k_hif_fetch_cal_eeprom(ar, &data, &data_len);

Instead we should free only caldata, right?

--=20
Kalle Valo=

^ permalink raw reply

* Re: [v2] ath10k: fix group privacy action frame decryption for qca4019
From: Kalle Valo @ 2016-09-02 15:38 UTC (permalink / raw)
  To: Rajkumar Manoharan; +Cc: ath10k, linux-wireless, Rajkumar Manoharan, rmanohar
In-Reply-To: <20160809063151.25993-1-rmanohar@qti.qualcomm.com>

Rajkumar Manoharan <rmanohar@qti.qualcomm.com> wrote:
> Recent commit 'mac80211: Encrypt "Group addressed privacy" action frames'
> encrypts group privacy action frames. But qca99x0 family chipset delivers
> broadcast/multicast management frames as encrypted and it should be
> decrypted by mac80211. Setting RX_FLAG_DECRYPTED stats for those frames
> is breaking mesh connection establishment.
> 
> Signed-off-by: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>

Thanks, 1 patch applied to ath-next branch of ath.git:

7d42298eb43d ath10k: fix group privacy action frame decryption for qca4019

-- 
Sent by pwcli
https://patchwork.kernel.org/patch/9270363/

^ permalink raw reply

* Re: ath10k: hide kernel addresses from logs using %pK format specifier
From: Kalle Valo @ 2016-09-02 15:29 UTC (permalink / raw)
  To: c_mkenna; +Cc: ath10k, mkenna, linux-wireless, Maharaja Kennadyrajan
In-Reply-To: <1470318711-28986-1-git-send-email-c_mkenna@qti.qualcomm.com>

c_mkenna@qti.qualcomm.com wrote:
> From: Maharaja Kennadyrajan <c_mkenna@qti.qualcomm.com>
> 
> With the %pK format specifier we hide the kernel addresses
> with the help of kptr_restrict sysctl.
> In this patch, %p is changed to %pK in the driver code.
> 
> The sysctl is documented in Documentation/sysctl/kernel.txt.
> 
> Signed-off-by: Maharaja Kennadyrajan <c_mkenna@qti.qualcomm.com>

Thanks, 1 patch applied to ath-next branch of ath.git:

75b34800a228 ath10k: hide kernel addresses from logs using %pK format specifier

-- 
Sent by pwcli
https://patchwork.kernel.org/patch/9263691/

^ permalink raw reply

* Re: ath10k: Add WMI_SERVICE_PERIODIC_CHAN_STAT_SUPPORT wmi service
From: Kalle Valo @ 2016-09-02 15:26 UTC (permalink / raw)
  To: c_traja; +Cc: ath10k, Tamizh chelvam, linux-wireless
In-Reply-To: <1470134594-10427-1-git-send-email-c_traja@qti.qualcomm.com>

c_traja@qti.qualcomm.com wrote:
> From: Tamizh chelvam <c_traja@qti.qualcomm.com>
> 
> WMI_SERVICE_PERIODIC_CHAN_STAT_SUPPORT service has missed in
> the commit 7e247a9e88dc ("ath10k: add dynamic tx mode switch
> config support for qca4019"). This patch adds the service to
> avoid mismatch between host and target.
> 
> Fixes:7e247a9e88dc ("ath10k: add dynamic tx mode switch config support for qca4019")
> Signed-off-by: Tamizh chelvam <c_traja@qti.qualcomm.com>

Thanks, 1 patch applied to ath-next branch of ath.git:

64ed5771aca2 ath10k: Add WMI_SERVICE_PERIODIC_CHAN_STAT_SUPPORT wmi service

-- 
Sent by pwcli
https://patchwork.kernel.org/patch/9255437/

^ permalink raw reply

* Re: [PATCH] mwifiex: handle edmac vendor command
From: Kalle Valo @ 2016-09-02 14:56 UTC (permalink / raw)
  To: Amitkumar Karwar; +Cc: linux-wireless@vger.kernel.org
In-Reply-To: <0fa2b5d5e09f4396a9c273c3c8dfc3db@SC-EXCH04.marvell.com>

Amitkumar Karwar <akarwar@marvell.com> writes:

>> Sorry for the delay. I have been thinking about vendor commands quite a
>> lot and I don't think they belong to upstream drivers. For regular use
>> cases (used by normal users) we have nl80211, for developer and testing
>> purposes we have debugfs and for manufacturing type of tests we have
>> nl80211 testmode interface. The focus of development should be adding
>> new functionality to nl80211 (and other generic interfaces), not to
>> driver specific vendor commands.
>> 
>> I know brcm80211 and ti's wlcore have few vendor commands but I'm hoping
>> to remove them sometime soon.
>> 
>> Thoughts?
>> 
>
> Thanks for your reply.
>
> There is something called energy detect mode. Chip can detect non-WiFi
> radio signal also and monitor it for specified time before
> transmitting frames. As per ETSI specification, enabling this mode is
> mandatory for some countries for certain frequencies.

To me this looks this is something which can be generic, not a driver
specific interface. And why can't regulatory code enable this
automatically, without any user involvement? It already knows what
country we are in.

> The purpose of this patch is to configure the chip for working in this
> mode. I can see cfg80211 has a framework to handle vendor specific
> commands and events. I think having vendor commands would be helpful
> to support vendor specific functionalities which can't be generalized.

First of all, I don't see how the interface is mwifiex specific. If I
could trust that using vendor commands will not slow down the
development of nl80211, and other generic interfaces, I might think
otherwise. But at the moment I am nowhere near that.

> I suppose debugfs interface is only for collecting stats and info
> which can be used for debugging purpose.

Correct. I consider debugfs as a development and debugging interface for
developers and advanced users.

-- 
Kalle Valo

^ permalink raw reply

* Re: [PATCH v6] mac80211: Move reorder-sensitive TX handlers to after TXQ dequeue.
From: Toke Høiland-Jørgensen @ 2016-09-02 14:44 UTC (permalink / raw)
  To: make-wifi-fast; +Cc: linux-wireless
In-Reply-To: <20160902134104.29309-1-toke@toke.dk>

Toke H=C3=B8iland-J=C3=B8rgensen <toke@toke.dk> writes:

> The TXQ intermediate queues can cause packet reordering when more than
> one flow is active to a single station. Since some of the wifi-specific
> packet handling (notably sequence number and encryption handling) is
> sensitive to re-ordering, things break if they are applied before the
> TXQ.
>
> This splits up the TX handlers and fast_xmit logic into two parts: An
> early part and a late part. The former is applied before TXQ enqueue,
> and the latter after dequeue. The non-TXQ path just applies both parts
> at once.
>
> Because fragments shouldn't be split up or reordered, the fragmentation
> handler is run after dequeue. Any fragments are then kept in the TXQ and
> on subsequent dequeues they take precedence over dequeueing from the FQ
> structure.
>
> This approach avoids having to scatter special cases for when TXQ is
> enabled, at the cost of making the fast_xmit and TX handler code
> slightly more complex.
>
> Signed-off-by: Toke H=C3=B8iland-J=C3=B8rgensen <toke@toke.dk>
> ---
> Changes since v5:
> - Move the fragmentation handler to *after* TXQ dequeue. Fragments are
>   kept in the TXQ for subsequent dequeues. This change also means that
>   the changes to make some of the handlers fragmentation aware are no
>   longer necessary.
> - One of the TX stats updates in the fast path was done before the
>   enqueue step; move that to xmit_fast_finish().
> - Move the rate selection handler to after dequeue, so it's run closer
>   to the time where the packet is actually transmitted.

Found one other thing that needs fixing shortly after posting this, but
figure that I'm probably not done anyway, so will leave it for the next
round. :)

-Toke

^ permalink raw reply

* [PATCH v5] ath9k: Switch to using mac80211 intermediate software queues.
From: Toke Høiland-Jørgensen @ 2016-09-02 14:00 UTC (permalink / raw)
  To: make-wifi-fast, linux-wireless
  Cc: Toke Høiland-Jørgensen, Tim Shepard, Felix Fietkau
In-Reply-To: <20160805160346.10545-1-toke@toke.dk>

This switches ath9k over to using the mac80211 intermediate software
queueing mechanism for data packets. It removes the queueing inside the
driver, except for the retry queue, and instead pulls from mac80211 when
a packet is needed. The retry queue is used to store a packet that was
pulled but can't be sent immediately.

The old code path in ath_tx_start that would queue packets has been
removed completely, as has the qlen limit tunables (since there's no
longer a queue in the driver to limit).

Based on Tim's original patch set, but reworked quite thoroughly.

Cc: Tim Shepard <shep@alum.mit.edu>
Cc: Felix Fietkau <nbd@nbd.name>
Signed-off-by: Toke H=C3=B8iland-J=C3=B8rgensen <toke@toke.dk>
---
Changes since v4:
- Fix regression where PS response frames (which go through the old TX
  path) would not get assigned a seqno because the tid variable was not
  assigned correctly. Many thanks to Felix for debugging this and coming
  up with a fix.

Hopefully that is the last regression (apart from the ongoing mac80211
restructure). :)

 drivers/net/wireless/ath/ath9k/ath9k.h     |  27 ++-
 drivers/net/wireless/ath/ath9k/channel.c   |   2 -
 drivers/net/wireless/ath/ath9k/debug.c     |  14 +-
 drivers/net/wireless/ath/ath9k/debug.h     |   2 -
 drivers/net/wireless/ath/ath9k/debug_sta.c |   4 +-
 drivers/net/wireless/ath/ath9k/init.c      |   2 +-
 drivers/net/wireless/ath/ath9k/main.c      |   9 +-
 drivers/net/wireless/ath/ath9k/xmit.c      | 338 ++++++++++++-----------=
------
 8 files changed, 163 insertions(+), 235 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireles=
s/ath/ath9k/ath9k.h
index 26fc8ec..378d345 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -91,7 +91,6 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_=
descdma *dd,
 #define ATH_RXBUF               512
 #define ATH_TXBUF               512
 #define ATH_TXBUF_RESERVE       5
-#define ATH_MAX_QDEPTH          (ATH_TXBUF / 4 - ATH_TXBUF_RESERVE)
 #define ATH_TXMAXTRY            13
 #define ATH_MAX_SW_RETRIES      30
=20
@@ -145,7 +144,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct at=
h_descdma *dd,
 #define BAW_WITHIN(_start, _bawsz, _seqno) \
 	((((_seqno) - (_start)) & 4095) < (_bawsz))
=20
-#define ATH_AN_2_TID(_an, _tidno)  (&(_an)->tid[(_tidno)])
+#define ATH_AN_2_TID(_an, _tidno) ath_node_to_tid(_an, _tidno)
=20
 #define IS_HT_RATE(rate)   (rate & 0x80)
 #define IS_CCK_RATE(rate)  ((rate >=3D 0x18) && (rate <=3D 0x1e))
@@ -164,7 +163,6 @@ struct ath_txq {
 	spinlock_t axq_lock;
 	u32 axq_depth;
 	u32 axq_ampdu_depth;
-	bool stopped;
 	bool axq_tx_inprogress;
 	struct list_head txq_fifo[ATH_TXFIFO_DEPTH];
 	u8 txq_headidx;
@@ -232,7 +230,6 @@ struct ath_buf {
=20
 struct ath_atx_tid {
 	struct list_head list;
-	struct sk_buff_head buf_q;
 	struct sk_buff_head retry_q;
 	struct ath_node *an;
 	struct ath_txq *txq;
@@ -247,13 +244,13 @@ struct ath_atx_tid {
 	s8 bar_index;
 	bool active;
 	bool clear_ps_filter;
+	bool has_queued;
 };
=20
 struct ath_node {
 	struct ath_softc *sc;
 	struct ieee80211_sta *sta; /* station struct we're part of */
 	struct ieee80211_vif *vif; /* interface with which we're associated */
-	struct ath_atx_tid tid[IEEE80211_NUM_TIDS];
=20
 	u16 maxampdu;
 	u8 mpdudensity;
@@ -276,7 +273,6 @@ struct ath_tx_control {
 	struct ath_node *an;
 	struct ieee80211_sta *sta;
 	u8 paprd;
-	bool force_channel;
 };
=20
=20
@@ -293,7 +289,6 @@ struct ath_tx {
 	struct ath_descdma txdma;
 	struct ath_txq *txq_map[IEEE80211_NUM_ACS];
 	struct ath_txq *uapsdq;
-	u32 txq_max_pending[IEEE80211_NUM_ACS];
 	u16 max_aggr_framelen[IEEE80211_NUM_ACS][4][32];
 };
=20
@@ -421,6 +416,22 @@ struct ath_offchannel {
 	int duration;
 };
=20
+static inline struct ath_atx_tid *
+ath_node_to_tid(struct ath_node *an, u8 tidno)
+{
+	struct ieee80211_sta *sta =3D an->sta;
+	struct ieee80211_vif *vif =3D an->vif;
+	struct ieee80211_txq *txq;
+
+	BUG_ON(!vif);
+	if (sta)
+		txq =3D sta->txq[tidno % ARRAY_SIZE(sta->txq)];
+	else
+		txq =3D vif->txq;
+
+	return (struct ath_atx_tid *) txq->drv_priv;
+}
+
 #define case_rtn_string(val) case val: return #val
=20
 #define ath_for_each_chanctx(_sc, _ctx)                             \
@@ -575,7 +586,6 @@ void ath_tx_edma_tasklet(struct ath_softc *sc);
 int ath_tx_aggr_start(struct ath_softc *sc, struct ieee80211_sta *sta,
 		      u16 tid, u16 *ssn);
 void ath_tx_aggr_stop(struct ath_softc *sc, struct ieee80211_sta *sta, u=
16 tid);
-void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta,=
 u16 tid);
=20
 void ath_tx_aggr_wakeup(struct ath_softc *sc, struct ath_node *an);
 void ath_tx_aggr_sleep(struct ieee80211_sta *sta, struct ath_softc *sc,
@@ -585,6 +595,7 @@ void ath9k_release_buffered_frames(struct ieee80211_h=
w *hw,
 				   u16 tids, int nframes,
 				   enum ieee80211_frame_release_type reason,
 				   bool more_data);
+void ath9k_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *=
queue);
=20
 /********/
 /* VIFs */
diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wirel=
ess/ath/ath9k/channel.c
index 57e26a6..929dd70 100644
--- a/drivers/net/wireless/ath/ath9k/channel.c
+++ b/drivers/net/wireless/ath/ath9k/channel.c
@@ -1010,7 +1010,6 @@ static void ath_scan_send_probe(struct ath_softc *s=
c,
 		goto error;
=20
 	txctl.txq =3D sc->tx.txq_map[IEEE80211_AC_VO];
-	txctl.force_channel =3D true;
 	if (ath_tx_start(sc->hw, skb, &txctl))
 		goto error;
=20
@@ -1133,7 +1132,6 @@ ath_chanctx_send_vif_ps_frame(struct ath_softc *sc,=
 struct ath_vif *avp,
 	memset(&txctl, 0, sizeof(txctl));
 	txctl.txq =3D sc->tx.txq_map[IEEE80211_AC_VO];
 	txctl.sta =3D sta;
-	txctl.force_channel =3D true;
 	if (ath_tx_start(sc->hw, skb, &txctl)) {
 		ieee80211_free_txskb(sc->hw, skb);
 		return false;
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireles=
s/ath/ath9k/debug.c
index c56e40f..89a94dd 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -600,7 +600,6 @@ static int read_file_xmit(struct seq_file *file, void=
 *data)
 	PR("MPDUs XRetried:  ", xretries);
 	PR("Aggregates:      ", a_aggr);
 	PR("AMPDUs Queued HW:", a_queued_hw);
-	PR("AMPDUs Queued SW:", a_queued_sw);
 	PR("AMPDUs Completed:", a_completed);
 	PR("AMPDUs Retried:  ", a_retries);
 	PR("AMPDUs XRetried: ", a_xretries);
@@ -629,8 +628,7 @@ static void print_queue(struct ath_softc *sc, struct =
ath_txq *txq,
 	seq_printf(file, "%s: %d ", "qnum", txq->axq_qnum);
 	seq_printf(file, "%s: %2d ", "qdepth", txq->axq_depth);
 	seq_printf(file, "%s: %2d ", "ampdu-depth", txq->axq_ampdu_depth);
-	seq_printf(file, "%s: %3d ", "pending", txq->pending_frames);
-	seq_printf(file, "%s: %d\n", "stopped", txq->stopped);
+	seq_printf(file, "%s: %3d\n", "pending", txq->pending_frames);
=20
 	ath_txq_unlock(sc, txq);
 }
@@ -1208,7 +1206,6 @@ static const char ath9k_gstrings_stats[][ETH_GSTRIN=
G_LEN] =3D {
 	AMKSTR(d_tx_mpdu_xretries),
 	AMKSTR(d_tx_aggregates),
 	AMKSTR(d_tx_ampdus_queued_hw),
-	AMKSTR(d_tx_ampdus_queued_sw),
 	AMKSTR(d_tx_ampdus_completed),
 	AMKSTR(d_tx_ampdu_retries),
 	AMKSTR(d_tx_ampdu_xretries),
@@ -1288,7 +1285,6 @@ void ath9k_get_et_stats(struct ieee80211_hw *hw,
 	AWDATA(xretries);
 	AWDATA(a_aggr);
 	AWDATA(a_queued_hw);
-	AWDATA(a_queued_sw);
 	AWDATA(a_completed);
 	AWDATA(a_retries);
 	AWDATA(a_xretries);
@@ -1346,14 +1342,6 @@ int ath9k_init_debug(struct ath_hw *ah)
 				    read_file_xmit);
 	debugfs_create_devm_seqfile(sc->dev, "queues", sc->debug.debugfs_phy,
 				    read_file_queues);
-	debugfs_create_u32("qlen_bk", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
-			   &sc->tx.txq_max_pending[IEEE80211_AC_BK]);
-	debugfs_create_u32("qlen_be", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
-			   &sc->tx.txq_max_pending[IEEE80211_AC_BE]);
-	debugfs_create_u32("qlen_vi", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
-			   &sc->tx.txq_max_pending[IEEE80211_AC_VI]);
-	debugfs_create_u32("qlen_vo", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
-			   &sc->tx.txq_max_pending[IEEE80211_AC_VO]);
 	debugfs_create_devm_seqfile(sc->dev, "misc", sc->debug.debugfs_phy,
 				    read_file_misc);
 	debugfs_create_devm_seqfile(sc->dev, "reset", sc->debug.debugfs_phy,
diff --git a/drivers/net/wireless/ath/ath9k/debug.h b/drivers/net/wireles=
s/ath/ath9k/debug.h
index cd68c5f..a078cdd 100644
--- a/drivers/net/wireless/ath/ath9k/debug.h
+++ b/drivers/net/wireless/ath/ath9k/debug.h
@@ -147,7 +147,6 @@ struct ath_interrupt_stats {
  * @completed: Total MPDUs (non-aggr) completed
  * @a_aggr: Total no. of aggregates queued
  * @a_queued_hw: Total AMPDUs queued to hardware
- * @a_queued_sw: Total AMPDUs queued to software queues
  * @a_completed: Total AMPDUs completed
  * @a_retries: No. of AMPDUs retried (SW)
  * @a_xretries: No. of AMPDUs dropped due to xretries
@@ -174,7 +173,6 @@ struct ath_tx_stats {
 	u32 xretries;
 	u32 a_aggr;
 	u32 a_queued_hw;
-	u32 a_queued_sw;
 	u32 a_completed;
 	u32 a_retries;
 	u32 a_xretries;
diff --git a/drivers/net/wireless/ath/ath9k/debug_sta.c b/drivers/net/wir=
eless/ath/ath9k/debug_sta.c
index b66cfa9..2a3a3c4 100644
--- a/drivers/net/wireless/ath/ath9k/debug_sta.c
+++ b/drivers/net/wireless/ath/ath9k/debug_sta.c
@@ -52,8 +52,8 @@ static ssize_t read_file_node_aggr(struct file *file, c=
har __user *user_buf,
 			 "TID", "SEQ_START", "SEQ_NEXT", "BAW_SIZE",
 			 "BAW_HEAD", "BAW_TAIL", "BAR_IDX", "SCHED", "PAUSED");
=20
-	for (tidno =3D 0, tid =3D &an->tid[tidno];
-	     tidno < IEEE80211_NUM_TIDS; tidno++, tid++) {
+	for (tidno =3D 0; tidno < IEEE80211_NUM_TIDS; tidno++) {
+		tid =3D ath_node_to_tid(an, tidno);
 		txq =3D tid->txq;
 		ath_txq_lock(sc, txq);
 		if (tid->active) {
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless=
/ath/ath9k/init.c
index cfa3fe8..96bba17 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -358,7 +358,6 @@ static int ath9k_init_queues(struct ath_softc *sc)
 	for (i =3D 0; i < IEEE80211_NUM_ACS; i++) {
 		sc->tx.txq_map[i] =3D ath_txq_setup(sc, ATH9K_TX_QUEUE_DATA, i);
 		sc->tx.txq_map[i]->mac80211_qnum =3D i;
-		sc->tx.txq_max_pending[i] =3D ATH_MAX_QDEPTH;
 	}
 	return 0;
 }
@@ -877,6 +876,7 @@ static void ath9k_set_hw_capab(struct ath_softc *sc, =
struct ieee80211_hw *hw)
 	hw->max_rate_tries =3D 10;
 	hw->sta_data_size =3D sizeof(struct ath_node);
 	hw->vif_data_size =3D sizeof(struct ath_vif);
+	hw->txq_data_size =3D sizeof(struct ath_atx_tid);
 	hw->extra_tx_headroom =3D 4;
=20
 	hw->wiphy->available_antennas_rx =3D BIT(ah->caps.max_rxchains) - 1;
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless=
/ath/ath9k/main.c
index a394622..1dedf13 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -1896,9 +1896,11 @@ static int ath9k_ampdu_action(struct ieee80211_hw =
*hw,
 	bool flush =3D false;
 	int ret =3D 0;
 	struct ieee80211_sta *sta =3D params->sta;
+	struct ath_node *an =3D (struct ath_node *)sta->drv_priv;
 	enum ieee80211_ampdu_mlme_action action =3D params->action;
 	u16 tid =3D params->tid;
 	u16 *ssn =3D &params->ssn;
+	struct ath_atx_tid *atid;
=20
 	mutex_lock(&sc->mutex);
=20
@@ -1931,9 +1933,9 @@ static int ath9k_ampdu_action(struct ieee80211_hw *=
hw,
 		ath9k_ps_restore(sc);
 		break;
 	case IEEE80211_AMPDU_TX_OPERATIONAL:
-		ath9k_ps_wakeup(sc);
-		ath_tx_aggr_resume(sc, sta, tid);
-		ath9k_ps_restore(sc);
+		atid =3D ath_node_to_tid(an, tid);
+		atid->baw_size =3D IEEE80211_MIN_AMPDU_BUF <<
+			        sta->ht_cap.ampdu_factor;
 		break;
 	default:
 		ath_err(ath9k_hw_common(sc->sc_ah), "Unknown AMPDU action\n");
@@ -2695,4 +2697,5 @@ struct ieee80211_ops ath9k_ops =3D {
 	.sw_scan_start	    =3D ath9k_sw_scan_start,
 	.sw_scan_complete   =3D ath9k_sw_scan_complete,
 	.get_txpower        =3D ath9k_get_txpower,
+	.wake_tx_queue      =3D ath9k_wake_tx_queue,
 };
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless=
/ath/ath9k/xmit.c
index 8ddd604..c074747 100644
--- a/drivers/net/wireless/ath/ath9k/xmit.c
+++ b/drivers/net/wireless/ath/ath9k/xmit.c
@@ -65,6 +65,8 @@ static struct ath_buf *ath_tx_setup_buffer(struct ath_s=
oftc *sc,
 					   struct ath_txq *txq,
 					   struct ath_atx_tid *tid,
 					   struct sk_buff *skb);
+static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb,
+			  struct ath_tx_control *txctl);
=20
 enum {
 	MCS_HT20,
@@ -118,6 +120,26 @@ static void ath_tx_queue_tid(struct ath_softc *sc, s=
truct ath_txq *txq,
 		list_add_tail(&tid->list, list);
 }
=20
+void ath9k_wake_tx_queue(struct ieee80211_hw *hw, struct ieee80211_txq *=
queue)
+{
+	struct ath_softc *sc =3D hw->priv;
+	struct ath_common *common =3D ath9k_hw_common(sc->sc_ah);
+	struct ath_atx_tid *tid =3D (struct ath_atx_tid *) queue->drv_priv;
+	struct ath_txq *txq =3D tid->txq;
+
+	ath_dbg(common, QUEUE, "Waking TX queue: %pM (%d)\n",
+		queue->sta ? queue->sta->addr : queue->vif->addr,
+		tid->tidno);
+
+	ath_txq_lock(sc, txq);
+
+	tid->has_queued =3D true;
+	ath_tx_queue_tid(sc, txq, tid);
+	ath_txq_schedule(sc, txq);
+
+	ath_txq_unlock(sc, txq);
+}
+
 static struct ath_frame_info *get_frame_info(struct sk_buff *skb)
 {
 	struct ieee80211_tx_info *tx_info =3D IEEE80211_SKB_CB(skb);
@@ -145,7 +167,6 @@ static void ath_set_rates(struct ieee80211_vif *vif, =
struct ieee80211_sta *sta,
 static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq,
 			     struct sk_buff *skb)
 {
-	struct ieee80211_tx_info *info =3D IEEE80211_SKB_CB(skb);
 	struct ath_frame_info *fi =3D get_frame_info(skb);
 	int q =3D fi->txq;
=20
@@ -156,14 +177,6 @@ static void ath_txq_skb_done(struct ath_softc *sc, s=
truct ath_txq *txq,
 	if (WARN_ON(--txq->pending_frames < 0))
 		txq->pending_frames =3D 0;
=20
-	if (txq->stopped &&
-	    txq->pending_frames < sc->tx.txq_max_pending[q]) {
-		if (ath9k_is_chanctx_enabled())
-			ieee80211_wake_queue(sc->hw, info->hw_queue);
-		else
-			ieee80211_wake_queue(sc->hw, q);
-		txq->stopped =3D false;
-	}
 }
=20
 static struct ath_atx_tid *
@@ -173,9 +186,48 @@ ath_get_skb_tid(struct ath_softc *sc, struct ath_nod=
e *an, struct sk_buff *skb)
 	return ATH_AN_2_TID(an, tidno);
 }
=20
+static struct sk_buff *
+ath_tid_pull(struct ath_atx_tid *tid)
+{
+	struct ieee80211_txq *txq =3D container_of((void*)tid, struct ieee80211=
_txq, drv_priv);
+	struct ath_softc *sc =3D tid->an->sc;
+	struct ieee80211_hw *hw =3D sc->hw;
+	struct ath_tx_control txctl =3D {
+		.txq =3D tid->txq,
+		.sta =3D tid->an->sta,
+	};
+	struct sk_buff *skb;
+	struct ath_frame_info *fi;
+	int q;
+
+	if (!tid->has_queued)
+		return NULL;
+
+	skb =3D ieee80211_tx_dequeue(hw, txq);
+	if (!skb) {
+		tid->has_queued =3D false;
+		return NULL;
+	}
+
+	if (ath_tx_prepare(hw, skb, &txctl)) {
+		ieee80211_free_txskb(hw, skb);
+		return NULL;
+	}
+
+	q =3D skb_get_queue_mapping(skb);
+	if (tid->txq =3D=3D sc->tx.txq_map[q]) {
+		fi =3D get_frame_info(skb);
+		fi->txq =3D q;
+		++tid->txq->pending_frames;
+	}
+
+	return skb;
+ }
+
+
 static bool ath_tid_has_buffered(struct ath_atx_tid *tid)
 {
-	return !skb_queue_empty(&tid->buf_q) || !skb_queue_empty(&tid->retry_q)=
;
+	return !skb_queue_empty(&tid->retry_q) || tid->has_queued;
 }
=20
 static struct sk_buff *ath_tid_dequeue(struct ath_atx_tid *tid)
@@ -184,46 +236,11 @@ static struct sk_buff *ath_tid_dequeue(struct ath_a=
tx_tid *tid)
=20
 	skb =3D __skb_dequeue(&tid->retry_q);
 	if (!skb)
-		skb =3D __skb_dequeue(&tid->buf_q);
+		skb =3D ath_tid_pull(tid);
=20
 	return skb;
 }
=20
-/*
- * ath_tx_tid_change_state:
- * - clears a-mpdu flag of previous session
- * - force sequence number allocation to fix next BlockAck Window
- */
-static void
-ath_tx_tid_change_state(struct ath_softc *sc, struct ath_atx_tid *tid)
-{
-	struct ath_txq *txq =3D tid->txq;
-	struct ieee80211_tx_info *tx_info;
-	struct sk_buff *skb, *tskb;
-	struct ath_buf *bf;
-	struct ath_frame_info *fi;
-
-	skb_queue_walk_safe(&tid->buf_q, skb, tskb) {
-		fi =3D get_frame_info(skb);
-		bf =3D fi->bf;
-
-		tx_info =3D IEEE80211_SKB_CB(skb);
-		tx_info->flags &=3D ~IEEE80211_TX_CTL_AMPDU;
-
-		if (bf)
-			continue;
-
-		bf =3D ath_tx_setup_buffer(sc, txq, tid, skb);
-		if (!bf) {
-			__skb_unlink(skb, &tid->buf_q);
-			ath_txq_skb_done(sc, txq, skb);
-			ieee80211_free_txskb(sc->hw, skb);
-			continue;
-		}
-	}
-
-}
-
 static void ath_tx_flush_tid(struct ath_softc *sc, struct ath_atx_tid *t=
id)
 {
 	struct ath_txq *txq =3D tid->txq;
@@ -858,20 +875,16 @@ static int ath_compute_num_delims(struct ath_softc =
*sc, struct ath_atx_tid *tid,
=20
 static struct ath_buf *
 ath_tx_get_tid_subframe(struct ath_softc *sc, struct ath_txq *txq,
-			struct ath_atx_tid *tid, struct sk_buff_head **q)
+			struct ath_atx_tid *tid)
 {
 	struct ieee80211_tx_info *tx_info;
 	struct ath_frame_info *fi;
-	struct sk_buff *skb;
+	struct sk_buff *skb, *first_skb =3D NULL;
 	struct ath_buf *bf;
 	u16 seqno;
=20
 	while (1) {
-		*q =3D &tid->retry_q;
-		if (skb_queue_empty(*q))
-			*q =3D &tid->buf_q;
-
-		skb =3D skb_peek(*q);
+		skb =3D ath_tid_dequeue(tid);
 		if (!skb)
 			break;
=20
@@ -883,7 +896,6 @@ ath_tx_get_tid_subframe(struct ath_softc *sc, struct =
ath_txq *txq,
 			bf->bf_state.stale =3D false;
=20
 		if (!bf) {
-			__skb_unlink(skb, *q);
 			ath_txq_skb_done(sc, txq, skb);
 			ieee80211_free_txskb(sc->hw, skb);
 			continue;
@@ -912,8 +924,20 @@ ath_tx_get_tid_subframe(struct ath_softc *sc, struct=
 ath_txq *txq,
 		seqno =3D bf->bf_state.seqno;
=20
 		/* do not step over block-ack window */
-		if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno))
+		if (!BAW_WITHIN(tid->seq_start, tid->baw_size, seqno)) {
+			__skb_queue_tail(&tid->retry_q, skb);
+
+			/* If there are other skbs in the retry q, they are
+			 * probably within the BAW, so loop immediately to get
+			 * one of them. Otherwise the queue can get stuck. */
+			if (!skb_queue_is_first(&tid->retry_q, skb) &&
+			    !WARN_ON(skb =3D=3D first_skb)) {
+				if(!first_skb) /* infinite loop prevention */
+					first_skb =3D skb;
+				continue;
+			}
 			break;
+		}
=20
 		if (tid->bar_index > ATH_BA_INDEX(tid->seq_start, seqno)) {
 			struct ath_tx_status ts =3D {};
@@ -921,7 +945,6 @@ ath_tx_get_tid_subframe(struct ath_softc *sc, struct =
ath_txq *txq,
=20
 			INIT_LIST_HEAD(&bf_head);
 			list_add(&bf->list, &bf_head);
-			__skb_unlink(skb, *q);
 			ath_tx_update_baw(sc, tid, seqno);
 			ath_tx_complete_buf(sc, bf, txq, &bf_head, &ts, 0);
 			continue;
@@ -933,11 +956,10 @@ ath_tx_get_tid_subframe(struct ath_softc *sc, struc=
t ath_txq *txq,
 	return NULL;
 }
=20
-static bool
+static int
 ath_tx_form_aggr(struct ath_softc *sc, struct ath_txq *txq,
 		 struct ath_atx_tid *tid, struct list_head *bf_q,
-		 struct ath_buf *bf_first, struct sk_buff_head *tid_q,
-		 int *aggr_len)
+		 struct ath_buf *bf_first)
 {
 #define PADBYTES(_len) ((4 - ((_len) % 4)) % 4)
 	struct ath_buf *bf =3D bf_first, *bf_prev =3D NULL;
@@ -947,12 +969,13 @@ ath_tx_form_aggr(struct ath_softc *sc, struct ath_t=
xq *txq,
 	struct ieee80211_tx_info *tx_info;
 	struct ath_frame_info *fi;
 	struct sk_buff *skb;
-	bool closed =3D false;
+
=20
 	bf =3D bf_first;
 	aggr_limit =3D ath_lookup_rate(sc, bf, tid);
=20
-	do {
+	while (bf)
+	{
 		skb =3D bf->bf_mpdu;
 		fi =3D get_frame_info(skb);
=20
@@ -961,12 +984,12 @@ ath_tx_form_aggr(struct ath_softc *sc, struct ath_t=
xq *txq,
 		if (nframes) {
 			if (aggr_limit < al + bpad + al_delta ||
 			    ath_lookup_legacy(bf) || nframes >=3D h_baw)
-				break;
+				goto stop;
=20
 			tx_info =3D IEEE80211_SKB_CB(bf->bf_mpdu);
 			if ((tx_info->flags & IEEE80211_TX_CTL_RATE_CTRL_PROBE) ||
 			    !(tx_info->flags & IEEE80211_TX_CTL_AMPDU))
-				break;
+				goto stop;
 		}
=20
 		/* add padding for previous frame to aggregation length */
@@ -988,20 +1011,18 @@ ath_tx_form_aggr(struct ath_softc *sc, struct ath_=
txq *txq,
 			ath_tx_addto_baw(sc, tid, bf);
 		bf->bf_state.ndelim =3D ndelim;
=20
-		__skb_unlink(skb, tid_q);
 		list_add_tail(&bf->list, bf_q);
 		if (bf_prev)
 			bf_prev->bf_next =3D bf;
=20
 		bf_prev =3D bf;
=20
-		bf =3D ath_tx_get_tid_subframe(sc, txq, tid, &tid_q);
-		if (!bf) {
-			closed =3D true;
-			break;
-		}
-	} while (ath_tid_has_buffered(tid));
-
+		bf =3D ath_tx_get_tid_subframe(sc, txq, tid);
+	}
+	goto finish;
+stop:
+	__skb_queue_tail(&tid->retry_q, bf->bf_mpdu);
+finish:
 	bf =3D bf_first;
 	bf->bf_lastbf =3D bf_prev;
=20
@@ -1012,9 +1033,7 @@ ath_tx_form_aggr(struct ath_softc *sc, struct ath_t=
xq *txq,
 		TX_STAT_INC(txq->axq_qnum, a_aggr);
 	}
=20
-	*aggr_len =3D al;
-
-	return closed;
+	return al;
 #undef PADBYTES
 }
=20
@@ -1391,18 +1410,15 @@ static void ath_tx_fill_desc(struct ath_softc *sc=
, struct ath_buf *bf,
 static void
 ath_tx_form_burst(struct ath_softc *sc, struct ath_txq *txq,
 		  struct ath_atx_tid *tid, struct list_head *bf_q,
-		  struct ath_buf *bf_first, struct sk_buff_head *tid_q)
+		  struct ath_buf *bf_first)
 {
 	struct ath_buf *bf =3D bf_first, *bf_prev =3D NULL;
-	struct sk_buff *skb;
 	int nframes =3D 0;
=20
 	do {
 		struct ieee80211_tx_info *tx_info;
-		skb =3D bf->bf_mpdu;
=20
 		nframes++;
-		__skb_unlink(skb, tid_q);
 		list_add_tail(&bf->list, bf_q);
 		if (bf_prev)
 			bf_prev->bf_next =3D bf;
@@ -1411,13 +1427,15 @@ ath_tx_form_burst(struct ath_softc *sc, struct at=
h_txq *txq,
 		if (nframes >=3D 2)
 			break;
=20
-		bf =3D ath_tx_get_tid_subframe(sc, txq, tid, &tid_q);
+		bf =3D ath_tx_get_tid_subframe(sc, txq, tid);
 		if (!bf)
 			break;
=20
 		tx_info =3D IEEE80211_SKB_CB(bf->bf_mpdu);
-		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU)
+		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
+			__skb_queue_tail(&tid->retry_q, bf->bf_mpdu);
 			break;
+		}
=20
 		ath_set_rates(tid->an->vif, tid->an->sta, bf);
 	} while (1);
@@ -1428,34 +1446,33 @@ static bool ath_tx_sched_aggr(struct ath_softc *s=
c, struct ath_txq *txq,
 {
 	struct ath_buf *bf;
 	struct ieee80211_tx_info *tx_info;
-	struct sk_buff_head *tid_q;
 	struct list_head bf_q;
 	int aggr_len =3D 0;
-	bool aggr, last =3D true;
+	bool aggr;
=20
 	if (!ath_tid_has_buffered(tid))
 		return false;
=20
 	INIT_LIST_HEAD(&bf_q);
=20
-	bf =3D ath_tx_get_tid_subframe(sc, txq, tid, &tid_q);
+	bf =3D ath_tx_get_tid_subframe(sc, txq, tid);
 	if (!bf)
 		return false;
=20
 	tx_info =3D IEEE80211_SKB_CB(bf->bf_mpdu);
 	aggr =3D !!(tx_info->flags & IEEE80211_TX_CTL_AMPDU);
 	if ((aggr && txq->axq_ampdu_depth >=3D ATH_AGGR_MIN_QDEPTH) ||
-		(!aggr && txq->axq_depth >=3D ATH_NON_AGGR_MIN_QDEPTH)) {
+	    (!aggr && txq->axq_depth >=3D ATH_NON_AGGR_MIN_QDEPTH)) {
+		__skb_queue_tail(&tid->retry_q, bf->bf_mpdu);
 		*stop =3D true;
 		return false;
 	}
=20
 	ath_set_rates(tid->an->vif, tid->an->sta, bf);
 	if (aggr)
-		last =3D ath_tx_form_aggr(sc, txq, tid, &bf_q, bf,
-					tid_q, &aggr_len);
+		aggr_len =3D ath_tx_form_aggr(sc, txq, tid, &bf_q, bf);
 	else
-		ath_tx_form_burst(sc, txq, tid, &bf_q, bf, tid_q);
+		ath_tx_form_burst(sc, txq, tid, &bf_q, bf);
=20
 	if (list_empty(&bf_q))
 		return false;
@@ -1498,9 +1515,6 @@ int ath_tx_aggr_start(struct ath_softc *sc, struct =
ieee80211_sta *sta,
 		an->mpdudensity =3D density;
 	}
=20
-	/* force sequence number allocation for pending frames */
-	ath_tx_tid_change_state(sc, txtid);
-
 	txtid->active =3D true;
 	*ssn =3D txtid->seq_start =3D txtid->seq_next;
 	txtid->bar_index =3D -1;
@@ -1525,7 +1539,6 @@ void ath_tx_aggr_stop(struct ath_softc *sc, struct =
ieee80211_sta *sta, u16 tid)
 	ath_txq_lock(sc, txq);
 	txtid->active =3D false;
 	ath_tx_flush_tid(sc, txtid);
-	ath_tx_tid_change_state(sc, txtid);
 	ath_txq_unlock_complete(sc, txq);
 }
=20
@@ -1535,14 +1548,12 @@ void ath_tx_aggr_sleep(struct ieee80211_sta *sta,=
 struct ath_softc *sc,
 	struct ath_common *common =3D ath9k_hw_common(sc->sc_ah);
 	struct ath_atx_tid *tid;
 	struct ath_txq *txq;
-	bool buffered;
 	int tidno;
=20
 	ath_dbg(common, XMIT, "%s called\n", __func__);
=20
-	for (tidno =3D 0, tid =3D &an->tid[tidno];
-	     tidno < IEEE80211_NUM_TIDS; tidno++, tid++) {
-
+	for (tidno =3D 0; tidno < IEEE80211_NUM_TIDS; tidno++) {
+		tid =3D ath_node_to_tid(an, tidno);
 		txq =3D tid->txq;
=20
 		ath_txq_lock(sc, txq);
@@ -1552,13 +1563,12 @@ void ath_tx_aggr_sleep(struct ieee80211_sta *sta,=
 struct ath_softc *sc,
 			continue;
 		}
=20
-		buffered =3D ath_tid_has_buffered(tid);
+		if (!skb_queue_empty(&tid->retry_q))
+			ieee80211_sta_set_buffered(sta, tid->tidno, true);
=20
 		list_del_init(&tid->list);
=20
 		ath_txq_unlock(sc, txq);
-
-		ieee80211_sta_set_buffered(sta, tidno, buffered);
 	}
 }
=20
@@ -1571,49 +1581,20 @@ void ath_tx_aggr_wakeup(struct ath_softc *sc, str=
uct ath_node *an)
=20
 	ath_dbg(common, XMIT, "%s called\n", __func__);
=20
-	for (tidno =3D 0, tid =3D &an->tid[tidno];
-	     tidno < IEEE80211_NUM_TIDS; tidno++, tid++) {
-
+	for (tidno =3D 0; tidno < IEEE80211_NUM_TIDS; tidno++) {
+		tid =3D ath_node_to_tid(an, tidno);
 		txq =3D tid->txq;
=20
 		ath_txq_lock(sc, txq);
 		tid->clear_ps_filter =3D true;
-
 		if (ath_tid_has_buffered(tid)) {
 			ath_tx_queue_tid(sc, txq, tid);
 			ath_txq_schedule(sc, txq);
 		}
-
 		ath_txq_unlock_complete(sc, txq);
 	}
 }
=20
-void ath_tx_aggr_resume(struct ath_softc *sc, struct ieee80211_sta *sta,
-			u16 tidno)
-{
-	struct ath_common *common =3D ath9k_hw_common(sc->sc_ah);
-	struct ath_atx_tid *tid;
-	struct ath_node *an;
-	struct ath_txq *txq;
-
-	ath_dbg(common, XMIT, "%s called\n", __func__);
-
-	an =3D (struct ath_node *)sta->drv_priv;
-	tid =3D ATH_AN_2_TID(an, tidno);
-	txq =3D tid->txq;
-
-	ath_txq_lock(sc, txq);
-
-	tid->baw_size =3D IEEE80211_MIN_AMPDU_BUF << sta->ht_cap.ampdu_factor;
-
-	if (ath_tid_has_buffered(tid)) {
-		ath_tx_queue_tid(sc, txq, tid);
-		ath_txq_schedule(sc, txq);
-	}
-
-	ath_txq_unlock_complete(sc, txq);
-}
-
 void ath9k_release_buffered_frames(struct ieee80211_hw *hw,
 				   struct ieee80211_sta *sta,
 				   u16 tids, int nframes,
@@ -1626,7 +1607,6 @@ void ath9k_release_buffered_frames(struct ieee80211=
_hw *hw,
 	struct ieee80211_tx_info *info;
 	struct list_head bf_q;
 	struct ath_buf *bf_tail =3D NULL, *bf;
-	struct sk_buff_head *tid_q;
 	int sent =3D 0;
 	int i;
=20
@@ -1641,11 +1621,10 @@ void ath9k_release_buffered_frames(struct ieee802=
11_hw *hw,
=20
 		ath_txq_lock(sc, tid->txq);
 		while (nframes > 0) {
-			bf =3D ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid, &tid_q);
+			bf =3D ath_tx_get_tid_subframe(sc, sc->tx.uapsdq, tid);
 			if (!bf)
 				break;
=20
-			__skb_unlink(bf->bf_mpdu, tid_q);
 			list_add_tail(&bf->list, &bf_q);
 			ath_set_rates(tid->an->vif, tid->an->sta, bf);
 			if (bf_isampdu(bf)) {
@@ -1660,7 +1639,7 @@ void ath9k_release_buffered_frames(struct ieee80211=
_hw *hw,
 			sent++;
 			TX_STAT_INC(txq->axq_qnum, a_queued_hw);
=20
-			if (an->sta && !ath_tid_has_buffered(tid))
+			if (an->sta && skb_queue_empty(&tid->retry_q))
 				ieee80211_sta_set_buffered(an->sta, i, false);
 		}
 		ath_txq_unlock_complete(sc, tid->txq);
@@ -1887,13 +1866,7 @@ bool ath_drain_all_txq(struct ath_softc *sc)
 		if (!ATH_TXQ_SETUP(sc, i))
 			continue;
=20
-		/*
-		 * The caller will resume queues with ieee80211_wake_queues.
-		 * Mark the queue as not stopped to prevent ath_tx_complete
-		 * from waking the queue too early.
-		 */
 		txq =3D &sc->tx.txq[i];
-		txq->stopped =3D false;
 		ath_draintxq(sc, txq);
 	}
=20
@@ -2292,16 +2265,14 @@ int ath_tx_start(struct ieee80211_hw *hw, struct =
sk_buff *skb,
 	struct ath_softc *sc =3D hw->priv;
 	struct ath_txq *txq =3D txctl->txq;
 	struct ath_atx_tid *tid =3D NULL;
+	struct ath_node *an =3D NULL;
 	struct ath_buf *bf;
-	bool queue, skip_uapsd =3D false, ps_resp;
+	bool ps_resp;
 	int q, ret;
=20
 	if (vif)
 		avp =3D (void *)vif->drv_priv;
=20
-	if (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)
-		txctl->force_channel =3D true;
-
 	ps_resp =3D !!(info->control.flags & IEEE80211_TX_CTRL_PS_RESPONSE);
=20
 	ret =3D ath_tx_prepare(hw, skb, txctl);
@@ -2316,63 +2287,18 @@ int ath_tx_start(struct ieee80211_hw *hw, struct =
sk_buff *skb,
=20
 	q =3D skb_get_queue_mapping(skb);
=20
-	ath_txq_lock(sc, txq);
-	if (txq =3D=3D sc->tx.txq_map[q]) {
-		fi->txq =3D q;
-		if (++txq->pending_frames > sc->tx.txq_max_pending[q] &&
-		    !txq->stopped) {
-			if (ath9k_is_chanctx_enabled())
-				ieee80211_stop_queue(sc->hw, info->hw_queue);
-			else
-				ieee80211_stop_queue(sc->hw, q);
-			txq->stopped =3D true;
-		}
-	}
-
-	queue =3D ieee80211_is_data_present(hdr->frame_control);
-
-	/* If chanctx, queue all null frames while NOA could be there */
-	if (ath9k_is_chanctx_enabled() &&
-	    ieee80211_is_nullfunc(hdr->frame_control) &&
-	    !txctl->force_channel)
-		queue =3D true;
-
-	/* Force queueing of all frames that belong to a virtual interface on
-	 * a different channel context, to ensure that they are sent on the
-	 * correct channel.
-	 */
-	if (((avp && avp->chanctx !=3D sc->cur_chan) ||
-	     sc->cur_chan->stopped) && !txctl->force_channel) {
-		if (!txctl->an)
-			txctl->an =3D &avp->mcast_node;
-		queue =3D true;
-		skip_uapsd =3D true;
-	}
-
-	if (txctl->an && queue)
-		tid =3D ath_get_skb_tid(sc, txctl->an, skb);
-
-	if (!skip_uapsd && ps_resp) {
-		ath_txq_unlock(sc, txq);
+	if (ps_resp)
 		txq =3D sc->tx.uapsdq;
-		ath_txq_lock(sc, txq);
-	} else if (txctl->an && queue) {
-		WARN_ON(tid->txq !=3D txctl->txq);
=20
-		if (info->flags & IEEE80211_TX_CTL_CLEAR_PS_FILT)
-			tid->clear_ps_filter =3D true;
-
-		/*
-		 * Add this frame to software queue for scheduling later
-		 * for aggregation.
-		 */
-		TX_STAT_INC(txq->axq_qnum, a_queued_sw);
-		__skb_queue_tail(&tid->buf_q, skb);
-		if (!txctl->an->sleeping)
-			ath_tx_queue_tid(sc, txq, tid);
+	if (txctl->sta) {
+		an =3D (struct ath_node *) sta->drv_priv;
+		tid =3D ath_get_skb_tid(sc, an, skb);
+	}
=20
-		ath_txq_schedule(sc, txq);
-		goto out;
+	ath_txq_lock(sc, txq);
+	if (txq =3D=3D sc->tx.txq_map[q]) {
+		fi->txq =3D q;
+		++txq->pending_frames;
 	}
=20
 	bf =3D ath_tx_setup_buffer(sc, txq, tid, skb);
@@ -2856,9 +2782,8 @@ void ath_tx_node_init(struct ath_softc *sc, struct =
ath_node *an)
 	struct ath_atx_tid *tid;
 	int tidno, acno;
=20
-	for (tidno =3D 0, tid =3D &an->tid[tidno];
-	     tidno < IEEE80211_NUM_TIDS;
-	     tidno++, tid++) {
+	for (tidno =3D 0; tidno < IEEE80211_NUM_TIDS; tidno++) {
+		tid =3D ath_node_to_tid(an, tidno);
 		tid->an        =3D an;
 		tid->tidno     =3D tidno;
 		tid->seq_start =3D tid->seq_next =3D 0;
@@ -2866,11 +2791,14 @@ void ath_tx_node_init(struct ath_softc *sc, struc=
t ath_node *an)
 		tid->baw_head  =3D tid->baw_tail =3D 0;
 		tid->active	   =3D false;
 		tid->clear_ps_filter =3D true;
-		__skb_queue_head_init(&tid->buf_q);
+		tid->has_queued  =3D false;
 		__skb_queue_head_init(&tid->retry_q);
 		INIT_LIST_HEAD(&tid->list);
 		acno =3D TID_TO_WME_AC(tidno);
 		tid->txq =3D sc->tx.txq_map[acno];
+
+		if (!an->sta)
+			break; /* just one multicast ath_atx_tid */
 	}
 }
=20
@@ -2880,9 +2808,8 @@ void ath_tx_node_cleanup(struct ath_softc *sc, stru=
ct ath_node *an)
 	struct ath_txq *txq;
 	int tidno;
=20
-	for (tidno =3D 0, tid =3D &an->tid[tidno];
-	     tidno < IEEE80211_NUM_TIDS; tidno++, tid++) {
-
+	for (tidno =3D 0; tidno < IEEE80211_NUM_TIDS; tidno++) {
+		tid =3D ath_node_to_tid(an, tidno);
 		txq =3D tid->txq;
=20
 		ath_txq_lock(sc, txq);
@@ -2894,6 +2821,9 @@ void ath_tx_node_cleanup(struct ath_softc *sc, stru=
ct ath_node *an)
 		tid->active =3D false;
=20
 		ath_txq_unlock(sc, txq);
+
+		if (!an->sta)
+			break; /* just one multicast ath_atx_tid */
 	}
 }
=20
--=20
2.9.3

^ permalink raw reply related

* [PATCH v6] mac80211: Move reorder-sensitive TX handlers to after TXQ dequeue.
From: Toke Høiland-Jørgensen @ 2016-09-02 13:41 UTC (permalink / raw)
  To: make-wifi-fast, linux-wireless; +Cc: Toke Høiland-Jørgensen
In-Reply-To: <20160901160312.31540-1-toke@toke.dk>

The TXQ intermediate queues can cause packet reordering when more than
one flow is active to a single station. Since some of the wifi-specific
packet handling (notably sequence number and encryption handling) is
sensitive to re-ordering, things break if they are applied before the
TXQ.

This splits up the TX handlers and fast_xmit logic into two parts: An
early part and a late part. The former is applied before TXQ enqueue,
and the latter after dequeue. The non-TXQ path just applies both parts
at once.

Because fragments shouldn't be split up or reordered, the fragmentation
handler is run after dequeue. Any fragments are then kept in the TXQ and
on subsequent dequeues they take precedence over dequeueing from the FQ
structure.

This approach avoids having to scatter special cases for when TXQ is
enabled, at the cost of making the fast_xmit and TX handler code
slightly more complex.

Signed-off-by: Toke H=C3=B8iland-J=C3=B8rgensen <toke@toke.dk>
---
Changes since v5:
- Move the fragmentation handler to *after* TXQ dequeue. Fragments are
  kept in the TXQ for subsequent dequeues. This change also means that
  the changes to make some of the handlers fragmentation aware are no
  longer necessary.
- One of the TX stats updates in the fast path was done before the
  enqueue step; move that to xmit_fast_finish().
- Move the rate selection handler to after dequeue, so it's run closer
  to the time where the packet is actually transmitted.
 =20
 include/net/mac80211.h     |   2 +
 net/mac80211/ieee80211_i.h |   2 +
 net/mac80211/tx.c          | 207 +++++++++++++++++++++++++++++++++++----=
------
 3 files changed, 168 insertions(+), 43 deletions(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index cca510a..9a6a3e9 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -715,6 +715,7 @@ enum mac80211_tx_info_flags {
  *	frame (PS-Poll or uAPSD).
  * @IEEE80211_TX_CTRL_RATE_INJECT: This frame is injected with rate info=
rmation
  * @IEEE80211_TX_CTRL_AMSDU: This frame is an A-MSDU frame
+ * @IEEE80211_TX_CTRL_FAST_XMIT: This frame is going through the fast_xm=
it path
  *
  * These flags are used in tx_info->control.flags.
  */
@@ -723,6 +724,7 @@ enum mac80211_tx_control_flags {
 	IEEE80211_TX_CTRL_PS_RESPONSE		=3D BIT(1),
 	IEEE80211_TX_CTRL_RATE_INJECT		=3D BIT(2),
 	IEEE80211_TX_CTRL_AMSDU			=3D BIT(3),
+	IEEE80211_TX_CTRL_FAST_XMIT		=3D BIT(4),
 };
=20
 /*
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index f56d342..de9991d 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -813,11 +813,13 @@ enum txq_info_flags {
  * @def_flow: used as a fallback flow when a packet destined to @tin has=
hes to
  *	a fq_flow which is already owned by a different tin
  * @def_cvars: codel vars for @def_flow
+ * @frags: used to keep fragments created after dequeue
  */
 struct txq_info {
 	struct fq_tin tin;
 	struct fq_flow def_flow;
 	struct codel_vars def_cvars;
+	struct sk_buff_head frags;
 	unsigned long flags;
=20
 	/* keep last! */
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 1d0746d..a3a4593 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -38,6 +38,12 @@
 #include "wme.h"
 #include "rate.h"
=20
+static int invoke_tx_handlers_late(struct ieee80211_tx_data *tx);
+static bool ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sda=
ta,
+				       struct sta_info *sta,
+				       struct ieee80211_fast_tx *fast_tx,
+				       struct sk_buff *skb, bool xmit);
+
 /* misc utils */
=20
 static inline void ieee80211_tx_stats(struct net_device *dev, u32 len)
@@ -1403,6 +1409,7 @@ void ieee80211_txq_init(struct ieee80211_sub_if_dat=
a *sdata,
 	fq_tin_init(&txqi->tin);
 	fq_flow_init(&txqi->def_flow);
 	codel_vars_init(&txqi->def_cvars);
+	__skb_queue_head_init(&txqi->frags);
=20
 	txqi->txq.vif =3D &sdata->vif;
=20
@@ -1425,6 +1432,7 @@ void ieee80211_txq_purge(struct ieee80211_local *lo=
cal,
 	struct fq_tin *tin =3D &txqi->tin;
=20
 	fq_tin_reset(fq, tin, fq_skb_free_func);
+	ieee80211_purge_tx_queue(&local->hw, &txqi->frags);
 }
=20
 int ieee80211_txq_setup_flows(struct ieee80211_local *local)
@@ -1481,33 +1489,62 @@ struct sk_buff *ieee80211_tx_dequeue(struct ieee8=
0211_hw *hw,
 {
 	struct ieee80211_local *local =3D hw_to_local(hw);
 	struct txq_info *txqi =3D container_of(txq, struct txq_info, txq);
-	struct ieee80211_hdr *hdr;
 	struct sk_buff *skb =3D NULL;
 	struct fq *fq =3D &local->fq;
 	struct fq_tin *tin =3D &txqi->tin;
+	struct ieee80211_tx_info *info;
=20
 	spin_lock_bh(&fq->lock);
=20
 	if (test_bit(IEEE80211_TXQ_STOP, &txqi->flags))
 		goto out;
=20
+	/* Make sure fragments stay together. */
+	skb =3D __skb_dequeue(&txqi->frags);
+	if (skb)
+		goto out;
+
+begin:
 	skb =3D fq_tin_dequeue(fq, tin, fq_tin_dequeue_func);
 	if (!skb)
 		goto out;
=20
 	ieee80211_set_skb_vif(skb, txqi);
=20
-	hdr =3D (struct ieee80211_hdr *)skb->data;
-	if (txq->sta && ieee80211_is_data_qos(hdr->frame_control)) {
+	info =3D IEEE80211_SKB_CB(skb);
+	if (txq->sta && info->control.flags & IEEE80211_TX_CTRL_FAST_XMIT) {
 		struct sta_info *sta =3D container_of(txq->sta, struct sta_info,
 						    sta);
-		struct ieee80211_tx_info *info =3D IEEE80211_SKB_CB(skb);
+		struct ieee80211_fast_tx *fast_tx;
=20
-		hdr->seq_ctrl =3D ieee80211_tx_next_seq(sta, txq->tid);
-		if (test_bit(IEEE80211_TXQ_AMPDU, &txqi->flags))
-			info->flags |=3D IEEE80211_TX_CTL_AMPDU;
-		else
-			info->flags &=3D ~IEEE80211_TX_CTL_AMPDU;
+		fast_tx =3D rcu_dereference(sta->fast_tx);
+		if (WARN_ON(!fast_tx)) {
+			/* lost the fast_tx pointer while the packet was queued */
+			ieee80211_free_txskb(hw, skb);
+			goto begin;
+		}
+		ieee80211_xmit_fast_finish(sta->sdata, sta, fast_tx, skb, false);
+	} else {
+		struct ieee80211_tx_data tx =3D { };
+
+		__skb_queue_head_init(&tx.skbs);
+		tx.local =3D local;
+		tx.skb =3D skb;
+		if (txq->sta) {
+			tx.sta =3D container_of(txq->sta, struct sta_info, sta);
+			tx.sdata =3D tx.sta->sdata;
+		} else {
+			tx.sdata =3D vif_to_sdata(info->control.vif);
+		}
+
+
+		if (invoke_tx_handlers_late(&tx))
+			goto begin;
+
+		skb =3D __skb_dequeue(&tx.skbs);
+
+		if (!skb_queue_empty(&tx.skbs))
+			skb_queue_splice_tail(&tx.skbs, &txqi->frags);
 	}
=20
 out:
@@ -1521,6 +1558,47 @@ out:
 }
 EXPORT_SYMBOL(ieee80211_tx_dequeue);
=20
+static bool ieee80211_queue_skb(struct ieee80211_local *local,
+				struct ieee80211_sub_if_data *sdata,
+				struct sta_info *sta,
+				struct sk_buff *skb)
+{
+	struct ieee80211_tx_info *info =3D IEEE80211_SKB_CB(skb);
+	struct fq *fq =3D &local->fq;
+	struct ieee80211_vif *vif;
+	struct txq_info *txqi;
+	struct ieee80211_sta *pubsta;
+
+	if (!local->ops->wake_tx_queue ||
+	    sdata->vif.type =3D=3D NL80211_IFTYPE_MONITOR)
+		return false;
+
+	if (sta && sta->uploaded)
+		pubsta =3D &sta->sta;
+	else
+		pubsta =3D NULL;
+
+	if (sdata->vif.type =3D=3D NL80211_IFTYPE_AP_VLAN)
+		sdata =3D container_of(sdata->bss,
+				     struct ieee80211_sub_if_data, u.ap);
+
+	vif =3D &sdata->vif;
+	txqi =3D ieee80211_get_txq(local, vif, pubsta, skb);
+
+	if (!txqi)
+		return false;
+
+	info->control.vif =3D vif;
+
+	spin_lock_bh(&fq->lock);
+	ieee80211_txq_enqueue(local, txqi, skb);
+	spin_unlock_bh(&fq->lock);
+
+	drv_wake_tx_queue(local, txqi);
+
+	return true;
+}
+
 static bool ieee80211_tx_frags(struct ieee80211_local *local,
 			       struct ieee80211_vif *vif,
 			       struct ieee80211_sta *sta,
@@ -1528,9 +1606,7 @@ static bool ieee80211_tx_frags(struct ieee80211_loc=
al *local,
 			       bool txpending)
 {
 	struct ieee80211_tx_control control =3D {};
-	struct fq *fq =3D &local->fq;
 	struct sk_buff *skb, *tmp;
-	struct txq_info *txqi;
 	unsigned long flags;
=20
 	skb_queue_walk_safe(skbs, skb, tmp) {
@@ -1545,21 +1621,6 @@ static bool ieee80211_tx_frags(struct ieee80211_lo=
cal *local,
 		}
 #endif
=20
-		txqi =3D ieee80211_get_txq(local, vif, sta, skb);
-		if (txqi) {
-			info->control.vif =3D vif;
-
-			__skb_unlink(skb, skbs);
-
-			spin_lock_bh(&fq->lock);
-			ieee80211_txq_enqueue(local, txqi, skb);
-			spin_unlock_bh(&fq->lock);
-
-			drv_wake_tx_queue(local, txqi);
-
-			continue;
-		}
-
 		spin_lock_irqsave(&local->queue_stop_reason_lock, flags);
 		if (local->queue_stop_reasons[q] ||
 		    (!txpending && !skb_queue_empty(&local->pending[q]))) {
@@ -1680,10 +1741,13 @@ static bool __ieee80211_tx(struct ieee80211_local=
 *local,
 /*
  * Invoke TX handlers, return 0 on success and non-zero if the
  * frame was dropped or queued.
+ *
+ * The handlers are split into an early and late part. The latter is eve=
rything
+ * that can be sensitive to reordering, and will be deferred to after pa=
ckets
+ * are dequeued from the intermediate queues (when they are enabled).
  */
-static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
+static int invoke_tx_handlers_early(struct ieee80211_tx_data *tx)
 {
-	struct ieee80211_tx_info *info =3D IEEE80211_SKB_CB(tx->skb);
 	ieee80211_tx_result res =3D TX_DROP;
=20
 #define CALL_TXH(txh) \
@@ -1697,7 +1761,28 @@ static int invoke_tx_handlers(struct ieee80211_tx_=
data *tx)
 	CALL_TXH(ieee80211_tx_h_check_assoc);
 	CALL_TXH(ieee80211_tx_h_ps_buf);
 	CALL_TXH(ieee80211_tx_h_check_control_port_protocol);
-	CALL_TXH(ieee80211_tx_h_select_key);
+
+ txh_done:
+	if (unlikely(res =3D=3D TX_DROP)) {
+		I802_DEBUG_INC(tx->local->tx_handlers_drop);
+		if (tx->skb)
+			ieee80211_free_txskb(&tx->local->hw, tx->skb);
+		else
+			ieee80211_purge_tx_queue(&tx->local->hw, &tx->skbs);
+		return -1;
+	} else if (unlikely(res =3D=3D TX_QUEUED)) {
+		I802_DEBUG_INC(tx->local->tx_handlers_queued);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int invoke_tx_handlers_late(struct ieee80211_tx_data *tx)
+{
+	struct ieee80211_tx_info *info =3D IEEE80211_SKB_CB(tx->skb);
+	ieee80211_tx_result res =3D TX_DROP;
+
 	if (!ieee80211_hw_check(&tx->local->hw, HAS_RATE_CONTROL))
 		CALL_TXH(ieee80211_tx_h_rate_ctrl);
=20
@@ -1707,6 +1792,7 @@ static int invoke_tx_handlers(struct ieee80211_tx_d=
ata *tx)
 		goto txh_done;
 	}
=20
+	CALL_TXH(ieee80211_tx_h_select_key);
 	CALL_TXH(ieee80211_tx_h_michael_mic_add);
 	CALL_TXH(ieee80211_tx_h_sequence);
 	CALL_TXH(ieee80211_tx_h_fragment);
@@ -1733,6 +1819,15 @@ static int invoke_tx_handlers(struct ieee80211_tx_=
data *tx)
 	return 0;
 }
=20
+static int invoke_tx_handlers(struct ieee80211_tx_data *tx)
+{
+	int r =3D invoke_tx_handlers_early(tx);
+	if (r)
+		return r;
+
+	return invoke_tx_handlers_late(tx);
+}
+
 bool ieee80211_tx_prepare_skb(struct ieee80211_hw *hw,
 			      struct ieee80211_vif *vif, struct sk_buff *skb,
 			      int band, struct ieee80211_sta **sta)
@@ -1807,7 +1902,13 @@ static bool ieee80211_tx(struct ieee80211_sub_if_d=
ata *sdata,
 		info->hw_queue =3D
 			sdata->vif.hw_queue[skb_get_queue_mapping(skb)];
=20
-	if (!invoke_tx_handlers(&tx))
+	if (invoke_tx_handlers_early(&tx))
+		return false;
+
+	if (ieee80211_queue_skb(local, sdata, tx.sta, tx.skb))
+		return true;
+
+	if (!invoke_tx_handlers_late(&tx))
 		result =3D __ieee80211_tx(local, &tx.skbs, led_len,
 					tx.sta, txpending);
=20
@@ -3159,7 +3260,7 @@ out:
 }
=20
 static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
-				struct net_device *dev, struct sta_info *sta,
+				struct sta_info *sta,
 				struct ieee80211_fast_tx *fast_tx,
 				struct sk_buff *skb)
 {
@@ -3170,8 +3271,6 @@ static bool ieee80211_xmit_fast(struct ieee80211_su=
b_if_data *sdata,
 	struct ethhdr eth;
 	struct ieee80211_tx_info *info =3D IEEE80211_SKB_CB(skb);
 	struct ieee80211_hdr *hdr =3D (void *)fast_tx->hdr;
-	struct ieee80211_tx_data tx;
-	ieee80211_tx_result r;
 	struct tid_ampdu_tx *tid_tx =3D NULL;
 	u8 tid =3D IEEE80211_NUM_TIDS;
=20
@@ -3210,8 +3309,6 @@ static bool ieee80211_xmit_fast(struct ieee80211_su=
b_if_data *sdata,
 			return true;
 	}
=20
-	ieee80211_tx_stats(dev, skb->len + extra_head);
-
 	if ((hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) &&
 	    ieee80211_amsdu_aggregate(sdata, sta, fast_tx, skb))
 		return true;
@@ -3240,11 +3337,32 @@ static bool ieee80211_xmit_fast(struct ieee80211_=
sub_if_data *sdata,
 	info->flags =3D IEEE80211_TX_CTL_FIRST_FRAGMENT |
 		      IEEE80211_TX_CTL_DONTFRAG |
 		      (tid_tx ? IEEE80211_TX_CTL_AMPDU : 0);
+	info->control.flags =3D IEEE80211_TX_CTRL_FAST_XMIT;
+
+	if (ieee80211_queue_skb(local, sdata, sta, skb))
+		return true;
+
+	return ieee80211_xmit_fast_finish(sdata, sta, fast_tx, skb, true);
+}
+
+static bool ieee80211_xmit_fast_finish(struct ieee80211_sub_if_data *sda=
ta,
+				       struct sta_info *sta,
+				       struct ieee80211_fast_tx *fast_tx,
+				       struct sk_buff *skb, bool xmit)
+{
+	struct ieee80211_local *local =3D sdata->local;
+	struct ieee80211_tx_info *info =3D IEEE80211_SKB_CB(skb);
+	struct ieee80211_hdr *hdr =3D (void *)skb->data;
+	struct ieee80211_tx_data tx;
+	ieee80211_tx_result r;
+	u8 tid =3D IEEE80211_NUM_TIDS;
+
+	ieee80211_tx_stats(skb->dev, skb->len);
=20
 	if (hdr->frame_control & cpu_to_le16(IEEE80211_STYPE_QOS_DATA)) {
+		tid =3D skb->priority & IEEE80211_QOS_CTL_TAG1D_MASK;
 		*ieee80211_get_qos_ctl(hdr) =3D tid;
-		if (!sta->sta.txq[0])
-			hdr->seq_ctrl =3D ieee80211_tx_next_seq(sta, tid);
+		hdr->seq_ctrl =3D ieee80211_tx_next_seq(sta, tid);
 	} else {
 		info->flags |=3D IEEE80211_TX_CTL_ASSIGN_SEQ;
 		hdr->seq_ctrl =3D cpu_to_le16(sdata->sequence_number);
@@ -3309,12 +3427,15 @@ static bool ieee80211_xmit_fast(struct ieee80211_=
sub_if_data *sdata,
 		}
 	}
=20
-	if (sdata->vif.type =3D=3D NL80211_IFTYPE_AP_VLAN)
-		sdata =3D container_of(sdata->bss,
-				     struct ieee80211_sub_if_data, u.ap);
+	if (xmit) {
+		if (sdata->vif.type =3D=3D NL80211_IFTYPE_AP_VLAN)
+			sdata =3D container_of(sdata->bss,
+					struct ieee80211_sub_if_data, u.ap);
+
+		__skb_queue_tail(&tx.skbs, skb);
+		ieee80211_tx_frags(local, &sdata->vif, &sta->sta, &tx.skbs, false);
+	}
=20
-	__skb_queue_tail(&tx.skbs, skb);
-	ieee80211_tx_frags(local, &sdata->vif, &sta->sta, &tx.skbs, false);
 	return true;
 }
=20
@@ -3342,7 +3463,7 @@ void __ieee80211_subif_start_xmit(struct sk_buff *s=
kb,
 		fast_tx =3D rcu_dereference(sta->fast_tx);
=20
 		if (fast_tx &&
-		    ieee80211_xmit_fast(sdata, dev, sta, fast_tx, skb))
+		    ieee80211_xmit_fast(sdata, sta, fast_tx, skb))
 			goto out;
 	}
=20
--=20
2.9.3

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox