* [PATCH V2 0/3] mac80211: notify the driver when it should enable TWT
From: John Crispin @ 2019-04-26 9:41 UTC (permalink / raw)
To: Johannes Berg, Kalle Valo
Cc: Srini Kode, Rajkumar Manoharan, linux-wireless, ath11k,
John Crispin
The ath11k FW has dedicated calls for setting the TWT state. Trigger these
via the BSS config changes.
In a later step we need to also add calls for manually starting TWT dialogs.
Changes in V2
* add NLA_POLICY_RANGE for NL80211_ATTR_TWT_RESPONDER
John Crispin (3):
mac80211: allow turning TWT responder support on and off via netlink
mac80211: dynamically turn TWT requester support on and off
ath11k: add TWT support
drivers/net/wireless/ath/ath11k/mac.c | 7 +++
drivers/net/wireless/ath/ath11k/wmi.c | 91 +++++++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath11k/wmi.h | 71 +++++++++++++++++++++++++++
include/net/cfg80211.h | 2 +
include/net/mac80211.h | 5 ++
include/uapi/linux/nl80211.h | 4 ++
net/mac80211/cfg.c | 6 +++
net/mac80211/mlme.c | 20 +++++++-
net/wireless/nl80211.c | 6 +++
9 files changed, 210 insertions(+), 2 deletions(-)
--
2.11.0
^ permalink raw reply
* Re: [PATCHv5 5/9] nl80211: Add netlink attribute to configure TID specific tx rate
From: Johannes Berg @ 2019-04-26 9:37 UTC (permalink / raw)
To: Tamizh chelvam, ath10k; +Cc: linux-wireless
In-Reply-To: <1553592550-15282-6-git-send-email-tamizhr@codeaurora.org>
> @@ -13354,6 +13367,42 @@ static int parse_tid_conf(struct cfg80211_registered_device *rdev,
> nla_get_u8(attrs[NL80211_ATTR_TID_CONFIG_RTSCTS_CTRL]);
> }
>
> + if (attrs[NL80211_ATTR_TID_CONFIG_TX_RATES_TYPE]) {
> + int idx;
> + enum nl80211_attrs attr;
> +
> + if (!wiphy_ext_feature_isset(&rdev->wiphy,
> + NL80211_EXT_FEATURE_PER_TID_TX_BITRATE_MASK))
> + return -EOPNOTSUPP;
> +
> + if (peer &&
> + !wiphy_ext_feature_isset(&rdev->wiphy,
> + NL80211_EXT_FEATURE_PER_STA_TX_BITRATE_MASK))
> + return -EOPNOTSUPP;
> +
> + idx = NL80211_ATTR_TID_CONFIG_TX_RATES_TYPE;
> + tid_conf->txrate_type = nla_get_u8(attrs[idx]);
> +
> + tid_conf->tid_conf_mask |= IEEE80211_TID_CONF_TX_BITRATE;
> + if (tid_conf->txrate_type != NL80211_TX_RATE_AUTOMATIC) {
> + tid_conf->mask =
> + kzalloc(sizeof(struct cfg80211_bitrate_mask),
> + GFP_KERNEL);
You leak this
> + if (!tid_conf->mask)
> + return -ENOMEM;
> +
> + attr = NL80211_ATTR_TID_CONFIG_TX_RATES;
> + ret = nl80211_parse_tx_bitrate_mask(attrs, rdev, attr,
> + tid_conf->mask);
> + if (ret) {
> + kfree(tid_conf->mask);
> + return ret;
> + }
> + } else {
> + tid_conf->mask = NULL;
> + }
> + }
> +
> return 0;
> }
>
> @@ -13407,7 +13456,7 @@ static int nl80211_set_tid_config(struct sk_buff *skb,
> }
>
> ret = parse_tid_conf(rdev, attrs, &tid_conf->tid_conf[conf_idx],
> - tid_conf->peer);
> + info, tid_conf->peer);
> if (ret)
> goto bad_tid_conf;
Practically everywhere, but particularly in the bad case in the next
loop iteration etc?
johannes
^ permalink raw reply
* Re: [PATCHv5 2/9] nl80211: Add new netlink attribute for TID speicific retry count
From: Johannes Berg @ 2019-04-26 9:32 UTC (permalink / raw)
To: Tamizh chelvam, ath10k; +Cc: linux-wireless
In-Reply-To: <1553592550-15282-3-git-send-email-tamizhr@codeaurora.org>
On Tue, 2019-03-26 at 14:59 +0530, Tamizh chelvam wrote:
>
> @@ -13288,6 +13291,37 @@ static int parse_tid_conf(struct cfg80211_registered_device *rdev,
> nla_get_u8(attrs[NL80211_ATTR_TID_CONFIG_NOACK]);
> }
>
> + if (nla_get_flag(attrs[NL80211_ATTR_TID_CONFIG_RETRY])) {
> + if (!wiphy_ext_feature_isset(&rdev->wiphy,
> + NL80211_EXT_FEATURE_PER_TID_RETRY_CONFIG))
> + return -ENOTSUPP;
> +
> + if (peer && !wiphy_ext_feature_isset(&rdev->wiphy,
> + NL80211_EXT_FEATURE_PER_STA_RETRY_CONFIG))
> + return -ENOTSUPP;
> +
> + tid_conf->tid_conf_mask |= IEEE80211_TID_CONF_RETRY;
> + if (attrs[NL80211_ATTR_TID_CONFIG_RETRY_SHORT]) {
> + tid_conf->retry_short =
> + nla_get_u8(attrs[NL80211_ATTR_TID_CONFIG_RETRY_SHORT]);
> + if (tid_conf->retry_short >
> + rdev->wiphy.max_data_retry_count)
> + return -EINVAL;
>
Forgot to comment on that before (perhaps also in the first patch), but
having extended error messages for this would be very useful.
johannes
^ permalink raw reply
* Re: [PATCHv5 2/9] nl80211: Add new netlink attribute for TID speicific retry count
From: Johannes Berg @ 2019-04-26 9:31 UTC (permalink / raw)
To: Tamizh chelvam, ath10k; +Cc: linux-wireless
In-Reply-To: <1553592550-15282-3-git-send-email-tamizhr@codeaurora.org>
On Tue, 2019-03-26 at 14:59 +0530, Tamizh chelvam wrote:
>
> + [NL80211_ATTR_TID_CONFIG_RETRY] = { .type = NLA_FLAG },
> + [NL80211_ATTR_TID_CONFIG_RETRY_SHORT] = NLA_POLICY_MIN(NLA_U8, 1),
> + [NL80211_ATTR_TID_CONFIG_RETRY_LONG] = NLA_POLICY_MIN(NLA_U8, 1),
>
I guess it's a question of semantics, but for *TX retries* 0 should be
valid. Unless you're setting *TX attempts* (including the first one) but
that would be wrong...
So I guess you should clarify that in the documentation and really allow
0, which is sort of equivalent to no-ack but not really because it does
request an ACK.
johannes
^ permalink raw reply
* Re: [PATCHv5 1/9] nl80211: New netlink command for TID specific configuration
From: Johannes Berg @ 2019-04-26 9:28 UTC (permalink / raw)
To: Tamizh chelvam, ath10k; +Cc: linux-wireless
In-Reply-To: <1553592550-15282-2-git-send-email-tamizhr@codeaurora.org>
Hi,
Sorry, I apologize for taking so long to review this (again).
> +enum ieee80211_tid_conf_mask {
> + IEEE80211_TID_CONF_NOACK = BIT(0),
> +};
> +
> +/**
> + * struct ieee80211_tid_cfg - TID specific configuration
> + * @tid: TID number
> + * @tid_conf_mask: bitmap indicating which parameter changed
> + * see %enum ieee80211_tid_conf_mask
> + * @noack: noack configuration value for the TID
> + */
> +struct ieee80211_tid_cfg {
> + u8 tid;
> + enum ieee80211_tid_conf_mask tid_conf_mask;
This shouldn't use the enum type if it's a bitmap. Doing the enum type
above is only useful for documentation, which you should add there.
> + * @set_tid_config: TID specific configuration. Apply this configuration for
> + * all the connected stations in the BSS if peer is NULL. Otherwise
%NULL renders better, IIRC
> + * @NL80211_ATTR_TID_CONFIG: TID specific configuration in a
> + * nested attribute with %NL80211_ATTR_TID_* sub-attributes.
Please use NL80211_TID_ATTR_* to disambiguate the namespaces
> +enum nl80211_tid_config {
> + NL80211_TID_CONFIG_DEFAULT,
> + NL80211_TID_CONFIG_ENABLE,
> + NL80211_TID_CONFIG_DISABLE,
> +};
That could do with some documentation
> +
> +/* enum nl80211_attr_tid_config - TID specific configuration.
> + * @NL80211_ATTR_TID_CONFIG_TID: a TID value (u8 attribute).
See above - TID_ATTR_* is better.
> + * @NL80211_ATTR_TID_CONFIG_NOACK: Configure ack policy for the TID.
> + * specified in %NL80211_ATTR_TID_CONFIG_TID. see %enum nl80211_tid_config.
> + * Its type is u8, if the peer MAC address is passed in %NL80211_ATTR_MAC,
> + * then the noack configuration is applied to the data frame for the tid
> + * to that connected station. This configuration is valid only for STA's
> + * current connection. i.e. the configuration will be reset to default when
> + * the station connects back after disconnection/roaming.
> + * when user-space does not include %NL80211_ATTR_MAC, then this
please use tabs consistently
> + * configuration should be treated as per-netdev configuration.
> + * This configuration will be cleared when the interface goes down and on
> + * the disconnection from a BSS.
"goes down" is redundant then? Or do you mean that's for the AP case?
> +static const struct nla_policy
> +nl80211_attr_tid_config_policy[NL80211_ATTR_TID_CONFIG_MAX + 1] = {
> + [NL80211_ATTR_TID_CONFIG_TID] = { .type = NLA_U8 },
Shouldn't this use NLA_POLICY_RANGE() or MAX()?
> + [NL80211_ATTR_TID_CONFIG_NOACK] =
> + NLA_POLICY_MAX(NLA_U8, NL80211_TID_CONFIG_DISABLE),
> +};
> +
> const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
> [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
> [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
> @@ -541,6 +548,8 @@ static int validate_ie_attr(const struct nlattr *attr,
> [NL80211_ATTR_PEER_MEASUREMENTS] =
> NLA_POLICY_NESTED(nl80211_pmsr_attr_policy),
> [NL80211_ATTR_AIRTIME_WEIGHT] = NLA_POLICY_MIN(NLA_U16, 1),
> + [NL80211_ATTR_TID_CONFIG] =
> + NLA_POLICY_NESTED(nl80211_attr_tid_config_policy),
> };
Great! :-)
> /* policy for the key attributes */
> @@ -13259,6 +13268,93 @@ static int nl80211_get_ftm_responder_stats(struct sk_buff *skb,
> return -ENOBUFS;
> }
>
> +static int parse_tid_conf(struct cfg80211_registered_device *rdev,
> + struct nlattr *attrs[],
> + struct ieee80211_tid_cfg *tid_conf,
> + const u8 *peer)
> +{
> + tid_conf->tid = nla_get_u8(attrs[NL80211_ATTR_TID_CONFIG_TID]);
You need to check that this is even present!
> + size_of_conf = sizeof(struct ieee80211_tid_config) +
> + num_conf * sizeof(struct ieee80211_tid_cfg);
use struct_size()
> + tid_conf = kzalloc(size_of_conf, GFP_KERNEL);
> + if (!tid_conf)
> + return -ENOMEM;
> +
> + tid_conf->n_tid_conf = num_conf;
> +
> + if (info->attrs[NL80211_ATTR_MAC])
> + tid_conf->peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
> + else
> + tid_conf->peer = NULL;
No need to initialize to NULL after kzalloc()
> + nla_for_each_nested(tid, info->attrs[NL80211_ATTR_TID_CONFIG],
> + rem_conf) {
> + ret = nla_parse_nested(attrs, NL80211_ATTR_TID_CONFIG_MAX, tid,
> + NULL, NULL);
> +
> + if (ret)
> + goto bad_tid_conf;
> +
> + if (!attrs[NL80211_ATTR_TID_CONFIG_TID]) {
> + ret = -EINVAL;
> + goto bad_tid_conf;
> + }
Oh, you check here. Perhaps easier to do inside though?
> + {
> + .cmd = NL80211_CMD_SET_TID_CONFIG,
> + .doit = nl80211_set_tid_config,
> + .policy = nl80211_policy,
The .policy field no longer exists.
johannes
^ permalink raw reply
* pull-request: mac80211 2019-04-26
From: Johannes Berg @ 2019-04-26 9:07 UTC (permalink / raw)
To: David Miller; +Cc: netdev, linux-wireless
Hi Dave,
Just have a few more stragglers, looks like things have quieted down.
Please pull and let me know if there's any problem.
Thanks,
johannes
The following changes since commit c03fd0171ba6c8807ff5ba7c797896c84e18f3bc:
Merge tag 'mac80211-for-davem-2019-04-09' of git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211 (2019-04-09 10:57:10 -0700)
are available in the Git repository at:
git://git.kernel.org/pub/scm/linux/kernel/git/jberg/mac80211.git tags/mac80211-for-davem-2019-04-26
for you to fetch changes up to 517879147493a5e1df6b89a50f708f1133fcaddb:
mac80211: don't attempt to rename ERR_PTR() debugfs dirs (2019-04-23 13:47:05 +0200)
----------------------------------------------------------------
* fix use-after-free in mac80211 TXQs
* fix RX STBC byte order
* fix debugfs rename crashing due to ERR_PTR()
* fix missing regulatory notification
----------------------------------------------------------------
Bhagavathi Perumal S (1):
mac80211: Fix kernel panic due to use of txq after free
Johannes Berg (2):
mac80211: fix RX STBC override byte order
mac80211: don't attempt to rename ERR_PTR() debugfs dirs
Sriram R (1):
cfg80211: Notify previous user request during self managed wiphy registration
net/mac80211/debugfs_netdev.c | 2 +-
net/mac80211/ht.c | 5 +++--
net/mac80211/iface.c | 3 +++
net/wireless/reg.c | 5 ++---
4 files changed, 9 insertions(+), 6 deletions(-)
^ permalink raw reply
* Re: [PATCH 1/3] mac80211: allow turning TWT responder support on and off via netlink
From: Johannes Berg @ 2019-04-26 8:49 UTC (permalink / raw)
To: John Crispin, Kalle Valo
Cc: Srini Kode, Rajkumar Manoharan, linux-wireless, ath11k,
Shashidhar Lakkavalli
In-Reply-To: <20190426084244.28098-2-john@phrozen.org>
On Fri, 2019-04-26 at 10:42 +0200, John Crispin wrote:
>
> +++ b/net/wireless/nl80211.c
> @@ -6105,6 +6105,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
> params.ht_opmode = -1;
> params.p2p_ctwindow = -1;
> params.p2p_opp_ps = -1;
> + params.twt_responder = -1;
>
> if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
> params.use_cts_prot =
> @@ -6149,6 +6150,10 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
> return -EINVAL;
> }
>
> + if (info->attrs[NL80211_ATTR_TWT_RESPONDER])
> + params.twt_responder =
> + nla_get_u8(info->attrs[NL80211_ATTR_TWT_RESPONDER]);
You need an NLA_POLICY_RANGE() policy for that attribute.
johannes
^ permalink raw reply
* [PATCH 2/3] mac80211: dynamically turn TWT requester support on and off
From: John Crispin @ 2019-04-26 8:42 UTC (permalink / raw)
To: Johannes Berg, Kalle Valo
Cc: Srini Kode, Rajkumar Manoharan, linux-wireless, ath11k,
John Crispin, Shashidhar Lakkavalli
In-Reply-To: <20190426084244.28098-1-john@phrozen.org>
Turn TWT for STA interfaces when they associate and/or receive a
beacon where the twt_responder bit has changed.
Signed-off-by: Shashidhar Lakkavalli <slakkavalli@datto.com>
Signed-off-by: John Crispin <john@phrozen.org>
---
net/mac80211/mlme.c | 20 ++++++++++++++++++--
1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index 2dbcf5d5512e..f3563670ed58 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -3151,6 +3151,21 @@ static bool ieee80211_twt_req_supported(const struct sta_info *sta,
IEEE80211_HE_MAC_CAP0_TWT_RES;
}
+static int ieee80211_recalc_twt_req(struct ieee80211_sub_if_data *sdata,
+ struct sta_info *sta,
+ struct ieee802_11_elems *elems)
+{
+ bool twt_req;
+
+ twt_req = ieee80211_twt_req_supported(sta, elems);
+
+ if (sdata->vif.bss_conf.twt_requester != twt_req) {
+ sdata->vif.bss_conf.twt_requester = twt_req;
+ return BSS_CHANGED_TWT;
+ }
+ return 0;
+}
+
static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
struct cfg80211_bss *cbss,
struct ieee80211_mgmt *mgmt, size_t len)
@@ -3333,8 +3348,7 @@ static bool ieee80211_assoc_success(struct ieee80211_sub_if_data *sdata,
sta);
bss_conf->he_support = sta->sta.he_cap.has_he;
- bss_conf->twt_requester =
- ieee80211_twt_req_supported(sta, &elems);
+ changed |= ieee80211_recalc_twt_req(sdata, sta, &elems);
} else {
bss_conf->he_support = false;
bss_conf->twt_requester = false;
@@ -3994,6 +4008,8 @@ static void ieee80211_rx_mgmt_beacon(struct ieee80211_sub_if_data *sdata,
mutex_lock(&local->sta_mtx);
sta = sta_info_get(sdata, bssid);
+ changed |= ieee80211_recalc_twt_req(sdata, sta, &elems);
+
if (ieee80211_config_bw(sdata, sta,
elems.ht_cap_elem, elems.ht_operation,
elems.vht_operation, elems.he_operation,
--
2.11.0
^ permalink raw reply related
* [PATCH 3/3] ath11k: add TWT support
From: John Crispin @ 2019-04-26 8:42 UTC (permalink / raw)
To: Johannes Berg, Kalle Valo
Cc: Srini Kode, Rajkumar Manoharan, linux-wireless, ath11k,
John Crispin, Shashidhar Lakkavalli
In-Reply-To: <20190426084244.28098-1-john@phrozen.org>
Add target wait time wmi calls to the driver. En/disable the support
from when the bss_config changes. We ignore the cmd completion events.
Signed-off-by: Shashidhar Lakkavalli <slakkavalli@datto.com>
Signed-off-by: John Crispin <john@phrozen.org>
---
drivers/net/wireless/ath/ath11k/mac.c | 7 +++
drivers/net/wireless/ath/ath11k/wmi.c | 91 +++++++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath11k/wmi.h | 71 +++++++++++++++++++++++++++
3 files changed, 169 insertions(+)
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index 8bb4ff82fb6f..a38c101e0140 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -1938,6 +1938,13 @@ static void ath11k_bss_info_changed(struct ieee80211_hw *hw,
ath11k_mac_txpower_recalc(ar);
}
+ if (changed & BSS_CHANGED_TWT) {
+ if (info->twt_responder || info->twt_responder)
+ ath11k_wmi_send_twt_enable_cmd(ar, ar->pdev_idx);
+ else
+ ath11k_wmi_send_twt_disable_cmd(ar, ar->pdev_idx);
+ }
+
mutex_unlock(&ar->conf_mutex);
}
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index 7edaed5c97df..52d6fb200d07 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -1797,6 +1797,10 @@ ath11k_wmi_copy_peer_flags(struct wmi_peer_assoc_complete_cmd *cmd,
cmd->peer_flags |= WMI_PEER_VHT;
if (param->he_flag)
cmd->peer_flags |= WMI_PEER_HE;
+ if (param->twt_requester)
+ cmd->peer_flags |= WMI_PEER_TWT_REQ;
+ if (param->twt_responder)
+ cmd->peer_flags |= WMI_PEER_TWT_RESP;
}
/* Suppress authorization for all AUTH modes that need 4-way handshake
@@ -2787,6 +2791,86 @@ int ath11k_wmi_pdev_pktlog_disable(struct ath11k *ar)
return ret;
}
+int
+ath11k_wmi_send_twt_enable_cmd(struct ath11k *ar, u32 pdev_id)
+{
+ struct ath11k_pdev_wmi *wmi = ar->wmi;
+ struct ath11k_base *ab = wmi->wmi_sc->sc;
+ struct wmi_twt_enable_params_cmd *cmd;
+ struct sk_buff *skb;
+ int ret, len;
+
+ len = sizeof(*cmd);
+
+ skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len);
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (void *)skb->data;
+ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_TWT_ENABLE_CMD) |
+ FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE);
+ cmd->pdev_id = pdev_id;
+ cmd->sta_cong_timer_ms = ATH11K_TWT_DEF_STA_CONG_TIMER_MS;
+ cmd->default_slot_size = ATH11K_TWT_DEF_DEFAULT_SLOT_SIZE;
+ cmd->congestion_thresh_setup = ATH11K_TWT_DEF_CONGESTION_THRESH_SETUP;
+ cmd->congestion_thresh_teardown =
+ ATH11K_TWT_DEF_CONGESTION_THRESH_TEARDOWN;
+ cmd->congestion_thresh_critical =
+ ATH11K_TWT_DEF_CONGESTION_THRESH_CRITICAL;
+ cmd->interference_thresh_teardown =
+ ATH11K_TWT_DEF_INTERFERENCE_THRESH_TEARDOWN;
+ cmd->interference_thresh_setup =
+ ATH11K_TWT_DEF_INTERFERENCE_THRESH_SETUP;
+ cmd->min_no_sta_setup = ATH11K_TWT_DEF_MIN_NO_STA_SETUP;
+ cmd->min_no_sta_teardown = ATH11K_TWT_DEF_MIN_NO_STA_TEARDOWN;
+ cmd->no_of_bcast_mcast_slots = ATH11K_TWT_DEF_NO_OF_BCAST_MCAST_SLOTS;
+ cmd->min_no_twt_slots = ATH11K_TWT_DEF_MIN_NO_TWT_SLOTS;
+ cmd->max_no_sta_twt = ATH11K_TWT_DEF_MAX_NO_STA_TWT;
+ cmd->mode_check_interval = ATH11K_TWT_DEF_MODE_CHECK_INTERVAL;
+ cmd->add_sta_slot_interval = ATH11K_TWT_DEF_ADD_STA_SLOT_INTERVAL;
+ cmd->remove_sta_slot_interval =
+ ATH11K_TWT_DEF_REMOVE_STA_SLOT_INTERVAL;
+ /* TODO add MBSSID support */
+ cmd->mbss_support = 0;
+
+ ret = ath11k_wmi_cmd_send(wmi, skb,
+ WMI_TWT_ENABLE_CMDID);
+ if (ret) {
+ ath11k_warn(ab, "Failed to send WMI_TWT_ENABLE_CMDID");
+ dev_kfree_skb(skb);
+ }
+ return ret;
+}
+
+int
+ath11k_wmi_send_twt_disable_cmd(struct ath11k *ar, u32 pdev_id)
+{
+ struct ath11k_pdev_wmi *wmi = ar->wmi;
+ struct ath11k_base *ab = wmi->wmi_sc->sc;
+ struct wmi_twt_disable_params_cmd *cmd;
+ struct sk_buff *skb;
+ int ret, len;
+
+ len = sizeof(*cmd);
+
+ skb = ath11k_wmi_alloc_skb(wmi->wmi_sc, len);
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (void *)skb->data;
+ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_TWT_DISABLE_CMD) |
+ FIELD_PREP(WMI_TLV_LEN, len - TLV_HDR_SIZE);
+ cmd->pdev_id = pdev_id;
+
+ ret = ath11k_wmi_cmd_send(wmi, skb,
+ WMI_TWT_DISABLE_CMDID);
+ if (ret) {
+ ath11k_warn(ab, "Failed to send WMI_TWT_DIeABLE_CMDID");
+ dev_kfree_skb(skb);
+ }
+ return ret;
+}
+
static inline void ath11k_fill_band_to_mac_param(struct ath11k_base *soc,
struct wmi_host_pdev_band_to_mac *band_to_mac)
{
@@ -2877,6 +2961,9 @@ ath11k_wmi_copy_resource_config(struct wmi_resource_config *wmi_cfg,
wmi_cfg->use_pdev_id = tg_cfg->use_pdev_id;
wmi_cfg->flag1 = tg_cfg->atf_config;
wmi_cfg->peer_map_unmap_v2_support = tg_cfg->peer_map_unmap_v2_support;
+ wmi_cfg->sched_params = tg_cfg->sched_params;
+ wmi_cfg->twt_ap_pdev_count = tg_cfg->twt_ap_pdev_count;
+ wmi_cfg->twt_ap_sta_count = tg_cfg->twt_ap_sta_count;
}
static int ath11k_init_cmd_send(struct ath11k_pdev_wmi *wmi,
@@ -3068,6 +3155,8 @@ int ath11k_wmi_cmd_init(struct ath11k_base *sc)
config.beacon_tx_offload_max_vdev = sc->num_radios * TARGET_MAX_BCN_OFFLD;
config.rx_batchmode = TARGET_RX_BATCHMODE;
config.peer_map_unmap_v2_support = 1;
+ config.twt_ap_pdev_count = 2;
+ config.twt_ap_sta_count = 1000;
memcpy(&wmi_sc->wlan_resource_config, &config, sizeof(config));
@@ -5859,6 +5948,8 @@ static void ath11k_wmi_tlv_op_rx(struct ath11k_base *ab, struct sk_buff *skb)
/* add Unsupported events here */
case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID:
case WMI_VDEV_DELETE_RESP_EVENTID:
+ case WMI_TWT_ENABLE_EVENTID:
+ case WMI_TWT_DISABLE_EVENTID:
ath11k_dbg(ab, ATH11K_DBG_WMI,
"ignoring unsupported event 0x%x\n", id);
break;
diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h
index 071537850c68..99f69fc3cf53 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -169,6 +169,10 @@ enum wmi_cmd_group {
WMI_GRP_MONITOR, /* 0x39 */
WMI_GRP_REGULATORY, /* 0x3a */
WMI_GRP_HW_DATA_FILTER, /* 0x3b */
+ WMI_GRP_WLM, /* 0x3c */
+ WMI_GRP_11K_OFFLOAD, /* 0x3d */
+ WMI_GRP_TWT, /* 0x3e */
+
};
#define WMI_CMD_GRP(grp_id) (((grp_id) << 12) | 0x1)
@@ -531,6 +535,12 @@ enum wmi_tlv_cmd_id {
WMI_NDP_RESPONDER_REQ_CMDID,
WMI_NDP_END_REQ_CMDID,
WMI_HW_DATA_FILTER_CMDID = WMI_TLV_CMD(WMI_GRP_HW_DATA_FILTER),
+ WMI_TWT_ENABLE_CMDID = WMI_TLV_CMD(WMI_GRP_TWT),
+ WMI_TWT_DISABLE_CMDID,
+ WMI_TWT_ADD_DIALOG_CMDID,
+ WMI_TWT_DEL_DIALOG_CMDID,
+ WMI_TWT_PAUSE_DIALOG_CMDID,
+ WMI_TWT_RESUME_DIALOG_CMDID,
};
enum wmi_tlv_event_id {
@@ -713,6 +723,13 @@ enum wmi_tlv_event_id {
WMI_NDP_INDICATION_EVENTID,
WMI_NDP_CONFIRM_EVENTID,
WMI_NDP_END_INDICATION_EVENTID,
+
+ WMI_TWT_ENABLE_EVENTID = WMI_TLV_CMD(WMI_GRP_TWT),
+ WMI_TWT_DISABLE_EVENTID,
+ WMI_TWT_ADD_DIALOG_EVENTID,
+ WMI_TWT_DEL_DIALOG_EVENTID,
+ WMI_TWT_PAUSE_DIALOG_EVENTID,
+ WMI_TWT_RESUME_DIALOG_EVENTID,
};
enum wmi_tlv_pdev_param {
@@ -2736,6 +2753,9 @@ struct wmi_resource_config {
u32 max_num_dbs_scan_duty_cycle;
u32 max_num_group_keys;
u32 peer_map_unmap_v2_support;
+ u32 sched_params;
+ u32 twt_ap_pdev_count;
+ u32 twt_ap_sta_count;
} __packed;
struct wmi_service_ready_event {
@@ -3907,6 +3927,8 @@ struct peer_assoc_params {
u32 peer_he_mcs_count;
u32 peer_he_rx_mcs_set[WMI_HOST_MAX_HE_RATE_SET];
u32 peer_he_tx_mcs_set[WMI_HOST_MAX_HE_RATE_SET];
+ bool twt_responder;
+ bool twt_requester;
struct ath11k_ppe_threshold peer_ppet;
};
@@ -4130,6 +4152,8 @@ struct wmi_pktlog_disable_cmd {
#define WMI_PEER_DYN_MIMOPS 0x00020000
#define WMI_PEER_STATIC_MIMOPS 0x00040000
#define WMI_PEER_SPATIAL_MUX 0x00200000
+#define WMI_PEER_TWT_REQ 0x00400000
+#define WMI_PEER_TWT_RESP 0x00800000
#define WMI_PEER_VHT 0x02000000
#define WMI_PEER_80MHZ 0x04000000
#define WMI_PEER_PMF 0x08000000
@@ -5005,6 +5029,48 @@ struct wmi_wmm_params_all_arg {
struct wmi_wmm_params_arg ac_vo;
};
+#define ATH11K_TWT_DEF_STA_CONG_TIMER_MS 5000
+#define ATH11K_TWT_DEF_DEFAULT_SLOT_SIZE 10
+#define ATH11K_TWT_DEF_CONGESTION_THRESH_SETUP 50
+#define ATH11K_TWT_DEF_CONGESTION_THRESH_TEARDOWN 20
+#define ATH11K_TWT_DEF_CONGESTION_THRESH_CRITICAL 100
+#define ATH11K_TWT_DEF_INTERFERENCE_THRESH_TEARDOWN 80
+#define ATH11K_TWT_DEF_INTERFERENCE_THRESH_SETUP 50
+#define ATH11K_TWT_DEF_MIN_NO_STA_SETUP 10
+#define ATH11K_TWT_DEF_MIN_NO_STA_TEARDOWN 2
+#define ATH11K_TWT_DEF_NO_OF_BCAST_MCAST_SLOTS 2
+#define ATH11K_TWT_DEF_MIN_NO_TWT_SLOTS 2
+#define ATH11K_TWT_DEF_MAX_NO_STA_TWT 500
+#define ATH11K_TWT_DEF_MODE_CHECK_INTERVAL 10000
+#define ATH11K_TWT_DEF_ADD_STA_SLOT_INTERVAL 1000
+#define ATH11K_TWT_DEF_REMOVE_STA_SLOT_INTERVAL 5000
+
+struct wmi_twt_enable_params_cmd {
+ u32 tlv_header;
+ u32 pdev_id;
+ u32 sta_cong_timer_ms;
+ u32 mbss_support;
+ u32 default_slot_size;
+ u32 congestion_thresh_setup;
+ u32 congestion_thresh_teardown;
+ u32 congestion_thresh_critical;
+ u32 interference_thresh_teardown;
+ u32 interference_thresh_setup;
+ u32 min_no_sta_setup;
+ u32 min_no_sta_teardown;
+ u32 no_of_bcast_mcast_slots;
+ u32 min_no_twt_slots;
+ u32 max_no_sta_twt;
+ u32 mode_check_interval;
+ u32 add_sta_slot_interval;
+ u32 remove_sta_slot_interval;
+};
+
+struct wmi_twt_disable_params_cmd {
+ u32 tlv_header;
+ u32 pdev_id;
+};
+
struct target_resource_config {
u32 num_vdevs;
u32 num_peers;
@@ -5065,6 +5131,9 @@ struct target_resource_config {
u32 max_bssid_rx_filters;
u32 use_pdev_id;
u32 peer_map_unmap_v2_support;
+ u32 sched_params;
+ u32 twt_ap_pdev_count;
+ u32 twt_ap_sta_count;
};
#define WMI_MAX_MEM_REQS 32
@@ -5190,4 +5259,6 @@ size_t ath11k_wmi_fw_stats_num_vdevs(struct list_head *head);
void ath11k_wmi_fw_stats_fill(struct ath11k *ar,
struct ath11k_fw_stats *fw_stats, u32 stats_id,
char *buf);
+int ath11k_wmi_send_twt_enable_cmd(struct ath11k *ar, u32 pdev_id);
+int ath11k_wmi_send_twt_disable_cmd(struct ath11k *ar, u32 pdev_id);
#endif
--
2.11.0
^ permalink raw reply related
* [PATCH 0/3] mac80211: notify the driver when it should enable TWT
From: John Crispin @ 2019-04-26 8:42 UTC (permalink / raw)
To: Johannes Berg, Kalle Valo
Cc: Srini Kode, Rajkumar Manoharan, linux-wireless, ath11k,
John Crispin
The ath11k FW has a dedicated call for setting the TWT state. trigger this
changes to the BSS config.
In a later step we need to also add calls for manually stating TWT dialogs.
John Crispin (3):
mac80211: allow turning TWT responder support on and off via netlink
mac80211: dynamically turn TWT requester support on and off
ath11k: add TWT support
drivers/net/wireless/ath/ath11k/mac.c | 7 +++
drivers/net/wireless/ath/ath11k/wmi.c | 91 +++++++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath11k/wmi.h | 71 +++++++++++++++++++++++++++
include/net/cfg80211.h | 2 +
include/net/mac80211.h | 5 ++
include/uapi/linux/nl80211.h | 4 ++
net/mac80211/cfg.c | 6 +++
net/mac80211/mlme.c | 20 +++++++-
net/wireless/nl80211.c | 5 ++
9 files changed, 209 insertions(+), 2 deletions(-)
--
2.11.0
^ permalink raw reply
* [PATCH 1/3] mac80211: allow turning TWT responder support on and off via netlink
From: John Crispin @ 2019-04-26 8:42 UTC (permalink / raw)
To: Johannes Berg, Kalle Valo
Cc: Srini Kode, Rajkumar Manoharan, linux-wireless, ath11k,
John Crispin, Shashidhar Lakkavalli
In-Reply-To: <20190426084244.28098-1-john@phrozen.org>
Allow the userland daemon to en/disable TWT support for an AP.
Signed-off-by: Shashidhar Lakkavalli <slakkavalli@datto.com>
Signed-off-by: John Crispin <john@phrozen.org>
---
include/net/cfg80211.h | 2 ++
include/net/mac80211.h | 5 +++++
include/uapi/linux/nl80211.h | 4 ++++
net/mac80211/cfg.c | 6 ++++++
net/wireless/nl80211.c | 5 +++++
5 files changed, 22 insertions(+)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index bb307a11ee63..8a20f5a42977 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1511,6 +1511,7 @@ struct mpath_info {
* (u16 = opmode, -1 = do not change)
* @p2p_ctwindow: P2P CT Window (-1 = no change)
* @p2p_opp_ps: P2P opportunistic PS (-1 = no change)
+ * @twt_responder: HE Target Wait Time support.
*/
struct bss_parameters {
int use_cts_prot;
@@ -1521,6 +1522,7 @@ struct bss_parameters {
int ap_isolate;
int ht_opmode;
s8 p2p_ctwindow, p2p_opp_ps;
+ int twt_responder;
};
/**
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index ac2ed8ec662b..87a6e149abda 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -317,6 +317,7 @@ struct ieee80211_vif_chanctx_switch {
* @BSS_CHANGED_MCAST_RATE: Multicast Rate setting changed for this interface
* @BSS_CHANGED_FTM_RESPONDER: fime timing reasurement request responder
* functionality changed for this BSS (AP mode).
+ * @BSS_CHANGED_TWT: TWT enable status changed
*
*/
enum ieee80211_bss_change {
@@ -347,6 +348,7 @@ enum ieee80211_bss_change {
BSS_CHANGED_KEEP_ALIVE = 1<<24,
BSS_CHANGED_MCAST_RATE = 1<<25,
BSS_CHANGED_FTM_RESPONDER = 1<<26,
+ BSS_CHANGED_TWT = 1<<27,
/* when adding here, make sure to change ieee80211_reconfig */
};
@@ -504,6 +506,8 @@ struct ieee80211_ftm_responder_params {
* @he_support: does this BSS support HE
* @twt_requester: does this BSS support TWT requester (relevant for managed
* mode only, set if the AP advertises TWT responder role)
+ * @twt_responder: does this BSS support TWT responder (relevant for AP
+ * mode only, set if the AP advertises TWT responder role)
* @assoc: association status
* @ibss_joined: indicates whether this station is part of an IBSS
* or not
@@ -611,6 +615,7 @@ struct ieee80211_bss_conf {
u16 frame_time_rts_th;
bool he_support;
bool twt_requester;
+ bool twt_responder;
/* association related data */
bool assoc, ibss_joined;
bool ibss_creator;
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index dd4f86ee286e..ba1f69751a4a 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2308,6 +2308,8 @@ enum nl80211_commands {
* @NL80211_ATTR_AIRTIME_WEIGHT: Station's weight when scheduled by the airtime
* scheduler.
*
+ * @NL80211_ATTR_TWT_RESPONDER: Enable target wait time responder support.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2759,6 +2761,8 @@ enum nl80211_attrs {
NL80211_ATTR_AIRTIME_WEIGHT,
+ NL80211_ATTR_TWT_RESPONDER,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 09dd1c2860fc..a66600bf9cef 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -2176,6 +2176,12 @@ static int ieee80211_change_bss(struct wiphy *wiphy,
changed |= BSS_CHANGED_P2P_PS;
}
+ if (params->twt_responder >= 0) {
+ sdata->vif.bss_conf.twt_responder =
+ params->twt_responder;
+ changed |= BSS_CHANGED_TWT;
+ }
+
ieee80211_bss_info_change_notify(sdata, changed);
return 0;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 25a9e3b5c154..4f98ee0a98f3 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -6105,6 +6105,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
params.ht_opmode = -1;
params.p2p_ctwindow = -1;
params.p2p_opp_ps = -1;
+ params.twt_responder = -1;
if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
params.use_cts_prot =
@@ -6149,6 +6150,10 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;
}
+ if (info->attrs[NL80211_ATTR_TWT_RESPONDER])
+ params.twt_responder =
+ nla_get_u8(info->attrs[NL80211_ATTR_TWT_RESPONDER]);
+
if (!rdev->ops->change_bss)
return -EOPNOTSUPP;
--
2.11.0
^ permalink raw reply related
* Re: [PATCH v2] mt76: fix endianness sparse warnings
From: Stanislaw Gruszka @ 2019-04-26 8:29 UTC (permalink / raw)
To: Ryder Lee
Cc: Lorenzo Bianconi, Felix Fietkau, Roy Luo, YF Luo, Yiwei Chung,
Sean Wang, linux-wireless, linux-mediatek, linux-kernel
In-Reply-To: <d593c15bae7b4d72df17c3c4670ca9e28ef5c47f.1556265836.git.ryder.lee@mediatek.com>
On Fri, Apr 26, 2019 at 04:09:22PM +0800, Ryder Lee wrote:
> Fix many warnings with incorrect endian assumptions.
>
> Reported-by: kbuild test robot <lkp@intel.com>
> Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
Reviewed-by: Stanislaw Gruszka <sgruszka@redhat.com
^ permalink raw reply
* [PATCH v2] mt76: fix endianness sparse warnings
From: Ryder Lee @ 2019-04-26 8:09 UTC (permalink / raw)
To: Lorenzo Bianconi, Felix Fietkau, Stanislaw Gruszka
Cc: Roy Luo, YF Luo, Yiwei Chung, Sean Wang, linux-wireless,
linux-mediatek, linux-kernel, Ryder Lee
Fix many warnings with incorrect endian assumptions.
Reported-by: kbuild test robot <lkp@intel.com>
Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
---
Change since v2: use standard le16_to_cpu() instead of underscored version
---
drivers/net/wireless/mediatek/mt76/mt7603/mac.c | 2 +-
drivers/net/wireless/mediatek/mt76/mt7615/mac.c | 12 ++++++------
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
index 2f2961ee0a92..b1d96a46a41c 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
@@ -627,7 +627,7 @@ mt7603_mac_fill_rx(struct mt7603_dev *dev, struct sk_buff *skb)
status->aggr = unicast &&
!ieee80211_is_qos_nullfunc(hdr->frame_control);
status->tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
- status->seqno = IEEE80211_SEQ_TO_SN(hdr->seq_ctrl);
+ status->seqno = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
return 0;
}
diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
index 1bf3e7b5f6a7..b8f48d10f27a 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
@@ -235,7 +235,7 @@ int mt7615_mac_fill_rx(struct mt7615_dev *dev, struct sk_buff *skb)
status->aggr = unicast &&
!ieee80211_is_qos_nullfunc(hdr->frame_control);
status->tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
- status->seqno = IEEE80211_SEQ_TO_SN(hdr->seq_ctrl);
+ status->seqno = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl));
return 0;
}
@@ -337,7 +337,7 @@ int mt7615_mac_write_txwi(struct mt7615_dev *dev, __le32 *txwi,
struct ieee80211_vif *vif = info->control.vif;
int tx_count = 8;
u8 fc_type, fc_stype, p_fmt, q_idx, omac_idx = 0;
- u16 fc = le16_to_cpu(hdr->frame_control);
+ __le16 fc = hdr->frame_control;
u16 seqno = 0;
u32 val;
@@ -353,8 +353,8 @@ int mt7615_mac_write_txwi(struct mt7615_dev *dev, __le32 *txwi,
tx_count = msta->rate_count;
}
- fc_type = (fc & IEEE80211_FCTL_FTYPE) >> 2;
- fc_stype = (fc & IEEE80211_FCTL_STYPE) >> 4;
+ fc_type = (le16_to_cpu(fc) & IEEE80211_FCTL_FTYPE) >> 2;
+ fc_stype = (le16_to_cpu(fc) & IEEE80211_FCTL_STYPE) >> 4;
if (ieee80211_is_data(fc)) {
q_idx = skb_get_queue_mapping(skb);
@@ -468,7 +468,7 @@ void mt7615_txp_skb_unmap(struct mt76_dev *dev,
txp = (struct mt7615_txp *)(txwi + MT_TXD_SIZE);
for (i = 1; i < txp->nbuf; i++)
dma_unmap_single(dev->dev, le32_to_cpu(txp->buf[i]),
- le32_to_cpu(txp->len[i]), DMA_TO_DEVICE);
+ le16_to_cpu(txp->len[i]), DMA_TO_DEVICE);
}
int mt7615_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
@@ -506,7 +506,7 @@ int mt7615_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr,
txp = (struct mt7615_txp *)(txwi + MT_TXD_SIZE);
for (i = 0; i < nbuf; i++) {
txp->buf[i] = cpu_to_le32(tx_info->buf[i + 1].addr);
- txp->len[i] = cpu_to_le32(tx_info->buf[i + 1].len);
+ txp->len[i] = cpu_to_le16(tx_info->buf[i + 1].len);
}
txp->nbuf = nbuf;
--
2.18.0
^ permalink raw reply related
* [PATCH v3 3/3] mt76x02: remove bogus mutex usage
From: Stanislaw Gruszka @ 2019-04-26 7:58 UTC (permalink / raw)
To: linux-wireless; +Cc: Felix Fietkau, Lorenzo Bianconi
In-Reply-To: <20190426075842.2986-1-sgruszka@redhat.com>
mac80211 .start(), .stop() callbacks are never called concurrently with
other callbacks. The only concurencly is with mt76 works which we cancel
on stop() and schedule on start().
This fixes possible deadlock on cancel_delayed_work_sync(&dev->mac_work)
as mac_work also take mutex.
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
---
.../net/wireless/mediatek/mt76/mt76x0/pci.c | 6 -----
.../net/wireless/mediatek/mt76/mt76x0/usb.c | 22 +++++--------------
.../wireless/mediatek/mt76/mt76x2/pci_main.c | 13 +++--------
.../wireless/mediatek/mt76/mt76x2/usb_main.c | 10 ++-------
4 files changed, 10 insertions(+), 41 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c
index f106dbfa665f..0eeccc3b529d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c
@@ -25,8 +25,6 @@ static int mt76x0e_start(struct ieee80211_hw *hw)
{
struct mt76x02_dev *dev = hw->priv;
- mutex_lock(&dev->mt76.mutex);
-
mt76x02_mac_start(dev);
mt76x0_phy_calibrate(dev, true);
ieee80211_queue_delayed_work(dev->mt76.hw, &dev->mt76.mac_work,
@@ -35,8 +33,6 @@ static int mt76x0e_start(struct ieee80211_hw *hw)
MT_CALIBRATE_INTERVAL);
set_bit(MT76_STATE_RUNNING, &dev->mt76.state);
- mutex_unlock(&dev->mt76.mutex);
-
return 0;
}
@@ -62,10 +58,8 @@ static void mt76x0e_stop(struct ieee80211_hw *hw)
{
struct mt76x02_dev *dev = hw->priv;
- mutex_lock(&dev->mt76.mutex);
clear_bit(MT76_STATE_RUNNING, &dev->mt76.state);
mt76x0e_stop_hw(dev);
- mutex_unlock(&dev->mt76.mutex);
}
static void
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
index 406ebfa74195..7c38ec4418db 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
@@ -81,8 +81,10 @@ static void mt76x0u_cleanup(struct mt76x02_dev *dev)
mt76u_queues_deinit(&dev->mt76);
}
-static void mt76x0u_mac_stop(struct mt76x02_dev *dev)
+static void mt76x0u_stop(struct ieee80211_hw *hw)
{
+ struct mt76x02_dev *dev = hw->priv;
+
clear_bit(MT76_STATE_RUNNING, &dev->mt76.state);
cancel_delayed_work_sync(&dev->cal_work);
cancel_delayed_work_sync(&dev->mt76.mac_work);
@@ -106,11 +108,9 @@ static int mt76x0u_start(struct ieee80211_hw *hw)
struct mt76x02_dev *dev = hw->priv;
int ret;
- mutex_lock(&dev->mt76.mutex);
-
ret = mt76x0_mac_start(dev);
if (ret)
- goto out;
+ return ret;
mt76x0_phy_calibrate(dev, true);
ieee80211_queue_delayed_work(dev->mt76.hw, &dev->mt76.mac_work,
@@ -118,19 +118,7 @@ static int mt76x0u_start(struct ieee80211_hw *hw)
ieee80211_queue_delayed_work(dev->mt76.hw, &dev->cal_work,
MT_CALIBRATE_INTERVAL);
set_bit(MT76_STATE_RUNNING, &dev->mt76.state);
-
-out:
- mutex_unlock(&dev->mt76.mutex);
- return ret;
-}
-
-static void mt76x0u_stop(struct ieee80211_hw *hw)
-{
- struct mt76x02_dev *dev = hw->priv;
-
- mutex_lock(&dev->mt76.mutex);
- mt76x0u_mac_stop(dev);
- mutex_unlock(&dev->mt76.mutex);
+ return 0;
}
static const struct ieee80211_ops mt76x0u_ops = {
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c
index 77f63cb14f35..ab716957b8ba 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci_main.c
@@ -22,15 +22,13 @@ mt76x2_start(struct ieee80211_hw *hw)
struct mt76x02_dev *dev = hw->priv;
int ret;
- mutex_lock(&dev->mt76.mutex);
-
ret = mt76x2_mac_start(dev);
if (ret)
- goto out;
+ return ret;
ret = mt76x2_phy_start(dev);
if (ret)
- goto out;
+ return ret;
ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mt76.mac_work,
MT_MAC_WORK_INTERVAL);
@@ -38,10 +36,7 @@ mt76x2_start(struct ieee80211_hw *hw)
MT_WATCHDOG_TIME);
set_bit(MT76_STATE_RUNNING, &dev->mt76.state);
-
-out:
- mutex_unlock(&dev->mt76.mutex);
- return ret;
+ return 0;
}
static void
@@ -49,10 +44,8 @@ mt76x2_stop(struct ieee80211_hw *hw)
{
struct mt76x02_dev *dev = hw->priv;
- mutex_lock(&dev->mt76.mutex);
clear_bit(MT76_STATE_RUNNING, &dev->mt76.state);
mt76x2_stop_hardware(dev);
- mutex_unlock(&dev->mt76.mutex);
}
static int
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c
index 305977a874a4..97bcf6494ec1 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c
@@ -21,30 +21,24 @@ static int mt76x2u_start(struct ieee80211_hw *hw)
struct mt76x02_dev *dev = hw->priv;
int ret;
- mutex_lock(&dev->mt76.mutex);
-
ret = mt76x2u_mac_start(dev);
if (ret)
- goto out;
+ return ret;
ieee80211_queue_delayed_work(mt76_hw(dev), &dev->mt76.mac_work,
MT_MAC_WORK_INTERVAL);
set_bit(MT76_STATE_RUNNING, &dev->mt76.state);
-out:
- mutex_unlock(&dev->mt76.mutex);
- return ret;
+ return 0;
}
static void mt76x2u_stop(struct ieee80211_hw *hw)
{
struct mt76x02_dev *dev = hw->priv;
- mutex_lock(&dev->mt76.mutex);
clear_bit(MT76_STATE_RUNNING, &dev->mt76.state);
mt76u_stop_tx(&dev->mt76);
mt76x2u_stop_hw(dev);
- mutex_unlock(&dev->mt76.mutex);
}
static int
--
2.20.1
^ permalink raw reply related
* [PATCH v3 2/3] mt76usb: fix tx/rx stop
From: Stanislaw Gruszka @ 2019-04-26 7:58 UTC (permalink / raw)
To: linux-wireless; +Cc: Felix Fietkau, Lorenzo Bianconi
In-Reply-To: <20190426075842.2986-1-sgruszka@redhat.com>
Disabling tasklets on stopping rx/tx is wrong. If blocked tasklet
is scheduled and we remove device we will get 100% cpu usage:
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
9 root 20 0 0 0 0 R 93.8 0.0 1:47.19 ksoftirqd/0
by infinite loop in tasklet_action_common() and eventuall crash on next
mt76usb module load:
[ 2068.591964] RIP: 0010:tasklet_action_common.isra.17+0x66/0x100
[ 2068.591966] Code: 41 89 f5 eb 25 f0 48 0f ba 33 00 0f 83 b1 00 00 00 48 8b 7a 20 48 8b 42 18 e8 56 a3 b5 00 f0 80 23 fd 48 89 ea 48 85 ed 74 53 <48> 8b 2a 48 8d 5a 08 f0 48 0f ba 6a 08 01 72 0b 8b 42 10 85 c0 74
[ 2068.591968] RSP: 0018:ffff98758c34be58 EFLAGS: 00010206
[ 2068.591969] RAX: ffff98758e6966d0 RBX: ffff98756e69aef8 RCX: 0000000000000006
[ 2068.591970] RDX: 01060a053d060305 RSI: 0000000000000006 RDI: ffff98758e6966d0
[ 2068.591971] RBP: 01060a053d060305 R08: 0000000000000000 R09: 00000000000203c0
[ 2068.591971] R10: 000003ff65b34f08 R11: 0000000000000001 R12: ffff98758e6966d0
[ 2068.591972] R13: 0000000000000006 R14: 0000000000000040 R15: 0000000000000006
[ 2068.591974] FS: 0000000000000000(0000) GS:ffff98758e680000(0000) knlGS:0000000000000000
[ 2068.591975] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 2068.591975] CR2: 00002c5f73a6cc20 CR3: 00000002f920a001 CR4: 00000000003606e0
[ 2068.591977] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
[ 2068.591978] DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
[ 2068.591978] Call Trace:
[ 2068.591985] __do_softirq+0xe3/0x30a
[ 2068.591989] ? sort_range+0x20/0x20
[ 2068.591990] run_ksoftirqd+0x26/0x40
[ 2068.591992] smpboot_thread_fn+0xc5/0x160
[ 2068.591995] kthread+0x112/0x130
[ 2068.591997] ? kthread_create_on_node+0x40/0x40
[ 2068.591998] ret_from_fork+0x35/0x40
[ 2068.591999] Modules linked in: ccm arc4 fuse rfcomm cmac bnep sunrpc snd_hda_codec_hdmi snd_soc_skl snd_soc_core snd_soc_acpi_intel_match snd_hda_codec_realtek snd_soc_acpi snd_hda_codec_generic snd_soc_skl_ipc snd_soc_sst_ipc snd_soc_sst_dsp snd_hda_ext_core iTCO_wdt snd_hda_intel intel_rapl iTCO_vendor_support x86_pkg_temp_thermal intel_powerclamp btusb mei_wdt coretemp btrtl snd_hda_codec btbcm btintel intel_cstate snd_hwdep intel_uncore uvcvideo snd_hda_core videobuf2_vmalloc videobuf2_memops intel_rapl_perf wmi_bmof videobuf2_v4l2 intel_wmi_thunderbolt snd_seq bluetooth joydev videobuf2_common snd_seq_device snd_pcm videodev media i2c_i801 snd_timer idma64 ecdh_generic intel_lpss_pci intel_lpss mei_me mei ucsi_acpi typec_ucsi processor_thermal_device intel_soc_dts_iosf intel_pch_thermal typec thinkpad_acpi wmi snd soundcore rfkill int3403_thermal int340x_thermal_zone int3400_thermal acpi_thermal_rel acpi_pad pcc_cpufreq uas usb_storage crc32c_intel i915 i2c_algo_bit nvme serio_raw
[ 2068.592033] drm_kms_helper e1000e nvme_core drm video ipv6 [last unloaded: cfg80211]
Fortunate thing is that this not happen frequently, as scheduling
tasklet on blocked state is very exceptional, though might happen.
Due to different RX/TX tasklet processing fix is different for those.
For RX we have to assure rx_tasklet do fail to resubmit buffers
by poisoning urb's and kill the tasklet.
For TX we need to handle all stop cases properly (suspend, module
unload, device removal).
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
---
drivers/net/wireless/mediatek/mt76/mac80211.c | 3 +-
drivers/net/wireless/mediatek/mt76/mt76.h | 7 +-
.../net/wireless/mediatek/mt76/mt76x0/usb.c | 10 +--
.../net/wireless/mediatek/mt76/mt76x2/usb.c | 8 +-
.../wireless/mediatek/mt76/mt76x2/usb_init.c | 1 -
.../wireless/mediatek/mt76/mt76x2/usb_main.c | 1 +
drivers/net/wireless/mediatek/mt76/usb.c | 78 +++++++++++++------
7 files changed, 66 insertions(+), 42 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index 60b86ca00b3d..12c8f16096d9 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -390,7 +390,7 @@ void mt76_rx(struct mt76_dev *dev, enum mt76_rxq_id q, struct sk_buff *skb)
}
EXPORT_SYMBOL_GPL(mt76_rx);
-static bool mt76_has_tx_pending(struct mt76_dev *dev)
+bool mt76_has_tx_pending(struct mt76_dev *dev)
{
struct mt76_queue *q;
int i;
@@ -403,6 +403,7 @@ static bool mt76_has_tx_pending(struct mt76_dev *dev)
return false;
}
+EXPORT_SYMBOL_GPL(mt76_has_tx_pending);
void mt76_set_channel(struct mt76_dev *dev)
{
diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
index 75a0d150a224..90d6e9df02a9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76.h
@@ -696,6 +696,7 @@ void mt76_release_buffered_frames(struct ieee80211_hw *hw,
u16 tids, int nframes,
enum ieee80211_frame_release_type reason,
bool more_data);
+bool mt76_has_tx_pending(struct mt76_dev *dev);
void mt76_set_channel(struct mt76_dev *dev);
int mt76_get_survey(struct ieee80211_hw *hw, int idx,
struct survey_info *survey);
@@ -790,10 +791,10 @@ int mt76u_vendor_request(struct mt76_dev *dev, u8 req,
void mt76u_single_wr(struct mt76_dev *dev, const u8 req,
const u16 offset, const u32 val);
int mt76u_init(struct mt76_dev *dev, struct usb_interface *intf);
-int mt76u_submit_rx_buffers(struct mt76_dev *dev);
int mt76u_alloc_queues(struct mt76_dev *dev);
-void mt76u_stop_queues(struct mt76_dev *dev);
-void mt76u_stop_stat_wk(struct mt76_dev *dev);
+void mt76u_stop_tx(struct mt76_dev *dev);
+void mt76u_stop_rx(struct mt76_dev *dev);
+int mt76u_resume_rx(struct mt76_dev *dev);
void mt76u_queues_deinit(struct mt76_dev *dev);
struct sk_buff *
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
index 53ec7dc38b9d..406ebfa74195 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
@@ -86,7 +86,7 @@ static void mt76x0u_mac_stop(struct mt76x02_dev *dev)
clear_bit(MT76_STATE_RUNNING, &dev->mt76.state);
cancel_delayed_work_sync(&dev->cal_work);
cancel_delayed_work_sync(&dev->mt76.mac_work);
- mt76u_stop_stat_wk(&dev->mt76);
+ mt76u_stop_tx(&dev->mt76);
mt76x02u_exit_beacon_config(dev);
if (test_bit(MT76_REMOVED, &dev->mt76.state))
@@ -313,7 +313,7 @@ static int __maybe_unused mt76x0_suspend(struct usb_interface *usb_intf,
{
struct mt76x02_dev *dev = usb_get_intfdata(usb_intf);
- mt76u_stop_queues(&dev->mt76);
+ mt76u_stop_rx(&dev->mt76);
clear_bit(MT76_STATE_MCU_RUNNING, &dev->mt76.state);
mt76x0_chip_onoff(dev, false, false);
@@ -323,16 +323,12 @@ static int __maybe_unused mt76x0_suspend(struct usb_interface *usb_intf,
static int __maybe_unused mt76x0_resume(struct usb_interface *usb_intf)
{
struct mt76x02_dev *dev = usb_get_intfdata(usb_intf);
- struct mt76_usb *usb = &dev->mt76.usb;
int ret;
- ret = mt76u_submit_rx_buffers(&dev->mt76);
+ ret = mt76u_resume_rx(&dev->mt76);
if (ret < 0)
goto err;
- tasklet_enable(&usb->rx_tasklet);
- tasklet_enable(&dev->mt76.tx_tasklet);
-
ret = mt76x0u_init_hardware(dev);
if (ret)
goto err;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c
index c55ad617edce..7a994a783510 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c
@@ -107,7 +107,7 @@ static int __maybe_unused mt76x2u_suspend(struct usb_interface *intf,
{
struct mt76x02_dev *dev = usb_get_intfdata(intf);
- mt76u_stop_queues(&dev->mt76);
+ mt76u_stop_rx(&dev->mt76);
return 0;
}
@@ -115,16 +115,12 @@ static int __maybe_unused mt76x2u_suspend(struct usb_interface *intf,
static int __maybe_unused mt76x2u_resume(struct usb_interface *intf)
{
struct mt76x02_dev *dev = usb_get_intfdata(intf);
- struct mt76_usb *usb = &dev->mt76.usb;
int err;
- err = mt76u_submit_rx_buffers(&dev->mt76);
+ err = mt76u_resume_rx(&dev->mt76);
if (err < 0)
goto err;
- tasklet_enable(&usb->rx_tasklet);
- tasklet_enable(&dev->mt76.tx_tasklet);
-
err = mt76x2u_init_hardware(dev);
if (err < 0)
goto err;
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c
index 96ee596a69ec..f2c57d5b87f9 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c
@@ -244,7 +244,6 @@ int mt76x2u_register_device(struct mt76x02_dev *dev)
void mt76x2u_stop_hw(struct mt76x02_dev *dev)
{
- mt76u_stop_stat_wk(&dev->mt76);
cancel_delayed_work_sync(&dev->cal_work);
cancel_delayed_work_sync(&dev->mt76.mac_work);
mt76x2u_mac_stop(dev);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c
index dcf67f4845be..305977a874a4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_main.c
@@ -42,6 +42,7 @@ static void mt76x2u_stop(struct ieee80211_hw *hw)
mutex_lock(&dev->mt76.mutex);
clear_bit(MT76_STATE_RUNNING, &dev->mt76.state);
+ mt76u_stop_tx(&dev->mt76);
mt76x2u_stop_hw(dev);
mutex_unlock(&dev->mt76.mutex);
}
diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
index d2c6718b5933..c299c6591072 100644
--- a/drivers/net/wireless/mediatek/mt76/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/usb.c
@@ -540,7 +540,7 @@ static void mt76u_rx_tasklet(unsigned long data)
rcu_read_unlock();
}
-int mt76u_submit_rx_buffers(struct mt76_dev *dev)
+static int mt76u_submit_rx_buffers(struct mt76_dev *dev)
{
struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
unsigned long flags;
@@ -558,7 +558,6 @@ int mt76u_submit_rx_buffers(struct mt76_dev *dev)
return err;
}
-EXPORT_SYMBOL_GPL(mt76u_submit_rx_buffers);
static int mt76u_alloc_rx(struct mt76_dev *dev)
{
@@ -605,14 +604,29 @@ static void mt76u_free_rx(struct mt76_dev *dev)
memset(&q->rx_page, 0, sizeof(q->rx_page));
}
-static void mt76u_stop_rx(struct mt76_dev *dev)
+void mt76u_stop_rx(struct mt76_dev *dev)
{
struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
int i;
for (i = 0; i < q->ndesc; i++)
- usb_kill_urb(q->entry[i].urb);
+ usb_poison_urb(q->entry[i].urb);
+
+ tasklet_kill(&dev->usb.rx_tasklet);
+}
+EXPORT_SYMBOL_GPL(mt76u_stop_rx);
+
+int mt76u_resume_rx(struct mt76_dev *dev)
+{
+ struct mt76_queue *q = &dev->q_rx[MT_RXQ_MAIN];
+ int i;
+
+ for (i = 0; i < q->ndesc; i++)
+ usb_unpoison_urb(q->entry[i].urb);
+
+ return mt76u_submit_rx_buffers(dev);
}
+EXPORT_SYMBOL_GPL(mt76u_resume_rx);
static void mt76u_tx_tasklet(unsigned long data)
{
@@ -834,38 +848,54 @@ static void mt76u_free_tx(struct mt76_dev *dev)
}
}
-static void mt76u_stop_tx(struct mt76_dev *dev)
+void mt76u_stop_tx(struct mt76_dev *dev)
{
+ struct mt76_queue_entry entry;
struct mt76_queue *q;
- int i, j;
+ int i, j, ret;
- for (i = 0; i < IEEE80211_NUM_ACS; i++) {
- q = dev->q_tx[i].q;
- for (j = 0; j < q->ndesc; j++)
- usb_kill_urb(q->entry[j].urb);
- }
-}
+ ret = wait_event_timeout(dev->tx_wait, !mt76_has_tx_pending(dev), HZ/5);
+ if (!ret) {
+ dev_err(dev->dev, "timed out waiting for pending tx\n");
-void mt76u_stop_queues(struct mt76_dev *dev)
-{
- tasklet_disable(&dev->usb.rx_tasklet);
- tasklet_disable(&dev->tx_tasklet);
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ q = dev->q_tx[i].q;
+ for (j = 0; j < q->ndesc; j++)
+ usb_kill_urb(q->entry[j].urb);
+ }
- mt76u_stop_rx(dev);
- mt76u_stop_tx(dev);
-}
-EXPORT_SYMBOL_GPL(mt76u_stop_queues);
+ tasklet_kill(&dev->tx_tasklet);
+
+ /* On device removal we maight queue skb's, but mt76u_tx_kick()
+ * will fail to submit urb, cleanup those skb's manually.
+ */
+ for (i = 0; i < IEEE80211_NUM_ACS; i++) {
+ q = dev->q_tx[i].q;
+
+ /* Assure we are in sync with killed tasklet. */
+ spin_lock_bh(&q->lock);
+ while (q->queued) {
+ entry = q->entry[q->head];
+ q->head = (q->head + 1) % q->ndesc;
+ q->queued--;
+
+ dev->drv->tx_complete_skb(dev, i, &entry);
+ }
+ spin_unlock_bh(&q->lock);
+ }
+ }
-void mt76u_stop_stat_wk(struct mt76_dev *dev)
-{
cancel_delayed_work_sync(&dev->usb.stat_work);
clear_bit(MT76_READING_STATS, &dev->state);
+
+ mt76_tx_status_check(dev, NULL, true);
}
-EXPORT_SYMBOL_GPL(mt76u_stop_stat_wk);
+EXPORT_SYMBOL_GPL(mt76u_stop_tx);
void mt76u_queues_deinit(struct mt76_dev *dev)
{
- mt76u_stop_queues(dev);
+ mt76u_stop_rx(dev);
+ mt76u_stop_tx(dev);
mt76u_free_rx(dev);
mt76u_free_tx(dev);
--
2.20.1
^ permalink raw reply related
* [PATCH v3 1/3] mt76x02u: remove bogus stop on suspend
From: Stanislaw Gruszka @ 2019-04-26 7:58 UTC (permalink / raw)
To: linux-wireless; +Cc: Felix Fietkau, Lorenzo Bianconi
In-Reply-To: <20190426075842.2986-1-sgruszka@redhat.com>
On suspend mac80211 .stop callback is called before .suspend(), so
hw mac is already stopped and we do not have to do this again.
Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
---
drivers/net/wireless/mediatek/mt76/mt76x0/usb.c | 1 -
drivers/net/wireless/mediatek/mt76/mt76x2/usb.c | 1 -
2 files changed, 2 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
index ee8d4b5c4558..53ec7dc38b9d 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c
@@ -314,7 +314,6 @@ static int __maybe_unused mt76x0_suspend(struct usb_interface *usb_intf,
struct mt76x02_dev *dev = usb_get_intfdata(usb_intf);
mt76u_stop_queues(&dev->mt76);
- mt76x0u_mac_stop(dev);
clear_bit(MT76_STATE_MCU_RUNNING, &dev->mt76.state);
mt76x0_chip_onoff(dev, false, false);
diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c
index d1bddd5931bd..c55ad617edce 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c
@@ -108,7 +108,6 @@ static int __maybe_unused mt76x2u_suspend(struct usb_interface *intf,
struct mt76x02_dev *dev = usb_get_intfdata(intf);
mt76u_stop_queues(&dev->mt76);
- mt76x2u_stop_hw(dev);
return 0;
}
--
2.20.1
^ permalink raw reply related
* [PATCH v3 0/3] mt76: suspend/stop/removal fixes
From: Stanislaw Gruszka @ 2019-04-26 7:58 UTC (permalink / raw)
To: linux-wireless; +Cc: Felix Fietkau, Lorenzo Bianconi
Fixes for some coroner cases during suspend/stop/removal intended to
-next since problems are not reproducible in practice and changes
relay on -next patches.
Stanislaw Gruszka (3):
mt76x02u: remove bogus stop on suspend
mt76usb: fix tx/rx stop
mt76x02: remove bogus mutex usage
RFC -> v1:
- remove unneeded goto
- fix comments and changelog
v1 -> v2
- actually sent updated patches
v2 -> v3
- rebase on latest tree
drivers/net/wireless/mediatek/mt76/mac80211.c | 3 +-
drivers/net/wireless/mediatek/mt76/mt76.h | 7 +-
.../net/wireless/mediatek/mt76/mt76x0/pci.c | 6 --
.../net/wireless/mediatek/mt76/mt76x0/usb.c | 33 ++------
.../wireless/mediatek/mt76/mt76x2/pci_main.c | 5 --
.../net/wireless/mediatek/mt76/mt76x2/usb.c | 10 +--
.../wireless/mediatek/mt76/mt76x2/usb_init.c | 1 -
.../wireless/mediatek/mt76/mt76x2/usb_main.c | 11 +--
drivers/net/wireless/mediatek/mt76/usb.c | 78 +++++++++++++------
9 files changed, 74 insertions(+), 80 deletions(-)
--
2.20.1
^ permalink raw reply
* Re: [PATCH] mt76: fix endianness sparse warnings
From: Stanislaw Gruszka @ 2019-04-26 7:46 UTC (permalink / raw)
To: Ryder Lee
Cc: Lorenzo Bianconi, Felix Fietkau, Roy Luo, Sean Wang,
linux-wireless, linux-mediatek, linux-kernel
In-Reply-To: <865a5eb36f295fc81e3dcbe95929dcfce4956897.1556203212.git.ryder.lee@mediatek.com>
On Thu, Apr 25, 2019 at 10:42:01PM +0800, Ryder Lee wrote:
> Fix many warnings with incorrect endian assumptions.
>
> Reported-by: kbuild test robot <lkp@intel.com>
> Signed-off-by: Ryder Lee <ryder.lee@mediatek.com>
> ---
> drivers/net/wireless/mediatek/mt76/mt7603/mac.c | 2 +-
> drivers/net/wireless/mediatek/mt76/mt7615/mac.c | 12 ++++++------
> 2 files changed, 7 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
> index 2f2961ee0a92..af5769d05e6f 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7603/mac.c
> @@ -627,7 +627,7 @@ mt7603_mac_fill_rx(struct mt7603_dev *dev, struct sk_buff *skb)
> status->aggr = unicast &&
> !ieee80211_is_qos_nullfunc(hdr->frame_control);
> status->tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
> - status->seqno = IEEE80211_SEQ_TO_SN(hdr->seq_ctrl);
> + status->seqno = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl));
>
> return 0;
> }
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
> index 1bf3e7b5f6a7..4b934b0f5a39 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mac.c
> @@ -235,7 +235,7 @@ int mt7615_mac_fill_rx(struct mt7615_dev *dev, struct sk_buff *skb)
> status->aggr = unicast &&
> !ieee80211_is_qos_nullfunc(hdr->frame_control);
> status->tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
> - status->seqno = IEEE80211_SEQ_TO_SN(hdr->seq_ctrl);
> + status->seqno = IEEE80211_SEQ_TO_SN(__le16_to_cpu(hdr->seq_ctrl));
Is there any reason to use underscored version instead of standard le16_to_cpu() ?
Stanislaw
^ permalink raw reply
* Re: [PATCH v3 12/26] compat_ioctl: move more drivers to compat_ptr_ioctl
From: Arnd Bergmann @ 2019-04-26 7:46 UTC (permalink / raw)
To: Johannes Berg
Cc: Al Viro, Mauro Carvalho Chehab, Linux FS-devel Mailing List,
y2038 Mailman List, Linux Kernel Mailing List, Jason Gunthorpe,
Daniel Vetter, Greg Kroah-Hartman, David Sterba, Darren Hart,
Jonathan Cameron, Bjorn Andersson, driverdevel, qat-linux,
open list:HARDWARE RANDOM NUMBER GENERATOR CORE,
Linux Media Mailing List, dri-devel, linaro-mm-sig, amd-gfx,
open list:HID CORE LAYER, linux-iio, linux-rdma, linux-nvdimm,
linux-nvme, linux-pci, Platform Driver, linux-remoteproc,
sparclinux, linux-scsi, USB list, linux-btrfs, ceph-devel,
linux-wireless, Networking, Sean Young
In-Reply-To: <5511420228cb38d08a67c0f6a614b7671d7d23d4.camel@sipsolutions.net>
On Thu, Apr 25, 2019 at 11:25 PM Johannes Berg
<johannes@sipsolutions.net> wrote:
> On Thu, 2019-04-25 at 17:55 +0200, Arnd Bergmann wrote:
> > On Thu, Apr 25, 2019 at 5:35 PM Al Viro <viro@zeniv.linux.org.uk> wrote:
> > >
> > > On Thu, Apr 25, 2019 at 12:21:53PM -0300, Mauro Carvalho Chehab wrote:
> > >
> > > > If I understand your patch description well, using compat_ptr_ioctl
> > > > only works if the driver is not for s390, right?
> > >
> > > No; s390 is where "oh, just set ->compat_ioctl same as ->unlocked_ioctl
> > > and be done with that; compat_ptr() is a no-op anyway" breaks. IOW,
> > > s390 is the reason for having compat_ptr_ioctl() in the first place;
> > > that thing works on all biarch architectures, as long as all stuff
> > > handled by ->ioctl() takes pointer to arch-independent object as
> > > argument. IOW,
> > > argument ignored => OK
> > > any arithmetical type => no go, compat_ptr() would bugger it
> > > pointer to int => OK
> > > pointer to string => OK
> > > pointer to u64 => OK
> > > pointer to struct {u64 addr; char s[11];} => OK
> >
> > To be extra pedantic, the 'struct {u64 addr; char s[11];} '
> > case is also broken on x86, because sizeof (obj) is smaller
> > on i386, even though the location of the members are
> > the same. i.e. you can copy_from_user() this
>
> Actually, you can't even do that because the struct might sit at the end
> of a page and then you'd erroneously fault in this case.
>
> We had this a while ago with struct ifreq, see commit 98406133dd and its
> parents.
Yes, you are right. Very rare to hit with real-life code, but easily
reproduced by intentionally hitting it and clearly a bug.
As the saying goes
| the difference between "always works" and "almost always works"
| is called data corruption
here the difference is an -EFAULT.
Arnd
^ permalink raw reply
* Re: [PATCH] ath10k: Drop WARN_ON()s that always trigger during system resume
From: Kalle Valo @ 2019-04-26 7:18 UTC (permalink / raw)
To: Brian Norris
Cc: Rafael J. Wysocki, Claire Chang, Sriram R, Linux PM,
Pradeep Kumar Chitrapu, linux-wireless, ath10k,
Srinivas Pandruvada, Todd Brandt
In-Reply-To: <20190403195718.GA74723@google.com>
Brian Norris <briannorris@chromium.org> writes:
> + Sriram, Pradeep, Claire
>
> On Sun, Mar 03, 2019 at 06:24:33PM +0100, Rafael J. Wysocki wrote:
>
> Ooh, exactly 1 month ago!
>
>> From: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
>>
>> ath10k_mac_vif_chan() always returns an error for the given vif
>> during system-wide resume which reliably triggers two WARN_ON()s
>> in ath10k_bss_info_changed() and they are not particularly
>> useful in that code path, so drop them.
>>
>
> Particularly, when WOWLAN isn't enabled, we get called during resume via
> ieee80211_reconfig(), where we're not associated and don't have any
> channel contexts. AFAICT, we shouldn't need to communicate anything in
> particular to the firmware here, and so failing the 'if' is definitely
> not worth WARN-ing about.
>
> I'd love to see this get applied with:
>
> Fixes: cd93b83ad927 ("ath10k: support for multicast rate control")
> Fixes: f279294e9ee2 ("ath10k: add support for configuring management packet rate")
>
> and sent to stable. This has been bugging people since 4.19. Spurious
> WARN_ON()s can trigger reports to various crash trackers, and on some
> systems appear as user-visible warnings ("System problem detected").
>
>> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
>
> Reviewed-by: Brian Norris <briannorris@chromium.org>
> Tested-by: Brian Norris <briannorris@chromium.org>
I added these now to the commit log, thanks Brian.
Rafael, could you please provide the hardware and firmware versions you
tested this on? We have so many different firmware branches to support
that I prefer to have that documented in the commit log. Providing
ath10k startup messages in dmesg are enough, I can then add it to the
commit log.
--
Kalle Valo
^ permalink raw reply
* Re: [PATCH] ath10k: remove iteration in wake_tx_queue
From: Kalle Valo @ 2019-04-26 7:07 UTC (permalink / raw)
To: Erik Stromdahl
Cc: Toke Høiland-Jørgensen, linux-wireless, ath10k, yiboz
In-Reply-To: <f915fa20-a4e1-7312-dea2-d60b716e4877@gmail.com>
Erik Stromdahl <erik.stromdahl@gmail.com> writes:
> On 4/16/19 9:07 PM, Toke Høiland-Jørgensen wrote:
>> Erik Stromdahl <erik.stromdahl@gmail.com> writes:
>>
>>> On 4/1/19 1:05 PM, Toke Høiland-Jørgensen wrote:
>>>> Erik Stromdahl <erik.stromdahl@gmail.com> writes:
>>>>
>>>>> Iterating the TX queue and thereby dequeuing all available packets in the
>>>>> queue could result in performance penalties on some SMP systems.
>>>>>
>>>>> The reason for this is most likely that the per-ac lock (active_txq_lock)
>>>>> in mac80211 will be held by the CPU iterating the current queue.
>>>>>
>>>>> This will lock up other CPUs trying to push new messages on the TX
>>>>> queue.
>>>>>
>>>>> Instead of iterating the queue we fetch just one packet at the time,
>>>>> resulting in minimal starvation of the other CPUs.
>>>>
>>>> Did you test this with Felix' patches reducing the time the lock is held
>>>> in mac80211?
>>>>
>>>> -Toke
>>>>
>>> Hi Toke,
>>>
>>> I am not aware of these patches. Can you please point them out for me?
>>
>> They've already been merged. Commits dcec1d9bc8a7 and 7ef769459f14 in
>> mac80211-next :)
>>
>> -Toke
>>
>
> I see. I am using the ath tree and I couldn't find them there.
> I can cherry-pick them to my own tree and try them out
> (or wait until Kalle updates ath.git).
It will take a while before these commits trickle down to ath-next
branch, most likely after v5.2-rc1 is released.
--
Kalle Valo
^ permalink raw reply
* [PATCH 5/5] ath10k: enable QCA6174 hw3.2 SDIO hardware
From: Kalle Valo @ 2019-04-26 6:41 UTC (permalink / raw)
To: ath10k; +Cc: linux-wireless, Wen Gong
In-Reply-To: <1556260871-2919-1-git-send-email-kvalo@codeaurora.org>
From: Wen Gong <wgong@codeaurora.org>
After implementing PN replay check we can enable SDIO support on QCA6174.
Tested with client mode on all security modes, and fragmentation as well. AP
mode does not work yet.
Also tone down the warning about SDIO being not ready yet.
Tested on QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00007-QCARMSWP-1. AP mode
is not working yet.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
drivers/net/wireless/ath/ath10k/core.c | 27 +++++++++++++++++++++++++++
drivers/net/wireless/ath/ath10k/hw.h | 1 +
drivers/net/wireless/ath/ath10k/sdio.c | 2 +-
3 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
index cfd7bb29a1ec..525a7ff87bf4 100644
--- a/drivers/net/wireless/ath/ath10k/core.c
+++ b/drivers/net/wireless/ath/ath10k/core.c
@@ -154,6 +154,33 @@ static const struct ath10k_hw_params ath10k_hw_params_list[] = {
.fw_diag_ce_download = false,
},
{
+ .id = QCA6174_HW_3_2_VERSION,
+ .dev_id = QCA6174_3_2_DEVICE_ID,
+ .bus = ATH10K_BUS_SDIO,
+ .name = "qca6174 hw3.2 sdio",
+ .patch_load_addr = QCA6174_HW_3_0_PATCH_LOAD_ADDR,
+ .uart_pin = 19,
+ .otp_exe_param = 0,
+ .channel_counters_freq_hz = 88000,
+ .max_probe_resp_desc_thres = 0,
+ .cal_data_len = 0,
+ .fw = {
+ .dir = QCA6174_HW_3_0_FW_DIR,
+ .board = QCA6174_HW_3_0_BOARD_DATA_FILE,
+ .board_size = QCA6174_BOARD_DATA_SZ,
+ .board_ext_size = QCA6174_BOARD_EXT_DATA_SZ,
+ },
+ .hw_ops = &qca6174_ops,
+ .hw_clk = qca6174_clk,
+ .target_cpu_freq = 176000000,
+ .decap_align_bytes = 4,
+ .n_cipher_suites = 8,
+ .num_peers = 10,
+ .ast_skid_limit = 0x10,
+ .num_wds_entries = 0x20,
+ .uart_pin_workaround = true,
+ },
+ {
.id = QCA6174_HW_2_1_VERSION,
.dev_id = QCA6164_2_1_DEVICE_ID,
.bus = ATH10K_BUS_PCI,
diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h
index a92fce1bb6f0..248cfb4e53c3 100644
--- a/drivers/net/wireless/ath/ath10k/hw.h
+++ b/drivers/net/wireless/ath/ath10k/hw.h
@@ -24,6 +24,7 @@ enum ath10k_bus {
#define QCA988X_2_0_DEVICE_ID (0x003c)
#define QCA6164_2_1_DEVICE_ID (0x0041)
#define QCA6174_2_1_DEVICE_ID (0x003e)
+#define QCA6174_3_2_DEVICE_ID (0x0042)
#define QCA99X0_2_0_DEVICE_ID (0x0040)
#define QCA9888_2_0_DEVICE_ID (0x0056)
#define QCA9984_1_0_DEVICE_ID (0x0046)
diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c
index 30102090e282..a2c1bad67b5b 100644
--- a/drivers/net/wireless/ath/ath10k/sdio.c
+++ b/drivers/net/wireless/ath/ath10k/sdio.c
@@ -2059,7 +2059,7 @@ static int ath10k_sdio_probe(struct sdio_func *func,
}
/* TODO: remove this once SDIO support is fully implemented */
- ath10k_warn(ar, "WARNING: ath10k SDIO support is incomplete, don't expect anything to work!\n");
+ ath10k_warn(ar, "WARNING: ath10k SDIO support is work-in-progress, problems may arise!\n");
return 0;
--
2.7.4
^ permalink raw reply related
* [PATCH 4/5] ath10k: add fragmentation handler for high latency devices
From: Kalle Valo @ 2019-04-26 6:41 UTC (permalink / raw)
To: ath10k; +Cc: linux-wireless, Wen Gong
In-Reply-To: <1556260871-2919-1-git-send-email-kvalo@codeaurora.org>
From: Wen Gong <wgong@codeaurora.org>
On high latency devices (SDIO, USB) ath10k did not handle fragmented frames and
all fragmented frames on receive path were lost in ath10k. Even a simple ping
test failed with fragmentation.
The fragmented packets are decapsulated based on the security mode, then the PN
is checked and the fragmented frame is passed to mac80211. mac80211 in
ieee80211_rx_h_defragment() will then combine the fragment frames and forward
to upper layers.
Tested on QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00007-QCARMSWP-1.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
drivers/net/wireless/ath/ath10k/htt.h | 13 ++
drivers/net/wireless/ath/ath10k/htt_rx.c | 230 +++++++++++++++++++++++++++++++
2 files changed, 243 insertions(+)
diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h
index fb5cb9169073..b28c167cb5b4 100644
--- a/drivers/net/wireless/ath/ath10k/htt.h
+++ b/drivers/net/wireless/ath/ath10k/htt.h
@@ -2092,6 +2092,9 @@ struct ath10k_htt_rx_ops {
int idx);
void* (*htt_get_vaddr_ring)(struct ath10k_htt *htt);
void (*htt_reset_paddrs_ring)(struct ath10k_htt *htt, int idx);
+ bool (*htt_rx_proc_rx_frag_ind)(struct ath10k_htt *htt,
+ struct htt_rx_fragment_indication *rx,
+ struct sk_buff *skb);
};
static inline size_t ath10k_htt_get_rx_ring_size(struct ath10k_htt *htt)
@@ -2131,6 +2134,16 @@ static inline void ath10k_htt_reset_paddrs_ring(struct ath10k_htt *htt, int idx)
htt->rx_ops->htt_reset_paddrs_ring(htt, idx);
}
+static inline bool ath10k_htt_rx_proc_rx_frag_ind(struct ath10k_htt *htt,
+ struct htt_rx_fragment_indication *rx,
+ struct sk_buff *skb)
+{
+ if (!htt->rx_ops->htt_rx_proc_rx_frag_ind)
+ return true;
+
+ return htt->rx_ops->htt_rx_proc_rx_frag_ind(htt, rx, skb);
+}
+
#define RX_HTT_HDR_STATUS_LEN 64
/* This structure layout is programmed via rx ring setup
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index 961e14633320..45f4eecc3839 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -2266,6 +2266,231 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt,
return true;
}
+static int ath10k_htt_rx_frag_tkip_decap_nomic(struct sk_buff *skb,
+ u16 head_len,
+ u16 hdr_len)
+{
+ u8 *ivp, *orig_hdr;
+
+ orig_hdr = skb->data;
+ ivp = orig_hdr + hdr_len + head_len;
+
+ /* the ExtIV bit is always set to 1 for TKIP */
+ if (!(ivp[IEEE80211_WEP_IV_LEN - 1] & ATH10K_IEEE80211_EXTIV))
+ return -EINVAL;
+
+ memmove(orig_hdr + IEEE80211_TKIP_IV_LEN, orig_hdr, head_len + hdr_len);
+ skb_pull(skb, IEEE80211_TKIP_IV_LEN);
+ skb_trim(skb, skb->len - ATH10K_IEEE80211_TKIP_MICLEN);
+ return 0;
+}
+
+static int ath10k_htt_rx_frag_tkip_decap_withmic(struct sk_buff *skb,
+ u16 head_len,
+ u16 hdr_len)
+{
+ u8 *ivp, *orig_hdr;
+
+ orig_hdr = skb->data;
+ ivp = orig_hdr + hdr_len + head_len;
+
+ /* the ExtIV bit is always set to 1 for TKIP */
+ if (!(ivp[IEEE80211_WEP_IV_LEN - 1] & ATH10K_IEEE80211_EXTIV))
+ return -EINVAL;
+
+ memmove(orig_hdr + IEEE80211_TKIP_IV_LEN, orig_hdr, head_len + hdr_len);
+ skb_pull(skb, IEEE80211_TKIP_IV_LEN);
+ skb_trim(skb, skb->len - IEEE80211_TKIP_ICV_LEN);
+ return 0;
+}
+
+static int ath10k_htt_rx_frag_ccmp_decap(struct sk_buff *skb,
+ u16 head_len,
+ u16 hdr_len)
+{
+ u8 *ivp, *orig_hdr;
+
+ orig_hdr = skb->data;
+ ivp = orig_hdr + hdr_len + head_len;
+
+ /* the ExtIV bit is always set to 1 for CCMP */
+ if (!(ivp[IEEE80211_WEP_IV_LEN - 1] & ATH10K_IEEE80211_EXTIV))
+ return -EINVAL;
+
+ skb_trim(skb, skb->len - IEEE80211_CCMP_MIC_LEN);
+ memmove(orig_hdr + IEEE80211_CCMP_HDR_LEN, orig_hdr, head_len + hdr_len);
+ skb_pull(skb, IEEE80211_CCMP_HDR_LEN);
+ return 0;
+}
+
+static int ath10k_htt_rx_frag_wep_decap(struct sk_buff *skb,
+ u16 head_len,
+ u16 hdr_len)
+{
+ u8 *orig_hdr;
+
+ orig_hdr = skb->data;
+
+ memmove(orig_hdr + IEEE80211_WEP_IV_LEN,
+ orig_hdr, head_len + hdr_len);
+ skb_pull(skb, IEEE80211_WEP_IV_LEN);
+ skb_trim(skb, skb->len - IEEE80211_WEP_ICV_LEN);
+ return 0;
+}
+
+static bool ath10k_htt_rx_proc_rx_frag_ind_hl(struct ath10k_htt *htt,
+ struct htt_rx_fragment_indication *rx,
+ struct sk_buff *skb)
+{
+ struct ath10k *ar = htt->ar;
+ enum htt_rx_tkip_demic_type tkip_mic = HTT_RX_NON_TKIP_MIC;
+ enum htt_txrx_sec_cast_type sec_index;
+ struct htt_rx_indication_hl *rx_hl;
+ enum htt_security_types sec_type;
+ u32 tid, frag, seq, rx_desc_info;
+ union htt_rx_pn_t new_pn = {0};
+ struct htt_hl_rx_desc *rx_desc;
+ u16 peer_id, sc, hdr_space;
+ union htt_rx_pn_t *last_pn;
+ struct ieee80211_hdr *hdr;
+ int ret, num_mpdu_ranges;
+ struct ath10k_peer *peer;
+ struct htt_resp *resp;
+ size_t tot_hdr_len;
+
+ resp = (struct htt_resp *)(skb->data + HTT_RX_FRAG_IND_INFO0_HEADER_LEN);
+ skb_pull(skb, HTT_RX_FRAG_IND_INFO0_HEADER_LEN);
+ skb_trim(skb, skb->len - FCS_LEN);
+
+ peer_id = __le16_to_cpu(rx->peer_id);
+ rx_hl = (struct htt_rx_indication_hl *)(&resp->rx_ind_hl);
+
+ spin_lock_bh(&ar->data_lock);
+ peer = ath10k_peer_find_by_id(ar, peer_id);
+ if (!peer) {
+ ath10k_dbg(ar, ATH10K_DBG_HTT, "invalid peer: %u\n", peer_id);
+ goto err;
+ }
+
+ num_mpdu_ranges = MS(__le32_to_cpu(rx_hl->hdr.info1),
+ HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
+
+ tot_hdr_len = sizeof(struct htt_resp_hdr) +
+ sizeof(rx_hl->hdr) +
+ sizeof(rx_hl->ppdu) +
+ sizeof(rx_hl->prefix) +
+ sizeof(rx_hl->fw_desc) +
+ sizeof(struct htt_rx_indication_mpdu_range) * num_mpdu_ranges;
+
+ tid = MS(rx_hl->hdr.info0, HTT_RX_INDICATION_INFO0_EXT_TID);
+ rx_desc = (struct htt_hl_rx_desc *)(skb->data + tot_hdr_len);
+ rx_desc_info = __le32_to_cpu(rx_desc->info);
+
+ if (!MS(rx_desc_info, HTT_RX_DESC_HL_INFO_ENCRYPTED)) {
+ spin_unlock_bh(&ar->data_lock);
+ return ath10k_htt_rx_proc_rx_ind_hl(htt, &resp->rx_ind_hl, skb,
+ HTT_RX_NON_PN_CHECK,
+ HTT_RX_NON_TKIP_MIC);
+ }
+
+ hdr = (struct ieee80211_hdr *)((u8 *)rx_desc + rx_hl->fw_desc.len);
+
+ if (ieee80211_has_retry(hdr->frame_control))
+ goto err;
+
+ hdr_space = ieee80211_hdrlen(hdr->frame_control);
+ sc = __le16_to_cpu(hdr->seq_ctrl);
+ seq = (sc & IEEE80211_SCTL_SEQ) >> 4;
+ frag = sc & IEEE80211_SCTL_FRAG;
+
+ sec_index = MS(rx_desc_info, HTT_RX_DESC_HL_INFO_MCAST_BCAST) ?
+ HTT_TXRX_SEC_MCAST : HTT_TXRX_SEC_UCAST;
+ sec_type = peer->rx_pn[sec_index].sec_type;
+ ath10k_htt_rx_mpdu_desc_pn_hl(rx_desc, &new_pn, peer->rx_pn[sec_index].pn_len);
+
+ switch (sec_type) {
+ case HTT_SECURITY_TKIP:
+ tkip_mic = HTT_RX_TKIP_MIC;
+ ret = ath10k_htt_rx_frag_tkip_decap_withmic(skb,
+ tot_hdr_len +
+ rx_hl->fw_desc.len,
+ hdr_space);
+ if (ret)
+ goto err;
+ break;
+ case HTT_SECURITY_TKIP_NOMIC:
+ ret = ath10k_htt_rx_frag_tkip_decap_nomic(skb,
+ tot_hdr_len +
+ rx_hl->fw_desc.len,
+ hdr_space);
+ if (ret)
+ goto err;
+ break;
+ case HTT_SECURITY_AES_CCMP:
+ ret = ath10k_htt_rx_frag_ccmp_decap(skb,
+ tot_hdr_len + rx_hl->fw_desc.len,
+ hdr_space);
+ if (ret)
+ goto err;
+ break;
+ case HTT_SECURITY_WEP128:
+ case HTT_SECURITY_WEP104:
+ case HTT_SECURITY_WEP40:
+ ret = ath10k_htt_rx_frag_wep_decap(skb,
+ tot_hdr_len + rx_hl->fw_desc.len,
+ hdr_space);
+ if (ret)
+ goto err;
+ break;
+ default:
+ break;
+ }
+
+ resp = (struct htt_resp *)(skb->data);
+
+ if (sec_type != HTT_SECURITY_AES_CCMP &&
+ sec_type != HTT_SECURITY_TKIP &&
+ sec_type != HTT_SECURITY_TKIP_NOMIC) {
+ spin_unlock_bh(&ar->data_lock);
+ return ath10k_htt_rx_proc_rx_ind_hl(htt, &resp->rx_ind_hl, skb,
+ HTT_RX_NON_PN_CHECK,
+ HTT_RX_NON_TKIP_MIC);
+ }
+
+ last_pn = &peer->frag_tids_last_pn[tid];
+
+ if (frag == 0) {
+ if (ath10k_htt_rx_pn_check_replay_hl(ar, peer, &resp->rx_ind_hl))
+ goto err;
+
+ last_pn->pn48 = new_pn.pn48;
+ peer->frag_tids_seq[tid] = seq;
+ } else if (sec_type == HTT_SECURITY_AES_CCMP) {
+ if (seq != peer->frag_tids_seq[tid])
+ goto err;
+
+ if (new_pn.pn48 != last_pn->pn48 + 1)
+ goto err;
+
+ last_pn->pn48 = new_pn.pn48;
+ last_pn = &peer->tids_last_pn[tid];
+ last_pn->pn48 = new_pn.pn48;
+ }
+
+ spin_unlock_bh(&ar->data_lock);
+
+ return ath10k_htt_rx_proc_rx_ind_hl(htt, &resp->rx_ind_hl, skb,
+ HTT_RX_NON_PN_CHECK, tkip_mic);
+
+err:
+ spin_unlock_bh(&ar->data_lock);
+
+ /* Tell the caller that it must free the skb since we have not
+ * consumed it
+ */
+ return true;
+}
+
static void ath10k_htt_rx_proc_rx_ind_ll(struct ath10k_htt *htt,
struct htt_rx_indication *rx)
{
@@ -3511,6 +3736,10 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
ath10k_dbg_dump(ar, ATH10K_DBG_HTT_DUMP, NULL, "htt event: ",
skb->data, skb->len);
atomic_inc(&htt->num_mpdus_ready);
+
+ return ath10k_htt_rx_proc_rx_frag_ind(htt,
+ &resp->rx_frag_ind,
+ skb);
break;
}
case HTT_T2H_MSG_TYPE_TEST:
@@ -3724,6 +3953,7 @@ static const struct ath10k_htt_rx_ops htt_rx_ops_64 = {
};
static const struct ath10k_htt_rx_ops htt_rx_ops_hl = {
+ .htt_rx_proc_rx_frag_ind = ath10k_htt_rx_proc_rx_frag_ind_hl,
};
void ath10k_htt_set_rx_ops(struct ath10k_htt *htt)
--
2.7.4
^ permalink raw reply related
* [PATCH 3/5] ath10k: add PN replay protection for high latency devices
From: Kalle Valo @ 2019-04-26 6:41 UTC (permalink / raw)
To: ath10k; +Cc: linux-wireless, Wen Gong
In-Reply-To: <1556260871-2919-1-git-send-email-kvalo@codeaurora.org>
From: Wen Gong <wgong@codeaurora.org>
On high latency devices (SDIO, USB) ath10k did not do PN replay check, a data
frame with an invalid PN number was not discard as it should have been. So this
patch implements PN replay in ath10k. PN replay check for fragmented frames is
implemented in followup patch.
With low latency devices (PCI, AHB) hardware can store the data
frames's content to host memory directly and the firmware can fully reorder
data frames, and do PN replay check at the same time. But for high latency
devices all data frames will be received and stored in firmware's memory and it
is hard to do full reorder because of the memory size limitations in the
firmware. This is why the PN replay protections needs to be implemented in host
driver.
Tested on QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00007-QCARMSWP-1.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
drivers/net/wireless/ath/ath10k/htt_rx.c | 97 +++++++++++++++++++++++++++++++-
1 file changed, 95 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index cc2875d73bf0..961e14633320 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -2061,9 +2061,91 @@ static int ath10k_htt_rx_handle_amsdu(struct ath10k_htt *htt)
return 0;
}
+static void ath10k_htt_rx_mpdu_desc_pn_hl(struct htt_hl_rx_desc *rx_desc,
+ union htt_rx_pn_t *pn,
+ int pn_len_bits)
+{
+ switch (pn_len_bits) {
+ case 48:
+ pn->pn48 = __le32_to_cpu(rx_desc->pn_31_0) +
+ ((u64)(__le32_to_cpu(rx_desc->u0.pn_63_32) & 0xFFFF) << 32);
+ break;
+ case 24:
+ pn->pn24 = __le32_to_cpu(rx_desc->pn_31_0);
+ break;
+ };
+}
+
+static bool ath10k_htt_rx_pn_cmp48(union htt_rx_pn_t *new_pn,
+ union htt_rx_pn_t *old_pn)
+{
+ return ((new_pn->pn48 & 0xffffffffffffULL) <=
+ (old_pn->pn48 & 0xffffffffffffULL));
+}
+
+static bool ath10k_htt_rx_pn_check_replay_hl(struct ath10k *ar,
+ struct ath10k_peer *peer,
+ struct htt_rx_indication_hl *rx)
+{
+ bool last_pn_valid, pn_invalid = false;
+ enum htt_txrx_sec_cast_type sec_index;
+ enum htt_security_types sec_type;
+ union htt_rx_pn_t new_pn = {0};
+ struct htt_hl_rx_desc *rx_desc;
+ union htt_rx_pn_t *last_pn;
+ u32 rx_desc_info, tid;
+ int num_mpdu_ranges;
+
+ lockdep_assert_held(&ar->data_lock);
+
+ if (!peer)
+ return false;
+
+ if (!(rx->fw_desc.flags & FW_RX_DESC_FLAGS_FIRST_MSDU))
+ return false;
+
+ num_mpdu_ranges = MS(__le32_to_cpu(rx->hdr.info1),
+ HTT_RX_INDICATION_INFO1_NUM_MPDU_RANGES);
+
+ rx_desc = (struct htt_hl_rx_desc *)&rx->mpdu_ranges[num_mpdu_ranges];
+ rx_desc_info = __le32_to_cpu(rx_desc->info);
+
+ if (!MS(rx_desc_info, HTT_RX_DESC_HL_INFO_ENCRYPTED))
+ return false;
+
+ tid = MS(rx->hdr.info0, HTT_RX_INDICATION_INFO0_EXT_TID);
+ last_pn_valid = peer->tids_last_pn_valid[tid];
+ last_pn = &peer->tids_last_pn[tid];
+
+ if (MS(rx_desc_info, HTT_RX_DESC_HL_INFO_MCAST_BCAST))
+ sec_index = HTT_TXRX_SEC_MCAST;
+ else
+ sec_index = HTT_TXRX_SEC_UCAST;
+
+ sec_type = peer->rx_pn[sec_index].sec_type;
+ ath10k_htt_rx_mpdu_desc_pn_hl(rx_desc, &new_pn, peer->rx_pn[sec_index].pn_len);
+
+ if (sec_type != HTT_SECURITY_AES_CCMP &&
+ sec_type != HTT_SECURITY_TKIP &&
+ sec_type != HTT_SECURITY_TKIP_NOMIC)
+ return false;
+
+ if (last_pn_valid)
+ pn_invalid = ath10k_htt_rx_pn_cmp48(&new_pn, last_pn);
+ else
+ peer->tids_last_pn_valid[tid] = 1;
+
+ if (!pn_invalid)
+ last_pn->pn48 = new_pn.pn48;
+
+ return pn_invalid;
+}
+
static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt,
struct htt_rx_indication_hl *rx,
- struct sk_buff *skb)
+ struct sk_buff *skb,
+ enum htt_rx_pn_check_type check_pn_type,
+ enum htt_rx_tkip_demic_type tkip_mic_type)
{
struct ath10k *ar = htt->ar;
struct ath10k_peer *peer;
@@ -2107,6 +2189,10 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt,
goto err;
}
+ if (check_pn_type == HTT_RX_PN_CHECK &&
+ ath10k_htt_rx_pn_check_replay_hl(ar, peer, rx))
+ goto err;
+
/* Strip off all headers before the MAC header before delivery to
* mac80211
*/
@@ -2114,6 +2200,7 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt,
sizeof(rx->ppdu) + sizeof(rx->prefix) +
sizeof(rx->fw_desc) +
sizeof(*mpdu_ranges) * num_mpdu_ranges + rx_desc_len;
+
skb_pull(skb, tot_hdr_len);
hdr = (struct ieee80211_hdr *)skb->data;
@@ -2162,6 +2249,10 @@ static bool ath10k_htt_rx_proc_rx_ind_hl(struct ath10k_htt *htt,
RX_FLAG_MMIC_STRIPPED;
}
+ if (tkip_mic_type == HTT_RX_TKIP_MIC)
+ rx_status->flag &= ~RX_FLAG_IV_STRIPPED &
+ ~RX_FLAG_MMIC_STRIPPED;
+
ieee80211_rx_ni(ar->hw, skb);
/* We have delivered the skb to the upper layers (mac80211) so we
@@ -3343,7 +3434,9 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
if (ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL)
return ath10k_htt_rx_proc_rx_ind_hl(htt,
&resp->rx_ind_hl,
- skb);
+ skb,
+ HTT_RX_PN_CHECK,
+ HTT_RX_NON_TKIP_MIC);
else
ath10k_htt_rx_proc_rx_ind_ll(htt, &resp->rx_ind);
break;
--
2.7.4
^ permalink raw reply related
* [PATCH 2/5] ath10k: add handler for HTT_T2H_MSG_TYPE_SEC_IND event
From: Kalle Valo @ 2019-04-26 6:41 UTC (permalink / raw)
To: ath10k; +Cc: linux-wireless, Wen Gong
In-Reply-To: <1556260871-2919-1-git-send-email-kvalo@codeaurora.org>
From: Wen Gong <wgong@codeaurora.org>
Add the handler for HTT_T2H_MSG_TYPE_SEC_IND event from firmware, which stores
PN for replay check implemented in the following patch.
Tested on QCA6174 SDIO with firmware WLAN.RMH.4.4.1-00007-QCARMSWP-1.
Signed-off-by: Wen Gong <wgong@codeaurora.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
---
drivers/net/wireless/ath/ath10k/htt_rx.c | 46 ++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)
diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c
index f22840bbc389..cc2875d73bf0 100644
--- a/drivers/net/wireless/ath/ath10k/htt_rx.c
+++ b/drivers/net/wireless/ath/ath10k/htt_rx.c
@@ -3267,6 +3267,51 @@ static void ath10k_fetch_10_2_tx_stats(struct ath10k *ar, u8 *data)
rcu_read_unlock();
}
+static int ath10k_htt_rx_pn_len(enum htt_security_types sec_type)
+{
+ switch (sec_type) {
+ case HTT_SECURITY_TKIP:
+ case HTT_SECURITY_TKIP_NOMIC:
+ case HTT_SECURITY_AES_CCMP:
+ return 48;
+ default:
+ return 0;
+ }
+}
+
+static void ath10k_htt_rx_sec_ind_handler(struct ath10k *ar,
+ struct htt_security_indication *ev)
+{
+ enum htt_txrx_sec_cast_type sec_index;
+ enum htt_security_types sec_type;
+ struct ath10k_peer *peer;
+
+ spin_lock_bh(&ar->data_lock);
+
+ peer = ath10k_peer_find_by_id(ar, __le16_to_cpu(ev->peer_id));
+ if (!peer) {
+ ath10k_warn(ar, "failed to find peer id %d for security indication",
+ __le16_to_cpu(ev->peer_id));
+ goto out;
+ }
+
+ sec_type = MS(ev->flags, HTT_SECURITY_TYPE);
+
+ if (ev->flags & HTT_SECURITY_IS_UNICAST)
+ sec_index = HTT_TXRX_SEC_UCAST;
+ else
+ sec_index = HTT_TXRX_SEC_MCAST;
+
+ peer->rx_pn[sec_index].sec_type = sec_type;
+ peer->rx_pn[sec_index].pn_len = ath10k_htt_rx_pn_len(sec_type);
+
+ memset(peer->tids_last_pn_valid, 0, sizeof(peer->tids_last_pn_valid));
+ memset(peer->tids_last_pn, 0, sizeof(peer->tids_last_pn));
+
+out:
+ spin_unlock_bh(&ar->data_lock);
+}
+
bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
{
struct ath10k_htt *htt = &ar->htt;
@@ -3360,6 +3405,7 @@ bool ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb)
struct ath10k *ar = htt->ar;
struct htt_security_indication *ev = &resp->security_indication;
+ ath10k_htt_rx_sec_ind_handler(ar, ev);
ath10k_dbg(ar, ATH10K_DBG_HTT,
"sec ind peer_id %d unicast %d type %d\n",
__le16_to_cpu(ev->peer_id),
--
2.7.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox