2013-10-04 08:17:50

by Michal Kazior

[permalink] [raw]
Subject: [PATCH v2 0/2] ath10k: wep & ampdu fix

Hi,

One throughput fix and one bug fix.

The bug fix addresses my oversight when reworking
HTC/WMI. I haven't seen the error yet but I'm
quite certain this should be fixed.

v2:
* A-MPDU patch contains a workaround for
Netgear/Linksys 11ac APs
(Kalle, can you kindly test this, please?)
* patchset cover mail title changed to avoid
confusion (too many patchsets named the same
way..)


Michal Kazior (1):
ath10k: fix scheduling while atomic bug

Sujith Manoharan (1):
ath10k: Fix bug in max. VHT A-MPDU size

drivers/net/wireless/ath/ath10k/core.h | 4 +-
drivers/net/wireless/ath/ath10k/mac.c | 67 +++++++++++++++++++++++---------
2 files changed, 52 insertions(+), 19 deletions(-)

--
1.7.9.5



2013-10-08 18:58:24

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH v3 0/2] ath10k: wep & ampdu fix

Michal Kazior <[email protected]> writes:

> Hi,
>
> One throughput fix and one bug fix.
>
> The bug fix addresses my oversight when reworking
> HTC/WMI. I haven't seen the error yet but I'm
> quite certain this should be fixed.
>
> v2:
> * A-MPDU patch contains a workaround for
> Netgear/Linksys 11ac APs
> * patchset cover mail title changed to avoid
> confusion (too many patchsets named the same
> way..)
>
> v3:
> * fix lines over 80 chars (Kalle)
> * fix max() cast warning (Kalle)
> * rebase (Kalle)
> * add Tested-by (thanks Kalle!)
>
>
> Michal Kazior (1):
> ath10k: fix scheduling while atomic bug

I dropped this patch for now, need to think more about how to handle
this.

> Sujith Manoharan (1):
> ath10k: Fix bug in max. VHT A-MPDU size

This is applied, thanks.

--
Kalle Valo

2013-10-07 16:33:42

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH v2 1/2] ath10k: Fix bug in max. VHT A-MPDU size

Michal Kazior <[email protected]> writes:

> From: Sujith Manoharan <[email protected]>
>
> For VHT peers, the maximum A-MPDU size has to be calculated
> from the VHT capabilities element and not the HT-cap. The formula
> is the same, but a higher value is used in VHT, allowing larger
> aggregates to be transmitted.
>
> The patch contains a workaround for some Netgear/Linksys APs that
> report Rx A-MPDU factor incorrectly.
>
> Signed-off-by: Sujith Manoharan <[email protected]>
> Signed-off-by: Michal Kazior <[email protected]>

I see warnings with this patch:

drivers/net/wireless/ath/ath10k/mac.c: In function 'ath10k_peer_assoc_h_vht':
drivers/net/wireless/ath/ath10k/mac.c:1049:179: warning: comparison of distinct pointer types lacks a cast [enabled by default]
drivers/net/wireless/ath/ath10k/mac.c:1049:30: error: incompatible types in comparison expression (different signedness)
drivers/net/wireless/ath/ath10k/mac.c:1042: WARNING: line over 80 characters

--
Kalle Valo

2013-10-04 08:17:45

by Michal Kazior

[permalink] [raw]
Subject: [PATCH v2 1/2] ath10k: Fix bug in max. VHT A-MPDU size

From: Sujith Manoharan <[email protected]>

For VHT peers, the maximum A-MPDU size has to be calculated
from the VHT capabilities element and not the HT-cap. The formula
is the same, but a higher value is used in VHT, allowing larger
aggregates to be transmitted.

The patch contains a workaround for some Netgear/Linksys APs that
report Rx A-MPDU factor incorrectly.

