Linux wireless drivers development
 help / color / mirror / Atom feed
* RE: [EXT] [PATCH] mwifiex: Fix three heap overflow at parsing element in cfg80211_ap_settings
From: Ganapathi Bhat @ 2019-08-28  3:01 UTC (permalink / raw)
  To: huangwenabc@gmail.com, linux-wireless@vger.kernel.org
  Cc: amitkarwar@gmail.com, nishants@marvell.com,
	huxinming820@gmail.com, solar@openwall.com, greg@kroah.com,
	kvalo@codeaurora.org, sashal@kernel.org, mrehak@redhat.com
In-Reply-To: <20190828020751.13625-1-huangwenabc@gmail.com>

Hi Wen Huang,

> mwifiex_update_vs_ie(),mwifiex_set_uap_rates() and
> mwifiex_set_wmm_params() call memcpy() without checking the destination
> size.Since the source is given from user-space, this may trigger a heap buffer
> overflow.
> 
> Fix them by putting the length check before performing memcpy().
> 
> This fix addresses CVE-2019-14814,CVE-2019-14815,CVE-2019-14816.

Thanks for the fix, this change looks good;

Acked-by: Ganapathi Bhat <gbhat@marvell.comg>

Regards,
Ganapathi

^ permalink raw reply

* RE: [PATCH 2/7] ath10k: change max RX bundle size from 8 to 32 for sdio
From: Wen Gong @ 2019-08-28  2:31 UTC (permalink / raw)
  To: Nicolas Boichat, Wen Gong
  Cc: open list:NETWORKING DRIVERS (WIRELESS),
	ath10k@lists.infradead.org
In-Reply-To: <CANMq1KBRMnVqw5rcnVcJs1UjYJxh+RqAEKSAjboojoMgJQpSDw@mail.gmail.com>

> -----Original Message-----
> From: ath10k <ath10k-bounces@lists.infradead.org> On Behalf Of Nicolas
> Boichat
> Sent: Tuesday, August 27, 2019 3:43 PM
> To: Wen Gong <wgong@codeaurora.org>
> Cc: open list:NETWORKING DRIVERS (WIRELESS) <linux-
> wireless@vger.kernel.org>; ath10k@lists.infradead.org
> Subject: [EXT] Re: [PATCH 2/7] ath10k: change max RX bundle size from 8 to
> 32 for sdio
> 
> > @@ -501,6 +501,7 @@ static int ath10k_sdio_mbox_alloc_bundle(struct
> ath10k *ar,
> >         int ret, i;
> >
> >         *bndl_cnt = FIELD_GET(ATH10K_HTC_FLAG_BUNDLE_MASK, htc_hdr-
> >flags);
> > +       *bndl_cnt += (FIELD_GET(GENMASK(3, 2), htc_hdr->flags) << 4);
> 
> GENMASK(3, 2): Please define this macro somewhere.
> 
> Also, I'd merge the 2 lines in 1.
Patch v2 has sent, https://patchwork.kernel.org/patch/11116677/
> ath10k mailing list
> ath10k@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/ath10k

^ permalink raw reply

* [PATCH] mwifiex: Fix three heap overflow at parsing element in cfg80211_ap_settings
From: huangwenabc @ 2019-08-28  2:07 UTC (permalink / raw)
  To: linux-wireless
  Cc: amitkarwar, nishants, gbhat, huxinming820, solar, greg, kvalo,
	sashal, mrehak

From: Wen Huang <huangwenabc@gmail.com>

mwifiex_update_vs_ie(),mwifiex_set_uap_rates() and 
mwifiex_set_wmm_params() call memcpy() without checking
the destination size.Since the source is given from 
user-space, this may trigger a heap buffer overflow.

Fix them by putting the length check before performing memcpy().

This fix addresses CVE-2019-14814,CVE-2019-14815,CVE-2019-14816.

Signed-off-by: Wen Huang <huangwenabc@gmail.com>
---
 drivers/net/wireless/marvell/mwifiex/ie.c      | 3 +++
 drivers/net/wireless/marvell/mwifiex/uap_cmd.c | 9 ++++++++-
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/marvell/mwifiex/ie.c b/drivers/net/wireless/marvell/mwifiex/ie.c
index 653d347a9..580387f9f 100644
--- a/drivers/net/wireless/marvell/mwifiex/ie.c
+++ b/drivers/net/wireless/marvell/mwifiex/ie.c
@@ -241,6 +241,9 @@ static int mwifiex_update_vs_ie(const u8 *ies, int ies_len,
 		}
 
 		vs_ie = (struct ieee_types_header *)vendor_ie;
+		if (le16_to_cpu(ie->ie_length) + vs_ie->len + 2 >
+			IEEE_MAX_IE_SIZE)
+			return -EINVAL;
 		memcpy(ie->ie_buffer + le16_to_cpu(ie->ie_length),
 		       vs_ie, vs_ie->len + 2);
 		le16_unaligned_add_cpu(&ie->ie_length, vs_ie->len + 2);
diff --git a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c
index 18f7d9bf3..0939a8c8f 100644
--- a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c
+++ b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c
@@ -265,6 +265,8 @@ mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg,
 
 	rate_ie = (void *)cfg80211_find_ie(WLAN_EID_SUPP_RATES, var_pos, len);
 	if (rate_ie) {
+		if (rate_ie->len > MWIFIEX_SUPPORTED_RATES)
+			return;
 		memcpy(bss_cfg->rates, rate_ie + 1, rate_ie->len);
 		rate_len = rate_ie->len;
 	}
@@ -272,8 +274,11 @@ mwifiex_set_uap_rates(struct mwifiex_uap_bss_param *bss_cfg,
 	rate_ie = (void *)cfg80211_find_ie(WLAN_EID_EXT_SUPP_RATES,
 					   params->beacon.tail,
 					   params->beacon.tail_len);
-	if (rate_ie)
+	if (rate_ie) {
+		if (rate_ie->len > MWIFIEX_SUPPORTED_RATES - rate_len)
+			return;
 		memcpy(bss_cfg->rates + rate_len, rate_ie + 1, rate_ie->len);
+	}
 
 	return;
 }
@@ -391,6 +396,8 @@ mwifiex_set_wmm_params(struct mwifiex_private *priv,
 					    params->beacon.tail_len);
 	if (vendor_ie) {
 		wmm_ie = vendor_ie;
+		if (*(wmm_ie + 1) > sizeof(struct mwifiex_types_wmm_info))
+			return;
 		memcpy(&bss_cfg->wmm_info, wmm_ie +
 		       sizeof(struct ieee_types_header), *(wmm_ie + 1));
 		priv->wmm_enabled = 1;
-- 
2.17.1


^ permalink raw reply related

* [PATCH 2/2] mac80211: Correctly set noencrypt for PAE frames
From: Denis Kenzior @ 2019-08-27 22:41 UTC (permalink / raw)
  To: linux-wireless; +Cc: Denis Kenzior, stable
In-Reply-To: <20190827224120.14545-1-denkenz@gmail.com>

The noencrypt flag was intended to be set if the "frame was received
unencrypted" according to include/uapi/linux/nl80211.h.  However, the
current behavior is opposite of this.

Cc: stable@vger.kernel.org
Fixes: 018f6fbf540d ("mac80211: Send control port frames over nl80211")
Signed-off-by: Denis Kenzior <denkenz@gmail.com>
---
 net/mac80211/rx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 7c4aeac006fb..8514c1f4ca90 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2447,7 +2447,7 @@ static void ieee80211_deliver_skb_to_local_stack(struct sk_buff *skb,
 		      skb->protocol == cpu_to_be16(ETH_P_PREAUTH)) &&
 		     sdata->control_port_over_nl80211)) {
 		struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-		bool noencrypt = status->flag & RX_FLAG_DECRYPTED;
+		bool noencrypt = (status->flag & RX_FLAG_DECRYPTED) == 0;
 
 		cfg80211_rx_control_port(dev, skb, noencrypt);
 		dev_kfree_skb(skb);
-- 
2.19.2


^ permalink raw reply related

* [PATCH 1/2] mac80211: Don't memset RXCB prior to PAE intercept
From: Denis Kenzior @ 2019-08-27 22:41 UTC (permalink / raw)
  To: linux-wireless; +Cc: Denis Kenzior, stable
In-Reply-To: <20190827224120.14545-1-denkenz@gmail.com>

In ieee80211_deliver_skb_to_local_stack intercepts EAPoL frames if
mac80211 is configured to do so and forwards the contents over nl80211.
During this process some additional data is also forwarded, including
whether the frame was received encrypted or not.  Unfortunately just
prior to the call to ieee80211_deliver_skb_to_local_stack, skb->cb is
cleared, resulting in incorrect data being exposed over nl80211.

Fixes: 018f6fbf540d ("mac80211: Send control port frames over nl80211")
Cc: stable@vger.kernel.org
Signed-off-by: Denis Kenzior <denkenz@gmail.com>
---
 net/mac80211/rx.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 3c1ab870fefe..7c4aeac006fb 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -2452,6 +2452,8 @@ static void ieee80211_deliver_skb_to_local_stack(struct sk_buff *skb,
 		cfg80211_rx_control_port(dev, skb, noencrypt);
 		dev_kfree_skb(skb);
 	} else {
+		memset(skb->cb, 0, sizeof(skb->cb));
+
 		/* deliver to local stack */
 		if (rx->napi)
 			napi_gro_receive(rx->napi, skb);
@@ -2546,8 +2548,6 @@ ieee80211_deliver_skb(struct ieee80211_rx_data *rx)
 
 	if (skb) {
 		skb->protocol = eth_type_trans(skb, dev);
-		memset(skb->cb, 0, sizeof(skb->cb));
-
 		ieee80211_deliver_skb_to_local_stack(skb, rx);
 	}
 
-- 
2.19.2


^ permalink raw reply related

* [PATCH 0/2] mac80211: Control Port over nl80211 fixes
From: Denis Kenzior @ 2019-08-27 22:41 UTC (permalink / raw)
  To: linux-wireless; +Cc: Denis Kenzior

Couple of small fixes for Control Port handling in mac80211.  The
original commit was working by some crazy luck in all testing, but
manifested itself on certain hardware that managed to drop PAE frames
with uncanny consistency.

Denis Kenzior (2):
  mac80211: Don't memset RXCB prior to PAE intercept
  mac80211: Correctly set noencrypt for PAE frames

 net/mac80211/rx.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

-- 
2.19.2


^ permalink raw reply

* Re: [PATCH 31/49] ath11k: add mac.c
From: Ben Greear @ 2019-08-27 19:13 UTC (permalink / raw)
  To: Toke Høiland-Jørgensen, Vasanthakumar Thiagarajan
  Cc: Kalle Valo, linux-wireless, ath11k, devicetree,
	linux-wireless-owner
In-Reply-To: <87sgpmikne.fsf@toke.dk>

On 8/27/19 10:27 AM, Toke Høiland-Jørgensen wrote:
> Vasanthakumar Thiagarajan <vthiagar@codeaurora.org> writes:
> 
>> On 2019-08-21 15:38, Toke Høiland-Jørgensen wrote:
>>> Vasanthakumar Thiagarajan <vthiagar@codeaurora.org> writes:
>>>
>>>> On 2019-08-20 22:21, Toke Høiland-Jørgensen wrote:
>>>>> [... snip ... ]
>>>>>
>>>>>> +static const struct ieee80211_ops ath11k_ops = {
>>>>>> +	.tx				= ath11k_mac_op_tx,
>>>>>
>>>>> No wake_tx_queue? :(
>>>>
>>>> Yes, packet queueing is handled in firmware. This makes sense
>>>> especially when we enable 802.11 encap offload support where most of
>>>> the data path processing in mac80211 will be skipped and packet is
>>>> given to driver/firmware in 802.3 format itself. Then firmware would
>>>> take care of all the classification, queueing and encapsulation
>>>> operations.
>>>
>>> Well, so does ath10k, and yet we still saw a significant improvement by
>>> moving queueing back into the host where it can be handled by the
>>> FQ-CoDel-enabled queueing structure.
>>>
>>
>> Sure, we could probably try that with ath11k as well at some point when
>> we have a baseline with HE support.
> 
> Well, rather than retrofit change things later, why not start out with a
> wake_tx_queue-based driver?

If there is something that works at all, lets get it upstream when it is
easier to develop and test against.  Then we will have a baseline to test against
when adding new features and so forth.

Thanks,
Ben

-- 
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc  http://www.candelatech.com


^ permalink raw reply

* Re: [PATCH 31/49] ath11k: add mac.c
From: Toke Høiland-Jørgensen @ 2019-08-27 17:27 UTC (permalink / raw)
  To: Vasanthakumar Thiagarajan
  Cc: Kalle Valo, linux-wireless, ath11k, devicetree,
	linux-wireless-owner
In-Reply-To: <14737343f1925a771ddd8dadf0f2b5a3@codeaurora.org>

Vasanthakumar Thiagarajan <vthiagar@codeaurora.org> writes:

> On 2019-08-21 15:38, Toke Høiland-Jørgensen wrote:
>> Vasanthakumar Thiagarajan <vthiagar@codeaurora.org> writes:
>> 
>>> On 2019-08-20 22:21, Toke Høiland-Jørgensen wrote:
>>>> [... snip ... ]
>>>> 
>>>>> +static const struct ieee80211_ops ath11k_ops = {
>>>>> +	.tx				= ath11k_mac_op_tx,
>>>> 
>>>> No wake_tx_queue? :(
>>> 
>>> Yes, packet queueing is handled in firmware. This makes sense
>>> especially when we enable 802.11 encap offload support where most of
>>> the data path processing in mac80211 will be skipped and packet is
>>> given to driver/firmware in 802.3 format itself. Then firmware would
>>> take care of all the classification, queueing and encapsulation
>>> operations.
>> 
>> Well, so does ath10k, and yet we still saw a significant improvement by
>> moving queueing back into the host where it can be handled by the
>> FQ-CoDel-enabled queueing structure.
>> 
>
> Sure, we could probably try that with ath11k as well at some point when 
> we have a baseline with HE support.

Well, rather than retrofit change things later, why not start out with a
wake_tx_queue-based driver?

-Toke

^ permalink raw reply

* Re: [PATCH 01/49] dt: bindings: net: add qcom,ath11k.txt
From: Rob Herring @ 2019-08-27 17:13 UTC (permalink / raw)
  To: Kalle Valo; +Cc: linux-wireless, ath11k, devicetree
In-Reply-To: <1566316095-27507-2-git-send-email-kvalo@codeaurora.org>

On Tue, Aug 20, 2019 at 06:47:27PM +0300, Kalle Valo wrote:

Missing commit message and Sob (and on the other patches).

> ---
>  .../bindings/net/wireless/qcom,ath11k.txt          | 127 +++++++++++++++++++++
>  1 file changed, 127 insertions(+)

Please use the DT schema format (YAML). See writing-schema.md.

> 
> diff --git a/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.txt b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.txt
> new file mode 100644
> index 000000000000..1824238b4b50
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/net/wireless/qcom,ath11k.txt
> @@ -0,0 +1,127 @@
> +* Qualcomm Technologies ath11k wireless devices
> +
> +Required properties:
> +- compatible: Should be "qcom,ipq8074-wifi"
> +
> +AHB based ipq8074 uses most of the properties defined in this doc.
> +
> +Optional properties:
> +- reg: Address and length of the register set for the device.
> +- interrupts: List of interrupt lines. Must contain an entry
> +	      for each entry in the interrupt-names property.

Need to be explicit as to how many interrupts and what they are.

> +- interrupt-names: Must include the entries for CE interrupt
> +		   names ("ce0" to "ce11") and hw srng interrupt
> +		   names.
> +- qcom,rproc: DT entry of q6v5-wcss
> +
> +Example:
> +
> +wifi0: wifi@c000000 {
> +	compatible = "qcom,ipq8074-wifi";
> +	reg = <0xc000000 0x2000000>;
> +	interrupts = <0 320 1>,
> +		     <0 319 1>,
> +		     <0 318 1>,
> +		     <0 317 1>,
> +		     <0 316 1>,
> +		     <0 315 1>,
> +		     <0 314 1>,
> +		     <0 311 1>,
> +		     <0 310 1>,
> +		     <0 411 1>,
> +		     <0 410 1>,
> +		     <0 40 1>,
> +		     <0 39 1>,
> +		     <0 302 1>,
> +		     <0 301 1>,
> +		     <0 37 1>,
> +		     <0 36 1>,
> +		     <0 296 1>,
> +		     <0 295 1>,
> +		     <0 294 1>,
> +		     <0 293 1>,
> +		     <0 292 1>,
> +		     <0 291 1>,
> +		     <0 290 1>,
> +		     <0 289 1>,
> +		     <0 288 1>,
> +		     <0 239 1>,
> +		     <0 236 1>,
> +		     <0 235 1>,
> +		     <0 234 1>,
> +		     <0 233 1>,
> +		     <0 232 1>,
> +		     <0 231 1>,
> +		     <0 230 1>,
> +		     <0 229 1>,
> +		     <0 228 1>,
> +		     <0 224 1>,
> +		     <0 223 1>,
> +		     <0 203 1>,
> +		     <0 183 1>,
> +		     <0 180 1>,
> +		     <0 179 1>,
> +		     <0 178 1>,
> +		     <0 177 1>,
> +		     <0 176 1>,
> +		     <0 163 1>,
> +		     <0 162 1>,
> +		     <0 160 1>,
> +		     <0 159 1>,
> +		     <0 158 1>,
> +		     <0 157 1>,
> +		     <0 156 1>;
> +	interrupt-names = "misc-pulse1",
> +			  "misc-latch",
> +			  "sw-exception",
> +			  "watchdog",
> +			  "ce0",
> +			  "ce1",
> +			  "ce2",
> +			  "ce3",
> +			  "ce4",
> +			  "ce5",
> +			  "ce6",
> +			  "ce7",
> +			  "ce8",
> +			  "ce9",
> +			  "ce10",
> +			  "ce11",
> +			  "host2wbm-desc-feed",
> +			  "host2reo-re-injection",
> +			  "host2reo-command",
> +			  "host2rxdma-monitor-ring3",
> +			  "host2rxdma-monitor-ring2",
> +			  "host2rxdma-monitor-ring1",
> +			  "reo2ost-exception",
> +			  "wbm2host-rx-release",
> +			  "reo2host-status",
> +			  "reo2host-destination-ring4",
> +			  "reo2host-destination-ring3",
> +			  "reo2host-destination-ring2",
> +			  "reo2host-destination-ring1",
> +			  "rxdma2host-monitor-destination-mac3",
> +			  "rxdma2host-monitor-destination-mac2",
> +			  "rxdma2host-monitor-destination-mac1",
> +			  "ppdu-end-interrupts-mac3",
> +			  "ppdu-end-interrupts-mac2",
> +			  "ppdu-end-interrupts-mac1",
> +			  "rxdma2host-monitor-status-ring-mac3",
> +			  "rxdma2host-monitor-status-ring-mac2",
> +			  "rxdma2host-monitor-status-ring-mac1",
> +			  "host2rxdma-host-buf-ring-mac3",
> +			  "host2rxdma-host-buf-ring-mac2",
> +			  "host2rxdma-host-buf-ring-mac1",
> +			  "rxdma2host-destination-ring-mac3",
> +			  "rxdma2host-destination-ring-mac2",
> +			  "rxdma2host-destination-ring-mac1",
> +			  "host2tcl-input-ring4",
> +			  "host2tcl-input-ring3",
> +			  "host2tcl-input-ring2",
> +			  "host2tcl-input-ring1",
> +			  "wbm2host-tx-completions-ring3",
> +			  "wbm2host-tx-completions-ring2",
> +			  "wbm2host-tx-completions-ring1",
> +			  "tcl2host-status-ring";
> +	qcom,rproc = <&qcom_q6v5_wcss>;
> +};
> 

^ permalink raw reply

* [PATCH] wil6210: Delete an unnecessary kfree() call in wil_tid_ampdu_rx_alloc()
From: Markus Elfring @ 2019-08-27 14:44 UTC (permalink / raw)
  To: linux-wireless, netdev, wil6210, David S. Miller, Kalle Valo,
	Maya Erez
  Cc: LKML, kernel-janitors

From: Markus Elfring <elfring@users.sourceforge.net>
Date: Tue, 27 Aug 2019 16:39:02 +0200

A null pointer would be passed to a call of the function “kfree”
directly after a call of the function “kcalloc” failed at one place.
Remove this superfluous function call.

This issue was detected by using the Coccinelle software.

Signed-off-by: Markus Elfring <elfring@users.sourceforge.net>
---
 drivers/net/wireless/ath/wil6210/rx_reorder.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/net/wireless/ath/wil6210/rx_reorder.c b/drivers/net/wireless/ath/wil6210/rx_reorder.c
index 784239bcb3a6..13246d216803 100644
--- a/drivers/net/wireless/ath/wil6210/rx_reorder.c
+++ b/drivers/net/wireless/ath/wil6210/rx_reorder.c
@@ -260,7 +260,6 @@ struct wil_tid_ampdu_rx *wil_tid_ampdu_rx_alloc(struct wil6210_priv *wil,
 	r->reorder_buf =
 		kcalloc(size, sizeof(struct sk_buff *), GFP_KERNEL);
 	if (!r->reorder_buf) {
-		kfree(r->reorder_buf);
 		kfree(r);
 		return NULL;
 	}
--
2.23.0


^ permalink raw reply related

* [PATCH] mt76: mt7603: use devm_platform_ioremap_resource() to simplify code
From: YueHaibing @ 2019-08-27 13:44 UTC (permalink / raw)
  To: nbd, lorenzo.bianconi83, ryder.lee, royluo, kvalo, davem,
	matthias.bgg, swboyd, yuehaibing, weiyongjun1
  Cc: linux-wireless, netdev, linux-arm-kernel, linux-mediatek,
	linux-kernel

Use devm_platform_ioremap_resource() to simplify the code a bit.
This is detected by coccinelle.

Reported-by: Hulk Robot <hulkci@huawei.com>
Signed-off-by: YueHaibing <yuehaibing@huawei.com>
---
 drivers/net/wireless/mediatek/mt76/mt7603/soc.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/soc.c b/drivers/net/wireless/mediatek/mt76/mt7603/soc.c
index c6c1ce6..c22715e 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/soc.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/soc.c
@@ -9,7 +9,6 @@
 static int
 mt76_wmac_probe(struct platform_device *pdev)
 {
-	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	struct mt7603_dev *dev;
 	void __iomem *mem_base;
 	struct mt76_dev *mdev;
@@ -20,7 +19,7 @@ mt76_wmac_probe(struct platform_device *pdev)
 	if (irq < 0)
 		return irq;
 
-	mem_base = devm_ioremap_resource(&pdev->dev, res);
+	mem_base = devm_platform_ioremap_resource(pdev, 0);
 	if (IS_ERR(mem_base)) {
 		dev_err(&pdev->dev, "Failed to get memory resource\n");
 		return PTR_ERR(mem_base);
-- 
2.7.4



^ permalink raw reply related

* Re: Implementing Mikrotik IE
From: Sebastian Gottschall @ 2019-08-27 13:14 UTC (permalink / raw)
  To: Johannes Berg, Josef Miegl; +Cc: linux-wireless
In-Reply-To: <ac3fa1160afc5f4ccb1b41bd0b2e6dd165448180.camel@sipsolutions.net>


Am 27.08.2019 um 15:10 schrieb Johannes Berg:
> On Tue, 2019-08-27 at 15:08 +0200, Sebastian Gottschall wrote:
>> Am 22.08.2019 um 23:06 schrieb Josef Miegl:
>>> On August 22, 2019 10:08:13 PM GMT+02:00, Johannes Berg <johannes@sipsolutions.net> wrote:
>>>> On Thu, 2019-08-22 at 09:00 +0200, Johannes Berg wrote:
>>>>> Perhaps it expects the 4-way-HS to already be in 4-addr frame format,
>>>> or
>>>>> something else special in the 4-way-HS if you have WDS?
>>>> I think this is actually the right guess.
>>> Yes, it indeed it! Thank you so much Johannes!
>> good finding. my idea here is if we parse the wds flags field from mtik
>> ie within mac80211 we could also support this special handing without
>> any workaround
> The same holds true if we parse it in hostapd, and add a flag to the
> EAPOL to control the encapsulation, which is way more general, so much
> preferable IMHO.

i know .same result and i know that the userspace solution is more prefered

Sebastian

>
> johannes
>
>

^ permalink raw reply

* Re: Implementing Mikrotik IE
From: Johannes Berg @ 2019-08-27 13:10 UTC (permalink / raw)
  To: Sebastian Gottschall, Josef Miegl; +Cc: linux-wireless
In-Reply-To: <21433c24-4860-be12-8d79-11e954e0bf9d@newmedia-net.de>

On Tue, 2019-08-27 at 15:08 +0200, Sebastian Gottschall wrote:
> Am 22.08.2019 um 23:06 schrieb Josef Miegl:
> > On August 22, 2019 10:08:13 PM GMT+02:00, Johannes Berg <johannes@sipsolutions.net> wrote:
> > > On Thu, 2019-08-22 at 09:00 +0200, Johannes Berg wrote:
> > > > Perhaps it expects the 4-way-HS to already be in 4-addr frame format,
> > > or
> > > > something else special in the 4-way-HS if you have WDS?
> > > I think this is actually the right guess.
> > Yes, it indeed it! Thank you so much Johannes!

> good finding. my idea here is if we parse the wds flags field from mtik 
> ie within mac80211 we could also support this special handing without 
> any workaround

The same holds true if we parse it in hostapd, and add a flag to the
EAPOL to control the encapsulation, which is way more general, so much
preferable IMHO.

johannes


^ permalink raw reply

* Re: Implementing Mikrotik IE
From: Sebastian Gottschall @ 2019-08-27 13:08 UTC (permalink / raw)
  To: Josef Miegl, Johannes Berg; +Cc: linux-wireless
In-Reply-To: <68A3B9AF-8864-4C0F-A50B-71CCB76AE81D@miegl.cz>


Am 22.08.2019 um 23:06 schrieb Josef Miegl:
> On August 22, 2019 10:08:13 PM GMT+02:00, Johannes Berg <johannes@sipsolutions.net> wrote:
>> On Thu, 2019-08-22 at 09:00 +0200, Johannes Berg wrote:
>>> Perhaps it expects the 4-way-HS to already be in 4-addr frame format,
>> or
>>> something else special in the 4-way-HS if you have WDS?
>> I think this is actually the right guess.
> Yes, it indeed it! Thank you so much Johannes!
good finding. my idea here is if we parse the wds flags field from mtik 
ie within mac80211 we could also support this special handing without 
any workaround


^ permalink raw reply

* Re: [PATCH v2] rtw88: pci: enable MSI interrupt
From: Jian-Hong Pan @ 2019-08-27 13:02 UTC (permalink / raw)
  To: Tony Chuang
  Cc: Ján Veselý, Daniel Drake, briannorris@chromium.org,
	gojun077@gmail.com, kvalo@codeaurora.org, linux-wireless,
	linux@endlessm.com
In-Reply-To: <F7CD281DE3E379468C6D07993EA72F84D18AE7C8@RTITMBSVM04.realtek.com.tw>

Tony Chuang <yhchuang@realtek.com> 於 2019年8月27日 週二 下午8:12寫道:
>
> Hi Daniel and Ján,
>
>
> > From: Ján Veselý > Sent: Friday, August 23, 2019 3:22 PM
> > On Fri, Aug 23, 2019 at 2:37 AM Daniel Drake <drake@endlessm.com> wrote:
> > >
> > > > +     rtw_pci_disable_interrupt(rtwdev, rtwpci);
> > >
> > > I checked the discussion on the v1 patch thread but I still don't follow
> > > this.
> > >
> > > You're worried about the case where we're inside the interrupt handler and:
> > >  1. We read the interrupt status to note what needs to be done
> > >  2. <another interrupt arrives here, requiring other work to be done>
> > >  3. We clear the interrupt status bits
> > >  4. We proceed to handle the interrupt but missing any work requested by
> > >     the interrupt in step 2.
> > >
> > > Is that right?
> > >
> > > I'm not an expert here, but I don't think this is something that drivers
> > > have to worry about. Surely the interrupt controller can be expected to
> > > have a mechanism to "queue up" any interrupt that arrives while an
> > > interrupt is being handled? Otherwise handling of all types of
> > > edge-triggered interrupts (not just MSI) would be overly painful across the
> > > board.
> >
> > That's my understanding as well.
> > entering the interrupt vector clears the IFLAG, so any interrupt will
> > wait until the IFLAG is restored, or delivered to a different CPU.
> > wouldn't it be safer to enable interrupts only _after_ registering the
> > handler in the "rtw_pci_request_irq" function?
> >
> > regards,
> > Jan
>
>
> Yes that's not something drivers need to care about. But I think it is
> Because there's a race condition between SW/HW when clearing the ISR.
> If interrupt comes after reading ISR and before write-1-clear, the interrupt
> controller would have interrupt status raised, and never issue interrupt
> signal to host when other new interrupts status are raised.
>
> To avoid this, driver requires to protect the ISR write-1-clear process by
> disabling the IMR.
>
>
> >
> >
> > >
> > > See e.g. https://patchwork.kernel.org/patch/3333681/ as a reference for
> > > what correct interrupt controller behaviour should look like.
> > >
> > > > +             ret = pci_enable_msi(pdev);
> > >
> > > pci_enable_msi() is "deprecated, don't use"
>
> Do you mean I should remove this?
> But I cannot find another proper way to enable the MSI.
> Maybe pci_alloc_irq_vectors() could work but I am not sure if
> It is recommended.

According to the kernel documentation "The MSI Driver Guide HOWTO",
pci_alloc_irq_vectors, pci_irq_vector and pci_free_irq_vectors are the
functions.
https://elixir.bootlin.com/linux/v5.3-rc6/source/Documentation/PCI/msi-howto.rst

Here is an example in r8169 module.
https://elixir.bootlin.com/linux/v5.3-rc6/source/drivers/net/ethernet/realtek/r8169_main.c#L6603

Jian-Hong Pan

^ permalink raw reply

* RE: [PATCH v2] rtw88: pci: enable MSI interrupt
From: Tony Chuang @ 2019-08-27 12:11 UTC (permalink / raw)
  To: Ján Veselý, Daniel Drake
  Cc: briannorris@chromium.org, gojun077@gmail.com,
	kvalo@codeaurora.org, linux-wireless, linux@endlessm.com
In-Reply-To: <CA+K+NcSYKEkdx5ux6iwUs7pMidObZBrg9yDcP1zT73DcccpDPQ@mail.gmail.com>

Hi Daniel and Ján,


> From: Ján Veselý > Sent: Friday, August 23, 2019 3:22 PM
> On Fri, Aug 23, 2019 at 2:37 AM Daniel Drake <drake@endlessm.com> wrote:
> >
> > > +     rtw_pci_disable_interrupt(rtwdev, rtwpci);
> >
> > I checked the discussion on the v1 patch thread but I still don't follow
> > this.
> >
> > You're worried about the case where we're inside the interrupt handler and:
> >  1. We read the interrupt status to note what needs to be done
> >  2. <another interrupt arrives here, requiring other work to be done>
> >  3. We clear the interrupt status bits
> >  4. We proceed to handle the interrupt but missing any work requested by
> >     the interrupt in step 2.
> >
> > Is that right?
> >
> > I'm not an expert here, but I don't think this is something that drivers
> > have to worry about. Surely the interrupt controller can be expected to
> > have a mechanism to "queue up" any interrupt that arrives while an
> > interrupt is being handled? Otherwise handling of all types of
> > edge-triggered interrupts (not just MSI) would be overly painful across the
> > board.
> 
> That's my understanding as well.
> entering the interrupt vector clears the IFLAG, so any interrupt will
> wait until the IFLAG is restored, or delivered to a different CPU.
> wouldn't it be safer to enable interrupts only _after_ registering the
> handler in the "rtw_pci_request_irq" function?
> 
> regards,
> Jan


Yes that's not something drivers need to care about. But I think it is
Because there's a race condition between SW/HW when clearing the ISR.
If interrupt comes after reading ISR and before write-1-clear, the interrupt
controller would have interrupt status raised, and never issue interrupt
signal to host when other new interrupts status are raised.

To avoid this, driver requires to protect the ISR write-1-clear process by
disabling the IMR.


> 
> 
> >
> > See e.g. https://patchwork.kernel.org/patch/3333681/ as a reference for
> > what correct interrupt controller behaviour should look like.
> >
> > > +             ret = pci_enable_msi(pdev);
> >
> > pci_enable_msi() is "deprecated, don't use"

Do you mean I should remove this?
But I cannot find another proper way to enable the MSI.
Maybe pci_alloc_irq_vectors() could work but I am not sure if
It is recommended.


Yan-Hsuan


^ permalink raw reply

* Re: [PATCH] ath10k: add fw coredump for sdio when firmware assert
From: Nicolas Boichat @ 2019-08-27 12:07 UTC (permalink / raw)
  To: Wen Gong; +Cc: ath10k, open list:NETWORKING DRIVERS (WIRELESS), Brian Norris
In-Reply-To: <1566371979-22730-1-git-send-email-wgong@codeaurora.org>

Just a few nits, this is a lot of code and I'll try to give it a second pass.

On Wed, Aug 21, 2019 at 3:20 PM Wen Gong <wgong@codeaurora.org> wrote:
>
> When firmware assert, it need coredump to analyze, this patch will
> collect the register and memory info for sdio chip.
>
> The coredump configuration is different between PCIE and SDIO for
> the same reversion, so this patch add bus type to distinguish PCIE
> and SDIO chip for coredump.
>
> Tested with QCA6174 SDIO with firmware
> WLAN.RMH.4.4.1-00007-QCARMSWP-1.
>
> Signed-off-by: Wen Gong <wgong@codeaurora.org>
> ---
>  drivers/net/wireless/ath/ath10k/bmi.c       |   1 +
>  drivers/net/wireless/ath/ath10k/core.c      |   7 +-
>  drivers/net/wireless/ath/ath10k/core.h      |   4 +-
>  drivers/net/wireless/ath/ath10k/coredump.c  | 338 +++++++++++++++++++++++++++-
>  drivers/net/wireless/ath/ath10k/coredump.h  |   1 +
>  drivers/net/wireless/ath/ath10k/hw.h        |   1 +
>  drivers/net/wireless/ath/ath10k/sdio.c      | 335 ++++++++++++++++++++++++++-
>  drivers/net/wireless/ath/ath10k/targaddrs.h |  10 +
>  8 files changed, 692 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c
> index 95dc4be..990fa4d 100644
> --- a/drivers/net/wireless/ath/ath10k/bmi.c
> +++ b/drivers/net/wireless/ath/ath10k/bmi.c
> @@ -197,6 +197,7 @@ int ath10k_bmi_read_memory(struct ath10k *ar,
>
>         return 0;
>  }
> +EXPORT_SYMBOL(ath10k_bmi_read_memory);
>
>  int ath10k_bmi_write_soc_reg(struct ath10k *ar, u32 address, u32 reg_val)
>  {
> diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
> index dc45d16..0ea4c36 100644
> --- a/drivers/net/wireless/ath/ath10k/core.c
> +++ b/drivers/net/wireless/ath/ath10k/core.c
> @@ -33,7 +33,6 @@
>  static bool skip_otp;
>  static bool rawmode;
>  static bool fw_diag_log;
> -

Don't do whitespace changes (unless you're changing code in the area anyway).

>  unsigned long ath10k_coredump_mask = BIT(ATH10K_FW_CRASH_DUMP_REGISTERS) |
>                                      BIT(ATH10K_FW_CRASH_DUMP_CE_DATA);
>
> @@ -708,6 +707,10 @@ static void ath10k_init_sdio(struct ath10k *ar, enum ath10k_firmware_mode mode)
>         ath10k_bmi_read32(ar, hi_option_flag, &param);
>         param |= HI_OPTION_DISABLE_DBGLOG;
>         ath10k_bmi_write32(ar, hi_option_flag, param);
> +
> +       ath10k_bmi_read32(ar, hi_option_flag2, &param);
> +       param |= HI_OPTION_SDIO_CRASH_DUMP_ENHANCEMENT_HOST;
> +       ath10k_bmi_write32(ar, hi_option_flag2, param);
>  }
>
>  static int ath10k_init_configure_target(struct ath10k *ar)
> @@ -1953,6 +1956,8 @@ static void ath10k_core_get_fw_name(struct ath10k *ar, char *fw_name,
>                 scnprintf(fw_name, fw_name_len, "%s-%d.bin",
>                           ATH10K_FW_FILE_BASE, fw_api);
>                 break;
> +       default:
> +               break;

Why?

>         }
>  }
>
> diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
> index 4d7db07..1b52a3c 100644
> --- a/drivers/net/wireless/ath/ath10k/core.h
> +++ b/drivers/net/wireless/ath/ath10k/core.h
> @@ -97,7 +97,9 @@ static inline const char *ath10k_bus_str(enum ath10k_bus bus)
>                 return "usb";
>         case ATH10K_BUS_SNOC:
>                 return "snoc";
> -       }
> +       default:
> +               return "unknown";
> +}

This change does not look very useful? Also the indentation is broken.


>
>         return "unknown";
>  }
> diff --git a/drivers/net/wireless/ath/ath10k/coredump.c b/drivers/net/wireless/ath/ath10k/coredump.c
> index b6d2932..b287509 100644
> --- a/drivers/net/wireless/ath/ath10k/coredump.c
> +++ b/drivers/net/wireless/ath/ath10k/coredump.c
> @@ -270,6 +270,277 @@
>         {0x80010, 0x80020},
>  };
>
> +static const struct ath10k_mem_section qca6174_hw30_sdio_register_sections[] = {
> +       {0x800, 0x810},
> +       {0x820, 0x82C},
> +       {0x830, 0x8F4},
> +       {0x90C, 0x91C},
> +       {0xA14, 0xA18},
> +       {0xA84, 0xA94},
> +       {0xAA8, 0xAD4},
> +       {0xADC, 0xB40},
> +       {0x1000, 0x10A4},
> +       {0x10BC, 0x111C},
> +       {0x1134, 0x1138},
> +       {0x1144, 0x114C},
> +       {0x1150, 0x115C},
> +       {0x1160, 0x1178},
> +       {0x1240, 0x1260},
> +       {0x2000, 0x207C},
> +       {0x3000, 0x3014},
> +       {0x4000, 0x4014},
> +       {0x5000, 0x5124},
> +       {0x6000, 0x6040},
> +       {0x6080, 0x60CC},
> +       {0x6100, 0x611C},
> +       {0x6140, 0x61D8},
> +       {0x6200, 0x6238},
> +       {0x6240, 0x628C},
> +       {0x62C0, 0x62EC},
> +       {0x6380, 0x63E8},
> +       {0x6400, 0x6440},
> +       {0x6480, 0x64CC},
> +       {0x6500, 0x651C},
> +       {0x6540, 0x6580},
> +       {0x6600, 0x6638},
> +       {0x6640, 0x668C},
> +       {0x66C0, 0x66EC},
> +       {0x6780, 0x67E8},
> +       {0x7080, 0x708C},
> +       {0x70C0, 0x70C8},
> +       {0x7400, 0x741C},
> +       {0x7440, 0x7454},
> +       {0x7800, 0x7818},
> +       {0x8010, 0x8060},
> +       {0x8080, 0x8084},
> +       {0x80A0, 0x80A4},
> +       {0x80C0, 0x80C4},
> +       {0x80E0, 0x80ec},
> +       {0x8110, 0x8128},
> +       {0x9000, 0x9004},
> +       {0xF000, 0xF0E0},
> +       {0xF140, 0xF190},
> +       {0xF250, 0xF25C},
> +       {0xF260, 0xF268},
> +       {0xF26C, 0xF2A8},
> +       {0x10008, 0x1000C},
> +       {0x10014, 0x10018},
> +       {0x1001C, 0x10020},
> +       {0x10024, 0x10028},
> +       {0x10030, 0x10034},
> +       {0x10040, 0x10054},
> +       {0x10058, 0x1007C},
> +       {0x10080, 0x100C4},
> +       {0x100C8, 0x10114},
> +       {0x1012C, 0x10130},
> +       {0x10138, 0x10144},
> +       {0x10200, 0x10220},
> +       {0x10230, 0x10250},
> +       {0x10260, 0x10280},
> +       {0x10290, 0x102B0},
> +       {0x102C0, 0x102DC},
> +       {0x102E0, 0x102F4},
> +       {0x102FC, 0x1037C},
> +       {0x10380, 0x10390},
> +       {0x10800, 0x10828},
> +       {0x10840, 0x10844},
> +       {0x10880, 0x10884},
> +       {0x108C0, 0x108E8},
> +       {0x10900, 0x10928},
> +       {0x10940, 0x10944},
> +       {0x10980, 0x10984},
> +       {0x109C0, 0x109E8},
> +       {0x10A00, 0x10A28},
> +       {0x10A40, 0x10A50},
> +       {0x11000, 0x11028},
> +       {0x11030, 0x11034},
> +       {0x11038, 0x11068},
> +       {0x11070, 0x11074},
> +       {0x11078, 0x110A8},
> +       {0x110B0, 0x110B4},
> +       {0x110B8, 0x110E8},
> +       {0x110F0, 0x110F4},
> +       {0x110F8, 0x11128},
> +       {0x11138, 0x11144},
> +       {0x11178, 0x11180},
> +       {0x111B8, 0x111C0},
> +       {0x111F8, 0x11200},
> +       {0x11238, 0x1123C},
> +       {0x11270, 0x11274},
> +       {0x11278, 0x1127C},
> +       {0x112B0, 0x112B4},
> +       {0x112B8, 0x112BC},
> +       {0x112F0, 0x112F4},
> +       {0x112F8, 0x112FC},
> +       {0x11338, 0x1133C},
> +       {0x11378, 0x1137C},
> +       {0x113B8, 0x113BC},
> +       {0x113F8, 0x113FC},
> +       {0x11438, 0x11440},
> +       {0x11478, 0x11480},
> +       {0x114B8, 0x114BC},
> +       {0x114F8, 0x114FC},
> +       {0x11538, 0x1153C},
> +       {0x11578, 0x1157C},
> +       {0x115B8, 0x115BC},
> +       {0x115F8, 0x115FC},
> +       {0x11638, 0x1163C},
> +       {0x11678, 0x1167C},
> +       {0x116B8, 0x116BC},
> +       {0x116F8, 0x116FC},
> +       {0x11738, 0x1173C},
> +       {0x11778, 0x1177C},
> +       {0x117B8, 0x117BC},
> +       {0x117F8, 0x117FC},
> +       {0x17000, 0x1701C},
> +       {0x17020, 0x170AC},
> +       {0x18000, 0x18050},
> +       {0x18054, 0x18074},
> +       {0x18080, 0x180D4},
> +       {0x180DC, 0x18104},
> +       {0x18108, 0x1813C},
> +       {0x18144, 0x18148},
> +       {0x18168, 0x18174},
> +       {0x18178, 0x18180},
> +       {0x181C8, 0x181E0},
> +       {0x181E4, 0x181E8},
> +       {0x181EC, 0x1820C},
> +       {0x1825C, 0x18280},
> +       {0x18284, 0x18290},
> +       {0x18294, 0x182A0},
> +       {0x18300, 0x18304},
> +       {0x18314, 0x18320},
> +       {0x18328, 0x18350},
> +       {0x1835C, 0x1836C},
> +       {0x18370, 0x18390},
> +       {0x18398, 0x183AC},
> +       {0x183BC, 0x183D8},
> +       {0x183DC, 0x183F4},
> +       {0x18400, 0x186F4},
> +       {0x186F8, 0x1871C},
> +       {0x18720, 0x18790},
> +       {0x19800, 0x19830},
> +       {0x19834, 0x19840},
> +       {0x19880, 0x1989C},
> +       {0x198A4, 0x198B0},
> +       {0x198BC, 0x19900},
> +       {0x19C00, 0x19C88},
> +       {0x19D00, 0x19D20},
> +       {0x19E00, 0x19E7C},
> +       {0x19E80, 0x19E94},
> +       {0x19E98, 0x19EAC},
> +       {0x19EB0, 0x19EBC},
> +       {0x19F70, 0x19F74},
> +       {0x19F80, 0x19F8C},
> +       {0x19FA0, 0x19FB4},
> +       {0x19FC0, 0x19FD8},
> +       {0x1A000, 0x1A200},
> +       {0x1A204, 0x1A210},
> +       {0x1A228, 0x1A22C},
> +       {0x1A230, 0x1A248},
> +       {0x1A250, 0x1A270},
> +       {0x1A280, 0x1A290},
> +       {0x1A2A0, 0x1A2A4},
> +       {0x1A2C0, 0x1A2EC},
> +       {0x1A300, 0x1A3BC},
> +       {0x1A3F0, 0x1A3F4},
> +       {0x1A3F8, 0x1A434},
> +       {0x1A438, 0x1A444},
> +       {0x1A448, 0x1A468},
> +       {0x1A580, 0x1A58C},
> +       {0x1A644, 0x1A654},
> +       {0x1A670, 0x1A698},
> +       {0x1A6AC, 0x1A6B0},
> +       {0x1A6D0, 0x1A6D4},
> +       {0x1A6EC, 0x1A70C},
> +       {0x1A710, 0x1A738},
> +       {0x1A7C0, 0x1A7D0},
> +       {0x1A7D4, 0x1A7D8},
> +       {0x1A7DC, 0x1A7E4},
> +       {0x1A7F0, 0x1A7F8},
> +       {0x1A888, 0x1A89C},
> +       {0x1A8A8, 0x1A8AC},
> +       {0x1A8C0, 0x1A8DC},
> +       {0x1A8F0, 0x1A8FC},
> +       {0x1AE04, 0x1AE08},
> +       {0x1AE18, 0x1AE24},
> +       {0x1AF80, 0x1AF8C},
> +       {0x1AFA0, 0x1AFB4},
> +       {0x1B000, 0x1B200},
> +       {0x1B284, 0x1B288},
> +       {0x1B2D0, 0x1B2D8},
> +       {0x1B2DC, 0x1B2EC},
> +       {0x1B300, 0x1B340},
> +       {0x1B374, 0x1B378},
> +       {0x1B380, 0x1B384},
> +       {0x1B388, 0x1B38C},
> +       {0x1B404, 0x1B408},
> +       {0x1B420, 0x1B428},
> +       {0x1B440, 0x1B444},
> +       {0x1B448, 0x1B44C},
> +       {0x1B450, 0x1B458},
> +       {0x1B45C, 0x1B468},
> +       {0x1B584, 0x1B58C},
> +       {0x1B68C, 0x1B690},
> +       {0x1B6AC, 0x1B6B0},
> +       {0x1B7F0, 0x1B7F8},
> +       {0x1C800, 0x1CC00},
> +       {0x1CE00, 0x1CE04},
> +       {0x1CF80, 0x1CF84},
> +       {0x1D200, 0x1D800},
> +       {0x1E000, 0x20014},
> +       {0x20100, 0x20124},
> +       {0x21400, 0x217A8},
> +       {0x21800, 0x21BA8},
> +       {0x21C00, 0x21FA8},
> +       {0x22000, 0x223A8},
> +       {0x22400, 0x227A8},
> +       {0x22800, 0x22BA8},
> +       {0x22C00, 0x22FA8},
> +       {0x23000, 0x233A8},
> +       {0x24000, 0x24034},
> +
> +       /* EFUSE0,1,2 is disabled here
> +        * because it's state may be reset

its state

> +        *
> +        * {0x24800, 0x24804},
> +        * {0x25000, 0x25004},
> +        * {0x25800, 0x25804},
> +        */
> +
> +       {0x26000, 0x26064},
> +       {0x27000, 0x27024},
> +       {0x34000, 0x3400C},
> +       {0x34400, 0x3445C},
> +       {0x34800, 0x3485C},
> +       {0x34C00, 0x34C5C},
> +       {0x35000, 0x3505C},
> +       {0x35400, 0x3545C},
> +       {0x35800, 0x3585C},
> +       {0x35C00, 0x35C5C},
> +       {0x36000, 0x3605C},
> +       {0x38000, 0x38064},
> +       {0x38070, 0x380E0},
> +       {0x3A000, 0x3A074},
> +
> +       /* DBI windows is skipped here, it can be only accessed when pcie
> +        * is active (not in reset) and CORE_CTRL_PCIE_LTSSM_EN = 0 &&
> +        * PCIE_CTRL_APP_LTSSM_ENALBE=0.
> +        * {0x3C000 , 0x3C004},
> +        */
> +
> +       {0x40000, 0x400A4},
> +
> +       /* SI register is skiped here.
> +        * Because it will cause bus hang
> +        *
> +        * {0x50000, 0x50018},
> +        */
> +
> +       {0x80000, 0x8000C},
> +       {0x80010, 0x80020},
> +};
> +
>  static const struct ath10k_mem_section qca6174_hw30_register_sections[] = {
>         {0x800, 0x810},
>         {0x820, 0x82C},
> @@ -602,6 +873,59 @@
>         },
>  };
>
> +static const struct ath10k_mem_region qca6174_hw30_sdio_mem_regions[] = {
> +       {
> +               .type = ATH10K_MEM_REGION_TYPE_DRAM,
> +               .start = 0x400000,
> +               .len = 0xa8000,
> +               .name = "DRAM",
> +               .section_table = {
> +               .sections = NULL,
> +               .size = 0,

Indentation.

> +               },
> +       },
> +       {
> +               .type = ATH10K_MEM_REGION_TYPE_AXI,
> +               .start = 0xa0000,
> +               .len = 0x18000,
> +               .name = "AXI",
> +               .section_table = {
> +                       .sections = NULL,
> +                       .size = 0,
> +               },
> +       },
> +       {
> +               .type = ATH10K_MEM_REGION_TYPE_IRAM1,
> +               .start = 0x00980000,
> +               .len = 0x00080000,
> +               .name = "IRAM1",
> +               .section_table = {
> +               .sections = NULL,
> +               .size = 0,

Indentation

> +               },
> +       },
> +       {
> +               .type = ATH10K_MEM_REGION_TYPE_IRAM2,
> +               .start = 0x00a00000,
> +               .len = 0x00040000,
> +               .name = "IRAM2",
> +               .section_table = {
> +               .sections = NULL,
> +               .size = 0,

Indentation

> +               },
> +       },
> +       {
> +               .type = ATH10K_MEM_REGION_TYPE_REG,
> +               .start = 0x800,
> +               .len = 0x80020 - 0x800,
> +               .name = "REG_TOTAL",
> +               .section_table = {
> +                       .sections = qca6174_hw30_sdio_register_sections,
> +                       .size = ARRAY_SIZE(qca6174_hw30_sdio_register_sections),
> +               },
> +       },
> +};
> +
>  static const struct ath10k_mem_region qca6174_hw30_mem_regions[] = {
>         {
>                 .type = ATH10K_MEM_REGION_TYPE_DRAM,
> @@ -995,12 +1319,22 @@
>         {
>                 .hw_id = QCA6174_HW_3_2_VERSION,
>                 .hw_rev = ATH10K_HW_QCA6174,
> +               .bus = ATH10K_BUS_PCI,
>                 .region_table = {
>                         .regions = qca6174_hw30_mem_regions,
>                         .size = ARRAY_SIZE(qca6174_hw30_mem_regions),
>                 },
>         },
>         {
> +               .hw_id = QCA6174_HW_3_2_VERSION,
> +               .hw_rev = ATH10K_HW_QCA6174,
> +               .bus = ATH10K_BUS_SDIO,
> +               .region_table = {
> +                       .regions = qca6174_hw30_sdio_mem_regions,
> +                       .size = ARRAY_SIZE(qca6174_hw30_sdio_mem_regions),
> +               },
> +       },
> +       {
>                 .hw_id = QCA9377_HW_1_1_DEV_VERSION,
>                 .hw_rev = ATH10K_HW_QCA9377,
>                 .region_table = {
> @@ -1090,7 +1424,9 @@ const struct ath10k_hw_mem_layout *ath10k_coredump_get_mem_layout(struct ath10k
>
>         for (i = 0; i < ARRAY_SIZE(hw_mem_layouts); i++) {
>                 if (ar->target_version == hw_mem_layouts[i].hw_id &&
> -                   ar->hw_rev == hw_mem_layouts[i].hw_rev)
> +                   ar->hw_rev == hw_mem_layouts[i].hw_rev &&
> +                   (hw_mem_layouts[i].bus == ATH10K_BUS_UNDEF ||
> +                    hw_mem_layouts[i].bus == ar->hif.bus))
>                         return &hw_mem_layouts[i];
>         }
>
> diff --git a/drivers/net/wireless/ath/ath10k/coredump.h b/drivers/net/wireless/ath/ath10k/coredump.h
> index 09de419..b191746 100644
> --- a/drivers/net/wireless/ath/ath10k/coredump.h
> +++ b/drivers/net/wireless/ath/ath10k/coredump.h
> @@ -155,6 +155,7 @@ struct ath10k_mem_region {
>  struct ath10k_hw_mem_layout {
>         u32 hw_id;
>         u32 hw_rev;
> +       enum ath10k_bus bus;
>
>         struct {
>                 const struct ath10k_mem_region *regions;
> diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
> index 2ae57c1..d2cfd29 100644
> --- a/drivers/net/wireless/ath/ath10k/hw.h
> +++ b/drivers/net/wireless/ath/ath10k/hw.h
> @@ -11,6 +11,7 @@
>  #include "targaddrs.h"
>
>  enum ath10k_bus {
> +       ATH10K_BUS_UNDEF,

Maybe call this "_ANY", given that you use it to match any bus?

>         ATH10K_BUS_PCI,
>         ATH10K_BUS_AHB,
>         ATH10K_BUS_SDIO,
> diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c
> index 8ed4fbd..cb75463 100644
> --- a/drivers/net/wireless/ath/ath10k/sdio.c
> +++ b/drivers/net/wireless/ath/ath10k/sdio.c
> @@ -23,6 +23,9 @@
>  #include "targaddrs.h"
>  #include "trace.h"
>  #include "sdio.h"
> +#include "coredump.h"
> +
> +void ath10k_sdio_fw_crashed_dump(struct ath10k *ar);
>
>  /* inlined helper functions */
>
> @@ -860,8 +863,7 @@ static int ath10k_sdio_mbox_proc_cpu_intr(struct ath10k *ar)
>  out:
>         mutex_unlock(&irq_data->mtx);
>         if (cpu_int_status & MBOX_CPU_STATUS_ENABLE_ASSERT_MASK) {
> -               ath10k_err(ar, "firmware crashed!\n");
> -               queue_work(ar->workqueue, &ar->restart_work);
> +               ath10k_sdio_fw_crashed_dump(ar);
>         }
>         return ret;
>  }
> @@ -1967,6 +1969,335 @@ static SIMPLE_DEV_PM_OPS(ath10k_sdio_pm_ops, ath10k_sdio_pm_suspend,
>
>  #endif /* CONFIG_PM_SLEEP */
>
> +static int ath10k_sdio_read_host_interest_value(struct ath10k *ar,
> +                                               u32 item_offset,
> +                                               u32 *val)
> +{
> +       u32 addr;
> +       int ret;
> +
> +       addr = host_interest_item_address(item_offset);
> +
> +       ret = ath10k_sdio_hif_diag_read32(ar, addr, val);
> +
> +       if (ret)
> +               ath10k_warn(ar, "unable to read host interest offset %d value\n",
> +                           item_offset);
> +
> +       return ret;
> +}
> +
> +static int ath10k_sdio_read_mem(struct ath10k *ar, u32 address, void *buf,
> +                               u32 buf_len)
> +{
> +       u32 val;
> +       int i, ret;
> +
> +       for (i = 0; i < buf_len; i += 4) {
> +               ret = ath10k_sdio_hif_diag_read32(ar, address + i, &val);
> +               if (ret) {
> +                       ath10k_warn(ar, "unable to read mem %d value\n", address + i);
> +                       break;
> +               }
> +               memcpy(buf + i, &val, 4);
> +       }
> +
> +       return ret;
> +}
> +
> +void ath10k_sdio_check_fw_reg(struct ath10k *ar, u32 *fast_dump)
> +{
> +       int ret = 0;
> +       u32 param;
> +
> +       ret = ath10k_sdio_read_host_interest_value(ar, HI_ITEM(hi_option_flag2), &param);
> +
> +       *fast_dump = ((param & HI_OPTION_SDIO_CRASH_DUMP_ENHANCEMENT_FW)
> +                            == HI_OPTION_SDIO_CRASH_DUMP_ENHANCEMENT_FW);
> +
> +       ath10k_err(ar, "check fw reg : %x\n", param);
> +}
> +
> +static void ath10k_sdio_dump_registers(struct ath10k *ar,
> +                                      struct ath10k_fw_crash_data *crash_data,
> +                                      u32 fast_dump)
> +{
> +       u32 reg_dump_values[REG_DUMP_COUNT_QCA988X] = {};
> +       int i, ret;
> +       u32 reg_dump_area;
> +
> +       ret = ath10k_sdio_read_host_interest_value(ar, HI_ITEM(hi_failure_state),
> +                                                  &reg_dump_area);
> +       if (ret) {
> +               ath10k_err(ar, "failed to read firmware dump area: %d\n", ret);
> +               return;
> +       }
> +
> +       if (fast_dump)
> +               ret = ath10k_bmi_read_memory(ar, reg_dump_area, reg_dump_values,
> +                                            sizeof(reg_dump_values));
> +       else
> +               ret = ath10k_sdio_read_mem(ar, reg_dump_area, reg_dump_values,
> +                                          sizeof(reg_dump_values));
> +
> +       if (ret) {
> +               ath10k_err(ar, "failed to read firmware dump value: %d\n", ret);
> +               return;
> +       }
> +
> +       ath10k_err(ar, "firmware register dump:\n");
> +       for (i = 0; i < ARRAY_SIZE(reg_dump_values); i += 4)
> +               ath10k_err(ar, "[%02d]: 0x%08X 0x%08X 0x%08X 0x%08X\n",
> +                          i,
> +                          reg_dump_values[i],
> +                          reg_dump_values[i + 1],
> +                          reg_dump_values[i + 2],
> +                          reg_dump_values[i + 3]);
> +
> +       if (!crash_data)
> +               return;
> +
> +       for (i = 0; i < ARRAY_SIZE(reg_dump_values); i++)
> +               crash_data->registers[i] = __cpu_to_le32(reg_dump_values[i]);
> +}
> +
> +static int ath10k_sdio_dump_memory_section(struct ath10k *ar,
> +                                          const struct ath10k_mem_region *mem_region,
> +                                          u8 *buf, size_t buf_len)
> +{
> +       const struct ath10k_mem_section *cur_section, *next_section;
> +       unsigned int count, section_size, skip_size;
> +       int ret, i, j;
> +
> +       if (!mem_region || !buf)
> +               return 0;
> +
> +       cur_section = &mem_region->section_table.sections[0];
> +
> +       if (mem_region->start > cur_section->start) {
> +               ath10k_warn(ar, "incorrect memdump region 0x%x with section start address 0x%x.\n",
> +                           mem_region->start, cur_section->start);
> +               return 0;
> +       }
> +
> +       skip_size = cur_section->start - mem_region->start;
> +
> +       /* fill the gap between the first register section and register
> +        * start address
> +        */
> +       for (i = 0; i < skip_size; i++) {
> +               *buf = ATH10K_MAGIC_NOT_COPIED;
> +               buf++;
> +       }
> +
> +       count = 0;
> +
> +       for (i = 0; cur_section; i++) {
> +               section_size = cur_section->end - cur_section->start;
> +
> +               if (section_size <= 0) {
> +                       ath10k_warn(ar, "incorrect ramdump format with start address 0x%x and stop address 0x%x\n",
> +                                   cur_section->start,
> +                                   cur_section->end);
> +                       break;
> +               }
> +
> +               if ((i + 1) == mem_region->section_table.size) {
> +                       /* last section */
> +                       next_section = NULL;
> +                       skip_size = 0;
> +               } else {
> +                       next_section = cur_section + 1;
> +
> +                       if (cur_section->end > next_section->start) {
> +                               ath10k_warn(ar, "next ramdump section 0x%x is smaller than current end address 0x%x\n",
> +                                           next_section->start,
> +                                           cur_section->end);
> +                               break;
> +                       }
> +
> +                       skip_size = next_section->start - cur_section->end;
> +               }
> +
> +               if (buf_len < (skip_size + section_size)) {
> +                       ath10k_warn(ar, "ramdump buffer is too small: %zu\n", buf_len);
> +                       break;
> +               }
> +
> +               buf_len -= skip_size + section_size;
> +
> +               /* read section to dest memory */
> +               ret = ath10k_sdio_read_mem(ar, cur_section->start,
> +                                          buf, section_size);
> +               if (ret) {
> +                       ath10k_warn(ar, "failed to read ramdump from section 0x%x: %d\n",
> +                                   cur_section->start, ret);
> +                       break;
> +               }
> +
> +               buf += section_size;
> +               count += section_size;
> +
> +               /* fill in the gap between this section and the next */
> +               for (j = 0; j < skip_size; j++) {
> +                       *buf = ATH10K_MAGIC_NOT_COPIED;
> +                       buf++;
> +               }
> +
> +               count += skip_size;
> +
> +               if (!next_section)
> +                       /* this was the last section */
> +                       break;
> +
> +               cur_section = next_section;
> +       }
> +
> +       return count;
> +}
> +
> +/* if an error happened returns < 0, otherwise the length */
> +static int ath10k_sdio_dump_memory_generic(struct ath10k *ar,
> +                                          const struct ath10k_mem_region *current_region,
> +                                          u8 *buf,
> +                                          u32 fast_dump)
> +{
> +       int ret;
> +
> +       if (current_region->section_table.size > 0)
> +               /* Copy each section individually. */
> +               return ath10k_sdio_dump_memory_section(ar,
> +                                                     current_region,
> +                                                     buf,
> +                                                     current_region->len);
> +
> +       /* No individiual memory sections defined so we can
> +        * copy the entire memory region.
> +        */
> +       if (fast_dump)
> +               ret = ath10k_bmi_read_memory(ar,
> +                                            current_region->start,
> +                                            buf,
> +                                            current_region->len);
> +       else
> +               ret = ath10k_sdio_read_mem(ar,
> +                                          current_region->start,
> +                                          buf,
> +                                          current_region->len);
> +
> +       if (ret) {
> +               ath10k_warn(ar, "failed to copy ramdump region %s: %d\n",
> +                           current_region->name, ret);
> +               return ret;
> +       }
> +
> +       return current_region->len;
> +}
> +
> +static void ath10k_sdio_dump_memory(struct ath10k *ar,
> +                                   struct ath10k_fw_crash_data *crash_data,
> +                                   u32 fast_dump)
> +{
> +       const struct ath10k_hw_mem_layout *mem_layout;
> +       const struct ath10k_mem_region *current_region;
> +       struct ath10k_dump_ram_data_hdr *hdr;
> +       u32 count;
> +       size_t buf_len;
> +       int ret, i;
> +       u8 *buf;
> +
> +       if (!crash_data)
> +               return;
> +
> +       mem_layout = ath10k_coredump_get_mem_layout(ar);
> +       if (!mem_layout)
> +               return;
> +
> +       current_region = &mem_layout->region_table.regions[0];
> +
> +       buf = crash_data->ramdump_buf;
> +       buf_len = crash_data->ramdump_buf_len;
> +
> +       memset(buf, 0, buf_len);
> +
> +       for (i = 0; i < mem_layout->region_table.size; i++) {
> +               count = 0;
> +
> +               if (current_region->len > buf_len) {
> +                       ath10k_warn(ar, "memory region %s size %d is larger that remaining ramdump buffer size %zu\n",
> +                                   current_region->name,
> +                                   current_region->len,
> +                                   buf_len);
> +                       break;
> +               }
> +
> +               /* Reserve space for the header. */
> +               hdr = (void *)buf;
> +               buf += sizeof(*hdr);
> +               buf_len -= sizeof(*hdr);
> +
> +               ret = ath10k_sdio_dump_memory_generic(ar, current_region, buf, fast_dump);
> +
> +               ath10k_err(ar, "dump mem, name:%s, type:%d, start:0x%x, len:0x%x, size:%d, ret:0x%x\n",
> +                          current_region->name,
> +                          current_region->type,
> +                          current_region->start,
> +                          current_region->len,
> +                          current_region->section_table.size,
> +                          ret);
> +
> +               if (ret >= 0)
> +                       count = ret;
> +
> +               hdr->region_type = cpu_to_le32(current_region->type);
> +               hdr->start = cpu_to_le32(current_region->start);
> +               hdr->length = cpu_to_le32(count);
> +
> +               if (count == 0)
> +                       /* Note: the header remains, just with zero length. */
> +                       break;
> +
> +               buf += count;
> +               buf_len -= count;
> +
> +               current_region++;
> +       }
> +}
> +
> +void ath10k_sdio_fw_crashed_dump(struct ath10k *ar)
> +{
> +       struct ath10k_fw_crash_data *crash_data;
> +       char guid[UUID_STRING_LEN + 1];
> +       u32 fast_dump = 0;
> +
> +       ath10k_err(ar, "begin fw dump\n", guid);
> +
> +       ath10k_sdio_check_fw_reg(ar, &fast_dump);
> +
> +       if (fast_dump)
> +               ar->bmi.done_sent = false;
> +
> +       ar->stats.fw_crash_counter++;
> +
> +       ath10k_sdio_hif_disable_intrs(ar);
> +
> +       crash_data = ath10k_coredump_new(ar);
> +
> +       if (crash_data)
> +               scnprintf(guid, sizeof(guid), "%pUl", &crash_data->guid);
> +       else
> +               scnprintf(guid, sizeof(guid), "n/a");
> +
> +       ath10k_err(ar, "firmware crashed! (guid %s)\n", guid);
> +       ath10k_print_driver_info(ar);
> +       ath10k_sdio_dump_registers(ar, crash_data, fast_dump);
> +       ath10k_sdio_dump_memory(ar, crash_data, fast_dump);
> +
> +       ath10k_sdio_hif_enable_intrs(ar);
> +
> +       queue_work(ar->workqueue, &ar->restart_work);
> +}
> +
>  static int ath10k_sdio_probe(struct sdio_func *func,
>                              const struct sdio_device_id *id)
>  {
> diff --git a/drivers/net/wireless/ath/ath10k/targaddrs.h b/drivers/net/wireless/ath/ath10k/targaddrs.h
> index dff6c8a..c65045a 100644
> --- a/drivers/net/wireless/ath/ath10k/targaddrs.h
> +++ b/drivers/net/wireless/ath/ath10k/targaddrs.h
> @@ -334,6 +334,16 @@ struct host_interest {
>  #define HI_ACS_FLAGS_SDIO_REDUCE_TX_COMPL_FW_ACK (1 << 17)
>
>  /*
> + * If both SDIO_CRASH_DUMP_ENHANCEMENT_HOST and SDIO_CRASH_DUMP_ENHANCEMENT_FW
> + * flags are set, then crashdump upload will be done using the BMI host/target
> + * communication channel.
> + */
> +/* HOST to support using BMI dump FW memory when hit assert */
> +#define HI_OPTION_SDIO_CRASH_DUMP_ENHANCEMENT_HOST 0x400
> +/* FW to support using BMI dump FW memory when hit assert */
> +#define HI_OPTION_SDIO_CRASH_DUMP_ENHANCEMENT_FW   0x800
> +
> +/*
>   * CONSOLE FLAGS
>   *
>   * Bit Range  Meaning
> --
> 1.9.1
>

^ permalink raw reply

* mac80211_hwsim (kernel 4.18+): wmediumd + 2.4Ghz
From: Ramon Fontes @ 2019-08-27 12:06 UTC (permalink / raw)
  To: linux-wireless

Hello,

When I use 2.4Ghz band with -only one- AP (running on top of hostapd)
I get a (additional) list of frequencies at 5Ghz. When I do "iw dev ..
scan"

BSS 02:00:00:00:04:00(on sta1-wlan0) -- associated
TSF: 1566905272877856 usec (18135d, 11:27:52)
freq: 2422
beacon interval: 100 TUs
capability: ESS ShortSlotTime (0x0401)
signal: -34.00 dBm
last seen: 0 ms ago
Information elements from Probe Response frame:
SSID: simplewifi
Supported rates: 1.0* 2.0* 5.5* 11.0* 6.0 9.0 12.0 18.0
DS Parameter set: channel 3
ERP: Barker_Preamble_Mode
Extended supported rates: 24.0 36.0 48.0 54.0
Extended capabilities:
* Extended Channel Switching
* Multiple BSSID
* SSID List
* Operating Mode Notification

BSS 02:00:00:00:04:00(on sta1-wlan0)
TSF: 1566905274269230 usec (18135d, 11:27:54)
freq: 5180
beacon interval: 100 TUs
capability: ESS ShortSlotTime (0x0401)
signal: -34.00 dBm
last seen: 0 ms ago
Information elements from Probe Response frame:
SSID: simplewifi
Supported rates: 1.0* 2.0* 5.5* 11.0* 6.0 9.0 12.0 18.0
DS Parameter set: channel 3
...

BSS 02:00:00:00:04:00(on sta1-wlan0)
TSF: 1566905274269230 usec (18135d, 11:27:54)
freq: 5200
beacon interval: 100 TUs
capability: ESS ShortSlotTime (0x0401)
signal: -34.00 dBm
last seen: 0 ms ago
Information elements from Probe Response frame:
SSID: simplewifi
Supported rates: 1.0* 2.0* 5.5* 11.0* 6.0 9.0 12.0 18.0
DS Parameter set: channel 3
...

and so on (please notice that channel number and frequency)..
iw dev scan returns all the 5Ghz frequencies defined in
https://github.com/torvalds/linux/blob/master/drivers/net/wireless/mac80211_hwsim.c#L328
It happens only when wmediumd is being used. When hostapd is running
with 5Ghz it seems to work as expected, since iw returns only the AP
running at 5Ghz. In other words, the problem occurs only when hostapd
is running at 2.4Ghz.

I noticed that it happens from kernel 4.18. If I comment
https://github.com/torvalds/linux/blob/b55f3b841099e641bdb2701d361a4c304e2dbd6f/drivers/net/wireless/mac80211_hwsim.c#L2838,
iw returns only 2422Mhz. However, I couldn't find the reason for such
problem.

There could be some problem with mac80211_hwsim, or am I missing something here?

Best Regards,
Ramon Fontes

^ permalink raw reply

* Re: pull request: iwlwifi firmware updates 2019-08-23
From: Josh Boyer @ 2019-08-27 12:03 UTC (permalink / raw)
  To: Luca Coelho
  Cc: linux-firmware@kernel.org, linux-wireless@vger.kernel.org,
	linuxwifi, kyle@infradead.org, ben@decadent.org.uk, dor.shaish
In-Reply-To: <2a5e239858780618fa248bf825e10e3958c56bc9.camel@coelho.fi>

On Fri, Aug 23, 2019 at 12:45 AM Luca Coelho <luca@coelho.fi> wrote:
>
> Hi,
>
> This contains some updated firmwares for all our currently maintained
> FW binaries.
>
> Please pull or let me know if there are any issues.
>
> --
> Cheers,
> Luca.
>
>
> The following changes since commit c0fb3d9862477e31717e04e008debf6328b8980a:
>
>   check_whence: Add copy-firmware.sh to the list of ignored files (2019-08-21 08:03:43 -0400)
>
> are available in the Git repository at:
>
>   git://git.kernel.org/pub/scm/linux/kernel/git/iwlwifi/linux-firmware.git tags/iwlwifi-fw-2019-08-23
>
> for you to fetch changes up to 40e4162adfc91390f6fbbd8269f9439832af1dde:
>
>   iwlwifi: update FWs to core45-152 release (2019-08-23 07:35:14 +0300)
>
> ----------------------------------------------------------------
> Update iwlwifi firmwares to Core45-152
>
> ----------------------------------------------------------------
> Luca Coelho (1):
>       iwlwifi: update FWs to core45-152 release
>
>  WHENCE                            |  22 +++++++++++-----------
>  iwlwifi-8000C-36.ucode            | Bin 2401356 -> 2401356 bytes
>  iwlwifi-8265-36.ucode             | Bin 2414592 -> 2414592 bytes
>  iwlwifi-9000-pu-b0-jf-b0-46.ucode | Bin 1467952 -> 1462068 bytes
>  iwlwifi-9260-th-b0-jf-b0-46.ucode | Bin 1469012 -> 1463820 bytes
>  iwlwifi-Qu-b0-hr-b0-48.ucode      | Bin 1106204 -> 1106228 bytes
>  iwlwifi-Qu-b0-jf-b0-48.ucode      | Bin 1052772 -> 1052796 bytes
>  iwlwifi-Qu-c0-hr-b0-48.ucode      | Bin 1106224 -> 1106248 bytes
>  iwlwifi-Qu-c0-jf-b0-48.ucode      | Bin 1052792 -> 1052816 bytes
>  iwlwifi-QuZ-a0-hr-b0-48.ucode     | Bin 1105644 -> 1105668 bytes
>  iwlwifi-QuZ-a0-jf-b0-48.ucode     | Bin 1052584 -> 1052608 bytes
>  iwlwifi-cc-a0-48.ucode            | Bin 1096680 -> 1096704 bytes
>  12 files changed, 11 insertions(+), 11 deletions(-)

Pulled and pushed out.

josh

^ permalink raw reply

* Re: [PATCH v2 1/7] ath10k: enable RX bundle receive for sdio
From: Nicolas Boichat @ 2019-08-27 11:23 UTC (permalink / raw)
  To: Wen Gong; +Cc: ath10k, open list:NETWORKING DRIVERS (WIRELESS)
In-Reply-To: <1566903707-27536-2-git-send-email-wgong@codeaurora.org>

On Tue, Aug 27, 2019 at 7:02 PM Wen Gong <wgong@codeaurora.org> wrote:
>
> From: Alagu Sankar <alagusankar@silex-india.com>
>
> The existing implementation of initiating multiple sdio transfers for
> receive bundling is slowing down the receive speed. Combining the
> transfers using a bundle method would be ideal.
>
> The transmission utilization ratio for sdio bus for small packet is
> slow, because the space and time cost for sdio bus is same for large
> length packet and small length packet. So the speed of data for large
> length packet is higher than small length.
>
> Test result of different length of data:
> data packet(byte)   cost time(us)   calculated rate(Mbps)
>       256               28                73
>       512               33               124
>      1024               35               234
>      1792               45               318
>     14336              168               682
>     28672              333               688
>     57344              660               695
>
> Tested with QCA6174 SDIO with firmware
> WLAN.RMH.4.4.1-00007-QCARMSWP-1.
>
> Signed-off-by: Alagu Sankar <alagusankar@silex-india.com>
> Signed-off-by: Wen Gong <wgong@codeaurora.org>
> ---
>  drivers/net/wireless/ath/ath10k/sdio.c | 112 +++++++++++++++++++++++----------
>  drivers/net/wireless/ath/ath10k/sdio.h |   7 ++-
>  2 files changed, 85 insertions(+), 34 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c
> index 8ed4fbd..eacb4d5 100644
> --- a/drivers/net/wireless/ath/ath10k/sdio.c
> +++ b/drivers/net/wireless/ath/ath10k/sdio.c
> @@ -24,6 +24,9 @@
>  #include "trace.h"
>  #include "sdio.h"
>
> +#define ATH10K_SDIO_DMA_BUF_SIZE       (32 * 1024)
> +#define ATH10K_SDIO_VSG_BUF_SIZE       (32 * 1024)
> +
>  /* inlined helper functions */
>
>  static inline int ath10k_sdio_calc_txrx_padded_len(struct ath10k_sdio *ar_sdio,
> @@ -381,16 +384,11 @@ static int ath10k_sdio_mbox_rx_process_packet(struct ath10k *ar,
>         struct ath10k_htc_hdr *htc_hdr = (struct ath10k_htc_hdr *)skb->data;
>         bool trailer_present = htc_hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT;
>         enum ath10k_htc_ep_id eid;
> -       u16 payload_len;
>         u8 *trailer;
>         int ret;
>
> -       payload_len = le16_to_cpu(htc_hdr->len);
> -       skb->len = payload_len + sizeof(struct ath10k_htc_hdr);
> -
>         if (trailer_present) {
> -               trailer = skb->data + sizeof(*htc_hdr) +
> -                         payload_len - htc_hdr->trailer_len;
> +               trailer = skb->data + skb->len - htc_hdr->trailer_len;
>
>                 eid = pipe_id_to_eid(htc_hdr->eid);
>
> @@ -489,11 +487,11 @@ static int ath10k_sdio_mbox_rx_process_packets(struct ath10k *ar,
>         return ret;
>  }
>
> -static int ath10k_sdio_mbox_alloc_pkt_bundle(struct ath10k *ar,
> -                                            struct ath10k_sdio_rx_data *rx_pkts,
> -                                            struct ath10k_htc_hdr *htc_hdr,
> -                                            size_t full_len, size_t act_len,
> -                                            size_t *bndl_cnt)
> +static int ath10k_sdio_mbox_alloc_bundle(struct ath10k *ar,
> +                                        struct ath10k_sdio_rx_data *rx_pkts,
> +                                        struct ath10k_htc_hdr *htc_hdr,
> +                                        size_t full_len, size_t act_len,
> +                                        size_t *bndl_cnt)
>  {
>         int ret, i;
>
> @@ -534,6 +532,7 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar,
>         size_t full_len, act_len;
>         bool last_in_bundle;
>         int ret, i;
> +       int pkt_cnt = 0;
>
>         if (n_lookaheads > ATH10K_SDIO_MAX_RX_MSGS) {
>                 ath10k_warn(ar,
> @@ -577,20 +576,22 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar,
>                          */
>                         size_t bndl_cnt;
>
> -                       ret = ath10k_sdio_mbox_alloc_pkt_bundle(ar,
> -                                                               &ar_sdio->rx_pkts[i],
> -                                                               htc_hdr,
> -                                                               full_len,
> -                                                               act_len,
> -                                                               &bndl_cnt);
> +                       struct ath10k_sdio_rx_data *rx_pkts =
> +                               &ar_sdio->rx_pkts[pkt_cnt];
> +
> +                       ret = ath10k_sdio_mbox_alloc_bundle(ar,
> +                                                           rx_pkts,
> +                                                           htc_hdr,
> +                                                           full_len,
> +                                                           act_len,
> +                                                           &bndl_cnt);
>
>                         if (ret) {
>                                 ath10k_warn(ar, "alloc_bundle error %d\n", ret);
>                                 goto err;
>                         }
>
> -                       n_lookaheads += bndl_cnt;
> -                       i += bndl_cnt;
> +                       pkt_cnt += bndl_cnt;
>                         /*Next buffer will be the last in the bundle */
>                         last_in_bundle = true;
>                 }
> @@ -602,7 +603,7 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar,
>                 if (htc_hdr->flags & ATH10K_HTC_FLAGS_RECV_1MORE_BLOCK)
>                         full_len += ATH10K_HIF_MBOX_BLOCK_SIZE;
>
> -               ret = ath10k_sdio_mbox_alloc_rx_pkt(&ar_sdio->rx_pkts[i],
> +               ret = ath10k_sdio_mbox_alloc_rx_pkt(&ar_sdio->rx_pkts[pkt_cnt],
>                                                     act_len,
>                                                     full_len,
>                                                     last_in_bundle,
> @@ -611,9 +612,10 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar,
>                         ath10k_warn(ar, "alloc_rx_pkt error %d\n", ret);
>                         goto err;
>                 }
> +               pkt_cnt++;
>         }
>
> -       ar_sdio->n_rx_pkts = i;
> +       ar_sdio->n_rx_pkts = pkt_cnt;
>
>         return 0;
>
> @@ -627,41 +629,78 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar,
>         return ret;
>  }
>
> -static int ath10k_sdio_mbox_rx_packet(struct ath10k *ar,
> -                                     struct ath10k_sdio_rx_data *pkt)
> +static int ath10k_sdio_mbox_rx_fetch(struct ath10k *ar)
>  {
>         struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
> +       struct ath10k_sdio_rx_data *pkt = &ar_sdio->rx_pkts[0];
>         struct sk_buff *skb = pkt->skb;
> +       struct ath10k_htc_hdr *htc_hdr;
>         int ret;
>
>         ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr,
>                                  skb->data, pkt->alloc_len);
> -       pkt->status = ret;
> -       if (!ret)
> +
> +       if (ret) {
> +               ar_sdio->n_rx_pkts = 0;
> +               ath10k_sdio_mbox_free_rx_pkt(pkt);
> +       } else {
> +               htc_hdr = (struct ath10k_htc_hdr *)skb->data;
> +               pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
> +               pkt->status = ret;
>                 skb_put(skb, pkt->act_len);
> +       }
>
>         return ret;
>  }
>
> -static int ath10k_sdio_mbox_rx_fetch(struct ath10k *ar)
> +static int ath10k_sdio_mbox_rx_fetch_bundle(struct ath10k *ar)
>  {
>         struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
> +       struct ath10k_sdio_rx_data *pkt;
> +       struct ath10k_htc_hdr *htc_hdr;
>         int ret, i;
> +       u32 pkt_offset, virt_pkt_len;
>
> +       virt_pkt_len = 0;
>         for (i = 0; i < ar_sdio->n_rx_pkts; i++) {
> -               ret = ath10k_sdio_mbox_rx_packet(ar,
> -                                                &ar_sdio->rx_pkts[i]);
> -               if (ret)
> +               virt_pkt_len += ar_sdio->rx_pkts[i].alloc_len;
> +       }
> +
> +       if (virt_pkt_len < ATH10K_SDIO_DMA_BUF_SIZE) {
> +               ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr,
> +                                        ar_sdio->vsg_buffer, virt_pkt_len);
> +               if (ret) {
> +                       i = 0;
>                         goto err;
> +               }
> +       } else {
> +               ath10k_err(ar, "size exceeding limit %d\n", virt_pkt_len);
> +       }
> +
> +       pkt_offset = 0;
> +       for (i = 0; i < ar_sdio->n_rx_pkts; i++) {
> +               struct sk_buff *skb = ar_sdio->rx_pkts[i].skb;
> +
> +               pkt = &ar_sdio->rx_pkts[i];
> +               htc_hdr = (struct ath10k_htc_hdr *)(ar_sdio->vsg_buffer + pkt_offset);
> +               pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
> +
> +               memcpy(skb->data, ar_sdio->vsg_buffer + pkt_offset,
> +                      pkt->act_len);
> +               pkt->status = 0;
> +               skb_put(skb, pkt->act_len);

Quite a bit of repeated code here, and now that memcpy and skb_put
work on the same length, you can use the function I suggested in v1:
skb_put_data .

I'd replace all the above with:
pkt = &ar_sdio->rx_pkts[i];

htc_hdr = (struct ath10k_htc_hdr *)(ar_sdio->vsg_buffer + pkt_offset);
pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);

skb_put_data(pkt->skb, htc_hdr, pkt->act_len);
pkt->status = 0;

> +               pkt_offset += pkt->alloc_len;
>         }
>
>         return 0;
>
>  err:
>         /* Free all packets that was not successfully fetched. */
> -       for (; i < ar_sdio->n_rx_pkts; i++)
> +       for (i = 0; i < ar_sdio->n_rx_pkts; i++)
>                 ath10k_sdio_mbox_free_rx_pkt(&ar_sdio->rx_pkts[i]);
>
> +       ar_sdio->n_rx_pkts = 0;
> +
>         return ret;
>  }
>
> @@ -704,7 +743,10 @@ static int ath10k_sdio_mbox_rxmsg_pending_handler(struct ath10k *ar,
>                          */
>                         *done = false;
>
> -               ret = ath10k_sdio_mbox_rx_fetch(ar);
> +               if (ar_sdio->n_rx_pkts > 1)
> +                       ret = ath10k_sdio_mbox_rx_fetch_bundle(ar);
> +               else
> +                       ret = ath10k_sdio_mbox_rx_fetch(ar);
>
>                 /* Process fetched packets. This will potentially update
>                  * n_lookaheads depending on if the packets contain lookahead
> @@ -1112,7 +1154,7 @@ static int ath10k_sdio_bmi_get_rx_lookahead(struct ath10k *ar)
>                                          MBOX_HOST_INT_STATUS_ADDRESS,
>                                          &rx_word);
>                 if (ret) {
> -                       ath10k_warn(ar, "unable to read RX_LOOKAHEAD_VALID: %d\n", ret);
> +                       ath10k_warn(ar, "unable to read rx_lookahd: %d\n", ret);
>                         return ret;
>                 }
>
> @@ -2007,6 +2049,12 @@ static int ath10k_sdio_probe(struct sdio_func *func,
>                 goto err_core_destroy;
>         }
>
> +       ar_sdio->vsg_buffer = devm_kmalloc(ar->dev, ATH10K_SDIO_VSG_BUF_SIZE, GFP_KERNEL);
> +       if (!ar_sdio->vsg_buffer) {
> +               ret = -ENOMEM;
> +               goto err_core_destroy;
> +       }
> +
>         ar_sdio->irq_data.irq_en_reg =
>                 devm_kzalloc(ar->dev, sizeof(struct ath10k_sdio_irq_enable_regs),
>                              GFP_KERNEL);
> diff --git a/drivers/net/wireless/ath/ath10k/sdio.h b/drivers/net/wireless/ath/ath10k/sdio.h
> index b8c7ac0..4896eca 100644
> --- a/drivers/net/wireless/ath/ath10k/sdio.h
> +++ b/drivers/net/wireless/ath/ath10k/sdio.h
> @@ -138,8 +138,8 @@ struct ath10k_sdio_irq_proc_regs {
>         u8 rx_lookahead_valid;
>         u8 host_int_status2;
>         u8 gmbox_rx_avail;
> -       __le32 rx_lookahead[2];
> -       __le32 rx_gmbox_lookahead_alias[2];
> +       __le32 rx_lookahead[2 * ATH10K_HIF_MBOX_NUM_MAX];
> +       __le32 int_status_enable;
>  };
>
>  struct ath10k_sdio_irq_enable_regs {
> @@ -196,6 +196,9 @@ struct ath10k_sdio {
>         struct ath10k *ar;
>         struct ath10k_sdio_irq_data irq_data;
>
> +       /* temporary buffer for sdio read */
> +       u8 *vsg_buffer;
> +
>         /* temporary buffer for BMI requests */
>         u8 *bmi_buf;
>
> --
> 1.9.1
>

^ permalink raw reply

* [PATCH v2 0/7] ath10k: improve throughout of tcp/udp TX/RX of sdio
From: Wen Gong @ 2019-08-27 11:01 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless

The bottleneck of throughout on sdio chip is the bus bandwidth, to the
patches are all to increase the use ratio of sdio bus.

                      udp-rx    udp-tx    tcp-rx    tcp-tx
without patches(Mbps)  320        180       170       151
with patches(Mbps)     450        410       400       320

These patches only affect sdio bus chip, explanation is mentioned in each
patch's commit log.

Alagu Sankar (1):
  ath10k: enable RX bundle receive for sdio
v2: fix incorrect skb tail of rx bundle in ath10k_sdio_mbox_rx_process_packet

Wen Gong (6):
  ath10k: change max RX bundle size from 8 to 32 for sdio
v2: change macro HTC_GET_BUNDLE_COUNT

  ath10k: add workqueue for RX path of sdio
  ath10k: disable TX complete indication of htt for sdio
v2: change some code style

  ath10k: add htt TX bundle for sdio
  ath10k: enable alt data of TX path for sdio
  ath10k: enable napi on RX path for sdio

 drivers/net/wireless/ath/ath10k/core.c   |  36 ++-
 drivers/net/wireless/ath/ath10k/core.h   |   4 +-
 drivers/net/wireless/ath/ath10k/hif.h    |   9 +
 drivers/net/wireless/ath/ath10k/htc.c    | 374 ++++++++++++++++++++++++++++---
 drivers/net/wireless/ath/ath10k/htc.h    |  41 +++-
 drivers/net/wireless/ath/ath10k/htt.c    |  15 ++
 drivers/net/wireless/ath/ath10k/htt.h    |  20 +-
 drivers/net/wireless/ath/ath10k/htt_rx.c |  82 ++++++-
 drivers/net/wireless/ath/ath10k/htt_tx.c |  37 ++-
 drivers/net/wireless/ath/ath10k/hw.h     |   2 +-
 drivers/net/wireless/ath/ath10k/sdio.c   | 292 +++++++++++++++++++++---
 drivers/net/wireless/ath/ath10k/sdio.h   |  31 ++-
 12 files changed, 850 insertions(+), 93 deletions(-)

-- 
1.9.1


^ permalink raw reply

* [PATCH v2 5/7] ath10k: add htt TX bundle for sdio
From: Wen Gong @ 2019-08-27 11:01 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless
In-Reply-To: <1566903707-27536-1-git-send-email-wgong@codeaurora.org>

The transmission utilization ratio for sdio bus for small packet is
slow, because the space and time cost for sdio bus is same for large
length packet and small length packet. So the speed of data for large
length packet is higher than small length.

Test result of different length of data:

data packet(byte)   cost time(us)   calculated rate(Mbps)
      256               28                73
      512               33               124
     1024               35               234
     1792               45               318
    14336              168               682
    28672              333               688
    57344              660               695

This patch change the TX packet from single packet to a large length
bundle packet, max size is 32, it results in significant performance
improvement on TX path.

This patch only effect sdio chip, it will not effect PCI, SNOC etc.
It only enable bundle for sdio chip.

Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00007-QCARMSWP-1.

Signed-off-by: Wen Gong <wgong@codeaurora.org>
---
 drivers/net/wireless/ath/ath10k/core.c   |  14 +-
 drivers/net/wireless/ath/ath10k/core.h   |   4 +-
 drivers/net/wireless/ath/ath10k/htc.c    | 353 ++++++++++++++++++++++++++++---
 drivers/net/wireless/ath/ath10k/htc.h    |  21 +-
 drivers/net/wireless/ath/ath10k/htt.c    |   8 +
 drivers/net/wireless/ath/ath10k/htt.h    |   4 +
 drivers/net/wireless/ath/ath10k/htt_rx.c |   1 +
 drivers/net/wireless/ath/ath10k/htt_tx.c |   9 +-
 8 files changed, 376 insertions(+), 38 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 762bba0..351f4ed 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -3194,6 +3194,11 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
 	if (!ar->workqueue_aux)
 		goto err_free_wq;
 
+	ar->workqueue_tx_complete =
+		create_singlethread_workqueue("ath10k_tx_complete_wq");
+	if (!ar->workqueue_tx_complete)
+		goto err_free_aux_wq;
+
 	mutex_init(&ar->conf_mutex);
 	mutex_init(&ar->dump_mutex);
 	spin_lock_init(&ar->data_lock);
@@ -3219,7 +3224,7 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
 
 	ret = ath10k_coredump_create(ar);
 	if (ret)
-		goto err_free_aux_wq;
+		goto err_free_tx_complete;
 
 	ret = ath10k_debug_create(ar);
 	if (ret)
@@ -3229,12 +3234,12 @@ struct ath10k *ath10k_core_create(size_t priv_size, struct device *dev,
 
 err_free_coredump:
 	ath10k_coredump_destroy(ar);
-
+err_free_tx_complete:
+	destroy_workqueue(ar->workqueue_tx_complete);
 err_free_aux_wq:
 	destroy_workqueue(ar->workqueue_aux);
 err_free_wq:
 	destroy_workqueue(ar->workqueue);
-
 err_free_mac:
 	ath10k_mac_destroy(ar);
 
@@ -3250,6 +3255,9 @@ void ath10k_core_destroy(struct ath10k *ar)
 	flush_workqueue(ar->workqueue_aux);
 	destroy_workqueue(ar->workqueue_aux);
 
+	flush_workqueue(ar->workqueue_tx_complete);
+	destroy_workqueue(ar->workqueue_tx_complete);
+
 	ath10k_debug_destroy(ar);
 	ath10k_coredump_destroy(ar);
 	ath10k_htt_tx_destroy(&ar->htt);
diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index 4d7db07..be9eb37 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -1079,7 +1079,7 @@ struct ath10k {
 	struct workqueue_struct *workqueue;
 	/* Auxiliary workqueue */
 	struct workqueue_struct *workqueue_aux;
-
+	struct workqueue_struct *workqueue_tx_complete;
 	/* prevents concurrent FW reconfiguration */
 	struct mutex conf_mutex;
 
@@ -1120,6 +1120,8 @@ struct ath10k {
 
 	struct work_struct register_work;
 	struct work_struct restart_work;
+	struct work_struct bundle_tx_work;
+	struct work_struct tx_complete_work;
 
 	/* cycle count is reported twice for each visited channel during scan.
 	 * access protected by data_lock
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c
index 4c6cdc2..e0eb5f0 100644
--- a/drivers/net/wireless/ath/ath10k/htc.c
+++ b/drivers/net/wireless/ath/ath10k/htc.c
@@ -51,10 +51,12 @@ void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep,
 				     struct sk_buff *skb)
 {
 	struct ath10k *ar = ep->htc->ar;
+	struct ath10k_htc_hdr *hdr;
 
 	ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: ep %d skb %pK\n", __func__,
 		   ep->eid, skb);
 
+	hdr = (struct ath10k_htc_hdr *)skb->data;
 	ath10k_htc_restore_tx_skb(ep->htc, skb);
 
 	if (!ep->ep_ops.ep_tx_complete) {
@@ -63,6 +65,11 @@ void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep,
 		return;
 	}
 
+	if (hdr->flags & ATH10K_HTC_FLAG_SEND_BUNDLE) {
+		dev_kfree_skb_any(skb);
+		return;
+	}
+
 	ep->ep_ops.ep_tx_complete(ep->htc->ar, skb);
 }
 EXPORT_SYMBOL(ath10k_htc_notify_tx_completion);
@@ -78,7 +85,7 @@ static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep,
 	hdr->eid = ep->eid;
 	hdr->len = __cpu_to_le16(skb->len - sizeof(*hdr));
 	hdr->flags = 0;
-	if (ep->tx_credit_flow_enabled)
+	if (ep->tx_credit_flow_enabled && !ep->bundle_tx)
 		hdr->flags |= ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE;
 
 	spin_lock_bh(&ep->htc->tx_lock);
@@ -86,6 +93,59 @@ static void ath10k_htc_prepare_tx_skb(struct ath10k_htc_ep *ep,
 	spin_unlock_bh(&ep->htc->tx_lock);
 }
 
+static int ath10k_htc_consume_credit(struct ath10k_htc_ep *ep,
+				     unsigned int len,
+				     bool consume)
+{
+	int credits;
+	struct ath10k_htc *htc = ep->htc;
+	struct ath10k *ar = htc->ar;
+	enum ath10k_htc_ep_id eid = ep->eid;
+
+	if (ep->tx_credit_flow_enabled) {
+		credits = DIV_ROUND_UP(len, ep->tx_credit_size);
+		spin_lock_bh(&htc->tx_lock);
+
+		if (ep->tx_credits < credits) {
+			ath10k_dbg(ar, ATH10K_DBG_HTC,
+				   "htc insufficient credits ep %d required %d available %d consume %d\n",
+				   eid, credits, ep->tx_credits, consume);
+			spin_unlock_bh(&htc->tx_lock);
+			return -EAGAIN;
+		}
+		if (consume) {
+			ep->tx_credits -= credits;
+			ath10k_dbg(ar, ATH10K_DBG_HTC,
+				   "htc ep %d consumed %d credits (total %d)\n",
+				   eid, credits, ep->tx_credits);
+		}
+		spin_unlock_bh(&htc->tx_lock);
+	}
+
+	return 0;
+}
+
+static void ath10k_htc_release_credit(struct ath10k_htc_ep *ep, unsigned int len)
+{
+	int credits;
+	struct ath10k_htc *htc = ep->htc;
+	struct ath10k *ar = htc->ar;
+	enum ath10k_htc_ep_id eid = ep->eid;
+
+	if (ep->tx_credit_flow_enabled) {
+		credits = DIV_ROUND_UP(len, ep->tx_credit_size);
+		spin_lock_bh(&htc->tx_lock);
+		ep->tx_credits += credits;
+		ath10k_dbg(ar, ATH10K_DBG_HTC,
+			   "htc ep %d reverted %d credits back (total %d)\n",
+			   eid, credits, ep->tx_credits);
+		spin_unlock_bh(&htc->tx_lock);
+
+		if (ep->ep_ops.ep_tx_credits)
+			ep->ep_ops.ep_tx_credits(htc->ar);
+	}
+}
+
 int ath10k_htc_send(struct ath10k_htc *htc,
 		    enum ath10k_htc_ep_id eid,
 		    struct sk_buff *skb)
@@ -95,8 +155,8 @@ int ath10k_htc_send(struct ath10k_htc *htc,
 	struct ath10k_skb_cb *skb_cb = ATH10K_SKB_CB(skb);
 	struct ath10k_hif_sg_item sg_item;
 	struct device *dev = htc->ar->dev;
-	int credits = 0;
 	int ret;
+	unsigned int skb_len;
 
 	if (htc->ar->state == ATH10K_STATE_WEDGED)
 		return -ECOMM;
@@ -108,23 +168,10 @@ int ath10k_htc_send(struct ath10k_htc *htc,
 
 	skb_push(skb, sizeof(struct ath10k_htc_hdr));
 
-	if (ep->tx_credit_flow_enabled) {
-		credits = DIV_ROUND_UP(skb->len, htc->target_credit_size);
-		spin_lock_bh(&htc->tx_lock);
-		if (ep->tx_credits < credits) {
-			ath10k_dbg(ar, ATH10K_DBG_HTC,
-				   "htc insufficient credits ep %d required %d available %d\n",
-				   eid, credits, ep->tx_credits);
-			spin_unlock_bh(&htc->tx_lock);
-			ret = -EAGAIN;
-			goto err_pull;
-		}
-		ep->tx_credits -= credits;
-		ath10k_dbg(ar, ATH10K_DBG_HTC,
-			   "htc ep %d consumed %d credits (total %d)\n",
-			   eid, credits, ep->tx_credits);
-		spin_unlock_bh(&htc->tx_lock);
-	}
+	skb_len = skb->len;
+	ret = ath10k_htc_consume_credit(ep, skb_len, true);
+	if (ret)
+		goto err_pull;
 
 	ath10k_htc_prepare_tx_skb(ep, skb);
 
@@ -155,17 +202,7 @@ int ath10k_htc_send(struct ath10k_htc *htc,
 	if (ar->bus_param.dev_type != ATH10K_DEV_TYPE_HL)
 		dma_unmap_single(dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE);
 err_credits:
-	if (ep->tx_credit_flow_enabled) {
-		spin_lock_bh(&htc->tx_lock);
-		ep->tx_credits += credits;
-		ath10k_dbg(ar, ATH10K_DBG_HTC,
-			   "htc ep %d reverted %d credits back (total %d)\n",
-			   eid, credits, ep->tx_credits);
-		spin_unlock_bh(&htc->tx_lock);
-
-		if (ep->ep_ops.ep_tx_credits)
-			ep->ep_ops.ep_tx_credits(htc->ar);
-	}
+	ath10k_htc_release_credit(ep, skb_len);
 err_pull:
 	skb_pull(skb, sizeof(struct ath10k_htc_hdr));
 	return ret;
@@ -581,6 +618,258 @@ static u8 ath10k_htc_get_credit_allocation(struct ath10k_htc *htc,
 	return allocation;
 }
 
+static int ath10k_htc_send_bundle(struct ath10k_htc_ep *ep,
+				  struct sk_buff *bundle_skb,
+				  struct sk_buff_head *tx_save_head)
+{
+	struct ath10k_hif_sg_item sg_item;
+	struct ath10k_htc *htc = ep->htc;
+	struct ath10k *ar = htc->ar;
+	struct sk_buff *skb;
+	int ret = 0;
+	int cn = 0;
+	unsigned int skb_len;
+
+	ath10k_dbg(ar, ATH10K_DBG_HTC, "bundle skb: len:%d\n", bundle_skb->len);
+	skb_len = bundle_skb->len;
+	ret = ath10k_htc_consume_credit(ep, skb_len, true);
+
+	if (!ret) {
+		sg_item.transfer_id = ep->eid;
+		sg_item.transfer_context = bundle_skb;
+		sg_item.vaddr = bundle_skb->data;
+		sg_item.len = bundle_skb->len;
+
+		ret = ath10k_hif_tx_sg(htc->ar, ep->ul_pipe_id, &sg_item, 1);
+		if (ret)
+			ath10k_htc_release_credit(ep, skb_len);
+	}
+
+	if (ret)
+		dev_kfree_skb_any(bundle_skb);
+
+	while (true) {
+		skb = skb_dequeue_tail(tx_save_head);
+		if (!skb)
+			break;
+
+		cn++;
+		if (ret) {
+			skb_pull(skb, sizeof(struct ath10k_htc_hdr));
+			skb_queue_head(&ep->tx_req_head, skb);
+		} else {
+			skb_queue_tail(&ep->tx_complete_head, skb);
+		}
+	}
+
+	if (!ret)
+		queue_work(ar->workqueue_tx_complete, &ar->tx_complete_work);
+
+	ath10k_dbg(ar, ATH10K_DBG_HTC,
+		   "bundle tx status:%d, eid:%d, req count:%d, count:%d, len:%d\n",
+		   ret, ep->eid, skb_queue_len(&ep->tx_req_head), cn, bundle_skb->len);
+	return ret;
+}
+
+static void ath10k_htc_send_one_skb(struct ath10k_htc_ep *ep, struct sk_buff *skb)
+{
+	struct ath10k_htc *htc = ep->htc;
+	struct ath10k *ar = htc->ar;
+	int ret;
+
+	ret = ath10k_htc_send(htc, ep->eid, skb);
+
+	if (ret)
+		skb_queue_head(&ep->tx_req_head, skb);
+
+	ath10k_dbg(ar, ATH10K_DBG_HTC, "tx one status:%d, eid:%d, len:%d, pending count:%d\n",
+		   ret, ep->eid, skb->len, skb_queue_len(&ep->tx_req_head));
+}
+
+static int ath10k_htc_send_bundle_skbs(struct ath10k_htc_ep *ep)
+{
+	int ret = 0;
+	struct ath10k_htc *htc = ep->htc;
+	struct sk_buff *bundle_skb, *skb;
+	struct sk_buff_head tx_save_head;
+	struct ath10k_htc_hdr *hdr;
+	u8 *bundle_buf;
+	int credit_pad, credit_remainder, trans_len, bundles_left = 0;
+
+	if (htc->ar->state == ATH10K_STATE_WEDGED)
+		return -ECOMM;
+
+	if (ep->tx_credit_flow_enabled &&
+	    ep->tx_credits < HTC_HOST_MIN_CREDIT_PER_TX_BUNDLE)
+		return 0;
+
+	bundles_left = HTC_HOST_MAX_MSG_PER_TX_BUNDLE * ep->tx_credit_size;
+	bundle_skb = dev_alloc_skb(bundles_left);
+
+	if (!bundle_skb)
+		return -ENOMEM;
+
+	bundle_buf = bundle_skb->data;
+	skb_queue_head_init(&tx_save_head);
+
+	while (true) {
+		skb = skb_dequeue(&ep->tx_req_head);
+		if (!skb)
+			break;
+
+		credit_pad = 0;
+		trans_len = skb->len + sizeof(*hdr);
+		credit_remainder = trans_len % ep->tx_credit_size;
+
+		if (credit_remainder != 0) {
+			credit_pad = ep->tx_credit_size - credit_remainder;
+			trans_len += credit_pad;
+		}
+
+		ret = ath10k_htc_consume_credit(ep,
+						bundle_buf + trans_len - bundle_skb->data,
+						false);
+		if (ret) {
+			skb_queue_head(&ep->tx_req_head, skb);
+			break;
+		}
+
+		if (bundles_left < trans_len) {
+			bundle_skb->len = bundle_buf - bundle_skb->data;
+			ret = ath10k_htc_send_bundle(ep, bundle_skb, &tx_save_head);
+
+			if (ret) {
+				skb_queue_head(&ep->tx_req_head, skb);
+				return ret;
+			}
+
+			if (skb_queue_len(&ep->tx_req_head) == 0) {
+				ath10k_htc_send_one_skb(ep, skb);
+				return ret;
+			}
+
+			if (ep->tx_credit_flow_enabled &&
+			    ep->tx_credits < HTC_HOST_MIN_CREDIT_PER_TX_BUNDLE) {
+				skb_queue_head(&ep->tx_req_head, skb);
+				return 0;
+			}
+
+			bundles_left =
+				HTC_HOST_MAX_MSG_PER_TX_BUNDLE * ep->tx_credit_size;
+			bundle_skb = dev_alloc_skb(bundles_left);
+
+			if (!bundle_skb) {
+				skb_queue_head(&ep->tx_req_head, skb);
+				return -ENOMEM;
+			}
+			bundle_buf = bundle_skb->data;
+			skb_queue_head_init(&tx_save_head);
+		}
+
+		skb_push(skb, sizeof(struct ath10k_htc_hdr));
+		ath10k_htc_prepare_tx_skb(ep, skb);
+
+		memcpy(bundle_buf, skb->data, skb->len);
+		hdr = (struct ath10k_htc_hdr *)bundle_buf;
+		hdr->flags |= ATH10K_HTC_FLAG_SEND_BUNDLE;
+		hdr->pad_len = __cpu_to_le16(credit_pad);
+		bundle_buf += trans_len;
+		bundles_left -= trans_len;
+		skb_queue_tail(&tx_save_head, skb);
+	}
+
+	if (bundle_buf != bundle_skb->data) {
+		bundle_skb->len = bundle_buf - bundle_skb->data;
+		ret = ath10k_htc_send_bundle(ep, bundle_skb, &tx_save_head);
+	} else {
+		dev_kfree_skb_any(bundle_skb);
+	}
+
+	return ret;
+}
+
+static void ath10k_htc_bundle_tx_work(struct work_struct *work)
+{
+	struct ath10k *ar = container_of(work, struct ath10k, bundle_tx_work);
+	int i;
+	struct ath10k_htc_ep *ep;
+	enum ath10k_htc_ep_id eid;
+	struct sk_buff *skb;
+
+	for (i = 0; i < ARRAY_SIZE(ar->htc.endpoint); i++) {
+		ep = &ar->htc.endpoint[i];
+		eid = ep->eid;
+		if (ep->bundle_tx) {
+			ath10k_dbg(ar, ATH10K_DBG_HTC, "bundle tx work, eid:%d, count:%d\n",
+				   ep->eid, skb_queue_len(&ep->tx_req_head));
+
+			if (skb_queue_len(&ep->tx_req_head) >=
+			    HTC_HOST_MIN_MSG_PER_TX_BUNDLE) {
+				ath10k_htc_send_bundle_skbs(ep);
+			} else {
+				skb = skb_dequeue(&ep->tx_req_head);
+
+				if (!skb)
+					continue;
+				ath10k_htc_send_one_skb(ep, skb);
+			}
+		}
+	}
+}
+
+static void ath10k_htc_tx_complete_work(struct work_struct *work)
+{
+	struct ath10k *ar = container_of(work, struct ath10k, tx_complete_work);
+	int i;
+	struct ath10k_htc_ep *ep;
+	enum ath10k_htc_ep_id eid;
+	struct sk_buff *skb;
+
+	for (i = 0; i < ARRAY_SIZE(ar->htc.endpoint); i++) {
+		ep = &ar->htc.endpoint[i];
+		eid = ep->eid;
+		if (ep->bundle_tx && eid == ar->htt.eid) {
+			ath10k_dbg(ar, ATH10K_DBG_HTC, "bundle tx complete, eid:%d, pending complete count:%d\n",
+				   ep->eid, skb_queue_len(&ep->tx_complete_head));
+
+			while (true) {
+				skb = skb_dequeue(&ep->tx_complete_head);
+				if (!skb)
+					break;
+				ath10k_htc_notify_tx_completion(ep, skb);
+			}
+		}
+	}
+}
+
+int ath10k_htc_send_hl(struct ath10k_htc *htc,
+		       enum ath10k_htc_ep_id eid,
+		       struct sk_buff *skb)
+{
+	struct ath10k_htc_ep *ep = &htc->endpoint[eid];
+	struct ath10k *ar = htc->ar;
+
+	ath10k_dbg(ar, ATH10K_DBG_HTC, "htc send hl: eid:%d, bundle:%d, tx count:%d, len:%d\n",
+		   eid, ep->bundle_tx, skb_queue_len(&ep->tx_req_head), skb->len);
+
+	if (ep->bundle_tx) {
+		skb_queue_tail(&ep->tx_req_head, skb);
+		queue_work(ar->workqueue, &ar->bundle_tx_work);
+		return 0;
+	} else {
+		return ath10k_htc_send(htc, eid, skb);
+	}
+}
+
+void ath10k_htc_setup_tx_req(struct ath10k_htc_ep *ep)
+{
+	if (ep->htc->max_msgs_per_htc_bundle >= HTC_HOST_MIN_MSG_PER_TX_BUNDLE) {
+		ep->bundle_tx = true;
+		skb_queue_head_init(&ep->tx_req_head);
+		skb_queue_head_init(&ep->tx_complete_head);
+	}
+}
+
 int ath10k_htc_wait_target(struct ath10k_htc *htc)
 {
 	struct ath10k *ar = htc->ar;
@@ -657,6 +946,9 @@ int ath10k_htc_wait_target(struct ath10k_htc *htc)
 			   htc->max_msgs_per_htc_bundle);
 	}
 
+	INIT_WORK(&ar->bundle_tx_work, ath10k_htc_bundle_tx_work);
+	INIT_WORK(&ar->tx_complete_work, ath10k_htc_tx_complete_work);
+
 	return 0;
 }
 
@@ -801,6 +1093,7 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
 	ep->max_tx_queue_depth = conn_req->max_send_queue_depth;
 	ep->max_ep_message_len = __le16_to_cpu(resp_msg->max_msg_size);
 	ep->tx_credits = tx_alloc;
+	ep->tx_credit_size = htc->target_credit_size;
 
 	/* copy all the callbacks */
 	ep->ep_ops = conn_req->ep_ops;
diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h
index 3c09fe8..d805ea5 100644
--- a/drivers/net/wireless/ath/ath10k/htc.h
+++ b/drivers/net/wireless/ath/ath10k/htc.h
@@ -40,6 +40,9 @@
  */
 
 #define HTC_HOST_MAX_MSG_PER_RX_BUNDLE        32
+#define HTC_HOST_MAX_MSG_PER_TX_BUNDLE        32
+#define HTC_HOST_MIN_MSG_PER_TX_BUNDLE        2
+#define HTC_HOST_MIN_CREDIT_PER_TX_BUNDLE     2
 
 enum ath10k_htc_tx_flags {
 	ATH10K_HTC_FLAG_NEED_CREDIT_UPDATE = 0x01,
@@ -68,8 +71,14 @@ struct ath10k_htc_hdr {
 		u8 seq_no; /* for tx */
 		u8 control_byte1;
 	} __packed;
-	u8 pad0;
-	u8 pad1;
+	union {
+		__le16 pad_len;
+		struct {
+			u8 pad0;
+			u8 pad1;
+		} __packed;
+	} __packed;
+
 } __packed __aligned(4);
 
 enum ath10k_ath10k_htc_msg_id {
@@ -338,7 +347,12 @@ struct ath10k_htc_ep {
 
 	u8 seq_no; /* for debugging */
 	int tx_credits;
+	int tx_credit_size;
 	bool tx_credit_flow_enabled;
+	bool bundle_tx;
+	struct sk_buff_head tx_req_head;
+	struct sk_buff_head tx_complete_head;
+
 };
 
 struct ath10k_htc_svc_tx_credits {
@@ -367,6 +381,7 @@ struct ath10k_htc {
 
 int ath10k_htc_init(struct ath10k *ar);
 int ath10k_htc_wait_target(struct ath10k_htc *htc);
+void ath10k_htc_setup_tx_req(struct ath10k_htc_ep *ep);
 int ath10k_htc_start(struct ath10k_htc *htc);
 int ath10k_htc_connect_service(struct ath10k_htc *htc,
 			       struct ath10k_htc_svc_conn_req  *conn_req,
@@ -376,6 +391,8 @@ void ath10k_htc_change_tx_credit_flow(struct ath10k_htc *htc,
 				      bool enable);
 int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid,
 		    struct sk_buff *packet);
+int ath10k_htc_send_hl(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid,
+		       struct sk_buff *packet);
 struct sk_buff *ath10k_htc_alloc_skb(struct ath10k *ar, int size);
 void ath10k_htc_tx_completion_handler(struct ath10k *ar, struct sk_buff *skb);
 void ath10k_htc_rx_completion_handler(struct ath10k *ar, struct sk_buff *skb);
diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c
index 4354bf2..127b4e4 100644
--- a/drivers/net/wireless/ath/ath10k/htt.c
+++ b/drivers/net/wireless/ath/ath10k/htt.c
@@ -135,6 +135,8 @@ int ath10k_htt_connect(struct ath10k_htt *htt)
 {
 	struct ath10k_htc_svc_conn_req conn_req;
 	struct ath10k_htc_svc_conn_resp conn_resp;
+	struct ath10k *ar = htt->ar;
+	struct ath10k_htc_ep *ep;
 	int status;
 
 	memset(&conn_req, 0, sizeof(conn_req));
@@ -142,6 +144,7 @@ int ath10k_htt_connect(struct ath10k_htt *htt)
 
 	conn_req.ep_ops.ep_tx_complete = ath10k_htt_htc_tx_complete;
 	conn_req.ep_ops.ep_rx_complete = ath10k_htt_htc_t2h_msg_handler;
+	conn_req.ep_ops.ep_tx_credits = ath10k_htt_op_ep_tx_credits;
 
 	/* connect to control service */
 	conn_req.service_id = ATH10K_HTC_SVC_ID_HTT_DATA_MSG;
@@ -154,6 +157,11 @@ int ath10k_htt_connect(struct ath10k_htt *htt)
 
 	htt->eid = conn_resp.eid;
 
+	if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL) {
+		ep = &ar->htc.endpoint[htt->eid];
+		ath10k_htc_setup_tx_req(ep);
+	}
+
 	htt->disable_tx_comp = ath10k_hif_get_htt_tx_complete(htt->ar);
 	if (htt->disable_tx_comp)
 		ath10k_htc_change_tx_credit_flow(&htt->ar->htc, htt->eid, true);
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index 889bf9f..4851a2e 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -2030,6 +2030,9 @@ struct ath10k_htt {
 	const struct ath10k_htt_tx_ops *tx_ops;
 	const struct ath10k_htt_rx_ops *rx_ops;
 	bool disable_tx_comp;
+	bool bundle_tx;
+	struct sk_buff_head tx_req_head;
+	struct sk_buff_head tx_complete_head;
 };
 
 struct ath10k_htt_tx_ops {
@@ -2276,6 +2279,7 @@ int ath10k_htt_tx_fetch_resp(struct ath10k *ar,
 			     __le16 fetch_seq_num,
 			     struct htt_tx_fetch_record *records,
 			     size_t num_records);
+void ath10k_htt_op_ep_tx_credits(struct ath10k *ar);
 
 void ath10k_htt_tx_txq_update(struct ieee80211_hw *hw,
 			      struct ieee80211_txq *txq);
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 9990da7..e2d8b51 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -3822,6 +3822,7 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
 			ath10k_dbg(ar, ATH10K_DBG_HTT,
 				   "credit total:%d\n",
 				   ep->tx_credits);
+			ep->ep_ops.ep_tx_credits(htc->ar);
 		}
 		break;
 	}
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 8da5545..402ed1b 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -541,6 +541,11 @@ void ath10k_htt_tx_free(struct ath10k_htt *htt)
 	ath10k_htt_tx_destroy(htt);
 }
 
+void ath10k_htt_op_ep_tx_credits(struct ath10k *ar)
+{
+	queue_work(ar->workqueue, &ar->bundle_tx_work);
+}
+
 void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
 {
 	struct ath10k_htt *htt = &ar->htt;
@@ -559,7 +564,7 @@ void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
 			flags1 = __le16_to_cpu(desc_hdr->flags1);
 
 			ath10k_dbg(ar, ATH10K_DBG_HTT,
-				   "ath10k_htt_htc_tx_complete msdu id:%u ,flags1:%x\n",
+				   "htt htc tx complete msdu id:%u ,flags1:%x\n",
 				   __le16_to_cpu(desc_hdr->id), flags1);
 
 			if (flags1 & HTT_DATA_TX_DESC_FLAGS1_TX_COMPLETE)
@@ -1356,7 +1361,7 @@ static int ath10k_htt_tx_hl(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txm
 	 */
 	tx_desc->peerid = __cpu_to_le32(HTT_INVALID_PEERID);
 
-	res = ath10k_htc_send(&htt->ar->htc, htt->eid, msdu);
+	res = ath10k_htc_send_hl(&htt->ar->htc, htt->eid, msdu);
 
 out:
 	return res;
-- 
1.9.1


^ permalink raw reply related

* [PATCH v2 4/7] ath10k: disable TX complete indication of htt for sdio
From: Wen Gong @ 2019-08-27 11:01 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless
In-Reply-To: <1566903707-27536-1-git-send-email-wgong@codeaurora.org>

Tx complete message from firmware cost bus bandwidth of sdio, and bus
bandwidth is the bollteneck of throughput, it will effect the bandwidth
occupancy of data packet of TX and RX.

This patch disable TX complete indication from firmware for htt data
packet, it results in significant performance improvement on TX path.

The downside of this patch is ath10k will not know the TX status of
the data packet for poor signal situation. Although upper network stack
or application layer have retry mechanism, the retry will be later than
ath10k get the TX fail status if not disable TX complete.

This patch only effect sdio chip, it will not effect PCI, SNOC etc.

Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00007-QCARMSWP-1.

Signed-off-by: Wen Gong <wgong@codeaurora.org>
---
 drivers/net/wireless/ath/ath10k/core.c   |  6 ++++++
 drivers/net/wireless/ath/ath10k/hif.h    |  9 ++++++++
 drivers/net/wireless/ath/ath10k/htc.c    | 10 +++++++++
 drivers/net/wireless/ath/ath10k/htc.h    |  3 +++
 drivers/net/wireless/ath/ath10k/htt.c    |  5 +++++
 drivers/net/wireless/ath/ath10k/htt.h    | 13 +++++++++++-
 drivers/net/wireless/ath/ath10k/htt_rx.c | 35 +++++++++++++++++++++++++++++++-
 drivers/net/wireless/ath/ath10k/htt_tx.c | 30 +++++++++++++++++++++++++++
 drivers/net/wireless/ath/ath10k/hw.h     |  2 +-
 drivers/net/wireless/ath/ath10k/sdio.c   | 28 +++++++++++++++++++++++++
 10 files changed, 138 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index dc45d16..762bba0 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -30,6 +30,7 @@
 
 static unsigned int ath10k_cryptmode_param;
 static bool uart_print;
+static bool disable_tx_comp = true;
 static bool skip_otp;
 static bool rawmode;
 static bool fw_diag_log;
@@ -41,6 +42,9 @@
 module_param_named(debug_mask, ath10k_debug_mask, uint, 0644);
 module_param_named(cryptmode, ath10k_cryptmode_param, uint, 0644);
 module_param(uart_print, bool, 0644);
+
+/* If upper layer need the TX complete status, it can enable tx complete */
+module_param(disable_tx_comp, bool, 0644);
 module_param(skip_otp, bool, 0644);
 module_param(rawmode, bool, 0644);
 module_param(fw_diag_log, bool, 0644);
@@ -689,6 +693,8 @@ static void ath10k_init_sdio(struct ath10k *ar, enum ath10k_firmware_mode mode)
 	 * is used for SDIO. disable it until fixed
 	 */
 	param &= ~HI_ACS_FLAGS_SDIO_REDUCE_TX_COMPL_SET;
+	if (disable_tx_comp)
+		param |= HI_ACS_FLAGS_SDIO_REDUCE_TX_COMPL_SET;
 
 	/* Alternate credit size of 1544 as used by SDIO firmware is
 	 * not big enough for mac80211 / native wifi frames. disable it
diff --git a/drivers/net/wireless/ath/ath10k/hif.h b/drivers/net/wireless/ath/ath10k/hif.h
index 496ee34..0dd8973 100644
--- a/drivers/net/wireless/ath/ath10k/hif.h
+++ b/drivers/net/wireless/ath/ath10k/hif.h
@@ -56,6 +56,8 @@ struct ath10k_hif_ops {
 
 	int (*swap_mailbox)(struct ath10k *ar);
 
+	int (*get_htt_tx_complete)(struct ath10k *ar);
+
 	int (*map_service_to_pipe)(struct ath10k *ar, u16 service_id,
 				   u8 *ul_pipe, u8 *dl_pipe);
 
@@ -144,6 +146,13 @@ static inline int ath10k_hif_swap_mailbox(struct ath10k *ar)
 	return 0;
 }
 
+static inline int ath10k_hif_get_htt_tx_complete(struct ath10k *ar)
+{
+	if (ar->hif.ops->get_htt_tx_complete)
+		return ar->hif.ops->get_htt_tx_complete(ar);
+	return 0;
+}
+
 static inline int ath10k_hif_map_service_to_pipe(struct ath10k *ar,
 						 u16 service_id,
 						 u8 *ul_pipe, u8 *dl_pipe)
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c
index 1d4d1a1..4c6cdc2 100644
--- a/drivers/net/wireless/ath/ath10k/htc.c
+++ b/drivers/net/wireless/ath/ath10k/htc.c
@@ -660,6 +660,16 @@ int ath10k_htc_wait_target(struct ath10k_htc *htc)
 	return 0;
 }
 
+void ath10k_htc_change_tx_credit_flow(struct ath10k_htc *htc,
+				      enum ath10k_htc_ep_id eid,
+				      bool enable)
+{
+	struct ath10k *ar = htc->ar;
+	struct ath10k_htc_ep *ep = &ar->htc.endpoint[eid];
+
+	ep->tx_credit_flow_enabled = enable;
+}
+
 int ath10k_htc_connect_service(struct ath10k_htc *htc,
 			       struct ath10k_htc_svc_conn_req *conn_req,
 			       struct ath10k_htc_svc_conn_resp *conn_resp)
diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h
index 8a07da0..3c09fe8 100644
--- a/drivers/net/wireless/ath/ath10k/htc.h
+++ b/drivers/net/wireless/ath/ath10k/htc.h
@@ -371,6 +371,9 @@ struct ath10k_htc {
 int ath10k_htc_connect_service(struct ath10k_htc *htc,
 			       struct ath10k_htc_svc_conn_req  *conn_req,
 			       struct ath10k_htc_svc_conn_resp *conn_resp);
+void ath10k_htc_change_tx_credit_flow(struct ath10k_htc *htc,
+				      enum ath10k_htc_ep_id eid,
+				      bool enable);
 int ath10k_htc_send(struct ath10k_htc *htc, enum ath10k_htc_ep_id eid,
 		    struct sk_buff *packet);
 struct sk_buff *ath10k_htc_alloc_skb(struct ath10k *ar, int size);
diff --git a/drivers/net/wireless/ath/ath10k/htt.c b/drivers/net/wireless/ath/ath10k/htt.c
index 7b75200..4354bf2 100644
--- a/drivers/net/wireless/ath/ath10k/htt.c
+++ b/drivers/net/wireless/ath/ath10k/htt.c
@@ -10,6 +10,7 @@
 #include "htt.h"
 #include "core.h"
 #include "debug.h"
+#include "hif.h"
 
 static const enum htt_t2h_msg_type htt_main_t2h_msg_types[] = {
 	[HTT_MAIN_T2H_MSG_TYPE_VERSION_CONF] = HTT_T2H_MSG_TYPE_VERSION_CONF,
@@ -153,6 +154,10 @@ int ath10k_htt_connect(struct ath10k_htt *htt)
 
 	htt->eid = conn_resp.eid;
 
+	htt->disable_tx_comp = ath10k_hif_get_htt_tx_complete(htt->ar);
+	if (htt->disable_tx_comp)
+		ath10k_htc_change_tx_credit_flow(&htt->ar->htc, htt->eid, true);
+
 	return 0;
 }
 
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index 30c0800..889bf9f 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -150,9 +150,19 @@ enum htt_data_tx_desc_flags1 {
 	HTT_DATA_TX_DESC_FLAGS1_MORE_IN_BATCH    = 1 << 12,
 	HTT_DATA_TX_DESC_FLAGS1_CKSUM_L3_OFFLOAD = 1 << 13,
 	HTT_DATA_TX_DESC_FLAGS1_CKSUM_L4_OFFLOAD = 1 << 14,
-	HTT_DATA_TX_DESC_FLAGS1_RSVD1            = 1 << 15
+	HTT_DATA_TX_DESC_FLAGS1_TX_COMPLETE      = 1 << 15
 };
 
+#define HTT_TX_CREDIT_DELTA_ABS_M      0xffff0000
+#define HTT_TX_CREDIT_DELTA_ABS_S      16
+#define HTT_TX_CREDIT_DELTA_ABS_GET(word) \
+	    (((word) & HTT_TX_CREDIT_DELTA_ABS_M) >> HTT_TX_CREDIT_DELTA_ABS_S)
+
+#define HTT_TX_CREDIT_SIGN_BIT_M       0x00000100
+#define HTT_TX_CREDIT_SIGN_BIT_S       8
+#define HTT_TX_CREDIT_SIGN_BIT_GET(word) \
+	    (((word) & HTT_TX_CREDIT_SIGN_BIT_M) >> HTT_TX_CREDIT_SIGN_BIT_S)
+
 enum htt_data_tx_ext_tid {
 	HTT_DATA_TX_EXT_TID_NON_QOS_MCAST_BCAST = 16,
 	HTT_DATA_TX_EXT_TID_MGMT                = 17,
@@ -2019,6 +2029,7 @@ struct ath10k_htt {
 	bool tx_mem_allocated;
 	const struct ath10k_htt_tx_ops *tx_ops;
 	const struct ath10k_htt_rx_ops *rx_ops;
+	bool disable_tx_comp;
 };
 
 struct ath10k_htt_tx_ops {
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 83a7fb6..9990da7 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -3691,6 +3691,9 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
 	}
 	case HTT_T2H_MSG_TYPE_MGMT_TX_COMPLETION: {
 		struct htt_tx_done tx_done = {};
+		struct ath10k_htt *htt = &ar->htt;
+		struct ath10k_htc *htc = &ar->htc;
+		struct ath10k_htc_ep *ep = &ar->htc.endpoint[htt->eid];
 		int status = __le32_to_cpu(resp->mgmt_tx_completion.status);
 		int info = __le32_to_cpu(resp->mgmt_tx_completion.info);
 
@@ -3716,6 +3719,12 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
 			break;
 		}
 
+		if (htt->disable_tx_comp) {
+			spin_lock_bh(&htc->tx_lock);
+			ep->tx_credits++;
+			spin_unlock_bh(&htc->tx_lock);
+		}
+
 		status = ath10k_txrx_tx_unref(htt, &tx_done);
 		if (!status) {
 			spin_lock_bh(&htt->tx_lock);
@@ -3790,8 +3799,32 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
 		skb_queue_tail(&htt->rx_in_ord_compl_q, skb);
 		return false;
 	}
-	case HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND:
+	case HTT_T2H_MSG_TYPE_TX_CREDIT_UPDATE_IND: {
+		struct ath10k_htt *htt = &ar->htt;
+		struct ath10k_htc *htc = &ar->htc;
+		struct ath10k_htc_ep *ep = &ar->htc.endpoint[htt->eid];
+		__le32 *msg = (__le32 *)resp;
+		u32 msg_word = __le32_to_cpu(*msg);
+		int htt_credit_delta;
+
+		htt_credit_delta = HTT_TX_CREDIT_DELTA_ABS_GET(msg_word);
+		if (HTT_TX_CREDIT_SIGN_BIT_GET(msg_word))
+			htt_credit_delta = -htt_credit_delta;
+
+		ath10k_dbg(ar, ATH10K_DBG_HTT,
+			   "credit update: delta:%d\n",
+			   htt_credit_delta);
+
+		if (htt->disable_tx_comp) {
+			spin_lock_bh(&htc->tx_lock);
+			ep->tx_credits += htt_credit_delta;
+			spin_unlock_bh(&htc->tx_lock);
+			ath10k_dbg(ar, ATH10K_DBG_HTT,
+				   "credit total:%d\n",
+				   ep->tx_credits);
+		}
 		break;
+	}
 	case HTT_T2H_MSG_TYPE_CHAN_CHANGE: {
 		u32 phymode = __le32_to_cpu(resp->chan_change.phymode);
 		u32 freq = __le32_to_cpu(resp->chan_change.freq);
diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c
index 2ef717f1..8da5545 100644
--- a/drivers/net/wireless/ath/ath10k/htt_tx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_tx.c
@@ -543,7 +543,33 @@ void ath10k_htt_tx_free(struct ath10k_htt *htt)
 
 void ath10k_htt_htc_tx_complete(struct ath10k *ar, struct sk_buff *skb)
 {
+	struct ath10k_htt *htt = &ar->htt;
+	struct htt_tx_done tx_done = {0};
+	struct htt_cmd_hdr *htt_hdr;
+	struct htt_data_tx_desc *desc_hdr;
+	u16 flags1;
+
 	dev_kfree_skb_any(skb);
+
+	if (htt->disable_tx_comp) {
+		htt_hdr = (struct htt_cmd_hdr *)skb->data;
+		if (htt_hdr->msg_type == HTT_H2T_MSG_TYPE_TX_FRM) {
+			desc_hdr = (struct htt_data_tx_desc *)
+				(skb->data + sizeof(*htt_hdr));
+			flags1 = __le16_to_cpu(desc_hdr->flags1);
+
+			ath10k_dbg(ar, ATH10K_DBG_HTT,
+				   "ath10k_htt_htc_tx_complete msdu id:%u ,flags1:%x\n",
+				   __le16_to_cpu(desc_hdr->id), flags1);
+
+			if (flags1 & HTT_DATA_TX_DESC_FLAGS1_TX_COMPLETE)
+				return;
+
+			tx_done.status = HTT_TX_COMPL_STATE_ACK;
+			tx_done.msdu_id = __le16_to_cpu(desc_hdr->id);
+			ath10k_txrx_tx_unref(&ar->htt, &tx_done);
+		}
+	}
 }
 
 void ath10k_htt_hif_tx_complete(struct ath10k *ar, struct sk_buff *skb)
@@ -1260,6 +1286,10 @@ static int ath10k_htt_tx_hl(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txm
 	case ATH10K_HW_TXRX_MGMT:
 		flags0 |= SM(ATH10K_HW_TXRX_MGMT,
 			     HTT_DATA_TX_DESC_FLAGS0_PKT_TYPE);
+
+		if (htt->disable_tx_comp)
+			flags1 |= HTT_DATA_TX_DESC_FLAGS1_TX_COMPLETE;
+
 		flags0 |= HTT_DATA_TX_DESC_FLAGS0_MAC_HDR_PRESENT;
 		break;
 	}
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
index 2ae57c1..6349665 100644
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -759,7 +759,7 @@ struct ath10k_hw_ops {
 #define TARGET_TLV_NUM_TDLS_VDEVS		1
 #define TARGET_TLV_NUM_TIDS			((TARGET_TLV_NUM_PEERS) * 2)
 #define TARGET_TLV_NUM_MSDU_DESC		(1024 + 32)
-#define TARGET_TLV_NUM_MSDU_DESC_HL		64
+#define TARGET_TLV_NUM_MSDU_DESC_HL		1024
 #define TARGET_TLV_NUM_WOW_PATTERNS		22
 #define TARGET_TLV_MGMT_NUM_MSDU_DESC		(50)
 
diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c
index 5363a37..a302eda 100644
--- a/drivers/net/wireless/ath/ath10k/sdio.c
+++ b/drivers/net/wireless/ath/ath10k/sdio.c
@@ -1790,6 +1790,33 @@ static int ath10k_sdio_hif_swap_mailbox(struct ath10k *ar)
 	return 0;
 }
 
+static int ath10k_sdio_get_htt_tx_complete(struct ath10k *ar)
+{
+	u32 addr, val;
+	int ret;
+
+	addr = host_interest_item_address(HI_ITEM(hi_acs_flags));
+
+	ret = ath10k_sdio_hif_diag_read32(ar, addr, &val);
+	if (ret) {
+		ath10k_warn(ar,
+			    "unable to read hi_acs_flags for htt tx comple : %d\n", ret);
+		return ret;
+	}
+
+	if (val & HI_ACS_FLAGS_SDIO_REDUCE_TX_COMPL_FW_ACK) {
+		ath10k_dbg(ar, ATH10K_DBG_SDIO,
+			   "sdio reduce tx comple fw ack\n");
+		ret = 1;
+	} else {
+		ath10k_dbg(ar, ATH10K_DBG_SDIO,
+			   "sdio reduce tx comple fw not ack\n");
+		ret = 0;
+	}
+
+	return ret;
+}
+
 /* HIF start/stop */
 
 static int ath10k_sdio_hif_start(struct ath10k *ar)
@@ -2073,6 +2100,7 @@ static void ath10k_sdio_hif_send_complete_check(struct ath10k *ar,
 	.start			= ath10k_sdio_hif_start,
 	.stop			= ath10k_sdio_hif_stop,
 	.swap_mailbox		= ath10k_sdio_hif_swap_mailbox,
+	.get_htt_tx_complete	= ath10k_sdio_get_htt_tx_complete,
 	.map_service_to_pipe	= ath10k_sdio_hif_map_service_to_pipe,
 	.get_default_pipe	= ath10k_sdio_hif_get_default_pipe,
 	.send_complete_check	= ath10k_sdio_hif_send_complete_check,
-- 
1.9.1


^ permalink raw reply related

* [PATCH v2 1/7] ath10k: enable RX bundle receive for sdio
From: Wen Gong @ 2019-08-27 11:01 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless
In-Reply-To: <1566903707-27536-1-git-send-email-wgong@codeaurora.org>

From: Alagu Sankar <alagusankar@silex-india.com>

The existing implementation of initiating multiple sdio transfers for
receive bundling is slowing down the receive speed. Combining the
transfers using a bundle method would be ideal.

The transmission utilization ratio for sdio bus for small packet is
slow, because the space and time cost for sdio bus is same for large
length packet and small length packet. So the speed of data for large
length packet is higher than small length.

Test result of different length of data:
data packet(byte)   cost time(us)   calculated rate(Mbps)
      256               28                73
      512               33               124
     1024               35               234
     1792               45               318
    14336              168               682
    28672              333               688
    57344              660               695

Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00007-QCARMSWP-1.

Signed-off-by: Alagu Sankar <alagusankar@silex-india.com>
Signed-off-by: Wen Gong <wgong@codeaurora.org>
---
 drivers/net/wireless/ath/ath10k/sdio.c | 112 +++++++++++++++++++++++----------
 drivers/net/wireless/ath/ath10k/sdio.h |   7 ++-
 2 files changed, 85 insertions(+), 34 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c
index 8ed4fbd..eacb4d5 100644
--- a/drivers/net/wireless/ath/ath10k/sdio.c
+++ b/drivers/net/wireless/ath/ath10k/sdio.c
@@ -24,6 +24,9 @@
 #include "trace.h"
 #include "sdio.h"
 
+#define ATH10K_SDIO_DMA_BUF_SIZE	(32 * 1024)
+#define ATH10K_SDIO_VSG_BUF_SIZE	(32 * 1024)
+
 /* inlined helper functions */
 
 static inline int ath10k_sdio_calc_txrx_padded_len(struct ath10k_sdio *ar_sdio,
@@ -381,16 +384,11 @@ static int ath10k_sdio_mbox_rx_process_packet(struct ath10k *ar,
 	struct ath10k_htc_hdr *htc_hdr = (struct ath10k_htc_hdr *)skb->data;
 	bool trailer_present = htc_hdr->flags & ATH10K_HTC_FLAG_TRAILER_PRESENT;
 	enum ath10k_htc_ep_id eid;
-	u16 payload_len;
 	u8 *trailer;
 	int ret;
 
-	payload_len = le16_to_cpu(htc_hdr->len);
-	skb->len = payload_len + sizeof(struct ath10k_htc_hdr);
-
 	if (trailer_present) {
-		trailer = skb->data + sizeof(*htc_hdr) +
-			  payload_len - htc_hdr->trailer_len;
+		trailer = skb->data + skb->len - htc_hdr->trailer_len;
 
 		eid = pipe_id_to_eid(htc_hdr->eid);
 
@@ -489,11 +487,11 @@ static int ath10k_sdio_mbox_rx_process_packets(struct ath10k *ar,
 	return ret;
 }
 
-static int ath10k_sdio_mbox_alloc_pkt_bundle(struct ath10k *ar,
-					     struct ath10k_sdio_rx_data *rx_pkts,
-					     struct ath10k_htc_hdr *htc_hdr,
-					     size_t full_len, size_t act_len,
-					     size_t *bndl_cnt)
+static int ath10k_sdio_mbox_alloc_bundle(struct ath10k *ar,
+					 struct ath10k_sdio_rx_data *rx_pkts,
+					 struct ath10k_htc_hdr *htc_hdr,
+					 size_t full_len, size_t act_len,
+					 size_t *bndl_cnt)
 {
 	int ret, i;
 
@@ -534,6 +532,7 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar,
 	size_t full_len, act_len;
 	bool last_in_bundle;
 	int ret, i;
+	int pkt_cnt = 0;
 
 	if (n_lookaheads > ATH10K_SDIO_MAX_RX_MSGS) {
 		ath10k_warn(ar,
@@ -577,20 +576,22 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar,
 			 */
 			size_t bndl_cnt;
 
-			ret = ath10k_sdio_mbox_alloc_pkt_bundle(ar,
-								&ar_sdio->rx_pkts[i],
-								htc_hdr,
-								full_len,
-								act_len,
-								&bndl_cnt);
+			struct ath10k_sdio_rx_data *rx_pkts =
+				&ar_sdio->rx_pkts[pkt_cnt];
+
+			ret = ath10k_sdio_mbox_alloc_bundle(ar,
+							    rx_pkts,
+							    htc_hdr,
+							    full_len,
+							    act_len,
+							    &bndl_cnt);
 
 			if (ret) {
 				ath10k_warn(ar, "alloc_bundle error %d\n", ret);
 				goto err;
 			}
 
-			n_lookaheads += bndl_cnt;
-			i += bndl_cnt;
+			pkt_cnt += bndl_cnt;
 			/*Next buffer will be the last in the bundle */
 			last_in_bundle = true;
 		}
@@ -602,7 +603,7 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar,
 		if (htc_hdr->flags & ATH10K_HTC_FLAGS_RECV_1MORE_BLOCK)
 			full_len += ATH10K_HIF_MBOX_BLOCK_SIZE;
 
-		ret = ath10k_sdio_mbox_alloc_rx_pkt(&ar_sdio->rx_pkts[i],
+		ret = ath10k_sdio_mbox_alloc_rx_pkt(&ar_sdio->rx_pkts[pkt_cnt],
 						    act_len,
 						    full_len,
 						    last_in_bundle,
@@ -611,9 +612,10 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar,
 			ath10k_warn(ar, "alloc_rx_pkt error %d\n", ret);
 			goto err;
 		}
+		pkt_cnt++;
 	}
 
-	ar_sdio->n_rx_pkts = i;
+	ar_sdio->n_rx_pkts = pkt_cnt;
 
 	return 0;
 
@@ -627,41 +629,78 @@ static int ath10k_sdio_mbox_rx_alloc(struct ath10k *ar,
 	return ret;
 }
 
-static int ath10k_sdio_mbox_rx_packet(struct ath10k *ar,
-				      struct ath10k_sdio_rx_data *pkt)
+static int ath10k_sdio_mbox_rx_fetch(struct ath10k *ar)
 {
 	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
+	struct ath10k_sdio_rx_data *pkt = &ar_sdio->rx_pkts[0];
 	struct sk_buff *skb = pkt->skb;
+	struct ath10k_htc_hdr *htc_hdr;
 	int ret;
 
 	ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr,
 				 skb->data, pkt->alloc_len);
-	pkt->status = ret;
-	if (!ret)
+
+	if (ret) {
+		ar_sdio->n_rx_pkts = 0;
+		ath10k_sdio_mbox_free_rx_pkt(pkt);
+	} else {
+		htc_hdr = (struct ath10k_htc_hdr *)skb->data;
+		pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
+		pkt->status = ret;
 		skb_put(skb, pkt->act_len);
+	}
 
 	return ret;
 }
 
-static int ath10k_sdio_mbox_rx_fetch(struct ath10k *ar)
+static int ath10k_sdio_mbox_rx_fetch_bundle(struct ath10k *ar)
 {
 	struct ath10k_sdio *ar_sdio = ath10k_sdio_priv(ar);
+	struct ath10k_sdio_rx_data *pkt;
+	struct ath10k_htc_hdr *htc_hdr;
 	int ret, i;
+	u32 pkt_offset, virt_pkt_len;
 
+	virt_pkt_len = 0;
 	for (i = 0; i < ar_sdio->n_rx_pkts; i++) {
-		ret = ath10k_sdio_mbox_rx_packet(ar,
-						 &ar_sdio->rx_pkts[i]);
-		if (ret)
+		virt_pkt_len += ar_sdio->rx_pkts[i].alloc_len;
+	}
+
+	if (virt_pkt_len < ATH10K_SDIO_DMA_BUF_SIZE) {
+		ret = ath10k_sdio_readsb(ar, ar_sdio->mbox_info.htc_addr,
+					 ar_sdio->vsg_buffer, virt_pkt_len);
+		if (ret) {
+			i = 0;
 			goto err;
+		}
+	} else {
+		ath10k_err(ar, "size exceeding limit %d\n", virt_pkt_len);
+	}
+
+	pkt_offset = 0;
+	for (i = 0; i < ar_sdio->n_rx_pkts; i++) {
+		struct sk_buff *skb = ar_sdio->rx_pkts[i].skb;
+
+		pkt = &ar_sdio->rx_pkts[i];
+		htc_hdr = (struct ath10k_htc_hdr *)(ar_sdio->vsg_buffer + pkt_offset);
+		pkt->act_len = le16_to_cpu(htc_hdr->len) + sizeof(*htc_hdr);
+
+		memcpy(skb->data, ar_sdio->vsg_buffer + pkt_offset,
+		       pkt->act_len);
+		pkt->status = 0;
+		skb_put(skb, pkt->act_len);
+		pkt_offset += pkt->alloc_len;
 	}
 
 	return 0;
 
 err:
 	/* Free all packets that was not successfully fetched. */
-	for (; i < ar_sdio->n_rx_pkts; i++)
+	for (i = 0; i < ar_sdio->n_rx_pkts; i++)
 		ath10k_sdio_mbox_free_rx_pkt(&ar_sdio->rx_pkts[i]);
 
+	ar_sdio->n_rx_pkts = 0;
+
 	return ret;
 }
 
@@ -704,7 +743,10 @@ static int ath10k_sdio_mbox_rxmsg_pending_handler(struct ath10k *ar,
 			 */
 			*done = false;
 
-		ret = ath10k_sdio_mbox_rx_fetch(ar);
+		if (ar_sdio->n_rx_pkts > 1)
+			ret = ath10k_sdio_mbox_rx_fetch_bundle(ar);
+		else
+			ret = ath10k_sdio_mbox_rx_fetch(ar);
 
 		/* Process fetched packets. This will potentially update
 		 * n_lookaheads depending on if the packets contain lookahead
@@ -1112,7 +1154,7 @@ static int ath10k_sdio_bmi_get_rx_lookahead(struct ath10k *ar)
 					 MBOX_HOST_INT_STATUS_ADDRESS,
 					 &rx_word);
 		if (ret) {
-			ath10k_warn(ar, "unable to read RX_LOOKAHEAD_VALID: %d\n", ret);
+			ath10k_warn(ar, "unable to read rx_lookahd: %d\n", ret);
 			return ret;
 		}
 
@@ -2007,6 +2049,12 @@ static int ath10k_sdio_probe(struct sdio_func *func,
 		goto err_core_destroy;
 	}
 
+	ar_sdio->vsg_buffer = devm_kmalloc(ar->dev, ATH10K_SDIO_VSG_BUF_SIZE, GFP_KERNEL);
+	if (!ar_sdio->vsg_buffer) {
+		ret = -ENOMEM;
+		goto err_core_destroy;
+	}
+
 	ar_sdio->irq_data.irq_en_reg =
 		devm_kzalloc(ar->dev, sizeof(struct ath10k_sdio_irq_enable_regs),
 			     GFP_KERNEL);
diff --git a/drivers/net/wireless/ath/ath10k/sdio.h b/drivers/net/wireless/ath/ath10k/sdio.h
index b8c7ac0..4896eca 100644
--- a/drivers/net/wireless/ath/ath10k/sdio.h
+++ b/drivers/net/wireless/ath/ath10k/sdio.h
@@ -138,8 +138,8 @@ struct ath10k_sdio_irq_proc_regs {
 	u8 rx_lookahead_valid;
 	u8 host_int_status2;
 	u8 gmbox_rx_avail;
-	__le32 rx_lookahead[2];
-	__le32 rx_gmbox_lookahead_alias[2];
+	__le32 rx_lookahead[2 * ATH10K_HIF_MBOX_NUM_MAX];
+	__le32 int_status_enable;
 };
 
 struct ath10k_sdio_irq_enable_regs {
@@ -196,6 +196,9 @@ struct ath10k_sdio {
 	struct ath10k *ar;
 	struct ath10k_sdio_irq_data irq_data;
 
+	/* temporary buffer for sdio read */
+	u8 *vsg_buffer;
+
 	/* temporary buffer for BMI requests */
 	u8 *bmi_buf;
 
-- 
1.9.1


^ permalink raw reply related

* [PATCH v2 6/7] ath10k: enable alt data of TX path for sdio
From: Wen Gong @ 2019-08-27 11:01 UTC (permalink / raw)
  To: ath10k; +Cc: linux-wireless
In-Reply-To: <1566903707-27536-1-git-send-email-wgong@codeaurora.org>

The default credit size is 1792 bytes, but the IP mtu is 1500 bytes,
then it has about 290 bytes's waste for each data packet on sdio
transfer path for TX bundle, it will reduce the transmission utilization
ratio for data packet.

This patch enable the small credit size in firmware, firmware will use
the new credit size 1556 bytes, it will increase the transmission
utilization ratio for data packet on TX patch. It results in significant
performance improvement on TX path.

This patch only effect sdio chip, it will not effect PCI, SNOC etc.

Tested with QCA6174 SDIO with firmware
WLAN.RMH.4.4.1-00017-QCARMSWP-1.

Signed-off-by: Wen Gong <wgong@codeaurora.org>
---
 drivers/net/wireless/ath/ath10k/core.c | 16 ++++++++++++++++
 drivers/net/wireless/ath/ath10k/htc.c  | 11 +++++++++--
 drivers/net/wireless/ath/ath10k/htc.h  | 11 +++++++++--
 3 files changed, 34 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index 351f4ed..7593d19 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -31,6 +31,7 @@
 static unsigned int ath10k_cryptmode_param;
 static bool uart_print;
 static bool disable_tx_comp = true;
+static bool alt_data = true;
 static bool skip_otp;
 static bool rawmode;
 static bool fw_diag_log;
@@ -45,6 +46,15 @@
 
 /* If upper layer need the TX complete status, it can enable tx complete */
 module_param(disable_tx_comp, bool, 0644);
+
+/* alt_data is only used for sdio chip, for previous version of firmware, its
+ * alt data size is 1544 which is not enough for native wifi, so it need to
+ * alt_data for the firmware.
+ * If the firmware has changed alt data size to 1556, then it can enable
+ * alt_data for the firmware.
+ * alt_data will not effect PCI, SNOC etc.
+ */
+module_param(alt_data, bool, 0644);
 module_param(skip_otp, bool, 0644);
 module_param(rawmode, bool, 0644);
 module_param(fw_diag_log, bool, 0644);
@@ -701,6 +711,12 @@ static void ath10k_init_sdio(struct ath10k *ar, enum ath10k_firmware_mode mode)
 	 */
 	param &= ~HI_ACS_FLAGS_ALT_DATA_CREDIT_SIZE;
 
+	/* If alternate credit size of 1556 as used by SDIO firmware is
+	 * big enough for mac80211 / native wifi frames. enable it
+	 */
+	if (alt_data && mode == ATH10K_FIRMWARE_MODE_NORMAL)
+		param |= HI_ACS_FLAGS_ALT_DATA_CREDIT_SIZE;
+
 	if (mode == ATH10K_FIRMWARE_MODE_UTF)
 		param &= ~HI_ACS_FLAGS_SDIO_SWAP_MAILBOX_SET;
 	else
diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c
index e0eb5f0..5cacab6 100644
--- a/drivers/net/wireless/ath/ath10k/htc.c
+++ b/drivers/net/wireless/ath/ath10k/htc.c
@@ -938,12 +938,15 @@ int ath10k_htc_wait_target(struct ath10k_htc *htc)
 	 */
 	if (htc->control_resp_len >=
 	    sizeof(msg->hdr) + sizeof(msg->ready_ext)) {
+		htc->alt_data_credit_size =
+			__le16_to_cpu(msg->ready_ext.reserved) & 0x0fff;
 		htc->max_msgs_per_htc_bundle =
 			min_t(u8, msg->ready_ext.max_msgs_per_htc_bundle,
 			      HTC_HOST_MAX_MSG_PER_RX_BUNDLE);
 		ath10k_dbg(ar, ATH10K_DBG_HTC,
-			   "Extended ready message. RX bundle size: %d\n",
-			   htc->max_msgs_per_htc_bundle);
+			   "Extended ready message. RX bundle size: %d, alt size:%d\n",
+			   htc->max_msgs_per_htc_bundle,
+			   htc->alt_data_credit_size);
 	}
 
 	INIT_WORK(&ar->bundle_tx_work, ath10k_htc_bundle_tx_work);
@@ -1095,6 +1098,10 @@ int ath10k_htc_connect_service(struct ath10k_htc *htc,
 	ep->tx_credits = tx_alloc;
 	ep->tx_credit_size = htc->target_credit_size;
 
+	if (conn_req->service_id == ATH10K_HTC_SVC_ID_HTT_DATA_MSG &&
+	    htc->alt_data_credit_size != 0)
+		ep->tx_credit_size = htc->alt_data_credit_size;
+
 	/* copy all the callbacks */
 	ep->ep_ops = conn_req->ep_ops;
 
diff --git a/drivers/net/wireless/ath/ath10k/htc.h b/drivers/net/wireless/ath/ath10k/htc.h
index d805ea5..f0a9e60af 100644
--- a/drivers/net/wireless/ath/ath10k/htc.h
+++ b/drivers/net/wireless/ath/ath10k/htc.h
@@ -139,8 +139,14 @@ struct ath10k_htc_ready_extended {
 	struct ath10k_htc_ready base;
 	u8 htc_version; /* @enum ath10k_htc_version */
 	u8 max_msgs_per_htc_bundle;
-	u8 pad0;
-	u8 pad1;
+	union {
+		__le16 reserved;
+		struct {
+			u8 pad0;
+			u8 pad1;
+		} __packed;
+	} __packed;
+
 } __packed;
 
 struct ath10k_htc_conn_svc {
@@ -377,6 +383,7 @@ struct ath10k_htc {
 	int total_transmit_credits;
 	int target_credit_size;
 	u8 max_msgs_per_htc_bundle;
+	int alt_data_credit_size;
 };
 
 int ath10k_htc_init(struct ath10k *ar);
-- 
1.9.1


^ 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