2021-10-11 13:43:00

by Carl Huang

[permalink] [raw]
Subject: [PATCH 3/6] ath11k: implement hw data filter

Host needs to set hw data filter before entering WoW to
let firmware drop needless bc/mc frames to avoid frequent wakeup.
Host clears hw data filter when leaving WoW.

Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1

Signed-off-by: Carl Huang <[email protected]>
---
drivers/net/wireless/ath/ath11k/wmi.c | 32 ++++++++++++++++++++
drivers/net/wireless/ath/ath11k/wmi.h | 15 +++++++++
drivers/net/wireless/ath/ath11k/wow.c | 57 ++++++++++++++++++++++++++++++++++-
3 files changed, 103 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index 9f466fe..be880dc 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -8348,6 +8348,38 @@ void ath11k_wmi_detach(struct ath11k_base *ab)
ath11k_wmi_free_dbring_caps(ab);
}

+int ath11k_wmi_send_hw_data_filter_cmd(struct ath11k *ar, u32 vdev_id,
+ u32 filter_bitmap, bool enable)
+{
+ struct wmi_hw_data_filter_cmd *cmd;
+ struct sk_buff *skb;
+ int len;
+
+ len = sizeof(*cmd);
+ skb = ath11k_wmi_alloc_skb(ar->wmi->wmi_ab, len);
+
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_hw_data_filter_cmd *)skb->data;
+ cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, WMI_TAG_HW_DATA_FILTER_CMD) |
+ FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE);
+
+ cmd->vdev_id = vdev_id;
+ cmd->enable = enable;
+ /* Set all modes in case of disable */
+ if (cmd->enable)
+ cmd->hw_filter_bitmap = filter_bitmap;
+ else
+ cmd->hw_filter_bitmap = ((u32)~0U);
+
+ ath11k_dbg(ar->ab, ATH11K_DBG_WMI,
+ "wmi set hw filter enable %d, filter_bitmap:0x%x\n",
+ enable, filter_bitmap);
+
+ return ath11k_wmi_cmd_send(ar->wmi, skb, WMI_HW_DATA_FILTER_CMDID);
+}
+
int ath11k_wmi_wow_host_wakeup_ind(struct ath11k *ar)
{
struct wmi_wow_host_wakeup_ind *cmd;
diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h
index 6d3dfb9..1301b17 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -5400,6 +5400,19 @@ struct ath11k_wmi_base {
struct ath11k_targ_cap *targ_cap;
};

+/* Definition of HW data filtering */
+enum hw_data_filter_type {
+ WMI_HW_DATA_FILTER_DROP_NON_ARP_BC = BIT(0),
+ WMI_HW_DATA_FILTER_DROP_NON_ICMPV6_MC = BIT(1),
+};
+
+struct wmi_hw_data_filter_cmd {
+ u32 tlv_header;
+ u32 vdev_id;
+ u32 enable;
+ u32 hw_filter_bitmap;
+} __packed;
+
/* WOW structures */
enum wmi_wow_wakeup_event {
WOW_BMISS_EVENT = 0,
@@ -5956,4 +5969,6 @@ int ath11k_wmi_wow_add_pattern(struct ath11k *ar, u32 vdev_id, u32 pattern_id,
int ath11k_wmi_wow_add_wakeup_event(struct ath11k *ar, u32 vdev_id,
enum wmi_wow_wakeup_event event,
u32 enable);
+int ath11k_wmi_send_hw_data_filter_cmd(struct ath11k *ar, u32 vdev_id,
+ u32 filter_bitmap, bool enable);
#endif
diff --git a/drivers/net/wireless/ath/ath11k/wow.c b/drivers/net/wireless/ath/ath11k/wow.c
index 839c58e7..35f1c5c 100644
--- a/drivers/net/wireless/ath/ath11k/wow.c
+++ b/drivers/net/wireless/ath/ath11k/wow.c
@@ -507,6 +507,48 @@ static int ath11k_wow_nlo_cleanup(struct ath11k *ar)
return 0;
}

+static int ath11k_wow_set_hw_filter(struct ath11k *ar)
+{
+ struct ath11k_vif *arvif;
+ int ret;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ list_for_each_entry(arvif, &ar->arvifs, list) {
+ ret = ath11k_wmi_send_hw_data_filter_cmd(ar, arvif->vdev_id,
+ WMI_HW_DATA_FILTER_DROP_NON_ICMPV6_MC |
+ WMI_HW_DATA_FILTER_DROP_NON_ARP_BC,
+ true);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to set hw data filter on vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int ath11k_wow_clear_hw_filter(struct ath11k *ar)
+{
+ struct ath11k_vif *arvif;
+ int ret;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ list_for_each_entry(arvif, &ar->arvifs, list) {
+ ret = ath11k_wmi_send_hw_data_filter_cmd(ar, arvif->vdev_id, 0, false);
+
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to clear hw data filter on vdev %i: %d\n",
+ arvif->vdev_id, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
int ath11k_wow_op_suspend(struct ieee80211_hw *hw,
struct cfg80211_wowlan *wowlan)
{
@@ -535,6 +577,13 @@ int ath11k_wow_op_suspend(struct ieee80211_hw *hw,
goto cleanup;
}

+ ret = ath11k_wow_set_hw_filter(ar);
+ if (ret) {
+ ath11k_warn(ar->ab, "failed to set hw filter: %d\n",
+ ret);
+ goto cleanup;
+ }
+
ret = ath11k_wow_enable(ar->ab);
if (ret) {
ath11k_warn(ar->ab, "failed to start wow: %d\n", ret);
@@ -598,8 +647,14 @@ int ath11k_wow_op_resume(struct ieee80211_hw *hw)
}

ret = ath11k_wow_nlo_cleanup(ar);
- if (ret)
+ if (ret) {
ath11k_warn(ar->ab, "failed to cleanup nlo: %d\n", ret);
+ goto exit;
+ }
+
+ ret = ath11k_wow_clear_hw_filter(ar);
+ if (ret)
+ ath11k_warn(ar->ab, "failed to clear hw filter: %d\n", ret);

exit:
if (ret) {
--
2.7.4


2021-12-09 15:07:39

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH 3/6] ath11k: implement hw data filter

Carl Huang <[email protected]> writes:

> Host needs to set hw data filter before entering WoW to
> let firmware drop needless bc/mc frames to avoid frequent wakeup.
> Host clears hw data filter when leaving WoW.

Please try to use full words to keep the commit log readable:

ath11k: implement hardware data filter

Host needs to set hardware data filter before entering WoW to let
firmware drop needless broadcast/multicast frames to avoid frequent
wakeup. Host clears the hardware data filter when leaving WoW.

--
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches

2021-12-09 15:47:23

by Kalle Valo

[permalink] [raw]
Subject: Re: [PATCH 3/6] ath11k: implement hw data filter

Carl Huang <[email protected]> writes:

> Host needs to set hw data filter before entering WoW to
> let firmware drop needless bc/mc frames to avoid frequent wakeup.
> Host clears hw data filter when leaving WoW.
>
> Tested-on: QCA6390 hw2.0 PCI WLAN.HST.1.0.1-01740-QCAHSTSWPLZ_V2_TO_X86-1
>
> Signed-off-by: Carl Huang <[email protected]>

[...]

> @@ -598,8 +647,14 @@ int ath11k_wow_op_resume(struct ieee80211_hw *hw)
> }
>
> ret = ath11k_wow_nlo_cleanup(ar);
> - if (ret)
> + if (ret) {
> ath11k_warn(ar->ab, "failed to cleanup nlo: %d\n", ret);
> + goto exit;
> + }
> +
> + ret = ath11k_wow_clear_hw_filter(ar);
> + if (ret)
> + ath11k_warn(ar->ab, "failed to clear hw filter: %d\n", ret);
>
> exit:
> if (ret) {

For consistency please add goto exit for ath11k_wow_clear_hw_filter().

--
https://patchwork.kernel.org/project/linux-wireless/list/

https://wireless.wiki.kernel.org/en/developers/documentation/submittingpatches