Signed-off-by: Sujith Manoharan <[email protected]>
Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/mac.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 8684e03..7e06e75 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1033,14 +1033,26 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
struct wmi_peer_assoc_complete_arg *arg)
{
const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+ u8 ampdu_factor;

if (!vht_cap->vht_supported)
return;

arg->peer_flags |= WMI_PEER_VHT;
-
arg->peer_vht_caps = vht_cap->cap;

+ ampdu_factor =
+ (vht_cap->cap & IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >>
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+
+ /* Workaround: Some Netgear/Linksys 11ac APs set Rx A-MPDU factor to
+ * zero in VHT IE. Using it would result in degraded throughput.
+ * arg->peer_max_mpdu at this point contains HT max_mpdu so keep
+ * it if VHT max_mpdu is smaller. */
+ arg->peer_max_mpdu = max(arg->peer_max_mpdu,
+ (1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
+ ampdu_factor)) - 1);
+
if (sta->bandwidth == IEEE80211_STA_RX_BW_80)
arg->peer_flags |= WMI_PEER_80MHZ;

--
1.7.9.5


2013-10-08 02:54:18

by Michal Kazior

[permalink] [raw]
Subject: [PATCH v3 0/2] ath10k: wep & ampdu fix

Hi,

One throughput fix and one bug fix.

The bug fix addresses my oversight when reworking
HTC/WMI. I haven't seen the error yet but I'm
quite certain this should be fixed.

v2:
* A-MPDU patch contains a workaround for
Netgear/Linksys 11ac APs
* patchset cover mail title changed to avoid
confusion (too many patchsets named the same
way..)

v3:
* fix lines over 80 chars (Kalle)
* fix max() cast warning (Kalle)
* rebase (Kalle)
* add Tested-by (thanks Kalle!)


Michal Kazior (1):
ath10k: fix scheduling while atomic bug

Sujith Manoharan (1):
ath10k: Fix bug in max. VHT A-MPDU size

drivers/net/wireless/ath/ath10k/core.h | 4 +-
drivers/net/wireless/ath/ath10k/mac.c | 68 +++++++++++++++++++++++++---------
2 files changed, 53 insertions(+), 19 deletions(-)

--
1.8.4.rc3


2013-10-08 02:54:22

by Michal Kazior

[permalink] [raw]
Subject: [PATCH v3 2/2] ath10k: fix scheduling while atomic bug

Recent WMI/HTC changes made it possible for WMI
commands to sleep (if there's not enough HTC TX
credits to submit a command). TX path is in an
atomic context so calling WMI commands in it is
wrong.

This simply moves WEP key index update to a worker
and fixes the 'scheduling while atomic' bug.

This still leaves multiple WEP key handling laggy,
i.e. some frames may be TXed with an old/different
key (although recipient should still be able to RX
them).

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/core.h | 4 ++-
drivers/net/wireless/ath/ath10k/mac.c | 53 +++++++++++++++++++++++-----------
2 files changed, 39 insertions(+), 18 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index ce36daa..cef5455 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -215,8 +215,10 @@ struct ath10k_vif {
struct ath10k *ar;
struct ieee80211_vif *vif;

+ struct work_struct wep_key_work;
struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1];
- u8 def_wep_key_index;
+ u8 def_wep_key_idx;
+ u8 def_wep_key_newidx;

u16 tx_seq_no;

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 049eca2..498514e 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1239,7 +1239,7 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
/* FIXME: why don't we print error if wmi call fails? */
ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);

- arvif->def_wep_key_index = 0;
+ arvif->def_wep_key_idx = 0;
}

static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
@@ -1467,6 +1467,30 @@ static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw,
skb_pull(skb, IEEE80211_QOS_CTL_LEN);
}

+static void ath10k_tx_wep_key_work(struct work_struct *work)
+{
+ struct ath10k_vif *arvif = container_of(work, struct ath10k_vif,
+ wep_key_work);
+ int ret, keyidx = arvif->def_wep_key_newidx;
+
+ if (arvif->def_wep_key_idx == keyidx)
+ return;
+
+ ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n",
+ arvif->vdev_id, keyidx);
+
+ ret = ath10k_wmi_vdev_set_param(arvif->ar,
+ arvif->vdev_id,
+ arvif->ar->wmi.vdev_param->def_keyid,
+ keyidx);
+ if (ret) {
+ ath10k_warn("could not update wep keyidx (%d)\n", ret);
+ return;
+ }
+
+ arvif->def_wep_key_idx = keyidx;
+}
+
static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1475,8 +1499,6 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
struct ath10k *ar = arvif->ar;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_key_conf *key = info->control.hw_key;
- u32 vdev_param;
- int ret;

if (!ieee80211_has_protected(hdr->frame_control))
return;
@@ -1488,21 +1510,14 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
key->cipher != WLAN_CIPHER_SUITE_WEP104)
return;

- if (key->keyidx == arvif->def_wep_key_index)
- return;
-
- ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d keyidx %d\n",
- arvif->vdev_id, key->keyidx);
-
- vdev_param = ar->wmi.vdev_param->def_keyid;
- ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
- key->keyidx);
- if (ret) {
- ath10k_warn("could not update wep keyidx (%d)\n", ret);
+ if (key->keyidx == arvif->def_wep_key_idx)
return;
- }

- arvif->def_wep_key_index = key->keyidx;
+ /* FIXME: Most likely a few frames will be TXed with an old key. Simply
+ * queueing frames until key index is updated is not an option because
+ * sk_buff may need more processing to be done, e.g. offchannel */
+ arvif->def_wep_key_newidx = key->keyidx;
+ ieee80211_queue_work(ar->hw, &arvif->wep_key_work);
}

static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb)
@@ -2023,6 +2038,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
arvif->ar = ar;
arvif->vif = vif;

+ INIT_WORK(&arvif->wep_key_work, ath10k_tx_wep_key_work);
+
if ((vif->type == NL80211_IFTYPE_MONITOR) && ar->monitor_present) {
ath10k_warn("Only one monitor interface allowed\n");
ret = -EBUSY;
@@ -2078,7 +2095,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,

vdev_param = ar->wmi.vdev_param->def_keyid;
ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param,
- arvif->def_wep_key_index);
+ arvif->def_wep_key_idx);
if (ret)
ath10k_warn("Failed to set default keyid: %d\n", ret);

@@ -2146,6 +2163,8 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,

mutex_lock(&ar->conf_mutex);

+ cancel_work_sync(&arvif->wep_key_work);
+
spin_lock_bh(&ar->data_lock);
if (arvif->beacon) {
dev_kfree_skb_any(arvif->beacon);
--
1.8.4.rc3


2013-10-08 02:54:20

by Michal Kazior

[permalink] [raw]
Subject: [PATCH v3 1/2] ath10k: Fix bug in max. VHT A-MPDU size

From: Sujith Manoharan <[email protected]>

For VHT peers, the maximum A-MPDU size has to be calculated
from the VHT capabilities element and not the HT-cap. The formula
is the same, but a higher value is used in VHT, allowing larger
aggregates to be transmitted.

The patch contains a workaround for some Netgear/Linksys APs that
report Rx A-MPDU factor incorrectly.

Tested-by: Kalle Valo <[email protected]>
Signed-off-by: Sujith Manoharan <[email protected]>
Signed-off-by: Michal Kazior <[email protected]>
---
v3:
* fix lines over 80 chars (Kalle)
* fix max() cast warning (Kalle)
* add Tested-by

drivers/net/wireless/ath/ath10k/mac.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 4b7c949..049eca2 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1033,14 +1033,27 @@ static void ath10k_peer_assoc_h_vht(struct ath10k *ar,
struct wmi_peer_assoc_complete_arg *arg)
{
const struct ieee80211_sta_vht_cap *vht_cap = &sta->vht_cap;
+ u8 ampdu_factor;

if (!vht_cap->vht_supported)
return;

arg->peer_flags |= WMI_PEER_VHT;
-
arg->peer_vht_caps = vht_cap->cap;

+
+ ampdu_factor = (vht_cap->cap &
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK) >>
+ IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT;
+
+ /* Workaround: Some Netgear/Linksys 11ac APs set Rx A-MPDU factor to
+ * zero in VHT IE. Using it would result in degraded throughput.
+ * arg->peer_max_mpdu at this point contains HT max_mpdu so keep
+ * it if VHT max_mpdu is smaller. */
+ arg->peer_max_mpdu = max(arg->peer_max_mpdu,
+ (1U << (IEEE80211_HT_MAX_AMPDU_FACTOR +
+ ampdu_factor)) - 1);
+
if (sta->bandwidth == IEEE80211_STA_RX_BW_80)
arg->peer_flags |= WMI_PEER_80MHZ;

--
1.8.4.rc3


2013-10-07 16:36:03

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH v2 2/2] ath10k: fix scheduling while atomic bug

Michal Kazior <[email protected]> writes:

> Recent WMI/HTC changes made it possible for WMI
> commands to sleep (if there's not enough HTC TX
> credits to submit a command). TX path is in an
> atomic context so calling WMI commands in it is
> wrong.
>
> This simply moves WEP key index update to a worker
> and fixes the 'scheduling while atomic' bug.
>
> This still leaves multiple WEP key handling laggy,
> i.e. some frames may be TXed with an old/different
> key (although recipient should still be able to RX
> them).
>
> Signed-off-by: Michal Kazior <[email protected]>

I see conflicts with this patch. Can you rebase, please?

--
Kalle Valo

2013-10-07 16:31:59

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH v2 0/2] ath10k: wep & ampdu fix

Michal Kazior <[email protected]> writes:

> Hi,
>
> One throughput fix and one bug fix.
>
> The bug fix addresses my oversight when reworking
> HTC/WMI. I haven't seen the error yet but I'm
> quite certain this should be fixed.
>
> v2:
> * A-MPDU patch contains a workaround for
> Netgear/Linksys 11ac APs
> (Kalle, can you kindly test this, please?)

I quickly tested patch 1 with D-Link DIR-865L. I didn't notice any
regressions so that part looks good. Though I didn't notice any
improvement either. But as long as there's no regression I'm happy :)

--
Kalle Valo

2013-10-04 08:17:52

by Michal Kazior

[permalink] [raw]
Subject: [PATCH v2 2/2] ath10k: fix scheduling while atomic bug

Recent WMI/HTC changes made it possible for WMI
commands to sleep (if there's not enough HTC TX
credits to submit a command). TX path is in an
atomic context so calling WMI commands in it is
wrong.

This simply moves WEP key index update to a worker
and fixes the 'scheduling while atomic' bug.

This still leaves multiple WEP key handling laggy,
i.e. some frames may be TXed with an old/different
key (although recipient should still be able to RX
them).

Signed-off-by: Michal Kazior <[email protected]>
---
drivers/net/wireless/ath/ath10k/core.h | 4 ++-
drivers/net/wireless/ath/ath10k/mac.c | 53 ++++++++++++++++++++++----------
2 files changed, 39 insertions(+), 18 deletions(-)

diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h
index ce36daa..cef5455 100644
--- a/drivers/net/wireless/ath/ath10k/core.h
+++ b/drivers/net/wireless/ath/ath10k/core.h
@@ -215,8 +215,10 @@ struct ath10k_vif {
struct ath10k *ar;
struct ieee80211_vif *vif;

+ struct work_struct wep_key_work;
struct ieee80211_key_conf *wep_keys[WMI_MAX_KEY_INDEX + 1];
- u8 def_wep_key_index;
+ u8 def_wep_key_idx;
+ u8 def_wep_key_newidx;

u16 tx_seq_no;

diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c
index 7e06e75..378d868 100644
--- a/drivers/net/wireless/ath/ath10k/mac.c
+++ b/drivers/net/wireless/ath/ath10k/mac.c
@@ -1238,7 +1238,7 @@ static void ath10k_bss_disassoc(struct ieee80211_hw *hw,
/* FIXME: why don't we print error if wmi call fails? */
ret = ath10k_wmi_vdev_down(ar, arvif->vdev_id);

- arvif->def_wep_key_index = 0;
+ arvif->def_wep_key_idx = 0;
}

static int ath10k_station_assoc(struct ath10k *ar, struct ath10k_vif *arvif,
@@ -1439,6 +1439,30 @@ static void ath10k_tx_h_qos_workaround(struct ieee80211_hw *hw,
skb_pull(skb, IEEE80211_QOS_CTL_LEN);
}

+static void ath10k_tx_wep_key_work(struct work_struct *work)
+{
+ struct ath10k_vif *arvif = container_of(work, struct ath10k_vif,
+ wep_key_work);
+ int ret, keyidx = arvif->def_wep_key_newidx;
+
+ if (arvif->def_wep_key_idx == keyidx)
+ return;
+
+ ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d set keyidx %d\n",
+ arvif->vdev_id, keyidx);
+
+ ret = ath10k_wmi_vdev_set_param(arvif->ar,
+ arvif->vdev_id,
+ arvif->ar->wmi.vdev_param->def_keyid,
+ keyidx);
+ if (ret) {
+ ath10k_warn("could not update wep keyidx (%d)\n", ret);
+ return;
+ }
+
+ arvif->def_wep_key_idx = keyidx;
+}
+
static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
{
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
@@ -1447,8 +1471,6 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
struct ath10k *ar = arvif->ar;
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct ieee80211_key_conf *key = info->control.hw_key;
- u32 vdev_param;
- int ret;

if (!ieee80211_has_protected(hdr->frame_control))
return;
@@ -1460,21 +1482,14 @@ static void ath10k_tx_h_update_wep_key(struct sk_buff *skb)
key->cipher != WLAN_CIPHER_SUITE_WEP104)
return;

- if (key->keyidx == arvif->def_wep_key_index)
- return;
-
- ath10k_dbg(ATH10K_DBG_MAC, "mac vdev %d keyidx %d\n",
- arvif->vdev_id, key->keyidx);
-
- vdev_param = ar->wmi.vdev_param->def_keyid;
- ret = ath10k_wmi_vdev_set_param(ar, arvif->vdev_id, vdev_param,
- key->keyidx);
- if (ret) {
- ath10k_warn("could not update wep keyidx (%d)\n", ret);
+ if (key->keyidx == arvif->def_wep_key_idx)
return;
- }

- arvif->def_wep_key_index = key->keyidx;
+ /* FIXME: Most likely a few frames will be TXed with an old key. Simply
+ * queueing frames until key index is updated is not an option because
+ * sk_buff may need more processing to be done, e.g. offchannel */
+ arvif->def_wep_key_newidx = key->keyidx;
+ ieee80211_queue_work(ar->hw, &arvif->wep_key_work);
}

static void ath10k_tx_h_add_p2p_noa_ie(struct ath10k *ar, struct sk_buff *skb)
@@ -2010,6 +2025,8 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,
arvif->ar = ar;
arvif->vif = vif;

+ INIT_WORK(&arvif->wep_key_work, ath10k_tx_wep_key_work);
+
if ((vif->type == NL80211_IFTYPE_MONITOR) && ar->monitor_present) {
ath10k_warn("Only one monitor interface allowed\n");
ret = -EBUSY;
@@ -2065,7 +2082,7 @@ static int ath10k_add_interface(struct ieee80211_hw *hw,

vdev_param = ar->wmi.vdev_param->def_keyid;
ret = ath10k_wmi_vdev_set_param(ar, 0, vdev_param,
- arvif->def_wep_key_index);
+ arvif->def_wep_key_idx);
if (ret)
ath10k_warn("Failed to set default keyid: %d\n", ret);

@@ -2133,6 +2150,8 @@ static void ath10k_remove_interface(struct ieee80211_hw *hw,

mutex_lock(&ar->conf_mutex);

+ cancel_work_sync(&arvif->wep_key_work);
+
spin_lock_bh(&ar->data_lock);
if (arvif->beacon) {
dev_kfree_skb_any(arvif->beacon);
--
1.7.9.5